1 /*
2 mkvmerge -- utility for splicing together matroska files
3 from component media subtypes
4
5 Distributed under the GPL v2
6 see the file COPYING for details
7 or visit https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
8
9 IO callback class implementation
10
11 Written by Moritz Bunkus <moritz@bunkus.org>.
12 */
13
14 #include "common/common_pch.h"
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #ifdef HAVE_UNISTD_H
19 #include <unistd.h>
20 #endif
21 #include <sys/stat.h>
22 #include <sys/types.h>
23
24 #include "common/mm_io_x.h"
25 #include "common/mm_file_io.h"
26 #include "common/mm_file_io_p.h"
27 #if defined(SYS_APPLE)
28 # include "common/fs_sys_helpers.h"
29 #endif
30
mm_file_io_private_c(std::string const & p_file_name,open_mode const p_mode)31 mm_file_io_private_c::mm_file_io_private_c(std::string const &p_file_name,
32 open_mode const p_mode)
33 : file_name{p_file_name}
34 , mode{p_mode}
35 {
36 #if defined(SYS_APPLE)
37 file_name = mtx::sys::normalize_unicode_string(file_name, mtx::sys::unicode_normalization_form_e::d);
38 #endif // SYS_APPLE
39
40 const char *cmode;
41
42 switch (mode) {
43 case MODE_READ:
44 cmode = "rb";
45 break;
46 case MODE_WRITE:
47 cmode = "r+b";
48 break;
49 case MODE_CREATE:
50 cmode = "w+b";
51 break;
52 case MODE_SAFE:
53 cmode = "rb";
54 break;
55 default:
56 throw mtx::invalid_parameter_x();
57 }
58
59 if ((MODE_WRITE == mode) || (MODE_CREATE == mode))
60 mm_file_io_c::prepare_path(file_name);
61 std::string local_path = g_cc_local_utf8->native(file_name);
62
63 struct stat st;
64 if ((0 == stat(local_path.c_str(), &st)) && S_ISDIR(st.st_mode))
65 throw mtx::mm_io::open_x{mtx::mm_io::make_error_code()};
66
67 file = fopen(local_path.c_str(), cmode);
68
69 #if defined(SYS_APPLE)
70 if (!file && (MODE_CREATE != mode)) {
71 // When reading files on macOS retry with names in NFC as they
72 // might come from other sources, e.g. via NFS mounts from NFC
73 // systems such as Linux.
74 local_path = g_cc_local_utf8->native(mtx::sys::normalize_unicode_string(file_name, mtx::sys::unicode_normalization_form_e::c));
75 file = fopen(local_path.c_str(), cmode);
76 }
77 #endif // SYS_APPLE
78
79 if (!file)
80 throw mtx::mm_io::open_x{mtx::mm_io::make_error_code()};
81 }
82
83 void
setFilePointer(int64_t offset,libebml::seek_mode mode)84 mm_file_io_c::setFilePointer(int64_t offset,
85 libebml::seek_mode mode) {
86 auto p = p_func();
87 int whence = mode == libebml::seek_beginning ? SEEK_SET
88 : mode == libebml::seek_end ? SEEK_END
89 : SEEK_CUR;
90
91 if (fseeko(p->file, offset, whence) != 0)
92 throw mtx::mm_io::seek_x{mtx::mm_io::make_error_code()};
93
94 p->current_position = ftello(p->file);
95 }
96
97 size_t
_write(const void * buffer,size_t size)98 mm_file_io_c::_write(const void *buffer,
99 size_t size) {
100 auto p = p_func();
101 size_t bwritten = fwrite(buffer, 1, size, p->file);
102 if (ferror(p->file) != 0)
103 throw mtx::mm_io::read_write_x{mtx::mm_io::make_error_code()};
104
105 p->current_position += bwritten;
106 p->cached_size = -1;
107
108 return bwritten;
109 }
110
111 uint32_t
_read(void * buffer,size_t size)112 mm_file_io_c::_read(void *buffer,
113 size_t size) {
114 auto p = p_func();
115 int64_t bread = fread(buffer, 1, size, p->file);
116
117 p->current_position += bread;
118
119 return bread;
120 }
121
122 void
close()123 mm_file_io_c::close() {
124 auto p = p_func();
125
126 if (p->file) {
127 if (mm_file_io_private_c::ms_flush_on_close && (p->mode != MODE_READ))
128 fflush(p->file);
129
130 fclose(p->file);
131 p->file = nullptr;
132 }
133
134 p->file_name.clear();
135 }
136
137 bool
eof()138 mm_file_io_c::eof() {
139 return feof(p_func()->file) != 0;
140 }
141
142 void
clear_eof()143 mm_file_io_c::clear_eof() {
144 clearerr(p_func()->file);
145 }
146
147 int
truncate(int64_t pos)148 mm_file_io_c::truncate(int64_t pos) {
149 auto p = p_func();
150 p->cached_size = -1;
151 return ftruncate(fileno(p->file), pos);
152 }
153