diff options
Diffstat (limited to 'modules/log/colors.go')
-rw-r--r-- | modules/log/colors.go | 66 |
1 files changed, 63 insertions, 3 deletions
diff --git a/modules/log/colors.go b/modules/log/colors.go index ed6477d431..0ec8ce4ba8 100644 --- a/modules/log/colors.go +++ b/modules/log/colors.go @@ -7,6 +7,7 @@ package log import ( "fmt" "io" + "reflect" "strconv" "strings" ) @@ -195,11 +196,12 @@ normalLoop: lasti := i if c.mode == escapeAll { - for i < end && (bytes[i] >= ' ' || bytes[i] == '\n') { + for i < end && (bytes[i] >= ' ' || bytes[i] == '\n' || bytes[i] == '\t') { i++ } } else { - for i < end && bytes[i] >= ' ' { + // Allow tabs if we're not escaping everything + for i < end && (bytes[i] >= ' ' || bytes[i] == '\t') { i++ } } @@ -266,6 +268,39 @@ normalLoop: return totalWritten, nil } +// ColorSprintf returns a colored string from a format and arguments +// arguments will be wrapped in ColoredValues to protect against color spoofing +func ColorSprintf(format string, args ...interface{}) string { + if len(args) > 0 { + v := make([]interface{}, len(args)) + for i := 0; i < len(v); i++ { + v[i] = NewColoredValuePointer(&args[i]) + } + return fmt.Sprintf(format, v...) + } + return fmt.Sprintf(format) +} + +// ColorFprintf will write to the provided writer similar to ColorSprintf +func ColorFprintf(w io.Writer, format string, args ...interface{}) (int, error) { + if len(args) > 0 { + v := make([]interface{}, len(args)) + for i := 0; i < len(v); i++ { + v[i] = NewColoredValuePointer(&args[i]) + } + return fmt.Fprintf(w, format, v...) + } + return fmt.Fprintf(w, format) +} + +// ColorFormatted structs provide their own colored string when formatted with ColorSprintf +type ColorFormatted interface { + // ColorFormat provides the colored representation of the value + ColorFormat(s fmt.State) +} + +var colorFormattedType = reflect.TypeOf((*ColorFormatted)(nil)).Elem() + // ColoredValue will Color the provided value type ColoredValue struct { colorBytes *[]byte @@ -316,8 +351,33 @@ func NewColoredValueBytes(value interface{}, colorBytes *[]byte) *ColoredValue { } } -// Format will format the provided value and protect against ANSI spoofing within the value +// NewColoredIDValue is a helper function to create a ColoredValue from a Value +// The Value will be colored with FgCyan +// If a ColoredValue is provided it is not changed +func NewColoredIDValue(value interface{}) *ColoredValue { + return NewColoredValueBytes(&value, &fgCyanBytes) +} + +// Format will format the provided value and protect against ANSI color spoofing within the value +// If the wrapped value is ColorFormatted and the format is "%-v" then its ColorString will +// be used. It is presumed that this ColorString is safe. func (cv *ColoredValue) Format(s fmt.State, c rune) { + if c == 'v' && s.Flag('-') { + if val, ok := (*cv.Value).(ColorFormatted); ok { + val.ColorFormat(s) + return + } + v := reflect.ValueOf(*cv.Value) + t := v.Type() + + if reflect.PtrTo(t).Implements(colorFormattedType) { + vp := reflect.New(t) + vp.Elem().Set(v) + val := vp.Interface().(ColorFormatted) + val.ColorFormat(s) + return + } + } s.Write([]byte(*cv.colorBytes)) fmt.Fprintf(&protectedANSIWriter{w: s}, fmtString(s, c), *(cv.Value)) s.Write([]byte(*cv.resetBytes)) |