· 5 years ago · Jul 12, 2020, 02:52 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
107function os.getComputerLabel()
108 local handle = fs.open(".computerLabel", "r")
109 local res = handle.readAll()
110 handle.close()
111 return res
112end
113function os.setComputerLabel(label)
114 fs.delete(".computerLabel")
115 local handle = fs.open(".computerLabel", "w")
116 handle.write(label)
117 return true
118end
119function os.pullEvent(sFilter)
120 local eventData = table.pack(os.pullEventRaw(sFilter))
121 if eventData[1] == "terminate" then
122 error("Terminated", 0)
123 end
124 return table.unpack(eventData, 1, eventData.n)
125end
126
127-- Install globals
128function sleep(nTime)
129 expect(1, nTime, "number", "nil")
130 local timer = os.startTimer(nTime or 0)
131 repeat
132 local _, param = os.pullEvent("timer")
133 until param == timer
134end
135
136function write(sText)
137 expect(1, sText, "string", "number")
138
139 local w, h = term.getSize()
140 local x, y = term.getCursorPos()
141
142 local nLinesPrinted = 0
143 local function newLine()
144 if y + 1 <= h then
145 term.setCursorPos(1, y + 1)
146 else
147 term.setCursorPos(1, h)
148 term.scroll(1)
149 end
150 x, y = term.getCursorPos()
151 nLinesPrinted = nLinesPrinted + 1
152 end
153
154 -- Print the line with proper word wrapping
155 sText = tostring(sText)
156 while #sText > 0 do
157 local whitespace = string.match(sText, "^[ \t]+")
158 if whitespace then
159 -- Print whitespace
160 term.write(whitespace)
161 x, y = term.getCursorPos()
162 sText = string.sub(sText, #whitespace + 1)
163 end
164
165 local newline = string.match(sText, "^\n")
166 if newline then
167 -- Print newlines
168 newLine()
169 sText = string.sub(sText, 2)
170 end
171
172 local text = string.match(sText, "^[^ \t\n]+")
173 if text then
174 sText = string.sub(sText, #text + 1)
175 if #text > w then
176 -- Print a multiline word
177 while #text > 0 do
178 if x > w then
179 newLine()
180 end
181 term.write(text)
182 text = string.sub(text, w - x + 2)
183 x, y = term.getCursorPos()
184 end
185 else
186 -- Print a word normally
187 if x + #text - 1 > w then
188 newLine()
189 end
190 term.write(text)
191 x, y = term.getCursorPos()
192 end
193 end
194 end
195
196 return nLinesPrinted
197end
198
199function print(...)
200 local nLinesPrinted = 0
201 local nLimit = select("#", ...)
202 for n = 1, nLimit do
203 local s = tostring(select(n, ...))
204 if n < nLimit then
205 s = s .. "\t"
206 end
207 nLinesPrinted = nLinesPrinted + write(s)
208 end
209 nLinesPrinted = nLinesPrinted + write("\n")
210 return nLinesPrinted
211end
212
213function printError(...)
214 local oldColour
215 if term.isColour() then
216 oldColour = term.getTextColour()
217 term.setTextColour(colors.red)
218 end
219 print(...)
220 if term.isColour() then
221 term.setTextColour(oldColour)
222 end
223end
224
225function read(_sReplaceChar, _tHistory, _fnComplete, _sDefault)
226 expect(1, _sReplaceChar, "string", "nil")
227 expect(2, _tHistory, "table", "nil")
228 expect(3, _fnComplete, "function", "nil")
229 expect(4, _sDefault, "string", "nil")
230
231 term.setCursorBlink(true)
232
233 local sLine
234 if type(_sDefault) == "string" then
235 sLine = _sDefault
236 else
237 sLine = ""
238 end
239 local nHistoryPos
240 local nPos, nScroll = #sLine, 0
241 if _sReplaceChar then
242 _sReplaceChar = string.sub(_sReplaceChar, 1, 1)
243 end
244
245 local tCompletions
246 local nCompletion
247 local function recomplete()
248 if _fnComplete and nPos == #sLine then
249 tCompletions = _fnComplete(sLine)
250 if tCompletions and #tCompletions > 0 then
251 nCompletion = 1
252 else
253 nCompletion = nil
254 end
255 else
256 tCompletions = nil
257 nCompletion = nil
258 end
259 end
260
261 local function uncomplete()
262 tCompletions = nil
263 nCompletion = nil
264 end
265
266 local w = term.getSize()
267 local sx = term.getCursorPos()
268
269 local function redraw(_bClear)
270 local cursor_pos = nPos - nScroll
271 if sx + cursor_pos >= w then
272 -- We've moved beyond the RHS, ensure we're on the edge.
273 nScroll = sx + nPos - w
274 elseif cursor_pos < 0 then
275 -- We've moved beyond the LHS, ensure we're on the edge.
276 nScroll = nPos
277 end
278
279 local _, cy = term.getCursorPos()
280 term.setCursorPos(sx, cy)
281 local sReplace = _bClear and " " or _sReplaceChar
282 if sReplace then
283 term.write(string.rep(sReplace, math.max(#sLine - nScroll, 0)))
284 else
285 term.write(string.sub(sLine, nScroll + 1))
286 end
287
288 if nCompletion then
289 local sCompletion = tCompletions[nCompletion]
290 local oldText, oldBg
291 if not _bClear then
292 oldText = term.getTextColor()
293 oldBg = term.getBackgroundColor()
294 term.setTextColor(colors.white)
295 term.setBackgroundColor(colors.gray)
296 end
297 if sReplace then
298 term.write(string.rep(sReplace, #sCompletion))
299 else
300 term.write(sCompletion)
301 end
302 if not _bClear then
303 term.setTextColor(oldText)
304 term.setBackgroundColor(oldBg)
305 end
306 end
307
308 term.setCursorPos(sx + nPos - nScroll, cy)
309 end
310
311 local function clear()
312 redraw(true)
313 end
314
315 recomplete()
316 redraw()
317
318 local function acceptCompletion()
319 if nCompletion then
320 -- Clear
321 clear()
322
323 -- Find the common prefix of all the other suggestions which start with the same letter as the current one
324 local sCompletion = tCompletions[nCompletion]
325 sLine = sLine .. sCompletion
326 nPos = #sLine
327
328 -- Redraw
329 recomplete()
330 redraw()
331 end
332 end
333 while true do
334 local sEvent, param, param1, param2 = os.pullEvent()
335 if sEvent == "char" then
336 -- Typed key
337 clear()
338 sLine = string.sub(sLine, 1, nPos) .. param .. string.sub(sLine, nPos + 1)
339 nPos = nPos + 1
340 recomplete()
341 redraw()
342
343 elseif sEvent == "paste" then
344 -- Pasted text
345 clear()
346 sLine = string.sub(sLine, 1, nPos) .. param .. string.sub(sLine, nPos + 1)
347 nPos = nPos + #param
348 recomplete()
349 redraw()
350
351 elseif sEvent == "key" then
352 if param == keys.enter then
353 -- Enter
354 if nCompletion then
355 clear()
356 uncomplete()
357 redraw()
358 end
359 break
360
361 elseif param == keys.left then
362 -- Left
363 if nPos > 0 then
364 clear()
365 nPos = nPos - 1
366 recomplete()
367 redraw()
368 end
369
370 elseif param == keys.right then
371 -- Right
372 if nPos < #sLine then
373 -- Move right
374 clear()
375 nPos = nPos + 1
376 recomplete()
377 redraw()
378 else
379 -- Accept autocomplete
380 acceptCompletion()
381 end
382
383 elseif param == keys.up or param == keys.down then
384 -- Up or down
385 if nCompletion then
386 -- Cycle completions
387 clear()
388 if param == keys.up then
389 nCompletion = nCompletion - 1
390 if nCompletion < 1 then
391 nCompletion = #tCompletions
392 end
393 elseif param == keys.down then
394 nCompletion = nCompletion + 1
395 if nCompletion > #tCompletions then
396 nCompletion = 1
397 end
398 end
399 redraw()
400
401 elseif _tHistory then
402 -- Cycle history
403 clear()
404 if param == keys.up then
405 -- Up
406 if nHistoryPos == nil then
407 if #_tHistory > 0 then
408 nHistoryPos = #_tHistory
409 end
410 elseif nHistoryPos > 1 then
411 nHistoryPos = nHistoryPos - 1
412 end
413 else
414 -- Down
415 if nHistoryPos == #_tHistory then
416 nHistoryPos = nil
417 elseif nHistoryPos ~= nil then
418 nHistoryPos = nHistoryPos + 1
419 end
420 end
421 if nHistoryPos then
422 sLine = _tHistory[nHistoryPos]
423 nPos, nScroll = #sLine, 0
424 else
425 sLine = ""
426 nPos, nScroll = 0, 0
427 end
428 uncomplete()
429 redraw()
430
431 end
432
433 elseif param == keys.backspace then
434 -- Backspace
435 if nPos > 0 then
436 clear()
437 sLine = string.sub(sLine, 1, nPos - 1) .. string.sub(sLine, nPos + 1)
438 nPos = nPos - 1
439 if nScroll > 0 then nScroll = nScroll - 1 end
440 recomplete()
441 redraw()
442 end
443
444 elseif param == keys.home then
445 -- Home
446 if nPos > 0 then
447 clear()
448 nPos = 0
449 recomplete()
450 redraw()
451 end
452
453 elseif param == keys.delete then
454 -- Delete
455 if nPos < #sLine then
456 clear()
457 sLine = string.sub(sLine, 1, nPos) .. string.sub(sLine, nPos + 2)
458 recomplete()
459 redraw()
460 end
461
462 elseif param == keys["end"] then
463 -- End
464 if nPos < #sLine then
465 clear()
466 nPos = #sLine
467 recomplete()
468 redraw()
469 end
470
471 elseif param == keys.tab then
472 -- Tab (accept autocomplete)
473 acceptCompletion()
474
475 end
476
477 elseif sEvent == "mouse_click" or sEvent == "mouse_drag" and param == 1 then
478 local _, cy = term.getCursorPos()
479 if param1 >= sx and param1 <= w and param2 == cy then
480 -- Ensure we don't scroll beyond the current line
481 nPos = math.min(math.max(nScroll + param1 - sx, 0), #sLine)
482 redraw()
483 end
484
485 elseif sEvent == "term_resize" then
486 -- Terminal resized
487 w = term.getSize()
488 redraw()
489
490 end
491 end
492
493 local _, cy = term.getCursorPos()
494 term.setCursorBlink(false)
495 term.setCursorPos(w + 1, cy)
496 print()
497
498 return sLine
499end
500
501function loadfile(filename, mode, env)
502 -- Support the previous `loadfile(filename, env)` form instead.
503 if type(mode) == "table" and env == nil then
504 mode, env = nil, mode
505 end
506
507 expect(1, filename, "string")
508 expect(2, mode, "string", "nil")
509 expect(3, env, "table", "nil")
510
511 local file = fs.open(filename, "r")
512 if not file then return nil, "File not found" end
513
514 local func, err = load(file.readAll(), "@" .. fs.getName(filename), mode, env)
515 file.close()
516 return func, err
517end
518
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
1000
1001-- Load user settings
1002if fs.exists(".settings") then
1003 settings.load(".settings")
1004end
1005
1006-- Run the shell
1007local ok, err = pcall(parallel.waitForAny,
1008 function()
1009 local sShell
1010 if term.isColour() and settings.get("bios.use_multishell") then
1011 sShell = "rom/programs/advanced/multishell.lua"
1012 else
1013 sShell = "rom/programs/shell.lua"
1014 end
1015 os.run({}, sShell)
1016 os.run({}, "rom/programs/shutdown.lua")
1017 end,
1018 rednet.run
1019)
1020
1021-- If the shell errored, let the user read it.
1022term.redirect(term.native())
1023if not ok then
1024 printError(err)
1025 pcall(function()
1026 term.setCursorBlink(false)
1027 print("Press any key to continue")
1028 os.pullEvent("key")
1029 end)
1030end
1031
1032-- End
1033os.shutdown()