func findMissingFuncs(data, source []byte, header string, d *decl, modName string, missingFuncs chan *missingFunc) {
	ch := make(chan string)
	go func() {
		defer func() {
			close(ch)
			if r := recover(); r != nil {
				fmt.Fprintf(os.Stderr, "Header file '%s' is malformed.\n", header)
				os.Exit(1)
			}
		}()
		var buf bytes.Buffer
		lines := strings.Split(string(data), "\n")
		for i := 0; i < len(lines); i++ {
			line := strings.TrimSpace(lines[i])
			buf.WriteString(line)
			if strings.HasPrefix(line, d.macro) {
				for !strings.HasSuffix(line, ";") {
					i++
					line = strings.TrimSpace(lines[i])
					buf.WriteString(line)
				}
			}
			ch <- buf.String()
			buf.Reset()
		}
	}()
	for line := range ch {
		vals := d.regex.FindStringSubmatch(line)
		if vals == nil {
			continue
		}
		name := strings.TrimSpace(vals[2])
		if strings.HasPrefix(name, "_") {
			continue
		}
		typ := strings.TrimSpace(vals[1])
		params := strings.TrimSpace(vals[3])
		if !bytes.Contains(source, []byte("C."+name)) {
			missingFuncs <- &missingFunc{Name: name, Type: typ, Params: params, Module: modName, Header: header}
		}
	}
}
findMissingFuncs() is a customized iteration method used to ensure that multi-line function declarations are found.
package main
import (
	"bytes"
	"container/list"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"sync"
	"testing"
)
var modules = []mod{
	mod{name: "acodec", decl: buildRegex("ALLEGRO_ACODEC_FUNC")},
	mod{name: "audio", decl: buildRegex("ALLEGRO_KCM_AUDIO_FUNC")},
	mod{name: "color", decl: buildRegex("ALLEGRO_COLOR_FUNC")},
	mod{name: "dialog", decl: buildRegex("ALLEGRO_DIALOG_FUNC"), header: "native_dialog"},
	mod{name: "font", decl: buildRegex("ALLEGRO_FONT_FUNC")},
	mod{name: "image", decl: buildRegex("ALLEGRO_IIO_FUNC")},
	mod{name: "memfile", decl: buildRegex("ALLEGRO_MEMFILE_FUNC")},
	mod{name: "physfs", decl: buildRegex("ALLEGRO_PHYSFS_FUNC")},
	mod{name: "primitives", decl: buildRegex("ALLEGRO_PRIM_FUNC")},
	mod{name: "ttf", decl: buildRegex("ALLEGRO_TTF_FUNC"), path: "font/ttf"},
}
type mod struct {
	name, path, header string
	decl               *decl
}
func (m *mod) Header() string {
	if m.header != "" {
		return "allegro_" + m.header + ".h"
	} else {
		return "allegro_" + m.name + ".h"
	}
}
func (m *mod) Path() string {
	if m.path != "" {
		return m.path
	} else {
		return m.name
	}
}
type decl struct {
	macro string
	regex *regexp.Regexp
}
var alFunc = buildRegex("AL_FUNC")
func buildRegex(macro string) *decl {
	return &decl{macro, regexp.MustCompile(macro + `\((?P<type>.*), (?P<name>.*), \((?P<params>.*)\)\)`)}
}
func getSource(dirs ...string) ([]byte, error) {
	var (
		buf bytes.Buffer
		err error
	)
	for _, dir := range dirs {
		if err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
			if info.IsDir() && path != dir {
				return filepath.SkipDir
			} else if !strings.HasSuffix(info.Name(), ".go") {
				return nil
			}
			data, err2 := ioutil.ReadFile(path)
			if err2 != nil {
				return fmt.Errorf("can't read Go source file \"%s\": %s", path, err2.Error())
			}
			buf.Write(data)
			return nil
		}); err != nil {
			return nil, err
		}
	}
	return buf.Bytes(), err
}
type missingFunc struct {
	Name   string
	Type   string
	Params string
	Header string
	Module string
}
func scanHeaders(packageRoot string, missingFuncs chan *missingFunc, errs chan error) {
	var (
		source     []byte
		sourceErr  error
		headerRoot string = os.Getenv("ALLEGRO_INCLUDE")
	)
	if headerRoot == "" {
		headerRoot = filepath.Join("/", "usr", "include", "allegro5")
	}
	defer func() {
		close(missingFuncs)
		close(errs)
	}()
	if _, err := os.Stat(headerRoot); os.IsNotExist(err) {
		return
	}
	source, sourceErr = getSource(packageRoot, filepath.Join(packageRoot, "x11"), filepath.Join(packageRoot, "windows"))
	if sourceErr != nil {
		errs <- sourceErr
		return
	}
	filepath.Walk(headerRoot, func(header string, info os.FileInfo, err error) error {
		if info == nil {
			return errors.New("no file info! headerRoot = " + headerRoot + ", header = " + header)
		}
		if info.IsDir() && info.Name() == "internal" {
			return filepath.SkipDir
		} else if info.IsDir() || !strings.HasSuffix(info.Name(), ".h") {
			return nil
		}
		data, err2 := ioutil.ReadFile(header)
		if err2 != nil {
			errs <- err2
			return nil
		}
		findMissingFuncs(data, source, header, alFunc, "", missingFuncs)
		return nil
	})
	for _, m := range modules {
		root := filepath.Join(packageRoot, m.Path())
		header := filepath.Join(headerRoot, m.Header())
		if _, err := os.Stat(header); os.IsNotExist(err) {
			errs <- fmt.Errorf("Module header not found at '%s'", header)
			continue
		}
		if info, err := os.Stat(root); os.IsNotExist(err) || !info.IsDir() {
			errs <- fmt.Errorf("Source not found at '%s'", root)
			continue
		}
		data, err := ioutil.ReadFile(header)
		if err != nil {
			errs <- err
			continue
		}
		source, sourceErr = getSource(root)
		if sourceErr != nil {
			errs <- sourceErr
			return
		}
		findMissingFuncs(data, source, header, m.decl, m.name, missingFuncs)
	}
}