From 95d4df60278d942406eed4a2a91d63b2f470a0fe Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Thu, 30 May 2019 21:39:20 +0200 Subject: [PATCH 1/5] refactor: reduce dependencies on libc --- examples/repl/main.rs | 6 ++-- src/dc_array.rs | 9 ++---- src/dc_chatlist.rs | 9 ++---- src/dc_configure.rs | 4 +-- src/dc_contact.rs | 5 ++- src/dc_loginparam.rs | 4 +-- src/dc_lot.rs | 5 ++- src/dc_mimeparser.rs | 6 ++-- src/dc_msg.rs | 4 +-- src/dc_param.rs | 6 ++-- src/dc_simplify.rs | 4 +-- src/dc_sqlite3.rs | 2 +- src/dc_strbuilder.rs | 8 ++--- src/dc_strencode.rs | 38 +++++++++++----------- src/dc_tools.rs | 73 +++++++++++++++---------------------------- src/types.rs | 42 ++++++------------------- src/x.rs | 22 ++----------- 17 files changed, 80 insertions(+), 167 deletions(-) diff --git a/examples/repl/main.rs b/examples/repl/main.rs index 465a05c3d..41fc28126 100644 --- a/examples/repl/main.rs +++ b/examples/repl/main.rs @@ -320,10 +320,8 @@ unsafe fn main_0(argc: libc::c_int, argv: *mut *mut libc::c_char) -> libc::c_int || strcmp(cmd, b"getbadqr\x00" as *const u8 as *const libc::c_char) == 0i32 { handles = Some(start_threads(ctx.clone())); - let qrstr: *mut libc::c_char = dc_get_securejoin_qr( - &ctx.read().unwrap(), - (if !arg1.is_null() { atoi(arg1) } else { 0i32 }) as uint32_t, - ); + let qrstr: *mut libc::c_char = + dc_get_securejoin_qr(&ctx.read().unwrap(), dc_atoi_null_is_0(arg1) as u32); if !qrstr.is_null() && 0 != *qrstr.offset(0isize) as libc::c_int { if strcmp(cmd, b"getbadqr\x00" as *const u8 as *const libc::c_char) == 0i32 && strlen(qrstr) > 40 diff --git a/src/dc_array.rs b/src/dc_array.rs index d50a54813..95c7bf0ad 100644 --- a/src/dc_array.rs +++ b/src/dc_array.rs @@ -62,9 +62,7 @@ pub unsafe fn dc_array_add_uint(mut array: *mut dc_array_t, item: uintptr_t) { (*array).array as *mut libc::c_void, (newsize).wrapping_mul(::std::mem::size_of::()), ) as *mut uintptr_t; - if (*array).array.is_null() { - exit(49i32); - } + assert!(!(*array).array.is_null()); (*array).allocated = newsize as size_t } *(*array).array.offset((*array).count as isize) = item; @@ -266,9 +264,8 @@ pub unsafe fn dc_array_new(initsize: size_t) -> *mut dc_array_t { pub unsafe fn dc_array_new_typed(type_0: libc::c_int, initsize: size_t) -> *mut dc_array_t { let mut array: *mut dc_array_t; array = calloc(1, ::std::mem::size_of::()) as *mut dc_array_t; - if array.is_null() { - exit(47i32); - } + assert!(!array.is_null()); + (*array).magic = 0xa11aai32 as uint32_t; (*array).count = 0i32 as size_t; (*array).allocated = if initsize < 1 { 1 } else { initsize }; diff --git a/src/dc_chatlist.rs b/src/dc_chatlist.rs index 0332bb9e7..420e00b54 100644 --- a/src/dc_chatlist.rs +++ b/src/dc_chatlist.rs @@ -83,15 +83,12 @@ pub unsafe fn dc_get_chatlist<'a>( pub unsafe fn dc_chatlist_new(context: &Context) -> *mut dc_chatlist_t { let mut chatlist: *mut dc_chatlist_t; chatlist = calloc(1, ::std::mem::size_of::()) as *mut dc_chatlist_t; - if chatlist.is_null() { - exit(20i32); - } + assert!(!chatlist.is_null()); + (*chatlist).magic = 0xc4a71157u32; (*chatlist).context = context; (*chatlist).chatNlastmsg_ids = dc_array_new(128i32 as size_t); - if (*chatlist).chatNlastmsg_ids.is_null() { - exit(32i32); - } + assert!(!(*chatlist).chatNlastmsg_ids.is_null()); chatlist } diff --git a/src/dc_configure.rs b/src/dc_configure.rs index c59c5e2bd..374e39a09 100644 --- a/src/dc_configure.rs +++ b/src/dc_configure.rs @@ -1406,7 +1406,7 @@ unsafe fn moz_autoconfigure_text_cb( (*(*moz_ac).out).mail_server = val; val = 0 as *mut libc::c_char } - 11 => (*(*moz_ac).out).mail_port = atoi(val), + 11 => (*(*moz_ac).out).mail_port = dc_atoi_null_is_0(val), 12 => { free((*(*moz_ac).out).mail_user as *mut libc::c_void); (*(*moz_ac).out).mail_user = val; @@ -1432,7 +1432,7 @@ unsafe fn moz_autoconfigure_text_cb( (*(*moz_ac).out).send_server = val; val = 0 as *mut libc::c_char } - 11 => (*(*moz_ac).out).send_port = atoi(val), + 11 => (*(*moz_ac).out).send_port = to_str(val).parse().unwrap_or_default(), 12 => { free((*(*moz_ac).out).send_user as *mut libc::c_void); (*(*moz_ac).out).send_user = val; diff --git a/src/dc_contact.rs b/src/dc_contact.rs index b4a69e528..ed7355abf 100644 --- a/src/dc_contact.rs +++ b/src/dc_contact.rs @@ -240,9 +240,8 @@ pub unsafe fn dc_block_contact(context: &Context, contact_id: uint32_t, new_bloc pub unsafe fn dc_contact_new<'a>(context: &'a Context) -> *mut dc_contact_t<'a> { let mut contact: *mut dc_contact_t; contact = calloc(1, ::std::mem::size_of::()) as *mut dc_contact_t; - if contact.is_null() { - exit(19i32); - } + assert!(!contact.is_null()); + (*contact).magic = 0xc047ac7i32 as uint32_t; (*contact).context = context; diff --git a/src/dc_loginparam.rs b/src/dc_loginparam.rs index 3475f2499..3f18a23ec 100644 --- a/src/dc_loginparam.rs +++ b/src/dc_loginparam.rs @@ -23,9 +23,7 @@ pub struct dc_loginparam_t { pub unsafe fn dc_loginparam_new() -> *mut dc_loginparam_t { let loginparam: *mut dc_loginparam_t; loginparam = calloc(1, ::std::mem::size_of::()) as *mut dc_loginparam_t; - if loginparam.is_null() { - exit(22i32); - } + assert!(!loginparam.is_null()); loginparam } diff --git a/src/dc_lot.rs b/src/dc_lot.rs index c913258e1..2903a385c 100644 --- a/src/dc_lot.rs +++ b/src/dc_lot.rs @@ -36,9 +36,8 @@ pub struct dc_lot_t { pub unsafe fn dc_lot_new() -> *mut dc_lot_t { let mut lot: *mut dc_lot_t; lot = calloc(1, ::std::mem::size_of::()) as *mut dc_lot_t; - if lot.is_null() { - exit(27i32); - } + assert!(!lot.is_null()); + (*lot).magic = 0x107107i32 as uint32_t; (*lot).text1_meaning = 0i32; diff --git a/src/dc_mimeparser.rs b/src/dc_mimeparser.rs index a21bca76a..59e05a3d0 100644 --- a/src/dc_mimeparser.rs +++ b/src/dc_mimeparser.rs @@ -360,7 +360,7 @@ pub unsafe fn dc_mimeparser_parse( b"Chat-Duration\x00" as *const u8 as *const libc::c_char, ); if !field_0.is_null() { - let duration_ms: libc::c_int = atoi((*field_0).fld_value); + let duration_ms: libc::c_int = dc_atoi_null_is_0((*field_0).fld_value); if duration_ms > 0i32 && duration_ms < 24i32 * 60i32 * 60i32 * 1000i32 { dc_param_set_int((*part_3).param, 'd' as i32, duration_ms); } @@ -439,9 +439,7 @@ pub unsafe fn dc_mimeparser_parse( unsafe fn dc_mimepart_new() -> *mut dc_mimepart_t { let mut mimepart: *mut dc_mimepart_t; mimepart = calloc(1, ::std::mem::size_of::()) as *mut dc_mimepart_t; - if mimepart.is_null() { - exit(33i32); - } + assert!(!mimepart.is_null()); (*mimepart).type_0 = 0i32; (*mimepart).param = dc_param_new(); mimepart diff --git a/src/dc_msg.rs b/src/dc_msg.rs index da0a071cf..f8f45d427 100644 --- a/src/dc_msg.rs +++ b/src/dc_msg.rs @@ -302,9 +302,7 @@ pub unsafe fn dc_msg_new_untyped<'a>(context: &'a Context) -> *mut dc_msg_t<'a> pub unsafe fn dc_msg_new<'a>(context: &'a Context, viewtype: libc::c_int) -> *mut dc_msg_t<'a> { let mut msg: *mut dc_msg_t; msg = calloc(1, ::std::mem::size_of::()) as *mut dc_msg_t; - if msg.is_null() { - exit(15i32); - } + assert!(!msg.is_null()); (*msg).context = context; (*msg).magic = 0x11561156i32 as uint32_t; (*msg).type_0 = viewtype; diff --git a/src/dc_param.rs b/src/dc_param.rs index cb6aa5a5a..7b2821397 100644 --- a/src/dc_param.rs +++ b/src/dc_param.rs @@ -164,7 +164,7 @@ pub unsafe fn dc_param_get_int( if str.is_null() { return def; } - let ret: int32_t = atol(str) as int32_t; + let ret: int32_t = to_str(str).parse().unwrap_or_default(); free(str as *mut libc::c_void); ret @@ -300,9 +300,7 @@ pub unsafe fn dc_param_set_int(param: *mut dc_param_t, key: libc::c_int, value: pub unsafe fn dc_param_new() -> *mut dc_param_t { let mut param: *mut dc_param_t; param = calloc(1, ::std::mem::size_of::()) as *mut dc_param_t; - if param.is_null() { - exit(28i32); - } + assert!(!param.is_null()); (*param).packed = calloc(1, 1) as *mut libc::c_char; param diff --git a/src/dc_simplify.rs b/src/dc_simplify.rs index 9d70afe84..9ecc2329e 100644 --- a/src/dc_simplify.rs +++ b/src/dc_simplify.rs @@ -15,9 +15,7 @@ pub struct dc_simplify_t { pub unsafe fn dc_simplify_new() -> *mut dc_simplify_t { let simplify: *mut dc_simplify_t; simplify = calloc(1, ::std::mem::size_of::()) as *mut dc_simplify_t; - if simplify.is_null() { - exit(31i32); - } + assert!(!simplify.is_null()); simplify } diff --git a/src/dc_sqlite3.rs b/src/dc_sqlite3.rs index f4df6b12c..0b1b27d9e 100644 --- a/src/dc_sqlite3.rs +++ b/src/dc_sqlite3.rs @@ -1166,7 +1166,7 @@ pub unsafe fn dc_sqlite3_get_config_int( if str.is_null() { return def; } - let ret = atoi(str) as int32_t; + let ret = dc_atoi_null_is_0(str); free(str as *mut libc::c_void); ret } diff --git a/src/dc_strbuilder.rs b/src/dc_strbuilder.rs index a5a460393..9881cd371 100644 --- a/src/dc_strbuilder.rs +++ b/src/dc_strbuilder.rs @@ -19,9 +19,7 @@ pub unsafe fn dc_strbuilder_init(mut strbuilder: *mut dc_strbuilder_t, init_byte 128i32 }; (*strbuilder).buf = malloc((*strbuilder).allocated as usize) as *mut libc::c_char; - if (*strbuilder).buf.is_null() { - exit(38i32); - } + assert!(!(*strbuilder).buf.is_null()); *(*strbuilder).buf.offset(0isize) = 0i32 as libc::c_char; (*strbuilder).free = (*strbuilder).allocated - 1i32; (*strbuilder).eos = (*strbuilder).buf; @@ -47,9 +45,7 @@ pub unsafe fn dc_strbuilder_cat( (*strbuilder).buf as *mut libc::c_void, ((*strbuilder).allocated + add_bytes) as usize, ) as *mut libc::c_char; - if (*strbuilder).buf.is_null() { - exit(39i32); - } + assert!(!(*strbuilder).buf.is_null()); (*strbuilder).free = (*strbuilder).free + add_bytes; (*strbuilder).eos = (*strbuilder).buf.offset(old_offset as isize) } diff --git a/src/dc_strencode.rs b/src/dc_strencode.rs index 66f3ffb9f..bf9b962ef 100644 --- a/src/dc_strencode.rs +++ b/src/dc_strencode.rs @@ -10,18 +10,9 @@ use crate::types::*; use crate::x::*; #[inline] -pub fn isalnum(mut _c: libc::c_int) -> libc::c_int { - if _c < std::u8::MAX as libc::c_int { - (_c as u8 as char).is_ascii_alphanumeric() as libc::c_int - } else { - 0 - } -} - -#[inline] -pub fn isdigit(mut _c: libc::c_int) -> libc::c_int { - if _c < std::u8::MAX as libc::c_int { - (_c as u8 as char).is_ascii_digit() as libc::c_int +pub fn isalnum(c: libc::c_int) -> libc::c_int { + if c < std::u8::MAX as libc::c_int { + (c as u8 as char).is_ascii_alphanumeric() as libc::c_int } else { 0 } @@ -116,12 +107,16 @@ pub unsafe fn dc_urldecode(to_decode: *const libc::c_char) -> *mut libc::c_char buf } -unsafe fn hex_2_int(ch: libc::c_char) -> libc::c_char { - return (if 0 != isdigit(ch as libc::c_int) { - ch as libc::c_int - '0' as i32 - } else { - tolower(ch as libc::c_int) - 'a' as i32 + 10i32 - }) as libc::c_char; +fn hex_2_int(ch: libc::c_char) -> libc::c_char { + let ch = ch as u8 as char; + if !ch.is_ascii_hexdigit() { + return (ch.to_ascii_lowercase() as i32 - 'a' as i32 + 10) as libc::c_char; + } + + match ch.to_digit(16) { + Some(res) => res as libc::c_char, + None => 0, + } } pub unsafe fn dc_encode_header_words(to_encode: *const libc::c_char) -> *mut libc::c_char { @@ -956,4 +951,11 @@ mod tests { free(buf2 as *mut libc::c_void); } } + #[test] + fn test_hex_to_int() { + assert_eq!(hex_2_int(b'A' as libc::c_char), 10); + assert_eq!(hex_2_int(b'a' as libc::c_char), 10); + assert_eq!(hex_2_int(b'4' as libc::c_char), 4); + assert_eq!(hex_2_int(b'K' as libc::c_char), 20); + } } diff --git a/src/dc_tools.rs b/src/dc_tools.rs index 9acf9e8b7..7ed8a0f3d 100644 --- a/src/dc_tools.rs +++ b/src/dc_tools.rs @@ -27,14 +27,10 @@ pub unsafe fn dc_strdup(s: *const libc::c_char) -> *mut libc::c_char { let ret: *mut libc::c_char; if !s.is_null() { ret = strdup(s); - if ret.is_null() { - exit(16i32); - } + assert!(!ret.is_null()); } else { ret = calloc(1, 1) as *mut libc::c_char; - if ret.is_null() { - exit(17i32); - } + assert!(!ret.is_null()); } ret @@ -50,24 +46,19 @@ pub unsafe fn dc_strdup_keep_null(s: *const libc::c_char) -> *mut libc::c_char { } pub unsafe fn dc_atoi_null_is_0(s: *const libc::c_char) -> libc::c_int { - return if !s.is_null() { atoi(s) } else { 0i32 }; + if !s.is_null() { + to_str(s).parse().unwrap_or_default() + } else { + 0 + } } -pub unsafe fn dc_atof(str: *const libc::c_char) -> libc::c_double { - // hack around atof() that may accept only `,` as decimal point on mac - let test: *mut libc::c_char = dc_mprintf(b"%f\x00" as *const u8 as *const libc::c_char, 1.2f64); - *test.offset(2isize) = 0i32 as libc::c_char; - let mut str_locale: *mut libc::c_char = dc_strdup(str); - dc_str_replace( - &mut str_locale, - b".\x00" as *const u8 as *const libc::c_char, - test.offset(1isize), - ); - let f: libc::c_double = atof(str_locale); - free(test as *mut libc::c_void); - free(str_locale as *mut libc::c_void); +pub fn dc_atof(s: *const libc::c_char) -> libc::c_double { + if s.is_null() { + return 0.; + } - f + to_str(s).parse().unwrap_or_default() } pub unsafe fn dc_str_replace( @@ -184,22 +175,14 @@ pub unsafe fn dc_trim(buf: *mut libc::c_char) { /* the result must be free()'d */ pub unsafe fn dc_strlower(in_0: *const libc::c_char) -> *mut libc::c_char { - let out: *mut libc::c_char = dc_strdup(in_0); - let mut p: *mut libc::c_char = out; - while 0 != *p { - *p = tolower(*p as libc::c_int) as libc::c_char; - p = p.offset(1isize) - } - - out + let raw = to_cstring(to_string(in_0).to_lowercase()); + strdup(raw.as_ptr()) } pub unsafe fn dc_strlower_in_place(in_0: *mut libc::c_char) { - let mut p: *mut libc::c_char = in_0; - while 0 != *p { - *p = tolower(*p as libc::c_int) as libc::c_char; - p = p.offset(1isize) - } + let raw = to_cstring(to_string(in_0).to_lowercase()); + assert_eq!(strlen(in_0), strlen(raw.as_ptr())); + memcpy(in_0 as *mut _, raw.as_ptr() as *const _, strlen(in_0)); } pub unsafe fn dc_str_contains( @@ -231,9 +214,7 @@ pub unsafe fn dc_null_terminate( bytes: libc::c_int, ) -> *mut libc::c_char { let out: *mut libc::c_char = malloc(bytes as usize + 1) as *mut libc::c_char; - if out.is_null() { - exit(45i32); - } + assert!(!out.is_null()); if !in_0.is_null() && bytes > 0i32 { strncpy(out, in_0, bytes as usize); } @@ -566,9 +547,8 @@ pub unsafe fn dc_str_to_clist( delimiter: *const libc::c_char, ) -> *mut clist { let list: *mut clist = clist_new(); - if list.is_null() { - exit(54i32); - } + assert!(!list.is_null()); + if !str.is_null() && !delimiter.is_null() && strlen(delimiter) >= 1 { let mut p1: *const libc::c_char = str; loop { @@ -779,9 +759,8 @@ unsafe fn encode_66bits_as_base64(v1: uint32_t, v2: uint32_t, fill: uint32_t) -> hex: 64 bit, 4 bits/character, length = 64/4 = 16 characters base64: 64 bit, 6 bits/character, length = 64/6 = 11 characters (plus 2 additional bits) */ let ret: *mut libc::c_char = malloc(12) as *mut libc::c_char; - if ret.is_null() { - exit(34i32); - } + assert!(!ret.is_null()); + static mut chars: [libc::c_char; 65] = [ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, @@ -1554,11 +1533,9 @@ mod tests { #[test] fn test_dc_atof() { - unsafe { - let f: libc::c_double = dc_atof(b"1.23\x00" as *const u8 as *const libc::c_char); - assert!(f > 1.22f64); - assert!(f < 1.24f64); - } + let f: libc::c_double = dc_atof(b"1.23\x00" as *const u8 as *const libc::c_char); + assert!(f > 1.22f64); + assert!(f < 1.24f64); } #[test] diff --git a/src/types.rs b/src/types.rs index 9619dc8ea..364e60c85 100644 --- a/src/types.rs +++ b/src/types.rs @@ -5,32 +5,14 @@ pub use libsqlite3_sys::*; pub use mmime::carray::*; pub use mmime::clist::*; -pub type __builtin_va_list = [__va_list_tag; 1]; -#[derive(Copy, Clone)] -#[repr(C)] -pub struct __va_list_tag { - pub gp_offset: libc::c_uint, - pub fp_offset: libc::c_uint, - pub overflow_arg_area: *mut libc::c_void, - pub reg_save_area: *mut libc::c_void, -} -pub type va_list = __builtin_va_list; -pub type __int64_t = libc::c_longlong; -pub type __darwin_ct_rune_t = libc::c_int; -pub type __darwin_wchar_t = libc::c_int; -pub type __darwin_rune_t = __darwin_wchar_t; -pub type uint64_t = libc::c_ulonglong; - -/** - * Callback function that should be given to dc_context_new(). - * - * @memberof Context - * @param context The context object as returned by dc_context_new(). - * @param event one of the @ref DC_EVENT constants - * @param data1 depends on the event parameter - * @param data2 depends on the event parameter - * @return return 0 unless stated otherwise in the event parameter documentation - */ +/// Callback function that should be given to dc_context_new(). +/// +/// @memberof Context +/// @param context The context object as returned by dc_context_new(). +/// @param event one of the @ref DC_EVENT constants +/// @param data1 depends on the event parameter +/// @param data2 depends on the event parameter +/// @return return 0 unless stated otherwise in the event parameter documentation pub type dc_callback_t = unsafe extern "C" fn(_: &Context, _: Event, _: uintptr_t, _: uintptr_t) -> uintptr_t; @@ -62,15 +44,9 @@ pub type sqlite3_int64 = sqlite_int64; pub type int32_t = libc::int32_t; pub type int64_t = libc::int64_t; pub type uintptr_t = libc::uintptr_t; -pub type __uint8_t = libc::uint8_t; -pub type __uint16_t = libc::uint16_t; -pub type __int32_t = libc::int32_t; -pub type __uint64_t = libc::uint64_t; - pub type size_t = libc::size_t; pub type ssize_t = libc::ssize_t; pub type uint32_t = libc::c_uint; pub type uint8_t = libc::c_uchar; pub type uint16_t = libc::c_ushort; - -pub type __uint32_t = libc::c_uint; +pub type uint64_t = libc::uint64_t; diff --git a/src/x.rs b/src/x.rs index 4c3a5f816..bacb5d900 100644 --- a/src/x.rs +++ b/src/x.rs @@ -3,9 +3,8 @@ use crate::dc_tools::*; use crate::types::*; pub use libc::{ - atoi, calloc, exit, free, malloc, memcmp, memcpy, memmove, memset, realloc, strcat, strchr, - strcmp, strcpy, strcspn, strlen, strncmp, strncpy, strrchr, strspn, strstr, strtol, system, - tolower, write, + calloc, exit, free, malloc, memcmp, memcpy, memmove, memset, realloc, strcat, strchr, strcmp, + strcpy, strcspn, strlen, strncmp, strncpy, strrchr, strspn, strstr, strtol, system, }; pub unsafe fn strdup(s: *const libc::c_char) -> *mut libc::c_char { @@ -43,7 +42,6 @@ extern "C" { unsafe extern "C" fn(_: *const libc::c_void, _: *const libc::c_void) -> libc::c_int, >, ); - pub fn atol(_: *const libc::c_char) -> libc::c_long; pub fn vsnprintf( _: *mut libc::c_char, _: libc::c_ulong, @@ -57,14 +55,6 @@ extern "C" { pub fn dc_mprintf(format: *const libc::c_char, _: ...) -> *mut libc::c_char; } -#[cfg(not(target_os = "android"))] -pub use libc::atof; - -#[cfg(target_os = "android")] -pub unsafe fn atof(nptr: *mut libc::c_char) -> libc::c_double { - libc::strtod(nptr, std::ptr::null_mut()) -} - pub(crate) unsafe fn strcasecmp(s1: *const libc::c_char, s2: *const libc::c_char) -> libc::c_int { let s1 = std::ffi::CStr::from_ptr(s1) .to_string_lossy() @@ -105,14 +95,6 @@ mod tests { use super::*; use crate::dc_tools::to_string; - #[test] - fn test_atox() { - unsafe { - assert_eq!(atol(b"\x00" as *const u8 as *const libc::c_char), 0); - assert_eq!(atoi(b"\x00" as *const u8 as *const libc::c_char), 0); - } - } - #[test] fn test_strndup() { unsafe { From 983ccaaccd3567db59928c9d30b34f28c50225f2 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Thu, 30 May 2019 23:16:41 +0200 Subject: [PATCH 2/5] feat(examples): refactor repl to use rustyline and safe rust --- .gitignore | 5 +- Cargo.toml | 4 + README.md | 31 +- examples/repl/cmdline.rs | 2092 +++++++++++++++----------------------- examples/repl/main.rs | 622 ++++++++---- src/dc_chat.rs | 2 +- src/dc_log.rs | 22 +- src/types.rs | 8 +- 8 files changed, 1287 insertions(+), 1499 deletions(-) diff --git a/.gitignore b/.gitignore index e0e9dec3b..09720f128 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,7 @@ Cargo.lock *.swp *.swo -include \ No newline at end of file +include +.dc-history.txt +*.db +*.db-blobs diff --git a/Cargo.toml b/Cargo.toml index a3ef96b11..60a702d9e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,10 @@ percent-encoding = "1.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" chrono = "0.4.6" +failure = "0.1.5" +# TODO: make optional +rustyline = { git = "https://github.com/kkawakam/rustyline/" } +lazy_static = "1.3.0" [dev-dependencies] tempfile = "3.0" diff --git a/README.md b/README.md index fa21151fe..1f9fe54c5 100644 --- a/README.md +++ b/README.md @@ -2,27 +2,10 @@ > Project porting deltachat-core to rust - [![CircleCI build status][circle-shield]][circle] [![Appveyor build status][appveyor-shield]][appveyor] Current commit on deltachat/deltachat-core: `12ef73c8e76185f9b78e844ea673025f56a959ab`. -## Development - -```sh -# run example -$ cargo run --example simple -# build c-ffi -$ cargo build -p deltachat_ffi --release -# run tests -$ cargo test --all -``` - -[circle-shield]: https://img.shields.io/circleci/project/github/deltachat/deltachat-core-rust/master.svg?style=flat-square -[circle]: https://circleci.com/gh/deltachat/deltachat-core-rust/ -[appveyor-shield]: https://ci.appveyor.com/api/projects/status/lqpegel3ld4ipxj8/branch/master?style=flat-square -[appveyor]: https://ci.appveyor.com/project/dignifiedquire/deltachat-core-rust/branch/master - ## Using the CLI client Run using `cargo`: @@ -73,3 +56,17 @@ For more commands type `> help`. ``` > help ``` + +## Development + +```sh +# run tests +$ cargo test --all +# build c-ffi +$ cargo build -p deltachat_ffi --release +``` + +[circle-shield]: https://img.shields.io/circleci/project/github/deltachat/deltachat-core-rust/master.svg?style=flat-square +[circle]: https://circleci.com/gh/deltachat/deltachat-core-rust/ +[appveyor-shield]: https://ci.appveyor.com/api/projects/status/lqpegel3ld4ipxj8/branch/master?style=flat-square +[appveyor]: https://ci.appveyor.com/project/dignifiedquire/deltachat-core-rust/branch/master diff --git a/examples/repl/cmdline.rs b/examples/repl/cmdline.rs index 4e41b3a7b..f51ca2e88 100644 --- a/examples/repl/cmdline.rs +++ b/examples/repl/cmdline.rs @@ -1,5 +1,3 @@ -use std::ffi::{CStr, CString}; - use deltachat::constants::*; use deltachat::context::*; use deltachat::dc_array::*; @@ -10,71 +8,47 @@ use deltachat::dc_contact::*; use deltachat::dc_imex::*; use deltachat::dc_job::*; use deltachat::dc_location::*; -use deltachat::dc_log::*; use deltachat::dc_lot::*; use deltachat::dc_msg::*; use deltachat::dc_qr::*; use deltachat::dc_receive_imf::*; -use deltachat::dc_securejoin::*; use deltachat::dc_sqlite3::*; -use deltachat::dc_strbuilder::*; use deltachat::dc_tools::*; use deltachat::peerstate::*; use deltachat::types::*; use deltachat::x::*; use num_traits::FromPrimitive; -/* - * Reset database tables. This function is called from Core cmdline. - * - * Argument is a bitmask, executing single or multiple actions in one call. - * - * e.g. bitmask 7 triggers actions definded with bits 1, 2 and 4. - */ -pub unsafe fn dc_reset_tables(context: &Context, bits: libc::c_int) -> libc::c_int { - dc_log_info( - context, - 0i32, - b"Resetting tables (%i)...\x00" as *const u8 as *const libc::c_char, - bits, - ); - if 0 != bits & 1i32 { +/// Reset database tables. This function is called from Core cmdline. +/// Argument is a bitmask, executing single or multiple actions in one call. +/// e.g. bitmask 7 triggers actions definded with bits 1, 2 and 4. +pub unsafe fn dc_reset_tables(context: &Context, bits: i32) -> i32 { + info!(context, 0, "Resetting tables ({})...", bits); + if 0 != bits & 1 { dc_sqlite3_execute( context, &context.sql.clone().read().unwrap(), b"DELETE FROM jobs;\x00" as *const u8 as *const libc::c_char, ); - dc_log_info( - context, - 0i32, - b"(1) Jobs reset.\x00" as *const u8 as *const libc::c_char, - ); + info!(context, 0, "(1) Jobs reset."); } - if 0 != bits & 2i32 { + if 0 != bits & 2 { dc_sqlite3_execute( context, &context.sql.clone().read().unwrap(), b"DELETE FROM acpeerstates;\x00" as *const u8 as *const libc::c_char, ); - dc_log_info( - context, - 0i32, - b"(2) Peerstates reset.\x00" as *const u8 as *const libc::c_char, - ); + info!(context, 0, "(2) Peerstates reset."); } - if 0 != bits & 4i32 { + if 0 != bits & 4 { dc_sqlite3_execute( context, &context.sql.clone().read().unwrap(), b"DELETE FROM keypairs;\x00" as *const u8 as *const libc::c_char, ); - dc_log_info( - context, - 0i32, - b"(4) Private keypairs reset.\x00" as *const u8 as *const libc::c_char, - ); + info!(context, 0, "(4) Private keypairs reset."); } - if 0 != bits & 8i32 { + if 0 != bits & 8 { dc_sqlite3_execute( context, &context.sql.clone().read().unwrap(), @@ -106,20 +80,19 @@ pub unsafe fn dc_reset_tables(context: &Context, bits: libc::c_int) -> libc::c_i &context.sql.clone().read().unwrap(), b"DELETE FROM leftgrps;\x00" as *const u8 as *const libc::c_char, ); - dc_log_info( - context, - 0i32, - b"(8) Rest but server config reset.\x00" as *const u8 as *const libc::c_char, - ); + info!(context, 0, "(8) Rest but server config reset."); } + (context.cb)( context, Event::MSGS_CHANGED, - 0i32 as uintptr_t, - 0i32 as uintptr_t, + 0 as uintptr_t, + 0 as uintptr_t, ); - return 1i32; + + 1 } + unsafe fn dc_poke_eml_file(context: &Context, filename: *const libc::c_char) -> libc::c_int { /* mainly for testing, may be called by dc_import_spec() */ let mut success: libc::c_int = 0i32; @@ -140,200 +113,175 @@ unsafe fn dc_poke_eml_file(context: &Context, filename: *const libc::c_char) -> 0i32 as uint32_t, 0i32 as uint32_t, ); - success = 1i32 + success = 1; } free(data as *mut libc::c_void); - return success; + + success } -/* * - * Import a file to the database. - * For testing, import a folder with eml-files, a single eml-file, e-mail plus public key and so on. - * For normal importing, use dc_imex(). - * - * @private @memberof Context - * @param context The context as created by dc_context_new(). - * @param spec The file or directory to import. NULL for the last command. - * @return 1=success, 0=error. - */ + +/// Import a file to the database. +/// For testing, import a folder with eml-files, a single eml-file, e-mail plus public key and so on. +/// For normal importing, use dc_imex(). +/// +/// @private @memberof Context +/// @param context The context as created by dc_context_new(). +/// @param spec The file or directory to import. NULL for the last command. +/// @return 1=success, 0=error. unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int { - let mut current_block: u64; - let mut success: libc::c_int = 0i32; - let mut real_spec: *mut libc::c_char = 0 as *mut libc::c_char; - let mut suffix: *mut libc::c_char = 0 as *mut libc::c_char; - let mut read_cnt: libc::c_int = 0i32; if 0 == dc_sqlite3_is_open(&context.sql.clone().read().unwrap()) { - dc_log_error( + error!(context, 0, "Import: Database not opened."); + return 0; + } + + let mut current_block: u64; + let mut success: libc::c_int = 0; + let real_spec: *mut libc::c_char; + let mut suffix: *mut libc::c_char = 0 as *mut libc::c_char; + let mut read_cnt: libc::c_int = 0; + + /* if `spec` is given, remember it for later usage; if it is not given, try to use the last one */ + if !spec.is_null() { + real_spec = dc_strdup(spec); + dc_sqlite3_set_config( context, - 0i32, - b"Import: Database not opened.\x00" as *const u8 as *const libc::c_char, + &context.sql.clone().read().unwrap(), + b"import_spec\x00" as *const u8 as *const libc::c_char, + real_spec, ); + current_block = 7149356873433890176; } else { - /* if `spec` is given, remember it for later usage; if it is not given, try to use the last one */ - if !spec.is_null() { - real_spec = dc_strdup(spec); - dc_sqlite3_set_config( - context, - &context.sql.clone().read().unwrap(), - b"import_spec\x00" as *const u8 as *const libc::c_char, - real_spec, - ); - current_block = 7149356873433890176; + real_spec = dc_sqlite3_get_config( + context, + &context.sql.clone().read().unwrap(), + b"import_spec\x00" as *const u8 as *const libc::c_char, + 0 as *const libc::c_char, + ); + if real_spec.is_null() { + error!(context, 0, "Import: No file or folder given."); + current_block = 8522321847195001863; } else { - real_spec = dc_sqlite3_get_config( - context, - &context.sql.clone().read().unwrap(), - b"import_spec\x00" as *const u8 as *const libc::c_char, - 0 as *const libc::c_char, - ); - if real_spec.is_null() { - dc_log_error( - context, - 0i32, - b"Import: No file or folder given.\x00" as *const u8 as *const libc::c_char, - ); - current_block = 8522321847195001863; - } else { - current_block = 7149356873433890176; - } + current_block = 7149356873433890176; } - match current_block { - 8522321847195001863 => {} - _ => { - suffix = dc_get_filesuffix_lc(real_spec); - if !suffix.is_null() - && strcmp(suffix, b"eml\x00" as *const u8 as *const libc::c_char) == 0i32 - { - if 0 != dc_poke_eml_file(context, real_spec) { - read_cnt += 1 + } + match current_block { + 8522321847195001863 => {} + _ => { + suffix = dc_get_filesuffix_lc(real_spec); + if !suffix.is_null() + && strcmp(suffix, b"eml\x00" as *const u8 as *const libc::c_char) == 0 + { + if 0 != dc_poke_eml_file(context, real_spec) { + read_cnt += 1 + } + current_block = 1622411330066726685; + } else { + /* import a directory */ + let dir_name = std::path::Path::new(to_str(real_spec)); + let dir = std::fs::read_dir(dir_name); + if dir.is_err() { + error!( + context, + 0, + "Import: Cannot open directory \"{}\".", + to_str(real_spec), + ); + current_block = 8522321847195001863; + } else { + let dir = dir.unwrap(); + for entry in dir { + if entry.is_err() { + break; + } + let entry = entry.unwrap(); + let name_f = entry.file_name(); + let name = name_f.to_string_lossy(); + if name.ends_with(".eml") { + let path_plus_name = format!("{}/{}", to_str(real_spec), name); + info!(context, 0, "Import: {}", path_plus_name); + let path_plus_name_c = to_cstring(path_plus_name); + + if 0 != dc_poke_eml_file(context, path_plus_name_c.as_ptr()) { + read_cnt += 1 + } + } } current_block = 1622411330066726685; - } else { - /* import a directory */ - let dir_name = std::path::Path::new(to_str(real_spec)); - let dir = std::fs::read_dir(dir_name); - if dir.is_err() { - dc_log_error( - context, - 0i32, - b"Import: Cannot open directory \"%s\".\x00" as *const u8 - as *const libc::c_char, - real_spec, - ); - current_block = 8522321847195001863; - } else { - let dir = dir.unwrap(); - for entry in dir { - if entry.is_err() { - break; - } - let entry = entry.unwrap(); - let name_f = entry.file_name(); - let name = name_f.to_string_lossy(); - if name.ends_with(".eml") { - let path_plus_name: *mut libc::c_char = dc_mprintf( - b"%s/%s\x00" as *const u8 as *const libc::c_char, - real_spec, - to_cstring(name).as_ptr(), - ); - dc_log_info( - context, - 0i32, - b"Import: %s\x00" as *const u8 as *const libc::c_char, - path_plus_name, - ); - if 0 != dc_poke_eml_file(context, path_plus_name) { - read_cnt += 1 - } - free(path_plus_name as *mut libc::c_void); - } - } - current_block = 1622411330066726685; - } } - match current_block { - 8522321847195001863 => {} - _ => { - dc_log_info( - context, - 0i32, - b"Import: %i items read from \"%s\".\x00" as *const u8 - as *const libc::c_char, - read_cnt, - real_spec, - ); - if read_cnt > 0i32 { - (context.cb)( - context, - Event::MSGS_CHANGED, - 0i32 as uintptr_t, - 0i32 as uintptr_t, - ); - } - success = 1i32 + } + match current_block { + 8522321847195001863 => {} + _ => { + info!( + context, + 0, + "Import: {} items read from \"{}\".", + read_cnt, + to_str(real_spec) + ); + if read_cnt > 0 { + (context.cb)(context, Event::MSGS_CHANGED, 0 as uintptr_t, 0 as uintptr_t); } + success = 1 } } } } + free(real_spec as *mut libc::c_void); free(suffix as *mut libc::c_void); success } -unsafe fn log_msg(context: &Context, prefix: *const libc::c_char, msg: *mut dc_msg_t) { +unsafe fn log_msg(context: &Context, prefix: impl AsRef, msg: *mut dc_msg_t) { let contact: *mut dc_contact_t = dc_get_contact(context, dc_msg_get_from_id(msg)); let contact_name: *mut libc::c_char = dc_contact_get_name(contact); let contact_id: libc::c_int = dc_contact_get_id(contact) as libc::c_int; - let mut statestr: *const libc::c_char = b"\x00" as *const u8 as *const libc::c_char; - match dc_msg_get_state(msg) { - 20 => statestr = b" o\x00" as *const u8 as *const libc::c_char, - 26 => statestr = b" \xe2\x88\x9a\x00" as *const u8 as *const libc::c_char, - 28 => statestr = b" \xe2\x88\x9a\xe2\x88\x9a\x00" as *const u8 as *const libc::c_char, - 24 => statestr = b" !!\x00" as *const u8 as *const libc::c_char, - _ => {} - } + let statestr = match dc_msg_get_state(msg) { + 20 => " o", + 26 => " √", + 28 => " √√", + 24 => " !!", + _ => "", + }; let temp2: *mut libc::c_char = dc_timestamp_to_str(dc_msg_get_timestamp(msg)); let msgtext: *mut libc::c_char = dc_msg_get_text(msg); - dc_log_info( + info!( context, - 0i32, - b"%s#%i%s%s: %s (Contact#%i): %s %s%s%s%s [%s]\x00" as *const u8 as *const libc::c_char, - prefix, + 0, + "{}#{}{}{}: {} (Contact#{}): {} {}{}{}{} [{}]", + prefix.as_ref(), dc_msg_get_id(msg) as libc::c_int, if 0 != dc_msg_get_showpadlock(msg) { - b"\xf0\x9f\x94\x92\x00" as *const u8 as *const libc::c_char + "🔒" } else { - b"\x00" as *const u8 as *const libc::c_char + "" }, - if dc_msg_has_location(msg) { - b"\xf0\x9f\x93\x8d\x00" as *const u8 as *const libc::c_char - } else { - b"\x00" as *const u8 as *const libc::c_char - }, - contact_name, + if dc_msg_has_location(msg) { "📍" } else { "" }, + to_str(contact_name), contact_id, - msgtext, + to_str(msgtext), if 0 != dc_msg_is_starred(msg) { - b" \xe2\x98\x85\x00" as *const u8 as *const libc::c_char + "★" } else { - b"\x00" as *const u8 as *const libc::c_char + "" }, - if dc_msg_get_from_id(msg) == 1i32 as libc::c_uint { - b"\x00" as *const u8 as *const libc::c_char - } else if dc_msg_get_state(msg) == 16i32 { - b"[SEEN]\x00" as *const u8 as *const libc::c_char - } else if dc_msg_get_state(msg) == 13i32 { - b"[NOTICED]\x00" as *const u8 as *const libc::c_char + if dc_msg_get_from_id(msg) == 1 as libc::c_uint { + "" + } else if dc_msg_get_state(msg) == 16 { + "[SEEN]" + } else if dc_msg_get_state(msg) == 13 { + "[NOTICED]" } else { - b"[FRESH]\x00" as *const u8 as *const libc::c_char + "[FRESH]" }, if 0 != dc_msg_is_info(msg) { - b"[INFO]\x00" as *const u8 as *const libc::c_char + "[INFO]" } else { - b"\x00" as *const u8 as *const libc::c_char + "" }, statestr, - temp2, + to_str(temp2), ); free(msgtext as *mut libc::c_void); free(temp2 as *mut libc::c_void); @@ -342,475 +290,421 @@ unsafe fn log_msg(context: &Context, prefix: *const libc::c_char, msg: *mut dc_m } unsafe fn log_msglist(context: &Context, msglist: *mut dc_array_t) { - let mut i: libc::c_int = 0; - let cnt: libc::c_int = dc_array_get_cnt(msglist) as libc::c_int; - let mut lines_out: libc::c_int = 0i32; - while i < cnt { - let msg_id: uint32_t = dc_array_get_id(msglist, i as size_t); - if msg_id == 9i32 as libc::c_uint { - dc_log_info(context, 0i32, - b"--------------------------------------------------------------------------------\x00" - as *const u8 as *const libc::c_char); + let cnt = dc_array_get_cnt(msglist) as usize; + let mut lines_out = 0; + for i in 0..cnt { + let msg_id = dc_array_get_id(msglist, i as size_t); + if msg_id == 9 as libc::c_uint { + info!( + context, + 0, + "--------------------------------------------------------------------------------" + ); + lines_out += 1 - } else if msg_id > 0i32 as libc::c_uint { - if lines_out == 0i32 { - dc_log_info(context, 0i32, - b"--------------------------------------------------------------------------------\x00" - as *const u8 as *const libc::c_char); + } else if msg_id > 0 as libc::c_uint { + if lines_out == 0 { + info!( + context, 0, + "--------------------------------------------------------------------------------", + ); lines_out += 1 } - let msg: *mut dc_msg_t = dc_get_msg(context, msg_id); - log_msg(context, b"Msg\x00" as *const u8 as *const libc::c_char, msg); + let msg = dc_get_msg(context, msg_id); + log_msg(context, "Msg", msg); dc_msg_unref(msg); } - i += 1 } - if lines_out > 0i32 { - dc_log_info( + if lines_out > 0 { + info!( context, - 0i32, - b"--------------------------------------------------------------------------------\x00" - as *const u8 as *const libc::c_char, + 0, "--------------------------------------------------------------------------------" ); - }; + } } + unsafe fn log_contactlist(context: &Context, contacts: *mut dc_array_t) { let mut contact: *mut dc_contact_t; - if !dc_array_search_id(contacts, 1i32 as uint32_t, 0 as *mut size_t) { - dc_array_add_id(contacts, 1i32 as uint32_t); + if !dc_array_search_id(contacts, 1 as uint32_t, 0 as *mut size_t) { + dc_array_add_id(contacts, 1 as uint32_t); } - let mut i = 0; - while i < dc_array_get_cnt(contacts) { - let contact_id: uint32_t = dc_array_get_id(contacts, i as size_t); - let line: *mut libc::c_char; - let mut line2: *mut libc::c_char = 0 as *mut libc::c_char; + let cnt = dc_array_get_cnt(contacts); + for i in 0..cnt { + let contact_id = dc_array_get_id(contacts, i as size_t); + let line; + let mut line2 = "".to_string(); contact = dc_get_contact(context, contact_id); if !contact.is_null() { let name: *mut libc::c_char = dc_contact_get_name(contact); let addr: *mut libc::c_char = dc_contact_get_addr(contact); let verified_state: libc::c_int = dc_contact_is_verified(contact); - let verified_str: *const libc::c_char = if 0 != verified_state { - if verified_state == 2i32 { - b" \xe2\x88\x9a\xe2\x88\x9a\x00" as *const u8 as *const libc::c_char + let verified_str = if 0 != verified_state { + if verified_state == 2 { + " √√" } else { - b" \xe2\x88\x9a\x00" as *const u8 as *const libc::c_char + " √" } } else { - b"\x00" as *const u8 as *const libc::c_char + "" }; - line = dc_mprintf( - b"%s%s <%s>\x00" as *const u8 as *const libc::c_char, + line = format!( + "{}{} <{}>", if !name.is_null() && 0 != *name.offset(0isize) as libc::c_int { - name + to_str(name) } else { - b"\x00" as *const u8 as *const libc::c_char + "" }, verified_str, if !addr.is_null() && 0 != *addr.offset(0isize) as libc::c_int { - addr + to_str(addr) } else { - b"addr unset\x00" as *const u8 as *const libc::c_char - }, + "addr unset" + } ); let peerstate = Peerstate::from_addr(context, &context.sql.clone().read().unwrap(), to_str(addr)); - if peerstate.is_some() && contact_id != 1i32 as libc::c_uint { - let pe = to_cstring(format!("{}", peerstate.as_ref().unwrap().prefer_encrypt)); - line2 = dc_mprintf( - b", prefer-encrypt=%s\x00" as *const u8 as *const libc::c_char, - pe.as_ptr(), + if peerstate.is_some() && contact_id != 1 as libc::c_uint { + line2 = format!( + ", prefer-encrypt={}", + peerstate.as_ref().unwrap().prefer_encrypt ); } dc_contact_unref(contact); free(name as *mut libc::c_void); free(addr as *mut libc::c_void); - dc_log_info( - context, - 0i32, - b"Contact#%i: %s%s\x00" as *const u8 as *const libc::c_char, - contact_id as libc::c_int, - line, - if !line2.is_null() { - line2 - } else { - b"\x00" as *const u8 as *const libc::c_char - }, - ); - free(line as *mut libc::c_void); - free(line2 as *mut libc::c_void); + info!(context, 0, "Contact#{}: {}{}", contact_id, line, line2); } - i += 1 } } -static mut s_is_auth: libc::c_int = 0i32; +static mut S_IS_AUTH: libc::c_int = 0; pub unsafe fn dc_cmdline_skip_auth() { - s_is_auth = 1i32; + S_IS_AUTH = 1; } -unsafe fn chat_prefix(chat: *const dc_chat_t) -> *const libc::c_char { - if (*chat).type_0 == 120i32 { - return b"Group\x00" as *const u8 as *const libc::c_char; - } else if (*chat).type_0 == 130i32 { - return b"VerifiedGroup\x00" as *const u8 as *const libc::c_char; + +unsafe fn chat_prefix(chat: *const dc_chat_t) -> &'static str { + if (*chat).type_0 == 120 { + "Group" + } else if (*chat).type_0 == 130 { + "VerifiedGroup" } else { - return b"Single\x00" as *const u8 as *const libc::c_char; - }; + "Single" + } } -pub unsafe fn dc_cmdline(context: &Context, cmdline: &str) -> *mut libc::c_char { - let mut ret: *mut libc::c_char = 1i32 as *mut libc::c_char; +pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::Error> { let chat_id = *context.cmdline_sel_chat_id.read().unwrap(); - let mut sel_chat = if chat_id > 0 { dc_get_chat(context, chat_id) } else { std::ptr::null_mut() }; - let mut args = cmdline.split_whitespace(); + let mut args = line.splitn(3, ' '); let arg0 = args.next().unwrap_or_default(); - let cmd = dc_strdup(CString::new(arg0).unwrap().as_ptr()); - let arg1 = args.next().unwrap_or_default(); - let arg1_c = CString::new(arg1).unwrap(); + let arg1_c = to_cstring(arg1); let arg1_c_ptr = if arg1.is_empty() { std::ptr::null() } else { arg1_c.as_ptr() }; let arg2 = args.next().unwrap_or_default(); - let arg2_c = CString::new(arg2).unwrap(); + let arg2_c = to_cstring(arg2); + let arg2_c_ptr = if arg2.is_empty() { + std::ptr::null() + } else { + arg2_c.as_ptr() + }; - if cmdline == "help" || cmdline == "?" { - if arg1 == "imex" { - ret = - dc_strdup(b"====================Import/Export commands==\ninitiate-key-transfer\nget-setupcodebegin \ncontinue-key-transfer \nhas-backup\nexport-backup\nimport-backup \nexport-keys\nimport-keys\nexport-setup\npoke [|| ]\nreset \nstop\n=============================================\x00" - as *const u8 as *const libc::c_char) - } else { - ret = - dc_strdup(b"==========================Database commands==\ninfo\nopen \nclose\nset []\nget \noauth2\nconfigure\nconnect\ndisconnect\nmaybenetwork\nhousekeeping\nhelp imex (Import/Export)\n==============================Chat commands==\nlistchats []\nlistarchived\nchat [|0]\ncreatechat \ncreatechatbymsg \ncreategroup \ncreateverified \naddmember \nremovemember \ngroupname \ngroupimage []\nchatinfo\nsendlocations \nsetlocation \ndellocations\ngetlocations []\nsend \nsendimage []\nsendfile \ndraft []\nlistmedia\narchive \nunarchive \ndelchat \n===========================Message commands==\nlistmsgs \nmsginfo \nlistfresh\nforward \nmarkseen \nstar \nunstar \ndelmsg \n===========================Contact commands==\nlistcontacts []\nlistverified []\naddcontact [] \ncontactinfo \ndelcontact \ncleanupcontacts\n======================================Misc.==\ngetqr []\ngetbadqr\ncheckqr \nevent \nfileinfo \nclear -- clear screen\nexit\n=============================================\x00" - as *const u8 as *const libc::c_char) - } - } else if 0 == s_is_auth { - if strcmp(cmd, b"auth\x00" as *const u8 as *const libc::c_char) == 0i32 { - let is_pw = dc_get_config(context, b"mail_pw\x00" as *const u8 as *const libc::c_char); - if arg1 == CStr::from_ptr(is_pw).to_str().unwrap() { - s_is_auth = 1; - ret = 2i32 as *mut libc::c_char + match arg0 { + "help" | "?" => match arg1 { + // TODO: reuse commands definition in main.rs. + "imex" => println!( + "====================Import/Export commands==\n\ + initiate-key-transfer\n\ + get-setupcodebegin \n\ + continue-key-transfer \n\ + has-backup\n\ + export-backup\n\ + import-backup \n\ + export-keys\n\ + import-keys\n\ + export-setup\n\ + poke [|| ]\n\ + reset \n\ + stop\n\ + =============================================" + ), + _ => println!( + "==========================Database commands==\n\ + info\n\ + open \n\ + close\n\ + set []\n\ + get \n\ + oauth2\n\ + configure\n\ + connect\n\ + disconnect\n\ + maybenetwork\n\ + housekeeping\n\ + help imex (Import/Export)\n\ + ==============================Chat commands==\n\ + listchats []\n\ + listarchived\n\ + chat [|0]\n\ + createchat \n\ + createchatbymsg \n\ + creategroup \n\ + createverified \n\ + addmember \n\ + removemember \n\ + groupname \n\ + groupimage []\n\ + chatinfo\n\ + sendlocations \n\ + setlocation \n\ + dellocations\n\ + getlocations []\n\ + send \n\ + sendimage []\n\ + sendfile \n\ + draft []\n\ + listmedia\n\ + archive \n\ + unarchive \n\ + delchat \n\ + ===========================Message commands==\n\ + listmsgs \n\ + msginfo \n\ + listfresh\n\ + forward \n\ + markseen \n\ + star \n\ + unstar \n\ + delmsg \n\ + ===========================Contact commands==\n\ + listcontacts []\n\ + listverified []\n\ + addcontact [] \n\ + contactinfo \n\ + delcontact \n\ + cleanupcontacts\n\ + ======================================Misc.==\n\ + getqr []\n\ + getbadqr\n\ + checkqr \n\ + event \n\ + fileinfo \n\ + clear -- clear screen\n\ + exit\n\ + =============================================" + ), + }, + "auth" => { + if 0 == S_IS_AUTH { + let is_pw = + dc_get_config(context, b"mail_pw\x00" as *const u8 as *const libc::c_char); + if arg1 == to_str(is_pw) { + S_IS_AUTH = 1; + } else { + println!("Bad password."); + } } else { - ret = b"Bad password.\x00" as *const u8 as *const libc::c_char as *mut libc::c_char + println!("Already authorized."); } - } else { - ret = dc_strdup( - b"Please authorize yourself using: auth \x00" as *const u8 - as *const libc::c_char, - ) } - } else if strcmp(cmd, b"auth\x00" as *const u8 as *const libc::c_char) == 0i32 { - ret = dc_strdup(b"Already authorized.\x00" as *const u8 as *const libc::c_char) - } else if strcmp(cmd, b"open\x00" as *const u8 as *const libc::c_char) == 0i32 { - if !arg1.is_empty() { + "open" => { + ensure!(!arg1.is_empty(), "Argument missing"); dc_close(context); - ret = if 0 != dc_open(context, arg1_c_ptr, 0 as *const libc::c_char) { - 2i32 as *mut libc::c_char - } else { - 1i32 as *mut libc::c_char - } - } else { - ret = dc_strdup( - b"ERROR: Argument missing.\x00" as *const u8 as *const libc::c_char, - ) + ensure!( + 0 != dc_open(context, arg1_c_ptr, 0 as *const libc::c_char), + "Open failed" + ); } - } else if strcmp(cmd, b"close\x00" as *const u8 as *const libc::c_char) == 0i32 { - dc_close(context); - ret = 2i32 as *mut libc::c_char - } else if strcmp( - cmd, - b"initiate-key-transfer\x00" as *const u8 as *const libc::c_char, - ) == 0i32 - { - let setup_code: *mut libc::c_char = dc_initiate_key_transfer(context); - ret = if !setup_code.is_null() { - dc_mprintf( - b"Setup code for the transferred setup message: %s\x00" as *const u8 - as *const libc::c_char, - setup_code, - ) - } else { - 1i32 as *mut libc::c_char - }; - free(setup_code as *mut libc::c_void); - } else if strcmp( - cmd, - b"get-setupcodebegin\x00" as *const u8 as *const libc::c_char, - ) == 0i32 - { - if !arg1.is_empty() { + "close" => { + dc_close(context); + } + "initiate-key-transfer" => { + let setup_code = dc_initiate_key_transfer(context); + if !setup_code.is_null() { + println!( + "Setup code for the transferred setup message: {}", + to_str(setup_code), + ); + free(setup_code as *mut libc::c_void); + } else { + bail!("Failed to generate setup code"); + }; + } + "get-setupcodebegin" => { + ensure!(!arg1.is_empty(), "Argument missing."); let msg_id: u32 = arg1.parse().unwrap(); let msg: *mut dc_msg_t = dc_get_msg(context, msg_id); if 0 != dc_msg_is_setupmessage(msg) { let setupcodebegin: *mut libc::c_char = dc_msg_get_setupcodebegin(msg); - ret = dc_mprintf( - b"The setup code for setup message Msg#%i starts with: %s\x00" as *const u8 - as *const libc::c_char, + println!( + "The setup code for setup message Msg#{} starts with: {}", msg_id, - setupcodebegin, + to_str(setupcodebegin), ); free(setupcodebegin as *mut libc::c_void); } else { - ret = dc_mprintf( - b"ERROR: Msg#%i is no setup message.\x00" as *const u8 as *const libc::c_char, - msg_id, - ) + bail!("Msg#{} is no setup message.", msg_id,); } dc_msg_unref(msg); - } else { - ret = dc_strdup( - b"ERROR: Argument missing.\x00" as *const u8 as *const libc::c_char, - ) } - } else if strcmp( - cmd, - b"continue-key-transfer\x00" as *const u8 as *const libc::c_char, - ) == 0i32 - { - if !arg1.is_empty() && !arg2.is_empty() { - ret = if 0 != dc_continue_key_transfer(context, arg1.parse().unwrap(), arg2_c.as_ptr()) - { - 2i32 as *mut libc::c_char - } else { - 1i32 as *mut libc::c_char + "continue-key-transfer" => { + ensure!( + !arg1.is_empty() && !arg2.is_empty(), + "Arguments expected" + ); + if 0 == dc_continue_key_transfer(context, arg1.parse().unwrap(), arg2_c_ptr) { + bail!("Continue key transfer failed"); } - } else { - ret = dc_strdup( - b"ERROR: Arguments expected.\x00" as *const u8 - as *const libc::c_char, - ) } - } else if strcmp(cmd, b"has-backup\x00" as *const u8 as *const libc::c_char) == 0i32 { - ret = dc_imex_has_backup(context, context.get_blobdir()); - if ret.is_null() { - ret = dc_strdup(b"No backup found.\x00" as *const u8 as *const libc::c_char) + "has-backup" => { + let ret = dc_imex_has_backup(context, context.get_blobdir()); + if ret.is_null() { + println!("No backup found."); + } } - } else if strcmp( - cmd, - b"export-backup\x00" as *const u8 as *const libc::c_char, - ) == 0i32 - { - dc_imex( - context, - 11i32, - context.get_blobdir(), - 0 as *const libc::c_char, - ); - ret = 2i32 as *mut libc::c_char - } else if strcmp( - cmd, - b"import-backup\x00" as *const u8 as *const libc::c_char, - ) == 0i32 - { - if !arg1.is_empty() { - dc_imex(context, 12i32, arg1_c_ptr, 0 as *const libc::c_char); - ret = 2i32 as *mut libc::c_char - } else { - ret = dc_strdup( - b"ERROR: Argument missing.\x00" as *const u8 as *const libc::c_char, - ) + "export-backup" => { + dc_imex(context, 11, context.get_blobdir(), 0 as *const libc::c_char); } - } else if strcmp(cmd, b"export-keys\x00" as *const u8 as *const libc::c_char) == 0i32 { - dc_imex( - context, - 1i32, - context.get_blobdir(), - 0 as *const libc::c_char, - ); - ret = 2i32 as *mut libc::c_char - } else if strcmp(cmd, b"import-keys\x00" as *const u8 as *const libc::c_char) == 0i32 { - dc_imex( - context, - 2i32, - context.get_blobdir(), - 0 as *const libc::c_char, - ); - ret = 2i32 as *mut libc::c_char - } else if strcmp(cmd, b"export-setup\x00" as *const u8 as *const libc::c_char) == 0i32 { - let setup_code_0: *mut libc::c_char = dc_create_setup_code(context); - let file_name: *mut libc::c_char = dc_mprintf( - b"%s/autocrypt-setup-message.html\x00" as *const u8 as *const libc::c_char, - context.get_blobdir(), - ); - let file_content: *mut libc::c_char; - file_content = dc_render_setup_file(context, setup_code_0); - if !file_content.is_null() - && 0 != dc_write_file( - context, - file_name, - file_content as *const libc::c_void, - strlen(file_content), - ) - { - ret = dc_mprintf( - b"Setup message written to: %s\nSetup code: %s\x00" as *const u8 - as *const libc::c_char, - file_name, - setup_code_0, - ) - } else { - ret = 1i32 as *mut libc::c_char + "import-backup" => { + ensure!(!arg1.is_empty(), "Argument missing."); + dc_imex(context, 12, arg1_c_ptr, 0 as *const libc::c_char); } - free(file_content as *mut libc::c_void); - free(file_name as *mut libc::c_void); - free(setup_code_0 as *mut libc::c_void); - } else if strcmp(cmd, b"poke\x00" as *const u8 as *const libc::c_char) == 0i32 { - ret = if 0 != poke_spec(context, arg1_c_ptr) { - 2i32 as *mut libc::c_char - } else { - 1i32 as *mut libc::c_char + "export-keys" => { + dc_imex(context, 1, context.get_blobdir(), 0 as *const libc::c_char); } - } else if strcmp(cmd, b"reset\x00" as *const u8 as *const libc::c_char) == 0i32 { - if !arg1.is_empty() { - let bits: libc::c_int = arg1.parse().unwrap(); - if bits > 15i32 { - ret = dc_strdup( - b"ERROR: must be lower than 16.\x00" as *const u8 as *const libc::c_char, + "import-keys" => { + dc_imex(context, 2, context.get_blobdir(), 0 as *const libc::c_char); + } + "export-setup" => { + let setup_code: *mut libc::c_char = dc_create_setup_code(context); + let file_name: *mut libc::c_char = dc_mprintf( + b"%s/autocrypt-setup-message.html\x00" as *const u8 as *const libc::c_char, + context.get_blobdir(), + ); + let file_content: *mut libc::c_char; + file_content = dc_render_setup_file(context, setup_code); + if !file_content.is_null() + && 0 != dc_write_file( + context, + file_name, + file_content as *const libc::c_void, + strlen(file_content), + ) + { + println!( + "Setup message written to: {}\nSetup code: {}", + to_str(file_name), + to_str(setup_code), ) } else { - ret = if 0 != dc_reset_tables(context, bits) { - 2i32 as *mut libc::c_char - } else { - 1i32 as *mut libc::c_char - } + bail!(""); } - } else { - ret = - dc_strdup(b"ERROR: Argument missing: 1=jobs, 2=peerstates, 4=private keys, 8=rest but server config\x00" - as *const u8 as *const libc::c_char) + free(file_content as *mut libc::c_void); + free(file_name as *mut libc::c_void); + free(setup_code as *mut libc::c_void); } - } else if strcmp(cmd, b"stop\x00" as *const u8 as *const libc::c_char) == 0i32 { - dc_stop_ongoing_process(context); - ret = 2i32 as *mut libc::c_char - } else if strcmp(cmd, b"set\x00" as *const u8 as *const libc::c_char) == 0i32 { - if !arg1.is_empty() { - ret = if 0 != dc_set_config(context, arg1_c_ptr, arg2_c.as_ptr()) { - 2i32 as *mut libc::c_char - } else { - 1i32 as *mut libc::c_char - } - } else { - ret = - dc_strdup(b"ERROR: Argument missing.\x00" as *const u8 as *const libc::c_char) + "poke" => { + ensure!(0 != poke_spec(context, arg1_c_ptr), "Poke failed"); } - } else if strcmp(cmd, b"get\x00" as *const u8 as *const libc::c_char) == 0i32 { - if !arg1.is_empty() { - let val: *mut libc::c_char = dc_get_config(context, arg1_c_ptr); - ret = dc_mprintf( - b"%s=%s\x00" as *const u8 as *const libc::c_char, - arg1_c_ptr, - val, + "reset" => { + ensure!(!arg1.is_empty(), "Argument missing: 1=jobs, 2=peerstates, 4=private keys, 8=rest but server config"); + let bits: i32 = arg1.parse().unwrap(); + ensure!(bits < 16, " must be lower than 16."); + ensure!(0 != dc_reset_tables(context, bits), "Reset failed"); + } + "stop" => { + dc_stop_ongoing_process(context); + } + "set" => { + ensure!(!arg1.is_empty(), "Argument missing."); + ensure!( + 0 != dc_set_config(context, arg1_c_ptr, arg2_c_ptr), + "Set config failed" ); + } + "get" => { + ensure!(!arg1.is_empty(), "Argument missing."); + let val = dc_get_config(context, arg1_c_ptr); + println!("{}={}", arg1, to_string(val)); free(val as *mut libc::c_void); - } else { - ret = - dc_strdup(b"ERROR: Argument missing.\x00" as *const u8 as *const libc::c_char) } - } else if strcmp(cmd, b"info\x00" as *const u8 as *const libc::c_char) == 0i32 { - ret = dc_get_info(context); - if ret.is_null() { - ret = 1i32 as *mut libc::c_char + "info" => { + println!("{}", to_string(dc_get_info(context))); } - } else if strcmp(cmd, b"maybenetwork\x00" as *const u8 as *const libc::c_char) == 0i32 { - dc_maybe_network(context); - ret = 2i32 as *mut libc::c_char - } else if strcmp(cmd, b"housekeeping\x00" as *const u8 as *const libc::c_char) == 0i32 { - dc_housekeeping(context); - ret = 2i32 as *mut libc::c_char - } else if strcmp(cmd, b"listchats\x00" as *const u8 as *const libc::c_char) == 0i32 - || strcmp(cmd, b"listarchived\x00" as *const u8 as *const libc::c_char) == 0i32 - || strcmp(cmd, b"chats\x00" as *const u8 as *const libc::c_char) == 0i32 - { - let listflags: libc::c_int = - if strcmp(cmd, b"listarchived\x00" as *const u8 as *const libc::c_char) == 0i32 { - 0x1i32 - } else { - 0i32 - }; + "maybenetwork" => { + dc_maybe_network(context); + } + "housekeeping" => { + dc_housekeeping(context); + } + "listchats" | "listarchived" | "chats" => { + let listflags = if arg0 == "listarchived" { 0x01 } else { 0 }; + let chatlist = dc_get_chatlist(context, listflags, arg1_c_ptr, 0 as uint32_t); + ensure!(!chatlist.is_null(), "Failed to retrieve chatlist"); - let chatlist: *mut dc_chatlist_t = - dc_get_chatlist(context, listflags, arg1_c_ptr, 0i32 as uint32_t); - if !chatlist.is_null() { let mut i: libc::c_int; - let cnt: libc::c_int = dc_chatlist_get_cnt(chatlist) as libc::c_int; - if cnt > 0i32 { - dc_log_info(context, 0i32, - b"================================================================================\x00" - as *const u8 as *const libc::c_char); - i = cnt - 1i32; + let cnt = dc_chatlist_get_cnt(chatlist) as libc::c_int; + if cnt > 0 { + info!( + context, 0, + "================================================================================" + ); - while i >= 0i32 { - let chat: *mut dc_chat_t = - dc_get_chat(context, dc_chatlist_get_chat_id(chatlist, i as size_t)); - let temp_subtitle: *mut libc::c_char = dc_chat_get_subtitle(chat); - let temp_name: *mut libc::c_char = dc_chat_get_name(chat); - dc_log_info( + i = cnt - 1; + + while i >= 0 { + let chat = dc_get_chat(context, dc_chatlist_get_chat_id(chatlist, i as size_t)); + let temp_subtitle = dc_chat_get_subtitle(chat); + let temp_name = dc_chat_get_name(chat); + info!( context, - 0i32, - b"%s#%i: %s [%s] [%i fresh]\x00" as *const u8 as *const libc::c_char, + 0, + "{}#{}: {} [{}] [{} fresh]", chat_prefix(chat), dc_chat_get_id(chat) as libc::c_int, - temp_name, - temp_subtitle, + to_str(temp_name), + to_str(temp_subtitle), dc_get_fresh_msg_cnt(context, dc_chat_get_id(chat)) as libc::c_int, ); free(temp_subtitle as *mut libc::c_void); free(temp_name as *mut libc::c_void); - let lot: *mut dc_lot_t = dc_chatlist_get_summary(chatlist, i as size_t, chat); - let mut statestr: *const libc::c_char = - b"\x00" as *const u8 as *const libc::c_char; - if 0 != dc_chat_get_archived(chat) { - statestr = b" [Archived]\x00" as *const u8 as *const libc::c_char + let lot = dc_chatlist_get_summary(chatlist, i as size_t, chat); + let statestr = if 0 != dc_chat_get_archived(chat) { + " [Archived]" } else { match dc_lot_get_state(lot) { - 20 => statestr = b" o\x00" as *const u8 as *const libc::c_char, - 26 => { - statestr = b" \xe2\x88\x9a\x00" as *const u8 as *const libc::c_char - } - 28 => { - statestr = b" \xe2\x88\x9a\xe2\x88\x9a\x00" as *const u8 - as *const libc::c_char - } - 24 => statestr = b" !!\x00" as *const u8 as *const libc::c_char, - _ => {} + 20 => " o", + 26 => " √", + 28 => " √√", + 24 => " !!", + _ => "", } - } - let timestr: *mut libc::c_char = dc_timestamp_to_str(dc_lot_get_timestamp(lot)); - let text1: *mut libc::c_char = dc_lot_get_text1(lot); - let text2: *mut libc::c_char = dc_lot_get_text2(lot); - dc_log_info( + }; + let timestr = dc_timestamp_to_str(dc_lot_get_timestamp(lot)); + let text1 = dc_lot_get_text1(lot); + let text2 = dc_lot_get_text2(lot); + info!( context, - 0i32, - b"%s%s%s%s [%s]%s\x00" as *const u8 as *const libc::c_char, - if !text1.is_null() { - text1 - } else { - b"\x00" as *const u8 as *const libc::c_char - }, - if !text1.is_null() { - b": \x00" as *const u8 as *const libc::c_char - } else { - b"\x00" as *const u8 as *const libc::c_char - }, - if !text2.is_null() { - text2 - } else { - b"\x00" as *const u8 as *const libc::c_char - }, + 0, + "{}{}{}{} [{}]{}", + to_string(text1), + if !text1.is_null() { ": " } else { "" }, + to_string(text2), statestr, - timestr, + to_str(timestr), if 0 != dc_chat_is_sending_locations(chat) { - b"\xf0\x9f\x93\x8d\x00" as *const u8 as *const libc::c_char + "📍" } else { - b"\x00" as *const u8 as *const libc::c_char + "" }, ); free(text1 as *mut libc::c_void); @@ -818,771 +712,491 @@ pub unsafe fn dc_cmdline(context: &Context, cmdline: &str) -> *mut libc::c_char free(timestr as *mut libc::c_void); dc_lot_unref(lot); dc_chat_unref(chat); - dc_log_info(context, 0i32, - b"================================================================================\x00" - as *const u8 as *const libc::c_char); + info!( + context, 0, + "================================================================================" + ); + i -= 1 } } - if 0 != dc_is_sending_locations_to_chat(context, 0i32 as uint32_t) { - dc_log_info( - context, - 0i32, - b"Location streaming enabled.\x00" as *const u8 as *const libc::c_char, - ); + if 0 != dc_is_sending_locations_to_chat(context, 0 as uint32_t) { + info!(context, 0, "Location streaming enabled."); } - ret = dc_mprintf( - b"%i chats.\x00" as *const u8 as *const libc::c_char, - cnt as libc::c_int, - ); + println!("{} chats", cnt); dc_chatlist_unref(chatlist); - } else { - ret = 1i32 as *mut libc::c_char } - } else if strcmp(cmd, b"chat\x00" as *const u8 as *const libc::c_char) == 0i32 { - if !arg1.is_empty() { - if !sel_chat.is_null() { + "chat" => { + if sel_chat.is_null() && arg1.is_empty() { + bail!("Argument [chat-id] is missing."); + } + if !sel_chat.is_null() && !arg1.is_empty() { dc_chat_unref(sel_chat); } + if !arg1.is_empty() { + let chat_id = arg1.parse().unwrap(); + println!("Selecting chat #{}", chat_id); + sel_chat = dc_get_chat(context, chat_id); + *context.cmdline_sel_chat_id.write().unwrap() = chat_id; + } - let chat_id = arg1.parse().unwrap(); - sel_chat = dc_get_chat(context, chat_id); - *context.cmdline_sel_chat_id.write().unwrap() = chat_id; - } - if !sel_chat.is_null() { - let msglist: *mut dc_array_t = dc_get_chat_msgs( + ensure!(!sel_chat.is_null(), "Failed to select chat"); + + let msglist = dc_get_chat_msgs(context, dc_chat_get_id(sel_chat), 0x1, 0); + let temp2 = dc_chat_get_subtitle(sel_chat); + let temp_name = dc_chat_get_name(sel_chat); + info!( context, - dc_chat_get_id(sel_chat), - 0x1i32 as uint32_t, - 0i32 as uint32_t, - ); - let temp2: *mut libc::c_char = dc_chat_get_subtitle(sel_chat); - let temp_name_0: *mut libc::c_char = dc_chat_get_name(sel_chat); - dc_log_info( - context, - 0i32, - b"%s#%i: %s [%s]%s\x00" as *const u8 as *const libc::c_char, + 0, + "{}#{}: {} [{}]{}", chat_prefix(sel_chat), dc_chat_get_id(sel_chat), - temp_name_0, - temp2, + to_str(temp_name), + to_str(temp2), if 0 != dc_chat_is_sending_locations(sel_chat) { - b"\xf0\x9f\x93\x8d\x00" as *const u8 as *const libc::c_char + "📍" } else { - b"\x00" as *const u8 as *const libc::c_char + "" }, ); - free(temp_name_0 as *mut libc::c_void); + free(temp_name as *mut libc::c_void); free(temp2 as *mut libc::c_void); if !msglist.is_null() { log_msglist(context, msglist); dc_array_unref(msglist); } - let draft: *mut dc_msg_t = dc_get_draft(context, dc_chat_get_id(sel_chat)); + let draft = dc_get_draft(context, dc_chat_get_id(sel_chat)); if !draft.is_null() { - log_msg( - context, - b"Draft\x00" as *const u8 as *const libc::c_char, - draft, - ); + log_msg(context, "Draft", draft); dc_msg_unref(draft); } - ret = dc_mprintf( - b"%i messages.\x00" as *const u8 as *const libc::c_char, - dc_get_msg_cnt(context, dc_chat_get_id(sel_chat)), + println!( + "{} messages.", + dc_get_msg_cnt(context, dc_chat_get_id(sel_chat)) ); dc_marknoticed_chat(context, dc_chat_get_id(sel_chat)); - } else { - ret = dc_strdup(b"No chat selected.\x00" as *const u8 as *const libc::c_char) } - } else if strcmp(cmd, b"createchat\x00" as *const u8 as *const libc::c_char) == 0i32 { - if !arg1.is_empty() { + "createchat" => { + ensure!(!arg1.is_empty(), "Argument missing."); let contact_id: libc::c_int = arg1.parse().unwrap(); let chat_id: libc::c_int = dc_create_chat_by_contact_id(context, contact_id as uint32_t) as libc::c_int; - ret = if chat_id != 0i32 { - dc_mprintf( - b"Single#%lu created successfully.\x00" as *const u8 as *const libc::c_char, - chat_id, - ) + if chat_id != 0 { + println!("Single#{} created successfully.", chat_id,); } else { - 1i32 as *mut libc::c_char + bail!("Failed to create chat"); } - } else { - ret = dc_strdup( - b"ERROR: Argument missing.\x00" as *const u8 as *const libc::c_char, - ) } - } else if strcmp( - cmd, - b"createchatbymsg\x00" as *const u8 as *const libc::c_char, - ) == 0i32 - { - if !arg1.is_empty() { + "createchatbymsg" => { + ensure!(!arg1.is_empty(), "Argument missing"); let msg_id_0: libc::c_int = arg1.parse().unwrap(); let chat_id_0: libc::c_int = dc_create_chat_by_msg_id(context, msg_id_0 as uint32_t) as libc::c_int; - if chat_id_0 != 0i32 { + if chat_id_0 != 0 { let chat_0: *mut dc_chat_t = dc_get_chat(context, chat_id_0 as uint32_t); - ret = dc_mprintf( - b"%s#%lu created successfully.\x00" as *const u8 as *const libc::c_char, + println!( + "{}#{} created successfully.", chat_prefix(chat_0), chat_id_0, ); dc_chat_unref(chat_0); } else { - ret = 1i32 as *mut libc::c_char + bail!(""); } - } else { - ret = dc_strdup( - b"ERROR: Argument missing.\x00" as *const u8 as *const libc::c_char, - ) } - } else if strcmp(cmd, b"creategroup\x00" as *const u8 as *const libc::c_char) == 0i32 { - if !arg1.is_empty() { + "creategroup" => { + ensure!(!arg1.is_empty(), "Argument missing."); let chat_id_1: libc::c_int = - dc_create_group_chat(context, 0i32, arg1_c_ptr) as libc::c_int; - ret = if chat_id_1 != 0i32 { - dc_mprintf( - b"Group#%lu created successfully.\x00" as *const u8 as *const libc::c_char, - chat_id_1, - ) + dc_create_group_chat(context, 0, arg1_c_ptr) as libc::c_int; + if chat_id_1 != 0 { + println!("Group#{} created successfully.", chat_id_1,); } else { - 1i32 as *mut libc::c_char + bail!("Failed to create group"); } - } else { - ret = dc_strdup( - b"ERROR: Argument missing.\x00" as *const u8 as *const libc::c_char, - ) } - } else if strcmp( - cmd, - b"createverified\x00" as *const u8 as *const libc::c_char, - ) == 0i32 - { - if !arg1.is_empty() { + "createverified" => { + ensure!(!arg1.is_empty(), "Argument missing."); let chat_id_2: libc::c_int = - dc_create_group_chat(context, 1i32, arg1_c_ptr) as libc::c_int; - ret = if chat_id_2 != 0i32 { - dc_mprintf( - b"VerifiedGroup#%lu created successfully.\x00" as *const u8 - as *const libc::c_char, - chat_id_2, - ) + dc_create_group_chat(context, 1, arg1_c_ptr) as libc::c_int; + if chat_id_2 != 0 { + println!("VerifiedGroup#{} created successfully.", chat_id_2,); } else { - 1i32 as *mut libc::c_char + bail!("Failed to create verified group"); } - } else { - ret = dc_strdup( - b"ERROR: Argument missing.\x00" as *const u8 as *const libc::c_char, - ) } - } else if strcmp(cmd, b"addmember\x00" as *const u8 as *const libc::c_char) == 0i32 { - if !sel_chat.is_null() { - if !arg1.is_empty() { - let contact_id_0: libc::c_int = arg1.parse().unwrap(); - if 0 != dc_add_contact_to_chat( - context, - dc_chat_get_id(sel_chat), - contact_id_0 as uint32_t, - ) { - ret = - dc_strdup(b"Contact added to chat.\x00" as *const u8 as *const libc::c_char) - } else { - ret = dc_strdup( - b"ERROR: Cannot add contact to chat.\x00" as *const u8 - as *const libc::c_char, - ) - } - } else { - ret = dc_strdup( - b"ERROR: Argument missing.\x00" as *const u8 - as *const libc::c_char, - ) - } - } else { - ret = dc_strdup(b"No chat selected.\x00" as *const u8 as *const libc::c_char) - } - } else if strcmp(cmd, b"removemember\x00" as *const u8 as *const libc::c_char) == 0i32 { - if !sel_chat.is_null() { - if !arg1.is_empty() { - let contact_id_1: libc::c_int = arg1.parse().unwrap(); - if 0 != dc_remove_contact_from_chat( - context, - dc_chat_get_id(sel_chat), - contact_id_1 as uint32_t, - ) { - ret = - dc_strdup(b"Contact added to chat.\x00" as *const u8 as *const libc::c_char) - } else { - ret = dc_strdup( - b"ERROR: Cannot remove member from chat.\x00" as *const u8 - as *const libc::c_char, - ) - } - } else { - ret = dc_strdup( - b"ERROR: Argument missing.\x00" as *const u8 - as *const libc::c_char, - ) - } - } else { - ret = dc_strdup(b"No chat selected.\x00" as *const u8 as *const libc::c_char) - } - } else if strcmp(cmd, b"groupname\x00" as *const u8 as *const libc::c_char) == 0i32 { - if !sel_chat.is_null() { - if !arg1.is_empty() { - ret = if 0 != dc_set_chat_name(context, dc_chat_get_id(sel_chat), arg1_c_ptr) { - 2i32 as *mut libc::c_char - } else { - 1i32 as *mut libc::c_char - } - } else { - ret = dc_strdup( - b"ERROR: Argument missing.\x00" as *const u8 as *const libc::c_char, - ) - } - } else { - ret = dc_strdup(b"No chat selected.\x00" as *const u8 as *const libc::c_char) - } - } else if strcmp(cmd, b"groupimage\x00" as *const u8 as *const libc::c_char) == 0i32 { - if !sel_chat.is_null() { - ret = if 0 - != dc_set_chat_profile_image( - context, - dc_chat_get_id(sel_chat), - if !arg1.is_empty() { - arg1_c_ptr - } else { - 0 as *mut libc::c_char - }, - ) { - 2i32 as *mut libc::c_char - } else { - 1i32 as *mut libc::c_char - } - } else { - ret = dc_strdup(b"No chat selected.\x00" as *const u8 as *const libc::c_char) - } - } else if strcmp(cmd, b"chatinfo\x00" as *const u8 as *const libc::c_char) == 0i32 { - if !sel_chat.is_null() { - let contacts: *mut dc_array_t = dc_get_chat_contacts(context, dc_chat_get_id(sel_chat)); - if !contacts.is_null() { - dc_log_info( - context, - 0i32, - b"Memberlist:\x00" as *const u8 as *const libc::c_char, - ); - log_contactlist(context, contacts); - ret = dc_mprintf( - b"%i contacts\nLocation streaming: %i\x00" as *const u8 as *const libc::c_char, - dc_array_get_cnt(contacts) as libc::c_int, - dc_is_sending_locations_to_chat(context, dc_chat_get_id(sel_chat)), - ); - dc_array_unref(contacts); - } else { - ret = 1i32 as *mut libc::c_char - } - } else { - ret = dc_strdup(b"No chat selected.\x00" as *const u8 as *const libc::c_char) - } - } else if strcmp(cmd, b"getlocations\x00" as *const u8 as *const libc::c_char) == 0i32 { - let contact_id_2: libc::c_int = if !arg1.is_empty() { - arg1.parse().unwrap() - } else { - 0i32 - }; - let loc: *mut dc_array_t = dc_get_locations( - context, - dc_chat_get_id(sel_chat), - contact_id_2 as uint32_t, - 0, - 0, - ); - let mut j = 0; - while j < dc_array_get_cnt(loc) { - let timestr_0: *mut libc::c_char = - dc_timestamp_to_str(dc_array_get_timestamp(loc, j as size_t)); - let marker: *mut libc::c_char = dc_array_get_marker(loc, j as size_t); - dc_log_info( + "addmember" => { + ensure!(!sel_chat.is_null(), "No chat selected"); + ensure!(!arg1.is_empty(), "Argument missing."); + + let contact_id_0: libc::c_int = arg1.parse().unwrap(); + if 0 != dc_add_contact_to_chat( context, - 0i32, - b"Loc#%i: %s: lat=%f lng=%f acc=%f Chat#%i Contact#%i Msg#%i %s\x00" as *const u8 - as *const libc::c_char, - dc_array_get_id(loc, j as size_t), - timestr_0, - dc_array_get_latitude(loc, j as size_t), - dc_array_get_longitude(loc, j as size_t), - dc_array_get_accuracy(loc, j as size_t), - dc_array_get_chat_id(loc, j as size_t), - dc_array_get_contact_id(loc, j as size_t), - dc_array_get_msg_id(loc, j as size_t), - if !marker.is_null() { - marker + dc_chat_get_id(sel_chat), + contact_id_0 as uint32_t, + ) { + println!("Contact added to chat."); + } else { + bail!("Cannot add contact to chat."); + } + } + "removemember" => { + ensure!(!sel_chat.is_null(), "No chat selected."); + ensure!(!arg1.is_empty(), "Argument missing."); + let contact_id_1: libc::c_int = arg1.parse().unwrap(); + if 0 != dc_remove_contact_from_chat( + context, + dc_chat_get_id(sel_chat), + contact_id_1 as uint32_t, + ) { + println!("Contact added to chat."); + } else { + bail!("Cannot remove member from chat."); + } + } + "groupname" => { + ensure!(!sel_chat.is_null(), "No chat selected."); + ensure!(!arg1.is_empty(), "Argument missing."); + if 0 != dc_set_chat_name(context, dc_chat_get_id(sel_chat), arg1_c_ptr) { + println!("Chat name set"); + } else { + bail!("Failed to set chat name"); + } + } + "groupimage" => { + ensure!(!sel_chat.is_null(), "No chat selected."); + ensure!(!arg1.is_empty(), "Argument missing."); + + if 0 != dc_set_chat_profile_image( + context, + dc_chat_get_id(sel_chat), + if !arg1.is_empty() { + arg1_c_ptr } else { - b"-\x00" as *const u8 as *const libc::c_char + std::ptr::null_mut() }, - ); - free(timestr_0 as *mut libc::c_void); - free(marker as *mut libc::c_void); - j += 1 - } - if dc_array_get_cnt(loc) == 0 { - dc_log_info( - context, - 0i32, - b"No locations.\x00" as *const u8 as *const libc::c_char, - ); - } - dc_array_unref(loc); - ret = 2i32 as *mut libc::c_char - } else if strcmp( - cmd, - b"sendlocations\x00" as *const u8 as *const libc::c_char, - ) == 0i32 - { - if !sel_chat.is_null() { - if !arg1.is_empty() { - let seconds: libc::c_int = arg1.parse().unwrap(); - dc_send_locations_to_chat(context, dc_chat_get_id(sel_chat), seconds); - ret = - dc_mprintf(b"Locations will be sent to Chat#%i for %i seconds. Use \'setlocation \' to play around.\x00" - as *const u8 as *const libc::c_char, - dc_chat_get_id(sel_chat), seconds) + ) { + println!("Chat image set"); } else { - ret = dc_strdup(b"ERROR: No timeout given.\x00" as *const u8 as *const libc::c_char) + bail!("Failed to set chat image"); } - } else { - ret = dc_strdup(b"No chat selected.\x00" as *const u8 as *const libc::c_char) } - } else if strcmp(cmd, b"setlocation\x00" as *const u8 as *const libc::c_char) == 0i32 { - if !arg1.is_empty() && !arg2.is_empty() { - let latitude: libc::c_double = arg1.parse().unwrap(); - let longitude: libc::c_double = arg2.parse().unwrap(); - let continue_streaming: libc::c_int = - dc_set_location(context, latitude, longitude, 0.0f64); - ret = dc_strdup(if 0 != continue_streaming { - b"Success, streaming should be continued.\x00" as *const u8 as *const libc::c_char - } else { - b"Success, streaming can be stoppped.\x00" as *const u8 as *const libc::c_char - }) - } else { - ret = dc_strdup( - b"ERROR: Latitude or longitude not given.\x00" as *const u8 as *const libc::c_char, - ) + "chatinfo" => { + ensure!(!sel_chat.is_null(), "No chat selected."); + + let contacts = dc_get_chat_contacts(context, dc_chat_get_id(sel_chat)); + ensure!(!contacts.is_null(), "Failed to retreive contacts"); + info!(context, 0, "Memberlist:"); + + log_contactlist(context, contacts); + println!( + "{} contacts\nLocation streaming: {}", + dc_array_get_cnt(contacts), + dc_is_sending_locations_to_chat(context, dc_chat_get_id(sel_chat)), + ); + dc_array_unref(contacts); } - } else if strcmp(cmd, b"dellocations\x00" as *const u8 as *const libc::c_char) == 0i32 { - dc_delete_all_locations(context); - ret = 2i32 as *mut libc::c_char - } else if strcmp(cmd, b"send\x00" as *const u8 as *const libc::c_char) == 0i32 { - if !sel_chat.is_null() { - if !arg1.is_empty() { - if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat), arg1_c_ptr) { - ret = dc_strdup(b"Message sent.\x00" as *const u8 as *const libc::c_char) - } else { - ret = - dc_strdup(b"ERROR: Sending failed.\x00" as *const u8 as *const libc::c_char) - } - } else { - ret = dc_strdup( - b"ERROR: No message text given.\x00" as *const u8 as *const libc::c_char, - ) + "getlocations" => { + let contact_id = arg1.parse().unwrap_or_default(); + let loc = dc_get_locations(context, dc_chat_get_id(sel_chat), contact_id, 0, 0); + let mut j = 0; + while j < dc_array_get_cnt(loc) { + let timestr_0 = dc_timestamp_to_str(dc_array_get_timestamp(loc, j as size_t)); + let marker = dc_array_get_marker(loc, j as size_t); + info!( + context, + 0, + "Loc#{}: {}: lat={} lng={} acc={} Chat#{} Contact#{} Msg#{} {}", + dc_array_get_id(loc, j as size_t), + to_str(timestr_0), + dc_array_get_latitude(loc, j as size_t), + dc_array_get_longitude(loc, j as size_t), + dc_array_get_accuracy(loc, j as size_t), + dc_array_get_chat_id(loc, j as size_t), + dc_array_get_contact_id(loc, j as size_t), + dc_array_get_msg_id(loc, j as size_t), + if !marker.is_null() { + to_str(marker) + } else { + "-" + }, + ); + free(timestr_0 as *mut libc::c_void); + free(marker as *mut libc::c_void); + j += 1 } - } else { - ret = dc_strdup(b"No chat selected.\x00" as *const u8 as *const libc::c_char) + if dc_array_get_cnt(loc) == 0 { + info!(context, 0, "No locations."); + } + dc_array_unref(loc); } - } else if strcmp(cmd, b"sendempty\x00" as *const u8 as *const libc::c_char) == 0i32 { - if !sel_chat.is_null() { + "sendlocations" => { + ensure!(!sel_chat.is_null(), "No chat selected."); + ensure!(!arg1.is_empty(), "No timeout given."); + + let seconds = arg1.parse().unwrap(); + dc_send_locations_to_chat(context, dc_chat_get_id(sel_chat), seconds); + println!("Locations will be sent to Chat#{} for {} seconds. Use \'setlocation \' to play around.", dc_chat_get_id(sel_chat), seconds); + } + "setlocation" => { + ensure!( + !arg1.is_empty() && !arg2.is_empty(), + "Latitude or longitude not given." + ); + let latitude = arg1.parse().unwrap(); + let longitude = arg2.parse().unwrap(); + + let continue_streaming = dc_set_location(context, latitude, longitude, 0.); + if 0 != continue_streaming { + println!("Success, streaming should be continued."); + } else { + println!("Success, streaming can be stoppped."); + } + } + "dellocations" => { + dc_delete_all_locations(context); + } + "send" => { + ensure!(!sel_chat.is_null(), "No chat selected."); + ensure!(!arg1.is_empty(), "No message text given."); + + let msg = to_cstring(format!("{} {}", arg1, arg2)); + + if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat), msg.as_ptr()) { + println!("Message sent."); + } else { + bail!("Sending failed."); + } + } + "sendempty" => { + ensure!(!sel_chat.is_null(), "No chat selected."); if 0 != dc_send_text_msg( context, dc_chat_get_id(sel_chat), b"\x00" as *const u8 as *const libc::c_char, ) { - ret = dc_strdup(b"Message sent.\x00" as *const u8 as *const libc::c_char) + println!("Message sent."); } else { - ret = dc_strdup(b"ERROR: Sending failed.\x00" as *const u8 as *const libc::c_char) + bail!("Sending failed."); } - } else { - ret = dc_strdup(b"No chat selected.\x00" as *const u8 as *const libc::c_char) } - } else if strcmp(cmd, b"sendimage\x00" as *const u8 as *const libc::c_char) == 0i32 - || strcmp(cmd, b"sendfile\x00" as *const u8 as *const libc::c_char) == 0i32 - { - if !sel_chat.is_null() { - if !arg1.is_empty() && !arg2.is_empty() { - let msg_0: *mut dc_msg_t = dc_msg_new( - context, - if strcmp(cmd, b"sendimage\x00" as *const u8 as *const libc::c_char) == 0i32 { - 20i32 - } else { - 60i32 - }, - ); - dc_msg_set_file(msg_0, arg1_c_ptr, 0 as *const libc::c_char); - dc_msg_set_text(msg_0, arg2_c.as_ptr()); - dc_send_msg(context, dc_chat_get_id(sel_chat), msg_0); - dc_msg_unref(msg_0); - ret = 2i32 as *mut libc::c_char + "sendimage" | "sendfile" => { + ensure!(!sel_chat.is_null(), "No chat selected."); + ensure!(!arg1.is_empty() && !arg2.is_empty(), "No file given."); + + let msg_0 = dc_msg_new(context, if arg0 == "sendimage" { 20 } else { 60 }); + dc_msg_set_file(msg_0, arg1_c_ptr, 0 as *const libc::c_char); + dc_msg_set_text(msg_0, arg2_c_ptr); + dc_send_msg(context, dc_chat_get_id(sel_chat), msg_0); + dc_msg_unref(msg_0); + } + "listmsgs" => { + ensure!(!arg1.is_empty(), "Argument missing."); + + let chat = if !sel_chat.is_null() { + dc_chat_get_id(sel_chat) } else { - ret = dc_strdup(b"ERROR: No file given.\x00" as *const u8 as *const libc::c_char) - } - } else { - ret = dc_strdup(b"No chat selected.\x00" as *const u8 as *const libc::c_char) - } - } else if strcmp(cmd, b"listmsgs\x00" as *const u8 as *const libc::c_char) == 0i32 { - if !arg1.is_empty() { - let msglist_0: *mut dc_array_t = dc_search_msgs( - context, - if !sel_chat.is_null() { - dc_chat_get_id(sel_chat) - } else { - 0i32 as libc::c_uint - }, - arg1_c_ptr, - ); + 0 as libc::c_uint + }; + + let msglist_0 = dc_search_msgs(context, chat, arg1_c_ptr); + if !msglist_0.is_null() { log_msglist(context, msglist_0); - ret = dc_mprintf( - b"%i messages.\x00" as *const u8 as *const libc::c_char, - dc_array_get_cnt(msglist_0) as libc::c_int, - ); + println!("{} messages.", dc_array_get_cnt(msglist_0)); dc_array_unref(msglist_0); } - } else { - ret = dc_strdup( - b"ERROR: Argument missing.\x00" as *const u8 as *const libc::c_char, - ) } - } else if strcmp(cmd, b"draft\x00" as *const u8 as *const libc::c_char) == 0i32 { - if !sel_chat.is_null() { + "draft" => { + ensure!(!sel_chat.is_null(), "No chat selected."); + if !arg1.is_empty() { - let draft_0: *mut dc_msg_t = dc_msg_new(context, 10i32); + let draft_0 = dc_msg_new(context, 10); dc_msg_set_text(draft_0, arg1_c_ptr); dc_set_draft(context, dc_chat_get_id(sel_chat), draft_0); dc_msg_unref(draft_0); - ret = dc_strdup(b"Draft saved.\x00" as *const u8 as *const libc::c_char) + println!("Draft saved."); } else { dc_set_draft(context, dc_chat_get_id(sel_chat), 0 as *mut dc_msg_t); - ret = dc_strdup(b"Draft deleted.\x00" as *const u8 as *const libc::c_char) + println!("Draft deleted."); } - } else { - ret = dc_strdup(b"No chat selected.\x00" as *const u8 as *const libc::c_char) } - } else if strcmp(cmd, b"listmedia\x00" as *const u8 as *const libc::c_char) == 0i32 { - if !sel_chat.is_null() { - let images: *mut dc_array_t = - dc_get_chat_media(context, dc_chat_get_id(sel_chat), 20i32, 21i32, 50i32); - let mut i_0: libc::c_int; - let icnt: libc::c_int = dc_array_get_cnt(images) as libc::c_int; - ret = dc_mprintf( - b"%i images or videos: \x00" as *const u8 as *const libc::c_char, - icnt, - ); - i_0 = 0i32; - while i_0 < icnt { - let temp: *mut libc::c_char = dc_mprintf( - b"%s%sMsg#%i\x00" as *const u8 as *const libc::c_char, - if 0 != i_0 { - b", \x00" as *const u8 as *const libc::c_char - } else { - b"\x00" as *const u8 as *const libc::c_char - }, - ret, - dc_array_get_id(images, i_0 as size_t) as libc::c_int, - ); - free(ret as *mut libc::c_void); - ret = temp; - i_0 += 1 - } - dc_array_unref(images); - } else { - ret = dc_strdup(b"No chat selected.\x00" as *const u8 as *const libc::c_char) - } - } else if strcmp(cmd, b"archive\x00" as *const u8 as *const libc::c_char) == 0i32 - || strcmp(cmd, b"unarchive\x00" as *const u8 as *const libc::c_char) == 0i32 - { - if !arg1.is_empty() { - let chat_id_3: libc::c_int = arg1.parse().unwrap(); - dc_archive_chat( - context, - chat_id_3 as uint32_t, - if strcmp(cmd, b"archive\x00" as *const u8 as *const libc::c_char) == 0i32 { - 1i32 - } else { - 0i32 - }, - ); - ret = 2i32 as *mut libc::c_char - } else { - ret = dc_strdup( - b"ERROR: Argument missing.\x00" as *const u8 as *const libc::c_char, - ) - } - } else if strcmp(cmd, b"delchat\x00" as *const u8 as *const libc::c_char) == 0i32 { - if !arg1.is_empty() { - let chat_id_4: libc::c_int = arg1.parse().unwrap(); - dc_delete_chat(context, chat_id_4 as uint32_t); - ret = 2i32 as *mut libc::c_char - } else { - ret = dc_strdup( - b"ERROR: Argument missing.\x00" as *const u8 as *const libc::c_char, - ) - } - } else if strcmp(cmd, b"msginfo\x00" as *const u8 as *const libc::c_char) == 0i32 { - if !arg1.is_empty() { - let id: libc::c_int = arg1.parse().unwrap(); - ret = dc_get_msg_info(context, id as uint32_t) - } else { - ret = dc_strdup( - b"ERROR: Argument missing.\x00" as *const u8 as *const libc::c_char, - ) - } - } else if strcmp(cmd, b"listfresh\x00" as *const u8 as *const libc::c_char) == 0i32 { - let msglist_1: *mut dc_array_t = dc_get_fresh_msgs(context); - if !msglist_1.is_null() { - log_msglist(context, msglist_1); - ret = dc_mprintf( - b"%i fresh messages.\x00" as *const u8 as *const libc::c_char, - dc_array_get_cnt(msglist_1) as libc::c_int, - ); - dc_array_unref(msglist_1); - } - } else if strcmp(cmd, b"forward\x00" as *const u8 as *const libc::c_char) == 0i32 { - if !arg1.is_empty() && !arg2.is_empty() { - let mut msg_ids: [uint32_t; 1] = [0; 1]; - let chat_id_5: uint32_t = arg2.parse().unwrap(); - msg_ids[0usize] = arg1.parse().unwrap(); - dc_forward_msgs(context, msg_ids.as_mut_ptr(), 1i32, chat_id_5); - ret = 2i32 as *mut libc::c_char - } else { - ret = dc_strdup( - b"ERROR: Arguments expected.\x00" as *const u8 - as *const libc::c_char, - ) - } - } else if strcmp(cmd, b"markseen\x00" as *const u8 as *const libc::c_char) == 0i32 { - if !arg1.is_empty() { - let mut msg_ids_0: [uint32_t; 1] = [0; 1]; - msg_ids_0[0usize] = arg1.parse().unwrap(); - dc_markseen_msgs(context, msg_ids_0.as_mut_ptr(), 1i32); - ret = 2i32 as *mut libc::c_char - } else { - ret = dc_strdup( - b"ERROR: Argument missing.\x00" as *const u8 as *const libc::c_char, - ) - } - } else if strcmp(cmd, b"star\x00" as *const u8 as *const libc::c_char) == 0i32 - || strcmp(cmd, b"unstar\x00" as *const u8 as *const libc::c_char) == 0i32 - { - if !arg1.is_empty() { - let mut msg_ids_1: [uint32_t; 1] = [0; 1]; - msg_ids_1[0] = arg1.parse().unwrap(); - dc_star_msgs( - context, - msg_ids_1.as_mut_ptr(), - 1i32, - if strcmp(cmd, b"star\x00" as *const u8 as *const libc::c_char) == 0i32 { - 1i32 - } else { - 0i32 - }, - ); - ret = 2i32 as *mut libc::c_char - } else { - ret = dc_strdup( - b"ERROR: Argument missing.\x00" as *const u8 as *const libc::c_char, - ) - } - } else if strcmp(cmd, b"delmsg\x00" as *const u8 as *const libc::c_char) == 0i32 { - if !arg1.is_empty() { - let mut ids: [uint32_t; 1] = [0; 1]; - ids[0usize] = arg1.parse().unwrap(); - dc_delete_msgs(context, ids.as_mut_ptr(), 1i32); - ret = 2i32 as *mut libc::c_char - } else { - ret = dc_strdup( - b"ERROR: Argument missing.\x00" as *const u8 as *const libc::c_char, - ) - } - } else if strcmp(cmd, b"listcontacts\x00" as *const u8 as *const libc::c_char) == 0i32 - || strcmp(cmd, b"contacts\x00" as *const u8 as *const libc::c_char) == 0i32 - || strcmp(cmd, b"listverified\x00" as *const u8 as *const libc::c_char) == 0i32 - { - let contacts_0: *mut dc_array_t = dc_get_contacts( - context, - (if strcmp(cmd, b"listverified\x00" as *const u8 as *const libc::c_char) == 0i32 { - 0x1i32 | 0x2i32 - } else { - 0x2i32 - }) as uint32_t, - arg1_c_ptr, - ); - if !contacts_0.is_null() { - log_contactlist(context, contacts_0); - ret = dc_mprintf( - b"%i contacts.\x00" as *const u8 as *const libc::c_char, - dc_array_get_cnt(contacts_0) as libc::c_int, - ); - dc_array_unref(contacts_0); - } else { - ret = 1i32 as *mut libc::c_char - } - } else if strcmp(cmd, b"addcontact\x00" as *const u8 as *const libc::c_char) == 0i32 { - if !arg1.is_empty() && !arg2.is_empty() { - let book: *mut libc::c_char = dc_mprintf( - b"%s\n%s\x00" as *const u8 as *const libc::c_char, - arg1_c_ptr, - arg2_c.as_ptr(), - ); - dc_add_address_book(context, book); - ret = 2i32 as *mut libc::c_char; - free(book as *mut libc::c_void); - } else if !arg1.is_empty() { - ret = if 0 != dc_create_contact(context, 0 as *const libc::c_char, arg1_c_ptr) { - 2i32 as *mut libc::c_char - } else { - 1i32 as *mut libc::c_char - } - } else { - ret = dc_strdup( - b"ERROR: Arguments [] expected.\x00" as *const u8 - as *const libc::c_char, - ) - } - } else if strcmp(cmd, b"contactinfo\x00" as *const u8 as *const libc::c_char) == 0i32 { - if !arg1.is_empty() { - let contact_id_3: libc::c_int = arg1.parse().unwrap(); - let mut strbuilder: dc_strbuilder_t = dc_strbuilder_t { - buf: 0 as *mut libc::c_char, - allocated: 0, - free: 0, - eos: 0 as *mut libc::c_char, - }; + "listmedia" => { + ensure!(!sel_chat.is_null(), "No chat selected."); - dc_strbuilder_init(&mut strbuilder, 0i32); - let contact: *mut dc_contact_t = dc_get_contact(context, contact_id_3 as uint32_t); - let nameNaddr: *mut libc::c_char = dc_contact_get_name_n_addr(contact); - dc_strbuilder_catf( - &mut strbuilder as *mut dc_strbuilder_t, - b"Contact info for: %s:\n\n\x00" as *const u8 as *const libc::c_char, - nameNaddr, - ); - free(nameNaddr as *mut libc::c_void); - dc_contact_unref(contact); - let encrinfo: *mut libc::c_char = - dc_get_contact_encrinfo(context, contact_id_3 as uint32_t); - dc_strbuilder_cat(&mut strbuilder, encrinfo); - free(encrinfo as *mut libc::c_void); - let chatlist_0: *mut dc_chatlist_t = dc_get_chatlist( - context, - 0i32, - 0 as *const libc::c_char, - contact_id_3 as uint32_t, - ); - let chatlist_cnt: libc::c_int = dc_chatlist_get_cnt(chatlist_0) as libc::c_int; - if chatlist_cnt > 0i32 { - dc_strbuilder_catf( - &mut strbuilder as *mut dc_strbuilder_t, - b"\n\n%i chats shared with Contact#%i: \x00" as *const u8 - as *const libc::c_char, - chatlist_cnt, - contact_id_3, - ); - let mut i_1: libc::c_int = 0i32; - while i_1 < chatlist_cnt { - if 0 != i_1 { - dc_strbuilder_cat( - &mut strbuilder, - b", \x00" as *const u8 as *const libc::c_char, - ); - } - let chat_1: *mut dc_chat_t = - dc_get_chat(context, dc_chatlist_get_chat_id(chatlist_0, i_1 as size_t)); - dc_strbuilder_catf( - &mut strbuilder as *mut dc_strbuilder_t, - b"%s#%i\x00" as *const u8 as *const libc::c_char, - chat_prefix(chat_1), - dc_chat_get_id(chat_1), - ); - dc_chat_unref(chat_1); - i_1 += 1 + let images = dc_get_chat_media(context, dc_chat_get_id(sel_chat), 20, 21, 50); + let icnt: libc::c_int = dc_array_get_cnt(images) as libc::c_int; + println!("{} images or videos: ", icnt); + for i in 0..icnt { + let data = dc_array_get_id(images, i as size_t); + if 0 == i { + print!("Msg#{}", data); + } else { + print!(", Msg#{}", data); } } - dc_chatlist_unref(chatlist_0); - ret = strbuilder.buf - } else { - ret = dc_strdup( - b"ERROR: Argument missing.\x00" as *const u8 as *const libc::c_char, - ) + print!("\n"); + dc_array_unref(images); } - } else if strcmp(cmd, b"delcontact\x00" as *const u8 as *const libc::c_char) == 0i32 { - if !arg1.is_empty() { - ret = if dc_delete_contact(context, arg1.parse().unwrap()) { - 2i32 as *mut libc::c_char + "archive" | "unarchive" => { + ensure!(!arg1.is_empty(), "Argument missing."); + let chat_id = arg1.parse().unwrap(); + dc_archive_chat(context, chat_id, if arg0 == "archive" { 1 } else { 0 }); + } + "delchat" => { + ensure!(!arg1.is_empty(), "Argument missing."); + let chat_id = arg1.parse().unwrap(); + dc_delete_chat(context, chat_id); + } + "msginfo" => { + ensure!(!arg1.is_empty(), "Argument missing."); + let id = arg1.parse().unwrap(); + let res = dc_get_msg_info(context, id); + println!("{}", to_str(res)); + } + "listfresh" => { + let msglist = dc_get_fresh_msgs(context); + ensure!(!msglist.is_null(), "Failed to retrieve messages"); + + log_msglist(context, msglist); + print!("{} fresh messages.", dc_array_get_cnt(msglist)); + dc_array_unref(msglist); + } + "forward" => { + ensure!( + !arg1.is_empty() && arg2.is_empty(), + "Arguments expected" + ); + + let mut msg_ids = [0; 1]; + let chat_id = arg2.parse().unwrap(); + msg_ids[0] = arg1.parse().unwrap(); + dc_forward_msgs(context, msg_ids.as_mut_ptr(), 1, chat_id); + } + "markseen" => { + ensure!(!arg1.is_empty(), "Argument missing."); + let mut msg_ids = [0; 1]; + msg_ids[0] = arg1.parse().unwrap(); + dc_markseen_msgs(context, msg_ids.as_mut_ptr(), 1); + } + "star" | "unstar" => { + ensure!(!arg1.is_empty(), "Argument missing."); + let mut msg_ids = [0; 1]; + msg_ids[0] = arg1.parse().unwrap(); + dc_star_msgs( + context, + msg_ids.as_mut_ptr(), + 1, + if arg0 == "star" { 1 } else { 0 }, + ); + } + "delmsg" => { + ensure!(!arg1.is_empty(), "Argument missing."); + let mut ids = [0; 1]; + ids[0] = arg1.parse().unwrap(); + dc_delete_msgs(context, ids.as_mut_ptr(), 1); + } + "listcontacts" | "contacts" | "listverified" => { + let contacts = dc_get_contacts( + context, + if arg0 == "listverified" { + 0x1 | 0x2 + } else { + 0x2 + }, + arg1_c_ptr, + ); + if !contacts.is_null() { + log_contactlist(context, contacts); + println!("{} contacts.", dc_array_get_cnt(contacts) as libc::c_int,); + dc_array_unref(contacts); } else { - 1i32 as *mut libc::c_char + bail!(""); } - } else { - ret = dc_strdup( - b"ERROR: Argument missing.\x00" as *const u8 as *const libc::c_char, - ) } - } else if strcmp(cmd, b"getqr\x00" as *const u8 as *const libc::c_char) == 0i32 { - ret = dc_get_securejoin_qr( - context, - (if !arg1.is_empty() { - arg1.parse().unwrap() + "addcontact" => { + ensure!(!arg1.is_empty(), "Arguments [] expected."); + + if !arg2.is_empty() { + let book = dc_mprintf( + b"%s\n%s\x00" as *const u8 as *const libc::c_char, + arg1_c_ptr, + arg2_c_ptr, + ); + dc_add_address_book(context, book); + free(book as *mut libc::c_void); } else { - 0i32 - }) as uint32_t, - ); - if ret.is_null() || *ret.offset(0isize) as libc::c_int == 0i32 { - free(ret as *mut libc::c_void); - ret = 1i32 as *mut libc::c_char + if 0 == dc_create_contact(context, 0 as *const libc::c_char, arg1_c_ptr) { + bail!("Failed to create contact"); + } + } } - } else if strcmp(cmd, b"checkqr\x00" as *const u8 as *const libc::c_char) == 0i32 { - if !arg1.is_empty() { - let res: *mut dc_lot_t = dc_check_qr(context, arg1_c_ptr); - ret = dc_mprintf( - b"state=%i, id=%i, text1=%s, text2=%s\x00" as *const u8 as *const libc::c_char, + "contactinfo" => { + ensure!(!arg1.is_empty(), "Argument missing."); + + let contact_id = arg1.parse().unwrap(); + let contact = dc_get_contact(context, contact_id); + let name_n_addr = dc_contact_get_name_n_addr(contact); + + let mut res = format!("Contact info for: {}:\n\n", to_str(name_n_addr),); + free(name_n_addr as *mut libc::c_void); + dc_contact_unref(contact); + + let encrinfo = dc_get_contact_encrinfo(context, contact_id); + res += to_str(encrinfo); + free(encrinfo as *mut libc::c_void); + + let chatlist = dc_get_chatlist(context, 0, 0 as *const libc::c_char, contact_id); + let chatlist_cnt = dc_chatlist_get_cnt(chatlist) as libc::c_int; + if chatlist_cnt > 0 { + res += &format!( + "\n\n{} chats shared with Contact#{}: ", + chatlist_cnt, contact_id, + ); + for i in 0..chatlist_cnt { + if 0 != i { + res += ", "; + } + let chat = dc_get_chat(context, dc_chatlist_get_chat_id(chatlist, i as size_t)); + res += &format!("{}#{}", chat_prefix(chat), dc_chat_get_id(chat)); + dc_chat_unref(chat); + } + } + dc_chatlist_unref(chatlist); + println!("{}", res); + } + "delcontact" => { + ensure!(!arg1.is_empty(), "Argument missing."); + if !dc_delete_contact(context, arg1.parse().unwrap()) { + bail!("Failed to delete contact"); + } + } + "checkqr" => { + ensure!(!arg1.is_empty(), "Argument missing."); + let res = dc_check_qr(context, arg1_c_ptr); + println!( + "state={}, id={}, text1={}, text2={}", (*res).state as libc::c_int, (*res).id, - if !(*res).text1.is_null() { - (*res).text1 - } else { - b"\x00" as *const u8 as *const libc::c_char - }, - if !(*res).text2.is_null() { - (*res).text2 - } else { - b"\x00" as *const u8 as *const libc::c_char - }, + to_string((*res).text1), + to_string((*res).text2) ); dc_lot_unref(res); - } else { - ret = dc_strdup( - b"ERROR: Argument missing.\x00" as *const u8 as *const libc::c_char, - ) } - } else if strcmp(cmd, b"event\x00" as *const u8 as *const libc::c_char) == 0i32 { - if !arg1.is_empty() { + "event" => { + ensure!(!arg1.is_empty(), "Argument missing."); let event = Event::from_u32(arg1.parse().unwrap()).unwrap(); - let r: uintptr_t = (context.cb)(context, event, 0i32 as uintptr_t, 0i32 as uintptr_t); - ret = dc_mprintf( - b"Sending event %i, received value %i.\x00" as *const u8 as *const libc::c_char, - event as libc::c_int, - r as libc::c_int, - ) - } else { - ret = - dc_strdup(b"ERROR: Argument missing.\x00" as *const u8 as *const libc::c_char) + let r = (context.cb)(context, event, 0 as uintptr_t, 0 as uintptr_t); + println!( + "Sending event {:?}({}), received value {}.", + event, event as usize, r as libc::c_int, + ); } - } else if strcmp(cmd, b"fileinfo\x00" as *const u8 as *const libc::c_char) == 0i32 { - if !arg1.is_empty() { - let mut buf: *mut libc::c_uchar = 0 as *mut libc::c_uchar; - let mut buf_bytes: size_t = 0; - let mut w: uint32_t = 0; - let mut h: uint32_t = 0; + "fileinfo" => { + ensure!(!arg1.is_empty(), "Argument missing."); + let mut buf = 0 as *mut libc::c_uchar; + let mut buf_bytes = 0; + let mut w = 0; + let mut h = 0; if 0 != dc_read_file( context, @@ -1591,38 +1205,18 @@ pub unsafe fn dc_cmdline(context: &Context, cmdline: &str) -> *mut libc::c_char &mut buf_bytes, ) { dc_get_filemeta(buf as *const libc::c_void, buf_bytes, &mut w, &mut h); - ret = dc_mprintf( - b"width=%i, height=%i\x00" as *const u8 as *const libc::c_char, - w as libc::c_int, - h as libc::c_int, - ) + println!("width={}, height={}", w, h,); + free(buf as *mut libc::c_void); } else { - ret = dc_strdup(b"ERROR: Command failed.\x00" as *const u8 as *const libc::c_char) + bail!("Command failed."); } - free(buf as *mut libc::c_void); - } else { - ret = dc_strdup( - b"ERROR: Argument missing.\x00" as *const u8 as *const libc::c_char, - ) } - } else { - ret = 3i32 as *mut libc::c_char + _ => bail!("Unknown command: \"{}\" type ? for help.", arg0), } - if ret == 2i32 as *mut libc::c_char { - ret = dc_strdup(b"Command executed successfully.\x00" as *const u8 as *const libc::c_char) - } else if ret == 1i32 as *mut libc::c_char { - ret = dc_strdup(b"ERROR: Command failed.\x00" as *const u8 as *const libc::c_char) - } else if ret == 3i32 as *mut libc::c_char { - ret = dc_mprintf( - b"ERROR: Unknown command \"%s\", type ? for help.\x00" as *const u8 - as *const libc::c_char, - cmd, - ) - } if !sel_chat.is_null() { dc_chat_unref(sel_chat); } - free(cmd as *mut libc::c_void); - ret + + Ok(()) } diff --git a/examples/repl/main.rs b/examples/repl/main.rs index 41fc28126..9f15714b1 100644 --- a/examples/repl/main.rs +++ b/examples/repl/main.rs @@ -1,23 +1,19 @@ //! This is a CLI program and a little testing frame. This file must not be //! included when using Delta Chat Core as a library. //! -//! Usage: messenger-backend -//! (for "Code::Blocks, use Project / Set programs' arguments") -//! all further options can be set using the set-command (type ? for help). +//! Usage: cargo run --example repl --release -- +//! All further options can be set using the set-command (type ? for help). -#![allow( - mutable_transmutes, - non_camel_case_types, - non_snake_case, - non_upper_case_globals, - non_upper_case_globals, - non_camel_case_types, - non_snake_case -)] +#[macro_use] +extern crate deltachat; +#[macro_use] +extern crate failure; +#[macro_use] +extern crate lazy_static; -use std::ffi::CString; -use std::io::{self, Write}; -use std::sync::{Arc, RwLock}; +use std::borrow::Cow::{self, Borrowed, Owned}; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, Mutex, RwLock}; use deltachat::constants::*; use deltachat::context::*; @@ -28,13 +24,19 @@ use deltachat::dc_tools::*; use deltachat::oauth2::*; use deltachat::types::*; use deltachat::x::*; +use rustyline::completion::{Completer, FilenameCompleter, Pair}; +use rustyline::config::OutputStreamType; +use rustyline::error::ReadlineError; +use rustyline::highlight::{Highlighter, MatchingBracketHighlighter}; +use rustyline::hint::{Hinter, HistoryHinter}; +use rustyline::{ + Cmd, CompletionType, Config, Context as RustyContext, EditMode, Editor, Helper, KeyPress, +}; mod cmdline; use self::cmdline::*; -/* ****************************************************************************** - * Event Handler - ******************************************************************************/ +// Event Handler unsafe extern "C" fn receive_event( _context: &Context, @@ -42,259 +44,479 @@ unsafe extern "C" fn receive_event( data1: uintptr_t, data2: uintptr_t, ) -> uintptr_t { - match event as u32 { - 2091 => {} - 100 => { + match event { + Event::GET_STRING => {} + Event::INFO => { /* do not show the event as this would fill the screen */ println!("{}", to_string(data2 as *const _),); } - 101 => { + Event::SMTP_CONNECTED => { println!("[DC_EVENT_SMTP_CONNECTED] {}", to_string(data2 as *const _)); } - 102 => { + Event::IMAP_CONNECTED => { println!("[DC_EVENT_IMAP_CONNECTED] {}", to_string(data2 as *const _),); } - 103 => { + Event::SMTP_MESSAGE_SENT => { println!( "[DC_EVENT_SMTP_MESSAGE_SENT] {}", to_string(data2 as *const _), ); } - 300 => { + Event::WARNING => { println!("[Warning] {}", to_string(data2 as *const _),); } - 400 => { + Event::ERROR => { println!( "\x1b[31m[DC_EVENT_ERROR] {}\x1b[0m", to_string(data2 as *const _), ); } - 401 => { + Event::ERROR_NETWORK => { println!( "\x1b[31m[DC_EVENT_ERROR_NETWORK] first={}, msg={}\x1b[0m", - data1 as libc::c_int, + data1 as usize, to_string(data2 as *const _), ); } - 410 => { + Event::ERROR_SELF_NOT_IN_GROUP => { println!( "\x1b[31m[DC_EVENT_ERROR_SELF_NOT_IN_GROUP] {}\x1b[0m", to_string(data2 as *const _), ); } - 2081 => { - print!("\x1b[33m{{Received DC_EVENT_IS_OFFLINE()}}\n\x1b[0m"); - } - 2000 => { + Event::MSGS_CHANGED => { print!( "\x1b[33m{{Received DC_EVENT_MSGS_CHANGED({}, {})}}\n\x1b[0m", - data1 as libc::c_int, data2 as libc::c_int, + data1 as usize, data2 as usize, ); } - 2030 => { + Event::CONTACTS_CHANGED => { print!("\x1b[33m{{Received DC_EVENT_CONTACTS_CHANGED()}}\n\x1b[0m"); } - 2035 => { + Event::LOCATION_CHANGED => { print!( "\x1b[33m{{Received DC_EVENT_LOCATION_CHANGED(contact={})}}\n\x1b[0m", - data1 as libc::c_int, + data1 as usize, ); } - 2041 => { + Event::CONFIGURE_PROGRESS => { print!( "\x1b[33m{{Received DC_EVENT_CONFIGURE_PROGRESS({} ‰)}}\n\x1b[0m", - data1 as libc::c_int, + data1 as usize, ); } - 2051 => { + Event::IMEX_PROGRESS => { print!( "\x1b[33m{{Received DC_EVENT_IMEX_PROGRESS({} ‰)}}\n\x1b[0m", - data1 as libc::c_int, + data1 as usize, ); } - 2052 => { + Event::IMEX_FILE_WRITTEN => { print!( "\x1b[33m{{Received DC_EVENT_IMEX_FILE_WRITTEN({})}}\n\x1b[0m", to_string(data1 as *const _) ); } - 2055 => { - print!( - "\x1b[33m{{Received DC_EVENT_FILE_COPIED({})}}\n\x1b[0m", - to_string(data1 as *const _) - ); - } - 2020 => { + Event::CHAT_MODIFIED => { print!( "\x1b[33m{{Received DC_EVENT_CHAT_MODIFIED({})}}\n\x1b[0m", - data1 as libc::c_int, + data1 as usize, ); } _ => { print!( - "\x1b[33m{{Received DC_EVENT_{}({}, {})}}\n\x1b[0m", - event as libc::c_int, data1 as libc::c_int, data2 as libc::c_int, + "\x1b[33m{{Received {:?}({}, {})}}\n\x1b[0m", + event, data1 as usize, data2 as usize, ); } } - return 0i32 as uintptr_t; -} -/* ****************************************************************************** - * Threads for waiting for messages and for jobs - ******************************************************************************/ -static mut run_threads: libc::c_int = 0i32; -unsafe fn start_threads( - c: Arc>, -) -> ( - std::thread::JoinHandle<()>, - std::thread::JoinHandle<()>, - std::thread::JoinHandle<()>, - std::thread::JoinHandle<()>, -) { - run_threads = 1; + 0 +} + +// Threads for waiting for messages and for jobs + +lazy_static! { + static ref HANDLE: Arc>> = Arc::new(Mutex::new(None)); + static ref IS_RUNNING: AtomicBool = AtomicBool::new(true); +} + +struct Handle { + handle_imap: Option>, + handle_mvbox: Option>, + handle_sentbox: Option>, + handle_smtp: Option>, +} + +macro_rules! while_running { + ($code:block) => { + if IS_RUNNING.load(Ordering::Relaxed) { + $code + } else { + break; + } + }; +} + +fn start_threads(c: Arc>) { + if HANDLE.clone().lock().unwrap().is_some() { + return; + } + + println!("Starting threads"); + IS_RUNNING.store(true, Ordering::Relaxed); let ctx = c.clone(); - let h1 = std::thread::spawn(move || { - let context = ctx.read().unwrap(); - while 0 != run_threads { - dc_perform_imap_jobs(&context); - dc_perform_imap_fetch(&context); - if 0 != run_threads { + let handle_imap = std::thread::spawn(move || loop { + while_running!({ + unsafe { + dc_perform_imap_jobs(&ctx.read().unwrap()); + dc_perform_imap_fetch(&ctx.read().unwrap()); + } + while_running!({ + let context = ctx.read().unwrap(); dc_perform_imap_idle(&context); - } - } + }); + }); }); let ctx = c.clone(); - let h2 = std::thread::spawn(move || { - let context = ctx.read().unwrap(); - while 0 != run_threads { - dc_perform_mvbox_fetch(&context); - if 0 != run_threads { - dc_perform_mvbox_idle(&context); - } - } + let handle_mvbox = std::thread::spawn(move || loop { + while_running!({ + unsafe { dc_perform_mvbox_fetch(&ctx.read().unwrap()) }; + while_running!({ + unsafe { dc_perform_mvbox_idle(&ctx.read().unwrap()) }; + }); + }); }); let ctx = c.clone(); - let h3 = std::thread::spawn(move || { - let context = ctx.read().unwrap(); - while 0 != run_threads { - dc_perform_sentbox_fetch(&context); - if 0 != run_threads { - dc_perform_sentbox_idle(&context); - } - } + let handle_sentbox = std::thread::spawn(move || loop { + while_running!({ + unsafe { dc_perform_sentbox_fetch(&ctx.read().unwrap()) }; + while_running!({ + unsafe { dc_perform_sentbox_idle(&ctx.read().unwrap()) }; + }); + }); }); - let ctx = c.clone(); - let h4 = std::thread::spawn(move || { - let context = ctx.read().unwrap(); - while 0 != run_threads { - dc_perform_smtp_jobs(&context); - if 0 != run_threads { - dc_perform_smtp_idle(&context); - } - } + let ctx = c; + let handle_smtp = std::thread::spawn(move || loop { + while_running!({ + unsafe { dc_perform_smtp_jobs(&ctx.read().unwrap()) }; + while_running!({ + unsafe { dc_perform_smtp_idle(&ctx.read().unwrap()) }; + }); + }); }); - (h1, h2, h3, h4) + *HANDLE.clone().lock().unwrap() = Some(Handle { + handle_imap: Some(handle_imap), + handle_mvbox: Some(handle_mvbox), + handle_sentbox: Some(handle_sentbox), + handle_smtp: Some(handle_smtp), + }); } -unsafe fn stop_threads( - context: &Context, - handles: Option<( - std::thread::JoinHandle<()>, - std::thread::JoinHandle<()>, - std::thread::JoinHandle<()>, - std::thread::JoinHandle<()>, - )>, -) { - run_threads = 0i32; - dc_interrupt_imap_idle(context); - dc_interrupt_mvbox_idle(context); - dc_interrupt_sentbox_idle(context); - dc_interrupt_smtp_idle(context); - if let Some((h1, h2, h3, h4)) = handles { - h1.join().unwrap(); - h2.join().unwrap(); - h3.join().unwrap(); - h4.join().unwrap(); +fn stop_threads(context: &Context) { + if let Some(ref mut handle) = *HANDLE.clone().lock().unwrap() { + println!("Stopping threads"); + IS_RUNNING.store(false, Ordering::Relaxed); + + unsafe { + dc_interrupt_imap_idle(context); + dc_interrupt_mvbox_idle(context); + dc_interrupt_sentbox_idle(context); + dc_interrupt_smtp_idle(context); + } + + handle.handle_imap.take().unwrap().join().unwrap(); + handle.handle_mvbox.take().unwrap().join().unwrap(); + handle.handle_sentbox.take().unwrap().join().unwrap(); + handle.handle_smtp.take().unwrap().join().unwrap(); } } -/* ****************************************************************************** - * The main loop - ******************************************************************************/ -fn read_cmd() -> String { - print!("> "); - io::stdout().flush().unwrap(); +// === The main loop - let mut input = String::new(); - io::stdin().read_line(&mut input).unwrap(); - input.trim_end().to_string() +struct DcHelper { + completer: FilenameCompleter, + highlighter: MatchingBracketHighlighter, + hinter: HistoryHinter, + colored_prompt: String, } -unsafe fn main_0(argc: libc::c_int, argv: *mut *mut libc::c_char) -> libc::c_int { - let mut cmd: *mut libc::c_char = 0 as *mut libc::c_char; +impl Completer for DcHelper { + type Candidate = Pair; + + fn complete( + &self, + line: &str, + pos: usize, + ctx: &RustyContext<'_>, + ) -> Result<(usize, Vec), ReadlineError> { + self.completer.complete(line, pos, ctx) + } +} + +const IMEX_COMMANDS: [&'static str; 12] = [ + "initiate-key-transfer", + "get-setupcodebegin", + "continue-key-transfer", + "has-backup", + "export-backup", + "import-backup", + "export-keys", + "import-keys", + "export-setup", + "poke", + "reset", + "stop", +]; + +const DB_COMMANDS: [&'static str; 11] = [ + "info", + "open", + "close", + "set", + "get", + "oauth2", + "configure", + "connect", + "disconnect", + "maybenetwork", + "housekeeping", +]; + +const CHAT_COMMANDS: [&'static str; 24] = [ + "listchats", + "listarchived", + "chat", + "createchat", + "createchatbymsg", + "creategroup", + "createverified", + "addmember", + "removemember", + "groupname", + "groupimage", + "chatinfo", + "sendlocations", + "setlocation", + "dellocations", + "getlocations", + "send", + "sendimage", + "sendfile", + "draft", + "listmedia", + "archive", + "unarchive", + "delchat", +]; +const MESSAGE_COMMANDS: [&'static str; 8] = [ + "listmsgs", + "msginfo", + "listfresh", + "forward", + "markseen", + "star", + "unstar", + "delmsg", +]; +const CONTACT_COMMANDS: [&'static str; 6] = [ + "listcontacts", + "listverified", + "addcontact", + "contactinfo", + "delcontact", + "cleanupcontacts", +]; +const MISC_COMMANDS: [&'static str; 8] = [ + "getqr", "getbadqr", "checkqr", "event", "fileinfo", "clear", "exit", "help", +]; + +impl Hinter for DcHelper { + fn hint(&self, line: &str, pos: usize, ctx: &RustyContext<'_>) -> Option { + if !line.is_empty() { + for &cmds in &[ + &IMEX_COMMANDS[..], + &DB_COMMANDS[..], + &CHAT_COMMANDS[..], + &MESSAGE_COMMANDS[..], + &CONTACT_COMMANDS[..], + &MISC_COMMANDS[..], + ] { + if let Some(entry) = cmds.iter().find(|el| el.starts_with(&line[..pos])) { + if *entry != line && *entry != &line[..pos] { + return Some(entry[pos..].to_owned()); + } + } + } + } + self.hinter.hint(line, pos, ctx) + } +} + +impl Highlighter for DcHelper { + fn highlight_prompt<'b, 's: 'b, 'p: 'b>( + &'s self, + prompt: &'p str, + default: bool, + ) -> Cow<'b, str> { + if default { + Borrowed(&self.colored_prompt) + } else { + Borrowed(prompt) + } + } + + fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> { + Owned("\x1b[1m".to_owned() + hint + "\x1b[m") + } + + fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> { + self.highlighter.highlight(line, pos) + } + + fn highlight_char(&self, line: &str, pos: usize) -> bool { + self.highlighter.highlight_char(line, pos) + } +} + +impl Helper for DcHelper {} + +fn main_0(args: Vec) -> Result<(), failure::Error> { let mut context = dc_context_new( receive_event, 0 as *mut libc::c_void, b"CLI\x00" as *const u8 as *const libc::c_char, ); - dc_cmdline_skip_auth(); + unsafe { dc_cmdline_skip_auth() }; - if argc == 2i32 { - if 0 == dc_open(&mut context, *argv.offset(1isize), 0 as *const libc::c_char) { - println!( - "ERROR: Cannot open {}.", - to_string(*argv.offset(1isize) as *const _) - ); + if args.len() == 2 { + if 0 == unsafe { + dc_open( + &mut context, + to_cstring(&args[1]).as_ptr(), + 0 as *const libc::c_char, + ) + } { + println!("Error: Cannot open {}.", args[0],); } - } else if argc != 1i32 { - println!("ERROR: Bad arguments"); + } else if args.len() != 1 { + println!("Error: Bad arguments, expected [db-name]."); } println!("Delta Chat Core is awaiting your commands."); - let mut handles = None; - let ctx = Arc::new(RwLock::new(context)); + let config = Config::builder() + .history_ignore_space(true) + .completion_type(CompletionType::List) + .edit_mode(EditMode::Emacs) + .output_stream(OutputStreamType::Stdout) + .build(); + let h = DcHelper { + completer: FilenameCompleter::new(), + highlighter: MatchingBracketHighlighter::new(), + hinter: HistoryHinter {}, + colored_prompt: "".to_owned(), + }; + let mut rl = Editor::with_config(config); + rl.set_helper(Some(h)); + rl.bind_sequence(KeyPress::Meta('N'), Cmd::HistorySearchForward); + rl.bind_sequence(KeyPress::Meta('P'), Cmd::HistorySearchBackward); + if rl.load_history(".dc-history.txt").is_err() { + println!("No previous history."); + } + loop { - /* read command */ - let cmdline = read_cmd(); - free(cmd as *mut libc::c_void); - cmd = dc_strdup(CString::new(cmdline.clone()).unwrap().as_ptr()); - let mut arg1: *mut libc::c_char = strchr(cmd, ' ' as i32); - if !arg1.is_null() { - *arg1 = 0i32 as libc::c_char; - arg1 = arg1.offset(1isize) + let p = "> "; + rl.helper_mut().unwrap().colored_prompt = format!("\x1b[1;32m{}\x1b[0m", p); + let readline = rl.readline(&p); + match readline { + Ok(line) => { + // TODO: ignore "set mail_pw" + rl.add_history_entry(line.as_str()); + let ctx = ctx.clone(); + match unsafe { handle_cmd(line.as_str(), ctx) } { + Ok(ExitResult::Continue) => {} + Ok(ExitResult::Exit) => break, + Err(err) => println!("Error: {}", err), + } + } + Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => { + println!("Exiting..."); + break; + } + Err(err) => { + println!("Error: {}", err); + break; + } } - if strcmp(cmd, b"connect\x00" as *const u8 as *const libc::c_char) == 0i32 { - handles = Some(start_threads(ctx.clone())); - } else if strcmp(cmd, b"disconnect\x00" as *const u8 as *const libc::c_char) == 0i32 { - stop_threads(&ctx.read().unwrap(), handles); - handles = None; - } else if strcmp(cmd, b"smtp-jobs\x00" as *const u8 as *const libc::c_char) == 0i32 { - if 0 != run_threads { + } + rl.save_history(".dc-history.txt")?; + println!("history saved"); + { + stop_threads(&ctx.read().unwrap()); + + unsafe { + let mut ctx = ctx.write().unwrap(); + dc_close(&mut ctx); + dc_context_unref(&mut ctx); + } + } + + Ok(()) +} + +#[derive(Debug)] +enum ExitResult { + Continue, + Exit, +} + +unsafe fn handle_cmd(line: &str, ctx: Arc>) -> Result { + let mut args = line.splitn(2, ' '); + let arg0 = args.next().unwrap_or_default(); + let arg1 = args.next().unwrap_or_default(); + let arg1_c = to_cstring(arg1); + let arg1_c_ptr = if arg1.is_empty() { + std::ptr::null() + } else { + arg1_c.as_ptr() + }; + + match arg0 { + "connect" => { + start_threads(ctx); + } + "disconnect" => { + stop_threads(&ctx.read().unwrap()); + } + "smtp-jobs" => { + if HANDLE.clone().lock().unwrap().is_some() { println!("smtp-jobs are already running in a thread.",); } else { dc_perform_smtp_jobs(&ctx.read().unwrap()); } - } else if strcmp(cmd, b"imap-jobs\x00" as *const u8 as *const libc::c_char) == 0i32 { - if 0 != run_threads { + } + "imap-jobs" => { + if HANDLE.clone().lock().unwrap().is_some() { println!("imap-jobs are already running in a thread."); } else { dc_perform_imap_jobs(&ctx.read().unwrap()); } - } else if strcmp(cmd, b"configure\x00" as *const u8 as *const libc::c_char) == 0i32 { - handles = { Some(start_threads(ctx.clone())) }; + } + "configure" => { + start_threads(ctx.clone()); dc_configure(&ctx.read().unwrap()); - } else if strcmp(cmd, b"oauth2\x00" as *const u8 as *const libc::c_char) == 0i32 { - let addr: *mut libc::c_char = dc_get_config( + } + "oauth2" => { + let addr = dc_get_config( &ctx.read().unwrap(), b"addr\x00" as *const u8 as *const libc::c_char, ); @@ -313,19 +535,17 @@ unsafe fn main_0(argc: libc::c_int, argv: *mut *mut libc::c_char) -> libc::c_int } } free(addr as *mut libc::c_void); - } else if strcmp(cmd, b"clear\x00" as *const u8 as *const libc::c_char) == 0i32 { + } + "clear" => { println!("\n\n\n"); print!("\x1b[1;1H\x1b[2J"); - } else if strcmp(cmd, b"getqr\x00" as *const u8 as *const libc::c_char) == 0i32 - || strcmp(cmd, b"getbadqr\x00" as *const u8 as *const libc::c_char) == 0i32 - { - handles = Some(start_threads(ctx.clone())); - let qrstr: *mut libc::c_char = - dc_get_securejoin_qr(&ctx.read().unwrap(), dc_atoi_null_is_0(arg1) as u32); + } + "getqr" | "getbadqr" => { + start_threads(ctx.clone()); + let qrstr = + dc_get_securejoin_qr(&ctx.read().unwrap(), arg1.parse().unwrap_or_default()); if !qrstr.is_null() && 0 != *qrstr.offset(0isize) as libc::c_int { - if strcmp(cmd, b"getbadqr\x00" as *const u8 as *const libc::c_char) == 0i32 - && strlen(qrstr) > 40 - { + if arg0 == "getbadqr" && strlen(qrstr) > 40 { let mut i: libc::c_int = 12i32; while i < 22i32 { *qrstr.offset(i as isize) = '0' as i32 as libc::c_char; @@ -333,7 +553,7 @@ unsafe fn main_0(argc: libc::c_int, argv: *mut *mut libc::c_char) -> libc::c_int } } println!("{}", to_string(qrstr as *const _)); - let syscmd: *mut libc::c_char = dc_mprintf( + let syscmd = dc_mprintf( b"qrencode -t ansiutf8 \"%s\" -o -\x00" as *const u8 as *const libc::c_char, qrstr, ); @@ -341,55 +561,25 @@ unsafe fn main_0(argc: libc::c_int, argv: *mut *mut libc::c_char) -> libc::c_int free(syscmd as *mut libc::c_void); } free(qrstr as *mut libc::c_void); - } else if strcmp(cmd, b"joinqr\x00" as *const u8 as *const libc::c_char) == 0i32 { - handles = Some(start_threads(ctx.clone())); - if !arg1.is_null() { - dc_join_securejoin(&ctx.read().unwrap(), arg1); - } - } else { - if strcmp(cmd, b"exit\x00" as *const u8 as *const libc::c_char) == 0i32 { - break; - } - if !(*cmd.offset(0isize) as libc::c_int == 0i32) { - let execute_result: *mut libc::c_char = dc_cmdline(&ctx.read().unwrap(), &cmdline); - if !execute_result.is_null() { - println!("{}", to_string(execute_result as *const _)); - free(execute_result as *mut libc::c_void); - } + } + "joinqr" => { + start_threads(ctx.clone()); + if !arg0.is_empty() { + dc_join_securejoin(&ctx.read().unwrap(), arg1_c_ptr); } } + "exit" => return Ok(ExitResult::Exit), + _ => dc_cmdline(&ctx.read().unwrap(), line)?, } - let ctx = ctx.clone(); - - { - let mut ctx = ctx.write().unwrap(); - free(cmd as *mut libc::c_void); - stop_threads(&ctx, handles); - dc_close(&mut ctx); - dc_context_unref(&mut ctx); - } - 0 + Ok(ExitResult::Continue) } -pub fn main() { +pub fn main() -> Result<(), failure::Error> { let _ = pretty_env_logger::try_init(); - let mut args: Vec<*mut libc::c_char> = Vec::new(); - for arg in ::std::env::args() { - args.push( - ::std::ffi::CString::new(arg) - .expect("Failed to convert argument into CString.") - .into_raw(), - ); - } - args.push(::std::ptr::null_mut()); + let args: Vec = std::env::args().collect(); + main_0(args)?; - let res = unsafe { - main_0( - (args.len() - 1) as libc::c_int, - args.as_mut_ptr() as *mut *mut libc::c_char, - ) - }; - ::std::process::exit(res) + Ok(()) } diff --git a/src/dc_chat.rs b/src/dc_chat.rs index 0afa4495b..77f6aebbd 100644 --- a/src/dc_chat.rs +++ b/src/dc_chat.rs @@ -740,7 +740,7 @@ unsafe fn prepare_msg_raw( VALUES (?,?,?, ?,?,1);\x00" as *const u8 as *const libc::c_char, ); - sqlite3_bind_int64(stmt, 1, timestamp as libc::int64_t); + sqlite3_bind_int64(stmt, 1, timestamp as i64); sqlite3_bind_int(stmt, 2, DC_CONTACT_ID_SELF as libc::c_int); sqlite3_bind_int(stmt, 3, (*chat).id as libc::c_int); sqlite3_bind_double( diff --git a/src/dc_log.rs b/src/dc_log.rs index a68c7c5e5..9fdc3052a 100644 --- a/src/dc_log.rs +++ b/src/dc_log.rs @@ -96,10 +96,10 @@ macro_rules! info { }; ($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {{ let formatted = format!($msg, $($args),*); - let formatted_c = crate::dc_tools::to_cstring(formatted); + let formatted_c = $crate::dc_tools::to_cstring(formatted); unsafe { - ($ctx.cb)($ctx, crate::constants::Event::INFO, $data1 as uintptr_t, - crate::dc_tools::dc_strdup(formatted_c.as_ptr()) as uintptr_t) + ($ctx.cb)($ctx, $crate::constants::Event::INFO, $data1 as uintptr_t, + $crate::dc_tools::dc_strdup(formatted_c.as_ptr()) as uintptr_t) } }}; } @@ -111,10 +111,10 @@ macro_rules! warn { }; ($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => { let formatted = format!($msg, $($args),*); - let formatted_c = crate::dc_tools::to_cstring(formatted); + let formatted_c = $crate::dc_tools::to_cstring(formatted); unsafe { - ($ctx.cb)($ctx, crate::constants::Event::WARNING, $data1 as libc::uintptr_t, - crate::dc_tools::dc_strdup(formatted_c.as_ptr()) as libc::uintptr_t) + ($ctx.cb)($ctx, $crate::constants::Event::WARNING, $data1 as libc::uintptr_t, + $crate::dc_tools::dc_strdup(formatted_c.as_ptr()) as libc::uintptr_t) } }; } @@ -126,10 +126,10 @@ macro_rules! error { }; ($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => { let formatted = format!($msg, $($args),*); - let formatted_c = crate::dc_tools::to_cstring(formatted); + let formatted_c = $crate::dc_tools::to_cstring(formatted); unsafe { - ($ctx.cb)($ctx, crate::constants::Event::ERROR, $data1 as uintptr_t, - crate::dc_tools::dc_strdup(formatted_c.as_ptr()) as uintptr_t) + ($ctx.cb)($ctx, $crate::constants::Event::ERROR, $data1 as uintptr_t, + $crate::dc_tools::dc_strdup(formatted_c.as_ptr()) as uintptr_t) } }; } @@ -141,10 +141,10 @@ macro_rules! log_event { }; ($ctx:expr, $event:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => { let formatted = format!($msg, $($args),*); - let formatted_c = crate::dc_tools::to_cstring(formatted); + let formatted_c = $crate::dc_tools::to_cstring(formatted); unsafe { ($ctx.cb)($ctx, $event, $data1 as uintptr_t, - crate::dc_tools::dc_strdup(formatted_c.as_ptr()) as uintptr_t) + $crate::dc_tools::dc_strdup(formatted_c.as_ptr()) as uintptr_t) } }; } diff --git a/src/types.rs b/src/types.rs index 364e60c85..328fd17e5 100644 --- a/src/types.rs +++ b/src/types.rs @@ -38,15 +38,15 @@ pub type dc_set_config_t = pub type dc_get_config_t = unsafe fn(_: &Context, _: *const libc::c_char, _: *const libc::c_char) -> *mut libc::c_char; -pub type sqlite_int64 = libc::int64_t; +pub type sqlite_int64 = i64; pub type sqlite3_int64 = sqlite_int64; -pub type int32_t = libc::int32_t; -pub type int64_t = libc::int64_t; +pub type int32_t = i32; +pub type int64_t = i64; pub type uintptr_t = libc::uintptr_t; pub type size_t = libc::size_t; pub type ssize_t = libc::ssize_t; pub type uint32_t = libc::c_uint; pub type uint8_t = libc::c_uchar; pub type uint16_t = libc::c_ushort; -pub type uint64_t = libc::uint64_t; +pub type uint64_t = u64; From dc7d23ec4730898fd8825756042c994a78dae7bc Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Sun, 2 Jun 2019 19:35:07 +0200 Subject: [PATCH 3/5] fix(x): do not enforce valid utf8 in strndup Closes #112 --- src/x.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/x.rs b/src/x.rs index bacb5d900..0f619b652 100644 --- a/src/x.rs +++ b/src/x.rs @@ -1,5 +1,4 @@ use crate::dc_strbuilder::dc_strbuilder_t; -use crate::dc_tools::*; use crate::types::*; pub use libc::{ @@ -27,9 +26,14 @@ pub fn strndup(s: *const libc::c_char, n: libc::c_ulong) -> *mut libc::c_char { return std::ptr::null_mut(); } - let s_r = to_str(s); - let end = std::cmp::min(n as usize, s_r.len()); - unsafe { strdup(to_cstring(&s_r[..end]).as_ptr()) } + let end = std::cmp::min(n as usize, unsafe { strlen(s) }); + unsafe { + let result = malloc(end + 1); + memcpy(result, s as *const _, end); + std::ptr::write_bytes(result.offset(end as isize), b'\x00', 1); + + result as *mut _ + } } extern "C" { From 05e097baaa06d9dcd62f8544a008ffdc8ce6e1b8 Mon Sep 17 00:00:00 2001 From: "B. Petersen" Date: Tue, 4 Jun 2019 16:49:28 +0200 Subject: [PATCH 4/5] trim() commandline as rustyline adds lineends to commands eg. from inside IntelliJ IDEA --- examples/repl/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/repl/main.rs b/examples/repl/main.rs index 9f15714b1..b2cd2e514 100644 --- a/examples/repl/main.rs +++ b/examples/repl/main.rs @@ -442,7 +442,7 @@ fn main_0(args: Vec) -> Result<(), failure::Error> { // TODO: ignore "set mail_pw" rl.add_history_entry(line.as_str()); let ctx = ctx.clone(); - match unsafe { handle_cmd(line.as_str(), ctx) } { + match unsafe { handle_cmd(line.trim(), ctx) } { Ok(ExitResult::Continue) => {} Ok(ExitResult::Exit) => break, Err(err) => println!("Error: {}", err), From 9a0a451999ae34d9ea2a1943a7fd75db69146833 Mon Sep 17 00:00:00 2001 From: Lars-Magnus Skog Date: Thu, 6 Jun 2019 13:02:40 +0200 Subject: [PATCH 5/5] chore: use rustyline@4.1.0 for now (#128) * switch to rustyline 4.1.0 * adapt highlight_prompt to 4.1.0 format * fix rust fmt --- Cargo.toml | 2 +- examples/repl/cmdline.rs | 7 +------ examples/repl/main.rs | 16 ++++++---------- 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 60a702d9e..15e5112d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ serde_json = "1.0" chrono = "0.4.6" failure = "0.1.5" # TODO: make optional -rustyline = { git = "https://github.com/kkawakam/rustyline/" } +rustyline = "4.1.0" lazy_static = "1.3.0" [dev-dependencies] diff --git a/examples/repl/cmdline.rs b/examples/repl/cmdline.rs index f51ca2e88..de7666e70 100644 --- a/examples/repl/cmdline.rs +++ b/examples/repl/cmdline.rs @@ -83,12 +83,7 @@ pub unsafe fn dc_reset_tables(context: &Context, bits: i32) -> i32 { info!(context, 0, "(8) Rest but server config reset."); } - (context.cb)( - context, - Event::MSGS_CHANGED, - 0 as uintptr_t, - 0 as uintptr_t, - ); + (context.cb)(context, Event::MSGS_CHANGED, 0 as uintptr_t, 0 as uintptr_t); 1 } diff --git a/examples/repl/main.rs b/examples/repl/main.rs index b2cd2e514..3832b73d3 100644 --- a/examples/repl/main.rs +++ b/examples/repl/main.rs @@ -243,7 +243,6 @@ struct DcHelper { completer: FilenameCompleter, highlighter: MatchingBracketHighlighter, hinter: HistoryHinter, - colored_prompt: String, } impl Completer for DcHelper { @@ -358,14 +357,13 @@ impl Hinter for DcHelper { } } +static COLORED_PROMPT: &'static str = "\x1b[1;32m> \x1b[0m"; +static PROMPT: &'static str = "> "; + impl Highlighter for DcHelper { - fn highlight_prompt<'b, 's: 'b, 'p: 'b>( - &'s self, - prompt: &'p str, - default: bool, - ) -> Cow<'b, str> { - if default { - Borrowed(&self.colored_prompt) + fn highlight_prompt<'p>(&self, prompt: &'p str) -> Cow<'p, str> { + if prompt == PROMPT { + Borrowed(COLORED_PROMPT) } else { Borrowed(prompt) } @@ -423,7 +421,6 @@ fn main_0(args: Vec) -> Result<(), failure::Error> { completer: FilenameCompleter::new(), highlighter: MatchingBracketHighlighter::new(), hinter: HistoryHinter {}, - colored_prompt: "".to_owned(), }; let mut rl = Editor::with_config(config); rl.set_helper(Some(h)); @@ -435,7 +432,6 @@ fn main_0(args: Vec) -> Result<(), failure::Error> { loop { let p = "> "; - rl.helper_mut().unwrap().colored_prompt = format!("\x1b[1;32m{}\x1b[0m", p); let readline = rl.readline(&p); match readline { Ok(line) => {