Anklang 0.3.0-460-gc4ef46ba
ASE — Anklang Sound Engine (C++)

« « « Anklang Documentation
Loading...
Searching...
No Matches
storage.cc
Go to the documentation of this file.
1 // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
2#include "storage.hh"
3#include "path.hh"
4#include "utils.hh"
5#include "api.hh"
6#include "compress.hh"
7#include "platform.hh"
8#include "minizip.h"
9#include "strings.hh"
10#include "compress.hh"
11#include "internal.hh"
12#include <stdlib.h> // mkdtemp
13#include <sys/stat.h> // mkdir
14#include <unistd.h> // rmdir
15#include <fcntl.h> // O_EXCL
16#include <signal.h>
17#include <filesystem>
18
19#define SDEBUG(...) Ase::debug ("storage", __VA_ARGS__)
20#define return_with_errno(ERRNO, RETVAL) ({ errno = ERRNO; return RETVAL; })
21
22namespace Ase {
23
25static String
26pid_string (int pid)
27{
28 static String boot_id = []() {
29 auto id = string_strip (Path::stringread ("/proc/sys/kernel/random/boot_id"));
30 if (id.empty())
31 id = string_strip (Path::stringread ("/etc/machine-id"));
32 if (id.empty())
33 id = string_format ("%08x", gethostid());
34 return id;
35 } ();
36 String text = string_format ("%u %s ", pid, boot_id);
37 String exename;
38 if (exename.empty() && Path::check ("/proc/self/exe", "r"))
39 {
40 const ssize_t max_size = 8100;
41 char exepath[max_size + 1 + 1] = { 0, };
42 ssize_t exepath_size = -1;
43 exepath_size = readlink (string_format ("/proc/%u/exe", pid).c_str(), exepath, max_size);
44 if (exepath_size > 0)
45 exename = exepath;
46 }
47 else if (exename.empty() && Path::check ("/proc/self/comm", "r"))
48 exename = Path::stringread (string_format ("/proc/%u/comm", pid));
49 else
50 {
51 if (getpgid (pid) >= 0 || errno != ESRCH)
52 exename = string_format ("%u", pid); // assume process `pid` exists
53 }
54 text += exename;
55 text += "\n";
56 return text;
57}
58
60static String
61tmpdir_prefix ()
62{
63 return string_format ("anklang-%x", getuid());
64}
65
67static String
68anklang_cachedir_base (bool createbase = false)
69{
70 // try ~/.cache/anklang/
71 String basedir = Ase::Path::cache_home() + "/anklang";
72 if (Ase::Path::check (basedir, "dw"))
73 return basedir;
74 else if (createbase) // !Ase::Path::check (basedir, "dw")
75 {
76 int err = mkdir (basedir.c_str(), 0700);
77 SDEBUG ("mkdir: %s: %s", basedir, strerror (err ? errno : 0));
78 if (Ase::Path::check (basedir, "dw"))
79 return basedir;
80 }
81 // try /tmp/
82 basedir = std::filesystem::temp_directory_path().string();
83 if (Ase::Path::check (basedir, "dw")) // sets errno
84 return basedir;
85 return "";
86}
87
88static std::vector<String> cachedirs_list;
89static std::mutex cachedirs_mutex;
90
92static void
93atexit_clean_cachedirs()
94{
95 std::lock_guard<std::mutex> locker (cachedirs_mutex);
96 while (cachedirs_list.size())
97 {
98 const String dir = cachedirs_list.back();
99 cachedirs_list.pop_back();
100 Path::rmrf (dir);
101 }
102}
103
105String
107{
108 String cachedir = anklang_cachedir_base (true); // sets errno
109 if (cachedir.empty())
110 return "";
111 cachedir += "/" + tmpdir_prefix() + "XXXXXX";
112 char *tmpchars = cachedir.data();
113 char *result = mkdtemp (tmpchars);
114 SDEBUG ("mkdtemp: %s: %s", tmpchars, strerror (result ? 0 : errno));
115 if (result)
116 {
117 String guardfile = cachedir + "/guard.pid";
118 String guardstring = pid_string (getpid());
119 const int guardfd = open (guardfile.c_str(), O_EXCL | O_CREAT, 0600);
120 if (guardfd >= 0 && Path::stringwrite (guardfile, guardstring))
121 {
122 close (guardfd);
123 std::lock_guard<std::mutex> locker (cachedirs_mutex);
124 cachedirs_list.push_back (cachedir);
125 static bool needatexit = true;
126 if (needatexit)
127 needatexit = std::atexit (atexit_clean_cachedirs);
128 SDEBUG ("create: %s: %s", guardfile, strerror (0));
129 return cachedir;
130 }
131 SDEBUG ("create: %s: %s", guardfile, strerror (errno));
132 const int err = errno;
133 close (guardfd);
134 Path::rmrf (cachedir);
135 errno = err;
136 }
137 return ""; // errno is set
138}
139
141void
143{
144 const String cachedir_base = anklang_cachedir_base (false);
145 assert_return (string_startswith (cachedir, cachedir_base));
146 if (Path::check (cachedir, "drw"))
147 {
148 const String guardfile = cachedir + "/guard.pid";
149 if (Path::check (guardfile, "frw"))
150 {
151 String guardstring = Path::stringread (guardfile, 3 * 4096);
152 const int guardpid = string_to_int (guardstring);
153 if (guardpid > 0 && guardstring == pid_string (guardpid))
154 Path::rmrf (cachedir);
155 }
156 }
157}
158
160void
162{
163 const String cachedir = anklang_cachedir_base (false);
164 const String tmpprefix = tmpdir_prefix();
165 if (!cachedir.empty())
166 for (auto &direntry : std::filesystem::directory_iterator (cachedir))
167 if (direntry.is_directory())
168 {
169 const String dirname = direntry.path().filename();
170 if (dirname.size() == tmpprefix.size() + 6 && string_startswith (dirname, tmpprefix))
171 {
172 const String guardfile = direntry.path() / "guard.pid";
173 if (Path::check (guardfile, "frw"))
174 {
175 String guardstring = Path::stringread (guardfile, 3 * 4096);
176 if (guardstring == pid_string (getpid()))
177 {
178 SDEBUG ("skipping dir (pid=self): %s", guardfile);
179 continue;
180 }
181 const int guardpid = string_to_int (guardstring);
182 if (guardpid > 0 && (kill (guardpid, 0) == 0 || Path::check (string_format ("/proc/%u/", guardpid), "d")))
183 {
184 SDEBUG ("skipping dir (live pid=%u): %s", guardpid, guardfile);
185 continue;
186 }
187 Path::rmrf (direntry.path().string());
188 }
189 }
190 }
191}
192
193// == Storage ==
194Storage::~Storage ()
195{}
196
197// == StorageWriter ==
199public:
200 void *writer = nullptr;
201 String zipname;
202 int flags = 0;
203 ~Impl()
204 {
205 if (writer)
206 warning ("Ase::StorageWriter: ZIP file left open: %s", zipname);
207 close();
208 }
209 Error
210 close()
211 {
212 return_unless (writer != nullptr, Error::NONE);
213 int mzerr = mz_zip_writer_close (writer);
214 const int saved_errno = errno;
215 if (mzerr != MZ_OK && !zipname.empty())
216 unlink (zipname.c_str());
217 mz_zip_writer_delete (&writer);
218 writer = nullptr;
219 errno = saved_errno;
220 return mzerr == MZ_OK ? Error::NONE : ase_error_from_errno (errno);
221 }
222 Error
223 remove_opened()
224 {
225 if (writer)
226 {
227 close();
228 if (!zipname.empty())
229 unlink (zipname.c_str());
230 }
231 return Error::NONE;
232 }
233 Error
234 open_for_writing (const String &filename)
235 {
236 assert_return (writer == nullptr, Error::INTERNAL);
237 zipname = filename;
238 mz_zip_writer_create (&writer);
239 mz_zip_writer_set_zip_cd (writer, false);
240 mz_zip_writer_set_password (writer, nullptr);
241 mz_zip_writer_set_store_links (writer, false);
242 mz_zip_writer_set_follow_links (writer, true);
243 mz_zip_writer_set_compress_level (writer, MZ_COMPRESS_LEVEL_BEST);
244 mz_zip_writer_set_compress_method (writer, MZ_COMPRESS_METHOD_DEFLATE);
245 int mzerr = mz_zip_writer_open_file (writer, zipname.c_str(), 0, false);
246 if (mzerr != MZ_OK)
247 {
248 const int saved_errno = errno;
249 mz_zip_writer_delete (&writer);
250 writer = nullptr;
251 unlink (filename.c_str());
252 return ase_error_from_errno (saved_errno);
253 }
254 return Error::NONE;
255 }
256 Error
257 store_file_data (const String &filename, const String &buffer, bool compress, int64_t epoch_seconds)
258 {
259 assert_return (mz_zip_writer_is_open (writer) == MZ_OK, Error::INTERNAL);
260 const uint32 attrib = S_IFREG | 0664;
261 const time_t fdate = epoch_seconds;
262 mz_zip_file file_info = {
263 .version_madeby = MZ_VERSION_MADEBY,
264 .flag = MZ_ZIP_FLAG_UTF8,
265 .compression_method = uint16_t (compress ? MZ_COMPRESS_METHOD_DEFLATE : MZ_COMPRESS_METHOD_STORE),
266 .modified_date = fdate,
267 .accessed_date = fdate,
268 .creation_date = 0,
269 .uncompressed_size = ssize_t (buffer.size()),
270 .external_fa = attrib,
271 .filename = filename.c_str(),
272 .zip64 = buffer.size() > 4294967295, // match libmagic's ZIP-with-mimetype
273 };
274 int32_t mzerr = MZ_OK;
275 if (MZ_HOST_SYSTEM (file_info.version_madeby) != MZ_HOST_SYSTEM_MSDOS &&
276 MZ_HOST_SYSTEM (file_info.version_madeby) != MZ_HOST_SYSTEM_WINDOWS_NTFS)
277 {
278 uint32 target_attrib = 0;
279 mzerr = mz_zip_attrib_convert (MZ_HOST_SYSTEM (file_info.version_madeby), attrib, MZ_HOST_SYSTEM_MSDOS, &target_attrib);
280 file_info.external_fa = target_attrib; // MSDOS attrib
281 file_info.external_fa |= attrib << 16; // OS attrib
282 }
283 if (mzerr == MZ_OK)
284 mzerr = mz_zip_writer_add_buffer (writer, (void*) buffer.data(), buffer.size(), &file_info);
285 return mzerr == MZ_OK ? Error::NONE : ase_error_from_errno (errno);
286 }
287 Error
288 store_file (const String &filename, const String &ondiskpath, bool maycompress)
289 {
290 assert_return (mz_zip_writer_is_open (writer) == MZ_OK, Error::INTERNAL);
291 if (maycompress)
292 maycompress = !is_compressed (Path::stringread (ondiskpath, 1024));
293 if (!maycompress)
294 mz_zip_writer_set_compress_method (writer, MZ_COMPRESS_METHOD_STORE);
295 int32_t mzerr = mz_zip_writer_add_file (writer, ondiskpath.c_str(), filename.c_str());
296 if (!maycompress)
297 mz_zip_writer_set_compress_method (writer, MZ_COMPRESS_METHOD_DEFLATE);
298 return mzerr == MZ_OK ? Error::NONE : ase_error_from_errno (errno);
299 }
300};
301
302StorageWriter::StorageWriter (StorageFlags sflags) :
303 impl_ (std::make_shared<StorageWriter::Impl>())
304{
305 impl_->flags = sflags;
306}
307
308StorageWriter::~StorageWriter ()
309{}
310
311Error
312StorageWriter::open_for_writing (const String &filename)
313{
314 assert_return (impl_, Error::INTERNAL);
315 return impl_->open_for_writing (filename);
316}
317
318Error
319StorageWriter::open_with_mimetype (const String &filename, const String &mimetype)
320{
321 Error err = open_for_writing (filename);
322 if (err == Error::NONE)
323 {
324 const int64_t ase_project_start = 844503962;
325 err = impl_->store_file_data ("mimetype", mimetype, false, ase_project_start);
326 if (err != Error::NONE && impl_)
327 impl_->remove_opened();
328 }
329 return err;
330}
331
332Error
333StorageWriter::store_file_data (const String &filename, const String &buffer, bool alwayscompress)
334{
335 assert_return (impl_, Error::INTERNAL);
336 const bool compressed = is_compressed (buffer);
337 if (!compressed && (alwayscompress || impl_->flags & AUTO_ZSTD))
338 {
339 const String cdata = zstd_compress (buffer);
340 if (alwayscompress || cdata.size() + 128 <= buffer.size())
341 return impl_->store_file_data (filename + ".zst", cdata, false, time (nullptr));
342 }
343 return impl_->store_file_data (filename, buffer,
344 compressed ? false : true,
345 time (nullptr));
346}
347
348Error
349StorageWriter::store_file (const String &filename, const String &ondiskpath, bool maycompress)
350{
351 assert_return (impl_, Error::INTERNAL);
352 return impl_->store_file (filename, ondiskpath, maycompress);
353}
354
355Error
356StorageWriter::close ()
357{
358 assert_return (impl_, Error::INTERNAL);
359 return impl_->close();
360}
361
362Error
363StorageWriter::remove_opened ()
364{
365 assert_return (impl_, Error::INTERNAL);
366 return impl_->remove_opened();
367}
368
369// == StorageReader ==
371public:
372 void *reader = nullptr;
373 String zipname;
374 int flags = 0;
375 ~Impl()
376 {
377 close();
378 }
379 Error
380 close()
381 {
382 return_unless (reader != nullptr, Error::NONE);
383 int mzerr = mz_zip_reader_close (reader);
384 const int saved_errno = errno;
385 mz_zip_reader_delete (&reader);
386 reader = nullptr;
387 errno = saved_errno;
388 return mzerr == MZ_OK ? Error::NONE : ase_error_from_errno (errno);
389 }
390 Error
391 open_for_reading (const String &filename)
392 {
393 assert_return (reader == nullptr, Error::INTERNAL);
394 zipname = filename;
395 // setup reader and open file
396 mz_zip_reader_create (&reader);
397 // mz_zip_reader_set_pattern (reader, pattern, 1);
398 mz_zip_reader_set_password (reader, nullptr);
399 mz_zip_reader_set_encoding (reader, MZ_ENCODING_UTF8);
400 errno = ELIBBAD;
401 int err = mz_zip_reader_open_file (reader, zipname.c_str());
402 if (err != MZ_OK)
403 {
404 const int saved_errno = errno;
405 mz_zip_reader_delete (&reader);
406 reader = nullptr;
407 if (saved_errno == ELIBBAD ||
408 (saved_errno == ENOENT && Path::check (zipname, "f")))
409 return Error::BROKEN_ARCHIVE;
410 return ase_error_from_errno (saved_errno);
411 }
412 return Error::NONE;
413 }
414 StringS
415 list_files()
416 {
417 errno = EINVAL;
418 StringS list;
419 assert_return (reader != nullptr, list);
420 // check and extract file entries
421 int err = mz_zip_reader_goto_first_entry (reader);
422 while (err == MZ_OK)
423 {
424 mz_zip_file *file_info = nullptr;
425 if (MZ_OK == mz_zip_reader_entry_get_info (reader, &file_info) && file_info->filename && file_info->filename[0])
426 {
427 if (!strchr (file_info->filename, '/') && // see: https://github.com/zlib-ng/minizip-ng/issues/433
428 !strchr (file_info->filename, '\\'))
429 list.push_back (file_info->filename);
430 }
431 err = mz_zip_reader_goto_next_entry (reader); // yields MZ_END_OF_LIST
432 }
433 return list;
434 }
435 bool
436 has_file (const String &filename)
437 {
438 return_unless (mz_zip_reader_is_open (reader) == MZ_OK, false);
439 String fname = Path::normalize (filename);
440 if (MZ_OK == mz_zip_reader_locate_entry (reader, fname.c_str(), false))
441 return true;
442 if (flags & AUTO_ZSTD)
443 {
444 fname += ".zst";
445 if (MZ_OK == mz_zip_reader_locate_entry (reader, fname.c_str(), false))
446 return true;
447 }
448 return false;
449 }
450 String
451 stringread (const String &filename)
452 {
453 errno = EINVAL;
454 assert_return (mz_zip_reader_is_open (reader) == MZ_OK, {});
455 String fname = Path::normalize (filename);
456 bool uncompress;
457 if (MZ_OK == mz_zip_reader_locate_entry (reader, fname.c_str(), false))
458 uncompress = false;
459 else if (flags & AUTO_ZSTD)
460 {
461 fname += ".zst";
462 if (MZ_OK == mz_zip_reader_locate_entry (reader, fname.c_str(), false))
463 uncompress = true;
464 else
465 return_with_errno (ENOENT, {});
466 }
467 else
468 return_with_errno (ENOENT, {});
469 ssize_t len = mz_zip_reader_entry_save_buffer_length (reader);
470 if (len >= 0)
471 {
472 String buffer (len, 0);
473 if (MZ_OK == mz_zip_reader_entry_save_buffer (reader, &buffer[0], buffer.size()))
474 {
475 errno = 0;
476 return uncompress ? zstd_uncompress (buffer) : buffer;
477 }
478 }
479 return_with_errno (ENOENT, {});
480 }
481};
482
483StorageReader::StorageReader (StorageFlags sflags) :
484 impl_ (std::make_shared<StorageReader::Impl>())
485{
486 impl_->flags = sflags;
487}
488
489StorageReader::~StorageReader ()
490{}
491
492Error
493StorageReader::open_for_reading (const String &filename)
494{
495 assert_return (impl_, Error::INTERNAL);
496 return impl_->open_for_reading (filename);
497}
498
500StorageReader::list_files ()
501{
502 assert_return (impl_, {});
503 return impl_->list_files();
504}
505
506Error
507StorageReader::close ()
508{
509 assert_return (impl_, Error::INTERNAL);
510 return impl_->close();
511}
512
513bool
514StorageReader::has_file (const String &filename)
515{
516 assert_return (impl_, false);
517 return impl_->has_file (filename);
518}
519
520String
521StorageReader::stringread (const String &filename, ssize_t maxlength)
522{
523 errno = EINVAL;
524 assert_return (impl_, {});
525 String data = impl_->stringread (filename);
526 if (maxlength >= 0 && maxlength < int64_t (data.size()))
527 data.resize (maxlength);
528 return data;
529}
530
531void
532StorageReader::search_dir (const String &dirname)
533{
534 // TODO: implement directory search for fallbacks
535}
536
537// == StreamReader ==
538StreamReader::~StreamReader()
539{}
540
541class StreamReaderFile final : public StreamReader {
542 FILE *file_ = nullptr;
543 String name_;
544public:
546 {
547 close();
548 }
549 bool
550 open (const String &filename)
551 {
552 return_unless (file_ == nullptr, false);
553 name_ = filename;
554 file_ = fopen (name_.c_str(), "r");
555 return file_ != nullptr;
556 }
557 ssize_t
558 read (void *buffer, size_t len) override
559 {
560 return_unless (file_ != nullptr, 0);
561 if (feof (file_))
562 return 0;
563 return fread (buffer, 1, len, file_);
564 }
565 bool
566 close() override
567 {
568 return_unless (file_ != nullptr, false);
569 const int e = fclose (file_);
570 file_ = nullptr;
571 return e == 0;
572 }
573 String
574 name() const override
575 {
576 return name_;
577 }
578};
579
580StreamReaderP
581stream_reader_from_file (const String &file)
582{
583 auto readerp = std::make_shared<StreamReaderFile>();
584 if (readerp->open (file))
585 return readerp;
586 return nullptr;
587}
588
590 void *reader_ = nullptr;
591 bool entry_opened_ = false;
592 String name_, member_;
593public:
595 {
596 close();
597 }
598 Error
599 open_zip (const String &zipname)
600 {
601 assert_return (reader_ == nullptr, Error::INTERNAL);
602 name_ = zipname;
603 mz_zip_reader_create (&reader_);
604 mz_zip_reader_set_password (reader_, nullptr);
605 mz_zip_reader_set_encoding (reader_, MZ_ENCODING_UTF8);
606 errno = ELIBBAD;
607 int err = mz_zip_reader_open_file (reader_, name_.c_str());
608 if (err != MZ_OK)
609 {
610 const int saved_errno = errno;
611 mz_zip_reader_delete (&reader_);
612 reader_ = nullptr;
613 return saved_errno == ELIBBAD ? Error::BROKEN_ARCHIVE : ase_error_from_errno (saved_errno);
614 }
615 return Error::NONE;
616 }
617 Error
618 open_entry (const String &member)
619 {
620 errno = EINVAL;
621 assert_return (mz_zip_reader_is_open (reader_) == MZ_OK, Error::INTERNAL);
622 assert_return (entry_opened_ == false, Error::INTERNAL);
623 String membername = Path::normalize (member);
624 if (MZ_OK != mz_zip_reader_locate_entry (reader_, membername.c_str(), false) ||
625 MZ_OK != mz_zip_reader_entry_open (reader_))
626 return Error::FILE_NOT_FOUND;
627 entry_opened_ = true;
628 member_ = membername;
629 return Error::NONE;
630 }
631 ssize_t
632 read (void *buffer, size_t len) override
633 {
634 return_unless (entry_opened_, 0);
635 if (entry_opened_)
636 {
637 ssize_t n = mz_zip_reader_entry_read (reader_, buffer, len);
638 if (n > 0)
639 return n;
640 mz_zip_reader_entry_close (reader_);
641 entry_opened_ = false;
642 }
643 return 0;
644 }
645 bool
646 close() override
647 {
648 return_unless (reader_ != nullptr, false);
649 const int mzerr = mz_zip_reader_close (reader_);
650 const int saved_errno = errno;
651 mz_zip_reader_delete (&reader_);
652 reader_ = nullptr;
653 entry_opened_ = false;
654 errno = saved_errno;
655 return mzerr == MZ_OK;
656 }
657 String
658 name() const override
659 {
660 return name_ + (member_.empty() ? "" : "/./" + member_);
661 }
662};
663
664StreamReaderP
665stream_reader_zip_member (const String &archive, const String &member, Storage::StorageFlags f)
666{
668 if (Error::NONE == readerp->open_zip (archive))
669 {
670 Error error = readerp->open_entry (member);
671 if (Error::NONE == error)
672 return readerp;
673 if (error == Error::FILE_NOT_FOUND && f & Storage::AUTO_ZSTD)
674 {
675 const String memberzstd = member + ".zst";
676 if (Error::NONE == readerp->open_entry (memberzstd))
677 {
678 StreamReaderP istream = readerp;
679 return stream_reader_zstd (istream);
680 }
681 }
682 }
683 return nullptr;
684}
685
686// == StreamWriter ==
687StreamWriter::~StreamWriter ()
688{}
689
690class FileStreamWriter final : public StreamWriter {
691 String name_;
692 int fd_ = -1;
693public:
694 FileStreamWriter (const String &filename)
695 {
696 name_ = filename;
697 }
699 {
700 close();
701 }
702 String
703 name() const override
704 {
705 return name_;
706 }
707 bool
708 create (int mode)
709 {
710 errno = EBUSY;
711 assert_return (fd_ < 0, false);
712 fd_ = open (name_.c_str(), O_CREAT | O_TRUNC | O_WRONLY, mode);
713 return fd_ >= 0;
714 }
715 ssize_t
716 write (const void *buffer, size_t len) override
717 {
718 if (len)
719 assert_return (buffer != nullptr, -1);
720 errno = EIO;
721 return_unless (fd_ >= 0, -1);
722 const char *p = (const char*) buffer, *const e = p + len;
723 while (p < e)
724 {
725 ssize_t l;
726 do
727 l = ::write (fd_, p, e - p);
728 while (l == -1 && errno == EINTR);
729 if (l < 0)
730 return -1;
731 p += l;
732 }
733 return len;
734 }
735 bool
736 close() override
737 {
738 int ret = 0;
739 if (fd_ >= 0)
740 {
741 ret = ::close (fd_);
742 fd_ = -1;
743 if (ret < 0)
744 printerr ("%s: StreamWriter: close(\"%s\"): %s\n", program_alias(), name_, strerror (errno));
745 }
746 return ret == 0;
747 }
748};
749
750StreamWriterP
751stream_writer_create_file (const String &filename, int mode)
752{
753 auto fw = std::make_shared<FileStreamWriter> (filename);
754 if (fw->create (mode) == false)
755 return nullptr;
756 return fw;
757}
758
759} // Ase
#define ENOENT
T atexit(T... args)
T back(T... args)
T c_str(T... args)
close
T data(T... args)
dirname
T empty(T... args)
errno
fclose
feof
fopen
fread
gethostid
getpid
getuid
#define assert_return(expr,...)
Return from the current function if expr is unmet and issue an assertion warning.
Definition internal.hh:29
#define return_unless(cond,...)
Return silently if cond does not evaluate to true with return value ...
Definition internal.hh:71
kill
mkdir
mkdtemp
bool check(const String &file, const String &mode)
Definition path.cc:625
String cache_home()
Get the $XDG_CACHE_HOME directory, see: https://specifications.freedesktop.org/basedir-spec/latest.
Definition path.cc:322
void rmrf(const String &dir)
Recursively delete directory tree.
Definition path.cc:236
The Anklang C++ API namespace.
Definition api.hh:9
std::string string_format(const char *format, const Args &...args) __attribute__((__format__(__printf__
Format a string similar to sprintf(3) with support for std::string and std::ostringstream convertible...
String anklang_cachedir_create()
Create exclusive cache directory for this process' runtime.
Definition storage.cc:106
std::vector< String > StringS
Convenience alias for a std::vector<std::string>.
Definition cxxaux.hh:36
Error
Enum representing Error states.
Definition api.hh:22
void anklang_cachedir_clean_stale()
Clean stale cache directories from past runtimes, may be called from any thread.
Definition storage.cc:161
int64 string_to_int(const String &string, size_t *consumed, uint base)
Parse a string into a 64bit integer, optionally specifying the expected number base.
Definition strings.cc:578
String program_alias()
Retrieve the program name as used for logging or debug messages.
Definition platform.cc:849
std::string String
Convenience alias for std::string.
Definition cxxaux.hh:35
String string_strip(const String &input)
Strip whitespaces from the left and right of a string.
Definition strings.cc:1126
uint32_t uint32
A 32-bit unsigned integer.
Definition cxxaux.hh:24
void anklang_cachedir_cleanup(const String &cachedir)
Cleanup a cachedir previously created with anklang_cachedir_create().
Definition storage.cc:142
bool string_startswith(const String &string, const String &fragment)
Returns whether string starts with fragment.
Definition strings.cc:846
open
T pop_back(T... args)
read
T push_back(T... args)
write
readlink
T size(T... args)
typedef int64_t
strchr
strerror
typedef ssize_t
T temp_directory_path(T... args)
time
unlink