source: trunk/gtk/util.c @ 525

Last change on this file since 525 was 525, checked in by joshe, 16 years ago

Move the ETA cap of 99:59:59 from libtransmission to the MacOS X GUI.
Display the ETA better in the GTK GUI.

  • Property svn:keywords set to Date Rev Author Id
File size: 8.5 KB
Line 
1/*
2  $Id: util.c 525 2006-07-04 22:31:14Z joshe $
3
4  Copyright (c) 2005-2006 Joshua Elsasser. All rights reserved.
5   
6  Redistribution and use in source and binary forms, with or without
7  modification, are permitted provided that the following conditions
8  are met:
9   
10   1. Redistributions of source code must retain the above copyright
11      notice, this list of conditions and the following disclaimer.
12   2. Redistributions in binary form must reproduce the above copyright
13      notice, this list of conditions and the following disclaimer in the
14      documentation and/or other materials provided with the distribution.
15   
16  THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS "AS IS" AND
17  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  POSSIBILITY OF SUCH DAMAGE.
27*/
28
29#include <sys/types.h>
30#include <sys/stat.h>
31#include <errno.h>
32#include <signal.h>
33#include <stdarg.h>
34#include <string.h>
35#include <unistd.h>
36
37#include <gtk/gtk.h>
38#include <glib/gi18n.h>
39
40#include "tr_torrent.h"
41#include "util.h"
42
43#define BESTDECIMAL(d)          (10.0 > (d) ? 2 : (100.0 > (d) ? 1 : 0))
44
45static void
46errcb(GtkWidget *wind, int resp, gpointer data);
47
48gboolean
49strbool(const char *str) {
50  switch(str[0]) {
51    case 'y':
52    case 'Y':
53    case '1':
54    case 'j':
55    case 'e':
56      return TRUE;
57    default:
58      if(0 == g_ascii_strcasecmp("on", str))
59        return TRUE;
60      break;
61  }
62
63  return FALSE;
64}
65
66static const char *sizestrs[] = {
67  N_("B"), N_("KiB"), N_("MiB"), N_("GiB"), N_("TiB"), N_("PiB"), N_("EiB"),
68};
69
70char *
71readablesize(guint64 size) {
72  unsigned int ii;
73  double small = size;
74
75  for(ii = 0; ii + 1 < ALEN(sizestrs) && 1024.0 <= small / 1024.0; ii++)
76    small /= 1024.0;
77
78  if(1024.0 <= small) {
79    small /= 1024.0;
80    ii++;
81  }
82
83  return g_strdup_printf("%.*f %s", BESTDECIMAL(small), small,
84                         gettext(sizestrs[ii]));
85}
86
87#define SECONDS(s)              ((s) % 60)
88#define MINUTES(s)              ((s) / 60 % 60)
89#define HOURS(s)                ((s) / 60 / 60 % 24)
90#define DAYS(s)                 ((s) / 60 / 60 / 24 % 7)
91#define WEEKS(s)                ((s) / 60 / 60 / 24 / 7)
92
93char *
94readabletime(int secs) {
95  if(60 > secs)
96    return g_strdup_printf(_("%i %s"),
97      SECONDS(secs), ngettext("second", "seconds", SECONDS(secs)));
98  else if(60 * 60 > secs)
99    return g_strdup_printf(_("%i %s %i %s"),
100      MINUTES(secs), ngettext("minute", "minutes", MINUTES(secs)),
101      SECONDS(secs), ngettext("second", "seconds", SECONDS(secs)));
102  else if(60 * 60 * 24 > secs)
103    return g_strdup_printf(_("%i %s %i %s"),
104      HOURS(secs),   ngettext("hour", "hours", HOURS(secs)),
105      MINUTES(secs), ngettext("minute", "minutes", MINUTES(secs)));
106  else if(60 * 60 * 24 * 7 > secs)
107    return g_strdup_printf(_("%i %s %i %s"),
108      DAYS(secs),    ngettext("day", "days", DAYS(secs)),
109      HOURS(secs),   ngettext("hour", "hours", HOURS(secs)));
110  else
111    return g_strdup_printf(_("%i %s %i %s"),
112      WEEKS(secs),   ngettext("week", "weeks", WEEKS(secs)),
113      DAYS(secs),    ngettext("hour", "hours", DAYS(secs)));
114}
115
116char *
117ratiostr(guint64 down, guint64 up) {
118  double ratio;
119
120  if(0 == up && 0 == down)
121    return g_strdup(_("N/A"));
122
123  if(0 == down)
124    /* this is a UTF-8 infinity symbol */
125    return g_strdup(_("\xE2\x88\x9E"));
126
127  ratio = (double)up / (double)down;
128
129  return g_strdup_printf("%.*f", BESTDECIMAL(ratio), ratio);
130}
131
132gboolean
133mkdir_p(const char *name, mode_t mode) {
134  struct stat sb;
135  char *parent;
136  gboolean ret;
137  int oerrno;
138
139  if(0 != stat(name, &sb)) {
140    if(ENOENT != errno)
141      return FALSE;
142    parent = g_path_get_dirname(name);
143    ret = mkdir_p(parent, mode);
144    oerrno = errno;
145    g_free(parent);
146    errno = oerrno;
147    return (ret ? (0 == mkdir(name, mode)) : FALSE);
148  }
149
150  if(!S_ISDIR(sb.st_mode)) {
151    errno = ENOTDIR;
152    return FALSE;
153  }
154
155  return TRUE;
156}
157
158char *
159joinstrlist(GList *list, char *sep) {
160  GList *ii;
161  int len;
162  char *ret, *dest;
163
164  if(0 > (len = strlen(sep) * (g_list_length(list) - 1)))
165    return NULL;
166
167  for(ii = g_list_first(list); NULL != ii; ii = ii->next)
168    len += strlen(ii->data);
169
170  dest = ret = g_new(char, len + 1);
171
172  for(ii = g_list_first(list); NULL != ii; ii = ii->next) {
173    dest = g_stpcpy(dest, ii->data);
174    if(NULL != ii->next)
175      dest = g_stpcpy(dest, sep);
176  }
177
178  return ret;
179}
180
181void
182freestrlist(GList *list) {
183  GList *ii;
184
185  if(NULL != list) {
186    for(ii = g_list_first(list); NULL != ii; ii = ii->next)
187      g_free(ii->data);
188    g_list_free(list);
189  }
190}
191
192char *
193urldecode(const char *str, int len) {
194  int ii, jj;
195  char *ret;
196  char buf[3];
197
198  if(0 >= len)
199    len = strlen(str);
200
201  for(ii = jj = 0; ii < len; ii++, jj++)
202    if('%' == str[ii])
203      ii += 2;
204
205  ret = g_new(char, jj + 1);
206
207  buf[2] = '\0';
208  for(ii = jj = 0; ii < len; ii++, jj++) {
209    switch(str[ii]) {
210      case '%':
211        if(ii + 2 < len) {
212          buf[0] = str[ii+1];
213          buf[1] = str[ii+2];
214          ret[jj] = g_ascii_strtoull(buf, NULL, 16);
215        }
216        ii += 2;
217        break;
218      case '+':
219        ret[jj] = ' ';
220      default:
221        ret[jj] = str[ii];
222    }
223  }
224  ret[jj] = '\0';
225
226  return ret;
227}
228
229GList *
230checkfilenames(int argc, char **argv) {
231  char *pwd = g_get_current_dir();
232  int ii, cd;
233  char *dirstr, *filestr;
234  GList *ret = NULL;
235
236  for(ii = 0; ii < argc; ii++) {
237    dirstr = g_path_get_dirname(argv[ii]);
238    if(!g_path_is_absolute(argv[ii])) {
239      filestr = g_build_filename(pwd, dirstr, NULL);
240      g_free(dirstr);
241      dirstr = filestr;
242    }
243    cd = chdir(dirstr);
244    g_free(dirstr);
245    if(0 > cd)
246      continue;
247    dirstr = g_get_current_dir();
248    filestr = g_path_get_basename(argv[ii]);
249    ret = g_list_append(ret, g_build_filename(dirstr, filestr, NULL));
250    g_free(dirstr);
251    g_free(filestr);
252  }
253
254  chdir(pwd);
255  g_free(pwd);
256
257  return ret;
258}
259
260guint
261addactionflag(const char *action) {
262  if(NULL == action)
263    return TR_TORNEW_SAVE_COPY;
264  else if(0 == strcmp("copy", action))
265    return TR_TORNEW_SAVE_COPY;
266  else if(0 == strcmp("move", action))
267    return TR_TORNEW_SAVE_MOVE;
268  else
269    return 0;
270}
271
272const char *
273addactionname(guint flag) {
274  static char name[6];
275
276  if(TR_TORNEW_SAVE_COPY & flag)
277    strcpy(name, "copy");
278  else if(TR_TORNEW_SAVE_MOVE & flag)
279    strcpy(name, "move");
280  else
281    strcpy(name, "leave");
282
283  return name;
284}
285
286GList *
287makeglist(void *ptr, ...) {
288  va_list ap;
289  GList *ret;
290
291  ret = g_list_append(NULL, ptr);
292
293  va_start(ap, ptr); 
294  while(NULL != (ptr = va_arg(ap, void*)))
295    ret = g_list_append(ret, ptr);
296  va_end(ap);
297
298  return ret;
299}
300
301GtkWidget *
302errmsg(GtkWindow *wind, const char *format, ...) {
303  GtkWidget *dialog;
304  va_list ap;
305
306  va_start(ap, format);
307  dialog = verrmsg(wind, NULL, NULL, format, ap);
308  va_end(ap);
309
310  return dialog;
311}
312
313GtkWidget *
314errmsg_full(GtkWindow *wind, callbackfunc_t func, void *data,
315            const char *format, ...) {
316  GtkWidget *dialog;
317  va_list ap;
318
319  va_start(ap, format);
320  dialog = verrmsg(wind, func, data, format, ap);
321  va_end(ap);
322
323  return dialog;
324}
325
326GtkWidget *
327verrmsg(GtkWindow *wind, callbackfunc_t func, void *data,
328        const char *format, va_list ap) {
329  GtkWidget *dialog;
330  char *msg;
331  GList *funcdata;
332
333  msg = g_strdup_vprintf(format, ap);
334
335  if(NULL == wind)
336    dialog = gtk_message_dialog_new(
337      NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", msg);
338  else
339    dialog = gtk_message_dialog_new(wind,
340      GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
341      GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", msg);
342
343  if(NULL == func)
344    funcdata = NULL;
345  else
346    funcdata = g_list_append(g_list_append(NULL, func), data);
347  g_signal_connect(dialog, "response", G_CALLBACK(errcb), funcdata);
348  if(NULL != wind)
349    gtk_widget_show(dialog);
350  g_free(msg);
351
352  return dialog;
353}
354
355static void
356errcb(GtkWidget *widget, int resp SHUTUP, gpointer data) {
357  GList *funcdata;
358  callbackfunc_t func;
359
360  if(NULL != data) {
361    funcdata = g_list_first(data);
362    func = funcdata->data;
363    data = funcdata->next->data;
364    func(data);
365    g_list_free(funcdata);
366  }
367
368  gtk_widget_destroy(widget);
369}
Note: See TracBrowser for help on using the repository browser.