Annotation of pkgsrc/pkgtools/pkglint/files/util.go, Revision 1.79
1.33 rillig 1: package pkglint
1.1 rillig 2:
3: import (
4: "fmt"
1.32 rillig 5: "hash/crc64"
1.13 rillig 6: "netbsd.org/pkglint/regex"
1.32 rillig 7: "netbsd.org/pkglint/textproc"
1.1 rillig 8: "path"
1.47 rillig 9: "reflect"
1.1 rillig 10: "regexp"
1.23 rillig 11: "sort"
1.1 rillig 12: "strconv"
13: "strings"
1.5 rillig 14: "time"
1.1 rillig 15: )
16:
1.26 rillig 17: type YesNoUnknown uint8
18:
19: const (
20: no YesNoUnknown = iota
21: yes
22: unknown
23: )
24:
25: func (ynu YesNoUnknown) String() string {
26: return [...]string{"no", "yes", "unknown"}[ynu]
27: }
28:
1.1 rillig 29: // Short names for commonly used functions.
1.59 rillig 30:
1.13 rillig 31: func contains(s, substr string) bool {
32: return strings.Contains(s, substr)
33: }
34: func hasPrefix(s, prefix string) bool {
35: return strings.HasPrefix(s, prefix)
36: }
37: func hasSuffix(s, suffix string) bool {
38: return strings.HasSuffix(s, suffix)
39: }
1.31 rillig 40: func sprintf(format string, args ...interface{}) string {
41: return fmt.Sprintf(format, args...)
42: }
1.48 rillig 43: func regcomp(re regex.Pattern) *regexp.Regexp {
44: return G.res.Compile(re)
45: }
46: func match(s string, re regex.Pattern) []string {
47: return G.res.Match(s, re)
48: }
1.15 rillig 49: func matches(s string, re regex.Pattern) bool {
1.29 rillig 50: return G.res.Matches(s, re)
1.13 rillig 51: }
1.15 rillig 52: func match1(s string, re regex.Pattern) (matched bool, m1 string) {
1.29 rillig 53: return G.res.Match1(s, re)
1.13 rillig 54: }
1.15 rillig 55: func match2(s string, re regex.Pattern) (matched bool, m1, m2 string) {
1.29 rillig 56: return G.res.Match2(s, re)
1.13 rillig 57: }
1.15 rillig 58: func match3(s string, re regex.Pattern) (matched bool, m1, m2, m3 string) {
1.29 rillig 59: return G.res.Match3(s, re)
1.13 rillig 60: }
1.29 rillig 61: func replaceAll(s string, re regex.Pattern, repl string) string {
62: return G.res.Compile(re).ReplaceAllString(s, repl)
63: }
64: func replaceAllFunc(s string, re regex.Pattern, repl func(string) string) string {
65: return G.res.Compile(re).ReplaceAllStringFunc(s, repl)
1.13 rillig 66: }
1.1 rillig 67:
1.54 rillig 68: func containsStr(slice []string, s string) bool {
69: for _, str := range slice {
70: if s == str {
71: return true
72: }
73: }
74: return false
75: }
76:
1.55 rillig 77: func mapStr(slice []string, fn func(s string) string) []string {
78: result := make([]string, len(slice))
79: for i, str := range slice {
80: result[i] = fn(str)
81: }
82: return result
83: }
84:
1.72 rillig 85: func anyStr(slice []string, fn func(s string) bool) bool {
86: for _, str := range slice {
87: if fn(str) {
88: return true
89: }
90: }
91: return false
92: }
93:
1.69 rillig 94: func filterStr(slice []string, fn func(s string) bool) []string {
95: result := make([]string, 0, len(slice))
96: for _, str := range slice {
97: if fn(str) {
98: result = append(result, str)
99: }
100: }
101: return result
102: }
103:
1.65 rillig 104: func invalidCharacters(s string, valid *textproc.ByteSet) string {
105: var unis strings.Builder
106:
107: for _, r := range s {
1.71 rillig 108: switch {
109: case r == rune(byte(r)) && valid.Contains(byte(r)):
1.65 rillig 110: continue
1.71 rillig 111: case '!' <= r && r <= '~':
112: unis.WriteByte(' ')
113: unis.WriteByte(byte(r))
114: case r == ' ':
115: unis.WriteString(" space")
116: case r == '\t':
117: unis.WriteString(" tab")
118: default:
119: _, _ = fmt.Fprintf(&unis, " %U", r)
1.65 rillig 120: }
121: }
122:
123: if unis.Len() == 0 {
124: return ""
125: }
126: return unis.String()[1:]
127: }
128:
1.36 rillig 129: // intern returns an independent copy of the given string.
130: //
131: // It should be called when only a small substring of a large string
132: // is needed for the rest of the program's lifetime.
133: //
134: // All strings allocated here will stay in memory forever,
135: // therefore it should only be used for long-lived strings.
136: func intern(str string) string { return G.interner.Intern(str) }
137:
1.30 rillig 138: // trimHspace returns str, with leading and trailing space (U+0020)
139: // and tab (U+0009) removed.
140: //
141: // It is simpler and faster than strings.TrimSpace.
142: func trimHspace(str string) string {
143: start := 0
144: end := len(str)
1.31 rillig 145: for start < end && isHspace(str[start]) {
1.30 rillig 146: start++
147: }
1.31 rillig 148: for start < end && isHspace(str[end-1]) {
1.30 rillig 149: end--
150: }
151: return str[start:end]
152: }
153:
1.46 rillig 154: func rtrimHspace(str string) string {
155: end := len(str)
156: for end > 0 && isHspace(str[end-1]) {
157: end--
158: }
159: return str[:end]
160: }
161:
1.62 rillig 162: // trimCommon returns the middle portion of the given strings that differs.
1.44 rillig 163: func trimCommon(a, b string) (string, string) {
164: // trim common prefix
165: for len(a) > 0 && len(b) > 0 && a[0] == b[0] {
166: a = a[1:]
167: b = b[1:]
168: }
169:
170: // trim common suffix
171: for len(a) > 0 && len(b) > 0 && a[len(a)-1] == b[len(b)-1] {
172: a = a[:len(a)-1]
173: b = b[:len(b)-1]
174: }
175:
176: return a, b
177: }
178:
1.68 rillig 179: func replaceOnce(s, from, to string) (ok bool, replaced string) {
180:
181: index := strings.Index(s, from)
182: if index != -1 && index == strings.LastIndex(s, from) {
183: return true, s[:index] + to + s[index+len(from):]
184: }
185: return false, s
186: }
187:
1.31 rillig 188: func isHspace(ch byte) bool {
189: return ch == ' ' || ch == '\t'
190: }
191:
1.49 rillig 192: func condStr(cond bool, a, b string) string {
193: if cond {
194: return a
195: }
196: return b
1.46 rillig 197: }
198:
1.49 rillig 199: func condInt(cond bool, trueValue, falseValue int) int {
1.1 rillig 200: if cond {
1.49 rillig 201: return trueValue
1.1 rillig 202: }
1.49 rillig 203: return falseValue
1.1 rillig 204: }
205:
1.23 rillig 206: func keysJoined(m map[string]bool) string {
1.74 rillig 207: return strings.Join(keysSorted(m), " ")
208: }
209:
210: func keysSorted(m map[string]bool) []string {
1.23 rillig 211: var keys []string
212: for key := range m {
213: keys = append(keys, key)
214: }
215: sort.Strings(keys)
1.74 rillig 216: return keys
1.23 rillig 217: }
218:
1.51 rillig 219: func copyStringMkLine(m map[string]*MkLine) map[string]*MkLine {
220: c := make(map[string]*MkLine, len(m))
221: for k, v := range m {
222: c[k] = v
223: }
224: return c
225: }
226:
227: func forEachStringMkLine(m map[string]*MkLine, action func(s string, mkline *MkLine)) {
228: var keys []string
229: for key := range m {
230: keys = append(keys, key)
231: }
232: sort.Strings(keys)
233: for _, key := range keys {
234: action(key, m[key])
235: }
236: }
237:
1.12 rillig 238: func imax(a, b int) int {
239: if a > b {
240: return a
241: }
242: return b
243: }
244:
1.66 rillig 245: func imin(a, b int) int {
246: if a < b {
247: return a
248: }
249: return b
250: }
251:
1.46 rillig 252: // assertNil ensures that the given error is nil.
253: //
254: // Contrary to other diagnostics, the format should not end in a period
255: // since it is followed by the error.
256: //
257: // Other than Assertf, this method does not require any comparison operator in the calling code.
258: // This makes it possible to get 100% branch coverage for cases that "really can never fail".
1.40 rillig 259: func assertNil(err error, format string, args ...interface{}) {
260: if err != nil {
261: panic("Pkglint internal error: " + sprintf(format, args...) + ": " + err.Error())
1.1 rillig 262: }
263: }
264:
1.47 rillig 265: func assertNotNil(obj interface{}) {
266:
267: // https://stackoverflow.com/questions/13476349/check-for-nil-and-nil-interface-in-go
268: isNil := func() bool {
269: defer func() { _ = recover() }()
270: return reflect.ValueOf(obj).IsNil()
271: }
272:
273: if obj == nil || isNil() {
274: panic("Pkglint internal error: unexpected nil pointer")
275: }
276: }
277:
278: // assert checks that the condition is true. Otherwise it terminates the
279: // process with a fatal error message, prefixed with "Pkglint internal error".
280: //
281: // This method must only be used for programming errors.
282: // For runtime errors, use dummyLine.Fatalf.
283: func assert(cond bool) {
284: if !cond {
285: panic("Pkglint internal error")
286: }
287: }
288:
1.46 rillig 289: // assertf checks that the condition is true. Otherwise it terminates the
290: // process with a fatal error message, prefixed with "Pkglint internal error".
291: //
292: // This method must only be used for programming errors.
293: // For runtime errors, use dummyLine.Fatalf.
294: func assertf(cond bool, format string, args ...interface{}) {
295: if !cond {
296: panic("Pkglint internal error: " + sprintf(format, args...))
297: }
298: }
299:
1.62 rillig 300: func isEmptyDir(filename CurrPath) bool {
1.60 rillig 301: if filename.HasSuffixPath("CVS") {
1.35 rillig 302: return true
303: }
304:
1.59 rillig 305: dirents, err := filename.ReadDir()
1.35 rillig 306: if err != nil {
1.47 rillig 307: return true // XXX: Why not false?
1.1 rillig 308: }
1.35 rillig 309:
1.1 rillig 310: for _, dirent := range dirents {
311: name := dirent.Name()
1.17 rillig 312: if isIgnoredFilename(name) {
1.1 rillig 313: continue
314: }
1.63 rillig 315: if dirent.IsDir() && isEmptyDir(filename.JoinNoClean(NewRelPathString(name))) {
1.1 rillig 316: continue
317: }
318: return false
319: }
320: return true
321: }
322:
1.63 rillig 323: func getSubdirs(filename CurrPath) []RelPath {
1.59 rillig 324: dirents, err := filename.ReadDir()
1.1 rillig 325: if err != nil {
1.73 rillig 326: G.Logger.TechFatalf(filename, "Cannot be read: %s", err)
1.1 rillig 327: }
328:
1.63 rillig 329: var subdirs []RelPath
1.1 rillig 330: for _, dirent := range dirents {
331: name := dirent.Name()
1.63 rillig 332: if dirent.IsDir() && !isIgnoredFilename(name) && !isEmptyDir(filename.JoinNoClean(NewRelPathString(name))) {
333: subdirs = append(subdirs, NewRelPathString(name))
1.1 rillig 334: }
335: }
336: return subdirs
337: }
338:
1.32 rillig 339: func isIgnoredFilename(filename string) bool {
340: switch filename {
1.63 rillig 341: case "CVS", ".svn", ".git", ".hg", ".idea":
1.17 rillig 342: return true
343: }
1.52 rillig 344: return hasPrefix(filename, ".#")
1.17 rillig 345: }
346:
1.1 rillig 347: // Checks whether a file is already committed to the CVS repository.
1.62 rillig 348: func isCommitted(filename CurrPath) bool {
1.47 rillig 349: entries := G.loadCvsEntries(filename)
1.59 rillig 350: _, found := entries[filename.Base()]
1.47 rillig 351: return found
1.1 rillig 352: }
353:
1.47 rillig 354: // isLocallyModified tests whether a file (not a directory) is modified,
355: // as seen by CVS.
356: //
357: // There is no corresponding test for Git (as used by pkgsrc-wip) since that
358: // is more difficult to implement than simply reading a CVS/Entries file.
1.62 rillig 359: func isLocallyModified(filename CurrPath) bool {
1.47 rillig 360: entries := G.loadCvsEntries(filename)
1.59 rillig 361: entry, found := entries[filename.Base()]
1.47 rillig 362: if !found {
363: return false
364: }
1.29 rillig 365:
1.59 rillig 366: st, err := filename.Stat()
1.47 rillig 367: if err != nil {
368: return true
1.31 rillig 369: }
370:
1.47 rillig 371: // Following http://cvsman.com/cvs-1.12.12/cvs_19.php, format both timestamps.
372: cvsModTime := entry.Timestamp
373: fsModTime := st.ModTime().UTC().Format(time.ANSIC)
374: if trace.Tracing {
375: trace.Stepf("cvs.time=%q fs.time=%q", cvsModTime, fsModTime)
376: }
1.8 rillig 377:
1.47 rillig 378: return cvsModTime != fsModTime
379: }
1.28 rillig 380:
1.47 rillig 381: // CvsEntry is one of the entries in a CVS/Entries file.
382: //
383: // See http://cvsman.com/cvs-1.12.12/cvs_19.php.
384: type CvsEntry struct {
1.78 rillig 385: Name RelPath
1.47 rillig 386: Revision string
387: Timestamp string
388: Options string
389: TagDate string
1.8 rillig 390: }
391:
1.1 rillig 392: // Returns the number of columns that a string occupies when printed with
393: // a tabulator size of 8.
1.51 rillig 394: func tabWidth(s string) int { return tabWidthAppend(0, s) }
395:
1.58 rillig 396: func tabWidthSlice(strs ...string) int {
397: w := 0
398: for _, str := range strs {
399: w = tabWidthAppend(w, str)
400: }
401: return w
402: }
403:
1.51 rillig 404: func tabWidthAppend(width int, s string) int {
1.1 rillig 405: for _, r := range s {
1.51 rillig 406: assert(r != '\n')
1.1 rillig 407: if r == '\t' {
1.51 rillig 408: width = width&-8 + 8
1.1 rillig 409: } else {
1.51 rillig 410: width++
1.1 rillig 411: }
412: }
1.51 rillig 413: return width
1.1 rillig 414: }
415:
1.19 rillig 416: func detab(s string) string {
1.42 rillig 417: var detabbed strings.Builder
1.19 rillig 418: for _, r := range s {
419: if r == '\t' {
1.49 rillig 420: detabbed.WriteString(" "[:8-detabbed.Len()&7])
1.19 rillig 421: } else {
1.45 rillig 422: detabbed.WriteRune(r)
1.19 rillig 423: }
424: }
1.42 rillig 425: return detabbed.String()
1.19 rillig 426: }
427:
1.65 rillig 428: // alignWith extends str with as many tabs and spaces as needed to reach
1.45 rillig 429: // the same screen width as the other string.
430: func alignWith(str, other string) string {
1.66 rillig 431: return str + alignmentTo(str, other)
432: }
433:
434: // alignmentTo returns the whitespace that is necessary to
435: // bring str to the same width as other.
436: func alignmentTo(str, other string) string {
1.65 rillig 437: strWidth := tabWidth(str)
438: otherWidth := tabWidth(other)
1.66 rillig 439: return alignmentToWidths(strWidth, otherWidth)
440: }
441:
442: func alignmentToWidths(strWidth, otherWidth int) string {
1.65 rillig 443: if otherWidth <= strWidth {
1.66 rillig 444: return ""
1.65 rillig 445: }
446: if strWidth&-8 != otherWidth&-8 {
447: strWidth &= -8
448: }
1.66 rillig 449: return indent(otherWidth - strWidth)
1.45 rillig 450: }
451:
1.49 rillig 452: func indent(width int) string {
1.66 rillig 453: const tabsAndSpaces = "\t\t\t\t\t\t\t\t\t "
454: middle := len(tabsAndSpaces) - 7
455: if width <= 8*middle+7 {
456: start := middle - width>>3
457: end := middle + width&7
458: return tabsAndSpaces[start:end]
459: }
1.49 rillig 460: return strings.Repeat("\t", width>>3) + " "[:width&7]
461: }
462:
463: // alignmentAfter returns the indentation that is necessary to get
464: // from the given prefix to the desired width.
465: func alignmentAfter(prefix string, width int) string {
466: pw := tabWidth(prefix)
467: assert(width >= pw)
468: return indent(width - condInt(pw&-8 != width&-8, pw&-8, pw))
469: }
470:
1.20 rillig 471: func shorten(s string, maxChars int) string {
1.35 rillig 472: codePoints := 0
1.20 rillig 473: for i := range s {
1.35 rillig 474: if codePoints >= maxChars {
1.20 rillig 475: return s[:i] + "..."
476: }
1.35 rillig 477: codePoints++
1.20 rillig 478: }
479: return s
480: }
481:
1.1 rillig 482: func varnameBase(varname string) string {
1.5 rillig 483: dot := strings.IndexByte(varname, '.')
1.29 rillig 484: if dot > 0 {
1.5 rillig 485: return varname[:dot]
486: }
487: return varname
1.1 rillig 488: }
1.35 rillig 489:
1.1 rillig 490: func varnameCanon(varname string) string {
1.5 rillig 491: dot := strings.IndexByte(varname, '.')
1.29 rillig 492: if dot > 0 {
1.5 rillig 493: return varname[:dot] + ".*"
1.1 rillig 494: }
1.5 rillig 495: return varname
1.1 rillig 496: }
1.35 rillig 497:
1.1 rillig 498: func varnameParam(varname string) string {
1.5 rillig 499: dot := strings.IndexByte(varname, '.')
1.29 rillig 500: if dot > 0 {
1.5 rillig 501: return varname[dot+1:]
502: }
503: return ""
1.1 rillig 504: }
505:
1.5 rillig 506: func toInt(s string, def int) int {
507: if n, err := strconv.Atoi(s); err == nil {
508: return n
509: }
510: return def
1.1 rillig 511: }
512:
1.69 rillig 513: func containsVarUse(s string) bool {
1.66 rillig 514: if !contains(s, "$") {
515: return false
516: }
517: lex := NewMkLexer(s, nil)
518: tokens, _ := lex.MkTokens()
519: for _, token := range tokens {
520: if token.Varuse != nil {
521: return true
1.1 rillig 522: }
1.66 rillig 523: }
524: return false
1.1 rillig 525: }
526:
1.66 rillig 527: func containsVarRefLong(s string) bool {
1.64 rillig 528: if !contains(s, "$") {
529: return false
530: }
531: lex := NewMkLexer(s, nil)
532: tokens, _ := lex.MkTokens()
533: for _, token := range tokens {
1.66 rillig 534: if token.Varuse != nil && len(token.Text) > 2 {
1.64 rillig 535: return true
536: }
537: }
538: return false
1.1 rillig 539: }
540:
1.17 rillig 541: // Once remembers with which arguments its FirstTime method has been called
542: // and only returns true on each first call.
543: type Once struct {
1.35 rillig 544: seen map[uint64]struct{}
1.46 rillig 545:
546: // Only used during testing, to trace the actual arguments,
547: // since hashing is a one-way function.
548: Trace bool
1.17 rillig 549: }
550:
551: func (o *Once) FirstTime(what string) bool {
1.75 rillig 552: key := o.keyString(what)
553: firstTime := o.check(key)
1.46 rillig 554: if firstTime && o.Trace {
1.77 rillig 555: G.Logger.out.WriteLine("FirstTime: " + what)
1.46 rillig 556: }
557: return firstTime
1.32 rillig 558: }
559:
560: func (o *Once) FirstTimeSlice(whats ...string) bool {
1.75 rillig 561: key := o.keyStrings(whats)
562: firstTime := o.check(key)
1.46 rillig 563: if firstTime && o.Trace {
1.77 rillig 564: G.Logger.out.WriteLine("FirstTime: " + strings.Join(whats, ", "))
1.17 rillig 565: }
1.46 rillig 566: return firstTime
1.32 rillig 567: }
568:
1.39 rillig 569: func (o *Once) Seen(what string) bool {
1.46 rillig 570: _, seen := o.seen[o.keyString(what)]
1.39 rillig 571: return seen
572: }
573:
1.70 rillig 574: func (o *Once) SeenSlice(whats ...string) bool {
575: _, seen := o.seen[o.keyStrings(whats)]
576: return seen
577: }
578:
1.46 rillig 579: func (*Once) keyString(what string) uint64 {
580: return crc64.Checksum([]byte(what), crc64.MakeTable(crc64.ECMA))
581: }
582:
583: func (*Once) keyStrings(whats []string) uint64 {
584: crc := crc64.New(crc64.MakeTable(crc64.ECMA))
585: for i, what := range whats {
586: if i != 0 {
587: _, _ = crc.Write([]byte{0})
588: }
589: _, _ = crc.Write([]byte(what))
590: }
591: return crc.Sum64()
592: }
593:
1.32 rillig 594: func (o *Once) check(key uint64) bool {
595: if _, ok := o.seen[key]; ok {
1.17 rillig 596: return false
597: }
1.32 rillig 598: if o.seen == nil {
1.35 rillig 599: o.seen = make(map[uint64]struct{})
1.32 rillig 600: }
1.35 rillig 601: o.seen[key] = struct{}{}
1.17 rillig 602: return true
603: }
1.20 rillig 604:
605: // Scope remembers which variables are defined and which are used
606: // in a certain scope, such as a package or a file.
1.39 rillig 607: //
608: // TODO: Decide whether the scope should consider variable assignments
609: // from the pkgsrc infrastructure. For Package.checkGnuConfigureUseLanguages
610: // it would be better to ignore them completely.
611: //
612: // TODO: Merge this code with Var, which defines essentially the
613: // same features.
1.67 rillig 614: //
615: // See also substScope, which already analyzes the possible variable values
616: // based on the conditional code paths.
617: //
618: // See also RedundantScope.
1.20 rillig 619: type Scope struct {
1.74 rillig 620: vs map[string]*scopeVar
621: }
622:
623: type scopeVar struct {
624: firstDef *MkLine
625: lastDef *MkLine
626: value string
627: used *MkLine
628: fallback string
629: usedAtLoadTime bool
630: indeterminate bool
1.20 rillig 631: }
632:
633: func NewScope() Scope {
1.74 rillig 634: return Scope{make(map[string]*scopeVar)}
635: }
636:
637: func (s *Scope) v(varname string) *scopeVar {
638: if v, found := s.vs[varname]; found {
639: return v
640: }
641: var sv scopeVar
642: s.vs[varname] = &sv
643: return &sv
1.20 rillig 644: }
645:
646: // Define marks the variable and its canonicalized form as defined.
1.47 rillig 647: func (s *Scope) Define(varname string, mkline *MkLine) {
1.69 rillig 648: s.def(varname, mkline)
649: varcanon := varnameCanon(varname)
650: if varcanon != varname {
651: s.def(varcanon, mkline)
652: }
653: }
654:
655: func (s *Scope) def(name string, mkline *MkLine) {
1.74 rillig 656: v := s.v(name)
657: if v.firstDef == nil {
658: v.firstDef = mkline
1.69 rillig 659: if trace.Tracing {
660: trace.Step2("Defining %q for the first time in %s", name, mkline.String())
1.37 rillig 661: }
1.69 rillig 662: } else if trace.Tracing {
663: trace.Step2("Defining %q in %s", name, mkline.String())
664: }
1.37 rillig 665:
1.74 rillig 666: v.lastDef = mkline
1.37 rillig 667:
1.69 rillig 668: // In most cases the defining lines are indeed variable assignments.
669: // Exceptions are comments from documentation sections, which still mark
1.73 rillig 670: // the variable as defined so that it doesn't produce the "used but not defined" warning;
1.69 rillig 671: // see MkLines.collectDocumentedVariables.
672: if !mkline.IsVarassign() {
673: return
1.20 rillig 674: }
1.35 rillig 675:
1.69 rillig 676: switch mkline.Op() {
677: case opAssignAppend:
678: value := mkline.Value()
679: if trace.Tracing {
680: trace.Stepf("Scope.Define.append %s: %s = %q + %q",
1.74 rillig 681: mkline.String(), name, v.value, value)
1.69 rillig 682: }
1.74 rillig 683: v.value += " " + value
1.69 rillig 684: case opAssignDefault:
1.74 rillig 685: if v.value == "" && !v.indeterminate {
686: v.value = mkline.Value()
1.73 rillig 687: }
1.69 rillig 688: case opAssignShell:
1.74 rillig 689: v.value = ""
690: v.indeterminate = true
1.69 rillig 691: default:
1.74 rillig 692: v.value = mkline.Value()
1.20 rillig 693: }
694: }
695:
1.29 rillig 696: func (s *Scope) Fallback(varname string, value string) {
1.74 rillig 697: s.v(varname).fallback = value
1.29 rillig 698: }
699:
1.20 rillig 700: // Use marks the variable and its canonicalized form as used.
1.47 rillig 701: func (s *Scope) Use(varname string, line *MkLine, time VucTime) {
1.41 rillig 702: use := func(name string) {
1.74 rillig 703: v := s.v(name)
704: if v.used == nil {
705: v.used = line
1.41 rillig 706: if trace.Tracing {
707: trace.Step2("Using %q in %s", name, line.String())
708: }
709: }
1.47 rillig 710: if time == VucLoadTime {
1.74 rillig 711: v.usedAtLoadTime = true
1.20 rillig 712: }
713: }
1.35 rillig 714:
1.41 rillig 715: use(varname)
716: use(varnameCanon(varname))
1.20 rillig 717: }
718:
1.43 rillig 719: // Mentioned returns the first line in which the variable is either:
720: // - defined,
721: // - mentioned in a commented variable assignment,
722: // - mentioned in a documentation comment.
1.47 rillig 723: func (s *Scope) Mentioned(varname string) *MkLine {
1.74 rillig 724: return s.v(varname).firstDef
1.43 rillig 725: }
726:
1.58 rillig 727: // IsDefined tests whether the variable is defined.
1.20 rillig 728: // It does NOT test the canonicalized variable name.
1.28 rillig 729: //
1.58 rillig 730: // Even if IsDefined returns true, FirstDefinition doesn't necessarily return true
1.28 rillig 731: // since the latter ignores the default definitions from vardefs.go, keyword dummyVardefMkline.
1.58 rillig 732: func (s *Scope) IsDefined(varname string) bool {
1.74 rillig 733: mkline := s.v(varname).firstDef
1.43 rillig 734: return mkline != nil && mkline.IsVarassign()
1.20 rillig 735: }
736:
1.58 rillig 737: // IsDefinedSimilar tests whether the variable or its canonicalized form is defined.
738: func (s *Scope) IsDefinedSimilar(varname string) bool {
739: if s.IsDefined(varname) {
1.20 rillig 740: if trace.Tracing {
741: trace.Step1("Variable %q is defined", varname)
742: }
743: return true
744: }
1.35 rillig 745:
1.20 rillig 746: varcanon := varnameCanon(varname)
1.58 rillig 747: if s.IsDefined(varcanon) {
1.20 rillig 748: if trace.Tracing {
749: trace.Step2("Variable %q (similar to %q) is defined", varcanon, varname)
750: }
751: return true
752: }
753: return false
754: }
755:
1.58 rillig 756: // IsUsed tests whether the variable is used.
1.20 rillig 757: // It does NOT test the canonicalized variable name.
1.58 rillig 758: func (s *Scope) IsUsed(varname string) bool {
1.74 rillig 759: return s.v(varname).used != nil
1.20 rillig 760: }
761:
1.58 rillig 762: // IsUsedSimilar tests whether the variable or its canonicalized form is used.
763: func (s *Scope) IsUsedSimilar(varname string) bool {
1.74 rillig 764: if s.v(varname).used != nil {
1.20 rillig 765: return true
766: }
1.74 rillig 767: return s.v(varnameCanon(varname)).used != nil
1.20 rillig 768: }
769:
1.58 rillig 770: // IsUsedAtLoadTime returns true if the variable is used at load time
1.41 rillig 771: // somewhere.
1.58 rillig 772: func (s *Scope) IsUsedAtLoadTime(varname string) bool {
1.74 rillig 773: return s.v(varname).usedAtLoadTime
1.41 rillig 774: }
775:
1.28 rillig 776: // FirstDefinition returns the line in which the variable has been defined first.
1.35 rillig 777: //
1.28 rillig 778: // Having multiple definitions is typical in the branches of "if" statements.
1.43 rillig 779: //
780: // Another typical case involves two files: the included file defines a default
781: // value, and the including file later overrides that value. Or the other way
782: // round: the including file sets a value first, and the included file then
783: // assigns a default value using ?=.
1.47 rillig 784: func (s *Scope) FirstDefinition(varname string) *MkLine {
1.74 rillig 785: mkline := s.v(varname).firstDef
1.37 rillig 786: if mkline != nil && mkline.IsVarassign() {
787: lastLine := s.LastDefinition(varname)
1.39 rillig 788: if trace.Tracing && lastLine != mkline {
789: trace.Stepf("%s: FirstDefinition differs from LastDefinition in %s.",
1.63 rillig 790: mkline.String(), mkline.RelMkLine(lastLine))
1.37 rillig 791: }
792: return mkline
793: }
794: return nil // See NewPackage and G.Pkgsrc.UserDefinedVars
795: }
796:
797: // LastDefinition returns the line in which the variable has been defined last.
798: //
799: // Having multiple definitions is typical in the branches of "if" statements.
800: //
801: // Another typical case involves two files: the included file defines a default
1.43 rillig 802: // value, and the including file later overrides that value. Or the other way
803: // round: the including file sets a value first, and the included file then
804: // assigns a default value using ?=.
1.47 rillig 805: func (s *Scope) LastDefinition(varname string) *MkLine {
1.74 rillig 806: mkline := s.v(varname).lastDef
1.28 rillig 807: if mkline != nil && mkline.IsVarassign() {
808: return mkline
809: }
810: return nil // See NewPackage and G.Pkgsrc.UserDefinedVars
1.20 rillig 811: }
812:
1.43 rillig 813: // Commented returns whether the variable has only been defined in commented
814: // variable assignments. These are ignored by bmake but used heavily in
815: // mk/defaults/mk.conf for documentation.
1.47 rillig 816: func (s *Scope) Commented(varname string) *MkLine {
817: var mklines []*MkLine
1.74 rillig 818: if first := s.v(varname).firstDef; first != nil {
1.43 rillig 819: mklines = append(mklines, first)
820: }
1.74 rillig 821: if last := s.v(varname).lastDef; last != nil {
1.43 rillig 822: mklines = append(mklines, last)
823: }
824:
825: for _, mkline := range mklines {
1.45 rillig 826: if mkline.IsVarassign() {
1.43 rillig 827: return nil
828: }
829: }
830:
831: for _, mkline := range mklines {
1.45 rillig 832: if mkline.IsCommentedVarassign() {
1.43 rillig 833: return mkline
834: }
835: }
836:
837: return nil
838: }
839:
1.47 rillig 840: func (s *Scope) FirstUse(varname string) *MkLine {
1.74 rillig 841: return s.v(varname).used
1.20 rillig 842: }
1.21 rillig 843:
1.37 rillig 844: // LastValue returns the value from the last variable definition.
845: //
1.73 rillig 846: // If an empty string is returned, this can mean either that the
1.37 rillig 847: // variable value is indeed the empty string or that the variable
1.66 rillig 848: // was not found, or that the variable value cannot be determined
849: // reliably. To distinguish these cases, call LastValueFound instead.
1.37 rillig 850: func (s *Scope) LastValue(varname string) string {
1.74 rillig 851: value, _, _ := s.LastValueFound(varname)
1.37 rillig 852: return value
853: }
854:
1.74 rillig 855: func (s *Scope) LastValueFound(varname string) (value string, found bool, indeterminate bool) {
856: v := s.vs[varname]
857: if v == nil {
858: return
859: }
860:
861: value = v.value
862: found = v.firstDef != nil && v.firstDef.IsVarassign()
863: indeterminate = v.indeterminate
864: if found {
865: return
1.28 rillig 866: }
1.74 rillig 867:
868: return v.fallback, v.fallback != "", v.indeterminate
1.28 rillig 869: }
870:
871: func (s *Scope) DefineAll(other Scope) {
1.29 rillig 872: var varnames []string
1.74 rillig 873: for varname := range other.vs {
1.29 rillig 874: varnames = append(varnames, varname)
875: }
876: sort.Strings(varnames)
877:
878: for _, varname := range varnames {
1.74 rillig 879: v := other.vs[varname]
880: if v.firstDef != nil {
881: s.Define(varname, v.firstDef)
882: s.Define(varname, v.lastDef)
883: }
884: }
885: }
886:
887: func (s *Scope) forEach(action func(varname string, data *scopeVar)) {
888: var keys []string
889: for key := range s.vs {
890: keys = append(keys, key)
891: }
892: sort.Strings(keys)
893: for _, key := range keys {
894: action(key, s.vs[key])
1.28 rillig 895: }
896: }
897:
1.21 rillig 898: // The MIT License (MIT)
899: //
900: // Copyright (c) 2015 Frits van Bommel
901: //
902: // Permission is hereby granted, free of charge, to any person obtaining a copy
903: // of this software and associated documentation files (the "Software"), to deal
904: // in the Software without restriction, including without limitation the rights
905: // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
906: // copies of the Software, and to permit persons to whom the Software is
907: // furnished to do so, subject to the following conditions:
908: // The above copyright notice and this permission notice shall be included in all
909: // copies or substantial portions of the Software.
910: // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
911: // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
912: // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
913: // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
914: // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
915: // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
916: // SOFTWARE.
917: //
918: // Taken from https://github.com/fvbommel/util/blob/11997822f8/sortorder/natsort.go
919: func naturalLess(str1, str2 string) bool {
920:
921: isDigit := func(b byte) bool { return '0' <= b && b <= '9' }
922:
923: idx := 0
924: len1, len2 := len(str1), len(str2)
1.32 rillig 925: minLen := len1 + len2 - imax(len1, len2)
926: for idx < minLen {
1.21 rillig 927: c1, c2 := str1[idx], str2[idx]
928: dig1, dig2 := isDigit(c1), isDigit(c2)
929: switch {
930: case dig1 != dig2: // Digits before other characters.
931: return dig1 // True if LHS is a digit, false if the RHS is one.
932: case !dig1: // && !dig2, because dig1 == dig2
933: // UTF-8 compares bytewise-lexicographically, no need to decode
934: // codepoints.
935: if c1 != c2 {
936: return c1 < c2
937: }
938: idx++
939: default: // Digits
940: // Eat zeros.
941: idx1, idx2 := idx, idx
942: for ; idx1 < len1 && str1[idx1] == '0'; idx1++ {
943: }
944: for ; idx2 < len2 && str2[idx2] == '0'; idx2++ {
945: }
946: // Eat all digits.
947: nonZero1, nonZero2 := idx1, idx2
948: for ; idx1 < len1 && isDigit(str1[idx1]); idx1++ {
949: }
950: for ; idx2 < len2 && isDigit(str2[idx2]); idx2++ {
951: }
952: // If lengths of numbers with non-zero prefix differ, the shorter
953: // one is less.
954: if len1, len2 := idx1-nonZero1, idx2-nonZero2; len1 != len2 {
955: return len1 < len2
956: }
957: // If they're not equal, string comparison is correct.
958: if nr1, nr2 := str1[nonZero1:idx1], str2[nonZero2:idx2]; nr1 != nr2 {
959: return nr1 < nr2
960: }
1.73 rillig 961: // Otherwise, the one with fewer zeros is less.
1.21 rillig 962: // Because everything up to the number is equal, comparing the index
963: // after the zeros is sufficient.
964: if nonZero1 != nonZero2 {
965: return nonZero1 < nonZero2
966: }
967: idx = idx1
968: }
969: // They're identical so far, so continue comparing.
970: }
971: // So far they are identical. At least one is ended. If the other continues,
972: // it sorts last.
973: return len1 < len2
974: }
1.24 rillig 975:
1.63 rillig 976: // LoadsPrefs returns whether the given file, when included, loads the user
1.30 rillig 977: // preferences.
1.63 rillig 978: func LoadsPrefs(filename RelPath) bool {
1.59 rillig 979: switch filename.Base() {
1.30 rillig 980: case // See https://github.com/golang/go/issues/28057
981: "bsd.prefs.mk", // in mk/
982: "bsd.fast.prefs.mk", // in mk/
983: "bsd.builtin.mk", // in mk/buildlink3/
984: "pkgconfig-builtin.mk", // in mk/buildlink3/
1.63 rillig 985: "pkg-build-options.mk", // in mk/
986: "compiler.mk", // in mk/
987: "options.mk", // in package directories
1.30 rillig 988: "bsd.options.mk": // in mk/
1.28 rillig 989: return true
990: }
1.63 rillig 991:
992: // Just assume that every pkgsrc infrastructure file includes
993: // bsd.prefs.mk, at least indirectly.
994: return filename.ContainsPath("mk")
995: }
996:
997: func IsPrefs(filename RelPath) bool {
998: base := filename.Base()
999: return base == "bsd.prefs.mk" || base == "bsd.fast.prefs.mk"
1.24 rillig 1000: }
1.29 rillig 1001:
1002: // FileCache reduces the IO load for commonly loaded files by about 50%,
1003: // especially for buildlink3.mk and *.buildlink3.mk files.
1004: type FileCache struct {
1005: table []*fileCacheEntry
1006: mapping map[string]*fileCacheEntry // Pointers into FileCache.table
1007: hits int
1008: misses int
1009: }
1010:
1011: type fileCacheEntry struct {
1.30 rillig 1012: count int
1013: key string
1014: options LoadOptions
1.47 rillig 1015: lines *Lines
1.29 rillig 1016: }
1017:
1018: func NewFileCache(size int) *FileCache {
1019: return &FileCache{
1020: make([]*fileCacheEntry, 0, size),
1021: make(map[string]*fileCacheEntry),
1022: 0,
1023: 0}
1024: }
1025:
1.62 rillig 1026: func (c *FileCache) Put(filename CurrPath, options LoadOptions, lines *Lines) {
1.32 rillig 1027: key := c.key(filename)
1.29 rillig 1028:
1029: entry := c.mapping[key]
1030: if entry == nil {
1031: if len(c.table) == cap(c.table) {
1.30 rillig 1032: c.removeOldEntries()
1.29 rillig 1033: }
1034:
1.30 rillig 1035: entry = new(fileCacheEntry)
1.29 rillig 1036: c.table = append(c.table, entry)
1037: c.mapping[key] = entry
1038: }
1039:
1.30 rillig 1040: entry.count = 1
1041: entry.key = key
1.29 rillig 1042: entry.options = options
1043: entry.lines = lines
1044: }
1045:
1.30 rillig 1046: func (c *FileCache) removeOldEntries() {
1.45 rillig 1047: sort.Slice(c.table, func(i, j int) bool {
1048: return c.table[j].count < c.table[i].count
1049: })
1.30 rillig 1050:
1.31 rillig 1051: if G.Testing {
1.30 rillig 1052: for _, e := range c.table {
1.32 rillig 1053: if trace.Tracing {
1054: trace.Stepf("FileCache %q with count %d.", e.key, e.count)
1055: }
1.30 rillig 1056: }
1057: }
1058:
1059: minCount := c.table[len(c.table)-1].count
1060: newLen := len(c.table)
1061: for newLen > 0 && c.table[newLen-1].count == minCount {
1062: e := c.table[newLen-1]
1.32 rillig 1063: if trace.Tracing {
1064: trace.Stepf("FileCache.Evict %q with count %d.", e.key, e.count)
1.30 rillig 1065: }
1066: delete(c.mapping, e.key)
1067: newLen--
1068: }
1069: c.table = c.table[0:newLen]
1070:
1071: // To avoid files getting stuck in the cache.
1072: for _, e := range c.table {
1.32 rillig 1073: if trace.Tracing {
1074: trace.Stepf("FileCache.Halve %q with count %d.", e.key, e.count)
1.30 rillig 1075: }
1076: e.count /= 2
1077: }
1078: }
1079:
1.62 rillig 1080: func (c *FileCache) Get(filename CurrPath, options LoadOptions) *Lines {
1.32 rillig 1081: key := c.key(filename)
1.29 rillig 1082: entry, found := c.mapping[key]
1083: if found && entry.options == options {
1084: c.hits++
1085: entry.count++
1086:
1.47 rillig 1087: lines := make([]*Line, entry.lines.Len())
1.31 rillig 1088: for i, line := range entry.lines.Lines {
1.73 rillig 1089: lines[i] = NewLineMulti(filename, line.Location.lineno, line.Text, line.raw)
1.29 rillig 1090: }
1.32 rillig 1091: return NewLines(filename, lines)
1.29 rillig 1092: }
1093: c.misses++
1094: return nil
1095: }
1096:
1.62 rillig 1097: func (c *FileCache) Evict(filename CurrPath) {
1.32 rillig 1098: key := c.key(filename)
1.29 rillig 1099: entry, found := c.mapping[key]
1.46 rillig 1100: if !found {
1101: return
1102: }
1103:
1104: delete(c.mapping, key)
1.29 rillig 1105:
1.46 rillig 1106: for i, e := range c.table {
1107: if e == entry {
1108: c.table[i] = c.table[len(c.table)-1]
1109: c.table = c.table[:len(c.table)-1]
1110: return
1111: }
1.29 rillig 1112: }
1113: }
1114:
1.62 rillig 1115: func (c *FileCache) key(filename CurrPath) string { return filename.Clean().String() }
1.31 rillig 1116:
1.53 rillig 1117: func bmakeHelp(topic string) string { return bmake("help topic=" + topic) }
1.31 rillig 1118:
1119: func bmake(target string) string { return sprintf("%s %s", confMake, target) }
1.32 rillig 1120:
1121: func seeGuide(sectionName, sectionID string) string {
1122: return sprintf("See the pkgsrc guide, section %q: https://www.NetBSD.org/docs/pkgsrc/pkgsrc.html#%s",
1123: sectionName, sectionID)
1124: }
1125:
1.36 rillig 1126: // wrap performs automatic word wrapping on the given lines.
1127: //
1128: // Empty lines, indented lines and lines starting with "*" are kept as-is.
1.32 rillig 1129: func wrap(max int, lines ...string) []string {
1130: var wrapped []string
1.36 rillig 1131: var sb strings.Builder
1.32 rillig 1132:
1133: for _, line := range lines {
1.36 rillig 1134:
1.32 rillig 1135: if line == "" || isHspace(line[0]) || line[0] == '*' {
1.36 rillig 1136:
1137: // Finish current paragraph.
1138: if sb.Len() > 0 {
1139: wrapped = append(wrapped, sb.String())
1140: sb.Reset()
1.32 rillig 1141: }
1.36 rillig 1142:
1.32 rillig 1143: wrapped = append(wrapped, line)
1144: continue
1145: }
1146:
1147: lexer := textproc.NewLexer(line)
1148: for !lexer.EOF() {
1149: bol := len(lexer.Rest()) == len(line)
1150: space := lexer.NextBytesSet(textproc.Space)
1.39 rillig 1151: word := lexer.NextBytesSet(notSpace)
1.32 rillig 1152:
1.36 rillig 1153: if bol && sb.Len() > 0 {
1.32 rillig 1154: space = " "
1155: }
1156:
1.36 rillig 1157: if sb.Len() > 0 && sb.Len()+len(space)+len(word) > max {
1158: wrapped = append(wrapped, sb.String())
1159: sb.Reset()
1160: space = ""
1.32 rillig 1161: }
1162:
1.36 rillig 1163: sb.WriteString(space)
1164: sb.WriteString(word)
1.32 rillig 1165: }
1166: }
1167:
1.36 rillig 1168: if sb.Len() > 0 {
1169: wrapped = append(wrapped, sb.String())
1.32 rillig 1170: }
1171:
1172: return wrapped
1173: }
1174:
1175: // escapePrintable returns an ASCII-only string that represents the given string
1176: // very closely, but without putting any physical terminal or terminal emulator
1177: // at the risk of interpreting malicious data from the files checked by pkglint.
1178: // This escaping is not reversible, and it doesn't need to.
1179: func escapePrintable(s string) string {
1.61 rillig 1180: escaped := NewLazyStringBuilder(s)
1181: for i, r := range s {
1.32 rillig 1182: switch {
1.61 rillig 1183: case rune(byte(r)) == r && textproc.XPrint.Contains(s[i]):
1.32 rillig 1184: escaped.WriteByte(byte(r))
1.61 rillig 1185: case r == 0xFFFD && !hasPrefix(s[i:], "\uFFFD"):
1186: _, _ = fmt.Fprintf(&escaped, "<0x%02X>", s[i])
1.32 rillig 1187: default:
1.34 rillig 1188: _, _ = fmt.Fprintf(&escaped, "<%U>", r)
1.32 rillig 1189: }
1190: }
1191: return escaped.String()
1192: }
1.36 rillig 1193:
1194: func stringSliceLess(a, b []string) bool {
1195: limit := len(a)
1196: if len(b) < limit {
1197: limit = len(b)
1198: }
1199:
1200: for i := 0; i < limit; i++ {
1201: if a[i] != b[i] {
1202: return a[i] < b[i]
1203: }
1204: }
1205:
1206: return len(a) < len(b)
1207: }
1208:
1209: func joinSkipEmpty(sep string, elements ...string) string {
1210: var nonempty []string
1211: for _, element := range elements {
1212: if element != "" {
1213: nonempty = append(nonempty, element)
1214: }
1215: }
1216: return strings.Join(nonempty, sep)
1217: }
1218:
1.74 rillig 1219: // joinCambridge returns "first, second conn third".
1220: // It is used when each element is a single word.
1221: // Empty elements are ignored completely.
1222: func joinCambridge(conn string, elements ...string) string {
1223: parts := make([]string, 0, 2+2*len(elements))
1.36 rillig 1224: for _, element := range elements {
1225: if element != "" {
1.74 rillig 1226: parts = append(parts, ", ", element)
1.36 rillig 1227: }
1228: }
1229:
1.74 rillig 1230: if len(parts) == 0 {
1231: return ""
1232: }
1233: if len(parts) < 4 {
1234: return parts[1]
1.36 rillig 1235: }
1236:
1.74 rillig 1237: parts = append(parts[1:len(parts)-2], " ", conn, " ", parts[len(parts)-1])
1238: return strings.Join(parts, "")
1.36 rillig 1239: }
1240:
1.74 rillig 1241: // joinOxford returns "first, second, conn third".
1242: // It is used when each element may consist of multiple words.
1243: // Empty elements are ignored completely.
1244: func joinOxford(conn string, elements ...string) string {
1.36 rillig 1245: var nonempty []string
1246: for _, element := range elements {
1247: if element != "" {
1248: nonempty = append(nonempty, element)
1249: }
1250: }
1251:
1252: if lastIndex := len(nonempty) - 1; lastIndex >= 1 {
1253: nonempty[lastIndex] = conn + " " + nonempty[lastIndex]
1254: }
1255:
1256: return strings.Join(nonempty, ", ")
1257: }
1258:
1.75 rillig 1259: var pathMatchers = make(map[string]*pathMatcher)
1260:
1.47 rillig 1261: type pathMatcher struct {
1262: matchType pathMatchType
1263: pattern string
1264: originalPattern string
1265: }
1266:
1267: func newPathMatcher(pattern string) *pathMatcher {
1.75 rillig 1268: matcher := pathMatchers[pattern]
1269: if matcher == nil {
1270: matcher = newPathMatcherUncached(pattern)
1271: pathMatchers[pattern] = matcher
1272: }
1273: return matcher
1274: }
1275:
1276: func newPathMatcherUncached(pattern string) *pathMatcher {
1.47 rillig 1277: assert(strings.IndexByte(pattern, '[') == -1)
1278: assert(strings.IndexByte(pattern, '?') == -1)
1279:
1280: stars := strings.Count(pattern, "*")
1281: assert(stars == 0 || stars == 1)
1282: switch {
1283: case stars == 0:
1284: return &pathMatcher{pmExact, pattern, pattern}
1285: case pattern[0] == '*':
1286: return &pathMatcher{pmSuffix, pattern[1:], pattern}
1287: default:
1288: assert(pattern[len(pattern)-1] == '*')
1289: return &pathMatcher{pmPrefix, pattern[:len(pattern)-1], pattern}
1290: }
1291: }
1292:
1293: func (m pathMatcher) matches(subject string) bool {
1294: switch m.matchType {
1295: case pmPrefix:
1296: return hasPrefix(subject, m.pattern)
1297: case pmSuffix:
1298: return hasSuffix(subject, m.pattern)
1299: default:
1300: return subject == m.pattern
1301: }
1302: }
1303:
1304: type pathMatchType uint8
1305:
1306: const (
1307: pmExact pathMatchType = iota
1308: pmPrefix
1309: pmSuffix
1310: )
1311:
1.36 rillig 1312: // StringInterner collects commonly used strings to avoid wasting heap memory
1313: // by duplicated strings.
1314: type StringInterner struct {
1315: strs map[string]string
1316: }
1317:
1318: func NewStringInterner() StringInterner {
1319: return StringInterner{make(map[string]string)}
1320: }
1321:
1322: func (si *StringInterner) Intern(str string) string {
1323: interned, found := si.strs[str]
1324: if found {
1325: return interned
1326: }
1327:
1328: // Ensure that the original string is never stored directly in the map
1329: // since it might be a substring of a very large string. The interned
1330: // strings must be completely independent of anything from the outside,
1331: // so that the large source string can be freed afterwards.
1332: var sb strings.Builder
1333: sb.WriteString(str)
1334: key := sb.String()
1335:
1336: si.strs[key] = key
1337: return key
1338: }
1.39 rillig 1339:
1.46 rillig 1340: // StringSet stores unique strings in insertion order.
1.39 rillig 1341: type StringSet struct {
1342: Elements []string
1343: seen map[string]struct{}
1344: }
1345:
1346: func NewStringSet() StringSet {
1347: return StringSet{nil, make(map[string]struct{})}
1348: }
1349:
1350: func (s *StringSet) Add(element string) {
1351: if _, found := s.seen[element]; !found {
1352: s.seen[element] = struct{}{}
1353: s.Elements = append(s.Elements, element)
1354: }
1355: }
1356:
1357: func (s *StringSet) AddAll(elements []string) {
1358: for _, element := range elements {
1359: s.Add(element)
1360: }
1361: }
1.51 rillig 1362:
1.53 rillig 1363: // See mk/tools/shquote.sh.
1364: func shquote(s string) string {
1365: if matches(s, `^[!%+,\-./0-9:=@A-Z_a-z]+$`) {
1366: return s
1367: }
1368: return "'" + strings.Replace(s, "'", "'\\''", -1) + "'"
1.51 rillig 1369: }
1.56 rillig 1370:
1371: func pathMatches(pattern, s string) bool {
1372: matched, err := path.Match(pattern, s)
1373: return err == nil && matched
1374: }
1375:
1.62 rillig 1376: type CurrPathQueue struct {
1377: entries []CurrPath
1.56 rillig 1378: }
1379:
1.62 rillig 1380: func (q *CurrPathQueue) PushFront(entries ...CurrPath) {
1381: q.entries = append(append([]CurrPath(nil), entries...), q.entries...)
1.56 rillig 1382: }
1383:
1.62 rillig 1384: func (q *CurrPathQueue) Push(entries ...CurrPath) {
1.56 rillig 1385: q.entries = append(q.entries, entries...)
1386: }
1387:
1.62 rillig 1388: func (q *CurrPathQueue) IsEmpty() bool {
1.56 rillig 1389: return len(q.entries) == 0
1390: }
1391:
1.62 rillig 1392: func (q *CurrPathQueue) Front() CurrPath {
1.56 rillig 1393: return q.entries[0]
1394: }
1395:
1.62 rillig 1396: func (q *CurrPathQueue) Pop() CurrPath {
1.56 rillig 1397: front := q.entries[0]
1398: q.entries = q.entries[1:]
1399: return front
1400: }
1.61 rillig 1401:
1402: // LazyStringBuilder builds a string that is most probably equal to an
1403: // already existing string. In that case, it avoids any memory allocations.
1404: type LazyStringBuilder struct {
1.62 rillig 1405: expected string
1.61 rillig 1406: len int
1407: usingBuf bool
1408: buf []byte
1409: }
1410:
1.79 ! rillig 1411: func NewLazyStringBuilder(expected string) LazyStringBuilder {
! 1412: return LazyStringBuilder{expected: expected}
! 1413: }
! 1414:
1.61 rillig 1415: func (b *LazyStringBuilder) Write(p []byte) (n int, err error) {
1416: for _, c := range p {
1417: b.WriteByte(c)
1418: }
1419: return len(p), nil
1420: }
1421:
1422: func (b *LazyStringBuilder) Len() int {
1423: return b.len
1424: }
1425:
1426: func (b *LazyStringBuilder) WriteString(s string) {
1.62 rillig 1427: if !b.usingBuf && b.len+len(s) <= len(b.expected) && hasPrefix(b.expected[b.len:], s) {
1.61 rillig 1428: b.len += len(s)
1429: return
1430: }
1431: for _, c := range []byte(s) {
1432: b.WriteByte(c)
1433: }
1434: }
1435:
1436: func (b *LazyStringBuilder) WriteByte(c byte) {
1.62 rillig 1437: if !b.usingBuf && b.len < len(b.expected) && b.expected[b.len] == c {
1.61 rillig 1438: b.len++
1439: return
1440: }
1441: b.writeToBuf(c)
1442: }
1443:
1444: func (b *LazyStringBuilder) writeToBuf(c byte) {
1445: if !b.usingBuf {
1446: if cap(b.buf) >= b.len {
1447: b.buf = b.buf[:b.len]
1.62 rillig 1448: assert(copy(b.buf, b.expected) == b.len)
1.61 rillig 1449: } else {
1.62 rillig 1450: b.buf = []byte(b.expected)[:b.len]
1.61 rillig 1451: }
1452: b.usingBuf = true
1453: }
1454:
1455: b.buf = append(b.buf, c)
1456: b.len++
1457: }
1458:
1459: func (b *LazyStringBuilder) Reset(expected string) {
1.62 rillig 1460: b.expected = expected
1.61 rillig 1461: b.usingBuf = false
1462: b.len = 0
1463: }
1464:
1465: func (b *LazyStringBuilder) String() string {
1466: if b.usingBuf {
1467: return string(b.buf[:b.len])
1468: }
1.62 rillig 1469: return b.expected[:b.len]
1.61 rillig 1470: }
1.67 rillig 1471:
1472: type interval struct {
1473: min int
1474: max int
1475: }
1476:
1477: func newInterval() *interval {
1478: return &interval{int(^uint(0) >> 1), ^int(^uint(0) >> 1)}
1479: }
1480:
1481: func (i *interval) add(x int) {
1482: if x < i.min {
1483: i.min = x
1484: }
1485: if x > i.max {
1486: i.max = x
1487: }
1488: }
1489:
1.68 rillig 1490: type optInt struct {
1491: isSet bool
1492: value int
1493: }
1494:
1495: func (i *optInt) get() int {
1496: assert(i.isSet)
1497: return i.value
1498: }
1499:
1500: func (i *optInt) set(value int) {
1501: i.value = value
1502: i.isSet = true
1503: }
1504:
1505: type bag struct {
1.73 rillig 1506: // Wrapping the slice in an extra struct avoids 'receiver might be nil'
1507: // warnings.
1508:
1509: entries []bagEntry
1.68 rillig 1510: }
1511:
1.73 rillig 1512: func (b *bag) sortDesc() {
1513: es := b.entries
1514: less := func(i, j int) bool { return es[j].count < es[i].count }
1515: sort.SliceStable(es, less)
1.68 rillig 1516: }
1517:
1.73 rillig 1518: func (b *bag) opt(index int) int {
1519: if uint(index) < uint(len(b.entries)) {
1520: return b.entries[index].count
1.68 rillig 1521: }
1522: return 0
1523: }
1524:
1.73 rillig 1525: func (b *bag) key(index int) interface{} { return b.entries[index].key }
1526:
1527: func (b *bag) add(key interface{}, count int) {
1528: b.entries = append(b.entries, bagEntry{key, count})
1.68 rillig 1529: }
1530:
1.73 rillig 1531: func (b *bag) len() int { return len(b.entries) }
1532:
1533: type bagEntry struct {
1534: key interface{}
1535: count int
1.68 rillig 1536: }
1.76 rillig 1537:
1538: type lazyBool struct {
1539: fn func() bool
1540: value bool
1541: }
1542:
1543: func newLazyBool(fn func() bool) *lazyBool { return &lazyBool{fn, false} }
1544:
1545: func (b *lazyBool) get() bool {
1546: if b.fn != nil {
1547: b.value = b.fn()
1548: b.fn = nil
1549: }
1550: return b.value
1551: }
CVSweb <webmaster@jp.NetBSD.org>