1
0
Fork 0
fgdata/Nasal/string.nas
mfranz f827653668 - create new Nasal module string.nas with simple shell style pattern matching
algorithm (needed for the file selector and useful for other purposes, like
  assembling lists of livery or screenshot files etc.)
- io.nas: move fixpath to string.nas (it's not only useful for file paths
  but can also be used for property paths)
- screen.nas: move trim to string.nas (used by screen.nas and nasal-console.xml)
- gui.nas: add pattern matching to FileSelctor
- ufo.nas: use patterm matching (only *.ac and *.xml files shall be listed)
2007-06-22 14:13:30 +00:00

173 lines
4.2 KiB
Text

var isupper = func(c) { c >= `A` and c <= `Z` }
var islower = func(c) { c >= `a` and c <= `z` }
var toupper = func(c) { islower(c) ? c + `A` - `a` : c }
var tolower = func(c) { isupper(c) ? c + `a` - `A` : c }
var isletter = func(c) { isupper(c) or islower(c) }
var isdigit = func(c) { c >= `0` and c <= `9` }
var isalnum = func(c) { isletter(c) or isdigit(c) }
var isspace = func(c) { c == ` ` or c == `\t` or c == `\n` or c == `\r` }
##
# trim spaces at the left (lr < 0), at the right (lr > 0), or both (lr = 0)
#
var trim = func(s, lr = 0) {
var l = 0;
if (lr <= 0)
for (; l < size(s); l += 1)
if (!isspace(s[l]))
break;
var r = size(s) - 1;
if (lr >= 0)
for (; r >= 0; r -= 1)
if (!isspace(s[r]))
break;
return r < l ? "" : substr(s, l, r - l + 1);
}
##
# return string converted to lower case letters
#
var lc = func(str) {
var s = "";
for (var i = 0; i < size(str); i += 1)
s ~= chr(tolower(str[i]));
return s;
}
##
# return string converted to upper case letters
#
var uc = func(str) {
var s = "";
for (var i = 0; i < size(str); i += 1)
s ~= chr(toupper(str[i]));
return s;
}
##
# case insensitive string compare and match functions
# (not very effective -- converting the array to be sorted
# first is faster)
#
var icmp = func(a, b) cmp(lc(a), lc(b));
var imatch = func(a, b) match(lc(a), lc(b));
##
# check if string <str> matches shell style pattern <patt>
#
# Rules:
# ? stands for any single character
# * stands for zero or any number of arbitrary characters
# \ escapes the next character and makes it stand for itself; that is:
# \? stands for a question mark (not "any single character")
# [] stands for a group of characters:
# [abc] stands for letters a or b or c
# [^abc] stands for any letter but any of a, b, and c
# [1-4] stands for digits 1 to 4 (1, 2, 3, 4)
# [1-4-] stands for digits 1 to 4 (1, 2, 3, 4), and the minus
# [-1-4] same as above
# [1-3-6] stands for digits 1 to 3, minus, and 6
# [1-3-6-9] stands for digits 1 to 3, minus, and 6 to 9
# [][] stands for the closing bracket and the opening bracket
# [^^] stands for all characters but the caret symbol
#
# Example:
# string.match(file, "*[0-9].xml"); ... true if string ends with digit followed by ".xml"
#
var match = func(str, patt) {
var s = 0;
for (var p = 0; p < size(patt) and s < size(str); ) {
if (patt[p] == `\\`) {
if ((p += 1) >= size(patt))
return !size(str); # pattern ends with backslash
} elsif (patt[p] == `?`) {
s += 1;
p += 1;
continue;
} elsif (patt[p] == `*`) {
for (; p < size(patt); p += 1)
if (patt[p] != `*`)
break;
if (p >= size(patt))
return 1;
for (; s < size(str); s += 1)
if (match(substr(str, s), substr(patt, p)))
return 1;
continue;
} elsif (patt[p] == `[`) {
setsize(var x = [], 256);
var invert = 0;
if ((p += 1) < size(patt) and patt[p] == `^`) {
p += 1;
invert = 1;
}
for (var i = 0; p < size(patt); p += 1) {
if (patt[p] == `]` and i)
break;
x[patt[p]] = 1;
i += 1;
if (p + 2 < patt[p] and patt[p + 1] == `-` and patt[p + 2] != `]`) {
var from = patt[p];
var to = patt[p += 2];
for (var c = from; c <= to; c += 1)
x[c] = 1;
}
}
if (invert ? !!x[str[s]] : !x[str[s]])
return 0;
s += 1;
p += 1;
continue;
}
if (str[s] != patt[p])
return 0;
s += 1;
p += 1;
}
return s == size(str) and p == size(patt);
}
##
# Removes superfluous slashes, emtpy and "." elements, expands
# all ".." elements, and turns all backslashes into slashes.
# The result will start with a slash if it started with a slash
# or backslash, it will end without slash. Should be applied on
# absolute property or file paths, otherwise ".." elements might
# be resolved wrongly.
#
var fixpath = func(path) {
var d = "";
for (var i = 0; i < size(path); i += 1)
d ~= path[i] == `\\` ? "/" : chr(path[i]);
var prefix = d[0] == `/` ? "/" : "";
var stack = [];
foreach (var e; split("/", d)) {
if (e == "." or e == "")
continue;
elsif (e == "..")
pop(stack);
else
append(stack, e);
}
if (!size(stack))
return "/";
path = stack[0];
foreach (var s; subvec(stack, 1))
path ~= "/" ~ s;
return prefix ~ path;
}