Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 73.7% 28 / 0 / 38
Functions: 88.9% 8 / 0 / 9
Branches: 60.0% 12 / 0 / 20

OMCompiler/SimulationRuntime/c/util/omc_file.c
Line Branch Exec Source
1 /*
2 * This file belongs to the OpenModelica Run-Time System
3 *
4 * Copyright (c) 1998-2026, Open Source Modelica Consortium (OSMC), c/o Linköpings
5 * universitet, Department of Computer and Information Science, SE-58183 Linköping, Sweden. All rights
6 * reserved.
7 *
8 * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF THE BSD NEW LICENSE OR THE
9 * AGPL VERSION 3 LICENSE OR THE OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.8. ANY
10 * USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES RECIPIENT'S
11 * ACCEPTANCE OF THE BSD NEW LICENSE OR THE OSMC PUBLIC LICENSE OR THE AGPL
12 * VERSION 3, ACCORDING TO RECIPIENTS CHOICE.
13 *
14 * The OpenModelica software and the OSMC (Open Source Modelica Consortium) Public License
15 * (OSMC-PL) are obtained from OSMC, either from the above address, from the URLs:
16 * http://www.openmodelica.org or https://github.com/OpenModelica/ or
17 * http://www.ida.liu.se/projects/OpenModelica, and in the OpenModelica distribution. GNU
18 * AGPL version 3 is obtained from: https://www.gnu.org/licenses/licenses.html#GPL. The BSD NEW
19 * License is obtained from: http://www.opensource.org/licenses/BSD-3-Clause.
20 *
21 * This program is distributed WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY
23 * SET FORTH IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF
24 * OSMC-PL.
25 *
26 */
27
28 #ifdef __cplusplus
29 extern "C" {
30 #endif
31
32 #include "omc_file.h"
33 #include "omc_error.h"
34
35 #define BUFSIZE 4096
36
37 #if defined(__MINGW32__) || defined(_MSC_VER)
38 wchar_t* longabspath(wchar_t* unicodePath);
39 #endif
40
41 #if defined(__MINGW32__) || defined(_MSC_VER)
42 /**
43 * @brief Convert a multibyte (normal) string to a wide character string.
44 * NOTE: The caller is responsible for deallocating the memory of the returned wchar string.
45 *
46 * @param in_mb_str multibyte (normal) string to be converted.
47 * @return wchar_t* A wide character representation of the multibyte string. The caller is responsible for deallocating the memory.
48 */
49 wchar_t* omc_multibyte_to_wchar_str(const char* in_mb_str) {
50 int length;
51 wchar_t* out_wc_str;
52
53 /* MultiByteToWideChar returns 0 for a NULL or otherwise unconvertible input.
54 Returning malloc(0) here would hand the caller an unterminated, uninitialised
55 buffer that it then reads as a string. */
56 if (!in_mb_str) {
57 return NULL;
58 }
59
60 length = MultiByteToWideChar(CP_UTF8, 0, in_mb_str, -1, NULL, 0);
61 if (length <= 0) {
62 return NULL;
63 }
64
65 out_wc_str = (wchar_t*) malloc(length * sizeof(wchar_t));
66 if (!out_wc_str) {
67 return NULL;
68 }
69
70 if (MultiByteToWideChar(CP_UTF8, 0, in_mb_str, -1, out_wc_str, length) <= 0) {
71 free(out_wc_str);
72 return NULL;
73 }
74
75 return out_wc_str;
76 }
77
78 /**
79 * @brief Convert a wide character string to multibyte (normal) string.
80 * NOTE: The caller is responsible for deallocating the memory of the returned multibyte string.
81 *
82 * @param in_wc_str wide character string to be converted.
83 * @return char* A multibyte string representation of the wide character. The caller is responsible for deallocating the memory.
84 */
85 char* omc_wchar_to_multibyte_str(const wchar_t* in_wc_str) {
86
87 int length = WideCharToMultiByte(CP_UTF8, 0, in_wc_str, -1, NULL, 0, NULL, NULL);
88
89 char* out_mb_str = (char*) malloc(length * sizeof(char));
90 WideCharToMultiByte(CP_UTF8, 0, in_wc_str, -1, out_mb_str, length, NULL, NULL);
91
92 return out_mb_str;
93 }
94
95 #endif // defined(__MINGW32__) || defined(_MSC_VER)
96
97
98 /**
99 * @brief Open a file in given mode.
100 *
101 * Using (long) unicode absolute path and `_wfopen` on Windows.
102 * Using `fopen` on Unix.
103 *
104 * Returns NULL on failure
105 *
106 * @param filename File name.
107 * @param mode Kind of access to file.
108 * @return FILE* Pointer to opened file.
109 */
110 1547522 FILE* omc_fopen(const char *filename, const char *mode)
111 {
112 #if defined(__MINGW32__) || defined(_MSC_VER)
113
114 wchar_t* unicodeFilename = omc_multibyte_to_wchar_str(filename);
115 wchar_t* unicodeMode = omc_multibyte_to_wchar_str(mode);
116
117 wchar_t* unicodeLongFileName = longabspath(unicodeFilename);
118 FILE *f;
119
120 /* The UCRT kills the process through the invalid parameter handler when one of
121 these is NULL, instead of returning an error, so do not let a path we failed
122 to resolve reach it. */
123 if (!unicodeLongFileName || !unicodeMode) {
124 free(unicodeLongFileName);
125 free(unicodeFilename);
126 free(unicodeMode);
127 return NULL;
128 }
129
130 f = _wfopen(unicodeLongFileName, unicodeMode);
131
132 free(unicodeLongFileName);
133 free(unicodeFilename);
134 free(unicodeMode);
135 #else /* unix */
136 1547522 FILE *f = fopen(filename, mode);
137 #endif
138
3/4
✓ Branch 0 taken 1547518 times.
✓ Branch 1 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1547518 times.
1547522 if (f == NULL || ferror(f)) {
139 4 return NULL;
140 }
141 return f;
142 }
143
144 /**
145 * @brief Close a file.
146 *
147 * @param FILE* Pointer to opened file.
148 */
149 2094 int omc_fclose(FILE* stream)
150 {
151 2094 int err = fclose(stream);
152
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2094 times.
2094 if (0 != err) {
153 ✗ fprintf(stderr, "Error: omc_fclose() failed to close file.\n");
154 }
155 2094 return err;
156 }
157
158 /**
159 * @brief Read data from stream.
160 *
161 * @param buffer Pointer to block of memory with a minimum size of `size`.
162 * @param size Size in bytes of each element to read.
163 * @param count Number of elements to read, each with size `size` bytes.
164 * @param stream Pointer to FILE object with input stream.
165 * @param allow_early_eof Specifies whether the call is okay or not with reaching
166 * EOF before reading the specified amount. Set it to 1 if you do not exactly know how much to read
167 * and would not mind if the file ends before 'count' elements are read from it.
168 * If you are not sure what to do start by passing 0.
169 * @return size_t Total number of elements read.
170 */
171
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11731627 times.
11731627 size_t omc_fread(void *buffer, size_t size, size_t count, FILE *stream, int allow_early_eof) {
172 size_t read_len = fread(buffer, size, count, stream);
173
2/2
✓ Branch 0 taken 27649 times.
✓ Branch 1 taken 11703978 times.
11731627 if(read_len != count) {
174
2/4
✓ Branch 1 taken 27649 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 27649 times.
27649 if (feof(stream) && !allow_early_eof) {
175 ✗ fprintf(stderr, "Error reading stream: unexpected end of file.\n");
176 ✗ fprintf(stderr, "Expected to read %zu. Read only %zu.\n", count, read_len);
177 }
178
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 27649 times.
27649 else if (ferror(stream)) {
179 ✗ fprintf(stderr, "Error: omc_fread() failed to read file.\n");
180 }
181 }
182
183 11731627 return read_len;
184 }
185
186 /**
187 * @brief Write data to stream.
188 *
189 * @param buffer Pointer to block of memory with a minimum size of `size*count` bytes.
190 * @param size Size in bytes of each element to write.
191 * @param count Number of elements to write, each with size `size` bytes.
192 * @param stream Pointer to FILE object with output stream.
193 * @return size_t Total number of elements written.
194 */
195 67536 size_t omc_fwrite(void *buffer, size_t size, size_t count, FILE *stream) {
196 67536 size_t write_len = fwrite(buffer, size, count, stream);
197
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 67536 times.
67536 if (ferror(stream)) {
198 ✗ fprintf(stderr, "Error: omc_fwrite() failed to write file.\n");
199 }
200
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 67536 times.
67536 if (write_len != count) {
201 ✗ fprintf(stderr, "Error writing stream: unexpected end of file.\n");
202 ✗ fprintf(stderr, "Expected to write %zu. Wrote only %zu\n", count, write_len);
203 }
204
205 67536 return write_len;
206 }
207
208 #if defined(__MINGW32__) || defined(_MSC_VER)
209 /**
210 * @brief File attributes
211 *
212 * Using (long) unicode absolute path and `_wstat` on Windows.
213 * Using `stat` on Unix.
214 *
215 * @param filename File name.
216 * @param statbuf Pointer to stat structure.
217 * @return int 0 on success, -1 on error.
218 */
219 int omc_stat(const char *filename, omc_stat_t* statbuf)
220 {
221 wchar_t* unicodeFilename = omc_multibyte_to_wchar_str(filename);
222 wchar_t* unicodeLongFileName = longabspath(unicodeFilename);
223 int res;
224
225 /* See omc_fopen: _wstat(NULL, ...) terminates the process rather than failing. A
226 path that could not be resolved simply does not exist as far as callers care. */
227 if (!unicodeLongFileName) {
228 free(unicodeFilename);
229 return -1;
230 }
231
232 // _wstat not working on MINGW64 with \\?\, because it links to the MSVCRT (Microsoft Visual C++ Runtime)
233 // We need to use UCRT64, which links to UCRT (Universal C Runtime)
234 res = _wstat(unicodeLongFileName, statbuf);
235
236 free(unicodeLongFileName);
237 free(unicodeFilename);
238
239 return res;
240 }
241 #else /* unix */
242 8612689 int omc_stat(const char *filename, omc_stat_t* statbuf)
243 {
244 int res;
245 8612689 res = stat(filename, statbuf);
246 8612689 return res;
247 }
248 #endif
249
250
251 #if defined(__MINGW32__) || defined(_MSC_VER)
252 /**
253 * @brief File attributes
254 *
255 * Using (long) unicode absolute path and `_wstat` on Windows.
256 * Using `stat` on Unix.
257 *
258 * @param filename File name.
259 * @param statbuf Pointer to stat structure.
260 * @return int 0 on success, -1 on error.
261 */
262 int omc_lstat(const char *filename, omc_stat_t* statbuf)
263 {
264 return omc_stat(filename, statbuf);
265 }
266 #else /* unix */
267 ✗ int omc_lstat(const char *filename, omc_stat_t* statbuf)
268 {
269 int res;
270 ✗ res = lstat(filename, statbuf);
271 ✗ return res;
272 }
273 #endif
274
275 /**
276 * @brief checks if a file/folder exists on the system.
277 * NOTE: Will return success even for directories, i.e., will not confirm that it is indeed a file.
278 *
279 * @param filename the filename to check for existence.
280 * @return int returns 1 if the file/folder exists, 0 otherwise.
281 */
282 3 int omc_file_exists(const char* filename) {
283 omc_stat_t statbuf;
284 3 return omc_stat(filename, &statbuf) == 0;
285 }
286
287
288 /**
289 * @brief Unlink file.
290 *
291 * Using (long) unicode absolute path and `_wunlink` on Windows.
292 * Using `unlink` on Unix.
293 *
294 * @param filename File name
295 * @return int 0 on success, -1 on error.
296 */
297 18558 int omc_unlink(const char *filename) {
298 int result = 0;
299 #if defined(__MINGW32__) || defined(_MSC_VER)
300 wchar_t* unicodeFilename = omc_multibyte_to_wchar_str(filename);
301 wchar_t* unicodeLongFileName = longabspath(unicodeFilename);
302 /* See omc_fopen: _wunlink(NULL) terminates the process rather than failing. */
303 if (!unicodeFilename) {
304 free(unicodeLongFileName);
305 return -1;
306 }
307 result = _wunlink(unicodeFilename);
308 free(unicodeLongFileName);
309 free(unicodeFilename);
310 #else /* unix */
311 18558 result = unlink(filename);
312 #endif
313 /* uncomment for debugging
314 if (result == -1)
315 {
316 const char* s = "Could not delete file: ";
317 char *msg = (char*)malloc(strlen(s) + strlen(filename) + 1);
318 sprintf(msg, "%s%s", s, filename);
319 perror(msg);
320 }
321 */
322 18558 return result;
323 }
324
325 // zero on success, anything else on failure!
326 106 int omc_rename(const char *source, const char *dest) {
327 #if defined(__MINGW32__) || defined(_MSC_VER)
328 // If the function succeeds, the return value is nonzero.
329 // If the function fails, the return value is zero (0). To get extended error information, call GetLastError.
330 return !MoveFileEx(source, dest, MOVEFILE_REPLACE_EXISTING);
331 #endif
332 // On success, zero is returned. On error, -1 is returned, and
333 // errno is set to indicate the error.
334 106 return rename(source,dest);
335 }
336
337 #if defined(__MINGW32__) || defined(_MSC_VER)
338 /**
339 * @brief Return long absolute path from given path.
340 *
341 * Windows only.
342 * Adding "\\?\" prefix if absolute path exceeds length of MAX_PATH and issue a warning.
343 *
344 * @param unicodePath Path
345 * @return wchar_t* (Long) absolute path
346 */
347 wchar_t* longabspath(wchar_t* unicodePath) {
348
349 DWORD retval;
350 wchar_t unicodeAbsPath[BUFSIZE];
351 wchar_t unicodeLongAbsPath[BUFSIZE];
352 wchar_t* path;
353
354 if (!unicodePath) {
355 return NULL;
356 }
357
358 retval = GetFullPathNameW(unicodePath, BUFSIZE, unicodeAbsPath, NULL);
359 if (retval == 0)
360 {
361 printf("GetFullPathName failed for %ls with error code %lu\n", unicodePath, GetLastError());
362 return NULL;
363 }
364 if (wcslen(unicodeAbsPath) >= MAX_PATH) {
365 printf("Warning: Maximum path length limitation reached while opening\n"
366 "\t%ls\n"
367 "Consider changing the working directory, "
368 "using shorter names or to enable longer paths in Windows.\n"
369 "See https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation for more information.\n", unicodeAbsPath);
370
371 const wchar_t longPathPrefix[] = L"\\\\\?\\";
372 size_t longPathPrefix_size = wcslen(longPathPrefix);
373 wcsncpy(unicodeLongAbsPath, longPathPrefix, longPathPrefix_size);
374 wcsncpy(unicodeLongAbsPath+longPathPrefix_size, unicodeAbsPath, BUFSIZE - longPathPrefix_size -1);
375 path = _wcsdup(unicodeLongAbsPath);
376 }
377 else {
378 path = _wcsdup(unicodeAbsPath);
379 }
380
381 return path;
382 }
383 #endif
384
385 #ifdef __cplusplus
386 }
387 #endif
388