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

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

Diff for /pkgsrc/pkgtools/pkglint/files/Attic/shell.go between version 1.6 and 1.7

version 1.6, 2015/12/05 21:00:42 version 1.7, 2015/12/05 22:42:01
Line 5  package main
Line 5  package main
 import (  import (
         "path"          "path"
         "strings"          "strings"
         "unicode"  
 )  )
   
 const (  const (
Line 81  func NewMkShellLine(line *Line) *MkShell
Line 80  func NewMkShellLine(line *Line) *MkShell
         return &MkShellLine{line}          return &MkShellLine{line}
 }  }
   
   type ShellwordState string
   
   const (
           swstPlain      ShellwordState = "plain"
           swstSquot      ShellwordState = "squot"
           swstDquot      ShellwordState = "dquot"
           swstDquotBackt ShellwordState = "dquot+backt"
           swstBackt      ShellwordState = "backt"
   )
   
 func (msline *MkShellLine) checkShellword(shellword string, checkQuoting bool) {  func (msline *MkShellLine) checkShellword(shellword string, checkQuoting bool) {
         defer tracecall("MkShellLine.checklineMkShellword", shellword, checkQuoting)()          defer tracecall("MkShellLine.checklineMkShellword", shellword, checkQuoting)()
   
Line 104  func (msline *MkShellLine) checkShellwor
Line 113  func (msline *MkShellLine) checkShellwor
                 line.warnf("Please use the RCD_SCRIPTS mechanism to install rc.d scripts automatically to ${RCD_SCRIPTS_EXAMPLEDIR}.")                  line.warnf("Please use the RCD_SCRIPTS mechanism to install rc.d scripts automatically to ${RCD_SCRIPTS_EXAMPLEDIR}.")
         }          }
   
         type ShellwordState string          repl := NewPrefixReplacer(shellword)
         const (  
                 swstPlain      ShellwordState = "plain"  
                 swstSquot      ShellwordState = "squot"  
                 swstDquot      ShellwordState = "dquot"  
                 swstDquotBackt ShellwordState = "dquot+backt"  
                 swstBackt      ShellwordState = "backt"  
         )  
   
         rest := shellword  
         state := swstPlain          state := swstPlain
 outer:  outer:
         for rest != "" {          for repl.rest != "" {
                 _ = G.opts.DebugShell && line.debugf("shell state %s: %q", state, rest)                  _ = G.opts.DebugShell && line.debugf("shell state %s: %q", state, repl.rest)
   
                 var m []string  
                 switch {                  switch {
                 // When parsing inside backticks, it is more                  // When parsing inside backticks, it is more
                 // reasonable to check the whole shell command                  // reasonable to check the whole shell command
                 // recursively, instead of splitting off the first                  // recursively, instead of splitting off the first
                 // make(1) variable.                  // make(1) variable.
                 case state == swstBackt || state == swstDquotBackt:                  case state == swstBackt || state == swstDquotBackt:
                         // Scan for the end of the backticks, checking                          var backtCommand string
                         // for single backslashes and removing one level                          backtCommand, state = msline.unescapeBackticks(shellword, repl, state)
                         // of backslashes. Backslashes are only removed                          msline.checkShelltext(backtCommand)
                         // before a dollar, a backslash or a backtick.  
                         //  
                         // References:  
                         // * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_03  
                         stripped := ""  
                         for rest != "" {  
                                 switch {  
                                 case replacePrefix(&rest, &m, "^`"):  
                                         if state == swstBackt {  
                                                 state = swstPlain  
                                         } else {  
                                                 state = swstDquot  
                                         }  
                                         goto endOfBackticks  
   
                                 case replacePrefix(&rest, &m, "^\\\\([\\\\`$])"):  
                                         stripped += m[1]  
   
                                 case replacePrefix(&rest, &m, `^(\\)`):  
                                         line.warnf("Backslashes should be doubled inside backticks.")  
                                         stripped += m[1]  
   
                                 case state == swstDquotBackt && replacePrefix(&rest, &m, `^"`):  
                                         line.warnf("Double quotes inside backticks inside double quotes are error prone.")  
                                         line.explain(  
                                                 "According to the SUSv3, they produce undefined results.",  
                                                 "",  
                                                 "See the paragraph starting \"Within the backquoted ...\" in",  
                                                 "http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html")  
   
                                 case replacePrefix(&rest, &m, "^([^\\\\`]+)"):  
                                         stripped += m[1]  
   
                                 default:  
                                         line.errorf("Internal pkglint error: checklineMkShellword shellword=%q rest=%q", shellword, rest)  
                                 }  
                         }  
                         line.errorf("Unfinished backquotes: rest=%q", rest)  
   
                 endOfBackticks:  
                         msline.checkShelltext(stripped)  
   
                 // Make(1) variables have the same syntax, no matter in which state we are currently.                  // Make(1) variables have the same syntax, no matter in which state we are currently.
                 case replacePrefix(&rest, &m, `^\$\{(`+reVarnameDirect+`|@)(:[^\{]+)?\}`),                  case repl.startsWith(`^\$\{(` + reVarnameDirect + `|@)(:[^\{]+)?\}`),
                         replacePrefix(&rest, &m, `^\$\((`+reVarnameDirect+`|@])(:[^\)]+)?\)`),                          repl.startsWith(`^\$\((` + reVarnameDirect + `|@])(:[^\)]+)?\)`),
                         replacePrefix(&rest, &m, `^\$([\w@])()`):                          repl.startsWith(`^\$([\w@])()`):
                         varname, mod := m[1], m[2]                          varname, mod := repl.m[1], repl.m[2]
   
                         if varname == "@" {                          if varname == "@" {
                                 line.warnf("Please use \"${.TARGET}\" instead of \"$@\".")                                  line.warnf("Please use \"${.TARGET}\" instead of \"$@\".")
Line 218  outer:
Line 176  outer:
                 // The syntax of the variable modifiers can get quite                  // The syntax of the variable modifiers can get quite
                 // hairy. In lack of motivation, we just skip anything                  // hairy. In lack of motivation, we just skip anything
                 // complicated, hoping that at least the braces are balanced.                  // complicated, hoping that at least the braces are balanced.
                 case replacePrefix(&rest, &m, `^\$\{`):                  case repl.startsWith(`^\$\{`):
                         braces := 1                          braces := 1
                 skip:                  skip:
                         for rest != "" && braces > 0 {                          for repl.rest != "" && braces > 0 {
                                 switch {                                  switch {
                                 case replacePrefix(&rest, &m, `^\}`):                                  case repl.startsWith(`^\}`):
                                         braces--                                          braces--
                                 case replacePrefix(&rest, &m, `^\{`):                                  case repl.startsWith(`^\{`):
                                         braces++                                          braces++
                                 case replacePrefix(&rest, &m, `^[^{}]+`):                                  case repl.startsWith(`^[^{}]+`):
                                 // skip                                  // skip
                                 default:                                  default:
                                         break skip                                          break skip
Line 236  outer:
Line 194  outer:
   
                 case state == swstPlain:                  case state == swstPlain:
                         switch {                          switch {
                         case replacePrefix(&rest, &m, `^[!#\%&\(\)*+,\-.\/0-9:;<=>?@A-Z\[\]^_a-z{|}~]+`),                          case repl.startsWith(`^[!#\%&\(\)*+,\-.\/0-9:;<=>?@A-Z\[\]^_a-z{|}~]+`),
                                 replacePrefix(&rest, &m, `^\\(?:[ !"#'\(\)*;?\\^{|}]|\$\$)`):                                  repl.startsWith(`^\\(?:[ !"#'\(\)*;?\\^{|}]|\$\$)`):
                         case replacePrefix(&rest, &m, `^'`):                          case repl.startsWith(`^'`):
                                 state = swstSquot                                  state = swstSquot
                         case replacePrefix(&rest, &m, `^"`):                          case repl.startsWith(`^"`):
                                 state = swstDquot                                  state = swstDquot
                         case replacePrefix(&rest, &m, "^`"):                          case repl.startsWith("^`"):
                                 state = swstBackt                                  state = swstBackt
                         case replacePrefix(&rest, &m, `^\$\$([0-9A-Z_a-z]+|\#)`),                          case repl.startsWith(`^\$\$([0-9A-Z_a-z]+|\#)`),
                                 replacePrefix(&rest, &m, `^\$\$\{([0-9A-Z_a-z]+|\#)\}`),                                  repl.startsWith(`^\$\$\{([0-9A-Z_a-z]+|\#)\}`),
                                 replacePrefix(&rest, &m, `^\$\$(\$)\$`):                                  repl.startsWith(`^\$\$(\$)\$`):
                                 shvarname := m[1]                                  shvarname := repl.m[1]
                                 if G.opts.WarnQuoting && checkQuoting && msline.variableNeedsQuoting(shvarname) {                                  if G.opts.WarnQuoting && checkQuoting && msline.variableNeedsQuoting(shvarname) {
                                         line.warnf("Unquoted shell variable %q.", shvarname)                                          line.warnf("Unquoted shell variable %q.", shvarname)
                                         line.explain(                                          line.explain(
Line 264  outer:
Line 222  outer:
                                                 "\tcp \"$fname\" /tmp",                                                  "\tcp \"$fname\" /tmp",
                                                 "\t# copies one file, as intended")                                                  "\t# copies one file, as intended")
                                 }                                  }
                         case replacePrefix(&rest, &m, `^\$@`):                          case repl.startsWith(`^\$@`):
                                 line.warnf("Please use %q instead of %q.", "${.TARGET}", "$@")                                  line.warnf("Please use %q instead of %q.", "${.TARGET}", "$@")
                                 line.explain(                                  line.explain(
                                         "It is more readable and prevents confusion with the shell variable of",                                          "It is more readable and prevents confusion with the shell variable of",
                                         "the same name.")                                          "the same name.")
   
                         case replacePrefix(&rest, &m, `^\$\$@`):                          case repl.startsWith(`^\$\$@`):
                                 line.warnf("The $@ shell variable should only be used in double quotes.")                                  line.warnf("The $@ shell variable should only be used in double quotes.")
   
                         case replacePrefix(&rest, &m, `^\$\$\?`):                          case repl.startsWith(`^\$\$\?`):
                                 line.warnf("The $? shell variable is often not available in \"set -e\" mode.")                                  line.warnf("The $? shell variable is often not available in \"set -e\" mode.")
   
                         case replacePrefix(&rest, &m, `^\$\$\(`):                          case repl.startsWith(`^\$\$\(`):
                                 line.warnf("Invoking subshells via $(...) is not portable enough.")                                  line.warnf("Invoking subshells via $(...) is not portable enough.")
                                 line.explain(                                  line.explain(
                                         "The Solaris /bin/sh does not know this way to execute a command in a",                                          "The Solaris /bin/sh does not know this way to execute a command in a",
Line 288  outer:
Line 246  outer:
   
                 case state == swstSquot:                  case state == swstSquot:
                         switch {                          switch {
                         case replacePrefix(&rest, &m, `^'`):                          case repl.startsWith(`^'`):
                                 state = swstPlain                                  state = swstPlain
                         case replacePrefix(&rest, &m, `^[^\$\']+`):                          case repl.startsWith(`^[^\$\']+`):
                                 // just skip                                  // just skip
                         case replacePrefix(&rest, &m, `^\$\$`):                          case repl.startsWith(`^\$\$`):
                                 // just skip                                  // just skip
                         default:                          default:
                                 break outer                                  break outer
Line 300  outer:
Line 258  outer:
   
                 case state == swstDquot:                  case state == swstDquot:
                         switch {                          switch {
                         case replacePrefix(&rest, &m, `^"`):                          case repl.startsWith(`^"`):
                                 state = swstPlain                                  state = swstPlain
                         case replacePrefix(&rest, &m, "^`"):                          case repl.startsWith("^`"):
                                 state = swstDquotBackt                                  state = swstDquotBackt
                         case replacePrefix(&rest, &m, "^[^$\"\\\\`]+"):                          case repl.startsWith("^[^$\"\\\\`]+"):
                                 // just skip                                  // just skip
                         case replacePrefix(&rest, &m, "^\\\\(?:[\\\\\"`]|\\$\\$)"):                          case repl.startsWith("^\\\\(?:[\\\\\"`]|\\$\\$)"):
                                 // just skip                                  // just skip
                         case replacePrefix(&rest, &m, `^\$\$\{([0-9A-Za-z_]+)\}`),                          case repl.startsWith(`^\$\$\{([0-9A-Za-z_]+)\}`),
                                 replacePrefix(&rest, &m, `^\$\$([0-9A-Z_a-z]+|[!#?@]|\$\$)`):                                  repl.startsWith(`^\$\$([0-9A-Z_a-z]+|[!#?@]|\$\$)`):
                                 shvarname := m[1]                                  shvarname := repl.m[1]
                                 _ = G.opts.DebugShell && line.debugf("checklineMkShellword: found double-quoted variable %q.", shvarname)                                  _ = G.opts.DebugShell && line.debugf("checklineMkShellword: found double-quoted variable %q.", shvarname)
                         case replacePrefix(&rest, &m, `^\$\$`):                          case repl.startsWith(`^\$\$`):
                                 line.warnf("Unquoted $ or strange shell variable found.")                                  line.warnf("Unquoted $ or strange shell variable found.")
                         case replacePrefix(&rest, &m, `^\\(.)`):                          case repl.startsWith(`^\\(.)`):
                                 char := m[1]                                  char := repl.m[1]
                                 line.warnf("Please use \"%s\" instead of \"%s\".", "\\\\"+char, "\\"+char)                                  line.warnf("Please use \"%s\" instead of \"%s\".", "\\\\"+char, "\\"+char)
                                 line.explain(                                  line.explain(
                                         "Although the current code may work, it is not good style to rely on",                                          "Although the current code may work, it is not good style to rely on",
Line 328  outer:
Line 286  outer:
                 }                  }
         }          }
   
         if strings.TrimSpace(rest) != "" {          if strings.TrimSpace(repl.rest) != "" {
                 line.errorf("Internal pkglint error: checklineMkShellword state=%s, rest=%q, shellword=%q", state, rest, shellword)                  line.errorf("Internal pkglint error: checklineMkShellword state=%s, rest=%q, shellword=%q", state, repl.rest, shellword)
           }
   }
   
   // Scan for the end of the backticks, checking for single backslashes
   // and removing one level of backslashes. Backslashes are only removed
   // before a dollar, a backslash or a backtick.
   //
   // See http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_03
   func (msline *MkShellLine) unescapeBackticks(shellword string, repl *PrefixReplacer, state ShellwordState) (unescaped string, newState ShellwordState) {
           line := msline.line
           for repl.rest != "" {
                   switch {
                   case repl.startsWith("^`"):
                           if state == swstBackt {
                                   state = swstPlain
                           } else {
                                   state = swstDquot
                           }
                           return unescaped, state
   
                   case repl.startsWith("^\\\\([\\\\`$])"):
                           unescaped += repl.m[1]
   
                   case repl.startsWith(`^(\\)`):
                           line.warnf("Backslashes should be doubled inside backticks.")
                           unescaped += repl.m[1]
   
                   case state == swstDquotBackt && repl.startsWith(`^"`):
                           line.warnf("Double quotes inside backticks inside double quotes are error prone.")
                           line.explain(
                                   "According to the SUSv3, they produce undefined results.",
                                   "",
                                   "See the paragraph starting \"Within the backquoted ...\" in",
                                   "http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html")
   
                   case repl.startsWith("^([^\\\\`]+)"):
                           unescaped += repl.m[1]
   
                   default:
                           line.errorf("Internal pkglint error: checklineMkShellword shellword=%q rest=%q", shellword, repl.rest)
                   }
         }          }
           line.errorf("Unfinished backquotes: rest=%q", repl.rest)
           return unescaped, state
 }  }
   
 func (msline *MkShellLine) variableNeedsQuoting(shvarname string) bool {  func (msline *MkShellLine) variableNeedsQuoting(shvarname string) bool {
Line 372  func (msline *MkShellLine) checkShelltex
Line 373  func (msline *MkShellLine) checkShelltex
                 line.notef("You don't need to use \"-\" before %q.", cmd)                  line.notef("You don't need to use \"-\" before %q.", cmd)
         }          }
   
         rest := shelltext  
   
         setE := false          setE := false
         var m []string          repl := NewPrefixReplacer(shelltext)
         if replacePrefix(&rest, &m, `^\s*([-@]*)(\$\{_PKG_SILENT\}\$\{_PKG_DEBUG\}|\$\{RUN\}|)`) {          if repl.startsWith(`^\s*([-@]*)(\$\{_PKG_SILENT\}\$\{_PKG_DEBUG\}|\$\{RUN\}|)`) {
                 hidden, macro := m[1], m[2]                  hidden, macro := repl.m[1], repl.m[2]
                 msline.checkLineStart(hidden, macro, rest, &setE)                  msline.checkLineStart(hidden, macro, repl.rest, &setE)
         }          }
   
         state := scstStart          state := scstStart
         for replacePrefix(&rest, &m, reShellword) {          for repl.startsWith(reShellword) {
                 shellword := m[1]                  shellword := repl.m[1]
   
                 _ = G.opts.DebugShell && line.debugf("checklineMkShelltext state=%v shellword=%q", state, shellword)                  _ = G.opts.DebugShell && line.debugf("checklineMkShelltext state=%v shellword=%q", state, shellword)
   
Line 416  func (msline *MkShellLine) checkShelltex
Line 415  func (msline *MkShellLine) checkShelltex
                 state = nextState(line, state, shellword)                  state = nextState(line, state, shellword)
         }          }
   
         if !matches(rest, `^\s*$`) {          repl.startsWith(`^\s+`)
                 line.errorf("Internal pkglint error: checklineMkShelltext state=%s rest=%q shellword=%q", state, rest, shelltext)          if repl.rest != "" {
                   line.errorf("Internal pkglint error: checklineMkShelltext state=%s rest=%q shellword=%q", state, repl.rest, shelltext)
         }          }
   
 }  }
Line 862  func nextState(line *Line, state scState
Line 862  func nextState(line *Line, state scState
 func splitIntoShellwords(line *Line, text string) ([]string, string) {  func splitIntoShellwords(line *Line, text string) ([]string, string) {
         var words []string          var words []string
   
         rest := text          repl := NewPrefixReplacer(text)
         var m []string          for repl.startsWith(reShellword) {
         for replacePrefix(&rest, &m, reShellword) {                  words = append(words, repl.m[1])
                 words = append(words, m[1])  
         }          }
           repl.startsWith(`^\s+`)
         rest = strings.TrimLeftFunc(rest, unicode.IsSpace)          return words, repl.rest
         return words, rest  
 }  }

Legend:
Removed from v.1.6  
changed lines
  Added in v.1.7

CVSweb <webmaster@jp.NetBSD.org>