Advertisement
Wobbo

CC grep

Nov 22nd, 2013
283
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 6.46 KB | None | 0 0
  1. local args = {...}
  2. --- The options that the user specified
  3. local   plain = false
  4. local   count = nil
  5. local   ignoreCase = false
  6. local   writeNamesOnce = false
  7. local   lineNumber = nil
  8. local quiet = false
  9. local   fileError = true
  10. local   invert = false
  11. local   matchWhole = false
  12. local printNames = false
  13. --- Table with patterns to check for
  14. local patternList = {}
  15. --- Table with paths to files that hold patterns
  16. local patternFiles = {}
  17. --- An iterator that will return the next line from the input
  18. local readLines
  19.  
  20. --- A function to parse all the command line arguments and stores them in the options table/pattern list/patternfile
  21. local function parseOptions(args, options, printMsg, errorMsg)
  22.     if not args or not options then
  23.         return false, "The arguments and the valid options have to be given."
  24.     end
  25.     errorMsg = errorMsg or 'Not a valid option: '
  26.     local current = nil
  27.     local pos = 1
  28.     return function()
  29.         if #args <= 0 then
  30.             return nil, 'Nothing left to process'
  31.         end
  32.         if not current or #current < pos then
  33.             if string.find(args[1], "^%-%w") then
  34.                 current = string.sub(args[1], 2, #args[1])
  35.                 pos = 1
  36.                 table.remove(args, 1)
  37.             else
  38.                 return nil
  39.             end
  40.         end
  41.         local char = current:sub(pos,pos)
  42.         pos = pos + 1
  43.         if char == '-' then
  44.             return nil
  45.         end
  46.         local i, j = string.find(options, char..':*')
  47.         if not i then
  48.             if printMsg then
  49.                 print(errorMsg..char)
  50.             end
  51.             return false, char
  52.         elseif j - i == 0 then
  53.             return char, nil
  54.         elseif  j - i == 1 then
  55.             if not args[1] or args[1]:sub(1,1) == '-' then
  56.                 return false, char..' needs an argument'
  57.             else
  58.                 return char, table.remove(args, 1)
  59.             end
  60.         elseif j - i == 2 then
  61.             if args[1] and args[1]:sub(1,1) ~= '-' then
  62.                 return char, table.remove(args, 1)
  63.             else
  64.                 return char, nil
  65.             end
  66.         end
  67.     end
  68. end
  69.  
  70. --- A function that prints the usage of Grep
  71. local function printUsage()
  72.     print('Usage: '..shell.getRunningProgram()..' [-F][-c| -l| -q][-insvx] [-e pattern] [-f file] [pattern] [file...]')
  73. end
  74.  
  75. local function fileError(file)
  76.     if opts.fileError then
  77.         print(shell.getRunningProgram()..': '..file..': No such file or directory')
  78.     end
  79. end
  80.    
  81.  
  82. --- A function to process the command line arguments
  83. local function processOptions()
  84.     for opt, arg in parseOptions(args, 'Fce:f:ilnqsvx') do
  85.         if opt == 'F' then
  86.             plain = true
  87.         elseif opt == 'c' then
  88.             counter = 0
  89.         elseif opt == 'e' then
  90.             table.insert(patternList, arg)
  91.         elseif opt == 'f' then
  92.             table.insert(patternFile, shell.resolve(arg))
  93.         elseif opt == 'i' then
  94.             ignoreCase = true
  95.         elseif opt == 'l' then
  96.             writeNamesOnce = true
  97.         elseif opt == 'n' then
  98.             lineNumber = 1
  99.         elseif opt == 'q' then
  100.             quiet = true
  101.         elseif opt == 's' then
  102.             fileError = false
  103.         elseif opt == 'v' then
  104.             invert = true
  105.         elseif opt == 'x' then
  106.             matchWhole = true
  107.         elseif opt == 'E' then
  108.         elseif opt == false then
  109.             printUsage()
  110.             return false
  111.         end
  112.     end
  113.     --- Read the pattern files and add the patterns to the patternList
  114.     for i = 1, #patternFiles do
  115.         if fs.exists(patternFiles[i]) then
  116.             file = io.open(patternFiles[i], 'r')
  117.             for pat in file:lines() do
  118.                 table.insert(patternList, pat)
  119.             end
  120.             file:close()
  121.         else
  122.             fileError(patternFiles[i])
  123.         end
  124.     end
  125.  
  126.     --- Check if their are patterns, if not, get args[1] and add it to the list
  127.     if #patternList == 0 then
  128.         if #args < 1 then
  129.             printUsage()
  130.             return false -- No patterns and no arguments, end the program
  131.         else
  132.             table.insert(patternList, table.remove(args, 1))
  133.         end
  134.     end
  135.  
  136.     --- Prepare an interator for reading
  137.     if #args > 0 then
  138.         readLines = function()
  139.             local files = args
  140.             local curHand = nil
  141.             local curFile
  142.             return function()
  143.                 if not curFile and #files > 0 then -- No current file, but there are files left
  144.                     if fs.exists(files[1]) then
  145.                         curFile = table.remove(files, 1)
  146.                         curHand = fs.open(curFile, 'r')
  147.                         if lineNumber then lineNumber = 1 end
  148.                     else
  149.                         fileError(tables.remove(files, 1))
  150.                         return false
  151.                     end
  152.                 end
  153.                 local line = curHand.readLine()
  154.                 if not line then
  155.                     curFile = nil
  156.                     curHand.close()
  157.                     if #files < 1 then
  158.                         return nil
  159.                     else
  160.                         return false
  161.                     end
  162.                 else
  163.                     return line, curFile
  164.                 end
  165.             end
  166.         end
  167.     else
  168.         readLines = function() return read end
  169.     end
  170.     return true
  171. end
  172.  
  173. local function printMatchedLine(line, file)
  174.     if not count then
  175.         if matchFile ~= file and writeNamesOnce then
  176.             print(file)
  177.         elseif printNames and not count then
  178.             write(file..':')
  179.         end
  180.         if lineNumber then write(lineNumber..':') end
  181.         print(line)
  182.     elseif matchFile ~= file then
  183.         if printNames then write(matchFile..':') end
  184.         print(count)
  185.         count = 1
  186.     else
  187.         count = count + 1
  188.     end
  189. end
  190.  
  191. --- Builds a case insensitive pattern, code from stackOverflow (questions/11401890/case-insensitive-lua-pattern-matching)
  192. local function caseInsensitivePattern(pattern)
  193.   -- find an optional '%' (group 1) followed by any character (group 2)
  194.   local p = pattern:gsub("(%%?)(.)", function(percent, letter)
  195.     if percent ~= "" or not letter:match("%a") then
  196.       -- if the '%' matched, or `letter` is not a letter, return "as is"
  197.       return percent .. letter
  198.     else
  199.       -- else, return a case-insensitive character class of the matched letter
  200.       return string.format("[%s%s]", letter:lower(), letter:upper())
  201.     end
  202.   end)
  203.   return p
  204. end
  205.  
  206. --- Main
  207.  
  208. if #args < 1 then
  209.     printUsage()
  210.     return 2
  211. end
  212.  
  213. if not processOptions() then
  214.     return 2
  215. end
  216.  
  217. if #args > 1 then printNames = true end
  218.  
  219. local matchFile = nil
  220. for line, file in readLines() do
  221.     if line then
  222.         local match = false
  223.         for _, pattern in pairs(patternList) do
  224.             if ignoreCase then pattern = caseInsensitivePattern(pattern) end
  225.             local i, j = line:find(pattern, 1, plain)
  226.             if not matchWhole then
  227.                 match = i and true or match
  228.             else
  229.                 if j == #line and i == 1 then match = true end
  230.             end
  231.         end
  232.         if match ~= invert then
  233.             if quiet then return 0 end
  234.             if not count then
  235.                 if matchFile ~= file and writeNamesOnce then
  236.                     print(file)
  237.                 elseif printNames and not count then
  238.                     write(file..':')
  239.                 end
  240.                 if lineNumber then write(lineNumber..':') end
  241.                 print(line)
  242.             elseif matchFile ~= file then
  243.                 if printNames then write(matchFile..':') end
  244.                 print(count)
  245.                 count = 1
  246.             else
  247.                 count = count + 1
  248.             end
  249.             matchFile = file
  250.         end
  251.         if lineNumber then lineNumber = lineNumber + 1 end
  252.     end
  253. end
  254.  
  255. return matchFile and 0 or 1
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement