github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/cmds/exp/ed/address.go (about) 1 // Copyright 2019 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 // address.go - contains methods for FileBuffer for line address resolution 6 package main 7 8 import ( 9 "fmt" 10 "regexp" 11 "strconv" 12 "strings" 13 ) 14 15 // regex helpers 16 func reGroup(s string) string { 17 return "(" + s + ")" 18 } 19 20 func reOr(s ...string) string { 21 return strings.Join(s, "|") 22 } 23 24 func reOpt(s string) string { 25 return s + "?" 26 } 27 28 func reStart(s string) string { 29 return "^" + s 30 } 31 32 // addr regex strings 33 var ( 34 reWhitespace = "(\\s)" 35 reSingleSymbol = "([.$])" 36 reNumber = "([0-9]+)" 37 reOffset = reGroup("([-+])" + reOpt(reNumber)) 38 reMark = "'([a-z])" 39 reRE = "(\\/((?:\\\\/|[^\\/])*)\\/|\\?((?:\\\\?|[^\\?])*)\\?)" 40 reSingle = reGroup(reOr(reSingleSymbol, reNumber, reOffset, reMark, reRE)) 41 reOff = "(?:\\s+" + reGroup(reOr(reNumber, reOffset)) + ")" 42 ) 43 44 // addr compiled regexes 45 var ( 46 rxWhitespace = regexp.MustCompile(reStart(reWhitespace)) 47 rxSingleSymbol = regexp.MustCompile(reStart(reSingleSymbol)) 48 rxNumber = regexp.MustCompile(reStart(reNumber)) 49 rxOffset = regexp.MustCompile(reStart(reOffset)) 50 rxMark = regexp.MustCompile(reStart(reMark)) 51 rxRE = regexp.MustCompile(reStart(reRE)) 52 rxSingle = regexp.MustCompile(reStart(reSingle)) 53 rxOff = regexp.MustCompile(reStart(reOff)) 54 ) 55 56 // ResolveOffset resolves an offset to an addr 57 func (f *FileBuffer) ResolveOffset(cmd string) (offset, cmdOffset int, e error) { 58 ms := rxOff.FindStringSubmatch(cmd) 59 // 0: full 60 // 1: without whitespace 61 // 2: num 62 // 3: offset full 63 // 4: offset +/- 64 // 5: offset num 65 if len(ms) == 0 { 66 return 67 } 68 n := 1 69 cmdOffset = len(ms[0]) 70 switch { 71 case len(ms[2]) > 0: 72 // num 73 if n, e = strconv.Atoi(ms[2]); e != nil { 74 return 75 } 76 offset = n 77 case len(ms[3]) > 0: 78 // offset 79 if len(ms[5]) > 0 { 80 if n, e = strconv.Atoi(ms[5]); e != nil { 81 return 82 } 83 } 84 switch ms[4][0] { 85 case '+': 86 offset = n 87 case '-': 88 offset = -n 89 } 90 } 91 return 92 } 93 94 // ResolveAddr resolves a command address from a cmd string 95 // - makes no attempt to verify that the resulting addr is valid 96 func (f *FileBuffer) ResolveAddr(cmd string) (line, cmdOffset int, e error) { 97 line = f.GetAddr() 98 cmdOffset = 0 99 m := rxSingle.FindString(cmd) 100 if len(m) == 0 { 101 // no match 102 return 103 } 104 cmdOffset = len(m) 105 switch { 106 case rxSingleSymbol.MatchString(m): 107 // no need to rematch; these are all single char 108 switch m[0] { 109 case '.': 110 // current 111 case '$': 112 // last 113 line = f.Len() - 1 114 } 115 case rxNumber.MatchString(m): 116 var n int 117 ns := rxNumber.FindString(m) 118 if n, e = strconv.Atoi(ns); e != nil { 119 return 120 } 121 line = n - 1 122 case rxOffset.MatchString(m): 123 n := 1 124 ns := rxOffset.FindStringSubmatch(m) 125 if len(ns[3]) > 0 { 126 if n, e = strconv.Atoi(ns[3]); e != nil { 127 return 128 } 129 } 130 switch ns[2][0] { 131 case '+': 132 line = f.GetAddr() + n 133 case '-': 134 line = f.GetAddr() - n 135 } 136 case rxMark.MatchString(m): 137 c := m[1] // len should already be verified by regexp 138 line, e = buffer.GetMark(c) 139 case rxRE.MatchString(m): 140 r := rxRE.FindAllStringSubmatch(m, -1) 141 // 0: full 142 // 1: regexp w/ delim 143 // 2: regexp 144 if len(r) < 1 || len(r[0]) < 3 { 145 e = fmt.Errorf("invalid regexp: %s", m) 146 return 147 } 148 restr := r[0][2] 149 sign := 1 150 if r[0][0][0] == '?' { 151 sign = -1 152 restr = r[0][3] 153 } 154 var re *regexp.Regexp 155 if re, e = regexp.Compile(restr); e != nil { 156 e = fmt.Errorf("invalid regexp: %v", e) 157 return 158 } 159 var c int 160 for i := 0; i < f.Len(); i++ { 161 c = (sign*i + f.GetAddr() + f.Len()) % f.Len() 162 if re.MatchString(f.GetMust(c, false)) { 163 line = c 164 return 165 } 166 } 167 e = fmt.Errorf("regexp not found: %s", restr) 168 } 169 if e != nil { 170 return 171 } 172 173 for { 174 var off, cmdoff int 175 if off, cmdoff, e = f.ResolveOffset(cmd[cmdOffset:]); e != nil { 176 return 177 } 178 if cmdoff > 0 { 179 // we got an offset 180 line += off 181 cmdOffset += cmdoff 182 } else { 183 // no more offsets 184 break 185 } 186 } 187 return 188 } 189 190 // ResolveAddrs resolves all addrs at the begining of a line 191 // - makes no attempt to verify that the resulting addrs are valid 192 // - will always return at least one addr as long as there isn't an error 193 // - if an error is reached, return value behavior is undefined 194 func (f *FileBuffer) ResolveAddrs(cmd string) (lines []int, cmdOffset int, e error) { 195 var line, off int 196 197 Loop: 198 for cmdOffset < len(cmd) { 199 cmdOffset += wsOffset(cmd[cmdOffset:]) 200 if line, off, e = f.ResolveAddr(cmd[cmdOffset:]); e != nil { 201 return 202 } 203 lines = append(lines, line) 204 cmdOffset += off 205 cmdOffset += wsOffset(cmd[cmdOffset:]) 206 if len(cmd)-1 <= cmdOffset { 207 return 208 } 209 switch cmd[cmdOffset] { // do we have more addrs? 210 case ',': 211 cmdOffset++ 212 case ';': 213 // we're the left side of a ; set the current addr 214 if e = f.SetAddr(line); e != nil { 215 return 216 } 217 cmdOffset++ 218 case '%': 219 lines = append(lines, 0, f.Len()-1) 220 cmdOffset++ 221 cmdOffset += wsOffset(cmd[cmdOffset:]) 222 return 223 default: 224 break Loop 225 } 226 } 227 return 228 } 229 230 // wsOffset is a helper to find the offset to skip whitespace 231 func wsOffset(cmd string) (o int) { 232 o = 0 233 ws := rxWhitespace.FindStringIndex(cmd) 234 if ws != nil { 235 o = ws[1] 236 } 237 return 238 } 239 240 /* 241 * The following three functions, AddrValue, AddrRange, AddrRangeOrLine all get the specified 242 * type of address from an already parsed address. 243 */ 244 245 // AddrValue gets the resolved single-line address 246 func (f *FileBuffer) AddrValue(addrs []int) (r int, e error) { 247 if len(addrs) == 0 { 248 e = ErrINV 249 return 250 } 251 r = addrs[len(addrs)-1] 252 if f.OOB(r) { 253 e = ErrOOB 254 } 255 return 256 } 257 258 // AddrRange gets and address range, fails if we don't have a range specified 259 func (f *FileBuffer) AddrRange(addrs []int) (r [2]int, e error) { 260 switch len(addrs) { 261 case 0: 262 e = ErrINV 263 return 264 case 1: 265 r[0] = f.GetAddr() 266 r[1] = addrs[0] 267 default: 268 r[0] = addrs[len(addrs)-2] 269 r[1] = addrs[len(addrs)-1] 270 } 271 if f.OOB(r[0]) || f.OOB(r[1]) { 272 e = ErrOOB 273 } 274 if r[0] > r[1] { 275 e = fmt.Errorf("address out of order") 276 } 277 return 278 } 279 280 // AddrRangeOrLine returns a range or single line if no range could be resolved 281 func (f *FileBuffer) AddrRangeOrLine(addrs []int) (r [2]int, e error) { 282 if len(addrs) > 1 { 283 // delete a range 284 if r, e = buffer.AddrRange(addrs); e != nil { 285 return 286 } 287 } else { 288 // delete a line 289 if r[0], e = buffer.AddrValue(addrs); e != nil { 290 return 291 } 292 r[1] = r[0] 293 } 294 return 295 }