1 /* $EPIC: array.c,v 1.17 2003/10/31 08:19:24 crazyed Exp $ */
2 /*
3 * array.c -- Karll's Array Suite
4 *
5 * Copyright � 1993, 2003 Aaron Gifford and others.
6 * All rights reserved, used with permission.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notices, the above paragraph (the one permitting redistribution),
15 * this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. The names of the author(s) may not be used to endorse or promote
18 * products derived from this software without specific prior written
19 * permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33 /*
34 DATE OF THIS VERSION:
35 ---------------------
36 Sat Nov 27 23:00:20 MST 1993
37
38 NEW SINCE 20 NOV 1993:
39 ----------------------
40 Two new functions were added: GETMATCHES() and GETRMATCHES()
41
42 BUGS:
43 -----
44 I had a script that used these functions that caused ircII to crash
45 once or twice, but I have been unable to trace the cause or reproduce
46 the crash. I would appreciate any help or info if anyone else
47 experiences similar problems. This IS my first time using writing code
48 to work with ircII code.
49
50 ARGUMENTS:
51 ----------
52 array_name : A string of some sort (no spaces, case insensitive)
53 identifying an array, either an existing array, or a new array.
54
55 item_number : A number or index into the array. All array items are
56 numbered beginning at zero and continuing sequentially. The
57 item number can be an existing item of an existing array, a
58 new item in an existing array (if the previous greatest item
59 number was 5, then the new item number must be 6, maintaining
60 the sequence), or a new item (must be item zero) in a new array.
61 data_... : Basically any data you want. It is stored internally as a
62 character string, and in indexed (more to come) internally
63 using the standard C library function strcmp().
64
65 FUNCTIONS:
66 ----------
67 SETITEM(array_name item_number data_to_be_stored)
68 Use SETITEM to set an existing item of an existing array to a new value,
69 to create a new value in an existing array (see note about item_number),
70 or to create a new array (see note about item_number above).
71 RETURNS: 0 (zero) if it successfully sets and existing item in the array
72 1 if it successfully creates a new array
73 2 if it successfully adds a new item to an existing array
74 -1 if it was unable to find the array specified (item_number > 0)
75 -2 if it was unable to find the item in an existing array
76 (item_number was too large)
77
78 GETITEM(array_name item_number)
79 Use this to retrieve previously stored data.
80 RETURNS: the data requested
81 OR an empty string if the array did not exist or the item did not.
82
83 NUMITEMS(array_name)
84 RETURNS: the number of items in the array
85 OR zero if the array name is invalid. Useful for auto-adding to
86 an array:
87 alias ADDDATA {
88 if (SETITEM(my-array $NUMITEMS(my-array) $*) >= 0) {
89 echo ADDED DATA
90 } {
91 echo FAILED TO ADD DATA
92 }
93 }
94
95 DELITEM(array_name item_number)
96 This deletes the item requested from the array. If it is the last item
97 (item zero), it deletes the array. If there are items numbered higher
98 than this item, they will each be moved down. So if we had a 25 item
99 array called "MY-ARRAY" and deleted item 7, items 8 through 24 (remember
100 that a 25 item array is numbered 0 to 24) would be moved down and become
101 items 7 through 23.
102 RETURNS: Zero on success,
103 -1 if unable to find the array,
104 -2 if unable find the item.
105
106 DELITEMS(array_name item_number ...)
107 This functions exactly like DELITEM() but it deletes any number of items
108 and performs better for deleting larger numbers of items. Note that
109 DELITEM() still performs better for deleting a single item.
110
111 MATCHITEM(array_name pattern)
112 Searches through the items in the array for the item that best matches
113 the pattern, much like the MATCH() function does.
114 RETURNS: zero or a positive number which is the item_number of the match
115 OR -1 if unable to find the array,
116 OR -2 if no match was found in the array
117
118 RMATCHITEM(array_name data_to_look_for)
119 This treats the items in the array as patterns, searching for the
120 pattern in the array that best matches the data_to_look_for, working
121 similarly to the RMATCH() function.
122 RETURNS: zero or a positive number which is the item_number of the match
123 OR -1 if unable to find the array,
124 OR -2 if no match was found in the array
125
126 GETMATCHES(array_name pattern)
127 Seeks all array items that match the pattern.
128 RETURNS: A string containing a space-separated list of item numbers of
129 array elements that match the pattern, or a null string if no
130 matches were found, or if the array was not found.
131
132 GETRMATCHES(array_name data_to_look_for)
133 Treats all array items as patterns, and seeks to find all patterns that
134 match the data_to_look_for.
135 RETURNS: A string containing a space-separated list of item numbers of
136 array elements that match the data, or a null string if no
137 matches were found, or if the array was not found.
138
139 FINDITEM(array_name data_to_search_for)
140 This does a binary search on the items stored in the array and returns
141 the item number of the data. It is an EXACT MATCH search. It is highly
142 case sensitive, using C's strcmp() and not IRCII's caseless comparison
143 functions. I did it this way, because I needed it, so there! ;)
144 RETURNS: zero or a positive number on success -- this number IS the
145 item_number of the first match it found
146 OR -1 if unable to find the array,
147 OR -2 if the item was not found in the array.
148
149 IGETITEM(array_name index_number)
150 This is exactly like GETITEM() except it uses a index number in the same
151 range as the item_number's. It returns the item that corresponds to the
152 internal alphabetized index that these functions maintain. Thus if you
153 access items 0 to 24 of "MY-ARRAY" with this function, you would observe
154 that the items returned came in an almost alphabetized manner. They
155 would not be truly alphabetized because the ordering is done using
156 strcmp() which is case sensitive.
157 RETURNS: the data to which the index refers
158 OR an empty string on failure to find the array or index.
159
160 INDEXTOITEM(array_name index_number)
161 This converts an index_number to an item_number.
162 RETURNS: the item_number that corresponds to the index_number for
163 the array
164 OR -1 if unable to find the array,
165 OR -2 if the index_number was invalid
166
167 ITEMTOINDEX(array_name item_number)
168 This converts an item_number to an index_number.
169 RETURNS: the index_number that corresponds to the item_number for
170 the array
171 OR -1 if unable to find the array,
172 OR -2 if the item_number was invalid
173
174 DELARRAY(array_name)
175 This deletes all items in an array.
176 RETURNS: zero on success, -1 if it couldn't find the array.
177
178 NUMARRAYS()
179 RETURNS: the number of arrays that currently exist.
180
181 GETARRAYS()
182 RETURNS: a string consisting of all the names of all the arrays
183 separated by spaces.
184
185 Thanks,
186 Aaron Gifford
187 Karll on IRC
188 <agifford@sci.dixie.edu>
189
190 */
191 /*
192 * FILE: array.c
193 * WRITTEN BY: Aaron Gifford (Karll on IRC)
194 * DATE: Sat Nov 27 23:00:20 MST 1993
195 */
196
197 #include "irc.h"
198 #include "array.h"
199 #include "ircaux.h"
200 #include "output.h"
201 #include "functions.h"
202 #include "words.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 int*)a1],
232 qsort_array->item[*(const int*)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 int*)a1 - *(const int*)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 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