pkgsrc-Changes archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

CVS commit: pkgsrc/pkgtools/pkglint



Module Name:    pkgsrc
Committed By:   rillig
Date:           Mon Jun 10 19:51:57 UTC 2019

Modified Files:
        pkgsrc/pkgtools/pkglint: Makefile PLIST
        pkgsrc/pkgtools/pkglint/files: autofix.go check_test.go distinfo.go
            line.go logging.go mkline.go mkline_test.go mklinechecker.go
            mklinechecker_test.go mklines.go mklines_test.go
            mklines_varalign_test.go mkparser.go mkshparser_test.go
            mkshwalker.go mktypes.go mktypes_test.go options_test.go package.go
            package_test.go patches_test.go pkglint.go pkglint_test.go
            pkgsrc.go shell.go shell_test.go shtokenizer.go shtypes.go
            substcontext.go substcontext_test.go tools.go tools_test.go
            toplevel_test.go util.go util_test.go var.go var_test.go vardefs.go
            vardefs_test.go vartype.go vartypecheck.go vartypecheck_test.go
        pkgsrc/pkgtools/pkglint/files/trace: tracing.go
Added Files:
        pkgsrc/pkgtools/pkglint/files: paragraph.go paragraph_test.go

Log Message:
pkgtools/pkglint: updated to 5.7.13

Changes since 5.7.12:

* Fixed a warning in the variable permissions that previously said "this
  variable should not be used at load time in this file, it would be ok
  in .", with an empty list of alternative files.

* Lots of refactoring and new tests.


To generate a diff of this commit:
cvs rdiff -u -r1.584 -r1.585 pkgsrc/pkgtools/pkglint/Makefile
cvs rdiff -u -r1.11 -r1.12 pkgsrc/pkgtools/pkglint/PLIST
cvs rdiff -u -r1.23 -r1.24 pkgsrc/pkgtools/pkglint/files/autofix.go
cvs rdiff -u -r1.42 -r1.43 pkgsrc/pkgtools/pkglint/files/check_test.go \
    pkgsrc/pkgtools/pkglint/files/pkglint_test.go
cvs rdiff -u -r1.33 -r1.34 pkgsrc/pkgtools/pkglint/files/distinfo.go
cvs rdiff -u -r1.35 -r1.36 pkgsrc/pkgtools/pkglint/files/line.go
cvs rdiff -u -r1.22 -r1.23 pkgsrc/pkgtools/pkglint/files/logging.go
cvs rdiff -u -r1.52 -r1.53 pkgsrc/pkgtools/pkglint/files/mkline.go
cvs rdiff -u -r1.58 -r1.59 pkgsrc/pkgtools/pkglint/files/mkline_test.go
cvs rdiff -u -r1.40 -r1.41 pkgsrc/pkgtools/pkglint/files/mklinechecker.go
cvs rdiff -u -r1.36 -r1.37 \
    pkgsrc/pkgtools/pkglint/files/mklinechecker_test.go
cvs rdiff -u -r1.49 -r1.50 pkgsrc/pkgtools/pkglint/files/mklines.go \
    pkgsrc/pkgtools/pkglint/files/vartypecheck_test.go
cvs rdiff -u -r1.44 -r1.45 pkgsrc/pkgtools/pkglint/files/mklines_test.go
cvs rdiff -u -r1.11 -r1.12 \
    pkgsrc/pkgtools/pkglint/files/mklines_varalign_test.go
cvs rdiff -u -r1.28 -r1.29 pkgsrc/pkgtools/pkglint/files/mkparser.go \
    pkgsrc/pkgtools/pkglint/files/patches_test.go
cvs rdiff -u -r1.13 -r1.14 pkgsrc/pkgtools/pkglint/files/mkshparser_test.go
cvs rdiff -u -r1.8 -r1.9 pkgsrc/pkgtools/pkglint/files/mkshwalker.go
cvs rdiff -u -r1.14 -r1.15 pkgsrc/pkgtools/pkglint/files/mktypes.go
cvs rdiff -u -r1.10 -r1.11 pkgsrc/pkgtools/pkglint/files/mktypes_test.go
cvs rdiff -u -r1.12 -r1.13 pkgsrc/pkgtools/pkglint/files/options_test.go
cvs rdiff -u -r1.55 -r1.56 pkgsrc/pkgtools/pkglint/files/package.go \
    pkgsrc/pkgtools/pkglint/files/pkglint.go
cvs rdiff -u -r1.46 -r1.47 pkgsrc/pkgtools/pkglint/files/package_test.go
cvs rdiff -u -r0 -r1.1 pkgsrc/pkgtools/pkglint/files/paragraph.go \
    pkgsrc/pkgtools/pkglint/files/paragraph_test.go
cvs rdiff -u -r1.27 -r1.28 pkgsrc/pkgtools/pkglint/files/pkgsrc.go
cvs rdiff -u -r1.41 -r1.42 pkgsrc/pkgtools/pkglint/files/shell.go
cvs rdiff -u -r1.47 -r1.48 pkgsrc/pkgtools/pkglint/files/shell_test.go
cvs rdiff -u -r1.16 -r1.17 pkgsrc/pkgtools/pkglint/files/shtokenizer.go \
    pkgsrc/pkgtools/pkglint/files/tools_test.go \
    pkgsrc/pkgtools/pkglint/files/toplevel_test.go
cvs rdiff -u -r1.15 -r1.16 pkgsrc/pkgtools/pkglint/files/shtypes.go \
    pkgsrc/pkgtools/pkglint/files/tools.go \
    pkgsrc/pkgtools/pkglint/files/vardefs_test.go
cvs rdiff -u -r1.24 -r1.25 pkgsrc/pkgtools/pkglint/files/substcontext.go
cvs rdiff -u -r1.25 -r1.26 pkgsrc/pkgtools/pkglint/files/substcontext_test.go
cvs rdiff -u -r1.45 -r1.46 pkgsrc/pkgtools/pkglint/files/util.go
cvs rdiff -u -r1.29 -r1.30 pkgsrc/pkgtools/pkglint/files/util_test.go
cvs rdiff -u -r1.3 -r1.4 pkgsrc/pkgtools/pkglint/files/var.go \
    pkgsrc/pkgtools/pkglint/files/var_test.go
cvs rdiff -u -r1.66 -r1.67 pkgsrc/pkgtools/pkglint/files/vardefs.go
cvs rdiff -u -r1.32 -r1.33 pkgsrc/pkgtools/pkglint/files/vartype.go
cvs rdiff -u -r1.56 -r1.57 pkgsrc/pkgtools/pkglint/files/vartypecheck.go
cvs rdiff -u -r1.8 -r1.9 pkgsrc/pkgtools/pkglint/files/trace/tracing.go

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: pkgsrc/pkgtools/pkglint/Makefile
diff -u pkgsrc/pkgtools/pkglint/Makefile:1.584 pkgsrc/pkgtools/pkglint/Makefile:1.585
--- pkgsrc/pkgtools/pkglint/Makefile:1.584      Mon May 27 15:18:29 2019
+++ pkgsrc/pkgtools/pkglint/Makefile    Mon Jun 10 19:51:57 2019
@@ -1,7 +1,6 @@
-# $NetBSD: Makefile,v 1.584 2019/05/27 15:18:29 bsiegert Exp $
+# $NetBSD: Makefile,v 1.585 2019/06/10 19:51:57 rillig Exp $
 
-PKGNAME=       pkglint-5.7.12
-PKGREVISION=   1
+PKGNAME=       pkglint-5.7.13
 CATEGORIES=    pkgtools
 DISTNAME=      tools
 MASTER_SITES=  ${MASTER_SITE_GITHUB:=golang/}

Index: pkgsrc/pkgtools/pkglint/PLIST
diff -u pkgsrc/pkgtools/pkglint/PLIST:1.11 pkgsrc/pkgtools/pkglint/PLIST:1.12
--- pkgsrc/pkgtools/pkglint/PLIST:1.11  Sun Mar 10 19:01:50 2019
+++ pkgsrc/pkgtools/pkglint/PLIST       Mon Jun 10 19:51:57 2019
@@ -1,4 +1,4 @@
-@comment $NetBSD: PLIST,v 1.11 2019/03/10 19:01:50 rillig Exp $
+@comment $NetBSD: PLIST,v 1.12 2019/06/10 19:51:57 rillig Exp $
 bin/pkglint
 gopkg/pkg/${GO_PLATFORM}/netbsd.org/pkglint.a
 gopkg/pkg/${GO_PLATFORM}/netbsd.org/pkglint/getopt.a
@@ -71,6 +71,8 @@ gopkg/src/netbsd.org/pkglint/options.go
 gopkg/src/netbsd.org/pkglint/options_test.go
 gopkg/src/netbsd.org/pkglint/package.go
 gopkg/src/netbsd.org/pkglint/package_test.go
+gopkg/src/netbsd.org/pkglint/paragraph.go
+gopkg/src/netbsd.org/pkglint/paragraph_test.go
 gopkg/src/netbsd.org/pkglint/patches.go
 gopkg/src/netbsd.org/pkglint/patches_test.go
 gopkg/src/netbsd.org/pkglint/pkglint.0

Index: pkgsrc/pkgtools/pkglint/files/autofix.go
diff -u pkgsrc/pkgtools/pkglint/files/autofix.go:1.23 pkgsrc/pkgtools/pkglint/files/autofix.go:1.24
--- pkgsrc/pkgtools/pkglint/files/autofix.go:1.23       Sun May 26 14:05:57 2019
+++ pkgsrc/pkgtools/pkglint/files/autofix.go    Mon Jun 10 19:51:57 2019
@@ -75,7 +75,7 @@ func (fix *Autofix) Explain(explanation 
        // Since a silent fix doesn't have a diagnostic, its explanation would
        // not provide any clue as to what diagnostic it belongs. That would
        // be confusing, therefore this case is not allowed.
-       G.Assertf(
+       assertf(
                fix.diagFormat != SilentAutofixFormat,
                "Autofix: Silent fixes cannot have an explanation.")
 
@@ -276,7 +276,7 @@ func (fix *Autofix) Apply() {
 
        // To fix this assertion, call one of Autofix.Errorf, Autofix.Warnf
        // or Autofix.Notef before calling Apply.
-       G.Assertf(
+       assertf(
                fix.level != nil,
                "Each autofix must have a log level and a diagnostic.")
 
@@ -345,8 +345,8 @@ func (fix *Autofix) Realign(mkline MkLin
        // This complicated code should not be in the Autofix type.
 
        fix.assertRealLine()
-       G.Assertf(mkline.IsMultiline(), "Line must be a multiline.")
-       G.Assertf(mkline.IsVarassign() || mkline.IsCommentedVarassign(), "Line must be a variable assignment.")
+       assertf(mkline.IsMultiline(), "Line must be a multiline.")
+       assertf(mkline.IsVarassign() || mkline.IsCommentedVarassign(), "Line must be a variable assignment.")
 
        if fix.skip() {
                return
@@ -404,13 +404,13 @@ func (fix *Autofix) Realign(mkline MkLin
 
 func (fix *Autofix) setDiag(level *LogLevel, format string, args []interface{}) {
        if G.Testing && format != SilentAutofixFormat {
-               G.Assertf(
+               assertf(
                        hasSuffix(format, "."),
                        "Autofix: format %q must end with a period.",
                        format)
        }
-       G.Assertf(fix.level == nil, "Autofix can only have a single diagnostic.")
-       G.Assertf(fix.diagFormat == "", "Autofix can only have a single diagnostic.")
+       assertf(fix.level == nil, "Autofix can only have a single diagnostic.")
+       assertf(fix.diagFormat == "", "Autofix can only have a single diagnostic.")
 
        fix.level = level
        fix.diagFormat = format
@@ -418,7 +418,7 @@ func (fix *Autofix) setDiag(level *LogLe
 }
 
 func (fix *Autofix) skip() bool {
-       G.Assertf(
+       assertf(
                fix.diagFormat != "",
                "Autofix: The diagnostic must be given before the action.")
        // This check is necessary for the --only command line option.
@@ -426,7 +426,7 @@ func (fix *Autofix) skip() bool {
 }
 
 func (fix *Autofix) assertRealLine() {
-       G.Assertf(fix.line.firstLine >= 1, "Cannot autofix this line since it is not a real line.")
+       assertf(fix.line.firstLine >= 1, "Cannot autofix this line since it is not a real line.")
 }
 
 // SaveAutofixChanges writes the given lines back into their files,

Index: pkgsrc/pkgtools/pkglint/files/check_test.go
diff -u pkgsrc/pkgtools/pkglint/files/check_test.go:1.42 pkgsrc/pkgtools/pkglint/files/check_test.go:1.43
--- pkgsrc/pkgtools/pkglint/files/check_test.go:1.42    Tue May 21 17:59:48 2019
+++ pkgsrc/pkgtools/pkglint/files/check_test.go Mon Jun 10 19:51:57 2019
@@ -189,7 +189,7 @@ func (t *Tester) SetUpMasterSite(varname
        }
 }
 
-// SetUpOption pretends that the package option is defined in mk/defaults/options.description.
+// SetUpOption pretends that the given package option is defined in mk/defaults/options.description.
 func (t *Tester) SetUpOption(name, description string) {
        G.Pkgsrc.PkgOptions[name] = description
 }
@@ -321,7 +321,7 @@ func (t *Tester) SetUpPkgsrc() {
 // SetUpCategory makes the given category valid by creating a dummy Makefile.
 // After that, it can be mentioned in the CATEGORIES variable of a package.
 func (t *Tester) SetUpCategory(name string) {
-       G.Assertf(!contains(name, "/"), "Category must not contain a slash.")
+       assertf(!contains(name, "/"), "Category must not contain a slash.")
 
        if _, err := os.Stat(t.File(name + "/Makefile")); os.IsNotExist(err) {
                t.CreateFileLines(name+"/Makefile",
@@ -509,9 +509,9 @@ func (t *Tester) File(relativeFileName s
 // Copy copies a file inside the temporary directory.
 func (t *Tester) Copy(relativeSrc, relativeDst string) {
        data, err := ioutil.ReadFile(t.File(relativeSrc))
-       G.AssertNil(err, "Copy.Read")
+       assertNil(err, "Copy.Read")
        err = ioutil.WriteFile(t.File(relativeDst), data, 0777)
-       G.AssertNil(err, "Copy.Write")
+       assertNil(err, "Copy.Write")
 }
 
 // Chdir changes the current working directory to the given subdirectory
@@ -605,13 +605,13 @@ func (t *Tester) SetUpHierarchy() (
                }
 
                mklines := NewMkLines(NewLines(filename, lines))
-               G.Assertf(files[filename] == nil, "MkLines with name %q already exists.", filename)
+               assertf(files[filename] == nil, "MkLines with name %q already exists.", filename)
                files[filename] = mklines
                return mklines
        }
 
        get = func(filename string) MkLines {
-               G.Assertf(files[filename] != nil, "MkLines with name %q doesn't exist.", filename)
+               assertf(files[filename] != nil, "MkLines with name %q doesn't exist.", filename)
                return files[filename]
        }
 
@@ -797,7 +797,7 @@ func (t *Tester) NewLine(filename string
 // NewMkLine creates an in-memory line in the Makefile format with the given text.
 func (t *Tester) NewMkLine(filename string, lineno int, text string) MkLine {
        basename := path.Base(filename)
-       G.Assertf(
+       assertf(
                hasSuffix(basename, ".mk") ||
                        basename == "Makefile" ||
                        hasPrefix(basename, "Makefile.") ||
@@ -807,8 +807,9 @@ func (t *Tester) NewMkLine(filename stri
        return MkLineParser{}.Parse(t.NewLine(filename, lineno, text))
 }
 
-func (t *Tester) NewShellLineChecker(mklines MkLines, filename string, lineno int, text string) *ShellLineChecker {
-       return NewShellLineChecker(mklines, t.NewMkLine(filename, lineno, text))
+func (t *Tester) NewShellLineChecker(text string) *ShellLineChecker {
+       mklines := t.NewMkLines("filename.mk", text)
+       return NewShellLineChecker(mklines, mklines.mklines[0])
 }
 
 // NewLines returns a list of simple lines that belong together.
@@ -837,7 +838,7 @@ func (t *Tester) NewLinesAt(filename str
 // see SetUpFileMkLines for loading Makefile fragments with line continuations.
 func (t *Tester) NewMkLines(filename string, lines ...string) MkLines {
        basename := path.Base(filename)
-       G.Assertf(
+       assertf(
                hasSuffix(basename, ".mk") || basename == "Makefile" || hasPrefix(basename, "Makefile."),
                "filename %q must be realistic, otherwise the variable permissions are wrong", filename)
 
@@ -857,7 +858,7 @@ func (t *Tester) Output() string {
 
        t.stdout.Reset()
        t.stderr.Reset()
-       if G.Usable() {
+       if G.usable() {
                G.Logger.logged = Once{}
                if G.Logger.out != nil { // Necessary because Main resets the G variable.
                        G.Logger.out.state = 0 // Prevent an empty line at the beginning of the next output.
@@ -865,7 +866,7 @@ func (t *Tester) Output() string {
                }
        }
 
-       G.Assertf(t.tmpdir != "", "Tester must be initialized before checking the output.")
+       assertf(t.tmpdir != "", "Tester must be initialized before checking the output.")
        return strings.Replace(stdout+stderr, t.tmpdir, "~", -1)
 }
 
@@ -883,7 +884,7 @@ func (t *Tester) CheckOutputEmpty() {
 //
 // See CheckOutputEmpty, CheckOutputLinesIgnoreSpace.
 func (t *Tester) CheckOutputLines(expectedLines ...string) {
-       G.Assertf(len(expectedLines) > 0, "To check empty lines, use CheckLinesEmpty instead.")
+       assertf(len(expectedLines) > 0, "To check empty lines, use CheckOutputEmpty instead.")
        t.CheckOutput(expectedLines)
 }
 
@@ -896,8 +897,8 @@ func (t *Tester) CheckOutputLines(expect
 //
 // See CheckOutputEmpty, CheckOutputLines.
 func (t *Tester) CheckOutputLinesIgnoreSpace(expectedLines ...string) {
-       G.Assertf(len(expectedLines) > 0, "To check empty lines, use CheckLinesEmpty instead.")
-       G.Assertf(t.tmpdir != "", "Tester must be initialized before checking the output.")
+       assertf(len(expectedLines) > 0, "To check empty lines, use CheckOutputEmpty instead.")
+       assertf(t.tmpdir != "", "Tester must be initialized before checking the output.")
 
        rawOutput := t.stdout.String() + t.stderr.String()
        _ = t.Output() // Just to consume the output
@@ -1055,7 +1056,7 @@ func (t *Tester) EnableSilentTracing() {
 // The diagnostics go to the in-memory buffer again,
 // ready to be checked with CheckOutputLines.
 func (t *Tester) DisableTracing() {
-       if G.Usable() {
+       if G.usable() {
                G.Logger.out = NewSeparatorWriter(&t.stdout)
        }
        trace.Tracing = false
Index: pkgsrc/pkgtools/pkglint/files/pkglint_test.go
diff -u pkgsrc/pkgtools/pkglint/files/pkglint_test.go:1.42 pkgsrc/pkgtools/pkglint/files/pkglint_test.go:1.43
--- pkgsrc/pkgtools/pkglint/files/pkglint_test.go:1.42  Tue May 21 17:59:48 2019
+++ pkgsrc/pkgtools/pkglint/files/pkglint_test.go       Mon Jun 10 19:51:57 2019
@@ -1,7 +1,6 @@
 package pkglint
 
 import (
-       "errors"
        "gopkg.in/check.v1"
        "io/ioutil"
        "os"
@@ -271,8 +270,6 @@ func (s *Suite) Test_Pkglint_Main__compl
 //
 // See https://github.com/rillig/gobco for the tool to measure the branch coverage.
 func (s *Suite) Test_Pkglint__realistic(c *check.C) {
-       t := s.Init(c)
-
        if cwd := os.Getenv("PKGLINT_TESTDIR"); cwd != "" {
                err := os.Chdir(cwd)
                c.Assert(err, check.IsNil)
@@ -283,7 +280,7 @@ func (s *Suite) Test_Pkglint__realistic(
                G.Logger.out = NewSeparatorWriter(os.Stdout)
                G.Logger.err = NewSeparatorWriter(os.Stderr)
                trace.Out = os.Stdout
-               t.Main(strings.Fields(cmdline)...)
+               G.Main(append([]string{"pkglint"}, strings.Fields(cmdline)...)...)
        }
 }
 
@@ -595,6 +592,40 @@ func (s *Suite) Test_Pkglint_checkReg__a
                "(Run \"pkglint -e\" to show explanations.)")
 }
 
+// Just for branch coverage.
+func (s *Suite) Test_Pkglint_checkReg__file_not_found(c *check.C) {
+       t := s.Init(c)
+
+       t.Chdir(".")
+
+       G.checkReg("buildlink3.mk", "buildlink3.mk", 2)
+       G.checkReg("DESCR", "DESCR", 2)
+       G.checkReg("distinfo", "distinfo", 2)
+       G.checkReg("MESSAGE", "MESSAGE", 2)
+       G.checkReg("patches/patch-aa", "patch-aa", 2)
+       G.checkReg("PLIST", "PLIST", 2)
+
+       t.CheckOutputLines(
+               "ERROR: buildlink3.mk: Cannot be read.",
+               "ERROR: DESCR: Cannot be read.",
+               "ERROR: distinfo: Cannot be read.",
+               "ERROR: MESSAGE: Cannot be read.",
+               "ERROR: patches/patch-aa: Cannot be read.",
+               "ERROR: PLIST: Cannot be read.")
+}
+
+// Just for branch coverage.
+func (s *Suite) Test_Pkglint_checkReg__no_tracing(c *check.C) {
+       t := s.Init(c)
+
+       t.Chdir(".")
+       t.DisableTracing()
+
+       G.checkReg("patches/manual-aa", "manual-aa", 2)
+
+       t.CheckOutputEmpty()
+}
+
 func (s *Suite) Test_Pkglint__profiling(c *check.C) {
        t := s.Init(c)
 
@@ -1062,6 +1093,11 @@ func (s *Suite) Test_Pkglint_checkdirPac
        G.Check(pkg)
 
        t.CheckOutputEmpty()
+
+       // Just for code coverage.
+       t.DisableTracing()
+       G.Check(pkg)
+       t.CheckOutputEmpty()
 }
 
 func (s *Suite) Test_Pkglint_checkdirPackage__ALTERNATIVES(c *check.C) {
@@ -1105,6 +1141,18 @@ func (s *Suite) Test_CheckFileMk__enoent
                "ERROR: ~/filename.mk: Cannot be read.")
 }
 
+// Just for code coverage.
+func (s *Suite) Test_CheckFileOther__no_tracing(c *check.C) {
+       t := s.Init(c)
+
+       t.DisableTracing()
+
+       CheckFileOther(t.File("filename.mk"))
+
+       t.CheckOutputLines(
+               "ERROR: ~/filename.mk: Cannot be read.")
+}
+
 func (s *Suite) Test_Pkglint_checkExecutable(c *check.C) {
        t := s.Init(c)
 
@@ -1137,16 +1185,6 @@ func (s *Suite) Test_Pkglint_checkExecut
        t.CheckOutputEmpty()
 }
 
-func (s *Suite) Test_Pkglint_AssertNil(c *check.C) {
-       t := s.Init(c)
-
-       G.AssertNil(nil, "this is not an error")
-
-       t.ExpectPanic(
-               func() { G.AssertNil(errors.New("unexpected error"), "Oops") },
-               "Pkglint internal error: Oops: unexpected error")
-}
-
 func (s *Suite) Test_Main(c *check.C) {
        t := s.Init(c)
 

Index: pkgsrc/pkgtools/pkglint/files/distinfo.go
diff -u pkgsrc/pkgtools/pkglint/files/distinfo.go:1.33 pkgsrc/pkgtools/pkglint/files/distinfo.go:1.34
--- pkgsrc/pkgtools/pkglint/files/distinfo.go:1.33      Mon May  6 20:27:17 2019
+++ pkgsrc/pkgtools/pkglint/files/distinfo.go   Mon Jun 10 19:51:57 2019
@@ -226,17 +226,17 @@ func (ck *distinfoLinesChecker) checkAlg
 
        computeHash := func(hasher hash.Hash) string {
                f, err := os.Open(distfile)
-               G.AssertNil(err, "Opening distfile")
+               assertNil(err, "Opening distfile")
 
                // Don't load the distfile into memory since some of them
                // are hundreds of MB in size.
                _, err = io.Copy(hasher, f)
-               G.AssertNil(err, "Computing hash of distfile")
+               assertNil(err, "Computing hash of distfile")
 
                hexHash := hex.EncodeToString(hasher.Sum(nil))
 
                err = f.Close()
-               G.AssertNil(err, "Closing distfile")
+               assertNil(err, "Closing distfile")
 
                return hexHash
        }
@@ -251,7 +251,7 @@ func (ck *distinfoLinesChecker) checkAlg
                        return computeHash(sha512.New())
                default:
                        fileInfo, err := os.Lstat(distfile)
-                       G.AssertNil(err, "Inaccessible distfile info")
+                       assertNil(err, "Inaccessible distfile info")
                        return sprintf("%d bytes", fileInfo.Size())
                }
        }
@@ -436,7 +436,7 @@ func computePatchSha1Hex(patchFilename s
        skipText := []byte("$" + "NetBSD")
        for _, patchLine := range bytes.SplitAfter(patchBytes, []byte("\n")) {
                if !bytes.Contains(patchLine, skipText) {
-                       hasher.Write(patchLine)
+                       _, _ = hasher.Write(patchLine)
                }
        }
        return sprintf("%x", hasher.Sum(nil)), nil

Index: pkgsrc/pkgtools/pkglint/files/line.go
diff -u pkgsrc/pkgtools/pkglint/files/line.go:1.35 pkgsrc/pkgtools/pkglint/files/line.go:1.36
--- pkgsrc/pkgtools/pkglint/files/line.go:1.35  Tue May 21 17:59:48 2019
+++ pkgsrc/pkgtools/pkglint/files/line.go       Mon Jun 10 19:51:57 2019
@@ -83,7 +83,7 @@ type LineImpl struct {
 }
 
 func NewLine(filename string, lineno int, text string, rawLine *RawLine) Line {
-       G.Assertf(rawLine != nil, "use NewLineMulti for creating a Line with no RawLine attached to it")
+       assertf(rawLine != nil, "use NewLineMulti for creating a Line with no RawLine attached to it")
        return NewLineMulti(filename, lineno, lineno, text, []*RawLine{rawLine})
 }
 

Index: pkgsrc/pkgtools/pkglint/files/logging.go
diff -u pkgsrc/pkgtools/pkglint/files/logging.go:1.22 pkgsrc/pkgtools/pkglint/files/logging.go:1.23
--- pkgsrc/pkgtools/pkglint/files/logging.go:1.22       Sun Apr 28 18:13:53 2019
+++ pkgsrc/pkgtools/pkglint/files/logging.go    Mon Jun 10 19:51:57 2019
@@ -190,14 +190,14 @@ func (l *Logger) Logf(level *LogLevel, f
 
        if G.Testing && format != AutofixFormat {
                if textproc.Alpha.Contains(format[0]) {
-                       G.Assertf(
+                       assertf(
                                textproc.Upper.Contains(format[0]),
                                "Diagnostic %q must start with an uppercase letter.",
                                format)
                }
 
                if !hasSuffix(format, ": %s") && !hasSuffix(format, ". %s") {
-                       G.Assertf(hasSuffix(format, "."), "Diagnostic format %q must end in a period.", format)
+                       assertf(hasSuffix(format, "."), "Diagnostic format %q must end in a period.", format)
                }
        }
 

Index: pkgsrc/pkgtools/pkglint/files/mkline.go
diff -u pkgsrc/pkgtools/pkglint/files/mkline.go:1.52 pkgsrc/pkgtools/pkglint/files/mkline.go:1.53
--- pkgsrc/pkgtools/pkglint/files/mkline.go:1.52        Sat Apr 27 19:33:57 2019
+++ pkgsrc/pkgtools/pkglint/files/mkline.go     Mon Jun 10 19:51:57 2019
@@ -320,6 +320,24 @@ func (mkline *MkLineImpl) Value() string
 // The leading "#" is included so that pkglint can distinguish between no comment at all and an empty comment.
 func (mkline *MkLineImpl) VarassignComment() string { return mkline.data.(mkLineAssign).comment }
 
+// FirstLineContainsValue returns whether the variable assignment of a
+// multiline contains a textual value in the first line.
+//
+//  VALUE_IN_FIRST_LINE= value \
+//          starts in first line
+//  NO_VALUE_IN_FIRST_LINE= \
+//          value starts in second line
+func (mkline *MkLineImpl) FirstLineContainsValue() bool {
+       assertf(mkline.IsVarassign() || mkline.IsCommentedVarassign(), "Line must be a variable assignment.")
+       assertf(mkline.IsMultiline(), "Line must be multiline.")
+
+       // Parsing the continuation marker as variable value is cheating but works well.
+       text := strings.TrimSuffix(mkline.raw[0].orignl, "\n")
+       data := MkLineParser{}.split(nil, text)
+       _, a := MkLineParser{}.MatchVarassign(mkline.Line, text, data)
+       return a.value != "\\"
+}
+
 func (mkline *MkLineImpl) ShellCommand() string { return mkline.data.(mkLineShell).command }
 
 func (mkline *MkLineImpl) Indent() string {
@@ -432,7 +450,7 @@ func (mkline *MkLineImpl) Tokenize(text 
 //
 // When several separators are adjacent, this results in empty words in the output.
 func (mkline *MkLineImpl) ValueSplit(value string, separator string) []string {
-       G.Assertf(separator != "", "Separator must not be empty; use ValueFields to split on whitespace")
+       assertf(separator != "", "Separator must not be empty; use ValueFields to split on whitespace")
 
        tokens := mkline.Tokenize(value, false)
        var split []string
@@ -579,7 +597,7 @@ func (mkline *MkLineImpl) Fields() []str
 
 }
 
-func (mkline *MkLineImpl) WithoutMakeVariables(value string) string {
+func (*MkLineImpl) WithoutMakeVariables(value string) string {
        var valueNovar strings.Builder
        for _, token := range NewMkParser(nil, value, false).MkTokens() {
                if token.Varuse == nil {
@@ -609,7 +627,7 @@ func (mkline *MkLineImpl) ResolveVarsInR
                        // Relative pkgsrc paths usually only contain two or three levels.
                        // A possible reason for reaching this assertion is a pkglint unit test
                        // that uses t.NewMkLines instead of the correct t.SetUpFileMkLines.
-                       G.Assertf(!contains(pkgsrcdir, "../../../../.."),
+                       assertf(!contains(pkgsrcdir, "../../../../.."),
                                "Relative path %q for %q is too deep below the pkgsrc root %q.",
                                pkgsrcdir, basedir, G.Pkgsrc.File("."))
                }
@@ -735,7 +753,7 @@ again:
                        return main, lexer.Rest()
                }
 
-               G.Assertf(lexer.EOF(), "unescapeComment(%q): sb = %q, rest = %q", text, main, lexer.Rest())
+               assertf(lexer.EOF(), "unescapeComment(%q): sb = %q, rest = %q", text, main, lexer.Rest())
                return main, ""
        }
 
@@ -759,19 +777,11 @@ type mkLineSplitResult struct {
 // have comments.
 func (p MkLineParser) split(line Line, text string) mkLineSplitResult {
 
-       main, comment := p.unescapeComment(text)
+       mainWithSpaces, comment := p.unescapeComment(text)
 
-       parser := NewMkParser(line, main, line != nil)
+       parser := NewMkParser(line, mainWithSpaces, line != nil)
        lexer := parser.lexer
 
-       rtrimHspace := func(s string) string {
-               end := len(s)
-               for end > 0 && isHspace(s[end-1]) {
-                       end--
-               }
-               return s[:end]
-       }
-
        parseOther := func() string {
                var sb strings.Builder
 
@@ -803,7 +813,7 @@ func (p MkLineParser) split(line Line, t
                        tokens = append(tokens, &MkToken{other, nil})
 
                } else {
-                       G.Assertf(lexer.SkipByte('$'), "Parse error for %q.", text)
+                       assertf(lexer.SkipByte('$'), "Parse error for %q.", text)
                        tokens = append(tokens, &MkToken{"$", nil})
                }
        }
@@ -813,20 +823,21 @@ func (p MkLineParser) split(line Line, t
                comment = comment[1:]
        }
 
-       G.Assertf(lexer.Rest() == "", "Parse error for %q.", text)
-
-       mainWithSpaces := main
-       main = rtrimHspace(main)
-       spaceBeforeComment := ifelseStr(true, mainWithSpaces[len(main):], "")
-       if spaceBeforeComment != "" && len(tokens) > 0 {
+       mainTrimmed := rtrimHspace(mainWithSpaces)
+       spaceBeforeComment := mainWithSpaces[len(mainTrimmed):]
+       if spaceBeforeComment != "" {
                tokenText := &tokens[len(tokens)-1].Text
                *tokenText = rtrimHspace(*tokenText)
                if *tokenText == "" {
-                       tokens = tokens[:len(tokens)-1]
+                       if len(tokens) == 1 {
+                               tokens = nil
+                       } else {
+                               tokens = tokens[:len(tokens)-1]
+                       }
                }
        }
 
-       return mkLineSplitResult{main, tokens, spaceBeforeComment, hasComment, comment}
+       return mkLineSplitResult{mainTrimmed, tokens, spaceBeforeComment, hasComment, comment}
 }
 
 func (p MkLineParser) parseDirective(line Line, data mkLineSplitResult) MkLine {
@@ -940,7 +951,7 @@ func (mkline *MkLineImpl) VariableNeedsQ
        }
 
        // SUBST_MESSAGE.perl= Replacing in ${REPLACE_PERL}
-       if vucVartype.IsPlainString() {
+       if vucVartype.basicType == BtMessage {
                return no
        }
 
@@ -1005,31 +1016,31 @@ func (mkline *MkLineImpl) ForEachUsed(ac
        switch {
 
        case mkline.IsVarassign():
-               searchIn(mkline.Varname(), vucTimeParse)
+               searchIn(mkline.Varname(), vucTimeLoad)
                searchIn(mkline.Value(), mkline.Op().Time())
 
        case mkline.IsDirective() && mkline.Directive() == "for":
-               searchIn(mkline.Args(), vucTimeParse)
+               searchIn(mkline.Args(), vucTimeLoad)
 
        case mkline.IsDirective() && mkline.Cond() != nil:
                mkline.Cond().Walk(&MkCondCallback{
                        VarUse: func(varuse *MkVarUse) {
-                               searchInVarUse(varuse, vucTimeParse)
+                               searchInVarUse(varuse, vucTimeLoad)
                        }})
 
        case mkline.IsShellCommand():
                searchIn(mkline.ShellCommand(), vucTimeRun)
 
        case mkline.IsDependency():
-               searchIn(mkline.Targets(), vucTimeParse)
-               searchIn(mkline.Sources(), vucTimeParse)
+               searchIn(mkline.Targets(), vucTimeLoad)
+               searchIn(mkline.Sources(), vucTimeLoad)
 
        case mkline.IsInclude():
-               searchIn(mkline.IncludedFile(), vucTimeParse)
+               searchIn(mkline.IncludedFile(), vucTimeLoad)
        }
 }
 
-func (mkline *MkLineImpl) UnquoteShell(str string) string {
+func (*MkLineImpl) UnquoteShell(str string) string {
        var sb strings.Builder
        n := len(str)
 
@@ -1106,7 +1117,7 @@ func (op MkOperator) String() string {
 // evaluated.
 func (op MkOperator) Time() vucTime {
        if op == opAssignShell || op == opAssignEval {
-               return vucTimeParse
+               return vucTimeLoad
        }
        return vucTimeRun
 }
@@ -1144,7 +1155,7 @@ const (
        // right-hand side, as well as the directives .if, .elif and .for.
        // During loading, not all variables are available yet.
        // Variable values are still subject to change, especially lists.
-       vucTimeParse
+       vucTimeLoad
 
        // All files have been read, all variables can be referenced.
        // Variable values don't change anymore.
@@ -1307,7 +1318,7 @@ func (ind *Indentation) Varnames() []str
        var varnames []string
        for _, level := range ind.levels {
                for _, levelVarname := range level.conditionalVars {
-                       G.Assertf(
+                       assertf(
                                !hasSuffix(levelVarname, "_MK"),
                                "multiple-inclusion guard must be filtered out earlier.")
                        varnames = append(varnames, levelVarname)

Index: pkgsrc/pkgtools/pkglint/files/mkline_test.go
diff -u pkgsrc/pkgtools/pkglint/files/mkline_test.go:1.58 pkgsrc/pkgtools/pkglint/files/mkline_test.go:1.59
--- pkgsrc/pkgtools/pkglint/files/mkline_test.go:1.58   Sun May 26 14:05:57 2019
+++ pkgsrc/pkgtools/pkglint/files/mkline_test.go        Mon Jun 10 19:51:57 2019
@@ -260,6 +260,35 @@ func (s *Suite) Test_MkLine_ValueAlign__
        c.Check(valueAlign, equals, "#SUBST_SED.${param}=\t")
 }
 
+func (s *Suite) Test_MkLine_FirstLineContainsValue(c *check.C) {
+       t := s.Init(c)
+
+       mklines := t.NewMkLines("filename.mk",
+               MkRcsID,
+               "VAR=\tvalue",
+               "VAR= value \\",
+               "\tstarts in first line",
+               "VAR= \\",
+               "\tvalue starts in second line",
+               "#VAR= value \\",
+               "\tstarts in first line",
+               "#VAR= \\",
+               "\tvalue starts in second line")
+
+       t.ExpectPanic(
+               func() { mklines.mklines[0].FirstLineContainsValue() },
+               "Pkglint internal error: Line must be a variable assignment.")
+
+       t.ExpectPanic(
+               func() { mklines.mklines[1].FirstLineContainsValue() },
+               "Pkglint internal error: Line must be multiline.")
+
+       t.Check(mklines.mklines[2].FirstLineContainsValue(), equals, true)
+       t.Check(mklines.mklines[3].FirstLineContainsValue(), equals, false)
+       t.Check(mklines.mklines[4].FirstLineContainsValue(), equals, true)
+       t.Check(mklines.mklines[5].FirstLineContainsValue(), equals, false)
+}
+
 // Demonstrates how a simple condition is structured internally.
 // For most of the checks, using cond.Walk is the simplest way to go.
 func (s *Suite) Test_MkLine_Cond(c *check.C) {
@@ -370,7 +399,7 @@ func (s *Suite) Test_MkLine_VariableNeed
        mkline := t.NewMkLine("filename.mk", 1, "PKGNAME:= ${UNKNOWN}")
        t.SetUpVartypes()
 
-       vuc := VarUseContext{G.Pkgsrc.VariableType(nil, "PKGNAME"), vucTimeParse, VucQuotUnknown, false}
+       vuc := VarUseContext{G.Pkgsrc.VariableType(nil, "PKGNAME"), vucTimeLoad, VucQuotUnknown, false}
        nq := mkline.VariableNeedsQuoting(nil, &MkVarUse{"UNKNOWN", nil}, nil, &vuc)
 
        c.Check(nq, equals, unknown)
@@ -381,14 +410,17 @@ func (s *Suite) Test_MkLine_VariableNeed
 
        t.SetUpVartypes()
        t.SetUpMasterSite("MASTER_SITE_SOURCEFORGE", "http://downloads.sourceforge.net/sourceforge/";)
-       mkline := t.NewMkLine("Makefile", 95, "MASTER_SITES=\t${HOMEPAGE}")
+       mklines := t.NewMkLines("Makefile",
+               MkRcsID,
+               "MASTER_SITES=\t${HOMEPAGE}")
+       mkline := mklines.mklines[1]
 
        vuc := VarUseContext{G.Pkgsrc.vartypes.Canon("MASTER_SITES"), vucTimeRun, VucQuotPlain, false}
        nq := mkline.VariableNeedsQuoting(nil, &MkVarUse{"HOMEPAGE", nil}, G.Pkgsrc.vartypes.Canon("HOMEPAGE"), &vuc)
 
        c.Check(nq, equals, no)
 
-       MkLineChecker{nil, mkline}.checkVarassign()
+       MkLineChecker{mklines, mkline}.checkVarassign()
 
        t.CheckOutputEmpty() // Up to version 5.3.6, pkglint warned about a missing :Q here, which was wrong.
 }
@@ -398,9 +430,11 @@ func (s *Suite) Test_MkLine_VariableNeed
 
        t.SetUpVartypes()
        t.SetUpMasterSite("MASTER_SITE_SOURCEFORGE", "http://downloads.sourceforge.net/sourceforge/";)
-       mkline := t.NewMkLine("Makefile", 96, "MASTER_SITES=\t${MASTER_SITE_SOURCEFORGE:=squirrel-sql/}")
+       mklines := t.NewMkLines("Makefile",
+               MkRcsID,
+               "MASTER_SITES=\t${MASTER_SITE_SOURCEFORGE:=squirrel-sql/}")
 
-       MkLineChecker{nil, mkline}.checkVarassign()
+       MkLineChecker{mklines, mklines.mklines[1]}.checkVarassign()
 
        // Assigning lists to lists is ok.
        t.CheckOutputEmpty()
@@ -410,26 +444,28 @@ func (s *Suite) Test_MkLine_VariableNeed
        t := s.Init(c)
 
        t.SetUpVartypes()
-       mkline := t.NewMkLine("builtin.mk", 3,
+       mklines := t.NewMkLines("builtin.mk",
+               MkRcsID,
                "USE_BUILTIN.Xfixes!=\t${PKG_ADMIN} pmatch 'pkg-[0-9]*' ${BUILTIN_PKG.Xfixes:Q}")
 
-       MkLineChecker{nil, mkline}.checkVarassign()
+       MkLineChecker{mklines, mklines.mklines[1]}.checkVarassign()
 
        t.CheckOutputLines(
-               "NOTE: builtin.mk:3: The :Q operator isn't necessary for ${BUILTIN_PKG.Xfixes} here.")
+               "NOTE: builtin.mk:2: The :Q operator isn't necessary for ${BUILTIN_PKG.Xfixes} here.")
 }
 
 func (s *Suite) Test_MkLine_VariableNeedsQuoting__command_in_single_quotes(c *check.C) {
        t := s.Init(c)
 
        t.SetUpVartypes()
-       mkline := t.NewMkLine("Makefile", 3,
+       mklines := t.NewMkLines("Makefile",
+               MkRcsID,
                "SUBST_SED.hpath=\t-e 's|^\\(INSTALL[\t:]*=\\).*|\\1${INSTALL}|'")
 
-       MkLineChecker{nil, mkline}.checkVarassign()
+       MkLineChecker{mklines, mklines.mklines[1]}.checkVarassign()
 
        t.CheckOutputLines(
-               "WARN: Makefile:3: Please use ${INSTALL:Q} instead of ${INSTALL} " +
+               "WARN: Makefile:2: Please use ${INSTALL:Q} instead of ${INSTALL} " +
                        "and make sure the variable appears outside of any quoting characters.")
 }
 
@@ -621,6 +657,21 @@ func (s *Suite) Test_MkLine_VariableNeed
        t.CheckOutputEmpty()
 }
 
+// Since a comment may be appended to, it is not necessary to mention
+// BtComment in the SUBST_MESSAGE case in VariableNeedsQuoting.
+func (s *Suite) Test_MkLine_VariableNeedsQuoting__command_in_package_comment(c *check.C) {
+       t := s.Init(c)
+
+       t.SetUpVartypes()
+       mklines := t.NewMkLines("benchmarks/iozone/Makefile",
+               "COMMENT=\tUtility for replacing ${REPLACE_PERL}")
+
+       MkLineChecker{mklines, mklines.mklines[0]}.Check()
+
+       // Don't suggest ${REPLACE_PERL:Q}.
+       t.CheckOutputEmpty()
+}
+
 func (s *Suite) Test_MkLine_VariableNeedsQuoting__guessed_list_variable_in_quotes(c *check.C) {
        t := s.Init(c)
 
@@ -1291,6 +1342,24 @@ func (s *Suite) Test_MkLine_ResolveVarsI
                "ERROR: ~/multimedia/totem/bla.mk:2: Relative path \"../../multimedia/totem/Makefile\" does not exist.")
 }
 
+// Just for code coverage
+func (s *Suite) Test_MkLine_ResolveVarsInRelativePath__without_tracing(c *check.C) {
+       t := s.Init(c)
+
+       t.DisableTracing()
+       t.SetUpVartypes()
+       mklines := t.SetUpFileMkLines("buildlink3.mk",
+               MkRcsID,
+               "BUILDLINK_PKGSRCDIR.totem?=\t../../${PKGPATH.multimedia/totem}")
+
+       mklines.Check()
+
+       t.CheckOutputLines(
+               // FIXME: It's ok to have variable parameters including a slash.
+               "WARN: ~/buildlink3.mk:2: Invalid part \"/totem\" after variable name \"PKGPATH.multimedia\".",
+               "WARN: ~/buildlink3.mk:2: PKGPATH.multimedia/totem is used but not defined.")
+}
+
 func (s *Suite) Test_MkLineParser_MatchVarassign(c *check.C) {
        t := s.Init(c)
 
@@ -2089,6 +2158,13 @@ func (s *Suite) Test_MkLineParser_split(
                                varuse("pattern"),
                                text(")")),
                })
+
+       test("   # comment after spaces",
+               mkLineSplitResult{
+                       spaceBeforeComment: "   ",
+                       hasComment:         true,
+                       comment:            " comment after spaces",
+               })
 }
 
 func (s *Suite) Test_MkLineParser_parseDirective(c *check.C) {

Index: pkgsrc/pkgtools/pkglint/files/mklinechecker.go
diff -u pkgsrc/pkgtools/pkglint/files/mklinechecker.go:1.40 pkgsrc/pkgtools/pkglint/files/mklinechecker.go:1.41
--- pkgsrc/pkgtools/pkglint/files/mklinechecker.go:1.40 Sun May 26 14:05:57 2019
+++ pkgsrc/pkgtools/pkglint/files/mklinechecker.go      Mon Jun 10 19:51:57 2019
@@ -225,7 +225,7 @@ func (ck MkLineChecker) checkDirectiveFo
                // running pkglint over the whole pkgsrc tree did not produce any different result
                // whether guessed was true or false.
                forLoopType := NewVartype(btForLoop, List, NewACLEntry("*", aclpAllRead))
-               forLoopContext := VarUseContext{forLoopType, vucTimeParse, VucQuotPlain, false}
+               forLoopContext := VarUseContext{forLoopType, vucTimeLoad, VucQuotPlain, false}
                mkline.ForEachUsed(func(varUse *MkVarUse, time vucTime) {
                        ck.CheckVaruse(varUse, &forLoopContext)
                })
@@ -233,7 +233,7 @@ func (ck MkLineChecker) checkDirectiveFo
 }
 
 func (ck MkLineChecker) checkDirectiveIndentation(expectedDepth int) {
-       if ck.MkLines == nil || !G.Opts.WarnSpace {
+       if !G.Opts.WarnSpace {
                return
        }
        mkline := ck.MkLine
@@ -408,10 +408,9 @@ func (ck MkLineChecker) checkVarassignLe
        }
 
        isRationale := func(mkline MkLine) bool {
-               if mkline.IsVarassign() || mkline.IsCommentedVarassign() {
-                       return mkline.VarassignComment() != ""
-               }
-               return mkline.IsComment() && !hasPrefix(mkline.Text, "# $")
+               return mkline.IsComment() &&
+                       !hasPrefix(mkline.Text, "# $") &&
+                       !mkline.IsCommentedVarassign()
        }
 
        needsRationale := func(mkline MkLine) bool {
@@ -531,13 +530,13 @@ func (ck MkLineChecker) checkVaruseUndef
                // Well-known variables are probably defined by the infrastructure.
                return
 
-       case ck.MkLines != nil && ck.MkLines.vars.DefinedSimilar(varname):
+       case ck.MkLines.vars.DefinedSimilar(varname):
                return
 
-       case ck.MkLines != nil && ck.MkLines.forVars[varname]:
+       case ck.MkLines.forVars[varname]:
                return
 
-       case ck.MkLines != nil && ck.MkLines.vars.Mentioned(varname) != nil:
+       case ck.MkLines.vars.Mentioned(varname) != nil:
                return
 
        case G.Pkg != nil && G.Pkg.vars.DefinedSimilar(varname):
@@ -549,7 +548,7 @@ func (ck MkLineChecker) checkVaruseUndef
        case G.Pkgsrc.vartypes.DefinedCanon(varname):
                return
 
-       case ck.MkLines == nil || !ck.MkLines.FirstTimeSlice("used but not defined: ", varname):
+       case !ck.MkLines.FirstTimeSlice("used but not defined: ", varname):
                return
        }
 
@@ -624,6 +623,7 @@ func (ck MkLineChecker) checkVarusePermi
                // many wrong warnings.
                return
        }
+
        if trace.Tracing {
                defer trace.Call(varname, vuc)()
        }
@@ -639,15 +639,29 @@ func (ck MkLineChecker) checkVarusePermi
                return
        }
 
-       mkline := ck.MkLine
-       if mkline.Basename == "hacks.mk" {
+       if vartype.Guessed() {
+               return
+       }
+
+       // Do not warn about unknown infrastructure variables.
+       // These have all permissions to prevent warnings when they are used.
+       // But when other variables are assigned to them it would seem as if
+       // these other variables could become evaluated at load time.
+       // And this is something that most variables do not allow.
+       if vuc.vartype != nil && vuc.vartype.basicType == BtUnknown {
+               return
+       }
+
+       basename := ck.MkLine.Basename
+       if basename == "hacks.mk" {
                return
        }
 
-       effPerms := vartype.EffectivePermissions(mkline.Basename)
+       effPerms := vartype.EffectivePermissions(basename)
        if effPerms.Contains(aclpUseLoadtime) {
-               // Skip any checks, assuming that if a variable may be used at
-               // load time, it may also be used at run time.
+               // Since the variable may be used at load time, it probably
+               // may be used at run time as well. If it weren't, that would
+               // be a rather strange permissions set.
                return
        }
 
@@ -657,66 +671,43 @@ func (ck MkLineChecker) checkVarusePermi
        // be used at load time somewhere in the future because it is
        // assigned to another variable, and that variable is allowed
        // to be used at load time.
-       directly := vuc.time == vucTimeParse
+       directly := vuc.time == vucTimeLoad
        indirectly := !directly && vuc.vartype != nil &&
                vuc.vartype.Union().Contains(aclpUseLoadtime)
 
-       if vartype.Guessed() {
+       if !directly && !indirectly && effPerms.Contains(aclpUse) {
+               // At this point the variable is either used at run time, or the
+               // time is not known.
                return
        }
 
        if directly || indirectly {
                // At this point the variable is used at load time although that
-               // is not allowed.
-
-               // Whether a tool variable may be used at load time depends on
-               // whether bsd.prefs.mk has been included. That file examines the
-               // tools that have been added to USE_TOOLS up to this point and
-               // makes their variables available for use at load time.
-               if tool := G.ToolByVarname(ck.MkLines, varname); tool != nil {
+               // is not allowed by the permissions. The variable could be a tool
+               // variable, and these tool variables have special rules.
+               tool := G.ToolByVarname(ck.MkLines, varname)
+               if tool != nil {
+
+                       // Whether a tool variable may be used at load time depends on
+                       // whether bsd.prefs.mk has been included before. That file
+                       // examines the tools that have been added to USE_TOOLS up to
+                       // this point and makes their variables available for use at
+                       // load time.
                        if !tool.UsableAtLoadTime(ck.MkLines.Tools.SeenPrefs) {
                                ck.warnVaruseToolLoadTime(varname, tool)
                        }
                        return
                }
-
-               // Continue to get a detailed warning showing alternative
-               // permissions and/or alternative files.
-
-       } else if effPerms.Contains(aclpUse) {
-               // At this point the variable is used at run time. Since that is
-               // allowed by the permissions, there is nothing more to check for.
-               return
        }
 
-       if ck.MkLines != nil && !ck.MkLines.FirstTimeSlice("checkVarusePermissions", varname) {
-               return
-       }
-
-       // Do not warn about unknown infrastructure variables.
-       // These have all permissions to prevent warnings when they are used.
-       // But when other variables are assigned to them it would seem as if
-       // these other variables could become evaluated at load time.
-       // And this is something that most variables do not allow.
-       if vuc.vartype != nil && vuc.vartype.basicType == BtUnknown {
-               return
+       if ck.MkLines.FirstTimeSlice("checkVarusePermissions", varname) {
+               ck.warnVarusePermissions(varname, vartype, directly, indirectly)
        }
-
-       // At this point the variable is used either at load time or at run
-       // time, and that particular use is not allowed in this file.
-       //
-       // If the variable is used at run time, it may or may not be used at
-       // load time in this file. Having a variable that may be used at load
-       // time but not at run time is not a practically important case.
-       // Therefore it is not handled specially here.
-       //
-       // Anyway, there must be a warning now since the requested use is not
-       // allowed. The only remaining question is about how detailed the
-       // warning will be.
-       ck.warnVarusePermissions(varname, vartype, directly, indirectly)
 }
 
-func (ck MkLineChecker) warnVarusePermissions(varname string, vartype *Vartype, directly, indirectly bool) {
+func (ck MkLineChecker) warnVarusePermissions(
+       varname string, vartype *Vartype, directly, indirectly bool) {
+
        mkline := ck.MkLine
 
        anyPerms := vartype.Union()
@@ -762,6 +753,10 @@ func (ck MkLineChecker) warnVarusePermis
                mkline.Warnf("%s should not be used at load time in any file.", varname)
                ck.explainPermissions(varname, vartype, loadTimeExplanation()...)
 
+       case alternativeFiles == "":
+               mkline.Warnf("%s should not be used in any file.", varname)
+               ck.explainPermissions(varname, vartype, loadTimeExplanation()...)
+
        case directly:
                mkline.Warnf(
                        "%s should not be used at load time in this file; "+
@@ -775,7 +770,6 @@ func (ck MkLineChecker) warnVarusePermis
                        varname, alternativeFiles)
                ck.explainPermissions(varname, vartype)
        }
-
 }
 
 // warnVaruseToolLoadTime logs a warning that the tool ${varname}
@@ -1003,7 +997,7 @@ func (ck MkLineChecker) checkVarassignLe
        ck.checkTextVarUse(
                ck.MkLine.Varname(),
                NewVartype(BtVariableName, NoVartypeOptions, NewACLEntry("*", aclpAll)),
-               vucTimeParse)
+               vucTimeLoad)
 }
 
 func (ck MkLineChecker) checkVarassignOp() {
@@ -1100,7 +1094,7 @@ func (ck MkLineChecker) checkVarassignLe
                return
        }
 
-       if ck.MkLines != nil && ck.MkLines.vars.UsedSimilar(varname) {
+       if ck.MkLines.vars.UsedSimilar(varname) {
                return
        }
 
@@ -1118,7 +1112,7 @@ func (ck MkLineChecker) checkVarassignLe
                return
        }
 
-       if ck.MkLines == nil || !ck.MkLines.FirstTimeSlice("defined but not used: ", varname) {
+       if !ck.MkLines.FirstTimeSlice("defined but not used: ", varname) {
                return
        }
 
@@ -1146,7 +1140,7 @@ func (ck MkLineChecker) checkVarassignRi
 
        time := vucTimeRun
        if op == opAssignEval || op == opAssignShell {
-               time = vucTimeParse
+               time = vucTimeLoad
        }
 
        vartype := G.Pkgsrc.VariableType(ck.MkLines, mkline.Varname())
@@ -1317,8 +1311,8 @@ func (ck MkLineChecker) checkVarassignLe
        // A few of the user-settable variables can also be set by packages.
        // That's an unfortunate situation since there is no definite source
        // of truth, but luckily only a few variables make use of it.
-       vartype := G.Pkgsrc.VariableType(nil, varname)
-       if vartype != nil && vartype.PackageSettable() {
+       vartype := G.Pkgsrc.VariableType(ck.MkLines, varname)
+       if vartype.PackageSettable() {
                return true
        }
 
@@ -1463,7 +1457,7 @@ func (ck MkLineChecker) checkDirectiveCo
 
        checkVarUse := func(varuse *MkVarUse) {
                var vartype *Vartype // TODO: Insert a better type guess here.
-               vuc := VarUseContext{vartype, vucTimeParse, VucQuotPlain, false}
+               vuc := VarUseContext{vartype, vucTimeLoad, VucQuotPlain, false}
                ck.CheckVaruse(varuse, &vuc)
        }
 
@@ -1689,7 +1683,7 @@ func (ck MkLineChecker) CheckRelativePat
 
        abs := path.Dir(mkline.Filename) + "/" + resolvedPath
        if _, err := os.Stat(abs); err != nil {
-               if mustExist && !(ck.MkLines != nil && ck.MkLines.indentation.IsCheckedFile(resolvedPath)) {
+               if mustExist && !ck.MkLines.indentation.IsCheckedFile(resolvedPath) {
                        mkline.Errorf("Relative path %q does not exist.", resolvedPath)
                }
                return

Index: pkgsrc/pkgtools/pkglint/files/mklinechecker_test.go
diff -u pkgsrc/pkgtools/pkglint/files/mklinechecker_test.go:1.36 pkgsrc/pkgtools/pkglint/files/mklinechecker_test.go:1.37
--- pkgsrc/pkgtools/pkglint/files/mklinechecker_test.go:1.36    Sun May 26 14:05:57 2019
+++ pkgsrc/pkgtools/pkglint/files/mklinechecker_test.go Mon Jun 10 19:51:57 2019
@@ -47,6 +47,22 @@ func (s *Suite) Test_MkLineChecker_check
                "WARN: ~/category/package/filename.mk:6: VAR is defined but not used.")
 }
 
+func (s *Suite) Test_MkLineChecker_checkVarassignLeftNotUsed__procedure_call_no_tracing(c *check.C) {
+       t := s.Init(c)
+
+       t.DisableTracing() // Just for code coverage
+       t.CreateFileLines("mk/pkg-build-options.mk")
+       mklines := t.SetUpFileMkLines("category/package/filename.mk",
+               MkRcsID,
+               "",
+               "pkgbase := glib2",
+               ".include \"../../mk/pkg-build-options.mk\"")
+
+       mklines.Check()
+
+       t.CheckOutputEmpty()
+}
+
 func (s *Suite) Test_MkLineChecker_checkVarassignLeftNotUsed__infra(c *check.C) {
        t := s.Init(c)
 
@@ -137,6 +153,90 @@ func (s *Suite) Test_MkLineChecker_check
                        "which differs from the default value \"default\" from mk/defaults/mk.conf.")
 }
 
+func (s *Suite) Test_MkLineChecker_checkVarassignLeftUserSettable__before_prefs(c *check.C) {
+       t := s.Init(c)
+
+       t.SetUpCommandLine("-Wall", "--explain")
+       t.SetUpPackage("category/package",
+               "BEFORE=\tvalue",
+               ".include \"../../mk/bsd.prefs.mk\"")
+       t.CreateFileLines("mk/defaults/mk.conf",
+               MkRcsID,
+               "BEFORE?=\tvalue")
+       t.Chdir("category/package")
+       t.FinishSetUp()
+
+       G.Check(".")
+
+       t.CheckOutputLines(
+               "NOTE: Makefile:20: Redundant definition for BEFORE from mk/defaults/mk.conf.",
+               "",
+               "\tInstead of defining the variable redundantly, it suffices to include",
+               "\t../../mk/bsd.prefs.mk, which provides all user-settable variables.",
+               "")
+}
+
+func (s *Suite) Test_MkLineChecker_checkVarassignLeftUserSettable__after_prefs(c *check.C) {
+       t := s.Init(c)
+
+       t.SetUpCommandLine("-Wall", "--explain")
+       t.SetUpPackage("category/package",
+               ".include \"../../mk/bsd.prefs.mk\"",
+               "AFTER=\tvalue")
+       t.CreateFileLines("mk/defaults/mk.conf",
+               MkRcsID,
+               "AFTER?=\t\tvalue")
+       t.Chdir("category/package")
+       t.FinishSetUp()
+
+       G.Check(".")
+
+       t.CheckOutputLines(
+               "NOTE: Makefile:21: Redundant definition for AFTER from mk/defaults/mk.conf.")
+}
+
+func (s *Suite) Test_MkLineChecker_checkVarassignLeftUserSettable__vartype_nil(c *check.C) {
+       t := s.Init(c)
+
+       t.CreateFileLines("category/package/vars.mk",
+               MkRcsID,
+               "#",
+               "# User-settable variables:",
+               "#",
+               "# USER_SETTABLE",
+               "#\tDocumentation for USER_SETTABLE.",
+               "",
+               ".include \"../../mk/bsd.prefs.mk\"",
+               "",
+               "USER_SETTABLE?=\tdefault")
+       t.SetUpPackage("category/package",
+               "USER_SETTABLE=\tvalue")
+       t.Chdir("category/package")
+       t.FinishSetUp()
+
+       G.Check(".")
+
+       // TODO: As of June 2019, pkglint doesn't parse the "User-settable variables"
+       //  comment. Therefore it doesn't know that USER_SETTABLE is intended to be
+       //  used by other packages. There should be no warning.
+       t.CheckOutputLines(
+               "WARN: Makefile:20: USER_SETTABLE is defined but not used.")
+}
+
+func (s *Suite) Test_MkLineChecker_checkVarassignLeftBsdPrefs__vartype_nil(c *check.C) {
+       t := s.Init(c)
+
+       mklines := t.NewMkLines("builtin.mk",
+               MkRcsID,
+               "VAR_SH?=\tvalue")
+
+       mklines.Check()
+
+       t.CheckOutputLines(
+               "WARN: builtin.mk:2: VAR_SH is defined but not used.",
+               "WARN: builtin.mk:2: Please include \"../../mk/bsd.prefs.mk\" before using \"?=\".")
+}
+
 func (s *Suite) Test_MkLineChecker_Check__url2pkg(c *check.C) {
        t := s.Init(c)
 
@@ -513,6 +613,23 @@ func (s *Suite) Test_MkLineChecker_check
                "WARN: filename.mk:2: The \"+=\" operator should only be used with lists, not with DISTNAME.")
 }
 
+func (s *Suite) Test_MkLineChecker_checkVartype__no_tracing(c *check.C) {
+       t := s.Init(c)
+
+       t.SetUpVartypes()
+       mklines := t.NewMkLines("filename.mk",
+               MkRcsID,
+               "UNKNOWN=\tvalue",
+               "CUR_DIR!=\tpwd")
+       t.DisableTracing()
+
+       mklines.Check()
+
+       t.CheckOutputLines(
+               "WARN: filename.mk:2: UNKNOWN is defined but not used.",
+               "WARN: filename.mk:3: CUR_DIR is defined but not used.")
+}
+
 // Pkglint once interpreted all lists as consisting of shell tokens,
 // splitting this URL at the ampersand.
 func (s *Suite) Test_MkLineChecker_checkVarassign__URL_with_shell_special_characters(c *check.C) {
@@ -774,39 +891,91 @@ func (s *Suite) Test_MkLineChecker_check
        t := s.Init(c)
 
        t.SetUpVartypes()
-       mklines := t.NewMkLines("filename.mk",
-               MkRcsID,
-               "ONLY_FOR_PLATFORM=\t*-*-*", // The CVS Id above is not a rationale.
-               "NOT_FOR_PLATFORM=\t*-*-*",  // Neither does this line have a rationale.
-               "",
-               "ONLY_FOR_PLATFORM+=\t*-*-* # rationale",
-               "",
-               "# rationale in the line above",
-               "ONLY_FOR_PLATFORM+=\t*-*-*",
-               "",
-               "#VAR=\tvalue",               // This comment is not a rationale.
-               "ONLY_FOR_PLATFORM+=\t*-*-*", // Needs a rationale.
-               "",
-               "# rationale",
-               "BROKEN_ON_PLATFORM+=\t*-*-*",
-               "BROKEN_ON_PLATFORM+=\t*-*-*", // The rationale applies to this line, too.
-               "",
-               "PKGNAME=\tpackage-1.0", // Does not need a rationale.
-               "UNKNOWN=\t${UNKNOWN}")  // Unknown type, does not need a rationale.
 
-       mklines.Check()
+       testLines := func(lines []string, diagnostics ...string) {
+               mklines := t.NewMkLines("filename.mk",
+                       lines...)
 
-       t.CheckOutputLines(
-               "WARN: filename.mk:2: Setting variable ONLY_FOR_PLATFORM should have a rationale.",
-               "WARN: filename.mk:3: Setting variable NOT_FOR_PLATFORM should have a rationale.",
-               "WARN: filename.mk:11: Setting variable ONLY_FOR_PLATFORM should have a rationale.")
+               mklines.Check()
 
-       // This check is only enabled when -Wextra is given.
-       t.SetUpCommandLine()
+               t.CheckOutput(diagnostics)
+       }
+       test := func(lines []string, diagnostics ...string) {
+               testLines(append([]string{MkRcsID, ""}, lines...), diagnostics...)
+       }
+       lines := func(lines ...string) []string { return lines }
 
-       mklines.Check()
+       test(
+               lines(
+                       MkRcsID,
+                       "ONLY_FOR_PLATFORM=\t*-*-*", // The CVS Id above is not a rationale.
+                       "NOT_FOR_PLATFORM=\t*-*-*",  // Neither does this line have a rationale.
+               ),
+               "WARN: filename.mk:4: Setting variable ONLY_FOR_PLATFORM should have a rationale.",
+               "WARN: filename.mk:5: Setting variable NOT_FOR_PLATFORM should have a rationale.")
 
-       t.CheckOutputEmpty()
+       test(
+               lines(
+                       "ONLY_FOR_PLATFORM+=\t*-*-* # rationale in the same line"),
+               nil...)
+
+       test(
+               lines(
+                       "",
+                       "# rationale in the line above",
+                       "ONLY_FOR_PLATFORM+=\t*-*-*"),
+               nil...)
+
+       // A commented variable assignment does not count as a rationale,
+       // since it is not in plain text.
+       test(
+               lines(
+                       "#VAR=\tvalue",
+                       "ONLY_FOR_PLATFORM+=\t*-*-*"),
+               "WARN: filename.mk:4: Setting variable ONLY_FOR_PLATFORM should have a rationale.")
+
+       // Another variable assignment with comment does not count as a rationale.
+       test(
+               lines(
+                       "PKGNAME=\t\tpackage-1.0 # this is not a rationale",
+                       "ONLY_FOR_PLATFORM+=\t*-*-*"),
+               "WARN: filename.mk:4: Setting variable ONLY_FOR_PLATFORM should have a rationale.")
+
+       // A rationale applies to all variable assignments directly below it.
+       test(
+               lines(
+                       "# rationale",
+                       "BROKEN_ON_PLATFORM+=\t*-*-*",
+                       "BROKEN_ON_PLATFORM+=\t*-*-*"), // The rationale applies to this line, too.
+               nil...)
+
+       // Just for code coverage.
+       test(
+               lines(
+                       "PKGNAME=\tpackage-1.0", // Does not need a rationale.
+                       "UNKNOWN=\t${UNKNOWN}"), // Unknown type, does not need a rationale.
+               nil...)
+
+       // When a line requiring a rationale appears in the very first line
+       // or in the second line of a file, there is no index out of bounds error.
+       testLines(
+               lines(
+                       "NOT_FOR_PLATFORM=\t*-*-*",
+                       "NOT_FOR_PLATFORM=\t*-*-*"),
+               sprintf("ERROR: filename.mk:1: Expected %q.", MkRcsID),
+               "WARN: filename.mk:1: Setting variable NOT_FOR_PLATFORM should have a rationale.",
+               "WARN: filename.mk:2: Setting variable NOT_FOR_PLATFORM should have a rationale.")
+
+       // The whole rationale check is only enabled when -Wextra is given.
+       t.SetUpCommandLine()
+
+       test(
+               lines(
+                       MkRcsID,
+                       "ONLY_FOR_PLATFORM=\t*-*-*", // The CVS Id above is not a rationale.
+                       "NOT_FOR_PLATFORM=\t*-*-*",  // Neither does this line have a rationale.
+               ),
+               nil...)
 }
 
 func (s *Suite) Test_MkLineChecker_checkVarassignOpShell(c *check.C) {
@@ -1292,6 +1461,34 @@ func (s *Suite) Test_MkLineChecker_check
                        "it is a write-only variable.")
 }
 
+func (s *Suite) Test_MkLineChecker_warnVarusePermissions__not_directly_and_no_alternative_files(c *check.C) {
+       t := s.Init(c)
+
+       t.SetUpVartypes()
+       mklines := t.NewMkLines("mk-c.mk",
+               MkRcsID,
+               "",
+               "TOOL_DEPENDS+=\t${BUILDLINK_API_DEPENDS.mk-c}:${BUILDLINK_PKGSRCDIR.mk-c}")
+
+       mklines.Check()
+
+       toolDependsType := G.Pkgsrc.VariableType(nil, "TOOL_DEPENDS")
+       t.Check(toolDependsType.String(), equals, "DependencyWithPath (list, package-settable)")
+       t.Check(toolDependsType.AlternativeFiles(aclpAppend), equals, "Makefile, Makefile.* or *.mk")
+       t.Check(toolDependsType.AlternativeFiles(aclpUse), equals, "Makefile, Makefile.* or *.mk")
+       t.Check(toolDependsType.AlternativeFiles(aclpUseLoadtime), equals, "")
+
+       apiDependsType := G.Pkgsrc.VariableType(nil, "BUILDLINK_API_DEPENDS.*")
+       t.Check(apiDependsType.String(), equals, "Dependency (list, package-settable)")
+       t.Check(apiDependsType.AlternativeFiles(aclpUse), equals, "")
+       t.Check(apiDependsType.AlternativeFiles(aclpUseLoadtime), equals, "buildlink3.mk or builtin.mk only")
+
+       t.CheckOutputLines(
+               "WARN: mk-c.mk:3: BUILDLINK_API_DEPENDS.mk-c should not be used in any file.",
+               "WARN: mk-c.mk:3: The list variable BUILDLINK_API_DEPENDS.mk-c should not be embedded in a word.",
+               "WARN: mk-c.mk:3: BUILDLINK_PKGSRCDIR.mk-c should not be used in any file.")
+}
+
 func (s *Suite) Test_MkLineChecker_checkVarassignDecreasingVersions(c *check.C) {
        t := s.Init(c)
 
@@ -1531,8 +1728,7 @@ func (s *Suite) Test_MkLineChecker_check
                        MkRcsID,
                        before,
                        ".endif")
-               mkline := mklines.mklines[1]
-               ck := MkLineChecker{nil, mkline}
+               ck := MkLineChecker{mklines, mklines.mklines[1]}
 
                t.SetUpCommandLine("-Wall")
                ck.checkDirectiveCond()
@@ -1625,9 +1821,19 @@ func (s *Suite) Test_MkLineChecker_check
                //  Luckily the !! pattern doesn't occur in practice.
                ".if !(${PKGPATH} == pattern)")
 
+       test(".if empty(PKGPATH:Mpattern) || 0",
+
+               "NOTE: module.mk:2: PKGPATH should be compared using != instead of matching against \":Mpattern\".",
+               "AUTOFIX: module.mk:2: Replacing \"empty(PKGPATH:Mpattern)\" with \"(${PKGPATH} != pattern)\".",
+
+               ".if (${PKGPATH} != pattern) || 0")
+
        // No note in this case since there is no implicit !empty around the varUse.
        test(".if ${PKGPATH:Mpattern} != ${OTHER}",
-               nil...)
+
+               "WARN: module.mk:2: OTHER is used but not defined.",
+
+               ".if ${PKGPATH:Mpattern} != ${OTHER}")
 
        test(
                ".if ${PKGPATH:Mpattern}",
@@ -1645,6 +1851,14 @@ func (s *Suite) Test_MkLineChecker_check
 
                ".if ${PKGPATH} != pattern")
 
+       test(
+               ".if !!${PKGPATH:Mpattern}",
+
+               "NOTE: module.mk:2: PKGPATH should be compared using != instead of matching against \":Mpattern\".",
+               "AUTOFIX: module.mk:2: Replacing \"!${PKGPATH:Mpattern}\" with \"(${PKGPATH} != pattern)\".",
+
+               ".if !(${PKGPATH} != pattern)")
+
        // This pattern with spaces doesn't make sense at all in the :M
        // modifier since it can never match.
        // Or can it, if the PKGPATH contains quotes?
@@ -1698,7 +1912,10 @@ func (s *Suite) Test_MkLineChecker_check
        // transformed at all.
        test(
                ".if ${UNKNOWN:Nnegative-pattern}",
-               nil...)
+
+               "WARN: module.mk:2: UNKNOWN is used but not defined.",
+
+               ".if ${UNKNOWN:Nnegative-pattern}")
 
        test(
                ".if ${PKGPATH:Mpath1} || ${PKGPATH:Mpath2}",
@@ -1737,6 +1954,21 @@ func (s *Suite) Test_MkLineChecker_check
                "WARN: Makefile:3: Use ${PKGSRC_COMPILER:Ngcc} instead of the != operator.")
 }
 
+func (s *Suite) Test_MkLineChecker_checkDirectiveCondCompareVarStr__no_tracing(c *check.C) {
+       t := s.Init(c)
+
+       t.SetUpVartypes()
+       mklines := t.NewMkLines("filename.mk",
+               ".if ${DISTFILES:Mpattern:O:u} == NetBSD")
+       t.DisableTracing()
+
+       ck := MkLineChecker{mklines, mklines.mklines[0]}
+       varUse := NewMkVarUse("DISTFILES", "Mpattern", "O", "u")
+       ck.checkDirectiveCondCompareVarStr(varUse, "==", "distfile-1.0.tar.gz")
+
+       t.CheckOutputEmpty()
+}
+
 func (s *Suite) Test_MkLineChecker_checkVartype__CFLAGS_with_backticks(c *check.C) {
        t := s.Init(c)
 
@@ -1775,17 +2007,6 @@ func (s *Suite) Test_MkLineChecker_check
                "WARN: Makefile:2: Compiler flag \"%s\\\\\\\"\" should start with a hyphen.")
 }
 
-func (s *Suite) Test_MkLineChecker_checkDirectiveIndentation(c *check.C) {
-       t := s.Init(c)
-
-       mkline := t.NewMkLine("filename.mk", 123, ".if 0")
-
-       // Calling this method is only useful in the context of a whole file.
-       MkLineChecker{nil, mkline}.checkDirectiveIndentation(4)
-
-       t.CheckOutputEmpty()
-}
-
 func (s *Suite) Test_MkLineChecker_checkDirectiveIndentation__autofix(c *check.C) {
        t := s.Init(c)
 
@@ -1973,7 +2194,11 @@ func (s *Suite) Test_MkLineChecker_Check
                "CPPPATH.Linux=\t/usr/bin/cpp")
        t.FinishSetUp()
 
-       ck := MkLineChecker{nil, t.NewMkLine("module.mk", 101, "COMMENT=\t${CPPPATH.SunOS}")}
+       mklines := t.NewMkLines("module.mk",
+               MkRcsID,
+               "COMMENT=\t${CPPPATH.SunOS}")
+
+       ck := MkLineChecker{mklines, mklines.mklines[1]}
 
        ck.CheckVaruse(NewMkVarUse("CPPPATH.SunOS"), &VarUseContext{
                vartype: &Vartype{
@@ -2101,21 +2326,22 @@ func (s *Suite) Test_MkLineChecker_check
 
        t.SetUpCommandLine("--show-autofix", "--source")
        t.SetUpVartypes()
-       mkline := t.NewMkLine("mk/compiler/gcc.mk", 150,
+       mklines := t.NewMkLines("mk/compiler/gcc.mk",
+               MkRcsID,
                "CC:=\t${CC:C/^/_asdf_/1:M_asdf_*:S/^_asdf_//}")
 
-       MkLineChecker{nil, mkline}.Check()
+       mklines.Check()
 
        t.CheckOutputLines(
-               "NOTE: mk/compiler/gcc.mk:150: "+
+               "NOTE: mk/compiler/gcc.mk:2: "+
                        "The modifier \":C/^/_asdf_/1:M_asdf_*:S/^_asdf_//\" can be written as \":[1]\".",
-               "AUTOFIX: mk/compiler/gcc.mk:150: "+
+               "AUTOFIX: mk/compiler/gcc.mk:2: "+
                        "Replacing \":C/^/_asdf_/1:M_asdf_*:S/^_asdf_//\" with \":[1]\".",
                "-\tCC:=\t${CC:C/^/_asdf_/1:M_asdf_*:S/^_asdf_//}",
                "+\tCC:=\t${CC:[1]}")
 
        // Now go through all the "almost" cases, to reach full branch coverage.
-       mklines := t.NewMkLines("gcc.mk",
+       mklines = t.NewMkLines("gcc.mk",
                MkRcsID,
                "\t: ${CC:M1:M2:M3}",
                "\t: ${CC:C/^begin//:M2:M3}",                    // M1 pattern not exactly ^
@@ -2139,13 +2365,14 @@ func (s *Suite) Test_MkLineChecker_Check
        t.SetUpVartypes()
        G.Pkgsrc.initDeprecatedVars()
 
-       mkline := t.NewMkLine("module.mk", 123,
+       mklines := t.NewMkLines("module.mk",
+               MkRcsID,
                "\t${_PKG_SILENT}${_PKG_DEBUG} :")
 
-       MkLineChecker{nil, mkline}.Check()
+       mklines.Check()
 
        t.CheckOutputLines(
-               "WARN: module.mk:123: Use of _PKG_SILENT and _PKG_DEBUG is deprecated. Use ${RUN} instead.")
+               "WARN: module.mk:2: Use of _PKG_SILENT and _PKG_DEBUG is deprecated. Use ${RUN} instead.")
 }
 
 func (s *Suite) Test_MkLineChecker_checkVaruseUndefined(c *check.C) {
@@ -2406,14 +2633,18 @@ func (s *Suite) Test_MkLineChecker_Check
 
        t.CreateFileLines("wip/mk/git-package.mk",
                MkRcsID)
+       t.CreateFileLines("wip/other/version.mk",
+               MkRcsID)
        t.SetUpPackage("wip/package",
-               ".include \"../mk/git-package.mk\"")
+               ".include \"../mk/git-package.mk\"",
+               ".include \"../other/version.mk\"")
        t.FinishSetUp()
 
        G.Check(t.File("wip/package"))
 
        t.CheckOutputLines(
-               "WARN: ~/wip/package/Makefile:20: " +
-                       "References to the pkgsrc-wip infrastructure should look like \"../../wip/mk\", " +
-                       "not \"../mk\".")
+               "WARN: ~/wip/package/Makefile:20: References to the pkgsrc-wip "+
+                       "infrastructure should look like \"../../wip/mk\", not \"../mk\".",
+               "WARN: ~/wip/package/Makefile:21: References to other packages "+
+                       "should look like \"../../category/package\", not \"../package\".")
 }

Index: pkgsrc/pkgtools/pkglint/files/mklines.go
diff -u pkgsrc/pkgtools/pkglint/files/mklines.go:1.49 pkgsrc/pkgtools/pkglint/files/mklines.go:1.50
--- pkgsrc/pkgtools/pkglint/files/mklines.go:1.49       Sun May 26 14:05:57 2019
+++ pkgsrc/pkgtools/pkglint/files/mklines.go    Mon Jun 10 19:51:57 2019
@@ -255,6 +255,9 @@ func (mklines *MkLinesImpl) ExpandLoopVa
 }
 
 func (mklines *MkLinesImpl) collectDefinedVariables() {
+       // FIXME: This method has a wrong name. It collects not only the defined
+       //  variables but also the used ones.
+
        if trace.Tracing {
                defer trace.Call0()()
        }
Index: pkgsrc/pkgtools/pkglint/files/vartypecheck_test.go
diff -u pkgsrc/pkgtools/pkglint/files/vartypecheck_test.go:1.49 pkgsrc/pkgtools/pkglint/files/vartypecheck_test.go:1.50
--- pkgsrc/pkgtools/pkglint/files/vartypecheck_test.go:1.49     Sun May 26 14:05:57 2019
+++ pkgsrc/pkgtools/pkglint/files/vartypecheck_test.go  Mon Jun 10 19:51:57 2019
@@ -500,10 +500,31 @@ func (s *Suite) Test_VartypeCheck_FetchU
        vt.Values(
                "${MASTER_SITE_GITHUB:S,^,-,:=project/archive/${DISTFILE}}")
 
-       // No warning since there is more than a single := modifier.
-       // In this case, because of the hyphen that is added at the beginning,
-       // the URL is not required to end with a slash anymore.
+       // No warning that the part after the := must end with a slash,
+       // since there is another modifier in the variable use, in this case :S.
+       //
+       // That modifier adds a hyphen at the beginning (but pkglint doesn't
+       // inspect this), therefore the URL is not required to end with a slash anymore.
        vt.OutputEmpty()
+
+       // As of June 2019, the :S modifier is not analyzed since it is unusual.
+       vt.Values(
+               "${MASTER_SITE_GNU:S,$,subdir/,}")
+       vt.OutputEmpty()
+}
+
+func (s *Suite) Test_VartypeCheck_FetchURL__without_package(c *check.C) {
+       t := s.Init(c)
+
+       vt := NewVartypeCheckTester(t, (*VartypeCheck).FetchURL)
+
+       vt.Varname("MASTER_SITES")
+       vt.Values(
+               "https://github.com/example/project/";,
+               "${MASTER_SITE_OWN}")
+
+       vt.Output(
+               "ERROR: filename.mk:2: The site MASTER_SITE_OWN does not exist.")
 }
 
 func (s *Suite) Test_VartypeCheck_Filename(c *check.C) {

Index: pkgsrc/pkgtools/pkglint/files/mklines_test.go
diff -u pkgsrc/pkgtools/pkglint/files/mklines_test.go:1.44 pkgsrc/pkgtools/pkglint/files/mklines_test.go:1.45
--- pkgsrc/pkgtools/pkglint/files/mklines_test.go:1.44  Tue May 21 17:59:48 2019
+++ pkgsrc/pkgtools/pkglint/files/mklines_test.go       Mon Jun 10 19:51:57 2019
@@ -366,6 +366,22 @@ func (s *Suite) Test_MkLines_collectDefi
                "WARN: ~/category/package/builtin.mk:8: H_UNDEF is used but not defined.")
 }
 
+func (s *Suite) Test_MkLines_collectDefinedVariables__no_tracing(c *check.C) {
+       t := s.Init(c)
+
+       mklines := t.SetUpFileMkLines("filename.mk",
+               MkRcsID,
+               "",
+               "BUILD_DEFS+=\tVAR1",
+               "PLIST_VARS+=\tvar2",
+               "SUBST_VARS.id+=\tVAR3")
+       t.DisableTracing()
+
+       mklines.collectDefinedVariables()
+
+       t.CheckOutputEmpty()
+}
+
 func (s *Suite) Test_MkLines_collectUsedVariables__simple(c *check.C) {
        t := s.Init(c)
 

Index: pkgsrc/pkgtools/pkglint/files/mklines_varalign_test.go
diff -u pkgsrc/pkgtools/pkglint/files/mklines_varalign_test.go:1.11 pkgsrc/pkgtools/pkglint/files/mklines_varalign_test.go:1.12
--- pkgsrc/pkgtools/pkglint/files/mklines_varalign_test.go:1.11 Tue Apr 23 21:20:49 2019
+++ pkgsrc/pkgtools/pkglint/files/mklines_varalign_test.go      Mon Jun 10 19:51:57 2019
@@ -303,6 +303,51 @@ func (s *Suite) Test_Varalign__continuat
        vt.Run()
 }
 
+// FIXME: The continuation line must not be fixed. It should either stay
+//  as it is, or the whole paragraph should be aligned to column 9, putting
+//  all variable values from the continuation line into the actual
+//  continuation line.
+func (s *Suite) Test_Varalign__continuation_line_one_tab_ahead(c *check.C) {
+       vt := NewVaralignTester(s, c)
+       vt.Input(
+               "VAR=\t\tvalue",
+               "MASTER_SITE_NEDIT=\thttps://example.org \\",
+               "\t\t\thttps://example.org";)
+       vt.Diagnostics(
+               "NOTE: ~/Makefile:2--3: This line should be aligned with \"\\t\\t\".")
+       vt.Autofixes(
+               "AUTOFIX: ~/Makefile:3: Replacing indentation \"\\t\\t\\t\" with \"\\t\\t\".")
+       vt.Fixed(
+               "VAR=            value",
+               "MASTER_SITE_NEDIT=      https://example.org \\",
+               "                https://example.org";)
+       vt.Run()
+}
+
+// As of June 2019, the long variable name doesn't count as an outlier
+// because it only needs one more tab than the second-longest variable.
+// This contradicts the visual impression, in which the variable names
+// differ largely in their length.
+//
+// TODO: The long variable name should count as an outlier since it is
+//  more than 8 characters longer, no matter how many tabs are needed.
+//
+// See cad/ghdl/Makefile revision 1.6.
+func (s *Suite) Test_Varalign__outlier_more_than_8_spaces(c *check.C) {
+       vt := NewVaralignTester(s, c)
+       vt.Input(
+               "V2=\tvalue",
+               "V0000000000014=\tvalue")
+       vt.Diagnostics(
+               "NOTE: ~/Makefile:1: This variable value should be aligned to column 17.")
+       vt.Autofixes(
+               "AUTOFIX: ~/Makefile:1: Replacing \"\\t\" with \"\\t\\t\".")
+       vt.Fixed(
+               "V2=             value",
+               "V0000000000014= value")
+       vt.Run()
+}
+
 // Ensures that a wrong warning introduced in ccb56a5 is not logged.
 func (s *Suite) Test_Varalign__aligned_continuation(c *check.C) {
        vt := NewVaralignTester(s, c)

Index: pkgsrc/pkgtools/pkglint/files/mkparser.go
diff -u pkgsrc/pkgtools/pkglint/files/mkparser.go:1.28 pkgsrc/pkgtools/pkglint/files/mkparser.go:1.29
--- pkgsrc/pkgtools/pkglint/files/mkparser.go:1.28      Tue Apr 23 21:20:49 2019
+++ pkgsrc/pkgtools/pkglint/files/mkparser.go   Mon Jun 10 19:51:57 2019
@@ -29,7 +29,7 @@ func (p *MkParser) Rest() string {
 // which means the # is a normal character and does not introduce a Makefile comment.
 // For VarUse, this distinction is irrelevant.
 func NewMkParser(line Line, text string, emitWarnings bool) *MkParser {
-       G.Assertf((line != nil) == emitWarnings, "line must be given iff emitWarnings is set")
+       assertf((line != nil) == emitWarnings, "line must be given iff emitWarnings is set")
        return &MkParser{line, textproc.NewLexer(text), emitWarnings}
 }
 
Index: pkgsrc/pkgtools/pkglint/files/patches_test.go
diff -u pkgsrc/pkgtools/pkglint/files/patches_test.go:1.28 pkgsrc/pkgtools/pkglint/files/patches_test.go:1.29
--- pkgsrc/pkgtools/pkglint/files/patches_test.go:1.28  Sat Jan 26 16:31:33 2019
+++ pkgsrc/pkgtools/pkglint/files/patches_test.go       Mon Jun 10 19:51:57 2019
@@ -519,6 +519,29 @@ func (s *Suite) Test_CheckLinesPatch__in
                "ERROR: ~/patch-aa:10: Invalid line in unified patch hunk: <<<<<<<<")
 }
 
+// Just for code coverage.
+func (s *Suite) Test_PatchChecker_checklineContext__no_tracing(c *check.C) {
+       t := s.Init(c)
+
+       lines := t.NewLines("patch-WithComment",
+               RcsID,
+               "",
+               "Documentation",
+               "",
+               "--- file.orig",
+               "+++ file",
+               "@@ -5,3 +5,3 @@",
+               " context before",
+               "-old line",
+               "+new line",
+               " context after")
+       t.DisableTracing()
+
+       CheckLinesPatch(lines)
+
+       t.CheckOutputEmpty()
+}
+
 func (s *Suite) Test_PatchChecker_checklineAdded__shell(c *check.C) {
        t := s.Init(c)
 

Index: pkgsrc/pkgtools/pkglint/files/mkshparser_test.go
diff -u pkgsrc/pkgtools/pkglint/files/mkshparser_test.go:1.13 pkgsrc/pkgtools/pkglint/files/mkshparser_test.go:1.14
--- pkgsrc/pkgtools/pkglint/files/mkshparser_test.go:1.13       Tue May 21 17:59:48 2019
+++ pkgsrc/pkgtools/pkglint/files/mkshparser_test.go    Mon Jun 10 19:51:57 2019
@@ -755,7 +755,7 @@ func (b *MkShBuilder) Subshell(list *MkS
 func (b *MkShBuilder) Token(mktext string) *ShToken {
        tokenizer := NewShTokenizer(dummyLine, mktext, false)
        token := tokenizer.ShToken()
-       G.Assertf(tokenizer.parser.EOF(), "Invalid token: %q", tokenizer.parser.Rest())
+       assertf(tokenizer.parser.EOF(), "Invalid token: %q", tokenizer.parser.Rest())
        return token
 }
 

Index: pkgsrc/pkgtools/pkglint/files/mkshwalker.go
diff -u pkgsrc/pkgtools/pkglint/files/mkshwalker.go:1.8 pkgsrc/pkgtools/pkglint/files/mkshwalker.go:1.9
--- pkgsrc/pkgtools/pkglint/files/mkshwalker.go:1.8     Sat Apr 20 17:43:24 2019
+++ pkgsrc/pkgtools/pkglint/files/mkshwalker.go Mon Jun 10 19:51:57 2019
@@ -92,7 +92,7 @@ func (w *MkShWalker) Walk(list *MkShList
        w.walkList(-1, list)
 
        // If this fails, the calls to w.push and w.pop are unbalanced.
-       G.Assertf(len(w.Context) == 0, "MkShWalker.Walk %v", w.Context)
+       assertf(len(w.Context) == 0, "MkShWalker.Walk %v", w.Context)
 }
 
 func (w *MkShWalker) walkList(index int, list *MkShList) {

Index: pkgsrc/pkgtools/pkglint/files/mktypes.go
diff -u pkgsrc/pkgtools/pkglint/files/mktypes.go:1.14 pkgsrc/pkgtools/pkglint/files/mktypes.go:1.15
--- pkgsrc/pkgtools/pkglint/files/mktypes.go:1.14       Sat Apr 20 17:43:24 2019
+++ pkgsrc/pkgtools/pkglint/files/mktypes.go    Mon Jun 10 19:51:57 2019
@@ -69,13 +69,15 @@ func (m MkVarUseModifier) Subst(str stri
                from = from[:len(from)-1]
        }
 
+       if regex && matches(from, `^[\w-]+$`) && matches(to, `^[^&$\\]*$`) {
+               // The "from" pattern is so simple that it doesn't matter whether
+               // the modifier is :S or :C, therefore treat it like the simpler :S.
+               regex = false
+       }
+
        if regex {
-               if matches(from, `^[\w-]+$`) && matches(to, `^[^&$\\]*$`) {
-                       regex = false
-               } else {
-                       // TODO: Maybe implement regular expression substitutions later.
-                       return "", false
-               }
+               // TODO: Maybe implement regular expression substitutions later.
+               return "", false
        }
 
        result := mkopSubst(str, leftAnchor, from, rightAnchor, to, options)

Index: pkgsrc/pkgtools/pkglint/files/mktypes_test.go
diff -u pkgsrc/pkgtools/pkglint/files/mktypes_test.go:1.10 pkgsrc/pkgtools/pkglint/files/mktypes_test.go:1.11
--- pkgsrc/pkgtools/pkglint/files/mktypes_test.go:1.10  Sat Apr 20 17:43:24 2019
+++ pkgsrc/pkgtools/pkglint/files/mktypes_test.go       Mon Jun 10 19:51:57 2019
@@ -139,3 +139,15 @@ func (s *Suite) Test_MkVarUseModifier_Su
        c.Check(ok, equals, false)
        c.Check(empty, equals, "")
 }
+
+func (s *Suite) Test_MkVarUseModifier_Subst__no_tracing(c *check.C) {
+       t := s.Init(c)
+
+       mod := MkVarUseModifier{"S,from,to,"}
+       t.DisableTracing()
+
+       result, ok := mod.Subst("from a to b")
+
+       c.Check(ok, equals, true)
+       c.Check(result, equals, "to a to b")
+}

Index: pkgsrc/pkgtools/pkglint/files/options_test.go
diff -u pkgsrc/pkgtools/pkglint/files/options_test.go:1.12 pkgsrc/pkgtools/pkglint/files/options_test.go:1.13
--- pkgsrc/pkgtools/pkglint/files/options_test.go:1.12  Sat Apr 20 17:43:24 2019
+++ pkgsrc/pkgtools/pkglint/files/options_test.go       Mon Jun 10 19:51:57 2019
@@ -264,3 +264,63 @@ func (s *Suite) Test_OptionsLinesChecker
                "WARN: ~/category/package/options.mk:8: OTHER_VARIABLE is used but not defined.",
                "WARN: ~/category/package/options.mk:4: Option \"opt\" should be handled below in an .if block.")
 }
+
+func (s *Suite) Test_CheckLinesOptionsMk__autofix(c *check.C) {
+       t := s.Init(c)
+
+       t.SetUpOption("opt", "")
+       t.CreateFileLines("mk/bsd.options.mk")
+       t.SetUpPackage("category/package",
+               ".include \"options.mk\"")
+       t.CreateFileLines("category/package/options.mk",
+               MkRcsID,
+               "",
+               "PKG_OPTIONS_VAR=\tPKG_OPTIONS.package",
+               "PKG_SUPPORTED_OPTIONS=\t# none",
+               "",
+               ".include \"../../mk/bsd.options.mk\"",
+               "",
+               ".if 0",
+               ".if 0",
+               ".endif",
+               ".endif")
+       t.FinishSetUp()
+       t.Chdir("category/package")
+
+       G.Check(".")
+
+       t.CheckOutputLines(
+               "NOTE: options.mk:9: This directive should be indented by 2 spaces.",
+               "NOTE: options.mk:10: This directive should be indented by 2 spaces.")
+
+       t.SetUpCommandLine("-Wall", "--show-autofix")
+
+       G.Check(".")
+
+       t.CheckOutputLines(
+               "NOTE: options.mk:9: This directive should be indented by 2 spaces.",
+               "AUTOFIX: options.mk:9: Replacing \".\" with \".  \".",
+               "NOTE: options.mk:10: This directive should be indented by 2 spaces.",
+               "AUTOFIX: options.mk:10: Replacing \".\" with \".  \".")
+
+       t.SetUpCommandLine("-Wall", "--autofix")
+
+       G.Check(".")
+
+       t.CheckOutputLines(
+               "AUTOFIX: options.mk:9: Replacing \".\" with \".  \".",
+               "AUTOFIX: options.mk:10: Replacing \".\" with \".  \".")
+
+       t.CheckFileLinesDetab("options.mk",
+               MkRcsID,
+               "",
+               "PKG_OPTIONS_VAR=        PKG_OPTIONS.package",
+               "PKG_SUPPORTED_OPTIONS=  # none",
+               "",
+               ".include \"../../mk/bsd.options.mk\"",
+               "",
+               ".if 0",
+               ".  if 0",
+               ".  endif",
+               ".endif")
+}

Index: pkgsrc/pkgtools/pkglint/files/package.go
diff -u pkgsrc/pkgtools/pkglint/files/package.go:1.55 pkgsrc/pkgtools/pkglint/files/package.go:1.56
--- pkgsrc/pkgtools/pkglint/files/package.go:1.55       Wed May 22 16:07:16 2019
+++ pkgsrc/pkgtools/pkglint/files/package.go    Mon Jun 10 19:51:57 2019
@@ -59,7 +59,7 @@ type Package struct {
 func NewPackage(dir string) *Package {
        pkgpath := G.Pkgsrc.ToRel(dir)
        if strings.Count(pkgpath, "/") != 1 {
-               G.Assertf(false, "Package directory %q must be two subdirectories below the pkgsrc root %q.",
+               assertf(false, "Package directory %q must be two subdirectories below the pkgsrc root %q.",
                        dir, G.Pkgsrc.File("."))
        }
 
@@ -807,6 +807,9 @@ func (pkg *Package) CheckVarorder(mkline
                return
        }
 
+       // TODO: Extract all this code into a separate VarOrderChecker
+       //  since it is equally useful for PKG_OPTIONS in options.mk.
+
        type Repetition uint8
        const (
                optional Repetition = iota
Index: pkgsrc/pkgtools/pkglint/files/pkglint.go
diff -u pkgsrc/pkgtools/pkglint/files/pkglint.go:1.55 pkgsrc/pkgtools/pkglint/files/pkglint.go:1.56
--- pkgsrc/pkgtools/pkglint/files/pkglint.go:1.55       Tue May 21 17:59:48 2019
+++ pkgsrc/pkgtools/pkglint/files/pkglint.go    Mon Jun 10 19:51:57 2019
@@ -61,8 +61,9 @@ func NewPkglint() Pkglint {
 
 // unusablePkglint returns a pkglint object that crashes as early as possible.
 // This is to ensure that tests are properly initialized and shut down.
-func unusablePkglint() Pkglint        { return Pkglint{} }
-func (pkglint *Pkglint) Usable() bool { return pkglint != nil }
+func unusablePkglint() Pkglint { return Pkglint{} }
+
+func (pkglint *Pkglint) usable() bool { return pkglint != nil }
 
 type InterPackage struct {
        hashes       map[string]*Hash    // Maps "alg:filename" => hash (inter-package check).
@@ -202,12 +203,12 @@ func (pkglint *Pkglint) Main(argv ...str
                        runtime.GC()
 
                        fd, err := os.Create("pkglint.heapdump")
-                       G.AssertNil(err, "heapDump.create")
+                       assertNil(err, "heapDump.create")
 
                        debug.WriteHeapDump(fd.Fd())
 
                        err = fd.Close()
-                       G.AssertNil(err, "heapDump.close")
+                       assertNil(err, "heapDump.close")
                }()
 
                f, err := os.Create("pkglint.pprof")
@@ -217,7 +218,7 @@ func (pkglint *Pkglint) Main(argv ...str
                defer f.Close()
 
                err = pprof.StartCPUProfile(f)
-               G.AssertNil(err, "Cannot start profiling")
+               assertNil(err, "Cannot start profiling")
                defer pprof.StopCPUProfile()
 
                pkglint.res.Profiling()
@@ -258,10 +259,9 @@ func (pkglint *Pkglint) Main(argv ...str
        pkglint.Pkgsrc.LoadInfrastructure()
 
        currentUser, err := user.Current()
-       if err == nil {
-               // On Windows, this is `Computername\Username`.
-               pkglint.Username = replaceAll(currentUser.Username, `^.*\\`, "")
-       }
+       assertNil(err, "user.Current")
+       // On Windows, this is `Computername\Username`.
+       pkglint.Username = replaceAll(currentUser.Username, `^.*\\`, "")
 
        for len(pkglint.Todo) > 0 {
                item := pkglint.Todo[0]
@@ -446,28 +446,6 @@ func (pkglint *Pkglint) checkdirPackage(
        pkg.check(files, mklines, allLines)
 }
 
-// Assertf checks that the condition is true. Otherwise it terminates the
-// process with a fatal error message, prefixed with "Pkglint internal error".
-//
-// This method must only be used for programming errors.
-// For runtime errors, use dummyLine.Fatalf.
-func (pkglint *Pkglint) Assertf(cond bool, format string, args ...interface{}) {
-       if !cond {
-               panic("Pkglint internal error: " + sprintf(format, args...))
-       }
-}
-
-// AssertNil ensures that the given error is nil.
-//
-// Contrary to other diagnostics, the format should not end in a period
-// since it is followed by the error.
-//
-// Other than Assertf, this method does not require any comparison operator in the calling code.
-// This makes it possible to get 100% branch coverage for cases that "really can never fail".
-func (pkglint *Pkglint) AssertNil(err error, format string, args ...interface{}) {
-       assertNil(err, format, args...)
-}
-
 // Returns the pkgsrc top-level directory, relative to the given directory.
 func findPkgsrcTopdir(dirname string) string {
        for _, dir := range [...]string{".", "..", "../..", "../../.."} {

Index: pkgsrc/pkgtools/pkglint/files/package_test.go
diff -u pkgsrc/pkgtools/pkglint/files/package_test.go:1.46 pkgsrc/pkgtools/pkglint/files/package_test.go:1.47
--- pkgsrc/pkgtools/pkglint/files/package_test.go:1.46  Mon May  6 20:27:17 2019
+++ pkgsrc/pkgtools/pkglint/files/package_test.go       Mon Jun 10 19:51:57 2019
@@ -46,6 +46,21 @@ func (s *Suite) Test_Package_checkLinesB
                "TRACE: - (*Package).checkLinesBuildlink3Inclusion()")
 }
 
+// Just for code coverage.
+func (s *Suite) Test_Package_checkLinesBuildlink3Inclusion__no_tracing(c *check.C) {
+       t := s.Init(c)
+
+       t.SetUpPackage("category/package",
+               "PKGNAME=\tpackage-1.0")
+       t.CreateFileDummyBuildlink3("category/package/buildlink3.mk")
+       t.FinishSetUp()
+
+       t.DisableTracing()
+       G.Check(t.File("category/package"))
+
+       t.CheckOutputEmpty()
+}
+
 func (s *Suite) Test_Package_pkgnameFromDistname(c *check.C) {
        t := s.Init(c)
 
@@ -98,31 +113,60 @@ func (s *Suite) Test_Package_pkgnameFrom
        test("${DISTFILE:C,\\..*,,}", "aspell-af-0.50-0", "")
 }
 
-func (s *Suite) Test_Package_CheckVarorder(c *check.C) {
+func (s *Suite) Test_Package_CheckVarorder__only_required_variables(c *check.C) {
        t := s.Init(c)
 
        pkg := NewPackage(t.File("x11/9term"))
+       mklines := t.NewMkLines("Makefile",
+               MkRcsID,
+               "",
+               "DISTNAME=9term",
+               "CATEGORIES=x11",
+               "",
+               ".include \"../../mk/bsd.pkg.mk\"")
 
-       pkg.CheckVarorder(t.NewMkLines("Makefile",
+       pkg.CheckVarorder(mklines)
+
+       t.CheckOutputLines(
+               "WARN: Makefile:3: The canonical order of the variables is " +
+                       "DISTNAME, CATEGORIES, empty line, COMMENT, LICENSE.")
+}
+
+func (s *Suite) Test_Package_CheckVarorder__with_optional_variables(c *check.C) {
+       t := s.Init(c)
+
+       pkg := NewPackage(t.File("x11/9term"))
+       mklines := t.NewMkLines("Makefile",
                MkRcsID,
                "",
                "GITHUB_PROJECT=project",
                "DISTNAME=9term",
-               "CATEGORIES=x11"))
+               "CATEGORIES=x11")
+
+       pkg.CheckVarorder(mklines)
 
        // TODO: Make this warning more specific to the actual situation.
        t.CheckOutputLines(
                "WARN: Makefile:3: The canonical order of the variables is " +
                        "GITHUB_PROJECT, DISTNAME, CATEGORIES, GITHUB_PROJECT, empty line, " +
                        "COMMENT, LICENSE.")
+}
 
-       pkg.CheckVarorder(t.NewMkLines("Makefile",
+// Just for code coverage.
+func (s *Suite) Test_Package_CheckVarorder__no_tracing(c *check.C) {
+       t := s.Init(c)
+
+       pkg := NewPackage(t.File("x11/9term"))
+       mklines := t.NewMkLines("Makefile",
                MkRcsID,
                "",
                "DISTNAME=9term",
                "CATEGORIES=x11",
                "",
-               ".include \"../../mk/bsd.pkg.mk\""))
+               ".include \"../../mk/bsd.pkg.mk\"")
+       t.DisableTracing()
+
+       pkg.CheckVarorder(mklines)
 
        t.CheckOutputLines(
                "WARN: Makefile:3: The canonical order of the variables is " +
@@ -176,8 +220,7 @@ func (s *Suite) Test_Package_CheckVarord
        t := s.Init(c)
 
        pkg := NewPackage(t.File("category/package"))
-
-       pkg.CheckVarorder(t.NewMkLines("Makefile",
+       mklines := t.NewMkLines("Makefile",
                MkRcsID,
                "",
                "DISTNAME=\tdistname-1.0",
@@ -186,11 +229,18 @@ func (s *Suite) Test_Package_CheckVarord
                ".if ${DISTNAME:Mdistname-*}",
                "MAINTAINER=\tpkgsrc-users%NetBSD.org@localhost",
                ".endif",
-               "LICENSE=\tgnu-gpl-v2"))
+               "LICENSE=\tgnu-gpl-v2")
+
+       pkg.CheckVarorder(mklines)
 
        // No warning about the missing COMMENT since the .if directive
        // causes the whole check to be skipped.
        t.CheckOutputEmpty()
+
+       // Just for code coverage.
+       t.DisableTracing()
+       pkg.CheckVarorder(mklines)
+       t.CheckOutputEmpty()
 }
 
 // TODO: Add more tests like skip_if_there_are_directives for other line types.
@@ -1233,6 +1283,35 @@ func (s *Suite) Test_Package_readMakefil
        t.Check(seen.seen, check.HasLen, 5)
 }
 
+// Just for code coverage.
+func (s *Suite) Test_Package_findIncludedFile__no_tracing(c *check.C) {
+       t := s.Init(c)
+
+       t.SetUpPackage("category/package",
+               ".include \"../../${UNKNOWN_PKGPATH}/buildlink3.mk\"",
+               ".include \"../../lang/language/buildlink3.mk\"")
+       t.CreateFileLines("lang/language/buildlink3.mk",
+               MkRcsID)
+       t.FinishSetUp()
+       pkg := NewPackage(t.File("category/package"))
+       t.DisableTracing()
+
+       pkg.loadPackageMakefile()
+
+       expected := []string{
+               "../../lang/language/buildlink3.mk",
+               "../../lang/language/builtin.mk",
+               "suppress-varorder.mk"}
+
+       seen := pkg.included
+       for _, filename := range expected {
+               if !seen.Seen(filename) {
+                       c.Errorf("File %q is not seen.", filename)
+               }
+       }
+       t.Check(seen.seen, check.HasLen, 3)
+}
+
 func (s *Suite) Test_Package_checkLocallyModified(c *check.C) {
        t := s.Init(c)
 
@@ -1296,6 +1375,26 @@ func (s *Suite) Test_Package_checkLocall
        t.CheckOutputEmpty()
 }
 
+// Just for code coverage.
+func (s *Suite) Test_Package_checkLocallyModified__no_tracing(c *check.C) {
+       t := s.Init(c)
+
+       G.Username = "example-user"
+       t.CreateFileLines("category/package/CVS/Entries",
+               "/Makefile//modified//")
+
+       pkg := t.SetUpPackage("category/package",
+               "MAINTAINER=\tmaintainer%example.org@localhost")
+       t.FinishSetUp()
+       t.DisableTracing()
+
+       G.Check(pkg)
+
+       t.CheckOutputLines(
+               "NOTE: ~/category/package/Makefile: Please only commit changes " +
+                       "that maintainer%example.org@localhost would approve.")
+}
+
 func (s *Suite) Test_Package_checkLocallyModified__directory(c *check.C) {
        t := s.Init(c)
 

Index: pkgsrc/pkgtools/pkglint/files/pkgsrc.go
diff -u pkgsrc/pkgtools/pkglint/files/pkgsrc.go:1.27 pkgsrc/pkgtools/pkglint/files/pkgsrc.go:1.28
--- pkgsrc/pkgtools/pkglint/files/pkgsrc.go:1.27        Sun May 26 14:05:57 2019
+++ pkgsrc/pkgtools/pkglint/files/pkgsrc.go     Mon Jun 10 19:51:57 2019
@@ -184,7 +184,7 @@ func (src *Pkgsrc) Latest(category strin
 //      => {"php-53", "php-56", "php-73"}
 func (src *Pkgsrc) ListVersions(category string, re regex.Pattern, repl string, errorIfEmpty bool) []string {
        if G.Testing {
-               G.Assertf(
+               assertf(
                        hasPrefix(string(re), "^") && hasSuffix(string(re), "$"),
                        "Regular expression %q must be anchored at both ends.", re)
        }
@@ -390,7 +390,7 @@ func (src *Pkgsrc) loadUntypedVars() {
        }
 
        handleFile := func(pathName string, info os.FileInfo, err error) error {
-               G.AssertNil(err, "handleFile %q", pathName)
+               assertNil(err, "handleFile %q", pathName)
                baseName := info.Name()
                if hasSuffix(baseName, ".mk") || baseName == "mk.conf" {
                        handleMkFile(filepath.ToSlash(pathName))
@@ -399,7 +399,7 @@ func (src *Pkgsrc) loadUntypedVars() {
        }
 
        err := filepath.Walk(src.File("mk"), handleFile)
-       G.AssertNil(err, "Walk error in pkgsrc infrastructure")
+       assertNil(err, "Walk error in pkgsrc infrastructure")
 }
 
 func (src *Pkgsrc) parseSuggestedUpdates(lines Lines) []SuggestedUpdate {

Index: pkgsrc/pkgtools/pkglint/files/shell.go
diff -u pkgsrc/pkgtools/pkglint/files/shell.go:1.41 pkgsrc/pkgtools/pkglint/files/shell.go:1.42
--- pkgsrc/pkgtools/pkglint/files/shell.go:1.41 Sun May 26 14:05:57 2019
+++ pkgsrc/pkgtools/pkglint/files/shell.go      Mon Jun 10 19:51:57 2019
@@ -1020,7 +1020,7 @@ func (ck *ShellLineChecker) checkInstall
                defer trace.Call0()()
        }
 
-       if ck.MkLines == nil || !matches(ck.MkLines.target, `^(?:pre|do|post)-install$`) {
+       if !matches(ck.MkLines.target, `^(?:pre|do|post)-install$`) {
                return
        }
 

Index: pkgsrc/pkgtools/pkglint/files/shell_test.go
diff -u pkgsrc/pkgtools/pkglint/files/shell_test.go:1.47 pkgsrc/pkgtools/pkglint/files/shell_test.go:1.48
--- pkgsrc/pkgtools/pkglint/files/shell_test.go:1.47    Sun May 26 14:05:57 2019
+++ pkgsrc/pkgtools/pkglint/files/shell_test.go Mon Jun 10 19:51:57 2019
@@ -418,77 +418,58 @@ func (s *Suite) Test_ShellLineChecker_Ch
 
        t.SetUpVartypes()
 
-       test := func(shellWord string, checkQuoting bool) {
-               // Provide a storage for the "used but not defined" warnings.
+       test := func(shellWord string, checkQuoting bool, diagnostics ...string) {
                // See checkVaruseUndefined and checkVarassignLeftNotUsed.
-               mklines := NewMkLines(NewLines("dummy.mk", nil))
-
-               ck := t.NewShellLineChecker(mklines, "dummy.mk", 1, "\t echo "+shellWord)
+               ck := t.NewShellLineChecker("\t echo " + shellWord)
                ck.CheckWord(shellWord, checkQuoting, RunTime)
+               t.CheckOutput(diagnostics)
        }
 
-       test("${${list}}", false)
-
        // No warning for the outer variable since it is completely indirect.
        // The inner variable ${list} must still be defined, though.
-       t.CheckOutputLines(
-               "WARN: dummy.mk:1: list is used but not defined.")
-
-       test("${SED_FILE.${id}}", false)
+       test("${${list}}", false,
+               "WARN: filename.mk:1: list is used but not defined.")
 
        // No warning for variables that are partly indirect.
        // TODO: Why not?
-       t.CheckOutputLines(
-               "WARN: dummy.mk:1: id is used but not defined.")
+       test("${SED_FILE.${id}}", false,
+               "WARN: filename.mk:1: id is used but not defined.")
 
        // TODO: Since $@ refers to ${.TARGET} and not sh.argv, there is no point in checking for quotes.
        // TODO: Having the same tests for $$@ would be much more interesting.
 
        // The unquoted $@ takes a different code path in pkglint than the quoted $@.
-       test("$@", false)
-
-       t.CheckOutputLines(
-               "WARN: dummy.mk:1: Please use \"${.TARGET}\" instead of \"$@\".")
+       test("$@", false,
+               "WARN: filename.mk:1: Please use \"${.TARGET}\" instead of \"$@\".")
 
        // When $@ appears as part of a shell token, it takes another code path in pkglint.
-       test("-$@-", false)
-
-       t.CheckOutputLines(
-               "WARN: dummy.mk:1: Please use \"${.TARGET}\" instead of \"$@\".")
+       test("-$@-", false,
+               "WARN: filename.mk:1: Please use \"${.TARGET}\" instead of \"$@\".")
 
        // The unquoted $@ takes a different code path in pkglint than the quoted $@.
-       test("\"$@\"", false)
+       test("\"$@\"", false,
+               "WARN: filename.mk:1: Please use \"${.TARGET}\" instead of \"$@\".")
 
-       t.CheckOutputLines(
-               "WARN: dummy.mk:1: Please use \"${.TARGET}\" instead of \"$@\".")
-
-       test("${COMMENT:Q}", true)
-
-       t.CheckOutputEmpty()
-
-       test("\"${DISTINFO_FILE:Q}\"", true)
-
-       t.CheckOutputLines(
-               "NOTE: dummy.mk:1: The :Q operator isn't necessary for ${DISTINFO_FILE} here.")
-
-       test("embed${DISTINFO_FILE:Q}ded", true)
-
-       t.CheckOutputLines(
-               "NOTE: dummy.mk:1: The :Q operator isn't necessary for ${DISTINFO_FILE} here.")
+       test("${COMMENT:Q}", true,
+               nil...)
 
-       test("s,\\.,,", true)
+       test("\"${DISTINFO_FILE:Q}\"", true,
+               "NOTE: filename.mk:1: The :Q operator isn't necessary for ${DISTINFO_FILE} here.")
 
-       t.CheckOutputEmpty()
+       test("embed${DISTINFO_FILE:Q}ded", true,
+               "NOTE: filename.mk:1: The :Q operator isn't necessary for ${DISTINFO_FILE} here.")
 
-       test("\"s,\\.,,\"", true)
+       test("s,\\.,,", true,
+               nil...)
 
-       t.CheckOutputEmpty()
+       test("\"s,\\.,,\"", true,
+               nil...)
 }
 
 func (s *Suite) Test_ShellLineChecker_CheckWord__dollar_without_variable(c *check.C) {
        t := s.Init(c)
 
-       ck := t.NewShellLineChecker(nil, "filename.mk", 1, "# dummy")
+       ck := t.NewShellLineChecker("# dummy")
 
        ck.CheckWord("/.*~$$//g", false, RunTime) // Typical argument to pax(1).
 
@@ -499,7 +480,7 @@ func (s *Suite) Test_ShellLineChecker_Ch
        t := s.Init(c)
 
        t.SetUpTool("find", "FIND", AtRunTime)
-       ck := t.NewShellLineChecker(nil, "filename.mk", 1, "\tfind . -exec rm -rf {} \\+")
+       ck := t.NewShellLineChecker("\tfind . -exec rm -rf {} \\+")
 
        ck.CheckShellCommandLine(ck.mkline.ShellCommand())
 
@@ -510,7 +491,7 @@ func (s *Suite) Test_ShellLineChecker_Ch
 func (s *Suite) Test_ShellLineChecker_CheckWord__squot_dollar(c *check.C) {
        t := s.Init(c)
 
-       ck := t.NewShellLineChecker(nil, "filename.mk", 1, "\t'$")
+       ck := t.NewShellLineChecker("\t'$")
 
        ck.CheckWord(ck.mkline.ShellCommand(), false, RunTime)
 
@@ -524,7 +505,7 @@ func (s *Suite) Test_ShellLineChecker_Ch
 func (s *Suite) Test_ShellLineChecker_CheckWord__dquot_dollar(c *check.C) {
        t := s.Init(c)
 
-       ck := t.NewShellLineChecker(nil, "filename.mk", 1, "\t\"$")
+       ck := t.NewShellLineChecker("\t\"$")
 
        ck.CheckWord(ck.mkline.ShellCommand(), false, RunTime)
 
@@ -536,7 +517,7 @@ func (s *Suite) Test_ShellLineChecker_Ch
 func (s *Suite) Test_ShellLineChecker_CheckWord__dollar_subshell(c *check.C) {
        t := s.Init(c)
 
-       ck := t.NewShellLineChecker(nil, "filename.mk", 1, "\t$$(echo output)")
+       ck := t.NewShellLineChecker("\t$$(echo output)")
 
        ck.CheckWord(ck.mkline.ShellCommand(), false, RunTime)
 
@@ -679,9 +660,12 @@ func (s *Suite) Test_ShellLineChecker_Ch
        t.SetUpTool("mv", "MV", AtRunTime)
        t.SetUpTool("sed", "SED", AtRunTime)
        text := "for f in *.pl; do ${SED} s,@PREFIX@,${PREFIX}, < $f > $f.tmp && ${MV} $f.tmp $f; done"
+       mklines := t.NewMkLines("Makefile",
+               MkRcsID,
+               "",
+               "\t"+text)
 
-       ck := t.NewShellLineChecker(nil, "Makefile", 3, "\t# dummy")
-       ck.mkline.Tokenize(text, true)
+       ck := NewShellLineChecker(mklines, mklines.mklines[2])
        ck.CheckShellCommandLine(text)
 
        t.CheckOutputLines(
@@ -689,7 +673,8 @@ func (s *Suite) Test_ShellLineChecker_Ch
                "WARN: Makefile:3: $f is ambiguous. Use ${f} if you mean a Make variable or $$f if you mean a shell variable.",
                "WARN: Makefile:3: $f is ambiguous. Use ${f} if you mean a Make variable or $$f if you mean a shell variable.",
                "WARN: Makefile:3: $f is ambiguous. Use ${f} if you mean a Make variable or $$f if you mean a shell variable.",
-               "NOTE: Makefile:3: Please use the SUBST framework instead of ${SED} and ${MV}.")
+               "NOTE: Makefile:3: Please use the SUBST framework instead of ${SED} and ${MV}.",
+               "WARN: Makefile:3: f is used but not defined.")
 
        ck.CheckShellCommandLine("install -c manpage.1 ${PREFIX}/man/man1/manpage.1")
 
@@ -706,10 +691,10 @@ func (s *Suite) Test_ShellLineChecker_ch
        t := s.Init(c)
 
        mklines := t.NewMkLines("filename.mk",
-               "# dummy")
+               "\t# dummy")
        mklines.target = "do-install"
 
-       ck := t.NewShellLineChecker(mklines, "filename.mk", 1, "\tdummy")
+       ck := NewShellLineChecker(mklines, mklines.mklines[0])
 
        ck.checkInstallCommand("sed")
 
@@ -728,63 +713,63 @@ func (s *Suite) Test_ShellLineChecker_Ch
        t.SetUpVartypes()
        t.SetUpTool("sed", "SED", AtRunTime)
        t.SetUpTool("mv", "MV", AtRunTime)
-       ck := t.NewShellLineChecker(nil, "Makefile", 85, "\t${RUN} ${SED} 's,#,// comment:,g' filename > filename.tmp; ${MV} filename.tmp filename")
+       ck := t.NewShellLineChecker("\t${RUN} ${SED} 's,#,// comment:,g' filename > filename.tmp; ${MV} filename.tmp filename")
 
        ck.CheckShellCommandLine(ck.mkline.ShellCommand())
 
        t.CheckOutputLines(
-               "NOTE: Makefile:85: Please use the SUBST framework instead of ${SED} and ${MV}.")
+               "NOTE: filename.mk:1: Please use the SUBST framework instead of ${SED} and ${MV}.")
 }
 
 func (s *Suite) Test_ShellLineChecker_CheckShellCommandLine__subshell(c *check.C) {
        t := s.Init(c)
 
-       ck := t.NewShellLineChecker(nil, "Makefile", 85, "\t${RUN} uname=$$(uname)")
+       ck := t.NewShellLineChecker("\t${RUN} uname=$$(uname)")
 
        ck.CheckShellCommandLine(ck.mkline.ShellCommand())
 
        t.CheckOutputLines(
-               "WARN: Makefile:85: Invoking subshells via $(...) is not portable enough.")
+               "WARN: filename.mk:1: Invoking subshells via $(...) is not portable enough.")
 }
 
 func (s *Suite) Test_ShellLineChecker_CheckShellCommandLine__install_dir(c *check.C) {
        t := s.Init(c)
 
        t.SetUpVartypes()
-       ck := t.NewShellLineChecker(nil, "Makefile", 85, "\t${RUN} ${INSTALL_DATA_DIR} ${DESTDIR}${PREFIX}/dir1 ${DESTDIR}${PREFIX}/dir2")
+       ck := t.NewShellLineChecker("\t${RUN} ${INSTALL_DATA_DIR} ${DESTDIR}${PREFIX}/dir1 ${DESTDIR}${PREFIX}/dir2")
 
        ck.CheckShellCommandLine(ck.mkline.ShellCommand())
 
        t.CheckOutputLines(
-               "NOTE: Makefile:85: You can use \"INSTALLATION_DIRS+= dir1\" instead of \"${INSTALL_DATA_DIR}\".",
-               "NOTE: Makefile:85: You can use \"INSTALLATION_DIRS+= dir2\" instead of \"${INSTALL_DATA_DIR}\".",
-               "WARN: Makefile:85: The INSTALL_*_DIR commands can only handle one directory at a time.")
+               "NOTE: filename.mk:1: You can use \"INSTALLATION_DIRS+= dir1\" instead of \"${INSTALL_DATA_DIR}\".",
+               "NOTE: filename.mk:1: You can use \"INSTALLATION_DIRS+= dir2\" instead of \"${INSTALL_DATA_DIR}\".",
+               "WARN: filename.mk:1: The INSTALL_*_DIR commands can only handle one directory at a time.")
 
        ck.CheckShellCommandLine("${INSTALL_DATA_DIR} -d -m 0755 ${DESTDIR}${PREFIX}/share/examples/gdchart")
 
        // No warning about multiple directories, since 0755 is an option, not an argument.
        t.CheckOutputLines(
-               "NOTE: Makefile:85: You can use \"INSTALLATION_DIRS+= share/examples/gdchart\" instead of \"${INSTALL_DATA_DIR}\".")
+               "NOTE: filename.mk:1: You can use \"INSTALLATION_DIRS+= share/examples/gdchart\" instead of \"${INSTALL_DATA_DIR}\".")
 
        ck.CheckShellCommandLine("${INSTALL_DATA_DIR} -d -m 0755 ${DESTDIR}${PREFIX}/dir1 ${PREFIX}/dir2")
 
        t.CheckOutputLines(
-               "NOTE: Makefile:85: You can use \"INSTALLATION_DIRS+= dir1\" instead of \"${INSTALL_DATA_DIR}\".",
-               "NOTE: Makefile:85: You can use \"INSTALLATION_DIRS+= dir2\" instead of \"${INSTALL_DATA_DIR}\".",
-               "WARN: Makefile:85: The INSTALL_*_DIR commands can only handle one directory at a time.")
+               "NOTE: filename.mk:1: You can use \"INSTALLATION_DIRS+= dir1\" instead of \"${INSTALL_DATA_DIR}\".",
+               "NOTE: filename.mk:1: You can use \"INSTALLATION_DIRS+= dir2\" instead of \"${INSTALL_DATA_DIR}\".",
+               "WARN: filename.mk:1: The INSTALL_*_DIR commands can only handle one directory at a time.")
 }
 
 func (s *Suite) Test_ShellLineChecker_CheckShellCommandLine__install_option_d(c *check.C) {
        t := s.Init(c)
 
        t.SetUpVartypes()
-       ck := t.NewShellLineChecker(nil, "Makefile", 85, "\t${RUN} ${INSTALL} -d ${DESTDIR}${PREFIX}/dir1 ${DESTDIR}${PREFIX}/dir2")
+       ck := t.NewShellLineChecker("\t${RUN} ${INSTALL} -d ${DESTDIR}${PREFIX}/dir1 ${DESTDIR}${PREFIX}/dir2")
 
        ck.CheckShellCommandLine(ck.mkline.ShellCommand())
 
        t.CheckOutputLines(
-               "NOTE: Makefile:85: You can use \"INSTALLATION_DIRS+= dir1\" instead of \"${INSTALL} -d\".",
-               "NOTE: Makefile:85: You can use \"INSTALLATION_DIRS+= dir2\" instead of \"${INSTALL} -d\".")
+               "NOTE: filename.mk:1: You can use \"INSTALLATION_DIRS+= dir1\" instead of \"${INSTALL} -d\".",
+               "NOTE: filename.mk:1: You can use \"INSTALLATION_DIRS+= dir2\" instead of \"${INSTALL} -d\".")
 }
 
 func (s *Suite) Test_ShellLineChecker__shell_comment_with_line_continuation(c *check.C) {
@@ -808,45 +793,59 @@ func (s *Suite) Test_ShellLineChecker_ch
        t.SetUpVartypes()
        t.SetUpTool("grep", "GREP", AtRunTime)
 
-       test := func(lineno int, input string) {
-               ck := t.NewShellLineChecker(nil, "module.mk", lineno, "\t"+input)
+       test := func(input string, diagnostics ...string) {
+               mklines := t.NewMkLines("module.mk",
+                       "\t"+input)
+               ck := NewShellLineChecker(mklines, mklines.mklines[0])
 
                ck.checkWordQuoting(ck.mkline.ShellCommand(), true, RunTime)
+
+               t.CheckOutput(diagnostics)
        }
 
-       test(101, "socklen=`${GREP} 'expr' ${WRKSRC}/config.h`")
+       test(
+               "socklen=`${GREP} 'expr' ${WRKSRC}/config.h`",
+               nil...)
 
-       test(102, "s,$$from,$$to,")
+       test(
+               "s,$$from,$$to,",
+               "WARN: module.mk:1: Unquoted shell variable \"from\".",
+               "WARN: module.mk:1: Unquoted shell variable \"to\".")
 
        // This variable is typically defined by GNU Configure,
        // which cannot handle directories with special characters.
        // Therefore using it unquoted is considered safe.
-       test(103, "${PREFIX}/$$bindir/program")
+       test(
+               "${PREFIX}/$$bindir/program",
+               nil...)
 
-       test(104, "$$@")
+       test(
+               "$$@",
+               "WARN: module.mk:1: The $@ shell variable should only be used in double quotes.")
 
        // TODO: Add separate tests for "set +e" and "set -e".
-       test(105, "$$?")
-
-       test(106, "$$(cat /bin/true)")
+       test(
+               "$$?",
+               "WARN: module.mk:1: The $? shell variable is often not available in \"set -e\" mode.")
+
+       test(
+               "$$(cat /bin/true)",
+               "WARN: module.mk:1: Invoking subshells via $(...) is not portable enough.")
 
-       test(107, "\"$$\"")
-
-       test(108, "$$$$")
+       test(
+               "\"$$\"",
+               nil...)
 
-       t.CheckOutputLines(
-               "WARN: module.mk:102: Unquoted shell variable \"from\".",
-               "WARN: module.mk:102: Unquoted shell variable \"to\".",
-               "WARN: module.mk:104: The $@ shell variable should only be used in double quotes.",
-               "WARN: module.mk:105: The $? shell variable is often not available in \"set -e\" mode.",
-               "WARN: module.mk:106: Invoking subshells via $(...) is not portable enough.")
+       test(
+               "$$$$",
+               nil...)
 }
 
 func (s *Suite) Test_ShellLineChecker_unescapeBackticks(c *check.C) {
        t := s.Init(c)
 
-       test := func(lineno int, input string, expectedOutput string, expectedRest string) {
-               ck := t.NewShellLineChecker(nil, "dummy.mk", lineno, "# dummy")
+       test := func(input string, expectedOutput string, expectedRest string, diagnostics ...string) {
+               ck := t.NewShellLineChecker("# dummy")
 
                tok := NewShTokenizer(nil, input, false)
                atoms := tok.ShAtoms()
@@ -869,56 +868,52 @@ func (s *Suite) Test_ShellLineChecker_un
 
                c.Check(backtCommand, equals, expectedOutput)
                c.Check(actualRest.String(), equals, expectedRest)
+               t.CheckOutput(diagnostics)
        }
 
-       // The 1xx test cases are in shqPlain mode.
-
-       test(100, "`echo`end", "echo", "end")
-       test(101, "`echo $$var`end", "echo $$var", "end")
-       test(102, "``end", "", "end")
-       test(103, "`echo \"hello\"`end", "echo \"hello\"", "end")
-       test(104, "`echo 'hello'`end", "echo 'hello'", "end")
-       test(105, "`echo '\\\\\\\\'`end", "echo '\\\\'", "end")
+       test("`echo`end", "echo", "end")
+       test("`echo $$var`end", "echo $$var", "end")
+       test("``end", "", "end")
+       test("`echo \"hello\"`end", "echo \"hello\"", "end")
+       test("`echo 'hello'`end", "echo 'hello'", "end")
+       test("`echo '\\\\\\\\'`end", "echo '\\\\'", "end")
 
        // Only the characters " $ ` \ are unescaped. All others stay the same.
-       test(120, "`echo '\\n'`end", "echo '\\n'", "end")
-       test(121, "\tsocklen=`${GREP} 'expr' ${WRKSRC}/config.h`", "${GREP} 'expr' ${WRKSRC}/config.h", "")
-
-       // TODO: Add more details regarding which backslash is meant.
-       t.CheckOutputLines(
-               "WARN: dummy.mk:120: Backslashes should be doubled inside backticks.")
+       test("`echo '\\n'`end", "echo '\\n'", "end",
+               // TODO: Add more details regarding which backslash is meant.
+               "WARN: filename.mk:1: Backslashes should be doubled inside backticks.")
+       test("\tsocklen=`${GREP} 'expr' ${WRKSRC}/config.h`", "${GREP} 'expr' ${WRKSRC}/config.h", "")
 
        // The 2xx test cases are in shqDquot mode.
 
-       test(200, "\"`echo`\"", "echo", "\"")
-       test(201, "\"`echo \"\"`\"", "echo \"\"", "\"")
-
-       t.CheckOutputLines(
-               "WARN: dummy.mk:201: Double quotes inside backticks inside double quotes are error prone.")
+       test("\"`echo`\"", "echo", "\"")
+       test("\"`echo \"\"`\"", "echo \"\"", "\"",
+               "WARN: filename.mk:1: Double quotes inside backticks inside double quotes are error prone.")
 
        // varname="`echo \"one   two\" "\ " "three"`"
-       test(202,
+       test(
                "varname=\"`echo \\\"one   two\\\" \"\\ \" \"three\"`\"",
                "echo \"one   two\" \"\\ \" \"three\"",
-               "\"")
+               "\"",
 
-       // TODO: Add more details regarding which backslash and backtick is meant.
-       t.CheckOutputLines(
-               "WARN: dummy.mk:202: Backslashes should be doubled inside backticks.",
-               "WARN: dummy.mk:202: Double quotes inside backticks inside double quotes are error prone.",
-               "WARN: dummy.mk:202: Double quotes inside backticks inside double quotes are error prone.")
+               // TODO: Add more details regarding which backslash and backtick is meant.
+               "WARN: filename.mk:1: Backslashes should be doubled inside backticks.",
+               "WARN: filename.mk:1: Double quotes inside backticks inside double quotes are error prone.",
+               "WARN: filename.mk:1: Double quotes inside backticks inside double quotes are error prone.")
 }
 
 func (s *Suite) Test_ShellLineChecker_unescapeBackticks__dquotBacktDquot(c *check.C) {
        t := s.Init(c)
 
        t.SetUpTool("echo", "", AtRunTime)
-       mkline := t.NewMkLine("dummy.mk", 13, "\t var=\"`echo \"\"`\"")
+       mklines := t.NewMkLines("dummy.mk",
+               MkRcsID,
+               "\t var=\"`echo \"\"`\"")
 
-       MkLineChecker{nil, mkline}.Check()
+       mklines.Check()
 
        t.CheckOutputLines(
-               "WARN: dummy.mk:13: Double quotes inside backticks inside double quotes are error prone.")
+               "WARN: dummy.mk:2: Double quotes inside backticks inside double quotes are error prone.")
 }
 
 func (s *Suite) Test_ShellLineChecker__variable_outside_quotes(c *check.C) {
@@ -1034,6 +1029,23 @@ func (s *Suite) Test_ShellLineChecker_ch
        t.CheckOutputEmpty()
 }
 
+func (s *Suite) Test_ShellLineChecker_checkHiddenAndSuppress__no_tracing(c *check.C) {
+       t := s.Init(c)
+
+       t.SetUpTool("ls", "LS", AtRunTime)
+       mklines := t.NewMkLines("Makefile",
+               MkRcsID,
+               "",
+               "pre-configure:",
+               "\t@ls -l")
+       t.DisableTracing()
+
+       mklines.Check()
+
+       t.CheckOutputLines(
+               "WARN: Makefile:4: The shell command \"ls\" should not be hidden.")
+}
+
 func (s *Suite) Test_SimpleCommandChecker_handleForbiddenCommand(c *check.C) {
        t := s.Init(c)
 
@@ -1099,12 +1111,14 @@ func (s *Suite) Test_SimpleCommandChecke
 func (s *Suite) Test_SimpleCommandChecker_handleComment(c *check.C) {
        t := s.Init(c)
 
-       mkline := t.NewMkLine("file.mk", 3, "\t# comment; continuation")
+       mklines := t.NewMkLines("file.mk",
+               MkRcsID,
+               "\t# comment; continuation")
 
-       MkLineChecker{nil, mkline}.Check()
+       mklines.Check()
 
        t.CheckOutputLines(
-               "WARN: file.mk:3: A shell comment should not contain semicolons.")
+               "WARN: file.mk:2: A shell comment should not contain semicolons.")
 }
 
 // This test ensures that the command line options to INSTALL_*_DIR are properly
@@ -1279,6 +1293,23 @@ func (s *Suite) Test_ShellProgramChecker
                        "(after \"touch 1\") to separate commands.")
 }
 
+func (s *Suite) Test_ShellProgramChecker_checkSetE__no_tracing(c *check.C) {
+       t := s.Init(c)
+
+       t.SetUpTool("touch", "", AtRunTime)
+       mklines := t.NewMkLines("Makefile",
+               MkRcsID,
+               "pre-configure:",
+               "\ttouch 1; touch 2")
+       t.DisableTracing()
+
+       mklines.Check()
+
+       t.CheckOutputLines(
+               "WARN: Makefile:3: Please switch to \"set -e\" mode before using a semicolon " +
+                       "(after \"touch 1\") to separate commands.")
+}
+
 func (s *Suite) Test_ShellProgramChecker_canFail(c *check.C) {
        t := s.Init(c)
 

Index: pkgsrc/pkgtools/pkglint/files/shtokenizer.go
diff -u pkgsrc/pkgtools/pkglint/files/shtokenizer.go:1.16 pkgsrc/pkgtools/pkglint/files/shtokenizer.go:1.17
--- pkgsrc/pkgtools/pkglint/files/shtokenizer.go:1.16   Sat Apr 20 17:43:24 2019
+++ pkgsrc/pkgtools/pkglint/files/shtokenizer.go        Mon Jun 10 19:51:57 2019
@@ -460,7 +460,7 @@ func (p *ShTokenizer) ShToken() *ShToken
                return nil
        }
 
-       G.Assertf(len(atoms) > 0, "ShTokenizer.ShToken")
+       assertf(len(atoms) > 0, "ShTokenizer.ShToken")
        return NewShToken(lexer.Since(initialMark), atoms...)
 }
 
Index: pkgsrc/pkgtools/pkglint/files/tools_test.go
diff -u pkgsrc/pkgtools/pkglint/files/tools_test.go:1.16 pkgsrc/pkgtools/pkglint/files/tools_test.go:1.17
--- pkgsrc/pkgtools/pkglint/files/tools_test.go:1.16    Wed May 22 16:07:16 2019
+++ pkgsrc/pkgtools/pkglint/files/tools_test.go Mon Jun 10 19:51:57 2019
@@ -34,7 +34,7 @@ func (s *Suite) Test_Tool_UsableAtRunTim
 // which confused an earlier version of pkglint into
 // thinking that the below definition was about a tool
 // called "NetBSD".
-func (s *Suite) Test_Tools_ParseToolLine(c *check.C) {
+func (s *Suite) Test_Tools_ParseToolLine__opsys(c *check.C) {
        t := s.Init(c)
 
        t.SetUpTool("tool1", "", Nowhere)
@@ -50,15 +50,53 @@ func (s *Suite) Test_Tools_ParseToolLine
        t.CheckOutputEmpty()
 }
 
+func (s *Suite) Test_Tools_ParseToolLine__invalid_tool_name(c *check.C) {
+       t := s.Init(c)
+
+       t.SetUpVartypes()
+       mklines := t.NewMkLines("Makefile",
+               MkRcsID,
+               "",
+               ".for t in abc ${UNDEFINED}",
+               "TOOLS_CREATE+=\t\t${t}",
+               "_TOOLS_VARNAME.${t}=\tVARNAME",
+               "TOOLS_PATH.${t}=\t/bin/${t}",
+               "TOOLS_ALIASES.${t}=\t${t} ${u} ${t}-arm64",
+               "TOOLS_ALIASES.tool=\t${t} ${u} ${t}-arm64",
+               "_TOOLS.${t}=\t${t}",
+               ".endfor")
+
+       mklines.collectDefinedVariables()
+       t.Check(mklines.Tools.byName, check.HasLen, 1)
+       t.Check(mklines.Tools.ByName("tool").String(), equals, "tool:::Nowhere:abc")
+
+       t.CheckOutputEmpty()
+}
+
+func (s *Suite) Test_Tools_parseUseTools(c *check.C) {
+       t := s.Init(c)
+
+       t.SetUpPkgsrc()
+       t.CreateFileLines("mk/triple-tool.mk",
+               MkRcsID,
+               "",
+               "USE_TOOLS+=\tunknown unknown unknown")
+       t.FinishSetUp()
+
+       t.Check(G.Pkgsrc.Tools.ByName("unknown"), check.IsNil)
+
+       t.CheckOutputEmpty()
+}
+
 func (s *Suite) Test_Tools_Define__invalid_tool_name(c *check.C) {
        t := s.Init(c)
 
        mkline := t.NewMkLine("dummy.mk", 123, "DUMMY=\tvalue")
        reg := NewTools()
 
-       reg.Define("tool_name", "", mkline)
-       reg.Define("tool:dependency", "", mkline)
-       reg.Define("tool:build", "", mkline)
+       t.Check(reg.Define("tool_name", "", mkline), check.IsNil)
+       t.Check(reg.Define("tool:dependency", "", mkline), check.IsNil)
+       t.Check(reg.Define("tool:build", "", mkline), check.IsNil)
 
        // As of October 2018, the underscore is not used in any tool name.
        // If there should ever be such a case, just use a different character for testing.
@@ -66,6 +104,8 @@ func (s *Suite) Test_Tools_Define__inval
                "ERROR: dummy.mk:123: Invalid tool name \"tool_name\".",
                "ERROR: dummy.mk:123: Invalid tool name \"tool:dependency\".",
                "ERROR: dummy.mk:123: Invalid tool name \"tool:build\".")
+
+       t.Check(reg.byName, check.HasLen, 0)
 }
 
 func (s *Suite) Test_Tools_Trace__coverage(c *check.C) {
@@ -504,6 +544,19 @@ func (s *Suite) Test_Tools_Fallback__too
        c.Check(local2.ByVarname("SED").String(), equals, "sed:SED::AfterPrefsMk")
 }
 
+func (s *Suite) Test_Tools_Fallback__called_twice(c *check.C) {
+       t := s.Init(c)
+
+       tools := NewTools()
+       fallback := NewTools()
+
+       tools.Fallback(fallback)
+
+       t.ExpectPanic(
+               func() { tools.Fallback(fallback) },
+               "Pkglint internal error: Tools.Fallback must only be called once.")
+}
+
 func (s *Suite) Test_Tools__aliases(c *check.C) {
        t := s.Init(c)
 
@@ -624,3 +677,19 @@ func (s *Suite) Test_Tools__autoconf213(
        // No warning, since autoconf213 defines autoconf implicitly.
        t.CheckOutputEmpty()
 }
+
+func (s *Suite) Test_Tools_IsValidToolName(c *check.C) {
+       t := s.Init(c)
+
+       t.SetUpVartypes()
+       t.SetUpTool("[", "", AtRunTime)
+       t.SetUpTool("echo -n", "ECHO_N", AtRunTime)
+       mklines := t.NewMkLines("filename.mk",
+               MkRcsID,
+               "",
+               "USE_TOOLS+=\t[")
+
+       mklines.Check()
+
+       t.CheckOutputEmpty()
+}
Index: pkgsrc/pkgtools/pkglint/files/toplevel_test.go
diff -u pkgsrc/pkgtools/pkglint/files/toplevel_test.go:1.16 pkgsrc/pkgtools/pkglint/files/toplevel_test.go:1.17
--- pkgsrc/pkgtools/pkglint/files/toplevel_test.go:1.16 Sun Jan 13 19:55:53 2019
+++ pkgsrc/pkgtools/pkglint/files/toplevel_test.go      Mon Jun 10 19:51:57 2019
@@ -14,7 +14,8 @@ func (s *Suite) Test_CheckdirToplevel(c 
                "SUBDIR+=\tccc",
                "#SUBDIR+=\tignoreme",
                "SUBDIR+=\tnonexisting", // This doesn't happen in practice, therefore no warning.
-               "SUBDIR+=\tbbb")
+               "SUBDIR+=\tbbb",
+               "SUBDIR+=\t${SITE_SPECIFIC_PKGS}")
        t.CreateFileLines("archivers/Makefile")
        t.CreateFileLines("bbb/Makefile")
        t.CreateFileLines("ccc/Makefile")
@@ -33,3 +34,102 @@ func (s *Suite) Test_CheckdirToplevel(c 
                // Ideally it would be at the same place as the other warning from Makefile:3.
                "NOTE: ~/Makefile:3: This variable value should be aligned with tabs, not spaces, to column 17.")
 }
+
+func (s *Suite) Test_Toplevel_checkSubdir__sorting_x11(c *check.C) {
+       t := s.Init(c)
+
+       t.CreateFileLines("Makefile",
+               MkRcsID,
+               "",
+               "SUBDIR+=\tx11",
+               "SUBDIR+=\tsysutils",
+               "SUBDIR+=\tarchivers")
+       t.CreateFileLines("archivers/Makefile")
+       t.CreateFileLines("sysutils/Makefile")
+       t.CreateFileLines("x11/Makefile")
+       t.SetUpVartypes()
+
+       CheckdirToplevel(t.File("."))
+
+       t.CheckOutputLines(
+               "WARN: ~/Makefile:4: sysutils should come before x11.",
+               "WARN: ~/Makefile:5: archivers should come before sysutils.")
+}
+
+func (s *Suite) Test_Toplevel_checkSubdir__commented_without_reason(c *check.C) {
+       t := s.Init(c)
+
+       t.CreateFileLines("Makefile",
+               MkRcsID,
+               "",
+               "#SUBDIR+=\taaa",
+               "#SUBDIR+=\tbbb\t#",
+               "#SUBDIR+=\tccc\t# reason")
+       t.CreateFileLines("aaa/Makefile")
+       t.CreateFileLines("bbb/Makefile")
+       t.CreateFileLines("ccc/Makefile")
+       t.SetUpVartypes()
+
+       CheckdirToplevel(t.File("."))
+
+       t.CheckOutputLines(
+               "WARN: ~/Makefile:3: \"aaa\" commented out without giving a reason.",
+               "WARN: ~/Makefile:4: \"bbb\" commented out without giving a reason.")
+}
+
+func (s *Suite) Test_CheckdirToplevel__recursive(c *check.C) {
+       t := s.Init(c)
+
+       t.CreateFileLines("mk/misc/category.mk",
+               MkRcsID)
+       t.SetUpPackage("category/package",
+               "UNKNOWN=\tvalue")
+       t.CreateFileLines("Makefile",
+               MkRcsID,
+               "",
+               "SUBDIR+=\tcategory")
+       t.CreateFileLines("category/Makefile",
+               MkRcsID,
+               "",
+               "COMMENT=\tThe category",
+               "",
+               "SUBDIR+=\tpackage",
+               "",
+               ".include \"../mk/misc/category.mk\"")
+
+       t.Main("-Wall", "-r", ".")
+
+       t.CheckOutputLines(
+               "WARN: ~/category/package/Makefile:20: UNKNOWN is defined but not used.",
+               "0 errors and 1 warning found.",
+               "(Run \"pkglint -e\" to show explanations.)")
+}
+
+func (s *Suite) Test_CheckdirToplevel__recursive_inter_package(c *check.C) {
+       t := s.Init(c)
+
+       t.CreateFileLines("mk/misc/category.mk",
+               MkRcsID)
+       t.SetUpPackage("category/package",
+               "UNKNOWN=\tvalue")
+       t.CreateFileLines("Makefile",
+               MkRcsID,
+               "",
+               "SUBDIR+=\tcategory")
+       t.CreateFileLines("category/Makefile",
+               MkRcsID,
+               "",
+               "COMMENT=\tThe category",
+               "",
+               "SUBDIR+=\tpackage",
+               "",
+               ".include \"../mk/misc/category.mk\"")
+
+       t.Main("-Wall", "-Call", "-r", ".")
+
+       t.CheckOutputLines(
+               "WARN: ~/category/package/Makefile:20: UNKNOWN is defined but not used.",
+               "WARN: ~/licenses/gnu-gpl-v2: This license seems to be unused.",
+               "0 errors and 2 warnings found.",
+               "(Run \"pkglint -e\" to show explanations.)")
+}

Index: pkgsrc/pkgtools/pkglint/files/shtypes.go
diff -u pkgsrc/pkgtools/pkglint/files/shtypes.go:1.15 pkgsrc/pkgtools/pkglint/files/shtypes.go:1.16
--- pkgsrc/pkgtools/pkglint/files/shtypes.go:1.15       Sat Apr 20 17:43:25 2019
+++ pkgsrc/pkgtools/pkglint/files/shtypes.go    Mon Jun 10 19:51:57 2019
@@ -41,11 +41,15 @@ type ShAtom struct {
        Type    ShAtomType
        MkText  string
        Quoting ShQuoting // The quoting state at the end of the token
-       data    interface{}
+
+       //  * usually nil
+       //  * for shtVaruse a *MkVarUse
+       //  * for shtShVarUse a string
+       data interface{}
 }
 
 func (atom *ShAtom) String() string {
-       if atom.Type == shtText && atom.Quoting == shqPlain && atom.data == nil {
+       if atom.Type == shtText && atom.Quoting == shqPlain {
                return sprintf("%q", atom.MkText)
        }
        if atom.Type == shtVaruse {
Index: pkgsrc/pkgtools/pkglint/files/tools.go
diff -u pkgsrc/pkgtools/pkglint/files/tools.go:1.15 pkgsrc/pkgtools/pkglint/files/tools.go:1.16
--- pkgsrc/pkgtools/pkglint/files/tools.go:1.15 Sun May 26 14:05:57 2019
+++ pkgsrc/pkgtools/pkglint/files/tools.go      Mon Jun 10 19:51:57 2019
@@ -142,6 +142,7 @@ func (tr *Tools) Define(name, varname st
 
        if !tr.IsValidToolName(name) {
                mkline.Errorf("Invalid tool name %q.", name)
+               return nil
        }
 
        validity := tr.validity(mkline.Basename, false)
@@ -149,6 +150,8 @@ func (tr *Tools) Define(name, varname st
 }
 
 func (tr *Tools) def(name, varname string, mustUseVarForm bool, validity Validity, aliases []string) *Tool {
+       assertf(tr.IsValidToolName(name), "Invalid tool name %q", name)
+
        fresh := Tool{name, varname, mustUseVarForm, validity, aliases}
 
        tool := tr.byName[name]
@@ -248,6 +251,10 @@ func (tr *Tools) ParseToolLine(mklines M
                        }
 
                case "TOOLS_ALIASES.*":
+                       if containsVarRef(varparam) {
+                               break
+                       }
+
                        tool := tr.def(varparam, "", false, Nowhere, nil)
 
                        for _, alias := range mkline.ValueFields(value) {
@@ -342,7 +349,7 @@ func (tr *Tools) validity(basename strin
                return AfterPrefsMk
        case basename == "Makefile" && !tr.SeenPrefs:
                return AfterPrefsMk
-       case useTools, basename == "bsd.pkg.mk":
+       case useTools:
                return AtRunTime
        default:
                return Nowhere
@@ -384,7 +391,7 @@ func (tr *Tools) Usable(tool *Tool, time
 }
 
 func (tr *Tools) Fallback(other *Tools) {
-       G.Assertf(tr.fallback == nil, "Tools.Fallback must only be called once.")
+       assertf(tr.fallback == nil, "Tools.Fallback must only be called once.")
        tr.fallback = other
 }
 
Index: pkgsrc/pkgtools/pkglint/files/vardefs_test.go
diff -u pkgsrc/pkgtools/pkglint/files/vardefs_test.go:1.15 pkgsrc/pkgtools/pkglint/files/vardefs_test.go:1.16
--- pkgsrc/pkgtools/pkglint/files/vardefs_test.go:1.15  Sun May 26 14:05:57 2019
+++ pkgsrc/pkgtools/pkglint/files/vardefs_test.go       Mon Jun 10 19:51:57 2019
@@ -12,7 +12,7 @@ func (s *Suite) Test_VarTypeRegistry_Ini
        c.Check(src.vartypes.Canon("USE_BUILTIN.*").basicType.name, equals, "YesNoIndirectly")
 }
 
-func (s *Suite) Test_VarTypeRegistry_Init__enumFrom(c *check.C) {
+func (s *Suite) Test_VarTypeRegistry_enumFrom(c *check.C) {
        t := s.Init(c)
 
        t.CreateFileLines("editors/emacs/modules.mk",
@@ -59,7 +59,25 @@ func (s *Suite) Test_VarTypeRegistry_Ini
        test("PKGSRC_COMPILER", "enum: ccache distcc f2c g95 gcc ido mipspro-ucode sunpro  (list, user-settable)")
 }
 
-func (s *Suite) Test_VarTypeRegistry_Init__enumFromDirs(c *check.C) {
+func (s *Suite) Test_VarTypeRegistry_enumFrom__no_tracing(c *check.C) {
+       t := s.Init(c)
+
+       t.CreateFileLines("mk/existing.mk",
+               MkRcsID,
+               "VAR=\tfirst second")
+       reg := NewVarTypeRegistry()
+       t.DisableTracing()
+
+       existingType := reg.enumFrom(&G.Pkgsrc, "mk/existing.mk", "defval", "VAR")
+       noAssignmentsType := reg.enumFrom(&G.Pkgsrc, "mk/existing.mk", "defval", "OTHER_VAR")
+       nonexistentType := reg.enumFrom(&G.Pkgsrc, "mk/nonexistent.mk", "defval", "VAR")
+
+       t.Check(existingType.AllowedEnums(), equals, "first second")
+       t.Check(noAssignmentsType.AllowedEnums(), equals, "defval")
+       t.Check(nonexistentType.AllowedEnums(), equals, "defval")
+}
+
+func (s *Suite) Test_VarTypeRegistry_enumFromDirs(c *check.C) {
        t := s.Init(c)
 
        // To make the test useful, these directories must differ from the
@@ -77,7 +95,7 @@ func (s *Suite) Test_VarTypeRegistry_Ini
        test("PYPKGPREFIX", "enum: py28 py33  (system-provided)")
 }
 
-func (s *Suite) Test_VarTypeRegistry_Init__enumFromFiles(c *check.C) {
+func (s *Suite) Test_VarTypeRegistry_enumFromFiles(c *check.C) {
        t := s.Init(c)
 
        t.CreateFileLines("mk/platform/NetBSD.mk")

Index: pkgsrc/pkgtools/pkglint/files/substcontext.go
diff -u pkgsrc/pkgtools/pkglint/files/substcontext.go:1.24 pkgsrc/pkgtools/pkglint/files/substcontext.go:1.25
--- pkgsrc/pkgtools/pkglint/files/substcontext.go:1.24  Sun May 26 14:05:57 2019
+++ pkgsrc/pkgtools/pkglint/files/substcontext.go       Mon Jun 10 19:51:57 2019
@@ -233,14 +233,14 @@ func (ctx *SubstContext) Finish(mkline M
        *ctx = *NewSubstContext()
 }
 
-func (ctx *SubstContext) dupString(mkline MkLine, pstr *string, varname, value string) {
+func (*SubstContext) dupString(mkline MkLine, pstr *string, varname, value string) {
        if *pstr != "" {
                mkline.Warnf("Duplicate definition of %q.", varname)
        }
        *pstr = value
 }
 
-func (ctx *SubstContext) dupBool(mkline MkLine, flag *bool, varname string, op MkOperator, value string) {
+func (*SubstContext) dupBool(mkline MkLine, flag *bool, varname string, op MkOperator, value string) {
        if *flag && op != opAssignAppend {
                mkline.Warnf("All but the first %q lines should use the \"+=\" operator.", varname)
        }
@@ -251,40 +251,8 @@ func (ctx *SubstContext) suggestSubstVar
 
        tokens, _ := splitIntoShellTokens(mkline.Line, mkline.Value())
        for _, token := range tokens {
-
-               parser := NewMkParser(nil, mkline.UnquoteShell(token), false)
-               lexer := parser.lexer
-               if !lexer.SkipByte('s') {
-                       continue
-               }
-
-               separator := lexer.NextByteSet(textproc.XPrint) // Really any character works
-               if separator == -1 {
-                       continue
-               }
-
-               if !lexer.SkipByte('@') {
-                       continue
-               }
-
-               varname := parser.Varname()
-               if !lexer.SkipByte('@') || !lexer.SkipByte(byte(separator)) {
-                       continue
-               }
-
-               varuse := parser.VarUse()
-               if varuse == nil || varuse.varname != varname {
-                       continue
-               }
-
-               switch varuse.Mod() {
-               case "", ":Q":
-                       break
-               default:
-                       continue
-               }
-
-               if !lexer.SkipByte(byte(separator)) {
+               varname := ctx.extractVarname(mkline.UnquoteShell(token))
+               if varname == "" {
                        continue
                }
 
@@ -308,3 +276,45 @@ func (ctx *SubstContext) suggestSubstVar
                ctx.curr.seenVars = true
        }
 }
+
+// extractVarname extracts the variable name from a sed command of the form
+// s,@VARNAME@,${VARNAME}, and some related variants thereof.
+func (*SubstContext) extractVarname(token string) string {
+       parser := NewMkParser(nil, token, false)
+       lexer := parser.lexer
+       if !lexer.SkipByte('s') {
+               return ""
+       }
+
+       separator := lexer.NextByteSet(textproc.XPrint) // Really any character works
+       if separator == -1 {
+               return ""
+       }
+
+       if !lexer.SkipByte('@') {
+               return ""
+       }
+
+       varname := parser.Varname()
+       if !lexer.SkipByte('@') || !lexer.SkipByte(byte(separator)) {
+               return ""
+       }
+
+       varuse := parser.VarUse()
+       if varuse == nil || varuse.varname != varname {
+               return ""
+       }
+
+       switch varuse.Mod() {
+       case "", ":Q":
+               break
+       default:
+               return ""
+       }
+
+       if !lexer.SkipByte(byte(separator)) {
+               return ""
+       }
+
+       return varname
+}

Index: pkgsrc/pkgtools/pkglint/files/substcontext_test.go
diff -u pkgsrc/pkgtools/pkglint/files/substcontext_test.go:1.25 pkgsrc/pkgtools/pkglint/files/substcontext_test.go:1.26
--- pkgsrc/pkgtools/pkglint/files/substcontext_test.go:1.25     Sun May 26 14:05:57 2019
+++ pkgsrc/pkgtools/pkglint/files/substcontext_test.go  Mon Jun 10 19:51:57 2019
@@ -553,7 +553,8 @@ func (s *Suite) Test_SubstContext_sugges
                "SUBST_SED.test+=\t-e s,@SH@,'\"'${SH:Q}'\"',g",  // Cannot be replaced since the double quotes are added.
                "SUBST_SED.test+=\t-e s",                         // Just to get 100% code coverage.
                "SUBST_SED.test+=\t-e s,@SH@,${SH:Q}",            // Just to get 100% code coverage.
-               "SUBST_SED.test+=\t-e s,@SH@,${SH:Q}, # comment", // This is not fixed automatically.
+               "SUBST_SED.test+=\t-e s,@SH@,${SH:Q}, # comment", // Just a note; not fixed because of the comment.
+               "SUBST_SED.test+=\t-n s,@SH@,${SH:Q},",           // Just a note; not fixed because of the -n.
                "# end")
 
        mklines.Check()
@@ -579,6 +580,10 @@ func (s *Suite) Test_SubstContext_sugges
                "NOTE: subst.mk:13: The substitution command \"s,'@SH@','${SH}',\" "+
                        "can be replaced with \"SUBST_VARS.test+= SH\".",
                "NOTE: subst.mk:18: The substitution command \"s,@SH@,${SH:Q},\" "+
+                       "can be replaced with \"SUBST_VARS.test+= SH\".",
+               "NOTE: subst.mk:19: Please always use \"-e\" in sed commands, "+
+                       "even if there is only one substitution.",
+               "NOTE: subst.mk:19: The substitution command \"s,@SH@,${SH:Q},\" "+
                        "can be replaced with \"SUBST_VARS.test+= SH\".")
 
        t.SetUpCommandLine("--show-autofix")
@@ -689,7 +694,7 @@ func (s *Suite) Test_SubstContext_sugges
                        "with \"SUBST_VARS.pfx+=\\tPREFIX\".")
 
        t.CheckFileLinesDetab("subst.mk",
-               "# $NetBSD: substcontext_test.go,v 1.25 2019/05/26 14:05:57 rillig Exp $",
+               "# $NetBSD: substcontext_test.go,v 1.26 2019/06/10 19:51:57 rillig Exp $",
                "",
                "SUBST_CLASSES+=         pfx",
                "SUBST_STAGE.pfx=        pre-configure",
@@ -728,7 +733,7 @@ func (s *Suite) Test_SubstContext_sugges
                        "with \"SUBST_VARS.pfx=\\t\\tPREFIX\".")
 
        t.CheckFileLinesDetab("subst.mk",
-               "# $NetBSD: substcontext_test.go,v 1.25 2019/05/26 14:05:57 rillig Exp $",
+               "# $NetBSD: substcontext_test.go,v 1.26 2019/06/10 19:51:57 rillig Exp $",
                "",
                "SUBST_CLASSES+=         pfx",
                "SUBST_STAGE.pfx=        pre-configure",
@@ -762,7 +767,7 @@ func (s *Suite) Test_SubstContext_sugges
                        "with \"SUBST_VARS.id=\\tPREFIX\".")
 
        t.CheckFileLinesDetab("subst.mk",
-               "# $NetBSD: substcontext_test.go,v 1.25 2019/05/26 14:05:57 rillig Exp $",
+               "# $NetBSD: substcontext_test.go,v 1.26 2019/06/10 19:51:57 rillig Exp $",
                "",
                "SUBST_CLASSES+= id",
                "SUBST_STAGE.id= pre-configure",
@@ -796,7 +801,7 @@ func (s *Suite) Test_SubstContext_sugges
                        "with \"SUBST_VARS.fix-paths=\\t\\tPREFIX\".")
 
        t.CheckFileLinesDetab("subst.mk",
-               "# $NetBSD: substcontext_test.go,v 1.25 2019/05/26 14:05:57 rillig Exp $",
+               "# $NetBSD: substcontext_test.go,v 1.26 2019/06/10 19:51:57 rillig Exp $",
                "",
                "SUBST_CLASSES+=                 fix-paths",
                "SUBST_STAGE.fix-paths=          pre-configure",
@@ -805,6 +810,53 @@ func (s *Suite) Test_SubstContext_sugges
                "SUBST_VARS.fix-paths=           PREFIX")
 }
 
+func (s *Suite) Test_SubstContext_extractVarname(c *check.C) {
+       t := s.Init(c)
+
+       test := func(input, expected string) {
+               t.Check((*SubstContext).extractVarname(nil, input), equals, expected)
+       }
+
+       // A simple variable name.
+       test("s,@VAR@,${VAR},", "VAR")
+
+       // A parameterized variable name.
+       test("s,@VAR.param@,${VAR.param},", "VAR.param")
+
+       // Only substitution commands can be replaced with SUBST_VARS.
+       test("/pattern/d", "")
+
+       // An incomplete substitution command.
+       test("s", "")
+
+       // Wrong placeholder character, only @ works.
+       test("s,!VAR!,${VAR},", "")
+
+       // The placeholder must have exactly 1 @ on each side.
+       test("s,@@VAR@@,${VAR},", "")
+
+       // Malformed because the comma is the separator.
+       test("s,@VAR,VAR@,${VAR},", "")
+
+       // The replacement pattern is not a simple variable name enclosed in @.
+       test("s,@VAR!VAR@,${VAR},", "")
+
+       // The replacement may only contain the :Q modifier.
+       test("s,@VAR@,${VAR:Mpattern},", "")
+
+       // The :Q modifier is allowed in the replacement.
+       test("s,@VAR@,${VAR:Q},", "VAR")
+
+       // The replacement may contain the :Q modifier only once.
+       test("s,@VAR@,${VAR:Q:Q},", "")
+
+       // The replacement must be a plain variable expression, without prefix.
+       test("s,@VAR@,prefix${VAR},", "")
+
+       // The replacement must be a plain variable expression, without suffix.
+       test("s,@VAR@,${VAR}suffix,", "")
+}
+
 // As of May 2019, pkglint does not check the order of the variables in
 // a SUBST block. Enforcing this order, or at least suggesting it, would
 // make pkgsrc packages more uniform, which is a good idea, but not urgent.
@@ -835,7 +887,7 @@ func simulateSubstLines(t *Tester, texts
        for _, lineText := range texts {
                var curr int
                _, err := fmt.Sscanf(lineText[0:4], "%d: ", &curr)
-               G.AssertNil(err, "")
+               assertNil(err, "")
 
                if lineno != 0 {
                        t.Check(curr, equals, lineno)

Index: pkgsrc/pkgtools/pkglint/files/util.go
diff -u pkgsrc/pkgtools/pkglint/files/util.go:1.45 pkgsrc/pkgtools/pkglint/files/util.go:1.46
--- pkgsrc/pkgtools/pkglint/files/util.go:1.45  Sun May 26 14:05:57 2019
+++ pkgsrc/pkgtools/pkglint/files/util.go       Mon Jun 10 19:51:57 2019
@@ -91,6 +91,14 @@ func trimHspace(str string) string {
        return str[start:end]
 }
 
+func rtrimHspace(str string) string {
+       end := len(str)
+       for end > 0 && isHspace(str[end-1]) {
+               end--
+       }
+       return str[:end]
+}
+
 func trimCommon(a, b string) (string, string) {
        // trim common prefix
        for len(a) > 0 && len(b) > 0 && a[0] == b[0] {
@@ -111,6 +119,10 @@ func isHspace(ch byte) bool {
        return ch == ' ' || ch == '\t'
 }
 
+func isHspaceRune(r rune) bool {
+       return r == ' ' || r == '\t'
+}
+
 func ifelseStr(cond bool, a, b string) string {
        if cond {
                return a
@@ -134,12 +146,30 @@ func imax(a, b int) int {
        return b
 }
 
+// assertNil ensures that the given error is nil.
+//
+// Contrary to other diagnostics, the format should not end in a period
+// since it is followed by the error.
+//
+// Other than Assertf, this method does not require any comparison operator in the calling code.
+// This makes it possible to get 100% branch coverage for cases that "really can never fail".
 func assertNil(err error, format string, args ...interface{}) {
        if err != nil {
                panic("Pkglint internal error: " + sprintf(format, args...) + ": " + err.Error())
        }
 }
 
+// assertf checks that the condition is true. Otherwise it terminates the
+// process with a fatal error message, prefixed with "Pkglint internal error".
+//
+// This method must only be used for programming errors.
+// For runtime errors, use dummyLine.Fatalf.
+func assertf(cond bool, format string, args ...interface{}) {
+       if !cond {
+               panic("Pkglint internal error: " + sprintf(format, args...))
+       }
+}
+
 func isEmptyDir(filename string) bool {
        if hasSuffix(filename, "/CVS") {
                return true
@@ -399,10 +429,10 @@ func relpath(from, to string) (result st
        absTo := abspath(cto)
 
        toTop, err := filepath.Rel(absFrom, absTopdir)
-       G.AssertNil(err, "relpath from %q to topdir %q", absFrom, absTopdir)
+       assertNil(err, "relpath from %q to topdir %q", absFrom, absTopdir)
 
        fromTop, err := filepath.Rel(absTopdir, absTo)
-       G.AssertNil(err, "relpath from topdir %q to %q", absTopdir, absTo)
+       assertNil(err, "relpath from topdir %q to %q", absTopdir, absTo)
 
        result = cleanpath(filepath.ToSlash(toTop) + "/" + filepath.ToSlash(fromTop))
 
@@ -468,25 +498,48 @@ func hasAlnumPrefix(s string) bool { ret
 // and only returns true on each first call.
 type Once struct {
        seen map[uint64]struct{}
+
+       // Only used during testing, to trace the actual arguments,
+       // since hashing is a one-way function.
+       Trace bool
 }
 
 func (o *Once) FirstTime(what string) bool {
-       return o.check(crc64.Checksum([]byte(what), crc64.MakeTable(crc64.ECMA)))
+       firstTime := o.check(o.keyString(what))
+       if firstTime && o.Trace {
+               G.Logger.out.WriteLine(sprintf("FirstTime: %s", what))
+       }
+       return firstTime
 }
 
 func (o *Once) FirstTimeSlice(whats ...string) bool {
-       crc := crc64.New(crc64.MakeTable(crc64.ECMA))
-       for _, what := range whats {
-               _, _ = crc.Write([]byte(what))
+       firstTime := o.check(o.keyStrings(whats))
+       if firstTime && o.Trace {
+               G.Logger.out.WriteLine(sprintf("FirstTime: %s", strings.Join(whats, ", ")))
        }
-       return o.check(crc.Sum64())
+       return firstTime
 }
 
 func (o *Once) Seen(what string) bool {
-       _, seen := o.seen[crc64.Checksum([]byte(what), crc64.MakeTable(crc64.ECMA))]
+       _, seen := o.seen[o.keyString(what)]
        return seen
 }
 
+func (*Once) keyString(what string) uint64 {
+       return crc64.Checksum([]byte(what), crc64.MakeTable(crc64.ECMA))
+}
+
+func (*Once) keyStrings(whats []string) uint64 {
+       crc := crc64.New(crc64.MakeTable(crc64.ECMA))
+       for i, what := range whats {
+               if i != 0 {
+                       _, _ = crc.Write([]byte{0})
+               }
+               _, _ = crc.Write([]byte(what))
+       }
+       return crc.Sum64()
+}
+
 func (o *Once) check(key uint64) bool {
        if _, ok := o.seen[key]; ok {
                return false
@@ -576,7 +629,7 @@ func (s *Scope) Use(varname string, line
                                trace.Step2("Using %q in %s", name, line.String())
                        }
                }
-               if time == vucTimeParse {
+               if time == vucTimeLoad {
                        s.usedAtLoadTime[name] = true
                }
        }
@@ -940,13 +993,18 @@ func (c *FileCache) Get(filename string,
 func (c *FileCache) Evict(filename string) {
        key := c.key(filename)
        entry, found := c.mapping[key]
-       if found {
-               delete(c.mapping, key)
+       if !found {
+               return
+       }
+
+       delete(c.mapping, key)
 
-               sort.Slice(c.table, func(i, j int) bool {
-                       return c.table[j] == entry && c.table[i] != entry
-               })
-               c.table = c.table[0 : len(c.table)-1]
+       for i, e := range c.table {
+               if e == entry {
+                       c.table[i] = c.table[len(c.table)-1]
+                       c.table = c.table[:len(c.table)-1]
+                       return
+               }
        }
 }
 
@@ -1134,7 +1192,7 @@ func (si *StringInterner) Intern(str str
        return key
 }
 
-// StringSets stores unique strings in insertion order.
+// StringSet stores unique strings in insertion order.
 type StringSet struct {
        Elements []string
        seen     map[string]struct{}

Index: pkgsrc/pkgtools/pkglint/files/util_test.go
diff -u pkgsrc/pkgtools/pkglint/files/util_test.go:1.29 pkgsrc/pkgtools/pkglint/files/util_test.go:1.30
--- pkgsrc/pkgtools/pkglint/files/util_test.go:1.29     Sun May 26 14:05:57 2019
+++ pkgsrc/pkgtools/pkglint/files/util_test.go  Mon Jun 10 19:51:57 2019
@@ -1,12 +1,23 @@
 package pkglint
 
 import (
+       "errors"
        "gopkg.in/check.v1"
        "os"
        "testing"
        "time"
 )
 
+func (s *Suite) Test_assertNil(c *check.C) {
+       t := s.Init(c)
+
+       assertNil(nil, "this is not an error")
+
+       t.ExpectPanic(
+               func() { assertNil(errors.New("unexpected error"), "Oops") },
+               "Pkglint internal error: Oops: unexpected error")
+}
+
 func (s *Suite) Test_YesNoUnknown_String(c *check.C) {
        c.Check(yes.String(), equals, "yes")
        c.Check(no.String(), equals, "no")
@@ -704,6 +715,62 @@ func (s *Suite) Test_FileCache_removeOld
                "TRACE:   FileCache.Halve \"filename2.mk\" with count 3.")
 }
 
+func (s *Suite) Test_FileCache_removeOldEntries__no_tracing(c *check.C) {
+       t := s.Init(c)
+
+       t.DisableTracing()
+
+       lines := t.NewLines("filename.mk",
+               MkRcsID)
+       cache := NewFileCache(3)
+       cache.Put("filename1.mk", 0, lines)
+       cache.Put("filename2.mk", 0, lines)
+       cache.Get("filename2.mk", 0)
+       cache.Get("filename2.mk", 0)
+       cache.Put("filename3.mk", 0, lines)
+       cache.Put("filename4.mk", 0, lines)
+
+       t.CheckOutputEmpty()
+}
+
+// Covers the newLen > 0 condition.
+func (s *Suite) Test_FileCache_removeOldEntries__zero_capacity(c *check.C) {
+       t := s.Init(c)
+
+       lines := t.NewLines("filename.mk",
+               MkRcsID)
+       cache := NewFileCache(1)
+       cache.Put("filename1.mk", 0, lines)
+
+       // This call removes all existing entries from the cache,
+       // as the cache's capacity is only 1.
+       cache.Put("filename2.mk", 0, lines)
+}
+
+func (s *Suite) Test_FileCache_Evict__sort(c *check.C) {
+       t := s.Init(c)
+
+       lines := t.NewLines("filename.mk",
+               MkRcsID)
+       cache := NewFileCache(10)
+       cache.Put("filename0.mk", 0, lines)
+       cache.Put("filename1.mk", 0, lines)
+       cache.Put("filename2.mk", 0, lines)
+       cache.Put("filename3.mk", 0, lines)
+       cache.Put("filename4.mk", 0, lines)
+       cache.Put("filename5.mk", 0, lines)
+       cache.Put("filename6.mk", 0, lines)
+       cache.Put("filename7.mk", 0, lines)
+       cache.Put("filename8.mk", 0, lines)
+       cache.Put("filename9.mk", 0, lines)
+
+       cache.Evict("filename5.mk")
+
+       t.Check(cache.table, check.HasLen, 9)
+       t.Check(cache.Get("filename5.mk", 0), check.IsNil)
+       t.Check(cache.Get("filename6.mk", 0), check.NotNil)
+}
+
 func (s *Suite) Test_makeHelp(c *check.C) {
        c.Check(makeHelp("subst"), equals, confMake+" help topic=subst")
 }
@@ -726,6 +793,23 @@ func (s *Suite) Test_Once(c *check.C) {
        c.Check(once.FirstTimeSlice("str", "str2"), equals, false)
 }
 
+func (s *Suite) Test_Once__trace(c *check.C) {
+       t := s.Init(c)
+
+       var once Once
+       once.Trace = true
+
+       c.Check(once.FirstTime("str"), equals, true)
+       c.Check(once.FirstTime("str"), equals, false)
+       c.Check(once.FirstTimeSlice("str"), equals, false)
+       c.Check(once.FirstTimeSlice("str", "str2"), equals, true)
+       c.Check(once.FirstTimeSlice("str", "str2"), equals, false)
+
+       t.CheckOutputLines(
+               "FirstTime: str",
+               "FirstTime: str, str2")
+}
+
 func (s *Suite) Test_wrap(c *check.C) {
 
        wrapped := wrap(20,

Index: pkgsrc/pkgtools/pkglint/files/var.go
diff -u pkgsrc/pkgtools/pkglint/files/var.go:1.3 pkgsrc/pkgtools/pkglint/files/var.go:1.4
--- pkgsrc/pkgtools/pkglint/files/var.go:1.3    Wed Apr  3 21:49:51 2019
+++ pkgsrc/pkgtools/pkglint/files/var.go        Mon Jun 10 19:51:57 2019
@@ -106,7 +106,7 @@ func (v *Var) Constant() bool {
 // Variable assignments in the pkgsrc infrastructure are taken into account
 // for determining the constant value.
 func (v *Var) ConstantValue() string {
-       G.Assertf(v.Constant(), "Variable must be constant.")
+       assertf(v.Constant(), "Variable must be constant.")
        return v.constantValue
 }
 
@@ -170,11 +170,11 @@ func (v *Var) Read(mkline MkLine) {
 // Side-effect assignments (${VAR::=value}) are not handled here since
 // they don't occur in practice.
 func (v *Var) Write(mkline MkLine, conditional bool, conditionVarnames ...string) {
-       G.Assertf(mkline.Varname() == v.Name, "wrong variable name")
+       assertf(mkline.Varname() == v.Name, "wrong variable name")
 
        v.writeLocations = append(v.writeLocations, mkline)
 
-       if conditional || len(conditionVarnames) > 0 {
+       if conditional {
                v.conditional = true
        }
        v.conditionalVars.AddAll(conditionVarnames)
Index: pkgsrc/pkgtools/pkglint/files/var_test.go
diff -u pkgsrc/pkgtools/pkglint/files/var_test.go:1.3 pkgsrc/pkgtools/pkglint/files/var_test.go:1.4
--- pkgsrc/pkgtools/pkglint/files/var_test.go:1.3       Sun May 26 14:05:57 2019
+++ pkgsrc/pkgtools/pkglint/files/var_test.go   Mon Jun 10 19:51:57 2019
@@ -235,6 +235,17 @@ func (s *Suite) Test_Var_Write__conditio
        })
 }
 
+func (s *Suite) Test_Var_Write__assertion(c *check.C) {
+       t := s.Init(c)
+
+       v := NewVar("VAR")
+       t.ExpectPanic(
+               func() {
+                       v.Write(t.NewMkLine("filename.mk", 1, "OTHER=value"), false, nil...)
+               },
+               "Pkglint internal error: wrong variable name")
+}
+
 func (s *Suite) Test_Var_Value__conditional_write_after_unconditional(c *check.C) {
        t := s.Init(c)
 

Index: pkgsrc/pkgtools/pkglint/files/vardefs.go
diff -u pkgsrc/pkgtools/pkglint/files/vardefs.go:1.66 pkgsrc/pkgtools/pkglint/files/vardefs.go:1.67
--- pkgsrc/pkgtools/pkglint/files/vardefs.go:1.66       Sun May 26 14:05:57 2019
+++ pkgsrc/pkgtools/pkglint/files/vardefs.go    Mon Jun 10 19:51:57 2019
@@ -58,7 +58,7 @@ func (reg *VarTypeRegistry) DefineType(v
 
 func (reg *VarTypeRegistry) Define(varname string, basicType *BasicType, options vartypeOptions, aclEntries ...ACLEntry) {
        m, varbase, varparam := match2(varname, `^([A-Z_.][A-Z0-9_]*|@)(|\*|\.\*)$`)
-       G.Assertf(m, "invalid variable name")
+       assertf(m, "invalid variable name")
 
        vartype := NewVartype(basicType, options, aclEntries...)
 
@@ -86,691 +86,693 @@ func (reg *VarTypeRegistry) DefineParse(
        reg.Define(varname, basicType, options, parsedEntries...)
 }
 
-// Init initializes the long list of predefined pkgsrc variables.
-// After this is done, PKGNAME, MAKE_ENV and all the other variables
-// can be used in Makefiles without triggering warnings about typos.
-func (reg *VarTypeRegistry) Init(src *Pkgsrc) {
+// acl defines the permissions of a variable by listing the permissions
+// individually.
+//
+// Each variable that uses this function directly must document:
+//  - which of the predefined permission sets is the closest
+//  - how this individual permission set differs
+//  - why the predefined permission set is not good enough
+//  - which packages need this custom permission set.
+func (reg *VarTypeRegistry) acl(varname string, basicType *BasicType, options vartypeOptions, aclEntries ...string) {
+       assertf(!reg.DefinedExact(varname), "Variable %q must only be defined once.", varname)
+       reg.DefineParse(varname, basicType, options, aclEntries...)
+}
 
-       // acl defines the permissions of a variable by listing the permissions
-       // individually.
-       //
-       // Each variable that uses this function directly must document:
-       //  - which of the predefined permission sets is the closest
-       //  - how this individual permission set differs
-       //  - why the predefined permission set is not good enough
-       //  - which packages need this custom permission set.
-       acl := func(varname string, basicType *BasicType, options vartypeOptions, aclEntries ...string) {
-               G.Assertf(!reg.DefinedExact(varname), "Variable %q must only be defined once.", varname)
-               reg.DefineParse(varname, basicType, options, aclEntries...)
-       }
+// acllist defines the permissions of a list variable by listing
+// the permissions individually.
+//
+// Each variable that uses this function directly must document:
+//  - which of the predefined permission sets is the closest
+//  - how this individual permission set differs
+//  - why the predefined permission set is not good enough
+//  - which packages need this custom permission set.
+func (reg *VarTypeRegistry) acllist(varname string, basicType *BasicType, options vartypeOptions, aclEntries ...string) {
+       reg.acl(varname, basicType, options|List, aclEntries...)
+}
 
-       // acllist defines the permissions of a list variable by listing
-       // the permissions individually.
-       //
-       // Each variable that uses this function directly must document:
-       //  - which of the predefined permission sets is the closest
-       //  - how this individual permission set differs
-       //  - why the predefined permission set is not good enough
-       //  - which packages need this custom permission set.
-       acllist := func(varname string, basicType *BasicType, options vartypeOptions, aclEntries ...string) {
-               acl(varname, basicType, options|List, aclEntries...)
-       }
+// A package-settable variable may be set in all Makefiles except buildlink3.mk and builtin.mk.
+func (reg *VarTypeRegistry) pkg(varname string, basicType *BasicType) {
+       reg.acl(varname, basicType,
+               PackageSettable,
+               "buildlink3.mk, builtin.mk: none",
+               "Makefile, Makefile.*, *.mk: default, set, use")
+}
 
-       // A package-settable variable may be set in all Makefiles except buildlink3.mk and builtin.mk.
-       pkg := func(varname string, basicType *BasicType) {
-               acl(varname, basicType,
-                       PackageSettable,
-                       "buildlink3.mk, builtin.mk: none",
-                       "Makefile, Makefile.*, *.mk: default, set, use")
-       }
+// Like pkg, but always needs a rationale.
+func (reg *VarTypeRegistry) pkgrat(varname string, basicType *BasicType) {
+       reg.acl(varname, basicType,
+               PackageSettable|NeedsRationale,
+               "buildlink3.mk, builtin.mk: none",
+               "Makefile, Makefile.*, *.mk: default, set, use")
+}
 
-       // Like pkg, but always needs a rationale.
-       pkgrat := func(varname string, basicType *BasicType) {
-               acl(varname, basicType,
-                       PackageSettable|NeedsRationale,
-                       "buildlink3.mk, builtin.mk: none",
-                       "Makefile, Makefile.*, *.mk: default, set, use")
-       }
+// pkgload is the same as pkg, except that the variable may be accessed at load time.
+func (reg *VarTypeRegistry) pkgload(varname string, basicType *BasicType) {
+       reg.acl(varname, basicType,
+               PackageSettable,
+               "buildlink3.mk: none",
+               "builtin.mk: use, use-loadtime",
+               "Makefile, Makefile.*, *.mk: default, set, use, use-loadtime")
+}
 
-       // pkgload is the same as pkg, except that the variable may be accessed at load time.
-       pkgload := func(varname string, basicType *BasicType) {
-               acl(varname, basicType,
-                       PackageSettable,
-                       "buildlink3.mk: none",
-                       "builtin.mk: use, use-loadtime",
-                       "Makefile, Makefile.*, *.mk: default, set, use, use-loadtime")
-       }
+// A package-defined list may be defined and appended to in all Makefiles
+// except buildlink3.mk and builtin.mk. Simple assignment (instead of
+// appending) is also allowed. If this leads of an unconditional
+// assignment overriding a previous value, the redundancy check will
+// catch it.
+func (reg *VarTypeRegistry) pkglist(varname string, basicType *BasicType) {
+       reg.acllist(varname, basicType,
+               List|PackageSettable,
+               "buildlink3.mk, builtin.mk: none",
+               "Makefile, Makefile.*, *.mk: default, set, append, use")
+}
 
-       // A package-defined list may be defined and appended to in all Makefiles
-       // except buildlink3.mk and builtin.mk. Simple assignment (instead of
-       // appending) is also allowed. If this leads of an unconditional
-       // assignment overriding a previous value, the redundancy check will
-       // catch it.
-       pkglist := func(varname string, basicType *BasicType) {
-               acllist(varname, basicType,
-                       List|PackageSettable,
-                       "buildlink3.mk, builtin.mk: none",
-                       "Makefile, Makefile.*, *.mk: default, set, append, use")
-       }
+// Like pkglist, but always needs a rationale.
+func (reg *VarTypeRegistry) pkglistrat(varname string, basicType *BasicType) {
+       reg.acllist(varname, basicType,
+               List|PackageSettable|NeedsRationale,
+               "buildlink3.mk, builtin.mk: none",
+               "Makefile, Makefile.*, *.mk: default, set, append, use")
+}
 
-       // Like pkglist, but always needs a rationale.
-       pkglistrat := func(varname string, basicType *BasicType) {
-               acllist(varname, basicType,
-                       List|PackageSettable|NeedsRationale,
-                       "buildlink3.mk, builtin.mk: none",
-                       "Makefile, Makefile.*, *.mk: default, set, append, use")
-       }
+// pkgappend declares a variable that may use the += operator,
+// even though it is not a list where each item can be interpreted
+// on its own.
+//
+// This applies to lists in which a single logical list item is
+// composed of several syntactical words, such as CONF_FILES, which is
+// a list of filename pairs.
+//
+// This also applies to COMMENT, which is not a list at all but a string
+// that is sometimes composed of a common prefix and a package-specific
+// suffix.
+func (reg *VarTypeRegistry) pkgappend(varname string, basicType *BasicType) {
+       reg.acl(varname, basicType,
+               PackageSettable,
+               "buildlink3.mk, builtin.mk: none",
+               "Makefile, Makefile.*, *.mk: default, set, append, use")
+}
 
-       // pkgappend declares a variable that may use the += operator,
-       // even though it is not a list where each item can be interpreted
-       // on its own.
-       //
-       // This applies to lists in which a single logical list item is
-       // composed of several syntactical words, such as CONF_FILES, which is
-       // a list of filename pairs.
-       //
-       // This also applies to COMMENT, which is not a list at all but a string
-       // that is sometimes composed of a common prefix and a package-specific
-       // suffix.
-       pkgappend := func(varname string, basicType *BasicType) {
-               acl(varname, basicType,
-                       PackageSettable,
-                       "buildlink3.mk, builtin.mk: none",
-                       "Makefile, Makefile.*, *.mk: default, set, append, use")
-       }
-       pkgappendbl3 := func(varname string, basicType *BasicType) {
-               acl(varname, basicType,
-                       PackageSettable,
-                       "Makefile, Makefile.*, *.mk: default, set, append, use")
-       }
+func (reg *VarTypeRegistry) pkgappendbl3(varname string, basicType *BasicType) {
+       reg.acl(varname, basicType,
+               PackageSettable,
+               "Makefile, Makefile.*, *.mk: default, set, append, use")
+}
 
-       // Like pkgappend, but always needs a rationale.
-       pkgappendrat := func(varname string, basicType *BasicType) {
-               acl(varname, basicType,
-                       PackageSettable|NeedsRationale,
-                       "buildlink3.mk, builtin.mk: none",
-                       "Makefile, Makefile.*, *.mk: default, set, append, use")
-       }
+// Like pkgappend, but always needs a rationale.
+func (reg *VarTypeRegistry) pkgappendrat(varname string, basicType *BasicType) {
+       reg.acl(varname, basicType,
+               PackageSettable|NeedsRationale,
+               "buildlink3.mk, builtin.mk: none",
+               "Makefile, Makefile.*, *.mk: default, set, append, use")
+}
 
-       // Some package-defined variables may be modified in buildlink3.mk files.
-       // These variables are typically related to compiling and linking files
-       // from C and related languages.
-       pkgbl3 := func(varname string, basicType *BasicType) {
-               acl(varname, basicType,
-                       PackageSettable,
-                       "Makefile, Makefile.*, *.mk: default, set, use")
-       }
-       // Some package-defined lists may also be modified in buildlink3.mk files,
-       // for example platform-specific CFLAGS and LDFLAGS.
-       pkglistbl3 := func(varname string, basicType *BasicType) {
-               acl(varname, basicType,
-                       List|PackageSettable,
-                       "Makefile, Makefile.*, *.mk: default, set, append, use")
-       }
+// Some package-defined variables may be modified in buildlink3.mk files.
+// These variables are typically related to compiling and linking files
+// from C and related languages.
+func (reg *VarTypeRegistry) pkgbl3(varname string, basicType *BasicType) {
+       reg.acl(varname, basicType,
+               PackageSettable,
+               "Makefile, Makefile.*, *.mk: default, set, use")
+}
 
-       // Like pkglistbl3, but always needs a rationale.
-       pkglistbl3rat := func(varname string, basicType *BasicType) {
-               acl(varname, basicType,
-                       List|PackageSettable|NeedsRationale,
-                       "Makefile, Makefile.*, *.mk: default, set, append, use")
-       }
+// Some package-defined lists may also be modified in buildlink3.mk files,
+// for example platform-specific CFLAGS and LDFLAGS.
+func (reg *VarTypeRegistry) pkglistbl3(varname string, basicType *BasicType) {
+       reg.acl(varname, basicType,
+               List|PackageSettable,
+               "Makefile, Makefile.*, *.mk: default, set, append, use")
+}
 
-       // sys declares a user-defined or system-defined variable that must not
-       // be modified by packages.
-       //
-       // It also must not be used in buildlink3.mk and builtin.mk files or at
-       // load time since the system/user preferences may not have been loaded
-       // when these files are included.
-       //
-       // TODO: These timing issues should be handled separately from the permissions.
-       //  They can be made more precise.
-       sys := func(varname string, basicType *BasicType) {
-               acl(varname, basicType,
-                       SystemProvided,
-                       "buildlink3.mk: none",
-                       "*: use")
-       }
+// Like pkglistbl3, but always needs a rationale.
+func (reg *VarTypeRegistry) pkglistbl3rat(varname string, basicType *BasicType) {
+       reg.acl(varname, basicType,
+               List|PackageSettable|NeedsRationale,
+               "Makefile, Makefile.*, *.mk: default, set, append, use")
+}
 
-       sysbl3 := func(varname string, basicType *BasicType) {
-               acl(varname, basicType,
-                       SystemProvided,
-                       "*: use")
-       }
+// sys declares a user-defined or system-defined variable that must not
+// be modified by packages.
+//
+// It also must not be used in buildlink3.mk and builtin.mk files or at
+// load time since the system/user preferences may not have been loaded
+// when these files are included.
+//
+// TODO: These timing issues should be handled separately from the permissions.
+//  They can be made more precise.
+func (reg *VarTypeRegistry) sys(varname string, basicType *BasicType) {
+       reg.acl(varname, basicType,
+               SystemProvided,
+               "buildlink3.mk: none",
+               "*: use")
+}
 
-       syslist := func(varname string, basicType *BasicType) {
-               acllist(varname, basicType,
-                       List|SystemProvided,
-                       "buildlink3.mk: none",
-                       "*: use")
-       }
+func (reg *VarTypeRegistry) sysbl3(varname string, basicType *BasicType) {
+       reg.acl(varname, basicType,
+               SystemProvided,
+               "*: use")
+}
 
-       // usr declares a user-defined variable that must not be modified by packages.
-       usr := func(varname string, basicType *BasicType) {
-               acl(varname, basicType,
-                       // TODO: why is builtin.mk missing here?
-                       UserSettable,
-                       "buildlink3.mk: none",
-                       "*: use, use-loadtime")
-       }
+func (reg *VarTypeRegistry) syslist(varname string, basicType *BasicType) {
+       reg.acllist(varname, basicType,
+               List|SystemProvided,
+               "buildlink3.mk: none",
+               "*: use")
+}
 
-       // usr declares a user-defined list variable that must not be modified by packages.
-       usrlist := func(varname string, basicType *BasicType) {
-               acllist(varname, basicType,
-                       // TODO: why is builtin.mk missing here?
-                       List|UserSettable,
-                       "buildlink3.mk: none",
-                       "*: use, use-loadtime")
-       }
+// usr declares a user-defined variable that must not be modified by packages.
+func (reg *VarTypeRegistry) usr(varname string, basicType *BasicType) {
+       reg.acl(varname, basicType,
+               // TODO: why is builtin.mk missing here?
+               UserSettable,
+               "buildlink3.mk: none",
+               "*: use, use-loadtime")
+}
 
-       // A few variables from mk/defaults/mk.conf may be overridden by packages.
-       // Therefore they need a separate definition of "user-settable".
-       //
-       // It is debatable whether packages should be allowed to override these
-       // variables at all since then there are two competing sources for the
-       // default values. Current practice is to have exactly this ambiguity,
-       // combined with some package Makefiles including bsd.prefs.mk and others
-       // omitting this necessary inclusion.
-       //
-       // TODO: parse all the below information directly from mk/defaults/mk.conf.
-       usrpkg := func(varname string, basicType *BasicType) {
-               acl(varname, basicType,
-                       PackageSettable|UserSettable,
-                       "Makefile: default, set, use, use-loadtime",
-                       "buildlink3.mk, builtin.mk: none",
-                       "Makefile.*, *.mk: default, set, use, use-loadtime",
-                       "*: use, use-loadtime")
-       }
+// usr declares a user-defined list variable that must not be modified by packages.
+func (reg *VarTypeRegistry) usrlist(varname string, basicType *BasicType) {
+       reg.acllist(varname, basicType,
+               // TODO: why is builtin.mk missing here?
+               List|UserSettable,
+               "buildlink3.mk: none",
+               "*: use, use-loadtime")
+}
 
-       // sysload declares a system-provided variable that may already be used at load time.
-       sysload := func(varname string, basicType *BasicType) {
-               acl(varname, basicType,
-                       SystemProvided,
-                       "*: use, use-loadtime")
-       }
+// A few variables from mk/defaults/mk.conf may be overridden by packages.
+// Therefore they need a separate definition of "user-settable".
+//
+// It is debatable whether packages should be allowed to override these
+// variables at all since then there are two competing sources for the
+// default values. Current practice is to have exactly this ambiguity,
+// combined with some package Makefiles including bsd.prefs.mk and others
+// omitting this necessary inclusion.
+//
+// TODO: parse all the below information directly from mk/defaults/mk.conf.
+func (reg *VarTypeRegistry) usrpkg(varname string, basicType *BasicType) {
+       reg.acl(varname, basicType,
+               PackageSettable|UserSettable,
+               "Makefile: default, set, use, use-loadtime",
+               "buildlink3.mk, builtin.mk: none",
+               "Makefile.*, *.mk: default, set, use, use-loadtime",
+               "*: use, use-loadtime")
+}
 
-       sysloadlist := func(varname string, basicType *BasicType) {
-               acl(varname, basicType,
-                       List|SystemProvided,
-                       "*: use, use-loadtime")
-       }
+// sysload declares a system-provided variable that may already be used at load time.
+func (reg *VarTypeRegistry) sysload(varname string, basicType *BasicType) {
+       reg.acl(varname, basicType,
+               SystemProvided,
+               "*: use, use-loadtime")
+}
 
-       // bl3list declares a list variable that is defined by buildlink3.mk and
-       // builtin.mk and can later be used by the package.
-       bl3list := func(varname string, basicType *BasicType) {
-               acl(varname, basicType,
-                       List, // not PackageSettable since the package uses it more than setting it.
-                       "buildlink3.mk, builtin.mk: append",
-                       "*: use")
-       }
+func (reg *VarTypeRegistry) sysloadlist(varname string, basicType *BasicType) {
+       reg.acl(varname, basicType,
+               List|SystemProvided,
+               "*: use, use-loadtime")
+}
 
-       // cmdline declares a variable that is defined on the command line. There
-       // are only few variables of this type, such as PKG_DEBUG_LEVEL.
-       cmdline := func(varname string, basicType *BasicType) {
-               acl(varname, basicType,
-                       CommandLineProvided,
-                       "buildlink3.mk, builtin.mk: none",
-                       "*: use, use-loadtime")
-       }
+// bl3list declares a list variable that is defined by buildlink3.mk and
+// builtin.mk and can later be used by the package.
+func (reg *VarTypeRegistry) bl3list(varname string, basicType *BasicType) {
+       reg.acl(varname, basicType,
+               List, // not PackageSettable since the package uses it more than setting it.
+               "buildlink3.mk, builtin.mk: append",
+               "*: use")
+}
 
-       // Only for infrastructure files; see mk/misc/show.mk
-       infralist := func(varname string, basicType *BasicType) {
-               acllist(varname, basicType,
-                       List,
-                       "*: append")
-       }
+// cmdline declares a variable that is defined on the command line. There
+// are only few variables of this type, such as PKG_DEBUG_LEVEL.
+func (reg *VarTypeRegistry) cmdline(varname string, basicType *BasicType) {
+       reg.acl(varname, basicType,
+               CommandLineProvided,
+               "buildlink3.mk, builtin.mk: none",
+               "*: use, use-loadtime")
+}
 
-       // compilerLanguages reads the available languages that are typically
-       // bundled in a single compiler framework, such as GCC or Clang.
-       compilerLanguages := enum(
-               func() string {
-                       mklines := LoadMk(src.File("mk/compiler.mk"), NotEmpty)
-                       languages := make(map[string]bool)
-                       if mklines != nil {
-                               for _, mkline := range mklines.mklines {
-                                       if mkline.IsDirective() && mkline.Directive() == "for" {
-                                               words := mkline.ValueFields(mkline.Args())
-                                               if len(words) > 2 && words[0] == "_version_" {
-                                                       for _, word := range words[2:] {
-                                                               languages[intern(word)] = true
-                                                       }
-                                               }
+// Only for infrastructure files; see mk/misc/show.mk
+func (reg *VarTypeRegistry) infralist(varname string, basicType *BasicType) {
+       reg.acllist(varname, basicType,
+               List,
+               "*: append")
+}
+
+// compilerLanguages reads the available languages that are typically
+// bundled in a single compiler framework, such as GCC or Clang.
+func (reg *VarTypeRegistry) compilerLanguages(src *Pkgsrc) *BasicType {
+       mklines := LoadMk(src.File("mk/compiler.mk"), NotEmpty)
+       languages := make(map[string]bool)
+       if mklines != nil {
+               for _, mkline := range mklines.mklines {
+                       if mkline.IsDirective() && mkline.Directive() == "for" {
+                               words := mkline.ValueFields(mkline.Args())
+                               if len(words) > 2 && words[0] == "_version_" {
+                                       for _, word := range words[2:] {
+                                               languages[intern(word)] = true
                                        }
                                }
                        }
-                       alwaysAvailable := [...]string{
-                               "ada", "c", "c99", "c++", "c++11", "c++14",
-                               "fortran", "fortran77", "java", "objc", "obj-c++"}
-                       for _, language := range alwaysAvailable {
-                               languages[language] = true
-                       }
+               }
+       }
+       alwaysAvailable := [...]string{
+               "ada", "c", "c99", "c++", "c++11", "c++14",
+               "fortran", "fortran77", "java", "objc", "obj-c++"}
+       for _, language := range alwaysAvailable {
+               languages[language] = true
+       }
 
-                       joined := keysJoined(languages)
-                       if trace.Tracing {
-                               trace.Stepf("Languages from mk/compiler.mk: %s", joined)
-                       }
-                       return joined
-               }())
+       joined := keysJoined(languages)
+       if trace.Tracing {
+               trace.Stepf("Languages from mk/compiler.mk: %s", joined)
+       }
 
-       // enumFrom parses all variable definitions for the given file,
-       // and for all variables matching one of the varcanons, all values
-       // are added as allowed values.
-       //
-       // If the file cannot be found, the allowed values are taken from
-       // defval. This is mostly useful when testing pkglint.
-       enumFrom := func(filename string, defval string, varcanons ...string) *BasicType {
-               mklines := LoadMk(src.File(filename), NotEmpty)
-               if mklines == nil {
-                       return enum(defval)
+       return enum(joined)
+}
+
+// enumFrom parses all variable definitions for the given file,
+// and for all variables matching one of the varcanons, all values
+// are added as allowed values.
+//
+// If the file cannot be found, the allowed values are taken from
+// defval. This is mostly useful when testing pkglint.
+func (reg *VarTypeRegistry) enumFrom(pkgsrc *Pkgsrc, filename string, defval string, varcanons ...string) *BasicType {
+       mklines := LoadMk(pkgsrc.File(filename), NotEmpty)
+       if mklines == nil {
+               return enum(defval)
+       }
+
+       values := make(map[string]bool)
+       for _, mkline := range mklines.mklines {
+               if !mkline.IsVarassign() {
+                       continue
                }
 
-               values := make(map[string]bool)
-               for _, mkline := range mklines.mklines {
-                       if !mkline.IsVarassign() {
+               varcanon := mkline.Varcanon()
+               for _, vc := range varcanons {
+                       if vc != varcanon {
                                continue
                        }
 
-                       varcanon := mkline.Varcanon()
-                       for _, vc := range varcanons {
-                               if vc != varcanon {
-                                       continue
+                       words := mkline.ValueFields(mkline.Value())
+                       for _, word := range words {
+                               if !contains(word, "$") {
+                                       values[intern(word)] = true
                                }
-
-                               words := mkline.ValueFields(mkline.Value())
-                               for _, word := range words {
-                                       if !contains(word, "$") {
-                                               values[intern(word)] = true
-                                       }
-                               }
-                       }
-               }
-
-               if len(values) > 0 {
-                       joined := keysJoined(values)
-                       if trace.Tracing {
-                               trace.Stepf("Enum from %s in %s with values: %s",
-                                       strings.Join(varcanons, " "), filename, joined)
                        }
-                       return enum(joined)
                }
+       }
 
+       if len(values) > 0 {
+               joined := keysJoined(values)
                if trace.Tracing {
-                       trace.Stepf("Enum from default value: %s", defval)
+                       trace.Stepf("Enum from %s in %s with values: %s",
+                               strings.Join(varcanons, " "), filename, joined)
                }
-               return enum(defval)
+               return enum(joined)
        }
 
-       // enumFromDirs reads the package directories from category, takes all
-       // that have a single number in them (such as php72) and ranks them
-       // from earliest to latest.
-       //
-       // If the directories cannot be found, the allowed values are taken
-       // from defval. This is mostly useful when testing pkglint.
-       enumFromDirs := func(category string, re regex.Pattern, repl string, defval string) *BasicType {
-               versions := src.ListVersions(category, re, repl, false)
-               if len(versions) == 0 {
-                       return enum(defval)
-               }
-               return enum(strings.Join(versions, " "))
+       if trace.Tracing {
+               trace.Stepf("Enum from default value: %s", defval)
        }
+       return enum(defval)
+}
 
-       // enumFromFiles reads the files from the given base directory,
-       // filtering it through the regular expression and the replacement.
-       //
-       // If no files are found, the allowed values are taken
-       // from defval. This should only happen in the pkglint tests.
-       enumFromFiles := func(basedir string, re regex.Pattern, repl string, defval string) *BasicType {
-               var relevant []string
-               for _, filename := range dirglob(G.Pkgsrc.File(basedir)) {
-                       basename := path.Base(filename)
-                       if matches(basename, re) {
-                               relevant = append(relevant, replaceAll(basename, re, repl))
-                       }
-               }
-               if len(relevant) == 0 {
-                       return enum(defval)
+// enumFromDirs reads the package directories from category, takes all
+// that have a single number in them (such as php72) and ranks them
+// from earliest to latest.
+//
+// If the directories cannot be found, the allowed values are taken
+// from defval. This is mostly useful when testing pkglint.
+func (reg *VarTypeRegistry) enumFromDirs(pkgsrc *Pkgsrc, category string, re regex.Pattern, repl string, defval string) *BasicType {
+       versions := pkgsrc.ListVersions(category, re, repl, false)
+       if len(versions) == 0 {
+               return enum(defval)
+       }
+       return enum(strings.Join(versions, " "))
+}
+
+// enumFromFiles reads the files from the given base directory,
+// filtering it through the regular expression and the replacement.
+//
+// If no files are found, the allowed values are taken
+// from defval. This should only happen in the pkglint tests.
+func (reg *VarTypeRegistry) enumFromFiles(basedir string, re regex.Pattern, repl string, defval string) *BasicType {
+       var relevant []string
+       for _, filename := range dirglob(G.Pkgsrc.File(basedir)) {
+               basename := path.Base(filename)
+               if matches(basename, re) {
+                       relevant = append(relevant, replaceAll(basename, re, repl))
                }
-               return enum(strings.Join(relevant, " "))
        }
+       if len(relevant) == 0 {
+               return enum(defval)
+       }
+       return enum(strings.Join(relevant, " "))
+}
+
+// Init initializes the long list of predefined pkgsrc variables.
+// After this is done, PKGNAME, MAKE_ENV and all the other variables
+// can be used in Makefiles without triggering warnings about typos.
+func (reg *VarTypeRegistry) Init(src *Pkgsrc) {
 
-       compilers := enumFrom(
+       compilers := reg.enumFrom(src,
                "mk/compiler.mk",
                "ccache ccc clang distcc f2c gcc hp icc ido mipspro mipspro-ucode pcc sunpro xlc",
                "_COMPILERS",
                "_PSEUDO_COMPILERS")
 
-       emacsVersions := enumFrom(
+       emacsVersions := reg.enumFrom(src,
                "editors/emacs/modules.mk",
                "emacs25 emacs21 emacs21nox emacs20 xemacs215 xemacs215nox xemacs214 xemacs214nox",
                "_EMACS_VERSIONS_ALL")
 
-       mysqlVersions := enumFrom(
+       mysqlVersions := reg.enumFrom(src,
                "mk/mysql.buildlink3.mk",
                "57 56 55 51 MARIADB55",
                "MYSQL_VERSIONS_ACCEPTED")
 
-       pgsqlVersions := enumFrom(
+       pgsqlVersions := reg.enumFrom(src,
                "mk/pgsql.buildlink3.mk",
                "10 96 95 94 93",
                "PGSQL_VERSIONS_ACCEPTED")
 
-       jvms := enumFrom(
+       jvms := reg.enumFrom(src,
                "mk/java-vm.mk",
                "openjdk8 oracle-jdk8 openjdk7 sun-jdk7 jdk16 jdk15 kaffe",
                "_PKG_JVMS.*")
 
        // Last synced with mk/defaults/mk.conf revision 1.300 (fe3d998769f).
-       usr("USE_CWRAPPERS", enum("yes no auto"))
-       usr("ALLOW_VULNERABLE_PACKAGES", BtYes)
-       usrlist("AUDIT_PACKAGES_FLAGS", BtShellWord)
-       usrlist("MANINSTALL", enum("maninstall catinstall"))
-       usr("MANZ", BtYes)
-       usrlist("GZIP", BtShellWord)
-       usr("MAKE_JOBS", BtInteger)
-       usr("OBJHOSTNAME", BtYes)
-       usr("OBJMACHINE", BtYes)
-       usr("SIGN_PACKAGES", enum("gpg x509"))
-       usr("X509_KEY", BtPathname)
-       usr("X509_CERTIFICATE", BtPathname)
-       usr("PATCH_DEBUG", BtYes)
-       usr("PKG_COMPRESSION", enum("gzip bzip2 xz none"))
-       usr("PKGSRC_LOCKTYPE", enum("none sleep once"))
-       usr("PKGSRC_SLEEPSECS", BtInteger)
-       usr("ABI", enum("32 64"))
-       usr("PKG_DEVELOPER", BtYesNo)
-       usr("USE_ABI_DEPENDS", BtYesNo)
-       usr("PKG_REGISTER_SHELLS", enum("YES NO"))
-       usrlist("PKGSRC_COMPILER", compilers)
-       usr("PKGSRC_KEEP_BIN_PKGS", BtYesNo)
-       usrlist("PKGSRC_MESSAGE_RECIPIENTS", BtMailAddress)
-       usr("PKGSRC_SHOW_BUILD_DEFS", BtYesNo)
-       usr("PKGSRC_RUN_TEST", BtYesNo)
-       usr("PKGSRC_MKPIE", BtYesNo)
-       usr("PKGSRC_MKREPRO", BtYesNo)
-       usr("PKGSRC_USE_CTF", BtYesNo)
-       usr("PKGSRC_USE_FORTIFY", enum("no weak strong"))
-       usr("PKGSRC_USE_RELRO", enum("no partial full"))
-       usr("PKGSRC_USE_SSP", enum("no yes strong all"))
-       usr("PKGSRC_USE_STACK_CHECK", enum("no yes"))
-       usr("PREFER.*", enum("pkgsrc native"))
-       usrlist("PREFER_PKGSRC", BtIdentifier)
-       usrlist("PREFER_NATIVE", BtIdentifier)
-       usr("PREFER_NATIVE_PTHREADS", BtYesNo)
-       usr("WRKOBJDIR", BtPathname)
-       usr("LOCALBASE", BtPathname)
-       usr("CROSSBASE", BtPathname)
-       usr("VARBASE", BtPathname)
+       reg.usr("USE_CWRAPPERS", enum("yes no auto"))
+       reg.usr("ALLOW_VULNERABLE_PACKAGES", BtYes)
+       reg.usrlist("AUDIT_PACKAGES_FLAGS", BtShellWord)
+       reg.usrlist("MANINSTALL", enum("maninstall catinstall"))
+       reg.usr("MANZ", BtYes)
+       reg.usrlist("GZIP", BtShellWord)
+       reg.usr("MAKE_JOBS", BtInteger)
+       reg.usr("OBJHOSTNAME", BtYes)
+       reg.usr("OBJMACHINE", BtYes)
+       reg.usr("SIGN_PACKAGES", enum("gpg x509"))
+       reg.usr("X509_KEY", BtPathname)
+       reg.usr("X509_CERTIFICATE", BtPathname)
+       reg.usr("PATCH_DEBUG", BtYes)
+       reg.usr("PKG_COMPRESSION", enum("gzip bzip2 xz none"))
+       reg.usr("PKGSRC_LOCKTYPE", enum("none sleep once"))
+       reg.usr("PKGSRC_SLEEPSECS", BtInteger)
+       reg.usr("ABI", enum("32 64"))
+       reg.usr("PKG_DEVELOPER", BtYesNo)
+       reg.usr("USE_ABI_DEPENDS", BtYesNo)
+       reg.usr("PKG_REGISTER_SHELLS", enum("YES NO"))
+       reg.usrlist("PKGSRC_COMPILER", compilers)
+       reg.usr("PKGSRC_KEEP_BIN_PKGS", BtYesNo)
+       reg.usrlist("PKGSRC_MESSAGE_RECIPIENTS", BtMailAddress)
+       reg.usr("PKGSRC_SHOW_BUILD_DEFS", BtYesNo)
+       reg.usr("PKGSRC_RUN_TEST", BtYesNo)
+       reg.usr("PKGSRC_MKPIE", BtYesNo)
+       reg.usr("PKGSRC_MKREPRO", BtYesNo)
+       reg.usr("PKGSRC_USE_CTF", BtYesNo)
+       reg.usr("PKGSRC_USE_FORTIFY", enum("no weak strong"))
+       reg.usr("PKGSRC_USE_RELRO", enum("no partial full"))
+       reg.usr("PKGSRC_USE_SSP", enum("no yes strong all"))
+       reg.usr("PKGSRC_USE_STACK_CHECK", enum("no yes"))
+       reg.usr("PREFER.*", enum("pkgsrc native"))
+       reg.usrlist("PREFER_PKGSRC", BtIdentifier)
+       reg.usrlist("PREFER_NATIVE", BtIdentifier)
+       reg.usr("PREFER_NATIVE_PTHREADS", BtYesNo)
+       reg.usr("WRKOBJDIR", BtPathname)
+       reg.usr("LOCALBASE", BtPathname)
+       reg.usr("CROSSBASE", BtPathname)
+       reg.usr("VARBASE", BtPathname)
 
        // X11_TYPE and X11BASE may be used in buildlink3.mk as well, which the
        // standard sysload doesn't allow.
-       acl("X11_TYPE", enum("modular native"),
+       reg.acl("X11_TYPE", enum("modular native"),
                UserSettable,
                "*: use, use-loadtime")
-       acl("X11BASE", BtPathname,
+       reg.acl("X11BASE", BtPathname,
                UserSettable,
                "*: use, use-loadtime")
 
-       usr("MOTIFBASE", BtPathname)
-       usr("PKGINFODIR", BtPathname)
-       usr("PKGMANDIR", BtPathname)
-       usr("PKGGNUDIR", BtPathname)
-       usr("BSDSRCDIR", BtPathname)
-       usr("BSDXSRCDIR", BtPathname)
-       usr("DISTDIR", BtPathname)
-       usr("DIST_PATH", BtPathlist)
-       usr("DEFAULT_VIEW", BtUnknown) // XXX: deprecate? pkgviews has been removed
-       usr("FETCH_CMD", BtShellCommand)
-       usr("FIX_SYSTEM_HEADERS", BtYes)
-       usr("LIBTOOLIZE_PLIST", BtYesNo)
-       usr("PKG_RESUME_TRANSFERS", BtYesNo)
-       usr("PKG_SYSCONFBASE", BtPathname)
-       usr("INIT_SYSTEM", enum("rc.d smf"))
-       usr("RCD_SCRIPTS_DIR", BtPathname)
-       usr("PACKAGES", BtPathname)
-       usr("PASSIVE_FETCH", BtYes)
-       usr("PATCH_FUZZ_FACTOR", enum("none -F0 -F1 -F2 -F3"))
-       usrlist("ACCEPTABLE_LICENSES", BtIdentifier)
-       usr("SPECIFIC_PKGS", BtYes)
-       usrlist("SITE_SPECIFIC_PKGS", BtPkgPath)
-       usrlist("HOST_SPECIFIC_PKGS", BtPkgPath)
-       usrlist("GROUP_SPECIFIC_PKGS", BtPkgPath)
-       usrlist("USER_SPECIFIC_PKGS", BtPkgPath)
-       usr("FAILOVER_FETCH", BtYes)
-       usrlist("MASTER_SORT", BtUnknown)
-       usrlist("MASTER_SORT_REGEX", BtUnknown)
-       usr("MASTER_SORT_RANDOM", BtYes)
-       usr("PKG_FC", BtShellCommand)
-       usrlist("IMAKEOPTS", BtShellWord)
-       usr("PRE_ROOT_CMD", BtShellCommand)
-       usr("SU_CMD", BtShellCommand)
-       usr("SU_CMD_PATH_APPEND", BtPathlist)
-       usr("FATAL_OBJECT_FMT_SKEW", BtYesNo)
-       usr("WARN_NO_OBJECT_FMT", BtYesNo)
-       usr("SMART_MESSAGES", BtYes)
-       usrlist("BINPKG_SITES", BtURL)
-       usrlist("BIN_INSTALL_FLAGS", BtShellWord)
-       usr("LOCALPATCHES", BtPathname)
-
-       usr("ACROREAD_FONTPATH", BtPathlist)
-       usr("AMANDA_USER", BtUserGroupName)
-       usr("AMANDA_TMP", BtPathname)
-       usr("AMANDA_VAR", BtPathname)
-       usr("APACHE_USER", BtUserGroupName)
-       usr("APACHE_GROUP", BtUserGroupName)
-       usrlist("APACHE_SUEXEC_CONFIGURE_ARGS", BtShellWord)
-       usrlist("APACHE_SUEXEC_DOCROOT", BtPathname)
-       usr("ARLA_CACHE", BtPathname)
-       usr("BIND_DIR", BtPathname)
-       usr("BIND_GROUP", BtUserGroupName)
-       usr("BIND_USER", BtUserGroupName)
-       usr("CACTI_GROUP", BtUserGroupName)
-       usr("CACTI_USER", BtUserGroupName)
-       usr("CANNA_GROUP", BtUserGroupName)
-       usr("CANNA_USER", BtUserGroupName)
-       usr("CDRECORD_CONF", BtPathname)
-       usr("CLAMAV_GROUP", BtUserGroupName)
-       usr("CLAMAV_USER", BtUserGroupName)
-       usr("CLAMAV_DBDIR", BtPathname)
-       usr("CONSERVER_DEFAULTHOST", BtIdentifier)
-       usr("CONSERVER_DEFAULTPORT", BtInteger)
-       usr("CUPS_GROUP", BtUserGroupName)
-       usr("CUPS_USER", BtUserGroupName)
-       usrlist("CUPS_SYSTEM_GROUPS", BtUserGroupName)
-       usr("CYRUS_IDLE", enum("poll idled no"))
-       usr("CYRUS_GROUP", BtUserGroupName)
-       usr("CYRUS_USER", BtUserGroupName)
-       usr("DAEMONTOOLS_LOG_USER", BtUserGroupName)
-       usr("DAEMONTOOLS_GROUP", BtUserGroupName)
-       usr("DBUS_GROUP", BtUserGroupName)
-       usr("DBUS_USER", BtUserGroupName)
-       usr("DEFANG_GROUP", BtUserGroupName)
-       usr("DEFANG_USER", BtUserGroupName)
-       usr("DEFANG_SPOOLDIR", BtPathname)
-       usr("DEFAULT_IRC_SERVER", BtIdentifier)
-       usr("DEFAULT_SERIAL_DEVICE", BtPathname)
-       usr("DIALER_GROUP", BtUserGroupName)
-       usr("DJBDNS_AXFR_USER", BtUserGroupName)
-       usr("DJBDNS_CACHE_USER", BtUserGroupName)
-       usr("DJBDNS_LOG_USER", BtUserGroupName)
-       usr("DJBDNS_RBL_USER", BtUserGroupName)
-       usr("DJBDNS_TINY_USER", BtUserGroupName)
-       usr("DJBDNS_DJBDNS_GROUP", BtUserGroupName)
-       usr("DT_LAYOUT", enum("US FI FR GER DV"))
-       usrlist("ELK_GUI", enum("none xaw motif"))
-       usr("EMACS_TYPE", emacsVersions)
-       usr("EXIM_GROUP", BtUserGroupName)
-       usr("EXIM_USER", BtUserGroupName)
-       usrpkg("EXTRACT_USING", enum("bsdtar gtar nbtar pax"))
-       usrlist("FETCH_BEFORE_ARGS", BtShellWord)
-       usrlist("FETCH_AFTER_ARGS", BtShellWord)
-       usrlist("FETCH_RESUME_ARGS", BtShellWord)
-       usrlist("FETCH_OUTPUT_ARGS", BtShellWord)
-       usrpkg("FETCH_USING", enum("auto curl custom fetch ftp manual wget"))
-       usr("FLUXBOX_USE_XINERAMA", enum("YES NO"))
-       usr("FLUXBOX_USE_KDE", enum("YES NO"))
-       usr("FLUXBOX_USE_GNOME", enum("YES NO"))
-       usr("FLUXBOX_USE_XFT", enum("YES NO"))
-       usr("FOX_USE_XUNICODE", enum("YES NO"))
-       usr("FREEWNN_USER", BtUserGroupName)
-       usr("FREEWNN_GROUP", BtUserGroupName)
-       usr("GAMES_USER", BtUserGroupName)
-       usr("GAMES_GROUP", BtUserGroupName)
-       usr("GAMEMODE", BtFileMode)
-       usr("GAMEDIRMODE", BtFileMode)
-       usr("GAMEDATAMODE", BtFileMode)
-       usr("GAMEGRP", BtUserGroupName)
-       usr("GAMEOWN", BtUserGroupName)
-       usr("GRUB_NETWORK_CARDS", BtIdentifier)
-       usr("GRUB_PRESET_COMMAND", enum("bootp dhcp rarp"))
-       usrlist("GRUB_SCAN_ARGS", BtShellWord)
-       usr("HASKELL_COMPILER", enum("ghc"))
-       usr("HOWL_GROUP", BtUserGroupName)
-       usr("HOWL_USER", BtUserGroupName)
-       usr("ICECAST_CHROOTDIR", BtPathname)
-       usr("ICECAST_CHUNKLEN", BtInteger)
-       usr("ICECAST_SOURCE_BUFFSIZE", BtInteger)
-       usr("IMAP_UW_CCLIENT_MBOX_FMT",
+       reg.usr("MOTIFBASE", BtPathname)
+       reg.usr("PKGINFODIR", BtPathname)
+       reg.usr("PKGMANDIR", BtPathname)
+       reg.usr("PKGGNUDIR", BtPathname)
+       reg.usr("BSDSRCDIR", BtPathname)
+       reg.usr("BSDXSRCDIR", BtPathname)
+       reg.usr("DISTDIR", BtPathname)
+       reg.usr("DIST_PATH", BtPathlist)
+       reg.usr("DEFAULT_VIEW", BtUnknown) // XXX: deprecate? pkgviews has been removed
+       reg.usr("FETCH_CMD", BtShellCommand)
+       reg.usr("FIX_SYSTEM_HEADERS", BtYes)
+       reg.usr("LIBTOOLIZE_PLIST", BtYesNo)
+       reg.usr("PKG_RESUME_TRANSFERS", BtYesNo)
+       reg.usr("PKG_SYSCONFBASE", BtPathname)
+       reg.usr("INIT_SYSTEM", enum("rc.d smf"))
+       reg.usr("RCD_SCRIPTS_DIR", BtPathname)
+       reg.usr("PACKAGES", BtPathname)
+       reg.usr("PASSIVE_FETCH", BtYes)
+       reg.usr("PATCH_FUZZ_FACTOR", enum("none -F0 -F1 -F2 -F3"))
+       reg.usrlist("ACCEPTABLE_LICENSES", BtIdentifier)
+       reg.usr("SPECIFIC_PKGS", BtYes)
+       reg.usrlist("SITE_SPECIFIC_PKGS", BtPkgPath)
+       reg.usrlist("HOST_SPECIFIC_PKGS", BtPkgPath)
+       reg.usrlist("GROUP_SPECIFIC_PKGS", BtPkgPath)
+       reg.usrlist("USER_SPECIFIC_PKGS", BtPkgPath)
+       reg.usr("FAILOVER_FETCH", BtYes)
+       reg.usrlist("MASTER_SORT", BtUnknown)
+       reg.usrlist("MASTER_SORT_REGEX", BtUnknown)
+       reg.usr("MASTER_SORT_RANDOM", BtYes)
+       reg.usr("PKG_FC", BtShellCommand)
+       reg.usrlist("IMAKEOPTS", BtShellWord)
+       reg.usr("PRE_ROOT_CMD", BtShellCommand)
+       reg.usr("SU_CMD", BtShellCommand)
+       reg.usr("SU_CMD_PATH_APPEND", BtPathlist)
+       reg.usr("FATAL_OBJECT_FMT_SKEW", BtYesNo)
+       reg.usr("WARN_NO_OBJECT_FMT", BtYesNo)
+       reg.usr("SMART_MESSAGES", BtYes)
+       reg.usrlist("BINPKG_SITES", BtURL)
+       reg.usrlist("BIN_INSTALL_FLAGS", BtShellWord)
+       reg.usr("LOCALPATCHES", BtPathname)
+
+       reg.usr("ACROREAD_FONTPATH", BtPathlist)
+       reg.usr("AMANDA_USER", BtUserGroupName)
+       reg.usr("AMANDA_TMP", BtPathname)
+       reg.usr("AMANDA_VAR", BtPathname)
+       reg.usr("APACHE_USER", BtUserGroupName)
+       reg.usr("APACHE_GROUP", BtUserGroupName)
+       reg.usrlist("APACHE_SUEXEC_CONFIGURE_ARGS", BtShellWord)
+       reg.usrlist("APACHE_SUEXEC_DOCROOT", BtPathname)
+       reg.usr("ARLA_CACHE", BtPathname)
+       reg.usr("BIND_DIR", BtPathname)
+       reg.usr("BIND_GROUP", BtUserGroupName)
+       reg.usr("BIND_USER", BtUserGroupName)
+       reg.usr("CACTI_GROUP", BtUserGroupName)
+       reg.usr("CACTI_USER", BtUserGroupName)
+       reg.usr("CANNA_GROUP", BtUserGroupName)
+       reg.usr("CANNA_USER", BtUserGroupName)
+       reg.usr("CDRECORD_CONF", BtPathname)
+       reg.usr("CLAMAV_GROUP", BtUserGroupName)
+       reg.usr("CLAMAV_USER", BtUserGroupName)
+       reg.usr("CLAMAV_DBDIR", BtPathname)
+       reg.usr("CONSERVER_DEFAULTHOST", BtIdentifier)
+       reg.usr("CONSERVER_DEFAULTPORT", BtInteger)
+       reg.usr("CUPS_GROUP", BtUserGroupName)
+       reg.usr("CUPS_USER", BtUserGroupName)
+       reg.usrlist("CUPS_SYSTEM_GROUPS", BtUserGroupName)
+       reg.usr("CYRUS_IDLE", enum("poll idled no"))
+       reg.usr("CYRUS_GROUP", BtUserGroupName)
+       reg.usr("CYRUS_USER", BtUserGroupName)
+       reg.usr("DAEMONTOOLS_LOG_USER", BtUserGroupName)
+       reg.usr("DAEMONTOOLS_GROUP", BtUserGroupName)
+       reg.usr("DBUS_GROUP", BtUserGroupName)
+       reg.usr("DBUS_USER", BtUserGroupName)
+       reg.usr("DEFANG_GROUP", BtUserGroupName)
+       reg.usr("DEFANG_USER", BtUserGroupName)
+       reg.usr("DEFANG_SPOOLDIR", BtPathname)
+       reg.usr("DEFAULT_IRC_SERVER", BtIdentifier)
+       reg.usr("DEFAULT_SERIAL_DEVICE", BtPathname)
+       reg.usr("DIALER_GROUP", BtUserGroupName)
+       reg.usr("DJBDNS_AXFR_USER", BtUserGroupName)
+       reg.usr("DJBDNS_CACHE_USER", BtUserGroupName)
+       reg.usr("DJBDNS_LOG_USER", BtUserGroupName)
+       reg.usr("DJBDNS_RBL_USER", BtUserGroupName)
+       reg.usr("DJBDNS_TINY_USER", BtUserGroupName)
+       reg.usr("DJBDNS_DJBDNS_GROUP", BtUserGroupName)
+       reg.usr("DT_LAYOUT", enum("US FI FR GER DV"))
+       reg.usrlist("ELK_GUI", enum("none xaw motif"))
+       reg.usr("EMACS_TYPE", emacsVersions)
+       reg.usr("EXIM_GROUP", BtUserGroupName)
+       reg.usr("EXIM_USER", BtUserGroupName)
+       reg.usrpkg("EXTRACT_USING", enum("bsdtar gtar nbtar pax"))
+       reg.usrlist("FETCH_BEFORE_ARGS", BtShellWord)
+       reg.usrlist("FETCH_AFTER_ARGS", BtShellWord)
+       reg.usrlist("FETCH_RESUME_ARGS", BtShellWord)
+       reg.usrlist("FETCH_OUTPUT_ARGS", BtShellWord)
+       reg.usrpkg("FETCH_USING", enum("auto curl custom fetch ftp manual wget"))
+       reg.usr("FLUXBOX_USE_XINERAMA", enum("YES NO"))
+       reg.usr("FLUXBOX_USE_KDE", enum("YES NO"))
+       reg.usr("FLUXBOX_USE_GNOME", enum("YES NO"))
+       reg.usr("FLUXBOX_USE_XFT", enum("YES NO"))
+       reg.usr("FOX_USE_XUNICODE", enum("YES NO"))
+       reg.usr("FREEWNN_USER", BtUserGroupName)
+       reg.usr("FREEWNN_GROUP", BtUserGroupName)
+       reg.usr("GAMES_USER", BtUserGroupName)
+       reg.usr("GAMES_GROUP", BtUserGroupName)
+       reg.usr("GAMEMODE", BtFileMode)
+       reg.usr("GAMEDIRMODE", BtFileMode)
+       reg.usr("GAMEDATAMODE", BtFileMode)
+       reg.usr("GAMEGRP", BtUserGroupName)
+       reg.usr("GAMEOWN", BtUserGroupName)
+       reg.usr("GRUB_NETWORK_CARDS", BtIdentifier)
+       reg.usr("GRUB_PRESET_COMMAND", enum("bootp dhcp rarp"))
+       reg.usrlist("GRUB_SCAN_ARGS", BtShellWord)
+       reg.usr("HASKELL_COMPILER", enum("ghc"))
+       reg.usr("HOWL_GROUP", BtUserGroupName)
+       reg.usr("HOWL_USER", BtUserGroupName)
+       reg.usr("ICECAST_CHROOTDIR", BtPathname)
+       reg.usr("ICECAST_CHUNKLEN", BtInteger)
+       reg.usr("ICECAST_SOURCE_BUFFSIZE", BtInteger)
+       reg.usr("IMAP_UW_CCLIENT_MBOX_FMT",
                enum("mbox mbx mh mmdf mtx mx news phile tenex unix"))
-       usr("IMAP_UW_MAILSPOOLHOME", BtFileName)
-       usr("IMDICTDIR", BtPathname)
-       usr("INN_DATA_DIR", BtPathname)
-       usr("INN_USER", BtUserGroupName)
-       usr("INN_GROUP", BtUserGroupName)
-       usr("IRCD_HYBRID_NICLEN", BtInteger)
-       usr("IRCD_HYBRID_TOPICLEN", BtInteger)
-       usr("IRCD_HYBRID_SYSLOG_EVENTS", BtUnknown)
-       usr("IRCD_HYBRID_SYSLOG_FACILITY", BtIdentifier)
-       usr("IRCD_HYBRID_MAXCONN", BtInteger)
-       usr("IRCD_HYBRID_IRC_USER", BtUserGroupName)
-       usr("IRCD_HYBRID_IRC_GROUP", BtUserGroupName)
-       usr("IRRD_USE_PGP", enum("5 2"))
-       usr("JABBERD_USER", BtUserGroupName)
-       usr("JABBERD_GROUP", BtUserGroupName)
-       usr("JABBERD_LOGDIR", BtPathname)
-       usr("JABBERD_SPOOLDIR", BtPathname)
-       usr("JABBERD_PIDDIR", BtPathname)
-       usr("JAKARTA_HOME", BtPathname)
-       usr("KERBEROS", BtYes)
-       usr("KERMIT_SUID_UUCP", BtYes)
-       usr("KJS_USE_PCRE", BtYes)
-       usr("KNEWS_DOMAIN_FILE", BtPathname)
-       usr("KNEWS_DOMAIN_NAME", BtIdentifier)
-       usr("LIBDVDCSS_HOMEPAGE", BtHomepage)
-       usrlist("LIBDVDCSS_MASTER_SITES", BtFetchURL)
-       usr("LIBUSB_TYPE", enum("compat native"))
-       usr("LATEX2HTML_ICONPATH", BtURL)
-       usr("LEAFNODE_DATA_DIR", BtPathname)
-       usr("LEAFNODE_USER", BtUserGroupName)
-       usr("LEAFNODE_GROUP", BtUserGroupName)
-       usrlist("LINUX_LOCALES", BtIdentifier)
-       usr("MAILAGENT_DOMAIN", BtIdentifier)
-       usr("MAILAGENT_EMAIL", BtMailAddress)
-       usr("MAILAGENT_FQDN", BtIdentifier)
-       usr("MAILAGENT_ORGANIZATION", BtUnknown)
-       usr("MAJORDOMO_HOMEDIR", BtPathname)
-       usrlist("MAKEINFO_ARGS", BtShellWord)
-       usr("MECAB_CHARSET", BtIdentifier)
-       usr("MEDIATOMB_GROUP", BtUserGroupName)
-       usr("MEDIATOMB_USER", BtUserGroupName)
-       usr("MIREDO_USER", BtUserGroupName)
-       usr("MIREDO_GROUP", BtUserGroupName)
-       usr("MLDONKEY_GROUP", BtUserGroupName)
-       usr("MLDONKEY_HOME", BtPathname)
-       usr("MLDONKEY_USER", BtUserGroupName)
-       usr("MONOTONE_GROUP", BtUserGroupName)
-       usr("MONOTONE_USER", BtUserGroupName)
-       usr("MOTIF_TYPE", enum("motif openmotif lesstif dt"))
-       usr("MOTIF_TYPE_DEFAULT", enum("motif openmotif lesstif dt"))
-       usr("MTOOLS_ENABLE_FLOPPYD", BtYesNo)
-       usr("MYSQL_USER", BtUserGroupName)
-       usr("MYSQL_GROUP", BtUserGroupName)
-       usr("MYSQL_DATADIR", BtPathname)
-       usr("MYSQL_CHARSET", BtIdentifier)
-       usrlist("MYSQL_EXTRA_CHARSET", BtIdentifier)
-       usr("NAGIOS_GROUP", BtUserGroupName)
-       usr("NAGIOS_USER", BtUserGroupName)
-       usr("NAGIOSCMD_GROUP", BtUserGroupName)
-       usr("NAGIOSDIR", BtPathname)
-       usr("NBPAX_PROGRAM_PREFIX", BtUnknown)
-       usr("NMH_EDITOR", BtIdentifier)
-       usr("NMH_MTA", enum("smtp sendmail"))
-       usr("NMH_PAGER", BtIdentifier)
-       usr("NS_PREFERRED", enum("communicator navigator mozilla"))
-       usr("NULLMAILER_USER", BtUserGroupName)
-       usr("NULLMAILER_GROUP", BtUserGroupName)
-       usr("OPENSSH_CHROOT", BtPathname)
-       usr("OPENSSH_USER", BtUserGroupName)
-       usr("OPENSSH_GROUP", BtUserGroupName)
-       usr("P4USER", BtUserGroupName)
-       usr("P4GROUP", BtUserGroupName)
-       usr("P4ROOT", BtPathname)
-       usr("P4PORT", BtInteger)
-       usr("PALMOS_DEFAULT_SDK", enum("1 2 3.1 3.5"))
-       usr("PAPERSIZE", enum("A4 Letter"))
-       usr("PGGROUP", BtUserGroupName)
-       usr("PGUSER", BtUserGroupName)
-       usr("PGHOME", BtPathname)
-       usr("PILRC_USE_GTK", BtYesNo)
-       usr("PKG_JVM_DEFAULT", jvms)
-       usr("POPTOP_USE_MPPE", BtYes)
-       usr("PROCMAIL_MAILSPOOLHOME", BtFileName)
+       reg.usr("IMAP_UW_MAILSPOOLHOME", BtFileName)
+       reg.usr("IMDICTDIR", BtPathname)
+       reg.usr("INN_DATA_DIR", BtPathname)
+       reg.usr("INN_USER", BtUserGroupName)
+       reg.usr("INN_GROUP", BtUserGroupName)
+       reg.usr("IRCD_HYBRID_NICLEN", BtInteger)
+       reg.usr("IRCD_HYBRID_TOPICLEN", BtInteger)
+       reg.usr("IRCD_HYBRID_SYSLOG_EVENTS", BtUnknown)
+       reg.usr("IRCD_HYBRID_SYSLOG_FACILITY", BtIdentifier)
+       reg.usr("IRCD_HYBRID_MAXCONN", BtInteger)
+       reg.usr("IRCD_HYBRID_IRC_USER", BtUserGroupName)
+       reg.usr("IRCD_HYBRID_IRC_GROUP", BtUserGroupName)
+       reg.usr("IRRD_USE_PGP", enum("5 2"))
+       reg.usr("JABBERD_USER", BtUserGroupName)
+       reg.usr("JABBERD_GROUP", BtUserGroupName)
+       reg.usr("JABBERD_LOGDIR", BtPathname)
+       reg.usr("JABBERD_SPOOLDIR", BtPathname)
+       reg.usr("JABBERD_PIDDIR", BtPathname)
+       reg.usr("JAKARTA_HOME", BtPathname)
+       reg.usr("KERBEROS", BtYes)
+       reg.usr("KERMIT_SUID_UUCP", BtYes)
+       reg.usr("KJS_USE_PCRE", BtYes)
+       reg.usr("KNEWS_DOMAIN_FILE", BtPathname)
+       reg.usr("KNEWS_DOMAIN_NAME", BtIdentifier)
+       reg.usr("LIBDVDCSS_HOMEPAGE", BtHomepage)
+       reg.usrlist("LIBDVDCSS_MASTER_SITES", BtFetchURL)
+       reg.usr("LIBUSB_TYPE", enum("compat native"))
+       reg.usr("LATEX2HTML_ICONPATH", BtURL)
+       reg.usr("LEAFNODE_DATA_DIR", BtPathname)
+       reg.usr("LEAFNODE_USER", BtUserGroupName)
+       reg.usr("LEAFNODE_GROUP", BtUserGroupName)
+       reg.usrlist("LINUX_LOCALES", BtIdentifier)
+       reg.usr("MAILAGENT_DOMAIN", BtIdentifier)
+       reg.usr("MAILAGENT_EMAIL", BtMailAddress)
+       reg.usr("MAILAGENT_FQDN", BtIdentifier)
+       reg.usr("MAILAGENT_ORGANIZATION", BtUnknown)
+       reg.usr("MAJORDOMO_HOMEDIR", BtPathname)
+       reg.usrlist("MAKEINFO_ARGS", BtShellWord)
+       reg.usr("MECAB_CHARSET", BtIdentifier)
+       reg.usr("MEDIATOMB_GROUP", BtUserGroupName)
+       reg.usr("MEDIATOMB_USER", BtUserGroupName)
+       reg.usr("MIREDO_USER", BtUserGroupName)
+       reg.usr("MIREDO_GROUP", BtUserGroupName)
+       reg.usr("MLDONKEY_GROUP", BtUserGroupName)
+       reg.usr("MLDONKEY_HOME", BtPathname)
+       reg.usr("MLDONKEY_USER", BtUserGroupName)
+       reg.usr("MONOTONE_GROUP", BtUserGroupName)
+       reg.usr("MONOTONE_USER", BtUserGroupName)
+       reg.usr("MOTIF_TYPE", enum("motif openmotif lesstif dt"))
+       reg.usr("MOTIF_TYPE_DEFAULT", enum("motif openmotif lesstif dt"))
+       reg.usr("MTOOLS_ENABLE_FLOPPYD", BtYesNo)
+       reg.usr("MYSQL_USER", BtUserGroupName)
+       reg.usr("MYSQL_GROUP", BtUserGroupName)
+       reg.usr("MYSQL_DATADIR", BtPathname)
+       reg.usr("MYSQL_CHARSET", BtIdentifier)
+       reg.usrlist("MYSQL_EXTRA_CHARSET", BtIdentifier)
+       reg.usr("NAGIOS_GROUP", BtUserGroupName)
+       reg.usr("NAGIOS_USER", BtUserGroupName)
+       reg.usr("NAGIOSCMD_GROUP", BtUserGroupName)
+       reg.usr("NAGIOSDIR", BtPathname)
+       reg.usr("NBPAX_PROGRAM_PREFIX", BtUnknown)
+       reg.usr("NMH_EDITOR", BtIdentifier)
+       reg.usr("NMH_MTA", enum("smtp sendmail"))
+       reg.usr("NMH_PAGER", BtIdentifier)
+       reg.usr("NS_PREFERRED", enum("communicator navigator mozilla"))
+       reg.usr("NULLMAILER_USER", BtUserGroupName)
+       reg.usr("NULLMAILER_GROUP", BtUserGroupName)
+       reg.usr("OPENSSH_CHROOT", BtPathname)
+       reg.usr("OPENSSH_USER", BtUserGroupName)
+       reg.usr("OPENSSH_GROUP", BtUserGroupName)
+       reg.usr("P4USER", BtUserGroupName)
+       reg.usr("P4GROUP", BtUserGroupName)
+       reg.usr("P4ROOT", BtPathname)
+       reg.usr("P4PORT", BtInteger)
+       reg.usr("PALMOS_DEFAULT_SDK", enum("1 2 3.1 3.5"))
+       reg.usr("PAPERSIZE", enum("A4 Letter"))
+       reg.usr("PGGROUP", BtUserGroupName)
+       reg.usr("PGUSER", BtUserGroupName)
+       reg.usr("PGHOME", BtPathname)
+       reg.usr("PILRC_USE_GTK", BtYesNo)
+       reg.usr("PKG_JVM_DEFAULT", jvms)
+       reg.usr("POPTOP_USE_MPPE", BtYes)
+       reg.usr("PROCMAIL_MAILSPOOLHOME", BtFileName)
        // Comma-separated list of string or integer literals.
-       usr("PROCMAIL_TRUSTED_IDS", BtUnknown)
-       usr("PVM_SSH", BtPathname)
-       usr("QMAILDIR", BtPathname)
-       usr("QMAIL_ALIAS_USER", BtUserGroupName)
-       usr("QMAIL_DAEMON_USER", BtUserGroupName)
-       usr("QMAIL_LOG_USER", BtUserGroupName)
-       usr("QMAIL_ROOT_USER", BtUserGroupName)
-       usr("QMAIL_PASSWD_USER", BtUserGroupName)
-       usr("QMAIL_QUEUE_USER", BtUserGroupName)
-       usr("QMAIL_REMOTE_USER", BtUserGroupName)
-       usr("QMAIL_SEND_USER", BtUserGroupName)
-       usr("QMAIL_QMAIL_GROUP", BtUserGroupName)
-       usr("QMAIL_NOFILES_GROUP", BtUserGroupName)
-       usr("QMAIL_QFILTER_TMPDIR", BtPathname)
-       usr("QMAIL_QUEUE_DIR", BtPathname)
-       usr("QMAIL_QUEUE_EXTRA", BtMailAddress)
-       usr("QPOPPER_FAC", BtIdentifier)
-       usr("QPOPPER_USER", BtUserGroupName)
-       usr("QPOPPER_SPOOL_DIR", BtPathname)
-       usr("RASMOL_DEPTH", enum("8 16 32"))
-       usr("RELAY_CTRL_DIR", BtPathname)
-       usr("RPM_DB_PREFIX", BtPathname)
-       usr("RSSH_SCP_PATH", BtPathname)
-       usr("RSSH_SFTP_SERVER_PATH", BtPathname)
-       usr("RSSH_CVS_PATH", BtPathname)
-       usr("RSSH_RDIST_PATH", BtPathname)
-       usr("RSSH_RSYNC_PATH", BtPathname)
-       usrlist("SAWFISH_THEMES", BtFileName)
-       usr("SCREWS_GROUP", BtUserGroupName)
-       usr("SCREWS_USER", BtUserGroupName)
-       usr("SDIST_PAWD", enum("pawd pwd"))
-       usrlist("SERIAL_DEVICES", BtPathname)
-       usr("SILC_CLIENT_WITH_PERL", BtYesNo)
-       usr("SNIPROXY_USER", BtUserGroupName)
-       usr("SNIPROXY_GROUP", BtUserGroupName)
-       usr("SSH_SUID", BtYesNo)
-       usr("SSYNC_PAWD", enum("pawd pwd"))
-       usr("SUSE_PREFER", enum("13.1 12.1 10.0")) // TODO: extract
-       usr("TEXMFSITE", BtPathname)
-       usr("THTTPD_LOG_FACILITY", BtIdentifier)
-       usr("UCSPI_SSL_USER", BtUserGroupName)
-       usr("UCSPI_SSL_GROUP", BtUserGroupName)
-       usr("UNPRIVILEGED", BtYesNo)
-       usr("USE_CROSS_COMPILE", BtYesNo)
-       usr("USERPPP_GROUP", BtUserGroupName)
-       usr("UUCP_GROUP", BtUserGroupName)
-       usr("UUCP_USER", BtUserGroupName)
-       usrlist("VIM_EXTRA_OPTS", BtShellWord)
-       usr("WCALC_HTMLDIR", BtPathname)
-       usr("WCALC_HTMLPATH", BtPathname) // URL path
-       usr("WCALC_CGIDIR", BtPrefixPathname)
-       usr("WCALC_CGIPATH", BtPathname) // URL path
-       usrlist("WDM_MANAGERS", BtIdentifier)
-       usr("X10_PORT", BtPathname)
-       usrpkg("XAW_TYPE", enum("standard 3d xpm neXtaw"))
-       usr("XLOCK_DEFAULT_MODE", BtIdentifier)
-       usr("ZSH_STATIC", BtYes)
+       reg.usr("PROCMAIL_TRUSTED_IDS", BtUnknown)
+       reg.usr("PVM_SSH", BtPathname)
+       reg.usr("QMAILDIR", BtPathname)
+       reg.usr("QMAIL_ALIAS_USER", BtUserGroupName)
+       reg.usr("QMAIL_DAEMON_USER", BtUserGroupName)
+       reg.usr("QMAIL_LOG_USER", BtUserGroupName)
+       reg.usr("QMAIL_ROOT_USER", BtUserGroupName)
+       reg.usr("QMAIL_PASSWD_USER", BtUserGroupName)
+       reg.usr("QMAIL_QUEUE_USER", BtUserGroupName)
+       reg.usr("QMAIL_REMOTE_USER", BtUserGroupName)
+       reg.usr("QMAIL_SEND_USER", BtUserGroupName)
+       reg.usr("QMAIL_QMAIL_GROUP", BtUserGroupName)
+       reg.usr("QMAIL_NOFILES_GROUP", BtUserGroupName)
+       reg.usr("QMAIL_QFILTER_TMPDIR", BtPathname)
+       reg.usr("QMAIL_QUEUE_DIR", BtPathname)
+       reg.usr("QMAIL_QUEUE_EXTRA", BtMailAddress)
+       reg.usr("QPOPPER_FAC", BtIdentifier)
+       reg.usr("QPOPPER_USER", BtUserGroupName)
+       reg.usr("QPOPPER_SPOOL_DIR", BtPathname)
+       reg.usr("RASMOL_DEPTH", enum("8 16 32"))
+       reg.usr("RELAY_CTRL_DIR", BtPathname)
+       reg.usr("RPM_DB_PREFIX", BtPathname)
+       reg.usr("RSSH_SCP_PATH", BtPathname)
+       reg.usr("RSSH_SFTP_SERVER_PATH", BtPathname)
+       reg.usr("RSSH_CVS_PATH", BtPathname)
+       reg.usr("RSSH_RDIST_PATH", BtPathname)
+       reg.usr("RSSH_RSYNC_PATH", BtPathname)
+       reg.usrlist("SAWFISH_THEMES", BtFileName)
+       reg.usr("SCREWS_GROUP", BtUserGroupName)
+       reg.usr("SCREWS_USER", BtUserGroupName)
+       reg.usr("SDIST_PAWD", enum("pawd pwd"))
+       reg.usrlist("SERIAL_DEVICES", BtPathname)
+       reg.usr("SILC_CLIENT_WITH_PERL", BtYesNo)
+       reg.usr("SNIPROXY_USER", BtUserGroupName)
+       reg.usr("SNIPROXY_GROUP", BtUserGroupName)
+       reg.usr("SSH_SUID", BtYesNo)
+       reg.usr("SSYNC_PAWD", enum("pawd pwd"))
+       reg.usr("SUSE_PREFER", enum("13.1 12.1 10.0")) // TODO: extract
+       reg.usr("TEXMFSITE", BtPathname)
+       reg.usr("THTTPD_LOG_FACILITY", BtIdentifier)
+       reg.usr("UCSPI_SSL_USER", BtUserGroupName)
+       reg.usr("UCSPI_SSL_GROUP", BtUserGroupName)
+       reg.usr("UNPRIVILEGED", BtYesNo)
+       reg.usr("USE_CROSS_COMPILE", BtYesNo)
+       reg.usr("USERPPP_GROUP", BtUserGroupName)
+       reg.usr("UUCP_GROUP", BtUserGroupName)
+       reg.usr("UUCP_USER", BtUserGroupName)
+       reg.usrlist("VIM_EXTRA_OPTS", BtShellWord)
+       reg.usr("WCALC_HTMLDIR", BtPathname)
+       reg.usr("WCALC_HTMLPATH", BtPathname) // URL path
+       reg.usr("WCALC_CGIDIR", BtPrefixPathname)
+       reg.usr("WCALC_CGIPATH", BtPathname) // URL path
+       reg.usrlist("WDM_MANAGERS", BtIdentifier)
+       reg.usr("X10_PORT", BtPathname)
+       reg.usrpkg("XAW_TYPE", enum("standard 3d xpm neXtaw"))
+       reg.usr("XLOCK_DEFAULT_MODE", BtIdentifier)
+       reg.usr("ZSH_STATIC", BtYes)
 
        // some other variables, sorted alphabetically
 
@@ -779,391 +781,391 @@ func (reg *VarTypeRegistry) Init(src *Pk
        //  subst, buildlink3, checks. This will make them easier to
        //  analyze and align the permissions.
 
-       sysload(".CURDIR", BtPathname)
-       sysload(".IMPSRC", BtPathname)
-       sys(".TARGET", BtPathname)
-       sys("@", BtPathname)
-       pkglistbl3("ALL_ENV", BtShellWord)
-       pkg("ALTERNATIVES_FILE", BtFileName)
-       pkglist("ALTERNATIVES_SRC", BtPathname)
-       pkg("APACHE_MODULE", BtYes)
-       sys("AR", BtShellCommand)
-       sys("AS", BtShellCommand)
-       pkglist("AUTOCONF_REQD", BtVersion)
-       pkglist("AUTOMAKE_OVERRIDE", BtPathmask)
-       pkglist("AUTOMAKE_REQD", BtVersion)
-       pkg("AUTO_MKDIRS", BtYesNo)
-       usr("BATCH", BtYes)
-       usr("BDB185_DEFAULT", BtUnknown)
-       sys("BDBBASE", BtPathname)
-       pkglist("BDB_ACCEPTED", enum("db1 db2 db3 db4 db5 db6"))
-       usr("BDB_DEFAULT", enum("db1 db2 db3 db4 db5 db6"))
-       syslist("BDB_LIBS", BtLdFlag)
-       sys("BDB_TYPE", enum("db1 db2 db3 db4 db5 db6"))
-       syslist("BIGENDIANPLATFORMS", BtMachinePlatformPattern)
-       sys("BINGRP", BtUserGroupName)
-       sys("BINMODE", BtFileMode)
-       sys("BINOWN", BtUserGroupName)
-       pkglist("BOOTSTRAP_DEPENDS", BtDependencyWithPath)
-       pkg("BOOTSTRAP_PKG", BtYesNo)
+       reg.sysload(".CURDIR", BtPathname)
+       reg.sysload(".IMPSRC", BtPathname)
+       reg.sys(".TARGET", BtPathname)
+       reg.sys("@", BtPathname)
+       reg.pkglistbl3("ALL_ENV", BtShellWord)
+       reg.pkg("ALTERNATIVES_FILE", BtFileName)
+       reg.pkglist("ALTERNATIVES_SRC", BtPathname)
+       reg.pkg("APACHE_MODULE", BtYes)
+       reg.sys("AR", BtShellCommand)
+       reg.sys("AS", BtShellCommand)
+       reg.pkglist("AUTOCONF_REQD", BtVersion)
+       reg.pkglist("AUTOMAKE_OVERRIDE", BtPathmask)
+       reg.pkglist("AUTOMAKE_REQD", BtVersion)
+       reg.pkg("AUTO_MKDIRS", BtYesNo)
+       reg.usr("BATCH", BtYes)
+       reg.usr("BDB185_DEFAULT", BtUnknown)
+       reg.sys("BDBBASE", BtPathname)
+       reg.pkglist("BDB_ACCEPTED", enum("db1 db2 db3 db4 db5 db6"))
+       reg.usr("BDB_DEFAULT", enum("db1 db2 db3 db4 db5 db6"))
+       reg.syslist("BDB_LIBS", BtLdFlag)
+       reg.sys("BDB_TYPE", enum("db1 db2 db3 db4 db5 db6"))
+       reg.syslist("BIGENDIANPLATFORMS", BtMachinePlatformPattern)
+       reg.sys("BINGRP", BtUserGroupName)
+       reg.sys("BINMODE", BtFileMode)
+       reg.sys("BINOWN", BtUserGroupName)
+       reg.pkglist("BOOTSTRAP_DEPENDS", BtDependencyWithPath)
+       reg.pkg("BOOTSTRAP_PKG", BtYesNo)
        // BROKEN should better be a list of messages instead of a simple string.
-       pkgappendrat("BROKEN", BtMessage)
-       pkg("BROKEN_GETTEXT_DETECTION", BtYesNo)
-       pkglistrat("BROKEN_EXCEPT_ON_PLATFORM", BtMachinePlatformPattern)
-       pkglistrat("BROKEN_ON_PLATFORM", BtMachinePlatformPattern)
-       syslist("BSD_MAKE_ENV", BtShellWord)
+       reg.pkgappendrat("BROKEN", BtMessage)
+       reg.pkg("BROKEN_GETTEXT_DETECTION", BtYesNo)
+       reg.pkglistrat("BROKEN_EXCEPT_ON_PLATFORM", BtMachinePlatformPattern)
+       reg.pkglistrat("BROKEN_ON_PLATFORM", BtMachinePlatformPattern)
+       reg.syslist("BSD_MAKE_ENV", BtShellWord)
        // TODO: Align the permissions of the various BUILDLINK_*.* variables with each other.
-       acllist("BUILDLINK_ABI_DEPENDS.*", BtDependency,
+       reg.acllist("BUILDLINK_ABI_DEPENDS.*", BtDependency,
                PackageSettable,
                "buildlink3.mk, builtin.mk: append, use-loadtime",
                "*: append")
-       acllist("BUILDLINK_API_DEPENDS.*", BtDependency,
+       reg.acllist("BUILDLINK_API_DEPENDS.*", BtDependency,
                PackageSettable,
                "buildlink3.mk, builtin.mk: append, use-loadtime",
                "*: append")
-       acl("BUILDLINK_AUTO_DIRS.*", BtYesNo,
+       reg.acl("BUILDLINK_AUTO_DIRS.*", BtYesNo,
                PackageSettable,
                "buildlink3.mk: append",
                "Makefile: set")
-       syslist("BUILDLINK_CFLAGS", BtCFlag)
-       bl3list("BUILDLINK_CFLAGS.*", BtCFlag)
-       acl("BUILDLINK_CONTENTS_FILTER.*", BtShellCommand,
+       reg.syslist("BUILDLINK_CFLAGS", BtCFlag)
+       reg.bl3list("BUILDLINK_CFLAGS.*", BtCFlag)
+       reg.acl("BUILDLINK_CONTENTS_FILTER.*", BtShellCommand,
                PackageSettable,
                "buildlink3.mk: set")
-       syslist("BUILDLINK_CPPFLAGS", BtCFlag)
-       bl3list("BUILDLINK_CPPFLAGS.*", BtCFlag)
-       acllist("BUILDLINK_DEPENDS", BtIdentifier,
+       reg.syslist("BUILDLINK_CPPFLAGS", BtCFlag)
+       reg.bl3list("BUILDLINK_CPPFLAGS.*", BtCFlag)
+       reg.acllist("BUILDLINK_DEPENDS", BtIdentifier,
                PackageSettable,
                "buildlink3.mk: append")
-       acllist("BUILDLINK_DEPMETHOD.*", BtBuildlinkDepmethod,
+       reg.acllist("BUILDLINK_DEPMETHOD.*", BtBuildlinkDepmethod,
                PackageSettable,
                "buildlink3.mk: default, append, use",
                "Makefile, Makefile.*, *.mk: default, set, append")
-       acl("BUILDLINK_DIR", BtPathname,
+       reg.acl("BUILDLINK_DIR", BtPathname,
                PackageSettable,
                "*: use")
-       bl3list("BUILDLINK_FILES.*", BtPathmask)
-       pkgbl3("BUILDLINK_FILES_CMD.*", BtShellCommand)
-       acllist("BUILDLINK_INCDIRS.*", BtPathname,
+       reg.bl3list("BUILDLINK_FILES.*", BtPathmask)
+       reg.pkgbl3("BUILDLINK_FILES_CMD.*", BtShellCommand)
+       reg.acllist("BUILDLINK_INCDIRS.*", BtPathname,
                PackageSettable,
                "buildlink3.mk: default, append",
                "Makefile, Makefile.*, *.mk: use")
-       acl("BUILDLINK_JAVA_PREFIX.*", BtPathname,
+       reg.acl("BUILDLINK_JAVA_PREFIX.*", BtPathname,
                PackageSettable,
                "buildlink3.mk: set, use")
-       acllist("BUILDLINK_LDADD.*", BtLdFlag,
+       reg.acllist("BUILDLINK_LDADD.*", BtLdFlag,
                PackageSettable,
                "builtin.mk: default, set, append, use",
                "buildlink3.mk: append, use",
                "Makefile, Makefile.*, *.mk: use")
-       acllist("BUILDLINK_LDFLAGS", BtLdFlag,
+       reg.acllist("BUILDLINK_LDFLAGS", BtLdFlag,
                PackageSettable,
                "*: use")
-       bl3list("BUILDLINK_LDFLAGS.*", BtLdFlag)
-       acllist("BUILDLINK_LIBDIRS.*", BtPathname,
+       reg.bl3list("BUILDLINK_LDFLAGS.*", BtLdFlag)
+       reg.acllist("BUILDLINK_LIBDIRS.*", BtPathname,
                PackageSettable,
                "buildlink3.mk, builtin.mk: append",
                "Makefile, Makefile.*, *.mk: use")
-       acllist("BUILDLINK_LIBS.*", BtLdFlag,
+       reg.acllist("BUILDLINK_LIBS.*", BtLdFlag,
                PackageSettable,
                "buildlink3.mk: append",
                "Makefile, Makefile.*, *.mk: set, append, use")
-       acllist("BUILDLINK_PASSTHRU_DIRS", BtPathname,
+       reg.acllist("BUILDLINK_PASSTHRU_DIRS", BtPathname,
                PackageSettable,
                "Makefile, Makefile.*, *.mk: append")
-       acllist("BUILDLINK_PASSTHRU_RPATHDIRS", BtPathname,
+       reg.acllist("BUILDLINK_PASSTHRU_RPATHDIRS", BtPathname,
                PackageSettable,
                "Makefile, Makefile.*, *.mk: append")
-       acl("BUILDLINK_PKGSRCDIR.*", BtRelativePkgDir,
+       reg.acl("BUILDLINK_PKGSRCDIR.*", BtRelativePkgDir,
                PackageSettable,
                "buildlink3.mk: default, use-loadtime")
-       acl("BUILDLINK_PREFIX.*", BtPathname,
+       reg.acl("BUILDLINK_PREFIX.*", BtPathname,
                PackageSettable,
                "builtin.mk: set, use",
                "Makefile, Makefile.*, *.mk: use")
-       acllist("BUILDLINK_RPATHDIRS.*", BtPathname,
+       reg.acllist("BUILDLINK_RPATHDIRS.*", BtPathname,
                PackageSettable,
                "buildlink3.mk: append")
-       acllist("BUILDLINK_TARGETS", BtIdentifier,
+       reg.acllist("BUILDLINK_TARGETS", BtIdentifier,
                PackageSettable,
                "Makefile, Makefile.*, *.mk: append")
-       acl("BUILDLINK_FNAME_TRANSFORM.*", BtSedCommands,
+       reg.acl("BUILDLINK_FNAME_TRANSFORM.*", BtSedCommands,
                PackageSettable,
                "Makefile, buildlink3.mk, builtin.mk, options.mk: append")
-       acllist("BUILDLINK_TRANSFORM", BtWrapperTransform,
+       reg.acllist("BUILDLINK_TRANSFORM", BtWrapperTransform,
                PackageSettable,
                "*: append")
-       acllist("BUILDLINK_TRANSFORM.*", BtWrapperTransform,
+       reg.acllist("BUILDLINK_TRANSFORM.*", BtWrapperTransform,
                PackageSettable,
                "*: append")
-       acllist("BUILDLINK_TREE", BtIdentifier,
+       reg.acllist("BUILDLINK_TREE", BtIdentifier,
                PackageSettable,
                "buildlink3.mk: append")
-       acl("BUILDLINK_X11_DIR", BtPathname,
+       reg.acl("BUILDLINK_X11_DIR", BtPathname,
                PackageSettable,
                "*: use")
-       acllist("BUILD_DEFS", BtVariableName,
+       reg.acllist("BUILD_DEFS", BtVariableName,
                PackageSettable,
                "Makefile, Makefile.*, *.mk: append")
-       pkglist("BUILD_DEFS_EFFECTS", BtVariableName)
-       pkglistbl3("BUILD_DEPENDS", BtDependencyWithPath)
-       pkglist("BUILD_DIRS", BtWrksrcSubdirectory)
-       pkglist("BUILD_ENV", BtShellWord)
-       sys("BUILD_MAKE_CMD", BtShellCommand)
-       pkglist("BUILD_MAKE_FLAGS", BtShellWord)
-       pkglist("BUILD_TARGET", BtIdentifier)
-       pkglist("BUILD_TARGET.*", BtIdentifier)
-       pkg("BUILD_USES_MSGFMT", BtYes)
-       acl("BUILTIN_PKG", BtIdentifier,
+       reg.pkglist("BUILD_DEFS_EFFECTS", BtVariableName)
+       reg.pkglistbl3("BUILD_DEPENDS", BtDependencyWithPath)
+       reg.pkglist("BUILD_DIRS", BtWrksrcSubdirectory)
+       reg.pkglist("BUILD_ENV", BtShellWord)
+       reg.sys("BUILD_MAKE_CMD", BtShellCommand)
+       reg.pkglist("BUILD_MAKE_FLAGS", BtShellWord)
+       reg.pkglist("BUILD_TARGET", BtIdentifier)
+       reg.pkglist("BUILD_TARGET.*", BtIdentifier)
+       reg.pkg("BUILD_USES_MSGFMT", BtYes)
+       reg.acl("BUILTIN_PKG", BtIdentifier,
                PackageSettable,
                "builtin.mk: set, use, use-loadtime",
                "Makefile, Makefile.*, *.mk: use, use-loadtime")
-       acl("BUILTIN_PKG.*", BtPkgName,
+       reg.acl("BUILTIN_PKG.*", BtPkgName,
                PackageSettable,
                "builtin.mk: set, use, use-loadtime")
-       pkglistbl3("BUILTIN_FIND_FILES_VAR", BtVariableName)
-       pkglistbl3("BUILTIN_FIND_FILES.*", BtPathname)
-       acl("BUILTIN_FIND_GREP.*", BtUnknown,
+       reg.pkglistbl3("BUILTIN_FIND_FILES_VAR", BtVariableName)
+       reg.pkglistbl3("BUILTIN_FIND_FILES.*", BtPathname)
+       reg.acl("BUILTIN_FIND_GREP.*", BtUnknown,
                PackageSettable,
                "builtin.mk: set")
-       acllist("BUILTIN_FIND_HEADERS_VAR", BtVariableName,
+       reg.acllist("BUILTIN_FIND_HEADERS_VAR", BtVariableName,
                PackageSettable,
                "builtin.mk: set")
-       acllist("BUILTIN_FIND_HEADERS.*", BtPathname,
+       reg.acllist("BUILTIN_FIND_HEADERS.*", BtPathname,
                PackageSettable,
                "builtin.mk: set")
-       acllist("BUILTIN_FIND_LIBS", BtPathname,
+       reg.acllist("BUILTIN_FIND_LIBS", BtPathname,
                PackageSettable,
                "builtin.mk: set")
-       sys("BUILTIN_X11_TYPE", BtUnknown)
-       sys("BUILTIN_X11_VERSION", BtUnknown)
-       pkglist("CATEGORIES", BtCategory)
-       sysload("CC_VERSION", BtMessage)
-       sysload("CC", BtShellCommand)
-       pkglistbl3("CFLAGS", BtCFlag)   // may also be changed by the user
-       pkglistbl3("CFLAGS.*", BtCFlag) // may also be changed by the user
-       acl("CHECK_BUILTIN", BtYesNo,
+       reg.sys("BUILTIN_X11_TYPE", BtUnknown)
+       reg.sys("BUILTIN_X11_VERSION", BtUnknown)
+       reg.pkglist("CATEGORIES", BtCategory)
+       reg.sysload("CC_VERSION", BtMessage)
+       reg.sysload("CC", BtShellCommand)
+       reg.pkglistbl3("CFLAGS", BtCFlag)   // may also be changed by the user
+       reg.pkglistbl3("CFLAGS.*", BtCFlag) // may also be changed by the user
+       reg.acl("CHECK_BUILTIN", BtYesNo,
                PackageSettable,
                "builtin.mk: default",
                "Makefile: set")
-       acl("CHECK_BUILTIN.*", BtYesNo,
+       reg.acl("CHECK_BUILTIN.*", BtYesNo,
                PackageSettable,
                "Makefile, options.mk, buildlink3.mk: set",
                "builtin.mk: default, use-loadtime",
                "*: use-loadtime")
-       pkglist("CHECK_FILES_SKIP", BtBasicRegularExpression)
-       pkg("CHECK_FILES_SUPPORTED", BtYesNo)
-       usr("CHECK_HEADERS", BtYesNo)
-       pkglist("CHECK_HEADERS_SKIP", BtPathmask)
-       usr("CHECK_INTERPRETER", BtYesNo)
-       pkglist("CHECK_INTERPRETER_SKIP", BtPathmask)
-       usr("CHECK_PERMS", BtYesNo)
-       pkglist("CHECK_PERMS_SKIP", BtPathmask)
-       usr("CHECK_PORTABILITY", BtYesNo)
-       pkglist("CHECK_PORTABILITY_SKIP", BtPathmask)
-       usr("CHECK_RELRO", BtYesNo)
-       pkglist("CHECK_RELRO_SKIP", BtPathmask)
-       pkg("CHECK_RELRO_SUPPORTED", BtYesNo)
-       pkg("CHECK_SHLIBS", BtYesNo)
-       pkglist("CHECK_SHLIBS_SKIP", BtPathmask)
-       pkg("CHECK_SHLIBS_SUPPORTED", BtYesNo)
-       pkglist("CHECK_WRKREF_SKIP", BtPathmask)
-       pkg("CMAKE_ARG_PATH", BtPathname)
-       pkglist("CMAKE_ARGS", BtShellWord)
-       pkglist("CMAKE_ARGS.*", BtShellWord)
-       pkglist("CMAKE_DEPENDENCIES_REWRITE", BtPathmask) // Relative to WRKSRC
-       pkglist("CMAKE_MODULE_PATH_OVERRIDE", BtPathmask) // Relative to WRKSRC
-       pkg("CMAKE_PKGSRC_BUILD_FLAGS", BtYesNo)
-       pkglist("CMAKE_PREFIX_PATH", BtPathmask)
-       pkg("CMAKE_USE_GNU_INSTALL_DIRS", BtYesNo)
-       pkg("CMAKE_INSTALL_PREFIX", BtPathname) // The default is ${PREFIX}.
-       pkgappend("COMMENT", BtComment)
-       sys("COMPILE.*", BtShellCommand)
-       sys("COMPILER_RPATH_FLAG", enum("-Wl,-rpath"))
-       pkglist("CONFIGURE_ARGS", BtShellWord)
-       pkglist("CONFIGURE_ARGS.*", BtShellWord)
-       pkglist("CONFIGURE_DIRS", BtWrksrcSubdirectory)
-       pkglistbl3("CONFIGURE_ENV", BtShellWord)
-       pkglistbl3("CONFIGURE_ENV.*", BtShellWord)
-       pkg("CONFIGURE_HAS_INFODIR", BtYesNo)
-       pkg("CONFIGURE_HAS_LIBDIR", BtYesNo)
-       pkg("CONFIGURE_HAS_MANDIR", BtYesNo)
-       pkg("CONFIGURE_SCRIPT", BtPathname)
-       pkglist("CONFIG_GUESS_OVERRIDE", BtPathmask)
-       pkglist("CONFIG_STATUS_OVERRIDE", BtPathmask)
-       pkg("CONFIG_SHELL", BtPathname)
-       pkglist("CONFIG_SUB_OVERRIDE", BtPathmask)
-       pkglist("CONFLICTS", BtDependency)
-       pkgappend("CONF_FILES", BtConfFiles)
-       pkg("CONF_FILES_MODE", enum("0644 0640 0600 0400"))
-       pkglist("CONF_FILES_PERMS", BtPerms)
-       sys("COPY", enum("-c")) // The flag that tells ${INSTALL} to copy a file
-       sys("CPP", BtShellCommand)
-       pkglistbl3("CPPFLAGS", BtCFlag)
-       pkglistbl3("CPPFLAGS.*", BtCFlag)
-       sys("CXX", BtShellCommand)
-       pkglistbl3("CXXFLAGS", BtCFlag)
-       pkglistbl3("CXXFLAGS.*", BtCFlag)
-       pkglistbl3("CWRAPPERS_APPEND.*", BtShellWord)
-       syslist("DEFAULT_DISTFILES", BtFetchURL) // From mk/fetch/bsd.fetch-vars.mk.
-       pkglist("DEINSTALL_SRC", BtPathname)
-       pkglist("DEINSTALL_TEMPLATES", BtPathname)
-       sys("DELAYED_ERROR_MSG", BtShellCommand)
-       sys("DELAYED_WARNING_MSG", BtShellCommand)
-       pkglistbl3("DEPENDS", BtDependencyWithPath)
-       usrlist("DEPENDS_TARGET", BtIdentifier)
-       pkglist("DESCR_SRC", BtPathname)
-       sys("DESTDIR", BtPathname)
-       pkg("DESTDIR_VARNAME", BtVariableName)
-       sys("DEVOSSAUDIO", BtPathname)
-       sys("DEVOSSSOUND", BtPathname)
-       pkglist("DISTFILES", BtFileName)
-       pkg("DISTINFO_FILE", BtRelativePkgPath)
-       pkg("DISTNAME", BtFileName)
-       pkg("DIST_SUBDIR", BtPathname)
-       pkglist("DJB_BUILD_ARGS", BtShellWord)
-       pkglist("DJB_BUILD_TARGETS", BtIdentifier)
-       pkgappend("DJB_CONFIG_CMDS", BtShellCommands)
-       pkglist("DJB_CONFIG_DIRS", BtWrksrcSubdirectory)
-       pkg("DJB_CONFIG_HOME", BtFileName)
-       pkg("DJB_CONFIG_PREFIX", BtPathname)
-       pkglist("DJB_INSTALL_TARGETS", BtIdentifier)
-       pkg("DJB_MAKE_TARGETS", BtYesNo)
-       pkg("DJB_RESTRICTED", BtYesNo)
-       pkg("DJB_SLASHPACKAGE", BtYesNo)
-       pkg("DLOPEN_REQUIRE_PTHREADS", BtYesNo)
-       pkg("DL_AUTO_VARS", BtYes)
-       acllist("DL_LIBS", BtLdFlag,
+       reg.pkglist("CHECK_FILES_SKIP", BtBasicRegularExpression)
+       reg.pkg("CHECK_FILES_SUPPORTED", BtYesNo)
+       reg.usr("CHECK_HEADERS", BtYesNo)
+       reg.pkglist("CHECK_HEADERS_SKIP", BtPathmask)
+       reg.usr("CHECK_INTERPRETER", BtYesNo)
+       reg.pkglist("CHECK_INTERPRETER_SKIP", BtPathmask)
+       reg.usr("CHECK_PERMS", BtYesNo)
+       reg.pkglist("CHECK_PERMS_SKIP", BtPathmask)
+       reg.usr("CHECK_PORTABILITY", BtYesNo)
+       reg.pkglist("CHECK_PORTABILITY_SKIP", BtPathmask)
+       reg.usr("CHECK_RELRO", BtYesNo)
+       reg.pkglist("CHECK_RELRO_SKIP", BtPathmask)
+       reg.pkg("CHECK_RELRO_SUPPORTED", BtYesNo)
+       reg.pkg("CHECK_SHLIBS", BtYesNo)
+       reg.pkglist("CHECK_SHLIBS_SKIP", BtPathmask)
+       reg.pkg("CHECK_SHLIBS_SUPPORTED", BtYesNo)
+       reg.pkglist("CHECK_WRKREF_SKIP", BtPathmask)
+       reg.pkg("CMAKE_ARG_PATH", BtPathname)
+       reg.pkglist("CMAKE_ARGS", BtShellWord)
+       reg.pkglist("CMAKE_ARGS.*", BtShellWord)
+       reg.pkglist("CMAKE_DEPENDENCIES_REWRITE", BtPathmask) // Relative to WRKSRC
+       reg.pkglist("CMAKE_MODULE_PATH_OVERRIDE", BtPathmask) // Relative to WRKSRC
+       reg.pkg("CMAKE_PKGSRC_BUILD_FLAGS", BtYesNo)
+       reg.pkglist("CMAKE_PREFIX_PATH", BtPathmask)
+       reg.pkg("CMAKE_USE_GNU_INSTALL_DIRS", BtYesNo)
+       reg.pkg("CMAKE_INSTALL_PREFIX", BtPathname) // The default is ${PREFIX}.
+       reg.pkgappend("COMMENT", BtComment)
+       reg.sys("COMPILE.*", BtShellCommand)
+       reg.sys("COMPILER_RPATH_FLAG", enum("-Wl,-rpath"))
+       reg.pkglist("CONFIGURE_ARGS", BtShellWord)
+       reg.pkglist("CONFIGURE_ARGS.*", BtShellWord)
+       reg.pkglist("CONFIGURE_DIRS", BtWrksrcSubdirectory)
+       reg.pkglistbl3("CONFIGURE_ENV", BtShellWord)
+       reg.pkglistbl3("CONFIGURE_ENV.*", BtShellWord)
+       reg.pkg("CONFIGURE_HAS_INFODIR", BtYesNo)
+       reg.pkg("CONFIGURE_HAS_LIBDIR", BtYesNo)
+       reg.pkg("CONFIGURE_HAS_MANDIR", BtYesNo)
+       reg.pkg("CONFIGURE_SCRIPT", BtPathname)
+       reg.pkglist("CONFIG_GUESS_OVERRIDE", BtPathmask)
+       reg.pkglist("CONFIG_STATUS_OVERRIDE", BtPathmask)
+       reg.pkg("CONFIG_SHELL", BtPathname)
+       reg.pkglist("CONFIG_SUB_OVERRIDE", BtPathmask)
+       reg.pkglist("CONFLICTS", BtDependency)
+       reg.pkgappend("CONF_FILES", BtConfFiles)
+       reg.pkg("CONF_FILES_MODE", enum("0644 0640 0600 0400"))
+       reg.pkglist("CONF_FILES_PERMS", BtPerms)
+       reg.sys("COPY", enum("-c")) // The flag that tells ${INSTALL} to copy a file
+       reg.sys("CPP", BtShellCommand)
+       reg.pkglistbl3("CPPFLAGS", BtCFlag)
+       reg.pkglistbl3("CPPFLAGS.*", BtCFlag)
+       reg.sys("CXX", BtShellCommand)
+       reg.pkglistbl3("CXXFLAGS", BtCFlag)
+       reg.pkglistbl3("CXXFLAGS.*", BtCFlag)
+       reg.pkglistbl3("CWRAPPERS_APPEND.*", BtShellWord)
+       reg.syslist("DEFAULT_DISTFILES", BtFetchURL) // From mk/fetch/bsd.fetch-vars.mk.
+       reg.pkglist("DEINSTALL_SRC", BtPathname)
+       reg.pkglist("DEINSTALL_TEMPLATES", BtPathname)
+       reg.sys("DELAYED_ERROR_MSG", BtShellCommand)
+       reg.sys("DELAYED_WARNING_MSG", BtShellCommand)
+       reg.pkglistbl3("DEPENDS", BtDependencyWithPath)
+       reg.usrlist("DEPENDS_TARGET", BtIdentifier)
+       reg.pkglist("DESCR_SRC", BtPathname)
+       reg.sys("DESTDIR", BtPathname)
+       reg.pkg("DESTDIR_VARNAME", BtVariableName)
+       reg.sys("DEVOSSAUDIO", BtPathname)
+       reg.sys("DEVOSSSOUND", BtPathname)
+       reg.pkglist("DISTFILES", BtFileName)
+       reg.pkg("DISTINFO_FILE", BtRelativePkgPath)
+       reg.pkg("DISTNAME", BtFileName)
+       reg.pkg("DIST_SUBDIR", BtPathname)
+       reg.pkglist("DJB_BUILD_ARGS", BtShellWord)
+       reg.pkglist("DJB_BUILD_TARGETS", BtIdentifier)
+       reg.pkgappend("DJB_CONFIG_CMDS", BtShellCommands)
+       reg.pkglist("DJB_CONFIG_DIRS", BtWrksrcSubdirectory)
+       reg.pkg("DJB_CONFIG_HOME", BtFileName)
+       reg.pkg("DJB_CONFIG_PREFIX", BtPathname)
+       reg.pkglist("DJB_INSTALL_TARGETS", BtIdentifier)
+       reg.pkg("DJB_MAKE_TARGETS", BtYesNo)
+       reg.pkg("DJB_RESTRICTED", BtYesNo)
+       reg.pkg("DJB_SLASHPACKAGE", BtYesNo)
+       reg.pkg("DLOPEN_REQUIRE_PTHREADS", BtYesNo)
+       reg.pkg("DL_AUTO_VARS", BtYes)
+       reg.acllist("DL_LIBS", BtLdFlag,
                PackageSettable,
                "*: append, use")
-       sys("DOCOWN", BtUserGroupName)
-       sys("DOCGRP", BtUserGroupName)
-       sys("DOCMODE", BtFileMode)
-       sys("DOWNLOADED_DISTFILE", BtPathname)
-       sys("DO_NADA", BtShellCommand)
-       pkg("DYNAMIC_SITES_CMD", BtShellCommand)
-       pkg("DYNAMIC_SITES_SCRIPT", BtPathname)
-       sysbl3("ECHO", BtShellCommand)
-       sysbl3("ECHO_MSG", BtShellCommand)
-       sysbl3("ECHO_N", BtShellCommand)
-       pkg("EGDIR", BtPathname) // Not defined anywhere but used in many places like this.
-       sys("EMACS_BIN", BtPathname)
-       sys("EMACS_ETCPREFIX", BtPathname)
-       sys("EMACS_FLAVOR", enum("emacs xemacs"))
-       sys("EMACS_INFOPREFIX", BtPathname)
-       sys("EMACS_LISPPREFIX", BtPathname)
-       pkglistbl3("EMACS_MODULES", BtIdentifier)
-       sys("EMACS_PKGNAME_PREFIX", BtIdentifier) // Or the empty string.
-       pkglist("EMACS_VERSIONS_ACCEPTED", emacsVersions)
-       sys("EMACS_VERSION_MAJOR", BtInteger)
-       sys("EMACS_VERSION_MINOR", BtInteger)
-       pkglistrat("EMACS_VERSION_REQD", emacsVersions)
-       sys("EMULDIR", BtPathname)
-       sys("EMULSUBDIR", BtPathname)
-       sys("OPSYS_EMULDIR", BtPathname)
-       sys("EMULSUBDIRSLASH", BtPathname)
-       sys("EMUL_ARCH", enum("arm i386 m68k none ns32k sparc vax x86_64"))
-       sys("EMUL_DISTRO", BtIdentifier)
-       sys("EMUL_IS_NATIVE", BtYes)
-       pkglist("EMUL_MODULES.*", BtIdentifier)
-       sys("EMUL_OPSYS", enum("darwin freebsd hpux irix linux osf1 solaris sunos none"))
-       pkg("EMUL_PKG_FMT", enum("plain rpm"))
-       usr("EMUL_PLATFORM", BtEmulPlatform)
-       pkglist("EMUL_PLATFORMS", BtEmulPlatform)
-       usrlist("EMUL_PREFER", BtEmulPlatform)
-       pkglist("EMUL_REQD", BtDependency)
-       usr("EMUL_TYPE.*", enum("native builtin suse suse-10.0 suse-12.1 suse-13.1"))
-       sys("ERROR_CAT", BtShellCommand)
-       sys("ERROR_MSG", BtShellCommand)
-       syslist("EXPORT_SYMBOLS_LDFLAGS", BtLdFlag)
-       sys("EXTRACT_CMD", BtShellCommand)
-       pkg("EXTRACT_DIR", BtPathname)
-       pkg("EXTRACT_DIR.*", BtPathname)
-       pkglist("EXTRACT_ELEMENTS", BtPathmask)
-       pkglist("EXTRACT_ENV", BtShellWord)
-       pkglist("EXTRACT_ONLY", BtPathname)
-       pkglist("EXTRACT_OPTS", BtShellWord)
-       pkglist("EXTRACT_OPTS_BIN", BtShellWord)
-       pkglist("EXTRACT_OPTS_LHA", BtShellWord)
-       pkglist("EXTRACT_OPTS_PAX", BtShellWord)
-       pkglist("EXTRACT_OPTS_RAR", BtShellWord)
-       pkglist("EXTRACT_OPTS_TAR", BtShellWord)
-       pkglist("EXTRACT_OPTS_ZIP", BtShellWord)
-       pkglist("EXTRACT_OPTS_ZOO", BtShellWord)
-       pkg("EXTRACT_SUFX", BtDistSuffix)
-       sys("FAIL_MSG", BtShellCommand)
-       sys("FAMBASE", BtPathname)
-       pkglist("FAM_ACCEPTED", enum("fam gamin"))
-       usr("FAM_DEFAULT", enum("fam gamin"))
-       sys("FAM_TYPE", enum("fam gamin"))
-       pkglist("FETCH_MESSAGE", BtShellWord)
-       pkgload("FILESDIR", BtRelativePkgPath)
-       pkglist("FILES_SUBST", BtShellWord)
-       syslist("FILES_SUBST_SED", BtShellWord)
-       pkglist("FIX_RPATH", BtVariableName)
-       pkglistrat("FLEX_REQD", BtVersion)
-       pkglist("FONTS_DIRS.*", BtPathname)
-       syslist("GAMEDATA_PERMS", BtPerms)
-       syslist("GAMEDIR_PERMS", BtPerms)
-       pkglistbl3rat("GCC_REQD", BtGccReqd)
-       pkgappend("GENERATE_PLIST", BtShellCommands)
-       pkg("GITHUB_PROJECT", BtIdentifier)
-       pkg("GITHUB_TAG", BtIdentifier)
-       pkg("GITHUB_RELEASE", BtFileName)
-       pkg("GITHUB_TYPE", enum("tag release"))
-       pkgrat("GMAKE_REQD", BtVersion)
+       reg.sys("DOCOWN", BtUserGroupName)
+       reg.sys("DOCGRP", BtUserGroupName)
+       reg.sys("DOCMODE", BtFileMode)
+       reg.sys("DOWNLOADED_DISTFILE", BtPathname)
+       reg.sys("DO_NADA", BtShellCommand)
+       reg.pkg("DYNAMIC_SITES_CMD", BtShellCommand)
+       reg.pkg("DYNAMIC_SITES_SCRIPT", BtPathname)
+       reg.sysbl3("ECHO", BtShellCommand)
+       reg.sysbl3("ECHO_MSG", BtShellCommand)
+       reg.sysbl3("ECHO_N", BtShellCommand)
+       reg.pkg("EGDIR", BtPathname) // Not defined anywhere but used in many places like this.
+       reg.sys("EMACS_BIN", BtPathname)
+       reg.sys("EMACS_ETCPREFIX", BtPathname)
+       reg.sys("EMACS_FLAVOR", enum("emacs xemacs"))
+       reg.sys("EMACS_INFOPREFIX", BtPathname)
+       reg.sys("EMACS_LISPPREFIX", BtPathname)
+       reg.pkglistbl3("EMACS_MODULES", BtIdentifier)
+       reg.sys("EMACS_PKGNAME_PREFIX", BtIdentifier) // Or the empty string.
+       reg.pkglist("EMACS_VERSIONS_ACCEPTED", emacsVersions)
+       reg.sys("EMACS_VERSION_MAJOR", BtInteger)
+       reg.sys("EMACS_VERSION_MINOR", BtInteger)
+       reg.pkglistrat("EMACS_VERSION_REQD", emacsVersions)
+       reg.sys("EMULDIR", BtPathname)
+       reg.sys("EMULSUBDIR", BtPathname)
+       reg.sys("OPSYS_EMULDIR", BtPathname)
+       reg.sys("EMULSUBDIRSLASH", BtPathname)
+       reg.sys("EMUL_ARCH", enum("arm i386 m68k none ns32k sparc vax x86_64"))
+       reg.sys("EMUL_DISTRO", BtIdentifier)
+       reg.sys("EMUL_IS_NATIVE", BtYes)
+       reg.pkglist("EMUL_MODULES.*", BtIdentifier)
+       reg.sys("EMUL_OPSYS", enum("darwin freebsd hpux irix linux osf1 solaris sunos none"))
+       reg.pkg("EMUL_PKG_FMT", enum("plain rpm"))
+       reg.usr("EMUL_PLATFORM", BtEmulPlatform)
+       reg.pkglist("EMUL_PLATFORMS", BtEmulPlatform)
+       reg.usrlist("EMUL_PREFER", BtEmulPlatform)
+       reg.pkglist("EMUL_REQD", BtDependency)
+       reg.usr("EMUL_TYPE.*", enum("native builtin suse suse-10.0 suse-12.1 suse-13.1"))
+       reg.sys("ERROR_CAT", BtShellCommand)
+       reg.sys("ERROR_MSG", BtShellCommand)
+       reg.syslist("EXPORT_SYMBOLS_LDFLAGS", BtLdFlag)
+       reg.sys("EXTRACT_CMD", BtShellCommand)
+       reg.pkg("EXTRACT_DIR", BtPathname)
+       reg.pkg("EXTRACT_DIR.*", BtPathname)
+       reg.pkglist("EXTRACT_ELEMENTS", BtPathmask)
+       reg.pkglist("EXTRACT_ENV", BtShellWord)
+       reg.pkglist("EXTRACT_ONLY", BtPathname)
+       reg.pkglist("EXTRACT_OPTS", BtShellWord)
+       reg.pkglist("EXTRACT_OPTS_BIN", BtShellWord)
+       reg.pkglist("EXTRACT_OPTS_LHA", BtShellWord)
+       reg.pkglist("EXTRACT_OPTS_PAX", BtShellWord)
+       reg.pkglist("EXTRACT_OPTS_RAR", BtShellWord)
+       reg.pkglist("EXTRACT_OPTS_TAR", BtShellWord)
+       reg.pkglist("EXTRACT_OPTS_ZIP", BtShellWord)
+       reg.pkglist("EXTRACT_OPTS_ZOO", BtShellWord)
+       reg.pkg("EXTRACT_SUFX", BtDistSuffix)
+       reg.sys("FAIL_MSG", BtShellCommand)
+       reg.sys("FAMBASE", BtPathname)
+       reg.pkglist("FAM_ACCEPTED", enum("fam gamin"))
+       reg.usr("FAM_DEFAULT", enum("fam gamin"))
+       reg.sys("FAM_TYPE", enum("fam gamin"))
+       reg.pkglist("FETCH_MESSAGE", BtShellWord)
+       reg.pkgload("FILESDIR", BtRelativePkgPath)
+       reg.pkglist("FILES_SUBST", BtShellWord)
+       reg.syslist("FILES_SUBST_SED", BtShellWord)
+       reg.pkglist("FIX_RPATH", BtVariableName)
+       reg.pkglistrat("FLEX_REQD", BtVersion)
+       reg.pkglist("FONTS_DIRS.*", BtPathname)
+       reg.syslist("GAMEDATA_PERMS", BtPerms)
+       reg.syslist("GAMEDIR_PERMS", BtPerms)
+       reg.pkglistbl3rat("GCC_REQD", BtGccReqd)
+       reg.pkgappend("GENERATE_PLIST", BtShellCommands)
+       reg.pkg("GITHUB_PROJECT", BtIdentifier)
+       reg.pkg("GITHUB_TAG", BtIdentifier)
+       reg.pkg("GITHUB_RELEASE", BtFileName)
+       reg.pkg("GITHUB_TYPE", enum("tag release"))
+       reg.pkgrat("GMAKE_REQD", BtVersion)
        // Some packages need to set GNU_ARCH.i386 to either i486 or i586.
-       pkg("GNU_ARCH.*", BtIdentifier)
+       reg.pkg("GNU_ARCH.*", BtIdentifier)
        // GNU_CONFIGURE needs to be tested in some buildlink3.mk files,
        // such as lang/vala.
-       acl("GNU_CONFIGURE", BtYes,
+       reg.acl("GNU_CONFIGURE", BtYes,
                PackageSettable,
                "buildlink3.mk: none",
                "builtin.mk: use, use-loadtime",
                "Makefile, Makefile.*, *.mk: default, set, use, use-loadtime")
-       pkg("GNU_CONFIGURE_INFODIR", BtPathname)
-       pkg("GNU_CONFIGURE_LIBDIR", BtPathname)
-       pkg("GNU_CONFIGURE_LIBSUBDIR", BtPathname)
-       pkg("GNU_CONFIGURE_MANDIR", BtPathname)
-       pkg("GNU_CONFIGURE_PREFIX", BtPathname)
-       pkg("GOPATH", BtPathname)
-       pkgload("HAS_CONFIGURE", BtYes)
-       pkglist("HEADER_TEMPLATES", BtPathname)
-       pkg("HOMEPAGE", BtHomepage)
-       pkg("ICON_THEMES", BtYes)
-       acl("IGNORE_PKG.*", BtYes,
+       reg.pkg("GNU_CONFIGURE_INFODIR", BtPathname)
+       reg.pkg("GNU_CONFIGURE_LIBDIR", BtPathname)
+       reg.pkg("GNU_CONFIGURE_LIBSUBDIR", BtPathname)
+       reg.pkg("GNU_CONFIGURE_MANDIR", BtPathname)
+       reg.pkg("GNU_CONFIGURE_PREFIX", BtPathname)
+       reg.pkg("GOPATH", BtPathname)
+       reg.pkgload("HAS_CONFIGURE", BtYes)
+       reg.pkglist("HEADER_TEMPLATES", BtPathname)
+       reg.pkg("HOMEPAGE", BtHomepage)
+       reg.pkg("ICON_THEMES", BtYes)
+       reg.acl("IGNORE_PKG.*", BtYes,
                PackageSettable,
                "*: set, use-loadtime")
-       sys("IMAKE", BtShellCommand)
-       pkglistbl3rat("INCOMPAT_CURSES", BtMachinePlatformPattern)
-       sys("INFO_DIR", BtPathname) // relative to PREFIX
-       pkg("INFO_FILES", BtYes)
-       sys("INSTALL", BtShellCommand)
-       pkglist("INSTALLATION_DIRS", BtPrefixPathname)
-       pkg("INSTALLATION_DIRS_FROM_PLIST", BtYes)
-       sys("INSTALL_DATA", BtShellCommand)
-       sys("INSTALL_DATA_DIR", BtShellCommand)
-       pkglist("INSTALL_DIRS", BtWrksrcSubdirectory)
-       pkglist("INSTALL_ENV", BtShellWord)
-       pkg("INSTALL_FILE", BtPathname)
-       sys("INSTALL_GAME", BtShellCommand)
-       sys("INSTALL_GAME_DATA", BtShellCommand)
-       sys("INSTALL_LIB", BtShellCommand)
-       sys("INSTALL_LIB_DIR", BtShellCommand)
-       pkglist("INSTALL_MAKE_FLAGS", BtShellWord)
-       sys("INSTALL_MAN", BtShellCommand)
-       sys("INSTALL_MAN_DIR", BtShellCommand)
-       sys("INSTALL_PROGRAM", BtShellCommand)
-       sys("INSTALL_PROGRAM_DIR", BtShellCommand)
-       sys("INSTALL_SCRIPT", BtShellCommand)
-       syslist("INSTALL_SCRIPTS_ENV", BtShellWord)
-       sys("INSTALL_SCRIPT_DIR", BtShellCommand)
-       pkglist("INSTALL_SRC", BtPathname)
-       pkglist("INSTALL_TARGET", BtIdentifier)
-       pkglist("INSTALL_TEMPLATES", BtPathname)
-       pkgload("INSTALL_UNSTRIPPED", BtYesNo)
-       pkglist("INTERACTIVE_STAGE", enum("fetch extract configure build test install"))
-       acl("IS_BUILTIN.*", BtYesNoIndirectly,
+       reg.sys("IMAKE", BtShellCommand)
+       reg.pkglistbl3rat("INCOMPAT_CURSES", BtMachinePlatformPattern)
+       reg.sys("INFO_DIR", BtPathname) // relative to PREFIX
+       reg.pkg("INFO_FILES", BtYes)
+       reg.sys("INSTALL", BtShellCommand)
+       reg.pkglist("INSTALLATION_DIRS", BtPrefixPathname)
+       reg.pkg("INSTALLATION_DIRS_FROM_PLIST", BtYes)
+       reg.sys("INSTALL_DATA", BtShellCommand)
+       reg.sys("INSTALL_DATA_DIR", BtShellCommand)
+       reg.pkglist("INSTALL_DIRS", BtWrksrcSubdirectory)
+       reg.pkglist("INSTALL_ENV", BtShellWord)
+       reg.pkg("INSTALL_FILE", BtPathname)
+       reg.sys("INSTALL_GAME", BtShellCommand)
+       reg.sys("INSTALL_GAME_DATA", BtShellCommand)
+       reg.sys("INSTALL_LIB", BtShellCommand)
+       reg.sys("INSTALL_LIB_DIR", BtShellCommand)
+       reg.pkglist("INSTALL_MAKE_FLAGS", BtShellWord)
+       reg.sys("INSTALL_MAN", BtShellCommand)
+       reg.sys("INSTALL_MAN_DIR", BtShellCommand)
+       reg.sys("INSTALL_PROGRAM", BtShellCommand)
+       reg.sys("INSTALL_PROGRAM_DIR", BtShellCommand)
+       reg.sys("INSTALL_SCRIPT", BtShellCommand)
+       reg.syslist("INSTALL_SCRIPTS_ENV", BtShellWord)
+       reg.sys("INSTALL_SCRIPT_DIR", BtShellCommand)
+       reg.pkglist("INSTALL_SRC", BtPathname)
+       reg.pkglist("INSTALL_TARGET", BtIdentifier)
+       reg.pkglist("INSTALL_TEMPLATES", BtPathname)
+       reg.pkgload("INSTALL_UNSTRIPPED", BtYesNo)
+       reg.pkglist("INTERACTIVE_STAGE", enum("fetch extract configure build test install"))
+       reg.acl("IS_BUILTIN.*", BtYesNoIndirectly,
                PackageSettable,
                // These two differ from the standard,
                // they are needed for devel/ncursesw.
@@ -1171,70 +1173,70 @@ func (reg *VarTypeRegistry) Init(src *Pk
                // The "set" differs from the standard sys.
                "builtin.mk: set, use, use-loadtime",
                "Makefile, Makefile.*, *.mk: default, set, use, use-loadtime")
-       sys("JAVA_BINPREFIX", BtPathname)
-       pkg("JAVA_CLASSPATH", BtShellWord)
-       pkg("JAVA_HOME", BtPathname)
-       pkg("JAVA_NAME", BtFileName)
-       pkglist("JAVA_UNLIMIT", enum("cmdsize datasize stacksize"))
-       pkglist("JAVA_WRAPPERS", BtFileName)
-       pkg("JAVA_WRAPPER_BIN.*", BtPathname)
-       sys("KRB5BASE", BtPathname)
-       pkglist("KRB5_ACCEPTED", enum("heimdal mit-krb5"))
-       usr("KRB5_DEFAULT", enum("heimdal mit-krb5"))
-       sys("KRB5_TYPE", BtIdentifier)
-       sys("LD", BtShellCommand)
-       pkglistbl3("LDFLAGS", BtLdFlag)       // May also be changed by the user.
-       pkglistbl3("LDFLAGS.*", BtLdFlag)     // May also be changed by the user.
-       sysload("LIBABISUFFIX", BtIdentifier) // Can also be empty.
-       sys("LIBGRP", BtUserGroupName)
-       sys("LIBMODE", BtFileMode)
-       sys("LIBOWN", BtUserGroupName)
-       sys("LIBOSSAUDIO", BtPathname)
-       pkglist("LIBS", BtLdFlag)
-       pkglist("LIBS.*", BtLdFlag)
-       sys("LIBTOOL", BtShellCommand)
-       pkglist("LIBTOOL_OVERRIDE", BtPathmask)
-       pkglistrat("LIBTOOL_REQD", BtVersion)
-       pkgappend("LICENCE", BtLicense)
-       pkgappend("LICENSE", BtLicense)
-       pkg("LICENSE_FILE", BtPathname)
-       sys("LINK.*", BtShellCommand)
-       sys("LINKER_RPATH_FLAG", BtShellWord)
-       syslist("LITTLEENDIANPLATFORMS", BtMachinePlatformPattern)
-       sys("LOWER_OPSYS", BtIdentifier)
-       sys("LOWER_VENDOR", BtIdentifier)
-       syslist("LP64PLATFORMS", BtMachinePlatformPattern)
-       pkglist("LTCONFIG_OVERRIDE", BtPathmask)
-       sysload("MACHINE_ARCH", enumMachineArch)
-       sysload("MACHINE_GNU_ARCH", enumMachineGnuArch)
-       sysload("MACHINE_GNU_PLATFORM", BtMachineGnuPlatform)
-       sysload("MACHINE_PLATFORM", BtMachinePlatform)
-       pkg("MAINTAINER", BtMailAddress)
-       sysload("MAKE", BtShellCommand)
-       pkglist("MAKEFLAGS", BtShellWord)
-       pkglistbl3("MAKEVARS", BtVariableName)
-       pkglist("MAKE_DIRS", BtPathname)
-       pkglist("MAKE_DIRS_PERMS", BtPerms)
-       pkglistbl3("MAKE_ENV", BtShellWord)
-       pkglistbl3("MAKE_ENV.*", BtShellWord)
-       pkg("MAKE_FILE", BtPathname)
-       pkglist("MAKE_FLAGS", BtShellWord)
-       pkglist("MAKE_FLAGS.*", BtShellWord)
-       pkgrat("MAKE_JOBS_SAFE", BtYesNo)
-       pkg("MAKE_PROGRAM", BtShellCommand)
-       pkg("MANCOMPRESSED", BtYesNo)
-       pkg("MANCOMPRESSED_IF_MANZ", BtYes)
-       sys("MANGRP", BtUserGroupName)
-       sys("MANMODE", BtFileMode)
-       sys("MANOWN", BtUserGroupName)
-       pkglist("MASTER_SITES", BtFetchURL)
+       reg.sys("JAVA_BINPREFIX", BtPathname)
+       reg.pkg("JAVA_CLASSPATH", BtShellWord)
+       reg.pkg("JAVA_HOME", BtPathname)
+       reg.pkg("JAVA_NAME", BtFileName)
+       reg.pkglist("JAVA_UNLIMIT", enum("cmdsize datasize stacksize"))
+       reg.pkglist("JAVA_WRAPPERS", BtFileName)
+       reg.pkg("JAVA_WRAPPER_BIN.*", BtPathname)
+       reg.sys("KRB5BASE", BtPathname)
+       reg.pkglist("KRB5_ACCEPTED", enum("heimdal mit-krb5"))
+       reg.usr("KRB5_DEFAULT", enum("heimdal mit-krb5"))
+       reg.sys("KRB5_TYPE", BtIdentifier)
+       reg.sys("LD", BtShellCommand)
+       reg.pkglistbl3("LDFLAGS", BtLdFlag)       // May also be changed by the user.
+       reg.pkglistbl3("LDFLAGS.*", BtLdFlag)     // May also be changed by the user.
+       reg.sysload("LIBABISUFFIX", BtIdentifier) // Can also be empty.
+       reg.sys("LIBGRP", BtUserGroupName)
+       reg.sys("LIBMODE", BtFileMode)
+       reg.sys("LIBOWN", BtUserGroupName)
+       reg.sys("LIBOSSAUDIO", BtPathname)
+       reg.pkglist("LIBS", BtLdFlag)
+       reg.pkglist("LIBS.*", BtLdFlag)
+       reg.sys("LIBTOOL", BtShellCommand)
+       reg.pkglist("LIBTOOL_OVERRIDE", BtPathmask)
+       reg.pkglistrat("LIBTOOL_REQD", BtVersion)
+       reg.pkgappend("LICENCE", BtLicense)
+       reg.pkgappend("LICENSE", BtLicense)
+       reg.pkg("LICENSE_FILE", BtPathname)
+       reg.sys("LINK.*", BtShellCommand)
+       reg.sys("LINKER_RPATH_FLAG", BtShellWord)
+       reg.syslist("LITTLEENDIANPLATFORMS", BtMachinePlatformPattern)
+       reg.sys("LOWER_OPSYS", BtIdentifier)
+       reg.sys("LOWER_VENDOR", BtIdentifier)
+       reg.syslist("LP64PLATFORMS", BtMachinePlatformPattern)
+       reg.pkglist("LTCONFIG_OVERRIDE", BtPathmask)
+       reg.sysload("MACHINE_ARCH", enumMachineArch)
+       reg.sysload("MACHINE_GNU_ARCH", enumMachineGnuArch)
+       reg.sysload("MACHINE_GNU_PLATFORM", BtMachineGnuPlatform)
+       reg.sysload("MACHINE_PLATFORM", BtMachinePlatform)
+       reg.pkg("MAINTAINER", BtMailAddress)
+       reg.sysload("MAKE", BtShellCommand)
+       reg.pkglist("MAKEFLAGS", BtShellWord)
+       reg.pkglistbl3("MAKEVARS", BtVariableName)
+       reg.pkglist("MAKE_DIRS", BtPathname)
+       reg.pkglist("MAKE_DIRS_PERMS", BtPerms)
+       reg.pkglistbl3("MAKE_ENV", BtShellWord)
+       reg.pkglistbl3("MAKE_ENV.*", BtShellWord)
+       reg.pkg("MAKE_FILE", BtPathname)
+       reg.pkglist("MAKE_FLAGS", BtShellWord)
+       reg.pkglist("MAKE_FLAGS.*", BtShellWord)
+       reg.pkgrat("MAKE_JOBS_SAFE", BtYesNo)
+       reg.pkg("MAKE_PROGRAM", BtShellCommand)
+       reg.pkg("MANCOMPRESSED", BtYesNo)
+       reg.pkg("MANCOMPRESSED_IF_MANZ", BtYes)
+       reg.sys("MANGRP", BtUserGroupName)
+       reg.sys("MANMODE", BtFileMode)
+       reg.sys("MANOWN", BtUserGroupName)
+       reg.pkglist("MASTER_SITES", BtFetchURL)
 
        for _, filename := range []string{"mk/fetch/sites.mk", "mk/fetch/fetch.mk"} {
                sitesMk := LoadMk(src.File(filename), NotEmpty)
                if sitesMk != nil {
                        sitesMk.ForEach(func(mkline MkLine) {
                                if mkline.IsVarassign() && hasPrefix(mkline.Varname(), "MASTER_SITE_") {
-                                       syslist(mkline.Varname(), BtFetchURL)
+                                       reg.syslist(mkline.Varname(), BtFetchURL)
                                }
                        })
                } else {
@@ -1242,174 +1244,174 @@ func (reg *VarTypeRegistry) Init(src *Pk
                }
        }
 
-       pkglist("MESSAGE_SRC", BtPathname)
-       pkglist("MESSAGE_SUBST", BtShellWord)
-       pkg("META_PACKAGE", BtYes)
-       syslist("MISSING_FEATURES", BtIdentifier)
-       pkglist("MYSQL_VERSIONS_ACCEPTED", mysqlVersions)
-       usr("MYSQL_VERSION_DEFAULT", BtVersion)
-       sys("NATIVE_CC", BtShellCommand) // See mk/platform/tools.NetBSD.mk (and some others).
-       sys("NM", BtShellCommand)
-       sys("NONBINMODE", BtFileMode)
-       pkglistrat("NOT_FOR_COMPILER", compilers)
-       pkglistrat("NOT_FOR_BULK_PLATFORM", BtMachinePlatformPattern)
-       pkglistrat("NOT_FOR_PLATFORM", BtMachinePlatformPattern)
-       pkgrat("NOT_FOR_UNPRIVILEGED", BtYesNo)
-       pkglistrat("NOT_PAX_ASLR_SAFE", BtPathmask)
-       pkglistrat("NOT_PAX_MPROTECT_SAFE", BtPathmask)
-       pkg("NO_BIN_ON_CDROM", BtRestricted)
-       pkg("NO_BIN_ON_FTP", BtRestricted)
-       pkgload("NO_BUILD", BtYes)
-       pkg("NO_CHECKSUM", BtYes)
-       pkg("NO_CONFIGURE", BtYes)
-       pkg("NO_EXPORT_CPP", BtYes)
-       pkg("NO_EXTRACT", BtYes)
-       pkg("NO_INSTALL_MANPAGES", BtYes) // only has an effect for Imake packages.
-       pkg("NO_PKGTOOLS_REQD_CHECK", BtYes)
-       pkg("NO_SRC_ON_CDROM", BtRestricted)
-       pkg("NO_SRC_ON_FTP", BtRestricted)
-       sysload("OBJECT_FMT", enum("COFF ECOFF ELF SOM XCOFF Mach-O PE a.out"))
-       pkglistrat("ONLY_FOR_COMPILER", compilers)
-       pkglistrat("ONLY_FOR_PLATFORM", BtMachinePlatformPattern)
-       pkgrat("ONLY_FOR_UNPRIVILEGED", BtYesNo)
-       sysload("OPSYS", enumFromFiles("mk/platform", `(.*)\.mk$`, "$1",
+       reg.pkglist("MESSAGE_SRC", BtPathname)
+       reg.pkglist("MESSAGE_SUBST", BtShellWord)
+       reg.pkg("META_PACKAGE", BtYes)
+       reg.syslist("MISSING_FEATURES", BtIdentifier)
+       reg.pkglist("MYSQL_VERSIONS_ACCEPTED", mysqlVersions)
+       reg.usr("MYSQL_VERSION_DEFAULT", BtVersion)
+       reg.sys("NATIVE_CC", BtShellCommand) // See mk/platform/tools.NetBSD.mk (and some others).
+       reg.sys("NM", BtShellCommand)
+       reg.sys("NONBINMODE", BtFileMode)
+       reg.pkglistrat("NOT_FOR_COMPILER", compilers)
+       reg.pkglistrat("NOT_FOR_BULK_PLATFORM", BtMachinePlatformPattern)
+       reg.pkglistrat("NOT_FOR_PLATFORM", BtMachinePlatformPattern)
+       reg.pkgrat("NOT_FOR_UNPRIVILEGED", BtYesNo)
+       reg.pkglistrat("NOT_PAX_ASLR_SAFE", BtPathmask)
+       reg.pkglistrat("NOT_PAX_MPROTECT_SAFE", BtPathmask)
+       reg.pkg("NO_BIN_ON_CDROM", BtRestricted)
+       reg.pkg("NO_BIN_ON_FTP", BtRestricted)
+       reg.pkgload("NO_BUILD", BtYes)
+       reg.pkg("NO_CHECKSUM", BtYes)
+       reg.pkg("NO_CONFIGURE", BtYes)
+       reg.pkg("NO_EXPORT_CPP", BtYes)
+       reg.pkg("NO_EXTRACT", BtYes)
+       reg.pkg("NO_INSTALL_MANPAGES", BtYes) // only has an effect for Imake packages.
+       reg.pkg("NO_PKGTOOLS_REQD_CHECK", BtYes)
+       reg.pkg("NO_SRC_ON_CDROM", BtRestricted)
+       reg.pkg("NO_SRC_ON_FTP", BtRestricted)
+       reg.sysload("OBJECT_FMT", enum("COFF ECOFF ELF SOM XCOFF Mach-O PE a.out"))
+       reg.pkglistrat("ONLY_FOR_COMPILER", compilers)
+       reg.pkglistrat("ONLY_FOR_PLATFORM", BtMachinePlatformPattern)
+       reg.pkgrat("ONLY_FOR_UNPRIVILEGED", BtYesNo)
+       reg.sysload("OPSYS", reg.enumFromFiles("mk/platform", `(.*)\.mk$`, "$1",
                "Cygwin DragonFly FreeBSD Linux NetBSD SunOS"))
-       pkglistbl3("OPSYSVARS", BtVariableName)
-       pkg("OSVERSION_SPECIFIC", BtYes)
-       sysload("OS_VERSION", BtVersion)
-       sysload("OSX_VERSION", BtVersion) // See mk/platform/Darwin.mk.
-       pkg("OVERRIDE_DIRDEPTH*", BtInteger)
-       pkg("OVERRIDE_GNU_CONFIG_SCRIPTS", BtYes)
-       pkg("OWNER", BtMailAddress)
-       pkglist("OWN_DIRS", BtPathmask)
-       pkglist("OWN_DIRS_PERMS", BtPerms)
-       sys("PAMBASE", BtPathname)
-       usr("PAM_DEFAULT", enum("linux-pam openpam solaris-pam"))
-       pkgload("PATCHDIR", BtRelativePkgPath)
-       pkglist("PATCHFILES", BtFileName)
-       pkglist("PATCH_ARGS", BtShellWord)
-       pkglist("PATCH_DIST_ARGS", BtShellWord)
-       pkg("PATCH_DIST_CAT", BtShellCommand)
-       pkg("PATCH_DIST_STRIP*", BtShellWord)
-       pkglist("PATCH_SITES", BtFetchURL)
-       pkg("PATCH_STRIP", BtShellWord)
-       sysload("PATH", BtPathlist)   // From the PATH environment variable.
-       sys("PAXCTL", BtShellCommand) // See mk/pax.mk.
-       pkglist("PERL5_PACKLIST", BtPerl5Packlist)
-       pkg("PERL5_PACKLIST_DIR", BtPathname)
-       pkglistrat("PERL5_REQD", BtVersion)
-       sysbl3("PERL5_INSTALLARCHLIB", BtPathname) // See lang/perl5/vars.mk
-       sysbl3("PERL5_INSTALLSCRIPT", BtPathname)
-       sysbl3("PERL5_INSTALLVENDORBIN", BtPathname)
-       sysbl3("PERL5_INSTALLVENDORSCRIPT", BtPathname)
-       sysbl3("PERL5_INSTALLVENDORARCH", BtPathname)
-       sysbl3("PERL5_INSTALLVENDORLIB", BtPathname)
-       sysbl3("PERL5_INSTALLVENDORMAN1DIR", BtPathname)
-       sysbl3("PERL5_INSTALLVENDORMAN3DIR", BtPathname)
-       sysbl3("PERL5_SUB_INSTALLARCHLIB", BtPrefixPathname) // See lang/perl5/vars.mk
-       sysbl3("PERL5_SUB_INSTALLSCRIPT", BtPrefixPathname)
-       sysbl3("PERL5_SUB_INSTALLVENDORBIN", BtPrefixPathname)
-       sysbl3("PERL5_SUB_INSTALLVENDORSCRIPT", BtPrefixPathname)
-       sysbl3("PERL5_SUB_INSTALLVENDORARCH", BtPrefixPathname)
-       sysbl3("PERL5_SUB_INSTALLVENDORLIB", BtPrefixPathname)
-       sysbl3("PERL5_SUB_INSTALLVENDORMAN1DIR", BtPrefixPathname)
-       sysbl3("PERL5_SUB_INSTALLVENDORMAN3DIR", BtPrefixPathname)
-       pkg("PERL5_USE_PACKLIST", BtYesNo)
-       sys("PGSQL_PREFIX", BtPathname)
-       acllist("PGSQL_VERSIONS_ACCEPTED", pgsqlVersions,
+       reg.pkglistbl3("OPSYSVARS", BtVariableName)
+       reg.pkg("OSVERSION_SPECIFIC", BtYes)
+       reg.sysload("OS_VERSION", BtVersion)
+       reg.sysload("OSX_VERSION", BtVersion) // See mk/platform/Darwin.mk.
+       reg.pkg("OVERRIDE_DIRDEPTH*", BtInteger)
+       reg.pkg("OVERRIDE_GNU_CONFIG_SCRIPTS", BtYes)
+       reg.pkg("OWNER", BtMailAddress)
+       reg.pkglist("OWN_DIRS", BtPathmask)
+       reg.pkglist("OWN_DIRS_PERMS", BtPerms)
+       reg.sys("PAMBASE", BtPathname)
+       reg.usr("PAM_DEFAULT", enum("linux-pam openpam solaris-pam"))
+       reg.pkgload("PATCHDIR", BtRelativePkgPath)
+       reg.pkglist("PATCHFILES", BtFileName)
+       reg.pkglist("PATCH_ARGS", BtShellWord)
+       reg.pkglist("PATCH_DIST_ARGS", BtShellWord)
+       reg.pkg("PATCH_DIST_CAT", BtShellCommand)
+       reg.pkg("PATCH_DIST_STRIP*", BtShellWord)
+       reg.pkglist("PATCH_SITES", BtFetchURL)
+       reg.pkg("PATCH_STRIP", BtShellWord)
+       reg.sysload("PATH", BtPathlist)   // From the PATH environment variable.
+       reg.sys("PAXCTL", BtShellCommand) // See mk/pax.mk.
+       reg.pkglist("PERL5_PACKLIST", BtPerl5Packlist)
+       reg.pkg("PERL5_PACKLIST_DIR", BtPathname)
+       reg.pkglistrat("PERL5_REQD", BtVersion)
+       reg.sysbl3("PERL5_INSTALLARCHLIB", BtPathname) // See lang/perl5/vars.mk
+       reg.sysbl3("PERL5_INSTALLSCRIPT", BtPathname)
+       reg.sysbl3("PERL5_INSTALLVENDORBIN", BtPathname)
+       reg.sysbl3("PERL5_INSTALLVENDORSCRIPT", BtPathname)
+       reg.sysbl3("PERL5_INSTALLVENDORARCH", BtPathname)
+       reg.sysbl3("PERL5_INSTALLVENDORLIB", BtPathname)
+       reg.sysbl3("PERL5_INSTALLVENDORMAN1DIR", BtPathname)
+       reg.sysbl3("PERL5_INSTALLVENDORMAN3DIR", BtPathname)
+       reg.sysbl3("PERL5_SUB_INSTALLARCHLIB", BtPrefixPathname) // See lang/perl5/vars.mk
+       reg.sysbl3("PERL5_SUB_INSTALLSCRIPT", BtPrefixPathname)
+       reg.sysbl3("PERL5_SUB_INSTALLVENDORBIN", BtPrefixPathname)
+       reg.sysbl3("PERL5_SUB_INSTALLVENDORSCRIPT", BtPrefixPathname)
+       reg.sysbl3("PERL5_SUB_INSTALLVENDORARCH", BtPrefixPathname)
+       reg.sysbl3("PERL5_SUB_INSTALLVENDORLIB", BtPrefixPathname)
+       reg.sysbl3("PERL5_SUB_INSTALLVENDORMAN1DIR", BtPrefixPathname)
+       reg.sysbl3("PERL5_SUB_INSTALLVENDORMAN3DIR", BtPrefixPathname)
+       reg.pkg("PERL5_USE_PACKLIST", BtYesNo)
+       reg.sys("PGSQL_PREFIX", BtPathname)
+       reg.acllist("PGSQL_VERSIONS_ACCEPTED", pgsqlVersions,
                PackageSettable|NeedsRationale,
                // The "set" is necessary for databases/postgresql-postgis2.
                "Makefile, Makefile.*, *.mk: default, set, append, use")
-       usr("PGSQL_VERSION_DEFAULT", BtVersion)
-       sys("PG_LIB_EXT", enum("dylib so"))
-       sys("PGSQL_TYPE",
-               enumFrom("mk/pgsql.buildlink3.mk", "postgresql11-client", "PGSQL_TYPE"))
-       sys("PGPKGSRCDIR", BtPathname)
-       sys("PHASE_MSG", BtShellCommand)
-       usr("PHP_VERSION_REQD", BtVersion)
-       acl("PHP_PKG_PREFIX",
-               enumFromDirs("lang", `^php(\d+)$`, "php$1", "php56 php71 php72 php73"),
+       reg.usr("PGSQL_VERSION_DEFAULT", BtVersion)
+       reg.sys("PG_LIB_EXT", enum("dylib so"))
+       reg.sys("PGSQL_TYPE",
+               reg.enumFrom(src, "mk/pgsql.buildlink3.mk", "postgresql11-client", "PGSQL_TYPE"))
+       reg.sys("PGPKGSRCDIR", BtPathname)
+       reg.sys("PHASE_MSG", BtShellCommand)
+       reg.usr("PHP_VERSION_REQD", BtVersion)
+       reg.acl("PHP_PKG_PREFIX",
+               reg.enumFromDirs(src, "lang", `^php(\d+)$`, "php$1", "php56 php71 php72 php73"),
                SystemProvided,
                "special:phpversion.mk: set",
                "*: use, use-loadtime")
-       sys("PKGBASE", BtIdentifier)
+       reg.sys("PKGBASE", BtIdentifier)
        // Despite its name, this is actually a list of filenames.
-       acllist("PKGCONFIG_FILE.*", BtPathname,
+       reg.acllist("PKGCONFIG_FILE.*", BtPathname,
                PackageSettable,
                "builtin.mk: set, append",
                "special:pkgconfig-builtin.mk: use-loadtime")
-       pkglist("PKGCONFIG_OVERRIDE", BtPathmask)
-       pkg("PKGCONFIG_OVERRIDE_STAGE", BtStage)
-       pkg("PKGDIR", BtRelativePkgDir)
-       sys("PKGDIRMODE", BtFileMode)
-       sys("PKGLOCALEDIR", BtPathname)
-       pkg("PKGNAME", BtPkgName)
-       sys("PKGNAME_NOREV", BtPkgName)
-       sysload("PKGPATH", BtPathname)
-       sys("PKGREPOSITORY", BtUnknown)
+       reg.pkglist("PKGCONFIG_OVERRIDE", BtPathmask)
+       reg.pkg("PKGCONFIG_OVERRIDE_STAGE", BtStage)
+       reg.pkg("PKGDIR", BtRelativePkgDir)
+       reg.sys("PKGDIRMODE", BtFileMode)
+       reg.sys("PKGLOCALEDIR", BtPathname)
+       reg.pkg("PKGNAME", BtPkgName)
+       reg.sys("PKGNAME_NOREV", BtPkgName)
+       reg.sysload("PKGPATH", BtPathname)
+       reg.sys("PKGREPOSITORY", BtUnknown)
        // This variable is special in that it really only makes sense to
        // be set in a package Makefile.
        // See VartypeCheck.PkgRevision for details.
-       acl("PKGREVISION", BtPkgRevision,
+       reg.acl("PKGREVISION", BtPkgRevision,
                PackageSettable,
                "Makefile: set")
-       sys("PKGSRCDIR", BtPathname)
+       reg.sys("PKGSRCDIR", BtPathname)
        // This definition is only valid in the top-level Makefile,
        // not in category or package Makefiles.
-       acl("PKGSRCTOP", BtYes,
+       reg.acl("PKGSRCTOP", BtYes,
                PackageSettable,
                "Makefile: set")
-       sys("PKGSRC_SETENV", BtShellCommand)
-       syslist("PKGTOOLS_ENV", BtShellWord)
-       sys("PKGVERSION", BtVersion)
-       sys("PKGVERSION_NOREV", BtVersion) // Without the nb* part.
-       sys("PKGWILDCARD", BtFileMask)
-       sysload("PKG_ADMIN", BtShellCommand)
-       sys("PKG_APACHE", enum("apache24"))
-       pkglistrat("PKG_APACHE_ACCEPTED", enum("apache24"))
-       usr("PKG_APACHE_DEFAULT", enum("apache24"))
-       sysloadlist("PKG_BUILD_OPTIONS.*", BtOption)
-       usr("PKG_CONFIG", BtYes)
+       reg.sys("PKGSRC_SETENV", BtShellCommand)
+       reg.syslist("PKGTOOLS_ENV", BtShellWord)
+       reg.sys("PKGVERSION", BtVersion)
+       reg.sys("PKGVERSION_NOREV", BtVersion) // Without the nb* part.
+       reg.sys("PKGWILDCARD", BtFileMask)
+       reg.sysload("PKG_ADMIN", BtShellCommand)
+       reg.sys("PKG_APACHE", enum("apache24"))
+       reg.pkglistrat("PKG_APACHE_ACCEPTED", enum("apache24"))
+       reg.usr("PKG_APACHE_DEFAULT", enum("apache24"))
+       reg.sysloadlist("PKG_BUILD_OPTIONS.*", BtOption)
+       reg.usr("PKG_CONFIG", BtYes)
        // ^^ No, this is not the popular command from GNOME, but the setting
        // whether the pkgsrc user wants configuration files automatically
        // installed or not.
-       sys("PKG_CREATE", BtShellCommand)
-       sys("PKG_DBDIR", BtPathname)
-       cmdline("PKG_DEBUG_LEVEL", BtInteger)
-       usrlist("PKG_DEFAULT_OPTIONS", BtOption)
-       sys("PKG_DELETE", BtShellCommand)
-       pkglist("PKG_DESTDIR_SUPPORT", enum("destdir user-destdir"))
-       pkglist("PKG_FAIL_REASON", BtShellWord)
-       sysload("PKG_FORMAT", BtIdentifier)
-       pkg("PKG_GECOS.*", BtMessage)
-       pkg("PKG_GID.*", BtInteger)
-       pkglist("PKG_GROUPS", BtShellWord)
-       pkglist("PKG_GROUPS_VARS", BtVariableName)
-       pkg("PKG_HOME.*", BtPathname)
+       reg.sys("PKG_CREATE", BtShellCommand)
+       reg.sys("PKG_DBDIR", BtPathname)
+       reg.cmdline("PKG_DEBUG_LEVEL", BtInteger)
+       reg.usrlist("PKG_DEFAULT_OPTIONS", BtOption)
+       reg.sys("PKG_DELETE", BtShellCommand)
+       reg.pkglist("PKG_DESTDIR_SUPPORT", enum("destdir user-destdir"))
+       reg.pkglist("PKG_FAIL_REASON", BtShellWord)
+       reg.sysload("PKG_FORMAT", BtIdentifier)
+       reg.pkg("PKG_GECOS.*", BtMessage)
+       reg.pkg("PKG_GID.*", BtInteger)
+       reg.pkglist("PKG_GROUPS", BtShellWord)
+       reg.pkglist("PKG_GROUPS_VARS", BtVariableName)
+       reg.pkg("PKG_HOME.*", BtPathname)
        // PKG_HACKS is used to record the applied hacks in the binary package.
        // Since only the hacks.mk can define hacks, appending to it only makes
        // sense there.
        //
        // TODO: Is it possible that a package includes the hacks.mk file from
        //  one of its dependencies?
-       acllist("PKG_HACKS", BtIdentifier,
+       reg.acllist("PKG_HACKS", BtIdentifier,
                PackageSettable,
                "*: none")
-       sys("PKG_INFO", BtShellCommand)
-       sys("PKG_JAVA_HOME", BtPathname)
-       sys("PKG_JVM", jvms)
-       pkglistrat("PKG_JVMS_ACCEPTED", jvms)
-       pkg("PKG_LIBTOOL", BtPathname)
+       reg.sys("PKG_INFO", BtShellCommand)
+       reg.sys("PKG_JAVA_HOME", BtPathname)
+       reg.sys("PKG_JVM", jvms)
+       reg.pkglistrat("PKG_JVMS_ACCEPTED", jvms)
+       reg.pkg("PKG_LIBTOOL", BtPathname)
 
        // begin PKG_OPTIONS section
        //
        // TODO: force the pkgsrc packages to only define options in the
        //  options.mk file. Most packages already do this, but some still
        //  define them in the Makefile or Makefile.common.
-       sysloadlist("PKG_OPTIONS", BtOption)
-       usrlist("PKG_OPTIONS.*", BtOption)
-       opt := pkg
-       optlist := pkglist
+       reg.sysloadlist("PKG_OPTIONS", BtOption)
+       reg.usrlist("PKG_OPTIONS.*", BtOption)
+       opt := reg.pkg
+       optlist := reg.pkglist
        optlist("PKG_LEGACY_OPTIONS", BtOption)
        optlist("PKG_OPTIONS_DEPRECATED_WARNINGS", BtShellWord)
        optlist("PKG_OPTIONS_GROUP.*", BtOption)
@@ -1420,211 +1422,211 @@ func (reg *VarTypeRegistry) Init(src *Pk
        optlist("PKG_OPTIONS_REQUIRED_GROUPS", BtIdentifier)
        optlist("PKG_OPTIONS_SET.*", BtOption)
        opt("PKG_OPTIONS_VAR", BtPkgOptionsVar)
-       pkglist("PKG_SKIP_REASON", BtShellWord)
+       reg.pkglist("PKG_SKIP_REASON", BtShellWord)
        optlist("PKG_SUGGESTED_OPTIONS", BtOption)
        optlist("PKG_SUGGESTED_OPTIONS.*", BtOption)
        optlist("PKG_SUPPORTED_OPTIONS", BtOption)
        // end PKG_OPTIONS section
 
-       pkg("PKG_PRESERVE", BtYes)
-       pkg("PKG_SHELL", BtPathname)
-       pkg("PKG_SHELL.*", BtPathname)
-       sys("PKG_SHLIBTOOL", BtPathname)
+       reg.pkg("PKG_PRESERVE", BtYes)
+       reg.pkg("PKG_SHELL", BtPathname)
+       reg.pkg("PKG_SHELL.*", BtPathname)
+       reg.sys("PKG_SHLIBTOOL", BtPathname)
        // The special exception for buildlink3.mk is only here because
        // of textproc/xmlcatmgr.
-       acl("PKG_SYSCONFDIR*", BtPathname,
+       reg.acl("PKG_SYSCONFDIR*", BtPathname,
                PackageSettable,
                "Makefile: set, use, use-loadtime",
                "buildlink3.mk, builtin.mk: use-loadtime",
                "Makefile.*, *.mk: default, set, use, use-loadtime")
-       pkglist("PKG_SYSCONFDIR_PERMS", BtPerms)
-       sys("PKG_SYSCONFBASEDIR", BtPathname)
-       pkg("PKG_SYSCONFSUBDIR", BtPathname)
-       pkg("PKG_SYSCONFVAR", BtIdentifier)
-       pkg("PKG_UID", BtInteger)
-       pkglist("PKG_USERS", BtShellWord)
-       pkglist("PKG_USERS_VARS", BtVariableName)
-       pkg("PKG_USE_KERBEROS", BtYes)
-       pkgload("PLIST.*", BtYes)
-       pkglist("PLIST_VARS", BtIdentifier)
-       pkglist("PLIST_SRC", BtRelativePkgPath)
-       pkglist("PLIST_SUBST", BtShellWord)
-       pkg("PLIST_TYPE", enum("dynamic static"))
-       pkglistbl3("PREPEND_PATH", BtPathname)
+       reg.pkglist("PKG_SYSCONFDIR_PERMS", BtPerms)
+       reg.sys("PKG_SYSCONFBASEDIR", BtPathname)
+       reg.pkg("PKG_SYSCONFSUBDIR", BtPathname)
+       reg.pkg("PKG_SYSCONFVAR", BtIdentifier)
+       reg.pkg("PKG_UID", BtInteger)
+       reg.pkglist("PKG_USERS", BtShellWord)
+       reg.pkglist("PKG_USERS_VARS", BtVariableName)
+       reg.pkg("PKG_USE_KERBEROS", BtYes)
+       reg.pkgload("PLIST.*", BtYes)
+       reg.pkglist("PLIST_VARS", BtIdentifier)
+       reg.pkglist("PLIST_SRC", BtRelativePkgPath)
+       reg.pkglist("PLIST_SUBST", BtShellWord)
+       reg.pkg("PLIST_TYPE", enum("dynamic static"))
+       reg.pkglistbl3("PREPEND_PATH", BtPathname)
 
-       acl("PREFIX", BtPathname,
+       reg.acl("PREFIX", BtPathname,
                UserSettable,
                "*: use")
        // BtPathname instead of BtPkgPath since the original package doesn't exist anymore.
        // It would be more precise to check for a PkgPath that doesn't exist anymore.
-       pkg("PREV_PKGPATH", BtPathname)
-       acl("PRINT_PLIST_AWK", BtAwkCommand,
+       reg.pkg("PREV_PKGPATH", BtPathname)
+       reg.acl("PRINT_PLIST_AWK", BtAwkCommand,
                PackageSettable,
                "*: append")
-       pkglist("PRIVILEGED_STAGES", enum("build install package clean"))
-       pkgbl3("PTHREAD_AUTO_VARS", BtYesNo)
-       syslist("PTHREAD_CFLAGS", BtCFlag)
-       syslist("PTHREAD_LDFLAGS", BtLdFlag)
-       syslist("PTHREAD_LIBS", BtLdFlag)
-       pkglistbl3("PTHREAD_OPTS", enum("native optional require"))
-       sysload("PTHREAD_TYPE", BtIdentifier) // Or "native" or "none".
-       pkg("PY_PATCHPLIST", BtYes)
-       acl("PYPKGPREFIX",
-               enumFromDirs("lang", `^python(\d+)$`, "py$1", "py27 py36"),
+       reg.pkglist("PRIVILEGED_STAGES", enum("build install package clean"))
+       reg.pkgbl3("PTHREAD_AUTO_VARS", BtYesNo)
+       reg.syslist("PTHREAD_CFLAGS", BtCFlag)
+       reg.syslist("PTHREAD_LDFLAGS", BtLdFlag)
+       reg.syslist("PTHREAD_LIBS", BtLdFlag)
+       reg.pkglistbl3("PTHREAD_OPTS", enum("native optional require"))
+       reg.sysload("PTHREAD_TYPE", BtIdentifier) // Or "native" or "none".
+       reg.pkg("PY_PATCHPLIST", BtYes)
+       reg.acl("PYPKGPREFIX",
+               reg.enumFromDirs(src, "lang", `^python(\d+)$`, "py$1", "py27 py36"),
                SystemProvided,
                "special:pyversion.mk: set",
                "*: use, use-loadtime")
        // See lang/python/pyversion.mk
-       pkg("PYTHON_FOR_BUILD_ONLY", enum("yes no test tool YES"))
-       pkglistrat("PYTHON_VERSIONS_ACCEPTED", BtVersion)
-       pkglistrat("PYTHON_VERSIONS_INCOMPATIBLE", BtVersion)
-       usr("PYTHON_VERSION_DEFAULT", BtVersion)
-       usr("PYTHON_VERSION_REQD", BtVersion)
-       pkglist("PYTHON_VERSIONED_DEPENDENCIES", BtPythonDependency)
-       sys("RANLIB", BtShellCommand)
-       pkglist("RCD_SCRIPTS", BtFileName)
+       reg.pkg("PYTHON_FOR_BUILD_ONLY", enum("yes no test tool YES"))
+       reg.pkglistrat("PYTHON_VERSIONS_ACCEPTED", BtVersion)
+       reg.pkglistrat("PYTHON_VERSIONS_INCOMPATIBLE", BtVersion)
+       reg.usr("PYTHON_VERSION_DEFAULT", BtVersion)
+       reg.usr("PYTHON_VERSION_REQD", BtVersion)
+       reg.pkglist("PYTHON_VERSIONED_DEPENDENCIES", BtPythonDependency)
+       reg.sys("RANLIB", BtShellCommand)
+       reg.pkglist("RCD_SCRIPTS", BtFileName)
        // TODO: Is the definition in www/squid3/Makefile detected as being redundant?
        //  No, but it could if the RedundancyScope were able to resolve ${FILESDIR}
        //  to "files".
-       pkg("RCD_SCRIPT_SRC.*", BtPathname)
-       pkg("RCD_SCRIPT_WRK.*", BtPathname)
-       usr("REAL_ROOT_USER", BtUserGroupName)
-       usr("REAL_ROOT_GROUP", BtUserGroupName)
+       reg.pkg("RCD_SCRIPT_SRC.*", BtPathname)
+       reg.pkg("RCD_SCRIPT_WRK.*", BtPathname)
+       reg.usr("REAL_ROOT_USER", BtUserGroupName)
+       reg.usr("REAL_ROOT_GROUP", BtUserGroupName)
 
        // Example:
        //  REPLACE.sys-AWK.old=    .*awk
        //  REPLACE.sys-AWK.new=    ${AWK}
        // BtUnknown since one of them is a regular expression and the other
        // is a plain string.
-       pkg("REPLACE.*", BtUnknown)
+       reg.pkg("REPLACE.*", BtUnknown)
 
-       pkglist("REPLACE_AWK", BtPathmask)
-       pkglist("REPLACE_BASH", BtPathmask)
-       pkglist("REPLACE_CSH", BtPathmask)
-       pkglist("REPLACE_FILES.*", BtPathmask)
-       pkglist("REPLACE_INTERPRETER", BtIdentifier)
-       pkglist("REPLACE_KSH", BtPathmask)
-       pkglist("REPLACE_LOCALEDIR_PATTERNS", BtFileMask)
-       pkglist("REPLACE_LUA", BtPathmask)
-       pkglist("REPLACE_PERL", BtPathmask)
-       pkglist("REPLACE_PYTHON", BtPathmask)
-       pkglist("REPLACE_SH", BtPathmask)
-       pkglist("REQD_DIRS", BtPathname)
-       pkglist("REQD_DIRS_PERMS", BtPerms)
-       pkglist("REQD_FILES", BtPathname)
-       pkg("REQD_FILES_MODE", enum("0644 0640 0600 0400"))
-       pkglist("REQD_FILES_PERMS", BtPerms)
-       pkg("RESTRICTED", BtMessage)
-       usr("ROOT_USER", BtUserGroupName)
-       usr("ROOT_GROUP", BtUserGroupName)
-       pkglist("RPMIGNOREPATH", BtPathmask)
-       acl("RUBY_BASE",
-               enumFromDirs("lang", `^ruby(\d+)$`, "ruby$1", "ruby22 ruby23 ruby24 ruby25"),
+       reg.pkglist("REPLACE_AWK", BtPathmask)
+       reg.pkglist("REPLACE_BASH", BtPathmask)
+       reg.pkglist("REPLACE_CSH", BtPathmask)
+       reg.pkglist("REPLACE_FILES.*", BtPathmask)
+       reg.pkglist("REPLACE_INTERPRETER", BtIdentifier)
+       reg.pkglist("REPLACE_KSH", BtPathmask)
+       reg.pkglist("REPLACE_LOCALEDIR_PATTERNS", BtFileMask)
+       reg.pkglist("REPLACE_LUA", BtPathmask)
+       reg.pkglist("REPLACE_PERL", BtPathmask)
+       reg.pkglist("REPLACE_PYTHON", BtPathmask)
+       reg.pkglist("REPLACE_SH", BtPathmask)
+       reg.pkglist("REQD_DIRS", BtPathname)
+       reg.pkglist("REQD_DIRS_PERMS", BtPerms)
+       reg.pkglist("REQD_FILES", BtPathname)
+       reg.pkg("REQD_FILES_MODE", enum("0644 0640 0600 0400"))
+       reg.pkglist("REQD_FILES_PERMS", BtPerms)
+       reg.pkg("RESTRICTED", BtMessage)
+       reg.usr("ROOT_USER", BtUserGroupName)
+       reg.usr("ROOT_GROUP", BtUserGroupName)
+       reg.pkglist("RPMIGNOREPATH", BtPathmask)
+       reg.acl("RUBY_BASE",
+               reg.enumFromDirs(src, "lang", `^ruby(\d+)$`, "ruby$1", "ruby22 ruby23 ruby24 ruby25"),
                SystemProvided,
                "special:rubyversion.mk: set",
                "*: use, use-loadtime")
-       usr("RUBY_VERSION_REQD", BtVersion)
-       acl("RUBY_PKGPREFIX",
-               enumFromDirs("lang", `^ruby(\d+)$`, "ruby$1", "ruby22 ruby23 ruby24 ruby25"),
+       reg.usr("RUBY_VERSION_REQD", BtVersion)
+       reg.acl("RUBY_PKGPREFIX",
+               reg.enumFromDirs(src, "lang", `^ruby(\d+)$`, "ruby$1", "ruby22 ruby23 ruby24 ruby25"),
                SystemProvided,
                "special:rubyversion.mk: default, set, use",
                "*: use, use-loadtime")
-       sys("RUN", BtShellCommand)
-       sys("RUN_LDCONFIG", BtYesNo)
-       pkglist("SCRIPTS_ENV", BtShellWord)
-       usrlist("SETGID_GAMES_PERMS", BtPerms)
-       usrlist("SETUID_ROOT_PERMS", BtPerms)
-       pkg("SET_LIBDIR", BtYes)
-       sys("SHAREGRP", BtUserGroupName)
-       sys("SHAREMODE", BtFileMode)
-       sys("SHAREOWN", BtUserGroupName)
-       sys("SHCOMMENT", BtShellCommand)
-       sys("SHLIBTOOL", BtShellCommand)
-       pkglist("SHLIBTOOL_OVERRIDE", BtPathmask)
-       sysload("SHLIB_TYPE",
+       reg.sys("RUN", BtShellCommand)
+       reg.sys("RUN_LDCONFIG", BtYesNo)
+       reg.pkglist("SCRIPTS_ENV", BtShellWord)
+       reg.usrlist("SETGID_GAMES_PERMS", BtPerms)
+       reg.usrlist("SETUID_ROOT_PERMS", BtPerms)
+       reg.pkg("SET_LIBDIR", BtYes)
+       reg.sys("SHAREGRP", BtUserGroupName)
+       reg.sys("SHAREMODE", BtFileMode)
+       reg.sys("SHAREOWN", BtUserGroupName)
+       reg.sys("SHCOMMENT", BtShellCommand)
+       reg.sys("SHLIBTOOL", BtShellCommand)
+       reg.pkglist("SHLIBTOOL_OVERRIDE", BtPathmask)
+       reg.sysload("SHLIB_TYPE",
                enum("COFF ECOFF ELF SOM XCOFF Mach-O PE PEwin a.out aixlib dylib none"))
-       pkglist("SITES.*", BtFetchURL)
-       usr("SMF_PREFIS", BtPathname)
-       pkg("SMF_SRCDIR", BtPathname)
-       pkg("SMF_NAME", BtFileName)
-       pkg("SMF_MANIFEST", BtPathname)
-       pkglist("SMF_INSTANCES", BtIdentifier)
-       pkglist("SMF_METHODS", BtFileName)
-       pkg("SMF_METHOD_SRC.*", BtPathname)
-       pkg("SMF_METHOD_SHELL", BtShellCommand)
-       pkglist("SPECIAL_PERMS", BtPerms)
-       sys("STEP_MSG", BtShellCommand)
-       sys("STRIP", BtShellCommand) // see mk/tools/strip.mk
+       reg.pkglist("SITES.*", BtFetchURL)
+       reg.usr("SMF_PREFIS", BtPathname)
+       reg.pkg("SMF_SRCDIR", BtPathname)
+       reg.pkg("SMF_NAME", BtFileName)
+       reg.pkg("SMF_MANIFEST", BtPathname)
+       reg.pkglist("SMF_INSTANCES", BtIdentifier)
+       reg.pkglist("SMF_METHODS", BtFileName)
+       reg.pkg("SMF_METHOD_SRC.*", BtPathname)
+       reg.pkg("SMF_METHOD_SHELL", BtShellCommand)
+       reg.pkglist("SPECIAL_PERMS", BtPerms)
+       reg.sys("STEP_MSG", BtShellCommand)
+       reg.sys("STRIP", BtShellCommand) // see mk/tools/strip.mk
 
        // Only valid in the top-level and the category Makefiles.
-       acllist("SUBDIR", BtFileName,
+       reg.acllist("SUBDIR", BtFileName,
                PackageSettable,
                "Makefile: append")
 
-       pkglistbl3("SUBST_CLASSES", BtIdentifier)
-       pkglistbl3("SUBST_CLASSES.*", BtIdentifier) // OPSYS-specific
-       pkglistbl3("SUBST_FILES.*", BtPathmask)
-       pkgbl3("SUBST_FILTER_CMD.*", BtShellCommand)
-       pkgbl3("SUBST_MESSAGE.*", BtMessage)
-       pkgappendbl3("SUBST_SED.*", BtSedCommands)
-       pkgbl3("SUBST_STAGE.*", BtStage)
-       pkglistbl3("SUBST_VARS.*", BtVariableName)
-
-       pkglist("SUPERSEDES", BtDependency)
-       pkglist("TEST_DEPENDS", BtDependencyWithPath)
-       pkglist("TEST_DIRS", BtWrksrcSubdirectory)
-       pkglist("TEST_ENV", BtShellWord)
-       pkglist("TEST_TARGET", BtIdentifier)
-       pkglistrat("TEXINFO_REQD", BtVersion)
-       pkglistbl3("TOOL_DEPENDS", BtDependencyWithPath)
-       syslist("TOOLS_ALIASES", BtFileName)
-       syslist("TOOLS_BROKEN", BtTool)
-       sys("TOOLS_CMD.*", BtPathname)
-       pkglist("TOOLS_CREATE", BtTool)
-       pkglist("TOOLS_DEPENDS.*", BtDependencyWithPath)
-       syslist("TOOLS_GNU_MISSING", BtTool)
-       syslist("TOOLS_NOOP", BtTool)
-       sys("TOOLS_PATH.*", BtPathname)
-       sysload("TOOLS_PLATFORM.*", BtShellCommand)
-       syslist("TOUCH_FLAGS", BtShellWord)
-       pkglist("UAC_REQD_EXECS", BtPrefixPathname)
-       pkglistbl3("UNLIMIT_RESOURCES",
+       reg.pkglistbl3("SUBST_CLASSES", BtIdentifier)
+       reg.pkglistbl3("SUBST_CLASSES.*", BtIdentifier) // OPSYS-specific
+       reg.pkglistbl3("SUBST_FILES.*", BtPathmask)
+       reg.pkgbl3("SUBST_FILTER_CMD.*", BtShellCommand)
+       reg.pkgbl3("SUBST_MESSAGE.*", BtMessage)
+       reg.pkgappendbl3("SUBST_SED.*", BtSedCommands)
+       reg.pkgbl3("SUBST_STAGE.*", BtStage)
+       reg.pkglistbl3("SUBST_VARS.*", BtVariableName)
+
+       reg.pkglist("SUPERSEDES", BtDependency)
+       reg.pkglist("TEST_DEPENDS", BtDependencyWithPath)
+       reg.pkglist("TEST_DIRS", BtWrksrcSubdirectory)
+       reg.pkglist("TEST_ENV", BtShellWord)
+       reg.pkglist("TEST_TARGET", BtIdentifier)
+       reg.pkglistrat("TEXINFO_REQD", BtVersion)
+       reg.pkglistbl3("TOOL_DEPENDS", BtDependencyWithPath)
+       reg.syslist("TOOLS_ALIASES", BtFileName)
+       reg.syslist("TOOLS_BROKEN", BtTool)
+       reg.sys("TOOLS_CMD.*", BtPathname)
+       reg.pkglist("TOOLS_CREATE", BtTool)
+       reg.pkglist("TOOLS_DEPENDS.*", BtDependencyWithPath)
+       reg.syslist("TOOLS_GNU_MISSING", BtTool)
+       reg.syslist("TOOLS_NOOP", BtTool)
+       reg.sys("TOOLS_PATH.*", BtPathname)
+       reg.sysload("TOOLS_PLATFORM.*", BtShellCommand)
+       reg.syslist("TOUCH_FLAGS", BtShellWord)
+       reg.pkglist("UAC_REQD_EXECS", BtPrefixPathname)
+       reg.pkglistbl3("UNLIMIT_RESOURCES",
                enum("cputime datasize memorysize stacksize"))
-       usr("UNPRIVILEGED_USER", BtUserGroupName)
-       usr("UNPRIVILEGED_GROUP", BtUserGroupName)
-       pkglist("UNWRAP_FILES", BtPathmask)
-       usrlist("UPDATE_TARGET", BtIdentifier)
-       pkg("USERGROUP_PHASE", enum("configure build pre-install"))
-       usrlist("USER_ADDITIONAL_PKGS", BtPkgPath)
-       pkg("USE_BSD_MAKEFILE", BtYes)
+       reg.usr("UNPRIVILEGED_USER", BtUserGroupName)
+       reg.usr("UNPRIVILEGED_GROUP", BtUserGroupName)
+       reg.pkglist("UNWRAP_FILES", BtPathmask)
+       reg.usrlist("UPDATE_TARGET", BtIdentifier)
+       reg.pkg("USERGROUP_PHASE", enum("configure build pre-install"))
+       reg.usrlist("USER_ADDITIONAL_PKGS", BtPkgPath)
+       reg.pkg("USE_BSD_MAKEFILE", BtYes)
 
        // USE_BUILTIN.* is usually set by the builtin.mk file, after checking
        // whether the package is available in the base system. To override
        // this check, a package may set this variable before including the
        // corresponding buildlink3.mk file.
-       acl("USE_BUILTIN.*", BtYesNoIndirectly,
+       reg.acl("USE_BUILTIN.*", BtYesNoIndirectly,
                PackageSettable,
                "Makefile, Makefile.*, *.mk: set, use, use-loadtime")
 
-       pkg("USE_CMAKE", BtYes)
-       usr("USE_DESTDIR", BtYes)
-       pkglist("USE_FEATURES", BtIdentifier)
-       pkg("USE_GAMESGROUP", BtYesNo)
-       pkg("USE_GCC_RUNTIME", BtYesNo)
-       pkg("USE_GNU_CONFIGURE_HOST", BtYesNo)
-       pkgload("USE_GNU_ICONV", BtYes)
-       pkg("USE_IMAKE", BtYes)
-       pkg("USE_JAVA", enum("run yes build"))
-       pkg("USE_JAVA2", enum("YES yes no 1.4 1.5 6 7 8"))
-       pkglist("USE_LANGUAGES", compilerLanguages)
-       pkg("USE_LIBTOOL", BtYes)
-       pkg("USE_MAKEINFO", BtYes)
-       pkg("USE_MSGFMT_PLURALS", BtYes)
-       pkg("USE_NCURSES", BtYes)
-       pkg("USE_OLD_DES_API", BtYesNo)
-       pkg("USE_PKGINSTALL", BtYes)
-       pkg("USE_PKGLOCALEDIR", BtYesNo)
-       usr("USE_PKGSRC_GCC", BtYes)
+       reg.pkg("USE_CMAKE", BtYes)
+       reg.usr("USE_DESTDIR", BtYes)
+       reg.pkglist("USE_FEATURES", BtIdentifier)
+       reg.pkg("USE_GAMESGROUP", BtYesNo)
+       reg.pkg("USE_GCC_RUNTIME", BtYesNo)
+       reg.pkg("USE_GNU_CONFIGURE_HOST", BtYesNo)
+       reg.pkgload("USE_GNU_ICONV", BtYes)
+       reg.pkg("USE_IMAKE", BtYes)
+       reg.pkg("USE_JAVA", enum("run yes build"))
+       reg.pkg("USE_JAVA2", enum("YES yes no 1.4 1.5 6 7 8"))
+       reg.pkglist("USE_LANGUAGES", reg.compilerLanguages(src))
+       reg.pkg("USE_LIBTOOL", BtYes)
+       reg.pkg("USE_MAKEINFO", BtYes)
+       reg.pkg("USE_MSGFMT_PLURALS", BtYes)
+       reg.pkg("USE_NCURSES", BtYes)
+       reg.pkg("USE_OLD_DES_API", BtYesNo)
+       reg.pkg("USE_PKGINSTALL", BtYes)
+       reg.pkg("USE_PKGLOCALEDIR", BtYesNo)
+       reg.usr("USE_PKGSRC_GCC", BtYes)
 
        // USE_TOOLS is not similar to any predefined permissions set.
        //
@@ -1641,38 +1643,38 @@ func (reg *VarTypeRegistry) Init(src *Pk
        //
        // The use-loadtime is only for devel/ncurses/Makefile.common, which
        // removes tbl from USE_TOOLS.
-       acllist("USE_TOOLS", BtTool,
+       reg.acllist("USE_TOOLS", BtTool,
                PackageSettable,
                "special:Makefile.common: set, append, use, use-loadtime",
                "buildlink3.mk: append",
                "builtin.mk: append, use-loadtime",
                "*: set, append, use")
-       acllist("USE_TOOLS.*", BtTool, // OPSYS-specific
+       reg.acllist("USE_TOOLS.*", BtTool, // OPSYS-specific
                PackageSettable,
                "buildlink3.mk, builtin.mk: append",
                "*: set, append, use")
 
-       pkg("USE_X11", BtYes)
-       syslist("WARNINGS", BtShellWord)
-       sys("WARNING_MSG", BtShellCommand)
-       sys("WARNING_CAT", BtShellCommand)
-       sysload("WRAPPER_DIR", BtPathname)
-       pkglistbl3("WRAPPER_REORDER_CMDS", BtWrapperReorder)
-       pkg("WRAPPER_SHELL", BtShellCommand)
-       pkglist("WRAPPER_TRANSFORM_CMDS", BtWrapperTransform)
-       sys("WRKDIR", BtPathname)
-       pkg("WRKSRC", BtWrkdirSubdirectory)
-       pkglist("X11_LDFLAGS", BtLdFlag)
-       sys("X11_PKGSRCDIR.*", BtPathname)
-       pkglist("XMKMF_FLAGS", BtShellWord)
-       pkglist("_WRAP_EXTRA_ARGS.*", BtShellWord)
-
-       infralist("_VARGROUPS", BtIdentifier)
-       infralist("_USER_VARS.*", BtIdentifier)
-       infralist("_PKG_VARS.*", BtIdentifier)
-       infralist("_SYS_VARS.*", BtIdentifier)
-       infralist("_DEF_VARS.*", BtIdentifier)
-       infralist("_USE_VARS.*", BtIdentifier)
+       reg.pkg("USE_X11", BtYes)
+       reg.syslist("WARNINGS", BtShellWord)
+       reg.sys("WARNING_MSG", BtShellCommand)
+       reg.sys("WARNING_CAT", BtShellCommand)
+       reg.sysload("WRAPPER_DIR", BtPathname)
+       reg.pkglistbl3("WRAPPER_REORDER_CMDS", BtWrapperReorder)
+       reg.pkg("WRAPPER_SHELL", BtShellCommand)
+       reg.pkglist("WRAPPER_TRANSFORM_CMDS", BtWrapperTransform)
+       reg.sys("WRKDIR", BtPathname)
+       reg.pkg("WRKSRC", BtWrkdirSubdirectory)
+       reg.pkglist("X11_LDFLAGS", BtLdFlag)
+       reg.sys("X11_PKGSRCDIR.*", BtPathname)
+       reg.pkglist("XMKMF_FLAGS", BtShellWord)
+       reg.pkglist("_WRAP_EXTRA_ARGS.*", BtShellWord)
+
+       reg.infralist("_VARGROUPS", BtIdentifier)
+       reg.infralist("_USER_VARS.*", BtIdentifier)
+       reg.infralist("_PKG_VARS.*", BtIdentifier)
+       reg.infralist("_SYS_VARS.*", BtIdentifier)
+       reg.infralist("_DEF_VARS.*", BtIdentifier)
+       reg.infralist("_USE_VARS.*", BtIdentifier)
 }
 
 func enum(values string) *BasicType {
@@ -1690,7 +1692,7 @@ func enum(values string) *BasicType {
 
 func (reg *VarTypeRegistry) parseACLEntries(varname string, aclEntries ...string) []ACLEntry {
 
-       G.Assertf(len(aclEntries) > 0, "At least one ACL entry must be given.")
+       assertf(len(aclEntries) > 0, "At least one ACL entry must be given.")
 
        // TODO: Use separate rules for infrastructure files.
        //  These rules would have the "infra:" prefix
@@ -1702,10 +1704,10 @@ func (reg *VarTypeRegistry) parseACLEntr
        prevperms := "(first)"
        for _, arg := range aclEntries {
                fields := strings.Split(arg, ": ")
-               G.Assertf(len(fields) == 2, "ACL entry %q must have exactly 1 colon.", arg)
+               assertf(len(fields) == 2, "ACL entry %q must have exactly 1 colon.", arg)
                globs, perms := fields[0], fields[1]
 
-               G.Assertf(perms != prevperms, "Repeated permissions %q for %q.", perms, varname)
+               assertf(perms != prevperms, "Repeated permissions %q for %q.", perms, varname)
                prevperms = perms
 
                permissions := reg.parsePermissions(varname, globs, perms)
@@ -1719,15 +1721,15 @@ func (reg *VarTypeRegistry) parseACLEntr
                        default:
                                withoutSpecial := strings.TrimPrefix(glob, "special:")
                                if withoutSpecial == glob {
-                                       G.Assertf(false, "Invalid ACL glob %q for %q.", glob, varname)
+                                       assertf(false, "Invalid ACL glob %q for %q.", glob, varname)
                                } else {
                                        glob = withoutSpecial
                                }
                        }
                        for _, prev := range result {
                                matched, err := path.Match(prev.glob, glob)
-                               G.AssertNil(err, "Invalid ACL pattern %q for %q", glob, varname)
-                               G.Assertf(!matched, "Unreachable ACL pattern %q for %q.", glob, varname)
+                               assertNil(err, "Invalid ACL pattern %q for %q", glob, varname)
+                               assertf(!matched, "Unreachable ACL pattern %q for %q.", glob, varname)
                        }
                        result = append(result, NewACLEntry(glob, permissions))
                }
@@ -1769,7 +1771,7 @@ func (reg *VarTypeRegistry) parsePermiss
        remove("use-loadtime", aclpUseLoadtime)
 
        if len(splitPerms) > 0 {
-               G.Assertf(
+               assertf(
                        false,
                        "Invalid ACL permission %q for %q in %q. "+
                                "Remaining parts are %q. "+

Index: pkgsrc/pkgtools/pkglint/files/vartype.go
diff -u pkgsrc/pkgtools/pkglint/files/vartype.go:1.32 pkgsrc/pkgtools/pkglint/files/vartype.go:1.33
--- pkgsrc/pkgtools/pkglint/files/vartype.go:1.32       Sun May 26 14:05:57 2019
+++ pkgsrc/pkgtools/pkglint/files/vartype.go    Mon Jun 10 19:51:57 2019
@@ -16,7 +16,7 @@ type Vartype struct {
 func NewVartype(basicType *BasicType, options vartypeOptions, aclEntries ...ACLEntry) *Vartype {
        for _, aclEntry := range aclEntries {
                _, err := path.Match(aclEntry.glob, "")
-               G.AssertNil(err, "path.Match")
+               assertNil(err, "path.Match")
        }
 
        return &Vartype{basicType, options, aclEntries}
@@ -141,7 +141,7 @@ func (vt *Vartype) AlternativeFiles(perm
                        redundant := false
                        for _, late := range slice[si+1:] {
                                matched, err := path.Match(late, early)
-                               G.AssertNil(err, "path.Match")
+                               assertNil(err, "path.Match")
                                if matched {
                                        redundant = true
                                        break
@@ -195,9 +195,6 @@ func (vt *Vartype) MayBeAppendedTo() boo
        switch vt.basicType {
        case BtAwkCommand, BtSedCommands, BtShellCommand, BtShellCommands, BtConfFiles:
                return true
-       }
-
-       switch vt.basicType {
        case BtComment, BtLicense:
                return true
        }
@@ -286,10 +283,6 @@ func (bt *BasicType) NeedsQ() bool {
        return !bt.IsEnum()
 }
 
-func (vt *Vartype) IsPlainString() bool {
-       return vt.basicType == BtComment || vt.basicType == BtMessage
-}
-
 type BasicType struct {
        name    string
        checker func(*VartypeCheck)

Index: pkgsrc/pkgtools/pkglint/files/vartypecheck.go
diff -u pkgsrc/pkgtools/pkglint/files/vartypecheck.go:1.56 pkgsrc/pkgtools/pkglint/files/vartypecheck.go:1.57
--- pkgsrc/pkgtools/pkglint/files/vartypecheck.go:1.56  Tue May 21 17:59:48 2019
+++ pkgsrc/pkgtools/pkglint/files/vartypecheck.go       Mon Jun 10 19:51:57 2019
@@ -526,7 +526,7 @@ func (cv *VartypeCheck) FetchURL() {
                }
 
                if G.Pkgsrc.MasterSiteVarToURL[name] == "" {
-                       if !(G.Pkg != nil && G.Pkg.vars.Defined(name)) {
+                       if G.Pkg == nil || !G.Pkg.vars.Defined(name) {
                                cv.Errorf("The site %s does not exist.", name)
                        }
                }
@@ -791,7 +791,7 @@ func (cv *VartypeCheck) Option() {
        }
 
        if m, optname := match1(value, `^-?([a-z][-0-9a-z+]*)$`); m {
-               if cv.MkLines != nil && !cv.MkLines.FirstTimeSlice("option:", optname) {
+               if !cv.MkLines.FirstTimeSlice("option:", optname) {
                        return
                }
 

Index: pkgsrc/pkgtools/pkglint/files/trace/tracing.go
diff -u pkgsrc/pkgtools/pkglint/files/trace/tracing.go:1.8 pkgsrc/pkgtools/pkglint/files/trace/tracing.go:1.9
--- pkgsrc/pkgtools/pkglint/files/trace/tracing.go:1.8  Sat Apr 20 17:43:25 2019
+++ pkgsrc/pkgtools/pkglint/files/trace/tracing.go      Mon Jun 10 19:51:57 2019
@@ -81,7 +81,7 @@ func (t *Tracer) Call(args ...interface{
 // http://stackoverflow.com/questions/13476349/check-for-nil-and-nil-interface-in-go
 func isNil(a interface{}) bool {
        defer func() {
-               recover()
+               _ = recover()
        }()
        return a == nil || reflect.ValueOf(a).IsNil()
 }

Added files:

Index: pkgsrc/pkgtools/pkglint/files/paragraph.go
diff -u /dev/null pkgsrc/pkgtools/pkglint/files/paragraph.go:1.1
--- /dev/null   Mon Jun 10 19:51:58 2019
+++ pkgsrc/pkgtools/pkglint/files/paragraph.go  Mon Jun 10 19:51:57 2019
@@ -0,0 +1,72 @@
+package pkglint
+
+import "strings"
+
+// Paragraph is a slice of Makefile lines that is surrounded by empty lines.
+//
+// All variable assignments in a paragraph should be aligned in the same column.
+//
+// If the paragraph adds an identifier to SUBST_CLASSES, the rest of the SUBST
+// block should be defined in the same paragraph.
+type Paragraph struct {
+       mklines []MkLine
+}
+
+func NewParagraph(mklines []MkLine) *Paragraph {
+       return &Paragraph{mklines}
+}
+
+func (p *Paragraph) Clear() {
+       p.mklines = nil
+}
+
+func (p *Paragraph) Add(mkline MkLine) {
+       assertf(!mkline.IsEmpty(), "A paragraph must not contain empty lines.")
+       p.mklines = append(p.mklines, mkline)
+}
+
+func (p *Paragraph) ForEach(action func(mkline MkLine)) {
+       for _, mkline := range p.mklines {
+               action(mkline)
+       }
+}
+
+func (p *Paragraph) Align() {
+       var align VaralignBlock
+       p.ForEach(align.Process)
+       align.Finish()
+}
+
+// AlignTo realigns all variable assignments in the paragraph so that their
+// values start in the same column. Variable assignments that are commented
+// out are also realigned.
+//
+// No warning or note is logged. Therefore this method must only be used to
+// realign the whole paragraph after one of its lines has been modified.
+func (p *Paragraph) AlignTo(column int) {
+       for _, mkline := range p.mklines {
+               if !mkline.IsVarassign() {
+                       continue
+               }
+
+               align := mkline.ValueAlign()
+               oldWidth := tabWidth(align)
+               if tabWidth(rtrimHspace(align)) > column {
+                       continue
+               }
+               if oldWidth == column && !hasSuffix(strings.TrimRight(align, "\t"), " ") {
+                       continue
+               }
+               if mkline.IsMultiline() && !mkline.FirstLineContainsValue() {
+                       continue
+               }
+
+               trimmed := strings.TrimRightFunc(align, isHspaceRune)
+               newSpace := strings.Repeat("\t", (7+column-tabWidth(trimmed))/8)
+
+               fix := mkline.Autofix()
+               fix.Notef(SilentAutofixFormat)
+               fix.ReplaceAfter(trimmed, align[len(trimmed):], newSpace)
+               fix.Apply()
+       }
+}
Index: pkgsrc/pkgtools/pkglint/files/paragraph_test.go
diff -u /dev/null pkgsrc/pkgtools/pkglint/files/paragraph_test.go:1.1
--- /dev/null   Mon Jun 10 19:51:58 2019
+++ pkgsrc/pkgtools/pkglint/files/paragraph_test.go     Mon Jun 10 19:51:57 2019
@@ -0,0 +1,153 @@
+package pkglint
+
+import "gopkg.in/check.v1"
+
+func (s *Suite) Test_Paragraph_Clear(c *check.C) {
+       t := s.Init(c)
+
+       para := NewParagraph(nil)
+
+       para.Clear()
+
+       t.Check(para.mklines, check.IsNil)
+
+       para.Add(t.NewMkLine("filename.mk", 123, "#"))
+
+       para.Clear()
+
+       t.Check(para.mklines, check.IsNil)
+}
+
+func (s *Suite) Test_Paragraph_Add__empty_line(c *check.C) {
+       t := s.Init(c)
+
+       para := NewParagraph(nil)
+
+       para.Clear()
+
+       t.Check(para.mklines, check.IsNil)
+
+       t.ExpectPanic(
+               func() { para.Add(t.NewMkLine("filename.mk", 123, "")) },
+               "Pkglint internal error: A paragraph must not contain empty lines.")
+}
+
+func (s *Suite) Test_Paragraph_Align(c *check.C) {
+       t := s.Init(c)
+
+       t.SetUpCommandLine("-Wall", "--autofix")
+       mklines := t.SetUpFileMkLines("filename.mk",
+               MkRcsID,
+               "VAR=value",
+               "VAR=\t\t\tvalue")
+       para := NewParagraph(nil)
+       for _, mkline := range mklines.mklines {
+               // Strictly speaking, lines 1 and 2 don't belong to the paragraph,
+               // but aligning the lines works nevertheless.
+               para.Add(mkline)
+       }
+
+       para.Align()
+       mklines.SaveAutofixChanges()
+
+       t.CheckOutputLines(
+               "AUTOFIX: ~/filename.mk:2: Replacing \"\" with \"\\t\".",
+               "AUTOFIX: ~/filename.mk:3: Replacing \"\\t\\t\\t\" with \"\\t\".")
+
+       t.CheckFileLinesDetab("filename.mk",
+               MkRcsID,
+               "VAR=    value",
+               "VAR=    value")
+}
+
+func (s *Suite) Test_Paragraph_AlignTo(c *check.C) {
+       t := s.Init(c)
+
+       t.SetUpCommandLine("-Wall", "--autofix")
+       mklines := t.SetUpFileMkLines("filename.mk",
+               MkRcsID,
+               "VAR=value",
+               "VAR=\t\tvalue",
+               "VAR=\t \tvalue",
+               "VAR=\t\t\tvalue")
+       para := NewParagraph(nil)
+       for _, mkline := range mklines.mklines {
+               // Strictly speaking, lines 1 and 2 don't belong to the paragraph,
+               // but aligning the lines works nevertheless.
+               para.Add(mkline)
+       }
+
+       para.AlignTo(16)
+       mklines.SaveAutofixChanges()
+
+       t.CheckOutputLines(
+               "AUTOFIX: ~/filename.mk:2: Replacing \"\" with \"\\t\\t\".",
+               "AUTOFIX: ~/filename.mk:4: Replacing \"\\t \\t\" with \"\\t\\t\".",
+               "AUTOFIX: ~/filename.mk:5: Replacing \"\\t\\t\\t\" with \"\\t\\t\".")
+
+       t.CheckFileLinesDetab("filename.mk",
+               MkRcsID,
+               "VAR=            value",
+               "VAR=            value",
+               "VAR=            value",
+               "VAR=            value")
+}
+
+func (s *Suite) Test_Paragraph_AlignTo__continued_lines(c *check.C) {
+       t := s.Init(c)
+
+       t.SetUpCommandLine("-Wall", "--autofix")
+       mklines := t.SetUpFileMkLines("filename.mk",
+               MkRcsID,
+               "VAR= \\",
+               "  value",
+               "VAR= value1 \\",
+               "value2 \\",
+               "\t\tvalue3")
+       para := NewParagraph(nil)
+       for _, mkline := range mklines.mklines {
+               para.Add(mkline)
+       }
+
+       para.AlignTo(16)
+       mklines.SaveAutofixChanges()
+
+       t.CheckOutputLines(
+               "AUTOFIX: ~/filename.mk:4: Replacing \" \" with \"\\t\\t\".")
+
+       t.CheckFileLinesDetab("filename.mk",
+               MkRcsID,
+               // Since this line does not contain the actual value, it doesn't need to be aligned.
+               "VAR= \\",
+               "  value",
+               "VAR=            value1 \\",
+               // TODO: The continuation lines should be indented with at least one tab.
+               "value2 \\",
+               "                value3")
+}
+
+func (s *Suite) Test_Paragraph_AlignTo__outlier(c *check.C) {
+       t := s.Init(c)
+
+       t.SetUpCommandLine("-Wall", "--autofix")
+       mklines := t.SetUpFileMkLines("filename.mk",
+               MkRcsID,
+               "VAR= value",
+               "VERY_LONG_VARIABLE_NAME= value1")
+       para := NewParagraph(nil)
+       for _, mkline := range mklines.mklines {
+               para.Add(mkline)
+       }
+
+       para.AlignTo(8)
+       mklines.SaveAutofixChanges()
+
+       t.CheckOutputLines(
+               "AUTOFIX: ~/filename.mk:2: Replacing \" \" with \"\\t\".")
+
+       t.CheckFileLinesDetab("filename.mk",
+               MkRcsID,
+               "VAR=    value",
+               // The space is preserved since this line is an outlier.
+               "VERY_LONG_VARIABLE_NAME= value1")
+}



Home | Main Index | Thread Index | Old Index