178 lines
4 KiB
Go
178 lines
4 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"os/user"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
blockedWords []*regexp.Regexp
|
|
redaction = "redacted"
|
|
)
|
|
|
|
func checkBlockedWordsFilePerm(path string) {
|
|
info, err := os.Stat(path)
|
|
if err != nil {
|
|
log.Fatalf("Failed to stat %s: %v", path, err)
|
|
}
|
|
|
|
mode := info.Mode().Perm()
|
|
if mode != 0o600 {
|
|
log.Fatalf("Permission on %s must be 600, found %o", path, mode)
|
|
}
|
|
}
|
|
|
|
func loadBlockedWords(path string) {
|
|
file, err := os.Open(path)
|
|
if err != nil {
|
|
log.Fatalf("Failed to open %s: %v", path, err)
|
|
}
|
|
defer file.Close()
|
|
|
|
scanner := bufio.NewScanner(file)
|
|
for scanner.Scan() {
|
|
line := strings.TrimSpace(scanner.Text())
|
|
if line == "" || strings.HasPrefix(line, "#") {
|
|
continue
|
|
}
|
|
if strings.HasPrefix(line, "redaction:") {
|
|
if redaction != "redacted" {
|
|
log.Fatalf("Multiple redaction definitions found in %s", path)
|
|
}
|
|
redaction = strings.TrimSpace(strings.TrimPrefix(line, "redaction:"))
|
|
continue
|
|
}
|
|
if strings.HasPrefix(line, "import:") {
|
|
dirPath := strings.TrimSpace(strings.TrimPrefix(line, "import:"))
|
|
expandAndImportDir(dirPath)
|
|
continue
|
|
}
|
|
blockedWords = append(blockedWords, regexp.MustCompile(regexp.QuoteMeta(line)))
|
|
}
|
|
if err := scanner.Err(); err != nil {
|
|
log.Fatalf("Error reading %s: %v", path, err)
|
|
}
|
|
}
|
|
|
|
func expandAndImportDir(dir string) {
|
|
expandedPath := os.ExpandEnv(strings.ReplaceAll(dir, "~", getHomeDir()))
|
|
err := filepath.Walk(expandedPath, func(path string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !info.IsDir() {
|
|
importWordsFromFile(path)
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
log.Printf("Warning: Failed to walk directory %s: %v", dir, err)
|
|
}
|
|
}
|
|
|
|
func importWordsFromFile(path string) {
|
|
file, err := os.Open(path)
|
|
if err != nil {
|
|
log.Printf("Warning: Cannot open import file %s: %v", path, err)
|
|
return
|
|
}
|
|
defer file.Close()
|
|
|
|
scanner := bufio.NewScanner(file)
|
|
for scanner.Scan() {
|
|
line := strings.TrimSpace(scanner.Text())
|
|
if line != "" && !strings.HasPrefix(line, "#") {
|
|
blockedWords = append(blockedWords, regexp.MustCompile(regexp.QuoteMeta(line)))
|
|
}
|
|
}
|
|
}
|
|
|
|
func censorLine(line string) string {
|
|
for _, re := range blockedWords {
|
|
line = re.ReplaceAllString(line, redaction)
|
|
}
|
|
return line
|
|
}
|
|
|
|
func process(input io.Reader, output io.Writer) {
|
|
scanner := bufio.NewScanner(input)
|
|
writer := bufio.NewWriter(output)
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
if strings.HasPrefix(strings.TrimSpace(line), "#") {
|
|
fmt.Fprintln(writer, line)
|
|
continue
|
|
}
|
|
censored := censorLine(line)
|
|
fmt.Fprintln(writer, censored)
|
|
}
|
|
writer.Flush()
|
|
if err := scanner.Err(); err != nil {
|
|
log.Fatalf("Error reading input: %v", err)
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
if len(os.Args) == 1 && isInputFromTerminal() {
|
|
fmt.Fprintln(os.Stderr, "No input provided. Exiting.")
|
|
os.Exit(1)
|
|
}
|
|
|
|
usr, err := user.Current()
|
|
if err != nil {
|
|
log.Fatalf("Unable to determine current user: %v", err)
|
|
}
|
|
confPath := filepath.Join(usr.HomeDir, ".blocked_words.txt")
|
|
checkBlockedWordsFilePerm(confPath)
|
|
loadBlockedWords(confPath)
|
|
|
|
switch len(os.Args) {
|
|
case 1:
|
|
process(os.Stdin, os.Stdout)
|
|
case 2:
|
|
inFile, err := os.Open(os.Args[1])
|
|
if err != nil {
|
|
log.Fatalf("Cannot open input file: %v", err)
|
|
}
|
|
defer inFile.Close()
|
|
process(inFile, os.Stdout)
|
|
case 3:
|
|
inFile, err := os.Open(os.Args[1])
|
|
if err != nil {
|
|
log.Fatalf("Cannot open input file: %v", err)
|
|
}
|
|
defer inFile.Close()
|
|
outFile, err := os.Create(os.Args[2])
|
|
if err != nil {
|
|
log.Fatalf("Cannot create output file: %v", err)
|
|
}
|
|
defer outFile.Close()
|
|
process(inFile, outFile)
|
|
default:
|
|
log.Fatalf("Usage: %s [input_file] [output_file]", os.Args[0])
|
|
}
|
|
}
|
|
|
|
func isInputFromTerminal() bool {
|
|
fileInfo, err := os.Stdin.Stat()
|
|
if err != nil {
|
|
return true
|
|
}
|
|
return (fileInfo.Mode() & os.ModeCharDevice) != 0
|
|
}
|
|
|
|
func getHomeDir() string {
|
|
usr, err := user.Current()
|
|
if err != nil {
|
|
log.Fatalf("Unable to determine home directory: %v", err)
|
|
}
|
|
return usr.HomeDir
|
|
}
|
|
|