1use 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 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 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 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}