// Program mknames parses the cap_names.h file and creates an
// equivalent names.go file including comments on each cap.Value from
// the documentation directory.
package main

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"strings"
)

var (
	header = flag.String("header", "", "name of header file")
	text   = flag.String("textdir", "", "directory name for value txt files")
)

func main() {
	flag.Parse()

	if *header == "" {
		log.Fatal("usage: mknames --header=.../cap_names.h")
	}
	d, err := ioutil.ReadFile(*header)
	if err != nil {
		log.Fatal("reading:", err)
	}

	b := bytes.NewBuffer(d)

	var list []string
	for {
		line, err := b.ReadString('\n')
		if err == io.EOF {
			break
		}
		if !strings.Contains(line, `"`) {
			continue
		}
		i := strings.Index(line, `"`)
		line = line[i+1:]
		i = strings.Index(line, `"`)
		line = line[:i]
		list = append(list, line)
	}

	// generate package file names.go
	fmt.Print(`package cap

/* ** DO NOT EDIT THIS FILE. IT WAS AUTO-GENERATED BY LIBCAP'S GO BUILDER (mknames.go) ** */

// NamedCount holds the number of capability values, with official
// names, known at the time this libcap/cap version was released. The
// "../libcap/cap" package is fully able to manipulate higher numbered
// capability values by numerical value. However, if you find
// cap.NamedCount < cap.MaxBits(), it is probably time to upgrade this
// package on your system.
//
// FWIW the userspace tool '/sbin/capsh' also contains a runtime check
// for the condition that libcap is behind the running kernel in this
// way.
const NamedCount = `, len(list), `

// CHOWN etc., are the named capability values of the Linux
// kernel. The canonical source for each name is the
// "uapi/linux/capabilities.h" file.  Some values may not be available
// (yet) where the kernel is older.  The actual number of capabities
// supported by the running kernel can be obtained using the
// cap.MaxBits() function.
const (
`)
	bits := make(map[string]string)
	for i, name := range list {
		doc := fmt.Sprintf("%s/%d.txt", *text, i)
		content, err := ioutil.ReadFile(doc)
		if err != nil {
			log.Fatalf("filed to read %q: %v", doc, err)
		}
		detail := strings.Split(strings.Replace(string(content), "CAP_", "cap.", -1), "\n")
		if i != 0 {
			fmt.Println()
		}
		v := strings.ToUpper(strings.TrimPrefix(name, "cap_"))
		for j, line := range detail {
			preamble := ""
			offset := 0
			if j == 0 {
				if !strings.HasPrefix(line, "Allows ") {
					log.Fatalf("line should begin \"Allows \": got %s:%d:%q", doc, j, line)
				}
				preamble = fmt.Sprint(v, " a")
				offset = 1
			}
			if len(line) != 0 || j != len(detail)-1 {
				fmt.Printf(" // %s%s\n", preamble, line[offset:])
			}
		}
		bits[name] = v
		if i == 0 {
			fmt.Println(v, " Value =  iota")
		} else {
			fmt.Println(v)
		}
	}
	fmt.Print(`)

var names = map[Value]string{
`)
	for _, name := range list {
		fmt.Printf("%s: %q,\n", bits[name], name)
	}
	fmt.Print(`}

var bits = map[string]Value {
`)
	for _, name := range list {
		fmt.Printf("%q: %s,\n", name, bits[name])
	}
	fmt.Println(`}`)
}
