1 //=============================================================================
2 //
3 //   File : KviKvsCoreFunctions_af.cpp
4 //   Creation date : Fri 31 Oct 2003 01:52:04 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 //   Copyright ©        2010 Kai Wasserbäch <debian@carbon-project.org>
9 //
10 //   This program is FREE software. You can redistribute it and/or
11 //   modify it under the terms of the GNU General Public License
12 //   as published by the Free Software Foundation; either version 2
13 //   of the License, or (at your option) any later version.
14 //
15 //   This program is distributed in the HOPE that it will be USEFUL,
16 //   but WITHOUT ANY WARRANTY; without even the implied warranty of
17 //   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 //   See the GNU General Public License for more details.
19 //
20 //   You should have received a copy of the GNU General Public License
21 //   along with this program. If not, write to the Free Software Foundation,
22 //   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 //
24 //=============================================================================
25 
26 #include "KviKvsCoreFunctions.h"
27 #include "KviKvsKernel.h"
28 #include "KviKvsObjectController.h"
29 #include "KviLocale.h"
30 #include "KviApplication.h"
31 #include "KviChannelWindow.h"
32 #include "KviConsoleWindow.h"
33 #include "KviIrcConnection.h"
34 #include "KviIrcConnectionUserInfo.h"
35 #include "KviControlCodes.h"
36 #include "KviAvatar.h"
37 #include "KviIrcUserDataBase.h"
38 #include "KviMainWindow.h"
39 #include "KviStatusBar.h"
40 #include "KviQString.h"
41 #include "KviSSLMaster.h"
42 #include "KviOptions.h"
43 #include "KviKvsAliasManager.h"
44 #include "KviKvsScript.h"
45 #include "KviBuildInfo.h"
46 
47 #include <QChar>
48 #include <QDateTime>
49 #include <QString>
50 #include <QStringList>
51 
52 namespace KviKvsCoreFunctions
53 {
54 	/*
55 		@doc: active
56 		@type:
57 			function
58 		@title:
59 			$active
60 		@short:
61 			Retrieves the window ID of the active window
62 		@syntax:
63 			<integer> $active[(<irc context id:integer>)]
64 		@description:
65 			Returns the [b]window ID[/b] of the active window
66 			bound to the IRC context specified by <irc context id>.
67 			If no window matches the specified IRC context, an invalid
68 			window ID is returned (0).[br]
69 			If no <irc context id> is specified, then
70 			the application active window is returned (the window
71 			that currently has the input focus).[br][br]Note that in this
72 			case the returned window may also belong to another IRC
73 			context or be not bound to any IRC context at all.
74 			In some extreme cases you may even get a window that
75 			has no output widget and thus has its output redirected.
76 			Using the [i]global[/i] active window should be used only
77 			for communicating something [b]really[/b] urgent (and maybe
78 			unrelated to a specific IRC connection) to the user.
79 		@seealso:
80 			[fnc]$window[/fnc]
81 	*/
82 
KVSCF(active)83 	KVSCF(active)
84 	{
85 		kvs_uint_t uContextId;
86 		KVSCF_PARAMETERS_BEGIN
87 		KVSCF_PARAMETER("context_id", KVS_PT_UINT, KVS_PF_OPTIONAL, uContextId)
88 		KVSCF_PARAMETERS_END
89 
90 		KviWindow * wnd = nullptr;
91 		if(KVSCF_pParams->count() > 0)
92 		{
93 			KviConsoleWindow * cons = g_pApp->findConsole(uContextId);
94 			if(cons)
95 				wnd = cons->activeWindow();
96 		}
97 		else
98 		{
99 			wnd = g_pActiveWindow;
100 		}
101 
102 		KVSCF_pRetBuffer->setInteger((kvs_int_t)(wnd ? wnd->numericId() : 0));
103 		return true;
104 	}
105 
106 	/*
107 		@doc: aliasBody
108 		@type:
109 			function
110 		@title:
111 			$aliasBody
112 		@short:
113 			Returns the body of an alias, if it exists
114 		@syntax:
115 			<string> $aliasBody(<alias_name:string>)
116 		@description:
117 			Returns the KVS body of the specified alias.
118 			If the alias doesn't exist it just returns an empty string.
119 		@seealso:
120 			[cmd]alias[/cmd]
121 	*/
122 
KVSCF(aliasBody)123 	KVSCF(aliasBody)
124 	{
125 		QString szAliasName;
126 		KVSCF_PARAMETERS_BEGIN
127 		KVSCF_PARAMETER("alias_name", KVS_PT_STRING, 0, szAliasName)
128 		KVSCF_PARAMETERS_END
129 
130 		const KviKvsScript * pAlias = KviKvsAliasManager::instance()->lookup(szAliasName);
131 		if(pAlias)
132 			KVSCF_pRetBuffer->setString(pAlias->code());
133 		else
134 			KVSCF_pRetBuffer->setString(QString());
135 		return true;
136 	}
137 
138 	/*
139 		@doc: ascii
140 		@type:
141 			function
142 		@title:
143 			$ascii
144 		@short:
145 			Returns the Unicode code of a character
146 		@syntax:
147 			<variant> $ascii(<char:string>)
148 		@description:
149 			This function has been renamed to $unicode and is present
150 			only for backward compatibility.
151 		@seealso:
152 			[fnc]$cr[/fnc], [fnc]$lf[/fnc], [fnc]$char[/fnc]
153 	*/
154 
155 	/*
156 		@doc: asciiToBase64
157 		@type:
158 			function
159 		@title:
160 			$asciiToBase64
161 		@short:
162 			Returns an encoded base64 string
163 		@syntax:
164 			$asciiToBase64(<ascii_string>)
165 		@description:
166 			Encodes an ASCII string to its base64 encoded representation
167 			Please note that since KVS is Unicode based, this function
168 			will first encode the string in UTF-8 and then base64-encode.
169 			This means that it is substantially only 7-bit safe (ASCII codes below 128).
170 		@examples:
171 			[example]
172 				[cmd]echo[/cmd] $asciiToBase64("Hello!")
173 			[/example]
174 		@seealso:
175 			[fnc]$base64toascii[/fnc]
176 	*/
177 
KVSCF(asciiToBase64)178 	KVSCF(asciiToBase64)
179 	{
180 		QString szAscii;
181 		KVSCF_PARAMETERS_BEGIN
182 		KVSCF_PARAMETER("ascii_string", KVS_PT_STRING, 0, szAscii)
183 		KVSCF_PARAMETERS_END
184 
185 		KviCString tmp1(szAscii);
186 		if(tmp1.len() > 0)
187 		{
188 			KviCString tmp2;
189 			tmp2.bufferToBase64(tmp1.ptr(), tmp1.len());
190 			KVSCF_pRetBuffer->setString(QString(tmp2.ptr()));
191 		}
192 		else
193 		{
194 			KVSCF_pRetBuffer->setString(QString());
195 		}
196 		return true;
197 	}
198 
199 	/*
200 		@doc: asciiToHex
201 		@type:
202 			function
203 		@title:
204 			$asciiToHex
205 		@short:
206 			Returns an encoded hex string
207 		@syntax:
208 			<string> $asciiToHex(<ascii_string:string>)
209 		@description:
210 			Encodes an ASCII string to its hex encoded representation.
211 			Please note that since KVS is Unicode based, this function
212 			will first encode the string in UTF-8 and then hex-encode.
213 			This means that it is substantially only 7bit safe (ASCII codes below 128).
214 		@examples:
215 			[example]
216 				[cmd]echo[/cmd] $asciiToHex("Hello!")
217 			[/example]
218 		@seealso:
219 			[fnc]$hextoascii[/fnc]
220 	*/
221 
KVSCF(asciiToHex)222 	KVSCF(asciiToHex)
223 	{
224 		QString szAscii;
225 		KVSCF_PARAMETERS_BEGIN
226 		KVSCF_PARAMETER("ascii_string", KVS_PT_STRING, 0, szAscii)
227 		KVSCF_PARAMETERS_END
228 
229 		KviCString tmp1(szAscii);
230 		if(tmp1.len() > 0)
231 		{
232 			KviCString tmp2;
233 			tmp2.bufferToHex(tmp1.ptr(), tmp1.len());
234 			KVSCF_pRetBuffer->setString(QString(tmp2.ptr()));
235 		}
236 		else
237 		{
238 			KVSCF_pRetBuffer->setString(QString());
239 		}
240 		return true;
241 	}
242 
243 	/*
244 		@doc: array
245 		@type:
246 			function
247 		@title:
248 			$array
249 		@short:
250 			Explicitly creates an array
251 		@syntax:
252 			<array> $array(<item:variant>,<item:variant>,<item:variant>,....);
253 		@description:
254 			Returns an array with the specified items. The items are indexed starting from 0.
255 			This is just an explicit way of creating an array with a defined set of items,
256 			useful for increasing readability.
257 		@examples:
258 			[example]
259 				[cmd]alias[/cmd](test) {
260 					[cmd]return[/cmd] $array(1,2,3);
261 				}
262 				%x = $test();
263 				[cmd]foreach[/cmd](%y,%x) {
264 					[cmd]echo[/cmd] %y;
265 				}
266 			[/example]
267 		@seealso:
268 			[fnc]$hash[/fnc]
269 	*/
270 
KVSCF(array)271 	KVSCF(array)
272 	{
273 		KviKvsArray * a = new KviKvsArray();
274 
275 		for(KviKvsVariant * v = KVSCF_pParams->first(); v; v = KVSCF_pParams->next())
276 			a->append(new KviKvsVariant(*v));
277 
278 		KVSCF_pRetBuffer->setArray(a);
279 		return true;
280 	}
281 
282 	/*
283 		@doc: away
284 		@type:
285 			function
286 		@title:
287 			$away
288 		@short:
289 			Returns true if the current user is away
290 		@syntax:
291 			<boolean> $away
292 		@description:
293 			Returns true if the current user is away, else false.
294 			If the current IRC context is not connected at all, this function returns false.
295 	*/
296 
KVSCF(away)297 	KVSCF(away)
298 	{
299 		kvs_uint_t uCntx;
300 
301 		KVSCF_PARAMETERS_BEGIN
302 		KVSCF_PARAMETER("irc_context_id", KVS_PT_UINT, KVS_PF_OPTIONAL, uCntx)
303 		KVSCF_PARAMETERS_END
304 
305 		KviConsoleWindow * cns = nullptr;
306 
307 		if(KVSCF_pParams->count() > 0)
308 		{
309 			cns = g_pApp->findConsole(uCntx);
310 			if(cns && cns->context()->isConnected())
311 				KVSCF_pRetBuffer->setBoolean(cns->connection()->userInfo()->isAway());
312 			else
313 				KVSCF_pRetBuffer->setNothing();
314 		}
315 		else
316 		{
317 			cns = KVSCF_pContext->window()->console();
318 			if(cns)
319 			{
320 				if(cns->context()->isConnected())
321 					KVSCF_pRetBuffer->setBoolean(cns->connection()->userInfo()->isAway());
322 				else
323 					KVSCF_pRetBuffer->setNothing();
324 			}
325 			else
326 			{
327 				KVSCF_pContext->warning(__tr2qs_ctx("This window has no associated IRC context", "kvs"));
328 				KVSCF_pRetBuffer->setNothing();
329 			}
330 		}
331 		return true;
332 	}
333 
334 	/*
335 		@doc: b
336 		@type:
337 			function
338 		@title:
339 			$b
340 		@short:
341 			Returns the BOLD mIRC control character
342 		@syntax:
343 			<string> $b
344 		@description:
345 			Returns the BOLD mIRC control character (Ctrl+B).[br]
346 		@seealso:
347 			[fnc]$k[/fnc], [fnc]$i[/fnc], [fnc]$u[/fnc], [fnc]$r[/fnc], [fnc]$o[/fnc]
348 	*/
349 
KVSCF(b)350 	KVSCF(b)
351 	{
352 		KVSCF_pRetBuffer->setString(QString(QChar(KviControlCodes::Bold)));
353 		return true;
354 	}
355 
356 	/*
357 		@doc: base64toAscii
358 		@type:
359 			function
360 		@title:
361 			$base64ToAscii
362 		@short:
363 			Returns a decoded base64 string
364 		@syntax:
365 			<string> $base64ToAscii(<base_64_encoded_string:string>)
366 		@description:
367 			Decodes a base64 encoded string.
368 		@seealso:
369 			[fnc]$asciiToBase64[/fnc]
370 	*/
371 
KVSCF(base64ToAscii)372 	KVSCF(base64ToAscii)
373 	{
374 		QString szBase64;
375 		KVSCF_PARAMETERS_BEGIN
376 		KVSCF_PARAMETER("base64_encoded_string", KVS_PT_STRING, 0, szBase64)
377 		KVSCF_PARAMETERS_END
378 
379 		KviCString tmp1(szBase64);
380 		char * buf;
381 		int len = tmp1.base64ToBuffer(&buf, true);
382 		QString szRet(buf);
383 		szRet.truncate(len);
384 		KVSCF_pRetBuffer->setString(szRet);
385 		KviCString::freeBuffer(buf);
386 		return true;
387 	}
388 
389 	/*
390 		@doc: boolean
391 		@type:
392 			function
393 		@title:
394 			$boolean
395 		@short:
396 			Casts a variable to a boolean
397 		@syntax:
398 			<integer> $boolean(<data:variant>)
399 		@description:
400 			Forces <data> to be a boolean data type by first casting
401 			to integer (see [fnc]$int[/fnc]()) and then comparing the result against zero.
402 			A zero integer will result in a false value while a non-zero one
403 			will result in a true value.
404 			This function is similar to the C++ (bool) cast and is internally
405 			aliased to [fnc]$bool[/fnc] too.
406 			Note that since KVIrc does most of the casting work automatically
407 			you shouldn't need to use this function.
408 		@seealso:
409 			[fnc]$real[/fnc]
410 			[fnc]$integer[/fnc]
411 	*/
412 
413 	/*
414 		@doc: bool
415 		@type:
416 			function
417 		@title:
418 			$bool
419 		@short:
420 			Casts a variable to a boolean
421 		@syntax:
422 			<integer> $bool(<data:variant>)
423 		@description:
424 			This is an internal alias to [fnc]$boolean[/fnc]().
425 		@seealso:
426 			[fnc]$real[/fnc]
427 			[fnc]$integer[/fnc]
428 	*/
429 
KVSCF(boolean)430 	KVSCF(boolean)
431 	{
432 		KviKvsVariant * v;
433 		KVSCF_PARAMETERS_BEGIN
434 		KVSCF_PARAMETER("data", KVS_PT_VARIANT, 0, v)
435 		KVSCF_PARAMETERS_END
436 
437 		kvs_int_t iVal;
438 		v->castToInteger(iVal);
439 		KVSCF_pRetBuffer->setBoolean(iVal);
440 		return true;
441 	}
442 
443 	/*
444 		@doc: certificate
445 		@type:
446 			function
447 		@title:
448 			$certificate
449 		@short:
450 			Returns information about the local certificate
451 		@syntax:
452 			<string> $certificate(<query:string>[,<param1:string>])
453 		@description:
454 			Returns the requested information about local certificate.[br]
455 			Some queries can accept an optional parameter <param1>.[br]
456 			Available query strings are:
457 			[ul]
458 				[li]signatureType[/li]
459 				[li]signatureContents[/li]
460 				[li]subjectCountry[/li]
461 				[li]subjectStateOrProvince[/li]
462 				[li]subjectLocality[/li]
463 				[li]subjectOrganization[/li]
464 				[li]subjectOrganizationalUnit[/li]
465 				[li]subjectCommonName[/li]
466 				[li]issuerCountry[/li]
467 				[li]issuerStateOrProvince[/li]
468 				[li]issuerLocality[/li]
469 				[li]issuerOrganization[/li]
470 				[li]issuerOrganizationalUnit[/li]
471 				[li]issuerCommonName[/li]
472 				[li]publicKeyBits[/li]
473 				[li]publicKeyType[/li]
474 				[li]serialNumber[/li]
475 				[li]pemBase64[/li]
476 				[li]version[/li]
477 				[li]fingerprintIsValid[/li]
478 				[li]fingerprintDigestId[/li]
479 				[li]fingerprintDigestStr[/li]
480 				[li]fingerprintContents * accepts parameter interpreted as [i]digest name[/i][/li]
481 			[/ul]
482 		@seealso:
483 			[fnc]$str.evpSign[/fnc]
484 			[fnc]$str.evpVerify[/fnc]
485 			[fnc]$dcc.getSSLCertInfo[/fnc]
486 	*/
487 
KVSCF(certificate)488 	KVSCF(certificate)
489 	{
490 		QString szQuery;
491 		QString szParam1;
492 
493 		KVSCF_PARAMETERS_BEGIN
494 		KVSCF_PARAMETER("query", KVS_PT_STRING, 0, szQuery)
495 		KVSCF_PARAMETER("param1", KVS_PT_STRING, KVS_PF_OPTIONAL, szParam1)
496 		KVSCF_PARAMETERS_END
497 
498 #ifndef COMPILE_SSL_SUPPORT
499 		KVSCF_pContext->warning(__tr2qs("This executable was built without SSL support"));
500 		return true;
501 #else
502 		KviSSL::globalSSLInit();
503 		X509 * cert = nullptr;
504 
505 		if(!KVI_OPTION_BOOL(KviOption_boolUseSSLCertificate))
506 		{
507 			KVSCF_pContext->warning(__tr2qs("No public key certificate defined in KVIrc options."));
508 			KVSCF_pRetBuffer->setString("");
509 			return true;
510 		}
511 
512 		FILE * f = fopen(KVI_OPTION_STRING(KviOption_stringSSLCertificatePath).toUtf8().data(), "r");
513 		if(!f)
514 		{
515 			KVSCF_pContext->warning(__tr2qs("File I/O error while trying to use the public key file %s"), KVI_OPTION_STRING(KviOption_stringSSLCertificatePath).toUtf8().data());
516 			KVSCF_pRetBuffer->setString("");
517 			return true;
518 		}
519 
520 		QString szPass = KVI_OPTION_STRING(KviOption_stringSSLCertificatePass).toUtf8();
521 		PEM_read_X509(f, &cert, nullptr, szPass.data());
522 
523 		fclose(f);
524 
525 		if(!cert)
526 		{
527 			KVSCF_pContext->warning(__tr2qs("Can't load certificate from public key file %s"), KVI_OPTION_STRING(KviOption_stringSSLCertificatePath).toUtf8().data());
528 			KVSCF_pRetBuffer->setString("");
529 			return true;
530 		}
531 
532 		// KviSSLCertificate takes ownership of the X509 structure, no need to free it
533 		KviSSLCertificate * pCert = new KviSSLCertificate(cert);
534 
535 		if(!pCert)
536 		{
537 			KVSCF_pContext->warning(__tr2qs_ctx("Error retrieving information from the local certificate", "dcc"));
538 			KVSCF_pRetBuffer->setString("");
539 			return true;
540 		}
541 
542 		if(KviSSLMaster::getSSLCertInfo(pCert, szQuery, szParam1, KVSCF_pRetBuffer))
543 			return true;
544 
545 		KVSCF_pContext->warning(__tr2qs_ctx("Unable to get certificate information: query not recognized", "dcc"));
546 		KVSCF_pRetBuffer->setString("");
547 		return true;
548 #endif
549 	}
550 
551 	/*
552 		@doc: channel
553 		@type:
554 			function
555 		@title:
556 			$channel
557 		@short:
558 			Retrieves the window ID of a specified channel
559 		@syntax:
560 			$channel[(<channel name>[,<irc context id>])]
561 		@description:
562 			Returns the [b]window ID[/b] of channel matching the
563 			<channel name> and bound to the connection specified by
564 			<irc context id>[br]
565 			If no window matches the specified name or connection, an invalid
566 			window ID is returned [b]0[/b].[br]
567 			If no <irc context id> is specified, this function looks for
568 			the channel in the current connection context (if any).[br]
569 			If no <channel name> is specified, this function returns the current
570 			channel window ID, if executed in a channel, and [b]0[/b] otherwise.[br]
571 		@seealso:
572 			[fnc]$window[/fnc],
573 			[fnc]$query[/fnc],
574 			[fnc]$console[/fnc],
575 			[doc:window_naming_conventions]Window naming conventions[/doc]
576 	*/
577 
KVSCF(channel)578 	KVSCF(channel)
579 	{
580 		QString szName;
581 		kvs_uint_t uContextId;
582 		KVSCF_PARAMETERS_BEGIN
583 		KVSCF_PARAMETER("channel_name", KVS_PT_NONEMPTYSTRING, KVS_PF_OPTIONAL, szName)
584 		KVSCF_PARAMETER("context_id", KVS_PT_UINT, KVS_PF_OPTIONAL, uContextId)
585 		KVSCF_PARAMETERS_END
586 
587 		KviWindow * wnd = nullptr;
588 		if(KVSCF_pParams->count() > 1)
589 		{
590 			KviConsoleWindow * cns = g_pApp->findConsole(uContextId);
591 			if(cns && cns->connection())
592 				wnd = cns->connection()->findChannel(szName);
593 			else if (!cns)
594 				KVSCF_pContext->warning(__tr2qs_ctx("No such IRC context (%u)", "kvs"), uContextId);
595 		}
596 		else if(KVSCF_pParams->count() == 1)
597 		{
598 			if(KVSCF_pContext->window()->connection())
599 				wnd = KVSCF_pContext->window()->connection()->findChannel(szName);
600 			else
601 			{
602 				if(!KVSCF_pContext->window()->console())
603 					KVSCF_pContext->warning(__tr2qs_ctx("This window is not associated to an IRC context", "kvs"));
604 			}
605 		}
606 		else
607 		{
608 			if(KVSCF_pContext->window()->type() == KviWindow::Channel)
609 				wnd = KVSCF_pContext->window();
610 		}
611 
612 		KVSCF_pRetBuffer->setInteger((kvs_int_t)(wnd ? wnd->numericId() : 0));
613 		return true;
614 	}
615 
616 	/*
617 		@doc: char
618 		@type:
619 			function
620 		@title:
621 			$char
622 		@short:
623 			Returns a character specified by Unicode
624 		@syntax:
625 			<string> $char(<unicode_value:integer>)
626 		@description:
627 			Returns a character corresponding to the Unicode code <unicode_value>.[br]
628 			This function can not return NUL character (Unicode 0). Basically
629 			you should never need it: if you do, drop me a mail.[br]
630 			If the <unicode_code> is not a valid Unicode code (or is 0), this function returns
631 			an empty string.[br]
632 		@seealso:
633 			[fnc]$cr[/fnc], [fnc]$lf[/fnc], [fnc]$unicode[/fnc]
634 	*/
635 
KVSCF(charCKEYWORDWORKAROUND)636 	KVSCF(charCKEYWORDWORKAROUND)
637 	{
638 		kvs_uint_t ac;
639 		KVSCF_PARAMETERS_BEGIN
640 		KVSCF_PARAMETER("unicode_value", KVS_PT_UINT, 0, ac)
641 		KVSCF_PARAMETERS_END
642 
643 		if(ac != 0 && ac < 65536)
644 			KVSCF_pRetBuffer->setString(QString(QChar((unsigned short)ac)));
645 		else
646 			KVSCF_pRetBuffer->setString(QString());
647 		return true;
648 	}
649 
650 	/*
651 		@doc: classdefined
652 		@type:
653 			function
654 		@title:
655 			$classdefined
656 		@short:
657 			Checks if a class is defined
658 		@syntax:
659 			$classdefined(<class_name>)
660 		@description:
661 			Returns [b]1[/b] if the class <class_name> is defined, and [b]0[/b] otherwise.
662 	*/
663 
KVSCF(classDefined)664 	KVSCF(classDefined)
665 	{
666 		// prologue: parameter handling
667 		QString szClassName;
668 
669 		KVSCF_PARAMETERS_BEGIN
670 		KVSCF_PARAMETER("className", KVS_PT_NONEMPTYSTRING, 0, szClassName)
671 		KVSCF_PARAMETERS_END
672 		KVSCF_pRetBuffer->setBoolean(KviKvsKernel::instance()->objectController()->lookupClass(szClassName) != nullptr);
673 		return true;
674 	}
675 
676 	/*
677 		@doc: console
678 		@type:
679 			function
680 		@title:
681 			$console
682 		@short:
683 			Retrieves the window ID of a specified console
684 		@syntax:
685 			$console[(<irc context id>)]
686 		@description:
687 			Returns the [b]window ID[/b] of the console bound
688 			to the IRC context specified by <irc context id>.
689 			If no window matches the specified IRC context, an invalid
690 			window ID is returned (0).[br]
691 			If no <irc context id> is specified, this function looks for
692 			the console in the current IRC context (if any).[br]
693 		@seealso:
694 			[fnc]$window[/fnc],
695 			[fnc]$channel[/fnc],
696 			[fnc]$query[/fnc],
697 			[doc:window_naming_conventions]Window naming conventions[/doc]
698 	*/
699 
KVSCF(console)700 	KVSCF(console)
701 	{
702 		kvs_uint_t uContextId;
703 		KVSCF_PARAMETERS_BEGIN
704 		KVSCF_PARAMETER("context_id", KVS_PT_UINT, KVS_PF_OPTIONAL, uContextId)
705 		KVSCF_PARAMETERS_END
706 
707 		KviConsoleWindow * cons;
708 		if(KVSCF_pParams->count() > 0)
709 		{
710 			cons = g_pApp->findConsole(uContextId);
711 		}
712 		else
713 		{
714 			cons = KVSCF_pContext->window()->console();
715 			if(!cons)
716 				KVSCF_pContext->warning(__tr2qs_ctx("This window is not associated to an IRC context", "kvs"));
717 		}
718 
719 		KVSCF_pRetBuffer->setInteger((kvs_int_t)(cons ? cons->numericId() : 0));
720 		return true;
721 	}
722 
723 	/*
724 		@doc: context
725 		@type:
726 			function
727 		@title:
728 			$context
729 		@short:
730 			Retrieves the ID of the specified IRC context
731 		@syntax:
732 			$context[(<server>,<nickname>)]
733 		@description:
734 			Returns the [b]IRC context ID[/b] of the IRC context that uses
735 			the specified <server> and local user's <nickname>.[br] This function can
736 			find only connected IRC contexts.
737 			If no context matches the server and nickname, an invalid
738 			[b]IRC context ID 0[/b] is returned.[br]
739 			If <server> is an empty string, the first context that matches
740 			the specified nickname is returned. If <nickname> is an empty string,
741 			the first context that uses the specified server is returned.
742 			If both parameters are missing this function returns the
743 			id of the current IRC context, or [b]0[/b] if the
744 			window that this call is executed in is not bound to any IRC context.
745 			Please note that in this last case you may find an [b]IRC context[/b]
746 			that is [i]not connected[/i].
747 			This can only happen if the current window is a console that is
748 			in the [i]idle[/i] state, with no connection established yet.[br]
749 			It is a good idea to take a look at the
750 			[doc:window_naming_conventions]window naming conventions[/doc].
751 		@seealso:
752 			[fnc]$window.context[/fnc]
753 	*/
754 
KVSCF(context)755 	KVSCF(context)
756 	{
757 		QString szServer, szNick;
758 		KVSCF_PARAMETERS_BEGIN
759 		KVSCF_PARAMETER("server", KVS_PT_STRING, KVS_PF_OPTIONAL, szServer)
760 		KVSCF_PARAMETER("nick", KVS_PT_STRING, KVS_PF_OPTIONAL, szNick)
761 		KVSCF_PARAMETERS_END
762 
763 		KviConsoleWindow * cons;
764 		if(!szServer.isEmpty() || !szNick.isEmpty())
765 			cons = g_pApp->findConsole(szServer, szNick);
766 		else
767 			cons = KVSCF_pContext->window()->console();
768 
769 		KVSCF_pRetBuffer->setInteger(cons ? cons->context()->id() : 0);
770 		return true;
771 	}
772 
773 	/*
774 		@doc: countStatusBarItems
775 		@type:
776 			function
777 		@title:
778 			$countStatusBarItems
779 		@short:
780 			Returns the number of items in the statusbar
781 		@syntax:
782 			<int> $countStatusBarItems
783 		@description:
784 			Returns the number of items in the statusbar.
785 		@seealso:
786 			[class]widget[/class]
787 	*/
788 
KVSCF(countStatusBarItems)789 	KVSCF(countStatusBarItems)
790 	{
791 		if(g_pMainWindow->mainStatusBar())
792 		{
793 			QList<QWidget *> widgets = g_pMainWindow->mainStatusBar()->findChildren<QWidget *>();
794 			KVSCF_pRetBuffer->setInteger(widgets.count());
795 		}
796 		else
797 			KVSCF_pRetBuffer->setInteger(0);
798 		return true;
799 	}
800 
801 	/*
802 		@doc: cr
803 		@type:
804 			function
805 		@title:
806 			$cr
807 		@short:
808 			Returns a carriage return character
809 		@syntax:
810 			<string> $cr
811 		@description:
812 			Returns a carriage return character
813 		@seealso:
814 			[fnc]$lf[/fnc], [fnc]$ascii[/fnc], [fnc]$char[/fnc]
815 	*/
816 
KVSCF(cr)817 	KVSCF(cr)
818 	{
819 		KVSCF_pRetBuffer->setString(QString(QChar('\r')));
820 		return true;
821 	}
822 
823 	// The formatting of the table contents has to remain this way (i.e. long lines),
824 	// else it adds long spaces between words in same sentence which looks awful on screen + harder to read.
825 	/*
826 		@doc: date
827 		@type:
828 			function
829 		@title:
830 			$date
831 		@short:
832 			Returns a date/time string using a specified format
833 		@syntax:
834 			<string> $date(<format:string>[,<unixtime:integer>])
835 		@description:
836 			Returns the string representation of <unixtime> or
837 			of the current time if <unixtime> is not given, based on <format>.[br]
838 			The <format string> should contain a set of characters
839 			that will be transformed according to the following rules:
840 			[table]
841 				[tr][td][b]a[/b][/td][td]The abbreviated weekday name according to the current locale.[/td][/tr]
842 				[tr][td][b]A[/b][/td][td]The full weekday name according to the current locale.[/td][/tr]
843 				[tr][td][b]b[/b][/td][td]The abbreviated month name according to the current locale.[/td][/tr]
844 				[tr][td][b]B[/b][/td][td]The full month name according to the current locale.[/td][/tr]
845 				[tr][td][b]c[/b][/td][td]The preferred date and time representation for the current locale.[/td][/tr]
846 				[tr][td][b]C[/b][/td][td]The century number (year/100) as a 2-digit integer. (SU)[/td][/tr]
847 				[tr][td][b]d[/b][/td][td]The day of the month as a decimal number (range 01 to 31).[/td][/tr]
848 				[tr][td][b]D[/b][/td][td]Equivalent to m/d/y.[/td][/tr]
849 				[tr][td][b]e[/b][/td][td]Like d, the day of the month as a decimal number, but a leading zero is replaced by a space. (SU)[/td][/tr]
850 				[tr][td][b]F[/b][/td][td]Equivalent to Y-m-d (the ISO 8601 date format). (C99)[/td][/tr]
851 				[tr][td][b]h[/b][/td][td]Equivalent to b.[/td][/tr]
852 				[tr][td][b]H[/b][/td][td]The hour as a decimal number using a 24-hour clock (range 00 to 23).[/td][/tr]
853 				[tr][td][b]I[/b][/td][td]The hour as a decimal number using a 12-hour clock (range 01 to 12).[/td][/tr]
854 				[tr][td][b]j[/b][/td][td]The day of the year as a decimal number (range 001 to 366).[/td][/tr]
855 				[tr][td][b]k[/b][/td][td]The hour (24-hour clock) as a decimal number (range 0 to 23); single digits are preceded by a blank. See also [b]H[/b].[/td][/tr]
856 				[tr][td][b]l[/b][/td][td]The hour (12-hour clock) as a decimal number (range 1 to 12); single digits are preceded by a blank. See also [b]I[/b].[/td][/tr]
857 				[tr][td][b]m[/b][/td][td]The month as a decimal number (range 01 to 12).[/td][/tr]
858 				[tr][td][b]M[/b][/td][td]The minute as a decimal number (range 00 to 59).[/td][/tr]
859 				[tr][td][b]n[/b][/td][td]A newline character. (SU)[/td][/tr]
860 				[tr][td][b]p[/b][/td][td]Either [b]AM[/b] or [b]PM[/b] according to the given time value, or the corresponding strings for the current locale. Noon is treated as [b]pm[/b] and midnight as [b]am[/b].[/td][/tr]
861 				[tr][td][b]r[/b][/td][td]The time in a.m. or p.m. notation. In the POSIX locale this is	equivalent to [b]I:M:S p[/b].[/td][/tr]
862 				[tr][td][b]s[/b][/td][td]The number of seconds since the Epoch, i.e., since 1970-01-01 00:00:00 UTC.[/td][/tr]
863 				[tr][td][b]S[/b][/td][td]The second as a decimal number (range 00 to 60). (The range is up to 60 to allow for occasional leap seconds.)[/td][/tr]
864 				[tr][td][b]t[/b][/td][td]A tab character.[/td][/tr]
865 				[tr][td][b]T[/b][/td][td]The time in 24-hour notation (H:M:S). (SU)[/td][/tr]
866 				[tr][td][b]u[/b][/td][td]The day of the week as a decimal, range 1 to 7, Monday being 1. See also [b]w[/b].[/td][/tr]
867 				[tr][td][b]V[/b][/td][td]The ISO 8601:1988 week number of the current year as a decimal number, range 01 to 53,	where week 1 is the first week that has at least 4 days in the current year, and with Monday as the first day of the week. See also [b]U[/b] and [/b]W.[/td][/tr]
868 				[tr][td][b]w[/b][/td][td]The day of the week as a decimal, range 0 to 6, Sunday being 0.[/td][/tr]
869 				[tr][td][b]W[/b][/td][td]The week number of the current year as a decimal number, range 00 to 53, starting with the first Monday as the first day of week 01.[/td][/tr]
870 				[tr][td][b]y[/b][/td][td]The year as a decimal number without a century (range 00 to 99).[/td][/tr]
871 				[tr][td][b]Y[/b][/td][td]The year as a decimal number including the century.[/td][/tr]
872 				[tr][td][b]z[/b][/td][td]The time-zone as hour offset from GMT. Required to emit RFC822-compliant dates (using [i]a, d b Y H:M:S z[/i]).[/td][/tr]
873 				[tr][td][b]Z[/b][/td][td]The time zone or name or abbreviation (not implemented yet).[/td][/tr]
874 			[/table]
875 		@examples:
876 			[example]
877 				[cmd]echo[/cmd] $date("d/m/Y H:M:S")
878 			[/example]
879 		@seealso:
880 			[fnc]$unixtime[/fnc], [fnc]$hptimestamp[/fnc]
881 		@author:
882 			Kai Wasserbäch <debian@carbon-project.org>
883 	*/
884 
KVSCF(date)885 	KVSCF(date)
886 	{
887 		QString szFormat;
888 		kvs_int_t iTime;
889 		KVSCF_PARAMETERS_BEGIN
890 		KVSCF_PARAMETER("format", KVS_PT_NONEMPTYSTRING, 0, szFormat)
891 		KVSCF_PARAMETER("unixtime", KVS_PT_INT, KVS_PF_OPTIONAL, iTime)
892 		KVSCF_PARAMETERS_END
893 
894 		// strftime() is not sufficient, as shown by #769, the following is
895 		// derived from KDateTime (not a copy, but the structure is similar,
896 		// which isn't surprising), but limited/extended to the required set of
897 		// functionality.
898 		// The reason for this is platform independent.
899 		QDateTime qDt;
900 		QString szFmtTime;
901 		int iLength, iVal, iTemp, iLocalTzH, iLocalTzM, iUtcH, iUtcM, iTzOffset;
902 		int iTzOffsetH, iTzOffsetM;
903 		QChar cDiv;
904 
905 		if(KVSCF_pParams->count() > 1)
906 			qDt.setTime_t(iTime);
907 		else
908 			qDt = QDateTime::currentDateTime();
909 
910 		if(!qDt.isValid())
911 		{
912 			KVSCF_pContext->warning(__tr2qs_ctx("Couldn't construct QDateTime object.", "kvs"));
913 			goto leavenow;
914 		}
915 
916 		for(auto i : szFormat)
917 		{
918 			ushort ch = i.unicode();
919 			iLength = 2;
920 			iVal = 0x8000000;
921 			cDiv = ' ';
922 
923 			switch(ch)
924 			{
925 				// FIXME: G, g, U, x, X, Z not implemented yet.
926 				//
927 				// E and O probably never will be implemented.
928 				case 'a': // the abbreviated localized day name (e.g.
929 					// 'Mon' to 'Sun'). Uses
930 					// QDate::shortDayName().
931 					szFmtTime += qDt.toString("ddd");
932 					break;
933 				case 'A': // the long localized day name (e.g.
934 					// 'Monday' to 'Qt::Sunday'). Uses
935 					// QDate::longDayName().
936 					szFmtTime += qDt.toString("dddd");
937 					break;
938 				case 'b': // the abbreviated localized month name
939 				case 'h': // (e.g. 'Jan' to 'Dec'). Uses
940 					// QDate::shortMonthName().
941 					szFmtTime += qDt.toString("MMM");
942 					break;
943 				case 'B': // the long localized month name (e.g.
944 					// 'January' to 'December'). Uses
945 					// QDate::longMonthName().
946 					szFmtTime += qDt.toString("MMMM");
947 					break;
948 				case 'c': // If the format is Qt::TextDate, the string
949 					// is formatted in the default way.
950 					szFmtTime += qDt.toString(Qt::TextDate);
951 					break;
952 				case 'C': // 2-digit "century" (yyyy/100)
953 					iVal = qDt.date().year() / 100;
954 					break;
955 				case 'd': // the day as number with a leading zero
956 					// (01 to 31)
957 					cDiv = '0';
958 				// break omitted on purpose, NEXT MUST BE 'e'
959 				case 'e': // 2-character wide day representation,
960 					// space padded
961 					iVal = qDt.date().day();
962 					break;
963 				case 'D': // American formatting
964 					szFmtTime += qDt.toString("MM/dd/yy");
965 					break;
966 				case 'F': // yyyy-MM-dd (ISO 8601)
967 					szFmtTime += qDt.date().toString(Qt::ISODate);
968 					break;
969 				case 'H': // the hour with a leading zero (00 to 23)
970 					cDiv = '0';
971 				// no break; on purpose (NEXT MUST BE case 'k')
972 				case 'k': // same as H, only that single-character values
973 					// are prefixed with a space
974 					iVal = qDt.time().hour();
975 					break;
976 				case 'I': // hour, 01 - 12
977 					cDiv = '0';
978 				// fall through to 'l'
979 				case 'l': // hour, 1 - 12
980 					iVal = (qDt.time().hour() + 11) % 12 + 1;
981 					break;
982 				case 'j': // day of the year (001 to 365/366)
983 					iVal = qDt.date().dayOfYear();
984 					iLength = 3;
985 					cDiv = '0';
986 					break;
987 				case 'M': // minutes (00 to 59)
988 					szFmtTime += qDt.toString("mm");
989 					break;
990 				case 'm': // month (01-12)
991 					szFmtTime += qDt.toString("MM");
992 					break;
993 				case 'n': // newline
994 					szFmtTime += "\n";
995 					break;
996 				case 'p': // AM/PM
997 					// FIXME: l10n for the am/pm? Maybe we can
998 					//        draw it from a different source?
999 					//        Qt doesn't offer l10n strings
1000 					//        for this.
1001 					if(qDt.time().hour() < 12)
1002 						szFmtTime += "AM";
1003 					else
1004 						szFmtTime += "PM";
1005 					break;
1006 				case 'P': // am/pm, the same FIXME as for p applies.
1007 					if(qDt.time().hour() < 12)
1008 						szFmtTime += "am";
1009 					else
1010 						szFmtTime += "pm";
1011 					break;
1012 				case 'r': // "I:M:S p"
1013 					szFmtTime += qDt.toString("hh:mm:ss AP");
1014 					break;
1015 				case 'R': // "H:M"
1016 					szFmtTime += qDt.toString("hh:mm");
1017 					break;
1018 				case 's': // seconds since epoch (currently 1970-01-01 00:00:00 UTC)
1019 					szFmtTime += QString::number(qDt.toTime_t());
1020 					break;
1021 				case 'S': // seconds (00-60)
1022 					cDiv = '0';
1023 					iVal = qDt.time().second();
1024 					break;
1025 				case 't': // Tab
1026 					szFmtTime += "\t";
1027 					break;
1028 				case 'T': // H:M:S
1029 					szFmtTime += qDt.toString("hh:mm:ss");
1030 					break;
1031 				case 'u': // day of the week (1-7)
1032 					iVal = qDt.date().dayOfWeek();
1033 					iLength = 1;
1034 					break;
1035 				case 'V': // week of the year (ISO 8601)
1036 				case 'W': // W is not entirely correct, but that's a
1037 					// lot easier this way.
1038 					iVal = qDt.date().weekNumber();
1039 					cDiv = '0';
1040 					break;
1041 				case 'w': // day of week (0-6, 0==Sunday)
1042 					iTemp = qDt.date().dayOfWeek();
1043 					if(iTemp == Qt::Sunday)
1044 						iTemp = 0;
1045 					szFmtTime += QString::number(iTemp);
1046 					break;
1047 				case 'y': // year (2-character)
1048 					szFmtTime += qDt.toString("yy");
1049 					break;
1050 				case 'Y': // year (4-character)
1051 					szFmtTime += qDt.toString("yyyy");
1052 					break;
1053 				case 'z': // numerical timezone offset
1054 					iLocalTzH = qDt.time().hour();
1055 					iLocalTzM = qDt.time().minute();
1056 					iUtcH = qDt.toUTC().time().hour();
1057 					iUtcM = qDt.toUTC().time().minute();
1058 					iTzOffsetH = iLocalTzH - iUtcH;
1059 					iTzOffsetM = iLocalTzM - iUtcM;
1060 					iTzOffset = iTzOffsetH * 100 + iTzOffsetM;
1061 
1062 					//clamp to -12:00 / +12:00 (ticket #924)
1063 					if(iTzOffset < 1200)
1064 						iTzOffset += 2400;
1065 
1066 					if(iTzOffset > 1200)
1067 						iTzOffset -= 2400;
1068 
1069 					if(iTzOffset > 0)
1070 						szFmtTime += "+";
1071 
1072 					szFmtTime += QString("%1").arg(iTzOffset, 4, 10, QChar('0'));
1073 					break;
1074 				// FIXME
1075 				// The abbrev. time zone name is a little bit trickier,
1076 				// as I don't want to reimplement KTimeZone, but can't
1077 				// use KTimeZone. I'll need to do some research on this.
1078 				//case 'Z':   // TZ abbrev.
1079 				//    szFmtTime +=
1080 				default:
1081 					szFmtTime += i;
1082 			}
1083 
1084 			if(iVal != 0x8000000)
1085 			{
1086 				if(iVal < 0)
1087 				{
1088 					iVal = -iVal;
1089 					szFmtTime += "-";
1090 				}
1091 				szFmtTime += QString("%1").arg(iVal, iLength, 10, cDiv);
1092 			}
1093 		}
1094 
1095 		KVSCF_pRetBuffer->setString(szFmtTime);
1096 
1097 	leavenow:
1098 		return true;
1099 	}
1100 
1101 	/*
1102 		@doc: escape
1103 		@type:
1104 			function
1105 		@title:
1106 			$escape
1107 		@short:
1108 			Returns a kvs-escaped version of the string
1109 		@syntax:
1110 			<string> $escape(<text:string>)
1111 		@description:
1112 			In KVS some characters in a string have special meanings: [b]%[/b] marks the start of a variable name, [b]$[/b] the start of a function name, etc..[br]
1113 			Sometimes you could need to escape them using a [b]\[/b] character to avoid KVIrc from interpreting the special meaning of these characters:
1114 			this function will to the dirty job for you, returning a correctly kvs-escaped version of the string passed as a parameter.[br]
1115 		@seealso:
1116 			[cmd]eval[/cmd]
1117 	*/
1118 
KVSCF(escape)1119 	KVSCF(escape)
1120 	{
1121 		QString szData;
1122 		KVSCF_PARAMETERS_BEGIN
1123 		KVSCF_PARAMETER("text", KVS_PT_STRING, KVS_PF_OPTIONAL, szData)
1124 		KVSCF_PARAMETERS_END
1125 
1126 		if(!szData.isEmpty())
1127 			KviQString::escapeKvs(&szData);
1128 
1129 		KVSCF_pRetBuffer->setString(szData);
1130 		return true;
1131 	}
1132 
1133 	/*
1134 		@doc: false
1135 		@type:
1136 			function
1137 		@title:
1138 			$false
1139 		@short:
1140 			The boolean false constant
1141 		@syntax:
1142 			<boolean> $false
1143 		@description:
1144 			Evaluates to the false boolean constant. False
1145 			is equivalent to the integer [b]0[/b] too. This function/constant
1146 			is useful to keep your code readable: when you
1147 			have a variable that can assume boolean values it's
1148 			nicer to use [fnc]$true[/fnc] and $false instead of
1149 			the integer constants [b]1[/b] and [b]0[/b]. The reader will
1150 			understand immediately that the variable simply can't
1151 			assume any other value.
1152 		@examples:
1153 			[example]
1154 				%a = $false
1155 				[cmd]echo[/cmd] $typeof(%a)
1156 				[cmd]echo[/cmd] $(%a + 1)
1157 			[/example]
1158 		@seealso:
1159 			[fnc]$true[/fnc]
1160 	*/
1161 
KVSCF(falseCKEYWORDWORKAROUND)1162 	KVSCF(falseCKEYWORDWORKAROUND)
1163 	{
1164 		KVSCF_pRetBuffer->setBoolean(false);
1165 		return true;
1166 	}
1167 
1168 	/*
1169 		@doc: features
1170 		@type:
1171 			function
1172 		@title:
1173 			$features
1174 		@short:
1175 			Returns the features that KVIrc supports
1176 		@syntax:
1177 			<array> $features()
1178 			<boolean> $features(<test_feature:string>)
1179 		@description:
1180 			The parameterless form returns an array of feature description strings that this KVIrc executable supports.[br]
1181 			This function is useful when some part of your script depends on
1182 			an optional KVIrc feature (like SSL support or IPv6 support).[br]
1183 			The returned value may be assigned to a dictionary too: it will be used to simulate an array.[br]
1184 			The form with the [test_feature] parameter returns true if and only if [test_feature] is available.[br]
1185 		@examples:
1186 			[example]
1187 				%myfeats[] = $features
1188 				[cmd]echo[/cmd] %myfeats[]
1189 				%i = %myfeats[]#
1190 				[cmd]while[/cmd](%i > 0)
1191 				{
1192 					%i--;
1193 					[cmd]echo[/cmd] "Supporting feature %myfeats[%i]"
1194 				}
1195 			[/example]
1196 			Nearly the same loop, just really shorter:
1197 			[example]
1198 			[cmd]foreach[/cmd](%f,$features)
1199 				[cmd]echo[/cmd] "Supporting feature %myfeats[%i]"
1200 			[/example]
1201 			You can test for a specific feature in the following way:
1202 			[example]
1203 				[cmd]if[/cmd]($features("SSL"))[cmd]echo[/cmd] "Yes! SSL is available";
1204 			[/example]
1205 			If used in [i]non-array[/i] context it returns just a comma separated list of entries:
1206 			[example]
1207 				[cmd]echo[/cmd] $features
1208 			[/example]
1209 		@seealso:
1210 			[fnc]$version[/fnc]
1211 	*/
1212 
KVSCF(features)1213 	KVSCF(features)
1214 	{
1215 		QString szFeature;
1216 		KVSCF_PARAMETERS_BEGIN
1217 		KVSCF_PARAMETER("test_feature", KVS_PT_STRING, KVS_PF_OPTIONAL, szFeature)
1218 		KVSCF_PARAMETERS_END
1219 
1220 		if(!szFeature.isEmpty())
1221 		{
1222 			KVSCF_pRetBuffer->setBoolean(feature_list.contains(szFeature, Qt::CaseInsensitive));
1223 		}
1224 		else
1225 		{
1226 			KviKvsArray * a = new KviKvsArray();
1227 			for(auto&& f : feature_list)
1228 				a->append(new KviKvsVariant(f));
1229 			KVSCF_pRetBuffer->setArray(a);
1230 		}
1231 
1232 		return true;
1233 	}
1234 
1235 	/*
1236 		@doc: firstconnectedconsole
1237 		@type:
1238 			function
1239 		@title:
1240 			$firstConnectedConsole
1241 		@short:
1242 			Returns the window ID of the first connected console
1243 		@syntax:
1244 			<uint> $firstConnectedConsole()
1245 		@description:
1246 			Returns the window ID of the first connected console
1247 			or 0 if no console is currently connected.
1248 	*/
1249 
KVSCF(firstConnectedConsole)1250 	KVSCF(firstConnectedConsole)
1251 	{
1252 		KviConsoleWindow * c = g_pApp->topmostConnectedConsole();
1253 		KVSCF_pRetBuffer->setInteger(c ? c->numericId() : 0);
1254 		return true;
1255 	}
1256 
1257 	/*
1258 		@doc: flatten
1259 		@type:
1260 			function
1261 		@title:
1262 			$flatten
1263 		@short:
1264 			Returns a flattened array of items
1265 		@syntax:
1266 			<array> $flatten(<data1:variant>[,<data2:variant>[,...]])
1267 		@description:
1268 			Returns an array of items built from the passed arguments
1269 			with the following rules:
1270 			[ul]
1271 				[li]If an argument is a scalar value then the argument itself is appended to the result.[/li]
1272 				[li]If an argument is an array then each contained item is appended to the result.[/li]
1273 				[li]If an argument is a hash then each contained value is appended to the result.[/li]
1274 			[/ul]
1275 			A simple example of usage is to merge N arrays into a new one.
1276 			(Please note that for merging one array into another the
1277 			[doc:arrayconcatenation]array concatenation operator[/doc] is more efficient).
1278 	*/
1279 
KVSCF(flatten)1280 	KVSCF(flatten)
1281 	{
1282 		KviKvsArray * a = new KviKvsArray();
1283 		KVSCF_pRetBuffer->setArray(a);
1284 		unsigned int uIdx = 0;
1285 		for(KviKvsVariant * v = KVSCF_pParams->first(); v; v = KVSCF_pParams->next())
1286 		{
1287 			switch(v->type())
1288 			{
1289 				case KviKvsVariantData::Array:
1290 				{
1291 					KviKvsArray * z = v->array();
1292 					unsigned int uSize = z->size();
1293 					unsigned int uIdx2 = 0;
1294 					while(uIdx2 < uSize)
1295 					{
1296 						KviKvsVariant * pInternal = z->at(uIdx2);
1297 						if(pInternal)
1298 							a->set(uIdx, new KviKvsVariant(*pInternal));
1299 						// else
1300 						//	don't set anything: just leave empty entry (nothing)
1301 						uIdx++;
1302 						uIdx2++;
1303 					}
1304 				}
1305 				break;
1306 				case KviKvsVariantData::Hash:
1307 				{
1308 					KviKvsHash * h = v->hash();
1309 					KviKvsHashIterator it(*(h->dict()));
1310 					while(KviKvsVariant * pInternal = it.current())
1311 					{
1312 						a->set(uIdx, new KviKvsVariant(*pInternal));
1313 						uIdx++;
1314 						++it;
1315 					}
1316 				}
1317 				break;
1318 				default:
1319 					a->set(uIdx, new KviKvsVariant(*v));
1320 					uIdx++;
1321 					break;
1322 			}
1323 		}
1324 		return true;
1325 	}
1326 
1327 	/*
1328 		@doc: fmtlink
1329 		@type:
1330 			function
1331 		@title:
1332 			$fmtlink
1333 		@short:
1334 			Returns a formatted link buffer
1335 		@syntax:
1336 			<string> $fmtlink(<link_text:string>,<double_click_command:string>[,<tooltip_text:string>])
1337 		@description:
1338 			Returns a link formatted for the [cmd]echo[/cmd] command.[br]
1339 			If you pass the returned string to the echo command, the string will be displayed
1340 			as a link and will be highlighted when the user moves the mouse over it.[br]
1341 			If the user leaves the mouse for a few seconds over the link, the <tooltip_text>
1342 			will be displayed in a small tooltip window. If <tooltip_text> is not given,
1343 			then no tooltip will be shown.[br]
1344 			The <double_click_command> will be executed when the user will double click on the link.[br]
1345 			Please remember that if <double_click_command> contains identifiers
1346 			that must be evaluated at double-click time, you MUST escape them in the $fmtlink() call
1347 			to prevent the evaluation.[br]
1348 			You might also take a look at [doc:escape_sequences]the escape sequences documentation[/doc]
1349 			to learn more about how the links are implemented and how to create more powerful links (add
1350 			right and middle button actions, use predefined KVIrc links etc...). Also take a look at [fnc]$link[/fnc]
1351 			which has related functionality.
1352 		@seealso:
1353 			[cmd]echo[/cmd], [doc:escape_sequences]the escape sequences documentation[/doc], [fnc]$link[/fnc]
1354 	*/
1355 
KVSCF(fmtlink)1356 	KVSCF(fmtlink)
1357 	{
1358 		QString szLinkText, szCmd, szToolTip;
1359 		KVSCF_PARAMETERS_BEGIN
1360 		KVSCF_PARAMETER("link_text", KVS_PT_NONEMPTYSTRING, 0, szLinkText)
1361 		KVSCF_PARAMETER("double_click_command", KVS_PT_STRING, 0, szCmd)
1362 		KVSCF_PARAMETER("tooltip_text", KVS_PT_STRING, KVS_PF_OPTIONAL, szToolTip)
1363 		KVSCF_PARAMETERS_END
1364 
1365 		QString szPart = QString("[!dbl]%1").arg(szCmd);
1366 		if(!szToolTip.isEmpty())
1367 			KviQString::appendFormatted(szPart, "[!txt]%Q", &szToolTip);
1368 		QString szLink = QString("\r!%1\r%2\r").arg(szPart, szLinkText);
1369 
1370 		KVSCF_pRetBuffer->setString(szLink);
1371 		return true;
1372 	}
1373 };
1374