@@ -5,7 +5,7 @@ use std::{
55 path:: PathBuf ,
66} ;
77
8- use anyhow:: { anyhow, bail, Result } ;
8+ use anyhow:: { anyhow, bail, Context , Result } ;
99use argp:: FromArgs ;
1010use object:: { Object , ObjectSymbol , SymbolScope } ;
1111
@@ -23,6 +23,7 @@ pub struct Args {
2323#[ argp( subcommand) ]
2424enum SubCommand {
2525 Create ( CreateArgs ) ,
26+ Extract ( ExtractArgs ) ,
2627}
2728
2829#[ derive( FromArgs , PartialEq , Eq , Debug ) ]
@@ -37,9 +38,28 @@ pub struct CreateArgs {
3738 files : Vec < PathBuf > ,
3839}
3940
41+ #[ derive( FromArgs , PartialEq , Eq , Debug ) ]
42+ /// Extracts a static library.
43+ #[ argp( subcommand, name = "extract" ) ]
44+ pub struct ExtractArgs {
45+ #[ argp( positional) ]
46+ /// input files
47+ files : Vec < PathBuf > ,
48+ #[ argp( option, short = 'o' ) ]
49+ /// output directory
50+ out : Option < PathBuf > ,
51+ #[ argp( switch, short = 'q' ) ]
52+ /// quiet output
53+ quiet : bool ,
54+ #[ argp( switch, short = 'v' ) ]
55+ /// verbose output
56+ verbose : bool ,
57+ }
58+
4059pub fn run ( args : Args ) -> Result < ( ) > {
4160 match args. command {
4261 SubCommand :: Create ( c_args) => create ( c_args) ,
62+ SubCommand :: Extract ( c_args) => extract ( c_args) ,
4363 }
4464}
4565
@@ -87,3 +107,51 @@ fn create(args: CreateArgs) -> Result<()> {
87107 builder. into_inner ( ) ?. flush ( ) ?;
88108 Ok ( ( ) )
89109}
110+
111+ fn extract ( args : ExtractArgs ) -> Result < ( ) > {
112+ // Process response files (starting with '@')
113+ let files = process_rsp ( & args. files ) ?;
114+
115+ // Extract files
116+ let mut num_files = 0 ;
117+ for path in & files {
118+ let mut out_dir = if let Some ( out) = & args. out { out. clone ( ) } else { PathBuf :: new ( ) } ;
119+ // If there are multiple files, extract to separate directories
120+ if files. len ( ) > 1 {
121+ out_dir
122+ . push ( path. with_extension ( "" ) . file_name ( ) . ok_or_else ( || anyhow ! ( "No file name" ) ) ?) ;
123+ }
124+ std:: fs:: create_dir_all ( & out_dir) ?;
125+ if !args. quiet {
126+ println ! ( "Extracting {} to {}" , path. display( ) , out_dir. display( ) ) ;
127+ }
128+
129+ let file = map_file ( path) ?;
130+ let mut archive = ar:: Archive :: new ( file. as_slice ( ) ) ;
131+ while let Some ( entry) = archive. next_entry ( ) {
132+ let mut entry =
133+ entry. with_context ( || format ! ( "Processing entry in {}" , path. display( ) ) ) ?;
134+ let file_name = std:: str:: from_utf8 ( entry. header ( ) . identifier ( ) ) ?;
135+ if !args. quiet && args. verbose {
136+ println ! ( "\t {}" , file_name) ;
137+ }
138+ let mut file_path = out_dir. clone ( ) ;
139+ for segment in file_name. split ( & [ '/' , '\\' ] ) {
140+ file_path. push ( sanitise_file_name:: sanitise ( segment) ) ;
141+ }
142+ if let Some ( parent) = file_path. parent ( ) {
143+ std:: fs:: create_dir_all ( parent) ?;
144+ }
145+ let mut file = File :: create ( & file_path)
146+ . with_context ( || format ! ( "Failed to create file {}" , file_path. display( ) ) ) ?;
147+ std:: io:: copy ( & mut entry, & mut file) ?;
148+ file. flush ( ) ?;
149+
150+ num_files += 1 ;
151+ }
152+ }
153+ if !args. quiet {
154+ println ! ( "Extracted {} files" , num_files) ;
155+ }
156+ Ok ( ( ) )
157+ }
0 commit comments