<commit_before>package segment

import "math"

// NewLazy return the initialized lazy segment tree (n == 2^p)
func NewLazy(n int) ([]int64, []int64) {
	t := New(n)
	return t, make([]int64, len(t))
}

// NewLazyS returns the initialization lazy segment tree value of the slice
func NewLazyS(inits []int64) ([]int64, []int64) {
	t := NewS(inits)
	return t, make([]int64, len(t))
}

func lazyEval(i int, t, lazy []int64) {
	t[i] += lazy[i]
	if i < (len(t)+1)/2-1 {
		lazy[i*2+1] += lazy[i]
		lazy[i*2+2] += lazy[i]
	}
	lazy[i] = 0
}

// LazyUpdate update the lazy segment tree
func LazyUpdate(a, b, i, l, r int, x int64, t, lazy []int64) {
	if r <= a || b <= l {
		return
	}

	if a <= l && r <= b {
		lazy[i] += x
		lazyEval(i, t, lazy)
		return
	}

	lazyEval(i, t, lazy)
	LazyUpdate(a, b, i*2+1, l, (l+r)/2, x, t, lazy)
	LazyUpdate(a, b, i*2+2, (l+r)/2, r, x, t, lazy)

	if t[i*2+1] < t[i*2+2] {
		t[i] = t[i*2+1]
	} else {
		t[i] = t[i*2+2]
	}
	return
}

// LazyQuery to run a query in the interval
func LazyQuery(a, b, i, l, r int, t, lazy []int64) int64 {
	lazyEval(i, t, lazy)

	if r <= a || b <= l {
		return math.MaxInt64
	}

	if a <= l && r <= b {
		return t[i]
	}

	vl := LazyQuery(a, b, i*2+1, l, (l+r)/2, t, lazy)
	vr := LazyQuery(a, b, i*2+2, (l+r)/2, r, t, lazy)

	if vl < vr {
		return vl
	}
	return vr
}
<commit_msg>Refactoring<commit_after>package segment

import "math"

// NewLazy return the initialized lazy segment tree (n == 2^p)
func NewLazy(n int) ([]int64, []int64) {
	t := New(n)
	return t, make([]int64, len(t))
}

// NewLazyS returns the initialization lazy segment tree value of the slice
func NewLazyS(inits []int64) ([]int64, []int64) {
	t := NewS(inits)
	return t, make([]int64, len(t))
}

// LazyUpdate update the lazy segment tree
func LazyUpdate(a, b, i, l, r int, x int64, t, lazy []int64) {
	if r <= a || b <= l {
		return
	}

	if a <= l && r <= b {
		lazy[i] += x
		lazyEval(i, t, lazy)
		return
	}

	lazyEval(i, t, lazy)
	LazyUpdate(a, b, i*2+1, l, (l+r)/2, x, t, lazy)
	LazyUpdate(a, b, i*2+2, (l+r)/2, r, x, t, lazy)

	if t[i*2+1] < t[i*2+2] {
		t[i] = t[i*2+1]
	} else {
		t[i] = t[i*2+2]
	}
	return
}

// LazyQuery to run a query in the interval
func LazyQuery(a, b, i, l, r int, t, lazy []int64) int64 {
	lazyEval(i, t, lazy)

	if r <= a || b <= l {
		return math.MaxInt64
	}

	if a <= l && r <= b {
		return t[i]
	}

	vl := LazyQuery(a, b, i*2+1, l, (l+r)/2, t, lazy)
	vr := LazyQuery(a, b, i*2+2, (l+r)/2, r, t, lazy)

	if vl < vr {
		return vl
	}
	return vr
}

func lazyEval(i int, t, lazy []int64) {
	t[i] += lazy[i]
	if i < (len(t)+1)/2-1 {
		lazy[i*2+1] += lazy[i]
		lazy[i*2+2] += lazy[i]
	}
	lazy[i] = 0
}
<|endoftext|>


<commit_before>package sensu

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"strings"

	"github.com/upfluence/sensu-client-go/Godeps/_workspace/src/github.com/upfluence/sensu-go/sensu/check"
	"github.com/upfluence/sensu-client-go/Godeps/_workspace/src/github.com/upfluence/sensu-go/sensu/client"
)

const defaultRabbitMQURI string = "amqp://guest:guest@localhost:5672/%2f"

type configFlagSet struct {
	configFile string
	verbose    bool
}

type Config struct {
	flagSet *configFlagSet
	config  *configPayload
}

type configPayload struct {
	Client      *client.Client `json:"client,omitempty"`
	Checks      []*check.Check `json:"checks,omitempty"`
	RabbitMQURI *string        `json:"rabbitmq_uri,omitempty"`
}

func fetchEnv(envs ...string) string {
	for _, env := range envs {
		if v := os.Getenv(env); v != "" {
			return v
		}
	}

	return ""
}

// split is a wrapper for strings.Split, which returns an empty array
// for empty string inputs instead of an array containing an empty string
func split(str string, token string) []string {
	if len(str) == 0 {
		return []string{}
	}

	return strings.Split(str, token)
}

func NewConfigFromFile(flagset *configFlagSet, configFile string) (*Config, error) {
	cfg := Config{flagset, &configPayload{}}
	buf, err := ioutil.ReadFile(configFile)

	if err != nil {
		return nil, err
	}

	if err := json.Unmarshal(buf, &cfg.config); err != nil {
		return nil, err
	}
	return &cfg, nil
}

func NewConfigFromFlagSet(flagset *configFlagSet) (*Config, error) {
	if flagset != nil && flagset.configFile != "" {
		cfg, err := NewConfigFromFile(flagset, flagset.configFile)
		if err != nil {
			return nil, err
		}
		return cfg, nil
	} else {
		var cfg = Config{flagset, &configPayload{}}
		return &cfg, nil
	}

}

func (c *Config) RabbitMQURI() string {
	if cfg := c.config; cfg != nil && cfg.RabbitMQURI != nil {
		return *cfg.RabbitMQURI
	} else if uri := fetchEnv("RABBITMQ_URI", "RABBITMQ_URL"); uri != "" {
		return uri
	}

	return defaultRabbitMQURI
}

func (c *Config) Client() *client.Client {
	if cfg := c.config; cfg != nil && cfg.Client != nil {
		return cfg.Client
	}

	return &client.Client{
		Name:          os.Getenv("SENSU_CLIENT_NAME"),
		Address:       fetchEnv("SENSU_CLIENT_ADDRESS", "SENSU_ADDRESS"),
		Subscriptions: split(os.Getenv("SENSU_CLIENT_SUBSCRIPTIONS"), ","),
	}
}

func (c *Config) Checks() []*check.Check {
	if cfg := c.config; cfg != nil {
		return cfg.Checks
	}

	return []*check.Check{}
}
<commit_msg>replace redundant error with returning tuple in NewConfigFromFlagSet<commit_after>package sensu

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"strings"

	"github.com/upfluence/sensu-client-go/Godeps/_workspace/src/github.com/upfluence/sensu-go/sensu/check"
	"github.com/upfluence/sensu-client-go/Godeps/_workspace/src/github.com/upfluence/sensu-go/sensu/client"
)

const defaultRabbitMQURI string = "amqp://guest:guest@localhost:5672/%2f"

type configFlagSet struct {
	configFile string
	verbose    bool
}

type Config struct {
	flagSet *configFlagSet
	config  *configPayload
}

type configPayload struct {
	Client      *client.Client `json:"client,omitempty"`
	Checks      []*check.Check `json:"checks,omitempty"`
	RabbitMQURI *string        `json:"rabbitmq_uri,omitempty"`
}

func fetchEnv(envs ...string) string {
	for _, env := range envs {
		if v := os.Getenv(env); v != "" {
			return v
		}
	}

	return ""
}

// split is a wrapper for strings.Split, which returns an empty array
// for empty string inputs instead of an array containing an empty string
func split(str string, token string) []string {
	if len(str) == 0 {
		return []string{}
	}

	return strings.Split(str, token)
}

func NewConfigFromFile(flagset *configFlagSet, configFile string) (*Config, error) {
	cfg := Config{flagset, &configPayload{}}
	buf, err := ioutil.ReadFile(configFile)

	if err != nil {
		return nil, err
	}

	if err := json.Unmarshal(buf, &cfg.config); err != nil {
		return nil, err
	}
	return &cfg, nil
}

func NewConfigFromFlagSet(flagset *configFlagSet) (*Config, error) {
	if flagset != nil && flagset.configFile != "" {
		return NewConfigFromFile(flagset, flagset.configFile)
	} else {
		var cfg = Config{flagset, &configPayload{}}
		return &cfg, nil
	}

}

func (c *Config) RabbitMQURI() string {
	if cfg := c.config; cfg != nil && cfg.RabbitMQURI != nil {
		return *cfg.RabbitMQURI
	} else if uri := fetchEnv("RABBITMQ_URI", "RABBITMQ_URL"); uri != "" {
		return uri
	}

	return defaultRabbitMQURI
}

func (c *Config) Client() *client.Client {
	if cfg := c.config; cfg != nil && cfg.Client != nil {
		return cfg.Client
	}

	return &client.Client{
		Name:          os.Getenv("SENSU_CLIENT_NAME"),
		Address:       fetchEnv("SENSU_CLIENT_ADDRESS", "SENSU_ADDRESS"),
		Subscriptions: split(os.Getenv("SENSU_CLIENT_SUBSCRIPTIONS"), ","),
	}
}

func (c *Config) Checks() []*check.Check {
	if cfg := c.config; cfg != nil {
		return cfg.Checks
	}

	return []*check.Check{}
}
<|endoftext|>


<commit_before>package server

var (
	// CodeFailure indicates the requested action failed.
	CodeFailure = "FAILURE"
	// CodeInvalidCredentials indicates the provided credentials are not valid.
	CodeInvalidCredentials = "INVALID_CREDENTIALS"
	// CodePermissionDenied indicates the provided credentials are valid, but the
	// requested resource requires other permissions.
	CodePermissionDenied = "PERMISSION_DENIED"
	// CodeResourceAlreadyExists indicates a resource does already exist.
	CodeResourceAlreadyExists = "RESOURCE_ALREADY_EXISTS"
	// CodeResourceCreated indicates a resource has been created.
	CodeResourceCreated = "RESOURCE_CREATED"
	// CodeResourceDeleted indicates a resource has been deleted.
	CodeResourceDeleted = "RESOURCE_DELETED"
	// CodeResourceDeletionStarted indicates a resource will be deleted.
	CodeResourceDeletionStarted = "RESOURCE_DELETION_STARTED"
	// CodeResourceNotFound indicates a resource could not be found.
	CodeResourceNotFound = "RESOURCE_NOT_FOUND"
	// CodeResourceUpdated indicates a resource has been updated.
	CodeResourceUpdated = "RESOURCE_UPDATED"
	// CodeSuccess indicates the requested action successed.
	CodeSuccess = "SUCCESS"
	// CodeImmutableAttribute indicates the provided data structure contains
	// fields that are immutable.
	CodeImmutableAttribute = "IMMUTABLE_ATTRIBUTE"
	// CodeUnknownAttribute indicates the provided data structure contains
	// unexpected fields.
	CodeUnknownAttribute = "UNKNOWN_ATTRIBUTE"
	// CodeInvalidInput indicates the user provided some input that does not validate
	// (usually HTTP status 400).
	CodeInvalidInput = "INVALID_INPUT"
	// CodeInternalError represents an error we don't want to give more details
	// about (usually HTTP status 500).
	CodeInternalError = "INTERNAL_ERROR"
)
<commit_msg>Add too many requests code. (#111)<commit_after>package server

var (
	// CodeFailure indicates the requested action failed.
	CodeFailure = "FAILURE"
	// CodeInvalidCredentials indicates the provided credentials are not valid.
	CodeInvalidCredentials = "INVALID_CREDENTIALS"
	// CodePermissionDenied indicates the provided credentials are valid, but the
	// requested resource requires other permissions.
	CodePermissionDenied = "PERMISSION_DENIED"
	// CodeResourceAlreadyExists indicates a resource does already exist.
	CodeResourceAlreadyExists = "RESOURCE_ALREADY_EXISTS"
	// CodeResourceCreated indicates a resource has been created.
	CodeResourceCreated = "RESOURCE_CREATED"
	// CodeResourceDeleted indicates a resource has been deleted.
	CodeResourceDeleted = "RESOURCE_DELETED"
	// CodeResourceDeletionStarted indicates a resource will be deleted.
	CodeResourceDeletionStarted = "RESOURCE_DELETION_STARTED"
	// CodeResourceNotFound indicates a resource could not be found.
	CodeResourceNotFound = "RESOURCE_NOT_FOUND"
	// CodeResourceUpdated indicates a resource has been updated.
	CodeResourceUpdated = "RESOURCE_UPDATED"
	// CodeSuccess indicates the requested action successed.
	CodeSuccess = "SUCCESS"
	// CodeTooManyRequests indicates the client is making too many requests.
	CodeTooManyRequests = "TOO_MANY_REQUESTS"
	// CodeImmutableAttribute indicates the provided data structure contains
	// fields that are immutable.
	CodeImmutableAttribute = "IMMUTABLE_ATTRIBUTE"
	// CodeUnknownAttribute indicates the provided data structure contains
	// unexpected fields.
	CodeUnknownAttribute = "UNKNOWN_ATTRIBUTE"
	// CodeInvalidInput indicates the user provided some input that does not validate
	// (usually HTTP status 400).
	CodeInvalidInput = "INVALID_INPUT"
	// CodeInternalError represents an error we don't want to give more details
	// about (usually HTTP status 500).
	CodeInternalError = "INTERNAL_ERROR"
)
<|endoftext|>


<commit_before>// Copyright 2012-2015 Apcera Inc. All rights reserved.

package server

import (
	"time"
)

const (
	// VERSION is the current version for the server.
	VERSION = "0.7.3"

	// DEFAULT_PORT is the deault port for client connections.
	DEFAULT_PORT = 4222

	// RANDOM_PORT is the value for port that, when supplied, will cause the
	// server to listen on a randomly-chosen available port. The resolved port
	// is available via the Addr() method.
	RANDOM_PORT = -1

	// DEFAULT_HOST defaults to all interfaces.
	DEFAULT_HOST = "0.0.0.0"

	// MAX_CONTROL_LINE_SIZE is the maximum allowed protocol control line size.
	// 1k should be plenty since payloads sans connect string are separate
	MAX_CONTROL_LINE_SIZE = 1024

	// MAX_PAYLOAD_SIZE is the maximum allowed payload size. Should be using
	// something different if > 1MB payloads are needed.
	MAX_PAYLOAD_SIZE = (1024 * 1024)

	// MAX_PENDING_SIZE is the maximum outbound size (in bytes) per client.
	MAX_PENDING_SIZE = (10 * 1024 * 1024)

	// DEFAULT_MAX_CONNECTIONS is the default maximum connections allowed.
	DEFAULT_MAX_CONNECTIONS = (64 * 1024)

	// TLS_TIMEOUT is the TLS wait time.
	TLS_TIMEOUT = 500 * time.Millisecond

	// AUTH_TIMEOUT is the authorization wait time.
	AUTH_TIMEOUT = 2 * TLS_TIMEOUT

	// DEFAULT_PING_INTERVAL is how often pings are sent to clients and routes.
	DEFAULT_PING_INTERVAL = 2 * time.Minute

	// DEFAULT_PING_MAX_OUT is maximum allowed pings outstanding before disconnect.
	DEFAULT_PING_MAX_OUT = 2

	// CRLF string
	CR_LF = "\r\n"

	// LEN_CR_LF hold onto the computed size.
	LEN_CR_LF = len(CR_LF)

	// DEFAULT_FLUSH_DEADLINE is the write/flush deadlines.
	DEFAULT_FLUSH_DEADLINE = 2 * time.Second

	// DEFAULT_HTTP_PORT is the default monitoring port.
	DEFAULT_HTTP_PORT = 8222

	// ACCEPT_MIN_SLEEP is the minimum acceptable sleep times on temporary errors.
	ACCEPT_MIN_SLEEP = 10 * time.Millisecond

	// ACCEPT_MAX_SLEEP is the maximum acceptable sleep times on temporary errors
	ACCEPT_MAX_SLEEP = 1 * time.Second

	// DEFAULT_ROUTE_CONNECT Route solicitation intervals.
	DEFAULT_ROUTE_CONNECT = 1 * time.Second

	// DEFAULT_ROUTE_RECONNECT Route reconnect intervals.
	DEFAULT_ROUTE_RECONNECT = 1 * time.Second

	// DEFAULT_ROUTE_DIAL Route dial timeout.
	DEFAULT_ROUTE_DIAL = 1 * time.Second

	// PROTO_SNIPPET_SIZE is the default size of proto to print on parse errors.
	PROTO_SNIPPET_SIZE = 32

	// MAX_MSG_ARGS Maximum possible number of arguments from MSG proto.
	MAX_MSG_ARGS = 4

	// MAX_PUB_ARGS Maximum possible number of arguments from PUB proto.
	MAX_PUB_ARGS = 3

	// Default Buffer size for reads and writes per connection. Will be replaced by dynamic
	// system in the long run.
	DEFAULT_BUF_SIZE = 32768
)
<commit_msg>beta tag was being overloaded with lots of changes, so bumped<commit_after>// Copyright 2012-2016 Apcera Inc. All rights reserved.

package server

import (
	"time"
)

const (
	// VERSION is the current version for the server.
	VERSION = "0.7.5"

	// DEFAULT_PORT is the deault port for client connections.
	DEFAULT_PORT = 4222

	// RANDOM_PORT is the value for port that, when supplied, will cause the
	// server to listen on a randomly-chosen available port. The resolved port
	// is available via the Addr() method.
	RANDOM_PORT = -1

	// DEFAULT_HOST defaults to all interfaces.
	DEFAULT_HOST = "0.0.0.0"

	// MAX_CONTROL_LINE_SIZE is the maximum allowed protocol control line size.
	// 1k should be plenty since payloads sans connect string are separate
	MAX_CONTROL_LINE_SIZE = 1024

	// MAX_PAYLOAD_SIZE is the maximum allowed payload size. Should be using
	// something different if > 1MB payloads are needed.
	MAX_PAYLOAD_SIZE = (1024 * 1024)

	// MAX_PENDING_SIZE is the maximum outbound size (in bytes) per client.
	MAX_PENDING_SIZE = (10 * 1024 * 1024)

	// DEFAULT_MAX_CONNECTIONS is the default maximum connections allowed.
	DEFAULT_MAX_CONNECTIONS = (64 * 1024)

	// TLS_TIMEOUT is the TLS wait time.
	TLS_TIMEOUT = 500 * time.Millisecond

	// AUTH_TIMEOUT is the authorization wait time.
	AUTH_TIMEOUT = 2 * TLS_TIMEOUT

	// DEFAULT_PING_INTERVAL is how often pings are sent to clients and routes.
	DEFAULT_PING_INTERVAL = 2 * time.Minute

	// DEFAULT_PING_MAX_OUT is maximum allowed pings outstanding before disconnect.
	DEFAULT_PING_MAX_OUT = 2

	// CRLF string
	CR_LF = "\r\n"

	// LEN_CR_LF hold onto the computed size.
	LEN_CR_LF = len(CR_LF)

	// DEFAULT_FLUSH_DEADLINE is the write/flush deadlines.
	DEFAULT_FLUSH_DEADLINE = 2 * time.Second

	// DEFAULT_HTTP_PORT is the default monitoring port.
	DEFAULT_HTTP_PORT = 8222

	// ACCEPT_MIN_SLEEP is the minimum acceptable sleep times on temporary errors.
	ACCEPT_MIN_SLEEP = 10 * time.Millisecond

	// ACCEPT_MAX_SLEEP is the maximum acceptable sleep times on temporary errors
	ACCEPT_MAX_SLEEP = 1 * time.Second

	// DEFAULT_ROUTE_CONNECT Route solicitation intervals.
	DEFAULT_ROUTE_CONNECT = 1 * time.Second

	// DEFAULT_ROUTE_RECONNECT Route reconnect intervals.
	DEFAULT_ROUTE_RECONNECT = 1 * time.Second

	// DEFAULT_ROUTE_DIAL Route dial timeout.
	DEFAULT_ROUTE_DIAL = 1 * time.Second

	// PROTO_SNIPPET_SIZE is the default size of proto to print on parse errors.
	PROTO_SNIPPET_SIZE = 32

	// MAX_MSG_ARGS Maximum possible number of arguments from MSG proto.
	MAX_MSG_ARGS = 4

	// MAX_PUB_ARGS Maximum possible number of arguments from PUB proto.
	MAX_PUB_ARGS = 3

	// Default Buffer size for reads and writes per connection. Will be replaced by dynamic
	// system in the long run.
	DEFAULT_BUF_SIZE = 32768
)
<|endoftext|>


<commit_before>package client

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"reflect"
	"strconv"
	"strings"

	"github.com/google/go-querystring/query"
	muxpkg "github.com/sqs/mux"
	"sourcegraph.com/sourcegraph/api_router"
	"sourcegraph.com/sourcegraph/config2"
)

const (
	libraryVersion = "0.0.1"
	userAgent      = "sourcegraph-client/" + libraryVersion
)

// A Client communicates with the Sourcegraph API.
type Client struct {
	// Services used to communicate with different parts of the Sourcegraph API.
	DocPages       DocPagesService
	Builds         BuildsService
	People         PeopleService
	Repositories   RepositoriesService
	RepositoryTree RepositoryTreeService
	Symbols        SymbolsService
	Units          UnitsService

	// Base URL for API requests, which should have a trailing slash.
	BaseURL *url.URL

	// Router used to generate URLs for the Sourcegraph API.
	apiRouter *muxpkg.Router

	// User agent used for HTTP requests to the Sourcegraph API.
	UserAgent string

	// HTTP client used to communicate with the Sourcegraph API.
	httpClient *http.Client
}

// NewClient returns a new Sourcegraph API client. If httpClient is nil,
// http.DefaultClient is used.
func NewClient(httpClient *http.Client) *Client {
	if httpClient == nil {
		cloned := *http.DefaultClient
		httpClient = &cloned
	}

	c := new(Client)
	c.httpClient = httpClient
	c.DocPages = &docPagesService{c}
	c.Builds = &buildsService{c}
	c.People = &peopleService{c}
	c.Repositories = &repositoriesService{c}
	c.RepositoryTree = &repositoryTreeService{c}
	c.Symbols = &symbolsService{c}
	c.Units = &unitsService{c}

	c.BaseURL = config2.BaseAPIURL
	c.apiRouter = api_router.NewAPIRouter("")

	c.UserAgent = userAgent

	return c
}

// url generates the URL to the named Sourcegraph API endpoint, using the
// specified route variables and query options.
func (c *Client) url(apiRouteName string, routeVars map[string]string, opt interface{}) (*url.URL, error) {
	route := c.apiRouter.Get(apiRouteName)
	if route == nil {
		return nil, fmt.Errorf("no API route named %q", apiRouteName)
	}

	routeVarsList := make([]string, 2*len(routeVars))
	i := 0
	for name, val := range routeVars {
		routeVarsList[i*2] = name
		routeVarsList[i*2+1] = val
		i++
	}
	url, err := route.URL(routeVarsList...)
	if err != nil {
		return nil, err
	}

	// make the route URL path relative to BaseURL by trimming the leading "/"
	url.Path = strings.TrimPrefix(url.Path, "/")

	if opt != nil {
		err = addOptions(url, opt)
		if err != nil {
			return nil, err
		}
	}

	return url, nil
}

// NewRequest creates an API request. A relative URL can be provided in urlStr,
// in which case it is resolved relative to the BaseURL of the Client. Relative
// URLs should always be specified without a preceding slash. If specified, the
// value pointed to by body is JSON encoded and included as the request body.
func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) {
	rel, err := url.Parse(urlStr)
	if err != nil {
		return nil, err
	}

	u := c.BaseURL.ResolveReference(rel)

	buf := new(bytes.Buffer)
	if body != nil {
		err := json.NewEncoder(buf).Encode(body)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest(method, u.String(), buf)
	if err != nil {
		return nil, err
	}

	req.Header.Add("User-Agent", c.UserAgent)
	return req, nil
}

// newResponse creates a new Response for the provided http.Response.
func newResponse(r *http.Response) *HTTPResponse {
	return &HTTPResponse{Response: r}
}

// HTTPResponse is a wrapped HTTP response from the Sourcegraph API with
// additional Sourcegraph-specific response information parsed out. It
// implements Response.
type HTTPResponse struct {
	*http.Response
}

// TotalCount implements Response.
func (r *HTTPResponse) TotalCount() int {
	tc := r.Header.Get("x-total-count")
	if tc == "" {
		return -1
	}
	n, err := strconv.Atoi(tc)
	if err != nil {
		return -1
	}
	return n
}

type MockResponse struct{}

// Response is a response from the Sourcegraph API. When using the HTTP API,
// API methods return *HTTPResponse values that implement Response.
type Response interface {
	// TotalCount is the total number of items in the resource or result set
	// that exist remotely. Only a portion of the total may be in the response
	// body. If the endpoint did not return a total count, then TotalCount
	// returns -1.
	TotalCount() int
}

// ListOptions specifies general pagination options for fetching a list of
// results.
type ListOptions struct {
	PerPage int `url:",omitempty"`
	Page    int `url:",omitempty"`
}

const DefaultPerPage = 10

func (o ListOptions) PageOrDefault() int {
	if o.Page <= 0 {
		return 1
	}
	return o.Page
}

func (o ListOptions) PerPageOrDefault() int {
	if o.PerPage <= 0 {
		return DefaultPerPage
	}
	return o.PerPage
}

// Limit implements api_common.ResultSlice.
func (o ListOptions) Limit() int { return o.PerPageOrDefault() }

// Offset returns the 0-indexed offset of the first item that appears on this
// page, based on the PerPage and Page values (which are given default values if
// they are zero).
func (o ListOptions) Offset() int {
	return (o.PageOrDefault() - 1) * o.PerPageOrDefault()
}

// Do sends an API request and returns the API response.  The API response is
// decoded and stored in the value pointed to by v, or returned as an error if
// an API error has occurred.
func (c *Client) Do(req *http.Request, v interface{}) (*HTTPResponse, error) {
	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	response := newResponse(resp)

	err = CheckResponse(resp)
	if err != nil {
		// even though there was an error, we still return the response
		// in case the caller wants to inspect it further
		return response, err
	}

	if v != nil {
		if bp, ok := v.(*[]byte); ok {
			*bp, err = ioutil.ReadAll(resp.Body)
		} else {
			err = json.NewDecoder(resp.Body).Decode(v)
		}
	}
	if err != nil {
		return nil, fmt.Errorf("error reading response from %s %s: %s", req.Method, req.URL.RequestURI(), err)
	}
	return response, nil
}

// addOptions adds the parameters in opt as URL query parameters to u. opt
// must be a struct whose fields may contain "url" tags.
func addOptions(u *url.URL, opt interface{}) error {
	v := reflect.ValueOf(opt)
	if v.Kind() == reflect.Ptr && v.IsNil() {
		return nil
	}

	qs, err := query.Values(opt)
	if err != nil {
		return err
	}

	u.RawQuery = qs.Encode()
	return nil
}

// NewMockClient returns a mockable Client for use in tests.
func NewMockClient() *Client {
	return &Client{
		DocPages:       &MockDocPagesService{},
		Builds:         &MockBuildsService{},
		People:         &MockPeopleService{},
		Repositories:   &MockRepositoriesService{},
		RepositoryTree: &MockRepositoryTreeService{},
		Symbols:        &MockSymbolsService{},
	}
}
<commit_msg>show source units on repo page, emit in importer<commit_after>package client

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"reflect"
	"strconv"
	"strings"

	"github.com/google/go-querystring/query"
	muxpkg "github.com/sqs/mux"
	"sourcegraph.com/sourcegraph/api_router"
	"sourcegraph.com/sourcegraph/config2"
)

const (
	libraryVersion = "0.0.1"
	userAgent      = "sourcegraph-client/" + libraryVersion
)

// A Client communicates with the Sourcegraph API.
type Client struct {
	// Services used to communicate with different parts of the Sourcegraph API.
	DocPages       DocPagesService
	Builds         BuildsService
	People         PeopleService
	Repositories   RepositoriesService
	RepositoryTree RepositoryTreeService
	Symbols        SymbolsService
	Units          UnitsService

	// Base URL for API requests, which should have a trailing slash.
	BaseURL *url.URL

	// Router used to generate URLs for the Sourcegraph API.
	apiRouter *muxpkg.Router

	// User agent used for HTTP requests to the Sourcegraph API.
	UserAgent string

	// HTTP client used to communicate with the Sourcegraph API.
	httpClient *http.Client
}

// NewClient returns a new Sourcegraph API client. If httpClient is nil,
// http.DefaultClient is used.
func NewClient(httpClient *http.Client) *Client {
	if httpClient == nil {
		cloned := *http.DefaultClient
		httpClient = &cloned
	}

	c := new(Client)
	c.httpClient = httpClient
	c.DocPages = &docPagesService{c}
	c.Builds = &buildsService{c}
	c.People = &peopleService{c}
	c.Repositories = &repositoriesService{c}
	c.RepositoryTree = &repositoryTreeService{c}
	c.Symbols = &symbolsService{c}
	c.Units = &unitsService{c}

	c.BaseURL = config2.BaseAPIURL
	c.apiRouter = api_router.NewAPIRouter("")

	c.UserAgent = userAgent

	return c
}

// url generates the URL to the named Sourcegraph API endpoint, using the
// specified route variables and query options.
func (c *Client) url(apiRouteName string, routeVars map[string]string, opt interface{}) (*url.URL, error) {
	route := c.apiRouter.Get(apiRouteName)
	if route == nil {
		return nil, fmt.Errorf("no API route named %q", apiRouteName)
	}

	routeVarsList := make([]string, 2*len(routeVars))
	i := 0
	for name, val := range routeVars {
		routeVarsList[i*2] = name
		routeVarsList[i*2+1] = val
		i++
	}
	url, err := route.URL(routeVarsList...)
	if err != nil {
		return nil, err
	}

	// make the route URL path relative to BaseURL by trimming the leading "/"
	url.Path = strings.TrimPrefix(url.Path, "/")

	if opt != nil {
		err = addOptions(url, opt)
		if err != nil {
			return nil, err
		}
	}

	return url, nil
}

// NewRequest creates an API request. A relative URL can be provided in urlStr,
// in which case it is resolved relative to the BaseURL of the Client. Relative
// URLs should always be specified without a preceding slash. If specified, the
// value pointed to by body is JSON encoded and included as the request body.
func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) {
	rel, err := url.Parse(urlStr)
	if err != nil {
		return nil, err
	}

	u := c.BaseURL.ResolveReference(rel)

	buf := new(bytes.Buffer)
	if body != nil {
		err := json.NewEncoder(buf).Encode(body)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest(method, u.String(), buf)
	if err != nil {
		return nil, err
	}

	req.Header.Add("User-Agent", c.UserAgent)
	return req, nil
}

// newResponse creates a new Response for the provided http.Response.
func newResponse(r *http.Response) *HTTPResponse {
	return &HTTPResponse{Response: r}
}

// HTTPResponse is a wrapped HTTP response from the Sourcegraph API with
// additional Sourcegraph-specific response information parsed out. It
// implements Response.
type HTTPResponse struct {
	*http.Response
}

// TotalCount implements Response.
func (r *HTTPResponse) TotalCount() int {
	tc := r.Header.Get("x-total-count")
	if tc == "" {
		return -1
	}
	n, err := strconv.Atoi(tc)
	if err != nil {
		return -1
	}
	return n
}

type MockResponse struct{}

// Response is a response from the Sourcegraph API. When using the HTTP API,
// API methods return *HTTPResponse values that implement Response.
type Response interface {
	// TotalCount is the total number of items in the resource or result set
	// that exist remotely. Only a portion of the total may be in the response
	// body. If the endpoint did not return a total count, then TotalCount
	// returns -1.
	TotalCount() int
}

// ListOptions specifies general pagination options for fetching a list of
// results.
type ListOptions struct {
	PerPage int `url:",omitempty"`
	Page    int `url:",omitempty"`
}

const DefaultPerPage = 10

func (o ListOptions) PageOrDefault() int {
	if o.Page <= 0 {
		return 1
	}
	return o.Page
}

func (o ListOptions) PerPageOrDefault() int {
	if o.PerPage <= 0 {
		return DefaultPerPage
	}
	return o.PerPage
}

// Limit implements api_common.ResultSlice.
func (o ListOptions) Limit() int { return o.PerPageOrDefault() }

// Offset returns the 0-indexed offset of the first item that appears on this
// page, based on the PerPage and Page values (which are given default values if
// they are zero).
func (o ListOptions) Offset() int {
	return (o.PageOrDefault() - 1) * o.PerPageOrDefault()
}

// Do sends an API request and returns the API response.  The API response is
// decoded and stored in the value pointed to by v, or returned as an error if
// an API error has occurred.
func (c *Client) Do(req *http.Request, v interface{}) (*HTTPResponse, error) {
	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	response := newResponse(resp)

	err = CheckResponse(resp)
	if err != nil {
		// even though there was an error, we still return the response
		// in case the caller wants to inspect it further
		return response, err
	}

	if v != nil {
		if bp, ok := v.(*[]byte); ok {
			*bp, err = ioutil.ReadAll(resp.Body)
		} else {
			err = json.NewDecoder(resp.Body).Decode(v)
		}
	}
	if err != nil {
		return nil, fmt.Errorf("error reading response from %s %s: %s", req.Method, req.URL.RequestURI(), err)
	}
	return response, nil
}

// addOptions adds the parameters in opt as URL query parameters to u. opt
// must be a struct whose fields may contain "url" tags.
func addOptions(u *url.URL, opt interface{}) error {
	v := reflect.ValueOf(opt)
	if v.Kind() == reflect.Ptr && v.IsNil() {
		return nil
	}

	qs, err := query.Values(opt)
	if err != nil {
		return err
	}

	u.RawQuery = qs.Encode()
	return nil
}

// NewMockClient returns a mockable Client for use in tests.
func NewMockClient() *Client {
	return &Client{
		DocPages:       &MockDocPagesService{},
		Builds:         &MockBuildsService{},
		People:         &MockPeopleService{},
		Repositories:   &MockRepositoriesService{},
		RepositoryTree: &MockRepositoryTreeService{},
		Symbols:        &MockSymbolsService{},
		Units:          &MockUnitsService{},
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"github.com/codegangsta/cli"
	"io/ioutil"
	"net/http"
	"os"
)

const (
	NAGIOS_OK       int = 0
	NAGIOS_WARNING  int = 1
	NAGIOS_CRITICAL int = 2
	NAGIOS_UNKNOWN  int = 3
)

var exitCode int

func main() {

	app := cli.NewApp()
	app.Name = "check_hypersearch"
	app.Version = "1.0.0"
	app.Author = "Paul Swanson"
	app.Usage = "Search for text on a web page"
	app.Flags = []cli.Flag{
		cli.StringFlag{"require,r", "all", "Require 'all' or 'some'"},
		cli.BoolFlag{"verbose", "Be verbose; for debugging etc."},
	}

	cli.AppHelpTemplate = `NAME:
{{.Name}} - {{.Usage}}

USAGE:
{{.Name}} [global options] [arguments...]

For example, {{.Name}} --require some http://www.google.com/ "<title>Google</title>" "Privacy & Terms"

VERSION:
{{.Version}}

GLOBAL OPTIONS:
{{range .Flags}}{{.}}
{{end}}
`

	app.Action = func(c *cli.Context) {

		var requireAll, verbose bool

		if c.String("require") == "some" {
			requireAll = false
		} else {
			requireAll = true
		}

		if c.Bool("verbose") {
			verbose = true
		}

		args := c.Args()
		argCount := len(args)

		if argCount < 2 {
			cli.ShowAppHelp(c)
			exitCode = NAGIOS_UNKNOWN
			return
		}

		if verbose {
			println("Accessing", args[0])
		}
		resp, err := http.Get(args[0])
		if err != nil {
			println("Couldn't access that link!")
			exitCode = NAGIOS_UNKNOWN
			return
		}
		defer resp.Body.Close()

		if verbose {
			println("Reading page...")
		}
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			println("Couldn't read that page!")
			exitCode = NAGIOS_UNKNOWN
			return
		}

		var found int
		queryCount := argCount - 1

		for _, s := range args[1:] {
			if bytes.Contains(body, []byte(s)) {
				found++
				if verbose {
					println("Found:", s)
				}
			} else {
				if verbose {
					println("Not found:", s)
				}
			}
		}

		var statusMessage string

		switch {
		case queryCount == found:
			statusMessage = "OK."
			exitCode = NAGIOS_OK
		case found == 0 || requireAll:
			statusMessage = "FAIL."
			exitCode = NAGIOS_CRITICAL
		default:
			statusMessage = "Some OK."
			exitCode = NAGIOS_WARNING
		}

		fmt.Printf("Found %v of %v %v\n", found, queryCount, statusMessage)

		if verbose {
			println("Nagios exit code:", exitCode)
		}

	}

	app.Run(os.Args)

	os.Exit(exitCode)
}
<commit_msg>All error messages now appear in Nagios Status Information, using fmt.Printf().<commit_after>package main

import (
	"bytes"
	"fmt"
	"github.com/codegangsta/cli"
	"io/ioutil"
	"net/http"
	"os"
)

const (
	NAGIOS_OK       int = 0
	NAGIOS_WARNING  int = 1
	NAGIOS_CRITICAL int = 2
	NAGIOS_UNKNOWN  int = 3
)

var exitCode int

func main() {

	app := cli.NewApp()
	app.Name = "check_hypersearch"
	app.Version = "1.0.0"
	app.Author = "Paul Swanson"
	app.Usage = "Search for text on a web page"
	app.Flags = []cli.Flag{
		cli.StringFlag{"require,r", "all", "Require 'all' or 'some'"},
		cli.BoolFlag{"verbose", "Be verbose; for debugging etc."},
	}

	cli.AppHelpTemplate = `NAME:
{{.Name}} - {{.Usage}}

USAGE:
{{.Name}} [global options] [arguments...]

For example, {{.Name}} --require some http://www.google.com/ "<title>Google</title>" "Privacy & Terms"

VERSION:
{{.Version}}

GLOBAL OPTIONS:
{{range .Flags}}{{.}}
{{end}}
`

	app.Action = func(c *cli.Context) {

		var requireAll, verbose bool

		if c.String("require") == "some" {
			requireAll = false
		} else {
			requireAll = true
		}

		if c.Bool("verbose") {
			verbose = true
		}

		args := c.Args()
		argCount := len(args)

		if argCount < 2 {
			cli.ShowAppHelp(c)
			exitCode = NAGIOS_UNKNOWN
			return
		}

		if verbose {
			fmt.Printf("Accessing %v\n", args[0])
		}
		resp, err := http.Get(args[0])
		if err != nil {
			fmt.Printf("Couldn't access that link!\n")
			exitCode = NAGIOS_UNKNOWN
			return
		}
		defer resp.Body.Close()

		if verbose {
			fmt.Printf("Reading page...\n")
		}
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			fmt.Printf("Couldn't read that page!\n")
			exitCode = NAGIOS_UNKNOWN
			return
		}

		var found int
		queryCount := argCount - 1

		for _, s := range args[1:] {
			if bytes.Contains(body, []byte(s)) {
				found++
				if verbose {
					fmt.Printf("Found: %v\n", s)
				}
			} else {
				if verbose {
					fmt.Printf("Not found: %v\n", s)
				}
			}
		}

		var statusMessage string

		switch {
		case queryCount == found:
			statusMessage = "OK."
			exitCode = NAGIOS_OK
		case found == 0 || requireAll:
			statusMessage = "FAIL."
			exitCode = NAGIOS_CRITICAL
		default:
			statusMessage = "Some OK."
			exitCode = NAGIOS_WARNING
		}

		fmt.Printf("Found %v of %v %v\n", found, queryCount, statusMessage)

		if verbose {
			fmt.Printf("Nagios exit code: %v\n", exitCode)
		}

	}

	app.Run(os.Args)

	os.Exit(exitCode)
}
<|endoftext|>


<commit_before>// Copyright 2015 MongoDB, Inc. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package main

import (
	"./model"
	"./util"
	"flag"
	"fmt"
	"github.com/fractalcat/nagiosplugin"
	"os"
	"time"
)

const (
	CredFile = ".mongodb_mms"
)

var groupId string
var hostname string
var metricName string
var server string
var warning string
var critical string
var timeout int
var maxAge int

func main() {
	setupFlags()
	if hostname == "" || groupId == "" {
		flag.Usage()
		os.Exit(2)
		return
	}

	check := nagiosplugin.NewCheck()
	defer check.Finish()

	config, err := util.LoadConfigFromHome(CredFile)
	if err != nil {
		check.AddResultf(nagiosplugin.UNKNOWN, "%v", err)
		return
	}

	username, apikey := config.GetCredentials()
	api, err := util.NewMMSAPI(server, timeout, username, apikey)
	if err != nil {
		check.AddResultf(nagiosplugin.UNKNOWN, "Failed to create API. Error: %v", err)
		return
	}

	host, err := api.GetHostByName(groupId, hostname)
	if err != nil {
		check.AddResultf(nagiosplugin.UNKNOWN, "%v", err)
		return
	}

	if metricName == "" {
		doHostCheck(check, host)
	} else {
		doMetricCheck(check, api, host)
	}
}

func doHostCheck(check *nagiosplugin.Check, host *model.Host) {
	age := time.Since(host.LastPing)

	critRange, err := nagiosplugin.ParseRange(critical)
	if err != nil {
		check.AddResultf(nagiosplugin.UNKNOWN, "Error parsing critical range. Error: %v", err)
		return
	}

	if critRange.Check(age.Seconds()) {
		check.AddResultf(nagiosplugin.CRITICAL, fmt.Sprintf("Last ping was %v seconds ago", age.Seconds()))
		return
	}

	warnRange, err := nagiosplugin.ParseRange(warning)
	if err != nil {
		check.AddResultf(nagiosplugin.UNKNOWN, "Error parsing warning range. Error: %v", err)
		return
	}

	if warnRange.Check(age.Seconds()) {
		check.AddResultf(nagiosplugin.WARNING, fmt.Sprintf("Last ping was %v seconds ago", age.Seconds()))
		return
	}

	check.AddResultf(nagiosplugin.OK, fmt.Sprintf("Last ping was %v seconds ago", age.Seconds()))
}

func doMetricCheck(check *nagiosplugin.Check, api *util.MMSAPI, host *model.Host) {
	metric, err := api.GetHostMetric(groupId, host.Id, metricName)
	if err != nil {
		check.AddResultf(nagiosplugin.UNKNOWN, "%v", err)
		return
	}

	if len(metric.DataPoints) == 0 {
		check.AddResultf(nagiosplugin.UNKNOWN, "No data points found for %v", metricName)
		return
	}

	lastDataPoint := metric.DataPoints[len(metric.DataPoints)-1]
	age := time.Since(lastDataPoint.Timestamp)
	if int(age.Seconds()) > maxAge {
		check.AddResultf(nagiosplugin.CRITICAL, "Last data point for %v is %v seconds old.", metricName, int(age.Seconds()))
		return
	}

	check.AddPerfDatum(metricName, "", lastDataPoint.Value)

	critRange, err := nagiosplugin.ParseRange(critical)
	if err != nil {
		check.AddResultf(nagiosplugin.UNKNOWN, "Error parsing critical range. Error: %v", err)
		return
	}

	if critRange.Check(lastDataPoint.Value) {
		check.AddResultf(nagiosplugin.CRITICAL, metric.ToStringLastDataPoint())
		return
	}

	warnRange, err := nagiosplugin.ParseRange(warning)
	if err != nil {
		check.AddResultf(nagiosplugin.UNKNOWN, "Error parsing warning range. Error: %v", err)
		return
	}

	if warnRange.Check(lastDataPoint.Value) {
		check.AddResultf(nagiosplugin.WARNING, metric.ToStringLastDataPoint())
		return
	}

	check.AddResultf(nagiosplugin.OK, metric.ToStringLastDataPoint())
}

func setupFlags() {
	const (
		groupIdDefault  = ""
		groupIdUsage    = "The MMS/Ops Manager group ID that contains the server"
		hostnameDefault = ""
		hostnameUsage   = "hostname:port of the mongod/s to check"
		metricDefault   = ""
		metricUsage     = "metric to query"
		serverDefault   = "https://mms.mongodb.com"
		serverUsage     = "hostname and port of the MMS/Ops Manager service"
		warningDefault  = "~:" // considered negative infinity to positive infinity (https://nagios-plugins.org/doc/guidelines.html#THRESHOLDFORMAT)
		warningUsage    = "warning threshold for given metric"
		criticalDefault = "~:"
		criticalUsage   = "critical threshold for given metric"
		timeoutDefault  = 10
		timeoutUsage    = "connection timeout connecting MMS/Ops Manager service"
		maxAgeDefault   = 180
		maxAgeUsage     = "the maximum number of seconds old a metric before it is considerd stale"
	)

	flag.StringVar(&groupId, "groupid", groupIdDefault, groupIdUsage)
	flag.StringVar(&groupId, "g", groupIdDefault, groupIdUsage)

	flag.StringVar(&hostname, "hostname", hostnameDefault, hostnameUsage)
	flag.StringVar(&hostname, "H", hostnameDefault, hostnameUsage)

	flag.StringVar(&metricName, "metric", metricDefault, metricUsage)
	flag.StringVar(&metricName, "m", metricDefault, metricUsage)

	flag.IntVar(&maxAge, "maxage", maxAgeDefault, maxAgeUsage)
	flag.IntVar(&maxAge, "a", maxAgeDefault, maxAgeUsage)

	flag.StringVar(&server, "server", serverDefault, serverUsage)
	flag.StringVar(&server, "s", serverDefault, serverUsage)

	flag.StringVar(&warning, "warning", warningDefault, warningUsage)
	flag.StringVar(&warning, "w", warningDefault, warningUsage)

	flag.StringVar(&critical, "critical", criticalDefault, criticalUsage)
	flag.StringVar(&critical, "c", criticalDefault, criticalUsage)

	flag.IntVar(&timeout, "timeout", timeoutDefault, timeoutUsage)
	flag.IntVar(&timeout, "t", timeoutDefault, timeoutUsage)

	flag.Usage = func() {
		fmt.Fprintf(os.Stdout, "Usage: check_mongodb_mms  -g groupid -H hostname [-m metric] [-a age] [-s server] [-t timeout] [-w warning_level] [-c critica_level]\n")
		fmt.Fprintf(os.Stdout, "     -g, --groupid  %v\n", groupIdUsage)
		fmt.Fprintf(os.Stdout, "     -H, --hostname %v\n", hostnameUsage)
		fmt.Fprintf(os.Stdout, "     -m, --metric (no metric means check last ping age in seconds) %v\n", metricUsage)
		fmt.Fprintf(os.Stdout, "     -a, --maxage (default %v) %v\n", maxAgeDefault, maxAgeUsage)
		fmt.Fprintf(os.Stdout, "     -s, --server (default: %v) %v\n", serverDefault, serverUsage)
		fmt.Fprintf(os.Stdout, "     -w, --warning (default: %v) %v\n", warningDefault, warningUsage)
		fmt.Fprintf(os.Stdout, "     -c, --critical (default: %v) %v\n", criticalDefault, criticalUsage)
		fmt.Fprintf(os.Stdout, "     -t, --timeout (default: %v) %v\n", timeoutDefault, timeoutUsage)
		fmt.Fprintf(os.Stdout, "\n     -w and -c support the standard nagios threshold formats.\n"+
			"     See https://nagios-plugins.org/doc/guidelines.html#THRESHOLDFORMAT for more details.\n")
	}
	flag.Parse()
}
<commit_msg>move maxAge to 6 minutes to account for various processing delays in MMS<commit_after>// Copyright 2015 MongoDB, Inc. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package main

import (
	"./model"
	"./util"
	"flag"
	"fmt"
	"github.com/fractalcat/nagiosplugin"
	"os"
	"time"
)

const (
	CredFile = ".mongodb_mms"
)

var groupId string
var hostname string
var metricName string
var server string
var warning string
var critical string
var timeout int
var maxAge int

func main() {
	setupFlags()
	if hostname == "" || groupId == "" {
		flag.Usage()
		os.Exit(2)
		return
	}

	check := nagiosplugin.NewCheck()
	defer check.Finish()

	config, err := util.LoadConfigFromHome(CredFile)
	if err != nil {
		check.AddResultf(nagiosplugin.UNKNOWN, "%v", err)
		return
	}

	username, apikey := config.GetCredentials()
	api, err := util.NewMMSAPI(server, timeout, username, apikey)
	if err != nil {
		check.AddResultf(nagiosplugin.UNKNOWN, "Failed to create API. Error: %v", err)
		return
	}

	host, err := api.GetHostByName(groupId, hostname)
	if err != nil {
		check.AddResultf(nagiosplugin.UNKNOWN, "%v", err)
		return
	}

	if metricName == "" {
		doHostCheck(check, host)
	} else {
		doMetricCheck(check, api, host)
	}
}

func doHostCheck(check *nagiosplugin.Check, host *model.Host) {
	age := time.Since(host.LastPing)

	critRange, err := nagiosplugin.ParseRange(critical)
	if err != nil {
		check.AddResultf(nagiosplugin.UNKNOWN, "Error parsing critical range. Error: %v", err)
		return
	}

	if critRange.Check(age.Seconds()) {
		check.AddResultf(nagiosplugin.CRITICAL, fmt.Sprintf("Last ping was %v seconds ago", age.Seconds()))
		return
	}

	warnRange, err := nagiosplugin.ParseRange(warning)
	if err != nil {
		check.AddResultf(nagiosplugin.UNKNOWN, "Error parsing warning range. Error: %v", err)
		return
	}

	if warnRange.Check(age.Seconds()) {
		check.AddResultf(nagiosplugin.WARNING, fmt.Sprintf("Last ping was %v seconds ago", age.Seconds()))
		return
	}

	check.AddResultf(nagiosplugin.OK, fmt.Sprintf("Last ping was %v seconds ago", age.Seconds()))
}

func doMetricCheck(check *nagiosplugin.Check, api *util.MMSAPI, host *model.Host) {
	metric, err := api.GetHostMetric(groupId, host.Id, metricName)
	if err != nil {
		check.AddResultf(nagiosplugin.UNKNOWN, "%v", err)
		return
	}

	if len(metric.DataPoints) == 0 {
		check.AddResultf(nagiosplugin.UNKNOWN, "No data points found for %v", metricName)
		return
	}

	lastDataPoint := metric.DataPoints[len(metric.DataPoints)-1]
	age := time.Since(lastDataPoint.Timestamp)
	if int(age.Seconds()) > maxAge {
		check.AddResultf(nagiosplugin.CRITICAL, "Last data point for %v is %v seconds old.", metricName, int(age.Seconds()))
		return
	}

	check.AddPerfDatum(metricName, "", lastDataPoint.Value)

	critRange, err := nagiosplugin.ParseRange(critical)
	if err != nil {
		check.AddResultf(nagiosplugin.UNKNOWN, "Error parsing critical range. Error: %v", err)
		return
	}

	if critRange.Check(lastDataPoint.Value) {
		check.AddResultf(nagiosplugin.CRITICAL, metric.ToStringLastDataPoint())
		return
	}

	warnRange, err := nagiosplugin.ParseRange(warning)
	if err != nil {
		check.AddResultf(nagiosplugin.UNKNOWN, "Error parsing warning range. Error: %v", err)
		return
	}

	if warnRange.Check(lastDataPoint.Value) {
		check.AddResultf(nagiosplugin.WARNING, metric.ToStringLastDataPoint())
		return
	}

	check.AddResultf(nagiosplugin.OK, metric.ToStringLastDataPoint())
}

func setupFlags() {
	const (
		groupIdDefault  = ""
		groupIdUsage    = "The MMS/Ops Manager group ID that contains the server"
		hostnameDefault = ""
		hostnameUsage   = "hostname:port of the mongod/s to check"
		metricDefault   = ""
		metricUsage     = "metric to query"
		serverDefault   = "https://mms.mongodb.com"
		serverUsage     = "hostname and port of the MMS/Ops Manager service"
		warningDefault  = "~:" // considered negative infinity to positive infinity (https://nagios-plugins.org/doc/guidelines.html#THRESHOLDFORMAT)
		warningUsage    = "warning threshold for given metric"
		criticalDefault = "~:"
		criticalUsage   = "critical threshold for given metric"
		timeoutDefault  = 10
		timeoutUsage    = "connection timeout connecting MMS/Ops Manager service"
		maxAgeDefault   = 360
		maxAgeUsage     = "the maximum number of seconds old a metric before it is considerd stale"
	)

	flag.StringVar(&groupId, "groupid", groupIdDefault, groupIdUsage)
	flag.StringVar(&groupId, "g", groupIdDefault, groupIdUsage)

	flag.StringVar(&hostname, "hostname", hostnameDefault, hostnameUsage)
	flag.StringVar(&hostname, "H", hostnameDefault, hostnameUsage)

	flag.StringVar(&metricName, "metric", metricDefault, metricUsage)
	flag.StringVar(&metricName, "m", metricDefault, metricUsage)

	flag.IntVar(&maxAge, "maxage", maxAgeDefault, maxAgeUsage)
	flag.IntVar(&maxAge, "a", maxAgeDefault, maxAgeUsage)

	flag.StringVar(&server, "server", serverDefault, serverUsage)
	flag.StringVar(&server, "s", serverDefault, serverUsage)

	flag.StringVar(&warning, "warning", warningDefault, warningUsage)
	flag.StringVar(&warning, "w", warningDefault, warningUsage)

	flag.StringVar(&critical, "critical", criticalDefault, criticalUsage)
	flag.StringVar(&critical, "c", criticalDefault, criticalUsage)

	flag.IntVar(&timeout, "timeout", timeoutDefault, timeoutUsage)
	flag.IntVar(&timeout, "t", timeoutDefault, timeoutUsage)

	flag.Usage = func() {
		fmt.Fprintf(os.Stdout, "Usage: check_mongodb_mms  -g groupid -H hostname [-m metric] [-a age] [-s server] [-t timeout] [-w warning_level] [-c critica_level]\n")
		fmt.Fprintf(os.Stdout, "     -g, --groupid  %v\n", groupIdUsage)
		fmt.Fprintf(os.Stdout, "     -H, --hostname %v\n", hostnameUsage)
		fmt.Fprintf(os.Stdout, "     -m, --metric (no metric means check last ping age in seconds) %v\n", metricUsage)
		fmt.Fprintf(os.Stdout, "     -a, --maxage (default %v) %v\n", maxAgeDefault, maxAgeUsage)
		fmt.Fprintf(os.Stdout, "     -s, --server (default: %v) %v\n", serverDefault, serverUsage)
		fmt.Fprintf(os.Stdout, "     -w, --warning (default: %v) %v\n", warningDefault, warningUsage)
		fmt.Fprintf(os.Stdout, "     -c, --critical (default: %v) %v\n", criticalDefault, criticalUsage)
		fmt.Fprintf(os.Stdout, "     -t, --timeout (default: %v) %v\n", timeoutDefault, timeoutUsage)
		fmt.Fprintf(os.Stdout, "\n     -w and -c support the standard nagios threshold formats.\n"+
			"     See https://nagios-plugins.org/doc/guidelines.html#THRESHOLDFORMAT for more details.\n")
	}
	flag.Parse()
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	vlc "github.com/kestein/go-vlc"
	"html/template"
	"io"
	"log"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
)

const videoPath string = "/home/kestein/Videos"
const screenshotPath = "/home/kestein/Pictures/screenshots"
const referer string = "Referer"
const secToMilli int64 = 1000
const uninitializedSubs int = -1000
const pageStart string = `

`
const content string = `
			{{block "links" .}}
				{{range .}}

{{.Filename}}

				{{end}}
			{{end}}
`
const pageEnd string = `

`

type videoLine struct {
	Action, Url, Filename string
}

type cartoon struct {
	inst             *vlc.Instance
	player           *vlc.Player
	media            *vlc.Media
	vidLen           int64
	playing          bool
	startingSubTrack int
}

type videoLineList []videoLine

func (a videoLineList) Len() int           { return len(a) }
func (a videoLineList) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a videoLineList) Less(i, j int) bool { return a[i].Url = len(videoPath) {
		if fullPath[0:len(videoPath)] != videoPath {
			http.Error(w, "Not in the video path", http.StatusUnauthorized)
		}
	}
	dir, err := os.Open(fullPath)
	if err != nil {
		log.Fatal("Unable to open video path %s", err)
	}
	files, err := dir.Readdir(0)
	if err != nil {
		log.Fatal("Unable to read dir %s", err)
	}
	// Write the HTML. Sort out by directories and files
	subDirs := []videoLine{}
	orphanFiles := []videoLine{}
	for i := 0; i < len(files); i++ {
		// For printing
		fileName := files[i].Name()
		// For RESTing
		vidURL := ""
		if len(req.URL.String()) > 0 {
			vidURL = fmt.Sprintf("%s/%s", req.URL.String(), url.QueryEscape(fileName))
		} else {
			vidURL = fmt.Sprintf("%s", url.QueryEscape(fileName))
		}
		if files[i].IsDir() {
			url := videoLine{"list", vidURL, fileName}
			subDirs = append(subDirs, url)
		} else {
			url := videoLine{"play", vidURL, fileName}
			orphanFiles = append(orphanFiles, url)
		}
	}
	// Write HTML to the reponse
	sort.Sort(videoLineList(subDirs))
	// Make the '..' directory at the top
	prev := strings.Split(req.URL.String(), "/")
	back := ""
	if len(prev) > 1 {
		back = strings.Join(prev[:len(prev)-1], "/")
	}
	previousLink := []videoLine{videoLine{"list", back, ".."}}
	subDirs = append(previousLink, subDirs...)
	sort.Sort(videoLineList(orphanFiles))
	// Write the HTML
	io.WriteString(w, pageStart)
	t := template.Must(template.New("content").Parse(content))
	err = t.Execute(w, subDirs)
	if err != nil {
		panic(err)
	}
	err = t.Execute(w, orphanFiles)
	if err != nil {
		panic(err)
	}
	io.WriteString(w, pageEnd)
}

func play(state *cartoon, w http.ResponseWriter, req *http.Request) {
	var media *vlc.Media
	// var evt *vlc.EventManager
	var err error

	// Obtain the name of the video to view
	rawVideo := req.RequestURI[6:] // Take out the '/play'
	video, err := url.QueryUnescape(rawVideo)
	if err != nil {
		fmt.Println("WRONG")
		return
	}
	// Make VLC instance
	if state.inst, err = vlc.New([]string{}); err != nil {
		fmt.Println(err)
		return
	}
	// Open the video file
	if media, err = state.inst.OpenMediaFile(fmt.Sprintf("%s/%s", videoPath, video)); err != nil {
		fmt.Println(err)
		return
	}
	state.media = media
	// Create the media player
	if state.player, err = media.NewPlayer(); err != nil {
		fmt.Println(err)
		return
	}
	// Initialize player state
	state.player.SetVolume(25)
	//state.player.SetFullscreen(true)

	// Make the page to control the video
	state.player.Play()
	// Wait for the player to start playing
	for {
		t, err := state.player.Length()
		if err != nil {
			panic(err)
		}
		if t > 0 {
			break
		}
	}
	vidLen, err := state.player.Length()
	state.vidLen = vidLen
	vidLen = vidLen / secToMilli
	state.playing = true
	t := template.Must(template.New("player.html").ParseFiles("./player.html"))
	vals := map[string]int64{
		"vol":  25,
		"secs": vidLen,
	}
	t.Execute(w, vals)
	//io.WriteString(w, playerPage)
}

func stop(state *cartoon, w *http.ResponseWriter, req *http.Request) {
	if state.player == nil {
		return
	}
	state.player.Stop()
	state.playing = false
	closePlayer(state)
}

func closePlayer(state *cartoon) {
	if state.player == nil {
		return
	}
	state.player.Release()
	state.inst.Release()
	state.media.Release()
	state.player = nil
	state.inst = nil
	state.media = nil
	state.vidLen = 0
	state.startingSubTrack = uninitializedSubs
}

func pausePlay(state *cartoon, w *http.ResponseWriter) {
	if state.player == nil {
		return
	}
	// Replay the video
	if !state.player.WillPlay() {
		replay(state)
	} else {
		state.player.TogglePause(state.playing)
		state.playing = !state.playing
	}
	curTime := vidTime(state)
	io.WriteString(*w, fmt.Sprintf("%d", curTime/secToMilli))
}

func replay(state *cartoon) {
	var e error
	state.player.Release()
	state.player, e = state.media.NewPlayer()
	if e != nil {
		panic(e)
	}
	// re set the volume
	state.player.Play()
	state.playing = true
}

func rewind(state *cartoon) {
	if state.player == nil {
		return
	}
	var secsRewound int64 = 10
	curTime := vidTime(state)
	rTime := curTime - (secsRewound * secToMilli)
	if rTime < 0 {
		state.player.SetTime(0)
	} else {
		state.player.SetTime(rTime)
	}
}

/* Returns the time of the video in milliseconds */
func vidTime(state *cartoon) int64 {
	curTime, err := state.player.Time()
	if err != nil {
		panic(err)
	}
	return curTime
}

func setVolume(state *cartoon, toVol int) {
	if state.player == nil {
		return
	}
	state.player.SetVolume(toVol)
}

func setTime(state *cartoon, seek int64, w *http.ResponseWriter) {
	if state.player == nil {
		return
	}
	if !state.player.WillPlay() {
		replay(state)
	}
	state.player.SetTime(seek * secToMilli)
	curTime := vidTime(state)
	io.WriteString(*w, fmt.Sprintf("%d", curTime/secToMilli))
}

func screenshot(state *cartoon) {
	if state.player == nil {
		return
	}
	state.player.TakeSnapshot(screenshotPath, 0, 0, 0)
}

func toggleSubs(state *cartoon) {
	if state.player == nil {
		return
	}
	subTrack, err := state.player.SubTile()
	if err != nil {
		/* Video does not have subtitles. Do nothing */
		return
	}
	if subTrack != -1 {
		if state.startingSubTrack == uninitializedSubs {
			state.startingSubTrack = subTrack
		}
		state.player.SetSubtitle(-1)
	} else {
		state.player.SetSubtitle(state.startingSubTrack)
	}
}

func main() {
	port := flag.String("p", "8100", "port to serve on")
	directory := flag.String("d", ".", "the directory of static file to host")
	flag.Parse()

	state := cartoon{
		inst:             nil,
		player:           nil,
		media:            nil,
		playing:          false,
		vidLen:           0,
		startingSubTrack: uninitializedSubs,
	}
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, "list", 301)
	})
	http.Handle("/list/", http.StripPrefix("/list/", http.HandlerFunc(list)))
	http.Handle("/play/", http.StripPrefix("/play/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			play(&state, w, req)
		})))
	http.Handle("/playback/", http.StripPrefix("/playback/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			pausePlay(&state, &w)
		})))
	http.Handle("/rewind/", http.StripPrefix("/rewind/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			rewind(&state)
		})))
	http.Handle("/stop/", http.StripPrefix("/stop/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			stop(&state, &w, req)
		})))
	http.Handle("/screenshot/", http.StripPrefix("/screenshot/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			screenshot(&state)
		})))
	// If this API endpoint is called "seek" it breaks if you try to seek to 0.
	// WTF
	http.Handle("/time/", http.StripPrefix("/time/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			seekPos, err := strconv.ParseInt(req.URL.String(), 10, 64)
			if err != nil {
				fmt.Println("NaN")
				return
			}
			setTime(&state, seekPos, &w)
		})))
	http.Handle("/volume/", http.StripPrefix("/volume/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			toVol, err := strconv.ParseInt(req.URL.String(), 10, 64)
			if err != nil {
				fmt.Println("NaN")
				return
			}
			setVolume(&state, int(toVol))
		})))
	http.Handle("/subs/", http.StripPrefix("/subs/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			toggleSubs(&state)
		})))
	http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {})

	log.Printf("Serving %s on HTTP port: %s\n", *directory, *port)
	log.Fatal(http.ListenAndServe("localhost:"+*port, nil))
}
<commit_msg>Slightly better error handling<commit_after>package main

import (
	"flag"
	"fmt"
	vlc "github.com/kestein/go-vlc"
	"html/template"
	"io"
	"log"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
)

const videoPath string = "/home/kestein/Videos"
const screenshotPath = "/home/kestein/Pictures/screenshots"
const referer string = "Referer"
const initVolume int = 75
const secToMilli int64 = 1000
const uninitializedSubs int = -1000
const pageStart string = `

`
const content string = `
			{{block "links" .}}
				{{range .}}

{{.Filename}}

				{{end}}
			{{end}}
`
const pageEnd string = `

`

type videoLine struct {
	Action, Url, Filename string
}

type cartoon struct {
	inst             *vlc.Instance
	player           *vlc.Player
	media            *vlc.Media
	vidLen           int64
	playing          bool
	startingSubTrack int
}

type videoLineList []videoLine

func (a videoLineList) Len() int           { return len(a) }
func (a videoLineList) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a videoLineList) Less(i, j int) bool { return a[i].Url = len(videoPath) {
		if fullPath[0:len(videoPath)] != videoPath {
			http.Error(w, "Not in the video path", http.StatusUnauthorized)
		}
	}
	dir, err := os.Open(fullPath)
	if err != nil {
		log.Fatal("Unable to open video path %s", err)
	}
	files, err := dir.Readdir(0)
	if err != nil {
		log.Fatal("Unable to read dir %s", err)
	}
	// Write the HTML. Sort out by directories and files
	subDirs := []videoLine{}
	orphanFiles := []videoLine{}
	for i := 0; i < len(files); i++ {
		// For printing
		fileName := files[i].Name()
		// For RESTing
		vidURL := ""
		if len(req.URL.String()) > 0 {
			vidURL = fmt.Sprintf("%s/%s", req.URL.String(), url.QueryEscape(fileName))
		} else {
			vidURL = fmt.Sprintf("%s", url.QueryEscape(fileName))
		}
		if files[i].IsDir() {
			url := videoLine{"list", vidURL, fileName}
			subDirs = append(subDirs, url)
		} else {
			url := videoLine{"play", vidURL, fileName}
			orphanFiles = append(orphanFiles, url)
		}
	}
	// Write HTML to the reponse
	sort.Sort(videoLineList(subDirs))
	// Make the '..' directory at the top
	prev := strings.Split(req.URL.String(), "/")
	back := ""
	if len(prev) > 1 {
		back = strings.Join(prev[:len(prev)-1], "/")
	}
	previousLink := []videoLine{videoLine{"list", back, ".."}}
	subDirs = append(previousLink, subDirs...)
	sort.Sort(videoLineList(orphanFiles))
	// Write the HTML
	io.WriteString(w, pageStart)
	t := template.Must(template.New("content").Parse(content))
	err = t.Execute(w, subDirs)
	if err != nil {
		panic(err)
	}
	err = t.Execute(w, orphanFiles)
	if err != nil {
		panic(err)
	}
	io.WriteString(w, pageEnd)
}

func play(state *cartoon, w http.ResponseWriter, req *http.Request) {
	var media *vlc.Media
	// var evt *vlc.EventManager
	var err error

	// Obtain the name of the video to view
	rawVideo := req.RequestURI[6:] // Take out the '/play/'
	video, err := url.QueryUnescape(rawVideo)
	if err != nil {
		panic(err)
	}
	// Make VLC instance
	if state.inst, err = vlc.New([]string{}); err != nil {
		panic(err)
	}
	// Open the video file
	if media, err = state.inst.OpenMediaFile(fmt.Sprintf("%s/%s", videoPath, video)); err != nil {
		panic(err)
	}
	state.media = media
	// Create the media player
	if state.player, err = media.NewPlayer(); err != nil {
		panic(err)
	}
	// Initialize player state
	state.player.SetVolume(initVolume)
	//state.player.SetFullscreen(true)

	// Make the page to control the video
	state.player.Play()
	// Wait for the player to start playing
	for {
		t, err := state.player.Length()
		if err != nil {
			state.player.Stop()
			panic(err)
		}
		if t > 0 {
			break
		}
	}
	vidLen, err := state.player.Length()
	state.vidLen = vidLen
	vidLen = vidLen / secToMilli
	state.playing = true
	t := template.Must(template.New("player.html").ParseFiles("./player.html"))
	vals := map[string]int64{
		"vol":  int64(initVolume),
		"secs": vidLen,
	}
	t.Execute(w, vals)
	//io.WriteString(w, playerPage)
}

func stop(state *cartoon, w *http.ResponseWriter, req *http.Request) {
	if state.player == nil {
		return
	}
	state.player.Stop()
	state.playing = false
	closePlayer(state)
}

func closePlayer(state *cartoon) {
	if state.player == nil {
		return
	}
	state.player.Release()
	state.inst.Release()
	state.media.Release()
	state.player = nil
	state.inst = nil
	state.media = nil
	state.vidLen = 0
	state.startingSubTrack = uninitializedSubs
}

func pausePlay(state *cartoon, w *http.ResponseWriter) {
	if state.player == nil {
		return
	}
	// Replay the video
	if !state.player.WillPlay() {
		replay(state)
	} else {
		state.player.TogglePause(state.playing)
		state.playing = !state.playing
	}
	curTime := vidTime(state)
	io.WriteString(*w, fmt.Sprintf("%d", curTime/secToMilli))
}

func replay(state *cartoon) {
	var e error
	state.player.Release()
	state.player, e = state.media.NewPlayer()
	if e != nil {
		state.player.Stop()
		panic(e)
	}
	// re set the volume
	state.player.Play()
	state.playing = true
}

/* Rewind the video by 10 seconds */
func rewind(state *cartoon) {
	if state.player == nil {
		return
	}
	var secsRewound int64 = 10
	curTime := vidTime(state)
	rTime := curTime - (secsRewound * secToMilli)
	if rTime < 0 {
		state.player.SetTime(0)
	} else {
		state.player.SetTime(rTime)
	}
}

/* Returns the time of the video in milliseconds */
func vidTime(state *cartoon) int64 {
	curTime, err := state.player.Time()
	if err != nil {
		state.player.Stop()
		panic(err)
	}
	return curTime
}

func setVolume(state *cartoon, toVol int) {
	if state.player == nil {
		return
	}
	state.player.SetVolume(toVol)
}

func setTime(state *cartoon, seek int64, w *http.ResponseWriter) {
	if state.player == nil {
		return
	}
	if !state.player.WillPlay() {
		replay(state)
	}
	state.player.SetTime(seek * secToMilli)
	curTime := vidTime(state)
	io.WriteString(*w, fmt.Sprintf("%d", curTime/secToMilli))
}

func screenshot(state *cartoon) {
	if state.player == nil {
		return
	}
	state.player.TakeSnapshot(screenshotPath, 0, 0, 0)
}

func toggleSubs(state *cartoon) {
	if state.player == nil {
		return
	}
	subTrack, err := state.player.SubTile()
	if err != nil {
		/* Video does not have subtitles. Do nothing */
		return
	}
	if subTrack != -1 {
		if state.startingSubTrack == uninitializedSubs {
			state.startingSubTrack = subTrack
		}
		state.player.SetSubtitle(-1)
	} else {
		state.player.SetSubtitle(state.startingSubTrack)
	}
}

func main() {
	port := flag.String("p", "8100", "port to serve on")
	directory := flag.String("d", ".", "the directory of static file to host")
	flag.Parse()

	state := cartoon{
		inst:             nil,
		player:           nil,
		media:            nil,
		playing:          false,
		vidLen:           0,
		startingSubTrack: uninitializedSubs,
	}
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, "list", 301)
	})
	http.Handle("/list/", http.StripPrefix("/list/", http.HandlerFunc(list)))
	http.Handle("/play/", http.StripPrefix("/play/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			play(&state, w, req)
		})))
	http.Handle("/playback/", http.StripPrefix("/playback/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			pausePlay(&state, &w)
		})))
	http.Handle("/rewind/", http.StripPrefix("/rewind/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			rewind(&state)
		})))
	http.Handle("/stop/", http.StripPrefix("/stop/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			stop(&state, &w, req)
		})))
	http.Handle("/screenshot/", http.StripPrefix("/screenshot/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			screenshot(&state)
		})))
	// If this API endpoint is called "seek" it breaks if you try to seek to 0.
	// WTF
	http.Handle("/time/", http.StripPrefix("/time/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			seekPos, err := strconv.ParseInt(req.URL.String(), 10, 64)
			if err != nil {
				fmt.Println("NaN")
				return
			}
			setTime(&state, seekPos, &w)
		})))
	http.Handle("/volume/", http.StripPrefix("/volume/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			toVol, err := strconv.ParseInt(req.URL.String(), 10, 64)
			if err != nil {
				fmt.Println("NaN")
				return
			}
			setVolume(&state, int(toVol))
		})))
	http.Handle("/subs/", http.StripPrefix("/subs/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			toggleSubs(&state)
		})))
	http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {})

	log.Printf("Serving %s on HTTP port: %s\n", *directory, *port)
	log.Fatal(http.ListenAndServe("192.168.2.3:"+*port, nil))
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/YouROK/go-mpv/mpv"
	"html/template"
	"io"
	"log"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
)

const videoPath string = "/home/kestein/Videos"
const screenshotPath = "/home/kestein/Pictures/screenshots"
const referer string = "Referer"
const initVolume int = 75
const secToMilli int64 = 1000
const uninitializedSubs int = -1000
const pageStart string = `

`
const content string = `
			{{block "links" .}}
				{{range .}}

{{.Filename}}

				{{end}}
			{{end}}
`
const pageEnd string = `

`

type videoLine struct {
	Action, Url, Filename string
}

type cartoon struct {
	player *mpv.Mpv
	video  string
	vidLen int64
	paused bool
	subbed bool
}

type videoLineList []videoLine

func (a videoLineList) Len() int           { return len(a) }
func (a videoLineList) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a videoLineList) Less(i, j int) bool { return a[i].Url = len(videoPath) {
		if fullPath[0:len(videoPath)] != videoPath {
			http.Error(w, "Not in the video path", http.StatusUnauthorized)
		}
	}
	dir, err := os.Open(fullPath)
	if err != nil {
		log.Fatal("Unable to open video path %s", err)
	}
	files, err := dir.Readdir(0)
	if err != nil {
		log.Fatal("Unable to read dir %s", err)
	}
	// Write the HTML. Sort out by directories and files
	subDirs := []videoLine{}
	orphanFiles := []videoLine{}
	for i := 0; i < len(files); i++ {
		// For printing
		fileName := files[i].Name()
		// For RESTing
		vidURL := ""
		if len(req.URL.String()) > 0 {
			vidURL = fmt.Sprintf("%s/%s", req.URL.String(), url.QueryEscape(fileName))
		} else {
			vidURL = fmt.Sprintf("%s", url.QueryEscape(fileName))
		}
		if files[i].IsDir() {
			url := videoLine{"list", vidURL, fileName}
			subDirs = append(subDirs, url)
		} else {
			url := videoLine{"play", vidURL, fileName}
			orphanFiles = append(orphanFiles, url)
		}
	}
	// Write HTML to the reponse
	sort.Sort(videoLineList(subDirs))
	// Make the '..' directory at the top
	prev := strings.Split(req.URL.String(), "/")
	back := ""
	if len(prev) > 1 {
		back = strings.Join(prev[:len(prev)-1], "/")
	}
	previousLink := []videoLine{videoLine{"list", back, ".."}}
	subDirs = append(previousLink, subDirs...)
	sort.Sort(videoLineList(orphanFiles))
	// Write the HTML
	io.WriteString(w, pageStart)
	t := template.Must(template.New("content").Parse(content))
	err = t.Execute(w, subDirs)
	if err != nil {
		panic(err)
	}
	err = t.Execute(w, orphanFiles)
	if err != nil {
		panic(err)
	}
	io.WriteString(w, pageEnd)
}

func play(state *cartoon, w http.ResponseWriter, req *http.Request) {
	var err error

	// Obtain the name of the video to view
	rawVideo := req.RequestURI[6:] // Take out the '/play/'
	video, err := url.QueryUnescape(rawVideo)
	if err != nil {
		panic(err)
	}
	// Start the video
	startPlay(state, video)
	state.video = video
	t := template.Must(template.New("player.html").ParseFiles("./player.html"))
	vals := map[string]int64{
		"vol":  int64(75),
		"secs": state.vidLen,
	}
	t.Execute(w, vals)
	//io.WriteString(w, playerPage)
}

func startPlay(state *cartoon, video string) {
	var player *mpv.Mpv = mpv.Create()
	go func() {
		for {
			e := player.WaitEvent(1)
			if e.Event_Id == mpv.EVENT_END_FILE {
				stop(state)
				closePlayer(state)
				break
			}
		}
	}()
	// Options ricing
	player.SetOptionString("fullscreen", "yes")
	player.SetOptionString("screenshot-format", "png")
	player.SetOptionString("screenshot-png-compression", "5")
	player.SetOptionString("screenshot-png-filter", "0")
	player.SetOptionString("screenshot-tag-colorspace", "yes")
	player.SetOptionString("screenshot-high-bit-depth", "yes")
	player.SetOptionString("slang", "eng,en,enUS,en-US")
	player.SetOptionString("alang", "jpn,jp,eng,en,enUS,en-US")
	err := player.Initialize()
	if err != nil {
		fmt.Println("failed to initialize ", err)
		panic("")
	}
	err = player.Command([]string{"loadfile", fmt.Sprintf("%s/%s", videoPath, video)})
	if err != nil {
		fmt.Println("failed to loadfile ", err)
		panic("")
	}
	state.player = player
	state.vidLen = vidLen(state)
	state.paused = false
	state.subbed = true
}

func stop(state *cartoon) {
	if state.player == nil {
		return
	}
	err := state.player.Command([]string{"stop"})
	if err != nil {
		fmt.Println("unable to stop ", err)
		panic("")
	}
	state.paused = true
}

func closePlayer(state *cartoon) {
	if state.player == nil {
		return
	}
	state.player.TerminateDestroy()
	state.player = nil
	state.vidLen = 0
}

func pausePlay(state *cartoon, w *http.ResponseWriter) {
	var err error
	if state.player == nil && len(state.video) > 0 {
		startPlay(state, state.video)
		io.WriteString(*w, fmt.Sprintf("%d", 0))
		return
	}
	if state.paused {
		err = state.player.SetOptionString("pause", "no")
	} else {
		err = state.player.SetOptionString("pause", "yes")
	}
	if err != nil {
		fmt.Println("problem toggling play ", err)
		panic("")
	}
	state.paused = !state.paused
	if w != nil {
		io.WriteString(*w, fmt.Sprintf("%d", vidTime(state)))
	}
}

/* Rewind the video by 10 seconds */
func rewind(state *cartoon) {
	var err error
	if state.player == nil {
		return
	}
	var secsRewound int64 = 5
	curTime := vidTime(state)
	rTime := curTime - secsRewound
	if rTime < 0 {
		err = state.player.Command([]string{"seek", "0", "absolute"})
	} else {
		err = state.player.Command([]string{"seek", "-5", "relative"})
	}
	if err != nil {
		fmt.Println("unable to seek ", err)
		panic("")
	}
}

/* Returns the time of the video in seconds */
func vidTime(state *cartoon) int64 {
	var vidTime int64
	for {
		t, _ := state.player.GetProperty("playback-time", mpv.FORMAT_INT64)
		if t != nil {
			vidTime = t.(int64)
			break
		}
	}
	return vidTime
}

func vidLen(state *cartoon) int64 {
	var vidLen int64
	// Wait for the video to initialize
	for {
		len, _ := state.player.GetProperty("duration", mpv.FORMAT_INT64)
		if len != nil {
			vidLen = len.(int64)
			break
		}
	}
	return vidLen
}

/*
func setVolume(state *cartoon, toVol int) {
	if state.player == nil {
		return
	}
	//state.player.SetVolume(toVol)
}
*/
func setTime(state *cartoon, seek int64, w *http.ResponseWriter) {
	if state.player == nil {
		startPlay(state, state.video)
	}
	err := state.player.Command([]string{"seek", strconv.FormatInt(seek, 10), "absolute"})
	if err != nil {
		fmt.Println("Unable to set the time ", err)
		panic("")
	}
	curTime := vidTime(state)
	if w != nil {
		io.WriteString(*w, fmt.Sprintf("%d", curTime))
	}
}

func screenshot(state *cartoon) {
	if state.player == nil {
		return
	}
	videoName := state.player.GetPropertyString("filename/no-ext")
	err := state.player.Command([]string{"screenshot-to-file", fmt.Sprintf("%s/%s-%d.png", screenshotPath, videoName, vidTime(state))})
	if err != nil {
		fmt.Println("Unable to take screenshot ", err)
		panic("")
	}
}

func toggleSubs(state *cartoon) {
	var err error
	if state.player == nil {
		return
	}
	if state.subbed {
		err = state.player.SetOptionString("sub-visibility", "no")
	} else {
		err = state.player.SetOptionString("sub-visibility", "yes")
	}
	if err != nil {
		fmt.Println("Unable to toggle subs ", err)
		panic("")
	}
	state.subbed = !state.subbed
}

func main() {
	port := flag.String("p", "8100", "port to serve on")
	directory := flag.String("d", ".", "the directory of static file to host")
	flag.Parse()

	state := cartoon{
		player: nil,
		video:  "",
		vidLen: 0,
		paused: true,
		subbed: true,
	}
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, "list", 301)
	})
	http.Handle("/list/", http.StripPrefix("/list/", http.HandlerFunc(list)))
	http.Handle("/play/", http.StripPrefix("/play/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			/* Sometimes the unload event does not trigger on mobile.
			   Ensure that the other player has stopped. */
			if state.player != nil {
				// Seek to the end of the video and let the goroutine clean up the previous player
				setTime(&state, state.vidLen, nil)
				for {
					if state.player == nil {
						break
					}
				}
			}
			play(&state, w, req)
		})))
	http.Handle("/playback/", http.StripPrefix("/playback/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			pausePlay(&state, &w)
		})))
	http.Handle("/rewind/", http.StripPrefix("/rewind/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			rewind(&state)
		})))
	http.Handle("/stop/", http.StripPrefix("/stop/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			if state.paused {
				pausePlay(&state, nil)
			}
			// Seek to the end of the video and let the goroutine clean up the previous player
			setTime(&state, state.vidLen, nil)
		})))
	http.Handle("/screenshot/", http.StripPrefix("/screenshot/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			screenshot(&state)
		})))
	// If this API endpoint is called "seek" it breaks if you try to seek to 0.
	// WTF
	http.Handle("/time/", http.StripPrefix("/time/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			seekPos, err := strconv.ParseInt(req.URL.String(), 10, 64)
			if err != nil {
				fmt.Println("NaN")
				return
			}
			setTime(&state, seekPos, &w)
		})))
	http.Handle("/volume/", http.StripPrefix("/volume/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			/*toVol, err := strconv.ParseInt(req.URL.String(), 10, 64)
			if err != nil {
				fmt.Println("NaN")
				return
			}
			setVolume(&state, int(toVol))
			*/
		})))
	http.Handle("/subs/", http.StripPrefix("/subs/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			toggleSubs(&state)
		})))
	http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {})

	log.Printf("Serving %s on HTTP port: %s\n", *directory, *port)
	log.Fatal(http.ListenAndServe("192.168.2.8:"+*port, nil))
}
<commit_msg>Unique screenshot names so stitches won't overwrite one another<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/YouROK/go-mpv/mpv"
	"html/template"
	"io"
	"log"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
)

const videoPath string = "/home/kestein/Videos"
const screenshotPath = "/home/kestein/Pictures/screenshots"
const referer string = "Referer"
const initVolume int = 75
const secToMilli int64 = 1000
const uninitializedSubs int = -1000
const pageStart string = `

`
const content string = `
			{{block "links" .}}
				{{range .}}

{{.Filename}}

				{{end}}
			{{end}}
`
const pageEnd string = `

`

type videoLine struct {
	Action, Url, Filename string
}

type cartoon struct {
	player *mpv.Mpv
	video  string
	vidLen int64
	paused bool
	subbed bool
}

type videoLineList []videoLine

func (a videoLineList) Len() int           { return len(a) }
func (a videoLineList) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a videoLineList) Less(i, j int) bool { return a[i].Url = len(videoPath) {
		if fullPath[0:len(videoPath)] != videoPath {
			http.Error(w, "Not in the video path", http.StatusUnauthorized)
		}
	}
	dir, err := os.Open(fullPath)
	if err != nil {
		log.Fatal("Unable to open video path %s", err)
	}
	files, err := dir.Readdir(0)
	if err != nil {
		log.Fatal("Unable to read dir %s", err)
	}
	// Write the HTML. Sort out by directories and files
	subDirs := []videoLine{}
	orphanFiles := []videoLine{}
	for i := 0; i < len(files); i++ {
		// For printing
		fileName := files[i].Name()
		// For RESTing
		vidURL := ""
		if len(req.URL.String()) > 0 {
			vidURL = fmt.Sprintf("%s/%s", req.URL.String(), url.QueryEscape(fileName))
		} else {
			vidURL = fmt.Sprintf("%s", url.QueryEscape(fileName))
		}
		if files[i].IsDir() {
			url := videoLine{"list", vidURL, fileName}
			subDirs = append(subDirs, url)
		} else {
			url := videoLine{"play", vidURL, fileName}
			orphanFiles = append(orphanFiles, url)
		}
	}
	// Write HTML to the reponse
	sort.Sort(videoLineList(subDirs))
	// Make the '..' directory at the top
	prev := strings.Split(req.URL.String(), "/")
	back := ""
	if len(prev) > 1 {
		back = strings.Join(prev[:len(prev)-1], "/")
	}
	previousLink := []videoLine{videoLine{"list", back, ".."}}
	subDirs = append(previousLink, subDirs...)
	sort.Sort(videoLineList(orphanFiles))
	// Write the HTML
	io.WriteString(w, pageStart)
	t := template.Must(template.New("content").Parse(content))
	err = t.Execute(w, subDirs)
	if err != nil {
		panic(err)
	}
	err = t.Execute(w, orphanFiles)
	if err != nil {
		panic(err)
	}
	io.WriteString(w, pageEnd)
}

func play(state *cartoon, w http.ResponseWriter, req *http.Request) {
	var err error

	// Obtain the name of the video to view
	rawVideo := req.RequestURI[6:] // Take out the '/play/'
	video, err := url.QueryUnescape(rawVideo)
	if err != nil {
		panic(err)
	}
	// Start the video
	startPlay(state, video)
	state.video = video
	t := template.Must(template.New("player.html").ParseFiles("./player.html"))
	vals := map[string]int64{
		"vol":  int64(75),
		"secs": state.vidLen,
	}
	t.Execute(w, vals)
	//io.WriteString(w, playerPage)
}

func startPlay(state *cartoon, video string) {
	var player *mpv.Mpv = mpv.Create()
	go func() {
		for {
			e := player.WaitEvent(1)
			if e.Event_Id == mpv.EVENT_END_FILE {
				stop(state)
				closePlayer(state)
				break
			}
		}
	}()
	// Options ricing
	player.SetOptionString("fullscreen", "yes")
	player.SetOptionString("screenshot-format", "png")
	player.SetOptionString("screenshot-directory", screenshotPath)
	player.SetOptionString("screenshot-template", "%F-%P-%n")
	player.SetOptionString("screenshot-png-compression", "0")
	player.SetOptionString("screenshot-png-filter", "0")
	player.SetOptionString("screenshot-tag-colorspace", "yes")
	player.SetOptionString("screenshot-high-bit-depth", "yes")
	player.SetOptionString("slang", "eng,en,enUS,en-US")
	player.SetOptionString("alang", "jpn,jp,eng,en,enUS,en-US")
	err := player.Initialize()
	if err != nil {
		fmt.Println("failed to initialize ", err)
		panic("")
	}
	err = player.Command([]string{"loadfile", fmt.Sprintf("%s/%s", videoPath, video)})
	if err != nil {
		fmt.Println("failed to loadfile ", err)
		panic("")
	}
	state.player = player
	state.vidLen = vidLen(state)
	state.paused = false
	state.subbed = true
}

func stop(state *cartoon) {
	if state.player == nil {
		return
	}
	err := state.player.Command([]string{"stop"})
	if err != nil {
		fmt.Println("unable to stop ", err)
		panic("")
	}
	state.paused = true
}

func closePlayer(state *cartoon) {
	if state.player == nil {
		return
	}
	state.player.TerminateDestroy()
	state.player = nil
	state.vidLen = 0
}

func pausePlay(state *cartoon, w *http.ResponseWriter) {
	var err error
	if state.player == nil && len(state.video) > 0 {
		startPlay(state, state.video)
		io.WriteString(*w, fmt.Sprintf("%d", 0))
		return
	}
	if state.paused {
		err = state.player.SetOptionString("pause", "no")
	} else {
		err = state.player.SetOptionString("pause", "yes")
	}
	if err != nil {
		fmt.Println("problem toggling play ", err)
		panic("")
	}
	state.paused = !state.paused
	if w != nil {
		io.WriteString(*w, fmt.Sprintf("%d", vidTime(state)))
	}
}

/* Rewind the video by 10 seconds */
func rewind(state *cartoon) {
	var err error
	if state.player == nil {
		return
	}
	var secsRewound int64 = 5
	curTime := vidTime(state)
	rTime := curTime - secsRewound
	if rTime < 0 {
		err = state.player.Command([]string{"seek", "0", "absolute"})
	} else {
		err = state.player.Command([]string{"seek", "-5", "relative"})
	}
	if err != nil {
		fmt.Println("unable to seek ", err)
		panic("")
	}
}

/* Returns the time of the video in seconds */
func vidTime(state *cartoon) int64 {
	var vidTime int64
	for {
		t, _ := state.player.GetProperty("playback-time", mpv.FORMAT_INT64)
		if t != nil {
			vidTime = t.(int64)
			break
		}
	}
	return vidTime
}

func vidLen(state *cartoon) int64 {
	var vidLen int64
	// Wait for the video to initialize
	for {
		len, _ := state.player.GetProperty("duration", mpv.FORMAT_INT64)
		if len != nil {
			vidLen = len.(int64)
			break
		}
	}
	return vidLen
}

/*
func setVolume(state *cartoon, toVol int) {
	if state.player == nil {
		return
	}
	//state.player.SetVolume(toVol)
}
*/
func setTime(state *cartoon, seek int64, w *http.ResponseWriter) {
	if state.player == nil {
		startPlay(state, state.video)
	}
	err := state.player.Command([]string{"seek", strconv.FormatInt(seek, 10), "absolute"})
	if err != nil {
		fmt.Println("Unable to set the time ", err)
		panic("")
	}
	curTime := vidTime(state)
	if w != nil {
		io.WriteString(*w, fmt.Sprintf("%d", curTime))
	}
}

func screenshot(state *cartoon) {
	if state.player == nil {
		return
	}
	err := state.player.Command([]string{"screenshot"})
	if err != nil {
		fmt.Println("Unable to take screenshot ", err)
		panic("")
	}
}

func toggleSubs(state *cartoon) {
	var err error
	if state.player == nil {
		return
	}
	if state.subbed {
		err = state.player.SetOptionString("sub-visibility", "no")
	} else {
		err = state.player.SetOptionString("sub-visibility", "yes")
	}
	if err != nil {
		fmt.Println("Unable to toggle subs ", err)
		panic("")
	}
	state.subbed = !state.subbed
}

func main() {
	port := flag.String("p", "8100", "port to serve on")
	directory := flag.String("d", ".", "the directory of static file to host")
	flag.Parse()

	state := cartoon{
		player: nil,
		video:  "",
		vidLen: 0,
		paused: true,
		subbed: true,
	}
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, "list", 301)
	})
	http.Handle("/list/", http.StripPrefix("/list/", http.HandlerFunc(list)))
	http.Handle("/play/", http.StripPrefix("/play/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			/* Sometimes the unload event does not trigger on mobile.
			   Ensure that the other player has stopped. */
			if state.player != nil {
				// Seek to the end of the video and let the goroutine clean up the previous player
				setTime(&state, state.vidLen, nil)
				for {
					if state.player == nil {
						break
					}
				}
			}
			play(&state, w, req)
		})))
	http.Handle("/playback/", http.StripPrefix("/playback/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			pausePlay(&state, &w)
		})))
	http.Handle("/rewind/", http.StripPrefix("/rewind/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			rewind(&state)
		})))
	http.Handle("/stop/", http.StripPrefix("/stop/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			if state.paused {
				pausePlay(&state, nil)
			}
			// Seek to the end of the video and let the goroutine clean up the previous player
			setTime(&state, state.vidLen, nil)
		})))
	http.Handle("/screenshot/", http.StripPrefix("/screenshot/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			screenshot(&state)
		})))
	// If this API endpoint is called "seek" it breaks if you try to seek to 0.
	// WTF
	http.Handle("/time/", http.StripPrefix("/time/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			seekPos, err := strconv.ParseInt(req.URL.String(), 10, 64)
			if err != nil {
				fmt.Println("NaN")
				return
			}
			setTime(&state, seekPos, &w)
		})))
	http.Handle("/volume/", http.StripPrefix("/volume/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			/*toVol, err := strconv.ParseInt(req.URL.String(), 10, 64)
			if err != nil {
				fmt.Println("NaN")
				return
			}
			setVolume(&state, int(toVol))
			*/
		})))
	http.Handle("/subs/", http.StripPrefix("/subs/",
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			toggleSubs(&state)
		})))
	http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {})

	log.Printf("Serving %s on HTTP port: %s\n", *directory, *port)
	log.Fatal(http.ListenAndServe("192.168.2.8:"+*port, nil))
}
<|endoftext|>


<commit_before>// Copyright 2013 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
Ls reads the directories in the command line and prints out the names.

The options are:
	-l		Long form.
	-r		raw (%v) form
	-R		recurse
*/

package main

import (
	"flag"
	"fmt"
	"os"
	"path/filepath"
)

var (
	long      = flag.Bool("l", false, "Long form")
	raw       = flag.Bool("r", false, "raw struct")
	recursive = flag.Bool("R", false, "Recurse")
)

func show(fi os.FileInfo) {
	switch {
	case *raw == true:
		fmt.Printf("%v\n", fi)
	case *long == false:
		fmt.Printf("%v\n", fi.Name())
	// -rw-r--r-- 1 root root 174 Aug 18 17:18 /etc/hosts
	case *long == true:
		fmt.Printf("%v\t%v\t%v\t%v\n", fi.Mode(), fi.Size(), fi.Name(), fi.ModTime())
	}

}

func main() {
	flag.Parse()

	dirs := flag.Args()

	if len(dirs) == 0 {
		dirs = []string{"."}
	}
	for _, v := range dirs {
		if len(dirs) > 1 {
			fmt.Printf("%v:\n", v)
		}
		err := filepath.Walk(v, func(path string, fi os.FileInfo, err error) error {
			show(fi)
			if fi.IsDir() && !*recursive && path != v {
				return filepath.SkipDir
			}

			return err
		})
		if err != nil {
			fmt.Printf("%s: %v\n", v, err)
		}
	}
}
<commit_msg>show symlinks<commit_after>// Copyright 2013 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
Ls reads the directories in the command line and prints out the names.

The options are:
	-l		Long form.
	-r		raw (%v) form
	-R		recurse
*/

package main

import (
	"flag"
	"fmt"
	"os"
	"path/filepath"
)

var (
	long      = flag.Bool("l", false, "Long form")
	raw       = flag.Bool("r", false, "raw struct")
	recursive = flag.Bool("R", false, "Recurse")
)

func show(fullpath string, fi os.FileInfo) {
	switch {
	case *raw == true:
		fmt.Printf("%v\n", fi)
	case *long == false:
		fmt.Printf("%v\n", fi.Name())
	// -rw-r--r-- 1 root root 174 Aug 18 17:18 /etc/hosts
	case *long == true:
		fmt.Printf("%v\t%v\t%v\t%v", fi.Mode(), fi.Size(), fi.Name(), fi.ModTime())
		if link, err := os.Readlink(fullpath); err == nil {
			fmt.Printf(" -> %v", link)
		}
		fmt.Printf("\n")
	}

}

func main() {
	flag.Parse()

	dirs := flag.Args()

	if len(dirs) == 0 {
		dirs = []string{"."}
	}
	for _, v := range dirs {
		if len(dirs) > 1 {
			fmt.Printf("%v:\n", v)
		}
		err := filepath.Walk(v, func(path string, fi os.FileInfo, err error) error {
			show(path, fi)
			if fi.IsDir() && !*recursive && path != v {
				return filepath.SkipDir
			}

			return err
		})
		if err != nil {
			fmt.Printf("%s: %v\n", v, err)
		}
	}
}
<|endoftext|>