Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package ripcalc for openSUSE:Factory checked 
in at 2026-08-18 16:37:04
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/ripcalc (Old)
 and      /work/SRC/openSUSE:Factory/.ripcalc.new.1258 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "ripcalc"

Tue Aug 18 16:37:04 2026 rev:7 rq:1371631 version:0.4.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/ripcalc/ripcalc.changes  2026-05-26 
16:34:08.072251557 +0200
+++ /work/SRC/openSUSE:Factory/.ripcalc.new.1258/ripcalc.changes        
2026-08-18 16:37:48.213765948 +0200
@@ -1,0 +2,8 @@
+Sun Aug 16 20:05:37 UTC 2026 - Heitor Moreira da Silva <[email protected]>
+
+- Update to version 0.4.0:
+  * feat: ula random ipv6
+  * keepdata option
+  * tidyup function definitions
+
+-------------------------------------------------------------------

Old:
----
  ripcalc-0.3.1.obscpio

New:
----
  ripcalc-0.4.0.obscpio

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ ripcalc.spec ++++++
--- /var/tmp/diff_new_pack.fh1SLu/_old  2026-08-18 16:37:50.846860176 +0200
+++ /var/tmp/diff_new_pack.fh1SLu/_new  2026-08-18 16:37:50.851860355 +0200
@@ -18,7 +18,7 @@
 
 
 Name:           ripcalc
-Version:        0.3.1
+Version:        0.4.0
 Release:        0
 Summary:        Tool for network addresses
 License:        GPL-3.0-or-later

++++++ _service ++++++
--- /var/tmp/diff_new_pack.fh1SLu/_old  2026-08-18 16:37:50.937863433 +0200
+++ /var/tmp/diff_new_pack.fh1SLu/_new  2026-08-18 16:37:50.944863683 +0200
@@ -5,7 +5,7 @@
     <param name="changesgenerate">enable</param>
     <param name="filename">ripcalc</param>
     <param name="versionformat">@PARENT_TAG@</param>
-    <param name="revision">v0.3.1</param>
+    <param name="revision">v0.4.0</param>
     <param name="versionrewrite-pattern">v(.*)</param>
     <param name="versionrewrite-replacement">\1</param>
   </service>

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.fh1SLu/_old  2026-08-18 16:37:50.990865329 +0200
+++ /var/tmp/diff_new_pack.fh1SLu/_new  2026-08-18 16:37:51.002865759 +0200
@@ -1,7 +1,7 @@
 <servicedata>
   <service name="tar_scm">
     <param name="url">https://gitlab.com/edneville/ripcalc.git</param>
-    <param 
name="changesrevision">d206b49b3b1447102c9220fd3e2a66be8c68fcb9</param>
+    <param 
name="changesrevision">279ea7fdc5c3b033f4ed4b9d3d210896516b45c8</param>
   </service>
 </servicedata>
 (No newline at EOF)

++++++ ripcalc-0.3.1.obscpio -> ripcalc-0.4.0.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ripcalc-0.3.1/Cargo.lock new/ripcalc-0.4.0/Cargo.lock
--- old/ripcalc-0.3.1/Cargo.lock        2026-05-21 21:26:33.000000000 +0200
+++ new/ripcalc-0.4.0/Cargo.lock        2026-07-22 22:33:13.000000000 +0200
@@ -901,6 +901,15 @@
 ]
 
 [[package]]
+name = "ppv-lite86"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
+dependencies = [
+ "zerocopy",
+]
+
+[[package]]
 name = "proc-macro2"
 version = "1.0.103"
 source = "registry+https://github.com/rust-lang/crates.io-index";
@@ -925,6 +934,36 @@
 checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
 
 [[package]]
+name = "rand"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom 0.2.16",
+]
+
+[[package]]
 name = "regex"
 version = "1.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
@@ -1011,7 +1050,7 @@
 
 [[package]]
 name = "ripcalc"
-version = "0.3.1"
+version = "0.4.0"
 dependencies = [
  "cdb",
  "chrono",
@@ -1020,6 +1059,7 @@
  "getopts",
  "maxminddb",
  "nix",
+ "rand",
  "regex",
  "reqwest",
  "serde_json",
@@ -1831,6 +1871,26 @@
 ]
 
 [[package]]
+name = "zerocopy"
+version = "0.8.27"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.8.27"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
 name = "zerofrom"
 version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index";
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ripcalc-0.3.1/Cargo.toml new/ripcalc-0.4.0/Cargo.toml
--- old/ripcalc-0.3.1/Cargo.toml        2026-05-21 21:26:33.000000000 +0200
+++ new/ripcalc-0.4.0/Cargo.toml        2026-07-22 22:33:13.000000000 +0200
@@ -1,7 +1,7 @@
 [package]
 name = "ripcalc"
 description = "ripcalc, format and lookup IP addresses"
-version = "0.3.1"
+version = "0.4.0"
 authors = ["ed neville <[email protected]>"]
 edition = "2018"
 license = "GPL-3.0-or-later"
@@ -25,3 +25,4 @@
 serde_json = "1"
 chrono = "0.4"
 maxminddb = "0.28"
+rand = "0.8"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ripcalc-0.3.1/README.md new/ripcalc-0.4.0/README.md
--- old/ripcalc-0.3.1/README.md 2026-05-21 21:26:33.000000000 +0200
+++ new/ripcalc-0.4.0/README.md 2026-07-22 22:33:13.000000000 +0200
@@ -413,6 +413,7 @@
         --makethymecdb PATH
                         download and build a cdb file, defaults to
                         thyme.apnic.net data
+        --keepdata      keep data files after download
         --data-raw-table URL or PATH
                         URL/path of raw data
         --ipv6-raw-table URL or PATH
@@ -424,7 +425,7 @@
         --iterations NUMBER
                         top iterations, use --consume to produce whole report
                         of stdin
-        --noclear       don't print clear screen codes
+        --json PATH     json prefix file
         --mmlang LANG   the lang to use in results, defaults to en
         --mmasn PATH    path to maxmind geoip asn file
         --mmcity PATH   path to maxmind geoip city file
@@ -433,10 +434,11 @@
     -m, --mask CIDR     cidr mask
     -n, --networks CIDR instead of hosts, display number of subnets of this
                         size
-        --json PATH     json prefix file
+        --noclear       don't print clear screen codes
     -q, --quiet         don't report errors
     -r, --reverse       (none, inputs, sources or both) v4 octets, v6 hex
     -s, --file PATH     lookup addresses from, - for stdin
+        --ula           generate pseudorandom IPv6 ULA address
     -v, --version       print version
 ```
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ripcalc-0.3.1/changelog.md 
new/ripcalc-0.4.0/changelog.md
--- old/ripcalc-0.3.1/changelog.md      2026-05-21 21:26:33.000000000 +0200
+++ new/ripcalc-0.4.0/changelog.md      2026-07-22 22:33:13.000000000 +0200
@@ -1,3 +1,8 @@
+0.4.0
+
+ * --keepdata to store downloaded data
+ * --ula for a pseudorandom IPv6
+
 0.3.1
 
  * --filter should assume --inside unless told otherwise
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ripcalc-0.3.1/ripcalc.1 new/ripcalc-0.4.0/ripcalc.1
--- old/ripcalc-0.3.1/ripcalc.1 2026-05-21 21:26:33.000000000 +0200
+++ new/ripcalc-0.4.0/ripcalc.1 2026-07-22 22:33:13.000000000 +0200
@@ -1,7 +1,7 @@
 '\" t
 .\" Automatically generated by Pandoc 3.7.0.2
 .\"
-.TH "ripcalc" "1" "21 May 2026" "ripcalc 0.3.1" "User Manual"
+.TH "ripcalc" "1" "22 July 2026" "ripcalc 0.4.0" "User Manual"
 .SH NAME
 ripcalc \- a tool for network addresses
 .SH SYNOPSIS
@@ -41,7 +41,8 @@
 \f[B]ripcalc \(enmakecdb [PATH]\f[R]
 .PP
 \f[B]ripcalc \(enmakethymecdb [PATH] [\-\-data\-raw\-table PATH]
-[\-\-ipv6\-raw\-table PATH] [\-\-data\-used\-autnums PATH]\f[R]
+[\-\-ipv6\-raw\-table PATH] [\-\-data\-used\-autnums PATH]
+[\-\-keepdata]\f[R]
 .PP
 \f[B]ripcalc [\-\-mmasn PATH] [\-\-mmcountry PATH] [\-\-mmcity
 PATH]\f[R]
@@ -52,6 +53,8 @@
 .PP
 \f[B]ripcalc \(enfilternum [NUMBER] [CIDR]\f[R]
 .PP
+\f[B]ripcalc \(enula\f[R]
+.PP
 \f[B]ripcalc \-q/\-\-quiet\f[R]
 .PP
 \f[B]ripcalc \-h/\-\-help\f[R]
@@ -113,6 +116,9 @@
 Using \f[CR]\-\-abuseipdb [key]\f[R] can populate
 \f[CR]%{abuseipdb_var}\f[R] values for \f[CR]\-\-format\f[R] strings,
 where \f[CR]var\f[R] is the field name in a abuseipdb result structure.
+.PP
+\f[CR]\-\-ula\f[R] or the \f[CR]%{ula}\f[R] format string will return a
+pseudorandom address from \f[CR]fd00::/8\f[R].
 .SH CSV
 Network matches can be returned from a \f[B]CSV\f[R].
 .IP
@@ -379,5 +385,8 @@
 database from \f[CR]https://thyme.apnic.net/\f[R].
 Alternatively, if the argument does not start with http, it is treated
 as file to read instead of download.
+.PP
+The \f[CR]\-\-keepdata\f[R] option can be used to retain downloaded data
+from \f[CR]\-\-makethymecdb\f[R].
 .SH AUTHORS
 Ed Neville (ed\-ripcalc\(ats5h.net).
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ripcalc-0.3.1/ripcalc.md new/ripcalc-0.4.0/ripcalc.md
--- old/ripcalc-0.3.1/ripcalc.md        2026-05-21 21:26:33.000000000 +0200
+++ new/ripcalc-0.4.0/ripcalc.md        2026-07-22 22:33:13.000000000 +0200
@@ -2,9 +2,9 @@
 title: ripcalc
 section: 1
 header: User Manual
-footer: ripcalc 0.3.1
+footer: ripcalc 0.4.0
 author: Ed Neville ([email protected])
-date: 21 May 2026
+date: 22 July 2026
 ---
 
 # NAME
@@ -45,7 +45,7 @@
 
 **ripcalc --makecdb [PATH]**
 
-**ripcalc --makethymecdb [PATH] [--data-raw-table PATH] [--ipv6-raw-table 
PATH] [--data-used-autnums PATH]**
+**ripcalc --makethymecdb [PATH] [--data-raw-table PATH] [--ipv6-raw-table 
PATH] [--data-used-autnums PATH] [--keepdata]**
 
 **ripcalc [--mmasn PATH] [--mmcountry PATH] [--mmcity PATH]**
 
@@ -55,6 +55,8 @@
 
 **ripcalc --filternum [NUMBER] [CIDR]**
 
+**ripcalc --ula**
+
 **ripcalc -q/--quiet**
 
 **ripcalc -h/--help**
@@ -89,6 +91,8 @@
 
 Using `--abuseipdb [key]` can populate `%{abuseipdb_var}` values for 
`--format` strings, where `var` is the field name in a abuseipdb result 
structure.
 
+`--ula` or the `%{ula}` format string will return a pseudorandom address from 
`fd00::/8`.
+
 # CSV
 
 Network matches can be returned from a **CSV**.
@@ -209,3 +213,5 @@
 
 The option `--makethymecdb` can be used to download an IP database from 
`https://thyme.apnic.net/`. Alternatively, if the argument does not start with 
http, it is treated as file to read instead of download.
 
+The `--keepdata` option can be used to retain downloaded data from 
`--makethymecdb`.
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ripcalc-0.3.1/src/lib.rs new/ripcalc-0.4.0/src/lib.rs
--- old/ripcalc-0.3.1/src/lib.rs        2026-05-21 21:26:33.000000000 +0200
+++ new/ripcalc-0.4.0/src/lib.rs        2026-07-22 22:33:13.000000000 +0200
@@ -7,6 +7,7 @@
 use nix::sys::socket::SockaddrStorage;
 use nix::sys::stat::fstat;
 use nix::sys::stat::SFlag;
+use rand::Rng;
 use regex::*;
 use reqwest::header::HeaderMap;
 use serde_json::*;
@@ -1374,20 +1375,36 @@
     None
 }
 
-pub fn config_option_true(config: &Config, opt: String) -> bool {
-    config.options.get(&opt).unwrap_or(&"false".to_string()) != "false"
+pub fn config_option_true(config: &Config, opt: &str) -> bool {
+    config.options.get(opt).unwrap_or(&"false".to_string()) != "false"
 }
 
-fn getnameforlang<'a>(names : &'a maxminddb::geoip2::Names, lang : &str) -> 
Option<&'a str> {
-    if lang == "de" { return names.german }
-    if lang == "en" { return names.english }
-    if lang == "es" { return names.spanish }
-    if lang == "fr" { return names.french }
-    if lang == "jp" { return names.japanese }
-    if lang == "pt-BR" { return names.brazilian_portuguese }
-    if lang == "ru" { return names.russian }
-    if lang == "zh-CN" { return names.simplified_chinese }
-    return None
+fn getnameforlang<'a>(names: &'a maxminddb::geoip2::Names, lang: &str) -> 
Option<&'a str> {
+    if lang == "de" {
+        return names.german;
+    }
+    if lang == "en" {
+        return names.english;
+    }
+    if lang == "es" {
+        return names.spanish;
+    }
+    if lang == "fr" {
+        return names.french;
+    }
+    if lang == "jp" {
+        return names.japanese;
+    }
+    if lang == "pt-BR" {
+        return names.brazilian_portuguese;
+    }
+    if lang == "ru" {
+        return names.russian;
+    }
+    if lang == "zh-CN" {
+        return names.simplified_chinese;
+    }
+    None
 }
 
 pub fn geoip_city(config: &RefCell<Config>, ip: &Ip) -> Option<HashMap<String, 
String>> {
@@ -1416,17 +1433,17 @@
         let default_lang = "en".to_string();
         let lang = config.options.get("mmlang").unwrap_or(&default_lang);
 
-        let c = getnameforlang(&city.city.names,lang);
+        let c = getnameforlang(&city.city.names, lang);
         if let Some(c) = c {
             ret.insert("city".to_string(), c.to_string());
         }
 
-        let c = getnameforlang(&city.continent.names,lang);
+        let c = getnameforlang(&city.continent.names, lang);
         if let Some(c) = c {
             ret.insert("continent".to_string(), c.to_string());
         }
 
-        let c = getnameforlang(&city.continent.names,lang);
+        let c = getnameforlang(&city.continent.names, lang);
         if let Some(c) = c {
             ret.insert("country".to_string(), c.to_string());
         }
@@ -1509,24 +1526,26 @@
 
     let f: IpAddr = ip.to_string().parse().unwrap();
 
-    if let Ok(Ok(Some(Some(country)))) = reader.lookup(f).map(|r| 
r.decode::<Option<geoip2::Country>>()) {
+    if let Ok(Ok(Some(Some(country)))) = reader
+        .lookup(f)
+        .map(|r| r.decode::<Option<geoip2::Country>>())
+    {
         let config = config.borrow();
         let default_lang = "en".to_string();
         let lang = config.options.get("mmlang").unwrap_or(&default_lang);
 
         let c = &country.continent.names;
 
-        if let Some(c) = getnameforlang(c,lang) {
+        if let Some(c) = getnameforlang(c, lang) {
             ret.insert("continent".to_string(), c.to_string());
         }
 
-
-        let c = getnameforlang(&country.country.names,lang);
+        let c = getnameforlang(&country.country.names, lang);
         if let Some(c) = c {
             ret.insert("country".to_string(), c.to_string());
         }
 
-        let c = getnameforlang(&country.registered_country.names,lang);
+        let c = getnameforlang(&country.registered_country.names, lang);
         if let Some(c) = c {
             ret.insert("registered_country".to_string(), c.to_string());
         }
@@ -1536,6 +1555,42 @@
     None
 }
 
+pub fn ula_gen() -> Ip {
+    let mut rng = rand::thread_rng();
+    Ip {
+        address: ula_subnet_interface(rng.gen_range(0..=65535)),
+        cidr: 48,
+    }
+}
+
+pub fn ula_subnet_interface(subnet_id: u16) -> Addr {
+    let mut rng = rand::thread_rng();
+    let mut global_id = [0u8; 5];
+
+    let _ = rng.try_fill(&mut global_id);
+
+    let ip_bytes: [u8; 16] = [
+        0xfd,
+        global_id[0],
+        global_id[1],
+        global_id[2],
+        global_id[3],
+        global_id[4],
+        (subnet_id >> 8) as u8,
+        (subnet_id & 0xFF) as u8,
+        rng.gen_range(0..=255),
+        rng.gen_range(0..=255),
+        rng.gen_range(0..=255),
+        rng.gen_range(0..=255),
+        rng.gen_range(0..=255),
+        rng.gen_range(0..=255),
+        rng.gen_range(0..=255),
+        rng.gen_range(0..=255),
+    ];
+
+    Addr::V6(Ipv6Addr::from(ip_bytes))
+}
+
 pub fn format_details(
     format_detail: &FormatDetail,
     formatted: String,
@@ -1583,9 +1638,7 @@
         }
     }
 
-    if config_option_true(&config.borrow(), "abuseipdb".to_string())
-        && reformatted.contains("%{abuseipdb")
-    {
+    if config_option_true(&config.borrow(), "abuseipdb") && 
reformatted.contains("%{abuseipdb") {
         let key = config
             .borrow()
             .options
@@ -1616,9 +1669,9 @@
         }
     }
 
-    if (config_option_true(&config.borrow(), "mmasn".to_string())
-        || config_option_true(&config.borrow(), "mmcity".to_string())
-        || config_option_true(&config.borrow(), "mmcountry".to_string()))
+    if (config_option_true(&config.borrow(), "mmasn")
+        || config_option_true(&config.borrow(), "mmcity")
+        || config_option_true(&config.borrow(), "mmcountry"))
         && (reformatted.contains("%{mmcity")
             || reformatted.contains("%{mmcountry")
             || reformatted.contains("%{mmasn"))
@@ -1654,6 +1707,10 @@
         }
     }
 
+    while reformatted.contains("%{ula}") {
+        reformatted = reformatted.replacen("%{ula}", &ula_gen().to_string(), 
1);
+    }
+
     if let Some(line) = &format_detail.line {
         reformatted = reformatted.replace("%{line}", line);
     }
@@ -1757,9 +1814,7 @@
                     }
                     'C' => {
                         let config = config.borrow();
-                        if config_option_true(&config, "countseen".to_string())
-                            && config.count.is_some()
-                        {
+                        if config_option_true(&config, "countseen") && 
config.count.is_some() {
                             out_str.push_str(&get_seen_count(&config, 
ip).to_string());
                         } else if let Some(used) = &config.used {
                             let count = used.keys().count();
@@ -1933,7 +1988,7 @@
 pub fn group_mask(config: &Config, i: &Ip) -> u32 {
     match i.address {
         Addr::V4(_) => {
-            if config_option_true(config, "group4".to_string()) {
+            if config_option_true(config, "group4") {
                 return config
                     .options
                     .get("group4")
@@ -1943,7 +1998,7 @@
             }
         }
         Addr::V6(_) => {
-            if config_option_true(config, "group6".to_string()) {
+            if config_option_true(config, "group6") {
                 return config
                     .options
                     .get("group6")
@@ -1954,7 +2009,7 @@
         }
     }
 
-    if config_option_true(config, "group".to_string()) {
+    if config_option_true(config, "group") {
         return config
             .options
             .get("group")
@@ -1967,13 +2022,13 @@
 }
 
 pub fn config_cidr_grouping(config: &Config) -> bool {
-    config_option_true(config, "group".to_string())
-        || config_option_true(config, "group4".to_string())
-        || config_option_true(config, "group6".to_string())
+    config_option_true(config, "group")
+        || config_option_true(config, "group4")
+        || config_option_true(config, "group6")
 }
 
 pub fn increment_seen_count(config: &mut Config, ip: Ip) {
-    if config_option_true(config, "countseen".to_string()) && 
config.count.is_some() {
+    if config_option_true(config, "countseen") && config.count.is_some() {
         let cidr = group_mask(config, &ip);
 
         let net_mask = network(&Ip {
@@ -1987,7 +2042,7 @@
 }
 
 pub fn get_seen_count(config: &Config, i: &Ip) -> usize {
-    if config_option_true(config, "countseen".to_string()) && 
config.count.is_some() {
+    if config_option_true(config, "countseen") && config.count.is_some() {
         let net_mask = network(&Ip {
             address: i.address.clone(),
             cidr: config
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ripcalc-0.3.1/src/main.rs 
new/ripcalc-0.4.0/src/main.rs
--- old/ripcalc-0.3.1/src/main.rs       2026-05-21 21:26:33.000000000 +0200
+++ new/ripcalc-0.4.0/src/main.rs       2026-07-22 22:33:13.000000000 +0200
@@ -182,8 +182,8 @@
         let nets = 
matches.opt_str("networks").unwrap().trim().parse().unwrap();
 
         if nets < ip.cidr
-            && !(config_option_true(&config.borrow(), 
"encapsulating".to_string())
-                && config_option_true(&config.borrow(), "group".to_string()))
+            && !(config_option_true(&config.borrow(), "encapsulating")
+                && config_option_true(&config.borrow(), "group"))
         {
             eprintln!("{} is bigger than the network mask {}", nets, ip.cidr);
             std::process::exit(1);
@@ -393,7 +393,7 @@
 
 fn process_json(
     u: &Value,
-    field_name: String,
+    field_name: &str,
     rows: &mut Option<HashMap<Ip, NetRow>>,
     input_base: Option<i32>,
     _reverse: &Reverse,
@@ -440,7 +440,7 @@
 fn process_csv(
     config: &RefCell<Config>,
     mut reader: csv::Reader<File>,
-    field_name: String,
+    field_name: &str,
     rows: &mut Option<HashMap<Ip, NetRow>>,
     input_base: Option<i32>,
     reverse: &Reverse,
@@ -449,7 +449,7 @@
     let mut header_names: Vec<String> = vec![];
     let mut field_num: Option<usize> = None;
     for i in 0..headers.len() {
-        if headers[i] == field_name {
+        if headers[i] == *field_name {
             field_num = Some(i);
         }
         header_names.push(headers[i].to_string());
@@ -463,7 +463,7 @@
 
     for result in reader.records() {
         if result.is_err() {
-            if !config_option_true(&config.borrow(), "quiet".to_string()) {
+            if !config_option_true(&config.borrow(), "quiet") {
                 eprintln!("{:?}", result.err());
             }
             continue;
@@ -483,7 +483,7 @@
             );
 
             if ip.is_none() {
-                if !config_option_true(&config.borrow(), "quiet".to_string()) {
+                if !config_option_true(&config.borrow(), "quiet") {
                     eprintln!("Could not parse {:?}", result.err());
                 }
                 continue;
@@ -524,7 +524,7 @@
 
     increment_seen_count(&mut config.borrow_mut(), i.clone());
 
-    if config_option_true(&config.borrow(), "group".to_string()) {
+    if config_option_true(&config.borrow(), "group") {
         match i.address {
             Addr::V4(_) => {
                 if network_size > 32 {
@@ -633,11 +633,11 @@
 }
 
 fn filter_pos(config: &RefCell<Config>) -> Option<u32> {
-    if config_option_true(&config.borrow(), "filterany".to_string()) {
+    if config_option_true(&config.borrow(), "filterany") {
         return None;
     }
 
-    let position: u32 = if config_option_true(&config.borrow(), 
"filter".to_string()) {
+    let position: u32 = if config_option_true(&config.borrow(), "filter") {
         match config
             .borrow()
             .options
@@ -760,7 +760,7 @@
         std::process::exit(0);
     }
 
-    if config_option_true(&config.borrow(), "encapsulating".to_string()) {
+    if config_option_true(&config.borrow(), "encapsulating") {
         let mut used: HashMap<Ip, bool> = HashMap::new();
         let mut network_size: u32 = 0;
 
@@ -834,7 +834,7 @@
     let _ = cdb.add("version".as_bytes(), "1".as_bytes());
 }
 
-fn make_cdb(path: String, config: &RefCell<Config>) {
+fn make_cdb(path: &str, config: &RefCell<Config>) {
     let reader = BufReader::new(std::io::stdin());
     let tmp_file = format!("{}.tmp", path);
     let cdb = cdb::CDBWriter::create(&tmp_file);
@@ -885,15 +885,15 @@
         std::process::exit(1);
     }
 
-    if fs::rename(&tmp_file, &path).is_err() {
+    if fs::rename(&tmp_file, path).is_err() {
         eprintln!("Cannot rename {} to {}", tmp_file, path);
         std::process::exit(1);
     }
 }
 
-fn make_thyme_cdb(location: String, config: &RefCell<Config>) {
+fn make_thyme_cdb(location: &str, config: &RefCell<Config>) {
     fn process_raw(
-        data: String,
+        data: &str,
         db: &mut HashMap<String, String>,
         asn_list: &mut HashMap<String, Vec<String>>,
     ) {
@@ -919,7 +919,7 @@
     }
 
     fn process_ipv6(
-        data: String,
+        data: &str,
         db: &mut HashMap<String, String>,
         asn_list: &mut HashMap<String, Vec<String>>,
     ) {
@@ -946,7 +946,7 @@
 
     fn process_asn(
         cdb: &mut cdb::CDBWriter,
-        lines: String,
+        lines: &str,
         asn: &mut HashMap<String, String>,
         data: &HashMap<String, String>,
         asn_list: &mut HashMap<String, Vec<String>>,
@@ -1039,6 +1039,21 @@
         }
     }
 
+    fn save_data(data_file: &str, source: &str, path: &str) -> bool {
+        use std::io::BufWriter;
+
+        if !source.starts_with("http") {
+            return false;
+        }
+
+        let file = File::create(path).unwrap_or_else(|_| panic!("Cannot open 
{}", path));
+        let mut writer = BufWriter::new(file);
+
+        let _ = writer.write(data_file.as_bytes());
+
+        true
+    }
+
     let client = ua_client().expect("Cannot make user-agent");
     let tmp_file = format!("{}.tmp", &location);
     let cdb = cdb::CDBWriter::create(&tmp_file);
@@ -1057,19 +1072,37 @@
         config.borrow().options.get("data-raw-table").unwrap(),
         &client,
     );
-    process_raw(data_raw, &mut data, &mut asn_list);
+    let _ = config_option_true(&config.borrow(), "keep_data")
+        && save_data(
+            &data_raw,
+            config.borrow().options.get("data-raw-table").unwrap(),
+            "data-raw-table",
+        );
+    process_raw(&data_raw, &mut data, &mut asn_list);
 
     let ipv6_raw = get_or_exit(
         config.borrow().options.get("ipv6-raw-table").unwrap(),
         &client,
     );
-    process_ipv6(ipv6_raw, &mut data, &mut asn_list);
+    let _ = config_option_true(&config.borrow(), "keep_data")
+        && save_data(
+            &ipv6_raw,
+            config.borrow().options.get("ipv6-raw-table").unwrap(),
+            "ipv6-raw-table",
+        );
+    process_ipv6(&ipv6_raw, &mut data, &mut asn_list);
 
     let data_used = get_or_exit(
         config.borrow().options.get("data-used-autnums").unwrap(),
         &client,
     );
-    process_asn(&mut cdb, data_used, &mut asn, &data, &mut asn_list);
+    let _ = config_option_true(&config.borrow(), "keep_data")
+        && save_data(
+            &data_used,
+            config.borrow().options.get("data-used-autnums").unwrap(),
+            "data-used-autnums",
+        );
+    process_asn(&mut cdb, &data_used, &mut asn, &data, &mut asn_list);
 
     generated_date(&mut cdb, Utc::now());
 
@@ -1078,7 +1111,7 @@
         std::process::exit(1);
     }
 
-    if let Err(error) = fs::rename(&tmp_file, &location) {
+    if let Err(error) = fs::rename(&tmp_file, location) {
         eprintln!("Cannot rename {} to {}: {}", &tmp_file, &location, &error);
         std::process::exit(1);
     }
@@ -1159,6 +1192,7 @@
         "download and build a cdb file, defaults to thyme.apnic.net data",
         "PATH",
     );
+    opts.optflag("", "keepdata", "keep data files after download");
     opts.optopt("", "data-raw-table", "URL/path of raw data", "URL or PATH");
     opts.optopt("", "ipv6-raw-table", "URL/path of ipv6 data", "URL or PATH");
     opts.optopt(
@@ -1176,7 +1210,7 @@
         "top iterations, use --consume to produce whole report of stdin",
         "NUMBER",
     );
-    opts.optflag("", "noclear", "don't print clear screen codes");
+    opts.optopt("", "json", "json prefix file", "PATH");
 
     opts.optopt(
         "",
@@ -1201,7 +1235,8 @@
         "CIDR",
     );
 
-    opts.optopt("", "json", "json prefix file", "PATH");
+    opts.optflag("", "noclear", "don't print clear screen codes");
+
     opts.optflag("q", "quiet", "don't report errors");
 
     opts.optopt(
@@ -1212,6 +1247,7 @@
     );
     opts.optopt("s", "file", "lookup addresses from, - for stdin", "PATH");
 
+    opts.optflag("", "ula", "generate pseudorandom IPv6 ULA address");
     opts.optflag("v", "version", "print version");
 }
 
@@ -1302,7 +1338,7 @@
     };
 
     let iterations = {
-        if config_option_true(&config.borrow(), "iterations".to_string()) {
+        if config_option_true(&config.borrow(), "iterations") {
             let cfg = config.borrow();
             let iterations = cfg.options.get("iterations").unwrap();
 
@@ -1316,10 +1352,10 @@
         }
     };
 
-    if config_option_true(&config.borrow(), "consume".to_string()) {
+    if config_option_true(&config.borrow(), "consume") {
         loop {
             let config = count.lock().unwrap().clone();
-            if !config_option_true(config.borrow(), "consumed".to_string()) {
+            if !config_option_true(config.borrow(), "consumed") {
                 sleep(100);
             } else {
                 break;
@@ -1328,7 +1364,7 @@
     }
 
     let mut loops = 0;
-    let clear_screen = !config_option_true(&config.borrow(), 
"noclear".to_string());
+    let clear_screen = !config_option_true(&config.borrow(), "noclear");
     let mut totals: Vec<usize> = vec![];
     loop {
         if clear_screen {
@@ -1623,7 +1659,14 @@
             "network".to_string()
         };
 
-        process_csv(&config, reader, field_name, &mut rows, input_base, 
&reverse);
+        process_csv(
+            &config,
+            reader,
+            &field_name,
+            &mut rows,
+            input_base,
+            &reverse,
+        );
     }
 
     if matches.opt_present("json") {
@@ -1639,7 +1682,7 @@
 
             let u: Value =
                 serde_json::from_reader(reader).unwrap_or_else(|_| 
panic!("Cannot parse {}", path));
-            process_json(&u, field_name, &mut rows, input_base, &reverse, 
&config);
+            process_json(&u, &field_name, &mut rows, input_base, &reverse, 
&config);
         }
     }
 
@@ -1688,7 +1731,7 @@
             .borrow_mut()
             .options
             .insert("encapsulating".to_string(), "true".to_string());
-        if !config_option_true(&config.borrow(), "group".to_string()) {
+        if !config_option_true(&config.borrow(), "group") {
             config
                 .borrow_mut()
                 .options
@@ -1700,6 +1743,13 @@
         input_mask = parse_mask(&v);
     }
 
+    if matches.opt_present("keepdata") {
+        config
+            .borrow_mut()
+            .options
+            .insert("keep_data".to_string(), "true".to_string());
+    }
+
     if let Some(v) = matches.opt_str("makethymecdb") {
         config.borrow_mut().options.insert(
             "data-raw-table".to_string(),
@@ -1720,12 +1770,12 @@
                 
.unwrap_or("https://thyme.apnic.net/.combined/data-used-autnums".to_string()),
         );
 
-        make_thyme_cdb(v, &config);
+        make_thyme_cdb(&v, &config);
         std::process::exit(0);
     }
 
     if let Some(v) = matches.opt_str("makecdb") {
-        make_cdb(v, &config);
+        make_cdb(&v, &config);
         std::process::exit(0);
     }
 
@@ -1875,6 +1925,10 @@
         }
     }
 
+    if matches.opt_present("ula") {
+        ip_args = vec![ula_gen()];
+    }
+
     let mut used: HashMap<Ip, bool> = HashMap::new();
 
     for arg in &ip_args {
@@ -1893,7 +1947,7 @@
             }
         }
 
-        if config_option_true(&config.borrow(), "encapsulating".to_string()) {
+        if config_option_true(&config.borrow(), "encapsulating") {
             let mut network_size: u32 = 0;
 
             if let Some(group) = config.borrow().options.get("group") {
@@ -1918,7 +1972,7 @@
 
     config.borrow_mut().used = Some(used.clone());
 
-    if config_option_true(&config.borrow(), "encapsulating".to_string()) {
+    if config_option_true(&config.borrow(), "encapsulating") {
         process_group_encapsulation(&matches, used, &rows, &config);
         std::process::exit(0);
     }

++++++ ripcalc.obsinfo ++++++
--- /var/tmp/diff_new_pack.fh1SLu/_old  2026-08-18 16:37:51.312876852 +0200
+++ /var/tmp/diff_new_pack.fh1SLu/_new  2026-08-18 16:37:51.321877174 +0200
@@ -1,5 +1,5 @@
 name: ripcalc
-version: 0.3.1
-mtime: 1779391593
-commit: d206b49b3b1447102c9220fd3e2a66be8c68fcb9
+version: 0.4.0
+mtime: 1784752393
+commit: 279ea7fdc5c3b033f4ed4b9d3d210896516b45c8
 

++++++ vendor.tar.zst ++++++
/work/SRC/openSUSE:Factory/ripcalc/vendor.tar.zst 
/work/SRC/openSUSE:Factory/.ripcalc.new.1258/vendor.tar.zst differ: char 7, 
line 1

Reply via email to