· 5 years ago · Jul 12, 2020, 02:54 PM
1-- Load in expect from the module path.
2--
3-- Ideally we'd use require, but that is part of the shell, and so is not
4-- available to the BIOS or any APIs. All APIs load this using dofile, but that
5-- has not been defined at this point.
6local expect
7
8do
9 local h = fs.open("rom/modules/main/cc/expect.lua", "r")
10 local f, err = loadstring(h.readAll(), "@expect.lua")
11 h.close()
12
13 if not f then error(err) end
14 expect = f().expect
15end
16
17if _VERSION == "Lua 5.1" then
18 -- If we're on Lua 5.1, install parts of the Lua 5.2/5.3 API so that programs can be written against it
19 local type = type
20 local nativeload = load
21 local nativeloadstring = loadstring
22 local nativesetfenv = setfenv
23
24 -- Historically load/loadstring would handle the chunk name as if it has
25 -- been prefixed with "=". We emulate that behaviour here.
26 local function prefix(chunkname)
27 if type(chunkname) ~= "string" then return chunkname end
28 local head = chunkname:sub(1, 1)
29 if head == "=" or head == "@" then
30 return chunkname
31 else
32 return "=" .. chunkname
33 end
34 end
35
36 function load(x, name, mode, env)
37 expect(1, x, "function", "string")
38 expect(2, name, "string", "nil")
39 expect(3, mode, "string", "nil")
40 expect(4, env, "table", "nil")
41
42 local ok, p1, p2 = pcall(function()
43 if type(x) == "string" then
44 local result, err = nativeloadstring(x, name)
45 if result then
46 if env then
47 env._ENV = env
48 nativesetfenv(result, env)
49 end
50 return result
51 else
52 return nil, err
53 end
54 else
55 local result, err = nativeload(x, name)
56 if result then
57 if env then
58 env._ENV = env
59 nativesetfenv(result, env)
60 end
61 return result
62 else
63 return nil, err
64 end
65 end
66 end)
67 if ok then
68 return p1, p2
69 else
70 error(p1, 2)
71 end
72 end
73
74 if _CC_DISABLE_LUA51_FEATURES then
75 -- Remove the Lua 5.1 features that will be removed when we update to Lua 5.2, for compatibility testing.
76 -- See "disable_lua51_functions" in ComputerCraft.cfg
77 setfenv = nil
78 getfenv = nil
79 loadstring = nil
80 unpack = nil
81 math.log10 = nil
82 table.maxn = nil
83 else
84 loadstring = function(string, chunkname) return nativeloadstring(string, prefix(chunkname)) end
85
86 -- Inject a stub for the old bit library
87 _G.bit = {
88 bnot = bit32.bnot,
89 band = bit32.band,
90 bor = bit32.bor,
91 bxor = bit32.bxor,
92 brshift = bit32.arshift,
93 blshift = bit32.lshift,
94 blogic_rshift = bit32.rshift,
95 }
96 end
97end
98
99-- Install lua parts of the os api
100function os.version()
101 return "CraftOS 1.8"
102end
103
104function os.pullEventRaw(sFilter)
105 return coroutine.yield(sFilter)
106end
107
108function os.pullEvent(sFilter)
109 local eventData = table.pack(os.pullEventRaw(sFilter))
110 if eventData[1] == "terminate" then
111 error("Terminated", 0)
112 end
113 return table.unpack(eventData, 1, eventData.n)
114end
115
116-- Install globals
117function sleep(nTime)
118 expect(1, nTime, "number", "nil")
119 local timer = os.startTimer(nTime or 0)
120 repeat
121 local _, param = os.pullEvent("timer")
122 until param == timer
123end
124
125function write(sText)
126 expect(1, sText, "string", "number")
127
128 local w, h = term.getSize()
129 local x, y = term.getCursorPos()
130
131 local nLinesPrinted = 0
132 local function newLine()
133 if y + 1 <= h then
134 term.setCursorPos(1, y + 1)
135 else
136 term.setCursorPos(1, h)
137 term.scroll(1)
138 end
139 x, y = term.getCursorPos()
140 nLinesPrinted = nLinesPrinted + 1
141 end
142
143 -- Print the line with proper word wrapping
144 sText = tostring(sText)
145 while #sText > 0 do
146 local whitespace = string.match(sText, "^[ \t]+")
147 if whitespace then
148 -- Print whitespace
149 term.write(whitespace)
150 x, y = term.getCursorPos()
151 sText = string.sub(sText, #whitespace + 1)
152 end
153
154 local newline = string.match(sText, "^\n")
155 if newline then
156 -- Print newlines
157 newLine()
158 sText = string.sub(sText, 2)
159 end
160
161 local text = string.match(sText, "^[^ \t\n]+")
162 if text then
163 sText = string.sub(sText, #text + 1)
164 if #text > w then
165 -- Print a multiline word
166 while #text > 0 do
167 if x > w then
168 newLine()
169 end
170 term.write(text)
171 text = string.sub(text, w - x + 2)
172 x, y = term.getCursorPos()
173 end
174 else
175 -- Print a word normally
176 if x + #text - 1 > w then
177 newLine()
178 end
179 term.write(text)
180 x, y = term.getCursorPos()
181 end
182 end
183 end
184
185 return nLinesPrinted
186end
187
188function print(...)
189 local nLinesPrinted = 0
190 local nLimit = select("#", ...)
191 for n = 1, nLimit do
192 local s = tostring(select(n, ...))
193 if n < nLimit then
194 s = s .. "\t"
195 end
196 nLinesPrinted = nLinesPrinted + write(s)
197 end
198 nLinesPrinted = nLinesPrinted + write("\n")
199 return nLinesPrinted
200end
201
202function printError(...)
203 local oldColour
204 if term.isColour() then
205 oldColour = term.getTextColour()
206 term.setTextColour(colors.red)
207 end
208 print(...)
209 if term.isColour() then
210 term.setTextColour(oldColour)
211 end
212end
213
214function read(_sReplaceChar, _tHistory, _fnComplete, _sDefault)
215 expect(1, _sReplaceChar, "string", "nil")
216 expect(2, _tHistory, "table", "nil")
217 expect(3, _fnComplete, "function", "nil")
218 expect(4, _sDefault, "string", "nil")
219
220 term.setCursorBlink(true)
221
222 local sLine
223 if type(_sDefault) == "string" then
224 sLine = _sDefault
225 else
226 sLine = ""
227 end
228 local nHistoryPos
229 local nPos, nScroll = #sLine, 0
230 if _sReplaceChar then
231 _sReplaceChar = string.sub(_sReplaceChar, 1, 1)
232 end
233
234 local tCompletions
235 local nCompletion
236 local function recomplete()
237 if _fnComplete and nPos == #sLine then
238 tCompletions = _fnComplete(sLine)
239 if tCompletions and #tCompletions > 0 then
240 nCompletion = 1
241 else
242 nCompletion = nil
243 end
244 else
245 tCompletions = nil
246 nCompletion = nil
247 end
248 end
249
250 local function uncomplete()
251 tCompletions = nil
252 nCompletion = nil
253 end
254
255 local w = term.getSize()
256 local sx = term.getCursorPos()
257
258 local function redraw(_bClear)
259 local cursor_pos = nPos - nScroll
260 if sx + cursor_pos >= w then
261 -- We've moved beyond the RHS, ensure we're on the edge.
262 nScroll = sx + nPos - w
263 elseif cursor_pos < 0 then
264 -- We've moved beyond the LHS, ensure we're on the edge.
265 nScroll = nPos
266 end
267
268 local _, cy = term.getCursorPos()
269 term.setCursorPos(sx, cy)
270 local sReplace = _bClear and " " or _sReplaceChar
271 if sReplace then
272 term.write(string.rep(sReplace, math.max(#sLine - nScroll, 0)))
273 else
274 term.write(string.sub(sLine, nScroll + 1))
275 end
276
277 if nCompletion then
278 local sCompletion = tCompletions[nCompletion]
279 local oldText, oldBg
280 if not _bClear then
281 oldText = term.getTextColor()
282 oldBg = term.getBackgroundColor()
283 term.setTextColor(colors.white)
284 term.setBackgroundColor(colors.gray)
285 end
286 if sReplace then
287 term.write(string.rep(sReplace, #sCompletion))
288 else
289 term.write(sCompletion)
290 end
291 if not _bClear then
292 term.setTextColor(oldText)
293 term.setBackgroundColor(oldBg)
294 end
295 end
296
297 term.setCursorPos(sx + nPos - nScroll, cy)
298 end
299
300 local function clear()
301 redraw(true)
302 end
303
304 recomplete()
305 redraw()
306
307 local function acceptCompletion()
308 if nCompletion then
309 -- Clear
310 clear()
311
312 -- Find the common prefix of all the other suggestions which start with the same letter as the current one
313 local sCompletion = tCompletions[nCompletion]
314 sLine = sLine .. sCompletion
315 nPos = #sLine
316
317 -- Redraw
318 recomplete()
319 redraw()
320 end
321 end
322 while true do
323 local sEvent, param, param1, param2 = os.pullEvent()
324 if sEvent == "char" then
325 -- Typed key
326 clear()
327 sLine = string.sub(sLine, 1, nPos) .. param .. string.sub(sLine, nPos + 1)
328 nPos = nPos + 1
329 recomplete()
330 redraw()
331
332 elseif sEvent == "paste" then
333 -- Pasted text
334 clear()
335 sLine = string.sub(sLine, 1, nPos) .. param .. string.sub(sLine, nPos + 1)
336 nPos = nPos + #param
337 recomplete()
338 redraw()
339
340 elseif sEvent == "key" then
341 if param == keys.enter then
342 -- Enter
343 if nCompletion then
344 clear()
345 uncomplete()
346 redraw()
347 end
348 break
349
350 elseif param == keys.left then
351 -- Left
352 if nPos > 0 then
353 clear()
354 nPos = nPos - 1
355 recomplete()
356 redraw()
357 end
358
359 elseif param == keys.right then
360 -- Right
361 if nPos < #sLine then
362 -- Move right
363 clear()
364 nPos = nPos + 1
365 recomplete()
366 redraw()
367 else
368 -- Accept autocomplete
369 acceptCompletion()
370 end
371
372 elseif param == keys.up or param == keys.down then
373 -- Up or down
374 if nCompletion then
375 -- Cycle completions
376 clear()
377 if param == keys.up then
378 nCompletion = nCompletion - 1
379 if nCompletion < 1 then
380 nCompletion = #tCompletions
381 end
382 elseif param == keys.down then
383 nCompletion = nCompletion + 1
384 if nCompletion > #tCompletions then
385 nCompletion = 1
386 end
387 end
388 redraw()
389
390 elseif _tHistory then
391 -- Cycle history
392 clear()
393 if param == keys.up then
394 -- Up
395 if nHistoryPos == nil then
396 if #_tHistory > 0 then
397 nHistoryPos = #_tHistory
398 end
399 elseif nHistoryPos > 1 then
400 nHistoryPos = nHistoryPos - 1
401 end
402 else
403 -- Down
404 if nHistoryPos == #_tHistory then
405 nHistoryPos = nil
406 elseif nHistoryPos ~= nil then
407 nHistoryPos = nHistoryPos + 1
408 end
409 end
410 if nHistoryPos then
411 sLine = _tHistory[nHistoryPos]
412 nPos, nScroll = #sLine, 0
413 else
414 sLine = ""
415 nPos, nScroll = 0, 0
416 end
417 uncomplete()
418 redraw()
419
420 end
421
422 elseif param == keys.backspace then
423 -- Backspace
424 if nPos > 0 then
425 clear()
426 sLine = string.sub(sLine, 1, nPos - 1) .. string.sub(sLine, nPos + 1)
427 nPos = nPos - 1
428 if nScroll > 0 then nScroll = nScroll - 1 end
429 recomplete()
430 redraw()
431 end
432
433 elseif param == keys.home then
434 -- Home
435 if nPos > 0 then
436 clear()
437 nPos = 0
438 recomplete()
439 redraw()
440 end
441
442 elseif param == keys.delete then
443 -- Delete
444 if nPos < #sLine then
445 clear()
446 sLine = string.sub(sLine, 1, nPos) .. string.sub(sLine, nPos + 2)
447 recomplete()
448 redraw()
449 end
450
451 elseif param == keys["end"] then
452 -- End
453 if nPos < #sLine then
454 clear()
455 nPos = #sLine
456 recomplete()
457 redraw()
458 end
459
460 elseif param == keys.tab then
461 -- Tab (accept autocomplete)
462 acceptCompletion()
463
464 end
465
466 elseif sEvent == "mouse_click" or sEvent == "mouse_drag" and param == 1 then
467 local _, cy = term.getCursorPos()
468 if param1 >= sx and param1 <= w and param2 == cy then
469 -- Ensure we don't scroll beyond the current line
470 nPos = math.min(math.max(nScroll + param1 - sx, 0), #sLine)
471 redraw()
472 end
473
474 elseif sEvent == "term_resize" then
475 -- Terminal resized
476 w = term.getSize()
477 redraw()
478
479 end
480 end
481
482 local _, cy = term.getCursorPos()
483 term.setCursorBlink(false)
484 term.setCursorPos(w + 1, cy)
485 print()
486
487 return sLine
488end
489
490function loadfile(filename, mode, env)
491 -- Support the previous `loadfile(filename, env)` form instead.
492 if type(mode) == "table" and env == nil then
493 mode, env = nil, mode
494 end
495
496 expect(1, filename, "string")
497 expect(2, mode, "string", "nil")
498 expect(3, env, "table", "nil")
499
500 local file = fs.open(filename, "r")
501 if not file then return nil, "File not found" end
502
503 local func, err = load(file.readAll(), "@" .. fs.getName(filename), mode, env)
504 file.close()
505 return func, err
506end
507function os.getComputerLabel()
508 local handle = fs.open(".computerLabel", "r")
509 local res = handle.readAll()
510 handle.close()
511 return res
512end
513function os.setComputerLabel(label)
514 fs.delete(".computerLabel")
515 local handle = fs.open(".computerLabel", "w")
516 handle.write(label)
517 return true
518end
519function dofile(_sFile)
520 expect(1, _sFile, "string")
521
522 local fnFile, e = loadfile(_sFile, nil, _G)
523 if fnFile then
524 return fnFile()
525 else
526 error(e, 2)
527 end
528end
529
530-- Install the rest of the OS api
531function os.run(_tEnv, _sPath, ...)
532 expect(1, _tEnv, "table")
533 expect(2, _sPath, "string")
534
535 local tEnv = _tEnv
536 setmetatable(tEnv, { __index = _G })
537 local fnFile, err = loadfile(_sPath, nil, tEnv)
538 if fnFile then
539 local ok, err = pcall(fnFile, ...)
540 if not ok then
541 if err and err ~= "" then
542 printError(err)
543 end
544 return false
545 end
546 return true
547 end
548 if err and err ~= "" then
549 printError(err)
550 end
551 return false
552end
553
554local tAPIsLoading = {}
555function os.loadAPI(_sPath)
556 expect(1, _sPath, "string")
557 local sName = fs.getName(_sPath)
558 if sName:sub(-4) == ".lua" then
559 sName = sName:sub(1, -5)
560 end
561 if tAPIsLoading[sName] == true then
562 printError("API " .. sName .. " is already being loaded")
563 return false
564 end
565 tAPIsLoading[sName] = true
566
567 local tEnv = {}
568 setmetatable(tEnv, { __index = _G })
569 local fnAPI, err = loadfile(_sPath, nil, tEnv)
570 if fnAPI then
571 local ok, err = pcall(fnAPI)
572 if not ok then
573 tAPIsLoading[sName] = nil
574 return error("Failed to load API " .. sName .. " due to " .. err, 1)
575 end
576 else
577 tAPIsLoading[sName] = nil
578 return error("Failed to load API " .. sName .. " due to " .. err, 1)
579 end
580
581 local tAPI = {}
582 for k, v in pairs(tEnv) do
583 if k ~= "_ENV" then
584 tAPI[k] = v
585 end
586 end
587
588 _G[sName] = tAPI
589 tAPIsLoading[sName] = nil
590 return true
591end
592
593function os.unloadAPI(_sName)
594 expect(1, _sName, "string")
595 if _sName ~= "_G" and type(_G[_sName]) == "table" then
596 _G[_sName] = nil
597 end
598end
599
600function os.sleep(nTime)
601 sleep(nTime)
602end
603
604local nativeShutdown = os.shutdown
605function os.shutdown()
606 nativeShutdown()
607 while true do
608 coroutine.yield()
609 end
610end
611
612local nativeReboot = os.reboot
613function os.reboot()
614 nativeReboot()
615 while true do
616 coroutine.yield()
617 end
618end
619
620-- Install the lua part of the HTTP api (if enabled)
621if http then
622 local nativeHTTPRequest = http.request
623
624 local methods = {
625 GET = true, POST = true, HEAD = true,
626 OPTIONS = true, PUT = true, DELETE = true,
627 PATCH = true, TRACE = true,
628 }
629
630 local function checkKey(options, key, ty, opt)
631 local value = options[key]
632 local valueTy = type(value)
633
634 if (value ~= nil or not opt) and valueTy ~= ty then
635 error(("bad field '%s' (expected %s, got %s"):format(key, ty, valueTy), 4)
636 end
637 end
638
639 local function checkOptions(options, body)
640 checkKey(options, "url", "string")
641 if body == false then
642 checkKey(options, "body", "nil")
643 else
644 checkKey(options, "body", "string", not body)
645 end
646 checkKey(options, "headers", "table", true)
647 checkKey(options, "method", "string", true)
648 checkKey(options, "redirect", "boolean", true)
649
650 if options.method and not methods[options.method] then
651 error("Unsupported HTTP method", 3)
652 end
653 end
654
655 local function wrapRequest(_url, ...)
656 local ok, err = nativeHTTPRequest(...)
657 if ok then
658 while true do
659 local event, param1, param2, param3 = os.pullEvent()
660 if event == "http_success" and param1 == _url then
661 return param2
662 elseif event == "http_failure" and param1 == _url then
663 return nil, param2, param3
664 end
665 end
666 end
667 return nil, err
668 end
669
670 http.get = function(_url, _headers, _binary)
671 if type(_url) == "table" then
672 checkOptions(_url, false)
673 return wrapRequest(_url.url, _url)
674 end
675
676 expect(1, _url, "string")
677 expect(2, _headers, "table", "nil")
678 expect(3, _binary, "boolean", "nil")
679 return wrapRequest(_url, _url, nil, _headers, _binary)
680 end
681
682 http.post = function(_url, _post, _headers, _binary)
683 if type(_url) == "table" then
684 checkOptions(_url, true)
685 return wrapRequest(_url.url, _url)
686 end
687
688 expect(1, _url, "string")
689 expect(2, _post, "string")
690 expect(3, _headers, "table", "nil")
691 expect(4, _binary, "boolean", "nil")
692 return wrapRequest(_url, _url, _post, _headers, _binary)
693 end
694
695 http.request = function(_url, _post, _headers, _binary)
696 local url
697 if type(_url) == "table" then
698 checkOptions(_url)
699 url = _url.url
700 else
701 expect(1, _url, "string")
702 expect(2, _post, "string", "nil")
703 expect(3, _headers, "table", "nil")
704 expect(4, _binary, "boolean", "nil")
705 url = _url.url
706 end
707
708 local ok, err = nativeHTTPRequest(_url, _post, _headers, _binary)
709 if not ok then
710 os.queueEvent("http_failure", url, err)
711 end
712 return ok, err
713 end
714
715 local nativeCheckURL = http.checkURL
716 http.checkURLAsync = nativeCheckURL
717 http.checkURL = function(_url)
718 local ok, err = nativeCheckURL(_url)
719 if not ok then return ok, err end
720
721 while true do
722 local _, url, ok, err = os.pullEvent("http_check")
723 if url == _url then return ok, err end
724 end
725 end
726
727 local nativeWebsocket = http.websocket
728 http.websocketAsync = nativeWebsocket
729 http.websocket = function(_url, _headers)
730 expect(1, _url, "string")
731 expect(2, _headers, "table", "nil")
732
733 local ok, err = nativeWebsocket(_url, _headers)
734 if not ok then return ok, err end
735
736 while true do
737 local event, url, param = os.pullEvent( )
738 if event == "websocket_success" and url == _url then
739 return param
740 elseif event == "websocket_failure" and url == _url then
741 return false, param
742 end
743 end
744 end
745end
746
747-- Install the lua part of the FS api
748local tEmpty = {}
749function fs.complete(sPath, sLocation, bIncludeFiles, bIncludeDirs)
750 expect(1, sPath, "string")
751 expect(2, sLocation, "string")
752 expect(3, bIncludeFiles, "boolean", "nil")
753 expect(4, bIncludeDirs, "boolean", "nil")
754
755 bIncludeFiles = bIncludeFiles ~= false
756 bIncludeDirs = bIncludeDirs ~= false
757 local sDir = sLocation
758 local nStart = 1
759 local nSlash = string.find(sPath, "[/\\]", nStart)
760 if nSlash == 1 then
761 sDir = ""
762 nStart = 2
763 end
764 local sName
765 while not sName do
766 local nSlash = string.find(sPath, "[/\\]", nStart)
767 if nSlash then
768 local sPart = string.sub(sPath, nStart, nSlash - 1)
769 sDir = fs.combine(sDir, sPart)
770 nStart = nSlash + 1
771 else
772 sName = string.sub(sPath, nStart)
773 end
774 end
775
776 if fs.isDir(sDir) then
777 local tResults = {}
778 if bIncludeDirs and sPath == "" then
779 table.insert(tResults, ".")
780 end
781 if sDir ~= "" then
782 if sPath == "" then
783 table.insert(tResults, bIncludeDirs and ".." or "../")
784 elseif sPath == "." then
785 table.insert(tResults, bIncludeDirs and "." or "./")
786 end
787 end
788 local tFiles = fs.list(sDir)
789 for n = 1, #tFiles do
790 local sFile = tFiles[n]
791 if #sFile >= #sName and string.sub(sFile, 1, #sName) == sName then
792 local bIsDir = fs.isDir(fs.combine(sDir, sFile))
793 local sResult = string.sub(sFile, #sName + 1)
794 if bIsDir then
795 table.insert(tResults, sResult .. "/")
796 if bIncludeDirs and #sResult > 0 then
797 table.insert(tResults, sResult)
798 end
799 else
800 if bIncludeFiles and #sResult > 0 then
801 table.insert(tResults, sResult)
802 end
803 end
804 end
805 end
806 return tResults
807 end
808 return tEmpty
809end
810
811function fs.isDriveRoot(sPath)
812 expect(1, sPath, "string")
813 -- Force the root directory to be a mount.
814 return fs.getDir(sPath) == ".." or fs.getDrive(sPath) ~= fs.getDrive(fs.getDir(sPath))
815end
816
817-- Load APIs
818local bAPIError = false
819local tApis = fs.list("rom/apis")
820for _, sFile in ipairs(tApis) do
821 if string.sub(sFile, 1, 1) ~= "." then
822 local sPath = fs.combine("rom/apis", sFile)
823 if not fs.isDir(sPath) then
824 if not os.loadAPI(sPath) then
825 bAPIError = true
826 end
827 end
828 end
829end
830
831if turtle and fs.isDir("rom/apis/turtle") then
832 -- Load turtle APIs
833 local tApis = fs.list("rom/apis/turtle")
834 for _, sFile in ipairs(tApis) do
835 if string.sub(sFile, 1, 1) ~= "." then
836 local sPath = fs.combine("rom/apis/turtle", sFile)
837 if not fs.isDir(sPath) then
838 if not os.loadAPI(sPath) then
839 bAPIError = true
840 end
841 end
842 end
843 end
844end
845
846if pocket and fs.isDir("rom/apis/pocket") then
847 -- Load pocket APIs
848 local tApis = fs.list("rom/apis/pocket")
849 for _, sFile in ipairs(tApis) do
850 if string.sub(sFile, 1, 1) ~= "." then
851 local sPath = fs.combine("rom/apis/pocket", sFile)
852 if not fs.isDir(sPath) then
853 if not os.loadAPI(sPath) then
854 bAPIError = true
855 end
856 end
857 end
858 end
859end
860
861if commands and fs.isDir("rom/apis/command") then
862 -- Load command APIs
863 if os.loadAPI("rom/apis/command/commands.lua") then
864 -- Add a special case-insensitive metatable to the commands api
865 local tCaseInsensitiveMetatable = {
866 __index = function(table, key)
867 local value = rawget(table, key)
868 if value ~= nil then
869 return value
870 end
871 if type(key) == "string" then
872 local value = rawget(table, string.lower(key))
873 if value ~= nil then
874 return value
875 end
876 end
877 return nil
878 end,
879 }
880 setmetatable(commands, tCaseInsensitiveMetatable)
881 setmetatable(commands.async, tCaseInsensitiveMetatable)
882
883 -- Add global "exec" function
884 exec = commands.exec
885 else
886 bAPIError = true
887 end
888end
889
890if bAPIError then
891 print("Press any key to continue")
892 os.pullEvent("key")
893 term.clear()
894 term.setCursorPos(1, 1)
895end
896
897-- Set default settings
898settings.define("shell.allow_startup", {
899 default = true,
900 description = "Run startup files when the computer turns on.",
901 type = "boolean",
902})
903settings.define("shell.allow_disk_startup", {
904 default = commands == nil,
905 description = "Run startup files from disk drives when the computer turns on.",
906 type = "boolean",
907})
908
909settings.define("shell.autocomplete", {
910 default = true,
911 description = "Autocomplete program and arguments in the shell.",
912 type = "boolean",
913})
914settings.define("edit.autocomplete", {
915 default = true,
916 description = "Autocomplete API and function names in the editor.",
917 type = "boolean",
918})
919settings.define("lua.autocomplete", {
920 default = true,
921 description = "Autocomplete API and function names in the Lua REPL.",
922 type = "boolean",
923})
924
925settings.define("edit.default_extension", {
926 default = "lua",
927 description = [[The file extension the editor will use if none is given. Set to "" to disable.]],
928 type = "string",
929})
930settings.define("paint.default_extension", {
931 default = "nfp",
932 description = [[The file extension the paint program will use if none is given. Set to "" to disable.]],
933 type = "string",
934})
935
936settings.define("list.show_hidden", {
937 default = false,
938 description = [[Show hidden files (those starting with "." in the Lua REPL)]],
939 type = "boolean",
940})
941
942settings.define("motd.enable", {
943 default = pocket == nil,
944 description = "Display a random message when the computer starts up.",
945 type = "boolean",
946})
947settings.define("motd.path", {
948 default = "/rom/motd.txt:/motd.txt",
949 description = [[The path to load random messages from. Should be a colon (":") separated string of file paths.]],
950 type = "string",
951})
952
953settings.define("lua.warn_against_use_of_local", {
954 default = true,
955 description = [[Print a message when input in the Lua REPL starts with the word 'local'. Local variables defined in the Lua REPL are be inaccessable on the next input.]],
956 type = "boolean",
957})
958settings.define("lua.function_args", {
959 default = true,
960 description = "Show function arguments when printing functions.",
961 type = "boolean",
962})
963settings.define("lua.function_source", {
964 default = false,
965 description = "Show where a function was defined when printing functions.",
966 type = "boolean",
967})
968
969if term.isColour() then
970 settings.define("bios.use_multishell", {
971 default = true,
972 description = [[Allow running multiple programs at once, through the use of the "fg" and "bg" programs.]],
973 type = "boolean",
974 })
975end
976if _CC_DEFAULT_SETTINGS then
977 for sPair in string.gmatch(_CC_DEFAULT_SETTINGS, "[^,]+") do
978 local sName, sValue = string.match(sPair, "([^=]*)=(.*)")
979 if sName and sValue then
980 local value
981 if sValue == "true" then
982 value = true
983 elseif sValue == "false" then
984 value = false
985 elseif sValue == "nil" then
986 value = nil
987 elseif tonumber(sValue) then
988 value = tonumber(sValue)
989 else
990 value = sValue
991 end
992 if value ~= nil then
993 settings.set(sName, value)
994 else
995 settings.unset(sName)
996 end
997 end
998 end
999end
1000function os.getComputerLabel()
1001 local handle = fs.open(".computerLabel", "r")
1002 local res = handle.readAll()
1003 handle.close()
1004 return res
1005end
1006function os.setComputerLabel(label)
1007 fs.delete(".computerLabel")
1008 local handle = fs.open(".computerLabel", "w")
1009 handle.write(label)
1010 return true
1011end
1012-- Load user settings
1013if fs.exists(".settings") then
1014 settings.load(".settings")
1015end
1016
1017-- Run the shell
1018local ok, err = pcall(parallel.waitForAny,
1019 function()
1020 local sShell
1021 if term.isColour() and settings.get("bios.use_multishell") then
1022 sShell = "rom/programs/advanced/multishell.lua"
1023 else
1024 sShell = "rom/programs/shell.lua"
1025 end
1026 os.run({}, sShell)
1027 os.run({}, "rom/programs/shutdown.lua")
1028 end,
1029 rednet.run
1030)
1031
1032-- If the shell errored, let the user read it.
1033term.redirect(term.native())
1034if not ok then
1035 printError(err)
1036 pcall(function()
1037 term.setCursorBlink(false)
1038 print("Press any key to continue")
1039 os.pullEvent("key")
1040 end)
1041end
1042
1043-- End
1044os.shutdown()