Thu Jul 7 12:09:27 2016 UTC ()
Updated pkglint to 5.4.3.
Changes since 5.4.2:
* Variables like ${VAR_${OTHER_VAR}} are no longer checked for
use/define mismatch
* The check for plural variable names has been removed
* The type of variables called *DESTDIR is no longer guessed to be a
directory name
* The check for unknown shell commands is disabled in Makefile
sections that depend on OPSYS
* The experimental hand-written shell parser has been replaced with
a Yacc-generated one
* Meta packages don't need a LICENSE
* When PKGNAME is defined in terms of ${DISTNAME:S/from/to/:tl}, more
modifiers (like :tl) are handled properly
* When the MAINTAINER or OWNER of a package is not the current user,
a warning is printed for modified files
* The check for share/applications/*.desktop has been disabled, since
pkglint would need to inspect the file's actual contents to see
whether desktopdb.mk must be included or not
* SUBST_CLASSES may also be SUBST_CLASSES.NetBSD
* Loosened the usage restrictions for several variables, e.g. many
variables that may be appended in a Makefile may also be set
unconditionally
* PKG_OPTIONS_VAR must be of the form PKG_OPTIONS.*
(rillig)
diff -r1.487 -r1.488 pkgsrc/pkgtools/pkglint/Makefile
diff -r1.9 -r1.10 pkgsrc/pkgtools/pkglint/files/check_test.go
diff -r1.9 -r1.10 pkgsrc/pkgtools/pkglint/files/shell.go
diff -r1.9 -r1.10 pkgsrc/pkgtools/pkglint/files/shell_test.go
diff -r1.10 -r1.11 pkgsrc/pkgtools/pkglint/files/globaldata.go
diff -r1.10 -r1.11 pkgsrc/pkgtools/pkglint/files/vardefs.go
diff -r1.10 -r1.11 pkgsrc/pkgtools/pkglint/files/vartypecheck_test.go
diff -r1.4 -r1.5 pkgsrc/pkgtools/pkglint/files/licenses.go
diff -r1.4 -r1.5 pkgsrc/pkgtools/pkglint/files/substcontext_test.go
diff -r1.12 -r1.13 pkgsrc/pkgtools/pkglint/files/mkline.go
diff -r1.12 -r1.13 pkgsrc/pkgtools/pkglint/files/mkline_test.go
diff -r1.5 -r1.6 pkgsrc/pkgtools/pkglint/files/mklines_test.go
diff -r1.5 -r1.6 pkgsrc/pkgtools/pkglint/files/plist.go
diff -r1.5 -r1.6 pkgsrc/pkgtools/pkglint/files/plist_test.go
diff -r1.5 -r1.6 pkgsrc/pkgtools/pkglint/files/substcontext.go
diff -r1.1 -r1.2 pkgsrc/pkgtools/pkglint/files/mkparser.go
diff -r1.1 -r1.2 pkgsrc/pkgtools/pkglint/files/mkparser_test.go
diff -r1.1 -r1.2 pkgsrc/pkgtools/pkglint/files/mkshparser.go
diff -r1.1 -r1.2 pkgsrc/pkgtools/pkglint/files/mkshparser_test.go
diff -r1.1 -r1.2 pkgsrc/pkgtools/pkglint/files/mkshtypes.go
diff -r1.1 -r1.2 pkgsrc/pkgtools/pkglint/files/mktypes_test.go
diff -r1.1 -r1.2 pkgsrc/pkgtools/pkglint/files/shtokenizer.go
diff -r1.1 -r1.2 pkgsrc/pkgtools/pkglint/files/shtokenizer_test.go
diff -r1.1 -r1.2 pkgsrc/pkgtools/pkglint/files/shtypes.go
diff -r0 -r1.1 pkgsrc/pkgtools/pkglint/files/mkshwalker.go
diff -r0 -r1.1 pkgsrc/pkgtools/pkglint/files/mkshwalker_test.go
diff -r0 -r1.1 pkgsrc/pkgtools/pkglint/files/shell.y
diff -r1.8 -r1.9 pkgsrc/pkgtools/pkglint/files/package.go
diff -r1.8 -r1.9 pkgsrc/pkgtools/pkglint/files/pkglint.go
diff -r1.7 -r1.8 pkgsrc/pkgtools/pkglint/files/package_test.go
diff -r1.7 -r1.8 pkgsrc/pkgtools/pkglint/files/util.go
diff -r1.3 -r1.4 pkgsrc/pkgtools/pkglint/files/util_test.go
diff -r1.14 -r1.15 pkgsrc/pkgtools/pkglint/files/vartypecheck.go
diff -r1.2 -r1.3 pkgsrc/pkgtools/pkglint/files/vercmp_test.go
--- pkgsrc/pkgtools/pkglint/Makefile 2016/06/19 18:03:29 1.487
+++ pkgsrc/pkgtools/pkglint/Makefile 2016/07/07 12:09:26 1.488
--- pkgsrc/pkgtools/pkglint/files/Attic/check_test.go 2016/06/05 11:24:32 1.9
+++ pkgsrc/pkgtools/pkglint/files/Attic/check_test.go 2016/07/07 12:09:27 1.10
--- pkgsrc/pkgtools/pkglint/files/Attic/shell.go 2016/06/05 11:24:32 1.9
+++ pkgsrc/pkgtools/pkglint/files/Attic/shell.go 2016/07/07 12:09:27 1.10
@@ -8,34 +8,6 @@
)
const (
- reShellToken = `^\s*(` +
- `#.*` + // shell comment
- `|(?:` +
- `'[^']*'` + // single quoted string
- "|\"`[^`]+`\"" + // backticks command execution in double quotes
- `|"(?:\\.|[^"])*"` + // double quoted string
- "|`[^`]*`" + // backticks command execution (very simple case)
- `|\\\$\$` + // a shell-escaped dollar sign
- `|\\[^\$]` + // other escaped characters
- `|\$[\w_]` + // one-character make(1) variable
- `|\$\$[0-9A-Z_a-z]+` + // shell variable
- `|\$\$[!#?@]` + // special shell variables
- `|\$\$[./]` + // unescaped dollar in shell, followed by punctuation
- `|\$\$\$\$` + // the special pid shell variable
- `|\$\$\{[0-9A-Z_a-z]+[#%:]?[^}]*\}` + // shell variable in braces
- `|[^\(\)'\"\\\s;&\|<>` + "`" + `\$]` + // non-special character
- `|\$\{[^\s\"'` + "`" + `]+` + // HACK: nested make(1) variables
- `)+` + // any of the above may be repeated
- `|\$\$\(` + // POSIX-style backticks replacement
- `|;;?` +
- `|&&?` +
- `|\|\|?` +
- `|\(` +
- `|\)` +
- `|>&` +
- `|<<?` +
- `|>>?` +
- `|#.*)`
reShVarassign = `^([A-Z_a-z]\w*)=`
reShVarname = `(?:[!#*\-\d?@]|\$\$|[A-Za-z_]\w*)`
reShVarexpansion = `(?:(?:#|##|%|%%|:-|:=|:\?|:\+|\+)[^$\\{}]*)`
@@ -43,63 +15,6 @@
reShDollar = `\\\$\$|` + reShVaruse + `|\$\$[,\-/|]`
)
-// ShellCommandState
-type scState uint8
-
-const (
- scstStart scState = iota
- scstCont
- scstInstall
- scstInstallD
- scstMkdir
- scstPax
- scstPaxS
- scstSed
- scstSedE
- scstSet
- scstSetCont
- scstCond
- scstCondCont
- scstCase
- scstCaseIn
- scstCaseLabel
- scstCaseLabelCont
- scstFor
- scstForIn
- scstForCont
- scstEcho
- scstInstallDir
- scstInstallDir2
-)
-
-func (st scState) String() string {
- return [...]string{
- "start",
- "continuation",
- "install",
- "install -d",
- "mkdir",
- "pax",
- "pax -s",
- "sed",
- "sed -e",
- "set",
- "set-continuation",
- "cond",
- "cond-continuation",
- "case",
- "case in",
- "case label",
- "case-label-continuation",
- "for",
- "for-in",
- "for-continuation",
- "echo",
- "install-dir",
- "install-dir2",
- }[st]
-}
-
type ShellLine struct {
line *Line
mkline *MkLine
@@ -355,12 +270,6 @@
return true
}
-type ShelltextContext struct {
- shline *ShellLine
- state scState
- shellword string
-}
-
func (shline *ShellLine) CheckShellCommandLine(shelltext string) {
if G.opts.Debug {
defer tracecall1(shelltext)()
@@ -411,62 +320,44 @@
}
func (shline *ShellLine) CheckShellCommand(shellcmd string, pSetE *bool) {
- if false {
- p := NewMkShParser(shline.line, shellcmd, false)
- cmds := p.Program()
- rest := p.tok.parser.Rest()
- if rest != "" {
- traceStep("shellcmd=%q", shellcmd)
- if cmds != nil {
- for _, andor := range cmds.AndOrs {
- traceStep("AndOr %v", andor)
- }
- }
- shline.line.Warnf("Pkglint parse error in ShellLine.CheckShellCommand at %q", p.peekText()+rest)
- }
+ if G.opts.Debug {
+ defer tracecall()()
}
- state := scstStart
- tokens, rest := splitIntoShellTokens(shline.line, shellcmd)
- if rest != "" {
- shline.line.Warnf("Pkglint parse error in ShellLine.CheckShellCommand at %q (state=%s)", rest, state)
+ program, err := parseShellProgram(shline.line, shellcmd)
+ if err != nil && contains(shellcmd, "$$(") { // Hack until the shell parser can handle subshells.
+ shline.line.Warn0("Invoking subshells via $(...) is not portable enough.")
+ return
}
+ if err != nil {
+ shline.line.Warnf("Pkglint ShellLine.CheckShellCommand: %s", err)
+ return
+ }
- prevToken := ""
- for _, token := range tokens {
- if G.opts.Debug {
- traceStep("checkShellCommand state=%v token=%q", state, token)
- }
+ spc := &ShellProgramChecker{shline}
+ spc.checkConditionalCd(program)
- {
- noQuotingNeeded := state == scstCase ||
- state == scstForCont ||
- state == scstSetCont ||
- (state == scstStart && matches(token, reShVarassign))
- shline.CheckWord(token, !noQuotingNeeded)
+ (*MkShWalker).Walk(nil, program, func(node interface{}) {
+ if cmd, ok := node.(*MkShSimpleCommand); ok {
+ scc := NewSimpleCommandChecker(shline, cmd)
+ scc.Check()
+ if scc.strcmd.Name == "set" && scc.strcmd.AnyArgMatches(`^-.*e`) {
+ *pSetE = true
+ }
}
- st := &ShelltextContext{shline, state, token}
- st.checkCommandStart()
- st.checkConditionalCd()
- if state != scstPaxS && state != scstSedE && state != scstCaseLabel {
- shline.line.CheckAbsolutePathname(token)
+ if cmd, ok := node.(*MkShList); ok {
+ spc.checkSetE(cmd, pSetE)
}
- st.checkAutoMkdirs()
- st.checkInstallMulti()
- st.checkPaxPe()
- st.checkQuoteSubstitution()
- st.checkEchoN()
- st.checkPipeExitcode()
- st.checkSetE(pSetE, prevToken)
- if state == scstSet && hasPrefix(token, "-") && contains(token, "e") || state == scstStart && token == "${RUN}" {
- *pSetE = true
+ if cmd, ok := node.(*MkShPipeline); ok {
+ spc.checkPipeExitcode(shline.line, cmd)
}
- state = shline.nextState(state, token)
- prevToken = token
- }
+ if word, ok := node.(*ShToken); ok {
+ spc.checkWord(word, false)
+ }
+ })
}
func (shline *ShellLine) CheckShellCommands(shellcmds string) {
@@ -493,7 +384,9 @@
// Shell comments may be hidden, since they cannot have side effects.
default:
- if m, cmd := match1(rest, reShellToken); m {
+ tokens, _ := splitIntoShellTokens(shline.line, rest)
+ if len(tokens) > 0 {
+ cmd := tokens[0]
switch cmd {
case "${DELAYED_ERROR_MSG}", "${DELAYED_WARNING_MSG}",
"${DO_NADA}",
@@ -525,28 +418,48 @@
}
}
-func (ctx *ShelltextContext) checkCommandStart() {
+type SimpleCommandChecker struct {
+ shline *ShellLine
+ cmd *MkShSimpleCommand
+ strcmd *StrCommand
+}
+
+func NewSimpleCommandChecker(shline *ShellLine, cmd *MkShSimpleCommand) *SimpleCommandChecker {
+ strcmd := NewStrCommand(cmd)
+ return &SimpleCommandChecker{shline, cmd, strcmd}
+
+}
+
+func (c *SimpleCommandChecker) Check() {
if G.opts.Debug {
- defer tracecall2(ctx.state.String(), ctx.shellword)()
+ defer tracecall(c.strcmd)()
}
- state, shellword := ctx.state, ctx.shellword
- if state != scstStart && state != scstCond {
- return
+ c.checkCommandStart()
+ c.checkAbsolutePathnames()
+ c.checkAutoMkdirs()
+ c.checkInstallMulti()
+ c.checkPaxPe()
+ c.checkEchoN()
+}
+
+func (ctx *SimpleCommandChecker) checkCommandStart() {
+ if G.opts.Debug {
+ defer tracecall()()
}
+ shellword := ctx.strcmd.Name
switch {
- case shellword == "${RUN}":
+ case shellword == "${RUN}" || shellword == "":
case ctx.handleForbiddenCommand():
case ctx.handleTool():
case ctx.handleCommandVariable():
- case matches(shellword, `^(?:\$\$\(|\(|\)|:|;|;;|&&|\|\||\{|\}|break|case|cd|continue|do|done|elif|else|esac|eval|exec|exit|export|fi|for|if|read|set|shift|then|umask|unset|while)$`):
- case matches(shellword, `^\w+=`): // Variable assignment
+ case matches(shellword, `^(?::|break|cd|continue|eval|exec|exit|export|read|set|shift|umask|unset)$`):
case hasPrefix(shellword, "./"): // All commands from the current directory are fine.
case hasPrefix(shellword, "${PKGSRCDIR"): // With or without the :Q modifier
case ctx.handleComment():
default:
- if G.opts.WarnExtra {
+ if G.opts.WarnExtra && !(G.Mk != nil && G.Mk.indentation.DependsOn("OPSYS")) {
ctx.shline.line.Warn1("Unknown shell command %q.", shellword)
Explain3(
"If you want your package to be portable to all platforms that pkgsrc",
@@ -556,12 +469,12 @@
}
}
-func (ctx *ShelltextContext) handleTool() bool {
+func (ctx *SimpleCommandChecker) handleTool() bool {
if G.opts.Debug {
- defer tracecall1(ctx.shellword)()
+ defer tracecall()()
}
- shellword := ctx.shellword
+ shellword := ctx.strcmd.Name
tool := G.globalData.Tools.byName[shellword]
if tool == nil {
return false
@@ -579,10 +492,15 @@
return true
}
-func (ctx *ShelltextContext) handleForbiddenCommand() bool {
- switch path.Base(ctx.shellword) {
+func (ctx *SimpleCommandChecker) handleForbiddenCommand() bool {
+ if G.opts.Debug {
+ defer tracecall()()
+ }
+
+ shellword := ctx.strcmd.Name
+ switch path.Base(shellword) {
case "ktrace", "mktexlsr", "strace", "texconfig", "truss":
- ctx.shline.line.Error1("%q must not be used in Makefiles.", ctx.shellword)
+ ctx.shline.line.Error1("%q must not be used in Makefiles.", shellword)
Explain3(
"This command must appear in INSTALL scripts, not in the package",
"Makefile, so that the package also works if it is installed as a binary",
@@ -592,12 +510,12 @@
return false
}
-func (ctx *ShelltextContext) handleCommandVariable() bool {
+func (ctx *SimpleCommandChecker) handleCommandVariable() bool {
if G.opts.Debug {
- defer tracecall1(ctx.shellword)()
+ defer tracecall()()
}
- shellword := ctx.shellword
+ shellword := ctx.strcmd.Name
if m, varname := match1(shellword, `^\$\{([\w_]+)\}$`); m {
if tool := G.globalData.Tools.byVarname[varname]; tool != nil {
@@ -622,12 +540,16 @@
return false
}
-func (ctx *ShelltextContext) handleComment() bool {
+func (ctx *SimpleCommandChecker) handleComment() bool {
if G.opts.Debug {
- defer tracecall1(ctx.shellword)()
+ defer tracecall()()
}
- shellword := ctx.shellword
+ shellword := ctx.strcmd.Name
+ if G.opts.Debug {
+ defer tracecall1(shellword)()
+ }
+
if !hasPrefix(shellword, "#") {
return false
}
@@ -660,91 +582,188 @@
return true
}
-func (ctx *ShelltextContext) checkConditionalCd() {
- if ctx.state == scstCond && ctx.shellword == "cd" {
- ctx.shline.line.Error0("The Solaris /bin/sh cannot handle \"cd\" inside conditionals.")
- Explain3(
- "When the Solaris shell is in \"set -e\" mode and \"cd\" fails, the",
- "shell will exit, no matter if it is protected by an \"if\" or the",
- "\"||\" operator.")
- }
+type ShellProgramChecker struct {
+ shline *ShellLine
}
-func (ctx *ShelltextContext) checkAutoMkdirs() {
- state, shellword := ctx.state, ctx.shellword
+func (c *ShellProgramChecker) checkConditionalCd(list *MkShList) {
+ if G.opts.Debug {
+ defer tracecall()()
+ }
- line := ctx.shline.line
- if (state == scstInstallD || state == scstMkdir) && matches(shellword, `^(?:\$\{DESTDIR\})?\$\{PREFIX(?:|:Q)\}/`) {
- line.Warn1("Please use AUTO_MKDIRS instead of %q.",
- ifelseStr(state == scstMkdir, "${MKDIR}", "${INSTALL} -d"))
- Explain4(
- "Setting AUTO_MKDIRS=yes automatically creates all directories that",
- "are mentioned in the PLIST. If you need additional directories,",
- "specify them in INSTALLATION_DIRS, which is a list of directories",
- "relative to ${PREFIX}.")
+ getSimple := func(list *MkShList) *MkShSimpleCommand {
+ if len(list.AndOrs) == 1 {
+ if len(list.AndOrs[0].Pipes) == 1 {
+ if len(list.AndOrs[0].Pipes[0].Cmds) == 1 {
+ return list.AndOrs[0].Pipes[0].Cmds[0].Simple
+ }
+ }
+ }
+ return nil
}
- if (state == scstInstallDir || state == scstInstallDir2) && !contains(shellword, "$$") {
- if m, dirname := match1(shellword, `^(?:\$\{DESTDIR\})?\$\{PREFIX(?:|:Q)\}/(.*)`); m {
- line.Note1("You can use AUTO_MKDIRS=yes or \"INSTALLATION_DIRS+= %s\" instead of this command.", dirname)
- Explain(
- "Many packages include a list of all needed directories in their",
- "PLIST file. In such a case, you can just set AUTO_MKDIRS=yes and",
- "be done. The pkgsrc infrastructure will then create all directories",
- "in advance.",
- "",
- "To create directories that are not mentioned in the PLIST file, it",
- "is easier to just list them in INSTALLATION_DIRS than to execute the",
- "commands explicitly. That way, you don't have to think about which",
- "of the many INSTALL_*_DIR variables is appropriate, since",
- "INSTALLATION_DIRS takes care of that.")
+ checkConditionalCd := func(cmd *MkShSimpleCommand) {
+ if NewStrCommand(cmd).Name == "cd" {
+ c.shline.line.Error0("The Solaris /bin/sh cannot handle \"cd\" inside conditionals.")
+ Explain3(
+ "When the Solaris shell is in \"set -e\" mode and \"cd\" fails, the",
+ "shell will exit, no matter if it is protected by an \"if\" or the",
+ "\"||\" operator.")
}
}
+
+ (*MkShWalker).Walk(nil, list, func(node interface{}) {
+ if cmd, ok := node.(*MkShIfClause); ok {
+ for _, cond := range cmd.Conds {
+ if simple := getSimple(cond); simple != nil {
+ checkConditionalCd(simple)
+ }
+ }
+ }
+ if cmd, ok := node.(*MkShLoopClause); ok {
+ if simple := getSimple(cmd.Cond); simple != nil {
+ checkConditionalCd(simple)
+ }
+ }
+ })
}
-func (ctx *ShelltextContext) checkInstallMulti() {
- if ctx.state == scstInstallDir2 && hasPrefix(ctx.shellword, "$") {
- line := ctx.shline.line
- line.Warn0("The INSTALL_*_DIR commands can only handle one directory at a time.")
- Explain2(
- "Many implementations of install(1) can handle more, but pkgsrc aims",
- "at maximum portability.")
+func (c *ShellProgramChecker) checkWords(words []*ShToken, checkQuoting bool) {
+ if G.opts.Debug {
+ defer tracecall()()
}
+
+ for _, word := range words {
+ c.checkWord(word, checkQuoting)
+ }
}
-func (ctx *ShelltextContext) checkPaxPe() {
- if ctx.state == scstPax && ctx.shellword == "-pe" {
- line := ctx.shline.line
- line.Warn0("Please use the -pp option to pax(1) instead of -pe.")
- Explain3(
- "The -pe option tells pax to preserve the ownership of the files, which",
- "means that the installed files will belong to the user that has built",
- "the package.")
+func (c *ShellProgramChecker) checkWord(word *ShToken, checkQuoting bool) {
+ if G.opts.Debug {
+ defer tracecall(word.MkText)()
}
+
+ c.shline.CheckWord(word.MkText, checkQuoting)
}
-func (ctx *ShelltextContext) checkQuoteSubstitution() {
- if ctx.state == scstPaxS || ctx.state == scstSedE {
- if false && !matches(ctx.shellword, `"^[\"\'].*[\"\']$`) {
- line := ctx.shline.line
- line.Warn1("Substitution commands like %q should always be quoted.", ctx.shellword)
+func (c *SimpleCommandChecker) checkAbsolutePathnames() {
+ if G.opts.Debug {
+ defer tracecall()()
+ }
+
+ cmdname := c.strcmd.Name
+ isSubst := false
+ for _, arg := range c.strcmd.Args {
+ if !isSubst {
+ c.shline.line.CheckAbsolutePathname(arg)
+ }
+ if false && isSubst && !matches(arg, `"^[\"\'].*[\"\']$`) {
+ c.shline.line.Warn1("Substitution commands like %q should always be quoted.", arg)
Explain3(
"Usually these substitution commands contain characters like '*' or",
"other shell metacharacters that might lead to lookup of matching",
"filenames and then expand to more than one word.")
}
+ isSubst = cmdname == "${PAX}" && arg == "-s" || cmdname == "${SED}" && arg == "-e"
}
}
-func (ctx *ShelltextContext) checkEchoN() {
- if ctx.state == scstEcho && ctx.shellword == "-n" {
+func (ctx *SimpleCommandChecker) checkAutoMkdirs() {
+ if G.opts.Debug {
+ defer tracecall()()
+ }
+
+ cmdname := ctx.strcmd.Name
+ switch {
+ case cmdname == "${MKDIR}":
+ break
+ case cmdname == "${INSTALL}" && ctx.strcmd.HasOption("-d"):
+ cmdname = "${INSTALL} -d"
+ case matches(cmdname, `^\$\{INSTALL_.*_DIR\}$`):
+ break
+ default:
+ return
+ }
+
+ for _, arg := range ctx.strcmd.Args {
+ if !contains(arg, "$$") && !matches(arg, `\$\{[_.]*[a-z]`) {
+ if m, dirname := match1(arg, `^(?:\$\{DESTDIR\})?\$\{PREFIX(?:|:Q)\}/(.*)`); m {
+ ctx.shline.line.Note2("You can use AUTO_MKDIRS=yes or \"INSTALLATION_DIRS+= %s\" instead of %q.", dirname, cmdname)
+ Explain(
+ "Many packages include a list of all needed directories in their",
+ "PLIST file. In such a case, you can just set AUTO_MKDIRS=yes and",
+ "be done. The pkgsrc infrastructure will then create all directories",
+ "in advance.",
+ "",
+ "To create directories that are not mentioned in the PLIST file, it",
+ "is easier to just list them in INSTALLATION_DIRS than to execute the",
+ "commands explicitly. That way, you don't have to think about which",
+ "of the many INSTALL_*_DIR variables is appropriate, since",
+ "INSTALLATION_DIRS takes care of that.")
+ }
+ }
+ }
+}
+
+func (ctx *SimpleCommandChecker) checkInstallMulti() {
+ if G.opts.Debug {
+ defer tracecall()()
+ }
+
+ cmd := ctx.strcmd
+
+ if hasPrefix(cmd.Name, "${INSTALL_") && hasSuffix(cmd.Name, "_DIR}") {
+ prevdir := ""
+ for i, arg := range cmd.Args {
+ switch {
+ case hasPrefix(arg, "-"):
+ break
+ case i > 0 && (cmd.Args[i-1] == "-m" || cmd.Args[i-1] == "-o" || cmd.Args[i-1] == "-g"):
+ break
+ default:
+ if prevdir != "" {
+ ctx.shline.line.Warn0("The INSTALL_*_DIR commands can only handle one directory at a time.")
+ Explain2(
+ "Many implementations of install(1) can handle more, but pkgsrc aims",
+ "at maximum portability.")
+ return
+ }
+ prevdir = arg
+ }
+ }
+ }
+}
+
+func (ctx *SimpleCommandChecker) checkPaxPe() {
+ if G.opts.Debug {
+ defer tracecall()()
+ }
+
+ if ctx.strcmd.Name == "${PAX}" && ctx.strcmd.HasOption("-pe") {
+ ctx.shline.line.Warn0("Please use the -pp option to pax(1) instead of -pe.")
+ Explain3(
+ "The -pe option tells pax to preserve the ownership of the files, which",
+ "means that the installed files will belong to the user that has built",
+ "the package.")
+ }
+}
+
+func (ctx *SimpleCommandChecker) checkEchoN() {
+ if G.opts.Debug {
+ defer tracecall()()
+ }
+
+ if ctx.strcmd.Name == "${ECHO}" && ctx.strcmd.HasOption("-n") {
ctx.shline.line.Warn0("Please use ${ECHO_N} instead of \"echo -n\".")
}
}
-func (ctx *ShelltextContext) checkPipeExitcode() {
- if G.opts.WarnExtra && ctx.state != scstCaseLabelCont && ctx.shellword == "|" {
- line := ctx.shline.line
+func (ctx *ShellProgramChecker) checkPipeExitcode(line *Line, pipeline *MkShPipeline) {
+ if G.opts.Debug {
+ defer tracecall()()
+ }
+
+ if G.opts.WarnExtra && len(pipeline.Cmds) > 1 {
line.Warn0("The exitcode of the left-hand-side command of the pipe operator is ignored.")
Explain(
"In a shell command like \"cat *.txt | grep keyword\", if the command",
@@ -755,10 +774,15 @@
}
}
-func (ctx *ShelltextContext) checkSetE(eflag *bool, prevToken string) {
- if G.opts.WarnExtra && ctx.shellword == ";" && ctx.state != scstCondCont && ctx.state != scstForCont && !*eflag {
+func (ctx *ShellProgramChecker) checkSetE(list *MkShList, eflag *bool) {
+ if G.opts.Debug {
+ defer tracecall()()
+ }
+
+ // Disabled until the shell parser can recognize "command || exit 1" reliably.
+ if false && G.opts.WarnExtra && !*eflag && "the current token" == ";" {
*eflag = true
- ctx.shline.line.Warn1("Please switch to \"set -e\" mode before using a semicolon (the one after %q) to separate commands.", prevToken)
+ ctx.shline.line.Warn1("Please switch to \"set -e\" mode before using a semicolon (the one after %q) to separate commands.", "previous token")
Explain(
"Normally, when a shell command fails (returns non-zero), the",
"remaining commands are still executed. For example, the following",
@@ -777,6 +801,10 @@
// Some shell commands should not be used in the install phase.
func (shline *ShellLine) checkCommandUse(shellcmd string) {
+ if G.opts.Debug {
+ defer tracecall()()
+ }
+
if G.Mk == nil || !matches(G.Mk.target, `^(?:pre|do|post)-install$`) {
return
}
@@ -815,113 +843,6 @@
}
}
-func (shline *ShellLine) nextState(state scState, shellword string) scState {
- switch {
- case shellword == ";;":
- return scstCaseLabel
- case state == scstCaseLabelCont && shellword == "|":
- return scstCaseLabel
- case matches(shellword, `^[;&\|]+$`):
- return scstStart
- case state == scstStart:
- switch shellword {
- case "${INSTALL}":
- return scstInstall
- case "${MKDIR}":
- return scstMkdir
- case "${PAX}":
- return scstPax
- case "${SED}":
- return scstSed
- case "${ECHO}", "echo":
- return scstEcho
- case "${RUN}", "then", "else", "do", "(":
- return scstStart
- case "set":
- return scstSet
- case "if", "elif", "while":
- return scstCond
- case "case":
- return scstCase
- case "for":
- return scstFor
- default:
- switch {
- case matches(shellword, `^\$\{INSTALL_[A-Z]+_DIR\}$`):
- return scstInstallDir
- case matches(shellword, reShVarassign):
- return scstStart
- default:
- return scstCont
- }
- }
- case state == scstMkdir:
- return scstMkdir
- case state == scstInstall && shellword == "-d":
- return scstInstallD
- case state == scstInstall, state == scstInstallD:
- if matches(shellword, `^-[ogm]$`) {
- return scstCont // XXX: why not keep the state?
- }
- return state
- case state == scstInstallDir && hasPrefix(shellword, "-"):
- return scstCont
- case state == scstInstallDir && hasPrefix(shellword, "$"):
- return scstInstallDir2
- case state == scstInstallDir || state == scstInstallDir2:
- return state
- case state == scstPax && shellword == "-s":
- return scstPaxS
- case state == scstPax && hasPrefix(shellword, "-"):
- return scstPax
- case state == scstPax:
- return scstCont
- case state == scstPaxS:
- return scstPax
- case state == scstSed && shellword == "-e":
- return scstSedE
- case state == scstSed && hasPrefix(shellword, "-"):
- return scstSed
- case state == scstSed:
- return scstCont
- case state == scstSedE:
- return scstSed
- case state == scstSet:
- return scstSetCont
- case state == scstSetCont:
- return scstSetCont
- case state == scstCase:
- return scstCaseIn
- case state == scstCaseIn && shellword == "in":
- return scstCaseLabel
- case state == scstCaseLabel && shellword == "esac":
- return scstCont
- case state == scstCaseLabel:
- return scstCaseLabelCont
- case state == scstCaseLabelCont && shellword == ")":
- return scstStart
- case state == scstCont:
- return scstCont
- case state == scstCond:
- return scstCondCont
- case state == scstCondCont:
- return scstCondCont
- case state == scstFor:
- return scstForIn
- case state == scstForIn && shellword == "in":
- return scstForCont
- case state == scstForCont:
- return scstForCont
- case state == scstEcho:
- return scstCont
- default:
- if G.opts.Debug {
- traceStep("Internal pkglint error: shellword.nextState state=%s shellword=%q", state, shellword)
- }
- return scstStart
- }
-}
-
// Example: "word1 word2;;;" => "word1", "word2", ";;", ";"
func splitIntoShellTokens(line *Line, text string) (tokens []string, rest string) {
if G.opts.Debug {
@@ -943,10 +864,10 @@
if atom.Type == shtSpace && q == shqPlain {
emit()
} else if atom.Type == shtWord || atom.Type == shtVaruse || atom.Quoting != shqPlain {
- word += atom.Text
+ word += atom.MkText
} else {
emit()
- tokens = append(tokens, atom.Text)
+ tokens = append(tokens, atom.MkText)
}
}
emit()
@@ -968,7 +889,7 @@
words = append(words, word)
word = ""
} else {
- word += atom.Text
+ word += atom.MkText
}
}
if word != "" && atoms[len(atoms)-1].Quoting == shqPlain {
--- pkgsrc/pkgtools/pkglint/files/Attic/shell_test.go 2016/06/05 11:24:32 1.9
+++ pkgsrc/pkgtools/pkglint/files/Attic/shell_test.go 2016/07/07 12:09:27 1.10
@@ -6,23 +6,6 @@
check "gopkg.in/check.v1"
)
-func (s *Suite) TestReShellToken(c *check.C) {
- re := `^(?:` + reShellToken + `)$`
- matches := check.NotNil
- doesntMatch := check.IsNil
-
- c.Check(match("", re), doesntMatch)
- c.Check(match("$var", re), matches)
- c.Check(match("$var$var", re), matches)
- c.Check(match("$var;;", re), doesntMatch) // More than one token
- c.Check(match("'single-quoted'", re), matches)
- c.Check(match("\"", re), doesntMatch) // Incomplete string
- c.Check(match("'...'\"...\"", re), matches) // Mixed strings
- c.Check(match("\"...\"", re), matches)
- c.Check(match("`cat file`", re), matches)
- c.Check(match("${file%.c}.o", re), matches)
-}
-
func (s *Suite) Test_SplitIntoShellTokens_LineContinuation(c *check.C) {
words, rest := splitIntoShellTokens(dummyLine, "if true; then \\")
@@ -94,6 +77,32 @@
c.Check(rest, equals, "")
}
+func (s *Suite) Test_splitIntoShellTokens_Redirect(c *check.C) {
+ words, rest := splitIntoShellTokens(dummyLine, "echo 1>output 2>>append 3>|clobber 4>&5 6<input >>append")
+
+ c.Check(words, deepEquals, []string{
+ "echo",
+ "1>", "output",
+ "2>>", "append",
+ "3>|", "clobber",
+ "4>&", "5",
+ "6<", "input",
+ ">>", "append"})
+ c.Check(rest, equals, "")
+
+ words, rest = splitIntoShellTokens(dummyLine, "echo 1> output 2>> append 3>| clobber 4>& 5 6< input >> append")
+
+ c.Check(words, deepEquals, []string{
+ "echo",
+ "1>", "output",
+ "2>>", "append",
+ "3>|", "clobber",
+ "4>&", "5",
+ "6<", "input",
+ ">>", "append"})
+ c.Check(rest, equals, "")
+}
+
func (s *Suite) TestChecklineMkShellCommandLine(c *check.C) {
s.UseCommandLine(c, "-Wall")
G.Mk = s.NewMkLines("fname",
@@ -108,9 +117,7 @@
c.Check(s.Output(), equals, ""+
"WARN: fname:1: Unknown shell command \"uname\".\n"+
- "WARN: fname:1: Please switch to \"set -e\" mode before using a semicolon (the one after \"uname=`uname`\") to separate commands.\n"+
"WARN: fname:1: Unknown shell command \"echo\".\n"+
- "WARN: fname:1: Unquoted shell variable \"uname\".\n"+
"WARN: fname:1: Unknown shell command \"echo\".\n")
s.RegisterTool(&Tool{Name: "echo", Predefined: true})
@@ -137,6 +144,12 @@
"WARN: fname:1: COMMENT may not be used in any file; it is a write-only variable.\n"+
"WARN: fname:1: Please move ${COMMENT:Q} outside of any quoting characters.\n")
+ shline.CheckShellCommandLine("echo target=$@ exitcode=$$? '$$' \"\\$$\"")
+
+ c.Check(s.Output(), equals, ""+
+ "WARN: fname:1: Please use \"${.TARGET}\" instead of \"$@\".\n"+
+ "WARN: fname:1: The $? shell variable is often not available in \"set -e\" mode.\n")
+
shline.CheckShellCommandLine("echo $$@")
c.Check(s.Output(), equals, "WARN: fname:1: The $@ shell variable should only be used in double quotes.\n")
@@ -145,9 +158,9 @@
c.Check(s.Output(), equals, ""+
"WARN: fname:1: Pkglint parse error in ShTokenizer.ShAtom at \"$$\\\"\" (quoting=d)\n"+
- "WARN: fname:1: Pkglint parse error in ShellLine.CheckShellCommand at \"$$\\\"\" (state=start)\n")
+ "WARN: fname:1: Pkglint ShellLine.CheckShellCommand: parse error at [\"]\n")
- shline.CheckShellCommandLine("echo \"\\n\"") // As seen by make(1); the shell sees: echo "\n"
+ shline.CheckShellCommandLine("echo \"\\n\"")
c.Check(s.Output(), equals, "")
@@ -163,8 +176,8 @@
shline.CheckShellCommandLine("${RUN} subdir=\"`unzip -c \"$$e\" install.rdf | awk '/re/ { print \"hello\" }'`\"")
c.Check(s.Output(), equals, ""+
- "WARN: fname:1: Unknown shell command \"unzip\".\n"+
"WARN: fname:1: The exitcode of the left-hand-side command of the pipe operator is ignored.\n"+
+ "WARN: fname:1: Unknown shell command \"unzip\".\n"+
"WARN: fname:1: Unknown shell command \"awk\".\n")
// From mail/thunderbird/Makefile, rev. 1.159
@@ -178,13 +191,12 @@
c.Check(s.Output(), equals, ""+
"WARN: fname:1: XPI_FILES is used but not defined. Spelling mistake?\n"+
- "WARN: fname:1: UNZIP_CMD is used but not defined. Spelling mistake?\n"+
"WARN: fname:1: The exitcode of the left-hand-side command of the pipe operator is ignored.\n"+
+ "WARN: fname:1: UNZIP_CMD is used but not defined. Spelling mistake?\n"+
"WARN: fname:1: Unknown shell command \"awk\".\n"+
- "WARN: fname:1: MKDIR is used but not defined. Spelling mistake?\n"+
"WARN: fname:1: Unknown shell command \"${MKDIR}\".\n"+
- "WARN: fname:1: UNZIP_CMD is used but not defined. Spelling mistake?\n"+
- "WARN: fname:1: Unquoted shell variable \"e\".\n")
+ "WARN: fname:1: MKDIR is used but not defined. Spelling mistake?\n"+
+ "WARN: fname:1: UNZIP_CMD is used but not defined. Spelling mistake?\n")
// From x11/wxGTK28/Makefile
shline.CheckShellCommandLine("" +
@@ -207,7 +219,14 @@
shline.CheckShellCommandLine("${RUN} ${INSTALL_DATA_DIR} share/pkgbase ${PREFIX}/share/pkgbase")
- c.Check(s.Output(), equals, "NOTE: fname:1: You can use AUTO_MKDIRS=yes or \"INSTALLATION_DIRS+= share/pkgbase\" instead of this command.\n")
+ c.Check(s.Output(), equals, ""+
+ "NOTE: fname:1: You can use AUTO_MKDIRS=yes or \"INSTALLATION_DIRS+= share/pkgbase\" instead of \"${INSTALL_DATA_DIR}\".\n"+
+ "WARN: fname:1: The INSTALL_*_DIR commands can only handle one directory at a time.\n")
+
+ // See PR 46570, item "1. It does not"
+ shline.CheckShellCommandLine("for x in 1 2 3; do echo \"$$x\" || exit 1; done")
+
+ c.Check(s.Output(), equals, "") // No warning about missing error checking.
}
func (s *Suite) TestShellLine_CheckShelltext_nofix(c *check.C) {
@@ -271,6 +290,10 @@
c.Check(tokens, deepEquals, []string{text})
c.Check(rest, equals, "")
+ shline.CheckWord(text, false)
+
+ c.Check(s.Output(), equals, "WARN: fname:1: Unknown shell command \"echo\".\n")
+
shline.CheckShellCommandLine(text)
c.Check(s.Output(), equals, ""+ // No parse errors
@@ -301,7 +324,7 @@
shline.CheckWord("${SED_FILE.${id}}", false)
- c.Check(s.Output(), equals, "WARN: fname:1: SED_FILE.${id} is used but not defined. Spelling mistake?\n")
+ c.Check(s.Output(), equals, "") // No warning for variables that are partly indirect.
shline.CheckWord("\"$@\"", false)
@@ -436,9 +459,22 @@
shline.CheckShellCommandLine(shline.mkline.Shellcmd())
c.Check(s.Output(), equals, ""+
- "NOTE: Makefile:85: You can use AUTO_MKDIRS=yes or \"INSTALLATION_DIRS+= dir1\" instead of this command.\n"+
- "NOTE: Makefile:85: You can use AUTO_MKDIRS=yes or \"INSTALLATION_DIRS+= dir2\" instead of this command.\n"+
+ "NOTE: Makefile:85: You can use AUTO_MKDIRS=yes or \"INSTALLATION_DIRS+= dir1\" instead of \"${INSTALL_DATA_DIR}\".\n"+
+ "NOTE: Makefile:85: You can use AUTO_MKDIRS=yes or \"INSTALLATION_DIRS+= dir2\" instead of \"${INSTALL_DATA_DIR}\".\n"+
"WARN: Makefile:85: The INSTALL_*_DIR commands can only handle one directory at a time.\n")
+
+ shline.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.
+ c.Check(s.Output(), equals, ""+
+ "NOTE: Makefile:85: You can use AUTO_MKDIRS=yes or \"INSTALLATION_DIRS+= share/examples/gdchart\" instead of \"${INSTALL_DATA_DIR}\".\n")
+
+ shline.CheckShellCommandLine("${INSTALL_DATA_DIR} -d -m 0755 ${DESTDIR}${PREFIX}/dir1 ${PREFIX}/dir2")
+
+ c.Check(s.Output(), equals, ""+
+ "NOTE: Makefile:85: You can use AUTO_MKDIRS=yes or \"INSTALLATION_DIRS+= dir1\" instead of \"${INSTALL_DATA_DIR}\".\n"+
+ "NOTE: Makefile:85: You can use AUTO_MKDIRS=yes or \"INSTALLATION_DIRS+= dir2\" instead of \"${INSTALL_DATA_DIR}\".\n"+
+ "WARN: Makefile:85: The INSTALL_*_DIR commands can only handle one directory at a time.\n")
}
func (s *Suite) TestShellLine_CheckShellCommandLine_InstallD(c *check.C) {
@@ -447,8 +483,8 @@
shline.CheckShellCommandLine(shline.mkline.Shellcmd())
c.Check(s.Output(), equals, ""+
- "WARN: Makefile:85: Please use AUTO_MKDIRS instead of \"${INSTALL} -d\".\n"+
- "WARN: Makefile:85: Please use AUTO_MKDIRS instead of \"${INSTALL} -d\".\n")
+ "NOTE: Makefile:85: You can use AUTO_MKDIRS=yes or \"INSTALLATION_DIRS+= dir1\" instead of \"${INSTALL} -d\".\n"+
+ "NOTE: Makefile:85: You can use AUTO_MKDIRS=yes or \"INSTALLATION_DIRS+= dir2\" instead of \"${INSTALL} -d\".\n")
}
func (s *Suite) TestShellLine_(c *check.C) {
--- pkgsrc/pkgtools/pkglint/files/Attic/globaldata.go 2016/06/10 19:42:42 1.10
+++ pkgsrc/pkgtools/pkglint/files/Attic/globaldata.go 2016/07/07 12:09:27 1.11
--- pkgsrc/pkgtools/pkglint/files/Attic/vardefs.go 2016/06/19 18:03:29 1.10
+++ pkgsrc/pkgtools/pkglint/files/Attic/vardefs.go 2016/07/07 12:09:27 1.11
@@ -144,11 +144,12 @@
acl("BUILDLINK_PASSTHRU_DIRS", lkShell, CheckvarPathname, "Makefile, Makefile.common, buildlink3.mk, hacks.mk: append")
acl("BUILDLINK_PASSTHRU_RPATHDIRS", lkShell, CheckvarPathname, "Makefile, Makefile.common, buildlink3.mk, hacks.mk: append")
acl("BUILDLINK_PKGSRCDIR.*", lkNone, CheckvarRelativePkgDir, "buildlink3.mk: default, use-loadtime")
- acl("BUILDLINK_PREFIX.*", lkNone, CheckvarPathname, "builtin.mk: set, use; buildlink3.mk: use; Makefile, Makefile.common, *.mk: use")
+ acl("BUILDLINK_PREFIX.*", lkNone, CheckvarPathname, "builtin.mk: set, use; Makefile, Makefile.common, *.mk: use")
acl("BUILDLINK_RPATHDIRS.*", lkShell, CheckvarPathname, "buildlink3.mk: append")
acl("BUILDLINK_TARGETS", lkShell, CheckvarIdentifier, "")
acl("BUILDLINK_FNAME_TRANSFORM.*", lkNone, CheckvarSedCommands, "Makefile, buildlink3.mk, builtin.mk, hacks.mk: append")
acl("BUILDLINK_TRANSFORM", lkShell, CheckvarWrapperTransform, "*: append")
+ acl("BUILDLINK_TRANSFORM.*", lkShell, CheckvarWrapperTransform, "*: append")
acl("BUILDLINK_TREE", lkShell, CheckvarIdentifier, "buildlink3.mk: append")
acl("BUILD_DEFS", lkShell, CheckvarVarname, "Makefile, Makefile.common, options.mk: append")
acl("BUILD_DEPENDS", lkSpace, CheckvarDependencyWithPath, "Makefile, Makefile.common, *.mk: append")
@@ -157,12 +158,15 @@
sys("BUILD_MAKE_CMD", lkNone, CheckvarShellCommand)
pkglist("BUILD_MAKE_FLAGS", lkShell, CheckvarShellWord)
pkglist("BUILD_TARGET", lkShell, CheckvarIdentifier)
+ pkglist("BUILD_TARGET.*", lkShell, CheckvarIdentifier)
pkg("BUILD_USES_MSGFMT", lkNone, CheckvarYes)
acl("BUILTIN_PKG", lkNone, CheckvarIdentifier, "builtin.mk: set, use-loadtime, use")
acl("BUILTIN_PKG.*", lkNone, CheckvarPkgName, "builtin.mk: set, use-loadtime, use")
acl("BUILTIN_FIND_FILES_VAR", lkShell, CheckvarVarname, "builtin.mk: set")
acl("BUILTIN_FIND_FILES.*", lkShell, CheckvarPathname, "builtin.mk: set")
acl("BUILTIN_FIND_GREP.*", lkNone, CheckvarString, "builtin.mk: set")
+ acl("BUILTIN_FIND_HEADERS_VAR", lkShell, CheckvarVarname, "builtin.mk: set")
+ acl("BUILTIN_FIND_HEADERS.*", lkShell, CheckvarPathname, "builtin.mk: set")
acl("BUILTIN_FIND_LIBS", lkShell, CheckvarPathname, "builtin.mk: set")
acl("BUILTIN_IMAKE_CHECK", lkShell, CheckvarUnchecked, "builtin.mk: set")
acl("BUILTIN_IMAKE_CHECK.*", lkNone, CheckvarYesNo, "")
@@ -171,9 +175,10 @@
acl("CATEGORIES", lkShell, CheckvarCategory, "Makefile: set, append; Makefile.common: set, default, append")
sys("CC_VERSION", lkNone, CheckvarMessage)
sys("CC", lkNone, CheckvarShellCommand)
- pkglist("CFLAGS*", lkShell, CheckvarCFlag) // may also be changed by the user
+ pkglist("CFLAGS", lkShell, CheckvarCFlag) // may also be changed by the user
+ pkglist("CFLAGS.*", lkShell, CheckvarCFlag) // may also be changed by the user
acl("CHECK_BUILTIN", lkNone, CheckvarYesNo, "builtin.mk: default; Makefile: set")
- acl("CHECK_BUILTIN.*", lkNone, CheckvarYesNo, "buildlink3.mk: set; builtin.mk: default; *: use-loadtime")
+ acl("CHECK_BUILTIN.*", lkNone, CheckvarYesNo, "Makefile, options.mk, buildlink3.mk: set; builtin.mk: default; *: use-loadtime")
acl("CHECK_FILES_SKIP", lkShell, CheckvarBasicRegularExpression, "Makefile, Makefile.common: append")
pkg("CHECK_FILES_SUPPORTED", lkNone, CheckvarYesNo)
usr("CHECK_HEADERS", lkNone, CheckvarYesNo)
@@ -190,11 +195,14 @@
pkglist("CHECK_WRKREF_SKIP", lkShell, CheckvarPathmask)
pkg("CMAKE_ARG_PATH", lkNone, CheckvarPathname)
pkglist("CMAKE_ARGS", lkShell, CheckvarShellWord)
+ pkglist("CMAKE_ARGS.*", lkShell, CheckvarShellWord)
acl("COMMENT", lkNone, CheckvarComment, "Makefile, Makefile.common: set, append")
acl("COMPILER_RPATH_FLAG", lkNone, enum("-Wl,-rpath"), "*: use")
pkglist("CONFIGURE_ARGS", lkShell, CheckvarShellWord)
+ pkglist("CONFIGURE_ARGS.*", lkShell, CheckvarShellWord)
pkglist("CONFIGURE_DIRS", lkShell, CheckvarWrksrcSubdirectory)
acl("CONFIGURE_ENV", lkShell, CheckvarShellWord, "Makefile, Makefile.common: append, set, use; buildlink3.mk, builtin.mk: append; *.mk: append, use")
+ acl("CONFIGURE_ENV.*", lkShell, CheckvarShellWord, "Makefile, Makefile.common: append, set, use; buildlink3.mk, builtin.mk: append; *.mk: append, use")
pkg("CONFIGURE_HAS_INFODIR", lkNone, CheckvarYesNo)
pkg("CONFIGURE_HAS_LIBDIR", lkNone, CheckvarYesNo)
pkg("CONFIGURE_HAS_MANDIR", lkNone, CheckvarYesNo)
@@ -209,10 +217,12 @@
pkglist("CONF_FILES_PERMS", lkShell, CheckvarPerms)
sys("COPY", lkNone, enum("-c")) // The flag that tells ${INSTALL} to copy a file
sys("CPP", lkNone, CheckvarShellCommand)
- pkglist("CPPFLAGS*", lkShell, CheckvarCFlag)
+ pkglist("CPPFLAGS", lkShell, CheckvarCFlag)
+ pkglist("CPPFLAGS.*", lkShell, CheckvarCFlag)
acl("CRYPTO", lkNone, CheckvarYes, "Makefile: set")
sys("CXX", lkNone, CheckvarShellCommand)
- pkglist("CXXFLAGS*", lkShell, CheckvarCFlag)
+ pkglist("CXXFLAGS", lkShell, CheckvarCFlag)
+ pkglist("CXXFLAGS.*", lkShell, CheckvarCFlag)
acl("DEINSTALL_FILE", lkNone, CheckvarPathname, "Makefile: set")
acl("DEINSTALL_SRC", lkShell, CheckvarPathname, "Makefile: set; Makefile.common: default, set")
acl("DEINSTALL_TEMPLATES", lkShell, CheckvarPathname, "Makefile: set, append; Makefile.common: set, default, append")
@@ -220,7 +230,7 @@
sys("DELAYED_WARNING_MSG", lkNone, CheckvarShellCommand)
pkglist("DEPENDS", lkSpace, CheckvarDependencyWithPath)
usr("DEPENDS_TARGET", lkShell, CheckvarIdentifier)
- acl("DESCR_SRC", lkShell, CheckvarPathname, "Makefile: set; Makefile.common: default, set")
+ acl("DESCR_SRC", lkShell, CheckvarPathname, "Makefile: set, append; Makefile.common: default, set")
sys("DESTDIR", lkNone, CheckvarPathname)
acl("DESTDIR_VARNAME", lkNone, CheckvarVarname, "Makefile, Makefile.common: set")
sys("DEVOSSAUDIO", lkNone, CheckvarPathname)
@@ -376,17 +386,19 @@
usr("KRB5_DEFAULT", lkNone, enum("heimdal mit-krb5"))
sys("KRB5_TYPE", lkNone, CheckvarIdentifier)
sys("LD", lkNone, CheckvarShellCommand)
- pkglist("LDFLAGS*", lkShell, CheckvarLdFlag)
+ pkglist("LDFLAGS", lkShell, CheckvarLdFlag)
+ pkglist("LDFLAGS.*", lkShell, CheckvarLdFlag)
sys("LIBGRP", lkNone, CheckvarUserGroupName)
sys("LIBMODE", lkNone, CheckvarFileMode)
sys("LIBOWN", lkNone, CheckvarUserGroupName)
sys("LIBOSSAUDIO", lkNone, CheckvarPathname)
- pkglist("LIBS*", lkShell, CheckvarLdFlag)
+ pkglist("LIBS", lkShell, CheckvarLdFlag)
+ pkglist("LIBS.*", lkShell, CheckvarLdFlag)
sys("LIBTOOL", lkNone, CheckvarShellCommand)
acl("LIBTOOL_OVERRIDE", lkShell, CheckvarPathmask, "Makefile: set, append")
pkglist("LIBTOOL_REQD", lkShell, CheckvarVersion)
- acl("LICENCE", lkNone, CheckvarLicense, "Makefile, Makefile.common: set; options.mk: set")
- acl("LICENSE", lkNone, CheckvarLicense, "Makefile, Makefile.common: set; options.mk: set")
+ acl("LICENCE", lkNone, CheckvarLicense, "Makefile, Makefile.common, options.mk: set")
+ acl("LICENSE", lkNone, CheckvarLicense, "Makefile, Makefile.common, options.mk: set")
pkg("LICENSE_FILE", lkNone, CheckvarPathname)
sys("LINKER_RPATH_FLAG", lkNone, CheckvarShellWord)
sys("LOWER_OPSYS", lkNone, CheckvarIdentifier)
@@ -398,12 +410,14 @@
acl("MAINTAINER", lkNone, CheckvarMailAddress, "Makefile: set; Makefile.common: default")
sys("MAKE", lkNone, CheckvarShellCommand)
pkglist("MAKEFLAGS", lkShell, CheckvarShellWord)
- acl("MAKEVARS", lkShell, CheckvarVarname, "builtin.mk: append; buildlink3.mk: append; hacks.mk: append")
+ acl("MAKEVARS", lkShell, CheckvarVarname, "buildlink3.mk, builtin.mk, hacks.mk: append")
pkglist("MAKE_DIRS", lkShell, CheckvarPathname)
pkglist("MAKE_DIRS_PERMS", lkShell, CheckvarPerms)
- acl("MAKE_ENV", lkShell, CheckvarShellWord, "Makefile: append, set, use; Makefile.common: append, set, use; buildlink3.mk: append; builtin.mk: append; *.mk: append, use")
+ acl("MAKE_ENV", lkShell, CheckvarShellWord, "Makefile, Makefile.common: append, set, use; buildlink3.mk, builtin.mk: append; *.mk: append, use")
+ acl("MAKE_ENV.*", lkShell, CheckvarShellWord, "Makefile, Makefile.common: append, set, use; buildlink3.mk, builtin.mk: append; *.mk: append, use")
pkg("MAKE_FILE", lkNone, CheckvarPathname)
pkglist("MAKE_FLAGS", lkShell, CheckvarShellWord)
+ pkglist("MAKE_FLAGS.*", lkShell, CheckvarShellWord)
usr("MAKE_JOBS", lkNone, CheckvarInteger)
pkg("MAKE_JOBS_SAFE", lkNone, CheckvarYesNo)
pkg("MAKE_PROGRAM", lkNone, CheckvarShellCommand)
@@ -445,7 +459,7 @@
sys("MASTER_SITE_XCONTRIB", lkShell, CheckvarFetchURL)
sys("MASTER_SITE_XEMACS", lkShell, CheckvarFetchURL)
pkglist("MESSAGE_SRC", lkShell, CheckvarPathname)
- acl("MESSAGE_SUBST", lkShell, CheckvarShellWord, "Makefile.common: append; Makefile: append; options.mk: append")
+ acl("MESSAGE_SUBST", lkShell, CheckvarShellWord, "Makefile, Makefile.common, options.mk: append")
pkg("META_PACKAGE", lkNone, CheckvarYes)
sys("MISSING_FEATURES", lkShell, CheckvarIdentifier)
acl("MYSQL_VERSIONS_ACCEPTED", lkShell, enum("51 55 56"), "Makefile: set")
@@ -485,8 +499,8 @@
acl("PATCH_ARGS", lkShell, CheckvarShellWord, "")
acl("PATCH_DIST_ARGS", lkShell, CheckvarShellWord, "Makefile: set, append")
acl("PATCH_DIST_CAT", lkNone, CheckvarShellCommand, "")
- acl("PATCH_DIST_STRIP*", lkNone, CheckvarShellWord, "Makefile, Makefile.common: set; buildlink3.mk:; builtin.mk:; *.mk: set")
- acl("PATCH_SITES", lkShell, CheckvarFetchURL, "Makefile: set; options.mk: set; Makefile.common: set")
+ acl("PATCH_DIST_STRIP*", lkNone, CheckvarShellWord, "buildlink3.mk, builtin.mk:; Makefile, Makefile.common, *.mk: set")
+ acl("PATCH_SITES", lkShell, CheckvarFetchURL, "Makefile, Makefile.common, options.mk: set")
acl("PATCH_STRIP", lkNone, CheckvarShellWord, "")
pkg("PERL5_USE_PACKLIST", lkNone, CheckvarYesNo)
acl("PERL5_PACKLIST", lkShell, CheckvarPerl5Packlist, "Makefile: set; options.mk: set, append")
@@ -548,21 +562,21 @@
acl("PKG_OPTIONS", lkSpace, CheckvarOption, "bsd.options.mk: set; *: use-loadtime, use")
usr("PKG_OPTIONS.*", lkSpace, CheckvarOption)
acl("PKG_OPTIONS_DEPRECATED_WARNINGS", lkShell, CheckvarShellWord, "")
- acl("PKG_OPTIONS_GROUP.*", lkSpace, CheckvarOption, "options.mk: set; Makefile: set")
- acl("PKG_OPTIONS_LEGACY_OPTS", lkSpace, CheckvarUnchecked, "Makefile, Makefile.common: append; options.mk: append")
- acl("PKG_OPTIONS_LEGACY_VARS", lkSpace, CheckvarUnchecked, "Makefile, Makefile.common: append; options.mk: append")
+ acl("PKG_OPTIONS_GROUP.*", lkSpace, CheckvarOption, "Makefile, options.mk: set, append")
+ acl("PKG_OPTIONS_LEGACY_OPTS", lkSpace, CheckvarUnchecked, "Makefile, Makefile.common, options.mk: append")
+ acl("PKG_OPTIONS_LEGACY_VARS", lkSpace, CheckvarUnchecked, "Makefile, Makefile.common, options.mk: append")
acl("PKG_OPTIONS_NONEMPTY_SETS", lkSpace, CheckvarIdentifier, "")
acl("PKG_OPTIONS_OPTIONAL_GROUPS", lkSpace, CheckvarIdentifier, "options.mk: set, append")
- acl("PKG_OPTIONS_REQUIRED_GROUPS", lkSpace, CheckvarIdentifier, "options.mk: set; Makefile: set")
+ acl("PKG_OPTIONS_REQUIRED_GROUPS", lkSpace, CheckvarIdentifier, "Makefile, options.mk: set")
acl("PKG_OPTIONS_SET.*", lkSpace, CheckvarOption, "")
- acl("PKG_OPTIONS_VAR", lkNone, CheckvarPkgOptionsVar, "options.mk: set; Makefile, Makefile.common: set; bsd.options.mk: use-loadtime")
+ acl("PKG_OPTIONS_VAR", lkNone, CheckvarPkgOptionsVar, "Makefile, Makefile.common, options.mk: set; bsd.options.mk: use-loadtime")
acl("PKG_PRESERVE", lkNone, CheckvarYes, "Makefile: set")
acl("PKG_SHELL", lkNone, CheckvarPathname, "Makefile, Makefile.common: set")
acl("PKG_SHELL.*", lkNone, CheckvarPathname, "Makefile, Makefile.common: set")
acl("PKG_SHLIBTOOL", lkNone, CheckvarPathname, "")
pkglist("PKG_SKIP_REASON", lkShell, CheckvarShellWord)
- acl("PKG_SUGGESTED_OPTIONS", lkShell, CheckvarOption, "options.mk: set, append; Makefile: set, append; Makefile.common: set")
- acl("PKG_SUPPORTED_OPTIONS", lkShell, CheckvarOption, "options.mk: set, append, use; Makefile: set, append; Makefile.common: set")
+ acl("PKG_SUGGESTED_OPTIONS", lkShell, CheckvarOption, "Makefile, Makefile.common, options.mk: set, append")
+ acl("PKG_SUPPORTED_OPTIONS", lkShell, CheckvarOption, "Makefile: set, append; Makefile.common: set; options.mk: set, append, use")
pkg("PKG_SYSCONFDIR*", lkNone, CheckvarPathname)
pkglist("PKG_SYSCONFDIR_PERMS", lkShell, CheckvarPerms)
sys("PKG_SYSCONFBASEDIR", lkNone, CheckvarPathname)
@@ -586,7 +600,7 @@
sys("PTHREAD_CFLAGS", lkShell, CheckvarCFlag)
sys("PTHREAD_LDFLAGS", lkShell, CheckvarLdFlag)
sys("PTHREAD_LIBS", lkShell, CheckvarLdFlag)
- acl("PTHREAD_OPTS", lkShell, enum("native optional require"), "Makefile: set, append; Makefile.common: append; buildlink3.mk: append")
+ acl("PTHREAD_OPTS", lkShell, enum("native optional require"), "Makefile: set, append; Makefile.common, buildlink3.mk: append")
sys("PTHREAD_TYPE", lkNone, CheckvarIdentifier) // Or "native" or "none".
pkg("PY_PATCHPLIST", lkNone, CheckvarYes)
acl("PYPKGPREFIX", lkNone, enum("py27 py33 py34 py35"), "pyversion.mk: set; *: use-loadtime, use")
@@ -638,7 +652,8 @@
sys("STEP_MSG", lkNone, CheckvarShellCommand)
acl("SUBDIR", lkShell, CheckvarFilename, "Makefile: append; *:")
acl("SUBST_CLASSES", lkShell, CheckvarIdentifier, "Makefile: set, append; *: append")
- acl("SUBST_FILES.*", lkShell, CheckvarPathmask, "Makefile: set, append; Makefile.*, *.mk: set, append")
+ acl("SUBST_CLASSES.*", lkShell, CheckvarIdentifier, "Makefile: set, append; *: append")
+ acl("SUBST_FILES.*", lkShell, CheckvarPathmask, "Makefile, Makefile.*, *.mk: set, append")
acl("SUBST_FILTER_CMD.*", lkNone, CheckvarShellCommand, "Makefile, Makefile.*, *.mk: set")
acl("SUBST_MESSAGE.*", lkNone, CheckvarMessage, "Makefile, Makefile.*, *.mk: set")
acl("SUBST_SED.*", lkNone, CheckvarSedCommands, "Makefile, Makefile.*, *.mk: set, append")
@@ -672,10 +687,10 @@
acl("USE_BUILTIN.*", lkNone, CheckvarYesNoIndirectly, "builtin.mk: set")
pkg("USE_CMAKE", lkNone, CheckvarYes)
usr("USE_DESTDIR", lkNone, CheckvarYes)
- pkg("USE_FEATURES", lkShell, CheckvarIdentifier)
+ pkglist("USE_FEATURES", lkShell, CheckvarIdentifier)
pkg("USE_GCC_RUNTIME", lkNone, CheckvarYesNo)
pkg("USE_GNU_CONFIGURE_HOST", lkNone, CheckvarYesNo)
- acl("USE_GNU_ICONV", lkNone, CheckvarYes, "Makefile, Makefile.common: set; options.mk: set")
+ acl("USE_GNU_ICONV", lkNone, CheckvarYes, "Makefile, Makefile.common, options.mk: set")
acl("USE_IMAKE", lkNone, CheckvarYes, "Makefile: set")
pkg("USE_JAVA", lkNone, enum("run yes build"))
pkg("USE_JAVA2", lkNone, enum("YES yes no 1.4 1.5 6 7 8"))
@@ -689,6 +704,7 @@
pkg("USE_PKGLOCALEDIR", lkNone, CheckvarYesNo)
usr("USE_PKGSRC_GCC", lkNone, CheckvarYes)
acl("USE_TOOLS", lkShell, CheckvarTool, "*: append")
+ acl("USE_TOOLS.*", lkShell, CheckvarTool, "*: append")
pkg("USE_X11", lkNone, CheckvarYes)
sys("WARNING_MSG", lkNone, CheckvarShellCommand)
sys("WARNING_CAT", lkNone, CheckvarShellCommand)
@@ -753,6 +769,7 @@
return nil
}
var result []AclEntry
+ prevperms := "(first)"
for _, arg := range strings.Split(aclentries, "; ") {
var globs, perms string
if fields := strings.SplitN(arg, ": ", 2); len(fields) == 2 {
@@ -760,6 +777,10 @@
} else {
globs = strings.TrimSuffix(arg, ":")
}
+ if perms == prevperms {
+ fmt.Printf("Repeated permissions for %s: %s\n", varname, perms)
+ }
+ prevperms = perms
var permissions AclPermissions
for _, perm := range strings.Split(perms, ", ") {
switch perm {
--- pkgsrc/pkgtools/pkglint/files/Attic/vartypecheck_test.go 2016/06/10 19:42:42 1.10
+++ pkgsrc/pkgtools/pkglint/files/Attic/vartypecheck_test.go 2016/07/07 12:09:27 1.11
--- pkgsrc/pkgtools/pkglint/files/Attic/licenses.go 2016/06/05 11:24:32 1.4
+++ pkgsrc/pkgtools/pkglint/files/Attic/licenses.go 2016/07/07 12:09:27 1.5
--- pkgsrc/pkgtools/pkglint/files/Attic/substcontext_test.go 2016/01/12 01:02:49 1.4
+++ pkgsrc/pkgtools/pkglint/files/Attic/substcontext_test.go 2016/07/07 12:09:27 1.5
@@ -49,6 +49,23 @@
c.Check(s.Output(), equals, "")
}
+func (s *Suite) Test_SubstContext_OPSYSVARS(c *check.C) {
+ G.opts.WarnExtra = true
+ ctx := new(SubstContext)
+
+ ctx.Varassign(newSubstLine(11, "SUBST_CLASSES.SunOS+=prefix"))
+ ctx.Varassign(newSubstLine(12, "SUBST_CLASSES.NetBSD+=prefix"))
+ ctx.Varassign(newSubstLine(13, "SUBST_FILES.prefix=Makefile"))
+ ctx.Varassign(newSubstLine(14, "SUBST_SED.prefix=s,@PREFIX@,${PREFIX},g"))
+ ctx.Varassign(newSubstLine(15, "SUBST_STAGE.prefix=post-configure"))
+
+ c.Check(ctx.IsComplete(), equals, true)
+
+ ctx.Finish(newSubstLine(15, ""))
+
+ c.Check(s.Output(), equals, "")
+}
+
func (s *Suite) TestSubstContext_NoClass(c *check.C) {
s.UseCommandLine(c, "-Wextra")
ctx := new(SubstContext)
--- pkgsrc/pkgtools/pkglint/files/Attic/mkline.go 2016/06/10 19:42:42 1.12
+++ pkgsrc/pkgtools/pkglint/files/Attic/mkline.go 2016/07/07 12:09:27 1.13
@@ -430,7 +430,7 @@
(vartype == nil || vartype.guessed) &&
!varIsUsed(varname) &&
!(G.Mk != nil && G.Mk.forVars[varname]) &&
- !hasPrefix(varname, "${") {
+ !containsVarRef(varname) {
mkline.Warn1("%s is used but not defined. Spelling mistake?", varname)
}
@@ -999,61 +999,6 @@
}
}
-const reVarnamePlural = `^(?:` +
- `.*[Ss]` +
- `|.*LIST` +
- `|.*_AWK` +
- `|.*_ENV` +
- `|.*_OVERRIDE` +
- `|.*_PREREQ` +
- `|.*_REQD` +
- `|.*_SED` +
- `|.*_SKIP` +
- `|.*_SRC` +
- `|.*_SUBST` +
- `|.*_TARGET` +
- `|.*_TMPL` +
- `|BROKEN_EXCEPT_ON_PLATFORM` +
- `|BROKEN_ON_PLATFORM` +
- `|BUILDLINK_DEPMETHOD` +
- `|BUILDLINK_LDADD` +
- `|BUILDLINK_TRANSFORM` +
- `|COMMENT` +
- `|CRYPTO` +
- `|DEINSTALL_TEMPLATE` +
- `|EVAL_PREFIX` +
- `|EXTRACT_ONLY` +
- `|FETCH_MESSAGE` +
- `|FIX_RPATH` +
- `|GENERATE_PLIST` +
- `|INSTALL_TEMPLATE` +
- `|INTERACTIVE_STAGE` +
- `|LICENSE` +
- `|MASTER_SITE_.*` +
- `|MASTER_SORT_REGEX` +
- `|NOT_FOR_COMPILER` +
- `|NOT_FOR_PLATFORM` +
- `|ONLY_FOR_COMPILER` +
- `|ONLY_FOR_PLATFORM` +
- `|PERL5_PACKLIST` +
- `|PLIST_CAT` +
- `|PLIST_PRE` +
- `|PKG_FAIL_REASON` +
- `|PKG_SKIP_REASON` +
- `|PREPEND_PATH` +
- `|PYTHON_VERSIONS_INCOMPATIBLE` +
- `|REPLACE_INTERPRETER` +
- `|REPLACE_PERL` +
- `|REPLACE_RUBY` +
- `|RESTRICTED` +
- `|SITES_.+` +
- `|TOOLS_ALIASES\..+` +
- `|TOOLS_BROKEN` +
- `|TOOLS_CREATE` +
- `|TOOLS_GNU_MISSING` +
- `|TOOLS_NOOP` +
- `)$`
-
func (mkline *MkLine) CheckVartype(varname string, op MkOperator, value, comment string) {
if G.opts.Debug {
defer tracecall(varname, op, value, comment)()
@@ -1063,22 +1008,16 @@
return
}
- varbase := varnameBase(varname)
vartype := mkline.getVariableType(varname)
if op == opAssignAppend {
- if vartype != nil {
- if !vartype.MayBeAppendedTo() {
- mkline.Warn0("The \"+=\" operator should only be used with lists.")
- }
- } else if !hasPrefix(varbase, "_") && !matches(varbase, reVarnamePlural) {
- mkline.Warn1("As %s is modified using \"+=\", its name should indicate plural.", varname)
+ if vartype != nil && !vartype.MayBeAppendedTo() {
+ mkline.Warn0("The \"+=\" operator should only be used with lists.")
}
}
switch {
case vartype == nil:
- // Cannot check anything if the type is not known.
if G.opts.Debug {
traceStep1("Unchecked variable assignment for %s.", varname)
}
@@ -1380,7 +1319,7 @@
)
func (nq NeedsQuoting) String() string {
- return [...]string{"no", "yes", "doesn't matter", "don't known"}[nq]
+ return [...]string{"no", "yes", "doesn't matter", "don't know"}[nq]
}
func (mkline *MkLine) variableNeedsQuoting(varname string, vartype *Vartype, vuc *VarUseContext) (needsQuoting NeedsQuoting) {
@@ -1530,7 +1469,7 @@
switch {
case hasSuffix(varbase, "DIRS"):
gtype = &Vartype{lkShell, CheckvarPathmask, allowRuntime, true}
- case hasSuffix(varbase, "DIR"), hasSuffix(varname, "_HOME"):
+ case hasSuffix(varbase, "DIR") && !hasSuffix(varbase, "DESTDIR"), hasSuffix(varname, "_HOME"):
gtype = &Vartype{lkNone, CheckvarPathname, allowRuntime, true}
case hasSuffix(varbase, "FILES"):
gtype = &Vartype{lkShell, CheckvarPathmask, allowRuntime, true}
--- pkgsrc/pkgtools/pkglint/files/Attic/mkline_test.go 2016/06/05 11:24:32 1.12
+++ pkgsrc/pkgtools/pkglint/files/Attic/mkline_test.go 2016/07/07 12:09:27 1.13
@@ -292,16 +292,11 @@
G.Mk = s.NewMkLines("Makefile",
"# $"+"NetBSD$",
- "ac_cv_libpari_libs+=\t-L${BUILDLINK_PREFIX.pari}/lib", // From math/clisp-pari/Makefile, rev. 1.8
- "var+=value")
+ "ac_cv_libpari_libs+=\t-L${BUILDLINK_PREFIX.pari}/lib") // From math/clisp-pari/Makefile, rev. 1.8
G.Mk.mklines[1].checkVarassign()
- G.Mk.mklines[2].checkVarassign()
- c.Check(s.Output(), equals, ""+
- "WARN: Makefile:2: ac_cv_libpari_libs is defined but not used. Spelling mistake?\n"+
- "WARN: Makefile:3: As var is modified using \"+=\", its name should indicate plural.\n"+
- "WARN: Makefile:3: var is defined but not used. Spelling mistake?\n")
+ c.Check(s.Output(), equals, "WARN: Makefile:2: ac_cv_libpari_libs is defined but not used. Spelling mistake?\n")
}
// In variable assignments, a plain '#' introduces a line comment, unless
@@ -679,6 +674,24 @@
c.Check(s.Output(), equals, "") // Don’t warn about missing :Q operators.
}
+func (s *Suite) Test_MkLine_variableNeedsQuoting_tool_in_CONFIGURE_ENV(c *check.C) {
+ s.UseCommandLine(c, "-Wall")
+ G.globalData.InitVartypes()
+ G.globalData.Tools = NewToolRegistry()
+ G.globalData.Tools.RegisterVarname("tar", "TAR")
+
+ mklines := s.NewMkLines("Makefile",
+ "# $"+"NetBSD$",
+ "",
+ "CONFIGURE_ENV+=\tSYS_TAR_COMMAND_PATH=${TOOLS_TAR:Q}")
+
+ mklines.mklines[2].checkVarassignVaruse()
+
+ // The TOOLS_* variables only contain the path to the tool,
+ // without any additional arguments that might be necessary.
+ c.Check(s.Output(), equals, "NOTE: Makefile:3: The :Q operator isn't necessary for ${TOOLS_TAR} here.\n")
+}
+
func (s *Suite) Test_MkLine_Varuse_Modifier_L(c *check.C) {
s.UseCommandLine(c, "-Wall")
G.globalData.InitVartypes()
@@ -770,4 +783,29 @@
mklines.Check()
c.Check(s.Output(), equals, "WARN: x11/motif/Makefile:3: Unknown shell command \"${GREP}\".\n") // No parse errors.
+}
+
+// See PR 46570, Ctrl+F "3. In lang/perl5".
+func (s *Suite) Test_MkLine_getVariableType(c *check.C) {
+ mkline := NewMkLine(dummyLine)
+
+ c.Check(mkline.getVariableType("_PERL5_PACKLIST_AWK_STRIP_DESTDIR"), check.IsNil)
+ c.Check(mkline.getVariableType("SOME_DIR").guessed, equals, true)
+ c.Check(mkline.getVariableType("SOMEDIR").guessed, equals, true)
+}
+
+// See PR 46570, Ctrl+F "4. Shell quoting".
+// Pkglint is correct, since this definition for CPPFLAGS should be
+// seen by the shell as three words, not one word.
+func (s *Suite) Test_MkLine_Cflags(c *check.C) {
+ G.globalData.InitVartypes()
+ mklines := s.NewMkLines("Makefile",
+ "# $"+"NetBSD$",
+ "CPPFLAGS.SunOS+=\t-DPIPECOMMAND=\\\"/usr/sbin/sendmail -bs %s\\\"")
+
+ mklines.Check()
+
+ c.Check(s.Output(), equals, ""+
+ "WARN: Makefile:2: Unknown compiler flag \"-bs\".\n"+
+ "WARN: Makefile:2: Compiler flag \"%s\\\\\\\"\" should start with a hyphen.\n")
}
--- pkgsrc/pkgtools/pkglint/files/Attic/mklines_test.go 2016/06/10 19:42:42 1.5
+++ pkgsrc/pkgtools/pkglint/files/Attic/mklines_test.go 2016/07/07 12:09:27 1.6
--- pkgsrc/pkgtools/pkglint/files/Attic/plist.go 2016/06/05 11:24:32 1.5
+++ pkgsrc/pkgtools/pkglint/files/Attic/plist.go 2016/07/07 12:09:27 1.6
--- pkgsrc/pkgtools/pkglint/files/Attic/plist_test.go 2016/01/27 21:55:51 1.5
+++ pkgsrc/pkgtools/pkglint/files/Attic/plist_test.go 2016/07/07 12:09:27 1.6
--- pkgsrc/pkgtools/pkglint/files/Attic/substcontext.go 2016/01/12 01:02:49 1.5
+++ pkgsrc/pkgtools/pkglint/files/Attic/substcontext.go 2016/07/07 12:09:27 1.6
@@ -20,12 +20,12 @@
varname := mkline.Varname()
op := mkline.Op()
value := mkline.Value()
- if varname == "SUBST_CLASSES" {
+ if varname == "SUBST_CLASSES" || hasPrefix(varname, "SUBST_CLASSES.") {
classes := splitOnSpace(value)
if len(classes) > 1 {
mkline.Warn0("Please add only one class at a time to SUBST_CLASSES.")
}
- if ctx.id != "" {
+ if ctx.id != "" && ctx.id != classes[0] {
mkline.Warn0("SUBST_CLASSES should only appear once in a SUBST block.")
}
ctx.id = classes[0]
--- pkgsrc/pkgtools/pkglint/files/Attic/mkparser.go 2016/06/05 11:24:32 1.1
+++ pkgsrc/pkgtools/pkglint/files/Attic/mkparser.go 2016/07/07 12:09:27 1.2
--- pkgsrc/pkgtools/pkglint/files/Attic/mkparser_test.go 2016/06/05 11:24:32 1.1
+++ pkgsrc/pkgtools/pkglint/files/Attic/mkparser_test.go 2016/07/07 12:09:27 1.2
--- pkgsrc/pkgtools/pkglint/files/Attic/mkshparser.go 2016/06/05 11:24:32 1.1
+++ pkgsrc/pkgtools/pkglint/files/Attic/mkshparser.go 2016/07/07 12:09:27 1.2
@@ -5,651 +5,220 @@
"strconv"
)
-type MkShParser struct {
- tok *ShTokenizer
- curr *ShToken
-}
-
-func NewMkShParser(line *Line, text string, emitWarnings bool) *MkShParser {
- shp := NewShTokenizer(line, text, emitWarnings)
- return &MkShParser{shp, nil}
-}
-
-func (p *MkShParser) Program() (retval *MkShList) {
- defer p.trace(&retval)()
-
- list := p.List()
- if list == nil {
- return nil
+func parseShellProgram(line *Line, program string) (list *MkShList, err error) {
+ if G.opts.Debug {
+ defer tracecall(program)()
}
- separator := p.Separator()
- if separator == nil {
- return list
- }
- return &MkShList{list.AndOrs, append(list.Separators, *separator)}
-}
-// ::= AndOr (SeparatorOp AndOr)*
-func (p *MkShParser) List() (retval *MkShList) {
- defer p.trace(&retval)()
- ok := false
- defer p.rollback(&ok)()
+ tokens, rest := splitIntoShellTokens(line, program)
+ lexer := NewShellLexer(tokens, rest)
+ parser := ­yParserImpl{}
- var andors []*MkShAndOr
- var seps []MkShSeparator
+ succeeded := parser.Parse(lexer)
- if andor := p.AndOr(); andor != nil {
- andors = append(andors, andor)
- } else {
- return nil
+ if succeeded == 0 && lexer.error == "" {
+ return lexer.result, nil
}
-
-next:
- mark := p.mark()
- if sep := p.SeparatorOp(); sep != nil {
- if andor := p.AndOr(); andor != nil {
- andors = append(andors, andor)
- seps = append(seps, *sep)
- goto next
- }
- }
- p.reset(mark)
-
- ok = true
- return &MkShList{andors, seps}
+ return nil, &ParseError{append([]string{lexer.current}, lexer.remaining...)}
}
-func (p *MkShParser) AndOr() (retval *MkShAndOr) {
- defer p.trace(&retval)()
- ok := false
- defer p.rollback(&ok)
-
- var pipes []*MkShPipeline
- var ops []string
-nextpipe:
- if pipe := p.Pipeline(); pipe != nil {
- pipes = append(pipes, pipe)
- switch op := p.peekText(); op {
- case "&&", "||":
- p.skip()
- p.Linebreak()
- ops = append(ops, op)
- goto nextpipe
- }
- }
-
- if len(pipes) == len(ops) {
- return nil
- }
- ok = true
- return &MkShAndOr{pipes, ops}
+type ParseError struct {
+ RemainingTokens []string
}
-// ::= Command (msttPipe Linebreak Command)*
-func (p *MkShParser) Pipeline() (retval *MkShPipeline) {
- defer p.trace(&retval)()
- ok := false
- defer p.rollback(&ok)()
-
- bang := p.eat("!")
- var cmds []*MkShCommand
-nextcmd:
- cmd := p.Command()
- if cmd == nil {
- return nil
- }
- cmds = append(cmds, cmd)
- if p.eat("|") {
- p.Linebreak()
- goto nextcmd
- }
- ok = true
- return &MkShPipeline{bang, cmds}
+func (e *ParseError) Error() string {
+ return fmt.Sprintf("parse error at %v", e.RemainingTokens)
}
-func (p *MkShParser) Command() (retval *MkShCommand) {
- defer p.trace(&retval)()
-
- if simple := p.SimpleCommand(); simple != nil {
- return &MkShCommand{Simple: simple}
- }
- if compound := p.CompoundCommand(); compound != nil {
- redirects := p.RedirectList()
- return &MkShCommand{Compound: compound, Redirects: redirects}
- }
- if funcdef := p.FunctionDefinition(); funcdef != nil {
- return &MkShCommand{FuncDef: funcdef}
- }
- return nil
+type ShellLexer struct {
+ current string
+ ioredirect string
+ remaining []string
+ atCommandStart bool
+ sinceFor int
+ sinceCase int
+ error string
+ result *MkShList
}
-func (p *MkShParser) CompoundCommand() (retval *MkShCompoundCommand) {
- defer p.trace(&retval)()
-
- if brace := p.BraceGroup(); brace != nil {
- return &MkShCompoundCommand{Brace: brace}
- }
- if subshell := p.Subshell(); subshell != nil {
- return &MkShCompoundCommand{Subshell: subshell}
- }
- if forclause := p.ForClause(); forclause != nil {
- return &MkShCompoundCommand{For: forclause}
- }
- if caseclause := p.CaseClause(); caseclause != nil {
- return &MkShCompoundCommand{Case: caseclause}
- }
- if ifclause := p.IfClause(); ifclause != nil {
- return &MkShCompoundCommand{If: ifclause}
- }
- if whileclause := p.WhileClause(); whileclause != nil {
- return &MkShCompoundCommand{While: whileclause}
- }
- if untilclause := p.UntilClause(); untilclause != nil {
- return &MkShCompoundCommand{Until: untilclause}
- }
- return nil
+func NewShellLexer(tokens []string, rest string) *ShellLexer {
+ return &ShellLexer{
+ current: "",
+ ioredirect: "",
+ remaining: tokens,
+ atCommandStart: true,
+ error: rest}
}
-
-func (p *MkShParser) Subshell() (retval *MkShList) {
- defer p.trace(&retval)()
- ok := false
- defer p.rollback(&ok)()
-
- if !p.eat("(") {
- return nil
+func (lex *ShellLexer) Lex(lval *shyySymType) (ttype int) {
+ if len(lex.remaining) == 0 {
+ return 0
}
- list := p.CompoundList()
- if list == nil {
- return nil
- }
- if !p.eat(")") {
- return nil
- }
- ok = true
- return list
-}
-// ::= Newline* AndOr (Separator AndOr)* Separator?
-func (p *MkShParser) CompoundList() (retval *MkShList) {
- defer p.trace(&retval)()
- ok := false
- defer p.rollback(&ok)()
-
- p.Linebreak()
- var andors []*MkShAndOr
- var separators []MkShSeparator
-nextandor:
- if andor := p.AndOr(); andor != nil {
- andors = append(andors, andor)
- if sep := p.Separator(); sep != nil {
- separators = append(separators, *sep)
- goto nextandor
- }
+ if G.opts.Debug {
+ defer func() {
+ tname := shyyTokname(shyyTok2[ttype-shyyPrivate])
+ switch ttype {
+ case tkWORD, tkASSIGNMENT_WORD:
+ traceStep("lex %v %q", tname, lval.Word.MkText)
+ case tkIO_NUMBER:
+ traceStep("lex %v %v", tname, lval.IONum)
+ default:
+ traceStep("lex %v", tname)
+ }
+ }()
}
- if len(andors) == 0 {
- return nil
- }
- ok = true
- return &MkShList{andors, separators}
-}
-// ::= "for" msttWORD Linebreak DoGroup
-// ::= "for" msttWORD Linebreak "in" SequentialSep DoGroup
-// ::= "for" msttWORD Linebreak "in" Wordlist SequentialSep DoGroup
-func (p *MkShParser) ForClause() (retval *MkShForClause) {
- defer p.trace(&retval)()
- ok := false
- defer p.rollback(&ok)()
-
- if !p.eat("for") {
- return nil
+ token := lex.ioredirect
+ lex.ioredirect = ""
+ if token == "" {
+ token = lex.remaining[0]
+ lex.current = token
+ lex.remaining = lex.remaining[1:]
}
- varword := p.Word(false)
- if varword == nil || !matches(varword.MkText, `^[A-Z_a-z][0-9A-Za-z]*`) {
- return nil
- }
- varname := varword.MkText
- var values []*ShToken
- if p.eat("in") {
- values = p.Wordlist()
- } else {
- values = []*ShToken{NewShToken("\"$$@\"",
- NewShAtom(shtWord, "\"", shqDquot),
- NewShAtom(shtWord, "$$@", shqDquot),
- NewShAtom(shtWord, "\"", shqPlain))}
+ switch token {
+ case ";":
+ lex.atCommandStart = true
+ return tkSEMI
+ case ";;":
+ lex.atCommandStart = true
+ return tkSEMISEMI
+ case "\n":
+ lex.atCommandStart = true
+ return tkNEWLINE
+ case "&":
+ lex.atCommandStart = true
+ return tkBACKGROUND
+ case "|":
+ lex.atCommandStart = true
+ return tkPIPE
+ case "(":
+ lex.atCommandStart = true
+ return tkLPAREN
+ case ")":
+ lex.atCommandStart = true
+ return tkRPAREN
+ case "&&":
+ lex.atCommandStart = true
+ return tkAND
+ case "||":
+ lex.atCommandStart = true
+ return tkOR
+ case ">":
+ lex.atCommandStart = false
+ return tkGT
+ case ">&":
+ lex.atCommandStart = false
+ return tkGTAND
+ case "<":
+ lex.atCommandStart = false
+ return tkLT
+ case "<&":
+ lex.atCommandStart = false
+ return tkLTAND
+ case "<>":
+ lex.atCommandStart = false
+ return tkLTGT
+ case ">>":
+ lex.atCommandStart = false
+ return tkGTGT
+ case "<<":
+ lex.atCommandStart = false
+ return tkLTLT
+ case "<<-":
+ lex.atCommandStart = false
+ return tkLTLTDASH
+ case ">|":
+ lex.atCommandStart = false
+ return tkGTPIPE
}
- if values == nil || !p.SequentialSep() {
- return nil
- }
- p.Linebreak()
- body := p.DoGroup()
- if body == nil {
- return nil
+ if m, fdstr, op := match2(token, `^(\d+)(<<-|<<|<>|<&|>>|>&|>\||<|>)$`); m {
+ fd, _ := strconv.Atoi(fdstr)
+ lval.IONum = fd
+ lex.ioredirect = op
+ return tkIO_NUMBER
}
- ok = true
- return &MkShForClause{varname, values, body}
-}
-
-func (p *MkShParser) Wordlist() (retval []*ShToken) {
- defer p.trace(&retval)()
-
- var words []*ShToken
-nextword:
- word := p.Word(false)
- if word != nil {
- words = append(words, word)
- goto nextword
- }
- return words
-}
-
-// ::= "case" msttWORD Linebreak "in" Linebreak CaseItem* "esac"
-func (p *MkShParser) CaseClause() (retval *MkShCaseClause) {
- defer p.trace(&retval)()
- ok := false
- defer p.rollback(&ok)()
-
- if !p.eat("case") {
- return nil
- }
-
- panic("CaseClause")
- p.Linebreak()
- p.CaseItem()
- return nil
-}
-
-// ::= "("? Pattern ")" (CompoundList | Linebreak) msttDSEMI? Linebreak
-func (p *MkShParser) CaseItem() (retval *MkShCaseItem) {
- defer p.trace(&retval)()
-
- panic("CaseItem")
- p.Pattern()
- p.Linebreak()
- p.CompoundList()
- return nil
-}
-
-// ::= msttWORD
-// ::= Pattern "|" msttWORD
-func (p *MkShParser) Pattern() (retval []*ShToken) {
- defer p.trace(&retval)()
- ok := false
- defer p.rollback(&ok)()
-
- var words []*ShToken
-nextword:
- word := p.Word(false)
- if word == nil {
- return nil
- }
- words = append(words, word)
- if p.eat("|") {
- goto nextword
-
- }
- ok = true
- return words
-}
-
-func (p *MkShParser) IfClause() (retval *MkShIfClause) {
- defer p.trace(&retval)()
- ok := false
- defer p.rollback(&ok)()
-
- var conds []*MkShList
- var actions []*MkShList
- var elseaction *MkShList
- if !p.eat("if") {
- return nil
- }
-
-nextcond:
- cond := p.CompoundList()
- if cond == nil || !p.eat("then") {
- return nil
- }
- action := p.CompoundList()
- if action == nil {
- return nil
- }
- conds = append(conds, cond)
- actions = append(actions, action)
- if p.eat("elif") {
- goto nextcond
- }
- if p.eat("else") {
- elseaction = p.CompoundList()
- if elseaction == nil {
- return nil
+ if lex.atCommandStart {
+ lex.sinceCase = -1
+ lex.sinceFor = -1
+ switch token {
+ case "if":
+ return tkIF
+ case "then":
+ return tkTHEN
+ case "elif":
+ return tkELIF
+ case "else":
+ return tkELSE
+ case "fi":
+ return tkFI
+ case "for":
+ lex.atCommandStart = false
+ lex.sinceFor = 0
+ return tkFOR
+ case "while":
+ return tkWHILE
+ case "until":
+ return tkUNTIL
+ case "do":
+ return tkDO
+ case "done":
+ return tkDONE
+ case "in":
+ lex.atCommandStart = false
+ return tkIN
+ case "case":
+ lex.atCommandStart = false
+ lex.sinceCase = 0
+ return tkCASE
+ case "{":
+ return tkLBRACE
+ case "}":
+ return tkRBRACE
+ case "!":
+ return tkEXCLAM
}
}
- if !p.eat("fi") {
- return nil
- }
- ok = true
- return &MkShIfClause{conds, actions, elseaction}
-}
-// ::= "while" CompoundList DoGroup
-func (p *MkShParser) WhileClause() (retval *MkShLoopClause) {
- defer p.trace(&retval)()
- ok := false
- defer p.rollback(&ok)()
-
- if !p.eat("while") {
- return nil
+ if lex.sinceFor >= 0 {
+ lex.sinceFor++
}
-
- panic("WhileClause")
- p.CompoundList()
- p.DoGroup()
- return nil
-}
-
-// ::= "until" CompoundList DoGroup
-func (p *MkShParser) UntilClause() (retval *MkShLoopClause) {
- defer p.trace(&retval)()
- ok := false
- defer p.rollback(&ok)()
-
- if !p.eat("until") {
- return nil
+ if lex.sinceCase >= 0 {
+ lex.sinceCase++
}
- panic("UntilClause")
- p.CompoundList()
- p.DoGroup()
- return nil
-}
-
-// ::= msttNAME "(" ")" Linebreak CompoundCommand Redirect*
-func (p *MkShParser) FunctionDefinition() (retval *MkShFunctionDefinition) {
- defer p.trace(&retval)()
- ok := false
- defer p.rollback(&ok)()
-
- funcname := p.Word(true)
- if funcname == nil || !matches(funcname.MkText, `^[A-Z_a-z][0-9A-Z_a-z]*$`) {
- return nil
+ switch {
+ case matches(token, `^\d+$`) && len(lex.remaining) != 0 && matches(lex.remaining[0], `^[<>]`):
+ ttype = tkIO_NUMBER
+ lval.IONum, _ = strconv.Atoi(token)
+ case lex.sinceFor == 2 && token == "in":
+ ttype = tkIN
+ lex.atCommandStart = false
+ case lex.sinceFor == 2 && token == "do":
+ ttype = tkDO
+ lex.atCommandStart = true
+ case lex.sinceCase == 2 && token == "in":
+ ttype = tkIN
+ lex.atCommandStart = false
+ case (lex.atCommandStart || lex.sinceCase == 3) && token == "esac":
+ ttype = tkESAC
+ lex.atCommandStart = false
+ case lex.atCommandStart && matches(token, `^[A-Za-z_]\w*=`):
+ ttype = tkASSIGNMENT_WORD
+ p := NewShTokenizer(dummyLine, token, false)
+ lval.Word = p.ShToken()
+ default:
+ ttype = tkWORD
+ p := NewShTokenizer(dummyLine, token, false)
+ lval.Word = p.ShToken()
+ lex.atCommandStart = false
}
- if !p.eat("(") || !p.eat(")") {
- return nil
- }
-
- p.Linebreak()
-
- body := p.CompoundCommand()
- if body == nil {
- return nil
- }
-
- redirects := p.RedirectList()
- ok = true
- return &MkShFunctionDefinition{funcname.MkText, body, redirects}
+ return ttype
}
-func (p *MkShParser) BraceGroup() (retval *MkShList) {
- defer p.trace(&retval)()
- ok := false
- defer p.rollback(&ok)()
-
- if !p.eat("{") {
- return nil
- }
- list := p.CompoundList()
- if list == nil {
- return nil
- }
- if !p.eat("}") {
- return nil
- }
- ok = true
- return list
-}
-
-func (p *MkShParser) DoGroup() (retval *MkShList) {
- defer p.trace(&retval)()
- ok := false
- defer p.rollback(&ok)()
-
- if !p.eat("do") {
- return nil
- }
- list := p.CompoundList()
- if list == nil {
- return nil
- }
- if !p.eat("done") {
- return nil
- }
- ok = true
- return list
-}
-
-func (p *MkShParser) SimpleCommand() (retval *MkShSimpleCommand) {
- defer p.trace(&retval)()
- ok := false
- defer p.rollback(&ok)()
-
- var assignments []*ShToken
- var name *ShToken
- var args []*ShToken
- var redirections []*MkShRedirection
- first := true
- seenName := false
-nextword:
- if word := p.Word(first); word != nil {
- first = false
- if !seenName && word.IsAssignment() {
- assignments = append(assignments, word)
- } else if !seenName {
- name = word
- seenName = true
- } else {
- args = append(args, word)
- }
- goto nextword
- }
- if len(assignments) == 0 && name == nil && len(args) == 0 && len(redirections) == 0 {
- return nil
- }
- ok = true
- return &MkShSimpleCommand{assignments, name, args, redirections}
-}
-
-func (p *MkShParser) RedirectList() (retval []*MkShRedirection) {
- defer p.trace(&retval)()
-
-nextredirect:
- if redirect := p.IoRedirect(); redirect != nil {
- retval = append(retval, redirect)
- goto nextredirect
- }
- return nil
-}
-
-func (p *MkShParser) IoRedirect() (retval *MkShRedirection) {
- defer p.trace(&retval)()
-
- if m, redirect, fdstr, op := match3(p.peekText(), `^((\d*)\s*(<|<&|>|>&|>>|<>|>\||<<|<<-))`); m {
- target := p.peekText()[len(redirect):]
- _, _, _ = fdstr, op, target
-
- fd, err := strconv.ParseInt(fdstr, 10, 32)
- if err != nil {
- fd = -1
- }
- p.skip()
- targetToken := NewShTokenizer(p.tok.mkp.Line, target, false).ShToken()
- return &MkShRedirection{int(fd), op, targetToken}
- }
- return nil
-}
-
-func (p *MkShParser) NewlineList() (retval bool) {
- defer p.trace(&retval)()
-
- ok := false
- for p.eat("\n") {
- ok = true
- }
- return ok
-}
-
-func (p *MkShParser) Linebreak() {
- for p.eat("\n") {
- }
-}
-
-func (p *MkShParser) SeparatorOp() (retval *MkShSeparator) {
- defer p.trace(&retval)()
-
- if p.eat(";") {
- op := MkShSeparator(";")
- return &op
- }
- if p.eat("&") {
- op := MkShSeparator("&")
- return &op
- }
- return nil
-}
-
-func (p *MkShParser) Separator() (retval *MkShSeparator) {
- defer p.trace(&retval)()
-
- op := p.SeparatorOp()
- if op == nil && p.eat("\n") {
- sep := MkShSeparator('\n')
- op = &sep
- }
- if op != nil {
- p.Linebreak()
- }
- return op
-}
-
-func (p *MkShParser) SequentialSep() (retval bool) {
- defer p.trace(&retval)()
-
- if p.peekText() == ";" {
- p.skip()
- p.Linebreak()
- return true
- } else {
- return p.NewlineList()
- }
-}
-
-func (p *MkShParser) Word(cmdstart bool) (retval *ShToken) {
- defer p.trace(&retval)()
-
- if token := p.peek(); token != nil && token.IsWord() {
- if cmdstart {
- switch token.MkText {
- case "while", "until", "for", "do", "done",
- "if", "then", "else", "elif", "fi",
- "{", "}":
- return nil
- }
- }
- p.skip()
- return token
- }
- return nil
-}
-
-func (p *MkShParser) EOF() bool {
- return p.peek() == nil
-}
-
-func (p *MkShParser) peek() *ShToken {
- if p.curr == nil {
- nexttoken:
- p.curr = p.tok.ShToken()
- if p.curr == nil && !p.tok.parser.EOF() {
- p.tok.mkp.Line.Warnf("Pkglint tokenize error at " + p.tok.parser.Rest())
- p.tok.mkp.Parser.repl.AdvanceRest()
- return nil
- }
- if p.curr != nil && hasPrefix(p.curr.MkText, "#") {
- goto nexttoken
- }
- }
- //traceStep("MkShParser.peek %v rest=%q", p.curr, p.tok.mkp.repl.rest)
- return p.curr
-}
-
-func (p *MkShParser) peekText() string {
- if next := p.peek(); next != nil {
- return next.MkText
- }
- return ""
-}
-
-func (p *MkShParser) skip() {
- p.curr = nil
-}
-
-func (p *MkShParser) eat(s string) bool {
- if p.peek() == nil {
- return false
- }
- if p.peek().MkText == s {
- p.skip()
- return true
- }
- return false
-}
-
-func (p *MkShParser) rollback(pok *bool) func() {
- mark := p.mark()
- return func() {
- if !*pok {
- p.reset(mark)
- }
- }
-}
-
-func (p *MkShParser) trace(retval interface{}) func() {
- if G.opts.Debug {
- return tracecallInternal(p.peek(), p.restref(), "=>", ref(retval))
- } else {
- return func() {}
- }
-}
-
-func (p *MkShParser) mark() MkShParserMark {
- return MkShParserMark{p.tok.parser.repl.Mark(), p.curr}
-}
-
-func (p *MkShParser) reset(mark MkShParserMark) {
- p.tok.parser.repl.Reset(mark.rest)
- p.curr = mark.curr
-}
-
-func (p *MkShParser) restref() MkShParserRest {
- return MkShParserRest{&p.tok.mkp.repl.rest}
-}
-
-func (p *MkShParser) Rest() string {
- return p.peekText() + p.tok.mkp.repl.AdvanceRest()
-}
-
-type MkShParserMark struct {
- rest PrefixReplacerMark
- curr *ShToken
-}
-
-type MkShParserRest struct {
- restref *string
-}
-
-func (rest MkShParserRest) String() string {
- return fmt.Sprintf("rest=%q", *rest.restref)
+func (lex *ShellLexer) Error(s string) {
+ lex.error = s
}
--- pkgsrc/pkgtools/pkglint/files/Attic/mkshparser_test.go 2016/06/05 11:24:32 1.1
+++ pkgsrc/pkgtools/pkglint/files/Attic/mkshparser_test.go 2016/07/07 12:09:27 1.2
@@ -1,352 +1,595 @@
package main
import (
- check "gopkg.in/check.v1"
+ "encoding/json"
+ "gopkg.in/check.v1"
+ "strconv"
)
-func (s *Suite) Test_MkShParser_Program(c *check.C) {
- parse := func(cmd string, expected *MkShList) {
- p := NewMkShParser(dummyLine, cmd, false)
- program := p.Program()
- c.Check(program, deepEquals, expected)
- c.Check(p.tok.parser.Rest(), equals, "")
- c.Check(s.Output(), equals, "")
- }
-
- if false {
- parse(""+
- "\tcd ${WRKSRC} && ${FIND} ${${_list_}} -type f ! -name '*.orig' 2>/dev/null "+
- "| pax -rw -pm ${DESTDIR}${PREFIX}/${${_dir_}}",
- NewMkShList())
- }
+type ShSuite struct {
+ c *check.C
}
-func (s *Suite) Test_MkShParser_List(c *check.C) {
+var _ = check.Suite(&ShSuite{})
-}
+func (s *ShSuite) Test_ShellParser_program(c *check.C) {
+ b := s.init(c)
-func (s *Suite) Test_MkShParser_AndOr(c *check.C) {
- parse := func(cmd string, expected *MkShAndOr) {
- p := NewMkShParser(dummyLine, cmd, false)
- andor := p.AndOr()
- c.Check(andor, deepEquals, expected)
- c.Check(p.tok.parser.Rest(), equals, "")
- c.Check(s.Output(), equals, "")
- }
- tester := &MkShTester{c}
+ s.test("",
+ b.List())
- parse("simplecmd",
- NewMkShAndOr(NewMkShPipeline(false, tester.ParseCommand("simplecmd"))))
+ s.test("echo ;",
+ b.List().AddCommand(b.SimpleCommand("echo")).AddSeparator(";"))
- expected := NewMkShAndOr(NewMkShPipeline(false, tester.ParseCommand("simplecmd1")))
- expected.Add("&&", NewMkShPipeline(false, tester.ParseCommand("simplecmd2")))
- parse("simplecmd1 && simplecmd2", expected)
+ s.test("echo",
+ b.List().AddCommand(b.SimpleCommand("echo")))
+
+ s.test(""+
+ "cd ${WRKSRC} && ${FIND} ${${_list_}} -type f ! -name '*.orig' 2 > /dev/null "+
+ "| pax -rw -pm ${DESTDIR}${PREFIX}/${${_dir_}}",
+ b.List().AddAndOr(b.AndOr(
+ b.Pipeline(false, b.SimpleCommand("cd", "${WRKSRC}"))).Add("&&",
+ b.Pipeline(false,
+ b.SimpleCommand("${FIND}", "${${_list_}}", "-type", "f", "!", "-name", "'*.orig'", "2>/dev/null"),
+ b.SimpleCommand("pax", "-rw", "-pm", "${DESTDIR}${PREFIX}/${${_dir_}}")))))
+
+ s.test("${CAT} ${PKGDIR}/PLIST | while read entry ; do : ; done",
+ b.List().AddAndOr(b.AndOr(b.Pipeline(false,
+ b.SimpleCommand("${CAT}", "${PKGDIR}/PLIST"),
+ b.While(
+ b.List().AddCommand(b.SimpleCommand("read", "entry")).AddSeparator(";"),
+ b.List().AddCommand(b.SimpleCommand(":")).AddSeparator(";"))))))
+
+ s.test("while read entry ; do case \"$$entry\" in include/c-client/* ) ${INSTALL_DATA} $$src $$dest ; esac ; done",
+ b.List().AddCommand(b.While(
+ b.List().AddCommand(b.SimpleCommand("read", "entry")).AddSeparator(";"),
+ b.List().AddCommand(b.Case(
+ b.Token("\"$$entry\""),
+ b.CaseItem(
+ b.Words("include/c-client/*"),
+ b.List().AddCommand(b.SimpleCommand("${INSTALL_DATA}", "$$src", "$$dest")),
+ &SEP_SEMI))).AddSeparator(";"))))
+
+ s.test("command | while condition ; do case selector in pattern ) : ; esac ; done",
+ b.List().AddAndOr(b.AndOr(b.Pipeline(false,
+ b.SimpleCommand("command"),
+ b.While(
+ b.List().AddCommand(b.SimpleCommand("condition")).AddSeparator(";"),
+ b.List().AddCommand(b.Case(
+ b.Token("selector"),
+ b.CaseItem(
+ b.Words("pattern"),
+ b.List().AddCommand(b.SimpleCommand(":")),
+ &SEP_SEMI))).AddSeparator(";"))))))
+
+ s.test("command1 \n command2 \n command3",
+ b.List().
+ AddCommand(b.SimpleCommand("command1")).
+ AddSeparator(SEP_NEWLINE).
+ AddCommand(b.SimpleCommand("command2")).
+ AddSeparator(SEP_NEWLINE).
+ AddCommand(b.SimpleCommand("command3")))
+
+ s.test("if condition; then action; else case selector in pattern) case-item-action ;; esac; fi",
+ b.List().AddCommand(b.If(
+ b.List().AddCommand(b.SimpleCommand("condition")).AddSeparator(";"),
+ b.List().AddCommand(b.SimpleCommand("action")).AddSeparator(";"),
+ b.List().AddCommand(b.Case(
+ b.Token("selector"),
+ b.CaseItem(
+ b.Words("pattern"),
+ b.List().AddCommand(b.SimpleCommand("case-item-action")), nil))).AddSeparator(";"))))
}
-func (s *Suite) Test_MkShParser_Pipeline(c *check.C) {
+func (s *ShSuite) Test_ShellParser_list(c *check.C) {
+ b := s.init(c)
+ s.test("echo1 && echo2",
+ b.List().AddAndOr(
+ b.AndOr(b.Pipeline(false, b.SimpleCommand("echo1"))).
+ Add("&&", b.Pipeline(false, b.SimpleCommand("echo2")))))
+
+ s.test("echo1 ; echo2",
+ b.List().
+ AddCommand(b.SimpleCommand("echo1")).
+ AddSeparator(";").
+ AddCommand(b.SimpleCommand("echo2")))
+
+ s.test("echo1 ; echo2 &",
+ b.List().
+ AddCommand(b.SimpleCommand("echo1")).
+ AddSeparator(";").
+ AddCommand(b.SimpleCommand("echo2")).
+ AddSeparator("&"))
}
-func (s *Suite) Test_MkShParser_Command(c *check.C) {
- parse := func(cmd string, expected *MkShCommand) {
- p := NewMkShParser(dummyLine, cmd, false)
- command := p.Command()
- c.Check(command, deepEquals, expected)
- c.Check(p.tok.parser.Rest(), equals, "")
- c.Check(s.Output(), equals, "")
- }
- tester := &MkShTester{c}
+func (s *ShSuite) Test_ShellParser_and_or(c *check.C) {
+ b := s.init(c)
- parse("simple",
- &MkShCommand{Simple: tester.ParseSimpleCommand("simple")})
+ s.test("echo1 | echo2",
+ b.List().AddAndOr(b.AndOr(b.Pipeline(false,
+ b.SimpleCommand("echo1"),
+ b.SimpleCommand("echo2")))))
+
+ s.test("echo1 | echo2 && echo3 | echo4",
+ b.List().AddAndOr(b.AndOr(
+ b.Pipeline(false,
+ b.SimpleCommand("echo1"),
+ b.SimpleCommand("echo2")),
+ ).Add("&&",
+ b.Pipeline(false,
+ b.SimpleCommand("echo3"),
+ b.SimpleCommand("echo4")))))
+
+ s.test("echo1 | echo2 || ! echo3 | echo4",
+ b.List().AddAndOr(b.AndOr(
+ b.Pipeline(false,
+ b.SimpleCommand("echo1"),
+ b.SimpleCommand("echo2")),
+ ).Add("||",
+ b.Pipeline(true,
+ b.SimpleCommand("echo3"),
+ b.SimpleCommand("echo4")))))
}
-func (s *Suite) Test_MkShParser_CompoundCommand(c *check.C) {
+func (s *ShSuite) Test_ShellParser_pipeline(c *check.C) {
+ b := s.init(c)
+ s.test("command1 | command2",
+ b.List().AddAndOr(b.AndOr(b.Pipeline(false,
+ b.SimpleCommand("command1"),
+ b.SimpleCommand("command2")))))
+
+ s.test("! command1 | command2",
+ b.List().AddAndOr(b.AndOr(b.Pipeline(true,
+ b.SimpleCommand("command1"),
+ b.SimpleCommand("command2")))))
}
-func (s *Suite) Test_MkShParser_Subshell(c *check.C) {
+func (s *ShSuite) Test_ShellParser_pipe_sequence(c *check.C) {
+ b := s.init(c)
+ s.test("command1 | if true ; then : ; fi",
+ b.List().AddAndOr(b.AndOr(b.Pipeline(false,
+ b.SimpleCommand("command1"),
+ b.If(
+ b.List().AddCommand(b.SimpleCommand("true")).AddSeparator(";"),
+ b.List().AddCommand(b.SimpleCommand(":")).AddSeparator(";"))))))
}
-func (s *Suite) Test_MkShParser_CompoundList(c *check.C) {
- parse := func(cmd string, expected *MkShList) {
- p := NewMkShParser(dummyLine, cmd, false)
- compoundList := p.CompoundList()
- c.Check(compoundList, deepEquals, expected)
- c.Check(p.tok.parser.Rest(), equals, "")
- c.Check(s.Output(), equals, "")
- }
- tester := &MkShTester{c}
+func (s *ShSuite) Test_ShellParser_command(c *check.C) {
+ b := s.init(c)
- parse("simplecmd",
- NewMkShList().AddAndOr(NewMkShAndOr(NewMkShPipeline(false, tester.ParseCommand("simplecmd")))))
+ s.test("simple_command",
+ b.List().AddAndOr(b.AndOr(b.Pipeline(false, b.SimpleCommand("simple_command")))))
- simplecmd1 := NewMkShPipeline(false, tester.ParseCommand("simplecmd1"))
- simplecmd2 := NewMkShPipeline(false, tester.ParseCommand("simplecmd2"))
- expected := NewMkShList().AddAndOr(NewMkShAndOr(simplecmd1).Add("&&", simplecmd2))
- parse("simplecmd1 && simplecmd2", expected)
+ s.test("while 1 ; do 2 ; done",
+ b.List().AddAndOr(b.AndOr(b.Pipeline(false,
+ b.While(
+ b.List().AddCommand(b.SimpleCommand("1")).AddSeparator(";"),
+ b.List().AddCommand(b.SimpleCommand("2")).AddSeparator(";"))))))
+
+ s.test("while 1 ; do 2 ; done 1 >& 2",
+ b.List().AddAndOr(b.AndOr(b.Pipeline(false,
+ b.While(
+ b.List().AddCommand(b.SimpleCommand("1")).AddSeparator(";"),
+ b.List().AddCommand(b.SimpleCommand("2")).AddSeparator(";"),
+ b.Redirection(1, ">&", "2"))))))
+
+ s.test("func ( ) { echo hello ; } 2 >& 1",
+ b.List().AddCommand(b.Function(
+ "func",
+ b.Brace(b.List().AddCommand(b.SimpleCommand("echo", "hello")).AddSeparator(";")).Compound,
+ b.Redirection(2, ">&", "1"))))
}
-func (s *Suite) Test_MkShParser_ForClause(c *check.C) {
- parse := func(cmd string, expected *MkShForClause) {
- p := NewMkShParser(dummyLine, cmd, false)
- forclause := p.ForClause()
- c.Check(forclause, deepEquals, expected)
- c.Check(p.tok.parser.Rest(), equals, "")
- c.Check(s.Output(), equals, "")
- }
- tester := &MkShTester{c}
+func (s *ShSuite) Test_ShellParser_compound_command(c *check.C) {
+ b := s.init(c)
- params := []*ShToken{tester.Token("\"$$@\"")}
- action := tester.ParseCompoundList("action;")
- parse("for var; do action; done",
- &MkShForClause{"var", params, action})
+ s.test("{ brace ; }",
+ b.List().AddCommand(b.Brace(
+ b.List().AddCommand(b.SimpleCommand("brace")).AddSeparator(";"))))
- abc := []*ShToken{tester.Token("a"), tester.Token("b"), tester.Token("c")}
- parse("for var in a b c; do action; done",
- &MkShForClause{"var", abc, action})
+ s.test("( subshell )",
+ b.List().AddCommand(b.Subshell(
+ b.List().AddCommand(b.SimpleCommand("subshell")))))
- actions := tester.ParseCompoundList("action1 && action2;")
- parse("for var in a b c; do action1 && action2; done",
- &MkShForClause{"var", abc, actions})
-}
+ s.test("for i in * ; do echo $i ; done",
+ b.List().AddCommand(b.For(
+ "i",
+ b.Words("*"),
+ b.List().AddCommand(b.SimpleCommand("echo", "$i")).AddSeparator(";"))))
-func (s *Suite) Test_MkShParser_Wordlist(c *check.C) {
+ s.test("case $i in esac",
+ b.List().AddCommand(b.Case(
+ b.Token("$i"))))
}
-func (s *Suite) Test_MkShParser_CaseClause(c *check.C) {
+func (s *ShSuite) Test_ShellParser_subshell(c *check.C) {
+ b := s.init(c)
+ sub3 := b.Subshell(b.List().AddCommand(b.SimpleCommand("sub3")))
+ sub2 := b.Subshell(b.List().AddCommand(sub3).AddSeparator(";").AddCommand(b.SimpleCommand("sub2")))
+ sub1 := b.Subshell(b.List().AddCommand(sub2).AddSeparator(";").AddCommand(b.SimpleCommand("sub1")))
+ s.test("( ( ( sub3 ) ; sub2 ) ; sub1 )", b.List().AddCommand(sub1))
}
-func (s *Suite) Test_MkShParser_CaseItem(c *check.C) {
+func (s *ShSuite) Test_ShellParser_compound_list(c *check.C) {
+ b := s.init(c)
+ s.test("( \n echo )",
+ b.List().AddCommand(b.Subshell(
+ b.List().AddCommand(b.SimpleCommand("echo")))))
}
-func (s *Suite) Test_MkShParser_Pattern(c *check.C) {
+func (s *ShSuite) Test_ShellParser_term(c *check.C) {
+ b := s.init(c)
+ _ = b
}
-func (s *Suite) Test_MkShParser_IfClause(c *check.C) {
+func (s *ShSuite) Test_ShellParser_for_clause(c *check.C) {
+ b := s.init(c)
+ s.test("for var do echo $var ; done",
+ b.List().AddCommand(b.For(
+ "var",
+ b.Words("\"$$@\""),
+ b.List().AddCommand(b.SimpleCommand("echo", "$var")).AddSeparator(";"))))
+
+ // Only linebreak is allowed, but not semicolon.
+ s.test("for var \n do echo $var ; done",
+ b.List().AddCommand(b.For(
+ "var",
+ b.Words("\"$$@\""),
+ b.List().AddCommand(b.SimpleCommand("echo", "$var")).AddSeparator(";"))))
+
+ s.test("for var in a b c ; do echo $var ; done",
+ b.List().AddCommand(b.For(
+ "var",
+ b.Words("a", "b", "c"),
+ b.List().AddCommand(b.SimpleCommand("echo", "$var")).AddSeparator(";"))))
+
+ s.test("for var \n \n \n in a b c ; do echo $var ; done",
+ b.List().AddCommand(b.For(
+ "var",
+ b.Words("a", "b", "c"),
+ b.List().AddCommand(b.SimpleCommand("echo", "$var")).AddSeparator(";"))))
+
+ s.test("for var in in esac ; do echo $var ; done",
+ b.List().AddCommand(b.For(
+ "var",
+ b.Words("in", "esac"),
+ b.List().AddCommand(b.SimpleCommand("echo", "$var")).AddSeparator(";"))))
+
+ // No semicolon necessary between the two “done”.
+ s.test("for i in 1; do for j in 1; do echo $$i$$j; done done",
+ b.List().AddCommand(b.For(
+ "i",
+ b.Words("1"),
+ b.List().AddCommand(b.For(
+ "j",
+ b.Words("1"),
+ b.List().AddCommand(b.SimpleCommand("echo", "$$i$$j")).AddSeparator(";"))))))
}
-func (s *Suite) Test_MkShParser_WhileClause(c *check.C) {
+func (s *ShSuite) Test_ShellParser_case_clause(c *check.C) {
+ b := s.init(c)
+ s.test("case $var in esac",
+ b.List().AddCommand(b.Case(b.Token("$var"))))
+
+ s.test("case selector in pattern) ;; pattern) esac",
+ b.List().AddCommand(b.Case(
+ b.Token("selector"),
+ b.CaseItem(b.Words("pattern"), b.List(), nil),
+ b.CaseItem(b.Words("pattern"), b.List(), nil))))
+
+ s.test("case $$i in *.c | *.h ) echo C ;; * ) echo Other ; esac",
+ b.List().AddCommand(b.Case(
+ b.Token("$$i"),
+ b.CaseItem(b.Words("*.c", "*.h"), b.List().AddCommand(b.SimpleCommand("echo", "C")), nil),
+ b.CaseItem(b.Words("*"), b.List().AddCommand(b.SimpleCommand("echo", "Other")), &SEP_SEMI))))
+
+ s.test("case $$i in *.c ) echo ; esac",
+ b.List().AddCommand(b.Case(
+ b.Token("$$i"),
+ b.CaseItem(b.Words("*.c"), b.List().AddCommand(b.SimpleCommand("echo")), &SEP_SEMI))))
+
+ s.test("case selector in pattern) case-item-action ; esac",
+ b.List().AddCommand(b.Case(
+ b.Token("selector"),
+ b.CaseItem(
+ b.Words("pattern"),
+ b.List().AddCommand(b.SimpleCommand("case-item-action")), &SEP_SEMI))))
+
+ s.test("case selector in pattern) case-item-action ;; esac",
+ b.List().AddCommand(b.Case(
+ b.Token("selector"),
+ b.CaseItem(
+ b.Words("pattern"),
+ b.List().AddCommand(b.SimpleCommand("case-item-action")), nil))))
+
}
-func (s *Suite) Test_MkShParser_UntilClause(c *check.C) {
+func (s *ShSuite) Test_ShellParser_if_clause(c *check.C) {
+ b := s.init(c)
+ s.test(
+ "if true ; then echo yes ; else echo no ; fi",
+ b.List().AddCommand(b.If(
+ b.List().AddCommand(b.SimpleCommand("true")).AddSeparator(";"),
+ b.List().AddCommand(b.SimpleCommand("echo", "yes")).AddSeparator(";"),
+ b.List().AddCommand(b.SimpleCommand("echo", "no")).AddSeparator(";"))))
+
+ // No semicolon necessary between the two “fi”.
+ s.test("if cond1; then if cond2; then action; fi fi",
+ b.List().AddCommand(b.If(
+ b.List().AddCommand(b.SimpleCommand("cond1")).AddSeparator(";"),
+ b.List().AddCommand(b.If(
+ b.List().AddCommand(b.SimpleCommand("cond2")).AddSeparator(";"),
+ b.List().AddCommand(b.SimpleCommand("action")).AddSeparator(";"))))))
}
-func (s *Suite) Test_MkShParser_FunctionDefinition(c *check.C) {
+func (s *ShSuite) Test_ShellParser_while_clause(c *check.C) {
+ b := s.init(c)
+ s.test("while condition ; do action ; done",
+ b.List().AddCommand(b.While(
+ b.List().AddCommand(b.SimpleCommand("condition")).AddSeparator(";"),
+ b.List().AddCommand(b.SimpleCommand("action")).AddSeparator(";"))))
}
-func (s *Suite) Test_MkShParser_BraceGroup(c *check.C) {
+func (s *ShSuite) Test_ShellParser_until_clause(c *check.C) {
+ b := s.init(c)
+ s.test("until condition ; do action ; done",
+ b.List().AddCommand(b.Until(
+ b.List().AddCommand(b.SimpleCommand("condition")).AddSeparator(";"),
+ b.List().AddCommand(b.SimpleCommand("action")).AddSeparator(";"))))
}
-func (s *Suite) Test_MkShParser_DoGroup(c *check.C) {
- tester := &MkShTester{c}
- check := func(str string, expected *MkShList) {
- p := NewMkShParser(dummyLine, str, false)
- dogroup := p.DoGroup()
- if c.Check(dogroup, check.NotNil) {
- if !c.Check(dogroup, deepEquals, expected) {
- for i, andor := range dogroup.AndOrs {
- c.Check(andor, deepEquals, expected.AndOrs[i])
- }
- }
- }
- c.Check(p.tok.parser.Rest(), equals, "")
- c.Check(s.Output(), equals, "")
- }
+func (s *ShSuite) Test_ShellParser_function_definition(c *check.C) {
+ b := s.init(c)
- andor := NewMkShAndOr(NewMkShPipeline(false, tester.ParseCommand("action")))
- check("do action; done",
- &MkShList{[]*MkShAndOr{andor}, []MkShSeparator{";"}})
+ _ = b
}
-func (s *Suite) Test_MkShParser_SimpleCommand(c *check.C) {
- parse := func(cmd string, builder *SimpleCommandBuilder) {
- expected := builder.Cmd
- p := NewMkShParser(dummyLine, cmd, false)
- shcmd := p.SimpleCommand()
- if c.Check(shcmd, check.NotNil) {
- if !c.Check(shcmd, deepEquals, expected) {
- for i, assignment := range shcmd.Assignments {
- c.Check(assignment, deepEquals, expected.Assignments[i])
- }
- c.Check(shcmd.Name, deepEquals, expected.Name)
- for i, word := range shcmd.Args {
- c.Check(word, deepEquals, expected.Args[i])
- }
- for i, redirection := range shcmd.Redirections {
- c.Check(redirection, deepEquals, expected.Redirections[i])
- }
- }
- }
- c.Check(p.tok.parser.Rest(), equals, "")
- c.Check(s.Output(), equals, "")
- }
+func (s *ShSuite) Test_ShellParser_brace_group(c *check.C) {
+ b := s.init(c)
- fail := func(noncmd string, expectedRest string) {
- p := NewMkShParser(dummyLine, noncmd, false)
- shcmd := p.SimpleCommand()
- c.Check(shcmd, check.IsNil)
- c.Check(p.tok.parser.Rest(), equals, expectedRest)
- c.Check(s.Output(), equals, "")
- }
- tester := &MkShTester{c}
+ // No semicolon necessary after the closing brace.
+ s.test("if true; then { echo yes; } fi",
+ b.List().AddCommand(b.If(
+ b.List().AddCommand(b.SimpleCommand("true")).AddSeparator(";"),
+ b.List().AddCommand(b.Brace(
+ b.List().AddCommand(b.SimpleCommand("echo", "yes")).AddSeparator(";"))))))
+}
- parse("echo ${PKGNAME:Q}",
- NewSimpleCommandBuilder().
- Name(tester.Token("echo")).
- Arg(tester.Token("${PKGNAME:Q}")))
+func (s *ShSuite) Test_ShellParser_simple_command(c *check.C) {
+ b := s.init(c)
- parse("${ECHO} \"Double-quoted\" 'Single-quoted'",
- NewSimpleCommandBuilder().
- Name(tester.Token("${ECHO}")).
- Arg(tester.Token("\"Double-quoted\"")).
- Arg(tester.Token("'Single-quoted'")))
+ s.test(
+ "echo hello, world",
+ b.List().AddCommand(b.SimpleCommand("echo", "hello,", "world")))
- parse("`cat plain` \"`cat double`\" '`cat single`'",
- NewSimpleCommandBuilder().
- Name(tester.Token("`cat plain`")).
- Arg(tester.Token("\"`cat double`\"")).
- Arg(tester.Token("'`cat single`'")))
+ s.test("echo ${PKGNAME:Q}",
+ b.List().AddCommand(b.SimpleCommand("echo", "${PKGNAME:Q}")))
- parse("`\"one word\"`",
- NewSimpleCommandBuilder().
- Name(tester.Token("`\"one word\"`")))
+ s.test("${ECHO} \"Double-quoted\" 'Single-quoted'",
+ b.List().AddCommand(b.SimpleCommand("${ECHO}", "\"Double-quoted\"", "'Single-quoted'")))
- parse("PAGES=\"`ls -1 | ${SED} -e 's,3qt$$,3,'`\"",
- NewSimpleCommandBuilder().
- Assignment(tester.Token("PAGES=\"`ls -1 | ${SED} -e 's,3qt$$,3,'`\"")))
+ s.test("`cat plain` \"`cat double`\" '`cat single`'",
+ b.List().AddCommand(b.SimpleCommand("`cat plain`", "\"`cat double`\"", "'`cat single`'")))
- parse("var=Plain var=\"Dquot\" var='Squot' var=Plain\"Dquot\"'Squot'",
- NewSimpleCommandBuilder().
- Assignment(tester.Token("var=Plain")).
- Assignment(tester.Token("var=\"Dquot\"")).
- Assignment(tester.Token("var='Squot'")).
- Assignment(tester.Token("var=Plain\"Dquot\"'Squot'")))
+ s.test("`\"one word\"`",
+ b.List().AddCommand(b.SimpleCommand("`\"one word\"`")))
- parse("${RUN} subdir=\"`unzip -c \"$$e\" install.rdf | awk '/re/ { print \"hello\" }'`\"",
- NewSimpleCommandBuilder().
- Name(tester.Token("${RUN}")).
- Arg(tester.Token("subdir=\"`unzip -c \"$$e\" install.rdf | awk '/re/ { print \"hello\" }'`\"")))
+ s.test("PAGES=\"`ls -1 | ${SED} -e 's,3qt$$,3,'`\"",
+ b.List().AddCommand(b.SimpleCommand("PAGES=\"`ls -1 | ${SED} -e 's,3qt$$,3,'`\"")))
- parse("PATH=/nonexistent env PATH=${PATH:Q} true",
- NewSimpleCommandBuilder().
- Assignment(tester.Token("PATH=/nonexistent")).
- Name(tester.Token("env")).
- Arg(tester.Token("PATH=${PATH:Q}")).
- Arg(tester.Token("true")))
+ s.test("var=Plain var=\"Dquot\" var='Squot' var=Plain\"Dquot\"'Squot'",
+ b.List().AddCommand(b.SimpleCommand("var=Plain", "var=\"Dquot\"", "var='Squot'", "var=Plain\"Dquot\"'Squot'")))
- parse("{OpenGrok args",
- NewSimpleCommandBuilder().
- Name(tester.Token("{OpenGrok")).
- Arg(tester.Token("args")))
+ // RUN is a special Make variable since it ends with a semicolon;
+ // therefore it needs to be split off before passing the rest of
+ // the command to the shell command parser.
+ s.test("${RUN} subdir=\"`unzip -c \"$$e\" install.rdf | awk '/re/ { print \"hello\" }'`\"",
+ b.List().AddCommand(b.SimpleCommand("${RUN}", "subdir=\"`unzip -c \"$$e\" install.rdf | awk '/re/ { print \"hello\" }'`\"")))
- fail("if clause", "if clause")
- fail("{ group; }", "{ group; }")
+ s.test("PATH=/nonexistent env PATH=${PATH:Q} true",
+ b.List().AddCommand(b.SimpleCommand("PATH=/nonexistent", "env", "PATH=${PATH:Q}", "true")))
+ s.test("{OpenGrok args",
+ b.List().AddCommand(b.SimpleCommand("{OpenGrok", "args")))
}
-func (s *Suite) Test_MkShParser_RedirectList(c *check.C) {
-}
+func (s *ShSuite) Test_ShellParser_io_redirect(c *check.C) {
+ b := s.init(c)
-func (s *Suite) Test_MkShParser_IoRedirect(c *check.C) {
-}
+ s.test("echo >> ${PLIST_SRC}",
+ b.List().AddCommand(b.SimpleCommand("echo", ">>${PLIST_SRC}")))
-func (s *Suite) Test_MkShParser_IoFile(c *check.C) {
-}
+ s.test("echo >> ${PLIST_SRC}",
+ b.List().AddCommand(b.SimpleCommand("echo", ">>${PLIST_SRC}")))
-func (s *Suite) Test_MkShParser_IoHere(c *check.C) {
+ s.test("echo 1>output 2>>append 3>|clobber 4>&5 6<input >>append",
+ b.List().AddCommand(&MkShCommand{Simple: &MkShSimpleCommand{
+ Assignments: nil,
+ Name: b.Token("echo"),
+ Args: nil,
+ Redirections: []*MkShRedirection{
+ {1, ">", b.Token("output")},
+ {2, ">>", b.Token("append")},
+ {3, ">|", b.Token("clobber")},
+ {4, ">&", b.Token("5")},
+ {6, "<", b.Token("input")},
+ {-1, ">>", b.Token("append")}}}}))
+
+ s.test("echo 1> output 2>> append 3>| clobber 4>& 5 6< input >> append",
+ b.List().AddCommand(&MkShCommand{Simple: &MkShSimpleCommand{
+ Assignments: nil,
+ Name: b.Token("echo"),
+ Args: nil,
+ Redirections: []*MkShRedirection{
+ {1, ">", b.Token("output")},
+ {2, ">>", b.Token("append")},
+ {3, ">|", b.Token("clobber")},
+ {4, ">&", b.Token("5")},
+ {6, "<", b.Token("input")},
+ {-1, ">>", b.Token("append")}}}}))
}
-func (s *Suite) Test_MkShParser_NewlineList(c *check.C) {
+func (s *ShSuite) Test_ShellParser_io_here(c *check.C) {
+ b := s.init(c)
+
+ _ = b
}
-func (s *Suite) Test_MkShParser_Linebreak(c *check.C) {
+func (s *ShSuite) init(c *check.C) *MkShBuilder {
+ s.c = c
+ return NewMkShBuilder()
}
-func (s *Suite) Test_MkShParser_SeparatorOp(c *check.C) {
+func (s *ShSuite) test(program string, expected *MkShList) {
+ tokens, rest := splitIntoShellTokens(dummyLine, program)
+ s.c.Check(rest, equals, "")
+ lexer := &ShellLexer{
+ current: "",
+ remaining: tokens,
+ atCommandStart: true,
+ error: ""}
+ parser := ­yParserImpl{}
+ succeeded := parser.Parse(lexer)
+
+ c := s.c
+
+ if ok1, ok2 := c.Check(succeeded, equals, 0), c.Check(lexer.error, equals, ""); ok1 && ok2 {
+ if !c.Check(lexer.result, deepEquals, expected) {
+ actualJson, actualErr := json.MarshalIndent(lexer.result, "", " ")
+ expectedJson, expectedErr := json.MarshalIndent(expected, "", " ")
+ if c.Check(actualErr, check.IsNil) && c.Check(expectedErr, check.IsNil) {
+ c.Check(string(actualJson), deepEquals, string(expectedJson))
+ }
+ }
+ } else {
+ c.Check(lexer.remaining, deepEquals, []string{})
+ }
}
-func (s *Suite) Test_MkShParser_Separator(c *check.C) {
+type MkShBuilder struct {
+}
+func NewMkShBuilder() *MkShBuilder {
+ return &MkShBuilder{}
}
-func (s *Suite) Test_MkShParser_SequentialSep(c *check.C) {
+func (b *MkShBuilder) List() *MkShList {
+ return NewMkShList()
+}
+func (b *MkShBuilder) AndOr(pipeline *MkShPipeline) *MkShAndOr {
+ return NewMkShAndOr(pipeline)
}
-func (s *Suite) Test_MkShParser_Word(c *check.C) {
+func (b *MkShBuilder) Pipeline(negated bool, cmds ...*MkShCommand) *MkShPipeline {
+ return NewMkShPipeline(negated, cmds...)
+}
+func (b *MkShBuilder) SimpleCommand(words ...string) *MkShCommand {
+ cmd := &MkShSimpleCommand{}
+ assignments := true
+ for _, word := range words {
+ if assignments && matches(word, `^\w+=`) {
+ cmd.Assignments = append(cmd.Assignments, b.Token(word))
+ } else if m, fdstr, op, rest := match3(word, `^(\d*)(<<-|<<|<&|>>|>&|>\||<|>)(.*)$`); m {
+ fd, err := strconv.Atoi(fdstr)
+ if err != nil {
+ fd = -1
+ }
+ cmd.Redirections = append(cmd.Redirections, b.Redirection(fd, op, rest))
+ } else {
+ assignments = false
+ if cmd.Name == nil {
+ cmd.Name = b.Token(word)
+ } else {
+ cmd.Args = append(cmd.Args, b.Token(word))
+ }
+ }
+ }
+ return &MkShCommand{Simple: cmd}
}
-type MkShTester struct {
- c *check.C
+func (b *MkShBuilder) If(condActionElse ...*MkShList) *MkShCommand {
+ ifclause := &MkShIfClause{}
+ for i, part := range condActionElse {
+ if i%2 == 0 && i != len(condActionElse)-1 {
+ ifclause.Conds = append(ifclause.Conds, part)
+ } else if i%2 == 1 {
+ ifclause.Actions = append(ifclause.Actions, part)
+ } else {
+ ifclause.Else = part
+ }
+ }
+ return &MkShCommand{Compound: &MkShCompoundCommand{If: ifclause}}
}
-func (t *MkShTester) ParseCommand(str string) *MkShCommand {
- p := NewMkShParser(dummyLine, str, false)
- cmd := p.Command()
- t.c.Check(cmd, check.NotNil)
- t.c.Check(p.Rest(), equals, "")
- return cmd
+func (b *MkShBuilder) For(varname string, items []*ShToken, action *MkShList) *MkShCommand {
+ return &MkShCommand{Compound: &MkShCompoundCommand{For: &MkShForClause{varname, items, action}}}
}
-func (t *MkShTester) ParseSimpleCommand(str string) *MkShSimpleCommand {
- p := NewMkShParser(dummyLine, str, false)
- parsed := p.SimpleCommand()
- t.c.Check(parsed, check.NotNil)
- t.c.Check(p.Rest(), equals, "")
- return parsed
+func (b *MkShBuilder) Case(selector *ShToken, items ...*MkShCaseItem) *MkShCommand {
+ return &MkShCommand{Compound: &MkShCompoundCommand{Case: &MkShCaseClause{selector, items}}}
}
-func (t *MkShTester) ParseCompoundList(str string) *MkShList {
- p := NewMkShParser(dummyLine, str, false)
- parsed := p.CompoundList()
- t.c.Check(parsed, check.NotNil)
- t.c.Check(p.Rest(), equals, "")
- return parsed
+func (b *MkShBuilder) CaseItem(patterns []*ShToken, action *MkShList, separator *MkShSeparator) *MkShCaseItem {
+ return &MkShCaseItem{patterns, action, separator}
}
-func (t *MkShTester) Token(str string) *ShToken {
- p := NewMkShParser(dummyLine, str, false)
- parsed := p.peek()
- p.skip()
- t.c.Check(parsed, check.NotNil)
- t.c.Check(p.Rest(), equals, "")
- return parsed
+func (b *MkShBuilder) While(cond, action *MkShList, redirects ...*MkShRedirection) *MkShCommand {
+ return &MkShCommand{
+ Compound: &MkShCompoundCommand{
+ Loop: &MkShLoopClause{cond, action, false}},
+ Redirects: redirects}
}
-type SimpleCommandBuilder struct {
- Cmd *MkShSimpleCommand
+func (b *MkShBuilder) Until(cond, action *MkShList, redirects ...*MkShRedirection) *MkShCommand {
+ return &MkShCommand{
+ Compound: &MkShCompoundCommand{
+ Loop: &MkShLoopClause{cond, action, true}},
+ Redirects: redirects}
}
-func NewSimpleCommandBuilder() *SimpleCommandBuilder {
- cmd := &MkShSimpleCommand{}
- return &SimpleCommandBuilder{cmd}
+func (b *MkShBuilder) Function(name string, body *MkShCompoundCommand, redirects ...*MkShRedirection) *MkShCommand {
+ return &MkShCommand{
+ FuncDef: &MkShFunctionDefinition{name, body},
+ Redirects: redirects}
}
-func (b *SimpleCommandBuilder) Name(name *ShToken) *SimpleCommandBuilder {
- b.Cmd.Name = name
- return b
+
+func (b *MkShBuilder) Brace(list *MkShList) *MkShCommand {
+ return &MkShCommand{Compound: &MkShCompoundCommand{Brace: list}}
}
-func (b *SimpleCommandBuilder) Assignment(assignment *ShToken) *SimpleCommandBuilder {
- b.Cmd.Assignments = append(b.Cmd.Assignments, assignment)
- return b
+
+func (b *MkShBuilder) Subshell(list *MkShList) *MkShCommand {
+ return &MkShCommand{Compound: &MkShCompoundCommand{Subshell: list}}
}
-func (b *SimpleCommandBuilder) Arg(arg *ShToken) *SimpleCommandBuilder {
- b.Cmd.Args = append(b.Cmd.Args, arg)
- return b
+
+func (b *MkShBuilder) Token(mktext string) *ShToken {
+ tokenizer := NewShTokenizer(dummyLine, mktext, false)
+ token := tokenizer.ShToken()
+ return token
}
-func (b *SimpleCommandBuilder) Redirection(redirection *MkShRedirection) *SimpleCommandBuilder {
- b.Cmd.Redirections = append(b.Cmd.Redirections, redirection)
- return b
+
+func (b *MkShBuilder) Words(words ...string) []*ShToken {
+ tokens := make([]*ShToken, len(words))
+ for i, word := range words {
+ tokens[i] = b.Token(word)
+ }
+ return tokens
+}
+
+func (b *MkShBuilder) Redirection(fd int, op string, target string) *MkShRedirection {
+ return &MkShRedirection{fd, op, b.Token(target)}
}
--- pkgsrc/pkgtools/pkglint/files/Attic/mkshtypes.go 2016/06/05 11:24:32 1.1
+++ pkgsrc/pkgtools/pkglint/files/Attic/mkshtypes.go 2016/07/07 12:09:27 1.2
@@ -1,8 +1,6 @@
package main
-import (
- "fmt"
-)
+import "fmt"
type MkShList struct {
AndOrs []*MkShAndOr
@@ -13,10 +11,6 @@
return &MkShList{nil, nil}
}
-func (list *MkShList) String() string {
- return fmt.Sprintf("MkShList(%v)", list.AndOrs)
-}
-
func (list *MkShList) AddAndOr(andor *MkShAndOr) *MkShList {
list.AndOrs = append(list.AndOrs, andor)
return list
@@ -36,10 +30,6 @@
return &MkShAndOr{[]*MkShPipeline{pipeline}, nil}
}
-func (andor *MkShAndOr) String() string {
- return fmt.Sprintf("MkShAndOr(%v)", andor.Pipes)
-}
-
func (andor *MkShAndOr) Add(op string, pipeline *MkShPipeline) *MkShAndOr {
andor.Pipes = append(andor.Pipes, pipeline)
andor.Ops = append(andor.Ops, op)
@@ -55,10 +45,6 @@
return &MkShPipeline{negated, cmds}
}
-func (pipe *MkShPipeline) String() string {
- return fmt.Sprintf("MkShPipeline(%v)", pipe.Cmds)
-}
-
func (pipe *MkShPipeline) Add(cmd *MkShCommand) *MkShPipeline {
pipe.Cmds = append(pipe.Cmds, cmd)
return pipe
@@ -71,70 +57,30 @@
Redirects []*MkShRedirection // For Compound and FuncDef
}
-func (cmd *MkShCommand) String() string {
- switch {
- case cmd.Simple != nil:
- return cmd.Simple.String()
- case cmd.Compound != nil:
- return cmd.Compound.String()
- case cmd.FuncDef != nil:
- return cmd.FuncDef.String()
- }
- return "MkShCommand(?)"
-}
-
type MkShCompoundCommand struct {
Brace *MkShList
Subshell *MkShList
For *MkShForClause
Case *MkShCaseClause
If *MkShIfClause
- While *MkShLoopClause
- Until *MkShLoopClause
+ Loop *MkShLoopClause
}
-func (cmd *MkShCompoundCommand) String() string {
- switch {
- case cmd.Brace != nil:
- return cmd.Brace.String()
- case cmd.Subshell != nil:
- return cmd.Subshell.String()
- case cmd.For != nil:
- return cmd.For.String()
- case cmd.Case != nil:
- return cmd.Case.String()
- case cmd.If != nil:
- return cmd.If.String()
- case cmd.While != nil:
- return cmd.While.String()
- case cmd.Until != nil:
- return cmd.Until.String()
- }
- return "MkShCompoundCommand(?)"
-}
-
type MkShForClause struct {
Varname string
Values []*ShToken
Body *MkShList
}
-func (cl *MkShForClause) String() string {
- return fmt.Sprintf("MkShForClause(%v, %v, %v)", cl.Varname, cl.Values, cl.Body)
-}
-
type MkShCaseClause struct {
Word *ShToken
Cases []*MkShCaseItem
}
-func (cl *MkShCaseClause) String() string {
- return fmt.Sprintf("MkShCaseClause(...)")
-}
-
type MkShCaseItem struct {
- Patterns []*ShToken
- Action *MkShList
+ Patterns []*ShToken
+ Action *MkShList
+ Separator *MkShSeparator
}
type MkShIfClause struct {
@@ -143,10 +89,6 @@
Else *MkShList
}
-func (cl *MkShIfClause) String() string {
- return "MkShIf(...)"
-}
-
func (cl *MkShIfClause) Prepend(cond *MkShList, action *MkShList) {
cl.Conds = append([]*MkShList{cond}, cl.Conds...)
cl.Actions = append([]*MkShList{action}, cl.Actions...)
@@ -158,20 +100,11 @@
Until bool
}
-func (cl *MkShLoopClause) String() string {
- return "MkShLoop(...)"
-}
-
type MkShFunctionDefinition struct {
- Name string
- Body *MkShCompoundCommand
- Redirects []*MkShRedirection
+ Name string
+ Body *MkShCompoundCommand
}
-func (def *MkShFunctionDefinition) String() string {
- return "MkShFunctionDef(...)"
-}
-
type MkShSimpleCommand struct {
Assignments []*ShToken
Name *ShToken
@@ -179,53 +112,62 @@
Redirections []*MkShRedirection
}
-func (scmd *MkShSimpleCommand) String() string {
- str := "SimpleCommand("
- first := true
- sep := func() {
- if first {
- first = false
- } else {
- str += ", "
- }
+func NewStrCommand(cmd *MkShSimpleCommand) *StrCommand {
+ strcmd := &StrCommand{
+ make([]string, len(cmd.Assignments)),
+ "",
+ make([]string, len(cmd.Args))}
+ for i, assignment := range cmd.Assignments {
+ strcmd.Assignments[i] = assignment.MkText
}
- for _, word := range scmd.Assignments {
- sep()
- str += word.MkText
+ if cmd.Name != nil {
+ strcmd.Name = cmd.Name.MkText
}
- if word := scmd.Name; word != nil {
- sep()
- str += word.MkText
+ for i, arg := range cmd.Args {
+ strcmd.Args[i] = arg.MkText
}
- for _, word := range scmd.Args {
- sep()
- str += word.MkText
+ return strcmd
+}
+
+type StrCommand struct {
+ Assignments []string
+ Name string
+ Args []string
+}
+
+func (c *StrCommand) HasOption(opt string) bool {
+ for _, arg := range c.Args {
+ if arg == opt {
+ return true
+ }
}
- for _, redirection := range scmd.Redirections {
- sep()
- str += redirection.String()
+ return false
+}
+
+func (c *StrCommand) AnyArgMatches(pattern string) bool {
+ for _, arg := range c.Args {
+ if matches(arg, pattern) {
+ return true
+ }
}
- return str + ")"
+ return false
}
+func (c *StrCommand) String() string {
+ return fmt.Sprintf("%v %v %v", c.Assignments, c.Name, c.Args)
+}
+
type MkShRedirection struct {
Fd int // Or -1
Op string
Target *ShToken
}
-func (r *MkShRedirection) String() string {
- if r.Fd != -1 {
- return fmt.Sprintf("%d%s%s", r.Fd, r.Op, r.Target.MkText)
- } else {
- return r.Op + r.Target.MkText
- }
-}
-
// One of ";", "&", "\n"
type MkShSeparator string
-func (sep *MkShSeparator) String() string {
+var (
- return fmt.Sprintf("%q", sep)
+ SEP_SEMI MkShSeparator = ";"
-
+ SEP_BACKGROUND MkShSeparator = "&"
-}
+ SEP_NEWLINE MkShSeparator = "\n"
+)
--- pkgsrc/pkgtools/pkglint/files/Attic/mktypes_test.go 2016/06/05 11:24:32 1.1
+++ pkgsrc/pkgtools/pkglint/files/Attic/mktypes_test.go 2016/07/07 12:09:27 1.2
--- pkgsrc/pkgtools/pkglint/files/Attic/shtokenizer.go 2016/06/05 11:24:32 1.1
+++ pkgsrc/pkgtools/pkglint/files/Attic/shtokenizer.go 2016/07/07 12:09:27 1.2
@@ -34,12 +34,16 @@
atom = p.shAtomSquot()
case shqBackt:
atom = p.shAtomBackt()
+ case shqSubsh:
+ atom = p.shAtomSub()
case shqDquotBackt:
atom = p.shAtomDquotBackt()
case shqBacktDquot:
atom = p.shAtomBacktDquot()
case shqBacktSquot:
atom = p.shAtomBacktSquot()
+ case shqSubshSquot:
+ atom = p.shAtomSubshSquot()
case shqDquotBacktDquot:
atom = p.shAtomDquotBacktDquot()
case shqDquotBacktSquot:
@@ -59,6 +63,8 @@
switch {
case repl.AdvanceHspace():
return &ShAtom{shtSpace, repl.s, q, nil}
+ case repl.AdvanceStr("\n"):
+ return &ShAtom{shtNewline, repl.s, q, nil}
case repl.AdvanceStr(";;"):
return &ShAtom{shtCaseSeparator, repl.s, q, nil}
case repl.AdvanceStr(";"):
@@ -81,7 +87,7 @@
return &ShAtom{shtWord, repl.s, shqSquot, nil}
case repl.AdvanceStr("`"):
return &ShAtom{shtWord, repl.s, shqBackt, nil}
- case repl.AdvanceRegexp(`^(?:<|<<|>|>>|>&)`):
+ case repl.AdvanceRegexp(`^\d*(?:<<-|<<|<&|<>|>>|>&|>\||<|>)`):
return &ShAtom{shtRedirect, repl.m[0], q, nil}
case repl.AdvanceRegexp(`^#.*`):
return &ShAtom{shtComment, repl.m[0], q, nil}
@@ -155,6 +161,46 @@
return nil
}
+func (p *ShTokenizer) shAtomSub() *ShAtom {
+ const q = shqSubsh
+ repl := p.parser.repl
+ mark := repl.Mark()
+ atom := func(typ ShAtomType) *ShAtom {
+ return NewShAtom(typ, repl.Since(mark), shqSubsh)
+ }
+ switch {
+ case repl.AdvanceHspace():
+ return atom(shtSpace)
+ case repl.AdvanceStr(";;"):
+ return atom(shtCaseSeparator)
+ case repl.AdvanceStr(";"):
+ return atom(shtSemicolon)
+ case repl.AdvanceStr("||"):
+ return atom(shtOr)
+ case repl.AdvanceStr("&&"):
+ return atom(shtAnd)
+ case repl.AdvanceStr("|"):
+ return atom(shtPipe)
+ case repl.AdvanceStr("&"):
+ return atom(shtBackground)
+ case repl.AdvanceStr("\""):
+ //return &ShAtom{shtWord, repl.s, shqDquot, nil}
+ case repl.AdvanceStr("'"):
+ return &ShAtom{shtWord, repl.s, shqSubshSquot, nil}
+ case repl.AdvanceStr("`"):
+ //return &ShAtom{shtWord, repl.s, shqBackt, nil}
+ case repl.AdvanceRegexp(`^\d*(?:<<-|<<|<&|<>|>>|>&|>\||<|>)`):
+ return &ShAtom{shtRedirect, repl.m[0], q, nil}
+ case repl.AdvanceRegexp(`^#.*`):
+ return &ShAtom{shtComment, repl.m[0], q, nil}
+ case repl.AdvanceStr(")"):
+ return NewShAtom(shtWord, repl.s, shqPlain)
+ case repl.AdvanceRegexp(`^(?:[!#%*+,\-./0-9:=?@A-Z\[\]^_a-z{}~]+|\\[^$]|` + reShDollar + `)+`):
+ return &ShAtom{shtWord, repl.m[0], q, nil}
+ }
+ return nil
+}
+
func (p *ShTokenizer) shAtomDquotBackt() *ShAtom {
const q = shqDquotBackt
repl := p.parser.repl
@@ -216,6 +262,18 @@
return nil
}
+func (p *ShTokenizer) shAtomSubshSquot() *ShAtom {
+ const q = shqSubshSquot
+ repl := p.parser.repl
+ switch {
+ case repl.AdvanceStr("'"):
+ return &ShAtom{shtWord, repl.s, shqSubsh, nil}
+ case repl.AdvanceRegexp(`^([\t !"#%&()*+,\-./0-9:;<=>?@A-Z\[\\\]^_` + "`" + `a-z{|}~]+|\$\$)+`):
+ return &ShAtom{shtWord, repl.m[0], q, nil}
+ }
+ return nil
+}
+
func (p *ShTokenizer) shAtomDquotBacktDquot() *ShAtom {
const q = shqDquotBacktDquot
repl := p.parser.repl
@@ -281,7 +339,7 @@
return nil
}
if atom := peek(); !atom.Type.IsWord() {
- return NewShToken(atom.Text, atom)
+ return NewShToken(atom.MkText, atom)
}
nextatom:
--- pkgsrc/pkgtools/pkglint/files/Attic/shtokenizer_test.go 2016/06/05 11:24:32 1.1
+++ pkgsrc/pkgtools/pkglint/files/Attic/shtokenizer_test.go 2016/07/07 12:09:27 1.2
@@ -15,9 +15,10 @@
}
return p.Rest()
}
- check := func(s string, expected ...*ShAtom) {
- rest := checkRest(s, expected...)
+ check := func(str string, expected ...*ShAtom) {
+ rest := checkRest(str, expected...)
c.Check(rest, equals, "")
+ c.Check(s.Output(), equals, "")
}
token := func(typ ShAtomType, text string, quoting ShQuoting) *ShAtom {
@@ -37,7 +38,7 @@
return &ShAtom{shtVaruse, text, shqPlain, varuse}
}
q := func(q ShQuoting, token *ShAtom) *ShAtom {
- return &ShAtom{token.Type, token.Text, q, token.Data}
+ return &ShAtom{token.Type, token.MkText, q, token.Data}
}
whitespace := func(s string) *ShAtom { return token(shtSpace, s, shqPlain) }
space := token(shtSpace, " ", shqPlain)
@@ -302,6 +303,22 @@
word("then"), space, word("action2"), semicolon, space,
word("else"), space, word("action3"), semicolon, space,
word("fi"))
+
+ if false {
+ check("$$(cat)",
+ token(shtWord, "$$(", shqSubsh),
+ token(shtWord, "cat", shqSubsh),
+ token(shtWord, ")", shqPlain))
+
+ check("$$(cat 'file')",
+ token(shtWord, "$$(", shqSubsh),
+ token(shtWord, "cat", shqSubsh),
+ token(shtSpace, " ", shqSubsh),
+ token(shtWord, "'", shqSubshSquot),
+ token(shtWord, "file", shqSubshSquot),
+ token(shtWord, "'", shqSubsh),
+ token(shtWord, ")", shqPlain))
+ }
}
func (s *Suite) Test_Shtokenizer_ShAtom_Quoting(c *check.C) {
@@ -314,7 +331,7 @@
if token == nil {
break
}
- result += token.Text
+ result += token.MkText
if token.Quoting != q {
q = token.Quoting
result += "[" + q.String() + "]"
--- pkgsrc/pkgtools/pkglint/files/Attic/shtypes.go 2016/06/05 11:24:32 1.1
+++ pkgsrc/pkgtools/pkglint/files/Attic/shtypes.go 2016/07/07 12:09:27 1.2
@@ -23,6 +23,7 @@
shtRedirect // >, <, >>
shtComment // # ...
shtSubshell // $$(
+ shtNewline // \n
)
func (t ShAtomType) String() string {
@@ -37,6 +38,7 @@
"or", "and",
"redirect",
"comment",
+ "newline",
}[t]
}
@@ -50,7 +52,7 @@
func (t ShAtomType) IsCommandDelimiter() bool {
switch t {
- case shtSemicolon, shtPipe, shtBackground, shtAnd, shtOr, shtCaseSeparator:
+ case shtSemicolon, shtNewline, shtPipe, shtBackground, shtAnd, shtOr, shtCaseSeparator:
return true
}
return false
@@ -59,7 +61,7 @@
// @Beta
type ShAtom struct {
Type ShAtomType
- Text string
+ MkText string
Quoting ShQuoting
Data interface{}
}
@@ -74,13 +76,13 @@
func (token *ShAtom) String() string {
if token.Type == shtWord && token.Quoting == shqPlain && token.Data == nil {
- return fmt.Sprintf("%q", token.Text)
+ return fmt.Sprintf("%q", token.MkText)
}
if token.Type == shtVaruse {
varuse := token.Data.(*MkVarUse)
return fmt.Sprintf("varuse(%q)", varuse.varname+varuse.Mod())
}
- return fmt.Sprintf("ShAtom(%v, %q, %s)", token.Type, token.Text, token.Quoting)
+ return fmt.Sprintf("ShAtom(%v, %q, %s)", token.Type, token.MkText, token.Quoting)
}
// ShQuoting describes the context in which a string appears
@@ -92,9 +94,11 @@
shqDquot
shqSquot
shqBackt
+ shqSubsh
shqDquotBackt
shqBacktDquot
shqBacktSquot
+ shqSubshSquot
shqDquotBacktDquot
shqDquotBacktSquot
shqUnknown
@@ -103,8 +107,8 @@
func (q ShQuoting) String() string {
return [...]string{
"plain",
- "d", "s", "b",
- "db", "bd", "bs",
+ "d", "s", "b", "S",
+ "db", "bd", "bs", "Ss",
"dbd", "dbs",
"unknown",
}[q]
package main
type MkShWalker struct {
}
func (w *MkShWalker) Walk(list *MkShList, callback func(node interface{})) {
for element := range w.iterate(list) {
callback(element)
}
}
func (w *MkShWalker) iterate(list *MkShList) chan interface{} {
elements := make(chan interface{})
go func() {
w.walkList(list, elements)
close(elements)
}()
return elements
}
func (w *MkShWalker) walkList(list *MkShList, collector chan interface{}) {
collector <- list
for _, andor := range list.AndOrs {
w.walkAndOr(andor, collector)
}
}
func (w *MkShWalker) walkAndOr(andor *MkShAndOr, collector chan interface{}) {
collector <- andor
for _, pipeline := range andor.Pipes {
w.walkPipeline(pipeline, collector)
}
}
func (w *MkShWalker) walkPipeline(pipeline *MkShPipeline, collector chan interface{}) {
collector <- pipeline
for _, command := range pipeline.Cmds {
w.walkCommand(command, collector)
}
}
func (w *MkShWalker) walkCommand(command *MkShCommand, collector chan interface{}) {
collector <- command
switch {
case command.Simple != nil:
w.walkSimpleCommand(command.Simple, collector)
case command.Compound != nil:
w.walkCompoundCommand(command.Compound, collector)
w.walkRedirects(command.Redirects, collector)
case command.FuncDef != nil:
w.walkFunctionDefinition(command.FuncDef, collector)
w.walkRedirects(command.Redirects, collector)
}
}
func (w *MkShWalker) walkSimpleCommand(command *MkShSimpleCommand, collector chan interface{}) {
collector <- command
w.walkWords(command.Assignments, collector)
if command.Name != nil {
w.walkWord(command.Name, collector)
}
w.walkWords(command.Args, collector)
w.walkRedirects(command.Redirections, collector)
}
func (w *MkShWalker) walkCompoundCommand(command *MkShCompoundCommand, collector chan interface{}) {
collector <- command
switch {
case command.Brace != nil:
w.walkList(command.Brace, collector)
case command.Case != nil:
w.walkCase(command.Case, collector)
case command.For != nil:
w.walkFor(command.For, collector)
case command.If != nil:
w.walkIf(command.If, collector)
case command.Loop != nil:
w.walkLoop(command.Loop, collector)
case command.Subshell != nil:
w.walkList(command.Subshell, collector)
}
}
func (w *MkShWalker) walkCase(caseClause *MkShCaseClause, collector chan interface{}) {
collector <- caseClause
w.walkWord(caseClause.Word, collector)
for _, caseItem := range caseClause.Cases {
collector <- caseItem
w.walkWords(caseItem.Patterns, collector)
w.walkList(caseItem.Action, collector)
}
}
func (w *MkShWalker) walkFunctionDefinition(funcdef *MkShFunctionDefinition, collector chan interface{}) {
collector <- funcdef
w.walkCompoundCommand(funcdef.Body, collector)
}
func (w *MkShWalker) walkIf(ifClause *MkShIfClause, collector chan interface{}) {
collector <- ifClause
for i, cond := range ifClause.Conds {
w.walkList(cond, collector)
w.walkList(ifClause.Actions[i], collector)
}
if ifClause.Else != nil {
w.walkList(ifClause.Else, collector)
}
}
func (w *MkShWalker) walkLoop(loop *MkShLoopClause, collector chan interface{}) {
collector <- loop
w.walkList(loop.Cond, collector)
w.walkList(loop.Action, collector)
}
func (w *MkShWalker) walkWords(words []*ShToken, collector chan interface{}) {
collector <- words
for _, word := range words {
w.walkWord(word, collector)
}
}
func (w *MkShWalker) walkWord(word *ShToken, collector chan interface{}) {
collector <- word
}
func (w *MkShWalker) walkRedirects(redirects []*MkShRedirection, collector chan interface{}) {
collector <- redirects
for _, redirect := range redirects {
collector <- redirect
w.walkWord(redirect.Target, collector)
}
}
func (w *MkShWalker) walkFor(forClause *MkShForClause, collector chan interface{}) {
collector <- forClause
collector <- forClause.Varname
w.walkWords(forClause.Values, collector)
w.walkList(forClause.Body, collector)
}
package main
import (
"gopkg.in/check.v1"
)
func (s *Suite) Test_MkShWalker_Walk(c *check.C) {
list, err := parseShellProgram(dummyLine, ""+
"if condition; then action; else case selector in pattern) case-item-action ;; esac; fi; "+
"set -e; cd ${WRKSRC}/locale; "+
"for lang in *.po; do "+
" [ \"$${lang}\" = \"wxstd.po\" ] && continue; "+
" ${TOOLS_PATH.msgfmt} -c -o \"$${lang%.po}.mo\" \"$${lang}\"; "+
"done")
if c.Check(err, check.IsNil) && c.Check(list, check.NotNil) {
var commands []string
(*MkShWalker).Walk(nil, list, func(node interface{}) {
if cmd, ok := node.(*MkShSimpleCommand); ok {
commands = append(commands, NewStrCommand(cmd).String())
}
})
c.Check(commands, deepEquals, []string{
"[] condition []",
"[] action []",
"[] case-item-action []",
"[] set [-e]",
"[] cd [${WRKSRC}/locale]",
"[] [ [\"$${lang}\" = \"wxstd.po\" ]]",
"[] continue []",
"[] ${TOOLS_PATH.msgfmt} [-c -o \"$${lang%.po}.mo\" \"$${lang}\"]"})
}
}
%{
package main
%}
%token <Word> tkWORD
%token <Word> tkASSIGNMENT_WORD
%token tkNEWLINE
%token <IONum> tkIO_NUMBER
%token tkBACKGROUND
%token tkPIPE tkSEMI
%token tkAND tkOR tkSEMISEMI
%token tkLT tkGT tkLTLT tkGTGT tkLTAND tkGTAND tkLTGT tkLTLTDASH tkGTPIPE
%token tkIF tkTHEN tkELSE tkELIF tkFI tkDO tkDONE
%token tkCASE tkESAC tkWHILE tkUNTIL tkFOR
%token tkLPAREN tkRPAREN tkLBRACE tkRBRACE tkEXCLAM
%token tkIN
%union {
IONum int
List *MkShList
AndOr *MkShAndOr
Pipeline *MkShPipeline
Command *MkShCommand
CompoundCommand *MkShCompoundCommand
Separator MkShSeparator
Simple *MkShSimpleCommand
FuncDef *MkShFunctionDefinition
For *MkShForClause
If *MkShIfClause
Case *MkShCaseClause
CaseItem *MkShCaseItem
Loop *MkShLoopClause
Words []*ShToken
Word *ShToken
Redirections []*MkShRedirection
Redirection *MkShRedirection
}
%type <List> start program compound_list brace_group subshell term do_group
%type <AndOr> and_or
%type <Pipeline> pipeline pipe_sequence
%type <Command> command
%type <CompoundCommand> compound_command
%type <Separator> separator separator_op sequential_sep
%type <Simple> simple_command cmd_prefix cmd_suffix
%type <FuncDef> function_definition
%type <For> for_clause
%type <If> if_clause else_part
%type <Case> case_clause case_list case_list_ns
%type <CaseItem> case_item case_item_ns
%type <Loop> while_clause until_clause
%type <Words> wordlist case_selector pattern
%type <Word> filename cmd_word here_end
%type <Redirections> redirect_list
%type <Redirection> io_redirect io_file io_here
%%
start : program {
shyylex.(*ShellLexer).result = $$
}
program : compound_list {
$$ = $1
}
program : /* empty */ {
$$ = &MkShList{}
}
and_or : pipeline {
$$ = NewMkShAndOr($1)
}
and_or : and_or tkAND linebreak pipeline {
$$.Add("&&", $4)
}
and_or : and_or tkOR linebreak pipeline {
$$.Add("||", $4)
}
pipeline : pipe_sequence {
/* empty */
}
pipeline : tkEXCLAM pipe_sequence {
$$ = $2
$$.Negated = true
}
pipe_sequence : command {
$$ = NewMkShPipeline(false, $1)
}
pipe_sequence : pipe_sequence tkPIPE linebreak command {
$$.Add($4)
}
command : simple_command {
$$ = &MkShCommand{Simple: $1}
}
command : compound_command {
$$ = &MkShCommand{Compound: $1}
}
command : compound_command redirect_list {
$$ = &MkShCommand{Compound: $1, Redirects: $2}
}
command : function_definition {
$$ = &MkShCommand{FuncDef: $1}
}
command : function_definition redirect_list {
$$ = &MkShCommand{FuncDef: $1, Redirects: $2}
}
compound_command : brace_group {
$$ = &MkShCompoundCommand{Brace: $1}
}
compound_command : subshell {
$$ = &MkShCompoundCommand{Subshell: $1}
}
compound_command : for_clause {
$$ = &MkShCompoundCommand{For: $1}
}
compound_command : case_clause {
$$ = &MkShCompoundCommand{Case: $1}
}
compound_command : if_clause {
$$ = &MkShCompoundCommand{If: $1}
}
compound_command : while_clause {
$$ = &MkShCompoundCommand{Loop: $1}
}
compound_command : until_clause {
$$ = &MkShCompoundCommand{Loop: $1}
}
subshell : tkLPAREN compound_list tkRPAREN {
$$ = $2
}
compound_list : linebreak term {
$$ = $2
}
compound_list : linebreak term separator {
$$ = $2
$$.AddSeparator($3)
}
term : and_or {
$$ = NewMkShList()
$$.AddAndOr($1)
}
term : term separator and_or {
$$.AddSeparator($2)
$$.AddAndOr($3)
}
for_clause : tkFOR tkWORD linebreak do_group {
args := NewShToken("\"$$@\"",
NewShAtom(shtWord, "\"",shqDquot),
NewShAtom(shtWord, "$$@",shqDquot),
NewShAtom(shtWord,"\"",shqPlain))
$$ = &MkShForClause{$2.MkText, []*ShToken{args}, $4}
}
for_clause : tkFOR tkWORD linebreak tkIN sequential_sep do_group {
$$ = &MkShForClause{$2.MkText, nil, $6}
}
for_clause : tkFOR tkWORD linebreak tkIN wordlist sequential_sep do_group {
$$ = &MkShForClause{$2.MkText, $5, $7}
}
wordlist : tkWORD {
$$ = append($$, $1)
}
wordlist : wordlist tkWORD {
$$ = append($$, $2)
}
case_clause : tkCASE tkWORD linebreak tkIN linebreak case_list tkESAC {
$$ = $6
$$.Word = $2
}
case_clause : tkCASE tkWORD linebreak tkIN linebreak case_list_ns tkESAC {
$$ = $6
$$.Word = $2
}
case_clause : tkCASE tkWORD linebreak tkIN linebreak tkESAC {
$$ = &MkShCaseClause{$2, nil}
}
case_list_ns : case_item_ns {
$$ = &MkShCaseClause{nil, nil}
$$.Cases = append($$.Cases, $1)
}
case_list_ns : case_list case_item_ns {
$$.Cases = append($$.Cases, $2)
}
case_list : case_item {
$$ = &MkShCaseClause{nil, nil}
$$.Cases = append($$.Cases, $1)
}
case_list : case_list case_item {
$$.Cases = append($$.Cases, $2)
}
case_selector : tkLPAREN pattern tkRPAREN {
$$ = $2
}
case_selector : pattern tkRPAREN {
/* empty */
}
case_item_ns : case_selector linebreak {
$$ = &MkShCaseItem{$1, &MkShList{}, nil}
}
case_item_ns : case_selector linebreak term linebreak {
$$ = &MkShCaseItem{$1, $3, nil}
}
case_item_ns : case_selector linebreak term separator_op linebreak {
$$ = &MkShCaseItem{$1, $3, &$4}
}
case_item : case_selector linebreak tkSEMISEMI linebreak {
$$ = &MkShCaseItem{$1, &MkShList{}, nil}
}
case_item : case_selector compound_list tkSEMISEMI linebreak {
$$ = &MkShCaseItem{$1, $2, nil}
}
pattern : tkWORD {
$$ = nil
$$ = append($$, $1)
}
pattern : pattern tkPIPE tkWORD {
$$ = append($$, $3)
}
if_clause : tkIF compound_list tkTHEN compound_list else_part tkFI {
$$ = $5
$$.Prepend($2, $4)
}
if_clause : tkIF compound_list tkTHEN compound_list tkFI {
$$ = &MkShIfClause{}
$$.Prepend($2, $4)
}
else_part : tkELIF compound_list tkTHEN compound_list {
$$ = &MkShIfClause{}
$$.Prepend($2, $4)
}
else_part : tkELIF compound_list tkTHEN compound_list else_part {
$$ = $5
$$.Prepend($2, $4)
}
else_part : tkELSE compound_list {
$$ = &MkShIfClause{nil, nil, $2}
}
while_clause : tkWHILE compound_list do_group {
$$ = &MkShLoopClause{$2, $3, false}
}
until_clause : tkUNTIL compound_list do_group {
$$ = &MkShLoopClause{$2, $3, true}
}
function_definition : tkWORD tkLPAREN tkRPAREN linebreak compound_command { /* Apply rule 9 */
$$ = &MkShFunctionDefinition{$1.MkText, $5}
}
brace_group : tkLBRACE compound_list tkRBRACE {
$$ = $2
}
do_group : tkDO compound_list tkDONE {
$$ = $2
}
simple_command : cmd_prefix cmd_word cmd_suffix {
$$.Name = $2
$$.Args = append($$.Args, $3.Args...)
$$.Redirections = append($$.Redirections, $3.Redirections...)
}
simple_command : cmd_prefix cmd_word {
$$.Name = $2
}
simple_command : cmd_prefix {
/* empty */
}
simple_command : tkWORD cmd_suffix {
$$ = $2
$$.Name = $1
}
simple_command : tkWORD {
$$ = &MkShSimpleCommand{Name: $1}
}
cmd_word : tkWORD { /* Apply rule 7b */
/* empty */
}
cmd_prefix : io_redirect {
$$ = &MkShSimpleCommand{}
$$.Redirections = append($$.Redirections, $1)
}
cmd_prefix : tkASSIGNMENT_WORD {
$$ = &MkShSimpleCommand{}
$$.Assignments = append($$.Assignments, $1)
}
cmd_prefix : cmd_prefix io_redirect {
$$.Redirections = append($$.Redirections, $2)
}
cmd_prefix : cmd_prefix tkASSIGNMENT_WORD {
$$.Assignments = append($$.Assignments, $2)
}
cmd_suffix : io_redirect {
$$ = &MkShSimpleCommand{}
$$.Redirections = append($$.Redirections, $1)
}
cmd_suffix : tkWORD {
$$ = &MkShSimpleCommand{}
$$.Args = append($$.Args, $1)
}
cmd_suffix : cmd_suffix io_redirect {
$$.Redirections = append($$.Redirections, $2)
}
cmd_suffix : cmd_suffix tkWORD {
$$.Args = append($$.Args, $2)
}
redirect_list : io_redirect {
$$ = nil
$$ = append($$, $1)
}
redirect_list : redirect_list io_redirect {
$$ = append($$, $2)
}
io_redirect : io_file {
/* empty */
}
io_redirect : tkIO_NUMBER io_file {
$$ = $2
$$.Fd = $1
}
io_redirect : io_here {
/* empty */
}
io_redirect : tkIO_NUMBER io_here {
$$ = $2
$$.Fd = $1
}
io_file : tkLT filename {
$$ = &MkShRedirection{-1, "<", $2}
}
io_file : tkLTAND filename {
$$ = &MkShRedirection{-1, "<&", $2}
}
io_file : tkGT filename {
$$ = &MkShRedirection{-1, ">", $2}
}
io_file : tkGTAND filename {
$$ = &MkShRedirection{-1, ">&", $2}
}
io_file : tkGTGT filename {
$$ = &MkShRedirection{-1, ">>", $2}
}
io_file : tkLTGT filename {
$$ = &MkShRedirection{-1, "<>", $2}
}
io_file : tkGTPIPE filename {
$$ = &MkShRedirection{-1, ">|", $2}
}
filename : tkWORD { /* Apply rule 2 */
/* empty */
}
io_here : tkLTLT here_end {
$$ = &MkShRedirection{-1, "<<", $2}
}
io_here : tkLTLTDASH here_end {
$$ = &MkShRedirection{-1, "<<-", $2}
}
here_end : tkWORD { /* Apply rule 3 */
/* empty */
}
newline_list : tkNEWLINE {
/* empty */
}
newline_list : newline_list tkNEWLINE {
/* empty */
}
linebreak : newline_list {
/* empty */
}
linebreak : /* empty */ {
/* empty */
}
separator_op : tkBACKGROUND {
$$ = "&"
}
separator_op : tkSEMI {
$$ = ";"
}
separator : separator_op linebreak {
/* empty */
}
separator : newline_list {
$$ = "\n"
}
sequential_sep : tkSEMI linebreak {
$$ = ";"
}
sequential_sep : tkNEWLINE linebreak {
$$ = "\n"
}
--- pkgsrc/pkgtools/pkglint/files/Attic/package.go 2016/06/05 11:24:32 1.8
+++ pkgsrc/pkgtools/pkglint/files/Attic/package.go 2016/07/07 12:09:27 1.9
@@ -2,6 +2,7 @@
import (
"fmt"
+ "os/user"
"path"
"regexp"
"strconv"
@@ -196,6 +197,7 @@
} else if hasSuffix(fname, "/distinfo") {
haveDistinfo = true
}
+ pkg.checkLocallyModified(fname)
}
if G.opts.CheckDistinfo && G.opts.CheckPatches {
@@ -387,7 +389,7 @@
perlLine.Warn1("REPLACE_PERL is ignored when NO_CONFIGURE is set (in %s)", noconfLine.Line.ReferenceFrom(perlLine.Line))
}
- if vardef["LICENSE"] == nil {
+ if vardef["LICENSE"] == nil && vardef["META_PACKAGE"] == nil {
NewLineWhole(fname).Error0("Each package must define its LICENSE.")
}
@@ -484,19 +486,43 @@
}
func (pkg *Package) pkgnameFromDistname(pkgname, distname string) string {
- pkgname = strings.Replace(pkgname, "${DISTNAME}", distname, -1)
+ tokens := NewMkParser(dummyLine, pkgname, false).MkTokens()
- if m, before, sep, subst, after := match4(pkgname, `^(.*)\$\{DISTNAME:S(.)([^\\}:]+)\}(.*)$`); m {
- qsep := regexp.QuoteMeta(sep)
- if m, left, from, right, to, mod := match5(subst, `^(\^?)([^:]*)(\$?)`+qsep+`([^:]*)`+qsep+`(g?)$`); m {
- newPkgname := before + mkopSubst(distname, left != "", from, right != "", to, mod != "") + after
+ subst := func(str, smod string) (result string) {
+ if G.opts.Debug {
+ defer tracecall(str, smod, ref(result))()
+ }
+ qsep := regexp.QuoteMeta(smod[1:2])
+ if m, left, from, right, to, flags := match5(smod, `^S`+qsep+`(\^?)([^:]*?)(\$?)`+qsep+`([^:]*)`+qsep+`([1g]*)$`); m {
+ result := mkopSubst(str, left != "", from, right != "", to, flags)
if G.opts.Debug {
- traceStep("%s: pkgnameFromDistname %q => %q", pkg.vardef["PKGNAME"], pkgname, newPkgname)
+ traceStep("subst %q %q => %q", str, smod, result)
}
- pkgname = newPkgname
+ return result
}
+ return str
}
- return pkgname
+
+ result := ""
+ for _, token := range tokens {
+ if token.Varuse != nil && token.Varuse.varname == "DISTNAME" {
+ newDistname := distname
+ for _, mod := range token.Varuse.modifiers {
+ if mod == "tl" {
+ newDistname = strings.ToLower(newDistname)
+ } else if hasPrefix(mod, "S") {
+ newDistname = subst(newDistname, mod)
+ } else {
+ newDistname = token.Text
+ break
+ }
+ }
+ result += newDistname
+ } else {
+ result += token.Text
+ }
+ }
+ return result
}
func (pkg *Package) checkUpdate() {
@@ -757,4 +783,54 @@
"documenting its interface.")
}
SaveAutofixChanges(lines)
+}
+
+func (pkg *Package) checkLocallyModified(fname string) {
+ if G.opts.Debug {
+ defer tracecall(fname)()
+ }
+
+ ownerLine := pkg.vardef["OWNER"]
+ maintainerLine := pkg.vardef["MAINTAINER"]
+ owner := ""
+ maintainer := ""
+ if ownerLine != nil && !containsVarRef(ownerLine.Value()) {
+ owner = ownerLine.Value()
+ }
+ if maintainerLine != nil && !containsVarRef(maintainerLine.Value()) && maintainerLine.Value() != "pkgsrc-users@NetBSD.org" {
+ maintainer = maintainerLine.Value()
+ }
+ if owner == "" && maintainer == "" {
+ return
+ }
+
+ user, err := user.Current()
+ if err != nil || user.Username == "" {
+ return
+ }
+ // On Windows, this is `Computername\Username`.
+ username := regcomp(`^.*\\`).ReplaceAllString(user.Username, "")
+
+ if G.opts.Debug {
+ traceStep("user=%q owner=%q maintainer=%q", username, owner, maintainer)
+ }
+
+ if username == strings.Split(owner, "@")[0] || username == strings.Split(maintainer, "@")[0] {
+ return
+ }
+
+ if isLocallyModified(fname) {
+ if owner != "" {
+ NewLineWhole(fname).Warn1("Don't commit changes to this file without asking the OWNER, %s.", owner)
+ Explain2(
+ "See the pkgsrc guide, section \"Package components\",",
+ "keyword \"owner\", for more information.")
+ }
+ if maintainer != "" {
+ NewLineWhole(fname).Note1("Please only commit changes that %s would approve.", maintainer)
+ Explain2(
+ "See the pkgsrc guide, section \"Package components\",",
+ "keyword \"maintainer\", for more information.")
+ }
+ }
}
--- pkgsrc/pkgtools/pkglint/files/Attic/pkglint.go 2016/06/05 11:24:32 1.8
+++ pkgsrc/pkgtools/pkglint/files/Attic/pkglint.go 2016/07/07 12:09:27 1.9
--- pkgsrc/pkgtools/pkglint/files/Attic/package_test.go 2016/06/05 11:24:32 1.7
+++ pkgsrc/pkgtools/pkglint/files/Attic/package_test.go 2016/07/07 12:09:27 1.8
--- pkgsrc/pkgtools/pkglint/files/Attic/util.go 2016/06/05 11:24:32 1.7
+++ pkgsrc/pkgtools/pkglint/files/Attic/util.go 2016/07/07 12:09:27 1.8
--- pkgsrc/pkgtools/pkglint/files/Attic/util_test.go 2016/01/12 01:02:49 1.3
+++ pkgsrc/pkgtools/pkglint/files/Attic/util_test.go 2016/07/07 12:09:27 1.4
@@ -5,30 +5,30 @@
)
func (s *Suite) TestMkopSubst_middle(c *check.C) {
- c.Check(mkopSubst("pkgname", false, "kgna", false, "ri", false), equals, "prime")
- c.Check(mkopSubst("pkgname", false, "pkgname", false, "replacement", false), equals, "replacement")
+ c.Check(mkopSubst("pkgname", false, "kgna", false, "ri", ""), equals, "prime")
+ c.Check(mkopSubst("pkgname", false, "pkgname", false, "replacement", ""), equals, "replacement")
}
func (s *Suite) TestMkopSubst_left(c *check.C) {
- c.Check(mkopSubst("pkgname", true, "kgna", false, "ri", false), equals, "pkgname")
- c.Check(mkopSubst("pkgname", true, "pkgname", false, "replacement", false), equals, "replacement")
+ c.Check(mkopSubst("pkgname", true, "kgna", false, "ri", ""), equals, "pkgname")
+ c.Check(mkopSubst("pkgname", true, "pkgname", false, "replacement", ""), equals, "replacement")
}
func (s *Suite) TestMkopSubst_right(c *check.C) {
- c.Check(mkopSubst("pkgname", false, "kgna", true, "ri", false), equals, "pkgname")
- c.Check(mkopSubst("pkgname", false, "pkgname", true, "replacement", false), equals, "replacement")
+ c.Check(mkopSubst("pkgname", false, "kgna", true, "ri", ""), equals, "pkgname")
+ c.Check(mkopSubst("pkgname", false, "pkgname", true, "replacement", ""), equals, "replacement")
}
func (s *Suite) TestMkopSubst_leftRight(c *check.C) {
- c.Check(mkopSubst("pkgname", true, "kgna", true, "ri", false), equals, "pkgname")
- c.Check(mkopSubst("pkgname", false, "pkgname", false, "replacement", false), equals, "replacement")
+ c.Check(mkopSubst("pkgname", true, "kgna", true, "ri", ""), equals, "pkgname")
+ c.Check(mkopSubst("pkgname", false, "pkgname", false, "replacement", ""), equals, "replacement")
}
-func (s *Suite) TestMkopSubst_all(c *check.C) {
- c.Check(mkopSubst("aaaaa", false, "a", false, "b", true), equals, "bbbbb")
- c.Check(mkopSubst("aaaaa", true, "a", false, "b", true), equals, "baaaa")
- c.Check(mkopSubst("aaaaa", false, "a", true, "b", true), equals, "aaaab")
- c.Check(mkopSubst("aaaaa", true, "a", true, "b", true), equals, "aaaaa")
+func (s *Suite) TestMkopSubst_gflag(c *check.C) {
+ c.Check(mkopSubst("aaaaa", false, "a", false, "b", "g"), equals, "bbbbb")
+ c.Check(mkopSubst("aaaaa", true, "a", false, "b", "g"), equals, "baaaa")
+ c.Check(mkopSubst("aaaaa", false, "a", true, "b", "g"), equals, "aaaab")
+ c.Check(mkopSubst("aaaaa", true, "a", true, "b", "g"), equals, "aaaaa")
}
func (s *Suite) TestReplaceFirst(c *check.C) {
--- pkgsrc/pkgtools/pkglint/files/Attic/vartypecheck.go 2016/06/10 19:42:42 1.14
+++ pkgsrc/pkgtools/pkglint/files/Attic/vartypecheck.go 2016/07/07 12:09:27 1.15
--- pkgsrc/pkgtools/pkglint/files/Attic/vercmp_test.go 2016/01/12 01:02:49 1.2
+++ pkgsrc/pkgtools/pkglint/files/Attic/vercmp_test.go 2016/07/07 12:09:27 1.3
@@ -15,15 +15,50 @@
c.Check(newVersion("nb1"), check.DeepEquals, &version{nil, 1})
c.Check(newVersion("1.0.1a"), deepEquals, &version{[]int{1, 0, 0, 0, 1, 1}, 0})
c.Check(newVersion("1.0.1z"), deepEquals, &version{[]int{1, 0, 0, 0, 1, 26}, 0})
+ c.Check(newVersion("0pre20160620"), deepEquals, &version{[]int{0, -1, 20160620}, 0})
}
func (s *Suite) TestPkgverCmp(c *check.C) {
- c.Check(pkgverCmp("1.0", "1.0alpha"), equals, 1)
- c.Check(pkgverCmp("1.0alpha", "1.0"), equals, -1)
- c.Check(pkgverCmp("1.0nb1", "1.0"), equals, 1)
- c.Check(pkgverCmp("1.0nb2", "1.0nb1"), equals, 1)
- c.Check(pkgverCmp("2.0.1nb17", "2.0.1nb4"), equals, 1)
- c.Check(pkgverCmp("2.0.1nb4", "2.0.1nb17"), equals, -1)
- c.Check(pkgverCmp("2.0pre", "2.0rc"), equals, 0)
- c.Check(pkgverCmp("2.0pre", "2.0pl"), equals, -1)
+ var versions = [][]string{
+ {"0pre20160620"},
+ {"0"},
+ {"nb1"},
+ {"0.0.1-SNAPSHOT"},
+ {"1.0alpha"},
+ {"1.0alpha3"},
+ {"1", "1.0", "1.0.0"},
+ {"1.0nb1"},
+ {"1.0nb2"},
+ {"1.0.1a"},
+ {"1.0.1z"},
+ {"2.0pre", "2.0rc"},
+ {"2.0", "2.0pl"},
+ {"2.0.1nb4"},
+ {"2.0.1nb17"},
+ {"2.5beta"},
+ {"5.0"},
+ {"5.0nb5"},
+ {"5.5", "5.005"},
+ {"20151110"},
+ }
+
+ for i, iversions := range versions {
+ for _, iversion := range iversions {
+ for j, jversions := range versions {
+ for _, jversion := range jversions {
+ actual := pkgverCmp(iversion, jversion)
+ if i < j && !(actual < 0) {
+ c.Check([]interface{}{i, iversion, j, jversion, "<0"}, deepEquals, []interface{}{i, iversion, j, jversion, actual})
+ }
+ if i == j && !(actual == 0) {
+ c.Check([]interface{}{i, iversion, j, jversion, "==0"}, deepEquals, []interface{}{i, iversion, j, jversion, actual})
+ }
+ if i > j && !(actual > 0) {
+ c.Check([]interface{}{i, iversion, j, jversion, ">0"}, deepEquals, []interface{}{i, iversion, j, jversion, actual})
+ }
+ }
+ }
+
+ }
+ }
}