update some stuff for paths, add env mappings

This commit is contained in:
brent s. 2021-11-16 01:44:08 -05:00
parent c1fc07de50
commit 3e51ac58db
Signed by: bts
GPG Key ID: 8C004C2F93481F6B
3 changed files with 309 additions and 54 deletions

208
funcs.go Normal file
View File

@ -0,0 +1,208 @@
package sysutils

import (
`os`
`runtime`
`strconv`
`strings`
)

/*
EnvMapper contains the environment variables as grouped by their basic type.
If a variable's type cannot be determined, it's placed in Strings.
If a variable's type is a list, it will be an []interface{} as each item may be a different variable type.
It essentially is the same as EnvMap except with the types split out for convenience.
*/
type EnvMapper struct {
Booleans map[string]bool `json:"bools"`
Numbers map[string]int `json:"nums"`
Strings map[string]string `json:"strings"`
Lists map[string][]interface{} `json:"lists"`
}

// GetEnvMapper returns a pointer to a populated EnvMapper.
func GetEnvMapper() (e *EnvMapper, err error) {

var em map[string]interface{}
var env EnvMapper

env = EnvMapper{
Booleans: nil,
Numbers: nil,
Strings: nil,
Lists: nil,
}

for k, v := range em {

switch t := v.(type) {
case bool:
if env.Booleans == nil {
env.Booleans = make(map[string]bool, 0)
}
env.Booleans[k] = t
continue
case int:
if env.Numbers == nil {
env.Numbers = make(map[string]int, 0)
}
env.Numbers[k] = t
continue
case []interface{}:
if env.Lists == nil {
env.Lists = make(map[string][]interface{}, 0)
}
env.Lists[k] = t
case string: // the "default" since everything could probably be typeswitched to a string otherwise.
if env.Strings == nil {
env.Strings = make(map[string]string, 0)
}
env.Strings[k] = t
}
}

*e = env

return
}

/*
EnvMap returns a map of environment variables.
The variable is an interface due to it attempting to use a variable's value to its "true" native type.
If a type cannot be determined, it will be treated a string.
*/
func EnvMap() (envs map[string]interface{}, err error) {

var ems map[string]string

if ems, err = EnvMapString(); err != nil {
return
}

for k, v := range ems {

// Is int?
if i, ok := getNum(v); ok {
envs[k] = i
continue
}

// Is bool?
if b, ok := getBool(v); ok {
envs[k] = b
continue
}

// Is array?
if a, ok := getArr(v); ok {
envs[k] = a
continue
}

// It's a string (probably).
envs[k] = v
}

return
}

// EnvMapString is like EnvMap, but all values are treated as strings.
func EnvMapString() (envs map[string]string, err error) {

var envArray []string

envs = make(map[string]string, 0)

envArray = os.Environ()

for _, e := range envArray {
var k, v string
var kv []string

kv = strings.SplitN(e, "=", 2)
k = kv[0]
if len(kv) == 2 {
v = kv[1]
} else {
v = ""
}
envs[k] = v
}

return
}

// UTILITY FUNCS

// getBool attempts to convert a string value to a boolean.
func getBool(s string) (b bool, ok bool) {

switch s2 := strings.ToLower(strings.TrimSpace(s)); s2 {
case "true", "yes", "y":
b = true
ok = true
case "false", "no", "n":
b = false
ok = true
}

return
}

// getNum attempts to convert a string value to an int.
func getNum(s string) (n int, ok bool) {

var err error

if n, err = strconv.Atoi(s); err == nil {
ok = true
}

return
}

// getArr attempts to convert a string value to an array of interface{}.
func getArr(s string) (a []interface{}, ok bool) {

var arrS []string

if arrS, ok = getArrStr(s); !ok {
return
}

a = make([]interface{}, len(arrS))

for idx, i := range arrS {
if b, ok := getBool(i); ok {
a[idx] = b
} else if n, ok := getNum(i); ok {
a[idx] = n
} else if l, ok := getArr(i); ok {
a[idx] = l
} else {
a[idx] = i
}
}

return
}

// getArrStr attempts to convert a string value to an array of strings.
func getArrStr(s string) (a []string, ok bool) {

var sep string = ":"

if runtime.GOOS == "windows" {
sep = ";"
}

a = strings.Split(s, sep)
l := len(s)
if l <= 1 {
return
}

ok = true

return
}

View File

@ -63,7 +63,7 @@ func main() {
"\tService: %v\n" +
"\tDesc: %v\n" +
"\tReserved?: %v\n",
p.Number, p.Proto.Name, p.ServiceName, p.Description, p.Reserved)
p.Number, p.Protocol.Name, p.ServiceName, p.Description, p.Reserved)
}
----


View File

@ -20,108 +20,155 @@ package paths

import (
"errors"
"fmt"
`fmt`
"os"
"os/user"
"path/filepath"
`runtime`
// "strconv"
"strings"

// "syscall"
)

var err error
/*
ExpandHome will take a tilde(~)-prefixed path and resolve it to the actual path in-place.
Note that it only works for current user; the syntax ~someotheruser/foo/bar is currently unsupported.
*/
func ExpandHome(path *string) (err error) {

var usr *user.User

func ExpandHome(path *string) error {
// Props to this guy.
// https://stackoverflow.com/a/43578461/733214
if len(*path) == 0 {
return errors.New("empty path")
err = errors.New("empty path")
return
} else if (*path)[0] != '~' {
return nil
return
}

// E(ffective)UID (e.g. chown'd user for SUID)
/*
uid := strconv.Itoa(syscall.Geteuid())
usr, err := user.LookupId(euid)
uid := strconv.Itoa(syscall.Geteuid())
usr, err := user.LookupId(euid)
*/
// R(real)UID (invoking user)
usr, err := user.Current()
if err != nil {
// (Real)UID (invoking user)
if usr, err = user.Current(); err != nil {
return err
}
*path = filepath.Join(usr.HomeDir, (*path)[1:])
return nil

return
}

func GetPathEnv() ([]string, error) {
paths := []string{}
for _, p := range strings.Split(os.Getenv("PATH"), ":") {
// GetPathEnv returns a slice of the PATH variable's items.
func GetPathEnv() (paths []string, err error) {

var pathVar string = "PATH"
var sep string = ":"

paths = make([]string, 0)

if runtime.GOOS == "windows" {
pathVar = "Path"
sep = ";"
}

for _, p := range strings.Split(os.Getenv(pathVar), sep) {
if err = RealPath(&p); err != nil {
return nil, err
return
}
paths = append(paths, p)
}
return paths, nil
return
}

func MakeDirIfNotExist(path *string) error {
exists, stat, err := RealPathExistsStat(path)
if err != nil {
// MakeDirIfNotExist will create a directory at a given path if it doesn't exist.
func MakeDirIfNotExist(path string) (err error) {

var stat os.FileInfo
var exists bool
var locPath string = path

if exists, stat, err = RealPathExistsStat(&locPath); err != nil {
if !exists {
// This, at least as of golang 1.15, uses the user's umask.
// It does not actually create a dir with 0777.
// It's up to the caller to do an os.Chmod() on the path after, if desired.
os.MkdirAll(*path, 0777)
return nil
if err = os.MkdirAll(locPath, 0777); err != nil {
return
}
err = nil
return
} else {
return err
return
}
}

// So it exists, but it probably isn't a dir.
if !stat.Mode().IsDir() {
return errors.New(fmt.Sprintf("path %v exists but is not a directory", *path))
err = errors.New(fmt.Sprintf("path %v exists but is not a directory", locPath))
return
}

// This should probably never happen. Probably.
return errors.New("undefined behaviour")
err = errors.New("undefined")
return
}

func RealPath(path *string) error {
err := ExpandHome(path)
if err != nil {
return err
// RealPath will transform a given path into the very best guess for an absolute path in-place.
func RealPath(path *string) (err error) {

if err = ExpandHome(path); err != nil {
return
}
*path, err = filepath.Abs(*path)
if err != nil {
return err

if *path, err = filepath.Abs(*path); err != nil {
return
}
return nil

return
}

func RealPathExists(path *string) (bool, error) {
// I know it's hacky, but we use the bool as a sort of proto-state-machine thing.
// If err != nil and bool is true, the error occurred during path absolution.
// If err != nil and bool is false, the path does not exist.
err := RealPath(path)
if err != nil {
return true, err
/*
RealPathExists is like RealPath, but will also return a boolean as to whether the path
actually exists or not.

It's hacky, but the "exists" bool along with err is a sort of proto-state-machine.
If err != nil and bool is true, the error occurred during path absolution.
If err != nil and bool is false, the path does not exist.
*/
func RealPathExists(path *string) (exists bool, err error) {

if err = RealPath(path); err != nil {
exists = true
return
}
if _, err := os.Stat(*path); err != nil {
return false, err

if _, err = os.Stat(*path); err != nil {
return
}
return true, nil

exists = true

return
}

func RealPathExistsStat(path *string) (bool, os.FileInfo, error) {
// Same deal as RealPathExists.
// If err != nil and bool is true, the error occurred during path absolution.
// If err != nil and bool is false, the path does not exist.
err := RealPath(path)
if err != nil {
return true, nil, err
// RealPathExistsStat is like RealPathExists except it will also return the os.FileInfo for the path (assuming it exists).
func RealPathExistsStat(path *string) (exists bool, stat os.FileInfo, err error) {

// See the comments for RealPathExists for details on this.
if err = RealPath(path); err != nil {
exists = true
return
}
stat, err := os.Stat(*path)
if err != nil {
return false, nil, err

if stat, err = os.Stat(*path); err != nil {
return
}
return true, stat, nil

exists = true

return
}