LCOV - code coverage report
Current view: top level - io/source - io_data_file.c (source / functions) Coverage Total Hit
Test: crystal-facet-uml_v1.70.5_covts Lines: 60.7 % 270 164
Test Date: 2026-05-28 21:31:40 Functions: 70.0 % 10 7

            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              : */
        

Generated by: LCOV version 2.0-1