1 // This is a part of rust-encoding.
2 // Copyright (c) 2014-2015, Kang Seonghoon.
3 // See README.md and LICENSE.txt for details.
4 
5 extern crate encoding;
6 extern crate getopts;
7 
8 use std::{io, env};
9 use std::io::{Read, Write};
10 use std::fs::File;
11 use std::path::Path;
12 use encoding::{EncoderTrap, DecoderTrap};
13 use encoding::label::encoding_from_whatwg_label;
14 use getopts::Options;
15 
main()16 fn main() {
17     let args: Vec<_> = env::args().collect();
18 
19     let mut opts = Options::new();
20     opts.optopt("f", "from-code", "set input encoding", "NAME");
21     opts.optopt("t", "to-code", "set output encoding", "NAME");
22     opts.optopt("e", "error-policy",
23                 "set error policy (one of strict, ignore, replace, ncr-escape)", "POLICY");
24     opts.optflag("c", "", "same as `--error-policy=ignore`");
25     opts.optopt("o", "output", "output file", "FILE");
26     opts.optflag("h", "help", "print this help menu");
27 
28     let matches = match opts.parse(&args[1..]) {
29         Ok(m) => m,
30         Err(e) => panic!(e.to_string()),
31     };
32     if matches.opt_present("h") {
33         println!("{}", opts.usage("Converts the character encoding using rust-encoding."));
34         return;
35     }
36 
37     let inencname = matches.opt_str("f");
38     let outencname = matches.opt_str("t");
39     let inenc = match inencname.as_ref().map(|s| &s[..]) {
40         Some(name) => match encoding_from_whatwg_label(name) {
41             Some(enc) => enc,
42             None => panic!("invalid input encoding name {}", name),
43         },
44         None => encoding::all::UTF_8 as encoding::EncodingRef,
45     };
46     let outenc = match outencname.as_ref().map(|s| &s[..]) {
47         Some(name) => match encoding_from_whatwg_label(name) {
48             Some(enc) => enc,
49             None => panic!("invalid output encoding name {}", name),
50         },
51         None => encoding::all::UTF_8 as encoding::EncodingRef,
52     };
53 
54     let mut policy = matches.opt_str("e");
55     if matches.opt_present("c") {
56         policy = Some("ignore".to_string());
57     }
58     let (intrap, outtrap) = match policy.as_ref().map(|s| &s[..]) {
59         Some("strict") | None => (DecoderTrap::Strict, EncoderTrap::Strict),
60         Some("ignore") => (DecoderTrap::Ignore, EncoderTrap::Ignore),
61         Some("replace") => (DecoderTrap::Replace, EncoderTrap::Replace),
62         Some("ncr-escape") => (DecoderTrap::Replace, EncoderTrap::NcrEscape),
63         Some(s) => panic!("invalid error policy {}", s),
64     };
65 
66     let mut input = match matches.free.first().map(|s| &s[..]) {
67         Some("-") | None => Box::new(io::stdin()) as Box<Read>,
68         Some(f) => match File::open(&Path::new(f)) {
69             Ok(f) => Box::new(f) as Box<Read>,
70             Err(e) => panic!("cannot open the input {}: {}", f, e),
71         },
72     };
73     let mut output = match matches.opt_str("o").as_ref().map(|s| &s[..]) {
74         Some("-") | None => Box::new(io::stdout()) as Box<Write>,
75         Some(f) => match File::create(&Path::new(f)) {
76             Ok(f) => Box::new(f) as Box<Write>,
77             Err(e) => panic!("cannot open the output {}: {}", f, e),
78         },
79     };
80 
81     // XXX should really use the incremental interface
82     let mut ret = Vec::new();
83     input.read_to_end(&mut ret).ok().expect("cannot read from the input");
84     let decoded = match inenc.decode(&ret, intrap) {
85         Ok(s) => s,
86         Err(e) => panic!("decoder error: {}", e),
87     };
88     let encoded = match outenc.encode(&decoded, outtrap) {
89         Ok(s) => s,
90         Err(e) => panic!("encoder error: {}", e),
91     };
92     output.write_all(&encoded).unwrap();
93 }
94 
95