summary refs log tree commit diff
path: root/src/libcargo
diff options
context:
space:
mode:
authorBrian Anderson <banderson@mozilla.com>2012-11-28 12:38:53 -0800
committerBrian Anderson <banderson@mozilla.com>2012-11-28 13:17:33 -0800
commitfc06114ddfd2bcdbc4f29076c226a7a1d66ea8d6 (patch)
treebd9de2c450f23b8ff0e09130ab59d784ace5b5e3 /src/libcargo
parent669fbddc4435a9ab152332df06a7fcca789c8059 (diff)
parent8179e268efd86ae5c1bcf21b4f8d4e01eea7c193 (diff)
Merge remote-tracking branch 'brson/companion' into incoming
Conflicts:
	src/compiletest/compiletest.rs
	src/libcargo/cargo.rs
	src/libcore/core.rs
	src/librustc/rustc.rs
	src/librustdoc/rustdoc.rc
Diffstat (limited to 'src/libcargo')
-rw-r--r--src/libcargo/cargo.rc1938
-rw-r--r--src/libcargo/cargo.rs1982
2 files changed, 1938 insertions, 1982 deletions
diff --git a/src/libcargo/cargo.rc b/src/libcargo/cargo.rc
index 184deba04ca..a559592e3c3 100644
--- a/src/libcargo/cargo.rc
+++ b/src/libcargo/cargo.rc
@@ -36,3 +36,1941 @@ use core::*;
 
 #[legacy_exports]
 mod pgp;
+
+use syntax::{ast, codemap, parse, visit, attr};
+use syntax::diagnostic::span_handler;
+use codemap::span;
+use rustc::metadata::filesearch::{get_cargo_root, get_cargo_root_nearest,
+                                     get_cargo_sysroot, libdir};
+use syntax::diagnostic;
+
+use result::{Ok, Err};
+use io::WriterUtil;
+use send_map::linear::LinearMap;
+use std::{map, json, tempfile, term, sort, getopts};
+use map::HashMap;
+use to_str::to_str;
+use getopts::{optflag, optopt, opt_present};
+use dvec::DVec;
+
+struct Package {
+    name: ~str,
+    uuid: ~str,
+    url: ~str,
+    method: ~str,
+    description: ~str,
+    reference: Option<~str>,
+    tags: ~[~str],
+    versions: ~[(~str, ~str)]
+}
+
+impl Package : cmp::Ord {
+    pure fn lt(&self, other: &Package) -> bool {
+        if (*self).name.lt(&(*other).name) { return true; }
+        if (*other).name.lt(&(*self).name) { return false; }
+        if (*self).uuid.lt(&(*other).uuid) { return true; }
+        if (*other).uuid.lt(&(*self).uuid) { return false; }
+        if (*self).url.lt(&(*other).url) { return true; }
+        if (*other).url.lt(&(*self).url) { return false; }
+        if (*self).method.lt(&(*other).method) { return true; }
+        if (*other).method.lt(&(*self).method) { return false; }
+        if (*self).description.lt(&(*other).description) { return true; }
+        if (*other).description.lt(&(*self).description) { return false; }
+        if (*self).tags.lt(&(*other).tags) { return true; }
+        if (*other).tags.lt(&(*self).tags) { return false; }
+        if (*self).versions.lt(&(*other).versions) { return true; }
+        return false;
+    }
+    pure fn le(&self, other: &Package) -> bool { !(*other).lt(&(*self)) }
+    pure fn ge(&self, other: &Package) -> bool { !(*self).lt(other)     }
+    pure fn gt(&self, other: &Package) -> bool { (*other).lt(&(*self))  }
+}
+
+struct Source {
+    name: ~str,
+    mut url: ~str,
+    mut method: ~str,
+    mut key: Option<~str>,
+    mut keyfp: Option<~str>,
+    packages: DVec<Package>
+}
+
+struct Cargo {
+    pgp: bool,
+    root: Path,
+    installdir: Path,
+    bindir: Path,
+    libdir: Path,
+    workdir: Path,
+    sourcedir: Path,
+    sources: map::HashMap<~str, @Source>,
+    mut current_install: ~str,
+    dep_cache: map::HashMap<~str, bool>,
+    opts: Options
+}
+
+struct Crate {
+    name: ~str,
+    vers: ~str,
+    uuid: ~str,
+    desc: Option<~str>,
+    sigs: Option<~str>,
+    crate_type: Option<~str>,
+    deps: ~[~str]
+}
+
+struct Options {
+    test: bool,
+    mode: Mode,
+    free: ~[~str],
+    help: bool,
+}
+
+enum Mode { SystemMode, UserMode, LocalMode }
+
+impl Mode : cmp::Eq {
+    pure fn eq(&self, other: &Mode) -> bool {
+        ((*self) as uint) == ((*other) as uint)
+    }
+    pure fn ne(&self, other: &Mode) -> bool { !(*self).eq(other) }
+}
+
+fn opts() -> ~[getopts::Opt] {
+    ~[optflag(~"g"), optflag(~"G"), optflag(~"test"),
+     optflag(~"h"), optflag(~"help")]
+}
+
+fn info(msg: ~str) {
+    let out = io::stdout();
+
+    if term::color_supported() {
+        term::fg(out, term::color_green);
+        out.write_str(~"info: ");
+        term::reset(out);
+        out.write_line(msg);
+    } else { out.write_line(~"info: " + msg); }
+}
+
+fn warn(msg: ~str) {
+    let out = io::stdout();
+
+    if term::color_supported() {
+        term::fg(out, term::color_yellow);
+        out.write_str(~"warning: ");
+        term::reset(out);
+        out.write_line(msg);
+    }else { out.write_line(~"warning: " + msg); }
+}
+
+fn error(msg: ~str) {
+    let out = io::stdout();
+
+    if term::color_supported() {
+        term::fg(out, term::color_red);
+        out.write_str(~"error: ");
+        term::reset(out);
+        out.write_line(msg);
+    }
+    else { out.write_line(~"error: " + msg); }
+}
+
+fn is_uuid(id: ~str) -> bool {
+    let parts = str::split_str(id, ~"-");
+    if vec::len(parts) == 5u {
+        let mut correct = 0u;
+        for vec::eachi(parts) |i, part| {
+            fn is_hex_digit(+ch: char) -> bool {
+                ('0' <= ch && ch <= '9') ||
+                ('a' <= ch && ch <= 'f') ||
+                ('A' <= ch && ch <= 'F')
+            }
+
+            if !part.all(is_hex_digit) {
+                return false;
+            }
+
+            match i {
+                0u => {
+                    if part.len() == 8u {
+                        correct += 1u;
+                    }
+                }
+                1u | 2u | 3u => {
+                    if part.len() == 4u {
+                        correct += 1u;
+                    }
+                }
+                4u => {
+                    if part.len() == 12u {
+                        correct += 1u;
+                    }
+                }
+                _ => { }
+            }
+        }
+        if correct >= 5u {
+            return true;
+        }
+    }
+    return false;
+}
+
+#[test]
+fn test_is_uuid() {
+    assert is_uuid(~"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaafAF09");
+    assert !is_uuid(~"aaaaaaaa-aaaa-aaaa-aaaaa-aaaaaaaaaaaa");
+    assert !is_uuid(~"");
+    assert !is_uuid(~"aaaaaaaa-aaa -aaaa-aaaa-aaaaaaaaaaaa");
+    assert !is_uuid(~"aaaaaaaa-aaa!-aaaa-aaaa-aaaaaaaaaaaa");
+    assert !is_uuid(~"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa-a");
+    assert !is_uuid(~"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaป");
+}
+
+// FIXME (#2661): implement url/URL parsing so we don't have to resort
+// to weak checks
+
+fn has_archive_extension(p: ~str) -> bool {
+    str::ends_with(p, ~".tar") ||
+    str::ends_with(p, ~".tar.gz") ||
+    str::ends_with(p, ~".tar.bz2") ||
+    str::ends_with(p, ~".tar.Z") ||
+    str::ends_with(p, ~".tar.lz") ||
+    str::ends_with(p, ~".tar.xz") ||
+    str::ends_with(p, ~".tgz") ||
+    str::ends_with(p, ~".tbz") ||
+    str::ends_with(p, ~".tbz2") ||
+    str::ends_with(p, ~".tb2") ||
+    str::ends_with(p, ~".taz") ||
+    str::ends_with(p, ~".tlz") ||
+    str::ends_with(p, ~".txz")
+}
+
+fn is_archive_path(u: ~str) -> bool {
+    has_archive_extension(u) && os::path_exists(&Path(u))
+}
+
+fn is_archive_url(u: ~str) -> bool {
+    // FIXME (#2661): this requires the protocol bit - if we had proper
+    // url parsing, we wouldn't need it
+
+    match str::find_str(u, ~"://") {
+        option::Some(_) => has_archive_extension(u),
+        _ => false
+    }
+}
+
+fn is_git_url(url: ~str) -> bool {
+    if str::ends_with(url, ~"/") { str::ends_with(url, ~".git/") }
+    else {
+        str::starts_with(url, ~"git://") || str::ends_with(url, ~".git")
+    }
+}
+
+fn assume_source_method(url: ~str) -> ~str {
+    if is_git_url(url) {
+        return ~"git";
+    }
+    if str::starts_with(url, ~"file://") || os::path_exists(&Path(url)) {
+        return ~"file";
+    }
+
+    ~"curl"
+}
+
+fn load_link(mis: ~[@ast::meta_item]) -> (Option<~str>,
+                                         Option<~str>,
+                                         Option<~str>) {
+    let mut name = None;
+    let mut vers = None;
+    let mut uuid = None;
+    for mis.each |a| {
+        match a.node {
+            ast::meta_name_value(v, {node: ast::lit_str(s), span: _}) => {
+                match v {
+                    ~"name" => name = Some(*s),
+                    ~"vers" => vers = Some(*s),
+                    ~"uuid" => uuid = Some(*s),
+                    _ => { }
+                }
+            }
+            _ => fail ~"load_link: meta items must be name-values"
+        }
+    }
+    (name, vers, uuid)
+}
+
+fn load_crate(filename: &Path) -> Option<Crate> {
+    let sess = parse::new_parse_sess(None);
+    let c = parse::parse_crate_from_file(filename, ~[], sess);
+
+    let mut name = None;
+    let mut vers = None;
+    let mut uuid = None;
+    let mut desc = None;
+    let mut sigs = None;
+    let mut crate_type = None;
+
+    for c.node.attrs.each |a| {
+        match a.node.value.node {
+            ast::meta_name_value(v, {node: ast::lit_str(_), span: _}) => {
+                match v {
+                    ~"desc" => desc = Some(v),
+                    ~"sigs" => sigs = Some(v),
+                    ~"crate_type" => crate_type = Some(v),
+                    _ => { }
+                }
+            }
+            ast::meta_list(v, mis) => {
+                if v == ~"link" {
+                    let (n, v, u) = load_link(mis);
+                    name = n;
+                    vers = v;
+                    uuid = u;
+                }
+            }
+            _ => {
+                fail ~"crate attributes may not contain " +
+                     ~"meta_words";
+            }
+        }
+    }
+
+    type env = @{
+        mut deps: ~[~str]
+    };
+
+    fn goto_view_item(ps: syntax::parse::parse_sess, e: env,
+                      i: @ast::view_item) {
+        match i.node {
+            ast::view_item_use(ident, metas, _) => {
+                let name_items =
+                    attr::find_meta_items_by_name(metas, ~"name");
+                let m = if name_items.is_empty() {
+                    metas + ~[attr::mk_name_value_item_str(
+                        ~"name", *ps.interner.get(ident))]
+                } else {
+                    metas
+                };
+                let mut attr_name = ident;
+                let mut attr_vers = ~"";
+                let mut attr_from = ~"";
+
+              for m.each |item| {
+                    match attr::get_meta_item_value_str(*item) {
+                        Some(value) => {
+                            let name = attr::get_meta_item_name(*item);
+
+                            match name {
+                                ~"vers" => attr_vers = value,
+                                ~"from" => attr_from = value,
+                                _ => ()
+                            }
+                        }
+                        None => ()
+                    }
+                }
+
+                let query = if !str::is_empty(attr_from) {
+                    attr_from
+                } else {
+                    if !str::is_empty(attr_vers) {
+                        ps.interner.get(attr_name) + ~"@" + attr_vers
+                    } else { *ps.interner.get(attr_name) }
+                };
+
+                match *ps.interner.get(attr_name) {
+                    ~"std" | ~"core" => (),
+                    _ => e.deps.push(query)
+                }
+            }
+            _ => ()
+        }
+    }
+    fn goto_item(_e: env, _i: @ast::item) {
+    }
+
+    let e = @{
+        mut deps: ~[]
+    };
+    let v = visit::mk_simple_visitor(@{
+        visit_view_item: |a| goto_view_item(sess, e, a),
+        visit_item: |a| goto_item(e, a),
+        .. *visit::default_simple_visitor()
+    });
+
+    visit::visit_crate(*c, (), v);
+
+    let deps = copy e.deps;
+
+    match (name, vers, uuid) {
+        (Some(name0), Some(vers0), Some(uuid0)) => {
+            Some(Crate {
+                name: name0,
+                vers: vers0,
+                uuid: uuid0,
+                desc: desc,
+                sigs: sigs,
+                crate_type: crate_type,
+                deps: deps })
+        }
+        _ => return None
+    }
+}
+
+fn print(s: ~str) {
+    io::stdout().write_line(s);
+}
+
+fn rest(s: ~str, start: uint) -> ~str {
+    if (start >= str::len(s)) {
+        ~""
+    } else {
+        str::slice(s, start, str::len(s))
+    }
+}
+
+fn need_dir(s: &Path) {
+    if os::path_is_dir(s) { return; }
+    if !os::make_dir(s, 493_i32 /* oct: 755 */) {
+        fail fmt!("can't make_dir %s", s.to_str());
+    }
+}
+
+fn valid_pkg_name(s: &str) -> bool {
+    fn is_valid_digit(+c: char) -> bool {
+        ('0' <= c && c <= '9') ||
+        ('a' <= c && c <= 'z') ||
+        ('A' <= c && c <= 'Z') ||
+        c == '-' ||
+        c == '_'
+    }
+
+    s.all(is_valid_digit)
+}
+
+fn parse_source(name: ~str, j: &json::Json) -> @Source {
+    if !valid_pkg_name(name) {
+        fail fmt!("'%s' is an invalid source name", name);
+    }
+
+    match *j {
+        json::Object(j) => {
+            let mut url = match j.find(&~"url") {
+                Some(json::String(u)) => u,
+                _ => fail ~"needed 'url' field in source"
+            };
+            let method = match j.find(&~"method") {
+                Some(json::String(u)) => u,
+                _ => assume_source_method(url)
+            };
+            let key = match j.find(&~"key") {
+                Some(json::String(u)) => Some(u),
+                _ => None
+            };
+            let keyfp = match j.find(&~"keyfp") {
+                Some(json::String(u)) => Some(u),
+                _ => None
+            };
+            if method == ~"file" {
+                url = os::make_absolute(&Path(url)).to_str();
+            }
+            return @Source {
+                name: name,
+                mut url: url,
+                mut method: method,
+                mut key: key,
+                mut keyfp: keyfp,
+                packages: DVec() };
+        }
+        _ => fail ~"needed dict value in source"
+    };
+}
+
+fn try_parse_sources(filename: &Path, sources: map::HashMap<~str, @Source>) {
+    if !os::path_exists(filename)  { return; }
+    let c = io::read_whole_file_str(filename);
+    match json::from_str(c.get()) {
+        Ok(json::Object(j)) => {
+            for j.each |k, v| {
+                sources.insert(copy *k, parse_source(*k, v));
+                debug!("source: %s", *k);
+            }
+        }
+        Ok(_) => fail ~"malformed sources.json",
+        Err(e) => fail fmt!("%s:%s", filename.to_str(), e.to_str())
+    }
+}
+
+fn load_one_source_package(src: @Source, p: &json::Object) {
+    let name = match p.find(&~"name") {
+        Some(json::String(n)) => {
+            if !valid_pkg_name(n) {
+                warn(~"malformed source json: "
+                     + src.name + ~", '" + n + ~"'"+
+                     ~" is an invalid name (alphanumeric, underscores and" +
+                     ~" dashes only)");
+                return;
+            }
+            n
+        }
+        _ => {
+            warn(~"malformed source json: " + src.name + ~" (missing name)");
+            return;
+        }
+    };
+
+    let uuid = match p.find(&~"uuid") {
+        Some(json::String(n)) => {
+            if !is_uuid(n) {
+                warn(~"malformed source json: "
+                     + src.name + ~", '" + n + ~"'"+
+                     ~" is an invalid uuid");
+                return;
+            }
+            n
+        }
+        _ => {
+            warn(~"malformed source json: " + src.name + ~" (missing uuid)");
+            return;
+        }
+    };
+
+    let url = match p.find(&~"url") {
+        Some(json::String(n)) => n,
+        _ => {
+            warn(~"malformed source json: " + src.name + ~" (missing url)");
+            return;
+        }
+    };
+
+    let method = match p.find(&~"method") {
+        Some(json::String(n)) => n,
+        _ => {
+            warn(~"malformed source json: "
+                 + src.name + ~" (missing method)");
+            return;
+        }
+    };
+
+    let reference = match p.find(&~"ref") {
+        Some(json::String(n)) => Some(n),
+        _ => None
+    };
+
+    let mut tags = ~[];
+    match p.find(&~"tags") {
+        Some(json::List(js)) => {
+          for js.each |j| {
+                match *j {
+                    json::String(ref j) => tags.grow(1u, j),
+                    _ => ()
+                }
+            }
+        }
+        _ => ()
+    }
+
+    let description = match p.find(&~"description") {
+        Some(json::String(n)) => n,
+        _ => {
+            warn(~"malformed source json: " + src.name
+                 + ~" (missing description)");
+            return;
+        }
+    };
+
+    let newpkg = Package {
+        name: name,
+        uuid: uuid,
+        url: url,
+        method: method,
+        description: description,
+        reference: reference,
+        tags: tags,
+        versions: ~[]
+    };
+
+    match src.packages.position(|pkg| pkg.uuid == uuid) {
+        Some(idx) => {
+            src.packages.set_elt(idx, newpkg);
+            log(debug, ~"  updated package: " + src.name + ~"/" + name);
+        }
+        None => {
+            src.packages.push(newpkg);
+        }
+    }
+
+    log(debug, ~"  loaded package: " + src.name + ~"/" + name);
+}
+
+fn load_source_info(c: &Cargo, src: @Source) {
+    let dir = c.sourcedir.push(src.name);
+    let srcfile = dir.push("source.json");
+    if !os::path_exists(&srcfile) { return; }
+    let srcstr = io::read_whole_file_str(&srcfile);
+    match json::from_str(srcstr.get()) {
+        Ok(ref json @ json::Object(_)) => {
+            let o = parse_source(src.name, json);
+
+            src.key = o.key;
+            src.keyfp = o.keyfp;
+        }
+        Ok(_) => {
+            warn(~"malformed source.json: " + src.name +
+                 ~"(source info is not a dict)");
+        }
+        Err(e) => {
+            warn(fmt!("%s:%s", src.name, e.to_str()));
+        }
+    };
+}
+fn load_source_packages(c: &Cargo, src: @Source) {
+    log(debug, ~"loading source: " + src.name);
+    let dir = c.sourcedir.push(src.name);
+    let pkgfile = dir.push("packages.json");
+    if !os::path_exists(&pkgfile) { return; }
+    let pkgstr = io::read_whole_file_str(&pkgfile);
+    match json::from_str(pkgstr.get()) {
+        Ok(json::List(js)) => {
+          for js.each |j| {
+                match *j {
+                    json::Object(p) => {
+                        load_one_source_package(src, p);
+                    }
+                    _ => {
+                        warn(~"malformed source json: " + src.name +
+                             ~" (non-dict pkg)");
+                    }
+                }
+            }
+        }
+        Ok(_) => {
+            warn(~"malformed packages.json: " + src.name +
+                 ~"(packages is not a list)");
+        }
+        Err(e) => {
+            warn(fmt!("%s:%s", src.name, e.to_str()));
+        }
+    };
+}
+
+fn build_cargo_options(argv: ~[~str]) -> Options {
+    let matches = &match getopts::getopts(argv, opts()) {
+        result::Ok(m) => m,
+        result::Err(f) => {
+            fail fmt!("%s", getopts::fail_str(f));
+        }
+    };
+
+    let test = opt_present(matches, ~"test");
+    let G    = opt_present(matches, ~"G");
+    let g    = opt_present(matches, ~"g");
+    let help = opt_present(matches, ~"h") || opt_present(matches, ~"help");
+    let len  = vec::len(matches.free);
+
+    let is_install = len > 1u && matches.free[1] == ~"install";
+    let is_uninstall = len > 1u && matches.free[1] == ~"uninstall";
+
+    if G && g { fail ~"-G and -g both provided"; }
+
+    if !is_install && !is_uninstall && (g || G) {
+        fail ~"-g and -G are only valid for `install` and `uninstall|rm`";
+    }
+
+    let mode =
+        if (!is_install && !is_uninstall) || g { UserMode }
+        else if G { SystemMode }
+        else { LocalMode };
+
+    Options {test: test, mode: mode, free: matches.free, help: help}
+}
+
+fn configure(opts: Options) -> Cargo {
+    let home = match get_cargo_root() {
+        Ok(home) => home,
+        Err(_err) => get_cargo_sysroot().get()
+    };
+
+    let get_cargo_dir = match opts.mode {
+        SystemMode => get_cargo_sysroot,
+        UserMode => get_cargo_root,
+        LocalMode => get_cargo_root_nearest
+    };
+
+    let p = get_cargo_dir().get();
+
+    let sources = HashMap();
+    try_parse_sources(&home.push("sources.json"), sources);
+    try_parse_sources(&home.push("local-sources.json"), sources);
+
+    let dep_cache = HashMap();
+
+    let mut c = Cargo {
+        pgp: pgp::supported(),
+        root: home,
+        installdir: p,
+        bindir: p.push("bin"),
+        libdir: p.push("lib"),
+        workdir: p.push("work"),
+        sourcedir: home.push("sources"),
+        sources: sources,
+        mut current_install: ~"",
+        dep_cache: dep_cache,
+        opts: opts
+    };
+
+    need_dir(&c.root);
+    need_dir(&c.installdir);
+    need_dir(&c.sourcedir);
+    need_dir(&c.workdir);
+    need_dir(&c.libdir);
+    need_dir(&c.bindir);
+
+    for sources.each_key |k| {
+        let mut s = sources.get(k);
+        load_source_packages(&c, s);
+        sources.insert(k, s);
+    }
+
+    if c.pgp {
+        pgp::init(&c.root);
+    } else {
+        warn(~"command `gpg` was not found");
+        warn(~"you have to install gpg from source " +
+             ~" or package manager to get it to work correctly");
+    }
+
+    move c
+}
+
+fn for_each_package(c: &Cargo, b: fn(s: @Source, p: &Package)) {
+    for c.sources.each_value |v| {
+        for v.packages.each |p| {
+            b(v, p);
+        }
+    }
+}
+
+// Runs all programs in directory <buildpath>
+fn run_programs(buildpath: &Path) {
+    let newv = os::list_dir_path(buildpath);
+    for newv.each |ct| {
+        run::run_program(ct.to_str(), ~[]);
+    }
+}
+
+// Runs rustc in <path + subdir> with the given flags
+// and returns <patho + subdir>
+fn run_in_buildpath(what: &str, path: &Path, subdir: &Path, cf: &Path,
+                    extra_flags: ~[~str]) -> Option<Path> {
+    let buildpath = path.push_rel(subdir);
+    need_dir(&buildpath);
+    debug!("%s: %s -> %s", what, cf.to_str(), buildpath.to_str());
+    let p = run::program_output(rustc_sysroot(),
+                                ~[~"--out-dir",
+                                  buildpath.to_str(),
+                                  cf.to_str()] + extra_flags);
+    if p.status != 0 {
+        error(fmt!("rustc failed: %d\n%s\n%s", p.status, p.err, p.out));
+        return None;
+    }
+    Some(buildpath)
+}
+
+fn test_one_crate(_c: &Cargo, path: &Path, cf: &Path) {
+    let buildpath = match run_in_buildpath(~"testing", path,
+                                           &Path("test"),
+                                           cf,
+                                           ~[ ~"--test"]) {
+      None => return,
+    Some(bp) => bp
+  };
+  run_programs(&buildpath);
+}
+
+fn install_one_crate(c: &Cargo, path: &Path, cf: &Path) {
+    let buildpath = match run_in_buildpath(~"installing", path,
+                                           &Path("build"),
+                                           cf, ~[]) {
+      None => return,
+      Some(bp) => bp
+    };
+    let newv = os::list_dir_path(&buildpath);
+    let exec_suffix = os::exe_suffix();
+    for newv.each |ct| {
+        if (exec_suffix != ~"" && str::ends_with(ct.to_str(),
+                                                 exec_suffix)) ||
+            (exec_suffix == ~"" &&
+             !str::starts_with(ct.filename().get(),
+                               ~"lib")) {
+            debug!("  bin: %s", ct.to_str());
+            install_to_dir(*ct, &c.bindir);
+            if c.opts.mode == SystemMode {
+                // FIXME (#2662): Put this file in PATH / symlink it so it can
+                // be used as a generic executable
+                // `cargo install -G rustray` and `rustray file.obj`
+            }
+        } else {
+            debug!("  lib: %s", ct.to_str());
+            install_to_dir(*ct, &c.libdir);
+        }
+    }
+}
+
+
+fn rustc_sysroot() -> ~str {
+    match os::self_exe_path() {
+        Some(path) => {
+            let rustc = path.push_many([~"..", ~"bin", ~"rustc"]);
+            debug!("  rustc: %s", rustc.to_str());
+            rustc.to_str()
+        }
+        None => ~"rustc"
+    }
+}
+
+fn install_source(c: &Cargo, path: &Path) {
+    debug!("source: %s", path.to_str());
+    os::change_dir(path);
+
+    let mut cratefiles = ~[];
+    for os::walk_dir(&Path(".")) |p| {
+        if p.filetype() == Some(~".rc") {
+            cratefiles.push(*p);
+        }
+    }
+
+    if vec::is_empty(cratefiles) {
+        fail ~"this doesn't look like a rust package (no .rc files)";
+    }
+
+    for cratefiles.each |cf| {
+        match load_crate(cf) {
+            None => loop,
+            Some(crate) => {
+              for crate.deps.each |query| {
+                    // FIXME (#1356): handle cyclic dependencies
+                    // (n.b. #1356 says "Cyclic dependency is an error
+                    // condition")
+
+                    let wd = get_temp_workdir(c);
+                    install_query(c, &wd, *query);
+                }
+
+                os::change_dir(path);
+
+                if c.opts.test {
+                    test_one_crate(c, path, cf);
+                }
+                install_one_crate(c, path, cf);
+            }
+        }
+    }
+}
+
+fn install_git(c: &Cargo, wd: &Path, url: ~str, reference: Option<~str>) {
+    run::program_output(~"git", ~[~"clone", url, wd.to_str()]);
+    if reference.is_some() {
+        let r = reference.get();
+        os::change_dir(wd);
+        run::run_program(~"git", ~[~"checkout", r]);
+    }
+
+    install_source(c, wd);
+}
+
+fn install_curl(c: &Cargo, wd: &Path, url: ~str) {
+    let tarpath = wd.push("pkg.tar");
+    let p = run::program_output(~"curl", ~[~"-f", ~"-s", ~"-o",
+                                         tarpath.to_str(), url]);
+    if p.status != 0 {
+        fail fmt!("fetch of %s failed: %s", url, p.err);
+    }
+    run::run_program(~"tar", ~[~"-x", ~"--strip-components=1",
+                               ~"-C", wd.to_str(),
+                               ~"-f", tarpath.to_str()]);
+    install_source(c, wd);
+}
+
+fn install_file(c: &Cargo, wd: &Path, path: &Path) {
+    run::program_output(~"tar", ~[~"-x", ~"--strip-components=1",
+                                  ~"-C", wd.to_str(),
+                                  ~"-f", path.to_str()]);
+    install_source(c, wd);
+}
+
+fn install_package(c: &Cargo, src: ~str, wd: &Path, pkg: Package) {
+    let url = copy pkg.url;
+    let method = match pkg.method {
+        ~"git" => ~"git",
+        ~"file" => ~"file",
+        _ => ~"curl"
+    };
+
+    info(fmt!("installing %s/%s via %s...", src, pkg.name, method));
+
+    match method {
+        ~"git" => install_git(c, wd, url, copy pkg.reference),
+        ~"file" => install_file(c, wd, &Path(url)),
+        ~"curl" => install_curl(c, wd, url),
+        _ => ()
+    }
+}
+
+fn cargo_suggestion(c: &Cargo, fallback: fn())
+{
+    if c.sources.size() == 0u {
+        error(~"no sources defined - you may wish to run " +
+              ~"`cargo init`");
+        return;
+    }
+    fallback();
+}
+
+fn install_uuid(c: &Cargo, wd: &Path, uuid: ~str) {
+    let mut ps = ~[];
+    for_each_package(c, |s, p| {
+        if p.uuid == uuid {
+            vec::push(&mut ps, (s.name, copy *p));
+        }
+    });
+    if vec::len(ps) == 1u {
+        let (sname, p) = copy ps[0];
+        install_package(c, sname, wd, p);
+        return;
+    } else if vec::len(ps) == 0u {
+        cargo_suggestion(c, || {
+            error(~"can't find package: " + uuid);
+        });
+        return;
+    }
+    error(~"found multiple packages:");
+    for ps.each |elt| {
+        let (sname,p) = copy *elt;
+        info(~"  " + sname + ~"/" + p.uuid + ~" (" + p.name + ~")");
+    }
+}
+
+fn install_named(c: &Cargo, wd: &Path, name: ~str) {
+    let mut ps = ~[];
+    for_each_package(c, |s, p| {
+        if p.name == name {
+            vec::push(&mut ps, (s.name, copy *p));
+        }
+    });
+    if vec::len(ps) == 1u {
+        let (sname, p) = copy ps[0];
+        install_package(c, sname, wd, p);
+        return;
+    } else if vec::len(ps) == 0u {
+        cargo_suggestion(c, || {
+            error(~"can't find package: " + name);
+        });
+        return;
+    }
+    error(~"found multiple packages:");
+    for ps.each |elt| {
+        let (sname,p) = copy *elt;
+        info(~"  " + sname + ~"/" + p.uuid + ~" (" + p.name + ~")");
+    }
+}
+
+fn install_uuid_specific(c: &Cargo, wd: &Path, src: ~str, uuid: ~str) {
+    match c.sources.find(src) {
+        Some(s) => {
+            for s.packages.each |p| {
+                if p.uuid == uuid {
+                    install_package(c, src, wd, *p);
+                    return;
+                }
+            }
+        }
+        _ => ()
+    }
+    error(~"can't find package: " + src + ~"/" + uuid);
+}
+
+fn install_named_specific(c: &Cargo, wd: &Path, src: ~str, name: ~str) {
+    match c.sources.find(src) {
+        Some(s) => {
+            for s.packages.each |p| {
+                if p.name == name {
+                    install_package(c, src, wd, *p);
+                    return;
+                }
+            }
+        }
+        _ => ()
+    }
+    error(~"can't find package: " + src + ~"/" + name);
+}
+
+fn cmd_uninstall(c: &Cargo) {
+    if vec::len(c.opts.free) < 3u {
+        cmd_usage();
+        return;
+    }
+
+    let lib = &c.libdir;
+    let bin = &c.bindir;
+    let target = c.opts.free[2u];
+
+    // FIXME (#2662): needs stronger pattern matching
+    // FIXME (#2662): needs to uninstall from a specified location in a
+    // cache instead of looking for it (binaries can be uninstalled by
+    // name only)
+
+    fn try_uninstall(p: &Path) -> bool {
+        if os::remove_file(p) {
+            info(~"uninstalled: '" + p.to_str() + ~"'");
+            true
+        } else {
+            error(~"could not uninstall: '" +
+                  p.to_str() + ~"'");
+            false
+        }
+    }
+
+    if is_uuid(target) {
+        for os::list_dir(lib).each |file| {
+            match str::find_str(*file, ~"-" + target + ~"-") {
+              Some(_) => if !try_uninstall(&lib.push(*file)) { return },
+              None => ()
+            }
+        }
+        error(~"can't find package with uuid: " + target);
+    } else {
+        for os::list_dir(lib).each |file| {
+            match str::find_str(*file, ~"lib" + target + ~"-") {
+              Some(_) => if !try_uninstall(&lib.push(*file)) { return },
+              None => ()
+            }
+        }
+        for os::list_dir(bin).each |file| {
+            match str::find_str(*file, target) {
+              Some(_) => if !try_uninstall(&lib.push(*file)) { return },
+              None => ()
+            }
+        }
+
+        error(~"can't find package with name: " + target);
+    }
+}
+
+fn install_query(c: &Cargo, wd: &Path, target: ~str) {
+    match c.dep_cache.find(target) {
+        Some(inst) => {
+            if inst {
+                return;
+            }
+        }
+        None => ()
+    }
+
+    c.dep_cache.insert(target, true);
+
+    if is_archive_path(target) {
+        install_file(c, wd, &Path(target));
+        return;
+    } else if is_git_url(target) {
+        let reference = if c.opts.free.len() >= 4u {
+            Some(c.opts.free[3u])
+        } else {
+            None
+        };
+        install_git(c, wd, target, reference);
+    } else if !valid_pkg_name(target) && has_archive_extension(target) {
+        install_curl(c, wd, target);
+        return;
+    } else {
+        let mut ps = copy target;
+
+        match str::find_char(ps, '/') {
+            option::Some(idx) => {
+                let source = str::slice(ps, 0u, idx);
+                ps = str::slice(ps, idx + 1u, str::len(ps));
+                if is_uuid(ps) {
+                    install_uuid_specific(c, wd, source, ps);
+                } else {
+                    install_named_specific(c, wd, source, ps);
+                }
+            }
+            option::None => {
+                if is_uuid(ps) {
+                    install_uuid(c, wd, ps);
+                } else {
+                    install_named(c, wd, ps);
+                }
+            }
+        }
+    }
+
+    // FIXME (#2662): This whole dep_cache and current_install thing is
+    // a bit of a hack. It should be cleaned up in the future.
+
+    if target == c.current_install {
+        for c.dep_cache.each |k, _v| {
+            c.dep_cache.remove(k);
+        }
+
+        c.current_install = ~"";
+    }
+}
+
+fn get_temp_workdir(c: &Cargo) -> Path {
+    match tempfile::mkdtemp(&c.workdir, "cargo") {
+      Some(wd) => wd,
+      None => fail fmt!("needed temp dir: %s",
+                        c.workdir.to_str())
+    }
+}
+
+fn cmd_install(c: &Cargo) unsafe {
+    let wd = get_temp_workdir(c);
+
+    if vec::len(c.opts.free) == 2u {
+        let cwd = os::getcwd();
+        let status = run::run_program(~"cp", ~[~"-R", cwd.to_str(),
+                                               wd.to_str()]);
+
+        if status != 0 {
+            fail fmt!("could not copy directory: %s", cwd.to_str());
+        }
+
+        install_source(c, &wd);
+        return;
+    }
+
+    sync(c);
+
+    let query = c.opts.free[2];
+    c.current_install = query.to_str();
+
+    install_query(c, &wd, query);
+}
+
+fn sync(c: &Cargo) {
+    for c.sources.each_key |k| {
+        let mut s = c.sources.get(k);
+        sync_one(c, s);
+        c.sources.insert(k, s);
+    }
+}
+
+fn sync_one_file(c: &Cargo, dir: &Path, src: @Source) -> bool {
+    let name = src.name;
+    let srcfile = dir.push("source.json.new");
+    let destsrcfile = dir.push("source.json");
+    let pkgfile = dir.push("packages.json.new");
+    let destpkgfile = dir.push("packages.json");
+    let keyfile = dir.push("key.gpg");
+    let srcsigfile = dir.push("source.json.sig");
+    let sigfile = dir.push("packages.json.sig");
+    let url = Path(src.url);
+    let mut has_src_file = false;
+
+    if !os::copy_file(&url.push("packages.json"), &pkgfile) {
+        error(fmt!("fetch for source %s (url %s) failed",
+                   name, url.to_str()));
+        return false;
+    }
+
+    if os::copy_file(&url.push("source.json"), &srcfile) {
+        has_src_file = false;
+    }
+
+    os::copy_file(&url.push("source.json.sig"), &srcsigfile);
+    os::copy_file(&url.push("packages.json.sig"), &sigfile);
+
+    match copy src.key {
+        Some(u) => {
+            let p = run::program_output(~"curl",
+                                        ~[~"-f", ~"-s",
+                                          ~"-o", keyfile.to_str(), u]);
+            if p.status != 0 {
+                error(fmt!("fetch for source %s (key %s) failed", name, u));
+                return false;
+            }
+            pgp::add(&c.root, &keyfile);
+        }
+        _ => ()
+    }
+    match (src.key, src.keyfp) {
+        (Some(_), Some(f)) => {
+            let r = pgp::verify(&c.root, &pkgfile, &sigfile);
+
+            if !r {
+                error(fmt!("signature verification failed for source %s with \
+                            key %s", name, f));
+                return false;
+            }
+
+            if has_src_file {
+                let e = pgp::verify(&c.root, &srcfile, &srcsigfile);
+
+                if !e {
+                    error(fmt!("signature verification failed for source %s \
+                                with key %s", name, f));
+                    return false;
+                }
+            }
+        }
+        _ => ()
+    }
+
+    copy_warn(&pkgfile, &destpkgfile);
+
+    if has_src_file {
+        copy_warn(&srcfile, &destsrcfile);
+    }
+
+    os::remove_file(&keyfile);
+    os::remove_file(&srcfile);
+    os::remove_file(&srcsigfile);
+    os::remove_file(&pkgfile);
+    os::remove_file(&sigfile);
+
+    info(fmt!("synced source: %s", name));
+
+    return true;
+}
+
+fn sync_one_git(c: &Cargo, dir: &Path, src: @Source) -> bool {
+    let name = src.name;
+    let srcfile = dir.push("source.json");
+    let pkgfile = dir.push("packages.json");
+    let keyfile = dir.push("key.gpg");
+    let srcsigfile = dir.push("source.json.sig");
+    let sigfile = dir.push("packages.json.sig");
+    let url = src.url;
+
+    fn rollback(name: ~str, dir: &Path, insecure: bool) {
+        fn msg(name: ~str, insecure: bool) {
+            error(fmt!("could not rollback source: %s", name));
+
+            if insecure {
+                warn(~"a past security check failed on source " +
+                     name + ~" and rolling back the source failed -"
+                     + ~" this source may be compromised");
+            }
+        }
+
+        if !os::change_dir(dir) {
+            msg(name, insecure);
+        }
+        else {
+            let p = run::program_output(~"git", ~[~"reset", ~"--hard",
+                                                ~"HEAD@{1}"]);
+
+            if p.status != 0 {
+                msg(name, insecure);
+            }
+        }
+    }
+
+    if !os::path_exists(&dir.push(".git")) {
+        let p = run::program_output(~"git", ~[~"clone", url, dir.to_str()]);
+
+        if p.status != 0 {
+            error(fmt!("fetch for source %s (url %s) failed", name, url));
+            return false;
+        }
+    }
+    else {
+        if !os::change_dir(dir) {
+            error(fmt!("fetch for source %s (url %s) failed", name, url));
+            return false;
+        }
+
+        let p = run::program_output(~"git", ~[~"pull"]);
+
+        if p.status != 0 {
+            error(fmt!("fetch for source %s (url %s) failed", name, url));
+            return false;
+        }
+    }
+
+    let has_src_file = os::path_exists(&srcfile);
+
+    match copy src.key {
+        Some(u) => {
+            let p = run::program_output(~"curl",
+                                        ~[~"-f", ~"-s",
+                                          ~"-o", keyfile.to_str(), u]);
+            if p.status != 0 {
+                error(fmt!("fetch for source %s (key %s) failed", name, u));
+                rollback(name, dir, false);
+                return false;
+            }
+            pgp::add(&c.root, &keyfile);
+        }
+        _ => ()
+    }
+    match (src.key, src.keyfp) {
+        (Some(_), Some(f)) => {
+            let r = pgp::verify(&c.root, &pkgfile, &sigfile);
+
+            if !r {
+                error(fmt!("signature verification failed for source %s with \
+                            key %s", name, f));
+                rollback(name, dir, false);
+                return false;
+            }
+
+            if has_src_file {
+                let e = pgp::verify(&c.root, &srcfile, &srcsigfile);
+
+                if !e {
+                    error(fmt!("signature verification failed for source %s \
+                                with key %s", name, f));
+                    rollback(name, dir, false);
+                    return false;
+                }
+            }
+        }
+        _ => ()
+    }
+
+    os::remove_file(&keyfile);
+
+    info(fmt!("synced source: %s", name));
+
+    return true;
+}
+
+fn sync_one_curl(c: &Cargo, dir: &Path, src: @Source) -> bool {
+    let name = src.name;
+    let srcfile = dir.push("source.json.new");
+    let destsrcfile = dir.push("source.json");
+    let pkgfile = dir.push("packages.json.new");
+    let destpkgfile = dir.push("packages.json");
+    let keyfile = dir.push("key.gpg");
+    let srcsigfile = dir.push("source.json.sig");
+    let sigfile = dir.push("packages.json.sig");
+    let mut url = src.url;
+    let smart = !str::ends_with(src.url, ~"packages.json");
+    let mut has_src_file = false;
+
+    if smart {
+        url += ~"/packages.json";
+    }
+
+    let p = run::program_output(~"curl",
+                                ~[~"-f", ~"-s",
+                                  ~"-o", pkgfile.to_str(), url]);
+
+    if p.status != 0 {
+        error(fmt!("fetch for source %s (url %s) failed", name, url));
+        return false;
+    }
+    if smart {
+        url = src.url + ~"/source.json";
+        let p =
+            run::program_output(~"curl",
+                                ~[~"-f", ~"-s",
+                                  ~"-o", srcfile.to_str(), url]);
+
+        if p.status == 0 {
+            has_src_file = true;
+        }
+    }
+
+    match copy src.key {
+       Some(u) => {
+            let p = run::program_output(~"curl",
+                                        ~[~"-f", ~"-s",
+                                          ~"-o", keyfile.to_str(), u]);
+            if p.status != 0 {
+                error(fmt!("fetch for source %s (key %s) failed", name, u));
+                return false;
+            }
+            pgp::add(&c.root, &keyfile);
+        }
+        _ => ()
+    }
+    match (src.key, src.keyfp) {
+        (Some(_), Some(f)) => {
+            if smart {
+                url = src.url + ~"/packages.json.sig";
+            }
+            else {
+                url = src.url + ~".sig";
+            }
+
+            let mut p = run::program_output(~"curl",
+                                            ~[~"-f", ~"-s", ~"-o",
+                                              sigfile.to_str(), url]);
+            if p.status != 0 {
+                error(fmt!("fetch for source %s (sig %s) failed", name, url));
+                return false;
+            }
+
+            let r = pgp::verify(&c.root, &pkgfile, &sigfile);
+
+            if !r {
+                error(fmt!("signature verification failed for source %s with \
+                            key %s", name, f));
+                return false;
+            }
+
+            if smart && has_src_file {
+                url = src.url + ~"/source.json.sig";
+
+                p = run::program_output(~"curl",
+                                        ~[~"-f", ~"-s", ~"-o",
+                                          srcsigfile.to_str(), url]);
+                if p.status != 0 {
+                    error(fmt!("fetch for source %s (sig %s) failed",
+                          name, url));
+                    return false;
+                }
+
+                let e = pgp::verify(&c.root, &srcfile, &srcsigfile);
+
+                if !e {
+                    error(~"signature verification failed for " +
+                          ~"source " + name + ~" with key " + f);
+                    return false;
+                }
+            }
+        }
+        _ => ()
+    }
+
+    copy_warn(&pkgfile, &destpkgfile);
+
+    if smart && has_src_file {
+        copy_warn(&srcfile, &destsrcfile);
+    }
+
+    os::remove_file(&keyfile);
+    os::remove_file(&srcfile);
+    os::remove_file(&srcsigfile);
+    os::remove_file(&pkgfile);
+    os::remove_file(&sigfile);
+
+    info(fmt!("synced source: %s", name));
+
+    return true;
+}
+
+fn sync_one(c: &Cargo, src: @Source) {
+    let name = src.name;
+    let dir = c.sourcedir.push(name);
+
+    info(fmt!("syncing source: %s...", name));
+
+    need_dir(&dir);
+
+    let result = match src.method {
+        ~"git" => sync_one_git(c, &dir, src),
+        ~"file" => sync_one_file(c, &dir, src),
+        _ => sync_one_curl(c, &dir, src)
+    };
+
+    if result {
+        load_source_info(c, src);
+        load_source_packages(c, src);
+    }
+}
+
+fn cmd_init(c: &Cargo) {
+    let srcurl = ~"http://www.rust-lang.org/cargo/sources.json";
+    let sigurl = ~"http://www.rust-lang.org/cargo/sources.json.sig";
+
+    let srcfile = c.root.push("sources.json.new");
+    let sigfile = c.root.push("sources.json.sig");
+    let destsrcfile = c.root.push("sources.json");
+
+    let p =
+        run::program_output(~"curl", ~[~"-f", ~"-s",
+                                       ~"-o", srcfile.to_str(), srcurl]);
+    if p.status != 0 {
+        error(fmt!("fetch of sources.json failed: %s", p.out));
+        return;
+    }
+
+    let p =
+        run::program_output(~"curl", ~[~"-f", ~"-s",
+                                       ~"-o", sigfile.to_str(), sigurl]);
+    if p.status != 0 {
+        error(fmt!("fetch of sources.json.sig failed: %s", p.out));
+        return;
+    }
+
+    let r = pgp::verify(&c.root, &srcfile, &sigfile);
+    if !r {
+        error(fmt!("signature verification failed for '%s'",
+                   srcfile.to_str()));
+        return;
+    }
+
+    copy_warn(&srcfile, &destsrcfile);
+    os::remove_file(&srcfile);
+    os::remove_file(&sigfile);
+
+    info(fmt!("initialized .cargo in %s", c.root.to_str()));
+}
+
+fn print_pkg(s: @Source, p: &Package) {
+    let mut m = s.name + ~"/" + p.name + ~" (" + p.uuid + ~")";
+    if vec::len(p.tags) > 0u {
+        m = m + ~" [" + str::connect(p.tags, ~", ") + ~"]";
+    }
+    info(m);
+    if p.description != ~"" {
+        print(~"   >> " + p.description + ~"\n")
+    }
+}
+
+fn print_source(s: @Source) {
+    info(s.name + ~" (" + s.url + ~")");
+
+    let pks = sort::merge_sort(s.packages.get(), sys::shape_lt);
+    let l = vec::len(pks);
+
+    print(io::with_str_writer(|writer| {
+        let mut list = ~"   >> ";
+
+        for vec::eachi(pks) |i, pk| {
+            if str::len(list) > 78u {
+                writer.write_line(list);
+                list = ~"   >> ";
+            }
+            list += pk.name + (if l - 1u == i { ~"" } else { ~", " });
+        }
+
+        writer.write_line(list);
+    }));
+}
+
+fn cmd_list(c: &Cargo) {
+    sync(c);
+
+    if vec::len(c.opts.free) >= 3u {
+        let v = vec::view(c.opts.free, 2u, vec::len(c.opts.free));
+        for vec::each(v) |name| {
+            if !valid_pkg_name(*name) {
+                error(fmt!("'%s' is an invalid source name", *name));
+            } else {
+                match c.sources.find(*name) {
+                    Some(source) => {
+                        print_source(source);
+                    }
+                    None => {
+                        error(fmt!("no such source: %s", *name));
+                    }
+                }
+            }
+        }
+    } else {
+        for c.sources.each_value |v| {
+            print_source(v);
+        }
+    }
+}
+
+fn cmd_search(c: &Cargo) {
+    if vec::len(c.opts.free) < 3u {
+        cmd_usage();
+        return;
+    }
+
+    sync(c);
+
+    let mut n = 0;
+    let name = c.opts.free[2];
+    let tags = vec::slice(c.opts.free, 3u, vec::len(c.opts.free));
+    for_each_package(c, |s, p| {
+        if (str::contains(p.name, name) || name == ~"*") &&
+            vec::all(tags, |t| vec::contains(p.tags, t) ) {
+            print_pkg(s, p);
+            n += 1;
+        }
+    });
+    info(fmt!("found %d packages", n));
+}
+
+fn install_to_dir(srcfile: &Path, destdir: &Path) {
+    let newfile = destdir.push(srcfile.filename().get());
+
+    let status = run::run_program(~"cp", ~[~"-r", srcfile.to_str(),
+                                           newfile.to_str()]);
+    if status == 0 {
+        info(fmt!("installed: '%s'", newfile.to_str()));
+    } else {
+        error(fmt!("could not install: '%s'", newfile.to_str()));
+    }
+}
+
+fn dump_cache(c: &Cargo) {
+    need_dir(&c.root);
+
+    let out = c.root.push("cache.json");
+    let _root = json::Object(~LinearMap());
+
+    if os::path_exists(&out) {
+        copy_warn(&out, &c.root.push("cache.json.old"));
+    }
+}
+fn dump_sources(c: &Cargo) {
+    if c.sources.size() < 1u {
+        return;
+    }
+
+    need_dir(&c.root);
+
+    let out = c.root.push("sources.json");
+
+    if os::path_exists(&out) {
+        copy_warn(&out, &c.root.push("sources.json.old"));
+    }
+
+    match io::buffered_file_writer(&out) {
+        result::Ok(writer) => {
+            let mut hash = ~LinearMap();
+
+            for c.sources.each |k, v| {
+                let mut chash = ~LinearMap();
+
+                chash.insert(~"url", json::String(v.url));
+                chash.insert(~"method", json::String(v.method));
+
+                match copy v.key {
+                    Some(key) => {
+                        chash.insert(~"key", json::String(copy key));
+                    }
+                    _ => ()
+                }
+                match copy v.keyfp {
+                    Some(keyfp) => {
+                        chash.insert(~"keyfp", json::String(copy keyfp));
+                    }
+                    _ => ()
+                }
+
+                hash.insert(copy k, json::Object(move chash));
+            }
+
+            json::to_writer(writer, &json::Object(move hash))
+        }
+        result::Err(e) => {
+            error(fmt!("could not dump sources: %s", e));
+        }
+    }
+}
+
+fn copy_warn(srcfile: &Path, destfile: &Path) {
+    if !os::copy_file(srcfile, destfile) {
+        warn(fmt!("copying %s to %s failed",
+                  srcfile.to_str(), destfile.to_str()));
+    }
+}
+
+fn cmd_sources(c: &Cargo) {
+    if vec::len(c.opts.free) < 3u {
+        for c.sources.each_value |v| {
+            info(fmt!("%s (%s) via %s",
+                      v.name, v.url, v.method));
+        }
+        return;
+    }
+
+    let action = c.opts.free[2u];
+
+    match action {
+        ~"clear" => {
+          for c.sources.each_key |k| {
+                c.sources.remove(k);
+            }
+
+            info(~"cleared sources");
+        }
+        ~"add" => {
+            if vec::len(c.opts.free) < 5u {
+                cmd_usage();
+                return;
+            }
+
+            let name = c.opts.free[3u];
+            let url = c.opts.free[4u];
+
+            if !valid_pkg_name(name) {
+                error(fmt!("'%s' is an invalid source name", name));
+                return;
+            }
+
+            if c.sources.contains_key(name) {
+                error(fmt!("source already exists: %s", name));
+            } else {
+                c.sources.insert(name, @Source {
+                    name: name,
+                    mut url: url,
+                    mut method: assume_source_method(url),
+                    mut key: None,
+                    mut keyfp: None,
+                    packages: DVec()
+                });
+                info(fmt!("added source: %s", name));
+            }
+        }
+        ~"remove" => {
+            if vec::len(c.opts.free) < 4u {
+                cmd_usage();
+                return;
+            }
+
+            let name = c.opts.free[3u];
+
+            if !valid_pkg_name(name) {
+                error(fmt!("'%s' is an invalid source name", name));
+                return;
+            }
+
+            if c.sources.contains_key(name) {
+                c.sources.remove(name);
+                info(fmt!("removed source: %s", name));
+            } else {
+                error(fmt!("no such source: %s", name));
+            }
+        }
+        ~"set-url" => {
+            if vec::len(c.opts.free) < 5u {
+                cmd_usage();
+                return;
+            }
+
+            let name = c.opts.free[3u];
+            let url = c.opts.free[4u];
+
+            if !valid_pkg_name(name) {
+                error(fmt!("'%s' is an invalid source name", name));
+                return;
+            }
+
+            match c.sources.find(name) {
+                Some(source) => {
+                    let old = copy source.url;
+                    let method = assume_source_method(url);
+
+                    source.url = url;
+                    source.method = method;
+
+                    c.sources.insert(name, source);
+
+                    info(fmt!("changed source url: '%s' to '%s'", old, url));
+                }
+                None => {
+                    error(fmt!("no such source: %s", name));
+                }
+            }
+        }
+        ~"set-method" => {
+            if vec::len(c.opts.free) < 5u {
+                cmd_usage();
+                return;
+            }
+
+            let name = c.opts.free[3u];
+            let method = c.opts.free[4u];
+
+            if !valid_pkg_name(name) {
+                error(fmt!("'%s' is an invalid source name", name));
+                return;
+            }
+
+            match c.sources.find(name) {
+                Some(source) => {
+                    let old = copy source.method;
+
+                    source.method = match method {
+                        ~"git" => ~"git",
+                        ~"file" => ~"file",
+                        _ => ~"curl"
+                    };
+
+                    c.sources.insert(name, source);
+
+                    info(fmt!("changed source method: '%s' to '%s'", old,
+                         method));
+                }
+                None => {
+                    error(fmt!("no such source: %s", name));
+                }
+            }
+        }
+        ~"rename" => {
+            if vec::len(c.opts.free) < 5u {
+                cmd_usage();
+                return;
+            }
+
+            let name = c.opts.free[3u];
+            let newn = c.opts.free[4u];
+
+            if !valid_pkg_name(name) {
+                error(fmt!("'%s' is an invalid source name", name));
+                return;
+            }
+            if !valid_pkg_name(newn) {
+                error(fmt!("'%s' is an invalid source name", newn));
+                return;
+            }
+
+            match c.sources.find(name) {
+                Some(source) => {
+                    c.sources.remove(name);
+                    c.sources.insert(newn, source);
+                    info(fmt!("renamed source: %s to %s", name, newn));
+                }
+                None => {
+                    error(fmt!("no such source: %s", name));
+                }
+            }
+        }
+        _ => cmd_usage()
+    }
+}
+
+fn cmd_usage() {
+    print(~"Usage: cargo <cmd> [options] [args..]
+e.g. cargo install <name>
+
+Where <cmd> is one of:
+    init, install, list, search, sources,
+    uninstall, usage
+
+Options:
+
+    -h, --help                  Display this message
+    <cmd> -h, <cmd> --help      Display help for <cmd>
+");
+}
+
+fn cmd_usage_init() {
+    print(~"cargo init
+
+Re-initialize cargo in ~/.cargo. Clears all sources and then adds the
+default sources from <www.rust-lang.org/sources.json>.");
+}
+
+fn cmd_usage_install() {
+    print(~"cargo install
+cargo install [source/]<name>[@version]
+cargo install [source/]<uuid>[@version]
+cargo install <git url> [ref]
+cargo install <tarball url>
+cargo install <tarball file>
+
+Options:
+    --test      Run crate tests before installing
+    -g          Install to the user level (~/.cargo/bin/ instead of
+                locally in ./.cargo/bin/ by default)
+    -G          Install to the system level (/usr/local/lib/cargo/bin/)
+
+Install a crate. If no arguments are supplied, it installs from
+the current working directory. If a source is provided, only install
+from that source, otherwise it installs from any source.");
+}
+
+fn cmd_usage_uninstall() {
+    print(~"cargo uninstall [source/]<name>[@version]
+cargo uninstall [source/]<uuid>[@version]
+cargo uninstall <meta-name>[@version]
+cargo uninstall <meta-uuid>[@version]
+
+Options:
+    -g          Remove from the user level (~/.cargo/bin/ instead of
+                locally in ./.cargo/bin/ by default)
+    -G          Remove from the system level (/usr/local/lib/cargo/bin/)
+
+Remove a crate. If a source is provided, only remove
+from that source, otherwise it removes from any source.
+If a crate was installed directly (git, tarball, etc.), you can remove
+it by metadata.");
+}
+
+fn cmd_usage_list() {
+    print(~"cargo list [sources..]
+
+If no arguments are provided, list all sources and their packages.
+If source names are provided, list those sources and their packages.
+");
+}
+
+fn cmd_usage_search() {
+    print(~"cargo search <query | '*'> [tags..]
+
+Search packages.");
+}
+
+fn cmd_usage_sources() {
+    print(~"cargo sources
+cargo sources add <name> <url>
+cargo sources remove <name>
+cargo sources rename <name> <new>
+cargo sources set-url <name> <url>
+cargo sources set-method <name> <method>
+
+If no arguments are supplied, list all sources (but not their packages).
+
+Commands:
+    add             Add a source. The source method will be guessed
+                    from the URL.
+    remove          Remove a source.
+    rename          Rename a source.
+    set-url         Change the URL for a source.
+    set-method      Change the method for a source.");
+}
+
+fn main() {
+    let argv = os::args();
+    let o = build_cargo_options(argv);
+
+    if vec::len(o.free) < 2u {
+        cmd_usage();
+        return;
+    }
+    if o.help {
+        match o.free[1] {
+            ~"init" => cmd_usage_init(),
+            ~"install" => cmd_usage_install(),
+            ~"uninstall" => cmd_usage_uninstall(),
+            ~"list" => cmd_usage_list(),
+            ~"search" => cmd_usage_search(),
+            ~"sources" => cmd_usage_sources(),
+            _ => cmd_usage()
+        }
+        return;
+    }
+    if o.free[1] == ~"usage" {
+        cmd_usage();
+        return;
+    }
+
+    let mut c = configure(o);
+    let home = c.root;
+    let first_time = os::path_exists(&home.push("sources.json"));
+
+    if !first_time && o.free[1] != ~"init" {
+        cmd_init(&c);
+
+        // FIXME (#2662): shouldn't need to reconfigure
+        c = configure(o);
+    }
+
+    let c = &move c;
+
+    match o.free[1] {
+        ~"init" => cmd_init(c),
+        ~"install" => cmd_install(c),
+        ~"uninstall" => cmd_uninstall(c),
+        ~"list" => cmd_list(c),
+        ~"search" => cmd_search(c),
+        ~"sources" => cmd_sources(c),
+        _ => cmd_usage()
+    }
+
+    dump_cache(c);
+    dump_sources(c);
+}
diff --git a/src/libcargo/cargo.rs b/src/libcargo/cargo.rs
deleted file mode 100644
index 4ccabdae9e4..00000000000
--- a/src/libcargo/cargo.rs
+++ /dev/null
@@ -1,1982 +0,0 @@
-// cargo.rs - Rust package manager
-
-#[legacy_exports];
-
-use syntax::{ast, codemap, parse, visit, attr};
-use syntax::diagnostic::span_handler;
-use codemap::span;
-use rustc::metadata::filesearch::{get_cargo_root, get_cargo_root_nearest,
-                                     get_cargo_sysroot, libdir};
-use syntax::diagnostic;
-
-use result::{Ok, Err};
-use io::WriterUtil;
-use send_map::linear::LinearMap;
-use std::{map, json, tempfile, term, sort, getopts};
-use map::HashMap;
-use to_str::to_str;
-use getopts::{optflag, optopt, opt_present};
-use dvec::DVec;
-
-struct Package {
-    name: ~str,
-    uuid: ~str,
-    url: ~str,
-    method: ~str,
-    description: ~str,
-    reference: Option<~str>,
-    tags: ~[~str],
-    versions: ~[(~str, ~str)]
-}
-
-impl Package : cmp::Ord {
-    #[cfg(stage0)]
-    pure fn lt(other: &Package) -> bool {
-        if self.name.lt(&(*other).name) { return true; }
-        if (*other).name.lt(&self.name) { return false; }
-        if self.uuid.lt(&(*other).uuid) { return true; }
-        if (*other).uuid.lt(&self.uuid) { return false; }
-        if self.url.lt(&(*other).url) { return true; }
-        if (*other).url.lt(&self.url) { return false; }
-        if self.method.lt(&(*other).method) { return true; }
-        if (*other).method.lt(&self.method) { return false; }
-        if self.description.lt(&(*other).description) { return true; }
-        if (*other).description.lt(&self.description) { return false; }
-        if self.tags.lt(&(*other).tags) { return true; }
-        if (*other).tags.lt(&self.tags) { return false; }
-        if self.versions.lt(&(*other).versions) { return true; }
-        return false;
-    }
-    #[cfg(stage1)]
-    #[cfg(stage2)]
-    pure fn lt(&self, other: &Package) -> bool {
-        if (*self).name.lt(&(*other).name) { return true; }
-        if (*other).name.lt(&(*self).name) { return false; }
-        if (*self).uuid.lt(&(*other).uuid) { return true; }
-        if (*other).uuid.lt(&(*self).uuid) { return false; }
-        if (*self).url.lt(&(*other).url) { return true; }
-        if (*other).url.lt(&(*self).url) { return false; }
-        if (*self).method.lt(&(*other).method) { return true; }
-        if (*other).method.lt(&(*self).method) { return false; }
-        if (*self).description.lt(&(*other).description) { return true; }
-        if (*other).description.lt(&(*self).description) { return false; }
-        if (*self).tags.lt(&(*other).tags) { return true; }
-        if (*other).tags.lt(&(*self).tags) { return false; }
-        if (*self).versions.lt(&(*other).versions) { return true; }
-        return false;
-    }
-    #[cfg(stage0)]
-    pure fn le(other: &Package) -> bool { !(*other).lt(&self) }
-    #[cfg(stage1)]
-    #[cfg(stage2)]
-    pure fn le(&self, other: &Package) -> bool { !(*other).lt(&(*self)) }
-    #[cfg(stage0)]
-    pure fn ge(other: &Package) -> bool { !self.lt(other)     }
-    #[cfg(stage1)]
-    #[cfg(stage2)]
-    pure fn ge(&self, other: &Package) -> bool { !(*self).lt(other)     }
-    #[cfg(stage0)]
-    pure fn gt(other: &Package) -> bool { (*other).lt(&self)  }
-    #[cfg(stage1)]
-    #[cfg(stage2)]
-    pure fn gt(&self, other: &Package) -> bool { (*other).lt(&(*self))  }
-}
-
-struct Source {
-    name: ~str,
-    mut url: ~str,
-    mut method: ~str,
-    mut key: Option<~str>,
-    mut keyfp: Option<~str>,
-    packages: DVec<Package>
-}
-
-struct Cargo {
-    pgp: bool,
-    root: Path,
-    installdir: Path,
-    bindir: Path,
-    libdir: Path,
-    workdir: Path,
-    sourcedir: Path,
-    sources: map::HashMap<~str, @Source>,
-    mut current_install: ~str,
-    dep_cache: map::HashMap<~str, bool>,
-    opts: Options
-}
-
-struct Crate {
-    name: ~str,
-    vers: ~str,
-    uuid: ~str,
-    desc: Option<~str>,
-    sigs: Option<~str>,
-    crate_type: Option<~str>,
-    deps: ~[~str]
-}
-
-struct Options {
-    test: bool,
-    mode: Mode,
-    free: ~[~str],
-    help: bool,
-}
-
-enum Mode { SystemMode, UserMode, LocalMode }
-
-impl Mode : cmp::Eq {
-    #[cfg(stage0)]
-    pure fn eq(other: &Mode) -> bool {
-        (self as uint) == ((*other) as uint)
-    }
-    #[cfg(stage1)]
-    #[cfg(stage2)]
-    pure fn eq(&self, other: &Mode) -> bool {
-        ((*self) as uint) == ((*other) as uint)
-    }
-    #[cfg(stage0)]
-    pure fn ne(other: &Mode) -> bool { !self.eq(other) }
-    #[cfg(stage1)]
-    #[cfg(stage2)]
-    pure fn ne(&self, other: &Mode) -> bool { !(*self).eq(other) }
-}
-
-fn opts() -> ~[getopts::Opt] {
-    ~[optflag(~"g"), optflag(~"G"), optflag(~"test"),
-     optflag(~"h"), optflag(~"help")]
-}
-
-fn info(msg: ~str) {
-    let out = io::stdout();
-
-    if term::color_supported() {
-        term::fg(out, term::color_green);
-        out.write_str(~"info: ");
-        term::reset(out);
-        out.write_line(msg);
-    } else { out.write_line(~"info: " + msg); }
-}
-
-fn warn(msg: ~str) {
-    let out = io::stdout();
-
-    if term::color_supported() {
-        term::fg(out, term::color_yellow);
-        out.write_str(~"warning: ");
-        term::reset(out);
-        out.write_line(msg);
-    }else { out.write_line(~"warning: " + msg); }
-}
-
-fn error(msg: ~str) {
-    let out = io::stdout();
-
-    if term::color_supported() {
-        term::fg(out, term::color_red);
-        out.write_str(~"error: ");
-        term::reset(out);
-        out.write_line(msg);
-    }
-    else { out.write_line(~"error: " + msg); }
-}
-
-fn is_uuid(id: ~str) -> bool {
-    let parts = str::split_str(id, ~"-");
-    if vec::len(parts) == 5u {
-        let mut correct = 0u;
-        for vec::eachi(parts) |i, part| {
-            fn is_hex_digit(+ch: char) -> bool {
-                ('0' <= ch && ch <= '9') ||
-                ('a' <= ch && ch <= 'f') ||
-                ('A' <= ch && ch <= 'F')
-            }
-
-            if !part.all(is_hex_digit) {
-                return false;
-            }
-
-            match i {
-                0u => {
-                    if part.len() == 8u {
-                        correct += 1u;
-                    }
-                }
-                1u | 2u | 3u => {
-                    if part.len() == 4u {
-                        correct += 1u;
-                    }
-                }
-                4u => {
-                    if part.len() == 12u {
-                        correct += 1u;
-                    }
-                }
-                _ => { }
-            }
-        }
-        if correct >= 5u {
-            return true;
-        }
-    }
-    return false;
-}
-
-#[test]
-fn test_is_uuid() {
-    assert is_uuid(~"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaafAF09");
-    assert !is_uuid(~"aaaaaaaa-aaaa-aaaa-aaaaa-aaaaaaaaaaaa");
-    assert !is_uuid(~"");
-    assert !is_uuid(~"aaaaaaaa-aaa -aaaa-aaaa-aaaaaaaaaaaa");
-    assert !is_uuid(~"aaaaaaaa-aaa!-aaaa-aaaa-aaaaaaaaaaaa");
-    assert !is_uuid(~"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa-a");
-    assert !is_uuid(~"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaป");
-}
-
-// FIXME (#2661): implement url/URL parsing so we don't have to resort
-// to weak checks
-
-fn has_archive_extension(p: ~str) -> bool {
-    str::ends_with(p, ~".tar") ||
-    str::ends_with(p, ~".tar.gz") ||
-    str::ends_with(p, ~".tar.bz2") ||
-    str::ends_with(p, ~".tar.Z") ||
-    str::ends_with(p, ~".tar.lz") ||
-    str::ends_with(p, ~".tar.xz") ||
-    str::ends_with(p, ~".tgz") ||
-    str::ends_with(p, ~".tbz") ||
-    str::ends_with(p, ~".tbz2") ||
-    str::ends_with(p, ~".tb2") ||
-    str::ends_with(p, ~".taz") ||
-    str::ends_with(p, ~".tlz") ||
-    str::ends_with(p, ~".txz")
-}
-
-fn is_archive_path(u: ~str) -> bool {
-    has_archive_extension(u) && os::path_exists(&Path(u))
-}
-
-fn is_archive_url(u: ~str) -> bool {
-    // FIXME (#2661): this requires the protocol bit - if we had proper
-    // url parsing, we wouldn't need it
-
-    match str::find_str(u, ~"://") {
-        option::Some(_) => has_archive_extension(u),
-        _ => false
-    }
-}
-
-fn is_git_url(url: ~str) -> bool {
-    if str::ends_with(url, ~"/") { str::ends_with(url, ~".git/") }
-    else {
-        str::starts_with(url, ~"git://") || str::ends_with(url, ~".git")
-    }
-}
-
-fn assume_source_method(url: ~str) -> ~str {
-    if is_git_url(url) {
-        return ~"git";
-    }
-    if str::starts_with(url, ~"file://") || os::path_exists(&Path(url)) {
-        return ~"file";
-    }
-
-    ~"curl"
-}
-
-fn load_link(mis: ~[@ast::meta_item]) -> (Option<~str>,
-                                         Option<~str>,
-                                         Option<~str>) {
-    let mut name = None;
-    let mut vers = None;
-    let mut uuid = None;
-    for mis.each |a| {
-        match a.node {
-            ast::meta_name_value(v, {node: ast::lit_str(s), span: _}) => {
-                match v {
-                    ~"name" => name = Some(*s),
-                    ~"vers" => vers = Some(*s),
-                    ~"uuid" => uuid = Some(*s),
-                    _ => { }
-                }
-            }
-            _ => fail ~"load_link: meta items must be name-values"
-        }
-    }
-    (name, vers, uuid)
-}
-
-fn load_crate(filename: &Path) -> Option<Crate> {
-    let sess = parse::new_parse_sess(None);
-    let c = parse::parse_crate_from_crate_file(filename, ~[], sess);
-
-    let mut name = None;
-    let mut vers = None;
-    let mut uuid = None;
-    let mut desc = None;
-    let mut sigs = None;
-    let mut crate_type = None;
-
-    for c.node.attrs.each |a| {
-        match a.node.value.node {
-            ast::meta_name_value(v, {node: ast::lit_str(_), span: _}) => {
-                match v {
-                    ~"desc" => desc = Some(v),
-                    ~"sigs" => sigs = Some(v),
-                    ~"crate_type" => crate_type = Some(v),
-                    _ => { }
-                }
-            }
-            ast::meta_list(v, mis) => {
-                if v == ~"link" {
-                    let (n, v, u) = load_link(mis);
-                    name = n;
-                    vers = v;
-                    uuid = u;
-                }
-            }
-            _ => {
-                fail ~"crate attributes may not contain " +
-                     ~"meta_words";
-            }
-        }
-    }
-
-    type env = @{
-        mut deps: ~[~str]
-    };
-
-    fn goto_view_item(ps: syntax::parse::parse_sess, e: env,
-                      i: @ast::view_item) {
-        match i.node {
-            ast::view_item_use(ident, metas, _) => {
-                let name_items =
-                    attr::find_meta_items_by_name(metas, ~"name");
-                let m = if name_items.is_empty() {
-                    metas + ~[attr::mk_name_value_item_str(
-                        ~"name", *ps.interner.get(ident))]
-                } else {
-                    metas
-                };
-                let mut attr_name = ident;
-                let mut attr_vers = ~"";
-                let mut attr_from = ~"";
-
-              for m.each |item| {
-                    match attr::get_meta_item_value_str(*item) {
-                        Some(value) => {
-                            let name = attr::get_meta_item_name(*item);
-
-                            match name {
-                                ~"vers" => attr_vers = value,
-                                ~"from" => attr_from = value,
-                                _ => ()
-                            }
-                        }
-                        None => ()
-                    }
-                }
-
-                let query = if !str::is_empty(attr_from) {
-                    attr_from
-                } else {
-                    if !str::is_empty(attr_vers) {
-                        ps.interner.get(attr_name) + ~"@" + attr_vers
-                    } else { *ps.interner.get(attr_name) }
-                };
-
-                match *ps.interner.get(attr_name) {
-                    ~"std" | ~"core" => (),
-                    _ => e.deps.push(query)
-                }
-            }
-            _ => ()
-        }
-    }
-    fn goto_item(_e: env, _i: @ast::item) {
-    }
-
-    let e = @{
-        mut deps: ~[]
-    };
-    let v = visit::mk_simple_visitor(@{
-        visit_view_item: |a| goto_view_item(sess, e, a),
-        visit_item: |a| goto_item(e, a),
-        .. *visit::default_simple_visitor()
-    });
-
-    visit::visit_crate(*c, (), v);
-
-    let deps = copy e.deps;
-
-    match (name, vers, uuid) {
-        (Some(name0), Some(vers0), Some(uuid0)) => {
-            Some(Crate {
-                name: name0,
-                vers: vers0,
-                uuid: uuid0,
-                desc: desc,
-                sigs: sigs,
-                crate_type: crate_type,
-                deps: deps })
-        }
-        _ => return None
-    }
-}
-
-fn print(s: ~str) {
-    io::stdout().write_line(s);
-}
-
-fn rest(s: ~str, start: uint) -> ~str {
-    if (start >= str::len(s)) {
-        ~""
-    } else {
-        str::slice(s, start, str::len(s))
-    }
-}
-
-fn need_dir(s: &Path) {
-    if os::path_is_dir(s) { return; }
-    if !os::make_dir(s, 493_i32 /* oct: 755 */) {
-        fail fmt!("can't make_dir %s", s.to_str());
-    }
-}
-
-fn valid_pkg_name(s: &str) -> bool {
-    fn is_valid_digit(+c: char) -> bool {
-        ('0' <= c && c <= '9') ||
-        ('a' <= c && c <= 'z') ||
-        ('A' <= c && c <= 'Z') ||
-        c == '-' ||
-        c == '_'
-    }
-
-    s.all(is_valid_digit)
-}
-
-fn parse_source(name: ~str, j: &json::Json) -> @Source {
-    if !valid_pkg_name(name) {
-        fail fmt!("'%s' is an invalid source name", name);
-    }
-
-    match *j {
-        json::Object(j) => {
-            let mut url = match j.find(&~"url") {
-                Some(json::String(u)) => u,
-                _ => fail ~"needed 'url' field in source"
-            };
-            let method = match j.find(&~"method") {
-                Some(json::String(u)) => u,
-                _ => assume_source_method(url)
-            };
-            let key = match j.find(&~"key") {
-                Some(json::String(u)) => Some(u),
-                _ => None
-            };
-            let keyfp = match j.find(&~"keyfp") {
-                Some(json::String(u)) => Some(u),
-                _ => None
-            };
-            if method == ~"file" {
-                url = os::make_absolute(&Path(url)).to_str();
-            }
-            return @Source {
-                name: name,
-                mut url: url,
-                mut method: method,
-                mut key: key,
-                mut keyfp: keyfp,
-                packages: DVec() };
-        }
-        _ => fail ~"needed dict value in source"
-    };
-}
-
-fn try_parse_sources(filename: &Path, sources: map::HashMap<~str, @Source>) {
-    if !os::path_exists(filename)  { return; }
-    let c = io::read_whole_file_str(filename);
-    match json::from_str(c.get()) {
-        Ok(json::Object(j)) => {
-            for j.each |k, v| {
-                sources.insert(copy *k, parse_source(*k, v));
-                debug!("source: %s", *k);
-            }
-        }
-        Ok(_) => fail ~"malformed sources.json",
-        Err(e) => fail fmt!("%s:%s", filename.to_str(), e.to_str())
-    }
-}
-
-fn load_one_source_package(src: @Source, p: &json::Object) {
-    let name = match p.find(&~"name") {
-        Some(json::String(n)) => {
-            if !valid_pkg_name(n) {
-                warn(~"malformed source json: "
-                     + src.name + ~", '" + n + ~"'"+
-                     ~" is an invalid name (alphanumeric, underscores and" +
-                     ~" dashes only)");
-                return;
-            }
-            n
-        }
-        _ => {
-            warn(~"malformed source json: " + src.name + ~" (missing name)");
-            return;
-        }
-    };
-
-    let uuid = match p.find(&~"uuid") {
-        Some(json::String(n)) => {
-            if !is_uuid(n) {
-                warn(~"malformed source json: "
-                     + src.name + ~", '" + n + ~"'"+
-                     ~" is an invalid uuid");
-                return;
-            }
-            n
-        }
-        _ => {
-            warn(~"malformed source json: " + src.name + ~" (missing uuid)");
-            return;
-        }
-    };
-
-    let url = match p.find(&~"url") {
-        Some(json::String(n)) => n,
-        _ => {
-            warn(~"malformed source json: " + src.name + ~" (missing url)");
-            return;
-        }
-    };
-
-    let method = match p.find(&~"method") {
-        Some(json::String(n)) => n,
-        _ => {
-            warn(~"malformed source json: "
-                 + src.name + ~" (missing method)");
-            return;
-        }
-    };
-
-    let reference = match p.find(&~"ref") {
-        Some(json::String(n)) => Some(n),
-        _ => None
-    };
-
-    let mut tags = ~[];
-    match p.find(&~"tags") {
-        Some(json::List(js)) => {
-          for js.each |j| {
-                match *j {
-                    json::String(ref j) => tags.grow(1u, j),
-                    _ => ()
-                }
-            }
-        }
-        _ => ()
-    }
-
-    let description = match p.find(&~"description") {
-        Some(json::String(n)) => n,
-        _ => {
-            warn(~"malformed source json: " + src.name
-                 + ~" (missing description)");
-            return;
-        }
-    };
-
-    let newpkg = Package {
-        name: name,
-        uuid: uuid,
-        url: url,
-        method: method,
-        description: description,
-        reference: reference,
-        tags: tags,
-        versions: ~[]
-    };
-
-    match src.packages.position(|pkg| pkg.uuid == uuid) {
-        Some(idx) => {
-            src.packages.set_elt(idx, newpkg);
-            log(debug, ~"  updated package: " + src.name + ~"/" + name);
-        }
-        None => {
-            src.packages.push(newpkg);
-        }
-    }
-
-    log(debug, ~"  loaded package: " + src.name + ~"/" + name);
-}
-
-fn load_source_info(c: &Cargo, src: @Source) {
-    let dir = c.sourcedir.push(src.name);
-    let srcfile = dir.push("source.json");
-    if !os::path_exists(&srcfile) { return; }
-    let srcstr = io::read_whole_file_str(&srcfile);
-    match json::from_str(srcstr.get()) {
-        Ok(ref json @ json::Object(_)) => {
-            let o = parse_source(src.name, json);
-
-            src.key = o.key;
-            src.keyfp = o.keyfp;
-        }
-        Ok(_) => {
-            warn(~"malformed source.json: " + src.name +
-                 ~"(source info is not a dict)");
-        }
-        Err(e) => {
-            warn(fmt!("%s:%s", src.name, e.to_str()));
-        }
-    };
-}
-fn load_source_packages(c: &Cargo, src: @Source) {
-    log(debug, ~"loading source: " + src.name);
-    let dir = c.sourcedir.push(src.name);
-    let pkgfile = dir.push("packages.json");
-    if !os::path_exists(&pkgfile) { return; }
-    let pkgstr = io::read_whole_file_str(&pkgfile);
-    match json::from_str(pkgstr.get()) {
-        Ok(json::List(js)) => {
-          for js.each |j| {
-                match *j {
-                    json::Object(p) => {
-                        load_one_source_package(src, p);
-                    }
-                    _ => {
-                        warn(~"malformed source json: " + src.name +
-                             ~" (non-dict pkg)");
-                    }
-                }
-            }
-        }
-        Ok(_) => {
-            warn(~"malformed packages.json: " + src.name +
-                 ~"(packages is not a list)");
-        }
-        Err(e) => {
-            warn(fmt!("%s:%s", src.name, e.to_str()));
-        }
-    };
-}
-
-fn build_cargo_options(argv: ~[~str]) -> Options {
-    let matches = &match getopts::getopts(argv, opts()) {
-        result::Ok(m) => m,
-        result::Err(f) => {
-            fail fmt!("%s", getopts::fail_str(f));
-        }
-    };
-
-    let test = opt_present(matches, ~"test");
-    let G    = opt_present(matches, ~"G");
-    let g    = opt_present(matches, ~"g");
-    let help = opt_present(matches, ~"h") || opt_present(matches, ~"help");
-    let len  = vec::len(matches.free);
-
-    let is_install = len > 1u && matches.free[1] == ~"install";
-    let is_uninstall = len > 1u && matches.free[1] == ~"uninstall";
-
-    if G && g { fail ~"-G and -g both provided"; }
-
-    if !is_install && !is_uninstall && (g || G) {
-        fail ~"-g and -G are only valid for `install` and `uninstall|rm`";
-    }
-
-    let mode =
-        if (!is_install && !is_uninstall) || g { UserMode }
-        else if G { SystemMode }
-        else { LocalMode };
-
-    Options {test: test, mode: mode, free: matches.free, help: help}
-}
-
-fn configure(opts: Options) -> Cargo {
-    let home = match get_cargo_root() {
-        Ok(home) => home,
-        Err(_err) => get_cargo_sysroot().get()
-    };
-
-    let get_cargo_dir = match opts.mode {
-        SystemMode => get_cargo_sysroot,
-        UserMode => get_cargo_root,
-        LocalMode => get_cargo_root_nearest
-    };
-
-    let p = get_cargo_dir().get();
-
-    let sources = HashMap();
-    try_parse_sources(&home.push("sources.json"), sources);
-    try_parse_sources(&home.push("local-sources.json"), sources);
-
-    let dep_cache = HashMap();
-
-    let mut c = Cargo {
-        pgp: pgp::supported(),
-        root: home,
-        installdir: p,
-        bindir: p.push("bin"),
-        libdir: p.push("lib"),
-        workdir: p.push("work"),
-        sourcedir: home.push("sources"),
-        sources: sources,
-        mut current_install: ~"",
-        dep_cache: dep_cache,
-        opts: opts
-    };
-
-    need_dir(&c.root);
-    need_dir(&c.installdir);
-    need_dir(&c.sourcedir);
-    need_dir(&c.workdir);
-    need_dir(&c.libdir);
-    need_dir(&c.bindir);
-
-    for sources.each_key |k| {
-        let mut s = sources.get(k);
-        load_source_packages(&c, s);
-        sources.insert(k, s);
-    }
-
-    if c.pgp {
-        pgp::init(&c.root);
-    } else {
-        warn(~"command `gpg` was not found");
-        warn(~"you have to install gpg from source " +
-             ~" or package manager to get it to work correctly");
-    }
-
-    move c
-}
-
-fn for_each_package(c: &Cargo, b: fn(s: @Source, p: &Package)) {
-    for c.sources.each_value |v| {
-        for v.packages.each |p| {
-            b(v, p);
-        }
-    }
-}
-
-// Runs all programs in directory <buildpath>
-fn run_programs(buildpath: &Path) {
-    let newv = os::list_dir_path(buildpath);
-    for newv.each |ct| {
-        run::run_program(ct.to_str(), ~[]);
-    }
-}
-
-// Runs rustc in <path + subdir> with the given flags
-// and returns <patho + subdir>
-fn run_in_buildpath(what: &str, path: &Path, subdir: &Path, cf: &Path,
-                    extra_flags: ~[~str]) -> Option<Path> {
-    let buildpath = path.push_rel(subdir);
-    need_dir(&buildpath);
-    debug!("%s: %s -> %s", what, cf.to_str(), buildpath.to_str());
-    let p = run::program_output(rustc_sysroot(),
-                                ~[~"--out-dir",
-                                  buildpath.to_str(),
-                                  cf.to_str()] + extra_flags);
-    if p.status != 0 {
-        error(fmt!("rustc failed: %d\n%s\n%s", p.status, p.err, p.out));
-        return None;
-    }
-    Some(buildpath)
-}
-
-fn test_one_crate(_c: &Cargo, path: &Path, cf: &Path) {
-    let buildpath = match run_in_buildpath(~"testing", path,
-                                           &Path("test"),
-                                           cf,
-                                           ~[ ~"--test"]) {
-      None => return,
-    Some(bp) => bp
-  };
-  run_programs(&buildpath);
-}
-
-fn install_one_crate(c: &Cargo, path: &Path, cf: &Path) {
-    let buildpath = match run_in_buildpath(~"installing", path,
-                                           &Path("build"),
-                                           cf, ~[]) {
-      None => return,
-      Some(bp) => bp
-    };
-    let newv = os::list_dir_path(&buildpath);
-    let exec_suffix = os::exe_suffix();
-    for newv.each |ct| {
-        if (exec_suffix != ~"" && str::ends_with(ct.to_str(),
-                                                 exec_suffix)) ||
-            (exec_suffix == ~"" &&
-             !str::starts_with(ct.filename().get(),
-                               ~"lib")) {
-            debug!("  bin: %s", ct.to_str());
-            install_to_dir(*ct, &c.bindir);
-            if c.opts.mode == SystemMode {
-                // FIXME (#2662): Put this file in PATH / symlink it so it can
-                // be used as a generic executable
-                // `cargo install -G rustray` and `rustray file.obj`
-            }
-        } else {
-            debug!("  lib: %s", ct.to_str());
-            install_to_dir(*ct, &c.libdir);
-        }
-    }
-}
-
-
-fn rustc_sysroot() -> ~str {
-    match os::self_exe_path() {
-        Some(path) => {
-            let rustc = path.push_many([~"..", ~"bin", ~"rustc"]);
-            debug!("  rustc: %s", rustc.to_str());
-            rustc.to_str()
-        }
-        None => ~"rustc"
-    }
-}
-
-fn install_source(c: &Cargo, path: &Path) {
-    debug!("source: %s", path.to_str());
-    os::change_dir(path);
-
-    let mut cratefiles = ~[];
-    for os::walk_dir(&Path(".")) |p| {
-        if p.filetype() == Some(~".rc") {
-            cratefiles.push(*p);
-        }
-    }
-
-    if vec::is_empty(cratefiles) {
-        fail ~"this doesn't look like a rust package (no .rc files)";
-    }
-
-    for cratefiles.each |cf| {
-        match load_crate(cf) {
-            None => loop,
-            Some(crate) => {
-              for crate.deps.each |query| {
-                    // FIXME (#1356): handle cyclic dependencies
-                    // (n.b. #1356 says "Cyclic dependency is an error
-                    // condition")
-
-                    let wd = get_temp_workdir(c);
-                    install_query(c, &wd, *query);
-                }
-
-                os::change_dir(path);
-
-                if c.opts.test {
-                    test_one_crate(c, path, cf);
-                }
-                install_one_crate(c, path, cf);
-            }
-        }
-    }
-}
-
-fn install_git(c: &Cargo, wd: &Path, url: ~str, reference: Option<~str>) {
-    run::program_output(~"git", ~[~"clone", url, wd.to_str()]);
-    if reference.is_some() {
-        let r = reference.get();
-        os::change_dir(wd);
-        run::run_program(~"git", ~[~"checkout", r]);
-    }
-
-    install_source(c, wd);
-}
-
-fn install_curl(c: &Cargo, wd: &Path, url: ~str) {
-    let tarpath = wd.push("pkg.tar");
-    let p = run::program_output(~"curl", ~[~"-f", ~"-s", ~"-o",
-                                         tarpath.to_str(), url]);
-    if p.status != 0 {
-        fail fmt!("fetch of %s failed: %s", url, p.err);
-    }
-    run::run_program(~"tar", ~[~"-x", ~"--strip-components=1",
-                               ~"-C", wd.to_str(),
-                               ~"-f", tarpath.to_str()]);
-    install_source(c, wd);
-}
-
-fn install_file(c: &Cargo, wd: &Path, path: &Path) {
-    run::program_output(~"tar", ~[~"-x", ~"--strip-components=1",
-                                  ~"-C", wd.to_str(),
-                                  ~"-f", path.to_str()]);
-    install_source(c, wd);
-}
-
-fn install_package(c: &Cargo, src: ~str, wd: &Path, pkg: Package) {
-    let url = copy pkg.url;
-    let method = match pkg.method {
-        ~"git" => ~"git",
-        ~"file" => ~"file",
-        _ => ~"curl"
-    };
-
-    info(fmt!("installing %s/%s via %s...", src, pkg.name, method));
-
-    match method {
-        ~"git" => install_git(c, wd, url, copy pkg.reference),
-        ~"file" => install_file(c, wd, &Path(url)),
-        ~"curl" => install_curl(c, wd, url),
-        _ => ()
-    }
-}
-
-fn cargo_suggestion(c: &Cargo, fallback: fn())
-{
-    if c.sources.size() == 0u {
-        error(~"no sources defined - you may wish to run " +
-              ~"`cargo init`");
-        return;
-    }
-    fallback();
-}
-
-fn install_uuid(c: &Cargo, wd: &Path, uuid: ~str) {
-    let mut ps = ~[];
-    for_each_package(c, |s, p| {
-        if p.uuid == uuid {
-            vec::push(&mut ps, (s.name, copy *p));
-        }
-    });
-    if vec::len(ps) == 1u {
-        let (sname, p) = copy ps[0];
-        install_package(c, sname, wd, p);
-        return;
-    } else if vec::len(ps) == 0u {
-        cargo_suggestion(c, || {
-            error(~"can't find package: " + uuid);
-        });
-        return;
-    }
-    error(~"found multiple packages:");
-    for ps.each |elt| {
-        let (sname,p) = copy *elt;
-        info(~"  " + sname + ~"/" + p.uuid + ~" (" + p.name + ~")");
-    }
-}
-
-fn install_named(c: &Cargo, wd: &Path, name: ~str) {
-    let mut ps = ~[];
-    for_each_package(c, |s, p| {
-        if p.name == name {
-            vec::push(&mut ps, (s.name, copy *p));
-        }
-    });
-    if vec::len(ps) == 1u {
-        let (sname, p) = copy ps[0];
-        install_package(c, sname, wd, p);
-        return;
-    } else if vec::len(ps) == 0u {
-        cargo_suggestion(c, || {
-            error(~"can't find package: " + name);
-        });
-        return;
-    }
-    error(~"found multiple packages:");
-    for ps.each |elt| {
-        let (sname,p) = copy *elt;
-        info(~"  " + sname + ~"/" + p.uuid + ~" (" + p.name + ~")");
-    }
-}
-
-fn install_uuid_specific(c: &Cargo, wd: &Path, src: ~str, uuid: ~str) {
-    match c.sources.find(src) {
-        Some(s) => {
-            for s.packages.each |p| {
-                if p.uuid == uuid {
-                    install_package(c, src, wd, *p);
-                    return;
-                }
-            }
-        }
-        _ => ()
-    }
-    error(~"can't find package: " + src + ~"/" + uuid);
-}
-
-fn install_named_specific(c: &Cargo, wd: &Path, src: ~str, name: ~str) {
-    match c.sources.find(src) {
-        Some(s) => {
-            for s.packages.each |p| {
-                if p.name == name {
-                    install_package(c, src, wd, *p);
-                    return;
-                }
-            }
-        }
-        _ => ()
-    }
-    error(~"can't find package: " + src + ~"/" + name);
-}
-
-fn cmd_uninstall(c: &Cargo) {
-    if vec::len(c.opts.free) < 3u {
-        cmd_usage();
-        return;
-    }
-
-    let lib = &c.libdir;
-    let bin = &c.bindir;
-    let target = c.opts.free[2u];
-
-    // FIXME (#2662): needs stronger pattern matching
-    // FIXME (#2662): needs to uninstall from a specified location in a
-    // cache instead of looking for it (binaries can be uninstalled by
-    // name only)
-
-    fn try_uninstall(p: &Path) -> bool {
-        if os::remove_file(p) {
-            info(~"uninstalled: '" + p.to_str() + ~"'");
-            true
-        } else {
-            error(~"could not uninstall: '" +
-                  p.to_str() + ~"'");
-            false
-        }
-    }
-
-    if is_uuid(target) {
-        for os::list_dir(lib).each |file| {
-            match str::find_str(*file, ~"-" + target + ~"-") {
-              Some(_) => if !try_uninstall(&lib.push(*file)) { return },
-              None => ()
-            }
-        }
-        error(~"can't find package with uuid: " + target);
-    } else {
-        for os::list_dir(lib).each |file| {
-            match str::find_str(*file, ~"lib" + target + ~"-") {
-              Some(_) => if !try_uninstall(&lib.push(*file)) { return },
-              None => ()
-            }
-        }
-        for os::list_dir(bin).each |file| {
-            match str::find_str(*file, target) {
-              Some(_) => if !try_uninstall(&lib.push(*file)) { return },
-              None => ()
-            }
-        }
-
-        error(~"can't find package with name: " + target);
-    }
-}
-
-fn install_query(c: &Cargo, wd: &Path, target: ~str) {
-    match c.dep_cache.find(target) {
-        Some(inst) => {
-            if inst {
-                return;
-            }
-        }
-        None => ()
-    }
-
-    c.dep_cache.insert(target, true);
-
-    if is_archive_path(target) {
-        install_file(c, wd, &Path(target));
-        return;
-    } else if is_git_url(target) {
-        let reference = if c.opts.free.len() >= 4u {
-            Some(c.opts.free[3u])
-        } else {
-            None
-        };
-        install_git(c, wd, target, reference);
-    } else if !valid_pkg_name(target) && has_archive_extension(target) {
-        install_curl(c, wd, target);
-        return;
-    } else {
-        let mut ps = copy target;
-
-        match str::find_char(ps, '/') {
-            option::Some(idx) => {
-                let source = str::slice(ps, 0u, idx);
-                ps = str::slice(ps, idx + 1u, str::len(ps));
-                if is_uuid(ps) {
-                    install_uuid_specific(c, wd, source, ps);
-                } else {
-                    install_named_specific(c, wd, source, ps);
-                }
-            }
-            option::None => {
-                if is_uuid(ps) {
-                    install_uuid(c, wd, ps);
-                } else {
-                    install_named(c, wd, ps);
-                }
-            }
-        }
-    }
-
-    // FIXME (#2662): This whole dep_cache and current_install thing is
-    // a bit of a hack. It should be cleaned up in the future.
-
-    if target == c.current_install {
-        for c.dep_cache.each |k, _v| {
-            c.dep_cache.remove(k);
-        }
-
-        c.current_install = ~"";
-    }
-}
-
-fn get_temp_workdir(c: &Cargo) -> Path {
-    match tempfile::mkdtemp(&c.workdir, "cargo") {
-      Some(wd) => wd,
-      None => fail fmt!("needed temp dir: %s",
-                        c.workdir.to_str())
-    }
-}
-
-fn cmd_install(c: &Cargo) unsafe {
-    let wd = get_temp_workdir(c);
-
-    if vec::len(c.opts.free) == 2u {
-        let cwd = os::getcwd();
-        let status = run::run_program(~"cp", ~[~"-R", cwd.to_str(),
-                                               wd.to_str()]);
-
-        if status != 0 {
-            fail fmt!("could not copy directory: %s", cwd.to_str());
-        }
-
-        install_source(c, &wd);
-        return;
-    }
-
-    sync(c);
-
-    let query = c.opts.free[2];
-    c.current_install = query.to_str();
-
-    install_query(c, &wd, query);
-}
-
-fn sync(c: &Cargo) {
-    for c.sources.each_key |k| {
-        let mut s = c.sources.get(k);
-        sync_one(c, s);
-        c.sources.insert(k, s);
-    }
-}
-
-fn sync_one_file(c: &Cargo, dir: &Path, src: @Source) -> bool {
-    let name = src.name;
-    let srcfile = dir.push("source.json.new");
-    let destsrcfile = dir.push("source.json");
-    let pkgfile = dir.push("packages.json.new");
-    let destpkgfile = dir.push("packages.json");
-    let keyfile = dir.push("key.gpg");
-    let srcsigfile = dir.push("source.json.sig");
-    let sigfile = dir.push("packages.json.sig");
-    let url = Path(src.url);
-    let mut has_src_file = false;
-
-    if !os::copy_file(&url.push("packages.json"), &pkgfile) {
-        error(fmt!("fetch for source %s (url %s) failed",
-                   name, url.to_str()));
-        return false;
-    }
-
-    if os::copy_file(&url.push("source.json"), &srcfile) {
-        has_src_file = false;
-    }
-
-    os::copy_file(&url.push("source.json.sig"), &srcsigfile);
-    os::copy_file(&url.push("packages.json.sig"), &sigfile);
-
-    match copy src.key {
-        Some(u) => {
-            let p = run::program_output(~"curl",
-                                        ~[~"-f", ~"-s",
-                                          ~"-o", keyfile.to_str(), u]);
-            if p.status != 0 {
-                error(fmt!("fetch for source %s (key %s) failed", name, u));
-                return false;
-            }
-            pgp::add(&c.root, &keyfile);
-        }
-        _ => ()
-    }
-    match (src.key, src.keyfp) {
-        (Some(_), Some(f)) => {
-            let r = pgp::verify(&c.root, &pkgfile, &sigfile);
-
-            if !r {
-                error(fmt!("signature verification failed for source %s with \
-                            key %s", name, f));
-                return false;
-            }
-
-            if has_src_file {
-                let e = pgp::verify(&c.root, &srcfile, &srcsigfile);
-
-                if !e {
-                    error(fmt!("signature verification failed for source %s \
-                                with key %s", name, f));
-                    return false;
-                }
-            }
-        }
-        _ => ()
-    }
-
-    copy_warn(&pkgfile, &destpkgfile);
-
-    if has_src_file {
-        copy_warn(&srcfile, &destsrcfile);
-    }
-
-    os::remove_file(&keyfile);
-    os::remove_file(&srcfile);
-    os::remove_file(&srcsigfile);
-    os::remove_file(&pkgfile);
-    os::remove_file(&sigfile);
-
-    info(fmt!("synced source: %s", name));
-
-    return true;
-}
-
-fn sync_one_git(c: &Cargo, dir: &Path, src: @Source) -> bool {
-    let name = src.name;
-    let srcfile = dir.push("source.json");
-    let pkgfile = dir.push("packages.json");
-    let keyfile = dir.push("key.gpg");
-    let srcsigfile = dir.push("source.json.sig");
-    let sigfile = dir.push("packages.json.sig");
-    let url = src.url;
-
-    fn rollback(name: ~str, dir: &Path, insecure: bool) {
-        fn msg(name: ~str, insecure: bool) {
-            error(fmt!("could not rollback source: %s", name));
-
-            if insecure {
-                warn(~"a past security check failed on source " +
-                     name + ~" and rolling back the source failed -"
-                     + ~" this source may be compromised");
-            }
-        }
-
-        if !os::change_dir(dir) {
-            msg(name, insecure);
-        }
-        else {
-            let p = run::program_output(~"git", ~[~"reset", ~"--hard",
-                                                ~"HEAD@{1}"]);
-
-            if p.status != 0 {
-                msg(name, insecure);
-            }
-        }
-    }
-
-    if !os::path_exists(&dir.push(".git")) {
-        let p = run::program_output(~"git", ~[~"clone", url, dir.to_str()]);
-
-        if p.status != 0 {
-            error(fmt!("fetch for source %s (url %s) failed", name, url));
-            return false;
-        }
-    }
-    else {
-        if !os::change_dir(dir) {
-            error(fmt!("fetch for source %s (url %s) failed", name, url));
-            return false;
-        }
-
-        let p = run::program_output(~"git", ~[~"pull"]);
-
-        if p.status != 0 {
-            error(fmt!("fetch for source %s (url %s) failed", name, url));
-            return false;
-        }
-    }
-
-    let has_src_file = os::path_exists(&srcfile);
-
-    match copy src.key {
-        Some(u) => {
-            let p = run::program_output(~"curl",
-                                        ~[~"-f", ~"-s",
-                                          ~"-o", keyfile.to_str(), u]);
-            if p.status != 0 {
-                error(fmt!("fetch for source %s (key %s) failed", name, u));
-                rollback(name, dir, false);
-                return false;
-            }
-            pgp::add(&c.root, &keyfile);
-        }
-        _ => ()
-    }
-    match (src.key, src.keyfp) {
-        (Some(_), Some(f)) => {
-            let r = pgp::verify(&c.root, &pkgfile, &sigfile);
-
-            if !r {
-                error(fmt!("signature verification failed for source %s with \
-                            key %s", name, f));
-                rollback(name, dir, false);
-                return false;
-            }
-
-            if has_src_file {
-                let e = pgp::verify(&c.root, &srcfile, &srcsigfile);
-
-                if !e {
-                    error(fmt!("signature verification failed for source %s \
-                                with key %s", name, f));
-                    rollback(name, dir, false);
-                    return false;
-                }
-            }
-        }
-        _ => ()
-    }
-
-    os::remove_file(&keyfile);
-
-    info(fmt!("synced source: %s", name));
-
-    return true;
-}
-
-fn sync_one_curl(c: &Cargo, dir: &Path, src: @Source) -> bool {
-    let name = src.name;
-    let srcfile = dir.push("source.json.new");
-    let destsrcfile = dir.push("source.json");
-    let pkgfile = dir.push("packages.json.new");
-    let destpkgfile = dir.push("packages.json");
-    let keyfile = dir.push("key.gpg");
-    let srcsigfile = dir.push("source.json.sig");
-    let sigfile = dir.push("packages.json.sig");
-    let mut url = src.url;
-    let smart = !str::ends_with(src.url, ~"packages.json");
-    let mut has_src_file = false;
-
-    if smart {
-        url += ~"/packages.json";
-    }
-
-    let p = run::program_output(~"curl",
-                                ~[~"-f", ~"-s",
-                                  ~"-o", pkgfile.to_str(), url]);
-
-    if p.status != 0 {
-        error(fmt!("fetch for source %s (url %s) failed", name, url));
-        return false;
-    }
-    if smart {
-        url = src.url + ~"/source.json";
-        let p =
-            run::program_output(~"curl",
-                                ~[~"-f", ~"-s",
-                                  ~"-o", srcfile.to_str(), url]);
-
-        if p.status == 0 {
-            has_src_file = true;
-        }
-    }
-
-    match copy src.key {
-       Some(u) => {
-            let p = run::program_output(~"curl",
-                                        ~[~"-f", ~"-s",
-                                          ~"-o", keyfile.to_str(), u]);
-            if p.status != 0 {
-                error(fmt!("fetch for source %s (key %s) failed", name, u));
-                return false;
-            }
-            pgp::add(&c.root, &keyfile);
-        }
-        _ => ()
-    }
-    match (src.key, src.keyfp) {
-        (Some(_), Some(f)) => {
-            if smart {
-                url = src.url + ~"/packages.json.sig";
-            }
-            else {
-                url = src.url + ~".sig";
-            }
-
-            let mut p = run::program_output(~"curl",
-                                            ~[~"-f", ~"-s", ~"-o",
-                                              sigfile.to_str(), url]);
-            if p.status != 0 {
-                error(fmt!("fetch for source %s (sig %s) failed", name, url));
-                return false;
-            }
-
-            let r = pgp::verify(&c.root, &pkgfile, &sigfile);
-
-            if !r {
-                error(fmt!("signature verification failed for source %s with \
-                            key %s", name, f));
-                return false;
-            }
-
-            if smart && has_src_file {
-                url = src.url + ~"/source.json.sig";
-
-                p = run::program_output(~"curl",
-                                        ~[~"-f", ~"-s", ~"-o",
-                                          srcsigfile.to_str(), url]);
-                if p.status != 0 {
-                    error(fmt!("fetch for source %s (sig %s) failed",
-                          name, url));
-                    return false;
-                }
-
-                let e = pgp::verify(&c.root, &srcfile, &srcsigfile);
-
-                if !e {
-                    error(~"signature verification failed for " +
-                          ~"source " + name + ~" with key " + f);
-                    return false;
-                }
-            }
-        }
-        _ => ()
-    }
-
-    copy_warn(&pkgfile, &destpkgfile);
-
-    if smart && has_src_file {
-        copy_warn(&srcfile, &destsrcfile);
-    }
-
-    os::remove_file(&keyfile);
-    os::remove_file(&srcfile);
-    os::remove_file(&srcsigfile);
-    os::remove_file(&pkgfile);
-    os::remove_file(&sigfile);
-
-    info(fmt!("synced source: %s", name));
-
-    return true;
-}
-
-fn sync_one(c: &Cargo, src: @Source) {
-    let name = src.name;
-    let dir = c.sourcedir.push(name);
-
-    info(fmt!("syncing source: %s...", name));
-
-    need_dir(&dir);
-
-    let result = match src.method {
-        ~"git" => sync_one_git(c, &dir, src),
-        ~"file" => sync_one_file(c, &dir, src),
-        _ => sync_one_curl(c, &dir, src)
-    };
-
-    if result {
-        load_source_info(c, src);
-        load_source_packages(c, src);
-    }
-}
-
-fn cmd_init(c: &Cargo) {
-    let srcurl = ~"http://www.rust-lang.org/cargo/sources.json";
-    let sigurl = ~"http://www.rust-lang.org/cargo/sources.json.sig";
-
-    let srcfile = c.root.push("sources.json.new");
-    let sigfile = c.root.push("sources.json.sig");
-    let destsrcfile = c.root.push("sources.json");
-
-    let p =
-        run::program_output(~"curl", ~[~"-f", ~"-s",
-                                       ~"-o", srcfile.to_str(), srcurl]);
-    if p.status != 0 {
-        error(fmt!("fetch of sources.json failed: %s", p.out));
-        return;
-    }
-
-    let p =
-        run::program_output(~"curl", ~[~"-f", ~"-s",
-                                       ~"-o", sigfile.to_str(), sigurl]);
-    if p.status != 0 {
-        error(fmt!("fetch of sources.json.sig failed: %s", p.out));
-        return;
-    }
-
-    let r = pgp::verify(&c.root, &srcfile, &sigfile);
-    if !r {
-        error(fmt!("signature verification failed for '%s'",
-                   srcfile.to_str()));
-        return;
-    }
-
-    copy_warn(&srcfile, &destsrcfile);
-    os::remove_file(&srcfile);
-    os::remove_file(&sigfile);
-
-    info(fmt!("initialized .cargo in %s", c.root.to_str()));
-}
-
-fn print_pkg(s: @Source, p: &Package) {
-    let mut m = s.name + ~"/" + p.name + ~" (" + p.uuid + ~")";
-    if vec::len(p.tags) > 0u {
-        m = m + ~" [" + str::connect(p.tags, ~", ") + ~"]";
-    }
-    info(m);
-    if p.description != ~"" {
-        print(~"   >> " + p.description + ~"\n")
-    }
-}
-
-fn print_source(s: @Source) {
-    info(s.name + ~" (" + s.url + ~")");
-
-    let pks = sort::merge_sort(s.packages.get(), sys::shape_lt);
-    let l = vec::len(pks);
-
-    print(io::with_str_writer(|writer| {
-        let mut list = ~"   >> ";
-
-        for vec::eachi(pks) |i, pk| {
-            if str::len(list) > 78u {
-                writer.write_line(list);
-                list = ~"   >> ";
-            }
-            list += pk.name + (if l - 1u == i { ~"" } else { ~", " });
-        }
-
-        writer.write_line(list);
-    }));
-}
-
-fn cmd_list(c: &Cargo) {
-    sync(c);
-
-    if vec::len(c.opts.free) >= 3u {
-        let v = vec::view(c.opts.free, 2u, vec::len(c.opts.free));
-        for vec::each(v) |name| {
-            if !valid_pkg_name(*name) {
-                error(fmt!("'%s' is an invalid source name", *name));
-            } else {
-                match c.sources.find(*name) {
-                    Some(source) => {
-                        print_source(source);
-                    }
-                    None => {
-                        error(fmt!("no such source: %s", *name));
-                    }
-                }
-            }
-        }
-    } else {
-        for c.sources.each_value |v| {
-            print_source(v);
-        }
-    }
-}
-
-fn cmd_search(c: &Cargo) {
-    if vec::len(c.opts.free) < 3u {
-        cmd_usage();
-        return;
-    }
-
-    sync(c);
-
-    let mut n = 0;
-    let name = c.opts.free[2];
-    let tags = vec::slice(c.opts.free, 3u, vec::len(c.opts.free));
-    for_each_package(c, |s, p| {
-        if (str::contains(p.name, name) || name == ~"*") &&
-            vec::all(tags, |t| vec::contains(p.tags, t) ) {
-            print_pkg(s, p);
-            n += 1;
-        }
-    });
-    info(fmt!("found %d packages", n));
-}
-
-fn install_to_dir(srcfile: &Path, destdir: &Path) {
-    let newfile = destdir.push(srcfile.filename().get());
-
-    let status = run::run_program(~"cp", ~[~"-r", srcfile.to_str(),
-                                           newfile.to_str()]);
-    if status == 0 {
-        info(fmt!("installed: '%s'", newfile.to_str()));
-    } else {
-        error(fmt!("could not install: '%s'", newfile.to_str()));
-    }
-}
-
-fn dump_cache(c: &Cargo) {
-    need_dir(&c.root);
-
-    let out = c.root.push("cache.json");
-    let _root = json::Object(~LinearMap());
-
-    if os::path_exists(&out) {
-        copy_warn(&out, &c.root.push("cache.json.old"));
-    }
-}
-fn dump_sources(c: &Cargo) {
-    if c.sources.size() < 1u {
-        return;
-    }
-
-    need_dir(&c.root);
-
-    let out = c.root.push("sources.json");
-
-    if os::path_exists(&out) {
-        copy_warn(&out, &c.root.push("sources.json.old"));
-    }
-
-    match io::buffered_file_writer(&out) {
-        result::Ok(writer) => {
-            let mut hash = ~LinearMap();
-
-            for c.sources.each |k, v| {
-                let mut chash = ~LinearMap();
-
-                chash.insert(~"url", json::String(v.url));
-                chash.insert(~"method", json::String(v.method));
-
-                match copy v.key {
-                    Some(key) => {
-                        chash.insert(~"key", json::String(copy key));
-                    }
-                    _ => ()
-                }
-                match copy v.keyfp {
-                    Some(keyfp) => {
-                        chash.insert(~"keyfp", json::String(copy keyfp));
-                    }
-                    _ => ()
-                }
-
-                hash.insert(copy k, json::Object(move chash));
-            }
-
-            json::to_writer(writer, &json::Object(move hash))
-        }
-        result::Err(e) => {
-            error(fmt!("could not dump sources: %s", e));
-        }
-    }
-}
-
-fn copy_warn(srcfile: &Path, destfile: &Path) {
-    if !os::copy_file(srcfile, destfile) {
-        warn(fmt!("copying %s to %s failed",
-                  srcfile.to_str(), destfile.to_str()));
-    }
-}
-
-fn cmd_sources(c: &Cargo) {
-    if vec::len(c.opts.free) < 3u {
-        for c.sources.each_value |v| {
-            info(fmt!("%s (%s) via %s",
-                      v.name, v.url, v.method));
-        }
-        return;
-    }
-
-    let action = c.opts.free[2u];
-
-    match action {
-        ~"clear" => {
-          for c.sources.each_key |k| {
-                c.sources.remove(k);
-            }
-
-            info(~"cleared sources");
-        }
-        ~"add" => {
-            if vec::len(c.opts.free) < 5u {
-                cmd_usage();
-                return;
-            }
-
-            let name = c.opts.free[3u];
-            let url = c.opts.free[4u];
-
-            if !valid_pkg_name(name) {
-                error(fmt!("'%s' is an invalid source name", name));
-                return;
-            }
-
-            if c.sources.contains_key(name) {
-                error(fmt!("source already exists: %s", name));
-            } else {
-                c.sources.insert(name, @Source {
-                    name: name,
-                    mut url: url,
-                    mut method: assume_source_method(url),
-                    mut key: None,
-                    mut keyfp: None,
-                    packages: DVec()
-                });
-                info(fmt!("added source: %s", name));
-            }
-        }
-        ~"remove" => {
-            if vec::len(c.opts.free) < 4u {
-                cmd_usage();
-                return;
-            }
-
-            let name = c.opts.free[3u];
-
-            if !valid_pkg_name(name) {
-                error(fmt!("'%s' is an invalid source name", name));
-                return;
-            }
-
-            if c.sources.contains_key(name) {
-                c.sources.remove(name);
-                info(fmt!("removed source: %s", name));
-            } else {
-                error(fmt!("no such source: %s", name));
-            }
-        }
-        ~"set-url" => {
-            if vec::len(c.opts.free) < 5u {
-                cmd_usage();
-                return;
-            }
-
-            let name = c.opts.free[3u];
-            let url = c.opts.free[4u];
-
-            if !valid_pkg_name(name) {
-                error(fmt!("'%s' is an invalid source name", name));
-                return;
-            }
-
-            match c.sources.find(name) {
-                Some(source) => {
-                    let old = copy source.url;
-                    let method = assume_source_method(url);
-
-                    source.url = url;
-                    source.method = method;
-
-                    c.sources.insert(name, source);
-
-                    info(fmt!("changed source url: '%s' to '%s'", old, url));
-                }
-                None => {
-                    error(fmt!("no such source: %s", name));
-                }
-            }
-        }
-        ~"set-method" => {
-            if vec::len(c.opts.free) < 5u {
-                cmd_usage();
-                return;
-            }
-
-            let name = c.opts.free[3u];
-            let method = c.opts.free[4u];
-
-            if !valid_pkg_name(name) {
-                error(fmt!("'%s' is an invalid source name", name));
-                return;
-            }
-
-            match c.sources.find(name) {
-                Some(source) => {
-                    let old = copy source.method;
-
-                    source.method = match method {
-                        ~"git" => ~"git",
-                        ~"file" => ~"file",
-                        _ => ~"curl"
-                    };
-
-                    c.sources.insert(name, source);
-
-                    info(fmt!("changed source method: '%s' to '%s'", old,
-                         method));
-                }
-                None => {
-                    error(fmt!("no such source: %s", name));
-                }
-            }
-        }
-        ~"rename" => {
-            if vec::len(c.opts.free) < 5u {
-                cmd_usage();
-                return;
-            }
-
-            let name = c.opts.free[3u];
-            let newn = c.opts.free[4u];
-
-            if !valid_pkg_name(name) {
-                error(fmt!("'%s' is an invalid source name", name));
-                return;
-            }
-            if !valid_pkg_name(newn) {
-                error(fmt!("'%s' is an invalid source name", newn));
-                return;
-            }
-
-            match c.sources.find(name) {
-                Some(source) => {
-                    c.sources.remove(name);
-                    c.sources.insert(newn, source);
-                    info(fmt!("renamed source: %s to %s", name, newn));
-                }
-                None => {
-                    error(fmt!("no such source: %s", name));
-                }
-            }
-        }
-        _ => cmd_usage()
-    }
-}
-
-fn cmd_usage() {
-    print(~"Usage: cargo <cmd> [options] [args..]
-e.g. cargo install <name>
-
-Where <cmd> is one of:
-    init, install, list, search, sources,
-    uninstall, usage
-
-Options:
-
-    -h, --help                  Display this message
-    <cmd> -h, <cmd> --help      Display help for <cmd>
-");
-}
-
-fn cmd_usage_init() {
-    print(~"cargo init
-
-Re-initialize cargo in ~/.cargo. Clears all sources and then adds the
-default sources from <www.rust-lang.org/sources.json>.");
-}
-
-fn cmd_usage_install() {
-    print(~"cargo install
-cargo install [source/]<name>[@version]
-cargo install [source/]<uuid>[@version]
-cargo install <git url> [ref]
-cargo install <tarball url>
-cargo install <tarball file>
-
-Options:
-    --test      Run crate tests before installing
-    -g          Install to the user level (~/.cargo/bin/ instead of
-                locally in ./.cargo/bin/ by default)
-    -G          Install to the system level (/usr/local/lib/cargo/bin/)
-
-Install a crate. If no arguments are supplied, it installs from
-the current working directory. If a source is provided, only install
-from that source, otherwise it installs from any source.");
-}
-
-fn cmd_usage_uninstall() {
-    print(~"cargo uninstall [source/]<name>[@version]
-cargo uninstall [source/]<uuid>[@version]
-cargo uninstall <meta-name>[@version]
-cargo uninstall <meta-uuid>[@version]
-
-Options:
-    -g          Remove from the user level (~/.cargo/bin/ instead of
-                locally in ./.cargo/bin/ by default)
-    -G          Remove from the system level (/usr/local/lib/cargo/bin/)
-
-Remove a crate. If a source is provided, only remove
-from that source, otherwise it removes from any source.
-If a crate was installed directly (git, tarball, etc.), you can remove
-it by metadata.");
-}
-
-fn cmd_usage_list() {
-    print(~"cargo list [sources..]
-
-If no arguments are provided, list all sources and their packages.
-If source names are provided, list those sources and their packages.
-");
-}
-
-fn cmd_usage_search() {
-    print(~"cargo search <query | '*'> [tags..]
-
-Search packages.");
-}
-
-fn cmd_usage_sources() {
-    print(~"cargo sources
-cargo sources add <name> <url>
-cargo sources remove <name>
-cargo sources rename <name> <new>
-cargo sources set-url <name> <url>
-cargo sources set-method <name> <method>
-
-If no arguments are supplied, list all sources (but not their packages).
-
-Commands:
-    add             Add a source. The source method will be guessed
-                    from the URL.
-    remove          Remove a source.
-    rename          Rename a source.
-    set-url         Change the URL for a source.
-    set-method      Change the method for a source.");
-}
-
-fn main() {
-    let argv = os::args();
-    let o = build_cargo_options(argv);
-
-    if vec::len(o.free) < 2u {
-        cmd_usage();
-        return;
-    }
-    if o.help {
-        match o.free[1] {
-            ~"init" => cmd_usage_init(),
-            ~"install" => cmd_usage_install(),
-            ~"uninstall" => cmd_usage_uninstall(),
-            ~"list" => cmd_usage_list(),
-            ~"search" => cmd_usage_search(),
-            ~"sources" => cmd_usage_sources(),
-            _ => cmd_usage()
-        }
-        return;
-    }
-    if o.free[1] == ~"usage" {
-        cmd_usage();
-        return;
-    }
-
-    let mut c = configure(o);
-    let home = c.root;
-    let first_time = os::path_exists(&home.push("sources.json"));
-
-    if !first_time && o.free[1] != ~"init" {
-        cmd_init(&c);
-
-        // FIXME (#2662): shouldn't need to reconfigure
-        c = configure(o);
-    }
-
-    let c = &move c;
-
-    match o.free[1] {
-        ~"init" => cmd_init(c),
-        ~"install" => cmd_install(c),
-        ~"uninstall" => cmd_uninstall(c),
-        ~"list" => cmd_list(c),
-        ~"search" => cmd_search(c),
-        ~"sources" => cmd_sources(c),
-        _ => cmd_usage()
-    }
-
-    dump_cache(c);
-    dump_sources(c);
-}