/
Genesis-1.0p36-DEV/
Genesis-1.0p36-DEV/bin/
Genesis-1.0p36-DEV/doc/
Genesis-1.0p36-DEV/etc/
Genesis-1.0p36-DEV/src/data/
/*
// 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);
}