From 2af6447238b564dfd145416fa21a7e0f133daab3 Mon Sep 17 00:00:00 2001 From: Soeren Sandmann Date: Wed, 23 Mar 2005 05:05:57 +0000 Subject: [PATCH] Primitive loading and saving. Wed Mar 23 00:04:07 2005 Soeren Sandmann Primitive loading and saving. * sysprof.c (on_open_clicked): Hook up loading. * sfile.c: Add a copy of g_file_replace() from glib CVS HEAD. * sfile.c (add_string): Escape and quote the string * sfile.c (sfile_load): Initialize current_instruction and instructions_by_location * sfile.c (post_process_instructions_recurse): Handle NULL pointers properly. * sfile.c (handle_begin_element, handle_end_element, handle_text): Move error handling here from state_transition_begin/text/end. * sfile.c (handle_text): Discard whitespace-only strings * sfile.c (sfile_get_pointer, sfile_get_integer, sfile_get_string): expect both begin, value, and end transitions. * sfile.c (hook_up_pointers): Only treat instructions as pointer values when they are. Handle NULL targets properly. * sfile.c (get_number): Fix a few read-freed-data bugs * profile.c (profile_load): Call sfile_end_get() for the profile; build the nodes_by_objects hash table. Build the call tree. * profile.c (create_format): Don't store next pointer, but do store total, self and toplevel. * profile.c (make_hash_table): New function to build nodes_by_object hashtable from loaded data --- ChangeLog | 38 +++++ profile.c | 50 +++++- sfile.c | 576 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-------- sysprof.c | 42 ++++- 4 files changed, 631 insertions(+), 75 deletions(-) diff --git a/ChangeLog b/ChangeLog index f1be485..60b857a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,41 @@ +Wed Mar 23 00:04:07 2005 Soeren Sandmann + + Primitive loading and saving. + + * sysprof.c (on_open_clicked): Hook up loading. + + * sfile.c: Add a copy of g_file_replace() from glib CVS HEAD. + + * sfile.c (add_string): Escape and quote the string + + * sfile.c (sfile_load): Initialize current_instruction and + instructions_by_location + + * sfile.c (post_process_instructions_recurse): Handle NULL + pointers properly. + + * sfile.c (handle_begin_element, handle_end_element, handle_text): + Move error handling here from state_transition_begin/text/end. + + * sfile.c (handle_text): Discard whitespace-only strings + + * sfile.c (sfile_get_pointer, sfile_get_integer, + sfile_get_string): expect both begin, value, and end transitions. + + * sfile.c (hook_up_pointers): Only treat instructions as pointer + values when they are. Handle NULL targets properly. + + * sfile.c (get_number): Fix a few read-freed-data bugs + + * profile.c (profile_load): Call sfile_end_get() for the profile; + build the nodes_by_objects hash table. Build the call tree. + + * profile.c (create_format): Don't store next pointer, but do + store total, self and toplevel. + + * profile.c (make_hash_table): New function to build + nodes_by_object hashtable from loaded data + Sat Mar 12 11:05:19 2005 Soeren Sandmann * sysprof-module.c: Fix small bug in add_timeout() diff --git a/profile.c b/profile.c index 315319d..6d5584b 100644 --- a/profile.c +++ b/profile.c @@ -22,7 +22,7 @@ update() static guint direct_hash_no_null (gconstpointer v) { - g_assert (v); + g_assert (v != NULL); return GPOINTER_TO_UINT (v); } @@ -78,7 +78,9 @@ create_format (void) sformat_new_pointer ("siblings", &node_type), sformat_new_pointer ("children", &node_type), sformat_new_pointer ("parent", &node_type), - sformat_new_pointer ("next", &node_type), + sformat_new_integer ("total"), + sformat_new_integer ("self"), + sformat_new_integer ("toplevel"), NULL)), NULL)); } @@ -109,7 +111,9 @@ serialize_call_tree (Node *node, SFileOutput *output) sfile_add_pointer (output, "siblings", node->siblings); sfile_add_pointer (output, "children", node->children); sfile_add_pointer (output, "parent", node->parent); - sfile_add_pointer (output, "next", node->next); + sfile_add_integer (output, "total", node->total); + sfile_add_integer (output, "self", node->self); + sfile_add_integer (output, "toplevel", node->toplevel); sfile_end_add (output, "node", node); serialize_call_tree (node->siblings, output); @@ -148,6 +152,25 @@ profile_save (Profile *profile, return result; } +static void +make_hash_table (Node *node, GHashTable *table) +{ + if (!node) + return; + + g_assert (node->object); + + node->next = g_hash_table_lookup (table, node->object); + g_hash_table_insert (table, node->object, node); + + g_print ("added %s\n", node->object->name); + + g_assert (node->siblings != 0x11); + + make_hash_table (node->siblings, table); + make_hash_table (node->children, table); +} + Profile * profile_load (const char *filename, GError **err) { @@ -164,6 +187,9 @@ profile_load (const char *filename, GError **err) profile = g_new (Profile, 1); + profile->nodes_by_object = + g_hash_table_new (direct_hash_no_null, g_direct_equal); + sfile_begin_get_record (input, "profile"); sfile_get_integer (input, "size", &profile->size); @@ -178,11 +204,12 @@ profile_load (const char *filename, GError **err) sfile_get_string (input, "name", &obj->name); sfile_get_integer (input, "total", &obj->total); sfile_get_integer (input, "self", &obj->self); - + sfile_end_get (input, "object", obj); } sfile_end_get (input, "objects", NULL); + profile->call_tree = NULL; n = sfile_begin_get_list (input, "nodes"); for (i = 0; i < n; ++i) { @@ -194,17 +221,28 @@ profile_load (const char *filename, GError **err) sfile_get_pointer (input, "siblings", (gpointer *)&node->siblings); sfile_get_pointer (input, "children", (gpointer *)&node->children); sfile_get_pointer (input, "parent", (gpointer *)&node->parent); - sfile_get_pointer (input, "next", (gpointer *)&node->next); + sfile_get_integer (input, "total", &node->total); + sfile_get_integer (input, "self", &node->self); + sfile_get_integer (input, "toplevel", &node->toplevel); sfile_end_get (input, "node", node); - if (!node->parent) + if (!profile->call_tree) profile->call_tree = node; + + g_assert (node->siblings != 0x11); } sfile_end_get (input, "nodes", NULL); + sfile_end_get (input, "profile", NULL); sformat_free (format); + /* FIXME: why don't we just store the root node? */ + while (profile->call_tree && profile->call_tree->parent) + profile->call_tree = profile->call_tree->parent; + + make_hash_table (profile->call_tree, profile->nodes_by_object); + return profile; } diff --git a/sfile.c b/sfile.c index f75b400..39e6090 100644 --- a/sfile.c +++ b/sfile.c @@ -210,6 +210,7 @@ fragment_queue (Fragment *fragment1, va_list args) * floating point values. How do we store those portably * without losing precision? Gnumeric may know. * enums, stored as strings + * booleans */ gpointer sformat_new_union (const char *name, @@ -400,11 +401,10 @@ static const State * state_transition_check (const State *state, const char *element, TransitionKind kind, - SType *type, - GError **err) + SType *type) { GList *list; - + for (list = state->transitions->head; list; list = list->next) { Transition *transition = list->data; @@ -417,27 +417,23 @@ state_transition_check (const State *state, } } - set_unknown_element_error (err, "<%s> or unexpected here", element, element); - return NULL; } static const State * -state_transition_begin (const State *state, const char *element, - SType *type, GError **err) +state_transition_begin (const State *state, const char *element, SType *type) { - return state_transition_check (state, element, BEGIN, type, err); + return state_transition_check (state, element, BEGIN, type); } static const State * -state_transition_end (const State *state, const char *element, - SType *type, GError **err) +state_transition_end (const State *state, const char *element, SType *type) { - return state_transition_check (state, element, END, type, err); + return state_transition_check (state, element, END, type); } static const State * -state_transition_text (const State *state, SType *type, GError **err) +state_transition_text (const State *state, SType *type) { GList *list; @@ -454,9 +450,12 @@ state_transition_text (const State *state, SType *type, GError **err) */ return transition->to; } +#if 0 + else + g_print ("transition: %d (%s)\n", transition->kind, transition->element); +#endif } - set_invalid_content_error (err, "Unexpected text data"); return NULL; } @@ -518,18 +517,19 @@ get_number (const char *text, int *number) char *end; int result; char *stripped; - + gboolean retval; + stripped = g_strstrip (g_strdup (text)); result = strtol (stripped, &end, 10); - g_free (stripped); - - if (*end != '\0') - return FALSE; + + retval = (*end == '\0'); + + if (retval && number) + *number = result; - if (number) - *number = result; + g_free (stripped); - return TRUE; + return retval; } struct SFileInput @@ -582,10 +582,15 @@ sfile_get_pointer (SFileInput *file, const char *name, gpointer *location) { - Instruction *instruction = file->current_instruction++; + Instruction *instruction; + + instruction = file->current_instruction++; g_return_if_fail (instruction->type == TYPE_POINTER && strcmp (instruction->name, name) == 0); + instruction = file->current_instruction++; + g_return_if_fail (instruction->type == TYPE_POINTER); + instruction->u.pointer.location = location; *location = (gpointer) 0xFedeAbe; @@ -597,6 +602,11 @@ sfile_get_pointer (SFileInput *file, g_hash_table_insert (file->instructions_by_location, location, instruction); } + + instruction = file->current_instruction++; + g_return_if_fail (instruction->type == TYPE_POINTER && + strcmp (instruction->name, name) == 0); + } void @@ -604,12 +614,21 @@ sfile_get_integer (SFileInput *file, const char *name, int *integer) { - Instruction *instruction = file->current_instruction++; + Instruction *instruction; + + instruction = file->current_instruction++; g_return_if_fail (instruction->type == TYPE_INTEGER && strcmp (instruction->name, name) == 0); + instruction = file->current_instruction++; + g_return_if_fail (instruction->type == TYPE_INTEGER); + if (integer) *integer = instruction->u.integer.value; + + instruction = file->current_instruction++; + g_return_if_fail (instruction->type == TYPE_INTEGER && + strcmp (instruction->name, name) == 0); } void @@ -617,28 +636,52 @@ sfile_get_string (SFileInput *file, const char *name, char **string) { - Instruction *instruction = file->current_instruction++; + Instruction *instruction; + + instruction = file->current_instruction++; g_return_if_fail (instruction->type == TYPE_STRING && strcmp (instruction->name, name) == 0); + instruction = file->current_instruction++; + g_return_if_fail (instruction->type == TYPE_STRING); + if (string) *string = g_strdup (instruction->u.string.value); + + instruction = file->current_instruction++; + g_return_if_fail (instruction->type == TYPE_STRING && + strcmp (instruction->name, name) == 0); } static void hook_up_pointers (SFileInput *file) { int i; - + +#if 0 + g_print ("emfle\n"); +#endif for (i = 0; i < file->n_instructions; ++i) { Instruction *instruction = &(file->instructions[i]); - if (instruction->type == TYPE_POINTER) + if (instruction->kind == VALUE && + instruction->type == TYPE_POINTER) { - gpointer target_object = - instruction->u.pointer.target_instruction->u.begin.end_instruction->u.end.object; + gpointer target_object; + Instruction *target_instruction; + + target_instruction = instruction->u.pointer.target_instruction; + if (target_instruction) + target_object = target_instruction->u.begin.end_instruction->u.end.object; + else + target_object = NULL; + +#if 0 + g_print ("target object: %p\n", target_object); +#endif + *(instruction->u.pointer.location) = target_object; } } @@ -705,16 +748,22 @@ handle_begin_element (GMarkupParseContext *parse_context, { BuildContext *build = user_data; Instruction instruction; - + instruction.u.begin.id = get_id (attribute_names, attribute_values, err); if (instruction.u.begin.id == -1) return; - build->state = state_transition_begin (build->state, element_name, &instruction.type, err); + build->state = state_transition_begin (build->state, element_name, &instruction.type); + if (!build->state) + { + set_unknown_element_error (err, "<%s> unexpected here", element_name); + return; + } + + /* FIXME: is there really a reason to add begin/end instructions for values? */ instruction.name = g_strdup (element_name); instruction.kind = BEGIN; - g_array_append_val (build->instructions, instruction); } @@ -727,7 +776,12 @@ handle_end_element (GMarkupParseContext *context, BuildContext *build = user_data; Instruction instruction; - build->state = state_transition_end (build->state, element_name, &instruction.type, err); + build->state = state_transition_end (build->state, element_name, &instruction.type); + if (!build->state) + { + set_unknown_element_error (err, " unexpected here", element_name); + return; + } instruction.name = g_strdup (element_name); instruction.kind = END; @@ -739,7 +793,7 @@ static gboolean decode_text (const char *text, char **decoded) { int length = strlen (text); - + if (length < 2) return FALSE; @@ -761,11 +815,28 @@ handle_text (GMarkupParseContext *context, { BuildContext *build = user_data; Instruction instruction; - - build->state = state_transition_text (build->state, &instruction.type, err); + char *free_me; + text = free_me = g_strstrip (g_strdup (text)); + + if (strlen (text) == 0) + goto out; + + build->state = state_transition_text (build->state, &instruction.type); + if (!build->state) + { + int line, ch; + g_markup_parse_context_get_position (context, &line, &ch); +#if 0 + g_print ("line: %d char: %d\n", line, ch); +#endif + set_invalid_content_error (err, "Unexpected text data"); + goto out; + } + instruction.name = NULL; instruction.kind = VALUE; + instruction.u.string.value = 0x01; switch (instruction.type) { @@ -773,47 +844,50 @@ handle_text (GMarkupParseContext *context, if (!get_number (text, &instruction.u.pointer.target_id)) { set_invalid_content_error (err, "Contents '%s' of pointer element is not a number", text); - return; + goto out; } break; - + case TYPE_INTEGER: if (!get_number (text, &instruction.u.integer.value)) { set_invalid_content_error (err, "Contents '%s' of integer element not a number", text); - return; + goto out; } break; - + case TYPE_STRING: if (!decode_text (text, &instruction.u.string.value)) { set_invalid_content_error (err, "Contents '%s' of text element is illformed", text); - return; + goto out; } break; - + default: g_assert_not_reached(); break; } - + g_array_append_val (build->instructions, instruction); + + out: + g_free (free_me); } static void free_instructions (Instruction *instructions, int n_instructions) { int i; - + for (i = 0; i < n_instructions; ++i) { Instruction *instruction = &(instructions[i]); if (instruction->name) g_free (instruction->name); - - if (instruction->type == TYPE_STRING) + + if (instruction->kind == VALUE && instruction->type == TYPE_STRING) g_free (instruction->u.string.value); } @@ -928,19 +1002,31 @@ post_process_read_instructions (Instruction *instructions, int n_instructions, G { Instruction *instruction = &(instructions[i]); - if (instruction->type == TYPE_POINTER) + if (instruction->kind == VALUE && + instruction->type == TYPE_POINTER) { int target_id = instruction->u.pointer.target_id; - - Instruction *target = g_hash_table_lookup (instructions_by_id, - GINT_TO_POINTER (target_id)); - if (!target) + if (target_id) { - set_invalid_content_error (err, "Id %d doesn't reference any record or list\n", - instruction->u.pointer.target_id); - retval = FALSE; - break; + Instruction *target = g_hash_table_lookup (instructions_by_id, + GINT_TO_POINTER (target_id)); + + if (target) + { + instruction->u.pointer.target_instruction = target; + } + else + { + set_invalid_content_error (err, "Id %d doesn't reference any record or list\n", + instruction->u.pointer.target_id); + retval = FALSE; + break; + } + } + else + { + instruction->u.pointer.target_instruction = NULL; } } } @@ -1018,6 +1104,9 @@ sfile_load (const char *filename, } g_free (contents); + + input->current_instruction = input->instructions; + input->instructions_by_location = g_hash_table_new (g_direct_hash, g_direct_equal); return input; } @@ -1050,8 +1139,7 @@ sfile_begin_add_record (SFileOutput *file, { Instruction instruction; - file->state = state_transition_begin ( - file->state, name, &instruction.type, NULL); + file->state = state_transition_begin (file->state, name, &instruction.type); g_return_if_fail (file->state); g_return_if_fail (is_record_type (instruction.type)); @@ -1068,8 +1156,7 @@ sfile_begin_add_list (SFileOutput *file, { Instruction instruction; - file->state = state_transition_begin ( - file->state, name, &instruction.type, NULL); + file->state = state_transition_begin (file->state, name, &instruction.type); g_return_if_fail (file->state); g_return_if_fail (is_list_type (instruction.type)); @@ -1093,8 +1180,7 @@ sfile_end_add (SFileOutput *file, return; } - file->state = state_transition_end ( - file->state, name, &instruction.type, NULL); + file->state = state_transition_end (file->state, name, &instruction.type); if (!file->state) { @@ -1119,13 +1205,13 @@ sfile_check_value (SFileOutput *file, { SType tmp_type; - file->state = state_transition_begin (file->state, name, &tmp_type, NULL); + file->state = state_transition_begin (file->state, name, &tmp_type); g_return_if_fail (file->state && tmp_type == type); - file->state = state_transition_text (file->state, &type, NULL); + file->state = state_transition_text (file->state, &type); g_return_if_fail (file->state && tmp_type == type); - file->state = state_transition_end (file->state, name, &type, NULL); + file->state = state_transition_end (file->state, name, &type); g_return_if_fail (file->state && tmp_type == type); } @@ -1238,7 +1324,7 @@ post_process_write_instructions (SFileOutput *sfile) if (target->u.end.begin_instruction->u.begin.id == -1) target->u.end.begin_instruction->u.begin.id = id++; - + instruction->u.pointer.target_id = target->u.end.begin_instruction->u.begin.id; } @@ -1269,7 +1355,27 @@ add_integer (GString *output, int value) static void add_string (GString *output, const char *str) { +#if 0 + /* FIXME: strings need to be encoded so that special + * xml characters can be added, and so they don't get + * confused with the deletion of whitespace-only + * text entries + */ g_string_append_printf (output, "%s", str); +#endif + + int i; + g_string_append_c (output, '\"'); + for (i = 0; str[i] != '\0'; ++i) + { + if (str[i] == '\"') + g_string_append_printf (output, "%s", "\\\""); + else if (str[i] == '\\') + g_string_append_printf (output, "\\\\"); + else + g_string_append_c (output, str[i]); + } + g_string_append_c (output, '\"'); } static void @@ -1296,6 +1402,12 @@ add_nl (GString *output) g_string_append_c (output, '\n'); } +static gboolean +file_replace (const gchar *filename, + const gchar *contents, + gssize length, + GError **error); + gboolean sfile_output_save (SFileOutput *sfile, const char *filename, @@ -1359,13 +1471,13 @@ sfile_output_save (SFileOutput *sfile, /* FIXME: don't dump this to stdout */ g_print (output->str); -#if 0 /* FIXME, cut-and-paste the g_file_write() implementation * as long as it isn't in glib */ - retval = g_file_write (filename, output->str, - 1, err); -#endif + retval = file_replace (filename, output->str, - 1, err); +#if 0 retval = TRUE; +#endif g_string_free (output, TRUE); @@ -1378,3 +1490,333 @@ sfile_output_free (SFileOutput *sfile) { /* FIXME */ } + + + + + + + + + + +/* A copy of g_file_replace() because I don't want to depend on + * GLib HEAD + */ +#include +#include +#include +#include + +static gboolean +rename_file (const char *old_name, + const char *new_name, + GError **err) +{ + errno = 0; + if (g_rename (old_name, new_name) == -1) + { + int save_errno = errno; + gchar *display_old_name = g_filename_display_name (old_name); + gchar *display_new_name = g_filename_display_name (new_name); + + g_set_error (err, + G_FILE_ERROR, + g_file_error_from_errno (save_errno), + "Failed to rename file '%s' to '%s': g_rename() failed: %s", + display_old_name, + display_new_name, + g_strerror (save_errno)); + + g_free (display_old_name); + g_free (display_new_name); + + return FALSE; + } + + return TRUE; +} + +static gboolean +set_umask_permissions (int fd, + GError **err) +{ +#ifdef G_OS_WIN32 + + return TRUE; + +#else + + /* All of this function is just to work around the fact that + * there is no way to get the umask without changing it. + * + * We can't just change-and-reset the umask because that would + * lead to a race condition if another thread tried to change + * the umask in between the getting and the setting of the umask. + * So we have to do the whole thing in a child process. + */ + + pid_t pid = fork (); + + if (pid == -1) + { + g_set_error (err, + G_FILE_ERROR, + g_file_error_from_errno (errno), + "Could not change file mode: fork() failed: %s", + g_strerror (errno)); + + return FALSE; + } + else if (pid == 0) + { + /* child */ + mode_t mask = umask (0666); + + errno = 0; + if (fchmod (fd, 0666 & ~mask) == -1) + _exit (errno); + else + _exit (0); + + return TRUE; /* To quiet gcc */ + } + else + { + /* parent */ + int status; + + waitpid (pid, &status, 0); + + if (WIFEXITED (status)) + { + int chmod_errno = WEXITSTATUS (status); + + if (chmod_errno == 0) + { + return TRUE; + } + else + { + g_set_error (err, + G_FILE_ERROR, + g_file_error_from_errno (chmod_errno), + "Could not change file mode: chmod() failed: %s", + g_strerror (chmod_errno)); + + return FALSE; + } + } + else if (WIFSIGNALED (status)) + { + g_set_error (err, + G_FILE_ERROR, + G_FILE_ERROR_FAILED, + "Could not change file mode: Child terminated by signal: %s", + g_strsignal (WTERMSIG (status))); + + return FALSE; + } + else + { + /* This shouldn't happen */ + g_set_error (err, + G_FILE_ERROR, + G_FILE_ERROR_FAILED, + "Could not change file mode: Child terminated abnormally"); + return FALSE; + } + } +#endif +} + +static gchar * +write_to_temp_file (const gchar *contents, + gssize length, + const gchar *template, + GError **err) +{ + gchar *tmp_name; + gchar *display_name; + gchar *retval; + FILE *file; + gint fd; + int save_errno; + + retval = NULL; + + tmp_name = g_strdup_printf ("%s.XXXXXX", template); + + errno = 0; + fd = g_mkstemp (tmp_name); + save_errno = errno; + display_name = g_filename_display_name (tmp_name); + + if (fd == -1) + { + g_set_error (err, + G_FILE_ERROR, + g_file_error_from_errno (save_errno), + "Failed to create file '%s': %s", + display_name, g_strerror (save_errno)); + + goto out; + } + + if (!set_umask_permissions (fd, err)) + { + close (fd); + g_unlink (tmp_name); + + goto out; + } + + errno = 0; + file = fdopen (fd, "wb"); + if (!file) + { + g_set_error (err, + G_FILE_ERROR, + g_file_error_from_errno (errno), + "Failed to open file '%s' for writing: fdopen() failed: %s", + display_name, + g_strerror (errno)); + + close (fd); + g_unlink (tmp_name); + + goto out; + } + + if (length > 0) + { + size_t n_written; + + errno = 0; + + n_written = fwrite (contents, 1, length, file); + + if (n_written < length) + { + g_set_error (err, + G_FILE_ERROR, + g_file_error_from_errno (errno), + "Failed to write file '%s': fwrite() failed: %s", + display_name, + g_strerror (errno)); + + fclose (file); + g_unlink (tmp_name); + + goto out; + } + } + + errno = 0; + if (fclose (file) == EOF) + { + g_set_error (err, + G_FILE_ERROR, + g_file_error_from_errno (errno), + "Failed to close file '%s': fclose() failed: %s", + display_name, + g_strerror (errno)); + + g_unlink (tmp_name); + + goto out; + } + + retval = g_strdup (tmp_name); + + out: + g_free (tmp_name); + g_free (display_name); + + return retval; +} + +static gboolean +file_replace (const gchar *filename, + const gchar *contents, + gssize length, + GError **error) +{ + gchar *tmp_filename; + gboolean retval; + GError *rename_error = NULL; + + g_return_val_if_fail (filename != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + g_return_val_if_fail (contents != NULL || length == 0, FALSE); + g_return_val_if_fail (length >= -1, FALSE); + + if (length == -1) + length = strlen (contents); + + tmp_filename = write_to_temp_file (contents, length, filename, error); + + if (!tmp_filename) + { + retval = FALSE; + goto out; + } + + if (!rename_file (tmp_filename, filename, &rename_error)) + { +#ifndef G_OS_WIN32 + + g_unlink (tmp_filename); + g_propagate_error (error, rename_error); + retval = FALSE; + goto out; + +#else /* G_OS_WIN32 */ + + /* Renaming failed, but on Windows this may just mean + * the file already exists. So if the target file + * exists, try deleting it and do the rename again. + */ + if (!g_file_test (filename, G_FILE_TEST_EXISTS)) + { + g_unlink (tmp_filename); + g_propagate_error (error, rename_error); + retval = FALSE; + goto out; + } + + g_error_free (rename_error); + + if (g_unlink (filename) == -1) + { + gchar *display_filename = g_filename_display_name (filename); + + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errno), + "Existing file '%s' could not be removed: g_unlink() failed: %s", + display_filename, + g_strerror (errno)); + + g_free (display_filename); + g_unlink (tmp_filename); + retval = FALSE; + goto out; + } + + if (!rename_file (tmp_filename, filename, error)) + { + g_unlink (tmp_filename); + retval = FALSE; + goto out; + } + +#endif + } + + retval = TRUE; + + out: + g_free (tmp_filename); + return retval; +} diff --git a/sysprof.c b/sysprof.c index 3b21579..dc9e238 100644 --- a/sysprof.c +++ b/sysprof.c @@ -56,6 +56,22 @@ struct Application int timeout_id; int generating_profile; + + gboolean profile_from_file; /* FIXME: This is a kludge. Figure out how + * to maintain the application model properly + * + * The fundamental issue is that the state of + * widgets is controlled by two different + * entities: + * + * The user clicks on them, changing their + * state. + * + * The application model changes, changing their + * state. + * + * Model/View/Controller is a possibility. + */ }; static void @@ -290,6 +306,7 @@ delete_data (Application *app) process_flush_caches (); app->n_samples = 0; queue_show_samples (app); + app->profile_from_file = FALSE; } static void @@ -349,6 +366,7 @@ fill_main_list (Application *app) G_TYPE_POINTER); objects = profile_get_objects (profile); + g_print ("got %d objects\n", g_list_length (objects)); for (list = objects; list != NULL; list = list->next) { ProfileObject *object = list->data; @@ -414,12 +432,12 @@ on_profile_toggled (gpointer widget, gpointer data) if (gtk_toggle_tool_button_get_active (widget)) { - if (app->profile) + if (app->profile && !app->profile_from_file) { profile_free (app->profile); app->profile = NULL; } - + ensure_profile (app); } } @@ -486,7 +504,27 @@ on_save_as_clicked (gpointer widget, gpointer data) static void on_open_clicked (gpointer widget, gpointer data) { +#if 0 sorry (NULL, "Open is not implemented yet. (Fortunately, neither is saving),"); +#endif + Application *app = data; + GError *err = NULL; + Profile *profile = profile_load ("name.profile", &err); + if (!profile) + sorry (NULL, "Could not open: %s\n", err->message); + else + { + delete_data (app); + + app->state = DISPLAYING; + + app->profile = profile; + app->profile_from_file = TRUE; + + fill_main_list (app); + + update_sensitivity (app); + } } static void -- 2.7.4