Update copyright message
[nautilus-actions.git] / src / core / na-tokens.c
blobab6f5493a11b86648932f4dec84c6ef4e8310408
1 /*
2 * Nautilus Actions
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.
24 * Authors:
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)
31 #ifdef HAVE_CONFIG_H
32 #include <config.h>
33 #endif
35 #include <glib/gi18n.h>
36 #include <string.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"
45 /* private class data
47 struct _NATokensClassPrivate {
48 void *empty; /* so that gcc -pedantic is happy */
51 /* private instance data
53 struct _NATokensPrivate {
54 gboolean dispose_has_run;
56 guint count;
58 GSList *uris;
59 gchar *uris_str;
60 GSList *filenames;
61 gchar *filenames_str;
62 GSList *basedirs;
63 gchar *basedirs_str;
64 GSList *basenames;
65 gchar *basenames_str;
66 GSList *basenames_woext;
67 gchar *basenames_woext_str;
68 GSList *exts;
69 gchar *exts_str;
70 GSList *mimetypes;
71 gchar *mimetypes_str;
73 gchar *hostname;
74 gchar *username;
75 guint port;
76 gchar *scheme;
79 static GObjectClass *st_parent_class = NULL;
81 static GType register_type( void );
82 static void class_init( NATokensClass *klass );
83 static void instance_init( GTypeInstance *instance, gpointer klass );
84 static void instance_dispose( GObject *object );
85 static void instance_finalize( GObject *object );
87 static NATokens *build_string_lists( NATokens *tokens );
88 static gchar *build_string_lists_item( GSList **pslist );
89 static void execute_action_command( const gchar *command, const NAObjectProfile *profile );
90 static gboolean is_singular_exec( const NATokens *tokens, const gchar *exec );
91 static gchar *parse_singular( const NATokens *tokens, const gchar *input, guint i, gboolean utf8 );
93 GType
94 na_tokens_get_type( void )
96 static GType object_type = 0;
98 if( !object_type ){
99 object_type = register_type();
102 return( object_type );
105 static GType
106 register_type( void )
108 static const gchar *thisfn = "na_tokens_register_type";
109 GType type;
111 static GTypeInfo info = {
112 sizeof( NATokensClass ),
113 ( GBaseInitFunc ) NULL,
114 ( GBaseFinalizeFunc ) NULL,
115 ( GClassInitFunc ) class_init,
116 NULL,
117 NULL,
118 sizeof( NATokens ),
120 ( GInstanceInitFunc ) instance_init
123 g_debug( "%s", thisfn );
125 type = g_type_register_static( G_TYPE_OBJECT, "NATokens", &info, 0 );
127 return( type );
130 static void
131 class_init( NATokensClass *klass )
133 static const gchar *thisfn = "na_tokens_class_init";
134 GObjectClass *object_class;
136 g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
138 st_parent_class = g_type_class_peek_parent( klass );
140 object_class = G_OBJECT_CLASS( klass );
141 object_class->dispose = instance_dispose;
142 object_class->finalize = instance_finalize;
144 klass->private = g_new0( NATokensClassPrivate, 1 );
147 static void
148 instance_init( GTypeInstance *instance, gpointer klass )
150 static const gchar *thisfn = "na_tokens_instance_init";
151 NATokens *self;
153 g_return_if_fail( NA_IS_TOKENS( instance ));
154 self = NA_TOKENS( instance );
156 g_debug( "%s: instance=%p (%s), klass=%p",
157 thisfn, ( void * ) instance, G_OBJECT_TYPE_NAME( instance ), ( void * ) klass );
159 self->private = g_new0( NATokensPrivate, 1 );
161 self->private->uris = NULL;
162 self->private->uris_str = NULL;
163 self->private->filenames = NULL;
164 self->private->filenames_str = NULL;
165 self->private->basedirs = NULL;
166 self->private->basedirs_str = NULL;
167 self->private->basenames = NULL;
168 self->private->basenames_str = NULL;
169 self->private->basenames_woext = NULL;
170 self->private->basenames_woext_str = NULL;
171 self->private->exts = NULL;
172 self->private->exts_str = NULL;
173 self->private->mimetypes = NULL;
174 self->private->mimetypes_str = NULL;
176 self->private->hostname = NULL;
177 self->private->username = NULL;
178 self->private->port = 0;
179 self->private->scheme = NULL;
181 self->private->dispose_has_run = FALSE;
184 static void
185 instance_dispose( GObject *object )
187 static const gchar *thisfn = "na_tokens_instance_dispose";
188 NATokens *self;
190 g_return_if_fail( NA_IS_TOKENS( object ));
191 self = NA_TOKENS( object );
193 if( !self->private->dispose_has_run ){
195 g_debug( "%s: object=%p (%s)", thisfn, ( void * ) object, G_OBJECT_TYPE_NAME( object ));
197 self->private->dispose_has_run = TRUE;
199 if( G_OBJECT_CLASS( st_parent_class )->dispose ){
200 G_OBJECT_CLASS( st_parent_class )->dispose( object );
205 static void
206 instance_finalize( GObject *object )
208 static const gchar *thisfn = "na_tokens_instance_finalize";
209 NATokens *self;
211 g_return_if_fail( NA_IS_TOKENS( object ));
212 self = NA_TOKENS( object );
214 g_debug( "%s: object=%p", thisfn, ( void * ) object );
216 g_free( self->private->scheme );
217 g_free( self->private->username );
218 g_free( self->private->hostname );
220 g_free( self->private->mimetypes_str );
221 na_core_utils_slist_free( self->private->mimetypes );
222 g_free( self->private->exts_str );
223 na_core_utils_slist_free( self->private->exts );
224 g_free( self->private->basenames_woext_str );
225 na_core_utils_slist_free( self->private->basenames_woext );
226 g_free( self->private->basenames_str );
227 na_core_utils_slist_free( self->private->basenames );
228 g_free( self->private->basedirs_str );
229 na_core_utils_slist_free( self->private->basedirs );
230 g_free( self->private->filenames_str );
231 na_core_utils_slist_free( self->private->filenames );
232 g_free( self->private->uris_str );
233 na_core_utils_slist_free( self->private->uris );
235 g_free( self->private );
237 /* chain call to parent class */
238 if( G_OBJECT_CLASS( st_parent_class )->finalize ){
239 G_OBJECT_CLASS( st_parent_class )->finalize( object );
244 * na_tokens_new_for_example:
246 * Returns: a new #NATokens object initialized with fake values for two
247 * regular files, in order to be used as an example of an expanded command
248 * line.
250 NATokens *
251 na_tokens_new_for_example( void )
253 NATokens *tokens;
254 const gchar *ex_uri1 = _( "file:///path/to/file1.mid" );
255 const gchar *ex_uri2 = _( "file:///path/to/file2.jpeg" );
256 const gchar *ex_mimetype1 = _( "audio/x-midi" );
257 const gchar *ex_mimetype2 = _( "image/jpeg" );
258 const guint ex_port = 8080;
259 const gchar *ex_host = _( "test.example.net" );
260 const gchar *ex_user = _( "user" );
261 NAGnomeVFSURI *vfs;
262 gchar *dirname, *bname, *bname_woext, *ext;
263 GSList *is;
264 gboolean first;
266 tokens = g_object_new( NA_TOKENS_TYPE, NULL );
267 first = TRUE;
268 tokens->private->count = 2;
270 tokens->private->uris = g_slist_append( tokens->private->uris, g_strdup( ex_uri1 ));
271 tokens->private->uris = g_slist_append( tokens->private->uris, g_strdup( ex_uri2 ));
273 for( is = tokens->private->uris ; is ; is = is->next ){
274 vfs = g_new0( NAGnomeVFSURI, 1 );
275 na_gnome_vfs_uri_parse( vfs, is->data );
277 tokens->private->filenames = g_slist_append( tokens->private->filenames, g_strdup( vfs->path ));
278 dirname = g_path_get_dirname( vfs->path );
279 tokens->private->basedirs = g_slist_append( tokens->private->basedirs, dirname );
280 bname = g_path_get_basename( vfs->path );
281 tokens->private->basenames = g_slist_append( tokens->private->basenames, bname );
282 na_core_utils_dir_split_ext( bname, &bname_woext, &ext );
283 tokens->private->basenames_woext = g_slist_append( tokens->private->basenames_woext, bname_woext );
284 tokens->private->exts = g_slist_append( tokens->private->exts, ext );
286 if( first ){
287 tokens->private->scheme = g_strdup( vfs->scheme );
288 first = FALSE;
291 na_gnome_vfs_uri_free( vfs );
294 tokens->private->mimetypes = g_slist_append( tokens->private->mimetypes, g_strdup( ex_mimetype1 ));
295 tokens->private->mimetypes = g_slist_append( tokens->private->mimetypes, g_strdup( ex_mimetype2 ));
297 tokens->private->hostname = g_strdup( ex_host );
298 tokens->private->username = g_strdup( ex_user );
299 tokens->private->port = ex_port;
301 return( build_string_lists( tokens ));
305 * na_tokens_new_from_selection:
306 * @selection: a #GList list of #NASelectedInfo objects.
308 * Returns: a new #NATokens object which holds all possible tokens.
310 NATokens *
311 na_tokens_new_from_selection( GList *selection )
313 static const gchar *thisfn = "na_tokens_new_from_selection";
314 NATokens *tokens;
315 GList *it;
316 gchar *uri, *filename, *basedir, *basename, *bname_woext, *ext, *mimetype;
317 gboolean first;
319 g_debug( "%s: selection=%p (count=%d)", thisfn, ( void * ) selection, g_list_length( selection ));
321 first = TRUE;
322 tokens = g_object_new( NA_TOKENS_TYPE, NULL );
324 tokens->private->count = g_list_length( selection );
326 for( it = selection ; it ; it = it->next ){
327 mimetype = na_selected_info_get_mime_type( NA_SELECTED_INFO( it->data ));
329 uri = na_selected_info_get_uri( NA_SELECTED_INFO( it->data ));
330 filename = na_selected_info_get_path( NA_SELECTED_INFO( it->data ));
331 basedir = na_selected_info_get_dirname( NA_SELECTED_INFO( it->data ));
332 basename = na_selected_info_get_basename( NA_SELECTED_INFO( it->data ));
333 na_core_utils_dir_split_ext( basename, &bname_woext, &ext );
335 if( first ){
336 tokens->private->hostname = na_selected_info_get_uri_host( NA_SELECTED_INFO( it->data ));
337 tokens->private->username = na_selected_info_get_uri_user( NA_SELECTED_INFO( it->data ));
338 tokens->private->port = na_selected_info_get_uri_port( NA_SELECTED_INFO( it->data ));
339 tokens->private->scheme = na_selected_info_get_uri_scheme( NA_SELECTED_INFO( it->data ));
340 first = FALSE;
343 tokens->private->uris = g_slist_prepend( tokens->private->uris, uri );
345 tokens->private->filenames = g_slist_prepend( tokens->private->filenames, g_shell_quote( filename ));
346 tokens->private->basedirs = g_slist_prepend( tokens->private->basedirs, g_shell_quote( basedir ));
347 tokens->private->basenames = g_slist_prepend( tokens->private->basenames, g_shell_quote( basename ));
348 tokens->private->basenames_woext = g_slist_prepend( tokens->private->basenames_woext, g_shell_quote( bname_woext ));
349 tokens->private->exts = g_slist_prepend( tokens->private->exts, g_shell_quote( ext ));
351 g_free( filename );
352 g_free( basedir );
353 g_free( basename );
354 g_free( bname_woext );
355 g_free( ext );
357 tokens->private->mimetypes = g_slist_prepend( tokens->private->mimetypes, mimetype );
360 return( build_string_lists( tokens ));
364 * na_tokens_parse_parameters:
365 * @tokens: a #NATokens object.
366 * @string: the input string, may or may not contain tokens.
367 * @utf8: whether the @input string is UTF-8 encoded, or a standard ASCII string.
369 * Expands the parameters in the given string.
371 * Returns: a copy of @input string with tokens expanded, as a newly
372 * allocated string which should be g_free() by the caller.
374 gchar *
375 na_tokens_parse_parameters( const NATokens *tokens, const gchar *string, gboolean utf8 )
377 return( parse_singular( tokens, string, 0, utf8 ));
381 * na_tokens_execute_action:
382 * @tokens: a #NATokens object.
383 * @profile: the #NAObjectProfile to be executed.
385 * Execute the given action, regarding the context described by @tokens.
387 void
388 na_tokens_execute_action( const NATokens *tokens, const NAObjectProfile *profile )
390 gchar *path, *parameters, *exec, *command;
391 gboolean singular;
392 guint i;
394 path = na_object_get_path( profile );
395 parameters = na_object_get_parameters( profile );
396 exec = g_strdup_printf( "%s %s", path, parameters );
397 g_free( parameters );
398 g_free( path );
400 singular = is_singular_exec( tokens, exec );
402 if( singular ){
403 for( i = 0 ; i < tokens->private->count ; ++i ){
404 command = parse_singular( tokens, exec, i, FALSE );
405 execute_action_command( command, profile );
406 g_free( command );
409 } else {
410 command = na_tokens_parse_parameters( tokens, exec, FALSE );
411 execute_action_command( command, profile );
412 g_free( command );
416 g_free( exec );
419 static NATokens *
420 build_string_lists( NATokens *tokens )
422 tokens->private->uris_str = build_string_lists_item( &tokens->private->uris );
423 tokens->private->filenames_str = build_string_lists_item( &tokens->private->filenames );
424 tokens->private->basedirs_str = build_string_lists_item( &tokens->private->basedirs );
425 tokens->private->basenames_str = build_string_lists_item( &tokens->private->basenames );
426 tokens->private->basenames_woext_str = build_string_lists_item( &tokens->private->basenames_woext );
427 tokens->private->exts_str = build_string_lists_item( &tokens->private->exts );
428 tokens->private->mimetypes_str = build_string_lists_item( &tokens->private->mimetypes );
430 return( tokens );
433 static gchar *
434 build_string_lists_item( GSList **pslist )
436 *pslist = g_slist_reverse( *pslist );
438 gchar *str = na_core_utils_slist_join_at_end( *pslist, " " );
440 return( str );
443 static void
444 execute_action_command( const gchar *command, const NAObjectProfile *profile )
446 static const gchar *thisfn = "nautilus_actions_execute_action_command";
448 g_debug( "%s: command=%s, profile=%p", thisfn, command, ( void * ) profile );
450 g_spawn_command_line_async( command, NULL );
454 * na_tokens_is_singular_exec:
455 * @tokens: the current #NATokens object.
456 * @exec: the to be executed command-line before having been parsed
458 * Returns: %TRUE if the first relevant parameter found in @exec
459 * command-line is of singular form, %FALSE else.
461 static gboolean
462 is_singular_exec( const NATokens *tokens, const gchar *exec )
464 gboolean singular;
465 gboolean found;
466 gchar *iter;
468 singular = FALSE;
469 found = FALSE;
470 iter = ( gchar * ) exec;
472 while(( iter = g_strstr_len( iter, -1, "%" )) != NULL && !found ){
474 switch( iter[1] ){
475 case 'b':
476 case 'd':
477 case 'f':
478 case 'm':
479 case 'o':
480 case 'u':
481 case 'w':
482 case 'x':
483 found = TRUE;
484 singular = TRUE;
485 break;
487 case 'B':
488 case 'D':
489 case 'F':
490 case 'M':
491 case 'O':
492 case 'U':
493 case 'W':
494 case 'X':
495 found = TRUE;
496 singular = FALSE;
497 break;
499 /* all other parameters are irrelevant according to DES-EMA
500 * c: selection count
501 * h: hostname
502 * n: username
503 * p: port
504 * s: scheme
505 * %: %
509 iter += 2; /* skip the % sign and the character after */
512 return( singular );
516 * na_tokens_parse_singular:
517 * @tokens: a #NATokens object.
518 * @input: the input string, may or may not contain tokens.
519 * @i: the number of the iteration in a multiple selection, starting with zero.
520 * @utf8: whether the @input string is UTF-8 encoded, or a standard ASCII
521 * string.
523 * A command is said of 'singular form' when its first parameter is not
524 * of plural form. In the case of a multiple selection, singular form
525 * commands are executed one time for each element of the selection
527 static gchar *
528 parse_singular( const NATokens *tokens, const gchar *input, guint i, gboolean utf8 )
530 GString *output;
531 gchar *iter, *prev_iter, *tmp;
532 const gchar *nth;
534 output = g_string_new( "" );
536 /* return NULL if input is NULL
538 if( !input ){
539 return( g_string_free( output, TRUE ));
542 /* return an empty string if input is empty
544 if( utf8 ){
545 if( !g_utf8_strlen( input, -1 )){
546 return( g_string_free( output, FALSE ));
548 } else {
549 if( !strlen( input )){
550 return( g_string_free( output, FALSE ));
554 iter = ( gchar * ) input;
555 prev_iter = iter;
557 while(( iter = g_strstr_len( iter, -1, "%" ))){
558 output = g_string_append_len( output, prev_iter, strlen( prev_iter ) - strlen( iter ));
560 switch( iter[1] ){
561 case 'b':
562 if( tokens->private->basenames ){
563 nth = ( const gchar * ) g_slist_nth_data( tokens->private->basenames, i );
564 if( nth ){
565 output = g_string_append( output, nth );
568 break;
570 case 'B':
571 if( tokens->private->basenames_str ){
572 output = g_string_append( output, tokens->private->basenames_str );
574 break;
576 case 'c':
577 g_string_append_printf( output, "%d", tokens->private->count );
578 break;
580 case 'd':
581 if( tokens->private->basedirs ){
582 nth = ( const gchar * ) g_slist_nth_data( tokens->private->basedirs, i );
583 if( nth ){
584 output = g_string_append( output, nth );
587 break;
589 case 'D':
590 if( tokens->private->basedirs_str ){
591 output = g_string_append( output, tokens->private->basedirs_str );
593 break;
595 case 'f':
596 if( tokens->private->filenames ){
597 nth = ( const gchar * ) g_slist_nth_data( tokens->private->filenames, i );
598 if( nth ){
599 output = g_string_append( output, nth );
602 break;
604 case 'F':
605 if( tokens->private->filenames_str ){
606 output = g_string_append( output, tokens->private->filenames_str );
608 break;
610 case 'h':
611 if( tokens->private->hostname ){
612 tmp = g_shell_quote( tokens->private->hostname );
613 output = g_string_append( output, tmp );
614 g_free( tmp );
616 break;
618 case 'm':
619 if( tokens->private->mimetypes ){
620 nth = ( const gchar * ) g_slist_nth_data( tokens->private->mimetypes, i );
621 if( nth ){
622 tmp = g_shell_quote( nth );
623 output = g_string_append( output, tmp );
624 g_free( tmp );
627 break;
629 case 'M':
630 if( tokens->private->mimetypes_str ){
631 output = g_string_append( output, tokens->private->mimetypes_str );
633 break;
635 /* no-op operators */
636 case 'o':
637 case 'O':
638 break;
640 case 'n':
641 if( tokens->private->username ){
642 tmp = g_shell_quote( tokens->private->username );
643 output = g_string_append( output, tmp );
644 g_free( tmp );
646 break;
648 case 'p':
649 g_string_append_printf( output, "%d", tokens->private->port );
650 break;
652 case 's':
653 if( tokens->private->scheme ){
654 tmp = g_shell_quote( tokens->private->scheme );
655 output = g_string_append( output, tmp );
656 g_free( tmp );
658 break;
660 case 'u':
661 if( tokens->private->uris ){
662 nth = ( const gchar * ) g_slist_nth_data( tokens->private->uris, i );
663 if( nth ){
664 tmp = g_shell_quote( nth );
665 output = g_string_append( output, tmp );
666 g_free( tmp );
669 break;
671 case 'U':
672 if( tokens->private->uris_str ){
673 output = g_string_append( output, tokens->private->uris_str );
675 break;
677 case 'w':
678 if( tokens->private->basenames_woext ){
679 nth = ( const gchar * ) g_slist_nth_data( tokens->private->basenames_woext, i );
680 if( nth ){
681 output = g_string_append( output, nth );
684 break;
686 case 'W':
687 if( tokens->private->basenames_woext_str ){
688 output = g_string_append( output, tokens->private->basenames_woext_str );
690 break;
692 case 'x':
693 if( tokens->private->exts ){
694 nth = ( const gchar * ) g_slist_nth_data( tokens->private->exts, i );
695 if( nth ){
696 output = g_string_append( output, nth );
699 break;
701 case 'X':
702 if( tokens->private->exts_str ){
703 output = g_string_append( output, tokens->private->exts_str );
705 break;
707 /* a percent sign
709 case '%':
710 output = g_string_append_c( output, '%' );
711 break;
714 iter += 2; /* skip the % sign and the character after */
715 prev_iter = iter; /* store the new start of the string */
718 output = g_string_append_len( output, prev_iter, strlen( prev_iter ));
720 return( g_string_free( output, FALSE ));