PennMUSH Community

root/1.8.3/trunk/src/funlist.c

Revision 1167, 77.8 kB (checked in by shawnw, 8 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
32 static char *next_token(char *str, char sep);
33 static int regrep_helper(dbref who, dbref what, dbref parent,
34                          char const *name, ATTR *atr, void *args);
35 enum itemfun_op { IF_DELETE, IF_REPLACE, IF_INSERT };
36 static void do_itemfuns(char *buff, char **bp, char *str, char *num,
37                         char *word, char *sep, enum itemfun_op flag);
38
39 char *iter_rep[MAX_ITERS];  /**< itext values */
40 int iter_place[MAX_ITERS];  /**< inum numbers */
41 int inum = 0;               /**< iter depth */
42 int inum_limit = 0;         /**< limit of iter depth */
43 extern const unsigned char *tables;
44
45 #define RealGoodObject(x) (GoodObject(x) && !IsGarbage(x))
46
47 static char *
48 next_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  */
74 int
75 list2arr_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  */
110 int
111 list2arr(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  */
140 void
141 arr2list(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  */
165 void
166 freearr(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 */
175 FUNCTION(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 */
295 FUNCTION(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 */
351 FUNCTION(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 */
386 FUNCTION(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 */
421 FUNCTION(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 */
476 FUNCTION(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 */
528 FUNCTION(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 fir