Line data Source code
1 : /* File: universal_file_input_stream.c; Copyright and License: see below */ 2 : 3 : #include "u8stream/universal_file_input_stream.h" 4 : #include "u8stream/universal_input_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_input_stream_if_t universal_file_input_stream_private_if 16 : = { 17 : .read = (u8_error_t (*)(universal_input_stream_impl_t*, void*, size_t, size_t*)) &universal_file_input_stream_read, 18 : .reset = (u8_error_t (*)(universal_input_stream_impl_t*)) &universal_file_input_stream_reset 19 : }; 20 : 21 11 : void universal_file_input_stream_init ( universal_file_input_stream_t *this_ ) 22 : { 23 11 : U8_TRACE_BEGIN(); 24 : 25 11 : (*this_).input = NULL; 26 11 : universal_input_stream_private_init( &((*this_).input_stream), &universal_file_input_stream_private_if, this_ ); 27 : 28 11 : U8_TRACE_END(); 29 11 : } 30 : 31 10 : u8_error_t universal_file_input_stream_destroy( universal_file_input_stream_t *this_ ) 32 : { 33 10 : U8_TRACE_BEGIN(); 34 10 : u8_error_t err = U8_ERROR_NONE; 35 : 36 10 : if ( (*this_).input != NULL ) 37 : { 38 2 : err = universal_file_input_stream_close( this_ ); 39 : } 40 10 : (*this_).input = NULL; 41 10 : universal_input_stream_private_destroy( &((*this_).input_stream) ); 42 : 43 10 : U8_TRACE_END_ERR(err); 44 10 : return err; 45 : } 46 : 47 13 : u8_error_t universal_file_input_stream_open ( universal_file_input_stream_t *this_, const char *path ) 48 : { 49 13 : U8_TRACE_BEGIN(); 50 13 : assert( path != NULL ); 51 13 : u8_error_t err = 0; 52 : 53 13 : if ( (*this_).input != 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_input_stream_close( this_ ); 58 : } 59 13 : (*this_).input = fopen( path, "r" ); 60 13 : if ( NULL == (*this_).input ) 61 : { 62 : /* Note: This need not be an error, could be intentionally to avoid TOCTOU issues. */ 63 4 : U8_LOG_EVENT_STR("could not open file for reading:", strerror(errno) ); 64 4 : err |= U8_ERROR_AT_FILE_READ; 65 : } 66 : 67 13 : U8_TRACE_END_ERR(err); 68 13 : return err; 69 : } 70 : 71 10 : u8_error_t universal_file_input_stream_read ( universal_file_input_stream_t *this_, 72 : void *out_buffer, 73 : size_t max_size, 74 : size_t *out_length ) 75 : { 76 : /*U8_TRACE_BEGIN();*/ 77 10 : assert( out_buffer != NULL ); 78 10 : u8_error_t err = U8_ERROR_NONE; 79 : 80 10 : if ( (*this_).input != NULL ) 81 : { 82 9 : size_t read_bytes = fread( out_buffer, 1, max_size, (*this_).input ); 83 9 : if ( read_bytes != 0 ) 84 : { 85 7 : *out_length = read_bytes; 86 : } 87 : else 88 : { 89 2 : err = U8_ERROR_END_OF_STREAM; /* finished, no more bytes to read or other error */ 90 2 : *out_length = 0; 91 : } 92 : } 93 : else 94 : { 95 1 : U8_LOG_ERROR("cannot read from a file that is not open."); 96 1 : err = U8_ERROR_WRONG_STATE; 97 : } 98 : 99 : /*U8_TRACE_END_ERR(err);*/ 100 10 : return err; 101 : } 102 : 103 3 : u8_error_t universal_file_input_stream_reset ( universal_file_input_stream_t *this_ ) 104 : { 105 3 : U8_TRACE_BEGIN(); 106 3 : u8_error_t err = U8_ERROR_NONE; 107 : 108 3 : if ( (*this_).input != NULL ) 109 : { 110 2 : int seek_err = fseek( (*this_).input, 0, SEEK_SET ); 111 2 : assert( seek_err == 0 ); /* this should not happen, but do not take this for granted */ 112 2 : seek_err = U8_FAULT_INJECT_COND( U8_TEST_COND_FSEEK, -1, seek_err ); 113 2 : if ( seek_err != 0 ) 114 : { 115 1 : U8_LOG_ERROR("error at resetting the read-cursor in a file."); 116 1 : err = U8_ERROR_AT_FILE_READ; 117 : } 118 : } 119 : else 120 : { 121 1 : U8_LOG_ERROR("cannot reset/seek a file that is not open."); 122 1 : err = U8_ERROR_WRONG_STATE; 123 : } 124 : 125 3 : U8_TRACE_END_ERR(err); 126 3 : return err; 127 : } 128 : 129 10 : u8_error_t universal_file_input_stream_close( universal_file_input_stream_t *this_ ) 130 : { 131 10 : U8_TRACE_BEGIN(); 132 10 : u8_error_t err = U8_ERROR_NONE; 133 : 134 10 : if ( (*this_).input != NULL ) 135 : { 136 9 : int close_err = fclose( (*this_).input ); 137 9 : assert( close_err == 0 ); /* this should not happen, but do not take this for granted */ 138 9 : close_err = U8_FAULT_INJECT_COND( U8_TEST_COND_FCLOSE, EOF, close_err ); 139 9 : if ( 0 != close_err ) 140 : { 141 1 : U8_LOG_ERROR_INT( "error at closing file:", close_err ); 142 1 : err = U8_ERROR_AT_FILE_READ; 143 : } 144 9 : (*this_).input = NULL; 145 : } 146 : else 147 : { 148 1 : U8_LOG_ERROR("cannot close a file that is not open."); 149 1 : err = U8_ERROR_WRONG_STATE; 150 : } 151 : 152 10 : U8_TRACE_END_ERR(err); 153 10 : return err; 154 : } 155 : 156 2 : universal_input_stream_t* universal_file_input_stream_get_input_stream( universal_file_input_stream_t *this_ ) 157 : { 158 2 : U8_TRACE_BEGIN(); 159 : 160 2 : universal_input_stream_t* result = &((*this_).input_stream); 161 : 162 2 : U8_TRACE_END(); 163 2 : return result; 164 : } 165 : 166 : 167 : /* 168 : Copyright 2021-2024 Andreas Warnke 169 : 170 : Licensed under the Apache License, Version 2.0 (the "License"); 171 : you may not use this file except in compliance with the License. 172 : You may obtain a copy of the License at 173 : 174 : http://www.apache.org/licenses/LICENSE-2.0 175 : 176 : Unless required by applicable law or agreed to in writing, software 177 : distributed under the License is distributed on an "AS IS" BASIS, 178 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 179 : See the License for the specific language governing permissions and 180 : limitations under the License. 181 : */