Перейти к содержимому

1kovand1

Пользователи
  • Публикации

    18
  • Зарегистрирован

  • Посещение

Сообщения, опубликованные пользователем 1kovand1


  1. Я на днях решил написать небольшой прототип проги для удаленного доступа, которая слушает события modem_message, и исполняет их содержимое, перенаправляя выход в ту же сетевую карту.

     

    У меня получился такой код(Полностью рабочий)

    local s = require"shell"
    local e = require"event"
    local m = require("component").modem
    m.open(512)
     
    env = _G
     
    function env.io.stdout:write(...)
    m.broadcast(512, ...)  
    end
     
     
    while true do
    args = {e.pull("modem_message")}
    s.execute(args[6], env)
    end
    

    Потом, недолго думая, я решил вынести это добро в отдельный поток, который создается в дефолтном шелле bin/sh.lua

    Вот такое дело у меня получилось

     

     

    local component = require("component")
    local computer = require("computer")
    local event = require("event")
    local fs = require("filesystem")
    local process = require("process")
    local shell = require("shell")
    local term = require("term")
    local text = require("text")
    local unicode = require("unicode")
    
    -------------------------------------------------------------------------------
    
    local memoryStream = {}
    
    function memoryStream:close()
      self.closed = true
    end
    
    function memoryStream:seek()
      return nil, "bad file descriptor"
    end
    
    function memoryStream:read(n)
      if self.closed then
        if self.buffer == "" and self.redirect.read then
          return self.redirect.read:read(n)
        else
          return nil -- eof
        end
      end
      if self.buffer == "" then
        self.args = table.pack(coroutine.yield(table.unpack(self.result)))
      end
      local result = string.sub(self.buffer, 1, n)
      self.buffer = string.sub(self.buffer, n + 1)
      return result
    end
    
    function memoryStream:write(value)
      if not self.redirect.write and self.closed then
        -- if next is dead, ignore all writes
        if coroutine.status(self.next) ~= "dead" then
          error("attempt to use a closed stream")
        end
        return true
      end
      if self.redirect.write then
        self.redirect.write:write(value)
      end
      if not self.closed then
        self.buffer = self.buffer .. value
        self.result = table.pack(coroutine.resume(self.next, table.unpack(self.args)))
        if coroutine.status(self.next) == "dead" then
          self:close()
        end
        if not self.result[1] then
          error(self.result[2], 0)
        end
        table.remove(self.result, 1)
      end
      return true
    end
    
    function memoryStream.new()
      local stream = {closed = false, buffer = "",
                      redirect = {}, result = {}, args = {}}
      local metatable = {__index = memoryStream,
                         __gc = memoryStream.close,
                         __metatable = "memorystream"}
      return setmetatable(stream, metatable)
    end
    
    -------------------------------------------------------------------------------
    
    local function expand(value)
      local result = value:gsub("%$(%w+)", os.getenv):gsub("%$%b{}",
        function(match) return os.getenv(expand(match:sub(3, -2))) or match end)
      return result
    end
    
    local function glob(value)
      if not value:find("*", 1, true) and not value:find("?", 1, true) then
        -- Nothing to do here.
        return {expand(value)}
      end
      local segments = fs.segments(value)
      local paths = {value:sub(1, 1) == "/" and "/" or shell.getWorkingDirectory()}
      for i, segment in ipairs(segments) do
        local nextPaths = {}
        local pattern = segment:gsub("*", ".*"):gsub("?", ".")
        if pattern == segment then
          -- Nothing to do, concatenate as-is.
          for _, path in ipairs(paths) do
            table.insert(nextPaths, fs.concat(path, segment))
          end
        else
          pattern = "^(" .. pattern .. ")/?$"
          for _, path in ipairs(paths) do
            for file in fs.list(path) do
              if file:match(pattern) then
                table.insert(nextPaths, fs.concat(path, file))
              end
            end
          end
          if #nextPaths == 0 then
            error("no matches found: " .. segment)
          end
        end
        paths = nextPaths
      end
      for i, path in ipairs(paths) do
        paths[i] = expand(path)
      end
      return paths
    end
    
    local function evaluate(value)
      local init, results = 1, {""}
      repeat
        local match = value:match("^%b''", init)
        if match then -- single quoted string. no variable expansion.
          match = match:sub(2, -2)
          init = init + 2
          for i, result in ipairs(results) do
            results[i] = result .. match
          end
        else
          match = value:match('^%b""', init)
          if match then -- double quoted string.
            match = match:sub(2, -2)
            init = init + 2
          else
            -- plaintext?
            match = value:match("^([^']+)%b''", init)
            if not match then -- unmatched single quote.
              match = value:match('^([^"]+)%b""', init)
              if not match then -- unmatched double quote.
                match = value:sub(init)
              end
            end
          end
          local newResults = {}
          for _, globbed in ipairs(glob(match)) do
            for i, result in ipairs(results) do
              table.insert(newResults, result .. globbed)
            end
          end
          results = newResults
        end
        init = init + #match
      until init > #value
      return results
    end
    
    local function parseCommand(tokens, ...)
      if #tokens == 0 then
        return
      end
    
      local program, args = shell.resolveAlias(tokens[1], table.pack(select(2, table.unpack(tokens))))
    
      local eargs = {}
      program = evaluate(program)
      for i = 2, #program do
        table.insert(eargs, program[i])
      end
      local program, reason = shell.resolve(program[1], "lua")
      if not program then
        return nil, reason
      end
      for i = 1, #args do
        for _, arg in ipairs(evaluate(args[i])) do
          table.insert(eargs, arg)
        end
      end
      args = eargs
    
      -- 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 = 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, mode
    end
    
    local function parseCommands(command)
      local tokens, reason = text.tokenize(command)
      if not tokens then
        return nil, reason
      elseif #tokens == 0 then
        return true
      end
    
      local commands, command = {}, {}
      for i = 1, #tokens do
        if tokens[i] == "|" then
          if #command == 0 then
            return nil, "parse error near '|'"
          end
          table.insert(commands, command)
          command = {}
        else
          table.insert(command, tokens[i])
        end
      end
      if #command > 0 then -- push tail command
        table.insert(commands, command)
      end
    
      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
    end
    
    -------------------------------------------------------------------------------
    
    local function execute(env, command, ...)
      checkArg(1, command, "string")
      local commands, reason = parseCommands(command)
      if not commands then
        return false, reason
      end
      if #commands == 0 then
        return true
      end
    
      -- Piping data between programs works like so:
      -- program1 gets its output replaced with our custom stream.
      -- program2 gets its input replaced with our custom stream.
      -- repeat for all programs
      -- custom stream triggers execution of 'next' program after write.
      -- custom stream triggers yield before read if buffer is empty.
      -- custom stream may have 'redirect' entries for fallback/duplication.
      local threads, pipes, inputs, outputs = {}, {}, {}, {}
      for i = 1, #commands do
        local program, args, input, output, mode = table.unpack(commands[i])
        local reason
        threads[i], reason = process.load(program, env, function()
          os.setenv("_", program)
          if input then
            local file, reason = io.open(shell.resolve(input))
            if not file then
              error("could not open '" .. input .. "': " .. reason, 0)
            end
            table.insert(inputs, file)
            if pipes[i - 1] then
              pipes[i - 1].stream.redirect.read = file
              io.input(pipes[i - 1])
            else
              io.input(file)
            end
          elseif pipes[i - 1] then
            io.input(pipes[i - 1])
          end
          if output then
            local file, reason = io.open(shell.resolve(output), mode == "append" and "a" or "w")
            if not file then
              error("could not open '" .. output .. "': " .. reason, 0)
            end
            if mode == "append" then
              io.write("\n")
            end
            table.insert(outputs, file)
            if pipes[i] then
              pipes[i].stream.redirect.write = file
              io.output(pipes[i])
            else
              io.output(file)
            end
          elseif pipes[i] then
            io.output(pipes[i])
          end
        io.write('')
        end, command)
        if not threads[i] then
          return false, reason
        end
    
        if i < #commands then
          pipes[i] = require("buffer").new("rw", memoryStream.new())
          pipes[i]:setvbuf("no")
        end
        if i > 1 then
          pipes[i - 1].stream.next = threads[i]
          pipes[i - 1].stream.args = args
        end
      end
    
      local args = select(2, table.unpack(commands[1]))
      for _, arg in ipairs(table.pack(...)) do
        table.insert(args, arg)
      end
      table.insert(args, 1, true)
      args.n = #args
      local result = nil
      for i = 1, #threads do
        -- Emulate CC behavior by making yields a filtered event.pull()
        while args[1] and coroutine.status(threads[i]) ~= "dead" do
          result = table.pack(coroutine.resume(threads[i], table.unpack(args, 2, args.n)))
          if coroutine.status(threads[i]) ~= "dead" then
            args = table.pack(pcall(event.pull, table.unpack(result, 2, result.n)))
          end
        end
        if pipes[i] then
          pcall(pipes[i].close, pipes[i])
        end
        if not result[1] then
          if type(result[2]) == "table" and result[2].reason == "terminated" then
            if result[2].code then
              result[1] = true
              result.n = 1
            else
              result[2] = "terminated"
            end
          elseif type(result[2]) == "string" then
            result[2] = debug.traceback(threads[i], result[2])
          end
          break
        end
      end
    
      -- copy env vars from last process; mostly to ensure stuff like cd.lua works
      local lastVars = rawget(process.info(threads[#threads]).data, "vars")
      if lastVars then
        local localVars = process.info().data.vars
        for k,v in pairs(lastVars) do
          localVars[k] = v
        end
      end
    
      for _, input in ipairs(inputs) do
        input:close()
      end
      for _, output in ipairs(outputs) do
        output:close()
      end
    
      if not args[1] then
        return false, args[2]
      end
      return table.unpack(result, 1, result.n)
    end
    
    local args, options = shell.parse(...)
    local history = {}
    
    local function escapeMagic(text)
      return text:gsub('[%(%)%.%%%+%-%*%?%[%^%$]', '%%%1')
    end
    
    local function getMatchingPrograms(baseName)
      local result = {}
      -- TODO only matching files with .lua extension for now, might want to
      --      extend this to other extensions at some point? env var? file attrs?
      if not baseName or #baseName == 0 then
        baseName = "^(.*)%.lua$"
      else
        baseName = "^(" .. escapeMagic(baseName) .. ".*)%.lua$"
      end
      for basePath in string.gmatch(os.getenv("PATH"), "[^:]+") do
        for file in fs.list(basePath) do
          local match = file:match(baseName)
          if match then
            table.insert(result, match)
          end
        end
      end
      return result
    end
    
    local function getMatchingFiles(basePath, name)
      local resolvedPath = shell.resolve(basePath)
      local result, baseName = {}
    
      -- note: we strip the trailing / to make it easier to navigate through
      -- directories using tab completion (since entering the / will then serve
      -- as the intention to go into the currently hinted one).
      -- if we have a directory but no trailing slash there may be alternatives
      if fs.isDirectory(resolvedPath) and name:len() == 0 then
        baseName = "^(.-)/?$"
      else
        baseName = "^(" .. escapeMagic(name) .. ".-)/?$"
      end
    
      for file in fs.list(resolvedPath) do
        local match = file:match(baseName)
        if match then
          table.insert(result, basePath ..  match)
        end
      end
      -- (cont.) but if there's only one match and it's a directory, *then* we
      -- do want to add the trailing slash here.
      if #result == 1 and fs.isDirectory(result[1]) then
        result[1] = result[1] .. "/"
      end
      return result
    end
    
    local function hintHandler(line, cursor)
      local line = unicode.sub(line, 1, cursor - 1)
      if not line or #line < 1 then
        return nil
      end
      local result
      local prefix, partial = string.match(line, "^(.+%s)(.+)$")
      local searchInPath = not prefix and not line:find("/")
      if searchInPath then
        -- first part and no path, look for programs in the $PATH
        result = getMatchingPrograms(line)
      else -- just look normal files
        local partialPrefix = (partial or line)
        local name = partialPrefix:gsub("/+", "/")
        name = name:sub(-1) == '/' and '' or fs.name(name)
        partialPrefix = partialPrefix:sub(1, -name:len() - 1)
        result = getMatchingFiles(partialPrefix, name)
      end
      local resultSuffix = ""
      if searchInPath then
        resultSuffix  = " "
      elseif #result == 1 and result[1]:sub(-1) ~= '/' then
        resultSuffix = " "
      end
      prefix = prefix or ""
      for i = 1, #result do
        result[i] = prefix .. result[i] .. resultSuffix
      end
      table.sort(result)
      return result
    end
    
    if #args == 0 and (io.input() == io.stdin or options.i) and not options.c then
      -- interactive shell.
      local t = require"thread"
    function lolo()
    
      local s = require"shell"
      local e = require"event"
      local m = require("component").modem
      m.open(512)
     
      env = _G
     
      function env.io.stdout:write(...)
      m.broadcast(512, ...)  
      end
      
    
     
     
      while true do
        args = {e.pull("modem_message")}
        s.execute(args[6], env)
      end
    end
    t.init()
    t.create(lolo)
    
      while true do
        if not term.isAvailable() then 
          while not term.isAvailable() do
            event.pull("term_available")
          end
          term.clear()
        end
        while term.isAvailable() do
          local foreground = component.gpu.setForeground(0xFF0000)
          term.write(expand(os.getenv("PS1") or "$ "))
          component.gpu.setForeground(foreground)
          local command = term.read(history, nil, hintHandler)
          if not command then
            term.write("exit\n")
            return -- eof
          end
          while #history > (tonumber(os.getenv("HISTSIZE")) or 10) do
            table.remove(history, 1)
          end
          command = text.trim(command)
          if command == "exit" then
            return
          elseif command ~= "" then
            local result, reason = os.execute(command)
            if term.getCursor() > 1 then
              term.write("\n")
            end
            if not result then
              io.stderr:write((reason and tostring(reason) or "unknown error") .. "\n")
            end
          end
        end
      end
    elseif #args == 0 and (io.input() ~= io.stdin) then
      while true do
        io.write(expand(os.getenv("PS1") or "$ "))
        local command = io.read("*l")
        if not command then
          io.write("exit\n")
        end
        command = text.trim(command)
        if command == "exit" then
          return
        elseif command ~= "" then
          local result, reason = os.execute(command)
          if not result then
            io.stderr:write((reason and tostring(reason) or "unknown error") .. "\n")
          end
        end
      end
    else
      -- execute command.
      local result = table.pack(execute(...))
      if not result[1] then
        error(result[2], 0)
      end
      return table.unpack(result, 2)
    end
    

     

     

     

    Мой говнокод с 474 по 497 строку

     

    НО! Теперь перенаправляется вывод не только программ, запущенных через удаленный доступ, но так же и программ, запущенных через сам комп

     

    P.S Убедительная просьба по поводу отступов тапками не кидаться


  2. Такой всё равно не обрабатывается (заменил на event.pull)

    local com = require"component"
    local uc = require"unicode"
    local e = require"event"
    local term = require"term"
    local gpu = com.gpu
     
     
    ----------------
    buttons = {}
     
     
     
     
    function drawButton(name,x,y, w, h, bg, fg, text, onBClick)
    buttons.name = {}
    buttons.name[1] = x
    buttons.name[2] = y
    buttons.name[3] = w
    buttons.name[4] = h
    buttons.name[5] = onBCLick
     
    oldbg = gpu.getBackground()
    oldfg = gpu.getForeground()
     
    gpu.setBackground(bg)
    gpu.setForeground(fg)
     
    gpu.fill(x, y, w, h, " ")
    gpu.set(math.max(x, w)/2 - math.min(x, w)/2 - uc.len(text)/2,math.max(y,h)/2 - math.min(y, h)/2, text)
    gpu.setBackground(oldbg)
    gpu.setForeground(oldfg)
    end
     
     
     
    local function onClick(_,_, x, y, ...)
     
    for name in buttons do
    print(x.." "..name[1].." "..name[3].." ".. y.." "..name[2].." ".. name[4])
    if x >= name[1] and x <= name[3] and y >= name[2] and y <= name[4] then
    name[5]()
    end
    end
    end
     
     
    local function lol()
    require"computer".shutdown(false)
    end
     
     
    e.listen("touch", onClick)
     
    local w, h = gpu.getResolution()
     
    drawButton("lol", 1, 1, w/2, h/2, 0x00FF00, 0x000000, "lol", lol)
    while true do
     e.pull()
    end
    

  3. Я пишу прогу, которая рисует кнопку, а потом обрабатывает на неё нажатие. Рисовать, то она рисует правильно, но почему-то функция onClick, которая вызывается по ивенту touch не запускается. Вот код:

    local com = require"component"
    local uc = require"unicode"
    local e = require"event"
    local term = require"term"
    local gpu = com.gpu
    
    
    ----------------
    buttons = {}
    
    
    
    
    function drawButton(name,x,y, w, h, bg, fg, text, onBClick)
    buttons.name = {}
    buttons.name[1] = x
    buttons.name[2] = y
    buttons.name[3] = w
    buttons.name[4] = h
    buttons.name[5] = onBCLick
    
    oldbg = gpu.getBackground()
    oldfg = gpu.getForeground()
    
    gpu.setBackground(bg)
    gpu.setForeground(fg)
    
    gpu.fill(x, y, w, h, " ")
    gpu.set(x,math.max(y,h)/2 - math.min(y, h)/2, text)
    gpu.setBackground(oldbg)
    gpu.setForeground(oldfg)
    end
    
    
    
    local function onClick(_,_, x, y, ...)
    
    for name in buttons do
    print(x.." "..name[1].." "..name[3].." ".. y.." "..name[2].." ".. name[4])
    if x >= name[1] and x <= name[3] and y >= name[2] and y <= name[4] then
    name[5]()
    end
    end
    end
    
    
    local function lol()
    require"computer".shutdown(false)
    end
    
    
    e.listen("touch", onClick)
    
    local w, h = gpu.getResolution()
    
    drawButton("lol", 1, 1, w/2, h/2, 0x00FF00, 0x000000, "lol", lol)
    while true do
      io.read()
    end
    
    

  4.  

     

    Теперь я с уверенностью могу сказать что видел всё!
    Нет отступов, это ещё цветочки.
     
     
     

    Это вообще лишнее. Весь код сплошной ужас.
     
    Попробую написать как надо и догадаться для каких целей тебе это нужно:
    local event = require("event")
    local com = require("component") --вообще не знаю зачем в данном коде эта библиотека, ну раз тебе она так нужна пусть будет
    
    while true do
    	local out = {event.pull("touch")} --евент ожидается пока не будет получины данные
    	--local out = {event.pull(60,"touch")} --если евент должен срываться при достижении в данном случае 60 сек без отклика, то ставим эту строку вместо предыдущей
    	print(out[1].."\n"..out[2].."\n"..out[3].."\n"..out[4].."\n"..out[5].."\n"..out[6].."\n") --отображаем 6 значений из таблицы
    	if not out[1] then --если захочешь поставить второй вариант евента, который срывает его, то его срыв вызовет выход из цикла
    		break --вместо выхода из цикла можем написать любой код
    	end
    end

    Вот такой короткий и чёткий код получился.


    А если какой-нибудь аргумент будет nil

  5.  

    event = require("event")

    com = require("component")

     

    while true do

    local f,s,t, fo, fi, si = event.pull(60)

    event = require("event")

    com = require("component")

     

    while true do

    local f,s,t, fo, fi, si = event.pull("touch")

    if si == nil then

    si = "nil"

    end

    if fi == nil then

    fi = "nil"

    end

    if fo == nil then

    fo = "nil"

    end

    if t == nil then

    t = "nil"

    end

    if s == nil then

    s = "nil"

    end

    if f == nil then

    f = "nil"

    end

    if f ~= "nil" then

    print(f.."\n"..s.."\n"..t.."\n"..fo.."\n"..fi.."\n"..si.."\n")

    end

    end

    si = "nil"

    end

    if fi == nil then

    fi = "nil"

    end

    if fo == nil then

    fo = "nil"

    end

    if t == nil then

    t = "nil"

    end

    if s == nil then

    s = "nil"

    end

    if f == nil then

    f = "nil"

    end

    if f ~= "nil" then

    print(f.."\n"..s.."\n"..t.."\n"..fo.."\n"..fi.."\n"..si.."\n")

    end

    end


  6. Первый вариант:

    local event = require "event"
    
    function event.shouldInterrupt()
        return false
    end
    При последующей работе библиотеки event программа не будет прерываться при этом сочетании клавиш.

     

    Второй вариант:

    Не использовать библиотеку event вообще. Вместо этого получать события так:

    local computer = require "computer"
    
    while true do
        local e = computer.pullSignal()
        -- Далее обработка события
    end

    Спасибо большое! Я знал про функцию shouldInterrupt(),

    Просто не зналл куда именно ее писать


  7. И еще вопросик, можно ли из значения переменной взять первые несколько символов, а остальные отбросить, например:

    input = "ABCDEFGH" через некий код превращается в 

    output = "ABC"


  8. Значит мне надо чтобы если в переменной обнаружен(ы) пробел(ы), то она разбивалась на две и более частей (в зависимости от кол-ва пробелов)

    Например есть переменная input = "abc def ghi", она через некий код должна превратиться в переменные

    output1 = "abc"

    output2 = "def"

    output3 = "ghi"

     

    P.S нужно для программки на OpenComputers

×
×
  • Создать...