1package main
2
3import (
4	"errors"
5	"os"
6	"path/filepath"
7	"time"
8)
9
10// Don't search for a corresponding header/source file for longer than ~0.5 seconds
11var fileSearchMaxTime = 500 * time.Millisecond
12
13// ExtFileSearch will search for a corresponding file, given a silce of extensions.
14// This is useful for ie. finding a corresponding .h file for a .c file.
15// The search starts in the current directory, then searches every parent directory in depth.
16// TODO: Search sibling and parent directories named "include" first, then search the rest.
17func ExtFileSearch(absCppFilename string, headerExtensions []string, maxTime time.Duration) (string, error) {
18	cppBasename := filepath.Base(absCppFilename)
19	searchPath := filepath.Dir(absCppFilename)
20	ext := filepath.Ext(cppBasename)
21	if ext == "" {
22		return "", errors.New("filename has no extension: " + cppBasename)
23	}
24	firstName := cppBasename[:len(cppBasename)-len(ext)]
25	var headerNames []string
26	for _, ext := range headerExtensions {
27		headerNames = append(headerNames, firstName+ext)
28	}
29	foundHeaderAbsPath := ""
30	startTime := time.Now()
31	for {
32		err := filepath.Walk(searchPath, func(path string, info os.FileInfo, err error) error {
33			basename := filepath.Base(info.Name())
34			if err == nil {
35				//logf("Walking %s\n", path)
36				for _, headerName := range headerNames {
37					if time.Since(startTime) > maxTime {
38						return errors.New("file search timeout")
39					}
40					if basename == headerName {
41						// Found the corresponding header!
42						absFilename, err := filepath.Abs(path)
43						if err != nil {
44							continue
45						}
46						foundHeaderAbsPath = absFilename
47						//logf("Found %s!\n", absFilename)
48						return nil
49					}
50				}
51			}
52			// No result
53			return nil
54		})
55		if err != nil {
56			return "", errors.New("error when searching for a corresponding header for " + cppBasename + ":" + err.Error())
57		}
58		if len(foundHeaderAbsPath) == 0 {
59			// Try the parent directory
60			searchPath = filepath.Dir(searchPath)
61			if len(searchPath) > 2 {
62				continue
63			}
64		}
65		break
66	}
67	if len(foundHeaderAbsPath) == 0 {
68		return "", errors.New("found no corresponding header for " + cppBasename)
69	}
70
71	// Return the result
72	return foundHeaderAbsPath, nil
73}
74