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 12 : void universal_file_input_stream_init ( universal_file_input_stream_t *this_ )
22 : {
23 12 : U8_TRACE_BEGIN();
24 :
25 12 : (*this_).input = NULL;
26 12 : universal_input_stream_private_init( &((*this_).input_stream), &universal_file_input_stream_private_if, this_ );
27 :
28 12 : U8_TRACE_END();
29 12 : }
30 :
31 11 : u8_error_t universal_file_input_stream_destroy( universal_file_input_stream_t *this_ )
32 : {
33 11 : U8_TRACE_BEGIN();
34 11 : u8_error_t err = U8_ERROR_NONE;
35 :
36 11 : if ( (*this_).input != NULL )
37 : {
38 2 : err = universal_file_input_stream_close( this_ );
39 : }
40 11 : (*this_).input = NULL;
41 11 : universal_input_stream_private_destroy( &((*this_).input_stream) );
42 :
43 11 : U8_TRACE_END_ERR(err);
44 11 : return err;
45 : }
46 :
47 14 : u8_error_t universal_file_input_stream_open ( universal_file_input_stream_t *this_, const char *path )
48 : {
49 14 : U8_TRACE_BEGIN();
50 14 : assert( path != NULL );
51 14 : u8_error_t err = 0;
52 :
53 14 : 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 14 : (*this_).input = fopen( path, "r" );
60 14 : 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 14 : U8_TRACE_END_ERR(err);
68 14 : return err;
69 : }
70 :
71 19 : 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 19 : assert( out_buffer != NULL );
78 19 : u8_error_t err = U8_ERROR_NONE;
79 :
80 19 : if ( (*this_).input != NULL )
81 : {
82 18 : size_t read_bytes = fread( out_buffer, 1, max_size, (*this_).input );
83 18 : if ( read_bytes != 0 )
84 : {
85 10 : *out_length = read_bytes;
86 : }
87 : else
88 : {
89 8 : err = U8_ERROR_END_OF_STREAM; /* finished, no more bytes to read or other error */
90 8 : *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 19 : return err;
101 : }
102 :
103 5 : u8_error_t universal_file_input_stream_reset ( universal_file_input_stream_t *this_ )
104 : {
105 5 : U8_TRACE_BEGIN();
106 5 : u8_error_t err = U8_ERROR_NONE;
107 :
108 5 : if ( (*this_).input != NULL )
109 : {
110 4 : int seek_err = fseek( (*this_).input, 0, SEEK_SET );
111 4 : assert( seek_err == 0 ); /* this should not happen, but do not take this for granted */
112 4 : U8_FAULT_INJECT_COND_SET( U8_TEST_COND_FSEEK, seek_err, -1 );
113 4 : 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 5 : U8_TRACE_END_ERR(err);
126 5 : return err;
127 : }
128 :
129 11 : u8_error_t universal_file_input_stream_close( universal_file_input_stream_t *this_ )
130 : {
131 11 : U8_TRACE_BEGIN();
132 11 : u8_error_t err = U8_ERROR_NONE;
133 :
134 11 : if ( (*this_).input != NULL )
135 : {
136 10 : int close_err = fclose( (*this_).input );
137 10 : assert( close_err == 0 ); /* this should not happen, but do not take this for granted */
138 10 : U8_FAULT_INJECT_COND_SET( U8_TEST_COND_FCLOSE, close_err, EOF );
139 10 : 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 10 : (*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 11 : U8_TRACE_END_ERR(err);
153 11 : return err;
154 : }
155 :
156 3 : universal_input_stream_t* universal_file_input_stream_get_input_stream( universal_file_input_stream_t *this_ )
157 : {
158 3 : U8_TRACE_BEGIN();
159 :
160 3 : universal_input_stream_t* result = &((*this_).input_stream);
161 :
162 3 : U8_TRACE_END();
163 3 : return result;
164 : }
165 :
166 :
167 : /*
168 : Copyright 2021-2025 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 : */
|