extern crate chrono;
use std::fs;
use std::fs::{File};
use std::fs::Metadata;
use std::collections::HashMap;
use std::io::Error;
use std::os::unix::fs::PermissionsExt;
use std::{mem, ptr};
use std::fmt::{Display,Formatter};
use chrono::offset::Utc;
use chrono::DateTime;
use std::os::unix::fs::MetadataExt;
use users::{Users,Groups,UsersCache};
static PERMS : [char;5] = ['d', '-', 'r', 'w', 'x'];
struct FileStats {
is_dir : bool,
permissions : String,
user : String,
group : String,
timestamp : String,
size : u64,
}
impl Display for FileStats {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write!(f, "{}\t{}\t{}\t{}\t{}", &self.permissions, &self.user, &self.group, bytes_to_string(self.size), &self.timestamp)
}
}
impl FileStats {
fn new(is_dir : bool, permissions : String, user : String, group: String, timestamp : String, size : u64) -> FileStats {
FileStats {
is_dir, permissions, user, group, timestamp, size
}
}
}
fn bytes_to_string(size: u64) -> String {
let mut size_str = String::new();
if size >= 1099511627776 {
size_str.push_str(&bytes_to_string_with_factor(size, 1099511627776));
size_str.push_str(" TB");
} else if size >= 1073741824 {
size_str.push_str(&bytes_to_string_with_factor(size, 1073741824));
size_str.push_str(" GB");
} else if size >= 1048576 {
size_str.push_str(&bytes_to_string_with_factor(size, 1048576));
size_str.push_str(" MB");
} else if size >= 1024 {
size_str.push_str(&bytes_to_string_with_factor(size, 1024));
size_str.push_str(" KB");
} else {
size_str.push_str(&format!("{}", size));
}
if size_str.len() <= 7 {
size_str.push('\t');
}
size_str
}
fn bytes_to_string_with_factor(size: u64, factor: u64) -> String {
format!("{:.2}", (size as f64 / factor as f64))
}
fn main() {
let args : Vec<String> = std::env::args().collect();
let default_file = String::from(".");
let path = args.get(1).unwrap_or_else(|| {
&default_file
});
let mut map : HashMap<String, FileStats> = HashMap::new();
let cache = UsersCache::new();
read_file(&mut map, path, &cache, false);
print_results(&map);
}
fn read_dir(map : &mut HashMap<String, FileStats>, path : &str, cache: &UsersCache) {
for p in fs::read_dir(path).expect(&format!("Failed to read path '{}'", path)) {
let sub_path = p.expect("Failed to read file").path().display().to_string();
read_file(map, &sub_path, cache, true);
}
}
fn read_file(map : &mut HashMap<String, FileStats>, path : &str, cache : &UsersCache, no_recursion : bool) {
let result = File::open(path);
match result {
Ok(file) => {
let meta = file.metadata().expect(&format!("Failed to read '{}' metadata", path));
let stats = create_file_stats(&meta, cache);
let stats = stats.expect(&format!("Failed to create file stats for '{}'", path));
let mut relative_path = String::new();
if !path.starts_with(".") && !path.starts_with("/") {
relative_path.push_str("./");
};
let is_dir = meta.is_dir();
relative_path.push_str(path);
if is_dir && !path.ends_with("/") {
relative_path.push('/');
}
map.insert(relative_path, stats);
if is_dir && !no_recursion {
read_dir(map, path, cache);
}
},
Err(_) => println!("Failed to stat '{}'", path),
}
}
fn print_results(map: &HashMap<String, FileStats>) {
let mut keys : Vec<String> = Vec::new();
for key in map.keys() {
keys.push(key.to_string());
}
keys.sort_unstable();
for path in keys.iter() {
let file_stats = map.get(path).unwrap();
if file_stats.is_dir {
println!("{}\t\x1B[36m{}\x1B[0m", file_stats, path);
} else {
println!("{}\t{}", file_stats, path);
}
}
println!("{} files/directories", keys.len());
}
fn create_file_stats(meta : &Metadata, cache: &UsersCache) -> Result<FileStats, Error> {
let is_dir = meta.is_dir();
let size = meta.len();
let created = meta.created().expect("Failed to reads files creation date");
let created: DateTime<Utc> = created.into();
let created = format!("{}", created.format("%d/%m/%Y %T"));
let permissions = meta.permissions();
let mode = format!("{:#o}", permissions.mode());
let mode = &mode[mode.len()-3..];
let permissions = build_permissions(is_dir, &mode);
let user = cache.get_user_by_uid(meta.uid()).unwrap();
let group = cache.get_group_by_gid(meta.gid()).unwrap();
let user = user.name().to_os_string().into_string().unwrap();
let group = group.name().to_os_string().into_string().unwrap();
Ok(FileStats::new(
is_dir,
permissions,
user,
group,
created,
size)
)
}
fn build_permissions(is_dir : bool, mode : &str) -> String {
let groups : Vec<char> = mode.chars().collect();
let mut permissions : [char; 10] = unsafe {
let mut array : [char; 10] = mem::uninitialized();
for (_i,e) in array.iter_mut().enumerate() {
ptr::write(e, '-');
}
array
};
match is_dir {
true => permissions[0] = PERMS[0],
_ => permissions[0] = PERMS[1],
};
let mut i : usize = 1;
let mut j : usize = 0;
while i < 8 {
build_permission_group(&mut permissions, groups.get(j).unwrap(), i);
i = i+3;
j = j+1;
}
let result : String = permissions.iter().collect();
result
}
fn build_permission_group(permissions : &mut [char], mode : &char, index : usize) {
if index > 9 {
return;
}
if *mode == '0' || *mode == '1' || *mode == '2' {
permissions[index] = '-';
} else {
permissions[index] = 'r';
}
if *mode == '0' || *mode == '1' || *mode == '4' || *mode == '5' {
permissions[index+1] = '-';
} else {
permissions[index+1] = 'w';
}
if *mode == '0' || *mode == '2' || *mode == '4' || *mode == '6' {
permissions[index+2] = '-';
} else {
permissions[index+2] = 'x';
}
}
ZXh0ZXJuIGNyYXRlIGNocm9ubzsKCnVzZSBzdGQ6OmZzOwp1c2Ugc3RkOjpmczo6e0ZpbGV9Owp1c2Ugc3RkOjpmczo6TWV0YWRhdGE7CnVzZSBzdGQ6OmNvbGxlY3Rpb25zOjpIYXNoTWFwOwp1c2Ugc3RkOjppbzo6RXJyb3I7CnVzZSBzdGQ6Om9zOjp1bml4Ojpmczo6UGVybWlzc2lvbnNFeHQ7CnVzZSBzdGQ6OnttZW0sIHB0cn07CnVzZSBzdGQ6OmZtdDo6e0Rpc3BsYXksRm9ybWF0dGVyfTsKdXNlIGNocm9ubzo6b2Zmc2V0OjpVdGM7CnVzZSBjaHJvbm86OkRhdGVUaW1lOwp1c2Ugc3RkOjpvczo6dW5peDo6ZnM6Ok1ldGFkYXRhRXh0Owp1c2UgdXNlcnM6OntVc2VycyxHcm91cHMsVXNlcnNDYWNoZX07CgpzdGF0aWMgUEVSTVMgOiBbY2hhcjs1XSA9IFsnZCcsICctJywgJ3InLCAndycsICd4J107CgpzdHJ1Y3QgRmlsZVN0YXRzIHsKICAgIGlzX2RpciA6IGJvb2wsCiAgICBwZXJtaXNzaW9ucyA6IFN0cmluZywKICAgIHVzZXIgOiBTdHJpbmcsCiAgICBncm91cCA6IFN0cmluZywKICAgIHRpbWVzdGFtcCA6IFN0cmluZywKICAgIHNpemUgOiB1NjQsCn0KCmltcGwgRGlzcGxheSBmb3IgRmlsZVN0YXRzIHsKICAgIGZuIGZtdCgmc2VsZiwgZjogJm11dCBGb3JtYXR0ZXIpIC0+IHN0ZDo6Zm10OjpSZXN1bHQgewogICAgICAgIHdyaXRlIShmLCAie31cdHt9XHR7fVx0e31cdHt9IiwgJnNlbGYucGVybWlzc2lvbnMsICZzZWxmLnVzZXIsICZzZWxmLmdyb3VwLCBieXRlc190b19zdHJpbmcoc2VsZi5zaXplKSwgJnNlbGYudGltZXN0YW1wKQogICAgfQp9CgppbXBsIEZpbGVTdGF0cyB7CiAgICBmbiBuZXcoaXNfZGlyIDogYm9vbCwgcGVybWlzc2lvbnMgOiBTdHJpbmcsIHVzZXIgOiBTdHJpbmcsIGdyb3VwOiBTdHJpbmcsIHRpbWVzdGFtcCA6IFN0cmluZywgc2l6ZSA6IHU2NCkgLT4gRmlsZVN0YXRzIHsKICAgICAgICBGaWxlU3RhdHMgewogICAgICAgICAgICBpc19kaXIsIHBlcm1pc3Npb25zLCB1c2VyLCBncm91cCwgdGltZXN0YW1wLCBzaXplCiAgICAgICAgfQogICAgfQp9CgpmbiBieXRlc190b19zdHJpbmcoc2l6ZTogdTY0KSAtPiBTdHJpbmcgewogICAgbGV0IG11dCBzaXplX3N0ciA9IFN0cmluZzo6bmV3KCk7CiAgICBpZiBzaXplID49IDEwOTk1MTE2Mjc3NzYgewogICAgICAgIHNpemVfc3RyLnB1c2hfc3RyKCZieXRlc190b19zdHJpbmdfd2l0aF9mYWN0b3Ioc2l6ZSwgMTA5OTUxMTYyNzc3NikpOwogICAgICAgIHNpemVfc3RyLnB1c2hfc3RyKCIgVEIiKTsKICAgIH0gZWxzZSBpZiBzaXplID49IDEwNzM3NDE4MjQgewogICAgICAgIHNpemVfc3RyLnB1c2hfc3RyKCZieXRlc190b19zdHJpbmdfd2l0aF9mYWN0b3Ioc2l6ZSwgMTA3Mzc0MTgyNCkpOwogICAgICAgIHNpemVfc3RyLnB1c2hfc3RyKCIgR0IiKTsKICAgIH0gZWxzZSBpZiBzaXplID49IDEwNDg1NzYgewogICAgICAgIHNpemVfc3RyLnB1c2hfc3RyKCZieXRlc190b19zdHJpbmdfd2l0aF9mYWN0b3Ioc2l6ZSwgMTA0ODU3NikpOwogICAgICAgIHNpemVfc3RyLnB1c2hfc3RyKCIgTUIiKTsKICAgIH0gZWxzZSBpZiBzaXplID49IDEwMjQgewogICAgICAgIHNpemVfc3RyLnB1c2hfc3RyKCZieXRlc190b19zdHJpbmdfd2l0aF9mYWN0b3Ioc2l6ZSwgMTAyNCkpOwogICAgICAgIHNpemVfc3RyLnB1c2hfc3RyKCIgS0IiKTsKICAgIH0gZWxzZSB7CiAgICAgICAgc2l6ZV9zdHIucHVzaF9zdHIoJmZvcm1hdCEoInt9Iiwgc2l6ZSkpOwogICAgfQogICAgaWYgc2l6ZV9zdHIubGVuKCkgPD0gNyB7CiAgICAgICAgc2l6ZV9zdHIucHVzaCgnXHQnKTsKICAgIH0KICAgIHNpemVfc3RyCn0KCmZuIGJ5dGVzX3RvX3N0cmluZ193aXRoX2ZhY3RvcihzaXplOiB1NjQsIGZhY3RvcjogdTY0KSAtPiBTdHJpbmcgewogICAgZm9ybWF0ISgiezouMn0iLCAoc2l6ZSBhcyBmNjQgLyBmYWN0b3IgYXMgZjY0KSkKfQoKZm4gbWFpbigpIHsKICAgIGxldCBhcmdzIDogVmVjPFN0cmluZz4gPSBzdGQ6OmVudjo6YXJncygpLmNvbGxlY3QoKTsKICAgIGxldCBkZWZhdWx0X2ZpbGUgPSBTdHJpbmc6OmZyb20oIi4iKTsKICAgIGxldCBwYXRoID0gYXJncy5nZXQoMSkudW53cmFwX29yX2Vsc2UofHwgewogICAgICAgICZkZWZhdWx0X2ZpbGUKICAgIH0pOwogICAgbGV0IG11dCBtYXAgOiBIYXNoTWFwPFN0cmluZywgRmlsZVN0YXRzPiA9IEhhc2hNYXA6Om5ldygpOwogICAgbGV0IGNhY2hlID0gVXNlcnNDYWNoZTo6bmV3KCk7CiAgICByZWFkX2ZpbGUoJm11dCBtYXAsIHBhdGgsICZjYWNoZSwgZmFsc2UpOwogICAgcHJpbnRfcmVzdWx0cygmbWFwKTsKfQoKZm4gcmVhZF9kaXIobWFwIDogJm11dCBIYXNoTWFwPFN0cmluZywgRmlsZVN0YXRzPiwgcGF0aCA6ICZzdHIsIGNhY2hlOiAmVXNlcnNDYWNoZSkgewogICAgZm9yIHAgaW4gZnM6OnJlYWRfZGlyKHBhdGgpLmV4cGVjdCgmZm9ybWF0ISgiRmFpbGVkIHRvIHJlYWQgcGF0aCAne30nIiwgcGF0aCkpIHsKICAgICAgICBsZXQgc3ViX3BhdGggPSBwLmV4cGVjdCgiRmFpbGVkIHRvIHJlYWQgZmlsZSIpLnBhdGgoKS5kaXNwbGF5KCkudG9fc3RyaW5nKCk7CiAgICAgICAgcmVhZF9maWxlKG1hcCwgJnN1Yl9wYXRoLCBjYWNoZSwgdHJ1ZSk7CiAgICB9Cn0KCmZuIHJlYWRfZmlsZShtYXAgOiAmbXV0IEhhc2hNYXA8U3RyaW5nLCBGaWxlU3RhdHM+LCBwYXRoIDogJnN0ciwgY2FjaGUgOiAmVXNlcnNDYWNoZSwgbm9fcmVjdXJzaW9uIDogYm9vbCkgewogICAgbGV0IHJlc3VsdCA9IEZpbGU6Om9wZW4ocGF0aCk7CiAgICBtYXRjaCByZXN1bHQgewogICAgICAgIE9rKGZpbGUpID0+IHsKICAgICAgICAgICAgbGV0IG1ldGEgPSBmaWxlLm1ldGFkYXRhKCkuZXhwZWN0KCZmb3JtYXQhKCJGYWlsZWQgdG8gcmVhZCAne30nIG1ldGFkYXRhIiwgcGF0aCkpOwogICAgICAgICAgICBsZXQgc3RhdHMgPSBjcmVhdGVfZmlsZV9zdGF0cygmbWV0YSwgY2FjaGUpOwogICAgICAgICAgICBsZXQgc3RhdHMgPSBzdGF0cy5leHBlY3QoJmZvcm1hdCEoIkZhaWxlZCB0byBjcmVhdGUgZmlsZSBzdGF0cyBmb3IgJ3t9JyIsIHBhdGgpKTsKICAgICAgICAgICAgbGV0IG11dCByZWxhdGl2ZV9wYXRoID0gU3RyaW5nOjpuZXcoKTsKICAgICAgICAgICAgaWYgIXBhdGguc3RhcnRzX3dpdGgoIi4iKSAmJiAhcGF0aC5zdGFydHNfd2l0aCgiLyIpIHsKICAgICAgICAgICAgICAgIHJlbGF0aXZlX3BhdGgucHVzaF9zdHIoIi4vIik7CiAgICAgICAgICAgIH07CiAgICAgICAgICAgIGxldCBpc19kaXIgPSBtZXRhLmlzX2RpcigpOwogICAgICAgICAgICByZWxhdGl2ZV9wYXRoLnB1c2hfc3RyKHBhdGgpOwogICAgICAgICAgICBpZiBpc19kaXIgJiYgIXBhdGguZW5kc193aXRoKCIvIikgewogICAgICAgICAgICAgICAgcmVsYXRpdmVfcGF0aC5wdXNoKCcvJyk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgbWFwLmluc2VydChyZWxhdGl2ZV9wYXRoLCBzdGF0cyk7CiAgICAgICAgICAgIGlmIGlzX2RpciAmJiAhbm9fcmVjdXJzaW9uIHsKICAgICAgICAgICAgICAgIHJlYWRfZGlyKG1hcCwgcGF0aCwgY2FjaGUpOwogICAgICAgICAgICB9IAogICAgICAgIH0sCiAgICAgICAgRXJyKF8pID0+IHByaW50bG4hKCJGYWlsZWQgdG8gc3RhdCAne30nIiwgcGF0aCksCiAgICB9Cn0KCmZuIHByaW50X3Jlc3VsdHMobWFwOiAmSGFzaE1hcDxTdHJpbmcsIEZpbGVTdGF0cz4pIHsKICAgIGxldCBtdXQga2V5cyA6IFZlYzxTdHJpbmc+ID0gVmVjOjpuZXcoKTsKICAgIGZvciBrZXkgaW4gbWFwLmtleXMoKSB7CiAgICAgICAga2V5cy5wdXNoKGtleS50b19zdHJpbmcoKSk7CiAgICB9CiAgICBrZXlzLnNvcnRfdW5zdGFibGUoKTsKICAgIGZvciBwYXRoIGluIGtleXMuaXRlcigpIHsKICAgICAgICBsZXQgZmlsZV9zdGF0cyA9IG1hcC5nZXQocGF0aCkudW53cmFwKCk7CiAgICAgICAgaWYgZmlsZV9zdGF0cy5pc19kaXIgewogICAgICAgICAgICBwcmludGxuISgie31cdFx4MUJbMzZte31ceDFCWzBtIiwgZmlsZV9zdGF0cywgcGF0aCk7CiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgcHJpbnRsbiEoInt9XHR7fSIsIGZpbGVfc3RhdHMsIHBhdGgpOwogICAgICAgIH0KICAgIH0KICAgIHByaW50bG4hKCJ7fSBmaWxlcy9kaXJlY3RvcmllcyIsIGtleXMubGVuKCkpOwp9CgpmbiBjcmVhdGVfZmlsZV9zdGF0cyhtZXRhIDogJk1ldGFkYXRhLCBjYWNoZTogJlVzZXJzQ2FjaGUpIC0+IFJlc3VsdDxGaWxlU3RhdHMsIEVycm9yPiB7CiAgICBsZXQgaXNfZGlyID0gbWV0YS5pc19kaXIoKTsKICAgIGxldCBzaXplID0gbWV0YS5sZW4oKTsKICAgIGxldCBjcmVhdGVkID0gbWV0YS5jcmVhdGVkKCkuZXhwZWN0KCJGYWlsZWQgdG8gcmVhZHMgZmlsZXMgY3JlYXRpb24gZGF0ZSIpOwogICAgbGV0IGNyZWF0ZWQ6IERhdGVUaW1lPFV0Yz4gPSBjcmVhdGVkLmludG8oKTsKICAgIGxldCBjcmVhdGVkID0gZm9ybWF0ISgie30iLCBjcmVhdGVkLmZvcm1hdCgiJWQvJW0vJVkgJVQiKSk7CiAgICBsZXQgcGVybWlzc2lvbnMgPSBtZXRhLnBlcm1pc3Npb25zKCk7CiAgICBsZXQgbW9kZSA9IGZvcm1hdCEoIns6I299IiwgcGVybWlzc2lvbnMubW9kZSgpKTsKICAgIGxldCBtb2RlID0gJm1vZGVbbW9kZS5sZW4oKS0zLi5dOwogICAgbGV0IHBlcm1pc3Npb25zID0gYnVpbGRfcGVybWlzc2lvbnMoaXNfZGlyLCAmbW9kZSk7CiAgICBsZXQgdXNlciA9IGNhY2hlLmdldF91c2VyX2J5X3VpZChtZXRhLnVpZCgpKS51bndyYXAoKTsKICAgIGxldCBncm91cCA9IGNhY2hlLmdldF9ncm91cF9ieV9naWQobWV0YS5naWQoKSkudW53cmFwKCk7CiAgICBsZXQgdXNlciA9IHVzZXIubmFtZSgpLnRvX29zX3N0cmluZygpLmludG9fc3RyaW5nKCkudW53cmFwKCk7CiAgICBsZXQgZ3JvdXAgPSBncm91cC5uYW1lKCkudG9fb3Nfc3RyaW5nKCkuaW50b19zdHJpbmcoKS51bndyYXAoKTsKCiAgICBPayhGaWxlU3RhdHM6Om5ldygKICAgICAgICAgICAgaXNfZGlyLAogICAgICAgICAgICBwZXJtaXNzaW9ucywKICAgICAgICAgICAgdXNlciwgCiAgICAgICAgICAgIGdyb3VwLAogICAgICAgICAgICBjcmVhdGVkLCAKICAgICAgICAgICAgc2l6ZSkKICAgICAgICApCn0KCmZuIGJ1aWxkX3Blcm1pc3Npb25zKGlzX2RpciA6IGJvb2wsIG1vZGUgOiAmc3RyKSAtPiBTdHJpbmcgewogICAgbGV0IGdyb3VwcyA6IFZlYzxjaGFyPiA9IG1vZGUuY2hhcnMoKS5jb2xsZWN0KCk7CiAgICBsZXQgbXV0IHBlcm1pc3Npb25zIDogW2NoYXI7IDEwXSA9IHVuc2FmZSB7CiAgICAgICAgbGV0IG11dCBhcnJheSA6IFtjaGFyOyAxMF0gPSBtZW06OnVuaW5pdGlhbGl6ZWQoKTsKICAgICAgICBmb3IgKF9pLGUpIGluIGFycmF5Lml0ZXJfbXV0KCkuZW51bWVyYXRlKCkgewogICAgICAgICAgICBwdHI6OndyaXRlKGUsICctJyk7CiAgICAgICAgfQogICAgICAgIGFycmF5CiAgICB9OwogICAgbWF0Y2ggaXNfZGlyIHsKICAgICAgICB0cnVlID0+IHBlcm1pc3Npb25zWzBdID0gUEVSTVNbMF0sCiAgICAgICAgXyA9PiBwZXJtaXNzaW9uc1swXSA9IFBFUk1TWzFdLAogICAgfTsKICAgIGxldCBtdXQgaSA6IHVzaXplID0gMTsKICAgIGxldCBtdXQgaiA6IHVzaXplID0gMDsKICAgIHdoaWxlIGkgPCA4IHsKICAgICAgICBidWlsZF9wZXJtaXNzaW9uX2dyb3VwKCZtdXQgcGVybWlzc2lvbnMsIGdyb3Vwcy5nZXQoaikudW53cmFwKCksIGkpOwogICAgICAgIGkgPSBpKzM7CiAgICAgICAgaiA9IGorMTsKICAgIH0KICAgIGxldCByZXN1bHQgOiBTdHJpbmcgPSBwZXJtaXNzaW9ucy5pdGVyKCkuY29sbGVjdCgpOwogICAgcmVzdWx0Cn0KCmZuIGJ1aWxkX3Blcm1pc3Npb25fZ3JvdXAocGVybWlzc2lvbnMgOiAmbXV0IFtjaGFyXSwgbW9kZSA6ICZjaGFyLCBpbmRleCA6IHVzaXplKSB7CiAgICBpZiBpbmRleCA+IDkgewogICAgICAgIHJldHVybjsKICAgIH0KICAgIGlmICptb2RlID09ICcwJyB8fCAqbW9kZSA9PSAnMScgfHwgKm1vZGUgPT0gJzInIHsKICAgICAgICBwZXJtaXNzaW9uc1tpbmRleF0gPSAnLSc7CiAgICB9IGVsc2UgewogICAgICAgIHBlcm1pc3Npb25zW2luZGV4XSA9ICdyJzsKICAgIH0KICAgIGlmICptb2RlID09ICcwJyB8fCAqbW9kZSA9PSAnMScgfHwgKm1vZGUgPT0gJzQnIHx8ICptb2RlID09ICc1JyB7CiAgICAgICAgcGVybWlzc2lvbnNbaW5kZXgrMV0gPSAnLSc7CiAgICB9IGVsc2UgewogICAgICAgIHBlcm1pc3Npb25zW2luZGV4KzFdID0gJ3cnOwogICAgfQogICAgaWYgKm1vZGUgPT0gJzAnIHx8ICptb2RlID09ICcyJyB8fCAqbW9kZSA9PSAnNCcgfHwgKm1vZGUgPT0gJzYnIHsKICAgICAgICBwZXJtaXNzaW9uc1tpbmRleCsyXSA9ICctJzsKICAgIH0gZWxzZSB7CiAgICAgICAgcGVybWlzc2lvbnNbaW5kZXgrMl0gPSAneCc7CiAgICB9Cn0=