added os.getenv and os.setenv; cleaned up and patched redirect parsing; added variable expansion and groundwork for further stuff to shell (contributed by @Wobbo)

This commit is contained in:
Florian Nücke 2014-02-17 02:13:26 +01:00
parent 0c37b69422
commit 183f00b398
3 changed files with 152 additions and 43 deletions

View File

@ -13,7 +13,7 @@ for i = 1, #dirs do
if i > 1 then
io.write("\n")
end
io.write("/", path, ":\n")
io.write(path, ":\n")
end
local list, reason = fs.list(path)
if not list then

View File

@ -4,6 +4,8 @@ local fs = require("filesystem")
local shell = require("shell")
local unicode = require("unicode")
local env = {}
os.execute = function(command)
if not command then
return type(shell) == "table"
@ -15,6 +17,17 @@ function os.exit()
error("terminated", 0)
end
function os.getenv(varname)
return env[varname]
end
function os.setenv(varname, value)
checkArg(1, varname, "string")
checkArg(2, value, "string", "nil")
env[varname] = value
return env[varname]
end
function os.remove(...)
return fs.remove(...)
end

View File

@ -61,65 +61,158 @@ local function findFile(name, ext)
return false
end
local function resolveAlias(program, args)
local lastProgram = nil
function expandVars(token)
local name = nil
local special = false
local ignore = false
local ignoreChar =''
local escaped = false
local lastEnd = 1
local doubleQuote = false
local singleQuote = false
local endToken = {}
for i = 1, unicode.len(token) do
local char = unicode.sub(token, i, i)
if escaped then
if name then
table.insert(name, char)
end
escaped = false
elseif char == '\\' then
escaped = not escaped
table.insert(endToken, unicode.sub(token, lastEnd, i-1))
lastEnd = i+1
elseif char == '"' and not singleQuote then
doubleQuote = not doubleQuote
table.insert(endToken, unicode.sub(token, lastEnd, i-1))
lastEnd = i+1
elseif char == "'" and not doubleQuote then
singleQuote = not singleQuote
table.insert(endToken, unicode.sub(token, lastEnd, i-1))
lastEnd = i+1
elseif char == "$" and not doubleQuote and not singleQuote then
if name then
ignore = true
else
name = {}
table.insert(endToken, unicode.sub(token, lastEnd, i-1))
end
elseif char == '{' and #name == 0 then
if ignore and ignoreChar == '' then
ignoreChar = '}'
else
special = true
end
elseif char == '(' and ignoreChar == '' then
ignoreChar = ')'
elseif char == '`' and special then
ignore = true
ignoreChar = '`'
elseif char == '}' and not ignore and not doubleQuote and not singleQuote then
table.insert(endToken, os.getenv(table.concat(name)))
name = nil
lastEnd = i+1
elseif char == '"' and not singleQuote then
doubleQuote = not doubleQuote
elseif char == "'" and not doubleQuote then
singleQuote = not singleQuote
elseif name and (char:match("[%a%d_]") or special) then
if char:match("%d") and #name == 0 then
error "Identifiers can't start with a digit!"
end
table.insert(name, char)
elseif char == ignoreChar and ignore then
ignore = false
ignoreChar = ''
elseif name then -- We are done with gathering the name
table.insert(endToken, os.getenv(table.concat(name)))
name = nil
lastEnd = i
end
end
if name then
table.insert(endToken, os.getenv(table.concat(name)))
name = nil
else
table.insert(endToken, unicode.sub(token, lastEnd, -1))
end
return table.concat(endToken)
end
local function resolveAlias(tokens)
local program, lastProgram = tokens[1], nil
table.remove(tokens, 1)
while true do
local alias = text.tokenize(shell.getAlias(program) or program)
if alias[1] == lastProgram then
program = alias[1]
if program == lastProgram then
break
end
lastProgram = program
program = alias[1]
if args then
for i = 2, #alias do
table.insert(args, alias[i])
end
table.remove(alias, 1)
for i = 1, #tokens do
table.insert(alias, tokens[i])
end
tokens = alias
end
return program
return program, tokens
end
local function parseCommand(tokens)
local program, args, input, output = tokens[1], {}, nil, nil
if not program then
if #tokens == 0 then
return
end
program = resolveAlias(program, args)
-- Variable expansion for all command parts.
for i = 1, #tokens do
tokens[i] = expandVars(tokens[i])
end
-- Resolve alias for command.
local program, args = resolveAlias(tokens)
-- Find redirects.
local input, output, mode = nil, nil, "write"
tokens = args
args = {}
local function smt(call) -- state metatable factory
local function index(_, token)
if token == "<" or token == ">" or token == ">>" then
return "parse error near " .. token
end
call(token)
return "args" -- default, return to normal arg parsing
end
return {__index=index}
end
local sm = { -- state machine for redirect parsing
args = setmetatable({["<"]="input", [">"]="output", [">>"]="append"},
smt(function(token)
table.insert(args, token)
end)),
input = setmetatable({}, smt(function(token)
input = token
end)),
output = setmetatable({}, smt(function(token)
output = token
mode = "write"
end)),
append = setmetatable({}, smt(function(token)
output = token
mode = "append"
end))
}
-- Run state machine over tokens.
local state = "args"
for i = 2, #tokens do
if state == "args" then
if tokens[i] == "<" then
state = "input"
elseif tokens[i] == ">" then
state = "output"
elseif tokens[i] == ">>" then
state = "append"
else
table.insert(args, tokens[i])
end
elseif state == "input" then
if tokens[i] == ">" then
if not input then
return nil, "parse error near '>'"
end
state = "output"
elseif tokens[i] == ">>" then
if not input then
return nil, "parse error near '>>'"
end
state = "append"
elseif not input then
input = tokens[i]
end
elseif state == "output" or state == "append" then
if not output then
output = tokens[i]
end
for i = 1, #tokens do
local token = tokens[i]
state = sm[state][token]
if not sm[state] then
return nil, state
end
end
return program, args, input, output, state
return program, args, input, output, mode
end
local function parseCommands(command)
@ -146,6 +239,9 @@ local function parseCommands(command)
for i = 1, #commands do
commands[i] = table.pack(parseCommand(commands[i]))
if commands[i][1] == nil then
return nil, commands[i][2]
end
end
return commands