pacman.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. package pacman
  2. import (
  3. "fmt"
  4. "os"
  5. "os/exec"
  6. "strings"
  7. "github.com/jguer/go-alpm"
  8. "github.com/jguer/yay/util"
  9. )
  10. // RepoSearch describes a Repository search.
  11. type RepoSearch []Result
  12. // Result describes a pkg.
  13. type Result struct {
  14. Name string
  15. Repository string
  16. Version string
  17. Description string
  18. Group string
  19. Installed bool
  20. }
  21. // PacmanConf describes the default pacman config file
  22. const PacmanConf string = "/etc/pacman.conf"
  23. // Determines NumberMenu and Search Order
  24. const (
  25. DownTop = iota
  26. TopDown
  27. )
  28. var conf alpm.PacmanConfig
  29. func init() {
  30. conf, _ = readConfig(PacmanConf)
  31. }
  32. func readConfig(pacmanconf string) (conf alpm.PacmanConfig, err error) {
  33. file, err := os.Open(pacmanconf)
  34. if err != nil {
  35. return
  36. }
  37. conf, err = alpm.ParseConfig(file)
  38. if err != nil {
  39. return
  40. }
  41. return
  42. }
  43. // UpdatePackages handles cache update and upgrade
  44. func UpdatePackages(flags []string) error {
  45. var cmd *exec.Cmd
  46. var args []string
  47. args = append(args, "pacman", "-Syu")
  48. args = append(args, flags...)
  49. cmd = exec.Command("sudo", args...)
  50. cmd.Stdout = os.Stdout
  51. cmd.Stdin = os.Stdin
  52. cmd.Stderr = os.Stderr
  53. err := cmd.Run()
  54. return err
  55. }
  56. // Search handles repo searches. Creates a RepoSearch struct.
  57. func Search(pkgName string) (s RepoSearch, n int, err error) {
  58. h, err := conf.CreateHandle()
  59. defer h.Release()
  60. if err != nil {
  61. }
  62. localDb, err := h.LocalDb()
  63. if err != nil {
  64. return
  65. }
  66. dbList, err := h.SyncDbs()
  67. if err != nil {
  68. return
  69. }
  70. var installed bool
  71. dbS := dbList.Slice()
  72. var f int
  73. if util.SortMode == DownTop {
  74. f = len(dbS) - 1
  75. } else {
  76. f = 0
  77. }
  78. for {
  79. pkgS := dbS[f].PkgCache().Slice()
  80. var i int
  81. if util.SortMode == DownTop {
  82. i = len(pkgS) - 1
  83. } else {
  84. i = 0
  85. }
  86. for {
  87. if strings.Contains(pkgS[i].Name(), pkgName) {
  88. if r, _ := localDb.PkgByName(pkgS[i].Name()); r != nil {
  89. installed = true
  90. } else {
  91. installed = false
  92. }
  93. s = append(s, Result{
  94. Name: pkgS[i].Name(),
  95. Description: pkgS[i].Description(),
  96. Version: pkgS[i].Version(),
  97. Repository: dbS[f].Name(),
  98. Group: strings.Join(pkgS[i].Groups().Slice(), ","),
  99. Installed: installed,
  100. })
  101. n++
  102. }
  103. if util.SortMode == DownTop {
  104. if i > 0 {
  105. i--
  106. } else {
  107. break
  108. }
  109. } else {
  110. if i < len(pkgS)-1 {
  111. i++
  112. } else {
  113. break
  114. }
  115. }
  116. }
  117. if util.SortMode == DownTop {
  118. if f > 0 {
  119. f--
  120. } else {
  121. break
  122. }
  123. } else {
  124. if f < len(dbS)-1 {
  125. f++
  126. } else {
  127. break
  128. }
  129. }
  130. }
  131. return
  132. }
  133. //PrintSearch receives a RepoSearch type and outputs pretty text.
  134. func (s RepoSearch) PrintSearch() {
  135. for i, res := range s {
  136. var toprint string
  137. if util.SearchVerbosity == util.NumberMenu {
  138. if util.SortMode == util.BottomUp {
  139. toprint += fmt.Sprintf("%d ", len(s)-i-1)
  140. } else {
  141. toprint += fmt.Sprintf("%d ", i)
  142. }
  143. } else if util.SearchVerbosity == util.Minimal {
  144. fmt.Println(res.Name)
  145. continue
  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. found := false
  188. for _, db := range dbList.Slice() {
  189. _, err = db.PkgByName(pkg)
  190. if err == nil {
  191. found = true
  192. repo = append(repo, pkg)
  193. break
  194. }
  195. }
  196. if !found {
  197. if _, err := dbList.PkgCachebyGroup(pkg); err == nil {
  198. repo = append(repo, pkg)
  199. } else {
  200. aur = append(aur, pkg)
  201. }
  202. }
  203. }
  204. err = nil
  205. return
  206. }
  207. // BuildDependencies finds packages, on the second run
  208. // compares with a baselist and avoids searching those
  209. func BuildDependencies(baselist []string) func(toCheck []string, isBaseList bool, last bool) (repo []string, notFound []string) {
  210. h, _ := conf.CreateHandle()
  211. localDb, _ := h.LocalDb()
  212. dbList, _ := h.SyncDbs()
  213. f := func(c rune) bool {
  214. return c == '>' || c == '<' || c == '=' || c == ' '
  215. }
  216. return func(toCheck []string, isBaseList bool, close bool) (repo []string, notFound []string) {
  217. if close {
  218. h.Release()
  219. return
  220. }
  221. Loop:
  222. for _, dep := range toCheck {
  223. if !isBaseList {
  224. for _, base := range baselist {
  225. if base == dep {
  226. continue Loop
  227. }
  228. }
  229. }
  230. if _, erp := localDb.PkgCache().FindSatisfier(dep); erp == nil {
  231. continue
  232. } else if pkg, erp := dbList.FindSatisfier(dep); erp == nil {
  233. repo = append(repo, pkg.Name())
  234. } else {
  235. field := strings.FieldsFunc(dep, f)
  236. notFound = append(notFound, field[0])
  237. }
  238. }
  239. return
  240. }
  241. }
  242. // DepSatisfier receives a string slice, returns a slice of packages found in
  243. // repos and one of packages not found in repos. Leaves out installed packages.
  244. func DepSatisfier(toCheck []string) (repo []string, notFound []string, err error) {
  245. h, err := conf.CreateHandle()
  246. defer h.Release()
  247. if err != nil {
  248. return
  249. }
  250. localDb, err := h.LocalDb()
  251. if err != nil {
  252. return
  253. }
  254. dbList, err := h.SyncDbs()
  255. if err != nil {
  256. return
  257. }
  258. f := func(c rune) bool {
  259. return c == '>' || c == '<' || c == '=' || c == ' '
  260. }
  261. for _, dep := range toCheck {
  262. if _, erp := localDb.PkgCache().FindSatisfier(dep); erp == nil {
  263. continue
  264. } else if pkg, erp := dbList.FindSatisfier(dep); erp == nil {
  265. repo = append(repo, pkg.Name())
  266. } else {
  267. field := strings.FieldsFunc(dep, f)
  268. notFound = append(notFound, field[0])
  269. }
  270. }
  271. err = nil
  272. return
  273. }
  274. // Install sends an install command to pacman with the pkgName slice
  275. func Install(pkgName []string, flags []string) (err error) {
  276. if len(pkgName) == 0 {
  277. return nil
  278. }
  279. var cmd *exec.Cmd
  280. var args []string
  281. args = append(args, "pacman", "-S")
  282. args = append(args, pkgName...)
  283. if len(flags) != 0 {
  284. args = append(args, flags...)
  285. }
  286. cmd = exec.Command("sudo", args...)
  287. cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
  288. cmd.Run()
  289. return nil
  290. }
  291. // CleanRemove sends a full removal command to pacman with the pkgName slice
  292. func CleanRemove(pkgName []string) (err error) {
  293. if len(pkgName) == 0 {
  294. return nil
  295. }
  296. var cmd *exec.Cmd
  297. var args []string
  298. args = append(args, "pacman", "-Rnsc")
  299. args = append(args, pkgName...)
  300. args = append(args, "--noconfirm")
  301. cmd = exec.Command("sudo", args...)
  302. cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
  303. cmd.Run()
  304. return nil
  305. }
  306. // ForeignPackages returns a map of foreign packages, with their version and date as values.
  307. func ForeignPackages() (foreign map[string]*struct {
  308. Version string
  309. Date int64
  310. }, n int, err error) {
  311. h, err := conf.CreateHandle()
  312. defer h.Release()
  313. if err != nil {
  314. return
  315. }
  316. localDb, err := h.LocalDb()
  317. if err != nil {
  318. return
  319. }
  320. dbList, err := h.SyncDbs()
  321. if err != nil {
  322. return
  323. }
  324. foreign = make(map[string]*struct {
  325. Version string
  326. Date int64
  327. })
  328. // Find foreign packages in system
  329. for _, pkg := range localDb.PkgCache().Slice() {
  330. // Change to more effective method
  331. found := false
  332. for _, db := range dbList.Slice() {
  333. _, err = db.PkgByName(pkg.Name())
  334. if err == nil {
  335. found = true
  336. break
  337. }
  338. }
  339. if !found {
  340. foreign[pkg.Name()] = &struct {
  341. Version string
  342. Date int64
  343. }{pkg.Version(), pkg.InstallDate().Unix()}
  344. n++
  345. }
  346. }
  347. return
  348. }
  349. // Statistics returns statistics about packages installed in system
  350. func Statistics() (info struct {
  351. Totaln int
  352. Expln int
  353. TotalSize int64
  354. }, err error) {
  355. var tS int64 // TotalSize
  356. var nPkg int
  357. var ePkg int
  358. h, err := conf.CreateHandle()
  359. defer h.Release()
  360. if err != nil {
  361. return
  362. }
  363. localDb, err := h.LocalDb()
  364. if err != nil {
  365. return
  366. }
  367. for _, pkg := range localDb.PkgCache().Slice() {
  368. tS += pkg.ISize()
  369. nPkg++
  370. if pkg.Reason() == 0 {
  371. ePkg++
  372. }
  373. }
  374. info = struct {
  375. Totaln int
  376. Expln int
  377. TotalSize int64
  378. }{
  379. nPkg, ePkg, tS,
  380. }
  381. return
  382. }
  383. // BiggestPackages prints the name of the ten biggest packages in the system.
  384. func BiggestPackages() {
  385. h, err := conf.CreateHandle()
  386. defer h.Release()
  387. if err != nil {
  388. return
  389. }
  390. localDb, err := h.LocalDb()
  391. if err != nil {
  392. return
  393. }
  394. pkgCache := localDb.PkgCache()
  395. pkgS := pkgCache.SortBySize().Slice()
  396. if len(pkgS) < 10 {
  397. return
  398. }
  399. for i := 0; i < 10; i++ {
  400. fmt.Printf("%s: \x1B[0;33m%dMB\x1B[0m\n", pkgS[i].Name(), pkgS[i].ISize()/(1024*1024))
  401. }
  402. // Could implement size here as well, but we just want the general idea
  403. }
  404. // HangingPackages returns a list of packages installed as deps
  405. // and unneeded by the system
  406. func HangingPackages() (hanging []string, err error) {
  407. h, err := conf.CreateHandle()
  408. defer h.Release()
  409. if err != nil {
  410. return
  411. }
  412. localDb, err := h.LocalDb()
  413. if err != nil {
  414. return
  415. }
  416. f := func(pkg alpm.Package) error {
  417. if pkg.Reason() != alpm.PkgReasonDepend {
  418. return nil
  419. }
  420. requiredby := pkg.ComputeRequiredBy()
  421. if len(requiredby) == 0 {
  422. hanging = append(hanging, pkg.Name())
  423. fmt.Printf("%s: \x1B[0;33m%dMB\x1B[0m\n", pkg.Name(), pkg.ISize()/(1024*1024))
  424. }
  425. return nil
  426. }
  427. err = localDb.PkgCache().ForEach(f)
  428. return
  429. }
  430. // SliceHangingPackages returns a list of packages installed as deps
  431. // and unneeded by the system from a provided list of package names.
  432. func SliceHangingPackages(pkgS []string) (hanging []string) {
  433. h, err := conf.CreateHandle()
  434. defer h.Release()
  435. if err != nil {
  436. return
  437. }
  438. localDb, err := h.LocalDb()
  439. if err != nil {
  440. return
  441. }
  442. big:
  443. for _, pkgName := range pkgS {
  444. for _, hangN := range hanging {
  445. if hangN == pkgName {
  446. continue big
  447. }
  448. }
  449. pkg, err := localDb.PkgByName(pkgName)
  450. if err == nil {
  451. if pkg.Reason() != alpm.PkgReasonDepend {
  452. continue
  453. }
  454. requiredby := pkg.ComputeRequiredBy()
  455. if len(requiredby) == 0 {
  456. hanging = append(hanging, pkgName)
  457. fmt.Printf("%s: \x1B[0;33m%dMB\x1B[0m\n", pkg.Name(), pkg.ISize()/(1024*1024))
  458. }
  459. }
  460. }
  461. return
  462. }