Skip to content

Commit 6729f9e

Browse files
authored
Merge pull request #422 from rustcoreutils/mailx
Merge Mailx
2 parents 5ef76c4 + 4db2fb6 commit 6729f9e

File tree

21 files changed

+9925
-4
lines changed

21 files changed

+9925
-4
lines changed

Cargo.lock

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ members = [
1616
"m4",
1717
"m4/test-manager",
1818
"gettext-rs",
19+
"mailx",
1920
"misc",
2021
"pathnames",
2122
"pax",

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ Because it is a FAQ, the major differences between this project and uutils are:
8585
- [x] link
8686
- [x] ls
8787
- [x] m4
88+
- [x] mailx
8889
- [x] make
8990
- [x] mkdir
9091
- [x] more
@@ -215,7 +216,6 @@ Because it is a FAQ, the major differences between this project and uutils are:
215216

216217
### Misc. category
217218
- [ ] lp
218-
- [ ] mailx
219219
- [ ] patch
220220

221221
## Installation

mailx/Cargo.toml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[package]
2+
name = "posixutils-mailx"
3+
version = "0.2.2"
4+
authors = ["Jeff Garzik"]
5+
repository.workspace = true
6+
license.workspace = true
7+
edition.workspace = true
8+
rust-version.workspace = true
9+
10+
[dependencies]
11+
chrono.workspace = true
12+
dirs = "5"
13+
plib = { path = "../plib" }
14+
15+
[dev-dependencies]
16+
tempfile = "3"
17+
filetime = "0.2"
18+
19+
[[bin]]
20+
name = "mailx"
21+
path = "./main.rs"
22+

mailx/args.rs

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
//! Command line argument parsing for mailx
2+
3+
#[derive(Debug, Clone)]
4+
pub enum Mode {
5+
Send,
6+
CheckMail,
7+
HeadersOnly,
8+
Receive,
9+
}
10+
11+
#[derive(Debug, Clone)]
12+
pub struct Args {
13+
pub mode: Mode,
14+
pub subject: Option<String>,
15+
pub addresses: Vec<String>,
16+
pub file: Option<String>,
17+
pub read_mbox: bool,
18+
pub record_to_recipient: bool,
19+
pub no_header_summary: bool,
20+
pub ignore_interrupts: bool,
21+
pub no_init: bool,
22+
pub user: Option<String>,
23+
}
24+
25+
impl Args {
26+
pub fn parse(args: Vec<String>) -> Result<Self, String> {
27+
let mut result = Args {
28+
mode: Mode::Receive,
29+
subject: None,
30+
addresses: Vec::new(),
31+
file: None,
32+
read_mbox: false,
33+
record_to_recipient: false,
34+
no_header_summary: false,
35+
ignore_interrupts: false,
36+
no_init: false,
37+
user: None,
38+
};
39+
40+
let mut i = 0;
41+
let mut check_mail = false;
42+
let mut headers_only = false;
43+
44+
while i < args.len() {
45+
let arg = &args[i];
46+
47+
if arg.starts_with('-') && arg.len() > 1 {
48+
let mut chars = arg[1..].chars().peekable();
49+
50+
while let Some(c) = chars.next() {
51+
match c {
52+
'e' => {
53+
check_mail = true;
54+
}
55+
'f' => {
56+
result.read_mbox = true;
57+
// Check if next argument is a file (not starting with -)
58+
if chars.peek().is_none() && i + 1 < args.len() {
59+
let next = &args[i + 1];
60+
if !next.starts_with('-') {
61+
result.file = Some(next.clone());
62+
i += 1;
63+
}
64+
}
65+
}
66+
'F' => {
67+
result.record_to_recipient = true;
68+
}
69+
'H' => {
70+
headers_only = true;
71+
}
72+
'i' => {
73+
result.ignore_interrupts = true;
74+
}
75+
'n' => {
76+
result.no_init = true;
77+
}
78+
'N' => {
79+
result.no_header_summary = true;
80+
}
81+
's' => {
82+
// Subject - get the value
83+
let rest: String = chars.collect();
84+
if !rest.is_empty() {
85+
result.subject = Some(rest);
86+
} else if i + 1 < args.len() {
87+
i += 1;
88+
result.subject = Some(args[i].clone());
89+
} else {
90+
return Err("option requires an argument -- s".to_string());
91+
}
92+
break;
93+
}
94+
'u' => {
95+
// User mailbox
96+
let rest: String = chars.collect();
97+
if !rest.is_empty() {
98+
result.user = Some(rest);
99+
} else if i + 1 < args.len() {
100+
i += 1;
101+
result.user = Some(args[i].clone());
102+
} else {
103+
return Err("option requires an argument -- u".to_string());
104+
}
105+
break;
106+
}
107+
_ => {
108+
return Err(format!("illegal option -- {}", c));
109+
}
110+
}
111+
}
112+
} else {
113+
// Non-option argument - must be an address
114+
result.addresses.push(arg.clone());
115+
}
116+
117+
i += 1;
118+
}
119+
120+
// Determine mode
121+
if check_mail {
122+
result.mode = Mode::CheckMail;
123+
} else if headers_only {
124+
result.mode = Mode::HeadersOnly;
125+
} else if !result.addresses.is_empty() {
126+
result.mode = Mode::Send;
127+
} else {
128+
result.mode = Mode::Receive;
129+
}
130+
131+
// If -u was specified, set up to read that user's mailbox
132+
if let Some(ref user) = result.user {
133+
result.file = Some(format!("/var/mail/{}", user));
134+
}
135+
136+
Ok(result)
137+
}
138+
}

0 commit comments

Comments
 (0)