#!/bin/sh
#---------------------------------------------
#   xdg-mime
#
#   Utility script to manipulate MIME related information
#   on XDG compliant systems.
#
#   Refer to the usage() function below for usage.
#
#   Copyright 2009-2010, Fathi Boudra <fabo@freedesktop.org>
#   Copyright 2009-2010, Rex Dieter <rdieter@fedoraproject.org>
#   Copyright 2006, Kevin Krammer <kevin.krammer@gmx.at>
#   Copyright 2006, Jeremy White <jwhite@codeweavers.com>
#
#   LICENSE:
#
#   Permission is hereby granted, free of charge, to any person obtaining a
#   copy of this software and associated documentation files (the "Software"),
#   to deal in the Software without restriction, including without limitation
#   the rights to use, copy, modify, merge, publish, distribute, sublicense,
#   and/or sell copies of the Software, and to permit persons to whom the
#   Software is furnished to do so, subject to the following conditions:
#
#   The above copyright notice and this permission notice shall be included
#   in all copies or substantial portions of the Software.
#
#   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
#   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
#   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
#   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
#   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
#   OTHER DEALINGS IN THE SOFTWARE.
#
#---------------------------------------------

manualpage()
{
cat << _MANUALPAGE
Name

   xdg-mime - command line tool for querying information about file type
   handling and adding descriptions for new file types

Synopsis

   xdg-mime query { filetype FILE | default mimetype }

   xdg-mime default application mimetype(s)

   xdg-mime install [--mode mode] [--novendor] mimetypes-file

   xdg-mime uninstall [--mode mode] mimetypes-file

   xdg-mime { --help | --manual | --version }

Description

   The xdg-mime program can be used to query information about file types and
   to add descriptions for new file types.

Commands

   query file

           Returns the file type of FILE in the form of a MIME type.

           Please note that FILE names starting with a - will be rejected,
           this can be mitigated:

              * Pass absolute paths, i.e. by using realpath as a
                preprocessor.

              * Prefix known relative filepaths with a “./”. For example
                using sed -E 's|^[^/]|./\0|' as a preprocessor.

           The query file option is for use inside a desktop session only. It
           is not recommended to use xdg-mime query file as root.

   query default

           Returns the default application that the desktop environment uses
           for opening files of type mimetype. The default application is
           identified by its *.desktop file.

           The value returned by this command may or may not match the
           application launched by xdg-open as xdg-open hands over to desktop
           specific openers most of the time which may implement their own
           logic.

           The query default option is for use inside a desktop session only.
           It is not recommended to use xdg-mime query default as root.

   default

           Ask the desktop environment to make application the default
           application for opening files of type mimetype. An application can
           be made the default for several file types by specifying multiple
           mimetypes.

           application is the desktop file id of the application and has the
           form vendor-name.desktop. application must already be installed in
           the desktop menu before it can be made the default handler. The
           application's desktop file must list support for all the MIME
           types that it wishes to be the default handler for.

           Requests to make an application a default handler may be subject
           to system policy or approval by the end-user. xdg-mime query can
           be used to verify whether an application is the actual default
           handler for a specific file type.

           The default option is for use inside a desktop session only. It is
           not recommended to use xdg-mime default as root.

   install
           Adds the file type descriptions provided in mimetypes-file to the
           desktop environment. mimetypes-file must be a XML file that
           follows the freedesktop.org Shared MIME-info Database
           specification and that has a mime-info element as its document
           root. For each new file type one or more icons with name
           type-subtype must be installed with the xdg-icon-resource command
           in the mimetypes context. For example the filetype
           application/vnd.oasis.opendocument.text requires an icon named
           application-vnd.oasis.opendocument.text to be installed (unless
           the file type recommends another icon name).

   uninstall
           Removes the file type descriptions provided in mimetypes-file and
           previously added with xdg-mime install from the desktop
           environment. mimetypes-file must be a XML file that follows the
           freedesktop.org Shared MIME-info Database specification and that
           has a mime-info element as its document root.

Options

   --mode mode

           mode can be user or system. In user mode the file is (un)installed
           for the current user only. In system mode the file is
           (un)installed for all users on the system. Usually only root is
           allowed to install in system mode.

           The default is to use system mode when called by root and to use
           user mode when called by a non-root user.

   --novendor

           Normally, xdg-mime checks to ensure that the mimetypes-file to be
           installed has a proper vendor prefix. This option can be used to
           disable that check.

           A vendor prefix consists of alpha characters ([a-zA-Z]) and is
           terminated with a dash ("-"). Companies and organizations are
           encouraged to use a word or phrase, preferably the organizations
           name, for which they hold a trademark as their vendor prefix. The
           purpose of the vendor prefix is to prevent name conflicts.

   --help
           Show command synopsis.

   --manual
           Show this manual page.

   --version
           Show the xdg-utils version information.

Environment Variables

   xdg-mime honours the following environment variables:

   XDG_UTILS_DEBUG_LEVEL
           Setting this environment variable to a non-zero numerical value
           makes xdg-mime do more verbose reporting on stderr. Setting a
           higher value increases the verbosity.

   XDG_UTILS_INSTALL_MODE
           This environment variable can be used by the user or administrator
           to override the installation mode. Valid values are user and
           system.

Exit Codes

   An exit code of 0 indicates success while a non-zero exit code indicates
   failure. The following failure codes can be returned:

   1
           Error in command line syntax.

   2
           One of the files passed on the command line did not exist.

   3
           A required tool could not be found.

   4
           The action failed.

   5
           No permission to read one of the files passed on the command line.

See Also

   xdg-icon-resource(1), xdg-desktop-menu(1), Shared MIME database
   specification, MIME applications associations specification

Examples

 xdg-mime query filetype /tmp/foobar.png

   Prints the MIME type of the file /tmp/foobar.png, in this case image/png

 xdg-mime query default image/png

   Prints the .desktop filename of the application which is registered to
   open PNG files.

 xdg-mime install shinythings-shiny.xml

   Adds a file type description for "shiny"-files. "shinythings-" is used as
   the vendor prefix. The file type description could look as follows.

 shinythings-shiny.xml:

 <?xml version="1.0"?>
 <mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>
   <mime-type type="text/x-shiny">
     <comment>Shiny new file type</comment>
     <glob pattern="*.shiny"/>
     <glob pattern="*.shi"/>
   </mime-type>
 </mime-info>

   An icon for this new file type must also be installed, for example with:

 xdg-icon-resource install --context mimetypes --size 64 shiny-file-icon.png text-x-shiny
_MANUALPAGE
}

usage()
{
cat << _USAGE
   xdg-mime - command line tool for querying information about file type
   handling and adding descriptions for new file types

Synopsis

   xdg-mime query { filetype FILE | default mimetype }

   xdg-mime default application mimetype(s)

   xdg-mime install [--mode mode] [--novendor] mimetypes-file

   xdg-mime uninstall [--mode mode] mimetypes-file

   xdg-mime { --help | --manual | --version }

_USAGE
}

#@xdg-utils-common@

#----------------------------------------------------------------------------
#   Common utility functions included in all XDG wrapper scripts
#----------------------------------------------------------------------------

#shellcheck shell=sh

DEBUG()
{
  [ -z "${XDG_UTILS_DEBUG_LEVEL}" ] && return 0;
  [ "${XDG_UTILS_DEBUG_LEVEL}" -lt "$1" ] && return 0;
  shift
  echo "$@" >&2
}

# This handles backslashes but not quote marks.
first_word()
{
    # shellcheck disable=SC2162 # No -r is intended here
    read first rest
    echo "$first"
}

#-------------------------------------------------------------
# map a binary to a .desktop file
binary_to_desktop_file()
{
    search="${XDG_DATA_HOME:-$HOME/.local/share}:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}"
    binary="$(command -v "$1")"
    binary="$(readlink -f "$binary")"
    base="$(basename "$binary")"
    IFS=:
    for dir in $search; do
        unset IFS
        [ "$dir" ] || continue
        [ -d "$dir/applications" ] || [ -d "$dir/applnk" ] || continue
        for file in "$dir"/applications/*.desktop "$dir"/applications/*/*.desktop "$dir"/applnk/*.desktop "$dir"/applnk/*/*.desktop; do
            [ -r "$file" ] || continue
            # Check to make sure it's worth the processing.
            grep -q "^Exec.*$base" "$file" || continue
            # Make sure it's a visible desktop file (e.g. not "preferred-web-browser.desktop").
            grep -Eq "^(NoDisplay|Hidden)=true" "$file" && continue
            command="$(grep -E "^Exec(\[[^]=]*])?=" "$file" | cut -d= -f 2- | first_word)"
            command="$(command -v "$command")"
            if [ x"$(readlink -f "$command")" = x"$binary" ]; then
                # Fix any double slashes that got added path composition
                echo "$file" | tr -s /
                return
            fi
        done
    done
}

#-------------------------------------------------------------
# map a .desktop file to a binary
desktop_file_to_binary()
{
    search="${XDG_DATA_HOME:-$HOME/.local/share}:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}"
    desktop="$(basename "$1")"
    IFS=:
    for dir in $search; do
        unset IFS
        [ "$dir" ] && [ -d "$dir/applications" ] || [ -d "$dir/applnk" ] || continue
        # Check if desktop file contains -
        if [ "${desktop#*-}" != "$desktop" ]; then
            vendor=${desktop%-*}
            app=${desktop#*-}
            if [ -r "$dir/applications/$vendor/$app" ]; then
                file_path="$dir/applications/$vendor/$app"
            elif [ -r "$dir/applnk/$vendor/$app" ]; then
                file_path="$dir/applnk/$vendor/$app"
            fi
        fi
        if test -z "$file_path" ; then
            for indir in "$dir"/applications/ "$dir"/applications/*/ "$dir"/applnk/ "$dir"/applnk/*/; do
                file="$indir/$desktop"
                if [ -r "$file" ]; then
                    file_path=$file
                    break
                fi
            done
        fi
        if [ -r "$file_path" ]; then
            # Remove any arguments (%F, %f, %U, %u, etc.).
            command="$(grep -E "^Exec(\[[^]=]*])?=" "$file_path" | cut -d= -f 2- | first_word)"
            command="$(command -v "$command")"
            readlink -f "$command"
            return
        fi
    done
}

#-------------------------------------------------------------
# Exit script on successfully completing the desired operation

# shellcheck disable=SC2120 # It is okay to call this without arguments
exit_success()
{
    if [ $# -gt 0 ]; then
        echo "$*"
        echo
    fi

    exit 0
}


#-----------------------------------------
# Exit script on malformed arguments, not enough arguments
# or missing required option.
# prints usage information

exit_failure_syntax()
{
    if [ $# -gt 0 ]; then
        echo "xdg-mime: $*" >&2
        echo "Try 'xdg-mime --help' for more information." >&2
    else
        usage
        echo "Use 'man xdg-mime' or 'xdg-mime --manual' for additional info."
    fi

    exit 1
}

#-------------------------------------------------------------
# Exit script on missing file specified on command line

exit_failure_file_missing()
{
    if [ $# -gt 0 ]; then
        echo "xdg-mime: $*" >&2
    fi

    exit 2
}

#-------------------------------------------------------------
# Exit script on failure to locate necessary tool applications

exit_failure_operation_impossible()
{
    if [ $# -gt 0 ]; then
        echo "xdg-mime: $*" >&2
    fi

    exit 3
}

#-------------------------------------------------------------
# Exit script on failure returned by a tool application

exit_failure_operation_failed()
{
    if [ $# -gt 0 ]; then
        echo "xdg-mime: $*" >&2
    fi

    exit 4
}

#------------------------------------------------------------
# Exit script on insufficient permission to read a specified file

exit_failure_file_permission_read()
{
    if [ $# -gt 0 ]; then
        echo "xdg-mime: $*" >&2
    fi

    exit 5
}

#------------------------------------------------------------
# Exit script on insufficient permission to write a specified file

exit_failure_file_permission_write()
{
    if [ $# -gt 0 ]; then
        echo "xdg-mime: $*" >&2
    fi

    exit 6
}

check_input_file()
{
    if [ ! -e "$1" ]; then
        exit_failure_file_missing "file '$1' does not exist"
    fi
    if [ ! -r "$1" ]; then
        exit_failure_file_permission_read "no permission to read file '$1'"
    fi
}

check_vendor_prefix()
{
    file_label="$2"
    [ -n "$file_label" ] || file_label="filename"
    file="$(basename "$1")"
    case "$file" in
       [[:alpha:]]*-*)
         return
         ;;
    esac

    echo "xdg-mime: $file_label '$file' does not have a proper vendor prefix" >&2
    echo 'A vendor prefix consists of alpha characters ([a-zA-Z]) and is terminated' >&2
    echo 'with a dash ("-"). An example '"$file_label"' is '"'example-$file'" >&2
    echo "Use --novendor to override or 'xdg-mime --manual' for additional info." >&2
    exit 1
}

check_output_file()
{
    # if the file exists, check if it is writeable
    # if it does not exists, check if we are allowed to write on the directory
    if [ -e "$1" ]; then
        if [ ! -w "$1" ]; then
            exit_failure_file_permission_write "no permission to write to file '$1'"
        fi
    else
        DIR="$(dirname "$1")"
        if [ ! -w "$DIR" ] || [ ! -x "$DIR" ]; then
            exit_failure_file_permission_write "no permission to create file '$1'"
        fi
    fi
}

#----------------------------------------
# Checks for shared commands, e.g. --help

check_common_commands()
{
    while [ $# -gt 0 ] ; do
        parm="$1"
        shift

        case "$parm" in
            --help)
            usage
            echo "Use 'man xdg-mime' or 'xdg-mime --manual' for additional info."
            exit_success
            ;;

            --manual)
            manualpage
            exit_success
            ;;

            --version)
            echo "xdg-mime 1.2.0-beta1"
            exit_success
            ;;
        esac
    done
}

check_common_commands "$@"

[ -z "${XDG_UTILS_DEBUG_LEVEL}" ] && unset XDG_UTILS_DEBUG_LEVEL;
# shellcheck disable=SC2034
if [ "${XDG_UTILS_DEBUG_LEVEL-0}" -lt 1 ]; then
    # Be silent
    xdg_redirect_output=" > /dev/null 2> /dev/null"
else
    # All output to stderr
    xdg_redirect_output=" >&2"
fi

#--------------------------------------
# Checks for known desktop environments
# set variable DE to the desktop environments name, lowercase

detectDE()
{
    # see https://bugs.freedesktop.org/show_bug.cgi?id=34164
    unset GREP_OPTIONS

    if [ -n "${XDG_CURRENT_DESKTOP}" ]; then
      case "${XDG_CURRENT_DESKTOP}" in
         # only recently added to menu-spec, pre-spec X- still in use
         Cinnamon|X-Cinnamon)
           DE=cinnamon;
           ;;
         ENLIGHTENMENT)
           DE=enlightenment;
           ;;
         # GNOME, GNOME-Classic:GNOME, or GNOME-Flashback:GNOME
         GNOME*)
           DE=gnome;
           ;;
         KDE)
           DE=kde;
           ;;
         DEEPIN|Deepin|deepin)
           DE=deepin;
           ;;
         LXDE)
           DE=lxde;
           ;;
         LXQt)
           DE=lxqt;
           ;;
         MATE)
           DE=mate;
           ;;
         XFCE)
           DE=xfce
           ;;
         X-Generic)
           DE=generic
           ;;
      esac
    fi

    # shellcheck disable=SC2153
    if [ -z "$DE" ]; then
      # classic fallbacks
      if [ -n "$KDE_FULL_SESSION" ]; then DE=kde;
      elif [ -n "$GNOME_DESKTOP_SESSION_ID" ]; then DE=gnome;
      elif [ -n "$MATE_DESKTOP_SESSION_ID" ]; then DE=mate;
      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;
      elif xprop -root _DT_SAVE_MODE 2> /dev/null | grep ' = \"xfce4\"$' >/dev/null 2>&1; then DE=xfce;
      elif xprop -root 2> /dev/null | grep -i '^xfce_desktop_window' >/dev/null 2>&1; then DE=xfce
      elif echo "$DESKTOP" | grep -q '^Enlightenment'; then DE=enlightenment;
      elif [ -n "$LXQT_SESSION_CONFIG" ]; then DE=lxqt;
      fi
    fi

    if [ -z "$DE" ]; then
      # fallback to checking $DESKTOP_SESSION
      case "$DESKTOP_SESSION" in
         gnome)
           DE=gnome;
           ;;
         LXDE|Lubuntu)
           DE=lxde;
           ;;
         MATE)
           DE=mate;
           ;;
         xfce|xfce4|'Xfce Session')
           DE=xfce;
           ;;
      esac
    fi

    if [ -z "$DE" ]; then
      # fallback to uname output for other platforms
      case "$(uname 2>/dev/null)" in
        CYGWIN*)
          DE=cygwin;
          ;;
        Darwin)
          DE=darwin;
          ;;
        Linux)
          grep -q microsoft /proc/version > /dev/null 2>&1 && \
          command -v explorer.exe > /dev/null 2>&1 && \
          DE=wsl;
          ;;
      esac
    fi

    if [ x"$DE" = x"gnome" ]; then
      # gnome-default-applications-properties is only available in GNOME 2.x
      # but not in GNOME 3.x
      command -v gnome-default-applications-properties > /dev/null || DE="gnome3"
    fi

    if [ -f "$XDG_RUNTIME_DIR/flatpak-info" ]; then
      DE="flatpak"
    fi
}

#----------------------------------------------------------------------------
# kfmclient exec/openURL can give bogus exit value in KDE <= 3.5.4
# It also always returns 1 in KDE 3.4 and earlier
# Simply return 0 in such case

kfmclient_fix_exit_code()
{
    version="$(LC_ALL=C.UTF-8 kde-config --version 2>/dev/null | grep '^KDE')"
    major="$(echo "$version" | sed 's/KDE.*: \([0-9]\).*/\1/')"
    minor="$(echo "$version" | sed 's/KDE.*: [0-9]*\.\([0-9]\).*/\1/')"
    release="$(echo "$version" | sed 's/KDE.*: [0-9]*\.[0-9]*\.\([0-9]\).*/\1/')"
    test "$major" -gt 3 && return "$1"
    test "$minor" -gt 5 && return "$1"
    test "$release" -gt 4 && return "$1"
    return 0
}

#----------------------------------------------------------------------------
# Returns true if there is a graphical display attached.

has_display()
{
    if [ -n "$DISPLAY" ] || [ -n "$WAYLAND_DISPLAY" ]; then
        return 0
    else
        return 1
    fi
}

update_kde_cache()
{
	DEBUG 1 "Running kbuildsycoca"
	if [ "${KDE_SESSION_VERSION:-0}" -gt 3 ] ; then
		eval 'kbuildsycoca${KDE_SESSION_VERSION}'$xdg_redirect_output
	else
		eval 'kbuildsycoca'$xdg_redirect_output
	fi
}

update_mime_database()
{
   if [ "$mode" = user ] && has_display; then
      detectDE
      if [ x"$DE" = x"kde" ] ; then
         update_kde_cache
      fi
   fi
   old_ifs="$IFS"
   IFS=:
   for x in $PATH /opt/gnome/bin ; do
	  IFS="$old_ifs"
      if [ -x "$x/update-mime-database" ] ; then
         DEBUG 1 "Running $x/update-mime-database $1"
         eval '$x/update-mime-database $1'$xdg_redirect_output
         return
      fi
   done
}

info_kde()
{
    if [ -n "${KDE_SESSION_VERSION}" ]; then
      case "${KDE_SESSION_VERSION}" in
        4)
          DEBUG 1 "Running kmimetypefinder \"$1\""
          kmimetypefinder "$1" 2>/dev/null | head -n 1
        ;;
        5)
          DEBUG 1 "Running kmimetypefinder5 \"$1\""
          kmimetypefinder5 "$1" 2>/dev/null | head -n 1
        ;;
        6)
          DEBUG 1 "Running kmimetypefinder \"$1\""
          kmimetypefinder "$1" 2>/dev/null | head -n 1
        ;;
      esac
    else
        DEBUG 1 "Running kfile \"$1\""
        kfile "$1" 2> /dev/null | head -n 1 | cut -d "(" -f 2 | cut -d ")" -f 1
    fi

    if [ $? -eq 0 ]; then
        exit_success
    else
        exit_failure_operation_failed
    fi
}

info_gnome()
{
    if gio help info 2>/dev/null 1>&2; then
        DEBUG 1 "Running gio info \"$1\""
        gio info "$1" 2> /dev/null | grep standard::content-type | cut -d' ' -f4
    elif gvfs-info --help 2>/dev/null 1>&2; then
        DEBUG 1 "Running gvfs-info \"$1\""
        gvfs-info "$1" 2> /dev/null | grep standard::content-type | cut -d' ' -f4
    elif gnomevfs-info --help 2>/dev/null 1>&2; then
        DEBUG 1 "Running gnomevfs-info \"$1\""
        gnomevfs-info --slow-mime "$1" 2> /dev/null | grep "^MIME" | cut -d ":" -f 2 | sed s/"^ "//
    else
        exit_failure_operation_impossible "no method available for querying MIME type of '$filename'"
    fi

    if [ $? -eq 0 ]; then
        exit_success
    else
        exit_failure_operation_failed
    fi
}

info_lxqt()
{
    if qtxdg-mat mimetype --help 2>/dev/null 1>&2; then
        qtxdg-mat mimetype "$1" 2>/dev/null
    else
        exit_failure_operation_impossible "no method available for querying MIME type of '$filename'"
    fi

    if [ $? -eq 0 ]; then
        exit_success
    else
        exit_failure_operation_failed
    fi
}

info_generic()
{
    if mimetype --version >/dev/null 2>&1; then
        DEBUG 1 "Running mimetype --brief --dereference \"$1\""
        mimetype --brief --dereference "$1"
    else
        DEBUG 1 "Running file --brief --dereference --mime-type \"$1\""
        /usr/bin/file --brief --dereference --mime-type "$1" 2> /dev/null
    fi

    if [ $? -eq 0 ]; then
        exit_success
    else
        exit_failure_operation_failed
    fi
}

make_default_kde()
{
    # $1 is vendor-name.desktop
    # $2 is mime/type
    #
    # On KDE 3, add to $KDE_CONFIG_PATH/profilerc:
    # [$2 - 1]
    # Application=$1
    #
    # Remove all [$2 - *] sections, or even better,
    # renumber [$2 - *] sections and remove duplicate
    #
    # On KDE 4, add $2=$1 to $XDG_DATA_APPS/mimeapps.list
    #
    # Example file:
    #
    # [Added Associations]
    # text/plain=kde4-kate.desktop;kde4-kwrite.desktop;
    #
    # [Removed Associations]
    # text/plain=gnome-gedit.desktop;gnu-emacs.desktop;
    vendor="$1"
    mimetype="$2"
    if [ "${KDE_SESSION_VERSION:-0}" -gt 4 ] ; then
        default_dir="$(qtpaths --writable-path ConfigLocation)"
        default_file="$default_dir/mimeapps.list"
    elif [ x"$KDE_SESSION_VERSION" = x"4" ]; then
        default_dir="$(kde4-config --path xdgdata-apps 2> /dev/null | cut -d ':' -f 1)"
        default_file="$default_dir/mimeapps.list"
    else
        default_dir="$(kde-config --path config 2> /dev/null | cut -d ':' -f 1)"
        default_file="$default_dir/profilerc"
    fi
    if [ -z "$default_dir" ]; then
        DEBUG 2 "make_default_kde: No kde runtime detected"
        return
    fi
    DEBUG 2 "make_default_kde $vendor $mimetype"
    DEBUG 1 "Updating $default_file"
    mkdir -p "$default_dir"
    [ -f "$default_file" ] || touch "$default_file"
    if [ "${KDE_SESSION_VERSION:-0}" -gt 3 ]; then
        [ -f "$default_file" ] || touch "$default_file"
        awk -v application="$vendor" -v mimetype="$mimetype" '
    BEGIN {
        prefix=mimetype "="
        associations=0
        found=0
        blanks=0
    }
    {
        suppress=0
        if (index($0, "[Added Associations]") == 1) {
            associations=1
        } else if (index($0, "[") == 1) {
            if (associations && !found) {
                print prefix application
                found=1
            }
            associations=0
        } else if ($0 == "") {
            blanks++
            suppress=1
        } else if (associations && index($0, prefix) == 1) {
            value=substr($0, length(prefix) + 1, length())
            split(value, apps, ";")
            value=application ";"
            count=0
            for (i in apps) {
              count++
            }
            for (i=0; i < count; i++) {
                if (apps[i] != application && apps[i] != "") {
                    value=value apps[i] ";"
                }
            }
            $0=prefix value
            found=1
        }
        if (!suppress) {
            while (blanks > 0) {
                print ""
                blanks--
            }
            print $0
        }
    }
    END {
        if (!found) {
            if (!associations) {
                print "[Added Associations]"
            }
            print prefix application
        }
        while (blanks > 0) {
            print ""
            blanks--
        }
    }
' "$default_file" > "${default_file}.new" && mv "${default_file}.new" "$default_file"
    else
        awk -v application="$vendor" -v mimetype="$mimetype" '
    BEGIN {
        header_start="[" mimetype " - "
        suppress=0
    }
    {
        if (index($0, header_start) == 1 )
            suppress=1
        else
            if (/^\[/) { suppress=0 }

        if (!suppress) {
            print $0
        }
    }
    END {
        print ""
        print "[" mimetype " - 1]"
        print "Application=" application
        print "AllowAsDefault=true"
        print "GenericServiceType=Application"
        print "Preference=1"
        print "ServiceType=" mimetype
    }
' "$default_file" > "${default_file}.new" && mv "${default_file}.new" "$default_file"
    fi
}

make_default_lxqt()
{
    # $1 is vendor-name.desktop
    # $2 is mime/type
    if qtxdg-mat defapp --help 2>/dev/null 1>&2; then
        qtxdg-mat defapp --set "$1" "$2" 1>/dev/null
    else
        exit_failure_operation_impossible "no method available for setting the default application for MIME type(s) of '$mimetype'"
    fi

    if [ $? -eq 0 ]; then
        exit_success
    else
        exit_failure_operation_failed
    fi
}

make_default_generic()
{
    # $1 is vendor-name.desktop
    # $2 is mime/type
    # Add $2=$1 to XDG_CONFIG_HOME/mimeapps.list
    #shellcheck disable=SC2153 # not misspelled
    xdg_config_home="$XDG_CONFIG_HOME"
    [ -n "$xdg_config_home" ] || xdg_config_home="$HOME/.config"
    default_file="$xdg_config_home/mimeapps.list"
    if [ -L "$default_file" ]; then
        out_file=$(readlink -f "$default_file")
    else
        out_file="$default_file"
    fi
    DEBUG 2 "make_default_generic $1 $2"
    DEBUG 1 "Updating $out_file"
    [ -f "$out_file" ] || touch "$out_file"
    awk -v mimetype="$2" -v application="$1" '
    BEGIN {
        prefix=mimetype "="
        indefault=0
        added=0
        blanks=0
        found=0
    }
    {
        suppress=0
        if (index($0, "[Default Applications]") == 1) {
            indefault=1
            found=1
        } else if (index($0, "[") == 1) {
            if (!added && indefault) {
                print prefix application
                added=1
            }
            indefault=0
        } else if ($0 == "") {
            suppress=1
            blanks++
        } else if (indefault && !added && index($0, prefix) == 1) {
                $0=prefix application
                added=1
        }
        if (!suppress) {
            while (blanks > 0) {
                print ""
                blanks--
            }
            print $0
        }
    }
    END {
        if (!added) {
            if (!found) {
                print ""
                print "[Default Applications]"
            }
            print prefix application
        }
        while (blanks > 0) {
            print ""
            blanks--
        }
    }
' "$out_file" > "$out_file.new" && mv "$out_file.new" "$out_file"
}

search_desktop_file()
{
    local MIME dir
    MIME="$1"
    dir="$2"

    grep -l "$MIME;" "$dir/"*.desktop 2>/dev/null

    for f in "$dir/"*/; do
      [ -d "$f" ] && search_desktop_file "$MIME" "$f"
    done
}

defapp_fallback()
{
    MIME="$1"

    xdg_user_dir="$XDG_DATA_HOME"
    [ -n "$xdg_user_dir" ] || xdg_user_dir="$HOME/.local/share"

    xdg_system_dirs="$XDG_DATA_DIRS"
    [ -n "$xdg_system_dirs" ] || xdg_system_dirs=/usr/local/share/:/usr/share/

    preference=-1
    desktop_file=""
    old_ifs="$IFS"
    IFS=:
    for d in $xdg_user_dir $xdg_system_dirs ; do
	   IFS="$old_ifs"
       for x in $(search_desktop_file "$MIME" "$d/applications") ; do
          pref="0$(awk -F"=" '/InitialPreference=/ {print($2)}' "$x")"
          DEBUG 2 " Checking $x"

          if [ "$pref" -gt "$preference" ]; then
             DEBUG 2 "   Select $x [ $preference => $pref ]"
             preference=$pref
             desktop_file=$x
          fi
       done
    done

    if [ -n "$desktop_file" ] ; then
       basename "$desktop_file"
       exit_success
    fi
}

check_mimeapps_list()
{
	local mimetype dir desktop oldifs
    mimetype="$1"
    dir="$2"
    oldifs="$IFS"

    IFS=:
    for desktop in $XDG_CURRENT_DESKTOP ''; do
        IFS="$oldifs"
        local prefix mimeapps_list result
        if [ -n "$desktop" ]; then
            prefix="$(echo "$desktop-" | tr '[:upper:]' '[:lower:]')"
        else
            prefix=""
        fi
        mimeapps_list="$dir/${prefix}mimeapps.list"
        if [ -f "$mimeapps_list" ] ; then
            DEBUG 2 "Checking $mimeapps_list"
            result="$(awk -v mimetype="$mimetype" '
    BEGIN {
        prefix=mimetype "="
        indefault=0
        found=0
    }
    {
        if (index($0, "[Default Applications]") == 1) {
            indefault=1
        } else if (index($0, "[") == 1) {
            indefault=0
        } else if (!found && indefault && index($0, prefix) == 1) {
            print substr($0, length(prefix) +1, length())
            found=1
        }
    }
' "$mimeapps_list")"
            if [ -n "$result" ]; then
                # $result could be a ; separated list of .desktop files
                # use the first on the system
                IFS=\;
                for app in $result; do
                    IFS="$oldifs"
                    exists=$(desktop_file_to_binary "$app")
                    if [ -n "$exists" ]; then
                        echo "$app"
                        exit_success
                    fi
                done
            fi
        fi
    done
}

defapp_generic()
{
    MIME="$1"
    xdg_config_home="$XDG_CONFIG_HOME"
    [ -n "$xdg_config_home" ] || xdg_config_home="$HOME/.config"
    #shellcheck disable=SC2153 # not misspelled
    xdg_config_dirs="$XDG_CONFIG_DIRS"
    [ -n "$xdg_config_dirs" ] || xdg_config_dirs="/etc/xdg"
    xdg_user_dir="$XDG_DATA_HOME"
    [ -n "$xdg_user_dir" ] || xdg_user_dir="$HOME/.local/share"
    xdg_system_dirs="$XDG_DATA_DIRS"
    [ -n "$xdg_system_dirs" ] || xdg_system_dirs=/usr/local/share/:/usr/share/

    local oldifs dir

    oldifs="$IFS"
    IFS=:
    for dir in $xdg_config_home $xdg_config_dirs; do
        IFS="$oldifs"
        check_mimeapps_list "$MIME" "$dir"
    done

    IFS=:
    for dir in $xdg_user_dir $xdg_system_dirs; do
        IFS="$oldifs"
        check_mimeapps_list "$MIME" "$dir/applications"
    done

	IFS=:
    for x in $xdg_user_dir $xdg_system_dirs ; do
       IFS="$oldifs"
       for prefix in "$XDG_MENU_PREFIX" ""; do
          DEBUG 2 "Checking $x/applications/${prefix}defaults.list and $x/applications/${prefix}mimeinfo.cache"
          trader_result="$(
          	grep "$MIME=" "$x/applications/${prefix}defaults.list" "$x/applications/${prefix}mimeinfo.cache" 2> /dev/null |
          	head -n 1 | cut -d '=' -f 2 | cut -d ';' -f 1
          )"
          if [ -n "$trader_result" ] ; then
              echo "$trader_result"
              exit_success
          fi
       done
    done

    defapp_fallback "$MIME"
    exit_success
}

defapp_kde()
{
    MIME="$1"

    if [ -n "${KDE_SESSION_VERSION}" ]; then
      case "${KDE_SESSION_VERSION}" in
        4)
          KTRADER="$(command -v ktraderclient)" # FIXME: Plasma 6?
        ;;
        5)
          KTRADER="$(command -v ktraderclient${KDE_SESSION_VERSION})"
        ;;
      esac
    else
        KTRADER="$(command -v ktradertest)"
    fi
    if [ -n "$KTRADER" ] ; then
        DEBUG 1 "Running KDE trader query \"$MIME\" mimetype and \"Application\" servicetype"
        trader_result="$($KTRADER --mimetype "$MIME" --servicetype Application 2>/dev/null \
            | grep -E "^DesktopEntryPath : |\.desktop$" | head -n1 | sed "s/^DesktopEntryPath : '\(.*\.desktop\)'\$/\1/")"
        if [ -n "$trader_result" ] ; then
            basename "$trader_result"
            exit_success
        else
            exit_failure_operation_failed
        fi
    else
        defapp_generic "$1"
    fi
}

defapp_lxqt()
{
    if qtxdg-mat defapp --help 2>/dev/null 1>&2; then
        qtxdg-mat defapp "$1"
    else
        exit_failure_operation_impossible "no method available for querying the default application for MIME type of '$mimetype'"
    fi

    if [ $? -eq 0 ]; then
        exit_success
    else
        exit_failure_operation_failed
    fi
}

[ x"$1" != x"" ] || exit_failure_syntax

mode=
action=
filename=
mimetype=

case $1 in
  install)
    action=install
    ;;

  uninstall)
    action=uninstall
    ;;

  query)
    shift

    if [ -z "$1" ] ; then
        exit_failure_syntax "query type argument missing"
    fi

    case $1 in
      filetype)
        action=info

        filename="$2"
        if [ -z "$filename" ] ; then
            exit_failure_syntax "FILE argument missing"
        fi
        case $filename in
          -*)
            exit_failure_syntax "unexpected option '$filename'"
            ;;
        esac
        check_input_file "$filename"
        filename="$(readlink -f -- "$filename")"
        ;;

      default)
        action=defapp
        mimetype="$2"
        if [ -z "$mimetype" ] ; then
            exit_failure_syntax "mimetype argument missing"
        fi
        case $mimetype in
          -*)
            exit_failure_syntax "unexpected option '$mimetype'"
            ;;

          */*)
            # Ok
            ;;

          *)
            exit_failure_syntax "mimetype '$mimetype' is not in the form 'minor/major'"
            ;;
        esac
        ;;

      *)
      exit_failure_syntax "unknown query type '$1'"
      ;;
    esac
    ;;

  default)
    action=makedefault
    shift

    if [ -z "$1" ] ; then
        exit_failure_syntax "application argument missing"
    fi
    case $1 in
      -*)
        exit_failure_syntax "unexpected option '$1'"
        ;;

      *.desktop)
        filename="$1"
        ;;

      *)
        exit_failure_syntax "malformed argument '$1', expected *.desktop"
        ;;
    esac
    ;;

  *)
  exit_failure_syntax "unknown command '$1'"
  ;;
esac

shift


if [ "$action" = "makedefault" ]; then
    if [ -z "$1" ] ; then
        exit_failure_syntax "mimetype argument missing"
    fi

    detectDE

    while [ $# -gt 0 ] ; do
        case $1 in
          -*)
            exit_failure_syntax "unexpected option '$1'"
            ;;
        esac
        mimetype="$1"
        shift

        case "$DE" in
            lxqt)
            make_default_lxqt "$filename" "$mimetype"
            ;;

            *)
            make_default_kde "$filename" "$mimetype"
            make_default_generic "$filename" "$mimetype"
            ;;
        esac
    done
    if [ "$DE" = "kde" ] ; then
        update_kde_cache
    fi
    exit_success
fi

if [ "$action" = "info" ]; then
    detectDE

    if [ x"$DE" = x"" ]; then
        if [ -x /usr/bin/file ]; then
            DE=generic
        fi
    fi

    case "$DE" in
        kde)
        info_kde "$filename"
        ;;

        gnome*|cinnamon|lxde|mate|xfce)
        info_gnome "$filename"
        ;;

        lxqt)
        info_lxqt "$filename"
        ;;

        *)
        info_generic "$filename"
        ;;
    esac
    exit_failure_operation_impossible "no method available for querying MIME type of '$filename'"
fi

if [ "$action" = "defapp" ]; then
    detectDE

    if [ "$DE" = "kde" ] && [ "$KDE_SESSION_VERSION" -lt "6" ]; then
        defapp_kde "$mimetype"
    fi

    case "$DE" in
        lxqt)
        defapp_lxqt "$mimetype"
        ;;

        *)
        defapp_generic "$mimetype"
        ;;
    esac
    exit_failure_operation_impossible "no method available for querying default application for '$mimetype'"
fi

vendor=true
while [ $# -gt 0 ] ; do
    parm="$1"
    shift

    case $parm in
      --mode)
        if [ -z "$1" ] ; then
            exit_failure_syntax "mode argument missing for --mode"
        fi
        case "$1" in
          user)
            mode="user"
            ;;

          system)
            mode="system"
            ;;

          *)
            exit_failure_syntax "unknown mode '$1'"
            ;;
        esac
        shift
        ;;

      --novendor)
        vendor=false
        ;;

      -*)
        exit_failure_syntax "unexpected option '$parm'"
        ;;

      *)
        if [ -n "$filename" ] ; then
            exit_failure_syntax "unexpected argument '$parm'"
        fi

        filename="$parm"
        check_input_file "$filename"
        ;;
    esac
done

if [ -z "$action" ] ; then
    exit_failure_syntax "command argument missing"
fi

if [ -n "$XDG_UTILS_INSTALL_MODE" ] ; then
    if [ "$XDG_UTILS_INSTALL_MODE" = "system" ] ; then
        mode="system"
    elif [ "$XDG_UTILS_INSTALL_MODE" = "user" ] ; then
        mode="user"
    fi
fi

if [ -z "$mode" ] ; then
    if [ "$(id -u)" -eq 0 ] ; then
        mode="system"
    else
        mode="user"
    fi
fi

if [ -z "$filename" ] ; then
    exit_failure_syntax "mimetypes-file argument missing"
fi

if [ "$vendor" =  "true" ] && [ "$action" = "install" ] ; then
    check_vendor_prefix "$filename"
fi

xdg_base_dir=
xdg_dir_name=mime/packages/

xdg_user_dir="$XDG_DATA_HOME"
[ -n "$xdg_user_dir" ] || xdg_user_dir="$HOME/.local/share"
[ x"$mode" = x"user" ] && xdg_base_dir="$xdg_user_dir/mime"
xdg_user_dir="$xdg_user_dir/$xdg_dir_name"

xdg_system_dirs="$XDG_DATA_DIRS"
[ -n "$xdg_system_dirs" ] || xdg_system_dirs=/usr/local/share/:/usr/share/

old_ifs="$IFS"
IFS=:
for x in $xdg_system_dirs ; do
	IFS="$old_ifs"
    if [ -w "$x/$xdg_dir_name" ] ; then
        [ x"$mode" = x"system" ] && xdg_base_dir="$x/mime"
        xdg_global_dir="$x/$xdg_dir_name"
        break
    fi
done
[ -w "$xdg_global_dir" ] || xdg_global_dir=
DEBUG 3 "xdg_user_dir: $xdg_user_dir"
DEBUG 3 "xdg_global_dir: $xdg_global_dir"

# Find KDE3 mimelnk directory
kde_user_dir=
kde_global_dir=
kde_global_dirs="$("kde${KDE_SESSION_VERSION}-config" --path mime 2> /dev/null)"
DEBUG 3 "kde_global_dirs: $kde_global_dirs"
first=
old_ifs="$IFS"
IFS=:
for x in $kde_global_dirs ; do
	IFS="$old_ifs"
    if [ -z "$first" ] ; then
        first=false
        kde_user_dir="$x"
    elif [ -w "$x" ] ; then
        kde_global_dir="$x"
    fi
done
DEBUG 3 "kde_user_dir: $kde_user_dir"
DEBUG 3 "kde_global_dir: $kde_global_dir"

# TODO: Gnome legacy support
# See http://forums.fedoraforum.org/showthread.php?t=26875
gnome_user_dir="$HOME/.gnome/apps"
gnome_global_dir=/usr/share/gnome/apps
[ -w $gnome_global_dir ] || gnome_global_dir=
DEBUG 3 "gnome_user_dir: $gnome_user_dir"
DEBUG 3 "gnome_global_dir: $gnome_global_dir"

if [ x"$mode" = x"user" ] ; then
    xdg_dir="$xdg_user_dir"
    kde_dir="$kde_user_dir"
    gnome_dir="$gnome_user_dir"
    my_umask=077
else
    xdg_dir="$xdg_global_dir"
    kde_dir="$kde_global_dir"
    gnome_dir="$gnome_global_dir"
    my_umask=022
    if [ -z "${xdg_dir}${kde_dir}${gnome_dir}" ] ; then
        exit_failure_operation_impossible "No writable system mimetype directory found."
    fi
fi

# echo "[xdg|$xdg_user_dir|$xdg_global_dir]"
# echo "[kde|$kde_user_dir|$kde_global_dir]"
# echo "[gnome|$gnome_user_dir|$gnome_global_dir]"
# echo "[using|$xdg_dir|$kde_dir|$gnome_dir]"

basefile="$(basename "$filename")"
#[ -z $vendor ] || basefile="$vendor-$basefile"

mimetypes=
if [ -n "$kde_dir" ] ; then
    DEBUG 2 "KDE3 mimelnk directory found, extracting mimetypes from XML file"

    mimetypes="$(awk < "$filename" '
# Strip XML comments
BEGIN {
 suppress=0
}
{
 do
    if (suppress) {
       if (match($0,/-->/)) {
           $0=substr($0,RSTART+RLENGTH)
           suppress=0
       }
       else {
           break
       }
    }
    else {
       if (match($0,/<!--/)) {
           if (RSTART>1) print substr($0,0,RSTART)
           $0=substr($0,RSTART+RLENGTH)
           suppress=1
       }
       else {
           if ($0) print $0
           break
       }
    }
 while(1)
}
' | awk '
# List MIME types listed in <mime-type> tags
BEGIN {
  RS="<"
}
/^mime-info/, /^\/mime-info/ {
  if (match($0,/^mime-type/)) {
    if (match($0,/type="[^"]*/) || match($0,/type='"'"'[^'"'"']*/)) {
      print substr($0,RSTART+6,RLENGTH-6)
    }
  }
}')"
fi

DEBUG 1 "$action mimetype in $xdg_dir"

case $action in
    install)
        save_umask="$(umask)"
        umask "$my_umask"

        for x in $xdg_dir ; do
            mkdir -p "$x"
            eval 'cp $filename $x/$basefile'$xdg_redirect_output
        done

        if [ -n "$mimetypes" ] ; then
            # No quotes around $mimetypes
            for x in $mimetypes ; do
                DEBUG 1 "Installing $kde_dir/$x.desktop (KDE 3.x support)"
                mkdir -p "$(dirname "$kde_dir/$x.desktop")"
                awk < "$filename" '
# Strip XML comments
BEGIN {
 suppress=0
}
{
 do
    if (suppress) {
       if (match($0,/-->/)) {
           $0=substr($0,RSTART+RLENGTH)
           suppress=0
       }
       else {
           break
       }
    }
    else {
       if (match($0,/<!--/)) {
           if (RSTART>1) print substr($0,0,RSTART)
           $0=substr($0,RSTART+RLENGTH)
           suppress=1
       }
       else {
           if ($0) print $0
           break
       }
    }
 while(1)
}
' | awk > "$kde_dir/$x.desktop" '
# Extract mimetype $x from the XML file $filename
# Note that bash requires us to escape a single quote as '"'"'
BEGIN {
  the_type=ARGV[1]
  the_source=ARGV[2]
  ARGC=1
  RS="<"
  found=0
  glob_patterns=""
}
/^mime-info/, /^\/mime-info/ {
  if (match($0,/^mime-type/)) {
    if (match($0,/type="[^"]*/) || match($0,/type='"'"'[^'"'"']*/)) {
      if (substr($0,RSTART+6,RLENGTH-6) == the_type) {
        found=1
        print "[Desktop Entry]"
        print "# Installed by xdg-mime from " the_source
        print "Type=MimeType"
        print "MimeType=" the_type
        the_icon=the_type
        sub("/", "-", the_icon)
        print "Icon=" the_icon
      }
    }
  }
  else if (found) {
    if (match($0,/^\/mime-type/)) {
      if (glob_patterns)
         print "Patterns=" glob_patterns
      exit 0
    }

    if (match($0,/^sub-class-of/)) {
      if (match($0,/type="[^"]*/) || match($0,/type='"'"'[^'"'"']*/)) {
        print "X-KDE-IsAlso=" substr($0,RSTART+6,RLENGTH-6)
      }
      else {
        print "Error: '"'"'type'"'"' argument missing in " RS $0
        exit 1
      }
    }
    if (match($0,/^glob/)) {
      if (match($0,/pattern="[^"]*/) || match($0,/pattern='"'"'[^'"'"']*/)) {
        glob_patterns = glob_patterns substr($0,RSTART+9,RLENGTH-9) ";"
      }
      else {
        print "Error: '"'"'pattern'"'"' argument missing in " RS $0
        exit 1
      }
    }
    if (match($0,/^comment/)) {
      if (match($0,/xml:lang="[^"]*/) || match($0,/xml:lang='"'"'[^'"'"']*/)) {
        lang=substr($0,RSTART+10,RLENGTH-10)
      }
      else {
        lang=""
      }
      if (match($0,/>/)) {
        comment=substr($0,RSTART+1)
        sub("&lt;", "<", comment)
        sub("&gt;", ">", comment)
        sub("&amp;", "\\&", comment)
        if (lang)
           print "Comment[" lang "]=" comment
        else
           print "Comment=" comment
      }
    }
  }
}
END {
  if (!found) {
    print "Error: Mimetype '"'"'" the_type "'"'"' not found"
    exit 1
  }
}
' "$x" "$basefile"
                if [ "$?" = "1" ] ; then
                    grep -A 10 "^Error:" "$kde_dir/$x.desktop" >&2
                    rm "$kde_dir/$x.desktop"
                    exit 1
                fi
            done
        fi

        umask "$save_umask"
        ;;

    uninstall)
        for x in $xdg_dir ; do
            rm -f "$x/$basefile"
        done

        # No quotes around $mimetypes
        for x in $mimetypes ; do
            if grep '^# Installed by xdg-mime' "$kde_dir/$x.desktop" >/dev/null 2>&1; then
                DEBUG 1 "Removing $kde_dir/$x.desktop (KDE 3.x support)"
                rm -f "$kde_dir/$x.desktop"
            fi
        done
        ;;
esac

update_mime_database "$xdg_base_dir"

exit_success
