#define _GNU_SOURCE #include #include #include typedef struct { GString *title; GString *content; GString *last_change_date; GString *create_date; GString *cursor_position; GString *width; GString *height; GString *x; GString *y; gboolean in_content_tag; } TomboyNoteData; static TomboyNoteData * __tomboy_note_data_new () { TomboyNoteData *note = g_new0 (TomboyNoteData, 1); note->title = g_string_new (""); note->content = g_string_new (""); note->last_change_date = g_string_new (""); note->create_date = g_string_new (""); note->cursor_position = g_string_new (""); note->width = g_string_new (""); note->height = g_string_new (""); note->x = g_string_new (""); note->y = g_string_new (""); note->in_content_tag = FALSE; return note; } static void __tomboy_note_data_free (TomboyNoteData *note, gboolean free_segments) { g_string_free (note->title, free_segments); g_string_free (note->content, free_segments); g_string_free (note->last_change_date, free_segments); g_string_free (note->create_date, free_segments); g_string_free (note->cursor_position, free_segments); g_string_free (note->width, free_segments); g_string_free (note->height, free_segments); g_string_free (note->x, free_segments); g_string_free (note->y, free_segments); g_free(note); } /* * If you call this function be sure to free the TomboyNoteData * with free_segments=FALSE, since the hashtable will ref the strings. */ static void __tomboy_note_data_to_hashmap (TomboyNoteData *note, GHashTable *metadata) { g_hash_table_insert (metadata, g_strdup("Note.Title"), note->title->str); g_hash_table_insert (metadata, g_strdup("Note.Content"), note->content->str); g_hash_table_insert (metadata, g_strdup("Note.LastChangeDate"), note->last_change_date->str); g_hash_table_insert (metadata, g_strdup("Note.CreateDate"), note->create_date->str); g_hash_table_insert (metadata, g_strdup("Note.CursorPosition"), note->cursor_position->str); g_hash_table_insert (metadata, g_strdup("Window.Width"), note->width->str); g_hash_table_insert (metadata, g_strdup("Window.Height"), note->height->str); g_hash_table_insert (metadata, g_strdup("Window.X"), note->x->str); g_hash_table_insert (metadata, g_strdup("Window.Y"), note->y->str); } /* * Callback for handling start tags */ static void __start_tag_handler (GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer user_data, GError **error) { TomboyNoteData *note = user_data; const gchar *elt = g_markup_parse_context_get_element (context); // We need to append the contents of any subtags to the content field // for this we need to know when we are inside the note-content tag if (g_ascii_strcasecmp (elt, "note-content") == 0) { note->in_content_tag = TRUE; } } /* * Callback for handling end tags */ static void __end_tag_handler (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error) { TomboyNoteData *note = user_data; const gchar *elt = g_markup_parse_context_get_element (context); if (g_ascii_strcasecmp (elt, "note-content") == 0) { note->in_content_tag = FALSE; } } /* * Callback for the GMarkup parser when it encounters a text element * * We need to append text to the current metadata because GMarkup doesn't * guarantee to return text from entire elements. */ static void __text_handler (GMarkupParseContext *context, const gchar *text, gsize text_len, gpointer user_data, GError **error) { TomboyNoteData *note = user_data; const gchar *elt = g_markup_parse_context_get_element (context); if (note->in_content_tag || (g_ascii_strcasecmp (elt, "note-content") == 0)) { g_string_append_len (note->content, text, text_len); } else if (g_ascii_strcasecmp (elt, "title") == 0) { g_string_append_len (note->title, text, text_len); } else if (g_ascii_strcasecmp (elt, "last-change-date") == 0) { g_string_append_len (note->last_change_date, text, text_len); } else if (g_ascii_strcasecmp (elt, "create-date") == 0) { g_string_append_len (note->create_date, text, text_len); } else if (g_ascii_strcasecmp (elt, "cursor-position") == 0) { g_string_append_len (note->cursor_position, text, text_len); } else if (g_ascii_strcasecmp (elt, "width") == 0) { g_string_append_len (note->width, text, text_len); } else if (g_ascii_strcasecmp (elt, "height") == 0) { g_string_append_len (note->height, text, text_len); } else if (g_ascii_strcasecmp (elt, "x") == 0) { g_string_append_len (note->x, text, text_len); } else if (g_ascii_strcasecmp (elt, "y") == 0) { g_string_append_len (note->y, text, text_len); } } static GMarkupParser tomboy_parser = { __start_tag_handler, // Start element handler __end_tag_handler, // End element handler __text_handler, // Text element handler NULL, // Passthrough handler NULL // Error handler }; void tracker_extract_tomboy (gchar *filename, GHashTable *metadata) { FILE *note_file; TomboyNoteData *note; GMarkupParseContext *parser; gchar *first_line; gchar buf[1024]; int num_read; note = __tomboy_note_data_new (); parser = g_markup_parse_context_new (&tomboy_parser, 0, note, NULL); if ((note_file = fopen (filename, "r"))) { // We need to strip the first xml declaration line, GMarkup chokes on it first_line = NULL; getline (&first_line, &num_read, note_file); g_free (first_line); while (!feof (note_file)) { num_read = fread (buf, sizeof(gchar), 1024, note_file); if (num_read == 0) { break; } else if (num_read < 0) { // ERROR ! break; } if (!g_markup_parse_context_parse(parser, buf, num_read, NULL)) { g_markup_parse_context_free(parser); __tomboy_note_data_free (note, TRUE); fclose (note_file); return; } } } else { // Failed to open file, clean up. FIXME: Error handling higher up in the stack? g_markup_parse_context_free(parser); __tomboy_note_data_free (note, TRUE); return; } if (!g_markup_parse_context_end_parse (parser, NULL)) { g_markup_parse_context_free(parser); __tomboy_note_data_free (note, TRUE); fclose (note_file); return; } __tomboy_note_data_to_hashmap (note, metadata); g_markup_parse_context_free (parser); __tomboy_note_data_free (note, FALSE); // Free without freeing string segments, they are refed by the hashmap now fclose (note_file); } /* // Test program, provide full path to note file as first arg, debug to stderr, ouput to stdout. // Compile: gcc `pkg-config --cflags --libs glib-2.0` tracker-extract-tomboy.c -o tb-extract int main (int argc, char **argv) { FILE *note_file; TomboyNoteData *note; GMarkupParseContext *parser; GError *error; gchar *filename; gchar *first_line; gchar buf[1024]; int num_read; error = NULL; filename = argv[1]; g_printerr (" ** Extracting tomboy note: %s\n", filename); note = __tomboy_note_data_new (); parser = g_markup_parse_context_new (&tomboy_parser, 0, note, NULL); if ((note_file = fopen (filename, "r"))) { // We need to strip the first xml declaration line, GMarkup chokes on it first_line = NULL; getline (&first_line, &num_read, note_file); g_free (first_line); while (!feof (note_file)) { g_printerr (" ** Read block\n"); num_read = fread (buf, sizeof(gchar), 1024, note_file); if (num_read == 0) { break; } else if (num_read < 0) { // ERROR ! break; } if (!g_markup_parse_context_parse(parser, buf, num_read, &error)) { g_printerr (" ** Error parsing note: %s", error->message); g_markup_parse_context_free(parser); __tomboy_note_data_free (note, TRUE); fclose (note_file); } } g_printerr (" ** Note read\n"); } else { // Failed to open file, clean up. FIXME: Error handling higher up in the stack? g_printerr (" ** Failed to open file: %s\n", filename); g_markup_parse_context_free(parser); __tomboy_note_data_free (note, TRUE); return; } if (!g_markup_parse_context_end_parse (parser, &error)) { g_printerr (" ** Failed to close parser: %s\n", error->message); g_markup_parse_context_free(parser); __tomboy_note_data_free (note, TRUE); fclose (note_file); return; } g_print ("Note.Title=%s\n", note->title->str); g_print ("Note.LastChangeDate=%s\n", note->last_change_date->str); g_print ("Note.CreateDate=%s\n", note->create_date->str); g_print ("Window.Width=%s\n", note->width->str); g_print ("Window.Height=%s\n", note->height->str); g_print ("Window.X=%s\n", note->x->str); g_print ("Window.Y=%s\n", note->y->str); g_print ("Note.Content=%s\n", note->content->str); g_markup_parse_context_free (parser); __tomboy_note_data_free (note, TRUE); fclose (note_file); return 0; } */