1
|
|
2
|
|
3
|
|
4
|
|
5
|
|
6
|
|
7
|
|
8
|
|
9
|
|
10
|
|
11
|
|
12
|
|
13
|
|
14
|
|
15
|
|
16
|
|
17
|
|
18
|
|
19
|
|
20
|
#include <stdint.h>
|
21
|
#include <stdlib.h>
|
22
|
#include <string.h>
|
23
|
|
24
|
#include <gio/gio.h>
|
25
|
|
26
|
#include <libaudcore/audstrings.h>
|
27
|
#include <libaudcore/i18n.h>
|
28
|
#include <libaudcore/interface.h>
|
29
|
#include <libaudcore/plugin.h>
|
30
|
#include <libaudcore/runtime.h>
|
31
|
|
32
|
|
33
|
static const char gio_about[] =
|
34
|
N_("GIO Plugin for Audacious\n"
|
35
|
"Copyright 2009-2012 John Lindgren");
|
36
|
|
37
|
static const char * const gio_schemes[] = {"ftp", "sftp", "smb", "stdin"};
|
38
|
|
39
|
class GIOTransport : public TransportPlugin
|
40
|
{
|
41
|
public:
|
42
|
static constexpr PluginInfo info = {N_("GIO Plugin"), PACKAGE, gio_about};
|
43
|
|
44
|
constexpr GIOTransport () : TransportPlugin (info, gio_schemes) {}
|
45
|
|
46
|
VFSImpl * fopen (const char * path, const char * mode, String & error);
|
47
|
};
|
48
|
|
49
|
EXPORT GIOTransport aud_plugin_instance;
|
50
|
|
51
|
class GIOFile : public VFSImpl
|
52
|
{
|
53
|
public:
|
54
|
GIOFile (const char * filename, const char * mode);
|
55
|
~GIOFile ();
|
56
|
|
57
|
|
58
|
struct OpenError {
|
59
|
String error;
|
60
|
};
|
61
|
|
62
|
protected:
|
63
|
int64_t fread (void * ptr, int64_t size, int64_t nmemb);
|
64
|
int64_t fwrite (const void * buf, int64_t size, int64_t nitems);
|
65
|
|
66
|
int fseek (int64_t offset, VFSSeekType whence);
|
67
|
int64_t ftell ();
|
68
|
|
69
|
int getc ();
|
70
|
int ungetc (int c);
|
71
|
|
72
|
bool feof ();
|
73
|
|
74
|
int ftruncate (int64_t length);
|
75
|
int64_t fsize ();
|
76
|
|
77
|
int fflush ();
|
78
|
|
79
|
private:
|
80
|
String m_filename;
|
81
|
GFile * m_file = nullptr;
|
82
|
GIOStream * m_iostream = nullptr;
|
83
|
GInputStream * m_istream = nullptr;
|
84
|
GOutputStream * m_ostream = nullptr;
|
85
|
GSeekable * m_seekable = nullptr;
|
86
|
};
|
87
|
|
88
|
#define CHECK_ERROR(op, name) do { \
|
89
|
if (error) { \
|
90
|
AUDERR ("Cannot %s %s: %s.\n", op, (const char *) name, error->message); \
|
91
|
g_error_free (error); \
|
92
|
goto FAILED; \
|
93
|
} \
|
94
|
} while (0)
|
95
|
|
96
|
#define CHECK_AND_SAVE_ERROR(op, name) do { \
|
97
|
if (error) { \
|
98
|
AUDERR ("Cannot %s %s: %s.\n", op, (const char *) name, error->message); \
|
99
|
errorstr = String (error->message); \
|
100
|
g_error_free (error); \
|
101
|
goto FAILED; \
|
102
|
} \
|
103
|
} while (0)
|
104
|
|
105
|
GIOFile::GIOFile (const char * filename, const char * mode) :
|
106
|
m_filename (filename)
|
107
|
{
|
108
|
GError * error = nullptr;
|
109
|
String errorstr;
|
110
|
|
111
|
#ifdef _WINDOWS
|
112
|
m_file = g_file_new_for_uri (filename);
|
113
|
#endif
|
114
|
#ifndef _WINDOWS
|
115
|
m_file = (strstr(filename, "://-.")) ? g_file_new_for_path ("/dev/stdin") : g_file_new_for_uri (filename);
|
116
|
#endif
|
117
|
|
118
|
switch (mode[0])
|
119
|
{
|
120
|
case 'r':
|
121
|
if (strchr (mode, '+'))
|
122
|
{
|
123
|
m_iostream = (GIOStream *) g_file_open_readwrite (m_file, 0, & error);
|
124
|
CHECK_AND_SAVE_ERROR ("open", filename);
|
125
|
m_istream = g_io_stream_get_input_stream (m_iostream);
|
126
|
m_ostream = g_io_stream_get_output_stream (m_iostream);
|
127
|
m_seekable = (GSeekable *) m_iostream;
|
128
|
}
|
129
|
else
|
130
|
{
|
131
|
m_istream = (GInputStream *) g_file_read (m_file, 0, & error);
|
132
|
CHECK_AND_SAVE_ERROR ("open", filename);
|
133
|
m_seekable = (GSeekable *) m_istream;
|
134
|
}
|
135
|
break;
|
136
|
case 'w':
|
137
|
if (strchr (mode, '+'))
|
138
|
{
|
139
|
m_iostream = (GIOStream *) g_file_replace_readwrite (m_file,
|
140
|
0, 0, (GFileCreateFlags) 0, 0, & error);
|
141
|
CHECK_AND_SAVE_ERROR ("open", filename);
|
142
|
m_istream = g_io_stream_get_input_stream (m_iostream);
|
143
|
m_ostream = g_io_stream_get_output_stream (m_iostream);
|
144
|
m_seekable = (GSeekable *) m_iostream;
|
145
|
}
|
146
|
else
|
147
|
{
|
148
|
m_ostream = (GOutputStream *) g_file_replace (m_file, 0, 0,
|
149
|
(GFileCreateFlags) 0, 0, & error);
|
150
|
CHECK_AND_SAVE_ERROR ("open", filename);
|
151
|
m_seekable = (GSeekable *) m_ostream;
|
152
|
}
|
153
|
break;
|
154
|
case 'a':
|
155
|
if (strchr (mode, '+'))
|
156
|
{
|
157
|
AUDERR ("Cannot open %s: GIO does not support read-and-append mode.\n", filename);
|
158
|
errorstr = String (_("Read-and-append mode not supported"));
|
159
|
goto FAILED;
|
160
|
}
|
161
|
else
|
162
|
{
|
163
|
m_ostream = (GOutputStream *) g_file_append_to (m_file,
|
164
|
(GFileCreateFlags) 0, 0, & error);
|
165
|
CHECK_AND_SAVE_ERROR ("open", filename);
|
166
|
m_seekable = (GSeekable *) m_ostream;
|
167
|
}
|
168
|
break;
|
169
|
default:
|
170
|
AUDERR ("Cannot open %s: invalid mode.\n", filename);
|
171
|
errorstr = String (_("Invalid open mode"));
|
172
|
goto FAILED;
|
173
|
}
|
174
|
|
175
|
return;
|
176
|
|
177
|
FAILED:
|
178
|
g_object_unref (m_file);
|
179
|
throw OpenError {errorstr};
|
180
|
}
|
181
|
|
182
|
GIOFile::~GIOFile ()
|
183
|
{
|
184
|
GError * error = 0;
|
185
|
|
186
|
if (m_iostream)
|
187
|
{
|
188
|
g_io_stream_close (m_iostream, 0, & error);
|
189
|
g_object_unref (m_iostream);
|
190
|
CHECK_ERROR ("close", m_filename);
|
191
|
}
|
192
|
else if (m_istream)
|
193
|
{
|
194
|
g_input_stream_close (m_istream, 0, & error);
|
195
|
g_object_unref (m_istream);
|
196
|
CHECK_ERROR ("close", m_filename);
|
197
|
}
|
198
|
else if (m_ostream)
|
199
|
{
|
200
|
g_output_stream_close (m_ostream, 0, & error);
|
201
|
g_object_unref (m_ostream);
|
202
|
CHECK_ERROR ("close", m_filename);
|
203
|
}
|
204
|
|
205
|
FAILED:
|
206
|
g_object_unref (m_file);
|
207
|
}
|
208
|
|
209
|
VFSImpl * GIOTransport::fopen (const char * filename, const char * mode, String & error)
|
210
|
{
|
211
|
#if ! GLIB_CHECK_VERSION (2, 36, 0)
|
212
|
g_type_init ();
|
213
|
#endif
|
214
|
|
215
|
try { return new GIOFile (filename, mode); }
|
216
|
catch (GIOFile::OpenError & ex)
|
217
|
{
|
218
|
error = std::move (ex.error);
|
219
|
return nullptr;
|
220
|
}
|
221
|
}
|
222
|
|
223
|
int64_t GIOFile::fread (void * buf, int64_t size, int64_t nitems)
|
224
|
{
|
225
|
GError * error = 0;
|
226
|
|
227
|
if (! m_istream)
|
228
|
{
|
229
|
AUDERR ("Cannot read from %s: not open for reading.\n", (const char *) m_filename);
|
230
|
return 0;
|
231
|
}
|
232
|
|
233
|
int64_t readed = g_input_stream_read (m_istream, buf, size * nitems, 0, & error);
|
234
|
CHECK_ERROR ("read from", m_filename);
|
235
|
|
236
|
return (size > 0) ? readed / size : 0;
|
237
|
|
238
|
FAILED:
|
239
|
return 0;
|
240
|
}
|
241
|
|
242
|
int64_t GIOFile::fwrite (const void * buf, int64_t size, int64_t nitems)
|
243
|
{
|
244
|
GError * error = 0;
|
245
|
|
246
|
if (! m_ostream)
|
247
|
{
|
248
|
AUDERR ("Cannot write to %s: not open for writing.\n", (const char *) m_filename);
|
249
|
return 0;
|
250
|
}
|
251
|
|
252
|
int64_t written = g_output_stream_write (m_ostream, buf, size * nitems, 0, & error);
|
253
|
CHECK_ERROR ("write to", m_filename);
|
254
|
|
255
|
return (size > 0) ? written / size : 0;
|
256
|
|
257
|
FAILED:
|
258
|
return 0;
|
259
|
}
|
260
|
|
261
|
int GIOFile::fseek (int64_t offset, VFSSeekType whence)
|
262
|
{
|
263
|
if (strstr(m_filename, "://-.")) return 0;
|
264
|
|
265
|
GError * error = 0;
|
266
|
GSeekType gwhence;
|
267
|
|
268
|
switch (whence)
|
269
|
{
|
270
|
case VFS_SEEK_SET:
|
271
|
gwhence = G_SEEK_SET;
|
272
|
break;
|
273
|
case VFS_SEEK_CUR:
|
274
|
gwhence = G_SEEK_CUR;
|
275
|
break;
|
276
|
case VFS_SEEK_END:
|
277
|
gwhence = G_SEEK_END;
|
278
|
break;
|
279
|
default:
|
280
|
AUDERR ("Cannot seek within %s: invalid whence.\n", (const char *) m_filename);
|
281
|
return -1;
|
282
|
}
|
283
|
|
284
|
g_seekable_seek (m_seekable, offset, gwhence, nullptr, & error);
|
285
|
CHECK_ERROR ("seek within", m_filename);
|
286
|
|
287
|
return 0;
|
288
|
|
289
|
FAILED:
|
290
|
return -1;
|
291
|
}
|
292
|
|
293
|
int64_t GIOFile::ftell ()
|
294
|
{
|
295
|
return g_seekable_tell (m_seekable);
|
296
|
}
|
297
|
|
298
|
int GIOFile::getc ()
|
299
|
{
|
300
|
unsigned char c;
|
301
|
return (fread (& c, 1, 1) == 1) ? c : -1;
|
302
|
}
|
303
|
|
304
|
int GIOFile::ungetc (int c)
|
305
|
{
|
306
|
return (! fseek (-1, VFS_SEEK_CUR)) ? c : -1;
|
307
|
}
|
308
|
|
309
|
bool GIOFile::feof ()
|
310
|
{
|
311
|
int test = getc ();
|
312
|
|
313
|
if (test < 0)
|
314
|
return TRUE;
|
315
|
|
316
|
ungetc (test);
|
317
|
return FALSE;
|
318
|
}
|
319
|
|
320
|
int GIOFile::ftruncate (int64_t length)
|
321
|
{
|
322
|
GError * error = 0;
|
323
|
|
324
|
g_seekable_truncate (m_seekable, length, nullptr, & error);
|
325
|
CHECK_ERROR ("truncate", m_filename);
|
326
|
|
327
|
return 0;
|
328
|
|
329
|
FAILED:
|
330
|
return -1;
|
331
|
}
|
332
|
|
333
|
int64_t GIOFile::fsize ()
|
334
|
{
|
335
|
GError * error = 0;
|
336
|
int64_t size;
|
337
|
|
338
|
|
339
|
|
340
|
|
341
|
|
342
|
if (! g_seekable_can_seek (m_seekable))
|
343
|
return -1;
|
344
|
|
345
|
GFileInfo * info = g_file_query_info (m_file,
|
346
|
G_FILE_ATTRIBUTE_STANDARD_SIZE, (GFileQueryInfoFlags) 0, 0, & error);
|
347
|
CHECK_ERROR ("get size of", m_filename);
|
348
|
|
349
|
size = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_SIZE);
|
350
|
|
351
|
g_object_unref (info);
|
352
|
return size;
|
353
|
|
354
|
FAILED:
|
355
|
return -1;
|
356
|
}
|
357
|
|
358
|
int GIOFile::fflush ()
|
359
|
{
|
360
|
int result;
|
361
|
GError * error = nullptr;
|
362
|
|
363
|
if (! m_ostream)
|
364
|
return 0;
|
365
|
|
366
|
result = g_output_stream_flush (m_ostream, nullptr, & error);
|
367
|
CHECK_ERROR ("flush", m_filename);
|
368
|
|
369
|
return result;
|
370
|
|
371
|
FAILED:
|
372
|
return -1;
|
373
|
}
|