diff icf/icf.go @ 12:84384cccda0e

bubfix subpaths and remove hardcode
author atarwn@g5
date Mon, 23 Mar 2026 12:05:05 +0500
parents 2ffb8028ccbb
children 9460f83a5664
line wrap: on
line diff
--- a/icf/icf.go	Thu Mar 19 20:00:49 2026 +0500
+++ b/icf/icf.go	Mon Mar 23 12:05:05 2026 +0500
@@ -146,13 +146,14 @@
 // Match finds the most specific block matching input (e.g. "host/path") and
 // returns resolved directives plus named captures.
 // Domain is matched exactly; path uses prefix match.
-func (c *Config) Match(input string) ([]Directive, map[string]string) {
+func (c *Config) Match(input string) ([]Directive, map[string]string, string) {
 	inHost, inPath, _ := strings.Cut(input, "/")
 
 	type hit struct {
 		block    ParsedBlock
 		captures map[string]string
 		score    int
+		rem      string
 	}
 	var best *hit
 
@@ -166,24 +167,33 @@
 		}
 
 		pathScore := 0
+		rem := inPath
 		if hasPath {
-			pathScore, ok = matchPrefix(patPath, inPath, caps)
+			var pathRem string
+			pathScore, pathRem, ok = matchPrefix(patPath, inPath, caps)
 			if !ok {
 				continue
 			}
+			rem = pathRem
 		}
 
 		score := domScore*1000 + pathScore
 		if best == nil || score > best.score {
-			best = &hit{block: b, captures: caps, score: score}
+			best = &hit{block: b, captures: caps, score: score, rem: rem}
 		}
 	}
 
 	if best == nil {
-		return nil, nil
+		return nil, nil, ""
 	}
 
-	return c.ResolveBlock(best.block, best.captures), best.captures
+	// rem is the unmatched suffix after the path pattern.
+	// Ensure it starts with "/" to form a valid absolute subpath.
+	subPath := best.rem
+	if !strings.HasPrefix(subPath, "/") {
+		subPath = "/" + subPath
+	}
+	return c.ResolveBlock(best.block, best.captures), best.captures, subPath
 }
 
 // ResolveBlock merges mixin directives (lower priority) with block directives,
@@ -286,9 +296,9 @@
 	return score, true
 }
 
-func matchPrefix(pat, s string, caps map[string]string) (int, bool) {
-	score, _, ok := matchCaptures(pat, s, caps)
-	return score, ok
+func matchPrefix(pat, s string, caps map[string]string) (int, string, bool) {
+	score, rem, ok := matchCaptures(pat, s, caps)
+	return score, rem, ok
 }
 
 func matchCaptures(pat, inp string, caps map[string]string) (int, string, bool) {
@@ -306,6 +316,10 @@
 			rest := pat[end+1:]
 
 			for split := len(inp); split >= 1; split-- {
+				// Captures never span path separators: <n> matches one segment only.
+				if strings.Contains(inp[:split], "/") {
+					continue
+				}
 				score, finalRem, ok := matchCaptures(rest, inp[split:], caps)
 				if ok {
 					if capName != "_" {