1 //=============================================================================
2 //
3 //   File : KviKvsParser_specialCommands.cpp
4 //   Creation date : Thu 06 Now 2003 14.14 CEST by Szymon Stefanek
5 //
6 //   This file is part of the KVIrc IRC client distribution
7 //   Copyright (C) 2003-2010 Szymon Stefanek (pragma at kvirc dot net)
8 //
9 //   This program is FREE software. You can redistribute it and/or
10 //   modify it under the terms of the GNU General Public License
11 //   as published by the Free Software Foundation; either version 2
12 //   of the License, or (at your option) any later version.
13 //
14 //   This program is distributed in the HOPE that it will be USEFUL,
15 //   but WITHOUT ANY WARRANTY; without even the implied warranty of
16 //   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 //   See the GNU General Public License for more details.
18 //
19 //   You should have received a copy of the GNU General Public License
20 //   along with this program. If not, write to the Free Software Foundation,
21 //   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 //
23 //=============================================================================
24 
25 #include "KviKvsParser.h"
26 #include "KviKvsTreeNode.h"
27 #include "KviKvsReport.h"
28 #include "KviKvsKernel.h"
29 #include "KviKvsParserMacros.h"
30 #include "KviKvsObjectFunctionHandler.h"
31 #include "KviLocale.h"
32 #include "KviCommandFormatter.h"
33 
34 /*
35 in fact this is not a fully special command
36 it is special only in the sense of parsing.
37 Once parsed, the command is routed to the perl module
38 with the interpreter code as FIRST parameter and the other parameters
39 of the command following.
40 the help page for perl.begin is in the perl module
41 the help page for python.begin is in the python module
42 
43 perl.begin(context) <perl code> perl.end
44 python.begin <python code> python.end
45 */
46 #define IMPLEMENT_EXTERNAL_INTERPRETER_BEGIN(__name)                                                                      \
47 	KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommand##__name##Begin()                                            \
48 	{                                                                                                                     \
49 		const QChar * pBegin = KVSP_curCharPointer;                                                                       \
50                                                                                                                           \
51 		skipSpaces();                                                                                                     \
52 		KviKvsTreeNodeDataList * dl;                                                                                      \
53 		if(KVSP_curCharUnicode == '(')                                                                                    \
54 		{                                                                                                                 \
55 			dl = parseCommaSeparatedParameterList();                                                                      \
56 			if(!dl)                                                                                                       \
57 				return nullptr;                                                                                           \
58 		}                                                                                                                 \
59 		else                                                                                                              \
60 		{                                                                                                                 \
61 			dl = new KviKvsTreeNodeDataList(pBegin);                                                                      \
62 		}                                                                                                                 \
63                                                                                                                           \
64 		if(!KVSP_curCharIsEndOfBuffer)                                                                                    \
65 			KVSP_skipChar;                                                                                                \
66                                                                                                                           \
67 		if(!skipSpacesAndNewlines())                                                                                      \
68 		{                                                                                                                 \
69 			delete dl;                                                                                                    \
70 			return nullptr;                                                                                               \
71 		}                                                                                                                 \
72                                                                                                                           \
73 		/* allow a ';' after [interpreter].begin */                                                                       \
74 		if(KVSP_curCharIsEndOfCommand && !KVSP_curCharIsEndOfBuffer)                                                      \
75 		{                                                                                                                 \
76 			KVSP_skipChar;                                                                                                \
77 			if(!skipSpacesAndNewlines())                                                                                  \
78 			{                                                                                                             \
79 				delete dl;                                                                                                \
80 				return nullptr;                                                                                           \
81 			}                                                                                                             \
82 		}                                                                                                                 \
83                                                                                                                           \
84 		const QChar * pInterpreterBegin = KVSP_curCharPointer;                                                            \
85 		QString szName;                                                                                                   \
86 		szName.sprintf("%s", #__name);                                                                                    \
87 		szName = szName.toLower();                                                                                        \
88                                                                                                                           \
89 		/* now look for [interpreter].end[terminator] */                                                                  \
90 		QString szInterpreterEnd(szName);                                                                                 \
91 		szInterpreterEnd += ".end";                                                                                       \
92 		const QChar * pInterpreterEnd;                                                                                    \
93 		for(;;)                                                                                                           \
94 		{                                                                                                                 \
95 			while(KVSP_curCharUnicode && (KVSP_curCharUnicode != 'p') && (KVSP_curCharUnicode != 'P'))                    \
96 				KVSP_skipChar;                                                                                            \
97 			if(KVSP_curCharIsEndOfBuffer)                                                                                 \
98 			{                                                                                                             \
99 				delete dl;                                                                                                \
100 				QString szErr = "Unexpected end of command buffer while looking for the ";                                \
101 				szErr += szInterpreterEnd;                                                                                \
102 				szErr += " statement";                                                                                    \
103                                                                                                                           \
104 				error(KVSP_curCharPointer, __tr2qs_ctx(szErr.toUtf8().data(), "kvs"));                                    \
105 				return nullptr;                                                                                           \
106 			}                                                                                                             \
107 			pInterpreterEnd = KVSP_curCharPointer;                                                                        \
108                                                                                                                           \
109 			if(KviQString::equalCIN(szInterpreterEnd, KVSP_curCharPointer, szInterpreterEnd.size()))                      \
110 			{                                                                                                             \
111 				KVSP_skipNChars(szInterpreterEnd.size());                                                                 \
112 				if(KVSP_curCharIsEndOfCommand || (KVSP_curCharUnicode == ' ') || (KVSP_curCharUnicode == '\t'))           \
113 				{                                                                                                         \
114 					/* yeah! */                                                                                           \
115 					QString szInterpreter(pInterpreterBegin, pInterpreterEnd - pInterpreterBegin);                        \
116 					dl->prependItem(new KviKvsTreeNodeConstantData(pInterpreterBegin, new KviKvsVariant(szInterpreter))); \
117 					while(!KVSP_curCharIsEndOfCommand)                                                                    \
118 						KVSP_skipChar;                                                                                    \
119 					if(!KVSP_curCharIsEndOfBuffer)                                                                        \
120 						KVSP_skipChar;                                                                                    \
121 					break;                                                                                                \
122 				}                                                                                                         \
123 				else                                                                                                      \
124 				{                                                                                                         \
125 					KVSP_backNChars(szInterpreterEnd.size() - 1);                                                         \
126 				}                                                                                                         \
127 			}                                                                                                             \
128 			else                                                                                                          \
129 			{                                                                                                             \
130 				KVSP_skipChar;                                                                                            \
131 			}                                                                                                             \
132 		}                                                                                                                 \
133                                                                                                                           \
134 		return new KviKvsTreeNodeModuleSimpleCommand(pBegin, szName, "begin", dl);                                        \
135 	}
136 
137 IMPLEMENT_EXTERNAL_INTERPRETER_BEGIN(Perl)
IMPLEMENT_EXTERNAL_INTERPRETER_BEGIN(Python)138 IMPLEMENT_EXTERNAL_INTERPRETER_BEGIN(Python)
139 
140 KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandBreak()
141 {
142 	/*
143 		@doc: break
144 		@type:
145 			command
146 		@title:
147 			break
148 		@syntax:
149 			break
150 		@short:
151 			Interrupts an iteration loop
152 		@description:
153 			Interrupts an iteration loop like [cmd]while[/cmd].[br]
154 			This command always jumps out of a single code block.[br]
155 			If called outside an iteration loop, will act just like [cmd]halt[/cmd]
156 			has been called but has no additional semantics for events.[br]
157 	*/
158 	const QChar * pBegin = KVSP_curCharPointer; // FIXME: this is not accurate at all : it may be even the end of the cmd
159 	skipSpaces();
160 	if(!KVSP_curCharIsEndOfCommand)
161 	{
162 		warning(KVSP_curCharPointer, __tr2qs_ctx("Trailing garbage at the end of the break command: ignored", "kvs"));
163 	}
164 
165 	while(!KVSP_curCharIsEndOfCommand)
166 		KVSP_skipChar;
167 	if(!KVSP_curCharIsEndOfBuffer)
168 		KVSP_skipChar;
169 	return new KviKvsTreeNodeSpecialCommandBreak(pBegin);
170 }
171 
parseSpecialCommandContinue()172 KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandContinue()
173 {
174 	/*
175 		@doc: continue
176 		@type:
177 			command
178 		@title:
179 			continue
180 		@syntax:
181 			continue
182 		@short:
183 			Continues an iteration loop
184 		@description:
185 			Continues an iteration loop like [cmd]while[/cmd].[br]
186 			This command always jumps to the next iteration in the code block.[br]
187 	*/
188 	const QChar * pBegin = KVSP_curCharPointer; // FIXME: this is not accurate at all : it may be even the end of the cmd
189 	skipSpaces();
190 	if(!KVSP_curCharIsEndOfCommand)
191 	{
192 		warning(KVSP_curCharPointer, __tr2qs_ctx("Trailing garbage at the end of the continue command: ignored", "kvs"));
193 	}
194 
195 	while(!KVSP_curCharIsEndOfCommand)
196 		KVSP_skipChar;
197 	if(!KVSP_curCharIsEndOfBuffer)
198 		KVSP_skipChar;
199 	return new KviKvsTreeNodeSpecialCommandContinue(pBegin);
200 }
201 
parseSpecialCommandUnset()202 KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandUnset()
203 {
204 	/*
205 		@doc: unset
206 		@type:
207 			command
208 		@title:
209 			unset
210 		@syntax:
211 			unset <variable_list>
212 		@keyterms:
213 			unsetting variables
214 		@short:
215 			Unsets a set of variables
216 		@description:
217 			Unsets the specified list of comma separated variables.
218 			It is equivalent to assigning the default empty value
219 			to each variable on its own: just does it all at once.
220 			Note that KVIrc automatically frees the local variable memory
221 			when they go out of scope and the global variable memory
222 			when KVIrc terminates.
223 		@examples:
224 			[example]
225 				%a = pippo
226 				%b = 1
227 				[cmd]echo[/cmd] %a %b
228 				unset %a %b
229 				[cmd]echo[/cmd] %a %b
230 			[/example]
231 	*/
232 
233 	const QChar * pCmdBegin = KVSP_curCharPointer;
234 
235 	KviPointerList<KviKvsTreeNodeVariable> * pVarList = new KviPointerList<KviKvsTreeNodeVariable>;
236 	pVarList->setAutoDelete(true);
237 
238 	while(KVSP_curCharUnicode == '%')
239 	{
240 		KviKvsTreeNodeVariable * d = parsePercent();
241 		if(!d)
242 		{
243 			delete pVarList;
244 			return nullptr;
245 		}
246 
247 		pVarList->append(d);
248 
249 		skipSpaces();
250 
251 		if(KVSP_curCharUnicode == ',')
252 		{
253 			KVSP_skipChar;
254 			skipSpaces();
255 		}
256 	}
257 
258 	if(!KVSP_curCharIsEndOfCommand)
259 	{
260 		delete pVarList;
261 		warning(KVSP_curCharPointer, __tr2qs_ctx("The 'unset' command needs a variable list", "kvs"));
262 		error(KVSP_curCharPointer, __tr2qs_ctx("Found character %q (Unicode %x) where a variable was expected", "kvs"), KVSP_curCharPointer, KVSP_curCharUnicode);
263 		return nullptr;
264 	}
265 
266 	if(!KVSP_curCharIsEndOfBuffer)
267 		KVSP_skipChar;
268 
269 	if(pVarList->count() < 1)
270 	{
271 		delete pVarList;
272 		warning(KVSP_curCharPointer, __tr2qs_ctx("'unset' command used without a variable list", "kvs"));
273 		return nullptr; // null unset ?
274 	}
275 	return new KviKvsTreeNodeSpecialCommandUnset(pCmdBegin, pVarList);
276 }
277 
parseSpecialCommandGlobal()278 KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandGlobal()
279 {
280 	/*
281 		@doc: global
282 		@type:
283 			command
284 		@title:
285 			global
286 		@syntax:
287 			global <variable_list>
288 		@keyterms:
289 			explicitly declaring global variables
290 		@short:
291 			Explicitly declares global variables
292 		@description:
293 			Declares a list of global variables.
294 			Once a variable has been declared as global
295 			it refers to the global KVIrc instance for the scope of the script.[br]
296 			Global variables are shared between scripts and keep their
297 			value until they are explicitly [cmd]unset[/cmd] or KVIrc quits.[br]
298 			This command can be used to override the default behaviour of
299 			declaring global variables by starting them with an uppercase letter
300 			and declaring local variables by starting them with a lowercase one.[br][br]
301 			In any particular instance instance where a global variable may interfere with desired operation,
302 			you can specifically [b][cmd]unset[/cmd][/b] the desired variable.
303 		@examples:
304 			[example]
305 				global %a, %b, %c;
306 			[/example]
307 		@seealso:
308 			[fnc]$global[/fnc]
309 			[cmd]unset[/cmd]
310 			[fnc]$hash[/fnc]
311 	*/
312 
313 	while(KVSP_curCharUnicode == '%')
314 	{
315 		KVSP_skipChar;
316 		const QChar * pBegin = KVSP_curCharPointer;
317 
318 		while((KVSP_curCharIsLetterOrNumber) || (KVSP_curCharUnicode == '_'))
319 			KVSP_skipChar;
320 
321 		QString szIdentifier(pBegin, KVSP_curCharPointer - pBegin);
322 
323 		if(!m_pGlobals)
324 		{
325 			m_pGlobals = new KviPointerHashTable<QString, QString>(17, false);
326 			m_pGlobals->setAutoDelete(true);
327 		}
328 		m_pGlobals->replace(szIdentifier, new QString());
329 
330 		skipSpaces();
331 
332 		if(KVSP_curCharUnicode == ',')
333 		{
334 			KVSP_skipChar;
335 			skipSpaces();
336 		}
337 	}
338 
339 	if(!KVSP_curCharIsEndOfCommand)
340 	{
341 		warning(KVSP_curCharPointer, __tr2qs_ctx("The 'global' command needs a list of variables", "kvs"));
342 		error(KVSP_curCharPointer, __tr2qs_ctx("Found character %q (Unicode %x) where a variable was expected", "kvs"), KVSP_curCharPointer, KVSP_curCharUnicode);
343 		return nullptr;
344 	}
345 
346 	if(!KVSP_curCharIsEndOfBuffer)
347 		KVSP_skipChar;
348 	return nullptr;
349 }
350 
parseSpecialCommandClass()351 KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandClass()
352 {
353 	/*
354 		@doc: class
355 		@title:
356 			class
357 		@short:
358 			Defines a new object class
359 		@keyterms:
360 			defining an object class
361 		@type:
362 			command
363 		@syntax:
364 			class(<classname:string>[,<base_class_name:string>])
365 			{
366 				[internal] [function] <function_name>[([<parameter reminder>])]
367 				{
368 					<function body>
369 				}
370 
371 				...
372 			}
373 		@description:
374 			Defines a new implementation of the class <classname>.
375 			If an implementation of that class was already existing
376 			it is removed with all the derived classes (and all the instances of this class
377 			and the derived ones are destroyed).
378 			<base_class_name> is the name of the class that the
379 			new class has to inherit from.[br]
380 			If <base_class_name> is omitted, the new class inherits automatically
381 			from [class:object]object[/class].[br]
382 			Note:[br]
383 			The keywords "function" and "event" that were used in KVIrc versions
384 			previous to 3.0.0 have been removed since they are "useless".[br]
385 			The function keyword, however, is still permitted.
386 			The keyword "internal" is useful when you want to hide
387 			certain function from the outside world. An internal function
388 			cannot be called by anyone else but the object instance itself. Note that
389 			this is different from the C++ "protected" or "private" keywords
390 			that refer to the object's class instead of the object instance.
391 			The <parameter reminder> part is an optional string
392 			that can be used to sign the parameters that the function expects;
393 			it acts as a programmer reminder or comment and it has no other
394 			meaning in KVIrc scripting. The <parameter reminder> respects the syntax
395 			of an expression, so it is terminated by a closed parenthesis.
396 			It's rather dangerous to use this command inside an object
397 			function handler: if the class definition <class> was already
398 			existing and it is a parent of the object's class, you might
399 			end up executing "non-existent" code.[br]
400 			As a thumb rule, use this command only outside object function handlers.[br]
401 			[br][br]
402 			Only for the curious: implementing protected and private access
403 			list on members would have a considerable runtime overhead because
404 			of the strange nature of the KVS language. Object member calls
405 			are resolved completely at runtime (and that permits a lot of funny tricks
406 			like [cmd]privateimpl[/cmd]) but unfortunately this also forces us
407 			to check access lists at runtime. OK, this would be a relatively small footprint for the "private"
408 			keyword where we need to run UP the called object inheritance hierarchy
409 			but would have a significant performance footprint for the "protected"
410 			keyword where we would need to traverse the [b]whole[/b] inheritance tree of the called and calling
411 			objects... "internal" still allows hiding members in a lot of situations
412 			and is really fast to verify at runtime: no inheritance tree traversal
413 			is needed and only object pointers are compared.
414 		@examples:
415 			[example]
416 				class(myclass,[class]object[/class])
417 				{
418 					constructor
419 					{
420 						[cmd]echo[/cmd] Hey this is my constructor
421 						[cmd]echo[/cmd] I have been just created
422 					}
423 
424 					destructor
425 					{
426 						[cmd]echo[/cmd] Oops! being destroyed
427 					}
428 
429 					sayHello(this function expects no parameters)
430 					{
431 						[cmd]echo[/cmd] Hello world!
432 					}
433 				}
434 			[/example]
435 		@seealso:
436 			[cmd]privateimpl[/cmd], [cmd]killclass[/cmd], [cmd]clearobjects[/cmd], [fnc]$classDefined[/fnc](),
437 			[doc:objects]Objects documentation[/doc]
438 	*/
439 
440 	if(KVSP_curCharUnicode != '(')
441 	{
442 		error(KVSP_curCharPointer, __tr2qs_ctx("Found character %q (Unicode %x) where an open parenthesis was expected", "kvs"), KVSP_curCharPointer, KVSP_curCharUnicode);
443 		return nullptr;
444 	}
445 
446 	const QChar * pBegin = KVSP_curCharPointer;
447 
448 	KviKvsTreeNodeDataList * l = parseCommaSeparatedParameterList();
449 	if(!l)
450 		return nullptr;
451 
452 	KviKvsTreeNodeSpecialCommandClass * pClass = new KviKvsTreeNodeSpecialCommandClass(pBegin, l);
453 
454 	if(!skipSpacesAndNewlines())
455 	{
456 		delete pClass;
457 		return nullptr;
458 	}
459 
460 	if(KVSP_curCharUnicode != '{')
461 	{
462 		errorBadChar(KVSP_curCharPointer, '{', "class");
463 		delete pClass;
464 		return nullptr;
465 	}
466 
467 	KVSP_skipChar;
468 
469 	if(!skipSpacesAndNewlines())
470 	{
471 		delete pClass;
472 		return nullptr;
473 	}
474 
475 	while(KVSP_curCharUnicode != '}')
476 	{
477 		if((KVSP_curCharUnicode == '#') || (KVSP_curCharUnicode == '/'))
478 		{
479 			parseComment();
480 			if(error())
481 			{
482 				delete pClass;
483 				return nullptr;
484 			}
485 			if(!skipSpacesAndNewlines())
486 			{
487 				delete pClass;
488 				return nullptr;
489 			}
490 			continue;
491 		}
492 
493 		const QChar * pLabelBegin = KVSP_curCharPointer;
494 
495 		if(KVSP_curCharIsLetter)
496 		{
497 			KVSP_skipChar;
498 			while(KVSP_curCharIsLetterOrNumber)
499 				KVSP_skipChar;
500 		}
501 
502 		if(KVSP_curCharIsEndOfBuffer)
503 		{
504 			error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of buffer in class definition", "kvs"));
505 			delete pClass;
506 			return nullptr;
507 		}
508 
509 		if(KVSP_curCharPointer == pLabelBegin)
510 		{
511 			error(KVSP_curCharPointer, __tr2qs_ctx("Found character %q (Unicode %x) where a function name was expected", "kvs"), KVSP_curCharPointer, KVSP_curCharUnicode);
512 			delete pClass;
513 			return nullptr;
514 		}
515 
516 		QString szLabel(pLabelBegin, KVSP_curCharPointer - pLabelBegin);
517 
518 		unsigned int uHandlerFlags = 0;
519 
520 		if(szLabel.toLower() == "internal")
521 		{
522 			uHandlerFlags |= KviKvsObjectFunctionHandler::Internal;
523 			skipSpaces();
524 			if(KVSP_curCharUnicode != '(')
525 			{
526 				pLabelBegin = KVSP_curCharPointer;
527 
528 				while(KVSP_curCharIsLetterOrNumber)
529 					KVSP_skipChar;
530 
531 				if(KVSP_curCharIsEndOfBuffer)
532 				{
533 					error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of buffer in class definition", "kvs"));
534 					delete pClass;
535 					return nullptr;
536 				}
537 
538 				if(KVSP_curCharPointer == pLabelBegin)
539 				{
540 					error(KVSP_curCharPointer, __tr2qs_ctx("Found character %q (Unicode %x) where a function name was expected", "kvs"), KVSP_curCharPointer, KVSP_curCharUnicode);
541 					delete pClass;
542 					return nullptr;
543 				}
544 				szLabel = QString(pLabelBegin, KVSP_curCharPointer - pLabelBegin);
545 			}
546 		}
547 
548 		if(szLabel.toLower() == "function")
549 		{
550 			skipSpaces();
551 			if(KVSP_curCharUnicode != '(')
552 			{
553 				pLabelBegin = KVSP_curCharPointer;
554 
555 				while(KVSP_curCharIsLetterOrNumber)
556 					KVSP_skipChar;
557 
558 				if(KVSP_curCharIsEndOfBuffer)
559 				{
560 					error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of buffer in class definition", "kvs"));
561 					delete pClass;
562 					return nullptr;
563 				}
564 
565 				if(KVSP_curCharPointer == pLabelBegin)
566 				{
567 					error(KVSP_curCharPointer, __tr2qs_ctx("Found character %q (Unicode %x) where a function name was expected", "kvs"), KVSP_curCharPointer, KVSP_curCharUnicode);
568 					delete pClass;
569 					return nullptr;
570 				}
571 				szLabel = QString(pLabelBegin, KVSP_curCharPointer - pLabelBegin);
572 			}
573 		}
574 
575 		if(!skipSpacesAndNewlines())
576 		{
577 			delete pClass;
578 			return nullptr;
579 		}
580 		QString szReminder;
581 		if(KVSP_curCharUnicode == '(')
582 		{
583 			KVSP_skipChar;
584 			const QChar * pReminderBegin = KVSP_curCharPointer;
585 			while((!(KVSP_curCharIsEndOfBuffer)) && (KVSP_curCharUnicode != ')'))
586 				KVSP_skipChar;
587 
588 			if(KVSP_curCharIsEndOfBuffer)
589 			{
590 				error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of buffer in function parameter list reminder", "kvs"));
591 				delete pClass;
592 				return nullptr;
593 			}
594 			szReminder = QString(pReminderBegin, KVSP_curCharPointer - pReminderBegin);
595 			KVSP_skipChar;
596 
597 			if(!skipSpacesAndNewlines())
598 			{
599 				delete pClass;
600 				return nullptr;
601 			}
602 		}
603 
604 		if(KVSP_curCharIsEndOfBuffer)
605 		{
606 			error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of buffer in class definition", "kvs"));
607 			delete pClass;
608 			return nullptr;
609 		}
610 
611 		if(KVSP_curCharUnicode != '{')
612 		{
613 			errorBadChar(KVSP_curCharPointer, '{', "class");
614 			delete pClass;
615 			return nullptr;
616 		}
617 
618 		pBegin = KVSP_curCharPointer;
619 		KviKvsTreeNodeInstruction * pInstruction = parseInstruction();
620 		if(!pInstruction)
621 		{
622 			// may be an empty instruction
623 			if(error())
624 			{
625 				delete pClass;
626 				return nullptr;
627 			}
628 		}
629 		delete pInstruction;
630 		int iLen = KVSP_curCharPointer - pBegin;
631 		QString szInstruction;
632 		if(iLen > 0)
633 		{
634 			szInstruction = QString(pBegin, KVSP_curCharPointer - pBegin);
635 			KviCommandFormatter::bufferFromBlock(szInstruction);
636 		}
637 
638 		pClass->addFunctionDefinition(new KviKvsTreeNodeSpecialCommandClassFunctionDefinition(pLabelBegin, szLabel, szInstruction, szReminder, uHandlerFlags));
639 
640 		if(!skipSpacesAndNewlines())
641 		{
642 			delete pClass;
643 			return nullptr;
644 		}
645 	}
646 
647 	KVSP_skipChar;
648 
649 	return pClass;
650 }
651 
parseSpecialCommandWhile()652 KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandWhile()
653 {
654 	/*
655 		@doc: while
656 		@type:
657 			command
658 		@title:
659 			while
660 		@syntax:
661 			while (<condition>) <command>
662 		@keyterms:
663 			iteration commands, flow control commands
664 		@short:
665 			Iteration command
666 		@description:
667 			Executes <command> while the <condition> evaluates
668 			to true (non zero result).[br]
669 			<command> may be either a single command or a block of commands.[br]
670 			It can contain the [cmd]break[/cmd] command: in that case the
671 			execution of the <command> will be immediately interrupted and the control
672 			transferred to the command following this while block.[br]
673 			It is valid for <command> to be an empty command terminated with a [b];[/b].
674 			<condition> is an expression as the ones evaluated by [doc:expressioneval]$(*)[/doc]
675 			with the following extensions:[br]
676 			If <condition> is a string, its length is evaluated: in this way a non-empty string
677 			causes the <condition> to be true, an empty string causes it to be false.[br]
678 			If <condition> is an array, its size is evaluated: in this way a non-empty array
679 			causes the <condition> to be true, an empty array causes it to be false.[br]
680 			If <condition> is a hash, the number of its entries is evaluated: in this way a non-empty hash
681 			causes the <condition> to be true, an empty hash causes it to be false.[br]
682 		@examples:
683 			[example]
684 				%i = 0;
685 				while(%i < 100)%i++
686 				while(%i > 0)
687 				{
688 					%i -= 10
689 					if(%i < 20)break;
690 				}
691 				echo %i
692 			[/example]
693 	*/
694 
695 	if(KVSP_curCharUnicode != '(')
696 	{
697 		warning(KVSP_curCharPointer, __tr2qs_ctx("The while command needs an expression enclosed in parenthesis", "kvs"));
698 		error(KVSP_curCharPointer, __tr2qs_ctx("Found character %q (Unicode %x) where an open parenthesis was expected", "kvs"), KVSP_curCharPointer, KVSP_curCharUnicode);
699 		return nullptr;
700 	}
701 
702 	const QChar * pBegin = KVSP_curCharPointer;
703 
704 	KVSP_skipChar;
705 
706 	KviKvsTreeNodeExpression * e = parseExpression(')');
707 	if(!e)
708 	{
709 		// must be an error
710 		return nullptr;
711 	}
712 
713 	if(!skipSpacesAndNewlines())
714 	{
715 		delete e;
716 		return nullptr;
717 	}
718 
719 	if(KVSP_curCharUnicode == 0)
720 	{
721 		warning(pBegin, __tr2qs_ctx("The last while command in the buffer has no conditional instructions: it's senseless", "kvs"));
722 		warning(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of script while looking for the instruction block of the while command", "kvs"));
723 	}
724 
725 	KviKvsTreeNodeInstruction * i = parseInstruction();
726 	if(!i)
727 	{
728 		if(error())
729 		{
730 			delete e;
731 			return nullptr;
732 		}
733 	} // else, just an empty instruction
734 
735 	return new KviKvsTreeNodeSpecialCommandWhile(pBegin, e, i);
736 }
737 
parseSpecialCommandDo()738 KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandDo()
739 {
740 	/*
741 		@doc: do
742 		@type:
743 			command
744 		@title:
745 			do
746 		@syntax:
747 			do <command> while (<condition>)
748 		@keyterms:
749 			iteration commands, flow control commands
750 		@short:
751 			Iteration command
752 		@description:
753 			Executes <command> once then evaluates the <condition>.
754 			If <condition> evaluates to true (non zero result) then repeats the execution again.[br]
755 			<command> may be either a single command or a block of commands.[br]
756 			It can contain the [cmd]break[/cmd] command: in that case the
757 			execution of the <command> will be immediately interrupted and the control
758 			transferred to the command following this while block.[br]
759 			It is valid for <command> to be an empty command terminated with a ';'.
760 			<condition> is an expression as the ones evaluated by [doc:expressioneval]$(*)[/doc]
761 			with the following extensions:[br]
762 			If <condition> is a string, its length is evaluated: in this way a non-empty string
763 			causes the <condition> to be true, an empty string causes it to be false.[br]
764 			If <condition> is an array, its size is evaluated: in this way a non-empty array
765 			causes the <condition> to be true, an empty array causes it to be false.[br]
766 			If <condition> is a hash, the number of its entries is evaluated: in this way a non-empty hash
767 			causes the <condition> to be true, an empty hash causes it to be false.[br]
768 		@examples:
769 			[example]
770 				%i = 0;
771 				do %i++; while(%i < 100);
772 				echo "After first execution:  %i";
773 				%i = 10
774 				do
775 				{
776 					echo "Executed!";
777 					%i++;
778 				}
779 				while(%i < 1)
780 				echo "After second execution:  %i";
781 			[/example]
782 		@seealso:
783 			[cmd]while[/cmd]
784 	*/
785 
786 	const QChar * pBegin = KVSP_curCharPointer;
787 
788 	KviKvsTreeNodeInstruction * i = parseInstruction();
789 	if(!i)
790 	{
791 		if(error())
792 			return nullptr;
793 	}
794 
795 	if(!skipSpacesAndNewlines())
796 	{
797 		if(i)
798 			delete i;
799 		return nullptr;
800 	}
801 
802 	static const unsigned short while_chars[10] = { 'W', 'w', 'H', 'h', 'I', 'i', 'L', 'l', 'E', 'e' };
803 
804 	for(int j = 0; j < 10; j++)
805 	{
806 		if(KVSP_curCharUnicode != while_chars[j])
807 		{
808 			j++;
809 			if(KVSP_curCharUnicode != while_chars[j])
810 			{
811 				if(KVSP_curCharIsEndOfBuffer)
812 					error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of command after the 'do' command block: expected 'while' keyword", "kvs"));
813 				else
814 					error(KVSP_curCharPointer, __tr2qs_ctx("Found character %q (Unicode %x) where a 'while' keyword was expected", "kvs"), KVSP_curCharPointer, KVSP_curCharUnicode);
815 				if(i)
816 					delete i;
817 				return nullptr;
818 			}
819 		}
820 		else
821 			j++;
822 		KVSP_skipChar;
823 	}
824 
825 	if(!skipSpacesAndNewlines())
826 	{
827 		if(i)
828 			delete i;
829 		return nullptr;
830 	}
831 
832 	if(KVSP_curCharUnicode != '(')
833 	{
834 		warning(KVSP_curCharPointer, __tr2qs_ctx("The 'while' block of the 'do' command needs an expression enclosed in parenthesis", "kvs"));
835 		errorBadChar(KVSP_curCharPointer, '(', "do");
836 		if(i)
837 			delete i;
838 		return nullptr;
839 	}
840 
841 	KVSP_skipChar;
842 
843 	KviKvsTreeNodeExpression * e = parseExpression(')');
844 	if(!e)
845 	{
846 		// must be an error
847 		if(i)
848 			delete i;
849 		return nullptr;
850 	}
851 
852 	skipSpaces();
853 
854 	if(!KVSP_curCharIsEndOfCommand)
855 	{
856 		warning(KVSP_curCharPointer, __tr2qs_ctx("Garbage string after the expression in 'do' command: ignored", "kvs"));
857 		while(!KVSP_curCharIsEndOfCommand)
858 			KVSP_skipChar;
859 	}
860 
861 	if(!KVSP_curCharIsEndOfBuffer)
862 		KVSP_skipChar;
863 
864 	return new KviKvsTreeNodeSpecialCommandDo(pBegin, e, i);
865 }
866 
parseSpecialCommandIf()867 KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandIf()
868 {
869 	/*
870 		@doc: if
871 		@type:
872 			command
873 		@title:
874 			if
875 		@syntax:
876 			if (<condition>) <command1> [else <command2>]
877 		@keyterms:
878 			conditional commands, flow control commands
879 		@short:
880 			Flow control command
881 		@description:
882 			Executes <command1> if the <condition> evaluates to true (non zero result).[br]
883 			If the [i]else part[/i] is given, <command2> is executed.[br]
884 			If the <condition> evaluates to false (result == '0').[br]
885 			the [b]<condition>[/b] is an expression evaluated in the same way as [doc:expressioneval]$(*)[/doc]
886 			with the following extensions:[br]
887 			If the [b]<condition>[/b] is a string, its length is evaluated - in this way a non-empty string
888 			causes the <condition> to be true, and an empty string causes it to be false.[br]
889 			If the [b]<condition>[/b] is an array, its size is evaluated - a non-empty array is true, an empty array is false.[br]
890 			If the [b]<condition>[/b] is a hash, the number of its entries is evaluated - a non-empty hash is true, an empty hash is false.[br]
891 		@examples:
892 			[example]
893 				if(%a != 10)
894 					[cmd]echo[/cmd] \%a was != 10
895 				else
896 					[cmd]echo[/cmd] \%a was 10!
897 			[/example]
898 		@seealso:
899 			[doc:expressioneval]Expression evaluation identifier[/doc]
900 
901 	*/
902 
903 	if(KVSP_curCharUnicode != '(')
904 	{
905 		warning(KVSP_curCharPointer, __tr2qs_ctx("The 'if' command needs an expression enclosed in parenthesis", "kvs"));
906 		errorBadChar(KVSP_curCharPointer, '(', "if");
907 		return nullptr;
908 	}
909 
910 	const QChar * pBegin = KVSP_curCharPointer;
911 
912 	KVSP_skipChar;
913 
914 	KviKvsTreeNodeExpression * e = parseExpression(')');
915 	if(!e)
916 	{
917 		// must be an error
918 		return nullptr;
919 	}
920 
921 	if(!skipSpacesAndNewlines())
922 	{
923 		delete e;
924 		return nullptr;
925 	}
926 
927 	if(KVSP_curCharUnicode == 0)
928 	{
929 		warning(pBegin, __tr2qs_ctx("The last if command in the buffer has no conditional instructions: it's senseless", "kvs"));
930 		warning(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of script while looking for the instruction block of the if command", "kvs"));
931 	}
932 
933 	KviKvsTreeNodeInstruction * i = parseInstruction();
934 	if(!i)
935 	{
936 		if(error())
937 		{
938 			delete e;
939 			return nullptr;
940 		}
941 	} // else, just an empty instruction
942 
943 	if(!skipSpacesAndNewlines())
944 	{
945 		if(i)
946 			delete i;
947 		return nullptr;
948 	}
949 
950 	const QChar * pElse = KVSP_curCharPointer;
951 
952 	if((KVSP_curCharUnicode != 'e') && (KVSP_curCharUnicode != 'E'))
953 		return new KviKvsTreeNodeSpecialCommandIf(pBegin, e, i, nullptr);
954 	KVSP_skipChar;
955 	if((KVSP_curCharUnicode != 'l') && (KVSP_curCharUnicode != 'L'))
956 	{
957 		KVSP_setCurCharPointer(pElse);
958 		return new KviKvsTreeNodeSpecialCommandIf(pBegin, e, i, nullptr);
959 	}
960 	KVSP_skipChar;
961 	if((KVSP_curCharUnicode != 's') && (KVSP_curCharUnicode != 'S'))
962 	{
963 		KVSP_setCurCharPointer(pElse);
964 		return new KviKvsTreeNodeSpecialCommandIf(pBegin, e, i, nullptr);
965 	}
966 	KVSP_skipChar;
967 	if((KVSP_curCharUnicode != 'e') && (KVSP_curCharUnicode != 'E'))
968 	{
969 		KVSP_setCurCharPointer(pElse);
970 		return new KviKvsTreeNodeSpecialCommandIf(pBegin, e, i, nullptr);
971 	}
972 	KVSP_skipChar;
973 	if(KVSP_curCharIsLetterOrNumber)
974 	{
975 		if((KVSP_curCharUnicode == 'i') || (KVSP_curCharUnicode == 'I'))
976 		{
977 			KVSP_skipChar;
978 			if((KVSP_curCharUnicode == 'f') || (KVSP_curCharUnicode == 'F'))
979 			{
980 				KVSP_skipChar;
981 				if(!KVSP_curCharIsLetterOrNumber)
982 				{
983 					// this is an "elseif"
984 					KVSP_backChar;
985 					KVSP_backChar;
986 					// point to if
987 					goto handle_else_instruction;
988 				}
989 				KVSP_backChar;
990 			}
991 			KVSP_backChar;
992 		}
993 
994 		KVSP_setCurCharPointer(pElse);
995 		return new KviKvsTreeNodeSpecialCommandIf(pBegin, e, i, nullptr);
996 	}
997 
998 handle_else_instruction:
999 	if(!skipSpacesAndNewlines())
1000 	{
1001 		delete e;
1002 		if(i)
1003 			delete i;
1004 		return nullptr;
1005 	}
1006 
1007 	KviKvsTreeNodeInstruction * i2 = parseInstruction();
1008 	if(!i2)
1009 	{
1010 		if(error())
1011 		{
1012 			delete e;
1013 			if(i)
1014 				delete i;
1015 			return nullptr;
1016 		}
1017 	} // else, just an empty instruction
1018 
1019 	return new KviKvsTreeNodeSpecialCommandIf(pBegin, e, i, i2);
1020 }
1021 
skipToEndOfForControlBlock()1022 bool KviKvsParser::skipToEndOfForControlBlock()
1023 {
1024 	bool bInString = false;
1025 	int iParLevel = 0;
1026 
1027 	for(;;)
1028 	{
1029 		switch(KVSP_curCharUnicode)
1030 		{
1031 			case '"':
1032 				bInString = !bInString;
1033 				KVSP_skipChar;
1034 				break;
1035 			case '(':
1036 				if(!bInString)
1037 					iParLevel++;
1038 				KVSP_skipChar;
1039 				break;
1040 			case ')':
1041 				if(!bInString)
1042 				{
1043 					if(iParLevel == 0)
1044 						return true;
1045 					else
1046 						iParLevel--;
1047 				}
1048 				KVSP_skipChar;
1049 				break;
1050 			case 0:
1051 				error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of buffer while looking for the closing ')' in the 'for' command", "kvs"));
1052 				return false;
1053 				break;
1054 			//case '\n':
1055 			// that's ok.. it may have a parenthesis on the next line
1056 			//KVSP_skipChar;
1057 			//break;
1058 			default:
1059 				KVSP_skipChar;
1060 				break;
1061 		}
1062 	}
1063 	// not reached
1064 	KVSP_ASSERT(false);
1065 	return false;
1066 }
1067 
parseSpecialCommandFor()1068 KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandFor()
1069 {
1070 	/*
1071 		@doc: for
1072 		@type:
1073 			command
1074 		@title:
1075 			for
1076 		@syntax:
1077 			for (<initialization>;<condition>;<update>) <command>
1078 		@keyterms:
1079 			iterational control commands
1080 		@short:
1081 			Iteration control command
1082 		@description:
1083 			Executes <initialization> once then runs the following iteration loop:
1084 			if <condition> evaluates to true then <command> is executed followed
1085 			by the <update> command. The iteration is repeated until <condition> evaluates to false.[br]
1086 			<condition> is an expression as the ones evaluated by [doc:expressioneval]$(*)[/doc]
1087 			with the following extensions:[br]
1088 			If <condition> is a string, its length is evaluated: in this way a non-empty string
1089 			causes the <condition> to be true, an empty string causes it to be false.[br]
1090 			If <condition> is an array, its size is evaluated: in this way a non-empty array
1091 			causes the <condition> to be true, an empty array causes it to be false.[br]
1092 			If <condition> is a hash, the number of its entries is evaluated: in this way a non-empty hash
1093 			causes the <condition> to be true, an empty hash causes it to be false.[br]
1094 		@examples:
1095 			[example]
1096 				for(%a = 0;%a < 100;%a++)
1097 					echo %a
1098 			[/example]
1099 	*/
1100 
1101 	if(KVSP_curCharUnicode != '(')
1102 	{
1103 		warning(KVSP_curCharPointer, __tr2qs_ctx("The 'for' command needs an expression enclosed in parenthesis", "kvs"));
1104 		errorBadChar(KVSP_curCharPointer, '(', "for");
1105 		return nullptr;
1106 	}
1107 
1108 	const QChar * pForBegin = KVSP_curCharPointer;
1109 
1110 	KVSP_skipChar;
1111 
1112 	skipSpaces();
1113 
1114 	KviKvsTreeNodeInstruction * i1 = parseInstruction();
1115 	if(!i1)
1116 	{
1117 		if(error())
1118 			return nullptr;
1119 	} // else just empty instruction
1120 
1121 	skipSpaces();
1122 
1123 	KviKvsTreeNodeExpression * e = parseExpression(';');
1124 	if(!e)
1125 	{
1126 		if(error())
1127 		{
1128 			if(i1)
1129 				delete i1;
1130 			return nullptr;
1131 		}
1132 	} // else just empty expression : assume true
1133 
1134 	skipSpaces();
1135 
1136 	// skip to the first non matching ')' that is not in a string
1137 
1138 	const QChar * pBegin = KVSP_curCharPointer;
1139 
1140 	if(!skipToEndOfForControlBlock())
1141 	{
1142 		if(error()) // <-- that's always true
1143 		{
1144 			if(i1)
1145 				delete i1;
1146 			if(e)
1147 				delete e;
1148 			return nullptr;
1149 		}
1150 	}
1151 
1152 	// HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK
1153 	// KVSP_curCharPointer is const!
1154 	// we shouldn't be able to modify it
1155 	QChar cSave = *(KVSP_curCharPointer);
1156 
1157 	QChar * pHack = (QChar *)KVSP_curCharPointer;
1158 	*pHack = QChar('\n');
1159 
1160 	KVSP_curCharPointer = pBegin;
1161 
1162 	KviKvsTreeNodeInstruction * i2 = parseInstruction();
1163 	*pHack = cSave;
1164 
1165 	KVSP_setCurCharPointer(pHack);
1166 	// EOF HACK EOF HACK EOF HACK EOF HACK EOF HACK EOF HACK EOF HACK
1167 
1168 	if(!i2)
1169 	{
1170 		if(error())
1171 		{
1172 			if(i1)
1173 				delete i1;
1174 			if(e)
1175 				delete e;
1176 			return nullptr;
1177 		}
1178 	} // else just empty instruction
1179 
1180 	skipSpaces();
1181 
1182 	if(KVSP_curCharUnicode != ')')
1183 	{
1184 		error(KVSP_curCharPointer, __tr2qs_ctx("Found char %q (Unicode %x) while looking for the terminating ')' in 'for' command", "kvs"), KVSP_curCharPointer, KVSP_curCharUnicode);
1185 		if(i1)
1186 			delete i1;
1187 		if(e)
1188 			delete e;
1189 		if(i2)
1190 			delete i2;
1191 		return nullptr;
1192 	}
1193 
1194 	KVSP_skipChar;
1195 
1196 	if(!skipSpacesAndNewlines())
1197 	{
1198 		if(i1)
1199 			delete i1;
1200 		if(e)
1201 			delete e;
1202 		if(i2)
1203 			delete i2;
1204 		return nullptr;
1205 	}
1206 
1207 	KviKvsTreeNodeInstruction * loop = parseInstruction();
1208 	if(!loop)
1209 	{
1210 		if(error())
1211 		{
1212 			if(i1)
1213 				delete i1;
1214 			if(e)
1215 				delete e;
1216 			if(i2)
1217 				delete i2;
1218 			return nullptr;
1219 		}
1220 
1221 		if((!i1) && (!e) && (!i2))
1222 		{
1223 			error(pForBegin, __tr2qs_ctx("Empty infinite 'for' loop: fix the script", "kvs"));
1224 			return nullptr;
1225 		}
1226 	} // else just an empty instruction
1227 
1228 	return new KviKvsTreeNodeSpecialCommandFor(pForBegin, i1, e, i2, loop);
1229 }
1230 
parseSpecialCommandForeach()1231 KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandForeach()
1232 {
1233 	/*
1234 		@doc: foreach
1235 		@type:
1236 			command
1237 		@title:
1238 			foreach
1239 		@syntax:
1240 			foreach [-a] (<variable>,[<item>[,<item>[,<item>[...]]]) <command>
1241 		@keyterms:
1242 			iteration commands, flow control commands
1243 		@switches:
1244 			!sw: -a | --all
1245 			Include empty variables in the iteration loop.
1246 		@short:
1247 			Iteration command
1248 		@description:
1249 			Executed <command> while assigning to <variable> each <item>.[br]
1250 			<item> may be a constant, a variable, an array, a dictionary or a function returning
1251 			either a constant string an array reference or a dictionary reference.[br]
1252 			If <item> is an array, a dictionary or a function that returns a dictionary or array reference
1253 			the iteration is done through all the dictionary/array items.[br]
1254 			Please note that the iteration order of dictionary items is undefined.[br]
1255 			You can always break from the loop by using the [cmd]break[/cmd] command,
1256 			or skip to the next item with continue.[br]
1257 			foreach doesn't iterate over empty scalar variables (i.e. the ones set to [fnc]$nothing[/fnc])
1258 			unless you use the -a switch. (Note that an array with *some* empty entries is [b]not[/b] empty so
1259 			the iteration is in fact done).
1260 		@examples:
1261 			[example]
1262 				foreach(%i,1,2,3,4,5,6,7,8,9)[cmd]echo[/cmd] %i
1263 				foreach(%chan,[fnc]$window.list[/fnc](channel))[cmd]me[/cmd] -r=%chan This is a test!
1264 				[comment]#  This will work too, and will do the same job[/comment]
1265 				%windows[] = [fnc]$window.list[/fnc](channel)
1266 				foreach(%chan,%windows[])[cmd]me[/cmd] -r=%chan This is a test!
1267 				[comment]#  And this too[/comment]
1268 				%windows[] = [fnc]$window.list[/fnc](channel)
1269 				foreach(%key,[fnc]$keys[/fnc](%windows[]))[cmd]me[/cmd] -r=%windows[%key] This is a test!
1270 				[comment]#  Another interesting example[/comment]
1271 				[cmd]alias[/cmd](test){ [cmd]return[/cmd] [fnc]$hash[/fnc](1,a,2,b,3,c,4,d); };
1272 				foreach(%x,[fnc]$keys[/fnc]($test)){ [cmd]echo[/cmd] %x, $test{%x}; }
1273 			[/example]
1274 	*/
1275 
1276 	if(KVSP_curCharUnicode != '(')
1277 	{
1278 		warning(KVSP_curCharPointer, __tr2qs_ctx("The 'foreach' command needs an expression enclosed in parenthesis", "kvs"));
1279 		errorBadChar(KVSP_curCharPointer, '(', "foreach");
1280 		return nullptr;
1281 	}
1282 
1283 	const QChar * pForeachBegin = KVSP_curCharPointer;
1284 
1285 	KVSP_skipChar;
1286 
1287 	skipSpaces();
1288 
1289 	if((KVSP_curCharUnicode != '%') && (KVSP_curCharUnicode != '$') && (KVSP_curCharUnicode != '@'))
1290 	{
1291 		warning(KVSP_curCharPointer, __tr2qs_ctx("The 'foreach' command expects a writable iteration variable as first parameter", "kvs"));
1292 		error(KVSP_curCharPointer, __tr2qs_ctx("Found character '%q' (Unicode %x) where '%' or '$' was expected: see /help foreach for the command syntax", "kvs"), KVSP_curCharPointer, KVSP_curCharUnicode);
1293 		return nullptr;
1294 	}
1295 
1296 	KviKvsTreeNodeData * d = parsePercentOrDollar();
1297 	if(!d)
1298 		return nullptr;
1299 
1300 	if(d->isFunctionCall() || d->isReadOnly())
1301 	{
1302 		warning(KVSP_curCharPointer, __tr2qs_ctx("The 'foreach' command expects a writable iteration variable as first parameter", "kvs"));
1303 		if(d->isFunctionCall())
1304 			error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected function call as 'foreach' iteration variable", "kvs"));
1305 		else
1306 			error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected read-only variable as 'foreach' iteration variable", "kvs"));
1307 		delete d;
1308 		return nullptr;
1309 	}
1310 
1311 	skipSpaces();
1312 	if(KVSP_curCharUnicode != ',')
1313 	{
1314 		if(KVSP_curCharUnicode == ')')
1315 		{
1316 			error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of 'foreach' parameters: at least one iteration data argument must be given", "kvs"));
1317 			delete d;
1318 			return nullptr;
1319 		}
1320 		warning(KVSP_curCharPointer, __tr2qs_ctx("The 'foreach' command expects a comma separated list of iteration data items after the first parameter", "kvs"));
1321 		errorBadChar(KVSP_curCharPointer, ',', "foreach");
1322 		return nullptr;
1323 	}
1324 
1325 	KviKvsTreeNodeDataList * l = parseCommaSeparatedParameterList();
1326 	if(!l)
1327 		return nullptr;
1328 
1329 	if(!skipSpacesAndNewlines())
1330 	{
1331 		delete d;
1332 		delete l;
1333 		return nullptr;
1334 	}
1335 
1336 	const QChar * pLoopBegin = KVSP_curCharPointer;
1337 
1338 	KviKvsTreeNodeInstruction * loop = parseInstruction();
1339 	if(!loop)
1340 	{
1341 		if(error())
1342 			return nullptr;
1343 		warning(pLoopBegin, __tr2qs_ctx("Found empty 'foreach' execution block: maybe you need to fix your script?", "kvs"));
1344 		loop = new KviKvsTreeNodeInstructionBlock(pLoopBegin);
1345 	}
1346 
1347 	return new KviKvsTreeNodeSpecialCommandForeach(pForeachBegin, d, l, loop);
1348 }
1349 
parseSpecialCommandSwitch()1350 KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandSwitch()
1351 {
1352 	/*
1353 		@doc: switch
1354 		@type:
1355 			command
1356 		@title:
1357 			switch
1358 		@syntax:
1359 			switch [-p] (<expression>)
1360 			{
1361 				case(<value>)[:]<command>
1362 				[break]
1363 				case(<value>)[:]<command>
1364 				[break]
1365 				....
1366 				match(<wildcard_expression>)[:]<command>
1367 				[break]
1368 				....
1369 				regexp(<regular_expression>)[:]<command>
1370 				[break]
1371 				....
1372 				case(<value>)[:]<command>
1373 				[break]
1374 				....
1375 				default[:]<command>
1376 				[break]
1377 			}
1378 		@switches:
1379 			!sw: -p | --passthrough
1380 			Use c-style 'break' flow control
1381 		@short:
1382 			Another flow control command
1383 		@description:
1384 			The switch command is based on the standard C 'switch' keyword.
1385 			It conditionally executes groups of commands chosen from a larger set of command groups.[br]
1386 			First <expression> is evaluated (<expression> is any arithmetic or string expression).[br]
1387 			Then the [b]match[/b], [b]regexp[/b], [b]case[/b] and [b]default[/b] labels are evaluated sequentially
1388 			in the order of appearance.[br]
1389 			[b]case(<value>)[:]<command>[/b][br]
1390 			The <value> is evaluated and is compared against the result of <expression>.
1391 			String comparison is case insensitive.[br]
1392 			If <value> is equal to <expression> then <command> is executed.
1393 			Please note that <command> must be either a single instruction or an instruction block [b]enclosed in braces[/b].
1394 			During or after <command> execution, if a [cmd]break[/cmd] statement is encountered the execution of the switch
1395 			is terminated, otherwise the next label is evaluated.[br]
1396 			If the -p (--passthrough) option is enabled, than the switch command will execute all the instructions blocks
1397 			until a [cmd]break[/cmd] statement is found.[br]
1398 			[b]match(<value>)[:]<command>[/b][br]
1399 			The <value> is expected to be a wildcard expression (wildcard characters being [b]*[/b] and [b]?[/b])
1400 			that is matched in a case insensitive fashion against <expression>.[br]
1401 			If a match occurs, the related <command> is executed.
1402 			The [cmd]break[/cmd] statement is treated as in the case label.[br]
1403 			[b]regexp(<value>)[:]<command>[/b][br]
1404 			The <value> is expected to be a complete standard regular expression
1405 			that is matched in a case insensitive fashion against <expression>.[br]
1406 			If a match occurs, the related <command> is executed.
1407 			The [cmd]break[/cmd] statement is treated as in the case label.[br]
1408 			[b]default[:]<command>[/b][br]
1409 			The default label is executed unconditionally if no previous label terminated execution with the [cmd]break[/cmd] statement.[br]
1410 		@examples:
1411 			[comment]# Try to change the 1 below to 2 or 3 to see the results[/comment]
1412 			[example]
1413 				%tmp = 1
1414 				switch(%tmp)
1415 				{
1416 					case(1):
1417 						echo \%tmp was 1!
1418 					break;
1419 					case(2)
1420 						echo \%tmp was 2!
1421 					break;
1422 					default:
1423 						echo \%tmp was not 1 nor 2: it was %tmp!
1424 					break;
1425 				}
1426 			[/example]
1427 			[example]
1428 				[comment]# A more complex example: change the 1 in 2 or 3[/comment]
1429 				%tmp = 1
1430 				switch(%tmp)
1431 				{
1432 					case(1):
1433 						echo \%tmp was 1!
1434 				case(2)
1435 						echo \%tmp was 2!
1436 					break;
1437 					default:
1438 						echo \%tmp was either 1 or something different from 2 (%tmp)
1439 					break;
1440 				}
1441 			[/example]
1442 			[example]
1443 				[comment]# An example of the -p switch[/comment]
1444 				%tmp = 1
1445 				switch -p (%tmp)
1446 				{
1447 					case(1):
1448 						echo \%tmp was 1!
1449 					case(2)
1450 						echo \%tmp was 1 or 2!
1451 						break;
1452 					default:
1453 						echo \%tmp was not 1 or 2 (%tmp)
1454 					break;
1455 				}
1456 			[/example]
1457 			[example]
1458 				[comment]# An example with strings[/comment]
1459 				%tmp = "This is a test"
1460 				%tmp2 = "This is not a test"
1461 				switch(%tmp)
1462 				{
1463 					case(%tmp2)
1464 						echo \%tmp == \%tmp2
1465 						break;
1466 					case(%tmp)
1467 					{
1468 						[comment]# do not break here[/comment]
1469 						echo "Yeah... It's stupid... \%tmp == \%tmp :D"
1470 					}
1471 					match("*TEST"):
1472 						echo "Matched *TEST"
1473 					regexp("[a-zA-Z ]*test"):
1474 						echo "Matched [a-zA-Z ]*text"
1475 					regexp("[a-zA-Z ]*not[a-zA-Z ]*"):
1476 						echo "Matched [a-zA-Z ]*not[a-zA-Z ]*"
1477 					default:
1478 						echo This is executed anyway (unless some break was called)
1479 					break;
1480 				}
1481 			[/example]
1482 */
1483 
1484 	if(KVSP_curCharUnicode != '(')
1485 	{
1486 		warning(KVSP_curCharPointer, __tr2qs_ctx("The 'switch' command needs an expression enclosed in parenthesis", "kvs"));
1487 		errorBadChar(KVSP_curCharPointer, '(', "switch");
1488 		return nullptr;
1489 	}
1490 
1491 	const QChar * pBegin = KVSP_curCharPointer;
1492 
1493 	KVSP_skipChar;
1494 
1495 	KviKvsTreeNodeExpression * e = parseExpression(')');
1496 	if(!e)
1497 	{
1498 		// must be an error
1499 		return nullptr;
1500 	}
1501 
1502 	if(!skipSpacesAndNewlines())
1503 	{
1504 		delete e;
1505 		return nullptr;
1506 	}
1507 
1508 	if(KVSP_curCharUnicode != '{')
1509 	{
1510 		errorBadChar(KVSP_curCharPointer, '{', "switch");
1511 		delete e;
1512 		return nullptr;
1513 	}
1514 
1515 	KVSP_skipChar;
1516 
1517 	if(!skipSpacesAndNewlines())
1518 	{
1519 		delete e;
1520 		return nullptr;
1521 	}
1522 
1523 	KviKvsTreeNodeSpecialCommandSwitch * pSwitch = new KviKvsTreeNodeSpecialCommandSwitch(pBegin, e);
1524 
1525 	KviKvsTreeNodeSpecialCommandSwitchLabel * pLabel = nullptr;
1526 
1527 	while(KVSP_curCharUnicode != '}')
1528 	{
1529 		// look for a 'case','match','default' or 'regexpr' label
1530 
1531 		const QChar * pLabelBegin = KVSP_curCharPointer;
1532 		while(KVSP_curCharIsLetter)
1533 			KVSP_skipChar;
1534 
1535 		if(KVSP_curCharIsEndOfBuffer)
1536 		{
1537 			error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of buffer in switch condition block", "kvs"));
1538 			delete pSwitch;
1539 			return nullptr;
1540 		}
1541 
1542 		if(KVSP_curCharPointer == pLabelBegin)
1543 		{
1544 			error(KVSP_curCharPointer, __tr2qs_ctx("Found character %q (Unicode %x) where a 'case','match','regexp','default' or 'break' label was expected", "kvs"), KVSP_curCharPointer, KVSP_curCharUnicode);
1545 			delete pSwitch;
1546 			return nullptr;
1547 		}
1548 
1549 		QString szLabel(pLabelBegin, KVSP_curCharPointer - pLabelBegin);
1550 		QString szLabelLow = szLabel.toLower();
1551 
1552 		bool bNeedParam = true;
1553 
1554 		if(szLabelLow == "case")
1555 		{
1556 			pLabel = new KviKvsTreeNodeSpecialCommandSwitchLabelCase(pLabelBegin);
1557 		}
1558 		else if(szLabelLow == "match")
1559 		{
1560 			pLabel = new KviKvsTreeNodeSpecialCommandSwitchLabelMatch(pLabelBegin);
1561 		}
1562 		else if(szLabelLow == "regexp")
1563 		{
1564 			pLabel = new KviKvsTreeNodeSpecialCommandSwitchLabelRegexp(pLabelBegin);
1565 		}
1566 		else if(szLabelLow == "default")
1567 		{
1568 			pLabel = new KviKvsTreeNodeSpecialCommandSwitchLabelDefault(pLabelBegin);
1569 			bNeedParam = false;
1570 		}
1571 		else if(szLabelLow == "break")
1572 		{
1573 			if(pLabel)
1574 			{
1575 				pLabel->setTerminatingBreak(true);
1576 				skipSpaces();
1577 				if(KVSP_curCharUnicode == ';')
1578 					KVSP_skipChar;
1579 				if(!skipSpacesAndNewlines())
1580 				{
1581 					delete pSwitch;
1582 					delete pLabel;
1583 					return nullptr;
1584 				}
1585 				continue;
1586 			}
1587 			else
1588 			{
1589 				error(pLabelBegin, __tr2qs_ctx("Found 'break' label where a 'case','match','regexp' or 'default' label was expected", "kvs"));
1590 				delete pSwitch;
1591 				return nullptr;
1592 			}
1593 		}
1594 		else
1595 		{
1596 			error(pLabelBegin, __tr2qs_ctx("Found token '%Q' where a 'case','match','regexp','default' or 'break' label was expected", "kvs"), &szLabel);
1597 			delete pSwitch;
1598 			return nullptr;
1599 		}
1600 
1601 		if(bNeedParam)
1602 		{
1603 			skipSpaces();
1604 			if(KVSP_curCharUnicode != '(')
1605 			{
1606 				errorBadChar(KVSP_curCharPointer, '(', "switch");
1607 				delete pSwitch;
1608 				delete pLabel;
1609 				return nullptr;
1610 			}
1611 			KVSP_skipChar;
1612 
1613 			KviKvsTreeNodeData * pParameter = parseSingleParameterInParenthesis();
1614 			if(!pParameter)
1615 			{
1616 				delete pSwitch;
1617 				delete pLabel;
1618 				return nullptr;
1619 			}
1620 
1621 			pLabel->setParameter(pParameter);
1622 		}
1623 
1624 		skipSpaces();
1625 		if(KVSP_curCharUnicode == ':')
1626 			KVSP_skipChar;
1627 		if(!skipSpacesAndNewlines())
1628 		{
1629 			delete pSwitch;
1630 			delete pLabel;
1631 			return nullptr;
1632 		}
1633 
1634 		KviKvsTreeNodeInstruction * pInstruction = parseInstruction();
1635 		if(!pInstruction)
1636 		{
1637 			// may be an empty instruction
1638 			if(error())
1639 			{
1640 				delete pSwitch;
1641 				delete pLabel;
1642 				return nullptr;
1643 			}
1644 		}
1645 
1646 		pLabel->setInstruction(pInstruction);
1647 		pSwitch->addLabel(pLabel);
1648 
1649 		if(!skipSpacesAndNewlines())
1650 		{
1651 			delete pSwitch;
1652 			return nullptr;
1653 		}
1654 	}
1655 
1656 	KVSP_skipChar;
1657 
1658 	if(pSwitch->isEmpty())
1659 	{
1660 		error(pBegin, __tr2qs_ctx("Senseless empty switch command: fix the script", "kvs"));
1661 		delete pSwitch;
1662 		return nullptr;
1663 	}
1664 
1665 	return pSwitch;
1666 }
1667 
parseSpecialCommandDefpopupLabelPopup()1668 KviKvsTreeNodeSpecialCommandDefpopupLabelPopup * KviKvsParser::parseSpecialCommandDefpopupLabelPopup()
1669 {
1670 	if(KVSP_curCharUnicode != '{')
1671 	{
1672 		errorBadChar(KVSP_curCharPointer, '{', "defpopup");
1673 		return nullptr;
1674 	}
1675 
1676 	std::unique_ptr<KviKvsTreeNodeSpecialCommandDefpopupLabelPopup> pPopup{new KviKvsTreeNodeSpecialCommandDefpopupLabelPopup(KVSP_curCharPointer)};
1677 
1678 	KVSP_skipChar;
1679 
1680 	if(!skipSpacesAndNewlines())
1681 		return nullptr;
1682 
1683 	while(KVSP_curCharUnicode != '}')
1684 	{
1685 		// look for 'label', 'prologue', 'epilogue', 'popup', 'item', 'separator', 'separatorid' or 'extpopup' label
1686 		const QChar * pLabelBegin = KVSP_curCharPointer;
1687 		while(KVSP_curCharIsLetter)
1688 			KVSP_skipChar;
1689 
1690 		if(KVSP_curCharIsEndOfBuffer)
1691 		{
1692 			error(KVSP_curCharPointer, __tr2qs_ctx("Unexpected end of buffer in defpopup block", "kvs"));
1693 			return nullptr;
1694 		}
1695 
1696 		if(KVSP_curCharPointer == pLabelBegin)
1697 		{
1698 			error(KVSP_curCharPointer, __tr2qs_ctx("Found character %q (Unicode %x) where a 'prologue', 'separator', 'separatorid', 'label', 'popup', 'item', 'extpopup' or 'epilogue' label was expected", "kvs"), KVSP_curCharPointer, KVSP_curCharUnicode);
1699 			return nullptr;
1700 		}
1701 
1702 		QString szLabel(pLabelBegin, KVSP_curCharPointer - pLabelBegin);
1703 		QString szLabelLow = szLabel.toLower();
1704 
1705 		KviPointerList<QString> * pParameters = nullptr;
1706 
1707 		QString szCondition;
1708 
1709 #define EXTRACT_POPUP_LABEL_PARAMETERS                      \
1710 	if(!skipSpacesAndNewlines())                            \
1711 		return nullptr;                                     \
1712 	if(KVSP_curCharUnicode != '(')                          \
1713 	{                                                       \
1714 		errorBadChar(KVSP_curCharPointer, '(', "defpopup"); \
1715 		return nullptr;                                     \
1716 	}                                                       \
1717 	pParameters = parseCommaSeparatedParameterListNoTree(); \
1718 	if(!pParameters)                                        \
1719 		return nullptr;
1720 
1721 #define EXTRACT_POPUP_LABEL_CONDITION                                  \
1722 	if(!skipSpacesAndNewlines())                                       \
1723 		return nullptr;                                                \
1724 	if(KVSP_curCharUnicode == '(')                                     \
1725 	{                                                                  \
1726 		const QChar * pBegin = KVSP_curCharPointer;                    \
1727 		KVSP_skipChar;                                                 \
1728 		KviKvsTreeNodeExpression * pExpression = parseExpression(')'); \
1729 		if(!pExpression)                                               \
1730 		{                                                              \
1731 			if(pParameters)                                            \
1732 				delete pParameters;                                    \
1733 			return nullptr;                                            \
1734 		}                                                              \
1735 		int cLen = (KVSP_curCharPointer - pBegin) - 2;                 \
1736 		if(cLen > 0)                                                   \
1737 			szCondition.setUnicode(pBegin + 1, cLen);                  \
1738 		delete pExpression;                                            \
1739 		if(!skipSpacesAndNewlines())                                   \
1740 		{                                                              \
1741 			if(pParameters)                                            \
1742 				delete pParameters;                                    \
1743 			return nullptr;                                            \
1744 		}                                                              \
1745 	}
1746 
1747 		if((szLabelLow == "prologue") || (szLabelLow == "epilogue"))
1748 		{
1749 			bool bPrologue = (szLabelLow == "prologue");
1750 			if(!skipSpacesAndNewlines())
1751 				return nullptr;
1752 
1753 			if(KVSP_curCharUnicode == '(')
1754 			{
1755 				EXTRACT_POPUP_LABEL_PARAMETERS
1756 				if(!skipSpacesAndNewlines())
1757 				{
1758 					if(pParameters)
1759 						delete pParameters;
1760 					return nullptr;
1761 				}
1762 			}
1763 			const QChar * pBegin = KVSP_curCharPointer;
1764 			KviKvsTreeNodeInstruction * pInstruction = parseInstruction();
1765 			if(pInstruction)
1766 			{
1767 				// in fact we don't need it at all, we just need the code buffer...
1768 				delete pInstruction;
1769 			}
1770 			else
1771 			{
1772 				// may be an empty instruction
1773 				if(error())
1774 				{
1775 					// error
1776 					if(pParameters)
1777 						delete pParameters;
1778 					return nullptr;
1779 				}
1780 				// empty instruction
1781 				if(bPrologue)
1782 					warning(pBegin, __tr2qs_ctx("Found empty prologue block: maybe you need to fix the script?", "kvs"));
1783 				else
1784 					warning(pBegin, __tr2qs_ctx("Found empty epilogue block: maybe you need to fix the script?", "kvs"));
1785 			}
1786 			int iLen = KVSP_curCharPointer - pBegin;
1787 			if(iLen > 0)
1788 			{
1789 				QString szInstruction(pBegin, KVSP_curCharPointer - pBegin);
1790 				KviCommandFormatter::bufferFromBlock(szInstruction);
1791 				QString * pItemName = pParameters ? pParameters->first() : nullptr;
1792 				QString szItemName = pItemName ? *pItemName : QString();
1793 				if(bPrologue)
1794 					pPopup->addLabel(new KviKvsTreeNodeSpecialCommandDefpopupLabelPrologue(pLabelBegin, szInstruction, szItemName));
1795 				else
1796 					pPopup->addLabel(new KviKvsTreeNodeSpecialCommandDefpopupLabelEpilogue(pLabelBegin, szInstruction, szItemName));
1797 			} // else the instruction was empty anyway: we don't need it in fact
1798 			if(pParameters)
1799 				delete pParameters;
1800 		}
1801 		else if(szLabelLow == "separator")
1802 		{
1803 			EXTRACT_POPUP_LABEL_CONDITION
1804 			if(KVSP_curCharUnicode == ';')
1805 				KVSP_skipChar;
1806 			QString szItemName = "dummySeparator";
1807 			pPopup->addLabel(new KviKvsTreeNodeSpecialCommandDefpopupLabelSeparator(pLabelBegin, szCondition, szItemName));
1808 		}
1809 		else if(szLabelLow == "separatorid")
1810 		{
1811 			EXTRACT_POPUP_LABEL_PARAMETERS
1812 			EXTRACT_POPUP_LABEL_CONDITION
1813 			if(KVSP_curCharUnicode == ';')
1814 				KVSP_skipChar;
1815 			QString * pItemName = pParameters->first();
1816 			pPopup->addLabel(new KviKvsTreeNodeSpecialCommandDefpopupLabelSeparator(pLabelBegin, szCondition, pItemName ? *pItemName : QString()));
1817 			delete pParameters;
1818 		}
1819 		else if(szLabelLow == "label")
1820 		{
1821 			EXTRACT_POPUP_LABEL_PARAMETERS
1822 			EXTRACT_POPUP_LABEL_CONDITION
1823 
1824 			QString * pText = pParameters->first();
1825 			if(!pText)
1826 			{
1827 				error(pLabelBegin, __tr2qs_ctx("Unexpected empty <text> field in label parameters. See /help defpopup for the syntax", "kvs"));
1828 				delete pParameters;
1829 				return nullptr;
1830 			}
1831 			QString * pIcon = pParameters->next();
1832 			if(KVSP_curCharUnicode == ';')
1833 				KVSP_skipChar;
1834 			QString * pItemName = pParameters->next();
1835 			pPopup->addLabel(new KviKvsTreeNodeSpecialCommandDefpopupLabelLabel(pLabelBegin, szCondition, *pText, pIcon ? *pIcon : QString(), pItemName ? *pItemName : QString()));
1836 			delete pParameters;
1837 		}
1838 		else if(szLabelLow == "popup")
1839 		{
1840 			EXTRACT_POPUP_LABEL_PARAMETERS
1841 			EXTRACT_POPUP_LABEL_CONDITION
1842 
1843 			QString * pText = pParameters->first();
1844 			if(!pText)
1845 			{
1846 				error(pLabelBegin, __tr2qs_ctx("Unexpected empty <text> field in extpopup parameters. See /help defpopup for the syntax", "kvs"));
1847 				delete pParameters;
1848 				return nullptr;
1849 			}
1850 			QString * pIcon = pParameters->next();
1851 			QString * pItemName = pParameters->next();
1852 
1853 			KviKvsTreeNodeSpecialCommandDefpopupLabelPopup * pSubPopup = parseSpecialCommandDefpopupLabelPopup();
1854 			if(!pSubPopup)
1855 			{
1856 				delete pParameters;
1857 				return nullptr;
1858 			}
1859 
1860 			pSubPopup->setCondition(szCondition);
1861 			pSubPopup->setText(*pText);
1862 			pSubPopup->setItemName(pItemName ? *pItemName : QString());
1863 			if(pIcon)
1864 				pSubPopup->setIcon(*pIcon);
1865 			pPopup->addLabel(pSubPopup);
1866 			delete pParameters;
1867 		}
1868 		else if(szLabelLow == "item")
1869 		{
1870 			EXTRACT_POPUP_LABEL_PARAMETERS
1871 			EXTRACT_POPUP_LABEL_CONDITION
1872 
1873 			QString * pText = pParameters->first();
1874 			if(!pText)
1875 			{
1876 				error(pLabelBegin, __tr2qs_ctx("Unexpected empty <text> field in extpopup parameters. See /help defpopup for the syntax", "kvs"));
1877 				delete pParameters;
1878 				return nullptr;
1879 			}
1880 			QString * pIcon = pParameters->next();
1881 			QString * pItemName = pParameters->next();
1882 
1883 			const QChar * pBegin = KVSP_curCharPointer;
1884 			KviKvsTreeNodeInstruction * pInstruction = parseInstruction();
1885 			if(pInstruction)
1886 			{
1887 				// in fact we don't need it: we just need the code block
1888 				delete pInstruction;
1889 			}
1890 			else
1891 			{
1892 				// empty instruction or error ?
1893 				if(error())
1894 				{
1895 					// error
1896 					delete pParameters;
1897 					return nullptr;
1898 				}
1899 				// empty instruction
1900 				warning(pBegin, __tr2qs_ctx("Found empty instruction for popup item: maybe you need to fix the script?", "kvs"));
1901 			}
1902 			int iLen = KVSP_curCharPointer - pBegin;
1903 			if(iLen > 0)
1904 			{
1905 				QString szInstruction(pBegin, KVSP_curCharPointer - pBegin);
1906 				KviCommandFormatter::bufferFromBlock(szInstruction);
1907 				pPopup->addLabel(new KviKvsTreeNodeSpecialCommandDefpopupLabelItem(pLabelBegin, szCondition, *pText, pIcon ? *pIcon : QString(), szInstruction, pItemName ? *pItemName : QString()));
1908 			}
1909 			else
1910 			{
1911 				// zero length instruction, but still add the item
1912 				QString szInstruction = "";
1913 				KviCommandFormatter::bufferFromBlock(szInstruction);
1914 				pPopup->addLabel(new KviKvsTreeNodeSpecialCommandDefpopupLabelItem(pLabelBegin, szCondition, *pText, pIcon ? *pIcon : QString(), szInstruction, pItemName ? *pItemName : QString()));
1915 			}
1916 			delete pParameters;
1917 		}
1918 		else if(szLabelLow == "extpopup")
1919 		{
1920 			EXTRACT_POPUP_LABEL_PARAMETERS
1921 			EXTRACT_POPUP_LABEL_CONDITION
1922 
1923 			QString * pText = pParameters->first();
1924 			if(!pText)
1925 			{
1926 				error(pLabelBegin, __tr2qs_ctx("Unexpected empty <text> field in extpopup parameters. See /help defpopup for the syntax", "kvs"));
1927 				delete pParameters;
1928 				return nullptr;
1929 			}
1930 			QString * pName = pParameters->next();
1931 			if(!pName)
1932 			{
1933 				error(pLabelBegin, __tr2qs_ctx("Unexpected empty <name> field in extpopup parameters. See /help defpopup for the syntax", "kvs"));
1934 				delete pParameters;
1935 				return nullptr;
1936 			}
1937 			QString * pIcon = pParameters->next();
1938 			QString * pItemName = pParameters->next();
1939 			if(KVSP_curCharUnicode == ';')
1940 				KVSP_skipChar;
1941 			pPopup->addLabel(new KviKvsTreeNodeSpecialCommandDefpopupLabelExtpopup(pLabelBegin, szCondition, *pText, pIcon ? *pIcon : QString(), *pName, pItemName ? *pItemName : QString()));
1942 			delete pParameters;
1943 		}
1944 		else
1945 		{
1946 			error(pLabelBegin, __tr2qs_ctx("Found token '%Q' where a 'prologue', 'separator', 'separatorid', 'label', 'popup', 'item', 'extpopup' or 'epilogue' label was expected", "kvs"), &szLabel);
1947 			return nullptr;
1948 		}
1949 
1950 		if(!skipSpacesAndNewlines())
1951 			return nullptr;
1952 	}
1953 
1954 	KVSP_skipChar;
1955 	return pPopup.release();
1956 }
1957 
parseSpecialCommandDefpopup()1958 KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandDefpopup()
1959 {
1960 	// FIXME: This SHOULD be renamed to popup.create (NOT popup.define!)
1961 	//        and internally aliased to defpopup as backward compat
1962 	//        There should be then also popup.destroy etc..
1963 
1964 	/*
1965 		@doc: defpopup
1966 		@type:
1967 			command
1968 		@title:
1969 			defpopup
1970 		@syntax:
1971 			defpopup [-m] (<popup_name>)
1972 			{
1973 				prologue[(<id>)] <prologue_command>
1974 
1975 				epilogue[(<id>)] <epilogue_command>
1976 
1977 				label(<text>[,<id>])[(<expression>)][;]
1978 
1979 				item(<text>[,<icon>[,<id>]])[(<expression>)]<command>
1980 
1981 				popup(<text>[,<icon>[,<id>]])[(<expression>)]
1982 				{
1983 					<popup body>
1984 				}
1985 
1986 				extpopup(<text>,<name>[,<icon>[,<id>]])[(<expression>)][;]
1987 
1988 				separator[(<expression>)][;]
1989 				separatorid[(<id>)][(<expression>)][;]
1990 				...
1991 			}
1992 		@short:
1993 			Defines a popup menu
1994 		@switches:
1995 			!sw: -m | --merge
1996 			Merges the new popup contents with the current named popup.
1997 			If the named popup does not exist, the popup is created.
1998 		@description:
1999 			Defines the popup menu <popup_name>. If the -m switch is [b]not[/b] used
2000 			the previous contents of the popups are cleared before updating.[br]
2001 			The popup is generated 'on the fly' when the [cmd]popup[/cmd] command
2002 			is called.[br]
2003 			The [i]item[/i] keyword adds a menu item with visible <text>,
2004 			the optional <icon> and <command> as code to be executed when the item
2005 			is clicked. <text> is a string that is evaluated at [cmd]popup[/cmd]
2006 			call time and may contain identifiers and variables. If <expression>
2007 			is given, it is evaluated at [cmd]popup[/cmd] call time and if the result
2008 			is 0, the item is not shown in the physical popup.[br]
2009 			The [i]popup[/i] keyword adds a submenu with visible <text>, the optional
2010 			<icon> and a popup body that has identical syntax to the defpopup body.
2011 			The <expression> has the same meaning as with the [i]item[/i] keyword.[br]
2012 			The 'extpopup' keyword adds a submenu with visible <text>, the optional
2013 			icon and a popup body that is defined by the popup menu <name>. This
2014 			basically allows to nest popup menus and define their parts separately.
2015 			<icon> and <expression> have the same meaning as with the [i]item[/i] keyword.[br]
2016 			The [i]separator[/i] keyword adds a straight line between items (separator).[br]
2017 			The 'separatorid' keyword adds a straight line between items, but permits to
2018 			specify a separator id.[br]
2019 			The [i]label[/i] keyword adds a descriptive label that acts like a separator.[br]
2020 			The [i]prologue[/i] keyword adds a <prologue_command> to be executed
2021 			just before the popup is filled at [cmd]popup[/cmd] command call.[br]
2022 			The [i]epilogue[/i] keyword adds an <epilogue_command> to be executed
2023 			just after the popup has been filled at [cmd]popup[/cmd] command call.[br]
2024 			There can be multiple prologue and epilogue commands: their execution order
2025 			is undefined.[br]
2026 			<icon> is always an [doc:image_id]image identifier[/doc].[br]
2027 			<id> is an unique identifier that can be used to remove single items
2028 			by the means of [cmd]delpopupitem[/cmd]. If <id> is omitted
2029 			then it is automatically generated.
2030 			Please note that using this command inside the prologue, epilogue
2031 			or item code of the modified popup menu is forbidden.
2032 			In other words: self modification of popup menus is [b]not[/b] allowed.[br]
2033 			To remove a popup menu use this command with an empty body:
2034 			[example]
2035 				defpopup(test){}
2036 			[/example]
2037 			This will remove the popup [i]test[/i] and free its memory.
2038 			Popups have a special kind of local variables that have an extended lifetime,
2039 			called [i]extended scope[/i] - this is described in the [doc:data_structures]Data structures documentation[/doc].[br]
2040 			The syntax for an "extended scope" variable is:[br]
2041 			[b]%:<variable name>[/b][br]
2042 			This type of variable is maintained during the entire [i]visible lifetime[/i] of the popup -
2043 			from the [cmd]popup[/cmd] command call to the moment in that the user selects an item and the
2044 			corresponding code is executed (essentially from a [cmd]popup[/cmd] call to the next one).[br]
2045 			This allows you to pre-calculate data and conditions in the prologue of the popup
2046 			and then use it in the item handlers or item conditions.[br]
2047 		@seealso:
2048 			[cmd]popup[/cmd]
2049 		@examples:
2050 	*/
2051 
2052 	if(KVSP_curCharUnicode != '(')
2053 	{
2054 		warning(KVSP_curCharPointer, __tr2qs_ctx("The 'defpopup' command needs an expression enclosed in parenthesis", "kvs"));
2055 		errorBadChar(KVSP_curCharPointer, '(', "defpopup");
2056 		return nullptr;
2057 	}
2058 
2059 	const QChar * pBegin = KVSP_curCharPointer;
2060 
2061 	KVSP_skipChar;
2062 
2063 	KviKvsTreeNodeData * pPopupName = parseSingleParameterInParenthesis();
2064 	if(!pPopupName)
2065 		return nullptr;
2066 
2067 	if(!skipSpacesAndNewlines())
2068 	{
2069 		delete pPopupName;
2070 		return nullptr;
2071 	}
2072 
2073 	KviKvsTreeNodeSpecialCommandDefpopupLabelPopup * pMainPopup = parseSpecialCommandDefpopupLabelPopup();
2074 	if(!pMainPopup)
2075 	{
2076 		delete pPopupName;
2077 		return nullptr;
2078 	}
2079 
2080 	return new KviKvsTreeNodeSpecialCommandDefpopup(pBegin, pPopupName, pMainPopup);
2081 }
2082 
parseSpecialCommandHelp()2083 KviKvsTreeNodeCommand * KviKvsParser::parseSpecialCommandHelp()
2084 {
2085 	// again not a fully special command: this routine just returns
2086 	// a CoreSimpleCommand but parses the parameters as constants
2087 
2088 	// we handle a single big parameter, with whitespace stripped
2089 	// This is because we want the identifiers to be preserved
2090 	// as unevaluated (i.e $function).
2091 
2092 	skipSpaces();
2093 
2094 	const QChar * pBegin = KVSP_curCharPointer;
2095 	while(!KVSP_curCharIsEndOfCommand)
2096 		KVSP_skipChar;
2097 
2098 	if(!KVSP_curCharIsEndOfBuffer)
2099 		KVSP_skipChar;
2100 
2101 	QString tmp(pBegin, KVSP_curCharPointer - pBegin);
2102 	tmp = tmp.trimmed();
2103 	const QString szHelpName("help");
2104 
2105 	KviKvsCoreSimpleCommandExecRoutine * r = KviKvsKernel::instance()->findCoreSimpleCommandExecRoutine(szHelpName);
2106 	if(!r)
2107 		return nullptr; // <--- internal error!
2108 
2109 	KviKvsTreeNodeDataList * p = new KviKvsTreeNodeDataList(pBegin);
2110 	p->addItem(new KviKvsTreeNodeConstantData(pBegin, new KviKvsVariant(tmp)));
2111 
2112 	return new KviKvsTreeNodeCoreSimpleCommand(pBegin, szHelpName, p, r);
2113 }
2114