source: util.c @ 5fca55f

release-1.7release-1.8release-1.9
Last change on this file since 5fca55f was 5fca55f, checked in by Karl Ramm <kcr@1ts.org>, 11 years ago
I/O errors on writing subscription file in :unsub are no longer clobbered by "No subscription present in..." owl_util_file_deleteline now returns -1 on error, and owl_zephyr_delsub now checks linesdeleted == 0 before erroring with "No subscription present in...". Additionally, free(filename) corrected to owl_free(filename) in two places. Reviewed-By: Karl Ramm <kcr@1ts.org>
  • Property mode set to 100644
File size: 17.1 KB
RevLine 
[7d4fbcd]1#include "owl.h"
2#include <stdlib.h>
3#include <string.h>
[5145235]4#include <unistd.h>
[7d4fbcd]5#include <ctype.h>
[f36222f]6#include <pwd.h>
[435001d]7#include <sys/stat.h>
8#include <sys/types.h>
[7d4fbcd]9
[05ca0d8]10#include <glib-object.h>
11
[e19eb97]12char **atokenize(const char *buffer, const char *sep, int *i)
[e4eebe8]13{
[7d4fbcd]14  /* each element of return must be freed by user */
15  char **args;
16  char *workbuff, *foo;
17  int done=0, first=1, count=0;
18
[46d940a]19  workbuff = owl_strdup(buffer);
[7d4fbcd]20
21  args=NULL;
22  while (!done) {
23    if (first) {
24      first=0;
[4d86e06]25      foo=strtok(workbuff, sep);
[7d4fbcd]26    } else {
[4d86e06]27      foo=strtok(NULL, sep);
[7d4fbcd]28    }
29    if (foo==NULL) {
30      done=1;
31    } else {
[4d86e06]32      args=owl_realloc(args, sizeof(char *) * (count+1));
[36486be]33      args[count] = owl_strdup(foo);
[7d4fbcd]34      count++;
35    }
36  }
37  *i=count;
38  owl_free(workbuff);
39  return(args);
40}
41
[e19eb97]42const char *skiptokens(const char *buff, int n) {
[e30ed92]43  /* skips n tokens and returns where that would be. */
44  char quote = 0;
[7d4fbcd]45  while (*buff && n>0) {
46      while (*buff == ' ') buff++;
[e30ed92]47      while (*buff && (quote || *buff != ' ')) {
48        if(quote) {
49          if(*buff == quote) quote = 0;
50        } else if(*buff == '"' || *buff == '\'') {
51          quote = *buff;
52        }
53        buff++;
[7d4fbcd]54      }
55      while (*buff == ' ') buff++;
56      n--;
57  }
58  return buff;
59}
60
[f36222f]61/* Return a "nice" version of the path.  Tilde expansion is done, and
62 * duplicate slashes are removed.  Caller must free the return.
63 */
[e19eb97]64char *owl_util_makepath(const char *in)
[f36222f]65{
66  int i, j, x;
67  char *out, user[MAXPATHLEN];
68  struct passwd *pw;
69
70  out=owl_malloc(MAXPATHLEN+1);
71  out[0]='\0';
72  j=strlen(in);
73  x=0;
74  for (i=0; i<j; i++) {
75    if (in[i]=='~') {
76      if ( (i==(j-1)) ||          /* last character */
77           (in[i+1]=='/') ) {     /* ~/ */
78        /* use my homedir */
79        pw=getpwuid(getuid());
80        if (!pw) {
81          out[x]=in[i];
82        } else {
83          out[x]='\0';
84          strcat(out, pw->pw_dir);
85          x+=strlen(pw->pw_dir);
86        }
87      } else {
88        /* another user homedir */
89        int a, b;
90        b=0;
91        for (a=i+1; i<j; a++) {
92          if (in[a]==' ' || in[a]=='/') {
93            break;
94          } else {
95            user[b]=in[a];
96            i++;
97            b++;
98          }
99        }
100        user[b]='\0';
101        pw=getpwnam(user);
102        if (!pw) {
[8766d44]103          out[x]=in[i];
[f36222f]104        } else {
105          out[x]='\0';
106          strcat(out, pw->pw_dir);
107          x+=strlen(pw->pw_dir);
108        }
109      }
110    } else if (in[i]=='/') {
111      /* check for a double / */
112      if (i<(j-1) && (in[i+1]=='/')) {
113        /* do nothing */
114      } else {
115        out[x]=in[i];
116        x++;
117      }
118    } else {
119      out[x]=in[i];
120      x++;
121    }
122  }
123  out[x]='\0';
124  return(out);
125}
126
[1672650]127void atokenize_delete(char **tok, int nels)
[e4eebe8]128{
[7d4fbcd]129  int i;
130  for (i=0; i<nels; i++) {
131    owl_free(tok[i]);
132  }
133  owl_free(tok);
134}
135
136
[40d22cf]137void owl_parse_delete(char **argv, int argc)
[e4eebe8]138{
[7d4fbcd]139  int i;
140
141  if (!argv) return;
142 
143  for (i=0; i<argc; i++) {
144    if (argv[i]) owl_free(argv[i]);
145  }
146  owl_free(argv);
147}
148
[e19eb97]149char **owl_parseline(const char *line, int *argc)
[e4eebe8]150{
[7d4fbcd]151  /* break a command line up into argv, argc.  The caller must free
152     the returned values.  If there is an error argc will be set to
153     -1, argv will be NULL and the caller does not need to free
154     anything */
155
156  char **argv;
157  int i, len, between=1;
158  char *curarg;
159  char quote;
160
161  argv=owl_malloc(sizeof(char *));
162  len=strlen(line);
163  curarg=owl_malloc(len+10);
164  strcpy(curarg, "");
165  quote='\0';
166  *argc=0;
167  for (i=0; i<len+1; i++) {
168    /* find the first real character */
169    if (between) {
170      if (line[i]==' ' || line[i]=='\t' || line[i]=='\0') {
171        continue;
172      } else {
173        between=0;
174        i--;
175        continue;
176      }
177    }
178
179    /* deal with a quote character */
180    if (line[i]=='"' || line[i]=="'"[0]) {
181      /* if this type of quote is open, close it */
182      if (quote==line[i]) {
183        quote='\0';
184        continue;
185      }
186
187      /* if no quoting is open then open with this */
188      if (quote=='\0') {
189        quote=line[i];
190        continue;
191      }
192
193      /* if another type of quote is open then treat this as a literal */
194      curarg[strlen(curarg)+1]='\0';
195      curarg[strlen(curarg)]=line[i];
196      continue;
197    }
198
199    /* if it's not a space or end of command, then use it */
200    if (line[i]!=' ' && line[i]!='\t' && line[i]!='\n' && line[i]!='\0') {
201      curarg[strlen(curarg)+1]='\0';
202      curarg[strlen(curarg)]=line[i];
203      continue;
204    }
205
206    /* otherwise, if we're not in quotes, add the whole argument */
207    if (quote=='\0') {
208      /* add the argument */
209      argv=owl_realloc(argv, sizeof(char *)*((*argc)+1));
[36486be]210      argv[*argc] = owl_strdup(curarg);
[7d4fbcd]211      *argc=*argc+1;
212      strcpy(curarg, "");
213      between=1;
214      continue;
215    }
216
217    /* if it is a space and we're in quotes, then use it */
218    curarg[strlen(curarg)+1]='\0';
219    curarg[strlen(curarg)]=line[i];
220  }
221
[d524c83]222  owl_free(curarg);
[95caa16]223
[7d4fbcd]224  /* check for unbalanced quotes */
225  if (quote!='\0') {
[40d22cf]226    owl_parse_delete(argv, *argc);
[7d4fbcd]227    *argc=-1;
228    return(NULL);
229  }
230
231  return(argv);
232}
233
[de03334]234/* caller must free the return */
[5b85d19]235char *owl_util_minutes_to_timestr(int in)
[de03334]236{
[f1e629d]237  int days, hours;
[de03334]238  long run;
239  char *out;
240
[5b85d19]241  run=in;
[de03334]242
[5b85d19]243  days=run/1440;
244  run-=days*1440;
245  hours=run/60;
246  run-=hours*60;
[de03334]247
248  if (days>0) {
[6eaf35b]249    out=owl_sprintf("%i d %2.2i:%2.2li", days, hours, run);
[de03334]250  } else {
[6eaf35b]251    out=owl_sprintf("    %2.2i:%2.2li", hours, run);
[de03334]252  }
253  return(out);
254}
255
[42abb10]256/* hooks for doing memory allocation et. al. in owl */
257
[e4eebe8]258void *owl_malloc(size_t size)
259{
[34509d5]260  return(g_malloc(size));
[7d4fbcd]261}
262
[e4eebe8]263void owl_free(void *ptr)
264{
[34509d5]265  g_free(ptr);
[7d4fbcd]266}
267
[e4eebe8]268char *owl_strdup(const char *s1)
269{
[34509d5]270  return(g_strdup(s1));
[7d4fbcd]271}
272
[e4eebe8]273void *owl_realloc(void *ptr, size_t size)
274{
[34509d5]275  return(g_realloc(ptr, size));
[7d4fbcd]276}
277
[e4eebe8]278/* allocates memory and returns the string or null.
279 * caller must free the string.
280 */
281char *owl_sprintf(const char *fmt, ...)
282{
[1c6c4d3]283  va_list ap;
[28ee32b]284  char *ret = NULL;
285  va_start(ap, fmt);
286  ret = g_strdup_vprintf(fmt, ap);
287  va_end(ap);
288  return ret;
[1c6c4d3]289}
290
[a3e61a2]291/* These are in order of their value in owl.h */
292static const struct {
293  int number;
294  const char *name;
295} color_map[] = {
296  {OWL_COLOR_INVALID, "invalid"},
297  {OWL_COLOR_DEFAULT, "default"},
298  {OWL_COLOR_BLACK, "black"},
299  {OWL_COLOR_RED, "red"},
300  {OWL_COLOR_GREEN, "green"},
301  {OWL_COLOR_YELLOW,"yellow"},
302  {OWL_COLOR_BLUE, "blue"},
303  {OWL_COLOR_MAGENTA, "magenta"},
304  {OWL_COLOR_CYAN, "cyan"},
305  {OWL_COLOR_WHITE, "white"},
306};
[28ee32b]307
[12c35df]308/* Return the owl color associated with the named color.  Return -1
309 * if the named color is not available
310 */
[e19eb97]311int owl_util_string_to_color(const char *color)
[e4eebe8]312{
[a3e61a2]313  int c, i;
[1b9d3cc]314  char *p;
[a3e61a2]315
316  for (i = 0; i < (sizeof(color_map)/sizeof(color_map[0])); i++)
317    if (strcasecmp(color, color_map[i].name) == 0)
318      return color_map[i].number;
319
[1b9d3cc]320  c = strtol(color, &p, 10);
321  if (p != color && c >= -1 && c < COLORS) {
[c2c5c77]322    return(c);
323  }
[601733d]324  return(OWL_COLOR_INVALID);
[7d4fbcd]325}
326
[e4eebe8]327/* Return a string name of the given owl color */
[e19eb97]328const char *owl_util_color_to_string(int color)
[e4eebe8]329{
[a3e61a2]330  if (color >= OWL_COLOR_INVALID && color <= OWL_COLOR_WHITE)
331    return color_map[color - OWL_COLOR_INVALID].name;
[7d4fbcd]332  return("Unknown color");
333}
[e1c4636]334
[e4eebe8]335/* Get the default tty name.  Caller must free the return */
[c79a047]336char *owl_util_get_default_tty(void)
[e4eebe8]337{
[e19eb97]338  const char *tmp;
[65b2173]339  char *out;
[61e79a9]340
341  if (getenv("DISPLAY")) {
342    out=owl_strdup(getenv("DISPLAY"));
[5145235]343  } else if ((tmp=ttyname(fileno(stdout)))!=NULL) {
344    out=owl_strdup(tmp);
[61e79a9]345    if (!strncmp(out, "/dev/", 5)) {
346      owl_free(out);
[5145235]347      out=owl_strdup(tmp+5);
[61e79a9]348    }
349  } else {
[5145235]350    out=owl_strdup("unknown");
[61e79a9]351  }
352  return(out);
353}
354
[e4eebe8]355/* strip leading and trailing new lines.  Caller must free the
356 * return.
357 */
[e19eb97]358char *owl_util_stripnewlines(const char *in)
[e4eebe8]359{
[7e3e00a]360 
361  char  *tmp, *ptr1, *ptr2, *out;
362
363  ptr1=tmp=owl_strdup(in);
364  while (ptr1[0]=='\n') {
365    ptr1++;
366  }
367  ptr2=ptr1+strlen(ptr1)-1;
[1bb1e67]368  while (ptr2>ptr1 && ptr2[0]=='\n') {
[7e3e00a]369    ptr2[0]='\0';
370    ptr2--;
371  }
372
373  out=owl_strdup(ptr1);
374  owl_free(tmp);
375  return(out);
376}
377
[946058b]378/* Delete all lines matching "line" from the named file.  If no such
379 * line is found the file is left intact.  If backup==1 then leave a
380 * backup file containing the original contents.  The match is
381 * case-insensitive.
[da60ba9]382 *
[5fca55f]383 * Returns the number of lines removed on success.  Returns -1 on failure.
[38cf544c]384 */
[da60ba9]385int owl_util_file_deleteline(const char *filename, const char *line, int backup)
[38cf544c]386{
[946058b]387  char *backupfile, *newfile, *buf = NULL;
388  FILE *old, *new;
389  struct stat st;
[da60ba9]390  int numremoved = 0;
[38cf544c]391
[946058b]392  if ((old = fopen(filename, "r")) == NULL) {
393    owl_function_error("Cannot open %s (for reading): %s",
394                       filename, strerror(errno));
[5fca55f]395    return -1;
[38cf544c]396  }
[e6449bc]397
[946058b]398  if (fstat(fileno(old), &st) != 0) {
399    owl_function_error("Cannot stat %s: %s", filename, strerror(errno));
[5fca55f]400    return -1;
[38cf544c]401  }
402
[946058b]403  newfile = owl_sprintf("%s.new", filename);
404  if ((new = fopen(newfile, "w")) == NULL) {
405    owl_function_error("Cannot open %s (for writing): %s",
406                       filename, strerror(errno));
[5fca55f]407    owl_free(newfile);
[946058b]408    fclose(old);
[5fca55f]409    return -1;
[946058b]410  }
411
412  if (fchmod(fileno(new), st.st_mode & 0777) != 0) {
413    owl_function_error("Cannot set permissions on %s: %s",
414                       filename, strerror(errno));
415    unlink(newfile);
416    fclose(new);
[5fca55f]417    owl_free(newfile);
[946058b]418    fclose(old);
[5fca55f]419    return -1;
[946058b]420  }
421
422  while (owl_getline_chomp(&buf, old))
423    if (strcasecmp(buf, line) != 0)
424      fprintf(new, "%s\n", buf);
425    else
[da60ba9]426      numremoved++;
[a61daae]427  owl_free(buf);
[38cf544c]428
[946058b]429  fclose(new);
430  fclose(old);
431
432  if (backup) {
433    backupfile = owl_sprintf("%s.backup", filename);
434    unlink(backupfile);
435    if (link(filename, backupfile) != 0) {
436      owl_function_error("Cannot link %s: %s", backupfile, strerror(errno));
437      owl_free(backupfile);
438      unlink(newfile);
439      owl_free(newfile);
[5fca55f]440      return -1;
[6a50af2]441    }
[946058b]442    owl_free(backupfile);
[38cf544c]443  }
444
[946058b]445  if (rename(newfile, filename) != 0) {
446    owl_function_error("Cannot move %s to %s: %s",
447                       newfile, filename, strerror(errno));
[5fca55f]448    numremoved = -1;
[38cf544c]449  }
450
[946058b]451  unlink(newfile);
452  owl_free(newfile);
[da60ba9]453
454  return numremoved;
[38cf544c]455}
456
[fe67f1f]457int owl_util_max(int a, int b)
458{
459  if (a>b) return(a);
460  return(b);
461}
462
463int owl_util_min(int a, int b)
464{
465  if (a<b) return(a);
466  return(b);
467}
468
[7a20e4c]469/* Return the base class or instance from a zephyr class, by removing
470   leading `un' or trailing `.d'.
[be5aa09]471   The caller is responsible for freeing the allocated string.
[7a20e4c]472*/
[e19eb97]473char * owl_util_baseclass(const char * class)
[7a20e4c]474{
[59916e8]475  char *start, *end;
476
[fa4562c]477  while(!strncmp(class, "un", 2)) {
478    class += 2;
[7a20e4c]479  }
[f166580]480
[fa4562c]481  start = owl_strdup(class);
[59916e8]482  end = start + strlen(start) - 1;
[04166e9]483  while(end > start && *end == 'd' && *(end-1) == '.') {
[7a20e4c]484    end -= 2;
485  }
486  *(end + 1) = 0;
[59916e8]487
[f166580]488  return start;
[7a20e4c]489}
490
[c79a047]491const char * owl_get_datadir(void)
[5376a95]492{
[e19eb97]493  const char * datadir = getenv("BARNOWL_DATA_DIR");
[5376a95]494  if(datadir != NULL)
[7bf51d5]495    return datadir;
[5376a95]496  return DATADIR;
497}
498
[9a7b4f2]499const char * owl_get_bindir(void)
500{
501  const char * bindir = getenv("BARNOWL_BIN_DIR");
502  if(bindir != NULL)
503    return bindir;
504  return BINDIR;
505}
506
[5376a95]507/* Strips format characters from a valid utf-8 string. Returns the
508   empty string if 'in' does not validate. */
[7f6a8a2]509char * owl_strip_format_chars(const char *in)
[5376a95]510{
511  char *r;
512  if (g_utf8_validate(in, -1, NULL)) {
[7f6a8a2]513    const char *s, *p;
[5376a95]514    r = owl_malloc(strlen(in)+1);
515    r[0] = '\0';
516    s = in;
517    p = strchr(s, OWL_FMTEXT_UC_STARTBYTE_UTF8);
518    while(p) {
519      /* If it's a format character, copy up to it, and skip all
520         immediately following format characters. */
[c1522ec]521      if (owl_fmtext_is_format_char(g_utf8_get_char(p))) {
[5376a95]522        strncat(r, s, p-s);
523        p = g_utf8_next_char(p);
[f119757]524        while (owl_fmtext_is_format_char(g_utf8_get_char(p))) {
[5376a95]525          p = g_utf8_next_char(p);
526        }
527        s = p;
528        p = strchr(s, OWL_FMTEXT_UC_STARTBYTE_UTF8);
529      }
530      else {
531        p = strchr(p+1, OWL_FMTEXT_UC_STARTBYTE_UTF8);
532      }
533    }
534    if (s) strcat(r,s);
535  }
536  else {
537    r = owl_strdup("");
538  }
539  return r;
540}
541
542/* If in is not UTF-8, convert from ISO-8859-1. We may want to allow
543 * the caller to specify an alternative in the future. We also strip
544 * out characters in Unicode Plane 16, as we use that plane internally
545 * for formatting.
546 */
[7f6a8a2]547char * owl_validate_or_convert(const char *in)
[5376a95]548{
[6201646]549  if (g_utf8_validate(in, -1, NULL)) {
[5376a95]550    return owl_strip_format_chars(in);
551  }
552  else {
[6201646]553    return g_convert(in, -1,
[5376a95]554                     "UTF-8", "ISO-8859-1",
555                     NULL, NULL, NULL);
556  }
[89f5338]557}
[4b17a6c]558/*
559 * Validate 'in' as UTF-8, and either return a copy of it, or an empty
560 * string if it is invalid utf-8.
[7b1d048]561 */
[7f6a8a2]562char * owl_validate_utf8(const char *in)
[7b1d048]563{
564  char *out;
565  if (g_utf8_validate(in, -1, NULL)) {
[4b17a6c]566    out = owl_strdup(in);
567  } else {
[7b1d048]568    out = owl_strdup("");
569  }
570  return out;
571}
[89f5338]572
[84027015]573/* This is based on _extract() and _isCJ() from perl's Text::WrapI18N */
574int owl_util_can_break_after(gunichar c)
575{
576 
577  if (c == ' ') return 1;
578  if (c >= 0x3000 && c <= 0x312f) {
579    /* CJK punctuations, Hiragana, Katakana, Bopomofo */
580    if (c == 0x300a || c == 0x300c || c == 0x300e ||
581        c == 0x3010 || c == 0x3014 || c == 0x3016 ||
582        c == 0x3018 || c == 0x301a)
583      return 0;
584    return 1;
585  }
586  if (c >= 0x31a0 && c <= 0x31bf) {return 1;}  /* Bopomofo */
587  if (c >= 0x31f0 && c <= 0x31ff) {return 1;}  /* Katakana extension */
588  if (c >= 0x3400 && c <= 0x9fff) {return 1;}  /* Han Ideogram */
589  if (c >= 0xf900 && c <= 0xfaff) {return 1;}  /* Han Ideogram */
590  if (c >= 0x20000 && c <= 0x2ffff) {return 1;}  /* Han Ideogram */
591  return 0;
592}
[eea72a1]593
594char *owl_escape_highbit(const char *str)
595{
596  GString *out = g_string_new("");
597  unsigned char c;
598  while((c = (*str++))) {
599    if(c == '\\') {
600      g_string_append(out, "\\\\");
601    } else if(c & 0x80) {
602      g_string_append_printf(out, "\\x%02x", (int)c);
603    } else {
604      g_string_append_c(out, c);
605    }
606  }
607  return g_string_free(out, 0);
608}
[6ace255]609
610/* innards of owl_getline{,_chomp} below */
611static int owl_getline_internal(char **s, FILE *fp, int newline)
612{
613  int size = 0;
614  int target = 0;
615  int count = 0;
616  int c;
617
618  while (1) {
619    c = getc(fp);
620    if ((target + 1) > size) {
621      size += BUFSIZ;
622      *s = owl_realloc(*s, size);
623    }
624    if (c == EOF)
625      break;
626    count++;
627    if (c != '\n' || newline)
628        (*s)[target++] = c;
629    if (c == '\n')
630      break;
631  }
632  (*s)[target] = 0;
633
634  return count;
635}
636
637/* Read a line from fp, allocating memory to hold it, returning the number of
638 * byte read.  *s should either be NULL or a pointer to memory allocated with
639 * owl_malloc; it will be owl_realloc'd as appropriate.  The caller must
640 * eventually free it.  (This is roughly the interface of getline in the gnu
641 * libc).
642 *
643 * The final newline will be included if it's there.
644 */
645int owl_getline(char **s, FILE *fp)
646{
647  return owl_getline_internal(s, fp, 1);
648}
649
650/* As above, but omitting the final newline */
651int owl_getline_chomp(char **s, FILE *fp)
652{
653  return owl_getline_internal(s, fp, 0);
654}
655
656/* Read the rest of the input available in fp into a string. */
657char *owl_slurp(FILE *fp)
658{
659  char *buf = NULL;
660  char *p;
661  int size = 0;
662  int count;
663
664  while (1) {
665    buf = owl_realloc(buf, size + BUFSIZ);
666    p = &buf[size];
667    size += BUFSIZ;
668
669    if ((count = fread(p, 1, BUFSIZ, fp)) < BUFSIZ)
670      break;
671  }
672  p[count] = 0;
673
674  return buf;
675}
[05ca0d8]676
[c1f1e1e]677gulong owl_dirty_window_on_signal(owl_window *w, gpointer sender, const gchar *detailed_signal)
678{
679  return owl_signal_connect_object(sender, detailed_signal, G_CALLBACK(owl_window_dirty), w, G_CONNECT_SWAPPED);
680}
681
[05ca0d8]682typedef struct { /*noproto*/
683  GObject  *sender;
684  gulong    signal_id;
685} SignalData;
686
687static void _closure_invalidated(gpointer data, GClosure *closure);
688
689/*
690 * GObject's g_signal_connect_object has a documented bug. This function is
691 * identical except it does not leak the signal handler.
692 */
693gulong owl_signal_connect_object(gpointer sender, const gchar *detailed_signal, GCallback c_handler, gpointer receiver, GConnectFlags connect_flags)
694{
695  g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (sender), 0);
696  g_return_val_if_fail (detailed_signal != NULL, 0);
697  g_return_val_if_fail (c_handler != NULL, 0);
698
699  if (receiver) {
700    SignalData *sdata;
701    GClosure *closure;
702    gulong signal_id;
703
704    g_return_val_if_fail (G_IS_OBJECT (receiver), 0);
705
706    closure = ((connect_flags & G_CONNECT_SWAPPED) ? g_cclosure_new_object_swap : g_cclosure_new_object) (c_handler, receiver);
707    signal_id = g_signal_connect_closure (sender, detailed_signal, closure, connect_flags & G_CONNECT_AFTER);
708
709    /* Register the missing hooks */
710    sdata = g_slice_new0(SignalData);
711    sdata->sender = sender;
712    sdata->signal_id = signal_id;
713
714    g_closure_add_invalidate_notifier(closure, sdata, _closure_invalidated);
715
716    return signal_id;
717  } else {
718    return g_signal_connect_data(sender, detailed_signal, c_handler, NULL, NULL, connect_flags);
719  }
720}
721
722/*
723 * There are three ways the signal could come to an end:
724 *
725 * 1. The user explicitly disconnects it with the returned signal_id.
726 *    - In that case, the disconnection unref's the closure, causing it
727 *      to first be invalidated. The handler's already disconnected, so
728 *      we have no work to do.
729 * 2. The sender gets destroyed.
730 *    - GObject will disconnect each signal which then goes into the above
731 *      case. Our handler does no work.
732 * 3. The receiver gets destroyed.
733 *    - The GClosure was created by g_cclosure_new_object_{,swap} which gets
734 *      invalidated when the receiver is destroyed. We then follow through case 1
735 *      again, but *this* time, the handler has not been disconnected. We then
736 *      clean up ourselves.
737 *
738 * We can't actually hook into this process earlier with weakrefs as GObject
739 * will, on object dispose, first disconnect signals, then invalidate closures,
740 * and notify weakrefs last.
741 */
742static void _closure_invalidated(gpointer data, GClosure *closure)
743{
744  SignalData *sdata = data;
745  if (g_signal_handler_is_connected(sdata->sender, sdata->signal_id)) {
746    g_signal_handler_disconnect(sdata->sender, sdata->signal_id);
747  }
748  g_slice_free(SignalData, sdata);
749}
750
Note: See TracBrowser for help on using the repository browser.