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

#![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,
&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
{
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,
&current_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)
}
}