1#!/bin/sh 2#--------------------------------------------- 3# xdg-desktop-icon 4# 5# Utility script to install desktop items on a Linux desktop. 6# 7# Refer to the usage() function below for usage. 8# 9# Copyright 2009-2010, Fathi Boudra <fabo@freedesktop.org> 10# Copyright 2009-2010, Rex Dieter <rdieter@fedoraproject.org> 11# Copyright 2006, Kevin Krammer <kevin.krammer@gmx.at> 12# Copyright 2006, Jeremy White <jwhite@codeweavers.com> 13# 14# LICENSE: 15# 16# Permission is hereby granted, free of charge, to any person obtaining a 17# copy of this software and associated documentation files (the "Software"), 18# to deal in the Software without restriction, including without limitation 19# the rights to use, copy, modify, merge, publish, distribute, sublicense, 20# and/or sell copies of the Software, and to permit persons to whom the 21# Software is furnished to do so, subject to the following conditions: 22# 23# The above copyright notice and this permission notice shall be included 24# in all copies or substantial portions of the Software. 25# 26# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 27# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 28# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 29# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 30# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 31# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 32# OTHER DEALINGS IN THE SOFTWARE. 33# 34#--------------------------------------------- 35 36usage() 37{ 38cat << _USAGE 39xdg-desktop-icon - command line tool for (un)installing icons to the desktop 40 41Synopsis 42 43xdg-desktop-icon install [--novendor] FILE 44 45xdg-desktop-icon uninstall FILE 46 47xdg-desktop-icon { --help | --manual | --version } 48 49_USAGE 50} 51 52manualpage() 53{ 54cat << _MANUALPAGE 55Name 56 57xdg-desktop-icon - command line tool for (un)installing icons to the desktop 58 59Synopsis 60 61xdg-desktop-icon install [--novendor] FILE 62 63xdg-desktop-icon uninstall FILE 64 65xdg-desktop-icon { --help | --manual | --version } 66 67Description 68 69The xdg-desktop-icon program can be used to install an application launcher or 70other file on the desktop of the current user. 71 72An application launcher is represented by a *.desktop file. Desktop files are 73defined by the freedesktop.org Desktop Entry Specification. The most important 74aspects of *.desktop files are summarized below. 75 76Commands 77 78install 79 Installs FILE to the desktop of the current user. FILE can be a *.desktop 80 file or any other type of file. 81uninstall 82 Removes FILE from the desktop of the current user. 83 84Options 85 86--novendor 87 88 Normally, xdg-desktop-icon checks to ensure that a *.desktop file to be 89 installed has a vendor prefix. This option can be used to disable that 90 check. 91 92 A vendor prefix consists of alpha characters ([a-zA-Z]) and is terminated 93 with a dash ("-"). Companies and organizations are encouraged to use a word 94 or phrase, preferably the organizations name, for which they hold a 95 trademark as their vendor prefix. The purpose of the vendor prefix is to 96 prevent name conflicts. 97 98--help 99 Show command synopsis. 100--manual 101 Show this manualpage. 102--version 103 Show the xdg-utils version information. 104 105Desktop Files 106 107An application launcher can be added to the desktop by installing a *.desktop 108file. A *.desktop file consists of a [Desktop Entry] header followed by several 109Key=Value lines. 110 111A *.desktop file can provide a name and description for an application in 112several different languages. This is done by adding a language code as used by 113LC_MESSAGES in square brackets behind the Key. This way one can specify 114different values for the same Key depending on the currently selected language. 115 116The following keys are often used: 117 118Value=1.0 119 This is a mandatory field to indicate that the *.desktop file follows the 120 1.0 version of the specification. 121Type=Application 122 This is a mandatory field that indicates that the *.desktop file describes 123 an application launcher. 124Name=Application Name 125 The name of the application. For example Mozilla 126GenericName=Generic Name 127 A generic description of the application. For example Web Browser 128Comment=Comment 129 Optional field to specify a tooltip for the application. For example Visit 130 websites on the Internet 131Icon=Icon File 132 The icon to use for the application. This can either be an absolute path to 133 an image file or an icon-name. If an icon-name is provided an image lookup 134 by name is done in the user's current icon theme. The xdg-icon-resource 135 command can be used to install image files into icon themes. The advantage 136 of using an icon-name instead of an absolute path is that with an icon-name 137 the application icon can be provided in several different sizes as well as 138 in several differently themed styles. 139Exec=Command Line 140 The command line to start the application. If the application can open 141 files the %f placeholder should be specified. When a file is dropped on the 142 application launcher the %f is replaced with the file path of the dropped 143 file. If multiple files can be specified on the command line the %F 144 placeholder should be used instead of %f. If the application is able to 145 open URLs in addition to local files then %u or %U can be used instead of 146 %f or %F. 147 148For a complete oveview of the *.desktop file format please visit http:// 149www.freedesktop.org/wiki/Standards/desktop-entry-spec 150 151Environment Variables 152 153xdg-desktop-icon honours the following environment variables: 154 155XDG_UTILS_DEBUG_LEVEL 156 Setting this environment variable to a non-zero numerical value makes 157 xdg-desktop-icon do more verbose reporting on stderr. Setting a higher 158 value increases the verbosity. 159 160Exit Codes 161 162An exit code of 0 indicates success while a non-zero exit code indicates 163failure. The following failure codes can be returned: 164 1651 166 Error in command line syntax. 1672 168 One of the files passed on the command line did not exist. 1693 170 A required tool could not be found. 1714 172 The action failed. 1735 174 No permission to read one of the files passed on the command line. 175 176See Also 177 178xdg-icon-resource(1) 179 180Examples 181 182The company ShinyThings Inc. has developed an application named "WebMirror" and 183would like to add a launcher for for on the desktop. The company will use 184"shinythings" as its vendor id. In order to add the application to the desktop 185there needs to be a .desktop file for the application: 186 187shinythings-webmirror.desktop: 188 189 [Desktop Entry] 190 Encoding=UTF-8 191 Type=Application 192 193 Exec=webmirror 194 Icon=shinythings-webmirror 195 196 Name=WebMirror 197 Name[nl]=WebSpiegel 198 199Now the xdg-desktop-icon tool can be used to add the webmirror.desktop file to 200the desktop: 201 202xdg-desktop-icon install ./shinythings-webmirror.desktop 203 204To add a README file to the desktop as well, the following command can be used: 205 206xdg-desktop-icon install ./shinythings-README 207 208_MANUALPAGE 209} 210 211#@xdg-utils-common@ 212 213#---------------------------------------------------------------------------- 214# Common utility functions included in all XDG wrapper scripts 215#---------------------------------------------------------------------------- 216 217DEBUG() 218{ 219 [ -z "${XDG_UTILS_DEBUG_LEVEL}" ] && return 0; 220 [ ${XDG_UTILS_DEBUG_LEVEL} -lt $1 ] && return 0; 221 shift 222 echo "$@" >&2 223} 224 225# This handles backslashes but not quote marks. 226first_word() 227{ 228 read first rest 229 echo "$first" 230} 231 232#------------------------------------------------------------- 233# map a binary to a .desktop file 234binary_to_desktop_file() 235{ 236 search="${XDG_DATA_HOME:-$HOME/.local/share}:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}" 237 binary="`which "$1"`" 238 binary="`readlink -f "$binary"`" 239 base="`basename "$binary"`" 240 IFS=: 241 for dir in $search; do 242 unset IFS 243 [ "$dir" ] || continue 244 [ -d "$dir/applications" -o -d "$dir/applnk" ] || continue 245 for file in "$dir"/applications/*.desktop "$dir"/applications/*/*.desktop "$dir"/applnk/*.desktop "$dir"/applnk/*/*.desktop; do 246 [ -r "$file" ] || continue 247 # Check to make sure it's worth the processing. 248 grep -q "^Exec.*$base" "$file" || continue 249 # Make sure it's a visible desktop file (e.g. not "preferred-web-browser.desktop"). 250 grep -Eq "^(NoDisplay|Hidden)=true" "$file" && continue 251 command="`grep -E "^Exec(\[[^]=]*])?=" "$file" | cut -d= -f 2- | first_word`" 252 command="`which "$command"`" 253 if [ x"`readlink -f "$command"`" = x"$binary" ]; then 254 # Fix any double slashes that got added path composition 255 echo "$file" | sed -e 's,//*,/,g' 256 return 257 fi 258 done 259 done 260} 261 262#------------------------------------------------------------- 263# map a .desktop file to a binary 264## FIXME: handle vendor dir case 265desktop_file_to_binary() 266{ 267 search="${XDG_DATA_HOME:-$HOME/.local/share}:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}" 268 desktop="`basename "$1"`" 269 IFS=: 270 for dir in $search; do 271 unset IFS 272 [ "$dir" -a -d "$dir/applications" ] || continue 273 file="$dir/applications/$desktop" 274 [ -r "$file" ] || continue 275 # Remove any arguments (%F, %f, %U, %u, etc.). 276 command="`grep -E "^Exec(\[[^]=]*])?=" "$file" | cut -d= -f 2- | first_word`" 277 command="`which "$command"`" 278 readlink -f "$command" 279 return 280 done 281} 282 283#------------------------------------------------------------- 284# Exit script on successfully completing the desired operation 285 286exit_success() 287{ 288 if [ $# -gt 0 ]; then 289 echo "$@" 290 echo 291 fi 292 293 exit 0 294} 295 296 297#----------------------------------------- 298# Exit script on malformed arguments, not enough arguments 299# or missing required option. 300# prints usage information 301 302exit_failure_syntax() 303{ 304 if [ $# -gt 0 ]; then 305 echo "xdg-desktop-icon: $@" >&2 306 echo "Try 'xdg-desktop-icon --help' for more information." >&2 307 else 308 usage 309 echo "Use 'man xdg-desktop-icon' or 'xdg-desktop-icon --manual' for additional info." 310 fi 311 312 exit 1 313} 314 315#------------------------------------------------------------- 316# Exit script on missing file specified on command line 317 318exit_failure_file_missing() 319{ 320 if [ $# -gt 0 ]; then 321 echo "xdg-desktop-icon: $@" >&2 322 fi 323 324 exit 2 325} 326 327#------------------------------------------------------------- 328# Exit script on failure to locate necessary tool applications 329 330exit_failure_operation_impossible() 331{ 332 if [ $# -gt 0 ]; then 333 echo "xdg-desktop-icon: $@" >&2 334 fi 335 336 exit 3 337} 338 339#------------------------------------------------------------- 340# Exit script on failure returned by a tool application 341 342exit_failure_operation_failed() 343{ 344 if [ $# -gt 0 ]; then 345 echo "xdg-desktop-icon: $@" >&2 346 fi 347 348 exit 4 349} 350 351#------------------------------------------------------------ 352# Exit script on insufficient permission to read a specified file 353 354exit_failure_file_permission_read() 355{ 356 if [ $# -gt 0 ]; then 357 echo "xdg-desktop-icon: $@" >&2 358 fi 359 360 exit 5 361} 362 363#------------------------------------------------------------ 364# Exit script on insufficient permission to write a specified file 365 366exit_failure_file_permission_write() 367{ 368 if [ $# -gt 0 ]; then 369 echo "xdg-desktop-icon: $@" >&2 370 fi 371 372 exit 6 373} 374 375check_input_file() 376{ 377 if [ ! -e "$1" ]; then 378 exit_failure_file_missing "file '$1' does not exist" 379 fi 380 if [ ! -r "$1" ]; then 381 exit_failure_file_permission_read "no permission to read file '$1'" 382 fi 383} 384 385check_vendor_prefix() 386{ 387 file_label="$2" 388 [ -n "$file_label" ] || file_label="filename" 389 file=`basename "$1"` 390 case "$file" in 391 [a-zA-Z]*-*) 392 return 393 ;; 394 esac 395 396 echo "xdg-desktop-icon: $file_label '$file' does not have a proper vendor prefix" >&2 397 echo 'A vendor prefix consists of alpha characters ([a-zA-Z]) and is terminated' >&2 398 echo 'with a dash ("-"). An example '"$file_label"' is '"'example-$file'" >&2 399 echo "Use --novendor to override or 'xdg-desktop-icon --manual' for additional info." >&2 400 exit 1 401} 402 403check_output_file() 404{ 405 # if the file exists, check if it is writeable 406 # if it does not exists, check if we are allowed to write on the directory 407 if [ -e "$1" ]; then 408 if [ ! -w "$1" ]; then 409 exit_failure_file_permission_write "no permission to write to file '$1'" 410 fi 411 else 412 DIR=`dirname "$1"` 413 if [ ! -w "$DIR" -o ! -x "$DIR" ]; then 414 exit_failure_file_permission_write "no permission to create file '$1'" 415 fi 416 fi 417} 418 419#---------------------------------------- 420# Checks for shared commands, e.g. --help 421 422check_common_commands() 423{ 424 while [ $# -gt 0 ] ; do 425 parm="$1" 426 shift 427 428 case "$parm" in 429 --help) 430 usage 431 echo "Use 'man xdg-desktop-icon' or 'xdg-desktop-icon --manual' for additional info." 432 exit_success 433 ;; 434 435 --manual) 436 manualpage 437 exit_success 438 ;; 439 440 --version) 441 echo "xdg-desktop-icon 1.1.0 rc1" 442 exit_success 443 ;; 444 esac 445 done 446} 447 448check_common_commands "$@" 449 450[ -z "${XDG_UTILS_DEBUG_LEVEL}" ] && unset XDG_UTILS_DEBUG_LEVEL; 451if [ ${XDG_UTILS_DEBUG_LEVEL-0} -lt 1 ]; then 452 # Be silent 453 xdg_redirect_output=" > /dev/null 2> /dev/null" 454else 455 # All output to stderr 456 xdg_redirect_output=" >&2" 457fi 458 459#-------------------------------------- 460# Checks for known desktop environments 461# set variable DE to the desktop environments name, lowercase 462 463detectDE() 464{ 465 # see https://bugs.freedesktop.org/show_bug.cgi?id=34164 466 unset GREP_OPTIONS 467 468 if [ -n "${XDG_CURRENT_DESKTOP}" ]; then 469 case "${XDG_CURRENT_DESKTOP}" in 470 GNOME) 471 DE=gnome; 472 ;; 473 KDE) 474 DE=kde; 475 ;; 476 LXDE) 477 DE=lxde; 478 ;; 479 XFCE) 480 DE=xfce 481 esac 482 fi 483 484 if [ x"$DE" = x"" ]; then 485 # classic fallbacks 486 if [ x"$KDE_FULL_SESSION" = x"true" ]; then DE=kde; 487 elif [ x"$GNOME_DESKTOP_SESSION_ID" != x"" ]; then DE=gnome; 488 elif `dbus-send --print-reply --dest=org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.GetNameOwner string:org.gnome.SessionManager > /dev/null 2>&1` ; then DE=gnome; 489 elif xprop -root _DT_SAVE_MODE 2> /dev/null | grep ' = \"xfce4\"$' >/dev/null 2>&1; then DE=xfce; 490 elif xprop -root 2> /dev/null | grep -i '^xfce_desktop_window' >/dev/null 2>&1; then DE=xfce 491 fi 492 fi 493 494 if [ x"$DE" = x"" ]; then 495 # fallback to checking $DESKTOP_SESSION 496 case "$DESKTOP_SESSION" in 497 gnome) 498 DE=gnome; 499 ;; 500 LXDE) 501 DE=lxde; 502 ;; 503 xfce|xfce4) 504 DE=xfce; 505 ;; 506 esac 507 fi 508 509 if [ x"$DE" = x"" ]; then 510 # fallback to uname output for other platforms 511 case "$(uname 2>/dev/null)" in 512 Darwin) 513 DE=darwin; 514 ;; 515 esac 516 fi 517 518 if [ x"$DE" = x"gnome" ]; then 519 # gnome-default-applications-properties is only available in GNOME 2.x 520 # but not in GNOME 3.x 521 which gnome-default-applications-properties > /dev/null 2>&1 || DE="gnome3" 522 fi 523} 524 525#---------------------------------------------------------------------------- 526# kfmclient exec/openURL can give bogus exit value in KDE <= 3.5.4 527# It also always returns 1 in KDE 3.4 and earlier 528# Simply return 0 in such case 529 530kfmclient_fix_exit_code() 531{ 532 version=`LC_ALL=C.UTF-8 kde-config --version 2>/dev/null | grep '^KDE'` 533 major=`echo $version | sed 's/KDE.*: \([0-9]\).*/\1/'` 534 minor=`echo $version | sed 's/KDE.*: [0-9]*\.\([0-9]\).*/\1/'` 535 release=`echo $version | sed 's/KDE.*: [0-9]*\.[0-9]*\.\([0-9]\).*/\1/'` 536 test "$major" -gt 3 && return $1 537 test "$minor" -gt 5 && return $1 538 test "$release" -gt 4 && return $1 539 return 0 540} 541 542[ x"$1" != x"" ] || exit_failure_syntax 543 544action= 545desktop_file= 546 547case $1 in 548 install) 549 action=install 550 ;; 551 552 uninstall) 553 action=uninstall 554 ;; 555 556 *) 557 exit_failure_syntax "unknown command '$1'" 558 ;; 559esac 560 561shift 562 563vendor=true 564while [ $# -gt 0 ] ; do 565 parm=$1 566 shift 567 568 case $parm in 569 --novendor) 570 vendor=false 571 ;; 572 573 -*) 574 exit_failure_syntax "unexpected option '$parm'" 575 ;; 576 577 *) 578 if [ -n "$desktop_file" ] ; then 579 exit_failure_syntax "unexpected argument '$parm'" 580 fi 581 if [ "$action" = "install" ] ; then 582 check_input_file "$parm" 583 fi 584 desktop_file=$parm 585 ;; 586 esac 587done 588 589# Shouldn't happen 590if [ -z "$action" ] ; then 591 exit_failure_syntax "command argument missing" 592fi 593 594if [ -z "$desktop_file" ] ; then 595 exit_failure_syntax "FILE argument missing" 596fi 597 598filetype= 599case "$desktop_file" in 600 *.desktop) 601 filetype=desktop 602 if [ "$vendor" = "true" -a "$action" = "install" ] ; then 603 check_vendor_prefix "$desktop_file" 604 fi 605 ;; 606 *) 607 filetype=other 608 ;; 609esac 610 611my_umask=077 612desktop_dir="$HOME/Desktop" 613if xdg-user-dir 2>/dev/null 1>&2; then 614 desktop_dir=`xdg-user-dir DESKTOP` 615fi 616desktop_dir_kde=`kde${KDE_SESSION_VERSION}-config --userpath desktop 2> /dev/null` 617if gconftool-2 -g /apps/nautilus/preferences/desktop_is_home_dir 2> /dev/null | grep true > /dev/null; then 618 desktop_dir_gnome="$HOME" 619 # Don't create $HOME/Desktop if it doesn't exist 620 [ -w "$desktop_dir" ] || desktop_dir= 621fi 622if [ -n "$desktop_dir_kde" ]; then 623 if [ ! -d "$desktop_dir_kde" ]; then 624 save_umask=`umask` 625 umask $my_umask 626 mkdir -p "$desktop_dir_kde" 627 umask $save_umask 628 fi 629 # Is the KDE desktop dir != $HOME/Desktop ? 630 if [ "x`readlink -f "$desktop_dir"`" != "x`readlink -f "$desktop_dir_kde"`" ]; then 631 # If so, don't create $HOME/Desktop if it doesn't exist 632 [ -w "$desktop_dir" ] || desktop_dir= 633 else 634 desktop_dir_kde= 635 fi 636fi 637 638basefile=`basename "$desktop_file"` 639 640DEBUG 1 "$action $desktop_file in $desktop_dir $desktop_dir_kde $desktop_dir_gnome" 641 642case $action in 643 install) 644 save_umask=`umask` 645 umask $my_umask 646 647 for x in "$desktop_dir" "$desktop_dir_kde" "$desktop_dir_gnome" ; do 648 if [ -n "$x" ]; then 649 mkdir -p "$x" 650 eval 'cp "$desktop_file" "$x/$basefile"'$xdg_redirect_output 651 chmod u+x "$x/$basefile" 652 fi 653 done 654 655 umask $save_umask 656 ;; 657 658 uninstall) 659 for x in "$desktop_dir" "$desktop_dir_kde" "$desktop_dir_gnome" ; do 660 if [ -n "$x" ]; then 661 rm -f "$x/$basefile" 662 fi 663 done 664 665 ;; 666esac 667 668exit_success 669 670 671