// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build (cgo || darwin) && !osusergo && unix && !android

package user

import (
	
	
	
	
	
	
)

func current() (*User, error) {
	return lookupUnixUid(syscall.Getuid())
}

func lookupUser( string) (*User, error) {
	var  _C_struct_passwd
	var  bool
	 := make([]byte, len()+1)
	copy(, )

	 := retryWithBuffer(userBuffer, func( []byte) syscall.Errno {
		var  syscall.Errno
		, ,  = _C_getpwnam_r((*_C_char)(unsafe.Pointer(&[0])),
			(*_C_char)(unsafe.Pointer(&[0])), _C_size_t(len()))
		return 
	})
	if  == syscall.ENOENT || ( == nil && !) {
		return nil, UnknownUserError()
	}
	if  != nil {
		return nil, fmt.Errorf("user: lookup username %s: %v", , )
	}
	return buildUser(&), nil
}

func lookupUserId( string) (*User, error) {
	,  := strconv.Atoi()
	if  != nil {
		return nil, 
	}
	return lookupUnixUid()
}

func lookupUnixUid( int) (*User, error) {
	var  _C_struct_passwd
	var  bool

	 := retryWithBuffer(userBuffer, func( []byte) syscall.Errno {
		var  syscall.Errno
		, ,  = _C_getpwuid_r(_C_uid_t(),
			(*_C_char)(unsafe.Pointer(&[0])), _C_size_t(len()))
		return 
	})
	if  == syscall.ENOENT || ( == nil && !) {
		return nil, UnknownUserIdError()
	}
	if  != nil {
		return nil, fmt.Errorf("user: lookup userid %d: %v", , )
	}
	return buildUser(&), nil
}

func buildUser( *_C_struct_passwd) *User {
	 := &User{
		Uid:      strconv.FormatUint(uint64(_C_pw_uid()), 10),
		Gid:      strconv.FormatUint(uint64(_C_pw_gid()), 10),
		Username: _C_GoString(_C_pw_name()),
		Name:     _C_GoString(_C_pw_gecos()),
		HomeDir:  _C_GoString(_C_pw_dir()),
	}
	// The pw_gecos field isn't quite standardized. Some docs
	// say: "It is expected to be a comma separated list of
	// personal data where the first item is the full name of the
	// user."
	.Name, _, _ = strings.Cut(.Name, ",")
	return 
}

func lookupGroup( string) (*Group, error) {
	var  _C_struct_group
	var  bool

	 := make([]byte, len()+1)
	copy(, )

	 := retryWithBuffer(groupBuffer, func( []byte) syscall.Errno {
		var  syscall.Errno
		, ,  = _C_getgrnam_r((*_C_char)(unsafe.Pointer(&[0])),
			(*_C_char)(unsafe.Pointer(&[0])), _C_size_t(len()))
		return 
	})
	if  == syscall.ENOENT || ( == nil && !) {
		return nil, UnknownGroupError()
	}
	if  != nil {
		return nil, fmt.Errorf("user: lookup groupname %s: %v", , )
	}
	return buildGroup(&), nil
}

func lookupGroupId( string) (*Group, error) {
	,  := strconv.Atoi()
	if  != nil {
		return nil, 
	}
	return lookupUnixGid()
}

func lookupUnixGid( int) (*Group, error) {
	var  _C_struct_group
	var  bool

	 := retryWithBuffer(groupBuffer, func( []byte) syscall.Errno {
		var  syscall.Errno
		, ,  = _C_getgrgid_r(_C_gid_t(),
			(*_C_char)(unsafe.Pointer(&[0])), _C_size_t(len()))
		return syscall.Errno()
	})
	if  == syscall.ENOENT || ( == nil && !) {
		return nil, UnknownGroupIdError(strconv.Itoa())
	}
	if  != nil {
		return nil, fmt.Errorf("user: lookup groupid %d: %v", , )
	}
	return buildGroup(&), nil
}

func buildGroup( *_C_struct_group) *Group {
	 := &Group{
		Gid:  strconv.Itoa(int(_C_gr_gid())),
		Name: _C_GoString(_C_gr_name()),
	}
	return 
}

type bufferKind _C_int

var (
	userBuffer  = bufferKind(_C__SC_GETPW_R_SIZE_MAX)
	groupBuffer = bufferKind(_C__SC_GETGR_R_SIZE_MAX)
)

func ( bufferKind) () _C_size_t {
	 := _C_sysconf(_C_int())
	if  == -1 {
		// DragonFly and FreeBSD do not have _SC_GETPW_R_SIZE_MAX.
		// Additionally, not all Linux systems have it, either. For
		// example, the musl libc returns -1.
		return 1024
	}
	if !isSizeReasonable(int64()) {
		// Truncate.  If this truly isn't enough, retryWithBuffer will error on the first run.
		return maxBufferSize
	}
	return _C_size_t()
}

// retryWithBuffer repeatedly calls f(), increasing the size of the
// buffer each time, until f succeeds, fails with a non-ERANGE error,
// or the buffer exceeds a reasonable limit.
func retryWithBuffer( bufferKind,  func([]byte) syscall.Errno) error {
	 := make([]byte, .initialSize())
	for {
		 := ()
		if  == 0 {
			return nil
		} else if runtime.GOOS == "aix" && +1 == 0 {
			// On AIX getpwuid_r appears to return -1,
			// not ERANGE, on buffer overflow.
		} else if  != syscall.ERANGE {
			return 
		}
		 := len() * 2
		if !isSizeReasonable(int64()) {
			return fmt.Errorf("internal buffer exceeds %d bytes", maxBufferSize)
		}
		 = make([]byte, )
	}
}

const maxBufferSize = 1 << 20

func isSizeReasonable( int64) bool {
	return  > 0 &&  <= maxBufferSize
}

// Because we can't use cgo in tests:
func structPasswdForNegativeTest() _C_struct_passwd {
	 := _C_struct_passwd{}
	*_C_pw_uidp(&) = 1<<32 - 2
	*_C_pw_gidp(&) = 1<<32 - 3
	return 
}