From d3e9a6788bf8fd5705de4c9c30091890487dd9a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hozda?= Date: Tue, 18 Aug 2020 16:37:05 +0200 Subject: [PATCH] remove global state, objectify, more de-C-ification --- Cargo.lock | 7 - Cargo.toml | 3 +- src/main.rs | 2116 +++++++++++++++++++++++++++------------------------ 3 files changed, 1107 insertions(+), 1019 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2782d06..c79e82a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,12 +17,6 @@ dependencies = [ "wasi", ] -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" version = "0.2.74" @@ -95,7 +89,6 @@ dependencies = [ name = "rgp" version = "0.1.0" dependencies = [ - "lazy_static", "libc", "simple_input", "tempfile", diff --git a/Cargo.toml b/Cargo.toml index faf0a48..68f63a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,5 @@ edition = "2018" [dependencies] libc = "*" -lazy_static = "1.4.0" -simple_input = "0.4.0" tempfile = "3.1.0" +simple_input = "0.4.0" diff --git a/src/main.rs b/src/main.rs index d078a67..32cc095 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,7 +13,6 @@ extern crate libc; extern crate tempfile; extern crate simple_input; -#[macro_use] extern crate lazy_static; use std::env; use std::path::Path; @@ -23,7 +22,6 @@ use std::time::Duration; use std::ffi::{CStr, CString}; use std::fs::{self, File}; use std::mem; -use std::ptr; use std::io::{Write}; use simple_input::input; @@ -239,248 +237,1110 @@ pub struct Config { pub verbose: String, } -pub static mut tmpfilename: String = String::new(); -pub static mut links: Option> = None; -pub static mut history: Option> = None; -pub static mut link_key: usize = 0; -pub static mut current_host: [libc::c_char; 512] = [0; 512]; -pub static mut current_port: usize = 0; -pub static mut current_selector: String = String::new(); -pub static mut parsed_host: String = String::new(); -pub static mut parsed_port: usize = 0; -pub static mut parsed_selector: String = String::new(); -pub static mut bookmarks: [[u8; 512]; 20] = [[0; 512]; 20]; - -lazy_static! { - pub static ref config: Config = Config { - start_uri: String::from("gopher://gopher.floodgap.com:70"), - cmd_text: String::from("less"), - cmd_image: String::from("display"), - cmd_browser: String::from("firefox"), - cmd_player: String::from("mplayer"), - color_prompt: String::from("1;34"), - color_selector: String::from("1;32"), - verbose: String::from("true"), - }; +#[derive(Clone, Debug)] +pub struct BrowserState { + pub tmpfilename: String, + pub links: Option>, + pub history: Option>, + pub link_key: usize, + pub current_host: [libc::c_char; 512], + pub current_port: usize, + pub current_selector: String, + pub parsed_host: String, + pub parsed_port: usize, + pub parsed_selector: String, + pub bookmarks: [[u8; 512]; 20], + pub config: Config, } -/* implementation */ -pub fn usage() { - eprintln!("usage: cgo [-v] [-H] [gopher URI]"); - exit(0); -} -#[no_mangle] -pub fn banner(to_error: bool) { - if to_error { - eprintln!("cgo 0.6.1 Copyright (c) 2020 Sebastian Steinhauer"); - } else { - println!("cgo 0.6.1 Copyright (c) 2020 Sebastian Steinhauer"); - } -} -#[no_mangle] -pub unsafe extern "C" fn check_option_true( - mut option: *const libc::c_char, -) -> libc::c_int { - return (strcasecmp(option, b"false\x00" as *const u8 as *const libc::c_char) != 0 - && strcasecmp(option, b"off\x00" as *const u8 as *const libc::c_char) != 0) - as libc::c_int; -} -#[no_mangle] -pub unsafe extern "C" fn parse_config_line(mut line: *const libc::c_char) { - let mut token: [libc::c_char; 1024] = [0; 1024]; - let mut bkey: [libc::c_char; 128] = [0; 128]; - let mut value: *mut libc::c_char = 0 as *mut libc::c_char; - let mut i: libc::c_int = 0; - let mut j: libc::c_int = 0; - while *line as libc::c_int == ' ' as i32 || *line as libc::c_int == '\t' as i32 { - line = line.offset(1) +impl BrowserState { + pub fn new() -> Self { + Self { + tmpfilename: String::new(), + links: None, + history: None, + link_key: 0, + current_host: [0; 512], + current_port: 0, + current_selector: String::new(), + parsed_host: String::new(), + parsed_port: 0, + parsed_selector: String::new(), + bookmarks: [[0; 512]; 20], + config: Config { + start_uri: String::from("gopher://gopher.floodgap.com:70"), + cmd_text: String::from("less"), + cmd_image: String::from("display"), + cmd_browser: String::from("firefox"), + cmd_player: String::from("mplayer"), + color_prompt: String::from("1;34"), + color_selector: String::from("1;32"), + verbose: String::from("true"), + }, + } } - i = 0 as libc::c_int; - while *line as libc::c_int != 0 - && *line as libc::c_int != ' ' as i32 - && *line as libc::c_int != '\t' as i32 - { - if (i as libc::c_ulong) - < (mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong) - .wrapping_sub(1 as libc::c_int as libc::c_ulong) + + unsafe fn parse_config_line(&mut self, mut line: *const libc::c_char) { + let mut token: [libc::c_char; 1024] = [0; 1024]; + let mut bkey: [libc::c_char; 128] = [0; 128]; + let mut value: *mut libc::c_char = 0 as *mut libc::c_char; + let mut i: libc::c_int = 0; + let mut j: libc::c_int = 0; + while *line as libc::c_int == ' ' as i32 || *line as libc::c_int == '\t' as i32 { + line = line.offset(1) + } + i = 0 as libc::c_int; + while *line as libc::c_int != 0 + && *line as libc::c_int != ' ' as i32 + && *line as libc::c_int != '\t' as i32 + { + if (i as libc::c_ulong) + < (mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong) + .wrapping_sub(1 as libc::c_int as libc::c_ulong) + { + let fresh0 = i; + i = i + 1; + token[fresh0 as usize] = *line + } + line = line.offset(1) + } + token[i as usize] = 0 as libc::c_int as libc::c_char; + if strcmp( + token.as_mut_ptr(), + b"start_uri\x00" as *const u8 as *const libc::c_char, + ) == 0 + { + value = CString::new(self.config.start_uri.clone()).unwrap().into_raw(); + } else if strcmp( + token.as_mut_ptr(), + b"cmd_text\x00" as *const u8 as *const libc::c_char, + ) == 0 + { + value = CString::new(self.config.cmd_text.clone()).unwrap().into_raw(); + } else if strcmp( + token.as_mut_ptr(), + b"cmd_browser\x00" as *const u8 as *const libc::c_char, + ) == 0 + { + value = CString::new(self.config.cmd_browser.clone()).unwrap().into_raw(); + } else if strcmp( + token.as_mut_ptr(), + b"cmd_image\x00" as *const u8 as *const libc::c_char, + ) == 0 + { + value = CString::new(self.config.cmd_image.clone()).unwrap().into_raw(); + } else if strcmp( + token.as_mut_ptr(), + b"cmd_player\x00" as *const u8 as *const libc::c_char, + ) == 0 + { + value = CString::new(self.config.cmd_player.clone()).unwrap().into_raw(); + } else if strcmp( + token.as_mut_ptr(), + b"color_prompt\x00" as *const u8 as *const libc::c_char, + ) == 0 + { + value = CString::new(self.config.color_prompt.clone()).unwrap().into_raw(); + } else if strcmp( + token.as_mut_ptr(), + b"color_selector\x00" as *const u8 as *const libc::c_char, + ) == 0 + { + value = CString::new(self.config.color_selector.clone()).unwrap().into_raw(); + } else if strcmp( + token.as_mut_ptr(), + b"verbose\x00" as *const u8 as *const libc::c_char, + ) == 0 { - let fresh0 = i; - i = i + 1; - token[fresh0 as usize] = *line + value = CString::new(self.config.verbose.clone()).unwrap().into_raw(); + } else { + j = 0 as libc::c_int; + while j < 20 as libc::c_int { + snprintf( + bkey.as_mut_ptr(), + mem::size_of::<[libc::c_char; 128]>() as libc::c_ulong, + b"bookmark%d\x00" as *const u8 as *const libc::c_char, + j + 1 as libc::c_int, + ); + if strcmp(token.as_mut_ptr(), bkey.as_mut_ptr()) == 0 { + value = CString::new(self.bookmarks[j as usize]).unwrap().into_raw(); + break; + } else { + j += 1 + } + } + if value.is_null() { + return; + } + } + while *line as libc::c_int == ' ' as i32 || *line as libc::c_int == '\t' as i32 { + line = line.offset(1) + } + i = 0 as libc::c_int; + while *line != 0 { + if i < 512 as libc::c_int - 1 as libc::c_int { + let fresh1 = i; + i = i + 1; + *value.offset(fresh1 as isize) = *line + } + line = line.offset(1) } - line = line.offset(1) + i -= 1; + while i > 0 as libc::c_int + && (*value.offset(i as isize) as libc::c_int == ' ' as i32 + || *value.offset(i as isize) as libc::c_int == '\t' as i32) + { + i -= 1 + } + i += 1; + *value.offset(i as isize) = 0 as libc::c_int as libc::c_char; } - token[i as usize] = 0 as libc::c_int as libc::c_char; - if strcmp(token.as_mut_ptr(), b"start_uri\x00" as *const u8 as *const libc::c_char) - == 0 - { - value = CString::new(config.start_uri.clone()).unwrap().into_raw(); - } else if strcmp( - token.as_mut_ptr(), - b"cmd_text\x00" as *const u8 as *const libc::c_char, - ) == 0 - { - value = CString::new(config.cmd_text.clone()).unwrap().into_raw(); - } else if strcmp( - token.as_mut_ptr(), - b"cmd_browser\x00" as *const u8 as *const libc::c_char, - ) == 0 - { - value = CString::new(config.cmd_browser.clone()).unwrap().into_raw(); - } else if strcmp( - token.as_mut_ptr(), - b"cmd_image\x00" as *const u8 as *const libc::c_char, - ) == 0 - { - value = CString::new(config.cmd_image.clone()).unwrap().into_raw(); - } else if strcmp( - token.as_mut_ptr(), - b"cmd_player\x00" as *const u8 as *const libc::c_char, - ) == 0 - { - value = CString::new(config.cmd_player.clone()).unwrap().into_raw(); - } else if strcmp( - token.as_mut_ptr(), - b"color_prompt\x00" as *const u8 as *const libc::c_char, - ) == 0 - { - value = CString::new(config.color_prompt.clone()).unwrap().into_raw(); - } else if strcmp( - token.as_mut_ptr(), - b"color_selector\x00" as *const u8 as *const libc::c_char, - ) == 0 - { - value = CString::new(config.color_selector.clone()).unwrap().into_raw(); - } else if strcmp( - token.as_mut_ptr(), - b"verbose\x00" as *const u8 as *const libc::c_char, - ) == 0 - { - value = CString::new(config.verbose.clone()).unwrap().into_raw(); - } else { - j = 0 as libc::c_int; - while j < 20 as libc::c_int { + + pub unsafe fn load_config(&mut self, mut filename: *const libc::c_char) { + let mut fp: *mut FILE = 0 as *mut FILE; + let mut ch: libc::c_int = 0; + let mut i: libc::c_int = 0; + let mut line: [libc::c_char; 1024] = [0; 1024]; + fp = fopen(filename, b"r\x00" as *const u8 as *const libc::c_char); + if fp.is_null() { + return; + } + memset( + line.as_mut_ptr() as *mut libc::c_void, + 0 as libc::c_int, + mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong, + ); + i = 0 as libc::c_int; + ch = fgetc(fp); + loop { + match ch { + 35 => + while ch != '\n' as i32 && ch != -(1 as libc::c_int) { + ch = fgetc(fp) + }, + -1 => { + self.parse_config_line(line.as_mut_ptr()); + fclose(fp); + return; + } + 13 => ch = fgetc(fp), + 10 => { + self.parse_config_line(line.as_mut_ptr()); + memset( + line.as_mut_ptr() as *mut libc::c_void, + 0 as libc::c_int, + mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong, + ); + i = 0 as libc::c_int; + ch = fgetc(fp) + } + _ => { + if (i as libc::c_ulong) + < (mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong) + .wrapping_sub(1 as libc::c_int as libc::c_ulong) + { + let fresh2 = i; + i = i + 1; + line[fresh2 as usize] = ch as libc::c_char + } + ch = fgetc(fp) + } + } + } + } + + pub unsafe fn init_config(&mut self) { + let mut filename: [libc::c_char; 1024] = [0; 1024]; + let mut home: *const libc::c_char = 0 as *const libc::c_char; + let mut i: libc::c_int = 0; + /* copy defaults */ + i = 0 as libc::c_int; + while i < 20 as libc::c_int { + self.bookmarks[i as usize][0 as libc::c_int as usize] = 0; + i += 1 + } + /* read configs */ + self.load_config(b"/etc/cgorc\x00" as *const u8 as *const libc::c_char); /* ignore incomplete selectors */ + home = getenv(b"HOME\x00" as *const u8 as *const libc::c_char); /* not needed for history...just clear them */ + if !home.is_null() { snprintf( - bkey.as_mut_ptr(), - mem::size_of::<[libc::c_char; 128]>() as libc::c_ulong, - b"bookmark%d\x00" as *const u8 as *const libc::c_char, - j + 1 as libc::c_int, + filename.as_mut_ptr(), + mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong, + b"%s%s\x00" as *const u8 as *const libc::c_char, + home, + b"/.cgorc\x00" as *const u8 as *const libc::c_char, ); - if strcmp(token.as_mut_ptr(), bkey.as_mut_ptr()) == 0 { - value = CString::new(bookmarks[j as usize]).unwrap().into_raw(); + self.load_config(filename.as_mut_ptr()); + }; + } + + pub unsafe fn download_file( + &mut self, + mut host: &str, + mut port: usize, + mut selector: &str, + mut file: &mut File, + ) -> bool { + let mut srvfd: libc::c_int = 0; + let mut len: usize = 0; + let mut total: usize = 0; + let mut buffer: [u8; 4096] = [0; 4096]; + if check_option_true( + CString::new(self.config.verbose.clone()).unwrap().into_raw(), + ) != 0 + { + println!("downloading [{}]...", selector); + } + srvfd = dial(&host, port, selector); + if srvfd == -(1 as libc::c_int) { + println!("error: downloading [{}] failed", selector); + return false; + } + loop { + len = read( + srvfd, + buffer.as_mut_ptr() as *mut libc::c_void, + mem::size_of::<[libc::c_char; 4096]>() as libc::c_ulong, + ) as usize; + if !(len > 0) { break; - } else { - j += 1 + } + file.write(&buffer[..len]).expect("failed to write to file"); + total += len; + if check_option_true( + CString::new(self.config.verbose.clone()).unwrap().into_raw(), + ) != 0 + { + println!("downloading [{}] ({} kb)...", selector, total / 1024); } } - if value.is_null() { - return; + close(srvfd); + if check_option_true( + CString::new(self.config.verbose.clone()).unwrap().into_raw(), + ) != 0 + { + println!("downloading [{}] complete", selector); } + true } - while *line as libc::c_int == ' ' as i32 || *line as libc::c_int == '\t' as i32 { - line = line.offset(1) + + pub unsafe fn download_temp( + &mut self, + mut host: &str, + mut port: usize, + mut selector: &str, + ) -> bool { + let mut tmpfile = match NamedTempFile::new() { + Ok(f) => f, + Err(_) => { + eprintln!("error: unable to create tmp file"); + return false; + } + }; + self.tmpfilename = tmpfile.path().display().to_string(); + if !self.download_file(host, port, selector, tmpfile.as_file_mut()) { + fs::remove_file(&self.tmpfilename).expect("failed to delete temp file"); + return false; + } + return true; } - i = 0 as libc::c_int; - while *line != 0 { - if i < 512 as libc::c_int - 1 as libc::c_int { - let fresh1 = i; - i = i + 1; - *value.offset(fresh1 as isize) = *line + + pub unsafe fn add_link( + &mut self, + mut which: libc::c_char, + mut name: String, + mut host: String, + mut port: usize, + mut selector: String, + ) { + let mut a: char = '\0'; + let mut b: char = '\0'; + let mut c: char = '\0'; + if host.is_empty() || port == 0 || selector.is_empty() { + return; } - line = line.offset(1) + let mut link = Link { + which: Some(which as u8 as char), + key: self.link_key, + host, + port, + selector, + next: if self.links.is_none() { None } else { self.links.clone() }, + }; + + println!("links: {:?}", self.links); + self.links = Some(Box::new(link)); + println!("links post: {:?}", self.links); + let fresh4 = self.link_key; + self.link_key += 1; + make_key_str(fresh4, &mut a, &mut b, &mut c); + println!("{}{}{} {}", a, b, c, name); } - i -= 1; - while i > 0 as libc::c_int - && (*value.offset(i as isize) as libc::c_int == ' ' as i32 - || *value.offset(i as isize) as libc::c_int == '\t' as i32) - { - i -= 1 + + pub unsafe fn clear_links(&mut self) { + self.links = None; + self.link_key = 0; } - i += 1; - *value.offset(i as isize) = 0 as libc::c_int as libc::c_char; -} -#[no_mangle] -pub unsafe extern "C" fn load_config(mut filename: *const libc::c_char) { - let mut fp: *mut FILE = 0 as *mut FILE; - let mut ch: libc::c_int = 0; - let mut i: libc::c_int = 0; - let mut line: [libc::c_char; 1024] = [0; 1024]; - fp = fopen(filename, b"r\x00" as *const u8 as *const libc::c_char); - if fp.is_null() { - return; + + pub unsafe fn add_history(&mut self) { + let mut link: Link = Link { + which: None, + key: 0, + host: CString::new( + self.current_host + .iter() + .take_while(|x| **x != 0) + .map(|x| *x as u8) + .collect::>(), + ) + .unwrap() + .into_string() + .unwrap(), + port: self.current_port, + selector: self.current_selector.clone(), + next: if self.history.is_none() { None } else { self.history.clone() }, + }; + self.history = Some(Box::new(link)); } - memset( - line.as_mut_ptr() as *mut libc::c_void, - 0 as libc::c_int, - mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong, - ); - i = 0 as libc::c_int; - ch = fgetc(fp); - loop { - match ch { - 35 => - while ch != '\n' as i32 && ch != -(1 as libc::c_int) { - ch = fgetc(fp) - }, - -1 => { - parse_config_line(line.as_mut_ptr()); - fclose(fp); - return; + + pub unsafe fn handle_directory_line(&mut self, mut line: *mut libc::c_char) { + let mut i: libc::c_int = 0; + let mut lp: *mut u8 = 0 as *mut u8; + let mut last: *mut u8 = 0 as *mut u8; + let mut fields: [*mut u8; 4] = [0 as *mut u8; 4]; + /* tokenize */ + i = 0 as libc::c_int; + last = &mut *line.offset(1 as libc::c_int as isize) as *mut i8 as *mut u8; + lp = last; + i = 0 as libc::c_int; + while i < 4 as libc::c_int { + if *lp as libc::c_int == '\t' as i32 || *lp as libc::c_int == '\u{0}' as i32 { + fields[i as usize] = last; + last = lp.offset(1 as libc::c_int as isize); + if *lp as libc::c_int == '\u{0}' as i32 { + break; + } + *lp = '\0' as u8; + i += 1 + } + lp = lp.offset(1) + } + /* determine listing type */ + match *line.offset(0 as libc::c_int as isize) as libc::c_int { + 105 | 51 => { + printf(b" %s\n\x00" as *const u8 as *const libc::c_char, fields[0]); + } + 46 => { + /* some gopher servers use this */ + puts(b"\x00" as *const u8 as *const libc::c_char); } - 13 => ch = fgetc(fp), - 10 => { - parse_config_line(line.as_mut_ptr()); - memset( - line.as_mut_ptr() as *mut libc::c_void, - 0 as libc::c_int, - mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong, + 48 | 49 | 53 | 55 | 56 | 57 | 103 | 73 | 112 | 104 | 115 => { + self.add_link( + *line.offset(0 as libc::c_int as isize), + CString::from_raw(fields[0] as *mut i8).into_string().unwrap(), + CString::from_raw(fields[2] as *mut i8).into_string().unwrap(), + CString::from_raw(fields[3] as *mut i8) + .into_string() + .unwrap() + .parse() + .unwrap(), + CString::from_raw(fields[1] as *mut i8).into_string().unwrap(), ); - i = 0 as libc::c_int; - ch = fgetc(fp) } _ => { - if (i as libc::c_ulong) - < (mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong) - .wrapping_sub(1 as libc::c_int as libc::c_ulong) + printf( + b"miss [%c]: %s\n\x00" as *const u8 as *const libc::c_char, + *line.offset(0 as libc::c_int as isize) as libc::c_int, + fields[0], + ); + } + }; + } + + pub unsafe fn view_directory( + &mut self, + mut host: &str, + mut port: usize, + mut selector: &str, + mut make_current: libc::c_int, + ) { + let mut is_dir: libc::c_int = 0; + let mut srvfd: libc::c_int = 0; + let mut i: libc::c_int = 0; + let mut head_read: libc::c_int = 0; + let mut line: [libc::c_char; 1024] = [0; 1024]; + let mut head: [[libc::c_char; 1024]; 5] = [[0; 1024]; 5]; + srvfd = dial(&host, port, selector); + if srvfd != -(1 as libc::c_int) { + /* only adapt current prompt when successful */ + /* make history entry */ + if make_current != 0 { + self.add_history(); + } + /* don't overwrite the current_* things... */ + if host + != CString::new( + self.current_host + .iter() + .take_while(|x| **x != 0) + .map(|x| *x as u8) + .collect::>(), + ) + .unwrap() + .into_string() + .unwrap() + { + snprintf( + self.current_host.as_mut_ptr(), + mem::size_of::<[libc::c_char; 512]>() as libc::c_ulong, + b"%s\x00" as *const u8 as *const libc::c_char, + host, + ); /* clear links *AFTER* dialing out!! */ + } /* quit if not successful */ + if port != self.current_port { + self.current_port = port; + } + if selector != self.current_selector { + self.current_selector = selector.to_string(); + } + } + self.clear_links(); + if srvfd == -(1 as libc::c_int) { + return; + } + head_read = 0 as libc::c_int; + is_dir = 1 as libc::c_int; + while head_read < 5 as libc::c_int + && read_line( + srvfd, + line.as_mut_ptr(), + mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong, + ) != 0 + { + strcpy(head[head_read as usize].as_mut_ptr(), line.as_mut_ptr()); + if is_valid_directory_entry(head[head_read as usize].as_mut_ptr()) == 0 { + is_dir = 0 as libc::c_int; + break; + } else { + head_read += 1 + } + } + if is_dir == 0 { + println!("error: Not a directory"); + close(srvfd); + return; + } + i = 0 as libc::c_int; + while i < head_read { + self.handle_directory_line(head[i as usize].as_mut_ptr()); + i += 1 + } + while read_line( + srvfd, + line.as_mut_ptr(), + mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong, + ) != 0 + { + self.handle_directory_line(line.as_mut_ptr()); + } + close(srvfd); + } + + pub unsafe fn view_file( + &mut self, + mut cmd: *const libc::c_char, + mut host: &str, + mut port: usize, + mut selector: &str, + ) { + let mut pid: pid_t = 0; + let mut status: libc::c_int = 0; + let mut i: libc::c_int = 0; + let mut j: libc::c_int = 0; + let mut buffer: [libc::c_char; 1024] = [0; 1024]; + let mut argv: [*mut libc::c_char; 32] = [0 as *mut libc::c_char; 32]; + let mut p: *mut libc::c_char = 0 as *mut libc::c_char; + if check_option_true( + CString::new(self.config.verbose.clone()).unwrap().into_raw(), + ) != 0 + { + println!("h({}) p({}) s({})", host, port, selector); + } + if !self.download_temp(host, port, selector) { + return; + } + /* parsed command line string */ + argv[0 as libc::c_int as usize] = + &mut *buffer.as_mut_ptr().offset(0 as libc::c_int as isize) + as *mut libc::c_char; + p = cmd as *mut libc::c_char; + i = 0 as libc::c_int; + j = 1 as libc::c_int; + while *p as libc::c_int != 0 + && (i as libc::c_ulong) + < (mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong) + .wrapping_sub(1 as libc::c_int as libc::c_ulong) + && j < 30 as libc::c_int + { + if *p as libc::c_int == ' ' as i32 || *p as libc::c_int == '\t' as i32 { + let fresh5 = i; + i = i + 1; + buffer[fresh5 as usize] = 0 as libc::c_int as libc::c_char; + let fresh6 = j; + j = j + 1; + argv[fresh6 as usize] = + &mut *buffer.as_mut_ptr().offset(i as isize) as *mut libc::c_char; + while *p as libc::c_int == ' ' as i32 || *p as libc::c_int == '\t' as i32 { - let fresh2 = i; - i = i + 1; - line[fresh2 as usize] = ch as libc::c_char + p = p.offset(1) } - ch = fgetc(fp) + } else { + let fresh7 = p; + p = p.offset(1); + let fresh8 = i; + i = i + 1; + buffer[fresh8 as usize] = *fresh7 } } + buffer[i as usize] = 0 as libc::c_int as libc::c_char; + let fresh9 = j; + j = j + 1; + argv[fresh9 as usize] = + CString::new(self.tmpfilename.clone()).unwrap().into_raw(); + argv[j as usize] = 0 as *mut libc::c_char; + /* fork and execute */ + if check_option_true( + CString::new(self.config.verbose.clone()).unwrap().into_raw(), + ) != 0 + { + printf( + b"executing: %s %s\n\x00" as *const u8 as *const libc::c_char, + cmd, + self.tmpfilename.as_mut_ptr(), + ); /* to wait for browsers etc. that return immediatly */ + } + pid = fork(); + if pid == 0 as libc::c_int { + if execvp( + argv[0 as libc::c_int as usize], + argv.as_mut_ptr() as *const *mut libc::c_char, + ) == -(1 as libc::c_int) + { + eprintln!("error: execvp() failed!"); + } + } else if pid == -(1 as libc::c_int) { + eprintln!("error: fork() failed!"); + } + sleep(Duration::from_secs(1)); + waitpid(pid, &mut status, 0 as libc::c_int); + fs::remove_file(&self.tmpfilename).expect("failed to delete temp file"); } -} -pub unsafe extern "C" fn init_config() { - let mut filename: [libc::c_char; 1024] = [0; 1024]; - let mut home: *const libc::c_char = 0 as *const libc::c_char; - let mut i: libc::c_int = 0; - /* copy defaults */ - i = 0 as libc::c_int; - while i < 20 as libc::c_int { - bookmarks[i as usize][0 as libc::c_int as usize] = 0; - i += 1 + + pub unsafe fn view_download( + &mut self, + mut host: &str, + mut port: usize, + mut selector: &str, + ) { + let mut filename: String = + Path::new(selector).file_name().unwrap_or_default().to_string_lossy().into(); + let mut line: String = match input(&format!( + "enter filename for download [{}]: ", + filename + )) + .as_str() + { + "" => { + println!("download aborted"); + return; + } + x => x.into(), + }; + filename = line; // TODO something stinky going on here + let mut file = match File::create(&filename) { + Ok(f) => f, + Err(_) => { + println!( + "error: unable to create file [{}]: {}", + filename, + CString::from_raw(strerror(*__errno_location())) + .into_string() + .unwrap() + ); + return; + } + }; + // O_CREAT, O_NOCTTY, O_WRONLY, O_EXCL + if !self.download_file(host, port, selector, &mut file) { + println!("error: eunable to download [{}]", selector); + fs::remove_file(filename).expect("failed to delete file"); + } } - /* read configs */ - load_config(b"/etc/cgorc\x00" as *const u8 as *const libc::c_char); /* ignore incomplete selectors */ - home = getenv(b"HOME\x00" as *const u8 as *const libc::c_char); /* not needed for history...just clear them */ - if !home.is_null() { + + pub unsafe fn view_search( + &mut self, + mut host: &str, + mut port: usize, + mut selector: &str, + ) { + let mut search_selector: [libc::c_char; 1024] = [0; 1024]; + let mut line: [libc::c_char; 1024] = [0; 1024]; + println!("enter search string: "); + if read_line( + 0 as libc::c_int, + line.as_mut_ptr(), + mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong, + ) == 0 + { + println!("search aborted"); + return; + } snprintf( - filename.as_mut_ptr(), + search_selector.as_mut_ptr(), mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong, - b"%s%s\x00" as *const u8 as *const libc::c_char, - home, - b"/.cgorc\x00" as *const u8 as *const libc::c_char, + b"%s\t%s\x00" as *const u8 as *const libc::c_char, + selector, + line.as_mut_ptr(), ); - load_config(filename.as_mut_ptr()); - }; + self.view_directory( + host, + port, + &CString::from_raw(search_selector.as_mut_ptr()).into_string().unwrap(), + 1 as libc::c_int, + ); + } + + pub unsafe fn view_history(&mut self, mut key: Option) { + let mut history_key: usize = 0; + let mut a: char = '\0'; + let mut b: char = '\0'; + let mut c: char = '\0'; + let mut link: Option> = None; + if self.history.is_none() { + println!("(empty history)"); + return; + } + if key.is_none() { + println!("(history)"); + link = self.history.clone(); + while let Some(l) = link { + let fresh10 = history_key; + history_key = history_key + 1; + make_key_str(fresh10, &mut a, &mut b, &mut c); + println!("{}{}{} {}:{}/{}", a, b, c, (*l).host, (*l).port, (*l).selector,); + link = (*l).next + } + } else if let Some(key) = key { + /* traverse history list */ + link = self.history.clone(); + while let Some(l) = link { + if history_key == key { + self.view_directory( + &(*l).host, + (*l).port, + &(*l).selector, + 0 as libc::c_int, + ); + return; + } + link = (*l).next; + history_key += 1 + } + println!("history item not found"); + }; + } + + pub unsafe fn view_bookmarks(&mut self, mut key: Option) { + let mut i: usize = 0; + let mut a: char = '\0'; + let mut b: char = '\0'; + let mut c: char = '\0'; + if key.is_none() { + println!("(bookmarks)"); + i = 0; + while i < 20 { + if self.bookmarks[i as usize][0] != 0 { + make_key_str(i, &mut a, &mut b, &mut c); + println!( + "{}{}{} {}", + a, + b, + c, + CString::new( + self.bookmarks[i as usize] + .iter() + .take_while(|x| **x != 0) + .cloned() + .collect::>() + ) + .unwrap() + .into_string() + .unwrap() + ); + } + i += 1 + } + } else if let Some(key) = key { + i = 0; + while i < 20 { + if self.bookmarks[i][0] != 0 && i == key { + if self.parse_uri( + CString::new( + self.bookmarks[i] + .iter() + .take_while(|x| **x != 0) + .cloned() + .collect::>(), + ) + .unwrap() + .into_string() + .unwrap(), + ) { + self.view_directory( + &self.parsed_host.clone(), + self.parsed_port.clone(), + &self.parsed_selector.clone(), + 0 as libc::c_int, + ); + } else { + println!( + "invalid gopher URI: {}", + CString::new( + self.bookmarks[i] + .iter() + .take_while(|x| **x != 0) + .cloned() + .collect::>() + ) + .unwrap() + .into_string() + .unwrap() + ); + } + return; + } + i += 1 + } + }; + } + + pub fn pop_history(&mut self) { + match &self.history.clone() { + None => println!("(empty history)"), + Some(h) => { + /* reload page from history (and don't count as history) */ + unsafe { + self.view_directory( + &(*h).host, + (*h).port, + &(*h).selector, + 0 as libc::c_int, + ); + } + /* history is history... :) */ + self.history = h.next.clone(); + } + } + } + + pub unsafe fn follow_link(&mut self, mut key: usize) -> bool { + let mut link: Option> = self.links.clone(); + + while let Some(ref l) = link { + if (*l).key != key { + link = (*l).next.clone() + } else if let Some(w) = (*l).which { + match w { + '0' => { + self.view_file( + CString::new(self.config.cmd_text.clone()) + .unwrap() + .into_raw(), + &(*l).host, + (*l).port, + &(*l).selector, + ); + } + '1' => { + self.view_directory( + &(*l).host, + (*l).port, + &(*l).selector, + 1 as libc::c_int, + ); + } + '7' => { + self.view_search(&(*l).host, (*l).port, &(*l).selector); + } + '5' | '9' => { + self.view_download(&(*l).host, (*l).port, &(*l).selector); + } + '8' => { + view_telnet(&(*l).host, (*l).port); + } + 'f' | 'I' | 'p' => { + self.view_file( + CString::new(self.config.cmd_image.clone()) + .unwrap() + .into_raw(), + &(*l).host, + (*l).port, + &(*l).selector, + ); + } + 'h' => { + self.view_file( + CString::new(self.config.cmd_browser.clone()) + .unwrap() + .into_raw(), + &(*l).host, + (*l).port, + &(*l).selector, + ); + } + 's' => { + self.view_file( + CString::new(self.config.cmd_player.clone()) + .unwrap() + .into_raw(), + &(*l).host, + (*l).port, + &(*l).selector, + ); + } + _ => { + println!("missing handler [{}]", w); + } + } + return true; + } + /* return the array is broken after view! */ + } + return false; + } + + pub unsafe fn download_link(&mut self, mut key: usize) { + let mut link: Option> = self.links.clone(); + while let Some(l) = link { + if (*l).key != key { + link = (*l).next + } else { + self.view_download(&(*l).host, (*l).port, &(*l).selector); + return; + } + } + println!("link not found"); + } + + /* function prototypes */ + pub unsafe fn parse_uri(&mut self, mut uri: String) -> bool { + let mut tmp: &str = &uri[..]; + /* strip gopher:// */ + if uri.starts_with("gopher://") { + tmp = &uri[9..]; + } + self.parsed_host = + tmp.chars().take_while(|x| !(*x == ':' || *x == '/')).collect(); + + if self.parsed_host.is_empty() { + return false; + } + + if tmp.contains(':') { + let port_string: String = tmp + .chars() + .skip_while(|x| *x != ':') + .skip(1) + .take_while(|x| *x != '/') + .collect(); + + if port_string.is_empty() { + self.parsed_port = 70; + } else { + self.parsed_port = match port_string.parse() { + Ok(p) => p, + Err(_) => { + eprintln!("failed to parse port"); + exit(1); + } + }; + } + } + tmp = &tmp[tmp.find('/').unwrap_or(0)..]; + /* parse selector (ignore slash and selector type) */ + if tmp.is_empty() || tmp.find('/').is_none() { + self.parsed_selector = "/".to_string(); + } else { + self.parsed_selector = tmp.into(); + } + true + } + + pub unsafe fn init(&mut self, mut argc: usize, mut argv: Vec) -> libc::c_int { + let mut i: usize = 0; + let mut line: [u8; 1024] = [0; 1024]; + let mut uri: String = String::new(); + /* copy defaults */ + self.init_config(); + uri = self.config.start_uri.clone(); + /* parse command line */ + i = 1; + while i < argc { + if argv[i].chars().next() == Some('-') { + match argv[i].chars().skip(1).next() { + Some('H') => { + usage(); + } + Some('v') => { + banner(true); + exit(0); + } + _ => { + usage(); + } + } + } else { + uri = argv[i].clone(); + } + i += 1 + } + /* parse uri */ + if !self.parse_uri(uri) { + banner(false); + eprintln!("invalid gopher URI: {}", argv[i],); + exit(1); + } + /* main loop */ + self.view_directory( + &self.parsed_host.clone(), + self.parsed_port.clone(), + &self.parsed_selector.clone(), + 0 as libc::c_int, + ); /* to display the prompt */ + loop { + // todo color prompt + println!( + "{}:{}{} ", + CString::new( + self.current_host + .iter() + .take_while(|x| **x != 0) + .map(|x| *x as u8) + .collect::>() + ) + .unwrap() + .into_string() + .unwrap(), + self.current_port, + &self.current_selector + ); + if read_line( + 0 as libc::c_int, + line.as_mut_ptr() as *mut i8, + mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong, + ) == 0 + { + println!("QUIT"); + return 0 as libc::c_int; + } + i = strlen(line.as_mut_ptr() as *mut i8) as usize; + match line[0 as libc::c_int as usize] as libc::c_int { + 63 => { + println!("? - help\n* - reload directory\n< - go back in history\n.[LINK] - download the given link\nH - show history\nH[LINK] - jump to the specified history item\nG[URI] - jump to the given gopher URI\nB - show bookmarks\nB[LINK] - jump to the specified bookmark item\nC^d - quit"); + } + 60 => { + self.pop_history(); + } + 42 => { + self.view_directory( + &CString::new( + self.current_host + .clone() + .iter() + .take_while(|x| **x != 0) + .map(|x| *x as u8) + .collect::>(), + ) + .unwrap() + .into_string() + .unwrap(), + self.current_port.clone(), + &self.current_selector.clone(), + 0 as libc::c_int, + ); + } + 46 => { + self.download_link( + make_key(line[1] as char, line[2] as char, line[3] as char) + .unwrap_or(0), + ); + } + 72 => + if i == 1 || i == 3 || i == 4 { + self.view_history(make_key( + line[1] as char, + line[2] as char, + line[3] as char, + )); + }, + 71 => { + if self.parse_uri( + CString::from_raw( + &mut *line.as_mut_ptr().offset(1 as libc::c_int as isize) + as *mut u8 as *mut i8, + ) + .into_string() + .unwrap(), + ) { + self.view_directory( + &self.parsed_host.clone(), + self.parsed_port.clone(), + &self.parsed_selector.clone(), + 1 as libc::c_int, + ); + } else { + println!("invalid gopher URI"); + } + } + 66 => + if i == 1 || i == 3 || i == 4 { + self.view_bookmarks(make_key( + line[1] as char, + line[2] as char, + line[3] as char, + )); + }, + _ => { + self.follow_link( + make_key(line[0] as char, line[1] as char, line[2] as char) + .unwrap_or(0), + ); + } + } + } + /* never get's here but stops cc complaining */ + } } -pub unsafe extern "C" fn dial( - mut host: &str, - mut port: usize, - mut selector: &str, -) -> libc::c_int { + +/* implementation */ +pub fn usage() { + eprintln!("usage: cgo [-v] [-H] [gopher URI]"); + exit(0); +} +pub fn banner(to_error: bool) { + if to_error { + eprintln!("cgo 0.6.1 Copyright (c) 2020 Sebastian Steinhauer"); + } else { + println!("cgo 0.6.1 Copyright (c) 2020 Sebastian Steinhauer"); + } +} +pub unsafe fn check_option_true(mut option: *const libc::c_char) -> libc::c_int { + return (strcasecmp(option, b"false\x00" as *const u8 as *const libc::c_char) != 0 + && strcasecmp(option, b"off\x00" as *const u8 as *const libc::c_char) != 0) + as libc::c_int; +} + +pub unsafe fn dial(mut host: &str, mut port: usize, mut selector: &str) -> libc::c_int { let mut hints: addrinfo = addrinfo { ai_flags: 0, ai_family: 0, @@ -503,7 +1363,13 @@ pub unsafe extern "C" fn dial( ); hints.ai_family = 0 as libc::c_int; hints.ai_socktype = SOCK_STREAM as libc::c_int; - if getaddrinfo(CString::new(host).unwrap().into_raw(), CString::new(port.to_string()).unwrap().into_raw(), &mut hints, &mut res) != 0 as libc::c_int { + if getaddrinfo( + CString::new(host).unwrap().into_raw(), + CString::new(port.to_string()).unwrap().into_raw(), + &mut hints, + &mut res, + ) != 0 as libc::c_int + { eprintln!( "error: cannot resolve hostname '{}:{}': {}", host, @@ -525,13 +1391,7 @@ pub unsafe extern "C" fn dial( } freeaddrinfo(res); if r.is_null() { - fprintf( - stderr, - b"error: cannot connect to host \'%s:%s\'\n\x00" as *const u8 - as *const libc::c_char, - host, - port, - ); + eprintln!("error: cannot connect to host '{}:{}'", host, port); return -(1 as libc::c_int); } snprintf( @@ -544,16 +1404,14 @@ pub unsafe extern "C" fn dial( if write(srv, request.as_mut_ptr() as *const libc::c_void, l as size_t) != l as libc::c_long { - fprintf( - stderr, - b"error: cannot complete request\n\x00" as *const u8 as *const libc::c_char, - ); + eprintln!("error: cannot complete request"); close(srv); return -(1 as libc::c_int); } return srv; } -pub unsafe extern "C" fn read_line( + +pub unsafe fn read_line( mut fd: libc::c_int, mut buf: *mut libc::c_char, mut buf_len: size_t, @@ -583,90 +1441,27 @@ pub unsafe extern "C" fn read_line( '\u{0}' as i32 as libc::c_char; return 1 as libc::c_int; } -pub unsafe fn download_file( - mut host: &str, - mut port: usize, - mut selector: &str, - mut file: &mut File, -) -> bool { - let mut srvfd: libc::c_int = 0; - let mut len: usize = 0; - let mut total: usize = 0; - let mut buffer: [u8; 4096] = [0; 4096]; - if check_option_true(CString::new(config.verbose.clone()).unwrap().into_raw()) != 0 { - println!("downloading [{}]...", selector); - } - srvfd = dial(&host, port, selector); - if srvfd == -(1 as libc::c_int) { - println!("error: downloading [{}] failed", selector); - return false; - } - loop { - len = read( - srvfd, - buffer.as_mut_ptr() as *mut libc::c_void, - mem::size_of::<[libc::c_char; 4096]>() as libc::c_ulong, - ) as usize; - if !(len > 0) { - break; - } - file.write(&buffer[..len]); - total += len; - if check_option_true(CString::new(config.verbose.clone()).unwrap().into_raw()) != 0 { - println!("downloading [{}] ({} kb)...", selector, total / 1024); - } - } - close(srvfd); - if check_option_true(CString::new(config.verbose.clone()).unwrap().into_raw()) != 0 { - println!("downloading [{}] complete", selector); - } - true -} -pub unsafe extern "C" fn download_temp( - mut host: &str, - mut port: usize, - mut selector: &str, -) -> bool { - let mut tmpfile = match NamedTempFile::new() { - Ok(f) => f, - Err(_) => { - eprintln!("error: unable to create tmp file"); - return false - } - }; - tmpfilename = tmpfile.path().display().to_string(); - if !download_file(host, port, selector, tmpfile.as_file_mut()) { - fs::remove_file(&tmpfilename); - return false; - } - return true; -} -pub unsafe extern "C" fn make_key( - mut c1: char, - mut c2: char, - mut c3: char, -) -> Option { + +pub unsafe fn make_key(mut c1: char, mut c2: char, mut c3: char) -> Option { if c1 == '\0' || c2 == '\0' { return None; } if c3 == '\0' { Some( - (c1 as u8 - 'a' as u8) as usize - * ('z' as usize - 'a' as usize + 1) - + (c2 as u8 - 'a' as u8) as usize + (c1 as u8 - 'a' as u8) as usize * ('z' as usize - 'a' as usize + 1) + + (c2 as u8 - 'a' as u8) as usize, ) } else { Some( ((c1 as u8 - 'a' as u8) as usize + 1) - * ('z' as usize - 'a' as usize + 1) - * ('z' as usize - 'a' as usize + 1) - + ((c2 as u8 - 'a' as u8) as usize) * ('z' as usize - 'a' as usize + 1) - + ((c3 as u8 - 'a' as u8) as usize) + * ('z' as usize - 'a' as usize + 1) + + ((c2 as u8 - 'a' as u8) as usize) * ('z' as usize - 'a' as usize + 1) + + ((c3 as u8 - 'a' as u8) as usize), ) } } -pub unsafe extern "C" fn make_key_str( +pub unsafe fn make_key_str( mut key: usize, mut c1: &mut char, mut c2: &mut char, @@ -676,10 +1471,10 @@ pub unsafe extern "C" fn make_key_str( < ('z' as usize - 'a' as usize + 1 as usize) * ('z' as usize - 'a' as usize + 1 as usize) { - *c1 = ('a' as usize + key / ('z' as usize - 'a' as usize + 1 as usize)) - as u8 as char; - *c2 = ('a' as usize + key % ('z' as usize - 'a' as usize + 1 as usize)) - as u8 as char; + *c1 = ('a' as usize + key / ('z' as usize - 'a' as usize + 1 as usize)) as u8 + as char; + *c2 = ('a' as usize + key % ('z' as usize - 'a' as usize + 1 as usize)) as u8 + as char; *c3 = 0 as usize as u8 as char } else { *c1 = ('a' as usize @@ -690,116 +1485,12 @@ pub unsafe extern "C" fn make_key_str( *c2 = ('a' as usize + key / ('z' as usize - 'a' as usize + 1 as usize) % ('z' as usize - 'a' as usize + 1 as usize)) as u8 as char; - *c3 = ('a' as usize + key % ('z' as usize - 'a' as usize + 1 as usize)) - as u8 as char + *c3 = ('a' as usize + key % ('z' as usize - 'a' as usize + 1 as usize)) as u8 + as char }; } -pub unsafe fn add_link( - mut which: libc::c_char, - mut name: String, - mut host: String, - mut port: usize, - mut selector: String, -) { - let mut a: char = '\0'; - let mut b: char = '\0'; - let mut c: char = '\0'; - if host.is_empty() || port == 0 || selector.is_empty() { - return; - } - let mut link = Link { - which: Some(which as u8 as char), - key: link_key, - host: host, - port: port, - selector: selector, - next: if links.is_none() { - None - } else { links.clone() } - }; - println!("links: {:?}", links); - links = Some(Box::new(link)); - println!("links post: {:?}", links); - let fresh4 = link_key; - link_key = link_key + 1; - make_key_str(fresh4, &mut a, &mut b, &mut c); - println!("{}{}{} {}", a, b, c, name); -} -pub unsafe extern "C" fn clear_links() { - links = None; - link_key = 0; -} -pub unsafe extern "C" fn add_history() { - let mut link: Link = Link { - which: None, - key: 0, - host: CString::new(current_host.iter().take_while(|x| **x != 0).map(|x| *x as u8).collect::>()).unwrap().into_string().unwrap(), - port: current_port, - selector: current_selector.clone(), - next: if history.is_none() { - None - } else { - history.clone() - } - }; - history = Some(Box::new(link)); -} -pub unsafe extern "C" fn handle_directory_line(mut line: *mut libc::c_char) { - let mut i: libc::c_int = 0; - let mut lp: *mut u8 = 0 as *mut u8; - let mut last: *mut u8 = 0 as *mut u8; - let mut fields: [*mut u8; 4] = [0 as *mut u8; 4]; - /* tokenize */ - i = 0 as libc::c_int; - last = &mut *line.offset(1 as libc::c_int as isize) as *mut i8 as *mut u8; - lp = last; - i = 0 as libc::c_int; - while i < 4 as libc::c_int { - if *lp as libc::c_int == '\t' as i32 || *lp as libc::c_int == '\u{0}' as i32 { - fields[i as usize] = last; - last = lp.offset(1 as libc::c_int as isize); - if *lp as libc::c_int == '\u{0}' as i32 { - break; - } - *lp = '\0' as u8; - i += 1 - } - lp = lp.offset(1) - } - /* determine listing type */ - match *line.offset(0 as libc::c_int as isize) as libc::c_int { - 105 | 51 => { - printf( - b" %s\n\x00" as *const u8 as *const libc::c_char, - fields[0], - ); - } - 46 => { - /* some gopher servers use this */ - puts(b"\x00" as *const u8 as *const libc::c_char); - } - 48 | 49 | 53 | 55 | 56 | 57 | 103 | 73 | 112 | 104 | 115 => { - add_link( - *line.offset(0 as libc::c_int as isize), - CString::from_raw(fields[0] as *mut i8).into_string().unwrap(), - CString::from_raw(fields[2] as *mut i8).into_string().unwrap(), - CString::from_raw(fields[3] as *mut i8).into_string().unwrap().parse().unwrap(), - CString::from_raw(fields[1] as *mut i8).into_string().unwrap(), - ); - } - _ => { - printf( - b"miss [%c]: %s\n\x00" as *const u8 as *const libc::c_char, - *line.offset(0 as libc::c_int as isize) as libc::c_int, - fields[0], - ); - } - }; -} -pub unsafe extern "C" fn is_valid_directory_entry( - mut line: *const libc::c_char, -) -> libc::c_int { +pub unsafe fn is_valid_directory_entry(mut line: *const libc::c_char) -> libc::c_int { match *line.offset(0 as libc::c_int as isize) as libc::c_int { 105 | 51 | 46 | 48 | 49 | 53 | 55 | 56 | 57 | 103 | 73 | 112 | 104 | 115 => { /* some gopher servers use this */ @@ -808,178 +1499,11 @@ pub unsafe extern "C" fn is_valid_directory_entry( _ => return 0 as libc::c_int, }; } -pub unsafe extern "C" fn view_directory( - mut host: &str, - mut port: usize, - mut selector: &str, - mut make_current: libc::c_int, -) { - let mut is_dir: libc::c_int = 0; - let mut srvfd: libc::c_int = 0; - let mut i: libc::c_int = 0; - let mut head_read: libc::c_int = 0; - let mut line: [libc::c_char; 1024] = [0; 1024]; - let mut head: [[libc::c_char; 1024]; 5] = [[0; 1024]; 5]; - srvfd = dial(&host, port, selector); - if srvfd != -(1 as libc::c_int) { - /* only adapt current prompt when successful */ - /* make history entry */ - if make_current != 0 { - add_history(); - } - /* don't overwrite the current_* things... */ - if host != CString::new(current_host.iter().take_while(|x| **x != 0).map(|x| *x as u8).collect::>()).unwrap().into_string().unwrap() { - snprintf( - current_host.as_mut_ptr(), - mem::size_of::<[libc::c_char; 512]>() as libc::c_ulong, - b"%s\x00" as *const u8 as *const libc::c_char, - host, - ); /* clear links *AFTER* dialing out!! */ - } /* quit if not successful */ - if port != current_port { - current_port = port; - } - if selector != current_selector { - current_selector = selector.to_string(); - } - } - clear_links(); - if srvfd == -(1 as libc::c_int) { - return; - } - head_read = 0 as libc::c_int; - is_dir = 1 as libc::c_int; - while head_read < 5 as libc::c_int - && read_line( - srvfd, - line.as_mut_ptr(), - mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong, - ) != 0 - { - strcpy(head[head_read as usize].as_mut_ptr(), line.as_mut_ptr()); - if is_valid_directory_entry(head[head_read as usize].as_mut_ptr()) == 0 { - is_dir = 0 as libc::c_int; - break; - } else { - head_read += 1 - } - } - if is_dir == 0 { - puts(b"error: Not a directory.\x00" as *const u8 as *const libc::c_char); - close(srvfd); - return; - } - i = 0 as libc::c_int; - while i < head_read { - handle_directory_line(head[i as usize].as_mut_ptr()); - i += 1 - } - while read_line( - srvfd, - line.as_mut_ptr(), - mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong, - ) != 0 - { - handle_directory_line(line.as_mut_ptr()); - } - close(srvfd); -} -pub unsafe extern "C" fn view_file( - mut cmd: *const libc::c_char, - mut host: &str, - mut port: usize, - mut selector: &str, -) { - let mut pid: pid_t = 0; - let mut status: libc::c_int = 0; - let mut i: libc::c_int = 0; - let mut j: libc::c_int = 0; - let mut buffer: [libc::c_char; 1024] = [0; 1024]; - let mut argv: [*mut libc::c_char; 32] = [0 as *mut libc::c_char; 32]; - let mut p: *mut libc::c_char = 0 as *mut libc::c_char; - if check_option_true(CString::new(config.verbose.clone()).unwrap().into_raw()) != 0 { - printf( - b"h(%s) p(%s) s(%s)\n\x00" as *const u8 as *const libc::c_char, - host, - port, - selector, - ); - } - if !download_temp(host, port, selector) { - return; - } - /* parsed command line string */ - argv[0 as libc::c_int as usize] = - &mut *buffer.as_mut_ptr().offset(0 as libc::c_int as isize) as *mut libc::c_char; - p = cmd as *mut libc::c_char; - i = 0 as libc::c_int; - j = 1 as libc::c_int; - while *p as libc::c_int != 0 - && (i as libc::c_ulong) - < (mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong) - .wrapping_sub(1 as libc::c_int as libc::c_ulong) - && j < 30 as libc::c_int - { - if *p as libc::c_int == ' ' as i32 || *p as libc::c_int == '\t' as i32 { - let fresh5 = i; - i = i + 1; - buffer[fresh5 as usize] = 0 as libc::c_int as libc::c_char; - let fresh6 = j; - j = j + 1; - argv[fresh6 as usize] = - &mut *buffer.as_mut_ptr().offset(i as isize) as *mut libc::c_char; - while *p as libc::c_int == ' ' as i32 || *p as libc::c_int == '\t' as i32 { - p = p.offset(1) - } - } else { - let fresh7 = p; - p = p.offset(1); - let fresh8 = i; - i = i + 1; - buffer[fresh8 as usize] = *fresh7 - } - } - buffer[i as usize] = 0 as libc::c_int as libc::c_char; - let fresh9 = j; - j = j + 1; - argv[fresh9 as usize] = CString::new(tmpfilename.clone()).unwrap().into_raw(); - argv[j as usize] = 0 as *mut libc::c_char; - /* fork and execute */ - if check_option_true(CString::new(config.verbose.clone()).unwrap().into_raw()) != 0 { - printf( - b"executing: %s %s\n\x00" as *const u8 as *const libc::c_char, - cmd, - tmpfilename.as_mut_ptr(), - ); /* to wait for browsers etc. that return immediatly */ - } - pid = fork(); - if pid == 0 as libc::c_int { - if execvp( - argv[0 as libc::c_int as usize], - argv.as_mut_ptr() as *const *mut libc::c_char, - ) == -(1 as libc::c_int) - { - puts(b"error: execvp() failed!\x00" as *const u8 as *const libc::c_char); - } - } else if pid == -(1 as libc::c_int) { - puts(b"error: fork() failed\x00" as *const u8 as *const libc::c_char); - } - sleep(Duration::from_secs(1)); - waitpid(pid, &mut status, 0 as libc::c_int); - fs::remove_file(&tmpfilename); -} -pub unsafe extern "C" fn view_telnet( - mut host: &str, - mut port: usize, -) { + +pub unsafe fn view_telnet(mut host: &str, mut port: usize) { let mut pid: pid_t = 0; let mut status: libc::c_int = 0; - printf( - b"executing: %s %s %s\n\x00" as *const u8 as *const libc::c_char, - b"telnet\x00" as *const u8 as *const libc::c_char, - host, - port, - ); + println!("executing: telnet {} {}", host, port); pid = fork(); if pid == 0 as libc::c_int { if execlp( @@ -990,446 +1514,18 @@ pub unsafe extern "C" fn view_telnet( 0 as *mut libc::c_void, ) == -(1 as libc::c_int) { - puts(b"error: execlp() failed!\x00" as *const u8 as *const libc::c_char); + println!("error: execlp() failed!"); } } else if pid == -(1 as libc::c_int) { - puts(b"error: fork() failed!\x00" as *const u8 as *const libc::c_char); + eprintln!("error: fork() failed"); } waitpid(pid, &mut status, 0 as libc::c_int); - puts(b"(done)\x00" as *const u8 as *const libc::c_char); -} -pub unsafe extern "C" fn view_download( - mut host: &str, - mut port: usize, - mut selector: &str, -) { - let mut filename: String = Path::new(selector).file_name().unwrap_or_default().to_string_lossy().into(); - let mut line: String = match input(&format!("enter filename for download [{}]: ", filename)).as_str() { - "" => { - println!("download aborted"); - return; - }, - x => x.into(), - }; - filename = line; // TODO something stinky going on here - let mut file = match File::create(&filename) { - Ok(f) => f, - Err(_) => { - println!("error: unable to create file [{}]: {}", filename, CString::from_raw(strerror(*__errno_location())).into_string().unwrap()); - return; - }, - }; - // O_CREAT, O_NOCTTY, O_WRONLY, O_EXCL - if !download_file(host, port, selector, &mut file) { - println!("error: eunable to download [{}]", selector); - fs::remove_file(filename); - } -} -pub unsafe extern "C" fn view_search( - mut host: &str, - mut port: usize, - mut selector: &str, -) { - let mut search_selector: [libc::c_char; 1024] = [0; 1024]; - let mut line: [libc::c_char; 1024] = [0; 1024]; - printf(b"enter search string: \x00" as *const u8 as *const libc::c_char); - if read_line( - 0 as libc::c_int, - line.as_mut_ptr(), - mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong, - ) == 0 - { - puts(b"search aborted\x00" as *const u8 as *const libc::c_char); - return; - } - snprintf( - search_selector.as_mut_ptr(), - mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong, - b"%s\t%s\x00" as *const u8 as *const libc::c_char, - selector, - line.as_mut_ptr(), - ); - view_directory(host, port, &CString::from_raw(search_selector.as_mut_ptr()).into_string().unwrap(), 1 as libc::c_int); -} -pub unsafe extern "C" fn view_history(mut key: usize) { - let mut history_key: usize = 0; - let mut a: char = '\0'; - let mut b: char = '\0'; - let mut c: char = '\0'; - let mut link: Option> = None; - if history.is_none() { - println!("(empty history)"); - return; - } - if key < 0 { - puts(b"(history)\x00" as *const u8 as *const libc::c_char); - link = history.clone(); - while let Some(l) = link { - let fresh10 = history_key; - history_key = history_key + 1; - make_key_str(fresh10, &mut a, &mut b, &mut c); - println!( - "{}{}{} {}:{}/{}", - a, - b, - c, - (*l).host, - (*l).port, - (*l).selector, - ); - link = (*l).next - } - } else { - /* traverse history list */ - link = history.clone(); - while let Some(l) = link { - if history_key == key { - view_directory( - &(*l).host, - (*l).port, - &(*l).selector, - 0 as libc::c_int, - ); - return; - } - link = (*l).next; - history_key += 1 - } - println!("history item not found"); - }; -} -pub unsafe extern "C" fn view_bookmarks(mut key: usize) { - let mut i: usize = 0; - let mut a: char = '\0'; - let mut b: char = '\0'; - let mut c: char = '\0'; - if key < 0 { - puts(b"(bookmarks)\x00" as *const u8 as *const libc::c_char); - i = 0; - while i < 20 { - if bookmarks[i as usize][0] != 0 { - make_key_str(i, &mut a, &mut b, &mut c); - println!( - "{}{}{} {}", - a, - b, - c, - CString::new(bookmarks[i as usize].iter().take_while(|x| **x != 0).cloned().collect::>()).unwrap().into_string().unwrap() - ); - } - i += 1 - } - } else { - i = 0; - while i < 20 { - if bookmarks[i][0] != 0 - && i == key - { - if parse_uri( - CString::new(bookmarks[i].iter().take_while(|x| **x != 0).cloned().collect::>()) - .unwrap() - .into_string() - .unwrap()) - { - view_directory( - &parsed_host, - parsed_port, - &parsed_selector, - 0 as libc::c_int, - ); - } else { - println!( - "invalid gopher URI: {}", - CString::new(bookmarks[i].iter().take_while(|x| **x != 0).cloned().collect::>()).unwrap().into_string().unwrap() - ); - } - return; - } - i += 1 - } - }; -} -pub fn pop_history() { - match unsafe { &history } { - None => println!("(empty history)"), - Some(h) => { - /* reload page from history (and don't count as history) */ - unsafe { view_directory( - &(*h).host, - (*h).port, - &(*h).selector, - 0 as libc::c_int, - ); } - /* history is history... :) */ - unsafe { history = h.next.clone(); } - }, - } -} -pub unsafe fn follow_link(mut key: usize) -> bool { - let mut link: Option> = links.clone(); - - while let Some(ref l) = link { - if (*l).key != key { - link = (*l).next.clone() - } else if let Some(w) = (*l).which { - match w { - '0' => { - view_file( - CString::new(config.cmd_text.clone()) - .unwrap() - .into_raw(), - &(*l).host, - (*l).port, - &(*l).selector, - ); - } - '1' => { - view_directory( - &(*l).host, - (*l).port, - &(*l).selector, - 1 as libc::c_int, - ); - } - '7' => { - view_search(&(*l).host, (*l).port, &(*l).selector); - } - '5' | '9' => { - view_download(&(*l).host, (*l).port, &(*l).selector); - } - '8' => { - view_telnet(&(*l).host, (*l).port); - } - 'f' | 'I' | 'p' => { - view_file( - CString::new(config.cmd_image.clone()) - .unwrap() - .into_raw(), - &(*l).host, - (*l).port, - &(*l).selector, - ); - } - 'h' => { - view_file( - CString::new(config.cmd_browser.clone()) - .unwrap() - .into_raw(), - &(*l).host, - (*l).port, - &(*l).selector, - ); - } - 's' => { - view_file( - CString::new(config.cmd_player.clone()) - .unwrap() - .into_raw(), - &(*l).host, - (*l).port, - &(*l).selector, - ); - } - _ => { - printf( - b"missing handler [%c]\n\x00" as *const u8 as *const libc::c_char, - w as u8 as libc::c_int, - ); - } - } - return true; - } - /* return the array is broken after view! */ - } - return false; -} -pub unsafe fn download_link(mut key: usize) { - let mut link: Option> = links.clone(); - while let Some(l) = link { - if (*l).key != key { - link = (*l).next - } else { - view_download(&(*l).host, (*l).port, &(*l).selector); - return; - } - } - println!("link not found"); + println!("(done)"); } -/* function prototypes */ -pub unsafe fn parse_uri(mut uri: String) -> bool { - let mut tmp: &str = &uri[..]; - /* strip gopher:// */ - if uri.starts_with("gopher://") { - tmp = &uri[9..]; - } - parsed_host = tmp.chars().take_while(|x| !(*x == ':' || *x == '/')).collect(); - if parsed_host.is_empty() { - return false - } - - if tmp.contains(':') { - let port_string: String = tmp.chars().skip_while(|x| *x != ':').skip(1).take_while(|x| *x != '/').collect(); - - if port_string.is_empty() { - parsed_port = 70; - } else { - parsed_port = match port_string.parse() { - Ok(p) => p, - Err(_) => { - eprintln!("failed to parse port"); - exit(1); - } - }; - } - } - tmp = &tmp[tmp.find('/').unwrap_or(0)..]; - /* parse selector (ignore slash and selector type) */ - if tmp.is_empty() || tmp.find('/').is_none() { - parsed_selector = "/".to_string(); - } else { - parsed_selector = tmp.into(); - } - true -} -unsafe fn main_0(mut argc: usize, mut argv: Vec) -> libc::c_int { - let mut i: usize = 0; - let mut line: [u8; 1024] = [0; 1024]; - let mut uri: String = String::new(); - /* copy defaults */ - init_config(); - uri = config.start_uri.clone(); - /* parse command line */ - i = 1; - while i < argc { - if argv[i].chars().next() == Some('-') - { - match argv[i].chars().skip(1).next() - { - Some('H') => { - usage(); - } - Some('v') => { - banner(true); - exit(0); - } - _ => { - usage(); - } - } - } else { - uri = argv[i].clone(); - } - i += 1 - } - /* parse uri */ - if !parse_uri(uri) { - banner(false); - eprintln!( - "invalid gopher URI: {}", - argv[i], - ); - exit(1); - } - /* main loop */ - view_directory( - &parsed_host, - parsed_port, - &parsed_selector, - 0 as libc::c_int, - ); /* to display the prompt */ - loop { - printf( - b"\x1b[%sm%s:%s%s\x1b[0m \x00" as *const u8 as *const libc::c_char, - CString::new(config.color_prompt.clone()).unwrap().into_raw(), - current_host.as_mut_ptr(), - current_port, - ¤t_selector, - ); - if read_line( - 0 as libc::c_int, - line.as_mut_ptr() as *mut i8, - mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong, - ) == 0 - { - puts(b"QUIT\x00" as *const u8 as *const libc::c_char); - return 0 as libc::c_int; - } - i = strlen(line.as_mut_ptr() as *mut i8) as usize; - match line[0 as libc::c_int as usize] as libc::c_int { - 63 => { - puts(b"? - help\n* - reload directory\n< - go back in history\n.[LINK] - download the given link\nH - show history\nH[LINK] - jump to the specified history item\nG[URI] - jump to the given gopher URI\nB - show bookmarks\nB[LINK] - jump to the specified bookmark item\nC^d - quit\x00" - as *const u8 as *const libc::c_char); - } - 60 => { - pop_history(); - } - 42 => { - view_directory( - &CString::new(current_host.iter().take_while(|x| **x != 0).map(|x| *x as u8).collect::>()).unwrap().into_string().unwrap(), - current_port, - ¤t_selector, - 0 as libc::c_int, - ); - } - 46 => { - download_link(make_key( - line[1] as char, - line[2] as char, - line[3] as char, - ).unwrap_or(0)); - } - 72 => { - if i == 1 || i == 3 || i == 4 - { - view_history(make_key( - line[1] as char, - line[2] as char, - line[3] as char, - ).unwrap_or(0)); - } - } - 71 => { - if parse_uri(CString::from_raw(&mut *line.as_mut_ptr().offset(1 as libc::c_int as isize) as *mut u8 as *mut i8).into_string().unwrap()) - { - view_directory( - &parsed_host, - parsed_port, - &parsed_selector, - 1 as libc::c_int, - ); - } else { - puts(b"invalid gopher URI\x00" as *const u8 as *const libc::c_char); - } - } - 66 => { - if i == 1 || i == 3 || i == 4 - { - view_bookmarks(make_key( - line[1] as char, - line[2] as char, - line[3] as char, - ).unwrap_or(0)); - } - } - _ => { - follow_link(make_key( - line[0] as char, - line[1] as char, - line[2] as char, - ).unwrap_or(0)); - } - } - } - /* never get's here but stops cc complaining */ -} #[main] pub fn main() { + let mut state = BrowserState::new(); let mut args: Vec = env::args().collect(); - unsafe { - exit(main_0( - args.len() - 1, - args, - ) as i32) - } + unsafe { exit(state.init(args.len() - 1, args) as i32) } }