<commit_before>// Copyright 2016 The rkt Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//+build linux

package common

// seccomp default whitelists/blacklists.
// rkt tries not to diverge from docker here, for the moment.

var (
	// DockerDefaultSeccompWhitelist contains a default whitelist of syscalls,
	// used by docker for seccomp filtering.
	// See https://github.com/docker/docker/blob/master/profiles/seccomp/default.json
	DockerDefaultSeccompWhitelist = []string{
		"accept",
		"accept4",
		"access",
		"alarm",
		"bind",
		"brk",
		"capget",
		"capset",
		"chdir",
		"chmod",
		"chown",
		"chown32",
		"clock_getres",
		"clock_gettime",
		"clock_nanosleep",
		"close",
		"connect",
		"copy_file_range",
		"creat",
		"dup",
		"dup2",
		"dup3",
		"epoll_create",
		"epoll_create1",
		"epoll_ctl",
		"epoll_ctl_old",
		"epoll_pwait",
		"epoll_wait",
		"epoll_wait_old",
		"eventfd",
		"eventfd2",
		"execve",
		"execveat",
		"exit",
		"exit_group",
		"faccessat",
		"fadvise64",
		"fadvise64_64",
		"fallocate",
		"fanotify_mark",
		"fchdir",
		"fchmod",
		"fchmodat",
		"fchown",
		"fchown32",
		"fchownat",
		"fcntl",
		"fcntl64",
		"fdatasync",
		"fgetxattr",
		"flistxattr",
		"flock",
		"fork",
		"fremovexattr",
		"fsetxattr",
		"fstat",
		"fstat64",
		"fstatat64",
		"fstatfs",
		"fstatfs64",
		"fsync",
		"ftruncate",
		"ftruncate64",
		"futex",
		"futimesat",
		"getcpu",
		"getcwd",
		"getdents",
		"getdents64",
		"getegid",
		"getegid32",
		"geteuid",
		"geteuid32",
		"getgid",
		"getgid32",
		"getgroups",
		"getgroups32",
		"getitimer",
		"getpeername",
		"getpgid",
		"getpgrp",
		"getpid",
		"getppid",
		"getpriority",
		"getrandom",
		"getresgid",
		"getresgid32",
		"getresuid",
		"getresuid32",
		"getrlimit",
		"get_robust_list",
		"getrusage",
		"getsid",
		"getsockname",
		"getsockopt",
		"get_thread_area",
		"gettid",
		"gettimeofday",
		"getuid",
		"getuid32",
		"getxattr",
		"inotify_add_watch",
		"inotify_init",
		"inotify_init1",
		"inotify_rm_watch",
		"io_cancel",
		"ioctl",
		"io_destroy",
		"io_getevents",
		"ioprio_get",
		"ioprio_set",
		"io_setup",
		"io_submit",
		"ipc",
		"kill",
		"lchown",
		"lchown32",
		"lgetxattr",
		"link",
		"linkat",
		"listen",
		"listxattr",
		"llistxattr",
		"_llseek",
		"lremovexattr",
		"lseek",
		"lsetxattr",
		"lstat",
		"lstat64",
		"madvise",
		"memfd_create",
		"mincore",
		"mkdir",
		"mkdirat",
		"mknod",
		"mknodat",
		"mlock",
		"mlock2",
		"mlockall",
		"mmap",
		"mmap2",
		"mprotect",
		"mq_getsetattr",
		"mq_notify",
		"mq_open",
		"mq_timedreceive",
		"mq_timedsend",
		"mq_unlink",
		"mremap",
		"msgctl",
		"msgget",
		"msgrcv",
		"msgsnd",
		"msync",
		"munlock",
		"munlockall",
		"munmap",
		"nanosleep",
		"newfstatat",
		"_newselect",
		"open",
		"openat",
		"pause",
		"personality", // this is args-filtered by docker
		"pipe",
		"pipe2",
		"poll",
		"ppoll",
		"prctl",
		"pread64",
		"preadv",
		"prlimit64",
		"pselect6",
		"pwrite64",
		"pwritev",
		"read",
		"readahead",
		"readlink",
		"readlinkat",
		"readv",
		"recv",
		"recvfrom",
		"recvmmsg",
		"recvmsg",
		"remap_file_pages",
		"removexattr",
		"rename",
		"renameat",
		"renameat2",
		"restart_syscall",
		"rmdir",
		"rt_sigaction",
		"rt_sigpending",
		"rt_sigprocmask",
		"rt_sigqueueinfo",
		"rt_sigreturn",
		"rt_sigsuspend",
		"rt_sigtimedwait",
		"rt_tgsigqueueinfo",
		"sched_getaffinity",
		"sched_getattr",
		"sched_getparam",
		"sched_get_priority_max",
		"sched_get_priority_min",
		"sched_getscheduler",
		"sched_rr_get_interval",
		"sched_setaffinity",
		"sched_setattr",
		"sched_setparam",
		"sched_setscheduler",
		"sched_yield",
		"seccomp",
		"select",
		"semctl",
		"semget",
		"semop",
		"semtimedop",
		"send",
		"sendfile",
		"sendfile64",
		"sendmmsg",
		"sendmsg",
		"sendto",
		"setfsgid",
		"setfsgid32",
		"setfsuid",
		"setfsuid32",
		"setgid",
		"setgid32",
		"setgroups",
		"setgroups32",
		"setitimer",
		"setpgid",
		"setpriority",
		"setregid",
		"setregid32",
		"setresgid",
		"setresgid32",
		"setresuid",
		"setresuid32",
		"setreuid",
		"setreuid32",
		"setrlimit",
		"set_robust_list",
		"setsid",
		"setsockopt",
		"set_thread_area",
		"set_tid_address",
		"setuid",
		"setuid32",
		"setxattr",
		"shmat",
		"shmctl",
		"shmdt",
		"shmget",
		"shutdown",
		"sigaltstack",
		"signalfd",
		"signalfd4",
		"sigreturn",
		"socket",
		"socketcall",
		"socketpair",
		"splice",
		"stat",
		"stat64",
		"statfs",
		"statfs64",
		"symlink",
		"symlinkat",
		"sync",
		"sync_file_range",
		"syncfs",
		"sysinfo",
		"syslog",
		"tee",
		"tgkill",
		"time",
		"timer_create",
		"timer_delete",
		"timerfd_create",
		"timerfd_gettime",
		"timerfd_settime",
		"timer_getoverrun",
		"timer_gettime",
		"timer_settime",
		"times",
		"tkill",
		"truncate",
		"truncate64",
		"ugetrlimit",
		"umask",
		"uname",
		"unlink",
		"unlinkat",
		"utime",
		"utimensat",
		"utimes",
		"vfork",
		"vmsplice",
		"wait4",
		"waitid",
		"waitpid",
		"write",
		"writev",
		"arch_prctl",
		"modify_ldt",
		"chroot",
		"clone", // this is args-filtered by docker
	}
	// DockerDefaultSeccompBlacklist contains a default blacklist of syscalls,
	// used by docker for seccomp filtering.
	// See https://github.com/docker/docker/blob/master/docs/security/seccomp.md
	DockerDefaultSeccompBlacklist = []string{
		"acct",
		"add_key",
		"adjtimex",
		"bpf",
		"clock_adjtime",
		"clock_settime",
		// "clone", // this is args-filtered by docker
		"create_module",
		"delete_module",
		"finit_module",
		"get_kernel_syms",
		"get_mempolicy",
		"init_module",
		"ioperm",
		"iopl",
		"kcmp",
		"kexec_file_load",
		"kexec_load",
		"keyctl",
		"lookup_dcookie",
		"mbind",
		"mount",
		"move_pages",
		"name_to_handle_at",
		"nfsservctl",
		"open_by_handle_at",
		"perf_event_open",
		// "personality", // this is args-filtered by docker
		"pivot_root",
		"process_vm_readv",
		"process_vm_writev",
		"ptrace",
		"query_module",
		"quotactl",
		"reboot",
		"request_key",
		"set_mempolicy",
		"setns",
		"settimeofday",
		"stime",
		"swapon",
		"swapoff",
		"sysfs",
		"_sysctl",
		"umount",
		"umount2",
		"unshare",
		"uselib",
		"userfaultfd",
		"ustat",
		"vm86",
		"vm86old",
	}

	// RktDefaultSeccompBlacklist contains a default blacklist of syscalls,
	// used by rkt for seccomp filtering.
	RktDefaultSeccompBlacklist = DockerDefaultSeccompBlacklist
	// RktDefaultSeccompWhitelist contains a default whitelist of syscalls,
	// used by rkt for seccomp filtering.
	RktDefaultSeccompWhitelist = DockerDefaultSeccompWhitelist
)
<commit_msg>seccomp: add ARM specific syscalls if on an ARM device.<commit_after>// Copyright 2016 The rkt Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//+build linux

package common

import "runtime"

// seccomp default whitelists/blacklists.
// rkt tries not to diverge from docker here, for the moment.

var (
	// DockerDefaultSeccompWhitelist contains a default whitelist of syscalls,
	// used by docker for seccomp filtering.
	// See https://github.com/docker/docker/blob/master/profiles/seccomp/default.json
	DockerDefaultSeccompWhitelist = []string{
		"accept",
		"accept4",
		"access",
		"alarm",
		"bind",
		"brk",
		"capget",
		"capset",
		"chdir",
		"chmod",
		"chown",
		"chown32",
		"clock_getres",
		"clock_gettime",
		"clock_nanosleep",
		"close",
		"connect",
		"copy_file_range",
		"creat",
		"dup",
		"dup2",
		"dup3",
		"epoll_create",
		"epoll_create1",
		"epoll_ctl",
		"epoll_ctl_old",
		"epoll_pwait",
		"epoll_wait",
		"epoll_wait_old",
		"eventfd",
		"eventfd2",
		"execve",
		"execveat",
		"exit",
		"exit_group",
		"faccessat",
		"fadvise64",
		"fadvise64_64",
		"fallocate",
		"fanotify_mark",
		"fchdir",
		"fchmod",
		"fchmodat",
		"fchown",
		"fchown32",
		"fchownat",
		"fcntl",
		"fcntl64",
		"fdatasync",
		"fgetxattr",
		"flistxattr",
		"flock",
		"fork",
		"fremovexattr",
		"fsetxattr",
		"fstat",
		"fstat64",
		"fstatat64",
		"fstatfs",
		"fstatfs64",
		"fsync",
		"ftruncate",
		"ftruncate64",
		"futex",
		"futimesat",
		"getcpu",
		"getcwd",
		"getdents",
		"getdents64",
		"getegid",
		"getegid32",
		"geteuid",
		"geteuid32",
		"getgid",
		"getgid32",
		"getgroups",
		"getgroups32",
		"getitimer",
		"getpeername",
		"getpgid",
		"getpgrp",
		"getpid",
		"getppid",
		"getpriority",
		"getrandom",
		"getresgid",
		"getresgid32",
		"getresuid",
		"getresuid32",
		"getrlimit",
		"get_robust_list",
		"getrusage",
		"getsid",
		"getsockname",
		"getsockopt",
		"get_thread_area",
		"gettid",
		"gettimeofday",
		"getuid",
		"getuid32",
		"getxattr",
		"inotify_add_watch",
		"inotify_init",
		"inotify_init1",
		"inotify_rm_watch",
		"io_cancel",
		"ioctl",
		"io_destroy",
		"io_getevents",
		"ioprio_get",
		"ioprio_set",
		"io_setup",
		"io_submit",
		"ipc",
		"kill",
		"lchown",
		"lchown32",
		"lgetxattr",
		"link",
		"linkat",
		"listen",
		"listxattr",
		"llistxattr",
		"_llseek",
		"lremovexattr",
		"lseek",
		"lsetxattr",
		"lstat",
		"lstat64",
		"madvise",
		"memfd_create",
		"mincore",
		"mkdir",
		"mkdirat",
		"mknod",
		"mknodat",
		"mlock",
		"mlock2",
		"mlockall",
		"mmap",
		"mmap2",
		"mprotect",
		"mq_getsetattr",
		"mq_notify",
		"mq_open",
		"mq_timedreceive",
		"mq_timedsend",
		"mq_unlink",
		"mremap",
		"msgctl",
		"msgget",
		"msgrcv",
		"msgsnd",
		"msync",
		"munlock",
		"munlockall",
		"munmap",
		"nanosleep",
		"newfstatat",
		"_newselect",
		"open",
		"openat",
		"pause",
		"personality", // this is args-filtered by docker
		"pipe",
		"pipe2",
		"poll",
		"ppoll",
		"prctl",
		"pread64",
		"preadv",
		"prlimit64",
		"pselect6",
		"pwrite64",
		"pwritev",
		"read",
		"readahead",
		"readlink",
		"readlinkat",
		"readv",
		"recv",
		"recvfrom",
		"recvmmsg",
		"recvmsg",
		"remap_file_pages",
		"removexattr",
		"rename",
		"renameat",
		"renameat2",
		"restart_syscall",
		"rmdir",
		"rt_sigaction",
		"rt_sigpending",
		"rt_sigprocmask",
		"rt_sigqueueinfo",
		"rt_sigreturn",
		"rt_sigsuspend",
		"rt_sigtimedwait",
		"rt_tgsigqueueinfo",
		"sched_getaffinity",
		"sched_getattr",
		"sched_getparam",
		"sched_get_priority_max",
		"sched_get_priority_min",
		"sched_getscheduler",
		"sched_rr_get_interval",
		"sched_setaffinity",
		"sched_setattr",
		"sched_setparam",
		"sched_setscheduler",
		"sched_yield",
		"seccomp",
		"select",
		"semctl",
		"semget",
		"semop",
		"semtimedop",
		"send",
		"sendfile",
		"sendfile64",
		"sendmmsg",
		"sendmsg",
		"sendto",
		"setfsgid",
		"setfsgid32",
		"setfsuid",
		"setfsuid32",
		"setgid",
		"setgid32",
		"setgroups",
		"setgroups32",
		"setitimer",
		"setpgid",
		"setpriority",
		"setregid",
		"setregid32",
		"setresgid",
		"setresgid32",
		"setresuid",
		"setresuid32",
		"setreuid",
		"setreuid32",
		"setrlimit",
		"set_robust_list",
		"setsid",
		"setsockopt",
		"set_thread_area",
		"set_tid_address",
		"setuid",
		"setuid32",
		"setxattr",
		"shmat",
		"shmctl",
		"shmdt",
		"shmget",
		"shutdown",
		"sigaltstack",
		"signalfd",
		"signalfd4",
		"sigreturn",
		"socket",
		"socketcall",
		"socketpair",
		"splice",
		"stat",
		"stat64",
		"statfs",
		"statfs64",
		"symlink",
		"symlinkat",
		"sync",
		"sync_file_range",
		"syncfs",
		"sysinfo",
		"syslog",
		"tee",
		"tgkill",
		"time",
		"timer_create",
		"timer_delete",
		"timerfd_create",
		"timerfd_gettime",
		"timerfd_settime",
		"timer_getoverrun",
		"timer_gettime",
		"timer_settime",
		"times",
		"tkill",
		"truncate",
		"truncate64",
		"ugetrlimit",
		"umask",
		"uname",
		"unlink",
		"unlinkat",
		"utime",
		"utimensat",
		"utimes",
		"vfork",
		"vmsplice",
		"wait4",
		"waitid",
		"waitpid",
		"write",
		"writev",
		"arch_prctl",
		"modify_ldt",
		"chroot",
		"clone", // this is args-filtered by docker
	}
	// DockerDefaultSeccompBlacklist contains a default blacklist of syscalls,
	// used by docker for seccomp filtering.
	// See https://github.com/docker/docker/blob/master/docs/security/seccomp.md
	DockerDefaultSeccompBlacklist = []string{
		"acct",
		"add_key",
		"adjtimex",
		"bpf",
		"clock_adjtime",
		"clock_settime",
		// "clone", // this is args-filtered by docker
		"create_module",
		"delete_module",
		"finit_module",
		"get_kernel_syms",
		"get_mempolicy",
		"init_module",
		"ioperm",
		"iopl",
		"kcmp",
		"kexec_file_load",
		"kexec_load",
		"keyctl",
		"lookup_dcookie",
		"mbind",
		"mount",
		"move_pages",
		"name_to_handle_at",
		"nfsservctl",
		"open_by_handle_at",
		"perf_event_open",
		// "personality", // this is args-filtered by docker
		"pivot_root",
		"process_vm_readv",
		"process_vm_writev",
		"ptrace",
		"query_module",
		"quotactl",
		"reboot",
		"request_key",
		"set_mempolicy",
		"setns",
		"settimeofday",
		"stime",
		"swapon",
		"swapoff",
		"sysfs",
		"_sysctl",
		"umount",
		"umount2",
		"unshare",
		"uselib",
		"userfaultfd",
		"ustat",
		"vm86",
		"vm86old",
	}

	//RktDefaultSeccompArmWhitelist contains the additional needed syscalls for arm support
	RktDefaultSeccompArmWhitelist = []string{
		"arm_fadvise64_64",
		"arm_sync_file_range",
		"breakpoint",
		"cacheflush",
		"set_tls",
		"sync_file_range2",
	}

	// RktDefaultSeccompBlacklist contains a default blacklist of syscalls,
	// used by rkt for seccomp filtering.
	RktDefaultSeccompBlacklist = DockerDefaultSeccompBlacklist
	// RktDefaultSeccompWhitelist contains a default whitelist of syscalls,
	// used by rkt for seccomp filtering.
	RktDefaultSeccompWhitelist = DockerDefaultSeccompWhitelist
)

func init() {
	if arch := runtime.GOARCH; arch == "arm" || arch == "arm64" {
		RktDefaultSeccompWhitelist = append(RktDefaultSeccompWhitelist, RktDefaultSeccompArmWhitelist...)
	}
}
<|endoftext|>


<commit_before>package rexster_client

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"strings"
)

// Rexster API

type Rexster struct {
	Host     string // Rexster server host
	RestPort uint16 // Rexster server REST API port (usually 8182)
	Debug    bool   // Enable debug logging
}

type Graph struct {
	Name   string  // Name of graph served by Rexster
	Server Rexster // The Rexster server that serves this graph
}

type Response struct {
	Results   interface{} `json:"results"`
	Success   bool        `json:"success"`
	Version   string      `json:"version"`
	QueryTime float64     `json:"queryTime"`
}

type errorResponse struct {
	Message string `json:"message"`
	Error   string `json:"error"`
}

func (g Graph) GetVertex(id string) (res *Response, err error) {
	g.log("GetVertex", id)
	url := g.getVertexURL(id)
	return g.Server.get(url)
}

func (g Graph) QueryVertices(key, value string) (res *Response, err error) {
	g.log("QueryVertices", key, value)
	url := g.queryVerticesURL(key, value)
	return g.Server.get(url)
}

func (g Graph) GetVertexBothE(id string) (res *Response, err error) {
	g.log("GetVertexBothE", id)
	url := g.getVertexSubURL(id, "bothE")
	return g.Server.get(url)
}

func (g Graph) GetVertexInE(id string) (res *Response, err error) {
	g.log("GetVertexInE", id)
	url := g.getVertexSubURL(id, "inE")
	return g.Server.get(url)
}

func (g Graph) GetVertexOutE(id string) (res *Response, err error) {
	g.log("GetVertexOutE", id)
	url := g.getVertexSubURL(id, "outE")
	return g.Server.get(url)
}

func (g Graph) QueryEdges(key, value string) (res *Response, err error) {
	g.log("QueryEdges", key, value)
	url := g.queryEdgesURL(key, value)
	return g.Server.get(url)
}

func (g Graph) Eval(script string) (res *Response, err error) {
	g.log("Eval", script)
	url := g.evalURL(script)
	return g.Server.get(url)
}

func (g Graph) CreateOrUpdateVertex(v *Vertex) (res *Response, err error) {
	g.log("CreateOrUpdateVertex", v.Id())
	url := g.getVertexURL(v.Id())
	return g.Server.send("POST", url, v.Map)
}

func (g Graph) log(v ...interface{}) {
	if g.Server.Debug {
		vs := []interface{}{"GRAPH", g.Name}
		vs = append(vs, v...)
		log.Println(vs...)
	}
}

func (r Rexster) get(url string) (resp *Response, err error) {
	return r.send("GET", url, nil)
}

func (r Rexster) send(method string, url string, data map[string]interface{}) (resp *Response, err error) {
	var body io.Reader
	if data != nil {
		buf, err := json.Marshal(data)
		if err != nil {
			return nil, err
		}
		body = bytes.NewReader(buf)
	}
	req, err := http.NewRequest(method, url, body)
	if err != nil {
		return nil, err
	}
	if body != nil {
		req.Header.Add("Content-Type", "application/json")
	}

	hr, err := http.DefaultClient.Do(req)
	if err != nil {
		if r.Debug {
			log.Println("HTTP GET failed", url, err)
		}
		return nil, err
	}
	resp, errResp := readResponseOrError(hr)
	if errResp != nil {
		err = errors.New(strings.TrimSpace(strings.Join([]string{errResp.Message, errResp.Error}, " ")))
		if r.Debug {
			log.Println("HTTP GET failed", url, err)
		}
	}
	return
}

func readResponseOrError(hr *http.Response) (resp *Response, errResp *errorResponse) {
	dec := json.NewDecoder(hr.Body)
	defer hr.Body.Close()
	if hr.StatusCode == 200 {
		resp = new(Response)
		dec.Decode(resp)
	} else {
		errResp = new(errorResponse)
		dec.Decode(errResp)
	}
	return
}

// URLs

func (r Rexster) baseURL() *url.URL {
	return &url.URL{
		Scheme: "http",
		Host:   fmt.Sprintf("%s:%d", r.Host, r.RestPort),
	}
}

func (g Graph) baseURL() *url.URL {
	u := g.Server.baseURL()
	u.Path = "/graphs/" + g.Name
	return u
}

func (g Graph) getVertexURL(id string) string {
	u := g.baseURL()
	u.Path += "/vertices/"
	return u.String() + strings.Replace(id, "/", "%2F", -1) // escape slashes
}

func (g Graph) queryVerticesURL(key, value string) string {
	u := g.baseURL()
	u.Path += "/vertices"
	q := url.Values{"key": {key}, "value": {value}}
	u.RawQuery = q.Encode()
	return u.String()
}

func (g Graph) getVertexSubURL(id, subresource string) string {
	u := g.getVertexURL(id)
	return u + "/" + subresource
}

func (g Graph) queryEdgesURL(key, value string) string {
	u := g.baseURL()
	u.Path += "/edges"
	q := url.Values{"key": {key}, "value": {value}}
	u.RawQuery = q.Encode()
	return u.String()
}

func (g Graph) evalURL(script string) string {
	u := g.baseURL()
	u.Path += "/tp/gremlin"
	q := url.Values{"script": []string{script}}
	u.RawQuery = q.Encode()
	return u.String()
}

// Data

type Vertex struct {
	Map map[string]interface{}
}

func NewVertex(id string, properties map[string]interface{}) (v *Vertex) {
	if properties == nil {
		properties = make(map[string]interface{}, 0)
	}
	v = &Vertex{properties}
	v.Map["_id"] = id
	return
}

// Vertex() gets the single vertex in the response. If the response
// does not contain a single vertex (i.e., if it contains multiple
// vertices, or a different data type), Vertex() returns nil.
func (r *Response) Vertex() (v *Vertex) {
	if v, ok := r.Results.(map[string]interface{}); ok && v["_type"] == "vertex" {
		return &Vertex{v}
	} else {
		return nil
	}
}

func (v Vertex) Id() string {
	return fmt.Sprintf("%v", v.Map["_id"])
}

func (v Vertex) Get(key string) string {
	if x, ok := v.Map[key]; ok {
		if s, ok := x.(string); ok {
			return s
		}
	}
	return ""
}

// Vertices() gets the array of vertices in the response. If the
// response does not contain an array of vertices (i.e., if it
// contains a single vertex not in an array, or a different data
// type), Vertices() returns nil.
func (r *Response) Vertices() (vs []*Vertex) {
	if vv, ok := r.Results.([]interface{}); ok {
		vs = make([]*Vertex, len(vv))
		for i, v := range vv {
			if v, ok := v.(map[string]interface{}); ok && v["_type"] == "vertex" {
				vs[i] = &Vertex{v}
			} else {
				return nil
			}
		}
	}
	return
}

type Edge struct {
	Map map[string]interface{}
}

// Edges() gets the array of edges in the response. If the
// response does not contain an array of edges (i.e., if it
// contains a single edge not in an array, or a different data
// type), Edges() returns nil.
func (r *Response) Edges() (es []*Edge) {
	if ee, ok := r.Results.([]interface{}); ok {
		es = make([]*Edge, len(ee))
		for i, e := range ee {
			if e, ok := e.(map[string]interface{}); ok && e["_type"] == "edge" {
				es[i] = &Edge{e}
			} else {
				return nil
			}
		}
	}
	return
}

func (e Edge) Get(key string) string {
	if x, ok := e.Map[key]; ok {
		if s, ok := x.(string); ok {
			return s
		}
	}
	return ""
}
<commit_msg>Factor out escapeSlashes<commit_after>package rexster_client

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"strings"
)

// Rexster API

type Rexster struct {
	Host     string // Rexster server host
	RestPort uint16 // Rexster server REST API port (usually 8182)
	Debug    bool   // Enable debug logging
}

type Graph struct {
	Name   string  // Name of graph served by Rexster
	Server Rexster // The Rexster server that serves this graph
}

type Response struct {
	Results   interface{} `json:"results"`
	Success   bool        `json:"success"`
	Version   string      `json:"version"`
	QueryTime float64     `json:"queryTime"`
}

type errorResponse struct {
	Message string `json:"message"`
	Error   string `json:"error"`
}

func (g Graph) GetVertex(id string) (res *Response, err error) {
	g.log("GetVertex", id)
	url := g.getVertexURL(id)
	return g.Server.get(url)
}

func (g Graph) QueryVertices(key, value string) (res *Response, err error) {
	g.log("QueryVertices", key, value)
	url := g.queryVerticesURL(key, value)
	return g.Server.get(url)
}

func (g Graph) GetVertexBothE(id string) (res *Response, err error) {
	g.log("GetVertexBothE", id)
	url := g.getVertexSubURL(id, "bothE")
	return g.Server.get(url)
}

func (g Graph) GetVertexInE(id string) (res *Response, err error) {
	g.log("GetVertexInE", id)
	url := g.getVertexSubURL(id, "inE")
	return g.Server.get(url)
}

func (g Graph) GetVertexOutE(id string) (res *Response, err error) {
	g.log("GetVertexOutE", id)
	url := g.getVertexSubURL(id, "outE")
	return g.Server.get(url)
}

func (g Graph) QueryEdges(key, value string) (res *Response, err error) {
	g.log("QueryEdges", key, value)
	url := g.queryEdgesURL(key, value)
	return g.Server.get(url)
}

func (g Graph) Eval(script string) (res *Response, err error) {
	g.log("Eval", script)
	url := g.evalURL(script)
	return g.Server.get(url)
}

func (g Graph) CreateOrUpdateVertex(v *Vertex) (res *Response, err error) {
	g.log("CreateOrUpdateVertex", v.Id())
	url := g.getVertexURL(v.Id())
	return g.Server.send("POST", url, v.Map)
}

func (g Graph) log(v ...interface{}) {
	if g.Server.Debug {
		vs := []interface{}{"GRAPH", g.Name}
		vs = append(vs, v...)
		log.Println(vs...)
	}
}

func (r Rexster) get(url string) (resp *Response, err error) {
	return r.send("GET", url, nil)
}

func (r Rexster) send(method string, url string, data map[string]interface{}) (resp *Response, err error) {
	var body io.Reader
	if data != nil {
		buf, err := json.Marshal(data)
		if err != nil {
			return nil, err
		}
		body = bytes.NewReader(buf)
	}
	req, err := http.NewRequest(method, url, body)
	if err != nil {
		return nil, err
	}
	if body != nil {
		req.Header.Add("Content-Type", "application/json")
	}

	hr, err := http.DefaultClient.Do(req)
	if err != nil {
		if r.Debug {
			log.Println("HTTP GET failed", url, err)
		}
		return nil, err
	}
	resp, errResp := readResponseOrError(hr)
	if errResp != nil {
		err = errors.New(strings.TrimSpace(strings.Join([]string{errResp.Message, errResp.Error}, " ")))
		if r.Debug {
			log.Println("HTTP GET failed", url, err)
		}
	}
	return
}

func readResponseOrError(hr *http.Response) (resp *Response, errResp *errorResponse) {
	dec := json.NewDecoder(hr.Body)
	defer hr.Body.Close()
	if hr.StatusCode == 200 {
		resp = new(Response)
		dec.Decode(resp)
	} else {
		errResp = new(errorResponse)
		dec.Decode(errResp)
	}
	return
}

// URLs

func (r Rexster) baseURL() *url.URL {
	return &url.URL{
		Scheme: "http",
		Host:   fmt.Sprintf("%s:%d", r.Host, r.RestPort),
	}
}

func (g Graph) baseURL() *url.URL {
	u := g.Server.baseURL()
	u.Path = "/graphs/" + g.Name
	return u
}

func (g Graph) getVertexURL(id string) string {
	u := g.baseURL()
	u.Path += "/vertices/"
	return u.String() + escapeSlashes(id)
}

func (g Graph) queryVerticesURL(key, value string) string {
	u := g.baseURL()
	u.Path += "/vertices"
	q := url.Values{"key": {key}, "value": {value}}
	u.RawQuery = q.Encode()
	return u.String()
}

func (g Graph) getVertexSubURL(id, subresource string) string {
	u := g.getVertexURL(id)
	return u + "/" + subresource
}

func (g Graph) queryEdgesURL(key, value string) string {
	u := g.baseURL()
	u.Path += "/edges"
	q := url.Values{"key": {key}, "value": {value}}
	u.RawQuery = q.Encode()
	return u.String()
}

func (g Graph) evalURL(script string) string {
	u := g.baseURL()
	u.Path += "/tp/gremlin"
	q := url.Values{"script": []string{script}}
	u.RawQuery = q.Encode()
	return u.String()
}

// Go's url.URL.String() improperly(?) redundantly escapes slashes in
// the path that are already percent-escaped.
func escapeSlashes(s string) string {
	return strings.Replace(s, "/", "%2F", -1)
}

// Data

type Vertex struct {
	Map map[string]interface{}
}

func NewVertex(id string, properties map[string]interface{}) (v *Vertex) {
	if properties == nil {
		properties = make(map[string]interface{}, 0)
	}
	v = &Vertex{properties}
	v.Map["_id"] = id
	return
}

// Vertex() gets the single vertex in the response. If the response
// does not contain a single vertex (i.e., if it contains multiple
// vertices, or a different data type), Vertex() returns nil.
func (r *Response) Vertex() (v *Vertex) {
	if v, ok := r.Results.(map[string]interface{}); ok && v["_type"] == "vertex" {
		return &Vertex{v}
	} else {
		return nil
	}
}

func (v Vertex) Id() string {
	return fmt.Sprintf("%v", v.Map["_id"])
}

func (v Vertex) Get(key string) string {
	if x, ok := v.Map[key]; ok {
		if s, ok := x.(string); ok {
			return s
		}
	}
	return ""
}

// Vertices() gets the array of vertices in the response. If the
// response does not contain an array of vertices (i.e., if it
// contains a single vertex not in an array, or a different data
// type), Vertices() returns nil.
func (r *Response) Vertices() (vs []*Vertex) {
	if vv, ok := r.Results.([]interface{}); ok {
		vs = make([]*Vertex, len(vv))
		for i, v := range vv {
			if v, ok := v.(map[string]interface{}); ok && v["_type"] == "vertex" {
				vs[i] = &Vertex{v}
			} else {
				return nil
			}
		}
	}
	return
}

type Edge struct {
	Map map[string]interface{}
}

// Edges() gets the array of edges in the response. If the
// response does not contain an array of edges (i.e., if it
// contains a single edge not in an array, or a different data
// type), Edges() returns nil.
func (r *Response) Edges() (es []*Edge) {
	if ee, ok := r.Results.([]interface{}); ok {
		es = make([]*Edge, len(ee))
		for i, e := range ee {
			if e, ok := e.(map[string]interface{}); ok && e["_type"] == "edge" {
				es[i] = &Edge{e}
			} else {
				return nil
			}
		}
	}
	return
}

func (e Edge) Get(key string) string {
	if x, ok := e.Map[key]; ok {
		if s, ok := x.(string); ok {
			return s
		}
	}
	return ""
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor: Julien Vehent jvehent@mozilla.com [:ulfr]

package main

import (
	"crypto/ecdsa"
	"crypto/rand"
	"crypto/x509"
	"encoding/base64"
	"fmt"
	"math/big"
)

type ecdsaSignature struct {
	R, S *big.Int // fields must be exported for ASN.1 marshalling
}

// A signer provides the configuration and key material to
// allow an authorized user to sign data with a private key
type signer struct {
	ID           string
	PrivateKey   string
	PublicKey    string
	X5U          string
	ecdsaPrivKey *ecdsa.PrivateKey
	siglen       int
}

func (s *signer) init() error {
	if s.ID == "" {
		return fmt.Errorf("missing signer ID in signer configuration")
	}
	if s.PrivateKey == "" {
		return fmt.Errorf("missing private key in signer configuration")
	}
	rawkey, err := base64.StdEncoding.DecodeString(s.PrivateKey)
	if err != nil {
		return err
	}
	s.ecdsaPrivKey, err = x509.ParseECPrivateKey(rawkey)
	if err != nil {
		return err
	}
	pubkeybytes, err := x509.MarshalPKIXPublicKey(s.ecdsaPrivKey.Public())
	if err != nil {
		return err
	}
	s.PublicKey = base64.StdEncoding.EncodeToString(pubkeybytes)
	// the signature length is double the size size of the curve field, in bytes
	// (each R and S value is equal to the size of the curve field)
	// If the curve field it not a multiple of 8, round to the upper multiple of 8
	if s.ecdsaPrivKey.Params().BitSize%8 != 0 {
		s.siglen = 8 - (s.ecdsaPrivKey.Params().BitSize % 8)
	}
	s.siglen += s.ecdsaPrivKey.Params().BitSize
	s.siglen /= 8
	s.siglen *= 2

	return nil
}

// sign takes input data and returns an ecdsa signature
func (s *signer) sign(data []byte) (sig *ecdsaSignature, err error) {
	sig = new(ecdsaSignature)
	sig.R, sig.S, err = ecdsa.Sign(rand.Reader, s.ecdsaPrivKey, data)
	if err != nil {
		return nil, fmt.Errorf("signing error: %v", err)
	}
	return
}

// ContentSignatureString returns a content-signature header string
func (s *signer) ContentSignature(ecdsaSig *ecdsaSignature) (string, error) {
	encodedsig, err := encode(ecdsaSig, s.siglen, "rs_base64url")
	if err != nil {
		return "", err
	}
	var csid string
	switch s.ecdsaPrivKey.Curve.Params().Name {
	case "P-256":
		csid = "p256ecdsa"
	case "P-384":
		csid = "p384ecdsa"
	default:
		return "", fmt.Errorf("unknown curve name %q", s.ecdsaPrivKey.Curve.Params().Name)
	}
	if s.X5U != "" {
		return fmt.Sprintf("x5u=%s; %s=%s", s.X5U, csid, encodedsig), nil
	}
	return fmt.Sprintf("keyid=%s; %s=%s", s.ID, csid, encodedsig), nil
}

// templateAndHash returns a hash of the signature input data. Templating is applied if necessary.
func templateAndHash(sigreq signaturerequest, curveName string) (string, []byte, error) {
	hashinput, err := fromBase64URL(sigreq.Input)
	if err != nil {
		return "", nil, err
	}
	if sigreq.Template != "" {
		hashinput, err = applyTemplate(hashinput, sigreq.Template)
		if err != nil {
			return "", nil, err
		}
	}
	// If we have a digest algorithm in the request, use it
	// otherwise try to infer it from the curve of the signer
	// and finally default to sha512
	if sigreq.HashWith != "" {
		hash, err := digest(hashinput, sigreq.HashWith)
		return sigreq.HashWith, hash, err
	}
	switch curveName {
	case "P-256":
		hash, err := digest(hashinput, "sha256")
		return "sha256", hash, err
	case "P-384":
		hash, err := digest(hashinput, "sha384")
		return "sha384", hash, err
	default:
		hash, err := digest(hashinput, "sha512")
		return "sha512", hash, err
	}
}

// applyTemplate returns a templated input using custom rules. This is used when requesting a
// Content-Signature without having to specify the header ahead of time.
func applyTemplate(input []byte, template string) (templated []byte, err error) {
	switch template {
	case "content-signature":
		templated = make([]byte, len("Content-Signature:\x00")+len(input))
		copy(templated[:len("Content-Signature:\x00")], []byte("Content-Signature:\x00"))
		copy(templated[len("Content-Signature:\x00"):], input)
	default:
		return nil, fmt.Errorf("unknown template %q", template)
	}
	return
}
<commit_msg>Remove space in content-signature header<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor: Julien Vehent jvehent@mozilla.com [:ulfr]

package main

import (
	"crypto/ecdsa"
	"crypto/rand"
	"crypto/x509"
	"encoding/base64"
	"fmt"
	"math/big"
)

type ecdsaSignature struct {
	R, S *big.Int // fields must be exported for ASN.1 marshalling
}

// A signer provides the configuration and key material to
// allow an authorized user to sign data with a private key
type signer struct {
	ID           string
	PrivateKey   string
	PublicKey    string
	X5U          string
	ecdsaPrivKey *ecdsa.PrivateKey
	siglen       int
}

func (s *signer) init() error {
	if s.ID == "" {
		return fmt.Errorf("missing signer ID in signer configuration")
	}
	if s.PrivateKey == "" {
		return fmt.Errorf("missing private key in signer configuration")
	}
	rawkey, err := base64.StdEncoding.DecodeString(s.PrivateKey)
	if err != nil {
		return err
	}
	s.ecdsaPrivKey, err = x509.ParseECPrivateKey(rawkey)
	if err != nil {
		return err
	}
	pubkeybytes, err := x509.MarshalPKIXPublicKey(s.ecdsaPrivKey.Public())
	if err != nil {
		return err
	}
	s.PublicKey = base64.StdEncoding.EncodeToString(pubkeybytes)
	// the signature length is double the size size of the curve field, in bytes
	// (each R and S value is equal to the size of the curve field)
	// If the curve field it not a multiple of 8, round to the upper multiple of 8
	if s.ecdsaPrivKey.Params().BitSize%8 != 0 {
		s.siglen = 8 - (s.ecdsaPrivKey.Params().BitSize % 8)
	}
	s.siglen += s.ecdsaPrivKey.Params().BitSize
	s.siglen /= 8
	s.siglen *= 2

	return nil
}

// sign takes input data and returns an ecdsa signature
func (s *signer) sign(data []byte) (sig *ecdsaSignature, err error) {
	sig = new(ecdsaSignature)
	sig.R, sig.S, err = ecdsa.Sign(rand.Reader, s.ecdsaPrivKey, data)
	if err != nil {
		return nil, fmt.Errorf("signing error: %v", err)
	}
	return
}

// ContentSignatureString returns a content-signature header string
func (s *signer) ContentSignature(ecdsaSig *ecdsaSignature) (string, error) {
	encodedsig, err := encode(ecdsaSig, s.siglen, "rs_base64url")
	if err != nil {
		return "", err
	}
	var csid string
	switch s.ecdsaPrivKey.Curve.Params().Name {
	case "P-256":
		csid = "p256ecdsa"
	case "P-384":
		csid = "p384ecdsa"
	default:
		return "", fmt.Errorf("unknown curve name %q", s.ecdsaPrivKey.Curve.Params().Name)
	}
	if s.X5U != "" {
		return fmt.Sprintf("x5u=%s;%s=%s", s.X5U, csid, encodedsig), nil
	}
	return fmt.Sprintf("keyid=%s;%s=%s", s.ID, csid, encodedsig), nil
}

// templateAndHash returns a hash of the signature input data. Templating is applied if necessary.
func templateAndHash(sigreq signaturerequest, curveName string) (string, []byte, error) {
	hashinput, err := fromBase64URL(sigreq.Input)
	if err != nil {
		return "", nil, err
	}
	if sigreq.Template != "" {
		hashinput, err = applyTemplate(hashinput, sigreq.Template)
		if err != nil {
			return "", nil, err
		}
	}
	// If we have a digest algorithm in the request, use it
	// otherwise try to infer it from the curve of the signer
	// and finally default to sha512
	if sigreq.HashWith != "" {
		hash, err := digest(hashinput, sigreq.HashWith)
		return sigreq.HashWith, hash, err
	}
	switch curveName {
	case "P-256":
		hash, err := digest(hashinput, "sha256")
		return "sha256", hash, err
	case "P-384":
		hash, err := digest(hashinput, "sha384")
		return "sha384", hash, err
	default:
		hash, err := digest(hashinput, "sha512")
		return "sha512", hash, err
	}
}

// applyTemplate returns a templated input using custom rules. This is used when requesting a
// Content-Signature without having to specify the header ahead of time.
func applyTemplate(input []byte, template string) (templated []byte, err error) {
	switch template {
	case "content-signature":
		templated = make([]byte, len("Content-Signature:\x00")+len(input))
		copy(templated[:len("Content-Signature:\x00")], []byte("Content-Signature:\x00"))
		copy(templated[len("Content-Signature:\x00"):], input)
	default:
		return nil, fmt.Errorf("unknown template %q", template)
	}
	return
}
<|endoftext|>


<commit_before>package main

import "github.com/gin-gonic/gin"

type simplePage struct {
	Handler, Template, TitleBar, KyutGrill string
}

var simplePages = [...]simplePage{
	{"/", "homepage.html", "Home Page", "homepage.jpg"},
	{"/login", "login.html", "Log in", "login.png"},
	{"/settings/avatar", "settings/avatar.html", "Change avatar", "settings.png"},
	{"/dev/tokens", "dev/tokens.html", "Your API tokens", "dev.png"},
	{"/beatmaps/rank_request", "beatmaps/rank_request.html", "Request beatmap ranking", "request_beatmap_ranking.jpg"},
	{"/donate", "support.html", "Support Ripple", "donate.jpg"},
	{"/doc", "doc.html", "Documentation", "documentation.jpg"},
	{"/doc/:id", "doc_content.html", "View document", "documentation.jpg"},
	{"/help", "help.html", "Contact support", "help.jpg"},
	{"/leaderboard", "leaderboard.html", "Leaderboard", "leaderboard.jpg"},
	{"/friends", "friends.html", "Friends", ""},
}

// indexes of pages in simplePages that have huge heading on the right
var hugeHeadingRight = [...]int{
	3,
}

var additionalJS = map[string][]string{
	"/settings/avatar": []string{"/static/croppie.min.js"},
}

func loadSimplePages(r *gin.Engine) {
	for i, el := range simplePages {
		// if the page has hugeheading on the right, tell it to the
		// simplePageFunc.
		var right bool
		for _, hhr := range hugeHeadingRight {
			if hhr == i {
				right = true
			}
		}
		r.GET(el.Handler, simplePageFunc(el, right))
	}
}

func simplePageFunc(p simplePage, hhr bool) gin.HandlerFunc {
	return func(c *gin.Context) {
		resp(c, 200, p.Template, &baseTemplateData{
			TitleBar:       p.TitleBar,
			KyutGrill:      p.KyutGrill,
			Scripts:        additionalJS[p.Handler],
			HeadingOnRight: hhr,
		})
	}
}
<commit_msg>Add minimum privileges in simplepage<commit_after>package main

import (
	"git.zxq.co/ripple/rippleapi/common"
	"github.com/gin-gonic/gin"
)

type simplePage struct {
	Handler, Template, TitleBar, KyutGrill string
	MinPrivileges                          common.UserPrivileges
}

var simplePages = [...]simplePage{
	{"/", "homepage.html", "Home Page", "homepage.jpg", 0},
	{"/login", "login.html", "Log in", "login.png", 0},
	{"/settings/avatar", "settings/avatar.html", "Change avatar", "settings.png", 2},
	{"/dev/tokens", "dev/tokens.html", "Your API tokens", "dev.png", 2},
	{"/beatmaps/rank_request", "beatmaps/rank_request.html", "Request beatmap ranking", "request_beatmap_ranking.jpg", 2},
	{"/donate", "support.html", "Support Ripple", "donate.jpg", 0},
	{"/doc", "doc.html", "Documentation", "documentation.jpg", 0},
	{"/doc/:id", "doc_content.html", "View document", "documentation.jpg", 0},
	{"/help", "help.html", "Contact support", "help.jpg", 0},
	{"/leaderboard", "leaderboard.html", "Leaderboard", "leaderboard.jpg", 0},
	{"/friends", "friends.html", "Friends", "", 2},
}

// indexes of pages in simplePages that have huge heading on the right
var hugeHeadingRight = [...]int{
	3,
}

var additionalJS = map[string][]string{
	"/settings/avatar": []string{"/static/croppie.min.js"},
}

func loadSimplePages(r *gin.Engine) {
	for i, el := range simplePages {
		// if the page has hugeheading on the right, tell it to the
		// simplePageFunc.
		var right bool
		for _, hhr := range hugeHeadingRight {
			if hhr == i {
				right = true
			}
		}
		r.GET(el.Handler, simplePageFunc(el, right))
	}
}

func simplePageFunc(p simplePage, hhr bool) gin.HandlerFunc {
	return func(c *gin.Context) {
		s := c.MustGet("context").(context)
		if s.User.Privileges&p.MinPrivileges != p.MinPrivileges {
			resp(c, 200, "empty.html", &baseTemplateData{TitleBar: "Forbidden", Messages: []message{warningMessage{"You should not be 'round here."}}})
			return
		}
		resp(c, 200, p.Template, &baseTemplateData{
			TitleBar:       p.TitleBar,
			KyutGrill:      p.KyutGrill,
			Scripts:        additionalJS[p.Handler],
			HeadingOnRight: hhr,
		})
	}
}
<|endoftext|>


<commit_before>package ws

import (
	"fmt"
	"io/ioutil"
	"net"

	"github.com/gorilla/websocket"
	"v2ray.com/core/common/log"
	v2net "v2ray.com/core/common/net"
	"v2ray.com/core/transport/internet"
	v2tls "v2ray.com/core/transport/internet/tls"
)

var (
	globalCache = NewConnectionCache()
)

func Dial(src v2net.Address, dest v2net.Destination, options internet.DialerOptions) (internet.Connection, error) {
	log.Info("WebSocket|Dailer: Creating connection to ", dest)
	if src == nil {
		src = v2net.AnyIP
	}
	networkSettings, err := options.Stream.GetEffectiveNetworkSettings()
	if err != nil {
		return nil, err
	}
	wsSettings := networkSettings.(*Config)

	id := src.String() + "-" + dest.NetAddr()
	var conn *wsconn
	if dest.Network == v2net.Network_TCP && wsSettings.ConnectionReuse.IsEnabled() {
		connt := globalCache.Get(id)
		if connt != nil {
			conn = connt.(*wsconn)
		}
	}
	if conn == nil {
		var err error
		conn, err = wsDial(src, dest, options)
		if err != nil {
			log.Warning("WebSocket|Dialer: Dial failed: ", err)
			return nil, err
		}
	}
	return NewConnection(id, conn, globalCache, wsSettings), nil
}

func init() {
	internet.WSDialer = Dial
}

func wsDial(src v2net.Address, dest v2net.Destination, options internet.DialerOptions) (*wsconn, error) {
	networkSettings, err := options.Stream.GetEffectiveNetworkSettings()
	if err != nil {
		return nil, err
	}
	wsSettings := networkSettings.(*Config)

	commonDial := func(network, addr string) (net.Conn, error) {
		return internet.DialToDest(src, dest)
	}

	dialer := websocket.Dialer{
		NetDial:         commonDial,
		ReadBufferSize:  65536,
		WriteBufferSize: 65536,
	}

	protocol := "ws"

	if options.Stream != nil && options.Stream.HasSecuritySettings() {
		protocol = "wss"
		securitySettings, err := options.Stream.GetEffectiveSecuritySettings()
		if err != nil {
			log.Error("WebSocket: Failed to create security settings: ", err)
			return nil, err
		}
		tlsConfig, ok := securitySettings.(*v2tls.Config)
		if ok {
			dialer.TLSClientConfig = tlsConfig.GetTLSConfig()
			if dest.Address.Family().IsDomain() {
				dialer.TLSClientConfig.ServerName = dest.Address.Domain()
			}
		}
	}

	uri := func(dst v2net.Destination, pto string, path string) string {
		return fmt.Sprintf("%v://%v/%v", pto, dst.NetAddr(), path)
	}(dest, protocol, wsSettings.Path)

	conn, resp, err := dialer.Dial(uri, nil)
	if err != nil {
		if resp != nil {
			reason, reasonerr := ioutil.ReadAll(resp.Body)
			log.Info(string(reason), reasonerr)
		}
		return nil, err
	}
	return func() internet.Connection {
		connv2ray := &wsconn{
			wsc:         conn,
			connClosing: false,
			config:      wsSettings,
		}
		connv2ray.setup()
		return connv2ray
	}().(*wsconn), nil
}
<commit_msg>fix build break<commit_after>package ws

import (
	"io/ioutil"
	"net"

	"github.com/gorilla/websocket"
	"v2ray.com/core/common/log"
	v2net "v2ray.com/core/common/net"
	"v2ray.com/core/transport/internet"
	v2tls "v2ray.com/core/transport/internet/tls"
)

var (
	globalCache = NewConnectionCache()
)

func Dial(src v2net.Address, dest v2net.Destination, options internet.DialerOptions) (internet.Connection, error) {
	log.Info("WebSocket|Dailer: Creating connection to ", dest)
	if src == nil {
		src = v2net.AnyIP
	}
	networkSettings, err := options.Stream.GetEffectiveNetworkSettings()
	if err != nil {
		return nil, err
	}
	wsSettings := networkSettings.(*Config)

	id := src.String() + "-" + dest.NetAddr()
	var conn *wsconn
	if dest.Network == v2net.Network_TCP && wsSettings.ConnectionReuse.IsEnabled() {
		connt := globalCache.Get(id)
		if connt != nil {
			conn = connt.(*wsconn)
		}
	}
	if conn == nil {
		var err error
		conn, err = wsDial(src, dest, options)
		if err != nil {
			log.Warning("WebSocket|Dialer: Dial failed: ", err)
			return nil, err
		}
	}
	return NewConnection(id, conn, globalCache, wsSettings), nil
}

func init() {
	internet.WSDialer = Dial
}

func wsDial(src v2net.Address, dest v2net.Destination, options internet.DialerOptions) (*wsconn, error) {
	networkSettings, err := options.Stream.GetEffectiveNetworkSettings()
	if err != nil {
		return nil, err
	}
	wsSettings := networkSettings.(*Config)

	commonDial := func(network, addr string) (net.Conn, error) {
		return internet.DialToDest(src, dest)
	}

	dialer := websocket.Dialer{
		NetDial:         commonDial,
		ReadBufferSize:  65536,
		WriteBufferSize: 65536,
	}

	protocol := "ws"

	if options.Stream != nil && options.Stream.HasSecuritySettings() {
		protocol = "wss"
		securitySettings, err := options.Stream.GetEffectiveSecuritySettings()
		if err != nil {
			log.Error("WebSocket: Failed to create security settings: ", err)
			return nil, err
		}
		tlsConfig, ok := securitySettings.(*v2tls.Config)
		if ok {
			dialer.TLSClientConfig = tlsConfig.GetTLSConfig()
			if dest.Address.Family().IsDomain() {
				dialer.TLSClientConfig.ServerName = dest.Address.Domain()
			}
		}
	}

	uri := protocol + "://" + dest.NetAddr() + "/" + wsSettings.Path

	conn, resp, err := dialer.Dial(uri, nil)
	if err != nil {
		if resp != nil {
			reason, reasonerr := ioutil.ReadAll(resp.Body)
			log.Info(string(reason), reasonerr)
		}
		return nil, err
	}
	return func() internet.Connection {
		connv2ray := &wsconn{
			wsc:         conn,
			connClosing: false,
			config:      wsSettings,
		}
		connv2ray.setup()
		return connv2ray
	}().(*wsconn), nil
}
<|endoftext|>


<commit_before>package robotgo

/*
//#if defined(IS_MACOSX)
	#cgo darwin CFLAGS: -x objective-c  -Wno-deprecated-declarations
	#cgo darwin LDFLAGS: -framework Cocoa -framework OpenGL -framework IOKit -framework Carbon -framework CoreFoundation
//#elif defined(USE_X11)
	#cgo linux CFLAGS:-I/usr/src
	#cgo linux LDFLAGS:-L/usr/src -lpng -lz -lX11 -lXtst -lm
//#endif
	#cgo windows LDFLAGS: -lgdi32 -luser32
//#include <AppKit/NSEvent.h>
#include "screen/goScreen.h"
#include "mouse/goMouse.h"
#include "key/goKey.h"
*/
import "C"

import (
	. "fmt"
	// "runtime"
	// "syscall"
)

/*
 __  __
|  \/  | ___  _   _ ___  ___
| |\/| |/ _ \| | | / __|/ _ \
| |  | | (_) | |_| \__ \  __/
|_|  |_|\___/ \__,_|___/\___|

*/

type MPoint struct {
	x int
	y int
}

//C.size_t  int
func MoveMouse(x, y C.int) {
	C.amoveMouse(x, y)
}

func DragMouse(x, y C.int) {
	C.adragMouse(x, y)
}

func MoveMouseSmooth(x, y C.int) {
	C.amoveMouseSmooth(x, y)
}

func GetMousePos() (C.size_t, C.size_t) {
	pos := C.agetMousePos()
	// Println("pos:###", pos, pos.x, pos.y)
	return pos.x, pos.y
}

func MouseClick() {
	C.amouseClick()
}

func MouseToggle() {
	C.amouseToggle()
}

func SetMouseDelay(x C.int) {
	C.asetMouseDelay(x)
}

func ScrollMouse(x C.int, y string) {
	z := C.CString(y)
	C.ascrollMouse(x, z)
}

/*
 _  __          _                         _
| |/ /___ _   _| |__   ___   __ _ _ __ __| |
| ' // _ \ | | | '_ \ / _ \ / _` | '__/ _` |
| . \  __/ |_| | |_) | (_) | (_| | | | (_| |
|_|\_\___|\__, |_.__/ \___/ \__,_|_|  \__,_|
		  |___/
*/
func Try(fun func(), handler func(interface{})) {
	defer func() {
		if err := recover(); err != nil {
			handler(err)
		}
	}()
	fun()
}

func KeyTap(args ...string) {
	var apara string
	Try(func() {
		apara = args[1]
	}, func(e interface{}) {
		// Println("err:::", e)
		apara = "null"
	})

	zkey := C.CString(args[0])
	amod := C.CString(apara)
	defer func() {
		C.akeyTap(zkey, amod)
	}()
}

func KeyToggle(args ...string) {
	var apara string
	Try(func() {
		apara = args[1]
	}, func(e interface{}) {
		// Println("err:::", e)
		apara = "null"
	})

	zkey := C.CString(args[0])
	amod := C.CString(apara)
	defer func() {
		str := C.akeyToggle(zkey, amod)
		Println(str)
	}()
}

func TypeString(x string) {
	cx := C.CString(x)
	C.atypeString(cx)
}

func TypeStringDelayed(x string, y C.size_t) {
	cx := C.CString(x)
	C.atypeStringDelayed(cx, y)
}

func SetKeyboardDelay(x C.size_t) {
	C.asetKeyboardDelay(x)
}

/*
  ____
 / ___|  ___ _ __ ___  ___ _ __
 \___ \ / __| '__/ _ \/ _ \ '_ \
  ___) | (__| | |  __/  __/ | | |
 |____/ \___|_|  \___|\___|_| |_|

*/

func GetPixelColor(x, y C.size_t) string {
	color := C.agetPixelColor(x, y)
	gcolor := C.GoString(color)
	return gcolor
}

func GetScreenSize() (C.size_t, C.size_t) {
	size := C.agetScreenSize()
	// Println("...", size, size.width)
	return size.width, size.height
}

func GetXDisplayName() string {
	name := C.agetXDisplayName()
	gname := C.GoString(name)
	return gname
}

func SetXDisplayName(name string) string {
	cname := C.CString(name)
	str := C.asetXDisplayName(cname)
	gstr := C.GoString(str)
	return gstr
}

func CaptureScreen(x, y, w, h C.int) {
	bit := C.acaptureScreen(x, y, w, h)
	Println("...", bit)
}
<commit_msg>Optimized memory recovery<commit_after>package robotgo

/*
//#if defined(IS_MACOSX)
	#cgo darwin CFLAGS: -x objective-c  -Wno-deprecated-declarations
	#cgo darwin LDFLAGS: -framework Cocoa -framework OpenGL -framework IOKit -framework Carbon -framework CoreFoundation
//#elif defined(USE_X11)
	#cgo linux CFLAGS:-I/usr/src
	#cgo linux LDFLAGS:-L/usr/src -lpng -lz -lX11 -lXtst -lm
//#endif
	#cgo windows LDFLAGS: -lgdi32 -luser32
//#include <AppKit/NSEvent.h>
#include "screen/goScreen.h"
#include "mouse/goMouse.h"
#include "key/goKey.h"
*/
import "C"

import (
	. "fmt"
	"unsafe"
	// "runtime"
	// "syscall"
)

/*
 __  __
|  \/  | ___  _   _ ___  ___
| |\/| |/ _ \| | | / __|/ _ \
| |  | | (_) | |_| \__ \  __/
|_|  |_|\___/ \__,_|___/\___|

*/

type MPoint struct {
	x int
	y int
}

//C.size_t  int
func MoveMouse(x, y C.int) {
	C.amoveMouse(x, y)
}

func DragMouse(x, y C.int) {
	C.adragMouse(x, y)
}

func MoveMouseSmooth(x, y C.int) {
	C.amoveMouseSmooth(x, y)
}

func GetMousePos() (C.size_t, C.size_t) {
	pos := C.agetMousePos()
	// Println("pos:###", pos, pos.x, pos.y)
	return pos.x, pos.y
}

func MouseClick() {
	C.amouseClick()
}

func MouseToggle() {
	C.amouseToggle()
}

func SetMouseDelay(x C.int) {
	C.asetMouseDelay(x)
}

func ScrollMouse(x C.int, y string) {
	z := C.CString(y)
	C.ascrollMouse(x, z)
	defer C.free(unsafe.Pointer(z))
}

/*
 _  __          _                         _
| |/ /___ _   _| |__   ___   __ _ _ __ __| |
| ' // _ \ | | | '_ \ / _ \ / _` | '__/ _` |
| . \  __/ |_| | |_) | (_) | (_| | | | (_| |
|_|\_\___|\__, |_.__/ \___/ \__,_|_|  \__,_|
		  |___/
*/
func Try(fun func(), handler func(interface{})) {
	defer func() {
		if err := recover(); err != nil {
			handler(err)
		}
	}()
	fun()
}

func KeyTap(args ...string) {
	var apara string
	Try(func() {
		apara = args[1]
	}, func(e interface{}) {
		// Println("err:::", e)
		apara = "null"
	})

	zkey := C.CString(args[0])
	amod := C.CString(apara)
	// defer func() {
	C.akeyTap(zkey, amod)
	// }()

	defer C.free(unsafe.Pointer(zkey))
	defer C.free(unsafe.Pointer(amod))
}

func KeyToggle(args ...string) {
	var apara string
	Try(func() {
		apara = args[1]
	}, func(e interface{}) {
		// Println("err:::", e)
		apara = "null"
	})

	zkey := C.CString(args[0])
	amod := C.CString(apara)
	// defer func() {
	str := C.akeyToggle(zkey, amod)
	Println(str)
	// }()
	defer C.free(unsafe.Pointer(zkey))
	defer C.free(unsafe.Pointer(amod))
}

func TypeString(x string) {
	cx := C.CString(x)
	C.atypeString(cx)
	defer C.free(unsafe.Pointer(cx))
}

func TypeStringDelayed(x string, y C.size_t) {
	cx := C.CString(x)
	C.atypeStringDelayed(cx, y)
	defer C.free(unsafe.Pointer(cx))
}

func SetKeyboardDelay(x C.size_t) {
	C.asetKeyboardDelay(x)
}

/*
  ____
 / ___|  ___ _ __ ___  ___ _ __
 \___ \ / __| '__/ _ \/ _ \ '_ \
  ___) | (__| | |  __/  __/ | | |
 |____/ \___|_|  \___|\___|_| |_|

*/

func GetPixelColor(x, y C.size_t) string {
	color := C.agetPixelColor(x, y)
	gcolor := C.GoString(color)
	defer C.free(unsafe.Pointer(color))
	return gcolor
}

func GetScreenSize() (C.size_t, C.size_t) {
	size := C.agetScreenSize()
	// Println("...", size, size.width)
	return size.width, size.height
}

func GetXDisplayName() string {
	name := C.agetXDisplayName()
	gname := C.GoString(name)
	defer C.free(unsafe.Pointer(name))
	return gname
}

func SetXDisplayName(name string) string {
	cname := C.CString(name)
	str := C.asetXDisplayName(cname)
	gstr := C.GoString(str)
	return gstr
}

func CaptureScreen(x, y, w, h C.int) {
	bit := C.acaptureScreen(x, y, w, h)
	Println("...", bit)
}
<|endoftext|>


<commit_before>package filter

import (
	"reflect"
	"testing"

	"github.com/golang/protobuf/proto"
	pb "github.com/google/cloudprober/targets/rds/proto"
)

func TestParseFilters(t *testing.T) {
	tests := []struct {
		desc                string
		reqFilters          map[string]string
		regexFilterKeys     []string
		freshnessFilterKey  string
		wantReFilters       []string
		wantLabelsFilter    bool
		wantFreshnessFilter bool
		wantErr             bool
	}{
		{
			desc:            "Error invalid filter key",
			reqFilters:      map[string]string{"random_key": "random_value"},
			regexFilterKeys: []string{"name"},
			wantErr:         true,
		},
		{
			desc: "Pass with 2 regex filters, labels filter and a freshness filter",
			reqFilters: map[string]string{
				"name":           "x.*",
				"namespace":      "y",
				"labels.app":     "cloudprober",
				"updated_within": "5m",
			},
			regexFilterKeys:     []string{"name", "namespace"},
			freshnessFilterKey:  "updated_within",
			wantReFilters:       []string{"name", "namespace"},
			wantLabelsFilter:    true,
			wantFreshnessFilter: true,
		},
	}

	for _, test := range tests {
		t.Run(test.desc, func(t *testing.T) {
			var reqFiltersPB []*pb.Filter
			for k, v := range test.reqFilters {
				reqFiltersPB = append(reqFiltersPB, &pb.Filter{Key: proto.String(k), Value: proto.String(v)})
			}

			allFilters, err := ParseFilters(reqFiltersPB, test.regexFilterKeys, test.freshnessFilterKey)

			if err != nil {
				if !test.wantErr {
					t.Errorf("Got unexpected error while parsing filters: %v", err)
				}
				return
			}

			var reFilterKeys []string
			for k := range allFilters.RegexFilters {
				reFilterKeys = append(reFilterKeys, k)
			}
			if !reflect.DeepEqual(reFilterKeys, test.wantReFilters) {
				t.Errorf("regex filters, got=%v, want=%v", reFilterKeys, test.wantReFilters)
			}

			if (allFilters.LabelsFilter != nil) != test.wantLabelsFilter {
				t.Errorf("labels filters, got=%v, wantLabelsFilter=%v", allFilters.LabelsFilter, test.wantLabelsFilter)
			}

			if (allFilters.FreshnessFilter != nil) != test.wantFreshnessFilter {
				t.Errorf("freshness filter filters, got=%v, wantFreshnessFilter=%v", allFilters.FreshnessFilter, test.wantFreshnessFilter)
			}
		})
	}

}
<commit_msg>Sort slices before comparing them.<commit_after>package filter

import (
	"reflect"
	"sort"
	"testing"

	"github.com/golang/protobuf/proto"
	pb "github.com/google/cloudprober/targets/rds/proto"
)

func TestParseFilters(t *testing.T) {
	tests := []struct {
		desc                string
		reqFilters          map[string]string
		regexFilterKeys     []string
		freshnessFilterKey  string
		wantReFilters       []string
		wantLabelsFilter    bool
		wantFreshnessFilter bool
		wantErr             bool
	}{
		{
			desc:            "Error invalid filter key",
			reqFilters:      map[string]string{"random_key": "random_value"},
			regexFilterKeys: []string{"name"},
			wantErr:         true,
		},
		{
			desc: "Pass with 2 regex filters, labels filter and a freshness filter",
			reqFilters: map[string]string{
				"name":           "x.*",
				"namespace":      "y",
				"labels.app":     "cloudprober",
				"updated_within": "5m",
			},
			regexFilterKeys:     []string{"name", "namespace"},
			freshnessFilterKey:  "updated_within",
			wantReFilters:       []string{"name", "namespace"},
			wantLabelsFilter:    true,
			wantFreshnessFilter: true,
		},
	}

	for _, test := range tests {
		t.Run(test.desc, func(t *testing.T) {
			var reqFiltersPB []*pb.Filter
			for k, v := range test.reqFilters {
				reqFiltersPB = append(reqFiltersPB, &pb.Filter{Key: proto.String(k), Value: proto.String(v)})
			}

			allFilters, err := ParseFilters(reqFiltersPB, test.regexFilterKeys, test.freshnessFilterKey)

			if err != nil {
				if !test.wantErr {
					t.Errorf("Got unexpected error while parsing filters: %v", err)
				}
				return
			}

			var reFilterKeys []string
			for k := range allFilters.RegexFilters {
				reFilterKeys = append(reFilterKeys, k)
			}

			sort.Strings(reFilterKeys)
			sort.Strings(test.wantReFilters)
			if !reflect.DeepEqual(reFilterKeys, test.wantReFilters) {
				t.Errorf("regex filters, got=%v, want=%v", reFilterKeys, test.wantReFilters)
			}

			if (allFilters.LabelsFilter != nil) != test.wantLabelsFilter {
				t.Errorf("labels filters, got=%v, wantLabelsFilter=%v", allFilters.LabelsFilter, test.wantLabelsFilter)
			}

			if (allFilters.FreshnessFilter != nil) != test.wantFreshnessFilter {
				t.Errorf("freshness filter filters, got=%v, wantFreshnessFilter=%v", allFilters.FreshnessFilter, test.wantFreshnessFilter)
			}
		})
	}

}
<|endoftext|>


<commit_before>/*
	Example of blending modes using blend Go library.
	https://github.com/phrozen/blend
	by Guillermo Estrada
*/

package main

import (
	"fmt"
	"github.com/phrozen/blend"
	"image"
	"image/png"
	"os"
)

var modes1 = map[string]blend.BlendFunc{
	"multiply":     blend.MULTIPLY,
	"screen":       blend.SCREEN,
	"overlay":      blend.OVERLAY,
	"soft_light":   blend.SOFT_LIGHT,
	"hard_light":   blend.HARD_LIGHT,
	"color_dodge":  blend.COLOR_DODGE,
	"color_burn":   blend.COLOR_BURN,
	"linear_dodge": blend.LINEAR_DODGE,
	"linear_burn":  blend.LINEAR_BURN,
	"darken":       blend.DARKEN,
	"lighten":      blend.LIGHTEN,
	"difference":   blend.DIFFERENCE,
	"exclusion":    blend.EXCLUSION,
	"reflex":       blend.REFLEX,
}

var modes2 = map[string]blend.BlendFunc{
	"linear_light": blend.LINEAR_LIGHT,
	"pin_light":    blend.PIN_LIGHT,
	"vivid_light":  blend.VIVID_LIGHT,
	"hard_mix":     blend.HARD_MIX,
}

func LoadPNG(filename string) (image.Image, error) {
	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}

	img, err := png.Decode(file)
	if err != nil {
		return nil, err
	}

	return img, nil
}

func SavePNG(filename string, img image.Image) error {
	file, err := os.Create(filename + ".png")
	if err != nil {
		return err
	}

	if err := png.Encode(file, img); err != nil {
		return err
	}

	err = file.Close()
	if err != nil {
		return err
	}

	return nil
}

func main() {
	var err error
	var img image.Image

	dst, err := LoadPNG("dest.png")
	if err != nil {
		panic(err)
	}

	src1, err := LoadPNG("source1.png")
	if err != nil {
		panic(err)
	}

	src2, err := LoadPNG("source2.png")
	if err != nil {
		panic(err)
	}

	fmt.Println("This program tests all the color blending modes in the library.")

	// Testing Blending Modes with source1.png
	for key, value := range modes1 {
		fmt.Print("*Blending Mode: ", key, " ...")
		img, err = blend.BlendImage(src1, dst, value)
		if err != nil {
			panic(err)
		}
		err = SavePNG(key, img)
		if err != nil {
			panic(err)
		}
		fmt.Println("Saved! ", key, ".png")
	}

	// Testing Blending Modes with source2.png
	for key, value := range modes2 {
		fmt.Print("*Blending Mode: ", key, " ...")
		img, err = blend.BlendImage(src2, dst, value)
		if err != nil {
			panic(err)
		}
		err = SavePNG(key, img)
		if err != nil {
			panic(err)
		}
		fmt.Println("Saved! ", key, ".png")
	}

}
<commit_msg>Simplified example with new interface<commit_after>/*
	Example of blending modes using blend Go library.
	https://github.com/phrozen/blend
	by Guillermo Estrada
*/

package main

import (
	"fmt"
	"github.com/phrozen/blend"
	"image"
	"image/jpeg"
	"os"
)

var modes = map[string]blend.BlendFunc{
	"multiply":     blend.MULTIPLY,
	"screen":       blend.SCREEN,
	"overlay":      blend.OVERLAY,
	"soft_light":   blend.SOFT_LIGHT,
	"hard_light":   blend.HARD_LIGHT,
	"color_dodge":  blend.COLOR_DODGE,
	"color_burn":   blend.COLOR_BURN,
	"linear_dodge": blend.LINEAR_DODGE,
	"linear_burn":  blend.LINEAR_BURN,
	"darken":       blend.DARKEN,
	"lighten":      blend.LIGHTEN,
	"difference":   blend.DIFFERENCE,
	"exclusion":    blend.EXCLUSION,
	"reflex":       blend.REFLEX,
	"linear_light": blend.LINEAR_LIGHT,
	"pin_light":    blend.PIN_LIGHT,
	"vivid_light":  blend.VIVID_LIGHT,
	"hard_mix":     blend.HARD_MIX,
}

func LoadJPG(filename string) (image.Image, error) {
	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}

	img, err := jpeg.Decode(file)
	if err != nil {
		return nil, err
	}

	return img, nil
}

func SaveJPG(filename string, img image.Image) error {
	file, err := os.Create(filename + ".jpg")
	if err != nil {
		return err
	}

	if err := jpeg.Encode(file, img, &jpeg.Options{85}); err != nil {
		return err
	}

	err = file.Close()
	if err != nil {
		return err
	}

	return nil
}

func main() {
	var err error
	var img image.Image

	dst, err := LoadJPG("dest.jpg")
	if err != nil {
		panic(err)
	}

	src, err := LoadJPG("source.jpg")
	if err != nil {
		panic(err)
	}

	fmt.Println("This program tests all the color blending modes in the library.")

	// Testing Blending Modes with source1.png
	for key, value := range modes {
		fmt.Print("*Blending Mode: ", key, " ...  \t")
		img, err = blend.BlendImage(src, dst, value)
		if err != nil {
			panic(err)
		}
		err = SaveJPG(key, img)
		if err != nil {
			panic(err)
		}
		fmt.Println("Saved! ", key, ".jpg")
	}

}
<|endoftext|>


<commit_before>package main

import (
	"encoding/xml"
	"errors"
	"io"
	"log"
	"os"
	"regexp"
	"strings"
)

var (
	Debug = false

	ParseComplete = errors.New("parsing complete")
	ParseStopped  = errors.New("parsing stopped")
	ReadComplete  = io.EOF

	AnyName = xml.Name{Local: "*"}
	AnyAttr = xml.Attr{Name: AnyName, Value: "*"}
	NoAttr  = xml.Attr{}

	// ugly regexp matching the basic XPath predicate expression [ @name = 'value' ]
	pAttribute = regexp.MustCompile(`^\\[\\s*@(.*)\\s*(=)\\s*["']?(.*?)["']?\\s*\\]`)
)

func dlog(args ...interface{}) {
	if Debug {
		log.Println(args...)
	}
}

////////////////////////////////////////

//SAX-like handler
type SAXHandler interface {
	//called when XML document start
	StartDocument() bool
	//called when XML document end
	EndDocument() bool
	//called when XML tag start
	StartElement(xml.StartElement) bool
	//called when XML tag end
	EndElement(xml.EndElement) bool
	//called when the parser encount chardata
	CharData(xml.CharData) bool
	//called when the parser encount comment
	Comment(xml.Comment) bool
	//called when the parser encount procInst
	//<!procinst >
	ProcInst(xml.ProcInst) bool
	//called when the parser encount directive
	//
	Directive(xml.Directive) bool
}

////////////////////////////////////////

//SAX-like XML Parser
type SAXParser struct {
	*xml.Decoder
	handler SAXHandler
	started bool
	ended   bool
}

//Create a New SAXParser
func NewSAXParser(reader io.Reader, handler SAXHandler) *SAXParser {
	decoder := xml.NewDecoder(reader)
	return &SAXParser{Decoder: decoder, handler: handler}
}

//SetHTMLMode make Parser can parse invalid HTML
func (p *SAXParser) SetHTMLMode() {
	p.Strict = false
	p.AutoClose = xml.HTMLAutoClose
	p.Entity = xml.HTMLEntity
}

//Parse calls handler's methods
//when the parser encount a start-element,a end-element, a comment and so on.
//
//The parsing process stops if the handler methods return "true" and can be restarted
//by calling Parse again until it returns ParseComplete or ReadComplete
func (p *SAXParser) Parse() (xml.Token, error) {
	if p.ended {
		return nil, ParseComplete
	}

	if !p.started {
		p.started = true
		p.handler.StartDocument()
	}

	stop := false

	for {
		token, err := p.Token()
		if err == io.EOF {
			return nil, ReadComplete
		}
		if err != nil {
			return nil, err
		}

		switch t := token.(type) {
		case xml.StartElement:
			stop = p.handler.StartElement(t)
		case xml.EndElement:
			stop = p.handler.EndElement(t)
		case xml.CharData:
			stop = p.handler.CharData(t)
		case xml.Comment:
			stop = p.handler.Comment(t)
		case xml.ProcInst:
			stop = p.handler.ProcInst(t)
		case xml.Directive:
			stop = p.handler.Directive(t)
		default:
			panic("unknown xml token")
		}

		if stop {
			return token, ParseStopped
		}

	}

	p.ended = true
	p.handler.EndDocument()
	return nil, ParseComplete
}

////////////////////////////////////////

type Matches int

const (
	MATCH_NO Matches = iota
	MATCH_PART
	MATCH_PATTERN
	MATCH_DESCENDANT
	MATCH_PARTIAL
	MATCH_TEXT
	MATCH_NOTEXT
)

////////////////////////////////////////

//XPattern is a structure to store pattern (paths) matches
type XPattern struct {
	parts []string
	attrs []xml.Attr
	texts []string
	start int
	curr  int
}

// NewXPattern builds the "parts" list (elements) and the attrs list (attributes)
func NewXPattern(pattern string) *XPattern {
	x := &XPattern{}

	x.parts = strings.Split(pattern, "/")
	x.attrs = make([]xml.Attr, len(x.parts))
	x.texts = make([]string, len(x.parts))

	for i, part := range x.parts {
		if p := strings.Index(part, "["); p >= 0 {
			matches := pAttribute.FindStringSubmatch(part[p:])
			x.parts[i] = part[:p]

			if matches != nil {
				x.attrs[i] = xml.Attr{Name: xml.Name{Local: matches[1]}, Value: matches[3]}
			}
			continue
		}

		if p := strings.Index(part, "="); p >= 0 {
			value := part[p:]
			x.parts[i] = part[:p]

			if strings.HasPrefix(value, `'`) {
				value = strings.Trim(value, `'`)
			} else if strings.HasPrefix(value, `"`) {
				value = strings.Trim(value, `"`)
			}

			x.texts[i] = value
		}
	}

	x.start = -1
	x.curr = -1
	return x
}

//CloneXPattern creates a copy if the input pattern
func CloneXPattern(p *XPattern) *XPattern {
	x := &XPattern{
		parts: make([]string, len(p.parts)),
		attrs: make([]xml.Attr, len(p.attrs)),
		texts: make([]string, len(p.texts)),
		start: -1,
		curr:  -1,
	}

	copy(x.parts, p.parts)
	copy(x.attrs, p.attrs)
	copy(x.texts, p.texts)
	return x
}

// matchName matches an element name. '*' means any name
func (p *XPattern) matchName(name xml.Name, i int) bool {
	return p.parts[i] == "*" || p.parts[i] == name.Local
}

// matchAttr matches an element attribute. nil means any attribute
func (p *XPattern) matchAttr(attr []xml.Attr, i int) bool {
	mattr := p.attrs[i]

	if mattr == NoAttr {
		return true
	}

	for _, a := range attr {
		if mattr == a {
			return true
		}
	}

	return false
}

// matchRoot matches the root of the path
func (p *XPattern) matchRoot(name xml.Name, attr []xml.Attr) bool {
	return p.matchName(name, 0) && p.matchAttr(attr, 0)
}

// matchLevel matches start level and name (use to match at end of elements)
func (p *XPattern) matchLevel(name xml.Name, level int) bool {
	return level == p.start && p.matchName(name, 0)
}

// matchPAth matches the Matches full path (use to match at start of element)
func (p *XPattern) matchPath(name xml.Name, attr []xml.Attr, level int) Matches {
	pos := 0

	if p.start >= 0 {
		if level < p.start {
			p.start = -1
			p.curr = -1
			return MATCH_NO
		}

		switch {
		case level-p.start >= len(p.parts):
			if p.curr-p.start == len(p.parts) {
				return MATCH_DESCENDANT
			} else {
				return MATCH_PARTIAL
			}

		case level > p.curr:
			return MATCH_PARTIAL

		case level < p.curr:
			p.curr = level
		}

		pos = p.curr - p.start
	}

	if !p.matchName(name, pos) {
		if p.start >= 0 {
			return MATCH_PARTIAL
		} else {
			return MATCH_NO
		}
	}

	if !p.matchAttr(attr, pos) {
		if p.start >= 0 {
			return MATCH_PARTIAL
		} else {
			return MATCH_NO
		}
	}

	if p.start < 0 {
		p.start = level
	}

	p.curr = level + 1

	if pos == len(p.parts)-1 {
		if p.texts[pos] == "" {
			return MATCH_PATTERN
		} else {
			return MATCH_TEXT
		}
	} else {
		return MATCH_PART
	}
}

// matchText matches the text for an element (in EndElement)
func (p *XPattern) matchText(chars xml.CharData) Matches {
	text := string(chars)
	last := len(p.parts) - 1

	switch {
	case p.texts[last] == "":
		return MATCH_NOTEXT

	case p.texts[last] == text:
		return MATCH_PATTERN

	default:
		return MATCH_NO
	}
}

////////////////////////////////////////

type SAXFinder struct {
	pattern   *XPattern    // pattern to find
	matching  []*XPattern  // pattern that matches
	current   string       // current path
	text      xml.CharData // current text
	attrs     []xml.Attr   // current attributes
	level     int          // current level
	lastMatch Matches
}

func NewSAXFinder() *SAXFinder {
	return &SAXFinder{
		level:     -1,
		lastMatch: MATCH_NO,
	}
}

func (h *SAXFinder) SetPattern(pattern string) {
	h.pattern = NewXPattern(pattern)
	h.matching = nil
	h.current = ""
	h.text = nil
	h.attrs = nil
	h.level = -1
	h.lastMatch = MATCH_NO
}

func (h *SAXFinder) StartDocument() (stop bool) {
	dlog("doc start")

	h.current = ""
	h.text = nil
	h.attrs = nil
	h.level = -1
	h.lastMatch = MATCH_NO
	return
}

func (h *SAXFinder) EndDocument() (stop bool) {
	dlog("doc end")
	return
}

func (h *SAXFinder) StartElement(element xml.StartElement) (stop bool) {
	dlog("start ", element.Name.Space, ":", element.Name.Local)

	h.text = nil
	h.attrs = element.Attr
	h.level += 1
	h.current += "/" + element.Name.Local

	_ = "breakpoint"

	if h.pattern.matchRoot(element.Name, element.Attr) {
		h.matching = append(h.matching, CloneXPattern(h.pattern))
	}

	var match *XPattern

	//
	// Process current matching patterns
	//
	for _, p := range h.matching {
		h.lastMatch = p.matchPath(element.Name, element.Attr, h.level)

		// If full match, we are done
		if h.lastMatch == MATCH_PATTERN {
			match = p
			break
		}

		if h.lastMatch == MATCH_TEXT {
			break
		}
	}

	if match != nil {
		return true
	}

	return
}

func (h *SAXFinder) EndElement(element xml.EndElement) (stop bool) {
	dlog("end", element.Name.Space, ":", element.Name.Local)

	/*
	 * See if we have any pending element to start
	 */
	if h.lastMatch == MATCH_TEXT {
		for i := 0; i < len(h.matching); i++ {
			p := h.matching[i]

			match := p.matchText(h.text)
			if match == MATCH_NOTEXT { // do the element
				h.lastMatch = match
				break
			}

			if match == MATCH_PATTERN {
				h.lastMatch = match
				break
			}
		}

		if h.lastMatch == MATCH_TEXT { // we didn't match
			h.lastMatch = MATCH_NO
		}
	}

	for i := len(h.matching) - 1; i >= 0; i-- {
		p := h.matching[i]

		if p.matchLevel(element.Name, h.level) {
			h.matching = append(h.matching[:i], h.matching[i+1:]...)
		}
	}

	l := strings.LastIndex(h.current, "/")
	h.current = h.current[:l]
	h.level--

	h.text = nil
	h.attrs = nil

	return h.lastMatch == MATCH_PATTERN
}

func (h *SAXFinder) Comment(comment xml.Comment) (stop bool) {
	dlog("comment", string(comment))
	return
}

func (h *SAXFinder) CharData(chars xml.CharData) (stop bool) {
	dlog("chardata", string(chars))

	h.text = append(h.text, chars...)
	return
}

func (h *SAXFinder) ProcInst(proc xml.ProcInst) (stop bool) {
	dlog("proc", proc.Target, string(proc.Inst))
	return
}

func (h *SAXFinder) Directive(dir xml.Directive) (stop bool) {
	dlog("directive", string(dir))
	return
}

////////////////////////////////////////

func main() {
	if len(os.Args) != 3 {
		log.Fatal("usage: saxpath {input.xml} {xpath}")
	}

	filename := os.Args[1]
	xpattern := os.Args[2]

	f, err := os.Open(filename)
	if err != nil {
		log.Fatal(err)
	}

	defer f.Close()

	Debug = true

	_ = "breakpoint"

	handler := NewSAXFinder()
	handler.SetPattern(xpattern)
	parser := NewSAXParser(f, handler)
	//parser.SetHTMLMode()

	for i := 0; i < 3; i++ {
		t, err := parser.Parse()
		log.Printf("Token: %#v, Error: %v", t, err)

		if err != ParseStopped {
			break
		}
	}
}
<commit_msg>Added FindElement<commit_after>package main

import (
	//"encoding/xml"
	"errors"
	"github.com/raff/saxpath/xml"
	"io"
	"log"
	"os"
	"regexp"
	"strings"
)

var (
	Debug = false

	ParseComplete = errors.New("parsing complete")
	ParseStopped  = errors.New("parsing stopped")
	ReadComplete  = io.EOF
	InvalidToken  = errors.New("invalid token")

	AnyName = xml.Name{Local: "*"}
	AnyAttr = xml.Attr{Name: AnyName, Value: "*"}
	NoAttr  = xml.Attr{}

	// ugly regexp matching the basic XPath predicate expression [ @name = 'value' ]
	pAttribute = regexp.MustCompile(`^\\[\\s*@(.*)\\s*(=)\\s*["']?(.*?)["']?\\s*\\]`)
)

func dlog(args ...interface{}) {
	if Debug {
		log.Println(args...)
	}
}

func dlogf(fmt string, args ...interface{}) {
	if Debug {
		log.Printf(fmt, args...)
	}
}

////////////////////////////////////////

//SAX-like handler
type SAXHandler interface {
	//called when XML document start
	StartDocument() bool
	//called when XML document end
	EndDocument() bool
	//called when XML tag start
	StartElement(xml.StartElement) bool
	//called when XML tag end
	EndElement(xml.EndElement) bool
	//called when the parser encount chardata
	CharData(xml.CharData) bool
	//called when the parser encount comment
	Comment(xml.Comment) bool
	//called when the parser encount procInst
	//<!procinst >
	ProcInst(xml.ProcInst) bool
	//called when the parser encount directive
	//
	Directive(xml.Directive) bool
}

////////////////////////////////////////

//SAX-like XML Parser
type SAXParser struct {
	*xml.Decoder
	handler SAXHandler
	started bool
	ended   bool
}

//Create a New SAXParser
func NewSAXParser(reader io.Reader, handler SAXHandler) *SAXParser {
	decoder := xml.NewDecoder(reader)
	return &SAXParser{Decoder: decoder, handler: handler}
}

//SetHTMLMode make Parser can parse invalid HTML
func (p *SAXParser) SetHTMLMode() {
	p.Strict = false
	p.AutoClose = xml.HTMLAutoClose
	p.Entity = xml.HTMLEntity
}

//Parse calls handler's methods
//when the parser encount a start-element,a end-element, a comment and so on.
//
//The parsing process stops if the handler methods return "true" and can be restarted
//by calling Parse again until it returns ParseComplete or ReadComplete
func (p *SAXParser) Parse() (xml.Token, error) {
	if p.ended {
		return nil, ParseComplete
	}

	if !p.started {
		p.started = true
		p.handler.StartDocument()
	}

	stop := false

	for {
		token, err := p.Token()
		if err == io.EOF {
			return nil, ReadComplete
		}
		if err != nil {
			return nil, err
		}

		switch t := token.(type) {
		case xml.StartElement:
			stop = p.handler.StartElement(t)
		case xml.EndElement:
			stop = p.handler.EndElement(t)
		case xml.CharData:
			stop = p.handler.CharData(t)
		case xml.Comment:
			stop = p.handler.Comment(t)
		case xml.ProcInst:
			stop = p.handler.ProcInst(t)
		case xml.Directive:
			stop = p.handler.Directive(t)
		default:
			panic("unknown xml token")
		}

		if stop {
			return token, ParseStopped
		}

	}

	p.ended = true
	p.handler.EndDocument()
	return nil, ParseComplete
}

////////////////////////////////////////

type Matches int

const (
	MATCH_NO Matches = iota
	MATCH_PART
	MATCH_PATTERN
	MATCH_DESCENDANT
	MATCH_PARTIAL
	MATCH_TEXT
	MATCH_NOTEXT
)

////////////////////////////////////////

//XPattern is a structure to store pattern (paths) matches
type XPattern struct {
	parts []string
	attrs []xml.Attr
	texts []string
	start int
	curr  int
}

// NewXPattern builds the "parts" list (elements) and the attrs list (attributes)
func NewXPattern(pattern string) *XPattern {
	x := &XPattern{}

	x.parts = strings.Split(pattern, "/")
	x.attrs = make([]xml.Attr, len(x.parts))
	x.texts = make([]string, len(x.parts))

	for i, part := range x.parts {
		if p := strings.Index(part, "["); p >= 0 {
			matches := pAttribute.FindStringSubmatch(part[p:])
			x.parts[i] = part[:p]

			if matches != nil {
				x.attrs[i] = xml.Attr{Name: xml.Name{Local: matches[1]}, Value: matches[3]}
			}
			continue
		}

		if p := strings.Index(part, "="); p >= 0 {
			value := part[p:]
			x.parts[i] = part[:p]

			if strings.HasPrefix(value, `'`) {
				value = strings.Trim(value, `'`)
			} else if strings.HasPrefix(value, `"`) {
				value = strings.Trim(value, `"`)
			}

			x.texts[i] = value
		}
	}

	x.start = -1
	x.curr = -1
	return x
}

//CloneXPattern creates a copy if the input pattern
func CloneXPattern(p *XPattern) *XPattern {
	x := &XPattern{
		parts: make([]string, len(p.parts)),
		attrs: make([]xml.Attr, len(p.attrs)),
		texts: make([]string, len(p.texts)),
		start: -1,
		curr:  -1,
	}

	copy(x.parts, p.parts)
	copy(x.attrs, p.attrs)
	copy(x.texts, p.texts)
	return x
}

// matchName matches an element name. '*' means any name
func (p *XPattern) matchName(name xml.Name, i int) bool {
	return p.parts[i] == "*" || p.parts[i] == name.Local
}

// matchAttr matches an element attribute. nil means any attribute
func (p *XPattern) matchAttr(attr []xml.Attr, i int) bool {
	mattr := p.attrs[i]

	if mattr == NoAttr {
		return true
	}

	for _, a := range attr {
		if mattr == a {
			return true
		}
	}

	return false
}

// matchRoot matches the root of the path
func (p *XPattern) matchRoot(name xml.Name, attr []xml.Attr) bool {
	return p.matchName(name, 0) && p.matchAttr(attr, 0)
}

// matchLevel matches start level and name (use to match at end of elements)
func (p *XPattern) matchLevel(name xml.Name, level int) bool {
	return level == p.start && p.matchName(name, 0)
}

// matchPAth matches the Matches full path (use to match at start of element)
func (p *XPattern) matchPath(name xml.Name, attr []xml.Attr, level int) Matches {
	pos := 0

	if p.start >= 0 {
		if level < p.start {
			p.start = -1
			p.curr = -1
			return MATCH_NO
		}

		switch {
		case level-p.start >= len(p.parts):
			if p.curr-p.start == len(p.parts) {
				return MATCH_DESCENDANT
			} else {
				return MATCH_PARTIAL
			}

		case level > p.curr:
			return MATCH_PARTIAL

		case level < p.curr:
			p.curr = level
		}

		pos = p.curr - p.start
	}

	if !p.matchName(name, pos) {
		if p.start >= 0 {
			return MATCH_PARTIAL
		} else {
			return MATCH_NO
		}
	}

	if !p.matchAttr(attr, pos) {
		if p.start >= 0 {
			return MATCH_PARTIAL
		} else {
			return MATCH_NO
		}
	}

	if p.start < 0 {
		p.start = level
	}

	p.curr = level + 1

	if pos == len(p.parts)-1 {
		if p.texts[pos] == "" {
			return MATCH_PATTERN
		} else {
			return MATCH_TEXT
		}
	} else {
		return MATCH_PART
	}
}

// matchText matches the text for an element (in EndElement)
func (p *XPattern) matchText(chars xml.CharData) Matches {
	text := string(chars)
	last := len(p.parts) - 1

	switch {
	case p.texts[last] == "":
		return MATCH_NOTEXT

	case p.texts[last] == text:
		return MATCH_PATTERN

	default:
		return MATCH_NO
	}
}

////////////////////////////////////////

type SAXFinder struct {
	pattern   *XPattern    // pattern to find
	matching  []*XPattern  // pattern that matches
	current   string       // current path
	text      xml.CharData // current text
	attrs     []xml.Attr   // current attributes
	level     int          // current level
	lastMatch Matches
}

func NewSAXFinder() *SAXFinder {
	return &SAXFinder{
		level:     -1,
		lastMatch: MATCH_NO,
	}
}

func (h *SAXFinder) SetPattern(pattern string) {
	h.pattern = NewXPattern(pattern)
	h.matching = nil
	h.current = ""
	h.text = nil
	h.attrs = nil
	h.level = -1
	h.lastMatch = MATCH_NO
}

func (h *SAXFinder) StartDocument() (stop bool) {
	dlog("doc start")

	h.current = ""
	h.text = nil
	h.attrs = nil
	h.level = -1
	h.lastMatch = MATCH_NO
	return
}

func (h *SAXFinder) EndDocument() (stop bool) {
	dlog("doc end")
	return
}

func (h *SAXFinder) StartElement(element xml.StartElement) (stop bool) {
	dlog("start ", element.Name.Space, ":", element.Name.Local)

	h.text = nil
	h.attrs = element.Attr
	h.level += 1
	h.current += "/" + element.Name.Local

	if h.pattern.matchRoot(element.Name, element.Attr) {
		h.matching = append(h.matching, CloneXPattern(h.pattern))
	}

	var match *XPattern

	//
	// Process current matching patterns
	//
	for _, p := range h.matching {
		h.lastMatch = p.matchPath(element.Name, element.Attr, h.level)

		// If full match, we are done
		if h.lastMatch == MATCH_PATTERN {
			match = p
			break
		}

		if h.lastMatch == MATCH_TEXT {
			break
		}
	}

	if match != nil {
		return true
	}

	return
}

func (h *SAXFinder) EndElement(element xml.EndElement) (stop bool) {
	dlog("end", element.Name.Space, ":", element.Name.Local)

	/*
	 * See if we have any pending element to start
	 */
	if h.lastMatch == MATCH_TEXT {
		for i := 0; i < len(h.matching); i++ {
			p := h.matching[i]

			match := p.matchText(h.text)
			if match == MATCH_NOTEXT { // do the element
				h.lastMatch = match
				break
			}

			if match == MATCH_PATTERN {
				h.lastMatch = match
				break
			}
		}

		if h.lastMatch == MATCH_TEXT { // we didn't match
			h.lastMatch = MATCH_NO
		}
	}

	for i := len(h.matching) - 1; i >= 0; i-- {
		p := h.matching[i]

		if p.matchLevel(element.Name, h.level) {
			h.matching = append(h.matching[:i], h.matching[i+1:]...)
		}
	}

	l := strings.LastIndex(h.current, "/")
	h.current = h.current[:l]
	h.level--

	h.text = nil
	h.attrs = nil

	return h.lastMatch == MATCH_PATTERN
}

func (h *SAXFinder) Comment(comment xml.Comment) (stop bool) {
	dlog("comment", string(comment))
	return
}

func (h *SAXFinder) CharData(chars xml.CharData) (stop bool) {
	dlog("chardata", string(chars))

	h.text = append(h.text, chars...)
	return
}

func (h *SAXFinder) ProcInst(proc xml.ProcInst) (stop bool) {
	dlog("proc", proc.Target, string(proc.Inst))
	return
}

func (h *SAXFinder) Directive(dir xml.Directive) (stop bool) {
	dlog("directive", string(dir))
	return
}

////////////////////////////////////////

func NewFinder(r io.Reader) *SAXParser {
	return NewSAXParser(r, NewSAXFinder())
}

func (p *SAXParser) FindElement(pattern string, res interface{}) error {
	p.handler.(*SAXFinder).SetPattern(pattern)
	t, err := p.Parse()

	dlogf("Token: %#v, State: %v", t, err)

	if err != ParseStopped || res == nil {
		return err
	}

	if s, ok := t.(xml.StartElement); ok {
		dlog("decode", s)
		return p.DecodeElement(res, &s)
	}

	return InvalidToken
}

////////////////////////////////////////

func main() {
	if len(os.Args) != 3 {
		log.Fatal("usage: saxpath {input.xml} {xpath}")
	}

	filename := os.Args[1]
	xpattern := os.Args[2]

	f, err := os.Open(filename)
	if err != nil {
		log.Fatal(err)
	}

	defer f.Close()

	Debug = true

	//var res struct {
	//    Value string `xml:",chardata"`
	//}

	var res string

	finder := NewFinder(f)
	err = finder.FindElement(xpattern, &res)
	log.Println(err, res)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"io"
)

type Token int

const (
	ILLEGAL Token = iota
	EOF
	EOL
	SPACE
	QUOTE
	LBRACKET
	RBRACKET
	IDENT
)

var eof = rune(0)

type Scanner struct {
	r         *bufio.Reader
	inQuote   bool
	inBracket bool
}

func NewScanner(r io.Reader) *Scanner {
	return &Scanner{r: bufio.NewReader(r)}
}

func (s *Scanner) read() rune {
	ch, _, err := s.r.ReadRune()
	if err != nil {
		return eof
	}
	return ch
}

func (s *Scanner) unread() { _ = s.r.UnreadRune() }

func (s *Scanner) Scan() (tok Token, lit string) {
	ch := s.read()

	if isNewline(ch) {
		s.unread()
		return s.scanNewline()
	}
	switch ch {
	case eof:
		return EOF, ""
	case ' ':
		return SPACE, " "
	case '"':
		s.inQuote = !s.inQuote
		return QUOTE, `"`
	case '[':
		s.inBracket = true
		return LBRACKET, "["
	case ']':
		s.inBracket = false
		return RBRACKET, "]"
	default:
		s.unread()
		return s.scanIdent()
	}
	return ILLEGAL, string(ch)
}

func (s *Scanner) scanNewline() (tok Token, lit string) {
	for {
		if ch := s.read(); ch == eof {
			break
		} else if !isNewline(ch) {
			s.unread()
			break
		}
	}
	return EOL, "\n"
}

func (s *Scanner) scanIdent() (tok Token, lit string) {
	var buf bytes.Buffer
	buf.WriteRune(s.read())
	for {
		if ch := s.read(); ch == eof {
			break
		} else if isNewline(ch) {
			s.unread()
			break
		} else if s.inQuote && ch != '"' {
			_, _ = buf.WriteRune(ch)
		} else if s.inBracket {
			if ch == ']' {
				s.unread()
				break
			}
			_, _ = buf.WriteRune(ch)
		} else if ch != ' ' && ch != '"' {
			_, _ = buf.WriteRune(ch)
		} else {
			s.unread()
			break
		}
	}
	return IDENT, buf.String()
}

func isNewline(ch rune) bool {
	switch ch {
	case '\r', '\n':
		return true
	default:
		return false
	}
}
<commit_msg>Fix go vet warning<commit_after>package main

import (
	"bufio"
	"bytes"
	"io"
)

type Token int

const (
	ILLEGAL Token = iota
	EOF
	EOL
	SPACE
	QUOTE
	LBRACKET
	RBRACKET
	IDENT
)

var eof = rune(0)

type Scanner struct {
	r         *bufio.Reader
	inQuote   bool
	inBracket bool
}

func NewScanner(r io.Reader) *Scanner {
	return &Scanner{r: bufio.NewReader(r)}
}

func (s *Scanner) read() rune {
	ch, _, err := s.r.ReadRune()
	if err != nil {
		return eof
	}
	return ch
}

func (s *Scanner) unread() { _ = s.r.UnreadRune() }

func (s *Scanner) Scan() (tok Token, lit string) {
	ch := s.read()

	if isNewline(ch) {
		s.unread()
		return s.scanNewline()
	}
	switch ch {
	case eof:
		return EOF, ""
	case ' ':
		return SPACE, " "
	case '"':
		s.inQuote = !s.inQuote
		return QUOTE, `"`
	case '[':
		s.inBracket = true
		return LBRACKET, "["
	case ']':
		s.inBracket = false
		return RBRACKET, "]"
	default:
		s.unread()
		return s.scanIdent()
	}
}

func (s *Scanner) scanNewline() (tok Token, lit string) {
	for {
		if ch := s.read(); ch == eof {
			break
		} else if !isNewline(ch) {
			s.unread()
			break
		}
	}
	return EOL, "\n"
}

func (s *Scanner) scanIdent() (tok Token, lit string) {
	var buf bytes.Buffer
	buf.WriteRune(s.read())
	for {
		if ch := s.read(); ch == eof {
			break
		} else if isNewline(ch) {
			s.unread()
			break
		} else if s.inQuote && ch != '"' {
			_, _ = buf.WriteRune(ch)
		} else if s.inBracket {
			if ch == ']' {
				s.unread()
				break
			}
			_, _ = buf.WriteRune(ch)
		} else if ch != ' ' && ch != '"' {
			_, _ = buf.WriteRune(ch)
		} else {
			s.unread()
			break
		}
	}
	return IDENT, buf.String()
}

func isNewline(ch rune) bool {
	switch ch {
	case '\r', '\n':
		return true
	default:
		return false
	}
}
<|endoftext|>