libs/core: bring get_first() boolean casting in line with get_bool()
[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, loadstring = ipairs, pairs, 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 v == nil then
277                 v = ""
278         elseif type(v) == "table" then
279                 v = table.concat(v, " ")
280         elseif type(v) ~= "string" then
281                 v = tostring(v)
282         end
283
284         return v:gmatch("%S+")
285 end
286
287 --- Parse certain units from the given string and return the canonical integer
288 -- value or 0 if the unit is unknown. Upper- or lower case is irrelevant.
289 -- Recognized units are:
290 --      o "y"   - one year   (60*60*24*366)
291 --  o "m"       - one month  (60*60*24*31)
292 --  o "w"       - one week   (60*60*24*7)
293 --  o "d"       - one day    (60*60*24)
294 --  o "h"       - one hour       (60*60)
295 --  o "min"     - one minute (60)
296 --  o "kb"  - one kilobyte (1024)
297 --  o "mb"      - one megabyte (1024*1024)
298 --  o "gb"      - one gigabyte (1024*1024*1024)
299 --  o "kib" - one si kilobyte (1000)
300 --  o "mib"     - one si megabyte (1000*1000)
301 --  o "gib"     - one si gigabyte (1000*1000*1000)
302 -- @param ustr  String containing a numerical value with trailing unit
303 -- @return              Number containing the canonical value
304 function parse_units(ustr)
305
306         local val = 0
307
308         -- unit map
309         local map = {
310                 -- date stuff
311                 y   = 60 * 60 * 24 * 366,
312                 m   = 60 * 60 * 24 * 31,
313                 w   = 60 * 60 * 24 * 7,
314                 d   = 60 * 60 * 24,
315                 h   = 60 * 60,
316                 min = 60,
317
318                 -- storage sizes
319                 kb  = 1024,
320                 mb  = 1024 * 1024,
321                 gb  = 1024 * 1024 * 1024,
322
323                 -- storage sizes (si)
324                 kib = 1000,
325                 mib = 1000 * 1000,
326                 gib = 1000 * 1000 * 1000
327         }
328
329         -- parse input string
330         for spec in ustr:lower():gmatch("[0-9%.]+[a-zA-Z]*") do
331
332                 local num = spec:gsub("[^0-9%.]+$","")
333                 local spn = spec:gsub("^[0-9%.]+", "")
334
335                 if map[spn] or map[spn:sub(1,1)] then
336                         val = val + num * ( map[spn] or map[spn:sub(1,1)] )
337                 else
338                         val = val + num
339                 end
340         end
341
342
343         return val
344 end
345
346 -- also register functions above in the central string class for convenience
347 string.escape      = escape
348 string.pcdata      = pcdata
349 string.striptags   = striptags
350 string.split       = split
351 string.trim        = trim
352 string.cmatch      = cmatch
353 string.parse_units = parse_units
354
355
356 --- Appends numerically indexed tables or single objects to a given table.
357 -- @param src   Target table
358 -- @param ...   Objects to insert
359 -- @return              Target table
360 function append(src, ...)
361         for i, a in ipairs({...}) do
362                 if type(a) == "table" then
363                         for j, v in ipairs(a) do
364                                 src[#src+1] = v
365                         end
366                 else
367                         src[#src+1] = a
368                 end
369         end
370         return src
371 end
372
373 --- Combines two or more numerically indexed tables and single objects into one table.
374 -- @param tbl1  Table value to combine
375 -- @param tbl2  Table value to combine
376 -- @param ...   More tables to combine
377 -- @return              Table value containing all values of given tables
378 function combine(...)
379         return append({}, ...)
380 end
381
382 --- Checks whether the given table contains the given value.
383 -- @param table Table value
384 -- @param value Value to search within the given table
385 -- @return              Boolean indicating whether the given value occurs within table
386 function contains(table, value)
387         for k, v in pairs(table) do
388                 if value == v then
389                         return k
390                 end
391         end
392         return false
393 end
394
395 --- Update values in given table with the values from the second given table.
396 -- Both table are - in fact - merged together.
397 -- @param t                     Table which should be updated
398 -- @param updates       Table containing the values to update
399 -- @return                      Always nil
400 function update(t, updates)
401         for k, v in pairs(updates) do
402                 t[k] = v
403         end
404 end
405
406 --- Retrieve all keys of given associative table.
407 -- @param t     Table to extract keys from
408 -- @return      Sorted table containing the keys
409 function keys(t)
410         local keys = { }
411         if t then
412                 for k, _ in kspairs(t) do
413                         keys[#keys+1] = k
414                 end
415         end
416         return keys
417 end
418
419 --- Clones the given object and return it's copy.
420 -- @param object        Table value to clone
421 -- @param deep          Boolean indicating whether to do recursive cloning
422 -- @return                      Cloned table value
423 function clone(object, deep)
424         local copy = {}
425
426         for k, v in pairs(object) do
427                 if deep and type(v) == "table" then
428                         v = clone(v, deep)
429                 end
430                 copy[k] = v
431         end
432
433         return setmetatable(copy, getmetatable(object))
434 end
435
436
437 --- Create a dynamic table which automatically creates subtables.
438 -- @return      Dynamic Table
439 function dtable()
440         return setmetatable({}, { __index =
441                 function(tbl, key)
442                         return rawget(tbl, key)
443                          or rawget(rawset(tbl, key, dtable()), key)
444                 end
445         })
446 end
447
448
449 -- Serialize the contents of a table value.
450 function _serialize_table(t, seen)
451         assert(not seen[t], "Recursion detected.")
452         seen[t] = true
453
454         local data  = ""
455         local idata = ""
456         local ilen  = 0
457
458         for k, v in pairs(t) do
459                 if type(k) ~= "number" or k < 1 or math.floor(k) ~= k or ( k - #t ) > 3 then
460                         k = serialize_data(k, seen)
461                         v = serialize_data(v, seen)
462                         data = data .. ( #data > 0 and ", " or "" ) ..
463                                 '[' .. k .. '] = ' .. v
464                 elseif k > ilen then
465                         ilen = k
466                 end
467         end
468
469         for i = 1, ilen do
470                 local v = serialize_data(t[i], seen)
471                 idata = idata .. ( #idata > 0 and ", " or "" ) .. v
472         end
473
474         return idata .. ( #data > 0 and #idata > 0 and ", " or "" ) .. data
475 end
476
477 --- Recursively serialize given data to lua code, suitable for restoring
478 -- with loadstring().
479 -- @param val   Value containing the data to serialize
480 -- @return              String value containing the serialized code
481 -- @see                 restore_data
482 -- @see                 get_bytecode
483 function serialize_data(val, seen)
484         seen = seen or setmetatable({}, {__mode="k"})
485
486         if val == nil then
487                 return "nil"
488         elseif type(val) == "number" then
489                 return val
490         elseif type(val) == "string" then
491                 return "%q" % val
492         elseif type(val) == "boolean" then
493                 return val and "true" or "false"
494         elseif type(val) == "function" then
495                 return "loadstring(%q)" % get_bytecode(val)
496         elseif type(val) == "table" then
497                 return "{ " .. _serialize_table(val, seen) .. " }"
498         else
499                 return '"[unhandled data type:' .. type(val) .. ']"'
500         end
501 end
502
503 --- Restore data previously serialized with serialize_data().
504 -- @param str   String containing the data to restore
505 -- @return              Value containing the restored data structure
506 -- @see                 serialize_data
507 -- @see                 get_bytecode
508 function restore_data(str)
509         return loadstring("return " .. str)()
510 end
511
512
513 --
514 -- Byte code manipulation routines
515 --
516
517 --- Return the current runtime bytecode of the given data. The byte code
518 -- will be stripped before it is returned.
519 -- @param val   Value to return as bytecode
520 -- @return              String value containing the bytecode of the given data
521 function get_bytecode(val)
522         local code
523
524         if type(val) == "function" then
525                 code = string.dump(val)
526         else
527                 code = string.dump( loadstring( "return " .. serialize_data(val) ) )
528         end
529
530         return code -- and strip_bytecode(code)
531 end
532
533 --- Strips unnescessary lua bytecode from given string. Information like line
534 -- numbers and debugging numbers will be discarded. Original version by
535 -- Peter Cawley (http://lua-users.org/lists/lua-l/2008-02/msg01158.html)
536 -- @param code  String value containing the original lua byte code
537 -- @return              String value containing the stripped lua byte code
538 function strip_bytecode(code)
539         local version, format, endian, int, size, ins, num, lnum = code:byte(5, 12)
540         local subint
541         if endian == 1 then
542                 subint = function(code, i, l)
543                         local val = 0
544                         for n = l, 1, -1 do
545                                 val = val * 256 + code:byte(i + n - 1)
546                         end
547                         return val, i + l
548                 end
549         else
550                 subint = function(code, i, l)
551                         local val = 0
552                         for n = 1, l, 1 do
553                                 val = val * 256 + code:byte(i + n - 1)
554                         end
555                         return val, i + l
556                 end
557         end
558
559         local function strip_function(code)
560                 local count, offset = subint(code, 1, size)
561                 local stripped = { string.rep("\0", size) }
562                 local dirty = offset + count
563                 offset = offset + count + int * 2 + 4
564                 offset = offset + int + subint(code, offset, int) * ins
565                 count, offset = subint(code, offset, int)
566                 for n = 1, count do
567                         local t
568                         t, offset = subint(code, offset, 1)
569                         if t == 1 then
570                                 offset = offset + 1
571                         elseif t == 4 then
572                                 offset = offset + size + subint(code, offset, size)
573                         elseif t == 3 then
574                                 offset = offset + num
575                         elseif t == 254 or t == 9 then
576                                 offset = offset + lnum
577                         end
578                 end
579                 count, offset = subint(code, offset, int)
580                 stripped[#stripped+1] = code:sub(dirty, offset - 1)
581                 for n = 1, count do
582                         local proto, off = strip_function(code:sub(offset, -1))
583                         stripped[#stripped+1] = proto
584                         offset = offset + off - 1
585                 end
586                 offset = offset + subint(code, offset, int) * int + int
587                 count, offset = subint(code, offset, int)
588                 for n = 1, count do
589                         offset = offset + subint(code, offset, size) + size + int * 2
590                 end
591                 count, offset = subint(code, offset, int)
592                 for n = 1, count do
593                         offset = offset + subint(code, offset, size) + size
594                 end
595                 stripped[#stripped+1] = string.rep("\0", int * 3)
596                 return table.concat(stripped), offset
597         end
598
599         return code:sub(1,12) .. strip_function(code:sub(13,-1))
600 end
601
602
603 --
604 -- Sorting iterator functions
605 --
606
607 function _sortiter( t, f )
608         local keys = { }
609
610         for k, v in pairs(t) do
611                 keys[#keys+1] = k
612         end
613
614         local _pos = 0
615
616         table.sort( keys, f )
617
618         return function()
619                 _pos = _pos + 1
620                 if _pos <= #keys then
621                         return keys[_pos], t[keys[_pos]]
622                 end
623         end
624 end
625
626 --- Return a key, value iterator which returns the values sorted according to
627 -- the provided callback function.
628 -- @param t     The table to iterate
629 -- @param f A callback function to decide the order of elements
630 -- @return      Function value containing the corresponding iterator
631 function spairs(t,f)
632         return _sortiter( t, f )
633 end
634
635 --- Return a key, value iterator for the given table.
636 -- The table pairs are sorted by key.
637 -- @param t     The table to iterate
638 -- @return      Function value containing the corresponding iterator
639 function kspairs(t)
640         return _sortiter( t )
641 end
642
643 --- Return a key, value iterator for the given table.
644 -- The table pairs are sorted by value.
645 -- @param t     The table to iterate
646 -- @return      Function value containing the corresponding iterator
647 function vspairs(t)
648         return _sortiter( t, function (a,b) return t[a] < t[b] end )
649 end
650
651
652 --
653 -- System utility functions
654 --
655
656 --- Test whether the current system is operating in big endian mode.
657 -- @return      Boolean value indicating whether system is big endian
658 function bigendian()
659         return string.byte(string.dump(function() end), 7) == 0
660 end
661
662 --- Execute given commandline and gather stdout.
663 -- @param command       String containing command to execute
664 -- @return                      String containing the command's stdout
665 function exec(command)
666         local pp   = io.popen(command)
667         local data = pp:read("*a")
668         pp:close()
669
670         return data
671 end
672
673 --- Return a line-buffered iterator over the output of given command.
674 -- @param command       String containing the command to execute
675 -- @return                      Iterator
676 function execi(command)
677         local pp = io.popen(command)
678
679         return pp and function()
680                 local line = pp:read()
681
682                 if not line then
683                         pp:close()
684                 end
685
686                 return line
687         end
688 end
689
690 -- Deprecated
691 function execl(command)
692         local pp   = io.popen(command)
693         local line = ""
694         local data = {}
695
696         while true do
697                 line = pp:read()
698                 if (line == nil) then break end
699                 data[#data+1] = line
700         end
701         pp:close()
702
703         return data
704 end
705
706 --- Returns the absolute path to LuCI base directory.
707 -- @return              String containing the directory path
708 function libpath()
709         return require "nixio.fs".dirname(ldebug.__file__)
710 end
711
712
713 --
714 -- Coroutine safe xpcall and pcall versions modified for Luci
715 -- original version:
716 -- coxpcall 1.13 - Copyright 2005 - Kepler Project (www.keplerproject.org)
717 --
718 -- Copyright © 2005 Kepler Project.
719 -- Permission is hereby granted, free of charge, to any person obtaining a
720 -- copy of this software and associated documentation files (the "Software"),
721 -- to deal in the Software without restriction, including without limitation
722 -- the rights to use, copy, modify, merge, publish, distribute, sublicense,
723 -- and/or sell copies of the Software, and to permit persons to whom the
724 -- Software is furnished to do so, subject to the following conditions:
725 --
726 -- The above copyright notice and this permission notice shall be
727 -- included in all copies or substantial portions of the Software.
728 --
729 -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
730 -- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
731 -- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
732 -- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
733 -- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
734 -- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
735 -- OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
736
737 local performResume, handleReturnValue
738 local oldpcall, oldxpcall = pcall, xpcall
739 coxpt = {}
740 setmetatable(coxpt, {__mode = "kv"})
741
742 -- Identity function for copcall
743 local function copcall_id(trace, ...)
744   return ...
745 end
746
747 --- This is a coroutine-safe drop-in replacement for Lua's "xpcall"-function
748 -- @param f             Lua function to be called protected
749 -- @param err   Custom error handler
750 -- @param ...   Parameters passed to the function
751 -- @return              A boolean whether the function call succeeded and the return
752 --                              values of either the function or the error handler
753 function coxpcall(f, err, ...)
754         local res, co = oldpcall(coroutine.create, f)
755         if not res then
756                 local params = {...}
757                 local newf = function() return f(unpack(params)) end
758                 co = coroutine.create(newf)
759         end
760         local c = coroutine.running()
761         coxpt[co] = coxpt[c] or c or 0
762
763         return performResume(err, co, ...)
764 end
765
766 --- This is a coroutine-safe drop-in replacement for Lua's "pcall"-function
767 -- @param f             Lua function to be called protected
768 -- @param ...   Parameters passed to the function
769 -- @return              A boolean whether the function call succeeded and the returns
770 --                              values of the function or the error object
771 function copcall(f, ...)
772         return coxpcall(f, copcall_id, ...)
773 end
774
775 -- Handle return value of protected call
776 function handleReturnValue(err, co, status, ...)
777         if not status then
778                 return false, err(debug.traceback(co, (...)), ...)
779         end
780
781         if coroutine.status(co) ~= 'suspended' then
782                 return true, ...
783         end
784
785         return performResume(err, co, coroutine.yield(...))
786 end
787
788 -- Resume execution of protected function call
789 function performResume(err, co, ...)
790         return handleReturnValue(err, co, coroutine.resume(co, ...))
791 end