[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.18

1.1       rillig      1: package main
                      2:
                      3: import (
                      4:        "fmt"
                      5:        "io/ioutil"
1.13      rillig      6:        "netbsd.org/pkglint/regex"
                      7:        "netbsd.org/pkglint/trace"
1.1       rillig      8:        "os"
                      9:        "path"
                     10:        "path/filepath"
                     11:        "regexp"
                     12:        "strconv"
                     13:        "strings"
1.5       rillig     14:        "time"
1.1       rillig     15: )
                     16:
                     17: // Short names for commonly used functions.
1.13      rillig     18: func contains(s, substr string) bool {
                     19:        return strings.Contains(s, substr)
                     20: }
                     21: func hasPrefix(s, prefix string) bool {
                     22:        return strings.HasPrefix(s, prefix)
                     23: }
                     24: func hasSuffix(s, suffix string) bool {
                     25:        return strings.HasSuffix(s, suffix)
                     26: }
1.15      rillig     27: func matches(s string, re regex.Pattern) bool {
1.13      rillig     28:        return regex.Matches(s, re)
                     29: }
1.15      rillig     30: func match1(s string, re regex.Pattern) (matched bool, m1 string) {
1.13      rillig     31:        return regex.Match1(s, re)
                     32: }
1.15      rillig     33: func match2(s string, re regex.Pattern) (matched bool, m1, m2 string) {
1.13      rillig     34:        return regex.Match2(s, re)
                     35: }
1.15      rillig     36: func match3(s string, re regex.Pattern) (matched bool, m1, m2, m3 string) {
1.13      rillig     37:        return regex.Match3(s, re)
                     38: }
1.15      rillig     39: func match4(s string, re regex.Pattern) (matched bool, m1, m2, m3, m4 string) {
1.13      rillig     40:        return regex.Match4(s, re)
                     41: }
1.1       rillig     42:
                     43: func ifelseStr(cond bool, a, b string) string {
                     44:        if cond {
                     45:                return a
                     46:        }
                     47:        return b
                     48: }
                     49:
1.12      rillig     50: func imax(a, b int) int {
                     51:        if a > b {
                     52:                return a
                     53:        }
                     54:        return b
                     55: }
                     56:
1.15      rillig     57: func mustMatch(s string, re regex.Pattern) []string {
1.13      rillig     58:        if m := regex.Match(s, re); m != nil {
1.1       rillig     59:                return m
                     60:        }
1.5       rillig     61:        panic(fmt.Sprintf("mustMatch %q %q", s, re))
1.1       rillig     62: }
                     63:
                     64: func isEmptyDir(fname string) bool {
                     65:        dirents, err := ioutil.ReadDir(fname)
                     66:        if err != nil || hasSuffix(fname, "/CVS") {
                     67:                return true
                     68:        }
                     69:        for _, dirent := range dirents {
                     70:                name := dirent.Name()
1.17      rillig     71:                if isIgnoredFilename(name) {
1.1       rillig     72:                        continue
                     73:                }
                     74:                if dirent.IsDir() && isEmptyDir(fname+"/"+name) {
                     75:                        continue
                     76:                }
                     77:                return false
                     78:        }
                     79:        return true
                     80: }
                     81:
                     82: func getSubdirs(fname string) []string {
                     83:        dirents, err := ioutil.ReadDir(fname)
                     84:        if err != nil {
1.7       rillig     85:                NewLineWhole(fname).Fatalf("Cannot be read: %s", err)
1.1       rillig     86:        }
                     87:
                     88:        var subdirs []string
                     89:        for _, dirent := range dirents {
                     90:                name := dirent.Name()
1.17      rillig     91:                if dirent.IsDir() && !isIgnoredFilename(name) && !isEmptyDir(fname+"/"+name) {
1.1       rillig     92:                        subdirs = append(subdirs, name)
                     93:                }
                     94:        }
                     95:        return subdirs
                     96: }
                     97:
1.17      rillig     98: func isIgnoredFilename(fileName string) bool {
                     99:        switch fileName {
                    100:        case ".", "..", "CVS", ".svn", ".git", ".hg":
                    101:                return true
                    102:        }
                    103:        return false
                    104: }
                    105:
1.1       rillig    106: // Checks whether a file is already committed to the CVS repository.
                    107: func isCommitted(fname string) bool {
1.10      rillig    108:        lines := loadCvsEntries(fname)
                    109:        needle := "/" + path.Base(fname) + "/"
1.1       rillig    110:        for _, line := range lines {
1.16      rillig    111:                if hasPrefix(line.Text, needle) {
1.1       rillig    112:                        return true
                    113:                }
                    114:        }
                    115:        return false
                    116: }
                    117:
1.8       rillig    118: func isLocallyModified(fname string) bool {
1.10      rillig    119:        lines := loadCvsEntries(fname)
                    120:        needle := "/" + path.Base(fname) + "/"
1.8       rillig    121:        for _, line := range lines {
1.16      rillig    122:                if hasPrefix(line.Text, needle) {
                    123:                        cvsModTime, err := time.Parse(time.ANSIC, strings.Split(line.Text, "/")[3])
1.8       rillig    124:                        if err != nil {
                    125:                                return false
                    126:                        }
                    127:                        st, err := os.Stat(fname)
                    128:                        if err != nil {
                    129:                                return false
                    130:                        }
                    131:
                    132:                        // https://msdn.microsoft.com/en-us/library/windows/desktop/ms724290(v=vs.85).aspx
1.13      rillig    133:                        // (System Services > Windows System Information > Time > About Time > File Times)
1.8       rillig    134:                        delta := cvsModTime.Unix() - st.ModTime().Unix()
1.13      rillig    135:                        if trace.Tracing {
                    136:                                trace.Stepf("cvs.time=%v fs.time=%v delta=%v", cvsModTime, st.ModTime(), delta)
1.8       rillig    137:                        }
                    138:                        return !(-2 <= delta && delta <= 2)
                    139:                }
                    140:        }
                    141:        return false
                    142: }
                    143:
1.16      rillig    144: func loadCvsEntries(fname string) []Line {
1.10      rillig    145:        dir := path.Dir(fname)
                    146:        if dir == G.CvsEntriesDir {
                    147:                return G.CvsEntriesLines
                    148:        }
                    149:
                    150:        lines, err := readLines(dir+"/CVS/Entries", false)
                    151:        if err != nil {
                    152:                return nil
                    153:        }
                    154:        G.CvsEntriesDir = dir
                    155:        G.CvsEntriesLines = lines
                    156:        return lines
                    157: }
                    158:
1.1       rillig    159: // Returns the number of columns that a string occupies when printed with
                    160: // a tabulator size of 8.
                    161: func tabLength(s string) int {
                    162:        length := 0
                    163:        for _, r := range s {
                    164:                if r == '\t' {
                    165:                        length = length - length%8 + 8
                    166:                } else {
                    167:                        length++
                    168:                }
                    169:        }
                    170:        return length
                    171: }
                    172:
                    173: func varnameBase(varname string) string {
1.5       rillig    174:        dot := strings.IndexByte(varname, '.')
                    175:        if dot != -1 {
                    176:                return varname[:dot]
                    177:        }
                    178:        return varname
1.1       rillig    179: }
                    180: func varnameCanon(varname string) string {
1.5       rillig    181:        dot := strings.IndexByte(varname, '.')
                    182:        if dot != -1 {
                    183:                return varname[:dot] + ".*"
1.1       rillig    184:        }
1.5       rillig    185:        return varname
1.1       rillig    186: }
                    187: func varnameParam(varname string) string {
1.5       rillig    188:        dot := strings.IndexByte(varname, '.')
                    189:        if dot != -1 {
                    190:                return varname[dot+1:]
                    191:        }
                    192:        return ""
1.1       rillig    193: }
                    194:
1.14      rillig    195: func defineVar(mkline MkLine, varname string) {
1.5       rillig    196:        if G.Mk != nil {
                    197:                G.Mk.DefineVar(mkline, varname)
1.1       rillig    198:        }
1.5       rillig    199:        if G.Pkg != nil {
                    200:                G.Pkg.defineVar(mkline, varname)
1.1       rillig    201:        }
                    202: }
                    203: func varIsDefined(varname string) bool {
                    204:        varcanon := varnameCanon(varname)
1.5       rillig    205:        if G.Mk != nil && (G.Mk.vardef[varname] != nil || G.Mk.vardef[varcanon] != nil) {
1.1       rillig    206:                return true
                    207:        }
1.5       rillig    208:        if G.Pkg != nil && (G.Pkg.vardef[varname] != nil || G.Pkg.vardef[varcanon] != nil) {
1.1       rillig    209:                return true
                    210:        }
                    211:        return false
                    212: }
                    213:
                    214: func varIsUsed(varname string) bool {
                    215:        varcanon := varnameCanon(varname)
1.5       rillig    216:        if G.Mk != nil && (G.Mk.varuse[varname] != nil || G.Mk.varuse[varcanon] != nil) {
1.1       rillig    217:                return true
                    218:        }
1.5       rillig    219:        if G.Pkg != nil && (G.Pkg.varuse[varname] != nil || G.Pkg.varuse[varcanon] != nil) {
1.1       rillig    220:                return true
                    221:        }
                    222:        return false
                    223: }
                    224:
                    225: func splitOnSpace(s string) []string {
1.13      rillig    226:        return regex.Compile(`\s+`).Split(s, -1)
1.1       rillig    227: }
                    228:
                    229: func fileExists(fname string) bool {
                    230:        st, err := os.Stat(fname)
                    231:        return err == nil && st.Mode().IsRegular()
                    232: }
                    233:
                    234: func dirExists(fname string) bool {
                    235:        st, err := os.Stat(fname)
                    236:        return err == nil && st.Mode().IsDir()
                    237: }
                    238:
                    239: // Useful in combination with regex.Find*Index
                    240: func negToZero(i int) int {
                    241:        if i >= 0 {
                    242:                return i
                    243:        }
                    244:        return 0
                    245: }
                    246:
1.5       rillig    247: func toInt(s string, def int) int {
                    248:        if n, err := strconv.Atoi(s); err == nil {
                    249:                return n
                    250:        }
                    251:        return def
1.1       rillig    252: }
                    253:
                    254: func dirglob(dirname string) []string {
                    255:        fis, err := ioutil.ReadDir(dirname)
                    256:        if err != nil {
                    257:                return nil
                    258:        }
1.18    ! rillig    259:        var fnames []string
        !           260:        for _, fi := range fis {
        !           261:                if !(isIgnoredFilename(fi.Name())) {
        !           262:                        fnames = append(fnames, dirname+"/"+fi.Name())
        !           263:                }
1.1       rillig    264:        }
                    265:        return fnames
                    266: }
                    267:
1.12      rillig    268: // Emulates make(1)'s :S substitution operator.
1.8       rillig    269: func mkopSubst(s string, left bool, from string, right bool, to string, flags string) string {
1.13      rillig    270:        if trace.Tracing {
                    271:                defer trace.Call(s, left, from, right, to, flags)()
1.8       rillig    272:        }
1.15      rillig    273:        re := regex.Pattern(ifelseStr(left, "^", "") + regexp.QuoteMeta(from) + ifelseStr(right, "$", ""))
1.1       rillig    274:        done := false
1.8       rillig    275:        gflag := contains(flags, "g")
1.13      rillig    276:        return regex.Compile(re).ReplaceAllStringFunc(s, func(match string) string {
1.8       rillig    277:                if gflag || !done {
                    278:                        done = !gflag
1.1       rillig    279:                        return to
                    280:                }
                    281:                return match
                    282:        })
                    283: }
                    284:
                    285: func relpath(from, to string) string {
                    286:        absFrom, err1 := filepath.Abs(from)
                    287:        absTo, err2 := filepath.Abs(to)
                    288:        rel, err3 := filepath.Rel(absFrom, absTo)
                    289:        if err1 != nil || err2 != nil || err3 != nil {
1.13      rillig    290:                trace.Stepf("relpath.panic", from, to, err1, err2, err3)
                    291:                panic("relpath")
1.1       rillig    292:        }
                    293:        result := filepath.ToSlash(rel)
1.13      rillig    294:        if trace.Tracing {
                    295:                trace.Stepf("relpath from %q to %q = %q", from, to, result)
1.5       rillig    296:        }
1.1       rillig    297:        return result
                    298: }
                    299:
                    300: func abspath(fname string) string {
                    301:        abs, err := filepath.Abs(fname)
                    302:        if err != nil {
1.7       rillig    303:                NewLineWhole(fname).Fatalf("Cannot determine absolute path.")
1.1       rillig    304:        }
                    305:        return filepath.ToSlash(abs)
                    306: }
                    307:
                    308: // Differs from path.Clean in that only "../../" is replaced, not "../".
                    309: // Also, the initial directory is always kept.
                    310: // This is to provide the package path as context in recursive invocations of pkglint.
                    311: func cleanpath(fname string) string {
                    312:        tmp := fname
                    313:        for len(tmp) > 2 && hasPrefix(tmp, "./") {
                    314:                tmp = tmp[2:]
                    315:        }
                    316:        for contains(tmp, "/./") {
                    317:                tmp = strings.Replace(tmp, "/./", "/", -1)
                    318:        }
                    319:        for contains(tmp, "//") {
                    320:                tmp = strings.Replace(tmp, "//", "/", -1)
                    321:        }
                    322:        tmp = reReplaceRepeatedly(tmp, `/[^.][^/]*/[^.][^/]*/\.\./\.\./`, "/")
                    323:        tmp = strings.TrimSuffix(tmp, "/")
                    324:        return tmp
                    325: }
                    326:
                    327: func containsVarRef(s string) bool {
                    328:        return contains(s, "${")
                    329: }
                    330:
1.15      rillig    331: func reReplaceRepeatedly(from string, re regex.Pattern, to string) string {
1.13      rillig    332:        replaced := regex.Compile(re).ReplaceAllString(from, to)
1.1       rillig    333:        if replaced != from {
                    334:                return reReplaceRepeatedly(replaced, re, to)
                    335:        }
                    336:        return replaced
                    337: }
                    338:
                    339: func hasAlnumPrefix(s string) bool {
                    340:        if s == "" {
                    341:                return false
                    342:        }
                    343:        b := s[0]
                    344:        return '0' <= b && b <= '9' || 'A' <= b && b <= 'Z' || b == '_' || 'a' <= b && b <= 'z'
                    345: }
1.17      rillig    346:
                    347: // Once remembers with which arguments its FirstTime method has been called
                    348: // and only returns true on each first call.
                    349: type Once struct {
                    350:        seen map[string]bool
                    351: }
                    352:
                    353: func (o *Once) FirstTime(what string) bool {
                    354:        if o.seen == nil {
                    355:                o.seen = make(map[string]bool)
                    356:        }
                    357:        if _, ok := o.seen[what]; ok {
                    358:                return false
                    359:        }
                    360:        o.seen[what] = true
                    361:        return true
                    362: }

CVSweb <webmaster@jp.NetBSD.org>