rewrote config file path search (#219)
added comment to clarify coding choices added package xdg to vendor folder rewrote config file path search Co-authored-by: crapStone <crapstone01@gmail.com> Reviewed-on: https://gitea.com/gitea/tea/pulls/219 Reviewed-by: 6543 <6543@noreply.gitea.io> Reviewed-by: Norwin <noerw@noreply.gitea.io>
This commit is contained in:
parent
cbd1bccbf9
commit
c4e2db32b5
1
go.mod
1
go.mod
|
@ -6,6 +6,7 @@ require (
|
|||
code.gitea.io/gitea-vet v0.2.0
|
||||
code.gitea.io/sdk/gitea v0.13.0
|
||||
github.com/AlecAivazis/survey/v2 v2.1.1
|
||||
github.com/adrg/xdg v0.2.1
|
||||
github.com/araddon/dateparse v0.0.0-20200409225146-d820a6159ab1
|
||||
github.com/charmbracelet/glamour v0.2.0
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
||||
|
|
2
go.sum
2
go.sum
|
@ -7,6 +7,8 @@ github.com/AlecAivazis/survey/v2 v2.1.1/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0T
|
|||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
|
||||
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
|
||||
github.com/adrg/xdg v0.2.1 h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=
|
||||
github.com/adrg/xdg v0.2.1/go.mod h1:ZuOshBmzV4Ta+s23hdfFZnBsdzmoR3US0d7ErpqSbTQ=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
||||
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U=
|
||||
|
|
|
@ -9,13 +9,13 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/tea/modules/git"
|
||||
"code.gitea.io/tea/modules/utils"
|
||||
|
||||
"github.com/adrg/xdg"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
|
@ -27,28 +27,33 @@ type LocalConfig struct {
|
|||
var (
|
||||
// Config contain if loaded local tea config
|
||||
Config LocalConfig
|
||||
yamlConfigPath string
|
||||
)
|
||||
|
||||
// TODO: do not use init function to detect the tea configuration, use GetConfigPath()
|
||||
func init() {
|
||||
homeDir, err := utils.Home()
|
||||
if err != nil {
|
||||
log.Fatal("Retrieve home dir failed")
|
||||
}
|
||||
|
||||
dir := filepath.Join(homeDir, ".tea")
|
||||
err = os.MkdirAll(dir, os.ModePerm)
|
||||
if err != nil {
|
||||
log.Fatal("Init tea config dir " + dir + " failed")
|
||||
}
|
||||
|
||||
yamlConfigPath = filepath.Join(dir, "tea.yml")
|
||||
}
|
||||
|
||||
// GetConfigPath return path to tea config file
|
||||
func GetConfigPath() string {
|
||||
return yamlConfigPath
|
||||
configFilePath, err := xdg.ConfigFile("tea/config.yml")
|
||||
|
||||
var exists bool
|
||||
if err != nil {
|
||||
exists = false
|
||||
} else {
|
||||
exists, _ = utils.PathExists(configFilePath)
|
||||
}
|
||||
|
||||
// fallback to old config if no new one exists
|
||||
if !exists {
|
||||
file := filepath.Join(xdg.Home, ".tea", "tea.yml")
|
||||
exists, _ = utils.PathExists(file)
|
||||
if exists {
|
||||
return file
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("unable to get or create config file")
|
||||
}
|
||||
|
||||
return configFilePath
|
||||
}
|
||||
|
||||
// LoadConfig load config into global Config var
|
||||
|
@ -58,12 +63,12 @@ func LoadConfig() error {
|
|||
if exist {
|
||||
bs, err := ioutil.ReadFile(ymlPath)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("Failed to read config file: %s", ymlPath)
|
||||
}
|
||||
|
||||
err = yaml.Unmarshal(bs, &Config)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("Failed to parse contents of config file: %s", ymlPath)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -131,7 +131,7 @@ func AddLogin(name, token, user, passwd, sshKey, giteaURL string, insecure bool)
|
|||
|
||||
err := LoadConfig()
|
||||
if err != nil {
|
||||
log.Fatal("Unable to load config file " + yamlConfigPath)
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for _, l := range Config.Logins {
|
||||
|
@ -253,7 +253,7 @@ func InitCommand(repoValue, loginValue, remoteValue string) (*Login, string, str
|
|||
|
||||
err := LoadConfig()
|
||||
if err != nil {
|
||||
log.Fatal("load config file failed ", yamlConfigPath)
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if login, err = GetDefaultLogin(); err != nil {
|
||||
|
@ -287,7 +287,7 @@ func InitCommand(repoValue, loginValue, remoteValue string) (*Login, string, str
|
|||
func InitCommandLoginOnly(loginValue string) *Login {
|
||||
err := LoadConfig()
|
||||
if err != nil {
|
||||
log.Fatal("load config file failed ", yamlConfigPath)
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var login *Login
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
// Copyright 2018 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Home returns the home directory for the executing user.
|
||||
//
|
||||
// This uses an OS-specific method for discovering the home directory.
|
||||
// An error is returned if a home directory cannot be detected.
|
||||
func Home() (string, error) {
|
||||
user, err := user.Current()
|
||||
if nil == err {
|
||||
return user.HomeDir, nil
|
||||
}
|
||||
|
||||
// cross compile support
|
||||
if "windows" == runtime.GOOS {
|
||||
return homeWindows()
|
||||
}
|
||||
|
||||
// Unix-like system, so just assume Unix
|
||||
return homeUnix()
|
||||
}
|
||||
|
||||
func homeUnix() (string, error) {
|
||||
// First prefer the HOME environmental variable
|
||||
if home := os.Getenv("HOME"); home != "" {
|
||||
return home, nil
|
||||
}
|
||||
|
||||
// If that fails, try getent
|
||||
var stdout bytes.Buffer
|
||||
cmd := exec.Command("getent", "passwd", strconv.Itoa(os.Getuid()))
|
||||
cmd.Stdout = &stdout
|
||||
if err := cmd.Run(); err != nil {
|
||||
// If the error is ErrNotFound, we ignore it. Otherwise, return it.
|
||||
if err != exec.ErrNotFound {
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
if passwd := strings.TrimSpace(stdout.String()); passwd != "" {
|
||||
// username:password:uid:gid:gecos:home:shell
|
||||
passwdParts := strings.SplitN(passwd, ":", 7)
|
||||
if len(passwdParts) > 5 {
|
||||
return passwdParts[5], nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If all else fails, try the shell
|
||||
stdout.Reset()
|
||||
cmd = exec.Command("sh", "-c", "cd && pwd")
|
||||
cmd.Stdout = &stdout
|
||||
if err := cmd.Run(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
result := strings.TrimSpace(stdout.String())
|
||||
if result == "" {
|
||||
return "", errors.New("blank output when reading home directory")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func homeWindows() (string, error) {
|
||||
// First prefer the HOME environmental variable
|
||||
if home := os.Getenv("HOME"); home != "" {
|
||||
return home, nil
|
||||
}
|
||||
|
||||
drive := os.Getenv("HOMEDRIVE")
|
||||
path := os.Getenv("HOMEPATH")
|
||||
home := drive + path
|
||||
if drive == "" || path == "" {
|
||||
home = os.Getenv("USERPROFILE")
|
||||
}
|
||||
if home == "" {
|
||||
return "", errors.New("HOMEDRIVE, HOMEPATH, and USERPROFILE are blank")
|
||||
}
|
||||
|
||||
return home, nil
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
language: go
|
||||
go:
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
- 1.13.x
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
- windows
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
git:
|
||||
autocrlf: false
|
||||
before_install:
|
||||
- go get -t -v ./...
|
||||
- go install github.com/golangci/golangci-lint/cmd/golangci-lint
|
||||
script:
|
||||
- golangci-lint run --enable-all -D wsl -D gochecknoinits -D gochecknoglobals -D prealloc
|
||||
- go test -v -race ./...
|
|
@ -0,0 +1,77 @@
|
|||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age,
|
||||
body size, disability, ethnicity, sex characteristics, gender identity and
|
||||
expression, level of experience, education, socio-economic status, nationality,
|
||||
personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behaviour that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behaviour by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behaviour and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behaviour.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviour that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behaviour may be
|
||||
reported by contacting the project team at adrg@epistack.com. All complaints
|
||||
will be reviewed and investigated and will result in a response that is deemed
|
||||
necessary and appropriate to the circumstances. The project team is obligated to
|
||||
maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 1.4, available at
|
||||
https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
|
@ -0,0 +1,135 @@
|
|||
# Contributing to this project
|
||||
|
||||
Contributions in the form of pull requests, issues or just general feedback,
|
||||
are always welcome. Please take a moment to review this document in order to
|
||||
make the contribution process easy and effective for everyone involved.
|
||||
|
||||
Following these guidelines helps to communicate that you respect the time of
|
||||
the developers managing and developing this open source project. In return,
|
||||
they should reciprocate that respect in addressing your issue or assessing
|
||||
patches and features.
|
||||
|
||||
## Using the issue tracker
|
||||
|
||||
The issue tracker is the preferred channel for [bug reports](#bugs),
|
||||
[features requests](#features) and [submitting pull
|
||||
requests](#pull-requests), but please respect the following restrictions:
|
||||
|
||||
* Please **do not** use the issue tracker for personal support requests (use
|
||||
[Stack Overflow](http://stackoverflow.com) or IRC).
|
||||
* Please **do not** derail or troll issues. Keep the discussion on topic and
|
||||
respect the opinions of others.
|
||||
|
||||
<a name="bugs"></a>
|
||||
## Bug reports
|
||||
|
||||
A bug is a _demonstrable problem_ that is caused by the code in the repository.
|
||||
Good bug reports are extremely helpful - thank you!
|
||||
|
||||
Guidelines for bug reports:
|
||||
|
||||
1. **Use the GitHub issue search** — check if the issue has already been
|
||||
reported.
|
||||
2. **Check if the issue has been fixed** — try to reproduce it using the
|
||||
latest `master` or development branch in the repository.
|
||||
3. **Isolate the problem** — create a reduced test case.
|
||||
|
||||
A good bug report shouldn't leave others needing to chase you up for more
|
||||
information. Please try to be as detailed as possible in your report. What is
|
||||
your environment? What steps will reproduce the issue? What browser(s) and OS
|
||||
experience the problem? What would you expect to be the outcome? All these
|
||||
details will help people to fix any potential bugs.
|
||||
|
||||
Example:
|
||||
|
||||
> Short and descriptive example bug report title
|
||||
>
|
||||
> A summary of the issue and the browser/OS environment in which it occurs. If
|
||||
> suitable, include the steps required to reproduce the bug.
|
||||
>
|
||||
> 1. This is the first step
|
||||
> 2. This is the second step
|
||||
> 3. Further steps, etc.
|
||||
>
|
||||
> `<url>` - a link to the reduced test case
|
||||
>
|
||||
> Any other information you want to share that is relevant to the issue being
|
||||
> reported. This might include the lines of code that you have identified as
|
||||
> causing the bug, and potential solutions (and your opinions on their
|
||||
> merits).
|
||||
|
||||
|
||||
<a name="features"></a>
|
||||
## Feature requests
|
||||
|
||||
Feature requests are welcome. But take a moment to find out whether your idea
|
||||
fits with the scope and aims of the project. It's up to *you* to make a strong
|
||||
case to convince the project's developers of the merits of this feature. Please
|
||||
provide as much detail and context as possible.
|
||||
|
||||
|
||||
<a name="pull-requests"></a>
|
||||
## Pull requests
|
||||
|
||||
Good pull requests - patches, improvements, new features - are a fantastic
|
||||
help. They should remain focused in scope and avoid containing unrelated
|
||||
commits.
|
||||
|
||||
**Please ask first** before embarking on any significant pull request (e.g.
|
||||
implementing features, refactoring code, porting to a different language),
|
||||
otherwise you risk spending a lot of time working on something that the
|
||||
project's developers might not want to merge into the project.
|
||||
|
||||
Please adhere to the coding conventions used throughout a project (indentation,
|
||||
accurate comments, etc.) and any other requirements (such as test coverage).
|
||||
|
||||
Follow this process if you'd like your work considered for inclusion in the
|
||||
project:
|
||||
|
||||
1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork,
|
||||
and configure the remotes:
|
||||
|
||||
```bash
|
||||
# Clone your fork of the repo into the current directory
|
||||
git clone https://github.com/<your-username>/<repo-name>
|
||||
# Navigate to the newly cloned directory
|
||||
cd <repo-name>
|
||||
# Assign the original repo to a remote called "upstream"
|
||||
git remote add upstream https://github.com/<upstream-owner>/<repo-name>
|
||||
```
|
||||
|
||||
2. If you cloned a while ago, get the latest changes from upstream:
|
||||
|
||||
```bash
|
||||
git checkout <dev-branch>
|
||||
git pull upstream <dev-branch>
|
||||
```
|
||||
|
||||
3. Create a new topic branch (off the main project development branch) to
|
||||
contain your feature, change, or fix:
|
||||
|
||||
```bash
|
||||
git checkout -b <topic-branch-name>
|
||||
```
|
||||
|
||||
4. Commit your changes in logical chunks and use descriptive commit messages.
|
||||
Use [interactive rebase](https://help.github.com/articles/interactive-rebase)
|
||||
to tidy up your commits before making them public.
|
||||
|
||||
5. Locally merge (or rebase) the upstream development branch into your topic branch:
|
||||
|
||||
```bash
|
||||
git pull [--rebase] upstream <dev-branch>
|
||||
```
|
||||
|
||||
6. Push your topic branch up to your fork:
|
||||
|
||||
```bash
|
||||
git push origin <topic-branch-name>
|
||||
```
|
||||
|
||||
7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/)
|
||||
with a clear title and description.
|
||||
|
||||
**IMPORTANT**: By submitting a patch, you agree to allow the project owner to
|
||||
license your work under the same license as that used by the project.
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Adrian-George Bostan <adrg@epistack.com>
|
||||
|
||||
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.
|
|
@ -0,0 +1,187 @@
|
|||
xdg
|
||||
===
|
||||
[![Build Status](https://travis-ci.org/adrg/xdg.svg?branch=master)](https://travis-ci.org/adrg/xdg)
|
||||
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/adrg/xdg)
|
||||
[![License: MIT](https://img.shields.io/badge/license-MIT-red.svg?style=flat-square)](https://opensource.org/licenses/MIT)
|
||||
[![Go Report Card](https://goreportcard.com/badge/github.com/adrg/xdg)](https://goreportcard.com/report/github.com/adrg/xdg)
|
||||
|
||||
Provides an implementation of the [XDG Base Directory Specification](https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html).
|
||||
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.
|
||||
The current implementation supports Windows, Mac OS and most flavors of Unix.
|
||||
|
||||
Full documentation can be found at: https://godoc.org/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 | Mac OS | Windows |
|
||||
| :--- | :--- | :----- | :--- |
|
||||
| XDG_DATA_HOME | `~/.local/share` | `~/Library/Application Support` | `%LOCALAPPDATA%` |
|
||||
| XDG_DATA_DIRS | `/usr/local/share`<br/>`/usr/share` | `/Library/Application Support` | `%APPDATA%\Roaming`<br/>`%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 | Mac OS | 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
|
||||
|
||||
Application directories
|
||||
|
||||
```
|
||||
Unix:
|
||||
- $XDG_DATA_HOME/applications
|
||||
- ~/.local/share/applications
|
||||
- /usr/local/share/applications
|
||||
- /usr/share/applications
|
||||
- $XDG_DATA_DIRS/applications
|
||||
|
||||
Mac OS:
|
||||
- /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
|
||||
|
||||
Mac OS:
|
||||
- ~/Library/Fonts
|
||||
- /Library/Fonts
|
||||
- /System/Library/Fonts
|
||||
- /Network/Library/Fonts
|
||||
|
||||
Windows:
|
||||
- %windir%\Fonts
|
||||
- %LOCALAPPDATA%\Microsoft\Windows\Fonts
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
#### XDG Base Directory
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/adrg/xdg"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// XDG Base Directory paths.
|
||||
log.Println("Home config 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("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 exists, 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()
|
||||
|
||||
// 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 user directories
|
||||
|
||||
```go
|
||||
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)
|
||||
}
|
||||
```
|
||||
|
||||
## References
|
||||
For more information see the
|
||||
[XDG Base Directory Specification](https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html) and
|
||||
[XDG user directories](https://wiki.archlinux.org/index.php/XDG_user_directories).
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions in the form of pull requests, issues or just general feedback,
|
||||
are always welcome.
|
||||
See [CONTRIBUTING.MD](https://github.com/adrg/xdg/blob/master/CONTRIBUTING.md).
|
||||
|
||||
## License
|
||||
Copyright (c) 2014 Adrian-George Bostan.
|
||||
|
||||
This project is licensed under the [MIT license](https://opensource.org/licenses/MIT).
|
||||
See [LICENSE](https://github.com/adrg/xdg/blob/master/LICENSE) for more details.
|
|
@ -0,0 +1,78 @@
|
|||
package xdg
|
||||
|
||||
import "os"
|
||||
|
||||
// XDG Base Directory environment variables.
|
||||
var (
|
||||
envDataHome = "XDG_DATA_HOME"
|
||||
envDataDirs = "XDG_DATA_DIRS"
|
||||
envConfigHome = "XDG_CONFIG_HOME"
|
||||
envConfigDirs = "XDG_CONFIG_DIRS"
|
||||
envCacheHome = "XDG_CACHE_HOME"
|
||||
envRuntimeDir = "XDG_RUNTIME_DIR"
|
||||
)
|
||||
|
||||
type baseDirectories struct {
|
||||
dataHome string
|
||||
data []string
|
||||
configHome string
|
||||
config []string
|
||||
cacheHome string
|
||||
runtime string
|
||||
|
||||
// Non-standard directories.
|
||||
fonts []string
|
||||
applications []string
|
||||
}
|
||||
|
||||
func (bd baseDirectories) dataFile(relPath string) (string, error) {
|
||||
return createPath(relPath, append([]string{bd.dataHome}, bd.data...))
|
||||
}
|
||||
|
||||
func (bd baseDirectories) configFile(relPath string) (string, error) {
|
||||
return createPath(relPath, append([]string{bd.configHome}, bd.config...))
|
||||
}
|
||||
|
||||
func (bd baseDirectories) cacheFile(relPath string) (string, error) {
|
||||
return createPath(relPath, []string{bd.cacheHome})
|
||||
}
|
||||
|
||||
func (bd baseDirectories) runtimeFile(relPath string) (string, error) {
|
||||
fi, err := os.Lstat(bd.runtime)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return createPath(relPath, []string{bd.runtime})
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
if fi.IsDir() {
|
||||
// The runtime directory must be owned by the user.
|
||||
if err = os.Chown(bd.runtime, os.Getuid(), os.Getgid()); err != nil {
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
// For security reasons, the runtime directory cannot be a symlink.
|
||||
if err = os.Remove(bd.runtime); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
return createPath(relPath, []string{bd.runtime})
|
||||
}
|
||||
|
||||
func (bd baseDirectories) searchDataFile(relPath string) (string, error) {
|
||||
return searchFile(relPath, append([]string{bd.dataHome}, bd.data...))
|
||||
}
|
||||
|
||||
func (bd baseDirectories) searchConfigFile(relPath string) (string, error) {
|
||||
return searchFile(relPath, append([]string{bd.configHome}, bd.config...))
|
||||
}
|
||||
|
||||
func (bd baseDirectories) searchCacheFile(relPath string) (string, error) {
|
||||
return searchFile(relPath, []string{bd.cacheHome})
|
||||
}
|
||||
|
||||
func (bd baseDirectories) searchRuntimeFile(relPath string) (string, error) {
|
||||
return searchFile(relPath, []string{bd.runtime})
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
module github.com/adrg/xdg
|
|
@ -0,0 +1,37 @@
|
|||
package xdg
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func initBaseDirs(home string) {
|
||||
// Initialize base directories.
|
||||
baseDirs.dataHome = xdgPath(envDataHome, filepath.Join(home, "Library", "Application Support"))
|
||||
baseDirs.data = xdgPaths(envDataDirs, "/Library/Application Support")
|
||||
baseDirs.configHome = xdgPath(envConfigHome, filepath.Join(home, "Library", "Preferences"))
|
||||
baseDirs.config = xdgPaths(envConfigDirs, "/Library/Preferences")
|
||||
baseDirs.cacheHome = xdgPath(envCacheHome, filepath.Join(home, "Library", "Caches"))
|
||||
baseDirs.runtime = xdgPath(envRuntimeDir, filepath.Join(home, "Library", "Application Support"))
|
||||
|
||||
// Initialize non-standard directories.
|
||||
baseDirs.applications = []string{
|
||||
"/Applications",
|
||||
}
|
||||
baseDirs.fonts = []string{
|
||||
filepath.Join(home, "Library/Fonts"),
|
||||
"/Library/Fonts",
|
||||
"/System/Library/Fonts",
|
||||
"/Network/Library/Fonts",
|
||||
}
|
||||
}
|
||||
|
||||
func initUserDirs(home string) {
|
||||
UserDirs.Desktop = xdgPath(envDesktopDir, filepath.Join(home, "Desktop"))
|
||||
UserDirs.Download = xdgPath(envDownloadDir, filepath.Join(home, "Downloads"))
|
||||
UserDirs.Documents = xdgPath(envDocumentsDir, filepath.Join(home, "Documents"))
|
||||
UserDirs.Music = xdgPath(envMusicDir, filepath.Join(home, "Music"))
|
||||
UserDirs.Pictures = xdgPath(envPicturesDir, filepath.Join(home, "Pictures"))
|
||||
UserDirs.Videos = xdgPath(envVideosDir, filepath.Join(home, "Movies"))
|
||||
UserDirs.Templates = xdgPath(envTemplatesDir, filepath.Join(home, "Templates"))
|
||||
UserDirs.PublicShare = xdgPath(envPublicShareDir, filepath.Join(home, "Public"))
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
// +build aix dragonfly freebsd linux nacl netbsd openbsd solaris
|
||||
|
||||
package xdg
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func initBaseDirs(home string) {
|
||||
// Initialize base directories.
|
||||
baseDirs.dataHome = xdgPath(envDataHome, filepath.Join(home, ".local", "share"))
|
||||
baseDirs.data = xdgPaths(envDataDirs, "/usr/local/share", "/usr/share")
|
||||
baseDirs.configHome = xdgPath(envConfigHome, filepath.Join(home, ".config"))
|
||||
baseDirs.config = xdgPaths(envConfigDirs, "/etc/xdg")
|
||||
baseDirs.cacheHome = xdgPath(envCacheHome, filepath.Join(home, ".cache"))
|
||||
baseDirs.runtime = xdgPath(envRuntimeDir, filepath.Join("/run/user", strconv.Itoa(os.Getuid())))
|
||||
|
||||
// Initialize non-standard directories.
|
||||
appDirs := []string{
|
||||
filepath.Join(baseDirs.dataHome, "applications"),
|
||||
filepath.Join(home, ".local/share/applications"),
|
||||
"/usr/local/share/applications",
|
||||
"/usr/share/applications",
|
||||
}
|
||||
|
||||
fontDirs := []string{
|
||||
filepath.Join(baseDirs.dataHome, "fonts"),
|
||||
filepath.Join(home, ".fonts"),
|
||||
filepath.Join(home, ".local/share/fonts"),
|
||||
"/usr/local/share/fonts",
|
||||
"/usr/share/fonts",
|
||||
}
|
||||
|
||||
for _, dir := range baseDirs.data {
|
||||
appDirs = append(appDirs, filepath.Join(dir, "applications"))
|
||||
fontDirs = append(fontDirs, filepath.Join(dir, "fonts"))
|
||||
}
|
||||
|
||||
baseDirs.applications = uniquePaths(appDirs)
|
||||
baseDirs.fonts = uniquePaths(fontDirs)
|
||||
}
|
||||
|
||||
func initUserDirs(home string) {
|
||||
UserDirs.Desktop = xdgPath(envDesktopDir, filepath.Join(home, "Desktop"))
|
||||
UserDirs.Download = xdgPath(envDownloadDir, filepath.Join(home, "Downloads"))
|
||||
UserDirs.Documents = xdgPath(envDocumentsDir, filepath.Join(home, "Documents"))
|
||||
UserDirs.Music = xdgPath(envMusicDir, filepath.Join(home, "Music"))
|
||||
UserDirs.Pictures = xdgPath(envPicturesDir, filepath.Join(home, "Pictures"))
|
||||
UserDirs.Videos = xdgPath(envVideosDir, filepath.Join(home, "Videos"))
|
||||
UserDirs.Templates = xdgPath(envTemplatesDir, filepath.Join(home, "Templates"))
|
||||
UserDirs.PublicShare = xdgPath(envPublicShareDir, filepath.Join(home, "Public"))
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package xdg
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func initBaseDirs(home string) {
|
||||
appDataDir := os.Getenv("APPDATA")
|
||||
if appDataDir == "" {
|
||||
appDataDir = filepath.Join(home, "AppData")
|
||||
}
|
||||
roamingAppDataDir := filepath.Join(appDataDir, "Roaming")
|
||||
|
||||
localAppDataDir := os.Getenv("LOCALAPPDATA")
|
||||
if localAppDataDir == "" {
|
||||
localAppDataDir = filepath.Join(appDataDir, "Local")
|
||||
}
|
||||
|
||||
programDataDir := os.Getenv("PROGRAMDATA")
|
||||
if programDataDir == "" {
|
||||
if systemDrive := os.Getenv("SystemDrive"); systemDrive != "" {
|
||||
programDataDir = filepath.Join(systemDrive, "ProgramData")
|
||||
} else {
|
||||
programDataDir = home
|
||||
}
|
||||
}
|
||||
|
||||
winDir := os.Getenv("windir")
|
||||
if winDir == "" {
|
||||
winDir = os.Getenv("SystemRoot")
|
||||
if winDir == "" {
|
||||
winDir = home
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize base directories.
|
||||
baseDirs.dataHome = xdgPath(envDataHome, localAppDataDir)
|
||||
baseDirs.data = xdgPaths(envDataDirs, roamingAppDataDir, programDataDir)
|
||||
baseDirs.configHome = xdgPath(envConfigHome, localAppDataDir)
|
||||
baseDirs.config = xdgPaths(envConfigDirs, programDataDir)
|
||||
baseDirs.cacheHome = xdgPath(envCacheHome, filepath.Join(localAppDataDir, "cache"))
|
||||
baseDirs.runtime = xdgPath(envRuntimeDir, localAppDataDir)
|
||||
|
||||
// Initialize non-standard directories.
|
||||
baseDirs.applications = []string{
|
||||
filepath.Join(roamingAppDataDir, "Microsoft", "Windows", "Start Menu", "Programs"),
|
||||
}
|
||||
baseDirs.fonts = []string{
|
||||
filepath.Join(winDir, "Fonts"),
|
||||
filepath.Join(localAppDataDir, "Microsoft", "Windows", "Fonts"),
|
||||
}
|
||||
}
|
||||
|
||||
func initUserDirs(home string) {
|
||||
publicDir := os.Getenv("PUBLIC")
|
||||
if publicDir == "" {
|
||||
publicDir = filepath.Join(home, "Public")
|
||||
}
|
||||
|
||||
UserDirs.Desktop = xdgPath(envDesktopDir, filepath.Join(home, "Desktop"))
|
||||
UserDirs.Download = xdgPath(envDownloadDir, filepath.Join(home, "Downloads"))
|
||||
UserDirs.Documents = xdgPath(envDocumentsDir, filepath.Join(home, "Documents"))
|
||||
UserDirs.Music = xdgPath(envMusicDir, filepath.Join(home, "Music"))
|
||||
UserDirs.Pictures = xdgPath(envPicturesDir, filepath.Join(home, "Pictures"))
|
||||
UserDirs.Videos = xdgPath(envVideosDir, filepath.Join(home, "Videos"))
|
||||
UserDirs.Templates = xdgPath(envTemplatesDir, filepath.Join(home, "Templates"))
|
||||
UserDirs.PublicShare = xdgPath(envPublicShareDir, publicDir)
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package xdg
|
||||
|
||||
// XDG user directories environment variables.
|
||||
var (
|
||||
envDesktopDir = "XDG_DESKTOP_DIR"
|
||||
envDownloadDir = "XDG_DOWNLOAD_DIR"
|
||||
envDocumentsDir = "XDG_DOCUMENTS_DIR"
|
||||
envMusicDir = "XDG_MUSIC_DIR"
|
||||
envPicturesDir = "XDG_PICTURES_DIR"
|
||||
envVideosDir = "XDG_VIDEOS_DIR"
|
||||
envTemplatesDir = "XDG_TEMPLATES_DIR"
|
||||
envPublicShareDir = "XDG_PUBLICSHARE_DIR"
|
||||
)
|
||||
|
||||
// UserDirectories defines the locations of well known user directories.
|
||||
type UserDirectories struct {
|
||||
// Desktop defines the location of the user's desktop directory.
|
||||
Desktop string
|
||||
|
||||
// Download defines a suitable location for user downloaded files.
|
||||
Download string
|
||||
|
||||
// Documents defines a suitable location for user document files.
|
||||
Documents string
|
||||
|
||||
// Music defines a suitable location for user audio files.
|
||||
Music string
|
||||
|
||||
// Pictures defines a suitable location for user image files.
|
||||
Pictures string
|
||||
|
||||
// VideosDir defines a suitable location for user video files.
|
||||
Videos string
|
||||
|
||||
// Templates defines a suitable location for user template files.
|
||||
Templates string
|
||||
|
||||
// PublicShare defines a suitable location for user shared files.
|
||||
PublicShare string
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
package xdg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func homeDir() string {
|
||||
homeEnv := "HOME"
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
homeEnv = "USERPROFILE"
|
||||
case "plan9":
|
||||
homeEnv = "home"
|
||||
}
|
||||
|
||||
if home := os.Getenv(homeEnv); home != "" {
|
||||
return home
|
||||
}
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "nacl":
|
||||
return "/"
|
||||
case "darwin":
|
||||
if runtime.GOARCH == "arm" || runtime.GOARCH == "arm64" {
|
||||
return "/"
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func exists(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return err == nil || os.IsExist(err)
|
||||
}
|
||||
|
||||
func expandPath(path, homeDir string) string {
|
||||
if path == "" || homeDir == "" {
|
||||
return path
|
||||
}
|
||||
if path[0] == '~' {
|
||||
return filepath.Join(homeDir, path[1:])
|
||||
}
|
||||
if strings.HasPrefix(path, "$HOME") {
|
||||
return filepath.Join(homeDir, path[5:])
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
func createPath(name string, paths []string) (string, error) {
|
||||
var searchedPaths []string
|
||||
for _, p := range paths {
|
||||
path := filepath.Join(p, name)
|
||||
dir := filepath.Dir(path)
|
||||
|
||||
if exists(dir) {
|
||||
return path, nil
|
||||
}
|
||||
if err := os.MkdirAll(dir, os.ModeDir|0700); err == nil {
|
||||
return path, nil
|
||||
}
|
||||
|
||||
searchedPaths = append(searchedPaths, dir)
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("could not create any of the following paths: %s",
|
||||
strings.Join(searchedPaths, ", "))
|
||||
}
|
||||
|
||||
func searchFile(name string, paths []string) (string, error) {
|
||||
var searchedPaths []string
|
||||
for _, p := range paths {
|
||||
path := filepath.Join(p, name)
|
||||
if exists(path) {
|
||||
return path, nil
|
||||
}
|
||||
|
||||
searchedPaths = append(searchedPaths, filepath.Dir(path))
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("could not locate `%s` in any of the following paths: %s",
|
||||
filepath.Base(name), strings.Join(searchedPaths, ", "))
|
||||
}
|
||||
|
||||
func xdgPath(name, defaultPath string) string {
|
||||
dir := expandPath(os.Getenv(name), Home)
|
||||
if dir != "" && filepath.IsAbs(dir) {
|
||||
return dir
|
||||
}
|
||||
|
||||
return defaultPath
|
||||
}
|
||||
|
||||
func xdgPaths(name string, defaultPaths ...string) []string {
|
||||
dirs := uniquePaths(filepath.SplitList(os.Getenv(name)))
|
||||
if len(dirs) != 0 {
|
||||
return dirs
|
||||
}
|
||||
|
||||
return uniquePaths(defaultPaths)
|
||||
}
|
||||
|
||||
func uniquePaths(paths []string) []string {
|
||||
var uniq []string
|
||||
registry := map[string]struct{}{}
|
||||
|
||||
for _, p := range paths {
|
||||
dir := expandPath(p, Home)
|
||||
if dir == "" || !filepath.IsAbs(dir) {
|
||||
continue
|
||||
}
|
||||
if _, ok := registry[dir]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
registry[dir] = struct{}{}
|
||||
uniq = append(uniq, dir)
|
||||
}
|
||||
|
||||
return uniq
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
Package xdg 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. The current implementation supports Windows, Mac OS and most
|
||||
flavors of Unix.
|
||||
|
||||
For more information regarding the XDG Base Directory Specification see:
|
||||
https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||
|
||||
For more information regarding the XDG user directories see:
|
||||
https://wiki.archlinux.org/index.php/XDG_user_directories
|
||||
*/
|
||||
package xdg
|
||||
|
||||
var (
|
||||
// Home contains the path of the user's home directory.
|
||||
Home string
|
||||
|
||||
// DataHome defines the base directory relative to which user-specific
|
||||
// data files should be stored. This directory is defined by the
|
||||
// environment variable $XDG_DATA_HOME. If this variable is not set,
|
||||
// a default equal to $HOME/.local/share should be used.
|
||||
DataHome string
|
||||
|
||||
// DataDirs defines the preference-ordered set of base directories to
|
||||
// search for data files in addition to the DataHome base directory.
|
||||
// This set of directories is defined by the environment variable
|
||||
// $XDG_DATA_DIRS. If this variable is not set, the default directories
|
||||
// to be used are /usr/local/share and /usr/share, in that order. The
|
||||
// DataHome directory is considered more important than any of the
|
||||
// directories defined by DataDirs. Therefore, user data files should be
|
||||
// written relative to the DataHome directory, if possible.
|
||||
DataDirs []string
|
||||
|
||||
// ConfigHome defines the base directory relative to which user-specific
|
||||
// configuration files should be written. This directory is defined by
|
||||
// the environment variable $XDG_CONFIG_HOME. If this variable is not
|
||||
// not set, a default equal to $HOME/.config should be used.
|
||||
ConfigHome string
|
||||
|
||||
// ConfigDirs defines the preference-ordered set of base directories to
|
||||
// search for configuration files in addition to the ConfigHome base
|
||||
// directory. This set of directories is defined by the environment
|
||||
// variable $XDG_CONFIG_DIRS. If this variable is not set, a default
|
||||
// equal to /etc/xdg should be used. The ConfigHome directory is
|
||||
// considered more important than any of the directories defined by
|
||||
// ConfigDirs. Therefore, user config files should be written
|
||||
// relative to the ConfigHome directory, if possible.
|
||||
ConfigDirs []string
|
||||
|
||||
// CacheHome defines the base directory relative to which user-specific
|
||||
// non-essential (cached) data should be written. This directory is
|
||||
// defined by the environment variable $XDG_CACHE_HOME. If this variable
|
||||
// is not set, a default equal to $HOME/.cache should be used.
|
||||
CacheHome string
|
||||
|
||||
// RuntimeDir defines the base directory relative to which user-specific
|
||||
// non-essential runtime files and other file objects (such as sockets,
|
||||
// named pipes, etc.) should be stored. This directory is defined by the
|
||||
// environment variable $XDG_RUNTIME_DIR. If this variable is not set,
|
||||
// applications should fall back to a replacement directory with similar
|
||||
// capabilities. Applications should use this directory for communication
|
||||
// and synchronization purposes and should not place larger files in it,
|
||||
// since it might reside in runtime memory and cannot necessarily be
|
||||
// swapped out to disk.
|
||||
RuntimeDir string
|
||||
|
||||
// UserDirs defines the locations of well known user directories.
|
||||
UserDirs UserDirectories
|
||||
|
||||
// FontDirs defines the common locations where font files are stored.
|
||||
FontDirs []string
|
||||
|
||||
// ApplicationDirs defines the common locations of applications.
|
||||
ApplicationDirs []string
|
||||
|
||||
// baseDirs defines the locations of base directories.
|
||||
baseDirs baseDirectories
|
||||
)
|
||||
|
||||
// Reload refreshes base and user directories by reading the environment.
|
||||
// Defaults are applied for XDG variables which are empty or not present
|
||||
// in the environment.
|
||||
func Reload() {
|
||||
// Initialize home directory.
|
||||
Home = homeDir()
|
||||
|
||||
// Initialize base directories.
|
||||
initBaseDirs(Home)
|
||||
DataHome = baseDirs.dataHome
|
||||
DataDirs = baseDirs.data
|
||||
ConfigHome = baseDirs.configHome
|
||||
ConfigDirs = baseDirs.config
|
||||
CacheHome = baseDirs.cacheHome
|
||||
RuntimeDir = baseDirs.runtime
|
||||
FontDirs = baseDirs.fonts
|
||||
ApplicationDirs = baseDirs.applications
|
||||
|
||||
// Initialize user directories.
|
||||
initUserDirs(Home)
|
||||
}
|
||||
|
||||
// DataFile returns a suitable location for the specified data file.
|
||||
// The relPath parameter must contain the name of the data file, and
|
||||
// optionally, a set of parent directories (e.g. appname/app.data).
|
||||
// If the specified directories do not exist, they will be created relative
|
||||
// to the base data directory. On failure, an error containing the
|
||||
// attempted paths is returned.
|
||||
func DataFile(relPath string) (string, error) {
|
||||
return baseDirs.dataFile(relPath)
|
||||
}
|
||||
|
||||
// ConfigFile returns a suitable location for the specified config file.
|
||||
// The relPath parameter must contain the name of the config file, and
|
||||
// optionally, a set of parent directories (e.g. appname/app.yaml).
|
||||
// If the specified directories do not exist, they will be created relative
|
||||
// to the base config directory. On failure, an error containing the
|
||||
// attempted paths is returned.
|
||||
func ConfigFile(relPath string) (string, error) {
|
||||
return baseDirs.configFile(relPath)
|
||||
}
|
||||
|
||||
// CacheFile returns a suitable location for the specified cache file.
|
||||
// The relPath parameter must contain the name of the cache file, and
|
||||
// optionally, a set of parent directories (e.g. appname/app.cache).
|
||||
// If the specified directories do not exist, they will be created relative
|
||||
// to the base cache directory. On failure, an error containing the
|
||||
// attempted paths is returned.
|
||||
func CacheFile(relPath string) (string, error) {
|
||||
return baseDirs.cacheFile(relPath)
|
||||
}
|
||||
|
||||
// RuntimeFile returns a suitable location for the specified runtime file.
|
||||
// The relPath parameter must contain the name of the runtime file, and
|
||||
// optionally, a set of parent directories (e.g. appname/app.pid).
|
||||
// If the specified directories do not exist, they will be created relative
|
||||
// to the base runtime directory. On failure, an error containing the
|
||||
// attempted paths is returned.
|
||||
func RuntimeFile(relPath string) (string, error) {
|
||||
return baseDirs.runtimeFile(relPath)
|
||||
}
|
||||
|
||||
// SearchDataFile searches for specified file in the data search paths.
|
||||
// The relPath parameter must contain the name of the data file, and
|
||||
// optionally, a set of parent directories (e.g. appname/app.data). If the
|
||||
// file cannot be found, an error specifying the searched paths is returned.
|
||||
func SearchDataFile(relPath string) (string, error) {
|
||||
return baseDirs.searchDataFile(relPath)
|
||||
}
|
||||
|
||||
// SearchConfigFile searches for the specified file in config search paths.
|
||||
// The relPath parameter must contain the name of the config file, and
|
||||
// optionally, a set of parent directories (e.g. appname/app.yaml). If the
|
||||
// file cannot be found, an error specifying the searched paths is returned.
|
||||
func SearchConfigFile(relPath string) (string, error) {
|
||||
return baseDirs.searchConfigFile(relPath)
|
||||
}
|
||||
|
||||
// SearchCacheFile searches for the specified file in the cache search path.
|
||||
// The relPath parameter must contain the name of the cache file, and
|
||||
// optionally, a set of parent directories (e.g. appname/app.cache). If the
|
||||
// file cannot be found, an error specifying the searched path is returned.
|
||||
func SearchCacheFile(relPath string) (string, error) {
|
||||
return baseDirs.searchCacheFile(relPath)
|
||||
}
|
||||
|
||||
// SearchRuntimeFile searches for the specified file in the runtime search path.
|
||||
// The relPath parameter must contain the name of the runtime file, and
|
||||
// optionally, a set of parent directories (e.g. appname/app.pid). If the
|
||||
// file cannot be found, an error specifying the searched path is returned.
|
||||
func SearchRuntimeFile(relPath string) (string, error) {
|
||||
return baseDirs.searchRuntimeFile(relPath)
|
||||
}
|
||||
|
||||
func init() {
|
||||
Reload()
|
||||
}
|
|
@ -7,6 +7,8 @@ code.gitea.io/sdk/gitea
|
|||
github.com/AlecAivazis/survey/v2
|
||||
github.com/AlecAivazis/survey/v2/core
|
||||
github.com/AlecAivazis/survey/v2/terminal
|
||||
# github.com/adrg/xdg v0.2.1
|
||||
github.com/adrg/xdg
|
||||
# github.com/alecthomas/chroma v0.7.3
|
||||
github.com/alecthomas/chroma
|
||||
github.com/alecthomas/chroma/formatters
|
||||
|
|
Loading…
Reference in New Issue