Skip to content

Commit

Permalink
add cmd-line options: -v, -f
Browse files Browse the repository at this point in the history
  • Loading branch information
jftuga committed Jan 24, 2022
1 parent 9bf1907 commit a91e285
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 11 deletions.
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ PS C:\chars> .\chars.exe -e perf.*dat -l 32 C:\Windows\System32\p*
## Example 3

* Pipe STDIN to `chars`
* Use JSON output
* Use JSON output, with `-j`

```shell
$ curl -s https://example.com/ | chars -j
Expand All @@ -92,6 +92,23 @@ $ curl -s https://example.com/ | chars -j
]
```

## Example 4

* Fail when certain characters are detected, with `-f`
* * OS exit code on a `-f` failure is always `100`
* * `-f` is a comma-delimited list containing: `crlf`, `lf`, `tab`, `nul`, `bom8`, `bom16`

```shell
$ chars -f lf,tab /etc/group ; echo $?
+------------+------+----+-----+-----+------+-------+-----------+
| FILENAME | CRLF | LF | TAB | NUL | BOM8 | BOM16 | BYTESREAD |
+------------+------+----+-----+-----+------+-------+-----------+
| /etc/group | 0 | 58 | 0 | 0 | 0 | 0 | 795 |
+------------+------+----+-----+-----+------+-------+-----------+

100
```

## Reading from STDIN on Windows
* **YMMV when piping to `STDIN` under Windows**
* * Under `cmd`, instead of `type input.txt | chars`, use `<` redirection when possible: `chars < input.txt`
Expand Down
50 changes: 43 additions & 7 deletions chars.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"path/filepath"
"regexp"
"strconv"
"strings"
"unicode/utf8"

"github.com/jftuga/ellipsis"
Expand All @@ -29,7 +30,7 @@ import (
const PgmName string = "chars"
const PgmDesc string = "Determine the end-of-line format, tabs, bom, and nul"
const PgmUrl string = "https://github.com/jftuga/chars"
const PgmVersion string = "2.1.0"
const PgmVersion string = "2.2.0"
const BlockSize int = 4096

type SpecialChars struct {
Expand Down Expand Up @@ -215,7 +216,7 @@ func GetJSON(allStats []SpecialChars) string {
}

// ProcessGlob - process all files matching the file-glob
func ProcessGlob(globArg string, allStats *[]SpecialChars, examineBinary bool, excludeMatched *regexp.Regexp) {
func ProcessGlob(globArg string, allStats *[]SpecialChars, examineBinary bool, excludeMatched *regexp.Regexp, fail string) uint64 {
var err error
anyCase := CaseInsensitive(globArg)
if len(globArg) > 0 && len(anyCase) == 0 {
Expand All @@ -229,11 +230,11 @@ func ProcessGlob(globArg string, allStats *[]SpecialChars, examineBinary bool, e
if len(globFiles) == 0 {
globFiles = []string{anyCase}
}
ProcessFileList(globFiles, allStats, examineBinary, excludeMatched)
return ProcessFileList(globFiles, allStats, examineBinary, excludeMatched, fail)
}

// ProcessFileList - process a list of filenames
func ProcessFileList(globFiles []string, allStats *[]SpecialChars, examineBinary bool, excludeMatched *regexp.Regexp) {
func ProcessFileList(globFiles []string, allStats *[]SpecialChars, examineBinary bool, excludeMatched *regexp.Regexp, fail string) uint64 {
var file *os.File
for _, filename := range globFiles {
info, err := os.Stat(filename)
Expand Down Expand Up @@ -272,18 +273,53 @@ func ProcessFileList(globFiles []string, allStats *[]SpecialChars, examineBinary
}
*allStats = append(*allStats, stats)
}
if len(fail) > 0 {
return GetFailures(fail, allStats)
}
return 0
}

// ProcessStdin - read a file stream directly from STDIN
func ProcessStdin(allStats *[]SpecialChars, examineBinary bool) CharsError {
func ProcessStdin(allStats *[]SpecialChars, examineBinary bool, fail string) (uint64, CharsError) {
var charsErr CharsError

reader := bufio.NewReader(os.Stdin)
stats, charsErr := searchForSpecialChars("STDIN", reader, examineBinary)
if charsErr.code != 0 {
return charsErr
return 0, charsErr
}

*allStats = append(*allStats, stats)
return charsErr
if len(fail) > 0 {
return GetFailures(fail, allStats), charsErr
}
return 0, charsErr
}

// GetFailures - parse as comma-delimited list and return the number of characters in the given character list
func GetFailures(commaList string, allStats *[]SpecialChars) uint64 {
var failed uint64

classes := strings.Split(strings.ToLower(commaList), ",")
for _, class := range classes {
for _, entry := range *allStats {
switch class {
case "crlf":
failed += entry.Crlf
case "lf":
failed += entry.Lf
case "tab":
failed += entry.Tab
case "bom8":
failed += entry.Bom8
case "bom16":
failed += entry.Bom16
case "nul":
failed += entry.Nul
default:
fmt.Fprintf(os.Stderr, "Unknown character passed to -f: %s\n", class)
}
}
}
return failed
}
19 changes: 16 additions & 3 deletions cmd/chars/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,18 @@ func main() {
argsExclude := flag.String("e", "", "exclude based on regular expression; use .* instead of *")
argsMaxLength := flag.Int("l", 0, "shorten files names to a maximum of this length")
argsJSON := flag.Bool("j", false, "output results in JSON format; can't be used with -l")
argsVersion := flag.Bool("v", false, "display version and then exit")
argsFail := flag.String("f", "", "fail with OS exit code=100 if any of the included characters exist; ex: -f crlf,nul,bom8")

flag.Usage = Usage
flag.Parse()
allGlobs := flag.Args()

if *argsVersion {
fmt.Printf("%s\n", chars.PgmVersion)
os.Exit(0)
}

if *argsMaxLength > 0 && *argsJSON {
_, _ = fmt.Fprintf(os.Stderr, "-l and -j are mutually exclusive")
os.Exit(2)
Expand All @@ -68,16 +75,18 @@ func main() {

// allStats will be modified in-place by one of the two functions below
var allStats []chars.SpecialChars
var failed, current uint64
for _, fileSelection := range allGlobs {
if fileSelection == "-" {
chars.ProcessStdin(&allStats, *argsBinary)
current, _ = chars.ProcessStdin(&allStats, *argsBinary, *argsFail)
} else {
if runtime.GOOS == "windows" {
chars.ProcessGlob(fileSelection, &allStats, *argsBinary, excludeFiles)
current = chars.ProcessGlob(fileSelection, &allStats, *argsBinary, excludeFiles, *argsFail)
} else {
chars.ProcessFileList([]string{fileSelection}, &allStats, *argsBinary, excludeFiles)
current = chars.ProcessFileList([]string{fileSelection}, &allStats, *argsBinary, excludeFiles, *argsFail)
}
}
failed += current
}

// output results to either JSON or text table
Expand All @@ -93,4 +102,8 @@ func main() {
os.Exit(5)
}
}

if len(*argsFail) > 0 && failed > 0 {
os.Exit(100)
}
}

0 comments on commit a91e285

Please sign in to comment.