core.bmx 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252
  1. ' Copyright (c) 2022-2023 Bruce A Henderson
  2. ' All rights reserved.
  3. '
  4. ' Redistribution and use in source and binary forms, with or without
  5. ' modification, are permitted provided that the following conditions are met:
  6. ' * Redistributions of source code must retain the above copyright
  7. ' notice, this list of conditions and the following disclaimer.
  8. ' * Redistributions in binary form must reproduce the above copyright
  9. ' notice, this list of conditions and the following disclaimer in the
  10. ' documentation and/or other materials provided with the distribution.
  11. '
  12. ' THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY
  13. ' EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  14. ' WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  15. ' DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
  16. ' DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  17. ' (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  18. ' LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  19. ' ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  20. ' (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  21. ' SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  22. '
  23. SuperStrict
  24. Rem
  25. bbdoc: Streaming Archive Library
  26. End Rem
  27. Module Archive.Core
  28. ModuleInfo "Version: 1.08"
  29. ModuleInfo "License: BSD"
  30. ModuleInfo "Copyright: libarchive - 2003-2018 Tim Kientzle"
  31. ModuleInfo "Copyright: Wrapper - 2013-2023 Bruce A Henderson"
  32. ModuleInfo "History: 1.08"
  33. ModuleInfo "History: Update to libarchive 3.7.0."
  34. ModuleInfo "History: 1.07"
  35. ModuleInfo "History: Update to libarchive 3.6.2.2d32907."
  36. ModuleInfo "History: Added DataStream() to TWriteArchive, returning a writeable TStream."
  37. ModuleInfo "History: 1.06"
  38. ModuleInfo "History: Update to libarchive 3.5.2.b967588."
  39. ModuleInfo "History: 1.05"
  40. ModuleInfo "History: Update to libarchive 3.3.4.458e493."
  41. ModuleInfo "History: 1.04"
  42. ModuleInfo "History: Update to libarchive 3.3.4.c16ce12."
  43. ModuleInfo "History: 1.03"
  44. ModuleInfo "History: Added zstd support."
  45. ModuleInfo "History: 1.02"
  46. ModuleInfo "History: Update to libarchive 3.3.2.77d26b0."
  47. ModuleInfo "History: 1.01"
  48. ModuleInfo "History: Removed libiconv requirement from Win32."
  49. ModuleInfo "History: 1.00"
  50. ModuleInfo "History: Initial Release. libarchive 3.1.2"
  51. ModuleInfo "CC_OPTS: -DHAVE_CONFIG_H -D_FILE_OFFSET_BITS=64"
  52. ?win32
  53. ModuleInfo "CC_OPTS: -DLIBARCHIVE_STATIC"
  54. ?
  55. Import "common.bmx"
  56. Import BRL.Stream
  57. Import BRL.FileSystem
  58. Rem
  59. bbdoc: Archive base type.
  60. End Rem
  61. Type TArchive
  62. Field archivePtr:Byte Ptr
  63. Rem
  64. bbdoc: Clears any error information left over from a previous call.
  65. about: Not generally used in client code.
  66. End Rem
  67. Method ClearError()
  68. bmx_libarchive_archive_clear_error(archivePtr)
  69. End Method
  70. Rem
  71. bbdoc: Returns a numeric error code indicating the reason for the most recent error return.
  72. about: Note that this can not be reliably used to detect whether an error has occurred.
  73. It should be used only after another libarchive function has returned an error status.
  74. End Rem
  75. Method Errno:Int()
  76. Return bmx_libarchive_archive_errno(archivePtr)
  77. End Method
  78. Rem
  79. bbdoc: Returns a textual error message suitable for display.
  80. about: The error message here is usually more specific than that obtained from passing the result of archive_errno() to strerror(3).
  81. End Rem
  82. Method ErrorString:String()
  83. Return bmx_libarchive_archive_error_string(archivePtr)
  84. End Method
  85. Rem
  86. bbdoc: Returns a count of the number of files processed by this archive object.
  87. about: The count is incremented by calls to WriteHeader or ReadNextHeader.
  88. End Rem
  89. Method FileCount:Int()
  90. Return bmx_libarchive_archive_file_count(archivePtr)
  91. End Method
  92. Rem
  93. bbdoc: Returns a numeric code identifying the indicated filter.
  94. about: See FilterCount() for details of the numbering.
  95. End Rem
  96. Method FilterCode:Int(filter:Int)
  97. Return bmx_libarchive_archive_filter_code(archivePtr, filter)
  98. End Method
  99. Rem
  100. bbdoc: Returns the number of filters in the current pipeline.
  101. about: For read archive handles, these filters are added automatically by the automatic format detection.
  102. For write archive handles, these filters are added by calls to the various AddFilterXXX() methods. Filters in the
  103. resulting pipeline are numbered so that filter 0 is the filter closest to the format handler. As a convenience,
  104. methods that expect a filter number will accept -1 as a synonym for the highest-numbered filter.
  105. <p>
  106. For example, when reading a uuencoded gzipped tar archive, there are three filters: filter 0 is the gunzip filter, filter 1 is the uudecode
  107. filter, and filter 2 is the pseudo-filter that wraps the archive read methods. In this case, requesting Position(-1) would be a synonym
  108. for Position(2) which would return the number of bytes currently read from the archive, while Position(1) would return the number of
  109. bytes after uudecoding, and Position(0) would return the number of bytes after decompression.
  110. </p>
  111. End Rem
  112. Method FilterCount:Int()
  113. Return bmx_libarchive_archive_filter_count(archivePtr)
  114. End Method
  115. Rem
  116. bbdoc: Returns a textual name identifying the indicated filter.
  117. about: See FilterCount() for details of the numbering.
  118. End Rem
  119. Method FilterName:String(filter:Int)
  120. Return bmx_libarchive_archive_filter_name(archivePtr, filter)
  121. End Method
  122. Rem
  123. bbdoc: Returns a numeric code indicating the format of the current archive entry.
  124. about: This value is set by a successful call to ReadNextHeader(). Note that it is common for this value to
  125. change from entry to entry. For example, a tar archive might have several entries that utilize GNU
  126. tar extensions and several entries that do not. These entries will have different format codes.
  127. End Rem
  128. Method Format:Int()
  129. Return bmx_libarchive_archive_format(archivePtr)
  130. End Method
  131. Rem
  132. bbdoc: A textual description of the format of the current entry.
  133. End Rem
  134. Method FormatName:String()
  135. Return bmx_libarchive_archive_format_name(archivePtr)
  136. End Method
  137. Rem
  138. bbdoc: Returns the number of bytes read from or written to the indicated filter.
  139. about: In particular, Position(0) returns the number of bytes read or written by the format handler,
  140. while Position(-1) returns the number of bytes read or written to the archive. See FilterCount() for details of the numbering here.
  141. End Rem
  142. Method Position:Long(filter:Int)
  143. Local v:Long
  144. bmx_libarchive_archive_position(archivePtr, filter, Varptr v)
  145. Return v
  146. End Method
  147. Method Data:Int(data:Byte Ptr, size:Size_T) Abstract
  148. Method Free() Abstract
  149. Method Delete()
  150. Free()
  151. End Method
  152. End Type
  153. Rem
  154. bbdoc: A readable archive.
  155. about: This is used for extracting data from existing archives.
  156. End Rem
  157. Type TReadArchive Extends TArchive
  158. Field callbackData:TArchiveCallbackData
  159. Field passphraseCallback:String(archive:TReadArchive, data:Object, cancel:Int Var)
  160. Field passphraseCallbackData:Object
  161. Field StaticArray pw:Byte[1024]
  162. Function CreateArchive:TReadArchive()
  163. Return New TReadArchive()
  164. End Function
  165. Rem
  166. bbdoc: Creates a new instance of #TReadArchive.
  167. End Rem
  168. Method New()
  169. archivePtr = bmx_libarchive_read_archive_new()
  170. End Method
  171. Rem
  172. bbdoc: Creates a new instance of #TReadArchive with the specified format and filters.
  173. End Rem
  174. Method New(format:EArchiveFormat, filters:EArchiveFilter[])
  175. New()
  176. SetFormat(format)
  177. For Local filter:EArchiveFilter = Eachin filters
  178. AddFilter(filter)
  179. Next
  180. End Method
  181. Rem
  182. bbdoc: Registers a passphrase for the archive.
  183. End Rem
  184. Method AddPassphrase:Int(passphrase:String)
  185. Return bmx_libarchive_archive_read_add_passphrase(archivePtr, passphrase)
  186. End Method
  187. Private
  188. Function _passphraseCallback:Byte Ptr(archive:TReadArchive) { nomangle }
  189. Local cancel:Int
  190. Local pass:String = archive.passphraseCallback(archive, archive.passphraseCallbackData, cancel)
  191. If cancel Then
  192. Return Null
  193. Else
  194. Local length:Size_T = 1024
  195. Return pass.ToUTF8StringBuffer(archive.pw, length)
  196. End If
  197. End Function
  198. Public
  199. Rem
  200. bbdoc: Registers a callback function that will be invoked to get a passphrase for decryption after trying all the passphrases registered by #AddPassphrase.
  201. End Rem
  202. Method SetPassphraseCallback:Int(callback:String(archive:TReadArchive, data:Object, cancel:Int Var), data:Object)
  203. Local cb:Byte Ptr = callback
  204. If Not cb Then
  205. passphraseCallback = Null
  206. passphraseCallbackData = Null
  207. Return bmx_libarchive_archive_read_set_passphrase_callback(archivePtr, Null)
  208. Else
  209. passphraseCallback = callback
  210. passphraseCallbackData = data
  211. Return bmx_libarchive_archive_read_set_passphrase_callback(archivePtr, self)
  212. End If
  213. End Method
  214. Rem
  215. bbdoc: Returns #True if the archive has encrypted entries.
  216. End Rem
  217. Method HasEncryptedEntries:Int()
  218. Return bmx_libarchive_archive_read_has_encrypted_entries(archivePtr)
  219. End Method
  220. Rem
  221. bbdoc: Opens archive for reading from a file of the given @filename, with a block size of @blockSize bytes.
  222. about: The file should be readable.
  223. End Rem
  224. Method Open:Int(filename:String, blockSize:Int = 10240)
  225. Local stream:TStream = ReadStream(filename)
  226. If Not stream Then
  227. Throw New TArchiveException("Unable to create stream for reading")
  228. End If
  229. Return Open(stream, blockSize, True)
  230. End Method
  231. Rem
  232. bbdoc: Opens archive for reading from memory.
  233. about: @size indicates the size of the archive in memory at location @buf.
  234. End Rem
  235. Method Open:Int(buf:Byte Ptr, size:Size_T)
  236. Return bmx_libarchive_archive_read_open_memory(archivePtr, buf, size)
  237. End Method
  238. Rem
  239. bbdoc: Opens archive for reading from a @stream, with a block size of @blockSize bytes.
  240. about: @stream should already be created and readable.
  241. If @closeAfterRead is set, the stream will be closed when the archive is closed.
  242. End Rem
  243. Method Open:Int(stream:TStream, blockSize:Int = 10240, closeAfterRead:Int = False)
  244. If Not stream Then
  245. Throw New TArchiveException("stream cannot be Null")
  246. End If
  247. If Not callbackData Then
  248. callbackData = New TArchiveCallbackData(stream, blockSize)
  249. Else
  250. callbackData.Update(stream, blockSize)
  251. End If
  252. callbackData.shouldClose = closeAfterRead
  253. Return bmx_libarchive_archive_read_open(archivePtr, callbackData)
  254. End Method
  255. Rem
  256. bbdoc: Read the header for the next entry and populate the provided entry
  257. returns: ARCHIVE_OK (the operation succeeded), ARCHIVE_WARN (the operation succeeded but a non-critical error was encountered), ARCHIVE_EOF (end-of-archive was encountered), ARCHIVE_RETRY (the operation failed but can be retried), and ARCHIVE_FATAL (there was a fatal error; the archive should be closed immediately).
  258. End Rem
  259. Method ReadNextHeader:Int(entry:TArchiveEntry)
  260. Return bmx_libarchive_archive_read_next_header(archivePtr, entry.entryPtr)
  261. End Method
  262. Rem
  263. bbdoc: A convenience method that repeatedly skips all of the data for this archive entry.
  264. End Rem
  265. Method DataSkip:Int()
  266. Return bmx_libarchive_archive_read_data_skip(archivePtr)
  267. End Method
  268. Rem
  269. bbdoc: Read data associated with the header just read.
  270. returns: A count of bytes actually read or zero at the end of the entry. On error, a value of #ARCHIVE_FATAL, #ARCHIVE_WARN, or #ARCHIVE_RETRY is returned.
  271. End Rem
  272. Method Data:Int(buf:Byte Ptr, size:Size_T) Override
  273. Return bmx_libarchive_archive_read_data(archivePtr, buf, size)
  274. End Method
  275. Rem
  276. bbdoc: Returns a readable #TStream of the current entry data.
  277. about: This can be used by any #TStream supported functionality.
  278. End Rem
  279. Method DataStream:TStream()
  280. Return New TArchiveInputStream(Self)
  281. End Method
  282. Rem
  283. bbdoc: Adds a filter for reading.
  284. End Rem
  285. Method AddFilter(filter:EArchiveFilter)
  286. ArchiveAddReadFilter(archivePtr, filter)
  287. End Method
  288. Rem
  289. bbdoc: Sets a format for reading.
  290. End Rem
  291. Method SetFormat(format:EArchiveFormat)
  292. ArchiveSetReadFormat(archivePtr, format)
  293. End Method
  294. Rem
  295. bbdoc: Sets a filter option.
  296. End Rem
  297. Method SetFilterOption:Int(option:String, value:String = Null, moduleName:String = Null)
  298. Return bmx_libarchive_archive_read_set_filter_option(archivePtr, option, value, moduleName)
  299. End Method
  300. Rem
  301. bbdoc: Sets a format option.
  302. End Rem
  303. Method SetFormatOption:Int(option:String, value:String = Null, moduleName:String = Null)
  304. Return bmx_libarchive_archive_read_set_format_option(archivePtr, option, value, moduleName)
  305. End Method
  306. Method Free() Override
  307. If archivePtr Then
  308. bmx_libarchive_archive_read_free(archivePtr)
  309. archivePtr = Null
  310. End If
  311. End Method
  312. End Type
  313. Rem
  314. bbdoc: Data from a TReadArchive entry as a stream.
  315. End Rem
  316. Type TArchiveInputStream Extends TStream
  317. Field archive:TArchive
  318. Field _eof:Int
  319. Method New(archive:TArchive)
  320. Self.archive = archive
  321. End Method
  322. Method Read:Long( buf:Byte Ptr,count:Long )
  323. Local size:Long = archive.Data(buf, Size_T(count))
  324. If size <= 0 Then
  325. _eof = True
  326. If size < 0 Then
  327. size = 0
  328. End If
  329. End If
  330. Return size
  331. End Method
  332. Method ReadBytes:Long( buf:Byte Ptr,count:Long )
  333. Local t:Long=0
  334. While count>0
  335. Local n:Long=Read( buf,count )
  336. If Not n Exit
  337. count:-n
  338. buf:+n
  339. t:+ n
  340. Wend
  341. Return t
  342. End Method
  343. Method Eof:Int()
  344. Return _eof
  345. End Method
  346. End Type
  347. Type TArchiveOutputStream Extends TStream
  348. Field archive:TWriteArchive
  349. Field _eof:Int
  350. Method New(archive:TWriteArchive)
  351. Self.archive = archive
  352. End Method
  353. Method Write:Long( buf:Byte Ptr,count:Long )
  354. Return archive.Data(buf, Size_T(count))
  355. End Method
  356. Method WriteBytes:Long( buf:Byte Ptr,count:Long )
  357. Local t:Long=count
  358. While count>0
  359. Local n:Long=Write( buf,count )
  360. If Not n Exit
  361. count:-n
  362. buf:+n
  363. Wend
  364. Return t
  365. End Method
  366. End Type
  367. Type TArchiveCallbackData
  368. Field data:Byte[]
  369. Field stream:TStream
  370. Field shouldClose:Int
  371. Method New(stream:TStream, blockSize:Int = 10240)
  372. Self.data = New Byte[blockSize]
  373. Self.stream = stream
  374. End Method
  375. Function _read:Byte Ptr(cbData:Object, count:Long Var) { nomangle }
  376. Local data:TArchiveCallbackData = TArchiveCallbackData(cbData)
  377. Local buf:Byte Ptr = data.data
  378. Local c:Long = data.data.Length
  379. Local ncount:Long
  380. While c
  381. Local n:Long = data.stream.Read(buf, c)
  382. If Not n Then
  383. Exit
  384. End If
  385. c:-n
  386. ncount:+ n
  387. buf:+n
  388. Wend
  389. count = ncount
  390. Return data.data
  391. End Function
  392. Function _seek(cbData:Object, offset:Long, whence:Int, count:Long Var) { nomangle }
  393. Local data:TArchiveCallbackData = TArchiveCallbackData(cbData)
  394. Select whence
  395. Case SEEK_SET_
  396. count = data.stream.seek(offset)
  397. Case SEEK_CUR_
  398. count = data.stream.seek(offset + data.stream.pos())
  399. Case SEEK_END_
  400. count = data.stream.seek(data.stream.size())
  401. End Select
  402. End Function
  403. Function _write(cbData:Object, buf:Byte Ptr, length:Size_T, count:Long Var) { nomangle }
  404. Local data:TArchiveCallbackData = TArchiveCallbackData(cbData)
  405. count = data.stream.Write(buf, length)
  406. End Function
  407. Function _close:Int(cbData:Object) { nomangle }
  408. Local data:TArchiveCallbackData = TArchiveCallbackData(cbData)
  409. data.Close()
  410. Return ARCHIVE_OK
  411. End Function
  412. Method Update(stream:TStream, size:Int)
  413. Self.stream = stream
  414. data = data[..size]
  415. End Method
  416. Method Close()
  417. If shouldClose Then
  418. stream.Close()
  419. End If
  420. End Method
  421. Method Delete()
  422. data = Null
  423. End Method
  424. End Type
  425. Rem
  426. bbdoc: A writeable archive.
  427. End Rem
  428. Type TWriteArchive Extends TArchive
  429. Field callbackData:TArchiveCallbackData
  430. Function CreateArchive:TWriteArchive()
  431. Return New TWriteArchive()
  432. End Function
  433. Rem
  434. bbdoc: Creates a new instance of #TWriteArchive.
  435. End Rem
  436. Method New()
  437. archivePtr = bmx_libarchive_write_archive_new()
  438. End Method
  439. Rem
  440. bbdoc: Creates a new instance of #TWriteArchive with the specified format and filters.
  441. End Rem
  442. Method New(format:EArchiveFormat, filters:EArchiveFilter[])
  443. New()
  444. SetFormat(format)
  445. For Local filter:EArchiveFilter = Eachin filters
  446. AddFilter(filter)
  447. Next
  448. End Method
  449. Rem
  450. bbdoc: Opens archive for writing to a file of the given @filename.
  451. about: The file should be writeable.
  452. End Rem
  453. Method Open:Int(filename:String)
  454. Local stream:TStream = WriteStream(filename)
  455. If Not stream Then
  456. Throw New TArchiveException("Unable to create stream for writing")
  457. End If
  458. Return Open(stream, True)
  459. End Method
  460. Rem
  461. bbdoc: Opens archive for writing to memory.
  462. about: The @size should be large enough for the final archive.
  463. After writing, @used will be populated with the total bytes used, so the pointer should remain valid until then.
  464. End Rem
  465. Method Open:Int(buf:Byte Ptr, size:Int, used:Size_T Ptr)
  466. Return bmx_libarchive_archive_write_open_memory(archivePtr, buf, size, Varptr used)
  467. End Method
  468. Rem
  469. bbdoc: Opens archive for writing to a @stream.
  470. about: @stream should already be created and writeable.
  471. If @closeAfterWrite is set, the stream will be closed once the write completes.
  472. End Rem
  473. Method Open:Int(stream:TStream, closeAfterWrite:Int = False)
  474. If Not stream Then
  475. Throw New TArchiveException("stream cannot be Null")
  476. End If
  477. If Not callbackData Then
  478. callbackData = New TArchiveCallbackData(stream, 0)
  479. Else
  480. callbackData.Update(stream, 0)
  481. End If
  482. callbackData.shouldClose = closeAfterWrite
  483. Return bmx_libarchive_archive_write_open(archivePtr, callbackData)
  484. End Method
  485. Rem
  486. bbdoc: Convience method for adding a file or #TStream to the archive.
  487. returns: #False if adding the entry was abandoned, #True otherwise.
  488. about: If adding a #TStream, you will also need to provide @pathname, @size and @ftime values.
  489. By default, files are added with the permission '0644' (decimal 420).
  490. @pathname can also be used to define the path (include sub directories) of the file within the archive - this
  491. is also dependent on support from the archive in question.
  492. @fileType will
  493. End Rem
  494. Method AddEntry:Int(file:Object, pathname:String = Null, size:Long = 0, ftime:Long = 0, fileType:EArchiveFileType = EArchiveFileType.File, progress:IArchiveWriteProgress = Null)
  495. If String(file) Then
  496. If Not pathname Then
  497. pathname = StripDir(String(file))
  498. End If
  499. If Not ftime Then
  500. ftime = FileTime(String(file))
  501. End If
  502. End If
  503. If Not pathname Then
  504. Throw New TArchiveException("Pathname is required. Unable to determine pathname from input")
  505. End If
  506. If fileType <> EArchiveFileType.File And fileType <> EArchiveFileType.Dir Then
  507. Throw New TArchiveException("fileType must be a File or a Dir.")
  508. End If
  509. Local stream:TStream
  510. If fileType = EArchiveFileType.File Then
  511. stream = ReadStream(file)
  512. If Not stream Then
  513. Throw New TArchiveException("Unabled to open stream for input file for path '" + pathname + "'")
  514. End If
  515. If Not size Then
  516. size = stream.Size()
  517. End If
  518. End If
  519. Local entry:TArchiveEntry = New TArchiveEntry
  520. entry.SetPathname(pathname)
  521. entry.SetFileType(fileType)
  522. Local stopped:Int = False
  523. If fileType = EArchiveFileType.File Then
  524. entry.SetPermission(420) ' 0644
  525. entry.SetSize(size)
  526. End If
  527. If ftime Then
  528. entry.SetModifiedTime(ftime)
  529. End If
  530. If Header(entry) <> ARCHIVE_OK Then
  531. Throw New TArchiveException(String.FromUTF8String(archive_error_string(archivePtr)))
  532. End If
  533. If fileType = EArchiveFileType.File Then
  534. Local StaticArray buf:Byte[16384]
  535. Local count:Long = size
  536. While count
  537. Local nCount:Long = stream.Read(buf, Min(count, buf.Length))
  538. If nCount <= 0 Then
  539. Exit
  540. End If
  541. Data(buf, Size_T(nCount))
  542. count :- nCount
  543. If progress And size > 0 Then
  544. Local p:Float = Float(size - count) / Float(size)
  545. If Not progress.Update(p) Then
  546. stopped = True
  547. Exit
  548. End If
  549. End If
  550. Wend
  551. If Not TStream(file) Then
  552. stream.Close()
  553. End If
  554. End If
  555. FinishEntry()
  556. entry.Free()
  557. Return (Not stopped)
  558. End Method
  559. Rem
  560. bbdoc: Build and write a header using the data in the provided in the TArchiveEntry object.
  561. about: See TArchiveEntry for information on creating and populating such objects.
  562. End Rem
  563. Method Header:Int(entry:TArchiveEntry)
  564. Return bmx_libarchive_archive_write_header(archivePtr, entry.entryPtr)
  565. End Method
  566. Rem
  567. bbdoc: Closes the archive.
  568. End Rem
  569. Method Close:Int()
  570. Return bmx_libarchive_archive_write_close(archivePtr)
  571. End Method
  572. Rem
  573. bbdoc: Informs the archive that the current archive entry is finished.
  574. End Rem
  575. Method FinishEntry:Int()
  576. Return bmx_libarchive_archive_write_finish_entry(archivePtr)
  577. End Method
  578. Rem
  579. bbdoc: Writes @size bytes of data to the current archive entry.
  580. End Rem
  581. Method Data:Int(data:Byte Ptr, size:Size_T) Override
  582. Return bmx_libarchive_archive_write_data(archivePtr, data, size)
  583. End Method
  584. Rem
  585. bbdoc: Returns a writeable #TStream.
  586. about: This can be used by any #TStream supported functionality.
  587. End Rem
  588. Method DataStream:TStream()
  589. Return New TArchiveOutputStream(Self)
  590. End Method
  591. Rem
  592. bbdoc: Sets a passphrase for the archive.
  593. End Rem
  594. Method SetPassphrase:Int(passphrase:String)
  595. Return bmx_libarchive_archive_write_set_passphrase(archivePtr, passphrase)
  596. End Method
  597. Rem
  598. bbdoc: Adds a filter for writing.
  599. End Rem
  600. Method AddFilter(filter:EArchiveFilter)
  601. ArchiveAddWriteFilter(archivePtr, filter)
  602. End Method
  603. Rem
  604. bbdoc: Adds a format for writing.
  605. End Rem
  606. Method SetFormat(format:EArchiveFormat)
  607. ArchiveSetWriteFormat(archivePtr, format)
  608. End Method
  609. Rem
  610. bbdoc: Sets a filter option.
  611. End Rem
  612. Method SetFilterOption:Int(option:String, value:String = Null, moduleName:String = Null)
  613. Return bmx_libarchive_archive_write_set_filter_option(archivePtr, option, value, moduleName)
  614. End Method
  615. Rem
  616. bbdoc: Sets a format option.
  617. End Rem
  618. Method SetFormatOption:Int(option:String, value:String = Null, moduleName:String = Null)
  619. Return bmx_libarchive_archive_write_set_format_option(archivePtr, option, value, moduleName)
  620. End Method
  621. Rem
  622. bbdoc: Sets the compression level for a compatible format/filter.
  623. returns: #ARCHIVE_OK on success, or an error code.
  624. about:
  625. | Type | Supported Compression Level | Notes |
  626. |---------|:---------------------------:|-------------------------------------------------------------------------------------------------------------------------------------------|
  627. | bzip2 | 1 - 9 | |
  628. | gzip | 0 - 9 | |
  629. | lz4 | 0 - 9 | |
  630. | lzop | 1 - 9 | |
  631. | xz | 0 - 9 | |
  632. | zstd | 1 - 22 | |
  633. | 7zip | 0 - 9 | |
  634. | iso9660 | 0 - 9 (default 6) | |
  635. | zip | 0 - 9 | A compression level of 0 switches the compression method to "store", other values will enable "deflate" compression with the given level. |
  636. End Rem
  637. Method SetCompressionLevel:Int(level:Int)
  638. Return bmx_libarchive_archive_write_set_option(archivePtr, "compression-level", String(level), Null)
  639. End Method
  640. Rem
  641. bbdoc: Sets the compression type for a compatible format/filter.
  642. returns: #ARCHIVE_OK on success, or an error code.
  643. about:
  644. | Type | Notes |
  645. |------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
  646. | 7zip | The value is one of "store", "deflate", "bzip2", "lzma1", "lzma2" or "ppmd" to indicate how the following entries should be compressed.<br>Note that this setting is ignored for directories, symbolic links, and other special entries. |
  647. | zip | The value is either "store" or "deflate" to indicate how the following entries should be compressed.<br>Note that this setting is ignored for directories, symbolic links, and other special entries. |
  648. End Rem
  649. Method SetCompressionType:Int(compressionType:String)
  650. Return bmx_libarchive_archive_write_set_option(archivePtr, "compression", compressionType, Null)
  651. End Method
  652. Rem
  653. bbdoc: Sets the encryption type.
  654. about: Only applies to Zip archives.
  655. End Rem
  656. Method SetEncryption:Int(encryptionType:EArchiveEncryptionType)
  657. Local ty:String
  658. Select encryptionType
  659. Case EArchiveEncryptionType.Zip
  660. ty = "zipcrypt"
  661. Case EArchiveEncryptionType.AES128
  662. ty = "aes128"
  663. Case EArchiveEncryptionType.AES256
  664. ty = "aes256"
  665. End Select
  666. If Not ty Then
  667. Return ARCHIVE_FAILED
  668. End If
  669. Return bmx_libarchive_archive_write_set_option(archivePtr, "encryption", ty, Null)
  670. End Method
  671. Rem
  672. bbdoc: Frees the archive.
  673. End Rem
  674. Method Free() Override
  675. If archivePtr Then
  676. bmx_libarchive_archive_write_free(archivePtr)
  677. archivePtr = Null
  678. End If
  679. End Method
  680. End Type
  681. Rem
  682. bbdoc: Represents entries within an archive.
  683. about: You can think of a #TArchiveEntry as a heavy-duty version of the c struct stat: it includes everything from struct stat plus
  684. associated pathname, textual group and user names, etc. These objects are used by libarchive to represent the metadata associated
  685. with a particular entry in an archive.
  686. End Rem
  687. Type TArchiveEntry
  688. Field entryPtr:Byte Ptr
  689. Function _create:TArchiveEntry(entryPtr:Byte Ptr)
  690. If entryPtr Then
  691. Local this:TArchiveEntry = New TArchiveEntry(entryPtr)
  692. Return this
  693. End If
  694. Return Null
  695. End Function
  696. Rem
  697. bbdoc: Creates a new #TArchiveEntry instance.
  698. End Rem
  699. Method New()
  700. entryPtr = bmx_libarchive_archive_entry_new()
  701. SetFileType(EArchiveFileType.File)
  702. End Method
  703. Rem
  704. bbdoc: Creates a new #TArchiveEntry instance using the specified @entryPtr handle.
  705. End Rem
  706. Method New(entryPtr:Byte Ptr)
  707. Self.entryPtr = entryPtr
  708. End Method
  709. Rem
  710. bbdoc: Returns the entry access time, as an #SDateTime.
  711. End Rem
  712. Method AccessDateTime:SDateTime()
  713. Return SDateTime.FromEpoch(bmx_libarchive_archive_entry_atime(entryPtr), bmx_libarchive_archive_entry_atime_nsec(entryPtr))
  714. End Method
  715. Rem
  716. bbdoc: Returns the entry access time, if set.
  717. End Rem
  718. Method AccessTime:Long()
  719. return bmx_libarchive_archive_entry_atime(entryPtr)
  720. End Method
  721. Rem
  722. bbdoc: Returns the entry access time, in nanoseconds.
  723. End Rem
  724. Method AccessTimeNano:Long()
  725. Return bmx_libarchive_archive_entry_atime_nsec(entryPtr)
  726. End Method
  727. Rem
  728. bbdoc: Returns #True if the access time is set.
  729. End Rem
  730. Method AccessTimeIsSet:Int()
  731. Return bmx_libarchive_archive_entry_atime_is_set(entryPtr)
  732. End Method
  733. Rem
  734. bbdoc: Sets the access time for the entry.
  735. End Rem
  736. Method SetAccessTime(atime:Long, nanoseconds:Long = 0)
  737. bmx_libarchive_archive_entry_set_atime(entryPtr, atime, nanoseconds)
  738. End Method
  739. Rem
  740. bbdoc: Unsets entry access time.
  741. End Rem
  742. Method UnsetAccessTime()
  743. bmx_libarchive_archive_entry_unset_atime(entryPtr)
  744. End Method
  745. Rem
  746. bbdoc: Returns the entry birth time, as an #SDateTime.
  747. End Rem
  748. Method BirthDateTime:SDateTime()
  749. Return SDateTime.FromEpoch(bmx_libarchive_archive_entry_birthtime(entryPtr), bmx_libarchive_archive_entry_birthtime_nsec(entryPtr))
  750. End Method
  751. Rem
  752. bbdoc: Returns the entry birth time, if set.
  753. End Rem
  754. Method BirthTime:Long()
  755. return bmx_libarchive_archive_entry_birthtime(entryPtr)
  756. End Method
  757. Rem
  758. bbdoc: Returns the entry birth time, in nanoseconds.
  759. End Rem
  760. Method BirthTimeNano:Long()
  761. return bmx_libarchive_archive_entry_birthtime_nsec(entryPtr)
  762. End Method
  763. Rem
  764. bbdoc: Returns #True if the birth time is set.
  765. End Rem
  766. Method BirthTimeIsSet:Int()
  767. return bmx_libarchive_archive_entry_birthtime_is_set(entryPtr)
  768. End Method
  769. Rem
  770. bbdoc: Sets the birth time for the entry.
  771. End Rem
  772. Method SetBirthTime(btime:Long, nanoseconds:Long = 0)
  773. bmx_libarchive_archive_entry_set_birthtime(entryPtr, btime, nanoseconds)
  774. End Method
  775. Rem
  776. bbdoc: Unsets the birth time of the entry.
  777. End Rem
  778. Method UnsetBirthTime()
  779. bmx_libarchive_archive_entry_unset_birthtime(entryPtr)
  780. End Method
  781. Rem
  782. bbdoc: Erases the object, resetting all internal fields to the same state as a newly-created object.
  783. about: This is provided to allow you to quickly recycle objects.
  784. End Rem
  785. Method Clear()
  786. entryPtr = bmx_libarchive_archive_entry_clear(entryPtr)
  787. End Method
  788. Rem
  789. bbdoc: A deep copy operation; all text fields are duplicated.
  790. End Rem
  791. Method Clone:TArchiveEntry()
  792. Return TArchiveEntry._create(bmx_libarchive_archive_entry_clone(entryPtr))
  793. End Method
  794. Rem
  795. bbdoc: Returns the entry inode change time, as an #SDateTime.
  796. End Rem
  797. Method CDateTime:SDateTime()
  798. Return SDateTime.FromEpoch(bmx_libarchive_archive_entry_ctime(entryPtr), bmx_libarchive_archive_entry_ctime_nsec(entryPtr))
  799. End Method
  800. Rem
  801. bbdoc: Returns the entry inode change time, if set.
  802. End Rem
  803. Method CTime:Long()
  804. return bmx_libarchive_archive_entry_ctime(entryPtr)
  805. End Method
  806. Rem
  807. bbdoc: Returns the entry inode change time, in nanoseconds.
  808. End Rem
  809. Method CTimeNano:Long()
  810. return bmx_libarchive_archive_entry_ctime_nsec(entryPtr)
  811. End Method
  812. Rem
  813. bbdoc: Returns #True if the inode change time is set.
  814. End Rem
  815. Method CTimeIsSet:Int()
  816. return bmx_libarchive_archive_entry_ctime_is_set(entryPtr)
  817. End Method
  818. Rem
  819. bbdoc: Unsets the inode change time.
  820. End Rem
  821. Method UnsetCTime()
  822. bmx_libarchive_archive_entry_unset_ctime(entryPtr)
  823. End Method
  824. Rem
  825. bbdoc: Returns the entry file type.
  826. End Rem
  827. Method FileType:EArchiveFileType()
  828. Return bmx_libarchive_archive_entry_filetype(entryPtr)
  829. End Method
  830. Rem
  831. bbdoc: Destination of the hardlink.
  832. End Rem
  833. Method Hardlink:String()
  834. Return bmx_libarchive_archive_entry_hardlink(entryPtr)
  835. End Method
  836. Rem
  837. bbdoc: Returns #True if the entry data is encrypted.
  838. End Rem
  839. Method IsDataEncrypted:Int()
  840. Return bmx_libarchive_archive_entry_is_data_encrypted(entryPtr)
  841. End Method
  842. Rem
  843. bbdoc: Returns #True if the entry metadata is encrypted.
  844. End Rem
  845. Method IsMetadataEncrypted:Int()
  846. Return bmx_libarchive_archive_entry_is_metadata_encrypted(entryPtr)
  847. End Method
  848. Rem
  849. bbdoc: Returns #True if the entry is encrypted.
  850. End Rem
  851. Method IsEncrypted:Int()
  852. Return bmx_libarchive_archive_entry_is_encrypted(entryPtr)
  853. End Method
  854. Rem
  855. bbdoc: Path in the archive.
  856. End Rem
  857. Method Pathname:String()
  858. Return bmx_libarchive_archive_entry_pathname(entryPtr)
  859. End Method
  860. Rem
  861. bbdoc: Destination of the symbolic link.
  862. End Rem
  863. Method Symlink:String()
  864. Return bmx_libarchive_archive_entry_symlink(entryPtr)
  865. End Method
  866. Rem
  867. bbdoc: Sets the creation time of the entry.
  868. End Rem
  869. Method SetCreationTime(time:Long, nanoseconds:Long = 0)
  870. bmx_libarchive_archive_entry_set_ctime(entryPtr, time, nanoseconds)
  871. End Method
  872. Rem
  873. bbdoc: Sets the file type of the entry.
  874. End Rem
  875. Method SetFileType(fType:EArchiveFileType)
  876. bmx_libarchive_archive_entry_set_filetype(entryPtr, fType)
  877. End Method
  878. Rem
  879. bbdoc:
  880. End Rem
  881. Method SetHardlink(path:String)
  882. End Method
  883. Rem
  884. bbdoc: For a symlink, update the destination, otherwise, make the entry a hardlink and alter the destination for that.
  885. about: Update only.
  886. End Rem
  887. Method SetLink(path:String)
  888. bmx_libarchive_archive_entry_set_link(entryPtr, path)
  889. End Method
  890. Rem
  891. bbdoc: Returns the entry modified time, as an #SDateTime.
  892. End Rem
  893. Method ModifiedDateTime:SDateTime()
  894. Return SDateTime.FromEpoch(bmx_libarchive_archive_entry_mtime(entryPtr), bmx_libarchive_archive_entry_mtime_nsec(entryPtr))
  895. End Method
  896. Rem
  897. bbdoc: Sets the modified time of the entry.
  898. End Rem
  899. Method SetModifiedTime(time:Long, nanoseconds:Long = 0)
  900. bmx_libarchive_archive_entry_set_mtime(entryPtr, time, nanoseconds)
  901. End Method
  902. Rem
  903. bbdoc: Returns the entry modified time, if set.
  904. End Rem
  905. Method ModifiedTime:Long()
  906. return bmx_libarchive_archive_entry_mtime(entryPtr)
  907. End Method
  908. Rem
  909. bbdoc: Returns the entry modified time, in nanoseconds.
  910. End Rem
  911. Method ModifiedTimeNano:Long()
  912. return bmx_libarchive_archive_entry_mtime_nsec(entryPtr)
  913. End Method
  914. Rem
  915. bbdoc: Returns #True if the modified time is set.
  916. End Rem
  917. Method ModifiedTimeIsSet:Int()
  918. return bmx_libarchive_archive_entry_mtime_is_set(entryPtr)
  919. End Method
  920. Rem
  921. bbdoc: Unsets the modified time.
  922. End Rem
  923. Method UnsetModifiedTime()
  924. bmx_libarchive_archive_entry_unset_mtime(entryPtr)
  925. End Method
  926. Rem
  927. bbdoc: Sets the pathname of the entry.
  928. End Rem
  929. Method SetPathname(path:String)
  930. bmx_libarchive_archive_entry_set_pathname(entryPtr, path)
  931. End Method
  932. Rem
  933. bbdoc: Sets the permission of the entry.
  934. End Rem
  935. Method SetPermission(perm:Int)
  936. bmx_libarchive_archive_entry_set_perm(entryPtr, perm)
  937. End Method
  938. Rem
  939. bbdoc: Sets the size, in bytes, of the entry.
  940. End Rem
  941. Method SetSize(size:Long)
  942. bmx_libarchive_archive_entry_set_size(entryPtr, size)
  943. End Method
  944. Rem
  945. bbdoc:
  946. End Rem
  947. Method SetSymlink(path:String)
  948. bmx_libarchive_archive_entry_set_symlink(entryPtr, path)
  949. End Method
  950. Rem
  951. bbdoc: Returns true if the size is set.
  952. End Rem
  953. Method SizeIsSet:Int()
  954. Return bmx_libarchive_archive_entry_size_is_set(entryPtr)
  955. End Method
  956. Rem
  957. bbdoc: Returns the uncompressed size, in bytes, of the entry, if available.
  958. End Rem
  959. Method Size:Long()
  960. Return bmx_libarchive_archive_entry_size(entryPtr)
  961. End Method
  962. Rem
  963. bbdoc: Unsets the size of the entry.
  964. End Rem
  965. Method UnsetSize()
  966. bmx_libarchive_archive_entry_unset_size(entryPtr)
  967. End Method
  968. Rem
  969. bbdoc: Releases the archive entry object.
  970. End Rem
  971. Method Free()
  972. If entryPtr Then
  973. bmx_libarchive_archive_entry_free(entryPtr)
  974. entryPtr = Null
  975. End If
  976. End Method
  977. Method Delete()
  978. Free()
  979. End Method
  980. End Type
  981. Private
  982. Global _archive_formats:TArchiveFormat
  983. Function ArchiveSetReadFormat(archive:Byte Ptr, format:EArchiveFormat)
  984. Local af:TArchiveFormat=_archive_formats
  985. While af
  986. If af.AddReadFormat(archive, format) Then
  987. Return
  988. End If
  989. af=af._succ
  990. Wend
  991. Throw New TArchiveException("Read format not found. Ensure the module has been imported : " + format.ToString())
  992. End Function
  993. Function ArchiveSetWriteFormat(archive:Byte Ptr, format:EArchiveFormat)
  994. Local af:TArchiveFormat=_archive_formats
  995. While af
  996. If af.AddWriteFormat(archive, format) Then
  997. Return
  998. End If
  999. af=af._succ
  1000. Wend
  1001. Throw New TArchiveException("Write format not found. Ensure the module has been imported : " + format.ToString())
  1002. End Function
  1003. Function ArchiveAddReadFilter(archive:Byte Ptr, filter:EArchiveFilter)
  1004. Local af:TArchiveFormat=_archive_formats
  1005. While af
  1006. If af.AddReadFilter(archive, filter) Then
  1007. Return
  1008. End If
  1009. af=af._succ
  1010. Wend
  1011. Throw New TArchiveException("Read filter not found. Ensure the module has been imported : " + filter.ToString())
  1012. End Function
  1013. Function ArchiveAddWriteFilter(archive:Byte Ptr, filter:EArchiveFilter)
  1014. Local af:TArchiveFormat=_archive_formats
  1015. While af
  1016. If af.AddWriteFilter(archive, filter) Then
  1017. Return
  1018. End If
  1019. af=af._succ
  1020. Wend
  1021. Throw New TArchiveException("Write filter not found. Ensure the module has been imported : " + filter.ToString())
  1022. End Function
  1023. Public
  1024. Type TArchiveFormat
  1025. Field _succ:TArchiveFormat
  1026. Method New()
  1027. _succ=_archive_formats
  1028. _archive_formats=Self
  1029. End Method
  1030. Method AddReadFormat:Int(archive:Byte Ptr, format:EArchiveFormat) Abstract
  1031. Method AddWriteFormat:Int(archive:Byte Ptr, format:EArchiveFormat) Abstract
  1032. Method AddReadFilter:Int(archive:Byte Ptr, filter:EArchiveFilter) Abstract
  1033. Method AddWriteFilter:Int(archive:Byte Ptr, filter:EArchiveFilter) Abstract
  1034. End Type
  1035. Type TArchiveException Extends TRuntimeException
  1036. End Type
  1037. Rem
  1038. bbdoc: An archive entry write progress indicator.
  1039. about: This is used to report the progress of writing an archive entry.
  1040. End Rem
  1041. Interface IArchiveWriteProgress
  1042. Rem
  1043. bbdoc: Called when the entry is being written.
  1044. about: @progress is a value between 0 and 1.
  1045. Should return #True to continue, or #False to abort.
  1046. End Rem
  1047. Method Update:Int(progress:Float)
  1048. End Interface