Lindenii Project Forge
Login

go-lindenii-common

Common library for the Lindenii Project
Commit info
ID
f1247b104a0dba8226c443d876737ee924db6a1d
Author
Runxi Yu <me@runxiyu.org>
Author date
Fri, 21 Mar 2025 14:58:42 +0800
Committer
Runxi Yu <me@runxiyu.org>
Committer date
Fri, 21 Mar 2025 14:58:42 +0800
Actions
misc: Remove underscores
package misc

import "fmt"

func Wrap_one_error(a error, b error) error {
func WrapOneError(a error, b error) error {
	return fmt.Errorf("%w: %w", a, b)
}

func First_or_panic[T any](v T, err error) T {
func FirstOrPanic[T any](v T, err error) T {
	if err != nil {
		panic(err)
	}
	return v
}
// Package misc provides miscellaneous functions.
package misc

import (
	"errors"
	"strings"
)

// Pointerize_first returns the address of its first argument, and the value of
// PointerizeFirst returns the address of its first argument, and the value of
// its second argument. This is useful to for taking the address of the
// non-error return value of a function that also has an error return value.
func Pointerize_first[T1 any, T2 any](x1 T1, x2 T2) (*T1, T2) {
func PointerizeFirst[T1 any, T2 any](x1 T1, x2 T2) (*T1, T2) {
	return &x1, x2
}

// Copy_map the map src to dst without clearing existing items in dst.
func Copy_map[K comparable, V any](dst map[K]V, src map[K]V) {
// CopyMap the map src to dst without clearing existing items in dst.
func CopyMap[K comparable, V any](dst map[K]V, src map[K]V) {
	for k, v := range src {
		dst[k] = v
	}
}

// String_to_byte_ptr returns a pointer to the first byte of a string. It
// StringToBytePtr returns a pointer to the first byte of a string. It
// ensures that the returned pointer is null-terminated.
func String_to_byte_ptr(s string) (*byte, error) {
func StringToBytePtr(s string) (*byte, error) {
	// If the string already contains a null then whoever attempts to
	// interpret this as a null-terminated string won't be able to see the
	// whole string. This is probably not expected by the caller.
	if strings.IndexByte(s, 0) != -1 {
		return nil, Err_null_byte
		return nil, ErrNullByte
	}
	buf := make([]byte, len(s)+1) // Zeros them out...
	copy(buf, s)                  // ... so the last byte would be null.
	return &buf[0], nil
}

var Err_null_byte = errors.New("string contains null byte")
var ErrNullByte = errors.New("string contains null byte")
package misc

import (
	"errors"
	"os"
	"path"
	"strings"
	"syscall"
)

type Dir_t struct {
type Dir struct {
	fd   int
	name string
}

var (
	Err_illegal_filename        = errors.New("illegal filename")
	Err_invalid_file_descriptor = errors.New("invalid file descriptor")
	ErrIllegalFilename        = errors.New("illegal filename")
	ErrInvalidFD = errors.New("invalid file descriptor")
)

// Open a file at exactly the given directory.
func Open_file_at(dir Dir_t, filename string, flags int, perms os.FileMode) (*os.File, error) {
func OpenFileAt(dir Dir, filename string, flags int, perms os.FileMode) (*os.File, error) {
	if strings.IndexByte(filename, '/') != -1 {
		return nil, Err_illegal_filename
		return nil, ErrIllegalFilename
	}
	return Open_file_beneath(dir, filename, flags, perms)
	return OpenFileBeneath(dir, filename, flags, perms)
}

// Open a file at or beneath the given directory.
func Open_file_beneath(dir Dir_t, filename string, flags int, perms os.FileMode) (*os.File, error) {
func OpenFileBeneath(dir Dir, filename string, flags int, perms os.FileMode) (*os.File, error) {
	fd, err := Openat2(dir.fd, filename, &Open_how_t{
		Flags:   uint64(flags) | syscall.O_CLOEXEC,
		Mode:    uint64(syscallMode(perms)),
		Resolve: RESOLVE_BENEATH | RESOLVE_NO_SYMLINKS,
	})
	if err != nil {
		return nil, err
	}
	file := os.NewFile(uintptr(fd), path.Join(dir.name, filename))
	if file == nil {
		return nil, Err_invalid_file_descriptor
		return nil, ErrInvalidFD
	} else {
		return file, nil
	}
}

// Open a directory as read-only and return a Dir_t to it. The caller is
// responsible for closing the directory with [Close_directory].
func Open_directory_readonly(path string) (Dir_t, error) {
func Open_directory_readonly(path string) (Dir, error) {
	_fd, err := syscall.Open(path, syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_CLOEXEC, 0)
	return Dir_t{fd: _fd, name: path}, err
	return Dir{fd: _fd, name: path}, err
}

// Close a directory returned by [Open_directory_readonly].
func (dir *Dir_t) Close() error {
func (dir *Dir) Close() error {
	return syscall.Close(dir.fd)
}

// syscallMode returns the syscall-specific mode flags from Go's portable mode flags.
func syscallMode(i os.FileMode) (o uint32) {
	// This part actually came from Go's os/file_posix.go but IMO it's too
	// small and should fall under fair use.
	o |= uint32(i.Perm())
	if i&os.ModeSetuid != 0 {
		o |= syscall.S_ISUID
	}
	if i&os.ModeSetgid != 0 {
		o |= syscall.S_ISGID
	}
	if i&os.ModeSticky != 0 {
		o |= syscall.S_ISVTX
	}
	// No mapping for Go's ModeTemporary (plan9 only).
	return
}
package misc

import (
	"syscall"
	"unsafe"
)

const SYS_OPENAT2 = 437

type Open_how_t struct {
	Flags   uint64
	Mode    uint64
	Resolve uint64
}

const (
	RESOLVE_BENEATH       = 0x8
	RESOLVE_IN_ROOT       = 0x10
	RESOLVE_NO_MAGICLINKS = 0x2
	RESOLVE_NO_SYMLINKS   = 0x4
	RESOLVE_NO_XDEV       = 0x1
)

// See openat2(2) on Linux
func Openat2(dirfd int, path string, open_how *Open_how_t) (fd int, err error) {
	path_ptr, err := String_to_byte_ptr(path)
	path_ptr, err := StringToBytePtr(path)
	if err != nil {
		return
	}
	_fd, _, errno := syscall.Syscall6(SYS_OPENAT2, uintptr(dirfd), uintptr(unsafe.Pointer(path_ptr)), uintptr(unsafe.Pointer(open_how)), uintptr(unsafe.Sizeof(Open_how_t{})), 0, 0)
	fd = int(_fd)
	if errno != 0 {
		err = errno
	}
	return
}
package misc

import (
	"path/filepath"
	"strings"
)

func Sanitize_path(path string) (string, bool) {
func SanitizePath(path string) (string, bool) {
	if path == "" {
		return "", false
	}

	path = strings.TrimLeft(path, "/")

	for _, part := range strings.Split(path, "/") {
		if part == "." || part == ".." {
			path = filepath.Clean(path)
			break
		}
	}

	if path == ".." || strings.HasPrefix(path, "../") {
		return "", false
	}

	return path, true
}