// Copyright 2019 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//go:build (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris) && !js
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
// +build !js

package procfs

import (
	
	
	
	
	

	
)

// ProcMapPermissions contains permission settings read from `/proc/[pid]/maps`.
type ProcMapPermissions struct {
	// mapping has the [R]ead flag set
	Read bool
	// mapping has the [W]rite flag set
	Write bool
	// mapping has the [X]ecutable flag set
	Execute bool
	// mapping has the [S]hared flag set
	Shared bool
	// mapping is marked as [P]rivate (copy on write)
	Private bool
}

// ProcMap contains the process memory-mappings of the process
// read from `/proc/[pid]/maps`.
type ProcMap struct {
	// The start address of current mapping.
	StartAddr uintptr
	// The end address of the current mapping
	EndAddr uintptr
	// The permissions for this mapping
	Perms *ProcMapPermissions
	// The current offset into the file/fd (e.g., shared libs)
	Offset int64
	// Device owner of this mapping (major:minor) in Mkdev format.
	Dev uint64
	// The inode of the device above
	Inode uint64
	// The file or psuedofile (or empty==anonymous)
	Pathname string
}

// parseDevice parses the device token of a line and converts it to a dev_t
// (mkdev) like structure.
func parseDevice( string) (uint64, error) {
	 := strings.Index(, ":")
	if  == -1 {
		return 0, fmt.Errorf("%w: expected separator `:` in %s", ErrFileParse, )
	}

	,  := strconv.ParseUint([0:], 16, 0)
	if  != nil {
		return 0, 
	}

	,  := strconv.ParseUint([+1:], 16, 0)
	if  != nil {
		return 0, 
	}

	return unix.Mkdev(uint32(), uint32()), nil
}

// parseAddress converts a hex-string to a uintptr.
func parseAddress( string) (uintptr, error) {
	,  := strconv.ParseUint(, 16, 0)
	if  != nil {
		return 0, 
	}

	return uintptr(), nil
}

// parseAddresses parses the start-end address.
func parseAddresses( string) (uintptr, uintptr, error) {
	 := strings.Index(, "-")
	if  == -1 {
		return 0, 0, fmt.Errorf("%w: expected separator `-` in %s", ErrFileParse, )
	}

	,  := parseAddress([0:])
	if  != nil {
		return 0, 0, 
	}

	,  := parseAddress([+1:])
	if  != nil {
		return 0, 0, 
	}

	return , , nil
}

// parsePermissions parses a token and returns any that are set.
func parsePermissions( string) (*ProcMapPermissions, error) {
	if len() < 4 {
		return nil, fmt.Errorf("%w: invalid permissions token", ErrFileParse)
	}

	 := ProcMapPermissions{}
	for ,  := range  {
		switch  {
		case 'r':
			.Read = true
		case 'w':
			.Write = true
		case 'x':
			.Execute = true
		case 'p':
			.Private = true
		case 's':
			.Shared = true
		}
	}

	return &, nil
}

// parseProcMap will attempt to parse a single line within a proc/[pid]/maps
// buffer.
func parseProcMap( string) (*ProcMap, error) {
	 := strings.Fields()
	if len() < 5 {
		return nil, fmt.Errorf("%w: truncated procmap entry", ErrFileParse)
	}

	, ,  := parseAddresses([0])
	if  != nil {
		return nil, 
	}

	,  := parsePermissions([1])
	if  != nil {
		return nil, 
	}

	,  := strconv.ParseInt([2], 16, 0)
	if  != nil {
		return nil, 
	}

	,  := parseDevice([3])
	if  != nil {
		return nil, 
	}

	,  := strconv.ParseUint([4], 10, 0)
	if  != nil {
		return nil, 
	}

	 := ""

	if len() >= 5 {
		 = strings.Join([5:], " ")
	}

	return &ProcMap{
		StartAddr: ,
		EndAddr:   ,
		Perms:     ,
		Offset:    ,
		Dev:       ,
		Inode:     ,
		Pathname:  ,
	}, nil
}

// ProcMaps reads from /proc/[pid]/maps to get the memory-mappings of the
// process.
func ( Proc) () ([]*ProcMap, error) {
	,  := os.Open(.path("maps"))
	if  != nil {
		return nil, 
	}
	defer .Close()

	 := []*ProcMap{}
	 := bufio.NewScanner()

	for .Scan() {
		,  := parseProcMap(.Text())
		if  != nil {
			return nil, 
		}

		 = append(, )
	}

	return , nil
}