/* // Full copyright information is available in the file ../doc/CREDITS */ #include "defs.h" #include <string.h> #include <ctype.h> #include "operators.h" #include "execute.h" #include "strutil.h" #include "util.h" #include "crypt.h" COLDC_FUNC(strlen) { cData *args; Int len; /* Accept a string to take the length of. */ if (!func_init_1(&args, STRING)) return; /* Replace the argument with its length. */ len = string_length(args[0].u.str); pop(1); push_int(len); } COLDC_FUNC(substr) { Int num_args, start, len, string_len; cData *args; /* Accept a string for the initial string, an integer specifying the start * of the substring, and an optional integer specifying the length of the * substring. */ if (!func_init_2_or_3(&args, &num_args, STRING, INTEGER, INTEGER)) return; string_len = string_length(args[0].u.str); start = args[1].u.val - 1; len = (num_args == 3) ? args[2].u.val : string_len - start; /* Make sure range is in bounds. */ if (start < 0) THROW((range_id, "Start (%d) is less than one.", start + 1)) else if (len < 0) THROW((range_id, "Length (%d) is less than zero.", len)) else if (start + len > string_len) THROW((range_id, "The substring extends to %d, past the end of the string (%d).", start + len, string_len)) /* Replace first argument with substring, and pop other arguments. */ anticipate_assignment(); args[0].u.str = string_substring(args[0].u.str, start, len); pop(num_args - 1); } COLDC_FUNC(explode) { Int argc, sep_len; Bool want_blanks; cData * args; cList * exploded; char * sep; /* Accept a string to explode and an optional string for the word * separator. */ if (!func_init_1_to_3(&args, &argc, STRING, STRING, 0)) return; want_blanks = (Bool) ((argc == 3) ? data_true(&args[2]) : NO); if (argc >= 2) { sep = string_chars(args[1].u.str); sep_len = string_length(args[1].u.str); } else { sep = " "; sep_len = 1; } if (!*sep) { cthrow(range_id, "Null string as separator."); return; } exploded = strexplode(args[0].u.str, sep, sep_len, want_blanks); /* Pop the arguments and push the list onto the stack. */ pop(argc); push_list(exploded); list_discard(exploded); } COLDC_FUNC(strsub) { #if 0 Int len, search_len, replace_len; cData *args; char *search, *replace, *s, *p, *q; cStr *subbed; #endif cData * args; int argc; Int flags = RF_GLOBAL; cStr * out; /* Accept a base string, a search string, and a replacement string. */ if (!func_init_3_or_4(&args, &argc, STRING, STRING, STRING, STRING)) return; #if DISABLED s = string_chars(args[0].u.str); len = string_length(args[0].u.str); search = string_chars(args[1].u.str); search_len = string_length(args[1].u.str); replace = string_chars(args[2].u.str); replace_len = string_length(args[2].u.str); if (*s == (char) NULL || *search == (char) NULL) { subbed = string_dup(args[0].u.str); } else { subbed = string_new(search_len); p = s; for (q = strcstr(p, search); q; q = strcstr(p, search)) { subbed = string_add_chars(subbed, p, q - p); subbed = string_add_chars(subbed, replace, replace_len); p = q + search_len; } subbed = string_add_chars(subbed, p, len - (p - s)); } #endif if (argc == 4) flags = parse_regfunc_args(string_chars(STR4), flags); out = strsub(STR1, STR2, STR3, flags); /* Pop the arguments and push the new string onto the stack. */ pop(argc); push_string(out); string_discard(out); } /* Pad a string on the left (positive length) or on the right (negative * length). The optional third argument gives the fill character. */ COLDC_FUNC(pad) { Int num_args, len, padding, filler_len; cData *args; char *filler; cStr *padded; if (!func_init_2_or_3(&args, &num_args, STRING, INTEGER, STRING)) return; if (num_args == 3) { filler = string_chars(args[2].u.str); filler_len = string_length(args[2].u.str); } else { filler = " "; filler_len = 1; } len = (args[1].u.val > 0) ? args[1].u.val : -args[1].u.val; padding = len - string_length(args[0].u.str); /* Construct the padded string. */ anticipate_assignment(); padded = args[0].u.str; if (padding == 0) { /* Do nothing. Easiest case. */ } else if (padding < 0) { /* We're shortening the string. Almost as easy. */ padded = string_truncate(padded, len); } else if (args[1].u.val > 0) { /* We're lengthening the string on the right. */ padded = string_add_padding(padded, filler, filler_len, padding); } else { /* We're lengthening the string on the left. */ padded = string_new(padding + args[0].u.str->len); padded = string_add_padding(padded, filler, filler_len, padding); padded = string_add(padded, args[0].u.str); string_discard(args[0].u.str); } args[0].u.str = padded; /* Discard all but the first argument. */ pop(num_args - 1); } COLDC_FUNC(match_begin) { cData *args; Int sep_len, search_len; char *sep, *search, *s, *p; Int num_args; /* Accept a base string, a search string, and an optional separator. */ if (!func_init_2_or_3(&args, &num_args, STRING, STRING, STRING)) return; s = string_chars(STR1); search = string_chars(STR2); search_len = string_length(STR2); if (num_args > 2) { sep = string_chars(args[2].u.str); sep_len = string_length(args[2].u.str); if (!sep_len) THROW((range_id, "Zero length separator.")) } else { sep = " "; sep_len = 1; } for (p = s - sep_len; p; p = strcstr(p + 1, sep)) { /* We found a separator; see if it's followed by search. */ if (strnccmp(p + sep_len, search, search_len) == 0) { pop(num_args); push_int(1); return; } } pop(num_args); push_int(0); } /* Match against a command template. */ COLDC_FUNC(match_template) { cData *args; cList *fields; char *ctemplate, *str; /* Accept a string for the template and a string to match against. */ if (!func_init_2(&args, STRING, STRING)) return; str = string_chars(STR1); ctemplate = string_chars(STR2); fields = match_template(ctemplate, str); pop(2); if (fields) { push_list(fields); list_discard(fields); } else { push_int(0); } } /* Match against a command template. */ COLDC_FUNC(match_pattern) { cData *args; cList *fields; char *pattern, *str; /* Accept a string for the pattern and a string to match against. */ if (!func_init_2(&args, STRING, STRING)) return; str = string_chars(STR1); pattern = string_chars(STR2); fields = match_pattern(pattern, str); pop(2); if (!fields) { push_int(0); return; } /* fields is backwards. Reverse it. */ fields = list_reverse(fields); push_list(fields); list_discard(fields); } COLDC_FUNC(match_regexp) { cData * args; cList * fields; Int argc, sensitive; Bool error; if (!func_init_2_or_3(&args, &argc, STRING, STRING, 0)) return; sensitive = (argc == 3) ? data_true(&args[2]) : 0; fields = match_regexp(STR2, string_chars(STR1), sensitive, &error); if (fields) { pop(argc); push_list(fields); list_discard(fields); } else { if (error == YES) /* we actually threw an error */ return; pop(argc); push_int(0); } } COLDC_FUNC(regexp) { cData * args; cList * fields; Int argc, sensitive; Bool error; if (!func_init_2_or_3(&args, &argc, STRING, STRING, 0)) return; sensitive = (argc == 3) ? data_true(&args[2]) : 0; fields = regexp_matches(STR2, string_chars(STR1), sensitive, &error); if (fields) { pop(argc); push_list(fields); list_discard(fields); } else { if (error == YES) return; pop(argc); push_int(0); } } COLDC_FUNC(strsed) { cData * args; cStr * out; Int argc, flags = RF_NONE, mult=2, arg_start = arg_starts[--arg_pos]; args = &stack[arg_start]; argc = stack_pos - arg_start; switch (argc) { case 5: if (args[4].type != INTEGER) THROW_TYPE_ERROR(STRING, "fifth", 4) mult = args[4].u.val; if (mult < 0) mult = 2; if (mult > 10) THROW((perm_id, "You can only specify a size multiplier of 1-10, sorry!")) case 4: if (args[3].type != STRING) THROW_TYPE_ERROR(STRING, "fourth", 3) flags = parse_regfunc_args(string_chars(STR4), flags); case 3: if (args[0].type != STRING) THROW_TYPE_ERROR(STRING, "first", 0) else if (args[1].type != STRING) THROW_TYPE_ERROR(STRING, "second", 1) else if (args[2].type != STRING) THROW_TYPE_ERROR(STRING, "third", 2) break; default: func_num_error(argc, "three to five"); return; } if (!(out = strsed(STR2, STR1, STR3, flags, mult))) return; pop(argc); push_string(out); string_discard(out); } /* Encrypt a string. */ COLDC_FUNC(crypt) { Int argc; cData * args; cStr * str; /* Accept a string to encrypt and an optional salt. */ if (!func_init_1_or_2(&args, &argc, STRING, STRING)) return; str = strcrypt(STR1, ((argc == 2) ? (STR2) : ((cStr *) NULL))); pop(argc); push_string(str); string_discard(str); } /* Match Encrypted string. */ /* first arg: already encrypted password */ /* second arg: word which may be what the encrypted password is */ COLDC_FUNC(match_crypted) { cData * args; Int rval; /* Accept a string to encrypt and an optional salt. */ if (!func_init_2(&args, STRING, STRING)) return; /* match_crypted returns F_FAILURE when it throws */ if ((rval = match_crypted(STR1, STR2)) == F_FAILURE) return; pop(2); push_int(rval); } COLDC_FUNC(uppercase) { cData * args; /* Accept a string to uppercase. */ if (!func_init_1(&args, STRING)) return; args[0].u.str = string_uppercase(args[0].u.str); } COLDC_FUNC(lowercase) { cData * args; /* Accept a string to uppercase. */ if (!func_init_1(&args, STRING)) return; args[0].u.str = string_lowercase(args[0].u.str); } COLDC_FUNC(strcmp) { cData *args; Int val; /* Accept two strings to compare. */ if (!func_init_2(&args, STRING, STRING)) return; /* Compare the strings case-sensitively. */ val = strcmp(string_chars(args[0].u.str), string_chars(args[1].u.str)); pop(2); push_int(val); } COLDC_FUNC(strgraft) { cData * args; cStr * new, * s1, * s2; Int pos; if (!func_init_3(&args, STRING, INTEGER, STRING)) return; pos = args[1].u.val - 1; s1 = args[0].u.str; s2 = args[2].u.str; if (pos > string_length(s1) || pos < 0) { cthrow(range_id, "Position %D is outside of the range of the string.", &args[1]); return; } else if (pos == 0) { s2 = string_add(s2, s1); new = string_dup(s2); } else if (pos == string_length(s1)) { s1 = string_add(s1, s2); new = string_dup(s1); } else { new = string_new(string_length(s1) + string_length(s2)); new = string_add_chars(new, string_chars(s1), pos); new = string_add(new, s2); new =string_add_chars(new,string_chars(s1)+pos,string_length(s1)-pos+1); } pop(3); push_string(new); string_discard(new); } COLDC_FUNC(strfmt) { Int arg_start, argc; cData * args; cStr * fmt, * out; /* init ourselves for a variable number of arguments of any type */ arg_start = arg_starts[--arg_pos]; argc = stack_pos - arg_start; if (!argc) THROW((numargs_id, "Called with no arguments, requires at least one.")) if (stack[arg_start].type != STRING) THROW((type_id, "First argument (%D) not a string.", &stack[arg_start])) /* leave the format on the stack, it is all they sent */ if (argc == 1) return; fmt = stack[arg_start].u.str; args = &stack[arg_start + 1]; if ((out = strfmt(fmt, args, argc - 1)) == (cStr *) NULL) return; pop(argc); push_string(out); string_discard(out); } COLDC_FUNC(split) { Int flags = RF_NONE; cList * list; INIT_2_OR_3_ARGS(STRING, STRING, STRING); if (argc == 3) flags = parse_regfunc_args(string_chars(STR3), flags); if (!(list = strsplit(STR1, STR2, flags))) return; pop(argc); push_list(list); list_discard(list); } COLDC_FUNC(stridx) { int origin; int r; INIT_2_OR_3_ARGS(STRING, STRING, INTEGER); if (argc == 3) origin = INT3; else origin = 1; if (!string_length(STR2)) THROW((type_id, "No search string.")) if ((r = string_index(STR1, STR2, origin)) == F_FAILURE) THROW((range_id, "Origin is beyond the range of the string.")) pop(argc); push_int(r); }