libs/core: improve luci.util.imatch() to not create temporary strings when iterating...
[project/luci.git] / libs / core / luasrc / util.lua
1 --[[
2 LuCI - Utility library
3
4 Description:
5 Several common useful Lua functions
6
7 FileId:
8 $Id$
9
10 License:
11 Copyright 2008 Steven Barth <steven@midlink.org>
12
13 Licensed under the Apache License, Version 2.0 (the "License");
14 you may not use this file except in compliance with the License.
15 You may obtain a copy of the License at
16
17         http://www.apache.org/licenses/LICENSE-2.0
18
19 Unless required by applicable law or agreed to in writing, software
20 distributed under the License is distributed on an "AS IS" BASIS,
21 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 See the License for the specific language governing permissions and
23 limitations under the License.
24
25 ]]--
26
27 local io = require "io"
28 local math = require "math"
29 local table = require "table"
30 local debug = require "debug"
31 local ldebug = require "luci.debug"
32 local string = require "string"
33 local coroutine = require "coroutine"
34 local tparser = require "luci.template.parser"
35
36 local getmetatable, setmetatable = getmetatable, setmetatable
37 local rawget, rawset, unpack = rawget, rawset, unpack
38 local tostring, type, assert = tostring, type, assert
39 local ipairs, pairs, next, loadstring = ipairs, pairs, next, loadstring
40 local require, pcall, xpcall = require, pcall, xpcall
41 local collectgarbage, get_memory_limit = collectgarbage, get_memory_limit
42
43 --- LuCI utility functions.
44 module "luci.util"
45
46 --
47 -- Pythonic string formatting extension
48 --
49 getmetatable("").__mod = function(a, b)
50         if not b then
51                 return a
52         elseif type(b) == "table" then
53                 for k, _ in pairs(b) do if type(b[k]) == "userdata" then b[k] = tostring(b[k]) end end
54                 return a:format(unpack(b))
55         else
56                 if type(b) == "userdata" then b = tostring(b) end
57                 return a:format(b)
58         end
59 end
60
61
62 --
63 -- Class helper routines
64 --
65
66 -- Instantiates a class
67 local function _instantiate(class, ...)
68         local inst = setmetatable({}, {__index = class})
69
70         if inst.__init__ then
71                 inst:__init__(...)
72         end
73
74         return inst
75 end
76
77 --- Create a Class object (Python-style object model).
78 -- The class object can be instantiated by calling itself.
79 -- Any class functions or shared parameters can be attached to this object.
80 -- Attaching a table to the class object makes this table shared between
81 -- all instances of this class. For object parameters use the __init__ function.
82 -- Classes can inherit member functions and values from a base class.
83 -- Class can be instantiated by calling them. All parameters will be passed
84 -- to the __init__ function of this class - if such a function exists.
85 -- The __init__ function must be used to set any object parameters that are not shared
86 -- with other objects of this class. Any return values will be ignored.
87 -- @param base  The base class to inherit from (optional)
88 -- @return              A class object
89 -- @see                 instanceof
90 -- @see                 clone
91 function class(base)
92         return setmetatable({}, {
93                 __call  = _instantiate,
94                 __index = base
95         })
96 end
97
98 --- Test whether the given object is an instance of the given class.
99 -- @param object        Object instance
100 -- @param class         Class object to test against
101 -- @return                      Boolean indicating whether the object is an instance
102 -- @see                         class
103 -- @see                         clone
104 function instanceof(object, class)
105         local meta = getmetatable(object)
106         while meta and meta.__index do
107                 if meta.__index == class then
108                         return true
109                 end
110                 meta = getmetatable(meta.__index)
111         end
112         return false
113 end
114
115
116 --
117 -- Scope manipulation routines
118 --
119
120 local tl_meta = {
121         __mode = "k",
122
123         __index = function(self, key)
124                 local t = rawget(self, coxpt[coroutine.running()]
125                  or coroutine.running() or 0)
126                 return t and t[key]
127         end,
128
129         __newindex = function(self, key, value)
130                 local c = coxpt[coroutine.running()] or coroutine.running() or 0
131                 if not rawget(self, c) then
132                         rawset(self, c, { [key] = value })
133                 else
134                         rawget(self, c)[key] = value
135                 end
136         end
137 }
138
139 --- Create a new or get an already existing thread local store associated with
140 -- the current active coroutine. A thread local store is private a table object
141 -- whose values can't be accessed from outside of the running coroutine.
142 -- @return      Table value representing the corresponding thread local store
143 function threadlocal(tbl)
144         return setmetatable(tbl or {}, tl_meta)
145 end
146
147
148 --
149 -- Debugging routines
150 --
151
152 --- Write given object to stderr.
153 -- @param obj   Value to write to stderr
154 -- @return              Boolean indicating whether the write operation was successful
155 function perror(obj)
156         return io.stderr:write(tostring(obj) .. "\n")
157 end
158
159 --- Recursively dumps a table to stdout, useful for testing and debugging.
160 -- @param t     Table value to dump
161 -- @param maxdepth      Maximum depth
162 -- @return      Always nil
163 function dumptable(t, maxdepth, i, seen)
164         i = i or 0
165         seen = seen or setmetatable({}, {__mode="k"})
166
167         for k,v in pairs(t) do
168                 perror(string.rep("\t", i) .. tostring(k) .. "\t" .. tostring(v))
169                 if type(v) == "table" and (not maxdepth or i < maxdepth) then
170                         if not seen[v] then
171                                 seen[v] = true
172                                 dumptable(v, maxdepth, i+1, seen)
173                         else
174                                 perror(string.rep("\t", i) .. "*** RECURSION ***")
175                         end
176                 end
177         end
178 end
179
180
181 --
182 -- String and data manipulation routines
183 --
184
185 --- Escapes all occurrences of the given character in given string.
186 -- @param s     String value containing unescaped characters
187 -- @param c     String value with character to escape (optional, defaults to "\")
188 -- @return      String value with each occurrence of character escaped with "\"
189 function escape(s, c)
190         c = c or "\\"
191         return s:gsub(c, "\\" .. c)
192 end
193
194 --- Create valid XML PCDATA from given string.
195 -- @param value String value containing the data to escape
196 -- @return              String value containing the escaped data
197 function pcdata(value)
198         return value and tparser.sanitize_pcdata(tostring(value))
199 end
200
201 --- Strip HTML tags from given string.
202 -- @param value String containing the HTML text
203 -- @return      String with HTML tags stripped of
204 function striptags(s)
205         return pcdata(tostring(s):gsub("</?[A-Za-z][A-Za-z0-9:_%-]*[^>]*>", " "):gsub("%s+", " "))
206 end
207
208 --- Splits given string on a defined separator sequence and return a table
209 -- containing the resulting substrings. The optional max parameter specifies
210 -- the number of bytes to process, regardless of the actual length of the given
211 -- string. The optional last parameter, regex, specifies whether the separator
212 -- sequence is interpreted as regular expression.
213 -- @param str           String value containing the data to split up
214 -- @param pat           String with separator pattern (optional, defaults to "\n")
215 -- @param max           Maximum times to split (optional)
216 -- @param regex         Boolean indicating whether to interpret the separator
217 --                                      pattern as regular expression (optional, default is false)
218 -- @return                      Table containing the resulting substrings
219 function split(str, pat, max, regex)
220         pat = pat or "\n"
221         max = max or #str
222
223         local t = {}
224         local c = 1
225
226         if #str == 0 then
227                 return {""}
228         end
229
230         if #pat == 0 then
231                 return nil
232         end
233
234         if max == 0 then
235                 return str
236         end
237
238         repeat
239                 local s, e = str:find(pat, c, not regex)
240                 max = max - 1
241                 if s and max < 0 then
242                         t[#t+1] = str:sub(c)
243                 else
244                         t[#t+1] = str:sub(c, s and s - 1)
245                 end
246                 c = e and e + 1 or #str + 1
247         until not s or max < 0
248
249         return t
250 end
251
252 --- Remove leading and trailing whitespace from given string value.
253 -- @param str   String value containing whitespace padded data
254 -- @return              String value with leading and trailing space removed
255 function trim(str)
256         return (str:gsub("^%s*(.-)%s*$", "%1"))
257 end
258
259 --- Count the occurences of given substring in given string.
260 -- @param str           String to search in
261 -- @param pattern       String containing pattern to find
262 -- @return                      Number of found occurences
263 function cmatch(str, pat)
264         local count = 0
265         for _ in str:gmatch(pat) do count = count + 1 end
266         return count
267 end
268
269 --- Return a matching iterator for the given value. The iterator will return
270 -- one token per invocation, the tokens are separated by whitespace. If the
271 -- input value is a table, it is transformed into a string first. A nil value
272 -- will result in a valid interator which aborts with the first invocation.
273 -- @param val           The value to scan (table, string or nil)
274 -- @return                      Iterator which returns one token per call
275 function imatch(v)
276         if type(v) == "table" then
277                 local k = nil
278                 return function()
279                         k = next(v, k)
280                         return v[k]
281                 end
282
283         elseif type(v) == "number" or type(v) == "boolean" then
284                 local x = true
285                 return function()
286                         if x then
287                                 x = false
288                                 return tostring(v)
289                         end
290                 end
291
292         elseif type(v) == "userdata" or type(v) == "string" then
293                 return tostring(v):gmatch("%S+")
294         end
295
296         return function() end
297 end
298
299 --- Parse certain units from the given string and return the canonical integer
300 -- value or 0 if the unit is unknown. Upper- or lower case is irrelevant.
301 -- Recognized units are:
302 --      o "y"   - one year   (60*60*24*366)
303 --  o "m"       - one month  (60*60*24*31)
304 --  o "w"       - one week   (60*60*24*7)
305 --  o "d"       - one day    (60*60*24)
306 --  o "h"       - one hour       (60*60)
307 --  o "min"     - one minute (60)
308 --  o "kb"  - one kilobyte (1024)
309 --  o "mb"      - one megabyte (1024*1024)
310 --  o "gb"      - one gigabyte (1024*1024*1024)
311 --  o "kib" - one si kilobyte (1000)
312 --  o "mib"     - one si megabyte (1000*1000)
313 --  o "gib"     - one si gigabyte (1000*1000*1000)
314 -- @param ustr  String containing a numerical value with trailing unit
315 -- @return              Number containing the canonical value
316 function parse_units(ustr)
317
318         local val = 0
319
320         -- unit map
321         local map = {
322                 -- date stuff
323                 y   = 60 * 60 * 24 * 366,
324                 m   = 60 * 60 * 24 * 31,
325                 w   = 60 * 60 * 24 * 7,
326                 d   = 60 * 60 * 24,
327                 h   = 60 * 60,
328                 min = 60,
329
330                 -- storage sizes
331                 kb  = 1024,
332                 mb  = 1024 * 1024,
333                 gb  = 1024 * 1024 * 1024,
334
335                 -- storage sizes (si)
336                 kib = 1000,
337                 mib = 1000 * 1000,
338                 gib = 1000 * 1000 * 1000
339         }
340
341         -- parse input string
342         for spec in ustr:lower():gmatch("[0-9%.]+[a-zA-Z]*") do
343
344                 local num = spec:gsub("[^0-9%.]+$","")
345                 local spn = spec:gsub("^[0-9%.]+", "")
346
347                 if map[spn] or map[spn:sub(1,1)] then
348                         val = val + num * ( map[spn] or map[spn:sub(1,1)] )
349                 else
350                         val = val + num
351                 end
352         end
353
354
355         return val
356 end
357
358 -- also register functions above in the central string class for convenience
359 string.escape      = escape
360 string.pcdata      = pcdata
361 string.striptags   = striptags
362 string.split       = split
363 string.trim        = trim
364 string.cmatch      = cmatch
365 string.parse_units = parse_units
366
367
368 --- Appends numerically indexed tables or single objects to a given table.
369 -- @param src   Target table
370 -- @param ...   Objects to insert
371 -- @return              Target table
372 function append(src, ...)
373         for i, a in ipairs({...}) do
374                 if type(a) == "table" then
375                         for j, v in ipairs(a) do
376                                 src[#src+1] = v
377                         end
378                 else
379                         src[#src+1] = a
380                 end
381         end
382         return src
383 end
384
385 --- Combines two or more numerically indexed tables and single objects into one table.
386 -- @param tbl1  Table value to combine
387 -- @param tbl2  Table value to combine
388 -- @param ...   More tables to combine
389 -- @return              Table value containing all values of given tables
390 function combine(...)
391         return append({}, ...)
392 end
393
394 --- Checks whether the given table contains the given value.
395 -- @param table Table value
396 -- @param value Value to search within the given table
397 -- @return              Boolean indicating whether the given value occurs within table
398 function contains(table, value)
399         for k, v in pairs(table) do
400                 if value == v then
401                         return k
402                 end
403         end
404         return false
405 end
406
407 --- Update values in given table with the values from the second given table.
408 -- Both table are - in fact - merged together.
409 -- @param t                     Table which should be updated
410 -- @param updates       Table containing the values to update
411 -- @return                      Always nil
412 function update(t, updates)
413         for k, v in pairs(updates) do
414                 t[k] = v
415         end
416 end
417
418 --- Retrieve all keys of given associative table.
419 -- @param t     Table to extract keys from
420 -- @return      Sorted table containing the keys
421 function keys(t)
422         local keys = { }
423         if t then
424                 for k, _ in kspairs(t) do
425                         keys[#keys+1] = k
426                 end
427         end
428         return keys
429 end
430
431 --- Clones the given object and return it's copy.
432 -- @param object        Table value to clone
433 -- @param deep          Boolean indicating whether to do recursive cloning
434 -- @return                      Cloned table value
435 function clone(object, deep)
436         local copy = {}
437
438         for k, v in pairs(object) do
439                 if deep and type(v) == "table" then
440                         v = clone(v, deep)
441                 end
442                 copy[k] = v
443         end
444
445         return setmetatable(copy, getmetatable(object))
446 end
447
448
449 --- Create a dynamic table which automatically creates subtables.
450 -- @return      Dynamic Table
451 function dtable()
452         return setmetatable({}, { __index =
453                 function(tbl, key)
454                         return rawget(tbl, key)
455                          or rawget(rawset(tbl, key, dtable()), key)
456                 end
457         })
458 end
459
460
461 -- Serialize the contents of a table value.
462 function _serialize_table(t, seen)
463         assert(not seen[t], "Recursion detected.")
464         seen[t] = true
465
466         local data  = ""
467         local idata = ""
468         local ilen  = 0
469
470         for k, v in pairs(t) do
471                 if type(k) ~= "number" or k < 1 or math.floor(k) ~= k or ( k - #t ) > 3 then
472                         k = serialize_data(k, seen)
473                         v = serialize_data(v, seen)
474                         data = data .. ( #data > 0 and ", " or "" ) ..
475                                 '[' .. k .. '] = ' .. v
476                 elseif k > ilen then
477                         ilen = k
478                 end
479         end
480
481         for i = 1, ilen do
482                 local v = serialize_data(t[i], seen)
483                 idata = idata .. ( #idata > 0 and ", " or "" ) .. v
484         end
485
486         return idata .. ( #data > 0 and #idata > 0 and ", " or "" ) .. data
487 end
488
489 --- Recursively serialize given data to lua code, suitable for restoring
490 -- with loadstring().
491 -- @param val   Value containing the data to serialize
492 -- @return              String value containing the serialized code
493 -- @see                 restore_data
494 -- @see                 get_bytecode
495 function serialize_data(val, seen)
496         seen = seen or setmetatable({}, {__mode="k"})
497
498         if val == nil then
499                 return "nil"
500         elseif type(val) == "number" then
501                 return val
502         elseif type(val) == "string" then
503                 return "%q" % val
504         elseif type(val) == "boolean" then
505                 return val and "true" or "false"
506         elseif type(val) == "function" then
507                 return "loadstring(%q)" % get_bytecode(val)
508         elseif type(val) == "table" then
509                 return "{ " .. _serialize_table(val, seen) .. " }"
510         else
511                 return '"[unhandled data type:' .. type(val) .. ']"'
512         end
513 end
514
515 --- Restore data previously serialized with serialize_data().
516 -- @param str   String containing the data to restore
517 -- @return              Value containing the restored data structure
518 -- @see                 serialize_data
519 -- @see                 get_bytecode
520 function restore_data(str)
521         return loadstring("return " .. str)()
522 end
523
524
525 --
526 -- Byte code manipulation routines
527 --
528
529 --- Return the current runtime bytecode of the given data. The byte code
530 -- will be stripped before it is returned.
531 -- @param val   Value to return as bytecode
532 -- @return              String value containing the bytecode of the given data
533 function get_bytecode(val)
534         local code
535
536         if type(val) == "function" then
537                 code = string.dump(val)
538         else
539                 code = string.dump( loadstring( "return " .. serialize_data(val) ) )
540         end
541
542         return code -- and strip_bytecode(code)
543 end
544
545 --- Strips unnescessary lua bytecode from given string. Information like line
546 -- numbers and debugging numbers will be discarded. Original version by
547 -- Peter Cawley (http://lua-users.org/lists/lua-l/2008-02/msg01158.html)
548 -- @param code  String value containing the original lua byte code
549 -- @return              String value containing the stripped lua byte code
550 function strip_bytecode(code)
551         local version, format, endian, int, size, ins, num, lnum = code:byte(5, 12)
552         local subint
553         if endian == 1 then
554                 subint = function(code, i, l)
555                         local val = 0
556                         for n = l, 1, -1 do
557                                 val = val * 256 + code:byte(i + n - 1)
558                         end
559                         return val, i + l
560                 end
561         else
562                 subint = function(code, i, l)
563                         local val = 0
564                         for n = 1, l, 1 do
565                                 val = val * 256 + code:byte(i + n - 1)
566                         end
567                         return val, i + l
568                 end
569         end
570
571         local function strip_function(code)
572                 local count, offset = subint(code, 1, size)
573                 local stripped = { string.rep("\0", size) }
574                 local dirty = offset + count
575                 offset = offset + count + int * 2 + 4
576                 offset = offset + int + subint(code, offset, int) * ins
577                 count, offset = subint(code, offset, int)
578                 for n = 1, count do
579                         local t
580                         t, offset = subint(code, offset, 1)
581                         if t == 1 then
582                                 offset = offset + 1
583                         elseif t == 4 then
584                                 offset = offset + size + subint(code, offset, size)
585                         elseif t == 3 then
586                                 offset = offset + num
587                         elseif t == 254 or t == 9 then
588                                 offset = offset + lnum
589                         end
590                 end
591                 count, offset = subint(code, offset, int)
592                 stripped[#stripped+1] = code:sub(dirty, offset - 1)
593                 for n = 1, count do
594                         local proto, off = strip_function(code:sub(offset, -1))
595                         stripped[#stripped+1] = proto
596                         offset = offset + off - 1
597                 end
598                 offset = offset + subint(code, offset, int) * int + int
599                 count, offset = subint(code, offset, int)
600                 for n = 1, count do
601                         offset = offset + subint(code, offset, size) + size + int * 2
602                 end
603                 count, offset = subint(code, offset, int)
604                 for n = 1, count do
605                         offset = offset + subint(code, offset, size) + size
606                 end
607                 stripped[#stripped+1] = string.rep("\0", int * 3)
608                 return table.concat(stripped), offset
609         end
610
611         return code:sub(1,12) .. strip_function(code:sub(13,-1))
612 end
613
614
615 --
616 -- Sorting iterator functions
617 --
618
619 function _sortiter( t, f )
620         local keys = { }
621
622         for k, v in pairs(t) do
623                 keys[#keys+1] = k
624         end
625
626         local _pos = 0
627
628         table.sort( keys, f )
629
630         return function()
631                 _pos = _pos + 1
632                 if _pos <= #keys then
633                         return keys[_pos], t[keys[_pos]]
634                 end
635         end
636 end
637
638 --- Return a key, value iterator which returns the values sorted according to
639 -- the provided callback function.
640 -- @param t     The table to iterate
641 -- @param f A callback function to decide the order of elements
642 -- @return      Function value containing the corresponding iterator
643 function spairs(t,f)
644         return _sortiter( t, f )
645 end
646
647 --- Return a key, value iterator for the given table.
648 -- The table pairs are sorted by key.
649 -- @param t     The table to iterate
650 -- @return      Function value containing the corresponding iterator
651 function kspairs(t)
652         return _sortiter( t )
653 end
654
655 --- Return a key, value iterator for the given table.
656 -- The table pairs are sorted by value.
657 -- @param t     The table to iterate
658 -- @return      Function value containing the corresponding iterator
659 function vspairs(t)
660         return _sortiter( t, function (a,b) return t[a] < t[b] end )
661 end
662
663
664 --
665 -- System utility functions
666 --
667
668 --- Test whether the current system is operating in big endian mode.
669 -- @return      Boolean value indicating whether system is big endian
670 function bigendian()
671         return string.byte(string.dump(function() end), 7) == 0
672 end
673
674 --- Execute given commandline and gather stdout.
675 -- @param command       String containing command to execute
676 -- @return                      String containing the command's stdout
677 function exec(command)
678         local pp   = io.popen(command)
679         local data = pp:read("*a")
680         pp:close()
681
682         return data
683 end
684
685 --- Return a line-buffered iterator over the output of given command.
686 -- @param command       String containing the command to execute
687 -- @return                      Iterator
688 function execi(command)
689         local pp = io.popen(command)
690
691         return pp and function()
692                 local line = pp:read()
693
694                 if not line then
695                         pp:close()
696                 end
697
698                 return line
699         end
700 end
701
702 -- Deprecated
703 function execl(command)
704         local pp   = io.popen(command)
705         local line = ""
706         local data = {}
707
708         while true do
709                 line = pp:read()
710                 if (line == nil) then break end
711                 data[#data+1] = line
712         end
713         pp:close()
714
715         return data
716 end
717
718 --- Returns the absolute path to LuCI base directory.
719 -- @return              String containing the directory path
720 function libpath()
721         return require "nixio.fs".dirname(ldebug.__file__)
722 end
723
724
725 --
726 -- Coroutine safe xpcall and pcall versions modified for Luci
727 -- original version:
728 -- coxpcall 1.13 - Copyright 2005 - Kepler Project (www.keplerproject.org)
729 --
730 -- Copyright © 2005 Kepler Project.
731 -- Permission is hereby granted, free of charge, to any person obtaining a
732 -- copy of this software and associated documentation files (the "Software"),
733 -- to deal in the Software without restriction, including without limitation
734 -- the rights to use, copy, modify, merge, publish, distribute, sublicense,
735 -- and/or sell copies of the Software, and to permit persons to whom the
736 -- Software is furnished to do so, subject to the following conditions:
737 --
738 -- The above copyright notice and this permission notice shall be
739 -- included in all copies or substantial portions of the Software.
740 --
741 -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
742 -- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
743 -- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
744 -- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
745 -- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
746 -- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
747 -- OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
748
749 local performResume, handleReturnValue
750 local oldpcall, oldxpcall = pcall, xpcall
751 coxpt = {}
752 setmetatable(coxpt, {__mode = "kv"})
753
754 -- Identity function for copcall
755 local function copcall_id(trace, ...)
756   return ...
757 end
758
759 --- This is a coroutine-safe drop-in replacement for Lua's "xpcall"-function
760 -- @param f             Lua function to be called protected
761 -- @param err   Custom error handler
762 -- @param ...   Parameters passed to the function
763 -- @return              A boolean whether the function call succeeded and the return
764 --                              values of either the function or the error handler
765 function coxpcall(f, err, ...)
766         local res, co = oldpcall(coroutine.create, f)
767         if not res then
768                 local params = {...}
769                 local newf = function() return f(unpack(params)) end
770                 co = coroutine.create(newf)
771         end
772         local c = coroutine.running()
773         coxpt[co] = coxpt[c] or c or 0
774
775         return performResume(err, co, ...)
776 end
777
778 --- This is a coroutine-safe drop-in replacement for Lua's "pcall"-function
779 -- @param f             Lua function to be called protected
780 -- @param ...   Parameters passed to the function
781 -- @return              A boolean whether the function call succeeded and the returns
782 --                              values of the function or the error object
783 function copcall(f, ...)
784         return coxpcall(f, copcall_id, ...)
785 end
786
787 -- Handle return value of protected call
788 function handleReturnValue(err, co, status, ...)
789         if not status then
790                 return false, err(debug.traceback(co, (...)), ...)
791         end
792
793         if coroutine.status(co) ~= 'suspended' then
794                 return true, ...
795         end
796
797         return performResume(err, co, coroutine.yield(...))
798 end
799
800 -- Resume execution of protected function call
801 function performResume(err, co, ...)
802         return handleReturnValue(err, co, coroutine.resume(co, ...))
803 end