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) {
|
func (b *Block) SetBlockBytes(buf []byte) {
|
||||||
b.serializedBlock = buf
|
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