pax_global_header00006660000000000000000000000064136331447250014522gustar00rootroot0000000000000052 comment=23a0a2355531ea924dbba5b78b549dbd18007f3f go-app-paths-0.2.1/000077500000000000000000000000001363314472500140225ustar00rootroot00000000000000go-app-paths-0.2.1/.github/000077500000000000000000000000001363314472500153625ustar00rootroot00000000000000go-app-paths-0.2.1/.github/FUNDING.yml000066400000000000000000000000171363314472500171750ustar00rootroot00000000000000github: muesli go-app-paths-0.2.1/.github/workflows/000077500000000000000000000000001363314472500174175ustar00rootroot00000000000000go-app-paths-0.2.1/.github/workflows/go.yml000066400000000000000000000017331363314472500205530ustar00rootroot00000000000000name: build on: [push, pull_request] jobs: test: strategy: matrix: go-version: [1.11.x, 1.12.x, 1.13.x, 1.14.x] platform: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.platform }} env: GO111MODULE: "on" steps: - name: Install Go uses: actions/setup-go@v1 with: go-version: ${{ matrix.go-version }} - name: Checkout code uses: actions/checkout@v1 - name: Download Go modules run: go mod download - name: Build run: go build -v ./... - name: Test run: go test ./... - name: Coverage env: COVERALLS_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | go test -race -covermode atomic -coverprofile=profile.cov ./... GO111MODULE=off go get github.com/mattn/goveralls $(go env GOPATH)/bin/goveralls -coverprofile=profile.cov -service=github if: matrix.go-version == '1.14.x' && matrix.platform == 'ubuntu-latest' go-app-paths-0.2.1/.gitignore000066400000000000000000000004231363314472500160110ustar00rootroot00000000000000# Binaries for programs and plugins *.exe *.dll *.so *.dylib # Test binary, build with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 .glide/ go-app-paths-0.2.1/LICENSE000066400000000000000000000020671363314472500150340ustar00rootroot00000000000000MIT License Copyright (c) 2018 Christian Muehlhaeuser 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. go-app-paths-0.2.1/README.md000066400000000000000000000157021363314472500153060ustar00rootroot00000000000000# go-app-paths [![Latest Release](https://img.shields.io/github/release/muesli/go-app-paths.svg)](https://github.com/muesli/go-app-paths/releases) [![GoDoc](https://godoc.org/github.com/golang/gddo?status.svg)](https://pkg.go.dev/github.com/muesli/go-app-paths?tab=doc) [![Build Status](https://github.com/muesli/go-app-paths/workflows/build/badge.svg)](https://github.com/muesli/go-app-paths/actions) [![Coverage Status](https://coveralls.io/repos/github/muesli/go-app-paths/badge.svg?branch=master)](https://coveralls.io/github/muesli/go-app-paths?branch=master) [![Go ReportCard](http://goreportcard.com/badge/muesli/go-app-paths)](http://goreportcard.com/report/muesli/go-app-paths) Lets you retrieve platform-specific paths (like directories for app-data, cache, config, and logs). It is fully compliant with the [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html) on Unix, but also provides implementations for macOS and Windows systems. ## Installation Make sure you have a working Go environment (Go 1.2 or higher is required). See the [install instructions](http://golang.org/doc/install.html). To install go-app-paths, simply run: go get github.com/muesli/go-app-paths ## Usage ```go import gap "github.com/muesli/go-app-paths" ``` ### Scopes You can initialize `gap` with either the `gap.User` or `gap.System` scope to retrieve user- and/or system-specific base directories and paths: ```go scope := gap.NewScope(gap.User, "app") ``` ### Directories `DataDirs` retrieves a priority-sorted list of data directories: ```go dirs, err := scope.DataDirs() ``` | Platform | User Scope | System Scope | | -------- | ---------------------------------------------------------------- | ------------------------------------------ | | Unix | ["~/.local/share/app", "/usr/local/share/app", "/usr/share/app"] | ["/usr/local/share/app", "/usr/share/app"] | | macOS | ["~/Library/Application Support/app"] | ["/Library/Application Support/app"] | | Windows | ["%LOCALAPPDATA%/app"] | ["%PROGRAMDATA%/app"] | --- `ConfigDirs` retrieves a priority-sorted list of config directories: ```go dirs, err := scope.ConfigDirs() ``` | Platform | User Scope | System Scope | | -------- | --------------------------------------------- | ---------------------------- | | Unix | ["~/.config/app", "/etc/xdg/app", "/etc/app"] | ["/etc/xdg/app", "/etc/app"] | | macOS | ["~/Library/Preferences/app"] | ["/Library/Preferences/app"] | | Windows | ["%LOCALAPPDATA%/app/Config"] | ["%PROGRAMDATA%/app/Config"] | --- `CacheDir` retrieves the app's cache directory: ```go dir, err := scope.CacheDir() ``` | Platform | User Scope | System Scope | | -------- | ------------------------ | ----------------------- | | Unix | ~/.cache/app | /var/cache/app | | macOS | ~/Library/Caches/app | /Library/Caches/app | | Windows | %LOCALAPPDATA%/app/Cache | %PROGRAMDATA%/app/Cache | ### Default File Paths `DataPath` retrieves the default path for a data file: ```go path, err := scope.DataPath("filename") ``` | Platform | User Scope | System Scope | | -------- | ------------------------------------------ | ----------------------------------------- | | Unix | ~/.local/share/app/filename | /usr/local/share/app/filename | | macOS | ~/Library/Application Support/app/filename | /Library/Application Support/app/filename | | Windows | %LOCALAPPDATA%/app/filename | %PROGRAMDATA%/app/filename | --- `ConfigPath` retrieves the default path for a config file: ```go path, err := scope.ConfigPath("filename.conf") ``` | Platform | User Scope | System Scope | | -------- | --------------------------------------- | -------------------------------------- | | Unix | ~/.config/app/filename.conf | /etc/xdg/app/filename.conf | | macOS | ~/Library/Preferences/app/filename.conf | /Library/Preferences/app/filename.conf | | Windows | %LOCALAPPDATA%/app/Config/filename.conf | %PROGRAMDATA%/app/Config/filename.conf | --- `LogPath` retrieves the default path for a log file: ```go path, err := scope.LogPath("filename.log") ``` | Platform | User Scope | System Scope | | -------- | ------------------------------------ | ----------------------------------- | | Unix | ~/.local/share/app/filename.log | /var/log/app/filename.log | | macOS | ~/Library/Logs/app/filename.log | /Library/Logs/app/filename.log | | Windows | %LOCALAPPDATA%/app/Logs/filename.log | %PROGRAMDATA%/app/Logs/filename.log | ### Lookup Methods `LookupData` retrieves a priority-sorted list of paths for existing data files with the name `filename`: ```go path, err := scope.LookupData("filename") ``` | Platform | User Scope | System Scope | | -------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | | Unix | ["~/.local/share/app/filename", "/usr/local/share/app/filename", "/usr/share/app/filename"] | ["/usr/local/share/app/filename", "/usr/share/app/filename"] | | macOS | ["~/Library/Application Support/app/filename"] | ["/Library/Application Support/app/filename"] | | Windows | ["%LOCALAPPDATA%/app/filename"] | ["%PROGRAMDATA%/app/filename"] | --- `LookupConfig` retrieves a priority-sorted list of paths for existing config files with the name `filename.conf`: ```go path, err := scope.LookupConfig("filename.conf") ``` | Platform | User Scope | System Scope | | -------- | --------------------------------------------------------------------------------------- | -------------------------------------------------------- | | Unix | ["~/.config/app/filename.conf", "/etc/xdg/app/filename.conf", "/etc/app/filename.conf"] | ["/etc/xdg/app/filename.conf", "/etc/app/filename.conf"] | | macOS | ["~/Library/Preferences/app/filename.conf"] | ["/Library/Preferences/app/filename.conf"] | | Windows | ["%LOCALAPPDATA%/app/Config/filename.conf"] | ["%PROGRAMDATA%/app/Config/filename.conf"] | go-app-paths-0.2.1/go.mod000066400000000000000000000001371363314472500151310ustar00rootroot00000000000000module github.com/muesli/go-app-paths go 1.14 require github.com/mitchellh/go-homedir v1.1.0 go-app-paths-0.2.1/go.sum000066400000000000000000000002651363314472500151600ustar00rootroot00000000000000github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= go-app-paths-0.2.1/paths.go000066400000000000000000000101071363314472500154670ustar00rootroot00000000000000package gap import ( "errors" "os" "strings" homedir "github.com/mitchellh/go-homedir" ) var ( // ErrInvalidScope gets returned when an invalid scope type has been set. ErrInvalidScope = errors.New("Invalid scope type") // ErrRetrievingPath gets returned when the path could not be resolved. ErrRetrievingPath = errors.New("Could not retrieve path") ) // ScopeType specifies whether returned paths are user-specific or system-wide. type ScopeType int const ( // System is the system-wide scope. System ScopeType = iota // User is the user-specific scope. User // CustomHome uses a custom user home as scope. CustomHome ) // Scope holds scope & app-specific information. type Scope struct { Type ScopeType CustomHome string Vendor string App string } // NewScope returns a new Scope that lets you query app- & platform-specific // paths. func NewScope(t ScopeType, app string) *Scope { return &Scope{ Type: t, App: app, } } // NewVendorScope returns a new Scope with vendor information that lets you // query app- & platform-specific paths. func NewVendorScope(t ScopeType, vendor, app string) *Scope { return &Scope{ Type: t, Vendor: vendor, App: app, } } // NewCustomHomeScope returns a new Scope that lets you operate on a custom path // prefix. func NewCustomHomeScope(path, vendor, app string) *Scope { return &Scope{ Type: CustomHome, CustomHome: path, Vendor: vendor, App: app, } } // DataDirs returns a priority-sorted slice of all the application's data dirs. func (s *Scope) DataDirs() ([]string, error) { ps, err := s.dataDirs() if err != nil { return nil, err } var sl []string for _, v := range ps { sl = append(sl, s.appendPaths(v)) } return sl, nil } // ConfigDirs returns a priority-sorted slice of all of the application's config // dirs. func (s *Scope) ConfigDirs() ([]string, error) { ps, err := s.configDirs() if err != nil { return nil, err } var sl []string for _, v := range ps { sl = append(sl, s.appendPaths(v)) } return sl, nil } // CacheDir returns the full path to the application's default cache dir. func (s *Scope) CacheDir() (string, error) { p, err := s.cacheDir() if err != nil { return p, err } return s.appendPaths(p), nil } // LogPath returns the full path to the application's default log file. func (s *Scope) LogPath(filename string) (string, error) { p, err := s.logDir() if err != nil { return p, err } return s.appendPaths(p, filename), nil } // DataPath returns the full path to a file in the application's default data // directory. func (s *Scope) DataPath(filename string) (string, error) { p, err := s.dataDir() if err != nil { return p, err } return s.appendPaths(p, filename), nil } // ConfigPath returns the full path to a file in the application's default // config directory. func (s *Scope) ConfigPath(filename string) (string, error) { p, err := s.configDir() if err != nil { return p, err } return s.appendPaths(p, filename), nil } // LookupConfig returns all existing configs with this filename. func (s *Scope) LookupConfig(filename string) ([]string, error) { paths, err := s.configDirs() if err != nil { return nil, err } return s.findExisting(paths, filename), nil } // LookupDataFile returns all existing data files with this filename. func (s *Scope) LookupDataFile(filename string) ([]string, error) { paths, err := s.dataDirs() if err != nil { return nil, err } return s.findExisting(paths, filename), nil } // expandUser is a helper function that expands the first '~' it finds in the // passed path with the home directory of the current user. func expandUser(path string) string { if u, err := homedir.Dir(); err == nil { return strings.Replace(path, "~", u, -1) } return path } // findExisting tries to find filename in all paths and returns a list of // existing paths. func (s *Scope) findExisting(paths []string, filename string) []string { var sl []string for _, p := range paths { f := s.appendPaths(p, filename) _, err := os.Stat(f) if err == nil || os.IsExist(err) { sl = append(sl, f) } } return sl } go-app-paths-0.2.1/paths_darwin.go000066400000000000000000000052221363314472500170350ustar00rootroot00000000000000// +build darwin package gap import ( "path/filepath" ) var ( defaultDataDir = "/Library/Application Support" defaultConfigDir = "/Library/Preferences" defaultLogDir = "/Library/Logs" defaultCacheDir = "/Library/Caches" ) // appendPaths appends the app-name and further variadic parts to a path func (s *Scope) appendPaths(path string, parts ...string) string { paths := []string{path, s.Vendor, s.App} paths = append(paths, parts...) return filepath.Join(paths...) } // dataDir returns the full path to the data directory. func (s *Scope) dataDir() (string, error) { switch s.Type { case System: return defaultDataDir, nil case User: return expandUser("~" + defaultDataDir), nil case CustomHome: return filepath.Join(s.CustomHome, defaultDataDir), nil } return "", ErrInvalidScope } // dataDirs returns a priority-sorted slice of data dirs. func (s *Scope) dataDirs() ([]string, error) { var sl []string switch s.Type { case CustomHome: path, err := s.dataDir() if err != nil { return sl, err } sl = append(sl, path) case User: path, err := s.dataDir() if err != nil { return sl, err } sl = append(sl, path) fallthrough case System: sl = append(sl, defaultDataDir) } return sl, nil } // configDir returns the full path to the config dir. func (s *Scope) configDir() (string, error) { switch s.Type { case System: return defaultConfigDir, nil case User: return expandUser("~" + defaultConfigDir), nil case CustomHome: return filepath.Join(s.CustomHome, defaultConfigDir), nil } return "", ErrInvalidScope } // configDirs returns a priority-sorted slice of config dirs. func (s *Scope) configDirs() ([]string, error) { var sl []string switch s.Type { case CustomHome: path, err := s.configDir() if err != nil { return sl, err } sl = append(sl, path) case User: path, err := s.configDir() if err != nil { return sl, err } sl = append(sl, path) fallthrough case System: sl = append(sl, defaultConfigDir) } return sl, nil } // cacheDir returns the full path to the cache directory. func (s *Scope) cacheDir() (string, error) { switch s.Type { case System: return defaultCacheDir, nil case User: return expandUser("~" + defaultCacheDir), nil case CustomHome: return filepath.Join(s.CustomHome, defaultCacheDir), nil } return "", ErrInvalidScope } // logDir returns the full path to the log dir. func (s *Scope) logDir() (string, error) { switch s.Type { case System: return defaultLogDir, nil case User: return expandUser("~" + defaultLogDir), nil case CustomHome: return filepath.Join(s.CustomHome, defaultLogDir), nil } return "", ErrInvalidScope } go-app-paths-0.2.1/paths_darwin_test.go000066400000000000000000000053731363314472500201030ustar00rootroot00000000000000// +build darwin package gap import ( "testing" ) func TestPaths(t *testing.T) { tests := []struct { scope *Scope dataDir string cacheDir string configFile string dataFile string logFile string }{ { scope: NewScope(System, "foobar"), dataDir: "/Library/Application Support/foobar", cacheDir: "/Library/Caches/foobar", configFile: "/Library/Preferences/foobar/foobar.conf", dataFile: "/Library/Application Support/foobar/foobar.data", logFile: "/Library/Logs/foobar/foobar.log", }, { scope: NewVendorScope(System, "barcorp", "foobar"), dataDir: "/Library/Application Support/barcorp/foobar", cacheDir: "/Library/Caches/barcorp/foobar", configFile: "/Library/Preferences/barcorp/foobar/foobar.conf", dataFile: "/Library/Application Support/barcorp/foobar/foobar.data", logFile: "/Library/Logs/barcorp/foobar/foobar.log", }, { scope: NewScope(User, "foobar"), dataDir: "~/Library/Application Support/foobar", cacheDir: "~/Library/Caches/foobar", configFile: "~/Library/Preferences/foobar/foobar.conf", dataFile: "~/Library/Application Support/foobar/foobar.data", logFile: "~/Library/Logs/foobar/foobar.log", }, { scope: NewCustomHomeScope("/tmp", "", "foobar"), dataDir: "/tmp/Library/Application Support/foobar", cacheDir: "/tmp/Library/Caches/foobar", configFile: "/tmp/Library/Preferences/foobar/foobar.conf", dataFile: "/tmp/Library/Application Support/foobar/foobar.data", logFile: "/tmp/Library/Logs/foobar/foobar.log", }, } for _, tt := range tests { paths, err := tt.scope.DataDirs() if err != nil { t.Errorf("Error retrieving data dir: %s", err) } if paths[0] != expandUser(tt.dataDir) { t.Errorf("Expected data dir: %s - got: %s", tt.dataDir, paths[0]) } path, err := tt.scope.CacheDir() if err != nil { t.Errorf("Error retrieving cache dir: %s", err) } if path != expandUser(tt.cacheDir) { t.Errorf("Expected cache dir: %s - got: %s", tt.cacheDir, path) } path, err = tt.scope.ConfigPath(tt.scope.App + ".conf") if err != nil { t.Errorf("Error retrieving config path: %s", err) } if path != expandUser(tt.configFile) { t.Errorf("Expected config path: %s - got: %s", tt.configFile, path) } path, err = tt.scope.DataPath(tt.scope.App + ".data") if err != nil { t.Errorf("Error retrieving data path: %s", err) } if path != expandUser(tt.dataFile) { t.Errorf("Expected data path: %s - got: %s", tt.dataFile, path) } path, err = tt.scope.LogPath(tt.scope.App + ".log") if err != nil { t.Errorf("Error retrieving log path: %s", err) } if path != expandUser(tt.logFile) { t.Errorf("Expected log path: %s - got: %s", tt.logFile, path) } } } go-app-paths-0.2.1/paths_unix.go000066400000000000000000000062651363314472500165440ustar00rootroot00000000000000// +build !darwin,!windows package gap import ( "os" "path/filepath" "strings" ) var ( defaultDataDirs = []string{"/usr/local/share", "/usr/share"} defaultConfigDirs = []string{"/etc/xdg", "/etc"} defaultLogDir = "/var/log" defaultCacheDir = "/var/cache" ) // appendPaths appends the app-name and further variadic parts to a path func (s *Scope) appendPaths(path string, parts ...string) string { paths := []string{path, s.Vendor, s.App} paths = append(paths, parts...) return filepath.Join(paths...) } // dataDir returns the full path to the data directory. func (s *Scope) dataDir() (string, error) { switch s.Type { case System: return defaultDataDirs[0], nil case User: path := os.Getenv("XDG_DATA_HOME") if path == "" { return expandUser("~/.local/share"), nil } return path, nil case CustomHome: return filepath.Join(s.CustomHome, ".local/share"), nil } return "", ErrInvalidScope } // dataDirs returns a priority-sorted slice of data dirs. func (s *Scope) dataDirs() ([]string, error) { var sl []string switch s.Type { case CustomHome: path, err := s.dataDir() if err != nil { return sl, err } sl = append(sl, path) case User: path, err := s.dataDir() if err != nil { return sl, err } sl = append(sl, path) path = os.Getenv("XDG_DATA_DIRS") if path != "" { paths := strings.Split(path, string(os.PathListSeparator)) for _, p := range paths { sl = append(sl, p) } } fallthrough case System: sl = append(sl, defaultDataDirs...) } return sl, nil } // configDir returns the full path to the config dir. func (s *Scope) configDir() (string, error) { switch s.Type { case System: return defaultConfigDirs[0], nil case User: path := os.Getenv("XDG_CONFIG_HOME") if path == "" { return expandUser("~/.config"), nil } return path, nil case CustomHome: return filepath.Join(s.CustomHome, ".config"), nil } return "", ErrInvalidScope } // configDirs returns a priority-sorted slice of config dirs. func (s *Scope) configDirs() ([]string, error) { var sl []string switch s.Type { case CustomHome: path, err := s.configDir() if err != nil { return sl, err } sl = append(sl, path) case User: path, err := s.configDir() if err != nil { return sl, err } sl = append(sl, path) path = os.Getenv("XDG_CONFIG_DIRS") if path != "" { paths := strings.Split(path, string(os.PathListSeparator)) for _, p := range paths { sl = append(sl, p) } } fallthrough case System: sl = append(sl, defaultConfigDirs...) } return sl, nil } // cacheDir returns the full path to the cache directory. func (s *Scope) cacheDir() (string, error) { switch s.Type { case System: return defaultCacheDir, nil case User: path := os.Getenv("XDG_CACHE_HOME") if path == "" { return expandUser("~/.cache"), nil } return path, nil case CustomHome: return filepath.Join(s.CustomHome, ".cache"), nil } return "", ErrInvalidScope } // logDir returns the full path to the log dir. func (s *Scope) logDir() (string, error) { switch s.Type { case System: return defaultLogDir, nil case User: fallthrough case CustomHome: return s.dataDir() } return "", ErrInvalidScope } go-app-paths-0.2.1/paths_unix_test.go000066400000000000000000000115711363314472500175770ustar00rootroot00000000000000// +build !darwin,!windows package gap import ( "fmt" "testing" ) func TestPaths(t *testing.T) { tests := []struct { scope *Scope dataDirs []string configDirs []string cacheDir string configFile string dataFile string logFile string }{ { scope: NewScope(System, "foobar"), dataDirs: []string{"/usr/local/share/foobar", "/usr/share/foobar"}, configDirs: []string{"/etc/xdg/foobar", "/etc/foobar"}, cacheDir: "/var/cache/foobar", configFile: "/etc/xdg/foobar/foobar.conf", dataFile: "/usr/local/share/foobar/foobar.data", logFile: "/var/log/foobar/foobar.log", }, { scope: NewVendorScope(System, "barcorp", "foobar"), dataDirs: []string{"/usr/local/share/barcorp/foobar", "/usr/share/barcorp/foobar"}, configDirs: []string{"/etc/xdg/barcorp/foobar", "/etc/barcorp/foobar"}, cacheDir: "/var/cache/barcorp/foobar", configFile: "/etc/xdg/barcorp/foobar/foobar.conf", dataFile: "/usr/local/share/barcorp/foobar/foobar.data", logFile: "/var/log/barcorp/foobar/foobar.log", }, { scope: NewScope(User, "foobar"), dataDirs: []string{"~/.local/share/foobar", "/usr/local/share/foobar", "/usr/share/foobar"}, configDirs: []string{"~/.config/foobar", "/etc/xdg/foobar", "/etc/foobar"}, cacheDir: "~/.cache/foobar", configFile: "~/.config/foobar/foobar.conf", dataFile: "~/.local/share/foobar/foobar.data", logFile: "~/.local/share/foobar/foobar.log", }, { scope: NewCustomHomeScope("/tmp", "", "foobar"), dataDirs: []string{"/tmp/.local/share/foobar"}, configDirs: []string{"/tmp/.config/foobar"}, cacheDir: "/tmp/.cache/foobar", configFile: "/tmp/.config/foobar/foobar.conf", dataFile: "/tmp/.local/share/foobar/foobar.data", logFile: "/tmp/.local/share/foobar/foobar.log", }, } for _, tt := range tests { paths, err := tt.scope.DataDirs() if err != nil { t.Errorf("Error retrieving data dir: %s", err) } if len(paths) != len(tt.dataDirs) { fmt.Println(paths) t.Fatalf("Expected %d results, got %d", len(tt.dataDirs), len(paths)) } for i := range paths { if paths[i] != expandUser(tt.dataDirs[i]) { t.Errorf("Expected data dir: %s - got: %s", tt.dataDirs[i], paths[i]) } } paths, err = tt.scope.ConfigDirs() if err != nil { t.Errorf("Error retrieving data dir: %s", err) } if len(paths) != len(tt.configDirs) { fmt.Println(paths) t.Fatalf("Expected %d results, got %d", len(tt.configDirs), len(paths)) } for i := range paths { if paths[i] != expandUser(tt.configDirs[i]) { t.Errorf("Expected data dir: %s - got: %s", tt.configDirs[i], paths[i]) } } path, err := tt.scope.CacheDir() if err != nil { t.Errorf("Error retrieving cache dir: %s", err) } if path != expandUser(tt.cacheDir) { t.Errorf("Expected cache dir: %s - got: %s", tt.cacheDir, path) } path, err = tt.scope.ConfigPath(tt.scope.App + ".conf") if err != nil { t.Errorf("Error retrieving config path: %s", err) } if path != expandUser(tt.configFile) { t.Errorf("Expected config path: %s - got: %s", tt.configFile, path) } path, err = tt.scope.DataPath(tt.scope.App + ".data") if err != nil { t.Errorf("Error retrieving data path: %s", err) } if path != expandUser(tt.dataFile) { t.Errorf("Expected data path: %s - got: %s", tt.dataFile, path) } path, err = tt.scope.LogPath(tt.scope.App + ".log") if err != nil { t.Errorf("Error retrieving log path: %s", err) } if path != expandUser(tt.logFile) { t.Errorf("Expected log path: %s - got: %s", tt.logFile, path) } } } func TestConfigLookups(t *testing.T) { tests := []struct { scope *Scope configFile string result []string }{ {NewScope(System, "ssh"), "sshd_config", []string{"/etc/ssh/sshd_config"}}, {NewScope(User, "ssh"), "sshd_config", []string{"/etc/ssh/sshd_config"}}, } for _, tt := range tests { r, err := tt.scope.LookupConfig(tt.configFile) if err != nil { t.Errorf("Error looking up config: %s", err) } if len(r) != 1 { fmt.Println(r) t.Fatalf("Expected 1 result, got %d results", len(r)) } if r[0] != tt.result[0] { t.Errorf("Expected config file: %s - got: %s", tt.result[0], r[0]) } } } func TestDataLookups(t *testing.T) { tests := []struct { scope *Scope dataFile string result []string }{ {NewVendorScope(System, "terminfo", "x"), "xterm+256color", []string{"/usr/share/terminfo/x/xterm+256color"}}, {NewVendorScope(User, "terminfo", "x"), "xterm+256color", []string{"/usr/share/terminfo/x/xterm+256color"}}, } for _, tt := range tests { r, err := tt.scope.LookupDataFile(tt.dataFile) if err != nil { t.Errorf("Error looking up data file: %s", err) } if len(r) != 1 { fmt.Println(r) t.Fatalf("Expected 1 result, got %d results", len(r)) } if r[0] != tt.result[0] { t.Errorf("Expected data file: %s - got: %s", tt.result[0], r[0]) } } } go-app-paths-0.2.1/paths_windows.go000066400000000000000000000066231363314472500172510ustar00rootroot00000000000000// +build windows package gap import ( "path/filepath" "sync" "syscall" "unsafe" ) // These are KNOWNFOLDERID constants that are passed to GetKnownFolderPath var ( initOnce sync.Once initError error getKnownFolderPath uintptr coTaskMemFree uintptr rfidLocalAppData = syscall.GUID{ 0xf1b32785, 0x6fba, 0x4fcf, [8]byte{0x9d, 0x55, 0x7b, 0x8e, 0x7f, 0x15, 0x70, 0x91}, } rfidRoamingAppData = syscall.GUID{ 0x3eb685db, 0x65f9, 0x4cf6, [8]byte{0xa0, 0x3a, 0xe3, 0xef, 0x65, 0x72, 0x9f, 0x3d}, } rfidProgramData = syscall.GUID{ 0x62ab5d82, 0xfdc1, 0x4dc3, [8]byte{0xa9, 0xdd, 0x07, 0x0d, 0x1d, 0x49, 0x5d, 0x97}, } ) // appendPaths appends the app-name and further variadic parts to a path func (s *Scope) appendPaths(path string, parts ...string) string { paths := []string{path} paths = append(paths, parts...) return filepath.Join(paths...) } // dataDir returns the full path to the data directory. func (s *Scope) dataDir() (string, error) { var rfid syscall.GUID switch s.Type { case System: rfid = rfidProgramData case User: rfid = rfidLocalAppData case CustomHome: return s.CustomHome, nil default: return "", ErrInvalidScope } path, err := getFolderPath(rfid) if err != nil { return "", ErrRetrievingPath } if path, err = filepath.Abs(path); err != nil { return "", ErrRetrievingPath } return filepath.Join(path, s.Vendor, s.App), nil } // dataDirs returns a priority-sorted slice of data dirs. func (s *Scope) dataDirs() ([]string, error) { p, err := s.dataDir() if err != nil { return nil, err } return []string{p}, nil } // configDir returns the full path to the config dir. func (s *Scope) configDir() (string, error) { p, err := s.dataDir() if err != nil { return p, err } return filepath.Join(p, "Config"), nil } // configDirs returns a priority-sorted slice of config dirs. func (s *Scope) configDirs() ([]string, error) { p, err := s.configDir() if err != nil { return nil, err } return []string{p}, nil } // cacheDir returns the full path to the cache directory. func (s *Scope) cacheDir() (string, error) { p, err := s.dataDir() if err != nil { return p, err } return filepath.Join(p, "Cache"), nil } // logDir returns the full path to the log dir. func (s *Scope) logDir() (string, error) { p, err := s.dataDir() if err != nil { return p, err } return filepath.Join(p, "Logs"), nil } func getFolderPath(rfid syscall.GUID) (string, error) { initOnce.Do(initDLL) if initError != nil { return "", initError } var res uintptr ret, _, callErr := syscall.Syscall6( uintptr(getKnownFolderPath), 4, uintptr(unsafe.Pointer(&rfid)), 0, 0, uintptr(unsafe.Pointer(&res)), 0, 0, ) if callErr != 0 && ret != 0 { return "", callErr } defer syscall.Syscall(uintptr(coTaskMemFree), 1, res, 0, 0) return ucs2PtrToString(res), nil } func ucs2PtrToString(p uintptr) string { ptr := (*[4096]uint16)(unsafe.Pointer(p)) return syscall.UTF16ToString((*ptr)[:]) } func initDLL() { shell32, err := syscall.LoadLibrary("shell32.dll") if err != nil { initError = err return } getKnownFolderPath, err = syscall.GetProcAddress(shell32, "SHGetKnownFolderPath") if err != nil { initError = err return } ole32, err := syscall.LoadLibrary("Ole32.dll") if err != nil { initError = err return } coTaskMemFree, err = syscall.GetProcAddress(ole32, "CoTaskMemFree") if err != nil { initError = err return } } go-app-paths-0.2.1/paths_windows_test.go000066400000000000000000000054161363314472500203070ustar00rootroot00000000000000// +build windows package gap import ( "testing" ) func TestPaths(t *testing.T) { tests := []struct { scope *Scope dataDir string cacheDir string configFile string dataFile string logFile string }{ { scope: NewScope(System, "foobar"), dataDir: "C:\\ProgramData\\foobar", cacheDir: "C:\\ProgramData\\foobar\\Cache", configFile: "C:\\ProgramData\\foobar\\Config\\foobar.conf", dataFile: "C:\\ProgramData\\foobar\\foobar.data", logFile: "C:\\ProgramData\\foobar\\Logs\\foobar.log", }, { scope: NewVendorScope(System, "barcorp", "foobar"), dataDir: "C:\\ProgramData\\barcorp\\foobar", cacheDir: "C:\\ProgramData\\barcorp\\foobar\\Cache", configFile: "C:\\ProgramData\\barcorp\\foobar\\Config\\foobar.conf", dataFile: "C:\\ProgramData\\barcorp\\foobar\\foobar.data", logFile: "C:\\ProgramData\\barcorp\\foobar\\Logs\\foobar.log", }, { scope: NewScope(User, "foobar"), dataDir: "C:\\Users\\runneradmin\\AppData\\Local\\foobar", cacheDir: "C:\\Users\\runneradmin\\AppData\\Local\\foobar\\Cache", configFile: "C:\\Users\\runneradmin\\AppData\\Local\\foobar\\Config\\foobar.conf", dataFile: "C:\\Users\\runneradmin\\AppData\\Local\\foobar\\foobar.data", logFile: "C:\\Users\\runneradmin\\AppData\\Local\\foobar\\Logs\\foobar.log", }, { scope: NewCustomHomeScope("C:\\tmp", "", "foobar"), dataDir: "C:\\tmp", cacheDir: "C:\\tmp\\Cache", configFile: "C:\\tmp\\Config\\foobar.conf", dataFile: "C:\\tmp\\foobar.data", logFile: "C:\\tmp\\Logs\\foobar.log", }, } for _, tt := range tests { paths, err := tt.scope.DataDirs() if err != nil { t.Errorf("Error retrieving data dir: %s", err) } if paths[0] != expandUser(tt.dataDir) { t.Errorf("Expected data dir: %s - got: %s", tt.dataDir, paths[0]) } path, err := tt.scope.CacheDir() if err != nil { t.Errorf("Error retrieving cache dir: %s", err) } if path != expandUser(tt.cacheDir) { t.Errorf("Expected cache dir: %s - got: %s", tt.cacheDir, path) } path, err = tt.scope.ConfigPath(tt.scope.App + ".conf") if err != nil { t.Errorf("Error retrieving config path: %s", err) } if path != expandUser(tt.configFile) { t.Errorf("Expected config path: %s - got: %s", tt.configFile, path) } path, err = tt.scope.DataPath(tt.scope.App + ".data") if err != nil { t.Errorf("Error retrieving data path: %s", err) } if path != expandUser(tt.dataFile) { t.Errorf("Expected data path: %s - got: %s", tt.dataFile, path) } path, err = tt.scope.LogPath(tt.scope.App + ".log") if err != nil { t.Errorf("Error retrieving log path: %s", err) } if path != expandUser(tt.logFile) { t.Errorf("Expected log path: %s - got: %s", tt.logFile, path) } } }