Advertisement
Vapio

TestOS

Aug 26th, 2023
1,140
0
Never
1
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 20.60 KB | None | 0 0
  1. tArgs = {...}
  2.  
  3. if not term.isColor() then
  4.     error('OneOS Requires an Advanced (gold) Computer')
  5. end
  6.  
  7. _jstr = [[
  8.     local base = _G
  9.  
  10.     -----------------------------------------------------------------------------
  11.     -- Module declaration
  12.     -----------------------------------------------------------------------------
  13.  
  14.     -- Public functions
  15.  
  16.     -- Private functions
  17.     local decode_scanArray
  18.     local decode_scanComment
  19.     local decode_scanConstant
  20.     local decode_scanNumber
  21.     local decode_scanObject
  22.     local decode_scanString
  23.     local decode_scanWhitespace
  24.     local encodePath
  25.     local isArray
  26.     local isEncodable
  27.  
  28.     local types =
  29.     {
  30.         string = 'string',
  31.         number = 'number',
  32.         boolean = 'boolean',
  33.         null = 'nil',
  34.         function = 'function',
  35.         table = 'table'
  36.     }
  37.  
  38.     local boolean_values =
  39.     {
  40.         true = 'true',
  41.         false = 'false'
  42.     }
  43.  
  44.     local stringNull = "null"
  45.  
  46.     -----------------------------------------------------------------------------
  47.     -- PUBLIC FUNCTIONS
  48.     -----------------------------------------------------------------------------
  49.     --- Encodes an arbitrary Lua object / variable.
  50.     -- @param v The Lua object / variable to be JSON encoded.
  51.     -- @return String containing the JSON encoding in internal Lua string format (i.e. not unicode)
  52.     function encode (variable)
  53.       -- Handle nil values
  54.       if variable == nil then
  55.         return stringNull
  56.       end
  57.      
  58.       local variableType = base.type(variable)  
  59.  
  60.       -- Handle strings
  61.       if variableType == types.string then    
  62.         return '"' .. encodePath(variable) .. '"'      -- Need to handle encoding in string
  63.       end
  64.      
  65.       -- Handle booleans
  66.       if variableType == types.number or variableType == types.boolean then
  67.         return base.tostring(variable)
  68.       end
  69.      
  70.       -- Handle tables
  71.       if variableType == types.table then
  72.         local rval = {}
  73.         -- Consider arrays separately
  74.         local bArray, maxCount = isArray(variable)
  75.         if bArray then
  76.           for i = 1,maxCount do
  77.             table.insert(rval, encode(variable[i]))
  78.           end
  79.         else -- An object, not an array
  80.           for i,j in base.pairs(variable) do
  81.             if isEncodable(i) and isEncodable(j) then
  82.               table.insert(rval, '"' .. encodePath(i) .. '":' .. encode(j))
  83.             end
  84.           end
  85.         end
  86.         if bArray then
  87.           return '[' .. table.concat(rval,',') ..']'
  88.         else
  89.           return '{' .. table.concat(rval,',') .. '}'
  90.         end
  91.       end
  92.      
  93.       -- Handle null values
  94.       if variableType == types.function and variable == nil then
  95.         return stringNull
  96.       end
  97.      
  98.       base.assert(false,'encode attempt to encode unsupported type ' .. variableType .. ':' .. base.tostring(variable))
  99.     end
  100.  
  101.  
  102.     --- Decodes a JSON string and returns the decoded value as a Lua data structure / value.
  103.     -- @param s The string to scan.
  104.     -- @param [startPos] Optional starting position where the JSON string is located. Defaults to 1.
  105.     -- @param Lua object, number The object that was scanned, as a Lua table / string / number / boolean or nil,
  106.     -- and the position of the first character after
  107.     -- the scanned JSON object.
  108.     function decode(s, startPos)
  109.       startPos = startPos and startPos or 1
  110.       startPos = decode_scanWhitespace(s,startPos)
  111.       base.assert(startPos<=string.len(s), 'Unterminated JSON encoded object found at position in [' .. s .. ']')
  112.       local curChar = string.sub(s,startPos,startPos)
  113.       -- Object
  114.       if curChar == '{' then
  115.         return decode_scanObject(s,startPos)
  116.       end
  117.       -- Array
  118.       if curChar == '[' then
  119.         return decode_scanArray(s,startPos)
  120.       end
  121.       -- Number
  122.       if string.find("+-0123456789.e", curChar, 1, true) then
  123.         return decode_scanNumber(s,startPos)
  124.       end
  125.       -- String
  126.       if curChar == '"' or curChar == "'" then
  127.         return decode_scanString(s,startPos)
  128.       end
  129.       if string.sub(s,startPos,startPos+1) == '/*' then
  130.         return decode(s, decode_scanComment(s,startPos))
  131.       end
  132.       -- Otherwise, it must be a constant
  133.       return decode_scanConstant(s,startPos)
  134.     end
  135.  
  136.     --- The null function allows one to specify a null value in an associative array (which is otherwise
  137.     -- discarded if you set the value with 'nil' in Lua. Simply set t = { first=json.null }
  138.     function null()
  139.       return null -- so json.null() will also return null ;-)
  140.     end
  141.     -----------------------------------------------------------------------------
  142.     -- Internal, PRIVATE functions.
  143.     -- Following a Python-like convention, I have prefixed all these 'PRIVATE'
  144.     -- functions with an underscore.
  145.     -----------------------------------------------------------------------------
  146.  
  147.     --- Scans an array from JSON into a Lua object
  148.     -- startPos begins at the start of the array.
  149.     -- Returns the array and the next starting position
  150.     -- @param s The string being scanned.
  151.     -- @param startPos The starting position for the scan.
  152.     -- @return table, int The scanned array as a table, and the position of the next character to scan.
  153.     function decode_scanArray(s,startPos)
  154.       local array = {}   -- The return value
  155.       local stringLen = string.len(s)
  156.       base.assert(string.sub(s,startPos,startPos) == '[','decode_scanArray called but array does not start at position ' .. startPos .. ' in string:\n'..s )
  157.       startPos = startPos + 1
  158.       -- Infinite loop for array elements
  159.       repeat
  160.         startPos = decode_scanWhitespace(s,startPos)
  161.         base.assert(startPos<=stringLen,'JSON String ended unexpectedly scanning array.')
  162.         local curChar = string.sub(s,startPos,startPos)
  163.         if (curChar == ']') then
  164.           return array, startPos+1
  165.         end
  166.         if (curChar == ',') then
  167.           startPos = decode_scanWhitespace(s,startPos+1)
  168.         end
  169.         base.assert(startPos<=stringLen, 'JSON String ended unexpectedly scanning array.')
  170.         object, startPos = decode(s,startPos)
  171.         table.insert(array,object)
  172.       until false
  173.     end
  174.  
  175.     --- Scans a comment and discards the comment.
  176.     -- Returns the position of the next character following the comment.
  177.     -- @param string s The JSON string to scan.
  178.     -- @param int startPos The starting position of the comment
  179.     function decode_scanComment(s, startPos)
  180.       base.assert( string.sub(s,startPos,startPos+1) == '/*', "decode_scanComment called but comment does not start at position " .. startPos)
  181.       local endPos = string.find(s,'*/',startPos+2)
  182.       base.assert(endPos~=nil, "Unterminated comment in string at " .. startPos)
  183.       return endPos+2  
  184.     end
  185.  
  186.     --- Scans for given constants: true, false or null
  187.     -- Returns the appropriate Lua type, and the position of the next character to read.
  188.     -- @param s The string being scanned.
  189.     -- @param startPos The position in the string at which to start scanning.
  190.     -- @return object, int The object (true, false or nil) and the position at which the next character should be
  191.     -- scanned.
  192.     function decode_scanConstant(s, startPos)
  193.       local consts = { ["true"] = true, ["false"] = false, ["null"] = nil }
  194.       local constNames = {"true","false","null"}
  195.  
  196.       for i,k in base.pairs(constNames) do
  197.         --print ("[" .. string.sub(s,startPos, startPos + string.len(k) -1) .."]", k)
  198.         if string.sub(s,startPos, startPos + string.len(k) -1 ) == k then
  199.           return consts[k], startPos + string.len(k)
  200.         end
  201.       end
  202.       base.assert(nil, 'Failed to scan constant from string ' .. s .. ' at starting position ' .. startPos)
  203.     end
  204.  
  205.     --- Scans a number from the JSON encoded string.
  206.     -- (in fact, also is able to scan numeric +- eqns, which is not
  207.     -- in the JSON spec.)
  208.     -- Returns the number, and the position of the next character
  209.     -- after the number.
  210.     -- @param s The string being scanned.
  211.     -- @param startPos The position at which to start scanning.
  212.     -- @return number, int The extracted number and the position of the next character to scan.
  213.     function decode_scanNumber(s,startPos)
  214.       local endPos = startPos+1
  215.       local stringLen = string.len(s)
  216.       local acceptableChars = "+-0123456789.e"
  217.       while (string.find(acceptableChars, string.sub(s,endPos,endPos), 1, true)
  218.        and endPos<=stringLen
  219.        ) do
  220.         endPos = endPos + 1
  221.       end
  222.       local stringValue = 'return ' .. string.sub(s,startPos, endPos-1)
  223.       local stringEval = base.loadstring(stringValue)
  224.       base.assert(stringEval, 'Failed to scan number [ ' .. stringValue .. '] in JSON string at position ' .. startPos .. ' : ' .. endPos)
  225.       return stringEval(), endPos
  226.     end
  227.  
  228.     --- Scans a JSON object into a Lua object.
  229.     -- startPos begins at the start of the object.
  230.     -- Returns the object and the next starting position.
  231.     -- @param s The string being scanned.
  232.     -- @param startPos The starting position of the scan.
  233.     -- @return table, int The scanned object as a table and the position of the next character to scan.
  234.     function decode_scanObject(s,startPos)
  235.       local object = {}
  236.       local stringLen = string.len(s)
  237.       local key, value
  238.       base.assert(string.sub(s,startPos,startPos) == '{','decode_scanObject called but object does not start at position ' .. startPos .. ' in string:\n' .. s)
  239.       startPos = startPos + 1
  240.       repeat
  241.         startPos = decode_scanWhitespace(s,startPos)
  242.         base.assert(startPos<=stringLen, 'JSON string ended unexpectedly while scanning object.')
  243.         local curChar = string.sub(s,startPos,startPos)
  244.         if (curChar == '}') then
  245.           return object,startPos+1
  246.         end
  247.         if (curChar == ',') then
  248.           startPos = decode_scanWhitespace(s,startPos+1)
  249.         end
  250.         base.assert(startPos<=stringLen, 'JSON string ended unexpectedly scanning object.')
  251.         -- Scan the key
  252.         key, startPos = decode(s,startPos)
  253.         base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key)
  254.         startPos = decode_scanWhitespace(s,startPos)
  255.         base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key)
  256.         base.assert(string.sub(s,startPos,startPos) == ':','JSON object key-value assignment mal-formed at ' .. startPos)
  257.         startPos = decode_scanWhitespace(s,startPos+1)
  258.         base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key)
  259.         value, startPos = decode(s,startPos)
  260.         object[key]=value
  261.       until false  -- infinite loop while key-value pairs are found
  262.     end
  263.  
  264.     --- Scans a JSON string from the opening inverted comma or single quote to the
  265.     -- end of the string.
  266.     -- Returns the string extracted as a Lua string,
  267.     -- and the position of the next non-string character
  268.     -- (after the closing inverted comma or single quote).
  269.     -- @param s The string being scanned.
  270.     -- @param startPos The starting position of the scan.
  271.     -- @return string, int The extracted string as a Lua string, and the next character to parse.
  272.     function decode_scanString(s,startPos)
  273.       base.assert(startPos, 'decode_scanString(..) called without start position')
  274.       local startChar = string.sub(s,startPos,startPos)
  275.       base.assert(startChar == "'" or startChar == '"','decode_scanString called for a non-string')
  276.       local escaped = false
  277.       local endPos = startPos + 1
  278.       local bEnded = false
  279.       local stringLen = string.len(s)
  280.       repeat
  281.         local curChar = string.sub(s,endPos,endPos)
  282.         -- Character escaping is only used to escape the string delimiters
  283.         if not escaped then
  284.           if curChar == '\\' then
  285.             escaped = true
  286.           else
  287.             bEnded = curChar  ==  startChar
  288.           end
  289.         else
  290.           -- If we're escaped, we accept the current character come what may
  291.           escaped = false
  292.         end
  293.         endPos = endPos + 1
  294.         base.assert(endPos <= stringLen+1, "String decoding failed: unterminated string at position " .. endPos)
  295.       until bEnded
  296.       local stringValue = 'return ' .. string.sub(s, startPos, endPos-1)
  297.       local stringEval = base.loadstring(stringValue)
  298.       base.assert(stringEval, 'Failed to load string [ ' .. stringValue .. '] in JSON4Lua.decode_scanString at position ' .. startPos .. ' : ' .. endPos)
  299.       return stringEval(), endPos  
  300.     end
  301.  
  302.     --- Scans a JSON string skipping all whitespace from the current start position.
  303.     -- Returns the position of the first non-whitespace character, or nil if the whole end of string is reached.
  304.     -- @param s The string being scanned
  305.     -- @param startPos The starting position where we should begin removing whitespace.
  306.     -- @return int The first position where non-whitespace was encountered, or string.len(s)+1 if the end of string
  307.     -- was reached.
  308.     function decode_scanWhitespace(s,startPos)
  309.       local whitespace=" \n\r\t"
  310.       local stringLen = string.len(s)
  311.       while ( string.find(whitespace, string.sub(s,startPos,startPos), 1, true)  and startPos <= stringLen) do
  312.         startPos = startPos + 1
  313.       end
  314.       return startPos
  315.     end
  316.  
  317.     --- Encodes a string to be JSON-compatible.
  318.     -- This just involves back-quoting inverted commas, back-quotes and newlines, I think ;-)
  319.     -- @param s The string to return as a JSON encoded (i.e. backquoted string)
  320.     -- @return The string appropriately escaped.
  321.     function encodePath(path)
  322.       path = string.gsub(path,'\\','\\\\')
  323.       path = string.gsub(path,'"','\\"')
  324.       path = string.gsub(path,"'","\\'")
  325.       path = string.gsub(path,'\n','\\n')
  326.       path = string.gsub(path,'\t','\\t')
  327.       return path
  328.     end
  329.  
  330.     -- Determines whether the given Lua type is an array or a table / dictionary.
  331.     -- We consider any table an array if it has indexes 1..n for its n items, and no
  332.     -- other data in the table.
  333.     -- I think this method is currently a little 'flaky', but can't think of a good way around it yet...
  334.     -- @param t The table to evaluate as an array
  335.     -- @return boolean, number True if the table can be represented as an array, false otherwise. If true,
  336.     -- the second returned value is the maximum
  337.     -- number of indexed elements in the array.
  338.     function isArray(array)
  339.       -- Next we count all the elements, ensuring that any non-indexed elements are not-encodable
  340.       -- (with the possible exception of 'n')
  341.       local index_max = 0
  342.       for k,v in base.pairs(array) do
  343.         if (base.type(k) ==  'number' and math.floor(k) == k and 1<=k) then   -- k,v is an indexed pair
  344.           if (not isEncodable(v)) then return false end   -- All array elements must be encodable
  345.           index_max = math.max(index_max,k)
  346.         else
  347.           if (k == 'n') then
  348.             if v ~= table.getn(array) then return false end  -- False if n does not hold the number of elements
  349.           else -- Else of (k == 'n')
  350.             if isEncodable(v) then return false end
  351.           end  -- End of (k~='n')
  352.         end -- End of k,v not an indexed pair
  353.       end  -- End of loop across all pairs
  354.       return true, index_max
  355.     end
  356.  
  357.     --- Determines whether the given Lua object / table / variable can be JSON encoded. The only
  358.     -- types that are JSON encodable are: string, boolean, number, nil, table and json.null.
  359.     -- In this implementation, all other types are ignored.
  360.     -- @param o The object to examine.
  361.     -- @return boolean True if the object should be JSON encoded, false if it should be ignored.
  362.     function isEncodable(variable)
  363.       local variable_type = base.type(variable)
  364.       return variable_type  ==  types.string or
  365.              variable_type  ==  types.boolean or
  366.              variable_type  ==  types.null or
  367.              variable_type  ==  types.table or
  368.              variable_type  ==  types.number or
  369.              variable_type  ==  types.function
  370.     end
  371.  
  372. ]]
  373.  
  374. function load_json()
  375.     local sName = 'JSON'
  376.        
  377.     local tEnv = {}
  378.     setmetatable( tEnv, { __index = _G } )
  379.     local fnAPI, err = loadstring(_jstr)
  380.     if fnAPI then
  381.         setfenv( fnAPI, tEnv )
  382.         fnAPI()
  383.     else
  384.         printError( err )
  385.         return false
  386.     end
  387.    
  388.     local tAPI = {}
  389.     for k,v in pairs( tEnv ) do
  390.         tAPI[k] =  v
  391.     end
  392.    
  393.     _G[sName] = tAPI
  394.     return true
  395. end
  396.  
  397. local main_title = 'OneOS Installer'
  398. local sub_title = 'Please wait...'
  399.  
  400. function Draw()
  401.     sleep(0)
  402.     term.setBackgroundColour(colours.white)
  403.     term.clear()
  404.     local w, file_version = term.getSize()
  405.     term.setTextColour(colours.lightBlue)
  406.     term.setCursorPos(math.ceil((w-#main_title)/2), 8)
  407.     term.write(main_title)
  408.     term.setTextColour(colours.blue)
  409.     term.setCursorPos(math.ceil((w-#sub_title)/2), 10)
  410.     term.write(sub_title)
  411. end
  412.  
  413. Settings = {
  414.     InstallPath = '/', --Where the program's installed, don't always asume root (if it's run under something like OneOS)
  415.     Hidden = false, --Whether or not the update is hidden (doesn't write to the screen), useful for background updates
  416.     GitHubUsername = 'oeed', --Your GitHub username as it appears in the URL
  417.     GitHubRepoName = 'OneOS', --The repo name as it appears in the URL
  418.     DownloadReleases = true, --If true it will download the latest release, otherwise it will download the files as they currently appear
  419.     UpdateFunction = nil, --Sent when something happens (file downloaded etc.)
  420.     TotalBytes = 0, --Do not change this value (especially programatically)!
  421.     DownloadedBytes = 0, --Do not change this value (especially programatically)!
  422.     Status = '',
  423.     SecondaryStatus = '',
  424. }
  425.  
  426. load_json()
  427.  
  428. function download_json(path)
  429.     local _json = http.get(path)
  430.     if not _json then
  431.         error('Could not download: '..path..' Check your connection.')
  432.     end
  433.     return JSON.decode(_json.readAll())
  434. end
  435.  
  436. if http then
  437.     sub_title = 'HTTP enabled, attempting update...'
  438.     Draw()
  439. else
  440.     sub_title = 'HTTP is required to update.'
  441.     Draw()
  442.     error('')
  443. end
  444.  
  445. sub_title = 'Determining Latest Version'
  446. Draw()
  447. local releases = download_json('https://api.github.com/repos/'..Settings.GitHubUsername..'/'..Settings.GitHubRepoName..'/releases')
  448. local latestReleaseTag = releases[1].tag_name
  449. if not tArgs or #tArgs ~= 1 and tArgs[1] ~= 'beta' then
  450.     for i, v in ipairs(releases) do
  451.         if not v.prerelease then
  452.             latestReleaseTag = v.tag_name
  453.             break
  454.         end
  455.     end
  456. end
  457. sub_title = 'Optaining Latest Version URL'
  458. Draw()
  459. local refs = download_json('https://api.github.com/repos/'..Settings.GitHubUsername..'/'..Settings.GitHubRepoName..'/git/refs/tags/'..latestReleaseTag)
  460. local latest_release_sha = refs.object.sha
  461.  
  462. sub_title = 'Downloading File Listing'
  463. Draw()
  464.  
  465. local tree = download_json('https://api.github.com/repos/'..Settings.GitHubUsername..'/'..Settings.GitHubRepoName..'/git/trees/'..latest_release_sha..'?recursive=1').tree
  466.  
  467. local blacklist = {
  468.     '/.gitignore',
  469.     '/README.md',
  470.     '/TODO',
  471.     '/Desktop/.Desktop.settings',
  472.     '/.version'
  473. }
  474.  
  475. function isBlacklisted(path)
  476.     for i, item in ipairs(blacklist) do
  477.         if item  ==  path then
  478.             return true
  479.         end
  480.     end
  481.     return false
  482. end
  483.  
  484. Settings.TotalFiles = 0
  485. Settings.TotalBytes = 0
  486. for i, v in ipairs(tree) do
  487.     if not isBlacklisted(Settings.InstallPath..v.path) and v.size then
  488.         Settings.TotalBytes = Settings.TotalBytes + v.size
  489.         Settings.TotalFiles = Settings.TotalFiles + 1
  490.     end
  491. end
  492.  
  493. Settings.DownloadedBytes = 0
  494. Settings.DownloadedFiles = 0
  495. function downloadBlob(v)
  496.     if isBlacklisted(Settings.InstallPath..v.path) then
  497.         return
  498.     end
  499.     if v.type  ==  'tree' then
  500.         -- sub_title = 'Making folder: '..'/'..Settings.InstallPath..v.path
  501.         Draw()
  502.         fs.makeDir('/'..Settings.InstallPath..v.path)
  503.     else
  504.         -- sub_title = 'Starting download for: '..Settings.InstallPath..v.path
  505.         Draw()
  506.  
  507.         local tries, f = 0
  508.         repeat
  509.             f = http.get(('https://raw.github.com/'..Settings.GitHubUsername..'/'..Settings.GitHubRepoName..'/'..latestReleaseTag..Settings.InstallPath..v.path):gsub(' ','%%20'))
  510.                 if not f then sleep(5) end
  511.                 tries = tries + 1
  512.         until tries > 5 or f
  513.  
  514.         if not f then
  515.             error('Downloading failed, try again. '..('https://raw.github.com/'..Settings.GitHubUsername..'/'..Settings.GitHubRepoName..'/'..latestReleaseTag..Settings.InstallPath..v.path):gsub(' ','%%20'))
  516.         end
  517.  
  518.         local file_version = fs.open('/'..Settings.InstallPath..v.path, 'w')
  519.         file_version.write(f.readAll())
  520.         file_version.close()
  521.         -- sub_title = 'Downloading: ' .. math.floor(100*(Settings.DownloadedBytes/Settings.TotalBytes))..'%'
  522.         sub_title = 'Downloading: ' .. math.floor(100*(Settings.DownloadedFiles/Settings.TotalFiles))..'%' -- using the number of files over the number of bytes actually appears to be more accurate, the connection takes longer than sending the data
  523.         -- sub_title = '('..math.floor(100*(Settings.DownloadedBytes/Settings.TotalBytes))..'%) Downloaded: '..Settings.InstallPath..v.path
  524.         Draw()
  525.         if v.size then
  526.             Settings.DownloadedBytes = Settings.DownloadedBytes + v.size
  527.             Settings.DownloadedFiles = Settings.DownloadedFiles + 1
  528.         end
  529.     end
  530. end
  531.  
  532. local connection_limit = 5
  533. local downloads = {}
  534. for i, v in ipairs(tree) do
  535.     local queueNumber = math.ceil(i / connection_limit)
  536.     if not downloads[queueNumber] then
  537.         downloads[queueNumber] = {}
  538.     end
  539.     table.insert(downloads[queueNumber], function()
  540.         downloadBlob(v)
  541.     end)
  542. end
  543.  
  544. for i, queue in ipairs(downloads) do
  545.     parallel.waitForAll(unpack(queue))
  546. end
  547.  
  548. local file_version = fs.open('/System/.version', 'w')
  549. file_version.write(latestReleaseTag)
  550. file_version.close()
  551.  
  552. main_title = 'Installation Complete!'
  553. sub_title = 'Rebooting in 1 second...'
  554. Draw()
  555. sleep(1)
  556. os.reboot()
Advertisement
Comments
Add Comment
Please, Sign In to add comment
Advertisement