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>
344 lines
6.5 KiB
Go
344 lines
6.5 KiB
Go
package bifs
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"strings"
|
|
|
|
"github.com/johnkerl/miller/v6/pkg/mlrval"
|
|
)
|
|
|
|
func BIF_dhms2sec(input1 *mlrval.Mlrval) *mlrval.Mlrval {
|
|
if !input1.IsString() {
|
|
return mlrval.FromNotStringError("dhms2sec", input1)
|
|
}
|
|
|
|
input := input1.String()
|
|
if input == "" {
|
|
return mlrval.FromNotStringError("dhms2sec", input1)
|
|
}
|
|
|
|
negate := false
|
|
if input[0] == '-' {
|
|
input = input[1:]
|
|
negate = true
|
|
}
|
|
|
|
remainingInput := input
|
|
var seconds int64
|
|
|
|
for remainingInput != "" {
|
|
var n int64
|
|
var rest string
|
|
|
|
_, err := fmt.Sscanf(remainingInput, "%d%s", &n, &rest)
|
|
if err != nil {
|
|
return mlrval.FromError(err)
|
|
}
|
|
if len(rest) < 1 {
|
|
return mlrval.FromErrorString("dhms2sec: input too short")
|
|
}
|
|
unitPart := rest[0]
|
|
remainingInput = rest[1:]
|
|
|
|
switch unitPart {
|
|
case 'd':
|
|
seconds += n * 86400
|
|
case 'h':
|
|
seconds += n * 3600
|
|
case 'm':
|
|
seconds += n * 60
|
|
case 's':
|
|
seconds += n
|
|
default:
|
|
return mlrval.FromError(
|
|
fmt.Errorf(
|
|
"dhms2sec(\"%s\"): unrecognized unit '%c'",
|
|
input1.OriginalString(),
|
|
unitPart,
|
|
),
|
|
)
|
|
}
|
|
}
|
|
if negate {
|
|
seconds = -seconds
|
|
}
|
|
|
|
return mlrval.FromInt(seconds)
|
|
}
|
|
|
|
func BIF_dhms2fsec(input1 *mlrval.Mlrval) *mlrval.Mlrval {
|
|
if !input1.IsString() {
|
|
return mlrval.FromNotStringError("dhms2fsec", input1)
|
|
}
|
|
|
|
input := input1.String()
|
|
if input == "" {
|
|
return mlrval.FromNotStringError("dhms2fsec", input1)
|
|
}
|
|
|
|
negate := false
|
|
if input[0] == '-' {
|
|
input = input[1:]
|
|
negate = true
|
|
}
|
|
|
|
remainingInput := input
|
|
var seconds float64
|
|
|
|
for remainingInput != "" {
|
|
var f float64
|
|
var rest string
|
|
|
|
_, err := fmt.Sscanf(remainingInput, "%f%s", &f, &rest)
|
|
if err != nil {
|
|
return mlrval.FromError(err)
|
|
}
|
|
if len(rest) < 1 {
|
|
return mlrval.FromErrorString("dhms2fsec: input too short")
|
|
}
|
|
unitPart := rest[0]
|
|
remainingInput = rest[1:]
|
|
|
|
switch unitPart {
|
|
case 'd':
|
|
seconds += f * 86400.0
|
|
case 'h':
|
|
seconds += f * 3600.0
|
|
case 'm':
|
|
seconds += f * 60.0
|
|
case 's':
|
|
seconds += f
|
|
default:
|
|
return mlrval.FromError(
|
|
fmt.Errorf(
|
|
"dhms2fsec(\"%s\"): unrecognized unit '%c'",
|
|
input1.OriginalString(),
|
|
unitPart,
|
|
),
|
|
)
|
|
}
|
|
}
|
|
if negate {
|
|
seconds = -seconds
|
|
}
|
|
|
|
return mlrval.FromFloat(seconds)
|
|
}
|
|
|
|
func BIF_hms2sec(input1 *mlrval.Mlrval) *mlrval.Mlrval {
|
|
if !input1.IsString() {
|
|
return mlrval.FromNotStringError("hms2sec", input1)
|
|
}
|
|
if input1.AcquireStringValue() == "" {
|
|
return mlrval.FromNotStringError("hms2sec", input1)
|
|
}
|
|
var h, m, s int64
|
|
|
|
if strings.HasPrefix(input1.AcquireStringValue(), "-") {
|
|
n, err := fmt.Sscanf(input1.AcquireStringValue(), "-%d:%d:%d", &h, &m, &s)
|
|
if n == 3 && err == nil {
|
|
return mlrval.FromInt(-(s + m*60 + h*60*60))
|
|
}
|
|
} else {
|
|
n, err := fmt.Sscanf(input1.AcquireStringValue(), "%d:%d:%d", &h, &m, &s)
|
|
if n == 3 && err == nil {
|
|
return mlrval.FromInt(s + m*60 + h*60*60)
|
|
}
|
|
}
|
|
|
|
return mlrval.FromError(
|
|
fmt.Errorf("hsm2sec: could not parse input \"%s\"", input1.OriginalString()),
|
|
)
|
|
}
|
|
|
|
func BIF_hms2fsec(input1 *mlrval.Mlrval) *mlrval.Mlrval {
|
|
if !input1.IsString() {
|
|
return mlrval.FromNotStringError("hms2fsec", input1)
|
|
}
|
|
|
|
var h, m int
|
|
var s float64
|
|
|
|
if strings.HasPrefix(input1.AcquireStringValue(), "-") {
|
|
n, err := fmt.Sscanf(input1.AcquireStringValue(), "-%d:%d:%f", &h, &m, &s)
|
|
if n == 3 && err == nil {
|
|
return mlrval.FromFloat(-(s + float64(m*60+h*60*60)))
|
|
}
|
|
} else {
|
|
n, err := fmt.Sscanf(input1.AcquireStringValue(), "%d:%d:%f", &h, &m, &s)
|
|
if n == 3 && err == nil {
|
|
return mlrval.FromFloat(s + float64(m*60+h*60*60))
|
|
}
|
|
}
|
|
|
|
return mlrval.FromError(
|
|
fmt.Errorf("hsm2fsec: could not parse input \"%s\"", input1.OriginalString()),
|
|
)
|
|
}
|
|
|
|
func BIF_sec2dhms(input1 *mlrval.Mlrval) *mlrval.Mlrval {
|
|
isec, ok := input1.GetIntValue()
|
|
if !ok {
|
|
return mlrval.FromNotIntError("sec2dhms", input1)
|
|
}
|
|
|
|
var d, h, m, s int64
|
|
|
|
splitIntToDHMS(isec, &d, &h, &m, &s)
|
|
if d != 0 {
|
|
return mlrval.FromString(
|
|
fmt.Sprintf("%dd%02dh%02dm%02ds", d, h, m, s),
|
|
)
|
|
} else if h != 0 {
|
|
return mlrval.FromString(
|
|
fmt.Sprintf("%dh%02dm%02ds", h, m, s),
|
|
)
|
|
} else if m != 0 {
|
|
return mlrval.FromString(
|
|
fmt.Sprintf("%dm%02ds", m, s),
|
|
)
|
|
} else {
|
|
return mlrval.FromString(
|
|
fmt.Sprintf("%ds", s),
|
|
)
|
|
}
|
|
}
|
|
|
|
func BIF_sec2hms(input1 *mlrval.Mlrval) *mlrval.Mlrval {
|
|
isec, ok := input1.GetIntValue()
|
|
if !ok {
|
|
return mlrval.FromNotIntError("sec2hms", input1)
|
|
}
|
|
sign := ""
|
|
if isec < 0 {
|
|
sign = "-"
|
|
isec = -isec
|
|
}
|
|
|
|
var d, h, m, s int64
|
|
|
|
splitIntToDHMS(isec, &d, &h, &m, &s)
|
|
h += d * 24
|
|
|
|
return mlrval.FromString(
|
|
fmt.Sprintf("%s%02d:%02d:%02d", sign, h, m, s),
|
|
)
|
|
}
|
|
|
|
func BIF_fsec2dhms(input1 *mlrval.Mlrval) *mlrval.Mlrval {
|
|
fsec, ok := input1.GetNumericToFloatValue()
|
|
if !ok {
|
|
return mlrval.FromNotIntError("fsec2dhms", input1)
|
|
}
|
|
|
|
sign := int64(1)
|
|
if fsec < 0 {
|
|
sign = -1
|
|
fsec = -fsec
|
|
}
|
|
isec := int64(math.Trunc(fsec))
|
|
fractional := fsec - float64(isec)
|
|
|
|
var d, h, m, s int64
|
|
|
|
splitIntToDHMS(isec, &d, &h, &m, &s)
|
|
|
|
if d != 0 {
|
|
d = sign * d
|
|
return mlrval.FromString(
|
|
fmt.Sprintf(
|
|
"%dd%02dh%02dm%09.6fs",
|
|
d, h, m, float64(s)+fractional),
|
|
)
|
|
} else if h != 0 {
|
|
h = sign * h
|
|
return mlrval.FromString(
|
|
fmt.Sprintf(
|
|
"%dh%02dm%09.6fs",
|
|
h, m, float64(s)+fractional),
|
|
)
|
|
} else if m != 0 {
|
|
m = sign * m
|
|
return mlrval.FromString(
|
|
fmt.Sprintf(
|
|
"%dm%09.6fs",
|
|
m, float64(s)+fractional),
|
|
)
|
|
} else {
|
|
s = sign * s
|
|
fractional = float64(sign) * fractional
|
|
return mlrval.FromString(
|
|
fmt.Sprintf(
|
|
"%.6fs",
|
|
float64(s)+fractional),
|
|
)
|
|
}
|
|
}
|
|
|
|
func BIF_fsec2hms(input1 *mlrval.Mlrval) *mlrval.Mlrval {
|
|
fsec, ok := input1.GetNumericToFloatValue()
|
|
if !ok {
|
|
return mlrval.FromNotIntError("fsec2hms", input1)
|
|
}
|
|
|
|
sign := ""
|
|
if fsec < 0 {
|
|
sign = "-"
|
|
fsec = -fsec
|
|
}
|
|
isec := int64(math.Trunc(fsec))
|
|
fractional := fsec - float64(isec)
|
|
|
|
var d, h, m, s int64
|
|
|
|
splitIntToDHMS(isec, &d, &h, &m, &s)
|
|
h += d * 24
|
|
|
|
// "%02.6f" does not exist so we have to do our own zero-pad
|
|
if s < 10 {
|
|
return mlrval.FromString(
|
|
fmt.Sprintf("%s%02d:%02d:0%.6f", sign, h, m, float64(s)+fractional),
|
|
)
|
|
} else {
|
|
return mlrval.FromString(
|
|
fmt.Sprintf("%s%02d:%02d:%.6f", sign, h, m, float64(s)+fractional),
|
|
)
|
|
}
|
|
}
|
|
|
|
// Helper function
|
|
func splitIntToDHMS(u int64, pd, ph, pm, ps *int64) {
|
|
d := int64(0)
|
|
h := int64(0)
|
|
m := int64(0)
|
|
s := int64(0)
|
|
sign := int64(1)
|
|
if u < 0 {
|
|
u = -u
|
|
sign = -1
|
|
}
|
|
s = u % 60
|
|
u = u / 60
|
|
if u == 0 {
|
|
s = s * sign
|
|
} else {
|
|
m = u % 60
|
|
u = u / 60
|
|
if u == 0 {
|
|
m = m * sign
|
|
} else {
|
|
h = u % 24
|
|
u = u / 24
|
|
if u == 0 {
|
|
h = h * sign
|
|
} else {
|
|
d = u * sign
|
|
}
|
|
}
|
|
}
|
|
*pd = d
|
|
*ph = h
|
|
*pm = m
|
|
*ps = s
|
|
}
|