xdg-open 25 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067
  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.3"
  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. # Deepin Desktop Environments
  344. DEEPIN|Deepin|deepin)
  345. DE=dde;
  346. ;;
  347. LXDE)
  348. DE=lxde;
  349. ;;
  350. LXQt)
  351. DE=lxqt;
  352. ;;
  353. MATE)
  354. DE=mate;
  355. ;;
  356. XFCE)
  357. DE=xfce
  358. ;;
  359. X-Generic)
  360. DE=generic
  361. ;;
  362. esac
  363. fi
  364. if [ x"$DE" = x"" ]; then
  365. # classic fallbacks
  366. if [ x"$KDE_FULL_SESSION" != x"" ]; then DE=kde;
  367. elif [ x"$GNOME_DESKTOP_SESSION_ID" != x"" ]; then DE=gnome;
  368. elif [ x"$MATE_DESKTOP_SESSION_ID" != x"" ]; then DE=mate;
  369. 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;
  370. elif xprop -root _DT_SAVE_MODE 2> /dev/null | grep ' = \"xfce4\"$' >/dev/null 2>&1; then DE=xfce;
  371. elif xprop -root 2> /dev/null | grep -i '^xfce_desktop_window' >/dev/null 2>&1; then DE=xfce
  372. elif echo $DESKTOP | grep -q '^Enlightenment'; then DE=enlightenment;
  373. elif [ x"$LXQT_SESSION_CONFIG" != x"" ]; then DE=lxqt;
  374. fi
  375. fi
  376. if [ x"$DE" = x"" ]; then
  377. # fallback to checking $DESKTOP_SESSION
  378. case "$DESKTOP_SESSION" in
  379. gnome)
  380. DE=gnome;
  381. ;;
  382. LXDE|Lubuntu)
  383. DE=lxde;
  384. ;;
  385. MATE)
  386. DE=mate;
  387. ;;
  388. xfce|xfce4|'Xfce Session')
  389. DE=xfce;
  390. ;;
  391. esac
  392. fi
  393. if [ x"$DE" = x"" ]; then
  394. # fallback to uname output for other platforms
  395. case "$(uname 2>/dev/null)" in
  396. CYGWIN*)
  397. DE=cygwin;
  398. ;;
  399. Darwin)
  400. DE=darwin;
  401. ;;
  402. esac
  403. fi
  404. if [ x"$DE" = x"gnome" ]; then
  405. # gnome-default-applications-properties is only available in GNOME 2.x
  406. # but not in GNOME 3.x
  407. which gnome-default-applications-properties > /dev/null 2>&1 || DE="gnome3"
  408. fi
  409. if [ -f "$XDG_RUNTIME_DIR/flatpak-info" ]; then
  410. DE="flatpak"
  411. fi
  412. }
  413. #----------------------------------------------------------------------------
  414. # kfmclient exec/openURL can give bogus exit value in KDE <= 3.5.4
  415. # It also always returns 1 in KDE 3.4 and earlier
  416. # Simply return 0 in such case
  417. kfmclient_fix_exit_code()
  418. {
  419. version=`LC_ALL=C.UTF-8 kde-config --version 2>/dev/null | grep '^KDE'`
  420. major=`echo $version | sed 's/KDE.*: \([0-9]\).*/\1/'`
  421. minor=`echo $version | sed 's/KDE.*: [0-9]*\.\([0-9]\).*/\1/'`
  422. release=`echo $version | sed 's/KDE.*: [0-9]*\.[0-9]*\.\([0-9]\).*/\1/'`
  423. test "$major" -gt 3 && return $1
  424. test "$minor" -gt 5 && return $1
  425. test "$release" -gt 4 && return $1
  426. return 0
  427. }
  428. #----------------------------------------------------------------------------
  429. # Returns true if there is a graphical display attached.
  430. has_display()
  431. {
  432. if [ -n "$DISPLAY" ] || [ -n "$WAYLAND_DISPLAY" ]; then
  433. return 0
  434. else
  435. return 1
  436. fi
  437. }
  438. # This handles backslashes but not quote marks.
  439. last_word()
  440. {
  441. read first rest
  442. echo "$rest"
  443. }
  444. # Get the value of a key in a desktop file's Desktop Entry group.
  445. # Example: Use get_key foo.desktop Exec
  446. # to get the values of the Exec= key for the Desktop Entry group.
  447. get_key()
  448. {
  449. local file="${1}"
  450. local key="${2}"
  451. local desktop_entry=""
  452. IFS_="${IFS}"
  453. IFS=""
  454. while read line
  455. do
  456. case "$line" in
  457. "[Desktop Entry]")
  458. desktop_entry="y"
  459. ;;
  460. # Reset match flag for other groups
  461. "["*)
  462. desktop_entry=""
  463. ;;
  464. "${key}="*)
  465. # Only match Desktop Entry group
  466. if [ -n "${desktop_entry}" ]
  467. then
  468. echo "${line}" | cut -d= -f 2-
  469. fi
  470. esac
  471. done < "${file}"
  472. IFS="${IFS_}"
  473. }
  474. # Returns true if argument is a file:// URL or path
  475. is_file_url_or_path()
  476. {
  477. if echo "$1" | grep -q '^file://' \
  478. || ! echo "$1" | egrep -q '^[[:alpha:]+\.\-]+:'; then
  479. return 0
  480. else
  481. return 1
  482. fi
  483. }
  484. # If argument is a file URL, convert it to a (percent-decoded) path.
  485. # If not, leave it as it is.
  486. file_url_to_path()
  487. {
  488. local file="$1"
  489. if echo "$file" | grep -q '^file:///'; then
  490. file=${file#file://}
  491. file=${file%%#*}
  492. file=$(echo "$file" | sed -r 's/\?.*$//')
  493. local printf=printf
  494. if [ -x /usr/bin/printf ]; then
  495. printf=/usr/bin/printf
  496. fi
  497. file=$($printf "$(echo "$file" | sed -e 's@%\([a-f0-9A-F]\{2\}\)@\\x\1@g')")
  498. fi
  499. echo "$file"
  500. }
  501. open_cygwin()
  502. {
  503. cygstart "$1"
  504. if [ $? -eq 0 ]; then
  505. exit_success
  506. else
  507. exit_failure_operation_failed
  508. fi
  509. }
  510. open_darwin()
  511. {
  512. open "$1"
  513. if [ $? -eq 0 ]; then
  514. exit_success
  515. else
  516. exit_failure_operation_failed
  517. fi
  518. }
  519. open_kde()
  520. {
  521. if [ -n "${KDE_SESSION_VERSION}" ]; then
  522. case "${KDE_SESSION_VERSION}" in
  523. 4)
  524. kde-open "$1"
  525. ;;
  526. 5)
  527. kde-open${KDE_SESSION_VERSION} "$1"
  528. ;;
  529. esac
  530. else
  531. kfmclient exec "$1"
  532. kfmclient_fix_exit_code $?
  533. fi
  534. if [ $? -eq 0 ]; then
  535. exit_success
  536. else
  537. exit_failure_operation_failed
  538. fi
  539. }
  540. open_dde()
  541. {
  542. if dde-open -version >/dev/null 2>&1; then
  543. dde-open "$1"
  544. else
  545. open_generic "$1"
  546. fi
  547. if [ $? -eq 0 ]; then
  548. exit_success
  549. else
  550. exit_failure_operation_failed
  551. fi
  552. }
  553. open_gnome3()
  554. {
  555. if gio help open 2>/dev/null 1>&2; then
  556. gio open "$1"
  557. elif gvfs-open --help 2>/dev/null 1>&2; then
  558. gvfs-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_gnome()
  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 gnome-open --help 2>/dev/null 1>&2; then
  575. gnome-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_mate()
  586. {
  587. if gio help open 2>/dev/null 1>&2; then
  588. gio open "$1"
  589. elif gvfs-open --help 2>/dev/null 1>&2; then
  590. gvfs-open "$1"
  591. elif mate-open --help 2>/dev/null 1>&2; then
  592. mate-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_xfce()
  603. {
  604. if exo-open --help 2>/dev/null 1>&2; then
  605. exo-open "$1"
  606. elif gio help open 2>/dev/null 1>&2; then
  607. gio open "$1"
  608. elif gvfs-open --help 2>/dev/null 1>&2; then
  609. gvfs-open "$1"
  610. else
  611. open_generic "$1"
  612. fi
  613. if [ $? -eq 0 ]; then
  614. exit_success
  615. else
  616. exit_failure_operation_failed
  617. fi
  618. }
  619. open_enlightenment()
  620. {
  621. if enlightenment_open --help 2>/dev/null 1>&2; then
  622. enlightenment_open "$1"
  623. else
  624. open_generic "$1"
  625. fi
  626. if [ $? -eq 0 ]; then
  627. exit_success
  628. else
  629. exit_failure_operation_failed
  630. fi
  631. }
  632. open_flatpak()
  633. {
  634. gdbus call --session \
  635. --dest org.freedesktop.portal.Desktop \
  636. --object-path /org/freedesktop/portal/desktop \
  637. --method org.freedesktop.portal.OpenURI.OpenURI \
  638. "" "$1" {}
  639. if [ $? -eq 0 ]; then
  640. exit_success
  641. else
  642. exit_failure_operation_failed
  643. fi
  644. }
  645. #-----------------------------------------
  646. # Recursively search .desktop file
  647. search_desktop_file()
  648. {
  649. local default="$1"
  650. local dir="$2"
  651. local target="$3"
  652. local file=""
  653. # look for both vendor-app.desktop, vendor/app.desktop
  654. if [ -r "$dir/$default" ]; then
  655. file="$dir/$default"
  656. elif [ -r "$dir/`echo $default | sed -e 's|-|/|'`" ]; then
  657. file="$dir/`echo $default | sed -e 's|-|/|'`"
  658. fi
  659. if [ -r "$file" ] ; then
  660. command="$(get_key "${file}" "Exec" | first_word)"
  661. command_exec=`which $command 2>/dev/null`
  662. icon="$(get_key "${file}" "Icon")"
  663. # FIXME: Actually LC_MESSAGES should be used as described in
  664. # http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s04.html
  665. localised_name="$(get_key "${file}" "Name")"
  666. set -- $(get_key "${file}" "Exec" | last_word)
  667. # We need to replace any occurrence of "%f", "%F" and
  668. # the like by the target file. We examine each
  669. # argument and append the modified argument to the
  670. # end then shift.
  671. local args=$#
  672. local replaced=0
  673. while [ $args -gt 0 ]; do
  674. case $1 in
  675. %[c])
  676. replaced=1
  677. arg="${localised_name}"
  678. shift
  679. set -- "$@" "$arg"
  680. ;;
  681. %[fFuU])
  682. replaced=1
  683. arg="$target"
  684. shift
  685. set -- "$@" "$arg"
  686. ;;
  687. %[i])
  688. replaced=1
  689. shift
  690. set -- "$@" "--icon" "$icon"
  691. ;;
  692. *)
  693. arg="$1"
  694. shift
  695. set -- "$@" "$arg"
  696. ;;
  697. esac
  698. args=$(( $args - 1 ))
  699. done
  700. [ $replaced -eq 1 ] || set -- "$@" "$target"
  701. "$command_exec" "$@"
  702. if [ $? -eq 0 ]; then
  703. exit_success
  704. fi
  705. fi
  706. for d in $dir/*/; do
  707. [ -d "$d" ] && search_desktop_file "$default" "$d" "$target"
  708. done
  709. }
  710. open_generic_xdg_mime()
  711. {
  712. filetype="$2"
  713. default=`xdg-mime query default "$filetype"`
  714. if [ -n "$default" ] ; then
  715. xdg_user_dir="$XDG_DATA_HOME"
  716. [ -n "$xdg_user_dir" ] || xdg_user_dir="$HOME/.local/share"
  717. xdg_system_dirs="$XDG_DATA_DIRS"
  718. [ -n "$xdg_system_dirs" ] || xdg_system_dirs=/usr/local/share/:/usr/share/
  719. DEBUG 3 "$xdg_user_dir:$xdg_system_dirs"
  720. for x in `echo "$xdg_user_dir:$xdg_system_dirs" | sed 's/:/ /g'`; do
  721. search_desktop_file "$default" "$x/applications/" "$1"
  722. done
  723. fi
  724. }
  725. open_generic_xdg_file_mime()
  726. {
  727. filetype=`xdg-mime query filetype "$1" | sed "s/;.*//"`
  728. open_generic_xdg_mime "$1" "$filetype"
  729. }
  730. open_generic_xdg_x_scheme_handler()
  731. {
  732. scheme="`echo $1 | sed -n 's/\(^[[:alnum:]+\.-]*\):.*$/\1/p'`"
  733. if [ -n $scheme ]; then
  734. filetype="x-scheme-handler/$scheme"
  735. open_generic_xdg_mime "$1" "$filetype"
  736. fi
  737. }
  738. has_single_argument()
  739. {
  740. test $# = 1
  741. }
  742. open_envvar()
  743. {
  744. local oldifs="$IFS"
  745. local browser browser_with_arg
  746. IFS=":"
  747. for browser in $BROWSER; do
  748. IFS="$oldifs"
  749. if [ -z "$browser" ]; then
  750. continue
  751. fi
  752. if echo "$browser" | grep -q %s; then
  753. # Avoid argument injection.
  754. # See https://bugs.freedesktop.org/show_bug.cgi?id=103807
  755. # URIs don't have IFS characters spaces anyway.
  756. has_single_argument $1 && $(printf "$browser" "$1")
  757. else
  758. $browser "$1"
  759. fi
  760. if [ $? -eq 0 ]; then
  761. exit_success
  762. fi
  763. done
  764. }
  765. open_generic()
  766. {
  767. if is_file_url_or_path "$1"; then
  768. local file="$(file_url_to_path "$1")"
  769. check_input_file "$file"
  770. if has_display; then
  771. filetype=`xdg-mime query filetype "$file" | sed "s/;.*//"`
  772. open_generic_xdg_mime "$file" "$filetype"
  773. fi
  774. if which run-mailcap 2>/dev/null 1>&2; then
  775. run-mailcap --action=view "$file"
  776. if [ $? -eq 0 ]; then
  777. exit_success
  778. fi
  779. fi
  780. if has_display && mimeopen -v 2>/dev/null 1>&2; then
  781. mimeopen -L -n "$file"
  782. if [ $? -eq 0 ]; then
  783. exit_success
  784. fi
  785. fi
  786. fi
  787. if has_display; then
  788. open_generic_xdg_x_scheme_handler "$1"
  789. fi
  790. if [ -n "$BROWSER" ]; then
  791. open_envvar "$1"
  792. fi
  793. # if BROWSER variable is not set, check some well known browsers instead
  794. if [ x"$BROWSER" = x"" ]; then
  795. BROWSER=www-browser:links2:elinks:links:lynx:w3m
  796. if has_display; then
  797. BROWSER=x-www-browser:firefox:iceweasel:seamonkey:mozilla:epiphany:konqueror:chromium:chromium-browser:google-chrome:$BROWSER
  798. fi
  799. fi
  800. open_envvar "$1"
  801. exit_failure_operation_impossible "no method available for opening '$1'"
  802. }
  803. open_lxde()
  804. {
  805. # pcmanfm only knows how to handle file:// urls and filepaths, it seems.
  806. if pcmanfm --help >/dev/null 2>&1 && is_file_url_or_path "$1"; then
  807. local file="$(file_url_to_path "$1")"
  808. # handle relative paths
  809. if ! echo "$file" | grep -q ^/; then
  810. file="$(pwd)/$file"
  811. fi
  812. pcmanfm "$file"
  813. else
  814. open_generic "$1"
  815. fi
  816. if [ $? -eq 0 ]; then
  817. exit_success
  818. else
  819. exit_failure_operation_failed
  820. fi
  821. }
  822. open_lxqt()
  823. {
  824. open_generic "$1"
  825. }
  826. [ x"$1" != x"" ] || exit_failure_syntax
  827. url=
  828. while [ $# -gt 0 ] ; do
  829. parm="$1"
  830. shift
  831. case "$parm" in
  832. -*)
  833. exit_failure_syntax "unexpected option '$parm'"
  834. ;;
  835. *)
  836. if [ -n "$url" ] ; then
  837. exit_failure_syntax "unexpected argument '$parm'"
  838. fi
  839. url="$parm"
  840. ;;
  841. esac
  842. done
  843. if [ -z "${url}" ] ; then
  844. exit_failure_syntax "file or URL argument missing"
  845. fi
  846. detectDE
  847. if [ x"$DE" = x"" ]; then
  848. DE=generic
  849. fi
  850. DEBUG 2 "Selected DE $DE"
  851. # sanitize BROWSER (avoid caling ourselves in particular)
  852. case "${BROWSER}" in
  853. *:"xdg-open"|"xdg-open":*)
  854. BROWSER=$(echo $BROWSER | sed -e 's|:xdg-open||g' -e 's|xdg-open:||g')
  855. ;;
  856. "xdg-open")
  857. BROWSER=
  858. ;;
  859. esac
  860. case "$DE" in
  861. kde)
  862. open_kde "$url"
  863. ;;
  864. dde)
  865. open_dde "$url"
  866. ;;
  867. gnome3|cinnamon)
  868. open_gnome3 "$url"
  869. ;;
  870. gnome)
  871. open_gnome "$url"
  872. ;;
  873. mate)
  874. open_mate "$url"
  875. ;;
  876. xfce)
  877. open_xfce "$url"
  878. ;;
  879. lxde)
  880. open_lxde "$url"
  881. ;;
  882. lxqt)
  883. open_lxqt "$url"
  884. ;;
  885. enlightenment)
  886. open_enlightenment "$url"
  887. ;;
  888. cygwin)
  889. open_cygwin "$url"
  890. ;;
  891. darwin)
  892. open_darwin "$url"
  893. ;;
  894. flatpak)
  895. open_flatpak "$url"
  896. ;;
  897. generic)
  898. open_generic "$url"
  899. ;;
  900. *)
  901. exit_failure_operation_impossible "no method available for opening '$url'"
  902. ;;
  903. esac