source: util.c @ 47efcf5

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