+
+bool
+dcp::ids_equal (string a, string b)
+{
+ transform (a.begin(), a.end(), a.begin(), ::tolower);
+ transform (b.begin(), b.end(), b.begin(), ::tolower);
+ trim (a);
+ trim (b);
+ return a == b;
+}
+
+string
+dcp::file_to_string (boost::filesystem::path p, uintmax_t max_length)
+{
+ uintmax_t len = boost::filesystem::file_size (p);
+ if (len > max_length) {
+ throw MiscError (String::compose ("Unexpectedly long file (%1)", p.string()));
+ }
+
+ FILE* f = fopen_boost (p, "r");
+ if (!f) {
+ throw FileError ("could not open file", p, errno);
+ }
+
+ char* c = new char[len];
+ /* This may read less than `len' if we are on Windows and we have CRLF in the file */
+ int const N = fread (c, 1, len, f);
+ fclose (f);
+
+ string s (c, N);
+ delete[] c;
+
+ return s;
+}
+
+/** @param key RSA private key in PEM format (optionally with -----BEGIN... / -----END...)
+ * @return SHA1 fingerprint of key
+ */
+string
+dcp::private_key_fingerprint (string key)
+{
+ boost::replace_all (key, "-----BEGIN RSA PRIVATE KEY-----\n", "");
+ boost::replace_all (key, "\n-----END RSA PRIVATE KEY-----\n", "");
+
+ unsigned char buffer[4096];
+ int const N = base64_decode (key, buffer, sizeof (buffer));
+
+ SHA_CTX sha;
+ SHA1_Init (&sha);
+ SHA1_Update (&sha, buffer, N);
+ uint8_t digest[20];
+ SHA1_Final (digest, &sha);
+
+ char digest_base64[64];
+ return Kumu::base64encode (digest, 20, digest_base64, 64);
+}
+
+xmlpp::Node *
+dcp::find_child (xmlpp::Node const * node, string name)
+{
+ xmlpp::Node::NodeList c = node->get_children ();
+ xmlpp::Node::NodeList::iterator i = c.begin();
+ while (i != c.end() && (*i)->get_name() != name) {
+ ++i;
+ }
+
+ DCP_ASSERT (i != c.end ());
+ return *i;
+}
+
+string
+dcp::remove_urn_uuid (string raw)
+{
+ DCP_ASSERT (raw.substr(0, 9) == "urn:uuid:");
+ return raw.substr (9);
+}
+
+string
+dcp::openjpeg_version ()
+{
+ return opj_version ();
+}
+
+string
+dcp::spaces (int n)
+{
+ string s = "";
+ for (int i = 0; i < n; ++i) {
+ s += " ";
+ }
+ return s;
+}
+
+void
+dcp::indent (xmlpp::Element* element, int initial)
+{
+ xmlpp::Node* last = 0;
+ BOOST_FOREACH (xmlpp::Node * n, element->get_children()) {
+ xmlpp::Element* e = dynamic_cast<xmlpp::Element*>(n);
+ if (e) {
+ element->add_child_text_before (e, "\n" + spaces(initial + 2));
+ indent (e, initial + 2);
+ last = n;
+ }
+ }
+ if (last) {
+ element->add_child_text (last, "\n" + spaces(initial));
+ }
+}
+
+/** @return true if the day represented by \ref a is less than or
+ * equal to the one represented by \ref b, ignoring the time parts.
+ */
+bool
+dcp::day_less_than_or_equal (LocalTime a, LocalTime b)
+{
+ if (a.year() != b.year()) {
+ return a.year() < b.year();
+ }
+
+ if (a.month() != b.month()) {
+ return a.month() < b.month();
+ }
+
+ return a.day() <= b.day();
+}
+
+/** @return true if the day represented by \ref a is greater than or
+ * equal to the one represented by \ref b, ignoring the time parts.
+ */
+bool
+dcp::day_greater_than_or_equal (LocalTime a, LocalTime b)
+{
+ if (a.year() != b.year()) {
+ return a.year() > b.year();
+ }
+
+ if (a.month() != b.month()) {
+ return a.month() > b.month();
+ }
+
+ return a.day() >= b.day();
+}
+
+/** Try quite hard to find a string which starts with \ref base and is
+ * not in \ref existing.
+ */
+string
+dcp::unique_string (vector<string> existing, string base)
+{
+ int const max_tries = existing.size() + 1;
+ for (int i = 0; i < max_tries; ++i) {
+ string trial = String::compose("%1%2", base, i);
+ if (find(existing.begin(), existing.end(), trial) == existing.end()) {
+ return trial;
+ }
+ }
+
+ DCP_ASSERT (false);
+}
+
+
+ASDCPErrorSuspender::ASDCPErrorSuspender ()
+ : _old (Kumu::DefaultLogSink())
+{
+ _sink = new Kumu::EntryListLogSink(_log);
+ Kumu::SetDefaultLogSink (_sink);
+}
+
+
+ASDCPErrorSuspender::~ASDCPErrorSuspender ()
+{
+ Kumu::SetDefaultLogSink (&_old);
+ delete _sink;
+}
+