[BACK]Return to util.go CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / pkgsrc / pkgtools / pkglint / files

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>