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>