2014-02-02 09:54:38 +00:00
|
|
|
#[crate_id(name="id", version="1.0.0", author="Alan Andrade")];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This file is part of the uutils coreutils package.
|
|
|
|
*
|
|
|
|
* (c) Alan Andrade <alan.andradec@gmail.com>
|
|
|
|
*
|
|
|
|
* For the full copyright and license information, please view the LICENSE
|
|
|
|
* file that was distributed with this source code.
|
|
|
|
*
|
|
|
|
* Synced with:
|
|
|
|
* http://ftp-archive.freebsd.org/mirror/FreeBSD-Archive/old-releases/i386/1.0-RELEASE/ports/shellutils/src/id.c
|
|
|
|
* http://www.opensource.apple.com/source/shell_cmds/shell_cmds-118/id/id.c
|
|
|
|
*/
|
|
|
|
|
2014-02-23 21:31:51 +00:00
|
|
|
#[allow(non_camel_case_types)];
|
2014-02-23 22:18:04 +00:00
|
|
|
#[feature(macro_rules)];
|
2014-02-16 21:29:31 +00:00
|
|
|
extern crate getopts;
|
2014-02-02 09:54:38 +00:00
|
|
|
|
|
|
|
use std::{libc, os, vec};
|
2014-02-16 21:29:31 +00:00
|
|
|
use std::ptr::read;
|
2014-02-23 22:18:04 +00:00
|
|
|
use std::libc::{
|
|
|
|
c_char,
|
|
|
|
c_int,
|
|
|
|
time_t,
|
|
|
|
uid_t,
|
|
|
|
getgid,
|
|
|
|
getegid,
|
|
|
|
getuid,
|
|
|
|
getlogin
|
|
|
|
};
|
2014-02-02 09:54:38 +00:00
|
|
|
use std::str::raw::from_c_str;
|
|
|
|
use getopts::{getopts, optflag, usage};
|
|
|
|
|
2014-02-23 22:18:04 +00:00
|
|
|
#[path = "../common/util.rs"]
|
|
|
|
mod util;
|
|
|
|
|
2014-02-02 09:54:38 +00:00
|
|
|
// These could be extracted into their own file
|
|
|
|
struct c_passwd {
|
|
|
|
pw_name: *c_char, /* user name */
|
|
|
|
pw_passwd: *c_char, /* user name */
|
|
|
|
pw_uid: c_int, /* user uid */
|
|
|
|
pw_gid: c_int, /* user gid */
|
|
|
|
pw_change: time_t,
|
|
|
|
pw_class: *c_char,
|
|
|
|
pw_gecos: *c_char,
|
|
|
|
pw_dir: *c_char,
|
|
|
|
pw_shell: *c_char,
|
|
|
|
pw_expire: time_t
|
|
|
|
}
|
|
|
|
|
|
|
|
struct c_group {
|
|
|
|
gr_name: *c_char /* group name */
|
|
|
|
}
|
|
|
|
|
2014-02-16 21:29:31 +00:00
|
|
|
#[cfg(not(target_os = "linux"))]
|
|
|
|
mod audit {
|
2014-02-23 21:31:51 +00:00
|
|
|
pub use std::mem::uninit;
|
2014-02-19 01:10:32 +00:00
|
|
|
use std::libc::{uid_t, pid_t, c_int, c_uint, uint64_t, dev_t};
|
2014-02-02 09:54:38 +00:00
|
|
|
|
2014-02-19 01:10:32 +00:00
|
|
|
pub type au_id_t = uid_t;
|
|
|
|
pub type au_asid_t = pid_t;
|
|
|
|
pub type au_event_t = c_uint;
|
|
|
|
pub type au_emod_t = c_uint;
|
|
|
|
pub type au_class_t = c_int;
|
2014-02-02 09:54:38 +00:00
|
|
|
|
2014-02-19 01:10:32 +00:00
|
|
|
pub struct au_mask {
|
2014-02-16 21:29:31 +00:00
|
|
|
am_success: c_uint,
|
|
|
|
am_failure: c_uint
|
|
|
|
}
|
2014-02-19 01:10:32 +00:00
|
|
|
pub type au_mask_t = au_mask;
|
2014-02-16 21:29:31 +00:00
|
|
|
|
2014-02-19 01:10:32 +00:00
|
|
|
pub struct au_tid_addr {
|
2014-02-16 21:29:31 +00:00
|
|
|
port: dev_t,
|
|
|
|
}
|
2014-02-19 01:10:32 +00:00
|
|
|
pub type au_tid_addr_t = au_tid_addr;
|
2014-02-16 21:29:31 +00:00
|
|
|
|
2014-02-19 01:10:32 +00:00
|
|
|
pub struct c_auditinfo_addr {
|
2014-02-16 21:29:31 +00:00
|
|
|
ai_auid: au_id_t, /* Audit user ID */
|
|
|
|
ai_mask: au_mask_t, /* Audit masks. */
|
|
|
|
ai_termid: au_tid_addr_t, /* Terminal ID. */
|
|
|
|
ai_asid: au_asid_t, /* Audit session ID. */
|
|
|
|
ai_flags: uint64_t /* Audit session flags */
|
|
|
|
}
|
|
|
|
pub type c_auditinfo_addr_t = c_auditinfo_addr;
|
|
|
|
|
|
|
|
extern {
|
|
|
|
pub fn getaudit(auditinfo_addr: *c_auditinfo_addr_t) -> c_int;
|
|
|
|
}
|
2014-02-02 09:54:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
extern {
|
|
|
|
fn getpwuid(uid: uid_t) -> *c_passwd;
|
|
|
|
fn getgrgid(gid: uid_t) -> *c_group;
|
|
|
|
fn getpwnam(login: *c_char) -> *c_passwd;
|
|
|
|
fn getgrouplist(name: *c_char,
|
|
|
|
basegid: c_int,
|
|
|
|
groups: *c_int,
|
|
|
|
ngroups: *mut c_int) -> c_int;
|
|
|
|
}
|
|
|
|
|
2014-02-23 22:18:04 +00:00
|
|
|
static NAME: &'static str = "id";
|
|
|
|
|
|
|
|
fn get_pw_from_args(matches: &getopts::Matches) -> Option<c_passwd> {
|
|
|
|
if matches.free.len() == 1 {
|
|
|
|
let username = matches.free[0].clone();
|
|
|
|
|
|
|
|
// Passed user as id
|
|
|
|
if username.chars().all(|c| c.is_digit()) {
|
|
|
|
let id = from_str::<u32>(username).unwrap();
|
|
|
|
let pw_pointer = unsafe { getpwuid(id) };
|
|
|
|
|
|
|
|
if pw_pointer.is_not_null() {
|
|
|
|
Some(unsafe { read(pw_pointer) })
|
|
|
|
} else {
|
|
|
|
crash!(1, "{:s}: no such user", username);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Passed the username as a string
|
|
|
|
} else {
|
|
|
|
let pw_pointer = unsafe {
|
|
|
|
getpwnam(username.as_slice().as_ptr() as *i8)
|
|
|
|
};
|
|
|
|
if pw_pointer.is_not_null() {
|
|
|
|
Some(unsafe { read(pw_pointer) })
|
|
|
|
} else {
|
|
|
|
crash!(1, "{:s}: no such user", username);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
2014-02-02 09:54:38 +00:00
|
|
|
|
|
|
|
fn main () {
|
|
|
|
let args = os::args();
|
|
|
|
let args_t = args.tail();
|
|
|
|
|
|
|
|
let options = [
|
|
|
|
optflag("h", "", "Show help"),
|
2014-02-16 21:29:31 +00:00
|
|
|
optflag("A", "", "Display the process audit (not available on Linux)"),
|
2014-02-02 09:54:38 +00:00
|
|
|
optflag("G", "", "Display the different group IDs"),
|
|
|
|
optflag("g", "", "Display the effective group ID as a number"),
|
|
|
|
optflag("n", "", "Display the name of the user or group ID for the -G, -g and -u options"),
|
|
|
|
optflag("P", "", "Display the id as a password file entry"),
|
|
|
|
optflag("p", "", "Make the output human-readable"),
|
|
|
|
optflag("r", "", "Display the real ID for the -g and -u options"),
|
|
|
|
optflag("u", "", "Display the effective user ID as a number")
|
|
|
|
];
|
|
|
|
|
|
|
|
let matches = match getopts(args_t, options) {
|
|
|
|
Ok(m) => { m },
|
|
|
|
Err(_) => {
|
2014-02-23 22:18:04 +00:00
|
|
|
println!("{:s}", usage(NAME, options));
|
2014-02-02 09:54:38 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if matches.opt_present("h") {
|
2014-02-23 22:18:04 +00:00
|
|
|
println!("{:s}", usage(NAME, options));
|
2014-02-02 09:54:38 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if matches.opt_present("A") {
|
|
|
|
auditid();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-02-23 22:18:04 +00:00
|
|
|
let possible_pw = get_pw_from_args(&matches);
|
2014-02-02 09:54:38 +00:00
|
|
|
|
|
|
|
let nflag = matches.opt_present("n");
|
|
|
|
let uflag = matches.opt_present("u");
|
|
|
|
let gflag = matches.opt_present("g");
|
|
|
|
let rflag = matches.opt_present("r");
|
|
|
|
|
|
|
|
if gflag {
|
|
|
|
let id = if possible_pw.is_some() {
|
|
|
|
possible_pw.unwrap().pw_gid
|
|
|
|
} else {
|
|
|
|
if rflag {
|
|
|
|
unsafe { getgid() as i32 }
|
|
|
|
} else {
|
|
|
|
unsafe { getegid() as i32 }
|
|
|
|
}
|
|
|
|
} as u32;
|
|
|
|
let gr = unsafe { getgrgid(id) };
|
|
|
|
|
2014-02-16 21:29:31 +00:00
|
|
|
if nflag && gr.is_not_null() {
|
|
|
|
let gr_name = unsafe { from_c_str(read(gr).gr_name) };
|
2014-02-02 09:54:38 +00:00
|
|
|
println!("{:s}", gr_name);
|
|
|
|
} else {
|
|
|
|
println!("{:u}", id);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if uflag {
|
|
|
|
let id = if possible_pw.is_some() {
|
|
|
|
possible_pw.unwrap().pw_uid
|
|
|
|
} else if rflag {
|
|
|
|
unsafe { getgid() as i32 }
|
|
|
|
} else {
|
|
|
|
unsafe { getegid() as i32 }
|
|
|
|
};
|
|
|
|
|
|
|
|
let pw = unsafe { getpwuid(id as u32) };
|
2014-02-16 21:29:31 +00:00
|
|
|
if nflag && pw.is_not_null() {
|
2014-02-02 09:54:38 +00:00
|
|
|
let pw_name = unsafe {
|
2014-02-16 21:29:31 +00:00
|
|
|
from_c_str(read(pw).pw_name)
|
2014-02-02 09:54:38 +00:00
|
|
|
};
|
|
|
|
println!("{:s}", pw_name);
|
|
|
|
} else {
|
2014-02-16 21:29:31 +00:00
|
|
|
println!("{:d}", id);
|
2014-02-02 09:54:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if matches.opt_present("G") {
|
|
|
|
group(possible_pw, nflag);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if matches.opt_present("P") {
|
|
|
|
pline(possible_pw);
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
|
|
|
|
if matches.opt_present("p") {
|
|
|
|
pretty(possible_pw);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if possible_pw.is_some() {
|
|
|
|
id_print(possible_pw, true, false, false)
|
|
|
|
} else {
|
|
|
|
id_print(possible_pw, false, true, true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn pretty(possible_pw: Option<c_passwd>) {
|
|
|
|
if possible_pw.is_some() {
|
|
|
|
let pw = possible_pw.unwrap();
|
|
|
|
|
|
|
|
let pw_name = unsafe { from_c_str(pw.pw_name) };
|
|
|
|
print!("uid\t{:s}\ngroups\t", pw_name);
|
|
|
|
group(possible_pw, true);
|
|
|
|
} else {
|
|
|
|
let login = unsafe { from_c_str(getlogin()) };
|
|
|
|
let rid = unsafe { getuid() };
|
|
|
|
let pw = unsafe { getpwuid(rid) };
|
|
|
|
|
|
|
|
let is_same_user = unsafe {
|
2014-02-16 21:29:31 +00:00
|
|
|
from_c_str(read(pw).pw_name) == login
|
2014-02-02 09:54:38 +00:00
|
|
|
};
|
|
|
|
|
2014-02-16 21:29:31 +00:00
|
|
|
if pw.is_null() || is_same_user {
|
2014-02-02 09:54:38 +00:00
|
|
|
println!("login\t{:s}", login);
|
|
|
|
}
|
|
|
|
|
2014-02-16 21:29:31 +00:00
|
|
|
if pw.is_not_null() {
|
2014-02-02 09:54:38 +00:00
|
|
|
println!(
|
|
|
|
"uid\t{:s}",
|
2014-02-16 21:29:31 +00:00
|
|
|
unsafe { from_c_str(read(pw).pw_name) })
|
2014-02-02 09:54:38 +00:00
|
|
|
} else {
|
2014-02-16 21:29:31 +00:00
|
|
|
println!("uid\t{:u}\n", rid);
|
2014-02-02 09:54:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let eid = unsafe { getegid() };
|
|
|
|
if eid == rid {
|
|
|
|
let pw = unsafe { getpwuid(eid) };
|
2014-02-16 21:29:31 +00:00
|
|
|
if pw.is_not_null() {
|
2014-02-02 09:54:38 +00:00
|
|
|
println!(
|
|
|
|
"euid\t{:s}",
|
2014-02-16 21:29:31 +00:00
|
|
|
unsafe { from_c_str(read(pw).pw_name) });
|
2014-02-02 09:54:38 +00:00
|
|
|
} else {
|
|
|
|
println!("euid\t{:u}", eid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let rid = unsafe { getgid() };
|
|
|
|
|
|
|
|
if rid != eid {
|
|
|
|
let gr = unsafe { getgrgid(rid) };
|
2014-02-16 21:29:31 +00:00
|
|
|
if gr.is_not_null() {
|
2014-02-02 09:54:38 +00:00
|
|
|
println!(
|
|
|
|
"rgid\t{:s}",
|
2014-02-16 21:29:31 +00:00
|
|
|
unsafe { from_c_str(read(gr).gr_name) });
|
2014-02-02 09:54:38 +00:00
|
|
|
} else {
|
|
|
|
println!("rgid\t{:u}", rid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
print!("groups\t");
|
|
|
|
group(None, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn pline(possible_pw: Option<c_passwd>) {
|
|
|
|
let pw = if possible_pw.is_none() {
|
2014-02-16 21:29:31 +00:00
|
|
|
unsafe { read(getpwuid(getuid())) }
|
2014-02-02 09:54:38 +00:00
|
|
|
} else {
|
|
|
|
possible_pw.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
let pw_name = unsafe { from_c_str(pw.pw_name) };
|
|
|
|
let pw_passwd = unsafe { from_c_str(pw.pw_passwd)};
|
|
|
|
let pw_class = unsafe { from_c_str(pw.pw_class) };
|
|
|
|
let pw_gecos = unsafe { from_c_str(pw.pw_gecos) };
|
|
|
|
let pw_dir = unsafe { from_c_str(pw.pw_dir) };
|
|
|
|
let pw_shell = unsafe { from_c_str(pw.pw_shell) };
|
|
|
|
|
|
|
|
println!(
|
|
|
|
"{:s}:{:s}:{:d}:{:d}:{:s}:{:d}:{:d}:{:s}:{:s}:{:s}",
|
|
|
|
pw_name,
|
|
|
|
pw_passwd,
|
|
|
|
pw.pw_uid,
|
|
|
|
pw.pw_gid,
|
|
|
|
pw_class,
|
|
|
|
pw.pw_change,
|
|
|
|
pw.pw_expire,
|
|
|
|
pw_gecos,
|
|
|
|
pw_dir,
|
|
|
|
pw_shell);
|
|
|
|
}
|
|
|
|
|
|
|
|
static NGROUPS: i32 = 20;
|
|
|
|
|
|
|
|
fn group(possible_pw: Option<c_passwd>, nflag: bool) {
|
|
|
|
let mut groups = vec::with_capacity(NGROUPS as uint);
|
|
|
|
let mut ngroups;
|
|
|
|
|
|
|
|
if possible_pw.is_some() {
|
|
|
|
ngroups = NGROUPS;
|
|
|
|
unsafe {
|
|
|
|
getgrouplist(
|
|
|
|
possible_pw.unwrap().pw_name,
|
|
|
|
possible_pw.unwrap().pw_gid,
|
|
|
|
groups.as_ptr(),
|
|
|
|
&mut ngroups);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ngroups = unsafe {
|
|
|
|
libc::getgroups(NGROUPS, groups.as_mut_ptr() as *mut u32)
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
unsafe { groups.set_len(ngroups as uint) };
|
|
|
|
|
|
|
|
for &g in groups.iter() {
|
|
|
|
if nflag {
|
|
|
|
let group = unsafe { getgrgid(g as u32) };
|
2014-02-16 21:29:31 +00:00
|
|
|
if group.is_not_null() {
|
2014-02-02 09:54:38 +00:00
|
|
|
let name = unsafe {
|
2014-02-16 21:29:31 +00:00
|
|
|
from_c_str(read(group).gr_name)
|
2014-02-02 09:54:38 +00:00
|
|
|
};
|
|
|
|
print!("{:s} ", name);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
print!("{:d} ", g);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
println!("");
|
|
|
|
}
|
|
|
|
|
2014-02-16 21:29:31 +00:00
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
fn auditid() { }
|
|
|
|
|
|
|
|
#[cfg(not(target_os = "linux"))]
|
|
|
|
fn auditid() {
|
|
|
|
let auditinfo: audit::c_auditinfo_addr_t = unsafe { audit::uninit() };
|
|
|
|
let address = &auditinfo as *audit::c_auditinfo_addr_t;
|
|
|
|
if unsafe { audit::getaudit(address) } < 0 {
|
2014-02-02 09:54:38 +00:00
|
|
|
println!("Couldlnt retrieve information");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
println!("auid={:u}", auditinfo.ai_auid);
|
|
|
|
println!("mask.success=0x{:x}", auditinfo.ai_mask.am_success);
|
|
|
|
println!("mask.failure=0x{:x}", auditinfo.ai_mask.am_failure);
|
|
|
|
println!("termid.port=0x{:x}", auditinfo.ai_termid.port);
|
|
|
|
println!("asid={:d}", auditinfo.ai_asid);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn id_print(possible_pw: Option<c_passwd>,
|
|
|
|
use_ggl: bool,
|
|
|
|
p_euid: bool,
|
|
|
|
p_egid: bool) {
|
|
|
|
|
|
|
|
let uid;
|
|
|
|
let gid;
|
|
|
|
|
|
|
|
if possible_pw.is_some() {
|
|
|
|
uid = possible_pw.unwrap().pw_uid;
|
|
|
|
gid = possible_pw.unwrap().pw_gid;
|
|
|
|
} else {
|
|
|
|
uid = unsafe { getuid() as i32 };
|
|
|
|
gid = unsafe { getgid() as i32 };
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut ngroups;
|
|
|
|
let mut groups = vec::with_capacity(NGROUPS as uint);
|
|
|
|
|
|
|
|
if use_ggl && possible_pw.is_some() {
|
|
|
|
ngroups = NGROUPS;
|
|
|
|
let pw_name = possible_pw.unwrap().pw_name;
|
|
|
|
|
|
|
|
unsafe { getgrouplist(pw_name, gid, groups.as_ptr(), &mut ngroups) };
|
|
|
|
} else {
|
|
|
|
ngroups = unsafe {
|
|
|
|
libc::getgroups(NGROUPS, groups.as_mut_ptr() as *mut u32)
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
if possible_pw.is_some() {
|
|
|
|
print!(
|
|
|
|
"uid={:d}({:s})",
|
|
|
|
uid,
|
|
|
|
unsafe { from_c_str(possible_pw.unwrap().pw_name) });
|
|
|
|
} else {
|
|
|
|
print!("uid={:u}", unsafe { getuid() });
|
|
|
|
}
|
|
|
|
|
|
|
|
print!(" gid={:d}", gid);
|
|
|
|
let gr = unsafe { getgrgid(gid as u32) };
|
2014-02-16 21:29:31 +00:00
|
|
|
if gr.is_not_null() {
|
2014-02-02 09:54:38 +00:00
|
|
|
print!(
|
|
|
|
"({:s})",
|
2014-02-16 21:29:31 +00:00
|
|
|
unsafe { from_c_str(read(gr).gr_name) });
|
2014-02-02 09:54:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let euid = unsafe { libc::geteuid() };
|
|
|
|
if p_euid && (euid != uid as u32) {
|
|
|
|
print!(" euid={:u}", euid);
|
|
|
|
let pw = unsafe { getpwuid(euid) };
|
2014-02-16 21:29:31 +00:00
|
|
|
if pw.is_not_null() {
|
2014-02-02 09:54:38 +00:00
|
|
|
print!(
|
|
|
|
"({:s})",
|
2014-02-16 21:29:31 +00:00
|
|
|
unsafe { from_c_str(read(pw).pw_name) });
|
2014-02-02 09:54:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let egid = unsafe { getegid() };
|
|
|
|
if p_egid && (egid != gid as u32) {
|
|
|
|
print!(" egid={:u}", egid);
|
|
|
|
unsafe {
|
|
|
|
let grp = getgrgid(egid);
|
2014-02-16 21:29:31 +00:00
|
|
|
if grp.is_not_null() {
|
|
|
|
print!("({:s})", from_c_str(read(grp).gr_name));
|
2014-02-02 09:54:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe { groups.set_len(ngroups as uint) };
|
|
|
|
|
|
|
|
if ngroups > 0 {
|
|
|
|
print!(" groups=");
|
|
|
|
|
|
|
|
let mut first = true;
|
|
|
|
for &gr in groups.iter() {
|
|
|
|
if !first { print!(",") }
|
|
|
|
print!("{:d}", gr);
|
|
|
|
let group = unsafe { getgrgid(gr as u32) };
|
2014-02-16 21:29:31 +00:00
|
|
|
if group.is_not_null() {
|
2014-02-02 09:54:38 +00:00
|
|
|
let name = unsafe {
|
2014-02-16 21:29:31 +00:00
|
|
|
from_c_str(read(group).gr_name)
|
2014-02-02 09:54:38 +00:00
|
|
|
};
|
|
|
|
print!("({:s})", name);
|
|
|
|
}
|
|
|
|
first = false
|
|
|
|
}
|
|
|
|
|
|
|
|
println!("");
|
|
|
|
}
|
|
|
|
}
|