/*------------------------------------------------------------------ * String functions. * *------------------------------------------------------------------ * A collection of string related functions and utilities: * * xstrncpy(): a safer strncpy(). * trim_all_spaces(): used in efun parse_command(). * * strbuf_t: an extendable string buffer, useful for incremental * construction of a string. * TODO: I am afraid the handling of length in _grow() itself and * TODO:: its calls is a bit too far on the conservative side. * TODO: Rewrite the strbuf_t to use a scatter-gather storing * TODO:: of data, to avoid allocations of large buffers (this happens * TODO:: when doing save/restore on large objects). * * --- Efuns and Operators --- * * f_convert_charset(): Convert charsets using iconv(). * intersect_strings(): Implements '&' and '-' on strings * x_filter_string(): Filter a string through a callback or mapping. * x_map_string(): Map a string through a callback or mapping. * *------------------------------------------------------------------ */ /*--------------------------------------------------------------------*/ #include "driver.h" #include "typedefs.h" #include "my-alloca.h" #include #include #include #include #include #include #ifdef HAS_ICONV #include #endif #include "strfuns.h" #include "comm.h" #include "interpret.h" #include "main.h" #include "mapping.h" #include "mstrings.h" #include "object.h" #include "simulate.h" #include "stdstrings.h" #include "svalue.h" #include "xalloc.h" /*--------------------------------------------------------------------*/ void strbuf_zero (strbuf_t *buf) /* Initialise the given string buffer . */ { buf->alloc_len = 0; buf->length = 0; buf->buf = NULL; } /*--------------------------------------------------------------------*/ void strbuf_free (strbuf_t *buf) /* Free the given string buffer . * TODO: Not necessary once all strings are counted and with length? */ { if (buf->buf) xfree(buf->buf); strbuf_zero(buf); } /*--------------------------------------------------------------------*/ static INLINE size_t strbuf_grow (strbuf_t *buf, size_t len) /* Extend the stringbuffer to hold at least more * bytes (ie. enough for a string of length -1). * * Return if all memory could be allocated, or a lower number * of only part of the required memory is available. * * N.B.: be careful with overflows when doing the checks. */ { size_t new_len; /* Catch some simple situations. */ if (buf->alloc_len >= MAX_STRBUF_LEN) return 0; /* Truncated */ if (buf->alloc_len - buf->length > len) return len; /* Allocate more than we need in anticipation of further adds, * but not more than we can manage */ if (MAX_STRBUF_LEN - buf->length < len * 3) { new_len = MAX_STRBUF_LEN; if (new_len - buf->length < len) len = new_len - buf->length; } else new_len = buf->length + len * 3; /* Is this the first allocation? */ if (!buf->buf) { memsafe(buf->buf = xalloc(new_len), new_len, "new strbuf"); buf->alloc_len = (u_long)new_len; buf->length = 0; *(buf->buf) = '\0'; return len; } /* Extension of the existing buffer */ memsafe(buf->buf = rexalloc(buf->buf, new_len), new_len, "larger strbuf"); buf->alloc_len = (u_long)new_len; return len; } /* strbuf_grow() */ /*--------------------------------------------------------------------*/ void strbuf_add (strbuf_t *buf, const char * text) /* Add the to string buffer . */ { size_t len; len = strlen(text) + 1; if (!len) return; if (len + buf->length > buf->alloc_len) len = strbuf_grow(buf, len); if (len) { memcpy(buf->buf+buf->length, text, len); buf->length += len-1; buf->buf[buf->length] = '\0'; } } /* strbuf_add() */ /*--------------------------------------------------------------------*/ void strbuf_addn (strbuf_t *buf, const char * text, size_t len) /* Add the characters starting at to string buffer . */ { if (!len) return; len += 1; if (len + buf->length > buf->alloc_len) len = strbuf_grow(buf, len); if (len) { len--; memcpy(buf->buf+buf->length, text, len); buf->length += len; buf->buf[buf->length] = '\0'; } } /* strbuf_addn() */ /*--------------------------------------------------------------------*/ void strbuf_addc (strbuf_t *buf, char ch) /* Add the aracter to string buffer . */ { size_t len; len = 2; if (2 + buf->length > buf->alloc_len) len = strbuf_grow(buf, 2); if (len) { buf->buf[buf->length] = ch; buf->length++; buf->buf[buf->length] = '\0'; } } /* strbuf_addc() */ /*--------------------------------------------------------------------*/ void strbuf_addf (strbuf_t *buf, const char * format, ...) /* Create a string from and the following arguments using * sprintf() rules, and add the result to the string buffer . */ { char tmpbuf[4096]; va_list vargs; tmpbuf[sizeof tmpbuf - 1] = '\0'; va_start(vargs, format); vsprintf(tmpbuf, format, vargs); va_end(vargs); if (tmpbuf[sizeof tmpbuf - 1]) fatal("strbuf_addf: Internal buffer overflow.\n"); strbuf_add(buf, tmpbuf); } /* strbuf_addf() */ /*--------------------------------------------------------------------*/ void strbuf_send (strbuf_t *buf) /* Send the string collected in out to the current user with * add_message(), and clear . */ { if (buf->buf && buf->length) { add_message("%s", buf->buf); } /* Empty the string buffer */ if (buf->buf) xfree(buf->buf); buf->buf = NULL; buf->length = 0; buf->alloc_len = 0; } /* strbuf_send() */ /*--------------------------------------------------------------------*/ void strbuf_store (strbuf_t *buf, svalue_t *svp) /* Store the string collected in , which may be the null string "", * into the empty svalue *, then clear . */ { svp->type = T_STRING; if (buf->buf && buf->length) { svp->u.str = new_n_mstring(buf->buf, buf->length); } else { svp->u.str = ref_mstring(STR_EMPTY); } /* Empty the string buffer */ if (buf->buf) xfree(buf->buf); buf->buf = NULL; buf->length = 0; buf->alloc_len = 0; } /* strbuf_store() */ /*--------------------------------------------------------------------*/ void strbuf_copy (strbuf_t *buf, char *cbuf) /* Copy the string collected in , which may be the null string "", * into the buffer which must have been allocated by the caller * to a suitable size. The copied string will be terminated with a '\0'. */ { if (buf->buf && buf->length) memcpy(cbuf, buf->buf, buf->length); cbuf[buf->length] = '\0'; } /* strbuf_copy() */ /*--------------------------------------------------------------------*/ char * xstrncpy (char * dest, const char * src, size_t num) /* Copy string at address up to and including the terminating * 0 or up to size , whichever comes first. Result is . * * In contrast to strncpy(), the copying terminates if a terminating 0 * is found (and copied) in - strncpy() would add additional 0s * until a total of characters has been written to . */ { char * p = dest; while (num-- != 0 && (*p++ = *src++) != '\0') NOOP; return dest; } /* xstrncpy() */ /*-------------------------------------------------------------------------*/ string_t * trim_all_spaces (const string_t * txt) /* Trim the input string by removing all leading and trailing * space, and by folding embedded space runs into just one each. * Return the new string with one ref; the refcount of is not changed. * * Throw an error when out of memory. */ { char * dest; const char * src; size_t dest_ix, src_ix, srclen; string_t * rc; dest = alloca(mstrsize(txt)); if (dest == NULL) errorf("Stack overflow (%zu bytes)\n", mstrsize(txt)); src = get_txt((string_t *const)txt); srclen = mstrsize(txt); src_ix = 0; dest_ix = 0; /* Blank out trailing spaces */ while (srclen > 0 && src[srclen-1] == ' ') srclen--; /* Skip leading spaces */ while (src_ix < srclen && *src == ' ') src_ix++, src++; /* Copy characters, but fold embedded spaces. */ for ( ; src_ix < srclen; src_ix++, src++, dest_ix++) { dest[dest_ix] = *src; /* If this and the next character is a space, forward * src until the last space in this run. */ if (' ' == *src) { while (src_ix+1 < srclen && ' ' == src[1]) src_ix++, src++; } } memsafe(rc = new_n_mstring(dest, dest_ix), dest_ix, "trimmed result"); return rc; } /* trim_all_spaces() */ /*====================================================================*/ /* EFUNS */ /*--------------------------------------------------------------------*/ #ifdef HAS_ICONV svalue_t * f_convert_charset (svalue_t *sp) /* EFUN convert_charset() * * string convert_charset(string str, string from_cs, string to_cs) * * Convert the string from charset to charset * and return the converted string. * * The efun is only available on systems with libiconv. */ { iconv_t context; string_t *from_cs, *to_cs, *in_str, *out_str; #if HAS_ICONV_NONCONST_IN # define ICONV_IN_CAST (char**) #else # define ICONV_IN_CAST #endif const char *pIn; /* Input string pointer */ size_t in_len; /* Input length */ size_t in_left; /* Input length left */ char * out_buf; /* Output buffer */ size_t out_size; /* Size of the output buffer */ size_t out_left; /* Size left in output buffer */ char *pOut; /* Output string pointer */ in_str = sp[-2].u.str; from_cs = sp[-1].u.str; to_cs = sp->u.str; pIn = get_txt(in_str); in_len = mstrsize(in_str); in_left = in_len; /* If the input string is empty, we can return immediately * (and in fact must since the allocator will balk at allocating 0 bytes) */ if (!in_len) { sp -= 2; free_string_svalue(sp); free_string_svalue(sp+1); put_string(sp, sp[2].u.str); return sp; } /* Allocate a temporary output string */ out_size = in_len > 65536 ? (in_len + 33) : (2 * in_len); out_left = out_size; xallocate(out_buf, out_size, "iconv buffer"); pOut = out_buf; /* Open the iconv context */ context = iconv_open(get_txt(to_cs), get_txt(from_cs)); if (context == (iconv_t) -1) { xfree(out_buf); if (errno == EINVAL) errorf("convert_charset(): Conversion '%s' -> '%s' not supported.\n" , get_txt(from_cs), get_txt(to_cs) ); else errorf("convert_charset(): Error %d.\n", errno); /* NOTREACHED */ return sp; } /* Convert the string, reallocating the output buffer where necessary */ while (in_left) { size_t rc; rc = iconv(context, ICONV_IN_CAST &pIn, &in_left, &pOut, &out_left); if (rc == (size_t)-1) { if (errno == E2BIG) { /* Reallocate output buffer */ size_t newsize; char * tmp; newsize = out_size + (in_len > 128 ? in_len : 128); tmp = rexalloc(out_buf, newsize); if (!tmp) { iconv_close(context); xfree(out_buf); outofmem(newsize, "iconv buffer"); /* NOTREACHED */ return sp; } out_buf = tmp; pOut = out_buf + out_size; out_left = newsize - out_size; out_size = newsize; continue; } /* Other error: clean up */ iconv_close(context); xfree(out_buf); if (errno == EILSEQ) { errorf("convert_charset(): Invalid character sequence at " "index %td\n", (ptrdiff_t)(pIn - get_txt(in_str))); /* NOTREACHED */ return sp; } if (errno == EINVAL) { errorf("convert_charset(): Incomplete character sequence at " "index %td\n", (ptrdiff_t)(pIn - get_txt(in_str))); /* NOTREACHED */ return sp; } errorf("convert_charset(): Error %d at index %td\n" , errno, (ptrdiff_t)(pIn - get_txt(in_str)) ); /* NOTREACHED */ return sp; } /* if (rc < 0) */ } /* while (in_left) */ /* While the actual conversion is complete, the output stream may now * be in a non-base state. Add the necessary epilogue to get back * to the base state. */ while(1) { size_t rc; rc = iconv(context, NULL, NULL, &pOut, &out_left); if (rc == (size_t)-1) { if (errno == E2BIG) { /* Reallocate output buffer */ size_t newsize; char * tmp; newsize = out_size + (in_len > 128 ? in_len : 128); tmp = rexalloc(out_buf, newsize); if (!tmp) { iconv_close(context); xfree(out_buf); outofmem(newsize, "iconv buffer"); /* NOTREACHED */ return sp; } out_buf = tmp; pOut = out_buf + out_size; out_left = newsize - out_size; out_size = newsize; continue; } /* Other error: clean up */ iconv_close(context); xfree(out_buf); if (errno == EILSEQ) { errorf("convert_charset(): Invalid character sequence at " "index %td\n", (ptrdiff_t)(pIn - get_txt(in_str))); /* NOTREACHED */ return sp; } if (errno == EINVAL) { errorf("convert_charset(): Incomplete character sequence at " "index %td\n", (ptrdiff_t)(pIn - get_txt(in_str))); /* NOTREACHED */ return sp; } errorf("convert_charset(): Error %d at index %td\n" , errno, (ptrdiff_t)(pIn - get_txt(in_str)) ); /* NOTREACHED */ return sp; } /* if (rc < 0) */ /* At this point, the iconv() succeeded: we're done */ break; } /* while(1) */ iconv_close(context); /* Get the return string and prepare the return arguments */ out_str = new_n_mstring(out_buf, out_size - out_left); xfree(out_buf); if (!out_str) { outofmem(out_size - out_left, "convert_charset() result"); /* NOTREACHED */ return sp; } free_string_svalue(sp--); free_string_svalue(sp--); free_string_svalue(sp); put_string(sp, out_str); return sp; } /* f_convert_charset() */ #endif /* HAS_ICONV */ /*--------------------------------------------------------------------*/ static char * sort_string (const string_t * p_in, size_t len, long ** pos) /* Sort the characters of string (with length ) by their numeric * values and return a newly allocated memory block with the sorted string. * If is not NULL, it will be set to a newly allocated memory block * giving the original positions of the characters in the sorted string. * * We use Mergesort to sort the strings. * TODO: Use Quicksort instead of Mergesort? */ { const char * in; /* Input string */ char * out; /* Result string */ long * outpos; /* Result position array */ char * tmp; /* Temporary string */ long * tmppos; /* Temporary position array */ size_t step; size_t i, j; in = get_txt((string_t *const)p_in); out = xalloc(len+1); tmp = xalloc(len+1); if (!out || !tmp) { if (out) xfree(out); if (tmp) xfree(tmp); errorf("(sort_string) Out of memory (2 * %zu bytes) for temporaries.\n" , len+1); } out[len] = '\0'; tmp[len] = '\0'; if (pos) { outpos = xalloc(len * sizeof(*outpos) + 1); tmppos = xalloc(len * sizeof(*outpos) + 1); /* +1 so that smalloc won't complain when given an empty string */ if (!outpos || !tmppos) { if (out) xfree(out); if (tmp) xfree(tmp); if (outpos) xfree(outpos); if (tmppos) xfree(tmppos); errorf("(sort_string) Out of memory (2 * %zu bytes) for positions.\n" , len*sizeof(*outpos)+1); } } else { outpos = NULL; tmppos = NULL; } /* First Mergesort pass: comparison of adjacent characters * and initialisation of the out arrays. */ for (i = 0; i < len; i += 2) { if (i == len-1) { out[i] = in[i]; if (outpos) outpos[i] = i; } else if (in[i] <= in[i+1]) { out[i] = in[i]; out[i+1] = in[i+1]; if (outpos) { outpos[i] = i; outpos[i+1] = i+1; } } else /* (in[i] > in[i+1]) */ { out[i] = in[i+1]; out[i+1] = in[i]; if (outpos) { outpos[i] = i+1; outpos[i+1] = i; } } } /* for(initial pass) */ /* Mergesort loop: perform the mergesort passes with increasing steps. * Invariant: out is the (semi-sorted) data, tmp is the scratchspace. */ for (step = 2; step < len; step *= 2) { size_t start, dest, left; /* Exchange out and tmp */ { char *tmp2; long *tmp2pos; tmp2 = out; out = tmp; tmp = tmp2; if (outpos) { tmp2pos = outpos; outpos = tmppos; tmppos = tmp2pos; } } for (start = 0, dest = 0; start <= len; start += 2*step) { for ( i = start, j = start+step, left = 2 * step ; left && dest < len ; left--, dest++ ) { if (i >= start+step || i >= len) { if (j < len) { out[dest] = tmp[j]; if (outpos) outpos[dest] = tmppos[j]; j++; } } else if (j >= start+2*step || j >= len) { if (i < len) { out[dest] = tmp[i]; if (outpos) outpos[dest] = tmppos[i]; i++; } } else if (tmp[i] <= tmp[j]) { out[dest] = tmp[i]; if (outpos) outpos[dest] = tmppos[i]; i++; } else /* (tmp[i] > tmp[i+step]) */ { out[dest] = tmp[j]; if (outpos) outpos[dest] = tmppos[j]; j++; } } /* for (sort run) */ } /* for (start) */ } /* for(step) */ /* Free the temporary data */ if (tmppos) xfree(tmppos); xfree(tmp); /* Return the result */ if (pos) *pos = outpos; return out; } /* sort_string() */ /*--------------------------------------------------------------------*/ string_t * intersect_strings (const string_t * p_left, const string_t * p_right, Bool bSubtract) /* !bSubtract: Intersect string with string and return * a newly allocated string with all those characters which are in * both strings. * bSubtract: Subtract string from string and return * a newly allocated string with all those characters which are in * but not in . * The order of the characters returned is their order of appearance * in . */ { size_t len_left, len_right, len_out; size_t ix_left, ix_right; long * pos; CBool * matches; const char * left_txt; char * left, * right, * result_txt; string_t *result; len_left = mstrsize(p_left); len_right = mstrsize(p_right); xallocate(matches, len_left+1, "intersection matches"); /* +1 so that smalloc won't complain when given an empty left string */ for (ix_left = 0; ix_left < len_left; ix_left++) matches[ix_left] = bSubtract ? MY_TRUE : MY_FALSE; /* Sort the two strings */ left = sort_string(p_left, len_left, &pos); right = sort_string(p_right, len_right, NULL); /* Intersect the two strings by mutual comparison. * Each non-matched character in left gets is pos[] set to -1. */ len_out = bSubtract ? len_left : 0; for ( ix_left = 0, ix_right = 0 ; ix_left < len_left && ix_right < len_right ; ) { if (left[ix_left] < right[ix_right]) ix_left++; else if (left[ix_left] > right[ix_right]) ix_right++; else /* left[ix_left] == right[ix_right]) */ { if (!bSubtract) { matches[pos[ix_left]] = MY_TRUE; len_out++; } else { matches[pos[ix_left]] = MY_FALSE; len_out--; } ix_left++; } } /* Create the result: copy all flagged characters */ memsafe(result = alloc_mstring(len_out), len_out, "intersection result"); left_txt = get_txt((string_t *const)p_left); result_txt = get_txt(result); for (ix_left = 0, ix_right = 0; ix_left < len_left; ix_left++) if (matches[ix_left]) result_txt[ix_right++] = left_txt[ix_left]; /* Free intermediate results */ xfree(pos); xfree(matches); xfree(left); xfree(right); return result; } /* intersect_strings() */ /*-------------------------------------------------------------------------*/ svalue_t * x_filter_string (svalue_t *sp, int num_arg) /* EFUN: filter() for strings. * * string filter(string arr, string fun, string|object obj, mixed extra, ...) * string filter(string arr, closure cl, mixed extra, ...) * string filter(string arr, mapping map) * * Filter the elements of through a filter defined by the other * arguments, and return an array of those elements, for which the * filter yields non-zero. * * The filter can be a function call: * * ->(elem, ...) * * or a mapping query: * * [elem] * * can both be an object reference or a filename. If omitted, * this_object() is used (this also works if the third argument is * neither a string nor an object). */ { string_t *rc; /* Result string */ string_t *str; /* Argument string */ svalue_t *arg; /* First argument the vm stack */ mp_int slen; /* Argument string length */ char *src, *dest; /* String text work pointers */ char *flags; /* Flag array, one flag for each element of * (in reverse order). */ mp_int res; /* Number of surviving elements */ res = 0; /* Locate the args on the stack, extract the string to filter * and allocate the flags vector. */ arg = sp - num_arg + 1; str = arg->u.str; slen = (mp_int)mstrsize(str); /* Every element in flags is associated by index number with an * element in the vector to filter. The filter function is evaluated * for every string character, and the associated flag is set to 0 * or 1 according to the result. * At the end, all 1-flagged elements are gathered and copied * into the result string. */ if (arg[1].type == T_MAPPING) { mp_int cnt; /* --- Filter by mapping query --- */ mapping_t *m; if (num_arg > 2) { errorf("Too many arguments to filter(array)\n"); } /* Allocate memory for the flag array. Simultaneously an error * handler is pushed onto the stack (after the arguments) for freeing * the buffer in case of runtime errors. */ flags = xalloc_with_error_handler((size_t)slen + 1); if (!flags) { errorf("Out of memory (%zu bytes) for temporary buffer in filter().\n", (size_t)slen + 1); } sp = inter_sp; m = arg[1].u.map; for (src = get_txt(str), cnt = slen; --cnt >= 0; src++) { svalue_t key; put_number(&key, *src); if (get_map_value(m, &key) == &const0) { flags[cnt] = 0; continue; } flags[cnt] = 1; res++; } } else { /* --- Filter by function call --- */ int error_index; callback_t cb; mp_int cnt; assign_eval_cost(); /* setup_efun_callback() will adopt and therefore remove the * arguments from arg+1 on to arg+num_arg from the stack and update * inter_sp. New top-of-stack will be arg. */ error_index = setup_efun_callback(&cb, arg+1, num_arg-1); if (error_index >= 0) { vefun_bad_arg(error_index+2, arg); /* NOTREACHED */ return arg; } /* push the callback structure onto the stack. */ sp = arg + 1; put_callback(sp, &cb); /* Allocate memory for the flag array. Simultaneously an error * handler is pushed onto the stack (after the arguments) for freeing * the buffer in case of runtime errors. */ inter_sp = sp; flags = xalloc_with_error_handler((size_t)slen + 1); if (!flags) { errorf("Out of memory (%"PRIdMPINT" bytes) for temporary buffer " "in filter().\n", slen + 1); } sp = inter_sp; /* Loop over all elements in p and call the filter. * w is the current element filtered. */ for (src = get_txt(str), cnt = slen; --cnt >= 0; src++) { svalue_t *v; flags[cnt] = 0; if (current_object->flags & O_DESTRUCTED) continue; /* Don't call the filter anymore, but fill the * flags array with 0es. */ if (!callback_object(&cb)) { inter_sp = sp; errorf("object used by filter(array) destructed"); } push_number(inter_sp, *src); v = apply_callback(&cb, 1); if (!v || (v->type == T_NUMBER && !v->u.number) ) continue; flags[cnt] = 1; res++; } } /* flags[] holds the filter results, res is the number of * elements to keep. Now create the result vector. */ rc = alloc_mstring(res); if (!rc) { errorf("Out of memory (%"PRIdMPINT" bytes) for result in filter().\n", slen+1); } for (src = get_txt(str), dest = get_txt(rc), flags = &flags[slen] ; res > 0 ; src++) { if (*--flags) { *dest++ = *src; res--; } } /* Cleanup. Arguments for the closure have already been removed. On the * stack are now the string, the mapping or callback structure and the * error handler. (Not using pop_n_elems() for 2 elements for saving loop * and function call overhead.) */ free_svalue(sp--); /* errorhandler, buffer and flags are freed by this. */ free_svalue(sp--); /* mapping or callback structure. */ free_mstring(str); /* string, at arg == sp */ sp->u.str = rc; /* put result here */ return sp; } /* x_filter_string() */ /*-------------------------------------------------------------------------*/ svalue_t * x_map_string (svalue_t *sp, int num_arg) /* EFUN map() for strings * * string map(string arg, string func, string|object ob, mixed extra...) * string map(string arg, closure cl, mixed extra...) * string map(string arg, mapping m) * * Call the function ->() resp. the closure for * every element of the array/struct/mapping/string , and return a result * made up from the returned values. * * It is also possible to map every entry through a lookup [element]. If * the mapping entry doesn't exist, the original value is kept, otherwise the * result of the mapping lookup. * * Since is a string, only integer return values are allowed, of which * only the lower 8 bits are considered. * * If is omitted, or neither an object nor a string, then * this_object() is used. */ { string_t *res; string_t *str; svalue_t *arg; mp_int len; char *src, *dest; inter_sp = sp; arg = sp - num_arg + 1; str = arg->u.str; len = mstrsize(str); if (arg[1].type == T_MAPPING) { /* --- Map through mapping --- */ mapping_t *m; if (num_arg > 2) { inter_sp = sp; errorf("Too many arguments to map(string)\n"); } m = arg[1].u.map; res = alloc_mstring(len); if (!res) errorf("(map_string) Out of memory: string[%"PRIdMPINT "] for result\n", len); push_string(inter_sp, res); /* In case of errors */ for (src = get_txt(str), dest = get_txt(res); --len >= 0; src++, dest++) { svalue_t key, *v; put_number(&key, *src); v = get_map_value(m, &key); if (v == &const0) *dest = *src; else { if (v->type != T_NUMBER) { errorf("(map_string) Illegal value: %s, expected string\n" , typename(v->type) ); } *dest = (v->u.number & 0xFF); } } free_svalue(arg+1); /* the mapping */ sp = arg; } else { /* --- Map through function call --- */ callback_t cb; int error_index; error_index = setup_efun_callback(&cb, arg+1, num_arg-1); if (error_index >= 0) { vefun_bad_arg(error_index+2, arg); /* NOTREACHED */ return arg; } inter_sp = sp = arg+1; put_callback(sp, &cb); num_arg = 2; res = alloc_mstring(len); if (!res) errorf("(map_string) Out of memory: string[%"PRIdMPINT "] for result\n", len); push_string(inter_sp, res); /* In case of errors */ for (src = get_txt(str), dest = get_txt(res); --len >= 0; src++, dest++) { svalue_t *v; if (current_object->flags & O_DESTRUCTED) continue; if (!callback_object(&cb)) errorf("object used by map(string) destructed"); push_number(inter_sp, *src); v = apply_callback(&cb, 1); if (v) { if (v->type != T_NUMBER) { errorf("(map_string) Illegal value: %s, expected string\n" , typename(v->type) ); } *dest = (v->u.number & 0xFF); } } free_callback(&cb); } /* The arguments have been removed already, now just replace * the arr on the stack with the result. */ free_mstring(str); arg->u.str = res; /* Keep svalue type: T_POINTER */ return arg; } /* x_map_string () */ #ifdef HAS_JSON /* support for javascript object notation * depends on the json-c library * see http://www.json.org for more information */ #include #include "array.h" svalue_t * ldmud_json_inner_parse (svalue_t *sp, struct json_object *val) { if (is_error(val)) { errorf("json_inner_parse: error"); /* NOTREACHED */ return sp; } if (val == NULL) { /* TODO: I (fippo) am not sure, if this is a really good idea... */ put_number(sp, 0); return sp; } switch(json_object_get_type(val)) { case json_type_null: put_number(sp, 0); break; case json_type_boolean: put_number(sp, json_object_get_boolean(val)); break; case json_type_double: put_float(sp, json_object_get_double(val)); break; case json_type_int: put_number(sp, json_object_get_int(val)); break; case json_type_string: put_c_string(sp, json_object_get_string(val)); break; case json_type_object: { mapping_t *m; struct lh_entry *e; char *key; struct json_object *newval; m = allocate_mapping(json_object_get_object(val)->count, 1); for (e = json_object_get_object(val)->head; e ? (key = (char*)e->k, newval = (struct json_object *)e->v, e) : 0; e = e->next) { svalue_t mkey, *mval; put_c_string(&mkey, key); mval = get_map_lvalue(m, &mkey); free_svalue(&mkey); ldmud_json_inner_parse(mval, newval); } put_mapping(sp, m); break; } case json_type_array: { vector_t *v; struct array_list *a; int size, i; size = json_object_array_length(val); v = allocate_array(size); a = json_object_get_array(val); for (i = 0; i < size; i++) { ldmud_json_inner_parse(&(v->item[i]), array_list_get_idx(a, i)); } put_array(sp, v); break; } } return sp; } svalue_t * f_json_parse (svalue_t *sp) { struct json_object *parsed; parsed = json_tokener_parse(get_txt(sp->u.str)); free_svalue(sp); ldmud_json_inner_parse (sp, parsed); /* TODO: free the object */ return sp; } struct json_object * ldmud_json_inner_serialize (svalue_t *sp) { struct json_object *val; switch(sp->type) { case T_NUMBER: val = json_object_new_int(sp->u.number); break; case T_STRING: val = json_object_new_string(get_txt(sp->u.str)); break; case T_POINTER: { int i; val = json_object_new_array(); for (i = VEC_SIZE(sp->u.vec) - 1; i >= 0; i--) json_object_array_put_idx(val, i, ldmud_json_inner_serialize(&sp->u.vec->item[i])); break; } case T_MAPPING: { int i; val = json_object_new_object(); for (i = 0; i < MAP_SIZE(sp->u.map); i++) walk_mapping(sp->u.map, &ldmud_json_walker, val); break; } case T_FLOAT: val = json_object_new_double(READ_DOUBLE(sp)); break; default: /* those are unimplemented */ val = json_object_new_object(); break; } return val; } void ldmud_json_walker(svalue_t *key, svalue_t *val, void *parent) { struct json_object *obj = (struct json_object *)parent; if (key->type != T_STRING) errorf("json only serializes string keys\n"); /* NOTREACHED */ json_object_object_add(obj, get_txt(key->u.str), ldmud_json_inner_serialize(val)); } svalue_t * f_json_serialize (svalue_t *sp) { struct json_object *val; val = ldmud_json_inner_serialize(sp); free_svalue(sp); put_c_string(sp, json_object_to_json_string(val)); return sp; } #endif /* HAS_JSON */ /*====================================================================*/