parser.go 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. package main
  2. import (
  3. "os"
  4. "fmt"
  5. "strings"
  6. "io"
  7. )
  8. type stringSet map[string]struct{}
  9. func (set stringSet) getAny() string {
  10. for v := range set {
  11. return v
  12. }
  13. //maybe should return error instrad
  14. return ""
  15. }
  16. func (set stringSet) toSlice() []string {
  17. slice := make([]string, 0, len(set))
  18. for v := range set {
  19. slice = append(slice, v)
  20. }
  21. return slice
  22. }
  23. func (set stringSet) removeAny() string {
  24. v := set.getAny()
  25. delete(set, v)
  26. return v
  27. }
  28. type arguments struct {
  29. op string
  30. options map[string]string
  31. globals map[string]string
  32. doubles stringSet //tracks args passed twice such as -yy and -dd
  33. targets stringSet
  34. }
  35. func makeArguments() *arguments {
  36. return &arguments {
  37. "",
  38. make(map[string]string),
  39. make(map[string]string),
  40. make(stringSet),
  41. make(stringSet),
  42. }
  43. }
  44. func (parser *arguments) copy() (cp *arguments) {
  45. cp = makeArguments()
  46. cp.op = parser.op
  47. for k,v := range parser.options {
  48. cp.options[k] = v
  49. }
  50. for k,v := range parser.globals {
  51. cp.globals[k] = v
  52. }
  53. for k,v := range parser.targets {
  54. cp.targets[k] = v
  55. }
  56. for k,v := range parser.doubles {
  57. cp.doubles[k] = v
  58. }
  59. return
  60. }
  61. func (parser *arguments) delArg(options ...string) {
  62. for _, option := range options {
  63. delete(parser.options, option)
  64. delete(parser.globals, option)
  65. delete(parser.doubles, option)
  66. }
  67. }
  68. func (parser *arguments) needRoot() bool {
  69. if parser.existsArg("h", "help") {
  70. return false
  71. }
  72. if parser.existsArg("p", "print") {
  73. return false
  74. }
  75. switch parser.op {
  76. case "V", "version":
  77. return false
  78. case "D", "database":
  79. return true
  80. case "F", "files":
  81. if parser.existsArg("y", "refresh") {
  82. return true
  83. }
  84. return false
  85. case "Q", "query":
  86. return false
  87. case "R", "remove":
  88. return true
  89. case "S", "sync":
  90. if parser.existsArg("y", "refresh") {
  91. return true
  92. }
  93. if parser.existsArg("u", "sysupgrade") {
  94. return true
  95. }
  96. if parser.existsArg("s", "search") {
  97. return false
  98. }
  99. if parser.existsArg("l", "list") {
  100. return false
  101. }
  102. if parser.existsArg("i", "info") {
  103. return false
  104. }
  105. return true
  106. case "T", "deptest":
  107. return false
  108. case "U", "upgrade":
  109. return true
  110. //yay specific
  111. case "Y", "yay":
  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. op := formatArg(parser.op)
  207. args = append(args, op)
  208. for option, arg := range parser.options {
  209. formatedOption := formatArg(option)
  210. args = append(args, formatedOption)
  211. if hasParam(option) {
  212. args = append(args, arg)
  213. }
  214. if parser.existsDouble(option) {
  215. args = append(args, formatedOption)
  216. }
  217. }
  218. return
  219. }
  220. func (parser *arguments) formatGlobals() (args []string) {
  221. for option, arg := range parser.globals {
  222. formatedOption := formatArg(option)
  223. args = append(args, formatedOption)
  224. if hasParam(option) {
  225. args = append(args, arg)
  226. }
  227. if parser.existsDouble(option) {
  228. args = append(args, formatedOption)
  229. }
  230. }
  231. return
  232. }
  233. func formatArg(arg string) string {
  234. if len(arg) > 1 {
  235. arg = "--" + arg
  236. } else {
  237. arg = "-" + arg
  238. }
  239. return arg
  240. }
  241. func isOp(op string) bool {
  242. switch op {
  243. case "V", "version":
  244. return true
  245. case "D", "database":
  246. return true
  247. case "F", "files":
  248. return true
  249. case "Q", "query":
  250. return true
  251. case "R", "remove":
  252. return true
  253. case "S", "sync":
  254. return true
  255. case "T", "deptest":
  256. return true
  257. case "U", "upgrade":
  258. return true
  259. //yay specific
  260. case "Y", "yay":
  261. return true
  262. case "G", "getpkgbuild":
  263. return true
  264. default:
  265. return false
  266. }
  267. }
  268. func isGlobal(op string) bool {
  269. switch op {
  270. case "b", "dbpath":
  271. return true
  272. case "r", "root":
  273. return true
  274. case "v", "verbose":
  275. return true
  276. case "arch":
  277. return true
  278. case "cachedir":
  279. return true
  280. case "color":
  281. return true
  282. case "config":
  283. return true
  284. case "debug":
  285. return true
  286. case "gpgdir":
  287. return true
  288. case "hookdir":
  289. return true
  290. case "logfile":
  291. return true
  292. case "noconfirm":
  293. return true
  294. case "confirm":
  295. return true
  296. default:
  297. return false
  298. }
  299. }
  300. func isYayParam(arg string) bool {
  301. switch arg {
  302. case "afterclean":
  303. return true
  304. case "noafterclean":
  305. return true
  306. case "devel":
  307. return true
  308. case "nodevel":
  309. return true
  310. case "timeupdate":
  311. return true
  312. case "notimeupdate":
  313. return true
  314. case "topdown":
  315. return true
  316. default:
  317. return false
  318. }
  319. }
  320. func hasParam(arg string) bool {
  321. switch arg {
  322. case "dbpath", "b":
  323. return true
  324. case "root", "r":
  325. return true
  326. case "sysroot":
  327. return true
  328. case "config":
  329. return true
  330. case "ignore":
  331. return true
  332. case "assume-installed":
  333. return true
  334. case "overwrite":
  335. return true
  336. case "ask":
  337. return true
  338. case "cachedir":
  339. return true
  340. case "hookdir":
  341. return true
  342. case "logfile":
  343. return true
  344. case "ignoregroup":
  345. return true
  346. case "arch":
  347. return true
  348. case "print-format":
  349. return true
  350. case "gpgdir":
  351. return true
  352. case "color":
  353. return true
  354. default:
  355. return false
  356. }
  357. }
  358. //parses short hand options such as:
  359. //-Syu -b/some/path -
  360. func (parser *arguments) parseShortOption(arg string, param string) (usedNext bool, err error) {
  361. if arg == "-" {
  362. err = parser.addArg("-")
  363. return
  364. }
  365. arg = arg[1:]
  366. for k, _char := range arg {
  367. char := string(_char)
  368. if hasParam(char) {
  369. if k < len(arg) - 2 {
  370. err = parser.addParam(char, arg[k+2:])
  371. } else {
  372. usedNext = true
  373. err = parser.addParam(char, param)
  374. }
  375. break
  376. } else {
  377. err = parser.addArg(char)
  378. if err != nil {
  379. return
  380. }
  381. }
  382. }
  383. return
  384. }
  385. //parses full length options such as:
  386. //--sync --refresh --sysupgrade --dbpath /some/path --
  387. func (parser *arguments) parseLongOption(arg string, param string) (usedNext bool, err error){
  388. if arg == "--" {
  389. err = parser.addArg(arg)
  390. return
  391. }
  392. arg = arg[2:]
  393. if hasParam(arg) {
  394. err = parser.addParam(arg, param)
  395. usedNext = true
  396. } else {
  397. err = parser.addArg(arg)
  398. }
  399. return
  400. }
  401. func (parser *arguments) parseStdin() (err error) {
  402. for true {
  403. var target string
  404. _, err = fmt.Scan(&target)
  405. if err != nil {
  406. if err == io.EOF {
  407. err = nil
  408. }
  409. return
  410. }
  411. parser.addTarget(target)
  412. }
  413. return
  414. }
  415. func (parser *arguments)parseCommandLine() (err error) {
  416. args := os.Args[1:]
  417. usedNext := false
  418. if len(args) < 1 {
  419. err = fmt.Errorf("no operation specified (use -h for help)")
  420. return
  421. }
  422. for k, arg := range args {
  423. var nextArg string
  424. if usedNext {
  425. usedNext = false
  426. continue
  427. }
  428. if k + 1 < len(args) {
  429. nextArg = args[k + 1]
  430. }
  431. if parser.existsArg("--") {
  432. parser.addTarget(arg)
  433. } else if strings.HasPrefix(arg, "--") {
  434. usedNext, err = parser.parseLongOption(arg, nextArg)
  435. } else if strings.HasPrefix(arg, "-") {
  436. usedNext, err = parser.parseShortOption(arg, nextArg)
  437. } else {
  438. parser.addTarget(arg)
  439. }
  440. if err != nil {
  441. return
  442. }
  443. }
  444. if parser.op == "" {
  445. parser.op = "Y"
  446. }
  447. if cmdArgs.existsArg("-") {
  448. err = cmdArgs.parseStdin();
  449. if err != nil {
  450. return
  451. }
  452. }
  453. return
  454. }