Advertisement
Wobbo

OC-Grep

Jan 26th, 2014
120
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 5.79 KB | None | 0 0
  1. --- POSIX grep for OpenComputers, only difference is that this version uses Lua regex, not POSIX regex.
  2. --
  3. -- Depends on getopt by Wobbo.
  4.  
  5. local args = {...} -- Catch the arguments
  6. local fs = require "filesystem"
  7. local getopt = require "getopt" -- Load getopt
  8. local shell = require "shell"
  9.  
  10. -- Specify the variables for the options
  11. local plain = false
  12. local count = nil
  13. local ignoreCase = false
  14. local writeNamesOnce = false
  15. local linNumber = nil
  16. local quiet = false
  17. local fileError = true
  18. local invert = false
  19. local metchWhole = false
  20. local printNames = false
  21.  
  22. -- Table with patterns to check for
  23. local patternList = {}
  24.  
  25. -- Table with files to look in
  26. local patternFiles = {}
  27.  
  28. local function printUsage()
  29.   print('Usage: '..shell.running()..' [-c|-l|-q][-Finsvx] [-e pattern] [-f patternFile] [pattern] [file...]')
  30. end
  31.  
  32. -- Resolve the location of a file, without searching the path
  33. local function resolve(file)
  34.   if file:sub(1,1) == '/' then
  35.     return fs.canonical(file)
  36.   else
  37.     if file:sub(1,1) == '.' then
  38.       file = file:sub(3, -1)
  39.     end
  40.     return fs.canonical(fs.concat(shell.getWorkingDirectory(), file))
  41.   end
  42. end
  43.  
  44. -- Checks if it should error, and errors if that is required
  45. local function fileError(file)
  46.   if fileError then
  47.     print(shell.running()..': '..file..': No such file or directory')
  48.   end
  49. end
  50.  
  51. --- Builds a case insensitive pattern, code from stackOverflow (questions/11401890/case-insensitive-lua-pattern-matching)
  52. local function caseInsensitivePattern(pattern)
  53.   -- find an optional '%' (group 1) followed by any character (group 2)
  54.   local p = pattern:gsub("(%%?)(.)", function(percent, letter)
  55.     if percent ~= "" or not letter:match("%a") then
  56.       -- if the '%' matched, or `letter` is not a letter, return "as is"
  57.       return percent .. letter
  58.     else
  59.       -- else, return a case-insensitive character class of the matched letter
  60.       return string.format("[%s%s]", letter:lower(), letter:upper())
  61.     end
  62.   end)
  63.   return p
  64. end
  65.  
  66. -- Process the command line arguments
  67. if #args < 1 then
  68.     printUsage()
  69.     return 2
  70. end
  71.  
  72. for opt, arg in getopt(args, "Fce:f:ilnqsvx") do
  73.   if opt == 'F' then
  74.     plain = true
  75.   elseif opt == 'c' then
  76.     if writeNamesOnce or quiet then printUsage() return end
  77.     counter = 0
  78.   elseif opt == 'e' then
  79.     table.insert(patternList, arg)
  80.   elseif opt == 'f' then
  81.     table.insert(patternFiles, arg)
  82.   elseif opt == 'i' then
  83.     ignoreCase = true
  84.   elseif opt == 'l' then
  85.     if counter or quiet then printUsage() return end
  86.     writeNamesOnce = true
  87.   elseif opt == 'n' then
  88.     lineNumber = 1
  89.   elseif opt == 'q' then
  90.     if writeNamesOnce or counter then printUsage() return end
  91.     quiet = true
  92.   elseif opt == 's' then
  93.     fileError = false
  94.   elseif opt == 'v' then
  95.     invert = true
  96.   elseif opt == 'x' then
  97.     matchWhole = true
  98.   elseif opt == 'E' then
  99.   elseif opt == '?' then
  100.     printUsage()
  101.     return 2
  102.   elseif opt == ':' then
  103.     print(opt..' Needs an argument!')
  104.     printUsage()
  105.     return 2
  106.   end
  107. end
  108.  
  109. -- Read the pattern files and add the patterns to the patternList
  110. for i = 1, #patternFiles do
  111.   file = resolve(patternFiles[i])
  112.   if fs.exists(file) then
  113.     handle = io.open(file, 'r')
  114.     for pat in handle:lines() do
  115.       table.insert(patternList, pat)
  116.     end
  117.     handle:close()
  118.   else
  119.     fileError(file)
  120.   end
  121. end
  122.  
  123. -- Check if there are patterns, if not, get args[1] and add it to the list
  124. if #patternList == 0 then
  125.   if #args < 1 then
  126.     printUsage()
  127.     return 2
  128.   else
  129.     table.insert(patternList, table.remove(args, 1))
  130.   end
  131. end
  132.  
  133. -- Prepare an iterator for reading files
  134. local readLines
  135. if #args >= 1 then
  136.   readLines = function()
  137.     local files = args
  138.     local curHand = nil
  139.     local curFile = nil
  140.     return function()
  141.       if not curFile and #files > 0 then
  142.         file = resolve(table.remove(files, 1))
  143.         if fs.exists(file) then
  144.           curFile = file
  145.           curHand = io.open(curFile, 'r')
  146.           if lineNumber then lineNumber = 1 end
  147.         else
  148.           fileError(file)
  149.           return false, "file not found"
  150.         end
  151.       end
  152.       local line = curHand:read("*l")
  153.       if not line then
  154.         curFile = nil
  155.         curHand:close()
  156.         if #files < 1 then
  157.           return nil
  158.         else
  159.           return false, "end of file"
  160.         end
  161.       else
  162.         return line, curFile
  163.       end
  164.     end
  165.   end
  166. else
  167.   readLines = io.lines
  168. end
  169.  
  170. -- Main
  171. if #args > 1 then printNames = true end -- Print the names of the files we read
  172.  
  173. local matchFile = nil
  174. for line, file in readLines() do
  175.   if not line then
  176.     if file ~= "end of file" then return 2 end
  177.   else
  178.     file = file or '(standard input)'
  179.     if line then
  180.       local match = false
  181.       for _, pattern in pairs(patternList) do
  182.         if ignoreCase then pattern = caseInsensitivePattern(pattern) end
  183.         local i, j = line:find(pattern, 1, plain)
  184.         if not matchWhole then
  185.           match = i and true or match
  186.         else
  187.           if j == #line and i == 1 then match = true end
  188.         end
  189.       end
  190.       if match ~= invert then
  191.         if quiet then return 0 end
  192.         if not count then
  193.           if writeNamesOnce then
  194.             if matchFile ~= file then print(file) end
  195.           else
  196.             if printNames then
  197.               io.write(file..': ')
  198.             end
  199.             if lineNumber then io.write(lineNumber..': ') end
  200.             print(line)
  201.           end
  202.         elseif matchFile ~= file then
  203.           if printNames then io.write(matchFile': ') end
  204.           print(count)
  205.           count = 1
  206.         else
  207.           count = count + 1
  208.         end
  209.         matchFile = file
  210.       end
  211.       if lineNumber then lineNumber = lineNumber + 1 end
  212.     end
  213.   end
  214. end
  215.  
  216. return (matchFile and 0 or 1)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement