<commit_before>package main

import (
	"fmt"

	"github.com/spf13/cobra"

	// Used by cgo
	_ "github.com/lxc/lxd/lxd/include"
)

/*
#ifndef _GNU_SOURCE
#define _GNU_SOURCE 1
#endif
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <limits.h>

#include "include/macro.h"
#include "include/memory_utils.h"
#include "include/syscall_wrappers.h"
#include <lxc/attach_options.h>
#include <lxc/lxccontainer.h>

extern char *advance_arg(bool required);

static bool write_nointr(int fd, const void *buf, size_t count)
{
	ssize_t ret;

	do {
		ret = write(fd, buf, count);
	} while (ret < 0 && errno == EINTR);

	if (ret < 0)
		return false;

	return (size_t)ret == count;
}

define_cleanup_function(struct lxc_container *, lxc_container_put);

static int wait_for_pid_status_nointr(pid_t pid)
{
	int status, ret;

again:
	ret = waitpid(pid, &status, 0);
	if (ret == -1) {
		if (errno == EINTR)
			goto again;

		return -1;
	}

	if (ret != pid)
		goto again;

	return status;
}

static int append_null_to_list(void ***list)
{
	int newentry = 0;
	void **new_list;

	if (*list)
		for (; (*list)[newentry]; newentry++)
			;

	new_list = realloc(*list, (newentry + 2) * sizeof(void **));
	if (!new_list)
		return ret_errno(ENOMEM);

	*list = new_list;
	(*list)[newentry + 1] = NULL;
	return newentry;
}

static int push_vargs(char ***list, char *entry)
{
	__do_free char *copy = NULL;
	int newentry;

	copy = strdup(entry);
	if (!copy)
		return ret_errno(ENOMEM);

	newentry = append_null_to_list((void ***)list);
	if (newentry < 0)
		return newentry;

	(*list)[newentry] = move_ptr(copy);

	return 0;
}

static int fd_cloexec(int fd, bool cloexec)
{
	int oflags, nflags;

	oflags = fcntl(fd, F_GETFD, 0);
	if (oflags < 0)
		return -errno;

	if (cloexec)
		nflags = oflags | FD_CLOEXEC;
	else
		nflags = oflags & ~FD_CLOEXEC;

	if (nflags == oflags)
		return 0;

	if (fcntl(fd, F_SETFD, nflags) < 0)
		return -errno;

	return 0;
}

static int safe_int(const char *numstr, int *converted)
{
	char *err = NULL;
	signed long int sli;

	errno = 0;
	sli = strtol(numstr, &err, 0);
	if (errno == ERANGE && (sli == LONG_MAX || sli == LONG_MIN))
		return -ERANGE;

	if (errno != 0 && sli == 0)
		return -EINVAL;

	if (err == numstr || *err != '\0')
		return -EINVAL;

	if (sli > INT_MAX || sli < INT_MIN)
		return -ERANGE;

	*converted = (int)sli;
	return 0;
}

static inline bool match_stdfds(int fd)
{
	return (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO);
}

static int close_inherited(int *fds_to_ignore, size_t len_fds)
{
	int fddir;
	DIR *dir;
	struct dirent *direntp;

restart:
	dir = opendir("/proc/self/fd");
	if (!dir)
		return -errno;

	fddir = dirfd(dir);

	while ((direntp = readdir(dir))) {
		int fd, ret;
		size_t i;

		if (strcmp(direntp->d_name, ".") == 0)
			continue;

		if (strcmp(direntp->d_name, "..") == 0)
			continue;

		ret = safe_int(direntp->d_name, &fd);
		if (ret < 0)
			continue;

		for (i = 0; i < len_fds; i++)
			if (fds_to_ignore[i] == fd)
				break;

		if (fd == fddir || (i < len_fds && fd == fds_to_ignore[i]))
			continue;

		if (match_stdfds(fd))
			continue;

		if (close(fd)) {
			return log_error(-errno, "%s - Failed to close file descriptor %d", strerror(errno), fd);
		} else {
			char fdpath[PATH_MAX], realpath[PATH_MAX];

			snprintf(fdpath, sizeof(fdpath), "/proc/self/fd/%d", fd);
			ret = readlink(fdpath, realpath, PATH_MAX);
			if (ret < 0)
				snprintf(realpath, sizeof(realpath), "unknown");
			else if (ret >= sizeof(realpath))
				realpath[sizeof(realpath) - 1] = '\0';

			log_error(-errno, "Closing unexpected file descriptor %d -> %s", fd, realpath);
		}

		closedir(dir);
		goto restart;
	}

	closedir(dir);
	return 0;
}

#define EXEC_STDIN_FD 3
#define EXEC_STDOUT_FD 4
#define EXEC_STDERR_FD 5
#define EXEC_PIPE_FD 6
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))

// We use a separate function because cleanup macros are called during stack
// unwinding if I'm not mistaken and if the compiler knows it exits it won't
// call them. That's not a problem since we're exiting but I just like to be on
// the safe side in case we ever call this from a different context. We also
// tell the compiler to not inline us.
__attribute__ ((noinline)) static int __forkexec(void)
{
	__do_close int status_pipe = EXEC_PIPE_FD;
	__do_free_string_list char **argvp = NULL, **envvp = NULL;
	call_cleaner(lxc_container_put) struct lxc_container *c = NULL;
	const char *config_path = NULL, *lxcpath = NULL, *name = NULL;
	char *cwd = NULL;
	lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT;
	lxc_attach_command_t command = {
		.program = NULL,
	};
	int fds_to_ignore[] = {EXEC_STDIN_FD, EXEC_STDOUT_FD, EXEC_STDERR_FD, EXEC_PIPE_FD};
	int ret;
	pid_t pid;
	uid_t uid;
	gid_t gid;

	if (geteuid() != 0)
		return log_error(EXIT_FAILURE, "Error: forkexec requires root privileges");

	name = advance_arg(false);
	if (name == NULL ||
	    (strcmp(name, "--help") == 0 ||
	     strcmp(name, "--version") == 0 || strcmp(name, "-h") == 0))
		return 0;

	lxcpath = advance_arg(true);
	config_path = advance_arg(true);
	cwd = advance_arg(true);
	uid = atoi(advance_arg(true));
	if (uid < 0)
		uid = (uid_t) - 1;
	gid = atoi(advance_arg(true));
	if (gid < 0)
		gid = (gid_t) - 1;

	for (char *arg = NULL, *section = NULL; (arg = advance_arg(false)); ) {
		if (!strcmp(arg, "--") && (!section || strcmp(section, "cmd"))) {
			section = NULL;
			continue;
		}

		if (!section) {
			section = arg;
			continue;
		}

		if (!strcmp(section, "env")) {
			if (!strncmp(arg, "HOME=", STRLITERALLEN("HOME=")))
				attach_options.initial_cwd = arg + STRLITERALLEN("HOME=");
			ret = push_vargs(&envvp, arg);
			if (ret < 0)
				return log_error(ret, "Failed to add %s to env array", arg);
		} else if (!strcmp(section, "cmd")) {
			ret = push_vargs(&argvp, arg);
			if (ret < 0)
				return log_error(ret, "Failed to add %s to arg array", arg);
		} else {
			return log_error(EXIT_FAILURE, "Invalid exec section %s", section);
		}
	}

	if (!argvp || !*argvp)
		return log_error(EXIT_FAILURE, "No command specified");

	ret = close_range(EXEC_PIPE_FD + 1, UINT_MAX, CLOSE_RANGE_UNSHARE);
	if (ret) {
		if (errno == ENOSYS)
			ret = close_inherited(fds_to_ignore, ARRAY_SIZE(fds_to_ignore));
	}
	if (ret)
		return log_error(EXIT_FAILURE, "Aborting attach to prevent leaking file descriptors into container");

	ret = fd_cloexec(status_pipe, true);
	if (ret)
		return log_errno(EXIT_FAILURE, "Failed to make pipe close-on-exec");

	c = lxc_container_new(name, lxcpath);
	if (!c)
		return log_error(EXIT_FAILURE, "Failed to load new container %s/%s", lxcpath, name);

	c->clear_config(c);

	if (!c->load_config(c, config_path))
		return log_error(EXIT_FAILURE, "Failed to load config file %s for %s/%s", config_path, lxcpath, name);

	if (strcmp(cwd, ""))
		attach_options.initial_cwd = cwd;
	attach_options.env_policy = LXC_ATTACH_CLEAR_ENV;
	attach_options.extra_env_vars = envvp;
	attach_options.stdin_fd = 3;
	attach_options.stdout_fd = 4;
	attach_options.stderr_fd = 5;
	attach_options.uid = uid;
	attach_options.gid = gid;
	command.program = argvp[0];
	command.argv = argvp;

	ret = c->attach(c, lxc_attach_run_command, &command, &attach_options, &pid);
	if (ret < 0)
		return EXIT_FAILURE;

	if (!write_nointr(status_pipe, &pid, sizeof(pid))) {
		// Kill the child just to be safe.
		fprintf(stderr, "Failed to send pid %d of executing child to LXD. Killing child\n", pid);
		kill(pid, SIGKILL);
	}

	ret = wait_for_pid_status_nointr(pid);
	if (ret < 0)
		return log_error(EXIT_FAILURE, "Failed to wait for child process %d", pid);

	if (WIFEXITED(ret))
		return WEXITSTATUS(ret);

	if (WIFSIGNALED(ret))
		return 128 + WTERMSIG(ret);

	return EXIT_FAILURE;
}

void forkexec(void)
{
	_exit(__forkexec());
}
*/
import "C"

type cmdForkexec struct {
	global *cmdGlobal
}

func (c *cmdForkexec) Command() *cobra.Command {
	// Main subcommand
	cmd := &cobra.Command{}
	cmd.Use = "forkexec <container name> <containers path> <config> <cwd> <uid> <gid> -- env [key=value...] -- cmd <args...>"
	cmd.Short = "Execute a task inside the container"
	cmd.Long = `Description:
  Execute a task inside the container

  This internal command is used to spawn a task inside the container and
  allow LXD to interact with it.
`
	cmd.RunE = c.Run
	cmd.Hidden = true

	return cmd
}

func (c *cmdForkexec) Run(cmd *cobra.Command, args []string) error {
	return fmt.Errorf("This command should have been intercepted in cgo")
}
<commit_msg>forkexec: handle broken close_range() backport in openSUSE Leap 15.3<commit_after>package main

import (
	"fmt"

	"github.com/spf13/cobra"

	// Used by cgo
	_ "github.com/lxc/lxd/lxd/include"
)

/*
#ifndef _GNU_SOURCE
#define _GNU_SOURCE 1
#endif
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <limits.h>

#include "include/macro.h"
#include "include/memory_utils.h"
#include "include/syscall_wrappers.h"
#include <lxc/attach_options.h>
#include <lxc/lxccontainer.h>

extern char *advance_arg(bool required);

static bool write_nointr(int fd, const void *buf, size_t count)
{
	ssize_t ret;

	do {
		ret = write(fd, buf, count);
	} while (ret < 0 && errno == EINTR);

	if (ret < 0)
		return false;

	return (size_t)ret == count;
}

define_cleanup_function(struct lxc_container *, lxc_container_put);

static int wait_for_pid_status_nointr(pid_t pid)
{
	int status, ret;

again:
	ret = waitpid(pid, &status, 0);
	if (ret == -1) {
		if (errno == EINTR)
			goto again;

		return -1;
	}

	if (ret != pid)
		goto again;

	return status;
}

static int append_null_to_list(void ***list)
{
	int newentry = 0;
	void **new_list;

	if (*list)
		for (; (*list)[newentry]; newentry++)
			;

	new_list = realloc(*list, (newentry + 2) * sizeof(void **));
	if (!new_list)
		return ret_errno(ENOMEM);

	*list = new_list;
	(*list)[newentry + 1] = NULL;
	return newentry;
}

static int push_vargs(char ***list, char *entry)
{
	__do_free char *copy = NULL;
	int newentry;

	copy = strdup(entry);
	if (!copy)
		return ret_errno(ENOMEM);

	newentry = append_null_to_list((void ***)list);
	if (newentry < 0)
		return newentry;

	(*list)[newentry] = move_ptr(copy);

	return 0;
}

static int fd_cloexec(int fd, bool cloexec)
{
	int oflags, nflags;

	oflags = fcntl(fd, F_GETFD, 0);
	if (oflags < 0)
		return -errno;

	if (cloexec)
		nflags = oflags | FD_CLOEXEC;
	else
		nflags = oflags & ~FD_CLOEXEC;

	if (nflags == oflags)
		return 0;

	if (fcntl(fd, F_SETFD, nflags) < 0)
		return -errno;

	return 0;
}

static int safe_int(const char *numstr, int *converted)
{
	char *err = NULL;
	signed long int sli;

	errno = 0;
	sli = strtol(numstr, &err, 0);
	if (errno == ERANGE && (sli == LONG_MAX || sli == LONG_MIN))
		return -ERANGE;

	if (errno != 0 && sli == 0)
		return -EINVAL;

	if (err == numstr || *err != '\0')
		return -EINVAL;

	if (sli > INT_MAX || sli < INT_MIN)
		return -ERANGE;

	*converted = (int)sli;
	return 0;
}

static inline bool match_stdfds(int fd)
{
	return (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO);
}

static int close_inherited(int *fds_to_ignore, size_t len_fds)
{
	int fddir;
	DIR *dir;
	struct dirent *direntp;

restart:
	dir = opendir("/proc/self/fd");
	if (!dir)
		return -errno;

	fddir = dirfd(dir);

	while ((direntp = readdir(dir))) {
		int fd, ret;
		size_t i;

		if (strcmp(direntp->d_name, ".") == 0)
			continue;

		if (strcmp(direntp->d_name, "..") == 0)
			continue;

		ret = safe_int(direntp->d_name, &fd);
		if (ret < 0)
			continue;

		for (i = 0; i < len_fds; i++)
			if (fds_to_ignore[i] == fd)
				break;

		if (fd == fddir || (i < len_fds && fd == fds_to_ignore[i]))
			continue;

		if (match_stdfds(fd))
			continue;

		if (close(fd)) {
			return log_error(-errno, "%s - Failed to close file descriptor %d", strerror(errno), fd);
		} else {
			char fdpath[PATH_MAX], realpath[PATH_MAX];

			snprintf(fdpath, sizeof(fdpath), "/proc/self/fd/%d", fd);
			ret = readlink(fdpath, realpath, PATH_MAX);
			if (ret < 0)
				snprintf(realpath, sizeof(realpath), "unknown");
			else if (ret >= sizeof(realpath))
				realpath[sizeof(realpath) - 1] = '\0';

			log_error(-errno, "Closing unexpected file descriptor %d -> %s", fd, realpath);
		}

		closedir(dir);
		goto restart;
	}

	closedir(dir);
	return 0;
}

#define EXEC_STDIN_FD 3
#define EXEC_STDOUT_FD 4
#define EXEC_STDERR_FD 5
#define EXEC_PIPE_FD 6
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))

// We use a separate function because cleanup macros are called during stack
// unwinding if I'm not mistaken and if the compiler knows it exits it won't
// call them. That's not a problem since we're exiting but I just like to be on
// the safe side in case we ever call this from a different context. We also
// tell the compiler to not inline us.
__attribute__ ((noinline)) static int __forkexec(void)
{
	__do_close int status_pipe = EXEC_PIPE_FD;
	__do_free_string_list char **argvp = NULL, **envvp = NULL;
	call_cleaner(lxc_container_put) struct lxc_container *c = NULL;
	const char *config_path = NULL, *lxcpath = NULL, *name = NULL;
	char *cwd = NULL;
	lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT;
	lxc_attach_command_t command = {
		.program = NULL,
	};
	int fds_to_ignore[] = {EXEC_STDIN_FD, EXEC_STDOUT_FD, EXEC_STDERR_FD, EXEC_PIPE_FD};
	int ret;
	pid_t pid;
	uid_t uid;
	gid_t gid;

	if (geteuid() != 0)
		return log_error(EXIT_FAILURE, "Error: forkexec requires root privileges");

	name = advance_arg(false);
	if (name == NULL ||
	    (strcmp(name, "--help") == 0 ||
	     strcmp(name, "--version") == 0 || strcmp(name, "-h") == 0))
		return 0;

	lxcpath = advance_arg(true);
	config_path = advance_arg(true);
	cwd = advance_arg(true);
	uid = atoi(advance_arg(true));
	if (uid < 0)
		uid = (uid_t) - 1;
	gid = atoi(advance_arg(true));
	if (gid < 0)
		gid = (gid_t) - 1;

	for (char *arg = NULL, *section = NULL; (arg = advance_arg(false)); ) {
		if (!strcmp(arg, "--") && (!section || strcmp(section, "cmd"))) {
			section = NULL;
			continue;
		}

		if (!section) {
			section = arg;
			continue;
		}

		if (!strcmp(section, "env")) {
			if (!strncmp(arg, "HOME=", STRLITERALLEN("HOME=")))
				attach_options.initial_cwd = arg + STRLITERALLEN("HOME=");
			ret = push_vargs(&envvp, arg);
			if (ret < 0)
				return log_error(ret, "Failed to add %s to env array", arg);
		} else if (!strcmp(section, "cmd")) {
			ret = push_vargs(&argvp, arg);
			if (ret < 0)
				return log_error(ret, "Failed to add %s to arg array", arg);
		} else {
			return log_error(EXIT_FAILURE, "Invalid exec section %s", section);
		}
	}

	if (!argvp || !*argvp)
		return log_error(EXIT_FAILURE, "No command specified");

	ret = close_range(EXEC_PIPE_FD + 1, UINT_MAX, CLOSE_RANGE_UNSHARE);
	if (ret) {
		// Fallback to close_inherited() when the syscall is not
		// available or when CLOSE_RANGE_UNSHARE isn't supported.
		// On a regular kernel CLOSE_RANGE_UNSHARE should always be
		// available but openSUSE Leap 15.3 seems to have a partial
		// backport without CLOSE_RANGE_UNSHARE support.
		if (errno == ENOSYS || errno == EINVAL)
			ret = close_inherited(fds_to_ignore, ARRAY_SIZE(fds_to_ignore));
	}
	if (ret)
		return log_error(EXIT_FAILURE, "Aborting attach to prevent leaking file descriptors into container");

	ret = fd_cloexec(status_pipe, true);
	if (ret)
		return log_errno(EXIT_FAILURE, "Failed to make pipe close-on-exec");

	c = lxc_container_new(name, lxcpath);
	if (!c)
		return log_error(EXIT_FAILURE, "Failed to load new container %s/%s", lxcpath, name);

	c->clear_config(c);

	if (!c->load_config(c, config_path))
		return log_error(EXIT_FAILURE, "Failed to load config file %s for %s/%s", config_path, lxcpath, name);

	if (strcmp(cwd, ""))
		attach_options.initial_cwd = cwd;
	attach_options.env_policy = LXC_ATTACH_CLEAR_ENV;
	attach_options.extra_env_vars = envvp;
	attach_options.stdin_fd = 3;
	attach_options.stdout_fd = 4;
	attach_options.stderr_fd = 5;
	attach_options.uid = uid;
	attach_options.gid = gid;
	command.program = argvp[0];
	command.argv = argvp;

	ret = c->attach(c, lxc_attach_run_command, &command, &attach_options, &pid);
	if (ret < 0)
		return EXIT_FAILURE;

	if (!write_nointr(status_pipe, &pid, sizeof(pid))) {
		// Kill the child just to be safe.
		fprintf(stderr, "Failed to send pid %d of executing child to LXD. Killing child\n", pid);
		kill(pid, SIGKILL);
	}

	ret = wait_for_pid_status_nointr(pid);
	if (ret < 0)
		return log_error(EXIT_FAILURE, "Failed to wait for child process %d", pid);

	if (WIFEXITED(ret))
		return WEXITSTATUS(ret);

	if (WIFSIGNALED(ret))
		return 128 + WTERMSIG(ret);

	return EXIT_FAILURE;
}

void forkexec(void)
{
	_exit(__forkexec());
}
*/
import "C"

type cmdForkexec struct {
	global *cmdGlobal
}

func (c *cmdForkexec) Command() *cobra.Command {
	// Main subcommand
	cmd := &cobra.Command{}
	cmd.Use = "forkexec <container name> <containers path> <config> <cwd> <uid> <gid> -- env [key=value...] -- cmd <args...>"
	cmd.Short = "Execute a task inside the container"
	cmd.Long = `Description:
  Execute a task inside the container

  This internal command is used to spawn a task inside the container and
  allow LXD to interact with it.
`
	cmd.RunE = c.Run
	cmd.Hidden = true

	return cmd
}

func (c *cmdForkexec) Run(cmd *cobra.Command, args []string) error {
	return fmt.Errorf("This command should have been intercepted in cgo")
}
<|endoftext|>


<commit_before>package db

import "time"

type Status string

const (
	StatusPending   Status = "pending"
	StatusStarted   Status = "started"
	StatusAborted   Status = "aborted"
	StatusSucceeded Status = "succeeded"
	StatusFailed    Status = "failed"
	StatusErrored   Status = "errored"
	StatusPaused    Status = "paused"
)

type Build struct {
	ID        int
	Name      string
	Status    Status
	Scheduled bool

	JobID        int
	JobName      string
	PipelineName string

	Engine         string
	EngineMetadata string

	StartTime time.Time
	EndTime   time.Time
}

func (b Build) OneOff() bool {
	return b.JobName == ""
}

func (b Build) IsRunning() bool {
	switch b.Status {
	case StatusPending, StatusStarted:
		return true
	default:
		return false
	}
}

func (b Build) Abortable() bool {
	return b.IsRunning()
}

type Resource struct {
	Name string
}

type SavedResource struct {
	ID           int
	CheckError   error
	Paused       bool
	PipelineName string
	Resource
}

func (r SavedResource) FailingToCheck() bool {
	return r.CheckError != nil
}

type VersionedResource struct {
	Resource     string
	Type         string
	Source       Source
	Version      Version
	Metadata     []MetadataField
	PipelineName string
}

type VersionedResources []VersionedResource

func (vrs VersionedResources) Lookup(name string) (VersionedResource, bool) {
	for _, vr := range vrs {
		if vr.Resource == name {
			return vr, true
		}
	}

	return VersionedResource{}, false
}

type SavedVersionedResource struct {
	ID int

	Enabled bool

	VersionedResource
}

type SavedVersionedResources []SavedVersionedResource

func (vrs SavedVersionedResources) Lookup(name string) (SavedVersionedResource, bool) {
	for _, vr := range vrs {
		if vr.Resource == name {
			return vr, true
		}
	}

	return SavedVersionedResource{}, false
}

type Source map[string]interface{}

type Version map[string]interface{}

type MetadataField struct {
	Name  string
	Value string
}
<commit_msg>remove unused 'paused' build status const<commit_after>package db

import "time"

type Status string

const (
	StatusPending   Status = "pending"
	StatusStarted   Status = "started"
	StatusAborted   Status = "aborted"
	StatusSucceeded Status = "succeeded"
	StatusFailed    Status = "failed"
	StatusErrored   Status = "errored"
)

type Build struct {
	ID        int
	Name      string
	Status    Status
	Scheduled bool

	JobID        int
	JobName      string
	PipelineName string

	Engine         string
	EngineMetadata string

	StartTime time.Time
	EndTime   time.Time
}

func (b Build) OneOff() bool {
	return b.JobName == ""
}

func (b Build) IsRunning() bool {
	switch b.Status {
	case StatusPending, StatusStarted:
		return true
	default:
		return false
	}
}

func (b Build) Abortable() bool {
	return b.IsRunning()
}

type Resource struct {
	Name string
}

type SavedResource struct {
	ID           int
	CheckError   error
	Paused       bool
	PipelineName string
	Resource
}

func (r SavedResource) FailingToCheck() bool {
	return r.CheckError != nil
}

type VersionedResource struct {
	Resource     string
	Type         string
	Source       Source
	Version      Version
	Metadata     []MetadataField
	PipelineName string
}

type VersionedResources []VersionedResource

func (vrs VersionedResources) Lookup(name string) (VersionedResource, bool) {
	for _, vr := range vrs {
		if vr.Resource == name {
			return vr, true
		}
	}

	return VersionedResource{}, false
}

type SavedVersionedResource struct {
	ID int

	Enabled bool

	VersionedResource
}

type SavedVersionedResources []SavedVersionedResource

func (vrs SavedVersionedResources) Lookup(name string) (SavedVersionedResource, bool) {
	for _, vr := range vrs {
		if vr.Resource == name {
			return vr, true
		}
	}

	return SavedVersionedResource{}, false
}

type Source map[string]interface{}

type Version map[string]interface{}

type MetadataField struct {
	Name  string
	Value string
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/mgutz/ansi"
	"github.com/cloudfoundry/cli/plugin"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	)

/*
*	This is the struct implementing the interface defined by the core CLI. It can
*	be found at  "github.com/cloudfoundry/cli/plugin/plugin.go"
*
 */
type downloadPlugin struct{}

/*
*	This function must be implemented by any plugin because it is part of the
*	plugin interface defined by the core CLI.
*
*	Run(....) is the entry point when the core CLI is invoking a command defined
*	by a plugin. The first parameter, plugin.CliConnection, is a struct that can
*	be used to invoke cli commands. The second paramter, args, is a slice of
*	strings. args[0] will be the name of the command, and will be followed by
*	any additional arguments a cli user typed in.
*
*	Any error handling should be handled with the plugin itself (this means printing
*	user facing errors). The CLI will exit 0 if the plugin exits 0 and will exit
*	1 should the plugin exits nonzero.
 */
func (c *downloadPlugin) Run(cliConnection plugin.CliConnection, args []string) {
	// Ensure that we called the command basic-plugin-command
	if args[0] == "download" {
		if(len(args) != 2){
			fmt.Println("\nError: Missing App Name")
        	os.Exit(1)
		}

		appName := args[1]
		// Invoke the cf command passed as the set of arguments
		// after the first argument.
		//
		// Calls to plugin.CliCommand([]string) must be done after the invocation
		// of plugin.Start() to ensure the environment is bootstrapped.

		// cf app APP_NAME --guid
		appGuidSlice, err := cliConnection.CliCommandWithoutTerminalOutput("app", appName, "--guid")
		check(err)
		appGuid := strings.TrimSpace(appGuidSlice[0])

		url := fmt.Sprintf("/v2/apps/%s/instances/%d/files/%s", appGuid, 0, "/app")
		fmt.Println(url)
		curlRequest := []string{"curl", url}
		output, err := cliConnection.CliCommandWithoutTerminalOutput(curlRequest...)
		check(err)

		// Print the output returned from the CLI command.
		fmt.Println(output)
		/*fmt.Println("---------- Command output from the plugin ----------")
		for index, val := range output {
			fmt.Println("#", index, " value: ", val)
		}
		fmt.Println("----------              FIN               -----------")*/
		fmt.Println("Starting file download!")

		pull()
	}
}

func pull() {
	response, err := http.Get("http://golang.org/")
	if err != nil {
		fmt.Printf("%s", err)
		os.Exit(1)
	} else {
		defer response.Body.Close()

		contents, err := ioutil.ReadAll(response.Body)
		check(err)

		dir, err := os.Getwd()
		check(err)

		dir += "/temp/"
		//create dir if does not exist
		err = os.MkdirAll(dir, 0755)
		check(err)

		// write file to dir
		filename := "google.html"
		dir += filename
		fmt.Printf("Writing directory: %s\n", dir)
	    err = ioutil.WriteFile(dir, contents, 0644)
	    check(err)

	    msg := ansi.Color("File Successfully Downloaded!", "green+b")
	    defer fmt.Println(msg)
	}
}

/*
*	This function must be implemented as part of the	plugin interface
*	defined by the core CLI.
*
*	GetMetadata() returns a PluginMetadata struct. The first field, Name,
*	determines the name of the plugin which should generally be without spaces.
*	If there are spaces in the name a user will need to properly quote the name
*	during uninstall otherwise the name will be treated as seperate arguments.
*	The second value is a slice of Command structs. Our slice only contains one
*	Command Struct, but could contain any number of them. The first field Name
*	defines the command `cf basic-plugin-command` once installed into the CLI. The
*	second field, HelpText, is used by the core CLI to display help information
*	to the user in the core commands `cf help`, `cf`, or `cf -h`.
 */
func (c *downloadPlugin) GetMetadata() plugin.PluginMetadata {
	return plugin.PluginMetadata{
		Name: "File Downloader",
		Version: plugin.VersionType{
			Major: 0,
			Minor: 0,
			Build: 1,
		},
		Commands: []plugin.Command{
			plugin.Command{
				Name:     "download",
				HelpText: "Download contents of targeted directory",

				// UsageDetails is optional
				// It is used to show help of usage of each command
				UsageDetails: plugin.Usage{
					Usage: "download\n   cf download",
				},
			},
		},
	}
}

// error check function
func check(e error) {
    if e != nil {
        fmt.Println("\nError: ", e)
        os.Exit(1)
    }
}

/*
* Unlike most Go programs, the `Main()` function will not be used to run all of the
* commands provided in your plugin. Main will be used to initialize the plugin
* process, as well as any dependencies you might require for your
* plugin.
 */
func main() {
	// Any initialization for your plugin can be handled here
	//
	// Note: to run the plugin.Start method, we pass in a pointer to the struct
	// implementing the interface defined at "github.com/cloudfoundry/cli/plugin/plugin.go"
	//
	// Note: The plugin's main() method is invoked at install time to collect
	// metadata. The plugin will exit 0 and the Run([]string) method will not be
	// invoked.
	plugin.Start(new(downloadPlugin))
	// Plugin code should be written in the Run([]string) method,
	// ensuring the plugin environment is bootstrapped.
}

<commit_msg>refactor<commit_after>package main

import (
	"fmt"
	"github.com/mgutz/ansi"
	"github.com/cloudfoundry/cli/plugin"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	)

/*
*	This is the struct implementing the interface defined by the core CLI. It can
*	be found at  "github.com/cloudfoundry/cli/plugin/plugin.go"
*
 */
type downloadPlugin struct{}

/*
*	This function must be implemented by any plugin because it is part of the
*	plugin interface defined by the core CLI.
*
*	Run(....) is the entry point when the core CLI is invoking a command defined
*	by a plugin. The first parameter, plugin.CliConnection, is a struct that can
*	be used to invoke cli commands. The second paramter, args, is a slice of
*	strings. args[0] will be the name of the command, and will be followed by
*	any additional arguments a cli user typed in.
*
*	Any error handling should be handled with the plugin itself (this means printing
*	user facing errors). The CLI will exit 0 if the plugin exits 0 and will exit
*	1 should the plugin exits nonzero.
 */
func (c *downloadPlugin) Run(cliConnection plugin.CliConnection, args []string) {
	// Ensure that we called the command basic-plugin-command
	if args[0] == "download" {
		if(len(args) != 2){
			fmt.Println("\nError: Missing App Name")
        	os.Exit(1)
		}

		appName := args[1]

		output, err := getDirString(cliConnection, appName)
		check(err)

		// Print the output returned from the CLI command.
		files, dirs := parseDir(output)

		fmt.Println("---------- Files ----------")
		for index, val := range files {
			fmt.Println("#", index, " value: ", val)
		}
		fmt.Println("")

		fmt.Println("---------- Directories ----------")
		for index, val := range dirs {
			fmt.Println("#", index, " value: ", val)
		}
		fmt.Println("")

		fmt.Println("Starting file download!")
		pull()
	}
}

func getDirString(cliConnection plugin.CliConnection, appName string) (string, error) {
	appGuidSlice, err := cliConnection.CliCommandWithoutTerminalOutput("app", appName, "--guid")
	check(err)
	appGuid := strings.TrimSpace(appGuidSlice[0])

	url := fmt.Sprintf("/v2/apps/%s/instances/%d/files/%s", appGuid, 0, "/app")
	fmt.Println(url)
	curlRequest := []string{"curl", url}
	output, err := cliConnection.CliCommandWithoutTerminalOutput(curlRequest...)
	check(err)

	return output[0], nil
}

func parseDir(dir string) ([]string, []string) {
// PRE: takes in a string of the directory and filesizes
// POST: FCTVAL==[]string of only filenames and folders without filesizes
    filesSlice := strings.Fields(dir)
    var files, dirs []string
    for i := 0; i < len(filesSlice); i+=2 {
    	if strings.HasSuffix(filesSlice[i], "/") {
    		dirs = append(dirs, filesSlice[i])
    	} else {
			files = append(files, filesSlice[i])
    	}

    }
    return files, dirs
}

func pull() {
	response, err := http.Get("http://golang.org/")
	if err != nil {
		fmt.Printf("%s", err)
		os.Exit(1)
	} else {
		defer response.Body.Close()

		contents, err := ioutil.ReadAll(response.Body)
		check(err)

		dir, err := os.Getwd()
		check(err)

		dir += "/temp/"
		//create dir if does not exist
		err = os.MkdirAll(dir, 0755)
		check(err)

		// write file to dir
		filename := "google.html"
		dir += filename
		fmt.Printf("Writing directory: %s\n", dir)
	    err = ioutil.WriteFile(dir, contents, 0644)
	    check(err)

	    msg := ansi.Color("File Successfully Downloaded!", "green+b")
	    defer fmt.Println(msg)
	}
}

/*
*	This function must be implemented as part of the	plugin interface
*	defined by the core CLI.
*
*	GetMetadata() returns a PluginMetadata struct. The first field, Name,
*	determines the name of the plugin which should generally be without spaces.
*	If there are spaces in the name a user will need to properly quote the name
*	during uninstall otherwise the name will be treated as seperate arguments.
*	The second value is a slice of Command structs. Our slice only contains one
*	Command Struct, but could contain any number of them. The first field Name
*	defines the command `cf basic-plugin-command` once installed into the CLI. The
*	second field, HelpText, is used by the core CLI to display help information
*	to the user in the core commands `cf help`, `cf`, or `cf -h`.
 */
func (c *downloadPlugin) GetMetadata() plugin.PluginMetadata {
	return plugin.PluginMetadata{
		Name: "File Downloader",
		Version: plugin.VersionType{
			Major: 0,
			Minor: 0,
			Build: 1,
		},
		Commands: []plugin.Command{
			plugin.Command{
				Name:     "download",
				HelpText: "Download contents of targeted directory",

				// UsageDetails is optional
				// It is used to show help of usage of each command
				UsageDetails: plugin.Usage{
					Usage: "download\n   cf download",
				},
			},
		},
	}
}

// error check function
func check(e error) {
    if e != nil {
        fmt.Println("\nError: ", e)
        os.Exit(1)
    }
}

/*
* Unlike most Go programs, the `Main()` function will not be used to run all of the
* commands provided in your plugin. Main will be used to initialize the plugin
* process, as well as any dependencies you might require for your
* plugin.
 */
func main() {
	// Any initialization for your plugin can be handled here
	//
	// Note: to run the plugin.Start method, we pass in a pointer to the struct
	// implementing the interface defined at "github.com/cloudfoundry/cli/plugin/plugin.go"
	//
	// Note: The plugin's main() method is invoked at install time to collect
	// metadata. The plugin will exit 0 and the Run([]string) method will not be
	// invoked.
	plugin.Start(new(downloadPlugin))
	// Plugin code should be written in the Run([]string) method,
	// ensuring the plugin environment is bootstrapped.
}

<|endoftext|>


<commit_before>/*
Package dsl contains the main Pact DSL used in the Consumer
collaboration test cases, and Provider contract test verification.
*/
package dsl

import (
	"fmt"
	"log"
	"os"
	"path/filepath"

	"github.com/hashicorp/logutils"
	"github.com/pact-foundation/pact-go/types"
)

// Pact is the container structure to run the Consumer Pact test cases.
type Pact struct {
	// Current server for the consumer.
	Server *types.MockServer

	// Port the Pact Daemon is running on.
	Port int

	// Pact RPC Client.
	pactClient *PactClient

	// Consumer is the name of the Consumer/Client.
	Consumer string

	// Provider is the name of the Providing service.
	Provider string

	// Interactions contains all of the Mock Service Interactions to be setup.
	Interactions []*Interaction

	// Log levels.
	LogLevel string

	// Used to detect if logging has been configured.
	logFilter *logutils.LevelFilter

	// Location of Pact external service invocation output logging.
	// Defaults to `<cwd>/logs`.
	LogDir string

	// Pact files will be saved in this folder.
	// Defaults to `<cwd>/pacts`.
	PactDir string

	// Specify which version of the Pact Specification should be used (1 or 2).
	// Defaults to 2.
	SpecificationVersion int
}

// AddInteraction creates a new Pact interaction, initialising all
// required things. Will automatically start a Mock Service if none running.
func (p *Pact) AddInteraction() *Interaction {
	p.Setup()
	log.Printf("[DEBUG] pact add interaction")
	i := &Interaction{}
	p.Interactions = append(p.Interactions, i)
	return i
}

// Setup starts the Pact Mock Server. This is usually called before each test
// suite begins. AddInteraction() will automatically call this if no Mock Server
// has been started.
func (p *Pact) Setup() *Pact {
	p.setupLogging()
	log.Printf("[DEBUG] pact setup")
	dir, _ := os.Getwd()

	if p.LogDir == "" {
		p.LogDir = fmt.Sprintf(filepath.Join(dir, "logs"))
	}

	if p.PactDir == "" {
		p.PactDir = fmt.Sprintf(filepath.Join(dir, "pacts"))
	}

	if p.SpecificationVersion == 0 {
		p.SpecificationVersion = 2
	}

	if p.Server == nil {
		args := []string{
			fmt.Sprintf("--pact-specification-version %d", p.SpecificationVersion),
			fmt.Sprintf("--pact-dir %s", filepath.FromSlash(p.PactDir)),
			fmt.Sprintf("--log %s/pact.log", filepath.FromSlash(p.LogDir+"/"+"pact.log")),
			fmt.Sprintf("--consumer %s", p.Consumer),
			fmt.Sprintf("--provider %s", p.Provider),
		}
		client := &PactClient{Port: p.Port}
		p.pactClient = client
		p.Server = client.StartServer(args)
	}

	return p
}

// Configure logging
func (p *Pact) setupLogging() {
	if p.logFilter == nil {
		if p.LogLevel == "" {
			p.LogLevel = "INFO"
		}
		p.logFilter = &logutils.LevelFilter{
			Levels:   []logutils.LogLevel{"DEBUG", "WARN", "ERROR"},
			MinLevel: logutils.LogLevel(p.LogLevel),
			Writer:   os.Stderr,
		}
		log.SetOutput(p.logFilter)
	}
	log.Printf("[DEBUG] pact setup logging")
}

// Teardown stops the Pact Mock Server. This usually is called on completion
// of each test suite.
func (p *Pact) Teardown() *Pact {
	log.Printf("[DEBUG] teardown")
	if p.Server != nil {
		p.Server = p.pactClient.StopServer(p.Server)
	}
	return p
}

// Verify runs the current test case against a Mock Service.
// Will cleanup interactions between tests within a suite.
func (p *Pact) Verify(integrationTest func() error) error {
	p.Setup()
	log.Printf("[DEBUG] pact verify")
	mockServer := &MockService{
		BaseURL:  fmt.Sprintf("http://localhost:%d", p.Server.Port),
		Consumer: p.Consumer,
		Provider: p.Provider,
	}

	for _, interaction := range p.Interactions {
		err := mockServer.AddInteraction(interaction)
		if err != nil {
			return err
		}
	}

	// Run the integration test
	integrationTest()

	// Run Verification Process
	err := mockServer.Verify()
	if err != nil {
		return err
	}

	// Clear out interations
	p.Interactions = make([]*Interaction, 0)

	return mockServer.DeleteInteractions()
}

// WritePact should be called writes when all tests have been performed for a
// given Consumer <-> Provider pair. It will write out the Pact to the
// configured file.
func (p *Pact) WritePact() error {
	p.Setup()
	log.Printf("[DEBUG] pact write Pact file")
	mockServer := MockService{
		BaseURL:  fmt.Sprintf("http://localhost:%d", p.Server.Port),
		Consumer: p.Consumer,
		Provider: p.Provider,
	}
	err := mockServer.WritePact()
	if err != nil {
		return err
	}

	return nil
}

// VerifyProvider reads the provided pact files and runs verification against
// a running Provider API.
func (p *Pact) VerifyProvider(request types.VerifyRequest) error {
	p.Setup()

	// If we provide a Broker, we go to it to find consumers
	if request.BrokerURL != "" {
		log.Printf("[DEBUG] pact provider verification - finding all consumers from broker: %s", request.BrokerURL)
		err := findConsumers(p.Provider, &request)
		if err != nil {
			return err
		}
	}

	log.Printf("[DEBUG] pact provider verification")

	content, err := p.pactClient.VerifyProvider(request)

	// Output test result to screen
	log.Println(content)

	return err
}
<commit_msg>fix(windows): update default pact output path<commit_after>/*
Package dsl contains the main Pact DSL used in the Consumer
collaboration test cases, and Provider contract test verification.
*/
package dsl

import (
	"fmt"
	"log"
	"os"
	"path/filepath"

	"github.com/hashicorp/logutils"
	"github.com/pact-foundation/pact-go/types"
)

// Pact is the container structure to run the Consumer Pact test cases.
type Pact struct {
	// Current server for the consumer.
	Server *types.MockServer

	// Port the Pact Daemon is running on.
	Port int

	// Pact RPC Client.
	pactClient *PactClient

	// Consumer is the name of the Consumer/Client.
	Consumer string

	// Provider is the name of the Providing service.
	Provider string

	// Interactions contains all of the Mock Service Interactions to be setup.
	Interactions []*Interaction

	// Log levels.
	LogLevel string

	// Used to detect if logging has been configured.
	logFilter *logutils.LevelFilter

	// Location of Pact external service invocation output logging.
	// Defaults to `<cwd>/logs`.
	LogDir string

	// Pact files will be saved in this folder.
	// Defaults to `<cwd>/pacts`.
	PactDir string

	// Specify which version of the Pact Specification should be used (1 or 2).
	// Defaults to 2.
	SpecificationVersion int
}

// AddInteraction creates a new Pact interaction, initialising all
// required things. Will automatically start a Mock Service if none running.
func (p *Pact) AddInteraction() *Interaction {
	p.Setup()
	log.Printf("[DEBUG] pact add interaction")
	i := &Interaction{}
	p.Interactions = append(p.Interactions, i)
	return i
}

// Setup starts the Pact Mock Server. This is usually called before each test
// suite begins. AddInteraction() will automatically call this if no Mock Server
// has been started.
func (p *Pact) Setup() *Pact {
	p.setupLogging()
	log.Printf("[DEBUG] pact setup")
	dir, _ := os.Getwd()

	if p.LogDir == "" {
		p.LogDir = fmt.Sprintf(filepath.Join(dir, "logs"))
	}

	if p.PactDir == "" {
		p.PactDir = fmt.Sprintf(filepath.Join(dir, "pacts"))
	}

	if p.SpecificationVersion == 0 {
		p.SpecificationVersion = 2
	}

	if p.Server == nil {
		args := []string{
			fmt.Sprintf("--pact-specification-version %d", p.SpecificationVersion),
			fmt.Sprintf("--pact-dir %s", filepath.FromSlash(p.PactDir)),
			fmt.Sprintf("--log %s", filepath.FromSlash(p.LogDir+"/"+"pact.log")),
			fmt.Sprintf("--consumer %s", p.Consumer),
			fmt.Sprintf("--provider %s", p.Provider),
		}
		client := &PactClient{Port: p.Port}
		p.pactClient = client
		p.Server = client.StartServer(args)
	}

	return p
}

// Configure logging
func (p *Pact) setupLogging() {
	if p.logFilter == nil {
		if p.LogLevel == "" {
			p.LogLevel = "INFO"
		}
		p.logFilter = &logutils.LevelFilter{
			Levels:   []logutils.LogLevel{"DEBUG", "WARN", "ERROR"},
			MinLevel: logutils.LogLevel(p.LogLevel),
			Writer:   os.Stderr,
		}
		log.SetOutput(p.logFilter)
	}
	log.Printf("[DEBUG] pact setup logging")
}

// Teardown stops the Pact Mock Server. This usually is called on completion
// of each test suite.
func (p *Pact) Teardown() *Pact {
	log.Printf("[DEBUG] teardown")
	if p.Server != nil {
		p.Server = p.pactClient.StopServer(p.Server)
	}
	return p
}

// Verify runs the current test case against a Mock Service.
// Will cleanup interactions between tests within a suite.
func (p *Pact) Verify(integrationTest func() error) error {
	p.Setup()
	log.Printf("[DEBUG] pact verify")
	mockServer := &MockService{
		BaseURL:  fmt.Sprintf("http://localhost:%d", p.Server.Port),
		Consumer: p.Consumer,
		Provider: p.Provider,
	}

	for _, interaction := range p.Interactions {
		err := mockServer.AddInteraction(interaction)
		if err != nil {
			return err
		}
	}

	// Run the integration test
	integrationTest()

	// Run Verification Process
	err := mockServer.Verify()
	if err != nil {
		return err
	}

	// Clear out interations
	p.Interactions = make([]*Interaction, 0)

	return mockServer.DeleteInteractions()
}

// WritePact should be called writes when all tests have been performed for a
// given Consumer <-> Provider pair. It will write out the Pact to the
// configured file.
func (p *Pact) WritePact() error {
	p.Setup()
	log.Printf("[DEBUG] pact write Pact file")
	mockServer := MockService{
		BaseURL:  fmt.Sprintf("http://localhost:%d", p.Server.Port),
		Consumer: p.Consumer,
		Provider: p.Provider,
	}
	err := mockServer.WritePact()
	if err != nil {
		return err
	}

	return nil
}

// VerifyProvider reads the provided pact files and runs verification against
// a running Provider API.
func (p *Pact) VerifyProvider(request types.VerifyRequest) error {
	p.Setup()

	// If we provide a Broker, we go to it to find consumers
	if request.BrokerURL != "" {
		log.Printf("[DEBUG] pact provider verification - finding all consumers from broker: %s", request.BrokerURL)
		err := findConsumers(p.Provider, &request)
		if err != nil {
			return err
		}
	}

	log.Printf("[DEBUG] pact provider verification")

	content, err := p.pactClient.VerifyProvider(request)

	// Output test result to screen
	log.Println(content)

	return err
}
<|endoftext|>


<commit_before>package mains

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"

	"github.com/zetamatta/go-box"
	"github.com/zetamatta/go-findfile"
	"github.com/zetamatta/go-getch"
	"github.com/zetamatta/go-mbcs"

	"github.com/zetamatta/nyagos/dos"
	"github.com/zetamatta/nyagos/ifdbg"
	"github.com/zetamatta/nyagos/readline"
	"github.com/zetamatta/nyagos/shell"
)

type any_t = interface{}

func toStr(arr []any_t, n int) string {
	if n < len(arr) {
		if ifdbg.DBG {
			println(fmt.Sprint(arr[n]))
		}
		return fmt.Sprint(arr[n])
	} else {
		if ifdbg.DBG {
			println("''")
		}
		return ""
	}
}

func cmdElevated([]any_t) []any_t {
	flag, _ := dos.IsElevated()
	return []any_t{flag}
}

func cmdChdir(args []any_t) []any_t {
	if len(args) >= 1 {
		dos.Chdir(fmt.Sprint(args[0]))
		return []any_t{true}
	}
	return []any_t{nil, errors.New("directory is required")}
}

func cmdBox(args []any_t) []any_t {
	t, ok := args[0].(map[any_t]any_t)
	if !ok {
		return []any_t{nil, "Not a table"}
	}
	if len(t) == 0 {
		return []any_t{}
	}
	sources := make([]string, 0, len(t))
	for i, i_ := 1, len(t); i <= i_; i++ {
		if val, ok := t[i]; ok {
			sources = append(sources, fmt.Sprint(val))
		}
	}
	return []any_t{box.Choice(sources, readline.Console)}
}

func cmdResetCharWidth(args []any_t) []any_t {
	readline.ResetCharWidth()
	return []any_t{}
}

func cmdNetDriveToUNC(args []any_t) []any_t {
	if len(args) < 1 {
		return []any_t{}
	}
	path, ok := args[0].(string)
	if !ok {
		return []any_t{path}
	}
	unc := dos.NetDriveToUNC(path)
	return []any_t{unc}
}

func cmdShellExecute(args []any_t) []any_t {
	err := dos.ShellExecute(
		toStr(args, 0),
		dos.TruePath(toStr(args, 1)),
		toStr(args, 2),
		toStr(args, 3))
	if err != nil {
		return []any_t{nil, err}
	} else {
		return []any_t{true}
	}
}

func cmdGetwd(args []any_t) []any_t {
	wd, err := os.Getwd()
	if err == nil {
		return []any_t{wd}
	} else {
		return []any_t{nil, err}
	}
}

func cmdGetKey(args []any_t) []any_t {
	keycode, scancode, shiftstatus := getch.Full()
	return []any_t{keycode, scancode, shiftstatus}
}

func cmdGetViewWidth(args []any_t) []any_t {
	width, height := box.GetScreenBufferInfo().ViewSize()
	return []any_t{width, height}
}

func cmdPathJoin(args []any_t) []any_t {
	if len(args) < 0 {
		return []any_t{""}
	}
	path := fmt.Sprint(args[0])
	for i, i_ := 1, len(args); i < i_; i++ {
		sub := fmt.Sprint(args[i])
		path = filepath.Join(path, sub)
	}
	return []any_t{path}
}

func cmdAccess(args []any_t) []any_t {
	if len(args) < 2 {
		return []any_t{nil, "nyagos.access requilres two arguments"}
	}
	path := fmt.Sprint(args[0])
	mode, mode_ok := args[1].(int)
	if !mode_ok {
		return []any_t{nil, "mode value must be interger"}
	}
	if ifdbg.DBG {
		fmt.Fprintf(os.Stderr, "given mode==%o\n", mode)
	}
	fi, err := os.Stat(path)

	var result bool
	if err != nil || fi == nil {
		result = false
	} else {
		if ifdbg.DBG {
			fmt.Fprintf(os.Stderr, "file mode==%o\n", fi.Mode().Perm())
		}
		switch {
		case mode == 0:
			result = true
		case (mode & 1) != 0: // X_OK
		case (mode & 2) != 0: // W_OK
			result = ((fi.Mode().Perm() & 0200) != 0)
		case (mode & 4) != 0: // R_OK
			result = ((fi.Mode().Perm() & 0400) != 0)
		}
	}
	return []any_t{result}
}

func cmdStat(args []any_t) []any_t {
	if len(args) < 1 {
		return []any_t{nil, errors.New("fee arguments")}
	}
	path := toStr(args, 0)
	var stat os.FileInfo
	var path_ string
	if len(path) > 0 && path[len(path)-1] == '\\' {
		path_ = filepath.Join(path, ".")
	} else {
		path_ = path
	}
	statErr := findfile.Walk(path_, func(f *findfile.FileInfo) bool {
		stat = f
		return false
	})
	if statErr != nil {
		return []any_t{nil, statErr}
	}
	if stat == nil {
		return []any_t{nil, fmt.Errorf("%s: failed to stat", path)}
	}
	t := stat.ModTime()
	return []any_t{
		map[string]any_t{
			"name":  stat.Name(),
			"size":  stat.Size(),
			"isdir": stat.IsDir(),
			"mtime": map[string]any_t{
				"year":   t.Year(),
				"month":  t.Month(),
				"day":    t.Day(),
				"hour":   t.Hour(),
				"minute": t.Minute(),
				"second": t.Second(),
			},
		},
	}
}

func cmdSetEnv(args []any_t) []any_t {
	if len(args) < 2 {
		return []any_t{nil, "too few arguments"}
	}
	name := fmt.Sprint(args[len(args)-2])
	value := fmt.Sprint(args[len(args)-1])
	if len(value) > 0 {
		os.Setenv(name, value)
	} else {
		os.Unsetenv(name)
	}
	return []any_t{true}
}

func cmdGetEnv(args []any_t) []any_t {
	if len(args) < 1 {
		return []any_t{nil, "too few arguments"}
	}
	name := fmt.Sprint(args[len(args)-1])
	value, ok := shell.OurGetEnv(name)
	if ok && len(value) > 0 {
		return []any_t{value}
	} else {
		return []any_t{nil}
	}
}

func cmdAtoU(args []any_t) []any_t {
	if len(args) < 1 {
		return []any_t{nil, "too few arguments"}
	}
	if s, ok := args[0].(string); ok {
		if val, err := mbcs.AtoU([]byte(s)); err == nil {
			return []any_t{val}
		} else {
			return []any_t{nil, err}
		}
	} else {
		return []any_t{fmt.Sprint(args[0])}
	}
}

func cmdUtoA(args []any_t) []any_t {
	if len(args) < 1 {
		return []any_t{nil, "too few arguments"}
	}
	utf8 := fmt.Sprint(args[0])
	bin, err := mbcs.UtoA(utf8)
	if err != nil {
		return []any_t{nil, err}
	}
	if len(bin) >= 1 {
		return []any_t{string(bin[:len(bin)-1]), nil}
	} else {
		return []any_t{"", nil}
	}
}
<commit_msg>(internal) lua function nyagos.utoa() uses lua.Lua.PushReflect([]byte) instead of lua.Lus.PushReflect(string)<commit_after>package mains

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"

	"github.com/zetamatta/go-box"
	"github.com/zetamatta/go-findfile"
	"github.com/zetamatta/go-getch"
	"github.com/zetamatta/go-mbcs"

	"github.com/zetamatta/nyagos/dos"
	"github.com/zetamatta/nyagos/ifdbg"
	"github.com/zetamatta/nyagos/readline"
	"github.com/zetamatta/nyagos/shell"
)

type any_t = interface{}

func toStr(arr []any_t, n int) string {
	if n < len(arr) {
		if ifdbg.DBG {
			println(fmt.Sprint(arr[n]))
		}
		return fmt.Sprint(arr[n])
	} else {
		if ifdbg.DBG {
			println("''")
		}
		return ""
	}
}

func cmdElevated([]any_t) []any_t {
	flag, _ := dos.IsElevated()
	return []any_t{flag}
}

func cmdChdir(args []any_t) []any_t {
	if len(args) >= 1 {
		dos.Chdir(fmt.Sprint(args[0]))
		return []any_t{true}
	}
	return []any_t{nil, errors.New("directory is required")}
}

func cmdBox(args []any_t) []any_t {
	t, ok := args[0].(map[any_t]any_t)
	if !ok {
		return []any_t{nil, "Not a table"}
	}
	if len(t) == 0 {
		return []any_t{}
	}
	sources := make([]string, 0, len(t))
	for i, i_ := 1, len(t); i <= i_; i++ {
		if val, ok := t[i]; ok {
			sources = append(sources, fmt.Sprint(val))
		}
	}
	return []any_t{box.Choice(sources, readline.Console)}
}

func cmdResetCharWidth(args []any_t) []any_t {
	readline.ResetCharWidth()
	return []any_t{}
}

func cmdNetDriveToUNC(args []any_t) []any_t {
	if len(args) < 1 {
		return []any_t{}
	}
	path, ok := args[0].(string)
	if !ok {
		return []any_t{path}
	}
	unc := dos.NetDriveToUNC(path)
	return []any_t{unc}
}

func cmdShellExecute(args []any_t) []any_t {
	err := dos.ShellExecute(
		toStr(args, 0),
		dos.TruePath(toStr(args, 1)),
		toStr(args, 2),
		toStr(args, 3))
	if err != nil {
		return []any_t{nil, err}
	} else {
		return []any_t{true}
	}
}

func cmdGetwd(args []any_t) []any_t {
	wd, err := os.Getwd()
	if err == nil {
		return []any_t{wd}
	} else {
		return []any_t{nil, err}
	}
}

func cmdGetKey(args []any_t) []any_t {
	keycode, scancode, shiftstatus := getch.Full()
	return []any_t{keycode, scancode, shiftstatus}
}

func cmdGetViewWidth(args []any_t) []any_t {
	width, height := box.GetScreenBufferInfo().ViewSize()
	return []any_t{width, height}
}

func cmdPathJoin(args []any_t) []any_t {
	if len(args) < 0 {
		return []any_t{""}
	}
	path := fmt.Sprint(args[0])
	for i, i_ := 1, len(args); i < i_; i++ {
		sub := fmt.Sprint(args[i])
		path = filepath.Join(path, sub)
	}
	return []any_t{path}
}

func cmdAccess(args []any_t) []any_t {
	if len(args) < 2 {
		return []any_t{nil, "nyagos.access requilres two arguments"}
	}
	path := fmt.Sprint(args[0])
	mode, mode_ok := args[1].(int)
	if !mode_ok {
		return []any_t{nil, "mode value must be interger"}
	}
	if ifdbg.DBG {
		fmt.Fprintf(os.Stderr, "given mode==%o\n", mode)
	}
	fi, err := os.Stat(path)

	var result bool
	if err != nil || fi == nil {
		result = false
	} else {
		if ifdbg.DBG {
			fmt.Fprintf(os.Stderr, "file mode==%o\n", fi.Mode().Perm())
		}
		switch {
		case mode == 0:
			result = true
		case (mode & 1) != 0: // X_OK
		case (mode & 2) != 0: // W_OK
			result = ((fi.Mode().Perm() & 0200) != 0)
		case (mode & 4) != 0: // R_OK
			result = ((fi.Mode().Perm() & 0400) != 0)
		}
	}
	return []any_t{result}
}

func cmdStat(args []any_t) []any_t {
	if len(args) < 1 {
		return []any_t{nil, errors.New("fee arguments")}
	}
	path := toStr(args, 0)
	var stat os.FileInfo
	var path_ string
	if len(path) > 0 && path[len(path)-1] == '\\' {
		path_ = filepath.Join(path, ".")
	} else {
		path_ = path
	}
	statErr := findfile.Walk(path_, func(f *findfile.FileInfo) bool {
		stat = f
		return false
	})
	if statErr != nil {
		return []any_t{nil, statErr}
	}
	if stat == nil {
		return []any_t{nil, fmt.Errorf("%s: failed to stat", path)}
	}
	t := stat.ModTime()
	return []any_t{
		map[string]any_t{
			"name":  stat.Name(),
			"size":  stat.Size(),
			"isdir": stat.IsDir(),
			"mtime": map[string]any_t{
				"year":   t.Year(),
				"month":  t.Month(),
				"day":    t.Day(),
				"hour":   t.Hour(),
				"minute": t.Minute(),
				"second": t.Second(),
			},
		},
	}
}

func cmdSetEnv(args []any_t) []any_t {
	if len(args) < 2 {
		return []any_t{nil, "too few arguments"}
	}
	name := fmt.Sprint(args[len(args)-2])
	value := fmt.Sprint(args[len(args)-1])
	if len(value) > 0 {
		os.Setenv(name, value)
	} else {
		os.Unsetenv(name)
	}
	return []any_t{true}
}

func cmdGetEnv(args []any_t) []any_t {
	if len(args) < 1 {
		return []any_t{nil, "too few arguments"}
	}
	name := fmt.Sprint(args[len(args)-1])
	value, ok := shell.OurGetEnv(name)
	if ok && len(value) > 0 {
		return []any_t{value}
	} else {
		return []any_t{nil}
	}
}

func cmdAtoU(args []any_t) []any_t {
	if len(args) < 1 {
		return []any_t{nil, "too few arguments"}
	}
	if s, ok := args[0].(string); ok {
		if val, err := mbcs.AtoU([]byte(s)); err == nil {
			return []any_t{val}
		} else {
			return []any_t{nil, err}
		}
	} else {
		return []any_t{fmt.Sprint(args[0])}
	}
}

func cmdUtoA(args []any_t) []any_t {
	if len(args) < 1 {
		return []any_t{nil, "too few arguments"}
	}
	utf8 := fmt.Sprint(args[0])
	bin, err := mbcs.UtoA(utf8)
	if err != nil {
		return []any_t{nil, err}
	}
	if len(bin) >= 1 {
		// trim the last zero byte from SJIS string
		return []any_t{bin[:len(bin)-1], nil}
	} else {
		return []any_t{"", nil}
	}
}
<|endoftext|>


<commit_before>package main

import(
	"fmt"
	"os"
	"go/ast"
	"go/parser"
	"go/token"
)

func main() {
	if len(os.Args) != 2 {
		println("usage: dump_ast  path/to/a/file.go")
		os.Exit(1)
	}

	fileSet := token.NewFileSet()
	rootNode, err := parser.ParseFile(fileSet, os.Args[1], nil, 0)
	if err != nil {
		panic(err.Error())
	}

	ast.Inspect(rootNode, func(node ast.Node) bool {
		if node == nil {
			return false
		}

		fmt.Printf("%p %#v\n", node, node)
		return true
	})

	return
}
<commit_msg>Dump objects on idents<commit_after>package main

import(
	"fmt"
	"os"
	"go/ast"
	"go/parser"
	"go/token"
)

func main() {
	if len(os.Args) != 2 {
		println("usage: dump_ast  path/to/a/file.go")
		os.Exit(1)
	}

	fileSet := token.NewFileSet()
	rootNode, err := parser.ParseFile(fileSet, os.Args[1], nil, 0)
	if err != nil {
		panic(err.Error())
	}

	ast.Inspect(rootNode, func(node ast.Node) bool {
		if node == nil {
			return true
		}

		switch node := node.(type) {
		case *ast.Ident:
			if node.Obj != nil {
				fmt.Printf("%p %#v\n%p %#v\n", node, node, node.Obj, node.Obj)
			} else {
				fmt.Printf("%p %#v\n", node, node)
			}
		default:
			fmt.Printf("%p %#v\n", node, node)
		}

		return true
	})

	return
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/sha1"
	"errors"
	"fmt"
	"io"
	"log"
	"os"
)

const blockSize = 4096 // Guestimate of a FS block-size for optimal read call

// Node type
type Node struct {
	Path string // File path
	Size int64  // File size
	Hash string // String form of SHA1 hash
}

func (n *Node) Value() interface{} {
	return n
}

// Calculate hash
func (node *Node) calculateHash(fast bool) string {
	// Open file
	file, err := os.Open(node.Path)
	if err != nil {
		log.Println("WARN", err)
		return ""
	}
	defer file.Close()

	var n int64 // bytes read
	hash := sha1.New()

	if fast && node.Size > blockSize {
		// Make one read call
		_, err = io.CopyN(hash, file, blockSize)

	} else {
		// Filesystem and memory optimal read
		n, err = io.Copy(hash, file)

		// Paranoid sanity check
		if n != node.Size {
			err = errors.New("Partial read: " + node.Path)
			log.Println("WARN", err)
			return ""
		}
	}

	// Check for normal errors
	if err != nil {
		log.Println("WARN", err)
		return ""
	}

	// Add hash value
	return fmt.Sprintf("%0x", hash.Sum(nil))
}

// Dup type describes found duplicate file
type Dup struct {
	Node      // Embed Node type Go type "inheritance"
	Count int // Number of identical copies for the hash
}

// Value implements mapreduce.Value interface
func (d Dup) Value() interface{} {
	return d
}

// Pretty printer
func (d Dup) String() string {
	return fmt.Sprintf("%s:%d:%d:%q", d.Hash, d.Count, d.Size, d.Path)
}
<commit_msg>- On the full SHA1 hash calculation, limit read to file size already determined during FS walk<commit_after>package main

import (
	"crypto/sha1"
	"errors"
	"fmt"
	"io"
	"log"
	"os"
)

const blockSize = 4096 // Guestimate of a FS block-size for optimal read call

// Node type
type Node struct {
	Path string // File path
	Size int64  // File size
	Hash string // String form of SHA1 hash
}

func (n *Node) Value() interface{} {
	return n
}

// Calculate hash
func (node *Node) calculateHash(fast bool) string {
	// Open file
	file, err := os.Open(node.Path)
	if err != nil {
		log.Println("WARN", err)
		return ""
	}
	defer file.Close()

	var n int64 // bytes read
	hash := sha1.New()

	if fast && node.Size > blockSize {
		// Make one read call
		_, err = io.CopyN(hash, file, blockSize)

	} else {
		// Always read no more that the file size already determined
		n, err = io.CopyN(hash, file, node.Size) // Optimal filesystem reads and memory use

		// Paranoid sanity check
		if n != node.Size {
			err = errors.New("Partial read: " + node.Path)
			log.Println("WARN", err)
			return ""
		}
	}

	// Check for normal errors
	if err != nil {
		log.Println("WARN", err)
		return ""
	}

	// Add hash value
	return fmt.Sprintf("%0x", hash.Sum(nil))
}

// Dup type describes found duplicate file
type Dup struct {
	Node      // Embed Node type Go type "inheritance"
	Count int // Number of identical copies for the hash
}

// Value implements mapreduce.Value interface
func (d Dup) Value() interface{} {
	return d
}

// Pretty printer
func (d Dup) String() string {
	return fmt.Sprintf("%s:%d:%d:%q", d.Hash, d.Count, d.Size, d.Path)
}
<|endoftext|>


<commit_before>package node

import (
	"fmt"
	"encoding/binary"
	"erlang/epmd"
	"erlang/dist"
	"log"
	"net"
	"strconv"
	"strings"
)

type Node struct {
	epmd.NodeInfo
	Cookie string
	port int32
}

func NewNode(name string, cookie string) (n *Node){
	log.Printf("Start with name '%s' and cookie '%s'", name, cookie)
	// TODO: add fqdn support
	ns := strings.Split(name, "@")
	nodeInfo := epmd.NodeInfo{
		FullName: name,
		Name: ns[0],
		Domain: ns[1],
		Port: 0,
		Type: 77,				// or 72 if hidden
		Protocol: 0,
		HighVsn: 5,
		LowVsn: 5,
		Creation: 0,
	}

	return &Node{
		NodeInfo: nodeInfo,
		Cookie: cookie,
	}
}

func (n *Node) Connect(remote string) {

}

func (n *Node) Publish(port int) (err error) {
	log.Printf("Publish ENode at %d", port)
	l, err := net.Listen("tcp", net.JoinHostPort("", strconv.Itoa(port)))
	if err != nil {
		return
	}
	n.Port = uint16(port)
	aliveResp := make(chan uint16)
	go epmdC(n, aliveResp)
	creation := <- aliveResp
	switch creation {
	case 99:
		return fmt.Errorf("Duplicate name '%s'", n.Name)
	case 100:
		return fmt.Errorf("Cannot connect to EPMD")
	default:
		n.Creation = creation
	}

	go func () {
		for {
			conn, err := l.Accept()
			log.Printf("Accept new at ENode")
			if err != nil {
				log.Printf(err.Error())
			} else {
				go n.mLoop(conn)
			}
		}
	}()
	return nil
}

func (currNode *Node) mLoop(c net.Conn) {

	currNd := dist.NewNodeDesc(currNode.FullName, currNode.Cookie, false)

	for {

		err := currNd.HandleMessage(c)
		if err != nil {
			log.Printf("Enode error: %s", err.Error())
			break
		}

	}
	c.Close()
}

func epmdC(n *Node, resp chan uint16) {
	conn, err := net.Dial("tcp", ":4369")
	defer conn.Close()
	if err != nil {
		resp <- 100
		return
	}

	epmdFROM := make(chan []byte)
	go epmdREADER(conn, epmdFROM)

	epmdTO := make(chan []byte)
	go epmdWRITER(conn, epmdFROM, epmdTO)

	epmdTO <- epmd.Compose_ALIVE2_REQ(&n.NodeInfo)

	for {

		select {
		case reply := <- epmdFROM:
			log.Printf("From EPMD: %v", reply)

			switch epmd.MessageId(reply[0]) {
			case epmd.ALIVE2_RESP:
				if creation, ok := epmd.Read_ALIVE2_RESP(reply); ok {
					resp <- creation
				} else {
					resp <- 99
				}
			}

		}

	}
}

func epmdREADER(conn net.Conn, in chan []byte) {
	for {
		buf := make([]byte, 1024)
		n, err := conn.Read(buf)
		if err != nil {
			in <- buf[0:n]
			in <- []byte{}
			return
		}
		log.Printf("Read from EPMD %d: %v", n, buf[:n])
		in <- buf[:n]
	}
}

func epmdWRITER(conn net.Conn, in chan []byte, out chan []byte) {
	for {
		select {
		case data := <- out:
			buf := make([]byte, 2)
			binary.BigEndian.PutUint16(buf[0:2], uint16(len(data)))
			buf = append(buf, data...)
			_, err := conn.Write(buf)
			if err != nil {
				in <- []byte{}
			}
		}
	}
}
<commit_msg>Parse all ping messages<commit_after>package node

import (
	"fmt"
	"encoding/binary"
	"erlang/epmd"
	"erlang/dist"
	"log"
	"net"
	"strconv"
	"strings"
)

type Node struct {
	epmd.NodeInfo
	Cookie string
	port int32
}

func NewNode(name string, cookie string) (n *Node){
	log.Printf("Start with name '%s' and cookie '%s'", name, cookie)
	// TODO: add fqdn support
	ns := strings.Split(name, "@")
	nodeInfo := epmd.NodeInfo{
		FullName: name,
		Name: ns[0],
		Domain: ns[1],
		Port: 0,
		Type: 77,				// or 72 if hidden
		Protocol: 0,
		HighVsn: 5,
		LowVsn: 5,
		Creation: 0,
	}

	return &Node{
		NodeInfo: nodeInfo,
		Cookie: cookie,
	}
}

func (n *Node) Connect(remote string) {

}

func (n *Node) Publish(port int) (err error) {
	log.Printf("Publish ENode at %d", port)
	l, err := net.Listen("tcp", net.JoinHostPort("", strconv.Itoa(port)))
	if err != nil {
		return
	}
	n.Port = uint16(port)
	aliveResp := make(chan uint16)
	go epmdC(n, aliveResp)
	creation := <- aliveResp
	switch creation {
	case 99:
		return fmt.Errorf("Duplicate name '%s'", n.Name)
	case 100:
		return fmt.Errorf("Cannot connect to EPMD")
	default:
		n.Creation = creation
	}

	go func () {
		for {
			conn, err := l.Accept()
			log.Printf("Accept new at ENode")
			if err != nil {
				log.Printf(err.Error())
			} else {
				go n.mLoop(conn)
			}
		}
	}()
	return nil
}

func (currNode *Node) mLoop(c net.Conn) {

	currNd := dist.NewNodeDesc(currNode.FullName, currNode.Cookie, false)

	for {
		err := currNd.ReadMessage(c)
		if err != nil {
			log.Printf("Enode error: %s", err.Error())
			break
		}

	}
	c.Close()
}

func epmdC(n *Node, resp chan uint16) {
	conn, err := net.Dial("tcp", ":4369")
	defer conn.Close()
	if err != nil {
		resp <- 100
		return
	}

	epmdFROM := make(chan []byte)
	go epmdREADER(conn, epmdFROM)

	epmdTO := make(chan []byte)
	go epmdWRITER(conn, epmdFROM, epmdTO)

	epmdTO <- epmd.Compose_ALIVE2_REQ(&n.NodeInfo)

	for {

		select {
		case reply := <- epmdFROM:
			log.Printf("From EPMD: %v", reply)

			switch epmd.MessageId(reply[0]) {
			case epmd.ALIVE2_RESP:
				if creation, ok := epmd.Read_ALIVE2_RESP(reply); ok {
					resp <- creation
				} else {
					resp <- 99
				}
			}

		}

	}
}

func epmdREADER(conn net.Conn, in chan []byte) {
	for {
		buf := make([]byte, 1024)
		n, err := conn.Read(buf)
		if err != nil {
			in <- buf[0:n]
			in <- []byte{}
			return
		}
		log.Printf("Read from EPMD %d: %v", n, buf[:n])
		in <- buf[:n]
	}
}

func epmdWRITER(conn net.Conn, in chan []byte, out chan []byte) {
	for {
		select {
		case data := <- out:
			buf := make([]byte, 2)
			binary.BigEndian.PutUint16(buf[0:2], uint16(len(data)))
			buf = append(buf, data...)
			_, err := conn.Write(buf)
			if err != nil {
				in <- []byte{}
			}
		}
	}
}
<|endoftext|>


<commit_before>package iradix

import (
	"bytes"
	"sort"
)

// WalkFn is used when walking the tree. Takes a
// key and value, returning if iteration should
// be terminated.
type WalkFn func(k []byte, v interface{}) bool

// leafNode is used to represent a value
type leafNode struct {
	key []byte
	val interface{}
}

// edge is used to represent an edge node
type edge struct {
	label byte
	node  *Node
}

// Node is an immutable node in the radix tree
type Node struct {
	// leaf is used to store possible leaf
	leaf *leafNode

	// prefix is the common prefix we ignore
	prefix []byte

	// Edges should be stored in-order for iteration.
	// We avoid a fully materialized slice to save memory,
	// since in most cases we expect to be sparse
	edges edges
}

func (n *Node) isLeaf() bool {
	return n.leaf != nil
}

func (n *Node) addEdge(e edge) {
	n.edges = append(n.edges, e)
	n.edges.Sort()
}

func (n *Node) replaceEdge(e edge) {
	num := len(n.edges)
	idx := sort.Search(num, func(i int) bool {
		return n.edges[i].label >= e.label
	})
	if idx < num && n.edges[idx].label == e.label {
		n.edges[idx].node = e.node
		return
	}
	panic("replacing missing edge")
}

func (n *Node) getEdge(label byte) (int, *Node) {
	num := len(n.edges)
	idx := sort.Search(num, func(i int) bool {
		return n.edges[i].label >= label
	})
	if idx < num && n.edges[idx].label == label {
		return idx, n.edges[idx].node
	}
	return -1, nil
}

func (n *Node) delEdge(label byte) {
	num := len(n.edges)
	idx := sort.Search(num, func(i int) bool {
		return n.edges[i].label >= label
	})
	if idx < num && n.edges[idx].label == label {
		copy(n.edges[idx:], n.edges[idx+1:])
		n.edges[len(n.edges)-1] = edge{}
		n.edges = n.edges[:len(n.edges)-1]
	}
}

func (n *Node) mergeChild() {
	e := n.edges[0]
	child := e.node
	n.prefix = concat(n.prefix, child.prefix)
	if child.leaf != nil {
		n.leaf = new(leafNode)
		*n.leaf = *child.leaf
	} else {
		n.leaf = nil
	}
	if len(child.edges) != 0 {
		n.edges = make([]edge, len(child.edges))
		copy(n.edges, child.edges)
	} else {
		n.edges = nil
	}
}

func (n *Node) Get(k []byte) (interface{}, bool) {
	search := k
	for {
		// Check for key exhaution
		if len(search) == 0 {
			if n.isLeaf() {
				return n.leaf.val, true
			}
			break
		}

		// Look for an edge
		_, n = n.getEdge(search[0])
		if n == nil {
			break
		}

		// Consume the search prefix
		if bytes.HasPrefix(search, n.prefix) {
			search = search[len(n.prefix):]
		} else {
			break
		}
	}
	return nil, false
}

// LongestPrefix is like Get, but instead of an
// exact match, it will return the longest prefix match.
func (n *Node) LongestPrefix(k []byte) ([]byte, interface{}, bool) {
	var last *leafNode
	search := k
	for {
		// Look for a leaf node
		if n.isLeaf() {
			last = n.leaf
		}

		// Check for key exhaution
		if len(search) == 0 {
			break
		}

		// Look for an edge
		_, n = n.getEdge(search[0])
		if n == nil {
			break
		}

		// Consume the search prefix
		if bytes.HasPrefix(search, n.prefix) {
			search = search[len(n.prefix):]
		} else {
			break
		}
	}
	if last != nil {
		return last.key, last.val, true
	}
	return nil, nil, false
}

// Minimum is used to return the minimum value in the tree
func (n *Node) Minimum() ([]byte, interface{}, bool) {
	for {
		if n.isLeaf() {
			return n.leaf.key, n.leaf.val, true
		}
		if len(n.edges) > 0 {
			n = n.edges[0].node
		} else {
			break
		}
	}
	return nil, nil, false
}

// Maximum is used to return the maximum value in the tree
func (n *Node) Maximum() ([]byte, interface{}, bool) {
	for {
		if num := len(n.edges); num > 0 {
			n = n.edges[num-1].node
			continue
		}
		if n.isLeaf() {
			return n.leaf.key, n.leaf.val, true
		} else {
			break
		}
	}
	return nil, nil, false
}

// Iterator is used to return an iterator at
// the given node to walk the tree
func (n *Node) Iterator() *Iterator {
	return &Iterator{node: n}
}

// Walk is used to walk the tree
func (n *Node) Walk(fn WalkFn) {
	recursiveWalk(n, fn)
}

// WalkPrefix is used to walk the tree under a prefix
func (n *Node) WalkPrefix(prefix []byte, fn WalkFn) {
	search := prefix
	for {
		// Check for key exhaution
		if len(search) == 0 {
			recursiveWalk(n, fn)
			return
		}

		// Look for an edge
		_, n = n.getEdge(search[0])
		if n == nil {
			break
		}

		// Consume the search prefix
		if bytes.HasPrefix(search, n.prefix) {
			search = search[len(n.prefix):]

		} else if bytes.HasPrefix(n.prefix, search) {
			// Child may be under our search prefix
			recursiveWalk(n, fn)
			return
		} else {
			break
		}
	}
}

// WalkPath is used to walk the tree, but only visiting nodes
// from the root down to a given leaf. Where WalkPrefix walks
// all the entries *under* the given prefix, this walks the
// entries *above* the given prefix.
func (n *Node) WalkPath(path []byte, fn WalkFn) {
	search := path
	for {
		// Visit the leaf values if any
		if n.leaf != nil && fn(n.leaf.key, n.leaf.val) {
			return
		}

		// Check for key exhaution
		if len(search) == 0 {
			return
		}

		// Look for an edge
		_, n = n.getEdge(search[0])
		if n == nil {
			return
		}

		// Consume the search prefix
		if bytes.HasPrefix(search, n.prefix) {
			search = search[len(n.prefix):]
		} else {
			break
		}
	}
}

// recursiveWalk is used to do a pre-order walk of a node
// recursively. Returns true if the walk should be aborted
func recursiveWalk(n *Node, fn WalkFn) bool {
	// Visit the leaf values if any
	if n.leaf != nil && fn(n.leaf.key, n.leaf.val) {
		return true
	}

	// Recurse on the children
	for _, e := range n.edges {
		if recursiveWalk(e.node, fn) {
			return true
		}
	}
	return false
}
<commit_msg>Optimize addEdge to avoid sort<commit_after>package iradix

import (
	"bytes"
	"sort"
)

// WalkFn is used when walking the tree. Takes a
// key and value, returning if iteration should
// be terminated.
type WalkFn func(k []byte, v interface{}) bool

// leafNode is used to represent a value
type leafNode struct {
	key []byte
	val interface{}
}

// edge is used to represent an edge node
type edge struct {
	label byte
	node  *Node
}

// Node is an immutable node in the radix tree
type Node struct {
	// leaf is used to store possible leaf
	leaf *leafNode

	// prefix is the common prefix we ignore
	prefix []byte

	// Edges should be stored in-order for iteration.
	// We avoid a fully materialized slice to save memory,
	// since in most cases we expect to be sparse
	edges edges
}

func (n *Node) isLeaf() bool {
	return n.leaf != nil
}

func (n *Node) addEdge(e edge) {
	num := len(n.edges)
	idx := sort.Search(num, func(i int) bool {
		return n.edges[i].label >= e.label
	})
	n.edges = append(n.edges, e)
	if idx != num {
		copy(n.edges[idx+1:], n.edges[idx:num])
		n.edges[idx] = e
	}
}

func (n *Node) replaceEdge(e edge) {
	num := len(n.edges)
	idx := sort.Search(num, func(i int) bool {
		return n.edges[i].label >= e.label
	})
	if idx < num && n.edges[idx].label == e.label {
		n.edges[idx].node = e.node
		return
	}
	panic("replacing missing edge")
}

func (n *Node) getEdge(label byte) (int, *Node) {
	num := len(n.edges)
	idx := sort.Search(num, func(i int) bool {
		return n.edges[i].label >= label
	})
	if idx < num && n.edges[idx].label == label {
		return idx, n.edges[idx].node
	}
	return -1, nil
}

func (n *Node) delEdge(label byte) {
	num := len(n.edges)
	idx := sort.Search(num, func(i int) bool {
		return n.edges[i].label >= label
	})
	if idx < num && n.edges[idx].label == label {
		copy(n.edges[idx:], n.edges[idx+1:])
		n.edges[len(n.edges)-1] = edge{}
		n.edges = n.edges[:len(n.edges)-1]
	}
}

func (n *Node) mergeChild() {
	e := n.edges[0]
	child := e.node
	n.prefix = concat(n.prefix, child.prefix)
	if child.leaf != nil {
		n.leaf = new(leafNode)
		*n.leaf = *child.leaf
	} else {
		n.leaf = nil
	}
	if len(child.edges) != 0 {
		n.edges = make([]edge, len(child.edges))
		copy(n.edges, child.edges)
	} else {
		n.edges = nil
	}
}

func (n *Node) Get(k []byte) (interface{}, bool) {
	search := k
	for {
		// Check for key exhaution
		if len(search) == 0 {
			if n.isLeaf() {
				return n.leaf.val, true
			}
			break
		}

		// Look for an edge
		_, n = n.getEdge(search[0])
		if n == nil {
			break
		}

		// Consume the search prefix
		if bytes.HasPrefix(search, n.prefix) {
			search = search[len(n.prefix):]
		} else {
			break
		}
	}
	return nil, false
}

// LongestPrefix is like Get, but instead of an
// exact match, it will return the longest prefix match.
func (n *Node) LongestPrefix(k []byte) ([]byte, interface{}, bool) {
	var last *leafNode
	search := k
	for {
		// Look for a leaf node
		if n.isLeaf() {
			last = n.leaf
		}

		// Check for key exhaution
		if len(search) == 0 {
			break
		}

		// Look for an edge
		_, n = n.getEdge(search[0])
		if n == nil {
			break
		}

		// Consume the search prefix
		if bytes.HasPrefix(search, n.prefix) {
			search = search[len(n.prefix):]
		} else {
			break
		}
	}
	if last != nil {
		return last.key, last.val, true
	}
	return nil, nil, false
}

// Minimum is used to return the minimum value in the tree
func (n *Node) Minimum() ([]byte, interface{}, bool) {
	for {
		if n.isLeaf() {
			return n.leaf.key, n.leaf.val, true
		}
		if len(n.edges) > 0 {
			n = n.edges[0].node
		} else {
			break
		}
	}
	return nil, nil, false
}

// Maximum is used to return the maximum value in the tree
func (n *Node) Maximum() ([]byte, interface{}, bool) {
	for {
		if num := len(n.edges); num > 0 {
			n = n.edges[num-1].node
			continue
		}
		if n.isLeaf() {
			return n.leaf.key, n.leaf.val, true
		} else {
			break
		}
	}
	return nil, nil, false
}

// Iterator is used to return an iterator at
// the given node to walk the tree
func (n *Node) Iterator() *Iterator {
	return &Iterator{node: n}
}

// Walk is used to walk the tree
func (n *Node) Walk(fn WalkFn) {
	recursiveWalk(n, fn)
}

// WalkPrefix is used to walk the tree under a prefix
func (n *Node) WalkPrefix(prefix []byte, fn WalkFn) {
	search := prefix
	for {
		// Check for key exhaution
		if len(search) == 0 {
			recursiveWalk(n, fn)
			return
		}

		// Look for an edge
		_, n = n.getEdge(search[0])
		if n == nil {
			break
		}

		// Consume the search prefix
		if bytes.HasPrefix(search, n.prefix) {
			search = search[len(n.prefix):]

		} else if bytes.HasPrefix(n.prefix, search) {
			// Child may be under our search prefix
			recursiveWalk(n, fn)
			return
		} else {
			break
		}
	}
}

// WalkPath is used to walk the tree, but only visiting nodes
// from the root down to a given leaf. Where WalkPrefix walks
// all the entries *under* the given prefix, this walks the
// entries *above* the given prefix.
func (n *Node) WalkPath(path []byte, fn WalkFn) {
	search := path
	for {
		// Visit the leaf values if any
		if n.leaf != nil && fn(n.leaf.key, n.leaf.val) {
			return
		}

		// Check for key exhaution
		if len(search) == 0 {
			return
		}

		// Look for an edge
		_, n = n.getEdge(search[0])
		if n == nil {
			return
		}

		// Consume the search prefix
		if bytes.HasPrefix(search, n.prefix) {
			search = search[len(n.prefix):]
		} else {
			break
		}
	}
}

// recursiveWalk is used to do a pre-order walk of a node
// recursively. Returns true if the walk should be aborted
func recursiveWalk(n *Node, fn WalkFn) bool {
	// Visit the leaf values if any
	if n.leaf != nil && fn(n.leaf.key, n.leaf.val) {
		return true
	}

	// Recurse on the children
	for _, e := range n.edges {
		if recursiveWalk(e.node, fn) {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package artnet

import (
	"fmt"
	"io"
	"net"
	"sync"
	"time"

	"github.com/jsimonetti/go-artnet/packet"
	"github.com/jsimonetti/go-artnet/packet/code"
)

// Node is the information known about a node
type Node struct {
	// Config holds the configuration of this node
	Config NodeConfig

	// conn is the UDP connection this node will listen on
	conn   net.PacketConn
	bconn  net.Conn
	sendCh chan netPayload
	recvCh chan netPayload

	// shutdownCh will be closed on shutdown of the node
	shutdownCh   chan struct{}
	shutdown     bool
	shutdownErr  error
	shutdownLock sync.Mutex

	// pollCh will receive ArtPoll packets
	pollCh chan packet.ArtPollPacket
	// pollCh will send ArtPollReply packets
	pollReplyCh chan packet.ArtPollReplyPacket

	log Logger
}

// netPayload contains bytes read from the network and/or an error
type netPayload struct {
	address net.UDPAddr
	err     error
	data    []byte
}

// NewNode return a Node
func NewNode(name string, style code.StyleCode, ip net.IP, log Logger) *Node {
	n := &Node{
		Config: NodeConfig{
			Name: name,
			Type: style,
		},
		conn:     nil,
		shutdown: true,
		log:      log.With(Fields{"type": "Node"}),
	}
	if len(ip) > 0 {
		n.Config.IP = ip
	}
	//n.Config.IP = GenerateIP()
	return n
}

// Stop will stop all running routines and close the network connection
func (n *Node) Stop() {
	n.shutdownLock.Lock()
	n.shutdown = true
	n.shutdownLock.Unlock()
	close(n.shutdownCh)
	if n.conn != nil {
		n.conn.Close()
	}
	if n.bconn != nil {
		n.bconn.Close()
	}
}

// Start will start the controller
func (n *Node) Start() error {
	n.log.With(Fields{"ip": n.Config.IP.String(), "type": n.Config.Type.String()}).Debug("node started")

	n.sendCh = make(chan netPayload, 10)
	n.recvCh = make(chan netPayload, 10)
	n.pollCh = make(chan packet.ArtPollPacket, 10)
	n.pollReplyCh = make(chan packet.ArtPollReplyPacket, 10)
	n.shutdownCh = make(chan struct{})
	n.shutdown = false

	var err error
	laddr := net.UDPAddr{
		n.Config.IP,
		0,
		"",
	}
	raddr := net.UDPAddr{
		net.ParseIP("2.255.255.255"),
		6454,
		"",
	}
	n.bconn, err = net.DialUDP("udp4", &laddr, &raddr)
	if err != nil {
		n.shutdownErr = fmt.Errorf("error net.DialUDP: %s", err)
		n.log.With(Fields{"error": err}).Error("error net.DialUDP")
		return err
	}

	n.conn, err = net.ListenPacket("udp4", "0.0.0.0:6454")
	if err != nil {
		n.shutdownErr = fmt.Errorf("error net.ListenPacket: %s", err)
		n.log.With(Fields{"error": err}).Error("error net.ListenPacket")
		return err
	}

	go n.recvLoop()
	go n.sendLoop()

	return nil
}

// pollReplyLoop loops to reply to ArtPoll packets
// when a controller asks for continuous updates, we do that using a ticker
func (n *Node) pollReplyLoop() {
	var timer time.Ticker

	// loop untill shutdown
	for {
		select {
		case <-timer.C:
			// if we should regularly send replies (can be requested by the controller)
			// we send it here

		case poll := <-n.pollCh:
			// reply with pollReply
			n.log.With(Fields{"poll": poll}).Debugf("poll received, now send a reply")

			// if we are asked to send changes regularyl, set the Ticker here

		case <-n.shutdownCh:
			return
		}
	}
}

// sendLoop is used to send packets to the network
func (n *Node) sendLoop() {
	// loop untill shutdown
	for {
		select {
		case payload := <-n.sendCh:
			n.shutdownLock.Lock()
			if !n.shutdown {
				var num int
				var err error
				if payload.address.IP.Equal(broadcastAddr.IP) {
					num, err = n.bconn.Write(payload.data)
				} else {
					num, err = n.conn.WriteTo(payload.data, &payload.address)
				}
				if err != nil {
					n.log.With(Fields{"error": err}).Debugf("error writing packet")
					continue
				}
				n.log.With(Fields{"dst": payload.address.String(), "bytes": num}).Debugf("packet sent")
			}
			n.shutdownLock.Unlock()
		case <-n.shutdownCh:
			return
		}
	}
}

// AddrToUDPAddr will turn a net.Addr into a net.UDPAddr
func AddrToUDPAddr(addr net.Addr) net.UDPAddr {
	udp := addr.(*net.UDPAddr)
	return *udp
}

// recvLoop is used to receive packets from the network
// it starts a goroutine for dumping the msgs onto a channel,
// the payload from that channel is then fed into a handler
// due to the nature of broadcasting, we see our own sent
// packets to, but we ignore them
func (n *Node) recvLoop() {
	// start a routine that will read data from n.conn
	// and (if not shutdown), send to the recvCh
	go func() {
		b := make([]byte, 4096)
		for {
			num, src, err := n.conn.ReadFrom(b)
			n.shutdownLock.Lock()
			if !n.shutdown {
				n.shutdownLock.Unlock()
				from := AddrToUDPAddr(src)
				if n.Config.IP.Equal(from.IP) {
					// this was sent by me, so we ignore it
					//n.log.With(Fields{"src": from.String(), "bytes": num}).Debugf("ignoring received packet from self")
					continue
				}

				n.log.With(Fields{"src": from.String(), "bytes": num}).Debugf("received packet")
				if err != nil && err != io.EOF {
					n.recvCh <- netPayload{
						address: from,
						data:    b[:num],
						err:     err,
					}
					continue
				}
				n.recvCh <- netPayload{
					address: from,
					data:    b[:num],
					err:     err,
				}
				continue
			}
			n.shutdownLock.Unlock()
			return
		}
	}()

	// loop untill shutdown
	for {
		select {
		case payload := <-n.recvCh:
			p, err := packet.Unmarshal(payload.data)
			if err != nil {
				n.log.Printf("failed to parse packet from %s: %v", payload.address.String(), err)
				continue
			}
			go n.handlePacket(p)

		case <-n.shutdownCh:
			return
		}
	}
}

// handlePacket contains the logic for dealing with incoming packets
func (n *Node) handlePacket(p packet.ArtNetPacket) {
	switch p := p.(type) {
	case *packet.ArtPollReplyPacket:
		// only handle these packets if we are a controller
		if n.Config.Type == code.StController {
			n.pollReplyCh <- *p
		}

	default:
		n.log.With(Fields{"packet": p}).Debugf("unknown packet type")
	}

}
<commit_msg>Better self detect<commit_after>package artnet

import (
	"fmt"
	"io"
	"net"
	"sync"
	"time"

	"github.com/jsimonetti/go-artnet/packet"
	"github.com/jsimonetti/go-artnet/packet/code"
)

// Node is the information known about a node
type Node struct {
	// Config holds the configuration of this node
	Config NodeConfig

	// conn is the UDP connection this node will listen on
	conn   net.PacketConn
	bconn  net.Conn
	sendCh chan netPayload
	recvCh chan netPayload

	// shutdownCh will be closed on shutdown of the node
	shutdownCh   chan struct{}
	shutdown     bool
	shutdownErr  error
	shutdownLock sync.Mutex

	// pollCh will receive ArtPoll packets
	pollCh chan packet.ArtPollPacket
	// pollCh will send ArtPollReply packets
	pollReplyCh chan packet.ArtPollReplyPacket

	log Logger
}

// netPayload contains bytes read from the network and/or an error
type netPayload struct {
	address net.UDPAddr
	err     error
	data    []byte
}

// NewNode return a Node
func NewNode(name string, style code.StyleCode, ip net.IP, log Logger) *Node {
	n := &Node{
		Config: NodeConfig{
			Name: name,
			Type: style,
		},
		conn:     nil,
		shutdown: true,
		log:      log.With(Fields{"type": "Node"}),
	}
	if len(ip) > 0 {
		n.Config.IP = ip
	}
	//n.Config.IP = GenerateIP()
	return n
}

// Stop will stop all running routines and close the network connection
func (n *Node) Stop() {
	n.shutdownLock.Lock()
	n.shutdown = true
	n.shutdownLock.Unlock()
	close(n.shutdownCh)
	if n.conn != nil {
		n.conn.Close()
	}
	if n.bconn != nil {
		n.bconn.Close()
	}
}

// Start will start the controller
func (n *Node) Start() error {
	n.log.With(Fields{"ip": n.Config.IP.String(), "type": n.Config.Type.String()}).Debug("node started")

	n.sendCh = make(chan netPayload, 10)
	n.recvCh = make(chan netPayload, 10)
	n.pollCh = make(chan packet.ArtPollPacket, 10)
	n.pollReplyCh = make(chan packet.ArtPollReplyPacket, 10)
	n.shutdownCh = make(chan struct{})
	n.shutdown = false

	var err error
	laddr := net.UDPAddr{
		n.Config.IP,
		0,
		"",
	}
	raddr := net.UDPAddr{
		net.ParseIP("2.255.255.255"),
		6454,
		"",
	}
	n.bconn, err = net.DialUDP("udp4", &laddr, &raddr)
	if err != nil {
		n.shutdownErr = fmt.Errorf("error net.DialUDP: %s", err)
		n.log.With(Fields{"error": err}).Error("error net.DialUDP")
		return err
	}

	n.conn, err = net.ListenPacket("udp4", "0.0.0.0:6454")
	if err != nil {
		n.shutdownErr = fmt.Errorf("error net.ListenPacket: %s", err)
		n.log.With(Fields{"error": err}).Error("error net.ListenPacket")
		return err
	}

	go n.recvLoop()
	go n.sendLoop()

	return nil
}

// pollReplyLoop loops to reply to ArtPoll packets
// when a controller asks for continuous updates, we do that using a ticker
func (n *Node) pollReplyLoop() {
	var timer time.Ticker

	// loop untill shutdown
	for {
		select {
		case <-timer.C:
			// if we should regularly send replies (can be requested by the controller)
			// we send it here

		case poll := <-n.pollCh:
			// reply with pollReply
			n.log.With(Fields{"poll": poll}).Debugf("poll received, now send a reply")

			// if we are asked to send changes regularyl, set the Ticker here

		case <-n.shutdownCh:
			return
		}
	}
}

// sendLoop is used to send packets to the network
func (n *Node) sendLoop() {
	// loop untill shutdown
	for {
		select {
		case payload := <-n.sendCh:
			n.shutdownLock.Lock()
			if !n.shutdown {
				var num int
				var err error
				if payload.address.IP.Equal(broadcastAddr.IP) {
					num, err = n.bconn.Write(payload.data)
				} else {
					num, err = n.conn.WriteTo(payload.data, &payload.address)
				}
				if err != nil {
					n.log.With(Fields{"error": err}).Debugf("error writing packet")
					continue
				}
				n.log.With(Fields{"dst": payload.address.String(), "bytes": num}).Debugf("packet sent")
			}
			n.shutdownLock.Unlock()
		case <-n.shutdownCh:
			return
		}
	}
}

// AddrToUDPAddr will turn a net.Addr into a net.UDPAddr
func AddrToUDPAddr(addr net.Addr) net.UDPAddr {
	udp := addr.(*net.UDPAddr)
	return *udp
}

// recvLoop is used to receive packets from the network
// it starts a goroutine for dumping the msgs onto a channel,
// the payload from that channel is then fed into a handler
// due to the nature of broadcasting, we see our own sent
// packets to, but we ignore them
func (n *Node) recvLoop() {
	// start a routine that will read data from n.conn
	// and (if not shutdown), send to the recvCh
	go func() {
		b := make([]byte, 4096)
		for {
			num, src, err := n.conn.ReadFrom(b)
			n.shutdownLock.Lock()
			if !n.shutdown {
				n.shutdownLock.Unlock()
				from := AddrToUDPAddr(src)
				if n.bconn != nil && n.bconn.LocalAddr() == src {
					// this was sent by me, so we ignore it
					//n.log.With(Fields{"src": from.String(), "bytes": num}).Debugf("ignoring received packet from self")
					continue
				}

				n.log.With(Fields{"src": from.String(), "bytes": num}).Debugf("received packet")
				if err != nil && err != io.EOF {
					n.recvCh <- netPayload{
						address: from,
						data:    b[:num],
						err:     err,
					}
					continue
				}
				n.recvCh <- netPayload{
					address: from,
					data:    b[:num],
					err:     err,
				}
				continue
			}
			n.shutdownLock.Unlock()
			return
		}
	}()

	// loop untill shutdown
	for {
		select {
		case payload := <-n.recvCh:
			p, err := packet.Unmarshal(payload.data)
			if err != nil {
				n.log.Printf("failed to parse packet from %s: %v", payload.address.String(), err)
				continue
			}
			go n.handlePacket(p)

		case <-n.shutdownCh:
			return
		}
	}
}

// handlePacket contains the logic for dealing with incoming packets
func (n *Node) handlePacket(p packet.ArtNetPacket) {
	switch p := p.(type) {
	case *packet.ArtPollReplyPacket:
		// only handle these packets if we are a controller
		if n.Config.Type == code.StController {
			n.pollReplyCh <- *p
		}

	default:
		n.log.With(Fields{"packet": p}).Debugf("unknown packet type")
	}

}
<|endoftext|>


<commit_before>package dwn

import (
	"encoding/json"
	"log"
	"net/http"
	"strconv"
	"time"
)

// PostHandler gets, creates, and updates posts
func PostHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	var post Post
	switch r.Method {
	case "GET":
		var id int
		qID := r.URL.Query().Get("ID")
		id, err := strconv.Atoi(qID)
		if err != nil {
			http.Error(w, "Post ID not valid", http.StatusBadRequest)
			return
		}
		if err := Db.One("ID", id, &post); err != nil {
			http.Error(w, "Post not found", http.StatusNotFound)
			return
		}
	case "PUT":
		err := json.NewDecoder(r.Body).Decode(&post)
		if err != nil {
			log.Println("PostHandler: could not unmarshal put request json body:", err)
			http.Error(w, "could not unmarshal put request json body", http.StatusInternalServerError)
			return
		}
		err = Db.Save(&post)
		if err != nil {
			log.Println("PostHandler: could not save put request json body:", err)
			http.Error(w, "could not save put request json body", http.StatusInternalServerError)
			return
		}
	case "POST":
		err := json.NewDecoder(r.Body).Decode(&post)
		if err != nil {
			log.Println("PostHandler: could not unmarshal post request json body:", err)
			http.Error(w, "could not unmarshal post request json body", http.StatusInternalServerError)
			return
		}
		err = Db.Save(&post)
		if err != nil {
			log.Println("PostHandler: could not save post request json body:", err)
			http.Error(w, "could not save post request json body", http.StatusInternalServerError)
			return
		}
	}
}

// Post stores a blog post
type Post struct {
	ID       int `storm:"id,increment"`
	Topic    Topic
	Title    string
	Slug     string
	Author   UserInfo
	Body     string
	Format   PostFormat
	IsStub   bool
	Modified time.Time
	Created  time.Time `storm:"index"`
}

// Topic indicates the topic of a post
type Topic int

const (
	// TopicAll indicates all topics
	TopicAll Topic = iota
	// TopicPersonal indicates personal topics
	TopicPersonal
	// TopicTech indicates tech and programming topics
	TopicTech
	// TopicFood indicates food and dining topics
	TopicFood
	// TopicFun indicates game and vacation topics
	TopicFun
)

// PostFormat indicates the format of a post
type PostFormat int

const (
	// PostFormatPlain indicates plaintext
	PostFormatPlain PostFormat = iota
	// PostFormatMarkdown indicates markdown
	PostFormatMarkdown
)
<commit_msg>index posts by topic<commit_after>package dwn

import (
	"encoding/json"
	"log"
	"net/http"
	"strconv"
	"time"
)

// PostHandler gets, creates, and updates posts
func PostHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	var post Post
	switch r.Method {
	case "GET":
		var id int
		qID := r.URL.Query().Get("ID")
		id, err := strconv.Atoi(qID)
		if err != nil {
			http.Error(w, "Post ID not valid", http.StatusBadRequest)
			return
		}
		if err := Db.One("ID", id, &post); err != nil {
			http.Error(w, "Post not found", http.StatusNotFound)
			return
		}
	case "PUT":
		err := json.NewDecoder(r.Body).Decode(&post)
		if err != nil {
			log.Println("PostHandler: could not unmarshal put request json body:", err)
			http.Error(w, "could not unmarshal put request json body", http.StatusInternalServerError)
			return
		}
		err = Db.Save(&post)
		if err != nil {
			log.Println("PostHandler: could not save put request json body:", err)
			http.Error(w, "could not save put request json body", http.StatusInternalServerError)
			return
		}
	case "POST":
		err := json.NewDecoder(r.Body).Decode(&post)
		if err != nil {
			log.Println("PostHandler: could not unmarshal post request json body:", err)
			http.Error(w, "could not unmarshal post request json body", http.StatusInternalServerError)
			return
		}
		err = Db.Save(&post)
		if err != nil {
			log.Println("PostHandler: could not save post request json body:", err)
			http.Error(w, "could not save post request json body", http.StatusInternalServerError)
			return
		}
	}
}

// Post stores a blog post
type Post struct {
	ID       int   `storm:"id,increment"`
	Topic    Topic `storm:"index"`
	Title    string
	Slug     string
	Author   UserInfo
	Body     string
	Format   PostFormat
	IsStub   bool
	Modified time.Time
	Created  time.Time `storm:"index"`
}

// Topic indicates the topic of a post
type Topic int

const (
	// TopicAll indicates all topics
	TopicAll Topic = iota
	// TopicPersonal indicates personal topics
	TopicPersonal
	// TopicTech indicates tech and programming topics
	TopicTech
	// TopicFood indicates food and dining topics
	TopicFood
	// TopicFun indicates game and vacation topics
	TopicFun
)

// PostFormat indicates the format of a post
type PostFormat int

const (
	// PostFormatPlain indicates plaintext
	PostFormatPlain PostFormat = iota
	// PostFormatMarkdown indicates markdown
	PostFormatMarkdown
)
<|endoftext|>


<commit_before>package dwn

import (
	"encoding/json"
	"log"
	"net/http"
	"time"
)

// PostHandler gets, creates, and updates posts
func PostHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	switch r.Method {
	case "GET":
	case "PUT":
		var post Post
		err := json.NewDecoder(r.Body).Decode(&post)
		if err != nil {
			log.Println("PostHandler: could not unmarshal put request json body:", err)
			http.Error(w, "could not unmarshal put request json body", http.StatusInternalServerError)
			return
		}
		err = Db.Save(&post)
		if err != nil {
			log.Println("PostHandler: could not save put request json body:", err)
			http.Error(w, "could not save put request json body", http.StatusInternalServerError)
			return
		}
	case "POST":
		var post Post
		err := json.NewDecoder(r.Body).Decode(&post)
		if err != nil {
			log.Println("PostHandler: could not unmarshal post request json body:", err)
			http.Error(w, "could not unmarshal post request json body", http.StatusInternalServerError)
			return
		}
		err = Db.Save(&post)
		if err != nil {
			log.Println("PostHandler: could not save post request json body:", err)
			http.Error(w, "could not save post request json body", http.StatusInternalServerError)
			return
		}
	}
}

// Post stores a blog post
type Post struct {
	ID       int `storm:"id,increment"`
	Topic    Topic
	Title    string
	Slug     string
	Author   UserInfo
	Body     string
	Format   PostFormat
	IsStub   bool
	Modified time.Time
	Created  time.Time `storm:"index"`
}

// Topic indicates the topic of a post
type Topic int

const (
	// TopicAll indicates all topics
	TopicAll Topic = iota
	// TopicPersonal indicates personal topics
	TopicPersonal
	// TopicTech indicates tech and programming topics
	TopicTech
	// TopicFood indicates food and dining topics
	TopicFood
	// TopicFun indicates game and vacation topics
	TopicFun
)

// PostFormat indicates the format of a post
type PostFormat int

const (
	// PostFormatPlain indicates plaintext
	PostFormatPlain PostFormat = iota
	// PostFormatMarkdown indicates markdown
	PostFormatMarkdown
)
<commit_msg>add get block<commit_after>package dwn

import (
	"encoding/json"
	"log"
	"net/http"
	"strconv"
	"time"
)

// PostHandler gets, creates, and updates posts
func PostHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	var post Post
	switch r.Method {
	case "GET":
		var id int
		qID := r.URL.Query().Get("ID")
		id, err := strconv.Atoi(qID)
		if err != nil {
			http.Error(w, "Post ID not valid", http.StatusBadRequest)
			return
		}
		if err := Db.One("ID", id, &post); err != nil {
			http.Error(w, "Post not found", http.StatusNotFound)
			return
		}
	case "PUT":
		err := json.NewDecoder(r.Body).Decode(&post)
		if err != nil {
			log.Println("PostHandler: could not unmarshal put request json body:", err)
			http.Error(w, "could not unmarshal put request json body", http.StatusInternalServerError)
			return
		}
		err = Db.Save(&post)
		if err != nil {
			log.Println("PostHandler: could not save put request json body:", err)
			http.Error(w, "could not save put request json body", http.StatusInternalServerError)
			return
		}
	case "POST":
		err := json.NewDecoder(r.Body).Decode(&post)
		if err != nil {
			log.Println("PostHandler: could not unmarshal post request json body:", err)
			http.Error(w, "could not unmarshal post request json body", http.StatusInternalServerError)
			return
		}
		err = Db.Save(&post)
		if err != nil {
			log.Println("PostHandler: could not save post request json body:", err)
			http.Error(w, "could not save post request json body", http.StatusInternalServerError)
			return
		}
	}
}

// Post stores a blog post
type Post struct {
	ID       int `storm:"id,increment"`
	Topic    Topic
	Title    string
	Slug     string
	Author   UserInfo
	Body     string
	Format   PostFormat
	IsStub   bool
	Modified time.Time
	Created  time.Time `storm:"index"`
}

// Topic indicates the topic of a post
type Topic int

const (
	// TopicAll indicates all topics
	TopicAll Topic = iota
	// TopicPersonal indicates personal topics
	TopicPersonal
	// TopicTech indicates tech and programming topics
	TopicTech
	// TopicFood indicates food and dining topics
	TopicFood
	// TopicFun indicates game and vacation topics
	TopicFun
)

// PostFormat indicates the format of a post
type PostFormat int

const (
	// PostFormatPlain indicates plaintext
	PostFormatPlain PostFormat = iota
	// PostFormatMarkdown indicates markdown
	PostFormatMarkdown
)
<|endoftext|>


<commit_before>package tme

import (
	"testing"
	"time"
)

func TestManualTimerMarkDone(t *testing.T) {
	const dur = 100 * time.Millisecond
	timer := NewManualTimer()
	bch := timer.Done()
	select {
	case <-bch:
		t.Errorf("timer was marked done when it wasn't done")
	case <-time.After(dur):
	}

	timer.MarkDone()
	ach := timer.Done()

	// bch should receive and close
	select {
	case <-bch:
	case <-time.After(dur):
		t.Errorf("timer was not marked done after %s", dur)
	}

	// ach should receive and close
	select {
	case <-ach:
	case <-time.After(dur):
		t.Errorf("Done didn't receive within %s", dur)
	}
}

func TestManualTimerStop(t *testing.T) {
	const dur = 100 * time.Millisecond
	timer := NewManualTimer()
	bch := timer.Done()
	timer.Stop()
	ach := timer.Done()
	select {
	case <-bch:
		t.Errorf("Done received when it shouldn't have")
	case <-time.After(dur):
	}
	select {
	case <-ach:
		t.Errorf("Done received when it shouldn't have")
	case <-time.After(dur):
	}
}
<commit_msg>adding test for multiple stops<commit_after>package tme

import (
	"sync"
	"testing"
	"time"
)

func TestManualTimerMarkDone(t *testing.T) {
	const dur = 100 * time.Millisecond
	timer := NewManualTimer()
	bch := timer.Done()
	select {
	case <-bch:
		t.Errorf("timer was marked done when it wasn't done")
	case <-time.After(dur):
	}

	timer.MarkDone()
	ach := timer.Done()

	// bch should receive and close
	select {
	case <-bch:
	case <-time.After(dur):
		t.Errorf("timer was not marked done after %s", dur)
	}

	// ach should receive and close
	select {
	case <-ach:
	case <-time.After(dur):
		t.Errorf("Done didn't receive within %s", dur)
	}
}

func TestManualTimerStop(t *testing.T) {
	const dur = 100 * time.Millisecond
	timer := NewManualTimer()
	bch := timer.Done()
	timer.Stop()
	ach := timer.Done()
	select {
	case <-bch:
		t.Errorf("Done received when it shouldn't have")
	case <-time.After(dur):
	}
	select {
	case <-ach:
		t.Errorf("Done received when it shouldn't have")
	case <-time.After(dur):
	}
}

func TestManualTimerMultipleStop(t *testing.T) {
	const n = 10
	const dur = 100 * time.Millisecond
	timer := NewManualTimer()
	if !timer.Stop() {
		t.Fatalf("first call to stop didn't return true")
	}
	var wg sync.WaitGroup
	for i := 0; i < n; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			if timer.Stop() {
				t.Errorf("call # %d to stop returned true", i)
			}
		}(i)
	}

	wg.Wait()
	select {
	case <-timer.Done():
		t.Errorf("Done returned")
	case <-time.After(dur):
	}
}
<|endoftext|>