├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .github ├── dependabot.yml └── workflows │ ├── builder-image.yml │ ├── test-git.yml │ └── test.yml ├── .gitignore ├── .golangci.yml ├── .vscode └── settings.json ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── alpm.go ├── alpm_test.go ├── callbacks.c ├── callbacks.go ├── callbacks.h ├── callbacks_test.go ├── config_test.go ├── db.go ├── db_test.go ├── deps.go ├── deps_test.go ├── enums.go ├── error.go ├── go.mod ├── go.sum ├── handle.go ├── interfaces.go ├── package.go ├── package_test.go ├── sync.go ├── testdata ├── README.md └── examples │ ├── Makefile │ ├── README.md │ ├── depends │ └── depends.go │ ├── installed │ └── installed.go │ ├── search │ └── search.go │ ├── sync │ └── sync.go │ └── updates │ └── updates.go ├── trans.go ├── types.go ├── types_test.go └── version.go /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the jguer/yay-builder image as a parent image with archlinux 2 | FROM docker.io/jguer/yay-builder 3 | 4 | # Install extra packages (pacman-contrib and fish) 5 | RUN sudo pacman -Syu --noconfirm pacman-contrib fish git-delta openssh bat go 6 | 7 | # Set passwordless sudo for the docker user 8 | RUN echo "docker ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/docker 9 | 10 | # Add /app/bin to the PATH 11 | ENV PATH="/app/bin:$PATH" 12 | 13 | # Create a non-root user and switch to it 14 | USER docker 15 | 16 | # Set the working directory 17 | WORKDIR /workspace 18 | 19 | # Command to run when starting the container 20 | CMD ["bash"] -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Existing Dockerfile", 3 | "build": { 4 | "context": "..", 5 | "dockerfile": "../.devcontainer/Dockerfile" 6 | }, 7 | "customizations": { 8 | "vscode": { 9 | "extensions": [ 10 | "golang.go" 11 | ] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "gomod" # See documentation for possible values 4 | directory: "/" # Location of package manifests 5 | schedule: 6 | interval: "weekly" 7 | groups: 8 | go-all: 9 | patterns: 10 | - '*' 11 | -------------------------------------------------------------------------------- /.github/workflows/builder-image.yml: -------------------------------------------------------------------------------- 1 | name: Builder image 2 | 3 | on: 4 | schedule: 5 | - cron: "0 3 * * 1" 6 | push: 7 | paths: 8 | - "Dockerfile" 9 | - "**/builder-image.yml" 10 | jobs: 11 | build: 12 | name: Push builder image to GitHub Packages 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Check out the repo 16 | uses: actions/checkout@v2 17 | - name: Push to GitHub Packages 18 | uses: docker/build-push-action@v1 19 | with: 20 | username: ${{ secrets.DOCKER_USERNAME }} 21 | password: ${{ secrets.DOCKER_PASSWORD }} 22 | repository: jguer/goalpm-builder 23 | tags: latest 24 | -------------------------------------------------------------------------------- /.github/workflows/test-git.yml: -------------------------------------------------------------------------------- 1 | name: Test pacman-git 2 | # This workflow is triggered on pushes to the repository. 3 | on: 4 | push: 5 | branches-ignore: 6 | - pacman* 7 | paths-ignore: 8 | - "examples/**" 9 | - "Dockerfile" 10 | - "**/builder-image.yml" 11 | pull_request: 12 | 13 | jobs: 14 | build: 15 | name: Build and test go-alpm 16 | runs-on: ubuntu-latest 17 | container: 18 | image: jguer/yay-builder:latest 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v2 22 | - name: Build and install pacman-git 23 | run: | 24 | useradd github 25 | pacman -Syu --noconfirm --overwrite=* --needed sudo base-devel git 26 | echo "github ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers 27 | git clone https://aur.archlinux.org/pacman-git 28 | chown -R github pacman-git 29 | su github -c "cd pacman-git; yes | makepkg -si --nocheck" 30 | - name: Run Build and Tests with pacman-git 31 | run: make test 32 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Build Lint Test (BLT) 2 | # This workflow is triggered on pushes to the repository. 3 | on: 4 | push: 5 | branches-ignore: 6 | - pacman* 7 | paths-ignore: 8 | - "examples/**" 9 | - "Dockerfile" 10 | - "**/builder-image.yml" 11 | pull_request: 12 | 13 | jobs: 14 | build: 15 | name: Build and test go-alpm 16 | runs-on: ubuntu-latest 17 | container: 18 | image: jguer/yay-builder:latest 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v2 22 | - name: Lint 23 | run: /app/bin/golangci-lint run ./... 24 | - name: Run Build and Tests 25 | run: make test -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | .vscode 10 | 11 | *.cgo1.go 12 | *.cgo2.c 13 | _cgo_defun.c 14 | _cgo_gotypes.go 15 | _cgo_export.* 16 | 17 | *.exe 18 | *.test 19 | *.prof 20 | *.tar.gz 21 | qemu-* 22 | .go 23 | 24 | # Locale 25 | *.mo 26 | *.pot 27 | *.po~ 28 | *.pprof 29 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | run: 3 | go: "1.22" 4 | linters: 5 | default: none 6 | enable: 7 | - bodyclose 8 | - copyloopvar 9 | - dogsled 10 | - dupl 11 | - errcheck 12 | - errorlint 13 | - forbidigo 14 | - gochecknoinits 15 | - goprintffuncname 16 | - gosec 17 | - govet 18 | - ineffassign 19 | - lll 20 | - misspell 21 | - nakedret 22 | - noctx 23 | - nolintlint 24 | - staticcheck 25 | - unconvert 26 | - unparam 27 | - unused 28 | - whitespace 29 | - revive 30 | settings: 31 | dupl: 32 | threshold: 100 33 | funlen: 34 | lines: 100 35 | statements: 50 36 | goconst: 37 | min-len: 3 38 | min-occurrences: 4 39 | gocritic: 40 | enabled-tags: 41 | - diagnostic 42 | - experimental 43 | - opinionated 44 | - performance 45 | - style 46 | gocyclo: 47 | min-complexity: 15 48 | lll: 49 | line-length: 140 50 | misspell: 51 | locale: US 52 | nolintlint: 53 | require-explanation: false 54 | require-specific: false 55 | allow-unused: false 56 | exclusions: 57 | generated: lax 58 | presets: 59 | - comments 60 | - common-false-positives 61 | - legacy 62 | - std-error-handling 63 | rules: 64 | - linters: 65 | - dupl 66 | - errcheck 67 | - errorlint 68 | - gochecknoinits 69 | - gocritic 70 | - godot 71 | - govet 72 | - lll 73 | - revive 74 | - staticcheck 75 | - wsl 76 | path: (.+)_test.go 77 | - path: (.+)\.go$ 78 | text: G204 79 | paths: 80 | - third_party$ 81 | - builtin$ 82 | - examples$ 83 | formatters: 84 | enable: 85 | - gofmt 86 | - goimports 87 | settings: 88 | goimports: 89 | local-prefixes: 90 | - github.com/Jguer/go-alpm/v2 91 | exclusions: 92 | generated: lax 93 | paths: 94 | - third_party$ 95 | - builtin$ 96 | - examples$ 97 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "go.lintTool": "golangci-lint", 3 | "gopls": { 4 | "formatting.gofumpt": true, 5 | "formatting.local": "github.com/Jguer/go-alpm/v2" 6 | } 7 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM lopsided/archlinux:devel 2 | LABEL maintainer="Jguer,joaogg3 at google mail" 3 | 4 | ENV GO111MODULE=on 5 | WORKDIR /app 6 | 7 | COPY go.mod . 8 | 9 | RUN pacman -Syu --overwrite=* --needed --noconfirm go && \ 10 | curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v2.1.5 && \ 11 | go mod download && \ 12 | rm -rfv /var/cache/pacman/* /var/lib/pacman/sync/* 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2013 The go-alpm Authors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | export GO111MODULE=on 2 | 3 | GO ?= go 4 | 5 | SOURCES ?= $(shell find . -name "*.go") 6 | GOFLAGS += $(shell pacman -T 'pacman-git' > /dev/null && echo "-tags next") 7 | 8 | .PHONY: test 9 | test: 10 | @test -z "$$(gofmt -l *.go)" || (echo "Files need to be linted. Use make fmt" && false) 11 | $(GO) test $(GOFLAGS) -v ./... 12 | 13 | .PHONY: fmt 14 | fmt: 15 | gofmt -s -w $(SOURCES) 16 | 17 | .PHONY: clean 18 | clean: 19 | go clean --modcache 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-alpm 2 | 3 | go-alpm is a Go package for binding libalpm. With go-alpm, it becomes possible 4 | to manipulate the Pacman databases and packages just as Pacman would. 5 | 6 | This project is MIT Licensed. See LICENSE for details. 7 | 8 | ## Getting started 9 | 10 | - Import the go-alpm repository in your go script 11 | 12 | ```go 13 | import "github.com/Jguer/go-alpm/v2" 14 | ``` 15 | 16 | - Try the included examples in [examples](testdata/examples/) 17 | 18 | ## Current Maintainers 19 | 20 | - Morganamilo 21 | - Jguer 22 | 23 | ## Original Contributors 24 | 25 | - Mike Rosset 26 | - Dave Reisner 27 | - Rémy Oudompheng 28 | - Jesus Alvarez 29 | -------------------------------------------------------------------------------- /alpm.go: -------------------------------------------------------------------------------- 1 | // alpm.go - Implements exported libalpm functions. 2 | // 3 | // Copyright (c) 2013 The go-alpm Authors 4 | // 5 | // MIT Licensed. See LICENSE for details. 6 | 7 | package alpm 8 | 9 | // #cgo LDFLAGS: -lalpm 10 | // #include 11 | import "C" 12 | import "unsafe" 13 | 14 | // Init initializes alpm handle 15 | func Initialize(root, dbpath string) (*Handle, error) { 16 | var ( 17 | cRoot = C.CString(root) 18 | cDBPath = C.CString(dbpath) 19 | cErr C.alpm_errno_t 20 | h = C.alpm_initialize(cRoot, cDBPath, &cErr) 21 | ) 22 | 23 | defer C.free(unsafe.Pointer(cRoot)) 24 | defer C.free(unsafe.Pointer(cDBPath)) 25 | 26 | if cErr != 0 { 27 | return nil, Error(cErr) 28 | } 29 | 30 | return &Handle{h}, nil 31 | } 32 | 33 | // Release releases the alpm handle 34 | func (h *Handle) Release() error { 35 | if er := C.alpm_release(h.ptr); er != 0 { 36 | return Error(er) 37 | } 38 | 39 | h.ptr = nil 40 | 41 | return nil 42 | } 43 | 44 | // LastError gets the last pm_error 45 | func (h *Handle) LastError() error { 46 | if h.ptr != nil { 47 | cErr := C.alpm_errno(h.ptr) 48 | if cErr != 0 { 49 | return Error(cErr) 50 | } 51 | } 52 | 53 | return nil 54 | } 55 | 56 | // Version returns libalpm version string. 57 | func Version() string { 58 | return C.GoString(C.alpm_version()) 59 | } 60 | -------------------------------------------------------------------------------- /alpm_test.go: -------------------------------------------------------------------------------- 1 | // alpm_test.go - Tests for alpm.go. 2 | // 3 | // Copyright (c) 2013 The go-alpm Authors 4 | // 5 | // MIT Licensed. See LICENSE for details. 6 | 7 | package alpm_test 8 | 9 | import ( 10 | "testing" 11 | 12 | "github.com/stretchr/testify/assert" 13 | 14 | alpm "github.com/Jguer/go-alpm/v2" 15 | ) 16 | 17 | const ( 18 | root = "/" 19 | dbpath = "/var/lib/pacman" 20 | ) 21 | 22 | func TestExampleVerCmp(t *testing.T) { 23 | t.Parallel() 24 | 25 | assert.Less(t, alpm.VerCmp("1.0-2", "2.0-1"), 0) 26 | assert.Equal(t, alpm.VerCmp("2.0.2-2", "2.0.2-2"), 0) 27 | assert.Greater(t, alpm.VerCmp("1:1.0-2", "2.0-1"), 0) 28 | } 29 | 30 | func TestRevdeps(t *testing.T) { 31 | t.Parallel() 32 | 33 | h, _ := alpm.Initialize(root, dbpath) 34 | 35 | db, _ := h.LocalDB() 36 | pkg := db.Pkg("glibc") 37 | for i, pkgname := range pkg.ComputeRequiredBy() { 38 | t.Log(pkgname) 39 | if i == 10 { 40 | t.Logf("and %d more...", len(pkg.ComputeRequiredBy())-10) 41 | return 42 | } 43 | } 44 | } 45 | 46 | func TestLocalDB(t *testing.T) { 47 | t.Parallel() 48 | h, _ := alpm.Initialize(root, dbpath) 49 | 50 | defer func() { 51 | if recover() != nil { 52 | t.Errorf("local db failed") 53 | } 54 | }() 55 | db, _ := h.LocalDB() 56 | number := 0 57 | for _, pkg := range db.PkgCache().Slice() { 58 | number++ 59 | if number <= 15 { 60 | t.Logf("%v", pkg.Name()) 61 | } 62 | } 63 | if number > 15 { 64 | t.Logf("%d more packages...", number-15) 65 | } 66 | } 67 | 68 | func TestRelease(t *testing.T) { 69 | t.Parallel() 70 | h, _ := alpm.Initialize(root, dbpath) 71 | 72 | if err := h.Release(); err != nil { 73 | t.Error(err) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /callbacks.c: -------------------------------------------------------------------------------- 1 | // callbacks.c - Sets alpm callbacks to Go functions. 2 | // 3 | // Copyright (c) 2013 The go-alpm Authors 4 | // 5 | // MIT Licensed. See LICENSE for details. 6 | 7 | #include 8 | #include 9 | #include 10 | #include "callbacks.h" 11 | 12 | typedef struct { 13 | void *go_cb; 14 | go_ctx_t go_ctx; 15 | } go_alpm_context_t; 16 | 17 | static void _log_cb(void *c_ctx, alpm_loglevel_t level, const char *fmt, va_list arg) { 18 | go_alpm_context_t *ctx = c_ctx; 19 | char *s = malloc(128); 20 | if (s == NULL) return; 21 | int16_t length = vsnprintf(s, 128, fmt, arg); 22 | if (length > 128) { 23 | length = (length + 16) & ~0xf; 24 | s = realloc(s, length); 25 | } 26 | if (s != NULL) { 27 | go_alpm_go_log_callback(&ctx->go_cb, ctx->go_ctx, level, s); 28 | free(s); 29 | } 30 | } 31 | 32 | static void _question_cb(void *c_ctx, alpm_question_t *question) { 33 | go_alpm_context_t *ctx = c_ctx; 34 | go_alpm_go_question_callback(&ctx->go_cb, ctx->go_ctx, question); 35 | } 36 | 37 | static void *alloc_ctx(go_alpm_context_t *ctx, void *go_cb, go_ctx_t go_ctx) { 38 | if (ctx == NULL ) { 39 | ctx = malloc(sizeof(go_alpm_context_t)); 40 | } 41 | 42 | ctx->go_cb = go_cb; 43 | ctx->go_ctx = go_ctx; 44 | 45 | return ctx; 46 | } 47 | 48 | void go_alpm_set_log_callback(alpm_handle_t *handle, void *go_cb, go_ctx_t go_ctx) { 49 | void *ctx = alpm_option_get_logcb_ctx(handle); 50 | ctx = alloc_ctx(ctx, *(void**)go_cb, go_ctx); 51 | alpm_option_set_logcb(handle, _log_cb, ctx); 52 | } 53 | 54 | void go_alpm_set_question_callback(alpm_handle_t *handle, void *go_cb, go_ctx_t go_ctx) { 55 | void *ctx = alpm_option_get_questioncb_ctx(handle); 56 | ctx = alloc_ctx(ctx, *(void**)go_cb, go_ctx); 57 | alpm_option_set_questioncb(handle, _question_cb, ctx); 58 | } 59 | -------------------------------------------------------------------------------- /callbacks.go: -------------------------------------------------------------------------------- 1 | // callbacks.go - Handles libalpm callbacks. 2 | // 3 | // Copyright (c) 2013 The go-alpm Authors 4 | // 5 | // MIT Licensed. See LICENSE for details. 6 | 7 | package alpm 8 | 9 | /* 10 | #include "callbacks.h" 11 | */ 12 | import "C" 13 | 14 | import ( 15 | "fmt" 16 | "os" 17 | "unsafe" 18 | ) 19 | 20 | var DefaultLogLevel = LogWarning 21 | 22 | type ( 23 | logCallbackSig func(interface{}, LogLevel, string) 24 | questionCallbackSig func(interface{}, QuestionAny) 25 | callbackContextPool map[C.go_ctx_t]interface{} 26 | ) 27 | 28 | var ( 29 | logCallbackContextPool = callbackContextPool{} 30 | questionCallbackContextPool = callbackContextPool{} 31 | ) 32 | 33 | func DefaultLogCallback(_ interface{}, lvl LogLevel, s string) { 34 | if lvl <= DefaultLogLevel { 35 | fmt.Fprintln(os.Stdout, "go-alpm: ", s) 36 | } 37 | } 38 | 39 | //export go_alpm_go_log_callback 40 | func go_alpm_go_log_callback(goCb unsafe.Pointer, goCtx C.go_ctx_t, lvl C.alpm_loglevel_t, s *C.char) { 41 | cb := *(*logCallbackSig)(goCb) 42 | ctx := logCallbackContextPool[goCtx] 43 | 44 | cb(ctx, LogLevel(lvl), C.GoString(s)) 45 | } 46 | 47 | //export go_alpm_go_question_callback 48 | func go_alpm_go_question_callback(goCb unsafe.Pointer, goCtx C.go_ctx_t, question *C.alpm_question_t) { 49 | q := (*C.alpm_question_any_t)(unsafe.Pointer(question)) 50 | 51 | cb := *(*questionCallbackSig)(goCb) 52 | ctx := questionCallbackContextPool[goCtx] 53 | 54 | cb(ctx, QuestionAny{q}) 55 | } 56 | 57 | func (h *Handle) SetLogCallback(cb logCallbackSig, ctx interface{}) { 58 | goCb := unsafe.Pointer(&cb) 59 | goCtx := C.go_ctx_t(h.ptr) 60 | 61 | logCallbackContextPool[goCtx] = ctx 62 | 63 | C.go_alpm_set_log_callback(h.ptr, goCb, goCtx) 64 | } 65 | 66 | func (h *Handle) SetQuestionCallback(cb questionCallbackSig, ctx interface{}) { 67 | goCb := unsafe.Pointer(&cb) 68 | goCtx := C.go_ctx_t(h.ptr) 69 | 70 | questionCallbackContextPool[goCtx] = ctx 71 | 72 | C.go_alpm_set_question_callback(h.ptr, goCb, goCtx) 73 | } 74 | -------------------------------------------------------------------------------- /callbacks.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | typedef void *go_ctx_t; 4 | 5 | void go_alpm_go_log_callback(void *go_cb, go_ctx_t go_ctx, alpm_loglevel_t level, char *message); 6 | void go_alpm_go_question_callback(void *go_cb, go_ctx_t go_ctx, alpm_question_t *question); 7 | 8 | void go_alpm_set_log_callback(alpm_handle_t *handle, void *go_cb, go_ctx_t go_ctx); 9 | void go_alpm_set_question_callback(alpm_handle_t *handle, void *go_cb, go_ctx_t go_ctx); 10 | -------------------------------------------------------------------------------- /callbacks_test.go: -------------------------------------------------------------------------------- 1 | package alpm_test 2 | 3 | import ( 4 | "testing" 5 | 6 | alpm "github.com/Jguer/go-alpm/v2" 7 | 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | type Cnt struct { 12 | cnt int 13 | } 14 | 15 | func TestCallbacks(t *testing.T) { 16 | t.Parallel() 17 | h, er := alpm.Initialize(root, dbpath) 18 | defer h.Release() 19 | if er != nil { 20 | t.Errorf("Failed at alpm initialization: %s", er) 21 | } 22 | 23 | cnt := &Cnt{cnt: 0} 24 | 25 | h.SetLogCallback(func(ctx interface{}, lvl alpm.LogLevel, msg string) { 26 | cnt := ctx.(*Cnt) 27 | cnt.cnt++ 28 | }, cnt) 29 | 30 | h.Release() 31 | 32 | require.Equal(t, 1, cnt.cnt) 33 | } 34 | -------------------------------------------------------------------------------- /config_test.go: -------------------------------------------------------------------------------- 1 | package alpm_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/Morganamilo/go-pacmanconf" 7 | "github.com/stretchr/testify/assert" 8 | 9 | alpm "github.com/Jguer/go-alpm/v2" 10 | ) 11 | 12 | type alpmExecutor struct { 13 | handle *alpm.Handle 14 | localDB alpm.IDB 15 | syncDB alpm.IDBList 16 | syncDBsCache []alpm.IDB 17 | conf *pacmanconf.Config 18 | } 19 | 20 | func (ae *alpmExecutor) RefreshHandle() error { 21 | if ae.handle != nil { 22 | if errRelease := ae.handle.Release(); errRelease != nil { 23 | return errRelease 24 | } 25 | } 26 | 27 | alpmHandle, err := alpm.Initialize(ae.conf.RootDir, ae.conf.DBPath) 28 | if err != nil { 29 | return err 30 | } 31 | 32 | if errConf := configureAlpm(ae.conf, alpmHandle); errConf != nil { 33 | return errConf 34 | } 35 | 36 | ae.handle = alpmHandle 37 | ae.syncDBsCache = nil 38 | 39 | ae.syncDB, err = alpmHandle.SyncDBs() 40 | if err != nil { 41 | return err 42 | } 43 | 44 | ae.localDB, err = alpmHandle.LocalDB() 45 | 46 | return err 47 | } 48 | 49 | func toUsage(usages []string) alpm.Usage { 50 | if len(usages) == 0 { 51 | return alpm.UsageAll 52 | } 53 | 54 | var ret alpm.Usage 55 | 56 | for _, usage := range usages { 57 | switch usage { 58 | case "Sync": 59 | ret |= alpm.UsageSync 60 | case "Search": 61 | ret |= alpm.UsageSearch 62 | case "Install": 63 | ret |= alpm.UsageInstall 64 | case "Upgrade": 65 | ret |= alpm.UsageUpgrade 66 | case "All": 67 | ret |= alpm.UsageAll 68 | } 69 | } 70 | 71 | return ret 72 | } 73 | 74 | func configureAlpm(pacmanConf *pacmanconf.Config, alpmHandle *alpm.Handle) error { 75 | for _, repo := range pacmanConf.Repos { 76 | // TODO: set SigLevel 77 | alpmDB, err := alpmHandle.RegisterSyncDB(repo.Name, 0) 78 | if err != nil { 79 | return err 80 | } 81 | 82 | alpmDB.SetServers(repo.Servers) 83 | alpmDB.SetUsage(toUsage(repo.Usage)) 84 | } 85 | 86 | if err := alpmHandle.SetCacheDirs(pacmanConf.CacheDir); err != nil { 87 | return err 88 | } 89 | 90 | // add hook directories 1-by-1 to avoid overwriting the system directory 91 | for _, dir := range pacmanConf.HookDir { 92 | if err := alpmHandle.AddHookDir(dir); err != nil { 93 | return err 94 | } 95 | } 96 | 97 | if err := alpmHandle.SetGPGDir(pacmanConf.GPGDir); err != nil { 98 | return err 99 | } 100 | 101 | if err := alpmHandle.SetLogFile(pacmanConf.LogFile); err != nil { 102 | return err 103 | } 104 | 105 | if err := alpmHandle.SetIgnorePkgs(pacmanConf.IgnorePkg); err != nil { 106 | return err 107 | } 108 | 109 | if err := alpmHandle.SetIgnoreGroups(pacmanConf.IgnoreGroup); err != nil { 110 | return err 111 | } 112 | 113 | if err := alpmHandle.SetArchitectures(pacmanConf.Architecture); err != nil { 114 | return err 115 | } 116 | 117 | if err := alpmHandle.SetNoUpgrades(pacmanConf.NoUpgrade); err != nil { 118 | return err 119 | } 120 | 121 | if err := alpmHandle.SetNoExtracts(pacmanConf.NoExtract); err != nil { 122 | return err 123 | } 124 | 125 | if err := alpmHandle.SetUseSyslog(pacmanConf.UseSyslog); err != nil { 126 | return err 127 | } 128 | 129 | return alpmHandle.SetCheckSpace(pacmanConf.CheckSpace) 130 | } 131 | 132 | func NewExecutor(pacmanConf *pacmanconf.Config) (*alpmExecutor, error) { 133 | ae := &alpmExecutor{conf: pacmanConf} 134 | 135 | err := ae.RefreshHandle() 136 | if err != nil { 137 | return nil, err 138 | } 139 | 140 | ae.localDB, err = ae.handle.LocalDB() 141 | if err != nil { 142 | return nil, err 143 | } 144 | 145 | ae.syncDB, err = ae.handle.SyncDBs() 146 | if err != nil { 147 | return nil, err 148 | } 149 | 150 | return ae, nil 151 | } 152 | 153 | func TestAlpmExecutor(t *testing.T) { 154 | t.Parallel() 155 | pacmanConf := &pacmanconf.Config{ 156 | RootDir: "/", 157 | DBPath: "/var/lib/pacman/", 158 | CacheDir: []string{"/cachedir/", "/another/"}, 159 | HookDir: []string{"/hookdir/"}, 160 | GPGDir: "/gpgdir/", 161 | LogFile: "/logfile", 162 | HoldPkg: []string(nil), 163 | IgnorePkg: []string{"ignore", "this", "package"}, 164 | IgnoreGroup: []string{"ignore", "this", "group"}, 165 | Architecture: []string{"8086"}, 166 | XferCommand: "", 167 | NoUpgrade: []string{"noupgrade"}, 168 | NoExtract: []string{"noextract"}, 169 | CleanMethod: []string{"KeepInstalled"}, 170 | SigLevel: []string{"PackageOptional", "PackageTrustedOnly", "DatabaseOptional", "DatabaseTrustedOnly"}, 171 | LocalFileSigLevel: []string(nil), 172 | RemoteFileSigLevel: []string(nil), 173 | UseSyslog: false, 174 | Color: false, 175 | UseDelta: 0, 176 | TotalDownload: true, 177 | CheckSpace: true, 178 | VerbosePkgLists: true, 179 | DisableDownloadTimeout: false, 180 | Repos: []pacmanconf.Repository{ 181 | {Name: "repo1", Servers: []string{"repo1"}, SigLevel: []string(nil), Usage: []string{"All"}}, 182 | {Name: "repo2", Servers: []string{"repo2"}, SigLevel: []string(nil), Usage: []string{"All"}}, 183 | }, 184 | } 185 | 186 | aExec, err := NewExecutor(pacmanConf) 187 | assert.NoError(t, err) 188 | 189 | assert.NotNil(t, aExec.conf) 190 | assert.EqualValues(t, pacmanConf, aExec.conf) 191 | 192 | assert.NotNil(t, aExec.localDB) 193 | assert.NotNil(t, aExec.syncDB) 194 | h := aExec.handle 195 | assert.NotNil(t, h) 196 | 197 | root, err := h.Root() 198 | assert.Nil(t, err) 199 | assert.Equal(t, "/", root) 200 | 201 | dbPath, err := h.DBPath() 202 | assert.Nil(t, err) 203 | assert.Equal(t, "/var/lib/pacman/", dbPath) 204 | 205 | cache, err := h.CacheDirs() 206 | assert.Nil(t, err) 207 | assert.Equal(t, []string{"/cachedir/", "/another/"}, cache.Slice()) 208 | 209 | log, err := h.LogFile() 210 | assert.Nil(t, err) 211 | assert.Equal(t, "/logfile", log) 212 | 213 | gpg, err := h.GPGDir() 214 | assert.Nil(t, err) 215 | assert.Equal(t, "/gpgdir/", gpg) 216 | 217 | hook, err := h.HookDirs() 218 | assert.Nil(t, err) 219 | assert.Equal(t, []string{"/usr/share/libalpm/hooks/", "/hookdir/"}, hook.Slice()) 220 | 221 | arch, err := alpmTestGetArch(h) 222 | assert.Nil(t, err) 223 | assert.Equal(t, []string{"8086"}, arch) 224 | 225 | ignorePkg, err := h.IgnorePkgs() 226 | assert.Nil(t, err) 227 | assert.Equal(t, []string{"ignore", "this", "package"}, ignorePkg.Slice()) 228 | 229 | ignoreGroup, err := h.IgnoreGroups() 230 | assert.Nil(t, err) 231 | assert.Equal(t, []string{"ignore", "this", "group"}, ignoreGroup.Slice()) 232 | 233 | noUp, err := h.NoUpgrades() 234 | assert.Nil(t, err) 235 | assert.Equal(t, []string{"noupgrade"}, noUp.Slice()) 236 | 237 | noEx, err := h.NoExtracts() 238 | assert.Nil(t, err) 239 | assert.Equal(t, []string{"noextract"}, noEx.Slice()) 240 | 241 | check, err := h.CheckSpace() 242 | assert.Nil(t, err) 243 | assert.Equal(t, true, check) 244 | } 245 | 246 | func alpmTestGetArch(h *alpm.Handle) ([]string, error) { 247 | architectures, err := h.GetArchitectures() 248 | 249 | return architectures.Slice(), err 250 | } 251 | -------------------------------------------------------------------------------- /db.go: -------------------------------------------------------------------------------- 1 | // db.go - Functions for database handling. 2 | // 3 | // Copyright (c) 2013 The go-alpm Authors 4 | // 5 | // MIT Licensed. See LICENSE for details. 6 | 7 | package alpm 8 | 9 | /* 10 | #include 11 | #include 12 | */ 13 | import "C" 14 | 15 | import ( 16 | "fmt" 17 | "io" 18 | "unsafe" 19 | ) 20 | 21 | // DB structure representing a alpm database. 22 | type DB struct { 23 | ptr *C.alpm_db_t 24 | handle Handle 25 | } 26 | 27 | // DBList structure representing a alpm database list. 28 | type DBList struct { 29 | *list 30 | handle Handle 31 | } 32 | 33 | // ForEach executes an action on each DB. 34 | func (l DBList) ForEach(f func(IDB) error) error { 35 | return l.forEach(func(p unsafe.Pointer) error { 36 | return f(&DB{(*C.alpm_db_t)(p), l.handle}) 37 | }) 38 | } 39 | 40 | // Slice converts DB list to DB slice. 41 | func (l DBList) Slice() []IDB { 42 | slice := []IDB{} 43 | _ = l.ForEach(func(db IDB) error { 44 | slice = append(slice, db) 45 | return nil 46 | }) 47 | 48 | return slice 49 | } 50 | 51 | // Append modifies a DB list by appending the given DB. 52 | func (l *DBList) Append(db IDB) { 53 | cdblist := (*C.alpm_list_t)(unsafe.Pointer(l.list)) 54 | cdb := unsafe.Pointer(db.(*DB).ptr) 55 | 56 | cdblist = C.alpm_list_add(cdblist, cdb) 57 | 58 | l.list = (*list)(unsafe.Pointer(cdblist)) 59 | } 60 | 61 | // SyncDBByName finds a registered database by name. 62 | func (h *Handle) SyncDBByName(name string) (db IDB, err error) { 63 | dblist, err := h.SyncDBs() 64 | if err != nil { 65 | return nil, err 66 | } 67 | 68 | _ = dblist.ForEach(func(b IDB) error { 69 | if b.Name() == name { 70 | db = b 71 | return io.EOF 72 | } 73 | return nil 74 | }) 75 | 76 | if db != nil { 77 | return db, nil 78 | } 79 | 80 | return nil, fmt.Errorf("database %s not found", name) 81 | } 82 | 83 | // SyncDBListByDBName creates and returns a database list with a single 84 | // database given by name. 85 | func (h *Handle) SyncDBListByDBName(name string) (IDBList, error) { 86 | db, err := h.SyncDBByName(name) 87 | if err != nil { 88 | return nil, err 89 | } 90 | 91 | dblist := h.NewDBList() 92 | dblist.Append(db) 93 | 94 | return dblist, nil 95 | } 96 | 97 | // RegisterSyncDB Loads a sync database with given name and signature check level. 98 | func (h *Handle) RegisterSyncDB(dbname string, siglevel SigLevel) (IDB, error) { 99 | cName := C.CString(dbname) 100 | 101 | defer C.free(unsafe.Pointer(cName)) 102 | 103 | db := C.alpm_register_syncdb(h.ptr, cName, C.int(siglevel)) 104 | if db == nil { 105 | return nil, h.LastError() 106 | } 107 | 108 | return &DB{db, *h}, nil 109 | } 110 | 111 | func (db *DB) Unregister() error { 112 | ok := C.alpm_db_unregister(db.ptr) 113 | if ok != 0 { 114 | return db.handle.LastError() 115 | } 116 | 117 | return nil 118 | } 119 | 120 | func (h *Handle) UnregisterAllSyncDBs() error { 121 | ok := C.alpm_unregister_all_syncdbs(h.ptr) 122 | if ok != 0 { 123 | return h.LastError() 124 | } 125 | 126 | return nil 127 | } 128 | 129 | // Name returns name of the db 130 | func (db *DB) Name() string { 131 | return C.GoString(C.alpm_db_get_name(db.ptr)) 132 | } 133 | 134 | // Servers returns host server URL. 135 | func (db *DB) Servers() []string { 136 | ptr := unsafe.Pointer(C.alpm_db_get_servers(db.ptr)) 137 | 138 | return StringList{(*list)(ptr)}.Slice() 139 | } 140 | 141 | // SetServers sets server list to use. 142 | func (db *DB) SetServers(servers []string) { 143 | C.alpm_db_set_servers(db.ptr, nil) 144 | 145 | for _, srv := range servers { 146 | Csrv := C.CString(srv) 147 | 148 | C.alpm_db_add_server(db.ptr, Csrv) 149 | C.free(unsafe.Pointer(Csrv)) 150 | } 151 | } 152 | 153 | // AddServers adds a string to the server list. 154 | func (db *DB) AddServer(server string) { 155 | Csrv := C.CString(server) 156 | 157 | defer C.free(unsafe.Pointer(Csrv)) 158 | 159 | C.alpm_db_add_server(db.ptr, Csrv) 160 | } 161 | 162 | // SetUsage sets the Usage of the database 163 | func (db *DB) SetUsage(usage Usage) { 164 | C.alpm_db_set_usage(db.ptr, C.int(usage)) 165 | } 166 | 167 | // Name searches a package in db. 168 | func (db *DB) Pkg(name string) IPackage { 169 | cName := C.CString(name) 170 | 171 | defer C.free(unsafe.Pointer(cName)) 172 | 173 | ptr := C.alpm_db_get_pkg(db.ptr, cName) 174 | if ptr == nil { 175 | return nil 176 | } 177 | 178 | return &Package{ptr, db.handle} 179 | } 180 | 181 | // PkgCache returns the list of packages of the database 182 | func (db *DB) PkgCache() IPackageList { 183 | if db == nil || db.ptr == nil { 184 | return PackageList{nil, db.handle} 185 | } 186 | pkgcache := (*list)(unsafe.Pointer(C.alpm_db_get_pkgcache(db.ptr))) 187 | return PackageList{pkgcache, db.handle} 188 | } 189 | 190 | // Search returns a list of packages matching the targets. 191 | // In case of error the Package List will be nil 192 | func (db *DB) Search(targets []string) IPackageList { 193 | var ( 194 | needles *C.alpm_list_t 195 | ret *C.alpm_list_t 196 | ) 197 | 198 | for _, str := range targets { 199 | needles = C.alpm_list_add(needles, unsafe.Pointer(C.CString(str))) 200 | } 201 | 202 | ok := C.alpm_db_search(db.ptr, needles, &ret) 203 | if ok != 0 { 204 | return PackageList{nil, db.handle} 205 | } 206 | 207 | C.alpm_list_free(needles) 208 | 209 | return PackageList{(*list)(unsafe.Pointer(ret)), db.handle} 210 | } 211 | 212 | // PkgCachebyGroup returns a PackageList of packages belonging to a group 213 | func (l DBList) FindGroupPkgs(name string) IPackageList { 214 | cName := C.CString(name) 215 | 216 | defer C.free(unsafe.Pointer(cName)) 217 | 218 | pkglist := (*C.struct__alpm_list_t)(unsafe.Pointer(l.list)) 219 | pkgcache := (*list)(unsafe.Pointer(C.alpm_find_group_pkgs(pkglist, cName))) 220 | 221 | return PackageList{pkgcache, l.handle} 222 | } 223 | -------------------------------------------------------------------------------- /db_test.go: -------------------------------------------------------------------------------- 1 | package alpm 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | ) 7 | 8 | func getTestAlpmHandle(t *testing.T) *Handle { 9 | // Try to use environment variables for test root/dbpath, or fallback to system defaults 10 | root := os.Getenv("ALPM_TEST_ROOT") 11 | if root == "" { 12 | root = "/" 13 | } 14 | dbpath := os.Getenv("ALPM_TEST_DBPATH") 15 | if dbpath == "" { 16 | dbpath = "/var/lib/pacman/" 17 | } 18 | 19 | h, err := Initialize(root, dbpath) 20 | if err != nil { 21 | t.Skipf("Could not initialize alpm: %v", err) 22 | } 23 | return h 24 | } 25 | 26 | func TestHandle_RegisterSyncDB_and_SyncDBByName(t *testing.T) { 27 | h := getTestAlpmHandle(t) 28 | defer h.Release() 29 | 30 | dbName := "core" // This should exist on most Arch systems 31 | db, err := h.RegisterSyncDB(dbName, 0) 32 | if err != nil { 33 | t.Fatalf("RegisterSyncDB failed: %v", err) 34 | } 35 | if db == nil { 36 | t.Fatalf("RegisterSyncDB returned nil DB") 37 | } 38 | 39 | found, err := h.SyncDBByName(dbName) 40 | if err != nil { 41 | t.Fatalf("SyncDBByName failed: %v", err) 42 | } 43 | if found == nil { 44 | t.Fatalf("SyncDBByName returned nil DB") 45 | } 46 | if found.Name() != dbName { 47 | t.Errorf("Expected DB name %q, got %q", dbName, found.Name()) 48 | } 49 | } 50 | 51 | func TestHandle_UnregisterAllSyncDBs(t *testing.T) { 52 | h := getTestAlpmHandle(t) 53 | defer h.Release() 54 | 55 | dbName := "core" 56 | _, err := h.RegisterSyncDB(dbName, 0) 57 | if err != nil { 58 | t.Fatalf("RegisterSyncDB failed: %v", err) 59 | } 60 | 61 | err = h.UnregisterAllSyncDBs() 62 | if err != nil { 63 | t.Fatalf("UnregisterAllSyncDBs failed: %v", err) 64 | } 65 | } 66 | 67 | // --- DB methods --- 68 | 69 | func TestDB_Search(t *testing.T) { 70 | h := getTestAlpmHandle(t) 71 | defer h.Release() 72 | 73 | db, err := h.RegisterSyncDB("core", 0) 74 | if err != nil { 75 | t.Skipf("RegisterSyncDB failed: %v", err) 76 | } 77 | 78 | // Dynamically pick a package name from the sync DB 79 | pkgs := db.PkgCache().Slice() 80 | if len(pkgs) == 0 { 81 | t.Skip("No packages found in the sync DB; skipping test") 82 | } 83 | target := pkgs[0].Name() 84 | 85 | searchResults := db.Search([]string{target}) 86 | if searchResults == nil { 87 | t.Fatalf("Search returned nil for target %q", target) 88 | } 89 | slice := searchResults.Slice() 90 | if len(slice) == 0 { 91 | t.Errorf("Expected at least one package for target %q, got 0", target) 92 | } 93 | found := false 94 | for _, pkg := range slice { 95 | if pkg != nil && pkg.Name() == target { 96 | found = true 97 | break 98 | } 99 | } 100 | if !found { 101 | t.Errorf("Did not find package with name %q in search results", target) 102 | } 103 | 104 | // Search for a non-existent package 105 | nonexistent := "thispackagedoesnotexist12345" 106 | none := db.Search([]string{nonexistent}) 107 | if none == nil { 108 | t.Fatalf("Search returned nil for non-existent target") 109 | } 110 | if len(none.Slice()) != 0 { 111 | t.Errorf("Expected 0 results for non-existent package, got %d", len(none.Slice())) 112 | } 113 | } 114 | 115 | func TestDB_AddServer(t *testing.T) { 116 | h := getTestAlpmHandle(t) 117 | defer h.Release() 118 | 119 | db, err := h.RegisterSyncDB("core", 0) 120 | if err != nil { 121 | t.Skipf("RegisterSyncDB failed: %v", err) 122 | } 123 | 124 | server := "http://example.com/$repo/os/$arch" 125 | db.AddServer(server) 126 | 127 | servers := db.Servers() 128 | found := false 129 | for _, s := range servers { 130 | if s == server { 131 | found = true 132 | break 133 | } 134 | } 135 | if !found { 136 | t.Errorf("Server %q not found in DB server list: %v", server, servers) 137 | } 138 | } 139 | 140 | // --- DBList group method --- 141 | 142 | func TestDBList_Slice_Empty(t *testing.T) { 143 | var dblist DBList // nil list 144 | slice := dblist.Slice() 145 | if len(slice) != 0 { 146 | t.Errorf("Expected empty slice, got %d elements", len(slice)) 147 | } 148 | } 149 | 150 | func TestDBList_ForEach_and_Slice_NonEmpty(t *testing.T) { 151 | h := getTestAlpmHandle(t) 152 | defer h.Release() 153 | 154 | dblist, err := h.SyncDBs() 155 | if err != nil { 156 | t.Fatalf("SyncDBs failed: %v", err) 157 | } 158 | 159 | var count int 160 | names := make(map[string]bool) 161 | err = dblist.ForEach(func(db IDB) error { 162 | count++ 163 | name := db.Name() 164 | names[name] = true 165 | return nil 166 | }) 167 | if err != nil { 168 | t.Fatalf("ForEach failed: %v", err) 169 | } 170 | 171 | slice := dblist.Slice() 172 | if len(slice) != count { 173 | t.Errorf("Slice length %d does not match ForEach count %d", len(slice), count) 174 | } 175 | for _, db := range slice { 176 | if !names[db.Name()] { 177 | t.Errorf("DB %q in slice not found in ForEach names", db.Name()) 178 | } 179 | } 180 | } 181 | 182 | func TestDBList_Append(t *testing.T) { 183 | h := getTestAlpmHandle(t) 184 | defer h.Release() 185 | 186 | db, err := h.RegisterSyncDB("core", 0) 187 | if err != nil { 188 | t.Skipf("RegisterSyncDB failed: %v", err) 189 | } 190 | 191 | dblist := h.NewDBList() 192 | if dblist == nil { 193 | t.Fatalf("NewDBList returned nil") 194 | } 195 | 196 | // Should be empty initially 197 | slice := dblist.Slice() 198 | if len(slice) != 0 { 199 | t.Errorf("Expected empty slice, got %d elements", len(slice)) 200 | } 201 | 202 | dblist.Append(db) 203 | slice = dblist.Slice() 204 | if len(slice) != 1 { 205 | t.Errorf("Expected slice of length 1 after append, got %d", len(slice)) 206 | } 207 | if slice[0].Name() != "core" { 208 | t.Errorf("Expected DB name 'core', got %q", slice[0].Name()) 209 | } 210 | } 211 | 212 | func TestDBList_FindGroupPkgs(t *testing.T) { 213 | h := getTestAlpmHandle(t) 214 | defer h.Release() 215 | 216 | dblist, err := h.SyncDBs() 217 | if err != nil { 218 | t.Fatalf("SyncDBs failed: %v", err) 219 | } 220 | 221 | group := "base" // Common group in Arch 222 | pkgs := dblist.FindGroupPkgs(group) 223 | if pkgs == nil { 224 | t.Fatalf("FindGroupPkgs returned nil for group %q", group) 225 | } 226 | // We can't guarantee the group exists, but we can check that the call succeeded 227 | } 228 | 229 | func TestHandle_SyncDBListByDBName(t *testing.T) { 230 | h := getTestAlpmHandle(t) 231 | defer h.Release() 232 | 233 | dbName := "core" 234 | _, err := h.RegisterSyncDB(dbName, 0) 235 | if err != nil { 236 | t.Skipf("RegisterSyncDB failed: %v", err) 237 | } 238 | 239 | dblist, err := h.SyncDBListByDBName(dbName) 240 | if err != nil { 241 | t.Fatalf("SyncDBListByDBName failed: %v", err) 242 | } 243 | if dblist == nil { 244 | t.Fatalf("SyncDBListByDBName returned nil list") 245 | } 246 | slice := dblist.Slice() 247 | if len(slice) != 1 { 248 | t.Errorf("Expected list of length 1, got %d", len(slice)) 249 | } 250 | if slice[0].Name() != dbName { 251 | t.Errorf("Expected DB name %q, got %q", dbName, slice[0].Name()) 252 | } 253 | 254 | // Negative case 255 | _, err = h.SyncDBListByDBName("doesnotexist123") 256 | if err == nil { 257 | t.Errorf("Expected error for non-existent DB name, got nil") 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /deps.go: -------------------------------------------------------------------------------- 1 | package alpm 2 | 3 | /* 4 | #include 5 | */ 6 | import "C" 7 | 8 | import ( 9 | "fmt" 10 | "unsafe" 11 | ) 12 | 13 | // FindSatisfier searches a DBList for a package that satisfies depstring 14 | // Example "glibc>=2.12" 15 | func (l DBList) FindSatisfier(depstring string) (IPackage, error) { 16 | cDepString := C.CString(depstring) 17 | 18 | defer C.free(unsafe.Pointer(cDepString)) 19 | 20 | pkgList := (*C.struct__alpm_list_t)(unsafe.Pointer(l.list)) 21 | pkgHandle := (*C.struct__alpm_handle_t)(unsafe.Pointer(l.handle.ptr)) 22 | 23 | ptr := C.alpm_find_dbs_satisfier(pkgHandle, pkgList, cDepString) 24 | if ptr == nil { 25 | return nil, 26 | fmt.Errorf("unable to satisfy dependency %s in DBlist", depstring) 27 | } 28 | 29 | return &Package{ptr, l.handle}, nil 30 | } 31 | 32 | // FindSatisfier finds a package that satisfies depstring from PkgList 33 | func (l PackageList) FindSatisfier(depstring string) (IPackage, error) { 34 | cDepString := C.CString(depstring) 35 | 36 | defer C.free(unsafe.Pointer(cDepString)) 37 | 38 | pkgList := (*C.struct__alpm_list_t)(unsafe.Pointer(l.list)) 39 | 40 | ptr := C.alpm_find_satisfier(pkgList, cDepString) 41 | if ptr == nil { 42 | return nil, 43 | fmt.Errorf("unable to find dependency %s in PackageList", depstring) 44 | } 45 | 46 | return &Package{ptr, l.handle}, nil 47 | } 48 | -------------------------------------------------------------------------------- /deps_test.go: -------------------------------------------------------------------------------- 1 | package alpm_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | 8 | alpm "github.com/Jguer/go-alpm/v2" 9 | ) 10 | 11 | func TestDBList_FindSatisfier(t *testing.T) { 12 | h, err := alpm.Initialize("/", "/var/lib/pacman") 13 | defer h.Release() 14 | if err != nil { 15 | t.Fatalf("Failed to initialize alpm: %v", err) 16 | } 17 | 18 | dblist, err := h.SyncDBs() 19 | if err != nil { 20 | t.Fatalf("Failed to get sync DBs: %v", err) 21 | } 22 | 23 | // Dynamically pick a package name from the sync DBs 24 | var foundPkgName string 25 | for _, db := range dblist.Slice() { 26 | pkgs := db.PkgCache().Slice() 27 | if len(pkgs) > 0 { 28 | foundPkgName = pkgs[0].Name() 29 | break 30 | } 31 | } 32 | if foundPkgName == "" { 33 | t.Skip("No packages found in sync DBs") 34 | } 35 | 36 | // Test a dependency that should exist 37 | pkg, err := dblist.FindSatisfier(foundPkgName) 38 | assert.NoError(t, err) 39 | assert.NotNil(t, pkg) 40 | if pkg != nil { 41 | assert.Equal(t, foundPkgName, pkg.Name()) 42 | } 43 | 44 | // Test a dependency that should not exist 45 | pkg, err = dblist.FindSatisfier("thispackagedoesnotexist>=1.0") 46 | assert.Error(t, err) 47 | assert.Nil(t, pkg) 48 | } 49 | 50 | func TestPackageList_FindSatisfier(t *testing.T) { 51 | h, err := alpm.Initialize("/", "/var/lib/pacman") 52 | defer h.Release() 53 | if err != nil { 54 | t.Fatalf("Failed to initialize alpm: %v", err) 55 | } 56 | 57 | db, err := h.LocalDB() 58 | if err != nil { 59 | t.Fatalf("Failed to get local DB: %v", err) 60 | } 61 | 62 | pkglist := db.PkgCache() 63 | 64 | // Test a dependency that should exist 65 | pkg, err := pkglist.FindSatisfier("glibc>=2.12") 66 | assert.NoError(t, err) 67 | assert.NotNil(t, pkg) 68 | if pkg != nil { 69 | assert.Equal(t, "glibc", pkg.Name()) 70 | } 71 | 72 | // Test a dependency that should not exist 73 | pkg, err = pkglist.FindSatisfier("thispackagedoesnotexist>=1.0") 74 | assert.Error(t, err) 75 | assert.Nil(t, pkg) 76 | } 77 | -------------------------------------------------------------------------------- /enums.go: -------------------------------------------------------------------------------- 1 | // enums.go - libaplm enumerations. 2 | // 3 | // Copyright (c) 2013 The go-alpm Authors 4 | // 5 | // MIT Licensed. See LICENSE for details. 6 | 7 | package alpm 8 | 9 | // Install reason of a package. 10 | type PkgReason uint 11 | 12 | const ( 13 | PkgReasonExplicit PkgReason = 0 14 | PkgReasonDepend PkgReason = 1 15 | ) 16 | 17 | func (r PkgReason) String() string { 18 | switch r { 19 | case PkgReasonExplicit: 20 | return "Explicitly installed" 21 | case PkgReasonDepend: 22 | return "Installed as a dependency of another package" 23 | } 24 | 25 | return "" 26 | } 27 | 28 | // Source of a package structure. 29 | type PkgFrom uint 30 | 31 | const ( 32 | FromFile PkgFrom = iota + 1 33 | FromLocalDB 34 | FromSyncDB 35 | ) 36 | 37 | // Dependency constraint types. 38 | type DepMod uint 39 | 40 | const ( 41 | DepModAny DepMod = iota + 1 // Any version. 42 | DepModEq // Specific version. 43 | DepModGE // Test for >= some version. 44 | DepModLE // Test for <= some version. 45 | DepModGT // Test for > some version. 46 | DepModLT // Test for < some version. 47 | ) 48 | 49 | func (mod DepMod) String() string { 50 | switch mod { 51 | case DepModEq: 52 | return "=" 53 | case DepModGE: 54 | return ">=" 55 | case DepModLE: 56 | return "<=" 57 | case DepModGT: 58 | return ">" 59 | case DepModLT: 60 | return "<" 61 | } 62 | 63 | return "" 64 | } 65 | 66 | // Signature checking level. 67 | type SigLevel int 68 | 69 | const ( 70 | SigPackage SigLevel = 1 << iota 71 | SigPackageOptional 72 | SigPackageMarginalOk 73 | SigPackageUnknownOk 74 | ) 75 | 76 | const ( 77 | SigDatabase SigLevel = 1 << (10 + iota) 78 | SigDatabaseOptional 79 | SigDatabaseMarginalOk 80 | SigDatabaseUnknownOk 81 | ) 82 | const SigUseDefault SigLevel = 1 << 30 83 | 84 | // Signature status. 85 | type SigStatus int 86 | 87 | const ( 88 | SigStatusValid SigStatus = iota 89 | SigStatusKeyExpired 90 | SigStatusSigExpired 91 | SigStatusKeyUnknown 92 | SigStatusKeyDisabled 93 | ) 94 | 95 | type LogLevel uint16 96 | 97 | // Logging levels. 98 | const ( 99 | LogError LogLevel = 1 << iota 100 | LogWarning 101 | LogDebug 102 | LogFunction 103 | ) 104 | 105 | type QuestionType uint 106 | 107 | const ( 108 | QuestionTypeInstallIgnorepkg QuestionType = 1 << iota 109 | QuestionTypeReplacePkg 110 | QuestionTypeConflictPkg 111 | QuestionTypeCorruptedPkg 112 | QuestionTypeRemovePkgs 113 | QuestionTypeSelectProvider 114 | QuestionTypeImportKey 115 | ) 116 | 117 | type Validation int 118 | 119 | const ( 120 | ValidationNone Validation = 1 << iota 121 | ValidationMD5Sum 122 | ValidationSHA256Sum 123 | ValidationSignature 124 | ValidationUnkown Validation = 0 125 | ) 126 | 127 | type Usage int 128 | 129 | const ( 130 | UsageSync Usage = 1 << iota 131 | UsageSearch 132 | UsageInstall 133 | UsageUpgrade 134 | UsageAll = (1 << 4) - 1 135 | ) 136 | 137 | type TransFlag int 138 | 139 | const ( 140 | TransFlagNoDeps TransFlag = 1 << iota 141 | TransFlagForce 142 | TransFlagNoSave 143 | TransFlagNoDepVersion 144 | TransFlagCascade 145 | TransFlagRecurse 146 | _ // 7 is missing. 147 | TransFlagDBOnly 148 | TransFlagAllDeps 149 | TransFlagDownloadOnly 150 | TransFlagNoScriptlets 151 | _ // 12 is missing. 152 | TransFlagNoConflicts 153 | TransFlagNeeded 154 | TransFlagAllExplicit 155 | TransFlagUnneeded 156 | TransFlagRecurseAll 157 | TransFlagNoLock 158 | ) 159 | -------------------------------------------------------------------------------- /error.go: -------------------------------------------------------------------------------- 1 | // error.go - Functions for converting libalpm erros to Go errors. 2 | // 3 | // Copyright (c) 2013 The go-alpm Authors 4 | // 5 | // MIT Licensed. See LICENSE for details. 6 | 7 | package alpm 8 | 9 | // #include 10 | import "C" 11 | 12 | // The Error type represents error codes from libalpm. 13 | type Error C.alpm_errno_t 14 | 15 | // The string representation of an error is given by C function 16 | // alpm_strerror(). 17 | func (er Error) Error() string { 18 | return C.GoString(C.alpm_strerror(C.alpm_errno_t(er))) 19 | } 20 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/Jguer/go-alpm/v2 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/Morganamilo/go-pacmanconf v0.0.0-20210502114700-cff030e927a5 7 | github.com/stretchr/testify v1.8.4 8 | ) 9 | 10 | require ( 11 | github.com/davecgh/go-spew v1.1.1 // indirect 12 | github.com/pmezard/go-difflib v1.0.0 // indirect 13 | gopkg.in/yaml.v3 v3.0.1 // indirect 14 | ) 15 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/Morganamilo/go-pacmanconf v0.0.0-20210502114700-cff030e927a5 h1:TMscPjkb1ThXN32LuFY5bEYIcXZx3YlwzhS1GxNpn/c= 2 | github.com/Morganamilo/go-pacmanconf v0.0.0-20210502114700-cff030e927a5/go.mod h1:Hk55m330jNiwxRodIlMCvw5iEyoRUCIY64W1p9D+tHc= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 7 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 8 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 9 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 10 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 11 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 12 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 13 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 14 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 15 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 16 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 17 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 18 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 19 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 20 | -------------------------------------------------------------------------------- /handle.go: -------------------------------------------------------------------------------- 1 | // handle.go - libalpm handle type and methods. 2 | // 3 | // Copyright (c) 2013 The go-alpm Authors 4 | // 5 | // MIT Licensed. See LICENSE for details. 6 | 7 | // Package alpm implements Go bindings to the libalpm library used by Pacman, 8 | // the Arch Linux package manager. Libalpm allows the creation of custom front 9 | // ends to the Arch Linux package ecosystem. 10 | // 11 | // Libalpm does not include support for the Arch User Repository (AUR). 12 | package alpm 13 | 14 | // #include 15 | // #include //C.free 16 | // #include //C.FNM_NOMATCH 17 | import "C" 18 | 19 | import ( 20 | "unsafe" 21 | ) 22 | 23 | // Handle contains the pointer to the alpm handle 24 | type Handle struct { 25 | ptr *C.alpm_handle_t 26 | } 27 | 28 | // 29 | // alpm options getters and setters 30 | // 31 | 32 | // helper functions for wrapping list_t getters and setters 33 | func (h *Handle) optionGetList(f func(*C.alpm_handle_t) *C.alpm_list_t) (StringList, error) { 34 | alpmList := f(h.ptr) 35 | goList := StringList{(*list)(unsafe.Pointer(alpmList))} 36 | 37 | if alpmList == nil { 38 | return goList, h.LastError() 39 | } 40 | 41 | return goList, nil 42 | } 43 | 44 | func (h *Handle) optionSetList(hookDirs []string, f func(*C.alpm_handle_t, *C.alpm_list_t) C.int) error { 45 | var cList *C.alpm_list_t 46 | 47 | for _, dir := range hookDirs { 48 | cDir := unsafe.Pointer(C.CString(dir)) 49 | cList = C.alpm_list_add(cList, cDir) 50 | } 51 | 52 | if ok := f(h.ptr, cList); ok < 0 { 53 | return h.LastError() 54 | } 55 | 56 | goList := (*list)(unsafe.Pointer(cList)) 57 | 58 | return goList.forEach(func(p unsafe.Pointer) error { 59 | C.free(p) 60 | return nil 61 | }) 62 | } 63 | 64 | func (h *Handle) optionAddList(hookDir string, f func(*C.alpm_handle_t, *C.char) C.int) error { 65 | cHookDir := C.CString(hookDir) 66 | 67 | defer C.free(unsafe.Pointer(cHookDir)) 68 | 69 | if ok := f(h.ptr, cHookDir); ok < 0 { 70 | return h.LastError() 71 | } 72 | 73 | return nil 74 | } 75 | 76 | func (h *Handle) optionRemoveList(dir string, f func(*C.alpm_handle_t, *C.char) C.int) (bool, error) { 77 | cDir := C.CString(dir) 78 | 79 | defer C.free(unsafe.Pointer(cDir)) 80 | 81 | ok := f(h.ptr, cDir) 82 | if ok < 0 { 83 | return false, h.LastError() 84 | } 85 | 86 | return ok == 1, nil 87 | } 88 | 89 | func (h *Handle) optionMatchList(dir string, f func(*C.alpm_handle_t, *C.char) C.int) (bool, error) { 90 | cDir := C.CString(dir) 91 | 92 | defer C.free(unsafe.Pointer(cDir)) 93 | 94 | if ok := f(h.ptr, cDir); ok == 0 { 95 | return true, nil 96 | } else if ok == C.FNM_NOMATCH { 97 | return false, h.LastError() 98 | } 99 | 100 | return false, nil 101 | } 102 | 103 | // helper functions for *char based getters and setters 104 | func (h *Handle) optionGetStr(f func(*C.alpm_handle_t) *C.char) (string, error) { 105 | cStr := f(h.ptr) 106 | str := C.GoString(cStr) 107 | 108 | defer C.free(unsafe.Pointer(cStr)) 109 | 110 | if cStr == nil { 111 | return str, h.LastError() 112 | } 113 | 114 | return str, nil 115 | } 116 | 117 | func (h *Handle) optionSetStr(str string, f func(*C.alpm_handle_t, *C.char) C.int) error { 118 | cStr := C.CString(str) 119 | 120 | defer C.free(unsafe.Pointer(cStr)) 121 | 122 | if ok := f(h.ptr, cStr); ok < 0 { 123 | return h.LastError() 124 | } 125 | 126 | return nil 127 | } 128 | 129 | // 130 | // end of helpers 131 | // 132 | 133 | func (h *Handle) Root() (string, error) { 134 | return h.optionGetStr(func(handle *C.alpm_handle_t) *C.char { 135 | return C.alpm_option_get_root(handle) 136 | }) 137 | } 138 | 139 | func (h *Handle) DBPath() (string, error) { 140 | return h.optionGetStr(func(handle *C.alpm_handle_t) *C.char { 141 | return C.alpm_option_get_dbpath(handle) 142 | }) 143 | } 144 | 145 | func (h *Handle) Lockfile() (string, error) { 146 | return h.optionGetStr(func(handle *C.alpm_handle_t) *C.char { 147 | return C.alpm_option_get_lockfile(handle) 148 | }) 149 | } 150 | 151 | func (h *Handle) CacheDirs() (StringList, error) { 152 | return h.optionGetList(func(handle *C.alpm_handle_t) *C.alpm_list_t { 153 | return C.alpm_option_get_cachedirs(handle) 154 | }) 155 | } 156 | 157 | func (h *Handle) AddCacheDir(hookDir string) error { 158 | return h.optionAddList(hookDir, func(handle *C.alpm_handle_t, str *C.char) C.int { 159 | return C.alpm_option_add_cachedir(handle, str) 160 | }) 161 | } 162 | 163 | func (h *Handle) SetCacheDirs(hookDirs []string) error { 164 | return h.optionSetList(hookDirs, func(handle *C.alpm_handle_t, l *C.alpm_list_t) C.int { 165 | return C.alpm_option_set_cachedirs(handle, l) 166 | }) 167 | } 168 | 169 | func (h *Handle) RemoveCacheDir(dir string) (bool, error) { 170 | return h.optionRemoveList(dir, func(handle *C.alpm_handle_t, str *C.char) C.int { 171 | return C.alpm_option_remove_cachedir(handle, str) 172 | }) 173 | } 174 | 175 | func (h *Handle) HookDirs() (StringList, error) { 176 | return h.optionGetList(func(handle *C.alpm_handle_t) *C.alpm_list_t { 177 | return C.alpm_option_get_hookdirs(handle) 178 | }) 179 | } 180 | 181 | func (h *Handle) AddHookDir(hookDir string) error { 182 | return h.optionAddList(hookDir, func(handle *C.alpm_handle_t, str *C.char) C.int { 183 | return C.alpm_option_add_hookdir(handle, str) 184 | }) 185 | } 186 | 187 | func (h *Handle) SetHookDirs(hookDirs []string) error { 188 | return h.optionSetList(hookDirs, func(handle *C.alpm_handle_t, l *C.alpm_list_t) C.int { 189 | return C.alpm_option_set_hookdirs(handle, l) 190 | }) 191 | } 192 | 193 | func (h *Handle) RemoveHookDir(dir string) (bool, error) { 194 | return h.optionRemoveList(dir, func(handle *C.alpm_handle_t, str *C.char) C.int { 195 | return C.alpm_option_remove_hookdir(handle, str) 196 | }) 197 | } 198 | 199 | func (h *Handle) LogFile() (string, error) { 200 | return h.optionGetStr(func(handle *C.alpm_handle_t) *C.char { 201 | return C.alpm_option_get_logfile(handle) 202 | }) 203 | } 204 | 205 | func (h *Handle) SetLogFile(str string) error { 206 | return h.optionSetStr(str, func(handle *C.alpm_handle_t, c_str *C.char) C.int { 207 | return C.alpm_option_set_logfile(handle, c_str) 208 | }) 209 | } 210 | 211 | func (h *Handle) GPGDir() (string, error) { 212 | return h.optionGetStr(func(handle *C.alpm_handle_t) *C.char { 213 | return C.alpm_option_get_gpgdir(handle) 214 | }) 215 | } 216 | 217 | func (h *Handle) SetGPGDir(str string) error { 218 | return h.optionSetStr(str, func(handle *C.alpm_handle_t, c_str *C.char) C.int { 219 | return C.alpm_option_set_gpgdir(handle, c_str) 220 | }) 221 | } 222 | 223 | func (h *Handle) UseSyslog() (bool, error) { 224 | ok := C.alpm_option_get_usesyslog(h.ptr) 225 | 226 | if ok > 0 { 227 | return true, nil 228 | } 229 | 230 | if ok < 0 { 231 | return false, h.LastError() 232 | } 233 | 234 | return false, nil 235 | } 236 | 237 | func (h *Handle) SetUseSyslog(value bool) error { 238 | var intValue C.int 239 | if value { 240 | intValue = 1 241 | } 242 | 243 | if ok := C.alpm_option_set_usesyslog(h.ptr, intValue); ok < 0 { 244 | return h.LastError() 245 | } 246 | 247 | return nil 248 | } 249 | 250 | func (h *Handle) NoUpgrades() (StringList, error) { 251 | return h.optionGetList(func(handle *C.alpm_handle_t) *C.alpm_list_t { 252 | return C.alpm_option_get_noupgrades(handle) 253 | }) 254 | } 255 | 256 | func (h *Handle) AddNoUpgrade(hookDir string) error { 257 | return h.optionAddList(hookDir, func(handle *C.alpm_handle_t, str *C.char) C.int { 258 | return C.alpm_option_add_noupgrade(handle, str) 259 | }) 260 | } 261 | 262 | func (h *Handle) SetNoUpgrades(hookDirs []string) error { 263 | return h.optionSetList(hookDirs, func(handle *C.alpm_handle_t, l *C.alpm_list_t) C.int { 264 | return C.alpm_option_set_noupgrades(handle, l) 265 | }) 266 | } 267 | 268 | func (h *Handle) RemoveNoUpgrade(dir string) (bool, error) { 269 | return h.optionRemoveList(dir, func(handle *C.alpm_handle_t, str *C.char) C.int { 270 | return C.alpm_option_remove_noupgrade(handle, str) 271 | }) 272 | } 273 | 274 | func (h *Handle) MatchNoUpgrade(dir string) (bool, error) { 275 | return h.optionMatchList(dir, func(handle *C.alpm_handle_t, str *C.char) C.int { 276 | return C.alpm_option_match_noupgrade(handle, str) 277 | }) 278 | } 279 | 280 | func (h *Handle) NoExtracts() (StringList, error) { 281 | return h.optionGetList(func(handle *C.alpm_handle_t) *C.alpm_list_t { 282 | return C.alpm_option_get_noextracts(handle) 283 | }) 284 | } 285 | 286 | func (h *Handle) AddNoExtract(hookDir string) error { 287 | return h.optionAddList(hookDir, func(handle *C.alpm_handle_t, str *C.char) C.int { 288 | return C.alpm_option_add_noextract(handle, str) 289 | }) 290 | } 291 | 292 | func (h *Handle) SetNoExtracts(hookDirs []string) error { 293 | return h.optionSetList(hookDirs, func(handle *C.alpm_handle_t, l *C.alpm_list_t) C.int { 294 | return C.alpm_option_set_noextracts(handle, l) 295 | }) 296 | } 297 | 298 | func (h *Handle) RemoveNoExtract(dir string) (bool, error) { 299 | return h.optionRemoveList(dir, func(handle *C.alpm_handle_t, str *C.char) C.int { 300 | return C.alpm_option_remove_noextract(handle, str) 301 | }) 302 | } 303 | 304 | func (h *Handle) MatchNoExtract(dir string) (bool, error) { 305 | return h.optionMatchList(dir, func(handle *C.alpm_handle_t, str *C.char) C.int { 306 | return C.alpm_option_match_noextract(handle, str) 307 | }) 308 | } 309 | 310 | func (h *Handle) IgnorePkgs() (StringList, error) { 311 | return h.optionGetList(func(handle *C.alpm_handle_t) *C.alpm_list_t { 312 | return C.alpm_option_get_ignorepkgs(handle) 313 | }) 314 | } 315 | 316 | func (h *Handle) AddIgnorePkg(hookDir string) error { 317 | return h.optionAddList(hookDir, func(handle *C.alpm_handle_t, str *C.char) C.int { 318 | return C.alpm_option_add_ignorepkg(handle, str) 319 | }) 320 | } 321 | 322 | func (h *Handle) SetIgnorePkgs(hookDirs []string) error { 323 | return h.optionSetList(hookDirs, func(handle *C.alpm_handle_t, l *C.alpm_list_t) C.int { 324 | return C.alpm_option_set_ignorepkgs(handle, l) 325 | }) 326 | } 327 | 328 | func (h *Handle) RemoveIgnorePkg(dir string) (bool, error) { 329 | return h.optionRemoveList(dir, func(handle *C.alpm_handle_t, str *C.char) C.int { 330 | return C.alpm_option_remove_ignorepkg(handle, str) 331 | }) 332 | } 333 | 334 | func (h *Handle) IgnoreGroups() (StringList, error) { 335 | return h.optionGetList(func(handle *C.alpm_handle_t) *C.alpm_list_t { 336 | return C.alpm_option_get_ignoregroups(handle) 337 | }) 338 | } 339 | 340 | func (h *Handle) AddIgnoreGroup(hookDir string) error { 341 | return h.optionAddList(hookDir, func(handle *C.alpm_handle_t, str *C.char) C.int { 342 | return C.alpm_option_add_ignoregroup(handle, str) 343 | }) 344 | } 345 | 346 | func (h *Handle) SetIgnoreGroups(hookDirs []string) error { 347 | return h.optionSetList(hookDirs, func(handle *C.alpm_handle_t, l *C.alpm_list_t) C.int { 348 | return C.alpm_option_set_ignoregroups(handle, l) 349 | }) 350 | } 351 | 352 | func (h *Handle) RemoveIgnoreGroup(dir string) (bool, error) { 353 | return h.optionRemoveList(dir, func(handle *C.alpm_handle_t, str *C.char) C.int { 354 | return C.alpm_option_remove_ignoregroup(handle, str) 355 | }) 356 | } 357 | 358 | /*func (h *Handle) optionGetList(f func(*C.alpm_handle_t) *C.alpm_list_t) (StringList, error){ 359 | alpmList := f(h.ptr) 360 | goList := StringList{(*list)(unsafe.Pointer(alpmList))} 361 | 362 | if alpmList == nil { 363 | return goList, h.LastError() 364 | } 365 | return goList, nil 366 | }*/ 367 | 368 | // use alpm_depend_t 369 | func (h *Handle) AssumeInstalled() (IDependList, error) { 370 | alpmList := C.alpm_option_get_assumeinstalled(h.ptr) 371 | depList := DependList{(*list)(unsafe.Pointer(alpmList))} 372 | 373 | if alpmList == nil { 374 | return depList, h.LastError() 375 | } 376 | 377 | return depList, nil 378 | } 379 | 380 | func (h *Handle) AddAssumeInstalled(dep Depend) error { 381 | cDep := convertCDepend(dep) 382 | defer freeCDepend(cDep) 383 | 384 | if ok := C.alpm_option_add_assumeinstalled(h.ptr, cDep); ok < 0 { 385 | return h.LastError() 386 | } 387 | 388 | return nil 389 | } 390 | 391 | // LocalDB returns the local database relative to the given handle. 392 | func (h *Handle) LocalDB() (IDB, error) { 393 | db := C.alpm_get_localdb(h.ptr) 394 | if db == nil { 395 | return nil, h.LastError() 396 | } 397 | 398 | return &DB{db, *h}, nil 399 | } 400 | 401 | // SyncDBs returns list of Synced DBs. 402 | func (h *Handle) SyncDBs() (IDBList, error) { 403 | dblist := C.alpm_get_syncdbs(h.ptr) 404 | if dblist == nil { 405 | return &DBList{nil, *h}, h.LastError() 406 | } 407 | 408 | return &DBList{(*list)(unsafe.Pointer(dblist)), *h}, nil 409 | } 410 | 411 | // NewDBList returns a new empty DB list. 412 | func (h *Handle) NewDBList() IDBList { 413 | return &DBList{nil, *h} 414 | } 415 | 416 | func (h *Handle) CheckSpace() (bool, error) { 417 | ok := C.alpm_option_get_checkspace(h.ptr) 418 | 419 | if ok > 0 { 420 | return true, nil 421 | } 422 | 423 | if ok < 0 { 424 | return false, h.LastError() 425 | } 426 | 427 | return false, nil 428 | } 429 | 430 | func (h *Handle) SetCheckSpace(value bool) error { 431 | var cValue C.int 432 | if value { 433 | cValue = 1 434 | } 435 | 436 | if ok := C.alpm_option_set_checkspace(h.ptr, cValue); ok < 0 { 437 | return h.LastError() 438 | } 439 | 440 | return nil 441 | } 442 | 443 | func (h *Handle) DBExt() (string, error) { 444 | return h.optionGetStr(func(handle *C.alpm_handle_t) *C.char { 445 | return C.alpm_option_get_dbext(handle) 446 | }) 447 | } 448 | 449 | func (h *Handle) SetDBExt(str string) error { 450 | return h.optionSetStr(str, func(handle *C.alpm_handle_t, cStr *C.char) C.int { 451 | return C.alpm_option_set_dbext(handle, cStr) 452 | }) 453 | } 454 | 455 | func (h *Handle) GetDefaultSigLevel() (SigLevel, error) { 456 | sigLevel := C.alpm_option_get_default_siglevel(h.ptr) 457 | 458 | if sigLevel < 0 { 459 | return SigLevel(sigLevel), h.LastError() 460 | } 461 | 462 | return SigLevel(sigLevel), nil 463 | } 464 | 465 | func (h *Handle) SetDefaultSigLevel(siglevel SigLevel) error { 466 | if ok := C.alpm_option_set_default_siglevel(h.ptr, C.int(siglevel)); ok < 0 { 467 | return h.LastError() 468 | } 469 | 470 | return nil 471 | } 472 | 473 | func (h *Handle) GetLocalFileSigLevel() (SigLevel, error) { 474 | sigLevel := C.alpm_option_get_local_file_siglevel(h.ptr) 475 | 476 | if sigLevel < 0 { 477 | return SigLevel(sigLevel), h.LastError() 478 | } 479 | 480 | return SigLevel(sigLevel), nil 481 | } 482 | 483 | func (h *Handle) SetLocalFileSigLevel(siglevel SigLevel) error { 484 | if ok := C.alpm_option_set_local_file_siglevel(h.ptr, C.int(siglevel)); ok < 0 { 485 | return h.LastError() 486 | } 487 | 488 | return nil 489 | } 490 | 491 | func (h *Handle) GetRemoteFileSigLevel() (SigLevel, error) { 492 | sigLevel := C.alpm_option_get_remote_file_siglevel(h.ptr) 493 | if sigLevel < 0 { 494 | return SigLevel(sigLevel), h.LastError() 495 | } 496 | 497 | return SigLevel(sigLevel), nil 498 | } 499 | 500 | func (h *Handle) SetRemoteFileSigLevel(siglevel SigLevel) error { 501 | if ok := C.alpm_option_set_remote_file_siglevel(h.ptr, C.int(siglevel)); ok < 0 { 502 | return h.LastError() 503 | } 504 | 505 | return nil 506 | } 507 | 508 | func (h *Handle) GetArchitectures() (StringList, error) { 509 | return h.optionGetList(func(handle *C.alpm_handle_t) *C.alpm_list_t { 510 | return C.alpm_option_get_architectures(handle) 511 | }) 512 | } 513 | 514 | func (h *Handle) SetArchitectures(str []string) error { 515 | return h.optionSetList(str, func(handle *C.alpm_handle_t, l *C.alpm_list_t) C.int { 516 | return C.alpm_option_set_architectures(handle, l) 517 | }) 518 | } 519 | 520 | func (h *Handle) AddArchitecture(str string) error { 521 | return h.optionAddList(str, func(handle *C.alpm_handle_t, cStr *C.char) C.int { 522 | return C.alpm_option_add_architecture(handle, cStr) 523 | }) 524 | } 525 | 526 | func (h *Handle) RemoveArchitecture(str string) (bool, error) { 527 | return h.optionRemoveList(str, func(handle *C.alpm_handle_t, cStr *C.char) C.int { 528 | return C.alpm_option_remove_architecture(handle, cStr) 529 | }) 530 | } 531 | -------------------------------------------------------------------------------- /interfaces.go: -------------------------------------------------------------------------------- 1 | package alpm 2 | 3 | // #include 4 | import "C" 5 | 6 | import ( 7 | "time" 8 | ) 9 | 10 | // IPackage is an interface type for alpm.Package. 11 | type IPackage interface { 12 | FileName() string 13 | Base() string 14 | Base64Signature() string 15 | Validation() Validation 16 | // Architecture returns the package target Architecture. 17 | Architecture() string 18 | // Backup returns a list of package backups. 19 | Backup() BackupList 20 | // BuildDate returns the BuildDate of the package. 21 | BuildDate() time.Time 22 | // Conflicts returns the conflicts of the package as a DependList. 23 | Conflicts() IDependList 24 | // DB returns the package's origin database. 25 | DB() IDB 26 | // Depends returns the package's dependency list. 27 | Depends() IDependList 28 | // Depends returns the package's optional dependency list. 29 | OptionalDepends() IDependList 30 | // Depends returns the package's check dependency list. 31 | CheckDepends() IDependList 32 | // Depends returns the package's make dependency list. 33 | MakeDepends() IDependList 34 | // Description returns the package's description. 35 | Description() string 36 | // Files returns the file list of the package. 37 | Files() []File 38 | // ContainsFile checks if the path is in the package filelist 39 | ContainsFile(path string) (File, error) 40 | // Groups returns the groups the package belongs to. 41 | Groups() StringList 42 | // ISize returns the package installed size. 43 | ISize() int64 44 | // InstallDate returns the package install date. 45 | InstallDate() time.Time 46 | // Licenses returns the package license list. 47 | Licenses() StringList 48 | // SHA256Sum returns package SHA256Sum. 49 | SHA256Sum() string 50 | // MD5Sum returns package MD5Sum. 51 | MD5Sum() string 52 | // Name returns package name. 53 | Name() string 54 | // Packager returns package packager name. 55 | Packager() string 56 | // Provides returns DependList of packages provides by package. 57 | Provides() IDependList 58 | // Reason returns package install reason. 59 | Reason() PkgReason 60 | // Origin returns package origin. 61 | Origin() PkgFrom 62 | // Replaces returns a DependList with the packages this package replaces. 63 | Replaces() IDependList 64 | // Size returns the packed package size. 65 | Size() int64 66 | // URL returns the upstream URL of the package. 67 | URL() string 68 | // Version returns the package version. 69 | Version() string 70 | // ComputeRequiredBy returns the names of reverse dependencies of a package 71 | ComputeRequiredBy() []string 72 | // ComputeOptionalFor returns the names of packages that optionally 73 | // require the given package 74 | ComputeOptionalFor() []string 75 | ShouldIgnore() bool 76 | 77 | // SyncNewVersion checks if there is a new version of the 78 | // package in a given DBlist. 79 | SyncNewVersion(l IDBList) IPackage 80 | 81 | Type() string 82 | 83 | // Get the underlying alpm pkg type 84 | getPmpkg() *C.alpm_pkg_t 85 | } 86 | 87 | // IPackageList exports the alpm.PackageList symbols. 88 | type IPackageList interface { 89 | // ForEach executes an action on each package of the PackageList. 90 | ForEach(func(IPackage) error) error 91 | // Slice converts the PackageList to a Package Slice. 92 | Slice() []IPackage 93 | // SortBySize returns a PackageList sorted by size. 94 | SortBySize() IPackageList 95 | // FindSatisfier finds a package that satisfies depstring from PkgList 96 | FindSatisfier(string) (IPackage, error) 97 | } 98 | 99 | type IDependList interface { 100 | // ForEach executes an action on each package of the DependList. 101 | ForEach(func(*Depend) error) error 102 | // Slice converts the DependList to a Depend Slice. 103 | Slice() []Depend 104 | } 105 | 106 | // IDB is an interface type for alpm.DB. 107 | type IDB interface { 108 | Unregister() error 109 | // Name returns name of the db 110 | Name() string 111 | // Servers returns host server URL. 112 | Servers() []string 113 | // SetServers sets server list to use. 114 | SetServers(servers []string) 115 | // AddServers adds a string to the server list. 116 | AddServer(server string) 117 | // SetUsage sets the Usage of the database 118 | SetUsage(usage Usage) 119 | // Name searches a package in db. 120 | Pkg(name string) IPackage 121 | // PkgCache returns the list of packages of the database 122 | PkgCache() IPackageList 123 | Search([]string) IPackageList 124 | } 125 | 126 | // IDBList interfaces alpm.DBList. 127 | type IDBList interface { 128 | // ForEach executes an action on each DB. 129 | ForEach(func(IDB) error) error 130 | // Slice converts DB list to DB slice. 131 | Slice() []IDB 132 | // Append modifies DB list with given DB appended. 133 | Append(IDB) 134 | // PkgCachebyGroup returns a PackageList of packages belonging to a group 135 | FindGroupPkgs(string) IPackageList 136 | // FindSatisfier searches a DBList for a package that satisfies depstring 137 | // Example "glibc>=2.12" 138 | FindSatisfier(string) (IPackage, error) 139 | } 140 | -------------------------------------------------------------------------------- /package.go: -------------------------------------------------------------------------------- 1 | // package.go - libalpm package type and methods. 2 | // 3 | // Copyright (c) 2013 The go-alpm Authors 4 | // 5 | // MIT Licensed. See LICENSE for details. 6 | 7 | package alpm 8 | 9 | /* 10 | #include 11 | 12 | int pkg_cmp(const void *v1, const void *v2) 13 | { 14 | alpm_pkg_t *p1 = (alpm_pkg_t *)v1; 15 | alpm_pkg_t *p2 = (alpm_pkg_t *)v2; 16 | off_t s1 = alpm_pkg_get_isize(p1); 17 | off_t s2 = alpm_pkg_get_isize(p2); 18 | 19 | if (s1 > s2) 20 | return -1; 21 | else if (s1 < s2) 22 | return 1; 23 | else 24 | return 0; 25 | } 26 | */ 27 | import "C" 28 | 29 | import ( 30 | "time" 31 | "unsafe" 32 | ) 33 | 34 | // Package describes a single package and associated handle. 35 | type Package struct { 36 | pmpkg *C.alpm_pkg_t 37 | handle Handle 38 | } 39 | 40 | // PackageList describes a linked list of packages and associated handle. 41 | type PackageList struct { 42 | *list 43 | handle Handle 44 | } 45 | 46 | // ForEach executes an action on each package of the PackageList. 47 | func (l PackageList) ForEach(f func(IPackage) error) error { 48 | return l.forEach(func(p unsafe.Pointer) error { 49 | return f(&Package{(*C.alpm_pkg_t)(p), l.handle}) 50 | }) 51 | } 52 | 53 | // Slice converts the PackageList to a Package Slice. 54 | func (l PackageList) Slice() []IPackage { 55 | slice := []IPackage{} 56 | _ = l.ForEach(func(p IPackage) error { 57 | slice = append(slice, p) 58 | return nil 59 | }) 60 | 61 | return slice 62 | } 63 | 64 | // DependList describes a linkedlist of dependency type packages. 65 | type DependList struct{ *list } 66 | 67 | // ForEach executes an action on each package of the DependList. 68 | func (l DependList) ForEach(f func(*Depend) error) error { 69 | return l.forEach(func(p unsafe.Pointer) error { 70 | dep := convertDepend((*C.alpm_depend_t)(p)) 71 | return f(dep) 72 | }) 73 | } 74 | 75 | // Slice converts the DependList to a Depend Slice. 76 | func (l DependList) Slice() []Depend { 77 | slice := []Depend{} 78 | _ = l.ForEach(func(dep *Depend) error { 79 | slice = append(slice, *dep) 80 | return nil 81 | }) 82 | 83 | return slice 84 | } 85 | 86 | func (pkg *Package) FileName() string { 87 | return C.GoString(C.alpm_pkg_get_filename(pkg.pmpkg)) 88 | } 89 | 90 | func (pkg *Package) Base() string { 91 | return C.GoString(C.alpm_pkg_get_base(pkg.pmpkg)) 92 | } 93 | 94 | func (pkg *Package) Base64Signature() string { 95 | return C.GoString(C.alpm_pkg_get_base64_sig(pkg.pmpkg)) 96 | } 97 | 98 | func (pkg *Package) Validation() Validation { 99 | return Validation(C.alpm_pkg_get_validation(pkg.pmpkg)) 100 | } 101 | 102 | // Architecture returns the package target Architecture. 103 | func (pkg *Package) Architecture() string { 104 | return C.GoString(C.alpm_pkg_get_arch(pkg.pmpkg)) 105 | } 106 | 107 | // Backup returns a list of package backups. 108 | func (pkg *Package) Backup() BackupList { 109 | ptr := unsafe.Pointer(C.alpm_pkg_get_backup(pkg.pmpkg)) 110 | return BackupList{(*list)(ptr)} 111 | } 112 | 113 | // BuildDate returns the BuildDate of the package. 114 | func (pkg *Package) BuildDate() time.Time { 115 | t := C.alpm_pkg_get_builddate(pkg.pmpkg) 116 | return time.Unix(int64(t), 0) 117 | } 118 | 119 | // Conflicts returns the conflicts of the package as a DependList. 120 | func (pkg *Package) Conflicts() IDependList { 121 | ptr := unsafe.Pointer(C.alpm_pkg_get_conflicts(pkg.pmpkg)) 122 | return DependList{(*list)(ptr)} 123 | } 124 | 125 | // DB returns the package's origin database. 126 | func (pkg *Package) DB() IDB { 127 | ptr := C.alpm_pkg_get_db(pkg.pmpkg) 128 | if ptr == nil { 129 | return nil 130 | } 131 | 132 | return &DB{ptr, pkg.handle} 133 | } 134 | 135 | // Depends returns the package's dependency list. 136 | func (pkg *Package) Depends() IDependList { 137 | ptr := unsafe.Pointer(C.alpm_pkg_get_depends(pkg.pmpkg)) 138 | return DependList{(*list)(ptr)} 139 | } 140 | 141 | // Depends returns the package's optional dependency list. 142 | func (pkg *Package) OptionalDepends() IDependList { 143 | ptr := unsafe.Pointer(C.alpm_pkg_get_optdepends(pkg.pmpkg)) 144 | return DependList{(*list)(ptr)} 145 | } 146 | 147 | // Depends returns the package's check dependency list. 148 | func (pkg *Package) CheckDepends() IDependList { 149 | ptr := unsafe.Pointer(C.alpm_pkg_get_checkdepends(pkg.pmpkg)) 150 | return DependList{(*list)(ptr)} 151 | } 152 | 153 | // Depends returns the package's make dependency list. 154 | func (pkg *Package) MakeDepends() IDependList { 155 | ptr := unsafe.Pointer(C.alpm_pkg_get_makedepends(pkg.pmpkg)) 156 | return DependList{(*list)(ptr)} 157 | } 158 | 159 | // Description returns the package's description. 160 | func (pkg *Package) Description() string { 161 | return C.GoString(C.alpm_pkg_get_desc(pkg.pmpkg)) 162 | } 163 | 164 | // Files returns the file list of the package. 165 | func (pkg *Package) Files() []File { 166 | cFiles := C.alpm_pkg_get_files(pkg.pmpkg) 167 | return convertFilelist(cFiles) 168 | } 169 | 170 | // ContainsFile checks if the path is in the package filelist 171 | func (pkg *Package) ContainsFile(path string) (File, error) { 172 | return convertFile(C.alpm_filelist_contains(C.alpm_pkg_get_files(pkg.pmpkg), C.CString(path))) 173 | } 174 | 175 | // Groups returns the groups the package belongs to. 176 | func (pkg *Package) Groups() StringList { 177 | ptr := unsafe.Pointer(C.alpm_pkg_get_groups(pkg.pmpkg)) 178 | return StringList{(*list)(ptr)} 179 | } 180 | 181 | // ISize returns the package installed size. 182 | func (pkg *Package) ISize() int64 { 183 | t := C.alpm_pkg_get_isize(pkg.pmpkg) 184 | return int64(t) 185 | } 186 | 187 | // InstallDate returns the package install date. 188 | func (pkg *Package) InstallDate() time.Time { 189 | t := C.alpm_pkg_get_installdate(pkg.pmpkg) 190 | return time.Unix(int64(t), 0) 191 | } 192 | 193 | // Licenses returns the package license list. 194 | func (pkg *Package) Licenses() StringList { 195 | ptr := unsafe.Pointer(C.alpm_pkg_get_licenses(pkg.pmpkg)) 196 | return StringList{(*list)(ptr)} 197 | } 198 | 199 | // SHA256Sum returns package SHA256Sum. 200 | func (pkg *Package) SHA256Sum() string { 201 | return C.GoString(C.alpm_pkg_get_sha256sum(pkg.pmpkg)) 202 | } 203 | 204 | // MD5Sum returns package MD5Sum. 205 | func (pkg *Package) MD5Sum() string { 206 | return C.GoString(C.alpm_pkg_get_md5sum(pkg.pmpkg)) 207 | } 208 | 209 | // Name returns package name. 210 | func (pkg *Package) Name() string { 211 | return C.GoString(C.alpm_pkg_get_name(pkg.pmpkg)) 212 | } 213 | 214 | // Packager returns package packager name. 215 | func (pkg *Package) Packager() string { 216 | return C.GoString(C.alpm_pkg_get_packager(pkg.pmpkg)) 217 | } 218 | 219 | // Provides returns DependList of packages provides by package. 220 | func (pkg *Package) Provides() IDependList { 221 | ptr := unsafe.Pointer(C.alpm_pkg_get_provides(pkg.pmpkg)) 222 | return DependList{(*list)(ptr)} 223 | } 224 | 225 | // Reason returns package install reason. 226 | func (pkg *Package) Reason() PkgReason { 227 | reason := C.alpm_pkg_get_reason(pkg.pmpkg) 228 | return PkgReason(reason) 229 | } 230 | 231 | // Origin returns package origin. 232 | func (pkg *Package) Origin() PkgFrom { 233 | origin := C.alpm_pkg_get_origin(pkg.pmpkg) 234 | return PkgFrom(origin) 235 | } 236 | 237 | // Replaces returns a DependList with the packages this package replaces. 238 | func (pkg *Package) Replaces() IDependList { 239 | ptr := unsafe.Pointer(C.alpm_pkg_get_replaces(pkg.pmpkg)) 240 | return DependList{(*list)(ptr)} 241 | } 242 | 243 | // Size returns the packed package size. 244 | func (pkg *Package) Size() int64 { 245 | t := C.alpm_pkg_get_size(pkg.pmpkg) 246 | return int64(t) 247 | } 248 | 249 | // URL returns the upstream URL of the package. 250 | func (pkg *Package) URL() string { 251 | return C.GoString(C.alpm_pkg_get_url(pkg.pmpkg)) 252 | } 253 | 254 | // Version returns the package version. 255 | func (pkg *Package) Version() string { 256 | return C.GoString(C.alpm_pkg_get_version(pkg.pmpkg)) 257 | } 258 | 259 | // ComputeRequiredBy returns the names of reverse dependencies of a package 260 | func (pkg *Package) ComputeRequiredBy() []string { 261 | result := C.alpm_pkg_compute_requiredby(pkg.pmpkg) 262 | requiredby := make([]string, 0) 263 | 264 | for i := (*list)(unsafe.Pointer(result)); i != nil; i = i.Next { 265 | if i.Data != nil { 266 | name := C.GoString((*C.char)(i.Data)) 267 | requiredby = append(requiredby, name) 268 | 269 | C.free(i.Data) 270 | } 271 | 272 | C.free(unsafe.Pointer(i)) 273 | } 274 | 275 | return requiredby 276 | } 277 | 278 | // ComputeOptionalFor returns the names of packages that optionally require the given package 279 | func (pkg *Package) ComputeOptionalFor() []string { 280 | result := C.alpm_pkg_compute_optionalfor(pkg.pmpkg) 281 | optionalfor := make([]string, 0) 282 | 283 | for i := (*list)(unsafe.Pointer(result)); i != nil; i = i.Next { 284 | if i.Data != nil { 285 | name := C.GoString((*C.char)(i.Data)) 286 | optionalfor = append(optionalfor, name) 287 | 288 | C.free(i.Data) 289 | } 290 | 291 | C.free(unsafe.Pointer(i)) 292 | } 293 | 294 | return optionalfor 295 | } 296 | 297 | func (pkg *Package) ShouldIgnore() bool { 298 | result := C.alpm_pkg_should_ignore(pkg.handle.ptr, pkg.pmpkg) 299 | return result == 1 300 | } 301 | 302 | func (pkg *Package) Type() string { 303 | return "alpm" 304 | } 305 | 306 | func (pkg *Package) getPmpkg() *C.alpm_pkg_t { 307 | return pkg.pmpkg 308 | } 309 | 310 | // SortBySize returns a PackageList sorted by size. 311 | func (l PackageList) SortBySize() IPackageList { 312 | pkgList := (*C.struct__alpm_list_t)(unsafe.Pointer(l.list)) 313 | 314 | pkgCache := (*list)(unsafe.Pointer( 315 | C.alpm_list_msort(pkgList, //nolint 316 | C.alpm_list_count(pkgList), 317 | C.alpm_list_fn_cmp(C.pkg_cmp)))) 318 | if pkgCache == nil { 319 | return nil 320 | } 321 | 322 | return PackageList{pkgCache, l.handle} 323 | } 324 | -------------------------------------------------------------------------------- /package_test.go: -------------------------------------------------------------------------------- 1 | // package_test.go - Tests for package.go 2 | // 3 | // Copyright (c) 2013 The go-alpm Authors 4 | // 5 | // MIT Licensed. See LICENSE for details. 6 | 7 | package alpm_test 8 | 9 | import ( 10 | "bytes" 11 | "testing" 12 | "text/template" 13 | "time" 14 | 15 | alpm "github.com/Jguer/go-alpm/v2" 16 | 17 | "github.com/stretchr/testify/assert" 18 | ) 19 | 20 | // Auxiliary formatting 21 | const pkginfoTemplate = ` 22 | Name : {{ .Name }} 23 | Version : {{ .Version }} 24 | Architecture : {{ .Architecture }} 25 | Description : {{ .Description }} 26 | URL : {{ .URL }} 27 | Groups : {{ .Groups.Slice }} 28 | Licenses : {{ .Licenses.Slice }} 29 | Dependencies : {{ range .Depends.Slice }}{{ . }} {{ end }} 30 | Provides : {{ range .Provides.Slice }}{{ . }} {{ end }} 31 | Replaces : {{ range .Replaces.Slice }}{{ . }} {{ end }} 32 | Conflicts : {{ range .Conflicts.Slice }}{{ . }} {{ end }} 33 | Packager : {{ .Packager }} 34 | Build Date : {{ .PrettyBuildDate }} 35 | Install Date : {{ .PrettyInstallDate }} 36 | Package Size : {{ .Size }} bytes 37 | Install Size : {{ .ISize }} bytes 38 | MD5 Sum : {{ .MD5Sum }} 39 | SHA256 Sum : {{ .SHA256Sum }} 40 | Reason : {{ .Reason }} 41 | 42 | Required By : {{ .ComputeRequiredBy }} 43 | Files : {{ range .Files }} 44 | {{ .Name }} {{ .Size }}{{ end }} 45 | ` 46 | 47 | type PrettyPackage struct { 48 | *alpm.Package 49 | } 50 | 51 | func (p PrettyPackage) PrettyBuildDate() string { 52 | return p.Package.BuildDate().Format(time.RFC1123) 53 | } 54 | 55 | func (p PrettyPackage) PrettyInstallDate() string { 56 | return p.Package.InstallDate().Format(time.RFC1123) 57 | } 58 | 59 | // Tests package attribute getters. 60 | func TestPkginfo(t *testing.T) { 61 | t.Parallel() 62 | pkginfoTemp, er := template.New("info").Parse(pkginfoTemplate) 63 | assert.NoError(t, er, "couldn't compile template") 64 | 65 | h, er := alpm.Initialize(root, dbpath) 66 | defer h.Release() 67 | if er != nil { 68 | t.Errorf("Failed at alpm initialization: %s", er) 69 | } 70 | 71 | db, _ := h.LocalDB() 72 | 73 | pkg := db.Pkg("glibc") 74 | buf := bytes.NewBuffer(nil) 75 | pkginfoTemp.Execute(buf, PrettyPackage{pkg.(*alpm.Package)}) 76 | 77 | pkg = db.Pkg("linux") 78 | if pkg != nil { 79 | buf = bytes.NewBuffer(nil) 80 | pkginfoTemp.Execute(buf, PrettyPackage{pkg.(*alpm.Package)}) 81 | } 82 | } 83 | 84 | func TestPkgNoExist(t *testing.T) { 85 | t.Parallel() 86 | h, er := alpm.Initialize(root, dbpath) 87 | defer h.Release() 88 | if er != nil { 89 | t.Errorf("Failed at alpm initialization: %s", er) 90 | } 91 | 92 | db, _ := h.LocalDB() 93 | 94 | pkg := db.Pkg("non-existing-package-fa93f4af") 95 | if pkg != nil { 96 | t.Errorf("pkg should be nil but got %v", pkg) 97 | } 98 | } 99 | 100 | func TestPkgFiles(t *testing.T) { 101 | t.Parallel() 102 | h, er := alpm.Initialize(root, dbpath) 103 | defer h.Release() 104 | if er != nil { 105 | t.Errorf("Failed at alpm initialization: %s", er) 106 | } 107 | 108 | db, _ := h.LocalDB() 109 | 110 | pkg := db.Pkg("glibc") 111 | _, err := pkg.ContainsFile("etc/locale.gen") 112 | if err != nil { 113 | t.Errorf("File should not be nil but got %v", err) 114 | } 115 | _, err = pkg.ContainsFile("etc/does-not-exist") 116 | if err == nil { 117 | t.Errorf("File should be nil but got %v", err) 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /sync.go: -------------------------------------------------------------------------------- 1 | // db.go - Functions for database handling. 2 | // 3 | // Copyright (c) 2013 The go-alpm Authors 4 | // 5 | // MIT Licensed. See LICENSE for details. 6 | 7 | package alpm 8 | 9 | /* 10 | #include 11 | */ 12 | import "C" 13 | 14 | import "unsafe" 15 | 16 | // SyncNewVersion checks if there is a new version of the 17 | // package in a given DBlist. 18 | func (pkg *Package) SyncNewVersion(l IDBList) IPackage { 19 | ptr := C.alpm_sync_get_new_version(pkg.pmpkg, (*C.alpm_list_t)(unsafe.Pointer(l.(*DBList).list))) 20 | if ptr == nil { 21 | return nil 22 | } 23 | 24 | return &Package{ptr, l.(*DBList).handle} 25 | } 26 | 27 | func (h *Handle) SyncSysupgrade(enableDowngrade bool) error { 28 | intEnableDowngrade := C.int(0) 29 | 30 | if enableDowngrade { 31 | intEnableDowngrade = C.int(1) 32 | } 33 | 34 | ret := C.alpm_sync_sysupgrade(h.ptr, intEnableDowngrade) 35 | if ret != 0 { 36 | return h.LastError() 37 | } 38 | 39 | return nil 40 | } 41 | -------------------------------------------------------------------------------- /testdata/README.md: -------------------------------------------------------------------------------- 1 | This directory contains testing related files. 2 | -------------------------------------------------------------------------------- /testdata/examples/Makefile: -------------------------------------------------------------------------------- 1 | all: alpm-installed alpm-search alpm-updates alpm-sync 2 | 3 | alpm-installed: installed.go 4 | go build -x -o $@ $< 5 | 6 | alpm-search: search.go 7 | go build -x -o $@ $< 8 | 9 | alpm-updates: updates.go 10 | go build -x -o $@ $< 11 | 12 | alpm-sync: sync.go 13 | go build -x -o $@ $< 14 | 15 | clean: 16 | rm -f alpm-installed alpm-search alpm-updates 17 | -------------------------------------------------------------------------------- /testdata/examples/README.md: -------------------------------------------------------------------------------- 1 | To build the examples, use make. The alpm library must be in your GOPATH. 2 | -------------------------------------------------------------------------------- /testdata/examples/depends/depends.go: -------------------------------------------------------------------------------- 1 | // installed.go - Example of getting a list of installed packages. 2 | // 3 | // Copyright (c) 2013 The go-alpm Authors 4 | // 5 | // MIT Licensed. See LICENSE for details. 6 | 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "os" 12 | 13 | "github.com/Jguer/go-alpm/v2" 14 | ) 15 | 16 | func main() { 17 | h, er := alpm.Initialize("/", "/var/lib/pacman") 18 | if er != nil { 19 | print(er, "\n") 20 | os.Exit(1) 21 | } 22 | 23 | db, er := h.LocalDB() 24 | if er != nil { 25 | fmt.Println(er) 26 | os.Exit(1) 27 | } 28 | 29 | for _, pkg := range db.PkgCache().Slice() { 30 | fmt.Printf("%s %s\n", pkg.Name(), pkg.Version()) 31 | fmt.Println(pkg.Depends().Slice()) 32 | break 33 | } 34 | 35 | if h.Release() != nil { 36 | os.Exit(1) 37 | } 38 | 39 | os.Exit(0) 40 | } 41 | -------------------------------------------------------------------------------- /testdata/examples/installed/installed.go: -------------------------------------------------------------------------------- 1 | // installed.go - Example of getting a list of installed packages. 2 | // 3 | // Copyright (c) 2013 The go-alpm Authors 4 | // 5 | // MIT Licensed. See LICENSE for details. 6 | 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "os" 12 | 13 | "github.com/Jguer/go-alpm/v2" 14 | ) 15 | 16 | func main() { 17 | h, er := alpm.Initialize("/", "/var/lib/pacman") 18 | if er != nil { 19 | print(er, "\n") 20 | os.Exit(1) 21 | } 22 | 23 | db, er := h.LocalDB() 24 | if er != nil { 25 | fmt.Println(er) 26 | os.Exit(1) 27 | } 28 | 29 | for _, pkg := range db.PkgCache().Slice() { 30 | fmt.Printf("%s %s\n", pkg.Name(), pkg.Version()) 31 | } 32 | 33 | if h.Release() != nil { 34 | os.Exit(1) 35 | } 36 | 37 | os.Exit(0) 38 | } 39 | -------------------------------------------------------------------------------- /testdata/examples/search/search.go: -------------------------------------------------------------------------------- 1 | // 2 | // 3 | // Copyright (c) 2013 The go-alpm Authors 4 | // 5 | // MIT Licensed. See LICENSE for details. 6 | 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | 12 | "github.com/Jguer/go-alpm/v2" 13 | ) 14 | 15 | func main() { 16 | h, er := alpm.Initialize("/", "/var/lib/pacman") 17 | if er != nil { 18 | fmt.Println(er) 19 | return 20 | } 21 | defer h.Release() 22 | 23 | db, _ := h.RegisterSyncDB("core", 0) 24 | h.RegisterSyncDB("community", 0) 25 | h.RegisterSyncDB("extra", 0) 26 | 27 | for _, pkg := range db.PkgCache().Slice() { 28 | fmt.Printf("%s %s\n %s\n", 29 | pkg.Name(), pkg.Version(), pkg.Description()) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /testdata/examples/sync/sync.go: -------------------------------------------------------------------------------- 1 | // sync.go - Example of installing, and removing packages. 2 | // 3 | // Copyright (c) 2013 The go-alpm Authors 4 | // 5 | // MIT Licensed. See LICENSE for details. 6 | 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "log" 12 | 13 | "github.com/Jguer/go-alpm/v2" 14 | "github.com/Morganamilo/go-pacmanconf" 15 | ) 16 | 17 | func main() { 18 | h, err := alpm.Initialize("/", "/var/lib/pacman") 19 | if err != nil { 20 | fmt.Println(err) 21 | return 22 | } 23 | 24 | defer func() { 25 | if err := h.Release(); err != nil { 26 | fmt.Println(err) 27 | return 28 | } 29 | }() 30 | 31 | conf, _, err := pacmanconf.ParseFile("/etc/pacman.conf") 32 | if err != nil { 33 | fmt.Println(err) 34 | return 35 | } 36 | 37 | fmt.Println("loading databases") 38 | 39 | var addPkg alpm.IPackage 40 | var removePkg alpm.IPackage 41 | 42 | for _, repo := range conf.Repos { 43 | db, err := h.RegisterSyncDB(repo.Name, 0) 44 | if err != nil { 45 | fmt.Println(err) 46 | return 47 | } 48 | db.SetServers(repo.Servers) 49 | 50 | foundPkg := db.Pkg("vim") 51 | if foundPkg != nil { 52 | addPkg = foundPkg 53 | } 54 | } 55 | 56 | localDb, err := h.LocalDB() 57 | if err != nil { 58 | log.Println(err) 59 | return 60 | } 61 | // A package can only be removed if it is in the local DB 62 | removePkg = localDb.Pkg("vi") 63 | 64 | fmt.Println("initializing transaction") 65 | if err := h.TransInit(0); err != nil { 66 | fmt.Println(err) 67 | return 68 | } 69 | 70 | defer func() { 71 | fmt.Println("releasing transaction") 72 | if err := h.TransRelease(); err != nil { 73 | fmt.Println(err) 74 | return 75 | } 76 | }() 77 | 78 | fmt.Println("synchronizing databases") 79 | if err := h.SyncSysupgrade(true); err != nil { 80 | fmt.Println(err) 81 | return 82 | } 83 | 84 | fmt.Println("adding package") 85 | if err := h.AddPkg(addPkg); err != nil { 86 | fmt.Println(err) 87 | return 88 | } 89 | 90 | fmt.Println("removing package") 91 | if removePkg != nil { 92 | if err := h.RemovePkg(removePkg); err != nil { 93 | fmt.Println(err) 94 | return 95 | } 96 | } else { 97 | fmt.Println("vi is not installed") 98 | } 99 | 100 | fmt.Println("preparing transaction") 101 | if err := h.TransPrepare(); err != nil { 102 | fmt.Println(err) 103 | return 104 | } 105 | 106 | fmt.Println("committing transaction") 107 | if err := h.TransCommit(); err != nil { 108 | fmt.Println(err) 109 | return 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /testdata/examples/updates/updates.go: -------------------------------------------------------------------------------- 1 | // 2 | // 3 | // Copyright (c) 2013 The go-alpm Authors 4 | // 5 | // MIT Licensed. See LICENSE for details. 6 | 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "log" 12 | 13 | paconf "github.com/Morganamilo/go-pacmanconf" 14 | 15 | "github.com/Jguer/go-alpm/v2" 16 | ) 17 | 18 | func human(size int64) string { 19 | floatsize := float32(size) 20 | units := [...]string{"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi"} 21 | for _, unit := range units { 22 | if floatsize < 1024 { 23 | return fmt.Sprintf("%.1f %sB", floatsize, unit) 24 | } 25 | floatsize /= 1024 26 | } 27 | return fmt.Sprintf("%d%s", size, "B") 28 | } 29 | 30 | func upgrades(h *alpm.Handle) ([]alpm.IPackage, error) { 31 | localDb, err := h.LocalDB() 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | syncDbs, err := h.SyncDBs() 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | slice := []alpm.IPackage{} 42 | for _, pkg := range localDb.PkgCache().Slice() { 43 | newPkg := pkg.SyncNewVersion(syncDbs) 44 | if newPkg != nil { 45 | slice = append(slice, newPkg) 46 | } 47 | } 48 | return slice, nil 49 | } 50 | 51 | func main() { 52 | h, er := alpm.Initialize("/", "/var/lib/pacman") 53 | if er != nil { 54 | fmt.Println(er) 55 | return 56 | } 57 | defer h.Release() 58 | 59 | PacmanConfig, _, err := paconf.ParseFile("/etc/pacman.conf") 60 | if err != nil { 61 | fmt.Println(err) 62 | return 63 | } 64 | 65 | /* 66 | We have to configure alpm with pacman configuration 67 | to load the repositories and other stuff 68 | */ 69 | for _, repo := range PacmanConfig.Repos { 70 | db, err := h.RegisterSyncDB(repo.Name, 0) 71 | if err != nil { 72 | fmt.Println(err) 73 | return 74 | } 75 | db.SetServers(repo.Servers) 76 | 77 | /* 78 | Configure repository usage to match with 79 | the alpm library provided formats 80 | */ 81 | if len(repo.Usage) == 0 { 82 | db.SetUsage(alpm.UsageAll) 83 | } 84 | for _, usage := range repo.Usage { 85 | switch usage { 86 | case "Sync": 87 | db.SetUsage(alpm.UsageSync) 88 | case "Search": 89 | db.SetUsage(alpm.UsageSearch) 90 | case "Install": 91 | db.SetUsage(alpm.UsageInstall) 92 | case "Upgrade": 93 | db.SetUsage(alpm.UsageUpgrade) 94 | case "All": 95 | db.SetUsage(alpm.UsageAll) 96 | } 97 | } 98 | } 99 | 100 | upgrades, err := upgrades(h) 101 | if err != nil { 102 | log.Fatalln(err) 103 | } 104 | 105 | var size int64 = 0 106 | for _, pkg := range upgrades { 107 | size += pkg.Size() 108 | fmt.Printf("%s %s -> %s\n", pkg.Name(), pkg.Version(), 109 | pkg.Version()) 110 | } 111 | fmt.Printf("Total Download Size: %s\n", human(size)) 112 | } 113 | -------------------------------------------------------------------------------- /trans.go: -------------------------------------------------------------------------------- 1 | // db.go - Functions for database handling. 2 | // 3 | // Copyright (c) 2013 The go-alpm Authors 4 | // 5 | // MIT Licensed. See LICENSE for details. 6 | 7 | package alpm 8 | 9 | /* 10 | #include 11 | */ 12 | import "C" 13 | 14 | import ( 15 | "unsafe" 16 | ) 17 | 18 | func (h *Handle) TransInit(flags TransFlag) error { 19 | ret := C.alpm_trans_init(h.ptr, C.int(flags)) 20 | if ret != 0 { 21 | return h.LastError() 22 | } 23 | 24 | return nil 25 | } 26 | 27 | func (h *Handle) TransPrepare() error { 28 | var data *C.alpm_list_t 29 | ret := C.alpm_trans_prepare(h.ptr, &data) 30 | if ret != 0 { 31 | return h.LastError() 32 | } 33 | 34 | return nil 35 | } 36 | 37 | func (h *Handle) TransInterrupt() error { 38 | ret := C.alpm_trans_interrupt(h.ptr) 39 | if ret != 0 { 40 | return h.LastError() 41 | } 42 | 43 | return nil 44 | } 45 | 46 | func (h *Handle) TransCommit() error { 47 | ret := C.alpm_trans_commit(h.ptr, nil) 48 | if ret != 0 { 49 | return h.LastError() 50 | } 51 | 52 | return nil 53 | } 54 | 55 | func (h *Handle) TransRelease() error { 56 | ret := C.alpm_trans_release(h.ptr) 57 | if ret != 0 { 58 | return h.LastError() 59 | } 60 | 61 | return nil 62 | } 63 | 64 | func (h *Handle) TransGetAdd() PackageList { 65 | pkgs := C.alpm_trans_get_add(h.ptr) 66 | return PackageList{(*list)(unsafe.Pointer(pkgs)), *h} 67 | } 68 | 69 | func (h *Handle) TransGetRemove() PackageList { 70 | pkgs := C.alpm_trans_get_remove(h.ptr) 71 | return PackageList{(*list)(unsafe.Pointer(pkgs)), *h} 72 | } 73 | 74 | func (h *Handle) TransGetFlags() (TransFlag, error) { 75 | flags := C.alpm_trans_get_flags(h.ptr) 76 | 77 | if flags == -1 { 78 | return -1, h.LastError() 79 | } 80 | 81 | return TransFlag(flags), nil 82 | } 83 | 84 | func (h *Handle) AddPkg(pkg IPackage) error { 85 | ret := C.alpm_add_pkg(h.ptr, pkg.getPmpkg()) 86 | if ret != 0 { 87 | return h.LastError() 88 | } 89 | 90 | return nil 91 | } 92 | 93 | func (h *Handle) RemovePkg(pkg IPackage) error { 94 | ret := C.alpm_remove_pkg(h.ptr, pkg.getPmpkg()) 95 | if ret != 0 { 96 | return h.LastError() 97 | } 98 | 99 | return nil 100 | } 101 | -------------------------------------------------------------------------------- /types.go: -------------------------------------------------------------------------------- 1 | // types.go - libalpm types. 2 | // 3 | // Copyright (c) 2013 The go-alpm Authors 4 | // 5 | // MIT Licensed. See LICENSE for details. 6 | 7 | package alpm 8 | 9 | // #cgo CFLAGS: -D_FILE_OFFSET_BITS=64 10 | // #include 11 | import "C" 12 | 13 | import ( 14 | "errors" 15 | "fmt" 16 | "unsafe" 17 | ) 18 | 19 | // Depend provides a description of a dependency. 20 | type Depend struct { 21 | Name string 22 | Version string 23 | Description string 24 | NameHash uint 25 | Mod DepMod 26 | } 27 | 28 | func convertDepend(dep *C.alpm_depend_t) *Depend { 29 | return &Depend{ 30 | Name: C.GoString(dep.name), 31 | Version: C.GoString(dep.version), 32 | Mod: DepMod(dep.mod), 33 | Description: C.GoString(dep.desc), 34 | NameHash: uint(dep.name_hash), 35 | } 36 | } 37 | 38 | func convertCDepend(dep Depend) *C.alpm_depend_t { 39 | cName := C.CString(dep.Name) 40 | cVersion := C.CString(dep.Version) 41 | cDesc := C.CString(dep.Description) 42 | 43 | cDep := C.alpm_depend_t{ 44 | name: cName, 45 | version: cVersion, 46 | desc: cDesc, 47 | name_hash: C.ulong(dep.NameHash), 48 | mod: C.alpm_depmod_t(dep.Mod), 49 | } 50 | 51 | return &cDep 52 | } 53 | 54 | func freeCDepend(dep *C.alpm_depend_t) { 55 | C.free(unsafe.Pointer(dep.name)) 56 | C.free(unsafe.Pointer(dep.version)) 57 | C.free(unsafe.Pointer(dep.desc)) 58 | } 59 | 60 | func (dep Depend) String() string { 61 | return dep.Name + dep.Mod.String() + dep.Version 62 | } 63 | 64 | // File provides a description of package files. 65 | type File struct { 66 | Name string 67 | Size int64 68 | Mode uint32 69 | } 70 | 71 | func convertFile(file *C.alpm_file_t) (File, error) { 72 | if file == nil { 73 | return File{}, errors.New("no file") 74 | } 75 | 76 | return File{ 77 | Name: C.GoString(file.name), 78 | Size: int64(file.size), 79 | Mode: uint32(file.mode), 80 | }, nil 81 | } 82 | 83 | func convertFilelist(files *C.alpm_filelist_t) []File { 84 | size := int(files.count) 85 | items := make([]File, size) 86 | 87 | cFiles := unsafe.Slice(files.files, size) 88 | 89 | for i := 0; i < size; i++ { 90 | if file, err := convertFile(&cFiles[i]); err == nil { 91 | items[i] = file 92 | } 93 | } 94 | 95 | return items 96 | } 97 | 98 | // Internal alpm list structure. 99 | type list struct { 100 | Data unsafe.Pointer 101 | Prev *list 102 | Next *list 103 | } 104 | 105 | // Iterates a function on a list and stop on error. 106 | func (l *list) forEach(f func(unsafe.Pointer) error) error { 107 | for ; l != nil; l = l.Next { 108 | err := f(l.Data) 109 | if err != nil { 110 | return err 111 | } 112 | } 113 | 114 | return nil 115 | } 116 | 117 | func (l *list) Len() int { 118 | count := 0 119 | for ; l != nil; l = l.Next { 120 | count++ 121 | } 122 | 123 | return count 124 | } 125 | 126 | func (l *list) Empty() bool { 127 | return l == nil 128 | } 129 | 130 | type StringList struct { 131 | *list 132 | } 133 | 134 | func (l StringList) ForEach(f func(string) error) error { 135 | return l.forEach(func(p unsafe.Pointer) error { 136 | return f(C.GoString((*C.char)(p))) 137 | }) 138 | } 139 | 140 | func (l StringList) Slice() []string { 141 | slice := []string{} 142 | _ = l.ForEach(func(s string) error { 143 | slice = append(slice, s) 144 | return nil 145 | }) 146 | 147 | return slice 148 | } 149 | 150 | type BackupFile struct { 151 | Name string 152 | Hash string 153 | } 154 | 155 | type BackupList struct { 156 | *list 157 | } 158 | 159 | func (l BackupList) ForEach(f func(BackupFile) error) error { 160 | return l.forEach(func(p unsafe.Pointer) error { 161 | bf := (*C.alpm_backup_t)(p) 162 | return f(BackupFile{ 163 | Name: C.GoString(bf.name), 164 | Hash: C.GoString(bf.hash), 165 | }) 166 | }) 167 | } 168 | 169 | func (l BackupList) Slice() (slice []BackupFile) { 170 | _ = l.ForEach(func(f BackupFile) error { 171 | slice = append(slice, f) 172 | return nil 173 | }) 174 | 175 | return 176 | } 177 | 178 | type QuestionAny struct { 179 | ptr *C.alpm_question_any_t 180 | } 181 | 182 | func (question QuestionAny) SetAnswer(answer bool) { 183 | if answer { 184 | question.ptr.answer = 1 185 | } else { 186 | question.ptr.answer = 0 187 | } 188 | } 189 | 190 | type QuestionInstallIgnorepkg struct { 191 | ptr *C.alpm_question_install_ignorepkg_t 192 | } 193 | 194 | func (question QuestionAny) Type() QuestionType { 195 | return QuestionType(question.ptr._type) 196 | } 197 | 198 | func (question QuestionAny) Answer() bool { 199 | return question.ptr.answer == 1 200 | } 201 | 202 | func (question QuestionAny) QuestionInstallIgnorepkg() (QuestionInstallIgnorepkg, error) { 203 | if question.Type() == QuestionTypeInstallIgnorepkg { 204 | return *(*QuestionInstallIgnorepkg)(unsafe.Pointer(&question)), nil 205 | } 206 | 207 | return QuestionInstallIgnorepkg{}, fmt.Errorf("cannot convert to QuestionInstallIgnorepkg") 208 | } 209 | 210 | func (question QuestionAny) QuestionSelectProvider() (QuestionSelectProvider, error) { 211 | if question.Type() == QuestionTypeSelectProvider { 212 | return *(*QuestionSelectProvider)(unsafe.Pointer(&question)), nil 213 | } 214 | 215 | return QuestionSelectProvider{}, fmt.Errorf("cannot convert to QuestionInstallIgnorepkg") 216 | } 217 | 218 | func (question QuestionAny) QuestionReplace() (QuestionReplace, error) { 219 | if question.Type() == QuestionTypeReplacePkg { 220 | return *(*QuestionReplace)(unsafe.Pointer(&question)), nil 221 | } 222 | 223 | return QuestionReplace{}, fmt.Errorf("cannot convert to QuestionReplace") 224 | } 225 | 226 | func (question QuestionInstallIgnorepkg) SetInstall(install bool) { 227 | if install { 228 | question.ptr.install = 1 229 | } else { 230 | question.ptr.install = 0 231 | } 232 | } 233 | 234 | func (question QuestionInstallIgnorepkg) Type() QuestionType { 235 | return QuestionType(question.ptr._type) 236 | } 237 | 238 | func (question QuestionInstallIgnorepkg) Install() bool { 239 | return question.ptr.install == 1 240 | } 241 | 242 | func (question QuestionInstallIgnorepkg) Pkg(h *Handle) IPackage { 243 | return &Package{ 244 | question.ptr.pkg, 245 | *h, 246 | } 247 | } 248 | 249 | type QuestionReplace struct { 250 | ptr *C.alpm_question_replace_t 251 | } 252 | 253 | func (question QuestionReplace) Type() QuestionType { 254 | return QuestionType(question.ptr._type) 255 | } 256 | 257 | func (question QuestionReplace) SetReplace(replace bool) { 258 | if replace { 259 | question.ptr.replace = 1 260 | } else { 261 | question.ptr.replace = 0 262 | } 263 | } 264 | 265 | func (question QuestionReplace) Replace() bool { 266 | return question.ptr.replace == 1 267 | } 268 | 269 | func (question QuestionReplace) NewPkg(h *Handle) IPackage { 270 | return &Package{ 271 | question.ptr.newpkg, 272 | *h, 273 | } 274 | } 275 | 276 | func (question QuestionReplace) OldPkg(h *Handle) IPackage { 277 | return &Package{ 278 | question.ptr.oldpkg, 279 | *h, 280 | } 281 | } 282 | 283 | type QuestionSelectProvider struct { 284 | ptr *C.alpm_question_select_provider_t 285 | } 286 | 287 | func (question QuestionSelectProvider) Type() QuestionType { 288 | return QuestionType(question.ptr._type) 289 | } 290 | 291 | func (question QuestionSelectProvider) SetUseIndex(index int) { 292 | question.ptr.use_index = C.int(index) 293 | } 294 | 295 | func (question QuestionSelectProvider) UseIndex() int { 296 | return int(question.ptr.use_index) 297 | } 298 | 299 | func (question QuestionSelectProvider) Providers(h *Handle) IPackageList { 300 | return PackageList{ 301 | (*list)(unsafe.Pointer(question.ptr.providers)), 302 | *h, 303 | } 304 | } 305 | 306 | func (question QuestionSelectProvider) Dep() *Depend { 307 | return convertDepend(question.ptr.depend) 308 | } 309 | -------------------------------------------------------------------------------- /types_test.go: -------------------------------------------------------------------------------- 1 | package alpm_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | 8 | alpm "github.com/Jguer/go-alpm/v2" 9 | ) 10 | 11 | func TestPkgFilesList(t *testing.T) { 12 | t.Parallel() 13 | const ( 14 | root = "/" 15 | dbpath = "/var/lib/pacman" 16 | ) 17 | 18 | h, er := alpm.Initialize(root, dbpath) 19 | defer h.Release() 20 | if er != nil { 21 | t.Errorf("Failed at alpm initialization: %s", er) 22 | } 23 | 24 | db, _ := h.LocalDB() 25 | 26 | pkg := db.Pkg("glibc") 27 | 28 | files := pkg.Files() 29 | assert.NotEmpty(t, files) 30 | assert.NotNil(t, files[0]) 31 | } 32 | -------------------------------------------------------------------------------- /version.go: -------------------------------------------------------------------------------- 1 | package alpm 2 | 3 | // #include 4 | import "C" 5 | import "unsafe" 6 | 7 | // VerCmp performs version comparison according to Pacman conventions. Return 8 | // value is <0 if and only if v1 is older than v2. 9 | func VerCmp(v1, v2 string) int { 10 | c1 := C.CString(v1) 11 | c2 := C.CString(v2) 12 | 13 | defer C.free(unsafe.Pointer(c1)) 14 | defer C.free(unsafe.Pointer(c2)) 15 | 16 | result := C.alpm_pkg_vercmp(c1, c2) 17 | 18 | return int(result) 19 | } 20 | --------------------------------------------------------------------------------