Updated Spanish translation
[anjuta-git-plugin.git] / plugins / sourceview / anjuta-document-saver.c
blob53305373ad12d18c41acbcfb2f07766221f2ad5f
1 /*
2 * anjuta-document-saver.c
3 * This file is part of anjuta
5 * Copyright (C) 2005 - Paolo Borelli and Paolo Maggi
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
24 * Modified by the anjuta Team, 2005. See the AUTHORS file for a
25 * list of people on the anjuta Team.
26 * See the ChangeLog files for a list of changes.
28 * $Id$
31 #ifdef HAVE_CONFIG_H
32 #include <config.h>
33 #endif
35 #include <sys/mman.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <fcntl.h>
39 #include <string.h>
40 #include <errno.h>
42 #include <glib/gi18n.h>
43 #include <glib/gfileutils.h>
44 #include <libgnomevfs/gnome-vfs.h>
46 #include "anjuta-encodings.h"
47 #include "anjuta-document-saver.h"
48 #include "anjuta-marshal.h"
49 #include "anjuta-convert.h"
51 #define ANJUTA_DOCUMENT_SAVER_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), ANJUTA_TYPE_DOCUMENT_SAVER, AnjutaDocumentSaverPrivate))
53 struct _AnjutaDocumentSaverPrivate
55 AnjutaDocument *document;
57 gchar *uri;
58 const AnjutaEncoding *encoding;
60 AnjutaDocumentSaveFlags flags;
62 gboolean keep_backup;
63 gchar *backup_ext;
64 gboolean backups_in_curr_dir;
66 time_t doc_mtime;
67 gchar *mime_type; //CHECK use FileInfo instead?
69 GnomeVFSFileSize size;
70 GnomeVFSFileSize bytes_written;
72 /* temp data for local files */
73 gint fd;
74 gchar *local_path;
76 /* temp data for remote files */
77 GnomeVFSURI *vfs_uri;
78 GnomeVFSAsyncHandle *handle;
79 GnomeVFSAsyncHandle *info_handle;
80 gint tmpfd;
81 gchar *tmp_fname;
82 GnomeVFSFileInfo *orig_info; /* used to restore permissions */
84 GError *error;
87 G_DEFINE_TYPE(AnjutaDocumentSaver, anjuta_document_saver, G_TYPE_OBJECT)
89 /* Signals */
91 enum {
92 SAVING,
93 LAST_SIGNAL
96 static guint signals[LAST_SIGNAL] = { 0 };
98 static void
99 anjuta_document_saver_finalize (GObject *object)
101 AnjutaDocumentSaverPrivate *priv = ANJUTA_DOCUMENT_SAVER (object)->priv;
103 g_free (priv->uri);
105 if (priv->vfs_uri)
106 gnome_vfs_uri_unref (priv->vfs_uri);
108 g_free (priv->backup_ext);
110 g_free (priv->local_path);
111 g_free (priv->mime_type);
112 g_free (priv->tmp_fname);
114 if (priv->orig_info)
115 gnome_vfs_file_info_unref (priv->orig_info);
117 if (priv->error)
118 g_error_free (priv->error);
120 G_OBJECT_CLASS (anjuta_document_saver_parent_class)->finalize (object);
123 static void
124 anjuta_document_saver_class_init (AnjutaDocumentSaverClass *klass)
126 GObjectClass *object_class = G_OBJECT_CLASS (klass);
128 object_class->finalize = anjuta_document_saver_finalize;
130 signals[SAVING] =
131 g_signal_new ("saving",
132 G_OBJECT_CLASS_TYPE (object_class),
133 G_SIGNAL_RUN_LAST,
134 G_STRUCT_OFFSET (AnjutaDocumentSaverClass, saving),
135 NULL, NULL,
136 anjuta_marshal_VOID__BOOLEAN_POINTER,
137 G_TYPE_NONE,
139 G_TYPE_BOOLEAN,
140 G_TYPE_POINTER);
142 g_type_class_add_private (object_class, sizeof(AnjutaDocumentSaverPrivate));
145 static void
146 anjuta_document_saver_init (AnjutaDocumentSaver *saver)
148 saver->priv = ANJUTA_DOCUMENT_SAVER_GET_PRIVATE (saver);
150 saver->priv->fd = -1;
152 saver->priv->tmpfd = -1;
154 saver->priv->error = NULL;
157 AnjutaDocumentSaver *
158 anjuta_document_saver_new (AnjutaDocument *doc)
160 AnjutaDocumentSaver *saver;
162 g_return_val_if_fail (ANJUTA_IS_DOCUMENT (doc), NULL);
164 saver = ANJUTA_DOCUMENT_SAVER (g_object_new (ANJUTA_TYPE_DOCUMENT_SAVER, NULL));
166 saver->priv->document = doc;
168 return saver;
172 * Write the document contents in fd.
174 static gboolean
175 write_document_contents (gint fd,
176 GtkTextBuffer *doc,
177 const AnjutaEncoding *encoding,
178 GError **error)
180 GtkTextIter start_iter;
181 GtkTextIter end_iter;
182 gchar *contents;
183 gsize len;
184 gboolean add_cr;
185 ssize_t written;
186 gboolean res;
188 gtk_text_buffer_get_bounds (doc, &start_iter, &end_iter);
189 contents = gtk_text_buffer_get_slice (doc, &start_iter, &end_iter, TRUE);
191 len = strlen (contents);
193 if (len >= 1)
194 add_cr = (*(contents + len - 1) != '\n');
195 else
196 add_cr = FALSE;
198 if (encoding != anjuta_encoding_get_utf8 ())
200 gchar *converted_contents;
201 gsize new_len;
203 converted_contents = anjuta_convert_from_utf8 (contents,
204 len,
205 encoding,
206 &new_len,
207 error);
208 g_free (contents);
210 if (*error != NULL)
212 /* Conversion error */
213 return FALSE;
215 else
217 contents = converted_contents;
218 len = new_len;
222 /* make sure we are at the start */
223 res = (lseek (fd, 0, SEEK_SET) != -1);
225 /* Truncate the file to 0, in case it was not empty */
226 if (res)
227 res = (ftruncate (fd, 0) == 0);
229 /* Save the file content */
230 if (res)
232 written = write (fd, contents, len);
233 res = ((written != -1) && ((gsize) written == len));
236 /* Add \n at the end if needed */
237 if (res && add_cr)
239 if (encoding != anjuta_encoding_get_utf8 ())
241 gchar *converted_n = NULL;
242 gsize n_len;
244 converted_n = anjuta_convert_from_utf8 ("\n",
245 -1,
246 encoding,
247 &n_len,
248 NULL);
250 if (converted_n == NULL)
252 /* we do not error out for this */
253 g_warning ("Cannot add '\\n' at the end of the file.");
255 else
257 written = write (fd, converted_n, n_len);
258 res = ((written != -1) && ((gsize) written == n_len));
259 g_free (converted_n);
262 else
264 res = (write (fd, "\n", 1) == 1);
268 g_free (contents);
270 if (!res)
272 GnomeVFSResult result = gnome_vfs_result_from_errno ();
274 g_set_error (error,
275 ANJUTA_DOCUMENT_ERROR,
276 result,
277 "%s", gnome_vfs_result_to_string (result));
280 return res;
283 static void
284 save_completed_or_failed (AnjutaDocumentSaver *saver)
286 /* the object will be unrefed in the callback of the saving
287 * signal, so we need to prevent finalization.
289 g_object_ref (saver);
291 g_signal_emit (saver,
292 signals[SAVING],
294 TRUE, /* completed */
295 saver->priv->error);
297 g_object_unref (saver);
300 static gchar *
301 get_backup_filename (AnjutaDocumentSaver *saver)
303 gchar *fname;
304 gchar *bak_ext = NULL;
306 if ((saver->priv->backup_ext != NULL) &&
307 (strlen (saver->priv->backup_ext) > 0))
308 bak_ext = saver->priv->backup_ext;
309 else
310 bak_ext = g_strdup("~");
312 fname = g_strconcat (saver->priv->local_path, bak_ext, NULL);
314 /* If we are not going to keep the backup file and fname
315 * already exists, try to use another name.
316 * Change one character, just before the extension.
318 if (!saver->priv->keep_backup &&
319 g_file_test (fname, G_FILE_TEST_EXISTS))
321 gchar *wp;
323 wp = fname + strlen (fname) - 1 - strlen (bak_ext);
324 g_return_val_if_fail (wp > fname, NULL);
326 *wp = 'z';
327 while ((*wp > 'a') && g_file_test (fname, G_FILE_TEST_EXISTS))
328 --*wp;
330 /* They all exist??? Must be something wrong. */
331 if (*wp == 'a')
333 g_free (fname);
334 fname = NULL;
338 return fname;
341 /* like unlink, but doesn't fail if the file wasn't there at all */
342 static gboolean
343 remove_file (const gchar *name)
345 gint res;
347 res = unlink (name);
349 return (res == 0) || ((res == -1) && (errno == ENOENT));
352 #define BUFSIZE 8192 /* size of normal write buffer */
354 static gboolean
355 copy_file_data (gint sfd,
356 gint dfd,
357 GError **error)
359 gboolean ret = TRUE;
360 GError *err = NULL;
361 gpointer buffer;
362 const gchar *write_buffer;
363 ssize_t bytes_read;
364 ssize_t bytes_to_write;
365 ssize_t bytes_written;
367 buffer = g_malloc (BUFSIZE);
371 bytes_read = read (sfd, buffer, BUFSIZE);
372 if (bytes_read == -1)
374 GnomeVFSResult result = gnome_vfs_result_from_errno ();
376 g_set_error (&err,
377 ANJUTA_DOCUMENT_ERROR,
378 result,
379 "%s", gnome_vfs_result_to_string (result));
381 ret = FALSE;
383 break;
386 bytes_to_write = bytes_read;
387 write_buffer = buffer;
391 bytes_written = write (dfd, write_buffer, bytes_to_write);
392 if (bytes_written == -1)
394 GnomeVFSResult result = gnome_vfs_result_from_errno ();
396 g_set_error (&err,
397 ANJUTA_DOCUMENT_ERROR,
398 result,
399 "%s", gnome_vfs_result_to_string (result));
401 ret = FALSE;
403 break;
406 bytes_to_write -= bytes_written;
407 write_buffer += bytes_written;
409 while (bytes_to_write > 0);
411 } while ((bytes_read != 0) && (ret == TRUE));
413 if (error)
414 *error = err;
416 return ret;
419 /* FIXME: this is ugly for multple reasons: it refetches all the info,
420 * it doesn't use fd etc... we need something better, possibly in gnome-vfs
421 * public api.
423 static gchar *
424 get_slow_mime_type (const char *text_uri)
426 GnomeVFSFileInfo *info;
427 char *mime_type;
428 GnomeVFSResult result;
430 info = gnome_vfs_file_info_new ();
431 result = gnome_vfs_get_file_info (text_uri, info,
432 GNOME_VFS_FILE_INFO_GET_MIME_TYPE |
433 GNOME_VFS_FILE_INFO_FORCE_SLOW_MIME_TYPE |
434 GNOME_VFS_FILE_INFO_FOLLOW_LINKS);
435 if (info->mime_type == NULL || result != GNOME_VFS_OK) {
436 mime_type = NULL;
437 } else {
438 mime_type = g_strdup (info->mime_type);
440 gnome_vfs_file_info_unref (info);
442 return mime_type;
445 /* ----------- local files ----------- */
447 static gboolean
448 save_existing_local_file (AnjutaDocumentSaver *saver)
450 mode_t saved_umask;
451 struct stat statbuf;
452 struct stat new_statbuf;
453 gchar *backup_filename = NULL;
454 gboolean backup_created = FALSE;
456 if (fstat (saver->priv->fd, &statbuf) != 0)
458 GnomeVFSResult result = gnome_vfs_result_from_errno ();
460 g_set_error (&saver->priv->error,
461 ANJUTA_DOCUMENT_ERROR,
462 result,
463 "%s", gnome_vfs_result_to_string (result));
465 goto out;
468 /* not a regular file */
469 if (!S_ISREG (statbuf.st_mode))
471 if (S_ISDIR (statbuf.st_mode))
473 g_set_error (&saver->priv->error,
474 ANJUTA_DOCUMENT_ERROR,
475 GNOME_VFS_ERROR_IS_DIRECTORY,
476 "%s", gnome_vfs_result_to_string (GNOME_VFS_ERROR_IS_DIRECTORY));
478 else
480 g_set_error (&saver->priv->error,
481 ANJUTA_DOCUMENT_ERROR,
482 ANJUTA_DOCUMENT_ERROR_NOT_REGULAR_FILE,
483 "Not a regular file");
486 goto out;
489 /* check if the file is actually writable */
490 if ((statbuf.st_mode & 0222) == 0) //FIXME... check better what else vim does
492 g_set_error (&saver->priv->error,
493 ANJUTA_DOCUMENT_ERROR,
494 GNOME_VFS_ERROR_READ_ONLY,
495 "%s", gnome_vfs_result_to_string (GNOME_VFS_ERROR_READ_ONLY));
497 goto out;
500 /* check if someone else modified the file externally,
501 * except when "saving as", when saving a new doc (mtime = 0)
502 * or when the mtime check is explicitely disabled
504 if (saver->priv->doc_mtime > 0 &&
505 statbuf.st_mtime != saver->priv->doc_mtime &&
506 ((saver->priv->flags & ANJUTA_DOCUMENT_SAVE_IGNORE_MTIME) == 0))
508 g_set_error (&saver->priv->error,
509 ANJUTA_DOCUMENT_ERROR,
510 ANJUTA_DOCUMENT_ERROR_EXTERNALLY_MODIFIED,
511 "Externally modified");
513 goto out;
516 /* prepare the backup name */
517 backup_filename = get_backup_filename (saver);
518 if (backup_filename == NULL)
520 /* bad bad luck... */
521 g_warning (_("Could not obtain backup filename"));
523 g_set_error (&saver->priv->error,
524 ANJUTA_DOCUMENT_ERROR,
525 GNOME_VFS_ERROR_GENERIC,
526 "%s", gnome_vfs_result_to_string (GNOME_VFS_ERROR_GENERIC));
528 goto out;
531 /* We now use two backup strategies.
532 * The first one (which is faster) consist in saving to a
533 * tmp file then rename the original file to the backup and the
534 * tmp file to the original name. This is fast but doesn't work
535 * when the file is a link (hard or symbolic) or when we can't
536 * write to the current dir or can't set the permissions on the
537 * new file. We also do not use it when the backup is not in the
538 * current dir, since if it isn't on the same FS rename wont work.
539 * The second strategy consist simply in copying the old file
540 * to a backup file and rewrite the contents of the file.
543 if (saver->priv->backups_in_curr_dir &&
544 !(statbuf.st_nlink > 1) &&
545 !g_file_test (saver->priv->local_path, G_FILE_TEST_IS_SYMLINK))
547 gchar *dirname;
548 gchar *tmp_filename;
549 gint tmpfd;
551 dirname = g_path_get_dirname (saver->priv->local_path);
552 tmp_filename = g_build_filename (dirname, ".anjuta-save-XXXXXX", NULL);
553 g_free (dirname);
555 /* We set the umask because some (buggy) implementations
556 * of mkstemp() use permissions 0666 and we want 0600.
558 saved_umask = umask (0077);
559 tmpfd = g_mkstemp (tmp_filename);
560 umask (saved_umask);
562 if (tmpfd == -1)
564 g_free (tmp_filename);
565 goto fallback_strategy;
568 /* try to set permissions */
569 if (fchown (tmpfd, statbuf.st_uid, statbuf.st_gid) == -1 ||
570 fchmod (tmpfd, statbuf.st_mode) == -1)
572 close (tmpfd);
573 unlink (tmp_filename);
574 g_free (tmp_filename);
575 goto fallback_strategy;
578 if (!write_document_contents (tmpfd,
579 GTK_TEXT_BUFFER (saver->priv->document),
580 saver->priv->encoding,
581 &saver->priv->error))
583 close (tmpfd);
584 unlink (tmp_filename);
585 g_free (tmp_filename);
586 goto out;
589 /* original -> backup */
590 if (rename (saver->priv->local_path, backup_filename) != 0)
592 GnomeVFSResult result = gnome_vfs_result_from_errno ();
594 g_set_error (&saver->priv->error,
595 ANJUTA_DOCUMENT_ERROR,
596 result,
597 "%s", gnome_vfs_result_to_string (result));
599 close (tmpfd);
600 unlink (tmp_filename);
601 g_free (tmp_filename);
602 goto out;
605 /* tmp -> original */
606 if (rename (tmp_filename, saver->priv->local_path) != 0)
608 GnomeVFSResult result = gnome_vfs_result_from_errno ();
610 g_set_error (&saver->priv->error,
611 ANJUTA_DOCUMENT_ERROR,
612 result,
613 "%s", gnome_vfs_result_to_string (result));
615 /* try to restore... no error checking */
616 rename (backup_filename, saver->priv->local_path);
618 close (tmpfd);
619 unlink (tmp_filename);
620 g_free (tmp_filename);
621 goto out;
624 g_free (tmp_filename);
626 /* restat and get the mime type */
627 if (fstat (tmpfd, &new_statbuf) != 0)
629 GnomeVFSResult result = gnome_vfs_result_from_errno ();
631 g_set_error (&saver->priv->error,
632 ANJUTA_DOCUMENT_ERROR,
633 result,
634 "%s", gnome_vfs_result_to_string (result));
636 close (tmpfd);
637 goto out;
640 saver->priv->doc_mtime = new_statbuf.st_mtime;
642 saver->priv->mime_type = get_slow_mime_type (saver->priv->uri);
644 if (!saver->priv->keep_backup)
645 unlink (backup_filename);
647 close (tmpfd);
649 goto out;
652 fallback_strategy:
654 /* try to copy the old contents in a backup for safety
655 * unless we are explicetely told not to.
657 if ((saver->priv->flags & ANJUTA_DOCUMENT_SAVE_IGNORE_BACKUP) == 0)
659 gint bfd;
661 /* move away old backups */
662 if (!remove_file (backup_filename))
664 /* we don't care about which was the problem, just
665 * that a backup was not possible.
667 g_set_error (&saver->priv->error,
668 ANJUTA_DOCUMENT_ERROR,
669 ANJUTA_DOCUMENT_ERROR_CANT_CREATE_BACKUP,
670 "No backup created");
672 goto out;
675 bfd = open (backup_filename,
676 O_WRONLY | O_CREAT | O_EXCL,
677 statbuf.st_mode & 0777);
679 if (bfd == -1)
681 g_set_error (&saver->priv->error,
682 ANJUTA_DOCUMENT_ERROR,
683 ANJUTA_DOCUMENT_ERROR_CANT_CREATE_BACKUP,
684 "No backup created");
686 goto out;
689 /* Try to set the group of the backup same as the
690 * original file. If this fails, set the protection
691 * bits for the group same as the protection bits for
692 * others. */
693 if (fchown (bfd, (uid_t) -1, statbuf.st_gid) != 0)
695 if (fchmod (bfd,
696 (statbuf.st_mode& 0707) |
697 ((statbuf.st_mode & 07) << 3)) != 0)
699 g_set_error (&saver->priv->error,
700 ANJUTA_DOCUMENT_ERROR,
701 ANJUTA_DOCUMENT_ERROR_CANT_CREATE_BACKUP,
702 "No backup created");
704 unlink (backup_filename);
705 close (bfd);
707 goto out;
711 if (!copy_file_data (saver->priv->fd, bfd, NULL))
713 g_set_error (&saver->priv->error,
714 ANJUTA_DOCUMENT_ERROR,
715 ANJUTA_DOCUMENT_ERROR_CANT_CREATE_BACKUP,
716 "No backup created");
718 unlink (backup_filename);
719 close (bfd);
721 goto out;
724 backup_created = TRUE;
725 close (bfd);
728 /* finally overwrite the original */
729 if (!write_document_contents (saver->priv->fd,
730 GTK_TEXT_BUFFER (saver->priv->document),
731 saver->priv->encoding,
732 &saver->priv->error))
734 goto out;
737 /* remove the backup if we don't want to keep it */
738 if (backup_created && !saver->priv->keep_backup)
740 unlink (backup_filename);
743 /* re stat the file and refetch the mime type */
744 if (fstat (saver->priv->fd, &new_statbuf) != 0)
746 GnomeVFSResult result = gnome_vfs_result_from_errno ();
748 g_set_error (&saver->priv->error,
749 ANJUTA_DOCUMENT_ERROR,
750 result,
751 "%s", gnome_vfs_result_to_string (result));
753 goto out;
756 saver->priv->doc_mtime = new_statbuf.st_mtime;
758 g_free (saver->priv->mime_type);
759 saver->priv->mime_type = get_slow_mime_type (saver->priv->uri);
761 out:
762 if (close (saver->priv->fd))
763 g_warning ("File '%s' has not been correctly closed: %s",
764 saver->priv->uri,
765 strerror (errno));
766 saver->priv->fd = -1;
768 g_free (backup_filename);
770 save_completed_or_failed (saver);
772 /* stop the timeout */
773 return FALSE;
776 static gboolean
777 save_new_local_file (AnjutaDocumentSaver *saver)
779 struct stat statbuf;
781 if (!write_document_contents (saver->priv->fd,
782 GTK_TEXT_BUFFER (saver->priv->document),
783 saver->priv->encoding,
784 &saver->priv->error))
786 goto out;
789 /* stat the file and fetch the mime type */
790 if (fstat (saver->priv->fd, &statbuf) != 0)
792 GnomeVFSResult result = gnome_vfs_result_from_errno ();
794 g_set_error (&saver->priv->error,
795 ANJUTA_DOCUMENT_ERROR,
796 result,
797 "%s", gnome_vfs_result_to_string (result));
799 goto out;
802 saver->priv->doc_mtime = statbuf.st_mtime;
804 g_free (saver->priv->mime_type);
805 saver->priv->mime_type = get_slow_mime_type (saver->priv->uri);
807 out:
808 if (close (saver->priv->fd))
809 g_warning ("File '%s' has not been correctly closed: %s",
810 saver->priv->uri,
811 strerror (errno));
813 saver->priv->fd = -1;
815 save_completed_or_failed (saver);
817 /* stop the timeout */
818 return FALSE;
821 static gboolean
822 open_local_failed (AnjutaDocumentSaver *saver)
824 save_completed_or_failed (saver);
826 /* stop the timeout */
827 return FALSE;
830 static void
831 save_local_file (AnjutaDocumentSaver *saver)
833 GSourceFunc next_phase;
834 GnomeVFSResult result;
836 /* saving start */
837 g_signal_emit (saver,
838 signals[SAVING],
840 FALSE,
841 NULL);
843 /* the file doesn't exist, create it */
844 saver->priv->fd = open (saver->priv->local_path,
845 O_CREAT | O_EXCL | O_WRONLY,
846 0666);
847 if (saver->priv->fd != -1)
849 next_phase = (GSourceFunc) save_new_local_file;
850 goto out;
853 /* the file already exist */
854 else if (errno == EEXIST)
856 saver->priv->fd = open (saver->priv->local_path, O_RDWR);
857 if (saver->priv->fd != -1)
859 next_phase = (GSourceFunc) save_existing_local_file;
860 goto out;
864 /* else error */
865 result = gnome_vfs_result_from_errno (); //may it happen that no errno?
867 g_set_error (&saver->priv->error,
868 ANJUTA_DOCUMENT_ERROR,
869 result,
870 "%s", gnome_vfs_result_to_string (result));
872 next_phase = (GSourceFunc) open_local_failed;
874 out:
875 g_timeout_add_full (G_PRIORITY_HIGH,
877 next_phase,
878 saver,
879 NULL);
882 /* ----------- remote files ----------- */
884 static void
885 remote_save_completed_or_failed (AnjutaDocumentSaver *saver)
887 /* we can now close and unlink the tmp file */
888 close (saver->priv->tmpfd);
889 unlink (saver->priv->tmp_fname);
891 save_completed_or_failed (saver);
894 static void
895 remote_get_info_cb (GnomeVFSAsyncHandle *handle,
896 GList *results,
897 gpointer data)
899 AnjutaDocumentSaver *saver = ANJUTA_DOCUMENT_SAVER (data);
900 GnomeVFSGetFileInfoResult *info_result;
902 /* assert that the list has one and only one item */
903 g_return_if_fail (results != NULL && results->next == NULL);
905 info_result = (GnomeVFSGetFileInfoResult *) results->data;
906 g_return_if_fail (info_result != NULL);
908 if (info_result->result != GNOME_VFS_OK)
910 g_set_error (&saver->priv->error,
911 ANJUTA_DOCUMENT_ERROR,
912 info_result->result,
913 "%s", gnome_vfs_result_to_string (info_result->result));
915 remote_save_completed_or_failed (saver);
917 return;
920 if (info_result->file_info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_MTIME)
921 saver->priv->doc_mtime = info_result->file_info->mtime;
923 if (info_result->file_info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE)
925 g_free (saver->priv->mime_type);
926 saver->priv->mime_type = g_strdup (info_result->file_info->mime_type);
929 remote_save_completed_or_failed (saver);
932 static gint
933 async_xfer_ok (GnomeVFSXferProgressInfo *progress_info,
934 AnjutaDocumentSaver *saver)
936 switch (progress_info->phase)
938 case GNOME_VFS_XFER_PHASE_INITIAL:
939 break;
940 case GNOME_VFS_XFER_CHECKING_DESTINATION:
942 GnomeVFSFileInfo *orig_info;
943 GnomeVFSResult res;
945 /* we need to retrieve info ourselves too, since xfer
946 * doesn't allow to access it :(
947 * If that was not enough we need to do it sync or we are going
948 * to mess everything up
950 orig_info = gnome_vfs_file_info_new ();
951 res = gnome_vfs_get_file_info_uri (saver->priv->vfs_uri,
952 orig_info,
953 GNOME_VFS_FILE_INFO_DEFAULT |
954 GNOME_VFS_FILE_INFO_FOLLOW_LINKS);
956 if (res == GNOME_VFS_ERROR_NOT_FOUND)
958 /* ok, we are not overwriting, go on with the xfer */
959 break;
962 if (res != GNOME_VFS_OK)
964 // CHECK: do we want to ignore the error and try to go on anyway?
965 g_set_error (&saver->priv->error,
966 ANJUTA_DOCUMENT_ERROR,
967 res,
968 "%s", gnome_vfs_result_to_string (res));
970 /* abort xfer */
971 return 0;
975 /* check if someone else modified the file externally,
976 * except when "saving as", when saving a new doc (mtime = 0)
977 * or when the mtime check is explicitely disabled
979 if (orig_info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_MTIME)
981 if (saver->priv->doc_mtime > 0 &&
982 orig_info->mtime != saver->priv->doc_mtime &&
983 ((saver->priv->flags & ANJUTA_DOCUMENT_SAVE_IGNORE_MTIME) == 0))
985 g_set_error (&saver->priv->error,
986 ANJUTA_DOCUMENT_ERROR,
987 ANJUTA_DOCUMENT_ERROR_EXTERNALLY_MODIFIED,
988 "Externally modified");
990 /* abort xfer */
991 return 0;
995 /* store the original file info, so that we can restore permissions */
996 // FIXME: what about the case where we are usin "Save as" but overwriting a file... we don't want to restore perms
997 if (orig_info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS)
998 saver->priv->orig_info = orig_info;
1000 break;
1001 case GNOME_VFS_XFER_PHASE_COLLECTING:
1002 case GNOME_VFS_XFER_PHASE_DELETESOURCE: // why do we get this phase??
1003 break;
1004 case GNOME_VFS_XFER_PHASE_READYTOGO:
1005 saver->priv->size = progress_info->bytes_total;
1006 break;
1007 case GNOME_VFS_XFER_PHASE_OPENSOURCE:
1008 case GNOME_VFS_XFER_PHASE_OPENTARGET:
1009 case GNOME_VFS_XFER_PHASE_COPYING:
1010 case GNOME_VFS_XFER_PHASE_WRITETARGET:
1011 case GNOME_VFS_XFER_PHASE_CLOSETARGET:
1012 if (progress_info->bytes_copied > 0)
1013 saver->priv->bytes_written = MIN (progress_info->total_bytes_copied,
1014 progress_info->bytes_total);
1015 break;
1016 case GNOME_VFS_XFER_PHASE_FILECOMPLETED:
1017 case GNOME_VFS_XFER_PHASE_CLEANUP:
1018 break;
1019 case GNOME_VFS_XFER_PHASE_COMPLETED:
1020 /* Transfer done!
1021 * Restore the permissions if needed and then refetch
1022 * info on our newly written file to get the mime etc */
1024 GList *uri_list = NULL;
1026 /* Try is not as paranoid as the local version (GID)... it would take
1027 * yet another stat to do it...
1029 if (saver->priv->orig_info != NULL &&
1030 (saver->priv->orig_info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS))
1032 gnome_vfs_set_file_info_uri (saver->priv->vfs_uri,
1033 saver->priv->orig_info,
1034 GNOME_VFS_SET_FILE_INFO_PERMISSIONS);
1036 // FIXME: for now is a blind try... do we want to error check?
1039 uri_list = g_list_prepend (uri_list, saver->priv->vfs_uri);
1041 gnome_vfs_async_get_file_info (&saver->priv->info_handle,
1042 uri_list,
1043 GNOME_VFS_FILE_INFO_DEFAULT |
1044 GNOME_VFS_FILE_INFO_GET_MIME_TYPE |
1045 GNOME_VFS_FILE_INFO_FORCE_SLOW_MIME_TYPE |
1046 GNOME_VFS_FILE_INFO_FOLLOW_LINKS,
1047 GNOME_VFS_PRIORITY_MAX,
1048 remote_get_info_cb,
1049 saver);
1050 g_list_free (uri_list);
1052 break;
1053 /* Phases we don't expect to see */
1054 case GNOME_VFS_XFER_PHASE_SETATTRIBUTES:
1055 case GNOME_VFS_XFER_PHASE_CLOSESOURCE:
1056 case GNOME_VFS_XFER_PHASE_MOVING:
1057 case GNOME_VFS_XFER_PHASE_READSOURCE:
1058 default:
1059 g_return_val_if_reached (0);
1062 /* signal the progress */
1063 g_signal_emit (saver,
1064 signals[SAVING],
1066 FALSE,
1067 NULL);
1069 return 1;
1072 static gint
1073 async_xfer_error (GnomeVFSXferProgressInfo *progress_info,
1074 AnjutaDocumentSaver *saver)
1076 g_set_error (&saver->priv->error,
1077 ANJUTA_DOCUMENT_ERROR,
1078 progress_info->vfs_status,
1079 "%s", gnome_vfs_result_to_string (progress_info->vfs_status));
1081 remote_save_completed_or_failed (saver);
1083 return GNOME_VFS_XFER_ERROR_ACTION_ABORT;
1086 static gint
1087 async_xfer_progress (GnomeVFSAsyncHandle *handle,
1088 GnomeVFSXferProgressInfo *progress_info,
1089 gpointer data)
1091 AnjutaDocumentSaver *saver = ANJUTA_DOCUMENT_SAVER (data);
1093 switch (progress_info->status)
1095 case GNOME_VFS_XFER_PROGRESS_STATUS_OK:
1096 return async_xfer_ok (progress_info, saver);
1097 case GNOME_VFS_XFER_PROGRESS_STATUS_VFSERROR:
1098 return async_xfer_error (progress_info, saver);
1100 /* we should never go in these */
1101 case GNOME_VFS_XFER_PROGRESS_STATUS_OVERWRITE:
1102 case GNOME_VFS_XFER_PROGRESS_STATUS_DUPLICATE:
1103 default:
1104 g_return_val_if_reached (0);
1108 static gboolean
1109 save_remote_file_real (AnjutaDocumentSaver *saver)
1111 mode_t saved_umask;
1112 gchar *tmp_uri;
1113 GnomeVFSURI *tmp_vfs_uri;
1114 GList *source_uri_list = NULL;
1115 GList *dest_uri_list = NULL;
1116 GnomeVFSResult result;
1118 /* For remote files we use the following strategy:
1119 * we save to a local temp file and then transfer it
1120 * over to the requested location asyncronously.
1121 * There is no backup of the original remote file.
1124 /* We set the umask because some (buggy) implementations
1125 * of mkstemp() use permissions 0666 and we want 0600.
1127 saved_umask = umask (0077);
1128 saver->priv->tmpfd = g_file_open_tmp (".anjuta-save-XXXXXX",
1129 &saver->priv->tmp_fname,
1130 &saver->priv->error);
1131 umask (saved_umask);
1133 if (saver->priv->tmpfd == -1)
1135 GnomeVFSResult result = gnome_vfs_result_from_errno ();
1137 g_set_error (&saver->priv->error,
1138 ANJUTA_DOCUMENT_ERROR,
1139 result,
1140 "%s", gnome_vfs_result_to_string (result));
1142 /* in this case no need to close the tmp file */
1143 save_completed_or_failed (saver);
1145 return FALSE;
1148 tmp_uri = g_filename_to_uri (saver->priv->tmp_fname,
1149 NULL,
1150 &saver->priv->error);
1151 if (tmp_uri == NULL)
1153 goto error;
1156 tmp_vfs_uri = gnome_vfs_uri_new (tmp_uri);
1157 //needs error checking?
1159 g_free (tmp_uri);
1161 source_uri_list = g_list_prepend (source_uri_list, tmp_vfs_uri);
1162 dest_uri_list = g_list_prepend (dest_uri_list, saver->priv->vfs_uri);
1164 if (!write_document_contents (saver->priv->tmpfd,
1165 GTK_TEXT_BUFFER (saver->priv->document),
1166 saver->priv->encoding,
1167 &saver->priv->error))
1169 goto error;
1172 result = gnome_vfs_async_xfer (&saver->priv->handle,
1173 source_uri_list,
1174 dest_uri_list,
1175 GNOME_VFS_XFER_DEFAULT | GNOME_VFS_XFER_TARGET_DEFAULT_PERMS, // CHECK needs more thinking, follow symlinks etc... options are undocumented :(
1176 GNOME_VFS_XFER_ERROR_MODE_ABORT, /* keep it simple, abort on any error */
1177 GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE, /* We have already asked confirm (even if it is racy) */
1178 GNOME_VFS_PRIORITY_DEFAULT,
1179 async_xfer_progress, saver,
1180 NULL, NULL);
1182 gnome_vfs_uri_unref (tmp_vfs_uri);
1183 g_list_free (source_uri_list);
1184 g_list_free (dest_uri_list);
1186 if (result != GNOME_VFS_OK)
1188 g_set_error (&saver->priv->error,
1189 ANJUTA_DOCUMENT_ERROR,
1190 result,
1191 "%s", gnome_vfs_result_to_string (result));
1193 goto error;
1196 /* No errors: stop the timeout */
1197 return FALSE;
1199 error:
1200 remote_save_completed_or_failed (saver);
1202 /* stop the timeout */
1203 return FALSE;
1206 static void
1207 save_remote_file (AnjutaDocumentSaver *saver)
1209 /* saving start */
1210 g_signal_emit (saver,
1211 signals[SAVING],
1213 FALSE,
1214 NULL);
1216 g_timeout_add_full (G_PRIORITY_HIGH,
1218 (GSourceFunc) save_remote_file_real,
1219 saver,
1220 NULL);
1223 /* ---------- public api ---------- */
1225 void
1226 anjuta_document_saver_save (AnjutaDocumentSaver *saver,
1227 const gchar *uri,
1228 const AnjutaEncoding *encoding,
1229 time_t oldmtime,
1230 AnjutaDocumentSaveFlags flags)
1232 gchar *local_path;
1234 g_return_if_fail (ANJUTA_IS_DOCUMENT_SAVER (saver));
1235 g_return_if_fail ((uri != NULL) && (strlen (uri) > 0));
1237 // CHECK:
1238 // - sanity check a max len for the uri?
1239 // report async (in an idle handler) or sync (bool ret)
1240 // async is extra work here, sync is special casing in the caller
1242 saver->priv->uri = g_strdup (uri);
1244 /* fetch saving options */
1245 saver->priv->backup_ext = g_strdup("~");
1247 /* never keep backup of autosaves */
1248 if ((flags & ANJUTA_DOCUMENT_SAVE_PRESERVE_BACKUP) != 0)
1249 saver->priv->keep_backup = FALSE;
1250 else
1251 /* FIXME: This should be configurable */
1252 saver->priv->keep_backup =TRUE;
1254 /* TODO: add support for configurable backup dir */
1255 saver->priv->backups_in_curr_dir = TRUE;
1257 if (encoding != NULL)
1258 saver->priv->encoding = encoding;
1259 else
1260 saver->priv->encoding = anjuta_encoding_get_utf8 ();
1262 saver->priv->doc_mtime = oldmtime;
1264 saver->priv->flags = flags;
1266 local_path = gnome_vfs_get_local_path_from_uri (uri);
1267 if (local_path != NULL)
1269 saver->priv->local_path = local_path;
1270 save_local_file (saver);
1272 else
1274 saver->priv->vfs_uri = gnome_vfs_uri_new (uri);
1275 save_remote_file (saver);
1279 const gchar *
1280 anjuta_document_saver_get_uri (AnjutaDocumentSaver *saver)
1282 g_return_val_if_fail (ANJUTA_IS_DOCUMENT_SAVER (saver), NULL);
1284 return saver->priv->uri;
1287 const gchar *
1288 anjuta_document_saver_get_mime_type (AnjutaDocumentSaver *saver)
1290 g_return_val_if_fail (ANJUTA_IS_DOCUMENT_SAVER (saver), NULL);
1292 return saver->priv->mime_type;
1295 time_t
1296 anjuta_document_saver_get_mtime (AnjutaDocumentSaver *saver)
1298 g_return_val_if_fail (ANJUTA_IS_DOCUMENT_SAVER (saver), 0);
1300 return saver->priv->doc_mtime;
1303 /* Returns 0 if file size is unknown */
1304 GnomeVFSFileSize
1305 anjuta_document_saver_get_file_size (AnjutaDocumentSaver *saver)
1307 g_return_val_if_fail (ANJUTA_IS_DOCUMENT_SAVER (saver), 0);
1309 return saver->priv->size;
1312 GnomeVFSFileSize
1313 anjuta_document_saver_get_bytes_written (AnjutaDocumentSaver *saver)
1315 g_return_val_if_fail (ANJUTA_IS_DOCUMENT_SAVER (saver), 0);
1317 return saver->priv->bytes_written;