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 16 : void universal_file_output_stream_init ( universal_file_output_stream_t *this_ )
22 : {
23 16 : U8_TRACE_BEGIN();
24 :
25 16 : (*this_).output = NULL;
26 16 : universal_output_stream_private_init( &((*this_).output_stream), &universal_file_output_stream_private_if, this_ );
27 :
28 16 : U8_TRACE_END();
29 16 : }
30 :
31 15 : u8_error_t universal_file_output_stream_destroy( universal_file_output_stream_t *this_ )
32 : {
33 15 : U8_TRACE_BEGIN();
34 15 : u8_error_t err = U8_ERROR_NONE;
35 :
36 15 : if ( (*this_).output != NULL )
37 : {
38 4 : err = universal_file_output_stream_close( this_ );
39 : }
40 15 : (*this_).output = NULL;
41 15 : universal_output_stream_private_destroy( &((*this_).output_stream) );
42 :
43 15 : U8_TRACE_END_ERR(err);
44 15 : return err;
45 : }
46 :
47 17 : u8_error_t universal_file_output_stream_open ( universal_file_output_stream_t *this_, const char *path )
48 : {
49 17 : U8_TRACE_BEGIN();
50 17 : assert( path != NULL );
51 17 : u8_error_t err = U8_ERROR_NONE;
52 :
53 17 : 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 17 : (*this_).output = fopen( path, "w" );
60 17 : 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 17 : U8_TRACE_END_ERR(err);
68 17 : return err;
69 : }
70 :
71 113 : 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 113 : u8_error_t err = U8_ERROR_NONE;
75 :
76 113 : if ( (*this_).output != NULL )
77 : {
78 111 : size_t written = 0;
79 222 : while (( written < length )&&( err == U8_ERROR_NONE ))
80 : {
81 111 : const size_t remaining = length - written;
82 : size_t out_count;
83 111 : out_count = fwrite( ((const char*)start)+written, 1, remaining, (*this_).output );
84 111 : assert( out_count != 0 ); /* this should not happen, but do not take this for granted */
85 111 : U8_FAULT_INJECT_COND_SET( U8_TEST_COND_FWRITE, out_count, 0 );
86 111 : 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 110 : 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 113 : return err;
105 : }
106 :
107 13 : u8_error_t universal_file_output_stream_flush( universal_file_output_stream_t *this_ )
108 : {
109 13 : U8_TRACE_BEGIN();
110 13 : u8_error_t err = U8_ERROR_NONE;
111 :
112 13 : if ( (*this_).output != NULL )
113 : {
114 12 : int flush_err = fflush( (*this_).output );
115 12 : assert( flush_err == 0 ); /* this should not happen, but do not take this for granted */
116 12 : U8_FAULT_INJECT_COND_SET( U8_TEST_COND_FFLUSH, flush_err, EOF );
117 12 : 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 13 : U8_TRACE_END_ERR(err);
130 13 : return err;
131 : }
132 :
133 17 : u8_error_t universal_file_output_stream_close( universal_file_output_stream_t *this_ )
134 : {
135 17 : U8_TRACE_BEGIN();
136 17 : u8_error_t err = U8_ERROR_NONE;
137 :
138 17 : if ( (*this_).output != NULL )
139 : {
140 15 : int close_err = fclose( (*this_).output );
141 15 : assert( close_err == 0 ); /* this should not happen, but do not take this for granted */
142 15 : U8_FAULT_INJECT_COND_SET( U8_TEST_COND_FCLOSE, close_err, EOF );
143 15 : 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 15 : (*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 17 : U8_TRACE_END_ERR(err);
157 17 : return err;
158 : }
159 :
160 3 : universal_output_stream_t* universal_file_output_stream_get_output_stream( universal_file_output_stream_t *this_ )
161 : {
162 3 : U8_TRACE_BEGIN();
163 :
164 3 : universal_output_stream_t* result = &((*this_).output_stream);
165 :
166 3 : U8_TRACE_END();
167 3 : return result;
168 : }
169 :
170 :
171 : /*
172 : Copyright 2020-2025 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 : */
|