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