1 /*
2  * functions.c -- Built-in functions for ircII
3  *
4  * Copyright (c) 1990 Michael Sandroff.
5  * Copyright (c) 1991, 1992 Troy Rollo.
6  * Copyright (c) 1992-1996 Matthew Green.
7  * Copyright 1993, 2010 EPIC Software Labs
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notices, the above paragraph (the one permitting redistribution),
17  *    this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. The names of the author(s) may not be used to endorse or promote
20  *    products derived from this software without specific prior written
21  *    permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 /*
36  * Some of the "others" include:
37  *	Matt Carothers 			Colten Edwards
38  *	James Sneeringer		Eli Sand
39  */
40 /*
41  * I split alias.c because it was just getting out of hand.
42  */
43 
44 #include "irc.h"
45 #define __need_ArgList_t__
46 #include "alias.h"
47 #include "alist.h"
48 #include "array.h"
49 #include "clock.h"
50 #include "ctcp.h"
51 #include "dcc.h"
52 #include "debug.h"
53 #include "commands.h"
54 #include "exec.h"
55 #include "files.h"
56 #include "flood.h"
57 #include "ignore.h"
58 #include "input.h"
59 #include "ircaux.h"
60 #include "keys.h"
61 #include "log.h"
62 #include "names.h"
63 #include "output.h"
64 #include "parse.h"
65 #include "screen.h"
66 #include "server.h"
67 #include "status.h"
68 #include "vars.h"
69 #include "window.h"
70 #include "termx.h"
71 #include "notify.h"
72 #include "numbers.h"
73 #include "sedcrypt.h"
74 #include "timer.h"
75 #define need_static_functions
76 #include "functions.h"
77 #include "options.h"
78 #include "words.h"
79 #include "reg.h"
80 #include "ifcmd.h"
81 #include "ssl.h"
82 #include "levels.h"
83 #include "extlang.h"
84 #include "ctcp.h"
85 #include "cJSON.h"
86 
87 #ifdef NEED_GLOB
88 # include "glob.h"
89 #else
90 # include <glob.h>
91 #endif
92 
93 #ifdef HAVE_REGEX_H
94 # include <regex.h>
95 #endif
96 #ifdef HAVE_UNAME
97 # include <sys/utsname.h>
98 #endif
99 #include <math.h>
100 
101 static	char
102 	*alias_detected 	(void), *alias_sent_nick 	(void),
103 	*alias_recv_nick 	(void), *alias_msg_body 	(void),
104 	*alias_joined_nick 	(void), *alias_public_nick 	(void),
105 	*alias_dollar 		(void), *alias_channel 		(void),
106 	*alias_server 		(void), *alias_query_nick 	(void),
107 	*alias_target 		(void), *alias_nick 		(void),
108 	*alias_invite 		(void), *alias_cmdchar 		(void),
109 	*alias_line 		(void), *alias_away 		(void),
110 	*alias_oper 		(void), *alias_chanop 		(void),
111 	*alias_modes 		(void), *alias_buffer 		(void),
112 	*alias_time 		(void), *alias_version 		(void),
113 	*alias_currdir 		(void), *alias_current_numeric	(void),
114 	*alias_server_version 	(void), *alias_show_userhost 	(void),
115 	*alias_show_realname 	(void), *alias_online 		(void),
116 	*alias_idle 		(void), *alias_version_str 	(void),
117 	*alias_banner		(void);
118 
119 typedef struct
120 {
121 	const char *	name;
122 	char *		(*func) (void);
123 }	BuiltIns;
124 
125 static	BuiltIns built_in[] =
126 {
127 	{ ".",		alias_sent_nick 	},
128 	{ ",",		alias_recv_nick 	},
129 	{ ":",		alias_joined_nick 	},
130 	{ ";",		alias_public_nick 	},
131 	{ "$",		alias_dollar 		},
132 	{ "A",		alias_away 		},
133 	{ "B",		alias_msg_body 		},
134 	{ "C",		alias_channel 		},
135 	{ "D",		alias_detected 		},
136 	{ "E",		alias_idle 		},
137 	{ "F",		alias_online 		},
138 	{ "G",		alias_banner		},
139 	{ "H", 		alias_current_numeric 	},
140 	{ "I",		alias_invite 		},
141 	{ "J",		alias_version_str 	},
142 	{ "K",		alias_cmdchar 		},
143 	{ "L",		alias_line 		},
144 	{ "M",		alias_modes 		},
145 	{ "N",		alias_nick 		},
146 	{ "O",		alias_oper 		},
147 	{ "P",		alias_chanop 		},
148 	{ "Q",		alias_query_nick 	},
149 	{ "R",		alias_server_version 	},
150 	{ "S",		alias_server 		},
151 	{ "T",		alias_target 		},
152 	{ "U",		alias_buffer 		},
153 	{ "V",		alias_version 		},
154 	{ "W",		alias_currdir 		},
155 	{ "X",		alias_show_userhost 	},
156 	{ "Y",		alias_show_realname 	},
157 	{ "Z",		alias_time 		},
158 	{ 0,	 	NULL 			}
159 };
160 
161 /* the 30 "standard" functions */
162 static	char
163 	*function_channels 	(char *),
164 	*function_connect 	(char *),
165 	*function_curpos 	(char *),
166 	*function_index 	(char *),
167 	*function_ischannel 	(char *),
168 	*function_ischanop 	(char *),
169 	*function_left 		(char *),
170 	*function_listen 	(char *),
171 	*function_match 	(char *),
172 	*function_mid 		(char *),
173 	*function_pid 		(char *),
174 	*function_ppid 		(char *),
175 	*function_rand 		(char *),
176 	*function_right 	(char *),
177 	*function_rindex 	(char *),
178 	*function_rmatch 	(char *),
179 	*function_servers 	(char *),
180 	*function_srand 	(char *),
181 	*function_stime 	(char *),
182 	*function_strip 	(char *),
183 	*function_tdiff 	(char *),
184 	*function_tdiff2 	(char *),
185 	*function_time 		(char *),
186 	*function_tolower 	(char *),
187 	*function_toupper 	(char *),
188 	*function_userhost 	(char *),
189 	*function_word 		(char *),
190 	*function_utime		(char *),
191 	*function_strftime	(char *),
192 
193 /* the countless "extended" functions */
194 	*function_abs		(char *),
195 	*function_acos		(char *),
196 	*function_asin		(char *),
197 	*function_atan		(char *),
198 	*function_acosh		(char *),
199 	*function_asinh		(char *),
200 	*function_atanh		(char *),
201 	*function_after 	(char *),
202 	*function_afterw 	(char *),
203 	*function_aliasctl	(char *),
204 	*function_ascii 	(char *),
205 	*function_asciiq 	(char *),
206 	*function_before 	(char *),
207 	*function_beforew 	(char *),
208 	*function_bindctl	(char *),
209 	*function_builtin	(char *),
210 	*function_ceil		(char *),
211 	*function_center 	(char *),
212 	*function_cexist	(char *),
213 	*function_chankey	(char *),
214 	*function_channel	(char *),
215 	*function_channellimit	(char *),
216 	*function_channelmode	(char *),
217 	*function_channelsyncing (char *),
218 	*function_check_code	(char *),
219 	*function_chmod		(char *),
220 	*function_chngw 	(char *),
221 	*function_chop		(char *),
222 	*function_chops 	(char *),
223 	*function_chr 		(char *),
224 	*function_chrq 		(char *),
225 	*function_cipher	(char *),
226 	*function_close 	(char *),
227 	*function_cofilter	(char *),
228 	*function_corfilter	(char *),
229 	*function_common 	(char *),
230 	*function_convert 	(char *),
231 	*function_copattern 	(char *),
232 	*function_corpattern 	(char *),
233 	*function_cos		(char *),
234 	*function_cosh		(char *),
235 	*function_count		(char *),
236 	*function_crypt 	(char *),
237 	*function_curcmd	(char *),
238 	*function_currchans	(char *),
239 	*function_dbmctl	(char *),
240 	*function_dccctl	(char *),
241 	*function_deuhc		(char *),
242 	*function_diff 		(char *),
243 	*function_encryptparm 	(char *),
244 	*function_eof 		(char *),
245 	*function_epic		(char *),
246 	*function_error		(char *),
247 	*function_exec		(char *),
248 	*function_execctl	(char *),
249 	*function_exp		(char *),
250 	*function_fnexist	(char *),
251 	*function_fexist 	(char *),
252 	*function_filter 	(char *),
253 	*function_findw		(char *),
254 	*function_findws	(char *),
255 	*function_fix_arglist	(char *),
256         *function_fix_width     (char *),
257 	*function_floor		(char *),
258 	*function_fromw 	(char *),
259 	*function_fsize	 	(char *),
260 	*function_ftime		(char *),
261 	*function_ftruncate 	(char *),
262 	*function_functioncall	(char *),
263 	*function_geom		(char *),
264 	*function_getcap	(char *),
265 	*function_getcommands	(char *),
266 	*function_getenv	(char *),
267 	*function_getfunctions	(char *),
268 	*function_getgid	(char *),
269 	*function_getlogin	(char *),
270 	*function_getopt	(char *),
271 	*function_getpgrp	(char *),
272 	*function_getserial	(char *),
273 	*function_getset	(char *),
274 	*function_getsets	(char *),
275 	*function_getuid	(char *),
276 	*function_glob		(char *),
277 	*function_globi		(char *),
278 	*function_hash_32bit	(char *),
279 #if 0
280 	*function_help_topics	(char *),
281 #endif
282 	*function_hookctl	(char *),
283 	*function_iconvctl	(char *),
284 	*function_idle		(char *),
285 	*function_ignorectl	(char *),
286 	*function_indextoword	(char *),
287 	*function_info		(char *),
288 	*function_insert 	(char *),
289 	*function_insertw 	(char *),
290 	*function_iptolong	(char *),
291 	*function_iptoname 	(char *),
292 	*function_irclib	(char *),
293 	*function_is8bit 	(char *),
294 	*function_isalpha 	(char *),
295 	*function_isaway	(char *),
296 	*function_ischanvoice	(char *),
297 	*function_isconnected	(char *),
298 	*function_iscurchan	(char *),
299 	*function_isdigit 	(char *),
300 	*function_isdisplaying	(char *),
301 	*function_isencrypted	(char *),
302 	*function_isfilevalid	(char *),
303 	*function_ishalfop	(char *),
304 	*function_isnumber	(char *),
305 	*function_isutf8	(char *),
306 	*function_jn		(char *),
307 	*function_joinstr	(char *),
308 	*function_jot 		(char *),
309 	*function_json_error	(char *),
310 	*function_json_explode	(char *),
311 	*function_json_implode	(char *),
312 	*function_key 		(char *),
313 	*function_killpid	(char *),
314 	*function_leftpc	(char *),
315 	*function_leftw 	(char *),
316 	*function_levelctl	(char *),
317 	*function_levelwindow	(char *),
318 	*function_loadinfo	(char *),
319 	*function_log		(char *),
320 	*function_log10		(char *),
321 	*function_logctl	(char *),
322 	*function_longtoip	(char *),
323 	*function_mask		(char *),
324 	*function_maxlen	(char *),
325 	*function_metric_time	(char *),
326 	*function_midw 		(char *),
327 	*function_mkdir		(char *),
328 	*function_mktime	(char *),
329 	*function_msar		(char *),
330 	*function_nametoip 	(char *),
331 	*function_nochops 	(char *),
332 	*function_nohighlight	(char *),
333 	*function_notify	(char *),
334 	*function_notifywindows	(char *),
335 	*function_notw 		(char *),
336 	*function_numlines	(char *),
337 	*function_numonchannel 	(char *),
338 	*function_numsort	(char *),
339 	*function_numwords 	(char *),
340 	*function_onchannel 	(char *),
341 	*function_open 		(char *),
342 	*function_outputinfo	(char *),
343 	*function_pad		(char *),
344 	*function_pattern 	(char *),
345 	*function_pass		(char *),
346 #ifdef HAVE_PERL
347 	*function_perl		(char *),
348 	*function_perlcall	(char *),
349 	*function_perlxcall	(char *),
350 #endif
351 #ifdef HAVE_PYTHON
352 	*function_pydirect	(char *),
353 	*function_python	(char *),
354 #endif
355 	*function_prefix	(char *),
356 	*function_printlen	(char *),
357 	*function_querywin	(char *),
358 	*function_qword		(char *),
359 	*function_randread	(char *),
360 	*function_read 		(char *),
361 	*function_realpath	(char *),
362 	*function_regcomp	(char *),
363 	*function_regcomp_cs	(char *),
364 	*function_regexec	(char *),
365 	*function_regerror	(char *),
366 	*function_regfree	(char *),
367 	*function_regmatches	(char *),
368 	*function_remw 		(char *),
369 	*function_remws		(char *),
370 	*function_rename 	(char *),
371 	*function_repeat	(char *),
372 	*function_rest		(char *),
373 	*function_restw 	(char *),
374 	*function_reverse 	(char *),
375 	*function_revw 		(char *),
376 	*function_rewind	(char *),
377 	*function_rfilter 	(char *),
378 	*function_rightw 	(char *),
379 	*function_rmdir 	(char *),
380 	*function_rpattern 	(char *),
381 	*function_rsubstr	(char *),
382 #ifdef HAVE_RUBY
383 	*function_ruby		(char *),
384 #endif
385 	*function_sar 		(char *),
386 	*function_seek		(char *),
387 	*function_server_version (char *),
388 	*function_serverctl	(char *),
389 	*function_servports	(char *),
390 	*function_serverwin	(char *),
391 	*function_sin		(char *),
392 	*function_sinh		(char *),
393 	*function_skip		(char *),
394 	*function_sort		(char *),
395 	*function_split 	(char *),
396 	*function_splitw	(char *),
397 	*function_splice 	(char *),
398 	*function_ssl		(char *),
399 	*function_startupfile	(char *),
400 	*function_stat		(char *),
401 	*function_status	(char *),
402 	*function_stripansi	(char *),
403 	*function_stripansicodes(char *),
404 	*function_stripc 	(char *),
405 	*function_stripcrap	(char *),
406 	*function_strlen	(char *),
407 	*function_strptime	(char *),
408 	*function_strtol	(char *),
409 	*function_substr	(char *),
410 	*function_symbolctl	(char *),
411 	*function_tan		(char *),
412 	*function_tanh		(char *),
413 	*function_tell		(char *),
414 	*function_timerctl	(char *),
415 #ifdef HAVE_TCL
416 	*function_tcl		(char *),
417 #endif
418 	*function_tobase	(char *),
419         *function_tow		(char *),
420 	*function_translate 	(char *),
421 	*function_truncate 	(char *),
422 	*function_ttyname	(char *),
423 	*function_twiddle	(char *),
424 	*function_uhc		(char *),
425 	*function_umask		(char *),
426 	*function_umode		(char *),
427 	*function_uname		(char *),
428 	*function_unicode	(char *),
429 	*function_uniq		(char *),
430 	*function_unlink 	(char *),
431 	*function_unsplit	(char *),
432 	*function_uuid4		(char *),
433 	*function_which 	(char *),
434 	*function_winchan	(char *),
435 	*function_windowctl	(char *),
436 	*function_wordtoindex	(char *),
437 	*function_write 	(char *),
438 	*function_writeb	(char *),
439 	*function_xform		(char *),
440 	*function_yn		(char *),
441 	*function_cp437test	(char *);
442 
443 char	*wrapper_pattern	(char *, int),
444 	*wrapper_rpattern	(char *, int);
445 
446 extern char
447 	*function_cparse	(char *),
448 	*function_push		(char *),
449 	*function_pop		(char *),
450 	*function_shift		(char *),
451         *function_shiftseg      (char *),
452         *function_unshift	(char *),
453 	*function_shiftbrace	(char *);
454 
455 typedef char *(bf) (char *);
456 typedef struct
457 {
458 	const char	*name;
459 	bf 		*func;
460 }	BuiltInFunctions;
461 
462 /*
463  * This is the built-in function list.  This list *must* be sorted because
464  * it is binary searched.   See the code for each function to see how it
465  * is used.  Or see the help files.  Or see both.  Oh heck.  Look at the code
466  * and see how it REALLY works, regardless of the documentation >;-)
467  */
468 static BuiltInFunctions	built_in_functions[] =
469 {
470 	{ "ABS",		function_abs		},
471 	{ "ACOS",		function_acos		},
472 	{ "ACOSH",		function_acosh		},
473 	{ "AFTER",              function_after 		},
474 	{ "AFTERW",             function_afterw 	},
475 	{ "ALIASCTL",		function_aliasctl	},
476 	{ "ASCII",		function_ascii 		},
477 	{ "ASCIIQ",		function_asciiq 	},
478 	{ "ASIN",		function_asin		},
479 	{ "ASINH",		function_asinh		},
480 	{ "ATAN",		function_atan		},
481 	{ "ATANH",		function_atanh		},
482 	{ "BEFORE",             function_before 	},
483 	{ "BEFOREW",            function_beforew 	},
484 	{ "BINDCTL",		function_bindctl	},
485 	{ "BUILTIN_EXPANDO",	function_builtin	},
486 	{ "CEIL",		function_ceil	 	},
487 	{ "CENTER",		function_center 	},
488 	{ "CEXIST",		function_cexist		},
489 	{ "CHANKEY",		function_chankey	},
490 	{ "CHANLIMIT",		function_channellimit	},
491 	{ "CHANMODE",		function_channelmode	},
492 	{ "CHANNEL",		function_channel	},
493 	{ "CHANUSERS",		function_onchannel 	},
494 	{ "CHANWIN",		function_winchan	},
495 	{ "CHANSYNCING",	function_channelsyncing },
496 	{ "CHECK_CODE",		function_check_code	},
497 	{ "CHMOD",		function_chmod		},
498 	{ "CHNGW",              function_chngw 		},
499 	{ "CHOP",		function_chop		},
500 	{ "CHOPS",              function_chops 		},
501 	{ "CHR",		function_chr 		},
502 	{ "CHRQ",		function_chrq 		},
503 	{ "CIPHER",		function_cipher		},
504 	{ "CLOSE",		function_close 		},
505 	{ "COFILTER",		function_cofilter	},
506 	{ "COMMON",             function_common 	},
507 	{ "CONNECT",		function_connect 	},
508 	{ "CONVERT",		function_convert 	},
509 	{ "COPATTERN",          function_copattern 	},
510 	{ "CORFILTER",		function_corfilter	},
511 	{ "CORPATTERN",		function_corpattern 	},
512 	{ "COS",		function_cos		},
513 	{ "COSH",		function_cosh		},
514 	{ "COUNT",		function_count		},
515 	{ "CP437TEST",		function_cp437test	},
516 	{ "CPARSE",		function_cparse		},
517 	{ "CRYPT",		function_crypt		},
518 	{ "CTCPCTL",		function_ctcpctl	},
519 	{ "CURCMD",		function_curcmd		},
520 	{ "CURPOS",		function_curpos 	},
521 	{ "CURRCHANS",		function_currchans	},
522 	{ "DBMCTL",		function_dbmctl		},
523 	{ "DCCCTL",		function_dccctl		},
524 	{ "DELARRAY",           function_delarray 	},
525 	{ "DELITEM",            function_delitem	},
526 	{ "DELITEMS",           function_delitems	},
527 	{ "DEUHC",		function_deuhc		},
528 	{ "DIFF",               function_diff 		},
529 	{ "ENCODINGCTL",	function_encodingctl	},
530 	{ "ENCRYPTPARM",	function_encryptparm	},
531 	{ "EOF",		function_eof 		},
532 	{ "EPIC",		function_epic		},
533 	{ "EXEC",		function_exec		},
534 	{ "EXECCTL",		function_execctl	},
535 	{ "EXP",		function_exp		},
536 	{ "FERROR",		function_error		},
537 	{ "FEXIST",             function_fexist 	},
538 	{ "FILTER",             function_filter 	},
539 	{ "FINDITEM",		function_finditem 	},
540 	{ "FINDITEMS",		function_finditems 	},
541 	{ "FINDW",		function_findw		},
542 	{ "FINDWS",		function_findws		},
543 	{ "FIX_ARGLIST",	function_fix_arglist	},
544         { "FIX_WIDTH",          function_fix_width      },
545 	{ "FLOODINFO",		function_floodinfo	},
546 	{ "FLOOR",		function_floor		},
547 	{ "FNEXIST",		function_fnexist	},
548 	{ "FREWIND",		function_rewind		},
549 	{ "FROMW",              function_fromw 		},
550 	{ "FSEEK",		function_seek		},
551 	{ "FSIZE",		function_fsize		},
552 	{ "FSKIP",		function_skip		},
553 	{ "FTELL",		function_tell		},
554 	{ "FTIME",		function_ftime		},
555 	{ "FTRUNCATE",		function_ftruncate 	},
556 	{ "FUNCTIONCALL",	function_functioncall	},
557 	{ "GEOM",		function_geom		},
558 	{ "GETARRAYS",          function_getarrays 	},
559 	{ "GETCAP",		function_getcap		},
560 	{ "GETCOMMANDS",	function_getcommands	},
561 	{ "GETENV",		function_getenv		},
562 	{ "GETFUNCTIONS",	function_getfunctions	},
563 	{ "GETGID",		function_getgid		},
564 	{ "GETITEM",            function_getitem 	},
565 	{ "GETLOGIN",		function_getlogin	},
566 	{ "GETMATCHES",         function_getmatches 	},
567 	{ "GETOPT",		function_getopt		},
568 	{ "GETPGRP",		function_getpgrp	},
569 	{ "GETRMATCHES",        function_getrmatches 	},
570 	{ "GETSERIAL",		function_getserial	},
571 	{ "GETSET",		function_getset		},
572 	{ "GETSETS",		function_getsets	},
573 	{ "GETTMATCH",		function_gettmatch	},
574 	{ "GETUID",		function_getuid		},
575 	{ "GLOB",		function_glob		},
576 	{ "GLOBI",		function_globi		},
577 	{ "HASH_32BIT",		function_hash_32bit	},
578 #if 0
579 	{ "HELP_TOPICS",	function_help_topics	},
580 #endif
581 	{ "HOOKCTL",		function_hookctl	},
582 	{ "ICONVCTL",		function_iconvctl	},
583 	{ "IDLE",		function_idle		},
584 	{ "IFINDFIRST",		function_ifindfirst 	},
585 	{ "IFINDITEM",		function_ifinditem	},
586 	{ "IFINDITEMS",		function_ifinditems	},
587 	{ "IGETITEM",           function_igetitem 	},
588 	{ "IGETMATCHES",	function_igetmatches	},
589 	{ "IGETRMATCHES",	function_igetrmatches	},
590 	{ "IGNORECTL",		function_ignorectl	},
591 	{ "INDEX",		function_index 		},
592 	{ "INDEXTOITEM",        function_indextoitem 	},
593 	{ "INDEXTOWORD",	function_indextoword	},
594 	{ "INFO",		function_info		},
595 	{ "INPUTCTL",		function_inputctl	},
596 	{ "INSERT",		function_insert 	},
597 	{ "INSERTW",            function_insertw 	},
598 	{ "IPTOLONG",		function_iptolong	},
599 	{ "IPTONAME",		function_iptoname 	},
600 	{ "IRCLIB",		function_irclib		},
601 	{ "IS8BIT",		function_is8bit 	},
602 	{ "ISALPHA",		function_isalpha 	},
603 	{ "ISAWAY",		function_isaway		},
604 	{ "ISCHANNEL",		function_ischannel 	},
605 	{ "ISCHANOP",		function_ischanop 	},
606 	{ "ISCHANVOICE",	function_ischanvoice	},
607 	{ "ISCONNECTED",	function_isconnected	},
608 	{ "ISCURCHAN",		function_iscurchan	},
609 	{ "ISDIGIT",		function_isdigit 	},
610 	{ "ISDISPLAYING",	function_isdisplaying	},
611 	{ "ISENCRYPTED",	function_isencrypted	},
612 	{ "ISFILEVALID",	function_isfilevalid	},
613 	{ "ISHALFOP",		function_ishalfop	},
614 	{ "ISNUMBER",		function_isnumber	},
615 	{ "ISUTF8",		function_isutf8		},
616 	{ "ITEMTOINDEX",        function_itemtoindex 	},
617 	{ "JN",			function_jn		},
618 	{ "JOINSTR",		function_joinstr	},
619 	{ "JOT",                function_jot 		},
620 	{ "JSON_ERROR",         function_json_error 	},
621 	{ "JSON_EXPLODE",       function_json_explode 	},
622 	{ "JSON_IMPLODE",       function_json_implode 	},
623 	{ "KEY",                function_key 		},
624 	{ "KILLPID",		function_killpid	},
625 	{ "LASTLOG",		function_lastlog	}, /* lastlog.h */
626 	{ "LEFT",		function_left 		},
627 	{ "LEFTPC",		function_leftpc		},
628 	{ "LEFTW",              function_leftw 		},
629 	{ "LEVELCTL",		function_levelctl	},
630 	{ "LEVELWINDOW",	function_levelwindow	},
631 	{ "LINE",		function_line		}, /* lastlog.h */
632 	{ "LISTARRAY",		function_listarray	},
633 	{ "LISTEN",		function_listen 	},
634 	{ "LOADINFO",		function_loadinfo	},
635 	{ "LOG",		function_log		},
636 	{ "LOG10",		function_log10		},
637 	{ "LOGCTL",		function_logctl		}, /* logfiles.h */
638 	{ "LONGTOIP",		function_longtoip	},
639 	{ "MASK",		function_mask		},
640 	{ "MATCH",		function_match 		},
641 	{ "MATCHITEM",          function_matchitem 	},
642 	{ "MAXLEN",		function_maxlen		},
643 	{ "METRIC_TIME",	function_metric_time	},
644 	{ "MID",		function_mid 		},
645 	{ "MIDW",               function_midw 		},
646 	{ "MKDIR",		function_mkdir		},
647 	{ "MKTIME",		function_mktime		},
648 	{ "MSAR",		function_msar		},
649 	{ "MYCHANNELS",		function_channels 	},
650 	{ "MYSERVERS",		function_servers 	},
651 	{ "NAMETOIP",		function_nametoip 	},
652 	{ "NOCHOPS",            function_nochops 	},
653 	{ "NOHIGHLIGHT",	function_nohighlight	},
654 	{ "NOTIFY",		function_notify		},
655 	{ "NOTIFYWINDOWS",	function_notifywindows	},
656 	{ "NOTW",               function_notw 		},
657 	{ "NUMARRAYS",          function_numarrays 	},
658 	{ "NUMITEMS",           function_numitems 	},
659 	{ "NUMLINES",		function_numlines	},
660 	{ "NUMONCHANNEL",	function_numonchannel 	},
661 	{ "NUMSORT",		function_numsort	},
662 	{ "NUMWORDS",		function_numwords	},
663 	{ "ONCHANNEL",          function_onchannel 	},
664 	{ "OPEN",		function_open 		},
665 	{ "OUTPUTINFO",		function_outputinfo	},
666 	{ "PAD",		function_pad		},
667 	{ "PASS",		function_pass		},
668 	{ "PATTERN",		function_pattern	},
669 #ifdef HAVE_PERL
670 	{ "PERL",		function_perl		},
671 	{ "PERLCALL",		function_perlcall	},
672 	{ "PERLXCALL",		function_perlxcall	},
673 #endif
674 #ifdef HAVE_PYTHON
675 	{ "PYDIRECT",		function_pydirect	},
676 	{ "PYTHON",		function_python		},
677 #endif
678 	{ "PID",		function_pid 		},
679 	{ "POP",		function_pop 		},
680 	{ "PPID",		function_ppid 		},
681 	{ "PREFIX",		function_prefix		},
682 	{ "PRINTLEN",		function_printlen	},
683 	{ "PUSH",		function_push 		},
684 	{ "QUERYWIN",		function_querywin	},
685 	{ "QWORD",		function_qword		},
686 	{ "RAND",		function_rand 		},
687 	{ "RANDREAD",		function_randread	},
688 	{ "READ",		function_read 		},
689 	{ "REALPATH",		function_realpath	},
690 	{ "REGCOMP",		function_regcomp	},
691 	{ "REGCOMP_CS",		function_regcomp_cs	},
692 	{ "REGERROR",		function_regerror	},
693 	{ "REGEXEC",		function_regexec	},
694 	{ "REGFREE",		function_regfree	},
695 	{ "REGMATCHES",		function_regmatches	},
696 	{ "REMW",               function_remw 		},
697 	{ "REMWS",		function_remws		},
698 	{ "RENAME",		function_rename 	},
699 	{ "REPEAT",		function_repeat		},
700 	{ "REST",		function_rest		},
701 	{ "RESTW",              function_restw 		},
702 	{ "REVERSE",            function_reverse 	},
703 	{ "REVW",               function_revw 		},
704 	{ "RFILTER",            function_rfilter 	},
705 	{ "RIGHT",		function_right 		},
706 	{ "RIGHTW",             function_rightw 	},
707 	{ "RINDEX",		function_rindex 	},
708 	{ "RMATCH",		function_rmatch 	},
709 	{ "RMATCHITEM",         function_rmatchitem 	},
710 	{ "RMDIR",		function_rmdir 		},
711 	{ "RPATTERN",           function_rpattern 	},
712 	{ "RSUBSTR",		function_rsubstr	},
713 #ifdef HAVE_RUBY
714 	{ "RUBY",		function_ruby		},
715 #endif
716 	{ "SAR",		function_sar 		},
717 	{ "SERVERCTL",		function_serverctl	},
718 	{ "SERVERWIN",		function_serverwin	},
719 	{ "SERVPORTS",		function_servports	},
720 	{ "SETITEM",            function_setitem 	},
721 	{ "SHIFT",		function_shift 		},
722 	{ "SHIFTBRACE",		function_shiftbrace	},
723 #if 0
724 	{ "SHIFTSEG",           function_shiftseg       },
725 #endif
726 	{ "SIN",		function_sin		},
727 	{ "SINH",		function_sinh		},
728 	{ "SORT",		function_sort		},
729 	{ "SPLICE",		function_splice 	},
730 	{ "SPLIT",		function_split 		},
731 	{ "SPLITW",		function_splitw 	},
732 	{ "SRAND",		function_srand 		},
733 	{ "SSL",		function_ssl		},
734 	{ "STARTUPFILE",	function_startupfile	},
735 	{ "STAT",		function_stat		},
736 	{ "STATUS",		function_status		},
737 	{ "STATUS_ONEOFF",	function_status_oneoff	},
738 	{ "STIME",		function_stime 		},
739 	{ "STRFTIME",		function_strftime	},
740 	{ "STRIP",		function_strip 		},
741 	{ "STRIPANSI",		function_stripansi	},
742 	{ "STRIPANSICODES",	function_stripansicodes },
743 	{ "STRIPC",		function_stripc		},
744 	{ "STRIPCRAP",		function_stripcrap	},
745 	{ "STRLEN",		function_strlen		},
746 	{ "STRPTIME",		function_strptime	},
747 	{ "STRTOL",		function_strtol		},
748 	{ "SUBSTR",		function_substr		},
749 	{ "SYMBOLCTL",		function_symbolctl	},
750 	{ "TAN",		function_tan		},
751 	{ "TANH",		function_tanh		},
752 #ifdef HAVE_TCL
753 	{ "TCL",		function_tcl		},
754 #endif
755 	{ "TDIFF",		function_tdiff 		},
756 	{ "TDIFF2",		function_tdiff2 	},
757 	{ "TIME",		function_time 		},
758 	{ "TIMERCTL",		function_timerctl	},
759 	{ "TOBASE",		function_tobase 	},
760         { "TOLOWER",		function_tolower 	},
761 	{ "TOUPPER",		function_toupper 	},
762 	{ "TOW",                function_tow 		},
763 	{ "TR",			function_translate 	},
764 	{ "TRUNC",		function_truncate 	},
765 	{ "TTYNAME",		function_ttyname	},
766 	{ "TWIDDLE",		function_twiddle	},
767 	{ "UHC",		function_uhc		},
768 	{ "UMASK",		function_umask		},
769 	{ "UNAME",		function_uname		},
770 	{ "UNICODE",		function_unicode 	},
771 	{ "UNIQ",		function_uniq		},
772 	{ "UNLINK",		function_unlink 	},
773 	{ "UNSHIFT",		function_unshift 	},
774 	{ "UNSPLIT",		function_unsplit	},
775 	{ "USERHOST",		function_userhost 	},
776 	{ "USERMODE",		function_umode		},
777 	{ "USETITEM",           function_usetitem 	},
778 	{ "UTIME",		function_utime	 	},
779 	{ "UUID4",		function_uuid4		},
780 	{ "VERSION",		function_server_version },
781 	{ "WHICH",		function_which 		},
782 	{ "WINCHAN",		function_winchan	},
783 	{ "WINDOWCTL",		function_windowctl	},
784 	{ "WORD",		function_word 		},
785 	{ "WORDTOINDEX",	function_wordtoindex	},
786 	{ "WRITE",		function_write 		},
787 	{ "WRITEB",		function_writeb		},
788 	{ "XDEBUG",		function_xdebug		},
789 	{ "XFORM",		function_xform		},
790 	{ "YN",			function_yn		},
791 	{ (char *) 0,		NULL }
792 };
793 
init_expandos(void)794 void	init_expandos (void)
795 {
796 	int	i;
797 
798 	for (i = 0; built_in[i].name; i++)
799 		add_builtin_expando(built_in[i].name, built_in[i].func);
800 }
801 
init_functions(void)802 void	init_functions (void)
803 {
804 	int	i;
805 
806 	for (i = 0; built_in_functions[i].name; i++)
807 		add_builtin_func_alias(built_in_functions[i].name, built_in_functions[i].func);
808 }
809 
810 
811 /*
812  * call_function has changed a little bit.  Now we take the entire call
813  * including args in the paren list.  This is a bit more convenient for
814  * the callers, since there are a bunch of them and all of them seperately
815  * handling extracting the args is just a pain in the butt.
816  */
call_function(char * name,const char * args)817 char	*call_function (char *name, const char *args)
818 {
819 	char	*tmp;
820 	char	*result = (char *) 0;
821 	char	*debug_copy = (char *) 0;
822 	char	*lparen;
823 	int	debugging;
824 	size_t	size;
825 	char *	buf;
826 	const char *	alias;
827 	char *	(*func) (char *) = NULL;
828 	void *	arglist = NULL;
829 	size_t	type;
830 	char *	str = NULL;
831 
832 	debugging = get_int_var(DEBUG_VAR);
833 
834 	if ((lparen = strchr(name, '(')))
835 	{
836 		ssize_t	span;
837 
838 		if ((span = MatchingBracket(lparen + 1, '(', ')')) >= 0)
839 			lparen[1 + span] = 0;
840 		else
841 			yell("Unmatched lparen in function call [%s]", name);
842 
843 		*lparen++ = 0;
844 	}
845 	else
846 		lparen = endstr(name);
847 
848 	upper(name);
849 	type = strspn(name, ":");
850 	name += type;
851 
852 	str = remove_brackets(name, args);
853 	alias = get_func_alias(str, &arglist, &func);
854 
855 	if ((type == 0 && (!func && !alias)) ||
856 	    (type == 1 && !alias) ||
857 	    (type == 2 && !func))
858 	{
859 	    if (x_debug & DEBUG_UNKNOWN)
860 		yell("Function call to non-existant alias [%s]", str);
861 	    if (debugging & DEBUG_FUNCTIONS)
862 		privileged_yell("Function %s(%s) returned ", str, lparen);
863 	    new_free(&str);
864             return malloc_strdup(empty_string);
865         }
866 
867 	tmp = expand_alias(lparen, args);
868 	debug_copy = LOCAL_COPY(tmp);
869 
870 	if (func && type != 1)
871 		result = func(tmp);
872 	else if (alias && type != 2)
873 		result = call_user_function(str, alias, tmp, arglist);
874 
875 	size = strlen(str) + strlen(debug_copy) + 15;
876 	buf = (char *)alloca(size);
877 	snprintf(buf, size, "$%s(%s)", str, debug_copy);
878 	MUST_BE_MALLOCED(result, buf);
879 
880 	if (debugging & DEBUG_FUNCTIONS)
881 		privileged_yell("Function %s(%s) returned %s",
882 					str, debug_copy, result);
883 
884 	new_free(&str);
885 	new_free(&tmp);
886 	return result;
887 }
888 
func_exist(char * command)889 static int	func_exist (char *command)
890 {
891 	char *	name;
892 	char *	(*func) (char *);
893 
894 	if (!command || !*command)
895 		return 0;
896 
897 	name = LOCAL_COPY(command);
898 	upper(name);
899 
900 	get_func_alias(name, NULL, &func);
901 	if (func == NULL)
902 		return 0;
903 	return 1;
904 }
905 
906 
907 /* built in expando functions */
alias_line(void)908 static	char	*alias_line 		(void) { return malloc_strdup(get_input()); }
alias_buffer(void)909 static	char	*alias_buffer 		(void) { return malloc_strdup(cut_buffer); }
alias_time(void)910 static	char	*alias_time 		(void) { return malloc_strdup(get_clock()); }
alias_dollar(void)911 static	char	*alias_dollar 		(void) { return malloc_strdup("$"); }
alias_detected(void)912 static	char	*alias_detected 	(void) { return malloc_strdup(last_notify_nick); }
alias_nick(void)913 static	char	*alias_nick 		(void) { return malloc_strdup((current_window->server != NOSERV) ? get_server_nickname(current_window->server) : empty_string); }
alias_away(void)914 static	char	*alias_away 		(void) { return malloc_strdup(get_server_away(from_server)); }
alias_sent_nick(void)915 static  char    *alias_sent_nick        (void) { return malloc_strdup((get_server_sent_nick(from_server)) ? get_server_sent_nick(from_server) : empty_string); }
alias_recv_nick(void)916 static  char    *alias_recv_nick        (void) { return malloc_strdup((get_server_recv_nick(from_server)) ? get_server_recv_nick(from_server) : empty_string); }
alias_msg_body(void)917 static  char    *alias_msg_body         (void) { return malloc_strdup((get_server_sent_body(from_server)) ? get_server_sent_body(from_server) : empty_string); }
alias_joined_nick(void)918 static  char    *alias_joined_nick      (void) { return malloc_strdup((get_server_joined_nick(from_server)) ? get_server_joined_nick(from_server) : empty_string); }
alias_public_nick(void)919 static  char    *alias_public_nick      (void) { return malloc_strdup((get_server_public_nick(from_server)) ? get_server_public_nick(from_server) : empty_string); }
alias_show_realname(void)920 static  char    *alias_show_realname 	(void) {
921 	return malloc_strdup(
922 		(current_window->server != NOSERV) ?
923 		get_server_realname(current_window->server) :
924 		empty_string); }
alias_version_str(void)925 static	char	*alias_version_str 	(void) { return malloc_strdup(irc_version); }
alias_invite(void)926 static  char    *alias_invite           (void) { return malloc_strdup((get_server_invite_channel(from_server)) ? get_server_invite_channel(from_server) : empty_string); }
alias_oper(void)927 static	char	*alias_oper 		(void) { return malloc_strdup((from_server != -1) ? get_server_operator(from_server) ?  get_string_var(STATUS_OPER_VAR) : empty_string : empty_string); }
alias_version(void)928 static	char	*alias_version 		(void) { return malloc_strdup(internal_version); }
alias_show_userhost(void)929 static  char    *alias_show_userhost 	(void) { return malloc_strdup(get_server_userhost(from_server)); }
alias_online(void)930 static  char    *alias_online 		(void) { return malloc_sprintf(NULL, INTMAX_FORMAT, (intmax_t)start_time.tv_sec); }
alias_idle(void)931 static  char    *alias_idle 		(void) { return malloc_sprintf(NULL, INTMAX_FORMAT, (intmax_t)time(NULL) - idle_time.tv_sec); }
alias_current_numeric(void)932 static	char	*alias_current_numeric	(void) { return malloc_sprintf(NULL, "%03d", current_numeric); }
alias_banner(void)933 static	char	*alias_banner		(void) { return malloc_strdup(banner()); }
934 
alias_currdir(void)935 static	char	*alias_currdir  	(void)
936 {
937 	char 	*tmp = (char *)new_malloc(PATH_MAX+1);
938 	if (!getcwd(tmp, PATH_MAX))
939 		*tmp = 0;
940 	return tmp;
941 }
942 
alias_channel(void)943 static	char	*alias_channel 		(void)
944 {
945 	const char	*tmp;
946 	return malloc_strdup((tmp = get_echannel_by_refnum(0)) ? tmp : zero);
947 }
948 
alias_server(void)949 static	char	*alias_server 		(void)
950 {
951 	return malloc_strdup((parsing_server_index != NOSERV) ?
952 		         get_server_itsname(parsing_server_index) :
953 		         (get_window_server(0) != NOSERV) ?
954 			        get_server_itsname(get_window_server(0)) :
955 				empty_string);
956 }
957 
alias_query_nick(void)958 static	char	*alias_query_nick 	(void)
959 {
960 	const char	*tmp;
961 	return malloc_strdup((tmp = query_nick()) ? tmp : empty_string);
962 }
963 
alias_target(void)964 static	char	*alias_target 		(void)
965 {
966 	const char	*tmp;
967 	return malloc_strdup((tmp = get_target_by_refnum(0)) ? tmp : empty_string);
968 }
969 
alias_cmdchar(void)970 static	char	*alias_cmdchar 		(void)
971 {
972 	const char *cmdchars;
973 	char tmp[2];
974 
975 	if ((cmdchars = get_string_var(CMDCHARS_VAR)) == (char *) 0)
976 		cmdchars = DEFAULT_CMDCHARS;
977 	tmp[0] = cmdchars[0];
978 	tmp[1] = 0;
979 	return malloc_strdup(tmp);
980 }
981 
alias_chanop(void)982 static	char	*alias_chanop 		(void)
983 {
984 	const char	*tmp;
985 	return malloc_strdup(((tmp = get_echannel_by_refnum(0)) && get_channel_oper(tmp, get_window_server(0))) ?
986 		"@" : empty_string);
987 }
988 
alias_modes(void)989 static	char	*alias_modes 		(void)
990 {
991 	const char	*tmp;
992 	return malloc_strdup((tmp = get_echannel_by_refnum(0)) ?
993 		get_channel_mode(tmp, get_window_server(0)) : empty_string);
994 }
995 
alias_server_version(void)996 static	char	*alias_server_version  (void)
997 {
998 	int s = from_server;
999 
1000 	if (s == NOSERV)
1001 	{
1002 		if (primary_server != NOSERV)
1003 			s = primary_server;
1004 		else
1005 			return malloc_strdup(empty_string);
1006 	}
1007 
1008 	return malloc_strdup(get_server_version_string(s));
1009 }
1010 
1011 
1012 /*	*	*	*	*	*	*	*	*	*
1013 		These are the built-in functions.
1014 
1015 	About 80 of them are here, the rest are in array.c.  All of the
1016 	stock client's functions are supported, as well as about 60 more.
1017 	Most of the 30 stock client's functions have been re-written for
1018 	optimization reasons, and also to further distance ircii's code
1019 	from EPIC.
1020  *	*	*	*	*	*	*	*	*	*/
1021 
1022 /*
1023  * Usage: $left(number text)
1024  * Returns: the <number> leftmost code points in <text>.
1025  * Example: $left(5 the quick brown frog) returns "the q"
1026  *
1027  * Note: the difference between $[10]foo and $left(10 foo) is that the former
1028  * is padded and the latter is not.
1029  *
1030  * Note: This function counts code points, not columns!  So non-printable
1031  * codepoints (like ^B/^V/^C) still count!
1032  */
BUILT_IN_FUNCTION(function_left,word)1033 BUILT_IN_FUNCTION(function_left, word)
1034 {
1035 	int		keepers,	/* The number of CPs to retain */
1036 			count,		/* How many we've copied so far */
1037 			code_point;	/* The current CP we're working on */
1038 	unsigned char 	*s;		/* Pointer at next CP */
1039 
1040 	GET_INT_ARG(keepers, word);
1041 	RETURN_IF_EMPTY(word);
1042 
1043 	if (keepers <= 0)
1044 		RETURN_EMPTY;
1045 
1046 	/* Return the whole string if it's "short" */
1047 	if (keepers >= quick_code_point_count(word))
1048 		RETURN_STR(word);
1049 
1050 	count = 0;
1051 	s = word;
1052 	while ((code_point = next_code_point(CUC_PP &s, 0)))
1053 	{
1054 		/* Invalid CPs count as 1, + we skip them. */
1055 		if (code_point == -1)
1056 			s++;
1057 
1058 		if (++count >= keepers)
1059 			break;
1060 	}
1061 
1062 	/* Chop the string off here */
1063 	*s = 0;
1064 	RETURN_STR(word);
1065 }
1066 
1067 /*
1068  * Usage: $right(number text)
1069  * Returns: the <number> rightmost characters in <text>.
1070  * Example: $right(5 the quick brown frog) returns " frog"
1071  * XXX This function should use previous_code_point() like $rest() does.
1072  */
BUILT_IN_FUNCTION(function_right,word)1073 BUILT_IN_FUNCTION(function_right, word)
1074 {
1075 	int	keepers,	/* The number of CPs to retain */
1076 		count,		/* How many we've copied so far */
1077 		code_point,	/* The current CP we're working on */
1078 		total,		/* How many CPs are in word */
1079 		ignores;	/* Leading CPs to ignore */
1080 	char 	*s;		/* Pointer at next CP */
1081 
1082 	GET_INT_ARG(keepers, word);
1083 	RETURN_IF_EMPTY(word);
1084 
1085 	if (keepers <= 0)
1086 		RETURN_EMPTY;
1087 
1088 	/* Return the whole string if it's "short" */
1089 	if (keepers >= ((total = quick_code_point_count(word))))
1090 		RETURN_STR(word);
1091 
1092 	/* Skip the first 'ignores' CPs */
1093 	ignores = total - keepers;
1094 	s = word;
1095 	while ((code_point = next_code_point(CUC_PP &s, 0)))
1096 	{
1097 		/* Invalid CPs count as 1, + we skip them. */
1098 		if (code_point == -1)
1099 			s++;
1100 
1101 		if (--ignores <= 0)
1102 			break;
1103 	}
1104 
1105 	RETURN_STR(s);
1106 }
1107 
1108 /*
1109  * Usage: $mid(start number text)
1110  * Returns: the <start>th through <start>+<number>th characters in <text>.
1111  * Example: $mid(3 4 the quick brown frog) returns " qui"
1112  *
1113  * Note: the first character is numbered zero.
1114  * XXX It's a shame this isn't generalized and shared with other funcs.
1115  */
BUILT_IN_FUNCTION(function_mid,word)1116 BUILT_IN_FUNCTION(function_mid, word)
1117 {
1118 	int	keepers,	/* The number of CPs to retain */
1119 		count,		/* How many we've copied so far */
1120 		code_point,	/* The current CP we're working on */
1121 		total;		/* How many CPs are in word */
1122 	int	start;
1123 	char 	*s;		/* Pointer at next CP */
1124 	char	*retval;
1125 
1126 	GET_INT_ARG(start, word);
1127 	GET_INT_ARG(keepers, word);
1128 	RETURN_IF_EMPTY(word);
1129 
1130 	if (keepers <= 0)
1131 		RETURN_EMPTY;
1132 
1133 	if (start < 0)
1134 		RETURN_EMPTY;
1135 	if (start > quick_code_point_count(word))
1136 		RETURN_EMPTY;
1137 
1138 	/* Skip the initial CPs */
1139 	for (s = word, count = 0; count < start; count++)
1140 	{
1141 		/* Invalid CPs count as 1, and we skip them. */
1142 		code_point = next_code_point(CUC_PP &s, 0);
1143 		if (code_point == -1)
1144 			s++;
1145 	}
1146 
1147 	/* This is our anchor */
1148 	retval = s;
1149 
1150 	/* Return the whole string if it's "short" */
1151 	if (keepers >= quick_code_point_count(retval))
1152 		RETURN_STR(retval);
1153 
1154 	/* Otherwise count off 'keepers' CPs */
1155 	count = 0;
1156 	s = retval;
1157 	while ((code_point = next_code_point(CUC_PP &s, 0)))
1158 	{
1159 		/* Invalid CPs count as 1, + we skip them. */
1160 		if (code_point == -1)
1161 			s++;
1162 
1163 		if (++count >= keepers)
1164 			break;
1165 	}
1166 
1167 	/* Chop the string off here */
1168 	*s = 0;
1169 	RETURN_STR(retval);
1170 }
1171 
1172 
1173 /*
1174  * Usage: $rand(max)
1175  * Returns: A random number from zero to max-1.
1176  * Example: $rand(10) might return any number from 0 to 9.
1177  */
BUILT_IN_FUNCTION(function_rand,word)1178 BUILT_IN_FUNCTION(function_rand, word)
1179 {
1180 	unsigned long	tempin, ret;
1181 static	unsigned long	rn = 0;
1182 
1183 	GET_INT_ARG(tempin, word);
1184 	if (tempin == 0)
1185 		ret = random_number(0);
1186 	else {
1187 		if (rn < tempin)
1188 			rn ^= random_number(0);
1189 		ret = rn % tempin;
1190 		rn /= tempin;
1191 	}
1192 	RETURN_INT(ret);
1193 }
1194 
1195 /*
1196  * Usage: $srand(seed)
1197  * Returns: Nothing.
1198  * Side effect: seeds the random number generater.
1199  * Note: the argument is ignored.
1200  */
BUILT_IN_FUNCTION(function_srand,word)1201 BUILT_IN_FUNCTION(function_srand, word)
1202 {
1203 	random_number(time(NULL));
1204 	RETURN_EMPTY;
1205 }
1206 
1207 /*
1208  * Usage: $time()
1209  * Returns: The number of seconds that has elapsed since Jan 1, 1970, GMT.
1210  * Example: $time() returned something around 802835348 at the time I
1211  * 	    wrote this comment.
1212  */
BUILT_IN_FUNCTION(function_time,input)1213 BUILT_IN_FUNCTION(function_time, input)
1214 {
1215 	RETURN_INT(time(NULL));
1216 }
1217 
1218 /*
1219  * Usage: $stime(time)
1220  * Returns: The human-readable form of the date based on the <time> argument.
1221  * Example: $stime(1000) returns what time it was 1000 seconds from the epoch.
1222  *
1223  * Note: $stime() is really useful when you give it the argument $time(), ala
1224  *       $stime($time()) is the human readable form for now.
1225  */
BUILT_IN_FUNCTION(function_stime,input)1226 BUILT_IN_FUNCTION(function_stime, input)
1227 {
1228 	time_t	ltime;
1229 	const char	*ret;
1230 
1231 	GET_INT_ARG(ltime, input);
1232 	ret = my_ctime(ltime);
1233 	RETURN_STR(ret);		/* Dont put function call in macro! */
1234 }
1235 
1236 /*
1237  * Usage: $tdiff(seconds)
1238  * Returns: The time that has elapsed represented in days/hours/minutes/seconds
1239  *          corresponding to the number of seconds passed as the argument.
1240  * Example: $tdiff(3663) returns "1 hour 1 minute 3 seconds"
1241  */
BUILT_IN_FUNCTION(function_tdiff,input)1242 BUILT_IN_FUNCTION(function_tdiff, input)
1243 {
1244 	time_t	ltime;
1245 	time_t	days,
1246 		hours,
1247 		minutes,
1248 		seconds;
1249 	size_t	size;
1250 	char	*tmp;
1251 	char	*after;
1252 
1253 	size = strlen(input) + 64;
1254 	tmp = alloca(size);
1255 	*tmp = 0;
1256 
1257 	/* XXX Why doesn't this use GET_INT_ARG? */
1258 	ltime = (time_t)strtol(input, &after, 10);
1259 	if (after == input)
1260 		RETURN_EMPTY;
1261 
1262 	seconds = ltime % 60;
1263 	ltime /= 60;
1264 	minutes = ltime % 60;
1265 	ltime /= 60;
1266 	hours = ltime % 24;
1267 	days = (ltime - hours) / 24;
1268 
1269 	if (days)
1270 	{
1271 		if (days == 1)
1272 			strlcat(tmp, "1 day ", size);
1273 		else
1274 			strlpcat(tmp, size, "%ld days ", (long)days);
1275 	}
1276 	if (hours)
1277 	{
1278 		if (hours == 1)
1279 			strlcat(tmp, "1 hour ", size);
1280 		else
1281 			strlpcat(tmp, size, "%ld hours ", (long)hours);
1282 	}
1283 	if (minutes)
1284 	{
1285 		if (minutes == 1)
1286 			strlcat(tmp, "1 minute ", size);
1287 		else
1288 			strlpcat(tmp, size, "%ld minutes ", (long)minutes);
1289 	}
1290 
1291 	if (seconds || (!days && !hours && !minutes) ||
1292 			(*after == '.' && is_number(after + 1)))
1293 	{
1294 		unsigned long number = 0;
1295 
1296 		/*
1297 		 * If we have a decmial point, and is_number() returns 1,
1298 		 * then we know that we have a real, authentic number AFTER
1299 		 * the decmial point.  As long as it isnt zero, we want it.
1300 		 */
1301 		strlcat(tmp, NUMSTR(seconds), size);
1302 		if (*after == '.')
1303 		{
1304 			if ((number = atol(after + 1)))
1305 				strlcat(tmp, after, size);
1306 		}
1307 
1308 		if (seconds == 1 && number == 0)
1309 			strlcat(tmp, " second", size);
1310 		else
1311 			strlcat(tmp, " seconds", size);
1312 	}
1313 	else
1314 		chop(tmp, 1);	/* Chop off that space! */
1315 
1316 	RETURN_STR(tmp);
1317 }
1318 
1319 /*
1320  * Usage: $index(characters text)
1321  * Returns: The number of leading characters in <text> that do not occur
1322  *          anywhere in the <characters> argument.
1323  * Example: $index(f three fine frogs) returns 6 (the 'f' in 'fine')
1324  *          $index(frg three fine frogs) returns 2 (the 'r' in 'three')
1325  */
BUILT_IN_FUNCTION(function_index,input)1326 BUILT_IN_FUNCTION(function_index, input)
1327 {
1328 	char *schars;
1329 	size_t	cpoffset;
1330 
1331 	GET_DWORD_ARG(schars, input);
1332 	cpoffset = -1;
1333 	cpindex((const unsigned char *)input, (const unsigned char *)schars, 1, &cpoffset);
1334 	RETURN_INT(cpoffset);
1335 }
1336 
1337 /*
1338  * Usage: $rindex(characters text)
1339  * Returns: The number of leading characters in <text> that occur before the
1340  *          *last* occurance of any of the characters in the <characters>
1341  *          argument.
1342  * Example: $rindex(f three fine frogs) returns 12 (the 'f' in 'frogs')
1343  *          $rindex(frg three fine frogs) returns 15 (the 'g' in 'frogs')
1344  */
BUILT_IN_FUNCTION(function_rindex,word)1345 BUILT_IN_FUNCTION(function_rindex, word)
1346 {
1347 	char	*chars;
1348 	size_t	cpoffset;
1349 
1350 	/* need to find out why ^x doesnt work */
1351 	GET_DWORD_ARG(chars, word);
1352 	cpoffset = -1;
1353 	if (!*word || !*chars)
1354 		RETURN_INT(-1);
1355 
1356 	rcpindex((const unsigned char *)word + strlen(word),
1357 		 (const unsigned char *)word,
1358 		 (const unsigned char *)chars, 1, &cpoffset);
1359 	RETURN_INT(cpoffset);
1360 }
1361 
1362 /*
1363  * Usage: $match(pattern list of words)
1364  * Returns: if no words in the list match the pattern, it returns 0.
1365  *	    Otherwise, it returns the number of the word that most
1366  *	    exactly matches the pattern (first word is numbered one)
1367  * Example: $match(f*bar foofum barfoo foobar) returns 3
1368  *	    $match(g*ant foofum barfoo foobar) returns 0
1369  *
1370  * Note: it is possible to embed spaces inside of a word or pattern simply
1371  *       by including the entire word or pattern in quotation marks. (")
1372  */
BUILT_IN_FUNCTION(function_match,input)1373 BUILT_IN_FUNCTION(function_match, input)
1374 {
1375 	char	*pattern, 	*word;
1376 	long	current_match,	best_match = 0,	match = 0, match_index = 0;
1377 
1378 	GET_FUNC_ARG(pattern, input);
1379 
1380 	while ((word = next_func_arg(input, &input)))
1381 	{
1382 		match_index++;
1383 		if ((current_match = wild_match(pattern, word)) > best_match)
1384 		{
1385 			match = match_index;
1386 			best_match = current_match;
1387 		}
1388 	}
1389 
1390 	RETURN_INT(match);
1391 }
1392 
1393 /*
1394  * Usage: $rmatch(word list of patterns)
1395  * Returns: if no pattern in the list matches the word, it returns 0.
1396  *	    Otherwise, it returns the number of the pattern that most
1397  *	    exactly matches the word (first word is numbered one)
1398  * Example: $rmatch(foobar f*bar foo*ar g*ant) returns 2
1399  *	    $rmatch(booya f*bar foo*ar g*ant) returns 0
1400  *
1401  * Note: It is possible to embed spaces into a word or pattern simply by
1402  *       including the entire word or pattern within quotation marks (")
1403  */
BUILT_IN_FUNCTION(function_rmatch,input)1404 BUILT_IN_FUNCTION(function_rmatch, input)
1405 {
1406 	char	*pattern,	*word;
1407 	int	current_match,	best_match = 0,	match = 0, rmatch_index = 0;
1408 
1409 	GET_FUNC_ARG(word, input);
1410 
1411 	while ((pattern = next_func_arg(input, &input)))
1412 	{
1413 		rmatch_index++;
1414 		if ((current_match = wild_match(pattern, word)) > best_match)
1415 		{
1416 			match = rmatch_index;
1417 			best_match = current_match;
1418 		}
1419 	}
1420 
1421 	RETURN_INT(match);
1422 }
1423 
1424 /*
1425  * Usage: $userhost()
1426  * Returns: the userhost (if any) of the most previously recieved message.
1427  * Caveat: $userhost() changes with every single line that appears on
1428  *         your screen, so if you want to save it, you will need to assign
1429  *         it to a variable.
1430  */
BUILT_IN_FUNCTION(function_userhost,input)1431 BUILT_IN_FUNCTION(function_userhost, input)
1432 {
1433 	if (input && *input)
1434 	{
1435 		char *retval = NULL;
1436 		size_t rvclue=0;
1437 		char *nick;
1438 		const char *uh;
1439 		const char *chan = NULL;
1440 
1441 		while (input && *input)
1442 		{
1443 			GET_FUNC_ARG(nick, input);
1444 			if (is_channel(nick))
1445 				chan = nick;
1446 			if ((uh = fetch_userhost(from_server, chan, nick)))
1447 				malloc_strcat_word_c(&retval, space, uh, DWORD_NO, &rvclue);
1448 			else
1449 				malloc_strcat_word_c(&retval, space, unknown_userhost, DWORD_NO, &rvclue);
1450 		}
1451 		RETURN_MSTR(retval);
1452 	}
1453 
1454 	RETURN_STR(FromUserHost);
1455 }
1456 
1457 /*
1458  * Usage: $strip(characters text)
1459  * Returns: <text> with all instances of any characters in the <characters>
1460  *	    argument removed.
1461  * Example: $strip(f free fine frogs) returns "ree ine rogs"
1462  *
1463  * To remove spaces, use $strip(" " text)
1464  */
BUILT_IN_FUNCTION(function_strip,input)1465 BUILT_IN_FUNCTION(function_strip, input)
1466 {
1467 	char *	search;
1468 	const unsigned char 	*s, *p;
1469 	int	c, d;
1470 	int	found;
1471 	char *	result, *r;
1472 
1473 	GET_DWORD_ARG(search, input);
1474 	RETURN_IF_EMPTY(input);
1475 
1476 	r = result = (char *)new_malloc(strlen(input) + 1);
1477 
1478 	p = input;
1479 	while ((c = next_code_point(&p, 1)))
1480 	{
1481 		found = 0;
1482 		s = search;
1483 		while ((d = next_code_point(&s, 1)))
1484 		{
1485 			if (c == d)
1486 			{
1487 				found = 1;
1488 				break;
1489 			}
1490 		}
1491 		if (!found)
1492 		{
1493 			unsigned char utf8str[16];
1494 			unsigned char *x;
1495 
1496 			ucs_to_utf8(c, utf8str, sizeof(utf8str));
1497 			for (x = utf8str; *x; x++)
1498 				*r++ = *x;
1499 		}
1500 	}
1501 	*r = 0;
1502 	return result;		/* DONT USE RETURN_STR HERE! */
1503 }
1504 
1505 /*
1506  * Usage: $ischannel(text)
1507  * Returns: If <text> could be a valid channel name, 1 is returned.
1508  *          If <text> is an invalid channel name, 0 is returned.
1509  *
1510  * Note: Contrary to popular belief, this function does NOT determine
1511  * whether a given channel name is in use!
1512  */
BUILT_IN_FUNCTION(function_ischannel,input)1513 BUILT_IN_FUNCTION(function_ischannel, input)
1514 {
1515 	char *	channel;
1516 	int	ret;
1517 
1518 	channel = next_func_arg(input, &input);
1519 	ret = is_channel(channel);
1520 	RETURN_INT(ret);
1521 }
1522 
1523 /*
1524  * Usage: $ischanop(nick channel)
1525  * Returns: 1 if <nick> is a channel operator on <channel>
1526  *          0 if <nick> is not a channel operator on <channel>
1527  *			* O R *
1528  *	      if you are not on <channel>
1529  *
1530  * Note: Contrary to popular belief, this function can only tell you
1531  *       who the channel operators are for channels you are already on!
1532  *
1533  * Boo Hiss:  This should be $ischanop(channel nick <nick...nick>)
1534  *	      and return a list (1 1 ... 0), which would allow us to
1535  *	      call is_chanop() without ripping off the nick, and allow
1536  *	      us to abstract is_chanop() to take a list. oh well...
1537  *	      Too late to change it now. :/
1538  */
BUILT_IN_FUNCTION(function_ischanop,input)1539 BUILT_IN_FUNCTION(function_ischanop, input)
1540 {
1541 	char 	*nick, *chan;
1542 	int	ret;
1543 
1544 	nick = next_func_arg(input, &input);
1545 	chan = next_func_arg(input, &input);
1546 	ret = is_chanop(chan, nick);
1547 	RETURN_INT(ret);
1548 }
1549 
1550 
1551 /*
1552  * Usage: $word(number text)
1553  * Returns: the <number>th word in <text>.  The first word is numbered zero.
1554  * Example: $word(3 one two three four five) returns "four" (think about it)
1555  */
BUILT_IN_FUNCTION(function_word,word)1556 BUILT_IN_FUNCTION(function_word, word)
1557 {
1558 	int	cvalue;
1559 	char	*w_word;
1560 
1561 	GET_INT_ARG(cvalue, word);
1562 	if (cvalue < 0)
1563 		RETURN_EMPTY;
1564 
1565 	while (cvalue-- > 0 && word && *word)
1566 		next_func_arg(word, &word);
1567 
1568 	GET_FUNC_ARG(w_word, word);
1569 	RETURN_STR(w_word);
1570 }
1571 
1572 /*
1573  * Usage: $qword(number text)
1574  * Returns: the <number>th word in <text>, but as a qword, suitable for
1575  *          passing back into a function call as a dword.
1576  *          The first word is numbered zero.
1577  * Example: $qword(3 one two three four five) returns "four" (think about it)
1578  * Remember, double quoted words require /xdebug dword to be turned on!
1579  */
BUILT_IN_FUNCTION(function_qword,word)1580 BUILT_IN_FUNCTION(function_qword, word)
1581 {
1582 	int	cvalue;
1583 	char *	w_word;
1584 	char *	retval = NULL;
1585 
1586 	GET_INT_ARG(cvalue, word);
1587 	if (cvalue < 0)
1588 		RETURN_EMPTY;
1589 
1590 	while (cvalue-- > 0 && word && *word)
1591 		next_func_arg(word, &word);
1592 
1593 	GET_FUNC_ARG(w_word, word);
1594 	malloc_strcat_word(&retval, space, w_word, DWORD_DWORDS);
1595 	RETURN_MSTR(retval);
1596 }
1597 
BUILT_IN_FUNCTION(function_connect,input)1598 BUILT_IN_FUNCTION(function_connect, input)
1599 {
1600 	char *	host;
1601 	char *	port;
1602 	char *	v;
1603 	int	family = AF_INET;
1604 
1605 	GET_FUNC_ARG(host, input);
1606 	GET_FUNC_ARG(port, input);
1607 	if (input && *input)
1608 	{
1609 		GET_FUNC_ARG(v, input)
1610 
1611 		/* Figure out what family the user wants */
1612 		if (*v == 'v' || *v == 'V')
1613 			v++;
1614 		if (*v == '4')
1615 			family = AF_INET;
1616 #ifdef INET6
1617 		else if (*v == '6')
1618 			family = AF_INET6;
1619 #endif
1620 		else if (*v == 'u' || *v == 'U')
1621 			family = AF_UNSPEC;
1622 	}
1623 
1624 	return dcc_raw_connect(host, port, family);	/* DONT USE RETURN_STR HERE! */
1625 }
1626 
BUILT_IN_FUNCTION(function_listen,input)1627 BUILT_IN_FUNCTION(function_listen, input)
1628 {
1629 	int	port = 0;
1630 	char *	v;
1631 	int	family = AF_INET;
1632 
1633 	/* Oops. found by CrowMan, listen() has a default. erf. */
1634 	if (input && *input)
1635 	{
1636 		char *tmp, *ptr;
1637 		if ((tmp = next_func_arg(input, &input)))
1638 		{
1639 			port = strtoul(tmp, &ptr, 0);
1640 			if (ptr == tmp)
1641 				RETURN_EMPTY;	/* error. */
1642 		}
1643 
1644 		if (input && *input)
1645 		{
1646 			GET_FUNC_ARG(v, input)
1647 
1648 			/* Figure out what family the user wants */
1649 			if (*v == 'v' || *v == 'V')
1650 				v++;
1651 			if (*v == '4')
1652 				family = AF_INET;
1653 #ifdef INET6
1654 			else if (*v == '6')
1655 				family = AF_INET6;
1656 #endif
1657 			else if (*v == 'u' || *v == 'U')
1658 				family = AF_UNSPEC;
1659 		}
1660 	}
1661 
1662 	return dcc_raw_listen(family, port);	/* DONT USE RETURN_STR HERE! */
1663 }
1664 
BUILT_IN_FUNCTION(function_toupper,input)1665 BUILT_IN_FUNCTION(function_toupper, input)
1666 {
1667 	return (upper(malloc_strdup(input)));
1668 }
1669 
BUILT_IN_FUNCTION(function_tolower,input)1670 BUILT_IN_FUNCTION(function_tolower, input)
1671 {
1672 	return (lower(malloc_strdup(input)));
1673 }
1674 
BUILT_IN_FUNCTION(function_curpos,input)1675 BUILT_IN_FUNCTION(function_curpos, input)
1676 {
1677 	RETURN_INT(cursor_position(current_window->screen));
1678 }
1679 
BUILT_IN_FUNCTION(function_channels,input)1680 BUILT_IN_FUNCTION(function_channels, input)
1681 {
1682 	int	server = from_server;
1683 	char *	retval;
1684 
1685 	if (isdigit(*input))
1686 		GET_INT_ARG(server, input)
1687 	else if (*input)
1688 	{
1689 		Window  *window;
1690 
1691 		server = -1;
1692 
1693 		/*
1694 		 * You may be wondering what I'm doing here.  It used to
1695 		 * be a historical idiom that you could do $mychannels(serv)
1696 		 * or $mychannels(#winref).  The "#" thing was handled else-
1697 		 * where, but I took it out becuase it had evil side effects.
1698 		 * But people need to be able to use "#" here, so specifically
1699 		 * support "#" here if needed.
1700 		 */
1701  		if ((window = get_window_by_desc(input)))
1702 			server = window->server;
1703 		else if (*input == '#')
1704 			if ((window = get_window_by_desc(input + 1)))
1705 				server = window->server;
1706 	}
1707 
1708 	retval = create_channel_list(server);
1709 	RETURN_MSTR(retval);
1710 }
1711 
BUILT_IN_FUNCTION(function_servers,input)1712 BUILT_IN_FUNCTION(function_servers, input)
1713 {
1714 	int count;
1715 	char *retval = NULL;
1716 	size_t rvclue=0;
1717 
1718 	if (!input || !*input)
1719 	{
1720 		retval = create_server_list();
1721 		RETURN_MSTR(retval);
1722 	}
1723 
1724 	for (count = 0; count < server_list_size(); count++)
1725 	{
1726 		if (is_server_registered(count))
1727 			malloc_strcat_word_c(&retval, space, ltoa(count), DWORD_NO, &rvclue);
1728 	}
1729 	if (!retval)
1730 		RETURN_EMPTY;
1731 
1732 	return retval;
1733 }
1734 
BUILT_IN_FUNCTION(function_pid,input)1735 BUILT_IN_FUNCTION(function_pid, input)
1736 {
1737 	RETURN_INT(getpid());
1738 }
1739 
BUILT_IN_FUNCTION(function_ppid,input)1740 BUILT_IN_FUNCTION(function_ppid, input)
1741 {
1742 	RETURN_INT(getppid());
1743 }
1744 
1745 
1746 /*
1747  * strftime() patch from hari (markc@arbld.unimelb.edu.au)
1748  */
BUILT_IN_FUNCTION(function_strftime,input)1749 BUILT_IN_FUNCTION(function_strftime, input)
1750 {
1751 	char		result[128];
1752 	time_t		ltime;
1753 	struct tm	*tm;
1754 
1755 	if (isdigit(*input))
1756 		ltime = strtoul(input, &input, 0);
1757 	else
1758 		ltime = time(NULL);
1759 
1760 	while (*input && my_isspace(*input))
1761 		++input;
1762 
1763 	if (!*input)
1764 		return malloc_strdup(empty_string);
1765 
1766 
1767 	tm = localtime(&ltime);
1768 
1769 	if (!strftime(result, 128, input, tm))
1770 		return malloc_strdup(empty_string);
1771 
1772 	return malloc_strdup(result);
1773 }
1774 
BUILT_IN_FUNCTION(function_idle,input)1775 BUILT_IN_FUNCTION(function_idle, input)
1776 {
1777 	return alias_idle();
1778 }
1779 
1780 
1781 
1782 /* The new "added" functions */
1783 
1784 /* $before(chars string of text)
1785  * returns the part of "string of text" that occurs before the
1786  * first instance of any character in "chars"
1787  * EX:  $before(! nick!user@host.com) returns "nick"
1788  */
BUILT_IN_FUNCTION(function_before,word)1789 BUILT_IN_FUNCTION(function_before, word)
1790 {
1791 	char	*pointer = (char *) 0;
1792 	char	*chars;
1793 	char	*tmp;
1794 	long	numint;
1795 
1796 	GET_DWORD_ARG(tmp, word);			/* DONT DELETE TMP! */
1797 	numint = my_atol(tmp);
1798 
1799 	if (numint)
1800 	{
1801 		GET_DWORD_ARG(chars, word);
1802 	}
1803 	else
1804 	{
1805 		numint = 1;
1806 		chars = tmp;
1807 	}
1808 
1809 	if (numint < 0 && strlen(word))
1810 		pointer = word + strlen(word) - 1;
1811 
1812 	pointer = search_for(word, &pointer, chars, numint);
1813 
1814 	if (!pointer)
1815 		RETURN_EMPTY;
1816 
1817 	*pointer = '\0';
1818 	RETURN_STR(word);
1819 }
1820 
1821 /* $after(chars string of text)
1822  * returns the part of "string of text" that occurs after the
1823  * first instance of any character in "chars"
1824  * EX: $after(! nick!user@host.com)  returns "user@host.com"
1825  */
BUILT_IN_FUNCTION(function_after,word)1826 BUILT_IN_FUNCTION(function_after, word)
1827 {
1828 	char	*chars;
1829 	char	*pointer = (char *) 0;
1830 	char 	*tmp;
1831 	long	numint;
1832 
1833 	GET_DWORD_ARG(tmp, word);
1834 	numint = my_atol(tmp);
1835 
1836 	if (numint)
1837 	{
1838 		GET_DWORD_ARG(chars, word)
1839 	}
1840 	else
1841 	{
1842 		numint = 1;
1843 		chars = tmp;
1844 	}
1845 
1846 	if (numint < 0 && strlen(word))
1847 		pointer = word + strlen(word) - 1;
1848 
1849 	pointer = search_for(word, &pointer, chars, numint);
1850 
1851 	if (!pointer || !*pointer)
1852 		RETURN_EMPTY;
1853 
1854 	RETURN_STR(pointer + 1);
1855 }
1856 
1857 /* $leftw(num string of text)
1858  * returns the left "num" words in "string of text"
1859  * EX: $leftw(3 now is the time for) returns "now is the"
1860  */
BUILT_IN_FUNCTION(function_leftw,word)1861 BUILT_IN_FUNCTION(function_leftw, word)
1862 {
1863 	int value;
1864 
1865 	GET_INT_ARG(value, word);
1866 	if (value < 1)
1867 		RETURN_EMPTY;
1868 
1869 	return (extractfw(word, 0, value-1));	/* DONT USE RETURN_STR HERE! */
1870 }
1871 
1872 /* $rightw(num string of text)
1873  * returns the right num words in "string of text"
1874  * EX: $rightw(3 now is the time for) returns "the time for"
1875  */
BUILT_IN_FUNCTION(function_rightw,word)1876 BUILT_IN_FUNCTION(function_rightw, word)
1877 {
1878 	int     value;
1879 
1880 	GET_INT_ARG(value, word);
1881 	if (value < 1)
1882 		RETURN_EMPTY;
1883 
1884 	return extractfw2(word, -value, EOS);
1885 }
1886 
1887 
1888 /* $midw(start num string of text)
1889  * returns "num" words starting at word "start" in the string "string of text"
1890  * NOTE: The first word is word #0.
1891  * EX: $midw(2 2 now is the time for) returns "the time"
1892  */
BUILT_IN_FUNCTION(function_midw,word)1893 BUILT_IN_FUNCTION(function_midw, word)
1894 {
1895 	int     start, num;
1896 
1897 	GET_INT_ARG(start, word);
1898 	GET_INT_ARG(num, word);
1899 
1900 	if (num < 1)
1901 		RETURN_EMPTY;
1902 
1903 	return extractfw(word, start, (start + num - 1));
1904 }
1905 
1906 /* $notw(num string of text)
1907  * returns "string of text" with word number "num" removed.
1908  * NOTE: The first word is numbered 0.
1909  * EX: $notw(3 now is the time for) returns "now is the for"
1910  */
BUILT_IN_FUNCTION(function_notw,word)1911 BUILT_IN_FUNCTION(function_notw, word)
1912 {
1913 	char    *booya = (char *)0;
1914 	int     where;
1915 
1916 	GET_INT_ARG(where, word);
1917 
1918 	/* An invalid word simply returns the string as-is */
1919 	if (where < 0)
1920 		RETURN_STR(word);
1921 
1922 	if (where > 0)
1923 	{
1924 		char *part1, *part2;
1925 		part1 = extractfw(word, 0, (where - 1));
1926 		part2 = extractfw(word, (where + 1), EOS);
1927 		booya = malloc_strdup(part1);
1928 		/* if part2 is there, append it. */
1929 		malloc_strcat_wordlist(&booya, space, part2);
1930 		new_free(&part1);
1931 		new_free(&part2);
1932 	}
1933 	else /* where == 0 */
1934 		booya = extractfw(word, 1, EOS);
1935 
1936 	return booya;				/* DONT USE RETURN_STR HERE! */
1937 }
1938 
1939 /*
1940  * $restw(num string of text)
1941  * returns "string of text" that occurs starting with and including
1942  * word number "num"
1943  * NOTE: the first word is numbered 0.
1944  * EX: $restw(3 now is the time for) returns "time for"
1945  */
BUILT_IN_FUNCTION(function_restw,word)1946 BUILT_IN_FUNCTION(function_restw, word)
1947 {
1948 	int     where;
1949 
1950 	GET_INT_ARG(where, word);
1951 	if (where < 0)
1952 		RETURN_EMPTY;
1953 	return extractfw(word, where, EOS);
1954 }
1955 
1956 /*
1957  * $remw(word string of text)
1958  * returns "string of text" with the word "word" removed
1959  * EX: $remw(the now is the time for) returns "now is time for"
1960  * XXX Should use standard word manip functions
1961  */
BUILT_IN_FUNCTION(function_remw,word)1962 BUILT_IN_FUNCTION(function_remw, word)
1963 {
1964 	int	where;
1965 	char *	lame = NULL;
1966 	char *	placeholder;
1967 	char *	booya;
1968 
1969 	lame = LOCAL_COPY(word);
1970 	where = my_atol((placeholder = function_findw(lame)));
1971 	new_free(&placeholder);
1972 
1973 	/* Whack off the word we're looking for... */
1974 	GET_FUNC_ARG(lame, word);
1975 
1976 	/* XXX Cut and pasted from $notw(). */
1977         /* An invalid word simply returns the string as-is */
1978         if (where < 0)
1979                 RETURN_STR(word);
1980 
1981         if (where > 0)
1982         {
1983                 char *part1, *part2;
1984                 part1 = extractfw(word, 0, (where - 1));
1985                 part2 = extractfw(word, (where + 1), EOS);
1986                 booya = malloc_strdup(part1);
1987                 /* if part2 is there, append it. */
1988                 malloc_strcat_wordlist(&booya, space, part2);
1989                 new_free(&part1);
1990                 new_free(&part2);
1991         }
1992         else /* where == 0 */
1993                 booya = extractfw(word, 1, EOS);
1994 
1995 	return booya;			/* DON'T USE RETURN_STR HERE */
1996 }
1997 
1998 /*
1999  * $insertw(num word string of text)
2000  * returns "string of text" such that "word" is the "num"th word
2001  * in the string.
2002  * NOTE: the first word is numbered 0.
2003  * EX: $insertw(3 foo now is the time for) returns "now is the foo time for"
2004  */
BUILT_IN_FUNCTION(function_insertw,word)2005 BUILT_IN_FUNCTION(function_insertw, word)
2006 {
2007 	int     where;
2008 	char    *what;
2009 	char    *booya=(char *)0;
2010 	char 	*str1, *str2;
2011 	size_t	clue = 0;
2012 
2013 	GET_INT_ARG(where, word);
2014 
2015 	/* If the word goes at the front of the string, then it
2016 	   already is: return it. ;-) */
2017 	if (where < 1)
2018 		booya = malloc_strdup(word);
2019 	else
2020 	{
2021 		GET_FUNC_ARG(what, word);
2022 		str1 = extractfw(word, 0, (where - 1));
2023 		str2 = extractfw(word, where, EOS);
2024 
2025 		malloc_strcat_wordlist_c(&booya, space, str1, &clue);
2026 		malloc_strcat_word_c(&booya, space, what, DWORD_DWORDS, &clue);
2027 		malloc_strcat_wordlist_c(&booya, space, str2, &clue);
2028 
2029 		new_free(&str1);
2030 		new_free(&str2);
2031 	}
2032 
2033 	return booya;				/* DONT USE RETURN_STR HERE! */
2034 }
2035 
2036 /* $chngw(num word string of text)
2037  * returns "string of text" such that the "num"th word is removed
2038  * and replaced by "word"
2039  * NOTE: the first word is numbered 0
2040  * EX: $chngw(3 foo now is the time for) returns "now is the foo for"
2041  */
BUILT_IN_FUNCTION(function_chngw,word)2042 BUILT_IN_FUNCTION(function_chngw, word)
2043 {
2044 	int     which;
2045 	char    *what;
2046 	char	*str1, *str2;
2047 
2048 	GET_INT_ARG(which, word);
2049 	GET_FUNC_ARG(what, word);
2050 
2051 	if (which < 0)
2052 		RETURN_STR(word);
2053 
2054 	/* hmmm. if which is 0, extract does the wrong thing. */
2055 	str1 = extractfw(word, 0, which - 1);
2056 	str2 = extractfw(word, which + 1, EOS);
2057 
2058 	malloc_strcat_word(&str1, space, what, DWORD_DWORDS);
2059 	malloc_strcat_wordlist(&str1, space, str2);
2060 	new_free(&str2);
2061 	return str1;
2062 }
2063 
2064 
2065 /* $common (string of text / string of text)
2066  * Given two sets of words seperated by a forward-slash '/', returns
2067  * all words that are found in both sets.
2068  * EX: $common(one two three / buckle my two shoe one) returns "one two"
2069  * NOTE: returned in order found in first string.
2070  * NOTE: This may fudge if you have a word in the first set for which
2071  *       there is a word in the second set that is a superset of the word:
2072  *	 $common(one two three / phone ooga booga) returns "one"
2073  */
BUILT_IN_FUNCTION(function_common,word)2074 BUILT_IN_FUNCTION(function_common, word)
2075 {
2076 	char    *left = (char *) 0;
2077 	char	*right = (char *) 0;
2078 	char 	*booya = NULL;
2079 	char **leftw = NULL;
2080 	char **rightw = NULL;
2081 	int	leftc, lefti,
2082 		rightc, righti;
2083 	size_t	rvclue=0;
2084 
2085 	left = word;
2086 	if (!(right = strchr(word,'/')))
2087 		RETURN_EMPTY;
2088 
2089 	*right++ = 0;
2090 	leftc = splitw(left, &leftw, DWORD_DWORDS);
2091 	rightc = splitw(right, &rightw, DWORD_DWORDS);
2092 
2093 	for (lefti = 0; lefti < leftc; lefti++)
2094 	{
2095 		for (righti = 0; righti < rightc; righti++)
2096 		{
2097 			if (rightw[righti] && !my_stricmp(leftw[lefti], rightw[righti]))
2098 			{
2099 				malloc_strcat_word_c(&booya, space, leftw[lefti], DWORD_DWORDS, &rvclue);
2100 				rightw[righti] = NULL;
2101 			}
2102 		}
2103 	}
2104 
2105 	new_free((char **)&leftw);
2106 	new_free((char **)&rightw);
2107 
2108 	RETURN_MSTR(booya);
2109 }
2110 
2111 /*
2112  * $diff(string of text / string of text)
2113  * given two sets of words, seperated by a forward-slash '/', returns
2114  * all words that are not found in both sets
2115  * EX: $diff(one two three / buckle my two shoe)
2116  * returns "one three buckle my shoe"
2117  */
BUILT_IN_FUNCTION(function_diff,word)2118 BUILT_IN_FUNCTION(function_diff, word)
2119 {
2120 	char 	*left = NULL,
2121 	     	*right = NULL,
2122 		*booya = NULL;
2123 	char **rightw = NULL,
2124 		   **leftw = NULL;
2125 	int 	lefti, leftc,
2126 	    	righti, rightc;
2127 	int 	found;
2128 	size_t	rvclue=0;
2129 
2130 	left = word;
2131 	if ((right = strchr(word, '/')) == (char *) 0)
2132 		RETURN_EMPTY;
2133 
2134 	*right++ = 0;
2135 	leftc = splitw(left, &leftw, DWORD_DWORDS);
2136 	rightc = splitw(right, &rightw, DWORD_DWORDS);
2137 
2138 	for (rvclue = lefti = 0; lefti < leftc; lefti++)
2139 	{
2140 		found = 0;
2141 		for (righti = 0; righti < rightc; righti++)
2142 		{
2143 			if (rightw[righti] && !my_stricmp(leftw[lefti], rightw[righti]))
2144 			{
2145 				found = 1;
2146 				rightw[righti] = NULL;
2147 			}
2148 		}
2149 		if (!found)
2150 			malloc_strcat_word_c(&booya, space, leftw[lefti], DWORD_DWORDS, &rvclue);
2151 	}
2152 
2153 	for (rvclue = righti = 0; righti < rightc; righti++)
2154 	{
2155 		if (rightw[righti])
2156 			malloc_strcat_word_c(&booya, space, rightw[righti], DWORD_DWORDS, &rvclue);
2157 	}
2158 
2159 	new_free((char **)&leftw);
2160 	new_free((char **)&rightw);
2161 
2162 	RETURN_MSTR(booya);
2163 }
2164 
wrapper_pattern(char * word,int mode)2165 char *wrapper_pattern(char *word, int mode)
2166 {
2167 	char    *blah;
2168 	char    *booya = NULL;
2169 	char    *pattern;
2170 	size_t	rvclue=0;
2171 
2172 	GET_FUNC_ARG(pattern, word)
2173 	while (((blah = next_func_arg(word, &word)) != NULL))
2174 	{
2175 		if (!!wild_match(pattern, blah) == !!mode)
2176 			malloc_strcat_word_c(&booya, space, blah, DWORD_DWORDS, &rvclue);
2177 	}
2178 	RETURN_MSTR(booya);
2179 }
2180 
2181 /* $pattern(pattern string of words)
2182  * given a pattern and a string of words, returns all words that
2183  * are matched by the pattern
2184  * EX: $pattern(f* one two three four five) returns "four five"
2185  */
2186 
BUILT_IN_FUNCTION(function_pattern,word)2187 BUILT_IN_FUNCTION(function_pattern, word) {
2188 	return(wrapper_pattern(word, 1));
2189 }
2190 
2191 /* $filter(pattern string of words)
2192  * given a pattern and a string of words, returns all words that are
2193  * NOT matched by the pattern
2194  * $filter(f* one two three four five) returns "one two three"
2195  */
2196 
BUILT_IN_FUNCTION(function_filter,word)2197 BUILT_IN_FUNCTION(function_filter, word) {
2198 	return(wrapper_pattern(word, 0));
2199 }
2200 
wrapper_rpattern(char * word,int mode)2201 char *wrapper_rpattern (char *word, int mode)
2202 {
2203 	char    *blah;
2204 	char    *booya = NULL;
2205 	char    *pattern;
2206 	size_t	rvclue=0;
2207 
2208 	GET_FUNC_ARG(blah, word)
2209 
2210 	while ((pattern = next_func_arg(word, &word)) != NULL)
2211 	{
2212 		if (!!wild_match(pattern, blah) == !!mode)
2213 			malloc_strcat_word_c(&booya, space, pattern, DWORD_DWORDS, &rvclue);
2214 	}
2215 	RETURN_MSTR(booya);
2216 }
2217 
2218 /* $rpattern(word list of patterns)
2219  * Given a word and a list of patterns, return all patterns that
2220  * match the word.
2221  * EX: $rpattern(user@host.com *@* user@* f*@*.com)
2222  * returns "*@* user@*"
2223  */
2224 
BUILT_IN_FUNCTION(function_rpattern,word)2225 BUILT_IN_FUNCTION(function_rpattern, word) {
2226 	return(wrapper_rpattern(word, 1));
2227 }
2228 
2229 /* $rfilter(word list of patterns)
2230  * given a word and a list of patterns, return all patterns that
2231  * do NOT match the word
2232  * EX: $rfilter(user@host.com *@* user@* f*@*.com)
2233  * returns "f*@*.com"
2234  */
2235 
BUILT_IN_FUNCTION(function_rfilter,word)2236 BUILT_IN_FUNCTION(function_rfilter, word) {
2237 	return(wrapper_rpattern(word, 0));
2238 }
2239 
2240 /* $copattern(pattern var_1 var_2)
2241  * Given a pattern and two variable names, it returns all words
2242  * in the variable_2 corresponding to any words in variable_1 that
2243  * are matched by the pattern
2244  * EX: @nicks = [nick1 nick2 nick3]
2245  *     @userh = [user1@host1.com user2@host1.com user3@host2.com]
2246  *     $copattern(*@host1.com userh nicks)
2247  *	returns "nick1 nick2"
2248  */
2249 #define COPATFUNC(fn, pat, arg, sense)                                 \
2250 BUILT_IN_FUNCTION((fn), word)                                          \
2251 {                                                                      \
2252        char    *booya = (char *) 0,                                    \
2253 	       *pattern = (char *) 0,                                  \
2254                *firstl = (char *) 0, *firstlist = (char *) 0, *firstel = (char *) 0,       \
2255                *secondl = (char *) 0, *secondlist = (char *) 0, *secondel = (char *) 0;    \
2256        char    *sfirstl, *ssecondl;                                          \
2257        size_t  rvclue=0;                                                     \
2258                                                                              \
2259        GET_DWORD_ARG(pattern, word);                                         \
2260        GET_FUNC_ARG(firstlist, word);                                       \
2261        GET_FUNC_ARG(secondlist, word);                                      \
2262                                                                              \
2263        firstl = get_variable(firstlist);                                     \
2264        secondl = get_variable(secondlist);                                   \
2265        sfirstl = firstl;                                                     \
2266        ssecondl = secondl;                                                   \
2267                                                                              \
2268        while ((firstel = next_func_arg(firstl, &firstl)))                     \
2269        {                                                                     \
2270                if (!(secondel = next_func_arg(secondl, &secondl)))            \
2271                        break;                                                \
2272                                                                              \
2273                if ((sense) == !wild_match((pat), (arg)))                     \
2274                        malloc_strcat_word_c(&booya, space, secondel, DWORD_DWORDS, &rvclue); \
2275        }                                                                     \
2276        new_free(&sfirstl);                                                   \
2277        new_free(&ssecondl);                                                  \
2278        RETURN_MSTR(booya);                                                   \
2279 }
2280 COPATFUNC(function_copattern, pattern, firstel, 0)
2281 COPATFUNC(function_corpattern, firstel, pattern, 0)
2282 COPATFUNC(function_cofilter, pattern, firstel, 1)
2283 COPATFUNC(function_corfilter, firstel, pattern, 1)
2284 #undef COPATFUNC
2285 
2286 
2287 /* $beforew(pattern string of words)
2288  * returns the portion of "string of words" that occurs before the
2289  * first word that is matched by "pattern"
2290  * EX: $beforew(three one two three o leary) returns "one two"
2291  */
BUILT_IN_FUNCTION(function_beforew,word)2292 BUILT_IN_FUNCTION(function_beforew, word)
2293 {
2294 	int     where;
2295 	char	*lame = (char *) 0;
2296 	char	*placeholder;
2297 
2298 	lame = LOCAL_COPY(word);
2299 	where = my_atol((placeholder = function_findw(word))) + 1;
2300 	new_free(&placeholder);
2301 
2302 	if (where < 1)
2303 		RETURN_EMPTY;
2304 
2305 	placeholder = extractfw(lame, 1, where - 1);
2306 	return placeholder;
2307 }
2308 
2309 /* Same as above, but includes the word being matched */
BUILT_IN_FUNCTION(function_tow,word)2310 BUILT_IN_FUNCTION(function_tow, word)
2311 {
2312 	int     where;
2313 	char	*lame = (char *) 0;
2314 	char	*placeholder;
2315 
2316 	lame = LOCAL_COPY(word);
2317 	where = my_atol((placeholder = function_findw(word))) + 1;
2318 	new_free(&placeholder);
2319 
2320 	if (where < 1)
2321 		RETURN_EMPTY;
2322 
2323 	placeholder = extractfw(lame, 1, where);
2324 	return placeholder;
2325 }
2326 
2327 /* Returns the string after the word being matched */
BUILT_IN_FUNCTION(function_afterw,word)2328 BUILT_IN_FUNCTION(function_afterw, word)
2329 {
2330 	int     where;
2331 	char	*lame = (char *) 0;
2332 	char	*placeholder;
2333 
2334 	lame = malloc_strdup(word);
2335 	placeholder = function_findw(word);
2336 	where = my_atol(placeholder) + 1;
2337 
2338 	new_free(&placeholder);
2339 
2340 	if (where < 1)
2341 	{
2342 		new_free(&lame);
2343 		RETURN_EMPTY;
2344 	}
2345 	placeholder = extractfw(lame, where + 1, EOS);
2346 	new_free(&lame);
2347 	return placeholder;
2348 }
2349 
2350 /* Returns the string starting with the word being matched */
BUILT_IN_FUNCTION(function_fromw,word)2351 BUILT_IN_FUNCTION(function_fromw, word)
2352 {
2353 	int     where;
2354 	char 	*lame = (char *) 0;
2355 	char	*placeholder;
2356 
2357 	lame = malloc_strdup(word);
2358 	placeholder = function_findw(word);
2359 	where = my_atol(placeholder) + 1;
2360 
2361 	new_free(&placeholder);
2362 
2363 	if (where < 1)
2364 	{
2365 		new_free(&lame);
2366 		RETURN_EMPTY;
2367 	}
2368 
2369 	placeholder = extractfw(lame, where, EOS);
2370 	new_free(&lame);
2371 	return placeholder;
2372 }
2373 
2374 /* Cut and paste a string */
BUILT_IN_FUNCTION(function_splice,word)2375 BUILT_IN_FUNCTION(function_splice, word)
2376 {
2377 	char    *variable;
2378 	int	start;
2379 	int	length;
2380 	char 	*left_part = NULL;
2381 	char	*middle_part = NULL;
2382 	char	*right_part = NULL;
2383 	char	*old_value = NULL;
2384 	char	*new_value = NULL;
2385 	int	num_words;
2386 	size_t	clue = 0;
2387 
2388 	GET_FUNC_ARG(variable, word);
2389 	GET_INT_ARG(start, word);
2390 	GET_INT_ARG(length, word);
2391 
2392 	old_value = get_variable(variable);
2393 	num_words = count_words(old_value, DWORD_DWORDS, "\"");
2394 
2395 	if (start < 0)
2396 	{
2397 		if ((length += start) <= 0)
2398 			RETURN_EMPTY;
2399 		start = 0;
2400 	}
2401 
2402 	if (start >= num_words)
2403 	{
2404 		left_part = malloc_strdup(old_value);
2405 		middle_part = malloc_strdup(empty_string);
2406 		right_part = malloc_strdup(empty_string);
2407 	}
2408 
2409 	else if (start + length >= num_words)
2410 	{
2411 		left_part = extractfw(old_value, 0, start - 1);
2412 		middle_part = extractfw(old_value, start, EOS);
2413 		right_part = malloc_strdup(empty_string);
2414 	}
2415 
2416 	else
2417 	{
2418 		left_part = extractfw(old_value, 0, start - 1);
2419 		middle_part = extractfw(old_value, start, start + length - 1);
2420 		right_part = extractfw(old_value, start + length, EOS);
2421 	}
2422 
2423 	malloc_strcat_wordlist_c(&new_value, space, left_part, &clue);
2424 	malloc_strcat_wordlist_c(&new_value, space, word, &clue);
2425 	malloc_strcat_wordlist_c(&new_value, space, right_part, &clue);
2426 
2427 	add_var_alias(variable, new_value, 0);
2428 
2429 	new_free(&old_value);
2430 	new_free(&new_value);
2431 	new_free(&left_part);
2432 	new_free(&right_part);
2433 	return middle_part;
2434 }
2435 
2436 /* Worked over by jfn on 3/20/97.  If it breaks, yell at me. */
BUILT_IN_FUNCTION(function_numonchannel,word)2437 BUILT_IN_FUNCTION(function_numonchannel, word)
2438 {
2439 	RETURN_INT(number_on_channel(next_func_arg(word, &word), from_server));
2440 }
2441 
2442 /* Worked over by jfn on 3/20/97.  If it breaks, yet all me. */
2443 /*
2444  * ircII2.8.2 defines $onchannel as:
2445  *   $onchannel(nick channel)
2446  * Which returns 1 if nick is on channel, 0, if not.
2447  * It returns empty on some kind of bizarre error.
2448  *
2449  * We had previously defined $onchannel as:
2450  *   $onchannel(channel)
2451  * Which returned everyone on the given channel
2452  * Which is a synonym for ircII2.8.2's $chanusers().
2453  */
BUILT_IN_FUNCTION(function_onchannel,word)2454 BUILT_IN_FUNCTION(function_onchannel, word)
2455 {
2456 	char		*channel;
2457 	char		*nicks = NULL;
2458 
2459 	channel = next_func_arg(word, &word);
2460 	if ((nicks = create_nick_list(channel, from_server)))
2461 		return nicks;
2462 	else
2463 	{
2464 		if (!nicks && (!word || !*word))
2465 			RETURN_EMPTY;
2466 
2467 		nicks = channel;
2468 		channel = next_func_arg(word, &word);
2469 		RETURN_INT(is_on_channel(channel, nicks) ? 1 : 0);
2470 	}
2471 }
2472 
BUILT_IN_FUNCTION(function_chops,word)2473 BUILT_IN_FUNCTION(function_chops, word)
2474 {
2475 	char		*channel;
2476 
2477 	channel = next_func_arg(word, &word);
2478 	return create_chops_list(channel, from_server);
2479 }
2480 
BUILT_IN_FUNCTION(function_nochops,word)2481 BUILT_IN_FUNCTION(function_nochops, word)
2482 {
2483 	char		*channel;
2484 
2485 	channel = next_func_arg(word, &word);
2486 	return create_nochops_list(channel, from_server);
2487 }
2488 
2489 /* Worked over by jfn on 3/20/97.  If it breaks, yet all me. */
BUILT_IN_FUNCTION(function_key,word)2490 BUILT_IN_FUNCTION(function_key, word)
2491 {
2492 	char		*channel;
2493 	char    	*booya = (char *) 0;
2494 	const char 	*key;
2495 	size_t		rvclue=0;
2496 
2497 	do
2498 	{
2499 		channel = next_func_arg(word, &word);
2500 		if ((!channel || !*channel) && booya)
2501 			break;
2502 
2503 		key = get_channel_key(channel, from_server);
2504 		malloc_strcat_word_c(&booya, space, (key && *key) ? key : "*", DWORD_DWORDS, &rvclue);
2505 	}
2506 	while (word && *word);
2507 
2508 	RETURN_MSTR(booya);
2509 }
2510 
2511 /*
2512  * Based on a contribution made a long time ago by wintrhawk
2513  */
2514 /* Worked over by jfn on 3/20/97.  If it breaks, yet all me. */
BUILT_IN_FUNCTION(function_channelmode,word)2515 BUILT_IN_FUNCTION(function_channelmode, word)
2516 {
2517 	char	*channel;
2518 	char    *booya = (char *) 0;
2519 	const char	*mode;
2520 	size_t	rvclue=0;
2521 
2522 	do
2523 	{
2524 		channel = next_func_arg(word, &word);
2525 		if ((!channel || !*channel) && booya)
2526 			break;
2527 
2528 		mode = get_channel_mode(channel, from_server);
2529 		malloc_strcat_word_c(&booya, space, (mode && *mode) ? mode : "*", DWORD_DWORDS, &rvclue);
2530 	}
2531 	while (word && *word);
2532 
2533 	RETURN_MSTR(booya);
2534 }
2535 
2536 
2537 
BUILT_IN_FUNCTION(function_revw,words)2538 BUILT_IN_FUNCTION(function_revw, words)
2539 {
2540 	char *booya = NULL;
2541 	size_t  rvclue=0, wclue=0;
2542 
2543 	while (words && *words)
2544 		malloc_strcat_word_c(&booya, space,
2545 				last_arg(&words, &wclue, DWORD_DWORDS),
2546 				DWORD_DWORDS, &rvclue);
2547 
2548 	RETURN_MSTR(booya);
2549 }
2550 
BUILT_IN_FUNCTION(function_reverse,words)2551 BUILT_IN_FUNCTION(function_reverse, words)
2552 {
2553 	char *	retval;
2554 	int	retval_size;
2555 	unsigned char	*x, *y, *p, *r;
2556 
2557 	retval_size = strlen(words);
2558 	r = retval = new_malloc(retval_size + 1);
2559 
2560 	/* Start at the end of the string */
2561 	x = words + strlen(words);
2562 
2563 	/* Walk back each code point from the end... */
2564 	while (p = x, (previous_code_point(words, CUC_PP &x)))
2565 	{
2566 		/* Save our place */
2567 		y = x;
2568 
2569 		/* Copy all bytes for this code point */
2570 		while (x < p)
2571 			*r++ = *x++;
2572 
2573 		/* Restore our place */
2574 		x = y;
2575 	}
2576 
2577 	*r = 0;
2578 	return retval;
2579 }
2580 
2581 /*
2582  * To save time, try to precalc the size of the output buffer.
2583  * jfn 03/18/98
2584  */
BUILT_IN_FUNCTION(function_jot,input)2585 BUILT_IN_FUNCTION(function_jot, input)
2586 {
2587 	double  start 	= 0;
2588 	double  stop 	= 0;
2589 	double  interval = 1;
2590 	double  counter;
2591 	char	*booya 	= NULL;
2592 	size_t	clue = 0;
2593 	char	ugh[100];
2594 
2595         GET_FLOAT_ARG(start,input)
2596         GET_FLOAT_ARG(stop, input)
2597         if (input && *input)
2598                 GET_FLOAT_ARG(interval, input)
2599         else
2600                 interval = 1.0;
2601 
2602 	if (interval == 0)
2603 		RETURN_EMPTY;
2604         if (interval < 0)
2605                 interval = -interval;
2606 
2607         if (start < stop)
2608 	{
2609 		for (counter = start;
2610 		     counter <= stop;
2611 		     counter += interval)
2612 		{
2613 			snprintf(ugh, 99, "%f", counter);
2614 			canon_number(ugh);
2615 			malloc_strcat_word_c(&booya, space, ugh, DWORD_NO, &clue);
2616 		}
2617 	}
2618         else
2619 	{
2620 		for (counter = start;
2621 		     counter >= stop;
2622 		     counter -= interval)
2623 		{
2624 			snprintf(ugh, 99, "%f", counter);
2625 			canon_number(ugh);
2626 			malloc_strcat_word_c(&booya, space, ugh, DWORD_NO, &clue);
2627 		}
2628 	}
2629 
2630 	RETURN_MSTR(booya);
2631 }
2632 
function_shiftbrace(char * word)2633 char *function_shiftbrace (char *word)
2634 {
2635 	char    *value = (char *) 0;
2636 	char    *var    = (char *) 0;
2637 	char	*booya 	= (char *) 0;
2638 	char    *placeholder;
2639 	char 	*oof;
2640 
2641 	GET_FUNC_ARG(var, word);
2642 	if (!var || !*var)
2643 		RETURN_EMPTY;
2644 
2645 	placeholder = value = get_variable(var);
2646 	if ((oof = next_expr(&value, '{')) == NULL)
2647 	{
2648 		new_free(&placeholder);
2649 		RETURN_EMPTY;
2650 	}
2651 	booya = malloc_strdup(oof);
2652 	add_var_alias(var, value, 0);
2653 
2654 	new_free(&placeholder);
2655 	RETURN_MSTR(booya);
2656 }
2657 
2658 #if 0
2659 char *function_shiftseg (char *input)
2660 {
2661 	char *var;
2662 	char *tok;
2663 
2664         char *placeholder;
2665 	char *blah;
2666 
2667 	if (!input || !*input)
2668 		RETURN_EMPTY;
2669 
2670 	GET_DWORD_ARG(tok, input);
2671         GET_FUNC_ARG(var, input);
2672 
2673 	upper(var);
2674 
2675 	placeholder = get_variable(var);
2676 
2677         ssize_t x = stristr(placeholder, tok);
2678 
2679         if (x < 0) {
2680                 add_var_alias(var, empty_string, 0);
2681 		RETURN_STR(placeholder);
2682         }
2683 
2684         blah = placeholder + x;
2685 
2686         /* we chop our string in half here, separating the result from
2687 	 what gets placed back into our variable */
2688 	*blah=0;
2689         /* then we skip over our token, since it's a separator and not data */
2690 	blah+=strlen(tok);
2691 
2692 	if (var)
2693                 add_var_alias(var, blah, 0);
2694 
2695         /* this will copy everything from the beginning up until our token */
2696 	blah = malloc_strdup(placeholder);
2697 
2698 	new_free(&placeholder);
2699 
2700 	return blah;
2701 }
2702 #endif
2703 
function_shift(char * word)2704 char *function_shift (char *word)
2705 {
2706 	char    *value = (char *) 0;
2707 	char    *var    = (char *) 0;
2708 	char	*booya 	= (char *) 0;
2709 	char *	free_it = NULL;
2710 
2711 	GET_FUNC_ARG(var, word);
2712 
2713 #if 0
2714 	if (word && *word)
2715 		GET_FUNC_ARG(next, word);
2716 	if (next && *next)
2717 		RETURN_STR(var);
2718 #endif
2719 
2720 	upper(var);
2721 	free_it = value = get_variable(var);
2722 
2723 	booya = malloc_strdup(next_func_arg(value, &value));
2724 	if (var)
2725 		add_var_alias(var, value, 0);
2726 	new_free(&free_it);
2727 	RETURN_MSTR(booya);
2728 }
2729 
2730 /*
2731  * Usage: $unshift(<lval> <text>)
2732  * Note that <lval> and <text> are not <word> or <word list>, so the
2733  * rules governing double quotes and spaces and all that don't apply.
2734  * If <lval> has an illegal character, the false value is returned.
2735  */
function_unshift(char * word)2736 char *function_unshift (char *word)
2737 {
2738 	char    *value = (char *) 0;
2739 	char    *var    = (char *) 0;
2740 	char	*booya  = (char *) 0;
2741 
2742 	var = word;
2743 	word = after_expando(word, 1, NULL);
2744 
2745 	if (isspace(*word))
2746 		*word++ = 0;
2747 	/* If the variable has an illegal character, punt */
2748 	else if (!*var || *word)
2749 		RETURN_EMPTY;
2750 
2751 	upper(var);
2752 	value = get_variable(var);
2753 	if (!word || !*word)
2754 		return value;
2755 
2756 	malloc_strcat_word(&booya, space, word, DWORD_DWORDS);
2757 	malloc_strcat_wordlist(&booya, space, value);
2758 
2759 	add_var_alias(var, booya, 0);
2760 	new_free(&value);
2761 	RETURN_MSTR(booya);
2762 }
2763 
2764 /*
2765  * Usage: $push(<lval> <text>)
2766  * Note that <lval> is an <lval> and not a <word> so it doesn't
2767  * honor double quotes and you can't have invalid characters in it.
2768  * If there is a syntax error, the false value is returned.  Note
2769  * that <text> is <text> and not <word list> so you can't put double
2770  * quotes in there, either.
2771  */
function_push(char * word)2772 char *function_push (char *word)
2773 {
2774 	char    *value = (char *) 0;
2775 	char    *var    = (char *) 0;
2776 
2777 	var = word;
2778 	word = after_expando(word, 1, NULL);
2779 
2780 	if (isspace(*word))
2781 		*word++ = 0;
2782 	/* If the variable has an illegal character, punt */
2783 	else if (*word)
2784 		RETURN_EMPTY;
2785 
2786 	upper(var);
2787 	value = get_variable(var);
2788 	if (!word || !*word)
2789 		RETURN_MSTR(value);
2790 
2791 	malloc_strcat_word(&value, space, word, DWORD_DWORDS);
2792 	add_var_alias(var, value, 0);
2793 	RETURN_MSTR(value);
2794 }
2795 
function_pop(char * word)2796 char *function_pop (char *word)
2797 {
2798 	char *value	= (char *) 0;
2799 	char *var	= (char *) 0;
2800 	char *pointer	= (char *) 0;
2801 	char *blech     = (char *) 0;
2802 	size_t	cluep;
2803 	char *free_it = NULL;
2804 
2805 	GET_FUNC_ARG(var, word);
2806 
2807 #if 0
2808 	while (word && *word)
2809 		GET_FUNC_ARG(last, word);
2810 	if (last && *last)
2811 		RETURN_STR(last);
2812 #endif
2813 
2814 	upper(var);
2815 	free_it = value = get_variable(var);
2816 	if (!value || !*value)
2817 	{
2818 		new_free(&free_it);
2819 		RETURN_EMPTY;
2820 	}
2821 
2822 	cluep = strlen(value);
2823 	pointer = last_arg(&value, &cluep, DWORD_DWORDS);
2824 	if (!value || cluep == 0)
2825 		value = endstr(pointer);
2826 
2827 	/*
2828 	 * because pointer points to value, we *must* make a copy of it
2829 	 * *before* we free value! (And we cant forget to free value, either)
2830 	 */
2831 	blech = malloc_strdup(pointer);
2832 	add_var_alias(var, value, 0);
2833 	new_free(&free_it);
2834 	return blech;
2835 }
2836 
2837 
2838 /*
2839  * Search and replace function --
2840  * Usage:   $sar(c/search/replace/data)
2841  * Commands:
2842  *		r - treat data as a variable name and
2843  *		    return the replaced data to the variable
2844  * 		g - Replace all instances, not just the first one
2845  *  The delimiter may be any character that is not a command (typically /)
2846  *  The delimiter MUST be the first character after the command
2847  *  Returns empty string on syntax error
2848  *  Returns 'data' if 'search' is of zero length.
2849  *
2850  * Note: Last rewritten on December 2, 1999
2851  * Rewritten again on Feb 14, 2005 based on function_msar.
2852  */
BUILT_IN_FUNCTION(function_sar,input)2853 BUILT_IN_FUNCTION(function_sar, input)
2854 {
2855 	int	variable = 0,
2856 		global = 0,
2857 		case_sensitive = 0;
2858 	int	delimiter;
2859 	char *	last_segment;
2860 	char *	text;
2861 	char *	after;
2862 	char *	workbuf = NULL;
2863 	char *	search;
2864 	char *	replace;
2865 
2866 	/*
2867 	 * Scan the leading part of the argument list, slurping up any
2868 	 * options, and grabbing the delimiter character.  If we don't
2869 	 * come across a delimiter, then just end abruptly.
2870 	 */
2871 	for (;; input++)
2872 	{
2873 		if (*input == 'r')
2874 			variable = 1;
2875 		else if (*input == 'g')
2876 			global = 1;
2877 		else if (*input == 'i')
2878 			case_sensitive = 0;
2879 		else if (*input == 'c')
2880 			case_sensitive = 1;
2881 		else if (!*input)
2882 			RETURN_EMPTY;
2883 		else
2884 		{
2885 			while ((delimiter = next_code_point(CUC_PP &input, 0)) == -1)
2886 				input++;
2887 
2888 			break;
2889 		}
2890 	}
2891 
2892 	/*
2893 	 * "input" contains a pair of strings like:
2894 	 *	search<delim>replace[<delim>|<eol>]
2895 	 *
2896 	 * If we don't find a pair, then perform no substitution --
2897 	 * just bail out right here.
2898 	 */
2899 	search = next_in_div_list(input, &after, delimiter);
2900 	RETURN_IF_EMPTY(search);
2901 	replace = next_in_div_list(after, &after, delimiter);
2902 /*	RETURN_IF_EMPTY(replace); */
2903 
2904 	/*
2905 	 * The last segment is either a text string, or a variable.  If it
2906 	 * is a variable, look up its value.
2907 	 */
2908 	last_segment = after;
2909 	RETURN_IF_EMPTY(last_segment);
2910 
2911 	if (variable == 1)
2912 		text = get_variable(last_segment);
2913 	else
2914 		text = malloc_strdup(last_segment);
2915 
2916 	workbuf = substitute_string(text, search, replace,
2917 					case_sensitive, global);
2918 	new_free(&text);
2919 
2920         if (variable)
2921                 add_var_alias(last_segment, workbuf, 0);
2922 	return workbuf;
2923 }
2924 
BUILT_IN_FUNCTION(function_center,word)2925 BUILT_IN_FUNCTION(function_center, word)
2926 {
2927 	size_t  fieldsize,
2928 		stringlen,
2929 		cols,		/* Apparently "columns" is taken */
2930 		pad;
2931 	char    *padc;
2932 	char	*copy;
2933 
2934 	/* The width they want */
2935 	GET_INT_ARG(fieldsize, word)
2936 
2937 	/*
2938 	 * XXX This is copied from function_printlen().
2939 	 * This should probably be a generic operation.
2940 	 */
2941         copy = new_normalize_string(word, 2, NORMALIZE);
2942         cols = output_with_count(copy, 0, 0);
2943 	stringlen = strlen(copy);
2944         new_free(&copy);
2945 
2946 	/* The string is wider than the field, just return it */
2947 	if (cols > fieldsize)
2948 		RETURN_STR(word);
2949 
2950 	/*
2951 	 * Calculate how much space we need.
2952 	 * For a string N columns wide, centered in a field of length X,
2953 	 * N / 2 is the size of the string in each half of the field;
2954 	 * (X - N) / 2 is the size of the space in each half of the field;
2955 	 * Therefore, the size of the field is:
2956 	 *      N + ((X - N) / 2)
2957 	 *
2958 	 * Since the string may be utf8, we must use the column count to
2959 	 * determine how many bytes to add; but we must use the actual
2960 	 * string length to decide how big the result is.
2961 	 */
2962 	pad = stringlen + ((fieldsize - cols) / 2);
2963 	padc = (char *)new_malloc(pad + 1);
2964 	snprintf(padc, pad + 1, "%*s", (int)pad, word);  /* Right justify it */
2965 	return padc;
2966 }
2967 
2968 /*
2969  * Usage: $fix_width(<width> <l, c, r> "<fill char>" <string>)
2970  * Returns: A string exactly <width> columns wide.
2971  * Arguments:
2972  *   $0  - How many columns the resulting string should be
2973  *   $1  - Justification - left, center, or right
2974  *   $2  - Character to use if the string is too short (always a dword)
2975  *   $3- - The string to format
2976  *
2977  * Note: The resulting string is always exactly <width> columns long.
2978  * If the string is too long, it will be truncated in accordance with
2979  * the justification (ie, 'l' means the end of the string is truncated)
2980  */
BUILT_IN_FUNCTION(function_fix_width,word)2981 BUILT_IN_FUNCTION(function_fix_width, word)
2982 {
2983         int 	width;
2984         char *	justify;
2985 	int	justifynum = -1;
2986 	char *	fillchar_str;
2987 	int	fillchar;
2988         char *	retval;
2989 
2990         GET_INT_ARG(width, word);
2991 	if (width < 0 || !*word)
2992                 RETURN_EMPTY;
2993 
2994         GET_FUNC_ARG(justify, word);
2995 	if (!my_strnicmp(justify, "left", 1))
2996 		justifynum = -1;
2997 	else if (!my_strnicmp(justify, "center", 1))
2998 		justifynum = 0;
2999 	else if (!my_strnicmp(justify, "right", 1))
3000 		justifynum = 1;
3001 	else
3002 		RETURN_EMPTY;
3003 
3004 	GET_DWORD_ARG(fillchar_str, word);
3005 	fillchar = next_code_point(CUC_PP &fillchar_str, 1);
3006 
3007 	retval = fix_string_width(word, justifynum, fillchar, width, 1);
3008 	RETURN_MSTR(retval);
3009 }
3010 
3011 /*
3012  * Why does this look so much like cpindex?
3013  * Because code points can take up different sizes.
3014  * So if the user tries to split on a 2-byte code point, we can't
3015  * just replace it with one space -- or two spaces -- in place!
3016  * No, we have to copy the whole string, byte by byte to do it right.
3017  */
BUILT_IN_FUNCTION(function_split,word)3018 BUILT_IN_FUNCTION(function_split, word)
3019 {
3020 	unsigned char 	*search;
3021 	int		inverted = 0;
3022 	unsigned char 	*retval, *r;
3023 const	unsigned char	*p, *s;
3024 	int		c, d;
3025 	int		found;
3026 
3027 	/*
3028 	 * What chars does the user want converted to space?
3029 	 * This has "sindex" semantics -- so a leading ^ inverts the set
3030 	 */
3031 	GET_DWORD_ARG(search, word)
3032 	if (*search == '^')
3033 	{
3034 		inverted = 1;
3035 		search++;
3036 	}
3037 
3038 	/* The return value will not be shorter than the input */
3039 	r = retval = new_malloc(strlen(word) + 1);
3040 
3041 	/* For each code point in the source string... */
3042 	p = word;
3043 	while ((c = next_code_point(&p, 1)))
3044 	{
3045 		/* Look for that same code point in the search string */
3046 		found = 0;
3047 		s = search;
3048 		while ((d = next_code_point(&s, 1)))
3049 		{
3050 			if (c == d)
3051 			{
3052 				found = 1;
3053 				break;
3054 			}
3055 		}
3056 
3057 		/* If we found a match, put a space here. */
3058 		if (found != inverted)
3059 			*r++ = ' ';
3060 		/* Otherwise, copy the code point over */
3061 		else
3062 		{
3063 			unsigned char utf8str[16];
3064 			char *x;
3065 
3066 			ucs_to_utf8(c, utf8str, sizeof(utf8str));
3067 			for (x = utf8str; *x; x++)
3068 				*r++ = *x;
3069 		}
3070 	}
3071 
3072 	*r = 0;
3073 	return retval;
3074 }
3075 
3076 /*
3077  * This function converts an input word list into a string, where each word
3078  * of the input string must be a cardinal number in base 10 (0-255) or base 16
3079  * (0x00-0xFF -- including the leading "0x"!).  The output string is the
3080  * sequence of bytes represented by the catenation of each of these cardinal
3081  * numbers.
3082  *
3083  * An invalid input word will be converted to 0 by my_atol() which results in
3084  * a zero byte, which terminates the string.  Therefore, any invalid word
3085  * in the input implicitly ends the return value.  You should not depend on
3086  * this behavior because in the future invalid words may be skipped rather than
3087  * converted to nul bytes.
3088  *
3089  * Example:
3090  * $chr(104 105 33) returns "hi!"
3091  */
BUILT_IN_FUNCTION(function_chr,word)3092 BUILT_IN_FUNCTION(function_chr, word)
3093 {
3094 	char *	aboo = NULL;
3095 	char *	ack;
3096 	char *	blah;
3097 	size_t	bytes = 0;
3098 	size_t	cnt;
3099 	char 	*s, *x;
3100 	unsigned char 	utf8str[8];
3101 	int	code_point;
3102 
3103 	cnt = count_words(word, DWORD_DWORDS, "\"");
3104 	ack = aboo = new_malloc(cnt * 6 + 7);
3105 
3106 	while ((blah = next_func_arg(word, &word)))
3107 	{
3108 		if (blah[0] == 'U' && blah[1] == '+')
3109 		{
3110 			blah += 2;
3111 			code_point = strtol(blah, &x, 16);
3112 
3113 			/* If it's invalid, skip it */
3114 			if (blah == x)
3115 				continue;
3116 
3117 			/* Otherwise, convert to utf8 */
3118 			ucs_to_utf8(code_point, utf8str, sizeof(utf8str));
3119 			for (x = utf8str; *x; x++)
3120 			{
3121 				*ack++ = *x;
3122 				bytes++;
3123 			}
3124 		}
3125 		else
3126 		{
3127 			*ack++ = (char)my_atol(blah);
3128 			bytes++;
3129 		}
3130 
3131 		if (bytes >= (cnt * 6))
3132 			break;
3133 	}
3134 
3135 	*ack = 0;
3136 	RETURN_MSTR(aboo);
3137 }
3138 
3139 /*
3140  * This function converts an input word list into a string, where each word
3141  * of the input string must be a cardinal number in base 10 (0-255) or base 16
3142  * (0x00-0xFF -- including the leading "0x"!).  The output string is the CTCP
3143  * ENQUOTED  sequence of bytes represented by the catenation of each of these
3144  * cardinal numbers.
3145  *
3146  * An invalid input word will be converted to 0 by my_atol() which results in
3147  * a zero byte.  You should not depend on this behavior because in the future
3148  * invalid words may be skipped rather than converted to nul bytes.
3149  *
3150  * Example:
3151  * $chr(104 105 33 0 134 104 105 33 0) returns "hi\0\\hi!\0"
3152  *	because the string undergoes CTCP QUOTING before being returned.
3153  *
3154  * XXX This is a clone of function_chr() except for the transformation
3155  * at the end.  How bogus.
3156  */
BUILT_IN_FUNCTION(function_chrq,word)3157 BUILT_IN_FUNCTION(function_chrq, word)
3158 {
3159 	char *	aboo = NULL;
3160 	char *	ack;
3161 	char *	blah;
3162 	size_t	bytes = 0;
3163 	size_t	cnt;
3164 	char 	*s, *x;
3165 	unsigned char 	utf8str[8];
3166 	int	code_point;
3167 	char	*ret;
3168 
3169 	cnt = count_words(word, DWORD_DWORDS, "\"");
3170 	ack = aboo = new_malloc(cnt * 6 + 7);
3171 
3172 	while ((blah = next_func_arg(word, &word)))
3173 	{
3174 		if (blah[0] == 'U' && blah[1] == '+')
3175 		{
3176 			blah += 2;
3177 			code_point = strtol(blah, &x, 16);
3178 
3179 			/* If it's invalid, skip it */
3180 			if (blah == x)
3181 				continue;
3182 
3183 			/*
3184 			 * As a SPECIAL CASE code point 0 gets copied.
3185 			 */
3186 			if (code_point == 0)
3187 			{
3188 				*ack++ = 0;
3189 				bytes++;
3190 			}
3191 
3192 			/* Otherwise, convert to utf8 */
3193 			else
3194 			{
3195 			    ucs_to_utf8(code_point, utf8str, sizeof(utf8str));
3196 			    for (x = utf8str; *x; x++)
3197 			    {
3198 				*ack++ = *x;
3199 				bytes++;
3200 			    }
3201 			}
3202 		}
3203 		else
3204 		{
3205 			*ack++ = (char)my_atol(blah);
3206 			bytes++;
3207 		}
3208 
3209 		if (bytes >= (cnt * 6))
3210 			break;
3211 	}
3212 
3213 	*ack = 0;
3214 	ret = transform_string_dyn("+CTCP", aboo, bytes, NULL);
3215 	new_free(&aboo);
3216 
3217 	RETURN_MSTR(ret);
3218 }
3219 
3220 /*
3221  * This function converts an input string into a word list, where each byte
3222  * of the input string is converted to a cardinal number in base 10 (0-255)
3223  * and the return value is a word list of such cardinal numbers.
3224  *
3225  * Example:
3226  * $ascii(hi!) returns "104 105 33"
3227  */
BUILT_IN_FUNCTION(function_ascii,word)3228 BUILT_IN_FUNCTION(function_ascii, word)
3229 {
3230 	char *aboo = NULL;
3231 	size_t  rvclue=0;
3232 
3233 	if (!word || !*word)
3234 		RETURN_EMPTY;
3235 
3236 	for (; *word; ++word)
3237 		malloc_strcat_wordlist_c(&aboo, space, ltoa((long)(unsigned char)*word), &rvclue);
3238 
3239 	return aboo;
3240 }
3241 
3242 /*
3243  * This function converts an input string into a word list, where each
3244  * unicode code point of the input string is converted to a unicode code
3245  * point description ("U+xxxx") which you could pass back to $chr(),
3246  * and the return value is a word list of such descriptions.
3247  * Invalid code points are discarded.
3248  *
3249  * Example:
3250  * $unicode(hi!) returns "U+0068 U+0069 U+0021"
3251  */
BUILT_IN_FUNCTION(function_unicode,word)3252 BUILT_IN_FUNCTION(function_unicode, word)
3253 {
3254 	char *	aboo = NULL;
3255 	size_t  rvclue=0;
3256 	const unsigned char *s, *x;
3257 	int	code_point;
3258 	char	result[8];
3259 
3260 	if (!word || !*word)
3261 		RETURN_EMPTY;
3262 
3263 	s = word;
3264 	while ((code_point = next_code_point(&s, 0)))
3265 	{
3266 		/* Skip invalid bytes/sequences */
3267 		if (code_point == -1)
3268 			continue;
3269 		snprintf(result, sizeof(result), "U+%04X", code_point);
3270 		malloc_strcat_wordlist_c(&aboo, space, result, &rvclue);
3271 	}
3272 
3273 	return aboo;
3274 }
3275 
3276 /*
3277  * This function converts a CTCP ENCODED input string into a word list, where
3278  * each byte of the input string is converted to a cardinal number in base 10
3279  * (0-255) and the return value is a word list of such cardinal numbers.
3280  *
3281  * Example:
3282  * $ascii(hi!) returns "104 105 33"   of course
3283  * $ascii(hi!\0\\hi!\0)  returns "104 105 33 0 134 104 105 33 0"
3284  *	because the string undergoes CTCP DEQUOTING first.
3285  *	-- Be careful: the CTCP QUOTE character (\) is the same as ircII's
3286  *	   quote character, so you can quickly get into quoting hell here.
3287  */
BUILT_IN_FUNCTION(function_asciiq,word)3288 BUILT_IN_FUNCTION(function_asciiq, word)
3289 {
3290 	char	*aboo = NULL, *free_it;
3291 	size_t	rvclue=0;
3292 
3293 	if (!word || !*word)
3294 		RETURN_EMPTY;
3295 
3296 	/* The CTCP enquoting is intentional... */
3297 	free_it = word = transform_string_dyn("-CTCP", word, 0, NULL);
3298 	RETURN_IF_EMPTY(word);
3299 
3300 	for (; word && *word; word++)
3301 	{
3302 		malloc_strcat_wordlist_c(&aboo, space,
3303 				ltoa((long)(unsigned char)*word), &rvclue);
3304 	}
3305 
3306 	new_free(&free_it);
3307 	return aboo;
3308 }
3309 
BUILT_IN_FUNCTION(function_which,word)3310 BUILT_IN_FUNCTION(function_which, word)
3311 {
3312 	char *file1;
3313 	Filename result;
3314 	const char *path;
3315 
3316 	GET_DWORD_ARG(file1, word);
3317 	if (word && *word)
3318 		path = word;
3319 	else
3320 		path = get_string_var(LOAD_PATH_VAR);
3321 
3322 	if (path_search(file1, path, result))
3323 		RETURN_EMPTY;
3324 
3325 	RETURN_STR(result);
3326 }
3327 
3328 
BUILT_IN_FUNCTION(function_isalpha,words)3329 BUILT_IN_FUNCTION(function_isalpha, words)
3330 {
3331 	if (((*words >= 'a') && (*words <= 'z')) ||
3332 	    ((*words >= 'A') && (*words <= 'Z')))
3333 		RETURN_INT(1);
3334 	else
3335 		RETURN_INT(0);
3336 }
3337 
BUILT_IN_FUNCTION(function_isdigit,words)3338 BUILT_IN_FUNCTION(function_isdigit, words)
3339 {
3340 	if (((*words >= '0') && (*words <= '9')) ||
3341 	    ((*words == '-') && ((*(words+1) >= '0') && (*(words+1) <= '9'))))
3342 		RETURN_INT(1);
3343 	else
3344 		RETURN_INT(0);
3345 }
3346 
BUILT_IN_FUNCTION(function_open,words)3347 BUILT_IN_FUNCTION(function_open, words)
3348 {
3349 	char *filename;
3350 	GET_DWORD_ARG(filename, words);
3351 	GET_FUNC_ARG(words, words); /* clobbers remaining args */
3352 
3353 	if (!words || !*words)
3354 		RETURN_EMPTY;
3355 	else if (!my_stricmp(words, "R"))
3356 		RETURN_INT(open_file_for_read(filename));
3357 	else if (!my_stricmp(words, "W"))
3358 		RETURN_INT(open_file_for_write(filename, "a"));
3359 	else
3360 		RETURN_INT(open_file_for_write(filename, lower(words)));
3361 }
3362 
BUILT_IN_FUNCTION(function_close,words)3363 BUILT_IN_FUNCTION(function_close, words)
3364 {
3365 	RETURN_IF_EMPTY(words);
3366 	RETURN_INT(file_close(my_atol(next_func_arg(words, &words))));
3367 }
3368 
BUILT_IN_FUNCTION(function_write,words)3369 BUILT_IN_FUNCTION(function_write, words)
3370 {
3371 	char *	fdc;
3372 	char 	target[1024];
3373 	int	retval;
3374 
3375 	GET_FUNC_ARG(fdc, words);
3376 	if (is_number(fdc) ||
3377 		( (*fdc == 'w' || *fdc == 'W' ||
3378 		   *fdc == 'l' || *fdc == 'L' ) &&
3379 			is_number(fdc + 1)))
3380 	{
3381 		snprintf(target, sizeof target, "@%s", fdc);
3382 		retval = target_file_write(target, words);
3383 		RETURN_INT(retval);
3384 	}
3385 	RETURN_EMPTY;
3386 }
3387 
BUILT_IN_FUNCTION(function_writeb,words)3388 BUILT_IN_FUNCTION(function_writeb, words)
3389 {
3390 	int	retval = 0;
3391 	char *	fdc;
3392 
3393 	GET_FUNC_ARG(fdc, words);
3394 
3395 	/* We don't use target_file_write() for biary data. */
3396 
3397 	if (is_number(fdc))
3398 		retval = file_writeb(0, my_atol(fdc), words);
3399 	else if ((*fdc == 'w' || *fdc == 'W') && is_number(fdc + 1))
3400 		retval = file_writeb(1, my_atol(fdc + 1), words);
3401 	else if ((*fdc == 'l' || *fdc == 'L') && is_number(fdc + 1))
3402 		retval = file_writeb(2, my_atol(fdc + 1), words);
3403 	else
3404 		RETURN_EMPTY;
3405 
3406 	RETURN_INT(retval);
3407 }
3408 
BUILT_IN_FUNCTION(function_read,words)3409 BUILT_IN_FUNCTION(function_read, words)
3410 {
3411 	char *fdc = NULL, *numb = NULL;
3412 
3413 	GET_FUNC_ARG(fdc, words);
3414 	if (words && *words)
3415 		GET_FUNC_ARG(numb, words);
3416 
3417 	if (numb)
3418 		return file_readb(my_atol(fdc), my_atol(numb));
3419 	else
3420 		return file_read(my_atol(fdc));
3421 }
3422 
BUILT_IN_FUNCTION(function_seek,words)3423 BUILT_IN_FUNCTION(function_seek, words)
3424 {
3425 	int	fdc;
3426 	off_t	numb;
3427 	char *	whence = NULL;
3428 
3429 	GET_INT_ARG(fdc, words);
3430 	GET_INT_ARG(numb, words);
3431 	GET_FUNC_ARG(whence, words);
3432 
3433 	RETURN_INT(file_seek(fdc, numb, whence));
3434 }
3435 
BUILT_IN_FUNCTION(function_eof,words)3436 BUILT_IN_FUNCTION(function_eof, words)
3437 {
3438 	RETURN_IF_EMPTY(words);
3439 	RETURN_INT(file_eof(my_atol(next_func_arg(words, &words))));
3440 }
3441 
BUILT_IN_FUNCTION(function_error,words)3442 BUILT_IN_FUNCTION(function_error, words)
3443 {
3444 	RETURN_IF_EMPTY(words);
3445 	RETURN_INT(file_error(my_atol(next_func_arg(words, &words))));
3446 }
3447 
BUILT_IN_FUNCTION(function_skip,words)3448 BUILT_IN_FUNCTION(function_skip, words)
3449 {
3450 	int arg1, arg2 = 1;
3451 	GET_INT_ARG(arg1, words);
3452 	if (words && *words)
3453 		GET_INT_ARG(arg2, words);
3454 	RETURN_INT(file_skip(arg1, arg2));
3455 }
3456 
BUILT_IN_FUNCTION(function_tell,words)3457 BUILT_IN_FUNCTION(function_tell, words)
3458 {
3459 	RETURN_IF_EMPTY(words);
3460 	RETURN_INT(file_tell(my_atol(next_func_arg(words, &words))));
3461 }
3462 
BUILT_IN_FUNCTION(function_isfilevalid,words)3463 BUILT_IN_FUNCTION(function_isfilevalid, words)
3464 {
3465 	RETURN_IF_EMPTY(words);
3466 	RETURN_INT(file_valid(my_atol(next_func_arg(words, &words))));
3467 }
3468 
BUILT_IN_FUNCTION(function_rewind,words)3469 BUILT_IN_FUNCTION(function_rewind, words)
3470 {
3471 	RETURN_IF_EMPTY(words);
3472 	RETURN_INT(file_rewind(my_atol(next_func_arg(words, &words))));
3473 }
3474 
BUILT_IN_FUNCTION(function_ftruncate,words)3475 BUILT_IN_FUNCTION(function_ftruncate, words)
3476 {
3477 	off_t	length;
3478 	char	*ret = NULL;
3479 
3480 	GET_INT_ARG(length, words);
3481 	if (truncate(words, length))
3482 		ret = strerror(errno);
3483 	RETURN_STR(ret);
3484 }
3485 
BUILT_IN_FUNCTION(function_iptoname,words)3486 BUILT_IN_FUNCTION(function_iptoname, words)
3487 {
3488 	char	ret[256];
3489 
3490 	*ret = 0;
3491 	inet_ptohn(AF_UNSPEC, words, ret, sizeof(ret));
3492 	RETURN_STR(ret);		/* Dont put function call in macro! */
3493 }
3494 
BUILT_IN_FUNCTION(function_nametoip,words)3495 BUILT_IN_FUNCTION(function_nametoip, words)
3496 {
3497 	char	ret[256];
3498 
3499 	*ret = 0;
3500 	inet_hntop(AF_INET, words, ret, sizeof(ret));
3501 	RETURN_STR(ret);		/* Dont put function call in macro! */
3502 }
3503 
BUILT_IN_FUNCTION(function_convert,words)3504 BUILT_IN_FUNCTION(function_convert, words)
3505 {
3506 	char	ret[256];
3507 
3508 	*ret = 0;
3509 	one_to_another(AF_INET, words, ret, sizeof(ret));
3510 	RETURN_STR(ret);		/* Dont put function call in macro! */
3511 }
3512 
3513 /*
3514  * $tr([delim][chars-out][delim][chars-in][delim][text])
3515  *
3516  * For the string [text], replace any instances of any character
3517  * in [chars-out] with the corresponding character in [chars-in].
3518  *
3519  * Example:
3520  *	$tr(/abc/def/happy happy job job/)
3521  * Means apply these transforms:
3522  *	a -> d
3523  *	b -> e
3524  *	c -> f
3525  * So the result is:
3526  *	hdppy hdppy joe joe
3527  *
3528  * This is *NOT* case insensitive, like you might expect.  Oh well!
3529  */
3530 
BUILT_IN_FUNCTION(function_translate,input)3531 BUILT_IN_FUNCTION(function_translate, input)
3532 {
3533 	int		delim, codepoint;
3534 	unsigned char 	*s, *p;
3535 	unsigned char	*chars_in, *chars_out, *text;
3536 	char		*retval, *r;
3537 	int		char_out, char_in, final_char_in;
3538 	unsigned char 	utf8str[16];
3539 
3540 	RETURN_IF_EMPTY(input);
3541 
3542 	/*
3543 	 * The first CP we see is the delimiter.
3544 	 * By convention, this is a slash ('/'), but it doesn't have to be.
3545 	 */
3546 	s = input;
3547 	while ((delim = next_code_point(CUC_PP &s, 0)) == -1)
3548 		s++;
3549 
3550 	/*
3551 	 * The chars to swap out begin after the delim.
3552 	 */
3553 	chars_out = s;
3554 
3555 	/*
3556 	 * Now we look for the next delim...
3557 	 */
3558 	for (;;)
3559 	{
3560 		p = s;
3561 		while ((codepoint = next_code_point(CUC_PP &s, 0)) == -1)
3562 			s++;
3563 		if (codepoint == 0)
3564 			RETURN_EMPTY;
3565 
3566 		/*
3567 		 * Since the delim might be multiple bytes,
3568 		 * we keep a pointer to the start of each CP.
3569 		 * If we find the delim, we null out the first
3570 		 * byte.
3571 		 */
3572 		if (codepoint == delim)
3573 		{
3574 			*p = 0;
3575 			break;
3576 		}
3577 	}
3578 
3579 	/*
3580 	 * The chars to swap in begin after the delim.
3581 	 */
3582 	chars_in = s;
3583 
3584 	/*
3585 	 * Now we look for the final delim...
3586 	 */
3587 	for (;;)
3588 	{
3589 		p = s;
3590 		while ((codepoint = next_code_point(CUC_PP &s, 0)) == -1)
3591 			s++;
3592 		if (codepoint == 0)
3593 			RETURN_EMPTY;
3594 
3595 		/*
3596 		 * Since the delim might be multiple bytes,
3597 		 * we keep a pointer to the start of each CP.
3598 		 * If we find the delim, we null out the first
3599 		 * byte.
3600 		 */
3601 		if (codepoint == delim)
3602 		{
3603 			*p = 0;
3604 			break;
3605 		}
3606 	}
3607 
3608 	text = s;
3609 
3610 	/*
3611 	 * The worst case is replacing every byte in 'text' with a
3612 	 * 6 byte utf8 code point.
3613 	 */
3614 	r = retval = new_malloc(strlen(text) * 6 + 6);
3615 
3616 	/*
3617 	 * Now we walk every code point in text, deciding what to do.
3618 	 */
3619 	for (;;)
3620 	{
3621 		while ((codepoint = next_code_point(CUC_PP &text, 0)) == -1)
3622 			text++;
3623 
3624 		/* The only way out is when we get the final nul. */
3625 		if (codepoint == 0)
3626 		{
3627 			*r = 0;
3628 			RETURN_MSTR(retval);
3629 		}
3630 
3631 		/*
3632 		 * For each byte, determine whether it is in the 'chars_out'
3633 		 * string.  If it is, replace it with the corresponding
3634 		 * byte in 'chars_in'
3635 		 *
3636 		 * XXX IMPORTANT!  We depend strongly on next_code_point()
3637 		 * refusing to walk off the end of a string here to handle
3638 		 * cases where the chars_out is shorter than chars_in.
3639 		 * The effect is that the "replaced" character is a nul,
3640 		 * which removes the character.  If next_code_point() ever
3641 		 * stops doing that, this will need to be rewritten.
3642 		 */
3643 		s = chars_out;
3644 		p = chars_in;
3645 		final_char_in = 0;
3646 		for (;;)
3647 		{
3648 			char_out = next_code_point(CUC_PP &s, 1);
3649 			char_in = next_code_point(CUC_PP &p, 1);
3650 
3651 			/*
3652 			 * The historical (and defined) behavior is that
3653 			 * every character in 'char_out' maps to a character
3654 			 * in 'char_in' and whenever 'char_in' is shorter
3655 			 * than 'char_out', it shall be extended with its
3656 			 * final character.
3657 			 *
3658 			 * For example:
3659 			 *	$tr(/abcde/xyz/tasty cakes/)
3660 			 * behaves as though it were:
3661 			 *	$tr(/abcde/xyzzz/tasty cakes/)
3662 			 */
3663 			if (char_in == 0)
3664 				char_in = final_char_in;
3665 			else
3666 				final_char_in = char_in;
3667 
3668 			/*
3669 			 * This CP was not in 'char_out' -- copy it.
3670 			 */
3671 			if (char_out == 0)
3672 				break;
3673 			/*
3674 			 * The CP was found -- change it to the right one.
3675 			 */
3676 			else if (codepoint == char_out)
3677 			{
3678 				codepoint = char_in;
3679 				break;
3680 			}
3681 		}
3682 
3683 		/*
3684 		 * Convert the code point back to a string and copy it in.
3685 		 */
3686 		ucs_to_utf8(codepoint, utf8str, sizeof(utf8str));
3687 		s = utf8str;
3688 		while (*s)
3689 			*r++ = *s++;
3690 	}
3691 }
3692 
BUILT_IN_FUNCTION(function_server_version,word)3693 BUILT_IN_FUNCTION(function_server_version, word)
3694 {
3695 	RETURN_STR("2.8");
3696 }
3697 
BUILT_IN_FUNCTION(function_unlink,words)3698 BUILT_IN_FUNCTION(function_unlink, words)
3699 {
3700 	Filename expanded;
3701 	int 	failure = 0;
3702 
3703 	while (words && *words)
3704 	{
3705 		char *fn = new_next_arg(words, &words);
3706 		if (!fn || !*fn)
3707 			fn = words, words = NULL;
3708 		if (normalize_filename(fn, expanded))
3709 			failure++;
3710 		else if (unlink(expanded))
3711 			failure++;
3712 	}
3713 
3714 	RETURN_INT(failure);
3715 }
3716 
BUILT_IN_FUNCTION(function_rename,words)3717 BUILT_IN_FUNCTION(function_rename, words)
3718 {
3719 	char *	filename1,
3720 	     *	filename2;
3721 	Filename expanded1;
3722 	Filename expanded2;
3723 
3724 	GET_DWORD_ARG(filename1, words)
3725 	if (normalize_filename(filename1, expanded1))
3726 		RETURN_INT(-1);
3727 
3728 	GET_DWORD_ARG(filename2, words)
3729 	if (normalize_filename(filename2, expanded2))
3730 		RETURN_INT(-1);
3731 
3732 	RETURN_INT(rename(expanded1, expanded2));
3733 }
3734 
BUILT_IN_FUNCTION(function_rmdir,words)3735 BUILT_IN_FUNCTION(function_rmdir, words)
3736 {
3737 	Filename expanded;
3738 	int 	failure = 0;
3739 
3740 	while (words && *words)
3741 	{
3742 		char *fn = new_next_arg(words, &words);
3743 		if (!fn || !*fn)
3744 			fn = words, words = NULL;
3745 		if (normalize_filename(fn, expanded))
3746 			failure++;
3747 		else if (rmdir(expanded))
3748 			failure++;
3749 	}
3750 
3751 	RETURN_INT(failure);
3752 }
3753 
BUILT_IN_FUNCTION(function_truncate,words)3754 BUILT_IN_FUNCTION(function_truncate, words)
3755 {
3756 	int		num = 0;
3757 	double		value = 0;
3758 	char		buffer[BIG_BUFFER_SIZE],
3759 			format[1024];
3760 
3761 	GET_INT_ARG(num, words);
3762 	GET_FLOAT_ARG(value, words);
3763 
3764 	if (num < 0)
3765 	{
3766 		float foo;
3767 		int end;
3768 
3769 		snprintf(format, sizeof format, "%%.%de", -num-1);
3770 		snprintf(buffer, sizeof buffer, format, value);
3771 		foo = atof(buffer);
3772 		snprintf(buffer, sizeof buffer, "%f", foo);
3773 		end = strlen(buffer) - 1;
3774 		if (end == 0)
3775 			RETURN_EMPTY;
3776 		while (buffer[end] == '0')
3777 			end--;
3778 		if (buffer[end] == '.')
3779 			end--;
3780 		buffer[end+1] = 0;
3781 	}
3782 	else if (num >= 0)
3783 	{
3784 		snprintf(format, sizeof format, "%%10.%dlf", num);
3785 		snprintf(buffer, sizeof buffer, format, value);
3786 	}
3787 	else
3788 		RETURN_EMPTY;
3789 
3790 	while (*buffer && isspace(*buffer))
3791 		ov_strcpy(buffer, buffer+1);
3792 
3793 	RETURN_STR(buffer);
3794 }
3795 
3796 
3797 /*
3798  * Apprantly, this was lifted from a CS client.  I reserve the right
3799  * to replace this code in future versions.
3800  */
3801 /*
3802 	I added this little function so that I can have stuff formatted
3803 	into days, hours, minutes, seconds; but with d, h, m, s abreviations.
3804 		-Taner
3805 */
3806 
BUILT_IN_FUNCTION(function_tdiff2,input)3807 BUILT_IN_FUNCTION(function_tdiff2, input)
3808 {
3809 	time_t	ltime;
3810 	time_t	days,
3811 		hours,
3812 		minutes,
3813 		seconds;
3814 	char	tmp[80];
3815 	char	*tstr;
3816 	size_t	size;
3817 
3818 	GET_INT_ARG(ltime, input);
3819 
3820 	seconds = ltime % 60;
3821 	ltime = (ltime - seconds) / 60;
3822 	minutes = ltime%60;
3823 	ltime = (ltime - minutes) / 60;
3824 	hours = ltime % 24;
3825 	days = (ltime - hours) / 24;
3826 	tstr = tmp;
3827 	size = sizeof tmp;
3828 
3829 	if (days)
3830 	{
3831 		snprintf(tstr, size, "%ldd ", (long)days);
3832 		size -= strlen(tstr);
3833 		tstr += strlen(tstr);
3834 	}
3835 	if (hours)
3836 	{
3837 		snprintf(tstr, size, "%ldh ", (long)hours);
3838 		size -= strlen(tstr);
3839 		tstr += strlen(tstr);
3840 	}
3841 	if (minutes)
3842 	{
3843 		snprintf(tstr, size, "%ldm ", (long)minutes);
3844 		size -= strlen(tstr);
3845 		tstr += strlen(tstr);
3846 	}
3847 	if (seconds || (!days && !hours && !minutes))
3848 		snprintf(tstr, size, "%lds", (long)seconds);
3849 	else
3850 		*--tstr = 0;	/* chop off that space! */
3851 
3852 	RETURN_STR(tmp);
3853 }
3854 
3855 /*
3856  * The idea came from a CS client.  I wrote this from scratch, though.
3857  */
BUILT_IN_FUNCTION(function_utime,input)3858 BUILT_IN_FUNCTION(function_utime, input)
3859 {
3860 	Timeval tp;
3861 
3862 	get_time(&tp);
3863 	return malloc_sprintf(NULL, INTMAX_FORMAT" %ld", (intmax_t)tp.tv_sec,
3864 							 (long)tp.tv_usec);
3865 }
3866 
3867 
3868 /*
3869  * This inverts any ansi sequence present in the string
3870  * from: Scott H Kilau <kilau@prairie.NoDak.edu>
3871  */
BUILT_IN_FUNCTION(function_stripansi,input)3872 BUILT_IN_FUNCTION(function_stripansi, input)
3873 {
3874 	char	*cp;
3875 
3876 	for (cp = input; *cp; cp++)
3877 		/* Strip anything from 14 - 33, except ^O and ^V */
3878 		if (*cp < 32 && *cp > 13)
3879 			if (*cp != 15 && *cp != 22)
3880 				*cp = (*cp & 127) | 64;
3881 
3882 	RETURN_STR(input);
3883 }
3884 
BUILT_IN_FUNCTION(function_numwords,input)3885 BUILT_IN_FUNCTION(function_numwords, input)
3886 {
3887 	RETURN_INT(count_words(input, DWORD_DWORDS, "\""));
3888 }
3889 
BUILT_IN_FUNCTION(function_strlen,input)3890 BUILT_IN_FUNCTION(function_strlen, input)
3891 {
3892 	RETURN_INT(quick_code_point_count(input));
3893 }
3894 
BUILT_IN_FUNCTION(function_aliasctl,input)3895 BUILT_IN_FUNCTION(function_aliasctl, input)
3896 {
3897 	return aliasctl(input);
3898 }
3899 
3900 
3901 
3902 /*
3903  * Next two contributed by Scott H Kilau (sheik), who for some reason doesnt
3904  * want to take credit for them. *shrug* >;-)
3905  *
3906  * Deciding not to be controversial, im keeping the original (contributed)
3907  * semantics of these two functions, which is to return 1 on success and
3908  * -1 on error.  If you dont like it, then tough. =)  I didnt write it, and
3909  * im not going to second guess any useful contributions.
3910  */
BUILT_IN_FUNCTION(function_fexist,words)3911 BUILT_IN_FUNCTION(function_fexist, words)
3912 {
3913         Filename expanded;
3914 	char	*filename;
3915 
3916 	if (!(filename = new_next_arg(words, &words)))
3917 		filename = words;
3918 
3919 	if (!filename || !*filename)
3920 		RETURN_INT(-1);
3921 
3922 	if (normalize_filename(filename, expanded))
3923 		RETURN_INT(-1);
3924 
3925 	if (access(expanded, R_OK) == -1)
3926 		RETURN_INT(-1);
3927 
3928 	RETURN_INT(1);
3929 }
3930 
BUILT_IN_FUNCTION(function_fsize,words)3931 BUILT_IN_FUNCTION(function_fsize, words)
3932 {
3933 	Filename expanded;
3934 	char *	filename;
3935 
3936 	if (!(filename = new_next_arg(words, &words)))
3937 		filename = words;
3938 
3939 	if (!filename || !*filename)
3940 		RETURN_INT(-1);
3941 
3942 	if (normalize_filename(filename, expanded))
3943 		RETURN_INT(-1);
3944 
3945 	RETURN_INT(file_size(expanded));
3946 }
3947 
3948 /*
3949  * Contributed by CrowMan
3950  * Updated by caf to allow selection of any hashing method supported by the
3951  * system.
3952  */
3953 /*
3954  * $crypt(password salt)
3955  *
3956  * This returns a password hash when given a salt and password.  The function
3957  * supports all hashing methods supported by the local C library, with the
3958  * hashing method selected by the format of the salt string.  See your local
3959  * crypt(3) man page for details.
3960  *
3961  * All systems (and older versions of EPIC) support the classic DES-based UNIX
3962  * hashing function, which is selected by a two-character salt in the range
3963  * [a-zA-Z0-9./].  This method returns a 13-char string and truncates the
3964  * supplied password to 8 characters.  It is also comically weak in the modern
3965  * era.
3966  *
3967  * Returns an empty string if one or both args missing, or if crypt() fails.
3968  *
3969  * Credits: Thanks to Strongbow for showing me how crypt() works.
3970  * Written by: CrowMan
3971  */
BUILT_IN_FUNCTION(function_crypt,words)3972 BUILT_IN_FUNCTION(function_crypt, words)
3973 {
3974 	/* Some systems use (char *) and some use (const char *) as arguments. */
3975 	extern char *crypt();
3976 	char *pass, *salt;
3977 	char *ret;
3978 
3979 	GET_DWORD_ARG(pass, words)
3980 	GET_DWORD_ARG(salt, words)
3981 	ret = crypt(pass, salt);
3982 	RETURN_STR(ret);		/* Dont call function in macro! */
3983 }
3984 
BUILT_IN_FUNCTION(function_info,words)3985 BUILT_IN_FUNCTION(function_info, words)
3986 {
3987 	char *which;
3988 	extern char *info_c_sum;
3989 
3990 	GET_FUNC_ARG(which, words);
3991 
3992 	     if (!my_strnicmp(which, "C", 1))
3993 		RETURN_STR(compile_info);
3994 	else if (!my_strnicmp(which, "O", 1))
3995 		RETURN_STR(compile_time_options);
3996 	else if (!my_strnicmp(which, "S", 1))
3997 		RETURN_STR(info_c_sum);
3998 	else if (!my_strnicmp(which, "W", 1))
3999 		RETURN_INT(1);
4000 	else if (!my_strnicmp(which, "M", 1))
4001 	{
4002 		if (get_int_var(OLD_MATH_PARSER_VAR))
4003 			RETURN_INT(0);
4004 		RETURN_INT(1);
4005 	}
4006 	else if (!my_strnicmp(which, "V", 1))
4007 		RETURN_STR(useful_info);
4008 	else if (!my_strnicmp(which, "R", 1))
4009 		RETURN_STR(ridiculous_version_name);
4010 	else if (!my_strnicmp(which, "I", 1))
4011 		RETURN_INT(commit_id);
4012 	else
4013 		RETURN_EMPTY;
4014 	/* more to be added as neccesary */
4015 }
4016 
BUILT_IN_FUNCTION(function_geom,words)4017 BUILT_IN_FUNCTION(function_geom, words)
4018 {
4019         const char *refnum;
4020         int  col, li;
4021 
4022         if (!words || !*words)
4023                 refnum = zero;
4024         else
4025                 GET_FUNC_ARG(refnum, words);
4026 
4027         if (get_geom_by_winref(refnum, &col, &li))
4028                 RETURN_EMPTY;
4029 
4030         return malloc_sprintf(NULL, "%d %d", col, li);
4031 }
4032 
4033 /*
4034  * This is basically the opposite of $strip().
4035  * The only difference is the "found" versus "!found"
4036  * XXX What a shame to have two functions differing by only one byte.
4037  */
BUILT_IN_FUNCTION(function_pass,input)4038 BUILT_IN_FUNCTION(function_pass, input)
4039 {
4040 	char *	search;
4041 	const unsigned char 	*s, *p;
4042 	int	c, d;
4043 	int	found;
4044 	char *	result, *r;
4045 
4046 	GET_DWORD_ARG(search, input);
4047 	RETURN_IF_EMPTY(input);
4048 
4049 	r = result = (char *)new_malloc(strlen(input) + 1);
4050 
4051 	p = input;
4052 	while ((c = next_code_point(&p, 1)))
4053 	{
4054 		found = 0;
4055 		s = search;
4056 		while ((d = next_code_point(&s, 1)))
4057 		{
4058 			if (c == d)
4059 			{
4060 				found = 1;
4061 				break;
4062 			}
4063 		}
4064 		if (found)
4065 		{
4066 			unsigned char utf8str[16];
4067 			unsigned char *x;
4068 
4069 			ucs_to_utf8(c, utf8str, sizeof(utf8str));
4070 			for (x = utf8str; *x; x++)
4071 				*r++ = *x;
4072 		}
4073 	}
4074 	*r = 0;
4075 	return result;		/* DONT USE RETURN_STR HERE! */
4076 }
4077 
BUILT_IN_FUNCTION(function_repeat,words)4078 BUILT_IN_FUNCTION(function_repeat, words)
4079 {
4080 	int	num;
4081 	size_t	size;
4082 	char *	retval = NULL;
4083 	size_t	clue;
4084 
4085 	/*
4086 	 * XXX - This is a brutal, unmerciful, and heinous offense
4087 	 * against all that is good and right in the world.  However,
4088 	 * backwards compatability requires that $repeat(10  ) work,
4089 	 * even though that violates standard practice of word extraction.
4090 	 *
4091 	 * This replaces a GET_INT_ARG(num, words)
4092 	 */
4093 	num = strtoul(words, &words, 10);
4094 	if (words && *words)
4095 		words++;
4096 
4097 	if (num < 1)
4098 		RETURN_EMPTY;
4099 
4100 	if (!words)
4101 		RETURN_EMPTY;
4102 
4103 	size = strlen(words) * num + 1;
4104 
4105 	/*
4106 	 * Don't allow the return value to be > 1MB to avoid
4107 	 * "out of memory failure" panic
4108 	 */
4109 	if (size > 1000000)
4110 	{
4111 		num = 1000000 / strlen(words);
4112 		size = strlen(words) * num + 1;
4113 	}
4114 
4115 	retval = (char *)new_malloc(size);
4116 	*retval = 0;
4117 	clue = 0;
4118 
4119 	for (; num > 0; num--)
4120 		strlcat_c(retval, words, size, &clue);
4121 
4122 	return retval;
4123 }
4124 
BUILT_IN_FUNCTION(function_epic,words)4125 BUILT_IN_FUNCTION(function_epic, words)
4126 {
4127 	RETURN_INT(1);
4128 }
4129 
4130 
BUILT_IN_FUNCTION(function_umode,words)4131 BUILT_IN_FUNCTION(function_umode, words)
4132 {
4133 	int   servnum;
4134 	const char *ret;
4135 
4136 	if (words && *words)
4137 		GET_INT_ARG(servnum, words)
4138 	else
4139 		servnum = from_server;
4140 
4141 	ret = get_umode(servnum);
4142 	RETURN_STR(ret);		/* Dont pass function call to macro! */
4143 }
4144 
sort_it(const void * val1,const void * val2)4145 static int sort_it (const void *val1, const void *val2)
4146 {
4147 	return my_stricmp(*(const char * const *)val1, *(const char * const *)val2);
4148 }
4149 
BUILT_IN_FUNCTION(function_sort,words)4150 BUILT_IN_FUNCTION(function_sort, words)
4151 {
4152 	int 	wordc;
4153 	char **wordl;
4154 	char	*retval;
4155 
4156 	if (!(wordc = splitw(words, &wordl, DWORD_DWORDS)))
4157 		RETURN_EMPTY;
4158 
4159 	qsort((void *)wordl, wordc, sizeof(char *), sort_it);
4160 	retval = unsplitw(&wordl, wordc, DWORD_DWORDS);
4161 	RETURN_MSTR(retval);
4162 }
4163 
num_sort_it(const void * val1,const void * val2)4164 static int num_sort_it (const void *val1, const void *val2)
4165 {
4166 	const char *oneptr = *(const char * const *)val1;
4167 	const char *twoptr = *(const char * const *)val2;
4168 	char *oneptr_result;
4169 	char *twoptr_result;
4170 	intmax_t v1, v2;
4171 
4172 	while (*oneptr && *twoptr)
4173 	{
4174 		while (*oneptr && *twoptr && !(my_isdigit(oneptr)) && !(my_isdigit(twoptr)))
4175 		{
4176 			char cone = tolower(*oneptr);
4177 			char ctwo = tolower(*twoptr);
4178 			if (cone != ctwo)
4179 				return (cone - ctwo);
4180 			oneptr++, twoptr++;
4181 		}
4182 
4183 		if (!*oneptr || !*twoptr)
4184 			break;
4185 
4186 		/* These casts discard 'const', but do i care? */
4187 		v1 = strtoimax(oneptr, &oneptr_result, 0);
4188 		v2 = strtoimax(twoptr, &twoptr_result, 0);
4189 		if (v1 != v2)
4190 			return v1 - v2;
4191 		oneptr = oneptr_result;
4192 		twoptr = twoptr_result;
4193 	}
4194 	return (*oneptr - *twoptr);
4195 }
4196 
BUILT_IN_FUNCTION(function_numsort,words)4197 BUILT_IN_FUNCTION(function_numsort, words)
4198 {
4199 	int wordc;
4200 	char **wordl;
4201 	char *retval;
4202 
4203 	if (!(wordc = splitw(words, &wordl, DWORD_DWORDS)))
4204 		RETURN_EMPTY;
4205 	qsort((void *)wordl, wordc, sizeof(char *), num_sort_it);
4206 	retval = unsplitw(&wordl, wordc, DWORD_DWORDS);
4207 	RETURN_MSTR(retval);
4208 }
4209 
4210 
BUILT_IN_FUNCTION(function_notify,words)4211 BUILT_IN_FUNCTION(function_notify, words)
4212 {
4213 	int showon = -1, showserver = from_server;
4214 	char *firstw;
4215 
4216 	while (words && *words)
4217 	{
4218 		firstw = next_func_arg(words, &words);
4219 		if (!my_strnicmp(firstw, "on", 2))
4220 		{
4221 			showon = 1;
4222 			continue;
4223 		}
4224 		if (!my_strnicmp(firstw, "off", 3))
4225 		{
4226 			showon = 0;
4227 			continue;
4228 		}
4229 		if (!my_strnicmp(firstw, "serv", 4))
4230 		{
4231 			GET_INT_ARG(showserver, words);
4232 		}
4233 	}
4234 
4235 	/* dont use RETURN_STR() here. */
4236 	return get_notify_nicks(showserver, showon);
4237 }
4238 
4239 #ifdef NEED_GLOB
4240 #define glob bsd_glob
4241 #define globfree bsd_globfree
4242 #endif
4243 #ifndef GLOB_INSENSITIVE
4244 #define GLOB_INSENSITIVE 0
4245 #endif
4246 
BUILT_IN_FUNCTION(function_glob,word)4247 BUILT_IN_FUNCTION(function_glob, word)
4248 {
4249 	char 	*path;
4250 	Filename path2;
4251 	char	*retval = NULL;
4252 	int 	numglobs, i;
4253 	size_t	rvclue=0;
4254 	glob_t 	globbers;
4255 
4256 	memset(&globbers, 0, sizeof(glob_t));
4257 	while (word && *word)
4258 	{
4259 		char	*freepath = NULL;
4260 
4261 		GET_DWORD_ARG(path, word);
4262 		if (!path || !*path)
4263 			path = word, word = NULL;
4264 		else
4265 		{
4266 		   /* The CTCP enquoting is intentional, probably
4267 		    * to do de-\-ing */
4268 		    freepath = path = transform_string_dyn("-CTCP", path, 0, NULL);
4269 		    RETURN_IF_EMPTY(path);
4270 		}
4271 		expand_twiddle(path, path2);
4272 
4273 		if ((numglobs = glob(path2, GLOB_MARK | GLOB_QUOTE | GLOB_BRACE,
4274 						NULL, &globbers)) < 0)
4275 			RETURN_INT(numglobs);
4276 
4277 		for (i = 0; i < globbers.gl_pathc; i++)
4278 		{
4279 			if (strpbrk(globbers.gl_pathv[i], " \""))
4280 			{
4281 				size_t size;
4282 				char *b;
4283 
4284 				size = strlen(globbers.gl_pathv[i]) + 4;
4285 				b = alloca(size);
4286 				snprintf(b, size, "\"%s\"",
4287 						globbers.gl_pathv[i]);
4288 				malloc_strcat_wordlist_c(&retval, space, b, &rvclue);
4289 			}
4290 			else
4291 				malloc_strcat_wordlist_c(&retval, space, globbers.gl_pathv[i], &rvclue);
4292 		}
4293 		globfree(&globbers);
4294 		new_free(&freepath);
4295 	}
4296 
4297 	RETURN_MSTR(retval);
4298 }
4299 
BUILT_IN_FUNCTION(function_globi,word)4300 BUILT_IN_FUNCTION(function_globi, word)
4301 {
4302 	char 	*path;
4303 	Filename path2;
4304 	char	*retval = NULL;
4305 	int 	numglobs, i;
4306 	size_t	rvclue=0;
4307 	glob_t 	globbers;
4308 
4309 	memset(&globbers, 0, sizeof(glob_t));
4310 	while (word && *word)
4311 	{
4312 		char	*freepath = NULL;
4313 
4314 		GET_DWORD_ARG(path, word);
4315 		if (!path || !*path)
4316 			path = word, word = NULL;
4317 		else
4318 		{
4319 		   /* The CTCP enquoting is intentional, probably
4320 		    * to do de-\-ing */
4321 		    freepath = path = transform_string_dyn("-CTCP", path, 0, NULL);
4322 		    RETURN_IF_EMPTY(path);
4323 		}
4324 		expand_twiddle(path, path2);
4325 
4326 		if ((numglobs = bsd_glob(path2,
4327 				GLOB_MARK | GLOB_INSENSITIVE | GLOB_QUOTE | GLOB_BRACE,
4328 				NULL, &globbers)) < 0)
4329 			RETURN_INT(numglobs);
4330 
4331 		for (i = 0; i < globbers.gl_pathc; i++)
4332 		{
4333 			if (strpbrk(globbers.gl_pathv[i], " \""))
4334 			{
4335 				size_t size;
4336 				char *b;
4337 
4338 				size = strlen(globbers.gl_pathv[i]) + 4;
4339 				b = alloca(size);
4340 				snprintf(b, size, "\"%s\"",
4341 						globbers.gl_pathv[i]);
4342 				malloc_strcat_wordlist_c(&retval, space, b, &rvclue);
4343 			}
4344 			else
4345 				malloc_strcat_wordlist_c(&retval, space, globbers.gl_pathv[i], &rvclue);
4346 		}
4347 		bsd_globfree(&globbers);
4348 		new_free(&freepath);
4349 	}
4350 
4351 	RETURN_MSTR(retval);
4352 }
4353 
4354 
BUILT_IN_FUNCTION(function_mkdir,words)4355 BUILT_IN_FUNCTION(function_mkdir, words)
4356 {
4357 	Filename expanded;
4358 	int 	failure = 0;
4359 
4360 	while (words && *words)
4361 	{
4362 		char *fn = new_next_arg(words, &words);
4363 		if (!fn || !*fn)
4364 			fn = words, words = NULL;
4365 		if (normalize_filename(fn, expanded))
4366 			failure++;
4367 		if (mkdir(expanded, 0777))
4368 			failure++;
4369 	}
4370 
4371 	RETURN_INT(failure);
4372 }
4373 
BUILT_IN_FUNCTION(function_umask,words)4374 BUILT_IN_FUNCTION(function_umask, words)
4375 {
4376 	int new_umask;
4377 	GET_INT_ARG(new_umask, words);
4378 	RETURN_INT(umask(new_umask));
4379 }
4380 
BUILT_IN_FUNCTION(function_chmod,words)4381 BUILT_IN_FUNCTION(function_chmod, words)
4382 {
4383 	char 	*filearg,
4384 		*after;
4385 	int 	fd = -1;
4386 	char 	*perm_s;
4387 	mode_t 	perm;
4388 	Filename expanded;
4389 
4390 	GET_DWORD_ARG(filearg, words);
4391 	fd = (int) strtoul(filearg, &after, 0);
4392 
4393 	GET_FUNC_ARG(perm_s, words);
4394 	perm = (mode_t) strtol(perm_s, &perm_s, 8);
4395 
4396 	if (after != words && *after == 0)
4397 	{
4398 		if (file_valid(fd))
4399 			RETURN_INT(fchmod(fd, perm));
4400 		else
4401 			RETURN_EMPTY;
4402 	}
4403 	else
4404 	{
4405 		if (normalize_filename(filearg, expanded))
4406 			RETURN_INT(-1);
4407 
4408 		RETURN_INT(chmod(expanded, perm));
4409 	}
4410 }
4411 
BUILT_IN_FUNCTION(function_twiddle,words)4412 BUILT_IN_FUNCTION(function_twiddle, words)
4413 {
4414 	Filename retval;
4415 
4416 	*retval = 0;
4417 	expand_twiddle(words, retval);
4418 	RETURN_STR(retval);
4419 }
4420 
4421 
unsort_it(const void * v1,const void * v2)4422 static int unsort_it (const void *v1, const void *v2)
4423 {
4424 	/* This just makes me itch. ;-) */
4425 	return (int)(*(const char * const *)v1 - *(const char * const *)v2);
4426 }
4427 /*
4428  * Date: Sun, 29 Sep 1996 19:17:25 -0700
4429  * Author: Thomas Morgan <tmorgan@pobox.com>
4430  * Submitted-by: archon <nuance@twri.tamu.edu>
4431  *
4432  * $uniq (string of text)
4433  * Given a set of words, returns a new set of words with duplicates
4434  * removed.
4435  * EX: $uniq(one two one two) returns "one two"
4436  */
BUILT_IN_FUNCTION(function_uniq,word)4437 BUILT_IN_FUNCTION(function_uniq, word)
4438 {
4439         char    **list = NULL;
4440 	char *booya = NULL;
4441         int     listc, listi, listo;
4442 
4443 	RETURN_IF_EMPTY(word);
4444         listc = splitw(word, &list, DWORD_DWORDS);
4445 
4446 	/* 'list' is set to NULL if 'word' is empty.  Punt in this case. */
4447 	if (!list)
4448 		RETURN_EMPTY;
4449 
4450 #if 1
4451 	/*
4452 	 * This was originally conceved by wd, although his code
4453 	 * never made it into the distribution.
4454 	 *
4455 	 * Sort followed up with a remove duplicates.  Standard stuff,
4456 	 * only, the whacky way we go about it is due to compatibility.
4457 	 *
4458 	 * The major assumption here is that the pointers in list[] are
4459 	 * in ascending order.  Since splitw() works by inserting nuls
4460 	 * into the original string, we can be somewhat secure here.
4461 	 */
4462 	qsort((void *)list, listc, sizeof(char *), sort_it);
4463 
4464 	/* Remove _subsequent_ duplicate values wrt the original list
4465 	 * This means, kill the higher valued pointers value.
4466 	 */
4467 	for (listo = 0, listi = 1; listi < listc; listi++) {
4468 		if (sort_it(&list[listi],&list[listo])) {
4469 			listo = listi;
4470 		} else {
4471 			if (list[listi]<list[listo]) {
4472 				*(char *)list[listo] = 0;
4473 				listo = listi;
4474 			} else {
4475 				*(char *)list[listi] = 0;
4476 			}
4477 		}
4478 	}
4479 
4480 	/* We want the remaining words to appear in their original order */
4481 	qsort((void *)list, listc, sizeof(char *), unsort_it);
4482 	booya = unsplitw(&list, listc, DWORD_DWORDS);
4483 
4484 #else
4485     { /* Oh hush.  It is #ifdef'd out, after all. */
4486 	char	*tval;
4487 	char	*input;
4488 	size_t	rvclue=0;
4489 	size_t	size;
4490 
4491 	/*
4492 	 * XXX This algorithm is too expensive.  It should be done
4493 	 * by some other way.  Calling function_findw() is a hack.
4494 	 */
4495 
4496 	/* The first word is always obviously included. */
4497 	booya = malloc_strdup(list[0]);
4498         for (listi = 1; listi < listc; listi++)
4499         {
4500 		size = strlen(list[listi]) + strlen(booya) + 2;
4501 		input = alloca(size);
4502 		strlopencat_c(input, size, NULL, list[listi], space, booya, NULL);
4503 
4504 		tval = function_findw(input);
4505 		if (my_atol(tval) == -1)
4506 			malloc_strcat_word_c(&booya, space, list[listi], DWORD_DWORDS, &rvclue);
4507 		new_free(&tval);
4508         }
4509     }
4510 #endif
4511 
4512         new_free((char **)&list);
4513 	RETURN_MSTR(booya);
4514 }
4515 
BUILT_IN_FUNCTION(function_status,word)4516 BUILT_IN_FUNCTION(function_status, word)
4517 {
4518 	unsigned 	window_refnum;
4519 	int 		status_line;
4520 	char *		retval;
4521 
4522 	GET_INT_ARG(window_refnum, word);
4523 	GET_INT_ARG(status_line, word);
4524 	retval = get_status_by_refnum(window_refnum, status_line);
4525 	RETURN_STR(retval);
4526 }
4527 
4528 /*
4529  * Date: Sat, 26 Apr 1997 13:41:11 +1200
4530  * Author: IceKarma (ankh@canuck.gen.nz)
4531  * Contributed by: author
4532  *
4533  * Usage: $winchan(#channel <server refnum|server name>)
4534  * Given a channel name and either a server refnum or a direct server
4535  * name or an effective server name, this function will return the
4536  * refnum of the window where the channel is the current channel (on that
4537  * server if appropriate.)
4538  *
4539  * Returns -1 (Too few arguments specified, or Window Not Found) on error.
4540  */
BUILT_IN_FUNCTION(function_winchan,input)4541 BUILT_IN_FUNCTION(function_winchan, input)
4542 {
4543 	char *arg1 = NULL;
4544 
4545 	if (input && *input)
4546 		GET_FUNC_ARG(arg1, input);
4547 
4548 	/*
4549 	 * Return window refnum by channel
4550 	 */
4551 	if (arg1 && is_channel(arg1))
4552 	{
4553 		int 	servnum;
4554 		char 	*chan;
4555 		int	win = -1;
4556 
4557 		chan = arg1;
4558 		if (is_string_empty(input))
4559 			servnum = from_server;
4560 		else
4561 			servnum = str_to_servref(input);
4562 
4563 		/* Now return window for *any* channel. */
4564 		if ((win = get_channel_winref(chan, servnum)))
4565 			RETURN_INT(win);
4566 
4567 		RETURN_INT(-1);
4568 	}
4569 
4570 	/*
4571 	 * Return channel by window refnum/desc
4572 	 */
4573 	else
4574 	{
4575 		Window *win;
4576 
4577 		if (arg1 && *arg1)
4578 			win = get_window_by_desc(arg1);
4579 		else
4580 			win = get_window_by_refnum(0);
4581 
4582 		if (win)
4583 			RETURN_STR(get_echannel_by_refnum(win->refnum));
4584 		RETURN_EMPTY;
4585 	}
4586 }
4587 
BUILT_IN_FUNCTION(function_findw,input)4588 BUILT_IN_FUNCTION(function_findw, input)
4589 {
4590 	char	*word, *this_word;
4591 	int	word_cnt = 0;
4592 
4593 	GET_FUNC_ARG(word, input);
4594 
4595 	while (input && *input)
4596 	{
4597 		GET_FUNC_ARG(this_word, input);
4598 		if (!my_stricmp(this_word, word))
4599 			RETURN_INT(word_cnt);
4600 
4601 		word_cnt++;
4602 	}
4603 
4604 	RETURN_INT(-1);
4605 }
4606 
BUILT_IN_FUNCTION(function_findws,input)4607 BUILT_IN_FUNCTION(function_findws, input)
4608 {
4609 	char	*word, *this_word;
4610 	int	word_cnt;
4611 	char	*ret = NULL;
4612 	size_t	clue = 0;
4613 
4614 	GET_FUNC_ARG(word, input);
4615 
4616 	for (word_cnt = 0; input && *input; word_cnt++)
4617 	{
4618 		GET_FUNC_ARG(this_word, input);
4619 		if (!my_stricmp(this_word, word))
4620 			malloc_strcat_word_c(&ret, space, ltoa(word_cnt), DWORD_NO, &clue);
4621 	}
4622 
4623 	RETURN_MSTR(ret);
4624 }
4625 
4626 
BUILT_IN_FUNCTION(function_uhc,input)4627 BUILT_IN_FUNCTION(function_uhc, input)
4628 {
4629 	char 	*nick, *user, *host;
4630 
4631 	if (!input || !*input)
4632 		RETURN_EMPTY;
4633 
4634 	if (figure_out_address(input, &nick, &user, &host))
4635 		RETURN_STR(input);
4636 	else
4637 		return malloc_sprintf(NULL, "%s!%s@%s",
4638 				*nick ? nick : star,
4639 				*user ? user : star,
4640 				*host ? host : star);
4641 }
4642 
4643 /*
4644  * Opposite of $uhc().  Breaks down a complete nick!user@host
4645  * into smaller components, based upon absolute wildcards.
4646  * Eg, *!user@host becomes user@host.  And *!*@*.host becomes *.host
4647  * Based on the contribution and further changes by Peter V. Evans
4648  * (arc@anet-chi.com)
4649  */
BUILT_IN_FUNCTION(function_deuhc,input)4650 BUILT_IN_FUNCTION(function_deuhc, input)
4651 {
4652 	char	*buf;
4653 
4654 	buf = LOCAL_COPY(input);
4655 
4656 	if (!strchr(buf, '!') || !strchr(buf, '@'))
4657 		RETURN_EMPTY;
4658 
4659 	if (!strncmp(buf, "*!", 2))
4660 	{
4661 		buf += 2;
4662 		if (!strncmp(buf, "*@", 2))
4663 			buf += 2;
4664 	}
4665 
4666 	RETURN_STR(buf);
4667 }
4668 
4669 
BUILT_IN_FUNCTION(function_ftime,words)4670 BUILT_IN_FUNCTION(function_ftime, words)
4671 {
4672 	char *	filename;
4673 	Filename fullname;
4674 	Stat 	s;
4675 
4676 	GET_DWORD_ARG(filename, words);
4677 
4678 	if (!filename || !*filename)
4679 		filename = words, words = NULL;
4680 
4681 	if (normalize_filename(filename, fullname))
4682 		RETURN_EMPTY;
4683 
4684 	if (stat(fullname, &s) == -1)
4685 		RETURN_EMPTY;
4686 
4687 	RETURN_INT(s.st_mtime);
4688 }
4689 
BUILT_IN_FUNCTION(function_irclib,input)4690 BUILT_IN_FUNCTION(function_irclib, input)
4691 {
4692 	RETURN_STR(irc_lib);
4693 }
4694 
BUILT_IN_FUNCTION(function_substr,input)4695 BUILT_IN_FUNCTION(function_substr, input)
4696 {
4697 	char *srch;
4698 	ssize_t span;
4699 
4700 	GET_DWORD_ARG(srch, input);
4701 
4702 	if ((span = stristr(input, srch)) >= 0)
4703 		RETURN_INT((unsigned long) span);
4704 	else
4705 		RETURN_INT(-1);
4706 }
4707 
BUILT_IN_FUNCTION(function_rsubstr,input)4708 BUILT_IN_FUNCTION(function_rsubstr, input)
4709 {
4710 	char *srch;
4711 	ssize_t	span;
4712 
4713 	GET_DWORD_ARG(srch, input);
4714 
4715 	if ((span = rstristr(input, srch)) >= 0)
4716 		RETURN_INT((unsigned long) span);
4717 	else
4718 		RETURN_INT(-1);
4719 }
4720 
BUILT_IN_FUNCTION(function_nohighlight,input)4721 BUILT_IN_FUNCTION(function_nohighlight, input)
4722 {
4723 	char *outbuf, *ptr;
4724 
4725 	ptr = outbuf = alloca(strlen(input) * 3 + 1);
4726 	while (*input)
4727 	{
4728 		switch (*input)
4729 		{
4730 			case REV_TOG:
4731 			case UND_TOG:
4732 			case BOLD_TOG:
4733 			case BLINK_TOG:
4734 			case ALL_OFF:
4735 			case ALT_TOG:
4736 			case '\003':
4737 			case '\033':
4738 			{
4739 				*ptr++ = REV_TOG;
4740 				*ptr++ = (*input++ | 0x40);
4741 				*ptr++ = REV_TOG;
4742 				break;
4743 			}
4744 			default:
4745 			{
4746 				*ptr++ = *input++;
4747 				break;
4748 			}
4749 		}
4750 	}
4751 
4752 	*ptr = 0;
4753 	RETURN_STR(outbuf);
4754 }
4755 
BUILT_IN_FUNCTION(function_isconnected,input)4756 BUILT_IN_FUNCTION(function_isconnected, input)
4757 {
4758 	int refnum;
4759 
4760 	/*
4761 	 * Allow no arguments to reference from_server
4762 	 */
4763 	if (!*input)
4764 		refnum = from_server;
4765 	else
4766 		GET_INT_ARG(refnum, input);
4767 
4768 	if (refnum == -1)
4769 		refnum = from_server;
4770 
4771 	RETURN_INT(is_server_registered(refnum));
4772 }
4773 
BUILT_IN_FUNCTION(function_currchans,input)4774 BUILT_IN_FUNCTION(function_currchans, input)
4775 {
4776 	int server = -1;
4777 	Window *blah = NULL;
4778 	char *retval = NULL;
4779 	const char *chan;
4780 	size_t	clue;
4781 
4782 	if (input && *input)
4783 		GET_INT_ARG(server, input)
4784 	else
4785 		server = -2;		/* DONT CHANGE THIS TO NOSERV */
4786 
4787 	if (server == -1)
4788 		server = from_server;
4789 
4790 	clue = 0;
4791 	while (traverse_all_windows(&blah))
4792 	{
4793 		if (server != -2 && blah->server != server)
4794 			continue;
4795 		if (!(chan = get_echannel_by_refnum(blah->refnum)))
4796 			continue;
4797 
4798 		malloc_strcat_wordlist_c(&retval, space, "\"", &clue);
4799 		malloc_strcat_wordlist_c(&retval, empty_string, ltoa(blah->server), &clue);
4800 		malloc_strcat_wordlist_c(&retval, space, chan, &clue);
4801 		malloc_strcat_wordlist_c(&retval, empty_string, "\"", &clue);
4802 	}
4803 
4804 	RETURN_MSTR(retval);
4805 }
4806 
4807 /*
4808  * Returns 1 if the specified function name is a built in function,
4809  * Returns 0 if not.
4810  */
BUILT_IN_FUNCTION(function_fnexist,input)4811 BUILT_IN_FUNCTION(function_fnexist, input)
4812 {
4813 	char *fname;
4814 
4815 	GET_FUNC_ARG(fname, input);
4816 	RETURN_INT(func_exist(fname));
4817 }
4818 
BUILT_IN_FUNCTION(function_cexist,input)4819 BUILT_IN_FUNCTION(function_cexist, input)
4820 {
4821 	char *fname;
4822 
4823 	GET_FUNC_ARG(fname, input);
4824 	RETURN_INT(command_exist(fname));
4825 }
4826 
4827 
4828 /*
4829  * These are used as an interface to regex support.  Here's the plan:
4830  *
4831  * $regcomp(<pattern>)
4832  *	will return an $encode()d string that is suitable for
4833  * 		assigning to a variable.  The return value of this
4834  *		function must at some point be passed to $regfree()!
4835  *
4836  * $regexec(<compiled> <string>)
4837  *	Will return "0" or "1" depending on whether or not the given string
4838  *		was matched by the previously compiled string.  The value
4839  *		for <compiled> must be a value previously returned by $regex().
4840  *		Failure to do this will result in undefined chaos.
4841  *
4842  * $regerror(<compiled>)
4843  *	Will return the error string for why the previous $regexec() or
4844  *		$regex() call failed.
4845  *
4846  * $regfree(<compiled>)
4847  *	Will free off any of the data that might be contained in <compiled>
4848  *		You MUST call $regfree() on any value that was previously
4849  *		returned by $regex(), or you will leak memory.  This is not
4850  *		my problem (tm).  It returns the FALSE value.
4851  */
4852 
4853 #ifdef HAVE_REGEX_H
4854 static int last_regex_error = 0; 		/* XXX */
4855 
BUILT_IN_FUNCTION(function_regcomp_cs,input)4856 BUILT_IN_FUNCTION(function_regcomp_cs, input)
4857 {
4858 	char *	dest;
4859 	regex_t preg;
4860 
4861 	memset(&preg, 0, sizeof(preg)); 	/* make valgrind happy */
4862 	last_regex_error = regcomp(&preg, input, REG_EXTENDED);
4863 
4864 	dest = transform_string_dyn("+ENC", (char *)&preg,
4865 					sizeof(regex_t), NULL);
4866 	RETURN_MSTR(dest);
4867 }
4868 
BUILT_IN_FUNCTION(function_regcomp,input)4869 BUILT_IN_FUNCTION(function_regcomp, input)
4870 {
4871 	char *	dest;
4872 	regex_t preg;
4873 
4874 	memset(&preg, 0, sizeof(preg)); 	/* make valgrind happy */
4875 	last_regex_error = regcomp(&preg, input, REG_EXTENDED | REG_ICASE);
4876 
4877 	dest = transform_string_dyn("+ENC", (char *)&preg,
4878 					sizeof(regex_t), NULL);
4879 	RETURN_MSTR(dest);
4880 }
4881 
BUILT_IN_FUNCTION(function_regexec,input)4882 BUILT_IN_FUNCTION(function_regexec, input)
4883 {
4884 	char *	unsaved;
4885 	regex_t preg;
4886 
4887 	GET_FUNC_ARG(unsaved, input);
4888 	if (strlen(unsaved) != sizeof(regex_t) * 2)
4889 	{
4890 		yell("First argument to $regexec() isn't proper length");
4891 		RETURN_EMPTY;
4892 	}
4893 
4894 	/* XXX - What if this fails? */
4895 	transform_string(ENC_xform, XFORM_DECODE, NULL,
4896 			 unsaved, strlen(unsaved),
4897 			 (char *)&preg, sizeof(preg));
4898 	last_regex_error = regexec(&preg, input, 0, NULL, 0);
4899 	RETURN_INT(last_regex_error);	/* DONT PASS FUNC CALL TO RETURN_INT */
4900 }
4901 
BUILT_IN_FUNCTION(function_regmatches,input)4902 BUILT_IN_FUNCTION(function_regmatches, input)
4903 {
4904 	char *	unsaved;
4905 	size_t	nmatch;
4906 	char *	ret = NULL;
4907 	regex_t preg;
4908 	regmatch_t *pmatch = NULL;
4909 
4910 	GET_FUNC_ARG(unsaved, input);
4911 	GET_INT_ARG(nmatch, input);
4912 
4913 	if (strlen(unsaved) != sizeof(regex_t) * 2)
4914 	{
4915 		yell("First argument to $regmatches() isn't proper length");
4916 		RETURN_EMPTY;
4917 	}
4918 
4919 	/* XXX - What if this fails? */
4920 	transform_string(ENC_xform, XFORM_DECODE, NULL,
4921 			 unsaved, strlen(unsaved),
4922 			 (char *)&preg, sizeof(preg));
4923 
4924 	RESIZE(pmatch, regmatch_t, nmatch);
4925 	if (!(last_regex_error = regexec(&preg, input, nmatch, pmatch, 0)))
4926 	{
4927 	    size_t	n, clue = 0;
4928 	    const unsigned char *rm_so_str, *rm_eo_str;
4929 	    int		rm_so_str_cnt, rm_eo_str_cnt;
4930 
4931 	    for (n = 0; n < nmatch; n++)
4932 	    {
4933 		if (pmatch[n].rm_so < 0)
4934 		{
4935 			/* This may need to be continue depending
4936 			 * on the regex implementation */
4937 			break;
4938 		}
4939 		rm_so_str = input + pmatch[n].rm_so;
4940 		rm_eo_str = input + pmatch[n].rm_eo;
4941 		rm_so_str_cnt = count_initial_codepoints(input, rm_so_str);
4942 		rm_eo_str_cnt = count_initial_codepoints(input, rm_eo_str);
4943 
4944 		malloc_strcat_word_c(&ret, space,
4945 					ltoa(rm_so_str_cnt),
4946 					DWORD_NO, &clue);
4947 		malloc_strcat_word_c(&ret, space,
4948 					ltoa(rm_eo_str_cnt - rm_so_str_cnt),
4949 					DWORD_NO, &clue);
4950 	    }
4951 	}
4952 	new_free((char **)&pmatch);
4953 	RETURN_MSTR(ret);	/* DONT PASS FUNC CALL TO RETURN_INT */
4954 }
4955 
BUILT_IN_FUNCTION(function_regerror,input)4956 BUILT_IN_FUNCTION(function_regerror, input)
4957 {
4958 	char *	unsaved;
4959 	char	error_buf[1024];
4960 	regex_t	preg;
4961 
4962 	GET_FUNC_ARG(unsaved, input);
4963 	if (strlen(unsaved) != sizeof(regex_t) * 2)
4964 	{
4965 		yell("First argument to $regmatches() isn't proper length");
4966 		RETURN_EMPTY;
4967 	}
4968 
4969 	/* XXX - What if this fails? */
4970 	transform_string(ENC_xform, XFORM_DECODE, NULL,
4971 			 unsaved, strlen(unsaved),
4972 			 (char *)&preg, sizeof(preg));
4973 
4974 	*error_buf = 0;
4975 	if (last_regex_error)
4976 		regerror(last_regex_error, &preg, error_buf, sizeof(error_buf));
4977 	RETURN_STR(error_buf);
4978 }
4979 
BUILT_IN_FUNCTION(function_regfree,input)4980 BUILT_IN_FUNCTION(function_regfree, input)
4981 {
4982 	char *unsaved;
4983 	regex_t	preg;
4984 
4985 	GET_FUNC_ARG(unsaved, input);
4986 	if (strlen(unsaved) != sizeof(regex_t) * 2)
4987 	{
4988 		yell("First argument to $regmatches() isn't proper length");
4989 		RETURN_EMPTY;
4990 	}
4991 
4992 	/* XXX - What if this fails? */
4993 	transform_string(ENC_xform, XFORM_DECODE, NULL,
4994 			 unsaved, strlen(unsaved),
4995 			 (char *)&preg, sizeof(preg));
4996 
4997 	regfree(&preg);
4998 	RETURN_EMPTY;
4999 }
5000 
5001 #else
BUILT_IN_FUNCTION(function_regexec,input)5002 BUILT_IN_FUNCTION(function_regexec, input)  { RETURN_EMPTY; }
BUILT_IN_FUNCTION(function_regcomp,input)5003 BUILT_IN_FUNCTION(function_regcomp, input)  { RETURN_EMPTY; }
BUILT_IN_FUNCTION(function_regerror,input)5004 BUILT_IN_FUNCTION(function_regerror, input) { RETURN_STR("no regex support"); }
BUILT_IN_FUNCTION(function_regfree,input)5005 BUILT_IN_FUNCTION(function_regfree, input)  { RETURN_EMPTY; }
5006 #endif
5007 
BUILT_IN_FUNCTION(function_getenv,input)5008 BUILT_IN_FUNCTION(function_getenv, input)
5009 {
5010 	char *env;
5011 
5012 	GET_FUNC_ARG(env, input);
5013 	RETURN_STR(getenv(env));
5014 }
5015 
BUILT_IN_FUNCTION(function_count,input)5016 BUILT_IN_FUNCTION(function_count, input)
5017 {
5018 	char 	*str;
5019 	const char *str2;
5020 	int 	count = 0;
5021 	ssize_t	span;
5022 
5023 	GET_DWORD_ARG(str, input);
5024 	str2 = input;
5025 
5026 	for (;;)
5027 	{
5028 		if ((span = stristr(str2, str)) < 0)
5029 			break;
5030 
5031 		count++;
5032 		str2 += span + 1;
5033 	}
5034 
5035 	RETURN_INT(count);
5036 }
5037 
5038 /*
5039  * Usage: $randread(filename)
5040  * Returns: A psuedo-random line in the specified file
5041  * Based on work contributed by srfrog
5042  *
5043  * XXX This function is not encoding aware.
5044  * I'm not even sure if that's possible.
5045  */
BUILT_IN_FUNCTION(function_randread,input)5046 BUILT_IN_FUNCTION(function_randread, input)
5047 {
5048 	FILE 	*fp;
5049 	char 	buffer[BIG_BUFFER_SIZE + 1];
5050 	char 	*filename;
5051 	Filename fullname;
5052 	off_t	filesize, offset;
5053 
5054 	*buffer = 0;
5055 	GET_DWORD_ARG(filename, input);
5056 
5057 	if (normalize_filename(filename, fullname))
5058 		RETURN_EMPTY;
5059 	if ((filesize = file_size(fullname)) <= 0)
5060 		RETURN_EMPTY;
5061 	if ((fp = fopen(fullname, "r")) == NULL)
5062 		RETURN_EMPTY;
5063 
5064 	offset = random_number(0) % filesize - 1;
5065 	fseek(fp, offset, SEEK_SET);
5066 	if (!fgets(buffer, BIG_BUFFER_SIZE, fp))
5067 		(void) 0;
5068 	if (!fgets(buffer, BIG_BUFFER_SIZE, fp))
5069 		(void) 0;
5070 	if (feof(fp))
5071 	{
5072 		fseek(fp, 0, SEEK_SET);
5073 		if (!fgets(buffer, BIG_BUFFER_SIZE, fp))
5074 			yell("I'm having some difficulty in $randread().");
5075 	}
5076 	chomp(buffer);
5077 	fclose(fp);
5078 
5079 	RETURN_STR(buffer);
5080 }
5081 
5082 /*
5083  * Search and replace function --
5084  * Usage:   $msar(c/search/replace/data)
5085  *  Commands:
5086  *              r - treat data as a variable name and
5087  *                  return the replaced data to the variable
5088  *              g - Replace all instances, not just the first one
5089  *              i - does a stristr()
5090  * The delimiter may be any character that is not a command (typically /)
5091  * The delimiter MUST be the first character after the command
5092  * Returns empty string on error
5093  *
5094  * Written by panasync, Contributed on 12/03/1997
5095  * Rewritten mostly from scratch, on Feb 14, 2005
5096  */
BUILT_IN_FUNCTION(function_msar,input)5097 BUILT_IN_FUNCTION(function_msar, input)
5098 {
5099 	int	variable = 0,
5100 		global = 0,
5101 		case_sensitive = 0;
5102 	int	delimiter;
5103 	char *	last_segment;
5104 	char *	text;
5105 	char *	after;
5106 	char *	workbuf = NULL;
5107 	char *	search;
5108 	char *	replace;
5109 	size_t	cpoffset;
5110 	char	*s, *p;
5111 	unsigned char	delimstr[16];
5112 
5113 	/*
5114 	 * Scan the leading part of the argument list, slurping up any
5115 	 * options, and grabbing the delimiter character.  If we don't
5116 	 * come across a delimiter, then just end abruptly.
5117 	 */
5118 	for (;; input++)
5119 	{
5120 		if (*input == 'r')
5121 			variable = 1;
5122 		else if (*input == 'g')
5123 			global = 1;
5124 		else if (*input == 'i')
5125 			case_sensitive = 0;
5126 		else if (*input == 'c')
5127 			case_sensitive = 1;
5128 		else if (!*input)
5129 			RETURN_EMPTY;
5130 		else
5131 		{
5132 			while ((delimiter = next_code_point(CUC_PP &input, 0)) == -1)
5133 				input++;
5134 			break;
5135 		}
5136 	}
5137 
5138 	ucs_to_utf8(delimiter, delimstr, sizeof(delimstr));
5139 
5140 	/* Now that we have the delimiter, find out what we're substituting */
5141 	if (!(last_segment = (char *)(intptr_t)rcpindex(input + strlen(input), input, delimstr, 1, &cpoffset)))
5142 		RETURN_EMPTY;
5143 
5144 	/*
5145 	 * The last segment is either a text string, or a variable.  If it
5146 	 * is a variable, look up its value.
5147 	 *
5148 	 * XXX This two-step is required because the delimiter might be
5149 	 * more than one byte.  So we null out the first byte, but move
5150 	 * last segment to the first byte of the following CP.
5151 	 */
5152 	p = s = last_segment;
5153 	next_code_point(CUC_PP &s, 1);
5154 	*p = 0;
5155 	last_segment = s;
5156 
5157 	if (variable == 1)
5158 		text = get_variable(last_segment);
5159 	else
5160 		text = malloc_strdup(last_segment);
5161 
5162 	/*
5163 	 * Perform substitutions while there are still pairs remaining.
5164 	 */
5165 	while (input && *input)
5166 	{
5167 	    /*
5168 	     * "input" contains pairs of strings like:
5169 	     *	search<delim>replace[<delim>|<eol>]
5170 	     *
5171 	     * If we don't find a pair, then perform no substitution --
5172 	     * just bail out right here.
5173 	     */
5174 	    search = next_in_div_list(input, &after, delimiter);
5175 	    if (!search || !*search)
5176 		break;
5177 	    replace = next_in_div_list(after, &after, delimiter);
5178 /*
5179 	    if (!replace || !*replace)
5180 		break;
5181 */
5182 	    input = after;
5183 
5184 	    workbuf = substitute_string(text, search, replace,
5185 					case_sensitive, global);
5186 	    new_free(&text);
5187 	    text = workbuf;
5188 	}
5189 
5190         if (variable)
5191                 add_var_alias(last_segment, text, 0);
5192 	return text;
5193 }
5194 
5195 /*
5196  * Usage: $leftpc(COUNT TEXT)
5197  * Return the *longest* initial part of TEXT that includes COUNT + 1 printable
5198  * characters. (Don't ask)
5199  */
BUILT_IN_FUNCTION(function_leftpc,word)5200 BUILT_IN_FUNCTION(function_leftpc, word)
5201 {
5202 	unsigned char ** 	prepared = NULL;
5203 	char *		retval;
5204 	int		my_lines = 1;
5205 	int		count;
5206 
5207 	GET_INT_ARG(count, word);
5208 	if (count < 0 || !*word)
5209 		RETURN_EMPTY;
5210 
5211 	/* Convert the string to "attribute format" */
5212 	word = new_normalize_string(word, 0, NORMALIZE);
5213 
5214 	/* Count off the first line of stuff */
5215 	prepared = prepare_display(-1, word, count, &my_lines, PREPARE_NOWRAP);
5216 
5217 	/* Convert the first line back to "logical format" */
5218 	retval = denormalize_string(prepared[0]);
5219 
5220 	/* Clean up and return. */
5221 	new_free(&word);
5222 	return retval;
5223 }
5224 
5225 
5226 /*
5227  * This was written to have the same net-effect as bitchx.  However, we use
5228  * ^C codes rather than ansi codes as bitchx does.  You shouldnt notice one
5229  * way or the other.
5230  */
function_cparse(char * input)5231 char *	function_cparse (char *input)
5232 {
5233 	int 	i, j, max;
5234 	char 	*output;
5235 	int	noappend = 0, expand = 0;
5236 	char *	stuff;
5237 
5238 	if (*input == '"')
5239 	{
5240 		stuff = new_next_arg(input, &input);
5241 		expand = 1;
5242 	}
5243 	else
5244 		stuff = input;
5245 
5246 	output = (char *)alloca(strlen(stuff) * 3 + 4);
5247 
5248 	for (i = 0, j = 0, max = strlen(stuff); i < max; i++)
5249 	{
5250 		if (stuff[i] == '%')
5251 		{
5252 		    i++;
5253 		    switch (stuff[i])
5254 		    {
5255 			case 'k':
5256 				output[j++] = '\003';
5257 				output[j++] = '3';
5258 				output[j++] = '0';
5259 				break;
5260 			case 'r':
5261 				output[j++] = '\003';
5262 				output[j++] = '3';
5263 				output[j++] = '1';
5264 				break;
5265 			case 'g':
5266 				output[j++] = '\003';
5267 				output[j++] = '3';
5268 				output[j++] = '2';
5269 				break;
5270 			case 'y':
5271 				output[j++] = '\003';
5272 				output[j++] = '3';
5273 				output[j++] = '3';
5274 				break;
5275 			case 'b':
5276 				output[j++] = '\003';
5277 				output[j++] = '3';
5278 				output[j++]   = '4';
5279 				break;
5280 			case 'm':
5281 			case 'p':
5282 				output[j++] = '\003';
5283 				output[j++] = '3';
5284 				output[j++]   = '5';
5285 				break;
5286 			case 'c':
5287 				output[j++] = '\003';
5288 				output[j++] = '3';
5289 				output[j++]   = '6';
5290 				break;
5291 			case 'w':
5292 				output[j++] = '\003';
5293 				output[j++] = '3';
5294 				output[j++]   = '7';
5295 				break;
5296 
5297 			case 'K':
5298 				output[j++] = '\003';
5299 				output[j++] = '5';
5300 				output[j++]   = '0';
5301 				break;
5302 			case 'R':
5303 				output[j++] = '\003';
5304 				output[j++] = '5';
5305 				output[j++]   = '1';
5306 				break;
5307 			case 'G':
5308 				output[j++] = '\003';
5309 				output[j++] = '5';
5310 				output[j++]   = '2';
5311 				break;
5312 			case 'Y':
5313 				output[j++] = '\003';
5314 				output[j++] = '5';
5315 				output[j++]   = '3';
5316 				break;
5317 			case 'B':
5318 				output[j++] = '\003';
5319 				output[j++] = '5';
5320 				output[j++]   = '4';
5321 				break;
5322 			case 'M':
5323 			case 'P':
5324 				output[j++] = '\003';
5325 				output[j++] = '5';
5326 				output[j++]   = '5';
5327 				break;
5328 			case 'C':
5329 				output[j++] = '\003';
5330 				output[j++] = '5';
5331 				output[j++]   = '6';
5332 				break;
5333 			case 'W':
5334 				output[j++] = '\003';
5335 				output[j++] = '5';
5336 				output[j++]   = '7';
5337 				break;
5338 
5339 			case '0': case '1': case '2': case '3':
5340 			case '4': case '5': case '6': case '7':
5341 				output[j++] = '\003';
5342 				output[j++] = ',';
5343 				output[j++] = '4';
5344 				output[j++]   = stuff[i];
5345 				break;
5346 
5347 			case 'F':
5348 				output[j++] = BLINK_TOG;
5349 				break;
5350 			case 'n':
5351 				output[j++] = '\003';
5352 				output[j++] = '-';
5353 				output[j++] = '1';
5354 				output[j++] = ALL_OFF;
5355 				break;
5356 			case 'X':
5357 				output[j++] = '\030';
5358 				break;
5359 
5360 			case 'N':
5361 				noappend = 1;
5362 				break;
5363 			case '%':
5364 				output[j++] = stuff[i];
5365 				break;
5366 			default:
5367 				output[j++] = '%';
5368 				output[j++] = stuff[i];
5369 				break;
5370 		    }
5371 		}
5372 		else
5373 			output[j++] = stuff[i];
5374 	}
5375 	if (noappend == 0)
5376 	{
5377 		output[j++] = '\003';
5378 		output[j++] = '-';
5379 		output[j++] = '1';
5380 	}
5381 	output[j] = 0;
5382 
5383 	if (expand)
5384 	{
5385 		stuff = expand_alias(output, input);
5386 		RETURN_MSTR(stuff);
5387 	}
5388 
5389 	RETURN_STR(output);
5390 }
5391 
5392 /*
5393  * $uname()
5394  * Returns system information.  Expandoes %a, %s, %r, %v, %m, and %n
5395  * correspond to uname(1)'s -asrvmn switches.  %% expands to a literal
5396  * '%'.  If no arguments are given, the function returns %s and %r (the
5397  * same information given in the client's ctcp version reply).
5398  *
5399  * Contributed by Matt Carothers (CrackBaby) on Dec 20, 1997
5400  */
BUILT_IN_FUNCTION(function_uname,input)5401 BUILT_IN_FUNCTION(function_uname, input)
5402 {
5403 #ifndef HAVE_UNAME
5404 	RETURN_STR("unknown");
5405 #else
5406 	struct utsname un;
5407 	size_t	size = BIG_BUFFER_SIZE;
5408 	char 	tmp[BIG_BUFFER_SIZE + 1];
5409 	char *	ptr = tmp;
5410 	int 	i;
5411 	int	len;
5412 
5413 	if (uname(&un) == -1)
5414 		RETURN_STR("unknown");
5415 
5416 	if (!*input)
5417 		input = LOCAL_COPY("%s %r");
5418 
5419 	for (i = 0, len = strlen(input); i < len; i++)
5420 	{
5421 		if (ptr - tmp >= (int)size)
5422 			break;
5423 
5424 		if (input[i] == '%')
5425 		{
5426 		    switch (input[++i])
5427 		    {
5428 			case 'm':	strlcpy(ptr, un.machine, size);
5429 					break;
5430 			case 'n':	strlcpy(ptr, un.nodename, size);
5431 					break;
5432 			case 'r':	strlcpy(ptr, un.release, size);
5433 					break;
5434 			case 's':	strlcpy(ptr, un.sysname, size);
5435 					break;
5436 			case 'v':	strlcpy(ptr, un.version, size);
5437 					break;
5438 			case 'a':	snprintf(ptr, size, "%s %s %s %s %s",
5439 						un.sysname, un.nodename,
5440 						un.release, un.version,
5441 						un.machine);
5442 					break;
5443 			case '%':	strlcpy(ptr, "%", size);
5444 		    }
5445 		    ptr += strlen(ptr);
5446 		}
5447 		else
5448 		    *ptr++ = input[i];
5449 	}
5450 	*ptr = 0;
5451 
5452 	RETURN_STR(tmp);
5453 #endif
5454 }
5455 
BUILT_IN_FUNCTION(function_querywin,args)5456 BUILT_IN_FUNCTION(function_querywin, args)
5457 {
5458 	Window *w = NULL;
5459 	char *	nick = NULL;
5460 	int	servref = -1;
5461 	const char *q;
5462 
5463 	GET_FUNC_ARG(nick, args);
5464 	if (args && *args)
5465 		GET_INT_ARG(servref, args);
5466 
5467 	while (traverse_all_windows(&w))
5468 	{
5469 	    q = get_equery_by_refnum(w->refnum);
5470 	    if (q && !my_stricmp(q, nick))
5471 		if (servref < 0 || servref == w->server)
5472 			RETURN_INT(w->refnum);
5473 	}
5474 
5475 	RETURN_INT(-1);
5476 }
5477 
5478 /*
5479  * $mask(type address)      OR
5480  * $mask(address type)
5481  * Returns address with a mask specified by type.
5482  *
5483  * $mask(1 nick!khaled@mardam.demon.co.uk)  returns *!*khaled@mardam.demon.co.uk
5484  * $mask(2 nick!khaled@mardam.demon.co.uk)  returns *!*@mardam.demon.co.uk
5485  *
5486  * The available types are:
5487  *
5488  * 0: *!user@host.domain
5489  * 1: *!*user@host.domain
5490  * 2: *!*@host.domain
5491  * 3: *!*user@*.domain
5492  * 4: *!*@*.domain
5493  * 5: nick!user@host.domain
5494  * 6: nick!*user@host.domain
5495  * 7: nick!*@host.domain
5496  * 8: nick!*user@*.domain
5497  * 9: nick!*@*.domain
5498  * 10:*!*user@*
5499  */
BUILT_IN_FUNCTION(function_mask,args)5500 BUILT_IN_FUNCTION(function_mask, args)
5501 {
5502 	char **my_dot = NULL, **colon = NULL;
5503  	char *buff, *my_nickname, *my_username, *my_hostname, *dmask, *dbuff, *p, *fa;
5504 	unsigned i, coloncount, dotcount, nondigit, method, userlen;
5505 	int	size;
5506 
5507 	fa = new_next_arg(args, &args);
5508 
5509 	if (is_number(fa)) {
5510 		method = my_atol(fa);
5511 		my_nickname = args;
5512 	} else {
5513 		my_nickname = fa;
5514 		GET_INT_ARG(method, args);
5515 	}
5516 
5517 	if ((p = strchr(my_nickname, '!'))) {
5518 		*p++ = '\0';
5519 		my_username = p;
5520 	} else {
5521 		p = my_nickname;
5522 		my_nickname = LOCAL_COPY("*");
5523 		my_username = p;
5524 	}
5525 
5526 	if ((p = strchr(p, '@'))) {
5527 		*p++ = '\0';
5528 		my_hostname = p;
5529 		userlen = strlen(my_username);
5530 	} else {
5531 		RETURN_EMPTY;
5532 	}
5533 
5534 	buff = (char *) new_malloc(BIG_BUFFER_SIZE + 1);
5535 	size = strlen(my_hostname) + 2;
5536 	dbuff = (char *) new_malloc(size);
5537 	p = dmask = dbuff + 1;
5538 	strlcpy(p, my_hostname, size);
5539 
5540 	for (i = coloncount = dotcount = nondigit = 0; p[i]; i++) {
5541 		if (p[i] == '.') {
5542 			if (!(dotcount % 50)) {
5543 				new_realloc((void **) &my_dot,
5544 					sizeof(char *) * (dotcount + 50));
5545 			}
5546 
5547 			my_dot[dotcount++] = p + i;
5548 		} else if (p[i] == ':') {
5549 			if (!(coloncount % 50)) {
5550 				new_realloc((void **) &colon,
5551 					sizeof(char *) * (coloncount + 50));
5552 			}
5553 
5554 			colon[coloncount++] = p + i;
5555 		} else if (!isdigit(p[i])) {
5556 			nondigit++;
5557 		}
5558 	}
5559 
5560 	if (coloncount >= 2) {
5561 		*(colon[1] + 1) = '*';
5562 		*(colon[1] + 2) = '\0';
5563 	} else if (!nondigit && dotcount == 3) {
5564 		*(my_dot[2] + 1) = '*';
5565 		*(my_dot[2] + 2) = '\0';
5566 	} else if (dotcount >=2) {
5567 		/* Treat domains like '.co.uk' as a tld. */
5568 
5569 		if ((p + i - my_dot[dotcount - 2]) < 7) {
5570 			if (dotcount >= 3) {
5571 				dmask = my_dot[dotcount - 3] - 1;
5572 				*dmask = '*';
5573 			}
5574 		} else {
5575 			dmask = my_dot[dotcount - 2] - 1;
5576 			*dmask = '*';
5577 		}
5578 	}
5579 
5580 	switch (method) {
5581 		case 0:
5582 			snprintf(buff, BIG_BUFFER_SIZE + 1, "*!%s@%s", my_username,
5583 				my_hostname);
5584 			break;
5585 		case 1:
5586 			snprintf(buff, BIG_BUFFER_SIZE + 1, "*!*%s@%s",
5587 				userlen > 7 ? my_username + 1 : my_username,
5588 				my_hostname);
5589 			break;
5590 		case 2:
5591 			snprintf(buff, BIG_BUFFER_SIZE + 1, "*!*@%s",
5592 				my_hostname);
5593 			break;
5594 		case 3:
5595 			snprintf(buff, BIG_BUFFER_SIZE + 1, "*!*%s@%s",
5596 				userlen > 7 ? my_username + 1 : my_username, dmask);
5597 			break;
5598 		case 4:
5599 			snprintf(buff, BIG_BUFFER_SIZE + 1, "*!*@%s", dmask);
5600 			break;
5601 		case 5:
5602 			snprintf(buff, BIG_BUFFER_SIZE + 1, "%s!%s@%s",
5603 				my_nickname, my_username, my_hostname);
5604 			break;
5605 		case 6:
5606 			snprintf(buff, BIG_BUFFER_SIZE + 1, "%s!*%s@%s",
5607 				my_nickname, userlen > 7 ? my_username + 1 : my_username,
5608 				my_hostname);
5609 			break;
5610 		case 7:
5611 			snprintf(buff, BIG_BUFFER_SIZE + 1, "%s!*@%s", my_nickname,
5612 				my_hostname);
5613 			break;
5614 		case 8:
5615 			snprintf(buff, BIG_BUFFER_SIZE + 1, "%s!*%s@%s",
5616 				my_nickname, userlen > 7 ? my_username + 1 : my_username,
5617 				dmask);
5618 			break;
5619 		case 9:
5620 			snprintf(buff, BIG_BUFFER_SIZE + 1, "%s!*@%s", my_nickname,
5621 				dmask);
5622 			break;
5623 		case 10:
5624 			snprintf(buff, BIG_BUFFER_SIZE + 1, "*!*%s@*", my_username);
5625 			break;
5626 		default:
5627 			new_free(&dbuff);
5628 			new_free(&my_dot);
5629 			new_free(&colon);
5630 
5631 			RETURN_EMPTY;
5632 	}
5633 
5634 	new_free(&dbuff);
5635 	new_free(&my_dot);
5636 	new_free(&colon);
5637 
5638 	RETURN_STR(buff);
5639 }
5640 
BUILT_IN_FUNCTION(function_ischanvoice,input)5641 BUILT_IN_FUNCTION(function_ischanvoice, input)
5642 {
5643 	char	*nick;
5644 
5645 	GET_FUNC_ARG(nick, input);
5646 	RETURN_INT(is_chanvoice(input, nick));
5647 }
5648 
BUILT_IN_FUNCTION(function_ishalfop,input)5649 BUILT_IN_FUNCTION(function_ishalfop, input)
5650 {
5651 	char	*nick;
5652 
5653 	GET_FUNC_ARG(nick, input);
5654 	RETURN_INT(is_halfop(input, nick));
5655 }
5656 
BUILT_IN_FUNCTION(function_servports,input)5657 BUILT_IN_FUNCTION(function_servports, input)
5658 {
5659 	int	servnum = from_server;
5660 
5661 	if (*input)
5662 		GET_INT_ARG(servnum, input);
5663 
5664 	if (servnum == -1)
5665 		servnum = from_server;
5666 	if (servnum < 0 || servnum > server_list_size())
5667 		RETURN_EMPTY;
5668 
5669 	return malloc_sprintf(NULL, "%d %d", get_server_port(servnum),
5670 				  get_server_local_port(servnum));
5671 }
5672 
BUILT_IN_FUNCTION(function_chop,input)5673 BUILT_IN_FUNCTION(function_chop, input)
5674 {
5675 	char *buffer;
5676 	int howmany = 1;
5677 
5678 	if (my_atol(input))
5679 		GET_INT_ARG(howmany, input);
5680 
5681 	buffer = malloc_strdup(input);
5682 	chop(buffer, howmany);
5683 	return buffer;
5684 }
5685 
BUILT_IN_FUNCTION(function_getuid,input)5686 BUILT_IN_FUNCTION(function_getuid, input)
5687 {
5688 	RETURN_INT(getuid());
5689 }
5690 
BUILT_IN_FUNCTION(function_getgid,input)5691 BUILT_IN_FUNCTION(function_getgid, input)
5692 {
5693 	RETURN_INT(getgid());
5694 }
5695 
BUILT_IN_FUNCTION(function_getlogin,input)5696 BUILT_IN_FUNCTION(function_getlogin, input)
5697 {
5698 #ifdef HAVE_GETLOGIN
5699 	char *retval = getlogin();
5700 #else
5701 	char *retval = getenv("LOGNAME");
5702 #endif
5703 	RETURN_STR(retval);
5704 }
5705 
BUILT_IN_FUNCTION(function_getpgrp,input)5706 BUILT_IN_FUNCTION(function_getpgrp, input)
5707 {
5708 	RETURN_INT(getpgrp());
5709 }
5710 
BUILT_IN_FUNCTION(function_iscurchan,input)5711 BUILT_IN_FUNCTION(function_iscurchan, input)
5712 {
5713 	Window 	*w = NULL;
5714 	const char *chan;
5715 	char *arg;
5716 
5717 	GET_FUNC_ARG(arg, input);
5718 	while (traverse_all_windows(&w))
5719 	{
5720 		/*
5721 		 * Check to see if the channel specified is the current
5722 		 * channel on *any* window for the current server.
5723 		 */
5724 		if ((chan = get_echannel_by_refnum(w->refnum)) &&
5725 			!my_stricmp(arg, chan) && w->server == from_server)
5726 				RETURN_INT(1);
5727 	}
5728 
5729 	RETURN_INT(0);
5730 }
5731 
5732 /* <horny> can you add something for me?  */
BUILT_IN_FUNCTION(function_channel,input)5733 BUILT_IN_FUNCTION(function_channel, input)
5734 {
5735 	char *chan;
5736 
5737 	chan = next_func_arg(input, &input);	/* dont use GET_FUNC_ARG */
5738 	return scan_channel(chan);
5739 }
5740 
BUILT_IN_FUNCTION(function_pad,input)5741 BUILT_IN_FUNCTION(function_pad, input)
5742 {
5743 	int	width;
5744 	size_t	awidth, len;
5745 	unsigned char	*pads;
5746 	char	*retval;
5747 	size_t	retvalsiz;
5748 	int	codepoint;
5749 	int	j;
5750 
5751 	GET_INT_ARG(width, input);
5752 	GET_DWORD_ARG(pads, input);
5753 
5754 	if ((codepoint = next_code_point(CUC_PP &pads, 0)) == -1)
5755 		codepoint = ' ';	/* Sigh */
5756 
5757         if (width < 0)
5758 	{
5759 		width = -width;
5760 		j = 1;
5761 	}
5762 	else if (width > 0)
5763 		j = -1;
5764 	else
5765 		j = 0;
5766 
5767 	retval = fix_string_width(input, j, codepoint, width, 0);
5768 	RETURN_MSTR(retval);
5769 }
5770 
5771 
5772 /*
5773  * $remws(word word word / word word word)
5774  * Returns the right hand side unchanged, except that any word on the right
5775  * hand side that is also found on the left hand side will be removed.
5776  * Space is *not* retained.  So there.
5777  */
BUILT_IN_FUNCTION(function_remws,word)5778 BUILT_IN_FUNCTION(function_remws, word)
5779 {
5780 	char    *left = NULL,
5781 		*right = NULL,
5782 		*booya = NULL;
5783 	char	**lhs = NULL,
5784 		**rhs = NULL;
5785 	int	leftc,
5786 		rightc,
5787 		righti;
5788 	size_t	rvclue=0;
5789 
5790 	left = word;
5791 	if (!(right = strchr(word,'/')))
5792 		RETURN_EMPTY;
5793 
5794 	*right++ = 0;
5795 	leftc = splitw(left, &lhs, DWORD_DWORDS);
5796 	if (leftc > 0)
5797 		qsort((void *)lhs, leftc, sizeof(char *), sort_it);
5798 	rightc = splitw(right, &rhs, DWORD_DWORDS);
5799 
5800 	for (righti = 0; righti < rightc; righti++)
5801 	{
5802 		if (leftc <= 0 || !bsearch(&rhs[righti], lhs, leftc, sizeof(char *), sort_it))
5803 			malloc_strcat_word_c(&booya, space, rhs[righti], DWORD_DWORDS, &rvclue);
5804 	}
5805 
5806 	new_free((char **)&lhs);
5807 	new_free((char **)&rhs);
5808 
5809 	RETURN_MSTR(booya);
5810 }
5811 
BUILT_IN_FUNCTION(function_printlen,input)5812 BUILT_IN_FUNCTION(function_printlen, input)
5813 {
5814 	unsigned char *copy;
5815 	int	retval;
5816 
5817 	copy = new_normalize_string(input, 2, NORMALIZE);
5818 	retval = output_with_count(copy, 0, 0);
5819 	new_free(&copy);
5820 	RETURN_INT(retval);
5821 }
5822 
BUILT_IN_FUNCTION(function_stripansicodes,input)5823 BUILT_IN_FUNCTION(function_stripansicodes, input)
5824 {
5825         return new_normalize_string(input, 1, NORMALIZE);
5826 }
5827 
5828 /*
5829  * $isnumber(number base)
5830  * returns the empty value if nothing is passed to it
5831  * returns 0 if the value passed is not a number
5832  * returns 1 if the value passed is a number.
5833  *
5834  * The "base" number is optional and should be prefixed by the
5835  * 	character 'b'.  ala,   $isnumber(0x0F b16) for hexadecimal.
5836  *	the special base zero (b0) means to 'auto-detect'.  Base must
5837  *	be between 0 and 36, inclusive.  If not, it defaults to 0.
5838  */
BUILT_IN_FUNCTION(function_isnumber,input)5839 BUILT_IN_FUNCTION(function_isnumber, input)
5840 {
5841 	int	base = 0;
5842 	char	*endptr;
5843 	char	*barg;
5844 	char	*number = NULL;
5845 	int	segments = 0;
5846 
5847 	/*
5848 	 * See if the first arg is the base
5849 	 */
5850 	barg = next_arg(input, &input);
5851 
5852 	/*
5853 	 * If it is, the number is the 2nd arg
5854 	 */
5855 	if (barg && *barg == 'b' && is_number(barg + 1))
5856 	{
5857 		GET_FUNC_ARG(number, input);
5858 	}
5859 	/*
5860 	 * Otherwise, the first arg is the number,
5861 	 * the 2nd arg probably is the base
5862 	 */
5863 	else
5864 	{
5865 		number = barg;
5866 		barg = next_arg(input, &input);
5867 	}
5868 
5869 	/*
5870 	 * If the user specified a base, parse it.
5871 	 * Must be between 0 and 36.
5872 	 */
5873 	if (barg && *barg == 'b')
5874 	{
5875 		base = my_atol(barg + 1);
5876 		if (base < 0 || base > 36)
5877 			base = 0;
5878 	}
5879 
5880 	/*
5881 	 * Must have specified a number, though.
5882 	 */
5883 	RETURN_IF_EMPTY(number);
5884 
5885 	/* XXX Why doesn't this use strtoimax? */
5886 	strtol(number, &endptr, base);
5887 	/*
5888 	 * You don't have to remind me that endptr can't be null
5889 	 * here.  This is just to make clang be quiet
5890 	 */
5891 	if (!endptr)
5892 		RETURN_INT(0);
5893 
5894 	if (endptr > number)
5895 		segments++;
5896 	if (*endptr == '.')
5897 	{
5898 		if (base == 0)
5899 			base = 10;	/* XXX So i'm chicken. */
5900 		number = endptr + 1;
5901 		/* XXX Why doesn't this use strtoimax? */
5902 		strtol(number, &endptr, base);
5903 		if (endptr > number)
5904 			segments++;
5905 	}
5906 
5907 	if (*endptr || segments == 0)
5908 		RETURN_INT(0);
5909 	else
5910 		RETURN_INT(1);
5911 }
5912 
5913 /*
5914  * $rest(index string)
5915  * 	Returns 'string' starting at the 'index'th character
5916  * $rest(string)
5917  * 	Returns 'string' without the first character.
5918  * Just like $restw() does for words.
5919  * Technically, 'index' can be negative, but this isn't documented.
5920  */
BUILT_IN_FUNCTION(function_rest,input)5921 BUILT_IN_FUNCTION(function_rest, input)
5922 {
5923 	int	start = 1;
5924 	char *	test_input;
5925 	int	len;
5926 	char *	x;
5927 
5928 	/*
5929 	 * XXX - The 'index' argument should not be optional.
5930 	 * So we have to determine if the 1st word is a number.
5931 	 */
5932 	test_input = input;
5933 	parse_number(&test_input);
5934 	if (test_input > input && my_isspace(*test_input))
5935 		GET_INT_ARG(start, input);
5936 
5937 	len = quick_code_point_count(input);
5938 
5939 	if (start >= len || -start >= len)
5940 		RETURN_EMPTY;
5941 	else if (start >= 0)
5942 	{
5943 		/* Skip 'start' code points at the start */
5944 		x = input;
5945 		while (start-- > 0)
5946 			next_code_point(CUC_PP &x, 1);
5947 		RETURN_STR(x);
5948 	}
5949 	else
5950 	{
5951 		/* Walk 'start' code points from the end */
5952 		x = input + strlen(input);
5953 		while (start++ < 0)
5954 			previous_code_point(input, CUC_PP &x);
5955 		RETURN_STR(x);
5956 	}
5957 }
5958 
5959 
5960 /* Written by panasync */
5961 /* Re-written by CE */
5962 #define GET_UNIFIED_ARRAY_FUNCTION(thisfn, nextfn)	\
5963 BUILT_IN_FUNCTION( thisfn , input)			\
5964 {							\
5965 	char *s = NULL;					\
5966 	char *r;					\
5967 	char *ret = NULL;				\
5968 	char **subresults;			\
5969 	size_t	clue = 0;				\
5970 	int	howmany = 0;				\
5971 							\
5972 	if (!input || !*input)					\
5973 	{							\
5974 		subresults = nextfn ("*", &howmany, 0, 0, 0);	\
5975 		ret = unsplitw(&subresults, howmany, DWORD_DWORDS);	\
5976 		RETURN_MSTR(ret);				\
5977 	}							\
5978 								\
5979 	while ((s = next_arg(input, &input)))			\
5980 	{							\
5981 		subresults = nextfn (s, &howmany, 0, 0, 0);		\
5982 		r = unsplitw(&subresults, howmany, DWORD_DWORDS);	\
5983 		malloc_strcat_wordlist_c(&ret, space, r, &clue);	\
5984 		new_free(&r);					\
5985 	}							\
5986 								\
5987 	RETURN_MSTR(ret);					\
5988 }
5989 
GET_UNIFIED_ARRAY_FUNCTION(function_getsets,pmatch_builtin_variables)5990 GET_UNIFIED_ARRAY_FUNCTION(function_getsets, pmatch_builtin_variables)
5991 GET_UNIFIED_ARRAY_FUNCTION(function_getcommands, pmatch_builtin_commands)
5992 GET_UNIFIED_ARRAY_FUNCTION(function_getfunctions, pmatch_builtin_functions)
5993 
5994 /*
5995  * XXX This should be an alias.
5996  */
5997 BUILT_IN_FUNCTION(function_stripc, input)
5998 {
5999 	char	*output;
6000 
6001 	output = new_normalize_string(input, 1, STRIP_COLOR);
6002 	RETURN_MSTR(output);
6003 }
6004 
BUILT_IN_FUNCTION(function_stripcrap,input)6005 BUILT_IN_FUNCTION(function_stripcrap, input)
6006 {
6007 	char	*how;
6008 	int	mangle;
6009 	char	*output;
6010 
6011 	GET_FUNC_ARG(how, input);
6012 	mangle = parse_mangle(how, 0, NULL);
6013 
6014 	output = new_normalize_string(input, 1, mangle);
6015 	RETURN_MSTR(output);
6016 }
6017 
6018 /*
6019  * Date: Wed, 2 Sep 1998 18:20:34 -0500 (CDT)
6020  * From: CrackBaby <crack@feeding.frenzy.com>
6021  */
6022 /*
6023  * $getopt(<optopt var> <optarg var> <opt string> <argument list>)
6024  *
6025  * Processes a list of switches and args.  Returns one switch char each time
6026  * it's called, sets $<optopt var> to the char, and sets $<optarg var> to the
6027  * value of the next argument if the switch needs one.
6028  *
6029  * Syntax for <opt string> and <argument list> should be identical to
6030  * getopt(3).  A ':' in <opt string> is a required argument, and a "::" is an
6031  * optional one.  A '-' by itself in <argument list> is a null argument, and
6032  * switch parsing stops after a "--"
6033  *
6034  * If a switch requires an argument, but the argument is missing, $getopt()
6035  * returns a '-'  If a switch is given which isn't in the opt string, the
6036  * function returns a '!'  $<optopt var> is still set in both cases.
6037  *
6038  * Example usage:
6039  * while (option = getopt(optopt optarg "ab:c:" $*)) {
6040  *	switch ($option) {
6041  * 		(a) {echo * option "$optopt" used}
6042  * 		(b) {echo * option "$optopt" used - $optarg}
6043  * 		(c) {echo * option "$optopt" used - $optarg}
6044  * 		(!) {echo * option "$optopt" is an invalid option}
6045  * 		(-) {echo * option "$optopt" is missing an argument}
6046  *	}
6047  * }
6048  */
BUILT_IN_FUNCTION(function_getopt,input)6049 BUILT_IN_FUNCTION(function_getopt, input)
6050 {
6051 static	char	*switches = NULL,
6052 		*args = NULL,
6053 		*last_input = NULL,
6054 		*swptr = NULL,
6055 		*aptr = NULL;
6056 static	size_t	input_size = 0;
6057 	char	*optopt_var,
6058 		*optarg_var,
6059 		*optstr,
6060 		*optptr;
6061 	char	*extra_args = NULL;
6062 	int	arg_flag = 0;
6063 	int	new_input = 0;
6064 	char	tmpstr[2];
6065 
6066 	/*
6067 	 * If this is not the same argument set as last time, re-initialize
6068 	 * the state variables to the new input data.
6069 	 */
6070 	if (last_input == NULL || strcmp(last_input, input))
6071 	{
6072 		input_size = strlen(input);
6073 		malloc_strcpy(&last_input, input);
6074 
6075 		new_realloc((void **)&switches, input_size * 2 + 1);
6076 		*switches = 0;
6077 		swptr = switches;
6078 
6079 		new_realloc((void **)&args, input_size + 1);
6080 		*args = 0;
6081 		aptr = args;
6082 
6083 		extra_args = (char *)alloca(input_size * 2 + 1);
6084 		*extra_args = 0;
6085 
6086 		new_input = 1;
6087 	}
6088 
6089 	/* Three arguments are required -- two variables and an op-string */
6090 	GET_FUNC_ARG(optopt_var, input);
6091 	GET_FUNC_ARG(optarg_var, input);
6092 	GET_DWORD_ARG(optstr, input);
6093 	if (!optopt_var || !optarg_var || !optstr)
6094 		RETURN_EMPTY;
6095 
6096 	if (new_input)
6097 	{
6098 	    char *	tmp;
6099 
6100 	    /* Process each word in the input string */
6101 	    while ((tmp = new_next_arg(input, &input)))
6102 	    {
6103 		/* Word is a switch or a group of switches */
6104 		if (tmp[0] == '-' &&
6105 		    tmp[1] && tmp[1] != '-' &&
6106 		    arg_flag == 0)
6107 		{
6108 		    /* Look at each char after the '-' */
6109 		    for (++tmp; *tmp; tmp++)
6110 		    {
6111 			/*
6112 			 * If the char is found in optstr and doesn't need
6113 			 * an argument, it's added to the switches list.
6114 		   	 * switches are stored as "xy" where x is the switch
6115 			 * letter and y is:
6116 			 *	'_' normal switch
6117 			 *	':' switch with arg
6118 			 *	'-' switch with missing arg
6119 			 *	'!' unrecognized switch
6120 			 */
6121 			*swptr++ = *tmp;
6122 
6123 			/* char is a valid switch */
6124 			if ((optptr = strchr(optstr, *tmp)))
6125 			{
6126 			    /* char requires an argument */
6127 			    if (optptr[1] == ':')
6128 			    {
6129 				/* -xfoo, argument is "foo" */
6130 				if (tmp[1])
6131 				{
6132 					tmp++;
6133 					strlcat(args, tmp, input_size + 1);
6134 					strlcat(args, " ", input_size + 1);
6135 					*swptr++ = ':';
6136 					break;
6137 				}
6138 				/*
6139 				 * Otherwise note that the next word in
6140 				 * the input is our arg.
6141 				 */
6142 				else if (*(optptr + 2) == ':')
6143 					arg_flag = 2;
6144 				else
6145 					arg_flag = 1;
6146 			    }
6147 			    /* Switch needs no argument */
6148 			    else
6149 				*swptr++ = '_';
6150 			}
6151 			/* Switch is not recognized */
6152 			else
6153 			    *swptr++ = '!';
6154 		    }
6155 		}
6156 		else
6157 		{
6158 		    /* Everything after a "--" is added to extra_args */
6159 		    if (*tmp == '-' && tmp[1] == '-')
6160 		    {
6161 			tmp += 2;
6162 			strlcat(extra_args, tmp, input_size * 2 + 1);
6163 			strlcat(extra_args, input, input_size * 2 + 1);
6164 			*input = 0;
6165 		    }
6166 
6167 		    /* A '-' by itself is a null arg  */
6168 		    else if (*tmp == '-' && !*(tmp + 1))
6169 		    {
6170 			if (arg_flag == 1)
6171 				*swptr++ = '-';
6172 			else if (arg_flag == 2)
6173 				*swptr++ = '_';
6174 			*tmp = 0;
6175 			arg_flag = 0;
6176 		    }
6177 
6178 		    /*
6179 		     * If the word doesn't start with a '-,' it must be
6180 		     * either the argument of a switch or just extra info.
6181 		     */
6182 		    else if (arg_flag)
6183 		    {
6184 			/*
6185 			 * If arg_flag is positive, we've processes
6186 			 * a switch which requires an arg and we can
6187 			 * just tack the word onto the end of args[]
6188 			 */
6189 			strlcat(args, tmp, input_size + 1);
6190 			strlcat(args, " ", input_size + 1);
6191 			*swptr++ = ':';
6192 			arg_flag = 0;
6193 		    }
6194 		    else
6195 		    {
6196 			/*
6197 			 * If not, we'll put it aside and add it to
6198 			 * args[] after all the switches have been
6199 			 * looked at.
6200 			 */
6201 			strlcat(extra_args, tmp, input_size * 2 + 1);
6202 			strlcat(extra_args, " ", input_size * 2 + 1);
6203 		    }
6204 		}
6205 	    }
6206 
6207 	    /*
6208 	     * If we're expecting an argument to a switch, but we've
6209 	     * reached the end of our input, the switch is missing its
6210 	     * arg.
6211 	     */
6212 	    if (arg_flag == 1)
6213 		*swptr++ = '-';
6214 	    else if (arg_flag == 2)
6215 		*swptr++ = '_';
6216 	    strlcat(args, extra_args, input_size);
6217 
6218 	    *swptr = 0;
6219 	    swptr = switches;
6220 	}
6221 
6222 	/* Terminating condition */
6223 	if (*swptr == 0)
6224 	{
6225 		add_var_alias(optopt_var, NULL, 0);
6226 		add_var_alias(optarg_var, aptr, 0);
6227 
6228 		*switches = 0;
6229 		*args = 0;
6230 		swptr = switches;
6231 		aptr = args;
6232 		new_free(&last_input);		/* Reset the system */
6233 
6234 		RETURN_EMPTY;
6235 	}
6236 
6237 	tmpstr[0] = *swptr++;
6238 	tmpstr[1] = 0;
6239 
6240 	switch (*swptr++)
6241 	{
6242 		case '_':
6243 			add_var_alias(optopt_var, tmpstr, 0);
6244 			add_var_alias(optarg_var, NULL, 0);
6245 			RETURN_STR(tmpstr);
6246 		case ':':
6247 			add_var_alias(optopt_var, tmpstr, 0);
6248 			add_var_alias(optarg_var, next_arg(aptr, &aptr), 0);
6249 			/* add_var_alias(optarg_var, args, 0); */
6250 			RETURN_STR(tmpstr);
6251 		case '-':
6252 			add_var_alias(optopt_var, tmpstr, 0);
6253 			add_var_alias(optarg_var, NULL, 0);
6254 			RETURN_STR("-");
6255 		case '!':
6256 			add_var_alias(optopt_var, tmpstr, 0);
6257 			add_var_alias(optarg_var, NULL, 0);
6258 			RETURN_STR("!");
6259 		default:
6260 			/* This shouldn't happen */
6261 			yell("*** getopt switch broken: %s", tmpstr);
6262 			RETURN_EMPTY;
6263 	}
6264 }
6265 
BUILT_IN_FUNCTION(function_isaway,input)6266 BUILT_IN_FUNCTION(function_isaway, input)
6267 {
6268         int     refnum = -1;
6269 
6270         if (!*input)
6271                 refnum = from_server;
6272         else
6273                 GET_INT_ARG(refnum, input);
6274 
6275         if (get_server_away(refnum))
6276                 RETURN_INT(1);
6277 
6278         RETURN_INT(0);
6279 }
6280 
6281 
6282 /*
6283  * KnghtBrd requested this. Returns the length of the longest word
6284  * in the word list.
6285  */
BUILT_IN_FUNCTION(function_maxlen,input)6286 BUILT_IN_FUNCTION(function_maxlen, input)
6287 {
6288 	size_t	maxlen = 0;
6289 	size_t	len;
6290 	char	*arg;
6291 
6292 	while (input && *input)
6293 	{
6294 		GET_FUNC_ARG(arg, input)
6295 
6296 		if ((len = quick_code_point_count(arg)) > maxlen)
6297 			maxlen = len;
6298 	}
6299 
6300 	RETURN_INT(maxlen);
6301 }
6302 
6303 
6304 /*
6305  * KnghtBrd requested this.  It returns a string that is the leading
6306  * substring of ALL the words in the word list.   If no string is common
6307  * to all words in the word list, then the FALSE value is returned.
6308  *
6309  * I didn't give this a whole lot of thought about the optimal way to
6310  * do this, so if you have a better idea, let me know.  All i do here is
6311  * start at 1 character and walk my way longer and longer until i find a
6312  * length that is not common, and then i stop there.  So the expense of this
6313  * algorithm is (M*N) where M is the number of words and N is the number of
6314  * characters they have in common when we're all done.
6315  */
BUILT_IN_FUNCTION(function_prefix,input)6316 BUILT_IN_FUNCTION(function_prefix, input)
6317 {
6318 	char	**words = NULL;
6319 	int	numwords;
6320 	int	max_len;
6321 	int	len_index;
6322 	int	word_index;
6323 	char	*retval = NULL;
6324 
6325 	RETURN_IF_EMPTY(input);
6326 
6327 	numwords = splitw(input, &words, DWORD_DWORDS);
6328 	if (numwords == 0)
6329 		RETURN_EMPTY;		/* "Oh bother," said pooh. */
6330 
6331 	max_len = strlen(words[0]);
6332 
6333 	for (len_index = 1; len_index <= max_len; len_index++)
6334 	{
6335 	    for (word_index = 1; word_index < numwords; word_index++)
6336 	    {
6337 		/*
6338 		 * Basically, our stopping point is the first time we
6339 		 * see a string that does NOT share the first "len_index"
6340 		 * characters with words[0] (which is chosen arbitrarily).
6341 		 * As long as all words start with the same leading chars,
6342 		 * we march along trying longer and longer substrings,
6343 		 * until one of them doesnt work, and then we exit right
6344 		 * there.
6345 		 */
6346 		if (my_strnicmp(words[0], words[word_index], len_index))
6347 		{
6348 			retval = new_malloc(len_index + 1);
6349 			strlcpy(retval, words[0], len_index);
6350 			new_free((char **)&words);
6351 			return retval;
6352 		}
6353 	    }
6354 	}
6355 
6356 	malloc_strcat_word(&retval, space, words[0], DWORD_DWORDS);
6357 	new_free((char **)&words);
6358 	RETURN_MSTR(retval);
6359 }
6360 
6361 /*
6362  * I added this so |rain| would stop asking me for it. ;-)  I reserve the
6363  * right to change the implementation of this in the future.
6364  */
BUILT_IN_FUNCTION(function_functioncall,input)6365 BUILT_IN_FUNCTION(function_functioncall, input)
6366 {
6367 	/*
6368 	 * These two variables are supposed to be private inside alias.c
6369 	 * Even though i export them here, it is EVIL EVIL EVIL to use
6370 	 * them outside of alias.c.  So do not follow my example and extern
6371 	 * these for your own use -- because who knows, one day i may make
6372 	 * them static and your use of them will not be my fault! :p
6373 	 */
6374 	extern	int	wind_index;		 	/* XXXXX Ick */
6375 	extern	int	last_function_call_level;	/* XXXXX Ick */
6376 
6377 	/*
6378 	 * If the last place we slapped down a FUNCTION_RETURN is in the
6379 	 * current operating stack frame, then we're in a user-function
6380 	 * call.  Otherwise we're not.  Pretty simple.
6381 	 */
6382 	if (last_function_call_level == wind_index)
6383 		RETURN_INT(1);
6384 	else
6385 		RETURN_INT(0);
6386 }
6387 
6388 /*
6389  * Creates a 32 bit hash value for a specified string
6390  * Usage: $hash_32bit(word length)
6391  *
6392  * "word" is the value to be hashed, and "length" is the number
6393  * of characters to hash.  If "length" is omitted or not 0 < length <= 64,
6394  * then it defaults to 20.
6395  *
6396  * This was contributed by srfrog (srfrog@lice.com).  I make no claims about
6397  * the usefulness of this hashing algorithm.
6398  *
6399  * The name was chosen just in case we decided to implement other hashing
6400  * algorithms for different sizes or types of return values.
6401  */
BUILT_IN_FUNCTION(function_hash_32bit,input)6402 BUILT_IN_FUNCTION(function_hash_32bit, input)
6403 {
6404 	char *		u_word;
6405 	char *		word;
6406 	char		c;
6407 	unsigned	bit_hi = 0;
6408 	int		bit_low = 0;
6409 	int		h_val;
6410 	int		len;
6411 
6412 	word = new_next_arg(input, &input);
6413 	len = my_atol(safe_new_next_arg(input, &input));
6414 	if (!word || !*word)
6415 		word = input;
6416 
6417 	if (len <= 0 || len > 64)
6418 		len = 20;
6419 
6420 	for (u_word = word; *u_word && len > 0; ++u_word, --len)
6421 	{
6422 		c = tolower(*u_word);
6423 		bit_hi = (bit_hi << 1) + c;
6424 		bit_low = (bit_low >> 1) + c;
6425 	}
6426 	h_val = ((bit_hi & 8191) << 3) + (bit_low & 0x7);
6427 	RETURN_INT(h_val);
6428 }
6429 
6430 
6431 /*
6432  * Usage: $indextoword(position text)
6433  *
6434  * This function returns the word-number (counting from 0) that contains
6435  * the 'position'th character in 'text' (counting from 0), such that the
6436  * following expression:
6437  *
6438  *		$word($indextoword($index(. $*) $*) $*)
6439  *
6440  * would return the *entire word* in which the first '.' in $* is found.
6441  * If 'position' is -1, or past the end of the string, -1 is returned as
6442  * an error flag.  Note that this function can be used anywhere that a
6443  * word-number would be used, such as $chngw().  The empty value is returned
6444  * if a syntax error returns.
6445  *
6446  * DONT ASK ME to support 'position' less than 0 to indicate a position
6447  * from the end of the string.  Either that can be supported, or you can
6448  * directly use $index() as the first argument; you can't do both.  I chose
6449  * the latter intentionally.  If you really want to calculate from the end of
6450  * your string, then just add your negative value to $strlen(string) and
6451  * pass that.
6452  *
6453  * 10/01/02 At the suggestion of fudd and rain, if pos == len, then return
6454  * the number of words in 'input' because if the cursor is at the end of the
6455  * input prompt and you do $indextoword($curpos() $L), right now it would
6456  * return EMPTY but it should return the word number right before the cursor.
6457  */
BUILT_IN_FUNCTION(function_indextoword,input)6458 BUILT_IN_FUNCTION(function_indextoword, input)
6459 {
6460 	unsigned char *s;
6461 	int	pos, count;
6462 	int	i;
6463 
6464 	GET_INT_ARG(pos, input);
6465 	count = quick_code_point_count(input);
6466 	if (pos < 0 || pos > count)
6467 		RETURN_EMPTY;
6468 
6469 	s = input;
6470 	for (i = 0; i < pos; i++)
6471 		next_code_point(CUC_PP &s, 1);
6472 
6473 	/*
6474 	 * This is a special case to handle multiple runs of words.
6475 	 * According to our wacky word rules, the *FIRST* space after
6476 	 * a word belongs to the previous word; but the second and
6477 	 * subsequent spaces belong to the following word. (urgh).
6478 	 *
6479 	 * In order to "fake" this, we put a non-space character right
6480 	 * here to make it "count" as a word if it isn't the first one.
6481 	 * But naturally, we only do that if we're not at the eos.
6482 	 */
6483 	if (*s)
6484 	{
6485 		*s = 'x';
6486 		*++s = 0;
6487 	}
6488 
6489 	RETURN_INT(count_words(input, DWORD_DWORDS, "\"") - 1);
6490 }
6491 
BUILT_IN_FUNCTION(function_realpath,input)6492 BUILT_IN_FUNCTION(function_realpath, input)
6493 {
6494 	char	resolvedname[PATH_MAX];
6495 
6496 	if (!normalize_filename(input, resolvedname))
6497 		RETURN_STR(resolvedname);
6498 
6499 	RETURN_EMPTY;
6500 }
6501 
BUILT_IN_FUNCTION(function_ttyname,input)6502 BUILT_IN_FUNCTION(function_ttyname, input)
6503 {
6504 	char *retval = ttyname(0);
6505 	RETURN_STR(retval);
6506 }
6507 
6508 /*
6509  * $insert(num word string of text)
6510  * returns "string of text" such that "word" begins in the "num"th position
6511  * in the string ($index()-wise)
6512  * NOTE: Positions are numbered from 0
6513  * EX: $insert(3 baz foobarbooya) returns "foobazbarbooya"
6514  */
BUILT_IN_FUNCTION(function_insert,input)6515 BUILT_IN_FUNCTION(function_insert, input)
6516 {
6517 	unsigned char *s, *p;
6518 	char 	*first_part, *second_part, *extra;
6519 	int	where;
6520 	char	*retval;
6521 
6522 	GET_INT_ARG(where, input);
6523 	GET_DWORD_ARG(extra, input);
6524 
6525 	s = first_part = input;
6526 	for (;;)
6527 	{
6528 		if (where <= 0)
6529 			break;
6530 		if (!*s)
6531 			break;
6532 
6533 		while (next_code_point(CUC_PP &s, 0) == -1)
6534 			s++;
6535 		where--;
6536 	}
6537 
6538 	second_part = LOCAL_COPY(s);
6539 	*s = 0;
6540 
6541 	retval = malloc_strdup3(first_part, extra, second_part);
6542 	RETURN_MSTR(retval);
6543 }
6544 
BUILT_IN_FUNCTION(function_stat,words)6545 BUILT_IN_FUNCTION(function_stat, words)
6546 {
6547 	Filename expanded;
6548 	char *	filename;
6549 	char 	retval[BIG_BUFFER_SIZE];
6550 	Stat	sb;
6551 
6552 
6553 	if (!(filename = new_next_arg(words, &words)))
6554 		filename = words;
6555 
6556 	if (!filename || !*filename)
6557 		RETURN_INT(-1);
6558 
6559 	if (normalize_filename(filename, expanded))
6560 		RETURN_INT(-1);
6561 
6562 	if (stat(expanded, &sb) < 0)
6563 		RETURN_EMPTY;
6564 
6565 	snprintf(retval, BIG_BUFFER_SIZE,
6566 		"%d %d %o %d %d %d %d "
6567 		UINTMAX_FORMAT" "UINTMAX_FORMAT" "UINTMAX_FORMAT" "
6568 		INTMAX_FORMAT" "INTMAX_FORMAT" "INTMAX_FORMAT,
6569 		(int)	sb.st_dev,		/* device number */
6570 		(int)	sb.st_ino,		/* Inode number */
6571 		(int)	sb.st_mode,		/* Permissions */
6572 		(int)	sb.st_nlink,		/* Hard links */
6573 		(int)	sb.st_uid,		/* Owner UID */
6574 		(int)	sb.st_gid,		/* Owner GID */
6575 		(int)	sb.st_rdev,		/* Device type */
6576 		(uintmax_t)sb.st_size,		/* Size of file */
6577 		(uintmax_t)sb.st_blksize,	/* Size of each block */
6578 		(uintmax_t)sb.st_blocks,	/* Blocks used in file */
6579 		(intmax_t)sb.st_atime,		/* Last-access time */
6580 		(intmax_t)sb.st_mtime,		/* Last-modified time */
6581 		(intmax_t)sb.st_ctime		/* Last-change time */
6582 			);
6583 
6584 	RETURN_STR(retval);
6585 }
6586 
BUILT_IN_FUNCTION(function_isdisplaying,input)6587 BUILT_IN_FUNCTION(function_isdisplaying, input)
6588 {
6589 	RETURN_INT(window_display);
6590 }
6591 
BUILT_IN_FUNCTION(function_getcap,input)6592 BUILT_IN_FUNCTION(function_getcap, input)
6593 {
6594 	char *	type;
6595 
6596 	GET_FUNC_ARG(type, input);
6597 	if (!my_stricmp(type, "TERM"))
6598 	{
6599 		const char *	retval;
6600 		char *	term = NULL;
6601 		int	querytype = 0;
6602 		int	mangle = 1;
6603 
6604 		GET_FUNC_ARG(term, input);
6605 		if (*input)
6606 			GET_INT_ARG(querytype, input);
6607 		if (*input)
6608 			GET_INT_ARG(mangle, input);
6609 
6610 		if (!term)			/* This seems spurious */
6611 			RETURN_EMPTY;
6612 
6613 		if ((retval = get_term_capability(term, querytype, mangle)))
6614 			RETURN_STR(retval);
6615 
6616 		RETURN_EMPTY;
6617 	}
6618 
6619 	RETURN_EMPTY;
6620 }
6621 
BUILT_IN_FUNCTION(function_getset,input)6622 BUILT_IN_FUNCTION(function_getset, input)
6623 {
6624 	char *retval = make_string_var(input);
6625 	RETURN_MSTR(retval);
6626 }
6627 
BUILT_IN_FUNCTION(function_builtin,input)6628 BUILT_IN_FUNCTION(function_builtin, input)
6629 {
6630 	char *(*efunc) (void) = NULL;
6631 	IrcVariable *sfunc;
6632 
6633 	get_var_alias(input, &efunc, &sfunc);
6634 	if (efunc == NULL)
6635 		RETURN_EMPTY;
6636 	return efunc();
6637 }
6638 
6639 /*
6640  * Returns what the status-line %F expando would return, except it is
6641  * space-seperated.  Basically, it is all the windows that are hidden,
6642  * that have /window notify on, and have output since they were hidden.
6643  */
BUILT_IN_FUNCTION(function_notifywindows,input)6644 BUILT_IN_FUNCTION(function_notifywindows, input)
6645 {
6646 	char *	retval = NULL;
6647 	size_t	rvclue=0;
6648 	Window *window;
6649 
6650 	window = NULL;
6651 	while (traverse_all_windows(&window))
6652 		if (window->notified)
6653 			malloc_strcat_word_c(&retval, space, ltoa(window->refnum), DWORD_NO, &rvclue);
6654 
6655 	RETURN_MSTR(retval);
6656 }
6657 
BUILT_IN_FUNCTION(function_loadinfo,input)6658 BUILT_IN_FUNCTION(function_loadinfo, input)
6659 {
6660 	return malloc_sprintf(NULL, "%d %s %s %s" , current_line(), current_filename()
6661 			, current_loader(), current_package() ? current_package() : empty_string);
6662 }
6663 
BUILT_IN_FUNCTION(function_wordtoindex,input)6664 BUILT_IN_FUNCTION(function_wordtoindex, input)
6665 {
6666 	int		wordnum;
6667 	const char *	ptr;
6668 	int		cpidx;
6669 
6670 	GET_INT_ARG(wordnum, input);
6671 	real_move_to_abs_word(input, &ptr, wordnum, DWORD_DWORDS, "\"");
6672 	cpidx = quick_code_point_index((const unsigned char *)input, (const unsigned char *)ptr);
6673 	RETURN_INT(cpidx);
6674 }
6675 
6676 /*
6677  * These four functions contributed by
6678  * B. Thomas Frazier (tfrazier@mjolnir.gisystems.net)
6679  * On December 12, 2000.
6680  */
6681 
BUILT_IN_FUNCTION(function_iptolong,word)6682 BUILT_IN_FUNCTION(function_iptolong, word)
6683 {
6684 	ISA	addr;
6685 	char *	dotted_quad;
6686 
6687 	addr.sin_family = AF_INET;
6688 	GET_FUNC_ARG(dotted_quad, word);
6689 	if (inet_strton(dotted_quad, NULL, (SA *)&addr, AI_NUMERICHOST))
6690 		RETURN_EMPTY;
6691 
6692 	return malloc_sprintf(NULL, UINTMAX_FORMAT,
6693 				(uintmax_t)ntohl(addr.sin_addr.s_addr));
6694 }
6695 
BUILT_IN_FUNCTION(function_longtoip,word)6696 BUILT_IN_FUNCTION(function_longtoip, word)
6697 {
6698 	char *	ip32;
6699 	SS	addr;
6700 	char	retval[256];
6701 
6702 	GET_FUNC_ARG(ip32, word);
6703 	((SA *)&addr)->sa_family = AF_INET;
6704 	if (inet_strton(ip32, NULL, (SA *)&addr, AI_NUMERICHOST))
6705 		RETURN_EMPTY;
6706 	inet_ntostr((SA *)&addr, retval, 256, NULL, 0, NI_NUMERICHOST);
6707 	RETURN_STR(retval);
6708 }
6709 
BUILT_IN_FUNCTION(function_isencrypted,input)6710 BUILT_IN_FUNCTION(function_isencrypted, input)
6711 {
6712 	int	sval = from_server;
6713 
6714 	if (*input)
6715 		GET_INT_ARG(sval, input);
6716 
6717 	/* garbage in, garbage out. */
6718 	if (sval < 0 || sval >= server_list_size())
6719 		RETURN_INT(0);
6720 
6721 	/* Check if it is encrypted connection */
6722 	RETURN_INT(get_server_ssl_enabled(sval));
6723 }
6724 
BUILT_IN_FUNCTION(function_ssl,words)6725 BUILT_IN_FUNCTION(function_ssl, words)
6726 {
6727 	RETURN_INT(client_ssl_enabled());
6728 }
6729 
BUILT_IN_FUNCTION(function_cipher,input)6730 BUILT_IN_FUNCTION(function_cipher, input)
6731 {
6732 	int     	sval = from_server;
6733 	const char *	ret;
6734 
6735 	if (*input)
6736 		GET_INT_ARG(sval, input);
6737 
6738 	if (sval < 0 || sval >= server_list_size())
6739 		RETURN_EMPTY;
6740 
6741 	ret = get_server_ssl_cipher(sval);
6742 	RETURN_STR(ret);
6743 }
6744 
6745 #define MATH_RETVAL(x)						\
6746 	{							\
6747 		if (errno == 0) 				\
6748 			RETURN_FLOAT(x);			\
6749 		else if (errno == EDOM) 			\
6750 			RETURN_STR("DOM");			\
6751 		else if (errno == ERANGE) 			\
6752 			RETURN_STR("RANGE");			\
6753 		else						\
6754 			RETURN_EMPTY;				\
6755 	}
6756 
6757 #define MATH_FUNCTION(x, y) 					\
6758 	BUILT_IN_FUNCTION( x , word) 				\
6759 	{ 							\
6760 		double	num; 					\
6761 								\
6762 		GET_FLOAT_ARG(num, word); 			\
6763 		errno = 0; 					\
6764 		num = y (num); 					\
6765 		MATH_RETVAL(num)				\
6766 	}
6767 
6768 #define MATH_FUNCTION2(x, y) 					\
6769 	BUILT_IN_FUNCTION( x , word) 				\
6770 	{ 							\
6771 		int	level;					\
6772 		double	num;					\
6773 								\
6774 		errno = 0;					\
6775 		GET_INT_ARG(level, word);			\
6776 		GET_FLOAT_ARG(num, word);			\
6777 		num = y (level, num);				\
6778 		MATH_RETVAL(num)				\
6779 	}
6780 
6781 
MATH_FUNCTION(function_abs,fabs)6782 MATH_FUNCTION(function_abs, fabs)
6783 MATH_FUNCTION(function_ceil, ceil)
6784 MATH_FUNCTION(function_floor, floor)
6785 
6786 MATH_FUNCTION(function_exp, exp)
6787 MATH_FUNCTION(function_log, log)
6788 MATH_FUNCTION(function_log10, log10)
6789 
6790 MATH_FUNCTION(function_cosh, cosh)
6791 MATH_FUNCTION(function_sinh, sinh)
6792 MATH_FUNCTION(function_tanh, tanh)
6793 MATH_FUNCTION(function_acosh, acosh)
6794 MATH_FUNCTION(function_asinh, asinh)
6795 MATH_FUNCTION(function_atanh, atanh)
6796 
6797 MATH_FUNCTION(function_cos, cos)
6798 MATH_FUNCTION(function_sin, sin)
6799 MATH_FUNCTION(function_tan, tan)
6800 MATH_FUNCTION(function_acos, acos)
6801 MATH_FUNCTION(function_asin, asin)
6802 
6803 MATH_FUNCTION2(function_jn, jn)
6804 MATH_FUNCTION2(function_yn, yn)
6805 
6806 BUILT_IN_FUNCTION(function_atan, word)
6807 {
6808 	double	num, num1, num2;
6809 
6810 	errno = 0;
6811 	GET_FLOAT_ARG(num1, word);
6812 	if (word && *word)
6813 	{
6814 		GET_FLOAT_ARG(num2, word);
6815 		num = atan2(num1, num2);
6816 	}
6817 	else
6818 		num = atan(num1);
6819 
6820 	MATH_RETVAL(num)
6821 }
6822 
6823 #ifdef HAVE_PERL
6824 
BUILT_IN_FUNCTION(function_perl,input)6825 BUILT_IN_FUNCTION(function_perl, input)
6826 {
6827 	return perleval ( input );
6828 }
6829 
BUILT_IN_FUNCTION(function_perlcall,input)6830 BUILT_IN_FUNCTION(function_perlcall, input)
6831 {
6832 	char *sub=NULL;
6833 
6834 	GET_DWORD_ARG(sub, input);
6835 	return perlcall ( sub, NULL, NULL, -1, input );
6836 }
6837 
BUILT_IN_FUNCTION(function_perlxcall,input)6838 BUILT_IN_FUNCTION(function_perlxcall, input)
6839 {
6840 	long item=0;
6841 	char *sub=NULL, *in=NULL, *out=NULL;
6842 
6843 	GET_DWORD_ARG(sub, input);
6844 	if (input && *input) GET_DWORD_ARG(in, input);
6845 	if (input && *input) GET_DWORD_ARG(out, input);
6846 	if (input && *input) GET_INT_ARG(item, input);
6847 	return perlcall ( sub, in, out, item, input );
6848 }
6849 
6850 #endif
6851 
6852 #ifdef HAVE_TCL
6853 
BUILT_IN_FUNCTION(function_tcl,input)6854 BUILT_IN_FUNCTION(function_tcl, input)
6855 {
6856 	return tcleval ( input );
6857 }
6858 
6859 #endif
6860 
BUILT_IN_FUNCTION(function_unsplit,input)6861 BUILT_IN_FUNCTION(function_unsplit, input)
6862 {
6863 	char *	sep;
6864 	char *	word;
6865 	char *	retval = NULL;
6866 	size_t	clue = 0;
6867 
6868 	GET_DWORD_ARG(sep, input);
6869 	while ((word = new_next_arg(input, &input)))
6870 		malloc_strcat_word_c(&retval, sep, word, DWORD_NO, &clue);
6871 	RETURN_MSTR(retval);
6872 }
6873 
BUILT_IN_FUNCTION(function_encryptparm,input)6874 BUILT_IN_FUNCTION(function_encryptparm, input)
6875 {
6876 	char	*ret = NULL, *entry = NULL;
6877 	size_t	clue = 0;
6878 	Crypt	*key;
6879 
6880 	GET_FUNC_ARG(entry, input);
6881 	if ((key = is_crypted(entry, from_server, NULL)))
6882 	{
6883 		malloc_strcat_word_c(&ret, space, key->nick, DWORD_DWORDS, &clue);
6884 		malloc_strcat_word_c(&ret, space, key->passwd, DWORD_DWORDS, &clue);
6885 		malloc_strcat_word_c(&ret, space, key->prog, DWORD_DWORDS, &clue);
6886 	}
6887 
6888 	RETURN_MSTR(ret);
6889 }
6890 
BUILT_IN_FUNCTION(function_serverctl,input)6891 BUILT_IN_FUNCTION(function_serverctl, input)
6892 {
6893 	return serverctl(input);
6894 }
6895 
6896 
6897 #ifdef NO_JOB_CONTROL
BUILT_IN_FUNCTION(function_killpid,input)6898 BUILT_IN_FUNCTION(function_killpid, input)
6899 {
6900 	RETURN_EMPTY;
6901 }
6902 #endif
6903 
BUILT_IN_FUNCTION(function_killpid,input)6904 BUILT_IN_FUNCTION(function_killpid, input)
6905 {
6906 	char *	pid_str;
6907 	char *	sig_str;
6908 	pid_t	pid;
6909 	int	sig;
6910 	int	retval = 0;
6911 
6912 	GET_FUNC_ARG(sig_str, input);
6913 	if (is_number(sig_str))
6914 	{
6915 		sig = my_atol(sig_str);
6916 		if ((sig < 1) || (sig >= NSIG))
6917 			RETURN_EMPTY;
6918 	}
6919 	else
6920 	{
6921 		for (sig = 1; sig < NSIG; sig++)
6922 		{
6923 			if (!get_signal_name(sig))
6924 				continue;
6925 			if (!my_stricmp(get_signal_name(sig), sig_str))
6926 				goto do_kill;	/* Oh, bite me. */
6927 		}
6928 
6929 		RETURN_EMPTY;
6930 	}
6931 
6932 do_kill:
6933 	while ((pid_str = next_func_arg(input, &input)))
6934 	{
6935 		pid = strtoul(pid_str, &pid_str, 10);
6936 		if (kill(pid, sig) == 0)
6937 			retval++;
6938 	}
6939 
6940 	RETURN_INT(retval);
6941 }
6942 
BUILT_IN_FUNCTION(function_bindctl,input)6943 BUILT_IN_FUNCTION(function_bindctl, input)
6944 {
6945 	return bindctl(input);
6946 }
6947 
BUILT_IN_FUNCTION(function_logctl,input)6948 BUILT_IN_FUNCTION(function_logctl, input)
6949 {
6950 	return logctl(input);
6951 }
6952 
6953 /*
6954  * Joins the word lists in two different variables together with an
6955  * optional seperator string.
6956  * Usage:  $joinstr(seperator var1 var2)
6957  */
BUILT_IN_FUNCTION(function_joinstr,input)6958 BUILT_IN_FUNCTION(function_joinstr, input)
6959 {
6960 	char	*sep, *word;
6961 	char	*retval = NULL, *sub = NULL;
6962 	char	**freeit = NULL, **vals = NULL;
6963 	size_t	valc = 0;
6964 	size_t	retclue = 0;
6965 	size_t	foo;
6966 
6967 	GET_FUNC_ARG(sep, input)
6968 	for (valc = 0; input && *input; valc++) {
6969 		char *var;
6970 
6971 		RESIZE(vals, vals, valc + 1);
6972 		RESIZE(freeit, freeit, valc + 1);
6973 
6974 		GET_FUNC_ARG(var, input)
6975 		freeit[valc] = vals[valc] = get_variable(var);
6976 	}
6977 
6978 	for (;;) {
6979 		size_t clue = 0;
6980 		char   more = 0;
6981 
6982 		for (foo = 0; foo < valc; foo++) {
6983 			more |= *vals[foo];
6984 			word = next_func_arg(vals[foo], &vals[foo]);
6985 			if (word == NULL)
6986 				word = endstr(vals[foo]);
6987 			malloc_strcat2_c(&sub, foo?sep:empty_string, word, &clue);
6988 		}
6989 
6990 		if (!more)
6991 			break;
6992 
6993 		malloc_strcat_word_c(&retval, space, sub, DWORD_DWORDS, &retclue);
6994 		*sub = 0;	/* Improve malloc performance */
6995 	}
6996 
6997 	for (foo = 0; foo < valc; foo++)
6998 		new_free(&freeit[foo]);
6999 
7000 	new_free(&freeit);
7001 	new_free(&vals);
7002 	new_free(&sub);
7003 
7004 	RETURN_MSTR(retval);
7005 }
7006 
BUILT_IN_FUNCTION(function_exec,input)7007 BUILT_IN_FUNCTION(function_exec, input)
7008 {
7009 	char	*ret = NULL;
7010 	char **args = NULL;
7011 	int	count, *fds, foo;
7012 	size_t	clue = 0;
7013 
7014 	RETURN_IF_EMPTY(input);
7015 	count = splitw(input, &args, DWORD_YES);
7016 	RESIZE(args, void *, count+1);
7017 	args[count] = NULL;
7018 
7019 	if (!count || !args)
7020 		RETURN_EMPTY;
7021 
7022 	fds = open_exec_for_in_out_err(args[0], (char * const *)args);
7023 	new_free(&args);
7024 
7025 	if (fds)
7026 		for (foo = 0; foo < 3; foo++)
7027 			malloc_strcat_word_c(&ret, space, ltoa(fds[foo]), DWORD_NO, &clue);
7028 
7029 	RETURN_MSTR(ret);
7030 }
7031 
7032 /*
7033  * getserial function:
7034  * arguments: <type> [...]
7035  * currently only 'HOOK' is available as a type, the arguments to the hook
7036  * type are a direction (+ or -) and a starting place  New types may be made
7037  * available later.
7038  */
7039 #include "hook.h"
7040 
BUILT_IN_FUNCTION(function_getserial,input)7041 BUILT_IN_FUNCTION(function_getserial, input) {
7042     char *type, *sdir;
7043     int dir, from, serial;
7044 
7045     GET_FUNC_ARG(type, input);
7046     if (!type || !*type)
7047 	RETURN_EMPTY;
7048     if (!my_stricmp(type, "HOOK")) {
7049 	GET_FUNC_ARG(sdir, input);
7050 	if (!strcmp(sdir, "+"))
7051 	    dir = 1;
7052 	else if (!strcmp(sdir, "-"))
7053 	    dir = -1;
7054 	else
7055 	    RETURN_EMPTY;
7056 
7057 	GET_INT_ARG(from, input);
7058 	serial = hook_find_free_serial(dir, from, INVALID_HOOKNUM);
7059 	RETURN_INT(serial);
7060     }
7061 
7062     RETURN_EMPTY;
7063 }
7064 
BUILT_IN_FUNCTION(function_timerctl,input)7065 BUILT_IN_FUNCTION(function_timerctl, input)
7066 {
7067 	return timerctl(input);
7068 }
7069 
BUILT_IN_FUNCTION(function_dccctl,input)7070 BUILT_IN_FUNCTION(function_dccctl, input)
7071 {
7072 	return dccctl(input);
7073 }
7074 
BUILT_IN_FUNCTION(function_outputinfo,input)7075 BUILT_IN_FUNCTION(function_outputinfo, input)
7076 {
7077 	if (who_from)
7078 		return malloc_sprintf(NULL, "%s %s", level_to_str(who_level),
7079 						who_from);
7080 	else
7081 		return malloc_strdup(level_to_str(who_level));
7082 }
7083 
BUILT_IN_FUNCTION(function_levelwindow,input)7084 BUILT_IN_FUNCTION(function_levelwindow, input)
7085 {
7086 	Mask	mask;
7087 	Window	*w = NULL;
7088 	int	server;
7089 	int	i;
7090 	char 	*rejects = NULL;
7091 
7092 	GET_INT_ARG(server, input);
7093 	str_to_mask(&mask, input, &rejects);	/* Errors are just ignored */
7094 	while (traverse_all_windows(&w))
7095 	{
7096 	    if (mask_isset(&mask, LEVEL_DCC) &&
7097 		mask_isset(&w->window_mask, LEVEL_DCC))
7098 		RETURN_INT(w->refnum);
7099 
7100 	    if (w->server != server)
7101 		continue;
7102 
7103 	    for (i = 1; BIT_VALID(i); i++)
7104 		if (mask_isset(&mask, i) &&
7105 		    mask_isset(&w->window_mask, i))
7106 			RETURN_INT(w->refnum);
7107 	}
7108 	RETURN_INT(-1);
7109 }
7110 
BUILT_IN_FUNCTION(function_serverwin,input)7111 BUILT_IN_FUNCTION(function_serverwin, input)
7112 {
7113 	int     	sval = from_server;
7114 	int		winref;
7115 
7116 	if (*input)
7117 		GET_INT_ARG(sval, input);
7118 
7119 	winref = get_winref_by_servref(sval);
7120 	RETURN_INT(winref);
7121 }
7122 
BUILT_IN_FUNCTION(function_ignorectl,input)7123 BUILT_IN_FUNCTION(function_ignorectl, input)
7124 {
7125         return ignorectl(input);
7126 }
7127 
BUILT_IN_FUNCTION(function_metric_time,input)7128 BUILT_IN_FUNCTION(function_metric_time, input)
7129 {
7130 	struct metric_time right_now;
7131 
7132 	right_now = get_metric_time(NULL);
7133 	return malloc_sprintf(NULL, "%ld %9.6f", right_now.mt_days, right_now.mt_mdays);
7134 }
7135 
BUILT_IN_FUNCTION(function_windowctl,input)7136 BUILT_IN_FUNCTION(function_windowctl, input)
7137 {
7138         return windowctl(input);
7139 }
7140 
7141 /*
7142  * $numlines(<columns> <string>)
7143  * Returns the number of lines that <string> will occupy after final
7144  * display in a window with a width of <columns>, or nothing on error.
7145  */
BUILT_IN_FUNCTION(function_numlines,input)7146 BUILT_IN_FUNCTION(function_numlines, input)
7147 {
7148 	int cols;
7149 	int numl = 0;
7150 	char *strval;
7151 
7152 	GET_INT_ARG(cols, input);
7153 	if (cols < 1)
7154 		RETURN_EMPTY;
7155 	cols--;
7156 
7157 	/* Normalize the line of output */
7158 	strval = new_normalize_string(input, 0, NORMALIZE);
7159 	prepare_display(-1, strval, cols, &numl, 0);
7160 	new_free(&strval);
7161 	RETURN_INT(numl+1);
7162 }
7163 
7164 /*
7165  * $strtol(<base> <number>)
7166  * Returns the decimal value of <number>, where number is a number
7167  * in base <base>. <base> must be higher than, or equal to 2, or
7168  * lower than, or equal to 36, or 0.
7169  * Returns empty on errors.
7170  * Written by howl
7171  */
BUILT_IN_FUNCTION(function_strtol,input)7172 BUILT_IN_FUNCTION(function_strtol, input)
7173 {
7174 	int	base;
7175 	char *	number;
7176 	intmax_t	retval;
7177 	char *	after;
7178 
7179 	if (!input || !*input)
7180 		RETURN_EMPTY;
7181 	GET_INT_ARG(base, input);
7182 	if (!input || !*input || (base != 0 && (base < 2 || base > 36)))
7183 		RETURN_EMPTY;
7184 	GET_FUNC_ARG(number, input);	/* Must not use GET_INT_ARG */
7185 
7186 	retval = strtoimax(number, &after, base);
7187 	/* Argh -- do we want to return error if invalid char found? */
7188 	RETURN_INT(retval);
7189 }
7190 
7191 /*
7192  * $tobase(<base> <number>)
7193  * Returns the string value of decimal number <number>, converted to base
7194  * <base>. <base> must be higher than, or equal to 2, or lower than, or
7195  * equal to 36.
7196  * Written by howl, from http://www.epicsol.org/~jnelson/base
7197  */
BUILT_IN_FUNCTION(function_tobase,input)7198 BUILT_IN_FUNCTION(function_tobase, input)
7199 {
7200 	int	c, base, len = 0, pos = 0, negate = 0;
7201 	intmax_t	n, num;
7202 	char *	string;
7203 	char 	table[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
7204 #if 0
7205 	char *	number, *after;
7206 #endif
7207 
7208 	len = pos = 0;
7209 
7210 	if (!input || !*input)
7211 		RETURN_EMPTY;
7212 	GET_INT_ARG(base, input);
7213 
7214 	if (!input || !*input || base < 2 || base > 36)
7215 		RETURN_EMPTY;
7216 
7217 	if (*input == '-')
7218 	{
7219 		negate = 1;
7220 		input++;
7221 	}
7222 	GET_INT_ARG(num, input);
7223 #if 0
7224 	num = strtoimax(number, &after, 10);	/* Must not use GET_INT_ARG */
7225 #endif
7226 	/* The exponent routine below doesn't work for 0 */
7227 	if (num == 0)
7228 		RETURN_INT(num);
7229 
7230 	while (pow(base, len) <= num)
7231 		len++;
7232 
7233 	if (!len)
7234 		RETURN_EMPTY;
7235 
7236 	if (negate == 1)
7237 		len++;
7238 
7239 	string = new_malloc(len + 1);
7240 	string[len] = 0;
7241 
7242 	if (negate)
7243 	{
7244 		string[0] = '-';
7245 		pos++;
7246 		len--;
7247 	}
7248 
7249 	while (len-- > 0)
7250 	{
7251 		n = (intmax_t)pow(base, len),
7252 		c = floor(num / n);
7253 		string[pos] = table[c];
7254 		pos++;
7255 		num -= n * c;
7256 	}
7257 
7258 	return string;
7259 }
7260 
BUILT_IN_FUNCTION(function_startupfile,input)7261 BUILT_IN_FUNCTION(function_startupfile, input)
7262 {
7263 	if (startup_file)
7264 		RETURN_STR(startup_file);
7265 	RETURN_EMPTY;
7266 }
7267 
7268 /*
7269  * $mktime(year month day hour minute second dst)
7270  * - Requires at least six (6) arguments
7271  * - Returns -1 on error
7272  * - Refer to the mktime(3) manpage for instructions on usage.
7273  * Written by howl
7274  */
BUILT_IN_FUNCTION(function_mktime,input)7275 BUILT_IN_FUNCTION(function_mktime, input)
7276 {
7277 	int ar[7], pos, retval;
7278 	struct tm tmtime;
7279 
7280 	for (pos = 0; pos < 7 && input && *input; pos++)
7281 		GET_INT_ARG(ar[pos], input);
7282 
7283 	if (pos < 6)
7284 		RETURN_INT(-1);
7285 
7286 	tmtime.tm_year = ar[0];
7287 	tmtime.tm_mon = ar[1];
7288 	tmtime.tm_mday = ar[2];
7289 	tmtime.tm_hour = ar[3];
7290 	tmtime.tm_min = ar[4];
7291 	tmtime.tm_sec = ar[5];
7292 	tmtime.tm_isdst = (pos > 6) ? ar[6] : -1;
7293 
7294 	retval = mktime(&tmtime);
7295 	RETURN_INT(retval);
7296 }
7297 
BUILT_IN_FUNCTION(function_hookctl,input)7298 BUILT_IN_FUNCTION(function_hookctl, input)
7299 {
7300         return hookctl(input);
7301 }
7302 
BUILT_IN_FUNCTION(function_fix_arglist,input)7303 BUILT_IN_FUNCTION(function_fix_arglist, input)
7304 {
7305 	ArgList *l;
7306 	char *r;
7307 	l = parse_arglist(input);
7308 	r = print_arglist(l);
7309 	if (r)
7310 	{
7311 		destroy_arglist(&l);
7312 		return r;
7313 	}
7314 	RETURN_EMPTY;
7315 }
7316 
BUILT_IN_FUNCTION(function_symbolctl,input)7317 BUILT_IN_FUNCTION(function_symbolctl, input)
7318 {
7319 	return symbolctl(input);
7320 }
7321 
BUILT_IN_FUNCTION(function_levelctl,input)7322 BUILT_IN_FUNCTION(function_levelctl, input)
7323 {
7324 	return levelctl(input);
7325 }
7326 
BUILT_IN_FUNCTION(function_dbmctl,input)7327 BUILT_IN_FUNCTION(function_dbmctl, input)
7328 {
7329 	return dbmctl(input);
7330 }
7331 
7332 /*
7333  * Here's the plan -- we're going to do this over again a second time.
7334  *
7335  * $xform("<transformations>" "meta" "meta" text)
7336  *
7337  * Where the <transformation>s are supported by transform_string().
7338  * At the time i write this, they are:
7339  *	URL		URL encoding		(reversable, no meta)
7340  *	ENC		Base16 encoding		(reversable, no meta)
7341  *	B64		Base64 encoding		(reversable, no meta)
7342  *	CTCP		CTCP encoding		(reversable, no meta)
7343  *	SED		Simple Encrypt Data	(reversable, requires meta)
7344  *	DEF		Default encryption	(reversable, requires meta)
7345  *
7346  * You can string together multiple transformations.  Any transformation
7347  * that requires a meta value (ie, a cipherkey) should be supplied after
7348  * the transformations *in the correct order*.  After this should be the
7349  * plain text.  To apply a transformation, prefix its name with a plus sign
7350  * ("+") and to remove a transformation, prefix its name with a minus sign
7351  * ("-").  For example, +URL means url encode, and -URL means url decode.
7352  *
7353  * The transformations is a dword (must be surrounded by double quotes if
7354  * it contains a space, which it will if you do multiple transformations).
7355  * The meta values are dwords (must be surrounded by double quotes if they
7356  * contain a space).  These two things make this function behave differently
7357  * than functions normally do, so this is a documented deviancy!
7358  *
7359  * Examples:
7360  * 	URL-encode a string	$xform(+URL this is a string)
7361  *	URL-decode a string	$xform(-URL this%20is%20a%20string)
7362  *	SED-cipher a string	$xform(+SED password this is a string)
7363  *	SED-decipher a string	$xform(-sed password <whatever>)
7364  *
7365  * More practical examples:
7366  * 1) Read binary data from a file, encrypt it, and url encode it again.
7367  *	@fd = open(file.txt R)
7368  *	@data = read($fd 1024)
7369  *	@cipher = xform("-CTCP +SED +URL" password $data)
7370  *	@close($fd)
7371  *	msg someone $cipher
7372  *
7373  * Why does this work?
7374  *  -- $read() returns ctcp-enquoted data, so -CTCP removes it
7375  *  -- Now we have binary data, so +SED will cipher it
7376  *  -- Now we have ciphertext, so +URL will url encode it.
7377  *
7378  * We can send this to someone else, and they can put it in $cipher...
7379  *
7380  *	@newfd = open(newfile.txt W)
7381  *	@newdata = xform("-URL -SED +CTCP" password $cipher)
7382  *	@writeb($newfd $newdata)
7383  *	@close($newfd)
7384  *
7385  * We did the reverse of the above:
7386  *  -- We -URL to recover the binary data
7387  *  -- We -SED to decrypt it using the password
7388  *  -- We +CTCP to put it in a form we can use with $writeb().
7389  *
7390  * Viola!
7391  */
BUILT_IN_FUNCTION(function_xform,input)7392 BUILT_IN_FUNCTION(function_xform, input)
7393 {
7394 #define MAX_TRANSFORMS 16
7395 	char *	directives;
7396 	int	x, i = 0;
7397 	int	types[MAX_TRANSFORMS];
7398 	char *	args[MAX_TRANSFORMS];
7399 	int	encodings[MAX_TRANSFORMS];
7400 
7401 	char *	typestr;
7402 	int	type;
7403 	int	encodingx;
7404 	char *	arg;
7405 	int	numargs;
7406 
7407 	char	*srcbuf, *destbuf, *ptr;
7408 	size_t	srcbuflen, destbuflen, ptrbuflen;
7409 	size_t	refbuflen;
7410 	int	expand, overh;
7411 
7412 	/*
7413 	 * First we have to calculate how many args there are before
7414 	 * the original text.  There is one arg for the directives,
7415 	 * and then we have to check each directive to see how many
7416 	 * args it takes.
7417 	 */
7418 	GET_DWORD_ARG(directives, input)
7419 	while ((typestr = next_arg(directives, &directives)))
7420 	{
7421 	    if (*typestr == '+')
7422 		typestr++, encodingx = 1;
7423 	    else if (*typestr == '-')
7424 		typestr++, encodingx = 0;
7425 	    else
7426 		encodingx = 1;
7427 
7428 	    if ((type = lookup_transform(typestr, &numargs, &expand, &overh)) > -1)
7429 	    {
7430 		if (numargs == 1)
7431 			GET_DWORD_ARG(arg, input)
7432 		else
7433 			arg = NULL;
7434 
7435 		if (i < MAX_TRANSFORMS)
7436 	        {
7437 			types[i] = type;
7438 			encodings[i] = encodingx;
7439 			args[i] = arg;
7440 			i++;
7441 		}
7442 	    }
7443 	}
7444 
7445 	refbuflen = strlen(input);
7446 	srcbuf = (char *)new_malloc(refbuflen * 10 + 256);
7447 	destbuf = (char *)new_malloc(refbuflen * 10 + 256);
7448 
7449 	strlcpy(srcbuf, input, refbuflen + 1);
7450 	srcbuflen = refbuflen;
7451 	destbuflen = refbuflen * 10 + 256;
7452 
7453 	for (x = 0; x < i; x++)
7454 	{
7455 		ptrbuflen = transform_string(types[x], encodings[x], args[x],
7456 				srcbuf, srcbuflen, destbuf, destbuflen);
7457 		ptr = srcbuf;
7458 		srcbuf = destbuf;
7459 		srcbuflen = ptrbuflen;
7460 		destbuf = ptr;
7461 		destbuflen = refbuflen * 10 + 256;
7462 	}
7463 
7464 	new_free(&destbuf);
7465 	srcbuf[srcbuflen] = 0;
7466 	RETURN_MSTR(srcbuf);
7467 }
7468 
7469 #ifdef HAVE_RUBY
7470 
BUILT_IN_FUNCTION(function_ruby,input)7471 BUILT_IN_FUNCTION(function_ruby, input)
7472 {
7473 	return rubyeval ( input );
7474 }
7475 
7476 #endif
7477 
BUILT_IN_FUNCTION(function_curcmd,unused)7478 BUILT_IN_FUNCTION(function_curcmd, unused) {
7479 	RETURN_STR(current_command);
7480 }
7481 
7482 /*
7483  * is8bit(s) returns the index number of the first character of string
7484  * s that is a 8-bit character. Possible use: "discovers" (?) Unicode or
7485  * whatever. Returns -1 if nil (0) 8-bit characters are found.
7486  */
BUILT_IN_FUNCTION(function_is8bit,input)7487 BUILT_IN_FUNCTION(function_is8bit, input) {
7488 	int n = 0, l;
7489 	if (!input || !*input)
7490 		RETURN_EMPTY;
7491 	l = strlen (input);
7492 	for (n = 0; n < l; n++)
7493 		if (input[n] & 0x80)
7494 			RETURN_INT(n);
7495 	RETURN_INT(-1);
7496 }
7497 
7498 /* Returns the result of num_code_points(). D'oh! */
BUILT_IN_FUNCTION(function_isutf8,input)7499 BUILT_IN_FUNCTION(function_isutf8, input)
7500 {
7501 	if (!input || !*input)
7502 		RETURN_EMPTY;
7503 	RETURN_INT(num_code_points(input));
7504 }
7505 
7506 /*
7507  * This function converts a delimited string (ie, $PATH) into a dword list.
7508  *  $splitw(: $PATH) -> /bin /usr/bin "/home/user/my programs/" ...
7509  */
BUILT_IN_FUNCTION(function_splitw,input)7510 BUILT_IN_FUNCTION(function_splitw, input)
7511 {
7512 	char *	delim_str;
7513 	int	delim;
7514 	char *	str;
7515 	unsigned char **wordl;
7516 	int 	wordc;
7517 	char *	retval;
7518 
7519 	/* The delimiter is the first code point in the first argument. */
7520 	GET_DWORD_ARG(delim_str, input);
7521 	delim = next_code_point(CUC_PP &delim_str, 0);
7522 
7523 	if (!(wordc = new_split_string(input, &wordl, delim)))
7524 		RETURN_EMPTY;
7525 
7526 	/* unsplitw() disposes of "wordl" for us. */
7527 	retval = unsplitw((char ***)&wordl, wordc, DWORD_YES);
7528 	RETURN_MSTR(retval);
7529 }
7530 
7531 /*
7532  * Usage: $strptime("strftime format" textual string)
7533  * -- This function does the reverse of $strftime().  It converts
7534  *    "textual string" into a $time() value, if and only if "textual string"
7535  *    is the representation of that $time() value as created by strftime().
7536  *    Got it?
7537  */
BUILT_IN_FUNCTION(function_strptime,input)7538 BUILT_IN_FUNCTION(function_strptime, input)
7539 {
7540 	struct tm	timeptr;
7541 	char *		format;
7542 	time_t		the_time;
7543 
7544 #ifdef HAVE_STRPTIME
7545 	GET_DWORD_ARG(format, input);
7546 
7547 	if (!(strptime(input, format, &timeptr)))
7548 		RETURN_EMPTY;
7549 
7550 	the_time = mktime(&timeptr);
7551 	RETURN_INT(the_time);
7552 #else
7553 	RETURN_EMPTY;
7554 #endif
7555 }
7556 
7557 /*
7558  * This function will return 1 if the argument is an expression that
7559  * appears to pass a basic syntax text.  The basic syntax test requires
7560  * that the first character either be a { or a ( and contain a matching
7561  * number of }s or )s.  We do this by calling my_next_expr() which is
7562  * what the command parser does anyways.
7563  *
7564  * What this function does NOT do is tell you whether or not what is
7565  * inside your block makes any sense.  That's not possible to tell
7566  * without running it.  All we can tell you is if it will be rejected
7567  * do to mismatched {} or ()s.
7568  */
BUILT_IN_FUNCTION(function_check_code,input)7569 BUILT_IN_FUNCTION(function_check_code, input)
7570 {
7571 	char type;
7572 
7573 	while (input && *input && isspace(*input))
7574 		input++;
7575 
7576 	if (input == NULL || (*input != '{' && *input != '('))
7577 		RETURN_INT(-1);		/* Not a block statement or expr */
7578 
7579 	type = *input;
7580 	if (!next_expr_failok(&input, type))
7581 		RETURN_INT(-2);		/* Unmatched brace or paren */
7582 
7583 	while (input && *input && isspace(*input))
7584 		input++;
7585 
7586 	if (input == NULL || *input)
7587 		RETURN_INT(-3);		/* Stuff after trailing } */
7588 
7589 	RETURN_INT(0);			/* Looks ok to me */
7590 }
7591 
7592 
BUILT_IN_FUNCTION(function_channellimit,word)7593 BUILT_IN_FUNCTION(function_channellimit, word)
7594 {
7595 	char	*channel;
7596 	char    *booya = (char *) 0;
7597 	int	limit;
7598 	size_t	rvclue=0;
7599 
7600 	do
7601 	{
7602 		channel = next_func_arg(word, &word);
7603 		if ((!channel || !*channel) && booya)
7604 			break;
7605 
7606 		limit = get_channel_limit(channel, from_server);
7607 		malloc_strcat_word_c(&booya, space, ltoa(limit),
7608 					DWORD_DWORDS, &rvclue);
7609 	}
7610 	while (word && *word);
7611 
7612 	RETURN_MSTR(booya);
7613 }
7614 
7615 
7616 /*
7617  * Here is the "plan": we allow the user to either do:
7618  *
7619  *   echo $xform(iconv FROMCODE/TOCODE[/OPTION] stuff)
7620  *
7621  * which is heaps expensive, because it /opens/ and
7622  * /closes/ an iconv_t descriptor for each usage.
7623  *   Or the user can control this stuff herself, by first
7624  * opening the iconv descriptor manually, and then refering to
7625  * its identifier thusly:
7626  *
7627  *   @ id = xform(add FROMCODE/TOCODE[/OPTION])
7628  *   echo $xform(+iconv $id stuff)
7629  *
7630  * This will allow for a reversal of the encoding like:
7631  *   echo $xform(-iconv $id stuff)
7632  *
7633  * The identifier will be closed by doing:
7634  *
7635  *   @ iconvctl(remove $id)
7636  *
7637  * A list of (occupied) identiers will accessable by:
7638  *
7639  *   echo $iconvctl(list)
7640  *
7641  * And a specific identifiers specification will be available by:
7642  *
7643  *   echo $iconvctl(get $id)
7644  *
7645  * Other functionality may or may not be added at
7646  * a later point of time.
7647  */
7648 
function_iconvctl(char * input)7649 char *function_iconvctl (char *input)
7650 {
7651 	size_t len, pos;
7652 	char *listc;
7653 	int id;
7654 	int intarg;
7655 
7656 	iconv_t forward, reverse;
7657 
7658 	GET_FUNC_ARG(listc, input);
7659 	len = strlen(listc);
7660 
7661 	/*
7662 	 * Add descriptor to the first available "slot",
7663 	 * or extend the list by one, and use the last "slot".
7664 	 */
7665 	if (!my_strnicmp(listc, "ADD", len))
7666 	{
7667 		GET_FUNC_ARG(listc, input);
7668 		if (my_iconv_open(&forward, &reverse, listc))
7669 			RETURN_EMPTY;
7670 
7671 		/* Do we need to initiate the list? */
7672 		if (iconv_list_size == 0)
7673 		{
7674 			iconv_list = (struct Iconv_stuff **) new_malloc(sizeof(struct Iconv_stuff *));
7675 			iconv_list_size = 1;
7676 			iconv_list[0] = NULL;
7677 		}
7678 
7679 		/* Is there an empty "slot"? */
7680 		for (id = 0; id < iconv_list_size; id++)
7681 			if (iconv_list[id] == NULL)
7682 				break;
7683 
7684 		/* Do we need to realloc the list? */
7685 		if (id == iconv_list_size)
7686 		{
7687 			iconv_list_size++;
7688 			new_realloc((void **) &iconv_list,
7689 				sizeof(struct Iconv_stuff *) * iconv_list_size);
7690 		}
7691 
7692 		/* Allocate a structure. */
7693 		iconv_list[id] = (struct Iconv_stuff *) new_malloc(sizeof(struct Iconv_stuff));
7694 
7695 		/* Do this stuff... */
7696 		iconv_list[id]->forward = forward;
7697 		iconv_list[id]->reverse = reverse;
7698 		iconv_list[id]->stuff = malloc_strdup(listc);
7699 
7700 		/* Return identifier. */
7701 		RETURN_INT(id);
7702 	}
7703 
7704 	if (iconv_list_size == 0)
7705 		RETURN_EMPTY;
7706 
7707 	/*
7708 	 * Return a list of used identifiers.
7709 	 */
7710 	if (!my_strnicmp(listc, "LIST", len))
7711 	{
7712 		size_t  clue = 0;
7713 		char *retval = NULL;
7714 		for (id = 0; id < iconv_list_size; id++)
7715 			if (iconv_list[id] != NULL && iconv_list[id]->stuff != NULL)
7716 				malloc_strcat_wordlist_c(&retval, space, ltoa(id), &clue);
7717 		RETURN_MSTR(retval);
7718 	}
7719 
7720 	/*
7721 	 * Return list size.
7722 	 */
7723 	 if (!my_strnicmp(listc,"SIZE", len))
7724 	 	RETURN_INT(iconv_list_size);
7725 
7726 	/*
7727 	 * Return the stuff from a given identifier;
7728 	 * 0 if NULL, and nothing if too high.
7729 	 */
7730 	if (!my_strnicmp(listc, "GET", len))
7731 	{
7732 		GET_INT_ARG(intarg, input);
7733 
7734 		if (intarg >= iconv_list_size || intarg < 0)
7735 			RETURN_EMPTY;
7736 		if (iconv_list[(size_t) intarg] == NULL)
7737 			RETURN_INT(0);
7738 		RETURN_STR(iconv_list[(size_t) intarg]->stuff);
7739 	}
7740 
7741 	/*
7742 	 * Remove a given identifier from the list, and
7743 	 * truncate the identifier was the last of the list.
7744 	 *
7745 	 * Return 0 if unsuccessful, 1 if successful.
7746 	 */
7747 	if (!my_strnicmp(listc, "REMOVE", len))
7748 	{
7749 		GET_INT_ARG(intarg, input);
7750 		if (intarg >= iconv_list_size || intarg < 0)
7751 			RETURN_EMPTY;
7752 		id = (size_t) intarg;
7753 		if (iconv_list[id] == NULL && id + 1 != iconv_list_size)
7754 			RETURN_INT(0);
7755 		if (iconv_list[id] != NULL)
7756 		{
7757 			iconv_close(iconv_list[id]->forward);
7758 			iconv_close(iconv_list[id]->reverse);
7759 			new_free(&iconv_list[id]->stuff);
7760 			new_free(&iconv_list[id]);
7761 			iconv_list[id] = NULL;
7762 		}
7763 
7764 		/* Check the last slots. If 1 or more is "empty", realloc. */
7765 		for (pos = iconv_list_size - 1;
7766 			pos > 0 && iconv_list[pos] == NULL; pos--);
7767 		if (((iconv_list_size -1) - pos) > 0)
7768 		{
7769 			iconv_list_size = pos + 1;
7770 			if (iconv_list[iconv_list_size - 1] == NULL)
7771 			{
7772 				new_free(&iconv_list);
7773 				iconv_list = NULL;
7774 				iconv_list_size = 0;
7775 			}
7776 			else
7777 			{
7778 				new_realloc((void **) &iconv_list, iconv_list_size *
7779 					sizeof(struct Iconv_stuff *));
7780 			}
7781 		}
7782 		RETURN_INT(1);
7783 	}
7784 
7785 	RETURN_EMPTY;
7786 }
7787 
BUILT_IN_FUNCTION(function_channelsyncing,word)7788 BUILT_IN_FUNCTION(function_channelsyncing, word)
7789 {
7790 	char *	channel;
7791 	char *	serverstr;
7792 	int	servref;
7793 	int	retval;
7794 
7795 	GET_FUNC_ARG(channel, word);
7796 	GET_FUNC_ARG(serverstr, word);
7797 	servref = str_to_servref(serverstr);
7798 
7799 	retval = channel_is_syncing(channel, servref);
7800 	RETURN_INT(retval);
7801 }
7802 
7803 #if 0
7804 void	help_topics_commands(FILE *);
7805 void	help_topics_functions(FILE *);
7806 void	help_topics_scripts(FILE *);
7807 void	help_topics_bind(FILE *);
7808 void	help_topics_ctcp(FILE *);
7809 void	help_topics_dcc(FILE *);
7810 void	help_topics_on(FILE *);
7811 void	help_topics_set(FILE *);
7812 void	help_topics_window(FILE *);
7813 
7814 void    help_topics_functions (FILE *f)
7815 {
7816         int     x;
7817 
7818         for (x = 0; built_in_functions[x].name; x++)
7819                 fprintf(f, "function %s\n", built_in_functions[x].name);
7820 }
7821 
7822 void	help_topics_scripts (FILE *f)
7823 {
7824 	char	dir[1024];
7825 	DIR *	d;
7826 	struct dirent *e;
7827 
7828 	snprintf(dir, 1024, "../script");
7829 	if (!(d = opendir(dir)))
7830 	{
7831 		yell("Could not open dir %s", dir);
7832 		return;
7833 	}
7834 
7835 	while ((e = readdir(d)))
7836 	{
7837 		if (*e->d_name == '.')
7838 			continue;
7839 		fprintf(f, "script %s\n", e->d_name);
7840 	}
7841 
7842 	closedir(d);
7843 }
7844 
7845 BUILT_IN_FUNCTION(function_help_topics, word)
7846 {
7847 	FILE *f;
7848 
7849 	if (!(f = fopen("help_topics.txt", "w")))
7850 	{
7851 		yell("Could not open \"help_topics.txt\"");
7852 		RETURN_EMPTY;
7853 	}
7854 
7855 	help_topics_commands(f);
7856 	help_topics_functions(f);
7857 	help_topics_scripts(f);
7858 	help_topics_bind(f);
7859 	help_topics_ctcp(f);
7860 	help_topics_dcc(f);
7861 	help_topics_on(f);
7862 	help_topics_set(f);
7863 	help_topics_window(f);
7864 	fclose(f);
7865 	say("Done.  Check help_topics.txt");
7866 	RETURN_EMPTY;
7867 }
7868 #endif
7869 
7870 /*
7871  * $chankey(servref #channel) - return the +k key for a channel.
7872  *
7873  * Arguments:
7874  *  $0 - servrer - A server refnum
7875  *  $1 - #channel - A channel
7876  *
7877  * Return Value:
7878  *	empty_string	- Either
7879  *			  1. 'refnum' not provided, or
7880  *			  2. '#channel' not provided, or
7881  *			  3. You're not on '#channel' on 'refnum', or
7882  *			  4. #channel doesn't have a key (mode +k)
7883  *	anything else	- The mode +k key for #channel on server 'refnum'
7884  */
BUILT_IN_FUNCTION(function_chankey,input)7885 BUILT_IN_FUNCTION(function_chankey, input)
7886 {
7887 	int		refnum = -1;
7888 	const char *	channel;
7889 	const char *	key;
7890 
7891 	GET_INT_ARG(refnum, input);
7892 	GET_FUNC_ARG(channel, input);
7893 
7894 	key = get_channel_key(channel, refnum);
7895 	RETURN_STR(key);
7896 }
7897 
7898 #ifdef HAVE_PYTHON
7899 
BUILT_IN_FUNCTION(function_python,input)7900 BUILT_IN_FUNCTION(function_python, input)
7901 {
7902 	return python_eval_expression ( input );
7903 }
7904 
BUILT_IN_FUNCTION(function_pydirect,input)7905 BUILT_IN_FUNCTION(function_pydirect, input)
7906 {
7907 	const char *	py_funcname;
7908 
7909 	GET_FUNC_ARG(py_funcname, input)
7910 	return call_python_directly(py_funcname, input);
7911 }
7912 
7913 #endif
7914 
7915 /*
7916  * $json_error() - show the parse error the from the last json_explode()
7917  *
7918  * Returns the parse error from the last $json_explode() call, or an empty
7919  * string if the last $json_explode() call succeeded.
7920  */
7921 static char *json_last_error;
7922 
BUILT_IN_FUNCTION(function_json_error,input)7923 BUILT_IN_FUNCTION(function_json_error, input)
7924 {
7925 	RETURN_STR(json_last_error);
7926 }
7927 
7928 /*
7929  * $json_explode(var json) - deserialise a JSON string into an assign var
7930  *
7931  * Arguments:
7932  *  $0 - var - An assign variable
7933  *  $1 - json - A valid JSON string
7934  *
7935  * Returns an empty string on failure or 1 on success.
7936  */
explode_json_object(const char * var,cJSON * item)7937 static void explode_json_object(const char *var, cJSON *item)
7938 {
7939 	int n;
7940 	cJSON *subitem;
7941 	char *subvar = NULL;
7942 	char *subitem_name = NULL;
7943 
7944 	switch (item->type)
7945 	{
7946 		case cJSON_False:
7947 		add_var_alias(var, "0", 0);
7948 		break;
7949 
7950 		case cJSON_True:
7951 		add_var_alias(var, "1", 0);
7952 		break;
7953 
7954 		case cJSON_NULL:
7955 		add_var_alias(var, NULL, 0);
7956 		break;
7957 
7958 		case cJSON_Number:
7959 		add_var_alias(var, ftoa(item->valuedouble), 0);
7960 		break;
7961 
7962 		case cJSON_String:
7963 		add_var_alias(var, item->valuestring, 0);
7964 		break;
7965 
7966 		case cJSON_Array:
7967 		case cJSON_Object:
7968 		for (subitem = item->child, n = 0; subitem; subitem = subitem->next, n++)
7969 		{
7970 			if (subitem->string)
7971 			{
7972 				char *ptr = malloc_strcpy(&subitem_name, subitem->string);
7973 
7974 				/* The name must be mangled to be suitable as an ASSIGN */
7975 				while (*ptr)
7976 				{
7977 					if (!isalnum(*ptr))
7978 						*ptr = '_';
7979 
7980 					ptr++;
7981 				}
7982 
7983 				malloc_sprintf(&subvar, "%s.%s", var, subitem_name);
7984 			}
7985 			else
7986 				malloc_sprintf(&subvar, "%s.%d", var, n);
7987 
7988 
7989 			explode_json_object(subvar, subitem);
7990 		}
7991 		break;
7992 
7993 		default:
7994 		yell("Odd cJSON type in $json_explode");
7995 	}
7996 	new_free(&subitem_name);
7997 	new_free(&subvar);
7998 }
7999 
BUILT_IN_FUNCTION(function_json_explode,input)8000 BUILT_IN_FUNCTION(function_json_explode, input)
8001 {
8002 	const char *var;
8003 	cJSON *root;
8004 
8005 	GET_FUNC_ARG(var, input);
8006 	root = cJSON_Parse(input);
8007 
8008 	if (!root)
8009 	{
8010 		malloc_sprintf(&json_last_error, "JSON parse error at position %u", (unsigned)(cJSON_GetErrorPtr() - input));
8011 		RETURN_EMPTY;
8012 	}
8013 
8014 	new_free(&json_last_error);
8015 	explode_json_object(var, root);
8016 	cJSON_Delete(root);
8017 	RETURN_INT(1);
8018 }
8019 
8020 /*
8021  * $json_implode(var) - serialise a Structure assign var into a JSON string
8022  *
8023  * Arguments:
8024  *  $0 - var - An assign variable
8025  *
8026  * Returns an empty string on failure or a valid JSON string on success.
8027  */
implode_struct_var(char * var,cJSON * parent)8028 static int implode_struct_var(char *var, cJSON *parent)
8029 {
8030 	char **sublist;
8031 	int count;
8032 	int i;
8033 	const char *name = strrchr(var, '.');
8034 	int err = 0;
8035 
8036 	if (name)
8037 		name++;
8038 	else
8039 		name = var;
8040 
8041 	sublist = get_subarray_elements(var, &count, VAR_ALIAS);
8042 
8043 	if (count == 0)
8044 	{
8045 		char *var_text = get_variable(var);
8046 		cJSON_AddStringToObject(parent, name, var_text);
8047 		new_free(&var_text);
8048 	}
8049 	else
8050 	{
8051 		cJSON *child = cJSON_CreateObject();
8052 
8053 		if (child)
8054 		{
8055 			cJSON_AddItemToObject(parent, name, child);
8056 
8057 			for (i = 0; i < count && err == 0; i++)
8058 				err = implode_struct_var(sublist[i], child);
8059 		}
8060 		else
8061 		{
8062 			yell("cJSON_CreateObject() returned NULL");
8063 			err = ENOMEM;
8064 		}
8065 	}
8066 
8067 	for (i = 0; i < count; i++)
8068 		new_free(&sublist[i]);
8069 	new_free(&sublist);
8070 	return err;
8071 }
8072 
BUILT_IN_FUNCTION(function_json_implode,input)8073 BUILT_IN_FUNCTION(function_json_implode, input)
8074 {
8075 	const char *var;
8076 	const char *canon_var;
8077 	cJSON *root;
8078 	char **sublist;
8079 	int count;
8080 	int i;
8081 	char *json = NULL;
8082 	char *retval;
8083 	int err = 0;
8084 
8085 	GET_FUNC_ARG(var, input);
8086 	canon_var = remove_brackets(var, NULL);
8087 
8088 	sublist = get_subarray_elements(canon_var, &count, VAR_ALIAS);
8089 	new_free(&canon_var);
8090 
8091 	if (count == 0)
8092 		RETURN_EMPTY;
8093 
8094 	root = cJSON_CreateObject();
8095 
8096 	if (root)
8097 	{
8098 		for (i = 0; i < count && err == 0; i++)
8099 			err = implode_struct_var(sublist[i], root);
8100 
8101 		if (err == 0)
8102 			json = cJSON_Print(root);
8103 
8104 		cJSON_Delete(root);
8105 	}
8106 
8107 	for (i = 0; i < count; i++)
8108 		new_free(&sublist[i]);
8109 	new_free(&sublist);
8110 
8111 	retval = malloc_strdup(json);
8112 
8113 	if (json)
8114 		cJSON_free(json);
8115 
8116 	return retval;
8117 }
8118 
BUILT_IN_FUNCTION(function_uuid4,input)8119 BUILT_IN_FUNCTION(function_uuid4, input)
8120 {
8121 	char *retval;
8122 	char *var;
8123 
8124 	if (!my_stricmp(input, "NODASHES"))
8125 		retval = uuid4_generate_no_dashes();
8126 	else
8127 		retval = uuid4_generate();
8128 
8129 	RETURN_MSTR(retval);	/* Never pass function call to RETURN_* */
8130 }
8131 
BUILT_IN_FUNCTION(function_execctl,input)8132 BUILT_IN_FUNCTION(function_execctl, input)
8133 {
8134 	char *retval;
8135 	retval = execctl(input);
8136 	RETURN_MSTR(retval);	/* Never pass function call to RETURN_* */
8137 }
8138 
BUILT_IN_FUNCTION(function_cp437test,input)8139 BUILT_IN_FUNCTION(function_cp437test, input)
8140 {
8141 	unsigned char 	my_string[257];
8142 	size_t		my_string_len;
8143 	char *		result;
8144 	size_t		resultlen = 0;
8145 	unsigned int	i;
8146 
8147 	for (i = 0; i < 256; i++)
8148 		my_string[i] = (unsigned char) i + 1;
8149 	my_string[256] = 0;
8150 
8151 	my_string_len = 257;
8152 	result = cp437_to_utf8(my_string, my_string_len, &resultlen);
8153 	say("%s", result);
8154 	RETURN_MSTR(result);
8155 }
8156 
8157