xdg-open 24 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025
  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. manualpage()
  36. {
  37. cat << _MANUALPAGE
  38. Name
  39. xdg-open - opens a file or URL in the user's preferred
  40. application
  41. Synopsis
  42. xdg-open { file | URL }
  43. xdg-open { --help | --manual | --version }
  44. Description
  45. xdg-open opens a file or URL in the user's preferred
  46. application. If a URL is provided the URL will be opened in the
  47. user's preferred web browser. If a file is provided the file
  48. will be opened in the preferred application for files of that
  49. type. xdg-open supports file, ftp, http and https URLs.
  50. xdg-open is for use inside a desktop session only. It is not
  51. recommended to use xdg-open as root.
  52. Options
  53. --help
  54. Show command synopsis.
  55. --manual
  56. Show this manual page.
  57. --version
  58. Show the xdg-utils version information.
  59. Exit Codes
  60. An exit code of 0 indicates success while a non-zero exit code
  61. indicates failure. The following failure codes can be returned:
  62. 1
  63. Error in command line syntax.
  64. 2
  65. One of the files passed on the command line did not
  66. exist.
  67. 3
  68. A required tool could not be found.
  69. 4
  70. The action failed.
  71. See Also
  72. xdg-mime(1), xdg-settings(1), MIME applications associations
  73. specification
  74. Examples
  75. xdg-open 'http://www.freedesktop.org/'
  76. Opens the freedesktop.org website in the user's default
  77. browser.
  78. xdg-open /tmp/foobar.png
  79. Opens the PNG image file /tmp/foobar.png in the user's default
  80. image viewing application.
  81. _MANUALPAGE
  82. }
  83. usage()
  84. {
  85. cat << _USAGE
  86. xdg-open - opens a file or URL in the user's preferred
  87. application
  88. Synopsis
  89. xdg-open { file | URL }
  90. xdg-open { --help | --manual | --version }
  91. _USAGE
  92. }
  93. #@xdg-utils-common@
  94. #----------------------------------------------------------------------------
  95. # Common utility functions included in all XDG wrapper scripts
  96. #----------------------------------------------------------------------------
  97. DEBUG()
  98. {
  99. [ -z "${XDG_UTILS_DEBUG_LEVEL}" ] && return 0;
  100. [ ${XDG_UTILS_DEBUG_LEVEL} -lt $1 ] && return 0;
  101. shift
  102. echo "$@" >&2
  103. }
  104. # This handles backslashes but not quote marks.
  105. first_word()
  106. {
  107. read first rest
  108. echo "$first"
  109. }
  110. #-------------------------------------------------------------
  111. # map a binary to a .desktop file
  112. binary_to_desktop_file()
  113. {
  114. search="${XDG_DATA_HOME:-$HOME/.local/share}:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}"
  115. binary="`which "$1"`"
  116. binary="`readlink -f "$binary"`"
  117. base="`basename "$binary"`"
  118. IFS=:
  119. for dir in $search; do
  120. unset IFS
  121. [ "$dir" ] || continue
  122. [ -d "$dir/applications" ] || [ -d "$dir/applnk" ] || continue
  123. for file in "$dir"/applications/*.desktop "$dir"/applications/*/*.desktop "$dir"/applnk/*.desktop "$dir"/applnk/*/*.desktop; do
  124. [ -r "$file" ] || continue
  125. # Check to make sure it's worth the processing.
  126. grep -q "^Exec.*$base" "$file" || continue
  127. # Make sure it's a visible desktop file (e.g. not "preferred-web-browser.desktop").
  128. grep -Eq "^(NoDisplay|Hidden)=true" "$file" && continue
  129. command="`grep -E "^Exec(\[[^]=]*])?=" "$file" | cut -d= -f 2- | first_word`"
  130. command="`which "$command"`"
  131. if [ x"`readlink -f "$command"`" = x"$binary" ]; then
  132. # Fix any double slashes that got added path composition
  133. echo "$file" | sed -e 's,//*,/,g'
  134. return
  135. fi
  136. done
  137. done
  138. }
  139. #-------------------------------------------------------------
  140. # map a .desktop file to a binary
  141. desktop_file_to_binary()
  142. {
  143. search="${XDG_DATA_HOME:-$HOME/.local/share}:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}"
  144. desktop="`basename "$1"`"
  145. IFS=:
  146. for dir in $search; do
  147. unset IFS
  148. [ "$dir" ] && [ -d "$dir/applications" ] || [ -d "$dir/applnk" ] || continue
  149. # Check if desktop file contains -
  150. if [ "${desktop#*-}" != "$desktop" ]; then
  151. vendor=${desktop%-*}
  152. app=${desktop#*-}
  153. if [ -r $dir/applications/$vendor/$app ]; then
  154. file_path=$dir/applications/$vendor/$app
  155. elif [ -r $dir/applnk/$vendor/$app ]; then
  156. file_path=$dir/applnk/$vendor/$app
  157. fi
  158. fi
  159. if test -z "$file_path" ; then
  160. for indir in "$dir"/applications/ "$dir"/applications/*/ "$dir"/applnk/ "$dir"/applnk/*/; do
  161. file="$indir/$desktop"
  162. if [ -r "$file" ]; then
  163. file_path=$file
  164. break
  165. fi
  166. done
  167. fi
  168. if [ -r "$file_path" ]; then
  169. # Remove any arguments (%F, %f, %U, %u, etc.).
  170. command="`grep -E "^Exec(\[[^]=]*])?=" "$file_path" | cut -d= -f 2- | first_word`"
  171. command="`which "$command"`"
  172. readlink -f "$command"
  173. return
  174. fi
  175. done
  176. }
  177. #-------------------------------------------------------------
  178. # Exit script on successfully completing the desired operation
  179. exit_success()
  180. {
  181. if [ $# -gt 0 ]; then
  182. echo "$@"
  183. echo
  184. fi
  185. exit 0
  186. }
  187. #-----------------------------------------
  188. # Exit script on malformed arguments, not enough arguments
  189. # or missing required option.
  190. # prints usage information
  191. exit_failure_syntax()
  192. {
  193. if [ $# -gt 0 ]; then
  194. echo "xdg-open: $@" >&2
  195. echo "Try 'xdg-open --help' for more information." >&2
  196. else
  197. usage
  198. echo "Use 'man xdg-open' or 'xdg-open --manual' for additional info."
  199. fi
  200. exit 1
  201. }
  202. #-------------------------------------------------------------
  203. # Exit script on missing file specified on command line
  204. exit_failure_file_missing()
  205. {
  206. if [ $# -gt 0 ]; then
  207. echo "xdg-open: $@" >&2
  208. fi
  209. exit 2
  210. }
  211. #-------------------------------------------------------------
  212. # Exit script on failure to locate necessary tool applications
  213. exit_failure_operation_impossible()
  214. {
  215. if [ $# -gt 0 ]; then
  216. echo "xdg-open: $@" >&2
  217. fi
  218. exit 3
  219. }
  220. #-------------------------------------------------------------
  221. # Exit script on failure returned by a tool application
  222. exit_failure_operation_failed()
  223. {
  224. if [ $# -gt 0 ]; then
  225. echo "xdg-open: $@" >&2
  226. fi
  227. exit 4
  228. }
  229. #------------------------------------------------------------
  230. # Exit script on insufficient permission to read a specified file
  231. exit_failure_file_permission_read()
  232. {
  233. if [ $# -gt 0 ]; then
  234. echo "xdg-open: $@" >&2
  235. fi
  236. exit 5
  237. }
  238. #------------------------------------------------------------
  239. # Exit script on insufficient permission to write a specified file
  240. exit_failure_file_permission_write()
  241. {
  242. if [ $# -gt 0 ]; then
  243. echo "xdg-open: $@" >&2
  244. fi
  245. exit 6
  246. }
  247. check_input_file()
  248. {
  249. if [ ! -e "$1" ]; then
  250. exit_failure_file_missing "file '$1' does not exist"
  251. fi
  252. if [ ! -r "$1" ]; then
  253. exit_failure_file_permission_read "no permission to read file '$1'"
  254. fi
  255. }
  256. check_vendor_prefix()
  257. {
  258. file_label="$2"
  259. [ -n "$file_label" ] || file_label="filename"
  260. file=`basename "$1"`
  261. case "$file" in
  262. [[:alpha:]]*-*)
  263. return
  264. ;;
  265. esac
  266. echo "xdg-open: $file_label '$file' does not have a proper vendor prefix" >&2
  267. echo 'A vendor prefix consists of alpha characters ([a-zA-Z]) and is terminated' >&2
  268. echo 'with a dash ("-"). An example '"$file_label"' is '"'example-$file'" >&2
  269. echo "Use --novendor to override or 'xdg-open --manual' for additional info." >&2
  270. exit 1
  271. }
  272. check_output_file()
  273. {
  274. # if the file exists, check if it is writeable
  275. # if it does not exists, check if we are allowed to write on the directory
  276. if [ -e "$1" ]; then
  277. if [ ! -w "$1" ]; then
  278. exit_failure_file_permission_write "no permission to write to file '$1'"
  279. fi
  280. else
  281. DIR=`dirname "$1"`
  282. if [ ! -w "$DIR" ] || [ ! -x "$DIR" ]; then
  283. exit_failure_file_permission_write "no permission to create file '$1'"
  284. fi
  285. fi
  286. }
  287. #----------------------------------------
  288. # Checks for shared commands, e.g. --help
  289. check_common_commands()
  290. {
  291. while [ $# -gt 0 ] ; do
  292. parm="$1"
  293. shift
  294. case "$parm" in
  295. --help)
  296. usage
  297. echo "Use 'man xdg-open' or 'xdg-open --manual' for additional info."
  298. exit_success
  299. ;;
  300. --manual)
  301. manualpage
  302. exit_success
  303. ;;
  304. --version)
  305. echo "xdg-open 1.1.2"
  306. exit_success
  307. ;;
  308. esac
  309. done
  310. }
  311. check_common_commands "$@"
  312. [ -z "${XDG_UTILS_DEBUG_LEVEL}" ] && unset XDG_UTILS_DEBUG_LEVEL;
  313. if [ ${XDG_UTILS_DEBUG_LEVEL-0} -lt 1 ]; then
  314. # Be silent
  315. xdg_redirect_output=" > /dev/null 2> /dev/null"
  316. else
  317. # All output to stderr
  318. xdg_redirect_output=" >&2"
  319. fi
  320. #--------------------------------------
  321. # Checks for known desktop environments
  322. # set variable DE to the desktop environments name, lowercase
  323. detectDE()
  324. {
  325. # see https://bugs.freedesktop.org/show_bug.cgi?id=34164
  326. unset GREP_OPTIONS
  327. if [ -n "${XDG_CURRENT_DESKTOP}" ]; then
  328. case "${XDG_CURRENT_DESKTOP}" in
  329. # only recently added to menu-spec, pre-spec X- still in use
  330. Cinnamon|X-Cinnamon)
  331. DE=cinnamon;
  332. ;;
  333. ENLIGHTENMENT)
  334. DE=enlightenment;
  335. ;;
  336. # GNOME, GNOME-Classic:GNOME, or GNOME-Flashback:GNOME
  337. GNOME*)
  338. DE=gnome;
  339. ;;
  340. KDE)
  341. DE=kde;
  342. ;;
  343. LXDE)
  344. DE=lxde;
  345. ;;
  346. LXQt)
  347. DE=lxqt;
  348. ;;
  349. MATE)
  350. DE=mate;
  351. ;;
  352. XFCE)
  353. DE=xfce
  354. ;;
  355. X-Generic)
  356. DE=generic
  357. ;;
  358. esac
  359. fi
  360. if [ x"$DE" = x"" ]; then
  361. # classic fallbacks
  362. if [ x"$KDE_FULL_SESSION" != x"" ]; then DE=kde;
  363. elif [ x"$GNOME_DESKTOP_SESSION_ID" != x"" ]; then DE=gnome;
  364. elif [ x"$MATE_DESKTOP_SESSION_ID" != x"" ]; then DE=mate;
  365. 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;
  366. elif xprop -root _DT_SAVE_MODE 2> /dev/null | grep ' = \"xfce4\"$' >/dev/null 2>&1; then DE=xfce;
  367. elif xprop -root 2> /dev/null | grep -i '^xfce_desktop_window' >/dev/null 2>&1; then DE=xfce
  368. elif echo $DESKTOP | grep -q '^Enlightenment'; then DE=enlightenment;
  369. elif [ x"$LXQT_SESSION_CONFIG" != x"" ]; then DE=lxqt;
  370. fi
  371. fi
  372. if [ x"$DE" = x"" ]; then
  373. # fallback to checking $DESKTOP_SESSION
  374. case "$DESKTOP_SESSION" in
  375. gnome)
  376. DE=gnome;
  377. ;;
  378. LXDE|Lubuntu)
  379. DE=lxde;
  380. ;;
  381. MATE)
  382. DE=mate;
  383. ;;
  384. xfce|xfce4|'Xfce Session')
  385. DE=xfce;
  386. ;;
  387. esac
  388. fi
  389. if [ x"$DE" = x"" ]; then
  390. # fallback to uname output for other platforms
  391. case "$(uname 2>/dev/null)" in
  392. CYGWIN*)
  393. DE=cygwin;
  394. ;;
  395. Darwin)
  396. DE=darwin;
  397. ;;
  398. esac
  399. fi
  400. if [ x"$DE" = x"gnome" ]; then
  401. # gnome-default-applications-properties is only available in GNOME 2.x
  402. # but not in GNOME 3.x
  403. which gnome-default-applications-properties > /dev/null 2>&1 || DE="gnome3"
  404. fi
  405. if [ -f "$XDG_RUNTIME_DIR/flatpak-info" ]; then
  406. DE="flatpak"
  407. fi
  408. }
  409. #----------------------------------------------------------------------------
  410. # kfmclient exec/openURL can give bogus exit value in KDE <= 3.5.4
  411. # It also always returns 1 in KDE 3.4 and earlier
  412. # Simply return 0 in such case
  413. kfmclient_fix_exit_code()
  414. {
  415. version=`LC_ALL=C.UTF-8 kde-config --version 2>/dev/null | grep '^KDE'`
  416. major=`echo $version | sed 's/KDE.*: \([0-9]\).*/\1/'`
  417. minor=`echo $version | sed 's/KDE.*: [0-9]*\.\([0-9]\).*/\1/'`
  418. release=`echo $version | sed 's/KDE.*: [0-9]*\.[0-9]*\.\([0-9]\).*/\1/'`
  419. test "$major" -gt 3 && return $1
  420. test "$minor" -gt 5 && return $1
  421. test "$release" -gt 4 && return $1
  422. return 0
  423. }
  424. #----------------------------------------------------------------------------
  425. # Returns true if there is a graphical display attached.
  426. has_display()
  427. {
  428. if [ -n "$DISPLAY" ] || [ -n "$WAYLAND_DISPLAY" ]; then
  429. return 0
  430. else
  431. return 1
  432. fi
  433. }
  434. # This handles backslashes but not quote marks.
  435. last_word()
  436. {
  437. read first rest
  438. echo "$rest"
  439. }
  440. # Get the value of a key in a desktop file's Desktop Entry group.
  441. # Example: Use get_key foo.desktop Exec
  442. # to get the values of the Exec= key for the Desktop Entry group.
  443. get_key()
  444. {
  445. local file="${1}"
  446. local key="${2}"
  447. local desktop_entry=""
  448. IFS_="${IFS}"
  449. IFS=""
  450. while read line
  451. do
  452. case "$line" in
  453. "[Desktop Entry]")
  454. desktop_entry="y"
  455. ;;
  456. # Reset match flag for other groups
  457. "["*)
  458. desktop_entry=""
  459. ;;
  460. "${key}="*)
  461. # Only match Desktop Entry group
  462. if [ -n "${desktop_entry}" ]
  463. then
  464. echo "${line}" | cut -d= -f 2-
  465. fi
  466. esac
  467. done < "${file}"
  468. IFS="${IFS_}"
  469. }
  470. # Returns true if argument is a file:// URL or path
  471. is_file_url_or_path()
  472. {
  473. if echo "$1" | grep -q '^file://' \
  474. || ! echo "$1" | egrep -q '^[[:alpha:]+\.\-]+:'; then
  475. return 0
  476. else
  477. return 1
  478. fi
  479. }
  480. # If argument is a file URL, convert it to a (percent-decoded) path.
  481. # If not, leave it as it is.
  482. file_url_to_path()
  483. {
  484. local file="$1"
  485. if echo "$file" | grep -q '^file:///'; then
  486. file=${file#file://}
  487. file=${file%%#*}
  488. file=$(echo "$file" | sed -r 's/\?.*$//')
  489. local printf=printf
  490. if [ -x /usr/bin/printf ]; then
  491. printf=/usr/bin/printf
  492. fi
  493. file=$($printf "$(echo "$file" | sed -e 's@%\([a-f0-9A-F]\{2\}\)@\\x\1@g')")
  494. fi
  495. echo "$file"
  496. }
  497. open_cygwin()
  498. {
  499. cygstart "$1"
  500. if [ $? -eq 0 ]; then
  501. exit_success
  502. else
  503. exit_failure_operation_failed
  504. fi
  505. }
  506. open_darwin()
  507. {
  508. open "$1"
  509. if [ $? -eq 0 ]; then
  510. exit_success
  511. else
  512. exit_failure_operation_failed
  513. fi
  514. }
  515. open_kde()
  516. {
  517. if [ -n "${KDE_SESSION_VERSION}" ]; then
  518. case "${KDE_SESSION_VERSION}" in
  519. 4)
  520. kde-open "$1"
  521. ;;
  522. 5)
  523. kde-open${KDE_SESSION_VERSION} "$1"
  524. ;;
  525. esac
  526. else
  527. kfmclient exec "$1"
  528. kfmclient_fix_exit_code $?
  529. fi
  530. if [ $? -eq 0 ]; then
  531. exit_success
  532. else
  533. exit_failure_operation_failed
  534. fi
  535. }
  536. open_gnome3()
  537. {
  538. if gio help open 2>/dev/null 1>&2; then
  539. gio open "$1"
  540. elif gvfs-open --help 2>/dev/null 1>&2; then
  541. gvfs-open "$1"
  542. else
  543. open_generic "$1"
  544. fi
  545. if [ $? -eq 0 ]; then
  546. exit_success
  547. else
  548. exit_failure_operation_failed
  549. fi
  550. }
  551. open_gnome()
  552. {
  553. if gio help open 2>/dev/null 1>&2; then
  554. gio open "$1"
  555. elif gvfs-open --help 2>/dev/null 1>&2; then
  556. gvfs-open "$1"
  557. elif gnome-open --help 2>/dev/null 1>&2; then
  558. gnome-open "$1"
  559. else
  560. open_generic "$1"
  561. fi
  562. if [ $? -eq 0 ]; then
  563. exit_success
  564. else
  565. exit_failure_operation_failed
  566. fi
  567. }
  568. open_mate()
  569. {
  570. if gio help open 2>/dev/null 1>&2; then
  571. gio open "$1"
  572. elif gvfs-open --help 2>/dev/null 1>&2; then
  573. gvfs-open "$1"
  574. elif mate-open --help 2>/dev/null 1>&2; then
  575. mate-open "$1"
  576. else
  577. open_generic "$1"
  578. fi
  579. if [ $? -eq 0 ]; then
  580. exit_success
  581. else
  582. exit_failure_operation_failed
  583. fi
  584. }
  585. open_xfce()
  586. {
  587. if exo-open --help 2>/dev/null 1>&2; then
  588. exo-open "$1"
  589. elif gio help open 2>/dev/null 1>&2; then
  590. gio open "$1"
  591. elif gvfs-open --help 2>/dev/null 1>&2; then
  592. gvfs-open "$1"
  593. else
  594. open_generic "$1"
  595. fi
  596. if [ $? -eq 0 ]; then
  597. exit_success
  598. else
  599. exit_failure_operation_failed
  600. fi
  601. }
  602. open_enlightenment()
  603. {
  604. if enlightenment_open --help 2>/dev/null 1>&2; then
  605. enlightenment_open "$1"
  606. else
  607. open_generic "$1"
  608. fi
  609. if [ $? -eq 0 ]; then
  610. exit_success
  611. else
  612. exit_failure_operation_failed
  613. fi
  614. }
  615. open_flatpak()
  616. {
  617. gdbus call --session \
  618. --dest org.freedesktop.portal.Desktop \
  619. --object-path /org/freedesktop/portal/desktop \
  620. --method org.freedesktop.portal.OpenURI.OpenURI \
  621. "" "$1" {}
  622. if [ $? -eq 0 ]; then
  623. exit_success
  624. else
  625. exit_failure_operation_failed
  626. fi
  627. }
  628. #-----------------------------------------
  629. # Recursively search .desktop file
  630. search_desktop_file()
  631. {
  632. local default="$1"
  633. local dir="$2"
  634. local target="$3"
  635. local file=""
  636. # look for both vendor-app.desktop, vendor/app.desktop
  637. if [ -r "$dir/$default" ]; then
  638. file="$dir/$default"
  639. elif [ -r "$dir/`echo $default | sed -e 's|-|/|'`" ]; then
  640. file="$dir/`echo $default | sed -e 's|-|/|'`"
  641. fi
  642. if [ -r "$file" ] ; then
  643. command="$(get_key "${file}" "Exec" | first_word)"
  644. command_exec=`which $command 2>/dev/null`
  645. icon="$(get_key "${file}" "Icon")"
  646. # FIXME: Actually LC_MESSAGES should be used as described in
  647. # http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s04.html
  648. localised_name="$(get_key "${file}" "Name")"
  649. set -- $(get_key "${file}" "Exec" | last_word)
  650. # We need to replace any occurrence of "%f", "%F" and
  651. # the like by the target file. We examine each
  652. # argument and append the modified argument to the
  653. # end then shift.
  654. local args=$#
  655. local replaced=0
  656. while [ $args -gt 0 ]; do
  657. case $1 in
  658. %[c])
  659. replaced=1
  660. arg="${localised_name}"
  661. shift
  662. set -- "$@" "$arg"
  663. ;;
  664. %[fFuU])
  665. replaced=1
  666. arg="$target"
  667. shift
  668. set -- "$@" "$arg"
  669. ;;
  670. %[i])
  671. replaced=1
  672. shift
  673. set -- "$@" "--icon" "$icon"
  674. ;;
  675. *)
  676. arg="$1"
  677. shift
  678. set -- "$@" "$arg"
  679. ;;
  680. esac
  681. args=$(( $args - 1 ))
  682. done
  683. [ $replaced -eq 1 ] || set -- "$@" "$target"
  684. "$command_exec" "$@"
  685. if [ $? -eq 0 ]; then
  686. exit_success
  687. fi
  688. fi
  689. for d in $dir/*/; do
  690. [ -d "$d" ] && search_desktop_file "$default" "$d" "$target"
  691. done
  692. }
  693. open_generic_xdg_mime()
  694. {
  695. filetype="$2"
  696. default=`xdg-mime query default "$filetype"`
  697. if [ -n "$default" ] ; then
  698. xdg_user_dir="$XDG_DATA_HOME"
  699. [ -n "$xdg_user_dir" ] || xdg_user_dir="$HOME/.local/share"
  700. xdg_system_dirs="$XDG_DATA_DIRS"
  701. [ -n "$xdg_system_dirs" ] || xdg_system_dirs=/usr/local/share/:/usr/share/
  702. DEBUG 3 "$xdg_user_dir:$xdg_system_dirs"
  703. for x in `echo "$xdg_user_dir:$xdg_system_dirs" | sed 's/:/ /g'`; do
  704. search_desktop_file "$default" "$x/applications/" "$1"
  705. done
  706. fi
  707. }
  708. open_generic_xdg_file_mime()
  709. {
  710. filetype=`xdg-mime query filetype "$1" | sed "s/;.*//"`
  711. open_generic_xdg_mime "$1" "$filetype"
  712. }
  713. open_generic_xdg_x_scheme_handler()
  714. {
  715. scheme="`echo $1 | sed -n 's/\(^[[:alnum:]+\.-]*\):.*$/\1/p'`"
  716. if [ -n $scheme ]; then
  717. filetype="x-scheme-handler/$scheme"
  718. open_generic_xdg_mime "$1" "$filetype"
  719. fi
  720. }
  721. open_envvar()
  722. {
  723. local oldifs="$IFS"
  724. local browser browser_with_arg
  725. IFS=":"
  726. for browser in $BROWSER; do
  727. IFS="$oldifs"
  728. if [ -z "$browser" ]; then
  729. continue
  730. fi
  731. if echo "$browser" | grep -q %s; then
  732. $(printf "$browser" "$1")
  733. else
  734. $browser "$1"
  735. fi
  736. if [ $? -eq 0 ]; then
  737. exit_success
  738. fi
  739. done
  740. }
  741. open_generic()
  742. {
  743. if is_file_url_or_path "$1"; then
  744. local file="$(file_url_to_path "$1")"
  745. check_input_file "$file"
  746. if has_display; then
  747. filetype=`xdg-mime query filetype "$file" | sed "s/;.*//"`
  748. open_generic_xdg_mime "$file" "$filetype"
  749. fi
  750. if which run-mailcap 2>/dev/null 1>&2; then
  751. run-mailcap --action=view "$file"
  752. if [ $? -eq 0 ]; then
  753. exit_success
  754. fi
  755. fi
  756. if has_display && mimeopen -v 2>/dev/null 1>&2; then
  757. mimeopen -L -n "$file"
  758. if [ $? -eq 0 ]; then
  759. exit_success
  760. fi
  761. fi
  762. fi
  763. if has_display; then
  764. open_generic_xdg_x_scheme_handler "$1"
  765. fi
  766. if [ -n "$BROWSER" ]; then
  767. open_envvar "$1"
  768. fi
  769. # if BROWSER variable is not set, check some well known browsers instead
  770. if [ x"$BROWSER" = x"" ]; then
  771. BROWSER=www-browser:links2:elinks:links:lynx:w3m
  772. if has_display; then
  773. BROWSER=x-www-browser:firefox:iceweasel:seamonkey:mozilla:epiphany:konqueror:chromium:chromium-browser:google-chrome:$BROWSER
  774. fi
  775. fi
  776. open_envvar "$1"
  777. exit_failure_operation_impossible "no method available for opening '$1'"
  778. }
  779. open_lxde()
  780. {
  781. # pcmanfm only knows how to handle file:// urls and filepaths, it seems.
  782. if is_file_url_or_path "$1"; then
  783. local file="$(file_url_to_path "$1")"
  784. # handle relative paths
  785. if ! echo "$file" | grep -q ^/; then
  786. file="$(pwd)/$file"
  787. fi
  788. pcmanfm "$file"
  789. else
  790. open_generic "$1"
  791. fi
  792. if [ $? -eq 0 ]; then
  793. exit_success
  794. else
  795. exit_failure_operation_failed
  796. fi
  797. }
  798. [ x"$1" != x"" ] || exit_failure_syntax
  799. url=
  800. while [ $# -gt 0 ] ; do
  801. parm="$1"
  802. shift
  803. case "$parm" in
  804. -*)
  805. exit_failure_syntax "unexpected option '$parm'"
  806. ;;
  807. *)
  808. if [ -n "$url" ] ; then
  809. exit_failure_syntax "unexpected argument '$parm'"
  810. fi
  811. url="$parm"
  812. ;;
  813. esac
  814. done
  815. if [ -z "${url}" ] ; then
  816. exit_failure_syntax "file or URL argument missing"
  817. fi
  818. detectDE
  819. if [ x"$DE" = x"" ]; then
  820. DE=generic
  821. fi
  822. DEBUG 2 "Selected DE $DE"
  823. # sanitize BROWSER (avoid caling ourselves in particular)
  824. case "${BROWSER}" in
  825. *:"xdg-open"|"xdg-open":*)
  826. BROWSER=$(echo $BROWSER | sed -e 's|:xdg-open||g' -e 's|xdg-open:||g')
  827. ;;
  828. "xdg-open")
  829. BROWSER=
  830. ;;
  831. esac
  832. case "$DE" in
  833. kde)
  834. open_kde "$url"
  835. ;;
  836. gnome3|cinnamon)
  837. open_gnome3 "$url"
  838. ;;
  839. gnome)
  840. open_gnome "$url"
  841. ;;
  842. mate)
  843. open_mate "$url"
  844. ;;
  845. xfce)
  846. open_xfce "$url"
  847. ;;
  848. lxde|lxqt)
  849. open_lxde "$url"
  850. ;;
  851. enlightenment)
  852. open_enlightenment "$url"
  853. ;;
  854. cygwin)
  855. open_cygwin "$url"
  856. ;;
  857. darwin)
  858. open_darwin "$url"
  859. ;;
  860. flatpak)
  861. open_flatpak "$url"
  862. ;;
  863. generic)
  864. open_generic "$url"
  865. ;;
  866. *)
  867. exit_failure_operation_impossible "no method available for opening '$url'"
  868. ;;
  869. esac