You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1436 lines
38 KiB
Rust
1436 lines
38 KiB
Rust
#![allow(
|
|
dead_code,
|
|
mutable_transmutes,
|
|
non_camel_case_types,
|
|
non_snake_case,
|
|
non_upper_case_globals,
|
|
unused_assignments,
|
|
unused_mut
|
|
)]
|
|
#![register_tool(c2rust)]
|
|
#![feature(const_raw_ptr_to_usize_cast, extern_types, main, register_tool)]
|
|
|
|
extern crate libc;
|
|
extern crate tempfile;
|
|
extern crate simple_input;
|
|
#[macro_use] extern crate lazy_static;
|
|
|
|
use std::env;
|
|
use std::path::Path;
|
|
use std::process::exit;
|
|
use std::thread::sleep;
|
|
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;
|
|
use tempfile::NamedTempFile;
|
|
|
|
extern "C" {
|
|
pub type _IO_wide_data;
|
|
pub type _IO_codecvt;
|
|
pub type _IO_marker;
|
|
#[no_mangle]
|
|
fn waitpid(
|
|
__pid: __pid_t,
|
|
__stat_loc: *mut libc::c_int,
|
|
__options: libc::c_int,
|
|
) -> __pid_t;
|
|
#[no_mangle]
|
|
fn socket(
|
|
__domain: libc::c_int,
|
|
__type: libc::c_int,
|
|
__protocol: libc::c_int,
|
|
) -> libc::c_int;
|
|
#[no_mangle]
|
|
fn connect(
|
|
__fd: libc::c_int,
|
|
__addr: *const sockaddr,
|
|
__len: socklen_t,
|
|
) -> libc::c_int;
|
|
#[no_mangle]
|
|
fn getaddrinfo(
|
|
__name: *const libc::c_char,
|
|
__service: *const libc::c_char,
|
|
__req: *const addrinfo,
|
|
__pai: *mut *mut addrinfo,
|
|
) -> libc::c_int;
|
|
#[no_mangle]
|
|
fn freeaddrinfo(__ai: *mut addrinfo);
|
|
#[no_mangle]
|
|
fn open(__file: *const libc::c_char, __oflag: libc::c_int, _: ...) -> libc::c_int;
|
|
#[no_mangle]
|
|
fn close(__fd: libc::c_int) -> libc::c_int;
|
|
#[no_mangle]
|
|
fn read(__fd: libc::c_int, __buf: *mut libc::c_void, __nbytes: size_t) -> ssize_t;
|
|
#[no_mangle]
|
|
fn write(__fd: libc::c_int, __buf: *const libc::c_void, __n: size_t) -> ssize_t;
|
|
#[no_mangle]
|
|
fn execvp(
|
|
__file: *const libc::c_char,
|
|
__argv: *const *mut libc::c_char,
|
|
) -> libc::c_int;
|
|
#[no_mangle]
|
|
fn execlp(
|
|
__file: *const libc::c_char,
|
|
__arg: *const libc::c_char,
|
|
_: ...
|
|
) -> libc::c_int;
|
|
#[no_mangle]
|
|
fn fork() -> __pid_t;
|
|
#[no_mangle]
|
|
fn unlink(__name: *const libc::c_char) -> libc::c_int;
|
|
#[no_mangle]
|
|
fn __errno_location() -> *mut libc::c_int;
|
|
#[no_mangle]
|
|
static mut stderr: *mut FILE;
|
|
#[no_mangle]
|
|
fn fclose(__stream: *mut FILE) -> libc::c_int;
|
|
#[no_mangle]
|
|
fn fopen(_: *const libc::c_char, _: *const libc::c_char) -> *mut FILE;
|
|
#[no_mangle]
|
|
fn fprintf(_: *mut FILE, _: *const libc::c_char, _: ...) -> libc::c_int;
|
|
#[no_mangle]
|
|
fn printf(_: *const libc::c_char, _: ...) -> libc::c_int;
|
|
#[no_mangle]
|
|
fn snprintf(
|
|
_: *mut libc::c_char,
|
|
_: libc::c_ulong,
|
|
_: *const libc::c_char,
|
|
_: ...
|
|
) -> libc::c_int;
|
|
#[no_mangle]
|
|
fn fgetc(__stream: *mut FILE) -> libc::c_int;
|
|
#[no_mangle]
|
|
fn fputs(__s: *const libc::c_char, __stream: *mut FILE) -> libc::c_int;
|
|
#[no_mangle]
|
|
fn puts(__s: *const libc::c_char) -> libc::c_int;
|
|
#[no_mangle]
|
|
fn calloc(_: libc::c_ulong, _: libc::c_ulong) -> *mut libc::c_void;
|
|
#[no_mangle]
|
|
fn free(__ptr: *mut libc::c_void);
|
|
#[no_mangle]
|
|
fn getenv(__name: *const libc::c_char) -> *mut libc::c_char;
|
|
#[no_mangle]
|
|
fn mkstemp(__template: *mut libc::c_char) -> libc::c_int;
|
|
#[no_mangle]
|
|
fn memset(
|
|
_: *mut libc::c_void,
|
|
_: libc::c_int,
|
|
_: libc::c_ulong,
|
|
) -> *mut libc::c_void;
|
|
#[no_mangle]
|
|
fn strcpy(_: *mut libc::c_char, _: *const libc::c_char) -> *mut libc::c_char;
|
|
#[no_mangle]
|
|
fn strcmp(_: *const libc::c_char, _: *const libc::c_char) -> libc::c_int;
|
|
#[no_mangle]
|
|
fn strncmp(
|
|
_: *const libc::c_char,
|
|
_: *const libc::c_char,
|
|
_: libc::c_ulong,
|
|
) -> libc::c_int;
|
|
#[no_mangle]
|
|
fn strdup(_: *const libc::c_char) -> *mut libc::c_char;
|
|
#[no_mangle]
|
|
fn strrchr(_: *const libc::c_char, _: libc::c_int) -> *mut libc::c_char;
|
|
#[no_mangle]
|
|
fn strcasecmp(_: *const libc::c_char, _: *const libc::c_char) -> libc::c_int;
|
|
#[no_mangle]
|
|
fn strlen(_: *const libc::c_char) -> libc::c_ulong;
|
|
#[no_mangle]
|
|
fn strerror(_: libc::c_int) -> *mut libc::c_char;
|
|
}
|
|
pub type __off_t = libc::c_long;
|
|
pub type __off64_t = libc::c_long;
|
|
pub type __pid_t = libc::c_int;
|
|
pub type __ssize_t = libc::c_long;
|
|
pub type __socklen_t = libc::c_uint;
|
|
pub type pid_t = __pid_t;
|
|
pub type ssize_t = __ssize_t;
|
|
pub type size_t = libc::c_ulong;
|
|
pub type socklen_t = __socklen_t;
|
|
pub type __socket_type = libc::c_uint;
|
|
pub const SOCK_NONBLOCK: __socket_type = 2048;
|
|
pub const SOCK_CLOEXEC: __socket_type = 524288;
|
|
pub const SOCK_PACKET: __socket_type = 10;
|
|
pub const SOCK_DCCP: __socket_type = 6;
|
|
pub const SOCK_SEQPACKET: __socket_type = 5;
|
|
pub const SOCK_RDM: __socket_type = 4;
|
|
pub const SOCK_RAW: __socket_type = 3;
|
|
pub const SOCK_DGRAM: __socket_type = 2;
|
|
pub const SOCK_STREAM: __socket_type = 1;
|
|
pub type sa_family_t = libc::c_ushort;
|
|
#[derive(Copy, Clone)]
|
|
#[repr(C)]
|
|
pub struct sockaddr {
|
|
pub sa_family: sa_family_t,
|
|
pub sa_data: [libc::c_char; 14],
|
|
}
|
|
#[derive(Copy, Clone)]
|
|
#[repr(C)]
|
|
pub struct addrinfo {
|
|
pub ai_flags: libc::c_int,
|
|
pub ai_family: libc::c_int,
|
|
pub ai_socktype: libc::c_int,
|
|
pub ai_protocol: libc::c_int,
|
|
pub ai_addrlen: socklen_t,
|
|
pub ai_addr: *mut sockaddr,
|
|
pub ai_canonname: *mut libc::c_char,
|
|
pub ai_next: *mut addrinfo,
|
|
}
|
|
#[derive(Copy, Clone)]
|
|
#[repr(C)]
|
|
pub struct _IO_FILE {
|
|
pub _flags: libc::c_int,
|
|
pub _IO_read_ptr: *mut libc::c_char,
|
|
pub _IO_read_end: *mut libc::c_char,
|
|
pub _IO_read_base: *mut libc::c_char,
|
|
pub _IO_write_base: *mut libc::c_char,
|
|
pub _IO_write_ptr: *mut libc::c_char,
|
|
pub _IO_write_end: *mut libc::c_char,
|
|
pub _IO_buf_base: *mut libc::c_char,
|
|
pub _IO_buf_end: *mut libc::c_char,
|
|
pub _IO_save_base: *mut libc::c_char,
|
|
pub _IO_backup_base: *mut libc::c_char,
|
|
pub _IO_save_end: *mut libc::c_char,
|
|
pub _markers: *mut _IO_marker,
|
|
pub _chain: *mut _IO_FILE,
|
|
pub _fileno: libc::c_int,
|
|
pub _flags2: libc::c_int,
|
|
pub _old_offset: __off_t,
|
|
pub _cur_column: libc::c_ushort,
|
|
pub _vtable_offset: libc::c_schar,
|
|
pub _shortbuf: [libc::c_char; 1],
|
|
pub _lock: *mut libc::c_void,
|
|
pub _offset: __off64_t,
|
|
pub _codecvt: *mut _IO_codecvt,
|
|
pub _wide_data: *mut _IO_wide_data,
|
|
pub _freeres_list: *mut _IO_FILE,
|
|
pub _freeres_buf: *mut libc::c_void,
|
|
pub __pad5: size_t,
|
|
pub _mode: libc::c_int,
|
|
pub _unused2: [libc::c_char; 20],
|
|
}
|
|
pub type _IO_lock_t = ();
|
|
pub type FILE = _IO_FILE;
|
|
|
|
/* structs */
|
|
#[derive(Clone, Debug)]
|
|
pub struct Link {
|
|
pub next: Option<Box<Link>>,
|
|
pub which: Option<char>,
|
|
pub key: usize,
|
|
pub host: String,
|
|
pub port: usize,
|
|
pub selector: String,
|
|
}
|
|
#[derive(Clone, Debug)]
|
|
pub struct Config {
|
|
pub start_uri: String,
|
|
pub cmd_text: String,
|
|
pub cmd_image: String,
|
|
pub cmd_browser: String,
|
|
pub cmd_player: String,
|
|
pub color_prompt: String,
|
|
pub color_selector: String,
|
|
pub verbose: String,
|
|
}
|
|
|
|
pub static mut tmpfilename: String = String::new();
|
|
pub static mut links: Option<Box<Link>> = None;
|
|
pub static mut history: Option<Box<Link>> = 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"),
|
|
};
|
|
}
|
|
|
|
/* 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)
|
|
}
|
|
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(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 {
|
|
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(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)
|
|
}
|
|
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;
|
|
}
|
|
#[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;
|
|
}
|
|
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;
|
|
}
|
|
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,
|
|
);
|
|
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 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
|
|
}
|
|
/* 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() {
|
|
snprintf(
|
|
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,
|
|
);
|
|
load_config(filename.as_mut_ptr());
|
|
};
|
|
}
|
|
pub unsafe extern "C" 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,
|
|
ai_socktype: 0,
|
|
ai_protocol: 0,
|
|
ai_addrlen: 0,
|
|
ai_addr: 0 as *mut sockaddr,
|
|
ai_canonname: 0 as *mut libc::c_char,
|
|
ai_next: 0 as *mut addrinfo,
|
|
};
|
|
let mut res: *mut addrinfo = 0 as *mut addrinfo;
|
|
let mut r: *mut addrinfo = 0 as *mut addrinfo;
|
|
let mut srv: libc::c_int = -(1 as libc::c_int);
|
|
let mut l: libc::c_int = 0;
|
|
let mut request: [libc::c_char; 512] = [0; 512];
|
|
memset(
|
|
&mut hints as *mut addrinfo as *mut libc::c_void,
|
|
0 as libc::c_int,
|
|
mem::size_of::<addrinfo>() as libc::c_ulong,
|
|
);
|
|
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 {
|
|
eprintln!(
|
|
"error: cannot resolve hostname '{}:{}': {}",
|
|
host,
|
|
port,
|
|
CStr::from_ptr(strerror(*__errno_location())).to_str().unwrap()
|
|
);
|
|
return -(1 as libc::c_int);
|
|
}
|
|
r = res;
|
|
while !r.is_null() {
|
|
srv = socket((*r).ai_family, (*r).ai_socktype, (*r).ai_protocol);
|
|
if !(srv == -(1 as libc::c_int)) {
|
|
if connect(srv, (*r).ai_addr, (*r).ai_addrlen) == 0 as libc::c_int {
|
|
break;
|
|
}
|
|
close(srv);
|
|
}
|
|
r = (*r).ai_next
|
|
}
|
|
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,
|
|
);
|
|
return -(1 as libc::c_int);
|
|
}
|
|
snprintf(
|
|
request.as_mut_ptr(),
|
|
mem::size_of::<[libc::c_char; 512]>() as libc::c_ulong,
|
|
b"%s\r\n\x00" as *const u8 as *const libc::c_char,
|
|
selector,
|
|
);
|
|
l = strlen(request.as_mut_ptr()) as libc::c_int;
|
|
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,
|
|
);
|
|
close(srv);
|
|
return -(1 as libc::c_int);
|
|
}
|
|
return srv;
|
|
}
|
|
pub unsafe extern "C" fn read_line(
|
|
mut fd: libc::c_int,
|
|
mut buf: *mut libc::c_char,
|
|
mut buf_len: size_t,
|
|
) -> libc::c_int {
|
|
let mut i: size_t = 0 as libc::c_int as size_t;
|
|
let mut c: libc::c_char = 0 as libc::c_int as libc::c_char;
|
|
loop {
|
|
if read(
|
|
fd,
|
|
&mut c as *mut libc::c_char as *mut libc::c_void,
|
|
mem::size_of::<libc::c_char>() as libc::c_ulong,
|
|
) as libc::c_ulong
|
|
!= mem::size_of::<libc::c_char>() as libc::c_ulong
|
|
{
|
|
return 0 as libc::c_int;
|
|
}
|
|
if c as libc::c_int != '\r' as i32 {
|
|
let fresh3 = i;
|
|
i = i.wrapping_add(1);
|
|
*buf.offset(fresh3 as isize) = c
|
|
}
|
|
if !(c as libc::c_int != '\n' as i32 && i < buf_len) {
|
|
break;
|
|
}
|
|
}
|
|
*buf.offset(i.wrapping_sub(1 as libc::c_int as libc::c_ulong) as isize) =
|
|
'\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<usize> {
|
|
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
|
|
)
|
|
} 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)
|
|
)
|
|
}
|
|
}
|
|
pub unsafe extern "C" fn make_key_str(
|
|
mut key: usize,
|
|
mut c1: &mut char,
|
|
mut c2: &mut char,
|
|
mut c3: &mut char,
|
|
) {
|
|
if key
|
|
< ('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;
|
|
*c3 = 0 as usize as u8 as char
|
|
} else {
|
|
*c1 = ('a' as usize
|
|
+ key
|
|
/ (('z' as usize - 'a' as usize + 1 as usize)
|
|
* ('z' as usize - 'a' as usize + 1 as usize))
|
|
- 1 as usize) as u8 as char;
|
|
*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
|
|
};
|
|
}
|
|
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::<Vec<_>>()).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 {
|
|
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 */
|
|
return 1 as libc::c_int;
|
|
}
|
|
_ => 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::<Vec<_>>()).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,
|
|
) {
|
|
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,
|
|
);
|
|
pid = fork();
|
|
if pid == 0 as libc::c_int {
|
|
if execlp(
|
|
b"telnet\x00" as *const u8 as *const libc::c_char,
|
|
b"telnet\x00" as *const u8 as *const libc::c_char,
|
|
host,
|
|
port,
|
|
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);
|
|
}
|
|
} else if pid == -(1 as libc::c_int) {
|
|
puts(b"error: fork() failed!\x00" as *const u8 as *const libc::c_char);
|
|
}
|
|
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<Box<Link>> = 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::<Vec<_>>()).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::<Vec<_>>())
|
|
.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::<Vec<_>>()).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<Box<Link>> = 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<Box<Link>> = 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");
|
|
}
|
|
/* 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<String>) -> 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::<Vec<_>>()).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 args: Vec<String> = env::args().collect();
|
|
unsafe {
|
|
exit(main_0(
|
|
args.len() - 1,
|
|
args,
|
|
) as i32)
|
|
}
|
|
}
|