pacman.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. package pacman
  2. import (
  3. "fmt"
  4. "os"
  5. "os/exec"
  6. "strings"
  7. "github.com/jguer/go-alpm"
  8. )
  9. // RepoSearch describes a Repository search.
  10. type RepoSearch []Result
  11. // Result describes a pkg.
  12. type Result struct {
  13. Name string
  14. Repository string
  15. Version string
  16. Description string
  17. Group string
  18. Installed bool
  19. }
  20. // PacmanConf describes the default pacman config file
  21. const PacmanConf string = "/etc/pacman.conf"
  22. // NoConfirm ignores prompts.
  23. var NoConfirm = false
  24. // SortMode NumberMenu and Search
  25. var SortMode = DownTop
  26. // Determines NumberMenu and Search Order
  27. const (
  28. DownTop = iota
  29. TopDown
  30. )
  31. var conf alpm.PacmanConfig
  32. func init() {
  33. conf, _ = readConfig(PacmanConf)
  34. }
  35. func readConfig(pacmanconf string) (conf alpm.PacmanConfig, err error) {
  36. file, err := os.Open(pacmanconf)
  37. if err != nil {
  38. return
  39. }
  40. conf, err = alpm.ParseConfig(file)
  41. if err != nil {
  42. return
  43. }
  44. return
  45. }
  46. // UpdatePackages handles cache update and upgrade
  47. func UpdatePackages(flags []string) error {
  48. var cmd *exec.Cmd
  49. var args []string
  50. args = append(args, "pacman", "-Syu")
  51. args = append(args, flags...)
  52. cmd = exec.Command("sudo", args...)
  53. cmd.Stdout = os.Stdout
  54. cmd.Stdin = os.Stdin
  55. cmd.Stderr = os.Stderr
  56. err := cmd.Run()
  57. return err
  58. }
  59. // Search handles repo searches. Creates a RepoSearch struct.
  60. func Search(pkgName string) (s RepoSearch, n int, err error) {
  61. h, err := conf.CreateHandle()
  62. defer h.Release()
  63. if err != nil {
  64. }
  65. localDb, err := h.LocalDb()
  66. if err != nil {
  67. return
  68. }
  69. dbList, err := h.SyncDbs()
  70. if err != nil {
  71. return
  72. }
  73. var installed bool
  74. dbS := dbList.Slice()
  75. var f int
  76. if SortMode == DownTop {
  77. f = len(dbS) - 1
  78. } else {
  79. f = 0
  80. }
  81. for {
  82. pkgS := dbS[f].PkgCache().Slice()
  83. var i int
  84. if SortMode == DownTop {
  85. i = len(pkgS) - 1
  86. } else {
  87. i = 0
  88. }
  89. for {
  90. if strings.Contains(pkgS[i].Name(), pkgName) {
  91. if r, _ := localDb.PkgByName(pkgS[i].Name()); r != nil {
  92. installed = true
  93. } else {
  94. installed = false
  95. }
  96. s = append(s, Result{
  97. Name: pkgS[i].Name(),
  98. Description: pkgS[i].Description(),
  99. Version: pkgS[i].Version(),
  100. Repository: dbS[f].Name(),
  101. Group: strings.Join(pkgS[i].Groups().Slice(), ","),
  102. Installed: installed,
  103. })
  104. n++
  105. }
  106. if SortMode == DownTop {
  107. if i > 0 {
  108. i--
  109. } else {
  110. break
  111. }
  112. } else {
  113. if i < len(pkgS)-1 {
  114. i++
  115. } else {
  116. break
  117. }
  118. }
  119. }
  120. if SortMode == DownTop {
  121. if f > 0 {
  122. f--
  123. } else {
  124. break
  125. }
  126. } else {
  127. if f < len(dbS)-1 {
  128. f++
  129. } else {
  130. break
  131. }
  132. }
  133. }
  134. return
  135. }
  136. //PrintSearch receives a RepoSearch type and outputs pretty text.
  137. func (s RepoSearch) PrintSearch(mode int) {
  138. for i, res := range s {
  139. var toprint string
  140. if mode != -1 {
  141. if mode == 0 {
  142. toprint += fmt.Sprintf("%d ", len(s)-i-1)
  143. } else {
  144. toprint += fmt.Sprintf("%d ", i)
  145. }
  146. }
  147. toprint += fmt.Sprintf("\x1b[1m%s/\x1b[33m%s \x1b[36m%s \x1b[0m",
  148. res.Repository, res.Name, res.Version)
  149. if len(res.Group) != 0 {
  150. toprint += fmt.Sprintf("(%s) ", res.Group)
  151. }
  152. if res.Installed == true {
  153. toprint += fmt.Sprintf("\x1b[32;40mInstalled\x1b[0m")
  154. }
  155. toprint += "\n" + res.Description
  156. fmt.Println(toprint)
  157. }
  158. }
  159. // PFactory execute an action over a series of packages without reopening the handle everytime.
  160. // Everybody told me it wouln't work. It does. It's just not pretty.
  161. // When it worked: https://youtu.be/a4Z5BdEL0Ag?t=1m11s
  162. func PFactory(action func(interface{})) func(name string, object interface{}, rel bool) {
  163. h, _ := conf.CreateHandle()
  164. localDb, _ := h.LocalDb()
  165. return func(name string, object interface{}, rel bool) {
  166. _, err := localDb.PkgByName(name)
  167. if err == nil {
  168. action(object)
  169. }
  170. if rel {
  171. h.Release()
  172. }
  173. }
  174. }
  175. // PackageSlices separates an input slice into aur and repo slices
  176. func PackageSlices(toCheck []string) (aur []string, repo []string, err error) {
  177. h, err := conf.CreateHandle()
  178. defer h.Release()
  179. if err != nil {
  180. return
  181. }
  182. dbList, err := h.SyncDbs()
  183. if err != nil {
  184. return
  185. }
  186. for _, pkg := range toCheck {
  187. // Check if dep is installed
  188. found := false
  189. for _, db := range dbList.Slice() {
  190. _, err = db.PkgByName(pkg)
  191. if err == nil {
  192. found = true
  193. repo = append(repo, pkg)
  194. break
  195. }
  196. }
  197. if !found {
  198. aur = append(aur, pkg)
  199. }
  200. }
  201. return
  202. }
  203. // OutofRepo returns a list of packages not installed and not resolvable
  204. // Accepts inputs like 'gtk2', 'java-environment=8', 'linux >= 4.20'
  205. func OutofRepo(toCheck []string) (aur []string, repo []string, err error) {
  206. h, err := conf.CreateHandle()
  207. defer h.Release()
  208. if err != nil {
  209. return
  210. }
  211. localDb, err := h.LocalDb()
  212. if err != nil {
  213. return
  214. }
  215. dbList, err := h.SyncDbs()
  216. if err != nil {
  217. return
  218. }
  219. f := func(c rune) bool {
  220. return c == '>' || c == '<' || c == '=' || c == ' '
  221. }
  222. toCheckLoop:
  223. for _, dep := range toCheck {
  224. field := strings.FieldsFunc(dep, f)
  225. for _, checkR := range repo {
  226. if field[0] == checkR {
  227. continue toCheckLoop
  228. }
  229. }
  230. for _, checkA := range aur {
  231. if field[0] == checkA {
  232. continue toCheckLoop
  233. }
  234. }
  235. // Check if dep is installed
  236. _, err = localDb.PkgByName(field[0])
  237. if err == nil {
  238. continue
  239. }
  240. found := false
  241. Loop:
  242. for _, db := range dbList.Slice() {
  243. // First, Check if they're provided by package name.
  244. _, err = db.PkgByName(field[0])
  245. if err == nil {
  246. found = true
  247. repo = append(repo, field[0])
  248. break Loop
  249. }
  250. for _, pkg := range db.PkgCache().Slice() {
  251. for _, p := range pkg.Provides().Slice() {
  252. if p.String() == dep {
  253. found = true
  254. repo = append(repo, pkg.Name())
  255. break Loop
  256. }
  257. }
  258. }
  259. }
  260. if !found {
  261. aur = append(aur, field[0])
  262. }
  263. }
  264. err = nil
  265. return
  266. }
  267. // Install sends an install command to pacman with the pkgName slice
  268. func Install(pkgName []string, flags []string) (err error) {
  269. if len(pkgName) == 0 {
  270. return nil
  271. }
  272. var cmd *exec.Cmd
  273. var args []string
  274. args = append(args, "pacman", "-S")
  275. args = append(args, pkgName...)
  276. if len(flags) != 0 {
  277. args = append(args, flags...)
  278. }
  279. cmd = exec.Command("sudo", args...)
  280. cmd.Stdout = os.Stdout
  281. cmd.Stdin = os.Stdin
  282. cmd.Stderr = os.Stderr
  283. cmd.Run()
  284. return nil
  285. }
  286. // CleanRemove sends a full removal command to pacman with the pkgName slice
  287. func CleanRemove(pkgName []string) (err error) {
  288. if len(pkgName) == 0 {
  289. return nil
  290. }
  291. var cmd *exec.Cmd
  292. var args []string
  293. args = append(args, "pacman", "-Rnsc")
  294. args = append(args, pkgName...)
  295. cmd = exec.Command("sudo", args...)
  296. cmd.Stdout = os.Stdout
  297. cmd.Stdin = os.Stdin
  298. cmd.Stderr = os.Stderr
  299. cmd.Run()
  300. return nil
  301. }
  302. // ForeignPackages returns a map of foreign packages, with their version and date as values.
  303. func ForeignPackages() (foreign map[string]*struct {
  304. Version string
  305. Date int64
  306. }, n int, err error) {
  307. h, err := conf.CreateHandle()
  308. defer h.Release()
  309. if err != nil {
  310. return
  311. }
  312. localDb, err := h.LocalDb()
  313. if err != nil {
  314. return
  315. }
  316. dbList, err := h.SyncDbs()
  317. if err != nil {
  318. return
  319. }
  320. foreign = make(map[string]*struct {
  321. Version string
  322. Date int64
  323. })
  324. // Find foreign packages in system
  325. for _, pkg := range localDb.PkgCache().Slice() {
  326. // Change to more effective method
  327. found := false
  328. for _, db := range dbList.Slice() {
  329. _, err = db.PkgByName(pkg.Name())
  330. if err == nil {
  331. found = true
  332. break
  333. }
  334. }
  335. if !found {
  336. foreign[pkg.Name()] = &struct {
  337. Version string
  338. Date int64
  339. }{pkg.Version(), pkg.InstallDate().Unix()}
  340. n++
  341. }
  342. }
  343. return
  344. }
  345. // Statistics returns statistics about packages installed in system
  346. func Statistics() (packages map[string]int64, info struct {
  347. Totaln int
  348. Expln int
  349. TotalSize int64
  350. }, err error) {
  351. var pkgs [10]alpm.Package
  352. var tS int64 // TotalSize
  353. var nPkg int
  354. var ePkg int
  355. h, err := conf.CreateHandle()
  356. defer h.Release()
  357. if err != nil {
  358. return
  359. }
  360. localDb, err := h.LocalDb()
  361. if err != nil {
  362. return
  363. }
  364. var k int
  365. for e, pkg := range localDb.PkgCache().Slice() {
  366. tS += pkg.ISize()
  367. k = -1
  368. nPkg++
  369. if pkg.Reason() == 0 {
  370. ePkg++
  371. }
  372. if e < 10 {
  373. pkgs[e] = pkg
  374. continue
  375. }
  376. for i, pkw := range pkgs {
  377. if k == -1 {
  378. if pkw.ISize() < pkg.ISize() {
  379. k = i
  380. }
  381. } else {
  382. if pkw.ISize() < pkgs[k].ISize() && pkw.ISize() < pkg.ISize() {
  383. k = i
  384. }
  385. }
  386. }
  387. if k != -1 {
  388. pkgs[k] = pkg
  389. }
  390. }
  391. packages = make(map[string]int64)
  392. for _, pkg := range pkgs {
  393. packages[pkg.Name()] = pkg.ISize()
  394. }
  395. info = struct {
  396. Totaln int
  397. Expln int
  398. TotalSize int64
  399. }{
  400. nPkg, ePkg, tS,
  401. }
  402. return
  403. }