Line data Source code
1 : /* File: io_data_file.c; Copyright and License: see below */
2 :
3 : #include "io_data_file.h"
4 : #include "io_exporter.h"
5 : #include "io_file_format.h"
6 : #include "io_importer.h"
7 : #include "io_import_mode.h"
8 : #include "entity/data_head.h"
9 : #include "entity/data_head_key.h"
10 : #include "storage/data_database_head.h"
11 : #include "u8dir/u8dir_file.h"
12 : #include "u8stream/universal_file_input_stream.h"
13 : #include "u8stream/universal_null_output_stream.h"
14 : #include "u8stream/universal_output_stream.h"
15 : #include "u8/u8_trace.h"
16 : #include "u8/u8_log.h"
17 : #include "u8/u8_u64.h"
18 : #include <assert.h>
19 :
20 : static const char *IO_DATA_FILE_TEMP_EXT = ".tmp-cfu";
21 : static const char *IO_DATA_FILE_JSON_EXT = ".cfuJ";
22 :
23 3 : void io_data_file_init ( io_data_file_t *this_ )
24 : {
25 3 : U8_TRACE_BEGIN();
26 :
27 3 : data_database_init( &((*this_).database) );
28 3 : ctrl_controller_init( &((*this_).controller), &((*this_).database) );
29 :
30 : (*this_).json_file_name
31 3 : = utf8stringbuf_new( (*this_).private_json_file_name_buffer, sizeof((*this_).private_json_file_name_buffer) );
32 3 : utf8stringbuf_clear( &((*this_).json_file_name) );
33 :
34 : (*this_).db_file_name
35 3 : = utf8stringbuf_new( (*this_).private_db_file_name_buffer, sizeof((*this_).private_db_file_name_buffer) );
36 3 : utf8stringbuf_clear( &((*this_).db_file_name) );
37 :
38 3 : (*this_).auto_writeback_to_json = false;
39 3 : (*this_).delete_db_when_finished = false;
40 3 : (*this_).sync_revision = DATA_REVISION_VOID;
41 :
42 3 : U8_TRACE_END();
43 3 : }
44 :
45 3 : void io_data_file_destroy ( io_data_file_t *this_ )
46 : {
47 3 : U8_TRACE_BEGIN();
48 :
49 3 : ctrl_controller_destroy( &((*this_).controller) );
50 3 : data_database_destroy( &((*this_).database) );
51 :
52 3 : U8_TRACE_END();
53 3 : }
54 :
55 4 : u8_error_t io_data_file_open ( io_data_file_t *this_,
56 : const char* requested_file_path,
57 : bool read_only,
58 : data_stat_t *io_stat,
59 : u8_error_info_t *out_err_info )
60 : {
61 4 : U8_TRACE_BEGIN();
62 4 : assert( requested_file_path != NULL );
63 4 : assert( io_stat != NULL );
64 4 : assert( out_err_info != NULL );
65 4 : u8_error_info_init_void( out_err_info );
66 4 : const utf8stringview_t req_file_path = UTF8STRINGVIEW_STR(requested_file_path);
67 : utf8stringview_t req_file_parent;
68 : utf8stringview_t req_file_name;
69 : utf8stringview_t req_file_basename;
70 : utf8stringview_t req_file_extension;
71 4 : io_data_file_private_split_path( this_, &req_file_path, &req_file_parent, &req_file_name );
72 4 : io_data_file_private_split_extension( this_, &req_file_name, &req_file_basename, &req_file_extension );
73 :
74 4 : const bool temp_requested = utf8string_ends_with_str( requested_file_path, IO_DATA_FILE_TEMP_EXT );
75 : bool is_json;
76 4 : const u8_error_t file_not_readable = io_data_file_private_guess_db_type( this_, requested_file_path, &is_json );
77 4 : u8_error_t err = U8_ERROR_NONE;
78 :
79 4 : if ( file_not_readable != U8_ERROR_NONE )
80 : {
81 2 : if (( temp_requested )||( ! is_json ))
82 : {
83 0 : U8_LOG_EVENT( "rejecting to create a new database based on a given tmp-cfu filename..." );
84 0 : U8_TRACE_INFO( "CASE: use temp db file that does not exist, is not accessible or has wrong format" );
85 : /* This request happens when the user selected the .tmp-fcu file of an already open database. */
86 : /* When the database has been closed, the .tmp-cfu file vanishes and now cannot be found anymore. */
87 0 : (*this_).auto_writeback_to_json = false;
88 0 : (*this_).delete_db_when_finished = false;
89 :
90 0 : err |= utf8stringbuf_copy_view( &((*this_).json_file_name), &req_file_parent );
91 0 : err |= utf8stringbuf_append_view( &((*this_).json_file_name), &req_file_basename );
92 0 : err |= utf8stringbuf_append_str( &((*this_).json_file_name), IO_DATA_FILE_JSON_EXT );
93 :
94 0 : err |= utf8stringbuf_copy_str( &((*this_).db_file_name), requested_file_path );
95 :
96 0 : U8_TRACE_INFO_STR( "json_file_name:", utf8stringbuf_get_string( &((*this_).json_file_name) ) );
97 0 : U8_TRACE_INFO_STR( "db_file_name: ", utf8stringbuf_get_string( &((*this_).db_file_name) ) );
98 :
99 : /* do not open a non-existing .tmp-cfu file */
100 0 : err = U8_ERROR_FILE_ALREADY_REMOVED;
101 :
102 : /* new file is not in sync by definition */
103 0 : (*this_).sync_revision = DATA_REVISION_VOID;
104 : }
105 : else
106 : {
107 2 : U8_LOG_EVENT( "creating a new database based on a given json filename (cfuJ)..." );
108 2 : U8_TRACE_INFO( "CASE: use json file that does not exist or is not accessible" );
109 : /* A new json file shall be created */
110 2 : (*this_).auto_writeback_to_json = true;
111 2 : (*this_).delete_db_when_finished = true;
112 2 : err |= utf8stringbuf_copy_str( &((*this_).json_file_name), requested_file_path );
113 :
114 2 : err |= utf8stringbuf_copy_view( &((*this_).db_file_name), &req_file_parent );
115 2 : err |= utf8stringbuf_append_view( &((*this_).db_file_name), &req_file_basename );
116 2 : err |= utf8stringbuf_append_str( &((*this_).db_file_name), IO_DATA_FILE_TEMP_EXT );
117 :
118 2 : U8_TRACE_INFO_STR( "json_file_name:", utf8stringbuf_get_string( &((*this_).json_file_name) ) );
119 2 : U8_TRACE_INFO_STR( "db_file_name: ", utf8stringbuf_get_string( &((*this_).db_file_name) ) );
120 :
121 2 : const bool temp_exists = u8dir_file_is_regular_file( utf8stringbuf_get_string( &((*this_).db_file_name) ) );
122 2 : if ( temp_exists )
123 : {
124 0 : err |= U8_ERROR_LOCKED_BY_TEMP_FILE;
125 : }
126 : else
127 : {
128 2 : err |= data_database_open( &((*this_).database), utf8stringbuf_get_string( &((*this_).db_file_name) ) );
129 : }
130 :
131 : /* new file is not in sync by definition */
132 2 : (*this_).sync_revision = DATA_REVISION_VOID;
133 : }
134 : }
135 : else
136 : {
137 2 : if ( temp_requested )
138 : {
139 0 : U8_LOG_EVENT( "opening an existing database based on a given tmp-cfu filename..." );
140 0 : U8_TRACE_INFO( "CASE: use existing temp file" );
141 : /* A temporary sqlite file shall be used and later be exported to json */
142 0 : (*this_).auto_writeback_to_json = true;
143 0 : (*this_).delete_db_when_finished = true;
144 0 : err |= utf8stringbuf_copy_view( &((*this_).json_file_name), &req_file_parent );
145 0 : err |= utf8stringbuf_append_view( &((*this_).json_file_name), &req_file_basename );
146 0 : err |= utf8stringbuf_append_str( &((*this_).json_file_name), IO_DATA_FILE_JSON_EXT );
147 :
148 0 : err |= utf8stringbuf_copy_str( &((*this_).db_file_name), requested_file_path );
149 :
150 0 : U8_TRACE_INFO_STR( "json_file_name:", utf8stringbuf_get_string( &((*this_).json_file_name) ) );
151 0 : U8_TRACE_INFO_STR( "db_file_name: ", utf8stringbuf_get_string( &((*this_).db_file_name) ) );
152 :
153 0 : err |= data_database_open( &((*this_).database), utf8stringbuf_get_string( &((*this_).db_file_name) ) );
154 :
155 : /* temp file is not in sync by definition */
156 0 : (*this_).sync_revision = DATA_REVISION_VOID;
157 :
158 : /* Reading the DATA_HEAD_KEY_DATA_FILE_NAME from the just opened (*this_).db_file_name */
159 : /* If found, update (*this_).json_file_name */
160 0 : if ( err == U8_ERROR_NONE )
161 : {
162 : data_database_head_t head_table;
163 0 : data_database_head_init( &head_table, &((*this_).database) );
164 :
165 : data_head_t head;
166 0 : u8_error_t key_err = data_database_head_read_value_by_key( &head_table, DATA_HEAD_KEY_DATA_FILE_NAME, &head );
167 0 : if ( key_err == U8_ERROR_NONE )
168 : {
169 : /* case: recovery after abnormal program termination */
170 0 : U8_TRACE_INFO_STR( "json_file_name (updated):", data_head_get_value_const( &head ) );
171 : /* set the json_file_name to the read head value */
172 0 : err |= utf8stringbuf_copy_view( &((*this_).json_file_name), &req_file_parent );
173 0 : err |= utf8stringbuf_append_str( &((*this_).json_file_name), data_head_get_value_const( &head ) );
174 : }
175 :
176 0 : data_database_head_destroy( &head_table );
177 : }
178 : }
179 2 : else if ( is_json )
180 : {
181 2 : U8_LOG_EVENT( "opening an existing database based on a given json filename (cfuJ)..." );
182 2 : U8_TRACE_INFO_STR( "CASE: use existing json file ", read_only ? "read_only" : "writeable" );
183 : /* An existing json file shall be used */
184 2 : (*this_).auto_writeback_to_json = ( ! read_only );
185 2 : (*this_).delete_db_when_finished = true;
186 2 : err |= utf8stringbuf_copy_str( &((*this_).json_file_name), requested_file_path );
187 :
188 2 : err |= utf8stringbuf_copy_view( &((*this_).db_file_name), &req_file_parent );
189 2 : err |= utf8stringbuf_append_view( &((*this_).db_file_name), &req_file_basename );
190 2 : err |= utf8stringbuf_append_str( &((*this_).db_file_name), IO_DATA_FILE_TEMP_EXT );
191 :
192 2 : U8_TRACE_INFO_STR( "json_file_name:", utf8stringbuf_get_string( &((*this_).json_file_name) ) );
193 2 : U8_TRACE_INFO_STR( "db_file_name: ", utf8stringbuf_get_string( &((*this_).db_file_name) ) );
194 :
195 : /* Do not open the database if a temp file exists --> U8_ERROR_LOCKED_BY_TEMP_FILE */
196 2 : const bool temp_exists = u8dir_file_is_regular_file( utf8stringbuf_get_string( &((*this_).db_file_name) ) );
197 2 : if ( temp_exists )
198 : {
199 0 : U8_LOG_ERROR("The temporary database file already exists, which indicates a running crystal-facet-uml.")
200 0 : (*this_).auto_writeback_to_json = false;
201 0 : (*this_).delete_db_when_finished = false;
202 0 : (*this_).sync_revision = DATA_REVISION_VOID;
203 0 : err |= U8_ERROR_LOCKED_BY_TEMP_FILE;
204 : }
205 : else
206 : {
207 2 : err |= data_database_open( &((*this_).database), utf8stringbuf_get_string( &((*this_).db_file_name) ) );
208 2 : if ( err != U8_ERROR_NONE )
209 : {
210 0 : U8_LOG_ERROR("An error occurred at creating a temporary database file, possibly the parent directory is read-only.")
211 0 : U8_LOG_WARNING("Changes will not be written back to not accidentally overwrite the data source")
212 0 : (*this_).auto_writeback_to_json = false;
213 0 : (*this_).delete_db_when_finished = true; /* do not keep .tmp-cfu file, it was not successfully created anyhow */
214 :
215 : /* temp file is not in sync because import not possible */
216 0 : (*this_).sync_revision = DATA_REVISION_VOID;
217 : }
218 : }
219 :
220 2 : if ( err == U8_ERROR_NONE )
221 : {
222 : /* import */
223 2 : err |= io_data_file_private_import( this_,
224 2 : utf8stringbuf_get_string( &((*this_).json_file_name) ),
225 : io_stat,
226 : out_err_info
227 : );
228 :
229 2 : if ( err != U8_ERROR_NONE )
230 : {
231 1 : err |= data_database_close( &((*this_).database) );
232 :
233 1 : U8_LOG_ERROR("An error occurred at reading a json data file")
234 1 : u8dir_file_remove( utf8stringbuf_get_string( &((*this_).db_file_name) ) ); /* ignore possible additional errors */
235 1 : U8_LOG_WARNING("Changes will not be written back to not accidentally overwrite the data source")
236 1 : (*this_).auto_writeback_to_json = false;
237 1 : (*this_).delete_db_when_finished = false; /* .tmp-cfu file was just deleted */
238 : /* file is not in sync with closed db */
239 1 : (*this_).sync_revision = DATA_REVISION_VOID;
240 : }
241 : else
242 : {
243 1 : (*this_).sync_revision = data_database_get_revision( &((*this_).database) );
244 1 : U8_TRACE_INFO_INT( "sync_revision", (*this_).sync_revision );
245 :
246 : /* update head data */
247 : data_database_head_t head_table;
248 1 : data_database_head_init( &head_table, &((*this_).database) );
249 : {
250 : /* DATA_HEAD_KEY_DATA_FILE_NAME */
251 : data_head_t head1;
252 1 : const char *const requested_file_name = utf8stringview_get_start( &req_file_name ); /* This view is null terminated */
253 1 : data_head_init_new( &head1, DATA_HEAD_KEY_DATA_FILE_NAME, requested_file_name );
254 1 : err |= data_database_head_create_value( &head_table, &head1, NULL );
255 1 : data_head_destroy( &head1 );
256 1 : U8_TRACE_INFO_STR( "io_data_file_open/DATA_FILE_NAME", requested_file_name );
257 :
258 : /* DATA_HEAD_KEY_DATA_FILE_LAST_SYNC_MOD_TIME */
259 : uint64_t mod_time;
260 1 : u8_error_t mtime_err = u8dir_file_get_modification_time( requested_file_path, &mod_time );
261 1 : if ( mtime_err == U8_ERROR_NONE )
262 : {
263 : u8_u64_hex_t hex_time;
264 1 : u8_u64_get_hex( mod_time, &hex_time );
265 : data_head_t head2;
266 1 : data_head_init_new( &head2, DATA_HEAD_KEY_DATA_FILE_LAST_SYNC_MOD_TIME, &(hex_time[0]) );
267 1 : err |= data_database_head_create_value( &head_table, &head2, NULL );
268 1 : data_head_destroy( &head2 );
269 1 : U8_TRACE_INFO_STR( "io_data_file_open/DATA_FILE_LAST_SYNC_MOD_TIME", &(hex_time[0]) );
270 : }
271 : }
272 1 : data_database_head_destroy( &head_table );
273 :
274 : /* restore revision to the one that is stored in the undo redo list */
275 1 : data_database_set_revision( &((*this_).database), (*this_).sync_revision );
276 : }
277 : }
278 : }
279 : else
280 : {
281 0 : U8_LOG_EVENT( "opening an existing database based on an old sqlite3 filename (cfu1)..." );
282 0 : U8_TRACE_INFO( "CASE: use existing sqlite file, auto-convert it to json" );
283 : /* An old sqlite file shall be used, this is automatically converted to json */
284 0 : (*this_).auto_writeback_to_json = true;
285 0 : (*this_).delete_db_when_finished = true;
286 0 : err |= utf8stringbuf_copy_view( &((*this_).json_file_name), &req_file_parent );
287 0 : err |= utf8stringbuf_append_view( &((*this_).json_file_name), &req_file_basename );
288 0 : err |= utf8stringbuf_append_str( &((*this_).json_file_name), IO_DATA_FILE_JSON_EXT );
289 :
290 0 : err |= utf8stringbuf_copy_str( &((*this_).db_file_name), requested_file_path );
291 :
292 0 : U8_TRACE_INFO_STR( "json_file_name:", utf8stringbuf_get_string( &((*this_).json_file_name) ) );
293 0 : U8_TRACE_INFO_STR( "db_file_name: ", utf8stringbuf_get_string( &((*this_).db_file_name) ) );
294 :
295 0 : err |= data_database_open( &((*this_).database), utf8stringbuf_get_string( &((*this_).db_file_name) ) );
296 :
297 : /* old file is not in sync to force conversion to json */
298 0 : (*this_).sync_revision = DATA_REVISION_VOID;
299 : }
300 : }
301 :
302 4 : U8_TRACE_END_ERR( err );
303 4 : return err;
304 : }
305 :
306 2 : u8_error_t io_data_file_close ( io_data_file_t *this_ )
307 : {
308 2 : U8_TRACE_BEGIN();
309 :
310 2 : u8_error_t result = U8_ERROR_NONE;
311 :
312 2 : if ( (*this_).auto_writeback_to_json && ( ! io_data_file_is_in_sync( this_ ) ) )
313 : {
314 2 : U8_TRACE_INFO( "CASE: auto_writeback_to_json == true" );
315 2 : result |= io_data_file_private_export( this_, utf8stringbuf_get_string( &((*this_).json_file_name) ) );
316 : }
317 :
318 2 : result |= data_database_close( &((*this_).database) );
319 :
320 2 : if ( (*this_).delete_db_when_finished )
321 : {
322 2 : U8_TRACE_INFO( "CASE: delete_db_when_finished == true" );
323 2 : u8dir_file_remove( utf8stringbuf_get_string( &((*this_).db_file_name) ) ); /* ignore possible errors */
324 : }
325 :
326 2 : (*this_).auto_writeback_to_json = false;
327 2 : (*this_).delete_db_when_finished = false;
328 :
329 2 : U8_TRACE_END_ERR( result );
330 2 : return result;
331 : }
332 :
333 0 : u8_error_t io_data_file_sync_to_disk ( io_data_file_t *this_ )
334 : {
335 0 : U8_TRACE_BEGIN();
336 :
337 0 : u8_error_t result = data_database_flush_caches( &((*this_).database) );
338 :
339 0 : if ( (*this_).auto_writeback_to_json ) /* ignore if already in_sync - if explicitly requested, simply do sync (again) */
340 : {
341 0 : result |= io_data_file_private_export( this_, utf8stringbuf_get_string( &((*this_).json_file_name) ) );
342 : }
343 :
344 : /* get sync revision */
345 0 : (*this_).sync_revision = data_database_get_revision( &((*this_).database) );
346 0 : U8_TRACE_INFO_INT( "sync_revision", (*this_).sync_revision );
347 :
348 : /* update head data */
349 : data_database_head_t head_table;
350 0 : data_database_head_init( &head_table, &((*this_).database) );
351 : {
352 : /* DATA_HEAD_KEY_DATA_FILE_LAST_SYNC_MOD_TIME */
353 : uint64_t mod_time;
354 0 : const char *const json_file_path = utf8stringbuf_get_string( &((*this_).json_file_name) );
355 0 : u8_error_t mtime_err = u8dir_file_get_modification_time( json_file_path, &mod_time );
356 0 : if ( mtime_err == U8_ERROR_NONE )
357 : {
358 : u8_u64_hex_t hex_time;
359 0 : u8_u64_get_hex( mod_time, &hex_time );
360 0 : result |= data_database_head_update_value_by_key( &head_table,
361 : DATA_HEAD_KEY_DATA_FILE_LAST_SYNC_MOD_TIME,
362 : &(hex_time[0]),
363 : true,
364 : NULL
365 : );
366 0 : U8_TRACE_INFO_STR( "io_data_file_sync_to_disk/DATA_FILE_LAST_SYNC_MOD_TIME", &(hex_time[0]) );
367 : }
368 : }
369 0 : data_database_head_destroy( &head_table );
370 :
371 : /* restore sync revision so that undo_redo history and this_ refer to the same revision */
372 0 : data_database_set_revision( &((*this_).database), (*this_).sync_revision );
373 :
374 0 : U8_TRACE_END_ERR( result );
375 0 : return result;
376 : }
377 :
378 0 : bool io_data_file_is_externally_modified ( io_data_file_t *this_ )
379 : {
380 0 : U8_TRACE_BEGIN();
381 0 : bool result = false;
382 :
383 0 : if ( data_database_is_open( &((*this_).database) ) )
384 : {
385 : data_database_head_t head_table;
386 0 : data_database_head_init( &head_table, &((*this_).database) );
387 : {
388 : uint64_t mod_time;
389 0 : const char *const json_file_path = utf8stringbuf_get_string( &((*this_).json_file_name) );
390 0 : u8_error_t mtime_err = u8dir_file_get_modification_time( json_file_path, &mod_time );
391 0 : if ( mtime_err == U8_ERROR_NONE )
392 : {
393 : u8_u64_hex_t hex_time;
394 0 : u8_u64_get_hex( mod_time, &hex_time );
395 :
396 : data_head_t head_val;
397 : const u8_error_t db_err
398 0 : = data_database_head_read_value_by_key( &head_table,
399 : DATA_HEAD_KEY_DATA_FILE_LAST_SYNC_MOD_TIME,
400 : &head_val
401 : );
402 0 : if ( db_err == U8_ERROR_NONE )
403 : {
404 0 : if ( ! utf8string_equals_str( &(hex_time[0]), data_head_get_value_const( &head_val ) ) )
405 : {
406 0 : result = true;
407 : }
408 : }
409 : }
410 : }
411 0 : data_database_head_destroy( &head_table );
412 : }
413 :
414 0 : if ( result == true )
415 : {
416 : /* if the json file was modified, reset the sync revision to void: */
417 0 : const bool was_in_sync = io_data_file_is_in_sync( this_ );
418 0 : (*this_).sync_revision = DATA_REVISION_VOID;
419 : /* one may consider to set auto_writeback_to_json to false and indicate this to the user... */
420 :
421 : /* set the database revision so that an update notification gets send - if sync status changed: */
422 0 : if ( was_in_sync )
423 : {
424 0 : const data_revision_t revision = data_database_get_revision( &((*this_).database) );
425 0 : data_database_set_revision( &((*this_).database), revision );
426 0 : U8_TRACE_INFO_INT( "revision not in sync:", revision );
427 : }
428 : }
429 :
430 0 : U8_TRACE_END();
431 0 : return result;
432 : }
433 :
434 0 : u8_error_t io_data_file_trace_stats ( io_data_file_t *this_ )
435 : {
436 0 : U8_TRACE_BEGIN();
437 :
438 0 : U8_TRACE_INFO_STR( "io_data_file_t:", utf8stringbuf_get_string( &((*this_).json_file_name) ) );
439 :
440 0 : const u8_error_t result = data_database_trace_stats( &((*this_).database) );
441 :
442 0 : U8_TRACE_END_ERR( result );
443 0 : return result;
444 : }
445 :
446 4 : u8_error_t io_data_file_private_guess_db_type ( const io_data_file_t *this_, const char *filename, bool *out_json )
447 : {
448 4 : U8_TRACE_BEGIN();
449 4 : assert( filename != NULL );
450 4 : assert( out_json != NULL );
451 4 : u8_error_t scan_head_error = U8_ERROR_NONE;
452 :
453 : /* open file */
454 : universal_file_input_stream_t in_file;
455 4 : universal_file_input_stream_init( &in_file );
456 4 : scan_head_error |= universal_file_input_stream_open( &in_file, filename );
457 :
458 : /* import from stream */
459 4 : if ( scan_head_error == U8_ERROR_NONE )
460 : {
461 : char file_prefix[16];
462 : size_t prefix_size;
463 : assert( sizeof(file_prefix) == sizeof(DATA_DATABASE_SQLITE3_MAGIC) );
464 2 : scan_head_error = universal_file_input_stream_read( &in_file, &file_prefix, sizeof(file_prefix), &prefix_size );
465 2 : if (( scan_head_error == U8_ERROR_NONE )&&( prefix_size == sizeof(file_prefix) )
466 2 : &&( 0 == memcmp( &file_prefix, &DATA_DATABASE_SQLITE3_MAGIC, sizeof(file_prefix) ) ))
467 : {
468 0 : U8_TRACE_INFO_STR("File exists and starts with sqlite3 magic:", filename);
469 0 : *out_json = false;
470 : }
471 : else
472 : {
473 2 : U8_TRACE_INFO_STR("File exists and is not of type sqlite3:", filename);
474 2 : *out_json = true;
475 : }
476 :
477 : /* close file */
478 2 : scan_head_error |= universal_file_input_stream_close( &in_file );
479 : }
480 : else
481 : {
482 2 : U8_TRACE_INFO_STR("File does not exist", filename);
483 2 : *out_json = true;
484 : }
485 :
486 : /* cleanup */
487 4 : scan_head_error |= universal_file_input_stream_destroy( &in_file );
488 :
489 4 : U8_TRACE_END_ERR( scan_head_error );
490 4 : return scan_head_error;
491 : }
492 :
493 2 : u8_error_t io_data_file_private_import( io_data_file_t *this_,
494 : const char *src_file,
495 : data_stat_t *io_stat,
496 : u8_error_info_t *out_err_info )
497 : {
498 2 : U8_TRACE_BEGIN();
499 2 : assert( src_file != NULL );
500 2 : assert( io_stat != NULL );
501 2 : assert( out_err_info != NULL );
502 2 : u8_error_info_init_void( out_err_info );
503 2 : u8_error_t import_err = U8_ERROR_NONE;
504 : universal_null_output_stream_t dev_null;
505 2 : universal_null_output_stream_init( &dev_null );
506 : utf8stream_writer_t out_null;
507 2 : utf8stream_writer_init( &out_null, universal_null_output_stream_get_output_stream( &dev_null ) );
508 :
509 2 : U8_TRACE_INFO_STR( "importing file:", src_file );
510 2 : if ( io_data_file_is_open( this_ ) )
511 : {
512 : static data_database_reader_t db_reader;
513 2 : data_database_reader_init( &db_reader, &((*this_).database) );
514 : static io_importer_t importer;
515 2 : io_importer_init( &importer, &db_reader, &((*this_).controller) );
516 : {
517 2 : import_err = io_importer_import_file( &importer, IO_IMPORT_MODE_IMPORT, src_file, io_stat, out_err_info, &out_null );
518 2 : data_stat_trace( io_stat );
519 : }
520 2 : io_importer_destroy( &importer );
521 2 : data_database_reader_destroy( &db_reader );
522 : }
523 : else
524 : {
525 0 : import_err = U8_ERROR_NO_DB;
526 : }
527 :
528 2 : utf8stream_writer_destroy( &out_null );
529 2 : universal_null_output_stream_destroy( &dev_null );
530 2 : U8_TRACE_END_ERR( import_err );
531 2 : return import_err;
532 : }
533 :
534 2 : u8_error_t io_data_file_private_export( io_data_file_t *this_, const char *dst_file )
535 : {
536 2 : U8_TRACE_BEGIN();
537 2 : assert( dst_file != NULL );
538 2 : u8_error_t export_err = U8_ERROR_NONE;
539 :
540 2 : U8_TRACE_INFO_STR( "exporting file:", dst_file );
541 2 : const char *document_filename = io_data_file_get_filename_const( this_ );
542 2 : if ( io_data_file_is_open( this_ ) )
543 : {
544 : static data_database_reader_t db_reader;
545 2 : data_database_reader_init( &db_reader, &((*this_).database) );
546 : static io_exporter_t exporter;
547 2 : io_exporter_init( &exporter, &db_reader );
548 : {
549 : data_stat_t export_stat;
550 2 : data_stat_init ( &export_stat );
551 2 : export_err = io_exporter_export_document_file( &exporter, IO_FILE_FORMAT_JSON, "title", document_filename, &export_stat );
552 2 : data_stat_trace( &export_stat );
553 2 : data_stat_destroy ( &export_stat );
554 : }
555 2 : io_exporter_destroy( &exporter );
556 2 : data_database_reader_destroy( &db_reader );
557 : }
558 : else
559 : {
560 0 : export_err = U8_ERROR_NO_DB;
561 : }
562 :
563 2 : U8_TRACE_END_ERR( export_err );
564 2 : return export_err;
565 : }
566 :
567 :
568 : /*
569 : Copyright 2022-2026 Andreas Warnke
570 :
571 : Licensed under the Apache License, Version 2.0 (the "License");
572 : you may not use this file except in compliance with the License.
573 : You may obtain a copy of the License at
574 :
575 : http://www.apache.org/licenses/LICENSE-2.0
576 :
577 : Unless required by applicable law or agreed to in writing, software
578 : distributed under the License is distributed on an "AS IS" BASIS,
579 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
580 : See the License for the specific language governing permissions and
581 : limitations under the License.
582 : */
|