ipld-eth-server/vendor/github.com/mikkeloscar/sshconfig/lex.go
2018-09-17 13:25:19 -05:00

278 lines
5.1 KiB
Go

// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// based on the lexer from: src/pkg/text/template/parse/lex.go (golang source)
package sshconfig
import (
"fmt"
"strings"
"unicode"
"unicode/utf8"
)
// pos is a position in input being scanned
type pos int
type item struct {
typ itemType
pos pos
val string
}
func (i item) String() string {
switch {
case i.typ == itemEOF:
return "EOF"
case i.typ == itemError:
return i.val
case len(i.val) > 10:
return fmt.Sprintf("%.10q...", i.val)
}
return fmt.Sprintf("%q", i.val)
}
type itemType int
const (
itemError itemType = iota
itemEOF
itemValue
itemHost
itemHostValue
itemHostName
itemUser
itemPort
itemProxyCommand
itemHostKeyAlgorithms
itemIdentityFile
)
// variables
var variables = map[string]itemType{
"host": itemHost,
"hostname": itemHostName,
"user": itemUser,
"port": itemPort,
"proxycommand": itemProxyCommand,
"hostkeyalgorithms": itemHostKeyAlgorithms,
"identityfile": itemIdentityFile,
}
const eof = -1
// stateFn represents the state of the scanner as a function that returns the next state
type stateFn func(*lexer) stateFn
// lexer holds the state of the scanner
type lexer struct {
input string
state stateFn
pos pos
start pos
width pos
lastPos pos
items chan item // channel of scanned items
}
// next returns the next rune in the input
func (l *lexer) next() rune {
if int(l.pos) >= len(l.input) {
l.width = 0
return eof
}
r, w := utf8.DecodeRuneInString(l.input[l.pos:])
l.width = pos(w)
l.pos += l.width
return r
}
// peek returns but does not consume the next rune in the input
func (l *lexer) peek() rune {
r := l.next()
l.backup()
return r
}
// backup steps back one rune. Can only be called once per call of next
func (l *lexer) backup() {
l.pos -= l.width
}
// emit passes an item back to the client
func (l *lexer) emit(t itemType) {
l.items <- item{t, l.start, l.input[l.start:l.pos]}
l.start = l.pos
}
// ignore skips over the pending input before this point
func (l *lexer) ignore() {
l.start = l.pos
}
// errorf returns an error token and terminates the scan by passing
// back a nil pointer that will be the next state, terminating l.nextItem.
func (l *lexer) errorf(format string, args ...interface{}) stateFn {
l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)}
return nil
}
// nextItem returns the next item from the input.
func (l *lexer) nextItem() item {
item := <-l.items
l.lastPos = item.pos
return item
}
func lex(input string) *lexer {
l := &lexer{
input: input,
items: make(chan item),
}
go l.run()
return l
}
func (l *lexer) run() {
for l.state = lexEnv; l.state != nil; {
l.state = l.state(l)
}
}
func lexEnv(l *lexer) stateFn {
switch r := l.next(); {
case r == eof:
l.emit(itemEOF)
return nil
case isAlphaNumeric(r):
return lexVariable
case r == '#':
return lexComment
case r == '\r':
l.ignore()
if l.next() != '\n' {
l.errorf("expected \\n")
return nil
}
l.ignore()
return lexEnv
case r == '\t' || r == ' ' || r == '\n':
l.ignore()
return lexEnv
default:
l.errorf("unable to parse character: %c", r)
return nil
}
}
func lexComment(l *lexer) stateFn {
for {
switch l.next() {
case '\r':
l.ignore()
if l.next() != '\n' {
l.errorf("expected \\n")
return nil
}
l.ignore()
return lexEnv
case '\n':
l.ignore()
return lexEnv
default:
l.ignore()
}
}
}
func lexVariable(l *lexer) stateFn {
for {
switch r := l.next(); {
case isAlphaNumeric(r):
// absorb
case r == ' ' || r == '=':
l.backup()
variable := strings.ToLower(l.input[l.start:l.pos])
if _, ok := variables[variable]; ok {
l.emit(variables[variable])
l.next()
l.ignore()
if variable == "host" {
return lexHostValue
}
return lexValue
}
return lexValue
default:
pattern := l.input[l.start:l.pos]
return l.errorf("invalid pattern: %#v", pattern)
}
}
}
func lexHostValue(l *lexer) stateFn {
for {
switch l.next() {
case ' ':
switch l.peek() {
case '\r':
if l.peek() != '\n' {
return l.errorf("expected \\n")
}
fallthrough
case '\n', eof:
break
default:
// more coming, wait
continue
}
l.emit(itemValue)
case '\r':
if l.peek() != '\n' {
return l.errorf("expected \\n")
}
fallthrough
case '\n':
l.backup()
l.emit(itemHostValue)
return lexEnv
case eof:
l.backup()
l.emit(itemHostValue)
l.next()
l.emit(itemEOF)
return nil
}
}
}
func lexValue(l *lexer) stateFn {
for {
switch l.next() {
case '\r':
if l.peek() != '\n' {
return l.errorf("expected \\n")
}
fallthrough
case '\n':
l.backup()
l.emit(itemValue)
return lexEnv
case eof:
l.backup()
l.emit(itemValue)
l.next()
l.emit(itemEOF)
return nil
}
}
}
// isAlphaNumeric reports whether r is an alphabetic or digit.
func isAlphaNumeric(r rune) bool {
return unicode.IsLetter(r) || unicode.IsDigit(r)
}