Audacious  $Id:Doxyfile42802007-03-2104:39:00Znenolod$
vfs.c
Go to the documentation of this file.
00001 /*  Audacious
00002  *  Copyright (c) 2006-2007 William Pitcock
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; under version 3 of the License.
00007  *
00008  *  This program is distributed in the hope that it will be useful,
00009  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00010  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011  *  GNU General Public License for more details.
00012  *
00013  *  You should have received a copy of the GNU General Public License
00014  *  along with this program.  If not, see <http://www.gnu.org/licenses>.
00015  *
00016  *  The Audacious team does not consider modular code linking to
00017  *  Audacious or using our public API to be a derived work.
00018  */
00019 
00020 #include <inttypes.h>
00021 
00022 #include "vfs.h"
00023 #include "audstrings.h"
00024 #include <stdio.h>
00025 #include <unistd.h>
00026 #include <sys/stat.h>
00027 #include <sys/types.h>
00028 #include <string.h>
00029 
00030 #include <mowgli.h>
00031 
00032 /* Audacious core provides us with a function that looks up a VFS transport for
00033  * a given URI scheme.  Since this function will load plugins as needed, it can
00034  * only be called from the main thread.  When VFS is used from parallel threads,
00035  * vfs_prepare must be called from the main thread to look up any needed
00036  * transports beforehand. */
00037 
00038 typedef struct {
00039     VFSConstructor * transport;
00040     gboolean prepared;
00041 } LookupNode;
00042 
00043 static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
00044 static GThread * lookup_thread = NULL;
00045 static VFSConstructor * (* lookup_func) (const gchar * scheme) = NULL;
00046 static mowgli_patricia_t * lookup_table = NULL;
00047 
00048 void vfs_set_lookup_func (VFSConstructor * (* func) (const gchar * scheme))
00049 {
00050     g_static_mutex_lock (& mutex);
00051 
00052     lookup_thread = g_thread_self ();
00053     lookup_func = func;
00054 
00055     if (! lookup_table)
00056         lookup_table = mowgli_patricia_create (NULL);
00057 
00058     g_static_mutex_unlock (& mutex);
00059 }
00060 
00061 static VFSConstructor * do_lookup (const gchar * scheme, gboolean prepare)
00062 {
00063     g_return_val_if_fail (lookup_thread && lookup_func && lookup_table, NULL);
00064 
00065     LookupNode * node = mowgli_patricia_retrieve (lookup_table, scheme);
00066     if (! node)
00067     {
00068         node = g_slice_new (LookupNode);
00069         node->transport = NULL;
00070         node->prepared = FALSE;
00071         mowgli_patricia_add (lookup_table, scheme, node);
00072     }
00073 
00074     if (prepare)
00075         node->prepared = TRUE;
00076 
00077     /* vfs_prepare can only be called from the main thread.  vfs_fopen can only
00078      * be called from the main thread unless vfs_prepare has been called. */
00079     if (prepare || ! node->prepared)
00080         g_return_val_if_fail (g_thread_self () == lookup_thread, NULL);
00081 
00082     /* do the actual lookup if needed, but only in the main thread */
00083     if (! node->transport && g_thread_self () == lookup_thread)
00084     {
00085         node->transport = lookup_func (scheme);
00086         /* This is normal for custom URI schemes.
00087         if (! node->transport)
00088             fprintf (stderr, "No transport plugin found for URI scheme \"%s\".\n", scheme); */
00089     }
00090 
00091     return node->transport;
00092 }
00093 
00094 void vfs_prepare (const gchar * scheme)
00095 {
00096     g_static_mutex_lock (& mutex);
00097     do_lookup (scheme, TRUE);
00098     g_static_mutex_unlock (& mutex);
00099 }
00100 
00101 void vfs_prepare_filename (const gchar * path)
00102 {
00103     const gchar * s = strstr (path, "://");
00104     g_return_if_fail (s);
00105     gchar scheme[s - path + 1];
00106     strncpy (scheme, path, s - path);
00107     scheme[s - path] = 0;
00108 
00109     vfs_prepare (scheme);
00110 }
00111 
00112 static gboolean verbose = FALSE;
00113 
00114 void vfs_set_verbose (gboolean set)
00115 {
00116     verbose = set;
00117 }
00118 
00119 static void logger (const gchar * format, ...)
00120 {
00121     static gchar last[256] = "";
00122     static gint repeated = 0;
00123 
00124     gchar buf[256];
00125 
00126     va_list args;
00127     va_start (args, format);
00128     vsnprintf (buf, sizeof buf, format, args);
00129     va_end (args);
00130 
00131     if (! strcmp (buf, last))
00132         repeated ++;
00133     else
00134     {
00135         if (repeated)
00136         {
00137             printf ("VFS: (last message repeated %d times)\n", repeated);
00138             repeated = 0;
00139         }
00140         
00141         fputs (buf, stdout);
00142         strcpy (last, buf);
00143     }
00144 }
00145 
00154 VFSFile *
00155 vfs_fopen(const gchar * path,
00156           const gchar * mode)
00157 {
00158     g_return_val_if_fail (path && mode, NULL);
00159     g_return_val_if_fail (lookup_func, NULL);
00160 
00161     VFSFile *file;
00162     VFSConstructor *vtable = NULL;
00163 
00164     const gchar * s = strstr (path, "://");
00165     g_return_val_if_fail (s, NULL);
00166     gchar scheme[s - path + 1];
00167     strncpy (scheme, path, s - path);
00168     scheme[s - path] = 0;
00169 
00170     g_static_mutex_lock (& mutex);
00171     vtable = do_lookup (scheme, FALSE);
00172     g_static_mutex_unlock (& mutex);
00173 
00174     if (! vtable)
00175         return NULL;
00176 
00177     file = vtable->vfs_fopen_impl(path, mode);
00178 
00179     if (verbose)
00180         logger ("VFS: <%p> open (mode %s) %s\n", file, mode, path);
00181 
00182     if (file == NULL)
00183         return NULL;
00184 
00185     file->uri = g_strdup(path);
00186     file->base = vtable;
00187     file->ref = 1;
00188     file->sig = VFS_SIG;
00189 
00190     return file;
00191 }
00192 
00199 gint
00200 vfs_fclose(VFSFile * file)
00201 {
00202     g_return_val_if_fail (file && file->sig == VFS_SIG, -1);
00203 
00204     if (verbose)
00205         printf ("VFS: <%p> close\n", file);
00206 
00207     gint ret = 0;
00208 
00209     if (--file->ref > 0)
00210         return -1;
00211 
00212     if (file->base->vfs_fclose_impl(file) != 0)
00213         ret = -1;
00214 
00215     g_free(file->uri);
00216 
00217     memset (file, 0, sizeof (VFSFile));
00218     g_free (file);
00219 
00220     return ret;
00221 }
00222 
00232 gint64 vfs_fread (void * ptr, gint64 size, gint64 nmemb, VFSFile * file)
00233 {
00234     g_return_val_if_fail (file && file->sig == VFS_SIG, 0);
00235 
00236     gint64 readed = file->base->vfs_fread_impl (ptr, size, nmemb, file);
00237     
00238     if (verbose)
00239         logger ("VFS: <%p> read %"PRId64" elements of size %"PRId64" = "
00240          "%"PRId64"\n", file, nmemb, size, readed);
00241 
00242     return readed;
00243 }
00244 
00254 gint64 vfs_fwrite (const void * ptr, gint64 size, gint64 nmemb, VFSFile * file)
00255 {
00256     g_return_val_if_fail (file && file->sig == VFS_SIG, 0);
00257 
00258     gint64 written = file->base->vfs_fwrite_impl (ptr, size, nmemb, file);
00259 
00260     if (verbose)
00261         logger ("VFS: <%p> write %"PRId64" elements of size %"PRId64" = "
00262          "%"PRId64"\n", file, nmemb, size, written);
00263 
00264     return written;
00265 }
00266 
00273 gint
00274 vfs_getc(VFSFile *file)
00275 {
00276     g_return_val_if_fail (file && file->sig == VFS_SIG, EOF);
00277 
00278     if (verbose)
00279         logger ("VFS: <%p> getc\n", file);
00280 
00281     return file->base->vfs_getc_impl(file);
00282 }
00283 
00291 gint
00292 vfs_ungetc(gint c, VFSFile *file)
00293 {
00294     g_return_val_if_fail (file && file->sig == VFS_SIG, EOF);
00295 
00296     if (verbose)
00297         logger ("VFS: <%p> ungetc\n", file);
00298 
00299     return file->base->vfs_ungetc_impl(c, file);
00300 }
00301 
00315 gint
00316 vfs_fseek(VFSFile * file,
00317           gint64 offset,
00318           gint whence)
00319 {
00320     g_return_val_if_fail (file && file->sig == VFS_SIG, -1);
00321 
00322     if (verbose)
00323         logger ("VFS: <%p> seek to %"PRId64" from %s\n", file, offset, whence ==
00324          SEEK_CUR ? "current" : whence == SEEK_SET ? "beginning" : whence ==
00325          SEEK_END ? "end" : "invalid");
00326 
00327     return file->base->vfs_fseek_impl(file, offset, whence);
00328 }
00329 
00335 void
00336 vfs_rewind(VFSFile * file)
00337 {
00338     g_return_if_fail (file && file->sig == VFS_SIG);
00339 
00340     if (verbose)
00341         logger ("VFS: <%p> rewind\n", file);
00342 
00343     file->base->vfs_rewind_impl(file);
00344 }
00345 
00352 gint64
00353 vfs_ftell(VFSFile * file)
00354 {
00355     g_return_val_if_fail (file && file->sig == VFS_SIG, -1);
00356 
00357     gint64 told = file->base->vfs_ftell_impl (file);
00358 
00359     if (verbose)
00360         logger ("VFS: <%p> tell = %"PRId64"\n", file, told);
00361 
00362     return told;
00363 }
00364 
00371 gboolean
00372 vfs_feof(VFSFile * file)
00373 {
00374     g_return_val_if_fail (file && file->sig == VFS_SIG, TRUE);
00375 
00376     gboolean eof = file->base->vfs_feof_impl (file);
00377 
00378     if (verbose)
00379         logger ("VFS: <%p> eof = %s\n", file, eof ? "yes" : "no");
00380 
00381     return eof;
00382 }
00383 
00391 gint vfs_ftruncate (VFSFile * file, gint64 length)
00392 {
00393     g_return_val_if_fail (file && file->sig == VFS_SIG, -1);
00394 
00395     if (verbose)
00396         logger ("VFS: <%p> truncate to %"PRId64"\n", file, length);
00397 
00398     return file->base->vfs_ftruncate_impl(file, length);
00399 }
00400 
00407 gint64 vfs_fsize (VFSFile * file)
00408 {
00409     g_return_val_if_fail (file && file->sig == VFS_SIG, -1);
00410 
00411     gint64 size = file->base->vfs_fsize_impl (file);
00412 
00413     if (verbose)
00414         logger ("VFS: <%p> size = %"PRId64"\n", file, size);
00415 
00416     return size;
00417 }
00418 
00426 gchar *
00427 vfs_get_metadata(VFSFile * file, const gchar * field)
00428 {
00429     if (file == NULL)
00430         return NULL;
00431 
00432     if (file->base->vfs_get_metadata_impl)
00433         return file->base->vfs_get_metadata_impl(file, field);
00434     return NULL;
00435 }
00436 
00444 gboolean
00445 vfs_file_test(const gchar * path, GFileTest test)
00446 {
00447     if (strncmp (path, "file://", 7))
00448         return FALSE; /* only local files are handled */
00449 
00450     gchar * path2 = uri_to_filename (path);
00451 
00452     if (path2 == NULL)
00453         path2 = g_strdup(path);
00454 
00455     gboolean ret = g_file_test (path2, test);
00456 
00457     g_free(path2);
00458 
00459     return ret;
00460 }
00461 
00468 gboolean
00469 vfs_is_writeable(const gchar * path)
00470 {
00471     struct stat info;
00472     gchar * realfn = uri_to_filename (path);
00473 
00474     if (stat(realfn, &info) == -1)
00475         return FALSE;
00476 
00477     g_free(realfn);
00478 
00479     return (info.st_mode & S_IWUSR);
00480 }
00481 
00491 VFSFile *
00492 vfs_dup(VFSFile *in)
00493 {
00494     g_return_val_if_fail(in != NULL, NULL);
00495 
00496     in->ref++;
00497 
00498     return in;
00499 }
00500 
00507 gboolean
00508 vfs_is_remote(const gchar * path)
00509 {
00510     return strncasecmp (path, "file://", 7) ? TRUE : FALSE;
00511 }
00512 
00519 gboolean
00520 vfs_is_streaming(VFSFile *file)
00521 {
00522     off_t size = 0;
00523 
00524     if (file == NULL)
00525         return FALSE;
00526 
00527     size = file->base->vfs_fsize_impl(file);
00528 
00529     if (size == -1)
00530         return TRUE;
00531     else
00532         return FALSE;
00533 }