home *** CD-ROM | disk | FTP | other *** search
Text File | 2006-11-29 | 27.3 KB | 1,117 lines |
- /**
- * File: modules/String.ycp
- * Package: yast2
- * Summary: String manipulation routines
- * Authors: Michal Svec <msvec@suse.cz>
- *
- * $Id: String.ycp 28838 2006-03-10 16:11:58Z mvidner $
- */
-
- {
-
- module "String";
- textdomain "base";
-
- /**
- * Quote a string with 's
- * @param var unquoted string
- * @return quoted string
- * @example quote("a'b") -> "a'\''b"
- */
- global define string Quote(string var) ``{
- if(var == nil || var == "") return "";
- return mergestring(splitstring(var, "'"), "'\\''");
- }
-
- /**
- * Unquote a string with 's (quoted with quote)
- * @param var quoted string
- * @return unquoted string
- * @see quote
- */
- global define string UnQuote(string var) ``{
- if(var == nil || var == "") return "";
- y2debug("var=%1", var);
- while(regexpmatch(var, "'\\\\''")) {
- var = regexpsub(var, "(.*)'\\\\''(.*)", "\\1'\\2");
- y2debug("var=%1", var);
- }
- return var;
- }
-
- /**
- * Optional formatted text
- * @return sformat (f, s) if s is neither empty or nil, else ""
- */
- global string OptFormat (string f, string s) {
- return (s == "" || s == nil)? "": sformat (f, s);
- }
-
- /**
- * Optional parenthesized text
- * @return " (Foo)" if Foo is neither empty or nil, else ""
- */
- global string OptParens (string s) {
- return OptFormat (" (%1)", s);
- }
-
- /**
- * @param l a list of strings
- * @return only non-"" items
- */
- global list<string> NonEmpty (list<string> l) {
- return filter (string i, l, ``( i != "" ));
- }
-
- /**
- * @param s \n-terminated items
- * @return the items as a list, with empty lines removed
- */
- global list<string> NewlineItems (string s) {
- return NonEmpty (splitstring (s, "\n"));
- }
-
- /**
- * Return a pretty description of a byte count
- *
- * Return a pretty description of a byte count with required precision
- * and using KB, MB or GB as unit as appropriate.
- *
- * @param bytes size (e.g. free diskspace, memory size) in Bytes
- * @param precision number of fraction digits in output
- * @param omit_zeroes if true then do not add zeroes
- * (usefull for memory size - 128 MB RAM looks better than 128.00 MB RAM)
- * @return formatted string
- *
- * @example FormatSizeWithPrecision(4096, 2, true) -> "4 KB"
- * @example FormatSizeWithPrecision(4096, 2, false) -> "4.00 KB"
- */
- global define string FormatSizeWithPrecision(integer bytes, integer precision, boolean omit_zeroes) ``{
-
- if(bytes == nil) return "";
-
- string unit = "";
- list units = [
- /* KiloByte abbreviated */
- _("KB"),
- /* MegaByte abbreviated */
- _("MB"),
- /* GigaByte abbreviated */
- _("GB"),
- /* TeraByte abbreviated */
- _("TB"),
- ];
- integer index = 0;
- float whole = tofloat(bytes);
-
- while((whole > 1024.0 || whole < -1024.0) && index < size(units)) {
- whole = whole / 1024.0;
- unit = units[index]:"";
- index = index + 1;
- }
-
- if (precision == nil || precision < 0 || (omit_zeroes == true && (whole - tointeger(whole) == 0.0)))
- {
- precision = 0;
- }
-
- return tostring(whole, precision) + " " + unit;
- }
-
- /**
- * Return a pretty description of a byte count
- *
- * Return a pretty description of a byte count, with two fraction digits
- * and using KB, MB or GB as unit as appropriate.
- *
- * @param bytes size (e.g. free diskspace) in Bytes
- * @return formatted string
- *
- * @example FormatSize(23456767890) -> "223.70 MB"
- */
- global define string FormatSize(integer bytes) ``{
- return FormatSizeWithPrecision(bytes, 2, false);
- }
-
- /**
- * Remove blanks at begin and end of input string.
- * @param input string to be stripped
- * @return stripped string
- * @example CutBlanks(" any input ") -> "any input"
- */
- global define string CutBlanks(string input) ``{
-
- if(input == nil || size(input) < 1) return "";
-
- integer pos1 = findfirstnotof(input, " \t");
- if(pos1 == nil) return "";
-
- integer pos2 = findlastnotof(input, " \t");
-
- return substring(input, pos1, pos2 - pos1 + 1);
- }
-
- /**
- * Remove any leading zeros
- *
- * Remove any leading zeros that make tointeger inadvertently
- * assume an octal number (e.g. "09" -> "9", "0001" -> "1",
- * but "0" -> "0")
- *
- * @param input number that might contain leadig zero
- * @return string that has leading zeros removed
- */
- global define string CutZeros(string input) ``{
- if(input == nil || size(input) < 1) return "";
- if(!regexpmatch(input, "^0.*")) return input;
- string output = regexpsub(input, "^0+(.*)$", "\\1");
- if(size(output) < 1) return "0";
- return output;
- }
-
- /**
- * Add spaces after the text to make it long enough
- *
- * Add spaces after the text to make it long enough. If the text is longer
- * than requested, no changes are made.
- *
- * @param text text to be padded
- * @param length requested length
- * @return padded text
- */
- global define string Pad(string text, integer length) {
- if(text == nil) text = "";
-
- integer rest = length - size(text);
- string pad = "";
- while(rest > 0) {
- pad = pad + " ";
- rest = rest - 1;
- }
-
- return text + pad;
- }
-
- /**
- * Add zeros before the text to make it long enough.
- *
- * Add zeros before the text to make it long enough. If the text is longer
- * than requested, no changes are made.
- *
- * @param text text to be padded
- * @param length requested length
- * @return padded text
- */
- global define string PadZeros(string text, integer length) {
- if(text == nil) text = "";
-
- integer rest = length - size(text);
- string pad = "";
- while(rest > 0) {
- pad = pad + "0";
- rest = rest - 1;
- }
-
- return pad + text;
- }
-
- /**
- * Parse string of values
- *
- * Parse string of values - split string to values, quoting and backslash sequences are supported
- * @param options Input string
- * @param parameters Parmeter used at parsing - map with keys:
- *"separator":<string> - value separator (default: " \t"),
- *"unique":<boolean> - result will not contain any duplicates, first occurance of the string is stored into output (default: false),
- *"interpret_backslash":<boolean> - convert backslash sequence into one character (e.g. "\\n" => "\n") (default: true)
- *"remove_whitespace":<boolean> - remove white spaces around values (default: true),
- * @return list<string> List of strings
- */
- global define list<string> ParseOptions(string options, map parameters) ``{
- list<string> ret = [];
-
- // parsing options
- string separator = parameters["separator"]:" \t";
- boolean unique = parameters["unique"]:false;
- boolean interpret_backslash = parameters["interpret_backslash"]:true;
- boolean remove_whitespace = parameters["remove_whitespace"]:true;
-
- y2debug("Input: string: '%1', parameters: %2", options, parameters);
- y2debug("Used values: separator: '%1', unique: %2, remove_whitespace: %3",
- separator, unique, remove_whitespace);
-
- if (options == nil)
- {
- return [];
- }
-
- // two algorithms are used:
- // first is much faster, but only usable if string
- // doesn't contain any double qoute characters
- // and backslash sequences are not interpreted
- // second is more general, but of course slower
-
- if (findfirstof(options, "\"") == nil && interpret_backslash == false)
- {
- // easy case - no qouting, don't interpres backslash sequences => use splitstring
- list<string> values = splitstring(options, separator);
-
- foreach (string v, values, ``{
- if (remove_whitespace == true)
- {
- v = CutBlanks(v);
- }
-
- if (unique == true)
- {
- if (!contains(ret, v)) ret = add(ret, v);
- }
- else
- {
- ret = add(ret, v);
- }
- });
- }
- else
- {
- // quoting is used or backslash interpretation is enabled
- // so it' not possible to split input
- // parsing each character is needed - use finite automaton
-
- // state
- symbol state = `out_of_string;
- // position in the input string
- integer index = 0;
- // parsed value - buffer
- string str = "";
-
- while(index < size(options))
- {
- string character = substring(options, index, 1);
-
- y2debug("character: %1 state: %2 index: %3", character, state, index);
-
- // interpret backslash sequence
- if (character == "\\" && interpret_backslash == true)
- {
- if (index + 1 < size(options))
- {
- string nextcharacter = substring(options, index + 1, 1);
- index = index + 1;
-
- // backslah sequences
- map backslash_seq = $[
- "a" : "\a", // alert
- "b" : "\b", // backspace
- "e" : "\e", // escape
- "f" : "\f", // FF
- "n" : "\n", // NL
- "r" : "\r", // CR
- "t" : "\t", // tab
- "v" : "\v", // vertical tab
- "\\": "\\", // backslash
- ];
-
- if (haskey(backslash_seq, nextcharacter) == true)
- {
- character = backslash_seq[nextcharacter]:"DUMMY";
- }
- else
- {
- if (nextcharacter != "\"")
- {
- // ignore backslash in invalid backslash sequence
- character = nextcharacter;
- }
- else
- {
- // backslash will be removed later,
- // double quote and escaped double quote have to different yet
- character = "\\\"";
- }
- }
-
- y2debug("backslash sequence: '%1'", character);
- }
- else
- {
- y2warning("Missing character after backslash (\\) at the end of string");
- }
- }
-
- if (state == `out_of_string)
- {
- // ignore separator or white space at the beginning of the string
- if (issubstring(separator, character) == true || (remove_whitespace == true && (character == " " || character == "\t")) )
- {
- index = index + 1;
- continue;
- }
- // start of a quoted string
- else if (character == "\"")
- {
- state = `in_quoted_string;
- }
- else
- {
- // start of a string
- state = `in_string;
-
- if (character == "\\\"")
- {
- str = "\"";
- }
- else
- {
- str = character;
- }
- }
- }
-
- // after double quoted string - handle non-separator chars after double quote
- else if (state == `in_quoted_string_after_dblqt)
- {
- if (issubstring(separator, character) == true)
- {
- if (unique == true)
- {
- if (!contains(ret, str)) ret = add(ret, str);
- }
- else
- {
- ret = add(ret, str);
- }
-
- str = "";
- state = `out_of_string;
- }
- else if (character == "\\\"")
- {
- str = str + "\"";
- }
- else
- {
- str = str + character;
- }
- }
- else if (state == `in_quoted_string)
- {
- if (character == "\"")
- {
- // end of quoted string
- state = `in_quoted_string_after_dblqt;
- }
- else if (character == "\\\"")
- {
- str = str + "\"";
- }
- else
- {
- str = str + character;
- }
- }
- else if (state == `in_string)
- {
- if (issubstring(separator, character) == true)
- {
- state = `out_of_string;
-
- if (remove_whitespace == true)
- {
- str = CutBlanks(str);
- }
-
- if (unique == true)
- {
- if (!contains(ret, str)) ret = add(ret, str);
- }
- else
- {
- ret = add(ret, str);
- }
-
- str = "";
- }
- else if (character == "\\\"")
- {
- str = str + "\"";
- }
- else
- {
- str = str + character;
- }
- }
-
- index = index + 1;
- }
-
- // error - still in quoted string
- if (state == `in_quoted_string || state == `in_quoted_string_after_dblqt)
- {
- if (state == `in_quoted_string)
- {
- y2warning("Missing trainling double quote character(\") in input: '%1'", options);
- }
-
- if (unique == true)
- {
- if (!contains(ret, str)) ret = add(ret, str);
- }
- else
- {
- ret = add(ret, str);
- }
- }
-
- // process last string in the buffer
- if (state == `in_string)
- {
- if (remove_whitespace)
- {
- str = CutBlanks(str);
- }
-
- if (unique == true)
- {
- if (!contains(ret, str)) ret = add(ret, str);
- }
- else
- {
- ret = add(ret, str);
- }
- }
- }
-
-
- y2debug("Parsed values: %1", ret);
-
- return ret;
- }
-
- /**
- * Remove first or every match of given regular expression from a string
- *
- * (e.g. CutRegexMatch( "abcdef12ef34gh000", "[0-9]+", true ) -> "abcdefefgh",
- * CutRegexMatch( "abcdef12ef34gh000", "[0-9]+", false ) -> "abcdefef34gh000")
- *
- * @param input string that might occur regex
- * @param regex regular expression to search for, must not contain brackets
- * @param glob flag if only first or every occuring match should be removed
- * @return string that has matches removed
- */
- global define string CutRegexMatch(string input, string regex, boolean glob) ``{
- if(input == nil || size(input) < 1) return "";
- string output = input;
- if( regexpmatch( output, regex ) )
- {
- list p = regexppos( output, regex );
- do
- {
- output = substring( output, 0, p[0]:0 ) +
- substring( output, p[0]:0+p[1]:0 );
- p = regexppos( output, regex );
- }
- while( glob && size(p)>0 );
- }
- return output;
- }
-
- /**
- * Function for escaping (replacing) (HTML|XML...) tags with their
- * (HTML|XML...) meaning.
- *
- * Usable to present text "as is" in RichText.
- *
- * @param string text to escape
- * @return string escaped text
- */
- global define string EscapeTags (string text) ``{
- text = mergestring(splitstring(text, "&"), "&");
- text = mergestring(splitstring(text, "<"), "<");
- text = mergestring(splitstring(text, ">"), ">");
-
- return text;
- }
-
- /**
- * Shorthand for select (splitstring (s, separators), 0, "")
- * Useful now that the above produces a deprecation warning.
- * @param s string to be split
- * @param separators characters which delimit components
- * @return first component or ""
- */
- global string FirstChunk (string s, string separators) {
- list <string> l = splitstring (s, separators);
- return l[0]:"";
- }
-
- // character sets, suitable for ValidChars
-
- string cupper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
- string clower = "abcdefghijklmnopqrstuvwxyz";
- string calpha = cupper + clower;
- string cdigit = "0123456789";
- string cxdigit = cdigit + "ABCDEFabcdef";
- string calnum = calpha + cdigit;
- string cpunct = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
- string cgraph = calnum + cpunct;
- string cspace = " \f\r\n\t\013";
- string cprint = cspace + cgraph;
-
- /**
- * @return the 26 uppercase ASCII letters
- */
- global string CUpper () {
- return cupper;
- }
-
- /**
- * @return the 26 lowercase ASCII letters
- */
- global string CLower () {
- return clower;
- }
-
- /**
- * @return the 52 upper and lowercase ASCII letters
- */
- global string CAlpha () {
- return calpha;
- }
-
- /**
- * @return 0123456789
- */
- global string CDigit () {
- return cdigit;
- }
-
- /**
- * @return hexadecimal digits: 0123456789ABCDEFabcdef
- */
- global string CXdigit () {
- return cxdigit;
- }
-
- /**
- * @return the 52 upper and lowercase ASCII letters and digits
- */
- global string CAlnum () {
- return calnum;
- }
-
- /**
- * @return the ASCII printable non-blank non-alphanumeric characters
- */
- global string CPunct () {
- return cpunct;
- }
-
- /**
- * @return printable ASCII charcters except whitespace
- */
- global string CGraph () {
- return cgraph;
- }
-
- /**
- * @return ASCII whitespace
- */
- global string CSpace () {
- return cspace;
- }
-
- /**
- * @return printable ASCII characters including whitespace
- */
- global string CPrint () {
- return cprint;
- }
-
- /**
- * Characters valid in a filename (not pathname).
- * Naturally "/" is disallowed. Otherwise, the graphical ASCII
- * characters are allowed.
- * @return string for ValidChars
- */
- global string ValidCharsFilename () {
- return deletechars (CGraph (), "/");
- }
-
-
- // 64 characters is the base undeline length
- string base_underline = "----------------------------------------------------------------";
-
- /* - hidden for documentation -
- *
- * Local function for finding longest records in the table.
- *
- * @param list <list <string> > table items
- * @return list <integer> longest records by columns
- */
- list <integer> FindLongestRecords (list <list <string> > items) {
- list <integer> longest = [];
-
- // searching all rows
- foreach (list <string> row, items, {
- // starting with column 0
- integer col_counter = 0;
- // testing all columns on the row
- foreach (string col, row, {
- integer col_size = size(col);
- // found longer record for this column
- if (col_size>longest[col_counter]:-1) longest[col_counter] = col_size;
- // next column
- col_counter = col_counter + 1;
- });
- });
-
- return longest;
- }
-
- /* - hidden for documentation -
- *
- * Local function creates table row.
- *
- * @param list <string> row items
- * @param list <integer> columns lengths
- * @param integer record horizontal padding
- * @return string padded table row
- */
- string CreateTableRow (list <string> row_items, list <integer> cols_lenghts, integer horizontal_padding) {
- string row = "";
-
- integer col_counter = 0;
- integer records_count = size(row_items) - 1;
-
- foreach (string record, row_items, {
- integer padding = cols_lenghts[col_counter]:0;
- if (col_counter<records_count) padding = padding + horizontal_padding;
-
- row = row + String::Pad(record, padding);
- col_counter = col_counter + 1;
- });
-
- return row;
- }
-
- /* - hidden for documentation -
- *
- * Local function returns underline string /length/ long.
- *
- * @param integer length of underline
- * @return string /length/ long underline
- */
- string CreateUnderline (integer length) {
- string underline = base_underline;
- while (size(underline)<length) {
- underline = underline + base_underline;
- }
- underline = substring(underline, 0, length);
-
- return underline;
- }
-
- /* - hidden for documentation -
- *
- * Local function for creating header underline for table.
- * It uses maximal lengths of records defined in cols_lenghts.
- *
- * @param list <integer> maximal lengths of records in columns
- * @param integer horizontal padding of records
- * @return string table header underline
- */
- string CreateTableHeaderUnderline (list <integer> cols_lenghts, integer horizontal_padding) {
- integer col_counter = 0;
- // count of added paddings
- integer records_count = size(cols_lenghts) - 1;
- // total length of underline
- integer total_size = 0;
-
- foreach (integer col_size, cols_lenghts, {
- total_size = total_size + col_size;
- // adding padding where necessary
- if (col_counter<records_count) {
- total_size = total_size + horizontal_padding;
- }
- col_counter = col_counter + 1;
- });
-
- return CreateUnderline(total_size);
- }
-
- /**
- * Function creates text table without using HTML tags.
- * (Useful for commandline)
- * Undefined option uses the default one.
- *
- * @param list <string> header
- * @param list <list <string> > items
- * @param map <string, string> options
- * @return string table
- *
- * Header: [ "Id", "Configuration", "Device" ]
- * Items: [ [ "1", "aaa", "Samsung Calex" ], [ "2", "bbb", "Trivial Trinitron" ] ]
- * Possible Options: horizontal_padding (for columns), table_left_padding (for table)
- */
- global define string TextTable (list<string> header, list <list <string> > items, map <string, any> options) {
-
- integer current_horizontal_padding = (integer) options["horizontal_padding"]:2;
- integer current_table_left_padding = (integer) options["table_left_padding"]:4;
-
- list <integer> cols_lenghts = FindLongestRecords(add(items, header));
-
- // whole table is left-padded
- string table_left_padding = String::Pad("", current_table_left_padding);
- // the last row has no newline
- integer rows_count = size(items);
- string table = "";
-
- table = table + table_left_padding
- + CreateTableRow(header, cols_lenghts, current_horizontal_padding) + "\n";
- table = table + table_left_padding
- + CreateTableHeaderUnderline(cols_lenghts, current_horizontal_padding) + "\n";
- integer rows_counter = 1;
- foreach (list <string> row, items, {
- table = table + table_left_padding
- + CreateTableRow(row, cols_lenghts, current_horizontal_padding) +
- (rows_counter<rows_count ? "\n":"");
- rows_counter = rows_counter + 1;
- });
- return table;
- }
-
- /**
- * Function returns underlined text header without using HTML tags.
- * (Useful for commandline)
- *
- * @param string header line
- * @param integer left padding
- * @return string underlined header line
- */
- global define string UnderlinedHeader (string header_line, integer left_padding) {
-
- return
- Pad("", left_padding) + header_line + "\n" +
- Pad("", left_padding) + CreateUnderline(size(header_line));
- }
-
-
-
- //////////////////////////////////////////
- // sysconfig metadata related functions //
- //////////////////////////////////////////
-
- /**
- * Get metadata lines from input string
- * @param input Input string - complete comment of a sysconfig variable
- * @return list<string> Metadata lines in list
- */
- global define list<string> GetMetaDataLines(string input) ``{
- if (input == nil || input == "")
- {
- return [];
- }
-
- list<string> lines = splitstring(input, "\n");
- return (filter(string line, lines, ``(regexpmatch(line, "^##.*"))));
- }
-
- /**
- * Get comment without metadata
- * @param input Input string - complete comment of a sysconfig variable
- * @return string Comment used as variable description
- */
- global define string GetCommentLines(string input) ``{
- if (input == nil || input == "")
- {
- return "";
- }
-
- list<string> lines = splitstring(input, "\n");
-
- string ret = "";
-
- foreach(string line, lines, ``{
- string com_line = regexpsub(line, "^#([^#].*)", "\\1");
-
- if (com_line == nil)
- {
- // add empty lines
- if (regexpmatch(line, "^#[ \t]*$") == true)
- {
- ret = ret + "\n";
- }
- }
- else
- {
- ret = ret + com_line + "\n";
- }
- }
- );
-
- return ret;
- }
-
- /**
- * Parse metadata from a sysconfig comment
- * @param comment comment of a sysconfig variable (single line or multiline string)
- * @return map parsed metadata
- */
- global define map<string, string> ParseSysconfigComment(string comment) ``{
- map<string, string> ret = $[];
-
- // get metadata part of comment
- list<string> metalines = GetMetaDataLines(comment);
- list<string> joined_multilines = [];
- string multiline = "";
-
- y2debug("metadata: %1", metalines);
-
- // join multi line metadata lines
- foreach(string metaline, metalines, ``{
- if (substring(metaline, size(metaline) - 1, 1) != "\\")
- {
- if (multiline != "")
- {
- // this not first multiline so remove comment mark
- string without_comment = regexpsub(metaline, "^##(.*)", "\\1");
-
- if (without_comment != nil)
- {
- metaline = without_comment;
- }
- }
- joined_multilines = add(joined_multilines, multiline + metaline);
- multiline = "";
- }
- else
- {
- string part = substring(metaline, 0, size(metaline) - 1);
-
- if (multiline != "")
- {
- // this not first multiline so remove comment mark
- string without_comment = regexpsub(part, "^##(.*)", "\\1");
-
- if (without_comment != nil)
- {
- part = without_comment;
- }
- }
-
- // add line to the previous lines
- multiline = multiline + part;
- }
- }
- );
-
- y2debug("metadata after multiline joining: %1", joined_multilines);
-
- // parse each metadata line
- foreach(string metaline, joined_multilines, ``{
-
- /* Ignore lines with ### -- general comments */
- if (regexpmatch(metaline, "^###"))
- {
- return;
- }
-
- string meta = regexpsub(metaline, "^##[ \t]*(.*)", "\\1");
-
- // split sting to the tag and value part
- integer colon_pos = findfirstof(meta, ":");
- string tag = "";
- string val = "";
-
- if (colon_pos == nil)
- {
- // colon is missing
- tag = meta;
- }
- else
- {
- tag = substring(meta, 0, colon_pos);
-
- if (size(meta) > colon_pos + 1)
- {
- val = substring(meta, colon_pos + 1);
- }
- }
-
- // remove whitespaces from parts
- tag = CutBlanks(tag);
- val = CutBlanks(val);
-
- y2debug("tag: %1 val: '%2'", tag, val);
-
- // add tag and value to map if they are present in comment
- if (tag != "")
- {
- ret = (map<string, string>) add(ret, tag, val);
- }
- else
- {
- // ignore separator lines
- if (!regexpmatch(metaline, "^#*$"))
- {
- y2warning("Unknown metadata line: %1", metaline);
- }
- }
- }
- );
-
- y2debug("parsed sysconfig comment: %1", ret);
-
- return ret;
- }
-
- /**
- * Replace substring in a string. All substrings source are replaced by string target.
- * @param s input string
- * @param source the string which will be replaced
- * @param target the new string which is used instead of source
- * @return string result
- */
- global string Replace(string s, string source, string target) {
- if (s == nil)
- {
- return nil;
- }
-
- if (source == nil || source == "")
- {
- y2warning("invalid parameter source: %1", source);
- return s;
- }
-
- if (target == nil)
- {
- y2warning("invalid parameter target: %1", target);
- return s;
- }
-
- integer pos = find(s, source);
- while (pos >= 0) {
- string tmp = substring(s, 0, pos) + target;
- if (size(s) > pos + size(source))
- {
- tmp = tmp + substring(s, pos + size(source));
- }
-
- s = tmp;
-
- pos = find(s, source);
- }
-
- return s;
- }
-
- /**
- * Returns text wrapped at defined margin. Very useful for translated strings
- * used for pop-up windows or dialogs where you can't know the width. It
- * controls the maximum width of the string so the text should allways fit into
- * the minimal ncurses window. If you expect some long words, such us URLs or
- * words with a hyphen inside, you can also set the additional split-characters
- * to "/-". Then the function can wrap the word also after these characters.
- * This function description was wrapped using the function String::WrapAt().
- *
- * @example String::WrapAt("Some very long text",30,"/-");
- *
- * @param string text to be wrapped
- * @param integer maximum width of the wrapped text
- * @param string additional split-characters such as "-" or "/"
- * @return string wrapped string
- */
- global string WrapAt(string text, integer width, string split_string) {
- string new_string = "";
- integer avail = width; // characters available in this line
- string lsep = ""; // set to "\n" when at the beginning of a new line
- string wsep = ""; // set to " " after words, unless at the beginning
-
- foreach (string word, splitstring(text, " \n"), {
- while (size(word) > 0) {
- // decide where to split the current word
- integer split_at = 0;
- if (size(word) <= width) {
- split_at = size(word);
- } else {
- split_at = findlastof(substring(word, 0, avail - size(wsep)),
- " \n" + split_string);
- if (split_at != nil) {
- split_at = split_at + 1;
- } else {
- split_at = findlastof(substring(word, 0, width),
- " \n" + split_string);
- if (split_at != nil) {
- split_at = split_at + 1;
- } else {
- split_at = avail - size(wsep);
- }
- }
- }
-
- // decide whether it fits into the same line or must go on
- // a separate line
- if (size(wsep) + split_at > avail) {
- if (size(new_string) > 0)
- new_string = new_string + "\n";
- avail = width;
- wsep = "";
- lsep = "";
- }
-
- // add the next word or partial word
- new_string = new_string + lsep + wsep +
- substring(word, 0, split_at);
- avail = avail - size(wsep) - split_at;
- wsep = ""; lsep = "";
- if (avail == 0) {
- avail = width;
- lsep = "\n";
- } else if (split_at == size(word)) {
- wsep = " ";
- }
- word = substring(word, split_at);
- }
- });
-
- return new_string;
- }
-
- /**
- * Make a random base-36 number.
- * srandom should be called beforehand.
- * @param len string length
- * @return random string of 0-9 and a-z
- */
- global string Random (integer len) {
- if (len <= 0)
- {
- return "";
- }
- string digits = cdigit + clower; // uses the character classes from above
- integer base = size (digits);
- integer max = 1;
- integer i = len;
- while (i > 0)
- {
- max = max * base;
- i = i - 1;
- }
- integer rnum = random (max);
- string ret = "";
- i = len;
- while (i > 0)
- {
- integer digit = rnum % base;
- rnum = rnum / base;
- ret = ret + substring (digits, digit, 1);
- i = i -1;
- }
- return ret;
- }
-
- /* EOF */
- }
-