├── apt-pkg-c ├── .gitignore ├── Makefile └── lib.cpp ├── examples ├── on-sid │ ├── .dockerignore │ ├── sources.list │ ├── Makefile │ └── Dockerfile ├── on-trusty │ ├── .dockerignore │ ├── Makefile │ ├── Dockerfile │ └── uprust.sh ├── epochs.rs ├── list.rs ├── policy.rs └── sources.rs ├── .gitignore ├── ci.Dockerfile ├── Cargo.toml ├── .github └── workflows │ └── rust.yml ├── LICENSE.MIT ├── Cargo.lock ├── README.md └── src ├── lib.rs ├── citer.rs ├── simple.rs ├── raw.rs └── sane.rs /apt-pkg-c/.gitignore: -------------------------------------------------------------------------------- 1 | lib.o 2 | -------------------------------------------------------------------------------- /examples/on-sid/.dockerignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | -------------------------------------------------------------------------------- /examples/on-trusty/.dockerignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | **/*.rs.bk 3 | .idea 4 | *.iml 5 | apt-c/lib.o 6 | apt-c/libapt-c.a 7 | -------------------------------------------------------------------------------- /examples/on-sid/sources.list: -------------------------------------------------------------------------------- 1 | deb http://deb.debian.org/debian sid main contrib non-free 2 | deb-src http://deb.debian.org/debian sid main contrib non-free 3 | -------------------------------------------------------------------------------- /apt-pkg-c/Makefile: -------------------------------------------------------------------------------- 1 | CXXFLAGS=-Wall -Wextra -g 2 | 3 | all: clean check 4 | 5 | check: lib.o 6 | 7 | lib.o: lib.cpp 8 | 9 | clean: 10 | $(RM) lib.o 11 | -------------------------------------------------------------------------------- /examples/on-sid/Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | docker build --network=mope --build-arg http_proxy=http://urika:3142 --tag sid-sources-list . 3 | 4 | clean: 5 | docker rmi sid-sources-list 6 | -------------------------------------------------------------------------------- /examples/on-sid/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:sid 2 | 3 | COPY sources.list /etc/apt/sources.list 4 | 5 | ARG http_proxy 6 | RUN env http_proxy=${http_proxy} apt-get update 7 | 8 | WORKDIR /mnt 9 | 10 | -------------------------------------------------------------------------------- /examples/on-trusty/Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | docker build --network=mope --build-arg http_proxy=http://urika:3142 --tag trusty-dev-vm . 3 | 4 | run: build 5 | docker run --network=mope -v $(shell pwd)/../..:/mnt -it trusty-dev-vm 6 | 7 | clean: 8 | docker rmi trusty-dev-vm 9 | 10 | .PHONY: build run 11 | -------------------------------------------------------------------------------- /examples/on-trusty/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:trusty 2 | 3 | ARG http_proxy 4 | RUN env http_proxy=${http_proxy} apt-get update \ 5 | && apt-get install -y build-essential libapt-pkg-dev curl 6 | 7 | COPY uprust.sh /root/uprust.sh 8 | 9 | RUN bash /root/uprust.sh --default-toolchain nightly -y 10 | 11 | WORKDIR /mnt 12 | -------------------------------------------------------------------------------- /ci.Dockerfile: -------------------------------------------------------------------------------- 1 | ARG BASE_VERSION 2 | FROM buildpack-deps:${BASE_VERSION} 3 | 4 | RUN curl https://sh.rustup.rs -sSf | sh -s -- -y 5 | ENV PATH="/root/.cargo/bin:${PATH}" 6 | 7 | RUN apt-get update && \ 8 | apt-get install -y libapt-pkg-dev && \ 9 | apt-get clean && \ 10 | rm -rf /var/lib/apt/lists/* 11 | 12 | ENV CARGO_TERM_COLOR=always 13 | WORKDIR /src 14 | ADD . . 15 | -------------------------------------------------------------------------------- /examples/epochs.rs: -------------------------------------------------------------------------------- 1 | use apt_pkg_native::Cache; 2 | 3 | use boolinator::Boolinator; 4 | 5 | fn main() { 6 | let mut cache = Cache::get_singleton(); 7 | for item in cache.iter().filter_map(|f| { 8 | f.versions() 9 | .any(|version| version.version().contains(':')) 10 | .as_some_from(|| f.name()) 11 | }) { 12 | println!("{}", item); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/list.rs: -------------------------------------------------------------------------------- 1 | use itertools::Itertools; 2 | 3 | use apt_pkg_native::Cache; 4 | use apt_pkg_native::simple; 5 | 6 | fn main() { 7 | let mut cache = Cache::get_singleton(); 8 | for item in cache.iter().map(simple::BinaryPackageVersions::new) { 9 | println!( 10 | "{} [{}]", 11 | item.pkg, 12 | item.versions.iter().map(|x| format!("{}", x)).join(", ") 13 | ); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Chris West (Faux) "] 3 | categories = [ 4 | "api-bindings", 5 | "os", 6 | ] 7 | description = "Bindings for libapt-pkg" 8 | license = "MIT" 9 | name = "apt-pkg-native" 10 | readme = "README.md" 11 | repository = "https://github.com/FauxFaux/apt-pkg-native-rs" 12 | version = "0.3.3" 13 | 14 | edition = "2024" 15 | 16 | [build-dependencies] 17 | cc = "1.0" 18 | 19 | [dependencies] 20 | lazy_static = "1" 21 | libc = "0.2" 22 | 23 | [dev-dependencies] 24 | boolinator = "2" 25 | itertools = "0.14" 26 | 27 | [features] 28 | default = [] 29 | ye-olde-apt = [] 30 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | # the inefficiency here is outrageous; don't copy this into other projects; 15 | # it's only vaguely acceptable here because this project is low volume 16 | strategy: 17 | matrix: 18 | buildpack: 19 | - bionic # 2018; apt 1.6 20 | - buster # 2019; apt 1.8 21 | - focal # 2020; apt 2.0 22 | - bullseye # 2021; apt 2.2 23 | - jammy # 2022; apt 2.4 24 | - bookworm # 2023; apt 2.6 25 | - noble # 2024; apt 2.7 26 | - trixie # 2025ish; 3.0-ish 27 | 28 | runs-on: ubuntu-latest 29 | 30 | steps: 31 | - uses: actions/checkout@v4 32 | 33 | - name: Test under ${{ matrix.buildpack }} 34 | run: | 35 | docker build -t img \ 36 | --build-arg BASE_VERSION=${{ matrix.buildpack }} \ 37 | --file ci.Dockerfile . 38 | docker run --rm img cargo test --verbose 39 | -------------------------------------------------------------------------------- /LICENSE.MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Chris West 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/policy.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | use apt_pkg_native::Cache; 4 | use apt_pkg_native::simple; 5 | 6 | fn main() { 7 | let pkg = env::args() 8 | .nth(1) 9 | .expect("usage: first argument: package name"); 10 | let arch = env::args().nth(2); 11 | 12 | let mut cache = Cache::get_singleton(); 13 | let mut found = match arch { 14 | Some(arch) => cache.find_by_name_arch(pkg.as_str(), arch.as_str()), 15 | None => cache.find_by_name(pkg.as_str()), 16 | }; 17 | 18 | if let Some(view) = found.next() { 19 | println!("{}:{}:", view.name(), view.arch()); 20 | 21 | let installed_version = view 22 | .current_version() 23 | .unwrap_or_else(|| "(none)".to_string()); 24 | println!(" Installed: {}", installed_version); 25 | println!( 26 | " Candidate: {}", 27 | view.candidate_version() 28 | .unwrap_or_else(|| "(none)".to_string(),) 29 | ); 30 | 31 | println!(" Version table:"); 32 | for simple::VersionOrigins { version, origins } in 33 | view.versions().map(simple::VersionOrigins::new) 34 | { 35 | let marker = if version.version == installed_version { 36 | "***" 37 | } else { 38 | " " 39 | }; 40 | #[cfg(not(feature = "ye-olde-apt"))] 41 | println!(" {} {} {}", marker, version.version, version.priority,); 42 | #[cfg(feature = "ye-olde-apt")] 43 | println!(" {} {}", marker, version.version,); 44 | 45 | for origin in origins { 46 | println!(" {:4} {}", "XXX", origin); 47 | } 48 | } 49 | } else { 50 | println!("unrecognised package: {}", pkg); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "apt-pkg-native" 7 | version = "0.3.3" 8 | dependencies = [ 9 | "boolinator", 10 | "cc", 11 | "itertools", 12 | "lazy_static", 13 | "libc", 14 | ] 15 | 16 | [[package]] 17 | name = "boolinator" 18 | version = "2.4.0" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9" 21 | 22 | [[package]] 23 | name = "cc" 24 | version = "1.2.40" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "e1d05d92f4b1fd76aad469d46cdd858ca761576082cd37df81416691e50199fb" 27 | dependencies = [ 28 | "find-msvc-tools", 29 | "shlex", 30 | ] 31 | 32 | [[package]] 33 | name = "either" 34 | version = "1.15.0" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" 37 | 38 | [[package]] 39 | name = "find-msvc-tools" 40 | version = "0.1.3" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "0399f9d26e5191ce32c498bebd31e7a3ceabc2745f0ac54af3f335126c3f24b3" 43 | 44 | [[package]] 45 | name = "itertools" 46 | version = "0.14.0" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" 49 | dependencies = [ 50 | "either", 51 | ] 52 | 53 | [[package]] 54 | name = "lazy_static" 55 | version = "1.5.0" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 58 | 59 | [[package]] 60 | name = "libc" 61 | version = "0.2.176" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" 64 | 65 | [[package]] 66 | name = "shlex" 67 | version = "1.3.0" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This crate provides bindings to `libapt-pkg`. 2 | 3 | 4 | ### Documentation and Examples 5 | 6 | See the `examples/` folder for some partial implementations of some commands. 7 | 8 | https://docs.rs/apt-pkg-native 9 | 10 | ### License Note 11 | 12 | While the code in this crate is available under a permissive MIT license, 13 | it is useless without [`libapt-pkg`](https://tracker.debian.org/pkg/apt), 14 | which is GPL2+. 15 | 16 | ### Building 17 | 18 | `libapt-pkg-dev` must be installed. The [`cc`](https://crates.io/crates/cc) 19 | crate is used to try and find a native compiler. 20 | 21 | The `ye-olde-apt` feature provides support for `apt <1.2` (Ubuntu 14.04 (Trusty), 22 | Debian 7 (Jessie) (2015)). This works by just deleting methods which are not 23 | available in that version. See 24 | [#2](https://github.com/FauxFaux/apt-pkg-native-rs/issues/2#issuecomment-351180818). 25 | This feature is no longer tested on CI, as these distros are long gone. 26 | 27 | 28 | ### Thread safety 29 | 30 | It is intended that the crate should be usable from multiple threads. 31 | However, this is generally implemented using singletons, which may be really 32 | annoying for your use-case. 33 | 34 | The current way the singleton is managed is awful, and it's not been fixed 35 | while I've been learning about the problems. A major version bump, and a 36 | proper singleton, may resolve some of the issues. This needs to be done eventually. 37 | 38 | `apt` does not have a concurrency model: you may not use threads. 39 | 40 | Since `apt` 1.4 or 1.5 (in Debian Stretch (2017), but not in Xenial 16.04), 41 | some operations are thread safe: it should be possible to initialise the cache 42 | twice in parallel. As these versions of `apt` are not widespread, the API of 43 | this crate does not attempt to expose this. 44 | 45 | 46 | ### Alternatives 47 | 48 | There is a similar project at [rust-apt](https://crates.io/crates/rust-apt). 49 | 50 | 51 | ### Switching distro with `docker` 52 | 53 | `examples/on-sid` has a docker file which builds a minimum Debian image with 54 | typical package lists downloaded. You can run a tool in it, from this directory, 55 | by: 56 | 57 | ``` 58 | (cd examples/on-sid && make) 59 | docker run -v $(pwd):/mnt sid-sources-list /mnt/target/release/examples/sources 60 | ``` 61 | -------------------------------------------------------------------------------- /examples/sources.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::collections::HashSet; 3 | /// A port of a randomly selected Python program: 4 | /// 5 | /// ```python 6 | /// #!/usr/bin/python3 7 | /// import collections 8 | /// import apt 9 | /// cache = apt.cache.Cache() 10 | /// def versions_in(suite): 11 | /// source_versions = collections.defaultdict(set) 12 | /// 13 | /// for package in cache: 14 | /// for version in package.versions: 15 | /// if suite and suite not in (origin.archive for origin in version.origins): 16 | /// continue 17 | /// source_versions[version.source_name].add(version.source_version) 18 | /// return source_versions 19 | /// 20 | /// if '__main__' == __name__: 21 | /// import sys 22 | /// sources = versions_in(sys.argv[1] if len(sys.argv) > 1 else None) 23 | /// for src in sorted(sources.keys()): 24 | /// # sort lexographically for determinism, not for any other reason 25 | /// for ver in sorted(sources[src]): 26 | /// print('{}={}'.format(src, ver)) 27 | /// ``` 28 | use std::env; 29 | 30 | use apt_pkg_native::Cache; 31 | 32 | #[cfg(feature = "ye-olde-apt")] 33 | fn main() { 34 | eprintln!("ye-olde-apt pre-dates source versions") 35 | } 36 | 37 | #[cfg(not(feature = "ye-olde-apt"))] 38 | fn main() { 39 | let archive_filter = env::args().nth(1); 40 | 41 | let mut cache = Cache::get_singleton(); 42 | let mut source_versions = HashMap::new(); 43 | { 44 | let mut all_packages = cache.iter(); 45 | 46 | while let Some(binary) = all_packages.next() { 47 | let mut binary_versions = binary.versions(); 48 | while let Some(version) = binary_versions.next() { 49 | if let Some(ref target_archive) = archive_filter { 50 | if version 51 | .origin_iter() 52 | .map(|origin| origin.file().next().unwrap().archive()) 53 | .any(|archive| archive == *target_archive) 54 | { 55 | continue; 56 | } 57 | } 58 | 59 | source_versions 60 | .entry(version.source_package()) 61 | .or_insert_with(HashSet::new) 62 | .insert(version.source_version()); 63 | } 64 | } 65 | } 66 | 67 | for src in lexicographic_sort(source_versions.keys()) { 68 | let mut sorted_versions: Vec<&String> = source_versions[src].iter().collect(); 69 | sorted_versions.sort_by(|left, right| cache.compare_versions(left, right)); 70 | for ver in sorted_versions { 71 | println!("{}={}", src, ver); 72 | } 73 | } 74 | } 75 | 76 | fn lexicographic_sort(input: I) -> Vec 77 | where 78 | T: Ord + Clone, 79 | I: Iterator, 80 | { 81 | let mut val: Vec = input.collect(); 82 | val.sort(); 83 | val 84 | } 85 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Here lies bindings to `libapt-pkg`, which is what the `apt`, `apt-get`, `apt-cache`, etc. 2 | //! commands use to view and manipulate the state of packages on the system. 3 | //! 4 | //! Currently, not much is exposed. You can pretty much only view basic package 5 | //! information, like with `apt-cache policy foo`. 6 | //! 7 | //! `libapt-pkg` has basically no documentation. `python-apt` is slightly better, 8 | //! but is also pretty inconsistent on the documentation front. The design of this 9 | //! crate is closer to `libapt-pkg`, despite it being pretty insane. 10 | //! 11 | //! The core concept here is an "iterator". Forget everything you know about iterators, 12 | //! these iterators are pretty much pointers. The crate attempts to make them act 13 | //! a bit more like Rust `Iterator`s, but is crippled by the insanity. 14 | //! 15 | //! Methods which "find" something will reposition one of these "iterators" at the right place 16 | //! in an existing stream of items. 17 | //! 18 | //! I recommend using `.map()` to turn an "iterator" into a Rust type as soon as possible. 19 | //! The returned map-like thing *is* a Rust `Iterator`, so you can do normal operations on it. 20 | //! 21 | //! Here's an example: normally you wouldn't need this ugly `.map(|_| ())` (read as "map anything 22 | //! to the empty object"), but here, it is *also* converting a sh... apt "iterator" into a 23 | //! real Iterator. 24 | //! 25 | //! ```rust,no_run 26 | //! extern crate apt_pkg_native; 27 | //! let mut cache = apt_pkg_native::Cache::get_singleton(); 28 | //! let total_packages = cache.iter().map(|_| ()).count(); 29 | //! ``` 30 | //! 31 | //! `libapt-pkg` also just segfaults if you do anything wrong, or re-use anything at the wrong time, 32 | //! or etc. I've tried to hide this, but I advise you not to push or outsmart the borrow checker. 33 | 34 | mod citer; 35 | mod raw; 36 | pub mod sane; 37 | pub mod simple; 38 | 39 | pub use crate::sane::Cache; 40 | 41 | #[cfg(test)] 42 | mod tests { 43 | use super::*; 44 | 45 | #[test] 46 | fn pretty_print_all() { 47 | let mut cache = Cache::get_singleton(); 48 | let read_all_and_count = cache.iter().map(simple::BinaryPackageVersions::new).count(); 49 | assert!(read_all_and_count > 2); 50 | assert_eq!(read_all_and_count, cache.iter().count()); 51 | } 52 | 53 | #[test] 54 | fn find_a_package() { 55 | let mut cache = Cache::get_singleton(); 56 | 57 | match cache.find_by_name("apt").next() { 58 | Some(view) => assert_eq!("apt", view.name()), 59 | _ => panic!("not found!"), 60 | } 61 | 62 | assert!( 63 | cache 64 | .find_by_name( 65 | "this-package-doesnt-exist-and-if-someone-makes-it-ill-be-really-angry" 66 | ) 67 | .next() 68 | .is_none() 69 | ); 70 | } 71 | 72 | #[test] 73 | fn compare_versions() { 74 | use std::cmp::Ordering; 75 | let cache = Cache::get_singleton(); 76 | assert_eq!(Ordering::Less, cache.compare_versions("3.0", "3.1")); 77 | assert_eq!(Ordering::Greater, cache.compare_versions("3.1", "3.0")); 78 | assert_eq!(Ordering::Equal, cache.compare_versions("3.0", "3.0")); 79 | } 80 | 81 | #[test] 82 | fn reload() { 83 | let mut cache = Cache::get_singleton(); 84 | cache.reload(); 85 | cache.reload(); 86 | cache.reload(); 87 | cache.reload(); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/citer.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::ops::Deref; 3 | 4 | pub trait RawIterator { 5 | type View; 6 | 7 | fn is_end(&self) -> bool; 8 | fn next(&mut self); 9 | 10 | fn as_view(&self) -> Self::View; 11 | 12 | fn release(&mut self); 13 | } 14 | 15 | pub struct CIterator 16 | where 17 | R: RawIterator, 18 | { 19 | pub first: bool, 20 | pub raw: R, 21 | } 22 | 23 | impl Drop for CIterator 24 | where 25 | R: RawIterator, 26 | { 27 | fn drop(&mut self) { 28 | self.raw.release(); 29 | } 30 | } 31 | 32 | pub struct Borrowed<'i, R> 33 | where 34 | R: 'i + RawIterator, 35 | { 36 | it: PhantomData<&'i CIterator>, 37 | val: R::View, 38 | } 39 | 40 | impl<'i, R> Deref for Borrowed<'i, R> 41 | where 42 | R: RawIterator, 43 | { 44 | type Target = R::View; 45 | 46 | fn deref(&self) -> &R::View { 47 | &self.val 48 | } 49 | } 50 | 51 | impl CIterator 52 | where 53 | R: RawIterator, 54 | { 55 | pub fn next(&mut self) -> Option> { 56 | if self.raw.is_end() { 57 | return None; 58 | } 59 | 60 | if !self.first { 61 | self.raw.next(); 62 | } 63 | 64 | self.first = false; 65 | 66 | // we don't want to observe the end marker 67 | if self.raw.is_end() { 68 | None 69 | } else { 70 | Some(Borrowed { 71 | it: PhantomData, 72 | val: self.raw.as_view(), 73 | }) 74 | } 75 | } 76 | 77 | pub fn map(self, f: F) -> CMap 78 | where 79 | F: FnMut(&R::View) -> B, 80 | { 81 | CMap { it: self, f } 82 | } 83 | 84 | pub fn filter_map(self, f: F) -> CFilterMap 85 | where 86 | F: FnMut(&R::View) -> Option, 87 | { 88 | CFilterMap { it: self, f } 89 | } 90 | 91 | pub fn any(mut self, mut f: F) -> bool 92 | where 93 | F: FnMut(&R::View) -> bool, 94 | { 95 | while let Some(view) = self.next() { 96 | if (f)(&view) { 97 | return true; 98 | } 99 | } 100 | 101 | false 102 | } 103 | 104 | pub fn all(mut self, mut f: F) -> bool 105 | where 106 | F: FnMut(&R::View) -> bool, 107 | { 108 | while let Some(view) = self.next() { 109 | if !(f)(&view) { 110 | return false; 111 | } 112 | } 113 | 114 | true 115 | } 116 | 117 | pub fn count(mut self) -> usize { 118 | // Not sure this is actually better than self.map(|_| ()).count() 119 | 120 | let mut count = 0; 121 | 122 | while !self.raw.is_end() { 123 | self.raw.next(); 124 | count += 1; 125 | } 126 | 127 | count 128 | } 129 | } 130 | 131 | #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] 132 | pub struct CMap 133 | where 134 | R: RawIterator, 135 | { 136 | it: CIterator, 137 | f: F, 138 | } 139 | 140 | impl Iterator for CMap 141 | where 142 | R: RawIterator, 143 | F: FnMut(&R::View) -> B, 144 | { 145 | type Item = B; 146 | 147 | fn next(&mut self) -> Option { 148 | match self.it.next() { 149 | Some(ref x) => Some((self.f)(x)), 150 | None => None, 151 | } 152 | } 153 | } 154 | 155 | #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] 156 | pub struct CFilterMap 157 | where 158 | R: RawIterator, 159 | { 160 | it: CIterator, 161 | f: F, 162 | } 163 | 164 | impl Iterator for CFilterMap 165 | where 166 | R: RawIterator, 167 | F: FnMut(&R::View) -> Option, 168 | { 169 | type Item = B; 170 | 171 | fn next(&mut self) -> Option { 172 | loop { 173 | match self.it.next() { 174 | Some(ref x) => { 175 | if let Some(y) = (self.f)(x) { 176 | return Some(y); 177 | } 178 | } 179 | None => return None, 180 | } 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/simple.rs: -------------------------------------------------------------------------------- 1 | //! Some structs representing basic concepts, and utilities to copy out of "iterators". 2 | 3 | use std::fmt; 4 | 5 | use crate::sane; 6 | 7 | #[derive(Clone, Debug)] 8 | pub struct BinaryPackage { 9 | pub name: String, 10 | pub arch: String, 11 | pub current_version: Option, 12 | pub candidate_version: Option, 13 | } 14 | 15 | impl BinaryPackage { 16 | pub fn new(view: &sane::PkgView) -> Self { 17 | BinaryPackage { 18 | name: view.name(), 19 | arch: view.arch(), 20 | current_version: view.current_version(), 21 | candidate_version: view.candidate_version(), 22 | } 23 | } 24 | } 25 | 26 | impl fmt::Display for BinaryPackage { 27 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 28 | write!(f, "{}:{}", self.name, self.arch)?; 29 | if let Some(ref version) = self.current_version { 30 | write!(f, " @ {version}")?; 31 | } 32 | if let Some(ref version) = self.candidate_version { 33 | write!(f, " -> {version}")?; 34 | } 35 | Ok(()) 36 | } 37 | } 38 | 39 | #[derive(Clone, Debug)] 40 | pub struct Version { 41 | pub version: String, 42 | pub arch: String, 43 | pub section: Option, 44 | 45 | #[cfg(not(feature = "ye-olde-apt"))] 46 | pub source_package: String, 47 | #[cfg(not(feature = "ye-olde-apt"))] 48 | pub source_version: String, 49 | #[cfg(not(feature = "ye-olde-apt"))] 50 | pub priority: i32, 51 | } 52 | 53 | impl Version { 54 | pub fn new(view: &sane::VerView) -> Self { 55 | Version { 56 | version: view.version(), 57 | arch: view.arch(), 58 | section: view.section(), 59 | #[cfg(not(feature = "ye-olde-apt"))] 60 | source_package: view.source_package(), 61 | #[cfg(not(feature = "ye-olde-apt"))] 62 | source_version: view.source_version(), 63 | #[cfg(not(feature = "ye-olde-apt"))] 64 | priority: view.priority(), 65 | } 66 | } 67 | } 68 | 69 | impl fmt::Display for Version { 70 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 71 | write!(f, "{}:{}", self.version, self.arch)?; 72 | if let Some(ref section) = self.section { 73 | write!(f, " in {section}")?; 74 | } 75 | #[cfg(not(feature = "ye-olde-apt"))] 76 | write!( 77 | f, 78 | " from {}:{} at {}", 79 | self.source_package, self.source_version, self.priority, 80 | )?; 81 | 82 | Ok(()) 83 | } 84 | } 85 | 86 | #[derive(Clone, Debug)] 87 | pub struct Origin { 88 | pub file_name: String, 89 | pub archive: String, 90 | pub version: Option, 91 | pub origin: Option, 92 | pub codename: Option, 93 | pub label: Option, 94 | pub site: Option, 95 | pub component: String, 96 | pub architecture: Option, 97 | pub index_type: String, 98 | } 99 | 100 | impl Origin { 101 | pub fn from_ver_file(view: &sane::VerFileView) -> Option { 102 | view.file().next().map(|x| Self::new(&x)) 103 | } 104 | 105 | pub fn new(view: &sane::PkgFileView) -> Self { 106 | Origin { 107 | file_name: view.file_name(), 108 | archive: view.archive(), 109 | version: view.version(), 110 | origin: view.origin(), 111 | codename: view.codename(), 112 | label: view.label(), 113 | site: view.site(), 114 | component: view.component(), 115 | architecture: view.architecture(), 116 | index_type: view.index_type(), 117 | } 118 | } 119 | } 120 | 121 | impl fmt::Display for Origin { 122 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 123 | // trying to simulate apt-cache policy, but a lot of information is missing 124 | if self.site.is_some() 125 | && self.origin.is_some() 126 | && self.label.is_some() 127 | && self.codename.is_some() 128 | && self.architecture.is_some() 129 | { 130 | write!( 131 | f, 132 | "TODO://{}/TODO(o:{}/l:{}/c:{}) {}/{} {} (f:{})", 133 | self.site.as_ref().unwrap(), 134 | self.origin.as_ref().unwrap(), 135 | self.label.as_ref().unwrap(), 136 | self.codename.as_ref().unwrap(), 137 | self.archive, 138 | self.component, 139 | self.architecture.as_ref().unwrap(), 140 | self.file_name 141 | ) 142 | } else { 143 | write!(f, "{}", self.file_name) 144 | } 145 | } 146 | } 147 | 148 | #[derive(Clone, Debug)] 149 | pub struct VersionOrigins { 150 | pub version: Version, 151 | pub origins: Vec, 152 | } 153 | 154 | impl VersionOrigins { 155 | pub fn new(view: &sane::VerView) -> Self { 156 | VersionOrigins { 157 | version: Version::new(view), 158 | origins: view 159 | .origin_iter() 160 | .map(|o| { 161 | Origin::from_ver_file(o) 162 | .expect("a version's origin should always have a backing file") 163 | }) 164 | .collect(), 165 | } 166 | } 167 | } 168 | 169 | #[derive(Clone, Debug)] 170 | pub struct BinaryPackageVersions { 171 | pub pkg: BinaryPackage, 172 | pub versions: Vec, 173 | } 174 | 175 | impl BinaryPackageVersions { 176 | pub fn new(view: &sane::PkgView) -> Self { 177 | BinaryPackageVersions { 178 | pkg: BinaryPackage::new(view), 179 | versions: view.versions().map(Version::new).collect(), 180 | } 181 | } 182 | } 183 | 184 | impl fmt::Display for BinaryPackageVersions { 185 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 186 | write!(f, "{} + {} versions", self.pkg, self.versions.len()) 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/raw.rs: -------------------------------------------------------------------------------- 1 | /// In general: 2 | /// * `*mut c_void` are to be released by the appropriate function 3 | /// * `*const c_chars` are short-term borrows 4 | /// * `*mut c_chars` are to be freed by `libc::free`. 5 | use std::sync::Mutex; 6 | 7 | use lazy_static::lazy_static; 8 | use libc::c_char; 9 | use libc::c_void; 10 | 11 | pub type PCache = *mut c_void; 12 | pub type PPkgIterator = *mut c_void; 13 | pub type PVerIterator = *mut c_void; 14 | pub type PDepIterator = *mut c_void; 15 | pub type PVerFileIterator = *mut c_void; 16 | pub type PPkgFileIterator = *mut c_void; 17 | pub type PVerFileParser = *mut c_void; 18 | 19 | #[link(name = "apt-pkg-c", kind = "static")] 20 | #[link(name = "apt-pkg")] 21 | unsafe extern "C" { 22 | /// Must be called exactly once, before anything else? 23 | fn init_config_system(); 24 | fn pkg_cache_create() -> PCache; 25 | fn pkg_cache_release(cache: PCache); 26 | 27 | pub fn pkg_cache_compare_versions( 28 | cache: PCache, 29 | left: *const c_char, 30 | right: *const c_char, 31 | ) -> i32; 32 | 33 | // Package iterators 34 | // ================= 35 | 36 | pub fn pkg_cache_pkg_iter(cache: PCache) -> PPkgIterator; 37 | pub fn pkg_cache_find_name(cache: PCache, name: *const c_char) -> PPkgIterator; 38 | pub fn pkg_cache_find_name_arch( 39 | cache: PCache, 40 | name: *const c_char, 41 | arch: *const c_char, 42 | ) -> PPkgIterator; 43 | pub fn pkg_iter_release(iterator: PPkgIterator); 44 | 45 | pub fn pkg_iter_next(iterator: PPkgIterator); 46 | pub fn pkg_iter_end(iterator: PPkgIterator) -> bool; 47 | 48 | // Package iterator accessors 49 | // ========================== 50 | 51 | pub fn pkg_iter_name(iterator: PPkgIterator) -> *const c_char; 52 | pub fn pkg_iter_arch(iterator: PPkgIterator) -> *const c_char; 53 | pub fn pkg_iter_current_version(iterator: PPkgIterator) -> *const c_char; 54 | pub fn pkg_iter_candidate_version(iterator: PPkgIterator) -> *const c_char; 55 | 56 | // Version iterators 57 | // ================= 58 | 59 | pub fn pkg_iter_ver_iter(pkg: PPkgIterator) -> PVerIterator; 60 | pub fn ver_iter_release(iterator: PVerIterator); 61 | 62 | pub fn ver_iter_next(iterator: PVerIterator); 63 | pub fn ver_iter_end(iterator: PVerIterator) -> bool; 64 | 65 | // Version accessors 66 | // ================= 67 | 68 | pub fn ver_iter_version(iterator: PVerIterator) -> *mut c_char; 69 | pub fn ver_iter_section(iterator: PVerIterator) -> *mut c_char; 70 | 71 | #[cfg(not(feature = "ye-olde-apt"))] 72 | pub fn ver_iter_source_package(iterator: PVerIterator) -> *mut c_char; 73 | 74 | #[cfg(not(feature = "ye-olde-apt"))] 75 | pub fn ver_iter_source_version(iterator: PVerIterator) -> *mut c_char; 76 | pub fn ver_iter_arch(iterator: PVerIterator) -> *mut c_char; 77 | pub fn ver_iter_priority_type(iterator: PVerIterator) -> *mut c_char; 78 | 79 | #[cfg(not(feature = "ye-olde-apt"))] 80 | pub fn ver_iter_priority(iterator: PVerIterator) -> i32; 81 | 82 | // Dependency iterators 83 | // ==================== 84 | 85 | pub fn ver_iter_dep_iter(iterator: PVerIterator) -> PDepIterator; 86 | pub fn dep_iter_release(iterator: PDepIterator); 87 | 88 | pub fn dep_iter_next(iterator: PDepIterator); 89 | pub fn dep_iter_end(iterator: PDepIterator) -> bool; 90 | 91 | // Dependency accessors 92 | // ==================== 93 | 94 | pub fn dep_iter_target_pkg(iterator: PDepIterator) -> PPkgIterator; 95 | pub fn dep_iter_target_ver(iterator: PDepIterator) -> *const c_char; 96 | pub fn dep_iter_comp_type(iterator: PDepIterator) -> *const c_char; 97 | pub fn dep_iter_dep_type(iterator: PDepIterator) -> *const c_char; 98 | 99 | pub fn ver_iter_ver_file_iter(iterator: PVerIterator) -> PVerFileIterator; 100 | pub fn ver_file_iter_release(iterator: PVerFileIterator); 101 | 102 | pub fn ver_file_iter_next(iterator: PVerFileIterator); 103 | pub fn ver_file_iter_end(iterator: PVerFileIterator) -> bool; 104 | 105 | pub fn ver_file_iter_get_parser(iterator: PVerFileIterator) -> PVerFileParser; 106 | pub fn ver_file_parser_short_desc(parser: PVerFileParser) -> *const c_char; 107 | pub fn ver_file_parser_long_desc(parser: PVerFileParser) -> *const c_char; 108 | pub fn ver_file_parser_maintainer(parser: PVerFileParser) -> *const c_char; 109 | pub fn ver_file_parser_homepage(parser: PVerFileParser) -> *const c_char; 110 | 111 | pub fn ver_file_iter_pkg_file_iter(iterator: PVerFileIterator) -> PPkgFileIterator; 112 | pub fn pkg_file_iter_release(iterator: PPkgFileIterator); 113 | 114 | pub fn pkg_file_iter_next(iterator: PPkgFileIterator); 115 | pub fn pkg_file_iter_end(iterator: PPkgFileIterator) -> bool; 116 | 117 | pub fn pkg_file_iter_file_name(iterator: PPkgFileIterator) -> *const c_char; 118 | pub fn pkg_file_iter_archive(iterator: PPkgFileIterator) -> *const c_char; 119 | pub fn pkg_file_iter_version(iterator: PPkgFileIterator) -> *const c_char; 120 | pub fn pkg_file_iter_origin(iterator: PPkgFileIterator) -> *const c_char; 121 | pub fn pkg_file_iter_codename(iterator: PPkgFileIterator) -> *const c_char; 122 | pub fn pkg_file_iter_label(iterator: PPkgFileIterator) -> *const c_char; 123 | pub fn pkg_file_iter_site(iterator: PPkgFileIterator) -> *const c_char; 124 | pub fn pkg_file_iter_component(iterator: PPkgFileIterator) -> *const c_char; 125 | pub fn pkg_file_iter_architecture(iterator: PPkgFileIterator) -> *const c_char; 126 | pub fn pkg_file_iter_index_type(iterator: PPkgFileIterator) -> *const c_char; 127 | } 128 | 129 | pub fn pkg_cache_get_singleton() -> &'static CACHE_SINGLETON { 130 | &CACHE_SINGLETON 131 | } 132 | 133 | #[derive(Debug)] 134 | pub struct CacheHolder { 135 | pub ptr: PCache, 136 | } 137 | 138 | unsafe impl Send for CacheHolder {} 139 | 140 | impl CacheHolder { 141 | pub fn re_up(&mut self) { 142 | unsafe { 143 | pkg_cache_release(self.ptr); 144 | self.ptr = pkg_cache_create(); 145 | } 146 | } 147 | } 148 | 149 | lazy_static! { 150 | #[derive(Debug)] 151 | pub static ref CACHE_SINGLETON: Mutex = { 152 | unsafe { 153 | init_config_system(); 154 | Mutex::new(CacheHolder { 155 | ptr: pkg_cache_create() 156 | }) 157 | } 158 | }; 159 | } 160 | -------------------------------------------------------------------------------- /examples/on-trusty/uprust.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright 2016 The Rust Project Developers. See the COPYRIGHT 3 | # file at the top-level directory of this distribution and at 4 | # http://rust-lang.org/COPYRIGHT. 5 | # 6 | # Licensed under the Apache License, Version 2.0 or the MIT license 8 | # , at your 9 | # option. This file may not be copied, modified, or distributed 10 | # except according to those terms. 11 | 12 | # This is just a little script that can be curled from the internet to 13 | # install rustup. It just does platform detection, curls the installer 14 | # and runs it. 15 | 16 | set -u 17 | 18 | RUSTUP_UPDATE_ROOT="https://static.rust-lang.org/rustup/dist" 19 | 20 | #XXX: If you change anything here, please make the same changes in setup_mode.rs 21 | usage() { 22 | cat 1>&2 < Choose a default host triple 38 | --default-toolchain Choose a default toolchain to install 39 | --default-toolchain none Do not install any toolchains 40 | EOF 41 | } 42 | 43 | main() { 44 | need_cmd uname 45 | need_cmd curl 46 | need_cmd mktemp 47 | need_cmd chmod 48 | need_cmd mkdir 49 | need_cmd rm 50 | need_cmd rmdir 51 | 52 | get_architecture || return 1 53 | local _arch="$RETVAL" 54 | assert_nz "$_arch" "arch" 55 | 56 | local _ext="" 57 | case "$_arch" in 58 | *windows*) 59 | _ext=".exe" 60 | ;; 61 | esac 62 | 63 | local _url="$RUSTUP_UPDATE_ROOT/$_arch/rustup-init$_ext" 64 | 65 | local _dir="$(mktemp -d 2>/dev/null || ensure mktemp -d -t rustup)" 66 | local _file="$_dir/rustup-init$_ext" 67 | 68 | local _ansi_escapes_are_valid=false 69 | if [ -t 2 ]; then 70 | if [ "${TERM+set}" = 'set' ]; then 71 | case "$TERM" in 72 | xterm*|rxvt*|urxvt*|linux*|vt*) 73 | _ansi_escapes_are_valid=true 74 | ;; 75 | esac 76 | fi 77 | fi 78 | 79 | # check if we have to use /dev/tty to prompt the user 80 | local need_tty=yes 81 | for arg in "$@"; do 82 | case "$arg" in 83 | -h|--help) 84 | usage 85 | exit 0 86 | ;; 87 | -y) 88 | # user wants to skip the prompt -- we don't need /dev/tty 89 | need_tty=no 90 | ;; 91 | *) 92 | ;; 93 | esac 94 | done 95 | 96 | if $_ansi_escapes_are_valid; then 97 | printf "\33[1minfo:\33[0m downloading installer\n" 1>&2 98 | else 99 | printf '%s\n' 'info: downloading installer' 1>&2 100 | fi 101 | 102 | ensure mkdir -p "$_dir" 103 | ensure curl -sSfL "$_url" -o "$_file" 104 | ensure chmod u+x "$_file" 105 | if [ ! -x "$_file" ]; then 106 | printf '%s\n' "Cannot execute $_file (likely because of mounting /tmp as noexec)." 1>&2 107 | printf '%s\n' "Please copy the file to a location where you can execute binaries and run ./rustup-init$_ext." 1>&2 108 | exit 1 109 | fi 110 | 111 | 112 | 113 | if [ "$need_tty" = "yes" ]; then 114 | # The installer is going to want to ask for confirmation by 115 | # reading stdin. This script was piped into `sh` though and 116 | # doesn't have stdin to pass to its children. Instead we're going 117 | # to explicitly connect /dev/tty to the installer's stdin. 118 | if [ ! -t 1 ]; then 119 | err "Unable to run interactively. Run with -y to accept defaults, --help for additional options" 120 | fi 121 | 122 | ignore "$_file" "$@" < /dev/tty 123 | else 124 | ignore "$_file" "$@" 125 | fi 126 | 127 | local _retval=$? 128 | 129 | ignore rm "$_file" 130 | ignore rmdir "$_dir" 131 | 132 | return "$_retval" 133 | } 134 | 135 | get_bitness() { 136 | need_cmd head 137 | # Architecture detection without dependencies beyond coreutils. 138 | # ELF files start out "\x7fELF", and the following byte is 139 | # 0x01 for 32-bit and 140 | # 0x02 for 64-bit. 141 | # The printf builtin on some shells like dash only supports octal 142 | # escape sequences, so we use those. 143 | local _current_exe_head=$(head -c 5 /proc/self/exe ) 144 | if [ "$_current_exe_head" = "$(printf '\177ELF\001')" ]; then 145 | echo 32 146 | elif [ "$_current_exe_head" = "$(printf '\177ELF\002')" ]; then 147 | echo 64 148 | else 149 | err "unknown platform bitness" 150 | fi 151 | } 152 | 153 | get_endianness() { 154 | local cputype=$1 155 | local suffix_eb=$2 156 | local suffix_el=$3 157 | 158 | # detect endianness without od/hexdump, like get_bitness() does. 159 | need_cmd head 160 | need_cmd tail 161 | 162 | local _current_exe_endianness="$(head -c 6 /proc/self/exe | tail -c 1)" 163 | if [ "$_current_exe_endianness" = "$(printf '\001')" ]; then 164 | echo "${cputype}${suffix_el}" 165 | elif [ "$_current_exe_endianness" = "$(printf '\002')" ]; then 166 | echo "${cputype}${suffix_eb}" 167 | else 168 | err "unknown platform endianness" 169 | fi 170 | } 171 | 172 | get_architecture() { 173 | 174 | local _ostype="$(uname -s)" 175 | local _cputype="$(uname -m)" 176 | 177 | if [ "$_ostype" = Linux ]; then 178 | if [ "$(uname -o)" = Android ]; then 179 | local _ostype=Android 180 | fi 181 | fi 182 | 183 | if [ "$_ostype" = Darwin -a "$_cputype" = i386 ]; then 184 | # Darwin `uname -s` lies 185 | if sysctl hw.optional.x86_64 | grep -q ': 1'; then 186 | local _cputype=x86_64 187 | fi 188 | fi 189 | 190 | case "$_ostype" in 191 | 192 | Android) 193 | local _ostype=linux-android 194 | ;; 195 | 196 | Linux) 197 | local _ostype=unknown-linux-gnu 198 | ;; 199 | 200 | FreeBSD) 201 | local _ostype=unknown-freebsd 202 | ;; 203 | 204 | NetBSD) 205 | local _ostype=unknown-netbsd 206 | ;; 207 | 208 | DragonFly) 209 | local _ostype=unknown-dragonfly 210 | ;; 211 | 212 | Darwin) 213 | local _ostype=apple-darwin 214 | ;; 215 | 216 | MINGW* | MSYS* | CYGWIN*) 217 | local _ostype=pc-windows-gnu 218 | ;; 219 | 220 | *) 221 | err "unrecognized OS type: $_ostype" 222 | ;; 223 | 224 | esac 225 | 226 | case "$_cputype" in 227 | 228 | i386 | i486 | i686 | i786 | x86) 229 | local _cputype=i686 230 | ;; 231 | 232 | xscale | arm) 233 | local _cputype=arm 234 | if [ "$_ostype" = "linux-android" ]; then 235 | local _ostype=linux-androideabi 236 | fi 237 | ;; 238 | 239 | armv6l) 240 | local _cputype=arm 241 | if [ "$_ostype" = "linux-android" ]; then 242 | local _ostype=linux-androideabi 243 | else 244 | local _ostype="${_ostype}eabihf" 245 | fi 246 | ;; 247 | 248 | armv7l | armv8l) 249 | local _cputype=armv7 250 | if [ "$_ostype" = "linux-android" ]; then 251 | local _ostype=linux-androideabi 252 | else 253 | local _ostype="${_ostype}eabihf" 254 | fi 255 | ;; 256 | 257 | aarch64) 258 | local _cputype=aarch64 259 | ;; 260 | 261 | x86_64 | x86-64 | x64 | amd64) 262 | local _cputype=x86_64 263 | ;; 264 | 265 | mips) 266 | local _cputype="$(get_endianness $_cputype "" 'el')" 267 | ;; 268 | 269 | mips64) 270 | local _bitness="$(get_bitness)" 271 | if [ $_bitness = "32" ]; then 272 | if [ $_ostype = "unknown-linux-gnu" ]; then 273 | # 64-bit kernel with 32-bit userland 274 | # endianness suffix is appended later 275 | local _cputype=mips 276 | fi 277 | else 278 | # only n64 ABI is supported for now 279 | local _ostype="${_ostype}abi64" 280 | fi 281 | 282 | local _cputype="$(get_endianness $_cputype "" 'el')" 283 | ;; 284 | 285 | ppc) 286 | local _cputype=powerpc 287 | ;; 288 | 289 | ppc64) 290 | local _cputype=powerpc64 291 | ;; 292 | 293 | ppc64le) 294 | local _cputype=powerpc64le 295 | ;; 296 | 297 | *) 298 | err "unknown CPU type: $_cputype" 299 | 300 | esac 301 | 302 | # Detect 64-bit linux with 32-bit userland 303 | if [ $_ostype = unknown-linux-gnu -a $_cputype = x86_64 ]; then 304 | if [ "$(get_bitness)" = "32" ]; then 305 | local _cputype=i686 306 | fi 307 | fi 308 | 309 | # Detect armv7 but without the CPU features Rust needs in that build, 310 | # and fall back to arm. 311 | # See https://github.com/rust-lang-nursery/rustup.rs/issues/587. 312 | if [ $_ostype = "unknown-linux-gnueabihf" -a $_cputype = armv7 ]; then 313 | if ensure grep '^Features' /proc/cpuinfo | grep -q -v neon; then 314 | # At least one processor does not have NEON. 315 | local _cputype=arm 316 | fi 317 | fi 318 | 319 | local _arch="$_cputype-$_ostype" 320 | 321 | RETVAL="$_arch" 322 | } 323 | 324 | say() { 325 | echo "rustup: $1" 326 | } 327 | 328 | err() { 329 | say "$1" >&2 330 | exit 1 331 | } 332 | 333 | need_cmd() { 334 | if ! command -v "$1" > /dev/null 2>&1 335 | then err "need '$1' (command not found)" 336 | fi 337 | } 338 | 339 | need_ok() { 340 | if [ $? != 0 ]; then err "$1"; fi 341 | } 342 | 343 | assert_nz() { 344 | if [ -z "$1" ]; then err "assert_nz $2"; fi 345 | } 346 | 347 | # Run a command that should never fail. If the command fails execution 348 | # will immediately terminate with an error showing the failing 349 | # command. 350 | ensure() { 351 | "$@" 352 | need_ok "command failed: $*" 353 | } 354 | 355 | # This is just for indicating that commands' results are being 356 | # intentionally ignored. Usually, because it's being executed 357 | # as part of error handling. 358 | ignore() { 359 | "$@" 360 | } 361 | 362 | main "$@" || exit 1 363 | -------------------------------------------------------------------------------- /apt-pkg-c/lib.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | struct PCache { 16 | // Owned by us. 17 | pkgCacheFile *cache_file; 18 | 19 | // Borrowed from cache_file. 20 | pkgCache *cache; 21 | 22 | // Owned by us. 23 | pkgRecords *records; 24 | }; 25 | 26 | struct PPkgIterator { 27 | // Owned by us. 28 | pkgCache::PkgIterator iterator; 29 | 30 | // Borrow of "static" PCache. 31 | PCache *cache; 32 | }; 33 | 34 | struct PVerIterator { 35 | // Owned by us. 36 | pkgCache::VerIterator iterator; 37 | 38 | // Borrowed from PCache. 39 | pkgCache::PkgIterator *pkg; 40 | 41 | // Borrow of "static" PCache. 42 | PCache *cache; 43 | }; 44 | 45 | struct PDepIterator { 46 | // Owned by us. 47 | pkgCache::DepIterator iterator; 48 | 49 | // Borrowed from PCache. 50 | pkgCache::VerIterator *ver; 51 | 52 | // Borrow of "static" PCache. 53 | PCache *cache; 54 | }; 55 | 56 | struct PVerFileIterator { 57 | // Owned by us. 58 | pkgCache::VerFileIterator iterator; 59 | 60 | // Borrow of "static" PCache. 61 | PCache *cache; 62 | }; 63 | 64 | struct PPkgFileIterator { 65 | // Owned by us. 66 | pkgCache::PkgFileIterator iterator; 67 | }; 68 | 69 | struct PVerFileParser { 70 | pkgRecords::Parser *parser; 71 | }; 72 | 73 | extern "C" { 74 | void init_config_system(); 75 | 76 | PCache *pkg_cache_create(); 77 | void pkg_cache_release(PCache *cache); 78 | 79 | int32_t pkg_cache_compare_versions(PCache *cache, const char *left, const char *right); 80 | 81 | // pkg_iter creation and deletion 82 | PPkgIterator *pkg_cache_pkg_iter(PCache *cache); 83 | PPkgIterator *pkg_cache_find_name(PCache *cache, const char *name); 84 | PPkgIterator *pkg_cache_find_name_arch(PCache *cache, const char *name, const char *arch); 85 | void pkg_iter_release(PPkgIterator *iterator); 86 | 87 | // pkg_iter mutation 88 | void pkg_iter_next(PPkgIterator *iterator); 89 | bool pkg_iter_end(PPkgIterator *iterator); 90 | 91 | // pkg_iter access 92 | const char *pkg_iter_name(PPkgIterator *iterator); 93 | const char *pkg_iter_arch(PPkgIterator *iterator); 94 | const char *pkg_iter_current_version(PPkgIterator *iterator); 95 | const char *pkg_iter_candidate_version(PPkgIterator *iterator); 96 | 97 | 98 | // ver_iter creation and deletion 99 | PVerIterator *pkg_iter_ver_iter(PPkgIterator *iterator); 100 | void ver_iter_release(PVerIterator *iterator); 101 | 102 | // ver_iter mutation 103 | void ver_iter_next(PVerIterator *iterator); 104 | bool ver_iter_end(PVerIterator *iterator); 105 | 106 | // ver_iter access 107 | const char *ver_iter_version(PVerIterator *iterator); 108 | const char *ver_iter_section(PVerIterator *iterator); 109 | const char *ver_iter_arch(PVerIterator *iterator); 110 | const char *ver_iter_priority_type(PVerIterator *wrapper); 111 | 112 | #ifndef YE_OLDE_APT 113 | const char *ver_iter_source_package(PVerIterator *iterator); 114 | const char *ver_iter_source_version(PVerIterator *iterator); 115 | int32_t ver_iter_priority(PVerIterator *iterator); 116 | #endif 117 | 118 | // dep_iter creation and deletion 119 | PDepIterator *ver_iter_dep_iter(PVerIterator *iterator); 120 | void dep_iter_release(PDepIterator *iterator); 121 | 122 | // dep_iter mutation 123 | void dep_iter_next(PDepIterator *iterator); 124 | bool dep_iter_end(PDepIterator *iterator); 125 | 126 | // dep_iter access 127 | PPkgIterator *dep_iter_target_pkg(PDepIterator *iterator); 128 | const char *dep_iter_target_ver(PDepIterator *iterator); 129 | const char *dep_iter_comp_type(PDepIterator *iterator); 130 | const char *dep_iter_dep_type(PDepIterator *iterator); 131 | 132 | // ver_file_iter creation and deletion 133 | PVerFileIterator *ver_iter_ver_file_iter(PVerIterator *iterator); 134 | void ver_file_iter_release(PVerFileIterator *iterator); 135 | 136 | // ver_file_iter mutation 137 | void ver_file_iter_next(PVerFileIterator *iterator); 138 | bool ver_file_iter_end(PVerFileIterator *iterator); 139 | 140 | // ver_file_parser creation 141 | PVerFileParser *ver_file_iter_get_parser(PVerFileIterator *iterator); 142 | 143 | // ver_file_parser access 144 | const char *ver_file_parser_short_desc(PVerFileParser *parser); 145 | const char *ver_file_parser_long_desc(PVerFileParser *parser); 146 | const char *ver_file_parser_maintainer(PVerFileParser *parser); 147 | const char *ver_file_parser_homepage(PVerFileParser *parser); 148 | 149 | // ver_file_iter has no accessors, only the creation of pkg_file_iter 150 | 151 | 152 | // pkg_file_iter creation 153 | PPkgFileIterator *ver_file_iter_pkg_file_iter(PVerFileIterator *iterator); 154 | void pkg_file_iter_release(PPkgFileIterator *iterator); 155 | 156 | // pkg_file_iter mutation 157 | void pkg_file_iter_next(PPkgFileIterator *iterator); 158 | bool pkg_file_iter_end(PPkgFileIterator *iterator); 159 | 160 | // pkg_file_iter access 161 | const char *pkg_file_iter_file_name(PPkgFileIterator *iterator); 162 | const char *pkg_file_iter_archive(PPkgFileIterator *iterator); 163 | const char *pkg_file_iter_version(PPkgFileIterator *iterator); 164 | const char *pkg_file_iter_origin(PPkgFileIterator *iterator); 165 | const char *pkg_file_iter_codename(PPkgFileIterator *iterator); 166 | const char *pkg_file_iter_label(PPkgFileIterator *iterator); 167 | const char *pkg_file_iter_site(PPkgFileIterator *iterator); 168 | const char *pkg_file_iter_component(PPkgFileIterator *iterator); 169 | const char *pkg_file_iter_architecture(PPkgFileIterator *iterator); 170 | const char *pkg_file_iter_index_type(PPkgFileIterator *iterator); 171 | } 172 | 173 | void init_config_system() { 174 | pkgInitConfig(*_config); 175 | pkgInitSystem(*_config, _system); 176 | } 177 | 178 | PCache *pkg_cache_create() { 179 | pkgCacheFile *cache_file = new pkgCacheFile(); 180 | pkgCache *cache = cache_file->GetPkgCache(); 181 | pkgRecords *records = new pkgRecords(*cache); 182 | 183 | PCache *ret = new PCache(); 184 | ret->cache_file = cache_file; 185 | ret->cache = cache; 186 | ret->records = records; 187 | 188 | return ret; 189 | } 190 | 191 | void pkg_cache_release(PCache *cache) { 192 | // TODO: is cache->cache cleaned up with cache->cache_file? 193 | delete cache->records; 194 | delete cache->cache_file; 195 | delete cache; 196 | } 197 | 198 | int32_t pkg_cache_compare_versions(PCache *cache, const char *left, const char *right) { 199 | // an int is returned here; presumably it will always be -1, 0 or 1. 200 | return cache->cache->VS->DoCmpVersion(left, left+strlen(left), right, right+strlen(right)); 201 | } 202 | 203 | PPkgIterator *pkg_cache_pkg_iter(PCache *cache) { 204 | PPkgIterator *wrapper = new PPkgIterator(); 205 | wrapper->iterator = cache->cache->PkgBegin(); 206 | wrapper->cache = cache; 207 | return wrapper; 208 | } 209 | 210 | PPkgIterator *pkg_cache_find_name(PCache *cache, const char *name) { 211 | PPkgIterator *wrapper = new PPkgIterator(); 212 | wrapper->iterator = cache->cache->FindPkg(name); 213 | wrapper->cache = cache; 214 | return wrapper; 215 | } 216 | 217 | PPkgIterator *pkg_cache_find_name_arch(PCache *cache, const char *name, const char *arch) { 218 | PPkgIterator *wrapper = new PPkgIterator(); 219 | wrapper->iterator = cache->cache->FindPkg(name, arch); 220 | wrapper->cache = cache; 221 | return wrapper; 222 | } 223 | 224 | void pkg_iter_release(PPkgIterator *wrapper) { 225 | delete wrapper; 226 | } 227 | 228 | void pkg_iter_next(PPkgIterator *wrapper) { 229 | ++wrapper->iterator; 230 | } 231 | 232 | bool pkg_iter_end(PPkgIterator *wrapper) { 233 | return wrapper->cache->cache->PkgEnd() == wrapper->iterator; 234 | } 235 | 236 | const char *pkg_iter_name(PPkgIterator *wrapper) { 237 | return wrapper->iterator.Name(); 238 | } 239 | 240 | const char *pkg_iter_arch(PPkgIterator *wrapper) { 241 | return wrapper->iterator.Arch(); 242 | } 243 | 244 | const char *pkg_iter_current_version(PPkgIterator *wrapper) { 245 | return wrapper->iterator.CurVersion(); 246 | } 247 | 248 | const char *pkg_iter_candidate_version(PPkgIterator *wrapper) { 249 | pkgCache::VerIterator it = wrapper->cache->cache_file->GetPolicy()->GetCandidateVer(wrapper->iterator); 250 | if (it.end()) { 251 | return nullptr; 252 | } 253 | return it.VerStr(); 254 | } 255 | 256 | PVerIterator *pkg_iter_ver_iter(PPkgIterator *wrapper) { 257 | PVerIterator *new_wrapper = new PVerIterator(); 258 | new_wrapper->iterator = wrapper->iterator.VersionList(); 259 | new_wrapper->pkg = &wrapper->iterator; 260 | new_wrapper->cache = wrapper->cache; 261 | return new_wrapper; 262 | } 263 | 264 | void ver_iter_release(PVerIterator *wrapper) { 265 | delete wrapper; 266 | } 267 | 268 | void ver_iter_next(PVerIterator *wrapper) { 269 | ++wrapper->iterator; 270 | } 271 | 272 | bool ver_iter_end(PVerIterator *wrapper) { 273 | return wrapper->iterator.end(); 274 | } 275 | 276 | 277 | const char *ver_iter_version(PVerIterator *wrapper) { 278 | return wrapper->iterator.VerStr(); 279 | } 280 | 281 | const char *ver_iter_section(PVerIterator *wrapper) { 282 | return wrapper->iterator.Section(); 283 | } 284 | 285 | const char *ver_iter_priority_type(PVerIterator *wrapper) { 286 | return wrapper->iterator.PriorityType(); 287 | } 288 | 289 | #ifndef YE_OLDE_APT 290 | 291 | const char *ver_iter_source_package(PVerIterator *wrapper) { 292 | return wrapper->iterator.SourcePkgName(); 293 | } 294 | 295 | const char *ver_iter_source_version(PVerIterator *wrapper) { 296 | return wrapper->iterator.SourceVerStr(); 297 | } 298 | 299 | int32_t ver_iter_priority(PVerIterator *wrapper) { 300 | // The priority is a "short", which is roughly a (signed) int16_t; 301 | // going bigger just in case 302 | return wrapper->cache->cache_file->GetPolicy()->GetPriority(wrapper->iterator); 303 | } 304 | 305 | #endif 306 | 307 | const char *ver_iter_arch(PVerIterator *wrapper) { 308 | return wrapper->iterator.Arch(); 309 | } 310 | 311 | 312 | PDepIterator *ver_iter_dep_iter(PVerIterator *wrapper) { 313 | PDepIterator *new_wrapper = new PDepIterator(); 314 | new_wrapper->iterator = wrapper->iterator.DependsList(); 315 | new_wrapper->cache = wrapper->cache; 316 | return new_wrapper; 317 | } 318 | 319 | void dep_iter_release(PDepIterator *wrapper) { 320 | delete wrapper; 321 | } 322 | 323 | void dep_iter_next(PDepIterator *wrapper) { 324 | ++wrapper->iterator; 325 | } 326 | 327 | bool dep_iter_end(PDepIterator *wrapper) { 328 | return wrapper->iterator.end(); 329 | } 330 | 331 | PPkgIterator *dep_iter_target_pkg(PDepIterator *wrapper) { 332 | PPkgIterator *new_wrapper = new PPkgIterator(); 333 | new_wrapper->iterator = wrapper->iterator.TargetPkg(); 334 | new_wrapper->cache = wrapper->cache; 335 | return new_wrapper; 336 | } 337 | 338 | const char *dep_iter_target_ver(PDepIterator *wrapper) { 339 | return wrapper->iterator.TargetVer(); 340 | } 341 | 342 | const char *dep_iter_comp_type(PDepIterator *wrapper) { 343 | return wrapper->iterator.CompType(); 344 | } 345 | 346 | const char *dep_iter_dep_type(PDepIterator *wrapper) { 347 | return wrapper->iterator.DepType(); 348 | } 349 | 350 | 351 | PVerFileIterator *ver_iter_ver_file_iter(PVerIterator *wrapper) { 352 | PVerFileIterator *new_wrapper = new PVerFileIterator(); 353 | new_wrapper->iterator = wrapper->iterator.FileList(); 354 | new_wrapper->cache = wrapper->cache; 355 | return new_wrapper; 356 | } 357 | 358 | void ver_file_iter_release(PVerFileIterator *wrapper) { 359 | delete wrapper; 360 | } 361 | 362 | void ver_file_iter_next(PVerFileIterator *wrapper) { 363 | ++wrapper->iterator; 364 | } 365 | 366 | PVerFileParser *ver_file_iter_get_parser(PVerFileIterator *wrapper) { 367 | PVerFileParser *parser = new PVerFileParser(); 368 | parser->parser = &wrapper->cache->records->Lookup(wrapper->iterator); 369 | return parser; 370 | } 371 | 372 | const char *to_c_string(std::string s) { 373 | char *cstr = new char[s.length()+1]; 374 | std::strcpy(cstr, s.c_str()); 375 | return cstr; 376 | } 377 | 378 | const char *ver_file_parser_short_desc(PVerFileParser *parser) { 379 | std::string desc = parser->parser->ShortDesc(); 380 | return to_c_string(desc); 381 | } 382 | 383 | const char *ver_file_parser_long_desc(PVerFileParser *parser) { 384 | std::string desc = parser->parser->LongDesc(); 385 | return to_c_string(desc); 386 | } 387 | 388 | const char *ver_file_parser_maintainer(PVerFileParser *parser) { 389 | std::string maint = parser->parser->Maintainer(); 390 | return to_c_string(maint); 391 | } 392 | 393 | const char *ver_file_parser_homepage(PVerFileParser *parser) { 394 | std::string hp = parser->parser->Homepage(); 395 | return to_c_string(hp); 396 | } 397 | 398 | bool ver_file_iter_end(PVerFileIterator *wrapper) { 399 | return wrapper->iterator.end(); 400 | } 401 | 402 | PPkgFileIterator *ver_file_iter_pkg_file_iter(PVerFileIterator *wrapper) { 403 | PPkgFileIterator *new_wrapper = new PPkgFileIterator(); 404 | new_wrapper->iterator = wrapper->iterator.File(); 405 | return new_wrapper; 406 | } 407 | 408 | void pkg_file_iter_release(PPkgFileIterator *wrapper) { 409 | delete wrapper; 410 | } 411 | 412 | void pkg_file_iter_next(PPkgFileIterator *wrapper) { 413 | ++wrapper->iterator; 414 | } 415 | 416 | bool pkg_file_iter_end(PPkgFileIterator *wrapper) { 417 | return wrapper->iterator.end(); 418 | } 419 | 420 | const char *pkg_file_iter_file_name(PPkgFileIterator *wrapper) { 421 | return wrapper->iterator.FileName(); 422 | } 423 | 424 | const char *pkg_file_iter_archive(PPkgFileIterator *wrapper) { 425 | return wrapper->iterator.Archive(); 426 | } 427 | 428 | const char *pkg_file_iter_version(PPkgFileIterator *wrapper) { 429 | return wrapper->iterator.Version(); 430 | } 431 | 432 | const char *pkg_file_iter_origin(PPkgFileIterator *wrapper) { 433 | return wrapper->iterator.Origin(); 434 | } 435 | 436 | const char *pkg_file_iter_codename(PPkgFileIterator *wrapper) { 437 | return wrapper->iterator.Codename(); 438 | } 439 | 440 | const char *pkg_file_iter_label(PPkgFileIterator *wrapper) { 441 | return wrapper->iterator.Label(); 442 | } 443 | 444 | const char *pkg_file_iter_site(PPkgFileIterator *wrapper) { 445 | return wrapper->iterator.Site(); 446 | } 447 | 448 | const char *pkg_file_iter_component(PPkgFileIterator *wrapper) { 449 | return wrapper->iterator.Component(); 450 | } 451 | 452 | const char *pkg_file_iter_architecture(PPkgFileIterator *wrapper) { 453 | return wrapper->iterator.Architecture(); 454 | } 455 | 456 | const char *pkg_file_iter_index_type(PPkgFileIterator *wrapper) { 457 | return wrapper->iterator.IndexType(); 458 | } 459 | -------------------------------------------------------------------------------- /src/sane.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | use std::ffi; 3 | use std::marker::PhantomData; 4 | use std::sync::MutexGuard; 5 | 6 | use crate::citer::CIterator; 7 | use crate::citer::RawIterator; 8 | use crate::raw; 9 | 10 | /// A reference to the package cache singleton, 11 | /// from which most functionality can be accessed. 12 | #[derive(Debug)] 13 | pub struct Cache { 14 | ptr_mutex: &'static raw::CACHE_SINGLETON, 15 | } 16 | 17 | impl Cache { 18 | /// Get a reference to the singleton. 19 | pub fn get_singleton() -> Cache { 20 | Cache { 21 | ptr_mutex: raw::pkg_cache_get_singleton(), 22 | } 23 | } 24 | 25 | /// Drop the cache, and re-create it from scratch. 26 | /// 27 | /// It's super important that there are no other outstanding 28 | /// references to the cache at this point. Again, I remind you 29 | /// not to try and outsmart the borrow checker. It doesn't know 30 | /// how much trouble there is in here. 31 | pub fn reload(&mut self) { 32 | self.ptr_mutex.lock().expect("poisoned mutex").re_up() 33 | } 34 | 35 | /// Walk through all of the packages, in a random order. 36 | /// 37 | /// If there are multiple architectures, multiple architectures will be returned. 38 | /// 39 | /// See the module documentation for apologies about how this isn't an iterator. 40 | pub fn iter(&mut self) -> CIterator> { 41 | let lock = self.ptr_mutex.lock().expect("poisoned mutex"); 42 | unsafe { 43 | let raw_iter = raw::pkg_cache_pkg_iter(lock.ptr); 44 | PkgIterator::new(lock, raw_iter) 45 | } 46 | } 47 | 48 | /// Find a package by name. It's not clear whether this picks a random arch, 49 | /// or the primary one. 50 | /// 51 | /// The returned iterator will either be at the end, or at a package with the name. 52 | pub fn find_by_name(&mut self, name: &str) -> CIterator> { 53 | let lock = self.ptr_mutex.lock().expect("poisoned mutex"); 54 | unsafe { 55 | let name = ffi::CString::new(name).unwrap(); 56 | let ptr = raw::pkg_cache_find_name(lock.ptr, name.as_ptr()); 57 | PkgIterator::new(lock, ptr) 58 | } 59 | } 60 | 61 | /// Find a package by name and architecture. 62 | /// 63 | /// The returned iterator will either be at the end, or at a matching package. 64 | pub fn find_by_name_arch(&mut self, name: &str, arch: &str) -> CIterator> { 65 | let lock = self.ptr_mutex.lock().expect("poisoned mutex"); 66 | unsafe { 67 | let name = ffi::CString::new(name).unwrap(); 68 | let arch = ffi::CString::new(arch).unwrap(); 69 | let ptr = raw::pkg_cache_find_name_arch(lock.ptr, name.as_ptr(), arch.as_ptr()); 70 | PkgIterator::new(lock, ptr) 71 | } 72 | } 73 | 74 | /// Compare two versions, returning an `Ordering`, as used by most Rusty `sort()` methods. 75 | /// 76 | /// This uses the "versioning scheme" currently set, which, in theory, can change, 77 | /// but in practice is always the "Standard .deb" scheme. As of 2017, there aren't even any 78 | /// other implementations. As such, this may eventually become a static method somewhere. 79 | /// 80 | /// # Examples 81 | /// 82 | /// ```rust 83 | /// # let mut cache = apt_pkg_native::Cache::get_singleton(); 84 | /// let mut packages = vec!["3.0", "3.1", "3.0~1"]; 85 | /// packages.sort_by(|left, right| cache.compare_versions(left, right)); 86 | /// assert_eq!(vec!["3.0~1", "3.0", "3.1"], packages); 87 | /// ``` 88 | pub fn compare_versions(&self, left: &str, right: &str) -> cmp::Ordering { 89 | unsafe { 90 | let left = ffi::CString::new(left).unwrap(); 91 | let right = ffi::CString::new(right).unwrap(); 92 | 93 | let lock = self.ptr_mutex.lock().expect("poisoned mutex"); 94 | raw::pkg_cache_compare_versions(lock.ptr, left.as_ptr(), right.as_ptr()).cmp(&0) 95 | } 96 | } 97 | } 98 | 99 | /// An "iterator"/pointer to a point in a package list. 100 | #[derive(Debug)] 101 | pub struct PkgIterator<'c> { 102 | _cache: MutexGuard<'c, raw::CacheHolder>, 103 | ptr: raw::PPkgIterator, 104 | } 105 | 106 | impl<'c> PkgIterator<'c> { 107 | fn new(cache: MutexGuard<'c, raw::CacheHolder>, ptr: raw::PCache) -> CIterator { 108 | CIterator { 109 | first: true, 110 | raw: PkgIterator { _cache: cache, ptr }, 111 | } 112 | } 113 | } 114 | 115 | // TODO: could this be a ref to the iterator? 116 | // TODO: Can't get the lifetimes to work. 117 | pub struct PkgView<'c> { 118 | cache: PhantomData<&'c MutexGuard<'c, raw::CacheHolder>>, 119 | ptr: raw::PPkgIterator, 120 | } 121 | 122 | impl<'c> RawIterator for PkgIterator<'c> { 123 | type View = PkgView<'c>; 124 | 125 | fn is_end(&self) -> bool { 126 | unsafe { raw::pkg_iter_end(self.ptr) } 127 | } 128 | 129 | fn next(&mut self) { 130 | unsafe { raw::pkg_iter_next(self.ptr) } 131 | } 132 | 133 | fn as_view(&self) -> Self::View { 134 | assert!(!self.is_end()); 135 | 136 | PkgView { 137 | ptr: self.ptr, 138 | cache: PhantomData, 139 | } 140 | } 141 | 142 | fn release(&mut self) { 143 | unsafe { raw::pkg_iter_release(self.ptr) } 144 | } 145 | } 146 | 147 | /// Actual accessors 148 | impl<'c> PkgView<'c> { 149 | pub fn name(&self) -> String { 150 | unsafe { 151 | make_owned_ascii_string(raw::pkg_iter_name(self.ptr)) 152 | .expect("packages always have names") 153 | } 154 | } 155 | 156 | pub fn arch(&self) -> String { 157 | unsafe { 158 | make_owned_ascii_string(raw::pkg_iter_arch(self.ptr)) 159 | .expect("packages always have architectures") 160 | } 161 | } 162 | 163 | pub fn current_version(&self) -> Option { 164 | unsafe { make_owned_ascii_string(raw::pkg_iter_current_version(self.ptr)) } 165 | } 166 | 167 | pub fn candidate_version(&self) -> Option { 168 | unsafe { make_owned_ascii_string(raw::pkg_iter_candidate_version(self.ptr)) } 169 | } 170 | 171 | pub fn versions(&self) -> CIterator> { 172 | CIterator { 173 | first: true, 174 | raw: VerIterator { 175 | cache: PhantomData, 176 | ptr: unsafe { raw::pkg_iter_ver_iter(self.ptr) }, 177 | }, 178 | } 179 | } 180 | } 181 | 182 | /// Represents a single PkgView without associated PkgIterator. Derefs to 183 | /// regular PkgView and releases the internal iterator on drop. 184 | pub struct SinglePkgView<'c> { 185 | view: PkgView<'c>, 186 | } 187 | 188 | impl<'c> std::ops::Deref for SinglePkgView<'c> { 189 | type Target = PkgView<'c>; 190 | 191 | fn deref(&self) -> &Self::Target { 192 | &self.view 193 | } 194 | } 195 | 196 | impl<'c> Drop for SinglePkgView<'c> { 197 | fn drop(&mut self) { 198 | unsafe { 199 | raw::pkg_iter_release(self.view.ptr); 200 | } 201 | } 202 | } 203 | 204 | /// An "iterator"/pointer to a point in a version list. 205 | pub struct VerIterator<'c> { 206 | cache: PhantomData<&'c MutexGuard<'c, raw::CacheHolder>>, 207 | ptr: raw::PVerIterator, 208 | } 209 | 210 | pub struct VerView<'c> { 211 | cache: PhantomData<&'c MutexGuard<'c, raw::CacheHolder>>, 212 | ptr: raw::PVerIterator, 213 | } 214 | 215 | impl<'c> RawIterator for VerIterator<'c> { 216 | type View = VerView<'c>; 217 | 218 | fn is_end(&self) -> bool { 219 | unsafe { raw::ver_iter_end(self.ptr) } 220 | } 221 | 222 | fn next(&mut self) { 223 | unsafe { raw::ver_iter_next(self.ptr) } 224 | } 225 | 226 | fn as_view(&self) -> Self::View { 227 | assert!(!self.is_end()); 228 | 229 | VerView { 230 | ptr: self.ptr, 231 | cache: self.cache, 232 | } 233 | } 234 | 235 | fn release(&mut self) { 236 | unsafe { raw::ver_iter_release(self.ptr) } 237 | } 238 | } 239 | 240 | /// Actual accessors 241 | impl<'c> VerView<'c> { 242 | pub fn version(&self) -> String { 243 | unsafe { 244 | make_owned_ascii_string(raw::ver_iter_version(self.ptr)) 245 | .expect("versions always have a version") 246 | } 247 | } 248 | 249 | pub fn arch(&self) -> String { 250 | unsafe { 251 | make_owned_ascii_string(raw::ver_iter_arch(self.ptr)) 252 | .expect("versions always have an arch") 253 | } 254 | } 255 | 256 | pub fn section(&self) -> Option { 257 | unsafe { make_owned_ascii_string(raw::ver_iter_section(self.ptr)) } 258 | } 259 | 260 | pub fn priority_type(&self) -> Option { 261 | unsafe { make_owned_ascii_string(raw::ver_iter_priority_type(self.ptr)) } 262 | } 263 | 264 | #[cfg(not(feature = "ye-olde-apt"))] 265 | pub fn source_package(&self) -> String { 266 | unsafe { 267 | make_owned_ascii_string(raw::ver_iter_source_package(self.ptr)) 268 | .expect("versions always have a source package") 269 | } 270 | } 271 | 272 | #[cfg(not(feature = "ye-olde-apt"))] 273 | pub fn source_version(&self) -> String { 274 | unsafe { 275 | make_owned_ascii_string(raw::ver_iter_source_version(self.ptr)) 276 | .expect("versions always have a source_version") 277 | } 278 | } 279 | 280 | #[cfg(not(feature = "ye-olde-apt"))] 281 | pub fn priority(&self) -> i32 { 282 | unsafe { raw::ver_iter_priority(self.ptr) } 283 | } 284 | 285 | pub fn origin_iter(&self) -> CIterator> { 286 | CIterator { 287 | first: true, 288 | raw: VerFileIterator { 289 | cache: PhantomData, 290 | ptr: unsafe { raw::ver_iter_ver_file_iter(self.ptr) }, 291 | }, 292 | } 293 | } 294 | 295 | pub fn dep_iter(&self) -> CIterator> { 296 | CIterator { 297 | first: true, 298 | raw: DepIterator { 299 | cache: PhantomData, 300 | ptr: unsafe { raw::ver_iter_dep_iter(self.ptr) }, 301 | }, 302 | } 303 | } 304 | } 305 | 306 | /// An "iterator"/pointer to a point in a dependency list. 307 | pub struct DepIterator<'c> { 308 | cache: PhantomData<&'c MutexGuard<'c, raw::CacheHolder>>, 309 | ptr: raw::PDepIterator, 310 | } 311 | 312 | pub struct DepView<'c> { 313 | cache: PhantomData<&'c MutexGuard<'c, raw::CacheHolder>>, 314 | ptr: raw::PDepIterator, 315 | } 316 | 317 | impl<'c> RawIterator for DepIterator<'c> { 318 | type View = DepView<'c>; 319 | 320 | fn is_end(&self) -> bool { 321 | unsafe { raw::dep_iter_end(self.ptr) } 322 | } 323 | 324 | fn next(&mut self) { 325 | unsafe { raw::dep_iter_next(self.ptr) } 326 | } 327 | 328 | fn as_view(&self) -> Self::View { 329 | assert!(!self.is_end()); 330 | 331 | DepView { 332 | ptr: self.ptr, 333 | cache: self.cache, 334 | } 335 | } 336 | 337 | fn release(&mut self) { 338 | unsafe { raw::dep_iter_release(self.ptr) } 339 | } 340 | } 341 | 342 | /// Actual accessors 343 | impl<'c> DepView<'c> { 344 | pub fn target_pkg(&self) -> SinglePkgView<'_> { 345 | let ptr = unsafe { raw::dep_iter_target_pkg(self.ptr) }; 346 | SinglePkgView { 347 | view: PkgView { 348 | cache: self.cache, 349 | ptr, 350 | }, 351 | } 352 | } 353 | 354 | pub fn target_ver(&self) -> String { 355 | unsafe { 356 | make_owned_ascii_string(raw::dep_iter_target_ver(self.ptr)) 357 | .expect("dependency always has target version") 358 | } 359 | } 360 | 361 | pub fn comp_type(&self) -> String { 362 | unsafe { 363 | make_owned_ascii_string(raw::dep_iter_comp_type(self.ptr)) 364 | .expect("dependency always has comp type") 365 | } 366 | } 367 | 368 | pub fn dep_type(&self) -> String { 369 | unsafe { 370 | make_owned_ascii_string(raw::dep_iter_dep_type(self.ptr)) 371 | .expect("dependency always has dep type") 372 | } 373 | } 374 | } 375 | 376 | /// An "iterator"/pointer to a point in a version's file list(?). 377 | pub struct VerFileIterator<'c> { 378 | cache: PhantomData<&'c MutexGuard<'c, raw::CacheHolder>>, 379 | ptr: raw::PVerFileIterator, 380 | } 381 | 382 | // TODO: could this be a ref to the iterator? 383 | // TODO: Can't get the lifetimes to work. 384 | pub struct VerFileView<'c> { 385 | cache: PhantomData<&'c MutexGuard<'c, raw::CacheHolder>>, 386 | ptr: raw::PVerFileIterator, 387 | parser: raw::PVerFileParser, 388 | } 389 | 390 | impl<'c> RawIterator for VerFileIterator<'c> { 391 | type View = VerFileView<'c>; 392 | 393 | fn is_end(&self) -> bool { 394 | unsafe { raw::ver_file_iter_end(self.ptr) } 395 | } 396 | 397 | fn next(&mut self) { 398 | unsafe { raw::ver_file_iter_next(self.ptr) } 399 | } 400 | 401 | fn as_view(&self) -> Self::View { 402 | assert!(!self.is_end()); 403 | 404 | let parser = unsafe { raw::ver_file_iter_get_parser(self.ptr) }; 405 | 406 | VerFileView { 407 | ptr: self.ptr, 408 | cache: self.cache, 409 | parser, 410 | } 411 | } 412 | 413 | fn release(&mut self) { 414 | unsafe { raw::ver_file_iter_release(self.ptr) } 415 | } 416 | } 417 | 418 | impl<'c> VerFileView<'c> { 419 | pub fn file(&self) -> CIterator> { 420 | CIterator { 421 | first: true, 422 | raw: PkgFileIterator { 423 | cache: PhantomData, 424 | ptr: unsafe { raw::ver_file_iter_pkg_file_iter(self.ptr) }, 425 | }, 426 | } 427 | } 428 | 429 | pub fn short_desc(&self) -> Option { 430 | unsafe { make_owned_ascii_string(raw::ver_file_parser_short_desc(self.parser)) } 431 | } 432 | 433 | pub fn long_desc(&self) -> Option { 434 | unsafe { make_owned_ascii_string(raw::ver_file_parser_long_desc(self.parser)) } 435 | } 436 | 437 | pub fn maintainer(&self) -> Option { 438 | unsafe { make_owned_ascii_string(raw::ver_file_parser_maintainer(self.parser)) } 439 | } 440 | 441 | pub fn homepage(&self) -> Option { 442 | unsafe { make_owned_ascii_string(raw::ver_file_parser_homepage(self.parser)) } 443 | } 444 | } 445 | 446 | /// An "iterator"/pointer to a point in a file list. 447 | pub struct PkgFileIterator<'c> { 448 | cache: PhantomData<&'c MutexGuard<'c, raw::CacheHolder>>, 449 | ptr: raw::PPkgFileIterator, 450 | } 451 | 452 | // TODO: could this be a ref to the iterator? 453 | // TODO: Can't get the lifetimes to work. 454 | pub struct PkgFileView<'c> { 455 | cache: PhantomData<&'c MutexGuard<'c, raw::CacheHolder>>, 456 | ptr: raw::PPkgFileIterator, 457 | } 458 | 459 | impl<'c> RawIterator for PkgFileIterator<'c> { 460 | type View = PkgFileView<'c>; 461 | 462 | fn is_end(&self) -> bool { 463 | unsafe { raw::pkg_file_iter_end(self.ptr) } 464 | } 465 | 466 | fn next(&mut self) { 467 | unsafe { raw::pkg_file_iter_next(self.ptr) } 468 | } 469 | 470 | fn as_view(&self) -> Self::View { 471 | assert!(!self.is_end()); 472 | 473 | PkgFileView { 474 | ptr: self.ptr, 475 | cache: self.cache, 476 | } 477 | } 478 | 479 | fn release(&mut self) { 480 | unsafe { raw::pkg_file_iter_release(self.ptr) } 481 | } 482 | } 483 | 484 | impl<'c> PkgFileView<'c> { 485 | pub fn file_name(&self) -> String { 486 | unsafe { 487 | make_owned_ascii_string(raw::pkg_file_iter_file_name(self.ptr)) 488 | .expect("package file always has a file name") 489 | } 490 | } 491 | pub fn archive(&self) -> String { 492 | unsafe { 493 | make_owned_ascii_string(raw::pkg_file_iter_archive(self.ptr)) 494 | .expect("package file always has an archive") 495 | } 496 | } 497 | pub fn version(&self) -> Option { 498 | unsafe { make_owned_ascii_string(raw::pkg_file_iter_version(self.ptr)) } 499 | } 500 | pub fn origin(&self) -> Option { 501 | unsafe { make_owned_ascii_string(raw::pkg_file_iter_origin(self.ptr)) } 502 | } 503 | pub fn codename(&self) -> Option { 504 | unsafe { make_owned_ascii_string(raw::pkg_file_iter_codename(self.ptr)) } 505 | } 506 | pub fn label(&self) -> Option { 507 | unsafe { make_owned_ascii_string(raw::pkg_file_iter_label(self.ptr)) } 508 | } 509 | pub fn site(&self) -> Option { 510 | unsafe { make_owned_ascii_string(raw::pkg_file_iter_site(self.ptr)) } 511 | } 512 | pub fn component(&self) -> String { 513 | unsafe { 514 | make_owned_ascii_string(raw::pkg_file_iter_component(self.ptr)) 515 | .expect("package file always has a component") 516 | } 517 | } 518 | pub fn architecture(&self) -> Option { 519 | unsafe { make_owned_ascii_string(raw::pkg_file_iter_architecture(self.ptr)) } 520 | } 521 | pub fn index_type(&self) -> String { 522 | unsafe { 523 | make_owned_ascii_string(raw::pkg_file_iter_index_type(self.ptr)) 524 | .expect("package file always has a index_type") 525 | } 526 | } 527 | } 528 | 529 | #[inline] 530 | unsafe fn make_owned_ascii_string(ptr: *const libc::c_char) -> Option { 531 | if ptr.is_null() { 532 | return None; 533 | } 534 | 535 | let string = unsafe { 536 | ffi::CStr::from_ptr(ptr) 537 | .to_str() 538 | .expect("value should always be low-ascii") 539 | .to_string() 540 | }; 541 | 542 | Some(string) 543 | } 544 | --------------------------------------------------------------------------------