3 * A Nautilus extension which offers configurable context menu actions.
5 * Copyright (C) 2005 The GNOME Foundation
6 * Copyright (C) 2006, 2007, 2008 Frederic Ruaudel and others (see AUTHORS)
7 * Copyright (C) 2009, 2010, 2011 Pierre Wieser and others (see AUTHORS)
9 * This Program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of
12 * the License, or (at your option) any later version.
14 * This Program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public
20 * License along with this Library; see the file COPYING. If not,
21 * write to the Free Software Foundation, Inc., 59 Temple Place,
22 * Suite 330, Boston, MA 02111-1307, USA.
25 * Frederic Ruaudel <grumz@grumz.net>
26 * Rodrigo Moya <rodrigo@gnome-db.org>
27 * Pierre Wieser <pwieser@trychlos.org>
28 * ... and many others (see AUTHORS)
35 #include <glib/gi18n.h>
38 #include <api/na-core-utils.h>
39 #include <api/na-object-api.h>
41 #include "na-gnome-vfs-uri.h"
42 #include "na-selected-info.h"
43 #include "na-tokens.h"
47 struct _NATokensClassPrivate
{
48 void *empty
; /* so that gcc -pedantic is happy */
51 /* private instance data
53 struct _NATokensPrivate
{
54 gboolean dispose_has_run
;
60 GSList
*basenames_woext
;
69 static GObjectClass
*st_parent_class
= NULL
;
71 static GType
register_type( void );
72 static void class_init( NATokensClass
*klass
);
73 static void instance_init( GTypeInstance
*instance
, gpointer klass
);
74 static void instance_dispose( GObject
*object
);
75 static void instance_finalize( GObject
*object
);
77 static void execute_action_command( gchar
*command
, const NAObjectProfile
*profile
);
78 static gboolean
is_singular_exec( const NATokens
*tokens
, const gchar
*exec
);
79 static gchar
*parse_singular( const NATokens
*tokens
, const gchar
*input
, guint i
, gboolean utf8
, gboolean quoted
);
80 static GString
*quote_string( GString
*input
, const gchar
*name
, gboolean quoted
);
81 static GString
*quote_string_list( GString
*input
, GSList
*names
, gboolean quoted
);
84 na_tokens_get_type( void )
86 static GType object_type
= 0;
89 object_type
= register_type();
92 return( object_type
);
98 static const gchar
*thisfn
= "na_tokens_register_type";
101 static GTypeInfo info
= {
102 sizeof( NATokensClass
),
103 ( GBaseInitFunc
) NULL
,
104 ( GBaseFinalizeFunc
) NULL
,
105 ( GClassInitFunc
) class_init
,
110 ( GInstanceInitFunc
) instance_init
113 g_debug( "%s", thisfn
);
115 type
= g_type_register_static( G_TYPE_OBJECT
, "NATokens", &info
, 0 );
121 class_init( NATokensClass
*klass
)
123 static const gchar
*thisfn
= "na_tokens_class_init";
124 GObjectClass
*object_class
;
126 g_debug( "%s: klass=%p", thisfn
, ( void * ) klass
);
128 st_parent_class
= g_type_class_peek_parent( klass
);
130 object_class
= G_OBJECT_CLASS( klass
);
131 object_class
->dispose
= instance_dispose
;
132 object_class
->finalize
= instance_finalize
;
134 klass
->private = g_new0( NATokensClassPrivate
, 1 );
138 instance_init( GTypeInstance
*instance
, gpointer klass
)
140 static const gchar
*thisfn
= "na_tokens_instance_init";
143 g_return_if_fail( NA_IS_TOKENS( instance
));
144 self
= NA_TOKENS( instance
);
146 g_debug( "%s: instance=%p (%s), klass=%p",
147 thisfn
, ( void * ) instance
, G_OBJECT_TYPE_NAME( instance
), ( void * ) klass
);
149 self
->private = g_new0( NATokensPrivate
, 1 );
151 self
->private->uris
= NULL
;
152 self
->private->filenames
= NULL
;
153 self
->private->basedirs
= NULL
;
154 self
->private->basenames
= NULL
;
155 self
->private->basenames_woext
= NULL
;
156 self
->private->exts
= NULL
;
157 self
->private->mimetypes
= NULL
;
158 self
->private->hostname
= NULL
;
159 self
->private->username
= NULL
;
160 self
->private->port
= 0;
161 self
->private->scheme
= NULL
;
163 self
->private->dispose_has_run
= FALSE
;
167 instance_dispose( GObject
*object
)
169 static const gchar
*thisfn
= "na_tokens_instance_dispose";
172 g_return_if_fail( NA_IS_TOKENS( object
));
173 self
= NA_TOKENS( object
);
175 if( !self
->private->dispose_has_run
){
177 g_debug( "%s: object=%p (%s)", thisfn
, ( void * ) object
, G_OBJECT_TYPE_NAME( object
));
179 self
->private->dispose_has_run
= TRUE
;
181 if( G_OBJECT_CLASS( st_parent_class
)->dispose
){
182 G_OBJECT_CLASS( st_parent_class
)->dispose( object
);
188 instance_finalize( GObject
*object
)
190 static const gchar
*thisfn
= "na_tokens_instance_finalize";
193 g_return_if_fail( NA_IS_TOKENS( object
));
194 self
= NA_TOKENS( object
);
196 g_debug( "%s: object=%p", thisfn
, ( void * ) object
);
198 g_free( self
->private->scheme
);
199 g_free( self
->private->username
);
200 g_free( self
->private->hostname
);
201 na_core_utils_slist_free( self
->private->mimetypes
);
202 na_core_utils_slist_free( self
->private->exts
);
203 na_core_utils_slist_free( self
->private->basenames_woext
);
204 na_core_utils_slist_free( self
->private->basenames
);
205 na_core_utils_slist_free( self
->private->basedirs
);
206 na_core_utils_slist_free( self
->private->filenames
);
207 na_core_utils_slist_free( self
->private->uris
);
209 g_free( self
->private );
211 /* chain call to parent class */
212 if( G_OBJECT_CLASS( st_parent_class
)->finalize
){
213 G_OBJECT_CLASS( st_parent_class
)->finalize( object
);
218 * na_tokens_new_for_example:
220 * Returns: a new #NATokens object initialized with fake values for two
221 * regular files, in order to be used as an example of an expanded command
225 na_tokens_new_for_example( void )
228 const gchar
*ex_uri1
= _( "file:///path/to/file1.mid" );
229 const gchar
*ex_uri2
= _( "file:///path/to/file2.jpeg" );
230 const gchar
*ex_mimetype1
= _( "audio/x-midi" );
231 const gchar
*ex_mimetype2
= _( "image/jpeg" );
232 const guint ex_port
= 8080;
233 const gchar
*ex_host
= _( "test.example.net" );
234 const gchar
*ex_user
= _( "user" );
236 gchar
*dirname
, *bname
, *bname_woext
, *ext
;
240 tokens
= g_object_new( NA_TOKENS_TYPE
, NULL
);
242 tokens
->private->count
= 2;
244 tokens
->private->uris
= g_slist_append( tokens
->private->uris
, g_strdup( ex_uri1
));
245 tokens
->private->uris
= g_slist_append( tokens
->private->uris
, g_strdup( ex_uri2
));
247 for( is
= tokens
->private->uris
; is
; is
= is
->next
){
248 vfs
= g_new0( NAGnomeVFSURI
, 1 );
249 na_gnome_vfs_uri_parse( vfs
, is
->data
);
251 tokens
->private->filenames
= g_slist_append( tokens
->private->filenames
, g_strdup( vfs
->path
));
252 dirname
= g_path_get_dirname( vfs
->path
);
253 tokens
->private->basedirs
= g_slist_append( tokens
->private->basedirs
, dirname
);
254 bname
= g_path_get_basename( vfs
->path
);
255 tokens
->private->basenames
= g_slist_append( tokens
->private->basenames
, bname
);
256 na_core_utils_dir_split_ext( bname
, &bname_woext
, &ext
);
257 tokens
->private->basenames_woext
= g_slist_append( tokens
->private->basenames_woext
, bname_woext
);
258 tokens
->private->exts
= g_slist_append( tokens
->private->exts
, ext
);
261 tokens
->private->scheme
= g_strdup( vfs
->scheme
);
265 na_gnome_vfs_uri_free( vfs
);
268 tokens
->private->mimetypes
= g_slist_append( tokens
->private->mimetypes
, g_strdup( ex_mimetype1
));
269 tokens
->private->mimetypes
= g_slist_append( tokens
->private->mimetypes
, g_strdup( ex_mimetype2
));
271 tokens
->private->hostname
= g_strdup( ex_host
);
272 tokens
->private->username
= g_strdup( ex_user
);
273 tokens
->private->port
= ex_port
;
279 * na_tokens_new_from_selection:
280 * @selection: a #GList list of #NASelectedInfo objects.
282 * Returns: a new #NATokens object which holds all possible tokens.
285 na_tokens_new_from_selection( GList
*selection
)
287 static const gchar
*thisfn
= "na_tokens_new_from_selection";
290 gchar
*uri
, *filename
, *basedir
, *basename
, *bname_woext
, *ext
, *mimetype
;
293 g_debug( "%s: selection=%p (count=%d)", thisfn
, ( void * ) selection
, g_list_length( selection
));
296 tokens
= g_object_new( NA_TOKENS_TYPE
, NULL
);
298 tokens
->private->count
= g_list_length( selection
);
300 for( it
= selection
; it
; it
= it
->next
){
301 mimetype
= na_selected_info_get_mime_type( NA_SELECTED_INFO( it
->data
));
303 uri
= na_selected_info_get_uri( NA_SELECTED_INFO( it
->data
));
304 filename
= na_selected_info_get_path( NA_SELECTED_INFO( it
->data
));
305 basedir
= na_selected_info_get_dirname( NA_SELECTED_INFO( it
->data
));
306 basename
= na_selected_info_get_basename( NA_SELECTED_INFO( it
->data
));
307 na_core_utils_dir_split_ext( basename
, &bname_woext
, &ext
);
310 tokens
->private->hostname
= na_selected_info_get_uri_host( NA_SELECTED_INFO( it
->data
));
311 tokens
->private->username
= na_selected_info_get_uri_user( NA_SELECTED_INFO( it
->data
));
312 tokens
->private->port
= na_selected_info_get_uri_port( NA_SELECTED_INFO( it
->data
));
313 tokens
->private->scheme
= na_selected_info_get_uri_scheme( NA_SELECTED_INFO( it
->data
));
317 tokens
->private->uris
= g_slist_append( tokens
->private->uris
, uri
);
318 tokens
->private->filenames
= g_slist_append( tokens
->private->filenames
, filename
);
319 tokens
->private->basedirs
= g_slist_append( tokens
->private->basedirs
, basedir
);
320 tokens
->private->basenames
= g_slist_append( tokens
->private->basenames
, basename
);
321 tokens
->private->basenames_woext
= g_slist_append( tokens
->private->basenames_woext
, bname_woext
);
322 tokens
->private->exts
= g_slist_append( tokens
->private->exts
, ext
);
323 tokens
->private->mimetypes
= g_slist_append( tokens
->private->mimetypes
, mimetype
);
330 * na_tokens_parse_for_display:
331 * @tokens: a #NATokens object.
332 * @string: the input string, may or may not contain tokens.
333 * @utf8: whether the @input string is UTF-8 encoded, or a standard ASCII string.
335 * Expands the parameters in the given string.
337 * This expanded string is meant to be displayed only (not executed) as
338 * filenames are not shell-quoted.
340 * Returns: a copy of @input string with tokens expanded, as a newly
341 * allocated string which should be g_free() by the caller.
344 na_tokens_parse_for_display( const NATokens
*tokens
, const gchar
*string
, gboolean utf8
)
346 return( parse_singular( tokens
, string
, 0, utf8
, FALSE
));
350 * na_tokens_execute_action:
351 * @tokens: a #NATokens object.
352 * @profile: the #NAObjectProfile to be executed.
354 * Execute the given action, regarding the context described by @tokens.
357 na_tokens_execute_action( const NATokens
*tokens
, const NAObjectProfile
*profile
)
359 gchar
*path
, *parameters
, *exec
;
364 path
= na_object_get_path( profile
);
365 parameters
= na_object_get_parameters( profile
);
366 exec
= g_strdup_printf( "%s %s", path
, parameters
);
367 g_free( parameters
);
370 singular
= is_singular_exec( tokens
, exec
);
373 for( i
= 0 ; i
< tokens
->private->count
; ++i
){
374 command
= parse_singular( tokens
, exec
, i
, FALSE
, TRUE
);
375 execute_action_command( command
, profile
);
380 command
= parse_singular( tokens
, exec
, 0, FALSE
, TRUE
);
381 execute_action_command( command
, profile
);
389 execute_action_command( gchar
*command
, const NAObjectProfile
*profile
)
391 static const gchar
*thisfn
= "nautilus_actions_execute_action_command";
398 g_debug( "%s: command=%s, profile=%p", thisfn
, command
, ( void * ) profile
);
402 if( !g_shell_parse_argv( command
, &argc
, &argv
, &error
)){
403 g_warning( "%s: %s", thisfn
, error
->message
);
404 g_error_free( error
);
407 wdir
= na_object_get_working_dir( profile
);
408 g_debug( "%s: wdir=%s", thisfn
, wdir
);
413 G_SPAWN_SEARCH_PATH
| G_SPAWN_STDOUT_TO_DEV_NULL
| G_SPAWN_STDERR_TO_DEV_NULL
,
419 g_warning( "%s: %s", thisfn
, error
->message
);
420 g_error_free( error
);
423 g_spawn_close_pid( child_pid
);
430 * na_tokens_is_singular_exec:
431 * @tokens: the current #NATokens object.
432 * @exec: the to be executed command-line before having been parsed
434 * Returns: %TRUE if the first relevant parameter found in @exec
435 * command-line is of singular form, %FALSE else.
438 is_singular_exec( const NATokens
*tokens
, const gchar
*exec
)
446 iter
= ( gchar
* ) exec
;
448 while(( iter
= g_strstr_len( iter
, -1, "%" )) != NULL
&& !found
){
475 /* all other parameters are irrelevant according to DES-EMA
485 iter
+= 2; /* skip the % sign and the character after */
493 * @tokens: a #NATokens object.
494 * @input: the input string, may or may not contain tokens.
495 * @i: the number of the iteration in a multiple selection, starting with zero.
496 * @utf8: whether the @input string is UTF-8 encoded, or a standard ASCII
499 * A command is said of 'singular form' when its first parameter is not
500 * of plural form. In the case of a multiple selection, singular form
501 * commands are executed one time for each element of the selection
503 * Returns: a #GSList which contains two fields: the command and its parameters.
504 * The returned #GSList should be na_core_utils_slist_free() by the caller.
507 parse_singular( const NATokens
*tokens
, const gchar
*input
, guint i
, gboolean utf8
, gboolean quoted
)
510 gchar
*iter
, *prev_iter
;
513 output
= g_string_new( "" );
515 /* return NULL if input is NULL
518 return( g_string_free( output
, TRUE
));
521 /* return an empty string if input is empty
524 if( !g_utf8_strlen( input
, -1 )){
525 return( g_string_free( output
, FALSE
));
528 if( !strlen( input
)){
529 return( g_string_free( output
, FALSE
));
533 iter
= ( gchar
* ) input
;
536 while(( iter
= g_strstr_len( iter
, -1, "%" ))){
537 output
= g_string_append_len( output
, prev_iter
, strlen( prev_iter
) - strlen( iter
));
541 if( tokens
->private->basenames
){
542 nth
= ( const gchar
* ) g_slist_nth_data( tokens
->private->basenames
, i
);
544 output
= quote_string( output
, nth
, quoted
);
550 if( tokens
->private->basenames
){
551 output
= quote_string_list( output
, tokens
->private->basenames
, quoted
);
556 g_string_append_printf( output
, "%d", tokens
->private->count
);
560 if( tokens
->private->basedirs
){
561 nth
= ( const gchar
* ) g_slist_nth_data( tokens
->private->basedirs
, i
);
563 output
= quote_string( output
, nth
, quoted
);
569 if( tokens
->private->basedirs
){
570 output
= quote_string_list( output
, tokens
->private->basedirs
, quoted
);
575 if( tokens
->private->filenames
){
576 nth
= ( const gchar
* ) g_slist_nth_data( tokens
->private->filenames
, i
);
578 output
= quote_string( output
, nth
, quoted
);
584 if( tokens
->private->filenames
){
585 output
= quote_string_list( output
, tokens
->private->filenames
, quoted
);
590 if( tokens
->private->hostname
){
591 output
= quote_string( output
, tokens
->private->hostname
, quoted
);
595 /* mimetypes are never quoted
598 if( tokens
->private->mimetypes
){
599 nth
= ( const gchar
* ) g_slist_nth_data( tokens
->private->mimetypes
, i
);
601 output
= quote_string( output
, nth
, FALSE
);
607 if( tokens
->private->mimetypes
){
608 output
= quote_string_list( output
, tokens
->private->mimetypes
, FALSE
);
612 /* no-op operators */
618 if( tokens
->private->username
){
619 output
= quote_string( output
, tokens
->private->username
, quoted
);
623 /* port number is never quoted
626 if( tokens
->private->port
> 0 ){
627 g_string_append_printf( output
, "%d", tokens
->private->port
);
632 if( tokens
->private->scheme
){
633 output
= quote_string( output
, tokens
->private->scheme
, quoted
);
638 if( tokens
->private->uris
){
639 nth
= ( const gchar
* ) g_slist_nth_data( tokens
->private->uris
, i
);
641 output
= quote_string( output
, nth
, quoted
);
647 if( tokens
->private->uris
){
648 output
= quote_string_list( output
, tokens
->private->uris
, quoted
);
653 if( tokens
->private->basenames_woext
){
654 nth
= ( const gchar
* ) g_slist_nth_data( tokens
->private->basenames_woext
, i
);
656 output
= quote_string( output
, nth
, quoted
);
662 if( tokens
->private->basenames_woext
){
663 output
= quote_string_list( output
, tokens
->private->basenames_woext
, quoted
);
668 if( tokens
->private->exts
){
669 nth
= ( const gchar
* ) g_slist_nth_data( tokens
->private->exts
, i
);
671 output
= quote_string( output
, nth
, quoted
);
677 if( tokens
->private->exts
){
678 output
= quote_string_list( output
, tokens
->private->exts
, quoted
);
685 output
= g_string_append_c( output
, '%' );
689 iter
+= 2; /* skip the % sign and the character after */
690 prev_iter
= iter
; /* store the new start of the string */
693 output
= g_string_append_len( output
, prev_iter
, strlen( prev_iter
));
695 return( g_string_free( output
, FALSE
));
699 quote_string( GString
*input
, const gchar
*name
, gboolean quoted
)
704 tmp
= g_shell_quote( name
);
705 input
= g_string_append( input
, tmp
);
709 input
= g_string_append( input
, name
);
716 quote_string_list( GString
*input
, GSList
*names
, gboolean quoted
)
722 GSList
*quoted_names
= NULL
;
723 for( it
= names
; it
; it
= it
->next
){
724 quoted_names
= g_slist_append( quoted_names
, g_shell_quote(( const gchar
* ) it
->data
));
726 tmp
= na_core_utils_slist_join_at_end( quoted_names
, " " );
727 na_core_utils_slist_free( quoted_names
);
730 tmp
= na_core_utils_slist_join_at_end( g_slist_reverse( names
), " " );
733 input
= g_string_append( input
, tmp
);