pkgcraft/pkg/
ebuild.rs

1use std::ffi::{c_char, CString};
2use std::{mem, ptr, slice};
3
4use pkgcraft::pkg::ebuild::{xml, EbuildPackage};
5use pkgcraft::pkg::Pkg;
6use pkgcraft::traits::IntoOwned;
7
8use crate::dep::{DependencySet, DependencySetKind};
9use crate::error::Error;
10use crate::macros::*;
11use crate::panic::ffi_catch_panic;
12use crate::utils::{boxed, obj_to_str, str_to_raw};
13
14pub mod keyword;
15use keyword::Keyword;
16
17/// Wrapper for package maintainers.
18#[repr(C)]
19pub struct Maintainer {
20    email: *mut c_char,
21    name: *mut c_char,
22    description: *mut c_char,
23    maint_type: *mut c_char,
24    proxied: *mut c_char,
25}
26
27impl Drop for Maintainer {
28    fn drop(&mut self) {
29        unsafe {
30            drop(CString::from_raw(self.email));
31            char_p_or_null_free!(self.name);
32            char_p_or_null_free!(self.description);
33            drop(CString::from_raw(self.maint_type));
34            drop(CString::from_raw(self.proxied));
35        }
36    }
37}
38
39/// Wrapper for package upstream remote-ids.
40#[repr(C)]
41pub struct RemoteId {
42    site: *mut c_char,
43    name: *mut c_char,
44}
45
46impl Drop for RemoteId {
47    fn drop(&mut self) {
48        unsafe {
49            drop(CString::from_raw(self.site));
50            drop(CString::from_raw(self.name));
51        }
52    }
53}
54
55/// Wrapper for upstream package maintainers.
56#[repr(C)]
57pub struct UpstreamMaintainer {
58    name: *mut c_char,
59    email: *mut c_char,
60    status: *mut c_char,
61}
62
63impl Drop for UpstreamMaintainer {
64    fn drop(&mut self) {
65        unsafe {
66            drop(CString::from_raw(self.name));
67            char_p_or_null_free!(self.email);
68            drop(CString::from_raw(self.status));
69        }
70    }
71}
72
73/// Wrapper for package upstream info.
74#[repr(C)]
75pub struct Upstream {
76    remote_ids_len: usize,
77    remote_ids: *mut *mut RemoteId,
78    maintainers_len: usize,
79    maintainers: *mut *mut UpstreamMaintainer,
80    bugs_to: *mut c_char,
81    changelog: *mut c_char,
82    doc: *mut c_char,
83}
84
85impl Drop for Upstream {
86    fn drop(&mut self) {
87        unsafe {
88            let len = self.remote_ids_len;
89            for ptr in Vec::from_raw_parts(self.remote_ids, len, len).into_iter() {
90                drop(Box::from_raw(ptr));
91            }
92            let len = self.maintainers_len;
93            for ptr in Vec::from_raw_parts(self.maintainers, len, len).into_iter() {
94                drop(Box::from_raw(ptr));
95            }
96            char_p_or_null_free!(self.bugs_to);
97            char_p_or_null_free!(self.changelog);
98            char_p_or_null_free!(self.doc);
99        }
100    }
101}
102
103/// Convert a given pointer into an ebuild package reference.
104macro_rules! try_pkg_from_ptr {
105    ( $var:expr ) => {{
106        let pkg = $crate::macros::try_ref_from_ptr!($var);
107        match pkg {
108            Pkg::Ebuild(p, _) => p,
109            Pkg::Configured(p, _) => p.into(),
110            _ => panic!("invalid pkg type: {pkg:?}"),
111        }
112    }};
113}
114
115/// Return a package's path.
116///
117/// # Safety
118/// The argument must be a non-null Pkg pointer.
119#[no_mangle]
120pub unsafe extern "C" fn pkgcraft_pkg_ebuild_path(p: *mut Pkg) -> *mut c_char {
121    let pkg = try_pkg_from_ptr!(p);
122    try_ptr_from_str!(pkg.path().as_str())
123}
124
125/// Return a package's ebuild file content.
126///
127/// Returns NULL on error.
128///
129/// # Safety
130/// The argument must be a non-null Pkg pointer.
131#[no_mangle]
132pub unsafe extern "C" fn pkgcraft_pkg_ebuild_ebuild(p: *mut Pkg) -> *mut c_char {
133    ffi_catch_panic! {
134        let pkg = try_pkg_from_ptr!(p);
135        let s = unwrap_or_panic!(pkg.ebuild());
136        try_ptr_from_str!(s)
137    }
138}
139
140/// Return a package's deprecated status.
141///
142/// # Safety
143/// The argument must be a non-null Pkg pointer.
144#[no_mangle]
145pub unsafe extern "C" fn pkgcraft_pkg_ebuild_deprecated(p: *mut Pkg) -> bool {
146    let pkg = try_pkg_from_ptr!(p);
147    pkg.deprecated()
148}
149
150/// Return a package's live status.
151///
152/// # Safety
153/// The argument must be a non-null Pkg pointer.
154#[no_mangle]
155pub unsafe extern "C" fn pkgcraft_pkg_ebuild_live(p: *mut Pkg) -> bool {
156    let pkg = try_pkg_from_ptr!(p);
157    pkg.live()
158}
159
160/// Return a package's masked status.
161///
162/// # Safety
163/// The argument must be a non-null Pkg pointer.
164#[no_mangle]
165pub unsafe extern "C" fn pkgcraft_pkg_ebuild_masked(p: *mut Pkg) -> bool {
166    let pkg = try_pkg_from_ptr!(p);
167    pkg.masked()
168}
169
170/// Return a package's description.
171///
172/// # Safety
173/// The argument must be a non-null Pkg pointer.
174#[no_mangle]
175pub unsafe extern "C" fn pkgcraft_pkg_ebuild_description(p: *mut Pkg) -> *mut c_char {
176    let pkg = try_pkg_from_ptr!(p);
177    try_ptr_from_str!(pkg.description())
178}
179
180/// Return a package's slot.
181///
182/// # Safety
183/// The argument must be a non-null Pkg pointer.
184#[no_mangle]
185pub unsafe extern "C" fn pkgcraft_pkg_ebuild_slot(p: *mut Pkg) -> *mut c_char {
186    let pkg = try_pkg_from_ptr!(p);
187    try_ptr_from_str!(pkg.slot())
188}
189
190/// Return a package's subslot.
191///
192/// # Safety
193/// The argument must be a non-null Pkg pointer.
194#[no_mangle]
195pub unsafe extern "C" fn pkgcraft_pkg_ebuild_subslot(p: *mut Pkg) -> *mut c_char {
196    let pkg = try_pkg_from_ptr!(p);
197    try_ptr_from_str!(pkg.subslot())
198}
199
200/// Return a package's dependencies for a given set of descriptors.
201///
202/// Returns NULL on error.
203///
204/// # Safety
205/// The argument must be a non-null Pkg pointer.
206#[no_mangle]
207pub unsafe extern "C" fn pkgcraft_pkg_ebuild_dependencies(
208    p: *mut Pkg,
209    keys: *mut *mut c_char,
210    len: usize,
211) -> *mut DependencySet {
212    ffi_catch_panic! {
213        let pkg = try_pkg_from_ptr!(p);
214        let keys = unsafe { slice::from_raw_parts(keys, len) };
215        let mut dep_keys = vec![];
216        for s in keys {
217            let s = try_str_from_ptr!(s);
218            let key = unwrap_or_panic!(
219                s.to_uppercase().parse().map_err(|_| Error::new(format!("invalid dep key: {s}")))
220            );
221            dep_keys.push(key);
222        }
223
224        let deps = pkg.dependencies(&dep_keys).into_owned();
225        Box::into_raw(Box::new(DependencySet::new_dep(deps)))
226    }
227}
228
229/// Return a package's DEPEND.
230///
231/// # Safety
232/// The argument must be a non-null Pkg pointer.
233#[no_mangle]
234pub unsafe extern "C" fn pkgcraft_pkg_ebuild_depend(p: *mut Pkg) -> *mut DependencySet {
235    let pkg = try_pkg_from_ptr!(p);
236    let set = DependencySet::new_dep(pkg.depend().clone());
237    Box::into_raw(Box::new(set))
238}
239
240/// Return a package's BDEPEND.
241///
242/// # Safety
243/// The argument must be a non-null Pkg pointer.
244#[no_mangle]
245pub unsafe extern "C" fn pkgcraft_pkg_ebuild_bdepend(p: *mut Pkg) -> *mut DependencySet {
246    let pkg = try_pkg_from_ptr!(p);
247    let set = DependencySet::new_dep(pkg.bdepend().clone());
248    Box::into_raw(Box::new(set))
249}
250
251/// Return a package's IDEPEND.
252///
253/// # Safety
254/// The argument must be a non-null Pkg pointer.
255#[no_mangle]
256pub unsafe extern "C" fn pkgcraft_pkg_ebuild_idepend(p: *mut Pkg) -> *mut DependencySet {
257    let pkg = try_pkg_from_ptr!(p);
258    let set = DependencySet::new_dep(pkg.idepend().clone());
259    Box::into_raw(Box::new(set))
260}
261
262/// Return a package's PDEPEND.
263///
264/// # Safety
265/// The argument must be a non-null Pkg pointer.
266#[no_mangle]
267pub unsafe extern "C" fn pkgcraft_pkg_ebuild_pdepend(p: *mut Pkg) -> *mut DependencySet {
268    let pkg = try_pkg_from_ptr!(p);
269    let set = DependencySet::new_dep(pkg.pdepend().clone());
270    Box::into_raw(Box::new(set))
271}
272
273/// Return a package's RDEPEND.
274///
275/// # Safety
276/// The argument must be a non-null Pkg pointer.
277#[no_mangle]
278pub unsafe extern "C" fn pkgcraft_pkg_ebuild_rdepend(p: *mut Pkg) -> *mut DependencySet {
279    let pkg = try_pkg_from_ptr!(p);
280    let set = DependencySet::new_dep(pkg.rdepend().clone());
281    Box::into_raw(Box::new(set))
282}
283
284/// Return a package's LICENSE.
285///
286/// # Safety
287/// The argument must be a non-null Pkg pointer.
288#[no_mangle]
289pub unsafe extern "C" fn pkgcraft_pkg_ebuild_license(p: *mut Pkg) -> *mut DependencySet {
290    let pkg = try_pkg_from_ptr!(p);
291    let set = DependencySet::new_string(pkg.license().clone(), DependencySetKind::License);
292    Box::into_raw(Box::new(set))
293}
294
295/// Return a package's PROPERTIES.
296///
297/// # Safety
298/// The argument must be a non-null Pkg pointer.
299#[no_mangle]
300pub unsafe extern "C" fn pkgcraft_pkg_ebuild_properties(p: *mut Pkg) -> *mut DependencySet {
301    let pkg = try_pkg_from_ptr!(p);
302    let set = DependencySet::new_string(pkg.properties().clone(), DependencySetKind::Properties);
303    Box::into_raw(Box::new(set))
304}
305
306/// Return a package's REQUIRED_USE.
307///
308/// # Safety
309/// The argument must be a non-null Pkg pointer.
310#[no_mangle]
311pub unsafe extern "C" fn pkgcraft_pkg_ebuild_required_use(p: *mut Pkg) -> *mut DependencySet {
312    let pkg = try_pkg_from_ptr!(p);
313    let set = DependencySet::new_string(pkg.required_use().clone(), DependencySetKind::RequiredUse);
314    Box::into_raw(Box::new(set))
315}
316
317/// Return a package's RESTRICT.
318///
319/// # Safety
320/// The argument must be a non-null Pkg pointer.
321#[no_mangle]
322pub unsafe extern "C" fn pkgcraft_pkg_ebuild_restrict(p: *mut Pkg) -> *mut DependencySet {
323    let pkg = try_pkg_from_ptr!(p);
324    let set = DependencySet::new_string(pkg.restrict().clone(), DependencySetKind::Restrict);
325    Box::into_raw(Box::new(set))
326}
327
328/// Return a package's SRC_URI.
329///
330/// # Safety
331/// The argument must be a non-null Pkg pointer.
332#[no_mangle]
333pub unsafe extern "C" fn pkgcraft_pkg_ebuild_src_uri(p: *mut Pkg) -> *mut DependencySet {
334    let pkg = try_pkg_from_ptr!(p);
335    let set = DependencySet::new_uri(pkg.src_uri().clone());
336    Box::into_raw(Box::new(set))
337}
338
339/// Return a package's homepage.
340///
341/// # Safety
342/// The argument must be a non-null Pkg pointer.
343#[no_mangle]
344pub unsafe extern "C" fn pkgcraft_pkg_ebuild_homepage(
345    p: *mut Pkg,
346    len: *mut usize,
347) -> *mut *mut c_char {
348    let pkg = try_pkg_from_ptr!(p);
349    iter_to_array!(pkg.homepage().iter(), len, str_to_raw)
350}
351
352/// Return a package's defined phases.
353///
354/// # Safety
355/// The argument must be a non-null Pkg pointer.
356#[no_mangle]
357pub unsafe extern "C" fn pkgcraft_pkg_ebuild_defined_phases(
358    p: *mut Pkg,
359    len: *mut usize,
360) -> *mut *mut c_char {
361    let pkg = try_pkg_from_ptr!(p);
362    iter_to_array!(pkg.defined_phases().iter(), len, str_to_raw)
363}
364
365/// Return a package's keywords.
366///
367/// # Safety
368/// The argument must be a non-null Pkg pointer.
369#[no_mangle]
370pub unsafe extern "C" fn pkgcraft_pkg_ebuild_keywords(
371    p: *mut Pkg,
372    len: *mut usize,
373) -> *mut *mut Keyword {
374    let pkg = try_pkg_from_ptr!(p);
375    iter_to_array!(pkg.keywords().iter(), len, |x| boxed(x.clone().into()))
376}
377
378/// Return a package's keywords as raw strings.
379///
380/// # Safety
381/// The argument must be a non-null Pkg pointer.
382#[no_mangle]
383pub unsafe extern "C" fn pkgcraft_pkg_ebuild_keywords_str(
384    p: *mut Pkg,
385    len: *mut usize,
386) -> *mut *mut c_char {
387    let pkg = try_pkg_from_ptr!(p);
388    iter_to_array!(pkg.keywords().iter(), len, obj_to_str)
389}
390
391/// Return a package's iuse.
392///
393/// # Safety
394/// The argument must be a non-null Pkg pointer.
395#[no_mangle]
396pub unsafe extern "C" fn pkgcraft_pkg_ebuild_iuse(
397    p: *mut Pkg,
398    len: *mut usize,
399) -> *mut *mut c_char {
400    let pkg = try_pkg_from_ptr!(p);
401    iter_to_array!(pkg.iuse().iter(), len, obj_to_str)
402}
403
404/// Return a package's directly inherited eclasses.
405///
406/// # Safety
407/// The argument must be a non-null Pkg pointer.
408#[no_mangle]
409pub unsafe extern "C" fn pkgcraft_pkg_ebuild_inherit(
410    p: *mut Pkg,
411    len: *mut usize,
412) -> *mut *mut c_char {
413    let pkg = try_pkg_from_ptr!(p);
414    iter_to_array!(pkg.inherit().iter().map(|e| e.name()), len, str_to_raw)
415}
416
417/// Return a package's inherited eclasses.
418///
419/// # Safety
420/// The argument must be a non-null Pkg pointer.
421#[no_mangle]
422pub unsafe extern "C" fn pkgcraft_pkg_ebuild_inherited(
423    p: *mut Pkg,
424    len: *mut usize,
425) -> *mut *mut c_char {
426    let pkg = try_pkg_from_ptr!(p);
427    iter_to_array!(pkg.inherited().iter().map(|e| e.name()), len, str_to_raw)
428}
429
430/// Return a package's long description.
431///
432/// Returns NULL on nonexistence.
433///
434/// # Safety
435/// The argument must be a non-null Pkg pointer.
436#[no_mangle]
437pub unsafe extern "C" fn pkgcraft_pkg_ebuild_long_description(p: *mut Pkg) -> *mut c_char {
438    let pkg = try_pkg_from_ptr!(p);
439    match pkg.long_description() {
440        Some(s) => try_ptr_from_str!(s),
441        None => ptr::null_mut(),
442    }
443}
444
445/// Return a package's maintainers.
446///
447/// # Safety
448/// The argument must be a non-null Pkg pointer.
449#[no_mangle]
450pub unsafe extern "C" fn pkgcraft_pkg_ebuild_maintainers(
451    p: *mut Pkg,
452    len: *mut usize,
453) -> *mut *mut Maintainer {
454    let pkg = try_pkg_from_ptr!(p);
455    let mut ptrs: Vec<_> = pkg
456        .maintainers()
457        .iter()
458        .map(|m| {
459            let maintainer = Maintainer {
460                email: try_ptr_from_str!(m.email()),
461                name: char_p_or_null!(m.name()),
462                description: char_p_or_null!(m.description()),
463                maint_type: try_ptr_from_str!(m.maint_type().as_ref()),
464                proxied: try_ptr_from_str!(m.proxied().as_ref()),
465            };
466            Box::into_raw(Box::new(maintainer))
467        })
468        .collect();
469    ptrs.shrink_to_fit();
470    unsafe { *len = ptrs.len() };
471    let p = ptrs.as_mut_ptr();
472    mem::forget(ptrs);
473    p
474}
475
476/// Free an array of Maintainer pointers.
477///
478/// # Safety
479/// The argument must be the value received from pkgcraft_pkg_ebuild_maintainers() or NULL along
480/// with the length of the array.
481#[no_mangle]
482pub unsafe extern "C" fn pkgcraft_pkg_ebuild_maintainers_free(
483    maintainers: *mut *mut Maintainer,
484    len: usize,
485) {
486    if !maintainers.is_null() {
487        unsafe {
488            for ptr in Vec::from_raw_parts(maintainers, len, len).into_iter() {
489                drop(Box::from_raw(ptr));
490            }
491        }
492    }
493}
494
495/// Return a package's upstream info.
496///
497/// Returns NULL on nonexistence.
498///
499/// # Safety
500/// The argument must be a non-null Pkg pointer.
501#[no_mangle]
502pub unsafe extern "C" fn pkgcraft_pkg_ebuild_upstream(p: *mut Pkg) -> *mut Upstream {
503    let pkg = try_pkg_from_ptr!(p);
504    match pkg.upstream() {
505        Some(u) => {
506            // convert remote ids to C wrapper objects
507            let mut remote_ids_len: usize = 0;
508            let convert = |r: &xml::RemoteId| {
509                let obj = RemoteId {
510                    site: try_ptr_from_str!(r.site()),
511                    name: try_ptr_from_str!(r.name()),
512                };
513                Box::into_raw(Box::new(obj))
514            };
515            let remote_ids =
516                iter_to_array!(u.remote_ids().iter(), &mut remote_ids_len as *mut _, convert);
517
518            // convert upstream maintainers to C wrapper objects
519            let mut maintainers_len: usize = 0;
520            let convert = |m: &xml::UpstreamMaintainer| {
521                let obj = UpstreamMaintainer {
522                    name: try_ptr_from_str!(m.name()),
523                    email: char_p_or_null!(m.email()),
524                    status: try_ptr_from_str!(m.status().to_string()),
525                };
526                Box::into_raw(Box::new(obj))
527            };
528            let maintainers =
529                iter_to_array!(u.maintainers().iter(), &mut maintainers_len as *mut _, convert);
530
531            let upstream = Upstream {
532                remote_ids_len,
533                remote_ids,
534                maintainers_len,
535                maintainers,
536                bugs_to: char_p_or_null!(u.bugs_to()),
537                changelog: char_p_or_null!(u.changelog()),
538                doc: char_p_or_null!(u.doc()),
539            };
540
541            Box::into_raw(Box::new(upstream))
542        }
543        None => ptr::null_mut(),
544    }
545}
546
547/// Free an Upstream.
548///
549/// # Safety
550/// The argument must be a Upstream pointer or NULL.
551#[no_mangle]
552pub unsafe extern "C" fn pkgcraft_pkg_ebuild_upstream_free(u: *mut Upstream) {
553    if !u.is_null() {
554        unsafe { drop(Box::from_raw(u)) };
555    }
556}