From 834a3f2151dd8738a1f878489f6207664c4af5aa Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Sun, 14 Jul 2013 17:14:53 -0700 Subject: Imported Upstream version 1.1.1 --- src/util.c | 654 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 654 insertions(+) create mode 100644 src/util.c (limited to 'src/util.c') diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..312c564 --- /dev/null +++ b/src/util.c @@ -0,0 +1,654 @@ +/* + * transmission-remote-gtk - A GTK RPC client to Transmission + * Copyright (C) 2011-2013 Alan Fitton + + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* Many of these functions are taken from the Transmission Project. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" + +/*** +**** The code for formatting size and speeds, taken from Transmission. +***/ + +const int disk_K = 1024; +const char *disk_K_str = N_("KiB"); +const char *disk_M_str = N_("MiB"); +const char *disk_G_str = N_("GiB"); +const char *disk_T_str = N_("TiB"); + +const int speed_K = 1024; +const char *speed_K_str = N_("KiB/s"); +const char *speed_M_str = N_("MiB/s"); +const char *speed_G_str = N_("GiB/s"); +const char *speed_T_str = N_("TiB/s"); + +struct formatter_unit { + char *name; + gint64 value; +}; + +struct formatter_units { + struct formatter_unit units[4]; +}; + +enum { TR_FMT_KB, TR_FMT_MB, TR_FMT_GB, TR_FMT_TB }; + +static void +formatter_init(struct formatter_units *units, + unsigned int kilo, + const char *kb, const char *mb, + const char *gb, const char *tb) +{ + guint64 value = kilo; + units->units[TR_FMT_KB].name = g_strdup(kb); + units->units[TR_FMT_KB].value = value; + + value *= kilo; + units->units[TR_FMT_MB].name = g_strdup(mb); + units->units[TR_FMT_MB].value = value; + + value *= kilo; + units->units[TR_FMT_GB].name = g_strdup(gb); + units->units[TR_FMT_GB].value = value; + + value *= kilo; + units->units[TR_FMT_TB].name = g_strdup(tb); + units->units[TR_FMT_TB].value = value; +} + +static char *formatter_get_size_str(const struct formatter_units *u, + char *buf, gint64 bytes, size_t buflen) +{ + int precision; + double value; + const char *units; + const struct formatter_unit *unit; + + if (bytes < u->units[1].value) + unit = &u->units[0]; + else if (bytes < u->units[2].value) + unit = &u->units[1]; + else if (bytes < u->units[3].value) + unit = &u->units[2]; + else + unit = &u->units[3]; + + value = (double) bytes / unit->value; + units = unit->name; + if (unit->value == 1) + precision = 0; + else if (value < 100) + precision = 2; + else + precision = 1; + tr_snprintf(buf, buflen, "%.*f %s", precision, value, units); + return buf; +} + +static struct formatter_units size_units; + +void +tr_formatter_size_init(unsigned int kilo, + const char *kb, const char *mb, + const char *gb, const char *tb) +{ + formatter_init(&size_units, kilo, kb, mb, gb, tb); +} + +char *tr_formatter_size_B(char *buf, gint64 bytes, size_t buflen) +{ + return formatter_get_size_str(&size_units, buf, bytes, buflen); +} + +static struct formatter_units speed_units; + +unsigned int tr_speed_K = 0u; + +void +tr_formatter_speed_init(unsigned int kilo, + const char *kb, const char *mb, + const char *gb, const char *tb) +{ + tr_speed_K = kilo; + formatter_init(&speed_units, kilo, kb, mb, gb, tb); +} + +char *tr_formatter_speed_KBps(char *buf, double KBps, size_t buflen) +{ + const double K = speed_units.units[TR_FMT_KB].value; + double speed = KBps; + + if (speed <= 999.95) /* 0.0 KB to 999.9 KB */ + tr_snprintf(buf, buflen, "%d %s", (int) speed, + speed_units.units[TR_FMT_KB].name); + else { + speed /= K; + if (speed <= 99.995) /* 0.98 MB to 99.99 MB */ + tr_snprintf(buf, buflen, "%.2f %s", speed, + speed_units.units[TR_FMT_MB].name); + else if (speed <= 999.95) /* 100.0 MB to 999.9 MB */ + tr_snprintf(buf, buflen, "%.1f %s", speed, + speed_units.units[TR_FMT_MB].name); + else { + speed /= K; + tr_snprintf(buf, buflen, "%.1f %s", speed, + speed_units.units[TR_FMT_GB].name); + } + } + + return buf; +} + +/* URL checkers. */ + +gboolean is_magnet(const gchar * string) +{ + return g_str_has_prefix(string, "magnet:"); +} + +gboolean is_url(const gchar * string) +{ + /* return g_regex_match_simple ("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?", string, 0, 0); */ + return g_regex_match_simple("^http[s]?://", string, 0, 0); +} + +/* + * Glib-ish Utility functions. + */ + +gchar *trg_base64encode(const gchar * filename) +{ + GError *error = NULL; + GMappedFile *mf = g_mapped_file_new(filename, FALSE, &error); + gchar *b64out = NULL; + + if (error) { + g_error("%s", error->message); + g_error_free(error); + } else { + b64out = + g_base64_encode((guchar *) g_mapped_file_get_contents(mf), + g_mapped_file_get_length(mf)); + } + + g_mapped_file_unref(mf); + + return b64out; +} + +gchar *trg_gregex_get_first(GRegex * rx, const gchar * src) +{ + GMatchInfo *mi = NULL; + gchar *dst = NULL; + g_regex_match(rx, src, 0, &mi); + if (mi) { + dst = g_match_info_fetch(mi, 1); + g_match_info_free(mi); + } + return dst; +} + +GRegex *trg_uri_host_regex_new(void) +{ + return + g_regex_new + ("^[^:/?#]+:?//(?:www\\.|torrent\\.|torrents\\.|tracker\\.|\\d+\\.)?([^/?#:]*)", + G_REGEX_OPTIMIZE, 0, NULL); +} + +void g_str_slist_free(GSList * list) +{ + g_slist_foreach(list, (GFunc) g_free, NULL); + g_slist_free(list); +} + +void rm_trailing_slashes(gchar * str) +{ + int i, len; + + if (!str) + return; + + if ((len = strlen(str)) < 1) + return; + + for (i = strlen(str) - 1; str[i]; i--) { + if (str[i] == '/') + str[i] = '\0'; + else + return; + } +} + +/* Working with torrents.. */ + +void add_file_id_to_array(JsonObject * args, const gchar * key, gint index) +{ + JsonArray *array; + if (json_object_has_member(args, key)) { + array = json_object_get_array_member(args, key); + } else { + array = json_array_new(); + json_object_set_array_member(args, key, array); + } + json_array_add_int_element(array, index); +} + +/* GTK utilities. */ + +GtkWidget *gtr_combo_box_new_enum(const char *text_1, ...) +{ + GtkWidget *w; + GtkCellRenderer *r; + GtkListStore *store; + va_list vl; + const char *text; + va_start(vl, text_1); + + store = gtk_list_store_new(2, G_TYPE_INT, G_TYPE_STRING); + + text = text_1; + if (text != NULL) + do { + const int val = va_arg(vl, int); + gtk_list_store_insert_with_values(store, NULL, INT_MAX, 0, val, + 1, text, -1); + text = va_arg(vl, const char *); + } + while (text != NULL); + + w = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store)); + r = gtk_cell_renderer_text_new(); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(w), r, TRUE); + gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(w), r, "text", 1, NULL); + + /* cleanup */ + g_object_unref(store); + return w; +} + +GtkWidget *my_scrolledwin_new(GtkWidget * child) +{ + GtkWidget *scrolled_win = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_win), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(scrolled_win), + GTK_SHADOW_ETCHED_IN); + gtk_container_add(GTK_CONTAINER(scrolled_win), child); + return scrolled_win; +} + +/* gtk_widget_set_sensitive() was introduced in 2.18, we can have a minimum of + * 2.16 otherwise. */ + +void trg_widget_set_visible(GtkWidget * w, gboolean visible) +{ + if (visible) + gtk_widget_show(w); + else + gtk_widget_hide(w); +} + +void trg_error_dialog(GtkWindow * parent, trg_response * response) +{ + gchar *msg = make_error_message(response->obj, response->status); + GtkWidget *dialog = gtk_message_dialog_new(parent, + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, "%s", + msg); + gtk_window_set_title(GTK_WINDOW(dialog), _("Error")); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + g_free(msg); +} + +gchar *make_error_message(JsonObject * response, int status) +{ + if (status == FAIL_JSON_DECODE) { + return g_strdup(_("JSON decoding error.")); + } else if (status == FAIL_RESPONSE_UNSUCCESSFUL) { + const gchar *resultStr = + json_object_get_string_member(response, "result"); + if (resultStr == NULL) + return g_strdup(_("Server responded, but with no result.")); + else + return g_strdup(resultStr); + } else if (status <= -100) { + return g_strdup_printf(_("Request failed with HTTP code %d"), + -(status + 100)); + } else { + return g_strdup(curl_easy_strerror(status)); + } +} + +/* Formatters and Transmission basic utility functions.. */ + +char *tr_strlpercent(char *buf, double x, size_t buflen) +{ + int precision; + if (x < 10.0) + precision = 2; + else if (x < 100.0) + precision = 1; + else + precision = 0; + + tr_snprintf(buf, buflen, "%.*f%%", precision, tr_truncd(x, precision)); + return buf; +} + +double tr_truncd(double x, int decimal_places) +{ + const int i = (int) pow(10, decimal_places); + double x2 = (int) (x * i); + return x2 / i; +} + +char *tr_strratio(char *buf, size_t buflen, double ratio, + const char *infinity) +{ + if ((int) ratio == TR_RATIO_NA) + tr_strlcpy(buf, _("None"), buflen); + else if ((int) ratio == TR_RATIO_INF) + tr_strlcpy(buf, infinity, buflen); + else if (ratio < 10.0) + tr_snprintf(buf, buflen, "%.2f", tr_truncd(ratio, 2)); + else if (ratio < 100.0) + tr_snprintf(buf, buflen, "%.1f", tr_truncd(ratio, 1)); + else + tr_snprintf(buf, buflen, "%'.0f", ratio); + return buf; +} + +char *tr_strlratio(char *buf, double ratio, size_t buflen) +{ + return tr_strratio(buf, buflen, ratio, "\xE2\x88\x9E"); +} + +char *tr_strltime_short(char *buf, long seconds, size_t buflen) +{ + int hours, minutes; + + if (seconds < 0) + seconds = 0; + + hours = seconds / 3600; + minutes = (seconds % 3600) / 60; + seconds = (seconds % 3600) % 60; + + g_snprintf(buf, buflen, "%02d:%02d:%02ld", hours, minutes, seconds); + + return buf; +} + +char *tr_strltime_long(char *buf, long seconds, size_t buflen) +{ + int days, hours, minutes; + char d[128], h[128], m[128], s[128]; + + if (seconds < 0) + seconds = 0; + + days = seconds / 86400; + hours = (seconds % 86400) / 3600; + minutes = (seconds % 3600) / 60; + seconds = (seconds % 3600) % 60; + + g_snprintf(d, sizeof(d), ngettext("%d day", "%d days", days), days); + g_snprintf(h, sizeof(h), ngettext("%d hour", "%d hours", hours), + hours); + g_snprintf(m, sizeof(m), ngettext("%d minute", "%d minutes", minutes), + minutes); + g_snprintf(s, sizeof(s), + ngettext("%ld second", "%ld seconds", seconds), seconds); + + if (days) { + if (days >= 4 || !hours) { + g_strlcpy(buf, d, buflen); + } else { + g_snprintf(buf, buflen, "%s, %s", d, h); + } + } else if (hours) { + if (hours >= 4 || !minutes) { + g_strlcpy(buf, h, buflen); + } else { + g_snprintf(buf, buflen, "%s, %s", h, m); + } + } else if (minutes) { + if (minutes >= 4 || !seconds) { + g_strlcpy(buf, m, buflen); + } else { + g_snprintf(buf, buflen, "%s, %s", m, s); + } + } else { + g_strlcpy(buf, s, buflen); + } + + return buf; +} + +char *gtr_localtime(time_t time) +{ + const struct tm tm = *localtime(&time); + char buf[256], *eoln; + + g_strlcpy(buf, asctime(&tm), sizeof(buf)); + if ((eoln = strchr(buf, '\n'))) + *eoln = '\0'; + + return g_locale_to_utf8(buf, -1, NULL, NULL, NULL); +} + +char *gtr_localtime2(char *buf, time_t time, size_t buflen) +{ + char *tmp = gtr_localtime(time); + g_strlcpy(buf, tmp, buflen); + g_free(tmp); + return buf; +} + +int tr_snprintf(char *buf, size_t buflen, const char *fmt, ...) +{ + int len; + va_list args; + + va_start(args, fmt); + len = evutil_vsnprintf(buf, buflen, fmt, args); + va_end(args); + return len; +} + +gchar *epoch_to_string(gint64 epoch) +{ +#if GLIB_CHECK_VERSION(2, 26, 00) + GDateTime *dt = g_date_time_new_from_unix_local(epoch); + gchar *timestring = g_date_time_format(dt, "%F %H:%M:%S"); + g_date_time_unref(dt); + return timestring; +#else + char buf[64]; + time_t time_val = epoch; + struct tm *ts = localtime(&time_val); + int wrote = strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ts); + return g_strndup(buf, wrote); +#endif +} + +/* wrap a link in text with a hyperlink, for use in pango markup. + * with or without any links - a newly allocated string is returned. */ + +gchar *add_links_to_text(const gchar * original) +{ + /* return if original already contains links */ + if (g_regex_match_simple("", original, 0, 0)) { + return g_strdup(original); + } + + gchar *newText, *url, *link; + GMatchInfo *match_info; + GRegex *regex = + g_regex_new("(https?://[a-zA-Z0-9_\\-\\./?=&]+)", 0, 0, NULL); + + // extract url and build escaped link + g_regex_match(regex, original, 0, &match_info); + url = g_match_info_fetch(match_info, 1); + + if(url) { + link = g_markup_printf_escaped("%s", url, url); + newText = g_regex_replace(regex, original, -1, 0, link, + 0, NULL); + g_free(url); + g_free(link); + } else { + newText = g_strdup(original); + } + + g_regex_unref(regex); + g_match_info_unref(match_info); + return newText; +} + +size_t tr_strlcpy(char *dst, const void *src, size_t siz) +{ +#ifdef HAVE_STRLCPY + return strlcpy(dst, src, siz); +#else + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0) { + while (--n != 0) { + if ((*d++ = *s++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++); + } + + return s - (char *) src - 1; /* count does not include NUL */ +#endif +} + +int +evutil_vsnprintf(char *buf, size_t buflen, const char *format, va_list ap) +{ +#ifdef _MSC_VER + int r = _vsnprintf(buf, buflen, format, ap); + buf[buflen - 1] = '\0'; + if (r >= 0) + return r; + else + return _vscprintf(format, ap); +#else + int r = vsnprintf(buf, buflen, format, ap); + buf[buflen - 1] = '\0'; + return r; +#endif +} + +char *tr_strlsize(char *buf, guint64 bytes, size_t buflen) +{ + if (!bytes) + g_strlcpy(buf, Q_("None"), buflen); + else + tr_formatter_size_B(buf, bytes, buflen); + + return buf; +} + +gboolean is_minimised_arg(const gchar * arg) +{ + return !g_strcmp0(arg, "-m") + || !g_strcmp0(arg, "--minimized") || !g_strcmp0(arg, "/m"); +} + +gboolean should_be_minimised(int argc, char *argv[]) +{ + int i; + for (i = 1; i < argc; i++) + if (is_minimised_arg(argv[i])) + return TRUE; + + return FALSE; +} + +GtkWidget *trg_hbox_new(gboolean homogeneous, gint spacing) +{ + GtkWidget *box; +#if GTK_CHECK_VERSION( 3, 0, 0 ) + box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, spacing); + gtk_box_set_homogeneous(GTK_BOX(box), homogeneous); +#else + box = gtk_hbox_new(homogeneous, spacing); +#endif + return box; +} + +GtkWidget *trg_vbox_new(gboolean homogeneous, gint spacing) +{ + GtkWidget *box; +#if GTK_CHECK_VERSION( 3, 0, 0 ) + box = gtk_box_new(GTK_ORIENTATION_VERTICAL, spacing); + gtk_box_set_homogeneous(GTK_BOX(box), homogeneous); +#else + box = gtk_vbox_new(homogeneous, spacing); +#endif + return box; +} + +#ifdef WIN32 +gchar *trg_win32_support_path(gchar * file) +{ + gchar *moddir = + g_win32_get_package_installation_directory_of_module(NULL); + gchar *path = g_build_filename(moddir, file, NULL); + g_free(moddir); + return path; +} +#endif + +gboolean is_unity() +{ + return g_strcmp0(g_getenv("XDG_CURRENT_DESKTOP"), "Unity") == 0; +} -- cgit v1.2.3