1 /*
2  * IRC-II Copyright (C) 1990 Michael Sandroff and others,
3  * This file - Copyright (C) 1993,1995 Aaron Gifford
4  * This file written by Aaron Gifford and contributed to the EPIC
5  *   project by Jeremy Nelson to whom it was contributed in Nov, 1993.
6  *
7  * Used with permission.  See the COPYRIGHT file for copyright info.
8  */
9 /*
10 	DATE OF THIS VERSION:
11 	---------------------
12 	Sat Nov 27 23:00:20 MST 1993
13 
14 	NEW SINCE 20 NOV 1993:
15 	----------------------
16   	Two new functions were added: GETMATCHES() and GETRMATCHES()
17 
18 	BUGS:
19 	-----
20   	I had a script that used these functions that caused ircII to crash
21 	once or twice, but I have been unable to trace the cause or reproduce
22 	the crash.  I would appreciate any help or info if anyone else
23 	experiences similar problems.  This IS my first time using writing code
24 	to work with ircII code.
25 
26 	ARGUMENTS:
27 	----------
28   	array_name  : A string of some sort (no spaces, case insensitive)
29 		identifying an array, either an existing array, or a new array.
30 
31   	item_number : A number or index into the array.  All array items are
32 		numbered beginning at zero and continuing sequentially.  The
33 		item number can be an existing item of an existing array, a
34 		new item in an existing array (if the previous greatest item
35 		number was 5, then the new item number must be 6, maintaining
36 		the sequence), or a new item (must be item zero) in a new array.
37   	data_...    : Basically any data you want.  It is stored internally as a
38                 character string, and in indexed (more to come) internally
39                 using the standard C library function strcmp().
40 
41 	FUNCTIONS:
42 	----------
43  	SETITEM(array_name item_number data_to_be_stored)
44     	Use SETITEM to set an existing item of an existing array to a new value,
45     	to create a new value in an existing array (see note about item_number),
46     	or to create a new array (see note about item_number above).
47     	RETURNS: 0 (zero) if it successfully sets and existing item in the array
48              	 1 if it successfully creates a new array
49              	 2 if it successfully adds a new item to an existing array
50             	-1 if it was unable to find the array specified (item_number > 0)
51             	-2 if it was unable to find the item in an existing array
52                		(item_number was too large)
53 
54   	GETITEM(array_name item_number)
55     	Use this to retrieve previously stored data.
56     	RETURNS: the data requested
57              OR an empty string if the array did not exist or the item did not.
58 
59   	NUMITEMS(array_name)
60     	RETURNS: the number of items in the array
61              OR zero if the array name is invalid.  Useful for auto-adding to
62              an array:
63                  alias ADDDATA {
64                      if (SETITEM(my-array $NUMITEMS(my-array) $*) >= 0) {
65                          echo ADDED DATA
66                      } {
67                          echo FAILED TO ADD DATA
68                      }
69                  }
70 
71   	DELITEM(array_name item_number)
72     	This deletes the item requested from the array.  If it is the last item
73     	(item zero), it deletes the array.  If there are items numbered higher
74     	than this item, they will each be moved down.  So if we had a 25 item
75     	array called "MY-ARRAY" and deleted item 7, items 8 through 24 (remember
76     	that a 25 item array is numbered 0 to 24) would be moved down and become
77     	items 7 through 23.
78     	RETURNS:  Zero on success,
79               -1 if unable to find the array,
80               -2 if unable find the item.
81 
82   	MATCHITEM(array_name pattern)
83     	Searches through the items in the array for the item that best matches
84 	the pattern, much like the MATCH() function does.
85     	RETURNS: zero or a positive number which is the item_number of the match
86              OR -1 if unable to find the array,
87              OR -2 if no match was found in the array
88 
89   	RMATCHITEM(array_name data_to_look_for)
90     	This treats the items in the array as patterns, searching for the
91 	pattern in the array that best matches the data_to_look_for, working
92 	similarly to the RMATCH() function.
93     	RETURNS: zero or a positive number which is the item_number of the match
94              OR -1 if unable to find the array,
95              OR -2 if no match was found in the array
96 
97   	GETMATCHES(array_name pattern)
98     	Seeks all array items that match the pattern.
99    	RETURNS: A string containing a space-separated list of item numbers of
100              array elements that match the pattern, or a null string if no
101              matches were found, or if the array was not found.
102 
103   	GETRMATCHES(array_name data_to_look_for)
104     	Treats all array items as patterns, and seeks to find all patterns that
105     	match the data_to_look_for.
106     	RETURNS: A string containing a space-separated list of item numbers of
107              array elements that match the data, or a null string if no
108              matches were found, or if the array was not found.
109 
110   	FINDITEM(array_name data_to_search_for)
111     	This does a binary search on the items stored in the array and returns
112     	the item number of the data.  It is an EXACT MATCH search.  It is highly
113     	case sensitive, using C's strcmp() and not IRCII's caseless comparison
114    	functions.  I did it this way, because I needed it, so there!  ;)
115     	RETURNS: zero or a positive number on success -- this number IS the
116              item_number of the first match it found
117              OR -1 if unable to find the array,
118              OR -2 if the item was not found in the array.
119 
120   	IGETITEM(array_name index_number)
121     	This is exactly like GETITEM() except it uses a index number in the same
122    	range as the item_number's.  It returns the item that corresponds to the
123     	internal alphabetized index that these functions maintain.  Thus if you
124     	access items 0 to 24 of "MY-ARRAY" with this function, you would observe
125     	that the items returned came in an almost alphabetized manner.  They
126 	would not be truly alphabetized because the ordering is done using
127 	strcmp() which is case sensitive.
128     	RETURNS: the data to which the index refers
129              OR an empty string on failure to find the array or index.
130 
131   	INDEXTOITEM(array_name index_number)
132     	This converts an index_number to an item_number.
133     	RETURNS: the item_number that corresponds to the index_number for
134 		the array
135              OR -1 if unable to find the array,
136              OR -2 if the index_number was invalid
137 
138   	ITEMTOINDEX(array_name item_number)
139     	This converts an item_number to an index_number.
140     	RETURNS: the index_number that corresponds to the item_number for
141 		the array
142              OR -1 if unable to find the array,
143              OR -2 if the item_number was invalid
144 
145   	DELARRAY(array_name)
146     	This deletes all items in an array.
147     	RETURNS:  zero on success, -1 if it couldn't find the array.
148 
149   	NUMARRAYS()
150     	RETURNS: the number of arrays that currently exist.
151 
152   	GETARRAYS()
153     	RETURNS: a string consisting of all the names of all the arrays
154 		 separated by spaces.
155 
156 Thanks,
157 Aaron Gifford
158 Karll on IRC
159 <agifford@sci.dixie.edu>
160 
161 */
162 /*
163  * FILE:       alias.c
164  * WRITTEN BY: Aaron Gifford (Karll on IRC)
165  * DATE:       Sat Nov 27 23:00:20 MST 1993
166  */
167 
168 #include "irc.h"
169 static char cvsrevision[] = "$Id: array.c 227 2013-02-16 08:11:07Z keaston $";
170 CVS_REVISION(array_c)
171 
172 #include "array.h"
173 #include "ircaux.h"
174 #include "output.h"
175 #undef index
176 
177 #define MAIN_SOURCE
178 #include "modval.h"
179 
180 #define ARRAY_THRESHOLD	100
181 
182 typedef struct an_array_struct {
183 	char **item;
184 	long *index;
185 	long size;
186 } an_array;
187 
188 static an_array array_info = {
189         NULL,
190         NULL,
191         0L
192 };
193 
194 static an_array *array_array = NULL;
195 
196 /*
197  * find_item() does a binary search of array.item[] using array.index[]
198  * to find an exact match of the string *find.  If found, it returns the item
199  * number (array.item[item_number]) of the match.  Otherwise, it returns a
200  * negative number.  The negative number, if made positive again, and then
201  * having 1 subtracted from it, will be the item_number where the string *find
202  * should be inserted into the array.item[].  The function_setitem() makes use
203  * of this attribute.
204  */
find_item(an_array array,char * find)205 extern 	long		find_item (an_array array, char *find)
206 {
207         long top, bottom, key, cmp;
208 
209         top = array.size - 1;
210         bottom = 0;
211 
212         while (top >= bottom)
213 	{
214                 key = (top - bottom) / 2 + bottom;
215                 cmp = strcmp(find, array.item[array.index[key]]);
216                 if (cmp == 0)
217                         return key;
218                 if (cmp < 0)
219                         top = key - 1;
220                 else
221                         bottom = key + 1;
222         }
223         return -bottom - 1;
224 }
225 
226 /*
227  * insert_index() takes a valid index (newIndex) and inserts it into the array
228  * **index, then increments the *size of the index array.
229  */
insert_index(long ** index,long * size,long newIndex)230 extern 	void		insert_index (long **index, long *size, long newIndex)
231 {
232 	long cnt;
233 
234 	if (*size)
235 		RESIZE(*index, long, *size + 1);
236 	else
237 	{
238 		*index = (long *)new_malloc(sizeof(long));
239 		newIndex = 0;
240 	}
241 
242 	for (cnt = *size; cnt > newIndex; cnt--)
243 		(*index)[cnt] = (*index)[cnt - 1];
244 	(*index)[newIndex] = *size;
245 	(*size)++;
246 }
247 
248 /*
249  * move_index() moves the array.index[] up or down to make room for new entries
250  * or to clean up so an entry can be deleted.
251  */
move_index(an_array * array,long oldindex,long newindex)252 extern	void		move_index (an_array *array, long oldindex, long newindex)
253 {
254 	long temp;
255 
256 	if (newindex > oldindex)
257 		newindex--;
258 	if (newindex == oldindex)
259 		return;
260 
261 	temp = array->index[oldindex];
262 
263 	if (oldindex < newindex)
264 		for (; oldindex < newindex; oldindex++)
265 			array->index[oldindex] = array->index[oldindex + 1];
266 	else
267 		for(; oldindex > newindex; oldindex--)
268 			array->index[oldindex] = array->index[oldindex - 1];
269 
270 	array->index[newindex] = temp;
271 }
272 
273 /*
274  * find_index() attempts to take an item and discover the index number
275  * that refers to it.  find_index() assumes that find_item() WILL always return
276  * a positive or zero result (be successful) because find_index() assumes that
277  * the item is valid, and thus a find will be successful.  I don't know what
278  * value ARRAY_THRESHOLD ought to be.  I figured someone smarter than I am
279  * could figure it out and tell me or tell me to abandon the entire idea.
280  */
find_index(an_array * array,long item)281 extern	long		find_index (an_array *array, long item)
282 {
283 	long i = 0;
284 
285 	if (array->size >= ARRAY_THRESHOLD)
286 	{
287 		i = find_item(*array, array->item[item]);
288 		while (i >= 0 && !strcmp(array->item[array->index[i]], array->item[item]))
289 			i--;
290 		i++;
291 	}
292 	while(array->index[i] != item && i < array->size)
293 		i++;
294 
295 	if (i == array->size)
296 		say("ERROR in find_index()");
297 	return i;
298 }
299 
300 /*
301  * get_array() searches and finds the array referenced by *name.  It returns
302  * a pointer to the array, or a null pointer on failure to find it.
303  */
get_array(char * name)304 extern 	an_array *	get_array (char *name)
305 {
306 	long index;
307 
308 	if (array_info.size && *name)
309         {
310                 upper(name);
311                 if ((index = find_item(array_info, name)) >= 0)
312                         return &array_array[array_info.index[index]];
313 	}
314 	return NULL;
315 }
316 
317 
318 /*
319  * delete_array() deletes the contents of an entire array.  It assumes that
320  * find_item(array_info, name) will succeed and return a valid zero or positive
321  * value.
322  */
delete_array(char * name)323 extern	void		delete_array (char *name)
324 {
325         char **ptr;
326         long cnt;
327         long index;
328         long item;
329         an_array *array;
330 
331         index = find_item(array_info, name);
332         item = array_info.index[index];
333         array = &array_array[item];
334         for (ptr=array->item, cnt=0; cnt < array->size; cnt++, ptr++)
335                 new_free((char **)ptr);
336         new_free((char **)&array->item);
337         new_free((char **)&array->index);
338 
339         if (array_info.size > 1)
340         {
341                 for(cnt = 0; cnt < array_info.size; cnt++)
342                         if (array_info.index[cnt] > item)
343                                 (array_info.index[cnt])--;
344                 move_index(&array_info, index, array_info.size);
345                 array_info.size--;
346                 for(ptr=&array_info.item[item], cnt=item; cnt < array_info.size; cnt++, ptr++, array++)
347                 {
348                         *ptr = *(ptr + 1);
349                         *array = *(array + 1);
350                 }
351                 RESIZE(array_info.item, char *, array_info.size);
352                 RESIZE(array_info.index, long, array_info.size);
353                 RESIZE(array_array, an_array, array_info.size);
354         }
355         else
356         {
357                 new_free((char **)&array_info.item);
358                 new_free((char **)&array_info.index);
359                 new_free((char **)&array_array);
360                 array_info.size = 0;
361         }
362 }
363 
delete_all_arrays(void)364 void delete_all_arrays(void)
365 {
366         char **ptr;
367         long index;
368         an_array *array;
369 #if 0
370 m_s3cat(&result, space, array_info.item[array_info.index[index]]);
371 #endif
372         for (index = 0; index < array_info.size; index++)
373         {
374         	array = &array_array[index];
375 		ptr = array->item;
376 		while (array->size > 0)
377 		{
378         	        new_free((char **)ptr);
379 			ptr++;
380 			array->size--;
381 		}
382 	        new_free((char **)&array->item);
383         	new_free((char **)&array->index);
384 	}
385 	for (index = 0; index < array_info.size; index++)
386 	{
387 		ptr = (char **)&array_info.item[index];
388 		new_free(ptr);
389 		ptr++;
390 	}
391 	new_free((char **)&array_info.item);
392 	new_free((char **)&array_info.index);
393 	new_free((char **)&array_array);
394 	array_info.size = 0;
395 }
396 
397 /*
398  * Now for the actual alias functions
399  * ==================================
400  */
401 
402 /*
403  * These are the same ones found in alias.c
404  */
405 #define EMPTY empty_string
406 #define RETURN_EMPTY return m_strdup(EMPTY)
407 #define RETURN_IF_EMPTY(x) if (empty( x )) RETURN_EMPTY
408 #define GET_INT_ARG(x, y) {RETURN_IF_EMPTY(y); x = my_atol(safe_new_next_arg(y, &y));}
409 #define GET_FLOAT_ARG(x, y) {RETURN_IF_EMPTY(y); x = atof(safe_new_next_arg(y, &y));}
410 #define GET_STR_ARG(x, y) {RETURN_IF_EMPTY(y); x = new_next_arg(y, &y);RETURN_IF_EMPTY(x);}
411 #define RETURN_STR(x) return m_strdup(x ? x : EMPTY);
412 #define RETURN_INT(x) return m_strdup(ltoa(x));
413 
414 /*
415  * function_matchitem() attempts to match a pattern to the contents of an array
416  * RETURNS -1 if it cannot find the array, or -2 if no matches occur
417  */
BUILT_IN_FUNCTION(function_matchitem)418 BUILT_IN_FUNCTION(function_matchitem)
419 {
420 	char	*name;
421 	long	index;
422 	an_array *array;
423         long     current_match;
424         long     best_match = 0;
425         long     match = -1;
426 
427 	if ((name = next_arg(input, &input)) && (array = get_array(name)))
428 	{
429 		match = -2;
430         	if (*input)
431         	{
432 			for (index = 0; index < array->size; index++)
433                 	{
434                         	if ((current_match = wild_match(input, array->item[index])) > best_match)
435                         	{
436                                		match = index;
437                                		best_match = current_match;
438                         	}
439                 	}
440 		}
441 	}
442 
443 	RETURN_INT(match);
444 }
445 
BUILT_IN_FUNCTION(function_igetmatches)446 BUILT_IN_FUNCTION(function_igetmatches)
447 {
448 	char    *result = NULL;
449 	char    *name = NULL;
450 	long    item;
451 	an_array *array;
452 
453 	if ((name = next_arg(input, &input)) &&
454 		(array = get_array(name)) && *input)
455 	{
456 		if (*input)
457 		{
458 			for (item = 0; item < array->size; item++)
459 			{
460 				if (wild_match(input, array->item[item]) > 0)
461 					m_s3cat(&result, space, ltoa(find_index(array, item)));
462 			}
463 		}
464 	}
465 
466 	if (!result)
467 		RETURN_EMPTY;
468 
469 	return result;
470 }
471 
472 /*
473  * function_listarray() attempts to list the contents of an array
474  * RETURNS "" if it cannot find the array
475  */
BUILT_IN_FUNCTION(function_listarray)476 BUILT_IN_FUNCTION(function_listarray)
477 {
478 	char	*name;
479 	an_array *array;
480 	long index;
481 	char *result = NULL;
482 
483 	if ((name = next_arg(input, &input)) && (array = get_array(name)))
484 	{
485 		for (index = 0; index < array->size; index++)
486 			m_s3cat(&result, space, array->item[index]);
487 	}
488 	return result ? result : m_strdup(empty_string);
489 }
490 
491 /*
492  * function_getmatches() attempts to match a pattern to the contents of an
493  * array and returns a list of item_numbers of all items that match the pattern
494  * or it returns an empty string if not items matches or if the array was not
495  * found.
496  */
BUILT_IN_FUNCTION(function_getmatches)497 BUILT_IN_FUNCTION(function_getmatches)
498 {
499         char    *result = NULL;
500         char    *name = NULL;
501         long    index;
502         an_array *array;
503 
504         if ((name = next_arg(input, &input)) &&
505 	    (array = get_array(name)) && *input)
506         {
507                 if (*input)
508                 {
509                         for (index = 0; index < array->size; index++)
510                         {
511                                 if (wild_match(input, array->item[index]) > 0)
512 					m_s3cat(&result, space, ltoa(index));
513                         }
514                 }
515         }
516 	if (!result)
517 		RETURN_EMPTY;
518         return result;
519 }
520 
521 /*
522  * function_rmatchitem() attempts to match the input text with an array of
523  * patterns, much like RMATCH()
524  * RETURNS -1 if it cannot find the array, or -2 if no matches occur
525  */
BUILT_IN_FUNCTION(function_rmatchitem)526 BUILT_IN_FUNCTION(function_rmatchitem)
527 {
528         char    *name = NULL;
529         long    index;
530         an_array *array;
531         long     current_match;
532         long     best_match = 0;
533         long     match = -1;
534 
535         if ((name = next_arg(input, &input)) && (array = get_array(name)))
536         {
537                 match = -2;
538                 if (*input)
539                 {
540                         for (index = 0; index < array->size; index++)
541                         {
542                                 if ((current_match = wild_match(array->item[index], input)) > best_match)
543                                 {
544                                         match = index;
545                                         best_match = current_match;
546                                 }
547                         }
548                 }
549         }
550 	RETURN_INT(match)
551 }
552 
553 /*
554  * function_getrmatches() attempts to match the input text with an array of
555  * patterns, and returns a list of item_numbers of all patterns that match the
556  * given text, or it returns a null string if no matches occur or if the array
557  * was not found.
558  */
BUILT_IN_FUNCTION(function_getrmatches)559 BUILT_IN_FUNCTION(function_getrmatches)
560 {
561         char    *result = NULL;
562         char    *name = NULL;
563         long    index;
564         an_array *array;
565 
566         if ((name = next_arg(input, &input)) && (array = get_array(name)))
567         {
568                 if (*input)
569                 {
570                         for (index = 0; index < array->size; index++)
571                         {
572                                 if (wild_match(array->item[index], input) > 0)
573 					m_s3cat(&result, space, ltoa(index));
574                         }
575                 }
576         }
577 
578 	if (!result)
579 		RETURN_EMPTY;
580         return result;
581 }
582 
583 /*
584  * function_numitems() returns the number of items in an array, or -1 if unable
585  * to find the array
586  */
BUILT_IN_FUNCTION(function_numitems)587 BUILT_IN_FUNCTION(function_numitems)
588 {
589         char *name = NULL;
590 	an_array *array;
591 	long items = 0;
592 
593         if ((name = next_arg(input, &input)) && (array = get_array(name)))
594                 items = array->size;
595 
596 	RETURN_INT(items);
597 }
598 
599 /*
600  * function_getitem() returns the value of the specified item of an array, or
601  * returns an empty string on failure to find the item or array
602  */
BUILT_IN_FUNCTION(function_getitem)603 BUILT_IN_FUNCTION(function_getitem)
604 {
605 	char *name = NULL;
606 	char *itemstr = NULL;
607 	long item;
608 	an_array *array;
609 	char *found = NULL;
610 
611 	if ((name = next_arg(input, &input)) && (array = get_array(name)))
612 	{
613 		if ((itemstr = next_arg(input, &input)))
614 		{
615 			item = my_atol(itemstr);
616 			if (item >= 0 && item < array->size)
617 				found = array->item[item];
618 		}
619 	}
620 	RETURN_STR(found);
621 }
622 
623 /*
624  * function_setitem() sets an item of an array to a value, or creates a new
625  * array if the array doesn not already exist and the item number is zero, or
626  * it adds a new item to an existing array if the item number is one more than
627  * the prevously largest item number in the array.
628  * RETURNS: 0 on success
629  *          1 on success if a new item was added to an existing array
630  *          2 on success if a new array was created and item zero was set
631  *         -1 if it is unable to find the array (and item number was not zero)
632  *         -2 if it was unable to find the item (item < 0 or item was greater
633  *            than 1 + the prevous maximum item number
634  */
BUILT_IN_FUNCTION(function_setitem)635 BUILT_IN_FUNCTION(function_setitem)
636 {
637         char *name = NULL;
638 	char *itemstr = NULL;
639 	long item;
640 	long index = 0;
641 	long oldindex;
642 	an_array *array;
643 	int result = -1;
644 
645         if ((name = next_arg(input, &input)))
646         {
647 		if (strlen(name) && (itemstr = next_arg(input, &input)))
648 		{
649 			item = my_atol(itemstr);
650 			if (item >= 0)
651 			{
652 				upper(name);
653 				if (array_info.size && ((index = find_item(array_info, name)) >= 0))
654        	         		{
655 					array =  &array_array[array_info.index[index]];
656 					result = -2;
657 					if (item < array->size)
658 					{
659 						oldindex = find_index(array, item);
660 						index = find_item(*array, input);
661 						index = (index >= 0) ? index : (-index) - 1;
662 						move_index(array, oldindex, index);
663 						new_free(&array->item[item]);
664 						malloc_strcpy(&array->item[item], input);
665 						result = 0;
666 					}
667 					else if (item == array->size)
668 					{
669 						RESIZE(array->item, char *, (array->size + 1));
670 						array->item[item] = NULL;
671 						malloc_strcpy(&array->item[item], input);
672 						index = find_item(*array, input);
673 						index = (index >= 0) ? index : (-index) - 1;
674 						insert_index(&array->index, &array->size, index);
675 						result = 2;
676 					}
677 				}
678 				else
679 				{
680 					if (item == 0)
681 					{
682 						if (array_info.size)
683 							RESIZE(array_array, an_array, (array_info.size + 1));
684 						else
685 							array_array = (an_array*)new_malloc(sizeof(an_array));
686 						array = &array_array[array_info.size];
687 						array->size = 1;
688 						array->item = (char **)new_malloc(sizeof(char *));
689 						array->index = (long *)new_malloc(sizeof(long));
690 						array->item[0] = (char*) 0;
691 						array->index[0] = 0;
692 						malloc_strcpy(&array->item[0], input);
693 						if (array_info.size)
694 							RESIZE(array_info.item, char *, (array_info.size + 1));
695 						else
696 							array_info.item = (char **)new_malloc(sizeof(char *));
697 						array_info.item[array_info.size] = NULL;
698 						malloc_strcpy(&array_info.item[array_info.size], name);
699 						insert_index(&array_info.index, &array_info.size, (-index) - 1);
700 						result = 1;
701 					}
702 				}
703 			}
704 		}
705 	}
706 	RETURN_INT(result);
707 }
708 
709 /*
710  * function_getarrays() returns a string containg the names of all currently
711  * existing arrays separated by spaces
712  */
BUILT_IN_FUNCTION(function_getarrays)713 BUILT_IN_FUNCTION(function_getarrays)
714 {
715 	long index;
716 	char *result = NULL;
717 
718 	for (index = 0; index < array_info.size; index++)
719                 if (!input || !*input || wild_match(input, array_info.item[array_info.index[index]]))
720                         m_s3cat(&result, space, array_info.item[array_info.index[index]]);
721 
722 	if (!result)
723 		RETURN_EMPTY;
724 
725 	return result;
726 }
727 
728 /*
729  * function_numarrays() returns the number of currently existing arrays
730  */
BUILT_IN_FUNCTION(function_numarrays)731 BUILT_IN_FUNCTION(function_numarrays)
732 {
733 	RETURN_INT(array_info.size)
734 }
735 
736 /*
737  * function_finditem() does a binary search and returns the item number of
738  * the string that exactly matches the string searched for, or it returns
739  * -1 if unable to find the array, or -2 if unable to find the item.
740  */
BUILT_IN_FUNCTION(function_finditem)741 BUILT_IN_FUNCTION(function_finditem)
742 {
743         char    *name = NULL;
744         an_array *array;
745 	long	item = -1;
746 
747 	if ((name = next_arg(input, &input)) && (array = get_array(name)))
748         {
749 		if (*input)
750 		{
751 			item = find_item(*array, input);
752 			item = (item >= 0) ? array->index[item] : -2;
753 		}
754         }
755 	RETURN_INT(item)
756 }
757 
758 /*
759  * function_ifinditem() does a binary search and returns the index number of
760  * the string that exactly matches the string searched for, or it returns
761  * -1 if unable to find the array, or -2 if unable to find the item.
762  */
BUILT_IN_FUNCTION(function_ifinditem)763 BUILT_IN_FUNCTION(function_ifinditem)
764 {
765         char    *name = NULL;
766         an_array *array;
767         long    item = -1;
768 
769         if ((name = next_arg(input, &input)) && (array = get_array(name)))
770         {
771 		if (*input)
772 		{
773 			if ((item = find_item(*array, input)) < 0)
774 				item = -2;
775 		}
776         }
777 	RETURN_INT(item)
778 }
779 
780 /*
781  * function_igetitem() returns the item referred to by the passed-in index
782  * or returns an empty string if unable to find the array or if the index was
783  * invalid.
784  */
BUILT_IN_FUNCTION(function_igetitem)785 BUILT_IN_FUNCTION(function_igetitem)
786 {
787         char *name = NULL;
788         char *itemstr = NULL;
789         long item;
790         an_array *array;
791         char *found = NULL;
792 
793         if ((name = next_arg(input, &input)) && (array = get_array(name)))
794         {
795                 if ((itemstr = next_arg(input, &input)))
796                 {
797                         item = my_atol(itemstr);
798                         if (item >= 0 && item < array->size)
799                                 found = array->item[array->index[item]];
800                 }
801         }
802 	RETURN_STR(found)
803 }
804 
805 /*
806  * function_indextoitem() converts an index number to an item number for the
807  * specified array.  It returns a valid item number, or -1 if unable to find
808  * the array, or -2 if the index was invalid.
809  */
BUILT_IN_FUNCTION(function_indextoitem)810 BUILT_IN_FUNCTION(function_indextoitem)
811 {
812         char *name = NULL;
813         char *itemstr = NULL;
814         long item;
815         an_array *array;
816 	long found = -1;
817 
818         if ((name = next_arg(input, &input)) && (array = get_array(name)))
819         {
820 		found = -2;
821                 if ((itemstr = next_arg(input, &input)))
822                 {
823                         item = my_atol(itemstr);
824                         if (item >= 0 && item < array->size)
825                                 found = array->index[item];
826                 }
827         }
828 	RETURN_INT(found)
829 }
830 
831 /*
832  * function_itemtoindex() takes an item number and searches for the index that
833  * refers to the item.  It returns the index number, or -1 if unable to find
834  * the array, or -2 if the item was invalid.
835  */
BUILT_IN_FUNCTION(function_itemtoindex)836 BUILT_IN_FUNCTION(function_itemtoindex)
837 {
838         char *name;
839         char *itemstr;
840         long item;
841         an_array *array;
842         long found = -1;
843 
844         if ((name = next_arg(input, &input)) && (array = get_array(name)))
845 	{
846                 found = -2;
847 		if ((itemstr = next_arg(input, &input)))
848                 {
849                         item = my_atol(itemstr);
850                         if (item >= 0 && item < array->size)
851                                 found = find_index(array, item);
852                 }
853         }
854 	RETURN_INT(found)
855 }
856 
857 /*
858  * function_delitem() deletes an item of an array and moves the contents of the
859  * array that were stored "above" the item down by one.  It returns 0 (zero)
860  * on success, -1 if unable to find the array, -2 if unable to find the item.
861  * Also, if the item is the last item in the array, it deletes the array.
862  */
BUILT_IN_FUNCTION(function_delitem)863 BUILT_IN_FUNCTION(function_delitem)
864 {
865         char *name;
866 	char *itemstr;
867         char **strptr;
868         long item;
869 	long cnt;
870 	long oldindex;
871         an_array *array;
872         long found = -1;
873 
874         if ((name = next_arg(input, &input)) && (array = get_array(name)))
875         {
876 		found = -2;
877                 if ((itemstr = next_arg(input, &input)))
878                 {
879                         item = my_atol(itemstr);
880                         if (item >= 0 && item < array->size)
881 			{
882                                 if (array->size == 1)
883 					delete_array(name);
884 				else
885 				{
886 					oldindex = find_index(array, item);
887 					for(cnt = 0; cnt < array->size; cnt++)
888 						if (array->index[cnt] > item)
889 							(array->index[cnt])--;
890 					move_index(array, oldindex, array->size);
891                                 	new_free(&array->item[item]);
892 					array->size--;
893 					for(strptr=&(array->item[item]), cnt=item; cnt < array->size; cnt++, strptr++)
894 						*strptr = *(strptr + 1);
895 					RESIZE(array->item, char *, array->size);
896 					RESIZE(array->index, long, array->size);
897 				}
898 				found = 0;
899                         }
900                 }
901         }
902 	RETURN_INT(found)
903 }
904 
905 /*
906  * function_delarray() deletes the entire contents of the array using the
907  * delete_array() function above.  It returns 0 on success, -1 on failure.
908  */
BUILT_IN_FUNCTION(function_delarray)909 BUILT_IN_FUNCTION(function_delarray)
910 {
911         char *name;
912         long found = -1;
913 
914 	if ((name = next_arg(input, &input)) && (get_array(name)))
915         {
916                 delete_array(name);
917 		found = 0;
918 	}
919 	RETURN_INT(found)
920 }
921 /*
922  * function_ifindfirst() returns the first index of an exact match with the
923  * search string, or returns -2 if unable to find the array, or -1 if unable
924  * to find any matches.
925  */
BUILT_IN_FUNCTION(function_ifindfirst)926 BUILT_IN_FUNCTION(function_ifindfirst)
927 {
928         char    *name;
929         an_array *array;
930         long    item = -1;
931 
932         if ((name = next_arg(input, &input)) && (array = get_array(name)))
933         {
934 		if (*input)
935 		{
936 			if ((item = find_item(*array, input)) < 0)
937 				item = -2;
938 			else
939 			{
940 				while (item >= 0 && !strcmp(array->item[array->index[item]], input))
941 					item--;
942 				item++;
943 			}
944 		}
945         }
946 	RETURN_INT(item)
947 }
948 
949 /*
950  * Given an array name with various strings in it, we wild_match() against
951  * the elements within the array. This allows parsing using % and *
952  * for wildcards. We return only the best match from the array, unlike
953  * getmatch() which returns ALL the matching items.
954  */
955 
BUILT_IN_FUNCTION(function_gettmatch)956 BUILT_IN_FUNCTION(function_gettmatch)
957 {
958 char *name;
959 an_array *array;
960 char *ret = NULL;
961 #if 0
962 <shade> gettmatch(users % user@host *) would match the userhost mask in the
963              second word of the array
964 #endif
965         if ((name = next_arg(input, &input)) && (array = get_array(name)))
966         {
967 		if (*input)
968           	{
969 			int index, current_match;
970 			int best_match = 0;
971 			int match = -1;
972                         for (index = 0; index < array->size; index++)
973                         {
974                                 if ((current_match = wild_match(input, array->item[index])) > best_match)
975                                 {
976                                         match = index;
977                                         best_match = current_match;
978                                 }
979                         }
980 			if (match != -1)
981 				ret = array->item[match];
982 
983 		}
984 	}
985 	RETURN_STR(ret);
986 }
987 
BUILT_IN_FUNCTION(function_igetrmatches)988 BUILT_IN_FUNCTION(function_igetrmatches)
989 {
990 	char    *result = (char *) 0;
991 	char    *name = (char *) 0;
992 	long    item;
993 	an_array *array;
994 
995 	if ((name = next_arg(input, &input)) &&
996 		(array = get_array(name)) && *input)
997 	{
998 		if (*input)
999 		{
1000 			for (item = 0; item < array->size; item++)
1001 			{
1002 				if (wild_match(array->item[item], input) > 0)
1003 					m_s3cat(&result, space, ltoa(find_index(array, item)));
1004 			}
1005 		}
1006 	}
1007 
1008 	if (!result)
1009 		RETURN_EMPTY;
1010 
1011 	return result;
1012 }
1013