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