1 | #!/bin/sh
|
2 | #---------------------------------------------
|
3 | # xdg-open
|
4 | #
|
5 | # Utility script to open a URL in the registered default application.
|
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 |
|
36 | manualpage()
|
37 | {
|
38 | cat << _MANUALPAGE
|
39 | Name
|
40 |
|
41 | xdg-open - opens a file or URL in the user's preferred application
|
42 |
|
43 | Synopsis
|
44 |
|
45 | xdg-open { file | URL }
|
46 |
|
47 | xdg-open { --help | --manual | --version }
|
48 |
|
49 | Description
|
50 |
|
51 | xdg-open opens a file or URL in the user's preferred application. If a URL is
|
52 | provided the URL will be opened in the user's preferred web browser. If a file
|
53 | is provided the file will be opened in the preferred application for files of
|
54 | that type. xdg-open supports file, ftp, http and https URLs.
|
55 |
|
56 | xdg-open is for use inside a desktop session only. It is not recommended to use
|
57 | xdg-open as root.
|
58 |
|
59 | Options
|
60 |
|
61 | --help
|
62 | Show command synopsis.
|
63 | --manual
|
64 | Show this manualpage.
|
65 | --version
|
66 | Show the xdg-utils version information.
|
67 |
|
68 | Exit Codes
|
69 |
|
70 | An exit code of 0 indicates success while a non-zero exit code indicates
|
71 | failure. The following failure codes can be returned:
|
72 |
|
73 | 1
|
74 | Error in command line syntax.
|
75 | 2
|
76 | One of the files passed on the command line did not exist.
|
77 | 3
|
78 | A required tool could not be found.
|
79 | 4
|
80 | The action failed.
|
81 |
|
82 | Examples
|
83 |
|
84 | xdg-open 'http://www.freedesktop.org/'
|
85 |
|
86 | Opens the Freedesktop.org website in the user's default browser
|
87 |
|
88 | xdg-open /tmp/foobar.png
|
89 |
|
90 | Opens the PNG image file /tmp/foobar.png in the user's default image viewing
|
91 | application.
|
92 |
|
93 | _MANUALPAGE
|
94 | }
|
95 |
|
96 | usage()
|
97 | {
|
98 | cat << _USAGE
|
99 | xdg-open - opens a file or URL in the user's preferred application
|
100 |
|
101 | Synopsis
|
102 |
|
103 | xdg-open { file | URL }
|
104 |
|
105 | xdg-open { --help | --manual | --version }
|
106 |
|
107 | _USAGE
|
108 | }
|
109 |
|
110 | #@xdg-utils-common@
|
111 |
|
112 | #----------------------------------------------------------------------------
|
113 | # Common utility functions included in all XDG wrapper scripts
|
114 | #----------------------------------------------------------------------------
|
115 |
|
116 | DEBUG()
|
117 | {
|
118 | [ -z "${XDG_UTILS_DEBUG_LEVEL}" ] && return 0;
|
119 | [ ${XDG_UTILS_DEBUG_LEVEL} -lt $1 ] && return 0;
|
120 | shift
|
121 | echo "$@" >&2
|
122 | }
|
123 |
|
124 | #-------------------------------------------------------------
|
125 | # Exit script on successfully completing the desired operation
|
126 |
|
127 | exit_success()
|
128 | {
|
129 | if [ $# -gt 0 ]; then
|
130 | echo "$@"
|
131 | echo
|
132 | fi
|
133 |
|
134 | exit 0
|
135 | }
|
136 |
|
137 |
|
138 | #-----------------------------------------
|
139 | # Exit script on malformed arguments, not enough arguments
|
140 | # or missing required option.
|
141 | # prints usage information
|
142 |
|
143 | exit_failure_syntax()
|
144 | {
|
145 | if [ $# -gt 0 ]; then
|
146 | echo "xdg-open: $@" >&2
|
147 | echo "Try 'xdg-open --help' for more information." >&2
|
148 | else
|
149 | usage
|
150 | echo "Use 'man xdg-open' or 'xdg-open --manual' for additional info."
|
151 | fi
|
152 |
|
153 | exit 1
|
154 | }
|
155 |
|
156 | #-------------------------------------------------------------
|
157 | # Exit script on missing file specified on command line
|
158 |
|
159 | exit_failure_file_missing()
|
160 | {
|
161 | if [ $# -gt 0 ]; then
|
162 | echo "xdg-open: $@" >&2
|
163 | fi
|
164 |
|
165 | exit 2
|
166 | }
|
167 |
|
168 | #-------------------------------------------------------------
|
169 | # Exit script on failure to locate necessary tool applications
|
170 |
|
171 | exit_failure_operation_impossible()
|
172 | {
|
173 | if [ $# -gt 0 ]; then
|
174 | echo "xdg-open: $@" >&2
|
175 | fi
|
176 |
|
177 | exit 3
|
178 | }
|
179 |
|
180 | #-------------------------------------------------------------
|
181 | # Exit script on failure returned by a tool application
|
182 |
|
183 | exit_failure_operation_failed()
|
184 | {
|
185 | if [ $# -gt 0 ]; then
|
186 | echo "xdg-open: $@" >&2
|
187 | fi
|
188 |
|
189 | exit 4
|
190 | }
|
191 |
|
192 | #------------------------------------------------------------
|
193 | # Exit script on insufficient permission to read a specified file
|
194 |
|
195 | exit_failure_file_permission_read()
|
196 | {
|
197 | if [ $# -gt 0 ]; then
|
198 | echo "xdg-open: $@" >&2
|
199 | fi
|
200 |
|
201 | exit 5
|
202 | }
|
203 |
|
204 | #------------------------------------------------------------
|
205 | # Exit script on insufficient permission to write a specified file
|
206 |
|
207 | exit_failure_file_permission_write()
|
208 | {
|
209 | if [ $# -gt 0 ]; then
|
210 | echo "xdg-open: $@" >&2
|
211 | fi
|
212 |
|
213 | exit 6
|
214 | }
|
215 |
|
216 | check_input_file()
|
217 | {
|
218 | if [ ! -e "$1" ]; then
|
219 | exit_failure_file_missing "file '$1' does not exist"
|
220 | fi
|
221 | if [ ! -r "$1" ]; then
|
222 | exit_failure_file_permission_read "no permission to read file '$1'"
|
223 | fi
|
224 | }
|
225 |
|
226 | check_vendor_prefix()
|
227 | {
|
228 | file_label="$2"
|
229 | [ -n "$file_label" ] || file_label="filename"
|
230 | file=`basename "$1"`
|
231 | case "$file" in
|
232 | [a-zA-Z]*-*)
|
233 | return
|
234 | ;;
|
235 | esac
|
236 |
|
237 | echo "xdg-open: $file_label '$file' does not have a proper vendor prefix" >&2
|
238 | echo 'A vendor prefix consists of alpha characters ([a-zA-Z]) and is terminated' >&2
|
239 | echo 'with a dash ("-"). An example '"$file_label"' is '"'example-$file'" >&2
|
240 | echo "Use --novendor to override or 'xdg-open --manual' for additional info." >&2
|
241 | exit 1
|
242 | }
|
243 |
|
244 | check_output_file()
|
245 | {
|
246 | # if the file exists, check if it is writeable
|
247 | # if it does not exists, check if we are allowed to write on the directory
|
248 | if [ -e "$1" ]; then
|
249 | if [ ! -w "$1" ]; then
|
250 | exit_failure_file_permission_write "no permission to write to file '$1'"
|
251 | fi
|
252 | else
|
253 | DIR=`dirname "$1"`
|
254 | if [ ! -w "$DIR" -o ! -x "$DIR" ]; then
|
255 | exit_failure_file_permission_write "no permission to create file '$1'"
|
256 | fi
|
257 | fi
|
258 | }
|
259 |
|
260 | #----------------------------------------
|
261 | # Checks for shared commands, e.g. --help
|
262 |
|
263 | check_common_commands()
|
264 | {
|
265 | while [ $# -gt 0 ] ; do
|
266 | parm="$1"
|
267 | shift
|
268 |
|
269 | case "$parm" in
|
270 | --help)
|
271 | usage
|
272 | echo "Use 'man xdg-open' or 'xdg-open --manual' for additional info."
|
273 | exit_success
|
274 | ;;
|
275 |
|
276 | --manual)
|
277 | manualpage
|
278 | exit_success
|
279 | ;;
|
280 |
|
281 | --version)
|
282 | echo "xdg-open 1.0.2"
|
283 | exit_success
|
284 | ;;
|
285 | esac
|
286 | done
|
287 | }
|
288 |
|
289 | check_common_commands "$@"
|
290 |
|
291 | [ -z "${XDG_UTILS_DEBUG_LEVEL}" ] && unset XDG_UTILS_DEBUG_LEVEL;
|
292 | if [ ${XDG_UTILS_DEBUG_LEVEL-0} -lt 1 ]; then
|
293 | # Be silent
|
294 | xdg_redirect_output=" > /dev/null 2> /dev/null"
|
295 | else
|
296 | # All output to stderr
|
297 | xdg_redirect_output=" >&2"
|
298 | fi
|
299 |
|
300 | #--------------------------------------
|
301 | # Checks for known desktop environments
|
302 | # set variable DE to the desktop environments name, lowercase
|
303 |
|
304 | detectDE()
|
305 | {
|
306 | if [ x"$KDE_FULL_SESSION" = x"true" ]; then DE=kde;
|
307 | elif [ x"$GNOME_DESKTOP_SESSION_ID" != x"" ]; then DE=gnome;
|
308 | 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;
|
309 | elif xprop -root _DT_SAVE_MODE 2> /dev/null | grep ' = \"xfce4\"$' >/dev/null 2>&1; then DE=xfce;
|
310 | elif [ x"$DESKTOP_SESSION" == x"LXDE" ]; then DE=lxde;
|
311 | else DE=""
|
312 | fi
|
313 | }
|
314 |
|
315 | #----------------------------------------------------------------------------
|
316 | # kfmclient exec/openURL can give bogus exit value in KDE <= 3.5.4
|
317 | # It also always returns 1 in KDE 3.4 and earlier
|
318 | # Simply return 0 in such case
|
319 |
|
320 | kfmclient_fix_exit_code()
|
321 | {
|
322 | version=`kde${KDE_SESSION_VERSION}-config --version 2>/dev/null | grep '^KDE'`
|
323 | major=`echo $version | sed 's/KDE.*: \([0-9]\).*/\1/'`
|
324 | minor=`echo $version | sed 's/KDE.*: [0-9]*\.\([0-9]\).*/\1/'`
|
325 | release=`echo $version | sed 's/KDE.*: [0-9]*\.[0-9]*\.\([0-9]\).*/\1/'`
|
326 | test "$major" -gt 3 && return $1
|
327 | test "$minor" -gt 5 && return $1
|
328 | test "$release" -gt 4 && return $1
|
329 | return 0
|
330 | }
|
331 |
|
332 | # This handles backslashes but not quote marks.
|
333 | first_word()
|
334 | {
|
335 | read first rest
|
336 | echo "$first"
|
337 | }
|
338 |
|
339 | open_kde()
|
340 | {
|
341 | if kde-open -v 2>/dev/null 1>&2; then
|
342 | kde-open "$1"
|
343 | else
|
344 | if [ x"$KDE_SESSION_VERSION" = x"4" ]; then
|
345 | kfmclient openURL "$1"
|
346 | else
|
347 | kfmclient exec "$1"
|
348 | kfmclient_fix_exit_code $?
|
349 | fi
|
350 | fi
|
351 |
|
352 | if [ $? -eq 0 ]; then
|
353 | exit_success
|
354 | else
|
355 | exit_failure_operation_failed
|
356 | fi
|
357 | }
|
358 |
|
359 | open_gnome()
|
360 | {
|
361 | if gvfs-open --help 2>/dev/null 1>&2; then
|
362 | gvfs-open "$1"
|
363 | else
|
364 | gnome-open "$1"
|
365 | fi
|
366 |
|
367 | if [ $? -eq 0 ]; then
|
368 | exit_success
|
369 | else
|
370 | exit_failure_operation_failed
|
371 | fi
|
372 | }
|
373 |
|
374 | open_xfce()
|
375 | {
|
376 | exo-open "$1"
|
377 |
|
378 | if [ $? -eq 0 ]; then
|
379 | exit_success
|
380 | else
|
381 | exit_failure_operation_failed
|
382 | fi
|
383 | }
|
384 |
|
385 | open_generic_xdg_mime()
|
386 | {
|
387 | filetype=`xdg-mime query filetype "$1" | sed "s/;.*//"`
|
388 | default=`xdg-mime query default "$filetype"`
|
389 | if [ -n "$default" ] ; then
|
390 | xdg_user_dir="$XDG_DATA_HOME"
|
391 | [ -n "$xdg_user_dir" ] || xdg_user_dir="$HOME/.local/share"
|
392 |
|
393 | xdg_system_dirs="$XDG_DATA_DIRS"
|
394 | [ -n "$xdg_system_dirs" ] || xdg_system_dirs=/usr/local/share/:/usr/share/
|
395 |
|
396 | for x in `echo "$xdg_user_dir:$xdg_system_dirs" | sed 's/:/ /g'`; do
|
397 | local file="$x/applications/$default"
|
398 | if [ -r "$file" ] ; then
|
399 | command="`grep -E "^Exec(\[[^]=]*])?=" "$file" | cut -d= -f 2- | first_word`"
|
400 | command_exec=`which $command 2>/dev/null`
|
401 | if [ -x "$command_exec" ] ; then
|
402 | $command_exec "$1"
|
403 | if [ $? -eq 0 ]; then
|
404 | exit_success
|
405 | fi
|
406 | fi
|
407 | fi
|
408 | done
|
409 | fi
|
410 | }
|
411 |
|
412 | open_generic()
|
413 | {
|
414 | # Paths or file:// URLs
|
415 | if (echo "$1" | grep -q '^file://' ||
|
416 | ! echo "$1" | egrep -q '^[a-zA-Z+\.\-]+:'); then
|
417 |
|
418 | local file="$1"
|
419 |
|
420 | # Decode URLs
|
421 | if echo "$file" | grep -q '^file:///'; then
|
422 | file=${file#file://}
|
423 | file="$(printf "$(echo "$file" | sed -e 's@%\([a-f0-9A-F]\{2\}\)@\\x\1@g')")"
|
424 | fi
|
425 | check_input_file "$file"
|
426 |
|
427 | open_generic_xdg_mime "$file"
|
428 |
|
429 | if [ -f /etc/debian_version ] &&
|
430 | which run-mailcap 2>/dev/null 1>&2; then
|
431 | run-mailcap --action=view "$file"
|
432 | if [ $? -eq 0 ]; then
|
433 | exit_success
|
434 | fi
|
435 | fi
|
436 |
|
437 | if mimeopen -v 2>/dev/null 1>&2; then
|
438 | mimeopen -L -n "$file"
|
439 | if [ $? -eq 0 ]; then
|
440 | exit_success
|
441 | fi
|
442 | fi
|
443 | fi
|
444 |
|
445 | IFS=":"
|
446 | for browser in $BROWSER; do
|
447 | if [ x"$browser" != x"" ]; then
|
448 |
|
449 | browser_with_arg=`printf "$browser" "$1" 2>/dev/null`
|
450 | if [ $? -ne 0 ]; then
|
451 | browser_with_arg=$browser;
|
452 | fi
|
453 |
|
454 | if [ x"$browser_with_arg" = x"$browser" ]; then
|
455 | "$browser" "$1";
|
456 | else eval '$browser_with_arg'$xdg_redirect_output;
|
457 | fi
|
458 |
|
459 | if [ $? -eq 0 ]; then
|
460 | exit_success;
|
461 | fi
|
462 | fi
|
463 | done
|
464 |
|
465 | exit_failure_operation_impossible "no method available for opening '$1'"
|
466 | }
|
467 |
|
468 | open_lxde()
|
469 | {
|
470 | # pcmanfm only knows how to handle file:// urls and filepaths, it seems.
|
471 | if (echo "$1" | grep -q '^file://' ||
|
472 | ! echo "$1" | egrep -q '^[a-zA-Z+\.\-]+:')
|
473 | then
|
474 | local file="$(echo "$1" | sed 's%^file://%%')"
|
475 |
|
476 | # handle relative paths
|
477 | if ! echo "$file" | grep -q '^/'; then
|
478 | file="$(pwd)/$file"
|
479 | fi
|
480 |
|
481 | pcmanfm "$file"
|
482 |
|
483 | else
|
484 | open_generic "$1"
|
485 | fi
|
486 |
|
487 | if [ $? -eq 0 ]; then
|
488 | exit_success
|
489 | else
|
490 | exit_failure_operation_failed
|
491 | fi
|
492 | }
|
493 |
|
494 | [ x"$1" != x"" ] || exit_failure_syntax
|
495 |
|
496 | url=
|
497 | while [ $# -gt 0 ] ; do
|
498 | parm="$1"
|
499 | shift
|
500 |
|
501 | case "$parm" in
|
502 | -*)
|
503 | exit_failure_syntax "unexpected option '$parm'"
|
504 | ;;
|
505 |
|
506 | *)
|
507 | if [ -n "$url" ] ; then
|
508 | exit_failure_syntax "unexpected argument '$parm'"
|
509 | fi
|
510 | url="$parm"
|
511 | ;;
|
512 | esac
|
513 | done
|
514 |
|
515 | if [ -z "${url}" ] ; then
|
516 | exit_failure_syntax "file or URL argument missing"
|
517 | fi
|
518 |
|
519 | detectDE
|
520 |
|
521 | if [ x"$DE" = x"" ]; then
|
522 | DE=generic
|
523 | fi
|
524 |
|
525 | # if BROWSER variable is not set, check some well known browsers instead
|
526 | if [ x"$BROWSER" = x"" ]; then
|
527 | BROWSER=links2:links:lynx:w3m
|
528 | if [ -n "$DISPLAY" ]; then
|
529 | BROWSER=firefox:mozilla:epiphany:konqueror:chromium-browser:google-chrome:$BROWSER
|
530 | fi
|
531 | fi
|
532 |
|
533 | case "$DE" in
|
534 | kde)
|
535 | open_kde "$url"
|
536 | ;;
|
537 |
|
538 | gnome)
|
539 | open_gnome "$url"
|
540 | ;;
|
541 |
|
542 | xfce)
|
543 | open_xfce "$url"
|
544 | ;;
|
545 |
|
546 | lxde)
|
547 | open_lxde "$url"
|
548 | ;;
|
549 |
|
550 | generic)
|
551 | open_generic "$url"
|
552 | ;;
|
553 |
|
554 | *)
|
555 | exit_failure_operation_impossible "no method available for opening '$url'"
|
556 | ;;
|
557 | esac
|