provenant/parsers/
autotools.rs1use crate::models::PackageData;
22use crate::models::{DatasourceId, PackageType};
23use packageurl::PackageUrl;
24use std::fs;
25use std::path::Path;
26
27use super::PackageParser;
28
29pub struct AutotoolsConfigureParser;
33
34const AUTOCONF_CONFIGURE_MARKERS: &[&str] = &[
35 "generated by gnu autoconf",
36 "generated automatically using autoconf",
37 "please tell bug-autoconf@gnu.org",
38];
39
40fn looks_like_autoconf_generated_configure(path: &Path) -> bool {
41 fs::read(path)
42 .ok()
43 .map(|content| {
44 String::from_utf8_lossy(&content)
45 .lines()
46 .take(250)
47 .map(|line| line.trim().to_ascii_lowercase())
48 .any(|line| {
49 AUTOCONF_CONFIGURE_MARKERS
50 .iter()
51 .any(|marker| line.contains(marker))
52 })
53 })
54 .unwrap_or(false)
55}
56
57impl PackageParser for AutotoolsConfigureParser {
58 const PACKAGE_TYPE: PackageType = PackageType::Autotools;
59
60 fn is_match(path: &Path) -> bool {
61 match path.file_name().and_then(|name| name.to_str()) {
62 Some("configure.ac") => true,
63 Some("configure") => looks_like_autoconf_generated_configure(path),
64 _ => false,
65 }
66 }
67
68 fn extract_packages(path: &Path) -> Vec<PackageData> {
69 let name = path
70 .parent()
71 .and_then(|p| p.file_name())
72 .and_then(|n| n.to_str())
73 .map(|s| s.to_string())
74 .or_else(|| {
75 path.file_name()
76 .is_some_and(|name| name == "configure" || name == "configure.ac")
77 .then_some("input".to_string())
78 });
79
80 let purl = name.as_deref().and_then(|name| {
81 PackageUrl::new(Self::PACKAGE_TYPE.as_str(), name)
82 .ok()
83 .map(|purl| purl.to_string())
84 });
85
86 vec![PackageData {
87 package_type: Some(Self::PACKAGE_TYPE),
88 name,
89 datasource_id: Some(DatasourceId::AutotoolsConfigure),
90 purl,
91 ..Default::default()
92 }]
93 }
94}
95
96crate::register_parser!(
97 "Autotools configure script",
98 &["**/configure", "**/configure.ac"],
99 "autotools",
100 "C",
101 Some("https://www.gnu.org/software/autoconf/"),
102);