source: util.c @ 351c535

release-1.8release-1.9
Last change on this file since 351c535 was 9efa5bd, checked in by David Benjamin <davidben@mit.edu>, 10 years ago
Clamp the number of color pairs to 256 without ext-color Debian doesn't build their ncurses with ext-color support, so it only supports 256 colorpairs. However, ncurses still reports the full 32768 value, and color pairs end up trampling over each other. When 256 is exceeded, the existing colorpair reset logic will now kick in and fix things. Reword a comment accordingly. While I'm here, get rid of the colorpairs member in owl_global. ncurses' works just fine. Reported-by: Mats Ahlgren <mats_a@mit.edu>
  • Property mode set to 100644
File size: 20.1 KB
Line 
1#include "owl.h"
2#include <stdlib.h>
3#include <string.h>
4#include <unistd.h>
5#include <ctype.h>
6#include <pwd.h>
7#include <sys/stat.h>
8#include <sys/types.h>
9#include <assert.h>
10#include <stdarg.h>
11#include <glib.h>
12#include <glib/gstdio.h>
13#include <glib-object.h>
14
15const char *skiptokens(const char *buff, int n) {
16  /* skips n tokens and returns where that would be. */
17  char quote = 0;
18  while (*buff && n>0) {
19      while (*buff == ' ') buff++;
20      while (*buff && (quote || *buff != ' ')) {
21        if(quote) {
22          if(*buff == quote) quote = 0;
23        } else if(*buff == '"' || *buff == '\'') {
24          quote = *buff;
25        }
26        buff++;
27      }
28      while (*buff == ' ') buff++;
29      n--;
30  }
31  return buff;
32}
33
34/* Return a "nice" version of the path.  Tilde expansion is done, and
35 * duplicate slashes are removed.  Caller must free the return.
36 */
37char *owl_util_makepath(const char *in)
38{
39  int i, j, x;
40  char *out, user[MAXPATHLEN];
41  struct passwd *pw;
42
43  out=g_new(char, MAXPATHLEN+1);
44  out[0]='\0';
45  j=strlen(in);
46  x=0;
47  for (i=0; i<j; i++) {
48    if (in[i]=='~') {
49      if ( (i==(j-1)) ||          /* last character */
50           (in[i+1]=='/') ) {     /* ~/ */
51        /* use my homedir */
52        pw=getpwuid(getuid());
53        if (!pw) {
54          out[x]=in[i];
55        } else {
56          out[x]='\0';
57          strcat(out, pw->pw_dir);
58          x+=strlen(pw->pw_dir);
59        }
60      } else {
61        /* another user homedir */
62        int a, b;
63        b=0;
64        for (a=i+1; i<j; a++) {
65          if (in[a]==' ' || in[a]=='/') {
66            break;
67          } else {
68            user[b]=in[a];
69            i++;
70            b++;
71          }
72        }
73        user[b]='\0';
74        pw=getpwnam(user);
75        if (!pw) {
76          out[x]=in[i];
77        } else {
78          out[x]='\0';
79          strcat(out, pw->pw_dir);
80          x+=strlen(pw->pw_dir);
81        }
82      }
83    } else if (in[i]=='/') {
84      /* check for a double / */
85      if (i<(j-1) && (in[i+1]=='/')) {
86        /* do nothing */
87      } else {
88        out[x]=in[i];
89        x++;
90      }
91    } else {
92      out[x]=in[i];
93      x++;
94    }
95  }
96  out[x]='\0';
97  return(out);
98}
99
100/* Break a command line up into argv, argc.  The caller must free
101   the returned values with g_strfreev.  If there is an error argc will be set
102   to -1, argv will be NULL and the caller does not need to free anything. The
103   returned vector is NULL-terminated. */
104char **owl_parseline(const char *line, int *argc)
105{
106  GPtrArray *argv;
107  int i, len, between=1;
108  GString *curarg;
109  char quote;
110
111  argv = g_ptr_array_new();
112  len=strlen(line);
113  curarg = g_string_new("");
114  quote='\0';
115  if (argc) *argc=0;
116  for (i=0; i<len+1; i++) {
117    /* find the first real character */
118    if (between) {
119      if (line[i]==' ' || line[i]=='\t' || line[i]=='\0') {
120        continue;
121      } else {
122        between=0;
123        i--;
124        continue;
125      }
126    }
127
128    /* deal with a quote character */
129    if (line[i]=='"' || line[i]=="'"[0]) {
130      /* if this type of quote is open, close it */
131      if (quote==line[i]) {
132        quote='\0';
133        continue;
134      }
135
136      /* if no quoting is open then open with this */
137      if (quote=='\0') {
138        quote=line[i];
139        continue;
140      }
141
142      /* if another type of quote is open then treat this as a literal */
143      g_string_append_c(curarg, line[i]);
144      continue;
145    }
146
147    /* if it's not a space or end of command, then use it */
148    if (line[i]!=' ' && line[i]!='\t' && line[i]!='\n' && line[i]!='\0') {
149      g_string_append_c(curarg, line[i]);
150      continue;
151    }
152
153    /* otherwise, if we're not in quotes, add the whole argument */
154    if (quote=='\0') {
155      /* add the argument */
156      g_ptr_array_add(argv, g_string_free(curarg, false));
157      curarg = g_string_new("");
158      between=1;
159      continue;
160    }
161
162    /* if it is a space and we're in quotes, then use it */
163    g_string_append_c(curarg, line[i]);
164  }
165
166  if (argc) *argc = argv->len;
167  g_ptr_array_add(argv, NULL);
168  g_string_free(curarg, true);
169
170  /* check for unbalanced quotes */
171  if (quote!='\0') {
172    /* TODO: when we move to requiring glib 2.22+, use
173     * g_ptr_array_new_with_free_func. */
174    g_ptr_array_foreach(argv, (GFunc)g_free, NULL);
175    g_ptr_array_free(argv, true);
176    if (argc) *argc = -1;
177    return(NULL);
178  }
179
180  return (char**)g_ptr_array_free(argv, false);
181}
182
183/* Appends a quoted version of arg suitable for placing in a
184 * command-line to a GString. Does not append a space. */
185void owl_string_append_quoted_arg(GString *buf, const char *arg)
186{
187  const char *argp;
188  if (arg[0] == '\0') {
189    /* Quote the empty string. */
190    g_string_append(buf, "''");
191  } else if (arg[strcspn(arg, "'\" \n\t")] == '\0') {
192    /* If there are no nasty characters, return as-is. */
193    g_string_append(buf, arg);
194  } else if (!strchr(arg, '\'')) {
195    /* Single-quote if possible. */
196    g_string_append_c(buf, '\'');
197    g_string_append(buf, arg);
198    g_string_append_c(buf, '\'');
199  } else {
200    /* Nasty case: double-quote, but change all internal "s to "'"'"
201     * so that they are single-quoted because we're too cool for
202     * backslashes.
203     */
204    g_string_append_c(buf, '"');
205    for (argp = arg; *argp; argp++) {
206      if (*argp == '"')
207        g_string_append(buf, "\"'\"'\"");
208      else
209        g_string_append_c(buf, *argp);
210    }
211    g_string_append_c(buf, '"');
212  }
213}
214
215/*
216 * Appends 'tmpl' to 'buf', replacing any instances of '%q' with arguments from
217 * the varargs provided, quoting them to be safe for placing in a barnowl
218 * command line.
219 */
220void owl_string_appendf_quoted(GString *buf, const char *tmpl, ...)
221{
222  va_list ap;
223  va_start(ap, tmpl);
224  owl_string_vappendf_quoted(buf, tmpl, ap);
225  va_end(ap);
226}
227
228void owl_string_vappendf_quoted(GString *buf, const char *tmpl, va_list ap)
229{
230  const char *p = tmpl, *last = tmpl;
231  while (true) {
232    p = strchr(p, '%');
233    if (p == NULL) break;
234    if (*(p+1) != 'q') {
235      p++;
236      if (*p) p++;
237      continue;
238    }
239    g_string_append_len(buf, last, p - last);
240    owl_string_append_quoted_arg(buf, va_arg(ap, char *));
241    p += 2; last = p;
242  }
243
244  g_string_append(buf, last);
245}
246
247char *owl_string_build_quoted(const char *tmpl, ...)
248{
249  GString *buf = g_string_new("");
250  va_list ap;
251  va_start(ap, tmpl);
252  owl_string_vappendf_quoted(buf, tmpl, ap);
253  va_end(ap);
254  return g_string_free(buf, false); 
255}
256
257/* Returns a quoted version of arg suitable for placing in a
258 * command-line. Result should be freed with g_free. */
259char *owl_arg_quote(const char *arg)
260{
261  GString *buf = g_string_new("");;
262  owl_string_append_quoted_arg(buf, arg);
263  return g_string_free(buf, false);
264}
265
266/* caller must free the return */
267char *owl_util_minutes_to_timestr(int in)
268{
269  int days, hours;
270  long run;
271  char *out;
272
273  run=in;
274
275  days=run/1440;
276  run-=days*1440;
277  hours=run/60;
278  run-=hours*60;
279
280  if (days>0) {
281    out=g_strdup_printf("%i d %2.2i:%2.2li", days, hours, run);
282  } else {
283    out=g_strdup_printf("    %2.2i:%2.2li", hours, run);
284  }
285  return(out);
286}
287
288/* These are in order of their value in owl.h */
289static const struct {
290  int number;
291  const char *name;
292} color_map[] = {
293  {OWL_COLOR_INVALID, "invalid"},
294  {OWL_COLOR_DEFAULT, "default"},
295  {OWL_COLOR_BLACK, "black"},
296  {OWL_COLOR_RED, "red"},
297  {OWL_COLOR_GREEN, "green"},
298  {OWL_COLOR_YELLOW,"yellow"},
299  {OWL_COLOR_BLUE, "blue"},
300  {OWL_COLOR_MAGENTA, "magenta"},
301  {OWL_COLOR_CYAN, "cyan"},
302  {OWL_COLOR_WHITE, "white"},
303};
304
305/* Return the owl color associated with the named color.  Return -1
306 * if the named color is not available
307 */
308int owl_util_string_to_color(const char *color)
309{
310  int c, i;
311  char *p;
312
313  for (i = 0; i < (sizeof(color_map)/sizeof(color_map[0])); i++)
314    if (strcasecmp(color, color_map[i].name) == 0)
315      return color_map[i].number;
316
317  c = strtol(color, &p, 10);
318  if (p != color && c >= -1 && c < COLORS) {
319    return(c);
320  }
321  return(OWL_COLOR_INVALID);
322}
323
324/* Return a string name of the given owl color */
325const char *owl_util_color_to_string(int color)
326{
327  if (color >= OWL_COLOR_INVALID && color <= OWL_COLOR_WHITE)
328    return color_map[color - OWL_COLOR_INVALID].name;
329  return("Unknown color");
330}
331
332/* Get the default tty name.  Caller must free the return */
333char *owl_util_get_default_tty(void)
334{
335  const char *tmp;
336  char *out;
337
338  if (getenv("DISPLAY")) {
339    out=g_strdup(getenv("DISPLAY"));
340  } else if ((tmp=ttyname(fileno(stdout)))!=NULL) {
341    out=g_strdup(tmp);
342    if (!strncmp(out, "/dev/", 5)) {
343      g_free(out);
344      out=g_strdup(tmp+5);
345    }
346  } else {
347    out=g_strdup("unknown");
348  }
349  return(out);
350}
351
352/* strip leading and trailing new lines.  Caller must free the
353 * return.
354 */
355char *owl_util_stripnewlines(const char *in)
356{
357 
358  char  *tmp, *ptr1, *ptr2, *out;
359
360  ptr1=tmp=g_strdup(in);
361  while (ptr1[0]=='\n') {
362    ptr1++;
363  }
364  ptr2=ptr1+strlen(ptr1)-1;
365  while (ptr2>ptr1 && ptr2[0]=='\n') {
366    ptr2[0]='\0';
367    ptr2--;
368  }
369
370  out=g_strdup(ptr1);
371  g_free(tmp);
372  return(out);
373}
374
375
376/* If filename is a link, recursively resolve symlinks.  Otherwise, return the filename
377 * unchanged.  On error, call owl_function_error and return NULL.
378 *
379 * This function assumes that filename eventually resolves to an acutal file.
380 * If you want to check this, you should stat() the file first.
381 *
382 * The caller of this function is responsible for freeing the return value.
383 *
384 * Error conditions are the same as g_file_read_link.
385 */
386gchar *owl_util_recursive_resolve_link(const char *filename)
387{
388  gchar *last_path = g_strdup(filename);
389  GError *err = NULL;
390
391  while (g_file_test(last_path, G_FILE_TEST_IS_SYMLINK)) {
392    gchar *link_path = g_file_read_link(last_path, &err);
393    if (link_path == NULL) {
394      owl_function_error("Cannot resolve symlink %s: %s",
395                         last_path, err->message);
396      g_error_free(err);
397      g_free(last_path);
398      return NULL;
399    }
400
401    /* Deal with obnoxious relative paths. If we really care, all this
402     * is racy. Whatever. */
403    if (!g_path_is_absolute(link_path)) {
404      char *last_dir = g_path_get_dirname(last_path);
405      char *tmp = g_build_path(G_DIR_SEPARATOR_S,
406                               last_dir,
407                               link_path,
408                               NULL);
409      g_free(last_dir);
410      g_free(link_path);
411      link_path = tmp;
412    }
413
414    g_free(last_path);
415    last_path = link_path;
416  }
417  return last_path;
418}
419
420/* Delete all lines matching "line" from the named file.  If no such
421 * line is found the file is left intact.  If backup==1 then leave a
422 * backup file containing the original contents.  The match is
423 * case-insensitive.
424 *
425 * Returns the number of lines removed on success.  Returns -1 on failure.
426 */
427int owl_util_file_deleteline(const char *filename, const char *line, int backup)
428{
429  char *backupfile, *newfile, *buf = NULL;
430  gchar *actual_filename; /* gchar; we need to g_free it */
431  FILE *old, *new;
432  struct stat st;
433  int numremoved = 0;
434
435  if ((old = fopen(filename, "r")) == NULL) {
436    owl_function_error("Cannot open %s (for reading): %s",
437                       filename, strerror(errno));
438    return -1;
439  }
440
441  if (fstat(fileno(old), &st) != 0) {
442    owl_function_error("Cannot stat %s: %s", filename, strerror(errno));
443    return -1;
444  }
445
446  /* resolve symlinks, because link() fails on symlinks, at least on AFS */
447  actual_filename = owl_util_recursive_resolve_link(filename);
448  if (actual_filename == NULL)
449    return -1; /* resolving the symlink failed, but we already logged this error */
450
451  newfile = g_strdup_printf("%s.new", actual_filename);
452  if ((new = fopen(newfile, "w")) == NULL) {
453    owl_function_error("Cannot open %s (for writing): %s",
454                       actual_filename, strerror(errno));
455    g_free(newfile);
456    fclose(old);
457    free(actual_filename);
458    return -1;
459  }
460
461  if (fchmod(fileno(new), st.st_mode & 0777) != 0) {
462    owl_function_error("Cannot set permissions on %s: %s",
463                       actual_filename, strerror(errno));
464    unlink(newfile);
465    fclose(new);
466    g_free(newfile);
467    fclose(old);
468    free(actual_filename);
469    return -1;
470  }
471
472  while (owl_getline_chomp(&buf, old))
473    if (strcasecmp(buf, line) != 0)
474      fprintf(new, "%s\n", buf);
475    else
476      numremoved++;
477  g_free(buf);
478
479  fclose(new);
480  fclose(old);
481
482  if (backup) {
483    backupfile = g_strdup_printf("%s.backup", actual_filename);
484    unlink(backupfile);
485    if (link(actual_filename, backupfile) != 0) {
486      owl_function_error("Cannot link %s: %s", backupfile, strerror(errno));
487      g_free(backupfile);
488      unlink(newfile);
489      g_free(newfile);
490      return -1;
491    }
492    g_free(backupfile);
493  }
494
495  if (rename(newfile, actual_filename) != 0) {
496    owl_function_error("Cannot move %s to %s: %s",
497                       newfile, actual_filename, strerror(errno));
498    numremoved = -1;
499  }
500
501  unlink(newfile);
502  g_free(newfile);
503
504  g_free(actual_filename);
505
506  return numremoved;
507}
508
509/* Return the base class or instance from a zephyr class, by removing
510   leading `un' or trailing `.d'.
511   The caller is responsible for freeing the allocated string.
512*/
513char * owl_util_baseclass(const char * class)
514{
515  char *start, *end;
516
517  while(!strncmp(class, "un", 2)) {
518    class += 2;
519  }
520
521  start = g_strdup(class);
522  end = start + strlen(start) - 1;
523  while(end > start && *end == 'd' && *(end-1) == '.') {
524    end -= 2;
525  }
526  *(end + 1) = 0;
527
528  return start;
529}
530
531const char * owl_get_datadir(void)
532{
533  const char * datadir = getenv("BARNOWL_DATA_DIR");
534  if(datadir != NULL)
535    return datadir;
536  return DATADIR;
537}
538
539const char * owl_get_bindir(void)
540{
541  const char * bindir = getenv("BARNOWL_BIN_DIR");
542  if(bindir != NULL)
543    return bindir;
544  return BINDIR;
545}
546
547/* Strips format characters from a valid utf-8 string. Returns the
548   empty string if 'in' does not validate. */
549char * owl_strip_format_chars(const char *in)
550{
551  char *r;
552  if (g_utf8_validate(in, -1, NULL)) {
553    const char *s, *p;
554    r = g_new(char, strlen(in)+1);
555    r[0] = '\0';
556    s = in;
557    p = strchr(s, OWL_FMTEXT_UC_STARTBYTE_UTF8);
558    while(p) {
559      /* If it's a format character, copy up to it, and skip all
560         immediately following format characters. */
561      if (owl_fmtext_is_format_char(g_utf8_get_char(p))) {
562        strncat(r, s, p-s);
563        p = g_utf8_next_char(p);
564        while (owl_fmtext_is_format_char(g_utf8_get_char(p))) {
565          p = g_utf8_next_char(p);
566        }
567        s = p;
568        p = strchr(s, OWL_FMTEXT_UC_STARTBYTE_UTF8);
569      }
570      else {
571        p = strchr(p+1, OWL_FMTEXT_UC_STARTBYTE_UTF8);
572      }
573    }
574    if (s) strcat(r,s);
575  }
576  else {
577    r = g_strdup("");
578  }
579  return r;
580}
581
582/* If in is not UTF-8, convert from ISO-8859-1. We may want to allow
583 * the caller to specify an alternative in the future. We also strip
584 * out characters in Unicode Plane 16, as we use that plane internally
585 * for formatting.
586 */
587char * owl_validate_or_convert(const char *in)
588{
589  if (g_utf8_validate(in, -1, NULL)) {
590    return owl_strip_format_chars(in);
591  }
592  else {
593    return g_convert(in, -1,
594                     "UTF-8", "ISO-8859-1",
595                     NULL, NULL, NULL);
596  }
597}
598/*
599 * Validate 'in' as UTF-8, and either return a copy of it, or an empty
600 * string if it is invalid utf-8.
601 */
602char * owl_validate_utf8(const char *in)
603{
604  char *out;
605  if (g_utf8_validate(in, -1, NULL)) {
606    out = g_strdup(in);
607  } else {
608    out = g_strdup("");
609  }
610  return out;
611}
612
613/* This is based on _extract() and _isCJ() from perl's Text::WrapI18N */
614int owl_util_can_break_after(gunichar c)
615{
616 
617  if (c == ' ') return 1;
618  if (c >= 0x3000 && c <= 0x312f) {
619    /* CJK punctuations, Hiragana, Katakana, Bopomofo */
620    if (c == 0x300a || c == 0x300c || c == 0x300e ||
621        c == 0x3010 || c == 0x3014 || c == 0x3016 ||
622        c == 0x3018 || c == 0x301a)
623      return 0;
624    return 1;
625  }
626  if (c >= 0x31a0 && c <= 0x31bf) {return 1;}  /* Bopomofo */
627  if (c >= 0x31f0 && c <= 0x31ff) {return 1;}  /* Katakana extension */
628  if (c >= 0x3400 && c <= 0x9fff) {return 1;}  /* Han Ideogram */
629  if (c >= 0xf900 && c <= 0xfaff) {return 1;}  /* Han Ideogram */
630  if (c >= 0x20000 && c <= 0x2ffff) {return 1;}  /* Han Ideogram */
631  return 0;
632}
633
634char *owl_escape_highbit(const char *str)
635{
636  GString *out = g_string_new("");
637  unsigned char c;
638  while((c = (*str++))) {
639    if(c == '\\') {
640      g_string_append(out, "\\\\");
641    } else if(c & 0x80) {
642      g_string_append_printf(out, "\\x%02x", (int)c);
643    } else {
644      g_string_append_c(out, c);
645    }
646  }
647  return g_string_free(out, 0);
648}
649
650/* innards of owl_getline{,_chomp} below */
651static int owl_getline_internal(char **s, FILE *fp, int newline)
652{
653  int size = 0;
654  int target = 0;
655  int count = 0;
656  int c;
657
658  while (1) {
659    c = getc(fp);
660    if ((target + 1) > size) {
661      size += BUFSIZ;
662      *s = g_renew(char, *s, size);
663    }
664    if (c == EOF)
665      break;
666    count++;
667    if (c != '\n' || newline)
668        (*s)[target++] = c;
669    if (c == '\n')
670      break;
671  }
672  (*s)[target] = 0;
673
674  return count;
675}
676
677/* Read a line from fp, allocating memory to hold it, returning the number of
678 * byte read.  *s should either be NULL or a pointer to memory allocated with
679 * g_malloc; it will be g_renew'd as appropriate.  The caller must
680 * eventually free it.  (This is roughly the interface of getline in the gnu
681 * libc).
682 *
683 * The final newline will be included if it's there.
684 */
685int owl_getline(char **s, FILE *fp)
686{
687  return owl_getline_internal(s, fp, 1);
688}
689
690/* As above, but omitting the final newline */
691int owl_getline_chomp(char **s, FILE *fp)
692{
693  return owl_getline_internal(s, fp, 0);
694}
695
696/* Read the rest of the input available in fp into a string. */
697char *owl_slurp(FILE *fp)
698{
699  char *buf = NULL;
700  char *p;
701  int size = 0;
702  int count;
703
704  while (1) {
705    buf = g_renew(char, buf, size + BUFSIZ);
706    p = &buf[size];
707    size += BUFSIZ;
708
709    if ((count = fread(p, 1, BUFSIZ, fp)) < BUFSIZ)
710      break;
711  }
712  p[count] = 0;
713
714  return buf;
715}
716
717int owl_util_get_colorpairs(void) {
718#ifndef NCURSES_EXT_COLORS
719  /* Without ext-color support (an ABI change), ncurses only supports 256
720   * different color pairs. However, it gives us a larger number even if your
721   * ncurses is compiled without ext-color. */
722  return MIN(COLOR_PAIRS, 256);
723#else
724  return COLOR_PAIRS;
725#endif
726}
727
728gulong owl_dirty_window_on_signal(owl_window *w, gpointer sender, const gchar *detailed_signal)
729{
730  return owl_signal_connect_object(sender, detailed_signal, G_CALLBACK(owl_window_dirty), w, G_CONNECT_SWAPPED);
731}
732
733typedef struct { /*noproto*/
734  GObject  *sender;
735  gulong    signal_id;
736} SignalData;
737
738static void _closure_invalidated(gpointer data, GClosure *closure);
739
740/*
741 * GObject's g_signal_connect_object has a documented bug. This function is
742 * identical except it does not leak the signal handler.
743 */
744gulong owl_signal_connect_object(gpointer sender, const gchar *detailed_signal, GCallback c_handler, gpointer receiver, GConnectFlags connect_flags)
745{
746  g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (sender), 0);
747  g_return_val_if_fail (detailed_signal != NULL, 0);
748  g_return_val_if_fail (c_handler != NULL, 0);
749
750  if (receiver) {
751    SignalData *sdata;
752    GClosure *closure;
753    gulong signal_id;
754
755    g_return_val_if_fail (G_IS_OBJECT (receiver), 0);
756
757    closure = ((connect_flags & G_CONNECT_SWAPPED) ? g_cclosure_new_object_swap : g_cclosure_new_object) (c_handler, receiver);
758    signal_id = g_signal_connect_closure (sender, detailed_signal, closure, connect_flags & G_CONNECT_AFTER);
759
760    /* Register the missing hooks */
761    sdata = g_slice_new0(SignalData);
762    sdata->sender = sender;
763    sdata->signal_id = signal_id;
764
765    g_closure_add_invalidate_notifier(closure, sdata, _closure_invalidated);
766
767    return signal_id;
768  } else {
769    return g_signal_connect_data(sender, detailed_signal, c_handler, NULL, NULL, connect_flags);
770  }
771}
772
773/*
774 * There are three ways the signal could come to an end:
775 *
776 * 1. The user explicitly disconnects it with the returned signal_id.
777 *    - In that case, the disconnection unref's the closure, causing it
778 *      to first be invalidated. The handler's already disconnected, so
779 *      we have no work to do.
780 * 2. The sender gets destroyed.
781 *    - GObject will disconnect each signal which then goes into the above
782 *      case. Our handler does no work.
783 * 3. The receiver gets destroyed.
784 *    - The GClosure was created by g_cclosure_new_object_{,swap} which gets
785 *      invalidated when the receiver is destroyed. We then follow through case 1
786 *      again, but *this* time, the handler has not been disconnected. We then
787 *      clean up ourselves.
788 *
789 * We can't actually hook into this process earlier with weakrefs as GObject
790 * will, on object dispose, first disconnect signals, then invalidate closures,
791 * and notify weakrefs last.
792 */
793static void _closure_invalidated(gpointer data, GClosure *closure)
794{
795  SignalData *sdata = data;
796  if (g_signal_handler_is_connected(sdata->sender, sdata->signal_id)) {
797    g_signal_handler_disconnect(sdata->sender, sdata->signal_id);
798  }
799  g_slice_free(SignalData, sdata);
800}
801
Note: See TracBrowser for help on using the repository browser.