blob: baeb42c46559173ee73b63a9335b674a6796f88f [file] [log] [blame]
dkegel@google.comf20da1f2009-06-30 19:16:32 +00001#!/bin/sh
2#---------------------------------------------
3# xdg-desktop-menu
4#
5# Utility script to install menu items on a Linux desktop.
6# Refer to the usage() function below for usage.
7#
8# Copyright 2006, Kevin Krammer <kevin.krammer@gmx.at>
9# Copyright 2006, Jeremy White <jwhite@codeweavers.com>
10#
11# LICENSE:
12#
13# Permission is hereby granted, free of charge, to any person obtaining a
14# copy of this software and associated documentation files (the "Software"),
15# to deal in the Software without restriction, including without limitation
16# the rights to use, copy, modify, merge, publish, distribute, sublicense,
17# and/or sell copies of the Software, and to permit persons to whom the
18# Software is furnished to do so, subject to the following conditions:
19#
20# The above copyright notice and this permission notice shall be included
21# in all copies or substantial portions of the Software.
22#
23# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
27# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
28# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
29# OTHER DEALINGS IN THE SOFTWARE.
30#
31#---------------------------------------------
32
33manualpage()
34{
35cat << _MANUALPAGE
36Name
37
38xdg-desktop-menu - command line tool for (un)installing desktop menu items
39
40Synopsis
41
42xdg-desktop-menu install [--noupdate] [--novendor] [--mode mode] directory-file
43(s) desktop-file(s)
44
45xdg-desktop-menu uninstall [--noupdate] [--mode mode] directory-file(s)
46desktop-file(s)
47
48xdg-desktop-menu forceupdate [--mode mode]
49
50xdg-desktop-menu { --help | --manual | --version }
51
52Description
53
54The xdg-desktop-menu program can be used to install new menu entries to the
55desktop's application menu.
56
57The application menu works according to the XDG Desktop Menu Specification at
58http://www.freedesktop.org/Standards/menu-spec
59
60Commands
61
62install
63
64 Install one or more applications in a submenu of the desktop menu system.
65
66 desktop-file: A desktop file represents a single menu entry in the menu.
67 Desktop files are defined by the freedesktop.org Desktop Entry
68 Specification. The most important aspects of *.desktop files are summarized
69 below.
70
71 Menu entries can be added to the menu system in two different ways. They
72 can either be added to a predefined submenu in the menu system based on one
73 or more category keywords, or they can be added to a new submenu.
74
75 To add a menu entry to a predefined submenu the desktop file that
76 represents the menu entry must have a Categories= entry that lists one or
77 more keywords. The menu item will be included in an appropriate submenu
78 based on the included keywords.
79
80 To add menu items to a new submenu the desktop-files must be preceded by a
81 directory-file that describes the submenu. If multiple desktop-files are
82 specified, all entries will be added to the same menu. If entries are
83 installed to a menu that has been created with a previous call to
84 xdg-desktop-menu the entries will be installed in addition to any already
85 existing entries.
86
87 directory-file: The *.directory file indicated by directory-file represents
88 a submenu. The directory file provides the name and icon for a submenu. The
89 name of the directory file is used to identify the submenu.
90
91 If multiple directory files are provided each file will represent a submenu
92 within the menu that preceeds it, creating a nested menu hierarchy
93 (sub-sub-menus). The menu entries themselves will be added to the last
94 submenu.
95
96 Directory files follow the syntax defined by the freedesktop.org Desktop
97 Entry Specification.
98
99uninstall
100
101 Remove applications or submenus from the desktop menu system previously
102 installed with xdg-desktop-menu install.
103
104 A submenu and the associated directory file is only removed when the
105 submenu no longer contains any menu entries.
106
107forceupdate
108
109 Force an update of the menu system.
110
111 This command is only useful if the last call to xdg-desktop-menu included
112 the --noupdate option.
113
114Options
115
116--noupdate
117 Postpone updating the menu system. If multiple updates to the menu system
118 are made in sequence this flag can be used to indicate that additional
119 changes will follow and that it is not necassery to update the menu system
120 right away.
121--novendor
122
123 Normally, xdg-desktop-menu checks to ensure that any *.directory and
124 *.desktop files to be installed has a vendor prefix. This option can be
125 used to disable that check.
126
127 A vendor prefix consists of alpha characters ([a-zA-Z]) and is terminated
128 with a dash ("-"). Companies and organizations are encouraged to use a word
129 or phrase, preferably the organizations name, for which they hold a
130 trademark as their vendor prefix. The purpose of the vendor prefix is to
131 prevent name conflicts.
132
133--mode mode
134
135 mode can be user or system. In user mode the file is (un)installed for the
136 current user only. In system mode the file is (un)installed for all users
137 on the system. Usually only root is allowed to install in system mode.
138
139 The default is to use system mode when called by root and to use user mode
140 when called by a non-root user.
141
142--help
143 Show command synopsis.
144--manual
145 Show this manualpage.
146--version
147 Show the xdg-utils version information.
148
149Desktop Files
150
151An application item in the application menu is represented by a *.desktop file.
152A *.desktop file consists of a [Desktop Entry] header followed by several Key=
153Value lines.
154
155A *.desktop file can provide a name and description for an application in
156several different languages. This is done by adding a language code as used by
157LC_MESSAGES in square brackets behind the Key. This way one can specify
158different values for the same Key depending on the currently selected language.
159
160The following keys are often used:
161
162Value=1.0
163 This is a mandatory field to indicate that the *.desktop file follows the
164 1.0 version of the specification.
165Type=Application
166 This is a mandatory field that indicates that the *.desktop file describes
167 an application launcher.
168Name=Application Name
169 The name of the application. For example Mozilla
170GenericName=Generic Name
171 A generic description of the application. For example Web Browser
172Comment=Comment
173 Optional field to specify a tooltip for the application. For example Visit
174 websites on the Internet
175Icon=Icon File
176 The icon to use for the application. This can either be an absolute path to
177 an image file or an icon-name. If an icon-name is provided an image lookup
178 by name is done in the user's current icon theme. The xdg-icon-resource
179 command can be used to install image files into icon themes. The advantage
180 of using an icon-name instead of an absolute path is that with an icon-name
181 the application icon can be provided in several different sizes as well as
182 in several differently themed styles.
183Exec=Command Line
184 The command line to start the application. If the application can open
185 files the %f placeholder should be specified. When a file is dropped on the
186 application launcher the %f is replaced with the file path of the dropped
187 file. If multiple files can be specified on the command line the %F
188 placeholder should be used instead of %f. If the application is able to
189 open URLs in addition to local files then %u or %U can be used instead of
190 %f or %F.
191Categories=Categories
192
193 A list of categories separated by semi-colons. A category is a keyword that
194 describes and classifies the application. By default applications are
195 organized in the application menu based on category. When menu entries are
196 explicitly assigned to a new submenu it is not necassery to list any
197 categories.
198
199 When using categories it is recommended to include one of the following
200 categories: AudioVideo, Development, Education, Game, Graphics, Network,
201 Office, Settings, System, Utility.
202
203 See Appendix A of the XDG Desktop Menu Specification for information about
204 additional categories. http://standards.freedesktop.org/menu-spec/
205 menu-spec-1.0.html
206
207MimeType=Mimetypes
208 A list of mimetypes separated by semi-colons. This field is used to
209 indicate which file types the application is able to open.
210
211For a complete oveview of the *.desktop file format please visit http://
212www.freedesktop.org/wiki/Standards/desktop-entry-spec
213
214Directory Files
215
216The appearance of submenu in the application menu is provided by a *.directory
217file. In particular it provides the title of the submenu and a possible icon. A
218*.directory file consists of a [Desktop Entry] header followed by several Key=
219Value lines.
220
221A *.directory file can provide a title (name) for the submenu in several
222different languages. This is done by adding a language code as used by
223LC_MESSAGES in square brackets behind the Key. This way one can specify
224different values for the same Key depending on the currently selected language.
225
226The following keys are relevqnt for submenus:
227
228Value=1.0
229 This is a mandatory field to indicate that the *.directory file follows the
230 1.0 version of the Desktop Entry specification.
231Type=Directory
232 This is a mandatory field that indicates that the *.directory file
233 describes a submenu.
234Name=Menu Name
235 The title of submenu. For example Mozilla
236Comment=Comment
237 Optional field to specify a tooltip for the submenu.
238Icon=Icon File
239 The icon to use for the submenu. This can either be an absolute path to an
240 image file or an icon-name. If an icon-name is provided an image lookup by
241 name is done in the user's current icon theme. The xdg-icon-resource
242 command can be used to install image files into icon themes. The advantage
243 of using an icon-name instead of an absolute path is that with an icon-name
244 the submenu icon can be provided in several different sizes as well as in
245 several differently themed styles.
246
247Environment Variables
248
249xdg-desktop-menu honours the following environment variables:
250
251XDG_UTILS_DEBUG_LEVEL
252 Setting this environment variable to a non-zero numerical value makes
253 xdg-desktop-menu do more verbose reporting on stderr. Setting a higher
254 value increases the verbosity.
255XDG_UTILS_INSTALL_MODE
256 This environment variable can be used by the user or administrator to
257 override the installation mode. Valid values are user and system.
258
259Exit Codes
260
261An exit code of 0 indicates success while a non-zero exit code indicates
262failure. The following failure codes can be returned:
263
2641
265 Error in command line syntax.
2662
267 One of the files passed on the command line did not exist.
2683
269 A required tool could not be found.
2704
271 The action failed.
2725
273 No permission to read one of the files passed on the command line.
274
275See Also
276
277xdg-desktop-icon(1), xdg-icon-resource(1), xdg-mime(1)
278
279Examples
280
281The company ShinyThings Inc. has developed an application named "WebMirror" and
282would like to add it to the application menu. The company will use
283"shinythings" as its vendor id. In order to add the application to the menu
284there needs to be a .desktop file with a suitable Categories entry:
285
286shinythings-webmirror.desktop:
287
288 [Desktop Entry]
289 Encoding=UTF-8
290 Type=Application
291
292 Exec=webmirror
293 Icon=webmirror
294
295 Name=WebMirror
296 Name[nl]=WebSpiegel
297
298 Categories=Network;WebDevelopment;
299
300Now the xdg-desktop-menu tool can be used to add the
301shinythings-webmirror.desktop file to the desktop application menu:
302
303xdg-desktop-menu install ./shinythings-webmirror.desktop
304
305Note that for the purpose of this example the menu items are available in two
306languages, English and Dutch. The language code for Dutch is nl.
307
308In the next example the company ShinyThings Inc. will add its own submenu to
309the desktop application menu consisting of a "WebMirror" menu item and a
310"WebMirror Admin Tool" menu item.
311
312First the company needs to create two .desktop files that describe the two menu
313items. Since the items are to be added to a new submenu it is not necassery to
314include a Categories= line:
315
316shinythings-webmirror.desktop:
317
318 [Desktop Entry]
319 Encoding=UTF-8
320 Type=Application
321
322 Exec=webmirror
323 Icon=shinythings-webmirror
324
325 Name=WebMirror
326 Name[nl]=WebSpiegel
327
328
329shinythings-webmirror-admin.desktop:
330
331 [Desktop Entry]
332 Encoding=UTF-8
333 Type=Application
334
335 Exec=webmirror-admintool
336 Icon=shinythings-webmirror-admintool
337
338 Name=WebMirror Admin Tool
339 Name[nl]=WebSpiegel Administratie Tool
340
341In addition a .directory file needs to be created to provide a title and icon
342for the sub-menu itself:
343
344shinythings-webmirror.directory:
345
346 [Desktop Entry]
347 Encoding=UTF-8
348
349 Icon=shinythings-webmirror-menu
350
351 Name=WebMirror
352 Name[nl]=WebSpiegel
353
354These file can now be installed with:
355
356xdg-desktop-menu install ./shinythings-webmirror.directory \
357 ./shinythings-webmirror.desktop ./shinythings-webmirror-admin.desktop
358
359The menu entries could also be installed one by one:
360
361xdg-desktop-menu install --noupdate ./shinythings-webmirror.directory \
362 ./shinythings-webmirror.desktop
363xdg-desktop-menu install --noupdate ./shinythings-webmirror.directory \
364 ./shinythings-webmirror-admin.desktop
365xdg-desktop-menu forceupdate
366
367Although the result is the same it is slightly more efficient to install all
368files at the same time.
369
370The *.desktop and *.directory files reference icons with the names webmirror,
371webmirror-admin and webmirror-menu which should also be installed. In this
372example the icons are installed in two different sizes, once with a size of
37322x22 pixels and once with a size of 64x64 pixels:
374
375xdg-icon-resource install --size 22 ./wmicon-22.png shinythings-webmirror
376xdg-icon-resource install --size 22 ./wmicon-menu-22.png shinythings-webmirror-menu
377xdg-icon-resource install --size 22 ./wmicon-admin-22.png shinythings-webmirror-admin
378xdg-icon-resource install --size 64 ./wmicon-64.png shinythings-webmirror
379xdg-icon-resource install --size 64 ./wmicon-menu-64.png shinythings-webmirror-menu
380xdg-icon-resource install --size 64 ./wmicon-admin-64.png shinythings-webmirror-admin
381
382_MANUALPAGE
383}
384
385usage()
386{
387cat << _USAGE
388xdg-desktop-menu - command line tool for (un)installing desktop menu items
389
390Synopsis
391
392xdg-desktop-menu install [--noupdate] [--novendor] [--mode mode] directory-file
393(s) desktop-file(s)
394
395xdg-desktop-menu uninstall [--noupdate] [--mode mode] directory-file(s)
396desktop-file(s)
397
398xdg-desktop-menu forceupdate [--mode mode]
399
400xdg-desktop-menu { --help | --manual | --version }
401
402_USAGE
403}
404
405#@xdg-utils-common@
406
407#----------------------------------------------------------------------------
408# Common utility functions included in all XDG wrapper scripts
409#----------------------------------------------------------------------------
410
411DEBUG()
412{
413 [ -z "${XDG_UTILS_DEBUG_LEVEL}" ] && return 0;
414 [ ${XDG_UTILS_DEBUG_LEVEL} -lt $1 ] && return 0;
415 shift
416 echo "$@" >&2
417}
418
419#-------------------------------------------------------------
420# Exit script on successfully completing the desired operation
421
422exit_success()
423{
424 if [ $# -gt 0 ]; then
425 echo "$@"
426 echo
427 fi
428
429 exit 0
430}
431
432
433#-----------------------------------------
434# Exit script on malformed arguments, not enough arguments
435# or missing required option.
436# prints usage information
437
438exit_failure_syntax()
439{
440 if [ $# -gt 0 ]; then
441 echo "xdg-desktop-menu: $@" >&2
442 echo "Try 'xdg-desktop-menu --help' for more information." >&2
443 else
444 usage
445 echo "Use 'man xdg-desktop-menu' or 'xdg-desktop-menu --manual' for additional info."
446 fi
447
448 exit 1
449}
450
451#-------------------------------------------------------------
452# Exit script on missing file specified on command line
453
454exit_failure_file_missing()
455{
456 if [ $# -gt 0 ]; then
457 echo "xdg-desktop-menu: $@" >&2
458 fi
459
460 exit 2
461}
462
463#-------------------------------------------------------------
464# Exit script on failure to locate necessary tool applications
465
466exit_failure_operation_impossible()
467{
468 if [ $# -gt 0 ]; then
469 echo "xdg-desktop-menu: $@" >&2
470 fi
471
472 exit 3
473}
474
475#-------------------------------------------------------------
476# Exit script on failure returned by a tool application
477
478exit_failure_operation_failed()
479{
480 if [ $# -gt 0 ]; then
481 echo "xdg-desktop-menu: $@" >&2
482 fi
483
484 exit 4
485}
486
487#------------------------------------------------------------
488# Exit script on insufficient permission to read a specified file
489
490exit_failure_file_permission_read()
491{
492 if [ $# -gt 0 ]; then
493 echo "xdg-desktop-menu: $@" >&2
494 fi
495
496 exit 5
497}
498
499#------------------------------------------------------------
mdm@chromium.org56df6d32010-08-31 18:16:49 +0000500# Exit script on insufficient permission to write a specified file
dkegel@google.comf20da1f2009-06-30 19:16:32 +0000501
502exit_failure_file_permission_write()
503{
504 if [ $# -gt 0 ]; then
505 echo "xdg-desktop-menu: $@" >&2
506 fi
507
508 exit 6
509}
510
511check_input_file()
512{
513 if [ ! -e "$1" ]; then
514 exit_failure_file_missing "file '$1' does not exist"
515 fi
516 if [ ! -r "$1" ]; then
517 exit_failure_file_permission_read "no permission to read file '$1'"
518 fi
519}
520
521check_vendor_prefix()
522{
523 file_label="$2"
524 [ -n "$file_label" ] || file_label="filename"
525 file=`basename "$1"`
526 case "$file" in
527 [a-zA-Z]*-*)
528 return
529 ;;
530 esac
531
532 echo "xdg-desktop-menu: $file_label '$file' does not have a proper vendor prefix" >&2
533 echo 'A vendor prefix consists of alpha characters ([a-zA-Z]) and is terminated' >&2
534 echo 'with a dash ("-"). An example '"$file_label"' is '"'example-$file'" >&2
535 echo "Use --novendor to override or 'xdg-desktop-menu --manual' for additional info." >&2
536 exit 1
537}
538
539check_output_file()
540{
541 # if the file exists, check if it is writeable
542 # if it does not exists, check if we are allowed to write on the directory
543 if [ -e "$1" ]; then
544 if [ ! -w "$1" ]; then
545 exit_failure_file_permission_write "no permission to write to file '$1'"
546 fi
547 else
548 DIR=`dirname "$1"`
549 if [ ! -w "$DIR" -o ! -x "$DIR" ]; then
550 exit_failure_file_permission_write "no permission to create file '$1'"
551 fi
552 fi
553}
554
555#----------------------------------------
556# Checks for shared commands, e.g. --help
557
558check_common_commands()
559{
560 while [ $# -gt 0 ] ; do
561 parm="$1"
562 shift
563
564 case "$parm" in
565 --help)
566 usage
567 echo "Use 'man xdg-desktop-menu' or 'xdg-desktop-menu --manual' for additional info."
568 exit_success
569 ;;
570
571 --manual)
572 manualpage
573 exit_success
574 ;;
575
576 --version)
mdm@chromium.org56df6d32010-08-31 18:16:49 +0000577 echo "xdg-desktop-menu 1.0.2"
dkegel@google.comf20da1f2009-06-30 19:16:32 +0000578 exit_success
579 ;;
580 esac
581 done
582}
583
584check_common_commands "$@"
585
586[ -z "${XDG_UTILS_DEBUG_LEVEL}" ] && unset XDG_UTILS_DEBUG_LEVEL;
587if [ ${XDG_UTILS_DEBUG_LEVEL-0} -lt 1 ]; then
588 # Be silent
589 xdg_redirect_output=" > /dev/null 2> /dev/null"
590else
591 # All output to stderr
592 xdg_redirect_output=" >&2"
593fi
594
595#--------------------------------------
596# Checks for known desktop environments
597# set variable DE to the desktop environments name, lowercase
598
599detectDE()
600{
601 if [ x"$KDE_FULL_SESSION" = x"true" ]; then DE=kde;
602 elif [ x"$GNOME_DESKTOP_SESSION_ID" != x"" ]; then DE=gnome;
mdm@chromium.org56df6d32010-08-31 18:16:49 +0000603 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;
604 elif xprop -root _DT_SAVE_MODE 2> /dev/null | grep ' = \"xfce4\"$' >/dev/null 2>&1; then DE=xfce;
dkegel@google.comf20da1f2009-06-30 19:16:32 +0000605 fi
606}
607
608#----------------------------------------------------------------------------
609# kfmclient exec/openURL can give bogus exit value in KDE <= 3.5.4
610# It also always returns 1 in KDE 3.4 and earlier
611# Simply return 0 in such case
612
613kfmclient_fix_exit_code()
614{
615 version=`kde${KDE_SESSION_VERSION}-config --version 2>/dev/null | grep KDE`
616 major=`echo $version | sed 's/KDE: \([0-9]\).*/\1/'`
617 minor=`echo $version | sed 's/KDE: [0-9]*\.\([0-9]\).*/\1/'`
618 release=`echo $version | sed 's/KDE: [0-9]*\.[0-9]*\.\([0-9]\).*/\1/'`
619 test "$major" -gt 3 && return $1
620 test "$minor" -gt 5 && return $1
621 test "$release" -gt 4 && return $1
622 return 0
623}
624
625update_desktop_database()
626{
627# echo Update desktop database: $mode
628 if [ "$mode" = "system" ] ; then
629 for x in `echo $PATH | sed 's/:/ /g'` /opt/gnome/bin; do
630 if [ -x $x/update-desktop-database ] ; then
631 DEBUG 1 "Running $x/update-desktop-database"
632 eval '$x/update-desktop-database'$xdg_redirect_output
633 return
634 fi
635 done
636 fi
637}
638
dkegel@google.comf20da1f2009-06-30 19:16:32 +0000639# Make application $1/$2 the default for all the mimetypes it support,
640# iff such mimetype didn't had a default application already.
641# $1 Install dir for desktop file
642# $2 base name of desktop file
643make_lazy_default()
644{
645 local mimetypes
646 local xdg_user_dir
647 local xdg_default_dirs
mdm@chromium.org56df6d32010-08-31 18:16:49 +0000648
dkegel@google.comf20da1f2009-06-30 19:16:32 +0000649 DEBUG 1 "make_lazy_default $1/$2"
650 mimetypes=`awk '
651{
652 if (match($0,/MimeType=/)) {
653 split(substr($0,RSTART+9),mimetypes,";")
654 for (n in mimetypes)
655 {
656 if (mimetypes[n])
657 print mimetypes[n]
658 }
659 }
660}' "$1/$2" 2> /dev/null`
mdm@chromium.org56df6d32010-08-31 18:16:49 +0000661
dkegel@google.comf20da1f2009-06-30 19:16:32 +0000662 for MIME in $mimetypes ; do
663 xdg_default_dirs="$XDG_DATA_DIRS"
664 [ -n "$xdg_default_dirs" ] || xdg_default_dirs=/usr/local/share/:/usr/share/
665 if [ x"$mode" = x"user" ] ; then
666 xdg_user_dir="$XDG_DATA_HOME"
667 [ -n "$xdg_user_dir" ] || xdg_user_dir="$HOME/.local/share"
668 xdg_default_dirs="$xdg_user_dir:$xdg_default_dirs"
669 fi
670 local default_app
671 for x in `echo "$xdg_default_dirs" | sed 's/:/ /g'`; do
672 DEBUG 2 "Checking $x/applications/defaults.list"
673 default_app=`grep "$MIME=" $x/applications/defaults.list 2> /dev/null | cut -d '=' -f 2`
674 if [ -n "$default_app" ] ; then
675 DEBUG 2 "Found default apps for $MIME: $default_app"
676 default_app="$default_app;"
677 break;
678 fi
679 done
680 DEBUG 2 "Current default apps for $MIME: $default_app"
681 if echo "$default_app" | grep "$2" > /dev/null 2> /dev/null; then
682 # App already listed as default
683 continue;
684 fi
685 default_file="$1/defaults.list"
686 DEBUG 1 "Updating $default_file"
687 grep -v "$MIME=" $default_file > ${default_file}.new 2> /dev/null
688 if ! grep "[Default Applications]" ${default_file}.new > /dev/null; then
689 echo "[Default Applications]" >> ${default_file}.new
690 fi
691 echo $MIME="$default_app$2" >> ${default_file}.new
692 mv ${default_file}.new $default_file
693 done
694}
695
696update_submenu()
697{
698 DEBUG 1 "update_submenu $1"
699 menu_file="$1"
700
701 xdg_dir_name=menus
702 xdg_user_dir="$XDG_CONFIG_HOME"
703 [ -n "$xdg_user_dir" ] || xdg_user_dir="$HOME/.config"
704 xdg_user_dir="$xdg_user_dir/$xdg_dir_name"
705
706 xdg_system_dirs="$XDG_CONFIG_DIRS"
707 [ -n "$xdg_system_dirs" ] || xdg_system_dirs=/etc/xdg
708 xdg_global_dir=
709 for x in `echo $xdg_system_dirs | sed 's/:/ /g'` ; do
710 if [ -w $x/$xdg_dir_name ] ; then
711 xdg_global_dir="$x/$xdg_dir_name"
712 break
713 fi
714 done
715 xdg_user_dir="$xdg_user_dir/applications-merged"
716 xdg_global_dir="$xdg_global_dir/applications-merged"
717
mdm@chromium.org56df6d32010-08-31 18:16:49 +0000718 DEBUG 3 "Install locations for *.menu file:"
dkegel@google.comf20da1f2009-06-30 19:16:32 +0000719 DEBUG 3 "xdg_user_dir: $xdg_user_dir"
720 DEBUG 3 "xdg_global_dir: $xdg_global_dir"
721 DEBUG 3 "kde_user_dir: $kde_user_dir"
722 DEBUG 3 "kde_global_dir: $kde_global_dir"
723 DEBUG 3 "gnome_user_dir: $gnome_user_dir"
724 DEBUG 3 "gnome_global_dir: $gnome_global_dir"
725
726 if [ x"$mode" = x"user" ] ; then
727 xdg_dir="$xdg_user_dir"
728 kde_dir="$kde_user_dir"
729 gnome_dir="$gnome_user_dir"
730 my_umask=077
731 my_chmod=0600
732 else
733 xdg_dir="$xdg_global_dir"
734 kde_dir="$kde_global_dir"
735 gnome_dir="$gnome_global_dir"
736 my_umask=022
737 my_chmod=0644
738 if [ -z "${xdg_dir}${kde_dir}${gnome_dir}" ] ; then
739 exit_failure_operation_impossible "No writable system menu directory found."
740 fi
741 fi
742
743 if [ -z "$menu_file" ] ; then
744 # Work around for SUSE/gnome 2.12 to pick up new ~/.local/share/applications
745 save_umask=`umask`
746 umask $my_umask
747
748 mkdir -p $xdg_dir
749 touch $xdg_dir/xdg-desktop-menu-dummy.menu
750
751 umask $save_umask
752 return
753 fi
754
755 if [ $action = "install" -a -f "/etc/xdg/menus/gnome-applications.menu" ] ; then
756 # Work around for Debian Gnome
757 gnome_xdg_dir=`echo "$xdg_dir" | sed -e 's^/applications-merged^/gnome-applications-merged^'`
758 if [ ! -e "$gnome_xdg_dir" ] ; then
759 DEBUG 1 "Debian Workaround: Link '$xdg_dir' to '$gnome_xdg_dir'"
760 mkdir -p `dirname "$gnome_xdg_dir"`
761 eval 'ln -s "applications-merged" "$gnome_xdg_dir"'$xdg_redirect_output
762 fi
763 fi
764 if [ $action = "install" -a -f "/etc/mandrake-release" ] ; then
765 # Work around for Mandriva 2006
766 mandrake_xdg_dir=`echo "$xdg_dir" | sed -e 's^/applications-merged^/applications-mdk-merged^'`
767 if [ ! -e "$mandrake_xdg_dir" ] ; then
768 DEBUG 1 "Mandriva Workaround: Link '$xdg_dir' to '$mandrake_xdg_dir'"
769 mkdir -p `dirname "$mandrake_xdg_dir"`
770 eval 'ln -s "applications-merged" "$mandrake_xdg_dir"'$xdg_redirect_output
771 fi
772 fi
773 if [ $action = "install" -a x"$mode" = x"user" -a -d "/etc/xdg/menus/kde-applications-merged" ] ; then
774 # Work around for Fedora Core 5 + patched KDE
775 kde_xdg_dir=`echo "$xdg_dir" | sed -e 's^/applications-merged^/kde-applications-merged^'`
776 if [ ! -e "$kde_xdg_dir" ] ; then
777 DEBUG 1 "Fedora Workaround: Link '$xdg_dir' to '$kde_xdg_dir'"
778 mkdir -p `dirname "$kde_xdg_dir"`
779 eval 'ln -s "applications-merged" "$kde_xdg_dir"'$xdg_redirect_output
780 fi
mdm@chromium.org56df6d32010-08-31 18:16:49 +0000781 fi
dkegel@google.comf20da1f2009-06-30 19:16:32 +0000782 if [ $action = "install" -a x"$mode" = x"system" -a -d "/etc/xdg/menus/kde-applications-merged" -a ! -d "/etc/xdg/menus/applications-merged" ] ; then
783 # Work around for Kubuntu 6.06
784 kde_xdg_dir=`echo "$xdg_dir" | sed -e 's^/applications-merged^/kde-applications-merged^'`
785 DEBUG 1 "Kubuntu Workaround: Link '$xdg_dir' to 'kde-applications-merged'"
786 eval 'ln -s "kde-applications-merged" "$xdg_dir"'$xdg_redirect_output
787 fi
788
789 orig_menu_file=$xdg_dir/$menu_file
790
791 DEBUG 1 "Updating $orig_menu_file ($action)"
792
793 test "${TMPDIR+set}" = set || TMPDIR=/tmp
794 tmpfile=`mktemp $TMPDIR/tmp.XXXXXXXXXX`
795 orig_desktop_files=
796 if [ -r "$orig_menu_file" ] ; then
797 awk '
798# List all files within <Filename> tags
799BEGIN {
800 RS="<"
801}
802/^Filename/ {
803 if (match($0,/>/)) {
804 print substr($0,RSTART+1)
805 }
806}' $orig_menu_file > $tmpfile
807 fi
808
809 orig_desktop_files=`cat $tmpfile`
810 new_desktop_files=
811 if [ $action = "install" ] ; then
812 for desktop_file in $desktop_files; do
mdm@chromium.orgad856b92009-07-27 21:50:25 +0000813 basefile=`basename "$desktop_file"`
dkegel@google.comf20da1f2009-06-30 19:16:32 +0000814 if ! grep '^'$basefile'$' $tmpfile > /dev/null 2> /dev/null ; then
815 # Append
816 echo "$basefile" >> $tmpfile
817 fi
818 done
819 new_desktop_files=`cat $tmpfile`
820 fi
821 if [ $action = "uninstall" ] ; then
822 echo > $tmpfile
823 for desktop_file in $desktop_files; do
824 echo "$desktop_file" >> $tmpfile
825 done
826 # Files to uninstall are listed in $tmpfile
827 # Existing files are in $orig_desktop_files
828 for desktop_file in $orig_desktop_files; do
829 if ! grep '^'$desktop_file'$' $tmpfile > /dev/null 2> /dev/null; then
830 # Keep this file, it's not in the uninstall list
831 new_desktop_files="$new_desktop_files $desktop_file"
832 fi
833 done
834 fi
835 rm -f "$tmpfile"
mdm@chromium.org56df6d32010-08-31 18:16:49 +0000836
dkegel@google.comf20da1f2009-06-30 19:16:32 +0000837 DEBUG 3 "Files to list in $menu_file: $new_desktop_files"
mdm@chromium.org56df6d32010-08-31 18:16:49 +0000838
dkegel@google.comf20da1f2009-06-30 19:16:32 +0000839 if [ -n "$new_desktop_files" ] ; then
840 # Install/update
841 test "${TMPDIR+set}" = set || TMPDIR=/tmp
842 tmpfile=`mktemp $TMPDIR/tmp.XXXXXXXXXX`
843 (
844 echo '<!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"'
845 echo ' "http://www.freedesktop.org/standards/menu-spec/menu-1.0.dtd">'
846 echo '<!-- Do not edit manually - generated and managed by xdg-desktop-menu -->'
847 echo '<Menu>'
848 echo ' <Name>Applications</Name>'
849
850 for desktop_file in $directory_files; do
mdm@chromium.orgad856b92009-07-27 21:50:25 +0000851 basefile=`basename "$desktop_file"`
dkegel@google.comf20da1f2009-06-30 19:16:32 +0000852 basefilename=`echo "$basefile"|cut -d '.' -f 1`
853 echo "<Menu>"
854 echo " <Name>$basefilename</Name>"
855 echo " <Directory>$basefile</Directory>"
856 done
857
858 echo " <Include>"
859 for desktop_file in $new_desktop_files; do
860 echo " <Filename>$desktop_file</Filename>"
861 done
862 echo " </Include>"
863
864 for desktop_file in $directory_files; do
865 echo "</Menu>"
866 done
867
mdm@chromium.org56df6d32010-08-31 18:16:49 +0000868 echo '</Menu>'
dkegel@google.comf20da1f2009-06-30 19:16:32 +0000869 ) > $tmpfile
870 chmod $my_chmod $tmpfile
mdm@chromium.org56df6d32010-08-31 18:16:49 +0000871
dkegel@google.comf20da1f2009-06-30 19:16:32 +0000872 save_umask=`umask`
873 umask $my_umask
874
875 mkdir -p $xdg_dir
876 eval 'cp $tmpfile $xdg_dir/$menu_file'$xdg_redirect_output
877
878 umask $save_umask
879 rm -f "$tmpfile"
880 else
881 # Uninstall
882 rm -f $xdg_dir/$menu_file
883 fi
mdm@chromium.org56df6d32010-08-31 18:16:49 +0000884
dkegel@google.comf20da1f2009-06-30 19:16:32 +0000885 # Uninstall .directory files only if no longer referenced
886 if [ $action = "uninstall" ] ; then
887 test "${TMPDIR+set}" = set || TMPDIR=/tmp
888 tmpfile=`mktemp $TMPDIR/tmp.XXXXXXXXXX`
889 for menu_file in $xdg_dir/*; do
890 if grep 'generated and managed by xdg-desktop-menu' $menu_file > /dev/null 2> /dev/null; then
891 awk '
892# List all files within <Directory> tags
893BEGIN {
894 RS="<"
895}
896/^Directory/ {
897 if (match($0,/>/)) {
898 print substr($0,RSTART+1)
899 }
900}' $menu_file >> $tmpfile
901 fi
mdm@chromium.org56df6d32010-08-31 18:16:49 +0000902 done
dkegel@google.comf20da1f2009-06-30 19:16:32 +0000903 orig_directory_files="$directory_files"
904 directory_files=
905 for desktop_file in $orig_directory_files; do
906 if ! grep '^'$desktop_file'$' $tmpfile > /dev/null 2> /dev/null; then
907 # No longer in use, safe to delete
908 directory_files="$directory_files $desktop_file"
909 fi
910 done
911 rm -f "$tmpfile"
912 fi
913}
914
915
916[ x"$1" != x"" ] || exit_failure_syntax
917
918mode=
919action=
920update=yes
921desktop_files=
922directory_files=
923
924case $1 in
925 install)
926 action=install
927 ;;
928
929 uninstall)
930 action=uninstall
931 ;;
932
933 forceupdate)
934 action=forceupdate
935 ;;
936
937 *)
938 exit_failure_syntax "unknown command '$1'"
939 ;;
940esac
941
942shift
943
944vendor=true
945while [ $# -gt 0 ] ; do
946 parm="$1"
947 shift
948
949 case "$parm" in
950 --noupdate)
951 update=no
952 ;;
953
954 --mode)
955 if [ -z "$1" ] ; then
956 exit_failure_syntax "mode argument missing for --mode"
957 fi
958 case "$1" in
959 user)
960 mode="user"
961 ;;
962
963 system)
964 mode="system"
965 ;;
mdm@chromium.org56df6d32010-08-31 18:16:49 +0000966
dkegel@google.comf20da1f2009-06-30 19:16:32 +0000967 *)
968 exit_failure_syntax "unknown mode '$1'"
969 ;;
970 esac
971 shift
972 ;;
973
974 --novendor)
975 vendor=false
976 ;;
977
978 -*)
979 exit_failure_syntax "unexpected option '$parm'"
980 ;;
981
982 *)
983 if [ "$action" = "install" ] ; then
984 check_input_file "$parm"
985 fi
986 case "$parm" in
987 *.directory)
988 if [ -n "$desktop_files" ] ; then
989 exit_failure_syntax "'$parm' must preceed any *.desktop file"
990 fi
991 directory_files="$directory_files $parm"
992 ;;
993 *.desktop)
994 desktop_files="$desktop_files $parm"
995 ;;
996 *)
997 exit_failure_syntax "file to $action must be a *.directory or *.desktop file"
998 ;;
999 esac
1000 ;;
1001 esac
1002done
1003
1004# Shouldn't happen
1005if [ -z "$action" ] ; then
1006 exit_failure_syntax "command argument missing"
1007fi
1008
1009if [ -n "$XDG_UTILS_INSTALL_MODE" ] ; then
1010 if [ "$XDG_UTILS_INSTALL_MODE" = "system" ] ; then
1011 mode="system"
1012 elif [ "$XDG_UTILS_INSTALL_MODE" = "user" ] ; then
1013 mode="user"
1014 fi
1015fi
1016
1017if [ -z "$mode" ] ; then
1018 if [ `whoami` = "root" ] ; then
1019 mode="system"
1020 else
1021 mode="user"
1022 fi
1023fi
1024
1025if [ x"$action" = x"forceupdate" ] ; then
1026 update_desktop_database
1027 exit_success
1028fi
1029
1030if [ -z "$desktop_files" ] ; then
1031 exit_failure_syntax "desktop-file argument missing"
1032fi
1033
1034menu_name=
1035for desktop_file in $directory_files; do
1036 if [ "$vendor" = "true" -a "$action" = "install" ] ; then
1037 check_vendor_prefix "$desktop_file"
1038 fi
1039
mdm@chromium.orgad856b92009-07-27 21:50:25 +00001040 basefilename=`basename "$desktop_file" | cut -d '.' -f 1`
dkegel@google.comf20da1f2009-06-30 19:16:32 +00001041 if [ -z "$menu_name" ] ; then
1042 menu_name="$basefilename"
1043 else
1044 menu_name="$menu_name-$basefilename"
1045 fi
1046done
1047
1048if [ -n "$menu_name" ] ; then
1049 if [ x"$mode" = x"user" ] ; then
1050 update_submenu "user-$menu_name.menu"
1051 else
1052 update_submenu "$menu_name.menu"
1053 fi
1054else
1055 # Work around for SUSE/gnome 2.12 to pick up new ~/.local/share/applications
1056 if [ x"$mode" = x"user" ] ; then
1057 update_submenu
1058 fi
1059fi
1060
1061# Install *.directory files
1062
1063xdg_dir_name=desktop-directories
1064
1065xdg_user_dir="$XDG_DATA_HOME"
1066[ -n "$xdg_user_dir" ] || xdg_user_dir="$HOME/.local/share"
1067xdg_user_dir="$xdg_user_dir/$xdg_dir_name"
1068
1069xdg_system_dirs="$XDG_DATA_DIRS"
1070[ -n "$xdg_system_dirs" ] || xdg_system_dirs=/usr/local/share/:/usr/share/
1071xdg_global_dir=
1072for x in `echo $xdg_system_dirs | sed 's/:/ /g'` ; do
1073 if [ -w $x/$xdg_dir_name ] ; then
1074 xdg_global_dir="$x/$xdg_dir_name"
1075 break
1076 fi
1077done
1078
mdm@chromium.org56df6d32010-08-31 18:16:49 +00001079DEBUG 3 "Install locations for *.directory files:"
dkegel@google.comf20da1f2009-06-30 19:16:32 +00001080DEBUG 3 "xdg_user_dir: $xdg_user_dir"
1081DEBUG 3 "xdg_global_dir: $xdg_global_dir"
1082DEBUG 3 "kde_user_dir: $kde_user_dir"
1083DEBUG 3 "kde_global_dir: $kde_global_dir"
1084DEBUG 3 "gnome_user_dir: $gnome_user_dir"
1085DEBUG 3 "gnome_global_dir: $gnome_global_dir"
1086
1087if [ x"$mode" = x"user" ] ; then
1088 xdg_dir="$xdg_user_dir"
1089 kde_dir="$kde_user_dir"
1090 gnome_dir="$gnome_user_dir"
1091 my_umask=077
1092else
1093 xdg_dir="$xdg_global_dir"
1094 kde_dir="$kde_global_dir"
1095 gnome_dir="$gnome_global_dir"
1096 my_umask=022
1097 if [ -z "${xdg_dir}${kde_dir}${gnome_dir}" ] ; then
1098 exit_failure_operation_impossible "No writable system menu directory found."
1099 fi
1100fi
1101
1102for desktop_file in $directory_files; do
mdm@chromium.orgad856b92009-07-27 21:50:25 +00001103 basefile=`basename "$desktop_file"`
dkegel@google.comf20da1f2009-06-30 19:16:32 +00001104
1105 DEBUG 1 "$action $desktop_file in $xdg_dir $kde_dir $gnome_dir"
1106
1107 case $action in
1108 install)
1109 save_umask=`umask`
1110 umask $my_umask
1111
1112 for x in $xdg_dir $kde_dir $gnome_dir ; do
1113 mkdir -p $x
1114 eval 'cp $desktop_file $x/$basefile'$xdg_redirect_output
1115 done
1116
1117 umask $save_umask
1118 ;;
1119
1120 uninstall)
1121 for x in $xdg_dir $kde_dir $gnome_dir ; do
1122 rm -f $x/$basefile
1123 done
1124
1125 ;;
1126 esac
1127done
1128
1129# Install *.desktop files
1130xdg_dir_name=applications
1131
1132xdg_user_dir="$XDG_DATA_HOME"
1133[ -n "$xdg_user_dir" ] || xdg_user_dir="$HOME/.local/share"
1134xdg_user_dir="$xdg_user_dir/$xdg_dir_name"
1135
1136xdg_system_dirs="$XDG_DATA_DIRS"
1137[ -n "$xdg_system_dirs" ] || xdg_system_dirs=/usr/local/share/:/usr/share/
1138xdg_global_dir=
1139for x in `echo $xdg_system_dirs | sed 's/:/ /g'` ; do
1140 if [ -w $x/$xdg_dir_name ] ; then
1141 xdg_global_dir="$x/$xdg_dir_name"
1142 break
1143 fi
1144done
1145
mdm@chromium.org56df6d32010-08-31 18:16:49 +00001146kde_user_dir=`kde${KDE_SESSION_VERSION}-config --path apps 2> /dev/null | cut -d ':' -f 1`
1147kde_global_dir=`kde${KDE_SESSION_VERSION}-config --path apps 2> /dev/null | cut -d ':' -f 2`
dkegel@google.comf20da1f2009-06-30 19:16:32 +00001148[ -w $kde_global_dir ] || kde_global_dir=
1149
1150gnome_user_dir="$HOME/.gnome/apps"
1151gnome_global_dir="/usr/share/gnome/apps"
1152[ -w $gnome_global_dir ] || gnome_global_dir=
1153
mdm@chromium.org56df6d32010-08-31 18:16:49 +00001154DEBUG 3 "Install locations for *.desktop files:"
dkegel@google.comf20da1f2009-06-30 19:16:32 +00001155DEBUG 3 "xdg_user_dir: $xdg_user_dir"
1156DEBUG 3 "xdg_global_dir: $xdg_global_dir"
1157DEBUG 3 "kde_user_dir: $kde_user_dir"
1158DEBUG 3 "kde_global_dir: $kde_global_dir"
1159DEBUG 3 "gnome_user_dir: $gnome_user_dir"
1160DEBUG 3 "gnome_global_dir: $gnome_global_dir"
1161
1162if [ x"$mode" = x"user" ] ; then
1163 xdg_dir="$xdg_user_dir"
1164 kde_dir="$kde_user_dir"
1165 gnome_dir="$gnome_user_dir"
1166 my_umask=077
1167else
1168 xdg_dir="$xdg_global_dir"
1169 kde_dir="$kde_global_dir"
1170 gnome_dir="$gnome_global_dir"
1171 my_umask=022
1172 if [ -z "${xdg_dir}${kde_dir}${gnome_dir}" ] ; then
1173 exit_failure_operation_impossible "No writable system menu directory found."
1174 fi
1175fi
1176
1177for desktop_file in $desktop_files; do
1178 if [ "$vendor" = "true" -a "$action" = "install" ] ; then
1179 check_vendor_prefix "$desktop_file"
1180 fi
1181
mdm@chromium.orgad856b92009-07-27 21:50:25 +00001182 basefile=`basename "$desktop_file"`
dkegel@google.comf20da1f2009-06-30 19:16:32 +00001183
1184 DEBUG 1 "$action $desktop_file in $xdg_dir $kde_dir $gnome_dir"
1185
1186 case $action in
1187 install)
1188 save_umask=`umask`
1189 umask $my_umask
1190
1191 for x in $xdg_dir $kde_dir $gnome_dir ; do
1192 mkdir -p $x
1193 eval 'cp $desktop_file $x/$basefile'$xdg_redirect_output
1194 done
1195
dkegel@google.comf20da1f2009-06-30 19:16:32 +00001196 if [ -f $kde_dir/$basefile ] ; then
1197 echo "OnlyShowIn=Old;" >> $kde_dir/$basefile
1198 fi
1199
1200 if [ -f $gnome_dir/$basefile ] ; then
1201 echo "OnlyShowIn=Old;" >> $gnome_dir/$basefile
1202 fi
mdm@chromium.org56df6d32010-08-31 18:16:49 +00001203
dkegel@google.comf20da1f2009-06-30 19:16:32 +00001204 make_lazy_default "$xdg_dir" "$basefile"
1205
1206 umask $save_umask
1207 ;;
1208
1209 uninstall)
1210 for x in $xdg_dir $kde_dir $gnome_dir ; do
1211 rm -f $x/$basefile
1212 done
1213
1214 ;;
1215 esac
1216done
1217
1218if [ x"$update" = x"yes" ] ; then
1219 update_desktop_database
1220fi
1221
1222exit_success