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