nkeynes@1034: /** nkeynes@1035: * $Id$ nkeynes@1034: * nkeynes@1034: * VMU management - maintains a list of all known VMUs nkeynes@1034: * nkeynes@1034: * Copyright (c) 2009 Nathan Keynes. nkeynes@1034: * nkeynes@1034: * This program is free software; you can redistribute it and/or modify nkeynes@1034: * it under the terms of the GNU General Public License as published by nkeynes@1034: * the Free Software Foundation; either version 2 of the License, or nkeynes@1034: * (at your option) any later version. nkeynes@1034: * nkeynes@1034: * This program is distributed in the hope that it will be useful, nkeynes@1034: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@1034: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@1034: * GNU General Public License for more details. nkeynes@1034: */ nkeynes@1034: nkeynes@1034: #include nkeynes@1034: #include nkeynes@1034: #include nkeynes@1034: #include nkeynes@1034: #include "vmulist.h" nkeynes@1034: #include "config.h" nkeynes@1034: nkeynes@1034: DEFINE_HOOK(vmulist_change_hook, vmulist_change_hook_t); nkeynes@1034: nkeynes@1034: typedef struct vmulist_entry { nkeynes@1034: const gchar *filename; nkeynes@1034: vmu_volume_t vol; nkeynes@1034: int attach_count; nkeynes@1034: } *vmulist_entry_t; nkeynes@1034: nkeynes@1034: /** nkeynes@1034: * Doubly-linked list of vmulist_entry_t maintained in sorted order by display name. nkeynes@1034: * Could be augmented with a hashtable if it gets too long nkeynes@1034: */ nkeynes@1034: static GList *vmu_list; nkeynes@1034: nkeynes@1034: #define ENTRY(it) ((vmulist_entry_t)(it)->data) nkeynes@1034: #define VOLUME(it) (ENTRY(it)->vol) nkeynes@1034: #define DISPLAY_NAME(it) vmulist_display_name(ENTRY(it)) nkeynes@1034: nkeynes@1034: static const char *vmulist_display_name(vmulist_entry_t ent) nkeynes@1034: { nkeynes@1034: if( ent->filename == NULL ) { nkeynes@1034: return NULL; nkeynes@1034: } nkeynes@1034: const char *s = strrchr(ent->filename, '/' ); nkeynes@1034: if( s == NULL || *(s+1) == '\0' ) { nkeynes@1034: return ent->filename; nkeynes@1034: } else { nkeynes@1034: return s+1; nkeynes@1034: } nkeynes@1034: } nkeynes@1034: nkeynes@1034: static void vmulist_update_config( void ) nkeynes@1034: { nkeynes@1034: GList *temp = NULL, *it; nkeynes@1034: nkeynes@1034: for( it = vmu_list; it != NULL; it = g_list_next(it) ) { nkeynes@1034: vmulist_entry_t entry = ENTRY(it); nkeynes@1034: temp = g_list_append( temp, (char *)entry->filename ); nkeynes@1034: } nkeynes@1034: lxdream_set_global_config_list_value( CONFIG_VMU, temp ); nkeynes@1034: g_list_free( temp ); nkeynes@1034: } nkeynes@1034: nkeynes@1034: static vmulist_entry_t vmulist_get_entry_by_name( const gchar *name ) nkeynes@1034: { nkeynes@1034: GList *it; nkeynes@1034: for( it = vmu_list; it != NULL; it = g_list_next(it) ) { nkeynes@1034: const gchar *vmu_name = DISPLAY_NAME(it); nkeynes@1034: if( name == NULL ? vmu_name == NULL : vmu_name != NULL && strcmp( vmu_name, name ) == 0 ) { nkeynes@1034: return ENTRY(it); nkeynes@1034: } nkeynes@1034: } nkeynes@1034: return NULL; // not found nkeynes@1034: } nkeynes@1034: nkeynes@1034: static vmulist_entry_t vmulist_get_entry_by_filename( const gchar *name ) nkeynes@1034: { nkeynes@1034: GList *it; nkeynes@1034: for( it = vmu_list; it != NULL; it = g_list_next(it) ) { nkeynes@1034: const gchar *vmu_name = ENTRY(it)->filename; nkeynes@1034: if( name == NULL ? vmu_name == NULL : vmu_name != NULL && strcmp( vmu_name, name ) == 0 ) { nkeynes@1034: return ENTRY(it); nkeynes@1034: } nkeynes@1034: } nkeynes@1034: return NULL; // not found nkeynes@1034: } nkeynes@1034: nkeynes@1034: static vmulist_entry_t vmulist_get_entry_by_volume( vmu_volume_t vol ) nkeynes@1034: { nkeynes@1034: GList *it; nkeynes@1034: for( it = vmu_list; it != NULL; it = g_list_next(it) ) { nkeynes@1034: if( VOLUME(it) == vol ) { nkeynes@1034: return ENTRY(it); nkeynes@1034: } nkeynes@1034: } nkeynes@1034: return NULL; // not found nkeynes@1034: } nkeynes@1034: nkeynes@1034: static vmulist_entry_t vmulist_get_entry_by_index( unsigned int index ) nkeynes@1034: { nkeynes@1034: return (vmulist_entry_t)g_list_nth_data(vmu_list,index); nkeynes@1034: } nkeynes@1034: nkeynes@1034: static gint vmulist_display_name_compare( gconstpointer a, gconstpointer b ) nkeynes@1034: { nkeynes@1034: const char *aname = vmulist_display_name((vmulist_entry_t)a); nkeynes@1034: const char *bname = vmulist_display_name((vmulist_entry_t)b); nkeynes@1034: if( aname == bname ) nkeynes@1034: return 0; nkeynes@1034: if( aname == NULL ) nkeynes@1034: return -1; nkeynes@1034: if( bname == NULL ) nkeynes@1034: return 1; nkeynes@1034: return strcmp(aname,bname); nkeynes@1034: } nkeynes@1034: nkeynes@1034: /** nkeynes@1034: * Add a new entry into the list, maintaining the sorted order. nkeynes@1034: * If the filename is already in the list, it is updated instead. nkeynes@1034: */ nkeynes@1034: static vmulist_entry_t vmulist_add_entry( const gchar *filename, vmu_volume_t vol ) nkeynes@1034: { nkeynes@1034: vmulist_entry_t entry = vmulist_get_entry_by_filename(filename); nkeynes@1034: if( entry == NULL ) { nkeynes@1034: entry = g_malloc( sizeof(struct vmulist_entry) ); nkeynes@1034: entry->filename = g_strdup(filename); nkeynes@1034: entry->vol = vol; nkeynes@1034: vmu_list = g_list_insert_sorted(vmu_list, entry, vmulist_display_name_compare ); nkeynes@1071: vmulist_update_config(); nkeynes@1071: nkeynes@1071: CALL_HOOKS( vmulist_change_hook, VMU_ADDED, g_list_index(vmu_list,entry) ); nkeynes@1034: } else { nkeynes@1034: if( entry->vol != vol && entry->vol != NULL ) nkeynes@1034: vmu_volume_destroy( entry->vol ); nkeynes@1034: entry->vol = vol; nkeynes@1034: /* NOTE: at the moment this can't require a resort, but if we allow nkeynes@1034: * user-editable display names it will nkeynes@1034: */ nkeynes@1034: } nkeynes@1034: entry->attach_count = 0; nkeynes@1034: nkeynes@1034: return entry; nkeynes@1034: } nkeynes@1034: nkeynes@1034: static void vmulist_remove_entry( vmulist_entry_t entry ) nkeynes@1034: { nkeynes@1034: int idx = g_list_index(vmu_list, entry); nkeynes@1034: vmu_list = g_list_remove( vmu_list, entry ); nkeynes@1034: g_free( (char *)entry->filename ); nkeynes@1034: g_free( entry ); nkeynes@1034: vmulist_update_config(); nkeynes@1034: CALL_HOOKS( vmulist_change_hook, VMU_REMOVED, idx ); nkeynes@1034: } nkeynes@1034: nkeynes@1034: static unsigned int vmulist_get_index( vmulist_entry_t entry ) nkeynes@1034: { nkeynes@1034: return g_list_index( vmu_list, entry ); nkeynes@1034: } nkeynes@1034: nkeynes@1034: int vmulist_add_vmu( const gchar *filename, vmu_volume_t vol ) nkeynes@1034: { nkeynes@1034: vmulist_entry_t entry = vmulist_add_entry( filename, vol ); nkeynes@1034: return vmulist_get_index(entry); nkeynes@1034: } nkeynes@1034: nkeynes@1034: void vmulist_remove_vmu( vmu_volume_t vol ) nkeynes@1034: { nkeynes@1034: vmulist_entry_t entry = vmulist_get_entry_by_volume(vol); nkeynes@1034: if( entry != NULL ) { nkeynes@1034: vmulist_remove_entry(entry); nkeynes@1034: } nkeynes@1034: } nkeynes@1034: nkeynes@1034: const char *vmulist_get_name( unsigned int idx ) nkeynes@1034: { nkeynes@1034: vmulist_entry_t entry = vmulist_get_entry_by_index(idx); nkeynes@1034: if( entry != NULL ) { nkeynes@1034: return vmulist_display_name(entry); nkeynes@1034: } nkeynes@1034: return NULL; nkeynes@1034: } nkeynes@1034: nkeynes@1034: const char *vmulist_get_filename( unsigned int idx ) nkeynes@1034: { nkeynes@1034: vmulist_entry_t entry = vmulist_get_entry_by_index(idx); nkeynes@1034: if( entry != NULL ) { nkeynes@1034: return entry->filename; nkeynes@1034: } nkeynes@1034: return NULL; nkeynes@1034: } nkeynes@1034: nkeynes@1034: const char *vmulist_get_volume_name( vmu_volume_t vol ) nkeynes@1034: { nkeynes@1034: vmulist_entry_t entry = vmulist_get_entry_by_volume(vol); nkeynes@1034: if( entry != NULL ) { nkeynes@1034: return entry->filename; nkeynes@1034: } nkeynes@1034: return NULL; nkeynes@1034: } nkeynes@1034: nkeynes@1034: vmu_volume_t vmulist_get_vmu( unsigned int idx ) nkeynes@1034: { nkeynes@1034: vmulist_entry_t entry = vmulist_get_entry_by_index(idx); nkeynes@1034: if( entry != NULL ) { nkeynes@1034: if( entry->vol == NULL ) { nkeynes@1034: entry->vol = vmu_volume_load(entry->filename); nkeynes@1034: } nkeynes@1034: return entry->vol; nkeynes@1034: } nkeynes@1034: return NULL; nkeynes@1034: } nkeynes@1034: nkeynes@1034: vmu_volume_t vmulist_get_vmu_by_name( const gchar *name ) nkeynes@1034: { nkeynes@1034: vmulist_entry_t entry = vmulist_get_entry_by_name(name); nkeynes@1034: if( entry != NULL ) { nkeynes@1034: if( entry->vol == NULL ) { nkeynes@1034: entry->vol = vmu_volume_load(entry->filename); nkeynes@1034: } nkeynes@1034: return entry->vol; nkeynes@1034: } nkeynes@1034: return NULL; nkeynes@1034: } nkeynes@1034: nkeynes@1034: vmu_volume_t vmulist_get_vmu_by_filename( const gchar *name ) nkeynes@1034: { nkeynes@1034: vmulist_entry_t entry = vmulist_get_entry_by_filename(name); nkeynes@1034: if( entry != NULL ) { nkeynes@1034: if( entry->vol == NULL ) { nkeynes@1034: entry->vol = vmu_volume_load(entry->filename); nkeynes@1034: } nkeynes@1034: return entry->vol; nkeynes@1034: } else { nkeynes@1034: vmu_volume_t vol = vmu_volume_load( name ); nkeynes@1034: vmulist_add_entry( name, vol ); nkeynes@1034: return vol; nkeynes@1034: } nkeynes@1034: } nkeynes@1034: nkeynes@1034: int vmulist_get_index_by_filename( const gchar *name ) nkeynes@1034: { nkeynes@1034: vmulist_entry_t entry = vmulist_get_entry_by_filename(name); nkeynes@1034: if( entry != NULL ) { nkeynes@1034: return g_list_index( vmu_list, entry ); nkeynes@1034: } nkeynes@1034: return -1; nkeynes@1034: } nkeynes@1034: nkeynes@1034: nkeynes@1034: int vmulist_create_vmu( const gchar *filename, gboolean create_only ) nkeynes@1034: { nkeynes@1034: vmu_volume_t vol = vmu_volume_new_default(filename); nkeynes@1034: nkeynes@1034: if( vmu_volume_save( filename, vol, create_only ) ) { nkeynes@1034: return vmulist_add_vmu( filename, vol ); nkeynes@1034: } else { nkeynes@1034: vmu_volume_destroy(vol); nkeynes@1034: } nkeynes@1034: return -1; nkeynes@1034: } nkeynes@1034: nkeynes@1034: gboolean vmulist_attach_vmu( vmu_volume_t vol, const gchar *where ) nkeynes@1034: { nkeynes@1034: vmulist_entry_t entry = vmulist_get_entry_by_volume(vol); nkeynes@1034: if( entry == NULL ) { nkeynes@1034: return FALSE; nkeynes@1034: } nkeynes@1034: entry->attach_count++; nkeynes@1034: return TRUE; nkeynes@1034: } nkeynes@1034: nkeynes@1034: void vmulist_detach_vmu( vmu_volume_t vol ) nkeynes@1034: { nkeynes@1034: vmulist_entry_t entry = vmulist_get_entry_by_volume(vol); nkeynes@1034: if( entry != NULL && entry->attach_count > 0 ) { nkeynes@1034: entry->attach_count--; nkeynes@1034: } nkeynes@1034: } nkeynes@1034: nkeynes@1034: unsigned int vmulist_get_size(void) nkeynes@1034: { nkeynes@1034: return g_list_length(vmu_list); nkeynes@1034: } nkeynes@1034: nkeynes@1034: void vmulist_init( void ) nkeynes@1034: { nkeynes@1034: GList *filenames = lxdream_get_global_config_list_value( CONFIG_VMU ); nkeynes@1034: GList *ptr; nkeynes@1034: for( ptr = filenames; ptr != NULL; ptr = g_list_next(ptr) ) { nkeynes@1034: vmulist_add_entry( (gchar *)ptr->data, NULL ); nkeynes@1034: g_free( ptr->data ); nkeynes@1034: } nkeynes@1034: g_list_free( filenames ); nkeynes@1034: } nkeynes@1034: nkeynes@1034: void vmulist_save_all( void ) nkeynes@1034: { nkeynes@1034: GList *it; nkeynes@1034: for( it = vmu_list; it != NULL; it = g_list_next(it) ) { nkeynes@1034: vmulist_entry_t entry = ENTRY(it); nkeynes@1034: if( entry->vol != NULL && vmu_volume_is_dirty(entry->vol) ) { nkeynes@1034: vmu_volume_save(entry->filename, entry->vol, FALSE); nkeynes@1034: } nkeynes@1034: } nkeynes@1034: } nkeynes@1034: nkeynes@1034: void vmulist_shutdown( void ) nkeynes@1034: { nkeynes@1034: vmulist_save_all(); nkeynes@1034: }