1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-07-01 01:23:30 +00:00
CC-Tweaked/src/main/resources/assets/computercraft/lua/rom/programs/shell

370 lines
9.7 KiB
Plaintext
Raw Normal View History

local multishell = multishell
local parentShell = shell
local parentTerm = term.current()
if multishell then
multishell.setTitle( multishell.getCurrent(), "shell" )
end
local bExit = false
local sDir = (parentShell and parentShell.dir()) or ""
local sPath = (parentShell and parentShell.path()) or ".:/rom/programs"
local tAliases = (parentShell and parentShell.aliases()) or {}
local tCompletionInfo = (parentShell and parentShell.getCompletionInfo()) or {}
local tProgramStack = {}
local shell = {}
local tEnv = {
[ "shell" ] = shell,
[ "multishell" ] = multishell,
}
-- Colours
local promptColour, textColour, bgColour
if term.isColour() then
promptColour = colours.yellow
textColour = colours.white
bgColour = colours.black
else
promptColour = colours.white
textColour = colours.white
bgColour = colours.black
end
local function run( _sCommand, ... )
local sPath = shell.resolveProgram( _sCommand )
if sPath ~= nil then
tProgramStack[#tProgramStack + 1] = sPath
if multishell then
multishell.setTitle( multishell.getCurrent(), fs.getName( sPath ) )
end
local result = os.run( tEnv, sPath, ... )
tProgramStack[#tProgramStack] = nil
if multishell then
if #tProgramStack > 0 then
multishell.setTitle( multishell.getCurrent(), fs.getName( tProgramStack[#tProgramStack] ) )
else
multishell.setTitle( multishell.getCurrent(), "shell" )
end
end
return result
else
printError( "No such program" )
return false
end
end
local function tokenise( ... )
local sLine = table.concat( { ... }, " " )
local tWords = {}
local bQuoted = false
for match in string.gmatch( sLine .. "\"", "(.-)\"" ) do
if bQuoted then
table.insert( tWords, match )
else
for m in string.gmatch( match, "[^ \t]+" ) do
table.insert( tWords, m )
end
end
bQuoted = not bQuoted
end
return tWords
end
-- Install shell API
function shell.run( ... )
local tWords = tokenise( ... )
local sCommand = tWords[1]
if sCommand then
return run( sCommand, table.unpack( tWords, 2 ) )
end
return false
end
function shell.exit()
bExit = true
end
function shell.dir()
return sDir
end
function shell.setDir( _sDir )
sDir = _sDir
end
function shell.path()
return sPath
end
function shell.setPath( _sPath )
sPath = _sPath
end
function shell.resolve( _sPath )
local sStartChar = string.sub( _sPath, 1, 1 )
if sStartChar == "/" or sStartChar == "\\" then
return fs.combine( "", _sPath )
else
return fs.combine( sDir, _sPath )
end
end
local function pathWithExtension( _sPath, _sExt )
local nLen = #sPath
local sEndChar = string.sub( _sPath, nLen, nLen )
-- Remove any trailing slashes so we can add an extension to the path safely
if sEndChar == "/" or sEndChar == "\\" then
_sPath = string.sub( _sPath, 1, nLen - 1 )
end
return _sPath .. "." .. _sExt
end
function shell.resolveProgram( _sCommand )
-- Substitute aliases firsts
if tAliases[ _sCommand ] ~= nil then
_sCommand = tAliases[ _sCommand ]
end
-- If the path is a global path, use it directly
local sStartChar = string.sub( _sCommand, 1, 1 )
if sStartChar == "/" or sStartChar == "\\" then
local sPath = fs.combine( "", _sCommand )
if fs.exists( sPath ) and not fs.isDir( sPath ) then
return sPath
else
local sPathLua = pathWithExtension( sPath, "lua" )
if fs.exists( sPathLua ) and not fs.isDir( sPathLua ) then
return sPathLua
end
end
return nil
end
-- Otherwise, look on the path variable
for sPath in string.gmatch(sPath, "[^:]+") do
sPath = fs.combine( shell.resolve( sPath ), _sCommand )
if fs.exists( sPath ) and not fs.isDir( sPath ) then
return sPath
else
local sPathLua = pathWithExtension( sPath, "lua" )
if fs.exists( sPathLua ) and not fs.isDir( sPathLua ) then
return sPathLua
end
end
end
-- Not found
return nil
end
function shell.programs( _bIncludeHidden )
local tItems = {}
-- Add programs from the path
for sPath in string.gmatch(sPath, "[^:]+") do
sPath = shell.resolve( sPath )
if fs.isDir( sPath ) then
local tList = fs.list( sPath )
for n=1,#tList do
local sFile = tList[n]
if not fs.isDir( fs.combine( sPath, sFile ) ) and
(_bIncludeHidden or string.sub( sFile, 1, 1 ) ~= ".") then
tItems[ sFile ] = true
end
end
end
end
-- Sort and return
local tItemList = {}
for sItem, b in pairs( tItems ) do
table.insert( tItemList, sItem )
end
table.sort( tItemList )
return tItemList
end
local function completeProgram( sLine )
if #sLine > 0 and string.sub( sLine, 1, 1 ) == "/" then
-- Add programs from the root
return fs.complete( sLine, "", true, false )
else
local tResults = {}
local tSeen = {}
-- Add aliases
for sAlias, sCommand in pairs( tAliases ) do
if #sAlias > #sLine and string.sub( sAlias, 1, #sLine ) == sLine then
local sResult = string.sub( sAlias, #sLine + 1 )
if not tSeen[ sResult ] then
table.insert( tResults, sResult )
tSeen[ sResult ] = true
end
end
end
-- Add programs from the path
local tPrograms = shell.programs()
for n=1,#tPrograms do
local sProgram = tPrograms[n]
if #sProgram > #sLine and string.sub( sProgram, 1, #sLine ) == sLine then
local sResult = string.sub( sProgram, #sLine + 1 )
if not tSeen[ sResult ] then
table.insert( tResults, sResult )
tSeen[ sResult ] = true
end
end
end
-- Sort and return
table.sort( tResults )
return tResults
end
end
local function completeProgramArgument( sProgram, nArgument, sPart, tPreviousParts )
local tInfo = tCompletionInfo[ sProgram ]
if tInfo then
return tInfo.fnComplete( shell, nArgument, sPart, tPreviousParts )
end
return nil
end
function shell.complete( sLine )
if #sLine > 0 then
local tWords = tokenise( sLine )
local nIndex = #tWords
if string.sub( sLine, #sLine, #sLine ) == " " then
nIndex = nIndex + 1
end
if nIndex == 1 then
local sBit = tWords[1] or ""
local sPath = shell.resolveProgram( sBit )
if tCompletionInfo[ sPath ] then
return { " " }
else
local tResults = completeProgram( sBit )
for n=1,#tResults do
local sResult = tResults[n]
local sPath = shell.resolveProgram( sBit .. sResult )
if tCompletionInfo[ sPath ] then
tResults[n] = sResult .. " "
end
end
return tResults
end
elseif nIndex > 1 then
local sPath = shell.resolveProgram( tWords[1] )
local sPart = tWords[nIndex] or ""
local tPreviousParts = tWords
tPreviousParts[nIndex] = nil
return completeProgramArgument( sPath , nIndex - 1, sPart, tPreviousParts )
end
end
return nil
end
function shell.completeProgram( sProgram )
return completeProgram( sProgram )
end
function shell.setCompletionFunction( sProgram, fnComplete )
tCompletionInfo[ sProgram ] = {
fnComplete = fnComplete
}
end
function shell.getCompletionInfo()
return tCompletionInfo
end
function shell.getRunningProgram()
if #tProgramStack > 0 then
return tProgramStack[#tProgramStack]
end
return nil
end
function shell.setAlias( _sCommand, _sProgram )
tAliases[ _sCommand ] = _sProgram
end
function shell.clearAlias( _sCommand )
tAliases[ _sCommand ] = nil
end
function shell.aliases()
-- Copy aliases
local tCopy = {}
for sAlias, sCommand in pairs( tAliases ) do
tCopy[sAlias] = sCommand
end
return tCopy
end
if multishell then
function shell.openTab( ... )
local tWords = tokenise( ... )
local sCommand = tWords[1]
if sCommand then
local sPath = shell.resolveProgram( sCommand )
if sPath == "rom/programs/shell" then
return multishell.launch( tEnv, sPath, table.unpack( tWords, 2 ) )
elseif sPath ~= nil then
return multishell.launch( tEnv, "rom/programs/shell", sCommand, table.unpack( tWords, 2 ) )
else
printError( "No such program" )
end
end
end
function shell.switchTab( nID )
multishell.setFocus( nID )
end
end
local tArgs = { ... }
if #tArgs > 0 then
-- "shell x y z"
-- Run the program specified on the commandline
shell.run( ... )
else
-- "shell"
-- Print the header
term.setBackgroundColor( bgColour )
term.setTextColour( promptColour )
print( os.version() )
term.setTextColour( textColour )
-- Run the startup program
if parentShell == nil then
shell.run( "/rom/startup" )
end
-- Read commands and execute them
local tCommandHistory = {}
while not bExit do
term.redirect( parentTerm )
term.setBackgroundColor( bgColour )
term.setTextColour( promptColour )
write( shell.dir() .. "> " )
term.setTextColour( textColour )
local sLine
if settings.get( "shell.autocomplete" ) then
sLine = read( nil, tCommandHistory, shell.complete )
else
sLine = read( nil, tCommandHistory )
end
table.insert( tCommandHistory, sLine )
shell.run( sLine )
end
end