<commit_before>package main

import (
	"fmt"

	"github.com/spf13/cobra"

	"github.com/lxc/lxd/lxc/config"
	"github.com/lxc/lxd/lxc/utils"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	cli "github.com/lxc/lxd/shared/cmd"
	"github.com/lxc/lxd/shared/i18n"
	"github.com/pkg/errors"
)

type cmdMove struct {
	global *cmdGlobal

	flagNoProfiles    bool
	flagProfile       []string
	flagConfig        []string
	flagInstanceOnly  bool
	flagDevice        []string
	flagMode          string
	flagStateless     bool
	flagStorage       string
	flagTarget        string
	flagTargetProject string
}

func (c *cmdMove) Command() *cobra.Command {
	cmd := &cobra.Command{}
	cmd.Use = usage("move", i18n.G("[<remote>:]<instance>[/<snapshot>] [<remote>:][<instance>[/<snapshot>]]"))
	cmd.Aliases = []string{"mv"}
	cmd.Short = i18n.G("Move instances within or in between LXD servers")
	cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G(
		`Move instances within or in between LXD servers`))
	cmd.Example = cli.FormatSection("", i18n.G(
		`lxc move [<remote>:]<source instance> [<remote>:][<destination instance>] [--instance-only]
    Move an instance between two hosts, renaming it if destination name differs.

lxc move <old name> <new name> [--instance-only]
    Rename a local instance.

lxc move <instance>/<old snapshot name> <instance>/<new snapshot name>
    Rename a snapshot.`))

	cmd.RunE = c.Run
	cmd.Flags().StringArrayVarP(&c.flagConfig, "config", "c", nil, i18n.G("Config key/value to apply to the target instance")+"``")
	cmd.Flags().StringArrayVarP(&c.flagDevice, "device", "d", nil, i18n.G("New key/value to apply to a specific device")+"``")
	cmd.Flags().StringArrayVarP(&c.flagProfile, "profile", "p", nil, i18n.G("Profile to apply to the target instance")+"``")
	cmd.Flags().BoolVar(&c.flagNoProfiles, "no-profiles", false, i18n.G("Unset all profiles on the target instance"))
	cmd.Flags().BoolVar(&c.flagInstanceOnly, "instance-only", false, i18n.G("Move the instance without its snapshots"))
	cmd.Flags().StringVar(&c.flagMode, "mode", moveDefaultMode, i18n.G("Transfer mode. One of pull (default), push or relay.")+"``")
	cmd.Flags().BoolVar(&c.flagStateless, "stateless", false, i18n.G("Copy a stateful instance stateless"))
	cmd.Flags().StringVarP(&c.flagStorage, "storage", "s", "", i18n.G("Storage pool name")+"``")
	cmd.Flags().StringVar(&c.flagTarget, "target", "", i18n.G("Cluster member name")+"``")
	cmd.Flags().StringVar(&c.flagTargetProject, "target-project", "", i18n.G("Copy to a project different from the source")+"``")

	return cmd
}

func (c *cmdMove) Run(cmd *cobra.Command, args []string) error {
	conf := c.global.conf

	// Sanity checks
	if c.flagTarget == "" && c.flagTargetProject == "" && c.flagStorage == "" {
		exit, err := c.global.CheckArgs(cmd, args, 2, 2)
		if exit {
			return err
		}
	} else {
		exit, err := c.global.CheckArgs(cmd, args, 1, 2)
		if exit {
			return err
		}
	}

	// Parse the mode
	mode := moveDefaultMode
	if c.flagMode != "" {
		mode = c.flagMode
	}

	sourceRemote, sourceName, err := conf.ParseRemote(args[0])
	if err != nil {
		return err
	}

	destRemote := sourceRemote
	destName := ""
	if len(args) == 2 {
		var err error
		destRemote, destName, err = conf.ParseRemote(args[1])
		if err != nil {
			return err
		}
	}

	// As an optimization, if the source an destination are the same, do
	// this via a simple rename. This only works for instances that aren't
	// running, instances that are running should be live migrated (of
	// course, this changing of hostname isn't supported right now, so this
	// simply won't work).
	if sourceRemote == destRemote && c.flagTarget == "" && c.flagStorage == "" && c.flagTargetProject == "" {
		if c.flagConfig != nil || c.flagDevice != nil || c.flagProfile != nil || c.flagNoProfiles {
			return fmt.Errorf(i18n.G("Can't override configuration or profiles in local rename"))
		}

		source, err := conf.GetInstanceServer(sourceRemote)
		if err != nil {
			return err
		}

		if shared.IsSnapshot(sourceName) {
			// Snapshot rename
			srcParent, srcSnap, _ := shared.InstanceGetParentAndSnapshotName(sourceName)
			dstParent, dstSnap, dstIsSnap := shared.InstanceGetParentAndSnapshotName(destName)

			if srcParent != dstParent {
				return fmt.Errorf("Invalid new snapshot name, parent must be the same as source")
			}

			if !dstIsSnap {
				return fmt.Errorf("Invalid new snapshot name")
			}

			op, err := source.RenameInstanceSnapshot(srcParent, srcSnap, api.InstanceSnapshotPost{Name: dstSnap})
			if err != nil {
				return err
			}

			return op.Wait()
		}

		// Instance rename
		op, err := source.RenameInstance(sourceName, api.InstancePost{Name: destName})
		if err != nil {
			return err
		}

		return op.Wait()
	}

	sourceResource := args[0]
	destResource := sourceResource
	if len(args) == 2 {
		destResource = args[1]
	}

	if c.flagTarget != "" {
		// If the target option was specified, we're moving an instance from a
		// cluster member to another, let's use the dedicated API.
		if sourceRemote == destRemote {
			if c.flagStateless {
				return fmt.Errorf(i18n.G("The --stateless flag can't be used with --target"))
			}

			if c.flagInstanceOnly {
				return fmt.Errorf(i18n.G("The --instance-only flag can't be used with --target"))
			}

			if c.flagStorage != "" {
				return fmt.Errorf(i18n.G("The --storage flag can't be used with --target"))
			}

			if c.flagMode != moveDefaultMode {
				return fmt.Errorf(i18n.G("The --mode flag can't be used with --target"))
			}

			return moveClusterInstance(conf, sourceResource, destResource, c.flagTarget)
		}

		dest, err := conf.GetInstanceServer(destRemote)
		if err != nil {
			return err
		}

		if !dest.IsClustered() {
			return fmt.Errorf(i18n.G("The destination LXD server is not clustered"))
		}
	}

	// Support for server-side pool move.
	if c.flagStorage != "" && sourceRemote == destRemote {
		source, err := conf.GetInstanceServer(sourceRemote)
		if err != nil {
			return err
		}

		if source.HasExtension("instance_pool_move") {
			if c.flagStateless {
				return fmt.Errorf(i18n.G("The --stateless flag can't be used with --storage"))
			}

			if c.flagMode != moveDefaultMode {
				return fmt.Errorf(i18n.G("The --mode flag can't be used with --storage"))
			}

			return moveInstancePool(conf, sourceResource, destResource, c.flagInstanceOnly, c.flagStorage)
		}
	}

	cpy := cmdCopy{}
	cpy.global = c.global
	cpy.flagTarget = c.flagTarget
	cpy.flagTargetProject = c.flagTargetProject
	cpy.flagConfig = c.flagConfig
	cpy.flagDevice = c.flagDevice
	cpy.flagProfile = c.flagProfile
	cpy.flagNoProfiles = c.flagNoProfiles

	stateful := !c.flagStateless
	instanceOnly := c.flagInstanceOnly

	// A move is just a copy followed by a delete; however, we want to
	// keep the volatile entries around since we are moving the instance.
	err = cpy.copyInstance(conf, sourceResource, destResource, true, -1, stateful, instanceOnly, mode, c.flagStorage, true)
	if err != nil {
		return err
	}

	del := cmdDelete{global: c.global}
	del.flagForce = true
	del.flagForceProtected = true
	err = del.Run(cmd, args[:1])
	if err != nil {
		return errors.Wrap(err, "Failed to delete original instance after copying it")
	}

	return nil
}

// Move an instance using special POST /instances/<name>?target=<member> API.
func moveClusterInstance(conf *config.Config, sourceResource string, destResource string, target string) error {
	// Parse the source.
	sourceRemote, sourceName, err := conf.ParseRemote(sourceResource)
	if err != nil {
		return err
	}

	// Parse the destination.
	_, destName, err := conf.ParseRemote(destResource)
	if err != nil {
		return err
	}

	// Make sure we have an instance or snapshot name.
	if sourceName == "" {
		return fmt.Errorf(i18n.G("You must specify a source instance name"))
	}

	// The destination name is optional.
	if destName == "" {
		destName = sourceName
	}

	// Connect to the source host
	source, err := conf.GetInstanceServer(sourceRemote)
	if err != nil {
		return errors.Wrap(err, i18n.G("Failed to connect to cluster member"))
	}

	// Check that it's a cluster
	if !source.IsClustered() {
		return fmt.Errorf(i18n.G("The source LXD server is not clustered"))
	}

	// The migrate API will do the right thing when passed a target.
	source = source.UseTarget(target)
	req := api.InstancePost{
		Name:      destName,
		Migration: true,
	}

	op, err := source.MigrateInstance(sourceName, req)
	if err != nil {
		return errors.Wrap(err, i18n.G("Migration API failure"))
	}

	// Watch the background operation
	progress := utils.ProgressRenderer{
		Format: i18n.G("Transferring instance: %s"),
		Quiet:  false,
	}
	_, err = op.AddHandler(progress.UpdateOp)
	if err != nil {
		progress.Done("")
		return err
	}
	// Wait for the move to complete
	err = utils.CancelableWait(op, &progress)
	if err != nil {
		progress.Done("")
		return err
	}
	progress.Done("")

	err = op.Wait()
	if err != nil {
		return errors.Wrap(err, i18n.G("Migration operation failure"))
	}

	return nil
}

// Move an instance between pools using special POST /instances/<name> API.
func moveInstancePool(conf *config.Config, sourceResource string, destResource string, instanceOnly bool, storage string) error {
	// Parse the source.
	sourceRemote, sourceName, err := conf.ParseRemote(sourceResource)
	if err != nil {
		return err
	}

	// Parse the destination.
	_, destName, err := conf.ParseRemote(destResource)
	if err != nil {
		return err
	}

	// Make sure we have an instance or snapshot name.
	if sourceName == "" {
		return fmt.Errorf(i18n.G("You must specify a source instance name"))
	}

	// The destination name is optional.
	if destName == "" {
		destName = sourceName
	}

	// Connect to the source host
	source, err := conf.GetInstanceServer(sourceRemote)
	if err != nil {
		return errors.Wrap(err, i18n.G("Failed to connect to cluster member"))
	}

	// Pass the new pool to the migration API.
	req := api.InstancePost{
		Name:         destName,
		Migration:    true,
		Pool:         storage,
		InstanceOnly: instanceOnly,
	}

	op, err := source.MigrateInstance(sourceName, req)
	if err != nil {
		return errors.Wrap(err, i18n.G("Migration API failure"))
	}

	err = op.Wait()
	if err != nil {
		return errors.Wrap(err, i18n.G("Migration operation failure"))
	}

	return nil
}

// Default migration mode when moving an instance.
const moveDefaultMode = "pull"
<commit_msg>lxc/move: Support for quietFlag for in-cluster instance move<commit_after>package main

import (
	"fmt"

	"github.com/spf13/cobra"

	"github.com/lxc/lxd/lxc/config"
	"github.com/lxc/lxd/lxc/utils"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	cli "github.com/lxc/lxd/shared/cmd"
	"github.com/lxc/lxd/shared/i18n"
	"github.com/pkg/errors"
)

type cmdMove struct {
	global *cmdGlobal

	flagNoProfiles    bool
	flagProfile       []string
	flagConfig        []string
	flagInstanceOnly  bool
	flagDevice        []string
	flagMode          string
	flagStateless     bool
	flagStorage       string
	flagTarget        string
	flagTargetProject string
}

func (c *cmdMove) Command() *cobra.Command {
	cmd := &cobra.Command{}
	cmd.Use = usage("move", i18n.G("[<remote>:]<instance>[/<snapshot>] [<remote>:][<instance>[/<snapshot>]]"))
	cmd.Aliases = []string{"mv"}
	cmd.Short = i18n.G("Move instances within or in between LXD servers")
	cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G(
		`Move instances within or in between LXD servers`))
	cmd.Example = cli.FormatSection("", i18n.G(
		`lxc move [<remote>:]<source instance> [<remote>:][<destination instance>] [--instance-only]
    Move an instance between two hosts, renaming it if destination name differs.

lxc move <old name> <new name> [--instance-only]
    Rename a local instance.

lxc move <instance>/<old snapshot name> <instance>/<new snapshot name>
    Rename a snapshot.`))

	cmd.RunE = c.Run
	cmd.Flags().StringArrayVarP(&c.flagConfig, "config", "c", nil, i18n.G("Config key/value to apply to the target instance")+"``")
	cmd.Flags().StringArrayVarP(&c.flagDevice, "device", "d", nil, i18n.G("New key/value to apply to a specific device")+"``")
	cmd.Flags().StringArrayVarP(&c.flagProfile, "profile", "p", nil, i18n.G("Profile to apply to the target instance")+"``")
	cmd.Flags().BoolVar(&c.flagNoProfiles, "no-profiles", false, i18n.G("Unset all profiles on the target instance"))
	cmd.Flags().BoolVar(&c.flagInstanceOnly, "instance-only", false, i18n.G("Move the instance without its snapshots"))
	cmd.Flags().StringVar(&c.flagMode, "mode", moveDefaultMode, i18n.G("Transfer mode. One of pull (default), push or relay.")+"``")
	cmd.Flags().BoolVar(&c.flagStateless, "stateless", false, i18n.G("Copy a stateful instance stateless"))
	cmd.Flags().StringVarP(&c.flagStorage, "storage", "s", "", i18n.G("Storage pool name")+"``")
	cmd.Flags().StringVar(&c.flagTarget, "target", "", i18n.G("Cluster member name")+"``")
	cmd.Flags().StringVar(&c.flagTargetProject, "target-project", "", i18n.G("Copy to a project different from the source")+"``")

	return cmd
}

func (c *cmdMove) Run(cmd *cobra.Command, args []string) error {
	conf := c.global.conf

	// Sanity checks
	if c.flagTarget == "" && c.flagTargetProject == "" && c.flagStorage == "" {
		exit, err := c.global.CheckArgs(cmd, args, 2, 2)
		if exit {
			return err
		}
	} else {
		exit, err := c.global.CheckArgs(cmd, args, 1, 2)
		if exit {
			return err
		}
	}

	// Parse the mode
	mode := moveDefaultMode
	if c.flagMode != "" {
		mode = c.flagMode
	}

	sourceRemote, sourceName, err := conf.ParseRemote(args[0])
	if err != nil {
		return err
	}

	destRemote := sourceRemote
	destName := ""
	if len(args) == 2 {
		var err error
		destRemote, destName, err = conf.ParseRemote(args[1])
		if err != nil {
			return err
		}
	}

	// As an optimization, if the source an destination are the same, do
	// this via a simple rename. This only works for instances that aren't
	// running, instances that are running should be live migrated (of
	// course, this changing of hostname isn't supported right now, so this
	// simply won't work).
	if sourceRemote == destRemote && c.flagTarget == "" && c.flagStorage == "" && c.flagTargetProject == "" {
		if c.flagConfig != nil || c.flagDevice != nil || c.flagProfile != nil || c.flagNoProfiles {
			return fmt.Errorf(i18n.G("Can't override configuration or profiles in local rename"))
		}

		source, err := conf.GetInstanceServer(sourceRemote)
		if err != nil {
			return err
		}

		if shared.IsSnapshot(sourceName) {
			// Snapshot rename
			srcParent, srcSnap, _ := shared.InstanceGetParentAndSnapshotName(sourceName)
			dstParent, dstSnap, dstIsSnap := shared.InstanceGetParentAndSnapshotName(destName)

			if srcParent != dstParent {
				return fmt.Errorf("Invalid new snapshot name, parent must be the same as source")
			}

			if !dstIsSnap {
				return fmt.Errorf("Invalid new snapshot name")
			}

			op, err := source.RenameInstanceSnapshot(srcParent, srcSnap, api.InstanceSnapshotPost{Name: dstSnap})
			if err != nil {
				return err
			}

			return op.Wait()
		}

		// Instance rename
		op, err := source.RenameInstance(sourceName, api.InstancePost{Name: destName})
		if err != nil {
			return err
		}

		return op.Wait()
	}

	sourceResource := args[0]
	destResource := sourceResource
	if len(args) == 2 {
		destResource = args[1]
	}

	if c.flagTarget != "" {
		// If the target option was specified, we're moving an instance from a
		// cluster member to another, let's use the dedicated API.
		if sourceRemote == destRemote {
			if c.flagStateless {
				return fmt.Errorf(i18n.G("The --stateless flag can't be used with --target"))
			}

			if c.flagInstanceOnly {
				return fmt.Errorf(i18n.G("The --instance-only flag can't be used with --target"))
			}

			if c.flagStorage != "" {
				return fmt.Errorf(i18n.G("The --storage flag can't be used with --target"))
			}

			if c.flagMode != moveDefaultMode {
				return fmt.Errorf(i18n.G("The --mode flag can't be used with --target"))
			}

			return moveClusterInstance(conf, sourceResource, destResource, c.flagTarget, c.global.flagQuiet)
		}

		dest, err := conf.GetInstanceServer(destRemote)
		if err != nil {
			return err
		}

		if !dest.IsClustered() {
			return fmt.Errorf(i18n.G("The destination LXD server is not clustered"))
		}
	}

	// Support for server-side pool move.
	if c.flagStorage != "" && sourceRemote == destRemote {
		source, err := conf.GetInstanceServer(sourceRemote)
		if err != nil {
			return err
		}

		if source.HasExtension("instance_pool_move") {
			if c.flagStateless {
				return fmt.Errorf(i18n.G("The --stateless flag can't be used with --storage"))
			}

			if c.flagMode != moveDefaultMode {
				return fmt.Errorf(i18n.G("The --mode flag can't be used with --storage"))
			}

			return moveInstancePool(conf, sourceResource, destResource, c.flagInstanceOnly, c.flagStorage)
		}
	}

	cpy := cmdCopy{}
	cpy.global = c.global
	cpy.flagTarget = c.flagTarget
	cpy.flagTargetProject = c.flagTargetProject
	cpy.flagConfig = c.flagConfig
	cpy.flagDevice = c.flagDevice
	cpy.flagProfile = c.flagProfile
	cpy.flagNoProfiles = c.flagNoProfiles

	stateful := !c.flagStateless
	instanceOnly := c.flagInstanceOnly

	// A move is just a copy followed by a delete; however, we want to
	// keep the volatile entries around since we are moving the instance.
	err = cpy.copyInstance(conf, sourceResource, destResource, true, -1, stateful, instanceOnly, mode, c.flagStorage, true)
	if err != nil {
		return err
	}

	del := cmdDelete{global: c.global}
	del.flagForce = true
	del.flagForceProtected = true
	err = del.Run(cmd, args[:1])
	if err != nil {
		return errors.Wrap(err, "Failed to delete original instance after copying it")
	}

	return nil
}

// Move an instance using special POST /instances/<name>?target=<member> API.
func moveClusterInstance(conf *config.Config, sourceResource string, destResource string, target string, quiet bool) error {
	// Parse the source.
	sourceRemote, sourceName, err := conf.ParseRemote(sourceResource)
	if err != nil {
		return err
	}

	// Parse the destination.
	_, destName, err := conf.ParseRemote(destResource)
	if err != nil {
		return err
	}

	// Make sure we have an instance or snapshot name.
	if sourceName == "" {
		return fmt.Errorf(i18n.G("You must specify a source instance name"))
	}

	// The destination name is optional.
	if destName == "" {
		destName = sourceName
	}

	// Connect to the source host
	source, err := conf.GetInstanceServer(sourceRemote)
	if err != nil {
		return errors.Wrap(err, i18n.G("Failed to connect to cluster member"))
	}

	// Check that it's a cluster
	if !source.IsClustered() {
		return fmt.Errorf(i18n.G("The source LXD server is not clustered"))
	}

	// The migrate API will do the right thing when passed a target.
	source = source.UseTarget(target)
	req := api.InstancePost{
		Name:      destName,
		Migration: true,
	}

	op, err := source.MigrateInstance(sourceName, req)
	if err != nil {
		return errors.Wrap(err, i18n.G("Migration API failure"))
	}

	// Watch the background operation
	progress := utils.ProgressRenderer{
		Format: i18n.G("Transferring instance: %s"),
		Quiet:  quiet,
	}
	_, err = op.AddHandler(progress.UpdateOp)
	if err != nil {
		progress.Done("")
		return err
	}
	// Wait for the move to complete
	err = utils.CancelableWait(op, &progress)
	if err != nil {
		progress.Done("")
		return err
	}
	progress.Done("")

	err = op.Wait()
	if err != nil {
		return errors.Wrap(err, i18n.G("Migration operation failure"))
	}

	return nil
}

// Move an instance between pools using special POST /instances/<name> API.
func moveInstancePool(conf *config.Config, sourceResource string, destResource string, instanceOnly bool, storage string) error {
	// Parse the source.
	sourceRemote, sourceName, err := conf.ParseRemote(sourceResource)
	if err != nil {
		return err
	}

	// Parse the destination.
	_, destName, err := conf.ParseRemote(destResource)
	if err != nil {
		return err
	}

	// Make sure we have an instance or snapshot name.
	if sourceName == "" {
		return fmt.Errorf(i18n.G("You must specify a source instance name"))
	}

	// The destination name is optional.
	if destName == "" {
		destName = sourceName
	}

	// Connect to the source host
	source, err := conf.GetInstanceServer(sourceRemote)
	if err != nil {
		return errors.Wrap(err, i18n.G("Failed to connect to cluster member"))
	}

	// Pass the new pool to the migration API.
	req := api.InstancePost{
		Name:         destName,
		Migration:    true,
		Pool:         storage,
		InstanceOnly: instanceOnly,
	}

	op, err := source.MigrateInstance(sourceName, req)
	if err != nil {
		return errors.Wrap(err, i18n.G("Migration API failure"))
	}

	err = op.Wait()
	if err != nil {
		return errors.Wrap(err, i18n.G("Migration operation failure"))
	}

	return nil
}

// Default migration mode when moving an instance.
const moveDefaultMode = "pull"
<|endoftext|>


<commit_before>/*
Copyright 2017, 2018 Ankyra

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package validate

import (
	"fmt"
	"regexp"
)

func InvalidProjectNameError(name string) error {
	return fmt.Errorf("The project name '%s' is not allowed. Expected a string matching /%s/",
		name, projectNameRegexFmt)
}

func InvalidDeploymentNameError(name string) error {
	return fmt.Errorf("The deployment name '%s' is not allowed. Expected a string matching /%s/",
		name, deploymentNameRegexFmt)
}

func InvalidDeploymentJobNameError(name string) error {
	return fmt.Errorf("The deployment job name '%s' is not allowed. Expected a string matching /%s/",
		name, deploymentJobNameRegexFmt)
}

func InvalidEnvironmentNameError(name string) error {
	return fmt.Errorf("The environment name '%s' is not allowed. Expected a string matching /%s/",
		name, environmentNameRegexFmt)
}

func InvalidStageNameError(name string) error {
	return fmt.Errorf("Invalid stage name '%s'. Expecting 'build' or 'deploy'.",
		name)
}

var projectNameRegexFmt = "^[a-zA-Z0-9]+[a-zA-Z0-9-_]+$"
var projectNameRegex = regexp.MustCompile(projectNameRegexFmt)

var environmentNameRegexFmt = "^[a-z]+[a-z0-9-_]*$"
var environmentNameRegex = regexp.MustCompile(environmentNameRegexFmt)

var deploymentNameRegexFmt = "^[a-zA-Z_]+[a-zA-Z0-9-_/]*$"
var deploymentNameRegex = regexp.MustCompile(deploymentNameRegexFmt)

var deploymentJobNameRegexFmt = "^[a-z]+[a-z0-9-_]*$"
var deploymentJobNameRegex = regexp.MustCompile(deploymentJobNameRegexFmt)

func IsValidProjectName(name string) bool {
	return projectNameRegex.MatchString(name)
}

func IsValidDeploymentName(name string) bool {
	return deploymentNameRegex.MatchString(name)
}

func isValidDeploymentJobName(name string) bool {
	return deploymentJobNameRegex.MatchString(name)
}

func IsValidEnvironmentName(name string) bool {
	return environmentNameRegex.MatchString(name)
}

func IsValidStageName(name string) bool {
	return name == "build" || name == "deploy"
}
<commit_msg>Revert "Add validation for deployment job name"<commit_after>/*
Copyright 2017, 2018 Ankyra

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package validate

import (
	"fmt"
	"regexp"
)

func InvalidProjectNameError(name string) error {
	return fmt.Errorf("The project name '%s' is not allowed. Expected a string matching /%s/",
		name, projectNameRegexFmt)
}

func InvalidDeploymentNameError(name string) error {
	return fmt.Errorf("The deployment name '%s' is not allowed. Expected a string matching /%s/",
		name, deploymentNameRegexFmt)
}

func InvalidEnvironmentNameError(name string) error {
	return fmt.Errorf("The environment name '%s' is not allowed. Expected a string matching /%s/",
		name, environmentNameRegexFmt)
}

func InvalidStageNameError(name string) error {
	return fmt.Errorf("Invalid stage name '%s'. Expecting 'build' or 'deploy'.",
		name)
}

var projectNameRegexFmt = "^[a-zA-Z0-9]+[a-zA-Z0-9-_]+$"
var projectNameRegex = regexp.MustCompile(projectNameRegexFmt)

var environmentNameRegexFmt = "^[a-z]+[a-z0-9-_]*$"
var environmentNameRegex = regexp.MustCompile(environmentNameRegexFmt)

var deploymentNameRegexFmt = "^[a-zA-Z_]+[a-zA-Z0-9-_/]*$"
var deploymentNameRegex = regexp.MustCompile(deploymentNameRegexFmt)

func IsValidProjectName(name string) bool {
	return projectNameRegex.MatchString(name)
}

func IsValidDeploymentName(name string) bool {
	return deploymentNameRegex.MatchString(name)
}

func IsValidEnvironmentName(name string) bool {
	return environmentNameRegex.MatchString(name)
}

func IsValidStageName(name string) bool {
	return name == "build" || name == "deploy"
}
<|endoftext|>


<commit_before>package main

import (
	"./monexec"
	"gopkg.in/yaml.v2"
	"os"
	"io/ioutil"
	"strings"
	"github.com/hashicorp/consul/api"
	"log"
	"time"
	"github.com/Pallinder/go-randomdata"
	"gopkg.in/alecthomas/kingpin.v2"
	"context"
	"os/signal"
	"syscall"
	"sync"
)

func loadConfigs(locations ...string) ([]*monexec.Executable, error) {
	var ans []*monexec.Executable
	var files []os.FileInfo
	for _, location := range locations {
		if stat, err := os.Stat(location); err != nil {
			return nil, err
		} else if stat.IsDir() {
			fs, err := ioutil.ReadDir(location)
			if err != nil {
				return nil, err
			}
			files = fs
		} else {
			files = []os.FileInfo{stat}
		}
		for _, info := range files {
			if strings.HasSuffix(info.Name(), ".yml") || strings.HasSuffix(info.Name(), ".yaml") {
				data, err := ioutil.ReadFile(info.Name())
				if err != nil {
					return nil, err
				}
				var exe monexec.Executable
				err = yaml.Unmarshal(data, &exe)
				if err != nil {
					return nil, err
				}
				ans = append(ans, &exe)
			}
		}
	}
	return ans, nil
}

func buildExec(mode, executable string, args []string, restart int, label string) *monexec.Executable {
	mon := monexec.Monitor{}
	var exe *monexec.Executable
	switch mode {
	case "oneshot":
		exe = mon.Oneshot(executable, args...)
	case "critical":
		exe = mon.Critical(executable, args...)
	case "forever":
		exe = mon.Forever(executable, args...)
	case "restart":
		exe = mon.Restart(restart, executable, args...)
	default:
		panic("Unknown mode " + mode)
	}
	exe.Label = label
	return exe
}

func dummyConsumer(events <-chan monexec.Event) {
	for range events {

	}
}

func consulEventsConsumer(events <-chan monexec.Event) {
	config := api.DefaultConfig()
	client, err := api.NewClient(config)
	if err != nil {
		log.Println("Unexpected error while connecting to Consul:", err)
		dummyConsumer(events)
		return
	}
	stateLock := sync.Mutex{}
	state := map[*monexec.Executable]monexec.Event{}

	interval := 5 * time.Second
	ticker := time.NewTicker(interval)
	notify := make(chan monexec.Event, 1)
	wg := sync.WaitGroup{}
	regEvent := func(event monexec.Event) {
		if event.Executable == nil {
			return
		}
		switch event.Type {
		case monexec.STARTED:
			autoDeregistrationTimeout := 2 * interval
			if autoDeregistrationTimeout < 1*time.Minute {
				autoDeregistrationTimeout = 1 * time.Minute
			}
			err := client.Agent().ServiceRegister(&api.AgentServiceRegistration{
				Name: event.Executable.ID(),
				Check: &api.AgentServiceCheck{
					Timeout:                        autoDeregistrationTimeout.String(),
					DeregisterCriticalServiceAfter: autoDeregistrationTimeout.String(),
				},
			})
			if err != nil {
				log.Println("Can't register service", event.Executable.ID(), "in Consul:", err)
			}
		case monexec.STOPPED:
			err := client.Agent().ServiceDeregister(event.Executable.ID())
			if err != nil {
				log.Println("Can't deregister service", event.Executable.ID(), "in Consul:", err)
			}
		}
	}
	wg.Add(1)
	go func() {
		defer wg.Done()
	LOOP:
		for {
			select {
			case _, ok := <-ticker.C:
				if !ok {
					break LOOP
				}
				snapshot := map[*monexec.Executable]monexec.Event{}
				stateLock.Lock()
				for ex, ev := range state {
					snapshot[ex] = ev
				}
				stateLock.Unlock()
				for _, event := range snapshot {
					regEvent(event)
				}
			case event, ok := <-notify:
				if !ok {
					break LOOP
				}
				regEvent(event)
			}
		}
	}()

	for event := range events {
		stateLock.Lock()
		state[event.Executable] = event
		stateLock.Unlock()
		notify <- event
	}
	ticker.Stop()
	close(notify)
	wg.Wait()

}

func
main() {
	app := kingpin.New("monexec", "Light supervisor for monitoring processes")
	app.Version("1.0.0").Author("Baryshnikov Alexander <dev@baryshnikov.net>")

	cmdGen := app.Command("gen", "Generate basic configuration file for executable")

	label := app.Flag("label", "Label for service").Short('l').Default(randomdata.Noun() + "-" + randomdata.Adjective()).String()

	genMode := cmdGen.Arg("mode", "Mode types").Required().Enum("oneshot", "critical", "forever", "restart")
	genExecutable := cmdGen.Arg("executable", "Applications to start").Required().String()
	genArgs := cmdGen.Arg("arg", "Arguments").Strings()
	genRestart := cmdGen.Flag("retries", "Restart count").Short('r').Default("5").Int()

	start := app.Command("start", "Start supervisor")
	configLocations := start.Arg("config", "Config file or directory with .yaml/.yml files").Strings()

	run := app.Command("run", "Run single executable")
	runMode := run.Arg("mode", "Mode types").Required().Enum("oneshot", "critical", "forever", "restart")
	runExecutable := run.Arg("executable", "Applications to start").Required().String()
	runArgs := run.Arg("arg", "Arguments").Strings()
	runRestart := run.Flag("retries", "Restart count").Short('r').Default("5").Int()
	runRestartInterval := run.Flag("restart-timeout", "Timeout before restart").Default("5s").Duration()
	runStartTimeout := run.Flag("start-timeout", "Timeout to check that process is started").Default("3s").Duration()
	runStopTimeout := run.Flag("stop-timeout", "Timeout for graceful shutdown").Default("5s").Duration()
	runWorkdir := run.Flag("workdir", "Working directory").Short('w').String()

	consulEnabled := app.Flag("consul", "Enable consul auto-registration (used ENV for config)").Bool()

	app.DefaultEnvars()

	switch kingpin.MustParse(app.Parse(os.Args[1:])) {
	case "gen":
		exe := buildExec(*genMode, *genExecutable, *genArgs, *genRestart, *label)
		data, err := yaml.Marshal(exe)
		if err != nil {
			panic(err)
		}
		os.Stdout.Write(data)
	case "start":
		execs, err := loadConfigs(*configLocations...)
		if err != nil {
			panic(err)
		}

		mon := monexec.Monitor{Executables: execs}
		ctx, stp := context.WithCancel(context.Background())

		c := make(chan os.Signal, 3)
		signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGHUP)
		go func() {
			for range c {
				stp()
			}
		}()

		events := mon.Start(ctx)

		if *consulEnabled {
			consulEventsConsumer(events)
		} else {
			dummyConsumer(events)
		}

	case "run":
		exe := buildExec(*runMode, *runExecutable, *runArgs, *runRestart, *label)
		exe.RestartTimeout = *runRestartInterval
		exe.StartTimeout = *runStartTimeout
		exe.StopTimeout = *runStopTimeout
		exe.WorkDir = *runWorkdir

		mon := monexec.Monitor{Executables: []*monexec.Executable{exe}}
		ctx, stp := context.WithCancel(context.Background())

		c := make(chan os.Signal, 3)
		signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGHUP)
		go func() {
			for range c {
				stp()
			}
		}()

		events := mon.Start(ctx)

		if *consulEnabled {
			consulEventsConsumer(events)
		} else {
			dummyConsumer(events)
		}
	}

}
<commit_msg>fix import<commit_after>package main

import (
	"github.com/reddec/monexec/monexec"
	"gopkg.in/yaml.v2"
	"os"
	"io/ioutil"
	"strings"
	"github.com/hashicorp/consul/api"
	"log"
	"time"
	"github.com/Pallinder/go-randomdata"
	"gopkg.in/alecthomas/kingpin.v2"
	"context"
	"os/signal"
	"syscall"
	"sync"
)

func loadConfigs(locations ...string) ([]*monexec.Executable, error) {
	var ans []*monexec.Executable
	var files []os.FileInfo
	for _, location := range locations {
		if stat, err := os.Stat(location); err != nil {
			return nil, err
		} else if stat.IsDir() {
			fs, err := ioutil.ReadDir(location)
			if err != nil {
				return nil, err
			}
			files = fs
		} else {
			files = []os.FileInfo{stat}
		}
		for _, info := range files {
			if strings.HasSuffix(info.Name(), ".yml") || strings.HasSuffix(info.Name(), ".yaml") {
				data, err := ioutil.ReadFile(info.Name())
				if err != nil {
					return nil, err
				}
				var exe monexec.Executable
				err = yaml.Unmarshal(data, &exe)
				if err != nil {
					return nil, err
				}
				ans = append(ans, &exe)
			}
		}
	}
	return ans, nil
}

func buildExec(mode, executable string, args []string, restart int, label string) *monexec.Executable {
	mon := monexec.Monitor{}
	var exe *monexec.Executable
	switch mode {
	case "oneshot":
		exe = mon.Oneshot(executable, args...)
	case "critical":
		exe = mon.Critical(executable, args...)
	case "forever":
		exe = mon.Forever(executable, args...)
	case "restart":
		exe = mon.Restart(restart, executable, args...)
	default:
		panic("Unknown mode " + mode)
	}
	exe.Label = label
	return exe
}

func dummyConsumer(events <-chan monexec.Event) {
	for range events {

	}
}

func consulEventsConsumer(events <-chan monexec.Event) {
	config := api.DefaultConfig()
	client, err := api.NewClient(config)
	if err != nil {
		log.Println("Unexpected error while connecting to Consul:", err)
		dummyConsumer(events)
		return
	}
	stateLock := sync.Mutex{}
	state := map[*monexec.Executable]monexec.Event{}

	interval := 5 * time.Second
	ticker := time.NewTicker(interval)
	notify := make(chan monexec.Event, 1)
	wg := sync.WaitGroup{}
	regEvent := func(event monexec.Event) {
		if event.Executable == nil {
			return
		}
		switch event.Type {
		case monexec.STARTED:
			autoDeregistrationTimeout := 2 * interval
			if autoDeregistrationTimeout < 1*time.Minute {
				autoDeregistrationTimeout = 1 * time.Minute
			}
			err := client.Agent().ServiceRegister(&api.AgentServiceRegistration{
				Name: event.Executable.ID(),
				Check: &api.AgentServiceCheck{
					Timeout:                        autoDeregistrationTimeout.String(),
					DeregisterCriticalServiceAfter: autoDeregistrationTimeout.String(),
				},
			})
			if err != nil {
				log.Println("Can't register service", event.Executable.ID(), "in Consul:", err)
			}
		case monexec.STOPPED:
			err := client.Agent().ServiceDeregister(event.Executable.ID())
			if err != nil {
				log.Println("Can't deregister service", event.Executable.ID(), "in Consul:", err)
			}
		}
	}
	wg.Add(1)
	go func() {
		defer wg.Done()
	LOOP:
		for {
			select {
			case _, ok := <-ticker.C:
				if !ok {
					break LOOP
				}
				snapshot := map[*monexec.Executable]monexec.Event{}
				stateLock.Lock()
				for ex, ev := range state {
					snapshot[ex] = ev
				}
				stateLock.Unlock()
				for _, event := range snapshot {
					regEvent(event)
				}
			case event, ok := <-notify:
				if !ok {
					break LOOP
				}
				regEvent(event)
			}
		}
	}()

	for event := range events {
		stateLock.Lock()
		state[event.Executable] = event
		stateLock.Unlock()
		notify <- event
	}
	ticker.Stop()
	close(notify)
	wg.Wait()

}

func
main() {
	app := kingpin.New("monexec", "Light supervisor for monitoring processes")
	app.Version("1.0.0").Author("Baryshnikov Alexander <dev@baryshnikov.net>")

	cmdGen := app.Command("gen", "Generate basic configuration file for executable")

	label := app.Flag("label", "Label for service").Short('l').Default(randomdata.Noun() + "-" + randomdata.Adjective()).String()

	genMode := cmdGen.Arg("mode", "Mode types").Required().Enum("oneshot", "critical", "forever", "restart")
	genExecutable := cmdGen.Arg("executable", "Applications to start").Required().String()
	genArgs := cmdGen.Arg("arg", "Arguments").Strings()
	genRestart := cmdGen.Flag("retries", "Restart count").Short('r').Default("5").Int()

	start := app.Command("start", "Start supervisor")
	configLocations := start.Arg("config", "Config file or directory with .yaml/.yml files").Strings()

	run := app.Command("run", "Run single executable")
	runMode := run.Arg("mode", "Mode types").Required().Enum("oneshot", "critical", "forever", "restart")
	runExecutable := run.Arg("executable", "Applications to start").Required().String()
	runArgs := run.Arg("arg", "Arguments").Strings()
	runRestart := run.Flag("retries", "Restart count").Short('r').Default("5").Int()
	runRestartInterval := run.Flag("restart-timeout", "Timeout before restart").Default("5s").Duration()
	runStartTimeout := run.Flag("start-timeout", "Timeout to check that process is started").Default("3s").Duration()
	runStopTimeout := run.Flag("stop-timeout", "Timeout for graceful shutdown").Default("5s").Duration()
	runWorkdir := run.Flag("workdir", "Working directory").Short('w').String()

	consulEnabled := app.Flag("consul", "Enable consul auto-registration (used ENV for config)").Bool()

	app.DefaultEnvars()

	switch kingpin.MustParse(app.Parse(os.Args[1:])) {
	case "gen":
		exe := buildExec(*genMode, *genExecutable, *genArgs, *genRestart, *label)
		data, err := yaml.Marshal(exe)
		if err != nil {
			panic(err)
		}
		os.Stdout.Write(data)
	case "start":
		execs, err := loadConfigs(*configLocations...)
		if err != nil {
			panic(err)
		}

		mon := monexec.Monitor{Executables: execs}
		ctx, stp := context.WithCancel(context.Background())

		c := make(chan os.Signal, 3)
		signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGHUP)
		go func() {
			for range c {
				stp()
			}
		}()

		events := mon.Start(ctx)

		if *consulEnabled {
			consulEventsConsumer(events)
		} else {
			dummyConsumer(events)
		}

	case "run":
		exe := buildExec(*runMode, *runExecutable, *runArgs, *runRestart, *label)
		exe.RestartTimeout = *runRestartInterval
		exe.StartTimeout = *runStartTimeout
		exe.StopTimeout = *runStopTimeout
		exe.WorkDir = *runWorkdir

		mon := monexec.Monitor{Executables: []*monexec.Executable{exe}}
		ctx, stp := context.WithCancel(context.Background())

		c := make(chan os.Signal, 3)
		signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGHUP)
		go func() {
			for range c {
				stp()
			}
		}()

		events := mon.Start(ctx)

		if *consulEnabled {
			consulEventsConsumer(events)
		} else {
			dummyConsumer(events)
		}
	}

}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"time"
)

var (
	email        = flag.String("auth.email", "", "authorization email")
	key          = flag.String("auth.key", "", "authorization key")
	baseURL      = flag.String("url", "https://api.cloudflare.com/client/v4/logs", "URL for CloudFlare logs API")
	start        = flag.Int64("start", -1, "the unix epoch timestamp to start downloading from")
	maxPast      = flag.Duration("max", 72*time.Hour, "the maximum time in the past the start can be")
	end          = flag.Int64("end", time.Now().Unix(), "the unix epoch timestamp to end downloading at")
	fileInterval = flag.Duration("interval", 1*time.Minute, "the time interval to save files in")
	dir          = flag.String("dir", os.TempDir(), "directory to download logs in")
)

const (
	fileTimeLayout = "logs-2006_01_02-15_04_05.log.gz"
	checkpointFile = "checkpoint"
)

func main() {
	validateFlags()
	log.Printf("Downloading to %s", *dir)
	downloadLogs()
}

// downloadLogs connects to log downloading service and saves
// logs to local disk.
func downloadLogs() {
	var (
		// Global interval to download.
		startT = time.Unix(*start, 0).UTC()
		endT   = time.Unix(*end, 0).UTC()

		// Interval to download for current file. Rounds down to the closest minute
		// to ensure that you do not get files that cross intervals
		s = startT.Truncate(time.Minute)
		e time.Time
	)
	for {
		if !s.Before(endT) {
			return
		}
		e = s.Add(*fileInterval)
		if e.After(endT) {
			e = endT
		}
		saveLogs(s, e)
		// saves the checkpoint file after the file is successfully downloaded
		saveCheckpoint(e)
		s = s.Add(*fileInterval)
	}
}

// saveLogs downloads logs for the period [s, e) and saves
// saves them to a file.
func saveLogs(s, e time.Time) {
	log.Printf("Downloading logs from %v to %v", s, e)
	u := fmt.Sprintf("%s?start=%d&end=%d", *baseURL, s.Unix(), e.Unix())
	req, err := http.NewRequest("GET", u, nil)
	if err != nil {
		log.Fatalf("Failed to create request: %v", err)
	}

	req.Header.Add("X-Auth-Email", *email)
	req.Header.Add("X-Auth-Key", *key)
	req.Header.Add("Accept-Encoding", "gzip")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatalf("Failed to make request: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		log.Fatalf("Received non-2xx status code: %d", resp.StatusCode)
	}

	fname := filepath.Join(*dir, s.Format(fileTimeLayout))
	f, err := os.Create(fname)
	if err != nil {
		log.Fatalf("Failed to create log file (%s): %v", fname, err)
	}
	defer f.Close()

	io.Copy(f, resp.Body)
    // saves the checkpoint file after the file is successfully downloaded
	saveCheckpoint()
}

// saveCheckpoint saves the last downloaded state in a file
// to resume download from.
func saveCheckpoint(t time.Time) {
	fp := filepath.Join(*dir, checkpointFile)
	os.Remove(fp)
	f, err := os.Create(fp)
	if err != nil {
		log.Fatalf("Failed to create checkpoint file (%s): %v", fp, err)
	}
	defer f.Close()
	if _, err := f.WriteString(strconv.FormatInt(t.Unix(), 10)); err != nil {
		log.Fatalf("Failed to write to checkpoint file (%s): %v", fp, err)
	}
}

// validateFlags parses flags, initializes appropriate ones
// from checkpointFile, and performs some sanity checks.
func validateFlags() {
	flag.Parse()
	if len(*email) == 0 {
		log.Fatal("No auth.email provided")
	}
	if len(*key) == 0 {
		log.Fatal("No auth.key provided")
	}
	if len(*baseURL) == 0 {
		log.Fatal("No url provided")
	}
	if *start < 0 {
		fp := filepath.Join(*dir, checkpointFile)
		b, err := ioutil.ReadFile(fp)
		if err != nil {
			log.Fatalf("Failed to read checkpoint file (%s): %v", fp, err)
		}
		s, err := strconv.ParseInt(string(b), 10, 0)
		if err != nil || s < 0 {
			log.Fatalf("Corrupt checkpoint file (%s)", fp)
		}
		*start = s
	}
	if time.Since(time.Unix(*start, 0)) > *maxPast {
		log.Fatalf("Start is more than %v old", maxPast)
	}
	if *end < 0 {
		log.Fatalf("The provided end (%d) is < 0", *end)
	}
	if !(*end > *start) {
		log.Fatalf("The provided end (%d) is not after start (%d)", *end, *start)
	}
	if _, err := os.Stat(*dir); os.IsNotExist(err) {
		log.Fatalf("The provided dir (%s) does not exist", *dir)
	}
}
<commit_msg>fixed missing func parameter in saveCheckpoint<commit_after>package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"time"
)

var (
	email        = flag.String("auth.email", "", "authorization email")
	key          = flag.String("auth.key", "", "authorization key")
	baseURL      = flag.String("url", "https://api.cloudflare.com/client/v4/logs", "URL for CloudFlare logs API")
	start        = flag.Int64("start", -1, "the unix epoch timestamp to start downloading from")
	maxPast      = flag.Duration("max", 72*time.Hour, "the maximum time in the past the start can be")
	end          = flag.Int64("end", time.Now().Unix(), "the unix epoch timestamp to end downloading at")
	fileInterval = flag.Duration("interval", 1*time.Minute, "the time interval to save files in")
	dir          = flag.String("dir", os.TempDir(), "directory to download logs in")
)

const (
	fileTimeLayout = "logs-2006_01_02-15_04_05.log.gz"
	checkpointFile = "checkpoint"
)

func main() {
	validateFlags()
	log.Printf("Downloading to %s", *dir)
	downloadLogs()
}

// downloadLogs connects to log downloading service and saves
// logs to local disk.
func downloadLogs() {
	var (
		// Global interval to download.
		startT = time.Unix(*start, 0).UTC()
		endT   = time.Unix(*end, 0).UTC()

		// Interval to download for current file. Rounds down to the closest minute
		// to ensure that you do not get files that cross intervals
		s = startT.Truncate(time.Minute)
		e time.Time
	)
	for {
		if !s.Before(endT) {
			return
		}
		e = s.Add(*fileInterval)
		if e.After(endT) {
			e = endT
		}
		saveLogs(s, e)
		// saves the checkpoint file after the file is successfully downloaded
		saveCheckpoint(e)
		s = s.Add(*fileInterval)
	}
}

// saveLogs downloads logs for the period [s, e) and saves
// saves them to a file.
func saveLogs(s, e time.Time) {
	log.Printf("Downloading logs from %v to %v", s, e)
	u := fmt.Sprintf("%s?start=%d&end=%d", *baseURL, s.Unix(), e.Unix())
	req, err := http.NewRequest("GET", u, nil)
	if err != nil {
		log.Fatalf("Failed to create request: %v", err)
	}

	req.Header.Add("X-Auth-Email", *email)
	req.Header.Add("X-Auth-Key", *key)
	req.Header.Add("Accept-Encoding", "gzip")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatalf("Failed to make request: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		log.Fatalf("Received non-2xx status code: %d", resp.StatusCode)
	}

	fname := filepath.Join(*dir, s.Format(fileTimeLayout))
	f, err := os.Create(fname)
	if err != nil {
		log.Fatalf("Failed to create log file (%s): %v", fname, err)
	}
	defer f.Close()

	io.Copy(f, resp.Body)
    // saves the checkpoint file after the file is successfully downloaded
	saveCheckpoint(e)
}

// saveCheckpoint saves the last downloaded state in a file
// to resume download from.
func saveCheckpoint(t time.Time) {
	fp := filepath.Join(*dir, checkpointFile)
	os.Remove(fp)
	f, err := os.Create(fp)
	if err != nil {
		log.Fatalf("Failed to create checkpoint file (%s): %v", fp, err)
	}
	defer f.Close()
	if _, err := f.WriteString(strconv.FormatInt(t.Unix(), 10)); err != nil {
		log.Fatalf("Failed to write to checkpoint file (%s): %v", fp, err)
	}
}

// validateFlags parses flags, initializes appropriate ones
// from checkpointFile, and performs some sanity checks.
func validateFlags() {
	flag.Parse()
	if len(*email) == 0 {
		log.Fatal("No auth.email provided")
	}
	if len(*key) == 0 {
		log.Fatal("No auth.key provided")
	}
	if len(*baseURL) == 0 {
		log.Fatal("No url provided")
	}
	if *start < 0 {
		fp := filepath.Join(*dir, checkpointFile)
		b, err := ioutil.ReadFile(fp)
		if err != nil {
			log.Fatalf("Failed to read checkpoint file (%s): %v", fp, err)
		}
		s, err := strconv.ParseInt(string(b), 10, 0)
		if err != nil || s < 0 {
			log.Fatalf("Corrupt checkpoint file (%s)", fp)
		}
		*start = s
	}
	if time.Since(time.Unix(*start, 0)) > *maxPast {
		log.Fatalf("Start is more than %v old", maxPast)
	}
	if *end < 0 {
		log.Fatalf("The provided end (%d) is < 0", *end)
	}
	if !(*end > *start) {
		log.Fatalf("The provided end (%d) is not after start (%d)", *end, *start)
	}
	if _, err := os.Stat(*dir); os.IsNotExist(err) {
		log.Fatalf("The provided dir (%s) does not exist", *dir)
	}
}
<|endoftext|>


<commit_before>/* Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc> */
/* See LICENSE for licensing information */

package main

import (
	"flag"
	"fmt"
	"go/ast"
	"go/importer"
	"go/parser"
	"go/token"
	"go/types"
	"io"
	"os"
	"path/filepath"
	"strings"
)

func init() {
	if err := typesInit(); err != nil {
		errExit(err)
	}
}

func main() {
	flag.Parse()
	if err := checkPaths(flag.Args(), os.Stdout); err != nil {
		errExit(err)
	}
}

func errExit(err error) {
	fmt.Fprintf(os.Stderr, "%v\n", err)
	os.Exit(1)
}

type call struct {
	params  []interface{}
	results []interface{}
}

var toToken = map[string]token.Token{
	"byte":    token.INT,
	"int":     token.INT,
	"int8":    token.INT,
	"int16":   token.INT,
	"int32":   token.INT,
	"int64":   token.INT,
	"uint":    token.INT,
	"uint8":   token.INT,
	"uint16":  token.INT,
	"uint32":  token.INT,
	"uint64":  token.INT,
	"string":  token.STRING,
	"float32": token.FLOAT,
	"float64": token.FLOAT,
}

func paramEqual(t1 types.Type, a2 interface{}) bool {
	switch x := a2.(type) {
	case string:
		return t1.String() == x
	case token.Token:
		return toToken[t1.String()] == x
	case nil:
		switch t1.(type) {
		case *types.Slice:
			return true
		case *types.Map:
			return true
		default:
			return false
		}
	default:
		return false
	}
}

func paramsMatch(types1 []types.Type, args2 []interface{}) bool {
	if len(types1) != len(args2) {
		return false
	}
	for i, t1 := range types1 {
		a2 := args2[i]
		if !paramEqual(t1, a2) {
			return false
		}
	}
	return true
}

func resultEqual(t1 types.Type, e2 interface{}) bool {
	switch x := e2.(type) {
	case string:
		return t1.String() == x
	case nil:
		// assigning to _
		return true
	default:
		return false
	}
}

func resultsMatch(types1 []types.Type, exps2 []interface{}) bool {
	if len(exps2) == 0 {
		return true
	}
	if len(types1) != len(exps2) {
		return false
	}
	for i, t1 := range types1 {
		e2 := exps2[i]
		if !resultEqual(t1, e2) {
			return false
		}
	}
	return true
}

func interfaceMatching(calls map[string]call) string {
	matchesIface := func(decls map[string]funcDecl) bool {
		if len(calls) > len(decls) {
			return false
		}
		for n, d := range decls {
			c, e := calls[n]
			if !e {
				return false
			}
			if !paramsMatch(d.params, c.params) {
				return false
			}
			if !resultsMatch(d.results, c.results) {
				return false
			}
		}
		return true
	}
	for name, decls := range parsed {
		if matchesIface(decls) {
			return name
		}
	}
	return ""
}

func isGoFile(info os.FileInfo) bool {
	name := info.Name()
	if strings.HasPrefix(name, ".") {
		return false
	}
	if !strings.HasSuffix(name, ".go") {
		return false
	}
	if strings.HasSuffix(name, "_test.go") {
		return false
	}
	return true
}

func getPaths(p string) ([]string, []string, error) {
	f, err := os.Open(p)
	if err != nil {
		return nil, nil, err
	}
	defer f.Close()
	info, err := f.Stat()
	if err != nil {
		return nil, nil, err
	}
	if !info.Mode().IsDir() {
		return []string{p}, nil, nil
	}
	names, err := f.Readdirnames(-1)
	if err != nil {
		return nil, nil, err
	}
	var gofiles, dirs []string
	for _, n := range names {
		fp := filepath.Join(p, n)
		info, err := os.Stat(fp)
		if err != nil {
			return nil, nil, err
		}
		if info.IsDir() {
			dirs = append(dirs, fp)
		} else if isGoFile(info) {
			gofiles = append(gofiles, fp)
		}
	}
	return gofiles, dirs, err
}

func getPkgPaths(p string) ([][]string, error) {
	recursive := filepath.Base(p) == "..."
	if recursive {
		p = filepath.Dir(p)
	}
	gofiles, dirs, err := getPaths(p)
	if err != nil {
		return nil, err
	}
	if !recursive {
		dirs = nil
	}
	pkgPaths := [][]string{gofiles}
	dirQueue := dirs
	for len(dirQueue) > 0 {
		d := dirQueue[0]
		dirQueue = dirQueue[1:]
		gofiles, dirs, err := getPaths(d)
		if err != nil {
			return nil, err
		}
		pkgPaths = append(pkgPaths, gofiles)
		dirQueue = append(dirQueue, dirs...)
	}
	return pkgPaths, nil
}

func checkPaths(paths []string, w io.Writer) error {
	for _, p := range paths {
		pkgPaths, err := getPkgPaths(p)
		if err != nil {
			return err
		}
		for _, gofiles := range pkgPaths {
			if err := checkPkg(gofiles, w); err != nil {
				return err
			}
		}
	}
	return nil
}

func checkPkg(gofiles []string, w io.Writer) error {
	gp := &goPkg{
		fset: token.NewFileSet(),
	}
	for _, fp := range gofiles {
		if err := gp.parsePath(fp); err != nil {
			return err
		}
	}
	if err := gp.check(w); err != nil {
		return err
	}
	return nil
}

type goPkg struct {
	fset  *token.FileSet
	files []*ast.File
}

func (gp *goPkg) parsePath(fp string) error {
	f, err := os.Open(fp)
	if err != nil {
		return err
	}
	defer f.Close()
	if err := gp.parseReader(fp, f); err != nil {
		return err
	}
	return nil
}

func (gp *goPkg) parseReader(name string, r io.Reader) error {
	f, err := parser.ParseFile(gp.fset, name, r, 0)
	if err != nil {
		return err
	}
	gp.files = append(gp.files, f)
	return nil
}

func (gp *goPkg) check(w io.Writer) error {
	conf := types.Config{Importer: importer.Default()}
	pkg, err := conf.Check("", gp.fset, gp.files, nil)
	if err != nil {
		return err
	}

	v := &Visitor{
		w:      w,
		fset:   gp.fset,
		scopes: []*types.Scope{pkg.Scope()},
	}
	for _, f := range gp.files {
		ast.Walk(v, f)
	}
	return nil
}

type Visitor struct {
	w      io.Writer
	fset   *token.FileSet
	scopes []*types.Scope

	nodes []ast.Node

	params map[string]types.Type
	used   map[string]map[string]call

	// TODO: don't just discard params with untracked usage
	unknown       map[string]struct{}
	recordUnknown bool
}

func (v *Visitor) scope() *types.Scope {
	return v.scopes[len(v.scopes)-1]
}

func scopeName(e ast.Expr) string {
	switch x := e.(type) {
	case *ast.Ident:
		return x.Name
	case *ast.StarExpr:
		return scopeName(x.X)
	default:
		return ""
	}
}

func (v *Visitor) recvFuncType(tname, fname string) *types.Func {
	_, obj := v.scope().LookupParent(tname, token.NoPos)
	st, ok := obj.(*types.TypeName)
	if !ok {
		return nil
	}
	named, ok := st.Type().(*types.Named)
	if !ok {
		return nil
	}
	for i := 0; i < named.NumMethods(); i++ {
		f := named.Method(i)
		if f.Name() == fname {
			return f
		}
	}
	return nil
}

func (v *Visitor) funcType(fd *ast.FuncDecl) *types.Func {
	fname := fd.Name.Name
	if fd.Recv == nil {
		f, ok := v.scope().Lookup(fname).(*types.Func)
		if !ok {
			return nil
		}
		return f
	}
	if len(fd.Recv.List) > 1 {
		return nil
	}
	tname := scopeName(fd.Recv.List[0].Type)
	return v.recvFuncType(tname, fname)
}

func typeMap(t *types.Tuple) map[string]types.Type {
	m := make(map[string]types.Type, t.Len())
	for i := 0; i < t.Len(); i++ {
		p := t.At(i)
		m[p.Name()] = p.Type()
	}
	return m
}

func (v *Visitor) Visit(node ast.Node) ast.Visitor {
	var top ast.Node
	if len(v.nodes) > 0 {
		top = v.nodes[len(v.nodes)-1]
	}
	switch x := node.(type) {
	case *ast.File:
	case *ast.FuncDecl:
		f := v.funcType(x)
		if f == nil {
			return nil
		}
		v.scopes = append(v.scopes, f.Scope())
		sign := f.Type().(*types.Signature)
		v.params = typeMap(sign.Params())
		v.used = make(map[string]map[string]call, 0)
		v.unknown = make(map[string]struct{})
	case *ast.CallExpr:
		if wasParamCall := v.onCall(x); wasParamCall {
			return nil
		}
	case *ast.BlockStmt:
		v.recordUnknown = true
	case *ast.Ident:
		if !v.recordUnknown {
			break
		}
		if _, e := v.params[x.Name]; e {
			v.unknown[x.Name] = struct{}{}
		}
	case nil:
		v.nodes = v.nodes[:len(v.nodes)-1]
		fd, ok := top.(*ast.FuncDecl)
		if !ok {
			return nil
		}
		v.scopes = v.scopes[:len(v.scopes)-1]
		v.funcEnded(fd)
		v.params = nil
		v.used = nil
		v.unknown = nil
		v.recordUnknown = false
	}
	if node != nil {
		v.nodes = append(v.nodes, node)
	}
	return v
}

func (v *Visitor) getType(name string) interface{} {
	_, obj := v.scope().LookupParent(name, token.NoPos)
	if obj == nil {
		return nil
	}
	switch x := obj.(type) {
	case *types.Var:
		return x.Type().String()
	default:
		return nil
	}
}

func (v *Visitor) descType(e ast.Expr) interface{} {
	switch x := e.(type) {
	case *ast.Ident:
		return v.getType(x.Name)
	case *ast.BasicLit:
		return x.Kind
	default:
		return nil
	}
}

func (v *Visitor) onCall(ce *ast.CallExpr) bool {
	if v.used == nil {
		return false
	}
	sel, ok := ce.Fun.(*ast.SelectorExpr)
	if !ok {
		return false
	}
	left, ok := sel.X.(*ast.Ident)
	if !ok {
		return false
	}
	right := sel.Sel
	vname := left.Name
	if _, e := v.params[vname]; !e {
		return false
	}
	tname, _ := v.descType(left).(string)
	fname := right.Name
	c := call{}
	f := v.recvFuncType(tname, fname)
	if f != nil {
		sign := f.Type().(*types.Signature)
		results := sign.Results()
		for i := 0; i < results.Len(); i++ {
			v := results.At(i)
			c.results = append(c.results, v.Type().String())
		}
	}
	for _, a := range ce.Args {
		c.params = append(c.params, v.descType(a))
	}
	if _, e := v.used[vname]; !e {
		v.used[vname] = make(map[string]call)
	}
	v.used[vname][fname] = c
	return true
}

func (v *Visitor) funcEnded(fd *ast.FuncDecl) {
	for name, methods := range v.used {
		if _, e := v.unknown[name]; e {
			continue
		}
		iface := interfaceMatching(methods)
		if iface == "" {
			continue
		}
		param, e := v.params[name]
		if !e {
			continue
		}
		if iface == param.String() {
			continue
		}
		pos := v.fset.Position(fd.Pos())
		fmt.Fprintf(v.w, "%s:%d: %s can be %s\n",
			pos.Filename, pos.Line, name, iface)
	}
}
<commit_msg>Use go/build to match wanted go files<commit_after>/* Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc> */
/* See LICENSE for licensing information */

package main

import (
	"flag"
	"fmt"
	"go/ast"
	"go/build"
	"go/importer"
	"go/parser"
	"go/token"
	"go/types"
	"io"
	"os"
	"path/filepath"
)

func init() {
	if err := typesInit(); err != nil {
		errExit(err)
	}
}

func main() {
	flag.Parse()
	if err := checkPaths(flag.Args(), os.Stdout); err != nil {
		errExit(err)
	}
}

func errExit(err error) {
	fmt.Fprintf(os.Stderr, "%v\n", err)
	os.Exit(1)
}

type call struct {
	params  []interface{}
	results []interface{}
}

var toToken = map[string]token.Token{
	"byte":    token.INT,
	"int":     token.INT,
	"int8":    token.INT,
	"int16":   token.INT,
	"int32":   token.INT,
	"int64":   token.INT,
	"uint":    token.INT,
	"uint8":   token.INT,
	"uint16":  token.INT,
	"uint32":  token.INT,
	"uint64":  token.INT,
	"string":  token.STRING,
	"float32": token.FLOAT,
	"float64": token.FLOAT,
}

func paramEqual(t1 types.Type, a2 interface{}) bool {
	switch x := a2.(type) {
	case string:
		return t1.String() == x
	case token.Token:
		return toToken[t1.String()] == x
	case nil:
		switch t1.(type) {
		case *types.Slice:
			return true
		case *types.Map:
			return true
		default:
			return false
		}
	default:
		return false
	}
}

func paramsMatch(types1 []types.Type, args2 []interface{}) bool {
	if len(types1) != len(args2) {
		return false
	}
	for i, t1 := range types1 {
		a2 := args2[i]
		if !paramEqual(t1, a2) {
			return false
		}
	}
	return true
}

func resultEqual(t1 types.Type, e2 interface{}) bool {
	switch x := e2.(type) {
	case string:
		return t1.String() == x
	case nil:
		// assigning to _
		return true
	default:
		return false
	}
}

func resultsMatch(types1 []types.Type, exps2 []interface{}) bool {
	if len(exps2) == 0 {
		return true
	}
	if len(types1) != len(exps2) {
		return false
	}
	for i, t1 := range types1 {
		e2 := exps2[i]
		if !resultEqual(t1, e2) {
			return false
		}
	}
	return true
}

func interfaceMatching(calls map[string]call) string {
	matchesIface := func(decls map[string]funcDecl) bool {
		if len(calls) > len(decls) {
			return false
		}
		for n, d := range decls {
			c, e := calls[n]
			if !e {
				return false
			}
			if !paramsMatch(d.params, c.params) {
				return false
			}
			if !resultsMatch(d.results, c.results) {
				return false
			}
		}
		return true
	}
	for name, decls := range parsed {
		if matchesIface(decls) {
			return name
		}
	}
	return ""
}

func getPaths(p string) ([]string, []string, error) {
	f, err := os.Open(p)
	if err != nil {
		return nil, nil, err
	}
	defer f.Close()
	info, err := f.Stat()
	if err != nil {
		return nil, nil, err
	}
	if !info.Mode().IsDir() {
		return []string{p}, nil, nil
	}
	names, err := f.Readdirnames(-1)
	if err != nil {
		return nil, nil, err
	}
	var gofiles, dirs []string
	for _, n := range names {
		fp := filepath.Join(p, n)
		info, err := os.Stat(fp)
		if err != nil {
			return nil, nil, err
		}
		if info.IsDir() {
			dirs = append(dirs, fp)
			continue
		}
		match, err := build.Default.MatchFile(p, n)
		if err != nil {
			return nil, nil, err
		}
		if !match {
			println(n)
			continue
		}
		gofiles = append(gofiles, fp)
	}
	return gofiles, dirs, err
}

func getPkgPaths(p string) ([][]string, error) {
	recursive := filepath.Base(p) == "..."
	if recursive {
		p = filepath.Dir(p)
	}
	gofiles, dirs, err := getPaths(p)
	if err != nil {
		return nil, err
	}
	if !recursive {
		dirs = nil
	}
	pkgPaths := [][]string{gofiles}
	dirQueue := dirs
	for len(dirQueue) > 0 {
		d := dirQueue[0]
		dirQueue = dirQueue[1:]
		gofiles, dirs, err := getPaths(d)
		if err != nil {
			return nil, err
		}
		pkgPaths = append(pkgPaths, gofiles)
		dirQueue = append(dirQueue, dirs...)
	}
	return pkgPaths, nil
}

func checkPaths(paths []string, w io.Writer) error {
	for _, p := range paths {
		pkgPaths, err := getPkgPaths(p)
		if err != nil {
			return err
		}
		for _, gofiles := range pkgPaths {
			if err := checkPkg(gofiles, w); err != nil {
				return err
			}
		}
	}
	return nil
}

func checkPkg(gofiles []string, w io.Writer) error {
	gp := &goPkg{
		fset: token.NewFileSet(),
	}
	for _, fp := range gofiles {
		if err := gp.parsePath(fp); err != nil {
			return err
		}
	}
	if err := gp.check(w); err != nil {
		return err
	}
	return nil
}

type goPkg struct {
	fset  *token.FileSet
	files []*ast.File
}

func (gp *goPkg) parsePath(fp string) error {
	f, err := os.Open(fp)
	if err != nil {
		return err
	}
	defer f.Close()
	if err := gp.parseReader(fp, f); err != nil {
		return err
	}
	return nil
}

func (gp *goPkg) parseReader(name string, r io.Reader) error {
	f, err := parser.ParseFile(gp.fset, name, r, 0)
	if err != nil {
		return err
	}
	gp.files = append(gp.files, f)
	return nil
}

func (gp *goPkg) check(w io.Writer) error {
	conf := types.Config{Importer: importer.Default()}
	pkg, err := conf.Check("", gp.fset, gp.files, nil)
	if err != nil {
		return err
	}

	v := &Visitor{
		w:      w,
		fset:   gp.fset,
		scopes: []*types.Scope{pkg.Scope()},
	}
	for _, f := range gp.files {
		ast.Walk(v, f)
	}
	return nil
}

type Visitor struct {
	w      io.Writer
	fset   *token.FileSet
	scopes []*types.Scope

	nodes []ast.Node

	params map[string]types.Type
	used   map[string]map[string]call

	// TODO: don't just discard params with untracked usage
	unknown       map[string]struct{}
	recordUnknown bool
}

func (v *Visitor) scope() *types.Scope {
	return v.scopes[len(v.scopes)-1]
}

func scopeName(e ast.Expr) string {
	switch x := e.(type) {
	case *ast.Ident:
		return x.Name
	case *ast.StarExpr:
		return scopeName(x.X)
	default:
		return ""
	}
}

func (v *Visitor) recvFuncType(tname, fname string) *types.Func {
	_, obj := v.scope().LookupParent(tname, token.NoPos)
	st, ok := obj.(*types.TypeName)
	if !ok {
		return nil
	}
	named, ok := st.Type().(*types.Named)
	if !ok {
		return nil
	}
	for i := 0; i < named.NumMethods(); i++ {
		f := named.Method(i)
		if f.Name() == fname {
			return f
		}
	}
	return nil
}

func (v *Visitor) funcType(fd *ast.FuncDecl) *types.Func {
	fname := fd.Name.Name
	if fd.Recv == nil {
		f, ok := v.scope().Lookup(fname).(*types.Func)
		if !ok {
			return nil
		}
		return f
	}
	if len(fd.Recv.List) > 1 {
		return nil
	}
	tname := scopeName(fd.Recv.List[0].Type)
	return v.recvFuncType(tname, fname)
}

func typeMap(t *types.Tuple) map[string]types.Type {
	m := make(map[string]types.Type, t.Len())
	for i := 0; i < t.Len(); i++ {
		p := t.At(i)
		m[p.Name()] = p.Type()
	}
	return m
}

func (v *Visitor) Visit(node ast.Node) ast.Visitor {
	var top ast.Node
	if len(v.nodes) > 0 {
		top = v.nodes[len(v.nodes)-1]
	}
	switch x := node.(type) {
	case *ast.File:
	case *ast.FuncDecl:
		f := v.funcType(x)
		if f == nil {
			return nil
		}
		v.scopes = append(v.scopes, f.Scope())
		sign := f.Type().(*types.Signature)
		v.params = typeMap(sign.Params())
		v.used = make(map[string]map[string]call, 0)
		v.unknown = make(map[string]struct{})
	case *ast.CallExpr:
		if wasParamCall := v.onCall(x); wasParamCall {
			return nil
		}
	case *ast.BlockStmt:
		v.recordUnknown = true
	case *ast.Ident:
		if !v.recordUnknown {
			break
		}
		if _, e := v.params[x.Name]; e {
			v.unknown[x.Name] = struct{}{}
		}
	case nil:
		v.nodes = v.nodes[:len(v.nodes)-1]
		fd, ok := top.(*ast.FuncDecl)
		if !ok {
			return nil
		}
		v.scopes = v.scopes[:len(v.scopes)-1]
		v.funcEnded(fd)
		v.params = nil
		v.used = nil
		v.unknown = nil
		v.recordUnknown = false
	}
	if node != nil {
		v.nodes = append(v.nodes, node)
	}
	return v
}

func (v *Visitor) getType(name string) interface{} {
	_, obj := v.scope().LookupParent(name, token.NoPos)
	if obj == nil {
		return nil
	}
	switch x := obj.(type) {
	case *types.Var:
		return x.Type().String()
	default:
		return nil
	}
}

func (v *Visitor) descType(e ast.Expr) interface{} {
	switch x := e.(type) {
	case *ast.Ident:
		return v.getType(x.Name)
	case *ast.BasicLit:
		return x.Kind
	default:
		return nil
	}
}

func (v *Visitor) onCall(ce *ast.CallExpr) bool {
	if v.used == nil {
		return false
	}
	sel, ok := ce.Fun.(*ast.SelectorExpr)
	if !ok {
		return false
	}
	left, ok := sel.X.(*ast.Ident)
	if !ok {
		return false
	}
	right := sel.Sel
	vname := left.Name
	if _, e := v.params[vname]; !e {
		return false
	}
	tname, _ := v.descType(left).(string)
	fname := right.Name
	c := call{}
	f := v.recvFuncType(tname, fname)
	if f != nil {
		sign := f.Type().(*types.Signature)
		results := sign.Results()
		for i := 0; i < results.Len(); i++ {
			v := results.At(i)
			c.results = append(c.results, v.Type().String())
		}
	}
	for _, a := range ce.Args {
		c.params = append(c.params, v.descType(a))
	}
	if _, e := v.used[vname]; !e {
		v.used[vname] = make(map[string]call)
	}
	v.used[vname][fname] = c
	return true
}

func (v *Visitor) funcEnded(fd *ast.FuncDecl) {
	for name, methods := range v.used {
		if _, e := v.unknown[name]; e {
			continue
		}
		iface := interfaceMatching(methods)
		if iface == "" {
			continue
		}
		param, e := v.params[name]
		if !e {
			continue
		}
		if iface == param.String() {
			continue
		}
		pos := v.fset.Position(fd.Pos())
		fmt.Fprintf(v.w, "%s:%d: %s can be %s\n",
			pos.Filename, pos.Line, name, iface)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/mpars0ns/sonar-es-go/helpers"
	"gopkg.in/olivere/elastic.v3"
	"log"
	//"os"
	"strings"
	"os"
)

type SonarImportedFile struct {
	File string `json:"file"`
	Sha1 string `json:"sha1"`
}

func main() {
	client, err := elastic.NewClient()
	import_check := helpers.Check_index_and_create("scansio-sonar-ssl-imported")
	if import_check == false {
		log.Fatal("We couldn't create a index properly exit out now!")
	}
	query := elastic.NewMatchAllQuery()
	searchResult, err := client.Search().Index("scansio-sonar-ssl-imported").Query(query).Do()
	if err != nil {
		log.Fatal("error running test MatchAll query on scansio index", err)
	}
	fmt.Println(searchResult.Hits.TotalHits)
	importedfiles := map[string]bool{}
	if searchResult.Hits.TotalHits > 0 {

		for _, hit := range searchResult.Hits.Hits {
			var t SonarImportedFile
			err := json.Unmarshal(*hit.Source, &t)

			if err != nil {
				fmt.Println("we had an error decoding hits from MatchAll: ", err)
			}
			importedfiles[t.File] = true
		}
	}
	res := helpers.DownloadFeed()
	for _, i := range res.Studies {
		if i.Uniqid == "sonar.ssl" {
			for _, f := range i.Files {
				fname := ""
				if strings.Contains(f.Name, "20131030-20150518") {
					fname = "20131030-20150518_certs.gz"
				} else {
					fname = f.Name[48:65]
				}
				if importedfiles[fname] {
					fmt.Printf("Already imported %v", fname)
					continue
				}
				if strings.Contains(fname, "certs.gz") {
					fmt.Printf("We need to import %v \n", fname)
					fmt.Printf("%v %v %v %v \n", f.Name, f.Size, f.Fingerprint, f.UpdatedAt)
					checkdownload, _ := helpers.Check_downloaded(fname, f.Fingerprint)
					if checkdownload == true {
						check, _ := helpers.Check_sha1(fname, f.Fingerprint)
						if check == false {
							err := helpers.DownloadFile(f.Name, fname)
							if err != nil {
								log.Fatal("We had an error on downloading file ", fname, err)
							}
							fmt.Printf("Download of file %v is successful \n", fname)
						}
					}

					err := helpers.DownloadFile(f.Name, fname)
					if err != nil {
						log.Fatal("We had an error in downloading file ", fname, err)
					}
					fmt.Printf("Download of file %v is successful", fname)
					check, err := helpers.Check_sha1(fname, f.Fingerprint)
					if err != nil {
						fmt.Printf("Error with sha1 on this file %v with error %v \n", f.Name, err)
						continue
					}
					if check == false {
						fmt.Printf("Error with sha1 on this file %v \n", f.Name)
						continue
					}
					helpers.Process_Certs(fname)
					parsed_file := SonarImportedFile{File: fname, Sha1: f.Fingerprint}
					_, certserr := client.Index().Index("scansio-sonar-ssl-imported").Type("imported-file").Id(f.Fingerprint).BodyJson(parsed_file).Do()
					if certserr != nil {
						// Handle error
						panic(err)
					}
					removeCertsErr := os.Remove(fname)
					if removeCertsErr != nil {
						fmt.Println(removeCertsErr)
					}
				}

				if strings.Contains(fname, "hosts.gz") {
					fmt.Printf("We need to import %v \n", fname)
					fmt.Printf("%v %v %v %v \n", f.Name, f.Size, f.Fingerprint, f.UpdatedAt)
					checkdownload, _ := helpers.Check_downloaded(fname, f.Fingerprint)
					if checkdownload == true {
						check, _ := helpers.Check_sha1(fname, f.Fingerprint)
						if check == false {
							err := helpers.DownloadFile(f.Name, fname)
							if err != nil {
								log.Fatal("We had an error on downloading file ", fname, err)
							}
							fmt.Printf("Download of file %v is successful \n", fname)
						}
					} else {
						err := helpers.DownloadFile(f.Name, fname)
						if err != nil {
							log.Fatal("We had an error on downloading file ", fname, err)
						}
						fmt.Printf("Download of file %v is successful \n", fname)
						check, err := helpers.Check_sha1(fname, f.Fingerprint)
						if err != nil {
							fmt.Printf("Error with sha1 on this file %v with error %v \n", f.Name, err)
						}
						if check == false {
							fmt.Printf("Error with sha1 on this file %v \n", f.Name)
							continue
						}
					}
					helpers.Process_Hosts(fname)
					parsed_file := SonarImportedFile{File: fname, Sha1: f.Fingerprint}
					_, hostserr := client.Index().Index("scansio-sonar-ssl-imported").Type("imported-file").Id(f.Fingerprint).BodyJson(parsed_file).Do()
					if hostserr != nil {
						// Handle error
						panic(hostserr)
					}
					removeHostsErr := os.Remove(fname)
					if removeHostsErr != nil {
						fmt.Println(removeHostsErr)
					}
				}
			}
		}
	}
}
<commit_msg>Logic problem with downloading cert<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/mpars0ns/sonar-es-go/helpers"
	"gopkg.in/olivere/elastic.v3"
	"log"
	//"os"
	"strings"
	"os"
)

type SonarImportedFile struct {
	File string `json:"file"`
	Sha1 string `json:"sha1"`
}

func main() {
	client, err := elastic.NewClient()
	import_check := helpers.Check_index_and_create("scansio-sonar-ssl-imported")
	if import_check == false {
		log.Fatal("We couldn't create a index properly exit out now!")
	}
	query := elastic.NewMatchAllQuery()
	searchResult, err := client.Search().Index("scansio-sonar-ssl-imported").Query(query).Do()
	if err != nil {
		log.Fatal("error running test MatchAll query on scansio index", err)
	}
	fmt.Println(searchResult.Hits.TotalHits)
	importedfiles := map[string]bool{}
	if searchResult.Hits.TotalHits > 0 {

		for _, hit := range searchResult.Hits.Hits {
			var t SonarImportedFile
			err := json.Unmarshal(*hit.Source, &t)

			if err != nil {
				fmt.Println("we had an error decoding hits from MatchAll: ", err)
			}
			importedfiles[t.File] = true
		}
	}
	res := helpers.DownloadFeed()
	for _, i := range res.Studies {
		if i.Uniqid == "sonar.ssl" {
			for _, f := range i.Files {
				fname := ""
				if strings.Contains(f.Name, "20131030-20150518") {
					fname = "20131030-20150518_certs.gz"
				} else {
					fname = f.Name[48:65]
				}
				if importedfiles[fname] {
					fmt.Printf("Already imported %v", fname)
					continue
				}
				if strings.Contains(fname, "certs.gz") {
					fmt.Printf("We need to import %v \n", fname)
					fmt.Printf("%v %v %v %v \n", f.Name, f.Size, f.Fingerprint, f.UpdatedAt)
					checkdownload, _ := helpers.Check_downloaded(fname, f.Fingerprint)
					if checkdownload == true {
						check, _ := helpers.Check_sha1(fname, f.Fingerprint)
						if check == false {
							fmt.Println("Checked File returned false")
							err := helpers.DownloadFile(f.Name, fname)
							if err != nil {
								log.Fatal("We had an error on downloading file ", fname, err)
							}
						}
						fmt.Printf("Download of file %v is successful \n", fname)

					} else {
						err := helpers.DownloadFile(f.Name, fname)
						if err != nil {
							log.Fatal("We had an error on downloading file ", fname, err)
						}
						fmt.Printf("Download of file %v is successful \n", fname)
						check, err := helpers.Check_sha1(fname, f.Fingerprint)
						if err != nil {
							fmt.Printf("Error with sha1 on this file %v with error %v \n", f.Name, err)
						}
						if check == false {
							fmt.Printf("Error with sha1 on this file %v \n", f.Name)
							continue
						}
					}
					err := helpers.DownloadFile(f.Name, fname)
					if err != nil {
						log.Fatal("We had an error in downloading file ", fname, err)
					}
					fmt.Printf("Download of file %v is successful", fname)
					check, err := helpers.Check_sha1(fname, f.Fingerprint)
					if err != nil {
						fmt.Printf("Error with sha1 on this file %v with error %v \n", f.Name, err)
						continue
					}
					if check == false {
						fmt.Printf("Error with sha1 on this file %v \n", f.Name)
						continue
					}
					helpers.Process_Certs(fname)
					parsed_file := SonarImportedFile{File: fname, Sha1: f.Fingerprint}
					_, certserr := client.Index().Index("scansio-sonar-ssl-imported").Type("imported-file").Id(f.Fingerprint).BodyJson(parsed_file).Do()
					if certserr != nil {
						// Handle error
						panic(err)
					}
					removeCertsErr := os.Remove(fname)
					if removeCertsErr != nil {
						fmt.Println(removeCertsErr)
					}
				}

				if strings.Contains(fname, "hosts.gz") {
					fmt.Printf("We need to import %v \n", fname)
					fmt.Printf("%v %v %v %v \n", f.Name, f.Size, f.Fingerprint, f.UpdatedAt)
					checkdownload, _ := helpers.Check_downloaded(fname, f.Fingerprint)
					if checkdownload == true {
						check, _ := helpers.Check_sha1(fname, f.Fingerprint)
						if check == false {
							err := helpers.DownloadFile(f.Name, fname)
							if err != nil {
								log.Fatal("We had an error on downloading file ", fname, err)
							}
							fmt.Printf("Download of file %v is successful \n", fname)
						}
					} else {
						err := helpers.DownloadFile(f.Name, fname)
						if err != nil {
							log.Fatal("We had an error on downloading file ", fname, err)
						}
						fmt.Printf("Download of file %v is successful \n", fname)
						check, err := helpers.Check_sha1(fname, f.Fingerprint)
						if err != nil {
							fmt.Printf("Error with sha1 on this file %v with error %v \n", f.Name, err)
						}
						if check == false {
							fmt.Printf("Error with sha1 on this file %v \n", f.Name)
							continue
						}
					}
					helpers.Process_Hosts(fname)
					parsed_file := SonarImportedFile{File: fname, Sha1: f.Fingerprint}
					_, hostserr := client.Index().Index("scansio-sonar-ssl-imported").Type("imported-file").Id(f.Fingerprint).BodyJson(parsed_file).Do()
					if hostserr != nil {
						// Handle error
						panic(hostserr)
					}
					removeHostsErr := os.Remove(fname)
					if removeHostsErr != nil {
						fmt.Println(removeHostsErr)
					}
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"
)

var config struct {
	Pid     string
	Timeout int
	Port    int
	LogPath string
}

func main() {
	// Set up config
	flag.IntVar(&config.Timeout, "timeout", 30, "timeout, in seconds")
	flag.IntVar(&config.Port, "port", 8080, "HTTP service port")
	flag.StringVar(&config.Pid, "pid", "demo-pid-01", "participant ID")
	flag.StringVar(&config.LogPath, "log", "liveqa.log", "path to log file")
	index := flag.String("index", "", "path to Indri index")
	flag.Parse()

	// Create a liveQA
	lqa := NewLiveQA()

	// Add a dummy answer producer to it
	lqa.AddProducer(&DummyAnswerProducer{})

	// Add an indri index answer producer
	iap, err := NewIndriIndexAnswerProducer(*index)
	if err != nil {
		log.Fatal("[indri index]", err)
	}

	lqa.AddProducer(iap)

	http.Handle("/", lqa)

	// Set up logging
	logfile, err := os.OpenFile(config.LogPath,
		os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
	if err == nil {
		log.SetOutput(io.MultiWriter(os.Stderr, logfile))

		lw := NewLogWatch(config.LogPath)
		http.Handle("/tail1000", lw)
	} else {
		log.Fatalf("[flag -logfile] %s", err)
		log.Fatalln("will carry on without the logfile")
	}

	log.Fatal(http.ListenAndServe(":"+strconv.Itoa(config.Port), nil))
}
<commit_msg>Replaced fatal log call with print<commit_after>package main

import (
	"flag"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"
)

var config struct {
	Pid     string
	Timeout int
	Port    int
	LogPath string
}

func main() {
	// Set up config
	flag.IntVar(&config.Timeout, "timeout", 30, "timeout, in seconds")
	flag.IntVar(&config.Port, "port", 8080, "HTTP service port")
	flag.StringVar(&config.Pid, "pid", "demo-pid-01", "participant ID")
	flag.StringVar(&config.LogPath, "log", "liveqa.log", "path to log file")
	index := flag.String("index", "", "path to Indri index")
	flag.Parse()

	// Create a liveQA
	lqa := NewLiveQA()

	// Add a dummy answer producer to it
	lqa.AddProducer(&DummyAnswerProducer{})

	// Add an indri index answer producer
	iap, err := NewIndriIndexAnswerProducer(*index)
	if err != nil {
		log.Fatal("[indri index]", err)
	}

	lqa.AddProducer(iap)

	http.Handle("/", lqa)

	// Set up logging
	logfile, err := os.OpenFile(config.LogPath,
		os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
	if err == nil {
		log.SetOutput(io.MultiWriter(os.Stderr, logfile))

		lw := NewLogWatch(config.LogPath)
		http.Handle("/tail1000", lw)
	} else {
		log.Printf("[flag -logfile] '%s' will carry on without the logfile\n", err)
	}

	log.Fatal(http.ListenAndServe(":"+strconv.Itoa(config.Port), nil))
}
<|endoftext|>


<commit_before>// Package c2go contains the main function for running the executable.
//
// Installation
//
//     go get -u github.com/elliotchance/c2go
//
// Usage
//
//     c2go myfile.c
//
package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"runtime"
	"strings"

	"errors"

	"github.com/elliotchance/c2go/ast"
	"github.com/elliotchance/c2go/preprocessor"
	"github.com/elliotchance/c2go/program"
	"github.com/elliotchance/c2go/transpiler"
)

// Version can be requested through the command line with:
//
//     c2go -v
//
// See https://github.com/elliotchance/c2go/wiki/Release-Process
const Version = "v0.19.1 Uranium 2017-12-20"

var stderr io.Writer = os.Stderr

// ProgramArgs defines the options available when processing the program. There
// is no constructor since the zeroed out values are the appropriate defaults -
// you need only set the options you need.
//
// TODO: Better separation on CLI modes
// https://github.com/elliotchance/c2go/issues/134
//
// Do not instantiate this directly. Instead use DefaultProgramArgs(); then
// modify any specific attributes.
type ProgramArgs struct {
	verbose     bool
	ast         bool
	inputFiles  []string
	clangFlags  []string
	outputFile  string
	packageName string

	// A private option to output the Go as a *_test.go file.
	outputAsTest bool
}

// DefaultProgramArgs default value of ProgramArgs
func DefaultProgramArgs() ProgramArgs {
	return ProgramArgs{
		verbose:      false,
		ast:          false,
		packageName:  "main",
		clangFlags:   []string{},
		outputAsTest: false,
	}
}

func readAST(data []byte) []string {
	return strings.Split(string(data), "\n")
}

type treeNode struct {
	indent int
	node   ast.Node
}

func convertLinesToNodes(lines []string) []treeNode {
	nodes := make([]treeNode, len(lines))
	var counter int
	for _, line := range lines {
		if strings.TrimSpace(line) == "" {
			continue
		}

		// It is tempting to discard null AST nodes, but these may
		// have semantic importance: for example, they represent omitted
		// for-loop conditions, as in for(;;).
		line = strings.Replace(line, "<<<NULL>>>", "NullStmt", 1)
		trimmed := strings.TrimLeft(line, "|\\- `")
		node := ast.Parse(trimmed)
		indentLevel := (len(line) - len(trimmed)) / 2
		nodes[counter] = treeNode{indentLevel, node}
		counter++
	}
	nodes = nodes[0:counter]

	return nodes
}

func convertLinesToNodesParallel(lines []string) []treeNode {
	// function f separate full list on 2 parts and
	// then each part can recursive run function f
	var f func([]string, int) []treeNode

	f = func(lines []string, deep int) []treeNode {
		deep = deep - 2
		part := len(lines) / 2

		var tr1 = make(chan []treeNode)
		var tr2 = make(chan []treeNode)

		go func(lines []string, deep int) {
			if deep <= 0 || len(lines) < deep {
				tr1 <- convertLinesToNodes(lines)
				return
			}
			tr1 <- f(lines, deep)
		}(lines[0:part], deep)

		go func(lines []string, deep int) {
			if deep <= 0 || len(lines) < deep {
				tr2 <- convertLinesToNodes(lines)
				return
			}
			tr2 <- f(lines, deep)
		}(lines[part:], deep)

		defer close(tr1)
		defer close(tr2)

		return append(<-tr1, <-tr2...)
	}

	// Parameter of deep - can be any, but effective to use
	// same amount of CPU
	return f(lines, runtime.NumCPU())
}

// buildTree converts an array of nodes, each prefixed with a depth into a tree.
func buildTree(nodes []treeNode, depth int) []ast.Node {
	if len(nodes) == 0 {
		return []ast.Node{}
	}

	// Split the list into sections, treat each section as a tree with its own
	// root.
	sections := [][]treeNode{}
	for _, node := range nodes {
		if node.indent == depth {
			sections = append(sections, []treeNode{node})
		} else {
			sections[len(sections)-1] = append(sections[len(sections)-1], node)
		}
	}

	results := []ast.Node{}
	for _, section := range sections {
		slice := []treeNode{}
		for _, n := range section {
			if n.indent > depth {
				slice = append(slice, n)
			}
		}

		children := buildTree(slice, depth+1)
		for _, child := range children {
			section[0].node.AddChild(child)
		}
		results = append(results, section[0].node)
	}

	return results
}

// Start begins transpiling an input file.
func Start(args ProgramArgs) (err error) {
	if args.verbose {
		fmt.Println("Start tanspiling ...")
	}

	if os.Getenv("GOPATH") == "" {
		return fmt.Errorf("The $GOPATH must be set")
	}

	// 1. Compile it first (checking for errors)
	for _, in := range args.inputFiles {
		_, err := os.Stat(in)
		if err != nil {
			return fmt.Errorf("Input file %s is not found", in)
		}
	}

	// 2. Preprocess
	if args.verbose {
		fmt.Println("Running clang preprocessor...")
	}

	pp, err := preprocessor.Analyze(args.inputFiles, args.clangFlags)
	if err != nil {
		return err
	}

	if args.verbose {
		fmt.Println("Writing preprocessor ...")
	}
	dir, err := ioutil.TempDir("", "c2go")
	if err != nil {
		return fmt.Errorf("Cannot create temp folder: %v", err)
	}
	defer os.RemoveAll(dir) // clean up

	ppFilePath := path.Join(dir, "pp.c")
	err = ioutil.WriteFile(ppFilePath, pp, 0644)
	if err != nil {
		return fmt.Errorf("writing to %s failed: %v", ppFilePath, err)
	}

	// 3. Generate JSON from AST
	if args.verbose {
		fmt.Println("Running clang for AST tree...")
	}
	astPP, err := exec.Command("clang", "-Xclang", "-ast-dump", "-fsyntax-only", "-fno-color-diagnostics", ppFilePath).Output()
	if err != nil {
		// If clang fails it still prints out the AST, so we have to run it
		// again to get the real error.
		errBody, _ := exec.Command("clang", ppFilePath).CombinedOutput()

		panic("clang failed: " + err.Error() + ":\n\n" + string(errBody))
	}

	if args.verbose {
		fmt.Println("Reading clang AST tree...")
	}
	lines := readAST(astPP)
	if args.ast {
		for _, l := range lines {
			fmt.Println(l)
		}
		fmt.Println()

		return nil
	}

	p := program.NewProgram()
	p.Verbose = args.verbose
	p.OutputAsTest = args.outputAsTest

	// Converting to nodes
	if args.verbose {
		fmt.Println("Converting to nodes...")
	}
	nodes := convertLinesToNodesParallel(lines)

	// build tree
	if args.verbose {
		fmt.Println("Building tree...")
	}
	tree := buildTree(nodes, 0)
	ast.FixPositions(tree)

	// Repair the floating literals. See RepairFloatingLiteralsFromSource for
	// more information.
	floatingErrors := ast.RepairFloatingLiteralsFromSource(tree[0], ppFilePath)

	for _, fErr := range floatingErrors {
		message := fmt.Sprintf("could not read exact floating literal: %s",
			fErr.Err.Error())
		p.AddMessage(p.GenerateWarningMessage(errors.New(message), fErr.Node))
	}

	outputFilePath := args.outputFile

	if outputFilePath == "" {
		// Choose inputFile for creating name of output file
		input := args.inputFiles[0]
		// We choose name for output Go code at the base
		// on filename for choosed input file
		cleanFileName := filepath.Clean(filepath.Base(input))
		extension := filepath.Ext(input)
		outputFilePath = cleanFileName[0:len(cleanFileName)-len(extension)] + ".go"
	}

	// transpile ast tree
	if args.verbose {
		fmt.Println("Transpiling tree...")
	}

	err = transpiler.TranspileAST(args.outputFile, args.packageName, p, tree[0].(ast.Node))
	if err != nil {
		return fmt.Errorf("cannot transpile AST : %v", err)
	}

	// write the output Go code
	if args.verbose {
		fmt.Println("Writing the output Go code...")
	}
	err = ioutil.WriteFile(outputFilePath, []byte(p.String()), 0644)
	if err != nil {
		return fmt.Errorf("writing Go output file failed: %v", err)
	}

	return nil
}

type inputDataFlags []string

func (i *inputDataFlags) String() (s string) {
	for pos, item := range *i {
		s += fmt.Sprintf("Flag %d. %s\n", pos, item)
	}
	return
}

func (i *inputDataFlags) Set(value string) error {
	*i = append(*i, value)
	return nil
}

var clangFlags inputDataFlags

func init() {
	transpileCommand.Var(&clangFlags, "clang-flag", "Pass arguments to clang. You may provide multiple -clang-flag items.")
}

var (
	versionFlag       = flag.Bool("v", false, "print the version and exit")
	transpileCommand  = flag.NewFlagSet("transpile", flag.ContinueOnError)
	verboseFlag       = transpileCommand.Bool("V", false, "print progress as comments")
	outputFlag        = transpileCommand.String("o", "", "output Go generated code to the specified file")
	packageFlag       = transpileCommand.String("p", "main", "set the name of the generated package")
	transpileHelpFlag = transpileCommand.Bool("h", false, "print help information")
	astCommand        = flag.NewFlagSet("ast", flag.ContinueOnError)
	astHelpFlag       = astCommand.Bool("h", false, "print help information")
)

func main() {
	code := runCommand()
	if code != 0 {
		os.Exit(code)
	}
}

func runCommand() int {

	flag.Usage = func() {
		usage := "Usage: %s [-v] [<command>] [<flags>] file1.c ...\n\n"
		usage += "Commands:\n"
		usage += "  transpile\ttranspile an input C source file or files to Go\n"
		usage += "  ast\t\tprint AST before translated Go code\n\n"

		usage += "Flags:\n"
		fmt.Fprintf(stderr, usage, os.Args[0])
		flag.PrintDefaults()
	}

	transpileCommand.SetOutput(stderr)
	astCommand.SetOutput(stderr)

	flag.Parse()

	if *versionFlag {
		// Simply print out the version and exit.
		fmt.Println(Version)
		return 0
	}

	if flag.NArg() < 1 {
		flag.Usage()
		return 1
	}

	args := DefaultProgramArgs()

	switch os.Args[1] {
	case "ast":
		err := astCommand.Parse(os.Args[2:])
		if err != nil {
			fmt.Printf("ast command cannot parse: %v", err)
			return 1
		}

		if *astHelpFlag || astCommand.NArg() == 0 {
			fmt.Fprintf(stderr, "Usage: %s ast file.c\n", os.Args[0])
			astCommand.PrintDefaults()
			return 1
		}

		args.ast = true
		args.inputFiles = astCommand.Args()
	case "transpile":
		err := transpileCommand.Parse(os.Args[2:])
		if err != nil {
			fmt.Printf("transpile command cannot parse: %v", err)
			return 1
		}

		if *transpileHelpFlag || transpileCommand.NArg() == 0 {
			fmt.Fprintf(stderr, "Usage: %s transpile [-V] [-o file.go] [-p package] file1.c ...\n", os.Args[0])
			transpileCommand.PrintDefaults()
			return 1
		}

		args.inputFiles = transpileCommand.Args()
		args.outputFile = *outputFlag
		args.packageName = *packageFlag
		args.verbose = *verboseFlag
		args.clangFlags = clangFlags
	default:
		flag.Usage()
		return 1
	}

	if err := Start(args); err != nil {
		fmt.Printf("Error: %v\n", err)
		return 1
	}

	return 0
}
<commit_msg>Bump version: v0.19.2 Uranium 2017-12-21<commit_after>// Package c2go contains the main function for running the executable.
//
// Installation
//
//     go get -u github.com/elliotchance/c2go
//
// Usage
//
//     c2go myfile.c
//
package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"runtime"
	"strings"

	"errors"

	"github.com/elliotchance/c2go/ast"
	"github.com/elliotchance/c2go/preprocessor"
	"github.com/elliotchance/c2go/program"
	"github.com/elliotchance/c2go/transpiler"
)

// Version can be requested through the command line with:
//
//     c2go -v
//
// See https://github.com/elliotchance/c2go/wiki/Release-Process
const Version = "v0.19.2 Uranium 2017-12-21"

var stderr io.Writer = os.Stderr

// ProgramArgs defines the options available when processing the program. There
// is no constructor since the zeroed out values are the appropriate defaults -
// you need only set the options you need.
//
// TODO: Better separation on CLI modes
// https://github.com/elliotchance/c2go/issues/134
//
// Do not instantiate this directly. Instead use DefaultProgramArgs(); then
// modify any specific attributes.
type ProgramArgs struct {
	verbose     bool
	ast         bool
	inputFiles  []string
	clangFlags  []string
	outputFile  string
	packageName string

	// A private option to output the Go as a *_test.go file.
	outputAsTest bool
}

// DefaultProgramArgs default value of ProgramArgs
func DefaultProgramArgs() ProgramArgs {
	return ProgramArgs{
		verbose:      false,
		ast:          false,
		packageName:  "main",
		clangFlags:   []string{},
		outputAsTest: false,
	}
}

func readAST(data []byte) []string {
	return strings.Split(string(data), "\n")
}

type treeNode struct {
	indent int
	node   ast.Node
}

func convertLinesToNodes(lines []string) []treeNode {
	nodes := make([]treeNode, len(lines))
	var counter int
	for _, line := range lines {
		if strings.TrimSpace(line) == "" {
			continue
		}

		// It is tempting to discard null AST nodes, but these may
		// have semantic importance: for example, they represent omitted
		// for-loop conditions, as in for(;;).
		line = strings.Replace(line, "<<<NULL>>>", "NullStmt", 1)
		trimmed := strings.TrimLeft(line, "|\\- `")
		node := ast.Parse(trimmed)
		indentLevel := (len(line) - len(trimmed)) / 2
		nodes[counter] = treeNode{indentLevel, node}
		counter++
	}
	nodes = nodes[0:counter]

	return nodes
}

func convertLinesToNodesParallel(lines []string) []treeNode {
	// function f separate full list on 2 parts and
	// then each part can recursive run function f
	var f func([]string, int) []treeNode

	f = func(lines []string, deep int) []treeNode {
		deep = deep - 2
		part := len(lines) / 2

		var tr1 = make(chan []treeNode)
		var tr2 = make(chan []treeNode)

		go func(lines []string, deep int) {
			if deep <= 0 || len(lines) < deep {
				tr1 <- convertLinesToNodes(lines)
				return
			}
			tr1 <- f(lines, deep)
		}(lines[0:part], deep)

		go func(lines []string, deep int) {
			if deep <= 0 || len(lines) < deep {
				tr2 <- convertLinesToNodes(lines)
				return
			}
			tr2 <- f(lines, deep)
		}(lines[part:], deep)

		defer close(tr1)
		defer close(tr2)

		return append(<-tr1, <-tr2...)
	}

	// Parameter of deep - can be any, but effective to use
	// same amount of CPU
	return f(lines, runtime.NumCPU())
}

// buildTree converts an array of nodes, each prefixed with a depth into a tree.
func buildTree(nodes []treeNode, depth int) []ast.Node {
	if len(nodes) == 0 {
		return []ast.Node{}
	}

	// Split the list into sections, treat each section as a tree with its own
	// root.
	sections := [][]treeNode{}
	for _, node := range nodes {
		if node.indent == depth {
			sections = append(sections, []treeNode{node})
		} else {
			sections[len(sections)-1] = append(sections[len(sections)-1], node)
		}
	}

	results := []ast.Node{}
	for _, section := range sections {
		slice := []treeNode{}
		for _, n := range section {
			if n.indent > depth {
				slice = append(slice, n)
			}
		}

		children := buildTree(slice, depth+1)
		for _, child := range children {
			section[0].node.AddChild(child)
		}
		results = append(results, section[0].node)
	}

	return results
}

// Start begins transpiling an input file.
func Start(args ProgramArgs) (err error) {
	if args.verbose {
		fmt.Println("Start tanspiling ...")
	}

	if os.Getenv("GOPATH") == "" {
		return fmt.Errorf("The $GOPATH must be set")
	}

	// 1. Compile it first (checking for errors)
	for _, in := range args.inputFiles {
		_, err := os.Stat(in)
		if err != nil {
			return fmt.Errorf("Input file %s is not found", in)
		}
	}

	// 2. Preprocess
	if args.verbose {
		fmt.Println("Running clang preprocessor...")
	}

	pp, err := preprocessor.Analyze(args.inputFiles, args.clangFlags)
	if err != nil {
		return err
	}

	if args.verbose {
		fmt.Println("Writing preprocessor ...")
	}
	dir, err := ioutil.TempDir("", "c2go")
	if err != nil {
		return fmt.Errorf("Cannot create temp folder: %v", err)
	}
	defer os.RemoveAll(dir) // clean up

	ppFilePath := path.Join(dir, "pp.c")
	err = ioutil.WriteFile(ppFilePath, pp, 0644)
	if err != nil {
		return fmt.Errorf("writing to %s failed: %v", ppFilePath, err)
	}

	// 3. Generate JSON from AST
	if args.verbose {
		fmt.Println("Running clang for AST tree...")
	}
	astPP, err := exec.Command("clang", "-Xclang", "-ast-dump", "-fsyntax-only", "-fno-color-diagnostics", ppFilePath).Output()
	if err != nil {
		// If clang fails it still prints out the AST, so we have to run it
		// again to get the real error.
		errBody, _ := exec.Command("clang", ppFilePath).CombinedOutput()

		panic("clang failed: " + err.Error() + ":\n\n" + string(errBody))
	}

	if args.verbose {
		fmt.Println("Reading clang AST tree...")
	}
	lines := readAST(astPP)
	if args.ast {
		for _, l := range lines {
			fmt.Println(l)
		}
		fmt.Println()

		return nil
	}

	p := program.NewProgram()
	p.Verbose = args.verbose
	p.OutputAsTest = args.outputAsTest

	// Converting to nodes
	if args.verbose {
		fmt.Println("Converting to nodes...")
	}
	nodes := convertLinesToNodesParallel(lines)

	// build tree
	if args.verbose {
		fmt.Println("Building tree...")
	}
	tree := buildTree(nodes, 0)
	ast.FixPositions(tree)

	// Repair the floating literals. See RepairFloatingLiteralsFromSource for
	// more information.
	floatingErrors := ast.RepairFloatingLiteralsFromSource(tree[0], ppFilePath)

	for _, fErr := range floatingErrors {
		message := fmt.Sprintf("could not read exact floating literal: %s",
			fErr.Err.Error())
		p.AddMessage(p.GenerateWarningMessage(errors.New(message), fErr.Node))
	}

	outputFilePath := args.outputFile

	if outputFilePath == "" {
		// Choose inputFile for creating name of output file
		input := args.inputFiles[0]
		// We choose name for output Go code at the base
		// on filename for choosed input file
		cleanFileName := filepath.Clean(filepath.Base(input))
		extension := filepath.Ext(input)
		outputFilePath = cleanFileName[0:len(cleanFileName)-len(extension)] + ".go"
	}

	// transpile ast tree
	if args.verbose {
		fmt.Println("Transpiling tree...")
	}

	err = transpiler.TranspileAST(args.outputFile, args.packageName, p, tree[0].(ast.Node))
	if err != nil {
		return fmt.Errorf("cannot transpile AST : %v", err)
	}

	// write the output Go code
	if args.verbose {
		fmt.Println("Writing the output Go code...")
	}
	err = ioutil.WriteFile(outputFilePath, []byte(p.String()), 0644)
	if err != nil {
		return fmt.Errorf("writing Go output file failed: %v", err)
	}

	return nil
}

type inputDataFlags []string

func (i *inputDataFlags) String() (s string) {
	for pos, item := range *i {
		s += fmt.Sprintf("Flag %d. %s\n", pos, item)
	}
	return
}

func (i *inputDataFlags) Set(value string) error {
	*i = append(*i, value)
	return nil
}

var clangFlags inputDataFlags

func init() {
	transpileCommand.Var(&clangFlags, "clang-flag", "Pass arguments to clang. You may provide multiple -clang-flag items.")
}

var (
	versionFlag       = flag.Bool("v", false, "print the version and exit")
	transpileCommand  = flag.NewFlagSet("transpile", flag.ContinueOnError)
	verboseFlag       = transpileCommand.Bool("V", false, "print progress as comments")
	outputFlag        = transpileCommand.String("o", "", "output Go generated code to the specified file")
	packageFlag       = transpileCommand.String("p", "main", "set the name of the generated package")
	transpileHelpFlag = transpileCommand.Bool("h", false, "print help information")
	astCommand        = flag.NewFlagSet("ast", flag.ContinueOnError)
	astHelpFlag       = astCommand.Bool("h", false, "print help information")
)

func main() {
	code := runCommand()
	if code != 0 {
		os.Exit(code)
	}
}

func runCommand() int {

	flag.Usage = func() {
		usage := "Usage: %s [-v] [<command>] [<flags>] file1.c ...\n\n"
		usage += "Commands:\n"
		usage += "  transpile\ttranspile an input C source file or files to Go\n"
		usage += "  ast\t\tprint AST before translated Go code\n\n"

		usage += "Flags:\n"
		fmt.Fprintf(stderr, usage, os.Args[0])
		flag.PrintDefaults()
	}

	transpileCommand.SetOutput(stderr)
	astCommand.SetOutput(stderr)

	flag.Parse()

	if *versionFlag {
		// Simply print out the version and exit.
		fmt.Println(Version)
		return 0
	}

	if flag.NArg() < 1 {
		flag.Usage()
		return 1
	}

	args := DefaultProgramArgs()

	switch os.Args[1] {
	case "ast":
		err := astCommand.Parse(os.Args[2:])
		if err != nil {
			fmt.Printf("ast command cannot parse: %v", err)
			return 1
		}

		if *astHelpFlag || astCommand.NArg() == 0 {
			fmt.Fprintf(stderr, "Usage: %s ast file.c\n", os.Args[0])
			astCommand.PrintDefaults()
			return 1
		}

		args.ast = true
		args.inputFiles = astCommand.Args()
	case "transpile":
		err := transpileCommand.Parse(os.Args[2:])
		if err != nil {
			fmt.Printf("transpile command cannot parse: %v", err)
			return 1
		}

		if *transpileHelpFlag || transpileCommand.NArg() == 0 {
			fmt.Fprintf(stderr, "Usage: %s transpile [-V] [-o file.go] [-p package] file1.c ...\n", os.Args[0])
			transpileCommand.PrintDefaults()
			return 1
		}

		args.inputFiles = transpileCommand.Args()
		args.outputFile = *outputFlag
		args.packageName = *packageFlag
		args.verbose = *verboseFlag
		args.clangFlags = clangFlags
	default:
		flag.Usage()
		return 1
	}

	if err := Start(args); err != nil {
		fmt.Printf("Error: %v\n", err)
		return 1
	}

	return 0
}
<|endoftext|>