<commit_before>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2019 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package executor

import (
	"context"
	"fmt"
	"math/big"
	"time"

	"github.com/sirupsen/logrus"

	"github.com/loadimpact/k6/lib"
	"github.com/loadimpact/k6/lib/types"
	"github.com/loadimpact/k6/ui/pb"
)

func sumStagesDuration(stages []Stage) (result time.Duration) {
	for _, s := range stages {
		result += time.Duration(s.Duration.Duration)
	}
	return
}

func getStagesUnscaledMaxTarget(unscaledStartValue int64, stages []Stage) int64 {
	max := unscaledStartValue
	for _, s := range stages {
		if s.Target.Int64 > max {
			max = s.Target.Int64
		}
	}
	return max
}

// A helper function to avoid code duplication
func validateStages(stages []Stage) []error {
	var errors []error
	if len(stages) == 0 {
		errors = append(errors, fmt.Errorf("at least one stage has to be specified"))
		return errors
	}

	for i, s := range stages {
		stageNum := i + 1
		if !s.Duration.Valid {
			errors = append(errors, fmt.Errorf("stage %d doesn't have a duration", stageNum))
		} else if s.Duration.Duration < 0 {
			errors = append(errors, fmt.Errorf("the duration for stage %d shouldn't be negative", stageNum))
		}
		if !s.Target.Valid {
			errors = append(errors, fmt.Errorf("stage %d doesn't have a target", stageNum))
		} else if s.Target.Int64 < 0 {
			errors = append(errors, fmt.Errorf("the target for stage %d shouldn't be negative", stageNum))
		}
	}
	return errors
}

// getIterationRunner is a helper function that returns an iteration executor
// closure. It takes care of updating the execution state statistics and
// warning messages. And returns whether a full iteration was finished or not
//
// TODO: emit the end-of-test iteration metrics here (https://github.com/loadimpact/k6/issues/1250)
func getIterationRunner(
	executionState *lib.ExecutionState, logger *logrus.Entry,
) func(context.Context, lib.ActiveVU) bool {
	return func(ctx context.Context, vu lib.ActiveVU) bool {
		err := vu.RunOnce()

		// TODO: track (non-ramp-down) errors from script iterations as a metric,
		// and have a default threshold that will abort the script when the error
		// rate exceeds a certain percentage

		select {
		case <-ctx.Done():
			// Don't log errors or emit iterations metrics from cancelled iterations
			executionState.AddInterruptedIterations(1)
			return false
		default:
			if err != nil {
				if s, ok := err.(fmt.Stringer); ok {
					logger.Error(s.String())
				} else {
					logger.Error(err.Error())
				}
				// TODO: investigate context cancelled errors
			}

			// TODO: move emission of end-of-iteration metrics here?
			executionState.AddFullIterations(1)
			return true
		}
	}
}

// getDurationContexts is used to create sub-contexts that can restrict an
// executor to only run for its allotted time.
//
// If the executor doesn't have a graceful stop period for iterations, then
// both returned sub-contexts will be the same one, with a timeout equal to
// the supplied regular executor duration.
//
// But if a graceful stop is enabled, then the first returned context (and the
// cancel func) will be for the "outer" sub-context. Its timeout will include
// both the regular duration and the specified graceful stop period. The second
// context will be a sub-context of the first one and its timeout will include
// only the regular duration.
//
// In either case, the usage of these contexts should be like this:
//  - As long as the regDurationCtx isn't done, new iterations can be started.
//  - After regDurationCtx is done, no new iterations should be started; every
//    VU that finishes an iteration from now on can be returned to the buffer
//    pool in the ExecutionState struct.
//  - After maxDurationCtx is done, any VUs with iterations will be
//    interrupted by the context's closing and will be returned to the buffer.
//  - If you want to interrupt the execution of all VUs prematurely (e.g. there
//    was an error or something like that), trigger maxDurationCancel().
//  - If the whole test is aborted, the parent context will be cancelled, so
//    that will also cancel these contexts, thus the "general abort" case is
//    handled transparently.
func getDurationContexts(parentCtx context.Context, regularDuration, gracefulStop time.Duration) (
	startTime time.Time, maxDurationCtx, regDurationCtx context.Context, maxDurationCancel func(),
) {
	startTime = time.Now()
	maxEndTime := startTime.Add(regularDuration + gracefulStop)

	maxDurationCtx, maxDurationCancel = context.WithDeadline(parentCtx, maxEndTime)
	if gracefulStop == 0 {
		return startTime, maxDurationCtx, maxDurationCtx, maxDurationCancel
	}
	regDurationCtx, _ = context.WithDeadline(maxDurationCtx, startTime.Add(regularDuration)) //nolint:govet
	return startTime, maxDurationCtx, regDurationCtx, maxDurationCancel
}

// trackProgress is a helper function that monitors certain end-events in an
// executor and updates its progressbar accordingly.
func trackProgress(
	parentCtx, maxDurationCtx, regDurationCtx context.Context,
	exec lib.Executor, snapshot func() (float64, []string),
) {
	progressBar := exec.GetProgress()
	logger := exec.GetLogger()

	<-regDurationCtx.Done() // Wait for the regular context to be over
	gracefulStop := exec.GetConfig().GetGracefulStop()
	if parentCtx.Err() == nil && gracefulStop > 0 {
		p, right := snapshot()
		logger.WithField("gracefulStop", gracefulStop).Debug(
			"Regular duration is done, waiting for iterations to gracefully finish",
		)
		progressBar.Modify(
			pb.WithStatus(pb.Stopping),
			pb.WithConstProgress(p, right...),
		)
	}

	<-maxDurationCtx.Done()
	p, right := snapshot()
	constProg := pb.WithConstProgress(p, right...)
	select {
	case <-parentCtx.Done():
		progressBar.Modify(pb.WithStatus(pb.Interrupted), constProg)
	default:
		status := pb.WithStatus(pb.Done)
		if p < 1 {
			status = pb.WithStatus(pb.Interrupted)
		}
		progressBar.Modify(status, constProg)
	}
}

// getScaledArrivalRate returns a rational number containing the scaled value of
// the given rate over the given period. This should generally be the first
// function that's called, before we do any calculations with the users-supplied
// rates in the arrival-rate executors.
func getScaledArrivalRate(es *lib.ExecutionSegment, rate int64, period time.Duration) *big.Rat {
	return es.InPlaceScaleRat(big.NewRat(rate, int64(period)))
}

// getTickerPeriod is just a helper function that returns the ticker interval
// we need for given arrival-rate parameters.
//
// It's possible for this function to return a zero duration (i.e. valid=false)
// and 0 isn't a valid ticker period. This happens so we don't divide by 0 when
// the arrival-rate period is 0. This case has to be handled separately.
func getTickerPeriod(scaledArrivalRate *big.Rat) types.NullDuration {
	if scaledArrivalRate.Sign() == 0 {
		return types.NewNullDuration(0, false)
	}
	// Basically, the ticker rate is time.Duration(1/arrivalRate). Considering
	// that time.Duration is represented as int64 nanoseconds, no meaningful
	// precision is likely to be lost here...
	result, _ := new(big.Rat).Inv(scaledArrivalRate).Float64()
	return types.NewNullDuration(time.Duration(result), true)
}

// getArrivalRatePerSec returns the iterations per second rate.
func getArrivalRatePerSec(scaledArrivalRate *big.Rat) *big.Rat {
	perSecRate := big.NewRat(int64(time.Second), 1)
	return perSecRate.Mul(perSecRate, scaledArrivalRate)
}

func getVUActivationParams(
	ctx context.Context, conf BaseConfig, deactivateCallback func(lib.InitializedVU),
) *lib.VUActivationParams {
	return &lib.VUActivationParams{
		RunContext:         ctx,
		Scenario:           conf.Name,
		Exec:               conf.GetExec(),
		Env:                conf.GetEnv(),
		Tags:               conf.GetTags(),
		DeactivateCallback: deactivateCallback,
	}
}
<commit_msg>mark stacktraces with a source in the logs<commit_after>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2019 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package executor

import (
	"context"
	"fmt"
	"math/big"
	"time"

	"github.com/sirupsen/logrus"

	"github.com/loadimpact/k6/lib"
	"github.com/loadimpact/k6/lib/types"
	"github.com/loadimpact/k6/ui/pb"
)

func sumStagesDuration(stages []Stage) (result time.Duration) {
	for _, s := range stages {
		result += time.Duration(s.Duration.Duration)
	}
	return
}

func getStagesUnscaledMaxTarget(unscaledStartValue int64, stages []Stage) int64 {
	max := unscaledStartValue
	for _, s := range stages {
		if s.Target.Int64 > max {
			max = s.Target.Int64
		}
	}
	return max
}

// A helper function to avoid code duplication
func validateStages(stages []Stage) []error {
	var errors []error
	if len(stages) == 0 {
		errors = append(errors, fmt.Errorf("at least one stage has to be specified"))
		return errors
	}

	for i, s := range stages {
		stageNum := i + 1
		if !s.Duration.Valid {
			errors = append(errors, fmt.Errorf("stage %d doesn't have a duration", stageNum))
		} else if s.Duration.Duration < 0 {
			errors = append(errors, fmt.Errorf("the duration for stage %d shouldn't be negative", stageNum))
		}
		if !s.Target.Valid {
			errors = append(errors, fmt.Errorf("stage %d doesn't have a target", stageNum))
		} else if s.Target.Int64 < 0 {
			errors = append(errors, fmt.Errorf("the target for stage %d shouldn't be negative", stageNum))
		}
	}
	return errors
}

// getIterationRunner is a helper function that returns an iteration executor
// closure. It takes care of updating the execution state statistics and
// warning messages. And returns whether a full iteration was finished or not
//
// TODO: emit the end-of-test iteration metrics here (https://github.com/loadimpact/k6/issues/1250)
func getIterationRunner(
	executionState *lib.ExecutionState, logger *logrus.Entry,
) func(context.Context, lib.ActiveVU) bool {
	return func(ctx context.Context, vu lib.ActiveVU) bool {
		err := vu.RunOnce()

		// TODO: track (non-ramp-down) errors from script iterations as a metric,
		// and have a default threshold that will abort the script when the error
		// rate exceeds a certain percentage

		select {
		case <-ctx.Done():
			// Don't log errors or emit iterations metrics from cancelled iterations
			executionState.AddInterruptedIterations(1)
			return false
		default:
			if err != nil {
				if s, ok := err.(fmt.Stringer); ok {
					// TODO better detection for stack traces
					// TODO don't count this as a full iteration?
					logger.WithField("source", "stacktrace").Error(s.String())
				} else {
					logger.Error(err.Error())
				}
				// TODO: investigate context cancelled errors
			}

			// TODO: move emission of end-of-iteration metrics here?
			executionState.AddFullIterations(1)
			return true
		}
	}
}

// getDurationContexts is used to create sub-contexts that can restrict an
// executor to only run for its allotted time.
//
// If the executor doesn't have a graceful stop period for iterations, then
// both returned sub-contexts will be the same one, with a timeout equal to
// the supplied regular executor duration.
//
// But if a graceful stop is enabled, then the first returned context (and the
// cancel func) will be for the "outer" sub-context. Its timeout will include
// both the regular duration and the specified graceful stop period. The second
// context will be a sub-context of the first one and its timeout will include
// only the regular duration.
//
// In either case, the usage of these contexts should be like this:
//  - As long as the regDurationCtx isn't done, new iterations can be started.
//  - After regDurationCtx is done, no new iterations should be started; every
//    VU that finishes an iteration from now on can be returned to the buffer
//    pool in the ExecutionState struct.
//  - After maxDurationCtx is done, any VUs with iterations will be
//    interrupted by the context's closing and will be returned to the buffer.
//  - If you want to interrupt the execution of all VUs prematurely (e.g. there
//    was an error or something like that), trigger maxDurationCancel().
//  - If the whole test is aborted, the parent context will be cancelled, so
//    that will also cancel these contexts, thus the "general abort" case is
//    handled transparently.
func getDurationContexts(parentCtx context.Context, regularDuration, gracefulStop time.Duration) (
	startTime time.Time, maxDurationCtx, regDurationCtx context.Context, maxDurationCancel func(),
) {
	startTime = time.Now()
	maxEndTime := startTime.Add(regularDuration + gracefulStop)

	maxDurationCtx, maxDurationCancel = context.WithDeadline(parentCtx, maxEndTime)
	if gracefulStop == 0 {
		return startTime, maxDurationCtx, maxDurationCtx, maxDurationCancel
	}
	regDurationCtx, _ = context.WithDeadline(maxDurationCtx, startTime.Add(regularDuration)) //nolint:govet
	return startTime, maxDurationCtx, regDurationCtx, maxDurationCancel
}

// trackProgress is a helper function that monitors certain end-events in an
// executor and updates its progressbar accordingly.
func trackProgress(
	parentCtx, maxDurationCtx, regDurationCtx context.Context,
	exec lib.Executor, snapshot func() (float64, []string),
) {
	progressBar := exec.GetProgress()
	logger := exec.GetLogger()

	<-regDurationCtx.Done() // Wait for the regular context to be over
	gracefulStop := exec.GetConfig().GetGracefulStop()
	if parentCtx.Err() == nil && gracefulStop > 0 {
		p, right := snapshot()
		logger.WithField("gracefulStop", gracefulStop).Debug(
			"Regular duration is done, waiting for iterations to gracefully finish",
		)
		progressBar.Modify(
			pb.WithStatus(pb.Stopping),
			pb.WithConstProgress(p, right...),
		)
	}

	<-maxDurationCtx.Done()
	p, right := snapshot()
	constProg := pb.WithConstProgress(p, right...)
	select {
	case <-parentCtx.Done():
		progressBar.Modify(pb.WithStatus(pb.Interrupted), constProg)
	default:
		status := pb.WithStatus(pb.Done)
		if p < 1 {
			status = pb.WithStatus(pb.Interrupted)
		}
		progressBar.Modify(status, constProg)
	}
}

// getScaledArrivalRate returns a rational number containing the scaled value of
// the given rate over the given period. This should generally be the first
// function that's called, before we do any calculations with the users-supplied
// rates in the arrival-rate executors.
func getScaledArrivalRate(es *lib.ExecutionSegment, rate int64, period time.Duration) *big.Rat {
	return es.InPlaceScaleRat(big.NewRat(rate, int64(period)))
}

// getTickerPeriod is just a helper function that returns the ticker interval
// we need for given arrival-rate parameters.
//
// It's possible for this function to return a zero duration (i.e. valid=false)
// and 0 isn't a valid ticker period. This happens so we don't divide by 0 when
// the arrival-rate period is 0. This case has to be handled separately.
func getTickerPeriod(scaledArrivalRate *big.Rat) types.NullDuration {
	if scaledArrivalRate.Sign() == 0 {
		return types.NewNullDuration(0, false)
	}
	// Basically, the ticker rate is time.Duration(1/arrivalRate). Considering
	// that time.Duration is represented as int64 nanoseconds, no meaningful
	// precision is likely to be lost here...
	result, _ := new(big.Rat).Inv(scaledArrivalRate).Float64()
	return types.NewNullDuration(time.Duration(result), true)
}

// getArrivalRatePerSec returns the iterations per second rate.
func getArrivalRatePerSec(scaledArrivalRate *big.Rat) *big.Rat {
	perSecRate := big.NewRat(int64(time.Second), 1)
	return perSecRate.Mul(perSecRate, scaledArrivalRate)
}

func getVUActivationParams(
	ctx context.Context, conf BaseConfig, deactivateCallback func(lib.InitializedVU),
) *lib.VUActivationParams {
	return &lib.VUActivationParams{
		RunContext:         ctx,
		Scenario:           conf.Name,
		Exec:               conf.GetExec(),
		Env:                conf.GetEnv(),
		Tags:               conf.GetTags(),
		DeactivateCallback: deactivateCallback,
	}
}
<|endoftext|>


<commit_before>package native

/*
int StartApp(char *setupTitle, char *appName, char *imageBytes, int imageLen);
void SetLabel(char *cString);
void SetProgress(int value);
char *ValidateBundle(char *bundlePath);
int LaunchBundle(char *bundlePath);
void Quit();
*/
import "C"

import (
	"context"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"time"
	"unsafe"

	"github.com/itchio/itch-setup/bindata"
	"github.com/itchio/itch-setup/cl"
	"github.com/itchio/itch-setup/setup"
	"github.com/itchio/ox/macox"
	"github.com/pkg/errors"
)

type nativeCore struct {
	cli                  cl.CLI
	selfPath             string
	roamingSetupPath     string
	homeApplicationsPath string
}

var globalNc *nativeCore

// NewCore returns a macOS-specific Core implementation
func NewCore(cli cl.CLI) (Core, error) {
	nc := &nativeCore{
		cli: cli,
	}

	appSupportPath, err := macox.GetApplicationSupportPath()
	if err != nil {
		return nil, err
	}
	nc.roamingSetupPath = filepath.Join(appSupportPath, fmt.Sprintf("%s-setup", cli.AppName))

	log.Printf("Base dir: %s", nc.roamingSetupPath)

	selfPath, err := os.Executable()
	if err != nil {
		return nil, err
	}
	nc.selfPath = selfPath
	log.Printf("Self path: %s", nc.selfPath)

	homePath, err := macox.GetHomeDirectory()
	if err != nil {
		return nil, err
	}
	nc.homeApplicationsPath = filepath.Join(homePath, "Applications")
	log.Printf("Home Applications path: %s", nc.homeApplicationsPath)

	globalNc = nc

	return nc, nil
}

func (nc *nativeCore) Install() error {
	cli := nc.cli
	setupTitle := cli.Localizer.T("setup.window.title", map[string]string{"app_name": cli.AppName})

	// thanks, go-bindata!
	imageData, err := bindata.Asset(fmt.Sprintf("data/installer-%s.png", cli.AppName))
	if err != nil {
		log.Printf("Installer image not found :()")
		return nil
	}

	imageBytes := unsafe.Pointer(&imageData[0])
	imageLen := C.int(len(imageData))
	C.StartApp(C.CString(setupTitle), C.CString(cli.AppName), (*C.char)(imageBytes), imageLen)
	return nil
}

func (nc *nativeCore) Uninstall() error {
	return errors.Errorf("uninstall: stub!")
}

func (nc *nativeCore) Upgrade() error {
	cli := nc.cli

	mv, err := nc.newMultiverse()
	if err != nil {
		return err
	}

	installer := setup.NewInstaller(setup.InstallerSettings{
		Localizer: cli.Localizer,
		AppName:   cli.AppName,
	})
	res, err := installer.Upgrade(mv)
	if err != nil {
		return err
	}

	// TODO: do we have something to do if res.DidUpgrade is true?

	return nil
}

func (nc *nativeCore) Relaunch() error {
	pid := nc.cli.RelaunchPID

	ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
	defer cancel()
	setup.WaitForProcessToExit(ctx, pid)

	mv, err := nc.newMultiverse()
	if err != nil {
		return err
	}

	if mv.HasReadyPending() {
		err = mv.MakeReadyCurrent()
		if err != nil {
			return err
		}
	}

	nc.tryLaunchCurrent(mv)
	return nil
}

func (nc *nativeCore) ErrorDialog(err error) {
	// TODO: use cocoa for this?
	log.Fatalf("Fatal error: %+v", err)
}

//export StartItchSetup
func StartItchSetup() {
	var installer *setup.Installer
	nc := globalNc
	cli := nc.cli

	mv, err := nc.newMultiverse()
	if err != nil {
		nc.ErrorDialog(err)
	}

	if cli.Silent {
		C.SetLabel(C.CString("Silent install mode is not supported on macOS"))
		return
	}

	if cli.PreferLaunch {
		log.Printf("--prefer-launch passed, looking for valid install")
		err := nc.tryLaunchCurrent(mv)
		if err != nil {
			log.Printf("Could not launch current: %v", err)
			log.Printf("Carrying on with install")
		}
	}

	installer = setup.NewInstaller(setup.InstallerSettings{
		Localizer: cli.Localizer,
		AppName:   cli.AppName,
		OnError: func(err error) {
			log.Printf("Error: %+v", err)
			C.SetLabel(C.CString(fmt.Sprintf("%+v", err)))
		},
		OnFinish: func(source setup.InstallSource) {
			err := nc.tryLaunchCurrent(mv)
			if err != nil {
				nc.ErrorDialog(err)
			}

			C.Quit()
		},
		OnProgress: func(progress float64) {
			C.SetProgress(C.int(progress * 1000.0))
		},
		OnProgressLabel: func(label string) {
			C.SetLabel(C.CString(label))
		},
	})
	installer.WarmUp()

	installer.Install(mv)
}

func (nc *nativeCore) tryLaunchCurrent(mv setup.Multiverse) error {
	b := mv.GetCurrentVersion()
	if b == nil {
		return errors.Errorf("No valid version of %s found installed", nc.cli.AppName)
	}

	log.Printf("Launching (%s) from (%s)", b.Version, b.Path)
	if C.LaunchBundle(C.CString(b.Path)) == 0 {
		return errors.Errorf("Could not launch (%s)", b.Path)
	}

	log.Printf("Bundle launched successfully, getting out of the way")
	C.Quit()

	// unreachable, but the go compiler doesn't know it
	return nil
}

func (nc *nativeCore) validateBundle(bundlePath string) error {
	log.Printf("Making sure (%s) is signed and valid", bundlePath)

	result := C.ValidateBundle(C.CString(bundlePath))
	if result != nil {
		return errors.Errorf("Bundle (%s) invalid: %s", bundlePath, C.GoString(result))
	}
	return nil
}

func (nc *nativeCore) newMultiverse() (setup.Multiverse, error) {
	return setup.NewMultiverse(&setup.MultiverseParams{
		AppName:         nc.cli.AppName,
		BaseDir:         nc.roamingSetupPath,
		ApplicationsDir: nc.homeApplicationsPath,

		OnValidate: nc.validateBundle,
	})
}
<commit_msg>Fix macOS build<commit_after>package native

/*
int StartApp(char *setupTitle, char *appName, char *imageBytes, int imageLen);
void SetLabel(char *cString);
void SetProgress(int value);
char *ValidateBundle(char *bundlePath);
int LaunchBundle(char *bundlePath);
void Quit();
*/
import "C"

import (
	"context"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"time"
	"unsafe"

	"github.com/itchio/itch-setup/bindata"
	"github.com/itchio/itch-setup/cl"
	"github.com/itchio/itch-setup/setup"
	"github.com/itchio/ox/macox"
	"github.com/pkg/errors"
)

type nativeCore struct {
	cli                  cl.CLI
	selfPath             string
	roamingSetupPath     string
	homeApplicationsPath string
}

var globalNc *nativeCore

// NewCore returns a macOS-specific Core implementation
func NewCore(cli cl.CLI) (Core, error) {
	nc := &nativeCore{
		cli: cli,
	}

	appSupportPath, err := macox.GetApplicationSupportPath()
	if err != nil {
		return nil, err
	}
	nc.roamingSetupPath = filepath.Join(appSupportPath, fmt.Sprintf("%s-setup", cli.AppName))

	log.Printf("Base dir: %s", nc.roamingSetupPath)

	selfPath, err := os.Executable()
	if err != nil {
		return nil, err
	}
	nc.selfPath = selfPath
	log.Printf("Self path: %s", nc.selfPath)

	homePath, err := macox.GetHomeDirectory()
	if err != nil {
		return nil, err
	}
	nc.homeApplicationsPath = filepath.Join(homePath, "Applications")
	log.Printf("Home Applications path: %s", nc.homeApplicationsPath)

	globalNc = nc

	return nc, nil
}

func (nc *nativeCore) Install() error {
	cli := nc.cli
	setupTitle := cli.Localizer.T("setup.window.title", map[string]string{"app_name": cli.AppName})

	// thanks, go-bindata!
	imageData, err := bindata.Asset(fmt.Sprintf("data/installer-%s.png", cli.AppName))
	if err != nil {
		log.Printf("Installer image not found :()")
		return nil
	}

	imageBytes := unsafe.Pointer(&imageData[0])
	imageLen := C.int(len(imageData))
	C.StartApp(C.CString(setupTitle), C.CString(cli.AppName), (*C.char)(imageBytes), imageLen)
	return nil
}

func (nc *nativeCore) Uninstall() error {
	return errors.Errorf("uninstall: stub!")
}

func (nc *nativeCore) Upgrade() error {
	cli := nc.cli

	mv, err := nc.newMultiverse()
	if err != nil {
		return err
	}

	installer := setup.NewInstaller(setup.InstallerSettings{
		Localizer: cli.Localizer,
		AppName:   cli.AppName,
	})
	res, err := installer.Upgrade(mv)
	if err != nil {
		return err
	}

	if res.DidUpgrade {
		log.Printf("Did upgrade! But nothing to do about it on macOS.")
	}

	return nil
}

func (nc *nativeCore) Relaunch() error {
	pid := nc.cli.RelaunchPID

	ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
	defer cancel()
	setup.WaitForProcessToExit(ctx, pid)

	mv, err := nc.newMultiverse()
	if err != nil {
		return err
	}

	if mv.HasReadyPending() {
		err = mv.MakeReadyCurrent()
		if err != nil {
			return err
		}
	}

	nc.tryLaunchCurrent(mv)
	return nil
}

func (nc *nativeCore) ErrorDialog(err error) {
	// TODO: use cocoa for this?
	log.Fatalf("Fatal error: %+v", err)
}

//export StartItchSetup
func StartItchSetup() {
	var installer *setup.Installer
	nc := globalNc
	cli := nc.cli

	mv, err := nc.newMultiverse()
	if err != nil {
		nc.ErrorDialog(err)
	}

	if cli.Silent {
		C.SetLabel(C.CString("Silent install mode is not supported on macOS"))
		return
	}

	if cli.PreferLaunch {
		log.Printf("--prefer-launch passed, looking for valid install")
		err := nc.tryLaunchCurrent(mv)
		if err != nil {
			log.Printf("Could not launch current: %v", err)
			log.Printf("Carrying on with install")
		}
	}

	installer = setup.NewInstaller(setup.InstallerSettings{
		Localizer: cli.Localizer,
		AppName:   cli.AppName,
		OnError: func(err error) {
			log.Printf("Error: %+v", err)
			C.SetLabel(C.CString(fmt.Sprintf("%+v", err)))
		},
		OnFinish: func(source setup.InstallSource) {
			err := nc.tryLaunchCurrent(mv)
			if err != nil {
				nc.ErrorDialog(err)
			}

			C.Quit()
		},
		OnProgress: func(progress float64) {
			C.SetProgress(C.int(progress * 1000.0))
		},
		OnProgressLabel: func(label string) {
			C.SetLabel(C.CString(label))
		},
	})
	installer.WarmUp()

	installer.Install(mv)
}

func (nc *nativeCore) tryLaunchCurrent(mv setup.Multiverse) error {
	b := mv.GetCurrentVersion()
	if b == nil {
		return errors.Errorf("No valid version of %s found installed", nc.cli.AppName)
	}

	log.Printf("Launching (%s) from (%s)", b.Version, b.Path)
	if C.LaunchBundle(C.CString(b.Path)) == 0 {
		return errors.Errorf("Could not launch (%s)", b.Path)
	}

	log.Printf("Bundle launched successfully, getting out of the way")
	C.Quit()

	// unreachable, but the go compiler doesn't know it
	return nil
}

func (nc *nativeCore) validateBundle(bundlePath string) error {
	log.Printf("Making sure (%s) is signed and valid", bundlePath)

	result := C.ValidateBundle(C.CString(bundlePath))
	if result != nil {
		return errors.Errorf("Bundle (%s) invalid: %s", bundlePath, C.GoString(result))
	}
	return nil
}

func (nc *nativeCore) newMultiverse() (setup.Multiverse, error) {
	return setup.NewMultiverse(&setup.MultiverseParams{
		AppName:         nc.cli.AppName,
		BaseDir:         nc.roamingSetupPath,
		ApplicationsDir: nc.homeApplicationsPath,

		OnValidate: nc.validateBundle,
	})
}
<|endoftext|>


<commit_before>package xlsx

import (
	"fmt"
	"regexp"
	"text/template"
	"time"
)

// Templates for the various XML content in XLST files
var (
	TemplateContentTypes          *template.Template
	TemplateRelationships         *template.Template
	TemplateWorkbook              *template.Template
	TemplateWorkbookRelationships *template.Template
	TemplateStyles                *template.Template
	TemplateStringLookups         *template.Template
	TemplateSheetStart            *template.Template
	TemplateApp                   *template.Template
	TemplateCore                  *template.Template
)

// Template function for integer addition. This is useful to convert between
// zero-based and one-based array offsets within templates
func plus(i int, n int) string {
	return fmt.Sprintf("%d", i+n)
}

// Template function for time formatting
func timeFormat(t time.Time) string {
	return t.Format(time.RFC3339)
}

func init() {
	re := regexp.MustCompile("\n[\t\n\f\r ]*")
	funcMap := template.FuncMap{"plus": plus, "timeFormat": timeFormat}

	TemplateContentTypes = template.Must(template.New("templateContentTypes").Funcs(funcMap).Parse(re.ReplaceAllLiteralString(templateContentTypes, "")))
	TemplateRelationships = template.Must(template.New("templateRelationships").Funcs(funcMap).Parse(re.ReplaceAllLiteralString(templateRelationships, "")))
	TemplateWorkbook = template.Must(template.New("templateWorkbook").Funcs(funcMap).Parse(re.ReplaceAllLiteralString(templateWorkbook, "")))
	TemplateWorkbookRelationships = template.Must(template.New("templateWorkbookRelationships").Funcs(funcMap).Parse(re.ReplaceAllLiteralString(templateWorkbookRelationships, "")))
	TemplateStyles = template.Must(template.New("templateStyles").Funcs(funcMap).Parse(re.ReplaceAllLiteralString(templateStyles, "")))
	TemplateStringLookups = template.Must(template.New("templateStringLookups").Funcs(funcMap).Parse(re.ReplaceAllLiteralString(templateStringLookups, "")))
	TemplateSheetStart = template.Must(template.New("templateSheetStart").Funcs(funcMap).Parse(re.ReplaceAllLiteralString(templateSheetStart, "")))
	TemplateApp = template.Must(template.New("templateApp").Funcs(funcMap).Parse(re.ReplaceAllLiteralString(templateApp, "")))
	TemplateCore = template.Must(template.New("templateCore").Funcs(funcMap).Parse(re.ReplaceAllLiteralString(templateCore, "")))
}

const templateContentTypes = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
      <Default Extension="xml" ContentType="application/xml"/>
      <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
      <Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
      {{ range $i, $_ := . }}
      <Override PartName="/xl/worksheets/sheet{{plus $i 1}}.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>
      {{ end }}
      <Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>
      <Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>
      <Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
      <Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>
  </Types>`

const templateRelationships = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
      <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
      <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
      <Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
  </Relationships>`

const templateWorkbook = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  <workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
      <fileVersion appName="xl" lastEdited="5" lowestEdited="5" rupBuild="9303"/>
      <workbookPr defaultThemeVersion="124226"/>
      <bookViews>
          <workbookView xWindow="480" yWindow="60" windowWidth="18195" windowHeight="8505"/>
      </bookViews>
      <sheets>
          {{ range $i, $e := . }}
          <sheet name="{{$e}}" sheetId="{{plus $i 1 }}" r:id="rId{{plus $i 1}}"/>
          {{ end }}
      </sheets>
      <calcPr calcId="145621"/>
  </workbook>`

const templateWorkbookRelationships = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
      {{ range $i, $_ := . }}
      <Relationship Id="rId{{plus $i 1}}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet{{plus $i 1}}.xml"/>
      {{ end }}
      {{ $i := len . }}
      <Relationship Id="rId{{plus $i 1}}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>
      <Relationship Id="rId{{plus $i 2}}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/>
  </Relationships>`

const templateStyles = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  `

const templateStringLookups = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="{{len .}}" uniqueCount="{{len .}}">
{{range .}}<si><t>{{.}}</t></si>{{end}}
</sst>`

const templateSheetStart = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">
            <sheetViews>
        <sheetView workbookViewId="0"/>
      </sheetViews>
      <sheetFormatPr defaultRowHeight="15" x14ac:dyDescent="0.25"/>
        <cols>
          {{range $i, $e := .Cols}}
          <col min="{{plus $i 1}}" max="{{plus $i 1}}" width="{{$e.Width}}" customWidth="1" style="1"/>
          {{end}}
        </cols>
      <sheetData>`

const templateApp = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  <Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">
  <Application>None</Application>
  <DocSecurity>0</DocSecurity>
  <ScaleCrop>false</ScaleCrop>
  <HeadingPairs>
    <vt:vector size="2" baseType="variant">
      <vt:variant>
        <vt:lpstr>Worksheets</vt:lpstr>
      </vt:variant>
      <vt:variant>
        <vt:i4>{{ len . }}</vt:i4>
      </vt:variant>
    </vt:vector>
  </HeadingPairs>
  <TitlesOfParts>
    <vt:vector size="1" baseType="lpstr">
      {{ range $i, $e := . }}
      <vt:lpstr>{{$e}}</vt:lpstr>
      {{ end }}
    </vt:vector>
  </TitlesOfParts>
  <LinksUpToDate>false</LinksUpToDate>
  <SharedDoc>false</SharedDoc>
  <HyperlinksChanged>false</HyperlinksChanged>
</Properties>`

const templateCore = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  <cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <dc:creator>{{.CreatedBy}}</dc:creator>
    <cp:lastModifiedBy>{{.ModifiedBy}}</cp:lastModifiedBy>
    <dcterms:created xsi:type="dcterms:W3CDTF">{{timeFormat .CreatedAt}}</dcterms:created>
    <dcterms:modified xsi:type="dcterms:W3CDTF">{{timeFormat .ModifiedAt}}</dcterms:modified>
  </cp:coreProperties>`
<commit_msg>Add jpeg content type<commit_after>package xlsx

import (
	"fmt"
	"regexp"
	"text/template"
	"time"
)

// Templates for the various XML content in XLST files
var (
	TemplateContentTypes          *template.Template
	TemplateRelationships         *template.Template
	TemplateWorkbook              *template.Template
	TemplateWorkbookRelationships *template.Template
	TemplateStyles                *template.Template
	TemplateStringLookups         *template.Template
	TemplateSheetStart            *template.Template
	TemplateApp                   *template.Template
	TemplateCore                  *template.Template
)

// Template function for integer addition. This is useful to convert between
// zero-based and one-based array offsets within templates
func plus(i int, n int) string {
	return fmt.Sprintf("%d", i+n)
}

// Template function for time formatting
func timeFormat(t time.Time) string {
	return t.Format(time.RFC3339)
}

func init() {
	re := regexp.MustCompile("\n[\t\n\f\r ]*")
	funcMap := template.FuncMap{"plus": plus, "timeFormat": timeFormat}

	TemplateContentTypes = template.Must(template.New("templateContentTypes").Funcs(funcMap).Parse(re.ReplaceAllLiteralString(templateContentTypes, "")))
	TemplateRelationships = template.Must(template.New("templateRelationships").Funcs(funcMap).Parse(re.ReplaceAllLiteralString(templateRelationships, "")))
	TemplateWorkbook = template.Must(template.New("templateWorkbook").Funcs(funcMap).Parse(re.ReplaceAllLiteralString(templateWorkbook, "")))
	TemplateWorkbookRelationships = template.Must(template.New("templateWorkbookRelationships").Funcs(funcMap).Parse(re.ReplaceAllLiteralString(templateWorkbookRelationships, "")))
	TemplateStyles = template.Must(template.New("templateStyles").Funcs(funcMap).Parse(re.ReplaceAllLiteralString(templateStyles, "")))
	TemplateStringLookups = template.Must(template.New("templateStringLookups").Funcs(funcMap).Parse(re.ReplaceAllLiteralString(templateStringLookups, "")))
	TemplateSheetStart = template.Must(template.New("templateSheetStart").Funcs(funcMap).Parse(re.ReplaceAllLiteralString(templateSheetStart, "")))
	TemplateApp = template.Must(template.New("templateApp").Funcs(funcMap).Parse(re.ReplaceAllLiteralString(templateApp, "")))
	TemplateCore = template.Must(template.New("templateCore").Funcs(funcMap).Parse(re.ReplaceAllLiteralString(templateCore, "")))
}

const templateContentTypes = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
      <Default Extension="xml" ContentType="application/xml"/>
      <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
	  <Default Extension="jpeg" ContentType="image/jpeg"/>
      <Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
      {{ range $i, $_ := . }}
      <Override PartName="/xl/worksheets/sheet{{plus $i 1}}.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>
      {{ end }}
      <Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>
      <Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>
      <Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
      <Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>
  </Types>`

const templateRelationships = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
      <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
      <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
      <Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
  </Relationships>`

const templateWorkbook = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  <workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
      <fileVersion appName="xl" lastEdited="5" lowestEdited="5" rupBuild="9303"/>
      <workbookPr defaultThemeVersion="124226"/>
      <bookViews>
          <workbookView xWindow="480" yWindow="60" windowWidth="18195" windowHeight="8505"/>
      </bookViews>
      <sheets>
          {{ range $i, $e := . }}
          <sheet name="{{$e}}" sheetId="{{plus $i 1 }}" r:id="rId{{plus $i 1}}"/>
          {{ end }}
      </sheets>
      <calcPr calcId="145621"/>
  </workbook>`

const templateWorkbookRelationships = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
      {{ range $i, $_ := . }}
      <Relationship Id="rId{{plus $i 1}}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet{{plus $i 1}}.xml"/>
      {{ end }}
      {{ $i := len . }}
      <Relationship Id="rId{{plus $i 1}}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>
      <Relationship Id="rId{{plus $i 2}}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/>
  </Relationships>`

const templateStyles = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  `

const templateStringLookups = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="{{len .}}" uniqueCount="{{len .}}">
{{range .}}<si><t>{{.}}</t></si>{{end}}
</sst>`

const templateSheetStart = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">
            <sheetViews>
        <sheetView workbookViewId="0"/>
      </sheetViews>
      <sheetFormatPr defaultRowHeight="15" x14ac:dyDescent="0.25"/>
        <cols>
          {{range $i, $e := .Cols}}
          <col min="{{plus $i 1}}" max="{{plus $i 1}}" width="{{$e.Width}}" customWidth="1" style="1"/>
          {{end}}
        </cols>
      <sheetData>`

const templateApp = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  <Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">
  <Application>None</Application>
  <DocSecurity>0</DocSecurity>
  <ScaleCrop>false</ScaleCrop>
  <HeadingPairs>
    <vt:vector size="2" baseType="variant">
      <vt:variant>
        <vt:lpstr>Worksheets</vt:lpstr>
      </vt:variant>
      <vt:variant>
        <vt:i4>{{ len . }}</vt:i4>
      </vt:variant>
    </vt:vector>
  </HeadingPairs>
  <TitlesOfParts>
    <vt:vector size="1" baseType="lpstr">
      {{ range $i, $e := . }}
      <vt:lpstr>{{$e}}</vt:lpstr>
      {{ end }}
    </vt:vector>
  </TitlesOfParts>
  <LinksUpToDate>false</LinksUpToDate>
  <SharedDoc>false</SharedDoc>
  <HyperlinksChanged>false</HyperlinksChanged>
</Properties>`

const templateCore = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  <cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <dc:creator>{{.CreatedBy}}</dc:creator>
    <cp:lastModifiedBy>{{.ModifiedBy}}</cp:lastModifiedBy>
    <dcterms:created xsi:type="dcterms:W3CDTF">{{timeFormat .CreatedAt}}</dcterms:created>
    <dcterms:modified xsi:type="dcterms:W3CDTF">{{timeFormat .ModifiedAt}}</dcterms:modified>
  </cp:coreProperties>`
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"html/template"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"time"
)

type templateSet struct {
	root  string
	data  *template.Template
	mtime time.Time
}

type viewmodel interface {
	TemplateFile() string
}

var htmlInterElementWhitespace = regexp.MustCompile(">\\s+<")

func (ts *templateSet) Render(w http.ResponseWriter, code int, vm viewmodel) error {
	name := vm.TemplateFile()
	stat, err := os.Stat(filepath.Join(ts.root, name))
	if ts.data == nil || (err == nil && stat.ModTime().After(ts.mtime)) {
		ts.data, err = template.ParseGlob(filepath.Join(ts.root, "*"))
		if err != nil {
			return err
		}
		ts.mtime = time.Now()
	}
	if t := ts.data.Lookup(name); t != nil {
		buf := &bytes.Buffer{}
		if err = t.Execute(buf, vm); err != nil {
			return err
		}
		w.Header().Set("Content-Type", "text/html; encoding=utf-8")
		w.WriteHeader(code)
		w.Write(htmlInterElementWhitespace.ReplaceAll(buf.Bytes(), []byte("> <")))
		return nil
	}
	return fmt.Errorf("template not found: %s", name)
}

var Render = (&templateSet{root: "templates"}).Render

type ErrorTemplate struct {
	Code    int
	Message string
}

func (_ ErrorTemplate) TemplateFile() string {
	return "error.html"
}

func (e ErrorTemplate) DisplayMessage() string {
	if e.Message != "" {
		return e.Message
	}

	switch e.Code {
	case 403:
		return "FOREBODEN."
	case 404:
		return "There is nothing here."
	case 405:
		return "Invalid HTTP method."
	case 418:
		return "I'm a little teapot."
	case 500:
		return "✋☠❄☜☼☠✌☹ 💧☜☼✞☜☼ ☜☼☼⚐☼"
	default:
		return "ERROR"
	}
}

func (e ErrorTemplate) DisplayComment() string {
	switch e.Code {
	case 403:
		return "you're just a dirty hacker, aren't you?"
	case 404:
		return "(The dog absorbs the page.)"
	case 418:
		return "Would you like a cup of tea?"
	case 500:
		return "Try submitting a bug report."
	default:
		return "Try something else."
	}
}

func RenderError(w http.ResponseWriter, code int, message string) error {
	w.Header().Set("Cache-Control", "no-cache")
	return Render(w, code, ErrorTemplate{code, message})
}

func RenderInvalidMethod(w http.ResponseWriter, methods string) error {
	w.Header().Set("Allow", methods)
	return RenderError(w, http.StatusMethodNotAllowed, "")
}

type Landing struct {
	User *UserData
}

func (_ Landing) TemplateFile() string {
	return "landing.html"
}

type Room struct {
	ID       string
	Editable bool
	Online   bool
	Meta     *StreamMetadata
	User     *UserData
}

func (_ Room) TemplateFile() string {
	return "room.html"
}

func (_ Room) Live() bool {
	return true
}

type Recordings struct {
	ID    string
	Owned bool
	User  *UserData
	*StreamHistory
}

func (_ Recordings) TemplateFile() string {
	return "recordings.html"
}

type Recording struct {
	ID       string
	Editable bool // false
	Online   bool // false
	Meta     *StreamRecording
	User     *UserData
}

func (_ Recording) TemplateFile() string {
	return "room.html"
}

func (r Recording) Live() bool {
	return false
}

type UserNew int
type UserLogin int
type UserRestore int
type UserConfig struct {
	User *UserData
}

func (_ UserNew) TemplateFile() string {
	return "user-new.html"
}

func (_ UserLogin) TemplateFile() string {
	return "user-login.html"
}

func (_ UserRestore) TemplateFile() string {
	return "user-restore.html"
}

func (_ UserConfig) TemplateFile() string {
	return "user-config.html"
}
<commit_msg>Allow marking any value as pre-escaped HTML.<commit_after>package main

import (
	"bytes"
	"fmt"
	"html/template"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"time"
)

type templateSet struct {
	root  string
	data  *template.Template
	mtime time.Time
}

type viewmodel interface {
	TemplateFile() string
}

var templateFuncs = template.FuncMap{
	"unsafe": func(s interface{}) template.HTML {
		return template.HTML(fmt.Sprint(s))
	},
}

var htmlInterElementWhitespace = regexp.MustCompile(">\\s+<")

func (ts *templateSet) Render(w http.ResponseWriter, code int, vm viewmodel) error {
	name := vm.TemplateFile()
	stat, err := os.Stat(filepath.Join(ts.root, name))
	if ts.data == nil || (err == nil && stat.ModTime().After(ts.mtime)) {
		ts.data, err = template.New(ts.root).Funcs(templateFuncs).ParseGlob(filepath.Join(ts.root, "*"))
		if err != nil {
			return err
		}
		ts.mtime = time.Now()
	}
	if t := ts.data.Lookup(name); t != nil {
		buf := &bytes.Buffer{}
		if err = t.Execute(buf, vm); err != nil {
			return err
		}
		w.Header().Set("Content-Type", "text/html; encoding=utf-8")
		w.WriteHeader(code)
		w.Write(htmlInterElementWhitespace.ReplaceAll(buf.Bytes(), []byte("> <")))
		return nil
	}
	return fmt.Errorf("template not found: %s", name)
}

var Render = (&templateSet{root: "templates"}).Render

type ErrorTemplate struct {
	Code    int
	Message string
}

func (_ ErrorTemplate) TemplateFile() string {
	return "error.html"
}

func (e ErrorTemplate) DisplayMessage() string {
	if e.Message != "" {
		return e.Message
	}

	switch e.Code {
	case 403:
		return "FOREBODEN."
	case 404:
		return "There is nothing here."
	case 405:
		return "Invalid HTTP method."
	case 418:
		return "I'm a little teapot."
	case 500:
		return "✋☠❄☜☼☠✌☹ 💧☜☼✞☜☼ ☜☼☼⚐☼"
	default:
		return "ERROR"
	}
}

func (e ErrorTemplate) DisplayComment() string {
	switch e.Code {
	case 403:
		return "you're just a dirty hacker, aren't you?"
	case 404:
		return "(The dog absorbs the page.)"
	case 418:
		return "Would you like a cup of tea?"
	case 500:
		return "Try submitting a bug report."
	default:
		return "Try something else."
	}
}

func RenderError(w http.ResponseWriter, code int, message string) error {
	w.Header().Set("Cache-Control", "no-cache")
	return Render(w, code, ErrorTemplate{code, message})
}

func RenderInvalidMethod(w http.ResponseWriter, methods string) error {
	w.Header().Set("Allow", methods)
	return RenderError(w, http.StatusMethodNotAllowed, "")
}

type Landing struct {
	User *UserData
}

func (_ Landing) TemplateFile() string {
	return "landing.html"
}

type Room struct {
	ID       string
	Editable bool
	Online   bool
	Meta     *StreamMetadata
	User     *UserData
}

func (_ Room) TemplateFile() string {
	return "room.html"
}

func (_ Room) Live() bool {
	return true
}

type Recordings struct {
	ID    string
	Owned bool
	User  *UserData
	*StreamHistory
}

func (_ Recordings) TemplateFile() string {
	return "recordings.html"
}

type Recording struct {
	ID       string
	Editable bool // false
	Online   bool // false
	Meta     *StreamRecording
	User     *UserData
}

func (_ Recording) TemplateFile() string {
	return "room.html"
}

func (r Recording) Live() bool {
	return false
}

type UserNew int
type UserLogin int
type UserRestore int
type UserConfig struct {
	User *UserData
}

func (_ UserNew) TemplateFile() string {
	return "user-new.html"
}

func (_ UserLogin) TemplateFile() string {
	return "user-login.html"
}

func (_ UserRestore) TemplateFile() string {
	return "user-restore.html"
}

func (_ UserConfig) TemplateFile() string {
	return "user-config.html"
}
<|endoftext|>


<commit_before>package tera

import (
	"fmt"
	"testing"
	"time"
)

func logExecTime(start time.Time, prefix string) {
	elapsed_ms := time.Since(start) / time.Millisecond
	fmt.Printf("Performance: %s cost %d ms.\n", prefix, elapsed_ms)
}

func TestTera(*testing.T) {
	fmt.Println("Hello terago!")
	client, c_err := NewClient("./tera.flag", "terago")
	defer client.Close()
	if c_err != nil {
		panic("tera.NewClient error: " + c_err.Error())
	}

	table, t_err := client.OpenTable("terago")
	defer table.Close()
	if t_err != nil {
		panic("tera.OpenTable error: " + t_err.Error())
	}

	{
		defer logExecTime(time.Now(), "PutKV")
		p_err := table.PutKV("hello", "terago", 10)
		if p_err != nil {
			panic("put key value error: " + p_err.Error())
		}
	}

	// get an exist key value, return value
	value, g_err := table.GetKV("hello")
	if g_err != nil {
		panic("get key value error: " + g_err.Error())
	}
	fmt.Printf("get key[%s] value[%s].\n", "hello", value)

	// get a not-exist key value, return "not found"
	value, g_err = table.GetKV("hell")
	if g_err == nil {
		panic("get key value should fail: " + g_err.Error())
	}

	d_err := table.DeleteKV("hello")
	if d_err != nil {
		panic("delete key value error: " + g_err.Error())
	}

	value, g_err = table.GetKV("hello")
	if g_err == nil {
		panic("get key value should fail: " + g_err.Error())
	}
}
<commit_msg>modify unit test perf<commit_after>package tera

import (
	"fmt"
	"testing"
	"time"
)

func logExecTime(start time.Time, prefix string) {
	elapsed_ms := time.Since(start) / time.Millisecond
	fmt.Printf("Performance: %s cost %d ms.\n", prefix, elapsed_ms)
}

func TestTera(*testing.T) {
	fmt.Println("Hello terago!")
	client, c_err := NewClient("./tera.flag", "terago")
	defer client.Close()
	if c_err != nil {
		panic("tera.NewClient error: " + c_err.Error())
	}

	table, t_err := client.OpenTable("terago")
	defer table.Close()
	if t_err != nil {
		panic("tera.OpenTable error: " + t_err.Error())
	}

	{
		defer logExecTime(time.Now(), "PutKV")
		p_err := table.PutKV("hello", "terago", 10)
		if p_err != nil {
			panic("put key value error: " + p_err.Error())
		}
	}

	{
		defer logExecTime(time.Now(), "GetKV")
		// get an exist key value, return value
		value, g_err := table.GetKV("hello")
		if g_err != nil {
			panic("get key value error: " + g_err.Error())
		}
		fmt.Printf("get key[%s] value[%s].\n", "hello", value)
	}

	{
		defer logExecTime(time.Now(), "GetKV_NotExist")
		// get a not-exist key value, return "not found"
		_, g_err := table.GetKV("hell")
		if g_err == nil {
			panic("get key value should fail: " + g_err.Error())
		}
	}

	{
		defer logExecTime(time.Now(), "DeleteKV")
		d_err := table.DeleteKV("hello")
		if d_err != nil {
			panic("delete key value error: " + d_err.Error())
		}
	}

	_, g_err := table.GetKV("hello")
	if g_err == nil {
		panic("get key value should fail: " + g_err.Error())
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"os/signal"
	"strconv"
	"strings"
	"syscall"
	"time"

	"code.google.com/p/go.net/websocket"
	"github.com/heroku/hk/term"
	"github.com/kr/pty"
	"github.com/nu7hatch/gouuid"
)

var daemon *bool = flag.Bool("d", false, "run the server daemon")
var readonly *bool = flag.Bool("r", false, "only allow participants viewing capability")
var server *string = flag.String("s", "young-dusk-7491.herokuapp.com:80", "use a different server")

type session struct {
	name         string
	readonly     bool
	participants []io.ReadWriteCloser
	presenterW   io.WriteCloser
	presenterR   io.ReadCloser
	participantW io.WriteCloser
	participantR io.ReadCloser
}

type flushWriter struct {
	f http.Flusher
	w io.Writer
}

func (fw flushWriter) Write(p []byte) (n int, err error) {
	n, err = fw.w.Write(p)
	if fw.f != nil {
		fw.f.Flush()
	}
	return
}

func present() {
	name, err := uuid.NewV4()
	if err != nil {
		panic(err)
	}
	resp, err := http.PostForm("http://"+*server+"/sessions", url.Values{"name": {name.String()}})
	if err != nil {
		panic(err)
	}
	var body []byte
	if resp.StatusCode == 200 {
		body, err = ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		if err != nil {
			panic(err)
		}
	} else {
		resp.Body.Close()
		log.Fatal("unable to open session")
	}
	fmt.Println(string(body))

	conn, err := websocket.Dial("ws://"+*server+"/"+name.String()+"/presenter", "", "http://"+*server)
	if err != nil {
		panic(err)
	}
	cols, err := term.Cols()
	if err != nil {
		panic(err)
	}
	lines, err := term.Lines()
	if err != nil {
		panic(err)
	}
	cmd := exec.Command(os.Getenv("SHELL"))
	cmd.Env = []string{
		"PS1=[termshare] \\W$ ",
		"TERM=" + os.Getenv("TERM"),
		"HOME=" + os.Getenv("HOME"),
		"USER=" + os.Getenv("USER"),
		"COLUMNS=" + strconv.Itoa(cols),
		"LINES=" + strconv.Itoa(lines),
	}
	pty, err := pty.Start(cmd)
	if err != nil {
		panic(err)
	}
	if err := term.MakeRaw(os.Stdin); err != nil {
		panic(err)
	}
	exitSignal := make(chan os.Signal)
	signal.Notify(exitSignal, os.Interrupt, syscall.SIGTERM)
	go func() {
		<-exitSignal
		term.Restore(os.Stdin)
		os.Exit(0)
	}()
	defer term.Restore(os.Stdin)
	eof := make(chan bool, 1)
	go func() {
		io.Copy(io.MultiWriter(os.Stdout, conn), pty)
		eof <- true
	}()
	go func() {
		io.Copy(pty, os.Stdin)
		eof <- true
	}()
	go func() {
		io.Copy(pty, conn)
		eof <- true
	}()
	go func() {
		for {
			_, err := conn.Write([]byte("\x00"))
			if err != nil {
				return
			}
			time.Sleep(10 * time.Second)
		}
	}()
	<-eof
}

func participate(name string) {
	conn, err := websocket.Dial("ws://"+*server+"/"+name, "", "http://"+*server)
	if err != nil {
		panic(err)
	}
	if err := term.MakeRaw(os.Stdin); err != nil {
		panic(err)
	}
	exitSignal := make(chan os.Signal)
	signal.Notify(exitSignal, os.Interrupt, syscall.SIGTERM)
	go func() {
		<-exitSignal
		term.Restore(os.Stdin)
		os.Exit(0)
	}()
	defer term.Restore(os.Stdin)
	eof := make(chan bool, 1)
	go func() {
		io.Copy(os.Stdout, conn)
		eof <- true
	}()
	go func() {
		io.Copy(conn, os.Stdin)
		eof <- true
	}()
	<-eof
}

func main() {
	flag.Parse()

	// TODO: lock
	sessions := make(map[string]session)

	if *daemon {
		http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
			switch {
			case r.RequestURI == "/":
				http.Redirect(w, r, "http://progrium.viewdocs.io/termshare", 301)
			case r.RequestURI == "/favicon.ico":
				return
			case r.RequestURI == "/sessions" && r.Method == "POST":
				r.ParseForm()
				name := r.PostForm.Get("name")
				_, found := sessions[name]
				if found {
					w.WriteHeader(http.StatusConflict)
					return
				}
				s := session{
					name:     name,
					readonly: false,
				}
				s.presenterR, s.presenterW = io.Pipe()
				s.participantR, s.participantW = io.Pipe()
				sessions[name] = s
				log.Println(name + ": session created")
				w.Write([]byte("http://termsha.re/" + name + "\n"))
			case strings.HasSuffix(r.RequestURI, "/presenter"):
				parts := strings.Split(r.RequestURI, "/")
				name := parts[1]
				s, found := sessions[name]
				if !found {
					w.WriteHeader(http.StatusNotFound)
					return
				}
				if s.presenterW == nil {
					w.WriteHeader(http.StatusConflict)
					return
				}
				websocket.Handler(func(ws *websocket.Conn) {
					eof := make(chan bool, 1)
					go func() {
						io.Copy(s.presenterW, ws)
						eof <- true
					}()
					go func() {
						io.Copy(ws, s.participantR)
						eof <- true
					}()
					log.Println(name + ": presenter connected")
					<-eof
					delete(sessions, name)
				}).ServeHTTP(w, r)
			default:
				parts := strings.Split(r.RequestURI, "/")
				name := parts[1]
				s, found := sessions[name]
				if !found {
					w.WriteHeader(http.StatusNotFound)
					return
				}
				if s.presenterW == nil {
					w.WriteHeader(http.StatusConflict)
					return
				}
				if r.Header.Get("Upgrade") == "websocket" {
					websocket.Handler(func(ws *websocket.Conn) {
						s.participantW.Write([]byte("\x07")) // ding!
						eof := make(chan bool, 1)
						go func() {
							io.Copy(s.participantW, ws)
							eof <- true
						}()
						go func() {
							io.Copy(ws, s.presenterR)
							eof <- true
						}()
						log.Println(name + ": participant connected (websocket)")
						<-eof
					}).ServeHTTP(w, r)
				} else {
					s.participantW.Write([]byte("\x07")) // ding!
					fw := flushWriter{w: w}
					if f, ok := w.(http.Flusher); ok {
						fw.f = f
					}
					eof := make(chan bool, 1)
					go func() {
						io.Copy(fw, s.presenterR)
						eof <- true
					}()
					log.Println(name + ": participant connected (http stream)")
					<-eof
				}
			}
		})
		log.Println("Termshare server started...")
		log.Fatal(http.ListenAndServe(":"+os.Getenv("PORT"), nil))
	} else {
		if flag.Arg(0) == "" {
			present()
		} else {
			participate(flag.Arg(0))
		}
	}
}
<commit_msg>refactor and working copilot model with multiple viewers. also, buffering for copilot<commit_after>package main

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"

	"code.google.com/p/go.net/websocket"
	"github.com/heroku/hk/term"
	"github.com/kr/pty"
	"github.com/nu7hatch/gouuid"
)

var daemon *bool = flag.Bool("d", false, "run the server daemon")
var broadcast *bool = flag.Bool("b", false, "only allow readonly viewers and no copilot")
var private *bool = flag.Bool("p", false, "only allow a copilot and no viewers")
var server *string = flag.String("s", "young-dusk-7491.herokuapp.com:80", "use a different server")

type session struct {
	Name          string
	Broadcast     bool
	Private       bool
	Viewers       *viewers
	Pilot         io.ReadWriteCloser
	Copilot       io.ReadWriteCloser
	CopilotBuffer *bufferWriter
	EOF           chan struct{}
}

type sessions struct {
	sync.Mutex
	s map[string]*session
}

func (s sessions) Get(name string) (sess *session, err error) {
	s.Lock()
	defer s.Unlock()
	sess, found := s.s[name]
	if !found {
		err = errors.New("session not found")
		return
	}
	return
}

func (s sessions) Create(name string, broadcast, private bool) (*session, error) {
	if sess, _ := s.Get(name); sess != nil {
		return nil, errors.New("session already exists")
	}
	sess := &session{
		Name:          name,
		Broadcast:     broadcast,
		Private:       private,
		Viewers:       &viewers{v: make(map[io.Writer]struct{})},
		EOF:           make(chan struct{}),
		CopilotBuffer: &bufferWriter{},
	}
	s.Lock()
	defer s.Unlock()
	s.s[name] = sess
	return sess, nil
}

func (s sessions) Delete(name string) {
	s.Lock()
	defer s.Unlock()
	delete(s.s, name)
}

type viewers struct {
	sync.Mutex
	v map[io.Writer]struct{}
}

func (v viewers) Write(data []byte) (n int, err error) {
	v.Lock()
	defer v.Unlock()
	for w := range v.v {
		n, err = w.Write(data)
		if err != nil {
			delete(v.v, w)
		}
		if n != len(data) {
			err = io.ErrShortWrite
			return
		}
	}
	return len(data), nil
}

func (v viewers) Add(viewer io.Writer) {
	v.Lock()
	defer v.Unlock()
	v.v[viewer] = struct{}{}
}

type flushWriter struct {
	f http.Flusher
	w io.Writer
}

func (fw flushWriter) Write(p []byte) (n int, err error) {
	n, err = fw.w.Write(p)
	if fw.f != nil {
		fw.f.Flush()
	}
	return
}

func FlushWriter(writer io.Writer) flushWriter {
	fw := flushWriter{w: writer}
	if f, ok := writer.(http.Flusher); ok {
		fw.f = f
	}
	return fw
}

type bufferWriter struct {
	w *websocket.Conn
	b *bytes.Buffer
}

func (bw *bufferWriter) Write(p []byte) (n int, err error) {
	if bw.b == nil {
		bw.b = new(bytes.Buffer)
	}
	if bw.b.Len() > 0 && bw.w != nil {
		if _, err = bw.b.WriteTo(bw.w); err != nil {
			bw.w = nil
			err = nil
		}
	}
	if bw.w != nil {
		n, err = bw.w.Write(p)
		if err != nil {
			bw.w = nil
			return bw.b.Write(p)
		}
		return n, err
	} else {
		return bw.b.Write(p)
	}
}

func share() {
	name, err := uuid.NewV4()
	if err != nil {
		panic(err)
	}
	resp, err := http.Post("http://"+*server+"/"+name.String(), "application/x-www-form-urlencoded", strings.NewReader(""))
	if err != nil {
		panic(err)
	}
	var body []byte
	if resp.StatusCode == 200 {
		body, err = ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		if err != nil {
			panic(err)
		}
	} else {
		resp.Body.Close()
		log.Fatal("unable to open session")
	}
	fmt.Println(string(body))

	conn, err := websocket.Dial("ws://"+*server+"/"+name.String(), "", "http://"+*server)
	if err != nil {
		panic(err)
	}
	cols, err := term.Cols()
	if err != nil {
		panic(err)
	}
	lines, err := term.Lines()
	if err != nil {
		panic(err)
	}
	cmd := exec.Command(os.Getenv("SHELL"))
	cmd.Env = []string{
		"PS1=[termshare] \\W$ ",
		"TERM=" + os.Getenv("TERM"),
		"HOME=" + os.Getenv("HOME"),
		"USER=" + os.Getenv("USER"),
		"COLUMNS=" + strconv.Itoa(cols),
		"LINES=" + strconv.Itoa(lines),
	}
	pty, err := pty.Start(cmd)
	if err != nil {
		panic(err)
	}
	if err := term.MakeRaw(os.Stdin); err != nil {
		panic(err)
	}
	exitSignal := make(chan os.Signal)
	signal.Notify(exitSignal, os.Interrupt, syscall.SIGTERM)
	go func() {
		<-exitSignal
		term.Restore(os.Stdin)
		os.Exit(0)
	}()
	defer term.Restore(os.Stdin)
	eof := make(chan bool, 1)
	go func() {
		io.Copy(io.MultiWriter(os.Stdout, conn), pty)
		eof <- true
	}()
	go func() {
		io.Copy(pty, os.Stdin)
		eof <- true
	}()
	go func() {
		io.Copy(pty, conn)
		eof <- true
	}()
	go func() {
		for {
			_, err := conn.Write([]byte("\x00"))
			if err != nil {
				return
			}
			time.Sleep(10 * time.Second)
		}
	}()
	<-eof
}

func connect(sessionName string) {
	conn, err := websocket.Dial("ws://"+*server+"/"+sessionName, "", "http://"+*server)
	if err != nil {
		panic(err)
	}
	if err := term.MakeRaw(os.Stdin); err != nil {
		panic(err)
	}
	exitSignal := make(chan os.Signal)
	signal.Notify(exitSignal, os.Interrupt, syscall.SIGTERM)
	go func() {
		<-exitSignal
		term.Restore(os.Stdin)
		os.Exit(0)
	}()
	defer term.Restore(os.Stdin)
	eof := make(chan bool, 1)
	go func() {
		io.Copy(os.Stdout, conn)
		eof <- true
	}()
	go func() {
		io.Copy(conn, os.Stdin)
		eof <- true
	}()
	<-eof
}

func sessionNameFromRequest(r *http.Request) string {
	parts := strings.Split(r.RequestURI, "/")
	return parts[1]
}

func isWebsocketRequest(r *http.Request) bool {
	return r.Header.Get("Upgrade") == "websocket"
}

func main() {
	flag.Parse()

	if *daemon {
		sessions := sessions{s: make(map[string]*session)}

		http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
			switch {
			case r.RequestURI == "/":
				http.Redirect(w, r, "http://progrium.viewdocs.io/termshare", 301)
			case r.RequestURI == "/favicon.ico":
				return
			default:
				sessionName := sessionNameFromRequest(r)
				session, err := sessions.Get(sessionName)
				if r.Method == "POST" {
					_, err = sessions.Create(sessionName, false, false)
					if err != nil {
						log.Println(err)
						w.WriteHeader(http.StatusConflict)
						return
					}
					log.Println(sessionName + ": session created")
					w.Write([]byte("http://termsha.re/" + sessionName + "\n"))
					return
				}
				if err != nil {
					//w.WriteHeader(http.StatusNotFound)
					return
				}
				switch {
				case session.Pilot == nil && isWebsocketRequest(r):
					websocket.Handler(func(conn *websocket.Conn) {
						session.Pilot = conn
						log.Println(sessionName + ": pilot connected")
						_, err := io.Copy(io.MultiWriter(session.Viewers, session.CopilotBuffer), session.Pilot)
						if err == io.EOF {
							close(session.EOF)
						} else {
							log.Println("pilot writing error: ", err)
						}
					}).ServeHTTP(w, r)
				case session.Pilot != nil && session.Copilot == nil && !session.Broadcast && isWebsocketRequest(r):
					websocket.Handler(func(conn *websocket.Conn) {
						session.Copilot = conn
						session.CopilotBuffer.w = conn
						session.Pilot.Write([]byte("\x07")) // ding!
						log.Println(sessionName + ": copilot connected")
						eof := make(chan struct{})
						go func() {
							io.Copy(session.Pilot, session.Copilot)
							session.Copilot = nil
							session.CopilotBuffer.w = nil
							eof <- struct{}{}
						}()
						<-eof
					}).ServeHTTP(w, r)
				case session.Pilot != nil && !session.Private:
					if isWebsocketRequest(r) {
						websocket.Handler(func(conn *websocket.Conn) {
							session.Viewers.Add(conn)
							log.Println(sessionName + ": viewer connected (websocket)")
							<-session.EOF
						}).ServeHTTP(w, r)
					} else {
						// TODO: check for curl, otherwise serve static page with term.js
						session.Viewers.Add(FlushWriter(w))
						log.Println(sessionName + ": viewer connected (http stream)")
						<-session.EOF
					}
				}
			}
		})
		log.Println("Termshare server started...")
		log.Fatal(http.ListenAndServe(":"+os.Getenv("PORT"), nil))
	} else {
		if flag.Arg(0) == "" {
			share()
		} else {
			connect(flag.Arg(0))
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2010 The GoGo Authors. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

//
// General code generation functions (registers, instructions, ...)
// Heavily depends on asm_out.go which represents the Plan9 assembly language
//

package main

import "./libgogo/_obj/libgogo"

// Currently register from R8-R15 are available for usage
var NumRegisters uint64 = 8;
var FreeRegisters [8]byte;

//
// Initialize the register to free-state.
//
func InitFreeRegisters() {
    var i uint64;
    for i = 0; i < NumRegisters; i = i + 1 {
        FreeRegisters[i] = 1;
    }
}

//
// Function returns a free register, BUT is not set to occupied.
//
func GetFreeRegister() uint64 {
    var i uint64;
    for i = 0; FreeRegisters[i] == 0; {
        i = i + 1;
        if i == NumRegisters {
            libgogo.ExitError("No more free registers available for code generation", 5);
        }
    }
    return i+8;
}

//
// Occupy a given register.
//
func OccupyRegister(index uint64) {
    var realIndex uint64;
    realIndex = index-8;
    FreeRegisters[realIndex] = 0;
}

//
// Free a given register.
//
func FreeRegister(index uint64) {
    var realIndex uint64;
    realIndex = index-8;
    FreeRegisters[realIndex] = 1;
}

//
// Frees the register occupied by the given item if applicable.
// Freeing is only possible if the mode is registered.
//
func FreeRegisterIfRequired(item *libgogo.Item) {
    if item.Mode == libgogo.MODE_REG {
        FreeRegister(item.R);
    }
}

//
// Moves the value of the address a register is currently pointing to into the register itself
//
func DereferRegisterIfNecessary(item *libgogo.Item) {
    if (item.Mode == libgogo.MODE_REG) && (item.A != 0) { //Derefer register if it contains an address
        PrintInstruction_Reg_Reg("MOVQ", "R", item.R, 1, 0, 0, "R", item.R, 0, 0, 0); //MOVQ (item.R), item.R
        item.A = 0; //Register now contains a value
    }
}

//
// Simple wrapper to asm_out printing
//
func GenerateComment(msg string) {
    var str string = "  // >>> ";
    libgogo.StringAppend(&str, msg);
    libgogo.StringAppend(&str,"\n");
    PrintOutput(str);
}

func GenerateFieldAccess(item *libgogo.Item, offset uint64, indirect uint64) {
    var offsetItem *libgogo.Item;
    var temp uint64;
    if Compile != 0 {
        if item.Mode == libgogo.MODE_VAR { //Variable
            item.A = item.A + offset; //Direct and indirect offset calculation
            if indirect != 0 { //Indirect
                temp = GetFreeRegister();
                OccupyRegister(temp);
                PrintInstruction_Var_Reg("LEAQ", item, "R", temp); //LEAQ item.A(SB), Rtemp (soon to be item.R)
                item.Mode = libgogo.MODE_REG;
                item.R = temp;
                item.A = 1; //Register contains address
                DereferRegisterIfNecessary(item); //Indirection
                item.A = 1; //Register still contains address
            }
        } else { //Register
            offsetItem = libgogo.NewItem(); //For direct and indirect offset calculation
            libgogo.SetItem(offsetItem, libgogo.MODE_CONST, uint64_t, offset, 0, 0); //Constant item for offset
            AddSubInstruction("ADDQ", item, offsetItem, 0, 1); //Add constant item (offset), calculating with addresses
            if indirect != 0 { //Indirect
                DereferRegisterIfNecessary(item); //Indirection
                item.A = 1; //Register still contains address
            }
        }
    }
}

//
// Function converts a given item to registered mode if it is not already
// a register.
//
func MakeRegistered(item *libgogo.Item, calculatewithaddresses uint64) {
    var reg uint64;
    if item.Mode != libgogo.MODE_REG {
        reg = GetFreeRegister();
        OccupyRegister(reg);

        if item.Mode == libgogo.MODE_CONST { // const item
            PrintInstruction_Imm_Reg("MOVQ", item.A, "R", reg, 0, 0, 0); // MOVQ $item.A, Rdone (soon to be item.R)
        } else { // var item
            if calculatewithaddresses == 0 {
                PrintInstruction_Var_Reg("MOVQ", item, "R", reg); // MOVQ item.A(SB), Rdone (soon to be item.R)
            } else {
                PrintInstruction_Var_Reg("LEAQ", item, "R", reg); // LEAQ item.A(SB), Rdone (soon to be item.R)
            }
        }

        item.Mode = libgogo.MODE_REG;
        item.R = reg; // item is now a register
        item.A = calculatewithaddresses; // item now contains a value if calculatewithaddresses is 0, or an address if calculatewithaddress is 1
    }
}

//
// Constant folding function. If both items are constants the operation can be
// done in the compiler.
//
func ConstFolding(item1 *libgogo.Item, item2 *libgogo.Item, constvalue uint64) uint64 {
    var boolFlag uint64 = 0;
    if (item1.Mode == libgogo.MODE_CONST) && (item2.Mode == libgogo.MODE_CONST) {
        item1.A = constvalue;
        boolFlag = 1;
    }
    return boolFlag;
}

//
// item1 = item1 OP item2, or constvalue if both item1 and item2 are constants
// Side effect: The register item2 occupies is freed if applicable
// If calculatewithaddresses is 0, it is assumed that registers contain values,
// otherwise it is assumed that they contain addresses
//
func AddSubInstruction(op string, item1 *libgogo.Item, item2 *libgogo.Item, constvalue uint64, calculatewithaddresses uint64) {
    var done uint64 = 0;

    done = ConstFolding(item1, item2, constvalue);

    if (done == 0) && (item1.Mode != libgogo.MODE_REG) { //item1 is not a register => make it a register
        MakeRegistered(item1, calulatewithaddresses);
    }

    if done == 0 { //item1 is now (or has even already been) a register => use it
        if calculatewithaddresses == 0 { //Calculate with values
            DereferRegisterIfNecessary(item1); //Calculate with values
        }
        if (done == 0) && (item2.Mode == libgogo.MODE_CONST) {
            PrintInstruction_Imm_Reg(op, item2.A, "R", item1.R, 0, 0, 0); //OP $item2.A, item1.R
            done = 1;
        }
        if (done == 0) && (item2.Mode == libgogo.MODE_VAR) {
            PrintInstruction_Var_Reg(op, item2, "R", item1.R); //OP item2.A(SB), item1.R
            done = 1;
        }
        if (done == 0) && (item2.Mode == libgogo.MODE_REG) {
            if calculatewithaddresses == 0 { // Calculate with values
                DereferRegisterIfNecessary(item2);
            }
            PrintInstruction_Reg_Reg(op, "R", item2.R, 0, 0, 0, "R", item1.R, 0, 0, 0); //OP item2.R, item1.R
            done = 1;
        }
    }

    FreeRegisterIfRequired(item2); // item2 should be useless by now
}

//
// item1 = item1 OP item2, or constvalue if both item1 and item2 are constants
// Difference here is that it uses a one operand assembly instruction which
// operates on AX as first operand
//
func DivMulInstruction(op string, item1 *libgogo.Item, item2 *libgogo.Item, constvalue uint64, calculatewithaddresses uint64) {
    var done uint64 = 0;

    done = ConstFolding(item1, item2, constvalue);

    if done == 0 { // item1 is now (or has even already been) a register => use it
        if calculatewithaddresses == 0 { // Calculate with values
            DereferRegisterIfNecessary(item1); // Calculate with values
        }

        if item1.Mode == libgogo.MODE_CONST {
            PrintInstruction_Imm_Reg("MOVQ", item1.A, "AX", 0, 0, 0, 0) // move $item1.A into AX
        }
        if item1.Mode == libgogo.MODE_VAR {
            PrintInstruction_Var_Reg("MOVQ", item1, "AX", 0); // move item2.A(SB), AX
        }
        if item1.Mode == libgogo.MODE_REG {
            PrintInstruction_Reg_Reg("MOVQ", "R", item1.R, 0, 0, 0, "AX", 0, 0, 0, 0) // move item1.R into AX
        }

        if item2.Mode != libgogo.MODE_REG {
            // item2 needs to be registered as the second operand of a DIV/MUL
            // instruction always needs to be a register
            MakeRegistered(item2, calculatewithaddresses);
        }

        // OP item2.R
        if calculatewithaddresses == 0 { // Calculate with values
            DereferRegisterIfNecessary(item2);
        }
        done = libgogo.StringCompare(op,"DIVQ");
        if done == 0 { //Set DX to zero to avoid 128 bit division as DX is "high" part of DX:AX 128 bit register
            PrintInstruction_Reg_Reg("XORQ", "DX", 0, 0, 0, 0, "DX", 0, 0, 0, 0); //XORQ DX, DX is equal to MOVQ $0, DX
        }
        PrintInstruction_Reg(op, "R", item2.R, 0, 0, 0); //op item2.R
        PrintInstruction_Reg_Reg("MOVQ", "AX", 0, 0, 0, 0, "R", item2.R, 0, 0, 0) // move AX into item2.R
    }

    // Since item2 already had to be converted to a register, we now assign
    // item2 to item1 after freeing item1 first (if necessary)
    FreeRegisterIfRequired(item1);
    item1.Mode = item2.Mode;
    item1.R = item2.R;
    item1.A = item2.A;
    item1.Itemtype = item2.Itemtype;
    item1.Global = item2.Global;
}
<commit_msg>codegen: Fix typo<commit_after>// Copyright 2010 The GoGo Authors. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

//
// General code generation functions (registers, instructions, ...)
// Heavily depends on asm_out.go which represents the Plan9 assembly language
//

package main

import "./libgogo/_obj/libgogo"

// Currently register from R8-R15 are available for usage
var NumRegisters uint64 = 8;
var FreeRegisters [8]byte;

//
// Initialize the register to free-state.
//
func InitFreeRegisters() {
    var i uint64;
    for i = 0; i < NumRegisters; i = i + 1 {
        FreeRegisters[i] = 1;
    }
}

//
// Function returns a free register, BUT is not set to occupied.
//
func GetFreeRegister() uint64 {
    var i uint64;
    for i = 0; FreeRegisters[i] == 0; {
        i = i + 1;
        if i == NumRegisters {
            libgogo.ExitError("No more free registers available for code generation", 5);
        }
    }
    return i+8;
}

//
// Occupy a given register.
//
func OccupyRegister(index uint64) {
    var realIndex uint64;
    realIndex = index-8;
    FreeRegisters[realIndex] = 0;
}

//
// Free a given register.
//
func FreeRegister(index uint64) {
    var realIndex uint64;
    realIndex = index-8;
    FreeRegisters[realIndex] = 1;
}

//
// Frees the register occupied by the given item if applicable.
// Freeing is only possible if the mode is registered.
//
func FreeRegisterIfRequired(item *libgogo.Item) {
    if item.Mode == libgogo.MODE_REG {
        FreeRegister(item.R);
    }
}

//
// Moves the value of the address a register is currently pointing to into the register itself
//
func DereferRegisterIfNecessary(item *libgogo.Item) {
    if (item.Mode == libgogo.MODE_REG) && (item.A != 0) { //Derefer register if it contains an address
        PrintInstruction_Reg_Reg("MOVQ", "R", item.R, 1, 0, 0, "R", item.R, 0, 0, 0); //MOVQ (item.R), item.R
        item.A = 0; //Register now contains a value
    }
}

//
// Simple wrapper to asm_out printing
//
func GenerateComment(msg string) {
    var str string = "  // >>> ";
    libgogo.StringAppend(&str, msg);
    libgogo.StringAppend(&str,"\n");
    PrintOutput(str);
}

func GenerateFieldAccess(item *libgogo.Item, offset uint64, indirect uint64) {
    var offsetItem *libgogo.Item;
    var temp uint64;
    if Compile != 0 {
        if item.Mode == libgogo.MODE_VAR { //Variable
            item.A = item.A + offset; //Direct and indirect offset calculation
            if indirect != 0 { //Indirect
                temp = GetFreeRegister();
                OccupyRegister(temp);
                PrintInstruction_Var_Reg("LEAQ", item, "R", temp); //LEAQ item.A(SB), Rtemp (soon to be item.R)
                item.Mode = libgogo.MODE_REG;
                item.R = temp;
                item.A = 1; //Register contains address
                DereferRegisterIfNecessary(item); //Indirection
                item.A = 1; //Register still contains address
            }
        } else { //Register
            offsetItem = libgogo.NewItem(); //For direct and indirect offset calculation
            libgogo.SetItem(offsetItem, libgogo.MODE_CONST, uint64_t, offset, 0, 0); //Constant item for offset
            AddSubInstruction("ADDQ", item, offsetItem, 0, 1); //Add constant item (offset), calculating with addresses
            if indirect != 0 { //Indirect
                DereferRegisterIfNecessary(item); //Indirection
                item.A = 1; //Register still contains address
            }
        }
    }
}

//
// Function converts a given item to registered mode if it is not already
// a register.
//
func MakeRegistered(item *libgogo.Item, calculatewithaddresses uint64) {
    var reg uint64;
    if item.Mode != libgogo.MODE_REG {
        reg = GetFreeRegister();
        OccupyRegister(reg);

        if item.Mode == libgogo.MODE_CONST { // const item
            PrintInstruction_Imm_Reg("MOVQ", item.A, "R", reg, 0, 0, 0); // MOVQ $item.A, Rdone (soon to be item.R)
        } else { // var item
            if calculatewithaddresses == 0 {
                PrintInstruction_Var_Reg("MOVQ", item, "R", reg); // MOVQ item.A(SB), Rdone (soon to be item.R)
            } else {
                PrintInstruction_Var_Reg("LEAQ", item, "R", reg); // LEAQ item.A(SB), Rdone (soon to be item.R)
            }
        }

        item.Mode = libgogo.MODE_REG;
        item.R = reg; // item is now a register
        item.A = calculatewithaddresses; // item now contains a value if calculatewithaddresses is 0, or an address if calculatewithaddress is 1
    }
}

//
// Constant folding function. If both items are constants the operation can be
// done in the compiler.
//
func ConstFolding(item1 *libgogo.Item, item2 *libgogo.Item, constvalue uint64) uint64 {
    var boolFlag uint64 = 0;
    if (item1.Mode == libgogo.MODE_CONST) && (item2.Mode == libgogo.MODE_CONST) {
        item1.A = constvalue;
        boolFlag = 1;
    }
    return boolFlag;
}

//
// item1 = item1 OP item2, or constvalue if both item1 and item2 are constants
// Side effect: The register item2 occupies is freed if applicable
// If calculatewithaddresses is 0, it is assumed that registers contain values,
// otherwise it is assumed that they contain addresses
//
func AddSubInstruction(op string, item1 *libgogo.Item, item2 *libgogo.Item, constvalue uint64, calculatewithaddresses uint64) {
    var done uint64 = 0;

    done = ConstFolding(item1, item2, constvalue);

    if (done == 0) && (item1.Mode != libgogo.MODE_REG) { //item1 is not a register => make it a register
        MakeRegistered(item1, calculatewithaddresses);
    }

    if done == 0 { //item1 is now (or has even already been) a register => use it
        if calculatewithaddresses == 0 { //Calculate with values
            DereferRegisterIfNecessary(item1); //Calculate with values
        }
        if (done == 0) && (item2.Mode == libgogo.MODE_CONST) {
            PrintInstruction_Imm_Reg(op, item2.A, "R", item1.R, 0, 0, 0); //OP $item2.A, item1.R
            done = 1;
        }
        if (done == 0) && (item2.Mode == libgogo.MODE_VAR) {
            PrintInstruction_Var_Reg(op, item2, "R", item1.R); //OP item2.A(SB), item1.R
            done = 1;
        }
        if (done == 0) && (item2.Mode == libgogo.MODE_REG) {
            if calculatewithaddresses == 0 { // Calculate with values
                DereferRegisterIfNecessary(item2);
            }
            PrintInstruction_Reg_Reg(op, "R", item2.R, 0, 0, 0, "R", item1.R, 0, 0, 0); //OP item2.R, item1.R
            done = 1;
        }
    }

    FreeRegisterIfRequired(item2); // item2 should be useless by now
}

//
// item1 = item1 OP item2, or constvalue if both item1 and item2 are constants
// Difference here is that it uses a one operand assembly instruction which
// operates on AX as first operand
//
func DivMulInstruction(op string, item1 *libgogo.Item, item2 *libgogo.Item, constvalue uint64, calculatewithaddresses uint64) {
    var done uint64 = 0;

    done = ConstFolding(item1, item2, constvalue);

    if done == 0 { // item1 is now (or has even already been) a register => use it
        if calculatewithaddresses == 0 { // Calculate with values
            DereferRegisterIfNecessary(item1); // Calculate with values
        }

        if item1.Mode == libgogo.MODE_CONST {
            PrintInstruction_Imm_Reg("MOVQ", item1.A, "AX", 0, 0, 0, 0) // move $item1.A into AX
        }
        if item1.Mode == libgogo.MODE_VAR {
            PrintInstruction_Var_Reg("MOVQ", item1, "AX", 0); // move item2.A(SB), AX
        }
        if item1.Mode == libgogo.MODE_REG {
            PrintInstruction_Reg_Reg("MOVQ", "R", item1.R, 0, 0, 0, "AX", 0, 0, 0, 0) // move item1.R into AX
        }

        if item2.Mode != libgogo.MODE_REG {
            // item2 needs to be registered as the second operand of a DIV/MUL
            // instruction always needs to be a register
            MakeRegistered(item2, calculatewithaddresses);
        }

        // OP item2.R
        if calculatewithaddresses == 0 { // Calculate with values
            DereferRegisterIfNecessary(item2);
        }
        done = libgogo.StringCompare(op,"DIVQ");
        if done == 0 { //Set DX to zero to avoid 128 bit division as DX is "high" part of DX:AX 128 bit register
            PrintInstruction_Reg_Reg("XORQ", "DX", 0, 0, 0, 0, "DX", 0, 0, 0, 0); //XORQ DX, DX is equal to MOVQ $0, DX
        }
        PrintInstruction_Reg(op, "R", item2.R, 0, 0, 0); //op item2.R
        PrintInstruction_Reg_Reg("MOVQ", "AX", 0, 0, 0, 0, "R", item2.R, 0, 0, 0) // move AX into item2.R
    }

    // Since item2 already had to be converted to a register, we now assign
    // item2 to item1 after freeing item1 first (if necessary)
    FreeRegisterIfRequired(item1);
    item1.Mode = item2.Mode;
    item1.R = item2.R;
    item1.A = item2.A;
    item1.Itemtype = item2.Itemtype;
    item1.Global = item2.Global;
}
<|endoftext|>