github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+incompatible/cmds/core/tr/tr.go (about) 1 // Copyright 2018 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // tr - translate or delete characters 6 7 // Synopsis: 8 // tr [OPTION]... SET1 [SET2] 9 10 // Description: 11 // Translate, squeeze, and/or delete characters from standard input, writing 12 // to standard output. 13 // 14 // -d, --delete: delete characters in SET1, do not translate 15 // 16 // SETs are specified as strings of characters. Most represent themselves. 17 // Interpreted sequences are: 18 // \\ backslash 19 // \a audible BEL 20 // \b backspace 21 // \f form feed 22 // \n new line 23 // \r return 24 // \t horizontal tab 25 // \v vertical tab 26 // [:alnum:] all letters and digits 27 // [:alpha:] all letters 28 // [:digit:] all digits 29 // [:graph:] all printable characters 30 // [:cntrl:] all control characters 31 // [:lower:] all lower case letters 32 // [:upper:] all upper case letters 33 // [:space:] all whitespaces 34 35 package main 36 37 import ( 38 "bufio" 39 "fmt" 40 "io" 41 "log" 42 "os" 43 "unicode" 44 45 flag "github.com/spf13/pflag" 46 ) 47 48 var delete = flag.BoolP("delete", "d", false, "delete characters in SET1, do not translate") 49 50 const name = "tr" 51 52 var escapeChars = map[rune]rune{ 53 '\\': '\\', 54 'a': '\a', 55 'b': '\b', 56 'f': '\f', 57 'n': '\n', 58 'r': '\r', 59 't': '\t', 60 'v': '\v', 61 } 62 63 type Set string 64 65 const ( 66 ALPHA Set = "[:alpha:]" 67 DIGIT Set = "[:digit:]" 68 GRAPH Set = "[:graph:]" 69 CNTRL Set = "[:cntrl:]" 70 PUNCT Set = "[:punct:]" 71 SPACE Set = "[:space:]" 72 ALNUM Set = "[:alnum:]" 73 LOWER Set = "[:lower:]" 74 UPPER Set = "[:upper:]" 75 ) 76 77 var sets = map[Set]func(r rune) bool{ 78 ALNUM: func(r rune) bool { 79 return unicode.IsLetter(r) || unicode.IsDigit(r) 80 }, 81 82 ALPHA: unicode.IsLetter, 83 DIGIT: unicode.IsDigit, 84 GRAPH: unicode.IsGraphic, 85 CNTRL: unicode.IsControl, 86 PUNCT: unicode.IsPunct, 87 SPACE: unicode.IsSpace, 88 LOWER: unicode.IsLower, 89 UPPER: unicode.IsUpper, 90 } 91 92 type transformer struct { 93 transform func(r rune) rune 94 } 95 96 func setToRune(s Set, outRune rune) *transformer { 97 check := sets[s] 98 return &transformer{ 99 transform: func(r rune) rune { 100 if check(r) { 101 return outRune 102 } 103 return r 104 }, 105 } 106 } 107 108 func lowerToUpper() *transformer { 109 return &transformer{ 110 transform: func(r rune) rune { 111 return unicode.ToUpper(r) 112 }, 113 } 114 } 115 116 func upperToLower() *transformer { 117 return &transformer{ 118 transform: func(r rune) rune { 119 return unicode.ToLower(r) 120 }, 121 } 122 } 123 124 func runesToRunes(in []rune, out ...rune) *transformer { 125 convs := make(map[rune]rune) 126 l := len(out) 127 for i, r := range in { 128 ind := i 129 if i > l-1 { 130 ind = l - 1 131 } 132 convs[r] = out[ind] 133 } 134 return &transformer{ 135 transform: func(r rune) rune { 136 if outRune, ok := convs[r]; ok { 137 return outRune 138 } 139 return r 140 }, 141 } 142 } 143 144 func (t *transformer) run(r io.Reader, w io.Writer) error { 145 in := bufio.NewReader(r) 146 out := bufio.NewWriter(w) 147 148 defer out.Flush() 149 150 for { 151 inRune, size, err := in.ReadRune() 152 if inRune == unicode.ReplacementChar { 153 // can skip error handling here, because 154 // previous operation was in.ReadRune() 155 in.UnreadRune() 156 157 b, err := in.ReadByte() 158 if err != nil { 159 return fmt.Errorf("read error: %v", err) 160 } 161 162 if err := out.WriteByte(b); err != nil { 163 return fmt.Errorf("write error: %v", err) 164 } 165 } else if size > 0 { 166 if outRune := t.transform(inRune); outRune != unicode.ReplacementChar { 167 if _, err := out.WriteRune(outRune); err != nil { 168 return fmt.Errorf("write error: %v", err) 169 } 170 } 171 } 172 173 if err != nil { 174 if err == io.EOF { 175 return nil 176 } 177 return err 178 } 179 } 180 } 181 182 func parse() (*transformer, error) { 183 flag.Parse() 184 185 narg := flag.NArg() 186 args := flag.Args() 187 switch { 188 case narg == 0 || (narg == 1 && !*delete): 189 return nil, fmt.Errorf("missing operand") 190 case narg > 1 && *delete: 191 return nil, fmt.Errorf("extra operand after %q", args[0]) 192 case narg > 2: 193 return nil, fmt.Errorf("extra operand after %q", args[1]) 194 } 195 196 set1 := Set(args[0]) 197 arg1, err := unescape(set1) 198 if err != nil { 199 return nil, err 200 } 201 202 var set2 Set 203 if *delete { 204 set2 = Set(unicode.ReplacementChar) 205 } else { 206 set2 = Set(args[1]) 207 } 208 209 if set1 == LOWER && set2 == UPPER { 210 return lowerToUpper(), nil 211 } 212 if set1 == UPPER && set2 == LOWER { 213 return upperToLower(), nil 214 } 215 216 if (set2 == LOWER || set2 == UPPER) && (set1 != LOWER && set1 != UPPER) || 217 (set1 == LOWER && set2 == LOWER) || (set1 == UPPER && set2 == UPPER) { 218 return nil, fmt.Errorf("misaligned [:upper:] and/or [:lower:] construct") 219 } 220 221 if _, ok := sets[set2]; ok { 222 return nil, fmt.Errorf(`the only character classes that may appear in SET2 are 'upper' and 'lower'`) 223 } 224 225 arg2, err := unescape(set2) 226 if err != nil { 227 return nil, err 228 } 229 if len(arg2) == 0 { 230 return nil, fmt.Errorf("SET2 must be non-empty") 231 } 232 if _, ok := sets[set1]; ok { 233 return setToRune(set1, arg2[0]), nil 234 } 235 return runesToRunes(arg1, arg2...), nil 236 } 237 238 func unescape(s Set) ([]rune, error) { 239 var out []rune 240 var escape bool 241 for _, r := range s { 242 if escape { 243 v, ok := escapeChars[r] 244 if !ok { 245 return nil, fmt.Errorf("unknown escape sequence '\\%c'", r) 246 } 247 out = append(out, v) 248 escape = false 249 continue 250 } 251 252 if r == '\\' { 253 escape = true 254 continue 255 } 256 257 out = append(out, r) 258 } 259 return out, nil 260 } 261 262 func main() { 263 t, err := parse() 264 if err != nil { 265 log.Fatalf("%s: %v\n", name, err) 266 } 267 if err := t.run(os.Stdin, os.Stdout); err != nil { 268 log.Fatalf("%s: %v\n", name, err) 269 } 270 }