Initial commit

This commit is contained in:
Nicolo P 2022-02-13 16:25:20 +01:00
commit 2d2895ce14
4 changed files with 190 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
*.swp
test_f*.txt
bin
lib*
*.cfg
share
*cache*

5
README.md Normal file
View File

@ -0,0 +1,5 @@
# crapgrep
Have you ever wanted to use a poorly-written, less-featured, slower replacement for an existing [powerful tool?](https://www.gnu.org/software/grep/)
Then this is for you...

166
crapgrep/crapgrep.py Normal file
View File

@ -0,0 +1,166 @@
import exceptions
import re
import os
import sys
def print_usage():
usage = """
Usage: pygrep.py [OPTIONS]... PATTERN [FILE]...
Example:
pygrep.py -i 'searching' target1.txt target2.txt
OPTIONS:
-E : PATTERN is an extended regexp
-i : case-insensitive search for PATTERN
-n : print line numbers
-r : search recursively in files in the given path
--help: print this help message
"""
print(usage)
def check_args(args: list) -> bool:
"""
The arguments passed to pygrep.py
"""
if len(args) < 2 or args[0] == '--help':
return False
return True
def check_pattern(pattern: str) -> bool:
"""
Compile pattern and return false if invalid
"""
try:
re.compile(pattern)
return True
except:
return False
def parse_args(args: list) -> dict:
"""
Parse cli arguments and return the tree as a dict
"""
# TODO move to class
OPTS = [
'i', # Case-insensitive search
'E', # Pattern is a full regexp
'r', # Search recursively in dir
'n', # Print line numbers
]
# TODO Handle extended options? (e.g. --ignore-case)
LONG_OPTS = [
'--ignore-case',
'--regexp',
'--recursive',
'--line-numbers',
]
args_tree = dict()
options = []
# Get options
for arg in args:
# Parse short options
if arg[0] == '-':
if arg[1:] not in OPTS:
raise exceptions.InvalidOption(arg[1:])
options.append(arg[1:])
args_tree["options"] = options
# Filter options from args
remaining_args = [a for a in args if a.lstrip('-') not in options]
pattern = remaining_args[0]
if pattern[0] == "'" and pattern[-1] == "'":
pattern = pattern.strip("'")
elif pattern[0] == '"' and pattern[-1] == '"':
pattern = pattern.strip('"')
args_tree["pattern"] = rf"{pattern}"
args_tree["files"] = remaining_args[1:]
"""
print(args_tree)
sys.exit(1)
"""
return args_tree
def match_regexp(pattern: str, line: str) -> object:
"""
Search for pattern in the given line and return
a match object or None if no match found.
Flags??
"""
return re.search(pattern, line)
def process_grep(args_tree: dict) -> list:
lines = []
found_lines = []
options = args_tree["options"]
pattern = args_tree["pattern"]
files = args_tree["files"]
try:
for f in files:
with open(f, 'r') as fp:
lines = fp.readlines()
f = f + ':' if len(files) > 1 else ''
# Check if case insensitive
if 'i' in options:
pattern = pattern.lower()
# Check if regexp flag
if 'E' in options:
if not check_pattern(pattern):
raise exceptions.InvalidPattern(pattern)
# Match regexp
# This isn't DRY... decorator?
for l in lines:
matches = re.findall(pattern, l)
if len(matches) > 0:
l = l.strip()
found_lines.append(f + l)
else:
for l in lines:
if l.find(pattern) != -1:
l = l.strip()
found_lines.append(f + l)
except FileNotFoundError as e:
print(e)
sys.exit(1)
return found_lines
# The main program...
def __main__():
args = sys.argv[1:]
if not check_args(args):
print_usage()
sys.exit(1)
try:
args_tree = parse_args(args)
except exceptions.InvalidOption as e:
print(e.get_message())
print_usage()
sys.exit(1)
try:
for l in process_grep(args_tree):
print(l)
except exceptions.InvalidPattern as e:
print(e.get_message())
sys.exit(1)
__main__()

12
crapgrep/exceptions.py Normal file
View File

@ -0,0 +1,12 @@
class InvalidOption(Exception):
def __init__(self, invalid_opt):
self.invalid_opt = invalid_opt
def get_message(self):
return f"pygrep: invalid option -- '{self.invalid_opt}'"
class InvalidPattern(Exception):
def __init__(self, pattern):
self.invalid = pattern
def get_message(self):
return f"pygrep: invalid pattern -- '{self.invalid}'"