#!/usr/bin/env bash

# parsing CLI options
while getopts :nlivx option; do
    case "$option" in
        # include line numbers in output
        n) linenr=1 ;;
        # only file names containing matching lines are printed
        l) filename=1 ;;
        # case insentivity
        i) shopt -s nocasematch ;;
        # matching lines are inverted (i.e., lines that do not match)
        v) invert=1 ;;
        # pattern should match whole line
        x) entire=1 ;;
        # other options are invalid and exit
        \?) echo Invalid option.; exit 1;;
    esac
done

# shifts the command-line arguments so that the remaining arguments
# are just the pattern and the files to search in
shift $((OPTIND-1))

pattern=$1
shift
files=( "$@" )
# if entire is set to 1, modifies the pattern to match the entire line
# by anchoring it with ^ (beginning of line) and $ (end of line)
(( entire )) && pattern="^$pattern$"

for file in "${files[@]}"; do
    count=0
    while read -r line
    do
        (( count++ ))
        out=
        if (( invert )); then 
            ! [[ ${line} =~ ${pattern} ]] && out="$line"
        else
            [[ ${line} =~ ${pattern} ]] && out="$line"
        fi
        # if filename is set to 1, prints the file name and breaks out
        # of the loop after the first matching line in each file
        if (( filename )) && [[ -n $out ]]; then
            echo $file;
            break;
        fi
        if (( linenr )) && [[ -n $out ]]; then
            out="$count":$out;
        fi
        if (( ${#files[@]} > 1 )) && [[ -n $out ]]; then
            out="$file":$out;
        fi
        [[ -n $out ]] && echo $out;
    done < "$file"
done 
exit 0
