Line data Source code
1 : /* File: universal_file_output_stream.c; Copyright and License: see below */ 2 : 3 : #include "u8stream/universal_file_output_stream.h" 4 : #include "u8stream/universal_output_stream_if.h" 5 : #include "u8_test_cond.h" 6 : #include "u8/u8_fault_inject.h" 7 : #include "u8/u8_trace.h" 8 : #include "u8/u8_log.h" 9 : #include <errno.h> 10 : #include <string.h> 11 : #include <stdbool.h> 12 : #include <assert.h> 13 : 14 : /* the vmt implementing the interface */ 15 : static const universal_output_stream_if_t universal_file_output_stream_private_if 16 : = { 17 : .write = (u8_error_t (*)(universal_output_stream_impl_t*, const void*, size_t)) &universal_file_output_stream_write, 18 : .flush = (u8_error_t (*)(universal_output_stream_impl_t*)) &universal_file_output_stream_flush 19 : }; 20 : 21 13 : void universal_file_output_stream_init ( universal_file_output_stream_t *this_ ) 22 : { 23 13 : U8_TRACE_BEGIN(); 24 : 25 13 : (*this_).output = NULL; 26 13 : universal_output_stream_private_init( &((*this_).output_stream), &universal_file_output_stream_private_if, this_ ); 27 : 28 13 : U8_TRACE_END(); 29 13 : } 30 : 31 12 : u8_error_t universal_file_output_stream_destroy( universal_file_output_stream_t *this_ ) 32 : { 33 12 : U8_TRACE_BEGIN(); 34 12 : u8_error_t err = U8_ERROR_NONE; 35 : 36 12 : if ( (*this_).output != NULL ) 37 : { 38 3 : err = universal_file_output_stream_close( this_ ); 39 : } 40 12 : (*this_).output = NULL; 41 12 : universal_output_stream_private_destroy( &((*this_).output_stream) ); 42 : 43 12 : U8_TRACE_END_ERR(err); 44 12 : return err; 45 : } 46 : 47 14 : u8_error_t universal_file_output_stream_open ( universal_file_output_stream_t *this_, const char *path ) 48 : { 49 14 : U8_TRACE_BEGIN(); 50 14 : assert( path != NULL ); 51 14 : u8_error_t err = U8_ERROR_NONE; 52 : 53 14 : if ( (*this_).output != NULL ) 54 : { 55 1 : U8_LOG_ERROR("cannot open a file that is already open."); 56 1 : err = U8_ERROR_WRONG_STATE; 57 1 : err |= universal_file_output_stream_close( this_ ); 58 : } 59 14 : (*this_).output = fopen( path, "w" ); 60 14 : if ( NULL == (*this_).output ) 61 : { 62 : /* Note: This need not be an error, could be intentionally to avoid TOCTOU issues. */ 63 2 : U8_LOG_EVENT_STR( "could not open file for writing:", strerror(errno) ); 64 2 : err |= U8_ERROR_AT_FILE_WRITE; 65 : } 66 : 67 14 : U8_TRACE_END_ERR(err); 68 14 : return err; 69 : } 70 : 71 12 : u8_error_t universal_file_output_stream_write ( universal_file_output_stream_t *this_, const void *start, size_t length ) 72 : { 73 : /*U8_TRACE_BEGIN();*/ 74 12 : u8_error_t err = U8_ERROR_NONE; 75 : 76 12 : if ( (*this_).output != NULL ) 77 : { 78 10 : size_t written = 0; 79 20 : while (( written < length )&&( err == U8_ERROR_NONE )) 80 : { 81 10 : const size_t remaining = length - written; 82 : size_t out_count; 83 10 : out_count = fwrite( ((const char*)start)+written, 1, remaining, (*this_).output ); 84 10 : assert( out_count != 0 ); /* this should not happen, but do not take this for granted */ 85 10 : out_count = U8_FAULT_INJECT_COND( U8_TEST_COND_FWRITE, 0, out_count ); 86 10 : if ( out_count == 0 ) 87 : { 88 1 : U8_LOG_ERROR_INT( "not all bytes could be written. missing:", remaining ); 89 1 : err = U8_ERROR_AT_FILE_WRITE; 90 : } 91 : else 92 : { 93 9 : written += out_count; 94 : } 95 : } 96 : } 97 : else 98 : { 99 2 : U8_LOG_ERROR("cannot write to a file that is not open."); 100 2 : err = U8_ERROR_WRONG_STATE; 101 : } 102 : 103 : /*U8_TRACE_END_ERR(err);*/ 104 12 : return err; 105 : } 106 : 107 3 : u8_error_t universal_file_output_stream_flush( universal_file_output_stream_t *this_ ) 108 : { 109 3 : U8_TRACE_BEGIN(); 110 3 : u8_error_t err = U8_ERROR_NONE; 111 : 112 3 : if ( (*this_).output != NULL ) 113 : { 114 2 : int flush_err = fflush( (*this_).output ); 115 2 : assert( flush_err == 0 ); /* this should not happen, but do not take this for granted */ 116 2 : flush_err = U8_FAULT_INJECT_COND( U8_TEST_COND_FFLUSH, EOF, flush_err ); 117 2 : if ( 0 != flush_err ) 118 : { 119 1 : U8_LOG_ERROR_INT("error at flushing file:",flush_err); 120 1 : err = U8_ERROR_AT_FILE_WRITE; 121 : } 122 : } 123 : else 124 : { 125 1 : U8_LOG_ERROR("cannot flush a file that is not open."); 126 1 : err = U8_ERROR_WRONG_STATE; 127 : } 128 : 129 3 : U8_TRACE_END_ERR(err); 130 3 : return err; 131 : } 132 : 133 14 : u8_error_t universal_file_output_stream_close( universal_file_output_stream_t *this_ ) 134 : { 135 14 : U8_TRACE_BEGIN(); 136 14 : u8_error_t err = U8_ERROR_NONE; 137 : 138 14 : if ( (*this_).output != NULL ) 139 : { 140 12 : int close_err = fclose( (*this_).output ); 141 12 : assert( close_err == 0 ); /* this should not happen, but do not take this for granted */ 142 12 : close_err = U8_FAULT_INJECT_COND( U8_TEST_COND_FCLOSE, EOF, close_err ); 143 12 : if ( 0 != close_err ) 144 : { 145 1 : U8_LOG_ERROR_INT("error at closing file:",close_err); 146 1 : err = U8_ERROR_AT_FILE_WRITE; 147 : } 148 12 : (*this_).output = NULL; 149 : } 150 : else 151 : { 152 2 : U8_LOG_ERROR("cannot close a file that is not open."); 153 2 : err = U8_ERROR_WRONG_STATE; 154 : } 155 : 156 14 : U8_TRACE_END_ERR(err); 157 14 : return err; 158 : } 159 : 160 1 : universal_output_stream_t* universal_file_output_stream_get_output_stream( universal_file_output_stream_t *this_ ) 161 : { 162 1 : U8_TRACE_BEGIN(); 163 : 164 1 : universal_output_stream_t* result = &((*this_).output_stream); 165 : 166 1 : U8_TRACE_END(); 167 1 : return result; 168 : } 169 : 170 : 171 : /* 172 : Copyright 2020-2024 Andreas Warnke 173 : 174 : Licensed under the Apache License, Version 2.0 (the "License"); 175 : you may not use this file except in compliance with the License. 176 : You may obtain a copy of the License at 177 : 178 : http://www.apache.org/licenses/LICENSE-2.0 179 : 180 : Unless required by applicable law or agreed to in writing, software 181 : distributed under the License is distributed on an "AS IS" BASIS, 182 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 183 : See the License for the specific language governing permissions and 184 : limitations under the License. 185 : */