Add new AppDataDir function.
This commit adds a new AppDataDir function that can be used to get an operating system and application specific directory to be used for storing application data. For example: dir := AppDataDir("myapp", false) Would result in: POSIX (Linux/BSD): ~/.myapp Mac OS: $HOME/Library/Application Support/Myapp Windows: %LOCALAPPDATA%\Myapp Plan 9: $home/myapp This is work toward conformal/btcd#30.
This commit is contained in:
parent
ccb6557298
commit
fcf9b83256
3 changed files with 245 additions and 0 deletions
107
appdata.go
Normal file
107
appdata.go
Normal file
|
@ -0,0 +1,107 @@
|
|||
// Copyright (c) 2013 Conformal Systems LLC.
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package btcutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// appDataDir returns an operating system specific directory to be used for
|
||||
// storing application data for an application. See AppDataDir for more
|
||||
// details. This unexported version takes an operating system argument
|
||||
// primarily to enable the testing package to properly test the function by
|
||||
// forcing an operating system that is not the currently one.
|
||||
func appDataDir(goos, appName string, roaming bool) string {
|
||||
if appName == "" || appName == "." {
|
||||
return "."
|
||||
}
|
||||
|
||||
// The caller really shouldn't prepend the appName with a period, but
|
||||
// if they do, handle it gracefully by stripping it.
|
||||
if strings.HasPrefix(appName, ".") {
|
||||
appName = appName[1:]
|
||||
}
|
||||
appNameUpper := string(unicode.ToUpper(rune(appName[0]))) + appName[1:]
|
||||
appNameLower := string(unicode.ToLower(rune(appName[0]))) + appName[1:]
|
||||
|
||||
// Get the OS specific home directory via the Go standard lib.
|
||||
var homeDir string
|
||||
usr, err := user.Current()
|
||||
if err == nil {
|
||||
homeDir = usr.HomeDir
|
||||
}
|
||||
|
||||
// Fall back to standard HOME environment variable that works
|
||||
// for most POSIX OSes if the directory from the Go standard
|
||||
// lib failed.
|
||||
if err != nil || homeDir == "" {
|
||||
homeDir = os.Getenv("HOME")
|
||||
}
|
||||
|
||||
switch goos {
|
||||
// Attempt to use the LOCALAPPDATA or APPDATA environment variable on
|
||||
// Windows.
|
||||
case "windows":
|
||||
// Windows XP and before didn't have a LOCALAPPDATA, so fallback
|
||||
// to regular APPDATA when LOCALAPPDATA is not set.
|
||||
appData := os.Getenv("LOCALAPPDATA")
|
||||
if roaming || appData == "" {
|
||||
appData = os.Getenv("APPDATA")
|
||||
}
|
||||
|
||||
if appData != "" {
|
||||
return filepath.Join(appData, appNameUpper)
|
||||
}
|
||||
|
||||
case "darwin":
|
||||
if homeDir != "" {
|
||||
return filepath.Join(homeDir, "Library",
|
||||
"Application Support", appNameUpper)
|
||||
}
|
||||
|
||||
case "plan9":
|
||||
if homeDir != "" {
|
||||
return filepath.Join(homeDir, appNameLower)
|
||||
}
|
||||
|
||||
default:
|
||||
if homeDir != "" {
|
||||
return filepath.Join(homeDir, "."+appNameLower)
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to the current directory if all else fails.
|
||||
return "."
|
||||
}
|
||||
|
||||
// AppDataDir returns an operating system specific directory to be used for
|
||||
// storing application data for an application.
|
||||
//
|
||||
// The appName parameter is the name of the application the data directory is
|
||||
// being requested for. This function will prepend a period to the appName for
|
||||
// POSIX style operating systems since that is standard practice. An empty
|
||||
// appName or one with a single dot is treated as requesting the current
|
||||
// directory so only "." will be returned. Further, the first character
|
||||
// of appName will be made lowercase for POSIX style operating systems and
|
||||
// uppercase for Mac and Windows since that is standard practice.
|
||||
//
|
||||
// The roaming parameter only applies to Windows where it specifies the roaming
|
||||
// application data profile (%APPDATA%) should be used instead of the local one
|
||||
// (%LOCALAPPDATA%) that is used by default.
|
||||
//
|
||||
// Example results:
|
||||
// dir := AppDataDir("myapp", false)
|
||||
// POSIX (Linux/BSD): ~/.myapp
|
||||
// Mac OS: $HOME/Library/Application Support/Myapp
|
||||
// Windows: %LOCALAPPDATA%\Myapp
|
||||
// Plan 9: $home/myapp
|
||||
func AppDataDir(appName string, roaming bool) string {
|
||||
return appDataDir(runtime.GOOS, appName, roaming)
|
||||
}
|
132
appdata_test.go
Normal file
132
appdata_test.go
Normal file
|
@ -0,0 +1,132 @@
|
|||
// Copyright (c) 2013 Conformal Systems LLC.
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package btcutil_test
|
||||
|
||||
import (
|
||||
"github.com/conformal/btcutil"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// TestAppDataDir tests the API for AppDataDir to ensure it gives expected
|
||||
// results for various operating systems.
|
||||
func TestAppDataDir(t *testing.T) {
|
||||
// App name plus upper and lowercase variants.
|
||||
appName := "myapp"
|
||||
appNameUpper := string(unicode.ToUpper(rune(appName[0]))) + appName[1:]
|
||||
appNameLower := string(unicode.ToLower(rune(appName[0]))) + appName[1:]
|
||||
|
||||
// When we're on Windows, set the expected local and roaming directories
|
||||
// per the environment vars. When we aren't on Windows, the function
|
||||
// should return the current directory when forced to provide the
|
||||
// Windows path since the environment variables won't exist.
|
||||
winLocal := "."
|
||||
winRoaming := "."
|
||||
if runtime.GOOS == "windows" {
|
||||
localAppData := os.Getenv("LOCALAPPDATA")
|
||||
roamingAppData := os.Getenv("APPDATA")
|
||||
if localAppData == "" {
|
||||
localAppData = roamingAppData
|
||||
}
|
||||
winLocal = filepath.Join(localAppData, appNameUpper)
|
||||
winRoaming = filepath.Join(roamingAppData, appNameUpper)
|
||||
}
|
||||
|
||||
// Get the home directory to use for testing expected results.
|
||||
var homeDir string
|
||||
usr, err := user.Current()
|
||||
if err != nil {
|
||||
t.Errorf("user.Current: %v", err)
|
||||
return
|
||||
}
|
||||
homeDir = usr.HomeDir
|
||||
|
||||
// Mac app data directory.
|
||||
macAppData := filepath.Join(homeDir, "Library", "Application Support")
|
||||
|
||||
tests := []struct {
|
||||
goos string
|
||||
appName string
|
||||
roaming bool
|
||||
want string
|
||||
}{
|
||||
// Various combinations of application name casing, leading
|
||||
// period, operating system, and roaming flags.
|
||||
{"windows", appNameLower, false, winLocal},
|
||||
{"windows", appNameUpper, false, winLocal},
|
||||
{"windows", "." + appNameLower, false, winLocal},
|
||||
{"windows", "." + appNameUpper, false, winLocal},
|
||||
{"windows", appNameLower, true, winRoaming},
|
||||
{"windows", appNameUpper, true, winRoaming},
|
||||
{"windows", "." + appNameLower, true, winRoaming},
|
||||
{"windows", "." + appNameUpper, true, winRoaming},
|
||||
{"linux", appNameLower, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"linux", appNameUpper, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"linux", "." + appNameLower, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"linux", "." + appNameUpper, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"darwin", appNameLower, false, filepath.Join(macAppData, appNameUpper)},
|
||||
{"darwin", appNameUpper, false, filepath.Join(macAppData, appNameUpper)},
|
||||
{"darwin", "." + appNameLower, false, filepath.Join(macAppData, appNameUpper)},
|
||||
{"darwin", "." + appNameUpper, false, filepath.Join(macAppData, appNameUpper)},
|
||||
{"openbsd", appNameLower, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"openbsd", appNameUpper, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"openbsd", "." + appNameLower, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"openbsd", "." + appNameUpper, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"freebsd", appNameLower, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"freebsd", appNameUpper, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"freebsd", "." + appNameLower, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"freebsd", "." + appNameUpper, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"netbsd", appNameLower, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"netbsd", appNameUpper, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"netbsd", "." + appNameLower, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"netbsd", "." + appNameUpper, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"plan9", appNameLower, false, filepath.Join(homeDir, appNameLower)},
|
||||
{"plan9", appNameUpper, false, filepath.Join(homeDir, appNameLower)},
|
||||
{"plan9", "." + appNameLower, false, filepath.Join(homeDir, appNameLower)},
|
||||
{"plan9", "." + appNameUpper, false, filepath.Join(homeDir, appNameLower)},
|
||||
{"unrecognized", appNameLower, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"unrecognized", appNameUpper, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"unrecognized", "." + appNameLower, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"unrecognized", "." + appNameUpper, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
|
||||
// No application name provided, so expect current directory.
|
||||
{"windows", "", false, "."},
|
||||
{"windows", "", true, "."},
|
||||
{"linux", "", false, "."},
|
||||
{"darwin", "", false, "."},
|
||||
{"openbsd", "", false, "."},
|
||||
{"freebsd", "", false, "."},
|
||||
{"netbsd", "", false, "."},
|
||||
{"plan9", "", false, "."},
|
||||
{"unrecognized", "", false, "."},
|
||||
|
||||
// Single dot provided for application name, so expect current
|
||||
// directory.
|
||||
{"windows", ".", false, "."},
|
||||
{"windows", ".", true, "."},
|
||||
{"linux", ".", false, "."},
|
||||
{"darwin", ".", false, "."},
|
||||
{"openbsd", ".", false, "."},
|
||||
{"freebsd", ".", false, "."},
|
||||
{"netbsd", ".", false, "."},
|
||||
{"plan9", ".", false, "."},
|
||||
{"unrecognized", ".", false, "."},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
ret := btcutil.TstAppDataDir(test.goos, test.appName, test.roaming)
|
||||
if ret != test.want {
|
||||
t.Errorf("appDataDir #%d (%s) does not match - "+
|
||||
"expected got %s, want %s", i, test.goos, ret,
|
||||
test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,3 +17,9 @@ package btcutil
|
|||
func (b *Block) SetBlockBytes(buf []byte) {
|
||||
b.serializedBlock = buf
|
||||
}
|
||||
|
||||
// TstAppDataDir makes the internal appDataDir function available to the test
|
||||
// package.
|
||||
func TstAppDataDir(goos, appName string, roaming bool) string {
|
||||
return appDataDir(goos, appName, roaming)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue