ffi_libarchive 1.1.0

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.
@@ -0,0 +1,182 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Archive
4
+ # Stream filter Codes
5
+ FILTER_NONE = 0
6
+ FILTER_GZIP = 1
7
+ FILTER_BZIP2 = 2
8
+ FILTER_COMPRESS = 3
9
+ FILTER_PROGRAM = 4
10
+ FILTER_LZMA = 5
11
+ FILTER_XZ = 6
12
+ FILTER_UU = 7
13
+ FILTER_RPM = 8
14
+ FILTER_LZIP = 9
15
+ FILTER_LRZIP = 10
16
+ FILTER_LZOP = 11
17
+ FILTER_GRZIP = 12
18
+ FILTER_LZ4 = 13
19
+ FILTER_ZSTD = 14
20
+ # endregion
21
+
22
+ # region Compression Codes (deprecated)
23
+ COMPRESSION_NONE = FILTER_NONE
24
+ COMPRESSION_GZIP = FILTER_GZIP
25
+ COMPRESSION_BZIP2 = FILTER_BZIP2
26
+ COMPRESSION_COMPRESS = FILTER_COMPRESS
27
+ COMPRESSION_PROGRAM = FILTER_PROGRAM
28
+ COMPRESSION_LZMA = FILTER_LZMA
29
+ COMPRESSION_XZ = FILTER_XZ
30
+ COMPRESSION_UU = FILTER_UU
31
+ COMPRESSION_RPM = FILTER_RPM
32
+ COMPRESSION_LZIP = FILTER_LZIP
33
+ COMPRESSION_LRZIP = FILTER_LRZIP
34
+ # endregion
35
+
36
+ # region Format Codes
37
+ FORMAT_BASE_MASK = 0xff0000
38
+ FORMAT_CPIO = 0x10000
39
+ FORMAT_CPIO_POSIX = (FORMAT_CPIO | 1)
40
+ FORMAT_CPIO_BIN_LE = (FORMAT_CPIO | 2)
41
+ FORMAT_CPIO_BIN_BE = (FORMAT_CPIO | 3)
42
+ FORMAT_CPIO_SVR4_NOCRC = (FORMAT_CPIO | 4)
43
+ FORMAT_CPIO_SVR4_CRC = (FORMAT_CPIO | 5)
44
+ FORMAT_CPIO_AFIO_LARGE = (FORMAT_CPIO | 6)
45
+ FORMAT_SHAR = 0x20000
46
+ FORMAT_SHAR_BASE = (FORMAT_SHAR | 1)
47
+ FORMAT_SHAR_DUMP = (FORMAT_SHAR | 2)
48
+ FORMAT_TAR = 0x30000
49
+ FORMAT_TAR_USTAR = (FORMAT_TAR | 1)
50
+ FORMAT_TAR_PAX_INTERCHANGE = (FORMAT_TAR | 2)
51
+ FORMAT_TAR_PAX_RESTRICTED = (FORMAT_TAR | 3)
52
+ FORMAT_TAR_GNUTAR = (FORMAT_TAR | 4)
53
+ FORMAT_ISO9660 = 0x40000
54
+ FORMAT_ISO9660_ROCKRIDGE = (FORMAT_ISO9660 | 1)
55
+ FORMAT_ZIP = 0x50000
56
+ FORMAT_EMPTY = 0x60000
57
+ FORMAT_AR = 0x70000
58
+ FORMAT_AR_GNU = (FORMAT_AR | 1)
59
+ FORMAT_AR_BSD = (FORMAT_AR | 2)
60
+ FORMAT_MTREE = 0x80000
61
+ FORMAT_RAW = 0x90000
62
+ FORMAT_XAR = 0xA0000
63
+ FORMAT_LHA = 0xB0000
64
+ FORMAT_CAB = 0xC0000
65
+ FORMAT_RAR = 0xD0000
66
+ FORMAT_7ZIP = 0xE0000
67
+ FORMAT_WARC = 0xF0000
68
+ FORMAT_RAR_V5 = 0x100000
69
+ # endregion
70
+
71
+ # region Extraction Flags
72
+ EXTRACT_OWNER = 0x0001
73
+ EXTRACT_PERM = 0x0002
74
+ EXTRACT_TIME = 0x0004
75
+ EXTRACT_NO_OVERWRITE = 0x0008
76
+ EXTRACT_UNLINK = 0x0010
77
+ EXTRACT_ACL = 0x0020
78
+ EXTRACT_FFLAGS = 0x0040
79
+ EXTRACT_XATTR = 0x0080
80
+ EXTRACT_SECURE_SYMLINKS = 0x0100
81
+ EXTRACT_SECURE_NODOTDOT = 0x0200
82
+ EXTRACT_NO_AUTODIR = 0x0400
83
+ EXTRACT_NO_OVERWRITE_NEWER = 0x0800
84
+ EXTRACT_SPARSE = 0x1000
85
+ EXTRACT_MAC_METADATA = 0x2000
86
+ EXTRACT_NO_HFS_COMPRESSION = 0x4000
87
+ EXTRACT_HFS_COMPRESSION_FORCED = 0x8000
88
+ EXTRACT_SECURE_NOABSOLUTEPATHS = 0x10000
89
+ EXTRACT_CLEAR_NOCHANGE_FFLAGS = 0x20000
90
+ # endregion
91
+
92
+ class << self
93
+ # @param [String] file_name
94
+ # @return [Reader]
95
+ # @yieldparam [Reader]
96
+ def read_open_filename(file_name, command = nil, &block)
97
+ Reader.open_filename file_name, command, &block
98
+ end
99
+
100
+ # @param [String] string
101
+ # @return [Reader]
102
+ # @yieldparam [Reader]
103
+ def read_open_memory(string, command = nil, &block)
104
+ Reader.open_memory string, command, &block
105
+ end
106
+
107
+ # @param [#call] reader
108
+ # @return [Reader]
109
+ # @yieldparam [Reader]
110
+ def read_open_stream(reader, command = nil, &block)
111
+ Reader.open_stream reader, command, &block
112
+ end
113
+
114
+ # @param [String] file_name
115
+ # @return [Writer]
116
+ # @yieldparam [Writer]
117
+ def write_open_filename(file_name, compression, format, &block)
118
+ Writer.open_filename file_name, compression, format, &block
119
+ end
120
+
121
+ # @param [String] string
122
+ # @return [Writer]
123
+ # @yieldparam [Writer]
124
+ def write_open_memory(string, compression, format, &block)
125
+ Writer.open_memory string, compression, format, &block
126
+ end
127
+
128
+ # @return [Integer]
129
+ def version_number
130
+ C.archive_version_number
131
+ end
132
+
133
+ # @return [String]
134
+ def version_string
135
+ C.archive_version_string
136
+ end
137
+ end
138
+
139
+ class Error < StandardError
140
+ # noinspection RubyNilAnalysis
141
+ def initialize(obj = nil)
142
+ super(obj.respond_to?(:error_string) ? obj.error_string : obj)
143
+ end
144
+ end
145
+
146
+ # @abstract
147
+ class BaseArchive
148
+ # @param [Method] alloc
149
+ # @param [Method] free
150
+ def initialize(alloc, free)
151
+ raise ArgumentError, 'Invalid methods' unless alloc.respond_to?(:call) && free.respond_to?(:call)
152
+
153
+ @archive = alloc.call
154
+ raise Error, 'No archive open' unless @archive
155
+
156
+ @archive_free = [free]
157
+ ObjectSpace.define_finalizer(self, method(:close).to_proc)
158
+ end
159
+
160
+ def close
161
+ # TODO: do we need synchronization here?
162
+ @archive_free[0].call(@archive) if @archive && @archive_free[0].respond_to?(:call) # TODO: Error check?
163
+ ensure
164
+ @archive = nil
165
+ @archive_free[0] = nil
166
+ end
167
+
168
+ # @!visibility protected
169
+ # @return [FFI::Pointer]
170
+ attr_reader :archive
171
+
172
+ # @return [String]
173
+ def error_string
174
+ C.archive_error_string(archive)
175
+ end
176
+
177
+ # @return [Integer]
178
+ def errno
179
+ C.archive_errno(archive)
180
+ end
181
+ end
182
+ end
@@ -0,0 +1,575 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Archive
4
+ class Entry
5
+ # region File-type Constants
6
+
7
+ S_IFMT = 0o170000 # bits mask
8
+ S_IFSOCK = 0o140000
9
+ S_IFLNK = 0o120000
10
+ S_IFREG = 0o100000
11
+ S_IFBLK = 0o060000
12
+ S_IFDIR = 0o040000
13
+ S_IFCHR = 0o020000
14
+ S_IFIFO = 0o010000
15
+
16
+ SOCKET = S_IFSOCK
17
+ SYMBOLIC_LINK = S_IFLNK
18
+ FILE = S_IFREG # regular file
19
+ BLOCK_DEVICE = S_IFBLK # block special device
20
+ DIRECTORY = S_IFDIR
21
+ CHARACTER_DEVICE = S_IFCHR # character special device
22
+ FIFO = S_IFIFO # named pipe (FIFO)
23
+
24
+ def self.file_types
25
+ @file_types ||= Hash[constants.reject { |k| k =~ /^S_/ }.map { |k| [k.downcase, const_get(k)] }]
26
+ end
27
+
28
+ # endregion
29
+
30
+ # @param [FFI::Pointer]
31
+ # @return [Entry]
32
+ def self.from_pointer(entry)
33
+ new entry
34
+ end
35
+
36
+ # @param [FFI::Pointer] entry
37
+ def initialize(entry = nil)
38
+ if entry
39
+ @entry = entry
40
+ else
41
+ @entry = C.archive_entry_new
42
+ raise Error, 'No entry object' unless @entry
43
+ end
44
+
45
+ if block_given?
46
+ begin
47
+ yield self
48
+ ensure
49
+ close
50
+ end
51
+ else
52
+ ObjectSpace.define_finalizer(self, method(:close).to_proc)
53
+ end
54
+ end
55
+
56
+ def close
57
+ # TODO: do we need synchronization here?
58
+ C.archive_entry_free(@entry) if @entry
59
+ ensure
60
+ @entry = nil
61
+ end
62
+
63
+ # @return [FFI::Pointer]
64
+ attr_reader :entry
65
+
66
+ class << self
67
+ protected
68
+
69
+ # @param [Symbol] api API method
70
+ # @param [Hash] opts
71
+ # @option opts [#to_sym] :name Method name
72
+ # @option opts [Boolean] :maybe Skip undefined method?
73
+ # @option opts [#call] :post The result transformer
74
+ # @option opts [#call] :pre The arguments processor
75
+ def attach_attribute(api, opts = {})
76
+ opts[:name] ||= api.to_s.sub(/^archive_entry_/, '').sub(/_is_.*$/, '\0?')
77
+
78
+ define_method(opts[:name].to_sym) do |*args|
79
+ opts[:pre].call(args) if opts[:pre].respond_to?(:call)
80
+
81
+ result = C.respond_to?(api) || !opts[:maybe] ? C.send(api, *args.unshift(entry)) : nil
82
+ opts[:post].respond_to?(:call) ? opts[:post].call(result) : result
83
+ end
84
+ end
85
+
86
+ def proc_time_at
87
+ @proc_time_at ||= Time.method(:at)
88
+ end
89
+
90
+ def proc_2_args_to_i
91
+ @proc_2_args_to_i ||= proc { |args| args.fill(0, args.size..1).map!(&:to_i) }
92
+ end
93
+
94
+ def proc_is_nonzero
95
+ @proc_is_nonzero ||= 0.method(:!=)
96
+ end
97
+
98
+ def proc_read_wide_string
99
+ @proc_read_wide_string ||= Utils.method(:read_wide_string)
100
+ end
101
+
102
+ def proc_string_arg_to_wide
103
+ @proc_string_arg_to_wide ||= proc { |args| args.map! { |s| Utils.to_wide_string s } }
104
+ end
105
+ end
106
+
107
+ # region Access time
108
+ # @!method atime
109
+ # @return [Time]
110
+ attach_attribute :archive_entry_atime, post: proc_time_at
111
+
112
+ # @!method set_atime(time, nsec = 0)
113
+ # @param [Time, #to_i] time
114
+ # @param [Integer, #to_i] nsec
115
+ attach_attribute :archive_entry_set_atime, pre: proc_2_args_to_i
116
+
117
+ alias atime= set_atime
118
+
119
+ # @!method atime_is_set?
120
+ # @return [Boolean]
121
+ attach_attribute :archive_entry_atime_is_set, post: proc_is_nonzero
122
+
123
+ # @!method atime_nsec
124
+ # @return [Integer] :long
125
+ attach_attribute :archive_entry_atime_nsec
126
+
127
+ # @!method unset_atime
128
+ attach_attribute :archive_entry_unset_atime
129
+ # endregion
130
+
131
+ # region Creation time
132
+ # @!method birthtime
133
+ # @return [Time]
134
+ attach_attribute :archive_entry_birthtime, post: proc_time_at
135
+
136
+ # @!method set_birthtime(time, nsec = 0)
137
+ # @param [Time, #to_i] time
138
+ # @param [Integer, #to_i] nsec
139
+ attach_attribute :archive_entry_set_birthtime, pre: proc_2_args_to_i
140
+
141
+ alias birthtime= set_birthtime
142
+
143
+ # @!method birthtime_is_set?
144
+ # @return [Boolean]
145
+ attach_attribute :archive_entry_birthtime_is_set, post: proc_is_nonzero
146
+
147
+ # @!method birthtime_nsec
148
+ # @return [Integer] :long
149
+ attach_attribute :archive_entry_birthtime_nsec
150
+
151
+ # @!method unset_birthtime
152
+ attach_attribute :archive_entry_unset_birthtime
153
+ # endregion
154
+
155
+ # region Change time
156
+ # @!method ctime
157
+ # @return [Time]
158
+ attach_attribute :archive_entry_ctime, post: proc_time_at
159
+
160
+ # @!method set_ctime(time, nsec = 0)
161
+ # @param [Time, #to_i] time
162
+ # @param [Integer, #to_i] nsec
163
+ attach_attribute :archive_entry_set_ctime, pre: proc_2_args_to_i
164
+
165
+ alias ctime= set_ctime
166
+
167
+ # @!method ctime_is_set?
168
+ # @return [Boolean]
169
+ attach_attribute :archive_entry_ctime_is_set, post: proc_is_nonzero
170
+
171
+ # @!method ctime_nsec
172
+ # @return [Integer] :long
173
+ attach_attribute :archive_entry_ctime_nsec
174
+
175
+ # @!method unset_ctime
176
+ attach_attribute :archive_entry_unset_ctime
177
+ # endregion
178
+
179
+ # region File-type
180
+
181
+ # @!method filetype
182
+ # @return [Integer] :mode_t
183
+ attach_attribute :archive_entry_filetype
184
+
185
+ # @!method filetype=(type)
186
+ # @param [Integer, #to_s] type
187
+ attach_attribute(
188
+ :archive_entry_set_filetype,
189
+ name: 'filetype=', pre: ->(args) { args.map! { |t| t.is_a?(Integer) ? t : const_get(t.to_s.upcase) } }
190
+ )
191
+
192
+ # @!method block_device?
193
+ # @return [Boolean]
194
+ # @!method character_device?
195
+ # @return [Boolean]
196
+ # @!method directory?
197
+ # @return [Boolean]
198
+ # @!method fifo?
199
+ # @return [Boolean]
200
+ # @!method file?
201
+ # @return [Boolean]
202
+ # @!method socket?
203
+ # @return [Boolean]
204
+ # @!method symbolic_link?
205
+ # @return [Boolean]
206
+ file_types.each do |k, v|
207
+ define_method("#{k}?".to_sym) { (filetype & S_IFMT) == v }
208
+ end
209
+
210
+ alias regular? file?
211
+ alias block_special? block_device?
212
+ alias character_special? character_device?
213
+
214
+ # @return [Symbol]
215
+ def filetype_s
216
+ self.class.file_types.key(filetype & S_IFMT)
217
+ end
218
+
219
+ # endregion
220
+
221
+ # region File status
222
+
223
+ # @!method stat
224
+ # @return [FFI::Pointer]
225
+ attach_attribute :archive_entry_stat
226
+
227
+ # @param [String, FFI::Pointer] filename
228
+ def copy_lstat(filename)
229
+ copy_stat_from(filename.is_a?(String) ? File.lstat(filename) : filename)
230
+ end
231
+
232
+ # @param [String, FFI::Pointer] filename
233
+ def copy_stat(filename)
234
+ copy_stat_from(filename.is_a?(String) ? File.stat(filename) : filename)
235
+ end
236
+
237
+ # @private
238
+ # @param [FFI::Pointer, File::Stat] stat
239
+ def copy_stat_from(stat)
240
+ if stat.respond_to?(:null?) && !stat.null?
241
+ C.archive_entry_copy_stat(entry, stat)
242
+
243
+ elsif stat.is_a?(File::Stat)
244
+ %w[dev gid uid ino nlink rdev size mode].each do |fn|
245
+ # @type [Integer]
246
+ f = stat.send(fn)
247
+ send "#{fn}=", f if f
248
+ end
249
+
250
+ %w[atime ctime mtime birthtime].each do |fn|
251
+ # @type [Time]
252
+ f = stat.respond_to?(fn) ? stat.send(fn) : nil
253
+ send "set_#{fn}", f, f.tv_nsec if f
254
+ end
255
+ else
256
+ raise ArgumentError, "Copying stat for #{stat.class} is not supported"
257
+ end
258
+ end
259
+
260
+ # endregion
261
+
262
+ # region Device number
263
+ # @!method dev
264
+ # @return [Integer] :dev_t
265
+ attach_attribute :archive_entry_dev
266
+
267
+ # @!method dev=(dev)
268
+ # @param [Integer] dev
269
+ attach_attribute :archive_entry_set_dev, name: 'dev='
270
+
271
+ # @!method devmajor
272
+ # @return [Integer] :dev_t
273
+ attach_attribute :archive_entry_devmajor
274
+
275
+ # @!method devmajor=(dev)
276
+ # @param [Integer] dev
277
+ attach_attribute :archive_entry_set_devmajor, name: 'devmajor='
278
+
279
+ # @!method devminor
280
+ # @return [Integer] :dev_t
281
+ attach_attribute :archive_entry_devminor
282
+
283
+ # @!method devminor=(dev)
284
+ # @param [Integer] dev
285
+ attach_attribute :archive_entry_set_devminor, name: 'devminor='
286
+ # endregion
287
+
288
+ # region File flags/attributes (see #lsattr)
289
+ # @return [Array<Integer>] of [:set, :clear]
290
+ def fflags
291
+ set = FFI::MemoryPointer.new :ulong
292
+ clear = FFI::MemoryPointer.new :ulong
293
+ C.archive_entry_fflags(entry, set, clear)
294
+
295
+ [set.get_ulong(0), clear.get_ulong(0)]
296
+ end
297
+
298
+ # @!method set_fflags(set, clear)
299
+ # @param [Integer] set
300
+ # @param [Integer] clear
301
+ attach_attribute :archive_entry_set_fflags
302
+
303
+ # @!method fflags_text
304
+ # @return [String]
305
+ attach_attribute :archive_entry_fflags_text
306
+
307
+ # @!method copy_fflags_text(fflags_text)
308
+ # @param [String] fflags_text
309
+ # @return [String] Invalid token string, or NULL if success
310
+ attach_attribute :archive_entry_copy_fflags_text
311
+
312
+ alias fflags_text= copy_fflags_text
313
+ # endregion
314
+
315
+ # region Group ownership
316
+ # @!method gid
317
+ # @return [Integer] :int64_t
318
+ attach_attribute :archive_entry_gid
319
+
320
+ # @!method gid=(gid)
321
+ # @param [Integer] gid
322
+ attach_attribute :archive_entry_set_gid, name: 'gid='
323
+
324
+ # @!method gname
325
+ # @return [String]
326
+ attach_attribute :archive_entry_gname
327
+
328
+ # @!method gname=(gname)
329
+ # @param [String] gname
330
+ attach_attribute :archive_entry_set_gname, name: 'gname='
331
+
332
+ # @!method copy_gname(gname)
333
+ # @param [String] gname
334
+ attach_attribute :archive_entry_copy_gname
335
+ # endregion
336
+
337
+ # region Links
338
+
339
+ # @!method hardlink
340
+ # @return [String]
341
+ attach_attribute :archive_entry_hardlink
342
+
343
+ # @!method hardlink=(lnk)
344
+ # @param [String] lnk
345
+ attach_attribute :archive_entry_set_hardlink, name: 'hardlink='
346
+
347
+ # @!method copy_hardlink(lnk)
348
+ # @param [String] lnk
349
+ attach_attribute :archive_entry_copy_hardlink
350
+
351
+ # @!method link=(lnk)
352
+ # @param [String] lnk
353
+ attach_attribute :archive_entry_set_link, name: 'link='
354
+
355
+ # @!method copy_link(lnk)
356
+ # @param [String] lnk
357
+ attach_attribute :archive_entry_copy_link
358
+
359
+ # @!method symlink
360
+ # @return [String]
361
+ attach_attribute :archive_entry_symlink
362
+
363
+ # @!method symlink=(lnk)
364
+ # @param [String] lnk
365
+ attach_attribute :archive_entry_set_symlink, name: 'symlink='
366
+
367
+ # @!method copy_symlink(lnk)
368
+ # @param [String] lnk
369
+ attach_attribute :archive_entry_copy_symlink
370
+
371
+ # endregion
372
+
373
+ # @!method ino
374
+ # @return [Integer] :int64_t of inode number
375
+ attach_attribute :archive_entry_ino
376
+
377
+ # @!method ino=(ino)
378
+ # @param [String] ino inode number
379
+ attach_attribute :archive_entry_set_ino, name: 'ino='
380
+
381
+ # region File permissions
382
+
383
+ # @!method mode
384
+ # @return [Integer] :mode_t
385
+ attach_attribute :archive_entry_mode
386
+
387
+ # @!method mode=(mode)
388
+ # @param [Integer] mode File protection (see #filetype)
389
+ attach_attribute :archive_entry_set_mode, name: 'mode='
390
+
391
+ # @!method perm
392
+ # @return [Integer] :mode_t
393
+ attach_attribute :archive_entry_perm
394
+
395
+ # @!method perm=(perm)
396
+ # @param [Integer] perm of :mode_t
397
+ attach_attribute :archive_entry_set_perm, name: 'perm='
398
+
399
+ # @!method strmode
400
+ # @return [String]
401
+ attach_attribute :archive_entry_strmode
402
+
403
+ # endregion
404
+
405
+ # region Modification time
406
+ # @!method mtime
407
+ # @return [Time]
408
+ attach_attribute :archive_entry_mtime, post: proc_time_at
409
+
410
+ # @!method set_mtime(time, nsec = 0)
411
+ # @param [Time, #to_i] time
412
+ # @param [Integer, #to_i] nsec
413
+ attach_attribute :archive_entry_set_mtime, pre: proc_2_args_to_i
414
+
415
+ alias mtime= set_mtime
416
+
417
+ # @!method mtime_is_set?
418
+ # @return [Boolean]
419
+ attach_attribute :archive_entry_mtime_is_set, post: proc_is_nonzero
420
+
421
+ # @!method mtime_nsec
422
+ # @return [Integer] :long
423
+ attach_attribute :archive_entry_mtime_nsec
424
+
425
+ # @!method unset_mtime
426
+ attach_attribute :archive_entry_unset_mtime
427
+ # endregion
428
+
429
+ # @!method nlink
430
+ # @return [Integer] :uint
431
+ attach_attribute :archive_entry_nlink
432
+
433
+ # @!method nlink=(nlink)
434
+ # @param [Integer] nlink Number of hard links / files in a directory
435
+ attach_attribute :archive_entry_set_nlink, name: 'nlink='
436
+
437
+ # region File path
438
+ # @!method pathname
439
+ # @return [String]
440
+ attach_attribute :archive_entry_pathname
441
+
442
+ # @!method pathname=(path)
443
+ # @param [String] path
444
+ attach_attribute :archive_entry_set_pathname, name: 'pathname='
445
+
446
+ # @!method pathname_w
447
+ # @return [String]
448
+ attach_attribute :archive_entry_pathname_w, maybe: true, post: proc_read_wide_string
449
+
450
+ # @!method pathname_w=(path)
451
+ # @param [String] path
452
+ attach_attribute :archive_entry_copy_pathname_w, name: 'pathname_w=', pre: proc_string_arg_to_wide
453
+
454
+ # @!method copy_pathname(file_name)
455
+ # @param [String] file_name
456
+ attach_attribute :archive_entry_copy_pathname
457
+ # endregion
458
+
459
+ # region Root device ID (if special?)
460
+ # @!method rdev
461
+ # @return [Integer] :dev_t
462
+ attach_attribute :archive_entry_rdev
463
+
464
+ # @!method rdev=(dev)
465
+ # @param [Integer] dev
466
+ attach_attribute :archive_entry_set_rdev, name: 'rdev='
467
+
468
+ # @!method rdevmajor
469
+ # @return [Integer] :dev_t
470
+ attach_attribute :archive_entry_rdevmajor
471
+
472
+ # @!method rdevmajor=(dev)
473
+ # @param [Integer] dev
474
+ attach_attribute :archive_entry_set_rdevmajor, name: 'rdevmajor='
475
+
476
+ # @!method rdevminor
477
+ # @return [Integer] :dev_t
478
+ attach_attribute :archive_entry_rdevminor
479
+
480
+ # @!method rdevminor=(dev)
481
+ # @param [Integer] dev
482
+ attach_attribute :archive_entry_set_rdevminor, name: 'rdevminor='
483
+ # endregion
484
+
485
+ # region File size
486
+
487
+ # @!method size
488
+ # @return [Integer] :int64_t
489
+ attach_attribute :archive_entry_size
490
+
491
+ # @!method size=(size)
492
+ # @param [Integer] size
493
+ attach_attribute :archive_entry_set_size, name: 'size='
494
+
495
+ # @!method size_is_set?
496
+ # @return [Boolean]
497
+ attach_attribute :archive_entry_size_is_set, post: proc_is_nonzero
498
+
499
+ # @!method unset_size
500
+ attach_attribute :archive_entry_unset_size
501
+
502
+ # endregion
503
+
504
+ # @!method sourcepath
505
+ # @return [String]
506
+ attach_attribute :archive_entry_sourcepath
507
+
508
+ # @!method copy_sourcepath(path)
509
+ # @param [String] path
510
+ attach_attribute :archive_entry_copy_sourcepath
511
+
512
+ alias sourcepath= copy_sourcepath
513
+
514
+ # region Ownership
515
+ # @!method uid
516
+ # @return [Integer] :int64_t
517
+ attach_attribute :archive_entry_uid
518
+
519
+ # @!method uid=(uid)
520
+ # @param [Integer] uid
521
+ attach_attribute :archive_entry_set_uid, name: 'uid='
522
+
523
+ # @!method uname
524
+ # @return [String]
525
+ attach_attribute :archive_entry_uname
526
+
527
+ # @!method uname=(uname)
528
+ # @param [String] uname
529
+ attach_attribute :archive_entry_set_uname, name: 'uname='
530
+
531
+ # @!method copy_uname(uname)
532
+ # @param [String] uname
533
+ attach_attribute :archive_entry_copy_uname
534
+ # endregion
535
+
536
+ # region Extended attributes
537
+
538
+ # @param [String] name
539
+ # @param [String] value
540
+ def xattr_add_entry(name, value)
541
+ raise ArgumentError, 'value is not a String' unless value.is_a?(String)
542
+
543
+ C.archive_entry_xattr_add_entry(entry, name, Utils.get_memory_ptr(value), value.bytesize)
544
+ end
545
+
546
+ # @!method xattr_clear
547
+ attach_attribute :archive_entry_xattr_clear
548
+
549
+ # @!method xattr_count
550
+ # @return [Integer]
551
+ attach_attribute :archive_entry_xattr_count
552
+
553
+ # @return [Array<String>] of [:name, :value]
554
+ def xattr_next
555
+ name = FFI::MemoryPointer.new :pointer
556
+ value = FFI::MemoryPointer.new :pointer
557
+ size = FFI::MemoryPointer.new :size_t
558
+ return nil if C.archive_entry_xattr_next(entry, name, value, size) != C::OK
559
+
560
+ name = name.get_pointer(0) unless name.null?
561
+ value = value.get_pointer(0) unless value.null?
562
+ # Someday size.get(:size_t) could work
563
+ [
564
+ name.null? ? nil : name.get_string(0),
565
+ value.null? ? nil : value.get_bytes(0, size.send("get_uint#{FFI.type_size(:size_t) * 8}", 0))
566
+ ]
567
+ end
568
+
569
+ # @!method xattr_reset
570
+ # @return [Integer]
571
+ attach_attribute :archive_entry_xattr_reset
572
+
573
+ # endregion
574
+ end
575
+ end