github.com/maruel/nin@v0.0.0-20220112143044-f35891e3ce7e/util.go (about) 1 // Copyright 2011 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package nin 16 17 import ( 18 "fmt" 19 "os" 20 "runtime" 21 "unsafe" 22 ) 23 24 // Have a generic fall-through for different versions of C/C++. 25 26 // Log a fatalf message and exit. 27 func fatalf(msg string, s ...interface{}) { 28 fmt.Fprintf(os.Stderr, "nin: fatal: ") 29 fmt.Fprintf(os.Stderr, msg, s...) 30 fmt.Fprintf(os.Stderr, "\n") 31 // On Windows, some tools may inject extra threads. 32 // exit() may block on locks held by those threads, so forcibly exit. 33 _ = os.Stderr.Sync() 34 _ = os.Stdout.Sync() 35 os.Exit(1) 36 } 37 38 // Log a warning message. 39 func warningf(msg string, s ...interface{}) { 40 fmt.Fprintf(os.Stderr, "nin: warning: ") 41 fmt.Fprintf(os.Stderr, msg, s...) 42 fmt.Fprintf(os.Stderr, "\n") 43 } 44 45 // Log an error message. 46 func errorf(msg string, s ...interface{}) { 47 fmt.Fprintf(os.Stderr, "nin: error: ") 48 fmt.Fprintf(os.Stderr, msg, s...) 49 fmt.Fprintf(os.Stderr, "\n") 50 } 51 52 func isPathSeparator(c byte) bool { 53 return c == '/' || c == '\\' 54 } 55 56 // CanonicalizePath canonicalizes a path like "foo/../bar.h" into just "bar.h". 57 func CanonicalizePath(path string) string { 58 // TODO(maruel): Call site should be the lexers, so that it's done as a 59 // single pass. 60 // WARNING: this function is performance-critical; please benchmark 61 // any changes you make to it. 62 l := len(path) 63 if l == 0 { 64 return path 65 } 66 67 p := make([]byte, l+1) 68 copy(p, path) 69 // Tell the compiler that l is safe for p. 70 _ = p[l] 71 dst := 0 72 src := 0 73 74 if c := p[src]; c == '/' || c == '\\' { 75 if runtime.GOOS == "windows" && l > 1 { 76 // network path starts with // 77 if c := p[src+1]; c == '/' || c == '\\' { 78 src += 2 79 dst += 2 80 } else { 81 src++ 82 dst++ 83 } 84 } else { 85 src++ 86 dst++ 87 } 88 } 89 90 var components [60]int 91 for componentCount := 0; src < l; { 92 if p[src] == '.' { 93 // It is fine to read one byte past because p is l+1 in 94 // length. It will be a 0 zero if so. 95 c := p[src+1] 96 if src+1 == l || (c == '/' || c == '\\') { 97 // '.' component; eliminate. 98 src += 2 99 continue 100 } 101 if c == '.' { 102 // It is fine to read one byte past because p is l+1 in 103 // length. It will be a 0 zero if so. 104 c := p[src+2] 105 if src+2 == l || (c == '/' || c == '\\') { 106 // '..' component. Back up if possible. 107 if componentCount > 0 { 108 dst = components[componentCount-1] 109 src += 3 110 componentCount-- 111 } else { 112 p[dst] = p[src] 113 p[dst+1] = p[src+1] 114 p[dst+2] = p[src+2] 115 dst += 3 116 src += 3 117 } 118 continue 119 } 120 } 121 } 122 123 if c := p[src]; c == '/' || c == '\\' { 124 src++ 125 continue 126 } 127 128 if componentCount == len(components) { 129 fatalf("path has too many components : %s", path) 130 } 131 components[componentCount] = dst 132 componentCount++ 133 134 for src != l { 135 c := p[src] 136 if c == '/' || c == '\\' { 137 break 138 } 139 p[dst] = c 140 dst++ 141 src++ 142 } 143 // Copy '/' or final \0 character as well. 144 p[dst] = p[src] 145 dst++ 146 src++ 147 } 148 149 if dst == 0 { 150 p[dst] = '.' 151 dst += 2 152 } 153 p = p[:dst-1] 154 if runtime.GOOS == "windows" { 155 for i, c := range p { 156 if c == '\\' { 157 p[i] = '/' 158 } 159 } 160 } 161 return unsafeString(p) 162 } 163 164 // CanonicalizePathBits canonicalizes a path like "foo/../bar.h" into just 165 // "bar.h". 166 // 167 // Returns a bits set starting from lowest for a backslash that was 168 // normalized to a forward slash. (only used on Windows) 169 func CanonicalizePathBits(path string) (string, uint64) { 170 // TODO(maruel): Call site should be the lexers, so that it's done as a 171 // single pass. 172 // WARNING: this function is performance-critical; please benchmark 173 // any changes you make to it. 174 l := len(path) 175 if l == 0 { 176 return path, 0 177 } 178 179 p := make([]byte, l+1) 180 copy(p, path) 181 // Tell the compiler that l is safe for p. 182 _ = p[l] 183 dst := 0 184 src := 0 185 186 if c := p[src]; c == '/' || c == '\\' { 187 if runtime.GOOS == "windows" && l > 1 { 188 // network path starts with // 189 if c := p[src+1]; c == '/' || c == '\\' { 190 src += 2 191 dst += 2 192 } else { 193 src++ 194 dst++ 195 } 196 } else { 197 src++ 198 dst++ 199 } 200 } 201 202 var components [60]int 203 for componentCount := 0; src < l; { 204 if p[src] == '.' { 205 // It is fine to read one byte past because p is l+1 in 206 // length. It will be a 0 zero if so. 207 c := p[src+1] 208 if src+1 == l || (c == '/' || c == '\\') { 209 // '.' component; eliminate. 210 src += 2 211 continue 212 } 213 if c == '.' { 214 // It is fine to read one byte past because p is l+1 in 215 // length. It will be a 0 zero if so. 216 c := p[src+2] 217 if src+2 == l || (c == '/' || c == '\\') { 218 // '..' component. Back up if possible. 219 if componentCount > 0 { 220 dst = components[componentCount-1] 221 src += 3 222 componentCount-- 223 } else { 224 p[dst] = p[src] 225 p[dst+1] = p[src+1] 226 p[dst+2] = p[src+2] 227 dst += 3 228 src += 3 229 } 230 continue 231 } 232 } 233 } 234 235 if c := p[src]; c == '/' || c == '\\' { 236 src++ 237 continue 238 } 239 240 if componentCount == len(components) { 241 fatalf("path has too many components : %s", path) 242 } 243 components[componentCount] = dst 244 componentCount++ 245 246 for src != l { 247 c := p[src] 248 if c == '/' || c == '\\' { 249 break 250 } 251 p[dst] = c 252 dst++ 253 src++ 254 } 255 // Copy '/' or final \0 character as well. 256 p[dst] = p[src] 257 dst++ 258 src++ 259 } 260 261 if dst == 0 { 262 p[dst] = '.' 263 dst += 2 264 } 265 p = p[:dst-1] 266 bits := uint64(0) 267 if runtime.GOOS == "windows" { 268 bitsMask := uint64(1) 269 for i, c := range p { 270 switch c { 271 case '\\': 272 bits |= bitsMask 273 p[i] = '/' 274 fallthrough 275 case '/': 276 bitsMask <<= 1 277 } 278 } 279 } 280 return unsafeString(p), bits 281 } 282 283 func stringNeedsShellEscaping(input string) bool { 284 for i := 0; i < len(input); i++ { 285 ch := input[i] 286 if 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || '0' <= ch && ch <= '9' { 287 continue 288 } 289 switch ch { 290 case '_', '+', '-', '.', '/': 291 default: 292 return true 293 } 294 } 295 return false 296 } 297 298 func stringNeedsWin32Escaping(input string) bool { 299 for i := 0; i < len(input); i++ { 300 switch input[i] { 301 case ' ', '"': 302 return true 303 default: 304 } 305 } 306 return false 307 } 308 309 // Escapes the item for bash. 310 func getShellEscapedString(input string) string { 311 if !stringNeedsShellEscaping(input) { 312 return input 313 } 314 315 const quote = byte('\'') 316 // Do one pass to calculate the ending size. 317 l := len(input) + 2 318 for i := 0; i != len(input); i++ { 319 if input[i] == quote { 320 l += 3 321 } 322 } 323 324 out := make([]byte, l) 325 out[0] = quote 326 offset := 1 327 for i := 0; i < len(input); i++ { 328 c := input[i] 329 out[offset] = c 330 if c == quote { 331 offset++ 332 out[offset] = '\\' 333 offset++ 334 out[offset] = '\'' 335 offset++ 336 out[offset] = '\'' 337 } 338 offset++ 339 } 340 out[offset] = quote 341 return unsafeString(out) 342 } 343 344 // Escapes the item for Windows's CommandLineToArgvW(). 345 func getWin32EscapedString(input string) string { 346 if !stringNeedsWin32Escaping(input) { 347 return input 348 } 349 350 result := "\"" 351 consecutiveBackslashCount := 0 352 spanBegin := 0 353 for it, c := range input { 354 switch c { 355 case '\\': 356 consecutiveBackslashCount++ 357 case '"': 358 result += input[spanBegin:it] 359 for j := 0; j < consecutiveBackslashCount+1; j++ { 360 result += "\\" 361 } 362 spanBegin = it 363 consecutiveBackslashCount = 0 364 default: 365 consecutiveBackslashCount = 0 366 } 367 } 368 result += input[spanBegin:] 369 for j := 0; j < consecutiveBackslashCount; j++ { 370 result += "\\" 371 } 372 result += "\"" 373 return result 374 } 375 376 // SpellcheckString provides the closest match to a misspelled string, given a 377 // list of correct spellings. 378 // 379 // Returns "" if there is no close enough match. 380 func SpellcheckString(text string, words ...string) string { 381 const maxValidEditDistance = 3 382 383 minDistance := maxValidEditDistance + 1 384 result := "" 385 for _, i := range words { 386 distance := editDistance(i, text, true, maxValidEditDistance) 387 if distance < minDistance { 388 minDistance = distance 389 result = i 390 } 391 } 392 return result 393 } 394 395 func islatinalpha(c byte) bool { 396 // isalpha() is locale-dependent. 397 return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') 398 } 399 400 /* 401 func calculateProcessorLoad(idleTicks, totalTicks uint64) float64 { 402 static uint64T previousIdleTicks = 0 403 static uint64T previousTotalTicks = 0 404 static double previousLoad = -0.0 405 406 uint64T idleTicksSinceLastTime = idleTicks - previousIdleTicks 407 uint64T totalTicksSinceLastTime = totalTicks - previousTotalTicks 408 409 bool firstCall = (previousTotalTicks == 0) 410 bool ticksNotUpdatedSinceLastCall = (totalTicksSinceLastTime == 0) 411 412 double load 413 if (firstCall || ticksNotUpdatedSinceLastCall) { 414 load = previousLoad 415 } else { 416 // Calculate load. 417 double idleToTotalRatio = 418 ((double)idleTicksSinceLastTime) / totalTicksSinceLastTime 419 double loadSinceLastCall = 1.0 - idleToTotalRatio 420 421 // Filter/smooth result when possible. 422 if(previousLoad > 0) { 423 load = 0.9 * previousLoad + 0.1 * loadSinceLastCall 424 } else { 425 load = loadSinceLastCall 426 } 427 } 428 429 previousLoad = load 430 previousTotalTicks = totalTicks 431 previousIdleTicks = idleTicks 432 433 return load 434 } 435 436 uint64T FileTimeToTickCount(const FILETIME & ft) 437 { 438 uint64T high = (((uint64T)(ft.dwHighDateTime)) << 32) 439 uint64T low = ft.dwLowDateTime 440 return (high | low) 441 } 442 */ 443 444 // @return the load average of the machine. A negative value is returned 445 // on error. 446 func getLoadAverage() float64 { 447 /* 448 FILETIME idleTime, kernelTime, userTime 449 BOOL getSystemTimeSucceeded = 450 GetSystemTimes(&idleTime, &kernelTime, &userTime) 451 452 posixCompatibleLoad := 0. 453 if getSystemTimeSucceeded { 454 idleTicks := FileTimeToTickCount(idleTime) 455 456 // kernelTime from GetSystemTimes already includes idleTime. 457 uint64T totalTicks = 458 FileTimeToTickCount(kernelTime) + FileTimeToTickCount(userTime) 459 460 processorLoad := calculateProcessorLoad(idleTicks, totalTicks) 461 posixCompatibleLoad = processorLoad * GetProcessorCount() 462 463 } else { 464 posixCompatibleLoad = -0.0 465 } 466 467 return posixCompatibleLoad 468 */ 469 return 0 470 } 471 472 /* 473 // @return the load average of the machine. A negative value is returned 474 // on error. 475 func getLoadAverage() float64 { 476 return -0.0f 477 } 478 479 // @return the load average of the machine. A negative value is returned 480 // on error. 481 func getLoadAverage() float64 { 482 var cpuStats perfstatCpuTotalT 483 if perfstatCpuTotal(nil, &cpuStats, sizeof(cpuStats), 1) < 0 { 484 return -0.0f 485 } 486 487 // Calculation taken from comment in libperfstats.h 488 return double(cpuStats.loadavg[0]) / double(1 << SBITS) 489 } 490 491 // @return the load average of the machine. A negative value is returned 492 // on error. 493 func getLoadAverage() float64 { 494 var si sysinfo 495 if sysinfo(&si) != 0 { 496 return -0.0f 497 } 498 return 1.0 / (1 << SI_LOAD_SHIFT) * si.loads[0] 499 } 500 501 // @return the load average of the machine. A negative value is returned 502 // on error. 503 func getLoadAverage() float64 { 504 return -0.0f 505 } 506 */ 507 508 // Elide the given string @a str with '...' in the middle if the length 509 // exceeds @a width. 510 func elideMiddle(str string, width int) string { 511 switch width { 512 case 0: 513 return "" 514 case 1: 515 return "." 516 case 2: 517 return ".." 518 case 3: 519 return "..." 520 } 521 const margin = 3 // Space for "...". 522 result := str 523 if len(result) > width { 524 elideSize := (width - margin) / 2 525 result = result[0:elideSize] + "..." + result[len(result)-elideSize:] 526 } 527 return result 528 } 529 530 // unsafeString performs an unsafe conversion from a []byte to a string. The 531 // returned string will share the underlying memory with the []byte which thus 532 // allows the string to be mutable through the []byte. We're careful to use 533 // this method only in situations in which the []byte will not be modified. 534 // 535 // A workaround for the absence of https://github.com/golang/go/issues/2632. 536 func unsafeString(b []byte) string { 537 return *(*string)(unsafe.Pointer(&b)) 538 }