Code Coverage Report for src/api/open-file.c


Hit Total Coverage
Lines: 41 41 100.0%
Branches: 98 98 100.0%

1 /*
2 * libnogg: a decoder library for Ogg Vorbis streams
3 * Copyright (c) 2014-2023 Andrew Church <achurch@achurch.org>
4 *
5 * This software may be copied and redistributed under certain conditions;
6 * see the file "COPYING" in the source code distribution for details.
7 * NO WARRANTY is provided with this software.
8 */
9
10 #include "include/nogg.h"
11 #include "src/common.h"
12 #include "src/util/open.h"
13
14 #ifdef USE_STDIO
15 # include <limits.h>
16 # include <stdio.h>
17 #else
18 # include <stddef.h>
19 #endif
20
21 /*************************************************************************/
22 /************************** File I/O callbacks ***************************/
23 /*************************************************************************/
24
25 #ifdef USE_STDIO
26
27 static int64_t file_length(void *opaque)
28 {
29 FILE *f = (FILE *)opaque;
30 const long saved_offset = ftell(f);
31 (16/16) if (saved_offset < 0) {
32 return -1; // Probably an unseekable stream.
33 }
34 /* If ftell() succeeded, the file handle is valid and the file is
35 * seekable, so both this seek to the end of the file and the seek
36 * below to restore the file position must succeed. */
37 ASSERT(fseek(f, 0, SEEK_END) == 0);
38 const int64_t length = ftell(f);
39 ASSERT(fseek(f, saved_offset, SEEK_SET) == 0);
40 return length;
41 }
42
43 static int64_t file_tell(void *opaque)
44 {
45 FILE *f = (FILE *)opaque;
46 return ftell(f);
47 }
48
49 static void file_seek(void *opaque, int64_t offset)
50 {
51 FILE *f = (FILE *)opaque;
52 /* This function is never called unless the file is known to be
53 * seekable (i.e., length() succeeds), so this seek call will always
54 * succeed. The cast of "offset" to long is technically unnecessary,
55 * but we include it just to clarify that the value is truncated in
56 * 32-bit environments; this is not a problem since ftell() should not
57 * succeed in such an environment if the file size does not fit in a
58 * long. */
59 ASSERT(fseek(f, (long)offset, SEEK_SET) == 0);
60 }
61
62 static int32_t file_read(void *opaque, void *buffer, int32_t length)
63 {
64 FILE *f = (FILE *)opaque;
65 return (int32_t)fread(buffer, 1, (size_t)length, f);
66 }
67
68 static void file_close(void *opaque)
69 {
70 FILE *f = (FILE *)opaque;
71 fclose(f);
72 }
73
74 static const vorbis_callbacks_t file_callbacks = {
75 .length = file_length,
76 .tell = file_tell,
77 .seek = file_seek,
78 .read = file_read,
79 .close = file_close,
80 };
81
82 #endif /* USE_STDIO */
83
84 /*************************************************************************/
85 /*************************** Interface routine ***************************/
86 /*************************************************************************/
87
88 vorbis_t *vorbis_open_file(
89 const char *path, unsigned int options, vorbis_error_t *error_ret)
90 {
91 #ifdef USE_STDIO
92
93 (16/16) if (!path) {
94 (16/16) if (error_ret) {
95 *error_ret = VORBIS_ERROR_INVALID_ARGUMENT;
96 }
97 return NULL;
98 }
99
100 FILE *f = fopen(path, "rb");
101 (16/16) if (!f) {
102 (16/16) if (error_ret) {
103 *error_ret = VORBIS_ERROR_FILE_OPEN_FAILED;
104 }
105 return NULL;
106 }
107
108 vorbis_t *handle = open_common(
109 &(open_params_t){.callbacks = &file_callbacks,
110 .callback_data = f,
111 .options = options,
112 .packet_mode = false},
113 error_ret);
114 (16/16) if (!handle) {
115 fclose(f);
116 }
117 return handle;
118
119 #else /* !USE_STDIO */
120
121 (2/2) if (error_ret) {
122 *error_ret = VORBIS_ERROR_DISABLED_FUNCTION;
123 }
124 return NULL;
125
126 #endif
127 }
128
129 /*************************************************************************/
130 /*************************************************************************/