File: cfbtool.rs

package info (click to toggle)
rust-cfb 0.10.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 580 kB
  • sloc: makefile: 2
file content (120 lines) | stat: -rw-r--r-- 3,542 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use std::io;
use std::path::PathBuf;

use clap::{Parser, Subcommand};
use time::OffsetDateTime;
use uuid::Uuid;

#[derive(Parser, Debug)]
#[clap(author, about, long_about = None)]
struct Cli {
    #[clap(subcommand)]
    command: Command,
}

#[derive(Subcommand, Debug)]
enum Command {
    /// Concatenates and prints streams
    Cat { path: Vec<String> },

    /// Changes storage CLSIDs
    Chcls { clsid: Uuid, path: Vec<String> },

    /// Lists storage contents
    Ls {
        #[clap(short, long)]
        /// Lists in long format
        long: bool,

        #[clap(short, long)]
        /// Includes . in output
        all: bool,

        path: Vec<String>,
    },
}

fn split(path: &str) -> (PathBuf, PathBuf) {
    let mut pieces = path.splitn(2, ':');
    if let Some(piece1) = pieces.next() {
        if let Some(piece2) = pieces.next() {
            (PathBuf::from(piece1), PathBuf::from(piece2))
        } else {
            (PathBuf::from(piece1), PathBuf::new())
        }
    } else {
        (PathBuf::new(), PathBuf::new())
    }
}

fn list_entry(name: &str, entry: &cfb::Entry, long: bool) {
    if !long {
        println!("{}", entry.name());
        return;
    }
    let length = if entry.len() >= 10_000_000_000 {
        format!("{} GB", entry.len() / (1 << 30))
    } else if entry.len() >= 100_000_000 {
        format!("{} MB", entry.len() / (1 << 20))
    } else if entry.len() >= 1_000_000 {
        format!("{} kB", entry.len() / (1 << 10))
    } else {
        format!("{} B ", entry.len())
    };
    let last_modified = {
        let timestamp = entry.created().max(entry.modified());
        let datetime = OffsetDateTime::from(timestamp);
        let (year, month, day) = datetime.to_calendar_date();
        format!("{:04}-{:02}-{:02}", year, month as u8, day)
    };
    println!(
        "{}{:08x}   {:>10}   {}   {}",
        if entry.is_storage() { '+' } else { '-' },
        entry.state_bits(),
        length,
        last_modified,
        name
    );
    if entry.is_storage() {
        println!(" {}", entry.clsid().hyphenated());
    }
}

fn main() {
    let cli = Cli::parse();
    match cli.command {
        Command::Cat { path } => {
            for path in path {
                let (comp_path, inner_path) = split(&path);
                let mut comp = cfb::open(&comp_path).unwrap();
                let mut stream = comp.open_stream(inner_path).unwrap();
                io::copy(&mut stream, &mut io::stdout()).unwrap();
            }
        }
        Command::Chcls { clsid, path } => {
            for path in path {
                let (comp_path, inner_path) = split(&path);
                let mut comp = cfb::open(&comp_path).unwrap();
                comp.set_storage_clsid(inner_path, clsid).unwrap();
                comp.flush().unwrap();
            }
        }
        Command::Ls { long, all, path } => {
            for path in path {
                let (comp_path, inner_path) = split(&path);
                let comp = cfb::open(&comp_path).unwrap();
                let entry = comp.entry(&inner_path).unwrap();
                if entry.is_stream() {
                    list_entry(entry.name(), &entry, long);
                } else {
                    if all {
                        list_entry(".", &entry, long);
                    }
                    for subentry in comp.read_storage(&inner_path).unwrap() {
                        list_entry(subentry.name(), &subentry, long);
                    }
                }
            }
        }
    }
}