libarchive-ruby 0.0.1.dev

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (7) hide show
  1. data/COPYING.txt +339 -0
  2. data/README.rdoc +135 -0
  3. data/ext/archive.cpp +1547 -0
  4. data/ext/entry.cpp +592 -0
  5. data/ext/extconf.rb +38 -0
  6. data/ext/main.hpp +86 -0
  7. metadata +80 -0
data/ext/archive.cpp ADDED
@@ -0,0 +1,1547 @@
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 <vector>
25
+ #include <exception>
26
+ #include <stdexcept>
27
+ #define _self wrap<rarchive*>(self)
28
+ #define RB_ENSURE(func1,obj1,func2,obj2)\
29
+ rb_ensure(RUBY_METHOD_FUNC(func1),(VALUE)obj1,RUBY_METHOD_FUNC(func2),(VALUE)obj2)
30
+
31
+ //TODO: handle string IO as archive Archive.new(stringio)
32
+
33
+
34
+ VALUE rb_cArchive,rb_eArchiveError,rb_eArchiveErrorFormat,rb_eArchiveErrorCompression;
35
+
36
+ VALUE Archive_alloc(VALUE self)
37
+ {
38
+ rarchive *result = new rarchive;
39
+ return wrap(result);
40
+ }
41
+
42
+ VALUE Archive_initialize_copy(VALUE self, VALUE other)
43
+ {
44
+ VALUE result = rb_call_super(1,&other);
45
+ _self->path = std::string(wrap<rarchive*>(other)->path.c_str());
46
+ _self->type = wrap<rarchive*>(other)->type;
47
+ return result;
48
+ }
49
+
50
+ struct extract_obj {
51
+ struct archive* archive;
52
+ int extract_opt;
53
+ };
54
+
55
+ struct write_obj {
56
+ struct archive* archive;
57
+ std::vector<struct archive_entry *> *entries;
58
+ std::vector<std::string> *allbuff;
59
+ };
60
+
61
+
62
+ VALUE Archive_read_block_ensure(struct archive * data)
63
+ {
64
+ archive_read_finish(data);
65
+ return Qnil;
66
+ }
67
+
68
+ VALUE Archive_write_block_ensure(struct archive * data)
69
+ {
70
+ archive_write_finish(data);
71
+ return Qnil;
72
+ }
73
+
74
+ int rubymyopen(struct archive *a, void *client_data)
75
+ {
76
+ rb_funcall((VALUE)client_data,rb_intern("rewind"),0);
77
+ return ARCHIVE_OK;
78
+ }
79
+ ssize_t
80
+ rubymyread(struct archive *a, void *client_data, const void **buff)
81
+ {
82
+ VALUE result = rb_funcall((VALUE)client_data,rb_intern("read"),0);
83
+ if(result == Qnil){
84
+ *buff = NULL;
85
+ return 0;
86
+ }else{
87
+ if (RSTRING_LEN(result))
88
+ *buff = RSTRING_PTR(result);
89
+ else
90
+ *buff = NULL;
91
+ return RSTRING_LEN(result);
92
+ }
93
+ }
94
+
95
+ ssize_t rubymywrite(struct archive *a,void *client_data,const void *buffer, size_t length){
96
+ VALUE string = rb_str_new((char*)buffer,length);
97
+ // rb_funcall(string,rb_intern("rstrip!"),0);
98
+ return NUM2INT(rb_funcall((VALUE)client_data,rb_intern("write"),1,string));
99
+ }
100
+
101
+ int rubymyclose(struct archive *a, void *client_data)
102
+ {
103
+ rb_funcall((VALUE)client_data,rb_intern("rewind"),0);
104
+ return ARCHIVE_OK;
105
+ }
106
+
107
+
108
+ void Archive_format_from_path(VALUE self,int &format,int &compression)
109
+ {
110
+ if(format == ARCHIVE_FORMAT_EMPTY){
111
+ if(_self->type == archive_path){
112
+ std::string selfpath = _self->path;
113
+ if(selfpath.substr(selfpath.length()-7).compare(".tar.xz")==0 || selfpath.substr(selfpath.length()-4).compare(".txz")==0 ){
114
+ format=ARCHIVE_FORMAT_TAR_USTAR;
115
+ compression=ARCHIVE_COMPRESSION_XZ;
116
+ }else if(selfpath.substr(selfpath.length()-9).compare(".tar.lzma")==0 ){
117
+ format=ARCHIVE_FORMAT_TAR_USTAR;
118
+ compression=ARCHIVE_COMPRESSION_LZMA;
119
+ }else if(selfpath.substr(selfpath.length()-7).compare(".tar.gz")==0 || selfpath.substr(selfpath.length()-4).compare(".tgz")==0){
120
+ format=ARCHIVE_FORMAT_TAR_USTAR;
121
+ compression=ARCHIVE_COMPRESSION_GZIP;
122
+ }else if(selfpath.substr(selfpath.length()-8).compare(".tar.bz2")==0 || selfpath.substr(selfpath.length()-4).compare(".tbz")==0 || selfpath.substr(selfpath.length()-4).compare(".tb2")==0){
123
+ format=ARCHIVE_FORMAT_TAR_USTAR;
124
+ compression=ARCHIVE_COMPRESSION_BZIP2;
125
+ }else if(selfpath.substr(selfpath.length()-4).compare(".tar")==0)
126
+ format=ARCHIVE_FORMAT_TAR_USTAR;
127
+ }
128
+ }
129
+ if(format==ARCHIVE_FORMAT_TAR_GNUTAR)
130
+ format=ARCHIVE_FORMAT_TAR_USTAR;
131
+ }
132
+
133
+ int Archive_read_ruby(VALUE self,struct archive *data)
134
+ {
135
+ int error =0;
136
+ switch(_self->type){
137
+ case archive_path:
138
+ error = archive_read_open_filename(data,_self->path.c_str(),10240);
139
+ break;
140
+ case archive_fd:
141
+ error = archive_read_open_fd(data,_self->fd,10240);
142
+ break;
143
+ case archive_buffer:
144
+ break;
145
+ case archive_ruby:
146
+ error = archive_read_open(data, (void*)_self->ruby,rubymyopen,rubymyread,rubymyclose);
147
+ break;
148
+ }
149
+ return error;
150
+ }
151
+
152
+
153
+
154
+ int Archive_write_ruby(VALUE self,struct archive *data)
155
+ {
156
+ int error =0;
157
+ switch(_self->type){
158
+ case archive_path:
159
+ error = archive_write_open_filename(data,_self->path.c_str());
160
+ break;
161
+ case archive_fd:
162
+ error = archive_write_open_fd(data,_self->fd);
163
+ break;
164
+ case archive_buffer:
165
+ break;
166
+ case archive_ruby:
167
+ error = archive_write_open(data,(void*)_self->ruby,rubymyopen,rubymywrite,rubymyclose);
168
+ break;
169
+ }
170
+ return error;
171
+ }
172
+
173
+ int Archive_write_set_compression(struct archive *data,int compression)
174
+ {
175
+ int error = 0;
176
+ switch(compression){
177
+ case ARCHIVE_COMPRESSION_NONE:
178
+ error = archive_write_set_compression_none(data);
179
+ break;
180
+ case ARCHIVE_COMPRESSION_GZIP:
181
+ error = archive_write_set_compression_gzip(data);
182
+ break;
183
+ case ARCHIVE_COMPRESSION_BZIP2:
184
+ error = archive_write_set_compression_bzip2(data);
185
+ break;
186
+ case ARCHIVE_COMPRESSION_COMPRESS:
187
+ error = archive_write_set_compression_compress(data);
188
+ break;
189
+ case ARCHIVE_COMPRESSION_LZMA:
190
+ error = archive_write_set_compression_lzma(data);
191
+ break;
192
+ case ARCHIVE_COMPRESSION_XZ:
193
+ error = archive_write_set_compression_xz(data);
194
+ break;
195
+ default:
196
+ rb_raise(rb_eArchiveErrorCompression,"unsupported compresstype");
197
+ break;
198
+ }
199
+ return error;
200
+ }
201
+ /*
202
+ *call-seq:
203
+ * new( path [, format [, compression ] ] ) → an_archive
204
+ *
205
+ * Makes a new Archive object. If format is given, a new archive is created or
206
+ * an existing archive will be converted into the given format.
207
+ * ===Parameters
208
+ * [path] The path to the archive. May or may not exist.
209
+ * [format] The archive's format as a symbol. If you ommit this, the format will
210
+ * be guessed from the file extension. Possible formats are:
211
+ * * :ar
212
+ * * :tar
213
+ * * :xar
214
+ * * :zip
215
+ * [compression] Symbol inidicating the compression you want to use. If
216
+ * ommited, it will be guessed from the file extension.
217
+ * Possible formats are:
218
+ * * :bzip2
219
+ * * :compress
220
+ * * :gzip
221
+ * * :lzma
222
+ * * :xz
223
+ * ===Raises
224
+ * [FormatError] Unknown archive format or writing not supported.
225
+ * [CompressionError] Unknown compression format.
226
+ * ===Examples
227
+ * See the README for some examples.
228
+ */
229
+
230
+ VALUE Archive_initialize(int argc, VALUE *argv,VALUE self)
231
+ {
232
+ VALUE path,r_format,r_compression;
233
+ rb_scan_args(argc, argv, "12", &path,&r_format,&r_compression);
234
+ if(rb_obj_is_kind_of(path,rb_cIO)){
235
+ _self->fd = NUM2INT(rb_funcall(path,rb_intern("fileno"),0));
236
+ _self->type = archive_fd;
237
+ rb_scan_args(argc, argv, "21", &path,&r_format,&r_compression);
238
+ }else if(rb_respond_to(path,rb_intern("read"))){
239
+ _self->ruby = path;
240
+ _self->type = archive_ruby;
241
+ rb_scan_args(argc, argv, "21", &path,&r_format,&r_compression);
242
+ }else{
243
+ path = rb_file_s_expand_path(1,&path);
244
+ _self->path = std::string(rb_string_value_cstr(&path));
245
+ _self->type =archive_path;
246
+ }
247
+
248
+ //to set the format the file must convert
249
+ if(!NIL_P(r_format)){
250
+
251
+ char buff[8192];
252
+ //std::string selfpath =_self->path;
253
+ size_t bytes_read;
254
+ struct archive *a = archive_read_new(),*b=archive_write_new();
255
+ struct archive_entry *entry;
256
+ int format,compression,error=0;
257
+ std::vector<struct archive_entry *> entries;
258
+ std::vector<std::string> allbuff;
259
+ if(rb_obj_is_kind_of(r_format,rb_cSymbol)!=Qtrue)
260
+ rb_raise(rb_eTypeError,"exepted Symbol");
261
+ if(SYM2ID(r_format) == rb_intern("tar"))
262
+ format = ARCHIVE_FORMAT_TAR_GNUTAR;
263
+ else if(SYM2ID(r_format) == rb_intern("zip"))
264
+ format = ARCHIVE_FORMAT_ZIP;
265
+ else if(SYM2ID(r_format) == rb_intern("ar"))
266
+ format = ARCHIVE_FORMAT_AR_GNU;
267
+ else if(SYM2ID(r_format) == rb_intern("xar"))
268
+ format = ARCHIVE_FORMAT_XAR;
269
+ else
270
+ rb_raise(rb_eTypeError,"wrong format");
271
+
272
+ if(NIL_P(r_compression)){
273
+ compression = ARCHIVE_COMPRESSION_NONE;
274
+ error = archive_write_set_compression_none(b);
275
+ }else if(SYM2ID(r_compression) == rb_intern("gzip")){
276
+ compression =ARCHIVE_COMPRESSION_GZIP;
277
+ error = archive_write_set_compression_gzip(b);
278
+ }else if(SYM2ID(r_compression) == rb_intern("bzip2")){
279
+ compression =ARCHIVE_COMPRESSION_BZIP2;
280
+ error = archive_write_set_compression_bzip2(b);
281
+ }else if(SYM2ID(r_compression) == rb_intern("compress")){
282
+ compression =ARCHIVE_COMPRESSION_BZIP2;
283
+ error = archive_write_set_compression_compress(b);
284
+ }else if(SYM2ID(r_compression) == rb_intern("lzma")){
285
+ compression =ARCHIVE_COMPRESSION_LZMA;
286
+ error = archive_write_set_compression_lzma(b);
287
+ }else if(SYM2ID(r_compression) == rb_intern("xz")){
288
+ compression =ARCHIVE_COMPRESSION_XZ;
289
+ error = archive_write_set_compression_xz(b);
290
+ }else
291
+ rb_raise(rb_eTypeError,"unsupported compression");
292
+
293
+ if(error)
294
+ rb_raise(rb_eArchiveError,"error (%d): %s ",archive_errno(b),archive_error_string(b));
295
+
296
+ archive_read_support_compression_all(a);
297
+ archive_read_support_format_all(a);
298
+ archive_read_support_format_raw(a);
299
+ //autodetect format and compression
300
+ error=Archive_read_ruby(self,a);
301
+ _self->format = format;
302
+ _self->compression = compression;
303
+ if(error==ARCHIVE_OK){
304
+ while(archive_read_next_header(a, &entry)==ARCHIVE_OK){
305
+ if(format==archive_format(a) && compression==archive_compression(a)){
306
+ archive_read_finish(a);
307
+ return self;
308
+ }
309
+ entries.push_back(archive_entry_clone(entry));
310
+ allbuff.push_back(std::string(""));
311
+ while ((bytes_read=archive_read_data(a,&buff,sizeof(buff)))>0)
312
+ allbuff.back().append(buff,bytes_read);
313
+ }
314
+ archive_read_finish(a);
315
+ }
316
+ //format fix
317
+ if(format==ARCHIVE_FORMAT_TAR_GNUTAR)
318
+ format=ARCHIVE_FORMAT_TAR_USTAR;
319
+
320
+ if((error = archive_write_set_format(b,format)) != ARCHIVE_OK)
321
+ rb_raise(rb_eArchiveErrorFormat,"error (%d): %s ",error,archive_error_string(b));
322
+ error=Archive_write_ruby(self,b);
323
+ if(error==ARCHIVE_OK){
324
+ //write old data back
325
+ for(unsigned int i=0; i<entries.size(); i++){
326
+ archive_write_header(b,entries[i]);
327
+ archive_write_data(b,allbuff[i].c_str(),allbuff[i].length());
328
+ archive_write_finish_entry(b);
329
+ }
330
+ archive_write_finish(b);
331
+ }
332
+ }
333
+ return self;
334
+ }
335
+
336
+ /*
337
+ * call-seq:
338
+ * path() → a_string
339
+ *
340
+ * Returns the path (filename) of the archive.
341
+ * ===Return value
342
+ * Returns path of the archive. May or may not exist.
343
+ * ===Example
344
+ * a.path #=> /home/freak/myarchive.tar.gz
345
+ */
346
+
347
+ VALUE Archive_path(VALUE self)
348
+ {
349
+ return (_self->type == archive_path) ? rb_str_new2(_self->path.c_str()) : Qnil;
350
+ }
351
+
352
+ VALUE Archive_each_block(struct archive *data)
353
+ {
354
+ VALUE result = rb_ary_new();
355
+ struct archive_entry *entry;
356
+ while (archive_read_next_header(data, &entry) == ARCHIVE_OK) {
357
+ VALUE temp = wrap(entry);
358
+ if(rb_proc_arity(rb_block_proc())==1){
359
+ rb_yield(temp);
360
+ archive_read_data_skip(data);
361
+ }else{
362
+ char buff[8192];
363
+ std::string str;
364
+ size_t bytes_read;
365
+ try {
366
+ while ((bytes_read=archive_read_data(data,&buff,sizeof(buff)))>0)
367
+ str.append(buff,bytes_read);
368
+ }catch(...){
369
+ rb_raise(rb_eArchiveError,"error:%d:%s",archive_errno(data),archive_error_string(data));
370
+ }
371
+ rb_yield_values(2,temp,rb_str_new2(str.c_str()));
372
+ }
373
+ rb_ary_push(result,temp);
374
+ }
375
+ return result;
376
+ }
377
+
378
+ /*
379
+ * call-seq:
380
+ * each(){|entry [, data]| ... } → an_array
381
+ * each() → an_enumerator
382
+ *
383
+ * Iterates through the archive and yields each entry as an Archive::Entry object. The second parameter
384
+ * contains the data of that entry, so you don't have to extract it only to read what's in it.
385
+ * ===Return value
386
+ * If a block is given, returns an array of Archive::Entry objects, otherwise an enumerator.
387
+ * ===Example
388
+ * a.each{|entry| p entry.path}
389
+ * a.each{|entry, data| puts "'#{entry.path}' contains '#{data}'"}
390
+ * Output:
391
+ * "file1.txt"
392
+ * "file2.txt"
393
+ * 'file1.txt' contains 'I am file1!'
394
+ * 'file2.txt' contains 'I am file2!'
395
+ */
396
+
397
+ VALUE Archive_each(VALUE self)
398
+ {
399
+ RETURN_ENUMERATOR(self,0,NULL);
400
+ struct archive *a = archive_read_new();
401
+
402
+ archive_read_support_compression_all(a);
403
+ archive_read_support_format_all(a);
404
+ archive_read_support_format_raw(a);
405
+ int error=Archive_read_ruby(self,a);
406
+ if(error==ARCHIVE_OK)
407
+ return RB_ENSURE(Archive_each_block,a,Archive_read_block_ensure,a);
408
+ return Qnil;
409
+ }
410
+
411
+
412
+ VALUE Archive_each_entry_block(struct archive *data)
413
+ {
414
+ VALUE result = rb_ary_new();
415
+ struct archive_entry *entry;
416
+ while (archive_read_next_header(data, &entry) == ARCHIVE_OK) {
417
+ VALUE temp = wrap(entry);
418
+ rb_yield(temp);
419
+ archive_read_data_skip(data);
420
+ rb_ary_push(result,temp);
421
+ }
422
+ return result;
423
+ }
424
+
425
+ /*
426
+ * call-seq:
427
+ * each_entry() {|entry| ... } → an_array
428
+ * each_entry() → an_enumerator
429
+ *
430
+ * Iterates through the archive and yields each entry as an Archive::Entry object.
431
+ * This is the same as #each, but doesn't allow for the second block parameter.
432
+ * ===Return value
433
+ * If a block is given, returns an array of Archive::Entry objects, otherwise an enumerator.
434
+ * ===Example
435
+ * a.each_entry{|entry| p entry.path}
436
+ * Output:
437
+ * "file1.txt"
438
+ * "file2.txt"
439
+ */
440
+
441
+ VALUE Archive_each_entry(VALUE self)
442
+ {
443
+ RETURN_ENUMERATOR(self,0,NULL);
444
+ struct archive *a = archive_read_new();
445
+
446
+ archive_read_support_compression_all(a);
447
+ archive_read_support_format_all(a);
448
+ archive_read_support_format_raw(a);
449
+ int error=Archive_read_ruby(self,a);
450
+ if(error==ARCHIVE_OK)
451
+ return RB_ENSURE(Archive_each_entry_block,a,Archive_read_block_ensure,a);
452
+ return Qnil;
453
+ }
454
+
455
+
456
+ VALUE Archive_each_data_block(struct archive *data)
457
+ {
458
+ VALUE result = rb_ary_new();
459
+ struct archive_entry *entry;
460
+ while (archive_read_next_header(data, &entry) == ARCHIVE_OK) {
461
+ char buff[8192];
462
+ std::string str;
463
+ size_t bytes_read;
464
+ try{
465
+ while ((bytes_read=archive_read_data(data,&buff,sizeof(buff)))>0)
466
+ str.append(buff,bytes_read);
467
+ } catch (...){
468
+ rb_raise(rb_eArchiveError,"error:%d:%s",archive_errno(data),archive_error_string(data));
469
+ }
470
+ rb_yield(rb_str_new2(str.c_str()));
471
+ rb_ary_push(result,rb_str_new2(str.c_str()));
472
+ }
473
+ return result;
474
+ }
475
+
476
+
477
+ /*
478
+ * call-seq:
479
+ * each_data() {|data| } → an_array
480
+ * each_data() → an_enumerator
481
+ *
482
+ * Iterates through the archive and yields each entry's data as a string.
483
+ * This is the same as #each, but doesn't allow for the first block parameter.
484
+ * ===Return value
485
+ * If a block is given, returns an array of String objects, otherwise an enumerator.
486
+ * ===Example
487
+ * a.each{|data| puts "This is: '#{data}'"}
488
+ * Output:
489
+ * This is: 'I am file1!'
490
+ * This is: 'I am file2!'
491
+ */
492
+
493
+ VALUE Archive_each_data(VALUE self)
494
+ {
495
+ RETURN_ENUMERATOR(self,0,NULL);
496
+ struct archive *a = archive_read_new();
497
+
498
+ archive_read_support_compression_all(a);
499
+ archive_read_support_format_all(a);
500
+ archive_read_support_format_raw(a);
501
+ int error=Archive_read_ruby(self,a);
502
+ if(error==ARCHIVE_OK)
503
+ return RB_ENSURE(Archive_each_data_block,a,Archive_read_block_ensure,a);
504
+ return Qnil;
505
+ }
506
+
507
+
508
+ /*
509
+ * call-seq:
510
+ * archive.to_hash → Hash
511
+ *
512
+ * Iterates through the archive and yields each data of an entry as a string object.
513
+ * ===Return value
514
+ * returns Hash of Archive::Entry => Sring
515
+ */
516
+
517
+ VALUE Archive_to_hash(VALUE self)
518
+ {
519
+ VALUE result = rb_hash_new();
520
+ struct archive *a = archive_read_new();
521
+ struct archive_entry *entry;
522
+ archive_read_support_compression_all(a);
523
+ archive_read_support_format_all(a);
524
+ archive_read_support_format_raw(a);
525
+ int error=Archive_read_ruby(self,a);
526
+ if(error==ARCHIVE_OK){
527
+ while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
528
+ char buff[8192];
529
+ std::string str;
530
+ size_t bytes_read;
531
+ try{
532
+ while ((bytes_read=archive_read_data(a,&buff,sizeof(buff)))>0)
533
+ str.append(buff,bytes_read);
534
+ } catch (...){
535
+ rb_raise(rb_eArchiveError,"error:%d:%s",archive_errno(a),archive_error_string(a));
536
+ }
537
+ rb_hash_aset(result,wrap(entry),rb_str_new2(str.c_str()));
538
+ }
539
+ archive_read_finish(a);
540
+ }
541
+ return result;
542
+ }
543
+
544
+ VALUE Archive_map_block(struct write_obj * data){
545
+ for(unsigned int i=0; i< data->entries->size(); i++){
546
+ VALUE temp = wrap(data->entries->at(i));
547
+ VALUE val;
548
+ if(rb_proc_arity(rb_block_proc())<2){
549
+ val = rb_yield(temp);
550
+ }else{
551
+ val = rb_yield_values(2,temp,rb_str_new2(data->allbuff->at(i).c_str()));
552
+ }
553
+ VALUE entry,rdata= Qnil;
554
+ if(rb_obj_is_kind_of(val,rb_cArray)==Qtrue){
555
+ entry=rb_ary_entry(val,0);
556
+ rdata=rb_ary_entry(val,1);
557
+ }else
558
+ entry = val;
559
+ if(rb_obj_is_kind_of(entry,rb_cArchiveEntry)==Qtrue){
560
+ archive_write_header(data->archive,wrap<struct archive_entry *>(val));
561
+
562
+ if(rdata == Qnil)
563
+ archive_write_data(data->archive,data->allbuff->at(i).c_str(),data->allbuff->at(i).length());
564
+ else{
565
+ char* buff = StringValueCStr(rdata);
566
+ archive_write_data(data->archive,buff,strlen(buff));
567
+ }
568
+ archive_write_finish_entry(data->archive);
569
+ }else if(entry==Qnil){
570
+ }else
571
+ rb_raise(rb_eTypeError,"exepted %s!",rb_class2name(rb_cArchiveEntry));
572
+ }
573
+ return Qnil;
574
+ }
575
+
576
+ /*
577
+ * call-seq:
578
+ * map!() {| entry [,data] | ... } → Array
579
+ * archive.map!() → Enumerator
580
+ *
581
+ * Iterates through the archive and changes it's data "on the fly", i.e.
582
+ * the value your block returns for each iteration is put for the data
583
+ * in the yielded entry. Your block is expected to return a 2-element
584
+ * array of form
585
+ * [archive_entry, "data"]
586
+ * where +archive_enty+ is the +entry+ yielded to the block (which you
587
+ * may modify via the Archive::Entry methods) and <tt>"data"</tt> is a
588
+ * string containing the data you want to set for this entry.
589
+ *
590
+ * The block parameters are the same as for #each.
591
+ * ===Return value
592
+ * The archive itself.
593
+ * ===Example
594
+ * #Double the contents in each file of the archive
595
+ * a.map!{|entry, data| [entry, data * 2]}
596
+ * #Clear all files in the archive
597
+ * a.map!{|entry| [entry, ""]}
598
+ */
599
+ VALUE Archive_map_self(VALUE self)
600
+ {
601
+ RETURN_ENUMERATOR(self,0,NULL);
602
+ char buff[8192];
603
+ std::string selfpath =_self->path;
604
+ size_t bytes_read;
605
+ struct archive *a = archive_read_new(),*b=archive_write_new();
606
+ struct archive_entry *entry;
607
+ int format = ARCHIVE_FORMAT_EMPTY,compression = ARCHIVE_COMPRESSION_NONE,error=0;
608
+ std::vector<struct archive_entry *> entries;
609
+ std::vector<std::string> allbuff;
610
+
611
+ archive_read_support_compression_all(a);
612
+ archive_read_support_format_all(a);
613
+ archive_read_support_format_raw(a);
614
+ //autodetect format and compression
615
+ error=Archive_read_ruby(self,a);
616
+ if(error==ARCHIVE_OK){
617
+ while(archive_read_next_header(a, &entry)==ARCHIVE_OK){
618
+ format = archive_format(a);
619
+ compression = archive_compression(a);
620
+ entries.push_back(archive_entry_clone(entry));
621
+ allbuff.push_back(std::string(""));
622
+ try{
623
+ while ((bytes_read=archive_read_data(a,&buff,sizeof(buff)))>0)
624
+ allbuff.back().append(buff,bytes_read);
625
+ } catch (...){
626
+ rb_raise(rb_eArchiveError,"error:%d:%s",archive_errno(a),archive_error_string(a));
627
+ }
628
+ }
629
+ archive_read_finish(a);
630
+ //format fix
631
+ if(format==ARCHIVE_FORMAT_TAR_GNUTAR)
632
+ format=ARCHIVE_FORMAT_TAR_USTAR;
633
+ if((error = archive_write_set_format(b,format)) != ARCHIVE_OK)
634
+ rb_raise(rb_eArchiveErrorFormat,"error (%d): %s ",error,archive_error_string(b));
635
+ Archive_write_set_compression(b,compression);
636
+
637
+ error=Archive_write_ruby(self,b);
638
+ if(error==ARCHIVE_OK){
639
+ write_obj obj;
640
+ obj.archive = b;
641
+ obj.entries = &entries;
642
+ obj.allbuff = &allbuff;
643
+ RB_ENSURE(Archive_map_block,&obj,Archive_write_block_ensure,b);
644
+ }
645
+ }
646
+ return self;
647
+ }
648
+
649
+ /*
650
+ * call-seq:
651
+ * [](name) → Archive::Entry or nil
652
+ *
653
+ * Returns an archive entry for the given name.
654
+ * ===Parameters
655
+ * [name] could be a String or a Regex.
656
+ * ===Return value
657
+ * If a matching entry is found, it's returned as an Archive::Entry object. If not,
658
+ * nil is returned.
659
+ * ===Example
660
+ * #Assuming your archive contains file.txt and ruby.txt
661
+ *
662
+ * a["file.txt"] #=> Archive::Entry
663
+ * a[/txt/] #=> Archive::Entry
664
+ */
665
+
666
+ VALUE Archive_get(VALUE self,VALUE val)
667
+ {
668
+ struct archive *a = archive_read_new();
669
+ struct archive_entry *entry;
670
+ archive_read_support_compression_all(a);
671
+ archive_read_support_format_all(a);
672
+ archive_read_support_format_raw(a);
673
+ int error=Archive_read_ruby(self,a);
674
+ if(error==ARCHIVE_OK){
675
+ while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
676
+ bool find = false;
677
+ if(rb_obj_is_kind_of(val,rb_cRegexp)==Qtrue){
678
+ find = rb_reg_match(val,rb_str_new2(archive_entry_pathname(entry)))!=Qnil;
679
+ }else{
680
+ val = rb_funcall(val,rb_intern("to_s"),0);
681
+ std::string str1(rb_string_value_cstr(&val)),str2(str1);
682
+ str2 += '/'; // dir ends of '/'
683
+ const char *cstr = archive_entry_pathname(entry);
684
+ find = (str1.compare(cstr)==0 || str2.compare(cstr)==0);
685
+ }
686
+ if(find){
687
+ VALUE result = wrap(entry);
688
+ archive_read_finish(a);
689
+ return result;
690
+ }
691
+ }
692
+ archive_read_finish(a);
693
+ }
694
+ return Qnil;
695
+ }
696
+ /*
697
+ * call-seq:
698
+ * extract( [name = nil [, io [ ,opt ] ] ] ) → an_array
699
+ *
700
+ * Extract files to current directory.
701
+ * ===Parameters
702
+ * [name] (nil) could be an Archive::Entry, a String or an Regex. If given,
703
+ * only this entry is extracted. Otherwise extracts the whole
704
+ * archive.
705
+ * [io] an instance of IO or something with a +write+ method like
706
+ * StringIO. If given, the entry specified via +name+ will be
707
+ * extracted into +io+ instead of a file.
708
+ * [opt] is an option hash. See below for possible options.
709
+ * ===Parameters for the option hash
710
+ * [:extract] flag, Integer combined of the Archive::Extract_* constants.
711
+ * This tells libarchive-ruby to only extract the file attributes
712
+ * you specify here. Exceptions is Archive::EXTRACT_NO_OVERWRITE
713
+ * which prevents this method from overwrtiting existing files.
714
+ * ===Return value
715
+ * The paths of the extracted entries as an array.
716
+ * ===Example
717
+ * #Simply extract everything into the current directory.
718
+ * a.extract
719
+ * #Extract only file1.txt
720
+ * a.extract("file1.txt")
721
+ * #Same as above, but extract it to a StringIO
722
+ * s = StringIO.new
723
+ * a.extract("file1.txt", s)
724
+ * #Same as the first example, but only extract information about the
725
+ * #modification time.
726
+ * a.extract(nil, nil, extract: Archive::EXTRACT_TIME)
727
+ */
728
+
729
+ VALUE Archive_extract(int argc, VALUE *argv, VALUE self)
730
+ {
731
+ char buff[8192];
732
+ size_t bytes_read;
733
+
734
+ VALUE result = rb_ary_new(),name,io,opts,temp;
735
+ struct archive *a = archive_read_new();
736
+ struct archive_entry *entry;
737
+ archive_read_support_compression_all(a);
738
+ archive_read_support_format_all(a);
739
+ archive_read_support_format_raw(a);//add raw, this is not by all
740
+ int extract_opt = 0,fd=-1,error=0;
741
+ rb_scan_args(argc, argv, "03", &name,&io,&opts);
742
+ if(rb_obj_is_kind_of(name,rb_cHash)){
743
+ opts = name;name = Qnil;
744
+ }
745
+ if(rb_obj_is_kind_of(io,rb_cHash)){
746
+ opts = io;io = Qnil;
747
+ }
748
+ if(rb_obj_is_kind_of(io,rb_cIO))
749
+ fd = NUM2INT(rb_funcall(io,rb_intern("fileno"),0));
750
+
751
+
752
+ if(rb_obj_is_kind_of(opts,rb_cHash))
753
+ if(RTEST(temp=rb_hash_aref(opts,ID2SYM(rb_intern("extract")))))
754
+ extract_opt = NUM2INT(temp);
755
+
756
+ error=Archive_read_ruby(self,a);
757
+ if(error==ARCHIVE_OK){
758
+ try{
759
+ if(NIL_P(name)){
760
+ if(!NIL_P(io)){
761
+ rb_raise(rb_eArgError,"You can't extract more than 1 entry into an IO-like object!");
762
+ }
763
+ while(archive_read_next_header(a, &entry) == ARCHIVE_OK){
764
+ archive_read_extract(a,entry,extract_opt);
765
+ rb_ary_push(result,rb_str_new2(archive_entry_pathname(entry)));
766
+ }
767
+ }else{
768
+ if(rb_obj_is_kind_of(name,rb_cArchiveEntry)==Qtrue){
769
+ if(rb_obj_is_kind_of(io,rb_cIO)==Qtrue){
770
+ while(archive_read_next_header(a, &entry) == ARCHIVE_OK){
771
+ if(std::string(archive_entry_pathname(entry)).compare(archive_entry_pathname(wrap<archive_entry*>(name)))==0)
772
+ archive_read_data_into_fd(a,fd);
773
+ }
774
+ }else if(rb_respond_to(io,rb_intern("write"))){
775
+ while(archive_read_next_header(a, &entry) == ARCHIVE_OK){
776
+ if(std::string(archive_entry_pathname(entry)).compare(archive_entry_pathname(wrap<archive_entry*>(name)))==0)
777
+ while ((bytes_read=archive_read_data(a,&buff,sizeof(buff)))>0)
778
+ rb_funcall(io,rb_intern("write"),1,rb_str_new(buff,bytes_read));
779
+
780
+ }
781
+ }else
782
+ archive_read_extract(a,wrap<archive_entry*>(name),extract_opt);
783
+ rb_ary_push(result,rb_str_new2(archive_entry_pathname(wrap<archive_entry*>(name))));
784
+ }else if(rb_obj_is_kind_of(name,rb_cRegexp)==Qtrue){
785
+ while(archive_read_next_header(a, &entry) == ARCHIVE_OK){
786
+ VALUE str = rb_str_new2(archive_entry_pathname(entry));
787
+ if(rb_reg_match(name,str)!=Qnil){
788
+ if(rb_obj_is_kind_of(io,rb_cIO)==Qtrue){
789
+ archive_read_data_into_fd(a,fd);
790
+ }else if(rb_respond_to(io,rb_intern("write"))){
791
+ while ((bytes_read=archive_read_data(a,&buff,sizeof(buff)))>0)
792
+ rb_funcall(io,rb_intern("write"),1,rb_str_new(buff,bytes_read));
793
+ }else
794
+ archive_read_extract(a,entry,extract_opt);
795
+ rb_ary_push(result,str);
796
+ }
797
+ }
798
+ }else{
799
+ name = rb_funcall(name,rb_intern("to_s"),0);
800
+ std::string str1(rb_string_value_cstr(&name)),str2(str1);
801
+ str2 += '/'; // dir ends of '/'
802
+ while(archive_read_next_header(a, &entry) == ARCHIVE_OK){
803
+ const char *cstr = archive_entry_pathname(entry);
804
+ if(str1.compare(cstr)==0 || str2.compare(cstr)==0){
805
+ if(rb_obj_is_kind_of(io,rb_cIO)==Qtrue){
806
+ archive_read_data_into_fd(a,fd);
807
+ }else if(rb_respond_to(io,rb_intern("write"))){
808
+ while ((bytes_read=archive_read_data(a,&buff,sizeof(buff)))>0)
809
+ rb_funcall(io,rb_intern("write"),1,rb_str_new(buff,bytes_read));
810
+ }else
811
+ archive_read_extract(a,entry,extract_opt);
812
+ rb_ary_push(result,rb_str_new2(cstr));
813
+ }
814
+ }
815
+ }
816
+ }
817
+ }catch (...){
818
+ rb_raise(rb_eArchiveError,"error:%d:%s",archive_errno(a),archive_error_string(a));
819
+ }
820
+ archive_read_finish(a);
821
+ }
822
+ return result;
823
+ }
824
+
825
+ VALUE Archive_extract_if_block(struct extract_obj * data)
826
+ {
827
+ VALUE result = rb_ary_new();
828
+ struct archive_entry *entry;
829
+ while(archive_read_next_header(data->archive, &entry) == ARCHIVE_OK){
830
+ VALUE str = rb_str_new2(archive_entry_pathname(entry));
831
+ if(RTEST(rb_yield(wrap(entry)))){
832
+ archive_read_extract(data->archive,entry,data->extract_opt);
833
+ rb_ary_push(result,str);
834
+ }
835
+ }
836
+ return result;
837
+ }
838
+ /*
839
+ * call-seq:
840
+ * extract_if( [ opt ] ) {|entry| } → an_array
841
+ * extract_if( [ opt ] ) → an_enumerator
842
+ *
843
+ * Yields each entry in the archive to the block and extracts only those
844
+ * entries (to the current directory) for which the block evaluates to a
845
+ * truth value.
846
+ * ===Parameters
847
+ * [opt] is the same option hash that you can pass to #extract.
848
+ * ====Parameters for the option hash
849
+ * See the #extract method for explanation.
850
+ * ===Return value
851
+ * The paths of all extracted entries if a block was given, an Enumerator
852
+ * otherwise.
853
+ */
854
+
855
+ VALUE Archive_extract_if(int argc, VALUE *argv, VALUE self)
856
+ {
857
+ RETURN_ENUMERATOR(self,argc,argv);
858
+ VALUE opts,temp;
859
+ struct archive *a = archive_read_new();
860
+ archive_read_support_compression_all(a);
861
+ archive_read_support_format_all(a);
862
+ archive_read_support_format_raw(a);
863
+ int extract_opt=0,error=0;
864
+
865
+ rb_scan_args(argc, argv, "01", &opts);
866
+ if(rb_obj_is_kind_of(opts,rb_cHash))
867
+ if(RTEST(temp=rb_hash_aref(opts,ID2SYM(rb_intern("extract")))))
868
+ extract_opt = NUM2INT(temp);
869
+ error=Archive_read_ruby(self,a);
870
+ if(error==ARCHIVE_OK){
871
+ extract_obj obj;
872
+ obj.archive = a;
873
+ obj.extract_opt = extract_opt;
874
+ return RB_ENSURE(Archive_extract_if_block,&obj,Archive_read_block_ensure,a);
875
+ }
876
+ return Qnil;
877
+ }
878
+
879
+ /*
880
+ * call-seq:
881
+ * format() → an_integer or nil
882
+ *
883
+ * Returns the archive format as an integer. You should use #format_name
884
+ * instead.
885
+ * ===Return value
886
+ * An integer or nil if the format wasn't detectable.
887
+ */
888
+
889
+ VALUE Archive_format(VALUE self)
890
+ {
891
+ struct archive *a = archive_read_new();
892
+ struct archive_entry *entry;
893
+ VALUE result = Qnil;
894
+ archive_read_support_compression_all(a);
895
+ archive_read_support_format_all(a);
896
+ archive_read_support_format_raw(a);
897
+ int error=Archive_read_ruby(self,a);
898
+ if(error==ARCHIVE_OK){
899
+ archive_read_next_header(a, &entry);
900
+ result = INT2NUM(archive_format(a));
901
+ archive_read_finish(a);
902
+ }
903
+ return result;
904
+ }
905
+
906
+ /*
907
+ * call-seq:
908
+ * compression() → an_integer or nil
909
+ *
910
+ * Returns the archive compression as an integer. You should use
911
+ * #compression_name instead.
912
+ * ===Return value
913
+ * An integer or nil if the compression wasn't detectable. 0 means
914
+ * no compression.
915
+ */
916
+
917
+ VALUE Archive_compression(VALUE self)
918
+ {
919
+ struct archive *a = archive_read_new();
920
+ struct archive_entry *entry;
921
+ VALUE result = Qnil;
922
+ archive_read_support_compression_all(a);
923
+ archive_read_support_format_all(a);
924
+ archive_read_support_format_raw(a);
925
+ int error=Archive_read_ruby(self,a);
926
+ if(error==ARCHIVE_OK){
927
+ archive_read_next_header(a, &entry);
928
+ result = INT2NUM(archive_compression(a));
929
+ archive_read_finish(a);
930
+ }
931
+ return result;
932
+ }
933
+
934
+ /*
935
+ * call-seq:
936
+ * format_name() → a_string or nil
937
+ *
938
+ * Returns the archive format's name as a string.
939
+ * ===Return value
940
+ * A string or nil if the format wasn't detectable.
941
+ * ===Example
942
+ * a.format_name #=> "GNU tar format"
943
+ */
944
+
945
+ VALUE Archive_format_name(VALUE self)
946
+ {
947
+ struct archive *a = archive_read_new();
948
+ struct archive_entry *entry;
949
+ const char* name = NULL;
950
+ archive_read_support_compression_all(a);
951
+ archive_read_support_format_all(a);
952
+ archive_read_support_format_raw(a);
953
+ int error=Archive_read_ruby(self,a);
954
+ if(error==ARCHIVE_OK){
955
+ if(archive_read_next_header(a, &entry)==ARCHIVE_OK){
956
+ name = archive_format_name(a);
957
+ archive_read_finish(a);
958
+ }
959
+ }
960
+ return name ? rb_str_new2(name) : Qnil ;
961
+ }
962
+
963
+ /*
964
+ * call-seq:
965
+ * compression_name() → a_string or nil
966
+ *
967
+ * Returns the archive compression's name as a string.
968
+ * ===Return value
969
+ * A string or nil if the compression format wasn't detectable. If there
970
+ * is no compression, <tt>"none"</tt> is returned.
971
+ * ===Example
972
+ * a.compression_name #=> "gzip"
973
+ *
974
+ * a.compression_name #=> "none"
975
+ */
976
+
977
+ VALUE Archive_compression_name(VALUE self)
978
+ {
979
+ struct archive *a = archive_read_new();
980
+ struct archive_entry *entry;
981
+ const char* name = NULL;
982
+ archive_read_support_compression_all(a);
983
+ archive_read_support_format_all(a);
984
+ archive_read_support_format_raw(a);
985
+ int error=Archive_read_ruby(self,a);
986
+ if(error==ARCHIVE_OK){
987
+ archive_read_next_header(a, &entry);
988
+ name = archive_compression_name(a);
989
+ archive_read_finish(a);
990
+ }
991
+ return name ? rb_str_new2(name) : Qnil ;
992
+ }
993
+
994
+ /*
995
+ * call-seq:
996
+ * add( obj , path ) → self
997
+ *
998
+ * Adds a file to an archive.
999
+ * ===Parameters
1000
+ * [obj] String, IO, File or an object which responds to +read+.
1001
+ * [path] Sets the file name inside the archive.
1002
+ * ===Return value
1003
+ * self
1004
+ * ===Raises
1005
+ * [FormatError] Raised if the archive format is not supported for writing.
1006
+ */
1007
+
1008
+ VALUE Archive_add(VALUE self,VALUE obj,VALUE name)
1009
+ {
1010
+ char buff[8192];
1011
+ char *path = NULL;
1012
+
1013
+ size_t bytes_read;
1014
+ struct archive *a = archive_read_new(),*b=archive_write_new(),*c=archive_read_disk_new();
1015
+ struct archive_entry *entry;
1016
+ std::vector<struct archive_entry *> entries;
1017
+ std::vector<std::string> allbuff;
1018
+ int format= ARCHIVE_FORMAT_EMPTY,compression=ARCHIVE_COMPRESSION_NONE,fd=-1,error=0;
1019
+ archive_read_support_compression_all(a);
1020
+ archive_read_support_format_all(a);
1021
+ archive_read_support_format_raw(a);
1022
+ //autodetect format and compression
1023
+ error=Archive_read_ruby(self,a);
1024
+ if(error==ARCHIVE_OK){
1025
+ while(archive_read_next_header(a, &entry)==ARCHIVE_OK){
1026
+ entries.push_back(archive_entry_clone(entry));
1027
+ allbuff.push_back(std::string(""));
1028
+ try{
1029
+ while ((bytes_read=archive_read_data(a,&buff,sizeof(buff)))>0)
1030
+ allbuff.back().append(buff,bytes_read);
1031
+ }catch(...){
1032
+ rb_raise(rb_eArchiveError,"error:%d:%s",archive_errno(a),archive_error_string(a));
1033
+ }
1034
+ }
1035
+ format = archive_format(a);
1036
+ compression = archive_compression(a);
1037
+ archive_read_finish(a);
1038
+ }
1039
+ if(rb_obj_is_kind_of(obj,rb_cIO)){
1040
+ fd = NUM2INT(rb_funcall(obj,rb_intern("fileno"),0));
1041
+ }else if(rb_obj_is_kind_of(obj,rb_cFile)){
1042
+ VALUE pathname = rb_funcall(obj,rb_intern("path"),0); //source path
1043
+ VALUE obj2 = rb_file_s_expand_path(1,&pathname);
1044
+ path = rb_string_value_cstr(&obj2);
1045
+ fd = NUM2INT(rb_funcall(obj,rb_intern("fileno"),0));
1046
+ }else if(rb_respond_to(obj,rb_intern("read"))){
1047
+ //stringio has neigther path or fileno, so do nothing
1048
+ }else {
1049
+ VALUE obj2 = rb_file_s_expand_path(1,&obj);
1050
+ path = rb_string_value_cstr(&obj2);
1051
+ fd = open(path, O_RDONLY);
1052
+ if (fd < 0) //TODO: add error
1053
+ return self;
1054
+ }
1055
+ Archive_format_from_path(self,format,compression);
1056
+ if((error = archive_write_set_format(b,format)) != ARCHIVE_OK)
1057
+ rb_raise(rb_eArchiveErrorFormat,"error:%d:%s",archive_errno(b),archive_error_string(b));
1058
+ Archive_write_set_compression(b,compression);
1059
+
1060
+ error=Archive_write_ruby(self,b);
1061
+ if(error==ARCHIVE_OK){
1062
+ for(unsigned int i=0; i<entries.size(); i++){
1063
+ if(std::string(rb_string_value_cstr(&name)).compare(archive_entry_pathname(entries[i]))!=0){
1064
+ archive_write_header(b,entries[i]);
1065
+ archive_write_data(b,allbuff[i].c_str(),allbuff[i].length());
1066
+ archive_write_finish_entry(b);
1067
+ }
1068
+ }
1069
+ archive_read_disk_set_standard_lookup(c);
1070
+ entry = archive_entry_new();
1071
+ if (path)
1072
+ archive_entry_copy_sourcepath(entry, path);
1073
+ archive_entry_copy_pathname(entry, rb_string_value_cstr(&name));
1074
+ if(fd < 0 and rb_respond_to(obj,rb_intern("read"))){
1075
+ archive_write_header(b, entry);
1076
+ VALUE result;
1077
+ result = rb_funcall(obj,rb_intern("read"),0);
1078
+ archive_write_data(b, rb_string_value_cstr(&result), RSTRING_LEN(result));
1079
+ }else{
1080
+ archive_read_disk_entry_from_file(c, entry, fd, NULL);
1081
+ archive_write_header(b, entry);
1082
+ while ((bytes_read = read(fd, buff, sizeof(buff))) > 0)
1083
+ archive_write_data(b, buff, bytes_read);
1084
+ }
1085
+ archive_write_finish_entry(b);
1086
+ archive_read_finish(c);
1087
+ archive_write_finish(b);
1088
+ }
1089
+
1090
+ if(fd >= 0 and !rb_obj_is_kind_of(name,rb_cIO))
1091
+ close(fd);
1092
+ return self;
1093
+ }
1094
+
1095
+ /*
1096
+ * call-seq:
1097
+ * << obj → self
1098
+ *
1099
+ * Adds a file to an archive. Basicly the same as #add, but you can't
1100
+ * set the path inside the archive.
1101
+ * ===Parameters
1102
+ * [obj] String or File
1103
+ * ===Return value
1104
+ * self
1105
+ * ===Raises
1106
+ * [FormatError] The archive format is not supported for writing.
1107
+ */
1108
+ VALUE Archive_add_shift(VALUE self,VALUE name)
1109
+ {
1110
+ if(rb_obj_is_kind_of(name,rb_cFile))
1111
+ return Archive_add(self,name,rb_funcall(name,rb_intern("path"),0));
1112
+ else
1113
+ return Archive_add(self,name,name);
1114
+ }
1115
+
1116
+ /*
1117
+ * call-seq:
1118
+ * delete( name ) → an_array
1119
+ *
1120
+ * Delete files from an archive.
1121
+ * ===Parameters
1122
+ * [name] An Archive::Entry, a String or a Regex.
1123
+ * ===Return value
1124
+ * A list of paths removed from the archive.
1125
+ * ===Raises
1126
+ * raise TypeError if the parameter is neigther String or File.
1127
+ * raise Error if the format has no write support
1128
+ */
1129
+
1130
+ VALUE Archive_delete(VALUE self,VALUE val)
1131
+ {
1132
+ char buff[8192];
1133
+ size_t bytes_read;
1134
+ struct archive *a = archive_read_new(),*b=archive_write_new();
1135
+ struct archive_entry *entry;
1136
+ int format = ARCHIVE_FORMAT_EMPTY,compression = ARCHIVE_COMPRESSION_NONE,error=0;
1137
+ std::vector<struct archive_entry *> entries;
1138
+ std::vector<std::string> allbuff;
1139
+ VALUE result = rb_ary_new();
1140
+ archive_read_support_compression_all(a);
1141
+ archive_read_support_format_all(a);
1142
+ archive_read_support_format_raw(a);
1143
+ //autodetect format and compression
1144
+ error=Archive_read_ruby(self,a);
1145
+ if(error==ARCHIVE_OK){
1146
+ while(archive_read_next_header(a, &entry)==ARCHIVE_OK){
1147
+ format = archive_format(a);
1148
+ compression = archive_compression(a);
1149
+ bool del = false;
1150
+ if(rb_obj_is_kind_of(val,rb_cArchiveEntry)==Qtrue){
1151
+ del = std::string(archive_entry_pathname(wrap<archive_entry*>(val))).compare(archive_entry_pathname(entry)) == 0;
1152
+ }else if(rb_obj_is_kind_of(val,rb_cRegexp)==Qtrue){
1153
+ VALUE str = rb_str_new2(archive_entry_pathname(entry));
1154
+ del = rb_reg_match(val,str)!=Qnil;
1155
+ }else {
1156
+ std::string str1(rb_string_value_cstr(&val)),str2(str1);
1157
+ str2 += '/'; // dir ends of '/'
1158
+ const char *cstr = archive_entry_pathname(entry);
1159
+ del = (str1.compare(cstr)==0 || str2.compare(cstr)==0);
1160
+ }
1161
+ if(!del){
1162
+ entries.push_back(archive_entry_clone(entry));
1163
+ allbuff.push_back(std::string(""));
1164
+ try{
1165
+ while ((bytes_read=archive_read_data(a,&buff,sizeof(buff)))>0)
1166
+ allbuff.back().append(buff,bytes_read);
1167
+ }catch(...){
1168
+ rb_raise(rb_eArchiveError,"error:%d:%s",archive_errno(a),archive_error_string(a));
1169
+ }
1170
+ }else
1171
+ rb_ary_push(result,wrap(archive_entry_clone(entry)));
1172
+ }
1173
+ archive_read_finish(a);
1174
+
1175
+ //format fix
1176
+ if(format==ARCHIVE_FORMAT_TAR_GNUTAR)
1177
+ format=ARCHIVE_FORMAT_TAR_USTAR;
1178
+
1179
+ //TODO add archive-error
1180
+ if((error = archive_write_set_format(b,format)) != ARCHIVE_OK)
1181
+ rb_raise(rb_eArchiveErrorFormat,"error (%d): %s ",error,archive_error_string(b));
1182
+ Archive_write_set_compression(b,compression);
1183
+ error=Archive_write_ruby(self,b);
1184
+ if(error==ARCHIVE_OK){
1185
+ //write old data back
1186
+ for(unsigned int i=0; i<entries.size(); i++){
1187
+ archive_write_header(b,entries[i]);
1188
+ archive_write_data(b,allbuff[i].c_str(),allbuff[i].length());
1189
+ archive_write_finish_entry(b);
1190
+ }
1191
+ archive_write_finish(b);
1192
+ }
1193
+
1194
+ }
1195
+ return result;
1196
+ }
1197
+
1198
+ VALUE Archive_delete_if_block(struct write_obj * data)
1199
+ {
1200
+ VALUE result= rb_ary_new();
1201
+ for(unsigned int i=0; i< data->entries->size(); i++){
1202
+ if(!RTEST(rb_yield(wrap(data->entries->at(i))))){
1203
+ archive_write_header(data->archive,data->entries->at(i));
1204
+ archive_write_data(data->archive,data->allbuff->at(i).c_str(),data->allbuff->at(i).length());
1205
+ archive_write_finish_entry(data->archive);
1206
+ }else
1207
+ rb_ary_push(result,wrap(data->entries->at(i)));
1208
+ }
1209
+ return result;
1210
+ }
1211
+
1212
+ /*
1213
+ * call-seq:
1214
+ * archive.delete_if {|entry| } -> self
1215
+ * archive.delete_if -> Enumerator
1216
+ *
1217
+ * Yields each entry in the archive to the block and deletes those for which
1218
+ * the block evaluates to a truth value.
1219
+ * ===Parameters
1220
+ * [name] An Archive::Entry, a String or a Regex.
1221
+ * ===Return value
1222
+ * If a block was given, returns self, otherwise an Enumerator.
1223
+ * ===Raises
1224
+ * raise Error if the format has no write support
1225
+ */
1226
+
1227
+ VALUE Archive_delete_if(VALUE self)
1228
+ {
1229
+ RETURN_ENUMERATOR(self,0,NULL);
1230
+ char buff[8192];
1231
+ std::string selfpath =_self->path;
1232
+ size_t bytes_read;
1233
+ struct archive *a = archive_read_new(),*b=archive_write_new();
1234
+ struct archive_entry *entry;
1235
+ int format = ARCHIVE_FORMAT_EMPTY,compression = ARCHIVE_COMPRESSION_NONE,error=0;
1236
+ std::vector<struct archive_entry *> entries;
1237
+ std::vector<std::string> allbuff;
1238
+
1239
+ archive_read_support_compression_all(a);
1240
+ archive_read_support_format_all(a);
1241
+ archive_read_support_format_raw(a);
1242
+ error=Archive_read_ruby(self,a);
1243
+ if(error==ARCHIVE_OK){
1244
+ while(archive_read_next_header(a, &entry)==ARCHIVE_OK){
1245
+ format = archive_format(a);
1246
+ compression = archive_compression(a);
1247
+ entries.push_back(archive_entry_clone(entry));
1248
+ allbuff.push_back(std::string(""));
1249
+ try{
1250
+ while ((bytes_read=archive_read_data(a,&buff,sizeof(buff)))>0)
1251
+ allbuff.back().append(buff,bytes_read);
1252
+ }catch(...){
1253
+ rb_raise(rb_eArchiveError,"error:%d:%s",archive_errno(a),archive_error_string(a));
1254
+ }
1255
+ }
1256
+ archive_read_finish(a);
1257
+ //format fix
1258
+ if(format==ARCHIVE_FORMAT_TAR_GNUTAR)
1259
+ format=ARCHIVE_FORMAT_TAR_USTAR;
1260
+ if((error = archive_write_set_format(b,format)) != ARCHIVE_OK)
1261
+ rb_raise(rb_eArchiveErrorFormat,"error (%d): %s ",error,archive_error_string(b));
1262
+ Archive_write_set_compression(b,compression);
1263
+ error=Archive_write_ruby(self,b);
1264
+ if(error==ARCHIVE_OK){
1265
+ write_obj obj;
1266
+ obj.archive = b;
1267
+ obj.entries = &entries;
1268
+ obj.allbuff = &allbuff;
1269
+ return RB_ENSURE(Archive_delete_if_block,&obj,Archive_write_block_ensure,b);
1270
+ }
1271
+ }
1272
+ return Qnil;
1273
+ }
1274
+
1275
+ /*
1276
+ * call-seq:
1277
+ * archive.clear -> self
1278
+ *
1279
+ * Deletes all files from an archive.
1280
+ * ===Return value
1281
+ * returns self.
1282
+ * ===Raises
1283
+ * raise Error if the format has no write support
1284
+ */
1285
+
1286
+ VALUE Archive_clear(VALUE self)
1287
+ {
1288
+
1289
+ std::string selfpath =_self->path;
1290
+
1291
+ struct archive *a = archive_read_new(),*b=archive_write_new();
1292
+ struct archive_entry *entry;
1293
+ int format = ARCHIVE_FORMAT_EMPTY,compression = ARCHIVE_COMPRESSION_NONE,error=0;
1294
+
1295
+ archive_read_support_compression_all(a);
1296
+ archive_read_support_format_all(a);
1297
+ archive_read_support_format_raw(a);
1298
+ error=Archive_read_ruby(self,a);
1299
+ if(error==ARCHIVE_OK){
1300
+ archive_read_next_header(a, &entry);
1301
+ format = archive_format(a);
1302
+ compression = archive_compression(a);
1303
+ archive_read_finish(a);
1304
+
1305
+ //format fix
1306
+ if(format==ARCHIVE_FORMAT_TAR_GNUTAR)
1307
+ format=ARCHIVE_FORMAT_TAR_USTAR;
1308
+
1309
+ if((error = archive_write_set_format(b,format)) != ARCHIVE_OK)
1310
+ rb_raise(rb_eArchiveErrorFormat,"error (%d): %s ",error,archive_error_string(b));
1311
+ Archive_write_set_compression(b,compression);
1312
+ error=Archive_write_ruby(self,b);
1313
+ if(error==ARCHIVE_OK)
1314
+ archive_write_finish(b);
1315
+ }
1316
+
1317
+ return self;
1318
+ }
1319
+
1320
+ /*:nodoc:
1321
+ * call-seq:
1322
+ * archive.exist? -> true or false
1323
+ *
1324
+ * Same as
1325
+ * File.exist?(archive.path)
1326
+ * . Checks wheather or not the archive file is existant.
1327
+ * ===Return value
1328
+ * True or false.
1329
+ */
1330
+
1331
+ VALUE Archive_exist(VALUE self)
1332
+ {
1333
+ return rb_funcall(rb_cFile,rb_intern("exist?"),1,Archive_path(self));
1334
+ }
1335
+
1336
+ /*:nodoc:
1337
+ * call-seq:
1338
+ * archive.unlink -> self
1339
+ *
1340
+ * call the File.unlink(path)
1341
+ */
1342
+
1343
+ VALUE Archive_unlink(VALUE self)
1344
+ {
1345
+ return rb_funcall(rb_cFile,rb_intern("unlink"),1,Archive_path(self));
1346
+ return self;
1347
+ }
1348
+
1349
+ /*:nodoc:
1350
+ * call-seq:
1351
+ * archive.mtime -> Time
1352
+ *
1353
+ * call the File.mtime(path)
1354
+ */
1355
+
1356
+ VALUE Archive_mtime(VALUE self)
1357
+ {
1358
+ return rb_funcall(rb_cFile,rb_intern("mtime"),1,Archive_path(self));
1359
+ }
1360
+
1361
+ /*:nodoc:
1362
+ * call-seq:
1363
+ * archive.atime -> Time
1364
+ *
1365
+ * call the File.atime(path)
1366
+ */
1367
+
1368
+ VALUE Archive_atime(VALUE self)
1369
+ {
1370
+ return rb_funcall(rb_cFile,rb_intern("atime"),1,Archive_path(self));
1371
+ }
1372
+
1373
+ /*:nodoc:
1374
+ * call-seq:
1375
+ * archive.ctime -> Time
1376
+ *
1377
+ * call the File.ctime(path)
1378
+ */
1379
+
1380
+
1381
+ VALUE Archive_ctime(VALUE self)
1382
+ {
1383
+ return rb_funcall(rb_cFile,rb_intern("ctime"),1,Archive_path(self));
1384
+ }
1385
+
1386
+ /*:nodoc:
1387
+ * call-seq:
1388
+ * archive.stat -> File::Stat
1389
+ *
1390
+ * call the File.stat(path)
1391
+ */
1392
+
1393
+ VALUE Archive_stat(VALUE self)
1394
+ {
1395
+ return rb_funcall(rb_cFile,rb_intern("stat"),1,Archive_path(self));
1396
+ }
1397
+
1398
+ /*
1399
+ * call-seq:
1400
+ * archive.inspect -> String
1401
+ *
1402
+ * Human-readable description.
1403
+ * ===Return value
1404
+ * String
1405
+ */
1406
+
1407
+ VALUE Archive_inspect(VALUE self)
1408
+ {
1409
+ VALUE array[3];
1410
+ switch(_self->type){
1411
+ case archive_path:
1412
+ array[0]=rb_str_new2("#<%s:%s>");
1413
+ array[1]=rb_class_of(self);
1414
+ array[2]=Archive_path(self);
1415
+ break;
1416
+ case archive_fd:
1417
+ array[0]=rb_str_new2("#<%s:%d>");
1418
+ array[1]=rb_class_of(self);
1419
+ array[2]=INT2NUM(_self->fd);
1420
+ break;
1421
+ case archive_buffer:
1422
+ break;
1423
+ case archive_ruby:
1424
+ array[0]=rb_str_new2("#<%s:%s>");
1425
+ array[1]=rb_class_of(self);
1426
+ array[2]=_self->ruby;
1427
+ break;
1428
+ }
1429
+ return rb_f_sprintf(3,array);
1430
+ }
1431
+
1432
+ /*
1433
+ * Document-class: Archive::Error
1434
+ * This is the superclass of all errors specific to this library.
1435
+ */
1436
+
1437
+ /*
1438
+ * Document-class: Archive::Error::Compression
1439
+ * This exception is thrown if you try to use an unknown compression format.
1440
+ */
1441
+
1442
+ /*
1443
+ * Document-class: Archive::Error::Format
1444
+ * This exception is thrown if you try to use an unknown archive format or libarchive doesn't
1445
+ * have write support for the format you wanted to write.
1446
+ */
1447
+
1448
+ /*
1449
+ * Document-class: Archive
1450
+ *
1451
+ * This class represents an archive file. The file may or may not exist,
1452
+ * depending on wheather you want to create a new archive or read from
1453
+ * an existing one. When instanciating this class, libarchive-ruby will
1454
+ * automatically detect the correct file format for you using libarchive's
1455
+ * own detection mechanism if the archive file is already present, otherwise
1456
+ * by looking at the archive file's file extension.
1457
+ */
1458
+ /*
1459
+ * Document-const: EXTRACT_TIME
1460
+ *
1461
+ * extract the atime and mtime
1462
+ */
1463
+ /*
1464
+ * Document-const: EXTRACT_PERM
1465
+ *
1466
+ * extract the permission
1467
+ */
1468
+ /*
1469
+ * Document-const: EXTRACT_OWNER
1470
+ *
1471
+ * extract the owner
1472
+ */
1473
+
1474
+ /*
1475
+ * Document-const: EXTRACT_ACL
1476
+ *
1477
+ * extract the access control list
1478
+ */
1479
+ /*
1480
+ * Document-const: EXTRACT_FFLAGS
1481
+ *
1482
+ * extract the fflags
1483
+ */
1484
+ /*
1485
+ * Document-const: EXTRACT_XATTR
1486
+ *
1487
+ * extract the extended information
1488
+ */
1489
+ extern "C" void Init_archive(void){
1490
+ rb_cArchive = rb_define_class("Archive",rb_cObject);
1491
+ rb_define_alloc_func(rb_cArchive,Archive_alloc);
1492
+ rb_define_method(rb_cArchive,"initialize",RUBY_METHOD_FUNC(Archive_initialize),-1);
1493
+ rb_define_private_method(rb_cArchive,"initialize_copy",RUBY_METHOD_FUNC(Archive_initialize_copy),1);
1494
+ rb_define_method(rb_cArchive,"path",RUBY_METHOD_FUNC(Archive_path),0);
1495
+
1496
+ rb_define_method(rb_cArchive,"each",RUBY_METHOD_FUNC(Archive_each),0);
1497
+ rb_define_method(rb_cArchive,"each_entry",RUBY_METHOD_FUNC(Archive_each_entry),0);
1498
+ rb_define_method(rb_cArchive,"each_data",RUBY_METHOD_FUNC(Archive_each_data),0);
1499
+ rb_define_method(rb_cArchive,"map!",RUBY_METHOD_FUNC(Archive_map_self),0);
1500
+ rb_define_alias(rb_cArchive,"collect!","map!");
1501
+ rb_define_method(rb_cArchive,"[]",RUBY_METHOD_FUNC(Archive_get),1);
1502
+
1503
+ rb_define_method(rb_cArchive,"to_hash",RUBY_METHOD_FUNC(Archive_to_hash),0);
1504
+
1505
+ rb_define_method(rb_cArchive,"extract",RUBY_METHOD_FUNC(Archive_extract),-1);
1506
+ rb_define_method(rb_cArchive,"extract_if",RUBY_METHOD_FUNC(Archive_extract_if),-1);
1507
+
1508
+ rb_define_method(rb_cArchive,"delete",RUBY_METHOD_FUNC(Archive_delete),1);
1509
+ rb_define_method(rb_cArchive,"delete_if",RUBY_METHOD_FUNC(Archive_delete_if),0);
1510
+ rb_define_method(rb_cArchive,"clear",RUBY_METHOD_FUNC(Archive_clear),0);
1511
+
1512
+ //rb_define_method(rb_cArchive,"move_to",RUBY_METHOD_FUNC(Archive_move_to),1);
1513
+
1514
+ rb_define_method(rb_cArchive,"add",RUBY_METHOD_FUNC(Archive_add),2);
1515
+ rb_define_method(rb_cArchive,"<<",RUBY_METHOD_FUNC(Archive_add_shift),1);
1516
+
1517
+ rb_define_method(rb_cArchive,"inspect",RUBY_METHOD_FUNC(Archive_inspect),0);
1518
+
1519
+ rb_define_method(rb_cArchive,"format",RUBY_METHOD_FUNC(Archive_format),0);
1520
+ rb_define_method(rb_cArchive,"compression",RUBY_METHOD_FUNC(Archive_compression),0);
1521
+ rb_define_method(rb_cArchive,"format_name",RUBY_METHOD_FUNC(Archive_format_name),0);
1522
+ rb_define_method(rb_cArchive,"compression_name",RUBY_METHOD_FUNC(Archive_compression_name),0);
1523
+
1524
+ rb_define_method(rb_cArchive,"unlink",RUBY_METHOD_FUNC(Archive_unlink),0);
1525
+ rb_define_method(rb_cArchive,"exist?",RUBY_METHOD_FUNC(Archive_exist),0);
1526
+ rb_define_method(rb_cArchive,"mtime",RUBY_METHOD_FUNC(Archive_mtime),0);
1527
+ rb_define_method(rb_cArchive,"atime",RUBY_METHOD_FUNC(Archive_atime),0);
1528
+ rb_define_method(rb_cArchive,"ctime",RUBY_METHOD_FUNC(Archive_ctime),0);
1529
+ rb_define_method(rb_cArchive,"stat",RUBY_METHOD_FUNC(Archive_stat),0);
1530
+
1531
+ rb_include_module(rb_cArchive,rb_mEnumerable);
1532
+ //rb_define_method(rb_cArchive,"size",RUBY_METHOD_FUNC(Archive_size),0);
1533
+
1534
+ Init_archive_entry(rb_cArchive);
1535
+
1536
+ rb_define_const(rb_cArchive,"EXTRACT_TIME",INT2NUM(ARCHIVE_EXTRACT_TIME));
1537
+ rb_define_const(rb_cArchive,"EXTRACT_OWNER",INT2NUM(ARCHIVE_EXTRACT_OWNER));
1538
+ rb_define_const(rb_cArchive,"EXTRACT_PERM",INT2NUM(ARCHIVE_EXTRACT_PERM));
1539
+ rb_define_const(rb_cArchive,"EXTRACT_NO_OVERWRITE",INT2NUM(ARCHIVE_EXTRACT_NO_OVERWRITE));
1540
+ rb_define_const(rb_cArchive,"EXTRACT_ACL",INT2NUM(ARCHIVE_EXTRACT_ACL));
1541
+ rb_define_const(rb_cArchive,"EXTRACT_FFLAGS",INT2NUM(ARCHIVE_EXTRACT_FFLAGS));
1542
+ rb_define_const(rb_cArchive,"EXTRACT_XATTR",INT2NUM(ARCHIVE_EXTRACT_XATTR));
1543
+
1544
+ rb_eArchiveError = rb_define_class_under(rb_cArchive,"Error",rb_eStandardError);
1545
+ rb_eArchiveErrorFormat = rb_define_class_under(rb_eArchiveError,"Format",rb_eArchiveError);
1546
+ rb_eArchiveErrorCompression = rb_define_class_under(rb_eArchiveError,"Compression",rb_eArchiveError);
1547
+ }