use crate::models::PackageData;
use crate::models::{DatasourceId, PackageType};
use packageurl::PackageUrl;
use std::fs;
use std::path::Path;
use super::PackageParser;
pub struct AutotoolsConfigureParser;
const AUTOCONF_CONFIGURE_MARKERS: &[&str] = &[
"generated by gnu autoconf",
"generated automatically using autoconf",
"please tell bug-autoconf@gnu.org",
"configure script for ",
];
fn looks_like_autoconf_generated_configure(path: &Path) -> bool {
fs::read(path)
.ok()
.map(|content| {
String::from_utf8_lossy(&content)
.lines()
.take(250)
.map(|line| line.trim().to_ascii_lowercase())
.any(|line| {
AUTOCONF_CONFIGURE_MARKERS
.iter()
.any(|marker| line.contains(marker))
})
})
.unwrap_or(false)
}
impl PackageParser for AutotoolsConfigureParser {
const PACKAGE_TYPE: PackageType = PackageType::Autotools;
fn is_match(path: &Path) -> bool {
match path.file_name().and_then(|name| name.to_str()) {
Some("configure.ac") => true,
Some("configure") => looks_like_autoconf_generated_configure(path),
_ => false,
}
}
fn extract_packages(path: &Path) -> Vec<PackageData> {
let name = path
.parent()
.and_then(|p| p.file_name())
.and_then(|n| n.to_str())
.map(|s| s.to_string())
.or_else(|| {
path.file_name()
.is_some_and(|name| name == "configure" || name == "configure.ac")
.then_some("input".to_string())
});
let purl = name.as_deref().and_then(|name| {
PackageUrl::new(Self::PACKAGE_TYPE.as_str(), name)
.ok()
.map(|purl| purl.to_string())
});
vec![PackageData {
package_type: Some(Self::PACKAGE_TYPE),
name,
datasource_id: Some(DatasourceId::AutotoolsConfigure),
purl,
..Default::default()
}]
}
}
crate::register_parser!(
"Autotools configure script",
&["**/configure", "**/configure.ac"],
"autotools",
"C",
Some("https://www.gnu.org/software/autoconf/"),
);