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, "&", "&"); line = strsub(line, "<", "<"); line = strsub(line, ">", ">"); 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]; };