root/1.8.3/tags/p6/src/funlist.c

Revision 1167, 77.8 KB (checked in by shawnw, 13 months ago)

Merge devel into trunk for p6 release

Line 
1/**
2 * \file funlist.c
3 *
4 * \brief List-handling functions for mushcode.
5 *
6 *
7 */
8#include "copyrite.h"
9
10#include "config.h"
11#include <string.h>
12#include <ctype.h>
13#include "conf.h"
14#include "case.h"
15#include "externs.h"
16#include "ansi.h"
17#include "parse.h"
18#include "function.h"
19#include "mymalloc.h"
20#include "mypcre.h"
21#include "match.h"
22#include "command.h"
23#include "attrib.h"
24#include "dbdefs.h"
25#include "flags.h"
26#include "mushdb.h"
27#include "lock.h"
28#include "sort.h"
29#include "confmagic.h"
30
31
32static char *next_token(char *str, char sep);
33static int regrep_helper(dbref who, dbref what, dbref parent,
34                         char const *name, ATTR *atr, void *args);
35enum itemfun_op { IF_DELETE, IF_REPLACE, IF_INSERT };
36static void do_itemfuns(char *buff, char **bp, char *str, char *num,
37                        char *word, char *sep, enum itemfun_op flag);
38
39char *iter_rep[MAX_ITERS];  /**< itext values */
40int iter_place[MAX_ITERS];  /**< inum numbers */
41int inum = 0;               /**< iter depth */
42int inum_limit = 0;         /**< limit of iter depth */
43extern const unsigned char *tables;
44
45#define RealGoodObject(x) (GoodObject(x) && !IsGarbage(x))
46
47static char *
48next_token(char *str, char sep)
49{
50  /* move pointer to start of the next token */
51
52  while (*str && (*str != sep))
53    str++;
54  if (!*str)
55    return NULL;
56  str++;
57  if (sep == ' ') {
58    while (*str == sep)
59      str++;
60  }
61  return str;
62}
63
64/** Convert list to array.
65 * Chops up a list of words into an array of words. The list is
66 * destructively modified. The array returned consists of
67 * mush_strdup'd strings.
68 * \param r pointer to array to store words.
69 * \param max maximum number of words to split out.
70 * \param list list of words as a string.
71 * \param sep separator character between list items.
72 * \return number of words split out.
73 */
74int
75list2arr_ansi(char *r[], int max, char *list, char sep)
76{
77  char *p, *lp;
78  int i;
79  int first;
80  ansi_string *as;
81  char *aptr;
82
83  as = parse_ansi_string(list);
84  aptr = as->text;
85
86  aptr = trim_space_sep(aptr, sep);
87
88  lp = list;
89  p = split_token(&aptr, sep);
90  first = 0;
91  for (i = 0; p && (i < max); i++, p = split_token(&aptr, sep)) {
92    lp = list;
93    safe_ansi_string(as, p - (as->text), strlen(p), list, &lp);
94    *lp = '\0';
95    r[i] = mush_strdup(list, "list2arr_item");
96  }
97  free_ansi_string(as);
98  return i;
99}
100
101/** Convert list to array.
102 * Chops up a list of words into an array of words. The list is
103 * destructively modified.
104 * \param r pointer to array to store words.
105 * \param max maximum number of words to split out.
106 * \param list list of words as a string.
107 * \param sep separator character between list items.
108 * \return number of words split out.
109 */
110int
111list2arr(char *r[], int max, char *list, char sep)
112{
113  char *p, *lp;
114  int i;
115  int first;
116  char *aptr;
117
118  memcpy(list, remove_markup(list, NULL), BUFFER_LEN);
119
120  aptr = trim_space_sep(list, sep);
121
122  lp = list;
123  p = split_token(&aptr, sep);
124  first = 0;
125  for (i = 0; p && (i < max); i++, p = split_token(&aptr, sep)) {
126    r[i] = p;
127  }
128  return i;
129}
130
131/** Convert array to list.
132 * Takes an array of words and concatenates them into a string,
133 * using our safe string functions.
134 * \param r pointer to array of words.
135 * \param max maximum number of words to concatenate.
136 * \param list string to fill with word list.
137 * \param lp pointer into end of list.
138 * \param sep string to use as separator between words.
139 */
140void
141arr2list(char *r[], int max, char *list, char **lp, const char *sep)
142{
143  int i;
144  int seplen = 0;
145
146  if (!max)
147    return;
148
149  if (sep && *sep)
150    seplen = strlen(sep);
151
152  safe_str(r[0], list, lp);
153  for (i = 1; i < max; i++) {
154    safe_strl(sep, seplen, list, lp);
155    safe_str(r[i], list, lp);
156  }
157  **lp = '\0';
158}
159
160/** Free an array generated by list2arr.
161 * Takes an array of words and frees it.
162 * \param r pointer to array of words.
163 * \param size The number of items in the list.
164 */
165void
166freearr(char *r[], int size)
167{
168  int i;
169  for (i = 0; i < size; i++) {
170    mush_free(r[i], "list2arr_item");
171  }
172}
173
174/* ARGSUSED */
175FUNCTION(fun_munge)
176{
177  /* This is a function which takes three arguments. The first is
178   * an obj-attr pair referencing a u-function to be called. The
179   * other two arguments are lists. The first list is passed to the
180   * u-function.  The second list is then rearranged to match the
181   * order of the first list as returned from the u-function.
182   * This rearranged list is returned by MUNGE.
183   * A fourth argument (separator) is optional.
184   */
185
186  char list1[BUFFER_LEN], *lp, rlist[BUFFER_LEN], *rp;
187  char **ptrs1, **ptrs2, **results;
188  char **ptrs3;
189  int i, j, nptrs1, nptrs2, nresults;
190  dbref thing;
191  ATTR *attrib;
192  char sep, isep[2] = { '\0', '\0' }, *osep, osepd[2] = {
193  '\0', '\0'};
194  int first;
195  char *uargs[2];
196
197  if (!delim_check(buff, bp, nargs, args, 4, &sep))
198    return;
199
200  isep[0] = sep;
201  if (nargs == 5)
202    osep = args[4];
203  else {
204    osepd[0] = sep;
205    osep = osepd;
206  }
207
208  /* find our object and attribute */
209  parse_anon_attrib(executor, args[0], &thing, &attrib);
210  if (!GoodObject(thing) || !attrib || !Can_Read_Attr(executor, thing, attrib)) {
211    free_anon_attrib(attrib);
212    return;
213  }
214  if (!CanEvalAttr(executor, thing, attrib)) {
215    free_anon_attrib(attrib);
216    return;
217  }
218
219  /* Copy the first list, since we need to pass it to two destructive
220   * routines.
221   */
222
223  strcpy(list1, args[1]);
224
225  /* Break up the two lists into their respective elements. */
226
227  ptrs1 = mush_calloc(MAX_SORTSIZE, sizeof(char *), "ptrarray");
228  ptrs2 = mush_calloc(MAX_SORTSIZE, sizeof(char *), "ptrarray");
229
230  /* ptrs3 is destructively modified, but it's a copy of ptrs2, so we
231   * make it a straight copy of ptrs2 and freearr() on ptrs2. */
232  ptrs3 = mush_calloc(MAX_SORTSIZE, sizeof(char *), "ptrarray");
233
234  if (!ptrs1 || !ptrs2)
235    mush_panic("Unable to allocate memory in fun_munge");
236  nptrs1 = list2arr_ansi(ptrs1, MAX_SORTSIZE, args[1], sep);
237  nptrs2 = list2arr_ansi(ptrs2, MAX_SORTSIZE, args[2], sep);
238  memcpy(ptrs3, ptrs2, MAX_SORTSIZE * sizeof(char *));
239
240  if (nptrs1 != nptrs2) {
241    safe_str(T("#-1 LISTS MUST BE OF EQUAL SIZE"), buff, bp);
242    freearr(ptrs1, nptrs1);
243    freearr(ptrs2, nptrs2);
244    mush_free((Malloc_t) ptrs1, "ptrarray");
245    mush_free((Malloc_t) ptrs2, "ptrarray");
246    mush_free((Malloc_t) ptrs3, "ptrarray");
247    free_anon_attrib(attrib);
248    return;
249  }
250  /* Call the user function */
251
252  lp = list1;
253  rp = rlist;
254  uargs[0] = lp;
255  uargs[1] = isep;
256  do_userfn(rlist, &rp, thing, attrib, 2, uargs,
257            executor, caller, enactor, pe_info, 0);
258  *rp = '\0';
259
260  /* Now that we have our result, put it back into array form. Search
261   * through list1 until we find the element position, then copy the
262   * corresponding element from list2.  Mark used elements with
263   * NULL to handle duplicates
264   */
265  results = mush_calloc(MAX_SORTSIZE, sizeof(char *), "ptrarray");
266  if (!results)
267    mush_panic("Unable to allocate memory in fun_munge");
268  nresults = list2arr_ansi(results, MAX_SORTSIZE, rlist, sep);
269
270  first = 1;
271  for (i = 0; i < nresults; i++) {
272    for (j = 0; j < nptrs1; j++) {
273      if (ptrs3[j] && !strcmp(results[i], ptrs1[j])) {
274        if (first)
275          first = 0;
276        else
277          safe_str(osep, buff, bp);
278        safe_str(ptrs3[j], buff, bp);
279        ptrs3[j] = NULL;
280        break;
281      }
282    }
283  }
284  freearr(ptrs1, nptrs1);
285  freearr(ptrs2, nptrs2);
286  freearr(results, nresults);
287  mush_free((Malloc_t) ptrs1, "ptrarray");
288  mush_free((Malloc_t) ptrs2, "ptrarray");
289  mush_free((Malloc_t) ptrs3, "ptrarray");
290  mush_free((Malloc_t) results, "ptrarray");
291  free_anon_attrib(attrib);
292}
293
294/* ARGSUSED */
295FUNCTION(fun_elements)
296{
297  /* Given a list and a list of numbers, return the corresponding
298   * elements of the list. elements(ack bar eep foof yay,2 4) = bar foof
299   * A separator for the first list is allowed.
300   * This code modified slightly from the Tiny 2.2.1 distribution
301   */
302  int nwords, cur;
303  char **ptrs;
304  char *wordlist;
305  char *s, *r, sep;
306  char *osep, osepd[2] = { '\0', '\0' };
307
308  if (!delim_check(buff, bp, nargs, args, 3, &sep))
309    return;
310
311  if (nargs == 4)
312    osep = args[3];
313  else {
314    osepd[0] = sep;
315    osep = osepd;
316  }
317
318  ptrs = mush_calloc(MAX_SORTSIZE, sizeof(char *), "ptrarray");
319  wordlist = mush_malloc(BUFFER_LEN, "string");
320  if (!ptrs || !wordlist)
321    mush_panic("Unable to allocate memory in fun_elements");
322
323  /* Turn the first list into an array. */
324  strcpy(wordlist, args[0]);
325  nwords = list2arr_ansi(ptrs, MAX_SORTSIZE, wordlist, sep);
326
327  s = trim_space_sep(args[1], ' ');
328
329  /* Go through the second list, grabbing the numbers and finding the
330   * corresponding elements.
331   */
332  r = split_token(&s, ' ');
333  cur = atoi(r) - 1;
334  if ((cur >= 0) && (cur < nwords) && ptrs[cur]) {
335    safe_str(ptrs[cur], buff, bp);
336  }
337  while (s) {
338    r = split_token(&s, ' ');
339    cur = atoi(r) - 1;
340    if ((cur >= 0) && (cur < nwords) && ptrs[cur]) {
341      safe_str(osep, buff, bp);
342      safe_str(ptrs[cur], buff, bp);
343    }
344  }
345  freearr(ptrs, nwords);
346  mush_free((Malloc_t) ptrs, "ptrarray");
347  mush_free((Malloc_t) wordlist, "string");
348}
349
350/* ARGSUSED */
351FUNCTION(fun_matchall)
352{
353  /* Check each word individually, returning the word number of all
354   * that match. If none match, return an empty string.
355   */
356
357  int wcount;
358  char *r, *s, *b, sep;
359  char *osep, osepd[2] = { '\0', '\0' };
360
361  if (!delim_check(buff, bp, nargs, args, 3, &sep))
362    return;
363
364  if (nargs == 4)
365    osep = args[3];
366  else {
367    osepd[0] = sep;
368    osep = osepd;
369  }
370
371  wcount = 1;
372  s = trim_space_sep(args[0], sep);
373  b = *bp;
374  do {
375    r = split_token(&s, sep);
376    if (quick_wild(args[1], r)) {
377      if (*bp != b)
378        safe_str(osep, buff, bp);
379      safe_integer(wcount, buff, bp);
380    }
381    wcount++;
382  } while (s);
383}
384
385/* ARGSUSED */
386FUNCTION(fun_graball)
387{
388  /* Check each word individually, returning all that match.
389   * If none match, return an empty string.  This is to grab()
390   * what matchall() is to match().
391   */
392
393  char *r, *s, *b, sep;
394  char *osep, osepd[2] = { '\0', '\0' };
395
396  if (!delim_check(buff, bp, nargs, args, 3, &sep))
397    return;
398
399  if (nargs == 4)
400    osep = args[3];
401  else {
402    osepd[0] = sep;
403    osep = osepd;
404  }
405
406  s = trim_space_sep(args[0], sep);
407  b = *bp;
408  do {
409    r = split_token(&s, sep);
410    if (quick_wild(args[1], r)) {
411      if (*bp != b)
412        safe_str(osep, buff, bp);
413      safe_str(r, buff, bp);
414    }
415  } while (s);
416}
417
418
419
420/* ARGSUSED */
421FUNCTION(fun_fold)
422{
423  /* iteratively evaluates an attribute with a list of arguments and
424   * optional base case. With no base case, the first list element is
425   * passed as %0, and the second as %1. The attribute is then evaluated
426   * with these args. The result is then used as %0, and the next arg as
427   * %1. Repeat until no elements are left in the list. The base case
428   * can provide a starting point.
429   */
430
431  ufun_attrib ufun;
432  char *cp;
433  char *wenv[2];
434  char sep;
435  int funccount, per;
436  char base[BUFFER_LEN];
437  char result[BUFFER_LEN];
438
439  if (!delim_check(buff, bp, nargs, args, 4, &sep))
440    return;
441
442  if (!fetch_ufun_attrib(args[0], executor, &ufun, 1))
443    return;
444
445  cp = args[1];
446
447  /* If we have three or more arguments, the third one is the base case */
448  if (nargs >= 3) {
449    strncpy(base, args[2], BUFFER_LEN);
450  } else {
451    strncpy(base, split_token(&cp, sep), BUFFER_LEN);
452  }
453  wenv[0] = base;
454  wenv[1] = split_token(&cp, sep);
455
456  call_ufun(&ufun, wenv, 2, result, executor, enactor, pe_info);
457
458  strncpy(base, result, BUFFER_LEN);
459
460  funccount = pe_info->fun_invocations;
461
462  /* handle the rest of the cases */
463  while (cp && *cp) {
464    wenv[1] = split_token(&cp, sep);
465    per = call_ufun(&ufun, wenv, 2, result, executor, enactor, pe_info);
466    if (per || (pe_info->fun_invocations >= FUNCTION_LIMIT &&
467                pe_info->fun_invocations == funccount && !strcmp(base, result)))
468      break;
469    funccount = pe_info->fun_invocations;
470    strcpy(base, result);
471  }
472  safe_str(base, buff, bp);
473}
474
475/* ARGSUSED */
476FUNCTION(fun_itemize)
477{
478  /* Called in one of two ways:
479   * itemize(<list>[,<delim>[,<conjunction>[,<punctuation>]]])
480   * elist(<list>[,<conjunction> [,<delim> [,<output delim> [,<punctuation>]]]])
481   * Either way, it takes the elements of list and:
482   *  If there's just one, returns it.
483   *  If there's two, returns <e1> <conjunction> <e2>
484   *  If there's >2, returns <e1><punc> <e2><punc> ... <conjunction> <en>
485   * Default <conjunction> is "and", default punctuation is ","
486   */
487  const char *outsep = " ";
488  char sep = ' ';
489  const char *lconj = "and";
490  const char *punc = ",";
491  char *cp;
492  char *word, *nextword;
493  int pos;
494
495  if (strcmp(called_as, "ELIST") == 0) {
496    /* elist ordering */
497    if (!delim_check(buff, bp, nargs, args, 3, &sep))
498      return;
499    if (nargs > 1)
500      lconj = args[1];
501    if (nargs > 3)
502      outsep = args[3];
503    if (nargs > 4)
504      punc = args[4];
505  } else {
506    /* itemize ordering */
507    if (!delim_check(buff, bp, nargs, args, 2, &sep))
508      return;
509    if (nargs > 2)
510      lconj = args[2];
511    if (nargs > 3)
512      punc = args[3];
513  }
514  cp = trim_space_sep(args[0], sep);
515  pos = 1;
516  word = split_token(&cp, sep);
517  while (word) {
518    nextword = split_token(&cp, sep);
519    safe_itemizer(pos, !(nextword), punc, lconj, outsep, buff, bp);
520    safe_str(word, buff, bp);
521    pos++;
522    word = nextword;
523  }
524}
525
526
527/* ARGSUSED */
528FUNCTION(fun_filter)
529{
530  /* take a user-def function and a list, and return only those elements
531   * of the list for which the function evaluates to 1.
532   */
533
534  ufun_attrib ufun;
535  char result[BUFFER_LEN];
536  char *cp;
537  char *wenv[1];
538  char sep;
539  int first;
540  int check_bool = 0;
541  int funccount;
542  char *osep, osepd[2] = { '\0', '\0' };
543
544  if (!delim_check(buff, bp, nargs, args, 3, &sep))
545    return;
546
547  osepd[0] = sep;
548  osep = (nargs >= 4) ? args[3] : osepd;
549
550  if (strcmp(called_as, "FILTERBOOL") == 0)
551    check_bool = 1;
552
553  /* find our object and attribute */
554  if (!fetch_ufun_attrib(args[0], executor, &ufun, 1))
555    return;
556
557  /* Go through each argument */
558  cp = trim_space_sep(args[1], sep);
559  first = 1;
560  funccount = pe_info->fun_invocations;
561  while (cp && *cp) {
562    wenv[0] = split_token(&cp, sep);
563    if (call_ufun(&ufun, wenv, 1, result, executor, enactor, pe_info))
564      break;
565    if ((check_bool == 0)
566        ? (*result == '1' && *(result + 1) == '\0')
567        : parse_boolean(result)) {
568      if (first)
569        first = 0;
570      else
571        safe_str(osep, buff, bp);
572      safe_str(wenv[0], buff, bp);
573    }
574    /* Can't do *bp == oldbp like in all the others, because bp might not
575     * move even when not full, if one of the list elements is null and
576     * we have a null separator. */
577    if (*bp == (buff + BUFFER_LEN - 1) && pe_info->fun_invocations == funccount)
578      break;
579    funccount = pe_info->fun_invocations;
580  }
581}
582
583/* ARGSUSED */
584FUNCTION(fun_shuffle)
585{
586  /* given a list of words, randomize the order of words.
587   * We do this by taking each element, and swapping it with another
588   * element with a greater array index (thus, words[0] can be swapped
589   * with anything up to words[n], words[5] with anything between
590   * itself and words[n], etc.
591   * This is relatively fast - linear time - and reasonably random.
592   * Will take an optional delimiter argument.
593   */
594
595  char *words[MAX_SORTSIZE];
596  int n, i, j;
597  char sep;
598  char *osep, osepd[2] = { '\0', '\0' };
599
600  if (!delim_check(buff, bp, nargs, args, 2, &sep))
601    return;
602
603  if (nargs == 3)
604    osep = args[2];
605  else {
606    osepd[0] = sep;
607    osep = osepd;
608  }
609
610  /* split the list up, or return if the list is empty */
611  if (!*args[0])
612    return;
613  n = list2arr_ansi(words, MAX_SORTSIZE, args[0], sep);
614
615  /* shuffle it */
616  for (i = 0; i < n; i++) {
617    char *tmp;
618    j = get_random_long(i, n - 1);
619    tmp = words[j];
620    words[j] = words[i];
621    words[i] = tmp;
622  }
623
624  arr2list(words, n, buff, bp, osep);
625  freearr(words, n);
626}
627
628
629/* ARGSUSED */
630FUNCTION(fun_sort)
631{
632  char *ptrs[MAX_SORTSIZE];
633  int nptrs;
634  char *sort_type;
635  char sep;
636  char outsep[BUFFER_LEN];
637
638  if (!nargs || !*args[0])
639    return;
640
641  if (!delim_check(buff, bp, nargs, args, 3, &sep))
642    return;
643
644  if (nargs < 4) {
645    outsep[0] = sep;
646    outsep[1] = '\0';
647  } else
648    strcpy(outsep, args[3]);
649
650  nptrs = list2arr_ansi(ptrs, MAX_SORTSIZE, args[0], sep);
651  sort_type = get_list_type(args, nargs, 2, ptrs, nptrs);
652  do_gensort(executor, ptrs, NULL, nptrs, sort_type);
653  arr2list(ptrs, nptrs, buff, bp, outsep);
654  freearr(ptrs, nptrs);
655}
656
657/* ARGSUSED */
658FUNCTION(fun_sortkey)
659{
660  char *ptrs[MAX_SORTSIZE];
661  char *keys[MAX_SORTSIZE];
662  int nptrs;
663  char *sort_type;
664  char sep;
665  char outsep[BUFFER_LEN];
666  int i;
667  char result[BUFFER_LEN];
668  ufun_attrib ufun;
669  char *wenv[1];
670
671  /* sortkey(attr,list,sort_type,delim,osep) */
672
673  if (!nargs || !*args[0] || !*args[1])
674    return;
675
676  if (!delim_check(buff, bp, nargs, args, 4, &sep))
677    return;
678
679  if (nargs < 5) {
680    outsep[0] = sep;
681    outsep[1] = '\0';
682  } else
683    strcpy(outsep, args[4]);
684
685  /* find our object and attribute */
686  if (!fetch_ufun_attrib(args[0], executor, &ufun, 1))
687    return;
688
689  nptrs = list2arr_ansi(ptrs, MAX_SORTSIZE, args[1], sep);
690
691  /* Now we make a list of keys */
692  for (i = 0; i < nptrs; i++) {
693    /* Build our %0 args */
694    wenv[0] = (char *) ptrs[i];
695    call_ufun(&ufun, wenv, 1, result, executor, enactor, pe_info);
696    keys[i] = mush_strdup(result, "sortkey");
697  }
698
699  sort_type = get_list_type(args, nargs, 3, keys, nptrs);
700  do_gensort(executor, keys, ptrs, nptrs, sort_type);
701  arr2list(ptrs, nptrs, buff, bp, outsep);
702  freearr(ptrs, nptrs);
703  for (i = 0; i < nptrs; i++) {
704    mush_free(keys[i], "sortkey");
705  }
706}
707
708
709/* From sort.c */
710extern dbref ucomp_executor, ucomp_caller, ucomp_enactor;
711extern char ucomp_buff[BUFFER_LEN];
712extern PE_Info *ucomp_pe_info;
713
714
715/* ARGSUSED */
716FUNCTION(fun_sortby)
717{
718  char *ptrs[MAX_SORTSIZE], *tptr[10];
719  char *up, sep;
720  int nptrs;
721  dbref thing;
722  ATTR *attrib;
723  char *osep, osepd[2] = { '\0', '\0' };
724
725  if (!nargs || !*args[0])
726    return;
727
728  if (!delim_check(buff, bp, nargs, args, 3, &sep))
729    return;
730
731  if (nargs == 4)
732    osep = args[3];
733  else {
734    osepd[0] = sep;
735    osep = osepd;
736  }
737
738  /* Find object and attribute to get sortby function from. */
739  parse_anon_attrib(executor, args[0], &thing, &attrib);
740  if (!GoodObject(thing) || !attrib || !Can_Read_Attr(executor, thing, attrib)) {
741    free_anon_attrib(attrib);
742    return;
743  }
744  if (!CanEvalAttr(executor, thing, attrib)) {
745    free_anon_attrib(attrib);
746    return;
747  }
748  up = ucomp_buff;
749  safe_str(atr_value(attrib), ucomp_buff, &up);
750  *up = '\0';
751
752  ucomp_executor = thing;
753  ucomp_caller = executor;
754  ucomp_enactor = enactor;
755  ucomp_pe_info = pe_info;
756
757  save_global_env("sortby", tptr);
758
759  /* Split up the list, sort it, reconstruct it. */
760  nptrs = list2arr_ansi(ptrs, MAX_SORTSIZE, args[1], sep);
761  if (nptrs > 1)                /* pointless to sort less than 2 elements */
762    sane_qsort((void *) ptrs, 0, nptrs - 1, u_comp);
763
764  arr2list(ptrs, nptrs, buff, bp, osep);
765  freearr(ptrs, nptrs);
766
767  restore_global_env("sortby", tptr);
768  free_anon_attrib(attrib);
769}
770
771/* ARGSUSED */
772FUNCTION(fun_setinter)
773{
774  char sep;
775  char **a1, **a2;
776  int n1, n2, x1, x2, val;
777  char *sort_type = ALPHANUM_LIST;
778  int osepl = 0;
779  char *osep = NULL, osepd[2] = { '\0', '\0' };
780
781  /* if no lists, then no work */
782  if (!*args[0]