// This is a part of encoding-next. // Copyright (c) 2014-2015, Kang Seonghoon. // See README.md and LICENSE.txt for details. use encoding::label::encoding_from_whatwg_label; use encoding::{DecoderTrap, EncoderTrap}; use getopts::Options; use std::fs::File; use std::io::{Read, Write}; use std::path::Path; use std::{env, io}; fn main() { let args: Vec<_> = env::args().collect(); let mut opts = Options::new(); opts.optopt("f", "from-code", "set input encoding", "NAME"); opts.optopt("t", "to-code", "set output encoding", "NAME"); opts.optopt( "e", "error-policy", "set error policy (one of strict, ignore, replace, ncr-escape)", "POLICY", ); opts.optflag("c", "", "same as `--error-policy=ignore`"); opts.optopt("o", "output", "output file", "FILE"); opts.optflag("h", "help", "print this help menu"); let matches = match opts.parse(&args[1..]) { Ok(m) => m, Err(e) => panic!("{}", e.to_string()), }; if matches.opt_present("h") { println!( "{}", opts.usage("Converts the character encoding using encoding-next.") ); return; } let inencname = matches.opt_str("f"); let outencname = matches.opt_str("t"); let inenc = match inencname.as_ref().map(|s| &s[..]) { Some(name) => match encoding_from_whatwg_label(name) { Some(enc) => enc, None => panic!("invalid input encoding name {}", name), }, None => encoding::all::UTF_8 as encoding::EncodingRef, }; let outenc = match outencname.as_ref().map(|s| &s[..]) { Some(name) => match encoding_from_whatwg_label(name) { Some(enc) => enc, None => panic!("invalid output encoding name {}", name), }, None => encoding::all::UTF_8 as encoding::EncodingRef, }; let mut policy = matches.opt_str("e"); if matches.opt_present("c") { policy = Some("ignore".to_string()); } let (intrap, outtrap) = match policy.as_ref().map(|s| &s[..]) { Some("strict") | None => (DecoderTrap::Strict, EncoderTrap::Strict), Some("ignore") => (DecoderTrap::Ignore, EncoderTrap::Ignore), Some("replace") => (DecoderTrap::Replace, EncoderTrap::Replace), Some("ncr-escape") => (DecoderTrap::Replace, EncoderTrap::NcrEscape), Some(s) => panic!("invalid error policy {}", s), }; let mut input = match matches.free.first().map(|s| &s[..]) { Some("-") | None => Box::new(io::stdin()) as Box, Some(f) => match File::open(&Path::new(f)) { Ok(f) => Box::new(f) as Box, Err(e) => panic!("cannot open the input {}: {}", f, e), }, }; let mut output = match matches.opt_str("o").as_ref().map(|s| &s[..]) { Some("-") | None => Box::new(io::stdout()) as Box, Some(f) => match File::create(&Path::new(f)) { Ok(f) => Box::new(f) as Box, Err(e) => panic!("cannot open the output {}: {}", f, e), }, }; // XXX should really use the incremental interface let mut ret = Vec::new(); input .read_to_end(&mut ret) .expect("cannot read from the input"); let decoded = match inenc.decode(&ret, intrap) { Ok(s) => s, Err(e) => panic!("decoder error: {}", e), }; let encoded = match outenc.encode(&decoded, outtrap) { Ok(s) => s, Err(e) => panic!("encoder error: {}", e), }; output.write_all(&encoded).unwrap(); }