aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llmessage/llmessagetemplateparser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'linden/indra/llmessage/llmessagetemplateparser.cpp')
-rw-r--r--linden/indra/llmessage/llmessagetemplateparser.cpp754
1 files changed, 754 insertions, 0 deletions
diff --git a/linden/indra/llmessage/llmessagetemplateparser.cpp b/linden/indra/llmessage/llmessagetemplateparser.cpp
new file mode 100644
index 0000000..eb5e87e
--- /dev/null
+++ b/linden/indra/llmessage/llmessagetemplateparser.cpp
@@ -0,0 +1,754 @@
1/**
2 * @file llmessagetemplateparser.cpp
3 * @brief LLMessageTemplateParser implementation
4 *
5 * Copyright (c) 2007-2007, Linden Research, Inc.
6 *
7 * Second Life Viewer Source Code
8 * The source code in this file ("Source Code") is provided by Linden Lab
9 * to you under the terms of the GNU General Public License, version 2.0
10 * ("GPL"), unless you have obtained a separate licensing agreement
11 * ("Other License"), formally executed by you and Linden Lab. Terms of
12 * the GPL can be found in doc/GPL-license.txt in this distribution, or
13 * online at http://secondlife.com/developers/opensource/gplv2
14 *
15 * There are special exceptions to the terms and conditions of the GPL as
16 * it is applied to this Source Code. View the full text of the exception
17 * in the file doc/FLOSS-exception.txt in this software distribution, or
18 * online at http://secondlife.com/developers/opensource/flossexception
19 *
20 * By copying, modifying or distributing this software, you acknowledge
21 * that you have read and understood your obligations described above,
22 * and agree to abide by those obligations.
23 *
24 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
25 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
26 * COMPLETENESS OR PERFORMANCE.
27 */
28
29#include "llmessagetemplateparser.h"
30#include <boost/tokenizer.hpp>
31
32
33// What follows is a bunch of C functions to do validation.
34
35// Lets support a small subset of regular expressions here
36// Syntax is a string made up of:
37// a - checks against alphanumeric ([A-Za-z0-9])
38// c - checks against character ([A-Za-z])
39// f - checks against first variable character ([A-Za-z_])
40// v - checks against variable ([A-Za-z0-9_])
41// s - checks against sign of integer ([-0-9])
42// d - checks against integer digit ([0-9])
43// * - repeat last check
44
45// checks 'a'
46BOOL b_return_alphanumeric_ok(char c)
47{
48 if ( ( (c < 'A')
49 ||(c > 'Z'))
50 &&( (c < 'a')
51 ||(c > 'z'))
52 &&( (c < '0')
53 ||(c > '9')))
54 {
55 return FALSE;
56 }
57 return TRUE;
58}
59
60// checks 'c'
61BOOL b_return_character_ok(char c)
62{
63 if ( ( (c < 'A')
64 ||(c > 'Z'))
65 &&( (c < 'a')
66 ||(c > 'z')))
67 {
68 return FALSE;
69 }
70 return TRUE;
71}
72
73// checks 'f'
74BOOL b_return_first_variable_ok(char c)
75{
76 if ( ( (c < 'A')
77 ||(c > 'Z'))
78 &&( (c < 'a')
79 ||(c > 'z'))
80 &&(c != '_'))
81 {
82 return FALSE;
83 }
84 return TRUE;
85}
86
87// checks 'v'
88BOOL b_return_variable_ok(char c)
89{
90 if ( ( (c < 'A')
91 ||(c > 'Z'))
92 &&( (c < 'a')
93 ||(c > 'z'))
94 &&( (c < '0')
95 ||(c > '9'))
96 &&(c != '_'))
97 {
98 return FALSE;
99 }
100 return TRUE;
101}
102
103// checks 's'
104BOOL b_return_signed_integer_ok(char c)
105{
106 if ( ( (c < '0')
107 ||(c > '9'))
108 &&(c != '-'))
109 {
110 return FALSE;
111 }
112 return TRUE;
113}
114
115// checks 'd'
116BOOL b_return_integer_ok(char c)
117{
118 if ( (c < '0')
119 ||(c > '9'))
120 {
121 return FALSE;
122 }
123 return TRUE;
124}
125
126BOOL (*gParseCheckCharacters[])(char c) =
127{
128 b_return_alphanumeric_ok,
129 b_return_character_ok,
130 b_return_first_variable_ok,
131 b_return_variable_ok,
132 b_return_signed_integer_ok,
133 b_return_integer_ok
134};
135
136S32 get_checker_number(char checker)
137{
138 switch(checker)
139 {
140 case 'a':
141 return 0;
142 case 'c':
143 return 1;
144 case 'f':
145 return 2;
146 case 'v':
147 return 3;
148 case 's':
149 return 4;
150 case 'd':
151 return 5;
152 case '*':
153 return 9999;
154 default:
155 return -1;
156 }
157}
158
159// check token based on passed simplified regular expression
160BOOL b_check_token(const char *token, char *regexp)
161{
162 S32 tptr, rptr = 0;
163 S32 current_checker, next_checker = 0;
164
165 current_checker = get_checker_number(regexp[rptr++]);
166
167 if (current_checker == -1)
168 {
169 llerrs << "Invalid regular expression value!" << llendl;
170 return FALSE;
171 }
172
173 if (current_checker == 9999)
174 {
175 llerrs << "Regular expression can't start with *!" << llendl;
176 return FALSE;
177 }
178
179 for (tptr = 0; token[tptr]; tptr++)
180 {
181 if (current_checker == -1)
182 {
183 llerrs << "Input exceeds regular expression!\nDid you forget a *?" << llendl;
184 return FALSE;
185 }
186
187 if (!gParseCheckCharacters[current_checker](token[tptr]))
188 {
189 return FALSE;
190 }
191 if (next_checker != 9999)
192 {
193 next_checker = get_checker_number(regexp[rptr++]);
194 if (next_checker != 9999)
195 {
196 current_checker = next_checker;
197 }
198 }
199 }
200 return TRUE;
201}
202
203// C variable can be made up of upper or lower case letters, underscores, or numbers, but can't start with a number
204BOOL b_variable_ok(const char *token)
205{
206 if (!b_check_token(token, "fv*"))
207 {
208 llwarns << "Token '" << token << "' isn't a variable!" << llendl;
209 return FALSE;
210 }
211 return TRUE;
212}
213
214// An integer is made up of the digits 0-9 and may be preceded by a '-'
215BOOL b_integer_ok(const char *token)
216{
217 if (!b_check_token(token, "sd*"))
218 {
219 llwarns << "Token isn't an integer!" << llendl;
220 return FALSE;
221 }
222 return TRUE;
223}
224
225// An integer is made up of the digits 0-9
226BOOL b_positive_integer_ok(const char *token)
227{
228 if (!b_check_token(token, "d*"))
229 {
230 llwarns << "Token isn't an integer!" << llendl;
231 return FALSE;
232 }
233 return TRUE;
234}
235
236
237// Done with C functions, here's the tokenizer.
238
239typedef boost::tokenizer< boost::char_separator<char> > tokenizer;
240
241LLTemplateTokenizer::LLTemplateTokenizer(const std::string & contents) : mStarted(false), mTokens()
242{
243 boost::char_separator<char> newline("\r\n", "", boost::keep_empty_tokens);
244 boost::char_separator<char> spaces(" \t");
245 U32 line_counter = 1;
246
247 tokenizer line_tokens(contents, newline);
248 for(tokenizer::iterator line_iter = line_tokens.begin();
249 line_iter != line_tokens.end();
250 ++line_iter, ++line_counter)
251 {
252 tokenizer word_tokens(*line_iter, spaces);
253 for(tokenizer::iterator word_iter = word_tokens.begin();
254 word_iter != word_tokens.end();
255 ++word_iter)
256 {
257 if((*word_iter)[0] == '/')
258 {
259 break; // skip to end of line on comments
260 }
261 positioned_token pt;// = new positioned_token();
262 pt.str = std::string(*word_iter);
263 pt.line = line_counter;
264 mTokens.push_back(pt);
265 }
266 }
267 mCurrent = mTokens.begin();
268}
269void LLTemplateTokenizer::inc()
270{
271 if(atEOF())
272 {
273 error("trying to increment token of EOF");
274 }
275 else if(mStarted)
276 {
277 ++mCurrent;
278 }
279 else
280 {
281 mStarted = true;
282 mCurrent = mTokens.begin();
283 }
284}
285void LLTemplateTokenizer::dec()
286{
287 if(mCurrent == mTokens.begin())
288 {
289 if(mStarted)
290 {
291 mStarted = false;
292 }
293 else
294 {
295 error("trying to decrement past beginning of file");
296 }
297 }
298 else
299 {
300 mCurrent--;
301 }
302}
303
304std::string LLTemplateTokenizer::get() const
305{
306 if(atEOF())
307 {
308 error("trying to get EOF");
309 }
310 return mCurrent->str;
311}
312
313U32 LLTemplateTokenizer::line() const
314{
315 if(atEOF())
316 {
317 return 0;
318 }
319 return mCurrent->line;
320}
321
322bool LLTemplateTokenizer::atEOF() const
323{
324 return mCurrent == mTokens.end();
325}
326
327std::string LLTemplateTokenizer::next()
328{
329 inc();
330 return get();
331}
332
333bool LLTemplateTokenizer::want(const std::string & token)
334{
335 if(atEOF()) return false;
336 inc();
337 if(atEOF()) return false;
338 if(get() != token)
339 {
340 dec(); // back up a step
341 return false;
342 }
343 return true;
344}
345
346bool LLTemplateTokenizer::wantEOF()
347{
348 // see if the next token is EOF
349 if(atEOF()) return true;
350 inc();
351 if(!atEOF())
352 {
353 dec(); // back up a step
354 return false;
355 }
356 return true;
357}
358
359void LLTemplateTokenizer::error(std::string message) const
360{
361 if(atEOF())
362 {
363 llerrs << "Unexpected end of file: " << message << llendl;
364 }
365 else
366 {
367 llerrs << "Problem parsing message template at line "
368 << line() << ", with token '" << get() << "' : "
369 << message << llendl;
370 }
371}
372
373
374// Done with tokenizer, next is the parser.
375
376LLTemplateParser::LLTemplateParser(LLTemplateTokenizer & tokens):
377 mVersion(0.f),
378 mMessages()
379{
380 // the version number should be the first thing in the file
381 if (tokens.want("version"))
382 {
383 // version number
384 std::string vers_string = tokens.next();
385 mVersion = (F32)atof(vers_string.c_str());
386
387 llinfos << "### Message template version " << mVersion << " ###" << llendl;
388 }
389 else
390 {
391 llerrs << "Version must be first in the message template, found "
392 << tokens.next() << llendl;
393 }
394
395 while(LLMessageTemplate * templatep = parseMessage(tokens))
396 {
397 if (templatep->getDeprecation() != MD_DEPRECATED)
398 {
399 mMessages.push_back(templatep);
400 }
401 }
402
403 if(!tokens.wantEOF())
404 {
405 llerrs << "Expected end of template or a message, instead found: "
406 << tokens.next() << " at " << tokens.line() << llendl;
407 }
408}
409
410F32 LLTemplateParser::getVersion() const
411{
412 return mVersion;
413}
414
415LLTemplateParser::message_iterator LLTemplateParser::getMessagesBegin() const
416{
417 return mMessages.begin();
418}
419
420LLTemplateParser::message_iterator LLTemplateParser::getMessagesEnd() const
421{
422 return mMessages.end();
423}
424
425
426// static
427LLMessageTemplate * LLTemplateParser::parseMessage(LLTemplateTokenizer & tokens)
428{
429 LLMessageTemplate *templatep = NULL;
430 if(!tokens.want("{"))
431 {
432 return NULL;
433 }
434
435 // name first
436 std::string template_name = tokens.next();
437
438 // is name a legit C variable name
439 if (!b_variable_ok(template_name.c_str()))
440 {
441 llerrs << "Not legit variable name: " << template_name << " at " << tokens.line() << llendl;
442 }
443
444 // ok, now get Frequency ("High", "Medium", or "Low")
445 EMsgFrequency frequency = MFT_LOW;
446 std::string freq_string = tokens.next();
447 if (freq_string == "High")
448 {
449 frequency = MFT_HIGH;
450 }
451 else if (freq_string == "Medium")
452 {
453 frequency = MFT_MEDIUM;
454 }
455 else if (freq_string == "Low" || freq_string == "Fixed")
456 {
457 frequency = MFT_LOW;
458 }
459 else
460 {
461 llerrs << "Expected frequency, got " << freq_string << " at " << tokens.line() << llendl;
462 }
463
464 // TODO more explicit checking here pls
465 U32 message_number = strtoul(tokens.next().c_str(),NULL,0);
466
467 switch (frequency) {
468 case MFT_HIGH:
469 break;
470 case MFT_MEDIUM:
471 message_number = (255 << 8) | message_number;
472 break;
473 case MFT_LOW:
474 message_number = (255 << 24) | (255 << 16) | message_number;
475 break;
476 default:
477 llerrs << "Unknown frequency enum: " << frequency << llendl;
478 }
479
480 templatep = new LLMessageTemplate(
481 template_name.c_str(),
482 message_number,
483 frequency);
484
485 // Now get trust ("Trusted", "NotTrusted")
486 std::string trust = tokens.next();
487 if (trust == "Trusted")
488 {
489 templatep->setTrust(MT_TRUST);
490 }
491 else if (trust == "NotTrusted")
492 {
493 templatep->setTrust(MT_NOTRUST);
494 }
495 else
496 {
497 llerrs << "Bad trust " << trust << " at " << tokens.line() << llendl;
498 }
499
500 // get encoding
501 std::string encoding = tokens.next();
502 if(encoding == "Unencoded")
503 {
504 templatep->setEncoding(ME_UNENCODED);
505 }
506 else if(encoding == "Zerocoded")
507 {
508 templatep->setEncoding(ME_ZEROCODED);
509 }
510 else
511 {
512 llerrs << "Bad encoding " << encoding << " at " << tokens.line() << llendl;
513 }
514
515 // get deprecation
516 if(tokens.want("Deprecated"))
517 {
518 templatep->setDeprecation(MD_DEPRECATED);
519 }
520 else if (tokens.want("UDPDeprecated"))
521 {
522 templatep->setDeprecation(MD_UDPDEPRECATED);
523 }
524 else if (tokens.want("NotDeprecated"))
525 {
526 // this is the default value, but it can't hurt to set it twice
527 templatep->setDeprecation(MD_NOTDEPRECATED);
528 }
529 else {
530 // It's probably a brace, let's just start block processing
531 }
532
533 while(LLMessageBlock * blockp = parseBlock(tokens))
534 {
535 templatep->addBlock(blockp);
536 }
537
538 if(!tokens.want("}"))
539 {
540 llerrs << "Expecting closing } for message " << template_name
541 << " at " << tokens.line() << llendl;
542 }
543 return templatep;
544}
545
546// static
547LLMessageBlock * LLTemplateParser::parseBlock(LLTemplateTokenizer & tokens)
548{
549 LLMessageBlock * blockp = NULL;
550
551 if(!tokens.want("{"))
552 {
553 return NULL;
554 }
555
556 // name first
557 std::string block_name = tokens.next();
558
559 // is name a legit C variable name
560 if (!b_variable_ok(block_name.c_str()))
561 {
562 llerrs << "not a legal block name: " << block_name
563 << " at " << tokens.line() << llendl;
564 }
565
566 // now, block type ("Single", "Multiple", or "Variable")
567 std::string block_type = tokens.next();
568 // which one is it?
569 if (block_type == "Single")
570 {
571 // ok, we can create a block
572 blockp = new LLMessageBlock(block_name.c_str(), MBT_SINGLE);
573 }
574 else if (block_type == "Multiple")
575 {
576 // need to get the number of repeats
577 std::string repeats = tokens.next();
578
579 // is it a legal integer
580 if (!b_positive_integer_ok(repeats.c_str()))
581 {
582 llerrs << "not a legal integer for block multiple count: "
583 << repeats << " at " << tokens.line() << llendl;
584 }
585
586 // ok, we can create a block
587 blockp = new LLMessageBlock(block_name.c_str(),
588 MBT_MULTIPLE,
589 atoi(repeats.c_str()));
590 }
591 else if (block_type == "Variable")
592 {
593 // ok, we can create a block
594 blockp = new LLMessageBlock(block_name.c_str(), MBT_VARIABLE);
595 }
596 else
597 {
598 llerrs << "bad block type: " << block_type
599 << " at " << tokens.line() << llendl;
600 }
601
602
603 while(LLMessageVariable * varp = parseVariable(tokens))
604 {
605 blockp->addVariable(varp->getName(),
606 varp->getType(),
607 varp->getSize());
608 delete varp;
609 }
610
611 if(!tokens.want("}"))
612 {
613 llerrs << "Expecting closing } for block " << block_name
614 << " at " << tokens.line() << llendl;
615 }
616 return blockp;
617
618}
619
620// static
621LLMessageVariable * LLTemplateParser::parseVariable(LLTemplateTokenizer & tokens)
622{
623 LLMessageVariable * varp = NULL;
624 if(!tokens.want("{"))
625 {
626 return NULL;
627 }
628
629 std::string var_name = tokens.next();
630
631 if (!b_variable_ok(var_name.c_str()))
632 {
633 llerrs << "Not a legit variable name: " << var_name
634 << " at " << tokens.line() << llendl;
635 }
636
637 std::string var_type = tokens.next();
638
639 if (var_type == "U8")
640 {
641 varp = new LLMessageVariable(var_name.c_str(), MVT_U8, 1);
642 }
643 else if (var_type == "U16")
644 {
645 varp = new LLMessageVariable(var_name.c_str(), MVT_U16, 2);
646 }
647 else if (var_type == "U32")
648 {
649 varp = new LLMessageVariable(var_name.c_str(), MVT_U32, 4);
650 }
651 else if (var_type == "U64")
652 {
653 varp = new LLMessageVariable(var_name.c_str(), MVT_U64, 8);
654 }
655 else if (var_type == "S8")
656 {
657 varp = new LLMessageVariable(var_name.c_str(), MVT_S8, 1);
658 }
659 else if (var_type == "S16")
660 {
661 varp = new LLMessageVariable(var_name.c_str(), MVT_S16, 2);
662 }
663 else if (var_type == "S32")
664 {
665 varp = new LLMessageVariable(var_name.c_str(), MVT_S32, 4);
666 }
667 else if (var_type == "S64")
668 {
669 varp = new LLMessageVariable(var_name.c_str(), MVT_S64, 8);
670 }
671 else if (var_type == "F32")
672 {
673 varp = new LLMessageVariable(var_name.c_str(), MVT_F32, 4);
674 }
675 else if (var_type == "F64")
676 {
677 varp = new LLMessageVariable(var_name.c_str(), MVT_F64, 8);
678 }
679 else if (var_type == "LLVector3")
680 {
681 varp = new LLMessageVariable(var_name.c_str(), MVT_LLVector3, 12);
682 }
683 else if (var_type == "LLVector3d")
684 {
685 varp = new LLMessageVariable(var_name.c_str(), MVT_LLVector3d, 24);
686 }
687 else if (var_type == "LLVector4")
688 {
689 varp = new LLMessageVariable(var_name.c_str(), MVT_LLVector4, 16);
690 }
691 else if (var_type == "LLQuaternion")
692 {
693 varp = new LLMessageVariable(var_name.c_str(), MVT_LLQuaternion, 12);
694 }
695 else if (var_type == "LLUUID")
696 {
697 varp = new LLMessageVariable(var_name.c_str(), MVT_LLUUID, 16);
698 }
699 else if (var_type == "BOOL")
700 {
701 varp = new LLMessageVariable(var_name.c_str(), MVT_BOOL, 1);
702 }
703 else if (var_type == "IPADDR")
704 {
705 varp = new LLMessageVariable(var_name.c_str(), MVT_IP_ADDR, 4);
706 }
707 else if (var_type == "IPPORT")
708 {
709 varp = new LLMessageVariable(var_name.c_str(), MVT_IP_PORT, 2);
710 }
711 else if (var_type == "Fixed" || var_type == "Variable")
712 {
713 std::string variable_size = tokens.next();
714
715 if (!b_positive_integer_ok(variable_size.c_str()))
716 {
717 llerrs << "not a legal integer variable size: " << variable_size
718 << " at " << tokens.line() << llendl;
719 }
720
721 EMsgVariableType type_enum;
722 if(var_type == "Variable")
723 {
724 type_enum = MVT_VARIABLE;
725 }
726 else if(var_type == "Fixed")
727 {
728 type_enum = MVT_FIXED;
729 }
730 else
731 {
732 type_enum = MVT_FIXED; // removes a warning
733 llerrs << "bad variable type: " << var_type
734 << " at " << tokens.line() << llendl;
735 }
736
737 varp = new LLMessageVariable(
738 var_name.c_str(),
739 type_enum,
740 atoi(variable_size.c_str()));
741 }
742 else
743 {
744 llerrs << "bad variable type:" << var_type
745 << " at " << tokens.line() << llendl;
746 }
747
748 if(!tokens.want("}"))
749 {
750 llerrs << "Expecting closing } for variable " << var_name
751 << " at " << tokens.line() << llendl;
752 }
753 return varp;
754}