appdata.go raw

   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