mirror of
https://github.com/johnkerl/miller.git
synced 2026-07-17 16:38:54 +00:00
* refine the plan
* Fix all staticcheck lint findings (uncapped)
golangci-lint's default max-same-issues=3 was hiding most of the backlog:
the true pre-fix count was 69 staticcheck findings, not 34. This fixes all
of them, driving staticcheck to zero:
- ST1023/QF1011 (37): omit explicit types inferred from the RHS
- S1009/S1031 (15): drop redundant nil checks before len()/range
- SA9003 (9): remove comment-only empty branches, keeping the comments
- QF1007 (3): merge conditional assignment into declaration
- QF1006 (3): lift break conditions into loop conditions
- QF1001 (3): apply De Morgan's law / name the negated predicate
Also updates plans/lintfixes.md with the cap discovery and the corrected
errcheck picture (1202 uncapped, ~949 of them fmt.Fprint*).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Drive errcheck to zero: config for bulk categories, propagate real errors
Adds .golangci.yml with errcheck exclude-functions for fmt.Fprint* (usage
printers), (*bufio.Writer).Write/WriteString (sticky errors, surfaced at the
now-checked final Flush), and (*strings.Builder).WriteString; pins
max-issues-per-linter/max-same-issues to 0 so CI reports true counts.
Real error paths now propagate instead of being dropped:
- Finalize{Reader,Writer}Options in join/put/filter/split/tee and the
repl/script entry points: 'mlr join -i badformat' now errors instead of
silently using wrong separators
- final output-stream Flush in pkg/stream: write failure no longer exits 0
- DSL emit/print/dump redirect writes, matching their sibling branches
- CSV writer WriteCSVRecordMaybeColorized, close-time Flush in file output
handlers, ENV[...] Setenv, REPL record-write and redirect-close errors
- termcvt write-side Close before rename (had "TODO: check return status")
The rest are deliberate ignores, marked with _ = and a comment where the
reason isn't obvious: unset-of-missing-path no-ops, read-side closes,
mid-stream FlushOnEveryRecord, init-time strftime registrations, in-memory
usage-capture pipes, and regtest-harness env/temp-file teardown.
golangci-lint now reports 0 issues on ./cmd/mlr ./pkg/... with all caps off.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
151 lines
3.8 KiB
Go
151 lines
3.8 KiB
Go
package auxents
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
)
|
|
|
|
// Copyright (c) 1998 John Kerl.
|
|
// This is a simple hex dump with hex offsets to the left, hex data in the
|
|
// middle, and ASCII at the right. This is a subset of the functionality of
|
|
// Unix od; I wrote it in my NT days.
|
|
//
|
|
// Example:
|
|
//
|
|
// $ d2h $(jot 0 128) | unhex | hex
|
|
// 00000000: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
|
|
// 00000010: 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f |................|
|
|
// 00000020: 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f | !"#$%&'()*+,-./|
|
|
// 00000030: 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f |0123456789:;<=>?|
|
|
// 00000040: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f |@ABCDEFGHIJKLMNO|
|
|
// 00000050: 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f |PQRSTUVWXYZ[\]^_|
|
|
// 00000060: 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f |`abcdefghijklmno|
|
|
// 00000070: 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f |pqrstuvwxyz{|}~.|
|
|
|
|
func hexUsage(verbName string, o *os.File, exitCode int) {
|
|
fmt.Fprintf(o, "Usage: mlr %s [options] {zero or more file names}\n", verbName)
|
|
fmt.Fprintf(o, "Simple hex-dump.\n")
|
|
fmt.Fprintf(o, "If zero file names are supplied, standard input is read.\n")
|
|
fmt.Fprintf(o, "Options:\n")
|
|
fmt.Fprintf(o, "-r: print only raw hex without leading offset indicators or trailing ASCII dump.\n")
|
|
fmt.Fprintf(o, "-h or --help: print this message\n")
|
|
os.Exit(exitCode)
|
|
}
|
|
|
|
func hexMain(args []string) int {
|
|
doRaw := false
|
|
|
|
// 'mlr' and 'hex' are already argv[0] and argv[1].
|
|
verb := args[1]
|
|
args = args[2:]
|
|
if len(args) >= 1 {
|
|
switch args[0] {
|
|
case "-r":
|
|
doRaw = true
|
|
args = args[1:]
|
|
case "-h", "--help":
|
|
hexUsage(verb, os.Stdout, 0)
|
|
}
|
|
}
|
|
|
|
if len(args) == 0 {
|
|
hexDumpFile(os.Stdin, doRaw)
|
|
} else {
|
|
for _, filename := range args {
|
|
// Print filename if there is more than one file, unless raw output
|
|
// was requested.
|
|
if !doRaw && len(args) > 1 {
|
|
fmt.Printf("%s:\n", filename)
|
|
}
|
|
|
|
istream, err := os.Open(filename)
|
|
if err != nil {
|
|
// TODO: "mlr"
|
|
fmt.Fprintf(os.Stderr, "mlr hex: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
hexDumpFile(istream, doRaw)
|
|
|
|
_ = istream.Close()
|
|
if !doRaw && len(args) > 1 {
|
|
fmt.Println()
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
func hexDumpFile(istream *os.File, doRaw bool) {
|
|
const bytesPerClump = 4
|
|
const clumpsPerLine = 4
|
|
const bufferSize = bytesPerClump * clumpsPerLine
|
|
|
|
buffer := make([]byte, bufferSize)
|
|
eof := false
|
|
offset := 0
|
|
|
|
for !eof {
|
|
numBytesRead, err := io.ReadFull(istream, buffer)
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
|
|
// io.ErrUnexpectedEOF is the normal case when the file size isn't an
|
|
// exact multiple of our buffer size. We'll break the loop after
|
|
// hex-dumping this last, partial fragment.
|
|
if err != nil && err != io.ErrUnexpectedEOF {
|
|
fmt.Fprintf(os.Stderr, "mlr hex: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Print offset "pre" part
|
|
if !doRaw {
|
|
fmt.Printf("%08x: ", offset)
|
|
}
|
|
|
|
// Print hex payload
|
|
for i := range bufferSize {
|
|
if i < numBytesRead {
|
|
fmt.Printf("%02x ", buffer[i])
|
|
} else {
|
|
fmt.Printf(" ")
|
|
}
|
|
if (i % bytesPerClump) == (bytesPerClump - 1) {
|
|
if (i > 0) && (i < bufferSize-1) {
|
|
fmt.Printf(" ")
|
|
}
|
|
}
|
|
}
|
|
|
|
// Print ASCII-dump "post" part
|
|
if !doRaw {
|
|
fmt.Printf("|")
|
|
|
|
for i := range numBytesRead {
|
|
if buffer[i] >= 0x20 && buffer[i] <= 0x7e {
|
|
fmt.Printf("%c", buffer[i])
|
|
} else {
|
|
fmt.Printf(".")
|
|
}
|
|
}
|
|
fmt.Printf("|")
|
|
}
|
|
|
|
// Print line end
|
|
fmt.Printf("\n")
|
|
|
|
// Break the loop if this was the last, partial fragment. If we don't
|
|
// break here we'll go back to the top of the loop and try the next
|
|
// read and get EOF -- which works fine if the input is a file. But if
|
|
// the input is at the terminal, the user will have to control-D twice
|
|
// which will be unsettling.
|
|
if numBytesRead < bufferSize {
|
|
break
|
|
}
|
|
|
|
offset += numBytesRead
|
|
}
|
|
}
|