Project

General

Profile

gio_v3.6jwt.cc

Jim Turner, March 06, 2015 19:39

 
1
/*
2
 * GIO Transport Plugin for Audacious
3
 * Copyright 2009-2012 John Lindgren
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions are met:
7
 *
8
 * 1. Redistributions of source code must retain the above copyright notice,
9
 *    this list of conditions, and the following disclaimer.
10
 *
11
 * 2. Redistributions in binary form must reproduce the above copyright notice,
12
 *    this list of conditions, and the following disclaimer in the documentation
13
 *    provided with the distribution.
14
 *
15
 * This software is provided "as is" and without any warranty, express or
16
 * implied. In no event shall the authors be liable for any damages arising from
17
 * the use of this software.
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
    // exception
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);   /* JWT:ADDED TO HANDLE INPUT FROM stdin */
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;   /* JWT: CAN'T SEEK ON STDIN!! */
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
    /* Audacious core expects one of two cases:
339
     *  1) File size is known and file is seekable.
340
     *  2) File size is unknown and file is not seekable.
341
     * Therefore, we return -1 for size if file is not seekable. */
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;  /* no-op */
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
}