#!/bin/ksh # Nautilus-Actions # A Nautilus extension which offers configurable context menu actions. # # Copyright (C) 2005 The GNOME Foundation # Copyright (C) 2006-2008 Frederic Ruaudel and others (see AUTHORS) # Copyright (C) 2009-2014 Pierre Wieser and others (see AUTHORS) # # Nautilus-Actions 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 2 of # the License, or (at your option) any later version. # # Nautilus-Actions 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 Nautilus-Actions; see the file COPYING. If not, see # . # # Authors: # Frederic Ruaudel # Rodrigo Moya # Pierre Wieser # ... and many others (see AUTHORS) errs=0 # will be the exit code of the script my_cmd="${0}" # e.g. "./make-ks.sh" my_parms="$*" # e.g. "-host toaster" my_cmdline="${my_cmd} ${my_parms}" me="${my_cmd##*/}" # e.g. "make-ks.sh" # used in msg and msgerr functions my_tmproot="/tmp/$(echo ${me} | sed 's?\..*$??').$$" # e.g. "/tmp/make-ks.1978" # These three functions must be defined using the name() syntax in order # to share traps with the caller process (cf. man (1) ksh). # trap_exit() { clear_tmpfiles [ "${opt_verbose}" = "yes" -o ${errs} -gt 0 ] && msg "exiting with code ${errs}" exit ${errs} } trap_int() { msg "quitting on keyboard interrupt" let errs+=1 exit } trap_term() { [ "${opt_verbose}" = "yes" ] && msg "quitting on TERM signal" exit } # setup the different trap functions trap 'trap_term' TERM trap 'trap_int' INT trap 'trap_exit' EXIT function clear_tmpfiles { \rm -f ${my_tmproot}.* } function msg { typeset _eol="\n" [ $# -ge 2 ] && _eol="${2}" printf "[%s] %s${_eol}" ${me} "${1}" return 0 } function msgerr { msg "error: ${1}" 1>&2 return $? } function msgwarn { msg "warning: ${1}" 1>&2 return $? } function msg_help { msg_version echo " This program is meant to safely migrate items, menus and actions, and preferences from GConf to .desktop files. Users items and preferences are automatically migrated when Nautilus-Actions menu plugin is loaded by Nautilus file-manager, or when one of the utilities is run by the user. A system administrator should nonetheless run himself this script with '--admin' option in order to migrate mandatory items and preferences he or a predecessor may have previously set. Usage: ${my_cmd} [options] --[no]help print this message, and exit [${opt_help_def}] --[no]version print script version, and exit [${opt_version_def}] --[no]dummy dummy execution [${opt_dummy_def}] --[no]verbose runs verbosely [${opt_verbose_def}] --dir=/dirname directory where the migrated objects must be stored [${opt_dir_def}] --[no]force force the rewriting of an already existing item [${opt_force_def}] --[no]delete delete the item after the migration [${opt_delete_def}] --[no]admin only execute administration tasks [${opt_admin_def}]" } function msg_version { echo " @PACKAGE_NAME@ v @PACKAGE_VERSION@ Copyright (C) 2011, 2012 Pierre Wieser." } function command { typeset _cmd="${1}" if [ "${opt_dummy}" = "yes" -o "${opt_verbose}" = "yes" ]; then typeset _prefix="" [ "${opt_dummy}" = "yes" ] && _prefix="[dummy] " echo "${_prefix}${_cmd}" fi if [ "${opt_dummy}" = "no" ]; then eval ${_cmd} fi } # initialize common command-line options nbopt=$# opt_help= opt_help_def="no" opt_dummy= opt_dummy_def="yes" opt_version= opt_version_def="no" opt_verbose= opt_verbose_def="no" # a first loop over command line arguments to detect verbose mode while : do # break when all arguments have been read case $# in 0) break ;; esac # get and try to interpret the next argument _option=$1 shift # make all options have two hyphens _orig_option=${_option} case ${_option} in --*) ;; -*) _option=-${_option} ;; esac # now process options and their argument case ${_option} in --noverb | --noverbo | --noverbos | --noverbose) opt_verbose="no" ;; --verb | --verbo | --verbos | --verbose) opt_verbose="yes" ;; esac done [ "${opt_verbose}" = "yes" ] && msg "setting opt_verbose to 'yes'" # we have scanned all command-line arguments in order to detect an # opt_verbose option; # reset now arguments so that they can be scanned again in main script set -- ${my_parms} # setting defaults # at this time, we consider that root only want run admin tasks if [ $(id -u) -eq 0 ]; then opt_dir_def="/usr" conf_dir="@sysconfdir@/xdg" dir_mode="0755" else opt_dir_def="${HOME}/.local" conf_dir="${HOME}/.config" dir_mode="0750" fi # interpreting command-line arguments opt_dir= opt_dir_def="${opt_dir_def}/share/file-manager/actions" # default is to not force rewriting of already existing items # instead they are renumbered opt_force= opt_force_def="no" # default is to not delete the migrated item opt_delete= opt_delete_def="no" # default is to run for standard user opt_admin= opt_admin_def="no" # path of the branch which contains the configurations na_package="/apps/@PACKAGE@" na_configurations="${na_package}/configurations" na_preferences="${na_package}/preferences" na_mandatory="${na_package}/mandatory" na_providers="${na_package}/io-providers" # read an item from GConf, printing as .desktop on stdout na_print_program="@bindir@/nautilus-actions-print" # loop over command line arguments pos=0 while : do # break when all arguments have been read case $# in 0) break ;; esac # get and try to interpret the next argument option=$1 shift # make all options have two hyphens orig_option=${option} case ${option} in --*) ;; -*) option=-${option} ;; esac # split and extract argument for options that take one case ${option} in --*=*) optarg=$(echo ${option} | sed -e 's/^[^=]*=//') option=$(echo ${option} | sed 's/=.*//') ;; # these options take a mandatory argument # since, we didn't find it in 'option', so it should be # next word in the command line --di | --dir) optarg=$1 shift ;; esac # now process options and their argument case ${option} in --a | --ad | --adm | --admi | --admin) [ "${opt_verbose}" = "yes" ] && msg "setting opt_admin to 'yes'" opt_admin="yes" ;; --de | --del | --dele | --delet | --delete) [ "${opt_verbose}" = "yes" ] && msg "setting opt_delete to 'yes'" opt_delete="yes" ;; --di | --dir) [ "${opt_verbose}" = "yes" ] && msg "setting opt_dir to '${optarg}'" opt_dir="${optarg}" ;; --du | --dum | --dumm | --dummy) [ "${opt_verbose}" = "yes" ] && msg "setting opt_dummy to 'yes'" opt_dummy="yes" ;; --f | --fo | --for | --forc | --force) [ "${opt_verbose}" = "yes" ] && msg "setting opt_force to 'yes'" opt_force="yes" ;; --h | --he | --hel | --help) [ "${opt_verbose}" = "yes" ] && msg "setting opt_help to 'yes'" opt_help="yes" ;; --noa | --noad | --noadm | --noadmi | --noadmin) [ "${opt_verbose}" = "yes" ] && msg "setting opt_admin to 'no'" opt_admin="no" ;; --node | --nodel | --nodele | --nodelet | --nodelete) [ "${opt_verbose}" = "yes" ] && msg "setting opt_delete to 'no'" opt_delete="no" ;; --nodu | --nodum | --nodumm | --nodummy) [ "${opt_verbose}" = "yes" ] && msg "setting opt_dummy to 'no'" opt_dummy="no" ;; --nof | --nofo | --nofor | --noforc | --noforce) [ "${opt_verbose}" = "yes" ] && msg "setting opt_force to 'no'" opt_force="no" ;; --noh | --nohe | --nohel | --nohelp) [ "${opt_verbose}" = "yes" ] && msg "setting opt_help to 'no'" opt_help="no" ;; --noverb | --noverbo | --noverbos | --noverbose) ;; --novers | --noversi | --noversio | --noversion) [ "${opt_verbose}" = "yes" ] && msg "setting opt_version to 'no'" opt_version="no" ;; --verb | --verbo | --verbos | --verbose) ;; --vers | --versi | --versio | --version) [ "${opt_verbose}" = "yes" ] && msg "setting opt_version to 'yes'" opt_version="yes" ;; --*) msgerr "unrecognized option: '${orig_option}'" let errs+=1 ;; # positional parameters *) let pos+=1 #if [ ${pos} -eq 1 ]; then # [ "${opt_verbose}" = "yes" ] && msg "setting opt_output to '${option}'" # opt_output=${option} #else msgerr "unexpected positional parameter #${pos}: '${option}'" let errs+=1 #fi ;; esac done # set option defaults # does not work with /bin/sh ?? #set | grep -e '^opt_' | cut -d= -f1 | while read _name; do # if [ "$(echo ${_name} | sed 's/.*\(_def\)/\1/')" != "_def" ]; then # _value="$(eval echo "$"${_name})" # if [ "${_value}" = "" ]; then # eval ${_name}="$(eval echo "$"${_name}_def)" # fi # fi #done opt_help=${opt_help:-${opt_help_def}} opt_dummy=${opt_dummy:-${opt_dummy_def}} opt_verbose=${opt_verbose:-${opt_verbose_def}} opt_version=${opt_version:-${opt_version_def}} opt_dir=${opt_dir:-${opt_dir_def}} opt_delete=${opt_delete:-${opt_delete_def}} opt_force=${opt_force:-${opt_force_def}} opt_admin=${opt_admin:-${opt_admin_def}} if [ "${opt_help}" = "yes" -o ${nbopt} -eq 0 ]; then msg_help echo "" exit fi if [ "${opt_version}" = "yes" ]; then msg_version echo "" exit fi # only root may run with '--admin' option # but root may also run with standard option for his own needs if [ "${opt_admin}" = "yes" -a $(id -u) -ne 0 ]; then msgerr "only root may run with '--admin' option" let errs+=1 fi if [ ${errs} -gt 0 ]; then msg "${errs} error(s) have been detected" msg "try '${my_cmd} --help' for usage" exit fi function add_pref { typeset _group="${1}" typeset _key="${2}" typeset _type="${3}" typeset _value="${4}" typeset _newvalue="${4}" if [ "${_type}" = "list" ]; then _newvalue="$(echo "${_value}" | sed -e 's/^\[//' -e 's/]$//' -e 's/,/;/g');" fi # make sure that GConf is at the end of the list of I/O providers if [ "${_key}" = "io-providers-order" ]; then if [ "$(echo ${_newvalue} | grep na-gconf)" != "" ]; then typeset _v="$(echo "${_newvalue}" | sed -e 's/na-gconf//');na-gconf;" _newvalue="$(echo "${_v}" | sed -e 's/;\+/;/g')" fi fi echo "${_key} = ${_newvalue}" >> ${my_tmproot}.${_group} } function add_pref_provider { typeset _group="${1}" typeset _key="${2}" typeset _value="${3}" typeset _groupname="$(echo "${_group}" | sed 's/ /_/g')" echo "${_key} = ${value}" >> ${my_tmproot}.${_groupname} } function create_first_level { typeset _newvalue="$(echo "$1" | sed -e 's/^\[//' -e 's/]$//' -e 's/,/;/g');" echo "ItemsList = ${_newvalue}" >> ${my_tmproot}.zero } # --------------------------------------------------------------------- # MAIN CODE # if we do not have gconftool-2, then exit which gconftool-2 1>/dev/null 2>&1 || { msg "gconftool-2: not available"; exit 1; } # create the destination directory if it does not yet exist command "mkdir -m ${dir_mode} -p ${opt_dir} || exit 1" let nbitems=0 let new=0 let ignored=0 let duplicates=0 # list objects in configurations/ subdir # each object, action or menu, is then exported in .a .desktop format # to be written to its .desktop file if [ "${opt_admin}" = "no" ]; then for dir in $(gconftool-2 --all-dirs ${na_configurations}); do id=${dir##*/} let nbitems+=1 [ "${opt_verbose}" = "yes" ] && msg "item=${id}" if [ ! -e ${opt_dir}/${id}.desktop -o "${opt_force}" = "yes" ]; then command "${na_print_program} --id ${id} | grep -v 'nautilus-actions-print' > ${opt_dir}/${id}.desktop" let new+=1 else # the item has most probably already been migrated # if there is no sensible modification, just ignore it # else create a copy with a new id msgwarn "${opt_dir}/${id}.desktop already exists" tmpfile=$(mktemp) command "${na_print_program} --id ${id} | grep -v 'nautilus-actions-print' > ${tmpfile}" diff -qB ${tmpfile} ${opt_dir}/${id}.desktop 1>/dev/null 2>&1 if [ $? -eq 0 ]; then msg "${opt_dir}/${id}.desktop has no modification, just ignoring it" let ignored+=1 command "rm -f ${tmpfile}" else i=0 while [ -e ${opt_dir}/${id}-${i}.desktop ]; do let i+=1 done command "mv ${tmpfile} ${opt_dir}/${id}-${i}.desktop" let duplicates+=1 fi fi done msg "${nbitems} items read from GConf: new=${new}, ignored=${ignored}, duplicates=${duplicates}" fi # we are using this same script to migrate preferences to .conf files # mandatory preferences go to SYSCONFDIR/xdg/nautilus-actions/nautilus-actions.conf # while user preferences go to HOME/.config/nautilus-actions/nautilus-actions.conf # Note also that the GConf I/O provider will be disabled for writings # after this migration destdir=${conf_dir}/@PACKAGE@ command "mkdir -m ${dir_mode} -p ${destdir}" destconf=${destdir}/@PACKAGE@.conf rm -f ${my_tmproot}.nact rm -f ${my_tmproot}.runtime rm -f ${my_tmproot}.io-provider_* if [ "${opt_admin}" = "no" ]; then gconftool-2 --all-entries ${na_preferences} | while read key x value; do case ${key} in add-capability-dialog) add_pref "nact" "capability-add-capability-wsp" list "${value}" ;; add-scheme-dialog) add_pref "nact" "scheme-add-scheme-wsp" list "${value}" ;; assistant-esc-confirm) add_pref "nact" "${key}" str "${value}" ;; assistant-esc-quit) add_pref "nact" "${key}" str "${value}" ;; auto-save-on) add_pref "nact" "main-save-auto" str "${value}" ;; auto-save-period) add_pref "nact" "main-save-period" str "${value}" ;; export-assistant) add_pref "nact" "export-assistant-wsp" list "${value}" ;; export-ask-user) add_pref "nact" "export-ask-user-wsp" list "${value}" ;; export-ask-user-last-format) add_pref "nact" "${key}" str "${value}" ;; export-folder-uri) add_pref "nact" "export-assistant-lfu" str "${value}" ;; export-format) add_pref "nact" "export-preferred-format" str "${value}" ;; icommand-command-chooser) add_pref "nact" "command-command-chooser-wsp" list "${value}" ;; icommand-folder-uri) add_pref "nact" "command-command-chooser-lfu" list "${value}" ;; icommand-legend-dialog) add_pref "nact" "command-legend-wsp" list "${value}" ;; icommand-working-dir-dialog) add_pref "nact" "command-working-dir-chooser-wsp" list "${value}" ;; icommand-working-dir-uri) add_pref "nact" "command-working-dir-chooser-lfu" str "${value}" ;; icons-chooser) add_pref "nact" "item-icon-chooser-wsp" list "${value}" ;; icons-path) add_pref "nact" "item-icon-chooser-last-file-uri" str "${value}" ;; ienvironment-show-if-running-dialog) add_pref "nact" "environment-show-if-running-wsp" list "${value}" ;; ienvironment-show-if-running-uri) add_pref "nact" "environment-show-if-running-lfu" str "${value}" ;; ienvironment-try-exec-dialog) add_pref "nact" "environment-try-exec-wsp" list "${value}" ;; ienvironment-try-exec-uri) add_pref "nact" "environment-try-exec-lfu" str "${value}" ;; ifolders-chooser) add_pref "nact" "folder-chooser-wsp" list "${value}" ;; ifolders-path) add_pref "nact" "folder-chooser-lfu" str "file://${value}" ;; import-ask-user) add_pref "nact" "import-ask-user-wsp" list "${value}" ;; import-ask-user-last-mode) add_pref "nact" "${key}" str "${value}" ;; import-assistant) add_pref "nact" "import-assistant-wsp" list "${value}" ;; import-folder-uri) add_pref "nact" "import-assistant-lfu" str "${value}" ;; import-keep-choice) add_pref "nact" "import-ask-user-keep-last-choice" list "${value}" ;; import-mode) add_pref "nact" "import-preferred-mode" str "${value}" ;; io-providers-order) add_pref "nact" "io-providers-write-order" list "${value}" ;; iprefs-add-about-item) add_pref "runtime" "items-add-about-item" str "${value}" ;; iprefs-alphabetical-order) add_pref "runtime" "items-list-order-mode" str "${value}" ;; iprefs-create-root-menu) add_pref "runtime" "items-create-root-menu" str "${value}" ;; iprefs-level-zero) # for now, keep the level zero order as a runtime preference # in nautilus-actions.conf #create_first_level "${value}" add_pref "runtime" "items-level-zero-order" list "${value}" ;; iprefs-relabel-actions) add_pref "nact" "relabel-when-duplicate-action" str "${value}" ;; iprefs-relabel-menus) add_pref "nact" "relabel-when-duplicate-menu" str "${value}" ;; iprefs-relabel-profiles) add_pref "nact" "relabel-when-duplicate-profile" str "${value}" ;; main-edit-toolbar) add_pref "nact" "main-toolbar-edit-display" str "${value}" ;; main-file-toolbar) add_pref "nact" "main-toolbar-file-display" str "${value}" ;; main-help-toolbar) add_pref "nact" "main-toolbar-help-display" str "${value}" ;; main-tools-toolbar) add_pref "nact" "main-toolbar-tools-display" str "${value}" ;; main-paned) add_pref "nact" "main-paned-width" str "${value}" ;; main-window) add_pref "nact" "main-window-wsp" list "${value}" ;; preferences-editor) add_pref "nact" "preferences-wsp" list "${value}" ;; schemes) add_pref "nact" "scheme-default-list" list "${value}" ;; esac done fi # migrate mandatory keys # /apps/nautilus-actions/mandatory/all/locked -> [runtime] # /apps/nautilus-actions/mandatory//locked -> [io-provider ] if [ "${opt_admin}" = "yes" ]; then for dir in $(gconftool-2 --all-dirs ${na_mandatory}); do bdir=${dir##*/} if [ "${bdir}" != "na-gconf" ]; then value=$(gconftool-2 --get ${dir}/locked 2>/dev/null) if [ "${value}" != "" ]; then # 'all/locked' means 'all preferences are read-only' if [ "${bdir}" = "all" ]; then add_pref "runtime" "preferences-locked" str "${value}" # '/locked' means that the i/o provider is read-only elif [ "${value}" = "true" ]; then add_pref_provider "io-provider ${bdir}" "writable" "false" fi fi fi done fi # migrate io-providers keys # locking GConf (not reconducting its keys), but setting it as read-only if [ "${opt_admin}" = "no" ]; then for dir in $(gconftool-2 --all-dirs ${na_providers}); do bdir=${dir##*/} if [ "${bdir}" != "na-gconf" ]; then gconftool-2 --all-entries ${dir} | while read key x value; do case ${key} in read-at-startup) add_pref_provider "io-provider ${bdir}" "readable" "${value}" ;; writable) add_pref_provider "io-provider ${bdir}" "${key}" "${value}" ;; esac done fi done fi # whether we are running admin tasks or not, we try to force the GConf i/o provider # to be locked cat <${my_tmproot}.io-provider_na-gconf # Starting with 3.1.0, GConf as I/O provider is deprecated readable=true writable = false ! let nbprefs=0 if [ -e ${destconf} ]; then msg "${destconf} already exists: do not replace it, update the N-A configuration file instead" command "@pkglibexecdir@/na-set-conf --group 'io-provider na-gconf' --key writable --type bool --value false" else nbprefs=$(cat ${my_tmproot}.nact ${my_tmproot}.runtime ${my_tmproot}.io-provider_* 2>/dev/null | wc -l) if [ ${nbprefs} -gt 0 ]; then rm -f ${destconf} if [ -s ${my_tmproot}.nact ]; then cat <>${destconf} [nact] $(sort < ${my_tmproot}.nact 2>/dev/null) ! fi if [ -s ${my_tmproot}.runtime ]; then cat <>${destconf} [runtime] $(sort < ${my_tmproot}.runtime 2>/dev/null) ! fi for f in $(ls -1 ${my_tmproot}.io-provider_* 2>/dev/null); do group="$(echo ${f} | sed -e "s,^${my_tmproot}\.,," -e 's/_/ /g')" cat <>${destconf} [${group}] $(sort < ${f}) ! done fi msg "${nbprefs} migrated preferences" fi #if [ -s ${my_tmproot}.zero ]; then # cat <${opt_dir}/level-zero.directory #[Desktop Entry] #$(cat ${my_tmproot}.zero) #! #fi # force sync killall gconfd-2 2>/dev/null let count=${nbitems}+${nbprefs} #echo "count=${count}" # at the end, we delete all package branch from GConf if [ "${opt_delete}" = "yes" -a ${count} -gt 0 ]; then if [ "${opt_admin}" = "yes" ]; then # rather a bad hack to find where mandatory keys are stored by GConf # na-delete-xmltree directly removes our branch from the XML tree! xml=$(find /etc -name gconf.xml.mandatory)/%gconf-tree.xml path='/gconf/dir[@name="apps"]/dir[@name="@PACKAGE@"]' command "@pkglibexecdir@/na-delete-xmltree --path '${path}' --xml ${xml} > ${xml}2" command "mv ${xml}2 ${xml}" else # this does not work for mandatory items command "gconftool-2 --recursive-unset ${na_package}" fi fi # re-force sync killall gconfd-2 2>/dev/null exit