1 var iscntrl = func(c) c >= 1 and c <= 31 or c == 127;
2 var isascii = func(c) c >= 0 and c <= 127;
3 var isupper = func(c) c >= `A` and c <= `Z`;
4 var islower = func(c) c >= `a` and c <= `z`;
5 var isdigit = func(c) c >= `0` and c <= `9`;
6 var isblank = func(c) c == ` ` or c == `\t`;
7 var ispunct = func(c) c >= `!` and c <= `/` or c >= `:` and c <= `@`
8 or c >= `[` and c <= `\`` or c >= `{` and c <= `~`;
10 var isxdigit = func(c) isdigit(c) or c >= `a` and c <= `f` or c >= `A` and c <= `F`;
11 var isspace = func(c) c == ` ` or c >= `\t` and c <= `\r`;
12 var isalpha = func(c) isupper(c) or islower(c);
13 var isalnum = func(c) isalpha(c) or isdigit(c);
14 var isgraph = func(c) isalnum(c) or ispunct(c);
15 var isprint = func(c) isgraph(c) or c == ` `;
17 var toupper = func(c) islower(c) ? c + `A` - `a` : c;
18 var tolower = func(c) isupper(c) ? c + `a` - `A` : c;
20 var isxspace = func(c) isspace(c) or c == `\n`;
24 # trim spaces at the left (lr < 0), at the right (lr > 0), or both (lr = 0)
25 # An optional function argument defines which characters should be trimmed:
27 # string.trim(a); # trim spaces
28 # string.trim(a, 1, string.isdigit); # trim digits at the right
29 # string.trim(a, 0, func(c) c == `\\` or c == `/`); # trim slashes/backslashes
31 var trim = func(s, lr = 0, istrim = nil) {
36 for (; l < size(s); l += 1)
41 for (; r >= 0; r -= 1)
44 return r < l ? "" : substr(s, l, r - l + 1);
49 # return string converted to lower case letters
53 for (var i = 0; i < size(str); i += 1)
54 s ~= chr(tolower(str[i]));
60 # return string converted to upper case letters
64 for (var i = 0; i < size(str); i += 1)
65 s ~= chr(toupper(str[i]));
71 # case insensitive string compare and match functions
72 # (not very efficient -- converting the array to be sorted
75 var icmp = func(a, b) cmp(lc(a), lc(b));
76 var imatch = func(a, b) match(lc(a), lc(b));
82 # Functions that are used in the IO security code (io.nas) are defined in a
83 # closure that holds safe copies of system functions. Later manipulation of
84 # append(), pop() etc. doesn't affect them. Of course, any security code
85 # must itself store safe copies of these tamper-proof functions before user
86 # code can redefine them, and the closure() command must be made inaccessible.
98 var setsize = setsize;
106 # check if string <str> matches shell style pattern <patt>
109 # ? stands for any single character
110 # * stands for any number (including zero) of arbitrary characters
111 # \ escapes the next character and makes it stand for itself; that is:
112 # \? stands for a question mark (not the "any single character" placeholder)
113 # [] stands for a group of characters:
114 # [abc] stands for letters a, b or c
115 # [^abc] stands for any character but a, b, and c (^ as first character -> inversion)
116 # [1-4] stands for digits 1 to 4 (1, 2, 3, 4)
117 # [1-4-] stands for digits 1 to 4, and the minus
118 # [-1-4] same as above
119 # [1-3-6] stands for digits 1 to 3, minus, and 6
120 # [1-3-6-9] stands for digits 1 to 3, minus, and 6 to 9
121 # [][] stands for the closing and the opening bracket (']' must be first!)
122 # [^^] stands for all characters but the caret symbol
123 # [\/] stands for a backslash or a slash (the backslash isn't an
124 # escape character in a [] character group)
126 # Note that a minus can't be a range delimiter, as in [a--e],
127 # which would be interpreted as any of a, e, or minus.
130 # string.match(name, "*[0-9].xml"); ... true if 'name' ends with digit followed by ".xml"
132 match = func(str, patt) {
134 for (var p = 0; p < size(patt) and s < size(str); ) {
135 if (patt[p] == `\\`) {
136 if ((p += 1) >= size(patt))
137 return 0; # pattern ends with backslash
139 } elsif (patt[p] == `?`) {
144 } elsif (patt[p] == `*`) {
145 for (; p < size(patt); p += 1)
151 for (; s < size(str); s += 1)
152 if (caller(0)[1](substr(str, s), substr(patt, p)))
156 } elsif (patt[p] == `[`) {
157 setsize(var x = [], 256);
159 if ((p += 1) < size(patt) and patt[p] == `^`) {
163 for (var i = 0; p < size(patt); p += 1) {
164 if (patt[p] == `]` and i)
169 if (p + 2 < patt[p] and patt[p] != `-` and patt[p + 1] == `-`
170 and patt[p + 2] != `]` and patt[p + 2] != `-`) {
172 var to = patt[p += 2];
173 for (var c = from; c <= to; c += 1)
177 if (invert ? !!x[str[s]] : !x[str[s]])
184 if (str[s] != patt[p])
189 return s == size(str) and p == size(patt);
194 # Removes superfluous slashes, empty and "." elements,
195 # expands all ".." elements keeping relative paths,
196 # and turns all backslashes into slashes.
197 # The result will start with a slash if it started with a slash or backslash,
198 # it will end without slash.
200 normpath = func(path) {
201 path = replace(path, "\\", "/");
202 var prefix = size(path) and path[0] == `/` ? "/" : "";
207 foreach (var e; split("/", path)) {
208 if (e == "." or e == "")
210 elsif (e == ".." and !relative)
217 return size(stack) ? prefix ~ join("/", stack) : "/";
222 # Join all elements of a list inserting a separator between every two of them.
224 join = func(sep, list) {
228 foreach (var s; subvec(list, 1))
235 # Replace all occurrences of 'old' by 'new'.
237 replace = func(str, old, new) {
238 return join(new, split(old, str));
241 })(); # end tamper-proof environment
245 # Get a function out of a string template for fast insertion of template
246 # parameters. This allows to use the same templates as with most available tile
247 # mapping engines (eg. Leaflet, Polymaps). Return a callable function object on
248 # success, and nil if parsing the templated fails.
250 # Example (Build MapQuest tile url):
252 # var makeUrl = string.compileTemplate(
253 # "http://otile1.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.jpg"
255 # print( makeUrl({x: 5, y: 4, z: 3}) );
259 # http://otile1.mqcdn.com/tiles/1.0.0/map/3/5/4.jpg
261 var compileTemplate = func(template)
263 # See http://james.padolsey.com/javascript/straight-up-interpolation/
267 while( (start = template.find('{', end)) >= 0 )
271 code ~= '"' ~ substr(template, end, start - end) ~ '"';
272 if( (end = template.find('}', start)) < 0 )
274 debug.warn("string.compileTemplate: unclosed '{' (" ~ template ~ ")");
278 code ~= '~arg[0]["' ~ substr(template, start + 1, end - start - 1) ~ '"]';
282 code ~= '~"' ~ substr(template, end, size(template) - end) ~ '"';
285 return compile(code);
290 # Simple scanf function. Takes an input string, a pattern, and a
291 # vector. It returns 0 if the format didn't match, and appends
292 # all found elements to the given vector. Return values:
294 # -1 string matched format ending with % (i.e. more chars than format cared about)
295 # 0 string didn't match format
296 # 1 string matched, but would still match if the right chars were added
297 # 2 string matched, and would not if any character would be added
299 # var r = string.scanf("comm3freq123.456", "comm%ufreq%f", var result = []);
301 # The result vector will be set to [3, 123.456].
304 new : func(s) {{ str: s, pos: 0, parents: [Scan] }},
306 if (me.pos >= size(me.str))
308 var c = me.str[me.pos];
312 ungetc : func { me.pos -= 1 },
313 rest : func { substr(me.str, me.pos) },
317 var scanf = func(test, format, result) {
318 if (find("%", format) < 0)
319 return cmp(test, format) ? 0 : 2;
322 var str = Scan.new(test);
323 var format = Scan.new(format);
326 var f = format.getc();
331 success = 1; # unsafe match
334 return -1; # format ended with %
336 if (str.getc() != `%`)
344 while ((f = format.getc()) != nil and isdigit(f))
345 fnum = fnum * 10 + f - `0`;
347 var fnum = -2; # because we add one if !prefix
353 if (f == `d` or f == `f` or f == `u`) {
362 (prefix, sign) = (1, -1);
369 while ((var c = str.getc()) != nil and (fnum -= 1)) {
370 if (f != `f` and c == `.`)
372 elsif (num(scanstr ~ chr(c) ~ '0') != nil) # append 0 to digest e/E
379 if (num(scanstr) == nil)
381 if (!size(scanstr) and prefix)
383 append(result, sign * num(scanstr));
387 while ((var c = str.getc()) != nil and c != ` ` and (fnum -= 1))
395 append(result, scanstr);
398 die("scanf: bad format element %" ~ chr(f));
402 } elsif (isspace(f)) {
403 while ((var c = str.getc()) != nil and isspace(c))
408 } elsif (f != (var c = str.getc())) {
412 success = 2; # safe match
415 return str.getc() == nil and format.getc() == nil ? success : 0;
420 # ANSI colors (see $ man console_codes)
422 var setcolors = func(enabled) {
423 if (enabled and getprop("/sim/startup/stderr-to-terminal"))
424 color = func(color, s) { "\x1b[" ~ color ~ "m" ~ s ~ "\x1b[m" }
426 color = func(dummy, s) { s }
431 # Add ANSI color codes to string, if terminal-ansi-colors are enabled and
432 # stderr prints to a terminal. Example:
434 # print(string.color("31;1", "this is red"));
436 var color = func nil;
437 setcolors(getprop("/sim/startup/terminal-ansi-colors"));
438 _setlistener("/sim/signals/nasal-dir-initialized", func {
439 setlistener("/sim/startup/terminal-ansi-colors", func(n) setcolors(n.getBoolValue()));