From: Dana Jansens Date: Tue, 21 Sep 2010 00:46:37 +0000 (-0400) Subject: Linkbase adds all the .desktops in the system, and updates them as they change. X-Git-Url: http://git.openbox.org/?a=commitdiff_plain;h=a35f6d502434f0db608288fbb2f35ac445b78532;p=dana%2Fopenbox.git Linkbase adds all the .desktops in the system, and updates them as they change. Fixes to .desktop parsing. Add language/locale support for .desktop files. Fixes to inotify watching, change what information is passed through the notify handler. Add linkbase.h to the public obt headers. --- diff --git a/Makefile.am b/Makefile.am index 11277b2a..e63184e0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -451,6 +451,7 @@ rrpubinclude_HEADERS = \ obtpubinclude_HEADERS = \ obt/link.h \ + obt/linkbase.h \ obt/display.h \ obt/keyboard.h \ obt/xml.h \ diff --git a/obt/ddparse.c b/obt/ddparse.c index 9c4424f1..fc988499 100644 --- a/obt/ddparse.c +++ b/obt/ddparse.c @@ -32,6 +32,7 @@ typedef struct _ObtDDParse ObtDDParse; Return TRUE if it is added to the hash table, and FALSE if not. */ typedef gboolean (*ObtDDParseValueFunc)(gchar *key, const gchar *val, + ObtDDParseLangMatch match, ObtDDParse *parse, gboolean *error); @@ -46,6 +47,9 @@ enum { struct _ObtDDParse { const gchar *filename; + const gchar *language; + const gchar *country; + const gchar *modifier; gulong lineno; gulong flags; ObtDDParseGroup *group; @@ -180,7 +184,7 @@ static gchar* parse_value_string(const gchar *in, } else if (*i == '\\') backslash = TRUE; - else if ((guchar)*i >= 127 || (guchar)*i < 32) { + else if ((!locale && (guchar)*i >= 127) || (guchar)*i < 32) { /* avoid ascii control characters */ parse_error("Found control character in string", parse, error); break; @@ -396,7 +400,7 @@ static gboolean parse_file_line(FILE *f, gchar **buf, } else { /* read more */ - size += BUFMUL; + *size += BUFMUL; *buf = g_renew(char, *buf, *size); } } @@ -446,7 +450,7 @@ static void parse_group(const gchar *buf, gulong len, g->seen = TRUE; parse->group = g; - g_print("Found group %s\n", g->name); + /* g_print("Found group %s\n", g->name); */ } } @@ -454,7 +458,10 @@ static void parse_key_value(const gchar *buf, gulong len, ObtDDParse *parse, gboolean *error) { gulong i, keyend, valstart, eq; + gulong langstart, langend, countrystart, countryend, modstart, modend; char *key; + ObtDDParseValue *val; + ObtDDParseLangMatch match; /* find the end of the key */ for (i = 0; i < len; ++i) @@ -471,15 +478,74 @@ static void parse_key_value(const gchar *buf, gulong len, parse_error("Empty key", parse, error); return; } + + /* is there a language specifier? */ + langstart = langend = countrystart = countryend = modstart = modend = 0; + if ((guchar)buf[i] == '[') { + langstart = i+1; + for (i = langstart; i < len; ++i) + if ((guchar)buf[i] == '.' || (guchar)buf[i] == '_' || + (guchar)buf[i] == '@' || (guchar)buf[i] == ']') + { + langend = i-1; + break; + } + else if (!(((guchar)buf[i] >= 'A' && (guchar)buf[i] <= 'Z') || + ((guchar)buf[i] >= 'a' && (guchar)buf[i] <= 'z'))) { + parse_error("Invalid character in language", parse, error); + return; + } + if ((guchar)buf[i] == '_') { + countrystart = i+1; + for (i = i+1; i < len; ++i) + if ((guchar)buf[i] == '.' || + (guchar)buf[i] == '@' || (guchar)buf[i] == ']') + { + langend = i-1; + break; + } + else if (!(((guchar)buf[i] >= 'A' && (guchar)buf[i] <= 'Z') || + ((guchar)buf[i] >= 'a' && (guchar)buf[i] <= 'z'))) { + parse_error("Invalid character in country", parse, error); + return; + } + } + if ((guchar)buf[i] == '.') { + for (i = i+1; i < len; ++i) + if ((guchar)buf[i] == '@' || (guchar)buf[i] == ']') + break; + else if (!(((guchar)buf[i] >= 'A' && (guchar)buf[i] <= 'Z') || + ((guchar)buf[i] >= 'a' && (guchar)buf[i] <= 'z'))) { + parse_error("Invalid character in encoding", parse, error); + return; + } + } + if ((guchar)buf[i] == '@') { + modstart = i+1; + for (i = i+1; i < len; ++i) + if ((guchar)buf[i] == ']') { + modend = i-1; + break; + } + else if (!(((guchar)buf[i] >= 'A' && (guchar)buf[i] <= 'Z') || + ((guchar)buf[i] >= 'a' && (guchar)buf[i] <= 'z'))) { + parse_error("Invalid character in locale modifier", + parse, error); + return; + } + } + ++i; + } + /* find the = character */ - for (i = keyend; i < len; ++i) { + for (; i < len; ++i) { if (buf[i] == '=') { eq = i; break; } else if (buf[i] != ' ') { parse_error("Invalid character in key name", parse, error); - return ; + return; } } if (i == len) { @@ -497,16 +563,38 @@ static void parse_key_value(const gchar *buf, gulong len, return; } + if (langend < langstart) + match = OBT_DDPARSE_MATCH_FAIL; + else + match = OBT_DDPARSE_MATCH_NONE; + if (parse->language && langend >= langstart && + strncmp(parse->language, buf+langstart, langend-langstart+1) == 0) + { + match = OBT_DDPARSE_MATCH_LANG; + if (parse->country && countryend >= countrystart && + strncmp(parse->country, buf+countrystart, + countryend-countrystart+1) == 0) + match = OBT_DDPARSE_MATCH_LANG_COUNTRY; + + if (parse->modifier && modend >= modstart && + strncmp(parse->modifier, buf+modstart, + modend-modstart+1) == 0) + match += 1; /* its one up for LANG and for LANG_COUNTY */ + } + key = g_strndup(buf, keyend); - if (g_hash_table_lookup(parse->group->key_hash, key)) { - parse_error("Duplicate key found", parse, error); - g_free(key); - return; + if ((val = g_hash_table_lookup(parse->group->key_hash, key))) { + if (val->language_match >= match) { + /* found a better match already */ + g_free(key); + return; + } } - g_print("Found key/value %s=%s.\n", key, buf+valstart); + /* g_print("Found key/value %s=%s\n", key, buf+valstart); */ if (parse->group->value_func) - if (!parse->group->value_func(key, buf+valstart, parse, error)) { - parse_error("Unknown key", parse, error); + if (!parse->group->value_func(key, buf+valstart, match, parse, error)) + { + /*parse_error("Unknown key", parse, error);*/ g_free(key); } } @@ -537,10 +625,13 @@ static gboolean parse_file(FILE *f, ObtDDParse *parse) } static gboolean parse_desktop_entry_value(gchar *key, const gchar *val, + ObtDDParseLangMatch match, ObtDDParse *parse, gboolean *error) { ObtDDParseValue v, *pv; + v.language_match = match; + switch (key[0]) { case 'C': switch (key[1]) { @@ -634,6 +725,13 @@ static gboolean parse_desktop_entry_value(gchar *key, const gchar *val, return FALSE; } + if (v.language_match && !(v.type == OBT_DDPARSE_LOCALESTRING || + v.type == OBT_DDPARSE_LOCALESTRINGS)) + { + parse_error("Invalid localization on key", parse, error); + return FALSE; + } + /* parse the value */ switch (v.type) { case OBT_DDPARSE_EXEC: { @@ -747,7 +845,10 @@ static gboolean parse_desktop_entry_value(gchar *key, const gchar *val, return TRUE; } -GHashTable* obt_ddparse_file(const gchar *filename) +GHashTable* obt_ddparse_file(const gchar *filename, + const gchar *language, + const gchar *country, + const gchar *modifier) { ObtDDParse parse; ObtDDParseGroup *desktop_entry; @@ -760,6 +861,9 @@ GHashTable* obt_ddparse_file(const gchar *filename) } parse.filename = filename; + parse.language = language; + parse.country = country; + parse.modifier = modifier; parse.lineno = 0; parse.group = NULL; parse.group_hash = g_hash_table_new_full(g_str_hash, @@ -818,7 +922,7 @@ gchar* obt_ddparse_file_to_id(const gchar *filename) { gint len; const gchar *in; - gchar *out; + gchar *out, *out_start; gboolean sep; if (!g_utf8_validate(filename, -1, NULL)) { @@ -829,9 +933,9 @@ gchar* obt_ddparse_file_to_id(const gchar *filename) len = strlen(filename) - 8; /* 8 = strlen(".desktop") */ g_assert(strcmp(filename+len, ".desktop") == 0); - out = g_new(char, len+1); + out_start = out = g_new(char, len+1); sep = TRUE; - for (in = filename; *in; ++in) { + for (in = filename; in < filename + len; ) { gchar *next; if (*in == '/') { @@ -841,6 +945,7 @@ gchar* obt_ddparse_file_to_id(const gchar *filename) ++out; } sep = TRUE; + ++in; } else { /* everything else is copied as is */ @@ -850,7 +955,9 @@ gchar* obt_ddparse_file_to_id(const gchar *filename) ++out; ++in; } + sep = FALSE; } } - return out; + *out = '\0'; + return out_start; } diff --git a/obt/ddparse.h b/obt/ddparse.h index 050aaa96..ce887f9d 100644 --- a/obt/ddparse.h +++ b/obt/ddparse.h @@ -33,6 +33,15 @@ typedef enum { OBT_DDPARSE_NUM_VALUE_TYPES } ObtDDParseValueType; +typedef enum { + OBT_DDPARSE_MATCH_NONE = 0, + OBT_DDPARSE_MATCH_FAIL, + OBT_DDPARSE_MATCH_LANG, + OBT_DDPARSE_MATCH_LANG_MODIFIER, + OBT_DDPARSE_MATCH_LANG_COUNTRY, + OBT_DDPARSE_MATCH_LANG_COUNTRY_MODIFIER +} ObtDDParseLangMatch; + typedef struct _ObtDDParseValue { ObtDDParseValueType type; union _ObtDDParseValueValue { @@ -46,13 +55,17 @@ typedef struct _ObtDDParseValue { guint enumerable; guint environments; /*!< A mask of flags from ObtLinkEnvMask */ } value; + ObtDDParseLangMatch language_match; } ObtDDParseValue; /*! Parse a .desktop file. @param filename The full path to the .desktop file to be read. @return Returns a hash table where the keys are groups, and the values are ObtDDParseGroups */ -GHashTable* obt_ddparse_file(const gchar *filename); +GHashTable* obt_ddparse_file(const gchar *filename, + const gchar *language, + const gchar *country, + const gchar *modifier); /*! Get the keys in a group from a .desktop file. The group comes from the hash table returned by obt_ddparse_file. diff --git a/obt/link.c b/obt/link.c index eb24a113..c54b7826 100644 --- a/obt/link.c +++ b/obt/link.c @@ -24,6 +24,8 @@ struct _ObtLink { guint ref; + gchar *path; /*!< The path to the file where the link came from */ + ObtLinkType type; gchar *name; /*!< Specific name for the object (eg Firefox) */ gboolean display; /*ref = 1; + link->path = g_strdup(path); link->display = TRUE; v = g_hash_table_lookup(keys, "Type"); @@ -240,3 +242,13 @@ const GQuark* obt_link_app_categories(ObtLink *e, gulong *n) *n = e->d.app.n_categories; return e->d.app.categories; } + +const gchar *obt_link_source_file(ObtLink *e) +{ + return e->path; +} + +gchar* obt_link_id_from_ddfile(const gchar *filename) +{ + return obt_ddparse_file_to_id(filename); +} diff --git a/obt/link.h b/obt/link.h index dff11f3c..749514de 100644 --- a/obt/link.h +++ b/obt/link.h @@ -63,17 +63,29 @@ typedef enum { typedef struct _ObtLink ObtLink; /*! Parse a .desktop (dd) file. - @param basepath The base directory in which to read the file. - @param filename The full path to the .desktop file _relative to_ basepath. - It must be in basepath or a subdirectory of it. + @param path The full path to the .desktop file. @param o An ObtPaths structure, which contains the executable paths. */ -ObtLink* obt_link_from_ddfile(const gchar *basepath, const gchar *filename, - struct _ObtPaths *p); +ObtLink* obt_link_from_ddfile(const gchar *path, + struct _ObtPaths *p, + const gchar *language, + const gchar *country, + const gchar *modifier); + +/*! Determine the identifier for a .desktop (dd) file. + @param filename The full path to the .desktop file _relative to_ some + basepath. For instance, if the desktop file is + /usr/share/applications/foo/bar.desktop, and the basepath is + /usr/share/applications, then the filename would be 'foo/bar.desktop'. + The filename must end with ".desktop" and be encoded in utf8. +*/ +gchar* obt_link_id_from_ddfile(const gchar *filename); void obt_link_ref(ObtLink *e); void obt_link_unref(ObtLink *e); +const gchar *obt_link_source_file(ObtLink *e); + /*! Returns TRUE if the file exists but says it should be ignored, with the Hidden flag. No other functions can be used for the ObtLink in this case. */ diff --git a/obt/linkbase.c b/obt/linkbase.c index 1039b348..9d913da1 100644 --- a/obt/linkbase.c +++ b/obt/linkbase.c @@ -21,46 +21,233 @@ #include "obt/paths.h" #include "obt/watch.h" +#ifdef HAVE_STRING_H +# include +#endif + +typedef struct _ObtLinkBaseEntry ObtLinkBaseEntry; + +struct _ObtLinkBaseEntry { + /*! Links come from a set of paths. Links found in earlier paths get lower + priority values (higher precedence). This is the index in that set of + paths of the base directory under which the link was found. */ + gint priority; + ObtLink *link; +}; + struct _ObtLinkBase { gint ref; + const gchar *language; + const gchar *country; + const gchar *modifier; + + ObtPaths *paths; ObtWatch *watch; + /*! This holds a GSList of ObtLinkBaseEntrys sorted by priority in + increasing order (by precedence in decreasing order). */ GHashTable *base; + /*! This holds the paths in which we look for links, and the data is an + integer that is the priority of that directory. */ + GHashTable *path_to_priority; }; -static void func(ObtWatch *w, const gchar *subpath, ObtWatchNotifyType type, - gpointer data) +static void base_entry_free(ObtLinkBaseEntry *e) +{ + obt_link_unref(e->link); + g_slice_free(ObtLinkBaseEntry, e); +} + +static void base_entry_list_free(GSList *list) +{ + GSList *it; + for (it = list; it; it = g_slist_next(it)) + base_entry_free(it->data); + g_slist_free(list); +} + +static GSList* find_base_entry_path(GSList *list, const gchar *full_path) { + GSList *it; + for (it = list; it; it = g_slist_next(it)) { + ObtLinkBaseEntry *e = it->data; + if (strcmp(obt_link_source_file(e->link), full_path) == 0) + break; + } + return it; } -ObtLinkBase* obt_linkbase_new(ObtPaths *paths) +/*! Finds the first entry in the list with a priority number >= @priority. */ +static GSList* find_base_entry_priority(GSList *list, gint priority) { - ObtLinkBase *b; GSList *it; + for (it = list; it; it = g_slist_next(it)) { + ObtLinkBaseEntry *e = it->data; + if (e->priority >= priority) + break; + } + return it; +} + +static void update(ObtWatch *w, const gchar *base_path, + const gchar *sub_path, + const gchar *full_path, + ObtWatchNotifyType type, + gpointer data) +{ + ObtLinkBase *self = data; + gchar *id; + GSList *list, *it; + gint *priority; + gboolean add = FALSE; + + if (!g_str_has_suffix(sub_path, ".desktop")) + return; /* ignore non-.desktop files */ + + id = obt_link_id_from_ddfile(sub_path); + list = g_hash_table_lookup(self->base, id); + + switch (type) { + case OBT_WATCH_SELF_REMOVED: + break; + case OBT_WATCH_REMOVED: + it = find_base_entry_path(list, full_path); + list = g_slist_delete_link(list, it); + base_entry_free(it->data); + + /* this will free 'id' */ + g_hash_table_insert(self->base, id, list); + id = NULL; + break; + case OBT_WATCH_MODIFIED: + it = find_base_entry_path(list, full_path); + list = g_slist_delete_link(list, it); + base_entry_free(it->data); + add = TRUE; /* this will put the modified list into the hash table */ + break; + case OBT_WATCH_ADDED: + priority = g_hash_table_lookup(self->path_to_priority, base_path); + add = TRUE; + + /* find the first position in the list with a higher priority value */ + if ((it = find_base_entry_priority(list, *priority))) { + const ObtLinkBaseEntry *e = it->data; + if (e->priority == *priority) { + /* already exists */ + add = FALSE; + } + } + break; + } + + if (add) { + ObtLinkBaseEntry *e = g_slice_new(ObtLinkBaseEntry); + e->priority = *priority; + e->link = obt_link_from_ddfile(full_path, self->paths, + self->language, self->country, + self->modifier); + list = g_slist_insert_before(list, it, e); + + /* this will free 'id' */ + g_hash_table_insert(self->base, id, list); + id = NULL; + } + + g_free(id); +} + +ObtLinkBase* obt_linkbase_new(ObtPaths *paths, const gchar *locale) +{ + ObtLinkBase *self; + GSList *it; + gint priority; + gint i; - b = g_slice_new(ObtLinkBase); - b->watch = obt_watch_new(); - b->base = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, - (GDestroyNotify)obt_link_unref); + self = g_slice_new0(ObtLinkBase); + self->watch = obt_watch_new(); + self->base = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + (GDestroyNotify)base_entry_list_free); + self->path_to_priority = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, g_free); + self->paths = paths; + obt_paths_ref(paths); + + for (i = 0; ; ++i) + if (!locale[i] || locale[i] == '_' || locale[i] == '.' || + locale[i] == '@') + { + self->language = g_strndup(locale, i); + break; + } + else if (((guchar)locale[i] < 'A' || (guchar)locale[i] > 'Z') && + ((guchar)locale[i] < 'a' || (guchar)locale[i] > 'z')) + break; + if (self->language && locale[i] == '_') { + locale += i+1; + for (i = 0; ; ++i) + if (!locale[i] || locale[i] == '.' || locale[i] == '@') { + self->country = g_strndup(locale, i); + break; + } + else if (((guchar)locale[i] < 'A' || (guchar)locale[i] > 'Z') && + ((guchar)locale[i] < 'a' || (guchar)locale[i] > 'z')) + break; + } + if (self->country && locale[i] == '.') + for (; ; ++i) + if (!locale[i] || locale[i] == '@') + break; + else if (((guchar)locale[i] < 'A' || (guchar)locale[i] > 'Z') && + ((guchar)locale[i] < 'a' || (guchar)locale[i] > 'z')) + break; + if (self->country && locale[i] == '@') { + locale += i+1; + for (i = 0; ; ++i) + if (!locale[i]) { + self->modifier = g_strndup(locale, i); + break; + } + else if (((guchar)locale[i] < 'A' || (guchar)locale[i] > 'Z') && + ((guchar)locale[i] < 'a' || (guchar)locale[i] > 'z')) + break; + } + priority = 0; for (it = obt_paths_data_dirs(paths); it; it = g_slist_next(it)) { - gchar *p; - p = g_strconcat(it->data, "/applications", NULL); - obt_watch_add(b->watch, p, FALSE, func, b); + if (!g_hash_table_lookup(self->path_to_priority, it->data)) { + gchar *base_path; + gint *pri; + + base_path = g_build_filename(it->data, "applications", NULL); + + /* add to the hash table before adding the watch. the new watch + will be calling our update handler, immediately with any + files present in the directory */ + pri = g_new(gint, 1); + *pri = priority; + g_hash_table_insert(self->path_to_priority, + g_strdup(base_path), pri); + + obt_watch_add(self->watch, base_path, FALSE, update, self); + + ++priority; + } } - return b; + return self; } -void obt_linkbase_ref(ObtLinkBase *lb) +void obt_linkbase_ref(ObtLinkBase *self) { - ++lb->ref; + ++self->ref; } -void obt_linkbase_unref(ObtLinkBase *lb) +void obt_linkbase_unref(ObtLinkBase *self) { - if (--lb->ref < 1) { - obt_watch_unref(lb->watch); - g_hash_table_unref(lb->base); - g_slice_free(ObtLinkBase, lb); + if (--self->ref < 1) { + obt_watch_unref(self->watch); + g_hash_table_unref(self->path_to_priority); + g_hash_table_unref(self->base); + obt_paths_unref(self->paths); + g_slice_free(ObtLinkBase, self); } } diff --git a/obt/linkbase.h b/obt/linkbase.h index 76bf4433..57602788 100644 --- a/obt/linkbase.h +++ b/obt/linkbase.h @@ -27,8 +27,11 @@ G_BEGIN_DECLS typedef struct _ObtLinkBase ObtLinkBase; -/*! Create a new database of ObtLinks. */ -ObtLinkBase* obt_linkbase_new(struct _ObtPaths *paths); +/*! Create a new database of ObtLinks. + @param paths An ObtPaths structure. + @param locale The value of LC_MESSAGES. +*/ +ObtLinkBase* obt_linkbase_new(struct _ObtPaths *paths, const gchar *locale); void obt_linkbase_ref(ObtLinkBase *lb); void obt_linkbase_unref(ObtLinkBase *lb); diff --git a/obt/watch.c b/obt/watch.c index 63a2ccef..6e1971ac 100644 --- a/obt/watch.c +++ b/obt/watch.c @@ -28,7 +28,8 @@ typedef struct _ObtWatchTarget ObtWatchTarget; /*! Callback function for the system-specific GSource to alert us to changes. */ -typedef void (*ObtWatchNotifyFunc)(const gchar *path, gpointer target, +typedef void (*ObtWatchNotifyFunc)(const gchar *sub_path, + const gchar *full_path, gpointer target, ObtWatchNotifyType type); @@ -71,8 +72,8 @@ struct _ObtWatchTarget { }; static void target_free(ObtWatchTarget *t); -static void target_notify(const gchar *const path, gpointer target, - ObtWatchNotifyType type); +static void target_notify(const gchar *sub_path, const gchar *full_path, + gpointer target, ObtWatchNotifyType type); ObtWatch* obt_watch_new() { @@ -150,13 +151,13 @@ void obt_watch_remove(ObtWatch *w, const gchar *path) g_hash_table_remove(w->targets_by_path, path); } -static void target_notify(const gchar *const path, gpointer target, - ObtWatchNotifyType type) +static void target_notify(const gchar *sub_path, const gchar *full_path, + gpointer target, ObtWatchNotifyType type) { ObtWatchTarget *t = target; if (type == OBT_WATCH_SELF_REMOVED) { /* this also calls target_free */ g_hash_table_remove(t->w->targets_by_path, t->base_path); } - t->func(t->w, path, type, t->data); + t->func(t->w, t->base_path, sub_path, full_path, type, t->data); } diff --git a/obt/watch.h b/obt/watch.h index d46e4f82..4df994da 100644 --- a/obt/watch.h +++ b/obt/watch.h @@ -26,7 +26,14 @@ G_BEGIN_DECLS typedef struct _ObtWatch ObtWatch; typedef enum _ObtWatchNotifyType ObtWatchNotifyType; -typedef void (*ObtWatchFunc)(ObtWatch *w, const gchar *subpath, +/*! Notification function for changes in a watch file/directory. + @param base_path is the path to the watch target (file or directory). + @param sub_path is a path relative to the watched directory. If the + notification is about the watch target itself, the subpath will be + an empty string. +*/ +typedef void (*ObtWatchFunc)(ObtWatch *w, const gchar *base_path, + const gchar *sub_path, const gchar *full_path, ObtWatchNotifyType type, gpointer data); enum _ObtWatchNotifyType { diff --git a/obt/watch_inotify.c b/obt/watch_inotify.c index 3a7d3da4..f9a5649a 100644 --- a/obt/watch_inotify.c +++ b/obt/watch_inotify.c @@ -39,7 +39,8 @@ typedef struct _InoTarget InoTarget; /*! Callback function in the watch general system. Matches definition in watch.c */ -typedef void (*ObtWatchNotifyFunc)(const gchar *path, gpointer target, +typedef void (*ObtWatchNotifyFunc)(const gchar *sub_path, + const gchar *full_path, gpointer target, ObtWatchNotifyType type); struct _InoSource { @@ -54,6 +55,8 @@ struct _InoSource { struct _InoTarget { gint key; gchar *path; + guint base_len; /* the length of the prefix of path which is the + target's path */ gpointer watch_target; gboolean is_dir; gboolean watch_hidden; @@ -68,6 +71,8 @@ static gint add_target(GSource *source, InoTarget *parent, gpointer target); static void remove_target(GSource *source, InoTarget *target); static void target_free(InoTarget *target); +static void notify_target(GSource *source, InoTarget *ino_target, + const gchar *path, ObtWatchNotifyType type); static GSourceFuncs source_funcs = { source_prepare, @@ -285,7 +290,7 @@ static gboolean source_read(GSource *source, GSourceFunc cb, gpointer data) if (cb) cb(data); /* call the WatchNotify callback */ - ino_source->notify(full_path, t->watch_target, type); + notify_target(source, t, full_path, type); } g_free(full_path); @@ -307,8 +312,8 @@ static void source_finalize(GSource *source) } static gint add_target(GSource *source, InoTarget *parent, - const gchar *path, gboolean watch_hidden, - gpointer target) + const gchar *path, + gboolean watch_hidden, gpointer target) { InoSource *ino_source; InoTarget *ino_target; @@ -339,6 +344,7 @@ static gint add_target(GSource *source, InoTarget *parent, ino_target = g_slice_new(InoTarget); ino_target->key = key; ino_target->path = g_strdup(path); + ino_target->base_len = (parent ? parent->base_len : strlen(path)); ino_target->is_dir = is_dir; ino_target->watch_hidden = watch_hidden; ino_target->watch_target = target; @@ -362,12 +368,12 @@ static gint add_target(GSource *source, InoTarget *parent, subpath = g_build_filename(path, name, NULL); if (g_file_test(subpath, G_FILE_TEST_IS_DIR)) - add_target(source, ino_target, subpath, watch_hidden, - target); + add_target(source, ino_target, subpath, + watch_hidden, target); else /* notify for each file in the directory on startup */ - ino_source->notify(subpath, ino_target->watch_target, - OBT_WATCH_ADDED); + notify_target(source, ino_target, subpath, + OBT_WATCH_ADDED); g_free(subpath); } } @@ -393,4 +399,14 @@ static void target_free(InoTarget *target) g_slice_free(InoTarget, target); } +static void notify_target(GSource *source, InoTarget *ino_target, + const gchar *path, ObtWatchNotifyType type) +{ + InoSource *ino_source = (InoSource*)source; + ino_source->notify(path + ino_target->base_len, + path, + ino_target->watch_target, + type); +} + #endif