// Copyright 2016 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.

// +build aix darwin dragonfly freebsd js,wasm !android,linux netbsd openbsd solaris
// +build !cgo osusergo

package user

import (
	
	
	
	
	
	
	
)

const groupFile = "/etc/group"
const userFile = "/etc/passwd"

var colon = []byte{':'}

func init() {
	groupImplemented = false
}

// lineFunc returns a value, an error, or (nil, nil) to skip the row.
type lineFunc func(line []byte) (v interface{}, err error)

// readColonFile parses r as an /etc/group or /etc/passwd style file, running
// fn for each row. readColonFile returns a value, an error, or (nil, nil) if
// the end of the file is reached without a match.
func readColonFile( io.Reader,  lineFunc) ( interface{},  error) {
	 := bufio.NewScanner()
	for .Scan() {
		 := .Bytes()
		// There's no spec for /etc/passwd or /etc/group, but we try to follow
		// the same rules as the glibc parser, which allows comments and blank
		// space at the beginning of a line.
		 = bytes.TrimSpace()
		if len() == 0 || [0] == '#' {
			continue
		}
		,  = ()
		if  != nil ||  != nil {
			return
		}
	}
	return nil, .Err()
}

func matchGroupIndexValue( string,  int) lineFunc {
	var  string
	if  > 0 {
		 = ":"
	}
	 := []byte( +  + ":")
	return func( []byte) ( interface{},  error) {
		if !bytes.Contains(, ) || bytes.Count(, colon) < 3 {
			return
		}
		// wheel:*:0:root
		 := strings.SplitN(string(), ":", 4)
		if len() < 4 || [0] == "" || [] !=  ||
			// If the file contains +foo and you search for "foo", glibc
			// returns an "invalid argument" error. Similarly, if you search
			// for a gid for a row where the group name starts with "+" or "-",
			// glibc fails to find the record.
			[0][0] == '+' || [0][0] == '-' {
			return
		}
		if ,  := strconv.Atoi([2]);  != nil {
			return nil, nil
		}
		return &Group{Name: [0], Gid: [2]}, nil
	}
}

func findGroupId( string,  io.Reader) (*Group, error) {
	if ,  := readColonFile(, matchGroupIndexValue(, 2));  != nil {
		return nil, 
	} else if  != nil {
		return .(*Group), nil
	}
	return nil, UnknownGroupIdError()
}

func findGroupName( string,  io.Reader) (*Group, error) {
	if ,  := readColonFile(, matchGroupIndexValue(, 0));  != nil {
		return nil, 
	} else if  != nil {
		return .(*Group), nil
	}
	return nil, UnknownGroupError()
}

// returns a *User for a row if that row's has the given value at the
// given index.
func matchUserIndexValue( string,  int) lineFunc {
	var  string
	if  > 0 {
		 = ":"
	}
	 := []byte( +  + ":")
	return func( []byte) ( interface{},  error) {
		if !bytes.Contains(, ) || bytes.Count(, colon) < 6 {
			return
		}
		// kevin:x:1005:1006::/home/kevin:/usr/bin/zsh
		 := strings.SplitN(string(), ":", 7)
		if len() < 6 || [] !=  || [0] == "" ||
			[0][0] == '+' || [0][0] == '-' {
			return
		}
		if ,  := strconv.Atoi([2]);  != nil {
			return nil, nil
		}
		if ,  := strconv.Atoi([3]);  != nil {
			return nil, nil
		}
		 := &User{
			Username: [0],
			Uid:      [2],
			Gid:      [3],
			Name:     [4],
			HomeDir:  [5],
		}
		// 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."
		if  := strings.Index(.Name, ",");  >= 0 {
			.Name = .Name[:]
		}
		return , nil
	}
}

func findUserId( string,  io.Reader) (*User, error) {
	,  := strconv.Atoi()
	if  != nil {
		return nil, errors.New("user: invalid userid " + )
	}
	if ,  := readColonFile(, matchUserIndexValue(, 2));  != nil {
		return nil, 
	} else if  != nil {
		return .(*User), nil
	}
	return nil, UnknownUserIdError()
}

func findUsername( string,  io.Reader) (*User, error) {
	if ,  := readColonFile(, matchUserIndexValue(, 0));  != nil {
		return nil, 
	} else if  != nil {
		return .(*User), nil
	}
	return nil, UnknownUserError()
}

func lookupGroup( string) (*Group, error) {
	,  := os.Open(groupFile)
	if  != nil {
		return nil, 
	}
	defer .Close()
	return findGroupName(, )
}

func lookupGroupId( string) (*Group, error) {
	,  := os.Open(groupFile)
	if  != nil {
		return nil, 
	}
	defer .Close()
	return findGroupId(, )
}

func lookupUser( string) (*User, error) {
	,  := os.Open(userFile)
	if  != nil {
		return nil, 
	}
	defer .Close()
	return findUsername(, )
}

func lookupUserId( string) (*User, error) {
	,  := os.Open(userFile)
	if  != nil {
		return nil, 
	}
	defer .Close()
	return findUserId(, )
}