// This file is part of the adequate Debian-native package, and is available
// under the Expat license. For the full terms please see debian/copyright.

package main

import (
	"fmt"
	"log"
	"regexp"
)

const OBSOLETE_CONFFILE_TAG = "obsolete-conffile"

type conffileErr struct {
	pkg  string
	path string
}

func (c conffileErr) Error() string {
	if c == (conffileErr{}) {
		return ""
	}
	return fmt.Sprintf("%s: %s %s", c.pkg, OBSOLETE_CONFFILE_TAG, c.path)
}

type dpkgQueryFunc func([]string) ([]string, error)
type packageConffileChecker struct {
	dqFunc dpkgQueryFunc
	emit   bool
}

func newPackageConffileChecker(tags tagFilter) packageConffileChecker {
	return packageConffileChecker{
		dqFunc: runCommand,
		emit:   tags.shouldEmit(OBSOLETE_CONFFILE_TAG),
	}
}

func (cc packageConffileChecker) check(pkgs []string) []conffileErr {
	if !cc.emit {
		return nil
	}
	// Try both ${binary:Package} and ${Package}; the former gives us
	// architecture information, but the later works with pre-multiarch dpkg.
	lines, err := cc.dqFunc(append([]string{
		"dpkg-query",
		"-Wf", "${binary:Package},${Package}\n${Conffiles}\n",
		"--"},
		pkgs...))
	if err != nil {
		log.Fatal(err)
	}

	// sample input:
	// zsh-common,zsh-common
	//  /etc/zsh/newuser.zshrc.recommended dac3563a2ddd13e8027b1861d415f3d4 obsolete
	//  /etc/zsh/zlogin 48032df2ace0977f2491b016e3c421a3
	pkgLine := regexp.MustCompile("^,?([^,\t\n\f\r ]+)")
	conffileLine := regexp.MustCompile("^ (" + nonSpaceToken + ") [0-9a-f]+( obsolete)?$")

	var pkg string
	obsFiles := make(map[string]bool)
	pkg2files := make(map[string][]string)
	for _, line := range lines {
		if m := pkgLine.FindStringSubmatch(line); len(m) == 2 {
			pkg = m[1]
			continue
		}

		m := conffileLine.FindStringSubmatch(line)
		switch {
		case len(m) > 0 && pkg == "":
			log.Fatalf("Unexpected output from dpkg-query -W")
		case len(m) != 3:
		case m[2] == "":
			// Work-around for dpkg bug #645849: don't consider a conffile
			// obsolete if it's listed as non-obsolete in a different
			// package.
			file := m[1]
			obsFiles[file] = false
		case m[2] == " obsolete":
			file := m[1]
			if _, ok := obsFiles[file]; ok {
				// File has already been marked by another package.
				continue
			}
			obsFiles[file] = true
			pkg2files[pkg] = append(pkg2files[pkg], file)
		}
	}

	var errs []conffileErr
	for _, pkg := range pkgs {
		for _, f := range pkg2files[pkg] {
			if obsFiles[f] {
				errs = append(errs, conffileErr{
					pkg:  pkg,
					path: f,
				})
			}

		}
	}

	return errs
}
