├── .gitignore ├── COPYING.txt ├── README.rdoc ├── Rakefile.rb ├── ext ├── archive.cpp ├── entry.cpp ├── extconf.rb ├── main.cpp └── main.hpp └── test ├── neue.tar.bz2 └── test_tararchive.rb /.gitignore: -------------------------------------------------------------------------------- 1 | doc/ 2 | html/ 3 | pkg/ 4 | *~ 5 | # Ignore compilation products 6 | *.o 7 | *.so 8 | 9 | #eclipse project files 10 | .cproject 11 | .project 12 | 13 | # Ignore mkmf-generated files 14 | ext/Makefile 15 | ext/extconf.h 16 | ext/mkmf.log 17 | 18 | -------------------------------------------------------------------------------- /COPYING.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = libarchive-ruby 2 | 3 | This is libarchive-ruby, your preferred archiving toolkit in Ruby! 4 | libarchive-ruby is a Ruby binding to the famous 5 | {libarchive library}[http://code.google.com/p/libarchive/] and supports 6 | nearly all features the library exposes through it's C++ interface. 7 | 8 | librarchive-ruby focuses on a clean and ruby-like syntax that makes it trivial 9 | to create, read, write and extract archive files of various formats. Want to 10 | know more? Keep reading! 11 | 12 | == Prerequesites 13 | 14 | In order to successfully install libarchive-ruby, you need the following: 15 | 16 | * Ruby >= 1.8.7 17 | * A C++ compiler (we use the {GNU project's}[http://www.gnu.org] g++) 18 | * libarchive (we tested with 2.8.4) 19 | 20 | == How to install? 21 | 22 | === RubyGems 23 | 24 | This is the easiest and preferred way. Ensure you have a proper built 25 | environment for C++ code, and then do 26 | 27 | # gem install libarchive-ruby 28 | 29 | === Building from source 30 | 31 | If you want to be on the bleeding etch, clone the our 32 | {git repository at GitHub}[https://github.com/Hanmac/libarchive-ruby]: 33 | 34 | $ git clone https://github.com/Hanmac/libarchive-ruby.git 35 | $ cd libarchive-ruby 36 | 37 | Then you can either choose to use the library from that directory by running 38 | 39 | $ rake compile 40 | 41 | or to make a gem and install that one. 42 | 43 | $ rake gem 44 | # gem install --local pkg/libarchive-ruby-x.x.x 45 | 46 | == How to use? 47 | 48 | First, you have to require the library: 49 | 50 | require "archive" #Note this is NOT "libarchive-ruby" 51 | 52 | Then you can use the beautiful rubyish API: 53 | 54 | === Read an archive 55 | 56 | Assuming, you have "myarchive.tar.bz2" in the current directory. 57 | 58 | a = Archive.new("myarchive.tar.bz2") 59 | puts "This archive contains:" 60 | a.each{|entry| puts entry} 61 | 62 | #Archive includes the Enumerable module, making available all that 63 | #nice enumerating functionality: 64 | puts All entries in uppercase are: 65 | puts a.map{|entry| entry.path.upcase}.join("\n") 66 | 67 | #Furthermore, you can even read from the files contained in the 68 | #archive without actually extracting it: 69 | a.each{|entry, data| puts "Content of #{entry} is: #{data}"} 70 | 71 | === Extract an archive 72 | 73 | Assuming you have "myarchive.tar.bz2" in the current directory. 74 | 75 | a = Archive.new("myarchive.tar.bz2") 76 | 77 | #Extract all files to the current directory 78 | a.extract 79 | 80 | #Extract a specific file 81 | a.extract("mydir/myfile") 82 | 83 | #Restrict what file attributes are extracted: 84 | a.extract(:extract => Archive::EXTRACT_OWNER | Archive::EXTRACT_TIME) 85 | 86 | #Extract only files whose path is longer than 5 characters 87 | a.extract_if{|entry| entry.path.size > 5} 88 | 89 | === Create an archive 90 | 91 | Assuming you have three files "a.rb", "b.cpp" and "README.rdoc" in your 92 | current directory: 93 | 94 | a = Archive.new("myarchive.tar.gz") 95 | a << "a.rb" << "b.cpp" << "README.rdoc" 96 | 97 | If you have more than one file to add you can use an Array: 98 | 99 | a = Archive.new("myarchive.tar.gz") 100 | a << ["a.rb", "b.cpp", "README.rdoc"] 101 | 102 | You can manipulate the entry witch is added to the file: 103 | 104 | a = Archive.new("myarchive.tar.gz") 105 | a.add("a.rb") {|entry| entry.mtime = Time.now } 106 | 107 | 108 | #Note how libarchive-ruby automatically picked up the archive format 109 | #you wanted by examining the file extension: 110 | a = Archive.new("myarchive.tar.gz") 111 | puts a.format_name #=> POSIX ustar format 112 | 113 | == Further reading 114 | 115 | Have a look at the documentation for the Archive and Archive::Entry classes 116 | for more information. 117 | 118 | == Contributors 119 | 120 | The following people have worked on libarchive-ruby beside 121 | me: 122 | 123 | * Quintus contributed to the docs 124 | 125 | == License 126 | 127 | libarchive-ruby is a Ruby binding for the C library libarchive. 128 | 129 | Copyright © 2011 Hans Mackowiak 130 | 131 | This program is free software; you can redistribute it and/or modify 132 | it under the terms of the GNU General Public License as published by 133 | the Free Software Foundation; either version 2 of the License, or 134 | (at your option) any later version. 135 | 136 | This program is distributed in the hope that it will be useful, 137 | but WITHOUT ANY WARRANTY; without even the implied warranty of 138 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 139 | GNU General Public License for more details. 140 | 141 | You should have received a copy of the GNU General Public License along 142 | with this program; if not, write to the Free Software Foundation, Inc., 143 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 144 | 145 | === Contact 146 | 147 | You can read me via the email address hanmac ÄT gmx DÖT de. 148 | -------------------------------------------------------------------------------- /Rakefile.rb: -------------------------------------------------------------------------------- 1 | #Encoding: UTF-8 2 | =begin 3 | This file is part of libarchive-ruby. 4 | 5 | libarchive-ruby is a Ruby binding for the C library libarchive. 6 | 7 | Copyright © 2011 Hans Mackowiak 8 | 9 | libarchive-ruby is free software; you can redistribute it and/or modify 10 | it under the terms of the GNU General Public License as published by 11 | the Free Software Foundation; either version 2 of the License, or 12 | (at your option) any later version. 13 | 14 | libarchive-ruby is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU General Public License for more details. 18 | 19 | You should have received a copy of the GNU General Public License along 20 | with libarchive-ruby; if not, write to the Free Software Foundation, Inc., 21 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 22 | =end 23 | 24 | require "rake" 25 | gem "rdoc" #Ruby's internal RDoc is not really good 26 | require "rdoc/task" 27 | require "rubygems/package_task" 28 | require "rake/testtask" 29 | require "rake/clean" 30 | 31 | #All generated files not necessary for final use 32 | CLEAN.include("ext/mkmf.log", "ext/Makefile", "ext/*.o") 33 | #All generated files 34 | CLOBBER.include("ext/*.so") 35 | 36 | spec = Gem::Specification.new do |s| 37 | s.name = "libarchive-ruby" 38 | s.summary = "Ruby bindings for libarchive" 39 | s.description = <= 2.0.0") 49 | #s.add_development_dependency("rdoc", ">= 3") 50 | s.requirements = ["A C++ compiler", "libarchive library"] 51 | s.files = ["README.rdoc", "COPYING.txt", Dir["ext/*.cpp"], Dir["ext/*.hpp"], Dir["ext/*.rb"], Dir["lib/**/*.rb"]].flatten 52 | s.extensions << "ext/extconf.rb" 53 | s.has_rdoc = true 54 | s.extra_rdoc_files = %w[README.rdoc] 55 | s.rdoc_options << "-t" << "libarchive-ruby RDocs" << "-m" << "README.rdoc" 56 | #s.homepage = "http://hanmac.com/libarchive-ruby 57 | end 58 | 59 | Gem::PackageTask.new(spec).define 60 | 61 | Rake::RDocTask.new do |rd| 62 | rd.rdoc_files.include("COPYING.txt", "lib/**/*.rb", "ext/**/*.cpp", "ext/**/*.hpp", "**/*.rdoc") 63 | rd.title = "libarchive-ruby RDocs" 64 | rd.main = "README.rdoc" 65 | rd.generator = "hanna" #Ignored if hanna-nouveau isn't installed 66 | rd.rdoc_dir = "doc" 67 | end 68 | 69 | Rake::TestTask.new("test") do |t| 70 | t.pattern = "test/test_*.rb" 71 | t.warning = true 72 | end 73 | 74 | desc "Compiles libarchive-ruby, outputting ext/archive.so" 75 | task :compile do 76 | cd "ext" 77 | ruby "extconf.rb" 78 | sh "make" 79 | cd ".." 80 | end 81 | -------------------------------------------------------------------------------- /ext/archive.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | This file is part of libarchive-ruby. 3 | 4 | libarchive-ruby is a Ruby binding for the C library libarchive. 5 | 6 | Copyright (C) 2011 Hans Mackowiak 7 | 8 | libarchive-ruby is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | libarchive-ruby is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with libarchive-ruby; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | ****************************************************************************/ 22 | 23 | #include "main.hpp" 24 | #include 25 | #include 26 | #include 27 | #include 28 | #define _self wrap(self) 29 | #define RB_ENSURE(func1,obj1,func2,obj2)\ 30 | rb_ensure(RUBY_METHOD_FUNC(func1),(VALUE)obj1,RUBY_METHOD_FUNC(func2),(VALUE)obj2) 31 | 32 | //TODO: handle string IO as archive Archive.new(stringio) 33 | 34 | #ifndef rb_proc_arity 35 | #define rb_proc_arity(obj) 2 36 | #endif 37 | 38 | 39 | typedef std::vector EntryVector; 40 | typedef std::vector BuffVector; 41 | typedef std::vector IntVector; 42 | 43 | typedef std::vector > DataVector; 44 | 45 | typedef std::map SymToIntType; 46 | SymToIntType SymToFormat; 47 | SymToIntType SymToFilter; 48 | 49 | typedef std::map > FileExtType; 50 | 51 | FileExtType fileExt; 52 | 53 | VALUE rb_cArchive,rb_eArchiveError,rb_eArchiveErrorFormat,rb_eArchiveErrorCompression; 54 | 55 | VALUE Archive_alloc(VALUE self) 56 | { 57 | rarchive *result = new rarchive; 58 | return wrap(result); 59 | } 60 | 61 | VALUE Archive_initialize_copy(VALUE self, VALUE other) 62 | { 63 | VALUE result = rb_call_super(1,&other); 64 | _self->path = std::string(wrap(other)->path.c_str()); 65 | _self->type = wrap(other)->type; 66 | return result; 67 | } 68 | 69 | struct extract_obj { 70 | struct archive* archive; 71 | int extract_opt; 72 | }; 73 | 74 | struct write_obj { 75 | struct archive* archive; 76 | DataVector *data; 77 | }; 78 | 79 | struct add_obj { 80 | struct archive* archive; 81 | DataVector *data; 82 | VALUE obj; 83 | VALUE name; 84 | }; 85 | 86 | 87 | VALUE wrap_data(const std::string &str) 88 | { 89 | #if HAVE_RUBY_ENCODING_H 90 | return rb_external_str_new_with_enc(&str[0],str.length(),rb_filesystem_encoding()); 91 | #else 92 | return rb_str_new(&str[0],str.length()); 93 | #endif 94 | } 95 | 96 | 97 | 98 | VALUE Archive_read_block_ensure(struct archive * data) 99 | { 100 | archive_read_close(data); 101 | return Qnil; 102 | } 103 | 104 | VALUE Archive_write_block_ensure(struct archive * data) 105 | { 106 | archive_write_close(data); 107 | return Qnil; 108 | } 109 | 110 | int rubymyopen(struct archive *a, void *client_data) 111 | { 112 | rb_funcall((VALUE)client_data,rb_intern("rewind"),0); 113 | return ARCHIVE_OK; 114 | } 115 | ssize_t rubymyread(struct archive *a, void *client_data, const void **buff) 116 | { 117 | VALUE result = rb_funcall((VALUE)client_data,rb_intern("read"),0); 118 | if(result == Qnil){ 119 | *buff = NULL; 120 | return 0; 121 | }else{ 122 | if (RSTRING_LEN(result)) 123 | *buff = RSTRING_PTR(result); 124 | else 125 | *buff = NULL; 126 | return RSTRING_LEN(result); 127 | } 128 | } 129 | 130 | ssize_t rubymywrite(struct archive *a,void *client_data,const void *buffer, size_t length){ 131 | VALUE string = rb_str_new((char*)buffer,length); 132 | // rb_funcall(string,rb_intern("rstrip!"),0); 133 | return NUM2INT(rb_funcall((VALUE)client_data,rb_intern("write"),1,string)); 134 | } 135 | 136 | int rubymyclose(struct archive *a, void *client_data) 137 | { 138 | rb_funcall((VALUE)client_data,rb_intern("rewind"),0); 139 | return ARCHIVE_OK; 140 | } 141 | 142 | namespace RubyArchive { 143 | 144 | void read_get_filters(struct archive *a,IntVector &filters) 145 | { 146 | size_t filtercount = archive_filter_count(a); 147 | for(size_t i = 0; i < filtercount; ++i) 148 | filters.push_back(archive_filter_code(a,i)); 149 | } 150 | 151 | void read_data(struct archive *a,std::string &str) 152 | { 153 | char buff[8192]; 154 | size_t bytes_read; 155 | try{ 156 | while ((bytes_read=archive_read_data(a,&buff,sizeof(buff)))>0) 157 | str.append(buff,bytes_read); 158 | } catch (...){ 159 | rb_raise(rb_eArchiveError,"error:%d:%s",archive_errno(a),archive_error_string(a)); 160 | } 161 | 162 | } 163 | 164 | void read_old_data(struct archive *a,DataVector &entries, int &format, IntVector &filters) 165 | { 166 | struct archive_entry *entry; 167 | 168 | while(archive_read_next_header(a, &entry)==ARCHIVE_OK){ 169 | format = archive_format(a); 170 | if(filters.empty()) 171 | read_get_filters(a,filters); 172 | std::string buff; 173 | read_data(a,buff); 174 | 175 | entries.push_back(std::make_pair(archive_entry_clone(entry),buff)); 176 | 177 | } 178 | archive_read_close(a); 179 | } 180 | 181 | struct archive* create_match_object(VALUE opts) 182 | { 183 | VALUE temp; 184 | struct archive* result = archive_match_new(); 185 | if(rb_obj_is_kind_of(opts,rb_cHash)) 186 | { 187 | if(RTEST(temp=rb_hash_aref(opts,ID2SYM(rb_intern("uid"))))) 188 | archive_match_include_uid(result,NUM2INT(temp)); 189 | if(RTEST(temp=rb_hash_aref(opts,ID2SYM(rb_intern("gid"))))) 190 | archive_match_include_gid(result,NUM2INT(temp)); 191 | 192 | } 193 | return result; 194 | } 195 | 196 | 197 | bool match_entry(struct archive_entry *entry,VALUE name) 198 | { 199 | const char *cstr = archive_entry_pathname(entry); 200 | if(rb_obj_is_kind_of(name,rb_cArchiveEntry)){ 201 | return std::string(cstr).compare(archive_entry_pathname(wrap(name)))==0; 202 | }else if(rb_obj_is_kind_of(name,rb_cRegexp)){ 203 | VALUE str = rb_str_new2(cstr); 204 | return rb_reg_match(name,str)!=Qnil; 205 | }else 206 | name = rb_funcall(name,rb_intern("to_s"),0); 207 | std::string str1(StringValueCStr(name)),str2(str1); 208 | str2 += '/'; // dir ends of '/' 209 | return str1.compare(cstr)==0 || str2.compare(cstr)==0; 210 | 211 | return false; 212 | } 213 | 214 | 215 | int write_set_filters(struct archive *data,const IntVector &filter) 216 | { 217 | int error = 0; 218 | 219 | for (IntVector::const_reverse_iterator it = filter.rbegin(); it != filter.rend();++it) 220 | if(*it != ARCHIVE_FILTER_NONE) 221 | archive_write_add_filter(data,*it); 222 | 223 | return error; 224 | } 225 | 226 | void read_data_from_fd(struct archive *file,VALUE name,int fd,DataVector &data) 227 | { 228 | char buff[8192]; 229 | size_t bytes_read; 230 | std::string strbuff; 231 | 232 | struct archive_entry *entry = archive_entry_new(); 233 | 234 | archive_read_disk_entry_from_file(file, entry, fd, NULL); 235 | archive_entry_copy_pathname(entry, StringValueCStr(name)); 236 | 237 | 238 | while ((bytes_read = read(fd, buff, sizeof(buff))) > 0) 239 | strbuff.append(buff, bytes_read); 240 | 241 | data.push_back(std::make_pair(entry,strbuff)); 242 | } 243 | 244 | void read_data_from_ruby(struct archive *file,VALUE name,VALUE obj,DataVector &data) 245 | { 246 | std::string strbuff; 247 | 248 | struct archive_entry *entry = archive_entry_new(); 249 | 250 | archive_entry_copy_pathname(entry, StringValueCStr(name)); 251 | 252 | //TODO should i read in chunks? 253 | VALUE result = rb_funcall(obj,rb_intern("read"),0); 254 | strbuff.append(StringValueCStr(result), RSTRING_LEN(result)); 255 | 256 | data.push_back(std::make_pair(entry,strbuff)); 257 | } 258 | 259 | struct read_data_path_obj 260 | { 261 | struct archive* archive; 262 | struct archive* match; 263 | DataVector *data; 264 | VALUE path; 265 | }; 266 | 267 | 268 | void read_data_match_callback(struct archive * arch, void *, struct archive_entry *entry) 269 | { 270 | // entry = archive_entry_clone(entry); 271 | // if(AE_IFDIR != archive_entry_filetype(entry)) 272 | // { 273 | // rb_warn("%i ",); 274 | //rb_warn("%i inclusions",archive_match_path_unmatched_inclusions(arch)); 275 | rb_warn("%s is excluded",archive_entry_pathname(entry)); 276 | // rb_warn("%d fflags",archive_entry_filetype(entry)); 277 | // rb_warn("%d uid",archive_entry_uid(entry)); 278 | 279 | // }else 280 | // rb_warn("%s is a DIR!",archive_entry_pathname(entry)); 281 | // archive_read_disk_descend(arch); 282 | 283 | 284 | } 285 | 286 | int read_data_meta_callback(struct archive *arch, void *, struct archive_entry *entry) 287 | { 288 | if(archive_read_disk_can_descend(arch)) 289 | archive_read_disk_descend(arch); 290 | return 1; 291 | } 292 | 293 | VALUE read_data_from_path_block(struct read_data_path_obj *obj) 294 | { 295 | struct archive_entry *entry = archive_entry_new(); 296 | 297 | while(archive_read_next_header2(obj->archive,entry) == ARCHIVE_OK) 298 | { 299 | const char *cstr = archive_entry_pathname(entry); 300 | 301 | //allways add subdirs 302 | //if(archive_read_disk_can_descend(obj->archive)) 303 | // archive_read_disk_descend(obj->archive); 304 | 305 | // ignore the "." base dir 306 | if(strcmp(cstr,".") == 0){ 307 | continue; 308 | } 309 | // fist look at the match object 310 | if(archive_match_excluded(obj->match,entry)) 311 | continue; 312 | 313 | // then if path is Regexp try to match against it 314 | if(rb_obj_is_kind_of(obj->path,rb_cRegexp)) 315 | if(!RTEST(rb_reg_match(obj->path,rb_str_new2(cstr)))) 316 | continue; 317 | 318 | // then use the block if given 319 | if((!rb_block_given_p()) || RTEST(rb_yield(wrap(entry)))) 320 | { 321 | //TODO: react to the change of the entry object 322 | std::string strbuff; 323 | const void *p; 324 | size_t size; 325 | int64_t offset; 326 | 327 | while(archive_read_data_block(obj->archive, &p, &size, &offset) == ARCHIVE_OK) 328 | strbuff.append((const char*)p,size); 329 | 330 | struct archive_entry *entry2 = archive_entry_clone(entry); 331 | 332 | 333 | //Drop the ./ from a filename when using pattern 334 | std::string path(archive_entry_pathname(entry2)); 335 | if(path.compare(0,2,"./") == 0) { 336 | path.erase(0,2); 337 | archive_entry_copy_pathname(entry2,path.c_str()); 338 | } 339 | 340 | obj->data->push_back(std::make_pair(entry2,strbuff)); 341 | 342 | } 343 | 344 | } 345 | return Qnil; 346 | } 347 | 348 | VALUE Archive_read_block_close(struct archive * data) 349 | { 350 | archive_read_close(data); 351 | return Qnil; 352 | } 353 | 354 | void read_data_from_path(struct archive * file,struct archive * match,VALUE rpath,const char* path,DataVector &data) 355 | { 356 | 357 | archive_read_disk_open(file,path); 358 | archive_read_disk_set_matching(file,match,read_data_match_callback,NULL); 359 | archive_read_disk_set_metadata_filter_callback(file, read_data_meta_callback, NULL); 360 | read_data_path_obj obj; 361 | obj.archive = file; 362 | obj.path = rpath; 363 | obj.match = match; 364 | obj.data = &data; 365 | 366 | //struct archive_entry *entry = archive_entry_new(); 367 | 368 | //archive_read_next_header2(file,entry); 369 | //archive_read_disk_descend(file); 370 | 371 | RB_ENSURE(read_data_from_path_block,&obj,Archive_read_block_close,file); 372 | } 373 | 374 | 375 | void read_data_from_path2(VALUE path,VALUE opts,DataVector &data) 376 | { 377 | struct archive * file = archive_read_disk_new(); 378 | struct archive * match = create_match_object(opts); 379 | 380 | archive_read_disk_set_standard_lookup(file); 381 | const char * str = "."; 382 | archive_match_include_pattern(match,"."); 383 | //archive_match_exclude_pattern(match,"*/*/*.cpp"); 384 | //archive_match_exclude_pattern(match,"*.cpp"); 385 | //archive_match_include_pattern(match,"*/"); 386 | 387 | if(!rb_obj_is_kind_of(path,rb_cRegexp)) 388 | { 389 | path = rb_funcall(path,rb_intern("to_s"),0); 390 | if(!RTEST(rb_funcall(rb_cFile,rb_intern("exist?"),1,path))){ 391 | archive_match_include_pattern(match,StringValueCStr(path)); 392 | }else 393 | str = StringValueCStr(path); 394 | } 395 | read_data_from_path(file,match,path,str,data); 396 | } 397 | 398 | } 399 | 400 | void Archive_format_from_path(VALUE self,int &format,IntVector &filters) 401 | { 402 | if(_self->type == archive_path){ 403 | std::string selfpath = _self->path; 404 | size_t selfpathlength = selfpath.length(); 405 | for(FileExtType::iterator it = fileExt.begin(); it != fileExt.end(); ++it) { 406 | if(selfpath.rfind(it->first) + it->first.length() == selfpathlength) { 407 | format = it->second.first; 408 | filters.push_back(it->second.second); 409 | return; 410 | } 411 | } 412 | } 413 | 414 | format = _self->format; 415 | filters.push_back(_self->filter); 416 | 417 | } 418 | 419 | int Archive_read_ruby(VALUE self,struct archive *data) 420 | { 421 | 422 | archive_read_support_filter_all(data); 423 | archive_read_support_format_all(data); 424 | #if HAVE_ARCHIVE_READ_SUPPORT_FORMAT_RAW 425 | archive_read_support_format_raw(data); 426 | #endif 427 | int error =0; 428 | switch(_self->type){ 429 | case archive_path: 430 | error = archive_read_open_filename(data,_self->path.c_str(),10240); 431 | break; 432 | case archive_fd: 433 | error = archive_read_open_fd(data,_self->fd,10240); 434 | break; 435 | case archive_buffer: 436 | break; 437 | case archive_ruby: 438 | error = archive_read_open(data, (void*)_self->ruby,rubymyopen,rubymyread,rubymyclose); 439 | break; 440 | } 441 | return error; 442 | } 443 | 444 | 445 | 446 | int Archive_write_ruby(VALUE self,struct archive *data,int format, const IntVector &filters) 447 | { 448 | int error =0; 449 | 450 | if((error = archive_write_set_format(data,format)) != ARCHIVE_OK) 451 | rb_raise(rb_eArchiveErrorFormat,"error (%d): %s ",error,archive_error_string(data)); 452 | 453 | RubyArchive::write_set_filters(data,filters); 454 | 455 | switch(_self->type){ 456 | case archive_path: 457 | error = archive_write_open_filename(data,_self->path.c_str()); 458 | break; 459 | case archive_fd: 460 | error = archive_write_open_fd(data,_self->fd); 461 | break; 462 | case archive_buffer: 463 | break; 464 | case archive_ruby: 465 | error = archive_write_open(data,(void*)_self->ruby,rubymyopen,rubymywrite,rubymyclose); 466 | break; 467 | } 468 | return error; 469 | } 470 | 471 | /* 472 | *call-seq: 473 | * new( path [, format [, compression ] ] ) → an_archive 474 | * 475 | * Makes a new Archive object. If format is given, a new archive is created or 476 | * an existing archive will be converted into the given format. 477 | * ===Parameters 478 | * [path] The path to the archive. May or may not exist. 479 | * [format] The archive's format as a symbol. If you ommit this, the format will 480 | * be guessed from the file extension. Possible formats are: 481 | * * :ar 482 | * * :tar 483 | * * :pax 484 | * * :xar 485 | * * :zip 486 | * [compression] Symbol inidicating the compression you want to use. If 487 | * ommited, it will be guessed from the file extension. 488 | * Possible formats are: 489 | * * :bzip2 490 | * * :compress 491 | * * :gzip 492 | * * :lzma 493 | * * :xz 494 | * ===Raises 495 | * [FormatError] Unknown archive format or writing not supported. 496 | * [CompressionError] Unknown compression format. 497 | * ===Examples 498 | * See the README for some examples. 499 | */ 500 | 501 | VALUE Archive_initialize(int argc, VALUE *argv,VALUE self) 502 | { 503 | VALUE path,r_format,r_filter; 504 | rb_scan_args(argc, argv, "12", &path,&r_format,&r_filter); 505 | if(rb_obj_is_kind_of(path,rb_cIO)){ 506 | _self->fd = NUM2INT(rb_funcall(path,rb_intern("fileno"),0)); 507 | _self->type = archive_fd; 508 | rb_scan_args(argc, argv, "21", &path,&r_format,&r_filter); 509 | }else if(rb_obj_is_kind_of(path,rb_cInteger)){ 510 | _self->fd = NUM2INT(path); 511 | _self->type = archive_fd; 512 | rb_scan_args(argc, argv, "21", &path,&r_format,&r_filter); 513 | }else if(rb_respond_to(path,rb_intern("read"))){ 514 | _self->type = archive_ruby; 515 | rb_scan_args(argc, argv, "21", &path,&r_format,&r_filter); 516 | }else{ 517 | path = rb_file_s_expand_path(1,&path); 518 | _self->path = std::string(StringValueCStr(path)); 519 | _self->type =archive_path; 520 | } 521 | _self->ruby = path; 522 | //to set the format the file must convert 523 | if(!NIL_P(r_format)){ 524 | 525 | //struct archive *a = archive_read_new(),*b=archive_write_new(); 526 | //struct archive_entry *entry; 527 | int format,filter; 528 | 529 | if(SYMBOL_P(r_format)){ 530 | SymToIntType::iterator it = SymToFormat.find(SYM2ID(r_format)); 531 | if(it != SymToFormat.end()) { 532 | format = it->second; 533 | }else 534 | rb_raise(rb_eTypeError,"wrong format"); 535 | }else 536 | rb_raise(rb_eTypeError,"exepted Symbol"); 537 | 538 | if(NIL_P(r_filter)){ 539 | filter = ARCHIVE_FILTER_NONE; 540 | }else if(SYMBOL_P(r_filter)){ 541 | SymToIntType::iterator it = SymToFilter.find(SYM2ID(r_filter)); 542 | if(it != SymToFilter.end()) { 543 | filter = it->second; 544 | } else 545 | rb_raise(rb_eTypeError,"unsupported filter"); 546 | }else 547 | rb_raise(rb_eTypeError,"exepted Symbol"); 548 | 549 | //autodetect format and compression 550 | 551 | _self->format = format; 552 | _self->filter = filter; 553 | 554 | // if(Archive_read_ruby(self,a)==ARCHIVE_OK){ 555 | // while(archive_read_next_header(a, &entry)==ARCHIVE_OK){ 556 | // if(format==archive_format(a) && filter==archive_filter_code(a,0)){ 557 | // archive_read_close(a); 558 | // return self; 559 | // } 560 | // entries.push_back(archive_entry_clone(entry)); 561 | // allbuff.push_back(std::string("")); 562 | // 563 | // RubyArchive::read_data(a,allbuff.back()); 564 | // } 565 | // archive_read_close(a); 566 | // if(Archive_write_ruby(self,b,format,IntVector(filter))==ARCHIVE_OK){ 567 | // //write old data back 568 | // for(unsigned int i=0; i /home/freak/myarchive.tar.gz 589 | */ 590 | 591 | VALUE Archive_path(VALUE self) 592 | { 593 | return (_self->type == archive_path) ? rb_str_new2(_self->path.c_str()) : Qnil; 594 | } 595 | 596 | VALUE Archive_each_block(struct archive *data) 597 | { 598 | struct archive_entry *entry; 599 | while (archive_read_next_header(data, &entry) == ARCHIVE_OK) { 600 | VALUE temp = wrap(entry); 601 | if(rb_proc_arity(rb_block_proc())==1){ 602 | rb_yield(temp); 603 | archive_read_data_skip(data); 604 | }else{ 605 | std::string str; 606 | RubyArchive::read_data(data,str); 607 | rb_yield_values(2,temp,wrap_data(str)); 608 | } 609 | } 610 | return Qnil; 611 | } 612 | 613 | /* 614 | * call-seq: 615 | * each(){|entry [, data]| ... } → self 616 | * each() → an_enumerator 617 | * 618 | * Iterates through the archive and yields each entry as an Archive::Entry object. The second parameter 619 | * contains the data of that entry, so you don't have to extract it only to read what's in it. 620 | * ===Return value 621 | * If a block is given, returns self, otherwise an enumerator. 622 | * ===Example 623 | * a.each{|entry| p entry.path} 624 | * a.each{|entry, data| puts "'#{entry.path}' contains '#{data}'"} 625 | * Output: 626 | * "file1.txt" 627 | * "file2.txt" 628 | * 'file1.txt' contains 'I am file1!' 629 | * 'file2.txt' contains 'I am file2!' 630 | */ 631 | 632 | VALUE Archive_each(VALUE self) 633 | { 634 | RETURN_ENUMERATOR(self,0,NULL); 635 | struct archive *a = archive_read_new(); 636 | 637 | 638 | int error=Archive_read_ruby(self,a); 639 | if(error==ARCHIVE_OK){ 640 | RB_ENSURE(Archive_each_block,a,Archive_read_block_ensure,a); 641 | return self; 642 | } 643 | return Qnil; 644 | } 645 | 646 | 647 | VALUE Archive_each_entry_block(struct archive *data) 648 | { 649 | VALUE result = rb_ary_new(); 650 | struct archive_entry *entry; 651 | while (archive_read_next_header(data, &entry) == ARCHIVE_OK) { 652 | VALUE temp = wrap(entry); 653 | rb_yield(temp); 654 | archive_read_data_skip(data); 655 | rb_ary_push(result,temp); 656 | } 657 | return result; 658 | } 659 | 660 | /* 661 | * call-seq: 662 | * each_entry() {|entry| ... } → an_array 663 | * each_entry() → an_enumerator 664 | * 665 | * Iterates through the archive and yields each entry as an Archive::Entry object. 666 | * This is the same as #each, but doesn't allow for the second block parameter. 667 | * ===Return value 668 | * If a block is given, returns an array of Archive::Entry objects, otherwise an enumerator. 669 | * ===Example 670 | * a.each_entry{|entry| p entry.path} 671 | * Output: 672 | * "file1.txt" 673 | * "file2.txt" 674 | */ 675 | 676 | VALUE Archive_each_entry(VALUE self) 677 | { 678 | RETURN_ENUMERATOR(self,0,NULL); 679 | struct archive *a = archive_read_new(); 680 | 681 | if(Archive_read_ruby(self,a)==ARCHIVE_OK) 682 | return RB_ENSURE(Archive_each_entry_block,a,Archive_read_block_ensure,a); 683 | return Qnil; 684 | } 685 | 686 | 687 | VALUE Archive_each_data_block(struct archive *data) 688 | { 689 | struct archive_entry *entry; 690 | while (archive_read_next_header(data, &entry) == ARCHIVE_OK) { 691 | std::string str; 692 | RubyArchive::read_data(data,str); 693 | rb_yield(wrap_data(str)); 694 | } 695 | return Qnil; 696 | } 697 | 698 | 699 | /* 700 | * call-seq: 701 | * each_data() {|data| } → self 702 | * each_data() → an_enumerator 703 | * 704 | * Iterates through the archive and yields each entry's data as a string. 705 | * This is the same as #each, but doesn't allow for the first block parameter. 706 | * ===Return value 707 | * If a block is given, returns self, otherwise an enumerator. 708 | * ===Example 709 | * a.each{|data| puts "This is: '#{data}'"} 710 | * Output: 711 | * This is: 'I am file1!' 712 | * This is: 'I am file2!' 713 | */ 714 | 715 | VALUE Archive_each_data(VALUE self) 716 | { 717 | RETURN_ENUMERATOR(self,0,NULL); 718 | struct archive *a = archive_read_new(); 719 | 720 | if(Archive_read_ruby(self,a)==ARCHIVE_OK) 721 | RB_ENSURE(Archive_each_data_block,a,Archive_read_block_ensure,a); 722 | return self; 723 | } 724 | 725 | VALUE Archive_each_filter_block(struct archive *data) 726 | { 727 | struct archive_entry *entry; 728 | archive_read_next_header(data, &entry); 729 | size_t count = archive_filter_count(data); 730 | for(size_t i = 0; i < count; ++i) 731 | rb_yield_values(2,INT2NUM(archive_filter_code(data,i)),rb_str_new2(archive_filter_name(data,i))); 732 | 733 | return Qnil; 734 | } 735 | 736 | 737 | VALUE Archive_each_filter(VALUE self) 738 | { 739 | RETURN_ENUMERATOR(self,0,NULL); 740 | struct archive *a = archive_read_new(); 741 | 742 | if(Archive_read_ruby(self,a)==ARCHIVE_OK) 743 | RB_ENSURE(Archive_each_filter_block,a,Archive_read_block_ensure,a); 744 | return self; 745 | } 746 | 747 | /* 748 | * call-seq: 749 | * archive.to_hash → Hash 750 | * 751 | * Iterates through the archive and yields each data of an entry as a string object. 752 | * ===Return value 753 | * returns Hash of Archive::Entry => String 754 | */ 755 | 756 | VALUE Archive_to_hash(VALUE self) 757 | { 758 | VALUE result = rb_hash_new(); 759 | struct archive *a = archive_read_new(); 760 | struct archive_entry *entry; 761 | 762 | if(Archive_read_ruby(self,a)==ARCHIVE_OK){ 763 | while (archive_read_next_header(a, &entry) == ARCHIVE_OK) { 764 | std::string str; 765 | RubyArchive::read_data(a,str); 766 | rb_hash_aset(result,wrap(entry),wrap_data(str)); 767 | } 768 | archive_read_close(a); 769 | } 770 | return result; 771 | } 772 | 773 | VALUE Archive_map_block(struct write_obj * data){ 774 | for(unsigned int i=0; i< data->data->size(); i++){ 775 | VALUE temp = wrap(data->data->at(i).first); 776 | VALUE val; 777 | if(rb_proc_arity(rb_block_proc())<2){ 778 | val = rb_yield(temp); 779 | }else{ 780 | val = rb_yield_values(2,temp,wrap_data(data->data->at(i).second)); 781 | } 782 | VALUE entry,rdata= Qnil; 783 | if(rb_obj_is_kind_of(val,rb_cArray)){ 784 | entry=rb_ary_entry(val,0); 785 | rdata=rb_ary_entry(val,1); 786 | }else 787 | entry = val; 788 | if(rb_obj_is_kind_of(entry,rb_cArchiveEntry)){ 789 | archive_write_header(data->archive,wrap(val)); 790 | 791 | if(rdata == Qnil){ 792 | std::string &buff = data->data->at(i).second; 793 | archive_write_data(data->archive,&buff[0],buff.length()); 794 | }else{ 795 | //char* buff = StringValueCStr(rdata); 796 | archive_write_data(data->archive,RSTRING_PTR(rdata),RSTRING_LEN(rdata)); 797 | } 798 | archive_write_finish_entry(data->archive); 799 | }else if(entry==Qnil){ 800 | }else 801 | rb_raise(rb_eTypeError,"exepted %s!",rb_class2name(rb_cArchiveEntry)); 802 | } 803 | return Qnil; 804 | } 805 | 806 | /* 807 | * call-seq: 808 | * map!() {| entry [,data] | ... } → Array 809 | * archive.map!() → Enumerator 810 | * 811 | * Iterates through the archive and changes it's data "on the fly", i.e. 812 | * the value your block returns for each iteration is put for the data 813 | * in the yielded entry. Your block is expected to return a 2-element 814 | * array of form 815 | * [archive_entry, "data"] 816 | * where +archive_enty+ is the +entry+ yielded to the block (which you 817 | * may modify via the Archive::Entry methods) and "data" is a 818 | * string containing the data you want to set for this entry. 819 | * 820 | * The block parameters are the same as for #each. 821 | * ===Return value 822 | * The archive itself. 823 | * ===Example 824 | * #Double the contents in each file of the archive 825 | * a.map!{|entry, data| [entry, data * 2]} 826 | * #Clear all files in the archive 827 | * a.map!{|entry| [entry, ""]} 828 | */ 829 | VALUE Archive_map_self(VALUE self) 830 | { 831 | RETURN_ENUMERATOR(self,0,NULL); 832 | 833 | struct archive *a = archive_read_new(),*b=archive_write_new(); 834 | int format = ARCHIVE_FORMAT_EMPTY; 835 | 836 | DataVector entries; 837 | IntVector filters; 838 | 839 | //autodetect format and filters 840 | if(Archive_read_ruby(self,a)==ARCHIVE_OK){ 841 | 842 | RubyArchive::read_old_data(a,entries, format, filters); 843 | 844 | if(Archive_write_ruby(self,b,format,filters)==ARCHIVE_OK){ 845 | write_obj obj; 846 | obj.archive = b; 847 | obj.data = &entries; 848 | RB_ENSURE(Archive_map_block,&obj,Archive_write_block_ensure,b); 849 | } 850 | } 851 | return self; 852 | } 853 | 854 | /* 855 | * call-seq: 856 | * [](name) → Archive::Entry or nil 857 | * 858 | * Returns an archive entry for the given name. 859 | * ===Parameters 860 | * [name] could be a String or a Regex. 861 | * ===Return value 862 | * If a matching entry is found, it's returned as an Archive::Entry object. If not, 863 | * nil is returned. 864 | * ===Example 865 | * #Assuming your archive contains file.txt and ruby.txt 866 | * 867 | * a["file.txt"] #=> Archive::Entry 868 | * a[/txt/] #=> Archive::Entry 869 | */ 870 | 871 | VALUE Archive_get(VALUE self,VALUE val) 872 | { 873 | struct archive *a = archive_read_new(); 874 | struct archive_entry *entry; 875 | 876 | if(Archive_read_ruby(self,a)==ARCHIVE_OK){ 877 | while (archive_read_next_header(a, &entry) == ARCHIVE_OK) { 878 | if(RubyArchive::match_entry(entry,val)){ 879 | VALUE result = wrap(entry); 880 | archive_read_close(a); 881 | return result; 882 | } 883 | } 884 | archive_read_close(a); 885 | } 886 | return Qnil; 887 | } 888 | 889 | 890 | void extract_extract(struct archive *a,struct archive_entry *entry,int extract_opt,VALUE io, int fd) 891 | { 892 | if(rb_obj_is_kind_of(io,rb_cIO)){ 893 | archive_read_data_into_fd(a,fd); 894 | }else if(rb_respond_to(io,rb_intern("write"))){ 895 | char buff[8192]; 896 | size_t bytes_read; 897 | while ((bytes_read=archive_read_data(a,&buff,sizeof(buff)))>0) 898 | rb_funcall(io,rb_intern("write"),1,rb_str_new(buff,bytes_read)); 899 | }else 900 | archive_read_extract(a,entry,extract_opt); 901 | } 902 | /* 903 | * call-seq: 904 | * extract( [name = nil [, io [ ,opt ] ] ] ) → an_array 905 | * 906 | * Extract files to current directory. 907 | * ===Parameters 908 | * [name] (nil) could be an Archive::Entry, a String or an Regex. If given, 909 | * only this entry is extracted. Otherwise extracts the whole 910 | * archive. 911 | * [io] an instance of IO or something with a +write+ method like 912 | * StringIO. If given, the entry specified via +name+ will be 913 | * extracted into +io+ instead of a file. 914 | * [opt] is an option hash. See below for possible options. 915 | * ===Parameters for the option hash 916 | * [:extract] flag, Integer combined of the Archive::Extract_* constants. 917 | * This tells libarchive-ruby to only extract the file attributes 918 | * you specify here. Exceptions is Archive::EXTRACT_NO_OVERWRITE 919 | * which prevents this method from overwrtiting existing files. 920 | * ===Return value 921 | * The paths of the extracted entries as an array. 922 | * ===Example 923 | * #Simply extract everything into the current directory. 924 | * a.extract 925 | * #Extract only file1.txt 926 | * a.extract("file1.txt") 927 | * #Same as above, but extract it to a StringIO 928 | * s = StringIO.new 929 | * a.extract("file1.txt", s) 930 | * #Same as the first example, but only extract information about the 931 | * #modification time. 932 | * a.extract(nil, nil, extract: Archive::EXTRACT_TIME) 933 | */ 934 | 935 | VALUE Archive_extract(int argc, VALUE *argv, VALUE self) 936 | { 937 | 938 | VALUE result = rb_ary_new(),name,io,opts,temp; 939 | struct archive *a = archive_read_new(); 940 | struct archive_entry *entry; 941 | //add raw, this is not by all 942 | int extract_opt = 0,fd=-1; 943 | rb_scan_args(argc, argv, "03", &name,&io,&opts); 944 | if(rb_obj_is_kind_of(name,rb_cHash)){ 945 | opts = name;name = Qnil; 946 | } 947 | if(rb_obj_is_kind_of(io,rb_cHash)){ 948 | opts = io;io = Qnil; 949 | } 950 | if(rb_obj_is_kind_of(io,rb_cIO)) 951 | fd = NUM2INT(rb_funcall(io,rb_intern("fileno"),0)); 952 | 953 | 954 | if(rb_obj_is_kind_of(opts,rb_cHash)) 955 | if(RTEST(temp=rb_hash_aref(opts,ID2SYM(rb_intern("extract"))))) 956 | extract_opt = NUM2INT(temp); 957 | 958 | if(Archive_read_ruby(self,a)==ARCHIVE_OK){ 959 | try{ 960 | if(NIL_P(name)){ 961 | if(!NIL_P(io)){ 962 | rb_raise(rb_eArgError,"You can't extract more than 1 entry into an IO-like object!"); 963 | } 964 | while(archive_read_next_header(a, &entry) == ARCHIVE_OK){ 965 | archive_read_extract(a,entry,extract_opt); 966 | rb_ary_push(result,rb_str_new2(archive_entry_pathname(entry))); 967 | } 968 | }else{ 969 | while(archive_read_next_header(a, &entry) == ARCHIVE_OK){ 970 | if(RubyArchive::match_entry(entry,name)){ 971 | extract_extract(a,entry,extract_opt,io,fd); 972 | rb_ary_push(result,rb_str_new2(archive_entry_pathname(entry))); 973 | } 974 | } 975 | } 976 | }catch (...){ 977 | rb_raise(rb_eArchiveError,"error:%d:%s",archive_errno(a),archive_error_string(a)); 978 | } 979 | archive_read_close(a); 980 | } 981 | return result; 982 | } 983 | 984 | VALUE Archive_extract_if_block(struct extract_obj * data) 985 | { 986 | VALUE result = rb_ary_new(); 987 | struct archive_entry *entry; 988 | while(archive_read_next_header(data->archive, &entry) == ARCHIVE_OK){ 989 | VALUE str = rb_str_new2(archive_entry_pathname(entry)); 990 | if(RTEST(rb_yield(wrap(entry)))){ 991 | archive_read_extract(data->archive,entry,data->extract_opt); 992 | rb_ary_push(result,str); 993 | } 994 | } 995 | return result; 996 | } 997 | /* 998 | * call-seq: 999 | * extract_if( [ opt ] ) {|entry| } → an_array 1000 | * extract_if( [ opt ] ) → an_enumerator 1001 | * 1002 | * Yields each entry in the archive to the block and extracts only those 1003 | * entries (to the current directory) for which the block evaluates to a 1004 | * truth value. 1005 | * ===Parameters 1006 | * [opt] is the same option hash that you can pass to #extract. 1007 | * ====Parameters for the option hash 1008 | * See the #extract method for explanation. 1009 | * ===Return value 1010 | * The paths of all extracted entries if a block was given, an Enumerator 1011 | * otherwise. 1012 | */ 1013 | 1014 | VALUE Archive_extract_if(int argc, VALUE *argv, VALUE self) 1015 | { 1016 | RETURN_ENUMERATOR(self,argc,argv); 1017 | VALUE opts,temp; 1018 | struct archive *a = archive_read_new(); 1019 | 1020 | int extract_opt=0; 1021 | 1022 | rb_scan_args(argc, argv, "01", &opts); 1023 | if(rb_obj_is_kind_of(opts,rb_cHash)) 1024 | if(RTEST(temp=rb_hash_aref(opts,ID2SYM(rb_intern("extract"))))) 1025 | extract_opt = NUM2INT(temp); 1026 | 1027 | if(Archive_read_ruby(self,a)==ARCHIVE_OK){ 1028 | extract_obj obj; 1029 | obj.archive = a; 1030 | obj.extract_opt = extract_opt; 1031 | return RB_ENSURE(Archive_extract_if_block,&obj,Archive_read_block_ensure,a); 1032 | } 1033 | return Qnil; 1034 | } 1035 | 1036 | /* 1037 | * call-seq: 1038 | * format() → an_integer or nil 1039 | * 1040 | * Returns the archive format as an integer. You should use #format_name 1041 | * instead. 1042 | * ===Return value 1043 | * An integer or nil if the format wasn't detectable. 1044 | */ 1045 | 1046 | VALUE Archive_format(VALUE self) 1047 | { 1048 | struct archive *a = archive_read_new(); 1049 | struct archive_entry *entry; 1050 | VALUE result = Qnil; 1051 | 1052 | if(Archive_read_ruby(self,a)==ARCHIVE_OK){ 1053 | archive_read_next_header(a, &entry); 1054 | result = INT2NUM(archive_format(a)); 1055 | archive_read_close(a); 1056 | } 1057 | return result; 1058 | } 1059 | 1060 | /* 1061 | * call-seq: 1062 | * format_name() → a_string or nil 1063 | * 1064 | * Returns the archive format's name as a string. 1065 | * ===Return value 1066 | * A string or nil if the format wasn't detectable. 1067 | * ===Example 1068 | * a.format_name #=> "GNU tar format" 1069 | */ 1070 | 1071 | VALUE Archive_format_name(VALUE self) 1072 | { 1073 | struct archive *a = archive_read_new(); 1074 | struct archive_entry *entry; 1075 | const char* name = NULL; 1076 | 1077 | if(Archive_read_ruby(self,a)==ARCHIVE_OK){ 1078 | if(archive_read_next_header(a, &entry)==ARCHIVE_OK){ 1079 | name = archive_format_name(a); 1080 | archive_read_close(a); 1081 | } 1082 | } 1083 | return name ? rb_str_new2(name) : Qnil; 1084 | } 1085 | 1086 | 1087 | 1088 | 1089 | 1090 | 1091 | 1092 | 1093 | 1094 | 1095 | ////key is the entry and val is 1096 | 1097 | ////TODO: hier nochmal ein ensure rein damit es das file wieder zu macht? 1098 | //int Archive_add_hash_block(VALUE key,VALUE val,struct add_obj *obj){ 1099 | // char buff[8192]; 1100 | // size_t bytes_read; 1101 | // int fd=-1; 1102 | // obj->file = archive_read_disk_new(); 1103 | // archive_read_disk_set_standard_lookup(obj->file); 1104 | // struct archive_entry *entry = archive_entry_new(); 1105 | // archive_entry_copy_pathname(entry, StringValueCStr(key)); 1106 | // if(rb_obj_is_kind_of(val,rb_cFile)){ 1107 | // VALUE pathname = rb_funcall(val,rb_intern("path"),0); //source path 1108 | // VALUE obj2 = rb_file_s_expand_path(1,&pathname); 1109 | // archive_entry_copy_sourcepath(entry, StringValueCStr(obj2)); 1110 | // fd = NUM2INT(rb_funcall(val,rb_intern("fileno"),0)); 1111 | // }else if(rb_obj_is_kind_of(val,rb_cIO)){ 1112 | // fd = NUM2INT(rb_funcall(val,rb_intern("fileno"),0)); 1113 | // }else if(rb_respond_to(val,rb_intern("read"))){ 1114 | // //stringio has neigther path or fileno, so do nothing 1115 | // }else { 1116 | // VALUE obj2 = rb_file_s_expand_path(1,&val); 1117 | // archive_entry_copy_sourcepath(entry, StringValueCStr(obj2)); 1118 | // fd = open(StringValueCStr(obj2), O_RDONLY); 1119 | // if (fd < 0) //TODO: add error 1120 | // { 1121 | // return 0; 1122 | // } 1123 | // } 1124 | // if(fd > 0) 1125 | // archive_read_disk_entry_from_file(obj->file, entry, fd, NULL); 1126 | // if(rb_block_given_p()){ 1127 | // VALUE temp = wrap(entry); 1128 | // VALUE result = rb_yield(temp); 1129 | // if(rb_obj_is_kind_of(result,rb_cArchiveEntry)) 1130 | // entry = wrap(result); 1131 | // else 1132 | // entry = wrap(temp); 1133 | // } 1134 | // for(unsigned int i = 0;i < obj->entries->size();) 1135 | // { 1136 | // if(std::string(archive_entry_pathname(obj->entries->at(i))).compare(archive_entry_pathname(entry)) == 0){ 1137 | // obj->entries->erase(obj->entries->begin()+i); 1138 | // obj->allbuff->erase(obj->allbuff->begin()+i); 1139 | // }else 1140 | // i++; 1141 | // } 1142 | // std::string strbuff; 1143 | // if(fd < 0 and rb_respond_to(val,rb_intern("read"))){ 1144 | // VALUE result = rb_funcall(val,rb_intern("read"),0); 1145 | // strbuff.append(StringValueCStr(result), RSTRING_LEN(result)); 1146 | // }else{ 1147 | // while ((bytes_read = read(fd, buff, sizeof(buff))) > 0) 1148 | // strbuff.append(buff, bytes_read); 1149 | // } 1150 | // obj->entries->push_back(entry); 1151 | // obj->allbuff->push_back(strbuff); 1152 | // if(fd >= 0 and !rb_obj_is_kind_of(val,rb_cIO)) 1153 | // close(fd); 1154 | // archive_read_close(obj->file); 1155 | // return 0; 1156 | //} 1157 | 1158 | //VALUE Archive_add_block(struct add_obj *obj ) 1159 | //{ 1160 | // VALUE robj = obj->obj; 1161 | // char buff[8192]; 1162 | // size_t bytes_read; 1163 | // archive_read_disk_set_standard_lookup(obj->file); 1164 | // if(rb_obj_is_kind_of(robj,rb_cArray)){ 1165 | // for(int i=0;i< RARRAY_LEN(robj);i++){ 1166 | // VALUE temp = RARRAY_PTR(robj)[i]; 1167 | // int fd = -1; 1168 | // char *sourcepath,*pathname; 1169 | // if(rb_obj_is_kind_of(temp,rb_cFile)){ 1170 | // VALUE rpath = rb_funcall(temp,rb_intern("path"),0); //source path 1171 | // pathname = StringValueCStr(rpath); 1172 | // VALUE obj2 = rb_file_s_expand_path(1,&rpath); 1173 | // sourcepath = StringValueCStr(obj2); 1174 | // fd = NUM2INT(rb_funcall(temp,rb_intern("fileno"),0)); 1175 | // }else{ 1176 | // VALUE obj2 = rb_file_s_expand_path(1,&temp); 1177 | // sourcepath = pathname = StringValueCStr(obj2); 1178 | // fd = open(pathname, O_RDONLY); 1179 | // if (fd < 0) //TODO: add error 1180 | // return Qnil; 1181 | // } 1182 | // struct archive_entry *entry = archive_entry_new(); 1183 | // archive_entry_copy_sourcepath(entry, sourcepath); 1184 | // archive_entry_copy_pathname(entry, pathname); 1185 | // archive_read_disk_entry_from_file(obj->file, entry, fd, NULL); 1186 | // if(rb_block_given_p()){ 1187 | // VALUE temp = wrap(entry); 1188 | // VALUE result = rb_yield(temp); 1189 | // if(rb_obj_is_kind_of(result,rb_cArchiveEntry)) 1190 | // entry = wrap(result); 1191 | // else 1192 | // entry = wrap(temp); 1193 | // } 1194 | // std::string strbuff; 1195 | // while ((bytes_read = read(fd, buff, sizeof(buff))) > 0) 1196 | // strbuff.append(buff, bytes_read); 1197 | // if(fd >= 0 and !rb_obj_is_kind_of(robj,rb_cIO)) 1198 | // close(fd); 1199 | // obj->entries->push_back(entry); 1200 | // obj->allbuff->push_back(strbuff); 1201 | // } 1202 | // }else if(rb_obj_is_kind_of(robj,rb_cHash)){ 1203 | // archive_read_close(obj->file); 1204 | // rb_hash_foreach(robj,(int (*)(...))Archive_add_hash_block,(VALUE)obj); 1205 | // }else 1206 | // { 1207 | // if(obj->fd > 0) 1208 | // archive_read_disk_entry_from_file(obj->file, obj->entry, obj->fd, NULL); 1209 | 1210 | // if(rb_block_given_p()){ 1211 | // VALUE temp = wrap(obj->entry); 1212 | // VALUE result = rb_yield(temp); 1213 | // if(rb_obj_is_kind_of(result,rb_cArchiveEntry)) 1214 | // obj->entry = wrap(result); 1215 | // else 1216 | // obj->entry = wrap(temp); 1217 | // } 1218 | // for(unsigned int i = 0;i < obj->entries->size();) 1219 | // { 1220 | // if(std::string(archive_entry_pathname(obj->entries->at(i))).compare(archive_entry_pathname(obj->entry)) == 0){ 1221 | // obj->entries->erase(obj->entries->begin()+i); 1222 | // obj->allbuff->erase(obj->allbuff->begin()+i); 1223 | // }else 1224 | // i++; 1225 | // } 1226 | // std::string strbuff; 1227 | // if(obj->fd < 0 and rb_respond_to(robj,rb_intern("read"))){ 1228 | // VALUE result = rb_funcall(robj,rb_intern("read"),0); 1229 | // strbuff.append(StringValueCStr(result), RSTRING_LEN(result)); 1230 | // }else{ 1231 | // while ((bytes_read = read(obj->fd, buff, sizeof(buff))) > 0) 1232 | // strbuff.append(buff, bytes_read); 1233 | // } 1234 | // obj->entries->push_back(obj->entry); 1235 | // obj->allbuff->push_back(strbuff); 1236 | // } 1237 | // return Qnil; 1238 | //} 1239 | 1240 | VALUE Archive_add_block(struct add_obj *obj ) 1241 | { 1242 | RubyArchive::read_data_from_path2(obj->obj,Qnil,*obj->data); 1243 | return Qnil; 1244 | } 1245 | 1246 | VALUE Archive_add_block_ensure(struct add_obj *obj ) 1247 | { 1248 | // if(!rb_obj_is_kind_of(obj->obj,rb_cHash)) 1249 | // archive_read_close(obj->file); 1250 | 1251 | size_t size = obj->data->size(); 1252 | for(unsigned int i=0; iarchive,obj->data->at(i).first); 1254 | archive_write_data(obj->archive,&obj->data->at(i).second[0],obj->data->at(i).second.length()); 1255 | archive_write_finish_entry(obj->archive); 1256 | } 1257 | archive_write_close(obj->archive); 1258 | return Qnil; 1259 | } 1260 | 1261 | /* 1262 | * call-seq: 1263 | * add( obj [, path] ) → self 1264 | * add( obj [, path] ) {|entry| } → self 1265 | * add( [obj, ... ] ) → self 1266 | * add( [obj, ... ] ) {|entry| } → self 1267 | * add( { path => obj} ) → self 1268 | * add( { path => obj} ) {|entry| } → self 1269 | * 1270 | * Adds a file to an archive. 1271 | * ===Parameters 1272 | * [obj] String, IO, File or an object which responds to +read+. 1273 | * [path] Sets the file name inside the archive. 1274 | * ===Return value 1275 | * self 1276 | * ===Raises 1277 | * [FormatError] Raised if the archive format is not supported for writing. 1278 | */ 1279 | 1280 | VALUE Archive_add(int argc, VALUE *argv, VALUE self)//(VALUE self,VALUE obj,VALUE name) 1281 | { 1282 | VALUE obj,name; 1283 | rb_scan_args(argc, argv, "11", &obj,&name); 1284 | if(NIL_P(name)){ 1285 | if(rb_obj_is_kind_of(obj,rb_cFile)) 1286 | name = rb_funcall(name,rb_intern("path"),0); 1287 | else if(rb_obj_is_kind_of(obj,rb_cIO)) 1288 | rb_scan_args(argc, argv, "20", &obj,&name); 1289 | else if(rb_respond_to(obj,rb_intern("read"))) 1290 | rb_scan_args(argc, argv, "20", &obj,&name); 1291 | else if(rb_obj_is_kind_of(obj,rb_cArray) or rb_obj_is_kind_of(obj,rb_cHash)) 1292 | rb_scan_args(argc, argv, "10", &obj); 1293 | else 1294 | name = obj; 1295 | } 1296 | 1297 | struct archive *a = archive_read_new(),*b=archive_write_new(); 1298 | 1299 | DataVector entries; 1300 | IntVector filters; 1301 | 1302 | 1303 | int format= ARCHIVE_FORMAT_EMPTY; 1304 | 1305 | 1306 | //autodetect format and compression 1307 | if(Archive_read_ruby(self,a)==ARCHIVE_OK){ 1308 | RubyArchive::read_old_data(a, entries, format, filters); 1309 | } 1310 | 1311 | // if(rb_obj_is_kind_of(obj,rb_cFile)){ 1312 | // VALUE pathname = rb_funcall(obj,rb_intern("path"),0); //source path 1313 | // VALUE obj2 = rb_file_s_expand_path(1,&pathname); 1314 | // path = StringValueCStr(obj2); 1315 | // fd = NUM2INT(rb_funcall(obj,rb_intern("fileno"),0)); 1316 | // }else if(rb_obj_is_kind_of(obj,rb_cIO)){ 1317 | // fd = NUM2INT(rb_funcall(obj,rb_intern("fileno"),0)); 1318 | // }else if(rb_respond_to(obj,rb_intern("read")) or rb_obj_is_kind_of(obj,rb_cArray) or rb_obj_is_kind_of(obj,rb_cHash)){ 1319 | // //stringio has neigther path or fileno, so do nothing 1320 | // }else { 1321 | // if(RTEST(rb_funcall(rb_cFile,rb_intern("directory?"),1,obj))) 1322 | // obj = rb_funcall(rb_cDir,rb_intern("glob"),1,rb_str_new2("**/**/*")); 1323 | // else{ 1324 | // VALUE obj2 = rb_file_s_expand_path(1,&obj); 1325 | // path = StringValueCStr(obj2); 1326 | // fd = open(path, O_RDONLY); 1327 | // if (fd < 0) //TODO: add error 1328 | // return self; 1329 | // } 1330 | // } 1331 | // Archive_format_from_path(self,format,compression); 1332 | 1333 | if(format == ARCHIVE_FORMAT_EMPTY){ 1334 | Archive_format_from_path(self,format,filters); 1335 | } 1336 | 1337 | if(Archive_write_ruby(self,b,format,filters)==ARCHIVE_OK){ 1338 | 1339 | add_obj temp; 1340 | temp.archive = b; 1341 | temp.obj = obj; 1342 | //temp.path = path; 1343 | temp.name = name; 1344 | temp.data = &entries; 1345 | RB_ENSURE(Archive_add_block,&temp,Archive_add_block_ensure,&temp); 1346 | } 1347 | 1348 | return self; 1349 | } 1350 | 1351 | /* 1352 | * call-seq: 1353 | * << obj → self 1354 | * 1355 | * Adds a file to an archive. Basicly the same as #add, but you can't 1356 | * set the path inside the archive. 1357 | * ===Parameters 1358 | * [obj] String or File 1359 | * ===Return value 1360 | * self 1361 | * ===Raises 1362 | * [FormatError] The archive format is not supported for writing. 1363 | */ 1364 | VALUE Archive_add_shift(VALUE self,VALUE name) 1365 | { 1366 | return Archive_add(1,&name,self); 1367 | } 1368 | 1369 | /* 1370 | * call-seq: 1371 | * delete( name ) → an_array 1372 | * 1373 | * Delete files from an archive. 1374 | * ===Parameters 1375 | * [name] An Archive::Entry, a String or a Regex. 1376 | * ===Return value 1377 | * A list of paths removed from the archive. 1378 | * ===Raises 1379 | * raise TypeError if the parameter is neigther String or File. 1380 | * raise Error if the format has no write support 1381 | */ 1382 | 1383 | VALUE Archive_delete(VALUE self,VALUE val) 1384 | { 1385 | struct archive *a = archive_read_new(),*b=archive_write_new(); 1386 | int format = ARCHIVE_FORMAT_EMPTY; 1387 | 1388 | DataVector entries; 1389 | IntVector filters; 1390 | 1391 | VALUE result = rb_ary_new(); 1392 | 1393 | //autodetect format and compression 1394 | if(Archive_read_ruby(self,a)==ARCHIVE_OK){ 1395 | RubyArchive::read_old_data(a, entries, format, filters); 1396 | 1397 | for(unsigned int i=0; idata->size(); i++){ 1423 | if(!RTEST(rb_yield(wrap(data->data->at(i).first)))){ 1424 | archive_write_header(data->archive,data->data->at(i).first); 1425 | archive_write_data(data->archive,&data->data->at(i).second[0],data->data->at(i).second.length()); 1426 | archive_write_finish_entry(data->archive); 1427 | }else 1428 | rb_ary_push(result,wrap(data->data->at(i).first)); 1429 | } 1430 | return result; 1431 | } 1432 | 1433 | /* 1434 | * call-seq: 1435 | * archive.delete_if {|entry| } -> self 1436 | * archive.delete_if -> Enumerator 1437 | * 1438 | * Yields each entry in the archive to the block and deletes those for which 1439 | * the block evaluates to a truth value. 1440 | * ===Parameters 1441 | * [name] An Archive::Entry, a String or a Regex. 1442 | * ===Return value 1443 | * If a block was given, returns self, otherwise an Enumerator. 1444 | * ===Raises 1445 | * raise Error if the format has no write support 1446 | */ 1447 | 1448 | VALUE Archive_delete_if(VALUE self) 1449 | { 1450 | RETURN_ENUMERATOR(self,0,NULL); 1451 | 1452 | struct archive *a = archive_read_new(),*b=archive_write_new(); 1453 | int format = ARCHIVE_FORMAT_EMPTY; 1454 | 1455 | DataVector entries; 1456 | IntVector filters; 1457 | 1458 | if(Archive_read_ruby(self,a)==ARCHIVE_OK){ 1459 | RubyArchive::read_old_data(a, entries, format, filters); 1460 | 1461 | if(Archive_write_ruby(self,b,format,filters)==ARCHIVE_OK){ 1462 | write_obj obj; 1463 | obj.archive = b; 1464 | obj.data = &entries; 1465 | return RB_ENSURE(Archive_delete_if_block,&obj,Archive_write_block_ensure,b); 1466 | } 1467 | } 1468 | return Qnil; 1469 | } 1470 | 1471 | /* 1472 | * call-seq: 1473 | * archive.clear -> self 1474 | * 1475 | * Deletes all files from an archive. 1476 | * ===Return value 1477 | * returns self. 1478 | * ===Raises 1479 | * raise Error if the format has no write support 1480 | */ 1481 | 1482 | VALUE Archive_clear(VALUE self) 1483 | { 1484 | 1485 | struct archive *a = archive_read_new(),*b=archive_write_new(); 1486 | struct archive_entry *entry; 1487 | int format = ARCHIVE_FORMAT_EMPTY; 1488 | 1489 | if(Archive_read_ruby(self,a)==ARCHIVE_OK){ 1490 | archive_read_next_header(a, &entry); 1491 | format = archive_format(a); 1492 | IntVector filters; 1493 | RubyArchive::read_get_filters(a,filters); 1494 | archive_read_close(a); 1495 | 1496 | if(Archive_write_ruby(self,b,format,filters)==ARCHIVE_OK) 1497 | archive_write_close(b); 1498 | } 1499 | 1500 | return self; 1501 | } 1502 | 1503 | /*:nodoc: 1504 | * call-seq: 1505 | * archive.exist? -> true or false 1506 | * 1507 | * Same as 1508 | * File.exist?(archive.path) 1509 | * . Checks wheather or not the archive file is existant. 1510 | * ===Return value 1511 | * True or false. 1512 | */ 1513 | 1514 | VALUE Archive_exist(VALUE self) 1515 | { 1516 | return rb_funcall(rb_cFile,rb_intern("exist?"),1,Archive_path(self)); 1517 | } 1518 | 1519 | /*:nodoc: 1520 | * call-seq: 1521 | * archive.unlink -> self 1522 | * 1523 | * call the File.unlink(path) 1524 | */ 1525 | 1526 | VALUE Archive_unlink(VALUE self) 1527 | { 1528 | return rb_funcall(rb_cFile,rb_intern("unlink"),1,Archive_path(self)); 1529 | return self; 1530 | } 1531 | 1532 | /*:nodoc: 1533 | * call-seq: 1534 | * archive.mtime -> Time 1535 | * 1536 | * call the File.mtime(path) 1537 | */ 1538 | 1539 | VALUE Archive_mtime(VALUE self) 1540 | { 1541 | return rb_funcall(rb_cFile,rb_intern("mtime"),1,Archive_path(self)); 1542 | } 1543 | 1544 | /*:nodoc: 1545 | * call-seq: 1546 | * archive.atime -> Time 1547 | * 1548 | * call the File.atime(path) 1549 | */ 1550 | 1551 | VALUE Archive_atime(VALUE self) 1552 | { 1553 | return rb_funcall(rb_cFile,rb_intern("atime"),1,Archive_path(self)); 1554 | } 1555 | 1556 | /*:nodoc: 1557 | * call-seq: 1558 | * archive.ctime -> Time 1559 | * 1560 | * call the File.ctime(path) 1561 | */ 1562 | 1563 | 1564 | VALUE Archive_ctime(VALUE self) 1565 | { 1566 | return rb_funcall(rb_cFile,rb_intern("ctime"),1,Archive_path(self)); 1567 | } 1568 | 1569 | /*:nodoc: 1570 | * call-seq: 1571 | * archive.stat -> File::Stat 1572 | * 1573 | * call the File.stat(path) 1574 | */ 1575 | 1576 | VALUE Archive_stat(VALUE self) 1577 | { 1578 | return rb_funcall(rb_cFile,rb_intern("stat"),1,Archive_path(self)); 1579 | } 1580 | 1581 | /* 1582 | * call-seq: 1583 | * archive.inspect -> String 1584 | * 1585 | * Human-readable description. 1586 | * ===Return value 1587 | * String 1588 | */ 1589 | 1590 | VALUE Archive_inspect(VALUE self) 1591 | { 1592 | VALUE array[3]; 1593 | switch(_self->type){ 1594 | case archive_path: 1595 | array[0]=rb_str_new2("#<%s:%s>"); 1596 | array[1]=rb_class_of(self); 1597 | array[2]=Archive_path(self); 1598 | break; 1599 | case archive_fd: 1600 | array[0]=rb_str_new2("#<%s:%d>"); 1601 | array[1]=rb_class_of(self); 1602 | array[2]=INT2NUM(_self->fd); 1603 | break; 1604 | case archive_buffer: 1605 | break; 1606 | case archive_ruby: 1607 | array[0]=rb_str_new2("#<%s:%s>"); 1608 | array[1]=rb_class_of(self); 1609 | array[2]=_self->ruby; 1610 | break; 1611 | } 1612 | return rb_f_sprintf(3,array); 1613 | } 1614 | 1615 | /* 1616 | * Document-class: Archive::Error 1617 | * This is the superclass of all errors specific to this library. 1618 | */ 1619 | 1620 | /* 1621 | * Document-class: Archive::Error::Compression 1622 | * This exception is thrown if you try to use an unknown compression format. 1623 | */ 1624 | 1625 | /* 1626 | * Document-class: Archive::Error::Format 1627 | * This exception is thrown if you try to use an unknown archive format or libarchive doesn't 1628 | * have write support for the format you wanted to write. 1629 | */ 1630 | 1631 | /* 1632 | * Document-class: Archive 1633 | * 1634 | * This class represents an archive file. The file may or may not exist, 1635 | * depending on wheather you want to create a new archive or read from 1636 | * an existing one. When instanciating this class, libarchive-ruby will 1637 | * automatically detect the correct file format for you using libarchive's 1638 | * own detection mechanism if the archive file is already present, otherwise 1639 | * by looking at the archive file's file extension. 1640 | */ 1641 | /* 1642 | * Document-const: EXTRACT_TIME 1643 | * 1644 | * extract the atime and mtime 1645 | */ 1646 | /* 1647 | * Document-const: EXTRACT_PERM 1648 | * 1649 | * extract the permission 1650 | */ 1651 | /* 1652 | * Document-const: EXTRACT_OWNER 1653 | * 1654 | * extract the owner 1655 | */ 1656 | 1657 | /* 1658 | * Document-const: EXTRACT_ACL 1659 | * 1660 | * extract the access control list 1661 | */ 1662 | /* 1663 | * Document-const: EXTRACT_FFLAGS 1664 | * 1665 | * extract the fflags 1666 | */ 1667 | /* 1668 | * Document-const: EXTRACT_XATTR 1669 | * 1670 | * extract the extended information 1671 | */ 1672 | extern "C" void Init_archive(void){ 1673 | rb_cArchive = rb_define_class("Archive",rb_cObject); 1674 | rb_define_alloc_func(rb_cArchive,Archive_alloc); 1675 | rb_define_method(rb_cArchive,"initialize",RUBY_METHOD_FUNC(Archive_initialize),-1); 1676 | rb_define_private_method(rb_cArchive,"initialize_copy",RUBY_METHOD_FUNC(Archive_initialize_copy),1); 1677 | rb_define_method(rb_cArchive,"path",RUBY_METHOD_FUNC(Archive_path),0); 1678 | 1679 | rb_define_method(rb_cArchive,"each",RUBY_METHOD_FUNC(Archive_each),0); 1680 | rb_define_method(rb_cArchive,"each_entry",RUBY_METHOD_FUNC(Archive_each_entry),0); 1681 | rb_define_method(rb_cArchive,"each_data",RUBY_METHOD_FUNC(Archive_each_data),0); 1682 | 1683 | rb_define_method(rb_cArchive,"each_filter",RUBY_METHOD_FUNC(Archive_each_filter),0); 1684 | 1685 | rb_define_method(rb_cArchive,"map!",RUBY_METHOD_FUNC(Archive_map_self),0); 1686 | rb_define_alias(rb_cArchive,"collect!","map!"); 1687 | rb_define_method(rb_cArchive,"[]",RUBY_METHOD_FUNC(Archive_get),1); 1688 | 1689 | rb_define_method(rb_cArchive,"to_hash",RUBY_METHOD_FUNC(Archive_to_hash),0); 1690 | 1691 | rb_define_method(rb_cArchive,"extract",RUBY_METHOD_FUNC(Archive_extract),-1); 1692 | rb_define_method(rb_cArchive,"extract_if",RUBY_METHOD_FUNC(Archive_extract_if),-1); 1693 | 1694 | rb_define_method(rb_cArchive,"delete",RUBY_METHOD_FUNC(Archive_delete),1); 1695 | rb_define_method(rb_cArchive,"delete_if",RUBY_METHOD_FUNC(Archive_delete_if),0); 1696 | rb_define_method(rb_cArchive,"clear",RUBY_METHOD_FUNC(Archive_clear),0); 1697 | 1698 | //rb_define_method(rb_cArchive,"move_to",RUBY_METHOD_FUNC(Archive_move_to),1); 1699 | 1700 | rb_define_method(rb_cArchive,"add",RUBY_METHOD_FUNC(Archive_add),-1); 1701 | rb_define_method(rb_cArchive,"<<",RUBY_METHOD_FUNC(Archive_add_shift),1); 1702 | 1703 | rb_define_method(rb_cArchive,"inspect",RUBY_METHOD_FUNC(Archive_inspect),0); 1704 | 1705 | rb_define_method(rb_cArchive,"format",RUBY_METHOD_FUNC(Archive_format),0); 1706 | //rb_define_method(rb_cArchive,"compression",RUBY_METHOD_FUNC(Archive_compression),0); 1707 | rb_define_method(rb_cArchive,"format_name",RUBY_METHOD_FUNC(Archive_format_name),0); 1708 | //rb_define_method(rb_cArchive,"compression_name",RUBY_METHOD_FUNC(Archive_compression_name),0); 1709 | 1710 | rb_define_method(rb_cArchive,"unlink",RUBY_METHOD_FUNC(Archive_unlink),0); 1711 | rb_define_method(rb_cArchive,"exist?",RUBY_METHOD_FUNC(Archive_exist),0); 1712 | rb_define_method(rb_cArchive,"mtime",RUBY_METHOD_FUNC(Archive_mtime),0); 1713 | rb_define_method(rb_cArchive,"atime",RUBY_METHOD_FUNC(Archive_atime),0); 1714 | rb_define_method(rb_cArchive,"ctime",RUBY_METHOD_FUNC(Archive_ctime),0); 1715 | rb_define_method(rb_cArchive,"stat",RUBY_METHOD_FUNC(Archive_stat),0); 1716 | 1717 | rb_include_module(rb_cArchive,rb_mEnumerable); 1718 | //rb_define_method(rb_cArchive,"size",RUBY_METHOD_FUNC(Archive_size),0); 1719 | 1720 | Init_archive_entry(rb_cArchive); 1721 | 1722 | rb_define_const(rb_cArchive,"EXTRACT_OWNER",INT2NUM(ARCHIVE_EXTRACT_OWNER)); 1723 | rb_define_const(rb_cArchive,"EXTRACT_PERM",INT2NUM(ARCHIVE_EXTRACT_PERM)); 1724 | rb_define_const(rb_cArchive,"EXTRACT_TIME",INT2NUM(ARCHIVE_EXTRACT_TIME)); 1725 | rb_define_const(rb_cArchive,"EXTRACT_NO_OVERWRITE",INT2NUM(ARCHIVE_EXTRACT_NO_OVERWRITE)); 1726 | rb_define_const(rb_cArchive,"EXTRACT_UNLINK",INT2NUM(ARCHIVE_EXTRACT_UNLINK)); 1727 | rb_define_const(rb_cArchive,"EXTRACT_ACL",INT2NUM(ARCHIVE_EXTRACT_ACL)); 1728 | rb_define_const(rb_cArchive,"EXTRACT_FFLAGS",INT2NUM(ARCHIVE_EXTRACT_FFLAGS)); 1729 | rb_define_const(rb_cArchive,"EXTRACT_XATTR",INT2NUM(ARCHIVE_EXTRACT_XATTR)); 1730 | rb_define_const(rb_cArchive,"EXTRACT_SECURE_SYMLINKS",INT2NUM(ARCHIVE_EXTRACT_SECURE_SYMLINKS)); 1731 | rb_define_const(rb_cArchive,"EXTRACT_SECURE_NODOTDOT",INT2NUM(ARCHIVE_EXTRACT_SECURE_NODOTDOT)); 1732 | rb_define_const(rb_cArchive,"EXTRACT_NO_AUTODIR",INT2NUM(ARCHIVE_EXTRACT_NO_AUTODIR)); 1733 | rb_define_const(rb_cArchive,"EXTRACT_NO_OVERWRITE_NEWER",INT2NUM(ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER)); 1734 | rb_define_const(rb_cArchive,"EXTRACT_SPARSE",INT2NUM(ARCHIVE_EXTRACT_SPARSE)); 1735 | rb_define_const(rb_cArchive,"EXTRACT_MAC_METADATA",INT2NUM(ARCHIVE_EXTRACT_MAC_METADATA)); 1736 | 1737 | 1738 | rb_eArchiveError = rb_define_class_under(rb_cArchive,"Error",rb_eStandardError); 1739 | rb_eArchiveErrorFormat = rb_define_class_under(rb_eArchiveError,"Format",rb_eArchiveError); 1740 | rb_eArchiveErrorCompression = rb_define_class_under(rb_eArchiveError,"Compression",rb_eArchiveError); 1741 | 1742 | 1743 | SymToFormat[rb_intern("tar")]=ARCHIVE_FORMAT_TAR_GNUTAR; 1744 | SymToFormat[rb_intern("pax")]=ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; 1745 | SymToFormat[rb_intern("zip")]=ARCHIVE_FORMAT_ZIP; 1746 | SymToFormat[rb_intern("ar")]=ARCHIVE_FORMAT_AR_GNU; 1747 | SymToFormat[rb_intern("xar")]=ARCHIVE_FORMAT_XAR; 1748 | SymToFormat[rb_intern("lha")]=ARCHIVE_FORMAT_LHA; 1749 | SymToFormat[rb_intern("cab")]=ARCHIVE_FORMAT_CAB; 1750 | SymToFormat[rb_intern("rar")]=ARCHIVE_FORMAT_RAR; 1751 | SymToFormat[rb_intern("7zip")]=ARCHIVE_FORMAT_7ZIP; 1752 | 1753 | 1754 | SymToFilter[rb_intern("gzip")]=ARCHIVE_FILTER_GZIP; 1755 | SymToFilter[rb_intern("bzip2")]=ARCHIVE_FILTER_BZIP2; 1756 | SymToFilter[rb_intern("compress")]=ARCHIVE_FILTER_BZIP2; 1757 | SymToFilter[rb_intern("lzma")]=ARCHIVE_FILTER_LZMA; 1758 | SymToFilter[rb_intern("xz")]=ARCHIVE_FILTER_XZ; 1759 | SymToFilter[rb_intern("uu")]=ARCHIVE_FILTER_UU; 1760 | SymToFilter[rb_intern("rpm")]=ARCHIVE_FILTER_RPM; 1761 | SymToFilter[rb_intern("lzip")]=ARCHIVE_FILTER_LZIP; 1762 | 1763 | #ifdef ARCHIVE_FILTER_LZOP 1764 | SymToFilter[rb_intern("lzop")]=ARCHIVE_FILTER_LZOP; 1765 | #endif 1766 | #ifdef ARCHIVE_FILTER_GRZIP 1767 | SymToFilter[rb_intern("grzip")]=ARCHIVE_FILTER_GRZIP; 1768 | #endif 1769 | 1770 | 1771 | FileExtType::mapped_type pair; 1772 | 1773 | pair = std::make_pair(ARCHIVE_FORMAT_TAR,ARCHIVE_FILTER_GZIP); 1774 | fileExt[".tar.gz"]=pair; 1775 | fileExt[".tgz"]=pair; 1776 | 1777 | pair = std::make_pair(ARCHIVE_FORMAT_TAR,ARCHIVE_FILTER_BZIP2); 1778 | fileExt[".tar.bz2"]=pair; 1779 | fileExt[".tbz"]=pair; 1780 | fileExt[".tb2"]=pair; 1781 | 1782 | pair = std::make_pair(ARCHIVE_FORMAT_TAR,ARCHIVE_FILTER_XZ); 1783 | fileExt[".tar.xz"]=pair; 1784 | fileExt[".txz"]=pair; 1785 | 1786 | fileExt[".tar.lzma"]=std::make_pair(ARCHIVE_FORMAT_TAR,ARCHIVE_FILTER_LZMA); 1787 | 1788 | fileExt[".tar"]=std::make_pair(ARCHIVE_FORMAT_TAR,ARCHIVE_FILTER_NONE); 1789 | 1790 | 1791 | DataVector data; 1792 | // //RubyArchive::read_data_from_path2(rb_reg_new(".",8,0),Qnil,data); 1793 | RubyArchive::read_data_from_path2(rb_str_new2("*/*.cpp"),Qnil,data); 1794 | rb_warn("%lu",data.size()); 1795 | //// 1796 | for(size_t i = 0; i < data.size(); ++i) 1797 | { 1798 | rb_warn("%s",archive_entry_pathname(data[i].first)); 1799 | rb_warn("%lu",data[i].second.size()); 1800 | } 1801 | } 1802 | -------------------------------------------------------------------------------- /ext/entry.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | This file is part of libarchive-ruby. 3 | 4 | libarchive-ruby is a Ruby binding for the C library libarchive. 5 | 6 | Copyright (C) 2011 Hans Mackowiak 7 | 8 | libarchive-ruby is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | libarchive-ruby is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with libarchive-ruby; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | ****************************************************************************/ 22 | 23 | #include "main.hpp" 24 | 25 | #define _self wrap(self) 26 | 27 | VALUE rb_cArchiveEntry; 28 | 29 | 30 | 31 | void rb_define_attr_method(VALUE klass,const std::string &name, VALUE get(VALUE),VALUE set(VALUE,VALUE)) 32 | { 33 | rb_define_method(klass,name.c_str(),RUBY_METHOD_FUNC(get),0); 34 | rb_define_method(klass,(name + "=").c_str(),RUBY_METHOD_FUNC(set),1); 35 | } 36 | 37 | 38 | #define macro_attr(attr,get,set) \ 39 | VALUE _get_##attr(VALUE self) { return get(archive_entry_##attr(_self)); } \ 40 | VALUE _set_##attr(VALUE self,VALUE val) { archive_entry_set_##attr(_self,set(val));return val; } 41 | 42 | #define is_type(type,flag) \ 43 | VALUE _is_##type(VALUE self) { return archive_entry_filetype(_self) == flag ? Qtrue : Qfalse; } 44 | 45 | 46 | 47 | namespace ArchiveEntry { 48 | 49 | VALUE _alloc(VALUE self) 50 | { 51 | return wrap(archive_entry_new()); 52 | } 53 | 54 | VALUE _initialize_copy(VALUE self,VALUE source) 55 | { 56 | rarchive_entry *file; 57 | Data_Get_Struct( self, rarchive_entry, file); 58 | file->entry = archive_entry_clone(wrap(source)); 59 | return self; 60 | } 61 | 62 | 63 | macro_attr(dev,INT2NUM,NUM2INT) 64 | macro_attr(devminor,INT2NUM,NUM2INT) 65 | macro_attr(devmajor,INT2NUM,NUM2INT) 66 | 67 | macro_attr(rdev,INT2NUM,NUM2INT) 68 | macro_attr(rdevminor,INT2NUM,NUM2INT) 69 | macro_attr(rdevmajor,INT2NUM,NUM2INT) 70 | 71 | macro_attr(uid,INT2NUM,NUM2INT) 72 | macro_attr(gid,INT2NUM,NUM2INT) 73 | 74 | macro_attr(uname,wrap,wrap) 75 | macro_attr(gname,wrap,wrap) 76 | 77 | macro_attr(pathname,wrap,wrap) 78 | macro_attr(symlink,wrap,wrap) 79 | macro_attr(hardlink,wrap,wrap) 80 | //macro_attr(sourcepath,wrap,wrap) 81 | 82 | is_type(file,AE_IFREG) 83 | is_type(symlink,AE_IFLNK) 84 | is_type(directory,AE_IFDIR) 85 | is_type(chardev,AE_IFCHR) 86 | is_type(blockdev,AE_IFBLK) 87 | is_type(pipe,AE_IFIFO) 88 | is_type(socket,AE_IFSOCK) 89 | 90 | 91 | 92 | 93 | VALUE _get_atime(VALUE self) 94 | { 95 | if(archive_entry_atime_is_set(_self)) 96 | return rb_time_new(archive_entry_atime(_self),archive_entry_atime_nsec(_self)); 97 | else 98 | return Qnil; 99 | } 100 | 101 | VALUE _get_ctime(VALUE self) 102 | { 103 | if(archive_entry_ctime_is_set(_self)) 104 | return rb_time_new(archive_entry_ctime(_self),archive_entry_ctime_nsec(_self)); 105 | else 106 | return Qnil; 107 | } 108 | 109 | VALUE _get_mtime(VALUE self) 110 | { 111 | if(archive_entry_mtime_is_set(_self)) 112 | return rb_time_new(archive_entry_mtime(_self),archive_entry_mtime_nsec(_self)); 113 | else 114 | return Qnil; 115 | } 116 | 117 | VALUE _get_birthtime(VALUE self) 118 | { 119 | if(archive_entry_birthtime_is_set(_self)) 120 | return rb_time_new(archive_entry_birthtime(_self),archive_entry_birthtime_nsec(_self)); 121 | else 122 | return Qnil; 123 | } 124 | 125 | VALUE _set_atime(VALUE self,VALUE value) 126 | { 127 | if(NIL_P(value)) 128 | archive_entry_unset_atime(_self); 129 | else 130 | archive_entry_set_atime(_self,NUM2INT(rb_funcall(value,rb_intern("to_i"),0)),NUM2INT(rb_funcall(value,rb_intern("usec"),0))); 131 | return value; 132 | } 133 | 134 | VALUE _set_ctime(VALUE self,VALUE value) 135 | { 136 | if(NIL_P(value)) 137 | archive_entry_unset_ctime(_self); 138 | else 139 | archive_entry_set_ctime(_self,NUM2INT(rb_funcall(value,rb_intern("to_i"),0)),NUM2INT(rb_funcall(value,rb_intern("usec"),0))); 140 | return value; 141 | } 142 | 143 | VALUE _set_mtime(VALUE self,VALUE value) 144 | { 145 | if(NIL_P(value)) 146 | archive_entry_unset_mtime(_self); 147 | else 148 | archive_entry_set_mtime(_self,NUM2INT(rb_funcall(value,rb_intern("to_i"),0)),NUM2INT(rb_funcall(value,rb_intern("usec"),0))); 149 | return value; 150 | } 151 | 152 | VALUE _set_birthtime(VALUE self,VALUE value) 153 | { 154 | if(NIL_P(value)) 155 | archive_entry_unset_birthtime(_self); 156 | else 157 | archive_entry_set_birthtime(_self,NUM2INT(rb_funcall(value,rb_intern("to_i"),0)),NUM2INT(rb_funcall(value,rb_intern("usec"),0))); 158 | return value; 159 | } 160 | 161 | 162 | /* 163 | * call-seq: 164 | * entry <=> other -> -1,0,1 or nil 165 | * 166 | * compares two entries 167 | */ 168 | VALUE _compare(VALUE self,VALUE other) 169 | { 170 | if(rb_obj_is_kind_of(other,rb_cArchiveEntry) != Qtrue) 171 | return Qnil; 172 | else { 173 | return rb_funcall(_get_mtime(self),rb_intern("<=>"),1,_get_mtime(other)); 174 | } 175 | } 176 | 177 | 178 | /* 179 | * call-seq: 180 | * entry.inspect -> String 181 | * 182 | * returns readable string. 183 | */ 184 | VALUE _inspect(VALUE self){ 185 | VALUE array[3]; 186 | array[0]=rb_str_new2("#<%s:%s>"); 187 | array[1]=rb_class_of(self); 188 | array[2]=_get_pathname(self); 189 | return rb_f_sprintf(3,array); 190 | } 191 | 192 | } 193 | 194 | //__LA_DECL __LA_MODE_T archive_entry_filetype(struct archive_entry *); 195 | 196 | 197 | 198 | /* 199 | * call-seq: 200 | * entry.sourcepath -> String or nil 201 | * 202 | * returns the hardlink 203 | */ 204 | VALUE ArchiveEntry_sourcepath(VALUE self) 205 | { 206 | return wrap(archive_entry_sourcepath(_self)); 207 | } 208 | 209 | /* 210 | * call-seq: 211 | * entry.strmode -> String or nil 212 | * 213 | * returns the mode as string 214 | */ 215 | VALUE ArchiveEntry_strmode(VALUE self) 216 | { 217 | return wrap(archive_entry_strmode(_self)); 218 | } 219 | 220 | //ACL added later with acl gem 221 | 222 | VALUE ArchiveEntry_access_acl(VALUE self){ 223 | if(rb_const_defined(rb_cObject,rb_intern("ACL"))){ 224 | VALUE rb_cAcl = rb_const_get(rb_cObject,rb_intern("ACL")); 225 | VALUE rb_cAclEntry = rb_const_get(rb_cObject,rb_intern("Entry")); 226 | VALUE result = rb_class_new_instance(0,NULL,rb_cAcl); 227 | archive_entry_acl_reset(_self,ARCHIVE_ENTRY_ACL_TYPE_ACCESS); 228 | int type,permset,tag,qual; 229 | const char* name; 230 | while(archive_entry_acl_next(_self, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,&type, &permset, &tag,&qual, &name) == 0){ 231 | VALUE entry; 232 | VALUE temp[3]; 233 | switch(tag){ 234 | case ARCHIVE_ENTRY_ACL_USER: 235 | case ARCHIVE_ENTRY_ACL_USER_OBJ: 236 | temp[0] = ID2SYM(rb_intern("user")); 237 | break; 238 | case ARCHIVE_ENTRY_ACL_GROUP: 239 | case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 240 | temp[0] = ID2SYM(rb_intern("group")); 241 | break; 242 | case ARCHIVE_ENTRY_ACL_MASK: 243 | temp[0] = ID2SYM(rb_intern("mask")); 244 | break; 245 | case ARCHIVE_ENTRY_ACL_OTHER: 246 | temp[0] = ID2SYM(rb_intern("other")); 247 | break; 248 | } 249 | temp[1] = INT2NUM(permset); 250 | switch(tag){ 251 | case ARCHIVE_ENTRY_ACL_USER: 252 | case ARCHIVE_ENTRY_ACL_GROUP: 253 | temp[2] = INT2NUM(qual); 254 | entry=rb_class_new_instance(3,temp,rb_cAclEntry); 255 | break; 256 | default: 257 | entry=rb_class_new_instance(2,temp,rb_cAclEntry); 258 | break; 259 | } 260 | rb_funcall(result,rb_intern("<<"),1,entry); 261 | } 262 | return result; 263 | }else 264 | rb_raise(rb_eNotImpError,"this function require the libacl-ruby gem!"); 265 | return Qnil; 266 | } 267 | 268 | VALUE ArchiveEntry_acl_add(VALUE self){ 269 | archive_entry_acl_add_entry(_self,ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_GROUP, 270 | 101, "abc"); 271 | return self; 272 | } 273 | 274 | 275 | 276 | /* Document-attr: atime 277 | * 278 | * Encapsulate the writing and reading of the configuration 279 | * file. ... 280 | */ 281 | /* Document-attr: ctime 282 | * 283 | * Encapsulate the writing and reading of the configuration 284 | * file. ... 285 | */ 286 | /* Document-attr: mtime 287 | * 288 | * Encapsulate the writing and reading of the configuration 289 | * file. ... 290 | */ 291 | 292 | void Init_archive_entry(VALUE rb_cArchive){ 293 | #if 0 294 | rb_cArchive = rb_define_class("Archive",rb_cObject); 295 | 296 | rb_define_attr(rb_cArchiveEntry,"path",1,1); 297 | rb_define_attr(rb_cArchiveEntry,"symlink",1,1); 298 | rb_define_attr(rb_cArchiveEntry,"hardlink",1,1); 299 | 300 | rb_define_attr(rb_cArchiveEntry,"uid",1,1); 301 | rb_define_attr(rb_cArchiveEntry,"uname",1,1); 302 | rb_define_attr(rb_cArchiveEntry,"gid",1,1); 303 | rb_define_attr(rb_cArchiveEntry,"gname",1,1); 304 | 305 | rb_define_attr(rb_cArchiveEntry,"atime",1,1); 306 | rb_define_attr(rb_cArchiveEntry,"ctime",1,1); 307 | rb_define_attr(rb_cArchiveEntry,"mtime",1,1); 308 | rb_define_attr(rb_cArchiveEntry,"birthtime",1,1); 309 | 310 | rb_define_attr(rb_cArchiveEntry,"dev",1,1); 311 | rb_define_attr(rb_cArchiveEntry,"devmajor",1,1); 312 | rb_define_attr(rb_cArchiveEntry,"devminor",1,1); 313 | 314 | rb_define_attr(rb_cArchiveEntry,"rdev",1,1); 315 | rb_define_attr(rb_cArchiveEntry,"rdevmajor",1,1); 316 | rb_define_attr(rb_cArchiveEntry,"rdevminor",1,1); 317 | 318 | 319 | #endif 320 | 321 | using namespace ArchiveEntry; 322 | rb_cArchiveEntry = rb_define_class_under(rb_cArchive,"Entry",rb_cObject); 323 | rb_define_alloc_func(rb_cArchiveEntry,_alloc); 324 | rb_define_private_method(rb_cArchiveEntry,"initialize_copy",RUBY_METHOD_FUNC(_initialize_copy),1); 325 | rb_define_method(rb_cArchiveEntry,"inspect",RUBY_METHOD_FUNC(_inspect),0); 326 | 327 | rb_define_attr_method(rb_cArchiveEntry,"path",_get_pathname,_set_pathname); 328 | rb_define_attr_method(rb_cArchiveEntry,"symlink",_get_symlink,_set_symlink); 329 | rb_define_attr_method(rb_cArchiveEntry,"hardlink",_get_hardlink,_set_hardlink); 330 | 331 | rb_define_method(rb_cArchiveEntry,"sourcepath",RUBY_METHOD_FUNC(ArchiveEntry_sourcepath),0); 332 | 333 | rb_define_attr_method(rb_cArchiveEntry,"uid",_get_uid,_set_uid); 334 | rb_define_attr_method(rb_cArchiveEntry,"gid",_get_gid,_set_gid); 335 | rb_define_attr_method(rb_cArchiveEntry,"uname",_get_uname,_set_uname); 336 | rb_define_attr_method(rb_cArchiveEntry,"gname",_get_gname,_set_gname); 337 | 338 | rb_define_attr_method(rb_cArchiveEntry,"atime",_get_atime,_set_atime); 339 | rb_define_attr_method(rb_cArchiveEntry,"ctime",_get_ctime,_set_ctime); 340 | rb_define_attr_method(rb_cArchiveEntry,"mtime",_get_mtime,_set_mtime); 341 | rb_define_attr_method(rb_cArchiveEntry,"birthtime",_get_birthtime,_set_birthtime); 342 | 343 | rb_define_attr_method(rb_cArchiveEntry,"dev",_get_dev,_set_dev); 344 | rb_define_attr_method(rb_cArchiveEntry,"dev_major",_get_devmajor,_set_devmajor); 345 | rb_define_attr_method(rb_cArchiveEntry,"dev_minor",_get_devminor,_set_devminor); 346 | 347 | rb_define_attr_method(rb_cArchiveEntry,"rdev",_get_rdev,_set_rdev); 348 | rb_define_attr_method(rb_cArchiveEntry,"rdev_major",_get_rdevmajor,_set_rdevmajor); 349 | rb_define_attr_method(rb_cArchiveEntry,"rdev_minor",_get_rdevminor,_set_rdevminor); 350 | 351 | rb_define_method(rb_cArchiveEntry,"file?",RUBY_METHOD_FUNC(_is_file),0); 352 | rb_define_method(rb_cArchiveEntry,"directory?",RUBY_METHOD_FUNC(_is_directory),0); 353 | rb_define_method(rb_cArchiveEntry,"chardev?",RUBY_METHOD_FUNC(_is_chardev),0); 354 | rb_define_method(rb_cArchiveEntry,"blockdev?",RUBY_METHOD_FUNC(_is_blockdev),0); 355 | rb_define_method(rb_cArchiveEntry,"symlink?",RUBY_METHOD_FUNC(_is_symlink),0); 356 | rb_define_method(rb_cArchiveEntry,"pipe?",RUBY_METHOD_FUNC(_is_pipe),0); 357 | rb_define_method(rb_cArchiveEntry,"socket?",RUBY_METHOD_FUNC(_is_socket),0); 358 | 359 | 360 | rb_include_module(rb_cArchiveEntry,rb_mComparable); 361 | rb_define_method(rb_cArchiveEntry,"<=>",RUBY_METHOD_FUNC(_compare),1); 362 | 363 | rb_define_alias(rb_cArchiveEntry,"to_s","path"); 364 | //* 365 | rb_define_method(rb_cArchiveEntry,"access_acl",RUBY_METHOD_FUNC(ArchiveEntry_access_acl),0); 366 | //*/ 367 | 368 | 369 | } 370 | -------------------------------------------------------------------------------- /ext/extconf.rb: -------------------------------------------------------------------------------- 1 | #Encoding: UTF-8 2 | =begin 3 | This file is part of libarchive-ruby. 4 | 5 | libarchive-ruby is a Ruby binding for the C library libarchive. 6 | 7 | Copyright © 2011 Hans Mackowiak 8 | 9 | libarchive-ruby is free software; you can redistribute it and/or modify 10 | it under the terms of the GNU General Public License as published by 11 | the Free Software Foundation; either version 2 of the License, or 12 | (at your option) any later version. 13 | 14 | libarchive-ruby is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU General Public License for more details. 18 | 19 | You should have received a copy of the GNU General Public License along 20 | with libarchive-ruby; if not, write to the Free Software Foundation, Inc., 21 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 22 | =end 23 | 24 | require 'mkmf' 25 | 26 | 27 | dir_config("archive") 28 | 29 | pkg_config("libarchive") 30 | unless(find_library("archive","main") && find_header("archive.h")) 31 | abort("libarchive dev files") 32 | end 33 | CONFIG["warnflags"] = RbConfig::CONFIG["warnflags"] = " -Wall" 34 | 35 | unless have_func("rb_string_value_cstr","ruby.h") 36 | abort("missing VALUE to char* convert!") 37 | end 38 | unless have_macro("RETURN_ENUMERATOR","ruby.h") 39 | abort("missing the return Enumerator macro.") 40 | end 41 | have_func("rb_proc_arity","ruby.h") 42 | have_func("archive_read_support_format_raw","archive.h") 43 | 44 | $CFLAGS += "-x c++ -Wall" 45 | 46 | 47 | create_header 48 | 49 | create_makefile("archive") 50 | -------------------------------------------------------------------------------- /ext/main.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | This file is part of libarchive-ruby. 3 | 4 | libarchive-ruby is a Ruby binding for the C library libarchive. 5 | 6 | Copyright (C) 2011,2012,2013 Hans Mackowiak 7 | 8 | libarchive-ruby is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | libarchive-ruby is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with libarchive-ruby; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | ****************************************************************************/ 22 | 23 | #include "main.hpp" 24 | 25 | template <> 26 | VALUE wrap< rarchive >(rarchive *file ) 27 | { 28 | return Data_Wrap_Struct(rb_cArchive, NULL, free, file); 29 | } 30 | 31 | 32 | template <> 33 | rarchive* wrap< rarchive* >(const VALUE &vfile) 34 | { 35 | if ( ! rb_obj_is_kind_of(vfile, rb_cArchive) ) 36 | return NULL; 37 | rarchive *file; 38 | Data_Get_Struct( vfile, rarchive, file); 39 | return file; 40 | } 41 | template <> 42 | VALUE wrap< archive_entry >(struct archive_entry *entry ) 43 | { 44 | rarchive_entry *temp = new rarchive_entry; 45 | //archive_entry other = archive_entry_clone(entry); 46 | temp->entry = archive_entry_clone(entry); 47 | return Data_Wrap_Struct(rb_cArchiveEntry, NULL, free, temp); 48 | } 49 | 50 | template <> 51 | archive_entry* wrap< archive_entry* >(const VALUE &vfile) 52 | { 53 | if ( ! rb_obj_is_kind_of(vfile, rb_cArchiveEntry) ) 54 | return NULL; 55 | rarchive_entry *file; 56 | Data_Get_Struct( vfile, rarchive_entry, file); 57 | return file->entry; 58 | } 59 | template <> 60 | VALUE wrap< const char >(const char *str ) 61 | { 62 | return str == NULL? Qnil : rb_str_new2(str); 63 | } 64 | template <> 65 | const char* wrap< const char* >(const VALUE &vfile) 66 | { 67 | return NIL_P(vfile) ? NULL : StringValueCStr((volatile VALUE&)vfile); 68 | } 69 | -------------------------------------------------------------------------------- /ext/main.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | This file is part of libarchive-ruby. 3 | 4 | libarchive-ruby is a Ruby binding for the C library libarchive. 5 | 6 | Copyright (C) 2011 Hans Mackowiak 7 | 8 | libarchive-ruby is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | libarchive-ruby is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with libarchive-ruby; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | ****************************************************************************/ 22 | 23 | #ifndef __RubyAchiveMain_H__ 24 | #define __RubyAchiveMain_H__ 25 | #include 26 | #if HAVE_RUBY_ENCODING_H 27 | #include 28 | #endif 29 | #include 30 | #include 31 | #include 32 | #include 33 | extern VALUE rb_cArchive,rb_cArchiveEntry; 34 | 35 | void Init_archive_entry(VALUE m); 36 | 37 | template 38 | VALUE wrap(T *arg){ return Qnil;}; 39 | template 40 | T wrap(const VALUE &arg){}; 41 | 42 | enum archive_type {archive_path,archive_fd,archive_buffer,archive_ruby}; 43 | 44 | struct rarchive{ 45 | //union drumrum? 46 | std::string path; 47 | VALUE ruby; 48 | std::string buffer; 49 | int fd; 50 | archive_type type; 51 | int format; 52 | int filter; 53 | }; 54 | 55 | struct rarchive_entry{ 56 | archive_entry *entry; 57 | }; 58 | 59 | template <> 60 | VALUE wrap< rarchive >(rarchive *file ); 61 | 62 | 63 | template <> 64 | rarchive* wrap< rarchive* >(const VALUE &vfile); 65 | 66 | template <> 67 | VALUE wrap< archive_entry >(struct archive_entry *entry ); 68 | 69 | template <> 70 | archive_entry* wrap< archive_entry* >(const VALUE &vfile); 71 | 72 | template <> 73 | VALUE wrap< const char >(const char *str ); 74 | 75 | template <> 76 | const char* wrap< const char* >(const VALUE &vfile); 77 | 78 | #endif /* __RubyAchiveMain_H__ */ 79 | 80 | -------------------------------------------------------------------------------- /test/neue.tar.bz2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hanmac/libarchive-ruby/0e051ae6c0dac2192abb5f5e04ae7b54f3f47062/test/neue.tar.bz2 -------------------------------------------------------------------------------- /test/test_tararchive.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | #Encoding: UTF-8 3 | =begin 4 | This file is part of libarchive-ruby. 5 | 6 | libarchive-ruby is a Ruby binding for the C library libarchive. 7 | 8 | Copyright © 2011 Hans Mackowiak 9 | 10 | libarchive-ruby is free software; you can redistribute it and/or modify 11 | it under the terms of the GNU General Public License as published by 12 | the Free Software Foundation; either version 2 of the License, or 13 | (at your option) any later version. 14 | 15 | libarchive-ruby is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU General Public License for more details. 19 | 20 | You should have received a copy of the GNU General Public License along 21 | with libarchive-ruby; if not, write to the Free Software Foundation, Inc., 22 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 23 | =end 24 | 25 | gem "test-unit", ">= 2.1" #Ensure we use the gem 26 | require "test/unit" 27 | require_relative File.join("..","ext","archive") 28 | 29 | class Test_TarArchive < Test::Unit::TestCase 30 | 31 | class << self 32 | def startup 33 | #Dir.chdir("test") 34 | path = "neue/neue Datei" 35 | archive = Archive.new(File.join("test","neue.tar.bz2")) 36 | archive.extract(path,extract: Archive::EXTRACT_TIME | Archive::EXTRACT_OWNER) 37 | end 38 | 39 | def shutdown 40 | #Dir.chdir("..") 41 | end 42 | end 43 | def test_format 44 | a = Archive.new(File.join("test","neue.tar.bz2")) 45 | assert_equal(a.format_name,"GNU tar format") 46 | end 47 | def test_compression 48 | a = Archive.new(File.join("test","neue.tar.bz2")) 49 | assert_equal(a.compression_name,"bzip2") 50 | end 51 | 52 | def test_mtime 53 | path = "neue/neue Datei" 54 | archive = Archive.new(File.join("test","neue.tar.bz2")) 55 | assert_equal(archive.find {|e| e.path == path}.mtime,File.mtime(path)) 56 | end 57 | 58 | def test_atime 59 | path = "neue/neue Datei" 60 | archive = Archive.new(File.join("test","neue.tar.bz2")) 61 | assert_equal(archive.find {|e| e.path == path}.atime,File.atime(path)) 62 | end 63 | 64 | def test_ctime 65 | path = "neue/neue Datei" 66 | archive = Archive.new(File.join("test","neue.tar.bz2")) 67 | assert_not_equal(archive.find {|e| e.path == path}.ctime,File.ctime(path)) 68 | end 69 | 70 | def test_uid 71 | path = "neue/neue Datei" 72 | archive = Archive.new(File.join("test","neue.tar.bz2")) 73 | assert_equal(archive.find {|e| e.path == path}.uid,File.stat(path).uid) 74 | end 75 | 76 | def test_gid 77 | path = "neue/neue Datei" 78 | archive = Archive.new(File.join("test","neue.tar.bz2")) 79 | assert_equal(archive.find {|e| e.path == path}.gid,File.stat(path).gid) 80 | end 81 | 82 | end 83 | --------------------------------------------------------------------------------