diff options
author | James Meyer <james.meyer@operamail.com> | 2008-10-20 19:43:05 (GMT) |
---|---|---|
committer | James Meyer <james.meyer@operamail.com> | 2008-10-20 19:43:05 (GMT) |
commit | 917f8e0bc9dcb481b5a44c19ca8348da1e241d27 (patch) | |
tree | bc95dfebebf61ca8dd5c62467509b8a951be8aef /abs/chroot-devel/pcmanfm | |
parent | ae1c36ce00971a8ce8c634006984f2de3d4033a7 (diff) | |
download | linhes_pkgbuild-917f8e0bc9dcb481b5a44c19ca8348da1e241d27.zip linhes_pkgbuild-917f8e0bc9dcb481b5a44c19ca8348da1e241d27.tar.gz linhes_pkgbuild-917f8e0bc9dcb481b5a44c19ca8348da1e241d27.tar.bz2 |
add pcmanfm to chroot-devel
Diffstat (limited to 'abs/chroot-devel/pcmanfm')
-rw-r--r-- | abs/chroot-devel/pcmanfm/PKGBUILD | 21 | ||||
-rw-r--r-- | abs/chroot-devel/pcmanfm/pcmanfm.install | 12 | ||||
-rw-r--r-- | abs/chroot-devel/pcmanfm/vfs-file-info.c | 699 |
3 files changed, 732 insertions, 0 deletions
diff --git a/abs/chroot-devel/pcmanfm/PKGBUILD b/abs/chroot-devel/pcmanfm/PKGBUILD new file mode 100644 index 0000000..a82890d --- /dev/null +++ b/abs/chroot-devel/pcmanfm/PKGBUILD @@ -0,0 +1,21 @@ +# Maintainer: Geoffroy Carrier <geoffroy.carrier@koon.fr> +pkgname=pcmanfm +pkgver=0.5 +pkgrel=3 +pkgdesc="File manager of the LXDE Desktop" +arch=('i686' 'x86_64') +url="http://pcmanfm.sourceforge.net/" +license=('GPL') +groups=('lxde') +depends=('gtk2' 'shared-mime-info' 'desktop-file-utils' 'startup-notification' 'gamin') +makedepends=('intltool' 'pkgconfig') +install=pcmanfm.install +source=(http://downloads.sourceforge.net/pcmanfm/pcmanfm-$pkgver.tar.gz) +build() { + cd "$srcdir/$pkgname-$pkgver" + cp $startdir/vfs-file-info.c src/vfs/ + + ./configure --sysconfdir=/etc --prefix=/usr --disable-hal || return 1 + make || return 1 + make DESTDIR="$pkgdir" install || return 1 +} diff --git a/abs/chroot-devel/pcmanfm/pcmanfm.install b/abs/chroot-devel/pcmanfm/pcmanfm.install new file mode 100644 index 0000000..1182e76 --- /dev/null +++ b/abs/chroot-devel/pcmanfm/pcmanfm.install @@ -0,0 +1,12 @@ +post_install() { + update-mime-database usr/share/mime > /dev/null + update-desktop-database -q +} + +post_upgrade() { + post_install +} + +post_remove() { + post_install +} diff --git a/abs/chroot-devel/pcmanfm/vfs-file-info.c b/abs/chroot-devel/pcmanfm/vfs-file-info.c new file mode 100644 index 0000000..a397893 --- /dev/null +++ b/abs/chroot-devel/pcmanfm/vfs-file-info.c @@ -0,0 +1,699 @@ +/* +* C Implementation: vfs-file-info +* +* Description: File information +* +* +* Author: Hong Jen Yee (PCMan) <pcman.tw (AT) gmail.com>, (C) 2006 +* +* Copyright: See COPYING file that comes with this distribution +* +*/ + +#include "vfs-file-info.h" +#include <glib.h> +#include "glib-mem.h" +#include <glib/gi18n.h> +#include <grp.h> /* Query group name */ +#include <pwd.h> /* Query user name */ +#include <string.h> + +#include "vfs-app-desktop.h" +#include "vfs-thumbnail-loader.h" +#include "vfs-utils.h" /* for vfs_load_icon */ + +static int big_thumb_size = 48, small_thumb_size = 20; +static gboolean utf8_file_name = FALSE; + +void vfs_file_info_set_utf8_filename( gboolean is_utf8 ) +{ + utf8_file_name = is_utf8; +} + +VFSFileInfo* vfs_file_info_new () +{ + VFSFileInfo * fi = g_slice_new0( VFSFileInfo ); + fi->n_ref = 1; + return fi; +} + +static void vfs_file_info_clear( VFSFileInfo* fi ) +{ + if ( fi->disp_name && fi->disp_name != fi->name ) + { + g_free( fi->disp_name ); + fi->disp_name = NULL; + } + if ( fi->name ) + { + g_free( fi->name ); + fi->name = NULL; + } + if ( fi->disp_size ) + { + g_free( fi->disp_size ); + fi->disp_size = NULL; + } + if ( fi->disp_owner ) + { + g_free( fi->disp_owner ); + fi->disp_owner = NULL; + } + if ( fi->disp_mtime ) + { + g_free( fi->disp_mtime ); + fi->disp_mtime = NULL; + } + if ( fi->big_thumbnail ) + { + gdk_pixbuf_unref( fi->big_thumbnail ); + fi->big_thumbnail = NULL; + } + if ( fi->small_thumbnail ) + { + gdk_pixbuf_unref( fi->small_thumbnail ); + fi->small_thumbnail = NULL; + } + + fi->disp_perm[ 0 ] = '\0'; + + if ( fi->mime_type ) + { + vfs_mime_type_unref( fi->mime_type ); + fi->mime_type = NULL; + } + fi->flags = VFS_FILE_INFO_NONE; +} + +VFSFileInfo* vfs_file_info_ref( VFSFileInfo* fi ) +{ + g_atomic_int_inc( &fi->n_ref ); + return fi; +} + +void vfs_file_info_unref( VFSFileInfo* fi ) +{ + if ( g_atomic_int_dec_and_test( &fi->n_ref) ) + { + vfs_file_info_clear( fi ); + g_slice_free( VFSFileInfo, fi ); + } +} + +gboolean vfs_file_info_get( VFSFileInfo* fi, + const char* file_path, + const char* base_name ) +{ + struct stat file_stat; + vfs_file_info_clear( fi ); + + if ( base_name ) + fi->name = g_strdup( base_name ); + else + fi->name = g_path_get_basename( file_path ); + + if ( lstat( file_path, &file_stat ) == 0 ) + { + /* This is time-consuming but can save much memory */ + fi->mode = file_stat.st_mode; + fi->dev = file_stat.st_dev; + fi->uid = file_stat.st_uid; + fi->gid = file_stat.st_gid; + fi->size = file_stat.st_size; + fi->mtime = file_stat.st_mtime; + fi->atime = file_stat.st_atime; + fi->blksize = file_stat.st_blksize; + fi->blocks = file_stat.st_blocks; + + if ( G_LIKELY( utf8_file_name && g_utf8_validate ( fi->name, -1, NULL ) ) ) + { + fi->disp_name = fi->name; /* Don't duplicate the name and save memory */ + } + else + { + fi->disp_name = g_filename_display_name( fi->name ); + } + fi->mime_type = vfs_mime_type_get_from_file( file_path, + fi->disp_name, + &file_stat ); + return TRUE; + } + else + fi->mime_type = vfs_mime_type_get_from_type( XDG_MIME_TYPE_UNKNOWN ); + return FALSE; +} + +const char* vfs_file_info_get_name( VFSFileInfo* fi ) +{ + return fi->name; +} + +/* Get displayed name encoded in UTF-8 */ +const char* vfs_file_info_get_disp_name( VFSFileInfo* fi ) +{ + return fi->disp_name; +} + +void vfs_file_info_set_disp_name( VFSFileInfo* fi, const char* name ) +{ + if ( fi->disp_name && fi->disp_name != fi->name ) + g_free( fi->disp_name ); + fi->disp_name = g_strdup( name ); +} + +void vfs_file_info_set_name( VFSFileInfo* fi, const char* name ) +{ + g_free( fi->name ); + fi->name = g_strdup( name ); +} + +off_t vfs_file_info_get_size( VFSFileInfo* fi ) +{ + return fi->size; +} + +const char* vfs_file_info_get_disp_size( VFSFileInfo* fi ) +{ + if ( G_UNLIKELY( !fi->disp_size ) ) + { + char buf[ 64 ]; + vfs_file_size_to_string( buf, fi->size ); + fi->disp_size = g_strdup( buf ); + } + return fi->disp_size; +} + +off_t vfs_file_info_get_blocks( VFSFileInfo* fi ) +{ + return fi->blocks; +} + +VFSMimeType* vfs_file_info_get_mime_type( VFSFileInfo* fi ) +{ + vfs_mime_type_ref( fi->mime_type ); + return fi->mime_type; +} + +void vfs_file_info_reload_mime_type( VFSFileInfo* fi, + const char* full_path ) +{ + VFSMimeType * old_mime_type; + struct stat file_stat; + + /* convert VFSFileInfo to struct stat */ + /* In current implementation, only st_mode is used in + mime-type detection, so let's save some CPU cycles + and don't copy unused fields. + */ + file_stat.st_mode = fi->mode; + /* + file_stat.st_dev = fi->dev; + file_stat.st_uid = fi->uid; + file_stat.st_gid = fi->gid; + file_stat.st_size = fi->size; + file_stat.st_mtime = fi->mtime; + file_stat.st_atime = fi->atime; + file_stat.st_blksize = fi->blksize; + file_stat.st_blocks = fi->blocks; + */ + old_mime_type = fi->mime_type; + fi->mime_type = vfs_mime_type_get_from_file( full_path, + fi->name, &file_stat ); + vfs_file_info_load_special_info( fi, full_path ); + vfs_mime_type_unref( old_mime_type ); /* FIXME: is vfs_mime_type_unref needed ?*/ +} + +const char* vfs_file_info_get_mime_type_desc( VFSFileInfo* fi ) +{ + return vfs_mime_type_get_description( fi->mime_type ); +} + +GdkPixbuf* vfs_file_info_get_big_icon( VFSFileInfo* fi ) +{ + /* get special icons for special files, especially for + some desktop icons */ + + if ( G_UNLIKELY( fi->flags != VFS_FILE_INFO_NONE ) ) + { + int w, h; + int icon_size; + vfs_mime_type_get_icon_size( &icon_size, NULL ); + if ( fi->big_thumbnail ) + { + w = gdk_pixbuf_get_width( fi->big_thumbnail ); + h = gdk_pixbuf_get_height( fi->big_thumbnail ); + } + else + w = h = 0; + + if ( ABS( MAX( w, h ) - icon_size ) > 2 ) + { + char * icon_name = NULL; + if ( fi->big_thumbnail ) + { + icon_name = ( char* ) g_object_steal_data( + G_OBJECT(fi->big_thumbnail), "name" ); + gdk_pixbuf_unref( fi->big_thumbnail ); + fi->big_thumbnail = NULL; + } + if ( G_LIKELY( icon_name ) ) + { + if ( G_UNLIKELY( icon_name[ 0 ] == '/' ) ) + fi->big_thumbnail = gdk_pixbuf_new_from_file( icon_name, NULL ); + else + fi->big_thumbnail = vfs_load_icon( + gtk_icon_theme_get_default(), + icon_name, icon_size ); + } + if ( fi->big_thumbnail ) + g_object_set_data_full( G_OBJECT(fi->big_thumbnail), "name", icon_name, g_free ); + else + g_free( icon_name ); + } + return fi->big_thumbnail ? gdk_pixbuf_ref( fi->big_thumbnail ) : NULL; + } + if( G_UNLIKELY(!fi->mime_type) ) + return NULL; + return vfs_mime_type_get_icon( fi->mime_type, TRUE ); +} + +GdkPixbuf* vfs_file_info_get_small_icon( VFSFileInfo* fi ) +{ + return vfs_mime_type_get_icon( fi->mime_type, FALSE ); +} + +GdkPixbuf* vfs_file_info_get_big_thumbnail( VFSFileInfo* fi ) +{ + return fi->big_thumbnail ? gdk_pixbuf_ref( fi->big_thumbnail ) : NULL; +} + +GdkPixbuf* vfs_file_info_get_small_thumbnail( VFSFileInfo* fi ) +{ + return fi->small_thumbnail ? gdk_pixbuf_ref( fi->small_thumbnail ) : NULL; +} + +const char* vfs_file_info_get_disp_owner( VFSFileInfo* fi ) +{ + struct passwd * puser; + struct group* pgroup; + char uid_str_buf[ 32 ]; + char* user_name; + char gid_str_buf[ 32 ]; + char* group_name; + + /* FIXME: user names should be cached */ + if ( ! fi->disp_owner ) + { + puser = getpwuid( fi->uid ); + if ( puser && puser->pw_name && *puser->pw_name ) + user_name = puser->pw_name; + else + { + sprintf( uid_str_buf, "%d", fi->uid ); + user_name = uid_str_buf; + } + + pgroup = getgrgid( fi->gid ); + if ( pgroup && pgroup->gr_name && *pgroup->gr_name ) + group_name = pgroup->gr_name; + else + { + sprintf( gid_str_buf, "%d", fi->gid ); + group_name = gid_str_buf; + } + fi->disp_owner = g_strdup_printf ( "%s:%s", user_name, group_name ); + } + return fi->disp_owner; +} + +const char* vfs_file_info_get_disp_mtime( VFSFileInfo* fi ) +{ + if ( ! fi->disp_mtime ) + { + char buf[ 64 ]; + strftime( buf, sizeof( buf ), + "%Y-%m-%d %H:%M", + localtime( &fi->mtime ) ); + fi->disp_mtime = g_strdup( buf ); + } + return fi->disp_mtime; +} + +time_t* vfs_file_info_get_mtime( VFSFileInfo* fi ) +{ + return & fi->mtime; +} + +time_t* vfs_file_info_get_atime( VFSFileInfo* fi ) +{ + return & fi->atime; +} + +static void get_file_perm_string( char* perm, mode_t mode ) +{ + perm[ 0 ] = S_ISDIR( mode ) ? 'd' : ( S_ISLNK( mode ) ? 'l' : '-' ); + perm[ 1 ] = ( mode & S_IRUSR ) ? 'r' : '-'; + perm[ 2 ] = ( mode & S_IWUSR ) ? 'w' : '-'; + perm[ 3 ] = ( mode & S_IXUSR ) ? 'x' : '-'; + perm[ 4 ] = ( mode & S_IRGRP ) ? 'r' : '-'; + perm[ 5 ] = ( mode & S_IWGRP ) ? 'w' : '-'; + perm[ 6 ] = ( mode & S_IXGRP ) ? 'x' : '-'; + perm[ 7 ] = ( mode & S_IROTH ) ? 'r' : '-'; + perm[ 8 ] = ( mode & S_IWOTH ) ? 'w' : '-'; + perm[ 9 ] = ( mode & S_IXOTH ) ? 'x' : '-'; + perm[ 10 ] = '\0'; +} + +const char* vfs_file_info_get_disp_perm( VFSFileInfo* fi ) +{ + if ( ! fi->disp_perm[ 0 ] ) + get_file_perm_string( fi->disp_perm, + fi->mode ); + return fi->disp_perm; +} + +void vfs_file_size_to_string( char* buf, guint64 size ) +{ + char * unit; + /* guint point; */ + gfloat val; + + /* + FIXME: Is floating point calculation slower than integer division? + Some profiling is needed here. + */ + if ( size > ( ( guint64 ) 1 ) << 30 ) + { + if ( size > ( ( guint64 ) 1 ) << 40 ) + { + /* + size /= ( ( ( guint64 ) 1 << 40 ) / 10 ); + point = ( guint ) ( size % 10 ); + size /= 10; + */ + val = ((gfloat)size) / ( ( guint64 ) 1 << 40 ); + unit = "TB"; + } + else + { + /* + size /= ( ( 1 << 30 ) / 10 ); + point = ( guint ) ( size % 10 ); + size /= 10; + */ + val = ((gfloat)size) / ( ( guint64 ) 1 << 30 ); + unit = "GB"; + } + } + else if ( size > ( 1 << 20 ) ) + { + /* + size /= ( ( 1 << 20 ) / 10 ); + point = ( guint ) ( size % 10 ); + size /= 10; + */ + val = ((gfloat)size) / ( ( guint64 ) 1 << 20 ); + unit = "MB"; + } + else if ( size > ( 1 << 10 ) ) + { + /* + size /= ( ( 1 << 10 ) / 10 ); + point = size % 10; + size /= 10; + */ + val = ((gfloat)size) / ( ( guint64 ) 1 << 10 ); + unit = "KB"; + } + else + { + unit = size > 1 ? "Bytes" : "Byte"; + sprintf( buf, "%u %s", ( guint ) size, unit ); + return ; + } + /* sprintf( buf, "%llu.%u %s", size, point, unit ); */ + sprintf( buf, "%.1f %s", val, unit ); +} + +gboolean vfs_file_info_is_dir( VFSFileInfo* fi ) +{ + if ( S_ISDIR( fi->mode ) ) + return TRUE; + if ( S_ISLNK( fi->mode ) && + 0 == strcmp( vfs_mime_type_get_type( fi->mime_type ), XDG_MIME_TYPE_DIRECTORY ) ) + { + return TRUE; + } + return FALSE; +} + +gboolean vfs_file_info_is_symlink( VFSFileInfo* fi ) +{ + return S_ISLNK( fi->mode ) ? TRUE : FALSE; +} + +gboolean vfs_file_info_is_image( VFSFileInfo* fi ) +{ + /* FIXME: We had better use functions of xdg_mime to check this */ + if ( ! strncmp( "image/", vfs_mime_type_get_type( fi->mime_type ), 6 ) ) + return TRUE; + return FALSE; +} + +gboolean vfs_file_info_is_desktop_entry( VFSFileInfo* fi ) +{ + return 0 != (fi->flags & VFS_FILE_INFO_DESKTOP_ENTRY); +} + +gboolean vfs_file_info_is_unknown_type( VFSFileInfo* fi ) +{ + if ( ! strcmp( XDG_MIME_TYPE_UNKNOWN, + vfs_mime_type_get_type( fi->mime_type ) ) ) + return TRUE; + return FALSE; +} + +/* full path of the file is required by this function */ +gboolean vfs_file_info_is_executable( VFSFileInfo* fi, const char* file_path ) +{ + //return mime_type_is_executable_file( file_path, fi->mime_type->type ); + return mime_type_is_executable_file( file_path, "crfap" ); +} + +/* full path of the file is required by this function */ +gboolean vfs_file_info_is_text( VFSFileInfo* fi, const char* file_path ) +{ + return mime_type_is_text_file( file_path, fi->mime_type->type ); +} + +/* +* Run default action of specified file. +* Full path of the file is required by this function. +*/ +gboolean vfs_file_info_open_file( VFSFileInfo* fi, + const char* file_path, + GError** err ) +{ + VFSMimeType * mime_type; + char* app_name; + VFSAppDesktop* app; + GList* files = NULL; + gboolean ret = FALSE; + char* argv[ 2 ]; + + if ( vfs_file_info_is_executable( fi, file_path ) ) + { + argv[ 0 ] = (char *) file_path; + argv[ 1 ] = '\0'; + ret = g_spawn_async( NULL, argv, NULL, G_SPAWN_STDOUT_TO_DEV_NULL| + G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, err ); + } + else + { + mime_type = vfs_file_info_get_mime_type( fi ); + app_name = vfs_mime_type_get_default_action( mime_type ); + if ( app_name ) + { + app = vfs_app_desktop_new( app_name ); + if ( ! vfs_app_desktop_get_exec( app ) ) + app->exec = g_strdup( app_name ); /* FIXME: app->exec */ + files = g_list_prepend( files, (gpointer) file_path ); + /* FIXME: working dir is needed */ + ret = vfs_app_desktop_open_files( gdk_screen_get_default(), + NULL, app, files, err ); + g_list_free( files ); + vfs_app_desktop_unref( app ); + g_free( app_name ); + } + vfs_mime_type_unref( mime_type ); + } + return ret; +} + +mode_t vfs_file_info_get_mode( VFSFileInfo* fi ) +{ + return fi->mode; +} + +gboolean vfs_file_info_is_thumbnail_loaded( VFSFileInfo* fi, gboolean big ) +{ + if ( big ) + return ( fi->big_thumbnail != NULL ); + return ( fi->small_thumbnail != NULL ); +} + +gboolean vfs_file_info_load_thumbnail( VFSFileInfo* fi, + const char* full_path, + gboolean big ) +{ + GdkPixbuf* thumbnail; + + if ( big ) + { + if ( fi->big_thumbnail ) + return TRUE; + } + else + { + if ( fi->small_thumbnail ) + return TRUE; + } + thumbnail = vfs_thumbnail_load_for_file( full_path, + big ? big_thumb_size : small_thumb_size , fi->mtime ); + if( G_LIKELY( thumbnail ) ) + { + if ( big ) + fi->big_thumbnail = thumbnail; + else + fi->small_thumbnail = thumbnail; + } + else /* fallback to mime_type icon */ + { + if ( big ) + fi->big_thumbnail = vfs_file_info_get_big_icon( fi ); + else + fi->small_thumbnail = vfs_file_info_get_small_icon( fi ); + } + return ( thumbnail != NULL ); +} + +void vfs_file_info_set_thumbnail_size( int big, int small ) +{ + big_thumb_size = big; + small_thumb_size = small; +} + +void vfs_file_info_load_special_info( VFSFileInfo* fi, + const char* file_path ) +{ + /*if ( G_LIKELY(fi->type) && G_UNLIKELY(fi->type->name, "application/x-desktop") ) */ + if ( G_UNLIKELY( g_str_has_suffix( fi->name, ".desktop") ) ) + { + VFSAppDesktop * desktop; + const char* icon_name; + + fi->flags |= VFS_FILE_INFO_DESKTOP_ENTRY; + desktop = vfs_app_desktop_new( file_path ); + if ( vfs_app_desktop_get_disp_name( desktop ) ) + { + vfs_file_info_set_disp_name( + fi, vfs_app_desktop_get_disp_name( desktop ) ); + } + + if ( (icon_name = vfs_app_desktop_get_icon_name( desktop )) ) + { + GdkPixbuf* icon; + int big_size, small_size; + vfs_mime_type_get_icon_size( &big_size, &small_size ); + if( ! fi->big_thumbnail ) + { + icon = vfs_app_desktop_get_icon( desktop, big_size, FALSE ); + if( G_LIKELY(icon) ) + fi->big_thumbnail =icon; + } + if( ! fi->small_thumbnail ) + { + icon = vfs_app_desktop_get_icon( desktop, small_size, FALSE ); + if( G_LIKELY(icon) ) + fi->small_thumbnail =icon; + } + } + vfs_app_desktop_unref( desktop ); + } +} + +void vfs_file_info_list_free( GList* list ) +{ + g_list_foreach( list, (GFunc)vfs_file_info_unref, NULL ); + g_list_free( list ); +} + + +char* vfs_file_resolve_path( const char* cwd, const char* relative_path ) +{ + GString* ret = g_string_sized_new( 4096 ); + int len; + gboolean strip_tail; + + g_return_val_if_fail( G_LIKELY(relative_path), NULL ); + + len = strlen( relative_path ); + strip_tail = (0 == len || relative_path[len-1] != '/'); + + if( G_UNLIKELY(*relative_path != '/') ) /* relative path */ + { + if( G_UNLIKELY(relative_path[0] == '~') ) /* home dir */ + { + g_string_append( ret, g_get_home_dir()); + ++relative_path; + } + else + { + if( ! cwd ) + { + char *cwd_new; + cwd_new = g_get_current_dir(); + g_string_append( ret, cwd_new ); + g_free( cwd_new ); + } + else + g_string_append( ret, cwd ); + } + } + + if( relative_path[0] != '/' && (0 == ret->len || ret->str[ ret->len - 1 ] != '/' ) ) + g_string_append_c( ret, '/' ); + + while( G_LIKELY( *relative_path ) ) + { + if( G_UNLIKELY(*relative_path == '.') ) + { + if( relative_path[1] == '/' || relative_path[1] == '\0' ) /* current dir */ + { + relative_path += relative_path[1] ? 2 : 1; + continue; + } + if( relative_path[1] == '.' && + ( relative_path[2] == '/' || relative_path[2] == '\0') ) /* parent dir */ + { + gsize len = ret->len - 2; + while( ret->str[ len ] != '/' ) + --len; + g_string_truncate( ret, len + 1 ); + relative_path += relative_path[2] ? 3 : 2; + continue; + } + } + + do + { + g_string_append_c( ret, *relative_path ); + }while( G_LIKELY( *(relative_path++) != '/' && *relative_path ) ); + } + + /* if original path contains tailing '/', preserve it; otherwise, remove it. */ + if( strip_tail && G_LIKELY( ret->len > 1 ) && G_UNLIKELY( ret->str[ ret->len - 1 ] == '/' ) ) + g_string_truncate( ret, ret->len - 1 ); + return g_string_free( ret, FALSE ); +} + |