1// Copyright (c) 2013-2017 The btcsuite developers
2// Use of this source code is governed by an ISC
3// license that can be found in the LICENSE file.
4
5package btcutil
6
7import (
8	"os"
9	"os/user"
10	"path/filepath"
11	"runtime"
12	"strings"
13	"unicode"
14)
15
16// appDataDir returns an operating system specific directory to be used for
17// storing application data for an application.  See AppDataDir for more
18// details.  This unexported version takes an operating system argument
19// primarily to enable the testing package to properly test the function by
20// forcing an operating system that is not the currently one.
21func appDataDir(goos, appName string, roaming bool) string {
22	if appName == "" || appName == "." {
23		return "."
24	}
25
26	// The caller really shouldn't prepend the appName with a period, but
27	// if they do, handle it gracefully by trimming it.
28	appName = strings.TrimPrefix(appName, ".")
29	appNameUpper := string(unicode.ToUpper(rune(appName[0]))) + appName[1:]
30	appNameLower := string(unicode.ToLower(rune(appName[0]))) + appName[1:]
31
32	// Get the OS specific home directory via the Go standard lib.
33	var homeDir string
34	usr, err := user.Current()
35	if err == nil {
36		homeDir = usr.HomeDir
37	}
38
39	// Fall back to standard HOME environment variable that works
40	// for most POSIX OSes if the directory from the Go standard
41	// lib failed.
42	if err != nil || homeDir == "" {
43		homeDir = os.Getenv("HOME")
44	}
45
46	switch goos {
47	// Attempt to use the LOCALAPPDATA or APPDATA environment variable on
48	// Windows.
49	case "windows":
50		// Windows XP and before didn't have a LOCALAPPDATA, so fallback
51		// to regular APPDATA when LOCALAPPDATA is not set.
52		appData := os.Getenv("LOCALAPPDATA")
53		if roaming || appData == "" {
54			appData = os.Getenv("APPDATA")
55		}
56
57		if appData != "" {
58			return filepath.Join(appData, appNameUpper)
59		}
60
61	case "darwin":
62		if homeDir != "" {
63			return filepath.Join(homeDir, "Library",
64				"Application Support", appNameUpper)
65		}
66
67	case "plan9":
68		if homeDir != "" {
69			return filepath.Join(homeDir, appNameLower)
70		}
71
72	default:
73		if homeDir != "" {
74			return filepath.Join(homeDir, "."+appNameLower)
75		}
76	}
77
78	// Fall back to the current directory if all else fails.
79	return "."
80}
81
82// AppDataDir returns an operating system specific directory to be used for
83// storing application data for an application.
84//
85// The appName parameter is the name of the application the data directory is
86// being requested for.  This function will prepend a period to the appName for
87// POSIX style operating systems since that is standard practice.  An empty
88// appName or one with a single dot is treated as requesting the current
89// directory so only "." will be returned.  Further, the first character
90// of appName will be made lowercase for POSIX style operating systems and
91// uppercase for Mac and Windows since that is standard practice.
92//
93// The roaming parameter only applies to Windows where it specifies the roaming
94// application data profile (%APPDATA%) should be used instead of the local one
95// (%LOCALAPPDATA%) that is used by default.
96//
97// Example results:
98//  dir := AppDataDir("myapp", false)
99//   POSIX (Linux/BSD): ~/.myapp
100//   Mac OS: $HOME/Library/Application Support/Myapp
101//   Windows: %LOCALAPPDATA%\Myapp
102//   Plan 9: $home/myapp
103func AppDataDir(appName string, roaming bool) string {
104	return appDataDir(runtime.GOOS, appName, roaming)
105}
106