Horizon
zip.hpp
Go to the documentation of this file.
1 /*
2  * zip.hpp -- a safe modern C++ wrapper on top of libzip
3  *
4  * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr>
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #ifndef ZIP_HPP
20 #define ZIP_HPP
21 
29 #if defined(_MSC_VER)
30 # if !defined(_CRT_SECURE_NO_WARNINGS)
31 # define _CRT_SECURE_NO_WARNINGS
32 # endif
33 #endif
34 
35 #include <cassert>
36 #include <cerrno>
37 #include <cstring>
38 #include <functional>
39 #include <iterator>
40 #include <memory>
41 #include <string>
42 
43 #include <zip.h>
44 
48 namespace libzip {
49 
53 typedef struct zip_stat stat;
54 
58 using source_cmd = enum zip_source_cmd;
59 
63 using source_callback = zip_source_callback;
64 
68 using flags_t = zip_flags_t;
69 
73 using int8_t = zip_int8_t;
74 
78 using uint8_t = zip_uint8_t;
79 
83 using int16_t = zip_int16_t;
84 
88 using uint16_t = zip_uint16_t;
89 
93 using int32_t = zip_int32_t;
94 
98 using uint32_t = zip_uint32_t;
99 
103 using int64_t = zip_int64_t;
104 
108 using uint64_t = zip_uint64_t;
109 
122 using source = std::function<struct zip_source* (struct zip*)>;
123 
127 class file {
128 private:
129  std::unique_ptr<struct zip_file, int (*)(struct zip_file*)> handle_;
130 
131  file(const file&) = delete;
132  file& operator=(const file&) = delete;
133 
134 public:
140  inline file(struct zip_file* file) noexcept
141  : handle_(file, zip_fclose)
142  {
143  }
144 
150  file(file&& other) noexcept = default;
151 
157  file& operator=(file&&) noexcept = default;
158 
166  inline int read(void* data, uint64_t length) noexcept
167  {
168  return zip_fread(handle_.get(), data, length);
169  }
170 
177  template <size_t Size>
178  inline int read(char (&data)[Size]) noexcept
179  {
180  return read(data, Size);
181  }
182 
191  std::string read(uint64_t length)
192  {
193  std::string result;
194 
195  result.resize(length);
196  auto count = read(&result[0], length);
197 
198  if (count < 0)
199  return "";
200 
201  result.resize(count);
202 
203  return result;
204  }
205 };
206 
213 inline source source_buffer(std::string data) noexcept
214 {
215  return [data = std::move(data)] (struct zip* archive) -> struct zip_source* {
216  auto size = data.size();
217  auto ptr = static_cast<char*>(std::malloc(size));
218 
219  if (ptr == nullptr)
220  throw std::runtime_error(std::strerror(errno));
221 
222  std::memcpy(ptr, data.data(), size);
223 
224  auto src = zip_source_buffer(archive, ptr, size, 1);
225 
226  if (src == nullptr) {
227  std::free(ptr);
228  throw std::runtime_error(zip_strerror(archive));
229  }
230 
231  return src;
232  };
233 }
234 
243 inline source source_file(std::string path, uint64_t start = 0, int64_t length = -1) noexcept
244 {
245  return [path = std::move(path), start, length] (struct zip* archive) -> struct zip_source* {
246  auto src = zip_source_file(archive, path.c_str(), start, length);
247 
248  if (src == nullptr)
249  throw std::runtime_error(zip_strerror(archive));
250 
251  return src;
252  };
253 }
254 
258 class stat_ptr {
259 private:
260  stat stat_;
261 
262 public:
268  inline stat_ptr(stat stat) noexcept
269  : stat_(stat)
270  {
271  }
272 
278  inline stat& operator*() noexcept
279  {
280  return stat_;
281  }
282 
288  inline const stat& operator*() const noexcept
289  {
290  return stat_;
291  }
292 
298  inline stat* operator->() noexcept
299  {
300  return &stat_;
301  }
302 
308  inline const stat* operator->() const noexcept
309  {
310  return &stat_;
311  }
312 };
313 
317 class archive {
318 private:
319  std::unique_ptr<struct zip, int (*)(struct zip *)> handle_;
320 
321  archive(const archive&) = delete;
322  archive& operator=(const archive&) = delete;
323 
324 public:
328  class iterator : public std::iterator<std::random_access_iterator_tag, libzip::stat> {
329  private:
330  friend class archive;
331 
332  archive *archive_{nullptr};
333  uint64_t index_{0};
334 
335  inline iterator(archive* archive, uint64_t index = 0) noexcept
336  : archive_(archive)
337  , index_(index)
338  {
339  }
340 
341  public:
345  iterator() noexcept = default;
346 
352  inline libzip::stat operator*() const
353  {
354  assert(archive_);
355 
356  return archive_->stat(index_);
357  }
358 
364  inline stat_ptr operator->() const
365  {
366  assert(archive_);
367 
368  return stat_ptr(archive_->stat(index_));
369  }
370 
376  inline iterator& operator++() noexcept
377  {
378  ++ index_;
379 
380  return *this;
381  }
382 
388  inline iterator operator++(int) noexcept
389  {
390  iterator save = *this;
391 
392  ++ index_;
393 
394  return save;
395  }
396 
402  inline iterator& operator--() noexcept
403  {
404  -- index_;
405 
406  return *this;
407  }
408 
414  inline iterator operator--(int) noexcept
415  {
416  iterator save = *this;
417 
418  -- index_;
419 
420  return save;
421  }
422 
429  inline iterator operator+(int inc) const noexcept
430  {
431  return iterator(archive_, index_ + inc);
432  }
433 
440  inline iterator operator-(int dec) const noexcept
441  {
442  return iterator(archive_, index_ - dec);
443  }
444 
451  inline bool operator==(const iterator& other) const noexcept
452  {
453  return index_ == other.index_;
454  }
455 
462  inline bool operator!=(const iterator& other) const noexcept
463  {
464  return index_ != other.index_;
465  }
466 
475  inline libzip::stat operator[](int index)
476  {
477  assert(archive_);
478 
479  return archive_->stat(index_ + index);
480  }
481  };
482 
483 public:
488 
493 
498 
502  using pointer = stat_ptr;
503 
507  using size_type = unsigned;
508 
513 
514 public:
522  archive(const std::string& path, flags_t flags = 0)
523  : handle_(nullptr, nullptr)
524  {
525  int error;
526  struct zip* archive = zip_open(path.c_str(), flags, &error);
527 
528  if (archive == nullptr) {
529  char buf[128]{0};
530 
531  zip_error_to_str(buf, sizeof (buf), error, errno);
532 
533  throw std::runtime_error(buf);
534  }
535 
536  handle_ = { archive, zip_close };
537  }
538 
544  archive(archive&& other) noexcept = default;
545 
552  archive& operator=(archive&& other) noexcept = default;
553 
559  inline iterator begin() noexcept
560  {
561  return iterator(this);
562  }
563 
569  inline const_iterator begin() const noexcept
570  {
571  return const_iterator(const_cast<archive*>(this));
572  }
573 
579  inline const_iterator cbegin() const noexcept
580  {
581  return const_iterator(const_cast<archive*>(this));
582  }
583 
589  inline iterator end() noexcept
590  {
591  return iterator(this, num_entries());
592  }
593 
599  inline const_iterator end() const noexcept
600  {
601  return const_iterator(const_cast<archive*>(this), num_entries());
602  }
603 
609  inline const_iterator cend() const noexcept
610  {
611  return const_iterator(const_cast<archive*>(this), num_entries());
612  }
613 
622  void set_file_comment(uint64_t index, const std::string& text = "", flags_t flags = 0)
623  {
624  auto size = text.size();
625  auto cstr = (size == 0) ? nullptr : text.c_str();
626 
627  if (zip_file_set_comment(handle_.get(), index, cstr, size, flags) < 0)
628  throw std::runtime_error(zip_strerror(handle_.get()));
629  }
630 
639  std::string file_comment(uint64_t index, flags_t flags = 0) const
640  {
641  zip_uint32_t length = 0;
642  auto text = zip_file_get_comment(handle_.get(), index, &length, flags);
643 
644  if (text == nullptr)
645  throw std::runtime_error(zip_strerror(handle_.get()));
646 
647  return std::string(text, length);
648  }
649 
656  void set_comment(const std::string& comment)
657  {
658  if (zip_set_archive_comment(handle_.get(), comment.c_str(), comment.size()) < 0)
659  throw std::runtime_error(zip_strerror(handle_.get()));
660  }
661 
669  std::string comment(flags_t flags = 0) const
670  {
671  int length = 0;
672  auto text = zip_get_archive_comment(handle_.get(), &length, flags);
673 
674  if (text == nullptr)
675  throw std::runtime_error(zip_strerror(handle_.get()));
676 
677  return std::string(text, static_cast<std::size_t>(length));
678  }
679 
687  bool exists(const std::string& name, flags_t flags = 0) const noexcept
688  {
689  return zip_name_locate(handle_.get(), name.c_str(), flags) >= 0;
690  }
691 
700  int64_t find(const std::string& name, flags_t flags = 0) const
701  {
702  auto index = zip_name_locate(handle_.get(), name.c_str(), flags);
703 
704  if (index < 0)
705  throw std::runtime_error(zip_strerror(handle_.get()));
706 
707  return index;
708  }
709 
718  libzip::stat stat(const std::string& name, flags_t flags = 0) const
719  {
720  libzip::stat st;
721 
722  if (zip_stat(handle_.get(), name.c_str(), flags, &st) < 0)
723  throw std::runtime_error(zip_strerror(handle_.get()));
724 
725  return st;
726  }
727 
736  libzip::stat stat(uint64_t index, flags_t flags = 0) const
737  {
738  libzip::stat st;
739 
740  if (zip_stat_index(handle_.get(), index, flags, &st) < 0)
741  throw std::runtime_error(zip_strerror(handle_.get()));
742 
743  return st;
744  }
745 
757  int64_t add(const source& source, const std::string& name, flags_t flags = 0)
758  {
759  auto src = source(handle_.get());
760  auto ret = zip_file_add(handle_.get(), name.c_str(), src, flags);
761 
762  if (ret < 0) {
763  zip_source_free(src);
764  throw std::runtime_error(zip_strerror(handle_.get()));
765  }
766 
767  return ret;
768  }
769 
778  int64_t mkdir(const std::string& directory, flags_t flags = 0)
779  {
780  auto ret = zip_dir_add(handle_.get(), directory.c_str(), flags);
781 
782  if (ret < 0)
783  throw std::runtime_error(zip_strerror(handle_.get()));
784 
785  return ret;
786  }
787 
796  void replace(const source& source, uint64_t index, flags_t flags = 0)
797  {
798  auto src = source(handle_.get());
799 
800  if (zip_file_replace(handle_.get(), index, src, flags) < 0) {
801  zip_source_free(src);
802  throw std::runtime_error(zip_strerror(handle_.get()));
803  }
804  }
805 
815  file open(const std::string& name, flags_t flags = 0, const std::string& password = "")
816  {
817  struct zip_file* file;
818 
819  if (password.size() > 0)
820  file = zip_fopen_encrypted(handle_.get(), name.c_str(), flags, password.c_str());
821  else
822  file = zip_fopen(handle_.get(), name.c_str(), flags);
823 
824  if (file == nullptr)
825  throw std::runtime_error(zip_strerror(handle_.get()));
826 
827  return file;
828  }
829 
839  file open(uint64_t index, flags_t flags = 0, const std::string& password = "")
840  {
841  struct zip_file* file;
842 
843  if (password.size() > 0)
844  file = zip_fopen_index_encrypted(handle_.get(), index, flags, password.c_str());
845  else
846  file = zip_fopen_index(handle_.get(), index, flags);
847 
848  if (file == nullptr)
849  throw std::runtime_error(zip_strerror(handle_.get()));
850 
851  return file;
852  }
853 
862  inline void rename(uint64_t index, const std::string& name, flags_t flags = 0)
863  {
864  if (zip_file_rename(handle_.get(), index, name.c_str(), flags) < 0)
865  throw std::runtime_error(zip_strerror(handle_.get()));
866  }
867 
876  inline void set_file_compression(uint64_t index, int32_t comp, uint32_t flags = 0)
877  {
878  if (zip_set_file_compression(handle_.get(), index, comp, flags) < 0)
879  throw std::runtime_error(zip_strerror(handle_.get()));
880  }
881 
888  inline void remove(uint64_t index)
889  {
890  if (zip_delete(handle_.get(), index) < 0)
891  throw std::runtime_error(zip_strerror(handle_.get()));
892  }
893 
900  inline int64_t num_entries(flags_t flags = 0) const noexcept
901  {
902  return zip_get_num_entries(handle_.get(), flags);
903  }
904 
911  inline void unchange(uint64_t index)
912  {
913  if (zip_unchange(handle_.get(), index) < 0)
914  throw std::runtime_error(zip_strerror(handle_.get()));
915  }
916 
922  inline void unchange_all()
923  {
924  if (zip_unchange_all(handle_.get()) < 0)
925  throw std::runtime_error(zip_strerror(handle_.get()));
926  }
927 
934  {
935  if (zip_unchange_archive(handle_.get()) < 0)
936  throw std::runtime_error(zip_strerror(handle_.get()));
937  }
938 
945  void set_default_password(const std::string& password = "")
946  {
947  auto cstr = (password.size() > 0) ? password.c_str() : nullptr;
948 
949  if (zip_set_default_password(handle_.get(), cstr) < 0)
950  throw std::runtime_error(zip_strerror(handle_.get()));
951  }
952 
960  inline void set_flag(flags_t flag, int value)
961  {
962  if (zip_set_archive_flag(handle_.get(), flag, value) < 0)
963  throw std::runtime_error(zip_strerror(handle_.get()));
964  }
965 
974  inline int flag(flags_t which, flags_t flags = 0) const
975  {
976  auto ret = zip_get_archive_flag(handle_.get(), which, flags);
977 
978  if (ret < 0)
979  throw std::runtime_error(zip_strerror(handle_.get()));
980 
981  return ret;
982  }
983 };
984 
985 } // !libzip
986 
987 #endif // !ZIP_HPP
libzip::archive::archive
archive(archive &&other) noexcept=default
Move constructor defaulted.
libzip::archive::set_file_compression
void set_file_compression(uint64_t index, int32_t comp, uint32_t flags=0)
Set file compression.
Definition: zip.hpp:876
libzip::archive::set_flag
void set_flag(flags_t flag, int value)
Set an archive flag.
Definition: zip.hpp:960
libzip::archive::iterator::operator++
iterator operator++(int) noexcept
Pre increment.
Definition: zip.hpp:388
libzip::archive::add
int64_t add(const source &source, const std::string &name, flags_t flags=0)
Add a file to the archive.
Definition: zip.hpp:757
libzip::uint8_t
zip_uint8_t uint8_t
zip_uint8_t typedef.
Definition: zip.hpp:78
libzip::archive::find
int64_t find(const std::string &name, flags_t flags=0) const
Locate a file on the archive.
Definition: zip.hpp:700
libzip::archive::iterator::iterator
iterator() noexcept=default
Default iterator.
libzip::archive::set_comment
void set_comment(const std::string &comment)
Set the archive comment.
Definition: zip.hpp:656
libzip::file::file
file(file &&other) noexcept=default
Move constructor defaulted.
libzip::archive::iterator::operator+
iterator operator+(int inc) const noexcept
Increment.
Definition: zip.hpp:429
libzip::archive::end
iterator end() noexcept
Get an iterator to the end.
Definition: zip.hpp:589
libzip::file::read
std::string read(uint64_t length)
Optimized function for reading all characters with only one allocation.
Definition: zip.hpp:191
libzip::stat_ptr::operator->
const stat * operator->() const noexcept
Access the object.
Definition: zip.hpp:308
libzip::archive::iterator::operator-
iterator operator-(int dec) const noexcept
Decrement.
Definition: zip.hpp:440
libzip::archive::replace
void replace(const source &source, uint64_t index, flags_t flags=0)
Replace an existing file in the archive.
Definition: zip.hpp:796
libzip::source_cmd
enum zip_source_cmd source_cmd
zip_source_cmd typedef.
Definition: zip.hpp:58
libzip::uint32_t
zip_uint32_t uint32_t
zip_uint32_t typedef.
Definition: zip.hpp:98
libzip::archive::cend
const_iterator cend() const noexcept
Overloaded function.
Definition: zip.hpp:609
libzip::archive::iterator
Base iterator class.
Definition: zip.hpp:328
libzip::archive::unchange_all
void unchange_all()
Revert all changes.
Definition: zip.hpp:922
libzip::int32_t
zip_int32_t int32_t
zip_int32_t typedef.
Definition: zip.hpp:93
libzip::archive::const_iterator
iterator const_iterator
Const random access iterator.
Definition: zip.hpp:512
libzip::file::file
file(struct zip_file *file) noexcept
Create a File with a zip_file structure.
Definition: zip.hpp:140
libzip::file
File for reading.
Definition: zip.hpp:127
libzip::stat
struct zip_stat stat
zip_stat typedef.
Definition: zip.hpp:53
libzip::archive::comment
std::string comment(flags_t flags=0) const
Get the archive comment.
Definition: zip.hpp:669
libzip::archive::remove
void remove(uint64_t index)
Delete a file from the archive.
Definition: zip.hpp:888
libzip::archive::iterator::operator==
bool operator==(const iterator &other) const noexcept
Compare equality.
Definition: zip.hpp:451
libzip::archive::value_type
libzip::stat value_type
Iterator conversion to Stat.
Definition: zip.hpp:487
libzip::file::read
int read(char(&data)[Size]) noexcept
Read some data to a fixed size array.
Definition: zip.hpp:178
libzip::archive::unchange
void unchange(uint64_t index)
Revert changes on the file.
Definition: zip.hpp:911
libzip::archive::stat
libzip::stat stat(uint64_t index, flags_t flags=0) const
Get information about a file.
Definition: zip.hpp:736
libzip::archive
Safe wrapper on the struct zip structure.
Definition: zip.hpp:317
libzip::source_buffer
source source_buffer(std::string data) noexcept
Add a file to the archive using a binary buffer.
Definition: zip.hpp:213
libzip::archive::file_comment
std::string file_comment(uint64_t index, flags_t flags=0) const
Get a comment from a file.
Definition: zip.hpp:639
libzip::archive::set_default_password
void set_default_password(const std::string &password="")
Set the defaut password.
Definition: zip.hpp:945
libzip::int64_t
zip_int64_t int64_t
zip_int64_t typedef.
Definition: zip.hpp:103
libzip::archive::set_file_comment
void set_file_comment(uint64_t index, const std::string &text="", flags_t flags=0)
Set a comment on a file.
Definition: zip.hpp:622
libzip::archive::iterator::operator!=
bool operator!=(const iterator &other) const noexcept
Compare equality.
Definition: zip.hpp:462
libzip::file::operator=
file & operator=(file &&) noexcept=default
Move operator defaulted.
libzip::archive::const_reference
libzip::stat const_reference
Const reference is a copy of Stat.
Definition: zip.hpp:497
libzip::archive::operator=
archive & operator=(archive &&other) noexcept=default
Move operator defaulted.
libzip::archive::open
file open(uint64_t index, flags_t flags=0, const std::string &password="")
Open a file in the archive.
Definition: zip.hpp:839
libzip::archive::exists
bool exists(const std::string &name, flags_t flags=0) const noexcept
Check if a file exists on the archive.
Definition: zip.hpp:687
libzip::archive::end
const_iterator end() const noexcept
Overloaded function.
Definition: zip.hpp:599
libzip::archive::unchange_archive
void unchange_archive()
Revert changes to archive.
Definition: zip.hpp:933
libzip::archive::num_entries
int64_t num_entries(flags_t flags=0) const noexcept
Get the number of entries in the archive.
Definition: zip.hpp:900
libzip::archive::iterator::operator->
stat_ptr operator->() const
Dereference the iterator.
Definition: zip.hpp:364
libzip::source
std::function< struct zip_source *(struct zip *)> source
Source creation for adding files.
Definition: zip.hpp:122
libzip::archive::iterator::operator--
iterator & operator--() noexcept
Post decrement.
Definition: zip.hpp:402
libzip::archive::iterator::operator[]
libzip::stat operator[](int index)
Access a stat information at the specified index.
Definition: zip.hpp:475
libzip::archive::reference
libzip::stat reference
Reference is a copy of Stat.
Definition: zip.hpp:492
libzip::int8_t
zip_int8_t int8_t
zip_int8_t typedef.
Definition: zip.hpp:73
libzip::archive::archive
archive(const std::string &path, flags_t flags=0)
Open an archive on the disk.
Definition: zip.hpp:522
libzip::source_callback
zip_source_callback source_callback
zip_source_cmd typedef.
Definition: zip.hpp:63
libzip::archive::size_type
unsigned size_type
Type of difference.
Definition: zip.hpp:507
libzip::archive::begin
const_iterator begin() const noexcept
Overloaded function.
Definition: zip.hpp:569
libzip::archive::iterator::operator++
iterator & operator++() noexcept
Post increment.
Definition: zip.hpp:376
libzip::archive::iterator::operator--
iterator operator--(int) noexcept
Pre decrement.
Definition: zip.hpp:414
libzip::int16_t
zip_int16_t int16_t
zip_int16_t typedef.
Definition: zip.hpp:83
libzip::stat_ptr::stat_ptr
stat_ptr(stat stat) noexcept
Constructor.
Definition: zip.hpp:268
libzip::stat_ptr::operator*
const stat & operator*() const noexcept
Get the reference.
Definition: zip.hpp:288
libzip::archive::mkdir
int64_t mkdir(const std::string &directory, flags_t flags=0)
Create a directory in the archive.
Definition: zip.hpp:778
libzip
The libzip namespace.
Definition: zip.hpp:48
libzip::source_file
source source_file(std::string path, uint64_t start=0, int64_t length=-1) noexcept
Add a file to the archive from the disk.
Definition: zip.hpp:243
libzip::archive::open
file open(const std::string &name, flags_t flags=0, const std::string &password="")
Open a file in the archive.
Definition: zip.hpp:815
libzip::file::read
int read(void *data, uint64_t length) noexcept
Read some data.
Definition: zip.hpp:166
libzip::archive::cbegin
const_iterator cbegin() const noexcept
Overloaded function.
Definition: zip.hpp:579
libzip::archive::stat
libzip::stat stat(const std::string &name, flags_t flags=0) const
Get information about a file.
Definition: zip.hpp:718
libzip::flags_t
zip_flags_t flags_t
zip_flags_t typedef.
Definition: zip.hpp:68
libzip::uint16_t
zip_uint16_t uint16_t
zip_uint16_t typedef.
Definition: zip.hpp:88
libzip::archive::rename
void rename(uint64_t index, const std::string &name, flags_t flags=0)
Rename an existing entry in the archive.
Definition: zip.hpp:862
libzip::stat_ptr
Wrapper for stat as pointer.
Definition: zip.hpp:258
libzip::archive::begin
iterator begin() noexcept
Get an iterator to the beginning.
Definition: zip.hpp:559
libzip::stat_ptr::operator*
stat & operator*() noexcept
Get the reference.
Definition: zip.hpp:278
libzip::uint64_t
zip_uint64_t uint64_t
zip_uint64_t_t typedef.
Definition: zip.hpp:108
libzip::archive::flag
int flag(flags_t which, flags_t flags=0) const
Get an archive flag.
Definition: zip.hpp:974
libzip::stat_ptr::operator->
stat * operator->() noexcept
Access the object.
Definition: zip.hpp:298