Getting an error when trying to check path - go

I am trying to check windows dir in my golang app.
Here is my code
func createWalletDirectory(path string) (err error) {
_, err = os.Stat(path)
if os.IsNotExist(err) {
return err
}
path = filepath.FromSlash(path)
path = path + string(os.PathSeparator) + DirectoryName
err = os.Mkdir(path, 0666)
return
}
So on the first line of the function I am getting an error look like this
invalid character 'i' in string escape code
Example path : C:\Users
Note: The path I am getting from users via POST request
So I need to make a code which will check crossplatform paths.
How can I solve this error ?

You can use path package to work with the urls('path/filepath' for the file paths) which also contributes in platform independency. So you can do following to create the path
givenPath = filepath.Join(DirectoryName, path)
There is also another way of doing this
path := strings.Join([]string{DirectoryName, path}, string(os.PathSeparator))

In Go strings enclosed by double quotes, a backslash starts an escape code, e.g. \n or \u2318. To avoid this, you have two options:
use a double backslash (\\), e.g. "C:\\Users"
use backticks (`) instead of double quotes to define a "raw string", e.g. `C:\Users`
Further reading

Related

Problem in connection string with escaping char

Im trying to develop my first API using Go, but at beginning I run into a problem.
str := "sqlserver://DESKTOP-AAAC9AC#SQLEXPRESS?database=GolangPlay"
dsn := strings.Replace(str, "#", `\`, -1)
fmt.Println("CS be like : " + dsn)
_, err := gorm.Open(sqlserver.Open(dsn), &gorm.Config{})
My connection string must contains escaping tag '' (DESKTOP-AAAC9AC\SQLEXPRESS), so I thought that firstly I might in my str string replace escaping tag with #. So that later when calling the gorm function sqlserver.Open(string) replace the # with escaping tag, but I dont understand why this function returns error with double backslash
"failed to initialize database, got error parse "sqlserver://DESKTOP-AAAC9AC\SQLEXPRESS?database=GolangPlay": invalid character "\" in host name"
but my println returns that what exactly I want
CS be like : sqlserver://DESKTOP-AAAC9AC\SQLEXPRESS?database=GolangPlay
Please explain why this is happening and help in solving it

Issues with go:embed directive: Invalid name

I want to serve music from my HDD. All audio files (mp3/flac/etc.) are read-only. so using //go:embed should work for those files. So I have
//go:embed "assets/Media/Music/*"
var f embed.FS
func main() {
router := gin.Default()
router.StaticFS("/public", http.FS(f))
router.GET("/", func(c *gin.Context) {
file, _ := f.ReadFile("assets/Media/Music/<some_other_folders>/01 - my_song.mp3")
c.Data(
http.StatusOK,
"audio/mpeg",
file,
)
}
router.Run(":8080")
}
The problem is that the Go compiler complains that certain folder names are invalid for example: 'Big K.R.I.T' and 'Al B. Sure!' The docs say that file names shouldn't have spaces or '.' but I'm unaware if that applies if I don't explicitly state the file/folder name.
My question is
What constitutes a "invalid name"
Is there a way to allow folder names with spaces for the embed directive
EDIT
The code above is (almost) exactly what I'm running locally. When I try to build (or go run main.go) VS Code catches this error. The ending is a folder name and not a file for clarification. Hopefully this helps. Thanks in advance
Consulting the go command src where the invalid name error is emitted:
if elem := filepath.Base(dir); isBadEmbedName(elem) {
if dir == file {
return nil, nil, fmt.Errorf("cannot embed %s %s: invalid name %s", what, rel, elem)
} else {
return nil, nil, fmt.Errorf("cannot embed %s %s: in invalid directory %s", what, rel, elem)
}
}
This references isBadEmbedName which then relies on the function module.CheckFilePath. The docs for this function states:
// CheckFilePath checks that a slash-separated file path is valid.
// The definition of a valid file path is the same as the definition
// of a valid import path except that the set of allowed characters is larger:
// all Unicode letters, ASCII digits, the ASCII space character (U+0020),
// and the ASCII punctuation characters
// “!#$%&()+,-.=#[]^_{}~”.
// (The excluded punctuation characters, " * < > ? ` ' | / \ and :,
// have special meanings in certain shells or operating systems.)
//
// CheckFilePath may be less restrictive in the future, but see the
// top-level package documentation for additional information about
// subtleties of Unicode.
So it would appear - based on the last line - that the embedded filename rules is a moving target.
Running some tests against this validation function, show even more subtleties e.g. dot.in.middle vs dot.at.end.:
https://play.golang.org/p/7x6i_Aj8eEJ
so this would explain why your path Big K.R.I.T. fails.
Also worthy of note: when using the //go:embed directive with a wildcard or not affects compilation success.
As you experienced, your compilation failed when using:
//go:embed "assets/Media/Music/*"
var f embed.FS
due to the invalid filename format in that directory. However, if you drop the wildcard:
//go:embed "assets/Media/Music"
var f embed.FS
compilation will succeed - but any invalid filenames will just be silently removed from the embed manifest.
If you want to inspect explicitly what files will be embedded in your program, you can avail of go list e.g.
go list -json
and look for the EmbedFiles section of the output. Or use jq to zero-in on this section:
$ go list -json | jq .EmbedFiles
[
"Static Files/Space Dir/Big K.R.I.T.mp3",
"Static Files/Space Dir/world.txt",
"Static Files/hey!"
]

Trim multiple characters to right of final slash including slash

Golang doesn't have the strrchr function that php does. If I want to remove /path (including the final slash) from this string, how does one do it in golang?
mystr := "/this/is/my/path"
Desired output
"/this/is/my"
I can get the index of the final slash like this
lastSlash := strings.LastIndex(mystr, "/")
but I'm not sure how to create a new string with /path removed. How to do that?
Try output := mystr[:strings.LastIndex(mystr, "/")]
mystr := "/this/is/my/path"
idx := strings.LastIndex(mystr, "/")
if idx != -1{
mystr = mystr[:idx]
}
fmt.Println(mystr)
playground link
captncraig's answer works for any type of separator char, but assuming you are running on a POSIX-style machine ("/" is the path separator) and what you are manipulating are indeed paths:
http://play.golang.org/p/oQbXTEhH30
package main
import (
"fmt"
"path/filepath"
)
func main() {
s := "/this/is/my/path"
fmt.Println(filepath.Dir(s))
// Output: /this/is/my
}
From the godoc (https://golang.org/pkg/path/filepath/#Dir):
Dir returns all but the last element of path, typically the path's directory. After dropping the final element, the path is Cleaned and trailing slashes are removed.
Though if you run it with /path, it will return /, which may or may not be what you want.
One corner case not covered by the previous (quite satisfactory) solutions is that of a trailing /. Ie - if you wanted /foo/bar/quux/ trimmed to /foo/bar rather than /foo/bar/quux. That can be accomplished with the regexp library:
mystr := "/this/is/my/path/"
trimpattern := regexp.MustCompile("^(.*?)/[^/]*/?$")
newstr := trimpattern.ReplaceAllString(mystr, "$1")
fmt.Println(newstr)
There's a bit fuller example here: http://play.golang.org/p/ii-svpbaHt

How to find a package name from given call in runtime?

For logging purpose I want to write a function which will print a package name.
I can do it for a directory name:
// file is the full file name
// 4 - how many calls we want to go up in a stack trace.
_, file, line, ok := runtime.Caller(4)
... but can't find a way for package name (package name can be different from directory name).
I came across a similar problem - from a package path how do you get the package name. The best solution I found is to exec the "go list" command. Not ideal but I came up blank elsewhere.
In my case I also had a problem that sometimes the package is an empty directory. With no source files, "go list" throws an error, so I added a function to create a sensible package name from the path.
Here's the code:
func getPackageName(path string) string {
output, err := exec.Command("go", "list", "-f", "{{.Name}}", path).CombinedOutput()
if err != nil {
return guessPackageName(path)
}
return strings.TrimSpace(string(output))
}
func guessPackageName(path string) string {
preferred := path
if strings.HasSuffix(preferred, "/") {
// training slashes are usually tolerated, so we can get rid of one if it exists
preferred = preferred[:len(preferred)-1]
}
if strings.Contains(preferred, "/") {
// if the path contains a "/", use the last part
preferred = preferred[strings.LastIndex(preferred, "/")+1:]
}
if strings.Contains(preferred, "-") {
// the name usually follows a hyphen - e.g. github.com/foo/go-bar
// if the package name contains a "-", use the last part
preferred = preferred[strings.LastIndex(preferred, "-")+1:]
}
if strings.Contains(preferred, ".") {
// dot is commonly usually used as a version - e.g. github.com/foo/bar.v1
// if the package name contains a ".", use the first part
preferred = preferred[:strings.LastIndex(preferred, ".")]
}
return preferred
}

check if given path is a subdirectory of another in golang

Say we have two paths:
c:\foo\bar\baz and c:\foo\bar
Is there any package/method that will help me determine if one is a subdirectory of another? I am looking at a cross-platform option.
You could try and use path.filepath.Rel():
func Rel(basepath, targpath string) (string, error)
Rel returns a relative path that is lexically equivalent to targpath when joined to basepath with an intervening separator.
That is, Join(basepath, Rel(basepath, targpath)) is equivalent to targpath itself
That means Rel("c:\foo\bar", "c:\foo\bar\baz") should be baz, meaning a subpath completely included in c:\foo\bar\baz, and without any '../'.
The same would apply for unix paths.
That would make c:\foo\bar\baz a subdirectory of c:\foo\bar.
I haven't found a reliable solution for all types of paths, but the best you can get is by using filepath.Rel as VonC suggested.
It works if both filepaths are either absolute or relative (mixing is not allowed) and works on both Windows and Linux:
func SubElem(parent, sub string) (bool, error) {
up := ".." + string(os.PathSeparator)
// path-comparisons using filepath.Abs don't work reliably according to docs (no unique representation).
rel, err := filepath.Rel(parent, sub)
if err != nil {
return false, err
}
if !strings.HasPrefix(rel, up) && rel != ".." {
return true, nil
}
return false, nil
}
Absolute windows paths that start with a drive letter will require an additional check though.
You can use the function path.filepath.Match()
Match reports whether name matches the shell file name pattern.
For example:
pattern := "C:\foo\bar" + string(filepath.Separator) + "*"
matched, err := filepath.Match(pattern, "C:\foo\bar\baz")
Where matched should be true.
If you first canonicalize both paths by calling filepath.EvalSymlinks() and filepath.Abs() on them, you can simply append a '/' to each one, since the UNIX kernel itself forbids a '/' within a path component. At this point you can simply use strings.HasPrefix() on the two paths, in either order.
Try this code. This checks if either is a sub-directory of the other. Try changing values of both base and path and the results should be valid.
package main
import (
"fmt"
"path/filepath"
"strings"
)
func main() {
base := "/b/c/"
path := "/a/b/c/d"
if len(base) > len(path) {
base, path = path, base
}
rel, err := filepath.Rel(base, path)
fmt.Printf("Base %q: Path %q: Rel %q Err %v\n", base, path, rel, err)
if err != nil {
fmt.Println("PROCEED")
return
}
if strings.Contains(rel, "..") {
fmt.Println("PROCEED")
return
}
fmt.Println("DENY")
}

Resources