@@ -2,7 +2,6 @@ package git
22
33import (
44 "bufio"
5- "io"
65 "net/url"
76 "os"
87 "path/filepath"
@@ -13,12 +12,10 @@ import (
1312)
1413
1514var (
16- sshHostRE ,
1715 sshTokenRE * regexp.Regexp
1816)
1917
2018func init () {
21- sshHostRE = regexp .MustCompile ("(?i)^[ \t ]*(host|hostname)[ \t ]+(.+)$" )
2219 sshTokenRE = regexp .MustCompile (`%[%h]` )
2320}
2421
@@ -45,55 +42,46 @@ func (m SSHAliasMap) Translator() func(*url.URL) *url.URL {
4542 }
4643}
4744
48- // ParseSSHConfig constructs a map of SSH hostname aliases based on user and
49- // system configuration files
50- func ParseSSHConfig () SSHAliasMap {
51- configFiles := []string {
52- "/etc/ssh_config" ,
53- "/etc/ssh/ssh_config" ,
54- }
55- if homedir , err := homedir .Dir (); err == nil {
56- userConfig := filepath .Join (homedir , ".ssh" , "config" )
57- configFiles = append ([]string {userConfig }, configFiles ... )
58- }
59-
60- openFiles := make ([]io.Reader , 0 , len (configFiles ))
61- for _ , file := range configFiles {
62- f , err := os .Open (file )
63- if err != nil {
64- continue
65- }
66- defer f .Close ()
67- openFiles = append (openFiles , f )
68- }
69- return sshParse (openFiles ... )
45+ type parser struct {
46+ aliasMap SSHAliasMap
7047}
7148
72- func sshParse ( r ... io. Reader ) SSHAliasMap {
73- config := make ( SSHAliasMap )
74- for _ , file := range r {
75- _ = sshParseConfig ( config , file )
49+ func ( p * parser ) read ( fileName string ) error {
50+ file , err := os . Open ( fileName )
51+ if err != nil {
52+ return err
7653 }
77- return config
78- }
54+ defer file .Close ()
7955
80- func sshParseConfig (c SSHAliasMap , file io.Reader ) error {
8156 hosts := []string {"*" }
8257 scanner := bufio .NewScanner (file )
8358 for scanner .Scan () {
8459 line := scanner .Text ()
85- match := sshHostRE .FindStringSubmatch (line )
86- if match == nil {
60+ fields := strings .Fields (line )
61+
62+ if len (fields ) < 2 {
8763 continue
8864 }
8965
90- names := strings .Fields (match [2 ])
91- if strings .EqualFold (match [1 ], "host" ) {
92- hosts = names
93- } else {
66+ directive , params := fields [0 ], fields [1 :]
67+ switch {
68+ case strings .EqualFold (directive , "Host" ):
69+ hosts = params
70+ case strings .EqualFold (directive , "Hostname" ):
9471 for _ , host := range hosts {
95- for _ , name := range names {
96- c [host ] = sshExpandTokens (name , host )
72+ for _ , name := range params {
73+ p .aliasMap [host ] = sshExpandTokens (name , host )
74+ }
75+ }
76+ case strings .EqualFold (directive , "Include" ):
77+ for _ , path := range absolutePaths (fileName , params ) {
78+ fileNames , err := filepath .Glob (path )
79+ if err != nil {
80+ continue
81+ }
82+
83+ for _ , fileName := range fileNames {
84+ _ = p .read (fileName )
9785 }
9886 }
9987 }
@@ -102,6 +90,55 @@ func sshParseConfig(c SSHAliasMap, file io.Reader) error {
10290 return scanner .Err ()
10391}
10492
93+ func isSystem (path string ) bool {
94+ return strings .HasPrefix (path , "/etc/ssh" )
95+ }
96+
97+ func absolutePaths (parentFile string , paths []string ) []string {
98+ absPaths := make ([]string , len (paths ))
99+
100+ for i , path := range paths {
101+ switch {
102+ case filepath .IsAbs (path ):
103+ absPaths [i ] = path
104+ case strings .HasPrefix (path , "~" ):
105+ absPaths [i ], _ = homedir .Expand (path )
106+ case isSystem (parentFile ):
107+ absPaths [i ] = filepath .Join ("/etc" , "ssh" , path )
108+ default :
109+ dir , _ := homedir .Dir ()
110+ absPaths [i ] = filepath .Join (dir , ".ssh" , path )
111+ }
112+ }
113+
114+ return absPaths
115+ }
116+
117+ func parse (files ... string ) SSHAliasMap {
118+ p := parser {aliasMap : make (SSHAliasMap )}
119+
120+ for _ , file := range files {
121+ _ = p .read (file )
122+ }
123+
124+ return p .aliasMap
125+ }
126+
127+ // ParseSSHConfig constructs a map of SSH hostname aliases based on user and
128+ // system configuration files
129+ func ParseSSHConfig () SSHAliasMap {
130+ configFiles := []string {
131+ "/etc/ssh_config" ,
132+ "/etc/ssh/ssh_config" ,
133+ }
134+ if homedir , err := homedir .Dir (); err == nil {
135+ userConfig := filepath .Join (homedir , ".ssh" , "config" )
136+ configFiles = append ([]string {userConfig }, configFiles ... )
137+ }
138+
139+ return parse (configFiles ... )
140+ }
141+
105142func sshExpandTokens (text , host string ) string {
106143 return sshTokenRE .ReplaceAllStringFunc (text , func (match string ) string {
107144 switch match {
0 commit comments