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  }