]> git.proxmox.com Git - proxmox-backup.git/commitdiff
parse_zpool_status_field: handle tabs without copying input
authorDietmar Maurer <dietmar@proxmox.com>
Thu, 18 Jun 2020 17:40:01 +0000 (19:40 +0200)
committerDietmar Maurer <dietmar@proxmox.com>
Thu, 18 Jun 2020 17:40:01 +0000 (19:40 +0200)
src/tools/disks/zpool_status.rs

index c56882de94adb690cd39158119e39bf003542db3..e2bcfa6ab31ffcc1946066ca02bbe51e7bb83f2e 100644 (file)
@@ -3,7 +3,8 @@ use serde_json::{json, Value};
 use ::serde::{Deserialize, Serialize};
 
 use crate::tools::nom::{
-    parse_complete, parse_failure, multispace0, multispace1, notspace1, parse_u64, IResult,
+    parse_complete, parse_error, parse_failure,
+    multispace0, multispace1, notspace1, parse_u64, IResult,
 };
 
 use nom::{
@@ -101,6 +102,30 @@ fn parse_zpool_status_tree(i: &str) -> IResult<&str, Vec<ZFSPoolVDevState>> {
     many1(parse_zpool_status_vdev)(i)
 }
 
+fn space_indented_line(indent: usize) -> impl Fn(&str) -> IResult<&str, &str> {
+    move |i| {
+        let mut len = 0;
+        let mut n = i;
+        loop {
+            if n.starts_with('\t') {
+                len += 8;
+                n = &n[1..];
+            } else if n.starts_with(' ') {
+                len += 1;
+                n = &n[1..];
+            } else {
+                break;
+            }
+            if len >= indent { break; }
+        };
+        if len != indent {
+            return Err(parse_error(i, "not correctly indented"));
+        }
+
+        take_while1(|c| c != '\n')(n)
+    }
+}
+
 fn parse_zpool_status_field(i: &str) -> IResult<&str, (String, String)> {
     let (i, prefix) = take_while1(|c| c != ':')(i)?;
     let (i, _) = tag(":")(i)?;
@@ -111,9 +136,11 @@ fn parse_zpool_status_field(i: &str) -> IResult<&str, (String, String)> {
 
     let field = prefix.trim().to_string();
 
-    let indent = (0..prefix.len()+2).fold(String::new(), |mut acc, _| { acc.push(' '); acc });
+    let prefix_len = expand_tab_length(prefix);
+
+    let indent: usize = prefix_len + 2;
 
-    let parse_continuation = opt(preceded(tag(indent.as_str()), take_while1(|c| c != '\n')));
+    let parse_continuation = opt(space_indented_line(indent));
 
     let mut value = value.to_string();
 
@@ -148,7 +175,6 @@ pub fn parse_zpool_status_config_tree(i: &str) -> Result<Vec<ZFSPoolVDevState>,
 }
 
 fn parse_zpool_status(input: &str) -> Result<Vec<(String, String)>, Error> {
-    let input = input.replace('\t', "        "); // important!
     parse_complete("zfs status output", &input, many0(parse_zpool_status_field))
 }