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