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