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(<ime);
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(©);
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(©);
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