Just fixed a few things that I thought about last night
[backups/.git] / main.cc
1 #include <iostream>
2 #include <iterator>
3 #include <vector>
4 #include <algorithm>
5 #include <cassert>
6
7 #include <sqlite3.h>
8
9 #include "filedata.hpp"
10
11 using namespace std;
12
13 vector<string> split( const string &line, char c, int limit = -1 ) {
14   string::size_type start = 0, end = 0;
15
16   vector<string> out;
17   while( 0 != limit-- && end != line.size() ) {
18     if( 0 == limit ) {
19       end = line.size();
20     } else {
21       end = line.find( c, start );
22       if( end == string::npos ) {
23         end = line.size();
24       }
25     }
26     out.push_back( line.substr( start, end-start ) );
27     start = end + 1;
28   }
29   return out;
30 }
31
32 // Callback function for getting files from the database
33 int populate_set( void *files_v, int, char **vals, char ** ) {
34   file_set *files = reinterpret_cast<file_set*>( files_v );
35   files->insert( new FileData( vals[0][0],
36         vals[1],
37         vals[2],
38         vals[3],
39         atoi( vals[4] ),
40         atoi( vals[5] ),
41         vals[6]) );
42   return 0;
43 }
44
45 int main() {
46   string file_string;
47
48   file_set current;
49
50   // Parse the list of files on stdin
51   do {
52     file_string.clear();
53     for( int c = cin.get(); 0 != c && ! cin.eof(); c = cin.get() ) {
54       file_string.push_back( c );
55     }
56     if( 0 != file_string.size() ) {
57       // Example entry
58       // type perms user group size datemodified name (7 total)
59       // f 0600 cnb cnb 424 20051015205340 ./.git/index
60       vector<string> vals = split( file_string, ' ', 7 );
61       current.insert( new FileData( vals[0][0],
62             vals[1],
63             vals[2],
64             vals[3],
65             atoi( vals[4].c_str() ),
66             atoi( vals[5].c_str() ),
67             vals[6]) );
68     }
69   } while( ! cin.eof() );
70
71   // Get the list of previously backed up files from the database.
72   sqlite3 *db;
73
74   const char *dbname = "test.db";
75   int rc = sqlite3_open( dbname, &db );
76   assert( SQLITE_OK == rc );
77
78   char *sqliteErrMsg = 0;
79   file_set backed_up;
80   rc = sqlite3_exec( db, "select * from filedata;", populate_set, &backed_up, &sqliteErrMsg );
81   assert( SQLITE_OK == rc );
82
83   rc = sqlite3_close( db );
84   assert( SQLITE_OK == rc );
85
86   // Now divide the two sets into three sets (new, deleted and updated )
87   FileDataPtrCmp cmp;
88
89   file_set added;
90   set_difference( current.begin(),   current.end(),
91                   backed_up.begin(), backed_up.end(),
92                   inserter( added, added.begin() ),
93                   cmp );
94
95   file_set deleted;
96   set_difference( backed_up.begin(), backed_up.end(),
97                   current.begin(),   current.end(),
98                   inserter( deleted, deleted.begin() ),
99                   cmp );
100
101   file_set updated;
102   set_union( current.begin(),   current.end(),
103              backed_up.begin(), backed_up.end(),
104              inserter( updated, updated.begin() ),
105              cmp );
106
107   { // This little block will copy the last_backup_date from the second set to the first
108     file_set updated_mirror;
109     set_union( current.begin(),   current.end(),
110                backed_up.begin(), backed_up.end(),
111                inserter( updated_mirror, updated_mirror.begin() ),
112                cmp );
113
114     // TODO Now we need to copy the last_backup_date from 
115     file_set::iterator i = updated.begin(), j = updated_mirror.begin();
116     for( ; i != updated.end(); ++i, ++j ) {
117       (*i)->setLastBackupDate( (*j)->getLastBackupDate() );
118     }
119   }
120
121   // Now find the list of files to backup.
122   file_set backup_list;
123
124   // backup all new files
125   copy( added.begin(), added.end(), inserter( backup_list, backup_list.begin() ) );
126
127   // backup already backed-up files that have changed since the last backup date.
128   for( file_set::iterator i = updated.begin(); i != updated.end(); ++i ) {
129     if( (*i)->getLastBackupDate() < (*i)->getModifiedDate() ) {
130       backup_list.insert( *i );
131     }
132   }
133
134   // Now, sort the backup_list by filesize and build a list of up to SIZE
135
136   // Now, sort the non-backed-up list my last_backup_date and back-fill
137
138   // Remove deleted files from the database.
139   // TODO CNB You were working in here.  Actually, just delete all records in
140   // the database and re-populate with the current list.
141   const char *delete_sql = "delete from filedata where filename = :filename";
142   sqlite3_stmt *ppStmt;
143
144   sqlite3_prepare( db, delete_sql, -1, &ppStmt, NULL );
145   assert( NULL != ppStmt );
146
147   for( file_set::iterator i = deleted.begin(); i != deleted.end(); ++i ) {
148     const string &name = (*i)->getFileName();
149
150     rc = sqlite3_bind_text( ppStmt, 1, name.data(), name.size(), SQLITE_TRANSIENT );
151     assert( SQLITE_OK == rc );
152
153     rc = sqlite3_step( ppStmt );
154     assert( SQLITE_OK == rc );
155   }
156   rc = sqlite3_finalize( ppStmt );
157   assert( SQLITE_OK == rc );
158
159   // Now, use the current set to update values in the database.  You should copy
160   // last_backup_date dates from the backed_up set first.  This will get all of
161   // the latest permissions but include the last_backup_date.
162
163   // Now, update the last_backup_date for all of the files that are in the list
164
165   // Clean-up
166   for( file_set::iterator i = backed_up.begin(); i != backed_up.end(); ++i ) { delete *i; }
167   for( file_set::iterator i = current.begin();   i != current.end();   ++i ) { delete *i; }
168 }