aboutsummaryrefslogtreecommitdiffstats
path: root/lisp
diff options
context:
space:
mode:
authorMark Oteiza2017-09-24 22:28:51 -0400
committerMark Oteiza2017-09-26 17:48:00 -0400
commita17f30d7cdfa3983f8c97e474015777ec051de35 (patch)
tree9c56ff4e146b9f1c071cb31541e00a1b3fa2e215 /lisp
parent52a1da03b226b8686856259ac5d9474a8462322a (diff)
downloademacs-a17f30d7cdfa3983f8c97e474015777ec051de35.tar.gz
emacs-a17f30d7cdfa3983f8c97e474015777ec051de35.zip
Add MIME apps spec utilities
Facilitates finding associations between MIME types and desktop files that report an association with that type. Combined with mailcap.el's MIME facilities, it should be easy to use desktop files. * lisp/xdg.el (xdg-mime-table): New variable. (xdg-mime-apps-files, xdg-mime-collect-associations, xdg-mime-apps): New functions. * test/data/xdg/mimeapps.list: New file. * test/data/xdg/mimeinfo.cache: New file. * test/lisp/xdg-tests.el (xdg-mime-associations): New test.
Diffstat (limited to 'lisp')
-rw-r--r--lisp/xdg.el103
1 files changed, 103 insertions, 0 deletions
diff --git a/lisp/xdg.el b/lisp/xdg.el
index 76106f42586..4250faaeb4b 100644
--- a/lisp/xdg.el
+++ b/lisp/xdg.el
@@ -34,6 +34,7 @@
34;;; Code: 34;;; Code:
35 35
36(eval-when-compile 36(eval-when-compile
37 (require 'cl-lib)
37 (require 'subr-x)) 38 (require 'subr-x))
38 39
39 40
@@ -212,6 +213,108 @@ Optional argument GROUP defaults to the string \"Desktop Entry\"."
212 (when (null (string-match-p "[^[:blank:]]" (car res))) (pop res)) 213 (when (null (string-match-p "[^[:blank:]]" (car res))) (pop res))
213 (nreverse res))) 214 (nreverse res)))
214 215
216
217;; MIME apps specification
218;; https://standards.freedesktop.org/mime-apps-spec/mime-apps-spec-1.0.1.html
219
220(defvar xdg-mime-table nil
221 "Table of MIME type to desktop file associations.
222The table is an alist with keys being MIME major types (\"application\",
223\"audio\", etc.), and values being hash tables. Each hash table has
224MIME subtypes as keys and lists of desktop file absolute filenames.")
225
226(defun xdg-mime-apps-files ()
227 "Return a list of files containing MIME/Desktop associations.
228The list is in order of descending priority: user config, then
229admin config, and finally system cached associations."
230 (let ((xdg-data-dirs (xdg-data-dirs))
231 (desktop (getenv "XDG_CURRENT_DESKTOP"))
232 res)
233 (when desktop
234 (setq desktop (format "%s-mimeapps.list" desktop)))
235 (dolist (name (cons "mimeapps.list" desktop))
236 (push (expand-file-name name (xdg-config-home)) res)
237 (push (expand-file-name (format "applications/%s" name) (xdg-data-home))
238 res)
239 (dolist (dir (xdg-config-dirs))
240 (push (expand-file-name name dir) res))
241 (dolist (dir xdg-data-dirs)
242 (push (expand-file-name (format "applications/%s" name) dir) res)))
243 (dolist (dir xdg-data-dirs)
244 (push (expand-file-name "applications/mimeinfo.cache" dir) res))
245 (nreverse res)))
246
247(defun xdg-mime-collect-associations (mime files)
248 "Return a list of desktop file names associated with MIME.
249The associations are searched in the list of file names FILES,
250which is expected to be ordered by priority as in
251`xdg-mime-apps-files'."
252 (let ((regexp (concat (regexp-quote mime) "=\\([^[:cntrl:]]*\\)$"))
253 res sec defaults added removed cached)
254 (with-temp-buffer
255 (dolist (f (reverse files))
256 (when (file-readable-p f)
257 (insert-file-contents-literally f nil nil nil t)
258 (goto-char (point-min))
259 (let (end)
260 (while (not (or (eobp) end))
261 (if (= (following-char) ?\[)
262 (progn (setq sec (char-after (1+ (point))))
263 (forward-line))
264 (if (not (looking-at regexp))
265 (forward-line)
266 (dolist (str (xdg-desktop-strings (match-string 1)))
267 (cl-pushnew str
268 (cond ((eq sec ?D) defaults)
269 ((eq sec ?A) added)
270 ((eq sec ?R) removed)
271 ((eq sec ?M) cached))
272 :test #'equal))
273 (while (and (zerop (forward-line))
274 (/= (following-char) ?\[)))))))
275 ;; Accumulate results into res
276 (dolist (f cached)
277 (when (not (member f removed)) (cl-pushnew f res :test #'equal)))
278 (dolist (f added)
279 (when (not (member f removed)) (push f res)))
280 (dolist (f removed)
281 (setq res (delete f res)))
282 (dolist (f defaults)
283 (push f res))
284 (setq defaults nil added nil removed nil cached nil))))
285 (delete-dups res)))
286
287(defun xdg-mime-apps (mime)
288 "Return list of desktop files associated with MIME, otherwise nil.
289The list is in order of descending priority, and each element is
290an absolute file name of a readable file.
291Results are cached in `xdg-mime-table'."
292 (pcase-let ((`(,type ,subtype) (split-string mime "/"))
293 (xdg-data-dirs (xdg-data-dirs))
294 (caches (xdg-mime-apps-files))
295 (files ()))
296 (let ((mtim1 (get 'xdg-mime-table 'mtime))
297 (mtim2 (cl-loop for f in caches when (file-readable-p f)
298 maximize (float-time (nth 5 (file-attributes f))))))
299 ;; If one of the MIME/Desktop cache files has been modified:
300 (when (or (null mtim1) (time-less-p mtim1 mtim2))
301 (setq xdg-mime-table nil)))
302 (when (null (assoc type xdg-mime-table))
303 (push (cons type (make-hash-table :test #'equal)) xdg-mime-table))
304 (if (let ((def (make-symbol "def"))
305 (table (cdr (assoc type xdg-mime-table))))
306 (not (eq (setq files (gethash subtype table def)) def)))
307 files
308 (and files (setq files nil))
309 (let ((dirs (mapcar (lambda (dir) (expand-file-name "applications" dir))
310 (cons (xdg-data-home) xdg-data-dirs))))
311 ;; Not being particular about desktop IDs
312 (dolist (f (nreverse (xdg-mime-collect-associations mime caches)))
313 (push (locate-file f dirs) files))
314 (when files
315 (put 'xdg-mime-table 'mtime (current-time)))
316 (puthash subtype (delq nil files) (cdr (assoc type xdg-mime-table)))))))
317
215(provide 'xdg) 318(provide 'xdg)
216 319
217;;; xdg.el ends here 320;;; xdg.el ends here