more updates
[synfig.git] / synfig-studio / trunk / src / gtkmm / autorecover.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file autorecover.cpp
3 **      \brief Template File
4 **
5 **      $Id: autorecover.cpp,v 1.1.1.1 2005/01/07 03:34:35 darco Exp $
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **
10 **      This package is free software; you can redistribute it and/or
11 **      modify it under the terms of the GNU General Public License as
12 **      published by the Free Software Foundation; either version 2 of
13 **      the License, or (at your option) any later version.
14 **
15 **      This package is distributed in the hope that it will be useful,
16 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
17 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 **      General Public License for more details.
19 **      \endlegal
20 */
21 /* ========================================================================= */
22
23 /* === H E A D E R S ======================================================= */
24
25 #ifdef USING_PCH
26 #       include "pch.h"
27 #else
28 #ifdef HAVE_CONFIG_H
29 #       include <config.h>
30 #endif
31
32 #include "autorecover.h"
33
34 //#include <unistd.h>
35 #include "app.h"
36 #include <synfig/savecanvas.h>
37 #include <synfig/loadcanvas.h>
38 #include <fstream>
39 #include <iostream>
40 #include "instance.h"
41
42 #ifdef HAVE_SYS_STAT_H
43 #include <sys/stat.h>
44 #endif
45
46 #ifdef HAVE_SYS_TIME_H
47 #include <sys/time.h>
48 #endif
49
50 #ifdef HAVE_SYS_WAIT_H
51 #include <sys/wait.h>
52 #endif
53
54 #ifdef HAVE_SYS_RESOURCE_H
55 #include <sys/resource.h>
56 #endif
57
58 #ifdef HAVE_UNISTD_H
59 #include <unistd.h>
60 #endif
61
62 #endif
63
64 /* === U S I N G =========================================================== */
65
66 using namespace std;
67 using namespace etl;
68 using namespace synfig;
69 using namespace studio;
70
71 /* === M A C R O S ========================================================= */
72
73 #ifdef _WIN32
74 #define mkdir(x,y) mkdir(x)
75 #endif
76
77 /* === G L O B A L S ======================================================= */
78
79 /* === P R O C E D U R E S ================================================= */
80
81 /* === M E T H O D S ======================================================= */
82
83 AutoRecover::AutoRecover()
84 {
85         // Three Minutes
86         set_timeout(3*60*1000);
87         
88         if(mkdir(get_shadow_directory().c_str(),ACCESSPERMS)<0)
89         {
90                 if(errno!=EEXIST)
91                         synfig::error("UNABLE TO CREATE \"%s\"",get_shadow_directory().c_str());
92         }
93         else
94         {
95                 synfig::info("Created directory \"%s\"",get_shadow_directory().c_str());
96         }
97 }
98
99 AutoRecover::~AutoRecover()
100 {
101 }
102
103 synfig::String
104 AutoRecover::get_shadow_directory()
105 {
106         return Glib::build_filename(App::get_user_app_directory(),"tmp");
107 }
108
109 int
110 AutoRecover::pid()
111 {
112 //      return getpid();
113         return 0;
114 }
115
116 void
117 AutoRecover::set_timeout(int milliseconds)
118 {
119         timeout=milliseconds;
120         auto_backup_connect.disconnect();
121         if(timeout)
122                 auto_backup_connect=Glib::signal_timeout().connect(sigc::ptr_fun(&AutoRecover::auto_backup),timeout);
123 //              auto_backup_connect=App::main.get_context()->signal_timeout().connect(sigc::mem_fun(&AutoRecover::auto_backup),timeout);
124 }
125
126 synfig::String
127 AutoRecover::get_shadow_file_name(const synfig::String& filename)
128 {
129         unsigned int hash1(0xdeadbeef);
130         unsigned int hash2(0x83502529);
131         char* str_hash1(reinterpret_cast<char*>(&hash1));
132         char* str_hash2(reinterpret_cast<char*>(&hash2));
133         
134         // First we need to hash up the directory
135         {
136                 String pool(dirname(filename));
137                 
138                 while(pool.size()>4)
139                 {
140                         str_hash1[0]^=pool[1];str_hash1[1]^=pool[2];str_hash1[2]^=pool[3];str_hash1[3]^=pool[0];
141                         str_hash2[3]+=pool[0];str_hash2[2]+=pool[1];str_hash2[1]+=pool[2];str_hash2[0]+=pool[3];
142                         swap(hash1,hash2);
143                         pool=String(pool,4,pool.size());
144                 }
145                 while(pool.size())
146                 {
147                         str_hash1[0]^=pool[0];
148                         str_hash1[2]^=pool[0];
149                         str_hash2[1]^=pool[0];
150                         str_hash2[3]^=pool[0];
151                         swap(hash1,hash2);
152                         pool=String(pool,1,pool.size());
153                 }
154         }
155         hash1^=hash2;
156         
157         return Glib::build_filename(get_shadow_directory(),strprintf("%08X-%s",hash1,basename(filename).c_str()));
158         
159 //      return dirname(filename) + ETL_DIRECTORY_SEPERATOR + ".shadow_" + basename(filename);
160 }
161
162 bool
163 AutoRecover::cleanup_pid(int pid)
164 {
165 #ifdef HAVE_FORK
166         int status=0;
167         if(waitpid(pid,&status,WNOHANG)==-1)
168         {
169                 synfig::info("PID %d isn't a zombie yet",pid);
170                 return true;
171         }
172         if(WEXITSTATUS(status)!=0)
173         {
174                 synfig::error("Autobackup seems to have failed! (PID=%d)",pid);
175         }
176         else
177                 synfig::info("PID=%d has been cleaned up",pid);
178 #endif
179         return false;
180 }
181
182 bool
183 AutoRecover::auto_backup()
184 {
185         int pid(0);
186
187 #ifdef HAVE_FORK
188         pid=fork();
189 #endif
190         
191         if(pid<=0)
192         {
193 #ifdef HAVE_SETPRIORITY
194                 // make us low priority so that we don't
195                 // cause the machine to slow down too much
196                 setpriority(PRIO_PROCESS,0,15);
197 #endif
198                 
199                 try
200                 {
201                         std::list<etl::handle<Instance> >::iterator iter;
202         
203                         std::string filename=App::get_config_file("autorecovery");
204                         std::ofstream file(filename.c_str());
205                         
206                         int savecount(0);
207                         
208                         for(iter=App::instance_list.begin();iter!=App::instance_list.end();++iter)
209                         {
210                                 // If this file hasn't even been changed
211                                 // since it was last saved, then don't bother
212                                 // backing it up.
213                                 if((*iter)->get_action_count()==0)
214                                         continue;
215                                         
216                                 Canvas::Handle canvas((*iter)->get_canvas());
217                                 file<<canvas->get_file_name()<<endl;
218                                 save_canvas(get_shadow_file_name(canvas->get_file_name()),canvas);
219                                 savecount++;
220                         }
221                         
222                         if(savecount)
223                                 synfig::info("AutoRecover::auto_backup(): %d Files backed up.",savecount);
224                 }
225                 catch(...)
226                 {
227                         synfig::error("AutoRecover::auto_backup(): UNKNOWN EXCEPTION THROWN.");
228                         synfig::error("AutoRecover::auto_backup(): FILES NOT BACKED UP.");
229                 }
230                 
231 #ifdef HAVE_FORK
232                 if(pid==0)
233                 {
234                         _exit(0);
235                 }
236 #endif
237         }
238
239 #ifdef HAVE_FORK
240         Glib::signal_timeout().connect(
241                 sigc::bind(
242                         sigc::ptr_fun(&AutoRecover::cleanup_pid),
243                         pid
244                 ),
245                 60*1000
246         );
247 #endif  
248         
249         // Also go ahead and save the settings
250         App::save_settings();
251         
252         return true;
253 }
254
255 bool
256 AutoRecover::recovery_needed()const
257 {
258         std::string filename=App::get_config_file("autorecovery");
259         std::ifstream file(filename.c_str());
260         if(!file)
261                 return false;
262
263         while(file)
264         {
265                 std::string filename;
266                 getline(file,filename);
267                 if(!filename.empty())
268                         return true;
269         }
270         
271         return false;
272 }
273
274 bool
275 AutoRecover::recover()
276 {
277         std::string filename=App::get_config_file("autorecovery");
278         std::ifstream file(filename.c_str());
279         if(!file)
280                 return false;
281         bool success=true;
282         
283         while(file)
284         {
285                 std::string filename;
286                 getline(file,filename);
287                 if(filename.empty())
288                         continue;
289
290                 // Open the file
291                 if(App::open_as(get_shadow_file_name(filename),filename))
292                 {
293                         // Correct the file name
294                         App::instance_list.back()->set_file_name(filename);
295                         
296                         // This file isn't saved! mark it as such
297                         App::instance_list.back()->inc_action_count();
298                 }
299                 else
300                         success=false;
301         }
302
303         return success;
304 }
305
306 void
307 AutoRecover::normal_shutdown()
308 {
309         // Turn off the timer
310         auto_backup_connect.disconnect();
311
312         std::string filename=App::get_config_file("autorecovery");
313         remove(filename.c_str());
314 }
315
316 void
317 AutoRecover::clear_backup(synfig::Canvas::Handle canvas)
318 {
319         if(canvas)
320                 remove(get_shadow_file_name(canvas->get_file_name()).c_str());
321 }