| 222 | return errors |
| 223 | |
| 224 | def _parse_main_lines(self, lines: List[str]) -> None: |
| 225 | self.config.hosts.clear() |
| 226 | self.config.global_options.clear() |
| 227 | self.config.include_directives.clear() |
| 228 | |
| 229 | current_host: Optional[SSHHost] = None |
| 230 | in_host = False |
| 231 | |
| 232 | for idx, line in enumerate(lines): |
| 233 | stripped = line.strip() |
| 234 | if not stripped or stripped.startswith("#"): |
| 235 | if in_host and current_host is not None: |
| 236 | current_host.raw_lines.append(line) |
| 237 | continue |
| 238 | |
| 239 | if stripped.lower().startswith("include "): |
| 240 | include_arg = stripped.split(None, 1)[1] |
| 241 | self.config.include_directives.append(include_arg) |
| 242 | continue |
| 243 | |
| 244 | if stripped.lower().startswith("host "): |
| 245 | if current_host is not None: |
| 246 | current_host.end_line = idx - 1 |
| 247 | self.config.hosts.append(current_host) |
| 248 | patterns = stripped.split(None, 1)[1].split() |
| 249 | current_host = SSHHost( |
| 250 | patterns=patterns, start_line=idx, raw_lines=[line] |
| 251 | ) |
| 252 | in_host = True |
| 253 | continue |
| 254 | |
| 255 | m = re.match(r"^(\S+)\s+(.+)$", stripped) |
| 256 | if m: |
| 257 | key, value = m.group(1), m.group(2) |
| 258 | indentation = line[: len(line) - len(line.lstrip())] |
| 259 | opt = SSHOption(key=key, value=value, indentation=indentation) |
| 260 | if in_host and current_host is not None: |
| 261 | current_host.options.append(opt) |
| 262 | current_host.raw_lines.append(line) |
| 263 | else: |
| 264 | self.config.global_options.append(opt) |
| 265 | continue |
| 266 | |
| 267 | if in_host and current_host is not None: |
| 268 | current_host.raw_lines.append(line) |
| 269 | |
| 270 | if current_host is not None: |
| 271 | current_host.end_line = len(lines) - 1 |
| 272 | self.config.hosts.append(current_host) |
| 273 | |
| 274 | def _resolve_includes(self) -> None: |
| 275 | resolved: Dict[Path, List[str]] = {} |