Started working on multiple filename arguments; prefix each line with filename containing the line; mostly indentation changes

master
Aadhavan Srinivasan 3 weeks ago
parent e79c19a929
commit 34149980a4

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"io" "io"
"os" "os"
"slices"
"github.com/fatih/color" "github.com/fatih/color"
@ -25,6 +26,7 @@ func main() {
multiLineFlag := flag.Bool("t", false, "Multi-line mode. Treats newline just like any character.") multiLineFlag := flag.Bool("t", false, "Multi-line mode. Treats newline just like any character.")
printMatchesFlag := flag.Bool("p", false, "Prints start and end index of each match. Can only be used with '-t' for multi-line mode.") printMatchesFlag := flag.Bool("p", false, "Prints start and end index of each match. Can only be used with '-t' for multi-line mode.")
caseInsensitiveFlag := flag.Bool("i", false, "Case-insensitive. Disregard the case of all characters.") caseInsensitiveFlag := flag.Bool("i", false, "Case-insensitive. Disregard the case of all characters.")
recursiveFlag := flag.Bool("r", false, "Recursively search all files in the given directory.")
matchNum := flag.Int("m", 0, "Print the match with the given index. Eg. -m 3 prints the third match.") matchNum := flag.Int("m", 0, "Print the match with the given index. Eg. -m 3 prints the third match.")
substituteText := flag.String("s", "", "Substitute the contents of each match with the given string. Overrides -o and -v") substituteText := flag.String("s", "", "Substitute the contents of each match with the given string. Overrides -o and -v")
flag.Parse() flag.Parse()
@ -64,21 +66,30 @@ func main() {
// 2. Build NFA from postfix representation (Thompson's algorithm) // 2. Build NFA from postfix representation (Thompson's algorithm)
// 3. Run the string against the NFA // 3. Run the string against the NFA
if len(flag.Args()) < 1 || len(flag.Args()) > 2 { // flag.Args() also strips out program name if len(flag.Args()) < 1 { // flag.Args() also strips out program name
fmt.Println("ERROR: Missing cmdline args")
os.Exit(22)
}
if *recursiveFlag && len(flag.Args()) < 2 { // File/Directory must be provided with '-r'
fmt.Println("ERROR: Missing cmdline args") fmt.Println("ERROR: Missing cmdline args")
os.Exit(22) os.Exit(22)
} }
var re string var re string
re = flag.Args()[0] re = flag.Args()[0]
var inputFile *os.File var inputFiles []*os.File
if len(flag.Args()) == 1 || flag.Args()[1] == "-" { // Either no file argument, or file argument is "-" if len(flag.Args()) == 1 || flag.Args()[1] == "-" { // Either no file argument, or file argument is "-"
inputFile = os.Stdin if !slices.Contains(inputFiles, os.Stdin) {
inputFiles = append(inputFiles, os.Stdin) // os.Stdin cannot be entered more than once into the file list
}
} else { } else {
var err error inputFilenames := flag.Args()[1:]
inputFile, err = os.Open(flag.Args()[1]) for _, inputFilename := range inputFilenames {
if err != nil { inputFile, err := os.Open(inputFilename)
fmt.Printf("%s: No such file or directory\n", flag.Args()[1]) if err != nil {
os.Exit(2) fmt.Printf("%s: No such file or directory\n", flag.Args()[1])
os.Exit(2)
}
inputFiles = append(inputFiles, inputFile)
} }
} }
@ -86,160 +97,169 @@ func main() {
var err error var err error
var linesRead bool // Whether or not we have read the lines in the file var linesRead bool // Whether or not we have read the lines in the file
lineNum := 0 // Current line number lineNum := 0 // Current line number
// Create reader for stdin and writer for stdout // Create writer for stdout
reader := bufio.NewReader(inputFile)
out := bufio.NewWriter(os.Stdout) out := bufio.NewWriter(os.Stdout)
// Compile regex
regComp, err := reg.Compile(re, flagsToCompile...) regComp, err := reg.Compile(re, flagsToCompile...)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
for true {
if linesRead { for _, inputFile := range inputFiles {
break reader := bufio.NewReader(inputFile)
} linesRead = false
if !(*multiLineFlag) { for true {
// Read every string from stdin until we encounter an error. If the error isn't EOF, panic. if !(*lineFlag) && (!*onlyFlag) {
test_str, err = reader.ReadString('\n') color.New(color.FgMagenta).Fprintf(out, "%s: ", inputFile.Name()) // The filename should be printed for every line, _except_ if we're not printing every line. This is the case with the lineFlag and onlyFlag
lineNum++ }
if err != nil { if linesRead {
break
}
if !(*multiLineFlag) {
// Read every string from stdin until we encounter an error. If the error isn't EOF, panic.
test_str, err = reader.ReadString('\n')
lineNum++
if err != nil {
if err == io.EOF {
linesRead = true
} else {
panic(err)
}
}
if len(test_str) > 0 && test_str[len(test_str)-1] == '\n' {
test_str = test_str[:len(test_str)-1]
}
} else {
// Multi-line mode - read every line of input into a temp. string.
// test_str will contain all lines of input (including newline characters)
// as one string.
var temp string
for temp, err = reader.ReadString('\n'); err == nil; temp, err = reader.ReadString('\n') {
test_str += temp
}
// Assuming err != nil
if err == io.EOF { if err == io.EOF {
if len(temp) > 0 {
test_str += temp // Add the last line (if it is non-empty)
}
linesRead = true linesRead = true
} else { } else {
panic(err) panic(err)
} }
} }
if len(test_str) > 0 && test_str[len(test_str)-1] == '\n' { matchIndices := make([]reg.Match, 0)
test_str = test_str[:len(test_str)-1] if matchNumFlagEnabled {
} tmp, err := regComp.FindNthMatch(test_str, *matchNum)
} else { if err == nil {
// Multi-line mode - read every line of input into a temp. string. matchIndices = append(matchIndices, tmp)
// test_str will contain all lines of input (including newline characters)
// as one string.
var temp string
for temp, err = reader.ReadString('\n'); err == nil; temp, err = reader.ReadString('\n') {
test_str += temp
}
// Assuming err != nil
if err == io.EOF {
if len(temp) > 0 {
test_str += temp // Add the last line (if it is non-empty)
} }
linesRead = true
} else { } else {
panic(err) matchIndices = regComp.FindAllSubmatch(test_str)
}
}
matchIndices := make([]reg.Match, 0)
if matchNumFlagEnabled {
tmp, err := regComp.FindNthMatch(test_str, *matchNum)
if err == nil {
matchIndices = append(matchIndices, tmp)
} }
} else {
matchIndices = regComp.FindAllSubmatch(test_str)
}
test_str_runes := []rune(test_str) // Converting to runes preserves unicode characters test_str_runes := []rune(test_str) // Converting to runes preserves unicode characters
if *printMatchesFlag { if *printMatchesFlag {
// if we are in single line mode, print the line on which // if we are in single line mode, print the line on which
// the matches occur // the matches occur
if len(matchIndices) > 0 { if len(matchIndices) > 0 {
if !(*multiLineFlag) { if !(*multiLineFlag) {
fmt.Fprintf(out, "Line %d:\n", lineNum) fmt.Fprintf(out, "Line %d:\n", lineNum)
} }
for _, m := range matchIndices { for _, m := range matchIndices {
fmt.Fprintf(out, "%s\n", m.String()) fmt.Fprintf(out, "%s\n", m.String())
} }
err := out.Flush() err := out.Flush()
if err != nil { if err != nil {
panic(err) panic(err)
}
} }
continue
} }
continue // Decompose the array of matchIndex structs into a flat unique array of ints - if matchIndex is {4,7}, flat array will contain 4,5,6
} // This should make checking O(1) instead of O(n)
// Decompose the array of matchIndex structs into a flat unique array of ints - if matchIndex is {4,7}, flat array will contain 4,5,6 indicesToPrint := new_uniq_arr[int]()
// This should make checking O(1) instead of O(n) for _, idx := range matchIndices {
indicesToPrint := new_uniq_arr[int]() indicesToPrint.add(genRange(idx[0].StartIdx, idx[0].EndIdx)...)
for _, idx := range matchIndices { }
indicesToPrint.add(genRange(idx[0].StartIdx, idx[0].EndIdx)...) // If we are inverting, then we should print the indices which _didn't_ match
} // in color.
// If we are inverting, then we should print the indices which _didn't_ match if *invertFlag {
// in color. oldIndices := indicesToPrint.values()
if *invertFlag { indicesToPrint = new_uniq_arr[int]()
oldIndices := indicesToPrint.values() // Explanation:
indicesToPrint = new_uniq_arr[int]() // Find all numbers from 0 to len(test_str_runes) that are NOT in oldIndices.
// Explanation: // These are the values we want to print, now that we have inverted the match.
// Find all numbers from 0 to len(test_str_runes) that are NOT in oldIndices. // Re-initialize indicesToPrint and add all of these values to it.
// These are the values we want to print, now that we have inverted the match. indicesToPrint.add(setDifference(genRange(0, len(test_str_runes)), oldIndices)...)
// Re-initialize indicesToPrint and add all of these values to it.
indicesToPrint.add(setDifference(genRange(0, len(test_str_runes)), oldIndices)...)
}
// If lineFlag is enabled, we should only print something if:
// a. We are not inverting, and have at least one match on the current line
// OR
// b. We are inverting, and have no matches at all on the current line.
// This checks for the inverse, and continues if it is true.
if *lineFlag {
if !(*invertFlag) && len(matchIndices) == 0 || *invertFlag && len(matchIndices) > 0 {
continue
} }
} // If lineFlag is enabled, we should only print something if:
// a. We are not inverting, and have at least one match on the current line
// OR
// b. We are inverting, and have no matches at all on the current line.
// This checks for the inverse, and continues if it is true.
if *lineFlag {
if !(*invertFlag) && len(matchIndices) == 0 || *invertFlag && len(matchIndices) > 0 {
continue
} else {
color.New(color.FgMagenta).Fprintf(out, "%s: ", inputFile.Name()) // Print filename
}
}
// If we are substituting, we need a different behavior, as follows: // If we are substituting, we need a different behavior, as follows:
// For every character in the test string: // For every character in the test string:
// 1. Check if the index is the start of any matchIndex // 1. Check if the index is the start of any matchIndex
// 2. If so, print the substitute text, and set our index to // 2. If so, print the substitute text, and set our index to
// the corresponding end index. // the corresponding end index.
// 3. If not, just print the character. // 3. If not, just print the character.
if substituteFlagEnabled { if substituteFlagEnabled {
for i := range test_str_runes { for i := range test_str_runes {
inMatchIndex := false inMatchIndex := false
for _, m := range matchIndices { for _, m := range matchIndices {
if i == m[0].StartIdx { if i == m[0].StartIdx {
fmt.Fprintf(out, "%s", *substituteText) fmt.Fprintf(out, "%s", *substituteText)
i = m[0].EndIdx i = m[0].EndIdx
inMatchIndex = true inMatchIndex = true
break break
}
}
if !inMatchIndex {
fmt.Fprintf(out, "%c", test_str_runes[i])
} }
} }
if !inMatchIndex { } else {
fmt.Fprintf(out, "%c", test_str_runes[i]) for i, c := range test_str_runes {
} if indicesToPrint.contains(i) {
} color.New(color.FgRed, color.Bold).Fprintf(out, "%c", c)
} else { // Newline after every match - only if -o is enabled and -v is disabled.
for i, c := range test_str_runes { if *onlyFlag && !(*invertFlag) {
if indicesToPrint.contains(i) { for matchIdxNum, idx := range matchIndices {
color.New(color.FgRed).Fprintf(out, "%c", c) if matchIdxNum < len(matchIndices)-1 { // Only print a newline afte printing a match, if there are multiple matches on the line, and we aren't on the last one. This is because the newline that gets added at the end will take care of that.
// Newline after every match - only if -o is enabled and -v is disabled. if i+1 == idx[0].EndIdx { // End index is one more than last index of match
if *onlyFlag && !(*invertFlag) { fmt.Fprintf(out, "\n")
for matchIdxNum, idx := range matchIndices { break
if matchIdxNum < len(matchIndices)-1 { // Only print a newline afte printing a match, if there are multiple matches on the line, and we aren't on the last one. This is because the newline that gets added at the end will take care of that. }
if i+1 == idx[0].EndIdx { // End index is one more than last index of match
fmt.Fprintf(out, "\n")
break
} }
} }
} }
} } else {
} else { if !(*onlyFlag) {
if !(*onlyFlag) { fmt.Fprintf(out, "%c", c)
fmt.Fprintf(out, "%c", c) }
} }
} }
} }
} err = out.Flush()
err = out.Flush() if err != nil {
if err != nil { panic(err)
panic(err) }
} // If the last character in the string wasn't a newline, AND we either have don't -o set or we do (and we've matched something), then print a newline
// If the last character in the string wasn't a newline, AND we either have don't -o set or we do (and we've matched something), then print a newline if (len(test_str_runes) > 0 && test_str_runes[len(test_str_runes)-1] != '\n') &&
if (len(test_str_runes) > 0 && test_str_runes[len(test_str_runes)-1] != '\n') && (!*onlyFlag || indicesToPrint.len() > 0) {
(!*onlyFlag || indicesToPrint.len() > 0) { fmt.Println()
fmt.Println() }
} }
} }
} }

Loading…
Cancel
Save