Accessing C Arrays of String from Rust

Now that I can list the group names, I want to be able to list the member of the groups.

First task is to add an field to the GroupEnt structure that can access the member list. The structure now looks like this:

#[repr(C)]
pub struct GroupEnt {
    gr_name:   *const c_char,        /* group name */
    gr_passwd:  *const c_char,      /* group password */
    gr_gid:    u32,         /* group ID */
    gr_mem:    *const *const c_char         /* NULL-terminated array of pointers to names of group members */
}

Iterating through the gr_mem field is done via the offset accessor like this:

 let member_ptr_arr: *const *const c_char  =  (*groupent).gr_mem;
                if member_ptr_arr == ptr::null() {
                    continue;
                }
                for i in 0 .. {
                    let member_ptr: *const c_char = *(member_ptr_arr.offset(i));

This code is tricky to get right:
let member_ptr: *const c_char = *(member_ptr_arr.offset(i));
offset inside the parents, * outside. But that matches the intention of the C code.
Access of the strings via the same method as the group names. If the pointer is null, exit the loop

     if member_ptr != ptr::null() {
     let member: &CStr = CStr::from_ptr(member_ptr) ;
         println!("  {}", member.to_str().unwrap());
     }else{
         break; 
     }

The whole function looks like this:

fn enumerate_groups(){
    let mut groupent: * const GroupEnt;
    unsafe{
        setgrent();
        groupent = getgrent();
    }
    while groupent != ptr::null(){
        let c_str: &CStr = unsafe { CStr::from_ptr((*groupent).gr_name) };
        println!("{}", c_str.to_str().unwrap());
        unsafe{
            if  (*groupent).gr_mem != ptr::null(){
                let member_ptr_arr: *const *const c_char  =  (*groupent).gr_mem;
                if member_ptr_arr == ptr::null() {
                    continue;
                }
                for i in 0 .. {
                    let member_ptr: *const c_char = *(member_ptr_arr.offset(i));
                    if member_ptr != ptr::null() {
                        let member: &CStr = CStr::from_ptr(member_ptr) ;
                        println!("  {}", member.to_str().unwrap());
                    }else{
                        break;
                    }
                }
            }
        }         
        unsafe{
            groupent = getgrent();
        }        
    }       
    unsafe{
        endgrent();
    }
}

Big thanks to Sebastian K (sebk) in #rust-beginners (Mozilla IRC) for helping me get this to work and understand this.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.