/*
Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
- Copyright (C) 2000-2007 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
void
terminate ()
{
- static bool tried_throw = false;
-
try {
+ static bool tried_throw = false;
// try once to re-throw currently active exception
if (!tried_throw) {
tried_throw = true;
#endif
}
-/** @param s A string.
- * @return Parts of the string split at spaces, except when a space is within quotation marks.
- */
-vector<string>
-split_at_spaces_considering_quotes (string s)
-{
- vector<string> out;
- bool in_quotes = false;
- string c;
- for (string::size_type i = 0; i < s.length(); ++i) {
- if (s[i] == ' ' && !in_quotes) {
- out.push_back (c);
- c = N_("");
- } else if (s[i] == '"') {
- in_quotes = !in_quotes;
- } else {
- c += s[i];
- }
- }
-
- out.push_back (c);
- return out;
-}
-
-/** @param job Optional job for which to report progress */
+/** Compute a digest of the first and last `size' bytes of a set of files. */
string
-md5_digest (vector<boost::filesystem::path> files, shared_ptr<Job> job)
+md5_digest_head_tail (vector<boost::filesystem::path> files, boost::uintmax_t size)
{
- boost::uintmax_t const buffer_size = 64 * 1024;
- char buffer[buffer_size];
-
+ boost::scoped_array<char> buffer (new char[size]);
MD5Digester digester;
- vector<int64_t> sizes;
- for (size_t i = 0; i < files.size(); ++i) {
- sizes.push_back (boost::filesystem::file_size (files[i]));
- }
-
- for (size_t i = 0; i < files.size(); ++i) {
+ /* Head */
+ boost::uintmax_t to_do = size;
+ char* p = buffer.get ();
+ int i = 0;
+ while (i < int64_t (files.size()) && to_do > 0) {
FILE* f = fopen_boost (files[i], "rb");
if (!f) {
throw OpenFileError (files[i].string());
}
- boost::uintmax_t const bytes = boost::filesystem::file_size (files[i]);
- boost::uintmax_t remaining = bytes;
-
- while (remaining > 0) {
- int const t = min (remaining, buffer_size);
- int const r = fread (buffer, 1, t, f);
- if (r != t) {
- throw ReadFileError (files[i], errno);
- }
- digester.add (buffer, t);
- remaining -= t;
-
- if (job) {
- job->set_progress ((float (i) + 1 - float(remaining) / bytes) / files.size ());
- }
+ boost::uintmax_t this_time = min (to_do, boost::filesystem::file_size (files[i]));
+ fread (p, 1, this_time, f);
+ p += this_time;
+ to_do -= this_time;
+ fclose (f);
+
+ ++i;
+ }
+ digester.add (buffer.get(), size - to_do);
+
+ /* Tail */
+ to_do = size;
+ p = buffer.get ();
+ i = files.size() - 1;
+ while (i >= 0 && to_do > 0) {
+ FILE* f = fopen_boost (files[i], "rb");
+ if (!f) {
+ throw OpenFileError (files[i].string());
}
+ boost::uintmax_t this_time = min (to_do, boost::filesystem::file_size (files[i]));
+ fseek (f, -this_time, SEEK_END);
+ fread (p, 1, this_time, f);
+ p += this_time;
+ to_do -= this_time;
fclose (f);
- }
+
+ --i;
+ }
+ digester.add (buffer.get(), size - to_do);
return digester.get ();
}
return int (n + float(r) / 2) &~ (r - 1);
}
-/** Read a sequence of key / value pairs from a text stream;
- * the keys are the first words on the line, and the values are
- * the remainder of the line following the key. Lines beginning
- * with # are ignored.
- * @param s Stream to read.
- * @return key/value pairs.
- */
-multimap<string, string>
-read_key_value (istream &s)
-{
- multimap<string, string> kv;
-
- string line;
- while (getline (s, line)) {
- if (line.empty ()) {
- continue;
- }
-
- if (line[0] == '#') {
- continue;
- }
-
- if (line[line.size() - 1] == '\r') {
- line = line.substr (0, line.size() - 1);
- }
-
- size_t const s = line.find (' ');
- if (s == string::npos) {
- continue;
- }
-
- kv.insert (make_pair (line.substr (0, s), line.substr (s + 1)));
- }
-
- return kv;
-}
-
-string
-get_required_string (multimap<string, string> const & kv, string k)
-{
- if (kv.count (k) > 1) {
- throw StringError (N_("unexpected multiple keys in key-value set"));
- }
-
- multimap<string, string>::const_iterator i = kv.find (k);
-
- if (i == kv.end ()) {
- throw StringError (String::compose (_("missing key %1 in key-value set"), k));
- }
-
- return i->second;
-}
-
-int
-get_required_int (multimap<string, string> const & kv, string k)
-{
- string const v = get_required_string (kv, k);
- return raw_convert<int> (v);
-}
-
-float
-get_required_float (multimap<string, string> const & kv, string k)
-{
- string const v = get_required_string (kv, k);
- return raw_convert<float> (v);
-}
-
-string
-get_optional_string (multimap<string, string> const & kv, string k)
-{
- if (kv.count (k) > 1) {
- throw StringError (N_("unexpected multiple keys in key-value set"));
- }
-
- multimap<string, string>::const_iterator i = kv.find (k);
- if (i == kv.end ()) {
- return N_("");
- }
-
- return i->second;
-}
-
-int
-get_optional_int (multimap<string, string> const & kv, string k)
-{
- if (kv.count (k) > 1) {
- throw StringError (N_("unexpected multiple keys in key-value set"));
- }
-
- multimap<string, string>::const_iterator i = kv.find (k);
- if (i == kv.end ()) {
- return 0;
- }
-
- return raw_convert<int> (i->second);
-}
-
/** Trip an assert if the caller is not in the UI thread */
void
ensure_ui_thread ()
return p;
}
-string
-entities_to_text (string e)
-{
- boost::algorithm::replace_all (e, "%3A", ":");
- boost::algorithm::replace_all (e, "%2F", "/");
- return e;
-}
-
-int64_t
-divide_with_round (int64_t a, int64_t b)
-{
- if (a % b >= (b / 2)) {
- return (a + b - 1) / b;
- } else {
- return a / b;
- }
-}
-
/** Return a user-readable string summarising the versions of our dependencies */
string
dependency_version_summary ()