· 10 years ago · Sep 28, 2016, 05:36 AM
1-- disable terminate
2local pullEvent = os.pullEvent
3os.pullEvent = os.pullEventRaw
4
5if not term.isColor() then
6 shell.run("clear")
7 print("No color!\nSetup will now exit...")
8 sleep(3)
9 os.shutdown()
10end
11
12 -- returns <disk name>/
13local function getDiskPath()
14 local p = shell.getRunningProgram()
15 p = string.sub(p, 1, string.find(p, "/"))
16 return p
17end
18
19local perm = 0 -- permission level (0: not logged in, 1: guest, 2: user, 3: super)
20local displayName
21local native = {}
22
23local maxUsername, maxPassword = 10, 10
24
25-- paths
26-- 1 ***** User's Renamed File, Named To Be Hidden
27local userStartup = "1userStartupURFNTOBH" -- the name given to the renamed "startup"
28local usersFile = "osData/crd" -- the file containing the users' credentials, is would be awesome to encode this
29local bin = "osData/bin"
30local cache = getDiskPath() .. "cache"
31local tmp = getDiskPath() .. "tmp"
32shell.setAlias("cls", "clear")
33shell.setAlias("del", "delete")
34shell.setAlias("ren", "rename")
35shell.setAlias("shell", "cmd")
36
37local URL = {
38 ['core'] = "http://cc.etk2000.com/alb%C3%BFno/src/core", -- the startup file
39 ['modules'] = "http://cc.etk2000.com/alb%C3%BFno/modules", -- the modules index
40 ['native'] = "http://cc.etk2000.com/alb%C3%BFno/src/native", -- the native file
41 ['version'] = "http://cc.etk2000.com/alb%C3%BFno/version" -- the current version
42}
43
44-- cache
45local w, h = term.getSize()
46local hasOS = fs.exists("osData/.ver")
47
48----------------------
49-- Helper functions --
50----------------------
51
52local sb = term.setBackgroundColor
53local sf = term.setTextColor
54local curb = colors.black
55local curf = colors.white
56
57function term.setBackgroundColor(col)
58 sb(col)
59 curb = col
60end
61
62function term.setTextColor(col)
63 sf(col)
64 curf = col
65end
66
67function term.getBackgroundColor()
68 return curb
69end
70
71function term.getTextColor()
72 return curf
73end
74
75function term.writeCentered(text, fgc, bgc)
76 local x, y = term.getCursorPos()
77 local bg = term.getBackgroundColor()
78 local fg = term.getTextColor()
79 if bgc ~= nil then
80 term.setBackgroundColor(bgc)
81 end
82 if fgc ~= nil then
83 term.setBackgroundColor(fgc)
84 end
85 term.setCursorPos((w - string.len(text) - 1) / 2, y) -- -1 to align text on left side
86 write(text)
87 if bgc ~= nil then
88 term.setBackgroundColor(bg)
89 end
90 if fgc ~= nil then
91 term.setBackgroundColor(fg)
92 end
93end
94
95function term.YN()
96 while true do
97 local x, y = term.getCursorPos()
98 local r = read()
99 if string.lower(string.sub(r, 1, 1)) == 'y' then
100 return true
101 elseif string.lower(string.sub(r, 1, 1)) == 'n' then
102 return false
103 end
104 term.setCursorPos(x, y)
105 for i=1, string.len(r) do
106 write(' ')
107 end
108 term.setCursorPos(x, y)
109 end
110end
111
112function term.writeColored(text, fgc, bgc)
113 local bg = term.getBackgroundColor()
114 local fg = term.getTextColor()
115 if bgc ~= nil then
116 term.setBackgroundColor(bgc)
117 end
118 term.setTextColor(fgc)
119 write(text)
120 if bgc ~= nil then
121 term.setBackgroundColor(bg)
122 end
123 term.setTextColor(fg)
124end
125
126-- term.clear() already exists
127function os.clear()
128 term.clear()
129 term.setCursorPos(1, 1)
130end
131os.clear()
132
133function os.pause()
134 write("Press any key to continue")
135 os.pullEvent("key")
136end
137
138function os.loading(color, func) -- func is a function that returns a boolean (that is all), true means stop
139 local anim = { '|', '/', '-', '\\' }
140 local i = 1;
141 local x, y = term.getCursorPos()
142 while (func == nil and true) or not func() do
143 local x1, y1 = term.getCursorPos()
144 term.setCursorPos(x, y)
145 term.writeColored(anim[i], color ~= nil and color or term.getTextColor())
146 if x1 ~= x and y1 ~= y then
147 term.setCursorPos(x1, y1)
148 end
149 i = i + 1
150 if i > 4 then
151 i = 1
152 end
153 sleep(0.1)
154 end
155end
156
157function os.spinner(color, func)
158 local anim = {{
159 " O ",
160 " O O ",
161 "O O",
162 "O O",
163 " O O ",
164 " O O "
165 }, {
166 " O O ",
167 " O ",
168 "O O",
169 "O O",
170 " O O ",
171 " O O "
172 }, {
173 " O O ",
174 " O O ",
175 "O ",
176 "O O",
177 " O O ",
178 " O O "
179 }, {
180 " O O ",
181 " O O ",
182 "O O",
183 "O ",
184 " O O ",
185 " O O "
186 }, {
187 " O O ",
188 " O O ",
189 "O O",
190 "O O",
191 " O ",
192 " O O "
193 }, {
194 " O O ",
195 " O O ",
196 "O O",
197 "O O",
198 " O O ",
199 " O "
200 }, {
201 " O O ",
202 " O O ",
203 "O O",
204 "O O",
205 " O O ",
206 " O "
207 }, {
208 " O O ",
209 " O O ",
210 "O O",
211 "O O",
212 " O ",
213 " O O "
214 }, {
215 " O O ",
216 " O O ",
217 "O O",
218 " O",
219 " O O ",
220 " O O "
221 }, {
222 " O O ",
223 " O O ",
224 " O",
225 "O O",
226 " O O ",
227 " O O "
228 }, {
229 " O O ",
230 " O ",
231 "O O",
232 "O O",
233 " O O ",
234 " O O "
235 }, {
236 " O ",
237 " O O ",
238 "O O",
239 "O O",
240 " O O ",
241 " O O "
242 }}
243 local frame = 1
244 local sw, sh = string.len(anim[1][1]), #anim[1]
245 while true do
246 for i=1,sh do
247 term.setCursorPos(math.ceil(w / 2 - sw / 2), math.ceil(h / 2) - sh / 2 + i - 1)
248 term.writeColored(anim[frame][i], color ~= nil and color or term.getTextColor())
249 end
250 frame = frame + 1
251 if frame > #anim then
252 frame = 1
253 end
254 sleep(0.075)
255 end
256end
257
258function http.download(url, file)
259 local req = http.get(url)
260 if not req then
261 error("Could not reach " .. url, 2)
262 end
263 local content = req.readAll()
264 if not content then
265 error("Could not connect to " .. url)
266 end
267 f = fs.open(file, "w")
268 f.write(content)
269 f.close()
270end
271
272function string.explode(str, div)
273 if (div == '') then
274 return false
275 end
276 local pos, res = 0, {}
277 -- for each divider found
278 for st,sp in function() return string.find(str, div, pos, true) end do
279 res[#res + 1] = string.sub(str, pos, st - 1) -- Attach chars left of current divider
280 pos = sp + 1 -- Jump past current divider
281 end
282 res[#res + 1] = string.sub(str, pos) -- Attach chars right of last divider
283 return res
284end
285
286function string.last(String, needle)
287 local i=String:match(".*"..needle.."()")
288 if i==nil then return nil else return i-1 end
289end
290
291function string.starts(String, Start)
292 return string.sub(String, 1, string.len(Start)) == Start
293end
294
295function table.contains(tbl, element)
296 for k, v in pairs(tbl) do
297 if v == element then
298 return true
299 end
300 end
301 return false
302end
303
304function os.try(func, ...)
305 return pcall(func, ...)
306end
307
308local function catchTerminate()
309 os.pullEventRaw("terminate")
310end
311
312----------------------
313-- Auth's functions --
314----------------------
315
316-- auth is verified as:
317-- USERNAME
318-- PASSWORD
319-- AUTH_LEVEL
320local function doAuth(username, password)
321 local file = native.open(usersFile, "r")
322 local line = file.readLine() -- read USERNAME
323 while line ~= nil do
324 if string.lower(username) == string.lower(line) then -- validate USERNAME
325 if password == file.readLine() then -- validate PASSWORD
326 local res = file.readLine() -- return PERM_LEVEL
327 file.close()
328 displayName = line
329 return line .. res -- {USERNAME}{AUTH_LEVEL}
330 end
331 else
332 file.readLine() -- skip PASSWORD
333 end
334 file.readLine() -- skip PERM_LEVEL
335 line = file.readLine() -- continue to next USERNAME
336 end
337 file.close()
338 return 0
339end
340
341-- NOTE: returns the completed, result, can cause errors if opening OS files then trying to modify not as OS
342-- NEVER RUN THIS WITHIN parallel.waitForAny WITH OTHER FINITE FUNCTIONS!!! IT MAY CAUSE PERMISSION LEAKS
343local function doAsOS(func, ...)
344 local p = perm
345 perm = 4
346 local ok, res = os.try(func, ...)
347 perm = p
348 return ok, res
349end
350
351local function hasPerms(perm_level)
352 if perm_level < 1 or perm_level > 3 then
353 throw("invalid perm_level, range is 1-3")
354 end
355 return perm >= perm_level
356end
357
358local function createUser(perm_level)
359 if not hasPerms(3) then
360 error("You are not permitted to create users")
361 end
362 if perm_level < 1 or perm_level > 3 then
363 error("Invalid perm_level, range is 1-3")
364 end
365
366 local username
367 local password
368
369 while true do
370 os.clear()
371 write("Username: ")
372 username = io.read()
373 if username:len() <= maxUsername then
374 write("Password: ")
375 password = read('*')
376 if password:len() <= maxPassword then
377 write("Confirm: ")
378 if password == read('*') then
379 break
380 end
381 term.writeColored("Passwords do not match!", colors.red)
382 else
383 term.writeColored("Passwords is too long! (max: " .. maxPassword .. ")", colors.red)
384 end
385 else
386 term.writeColored("Username is too long! (max: " .. maxUsername .. ")", colors.red)
387 end
388 sleep(2)
389 end
390 os.clear()
391 -- save the user
392 fs.makeDir(string.sub(usersFile, 1, string.last(usersFile, '/') - 1))
393 local file = fs.open(usersFile, fs.exists(usersFile) and "a" or "w")
394 file.writeLine(username)
395 file.writeLine(password)
396 file.writeLine(tostring(perm_level))
397 file.close()
398end
399
400-----------------------
401-- backup functions --
402-----------------------
403native.copy = fs.copy
404native.delete = fs.delete
405native.exists = fs.exists
406native.find = fs.find
407native.getSize = fs.getSize
408native.isDir = fs.isDir
409native.isReadOnly = fs.isReadOnly
410native.list = fs.list
411native.makeDir = fs.makeDir
412native.move = fs.move
413native.open = fs.open
414native.ioOpen = io.open
415
416----------------------
417-- define functions --
418----------------------
419
420function table.size(t)
421 local res = 0
422 for k,v in pairs(t) do
423 res = res + 1
424 end
425 return res
426end
427
428-- ordered pairs
429function __genOrderedIndex( t )
430 local orderedIndex = {}
431 for key in pairs(t) do
432 table.insert(orderedIndex, key)
433 end
434 table.sort(orderedIndex)
435 return orderedIndex
436end
437
438function orderedNext(t, state)
439 -- Equivalent of the next function, but returns the keys in the alphabetic
440 -- order. We use a temporary ordered key table that is stored in the
441 -- table being iterated.
442 key = nil
443 if state == nil then
444 -- the first time, generate the index
445 t.__orderedIndex = __genOrderedIndex(t)
446 key = t.__orderedIndex[1]
447 else
448 -- fetch the next value
449 for i = 1, table.getn(t.__orderedIndex) do
450 if t.__orderedIndex[i] == state then
451 key = t.__orderedIndex[i + 1]
452 end
453 end
454 end
455
456 if key then
457 return key, t[key]
458 end
459
460 -- no more value to return, cleanup
461 t.__orderedIndex = nil
462 return
463end
464
465-- Equivalent of the pairs() function on tables. Allows to iterate in order
466function orderedPairs(t)
467 return orderedNext, t, nil
468end
469
470-- moves the specified file replacing any existing one
471function fs.replace(from, to)
472 if shell.getRunningProgram() == getDiskPath() .. "startup" or shell.getRunningProgram() == (bin .. "/apt-get") then -- a little hack to allow apt-get to update system files
473 if native.exists(to) then
474 native.delete(to)
475 end
476 native.move(from, to)
477 else
478 if fs.exists(to) then
479 fs.delete(to)
480 end
481 fs.move(from, to)
482 end
483 end
484
485-- copies the specified file replacing any existing one
486function fs.replaceCopy(from, to)
487 if shell.getRunningProgram() == getDiskPath() .. "startup" then
488 if native.exists(to) then
489 native.delete(to)
490 end
491 native.copy(from, to)
492 else
493 if fs.exists(to) then
494 fs.delete(to)
495 end
496 fs.copy(from, to)
497 end
498 end
499
500local downOS = false
501local function downloadOS() -- download OS core
502 downOS = true
503 local downloaded
504 parallel.waitForAny(
505 function()
506 local res
507 downloaded, res = doAsOS(http.download, URL['version'], tmp .. "/ver")
508 if not downloaded then
509 res = string.sub(res, 8, string.len(res)) -- remove the "pcall: "
510 local f = string.sub(URL['version'], 1, 7) -- get the beginning of the URL
511 res = string.sub(res, 1, string.find(res, f) - 1) -- remove the URL
512 res = res .. 'update URL!'
513 term.writeColored(res, colors.red)
514 end
515 end,
516 function()
517 catchTerminate()
518 term.writeColored("cancelled", colors.red)
519 downloaded = false
520 end,
521 function()
522 os.loading(colors.blue)
523 end)
524
525 write("\n") -- just to make it look better
526 local file = fs.open(tmp .. "/ver", "r") -- basically doAsOS
527 local version = tonumber(file.readLine()) -- only one line
528 file.close()
529 native.delete(tmp .. "/ver")
530 if downloaded then
531 if os.getVersion() == 0 then
532 local ok, res = doAsOS(http.download, URL['core'], cache .. "/startup") -- download the OS
533 if not ok then
534 term.writeColored("Failed to download OS!\n", colors.red)
535 term.writeColored(res .. "\n", colors.red)
536 native.delete(cache)
537 else
538 ok, res = doAsOS(http.download, URL['native'], cache .. "/native") -- download the OS
539 if not ok then
540 term.writeColored("Failed to download OS!\n", colors.red)
541 term.writeColored(res .. "\n", colors.red)
542 native.delete(cache)
543 else
544 file = native.open(cache .. "/ver", "w")
545 file.write("[core]=" .. tostring(version))
546 file.close()
547 end
548 end
549 elseif version > os.getVersion() then
550 term.writeColored("A new version (" .. version .. ") is available!\n", colors.yellow)
551 term.writeColored("Update [Y/n]? ", colors.cyan)
552 local c = term.getTextColor()
553 term.setTextColor(colors.orange)
554 local a = term.YN()
555 term.setTextColor(c)
556 if a then
557 local ok, res = doAsOS(http.download, URL['core'], cache .. "/startup") -- download the OS
558 if not ok then
559 term.writeColored("Failed to download OS!\n", colors.red)
560 term.writeColored(res .. "\n", colors.red)
561 end
562 end
563 else
564 term.writeColored("OS is up to date!\n", colors.green)
565 end
566 end
567 downOS = false
568end
569
570local moduleNames, modules = { "add-apt-repository", "apt-get", "cmd", "config", "explorer", "su", "sudo" }, {}
571local function downloadModule(moduleName)
572 local url = modules["|base"]:gsub("%$name", moduleName)
573 local downloaded
574 parallel.waitForAny(
575 function()
576 local res
577 downloaded, res = doAsOS(http.download, url, cache .. "/" .. moduleName)
578 if not downloaded then
579 res = string.sub(res, 8, string.len(res)) -- remove the "pcall: "
580 local f = string.sub(url, 1, 7) -- get the beginning of the URL
581 res = string.sub(res, 1, string.find(res, f) - 1) -- remove the URL
582 res = res .. 'update URL!' .. url .. "\n"
583 term.writeColored(res, colors.red)
584 else
585 local arr = {}
586 local found = false
587 local p = perm
588 perm = 4 -- doAsOS for io.lines
589 for line in io.lines(cache .. "/ver") do
590 if string.starts(line, "[" .. moduleName .. "]=") then
591 table.insert(arr, "[" .. moduleName .. "]=" .. modules[moduleName]["ver"]);
592 found = true
593 else
594 table.insert(arr, line);
595 end
596 end
597 perm = p
598 if not found then -- newly installed
599 table.insert(arr, "[" .. moduleName .. "]=" .. modules[moduleName]["ver"]);
600 end
601
602 file = native.open(cache .. "/ver", "w")
603 for k, v in pairs(arr) do
604 file.writeLine(v)
605 end
606 file.close()
607 end
608 end,
609 function()
610 catchTerminate()
611 term.writeColored("cancelled", colors.red)
612 downloaded = false
613 end,
614 function()
615 os.loading(colors.blue)
616 end)
617end
618
619local function removeModule(moduleName)
620 local arr = {}
621 local p = perm
622 native.delete(cache .. "/" .. moduleName)
623 perm = 4 -- doAsOS for io.lines
624 for line in io.lines(cache .. "/ver") do
625 if not string.starts(line, "[" .. moduleName .. "]=") then
626 table.insert(arr, line);
627 end
628 end
629 perm = p
630
631 file = native.open(cache .. "/ver", "w")
632 for k, v in pairs(arr) do
633 file.writeLine(v)
634 end
635 file.close()
636end
637
638local function getModules()
639 local downloaded
640 parallel.waitForAny(
641 function()
642 local res
643 downloaded, res = os.try(http.download, URL['modules'], tmp .. "/mod")
644 if not downloaded then
645 res = string.sub(res, 8, string.len(res)) -- remove the "pcall: "
646 local f = string.sub(URL['modules'], 1, 7) -- get the beginning of the URL
647 res = string.sub(res, 1, string.find(res, f) - 1) -- remove the URL
648 res = res .. 'update URL!'
649 term.writeColored(res, colors.red)
650 end
651 end,
652 function()
653 catchTerminate()
654 term.writeColored("cancelled", colors.red)
655 downloaded = false
656 end,
657 function()
658 os.loading(colors.blue)
659 end)
660
661 -- apt-get update
662 local file = fs.open(tmp .. "/_m", "w")
663 local fine, base, form = true
664 for line in io.lines(tmp .. "/mod") do
665 if not string.starts(line, "#") then -- ignore comments
666 if fine then
667 if string.starts(line, '[basepath]=') then
668 base = string.sub(line, 12, string.len(line))
669 file.write(base .. "\n")
670 elseif string.starts(line, '[format]=') then
671 form = string.explode(string.sub(line, 10, string.len(line)), ",")
672 for i=1, #form do
673 if not fine or not string.starts(form[i], "$") then
674 fine = false
675 else
676 form[i] = string.sub(form[i], 2, string.len(form[i]))
677 end
678 end
679 else
680 if base and form then
681 local mod = string.explode(line, ",")
682 for i=#mod, 1, -1 do
683 mod[form[i]] = mod[i]
684 table.remove(mod, i)
685 end
686 if table.contains(moduleNames, mod["name"]) then
687 file.write("\t" .. mod["name"] .. "\n")
688 for k,v in pairs(mod) do
689 if k ~= "name" then
690 file.write("\t\t" .. k .. "=" .. v .. "\n")
691 end
692 end
693 end
694 else
695 fine = false
696 end
697 end
698 end
699 end
700 end
701 file.close()
702 fs.delete(tmp .. "/mod")
703
704 local repo, curM = {}
705 for line in io.lines(tmp .. "/_m") do
706 if string.starts(line, "\t\t") then
707 local v = string.explode(string.sub(line, 3, string.len(line)), "=") -- should only produce string[2]
708 repo[curM][v[1]] = v[2]
709 elseif string.starts(line, "\t") then -- found a module definition
710 curM = string.sub(line, 2, string.len(line))
711 repo[curM] = {}
712 else
713 repo["|base"] = string.sub(line, 1, string.len(line))
714 end
715 end
716 modules = repo
717 for k, v in pairs(repo) do
718 if v["req"] == '1' and not fs.exists(cache .. "/" .. k) then
719 os.clear()
720 downloadModule(k)
721 end
722 end
723 os.clear()
724end
725
726function os.getName()
727 return "Albyno"--"Alb??no"
728end
729
730function os.getVersion()
731 if not native.exists(cache .. "/ver") then
732 if downOS then
733 return 0
734 end
735 downloadOS()
736 end
737 local r
738 while not r do
739 local p = perm
740 perm = 4 -- doAsOS for io.lines
741 for line in io.lines(cache .. "/ver") do
742 if string.starts(line, "[core]=") then
743 r = tonumber(string.sub(line, 8, string.len(line)))
744 end
745 end
746 perm = p
747 if not r then -- corrupted data, re-download the OS core
748 downloadOS()
749 end
750 end
751 return r
752end
753
754function os.version()
755 return os.getName() .. " " .. os.getVersion() .. " setup"
756end
757
758function doLogin()
759 if not native.exists(usersFile) then -- no users, create one
760 term.writeColored("Please create an account", colors.green)
761 sleep(2)
762 perm = 3 -- allow user creation
763 createUser(3) -- create an admin
764 perm = 0
765 else
766 while perm < 3 do -- while not loggedin, try to login
767 os.clear()
768 term.writeColored("A user exists, Please login!\n", colors.green)
769 write("Username: ")
770 username = io.read()
771 write("Password: ")
772 local password = read('*')
773 perm = doAuth(username, password)
774 if perm ~= 0 then
775 username = string.sub(perm, 1, string.len(perm) - 1)
776 perm = tonumber(string.sub(perm, string.len(perm) - 1, string.len(perm)))
777 if perm == 3 then
778 term.writeColored("Login successful!\n", colors.green)
779 os.getPermLevel = function()
780 return perm
781 end
782 os.getUsername = function()
783 return username
784 end
785 else
786 term.writeColored("Admin required!\n", colors.red)
787 end
788 else
789 term.writeColored("Login failure!\n", colors.red)
790 end
791 sleep(1)
792 end
793 end
794 os.clear()
795end
796
797function configure()
798 getModules()
799 local vers = {}
800 for k, v in pairs(moduleNames) do
801 vers[v] = false
802 end
803
804 local p = perm
805 perm = 4 -- doAsOS for io.lines
806 for line in io.lines(cache .. "/ver") do
807 for k, v in pairs(vers) do
808 if not v and string.starts(line, "[" .. k .. "]=") and fs.exists(cache .. "/" .. k) then
809 vers[k] = tonumber(string.sub(line, 4 + string.len(k), string.len(line)))
810 end
811 end
812 end
813 perm = p
814
815 lastColor = term.getBackgroundColor()
816 term.setCursorPos(1, 1)
817 term.writeColored("core", colors.green)
818 term.writeCentered("" .. os.getVersion())
819 term.setCursorPos(w - 9, 1)
820 term.setBackgroundColor(colors.blue)
821 write("redownload")
822 term.setBackgroundColor(lastColor)
823 term.setCursorPos(1, 2)
824 term.writeColored("modules:", colors.cyan)
825 for i, v in ipairs(moduleNames) do
826 term.setCursorPos(4, 2 + i)
827 term.writeColored(v, vers[v] and colors.green or colors.red)
828 if vers[v] then
829 term.writeCentered(vers[v])
830 end
831 term.setBackgroundColor(vers[v] and (modules[v]["req"] == '1' and colors.blue or colors.red) or colors.green)
832 term.setCursorPos(w - 9, 2 + i)
833 write(vers[v] and (modules[v]["req"] == '1' and "redownload" or " remove ") or " download ")
834 term.setBackgroundColor(lastColor)
835 end
836 term.setCursorPos(w, h)
837 term.writeColored("x", colors.red)
838
839 -- wait for input
840 parallel.waitForAny(
841 function()
842 local event, button, xPos, yPos = pullEvent("mouse_click")
843 while yPos ~= h or xPos ~= w do
844 -- do some calculations
845 if yPos == 1 and xPos >= w - 9 then
846 os.clear()
847 downloadOS()
848 sleep(2)
849 os.clear()
850 configure()
851 break
852 elseif yPos > 2 and yPos <= 2 + table.size(vers) and xPos >= w - 9 then
853 local i, m = 1
854 for k, v in orderedPairs(vers) do
855 if i == (yPos - 2) then
856 m = k
857 end
858 i = i + 1
859 end
860 os.clear()
861 if not vers[m] or modules[m]["req"] == '1' then
862 write("downloading...")
863 downloadModule(m)
864 else
865 removeModule(m)
866 end
867 os.clear()
868 configure()
869 break
870 end
871 event, button, xPos, yPos = pullEvent("mouse_click")
872 end
873 end)
874end
875
876----------------------
877-- Main entry point --
878----------------------
879term.setTextColor(colors.white)
880
881-- no OS cached, download is required
882while not native.exists(cache) do
883 term.writeColored("No OS found on disk!\n", colors.red)
884 term.writeColored("Downloading...", colors.orange)
885 downloadOS()
886 getModules()
887end
888
889-- show UI
890local ok, res = os.try(
891 function()
892 while true do
893 os.clear()
894 if w > 22 then
895 local width = math.floor(w / 2 - 2)
896 local lastColor = term.getBackgroundColor()
897 for i=1, 10 do
898 term.setBackgroundColor(colors.red)
899 term.setCursorPos(3, math.ceil(h / 2 - 6 + i))
900 for j=1, width do
901 if i == 5 and j >= math.ceil(width / 2 - 3) and j < math.ceil(width / 2 - 3) + 7 then
902 write(string.sub('Install', j - 2 - width / 2 - 3, j - 2 - width / 2 - 3))
903 else
904 write(" ")
905 end
906 end
907 term.setCursorPos(width + 4, math.ceil(h / 2 - 6 + i))
908 for j=1, width do
909 if i == 1 or i == 10 or j == 1 or j == width then
910 term.setBackgroundColor(colors.blue)
911 else
912 term.setBackgroundColor(colors.cyan)
913 end
914 if i == 5 and j >= math.ceil(width / 2 - 4) and j < math.ceil(width / 2 - 3) + 8 then
915 write(string.sub('Configure', j - 2 - width / 2 - 4, j - 2 - width / 2 - 4))
916 else
917 write(" ")
918 end
919 end
920 end
921 if fs.exists(cache .. "/cmd") then
922 term.setCursorPos(1, h)
923 term.setBackgroundColor(colors.brown)
924 write("cmd")
925 end
926 term.setBackgroundColor(lastColor)
927 term.setCursorPos(w, h)
928 lastColor = term.getTextColor()
929 term.setTextColor(colors.red)
930 write("x")
931 term.setTextColor(lastColor)
932
933 -- wait for input
934 parallel.waitForAny(
935 function()
936 local event, button, xPos, yPos = pullEvent("mouse_click")
937 if yPos > math.ceil(h / 2 - 6) and yPos <= (math.ceil(h / 2 - 6) + 10) then
938 if xPos > 2 and xPos <= (2 + width) then -- Install clicked
939 os.clear()
940 term.writeColored("Install [Y/n]? ", colors.orange)
941 if term.YN() then
942 doLogin()
943 local done = false
944 parallel.waitForAny(
945 function()
946 os.spinner(colors.cyan, function() return done end)
947 end,
948 function()
949 if not hasOS and fs.exists("startup") --[[ and not fs.exists(userStartup) ]] then
950 fs.move("startup", userStartup)
951 end
952
953 -- install required packages
954 fs.replaceCopy(cache .. "/startup", "startup")
955 fs.replaceCopy(cache .. "/native", "osData/native")
956 fs.replaceCopy(cache .. "/add-apt-repository", "osData/bin/add-apt-repository")
957 fs.replaceCopy(cache .. "/apt-get", "osData/bin/apt-get")
958 fs.replaceCopy(cache .. "/cmd", "osData/bin/cmd")
959 fs.replaceCopy(cache .. "/config", "osData/bin/config")
960
961 -- install optional packages
962 if fs.exists(cache .. "/explorer") then
963 fs.replaceCopy(cache .. "/explorer", "osData/bin/explorer")
964 end
965 if fs.exists(cache .. "/su") then
966 fs.replaceCopy(cache .. "/su", "osData/bin/su")
967 end
968 if fs.exists(cache .. "/sudo") then
969 fs.replaceCopy(cache .. "/sudo", "osData/bin/sudo")
970 end
971
972 -- write current version
973 file = native.open("osData/.ver", "w")
974 file.write(os.getVersion())
975 file.close()
976
977 -- set the versions of the installed packages in the progs
978 file = native.open("osData/cfg/progs", "w")
979 -- get the module path
980 for line in io.lines(tmp .. "/_m") do
981 if not string.starts(line, "#") and not string.starts(line, "\t") then
982 file.write(line .. "\n")
983 break
984 end
985 end
986 -- get the installed programs
987 for line in io.lines(cache .. "/ver") do
988 if not string.starts(line, "[core]") then
989 local i = string.find(line, "%]")
990 file.write("\t" .. string.sub(line, 1, i - 1) .. "\n")
991 file.write("\t\t" .. string.sub(line, i + 1) .. "\n")
992 break
993 end
994 end
995 file.close()
996
997 hasOS = true
998 done = true
999 end)
1000 else
1001 term.writeColored("Cancelled...\n", colors.red)
1002 end
1003 sleep(1)
1004 elseif xPos > (3 + width) and xPos < (w - 1) then
1005 os.clear()
1006 configure()
1007 end
1008 elseif yPos == h and xPos < 4 and fs.exists(cache .. "/cmd") then
1009 os.clear()
1010 parallel.waitForAny(catchTerminate,
1011 function()
1012 shell.run(cache .. "/cmd")
1013 -- TODO: fix cmd crashing after entering command
1014 end)
1015 term.setCursorBlink(false)
1016 elseif yPos == h and xPos == w then
1017 os.queueEvent("terminate")
1018 end
1019 end)
1020 else
1021 end
1022 end
1023 end)
1024if not ok then
1025 if res == "Terminated" then
1026 os.clear()
1027 term.writeColored("Bye bye!\n", colors.yellow)
1028 else
1029 term.writeColored(res .. "\n", colors.red)
1030 end
1031 os.pause()
1032 os.shutdown()
1033end