uu_sum/
sum.rs

1// This file is part of the uutils coreutils package.
2//
3// For the full copyright and license information, please view the LICENSE
4// file that was distributed with this source code.
5
6// spell-checker:ignore (ToDO) sysv
7
8use clap::{crate_version, Arg, ArgAction, Command};
9use std::fs::File;
10use std::io::{stdin, Read};
11use std::path::Path;
12use uucore::display::Quotable;
13use uucore::error::{FromIo, UResult, USimpleError};
14use uucore::{format_usage, help_about, help_usage, show};
15
16const USAGE: &str = help_usage!("sum.md");
17const ABOUT: &str = help_about!("sum.md");
18
19fn bsd_sum(mut reader: Box<dyn Read>) -> (usize, u16) {
20    let mut buf = [0; 4096];
21    let mut bytes_read = 0;
22    let mut checksum: u16 = 0;
23    loop {
24        match reader.read(&mut buf) {
25            Ok(n) if n != 0 => {
26                bytes_read += n;
27                for &byte in &buf[..n] {
28                    checksum = checksum.rotate_right(1);
29                    checksum = checksum.wrapping_add(u16::from(byte));
30                }
31            }
32            _ => break,
33        }
34    }
35
36    // Report blocks read in terms of 1024-byte blocks.
37    let blocks_read = bytes_read.div_ceil(1024);
38    (blocks_read, checksum)
39}
40
41fn sysv_sum(mut reader: Box<dyn Read>) -> (usize, u16) {
42    let mut buf = [0; 4096];
43    let mut bytes_read = 0;
44    let mut ret = 0u32;
45
46    loop {
47        match reader.read(&mut buf) {
48            Ok(n) if n != 0 => {
49                bytes_read += n;
50                for &byte in &buf[..n] {
51                    ret = ret.wrapping_add(u32::from(byte));
52                }
53            }
54            _ => break,
55        }
56    }
57
58    ret = (ret & 0xffff) + (ret >> 16);
59    ret = (ret & 0xffff) + (ret >> 16);
60
61    // Report blocks read in terms of 512-byte blocks.
62    let blocks_read = bytes_read.div_ceil(512);
63    (blocks_read, ret as u16)
64}
65
66fn open(name: &str) -> UResult<Box<dyn Read>> {
67    match name {
68        "-" => Ok(Box::new(stdin()) as Box<dyn Read>),
69        _ => {
70            let path = &Path::new(name);
71            if path.is_dir() {
72                return Err(USimpleError::new(
73                    2,
74                    format!("{}: Is a directory", name.maybe_quote()),
75                ));
76            };
77            // Silent the warning as we want to the error message
78            if path.metadata().is_err() {
79                return Err(USimpleError::new(
80                    2,
81                    format!("{}: No such file or directory", name.maybe_quote()),
82                ));
83            };
84            let f = File::open(path).map_err_context(String::new)?;
85            Ok(Box::new(f) as Box<dyn Read>)
86        }
87    }
88}
89
90mod options {
91    pub static FILE: &str = "file";
92    pub static BSD_COMPATIBLE: &str = "r";
93    pub static SYSTEM_V_COMPATIBLE: &str = "sysv";
94}
95
96#[uucore::main]
97pub fn uumain(args: impl uucore::Args) -> UResult<()> {
98    let matches = uu_app().try_get_matches_from(args)?;
99
100    let files: Vec<String> = match matches.get_many::<String>(options::FILE) {
101        Some(v) => v.cloned().collect(),
102        None => vec!["-".to_owned()],
103    };
104
105    let sysv = matches.get_flag(options::SYSTEM_V_COMPATIBLE);
106
107    let print_names = files.len() > 1 || files[0] != "-";
108    let width = if sysv { 1 } else { 5 };
109
110    for file in &files {
111        let reader = match open(file) {
112            Ok(f) => f,
113            Err(error) => {
114                show!(error);
115                continue;
116            }
117        };
118        let (blocks, sum) = if sysv {
119            sysv_sum(reader)
120        } else {
121            bsd_sum(reader)
122        };
123
124        if print_names {
125            println!("{sum:0width$} {blocks:width$} {file}");
126        } else {
127            println!("{sum:0width$} {blocks:width$}");
128        }
129    }
130    Ok(())
131}
132
133pub fn uu_app() -> Command {
134    Command::new(uucore::util_name())
135        .version(crate_version!())
136        .override_usage(format_usage(USAGE))
137        .about(ABOUT)
138        .infer_long_args(true)
139        .arg(
140            Arg::new(options::FILE)
141                .action(ArgAction::Append)
142                .hide(true)
143                .value_hint(clap::ValueHint::FilePath),
144        )
145        .arg(
146            Arg::new(options::BSD_COMPATIBLE)
147                .short('r')
148                .help("use the BSD sum algorithm, use 1K blocks (default)")
149                .action(ArgAction::SetTrue),
150        )
151        .arg(
152            Arg::new(options::SYSTEM_V_COMPATIBLE)
153                .short('s')
154                .long(options::SYSTEM_V_COMPATIBLE)
155                .help("use System V sum algorithm, use 512 bytes blocks")
156                .action(ArgAction::SetTrue),
157        )
158}