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

package procfs

import (
	
	
	
	

	
)

var (
	rPos          = regexp.MustCompile(`^pos:\s+(\d+)$`)
	rFlags        = regexp.MustCompile(`^flags:\s+(\d+)$`)
	rMntID        = regexp.MustCompile(`^mnt_id:\s+(\d+)$`)
	rIno          = regexp.MustCompile(`^ino:\s+(\d+)$`)
	rInotify      = regexp.MustCompile(`^inotify`)
	rInotifyParts = regexp.MustCompile(`^inotify\s+wd:([0-9a-f]+)\s+ino:([0-9a-f]+)\s+sdev:([0-9a-f]+)(?:\s+mask:([0-9a-f]+))?`)
)

// ProcFDInfo contains represents file descriptor information.
type ProcFDInfo struct {
	// File descriptor
	FD string
	// File offset
	Pos string
	// File access mode and status flags
	Flags string
	// Mount point ID
	MntID string
	// Inode number
	Ino string
	// List of inotify lines (structured) in the fdinfo file (kernel 3.8+ only)
	InotifyInfos []InotifyInfo
}

// FDInfo constructor. On kernels older than 3.8, InotifyInfos will always be empty.
func ( Proc) ( string) (*ProcFDInfo, error) {
	,  := util.ReadFileNoStat(.path("fdinfo", ))
	if  != nil {
		return nil, 
	}

	var , , , ,  string
	var  []InotifyInfo

	 := bufio.NewScanner(bytes.NewReader())
	for .Scan() {
		 = .Text()
		if rPos.MatchString() {
			 = rPos.FindStringSubmatch()[1]
		} else if rFlags.MatchString() {
			 = rFlags.FindStringSubmatch()[1]
		} else if rMntID.MatchString() {
			 = rMntID.FindStringSubmatch()[1]
		} else if rIno.MatchString() {
			 = rIno.FindStringSubmatch()[1]
		} else if rInotify.MatchString() {
			,  := parseInotifyInfo()
			if  != nil {
				return nil, 
			}
			 = append(, *)
		}
	}

	 := &ProcFDInfo{
		FD:           ,
		Pos:          ,
		Flags:        ,
		MntID:        ,
		Ino:          ,
		InotifyInfos: ,
	}

	return , nil
}

// InotifyInfo represents a single inotify line in the fdinfo file.
type InotifyInfo struct {
	// Watch descriptor number
	WD string
	// Inode number
	Ino string
	// Device ID
	Sdev string
	// Mask of events being monitored
	Mask string
}

// InotifyInfo constructor. Only available on kernel 3.8+.
func parseInotifyInfo( string) (*InotifyInfo, error) {
	 := rInotifyParts.FindStringSubmatch()
	if len() >= 4 {
		var  string
		if len() == 5 {
			 = [4]
		}
		 := &InotifyInfo{
			WD:   [1],
			Ino:  [2],
			Sdev: [3],
			Mask: ,
		}
		return , nil
	}
	return nil, fmt.Errorf("%w: invalid inode entry: %q", ErrFileParse, )
}

// ProcFDInfos represents a list of ProcFDInfo structs.
type ProcFDInfos []ProcFDInfo

func ( ProcFDInfos) () int           { return len() }
func ( ProcFDInfos) (,  int)      { [], [] = [], [] }
func ( ProcFDInfos) (,  int) bool { return [].FD < [].FD }

// InotifyWatchLen returns the total number of inotify watches.
func ( ProcFDInfos) () (int, error) {
	 := 0
	for ,  := range  {
		 += len(.InotifyInfos)
	}

	return , nil
}