/
ColdCore-3.0a9.02/
ColdCore-3.0a9.02/src/
new object $string: $libraries;

var $root created_on = 796268969;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root inited = 1;
var $root managed = [$string];
var $root manager = $string;
var $string alphabet = "abcdefghijklmnopqrstuvwxyz";
var $string base64str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
var $string non_alphanumeric = "!@#$%^&*()_+-=~`'{}[]|/?\",.<>;: ";
var $string number_names = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"];
var $string numbers = "0123456789";

public method .a_or_an() {
    arg string;
    
    return $english_lib.indef_article(string);
    if ((string[1]) in "aeiou")
        return "an";
    return "a";
};

public method .add_indefinite() {
    arg str;
    
    anticipate_assignment();
    return ((str.a_or_an()) + " ") + str;
};

public method .alphabet() {
    return alphabet;
};

public method .capitalize(): native;

public method .center() {
    arg text, width, @args;
    var fill, sides;
    
    // args[1] <op> == what to fill the left|right side with.
    // args[2] <op> == if exists also fill the right side.
    [(fill ?= " "), (sides ?= 'left)] = args;
    if (sides == 'both)
        return strfmt(("%*{" + fill) + "}c", width, text);
    else
        return pad("", (width - strlen(text)) / 2, fill) + text;
};

public method .chop() {
    arg str, len, @end;
    
    // chops string off end.length() characters before len and appends len
    [(end ?= "...")] = end;
    if ((strlen(str) < len) || (strlen(str) < strlen(end)))
        return str;
    anticipate_assignment();
    return pad(str, len - strlen(end)) + end;
};

public method .compare(): native;

public method .crypt(): native;

public method .dbquote_explode(): native;

public method .decode64() {
    arg enc_chars;
    var i, j, k, ints, out_buf, len;
    
    i = 1;
    out_buf = `[];
    len = enc_chars.length();
    while (i < len) {
        refresh();
        ints = [];
        for j in [1 .. 4] {
            for k in [1 .. 65] {
                if (strcmp(enc_chars[i], base64str[k]) == 0) {
                    ints = [@ints, (k - 1) % 64];
                    break;
                }
            }
            i++;
        }
        out_buf = out_buf + `[(((ints[1]).shleft(2)) % 256) + ((ints[2]).shright(4)), (((ints[2]).shleft(4)) % 256) + ((ints[3]).shright(2)), (((ints[3]).shleft(6)) % 256) + (ints[4])];
    }
    return out_buf.to_string();
};

public method .explode(): native;

public method .explode_delimited() {
    arg str, left, right;
    var pattern, parsed, matched, match_num, match_result;
    
    // parse str looking for anything surrounded by left and right
    // ;$string.explode_delimited("foo<bar>baz", "<", ">")  
    // => [["foo", 1, "baz"], ["bar"]]
    pattern = ((("*" + left) + "*") + right) + "*";
    parsed = [];
    matched = [];
    match_num = 0;
    anticipate_assignment();
    while (str) {
        match_result = match_pattern(str, pattern);
        if (match_result) {
            match_num++;
            parsed += [match_result[1], match_num];
            matched += [match_result[2]];
            str = match_result[3];
        } else {
            parsed += [str];
            str = "";
        }
    }
    return [parsed, matched];
};

public method .explode_english_list() {
    arg line, @opts;
    var x, output, tmp;
    
    if (!line)
        return [];
    
    // explodes an english list ("foo, bar and zoo").
    line = line.explode(",");
    output = [];
    for x in (line) {
        x = .trim(x);
        if ((| x.subrange(1, 4) |) == "and ")
            output += [.trim(x.subrange(4))];
        else
            output += [x];
    }
    
    // check the last element, if they didn't specify  'noand
    if (!('noand in opts)) {
        line = (output[output.length()]).explode();
        tmp = "";
        for x in [1 .. line.length()] {
            if ((line[x]) == "and") {
                output = output.delete(output.length());
                if (tmp)
                    output += [tmp];
                tmp = (line.subrange(x + 1)).join();
                if (tmp)
                    output += [tmp];
    
                // only bother with the first "and"
                break;
            }
            tmp = (tmp + (tmp ? " " : "")) + (line[x]);
        }
    }
    return output;
};

public method .explode_http_encoding() {
    arg args;
    var fields, field, values;
    
    fields = #[];
    for field in (args.explode("&")) {
        field = field.explode("=");
        if (listlen(field) == 1)
            field = [field[1], ""];
        fields = fields.add(@field);
    }
    return fields;
};

public method .explode_list() {
    arg str;
    
    if (("," in str) || (" and " in str))
        return str.explode_english_list();
    else
        return str.explode();
};

public method .explode_quoted() {
    arg str;
    var out, result, x, qstr;
    
    out = [];
    
    // HACK: we need to make this a native--its horribly inefficient
    qstr = ("#$#QUOTE-" + time()) + "#$#";
    str = strsub(str, "\\\"", qstr);
    while (str) {
        result = match_pattern(str, "*\"*\"*");
        if (result) {
            out += ((result[1]).explode()) + [(result[2]).trim()];
            str = result[3];
        } else {
            out += str.explode();
            str = "";
        }
    }
    for x in [1 .. listlen(out)]
        out = replace(out, x, strsub(out[x], qstr, "\""));
    return out;
};

public method .explode_template_word() {
    arg template;
    var t, x, idx, out, new;
    
    // this only explodes single word templates
    template = template.explode("|");
    out = [];
    for t in (template) {
        idx = "?" in t;
        if (idx) {
            t = t.strip("?");
            new = t.subrange(1, idx - 1);
            out += [new];
            for x in [idx .. t.length()] {
                new += t[x];
                out += [new];
            }
        } else {
            out += [t];
        }
    }
    return out;
};

public method .explode_url() {
    arg line;
    var out, args, i;
    
    i = "?" in line;
    if (i) {
        args = substr(line, i + 1);
        line = substr(line, 1, i - 1);
    }
    if (args)
        out = #[['path, explode(line, "/")], ['args, .explode_http_encoding(args)]];
    else
        out = #[['path, explode(line, "/")], ['args, #[]]];
    return out;
};

public method .find_escaped() {
    arg str, char;
    var good, start, pos, p;
    
    good = 0;
    start = 0;
    while ((!good) && (start < (str.length()))) {
        pos = (char in (str.subrange(start + 1))) + start;
        good = 1;
        if (pos > start) {
            p = pos - 1;
            while ((p > 0) && ((str[p]) == "\\")) {
                good = good ? 0 : 1;
                p = p - 1;
            }
        }
        if (good)
            return pos;
        else
            start = pos;
    }
};

public method .format(): native;

public method .global_regexp() {
    arg string, regexp;
    var result, location, left, begin_match, end_match;
    
    result = [];
    left = string;
    while (left) {
        if ((location = match_regexp(left, regexp))) {
            [begin_match, end_match] = location[1];
            result += [(begin_match == 1) ? "" : (left.subrange(1, begin_match - 1))];
            result += [left.subrange(begin_match, end_match)];
            left = left.subrange(begin_match + end_match);
        } else {
            result += [left];
            left = "";
        }
    }
    if (!((result.length()) % 2))
        result += [""];
    return result;
};

public method .html_escape(): native;

public method .is_boolean() {
    arg str;
    
    if (match_regexp(str, "^(yes|y|true|t|1)$"))
        return 1;
    if (match_regexp(str, "^(no|n|false|f|0)$"))
        return 0;
    return -1;
};

public method .is_numeric() {
    arg string;
    
    return toint(string) || (string == "0");
};

public method .last() {
    arg str;
    
    return str[str.length()];
};

public method .left() {
    arg str, width, @fchar;
    
    // will not chop off 'str' if it is longer than width, use pad() for that.
    [(fchar ?= " ")] = fchar;
    if (strlen(str) < width)
        return str + pad("", width - (str.length()), fchar);
    return str;
};

public method .length(): native;

public method .lowercase(): native;

public method .match_begin(): native;

public method .match_pattern(): native;

public method .match_regexp(): native;

public method .match_template(): native;

public method .non_alphanumeric() {
    return non_alphanumeric;
};

public method .numbers() {
    return numbers;
};

public method .pad(): native;

public method .random() {
    arg str;
    
    return str[random(strlen(str))];
};

public method .rangeset() {
    arg string, start, len, new;
    
    anticipate_assignment();
    return ((string.subrange(1, start - 1)) + new) + (string.subrange(start + len));
};

public method .regexp(): native;

public method .replace(): native;

public method .rformat() {
    arg first, second, @rest;
    
    // so you can mmap a list of strings through strfmt
    return strfmt(second, first, @rest);
};

public method .right() {
    arg str, width, @fchar;
    
    // will not chop off 'str' if it is longer than width (unlike pad())
    [(fchar ?= " ")] = fchar;
    if (strlen(str) < width)
        return pad(str, -width, fchar);
    return str;
};

public method .rindex() {
    arg string, index;
    
    return stridx(string, index, -1);
};

public method .sed(): native;

public method .split(): native;

public method .strip() {
    arg string, @strip;
    
    anticipate_assignment();
    if (!strip)
        return strsed(string, "[][!@#$%^&*()_+=~`'{}|/?\"\,.<>;: -]", "", "g");
    else
        return strsed(string, ("[" + (strip[1])) + "]", "", "g");
};

public method .strip_article() {
    arg str;
    
    return strsed(str, "^(an|a|the)  *", "", "g");
};

public method .subrange(): native;

public method .to_buffer() {
    arg string;
    
    return (> str_to_buf(string) <);
};

public method .to_bytes() {
    arg str;
    var words, num;
    
    words = str.explode();
    if (listlen(words) == 1) {
        if (!(words = regexp(words[1], "^([0-9]+)(.*)$")))
            throw(~type, "Invalid size.");
    }
    num = (> (words[1]).to_number() <);
    switch (words[2]) {
        case "b", "byte", "bytes":
            return num;
        case "k", "kb", "kbytes", "kilobytes":
            return num * 1024;
        case "m", "mb", "mbytes", "megabytes":
            return (num * 1024) * 1024;
        case "g", "gb", "gbytes", "gigabytes":
            return ((num * 1024) * 1024) * 1024;
        default:
            throw(~type, "Invalid byte size: " + (words[2]));
    }
};

public method .to_html() {
    arg line;
    
    anticipate_assignment();
    line = strsub(line, "&", "&amp;");
    line = strsub(line, "<", "&lt;");
    line = strsub(line, ">", "&gt;");
    return line;
};

public method .to_number() {
    arg str;
    
    if (str.is_numeric())
        return toint(str);
    throw(~nonum, ("\"" + str) + "\" is not a number.");
};

public method .to_symbol() {
    arg str;
    
    str = lowercase(strsed(str, "[^a-zA-Z0-9_]+", "", "g"));
    return (> tosym(str) <);
};

public method .trim(): native;

public method .unquote() {
    arg str;
    
    if (str && (((str[1]) == "\"") && ((str[str.length()]) == "\"")))
        return str.subrange(2, (str.length()) - 2);
    return str;
};

public method .uppercase(): native;

public method .valid_ident() {
    arg str;
    
    return strsed(str, "[^a-z0-9_]+", "", "g") == str;
};

public method .word(): native;

public method .wrap_line() {
    arg str, len, @stuff;
    var output, cutoff, firstline, prefix, plen;
    
    // takes string and wraps it by words, compared to length, breaks with \n
    [(prefix ?= ""), (firstline ?= 0)] = stuff;
    output = "";
    if (firstline)
        str = prefix + str;
    plen = strlen(prefix);
    while (strlen(str) > len) {
        cutoff = stridx(substr(str, 1, len), " ", -1);
        if (cutoff <= plen) {
            output += "\n" + substr(str, 1, len);
            str = prefix + substr(str, len + 1);
        } else {
            output += "\n" + substr(str, 1, cutoff - 1);
            str = prefix + substr(str, cutoff + 1);
        }
    }
    return (output ? ((output.subrange(3)) + "\n") : "") + str;
};

public method .wrap_lines() {
    arg str, len, @stuff;
    var output, cutoff, firstline, prefix, plen;
    
    // takes string and wraps it by words, compared to length, returns a list. 
    [(prefix ?= ""), (firstline ?= 0)] = stuff;
    output = [];
    if (firstline)
        str = prefix + str;
    plen = strlen(prefix);
    while (strlen(str) > len) {
        cutoff = stridx(substr(str, 1, len), " ", -1);
        if (cutoff <= plen) {
            output += [substr(str, 1, len)];
            str = prefix + substr(str, len + 1);
        } else {
            output += [substr(str, 1, cutoff - 1)];
            str = prefix + substr(str, cutoff + 1);
        }
    }
    return output + [str];
};