--- gnumeric-1.6.2/src/workbook-control-gui.h.dotlocking 2005-05-03 14:38:59.000000000 -0400 +++ gnumeric-1.6.2/src/workbook-control-gui.h 2006-03-08 15:56:05.000000000 -0500 @@ -41,9 +41,14 @@ void wbcg_copy_toolbar_visibility void wbcg_toggle_end_mode (WorkbookControlGUI *wbcg); void wbcg_set_end_mode (WorkbookControlGUI *wbcg, gboolean flag); void wbcg_set_transient_for (WorkbookControlGUI *wbcg, GtkWindow *window); +void wbcg_save_set_sensitivity (WorkbookControlGUI *wbcg, gboolean state); + +void wbcg_locked_file_dialog (WorkbookControl *wbc, const char *data, const char *msg); + + PangoFontDescription *wbcg_get_font_desc (WorkbookControlGUI *wbcg); #endif /* GNUMERIC_WORKBOOK_CONTROL_GUI_H */ --- /dev/null 2006-02-28 19:57:22.510895250 -0500 +++ gnumeric-1.6.2/src/flock.h 2006-03-08 15:56:05.000000000 -0500 @@ -0,0 +1,10 @@ +#ifndef FLOCK_H +#define FLOCK_H + +char *flock_acquire (const char *); + +char *flock_verify (const char *); + +gboolean flock_release (const char *); + +#endif /* FLOCK_H */ --- gnumeric-1.6.2/src/workbook-view.c.dotlocking 2005-09-15 21:27:13.000000000 -0400 +++ gnumeric-1.6.2/src/workbook-view.c 2006-03-08 15:56:05.000000000 -0500 @@ -1,7 +1,8 @@ -/* vim: set sw=8: */ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + /* * workbook-view.c: View functions for the workbook * * Copyright (C) 2000-2004 Jody Goldberg (jody gnome org) * * This program is free software; you can redistribute it and/or @@ -21,12 +22,13 @@ */ #include #include "gnumeric.h" #include "workbook-view.h" #include "workbook-control-priv.h" +#include "workbook-control-gui.h" #include "workbook.h" #include "application.h" #include "sheet.h" #include "sheet-view.h" #include "sheet-merge.h" #include "sheet-style.h" @@ -59,12 +61,14 @@ #include #include #include #include "mathfunc.h" #include +#include "flock.h" + /* WorkbookView signals */ enum { LAST_SIGNAL }; Workbook * @@ -578,12 +582,14 @@ workbook_view_new (Workbook *wb) wbv->show_horizontal_scrollbar = TRUE; wbv->show_vertical_scrollbar = TRUE; wbv->show_notebook_tabs = TRUE; wbv->do_auto_completion = gnm_app_use_auto_complete (); wbv->is_protected = FALSE; + workbook_set_externally_locked (wb, FALSE); + /* Set the default operation to be performed over selections */ wbv->auto_expr = NULL; wbv->auto_expr_desc = NULL; wbv->auto_expr_value_as_string = NULL; wbv->auto_expr_use_max_precision = FALSE; wb_view_auto_expr (wbv, _("Sum"), "sum"); @@ -644,12 +650,72 @@ wbv_save_to_uri (WorkbookView *wbv, GOFi if (!gnumeric_io_error_occurred (io_context)) go_cmd_context_error_export (GO_CMD_CONTEXT (io_context), msg); g_free (msg); } +void +wb_view_adapt_to_locking (WorkbookView *wbv, + const char *lockdata, + WbvLockNotification ln) +{ + char *uri, *msg, *tmp_data; + const char *raw_msg; + + if ((ln != WBV_EXT_LOCK_SAVE) && (ln != WBV_EXT_LOCK_SAVEAS)) { + WORKBOOK_VIEW_FOREACH_CONTROL (wbv, control, wbcg_save_set_sensitivity (control, FALSE);); + } + + switch (ln) { + case WBV_EXT_LOCK_OPEN: + raw_msg = "The workbook '%s' is locked, which should mean that " + "someone else is currently using it. The workbook " + "will be opened, but saving changes will be disabled " + "to prevent the possibility of conflicting changes by " + "concurrent users. Use the SAVE AS menu action to " + "store any changes under a new name. Otherwise try " + "re-opening the workbook later."; + break; + case WBV_EXT_LOCK_SAVE: + raw_msg = "Saving of workbook '%s' has been disabled. Use the " + "SAVE AS menu action to store any changes under a new name."; + break; + case WBV_EXT_LOCK_SAVEAS: + raw_msg = "The workbook '%s' is locked. This likely means that " + "someone else is currently using it. To prevent the possibility " + "of conflicting changes by concurrent users, saving to this " + "workbook has been disabled."; + break; + case WBV_SELF_LOCK_FAULT: + raw_msg = "The lock for workbook '%s' has been removed or damaged. " + "This is unexpected and is probably a sign that something " + "has gone wrong. To prevent the possibility of conflicting " + "changes by concurrent users, saving to this workbook has " + "been disabled."; + break; + default: + g_assert_not_reached(); + } + + uri = workbook_get_uri (wb_view_workbook (wbv)); + if (lockdata == NULL) { + tmp_data = flock_verify (uri); + } else { + tmp_data = lockdata; + } + msg = g_strdup_printf (raw_msg, g_path_get_basename (uri)); + + WORKBOOK_VIEW_FOREACH_CONTROL (wbv, control, + wbcg_locked_file_dialog(control, tmp_data, msg); break;); + + if (lockdata == NULL) { + g_free (tmp_data); + } + g_free (msg); +} + /** * wb_view_save_as: * @wbv : Workbook View * @fs : GOFileSaver object * @uri : URI to save as. * @context : @@ -664,21 +730,34 @@ gboolean wb_view_save_as (WorkbookView *wbv, GOFileSaver *fs, char const *uri, GOCmdContext *context) { IOContext *io_context; Workbook *wb; gboolean has_error, has_warning; + char *lockdata; g_return_val_if_fail (IS_WORKBOOK_VIEW (wbv), FALSE); g_return_val_if_fail (IS_GO_FILE_SAVER (fs), FALSE); g_return_val_if_fail (uri != NULL, FALSE); g_return_val_if_fail (IS_GO_CMD_CONTEXT (context), FALSE); wb = wb_view_workbook (wbv); io_context = gnumeric_io_context_new (context); + if ((lockdata = flock_acquire(uri)) != NULL) { + wb_view_adapt_to_locking (wbv, lockdata, WBV_EXT_LOCK_SAVEAS); + g_free (lockdata); + return FALSE; + } + if (! workbook_externally_locked (wb)) { + flock_release (workbook_get_uri(wb)); + } else { + workbook_set_externally_locked (wbv->wb, FALSE); + WORKBOOK_VIEW_FOREACH_CONTROL (wbv, control, wbcg_save_set_sensitivity (control, TRUE);); + } + go_cmd_context_set_sensitive (context, FALSE); wbv_save_to_uri (wbv, fs, uri, io_context); go_cmd_context_set_sensitive (context, TRUE); has_error = gnumeric_io_error_occurred (io_context); has_warning = gnumeric_io_warning_occurred (io_context); @@ -710,17 +789,30 @@ gboolean wb_view_save (WorkbookView *wbv, GOCmdContext *context) { IOContext *io_context; Workbook *wb; GOFileSaver *fs; gboolean has_error, has_warning; + char *lockdata; g_return_val_if_fail (IS_WORKBOOK_VIEW (wbv), FALSE); g_return_val_if_fail (IS_GO_CMD_CONTEXT (context), FALSE); wb = wb_view_workbook (wbv); + + if ((lockdata = flock_verify (workbook_get_uri(wb))) != NULL) { + if (workbook_externally_locked (wb)) { + wb_view_adapt_to_locking (wbv, lockdata, WBV_EXT_LOCK_SAVE); + } else { + workbook_set_externally_locked (wbv->wb, TRUE); + wb_view_adapt_to_locking (wbv, lockdata, WBV_SELF_LOCK_FAULT); + } + g_free (lockdata); + return FALSE; + } + fs = workbook_get_file_saver (wb); if (fs == NULL) fs = go_file_saver_get_default (); io_context = gnumeric_io_context_new (context); if (fs == NULL) @@ -1019,18 +1111,29 @@ wb_view_new_from_uri (char const *uri, g_return_val_if_fail (uri != NULL, NULL); input = go_file_open (uri, &err); if (input != NULL) { WorkbookView *res; + char *lockdata; g_printerr ("Reading %s\n", uri); res = wb_view_new_from_input (input, optional_fmt, io_context, optional_enc); g_object_unref (G_OBJECT (input)); + + if ((lockdata = flock_acquire(uri)) != NULL) { + /* There is apparently no GUI existing yet (at least in most cases), + so we cannot disable (grey out) SAVE here... Only make a note for later. */ + workbook_set_externally_locked (res->wb, TRUE); + g_free (lockdata); + } else { + workbook_set_externally_locked (res->wb, FALSE); + } + return res; } if (err != NULL) { if (err->message != NULL) msg = g_strdup (err->message); --- gnumeric-1.6.2/src/workbook.c.dotlocking 2005-09-21 00:46:42.000000000 -0400 +++ gnumeric-1.6.2/src/workbook.c 2006-03-08 15:56:05.000000000 -0500 @@ -37,12 +37,14 @@ #include "gutils.h" #include "gnm-marshalers.h" #include "style-color.h" #include #include +#include "flock.h" + #ifdef WITH_GTK #ifdef WITH_GNOME #include #else #include /* for gtk_main_quit */ #endif @@ -69,20 +71,36 @@ static void cb_saver_finalize (Workbook *wb, GOFileSaver *saver) { g_return_if_fail (IS_GO_FILE_SAVER (saver)); g_return_if_fail (IS_WORKBOOK (wb)); g_return_if_fail (wb->file_saver == saver); wb->file_saver = NULL; +} + +gboolean +workbook_externally_locked (Workbook *wb) +{ + return wb->externally_locked; +} + +void +workbook_set_externally_locked (Workbook *wb, gboolean state) +{ + wb->externally_locked = state; } static void workbook_dispose (GObject *wb_object) { Workbook *wb = WORKBOOK (wb_object); GList *sheets, *ptr; + if (! wb->externally_locked) { + flock_release(wb->uri); + } + wb->during_destruction = TRUE; if (wb->file_saver) workbook_set_saveinfo (wb, wb->file_format_level, NULL); /* Remove all the sheet controls to avoid displaying while we exit */ --- gnumeric-1.6.2/src/workbook-priv.h.dotlocking 2005-03-16 17:28:00.000000000 -0500 +++ gnumeric-1.6.2/src/workbook-priv.h 2006-03-08 15:56:05.000000000 -0500 @@ -17,12 +17,14 @@ struct _Workbook { GHashTable *sheet_order_dependents; GHashTable *sheet_local_functions; gboolean modified; gboolean is_placeholder; + gboolean externally_locked; + gchar *uri; FileFormatLevel file_format_level; GOFileSaver *file_saver; /* Undo support */ GSList *undo_commands; --- /dev/null 2006-02-28 19:57:22.510895250 -0500 +++ gnumeric-1.6.2/src/flock.c 2006-03-08 15:56:05.000000000 -0500 @@ -0,0 +1,238 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "gnumeric.h" +#include "flock.h" + +#ifndef O_NOFOLLOW +#define O_NOFOLLOW (0) +#endif + +const unsigned max_lockdata_sz = 128; + +char * gen_lockname (const char *); +char * gen_lockdata (void); + +char * +gen_lockname (const char *uri) +{ + char *dir, *base, *lockname; + const char *s0 = "/."; + const char *s1 = ".lock"; + + if ((uri == NULL) || (strlen(uri) == 0) || (strncmp (uri, "file://", 7) != 0)) { + return NULL; + } + + dir = go_dirname_from_uri (uri, TRUE); + base = go_basename_from_uri (uri); + lockname = (char *) g_malloc (strlen (dir) + strlen (base) + strlen (s0) + strlen (s1) + 1); + sprintf (lockname, "%s%s%s%s", dir, s0, base, s1); + g_free (base); + g_free (dir); + + /* XXX debugging */ + /* g_printerr ("lockname is '%s'\n", lockname); */ + + return lockname; +} + +char * +gen_lockdata () +{ + char hostname [HOST_NAME_MAX + 1]; + struct passwd *pw; + uid_t uid; + pid_t pid; + char *uid_str, *pid_str; + char *data; + const char *s0 = "@"; + const char *s1 = " (uid "; + const char *s2 = ", pid "; + const char *s3 = ")"; + + if (gethostname (hostname, HOST_NAME_MAX) == -1) { + return NULL; + } + uid = getuid (); + pid = getpid (); + if ((pw = getpwuid (uid)) == NULL) { + return NULL; + } + uid_str = g_strdup_printf ("%lu", (long unsigned) uid); + pid_str = g_strdup_printf ("%lu", (long unsigned) pid); + data = (char *) g_malloc (MIN(strlen (pw->pw_name) + strlen (hostname) + strlen (uid_str) + + strlen (pid_str) + strlen (s0) + strlen (s1) + strlen (s2) + + strlen (s3) + 1, max_lockdata_sz)); + snprintf (data, max_lockdata_sz - 1, "%s%s%s%s%s%s%s%s", + pw->pw_name, s0, hostname, s1, uid_str, s2, pid_str, s3); + g_free(uid_str); + g_free(pid_str); + + return data; +} + +char * +flock_acquire (const char* uri) +{ + char *lockname = NULL; + char *lockdata = NULL; + const char *err_msg = NULL; + int fd; + + lockname = gen_lockname (uri); + if (lockname == NULL) { + err_msg = "illegal lockname"; + goto flock_lock_finish; + } + + /* XXX debugging */ + /* g_printerr ("locking '%s'\n", lockname); */ + + fd = g_open (lockname, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd != -1) { + int w_rc, c_rc; + + lockdata = gen_lockdata (); + if (lockdata == NULL) { + err_msg = "lock data generation error"; + goto flock_lock_finish; + } + + w_rc = write (fd, lockdata, strlen (lockdata) + 1); + g_free(lockdata); + lockdata = NULL; + c_rc = close (fd); + + if (w_rc == -1) { + err_msg = "lock write failure"; + goto flock_lock_finish; + } + if (c_rc == -1) { + err_msg = "lock close failure"; + goto flock_lock_finish; + } + } else if (g_file_error_from_errno(errno) == G_FILE_ERROR_EXIST) { + GError *error = NULL; + + if (! g_file_get_contents (lockname, &lockdata, NULL, &error)) { + err_msg = "lock exists, but reading it failed"; + goto flock_lock_finish; + } + + /* XXX debugging */ + /* g_printerr ("lock already held by '%s'\n", lockdata); */ + + } else { + err_msg = "lock open failed"; + } + + flock_lock_finish: + if (err_msg != NULL) { + /* XXX debugging */ + /* g_printerr ("locking failed for '%s'\n", lockname); */ + + g_assert (lockdata == NULL); + lockdata = g_strdup_printf ("unknown (%s)", err_msg); + } + + if (lockname != NULL) { + g_free(lockname); + } + return lockdata; +} + +char * +flock_verify (const char* uri) +{ + char *lockname = NULL; + char *expected_lockdata = NULL; + char *actual_lockdata = NULL; + const char *err_msg = NULL; + GError *error = NULL; + + lockname = gen_lockname (uri); + if (lockname == NULL) { + err_msg = "illegal lockname"; + goto flock_verify_finish; + } + + expected_lockdata = gen_lockdata (); + if (expected_lockdata == NULL) { + err_msg = "lock data generation error"; + goto flock_verify_finish; + } + + /* XXX debugging */ + /* g_printerr ("verifying '%s'\n", lockname); */ + + g_file_get_contents (lockname, &actual_lockdata, NULL, &error); + g_assert ((actual_lockdata == NULL && error != NULL) || + (actual_lockdata != NULL && error == NULL)); + if (error != NULL) { + /* XXX debugging */ + /* g_printerr ("ERROR: reading lock file -- %s\n", error->message); */ + g_error_free (error); + err_msg = "reading lock failed"; + goto flock_verify_finish; + } + + if (strncmp (expected_lockdata, actual_lockdata, max_lockdata_sz) == 0) { + g_free(actual_lockdata); + actual_lockdata = NULL; + } else { + /* XXX debugging */ + /* g_printerr ("expected '%s', actually '%s'\n", expected_lockdata, actual_lockdata); */ + } + + flock_verify_finish: + if (err_msg != NULL) { + /* XXX debugging */ + /* g_printerr ("lock verify failed for '%s'\n", lockname); */ + + g_assert (actual_lockdata == NULL); + actual_lockdata = g_strdup_printf ("unknown (%s)", err_msg); + } + + if (lockname != NULL) { + g_free(lockname); + } + if (expected_lockdata != NULL) { + g_free(expected_lockdata); + } + return actual_lockdata; +} + +gboolean +flock_release (const char* uri) +{ + char *lockname; + gboolean success; + + if (flock_verify (uri) != NULL) { + return FALSE; + } + + lockname = gen_lockname (uri); + + /* XXX debugging */ + /* g_printerr ("releasing '%s'\n", lockname); */ + + success = (g_remove (lockname) == 0); + g_free(lockname); + + return success; +} --- gnumeric-1.6.2/src/workbook-control-gui.c.dotlocking 2005-11-14 01:34:05.000000000 -0500 +++ gnumeric-1.6.2/src/workbook-control-gui.c 2006-03-08 15:56:05.000000000 -0500 @@ -1174,12 +1174,38 @@ wbcg_menu_state_update (WorkbookControl ? _("Remove a filter") : _("Add a filter"); wbcg_set_action_label (wbcg, "DataAutoFilter", NULL, label, new_tip); } } +void +wbcg_save_set_sensitivity (WorkbookControlGUI *wbcg, gboolean state) +{ + g_return_if_fail (wbcg != NULL); + wbcg_set_action_sensitivity (wbcg, "FileSave", state); +} + +void +wbcg_locked_file_dialog (WorkbookControl *wbc, const char *data, const char *msg) +{ + GtkWidget *d; + WorkbookControlGUI *wbcg = (WorkbookControlGUI *)wbc; + char *header; + + g_return_if_fail (wbcg != NULL); + + header = g_strdup_printf ("FILE LOCKED by %s", data); + d = gnumeric_message_dialog_new (wbcg_toplevel (wbcg), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_WARNING, + header, msg); + g_free (header); + go_gtk_dialog_add_button (GTK_DIALOG(d), "OK", GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); + go_gtk_dialog_run (GTK_DIALOG (d), wbcg_toplevel (wbcg)); +} + static void wbcg_undo_redo_labels (WorkbookControl *wbc, char const *undo, char const *redo) { WorkbookControlGUI *wbcg = (WorkbookControlGUI *)wbc; g_return_if_fail (wbcg != NULL); --- gnumeric-1.6.2/src/Makefile.am.dotlocking 2006-01-30 00:24:47.000000000 -0500 +++ gnumeric-1.6.2/src/Makefile.am 2006-03-08 15:56:05.000000000 -0500 @@ -82,12 +82,14 @@ GNUMERIC_BASE = \ expr.h \ expr-impl.h \ expr-name.c \ expr-name.h \ file-autoft.c \ file-autoft.h \ + flock.c \ + flock.h \ format-template.c \ format-template.h \ func.c \ func.h \ func-builtin.c \ func-builtin.h \ --- gnumeric-1.6.2/src/main-application.c.dotlocking 2006-01-30 00:24:47.000000000 -0500 +++ gnumeric-1.6.2/src/main-application.c 2006-03-08 15:56:05.000000000 -0500 @@ -445,12 +445,16 @@ main (int argc, char const *argv []) opened_workbook = TRUE; icg_set_transient_for (IO_CONTEXT_GTK (ioc), wbcg_toplevel (wbcg)); if (immediate_exit_flag) wbcgs_to_kill = g_slist_prepend (wbcgs_to_kill, wbcg); + + if (workbook_externally_locked (wbv->wb)) { + wb_view_adapt_to_locking (wbv, NULL, WBV_EXT_LOCK_OPEN); + } } /* cheesy attempt to keep the ui from freezing during load */ handle_paint_events (); if (icg_get_interrupted (IO_CONTEXT_GTK (ioc))) break; /* Don't load any more workbooks */ --- gnumeric-1.6.2/src/workbook-view.h.dotlocking 2005-06-08 15:03:16.000000000 -0400 +++ gnumeric-1.6.2/src/workbook-view.h 2006-03-08 15:56:05.000000000 -0500 @@ -1,6 +1,8 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + #ifndef GNUMERIC_WORKBOOK_VIEW_H #define GNUMERIC_WORKBOOK_VIEW_H #include "gnumeric.h" #include #include @@ -44,12 +46,19 @@ typedef struct { #define WORKBOOK_VIEW_TYPE (workbook_view_get_type ()) #define WORKBOOK_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), WORKBOOK_VIEW_TYPE, WorkbookView)) #define WORKBOOK_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), WORKBOOK_VIEW_TYPE, WorkbookViewClass)) #define IS_WORKBOOK_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), WORKBOOK_VIEW_TYPE)) +typedef enum { + WBV_EXT_LOCK_OPEN, + WBV_EXT_LOCK_SAVE, + WBV_EXT_LOCK_SAVEAS, + WBV_SELF_LOCK_FAULT +} WbvLockNotification; + /* Lifecycle */ GType workbook_view_get_type (void); WorkbookView *workbook_view_new (Workbook *optional_workbook); void wb_view_attach_control (WorkbookView *wbv, WorkbookControl *wbc); void wb_view_detach_control (WorkbookControl *wbc); --- gnumeric-1.6.2/src/gui-file.c.dotlocking 2005-11-14 01:34:04.000000000 -0500 +++ gnumeric-1.6.2/src/gui-file.c 2006-03-08 15:56:05.000000000 -0500 @@ -142,12 +142,16 @@ gui_wb_view_show (WorkbookControlGUI *wb new_wbcg = WORKBOOK_CONTROL_GUI (new_wbc); wbcg_copy_toolbar_visibility (new_wbcg, wbcg); } #endif + if (workbook_externally_locked (wbv->wb)) { + wb_view_adapt_to_locking (wbv, NULL, WBV_EXT_LOCK_OPEN); + } + sheet_update (wb_view_cur_sheet (wbv)); } gboolean gui_file_read (WorkbookControlGUI *wbcg, char const *uri, GOFileOpener const *optional_format, gchar const *optional_encoding) --- gnumeric-1.6.2/src/workbook.h.dotlocking 2005-06-30 12:08:02.000000000 -0400 +++ gnumeric-1.6.2/src/workbook.h 2006-03-08 15:56:05.000000000 -0500 @@ -35,12 +35,14 @@ gboolean workbook_sheet_rename GSList *new_names, GOCmdContext *cc); unsigned workbook_find_command (Workbook *wb, gboolean is_undo, gpointer cmd); +gboolean workbook_externally_locked (Workbook *wb); +void workbook_set_externally_locked (Workbook *wb, gboolean state); /* IO Routines */ gboolean workbook_set_uri (Workbook *wb, char const *uri); char const *workbook_get_uri (Workbook const *wb); gboolean workbook_set_saveinfo (Workbook *wb,