parser.go 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. package main
  2. import (
  3. "fmt"
  4. "io"
  5. "os"
  6. "strings"
  7. )
  8. type stringSet map[string]struct{}
  9. func (set stringSet) set(v string) {
  10. set[v] = struct{}{}
  11. }
  12. func (set stringSet) get(v string) bool {
  13. _, exists := set[v]
  14. return exists
  15. }
  16. func (set stringSet) remove(v string) {
  17. delete(set, v)
  18. }
  19. func (set stringSet) toSlice() []string {
  20. slice := make([]string, 0, len(set))
  21. for v := range set {
  22. slice = append(slice, v)
  23. }
  24. return slice
  25. }
  26. type arguments struct {
  27. op string
  28. options map[string]string
  29. globals map[string]string
  30. doubles stringSet //tracks args passed twice such as -yy and -dd
  31. targets stringSet
  32. }
  33. func makeArguments() *arguments {
  34. return &arguments{
  35. "",
  36. make(map[string]string),
  37. make(map[string]string),
  38. make(stringSet),
  39. make(stringSet),
  40. }
  41. }
  42. func (parser *arguments) copy() (cp *arguments) {
  43. cp = makeArguments()
  44. cp.op = parser.op
  45. for k, v := range parser.options {
  46. cp.options[k] = v
  47. }
  48. for k, v := range parser.globals {
  49. cp.globals[k] = v
  50. }
  51. for k, v := range parser.targets {
  52. cp.targets[k] = v
  53. }
  54. for k, v := range parser.doubles {
  55. cp.doubles[k] = v
  56. }
  57. return
  58. }
  59. func (parser *arguments) delArg(options ...string) {
  60. for _, option := range options {
  61. delete(parser.options, option)
  62. delete(parser.globals, option)
  63. delete(parser.doubles, option)
  64. }
  65. }
  66. func (parser *arguments) needRoot() bool {
  67. if parser.existsArg("h", "help") {
  68. return false
  69. }
  70. if parser.existsArg("p", "print") {
  71. return false
  72. }
  73. switch parser.op {
  74. case "V", "version":
  75. return false
  76. case "D", "database":
  77. return true
  78. case "F", "files":
  79. if parser.existsArg("y", "refresh") {
  80. return true
  81. }
  82. return false
  83. case "Q", "query":
  84. return false
  85. case "R", "remove":
  86. return true
  87. case "S", "sync":
  88. if parser.existsArg("y", "refresh") {
  89. return true
  90. }
  91. if parser.existsArg("u", "sysupgrade") {
  92. return true
  93. }
  94. if parser.existsArg("s", "search") {
  95. return false
  96. }
  97. if parser.existsArg("l", "list") {
  98. return false
  99. }
  100. if parser.existsArg("i", "info") {
  101. return false
  102. }
  103. return true
  104. case "T", "deptest":
  105. return false
  106. case "U", "upgrade":
  107. return true
  108. //yay specific
  109. case "Y", "yay":
  110. return false
  111. case "P", "print":
  112. return false
  113. case "G", "getpkgbuild":
  114. return false
  115. default:
  116. return false
  117. }
  118. }
  119. func (parser *arguments) addOP(op string) (err error) {
  120. if parser.op != "" {
  121. err = fmt.Errorf("only one operation may be used at a time")
  122. return
  123. }
  124. parser.op = op
  125. return
  126. }
  127. func (parser *arguments) addParam(option string, arg string) (err error) {
  128. if isOp(option) {
  129. err = parser.addOP(option)
  130. return
  131. }
  132. if parser.existsArg(option) {
  133. parser.doubles[option] = struct{}{}
  134. } else if isGlobal(option) {
  135. parser.globals[option] = arg
  136. } else {
  137. parser.options[option] = arg
  138. }
  139. return
  140. }
  141. func (parser *arguments) addArg(options ...string) (err error) {
  142. for _, option := range options {
  143. err = parser.addParam(option, "")
  144. if err != nil {
  145. return
  146. }
  147. }
  148. return
  149. }
  150. //multiple args acts as an OR operator
  151. func (parser *arguments) existsArg(options ...string) bool {
  152. for _, option := range options {
  153. _, exists := parser.options[option]
  154. if exists {
  155. return true
  156. }
  157. _, exists = parser.globals[option]
  158. if exists {
  159. return true
  160. }
  161. }
  162. return false
  163. }
  164. func (parser *arguments) getArg(options ...string) (arg string, double bool, exists bool) {
  165. for _, option := range options {
  166. arg, exists = parser.options[option]
  167. if exists {
  168. _, double = parser.doubles[option]
  169. return
  170. }
  171. arg, exists = parser.globals[option]
  172. if exists {
  173. _, double = parser.doubles[option]
  174. return
  175. }
  176. }
  177. return
  178. }
  179. func (parser *arguments) addTarget(targets ...string) {
  180. for _, target := range targets {
  181. parser.targets[target] = struct{}{}
  182. }
  183. }
  184. func (parser *arguments) delTarget(targets ...string) {
  185. for _, target := range targets {
  186. delete(parser.targets, target)
  187. }
  188. }
  189. //multiple args acts as an OR operator
  190. func (parser *arguments) existsDouble(options ...string) bool {
  191. for _, option := range options {
  192. _, exists := parser.doubles[option]
  193. if exists {
  194. return true
  195. }
  196. }
  197. return false
  198. }
  199. func (parser *arguments) formatTargets() (args []string) {
  200. for target := range parser.targets {
  201. args = append(args, target)
  202. }
  203. return
  204. }
  205. func (parser *arguments) formatArgs() (args []string) {
  206. var op string
  207. if parser.op != "" {
  208. op = formatArg(parser.op)
  209. }
  210. args = append(args, op)
  211. for option, arg := range parser.options {
  212. if option == "--" {
  213. continue
  214. }
  215. formatedOption := formatArg(option)
  216. args = append(args, formatedOption)
  217. if hasParam(option) {
  218. args = append(args, arg)
  219. }
  220. if parser.existsDouble(option) {
  221. args = append(args, formatedOption)
  222. }
  223. }
  224. return
  225. }
  226. func (parser *arguments) formatGlobals() (args []string) {
  227. for option, arg := range parser.globals {
  228. formatedOption := formatArg(option)
  229. args = append(args, formatedOption)
  230. if hasParam(option) {
  231. args = append(args, arg)
  232. }
  233. if parser.existsDouble(option) {
  234. args = append(args, formatedOption)
  235. }
  236. }
  237. return
  238. }
  239. func formatArg(arg string) string {
  240. if len(arg) > 1 {
  241. arg = "--" + arg
  242. } else {
  243. arg = "-" + arg
  244. }
  245. return arg
  246. }
  247. func isOp(op string) bool {
  248. switch op {
  249. case "V", "version":
  250. return true
  251. case "D", "database":
  252. return true
  253. case "F", "files":
  254. return true
  255. case "Q", "query":
  256. return true
  257. case "R", "remove":
  258. return true
  259. case "S", "sync":
  260. return true
  261. case "T", "deptest":
  262. return true
  263. case "U", "upgrade":
  264. return true
  265. //yay specific
  266. case "Y", "yay":
  267. return true
  268. case "P", "print":
  269. return true
  270. case "G", "getpkgbuild":
  271. return true
  272. default:
  273. return false
  274. }
  275. }
  276. func isGlobal(op string) bool {
  277. switch op {
  278. case "b", "dbpath":
  279. return true
  280. case "r", "root":
  281. return true
  282. case "v", "verbose":
  283. return true
  284. case "arch":
  285. return true
  286. case "cachedir":
  287. return true
  288. case "color":
  289. return true
  290. case "config":
  291. return true
  292. case "debug":
  293. return true
  294. case "gpgdir":
  295. return true
  296. case "hookdir":
  297. return true
  298. case "logfile":
  299. return true
  300. case "noconfirm":
  301. return true
  302. case "confirm":
  303. return true
  304. default:
  305. return false
  306. }
  307. }
  308. func hasParam(arg string) bool {
  309. switch arg {
  310. case "dbpath", "b":
  311. return true
  312. case "root", "r":
  313. return true
  314. case "sysroot":
  315. return true
  316. case "config":
  317. return true
  318. case "ignore":
  319. return true
  320. case "assume-installed":
  321. return true
  322. case "overwrite":
  323. return true
  324. case "ask":
  325. return true
  326. case "cachedir":
  327. return true
  328. case "hookdir":
  329. return true
  330. case "logfile":
  331. return true
  332. case "ignoregroup":
  333. return true
  334. case "arch":
  335. return true
  336. case "print-format":
  337. return true
  338. case "gpgdir":
  339. return true
  340. case "color":
  341. return true
  342. default:
  343. return false
  344. }
  345. }
  346. //parses short hand options such as:
  347. //-Syu -b/some/path -
  348. func (parser *arguments) parseShortOption(arg string, param string) (usedNext bool, err error) {
  349. if arg == "-" {
  350. err = parser.addArg("-")
  351. return
  352. }
  353. arg = arg[1:]
  354. for k, _char := range arg {
  355. char := string(_char)
  356. if hasParam(char) {
  357. if k < len(arg)-2 {
  358. err = parser.addParam(char, arg[k+2:])
  359. } else {
  360. usedNext = true
  361. err = parser.addParam(char, param)
  362. }
  363. break
  364. } else {
  365. err = parser.addArg(char)
  366. if err != nil {
  367. return
  368. }
  369. }
  370. }
  371. return
  372. }
  373. //parses full length options such as:
  374. //--sync --refresh --sysupgrade --dbpath /some/path --
  375. func (parser *arguments) parseLongOption(arg string, param string) (usedNext bool, err error) {
  376. if arg == "--" {
  377. err = parser.addArg(arg)
  378. return
  379. }
  380. arg = arg[2:]
  381. if hasParam(arg) {
  382. err = parser.addParam(arg, param)
  383. usedNext = true
  384. } else {
  385. err = parser.addArg(arg)
  386. }
  387. return
  388. }
  389. func (parser *arguments) parseStdin() (err error) {
  390. for {
  391. var target string
  392. _, err = fmt.Scan(&target)
  393. if err != nil {
  394. if err == io.EOF {
  395. err = nil
  396. }
  397. return
  398. }
  399. parser.addTarget(target)
  400. }
  401. return
  402. }
  403. func (parser *arguments) parseCommandLine() (err error) {
  404. args := os.Args[1:]
  405. usedNext := false
  406. if len(args) < 1 {
  407. err = fmt.Errorf("no operation specified (use -h for help)")
  408. return
  409. }
  410. for k, arg := range args {
  411. var nextArg string
  412. if usedNext {
  413. usedNext = false
  414. continue
  415. }
  416. if k+1 < len(args) {
  417. nextArg = args[k+1]
  418. }
  419. if parser.existsArg("--") {
  420. parser.addTarget(arg)
  421. } else if strings.HasPrefix(arg, "--") {
  422. usedNext, err = parser.parseLongOption(arg, nextArg)
  423. } else if strings.HasPrefix(arg, "-") {
  424. usedNext, err = parser.parseShortOption(arg, nextArg)
  425. } else {
  426. parser.addTarget(arg)
  427. }
  428. if err != nil {
  429. return
  430. }
  431. }
  432. if parser.op == "" {
  433. parser.op = "Y"
  434. }
  435. if cmdArgs.existsArg("-") {
  436. err = cmdArgs.parseStdin()
  437. if err != nil {
  438. return
  439. }
  440. }
  441. return
  442. }