Skip to content

Platform-agnostic GContentType

LRN requested to merge lrn/glib:xdgmime3 into master

This MR is my third attempt to push this through. The first one was in 2014, the second one was in 2017. Third time's a charm? :)

For those of you who just tuned in, a short recap:

  • GContentType is a string.
  • The contents of this string are platform-dependent:
    • On *nix it is a MIME/type, such as text/plain
    • On Windows it is a file extension, such as .txt
    • On MacOS it is a Uniform Type Identifier, such as com.apple.application
  • The idea was that these are the things that various platforms use to tie applications to files (i.e. what happens when the user doubleclicks a file, for example).
  • The problem is that most applications are written with *nix (or, more specifically, Gnome) in mind, and tend to use MIME/types. They might also want MIME/types (GContentType on *nix is said to be MIME/type, but the API is written in such a way that there's a function to get MIME/type from GContentType, which is implemented as g_strdup() on *nix, but has other platforms do all kinds of contortions) and use the APIs to get it from GContentType, which are not trivial to implement.
  • Windows native support for MIME/types is abysmal and is not worth mentioning.
  • I'm not sure what the situation with MacOS is, but there were issues in that area too (though they seem to be fixed).
  • Luckily, the mechanism that handles MIME/types on *nix, which is xdgmime + shared-mime-info, is not specific to *nix. It's just a bunch of xml files, some code to use them efficiently, and a convention as to where applications should install their mime-info additions. This can be made to work on Windows.
  • The problem with using it on Windows is that Windows continues to use file extensions for everything, and if Glib uses MIME/types instead, we'll have incompatibilities and information loss (basically, you might not be able to create a GContentType-that-is-MIME/type from file extension and then convert it back to the same file extension you started with, as the mapping is not straightforward).
  • The solution to that was to use a rare feature of MIME/type specs (RFC 2045 and 2046). I'm too lazy to read the spec again to figure out whether a technical term for this exists, i'm just calling it extended MIME/types. eM/ts have the form of MIME/type; foo=bar baz="blue" zool=over, i.e. it allows arbitrary parameters to be appended to the type itself.
  • We can use this to append ext=.foo to denote that GContentType-that-is-MIME/type was created for a file that has .foo extension. This information will remain with the GContentType for as long as it lives, and will be available in case it is later used to, for example, find the handler for that type (since on Windows that requires using a file extension, not a MIME/type).
  • Additionally it was proposed to generate application/x-extension-foo MIME/type dynamically for *.foo files for which we've been unable to guess the MIME/type. ext=foo and application/x-extension-foo are mostly orthogonal, although it seems prudent to avoid redundant cases like application/x-extension-foo; ext=foo.
  • Theoretically, this can be extended to store MacOS UTI strings, i.e. appending uti=....

I implemented all of this, and adjusted the testsuite to make it pass (the new logic is, IMO, more correct than the old one, so the adjustments to the testsuite is less "made the tests fit the code bugs" and more "fixed the code and fixed broken tests").

To market this MR better, i developed it on my Debian machine, meaning that this is not a W32-specific code dump. Most changes will affect the *nix platform, and the testsuite passes there.

I'm open to suggestions for new tests could be added to cover the new functionality, and ideas on which of the APIs should be made public (for all platforms or for some of them).

This code should not break compliant Glib programs. Non-compliant Glib programs are those that exploited the knowledge of implementation details, i.e. interpreted GContentType strings literally as file extensions or MIME/types, without going through any APIs. This also includes the programs that used strcmp() to compare literal values of GContentTypes instead of using the appropriate APIs.

I didn't test anything on MacOS, and i'm not sure whether it compiles anymore after these changes (hopefully, it does).

This code is made to be as compatible as possible with Windows code in places where it is feasible. For example, it's easy to tell apart file extensions and MIME/type strings, since the former start with a . while the latter do not, so Windows part of the code won't try to pass extensions to xdgmime as MIME/types.

This code includes a few changes to improve compatibility with MSVC (the result of a review by fanc some years ago), though i didn't verify that it still compiles with MSVC.

Merge request reports