#!/bin/sh # # suse-update-mime-defaults - create default application ordering for MIME associations # # Copyright (C) 2023 Guido Berhoerster # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # TORTIOUS ACTION, ARISING OUT OF PERFORMANCE OF THIS SOFTWARE. # r= case $1 in DESTDIR=*) r="${1#DESTDIR=}" ;; esac # reset XDG_DATA_DIRS so it can be trusted unset XDG_DATA_DIRS if [ "${PROFILEREAD}" != "true" ]; then . "$r/etc/profile" fi export LC_ALL=C # ensure the cache directory structure is in order mkdir -m 755 -p "$r/var/cache/gio-2.0" for desktop in gnome xfce lxde pantheon budgie mate; do awk -vdesktop=${desktop} ' # print a warning to stderr function warn(msg, cmd) { cmd = "cat >&2" print msg | cmd close(cmd) } # print an error message and exit with the given exit status function err(status, msg) { warn(msg) exit_status = status exit exit_status } # delete all elements of an array function delete_array(arr, i) { for (i in arr) { delete arr[i] } } # find preferred combination of mimetype and category function find_preferred_category(mimetypes_categories, categories, mimetype, i) { for (i = 1; i in categories; i++) { if (mimetype SUBSEP categories[i] in mimetypes_categories) { return mimetypes_categories[mimetype,categories[i]] } } } # sort keys of an array by index (using the awk default comparison) function asorti2(src, dest, key, len) { len = 0 delete_array(dest) for (key in src) { dest[len++] = key } _qsorti(dest, 0, len - 1); } function array_swap(arr, i, j, tmp) { tmp = arr[i] arr[i] = arr[j] arr[j] = tmp } # Based on Bentley, J. L., 2000. Programming Pearls. 2nd ed. Reading, MA: # Addison-Wesley Professional. function _qsorti(dest, l, u, val, i, j) { if (l >= u) { return } array_swap(dest, l, l + int(rand() * (u - l))) val = dest[l] i = l j = u + 1 while (1) { do { i++ } while (i <= u && dest[i] < val) do { j-- } while (dest[j] > val) if (i > j) { break } array_swap(dest, i, j) } array_swap(dest, l, j) _qsorti(dest, l, j - 1) _qsorti(dest, j + 1, u) } BEGIN { desktop = desktop != "" ? desktop : "gnome" if (desktop == "gnome") { categories_list = "GNOME,GTK" } else if (desktop == "xfce") { categories_list = "XFCE,GTK" } else if (desktop == "lxde") { categories_list = "GTK" } else if (desktop == "pantheon") { categories_list = "GTK" } else if (desktop == "budgie") { categories_list = "GTK" } else if (desktop == "mate") { categories_list = "MATE,GTK" } split(categories_list, categories, /,/) root = ENVIRON["r"] defaults_conf = root "/etc/" desktop "_defaults.conf" # parse desktop defaults preferences lineno = 0 while ((getline < defaults_conf) > 0) { lineno++ if (NF == 0 || $1 ~ /^#/) { # skip comments and empty lines continue } else if (NF != 1) { err(1, "syntax error in " defaults_conf " line " lineno) } else if (split($1, arr, /=/) == 2) { # handle MIME type defaults mimetype_default_apps[arr[1]] = arr[2] } else if ($1 ~ /^!.+\.desktop$/) { # handle preferred default applications preferred_default_apps[substr($1, 2)] = substr($1, 2) } else if ($1 ~ /^.+\.desktop$/) { # handle regular default applications default_apps[$1] = $1 } else { err(1, "syntax error in " defaults_conf ", line " lineno) } } close(defaults_conf) # find all desktop files for (i = split("XDG_DATA_DIRS" in ENVIRON ? ENVIRON["XDG_DATA_DIRS"] : \ "/usr/local/share:/usr/share", xdg_data_dirs, /:/); i > 0; i--) { # XDG_DATA_DIRS is trusted here because it has been reset cmd = "find \"" root xdg_data_dirs[i] "/applications/\" -name \"*.desktop\" " \ "2>/dev/null" while ((cmd | getline desktopfile) > 0) { l = split(desktopfile, arr, "/") desktopfiles[arr[l]] = desktopfile } close(cmd) } # process all desktop files in alphabetical order asorti2(desktopfiles, desktopfiles_keys) for (i = 0; i in desktopfiles_keys; i++) { # parse a desktop file desktopfile = desktopfiles_keys[i] delete_array(desktopfile_mimetypes) delete_array(desktopfile_categories) lineno = 0 in_desktop_entry = 0 while ((getline < desktopfiles[desktopfile]) > 0) { lineno++ if (NF == 0 || $1 ~ /^#/) { # skip comments and empty lines continue } else if (in_desktop_entry == 0 && \ $0 ~ /^\[Desktop Entry\][\t ]*$/) { # desktop entry group in_desktop_entry = 1 } else if (in_desktop_entry == 1) { if (in_desktop_entry == 1 && $1 ~ /^\[/) { # quit when a different group starts, "Desktop Entry" must # come first break } else if ($0 ~ /^MimeType *=/ && split($0, arr, /=/) == 2) { # handle MimeTypes gsub(/(^ *|; *$)/, "", arr[2]) split(arr[2], desktopfile_mimetypes, /;/) } else if ($0 ~ /^Categories *=/ && split($0, arr, /=/) == 2) { # handle Categories gsub(/(^ *|; *$)/, "", arr[2]) split(arr[2], desktopfile_categories, /;/) } else if ($0 ~ /^[A-Za-z0-9\[\]@_-]+ *=/) { # skip other keys continue } } else { warn("syntax error in " desktopfiles[desktopfile] ", line " \ lineno) break } } close(desktopfiles[desktopfile]) # store the results for (j = 1; j in desktopfile_mimetypes; j++) { if (desktopfile_mimetypes[j] in mimetype_default_apps && \ mimetype_default_apps[desktopfile_mimetypes[j]] == \ desktopfile) { mimetype_defaults[desktopfile_mimetypes[j]] = desktopfile } if (desktopfile in preferred_default_apps) { preferred_defaults[desktopfile_mimetypes[j]] = desktopfile } if (desktopfile in default_apps) { defaults[desktopfile_mimetypes[j]] = desktopfile } for (k = 1; k in desktopfile_categories; k++) { mimetypes_categories[desktopfile_mimetypes[j], \ desktopfile_categories[k]] = desktopfile } generic_mimetypes[desktopfile_mimetypes[j]] = desktopfile } } # determine default mimetype handlers for (mimetype in generic_mimetypes) { if (mimetype in mimetype_defaults) { defaults_list[mimetype] = mimetype_defaults[mimetype] } else if (mimetype in preferred_defaults) { defaults_list[mimetype] = preferred_defaults[mimetype] } else if (mimetype in defaults) { defaults_list[mimetype] = defaults[mimetype] } else if ((desktopfile = \ find_preferred_category(mimetypes_categories, categories, \ mimetype)) != "") { defaults_list[mimetype] = desktopfile } else if (mimetype in generic_mimetypes) { defaults_list[mimetype] = generic_mimetypes[mimetype] } } print "# generated by suse-update-mime-defaults from " defaults_conf print "[Default Applications]" asorti2(defaults_list, defaults_list_keys) for (i = 0; i in defaults_list_keys; i++) { mimetype = defaults_list_keys[i] printf("%s=%s\n", mimetype, defaults_list[mimetype]) } } ' >"$r/var/cache/gio-2.0/${desktop}-mimeapps.list" done