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

Annotation of pkgsrc/pkgtools/pkglint/files/vartype.go, Revision 1.38

1.23      rillig      1: package pkglint
1.1       rillig      2:
1.30      rillig      3: import (
                      4:        "path"
                      5:        "strings"
                      6: )
1.1       rillig      7:
                      8: // Vartype is a combination of a data type and a permission specification.
                      9: // See vardefs.go for examples, and vartypecheck.go for the implementation.
                     10: type Vartype struct {
1.10      rillig     11:        basicType  *BasicType
1.30      rillig     12:        options    vartypeOptions
1.13      rillig     13:        aclEntries []ACLEntry
1.1       rillig     14: }
                     15:
1.32      rillig     16: func NewVartype(basicType *BasicType, options vartypeOptions, aclEntries ...ACLEntry) *Vartype {
                     17:        return &Vartype{basicType, options, aclEntries}
                     18: }
                     19:
1.30      rillig     20: type vartypeOptions uint8
1.1       rillig     21:
                     22: const (
1.30      rillig     23:        // List is a compound type, consisting of several space-separated elements.
                     24:        // Elements can have embedded spaces by enclosing them in double or single
                     25:        // quotes, like in the shell.
1.25      rillig     26:        //
                     27:        // These lists are used in the :M, :S modifiers, in .for loops,
                     28:        // and as lists of arbitrary things.
1.30      rillig     29:        List vartypeOptions = 1 << iota
                     30:
                     31:        Guessed
                     32:        PackageSettable
                     33:        UserSettable
                     34:        SystemProvided
                     35:        CommandLineProvided
1.31      rillig     36:
                     37:        // NeedsRationale marks variables that should always contain a comment
                     38:        // describing why they are set. Typical examples are NOT_FOR_* variables.
                     39:        NeedsRationale
                     40:
1.38    ! rillig     41:        OnePerLine
        !            42:
1.30      rillig     43:        NoVartypeOptions = 0
1.1       rillig     44: )
                     45:
1.13      rillig     46: type ACLEntry struct {
1.34      rillig     47:        matcher     *pathMatcher
1.13      rillig     48:        permissions ACLPermissions
1.1       rillig     49: }
                     50:
1.32      rillig     51: func NewACLEntry(glob string, permissions ACLPermissions) ACLEntry {
1.34      rillig     52:        return ACLEntry{newPathMatcher(glob), permissions}
1.32      rillig     53: }
                     54:
1.13      rillig     55: type ACLPermissions uint8
1.1       rillig     56:
                     57: const (
1.13      rillig     58:        aclpSet         ACLPermissions = 1 << iota // VAR = value
1.5       rillig     59:        aclpSetDefault                             // VAR ?= value
                     60:        aclpAppend                                 // VAR += value
                     61:        aclpUseLoadtime                            // OTHER := ${VAR}, OTHER != ${VAR}
                     62:        aclpUse                                    // OTHER = ${VAR}
1.28      rillig     63:
1.29      rillig     64:        aclpNone ACLPermissions = 0
                     65:
1.13      rillig     66:        aclpAllWrite   = aclpSet | aclpSetDefault | aclpAppend
                     67:        aclpAllRead    = aclpUseLoadtime | aclpUse
1.23      rillig     68:        aclpAll        = aclpAllWrite | aclpAllRead
                     69:        aclpAllRuntime = aclpAll &^ aclpUseLoadtime
1.1       rillig     70: )
                     71:
1.29      rillig     72: // Contains returns whether each permission of the given subset is
                     73: // contained in this permission set.
1.13      rillig     74: func (perms ACLPermissions) Contains(subset ACLPermissions) bool {
1.5       rillig     75:        return perms&subset == subset
                     76: }
                     77:
1.13      rillig     78: func (perms ACLPermissions) String() string {
1.5       rillig     79:        if perms == 0 {
                     80:                return "none"
                     81:        }
1.26      rillig     82:        return joinSkipEmpty(", ",
1.35      rillig     83:                condStr(perms.Contains(aclpSet), "set", ""),
                     84:                condStr(perms.Contains(aclpSetDefault), "set-default", ""),
                     85:                condStr(perms.Contains(aclpAppend), "append", ""),
                     86:                condStr(perms.Contains(aclpUseLoadtime), "use-loadtime", ""),
                     87:                condStr(perms.Contains(aclpUse), "use", ""))
1.5       rillig     88: }
                     89:
1.13      rillig     90: func (perms ACLPermissions) HumanString() string {
1.26      rillig     91:        return joinSkipEmptyOxford("or",
1.35      rillig     92:                condStr(perms.Contains(aclpSet), "set", ""),
                     93:                condStr(perms.Contains(aclpSetDefault), "given a default value", ""),
                     94:                condStr(perms.Contains(aclpAppend), "appended to", ""),
                     95:                condStr(perms.Contains(aclpUseLoadtime), "used at load time", ""),
                     96:                condStr(perms.Contains(aclpUse), "used", ""))
1.5       rillig     97: }
                     98:
1.30      rillig     99: func (vt *Vartype) List() bool                { return vt.options&List != 0 }
                    100: func (vt *Vartype) Guessed() bool             { return vt.options&Guessed != 0 }
                    101: func (vt *Vartype) PackageSettable() bool     { return vt.options&PackageSettable != 0 }
                    102: func (vt *Vartype) UserSettable() bool        { return vt.options&UserSettable != 0 }
                    103: func (vt *Vartype) SystemProvided() bool      { return vt.options&SystemProvided != 0 }
                    104: func (vt *Vartype) CommandLineProvided() bool { return vt.options&CommandLineProvided != 0 }
1.31      rillig    105: func (vt *Vartype) NeedsRationale() bool      { return vt.options&NeedsRationale != 0 }
1.38    ! rillig    106: func (vt *Vartype) OnePerLine() bool          { return vt.options&OnePerLine != 0 }
1.30      rillig    107:
1.18      rillig    108: func (vt *Vartype) EffectivePermissions(basename string) ACLPermissions {
1.2       rillig    109:        for _, aclEntry := range vt.aclEntries {
1.34      rillig    110:                if aclEntry.matcher.matches(basename) {
1.1       rillig    111:                        return aclEntry.permissions
                    112:                }
                    113:        }
1.29      rillig    114:        return aclpNone
1.1       rillig    115: }
                    116:
1.16      rillig    117: // Union returns the union of all possible permissions.
                    118: // This can be used to check whether a variable may be defined or used
                    119: // at all, or if it is read-only.
1.13      rillig    120: func (vt *Vartype) Union() ACLPermissions {
                    121:        var permissions ACLPermissions
1.2       rillig    122:        for _, aclEntry := range vt.aclEntries {
1.5       rillig    123:                permissions |= aclEntry.permissions
1.1       rillig    124:        }
                    125:        return permissions
                    126: }
                    127:
1.29      rillig    128: // AlternativeFiles lists the file patterns in which all of the given
                    129: // permissions are allowed, readily formatted to be used in a diagnostic.
                    130: //
                    131: // If the permission is allowed nowhere, an empty string is returned.
                    132: func (vt *Vartype) AlternativeFiles(perms ACLPermissions) string {
1.32      rillig    133:        var pos []string
                    134:        var neg []string
1.29      rillig    135:
                    136:        merge := func(slice []string) []string {
                    137:                di := 0
                    138:                for si, early := range slice {
                    139:                        redundant := false
                    140:                        for _, late := range slice[si+1:] {
                    141:                                matched, err := path.Match(late, early)
1.33      rillig    142:                                assertNil(err, "path.Match")
1.32      rillig    143:                                if matched {
1.29      rillig    144:                                        redundant = true
                    145:                                        break
                    146:                                }
                    147:                        }
                    148:                        if !redundant {
                    149:                                slice[di] = early
                    150:                                di++
                    151:                        }
                    152:                }
                    153:                return slice[:di]
                    154:        }
                    155:
1.5       rillig    156:        for _, aclEntry := range vt.aclEntries {
                    157:                if aclEntry.permissions.Contains(perms) {
1.34      rillig    158:                        pos = append(pos, aclEntry.matcher.originalPattern)
1.29      rillig    159:                } else {
1.34      rillig    160:                        neg = append(neg, aclEntry.matcher.originalPattern)
1.5       rillig    161:                }
                    162:        }
1.29      rillig    163:
                    164:        if len(neg) == 0 {
                    165:                pos = merge(pos)
                    166:        }
                    167:        if len(pos) == 0 {
                    168:                neg = merge(neg)
                    169:        }
                    170:
                    171:        positive := joinSkipEmptyCambridge("or", pos...)
                    172:        if positive == "" {
                    173:                return ""
                    174:        }
                    175:
                    176:        negative := joinSkipEmptyCambridge("or", neg...)
                    177:        if negative == "" {
                    178:                return positive
                    179:        }
                    180:
                    181:        if negative == "*" {
                    182:                return positive + " only"
                    183:        }
                    184:
                    185:        return positive + ", but not " + negative
1.5       rillig    186: }
                    187:
1.30      rillig    188: func (vt *Vartype) MayBeAppendedTo() bool {
                    189:        if vt.List() {
1.1       rillig    190:                return true
                    191:        }
1.30      rillig    192:
1.10      rillig    193:        switch vt.basicType {
1.26      rillig    194:        case BtAwkCommand, BtSedCommands, BtShellCommand, BtShellCommands, BtConfFiles:
1.1       rillig    195:                return true
1.26      rillig    196:        case BtComment, BtLicense:
                    197:                return true
                    198:        }
                    199:        return false
1.1       rillig    200: }
                    201:
1.2       rillig    202: func (vt *Vartype) String() string {
1.30      rillig    203:        var opts []string
                    204:        if vt.List() {
                    205:                opts = append(opts, "list")
                    206:        }
                    207:        if vt.Guessed() {
                    208:                opts = append(opts, "guessed")
                    209:        }
                    210:        if vt.PackageSettable() {
                    211:                opts = append(opts, "package-settable")
                    212:        }
                    213:        if vt.UserSettable() {
                    214:                opts = append(opts, "user-settable")
                    215:        }
                    216:        if vt.SystemProvided() {
                    217:                opts = append(opts, "system-provided")
                    218:        }
                    219:        if vt.CommandLineProvided() {
                    220:                opts = append(opts, "command-line-provided")
                    221:        }
                    222:
                    223:        optsSuffix := ""
                    224:        if len(opts) > 0 {
                    225:                optsSuffix = " (" + strings.Join(opts, ", ") + ")"
                    226:        }
                    227:
                    228:        return vt.basicType.name + optsSuffix
1.1       rillig    229: }
                    230:
1.7       rillig    231: func (vt *Vartype) IsShell() bool {
1.10      rillig    232:        switch vt.basicType {
                    233:        case BtCFlag, // Subtype of ShellWord
                    234:                BtLdFlag, // Subtype of ShellWord
                    235:                BtSedCommands,
                    236:                BtShellCommand,
                    237:                BtShellCommands,
                    238:                BtShellWord:
1.7       rillig    239:                return true
                    240:        }
                    241:        return false
                    242: }
                    243:
1.21      rillig    244: // NeedsQ returns whether variables of this type need the :Q
                    245: // modifier to be safely embedded in other variables or shell programs.
                    246: //
                    247: // Variables that can consists only of characters like A-Za-z0-9-._
                    248: // don't need the :Q modifier. All others do, for safety reasons.
                    249: func (bt *BasicType) NeedsQ() bool {
                    250:        switch bt {
1.10      rillig    251:        case BtBuildlinkDepmethod,
                    252:                BtCategory,
                    253:                BtDistSuffix,
                    254:                BtEmulPlatform,
                    255:                BtFileMode,
1.37      rillig    256:                BtFilename,
1.10      rillig    257:                BtIdentifier,
                    258:                BtInteger,
                    259:                BtMachineGnuPlatform,
                    260:                BtMachinePlatform,
                    261:                BtOption,
                    262:                BtPathname,
                    263:                BtPerl5Packlist,
1.37      rillig    264:                BtPkgname,
1.10      rillig    265:                BtPkgOptionsVar,
1.37      rillig    266:                BtPkgpath,
                    267:                BtPkgrevision,
1.10      rillig    268:                BtPrefixPathname,
                    269:                BtPythonDependency,
1.36      rillig    270:                BtRPkgName,
                    271:                BtRPkgVer,
1.10      rillig    272:                BtRelativePkgDir,
                    273:                BtRelativePkgPath,
                    274:                BtStage,
1.14      rillig    275:                BtTool, // Sometimes contains a colon, but that should be ok.
1.10      rillig    276:                BtUserGroupName,
                    277:                BtVersion,
                    278:                BtWrkdirSubdirectory,
                    279:                BtYesNo,
                    280:                BtYesNoIndirectly:
1.21      rillig    281:                return false
1.7       rillig    282:        }
1.21      rillig    283:        return !bt.IsEnum()
1.7       rillig    284: }
                    285:
1.10      rillig    286: type BasicType struct {
1.1       rillig    287:        name    string
                    288:        checker func(*VartypeCheck)
                    289: }
                    290:
1.10      rillig    291: func (bt *BasicType) IsEnum() bool {
                    292:        return hasPrefix(bt.name, "enum: ")
1.1       rillig    293: }
1.25      rillig    294:
1.10      rillig    295: func (bt *BasicType) HasEnum(value string) bool {
                    296:        return !contains(value, " ") && contains(bt.name, " "+value+" ")
1.1       rillig    297: }
1.25      rillig    298:
1.10      rillig    299: func (bt *BasicType) AllowedEnums() string {
                    300:        return bt.name[6 : len(bt.name)-1]
1.1       rillig    301: }
                    302:
1.22      rillig    303: // TODO: Try to implement BasicType.PossibleChars()
                    304: // TODO: Try to implement BasicType.CanBeEmpty()
                    305: // TODO: Try to implement BasicType.PossibleWords() / PossibleValues()
                    306:
1.1       rillig    307: var (
1.10      rillig    308:        BtAwkCommand             = &BasicType{"AwkCommand", (*VartypeCheck).AwkCommand}
                    309:        BtBasicRegularExpression = &BasicType{"BasicRegularExpression", (*VartypeCheck).BasicRegularExpression}
                    310:        BtBuildlinkDepmethod     = &BasicType{"BuildlinkDepmethod", (*VartypeCheck).BuildlinkDepmethod}
                    311:        BtCategory               = &BasicType{"Category", (*VartypeCheck).Category}
                    312:        BtCFlag                  = &BasicType{"CFlag", (*VartypeCheck).CFlag}
                    313:        BtComment                = &BasicType{"Comment", (*VartypeCheck).Comment}
1.12      rillig    314:        BtConfFiles              = &BasicType{"ConfFiles", (*VartypeCheck).ConfFiles}
1.10      rillig    315:        BtDependency             = &BasicType{"Dependency", (*VartypeCheck).Dependency}
                    316:        BtDependencyWithPath     = &BasicType{"DependencyWithPath", (*VartypeCheck).DependencyWithPath}
                    317:        BtDistSuffix             = &BasicType{"DistSuffix", (*VartypeCheck).DistSuffix}
                    318:        BtEmulPlatform           = &BasicType{"EmulPlatform", (*VartypeCheck).EmulPlatform}
                    319:        BtFetchURL               = &BasicType{"FetchURL", (*VartypeCheck).FetchURL}
1.37      rillig    320:        BtFilename               = &BasicType{"Filename", (*VartypeCheck).Filename}
                    321:        BtFilePattern            = &BasicType{"FilePattern", (*VartypeCheck).FilePattern}
1.10      rillig    322:        BtFileMode               = &BasicType{"FileMode", (*VartypeCheck).FileMode}
1.19      rillig    323:        BtGccReqd                = &BasicType{"GccReqd", (*VartypeCheck).GccReqd}
1.10      rillig    324:        BtHomepage               = &BasicType{"Homepage", (*VartypeCheck).Homepage}
                    325:        BtIdentifier             = &BasicType{"Identifier", (*VartypeCheck).Identifier}
                    326:        BtInteger                = &BasicType{"Integer", (*VartypeCheck).Integer}
                    327:        BtLdFlag                 = &BasicType{"LdFlag", (*VartypeCheck).LdFlag}
                    328:        BtLicense                = &BasicType{"License", (*VartypeCheck).License}
                    329:        BtMachineGnuPlatform     = &BasicType{"MachineGnuPlatform", (*VartypeCheck).MachineGnuPlatform}
                    330:        BtMachinePlatform        = &BasicType{"MachinePlatform", (*VartypeCheck).MachinePlatform}
                    331:        BtMachinePlatformPattern = &BasicType{"MachinePlatformPattern", (*VartypeCheck).MachinePlatformPattern}
                    332:        BtMailAddress            = &BasicType{"MailAddress", (*VartypeCheck).MailAddress}
                    333:        BtMessage                = &BasicType{"Message", (*VartypeCheck).Message}
                    334:        BtOption                 = &BasicType{"Option", (*VartypeCheck).Option}
                    335:        BtPathlist               = &BasicType{"Pathlist", (*VartypeCheck).Pathlist}
1.37      rillig    336:        BtPathPattern            = &BasicType{"PathPattern", (*VartypeCheck).PathPattern}
1.21      rillig    337:        BtPathname               = &BasicType{"Pathname", (*VartypeCheck).Pathname}
1.10      rillig    338:        BtPerl5Packlist          = &BasicType{"Perl5Packlist", (*VartypeCheck).Perl5Packlist}
                    339:        BtPerms                  = &BasicType{"Perms", (*VartypeCheck).Perms}
1.37      rillig    340:        BtPkgname                = &BasicType{"Pkgname", (*VartypeCheck).Pkgname}
                    341:        BtPkgpath                = &BasicType{"Pkgpath", (*VartypeCheck).Pkgpath}
1.10      rillig    342:        BtPkgOptionsVar          = &BasicType{"PkgOptionsVar", (*VartypeCheck).PkgOptionsVar}
1.37      rillig    343:        BtPkgrevision            = &BasicType{"Pkgrevision", (*VartypeCheck).Pkgrevision}
1.10      rillig    344:        BtPrefixPathname         = &BasicType{"PrefixPathname", (*VartypeCheck).PrefixPathname}
                    345:        BtPythonDependency       = &BasicType{"PythonDependency", (*VartypeCheck).PythonDependency}
1.36      rillig    346:        BtRPkgName               = &BasicType{"RPkgName", (*VartypeCheck).RPkgName}
                    347:        BtRPkgVer                = &BasicType{"RPkgVer", (*VartypeCheck).RPkgVer}
1.10      rillig    348:        BtRelativePkgDir         = &BasicType{"RelativePkgDir", (*VartypeCheck).RelativePkgDir}
                    349:        BtRelativePkgPath        = &BasicType{"RelativePkgPath", (*VartypeCheck).RelativePkgPath}
                    350:        BtRestricted             = &BasicType{"Restricted", (*VartypeCheck).Restricted}
                    351:        BtSedCommands            = &BasicType{"SedCommands", (*VartypeCheck).SedCommands}
                    352:        BtShellCommand           = &BasicType{"ShellCommand", nil}
                    353:        BtShellCommands          = &BasicType{"ShellCommands", nil}
                    354:        BtShellWord              = &BasicType{"ShellWord", nil}
                    355:        BtStage                  = &BasicType{"Stage", (*VartypeCheck).Stage}
                    356:        BtTool                   = &BasicType{"Tool", (*VartypeCheck).Tool}
                    357:        BtUnknown                = &BasicType{"Unknown", (*VartypeCheck).Unknown}
                    358:        BtURL                    = &BasicType{"URL", (*VartypeCheck).URL}
                    359:        BtUserGroupName          = &BasicType{"UserGroupName", (*VartypeCheck).UserGroupName}
                    360:        BtVariableName           = &BasicType{"VariableName", (*VartypeCheck).VariableName}
1.34      rillig    361:        BtVariableNamePattern    = &BasicType{"VariableNamePattern", (*VartypeCheck).VariableNamePattern}
1.10      rillig    362:        BtVersion                = &BasicType{"Version", (*VartypeCheck).Version}
                    363:        BtWrapperReorder         = &BasicType{"WrapperReorder", (*VartypeCheck).WrapperReorder}
                    364:        BtWrapperTransform       = &BasicType{"WrapperTransform", (*VartypeCheck).WrapperTransform}
                    365:        BtWrkdirSubdirectory     = &BasicType{"WrkdirSubdirectory", (*VartypeCheck).WrkdirSubdirectory}
                    366:        BtWrksrcSubdirectory     = &BasicType{"WrksrcSubdirectory", (*VartypeCheck).WrksrcSubdirectory}
                    367:        BtYes                    = &BasicType{"Yes", (*VartypeCheck).Yes}
                    368:        BtYesNo                  = &BasicType{"YesNo", (*VartypeCheck).YesNo}
                    369:        BtYesNoIndirectly        = &BasicType{"YesNoIndirectly", (*VartypeCheck).YesNoIndirectly}
1.29      rillig    370:
                    371:        btForLoop = &BasicType{".for loop", nil /* never called */}
1.1       rillig    372: )
                    373:
1.25      rillig    374: // Necessary due to circular dependencies between the checkers.
                    375: //
                    376: // The Go compiler is stricter than absolutely necessary for this particular case.
                    377: // The following methods are only referred to but not invoked during initialization.
                    378: func init() {
1.10      rillig    379:        BtShellCommand.checker = (*VartypeCheck).ShellCommand
                    380:        BtShellCommands.checker = (*VartypeCheck).ShellCommands
                    381:        BtShellWord.checker = (*VartypeCheck).ShellWord
1.1       rillig    382: }

CVSweb <webmaster@jp.NetBSD.org>