/* $Id$ Copyright (C) 2003-2010 tooar This file is part of emelFM2. emelFM2 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. emelFM2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with emelFM2; see the file GPL. If not, see http://www.gnu.org/licenses. */ /** @file plugins/e2p_glob.c @brief plugin for running a command on each selecting item, individually */ #include "emelfm2.h" #include #include "e2_plugins.h" #include "e2_task.h" #include "e2_dialog.h" #include "e2_filelist.h" //hackish mechanism to make each command non-static //FIXME find a way to do this which eliminates _any_ chance of a race static GList *each_command_list = NULL; //mutex for added security with static list of commands static GStaticRecMutex eachcmd_mutex; /** @brief run a command on each selected item This is called from a "run immediately" task thread @param qed pointer to action data for the task @return TRUE if task completed successfully, else FALSE */ static gboolean _e2p_foreachQ (E2_ActionTaskData *qed) { g_static_rec_mutex_lock (&eachcmd_mutex); if (each_command_list == NULL) { g_static_rec_mutex_unlock (&eachcmd_mutex); return FALSE; //possible race-condition ?? } //get the command to run from the back of the commands list //(possible race if another foreach was initiated while this thread was being established !) GList *this = g_list_last (each_command_list); each_command_list = g_list_remove_link (each_command_list, this); g_static_rec_mutex_unlock (&eachcmd_mutex); gint res; guint count; gboolean retval = TRUE; gchar *this_cmd, *utf; gchar *each_cmd = (gchar *) this->data; gchar *utfd = F_FILENAME_FROM_LOCALE (qed->currdir); GString *fullpath = g_string_sized_new (PATH_MAX+NAME_MAX); GPtrArray *names = qed->names; E2_SelectedItemInfo **iterator = (E2_SelectedItemInfo **) names->pdata; e2_filelist_disable_refresh (); for (count = 0; count < names->len; count++, iterator++) { utf = F_FILENAME_FROM_LOCALE ((*iterator)->filename); g_string_printf (fullpath, "%s%s", utfd, utf); //separator comes with dir this_cmd = e2_utils_replace_name_macros (each_cmd, fullpath->str); if (this_cmd != each_cmd) { gdk_threads_enter (); res = e2_command_run (this_cmd, E2_COMMAND_RANGE_DEFAULT, curr_view->treeview #ifdef E2_COMMANDQ , TRUE #endif ); gdk_threads_leave (); g_free (this_cmd); } else res = 1; //no replacement of maccro in each_cmd, force error, quit F_FREE (utf, (*iterator)->filename); if (res != 0) { //FIXME advise user retval = FALSE; break; } } e2_filelist_enable_refresh (); g_free (each_cmd); g_list_free (this); g_string_free (fullpath, TRUE); return retval; } /** @brief initiate repeated command from action data or user choice This action takes its command from action runtime data, or if the latter is NULL, will ask for the command here @param from the button, menu item etc which was activated @param art action runtime data @return TRUE if action was initiated, else FALSE */ static gboolean _e2p_foreach (gpointer from, E2_ActionRuntime *art) { DialogButtons result; gchar *each_command; gpointer saved_command = NULL; if (art->data == NULL) { //we don't yet know what to run for each item result = e2_dialog_line_input (_("repeat action"), _("Action to run for each selected item:"), "", 0, FALSE, &each_command); //a blank entry will not return OK } else { //the action has been initiated from the commandline each_command = g_strdup ((gchar *)art->data); //but we do NOT want the command to be considered as names of items ! saved_command = art->data; art->data = NULL; result = OK; } if (result == OK) { //FIXME support macros other than with english letters if (strstr (each_command, "%f") == NULL && strstr (each_command, "%p") == NULL) { gchar *freeme = each_command; each_command = g_strconcat (each_command, " %f", NULL); g_free (freeme); } g_static_rec_mutex_lock (&eachcmd_mutex); each_command_list = g_list_append (each_command_list, each_command); g_static_rec_mutex_unlock (&eachcmd_mutex); gboolean success = e2_task_do_task (E2_TASK_FOREACH, art, from, _e2p_foreachQ, NULL); if (saved_command != NULL) art->data = saved_command; if (!success) { g_free (each_command); g_static_rec_mutex_lock (&eachcmd_mutex); each_command_list = g_list_delete_link (each_command_list, g_list_last (each_command_list)); g_static_rec_mutex_unlock (&eachcmd_mutex); } return success; } return FALSE; } //aname must be confined to this module static gchar *aname; /** @brief plugin initialization function, called by main program @param p ptr to plugin data struct @return TRUE if the initialization succeeds, else FALSE */ gboolean init_plugin (Plugin *p) { #define ANAME "for_each" aname = _("foreach"); p->signature = ANAME VERSION; p->menu_name = _("For _each.."); p->description = _("Execute an entered command on each selected item separately"); p->icon = "plugin_foreach"E2ICONTB; //use icon file pathname if appropriate if (p->action == NULL) { //don't free name string here E2_Action plugact = {g_strconcat (_A(6),".",aname,NULL),_e2p_foreach,TRUE,E2_ACTION_TYPE_ITEM,0,NULL,NULL}; p->action = e2_plugins_action_register (&plugact); if G_UNLIKELY((p->action == NULL)) { g_free (plugact.name); return FALSE; } //setup mutex to protect threaded access to foreach functionality g_static_rec_mutex_init (&eachcmd_mutex); return TRUE; } return FALSE; } /** @brief cleanup transient things for this plugin @param p pointer to data struct for the plugin @return TRUE if all cleanups were completed */ gboolean clean_plugin (Plugin *p) { gchar *action_name = g_strconcat (_A(6),".",aname,NULL); gboolean ret = e2_plugins_action_unregister (action_name); g_free (action_name); if (ret && each_command_list != NULL) { g_static_rec_mutex_lock (&eachcmd_mutex); e2_list_free_with_data (&each_command_list); g_static_rec_mutex_unlock (&eachcmd_mutex); } return ret; }