#How Do I pass a C struct as pointer

56 messages · Page 1 of 1 (latest)

slim barn
#

I wouldn't touch that library's cups bindings with a ten-feet pole. No safety comments and some things are definitely broken. When they call cupsFreeDests they must pass the original pointer returned from cupsGetDests but instead they allocate their own vector and copy just the pointers to the list over, completely broken and probably related to the unsoundness issue #22. get_option_by_key also doesn't validate any of its unsafe pointer dereferences, but it really should. At least a comment why offset 1 is always valid? Chances are the segfault isn't on you

steady ether
#

well, if I change to i32 and pass 0 as the argument for option, it works, I seem to just miss understand how to pass the option struct.
let result = cupsPrintFile(printer_name.as_ptr(), filename.as_ptr(), title.as_ptr(), 0);

wary quarry
#

what do you mean with "change"

#

are you using your own bindings or those written by others

slim barn
steady ether
#

My old way consists in a nodejs(10/16) server that uses node gyp, this is no longer an option.

steady ether
#

so, obviously it does not work, because I put the wrong number of arguments, the function is :

extern int cupsPrintFile(const char *printer, const char *filename,
             const char *title, int num_options,
             cups_option_t * options);```

no this means I need to set number of options and then pass the option struct,

I'll try that,

documentation: https://refspecs.linuxfoundation.org/LSB_4.0.0/LSB-Printing/LSB-Printing.html#LIBCUPS.CUPSPRINTFILES.1
#

omfg it works

#

if I had alcohol at home, I would've start drinking...

wary quarry
#

make a safe wrapper for it and you'll never have to worry about it again

steady ether
#

hmm, the one thing that remains is: how do I pass multiple options not just one ?

let mut printer_option_vec:Vec<CupsOptionT> = Vec::new();

seems is not the way

steady ether
#

nope, nothing works lol.

imma bang my head againt the wall...

empty moss
# steady ether nope, nothing works lol. imma bang my head againt the wall...

you should use a slice:

fn takes_slice([others]... opts: &[CuptsOptionT]) {
  unsafe { cupsPrintFiles(..., /*num_options*/ opts.len() as c_int, /*options*/ opts.as_ptr().cast()); }
}
let arr = [a bunch of CupsOptionT];
takes_slice([others].. &arr);
let vec = vec![more CupsOptionT];
//works with vec too since Vec implements Deref<Target=[T]>
takes_slice([others.. &vec);
#

at least assuming that cups doesnt expect the options array to outlive the body of the function its passed into

steady ether
#

OK, thank you, but I seem to missunderstand stuff,
let's take this simple test

        let option_name = CString::new("copies").unwrap();
        let option_value = CString::new("44").unwrap();

        let option = CupsOptionT {
            name: option_name.as_ptr() as *mut c_char,
            value: option_value.as_ptr() as *mut c_char,
        };
        let cups_option_ptr: *const CupsOptionT = &option;


        let get_opv = CString::new("copies").unwrap();
        let get_options =  cupsGetOption(get_opv.as_ptr(),1,cups_option_ptr);
        let c_str2 = CStr::from_ptr(get_options) ;
        let rust_str2 = c_str2.to_str().unwrap();
        println!("Get Option: {:?}",rust_str2); //prints 44


       
     

       let option_name = CString::new("orientation-requested").unwrap();
       let option_value = CString::new("4").unwrap();
       let n_options =  cupsAddOption(option_name.as_ptr(), option_value.as_ptr(), 1, cups_option_ptr); //Segmentation fault (core dumped)
       let c_str3 = CStr::from_ptr(n_options) ;
       let rust_str3 = c_str3.to_str().unwrap();
       println!("Add Option: {:?}",rust_str3);
  1. IDK how to create a struct in a way that I can add options !
  2. I don't want to create option struct from zero, I want the default option struct that contains options for file printing and then adding or modifying.

In my previous message when I said it works no it didn't I cannot manage to understand how to pass the option struct (or with what) so it will print multiple files,
E.g even if option has copies option it's not taked by

 _result = cupsPrintFile(
                printer_name.as_ptr(),
                filename.as_ptr(),
                title.as_ptr(),
                1,
                cups_option_ptr,
            );
steady ether
#

Aha, you get it from: extern cups_dest_t *cupsGetDest(const char *name, const char *instance, int num_dests, cups_dest_t * dests);

steady ether
#

So,
Given this:

#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct CupsDestT {
    name: *mut c_char,
    instance: *mut c_char,
    is_default: c_int,
    num_options: c_int,
    options: *mut CupsOptionT,
}

I wanted to check the values, so:

      let dests:Vec<&CupsDestT> = get_dests();

       let dest_01:&&CupsDestT =  dests.get(1).unwrap();
       println!("Printer name: {:?}",dest_01.get_name());//PDF
       println!("No. of options from PDF printer: {:?}",dest_01.num_options); //23


       let name_dest_01: *mut CupsOptionT =  dest_01.options;
       let name:&CStr = CStr::from_ptr((*name_dest_01).name);
        let value:&CStr = CStr::from_ptr((*name_dest_01).value);

        println!("Name: {}", name.to_str().unwrap());//copies
        println!("Value: {}", value.to_str().unwrap());//1

        let orig_option:*mut CupsOptionT =  dest_01.options.clone();
       let vall =  orig_option.as_ref().unwrap().value;
       let c_str = CStr::from_ptr(vall) ;
       let rust_str = c_str.to_str().unwrap();
        println!("value option : {:?}",rust_str);//"1"

I suppose I need to change options: *mut CupsOptionT, because I onpy get one option.
can you give me ideas on how to do that, my brain is on fire ...

#

and How do I change the values because:

   let option_name = CString::new("copies").unwrap();
       let option_value = CString::new("4").unwrap();
       let n_options =  cupsAddOption(option_name.as_ptr(), option_value.as_ptr(), dest_01.num_options, orig_option);

gives Segmentation fault (core dumped)

#

I guess not like this:
*orig_option.as_ref().unwrap().value = 4; ///4 as i8 is the same

because instead of "4" it goes like this: "\u{4}"

steady ether
#

I"m done for today, my brain melted 🫠

slim barn
#

cupsAddOption is:

int cupsAddOption(const char * name, const char * value, int num_options, cups_option_t ** options);

In other words, the last argument is a pointer to an array (expressed as pointer-to-pointer-to-cups_option_t). cups wants to be able (re-)allocate that array for you when modifying. You can't pass your own. Instead you'll initially pass a mutable reference to a null-pointer with num_options set to 0. And then always a mutable reference to the modified value with the number as indicated by the new successive return values.

steady ether
#

ok, I'm back from my tent trip

can you please provide me with an example? because I'm not sure I understood, How the struct should look like on the rust part ?

steady ether
#
#[repr(C)]
pub struct CupsOptionT {
    pub name: *mut c_char,
    pub value: *mut c_char,
}```
 
so, is it something like this:

struct MyCupsOptionT {
options: *mut *mut CupsOptionT,
num_options: * mut c_int,
}
impl MyCupsOptionT {
fn new() -> Self {
Self {
options: std::ptr::null_mut(),
num_options: std::ptr::null_mut(),
}
}

fn add_option(&mut self, name: &str, value: &str) -> *const c_char {
    let ret = unsafe { cupsAddOption(name.as_ptr() as *const i8, value.as_ptr() as *const i8, *self.num_options, self.options as *mut *mut CupsOptionT) };
    ret
}

}

I also changed from :
fn cupsAddOption(name: *const c_char,value: *const c_char, num_options: c_int, options: *const CupsOptionT) -> *const c_char; //test
to 
fn cupsAddOption(name: *const c_char,value: *const c_char, num_options: c_int, options: *mut *mut CupsOptionT) -> *const c_char; //test


did I understood right ?
@slim barn   ?
wary quarry
wary quarry
steady ether
wary quarry
#

you will have to show the code you are running

#

it's probably related to what i said earlier about strings not being null terminated

slim barn
#
struct MyCupsOptionT {
    options: *mut CupsOptionT,
     num_options: c_int,
}

impl MyCupsOptionT {
// …
  self.num_options = cupsAddOption(…, …, self.num_options, &mut self.options)
}
steady ether
slim barn
#

Cups is asking you to have a custom vector type essentially, passed in parts via multiple arguments instead of one.

steady ether
#

I'lll try it.

slim barn
#

Oops, one &mut too many. The num_options is passed by value as c_int

wary quarry
# steady ether ^ it's all above.

most of the snippets you have posted so far are incompatible. frankly, i don't care enough to go and piece together a program that may or may not look like what you are actually running. i imagine that the other people can't be bothered either, which is why the advice youve got so far isn't particularly helpful either

#

this is why most comments here are just pointing out various things that are almost certainly wrong, rather than advice on how you can actually fit this together in a working program

steady ether
#

sure, it's understandable, I'll post it

#

still gives Segmentation fault (core dumped) so I still miss understand stuff.

slim barn
#
 cupsAddOption(name: *const c_char,value: *const c_char, num_options: c_int, options: *mut CupsOptionT) -> *const c_int; 

That type is still utterly wrong. Are you programming by GPT?

slim barn
#

Gee. don't. I'm out. Not playing secondary llm.

wary quarry
#

this is a horrible idea

steady ether
#

well that was quick.

wary quarry
#

the problem with gpt and friends is that they don't understand anything. it's just like the autocorrect on your phone keyboard trying to predict the most likely next words

#

the problem is that you yourself also don't understand anything. which is fine, everyone starts out with nothing. it means you need to go slowly and read and ask questions. it also means you are not capable of telling whether a llm spits out something that is good, or alright or complete bullshit

#

it seems that in this case it's pretty close to complete bullshit

steady ether
#

yes, I agree, that;s why I take it as a suggestion and try stuff from my knowledge until I understand.

that's why the prints and stuff.

#

well, this can be closed as
gpt learning unfrendly and not enough knowledge.

wary quarry
#

you can learn knowledge

#

i think the first step, if you still want to move forward, is:

  1. make your project a workspace
  2. create a cups_ffi crate
  3. in this crate, put all the ffi functions and structs corresponding to cups' header files
  4. this crate should not have things like getters and impls of your traits and stuff like that
  5. create another crate that uses cups_ffi and provides a safe wrapper around it
#

you can probably use rust-bindgen to do most of the above for you