//
// Syd: rock-solid application kernel
// src/utils/syd-net.rs: Aggregate IP networks
//
// Copyright (c) 2024, 2025 Ali Polatel <alip@chesswob.org>
//
// SPDX-License-Identifier: GPL-3.0

use std::{
    fs::File,
    io::{stdin, BufReader},
    process::ExitCode,
};

use syd::sandbox::Sandbox;

// Set global allocator to GrapheneOS allocator.
#[cfg(all(
    not(coverage),
    not(feature = "prof"),
    not(target_os = "android"),
    target_page_size_4k,
    target_pointer_width = "64"
))]
#[global_allocator]
static GLOBAL: hardened_malloc::HardenedMalloc = hardened_malloc::HardenedMalloc;

// Set global allocator to tcmalloc if profiling is enabled.
#[cfg(feature = "prof")]
#[global_allocator]
static GLOBAL: tcmalloc::TCMalloc = tcmalloc::TCMalloc;

syd::main! {
    use lexopt::prelude::*;

    syd::set_sigpipe_dfl()?;

    // Parse CLI options.
    //
    // Note, option parsing is POSIXly correct:
    // POSIX recommends that no more options are parsed after the first
    // positional argument. The other arguments are then all treated as
    // positional arguments.
    // See: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html#tag_12_02
    let mut paths = Vec::new();
    let mut parser = lexopt::Parser::from_env();
    while let Some(arg) = parser.next()? {
        match arg {
            Short('h') => {
                help();
                return Ok(ExitCode::SUCCESS);
            }
            Value(val) => {
                paths.push(val);
                paths.extend(parser.raw_args()?);
            }
            _ => return Err(arg.unexpected().into()),
        }
    }

    let mut sin = true; // read standard input.
    let mut syd = Sandbox::new();
    for path in paths {
        sin = false;

        #[expect(clippy::disallowed_methods)]
        let file = File::open(path)?;
        syd.parse_netset(BufReader::new(file))?;
    }

    if sin {
        let file = stdin();
        syd.parse_netset(BufReader::new(file))?;
    }

    syd.rule_agg_block("")?;
    for addr in syd.block4() {
        println!("{addr}");
    }
    for addr in syd.block6() {
        println!("{addr}");
    }

    Ok(ExitCode::SUCCESS)
}

fn help() {
    println!("Usage: syd-net [-h] <path>...");
    println!("Tool to aggregate IP networks.");
    println!("Reads IP networks from the given list of paths.");
    println!("Given no arguments, reads from standard input.");
}
