Go implementation of the XDG Base Directory Specification and XDG user directories

xdg

Build Status Code coverage pkg.go.dev documentation MIT license Awesome Go Go report card GitHub issues Buy me a coffee

Provides an implementation of the XDG Base Directory Specification. The specification defines a set of standard paths for storing application files, including data and configuration files. For portability and flexibility reasons, applications should use the XDG defined locations instead of hardcoding paths.

The package also includes the locations of well known user directories and an implementation of the state directory proposal. Windows, macOS and most flavors of Unix are supported.

Full documentation can be found at: https://pkg.go.dev/github.com/adrg/xdg.

Installation

go get github.com/adrg/xdg

Default locations

The package defines sensible defaults for XDG variables which are empty or not present in the environment.

XDG Base Directory

Unix macOS Windows
XDG_DATA_HOME ~/.local/share ~/Library/Application Support %LOCALAPPDATA%
XDG_DATA_DIRS /usr/local/share
/usr/share
/Library/Application Support %APPDATA%\Roaming
%PROGRAMDATA%
XDG_CONFIG_HOME ~/.config ~/Library/Preferences %LOCALAPPDATA%
XDG_CONFIG_DIRS /etc/xdg /Library/Preferences %PROGRAMDATA%
XDG_CACHE_HOME ~/.cache ~/Library/Caches %LOCALAPPDATA%\cache
XDG_RUNTIME_DIR /run/user/UID ~/Library/Application Support %LOCALAPPDATA%

XDG user directories

Unix macOS Windows
XDG_DESKTOP_DIR ~/Desktop ~/Desktop %USERPROFILE%/Desktop
XDG_DOWNLOAD_DIR ~/Downloads ~/Downloads %USERPROFILE%/Downloads
XDG_DOCUMENTS_DIR ~/Documents ~/Documents %USERPROFILE%/Documents
XDG_MUSIC_DIR ~/Music ~/Music %USERPROFILE%/Music
XDG_PICTURES_DIR ~/Pictures ~/Pictures %USERPROFILE%/Pictures
XDG_VIDEOS_DIR ~/Videos ~/Movies %USERPROFILE%/Videos
XDG_TEMPLATES_DIR ~/Templates ~/Templates %USERPROFILE%/Templates
XDG_PUBLICSHARE_DIR ~/Public ~/Public %PUBLIC%

Non-standard directories

State directory

Unix
  • ~/.local/state
macOS
  • ~/Library/Application Support
Windows
  • %LOCALAPPDATA%

Application directories

Unix
  • $XDG_DATA_HOME/applications
  • ~/.local/share/applications
  • /usr/local/share/applications
  • /usr/share/applications
  • $XDG_DATA_DIRS/applications
macOS
  • /Applications
Windows
  • %APPDATA%\Roaming\Microsoft\Windows\Start Menu\Programs

Font directories

Unix
  • $XDG_DATA_HOME/fonts
  • ~/.fonts
  • ~/.local/share/fonts
  • /usr/local/share/fonts
  • /usr/share/fonts
  • $XDG_DATA_DIRS/fonts
macOS
  • ~/Library/Fonts
  • /Library/Fonts
  • /System/Library/Fonts
  • /Network/Library/Fonts
Windows
  • %windir%\Fonts
  • %LOCALAPPDATA%\Microsoft\Windows\Fonts

Usage

XDG Base Directory

package main

import (
	"log"

	"github.com/adrg/xdg"
)

func main() {
	// XDG Base Directory paths.
	log.Println("Home data directory:", xdg.DataHome)
	log.Println("Data directories:", xdg.DataDirs)
	log.Println("Home config directory:", xdg.ConfigHome)
	log.Println("Config directories:", xdg.ConfigDirs)
	log.Println("Cache directory:", xdg.CacheHome)
	log.Println("Runtime directory:", xdg.RuntimeDir)

	// Non-standard directories.
	log.Println("Home state directory:", xdg.StateHome)
	log.Println("Application directories:", xdg.ApplicationDirs)
	log.Println("Font directories:", xdg.FontDirs)

	// Obtain a suitable location for application config files.
	// ConfigFile takes one parameter which must contain the name of the file,
	// but it can also contain a set of parent directories. If the directories
	// don't exist, they will be created relative to the base config directory.
	configFilePath, err := xdg.ConfigFile("appname/config.yaml")
	if err != nil {
		log.Fatal(err)
	}
	log.Println("Save the config file at:", configFilePath)

	// For other types of application files use:
	// xdg.DataFile()
	// xdg.CacheFile()
	// xdg.RuntimeFile()
	// xdg.StateFile()

	// Finding application config files.
	// SearchConfigFile takes one parameter which must contain the name of
	// the file, but it can also contain a set of parent directories relative
	// to the config search paths (xdg.ConfigHome and xdg.ConfigDirs).
	configFilePath, err = xdg.SearchConfigFile("appname/config.yaml")
	if err != nil {
		log.Fatal(err)
	}
	log.Println("Config file was found at:", configFilePath)

	// For other types of application files use:
	// xdg.SearchDataFile()
	// xdg.SearchCacheFile()
	// xdg.SearchRuntimeFile()
	// xdg.SearchStateFile()
}

XDG user directories

package main

import (
	"log"

	"github.com/adrg/xdg"
)

func main() {
	// XDG user directories.
	log.Println("Desktop directory:", xdg.UserDirs.Desktop)
	log.Println("Download directory:", xdg.UserDirs.Download)
	log.Println("Documents directory:", xdg.UserDirs.Documents)
	log.Println("Music directory:", xdg.UserDirs.Music)
	log.Println("Pictures directory:", xdg.UserDirs.Pictures)
	log.Println("Videos directory:", xdg.UserDirs.Videos)
	log.Println("Templates directory:", xdg.UserDirs.Templates)
	log.Println("Public directory:", xdg.UserDirs.PublicShare)
}

Stargazers over time

Stargazers over time

Contributing

Contributions in the form of pull requests, issues or just general feedback, are always welcome.
See CONTRIBUTING.MD.

Contributors: adrg, wichert, bouncepaw, gabriel-vasile, KalleDK.

References

For more information see:

License

Copyright (c) 2014 Adrian-George Bostan.

This project is licensed under the MIT license. See LICENSE for more details.

Comments
  • Fix link and socket detection in search methods

    Fix link and socket detection in search methods

    Since we don't use the result and only want to know if the target exists, we can do even less processing of the target by using Lstat. That is, don't resolve links, don't call file relevant stat methods on non-files like sockets, etc. Just check if they exist. This specifically resolves an issue with os.Stat returning an error if the path is a Unix domain socket, on Windows (despite the path being valid and usable).

    For example, this: https://github.com/golang/go/issues/33357#issuecomment-516847781 will return CreateFile server.sock: The file cannot be accessed by the system. While changing to Lstat returns information on the target without error. I'm experiencing this same issue with my own Go program which is creating and using valid Unix sockets, but they're not being found by xdg.SearchRuntimeFile, xdg.SearchConfigFile, etc.

    This specific issue with Stat may need to be fixed upstream, but using Lstat here may still make sense for symlinks, and other types. *(Unless this goes against some xdg expectation, I'm not sure)

  • Added XDG_STATE_HOME

    Added XDG_STATE_HOME

    XDG_STATE_HOME is to enable people to split mutable and immutable data, such that immutable date stays in XDG_DATA_HOME, but applications can use XDG_STATE_HOME to store what normally would be stored in /var/lib/app

    Better description of the problem https://github.com/ayekat/dotfiles/issues/30

    Links

    • https://wiki.debian.org/XDGBaseDirectorySpecification#Proposal:_STATE_directory
    • https://lists.freedesktop.org/pipermail/xdg/2009-February/010191.html
    • https://lists.freedesktop.org/pipermail/xdg/2012-December/012598.html
    • https://lists.freedesktop.org/archives/xdg/2016-December/013803.html
    • ActiveState/appdirs@ea0701d

    … and more! (search the web for XDG_STATE_HOME)

  • Internal improvements

    Internal improvements

    • Add workaround for os.Stat bugs on Windows, described by issues:
      • https://github.com/golang/go/issues/33357
      • https://github.com/microsoft/Windows-Containers/issues/97
      • https://github.com/golang/go/issues/34900
    • Enable build for js/wasm
    • Minor internal refactoring
    • Improve testing

    On some versions of Windows, os.Stat returns an error for Unix sockets. Also, seems like on all Windows versions, it returns an error for symbolic links to directories.

  • Support more XDG directories

    Support more XDG directories

    Please also support those directories:

    • USER_DIRECTORY_DESKTOP
    • USER_DIRECTORY_DOCUMENTS
    • USER_DIRECTORY_DOWNLOAD
    • USER_DIRECTORY_MUSIC
    • USER_DIRECTORY_PICTURES
    • USER_DIRECTORY_PUBLIC_SHARE
    • USER_DIRECTORY_TEMPLATES
    • USER_DIRECTORY_VIDEOS
    • USER_N_DIRECTORIES
  • Plan 9 support

    Plan 9 support

    When building my project Mycorrhiza, a user complained that it doesn't build on Plan 9 operating system. From the error messages I understood that the problem is that initBaseDirs and initUserDirs is not implemented for Plan 9, I'd love to see that implemented.

  • createPath might be doing to much

    createPath might be doing to much

    We should check if that path exists, as we shouldn't create this "part" of the folder imho.

    As in all the folders in the name variable is okay to create, but the folders in the paths could be system folders.

    A "constructed" case would be a root user running a program with this, might end up creating /usr/local/share, even if /usr/share is the only path there.

    I understand that this would create a problem with paths like ~/.local/state as that wouldn't be created, and I could see a reason for that to be created.

    A solution to this might be a few functions to create the XDG_BASE places, and let createPath only handle the relative part.

    func main() {
      // For homes that we need to create files in
      xdg.CreateCacheHome()
      xdg.CreateConfigHome()
    }
    

    https://github.com/adrg/xdg/blob/aad56aee3b6d35da74f30725a6c356560a61f1cd/utils.go#L63-L71

  • Remove might be a bit to harsh

    Remove might be a bit to harsh

    https://github.com/adrg/xdg/blob/aad56aee3b6d35da74f30725a6c356560a61f1cd/base_dirs.go#L57-L60

    Shouldn't this just give an error? It feels like deleting a file without "consent" is a bit harsh.

  • Bump github.com/stretchr/testify from 1.8.0 to 1.8.1

    Bump github.com/stretchr/testify from 1.8.0 to 1.8.1

    Bumps github.com/stretchr/testify from 1.8.0 to 1.8.1.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
  • Bump github.com/stretchr/testify from 1.7.4 to 1.7.5

    Bump github.com/stretchr/testify from 1.7.4 to 1.7.5

    Bumps github.com/stretchr/testify from 1.7.4 to 1.7.5.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
  • Bump github.com/stretchr/testify from 1.7.1 to 1.7.3

    Bump github.com/stretchr/testify from 1.7.1 to 1.7.3

    Bumps github.com/stretchr/testify from 1.7.1 to 1.7.3.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
  • Bump github.com/stretchr/testify from 1.7.1 to 1.7.2

    Bump github.com/stretchr/testify from 1.7.1 to 1.7.2

    Bumps github.com/stretchr/testify from 1.7.1 to 1.7.2.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
  • Fix #29: Parse ~/.config/user-dirs.dirs file

    Fix #29: Parse ~/.config/user-dirs.dirs file

    Read ~/.config/user-dirs.dirs if exist, inject in env var if not already present

    Test TestDefaultUserDirs will fail if localized file found.

    Workaround without this merge:

    func LoadlinuxXDG() {
    	var (
    		homeDir string
    		err     error
    	)
    
    	if homeDir, err = os.UserHomeDir(); err != nil {
    		fmt.Fprintln(os.Stderr, err)
    		return
    	}
    
    	userDirFile := path.Join(homeDir, ".config/user-dirs.dirs")
    
    	if _, err = os.Stat(userDirFile); err != nil {
    		return
    	}
    
    	cfg, err := ini.Load(userDirFile)
    	if err != nil {
    		return
    	}
    
    	// Load xdg env
    	for _, key := range cfg.Section("").Keys() {
    		value := strings.Replace(key.Value(), "$HOME", homeDir, 1)
    		os.Setenv(key.Name(), value)
    	}
    
    	xdg.Reload()
    }
    
  • xdg: Add the hability to initialize a new BaseDir with different FS behind

    xdg: Add the hability to initialize a new BaseDir with different FS behind

    This allows to not only use the machine FS but any other supported by afero.Fs in this case.

    This is a PoC PR as I want to be sure this is a correct way to do it in your opinion, for me it looks clean and requires no changes for people already using this lib.

    The idea behind is to have the BaseDirectories be able to be initialized with an afero.Fs and make it public so anyone could choose what to do. By default the baseDir is initialized with afero.NewOsFs() which uses the stdlib of GO for os so it works as it worked before.

    Another solution would be to have a global variable with the FS to use but I really hate to use global variables for this things, and having a way to initialize different xdg with different FS makes it easy to use IMO.

    We could also make the variable BaseDir public or add a public function to change that value instead of having the New but I had the same feeling of global things not beeing that useful and easy to use/understand.

    If this changes where to be on the right way there are a few things still to be done:

    • Change all the test to use afero.NewMemMapFs which is an in-memory implementation of a FS, quite useful for testing.
    • Update the documentation
    • Potentially update the examples?

    Whenever you have time take a look and if it's ok I'll continue with the things I mention to finish or any other thing you may want or suggestion to change :).

    Closes #35

  • Parse ~/.config/user-dirs.dirs file

    Parse ~/.config/user-dirs.dirs file

    I am testing this library using fedora and arch Linux and it seems like none of the distributions are setting the env vars for XDG User Directories.

    [a@localhost-live test]$ cat /home/a/.config/user-dirs.dirs
    # This file is written by xdg-user-dirs-update
    # If you want to change or add directories, just edit the line you're
    # interested in. All local changes will be retained on the next run.
    # Format is XDG_xxx_DIR="$HOME/yyy", where yyy is a shell-escaped
    # homedir-relative path, or XDG_xxx_DIR="/yyy", where /yyy is an
    # absolute path. No other format is supported.
    # 
    XDG_DESKTOP_DIR="$HOME/Pulpit"
    XDG_DOWNLOAD_DIR="$HOME/Pobrane"
    XDG_TEMPLATES_DIR="$HOME/Szablony"
    XDG_PUBLICSHARE_DIR="$HOME/Publiczny"
    XDG_DOCUMENTS_DIR="$HOME/Dokumenty"
    XDG_MUSIC_DIR="$HOME/Muzyka"
    XDG_PICTURES_DIR="$HOME/Obrazy"
    XDG_VIDEOS_DIR="$HOME/Wideo"
    [a@localhost-live test]$ ./main 
    2022/02/04 18:14:32 Desktop directory: /home/a/Desktop
    2022/02/04 18:14:32 Download directory: /home/a/Downloads
    2022/02/04 18:14:32 Documents directory: /home/a/Documents
    2022/02/04 18:14:32 Music directory: /home/a/Music
    2022/02/04 18:14:32 Pictures directory: /home/a/Pictures
    2022/02/04 18:14:32 Videos directory: /home/a/Videos
    2022/02/04 18:14:32 Templates directory: /home/a/Templates
    2022/02/04 18:14:32 Public directory: /home/a/Public
    

    Firefox for example reads the ~/.config/user-dirs.dirs directly.

    [a@localhost-live test]$ cat firefox-strace | grep .config/user-dirs
    openat(AT_FDCWD, "/home/a/.config/user-dirs.dirs", O_RDONLY) = 53
    
  • Add base support for XDG base user executable dir

    Add base support for XDG base user executable dir

    https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html

    There is a single base directory relative to which user-specific executable files may be written.

    User-specific executable files may be stored in $HOME/.local/bin.

Atomic Arbitrage - A base example of a bare implementation of an arbitrage bot

Atomic Arbitrage Atomic Arbitrage is a base example of a bare implementation of

Nov 23, 2022
DSV Parallel Processor takes input files and query specification via a spec file

DSV Parallel Processor Spec file DSV Parallel Processor takes input files and query specification via a spec file (conventionally named "spec.toml").

Oct 9, 2021
seqinfo gathers image sequence info from directories.

seqinfo seqinfo gathers info from sequences in directories, and prints or writes it to an excel file. Usage seqinfo /path/to/search/sequences For adv

Dec 23, 2021
An application that is developed to generate application by API specification

GO boilerplate is an application that is developed to generate application by API specification and Database schema with the collaboration with opn-generator.

Oct 14, 2021
Generates Golang code with enums from TOML specification

enum-generator This enum-generator is intended to generate golang source code for string-based enum types and enum types with associated data from des

Dec 29, 2021
Golang CS:GO external base. Development currently halted due to compiler/runtime Golang bugs.

gogo Golang CS:GO External cheat/base. Also, my first Golang project. Wait! Development momentarily halted due to compiler/runtime bugs. Disclaimer Th

Jun 25, 2022
Golang Base Code

Golang Base Code Getting Started Clone repository. git clone [email protected]:mo-t

Dec 14, 2022
Quickly clone an entire org/users repositories into one directory - Supports GitHub, GitLab, Bitbucket, and more
Quickly clone an entire org/users repositories into one directory - Supports GitHub, GitLab, Bitbucket, and more

ghorg ghorg allows you to quickly clone all of an orgs, or users repos into a single directory. This can be useful in many situations including Search

Jan 1, 2023
Tool for printing a directory tree and indicating the space it occupies.

Tool for printing a directory tree and indicating the space it occupies.

Nov 6, 2021
W3C WoT Thing Description Directory (TDD)

TinyIoT Thing Directory This is an implementation of the W3C WoT Thing Description Directory (TDD), a registry of Thing Descriptions. This project is

Jul 22, 2022
A file wiper (for the current directory) written in Go
A file wiper (for the current directory) written in Go

A file wiper (for the current directory) written in Go. Rewrites all files within all accessible directories from the current directory, including itself. to be blank, usually making victims think nothing was harmed.

Dec 9, 2021
set and get github user statuses

gh user-status being an extension for interacting with the status on a GitHub profile. gh user-status set gh user-status set interactively set status

Nov 19, 2022
create temporary Firefox profile, install user.js and extensions, launch Firefox

tmpfox tmpfox is a Firefox wrapper that: Creates a temporary Firefox profile Installs user.js configuration file from Arkenfox for increased privacy a

Jul 27, 2022
The gofinder program is an acme user interface to search through Go projects.

The gofinder program is an acme user interface to search through Go projects.

Jun 14, 2021
User level X Keyboard Grabber

xkg - X Keyboard Grabber Installation go get gopkg.in/xkg.v0 Usage example: package main import ( "fmt" "gopkg.in/xkg.v0" ) func main() { var ke

Sep 27, 2022
Get user-like access to VirtualBox VMs from Go code.

#Vboxgo Get user-like access to VirtualBox VMs from Go code. This library wraps some define-tainted VirtualBox SDK functions, making it possible to ge

Oct 25, 2021
Yubigo is a Yubikey client API library that provides an easy way to integrate the Yubico Yubikey into your existing Go-based user authentication infrastructure.

yubigo Yubigo is a Yubikey client API library that provides an easy way to integrate the Yubikey into any Go application. Installation Installation is

Oct 27, 2022
Create one endpoint with add user functionality

hubuc-task Create one endpoint with add user functionality

Nov 13, 2021
Functional programming library for Go including a lazy list implementation and some of the most usual functions.

functional A functional programming library including a lazy list implementation and some of the most usual functions. import FP "github.com/tcard/fun

May 21, 2022