turtle5204

future deltaOS installer

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