#!/bin/sh
#---------------------------------------------
#   xdg-icon-resource
#
#   Utility script to install icons on a Linux desktop.
#
#   Refer to the usage() function below for usage.
#
#   Copyright 2006, Kevin Krammer <kevin.krammer@gmx.at>
#   Copyright 2006, Jeremy White <jwhite@codeweavers.com>
#
#   LICENSE:
#
#   Permission is hereby granted, free of charge, to any person obtaining a
#   copy of this software and associated documentation files (the "Software"),
#   to deal in the Software without restriction, including without limitation
#   the rights to use, copy, modify, merge, publish, distribute, sublicense,
#   and/or sell copies of the Software, and to permit persons to whom the
#   Software is furnished to do so, subject to the following conditions:
#
#   The above copyright notice and this permission notice shall be included
#   in all copies or substantial portions of the Software.
#
#   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
#   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
#   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
#   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
#   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
#   OTHER DEALINGS IN THE SOFTWARE.
#
#---------------------------------------------

manualpage()
{
cat << _MANUALPAGE
Name

xdg-icon-resource - command line tool for (un)installing icon resources

Synopsis

xdg-icon-resource install [--noupdate] [--novendor] [--theme theme] [--context
context] [--size size] [--mode mode] icon-file [icon-name]

xdg-icon-resource uninstall [--noupdate] [--theme theme] [--context context]
[--size size] [--mode mode] icon-name

xdg-icon-resource forceupdate [--theme theme] [--mode mode]

xdg-icon-resource { --help | --manual | --version }

Description

The xdg-icon-resource program can be used to install icon resources into the
desktop icon system in order to illustrate menu entries, to depict desktop
icons or to graphically represent file types.

The desktop icon system identifies icons by name. Depending on the required
size, the choice of icon theme and the context in which the icon is used, the
desktop icon system locates an appropriate icon resource to depict an icon.
Icon resources can be XPM files, PNG files or SVG files.

The desktop icon system works according to the XDG Icon Theme Specification at
http://www.freedesktop.org/Standards/icon-theme-spec

Commands

install
    Installs the icon file indicated by icon-file to the desktop icon system
    under the name icon-name. If icon-name is not provided the name is derived
    from icon-file. The icon file must have .png, .xpm or .svg as extension. If
    a corresponding .icon file exists in the same location as icon-file it will
    be installed as well.
uninstall
    Removes the icon file indicated by icon-name from the desktop icon system.
forceupdate
    Force an update of the desktop icon system. This is only useful if the last
    call to xdg-icon-resource included the --noupdate option.

Options

--noupdate
    Postpone updating the desktop icon system. If multiple icons are added in
    sequence this flag can be used to indicate that additional changes will
    follow and that it is not necassery to update the desktop icon system right
    away.
--novendor

    Normally, xdg-icon-resource checks to ensure that an icon file to be
    installed in the apps context has a proper vendor prefix. This option can
    be used to disable that check.

    A vendor prefix consists of alpha characters ([a-zA-Z]) and is terminated
    with a dash ("-"). Companies and organizations are encouraged to use a word
    or phrase, preferably the organizations name, for which they hold a
    trademark as their vendor prefix. The purpose of the vendor prefix is to
    prevent name conflicts.

--theme theme
    Installs or removes the icon file as part of theme. If no theme is
    specified the icons will be installed as part of the default hicolor theme.
    Applications may install icons under multiple themes but should at least
    install icons for the default hicolor theme.
--context context
    Specifies the context for the icon. Icons to be used in the application
    menu and as desktop icon should use apps as context which is the default
    context. Icons to be used as file icons should use mimetypes as context.
    Other common contexts are actions, devices, emblems, filesystems and stock.
--size size
    Specifies the size of the icon (required for png and xpm icons). All icons
    must be square. Common sizes for icons in the apps context are: 16, 22, 32,
    48, 64 and 128. Common sizes for icons in the mimetypes context are: 16,
    22, 32, 48, 64 and 128
--mode mode

    mode can be user or system. In user mode the file is (un)installed for the
    current user only. In system mode the file is (un)installed for all users
    on the system. Usually only root is allowed to install in system mode.

    The default is to use system mode when called by root and to use user mode
    when called by a non-root user.

--help
    Show command synopsis.
--manual
    Show this manualpage.
--version
    Show the xdg-utils version information.

Environment Variables

xdg-icon-resource honours the following environment variables:

XDG_UTILS_DEBUG_LEVEL
    Setting this environment variable to a non-zero numerical value makes
    xdg-icon-resource do more verbose reporting on stderr. Setting a higher
    value increases the verbosity.

Exit Codes

An exit code of 0 indicates success while a non-zero exit code indicates
failure. The following failure codes can be returned:

1
    Error in command line syntax.
2
    One of the files passed on the command line did not exist.
3
    A required tool could not be found.
4
    The action failed.
5
    No permission to read one of the files passed on the command line.

See Also

xdg-desktop-icon(1), xdg-desktop-menu(1), xdg-mime(1)

Examples

To install an icon resource to depict a launcher for the application myfoobar,
the company ShinyThings Inc. can use:

xdg-icon-resource install --size 64 shinythings-myfoobar.png

To install an icon for a new application/x-foobar file type one can use:

xdg-icon-resource install --context mimetypes --size 48 ./mime-foobar-48.png application-x-foobar.png
xdg-icon-resource install --context mimetypes --size 64 ./mime-foobar-64.png application-x-foobar.png

This will install two icons with the name application-x-foobar.png but with
different sizes.

_MANUALPAGE
}

usage()
{
cat << _USAGE
xdg-icon-resource - command line tool for (un)installing icon resources

Synopsis

xdg-icon-resource install [--noupdate] [--novendor] [--theme theme] [--context
context] [--size size] [--mode mode] icon-file [icon-name]

xdg-icon-resource uninstall [--noupdate] [--theme theme] [--context context]
[--size size] [--mode mode] icon-name

xdg-icon-resource forceupdate [--theme theme] [--mode mode]

xdg-icon-resource { --help | --manual | --version }

_USAGE
}

#@xdg-utils-common@

#----------------------------------------------------------------------------
#   Common utility functions included in all XDG wrapper scripts
#----------------------------------------------------------------------------

DEBUG()
{
  [ ${XDG_UTILS_DEBUG_LEVEL-0} -lt $1 ] && return 0;
  shift
  echo "$@" >&2
}

#-------------------------------------------------------------
# Exit script on successfully completing the desired operation

exit_success()
{
    if [ $# -gt 0 ]; then
        echo "$@"
        echo
    fi

    exit 0
}


#-----------------------------------------
# Exit script on malformed arguments, not enough arguments
# or missing required option.
# prints usage information

exit_failure_syntax()
{
    if [ $# -gt 0 ]; then
        echo "xdg-icon-resource: $@" >&2
        echo "Try 'xdg-icon-resource --help' for more information." >&2
    else
        usage
        echo "Use 'man xdg-icon-resource' or 'xdg-icon-resource --manual' for additional info."
    fi

    exit 1
}

#-------------------------------------------------------------
# Exit script on missing file specified on command line

exit_failure_file_missing()
{
    if [ $# -gt 0 ]; then
        echo "xdg-icon-resource: $@" >&2
    fi

    exit 2
}

#-------------------------------------------------------------
# Exit script on failure to locate necessary tool applications

exit_failure_operation_impossible()
{
    if [ $# -gt 0 ]; then
        echo "xdg-icon-resource: $@" >&2
    fi

    exit 3
}

#-------------------------------------------------------------
# Exit script on failure returned by a tool application

exit_failure_operation_failed()
{
    if [ $# -gt 0 ]; then
        echo "xdg-icon-resource: $@" >&2
    fi

    exit 4
}

#------------------------------------------------------------
# Exit script on insufficient permission to read a specified file

exit_failure_file_permission_read()
{
    if [ $# -gt 0 ]; then
        echo "xdg-icon-resource: $@" >&2
    fi

    exit 5
}

#------------------------------------------------------------
# Exit script on insufficient permission to read a specified file

exit_failure_file_permission_write()
{
    if [ $# -gt 0 ]; then
        echo "xdg-icon-resource: $@" >&2
    fi

    exit 6
}

check_input_file()
{
    if [ ! -e "$1" ]; then
        exit_failure_file_missing "file '$1' does not exist"
    fi
    if [ ! -r "$1" ]; then
        exit_failure_file_permission_read "no permission to read file '$1'"
    fi
}

check_vendor_prefix()
{
    file=`basename "$1"`
    case "$file" in
       [a-zA-Z]*-*)
         return
         ;;
    esac

    echo "xdg-icon-resource: filename '$file' does not have a proper vendor prefix" >&2
    echo 'A vendor prefix consists of alpha characters ([a-zA-Z]) and is terminated' >&2
    echo 'with a dash ("-"). An example filename is '"'example-$file'" >&2
    echo "Use --novendor to override or 'xdg-icon-resource --manual' for additional info." >&2
    exit 1
}

check_output_file()
{
    # if the file exists, check if it is writeable
    # if it does not exists, check if we are allowed to write on the directory
    if [ -e "$1" ]; then
        if [ ! -w "$1" ]; then
            exit_failure_file_permission_write "no permission to write to file '$1'"
        fi
    else
        DIR=`dirname "$1"`
        if [ ! -w "$DIR" -o ! -x "$DIR" ]; then
            exit_failure_file_permission_write "no permission to create file '$1'"
        fi
    fi
}

#----------------------------------------
# Checks for shared commands, e.g. --help

check_common_commands()
{
    while [ $# -gt 0 ] ; do
        parm="$1"
        shift

        case "$parm" in
            --help)
            usage
            echo "Use 'man xdg-icon-resource' or 'xdg-icon-resource --manual' for additional info."
            exit_success
            ;;

            --manual)
            manualpage
            exit_success
            ;;

            --version)
            echo "xdg-icon-resource 1.0beta3"
            exit_success
            ;;
        esac
    done
}

check_common_commands "$@"
if [ ${XDG_UTILS_DEBUG_LEVEL-0} -lt 1 ]; then
    # Be silent
    xdg_redirect_output=" > /dev/null 2> /dev/null"
else
    # All output to stderr
    xdg_redirect_output=" >&2"
fi

#--------------------------------------
# Checks for known desktop environments
# set variable DE to the desktop environments name, lowercase

detectDE()
{
    if [ x"$KDE_FULL_SESSION" = x"true" ]; then DE=kde;
    elif [ x"$GNOME_DESKTOP_SESSION_ID" != x"" ]; then DE=gnome;
    elif xprop -root _DT_SAVE_MODE | grep ' = \"xfce4\"$' >/dev/null 2>&1; then DE=xfce;
    fi
}

#----------------------------------------------------------------------------
# kfmclient exec/openURL can give bogus exit value in KDE <= 3.5.4
# It also always returns 1 in KDE 3.4 and earlier
# Simply return 0 in such case

kfmclient_fix_exit_code()
{
    version=`kde-config --version 2>/dev/null | grep KDE`
    major=`echo $version | sed 's/KDE: \([0-9]\).*/\1/'`
    minor=`echo $version | sed 's/KDE: [0-9]*\.\([0-9]\).*/\1/'`
    release=`echo $version | sed 's/KDE: [0-9]*\.[0-9]*\.\([0-9]\).*/\1/'`
    test "$major" -gt 3 && return $1
    test "$minor" -gt 5 && return $1
    test "$release" -gt 4 && return $1
    return 0
}

update_icon_database()
{
   for x in `echo "$PATH:/opt/gnome/bin" | sed 's/:/ /g'`; do
      DEBUG 3 "Checking $x for gtk-update-icon-cache"
      if [ -x $x/gtk-update-icon-cache ] ; then
         DEBUG 1 "Running $x/gtk-update-icon-cache -f -t \"$1\""
         eval '$x/gtk-update-icon-cache -f -t "$1"'$xdg_redirect_output
         return
      fi
   done
}

[ x"$1" != x"" ] || exit_failure_syntax

mode=
action=
update=yes
size=
theme=hicolor
context=apps
icon_file=
icon_name=

case $1 in
  install)
    action=install
    ;;

  uninstall)
    action=uninstall
    ;;

  forceupdate)
    action=forceupdate
    ;;

  *)
    exit_failure_syntax "unknown command '$1'"
    ;;
esac

shift

vendor=true
while [ $# -gt 0 ] ; do
    parm="$1"
    shift

    case $parm in
      --noupdate)
        update=no
        ;;

      --mode)
        if [ -z "$1" ] ; then
            exit_failure_syntax "mode argument missing for --mode"
        fi
        case "$1" in
          user)
            mode=user
            ;;

          system)
            mode=system
            ;;
            
          *)
            exit_failure_syntax "unknown mode '$1'"
            ;;
        esac
        shift
        ;;

      --theme)
        if [ -z "$1" ] ; then
            exit_failure_syntax "theme argument missing for --theme"
        fi
        theme="$1"
        shift
        ;;

      --size)
        if [ -z "$1" ] ; then
            exit_failure_syntax "size argument missing for --size"
        fi
        size="$1"
        shift
        ;;

      --context)
        if [ -z "$1" ] ; then
            exit_failure_syntax "context argument missing for --context"
        fi
        context="$1"
        shift
        ;;

      --novendor)
        vendor=false
        ;;

      -*)
        exit_failure_syntax "unexpected option '$parm'"
        ;;

      *)
        if [ -n "$icon_name" ] ; then
            exit_failure_syntax "unexpected argument '$parm'"
        elif [ -n "$icon_file" ] ; then
            icon_name="$parm"
        else
            if [ "$action" = "install" ] ; then
                check_input_file "$parm"
            fi
            icon_file="$parm"
        fi
        ;;
    esac
done

# Shouldn't happen
if [ -z "$action" ] ; then
    exit_failure_syntax "command argument missing"
fi

# Shouldn't happen
if [ -z "$context" ] ; then
    exit_failure_syntax "context argument missing"
fi

if [ -z "$mode" ] ; then
    if [ `whoami` = "root" ] ; then
       mode=system
    else
       mode=user
    fi
fi

xdg_dir_name="icons/$theme"

xdg_user_dir="$XDG_DATA_HOME"
[ -n "$xdg_user_dir" ] || xdg_user_dir="$HOME/.local/share"
xdg_user_prefix="$xdg_user_dir/icons"
xdg_user_dir="$xdg_user_dir/$xdg_dir_name"

xdg_global_dir=
xdg_global_prefix=
xdg_system_dirs="$XDG_DATA_DIRS"
[ -n "$xdg_system_dirs" ] || xdg_system_dirs="/usr/local/share/:/usr/share/"
for x in `echo "$xdg_system_dirs" | sed 's/:/ /g'`; do
   if [ -w $x/$xdg_dir_name ] ; then
      xdg_global_prefix="$x/icons"
      xdg_global_dir="$x/$xdg_dir_name"
      break
   fi
done
[ -w $xdg_global_dir ] || xdg_global_dir=

if [ x"$mode" = x"user" ] ; then
    xdg_base_dir="$xdg_user_dir"
else
    xdg_base_dir="$xdg_global_dir"
    if [ -z "$xdg_base_dir" ] ; then
        exit_failure_operation_impossible "No writable system icon directory found."
    fi
fi

if [ x"$action" = x"forceupdate" ] ; then
    if [ -n "$icon_file" ] ; then
      exit_failure_syntax "unexpected argument '$icon_file'"
    fi
    update_icon_database $xdg_base_dir
    exit_success
fi

if [ -z "$icon_file" ] ; then
    if [ x"$action" = x"install" ] ; then
      exit_failure_syntax "icon-file argument missing"
    else
      exit_failure_syntax "icon-name argument missing"
    fi
fi

filetype=
xdg_size_name=
case $icon_file in
  *.xpm)
     filetype=bitmap
     if [ -z "$size" ] ; then
         exit_failure_syntax "*.xpm icons must have the icon size specified with --size"
     fi
     xdg_size_name="${size}x${size}"
     ;;
  *.png)
     filetype=bitmap
     if [ -z "$size" ] ; then
         exit_failure_syntax "*.png icons must have the icon size specified with --size"
     fi
     xdg_size_name="${size}x${size}"
     ;;
  *.svg)
     fileype=svg
     if [ -n "$size" ] ; then
         echo "xdg-icon-resource: Warning, no icon size should be specified for .svg icons"
     fi
     xdg_size_name=scalable
     ;;
  *)
     exit_failure_syntax "icon file to $action must be a *.png, *.xpm or *.svg file"
     ;;
esac

if [ -n "$icon_name" ] ; then
    case $icon_name in
      *.xpm)
         ;;
      *.png)
         ;;
      *.svg)
         ;;
      *)
         exit_failure_syntax "icon name must have a .png, .xpm or .svg extension"
         ;;
    esac
fi

# Start KDE legacy workaround section
function need_kde_icon_path()
{
  local path
  path=`readlink -f "$1" 2> /dev/null` # Normalize path
  DEBUG 2 "need_kde_icon_path $path"
  if [ -z "$path" ] ; then
     DEBUG 2 "need_kde_icon_path RETURN 1 (not needed, no xdg icon dir)"
     return 1; # Not needed
  fi

  # if kde-config not found... return 0
  kde_icon_dirs=`kde-config --path icon 2> /dev/null |sed 's/:/ /g'`
  DEBUG 3 "kde_icon_dirs: $kde_icon_dirs"
  if [ -z "$kde_icon_dirs" ] ; then
     DEBUG 3 "no result from kde-config --path icon"
     DEBUG 2 "need_kde_icon_path RETURN 1 (not needed, no kde icon path)"
     return 1; # Not needed
  fi
  needed=0 # Needed
  for y in $kde_icon_dirs ; do
    x=`readlink -f "$y"` # Normalize path
    DEBUG 3 "Normalize $y --> $x"
    if [ -n "$x" ] ; then
      if [ "$x" = "$path" ] ; then
        needed=1 # Not needed
      fi
      if [ -w "$x" ] ; then
        kde_global_prefix="$x"
        # Take last writable dir
      fi
    fi
  done
  DEBUG 2 "kde_global_prefix: $kde_global_prefix"
  [ $needed -eq "1" ] && DEBUG 2 "need_kde_icon_path RETURN $needed (not needed)"
  [ $needed -eq "0" ] && DEBUG 2 "need_kde_icon_path RETURN $needed (needed)"
  return $needed
}

kde_dir=
if [ x"$mode" = x"user" ] ; then
    xdg_dir="$xdg_base_dir/$xdg_size_name/$context"
    #KDE 3.x doesn't support XDG_DATA_HOME for icons
    #Check if xdg_dir prefix is listed by kde-config --path icon
    #If not, install additional symlink to kdedir 
    if need_kde_icon_path "$xdg_user_prefix" ; then
        kde_user_dir="$HOME/.kde/share/icons/$theme"
        kde_dir="$kde_user_dir/$xdg_size_name/$context"
    fi
    my_umask=077
else
    xdg_dir="$xdg_base_dir/$xdg_size_name/$context"
    #KDE 3.x doesn't support XDG_DATA_DIRS for icons
    #Check if xdg_dir prefix is listed by kde-config --path icon
    #If not, install additional symlink to kdedir 
    if need_kde_icon_path "$xdg_global_prefix" ; then
        kde_global_dir="$kde_global_prefix/$theme"
        kde_dir="$kde_global_dir/$xdg_size_name/$context"
    fi
    my_umask=022
fi
# End KDE legacy workaround section

# Start GNOME legacy workaround section
need_gnome_mime=
[ $context = "mimetypes" ] && need_gnome_mime=true 
# End GNOME legacy workaround section

[ -n "$icon_name" ] || icon_name=`basename $icon_file`

if [ "$vendor" = "true" -a "$action" = "install" -a "$context" = "apps" ] ; then
    check_vendor_prefix "$icon_name"
fi

icon_icon_file=`echo "$icon_file" | sed 's/\.[^\.]*$/.icon/'`
icon_icon_name=`echo "$icon_name" | sed 's/\.[^\.]*$/.icon/'`

DEBUG 1 "$action icon in $xdg_dir"
[ $action = "install" -a -f $icon_icon_file ] && DEBUG 1 "install $icon_icon_name meta file in $xdg_dir"
[ -n "$kde_dir" ] && DEBUG 1 "$action symlink in $kde_dir (KDE 3.x support)"
[ -n "$need_gnome_mime" ] && DEBUG 1 "$action gnome-mime-$icon_name symlink  (GNOME 2.x support)"

case $action in
    install)
        save_umask=`umask`
        umask $my_umask

        mkdir -p $xdg_dir
        eval 'cp $icon_file $xdg_dir/$icon_name'$xdg_redirect_output
        if [ -f "$icon_icon_file" ] ; then
            eval 'cp $icon_icon_file $xdg_dir/$icon_icon_name'$xdg_redirect_output
        fi
        if [ -n "$kde_dir" ] ; then
            mkdir -p $kde_dir
            eval 'ln -s $xdg_dir/$icon_name $kde_dir/$icon_name'$xdg_redirect_output
        fi
        if [ -n "$need_gnome_mime" ] ; then
            eval 'ln -s $xdg_dir/$icon_name $xdg_dir/gnome-mime-$icon_name'$xdg_redirect_output
        fi

        umask $save_umask
        ;;

    uninstall)
        rm -f $xdg_dir/$icon_name
        rm -f $xdg_dir/$icon_icon_name
        if [ -n "$kde_dir" ] ; then
            rm -f $kde_dir/$icon_name
        fi
        if [ -n "$need_gnome_mime" ] ; then
            rm -f $xdg_dir/gnome-mime-$icon_name
        fi

        ;;
esac

if [ x"$update" = x"yes" ] ; then
    update_icon_database "$xdg_base_dir"
fi

exit_success
