getopt.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. /*
  2. Copyright 2019 Drew DeVault <sir@cmpwn.com>
  3. Redistribution and use in source and binary forms, with or without
  4. modification, are permitted provided that the following conditions are met:
  5. 1. Redistributions of source code must retain the above copyright notice, this
  6. list of conditions and the following disclaimer.
  7. 2. Redistributions in binary form must reproduce the above copyright notice,
  8. this list of conditions and the following disclaimer in the documentation
  9. and/or other materials provided with the distribution.
  10. 3. Neither the name of the copyright holder nor the names of its contributors
  11. may be used to endorse or promote products derived from this software without
  12. specific prior written permission.
  13. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  14. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  15. WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  16. DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  17. FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  18. DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  19. SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  20. CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  21. OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  22. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  23. */
  24. package util
  25. import (
  26. "fmt"
  27. "os"
  28. )
  29. // In the case of "-o example", Option is 'o' and "example" is Value. For
  30. // options which do not take an argument, Value is "".
  31. type Option struct {
  32. Option rune
  33. Value string
  34. }
  35. // This is returned when an unknown option is found in argv, but not in the
  36. // option spec.
  37. type UnknownOptionError rune
  38. func (e UnknownOptionError) Error() string {
  39. return fmt.Sprintf("%s: unknown option -%c", os.Args[0], rune(e))
  40. }
  41. // This is returned when an option with a mandatory argument is missing that
  42. // argument.
  43. type MissingOptionError rune
  44. func (e MissingOptionError) Error() string {
  45. return fmt.Sprintf("%s: expected argument for -%c", os.Args[0], rune(e))
  46. }
  47. // Getopts implements a POSIX-compatible options interface.
  48. //
  49. // Returns a slice of options and the index of the first non-option argument.
  50. //
  51. // If an error is returned, you must print it to stderr to be POSIX complaint.
  52. func Getopts(argv []string, spec string) ([]Option, int, error) {
  53. optmap := make(map[rune]bool)
  54. runes := []rune(spec)
  55. for i, rn := range spec {
  56. if rn == ':' {
  57. if i == 0 {
  58. continue
  59. }
  60. optmap[runes[i-1]] = true
  61. } else {
  62. optmap[rn] = false
  63. }
  64. }
  65. var (
  66. i int
  67. opts []Option
  68. )
  69. for i = 1; i < len(argv); i++ {
  70. arg := argv[i]
  71. runes = []rune(arg)
  72. if len(arg) == 0 || arg == "-" {
  73. break
  74. }
  75. if arg[0] != '-' {
  76. break
  77. }
  78. if arg == "--" {
  79. i++
  80. break
  81. }
  82. for j, opt := range runes[1:] {
  83. if optopt, ok := optmap[opt]; !ok {
  84. opts = append(opts, Option{'?', ""})
  85. return opts, i, UnknownOptionError(opt)
  86. } else if optopt {
  87. if j+1 < len(runes)-1 {
  88. opts = append(opts, Option{opt, string(runes[j+2:])})
  89. break
  90. } else {
  91. if i+1 >= len(argv) {
  92. if len(spec) >= 1 && spec[0] == ':' {
  93. opts = append(opts, Option{':', string(opt)})
  94. } else {
  95. return opts, i, MissingOptionError(opt)
  96. }
  97. } else {
  98. opts = append(opts, Option{opt, argv[i+1]})
  99. i++
  100. }
  101. }
  102. } else {
  103. opts = append(opts, Option{opt, ""})
  104. }
  105. }
  106. }
  107. return opts, i, nil
  108. }