Skip to content

Commit

Permalink
Move platform-specific arg handling to sys::args
Browse files Browse the repository at this point in the history
  • Loading branch information
brson committed Oct 2, 2016
1 parent 29e0235 commit 4d76ac8
Show file tree
Hide file tree
Showing 11 changed files with 294 additions and 282 deletions.
5 changes: 3 additions & 2 deletions src/libstd/env.rs
Expand Up @@ -21,6 +21,7 @@ use ffi::{OsStr, OsString};
use fmt;
use io;
use path::{Path, PathBuf};
use sys;
use sys::os as os_imp;

/// Returns the current working directory as a `PathBuf`.
Expand Down Expand Up @@ -557,7 +558,7 @@ pub struct Args { inner: ArgsOs }
///
/// This structure is created through the `std::env::args_os` method.
#[stable(feature = "env", since = "1.0.0")]
pub struct ArgsOs { inner: os_imp::Args }
pub struct ArgsOs { inner: sys::args::Args }

/// Returns the arguments which this program was started with (normally passed
/// via the command line).
Expand Down Expand Up @@ -606,7 +607,7 @@ pub fn args() -> Args {
/// ```
#[stable(feature = "env", since = "1.0.0")]
pub fn args_os() -> ArgsOs {
ArgsOs { inner: os_imp::args() }
ArgsOs { inner: sys::args::args() }
}

#[stable(feature = "env", since = "1.0.0")]
Expand Down
2 changes: 1 addition & 1 deletion src/libstd/rt.rs
Expand Up @@ -51,7 +51,7 @@ fn lang_start(main: *const u8, argc: isize, argv: *const *const u8) -> isize {
thread_info::set(main_guard, thread);

// Store our args if necessary in a squirreled away location
sys_common::args::init(argc, argv);
sys::args::init(argc, argv);

// Let's run some code!
let res = panic::catch_unwind(mem::transmute::<_, fn()>(main));
Expand Down
100 changes: 0 additions & 100 deletions src/libstd/sys/common/args.rs

This file was deleted.

3 changes: 1 addition & 2 deletions src/libstd/sys/common/mod.rs
Expand Up @@ -25,7 +25,6 @@ macro_rules! rtassert {
})
}

pub mod args;
pub mod at_exit_imp;
#[cfg(any(not(cargobuild), feature = "backtrace"))]
pub mod backtrace;
Expand Down Expand Up @@ -92,7 +91,7 @@ pub fn at_exit<F: FnOnce() + Send + 'static>(f: F) -> Result<(), ()> {
pub fn cleanup() {
static CLEANUP: Once = Once::new();
CLEANUP.call_once(|| unsafe {
args::cleanup();
sys::args::cleanup();
sys::stack_overflow::cleanup();
at_exit_imp::cleanup();
});
Expand Down
211 changes: 211 additions & 0 deletions src/libstd/sys/unix/args.rs
@@ -0,0 +1,211 @@
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Global initialization and retreival of command line arguments.
//!
//! On some platforms these are stored during runtime startup,
//! and on some they are retrieved from the system on demand.

#![allow(dead_code)] // runtime init functions not used during testing

use ffi::OsString;
use marker::PhantomData;
use vec;

/// One-time global initialization.
pub unsafe fn init(argc: isize, argv: *const *const u8) { imp::init(argc, argv) }

/// One-time global cleanup.
pub unsafe fn cleanup() { imp::cleanup() }

/// Returns the command line arguments
pub fn args() -> Args {
imp::args()
}

pub struct Args {
iter: vec::IntoIter<OsString>,
_dont_send_or_sync_me: PhantomData<*mut ()>,
}

impl Iterator for Args {
type Item = OsString;
fn next(&mut self) -> Option<OsString> { self.iter.next() }
fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
}

impl ExactSizeIterator for Args {
fn len(&self) -> usize { self.iter.len() }
}

impl DoubleEndedIterator for Args {
fn next_back(&mut self) -> Option<OsString> { self.iter.next_back() }
}

#[cfg(any(target_os = "linux",
target_os = "android",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "bitrig",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
target_os = "emscripten",
target_os = "haiku"))]
mod imp {
use os::unix::prelude::*;
use mem;
use ffi::{CStr, OsString};
use marker::PhantomData;
use libc;
use super::Args;

use sys_common::mutex::Mutex;

static mut GLOBAL_ARGS_PTR: usize = 0;
static LOCK: Mutex = Mutex::new();

pub unsafe fn init(argc: isize, argv: *const *const u8) {
let args = (0..argc).map(|i| {
CStr::from_ptr(*argv.offset(i) as *const libc::c_char).to_bytes().to_vec()
}).collect();

LOCK.lock();
let ptr = get_global_ptr();
assert!((*ptr).is_none());
(*ptr) = Some(box args);
LOCK.unlock();
}

pub unsafe fn cleanup() {
LOCK.lock();
*get_global_ptr() = None;
LOCK.unlock();
}

pub fn args() -> Args {
let bytes = clone().unwrap_or(Vec::new());
let v: Vec<OsString> = bytes.into_iter().map(|v| {
OsStringExt::from_vec(v)
}).collect();
Args { iter: v.into_iter(), _dont_send_or_sync_me: PhantomData }
}

fn clone() -> Option<Vec<Vec<u8>>> {
unsafe {
LOCK.lock();
let ptr = get_global_ptr();
let ret = (*ptr).as_ref().map(|s| (**s).clone());
LOCK.unlock();
return ret
}
}

fn get_global_ptr() -> *mut Option<Box<Vec<Vec<u8>>>> {
unsafe { mem::transmute(&GLOBAL_ARGS_PTR) }
}

}

#[cfg(any(target_os = "macos",
target_os = "ios"))]
mod imp {
use ffi::CStr;
use marker::PhantomData;
use libc;
use super::Args;

pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
}

pub fn cleanup() {
}

#[cfg(target_os = "macos")]
pub fn args() -> Args {
use os::unix::prelude::*;
extern {
// These functions are in crt_externs.h.
fn _NSGetArgc() -> *mut libc::c_int;
fn _NSGetArgv() -> *mut *mut *mut libc::c_char;
}

let vec = unsafe {
let (argc, argv) = (*_NSGetArgc() as isize,
*_NSGetArgv() as *const *const libc::c_char);
(0.. argc as isize).map(|i| {
let bytes = CStr::from_ptr(*argv.offset(i)).to_bytes().to_vec();
OsStringExt::from_vec(bytes)
}).collect::<Vec<_>>()
};
Args {
iter: vec.into_iter(),
_dont_send_or_sync_me: PhantomData,
}
}

// As _NSGetArgc and _NSGetArgv aren't mentioned in iOS docs
// and use underscores in their names - they're most probably
// are considered private and therefore should be avoided
// Here is another way to get arguments using Objective C
// runtime
//
// In general it looks like:
// res = Vec::new()
// let args = [[NSProcessInfo processInfo] arguments]
// for i in (0..[args count])
// res.push([args objectAtIndex:i])
// res
#[cfg(target_os = "ios")]
pub fn args() -> Args {
use ffi::OsString;
use mem;
use str;

extern {
fn sel_registerName(name: *const libc::c_uchar) -> Sel;
fn objc_msgSend(obj: NsId, sel: Sel, ...) -> NsId;
fn objc_getClass(class_name: *const libc::c_uchar) -> NsId;
}

#[link(name = "Foundation", kind = "framework")]
#[link(name = "objc")]
#[cfg(not(cargobuild))]
extern {}

type Sel = *const libc::c_void;
type NsId = *const libc::c_void;

let mut res = Vec::new();

unsafe {
let process_info_sel = sel_registerName("processInfo\0".as_ptr());
let arguments_sel = sel_registerName("arguments\0".as_ptr());
let utf8_sel = sel_registerName("UTF8String\0".as_ptr());
let count_sel = sel_registerName("count\0".as_ptr());
let object_at_sel = sel_registerName("objectAtIndex:\0".as_ptr());

let klass = objc_getClass("NSProcessInfo\0".as_ptr());
let info = objc_msgSend(klass, process_info_sel);
let args = objc_msgSend(info, arguments_sel);

let cnt: usize = mem::transmute(objc_msgSend(args, count_sel));
for i in 0..cnt {
let tmp = objc_msgSend(args, object_at_sel, i);
let utf_c_str: *const libc::c_char =
mem::transmute(objc_msgSend(tmp, utf8_sel));
let bytes = CStr::from_ptr(utf_c_str).to_bytes();
res.push(OsString::from(str::from_utf8(bytes).unwrap()))
}
}

Args { iter: res.into_iter(), _dont_send_or_sync_me: PhantomData }
}
}
1 change: 1 addition & 0 deletions src/libstd/sys/unix/mod.rs
Expand Up @@ -30,6 +30,7 @@ use libc;
#[macro_use]
pub mod weak;

pub mod args;
pub mod android;
#[cfg(any(not(cargobuild), feature = "backtrace"))]
pub mod backtrace;
Expand Down

0 comments on commit 4d76ac8

Please sign in to comment.