Finish up config parser port

This commit is contained in:
Kovid Goyal 2023-02-18 21:58:14 +05:30
parent 747411be00
commit 2b7d6d45df
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 103 additions and 22 deletions

View File

@ -11,8 +11,6 @@ from functools import lru_cache
from kittens.ssh.config import load_config from kittens.ssh.config import load_config
from kittens.ssh.main import bootstrap_script, get_connection_data, wrap_bootstrap_script from kittens.ssh.main import bootstrap_script, get_connection_data, wrap_bootstrap_script
from kittens.ssh.options.types import Options as SSHOptions
from kittens.ssh.options.utils import DELETE_ENV_VAR
from kittens.transfer.utils import set_paths from kittens.transfer.utils import set_paths
from kitty.constants import is_macos, runtime_dir from kitty.constants import is_macos, runtime_dir
from kitty.fast_data_types import CURSOR_BEAM, shm_unlink from kitty.fast_data_types import CURSOR_BEAM, shm_unlink

View File

@ -4,8 +4,10 @@ package utils
import ( import (
"bufio" "bufio"
"bytes"
"errors" "errors"
"fmt" "fmt"
"io"
"io/fs" "io/fs"
"os" "os"
"path/filepath" "path/filepath"
@ -26,8 +28,11 @@ type ConfigLine struct {
} }
type ConfigParser struct { type ConfigParser struct {
BadLines []ConfigLine
LineHandler func(key, val string) error LineHandler func(key, val string) error
bad_lines []ConfigLine
seen_includes map[string]bool
override_env []string
} }
type Scanner interface { type Scanner interface {
@ -36,7 +41,24 @@ type Scanner interface {
Err() error Err() error
} }
func (self *ConfigParser) parse(scanner Scanner, name, base_path_for_includes string) error { func (self *ConfigParser) BadLines() []ConfigLine {
return self.bad_lines
}
func (self *ConfigParser) parse(scanner Scanner, name, base_path_for_includes string, depth int) error {
if self.seen_includes[name] { // avoid include loops
return nil
}
self.seen_includes[name] = true
recurse := func(r io.Reader, nname, base_path_for_includes string) error {
if depth > 32 {
return fmt.Errorf("Too many nested include directives while processing config file: %s", name)
}
escanner := bufio.NewScanner(r)
return self.parse(escanner, nname, base_path_for_includes, depth+1)
}
lnum := 0 lnum := 0
make_absolute := func(path string) (string, error) { make_absolute := func(path string) (string, error) {
if path == "" { if path == "" {
@ -58,7 +80,7 @@ func (self *ConfigParser) parse(scanner Scanner, name, base_path_for_includes st
default: default:
err := self.LineHandler(key, val) err := self.LineHandler(key, val)
if err != nil { if err != nil {
self.BadLines = append(self.BadLines, ConfigLine{Src_file: name, Line: line, Line_number: lnum, Err: err}) self.bad_lines = append(self.bad_lines, ConfigLine{Src_file: name, Line: line, Line_number: lnum, Err: err})
} }
case "include", "globinclude", "envinclude": case "include", "globinclude", "envinclude":
var includes []string var includes []string
@ -77,12 +99,15 @@ func (self *ConfigParser) parse(scanner Scanner, name, base_path_for_includes st
} }
} }
case "envinclude": case "envinclude":
for _, x := range os.Environ() { env := self.override_env
if env == nil {
env = os.Environ()
}
for _, x := range env {
key, eval, _ := strings.Cut(x, "=") key, eval, _ := strings.Cut(x, "=")
is_match, err := filepath.Match(val, key) is_match, err := filepath.Match(val, key)
if is_match && err == nil { if is_match && err == nil {
escanner := bufio.NewScanner(strings.NewReader(eval)) err := recurse(strings.NewReader(eval), "<env var: "+key+">", base_path_for_includes)
err := self.parse(escanner, "<env var: "+key+">", base_path_for_includes)
if err != nil { if err != nil {
return err return err
} }
@ -93,8 +118,7 @@ func (self *ConfigParser) parse(scanner Scanner, name, base_path_for_includes st
for _, incpath := range includes { for _, incpath := range includes {
raw, err := os.ReadFile(incpath) raw, err := os.ReadFile(incpath)
if err == nil { if err == nil {
escanner := bufio.NewScanner(strings.NewReader(UnsafeBytesToString(raw))) err := recurse(bytes.NewReader(raw), incpath, filepath.Dir(incpath))
err := self.parse(escanner, incpath, filepath.Dir(incpath))
if err != nil { if err != nil {
return err return err
} }
@ -108,18 +132,24 @@ func (self *ConfigParser) parse(scanner Scanner, name, base_path_for_includes st
return nil return nil
} }
func (self *ConfigParser) ParseFile(path string) error { func (self *ConfigParser) ParseFiles(paths ...string) error {
apath, err := filepath.Abs(path) for _, path := range paths {
if err == nil { apath, err := filepath.Abs(path)
path = apath if err == nil {
path = apath
}
raw, err := os.ReadFile(path)
if err != nil {
return err
}
scanner := bufio.NewScanner(bytes.NewReader(raw))
self.seen_includes = make(map[string]bool)
err = self.parse(scanner, path, filepath.Dir(path), 0)
if err != nil {
return err
}
} }
f, err := os.Open(path) return nil
if err != nil {
return err
}
defer f.Close()
scanner := bufio.NewScanner(f)
return self.parse(scanner, f.Name(), filepath.Dir(f.Name()))
} }
type LinesScanner struct { type LinesScanner struct {
@ -142,5 +172,6 @@ func (self *LinesScanner) Err() error {
func (self *ConfigParser) ParseOverrides(overrides ...string) error { func (self *ConfigParser) ParseOverrides(overrides ...string) error {
s := LinesScanner{lines: overrides} s := LinesScanner{lines: overrides}
return self.parse(&s, "<overrides>", ConfigDir()) self.seen_includes = make(map[string]bool)
return self.parse(&s, "<overrides>", ConfigDir(), 0)
} }

View File

@ -0,0 +1,52 @@
// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>
package utils
import (
"fmt"
"os"
"path/filepath"
"testing"
"github.com/google/go-cmp/cmp"
)
var _ = fmt.Print
func TestConfigParsing(t *testing.T) {
tdir := t.TempDir()
conf_file := filepath.Join(tdir, "a.conf")
os.Mkdir(filepath.Join(tdir, "sub"), 0o700)
os.WriteFile(conf_file, []byte(`
# ignore me
a one
#: other
include sub/b.conf
b
include non-existent
globinclude sub/c?.conf
`), 0o600)
os.WriteFile(filepath.Join(tdir, "sub/b.conf"), []byte("incb cool\ninclude a.conf"), 0o600)
os.WriteFile(filepath.Join(tdir, "sub/c1.conf"), []byte("inc1 cool"), 0o600)
os.WriteFile(filepath.Join(tdir, "sub/c2.conf"), []byte("inc2 cool\nenvinclude ENVINCLUDE"), 0o600)
os.WriteFile(filepath.Join(tdir, "sub/c.conf"), []byte("inc notcool"), 0o600)
var parsed_lines []string
pl := func(key, val string) error {
if key == "error" {
return fmt.Errorf("%s", val)
}
parsed_lines = append(parsed_lines, key+" "+val)
return nil
}
p := ConfigParser{LineHandler: pl, override_env: []string{"ENVINCLUDE=env cool\ninclude c.conf"}}
err := p.ParseFiles(conf_file)
if err != nil {
t.Fatal(err)
}
diff := cmp.Diff([]string{"a one", "incb cool", "b ", "inc1 cool", "inc2 cool", "env cool", "inc notcool"}, parsed_lines)
if diff != "" {
t.Fatalf("Unexpected parsed config values:\n%s", diff)
}
}