2008-07-03 16:24:52 +00:00
|
|
|
var iscntrl = func(c) c >= 1 and c <= 31 or c == 127;
|
|
|
|
var isascii = func(c) c >= 0 and c <= 127;
|
|
|
|
var isupper = func(c) c >= `A` and c <= `Z`;
|
|
|
|
var islower = func(c) c >= `a` and c <= `z`;
|
|
|
|
var isdigit = func(c) c >= `0` and c <= `9`;
|
|
|
|
var isblank = func(c) c == ` ` or c == `\t`;
|
|
|
|
var ispunct = func(c) c >= `!` and c <= `/` or c >= `:` and c <= `@`
|
|
|
|
or c >= `[` and c <= `\`` or c >= `{` and c <= `~`;
|
2007-11-16 17:16:42 +00:00
|
|
|
|
2008-07-03 16:24:52 +00:00
|
|
|
var isxdigit = func(c) isdigit(c) or c >= `a` and c <= `f` or c >= `A` and c <= `F`;
|
|
|
|
var isspace = func(c) c == ` ` or c >= `\t` and c <= `\r`;
|
|
|
|
var isalpha = func(c) isupper(c) or islower(c);
|
|
|
|
var isalnum = func(c) isalpha(c) or isdigit(c);
|
|
|
|
var isgraph = func(c) isalnum(c) or ispunct(c);
|
|
|
|
var isprint = func(c) isgraph(c) or c == ` `;
|
2007-11-16 17:16:42 +00:00
|
|
|
|
2008-07-03 16:24:52 +00:00
|
|
|
var toupper = func(c) islower(c) ? c + `A` - `a` : c;
|
|
|
|
var tolower = func(c) isupper(c) ? c + `a` - `A` : c;
|
2007-06-22 14:13:30 +00:00
|
|
|
|
2009-01-27 19:27:21 +00:00
|
|
|
var isxspace = func(c) isspace(c) or c == `\n`;
|
|
|
|
|
2007-06-22 14:13:30 +00:00
|
|
|
|
|
|
|
##
|
|
|
|
# trim spaces at the left (lr < 0), at the right (lr > 0), or both (lr = 0)
|
2009-01-27 01:42:46 +00:00
|
|
|
# An optional function argument defines which characters should be trimmed:
|
2007-06-22 14:13:30 +00:00
|
|
|
#
|
2009-01-27 19:27:21 +00:00
|
|
|
# string.trim(a); # trim spaces
|
|
|
|
# string.trim(a, 1, string.isdigit); # trim digits at the right
|
|
|
|
# string.trim(a, 0, func(c) c == `\\` or c == `/`); # trim slashes/backslashes
|
2009-01-27 01:42:46 +00:00
|
|
|
#
|
|
|
|
var trim = func(s, lr = 0, istrim = nil) {
|
|
|
|
if (istrim == nil)
|
|
|
|
istrim = isspace;
|
2007-06-22 14:13:30 +00:00
|
|
|
var l = 0;
|
|
|
|
if (lr <= 0)
|
|
|
|
for (; l < size(s); l += 1)
|
2009-01-27 01:42:46 +00:00
|
|
|
if (!istrim(s[l]))
|
2007-06-22 14:13:30 +00:00
|
|
|
break;
|
|
|
|
var r = size(s) - 1;
|
|
|
|
if (lr >= 0)
|
|
|
|
for (; r >= 0; r -= 1)
|
2009-01-27 01:42:46 +00:00
|
|
|
if (!istrim(s[r]))
|
2007-06-22 14:13:30 +00:00
|
|
|
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
|
2008-06-08 11:14:57 +00:00
|
|
|
# (not very efficient -- converting the array to be sorted
|
2007-06-22 14:13:30 +00:00
|
|
|
# first is faster)
|
|
|
|
#
|
|
|
|
var icmp = func(a, b) cmp(lc(a), lc(b));
|
|
|
|
var imatch = func(a, b) match(lc(a), lc(b));
|
|
|
|
|
2010-06-27 22:23:56 +00:00
|
|
|
|
2008-07-22 21:22:59 +00:00
|
|
|
|
|
|
|
|
|
|
|
##
|
|
|
|
# Functions that are used in the IO security code (io.nas) are defined in a
|
|
|
|
# closure that holds safe copies of system functions. Later manipulation of
|
|
|
|
# append(), pop() etc. doesn't affect them. Of course, any security code
|
|
|
|
# must itself store safe copies of these tamper-proof functions before user
|
|
|
|
# code can redefine them, and the closure() command must be made inaccessible.
|
|
|
|
##
|
|
|
|
|
|
|
|
var match = nil;
|
2008-10-15 16:35:11 +00:00
|
|
|
var normpath = nil;
|
2008-07-22 21:22:59 +00:00
|
|
|
var join = nil;
|
|
|
|
var replace = nil;
|
|
|
|
|
|
|
|
(func {
|
|
|
|
var append = append;
|
|
|
|
var caller = caller;
|
|
|
|
var pop = pop;
|
|
|
|
var setsize = setsize;
|
|
|
|
var size = size;
|
|
|
|
var split = split;
|
|
|
|
var substr = substr;
|
|
|
|
var subvec = subvec;
|
|
|
|
|
|
|
|
|
2007-06-22 14:13:30 +00:00
|
|
|
##
|
|
|
|
# check if string <str> matches shell style pattern <patt>
|
|
|
|
#
|
|
|
|
# Rules:
|
|
|
|
# ? stands for any single character
|
2007-06-22 18:49:38 +00:00
|
|
|
# * stands for any number (including zero) of arbitrary characters
|
2007-06-22 14:13:30 +00:00
|
|
|
# \ escapes the next character and makes it stand for itself; that is:
|
2007-06-22 20:41:00 +00:00
|
|
|
# \? stands for a question mark (not the "any single character" placeholder)
|
2007-06-22 14:13:30 +00:00
|
|
|
# [] stands for a group of characters:
|
2007-06-22 20:41:00 +00:00
|
|
|
# [abc] stands for letters a, b or c
|
2007-10-07 15:12:05 +00:00
|
|
|
# [^abc] stands for any character but a, b, and c (^ as first character -> inversion)
|
2007-06-22 14:13:30 +00:00
|
|
|
# [1-4] stands for digits 1 to 4 (1, 2, 3, 4)
|
2007-06-22 17:52:37 +00:00
|
|
|
# [1-4-] stands for digits 1 to 4, and the minus
|
2007-06-22 14:13:30 +00:00
|
|
|
# [-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
|
2007-06-22 18:49:38 +00:00
|
|
|
# [][] stands for the closing and the opening bracket (']' must be first!)
|
2007-06-22 14:13:30 +00:00
|
|
|
# [^^] stands for all characters but the caret symbol
|
2007-10-07 15:12:05 +00:00
|
|
|
# [\/] stands for a backslash or a slash (the backslash isn't an
|
|
|
|
# escape character in a [] character group)
|
2007-06-22 14:13:30 +00:00
|
|
|
#
|
2007-10-07 15:12:05 +00:00
|
|
|
# Note that a minus can't be a range delimiter, as in [a--e],
|
|
|
|
# which would be interpreted as any of a, e, or minus.
|
2007-06-22 20:41:00 +00:00
|
|
|
#
|
2007-06-22 14:13:30 +00:00
|
|
|
# Example:
|
2007-06-22 17:52:37 +00:00
|
|
|
# string.match(name, "*[0-9].xml"); ... true if 'name' ends with digit followed by ".xml"
|
2007-06-22 14:13:30 +00:00
|
|
|
#
|
2008-07-22 21:22:59 +00:00
|
|
|
match = func(str, patt) {
|
2007-06-22 14:13:30 +00:00
|
|
|
var s = 0;
|
|
|
|
for (var p = 0; p < size(patt) and s < size(str); ) {
|
|
|
|
if (patt[p] == `\\`) {
|
|
|
|
if ((p += 1) >= size(patt))
|
2007-06-22 17:52:37 +00:00
|
|
|
return 0; # pattern ends with backslash
|
2007-06-22 14:13:30 +00:00
|
|
|
|
|
|
|
} 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)
|
2008-07-23 00:41:18 +00:00
|
|
|
if (caller(0)[1](substr(str, s), substr(patt, p)))
|
2007-06-22 14:13:30 +00:00
|
|
|
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;
|
|
|
|
|
2007-06-22 20:41:00 +00:00
|
|
|
if (p + 2 < patt[p] and patt[p] != `-` and patt[p + 1] == `-`
|
|
|
|
and patt[p + 2] != `]` and patt[p + 2] != `-`) {
|
2007-06-22 14:13:30 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-06-13 13:01:38 +00:00
|
|
|
##
|
|
|
|
# Removes superfluous slashes, empty and "." elements, expands
|
|
|
|
# all ".." elements, and turns all backslashes into slashes.
|
|
|
|
# The result will start with a slash if it started with a slash
|
2008-07-27 22:00:58 +00:00
|
|
|
# or backslash, it will end without slash. Should be applied to
|
2008-06-13 13:01:38 +00:00
|
|
|
# absolute property or file paths, otherwise ".." elements might
|
|
|
|
# be resolved wrongly.
|
|
|
|
#
|
2008-10-15 16:35:11 +00:00
|
|
|
normpath = func(path) {
|
2008-06-22 11:33:13 +00:00
|
|
|
path = replace(path, "\\", "/");
|
2008-07-11 15:23:23 +00:00
|
|
|
var prefix = size(path) and path[0] == `/` ? "/" : "";
|
2008-06-13 13:01:38 +00:00
|
|
|
var stack = [];
|
2008-06-22 11:33:13 +00:00
|
|
|
|
|
|
|
foreach (var e; split("/", path)) {
|
2008-06-13 13:01:38 +00:00
|
|
|
if (e == "." or e == "")
|
|
|
|
continue;
|
|
|
|
elsif (e == "..")
|
|
|
|
pop(stack);
|
|
|
|
else
|
|
|
|
append(stack, e);
|
|
|
|
}
|
2008-06-22 11:33:13 +00:00
|
|
|
return size(stack) ? prefix ~ join("/", stack) : "/";
|
2008-06-13 13:01:38 +00:00
|
|
|
}
|
|
|
|
|
2008-06-22 11:33:13 +00:00
|
|
|
|
2008-07-22 21:22:59 +00:00
|
|
|
##
|
|
|
|
# Join all elements of a list inserting a separator between every two of them.
|
|
|
|
#
|
|
|
|
join = func(sep, list) {
|
|
|
|
if (!size(list))
|
|
|
|
return "";
|
|
|
|
var str = list[0];
|
|
|
|
foreach (var s; subvec(list, 1))
|
|
|
|
str ~= sep ~ s;
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
##
|
|
|
|
# Replace all occurrences of 'old' by 'new'.
|
|
|
|
#
|
|
|
|
replace = func(str, old, new) {
|
|
|
|
return join(new, split(old, str));
|
|
|
|
}
|
|
|
|
|
|
|
|
})(); # end tamper-proof environment
|
2008-09-26 12:04:57 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
##
|
2008-09-29 10:06:16 +00:00
|
|
|
# Simple scanf function. Takes an input string, a pattern, and a
|
|
|
|
# vector. It returns 0 if the format didn't match, and appends
|
2008-10-03 20:46:08 +00:00
|
|
|
# all found elements to the given vector. Return values:
|
|
|
|
#
|
|
|
|
# -1 string matched format ending with % (i.e. more chars than format cared about)
|
|
|
|
# 0 string didn't match format
|
|
|
|
# 1 string matched, but would still match if the right chars were added
|
|
|
|
# 2 string matched, and would not if any character would be added
|
2008-09-26 12:04:57 +00:00
|
|
|
#
|
2008-09-29 10:06:16 +00:00
|
|
|
# var r = string.scanf("comm3freq123.456", "comm%ufreq%f", var result = []);
|
2008-09-26 12:04:57 +00:00
|
|
|
#
|
2008-09-29 10:06:16 +00:00
|
|
|
# The result vector will be set to [3, 123.456].
|
2008-09-26 12:04:57 +00:00
|
|
|
#
|
|
|
|
var Scan = {
|
|
|
|
new : func(s) {{ str: s, pos: 0, parents: [Scan] }},
|
|
|
|
getc : func {
|
|
|
|
if (me.pos >= size(me.str))
|
|
|
|
return nil;
|
|
|
|
var c = me.str[me.pos];
|
|
|
|
me.pos += 1;
|
|
|
|
return c;
|
|
|
|
},
|
|
|
|
ungetc : func { me.pos -= 1 },
|
|
|
|
rest : func { substr(me.str, me.pos) },
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2008-09-29 10:06:16 +00:00
|
|
|
var scanf = func(test, format, result) {
|
2008-09-26 12:04:57 +00:00
|
|
|
if (find("%", format) < 0)
|
2008-09-29 10:06:16 +00:00
|
|
|
return cmp(test, format) ? 0 : 2;
|
2008-09-26 12:04:57 +00:00
|
|
|
|
2008-09-29 10:06:16 +00:00
|
|
|
var success = 0;
|
|
|
|
var str = Scan.new(test);
|
2008-09-26 12:04:57 +00:00
|
|
|
var format = Scan.new(format);
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
var f = format.getc();
|
|
|
|
if (f == nil) {
|
|
|
|
break;
|
|
|
|
|
|
|
|
} elsif (f == `%`) {
|
2008-09-29 10:06:16 +00:00
|
|
|
success = 1; # unsafe match
|
2008-09-26 12:04:57 +00:00
|
|
|
f = format.getc();
|
|
|
|
if (f == nil)
|
2008-10-03 20:46:08 +00:00
|
|
|
return -1; # format ended with %
|
2008-10-03 09:47:40 +00:00
|
|
|
if (f == `%`) {
|
|
|
|
if (str.getc() != `%`)
|
|
|
|
return 0;
|
|
|
|
success = 2;
|
|
|
|
continue;
|
|
|
|
}
|
2008-09-26 12:04:57 +00:00
|
|
|
|
|
|
|
if (isdigit(f)) {
|
2008-09-28 20:23:14 +00:00
|
|
|
var fnum = f - `0`;
|
2008-09-26 12:04:57 +00:00
|
|
|
while ((f = format.getc()) != nil and isdigit(f))
|
|
|
|
fnum = fnum * 10 + f - `0`;
|
2008-09-28 20:23:14 +00:00
|
|
|
} else {
|
|
|
|
var fnum = -2; # because we add one if !prefix
|
2008-09-26 12:04:57 +00:00
|
|
|
}
|
|
|
|
|
2008-10-01 18:36:09 +00:00
|
|
|
var scanstr = "";
|
2008-09-28 20:23:14 +00:00
|
|
|
var prefix = 0;
|
|
|
|
var sign = 1;
|
|
|
|
if (f == `d` or f == `f` or f == `u`) {
|
2008-09-28 16:20:49 +00:00
|
|
|
var c = str.getc();
|
2008-10-02 00:58:57 +00:00
|
|
|
if (c == nil) {
|
|
|
|
return 0;
|
|
|
|
} elsif (c == `+`) {
|
2008-09-28 20:23:14 +00:00
|
|
|
prefix = 1;
|
|
|
|
} elsif (c == `-`) {
|
|
|
|
if (f == `u`)
|
2008-09-29 10:06:16 +00:00
|
|
|
return 0;
|
2008-09-28 20:23:14 +00:00
|
|
|
(prefix, sign) = (1, -1);
|
|
|
|
} else {
|
2008-09-26 12:04:57 +00:00
|
|
|
str.ungetc();
|
2008-09-28 20:23:14 +00:00
|
|
|
}
|
|
|
|
if (!prefix)
|
|
|
|
fnum += 1;
|
|
|
|
|
|
|
|
while ((var c = str.getc()) != nil and (fnum -= 1)) {
|
|
|
|
if (f != `f` and c == `.`)
|
|
|
|
break;
|
2008-10-01 18:36:09 +00:00
|
|
|
elsif (num(scanstr ~ chr(c) ~ '0') != nil) # append 0 to digest e/E
|
|
|
|
scanstr ~= chr(c);
|
2008-09-26 12:04:57 +00:00
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (c != nil)
|
|
|
|
str.ungetc();
|
2008-10-01 18:36:09 +00:00
|
|
|
if (num(scanstr) == nil)
|
2008-09-29 10:06:16 +00:00
|
|
|
return 0;
|
2008-10-01 18:36:09 +00:00
|
|
|
if (!size(scanstr) and prefix)
|
|
|
|
return 0;
|
|
|
|
append(result, sign * num(scanstr));
|
2008-10-01 19:51:44 +00:00
|
|
|
|
2008-10-01 18:36:09 +00:00
|
|
|
} elsif (f == `s`) {
|
2008-10-01 19:51:44 +00:00
|
|
|
fnum += 1;
|
|
|
|
while ((var c = str.getc()) != nil and c != ` ` and (fnum -= 1))
|
2008-10-01 18:36:09 +00:00
|
|
|
scanstr ~= chr(c);
|
2008-10-01 19:51:44 +00:00
|
|
|
|
2008-10-01 18:36:09 +00:00
|
|
|
if (c != nil)
|
|
|
|
str.ungetc();
|
|
|
|
if (!size(scanstr))
|
|
|
|
return 0;
|
2008-10-01 19:51:44 +00:00
|
|
|
|
2008-10-01 18:36:09 +00:00
|
|
|
append(result, scanstr);
|
2008-10-01 19:51:44 +00:00
|
|
|
|
2008-09-28 20:23:14 +00:00
|
|
|
} else {
|
2008-09-29 10:06:16 +00:00
|
|
|
die("scanf: bad format element %" ~ chr(f));
|
2008-09-26 12:04:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-09-29 10:06:16 +00:00
|
|
|
} elsif (isspace(f)) {
|
|
|
|
while ((var c = str.getc()) != nil and isspace(c))
|
|
|
|
nil;
|
|
|
|
if (c != nil)
|
|
|
|
str.ungetc();
|
|
|
|
|
2008-09-26 12:04:57 +00:00
|
|
|
} elsif (f != (var c = str.getc())) {
|
2008-09-29 10:06:16 +00:00
|
|
|
return 0;
|
|
|
|
|
2008-09-26 12:04:57 +00:00
|
|
|
} else {
|
2008-09-29 10:06:16 +00:00
|
|
|
sucess = 2; # safe match
|
2008-09-26 12:04:57 +00:00
|
|
|
}
|
|
|
|
}
|
2008-09-29 10:06:16 +00:00
|
|
|
return str.getc() == nil and format.getc() == nil ? success : 0;
|
2008-09-26 12:04:57 +00:00
|
|
|
}
|
2008-10-01 19:51:44 +00:00
|
|
|
|
|
|
|
|
2008-10-03 15:20:12 +00:00
|
|
|
##
|
|
|
|
# ANSI colors (see $ man console_codes)
|
|
|
|
#
|
|
|
|
var setcolors = func(enabled) {
|
|
|
|
if (enabled and getprop("/sim/startup/stderr-to-terminal"))
|
|
|
|
color = func(color, s) { "\x1b[" ~ color ~ "m" ~ s ~ "\x1b[m" }
|
|
|
|
else
|
|
|
|
color = func(dummy, s) { s }
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
##
|
|
|
|
# Add ANSI color codes to string, if terminal-ansi-colors are enabled and
|
|
|
|
# stderr prints to a terminal. Example:
|
|
|
|
#
|
2008-10-03 20:46:08 +00:00
|
|
|
# print(string.color("31;1", "this is red"));
|
2008-10-03 15:20:12 +00:00
|
|
|
#
|
|
|
|
var color = func nil;
|
|
|
|
setcolors(getprop("/sim/startup/terminal-ansi-colors"));
|
2008-10-06 20:22:28 +00:00
|
|
|
_setlistener("/sim/signals/nasal-dir-initialized", func {
|
|
|
|
setlistener("/sim/startup/terminal-ansi-colors", func(n) setcolors(n.getBoolValue()));
|
|
|
|
});
|
2008-10-03 15:20:12 +00:00
|
|
|
|
|
|
|
|