1414 * limitations under the License.
1515 */
1616
17- use crate :: core:: config:: get_config_file ;
17+ use crate :: core:: config:: default_config_file ;
1818use crate :: core:: version;
1919use clap:: { arg, Parser } ;
20- use std:: env;
21- use std:: ffi:: CString ;
20+ use std:: ffi:: { c_char, CString } ;
2221use std:: path:: PathBuf ;
22+ use std:: slice;
2323
2424// Struct to hold the command line arguments
2525#[ derive( Parser , Debug ) ]
@@ -57,17 +57,18 @@ pub struct CliArgs {
5757impl CliArgs {
5858 /// Verifies the command line arguments.
5959 pub fn verify ( mut self ) -> Self {
60- let work_dir = env:: current_dir ( ) . unwrap_or_default ( ) ;
61- let config_path: Option < PathBuf > = self . config_file . as_ref ( ) . map ( PathBuf :: from) ;
62-
63- // Resolve the config file path using get_config_file
64- self . config_file = match get_config_file ( & work_dir, config_path) {
65- Ok ( path) => Some ( path. to_string_lossy ( ) . to_string ( ) ) ,
66- Err ( e) => {
67- eprintln ! ( "error: failed to find configuration file: {}" , e) ;
68- std:: process:: exit ( 1 ) ;
69- }
70- } ;
60+ let config_file = self
61+ . config_file
62+ . map ( PathBuf :: from)
63+ . unwrap_or_else ( default_config_file) ;
64+
65+ // FIXME: don't put back as a string after resolving
66+ self . config_file = Some (
67+ config_file
68+ . to_str ( )
69+ . expect ( "path sourced from string should convert" )
70+ . to_string ( ) ,
71+ ) ;
7172
7273 if self . verbose {
7374 self . log_level = 3 ;
@@ -81,12 +82,7 @@ impl CliArgs {
8182 . config_file
8283 . as_ref ( )
8384 . map_or_else (
84- || {
85- let work_dir = std:: env:: current_dir ( ) . unwrap_or_default ( ) ;
86- let default_config = get_config_file ( & work_dir, None )
87- . unwrap_or_else ( |_| "examples/config/pushpin.conf" . into ( ) ) ;
88- CString :: new ( default_config. to_string_lossy ( ) . to_string ( ) ) . unwrap ( )
89- } ,
85+ || CString :: new ( "" ) . unwrap ( ) ,
9086 |s| CString :: new ( s. as_str ( ) ) . unwrap ( ) ,
9187 )
9288 . into_raw ( ) ;
@@ -109,47 +105,47 @@ impl CliArgs {
109105 )
110106 . into_raw ( ) ;
111107
112- let ( routes, routes_count) = if !self . route . is_empty ( ) {
113- // Allocate array of string pointers
114- let routes_array = unsafe {
115- libc:: malloc ( self . route . len ( ) * std:: mem:: size_of :: < * mut libc:: c_char > ( ) )
116- as * mut * mut libc:: c_char
117- } ;
118-
119- // Convert each route to CString and store pointer in array
120- for ( i, item) in self . route . iter ( ) . enumerate ( ) {
121- let c_string = CString :: new ( item. to_string ( ) ) . unwrap ( ) . into_raw ( ) ;
122- unsafe {
123- * routes_array. add ( i) = c_string;
124- }
125- }
126-
127- ( routes_array, self . route . len ( ) as libc:: c_uint )
128- } else {
129- let routes_array = unsafe { libc:: malloc ( 0 ) as * mut * mut libc:: c_char } ;
130- ( routes_array, 0 )
131- } ;
108+ let mut routes: Vec < * mut c_char > = Vec :: new ( ) ;
109+
110+ // Convert each route to CString and store pointer in array
111+ for item in & self . route {
112+ let item = item. as_bytes ( ) ;
113+
114+ // ensure no trailing nul byte
115+ let end = item. iter ( ) . position ( |b| * b == 0 ) . unwrap_or ( item. len ( ) ) ;
116+ let item = & item[ ..end] ;
117+
118+ let c_string = CString :: new ( item) . unwrap ( ) . into_raw ( ) ;
119+ routes. push ( c_string) ;
120+ }
121+
122+ let routes = routes. into_boxed_slice ( ) ;
123+
124+ let routes_count = routes. len ( ) ;
125+ let routes: * mut [ * mut c_char ] = Box :: into_raw ( routes) ;
132126
133127 ffi:: ProxyCliArgs {
134128 config_file,
135129 log_file,
136130 log_level : self . log_level ,
137131 ipc_prefix,
138- routes,
132+ routes : routes as * mut * mut c_char ,
139133 routes_count,
140134 }
141135 }
142136}
143137
144138pub mod ffi {
139+ use std:: ffi:: { c_char, c_uint} ;
140+
145141 #[ repr( C ) ]
146142 pub struct ProxyCliArgs {
147- pub config_file : * mut libc :: c_char ,
148- pub log_file : * mut libc :: c_char ,
149- pub log_level : libc :: c_uint ,
150- pub ipc_prefix : * mut libc :: c_char ,
151- pub routes : * mut * mut libc :: c_char ,
152- pub routes_count : libc:: c_uint ,
143+ pub config_file : * mut c_char ,
144+ pub log_file : * mut c_char ,
145+ pub log_level : c_uint ,
146+ pub ipc_prefix : * mut c_char ,
147+ pub routes : * mut * mut c_char ,
148+ pub routes_count : libc:: size_t ,
153149 }
154150}
155151
@@ -177,15 +173,18 @@ pub unsafe extern "C" fn destroy_proxy_cli_args(ffi_args: ffi::ProxyCliArgs) {
177173 let _ = CString :: from_raw ( ffi_args. ipc_prefix ) ;
178174 }
179175 if !ffi_args. routes . is_null ( ) {
176+ // SAFETY: the raw parts were originally derived from a boxed slice
177+ let routes = unsafe {
178+ Box :: from_raw ( slice:: from_raw_parts_mut (
179+ ffi_args. routes ,
180+ ffi_args. routes_count ,
181+ ) )
182+ } ;
183+
180184 // Free each individual route string
181- for i in 0 ..ffi_args. routes_count {
182- let route_ptr = * ffi_args. routes . add ( i as usize ) ;
183- if !route_ptr. is_null ( ) {
184- let _ = CString :: from_raw ( route_ptr) ;
185- }
185+ for item in routes. iter ( ) {
186+ let _ = CString :: from_raw ( * item) ;
186187 }
187-
188- libc:: free ( ffi_args. routes as * mut libc:: c_void ) ;
189188 }
190189}
191190
@@ -209,8 +208,6 @@ mod tests {
209208 route : vec ! [ "route1" . to_string( ) , "route2" . to_string( ) ] ,
210209 } ;
211210
212- let args_ffi = args. to_ffi ( ) ;
213-
214211 // Test verify() method
215212 let verified_args = args. verify ( ) ;
216213 assert_eq ! ( verified_args. config_file, Some ( config_test_file. clone( ) ) ) ;
@@ -223,6 +220,8 @@ mod tests {
223220 vec![ "route1" . to_string( ) , "route2" . to_string( ) ]
224221 ) ;
225222
223+ let args_ffi = verified_args. to_ffi ( ) ;
224+
226225 // Test conversion to C++-compatible struct
227226 unsafe {
228227 assert_eq ! (
@@ -277,14 +276,9 @@ mod tests {
277276 route : Vec :: new ( ) ,
278277 } ;
279278
280- let empty_args_ffi = empty_args. to_ffi ( ) ;
281-
282279 // Test verify() with empty args
283280 let verified_empty_args = empty_args. verify ( ) ;
284- let default_config_file = get_config_file ( & env:: current_dir ( ) . unwrap ( ) , None )
285- . unwrap ( )
286- . to_string_lossy ( )
287- . to_string ( ) ;
281+ let default_config_file = default_config_file ( ) . to_str ( ) . unwrap ( ) . to_string ( ) ;
288282 assert_eq ! (
289283 verified_empty_args. config_file,
290284 Some ( default_config_file. clone( ) )
@@ -295,6 +289,8 @@ mod tests {
295289 assert_eq ! ( verified_empty_args. ipc_prefix, None ) ;
296290 assert ! ( verified_empty_args. route. is_empty( ) ) ;
297291
292+ let empty_args_ffi = verified_empty_args. to_ffi ( ) ;
293+
298294 // Test conversion to C++-compatible struct
299295 unsafe {
300296 assert_eq ! (
0 commit comments