split_command.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. package parser
  2. import (
  3. "strings"
  4. "unicode"
  5. )
  6. // splitCommand takes a single line of text and parses out the cmd and args,
  7. // which are used for dispatching to more exact parsing functions.
  8. func splitCommand(line string) (string, []string, string, error) {
  9. var args string
  10. var flags []string
  11. // Make sure we get the same results irrespective of leading/trailing spaces
  12. cmdline := tokenWhitespace.Split(strings.TrimSpace(line), 2)
  13. cmd := strings.ToLower(cmdline[0])
  14. if len(cmdline) == 2 {
  15. var err error
  16. args, flags, err = extractBuilderFlags(cmdline[1])
  17. if err != nil {
  18. return "", nil, "", err
  19. }
  20. }
  21. return cmd, flags, strings.TrimSpace(args), nil
  22. }
  23. func extractBuilderFlags(line string) (string, []string, error) {
  24. // Parses the BuilderFlags and returns the remaining part of the line
  25. const (
  26. inSpaces = iota // looking for start of a word
  27. inWord
  28. inQuote
  29. )
  30. words := []string{}
  31. phase := inSpaces
  32. word := ""
  33. quote := '\000'
  34. blankOK := false
  35. var ch rune
  36. for pos := 0; pos <= len(line); pos++ {
  37. if pos != len(line) {
  38. ch = rune(line[pos])
  39. }
  40. if phase == inSpaces { // Looking for start of word
  41. if pos == len(line) { // end of input
  42. break
  43. }
  44. if unicode.IsSpace(ch) { // skip spaces
  45. continue
  46. }
  47. // Only keep going if the next word starts with --
  48. if ch != '-' || pos+1 == len(line) || rune(line[pos+1]) != '-' {
  49. return line[pos:], words, nil
  50. }
  51. phase = inWord // found something with "--", fall through
  52. }
  53. if (phase == inWord || phase == inQuote) && (pos == len(line)) {
  54. if word != "--" && (blankOK || len(word) > 0) {
  55. words = append(words, word)
  56. }
  57. break
  58. }
  59. if phase == inWord {
  60. if unicode.IsSpace(ch) {
  61. phase = inSpaces
  62. if word == "--" {
  63. return line[pos:], words, nil
  64. }
  65. if blankOK || len(word) > 0 {
  66. words = append(words, word)
  67. }
  68. word = ""
  69. blankOK = false
  70. continue
  71. }
  72. if ch == '\'' || ch == '"' {
  73. quote = ch
  74. blankOK = true
  75. phase = inQuote
  76. continue
  77. }
  78. if ch == '\\' {
  79. if pos+1 == len(line) {
  80. continue // just skip \ at end
  81. }
  82. pos++
  83. ch = rune(line[pos])
  84. }
  85. word += string(ch)
  86. continue
  87. }
  88. if phase == inQuote {
  89. if ch == quote {
  90. phase = inWord
  91. continue
  92. }
  93. if ch == '\\' {
  94. if pos+1 == len(line) {
  95. phase = inWord
  96. continue // just skip \ at end
  97. }
  98. pos++
  99. ch = rune(line[pos])
  100. }
  101. word += string(ch)
  102. }
  103. }
  104. return "", words, nil
  105. }