<commit_before>package ccg

//go:generate myccg -uses AstDecls,AstDecls.Filter -package ccg -output utils.go slice ast.Decl AstDecls
//go:generate myccg -uses AstSpecs,AstSpecs.Filter -package ccg -output utils.go slice ast.Spec AstSpecs

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/format"
	"go/token"
	"io"

	"golang.org/x/tools/go/loader"
	"golang.org/x/tools/go/types"
	"golang.org/x/tools/imports"
)

var (
	pt = fmt.Printf
)

type Config struct {
	From        string
	Params      map[string]string
	Renames     map[string]string
	Writer      io.Writer
	Package     string
	Decls       []ast.Decl
	NameFilters []func(string) bool
	FileSet     *token.FileSet
}

func Copy(config Config) error {
	// load package
	loadConf := loader.Config{
		Fset: config.FileSet,
	}
	loadConf.Import(config.From)
	program, err := loadConf.Load()
	if err != nil {
		return fmt.Errorf("ccg: load package %v", err)
	}
	info := program.Imported[config.From]

	// remove param declarations
	for _, f := range info.Files {
		f.Decls = AstDecls(f.Decls).Filter(func(decl ast.Decl) (ret bool) {
			if decl, ok := decl.(*ast.GenDecl); !ok {
				return true
			} else {
				if decl.Tok == token.TYPE {
					decl.Specs = AstSpecs(decl.Specs).Filter(func(spec ast.Spec) bool {
						name := spec.(*ast.TypeSpec).Name.Name
						_, exists := config.Params[name]
						return !exists
					})
					ret = len(decl.Specs) > 0
				} else if decl.Tok == token.VAR {
					decl.Specs = AstSpecs(decl.Specs).Filter(func(sp ast.Spec) bool {
						spec := sp.(*ast.ValueSpec)
						names := []*ast.Ident{}
						values := []ast.Expr{}
						for i, name := range spec.Names {
							if _, exists := config.Params[name.Name]; !exists {
								names = append(names, name)
								if i < len(spec.Values) {
									values = append(values, spec.Values[i])
								}
							}
						}
						spec.Names = names
						if len(values) == 0 {
							spec.Values = nil
						} else {
							spec.Values = values
						}
						return len(spec.Names) > 0
					})
					ret = len(decl.Specs) > 0
				}
			}
			return
		})
	}

	// collect objects to rename
	objects := make(map[types.Object]string)
	collectObjects := func(mapping map[string]string) error {
		for from, to := range mapping {
			obj := info.Pkg.Scope().Lookup(from)
			if obj == nil {
				return fmt.Errorf("ccg: name not found %s", from)
			}
			objects[obj] = to
		}
		return nil
	}
	if err := collectObjects(config.Params); err != nil {
		return err
	}
	if err := collectObjects(config.Renames); err != nil {
		return err
	}

	// rename
	rename := func(defs map[*ast.Ident]types.Object) {
		for id, obj := range defs {
			if to, ok := objects[obj]; ok {
				id.Name = to
			}
		}
	}
	rename(info.Defs)
	rename(info.Uses)

	// collect existing decls
	existingVars := make(map[string]func(expr ast.Expr))
	existingTypes := make(map[string]func(expr ast.Expr))
	existingFuncs := make(map[string]func(fn *ast.FuncDecl))
	for i, decl := range config.Decls {
		switch decl := decl.(type) {
		case *ast.GenDecl:
			switch decl.Tok {
			case token.VAR:
				for _, spec := range decl.Specs {
					spec := spec.(*ast.ValueSpec)
					for i, name := range spec.Names {
						i := i
						spec := spec
						existingVars[name.Name] = func(expr ast.Expr) {
							spec.Values[i] = expr
						}
					}
				}
			case token.TYPE:
				for i, spec := range decl.Specs {
					spec := spec.(*ast.TypeSpec)
					i := i
					decl := decl
					existingTypes[spec.Name.Name] = func(expr ast.Expr) {
						decl.Specs[i].(*ast.TypeSpec).Type = expr
					}
				}
			}
		case *ast.FuncDecl:
			name := decl.Name.Name
			if decl.Recv != nil {
				name = decl.Recv.List[0].Type.(*ast.Ident).Name + "." + name
			}
			i := i
			existingFuncs[name] = func(fndecl *ast.FuncDecl) {
				config.Decls[i] = fndecl
			}
		}
	}

	// collect output declarations
	decls := []ast.Decl{}
	for _, f := range info.Files {
	loopSpec:
		for _, decl := range f.Decls {
			switch decl := decl.(type) {
			case *ast.GenDecl:
				switch decl.Tok {
				// var
				case token.VAR:
					newDecl := &ast.GenDecl{
						Tok: token.VAR,
					}
					for _, spec := range decl.Specs {
						spec := spec.(*ast.ValueSpec)
					loopVarName:
						for i, name := range spec.Names {
							for _, filter := range config.NameFilters {
								if !filter(name.Name) {
									continue loopVarName
								}
							}
							if mutator, ok := existingVars[name.Name]; ok {
								mutator(spec.Values[i])
							} else {
								newDecl.Specs = append(newDecl.Specs, spec)
							}
						}
					}
					if len(newDecl.Specs) > 0 {
						decls = append(decls, newDecl)
					}
				// type
				case token.TYPE:
					newDecl := &ast.GenDecl{
						Tok: token.TYPE,
					}
				loopTypeSpec:
					for _, spec := range decl.Specs {
						spec := spec.(*ast.TypeSpec)
						name := spec.Name.Name
						for _, filter := range config.NameFilters {
							if !filter(name) {
								continue loopTypeSpec
							}
						}
						if mutator, ok := existingTypes[name]; ok {
							mutator(spec.Type)
						} else {
							newDecl.Specs = append(newDecl.Specs, spec)
						}
					}
					if len(newDecl.Specs) > 0 {
						decls = append(decls, newDecl)
					}
				}
			// func
			case *ast.FuncDecl:
				name := decl.Name.Name
				for _, filter := range config.NameFilters {
					if !filter(name) {
						continue loopSpec
					}
				}
				if decl.Recv != nil {
					name = decl.Recv.List[0].Type.(*ast.Ident).Name + "." + name
				}
				if mutator, ok := existingFuncs[name]; ok {
					mutator(decl)
				} else {
					config.Decls = append(config.Decls, decl)
				}
			}
		}
	}
	decls = append(decls, config.Decls...)

	var importDecls, newDecls []ast.Decl
	for _, decl := range decls {
		// ensure linebreak between decls
		switch decl := decl.(type) {
		case *ast.FuncDecl:
			if decl.Doc == nil {
				decl.Doc = new(ast.CommentGroup)
			}
		case *ast.GenDecl:
			if decl.Doc == nil {
				decl.Doc = new(ast.CommentGroup)
			}
		}
		// move import decls to beginning
		if decl, ok := decl.(*ast.GenDecl); ok && decl.Tok == token.IMPORT {
			importDecls = append(importDecls, decl)
			continue
		}
		newDecls = append(newDecls, decl)
	}
	decls = append(importDecls, newDecls...)

	// output
	if config.Writer != nil {
		if config.Package != "" { // output complete file
			file := &ast.File{
				Name:  ast.NewIdent(config.Package),
				Decls: decls,
			}
			buf := new(bytes.Buffer)
			err = format.Node(buf, program.Fset, file)
			if err != nil { //NOCOVER
				return fmt.Errorf("ccg: format output %v", err)
			}
			bs, err := imports.Process("", buf.Bytes(), nil)
			if err != nil { //NOCOVER
				return fmt.Errorf("ccg: format output %v", err)
			}
			config.Writer.Write(bs)
		} else { // output decls only
			err = format.Node(config.Writer, program.Fset, decls)
			if err != nil { //NOCOVER
				return fmt.Errorf("ccg: format output %v", err)
			}
		}
	}

	return nil
}
<commit_msg>deal with import and const decls correctly<commit_after>package ccg

//go:generate myccg -uses AstDecls,AstDecls.Filter -package ccg -output utils.go slice ast.Decl AstDecls
//go:generate myccg -uses AstSpecs,AstSpecs.Filter -package ccg -output utils.go slice ast.Spec AstSpecs

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/format"
	"go/token"
	"io"

	"golang.org/x/tools/go/loader"
	"golang.org/x/tools/go/types"
	"golang.org/x/tools/imports"
)

var (
	pt = fmt.Printf
)

type Config struct {
	From        string
	Params      map[string]string
	Renames     map[string]string
	Writer      io.Writer
	Package     string
	Decls       []ast.Decl
	NameFilters []func(string) bool
	FileSet     *token.FileSet
}

func Copy(config Config) error {
	// load package
	loadConf := loader.Config{
		Fset: config.FileSet,
	}
	loadConf.Import(config.From)
	program, err := loadConf.Load()
	if err != nil {
		return fmt.Errorf("ccg: load package %v", err)
	}
	info := program.Imported[config.From]

	// remove param declarations
	for _, f := range info.Files {
		f.Decls = AstDecls(f.Decls).Filter(func(decl ast.Decl) (ret bool) {
			switch decl := decl.(type) {
			case *ast.GenDecl:
				switch decl.Tok {
				case token.TYPE:
					decl.Specs = AstSpecs(decl.Specs).Filter(func(spec ast.Spec) bool {
						name := spec.(*ast.TypeSpec).Name.Name
						_, exists := config.Params[name]
						return !exists
					})
					ret = len(decl.Specs) > 0
				case token.VAR:
					decl.Specs = AstSpecs(decl.Specs).Filter(func(sp ast.Spec) bool {
						spec := sp.(*ast.ValueSpec)
						names := []*ast.Ident{}
						values := []ast.Expr{}
						for i, name := range spec.Names {
							if _, exists := config.Params[name.Name]; !exists {
								names = append(names, name)
								if i < len(spec.Values) {
									values = append(values, spec.Values[i])
								}
							}
						}
						spec.Names = names
						if len(values) == 0 {
							spec.Values = nil
						} else {
							spec.Values = values
						}
						return len(spec.Names) > 0
					})
					ret = len(decl.Specs) > 0
				default:
					return true
				}
			default:
				return true
			}
			return
		})
	}

	// collect objects to rename
	objects := make(map[types.Object]string)
	collectObjects := func(mapping map[string]string) error {
		for from, to := range mapping {
			obj := info.Pkg.Scope().Lookup(from)
			if obj == nil {
				return fmt.Errorf("ccg: name not found %s", from)
			}
			objects[obj] = to
		}
		return nil
	}
	if err := collectObjects(config.Params); err != nil {
		return err
	}
	if err := collectObjects(config.Renames); err != nil {
		return err
	}

	// rename
	rename := func(defs map[*ast.Ident]types.Object) {
		for id, obj := range defs {
			if to, ok := objects[obj]; ok {
				id.Name = to
			}
		}
	}
	rename(info.Defs)
	rename(info.Uses)

	// collect existing decls
	existingVars := make(map[string]func(expr ast.Expr))
	existingTypes := make(map[string]func(expr ast.Expr))
	existingFuncs := make(map[string]func(fn *ast.FuncDecl))
	for i, decl := range config.Decls {
		switch decl := decl.(type) {
		case *ast.GenDecl:
			switch decl.Tok {
			case token.VAR:
				for _, spec := range decl.Specs {
					spec := spec.(*ast.ValueSpec)
					for i, name := range spec.Names {
						i := i
						spec := spec
						existingVars[name.Name] = func(expr ast.Expr) {
							spec.Values[i] = expr
						}
					}
				}
			case token.TYPE:
				for i, spec := range decl.Specs {
					spec := spec.(*ast.TypeSpec)
					i := i
					decl := decl
					existingTypes[spec.Name.Name] = func(expr ast.Expr) {
						decl.Specs[i].(*ast.TypeSpec).Type = expr
					}
				}
			}
		case *ast.FuncDecl:
			name := decl.Name.Name
			if decl.Recv != nil {
				name = decl.Recv.List[0].Type.(*ast.Ident).Name + "." + name
			}
			i := i
			existingFuncs[name] = func(fndecl *ast.FuncDecl) {
				config.Decls[i] = fndecl
			}
		}
	}

	// collect output declarations
	decls := []ast.Decl{}
	for _, f := range info.Files {
	loopSpec:
		for _, decl := range f.Decls {
			switch decl := decl.(type) {
			case *ast.GenDecl:
				switch decl.Tok {
				// var
				case token.VAR:
					newDecl := &ast.GenDecl{
						Tok: token.VAR,
					}
					for _, spec := range decl.Specs {
						spec := spec.(*ast.ValueSpec)
					loopVarName:
						for i, name := range spec.Names {
							for _, filter := range config.NameFilters {
								if !filter(name.Name) {
									continue loopVarName
								}
							}
							if mutator, ok := existingVars[name.Name]; ok {
								mutator(spec.Values[i])
							} else {
								newDecl.Specs = append(newDecl.Specs, spec)
							}
						}
					}
					if len(newDecl.Specs) > 0 {
						decls = append(decls, newDecl)
					}
				// type
				case token.TYPE:
					newDecl := &ast.GenDecl{
						Tok: token.TYPE,
					}
				loopTypeSpec:
					for _, spec := range decl.Specs {
						spec := spec.(*ast.TypeSpec)
						name := spec.Name.Name
						for _, filter := range config.NameFilters {
							if !filter(name) {
								continue loopTypeSpec
							}
						}
						if mutator, ok := existingTypes[name]; ok {
							mutator(spec.Type)
						} else {
							newDecl.Specs = append(newDecl.Specs, spec)
						}
					}
					if len(newDecl.Specs) > 0 {
						decls = append(decls, newDecl)
					}
				// import or const
				default:
					decls = append(decls, decl)
				}
			// func
			case *ast.FuncDecl:
				name := decl.Name.Name
				for _, filter := range config.NameFilters {
					if !filter(name) {
						continue loopSpec
					}
				}
				if decl.Recv != nil {
					name = decl.Recv.List[0].Type.(*ast.Ident).Name + "." + name
				}
				if mutator, ok := existingFuncs[name]; ok {
					mutator(decl)
				} else {
					config.Decls = append(config.Decls, decl)
				}
			}
		}
	}
	decls = append(decls, config.Decls...)

	var importDecls, newDecls []ast.Decl
	for _, decl := range decls {
		// ensure linebreak between decls
		switch decl := decl.(type) {
		case *ast.FuncDecl:
			if decl.Doc == nil {
				decl.Doc = new(ast.CommentGroup)
			}
		case *ast.GenDecl:
			if decl.Doc == nil {
				decl.Doc = new(ast.CommentGroup)
			}
		}
		// move import decls to beginning
		if decl, ok := decl.(*ast.GenDecl); ok && decl.Tok == token.IMPORT {
			importDecls = append(importDecls, decl)
			continue
		}
		newDecls = append(newDecls, decl)
	}
	decls = append(importDecls, newDecls...)

	// output
	if config.Writer != nil {
		if config.Package != "" { // output complete file
			file := &ast.File{
				Name:  ast.NewIdent(config.Package),
				Decls: decls,
			}
			buf := new(bytes.Buffer)
			err = format.Node(buf, program.Fset, file)
			if err != nil { //NOCOVER
				return fmt.Errorf("ccg: format output %v", err)
			}
			bs, err := imports.Process("", buf.Bytes(), nil)
			if err != nil { //NOCOVER
				return fmt.Errorf("ccg: format output %v", err)
			}
			config.Writer.Write(bs)
		} else { // output decls only
			err = format.Node(config.Writer, program.Fset, decls)
			if err != nil { //NOCOVER
				return fmt.Errorf("ccg: format output %v", err)
			}
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"io"
	"strings"

	"github.com/naoty/table/table"
	"github.com/naoty/table/writers"
)

// Exit codes represent exit codes for particular situations.
const (
	ExitCodeOK = 0

	ExitCodeError = 10 + iota
)

// Format option values
const (
	FormatOptionASCII      = "ascii"
	FormatOptionMarkdown   = "markdown"
	FormatOptionConfluence = "confluence"
)

// CLI represents the CLI for this application.
type CLI struct {
	inStream             io.Reader
	outStream, errStream io.Writer
}

// Run runs commands with given args.
func (cli *CLI) Run(args []string) int {
	var writer writers.Writer = writers.ASCIIWriter{}
	shouldShowHeader := false

	for i, arg := range args {
		switch arg {
		case "--format", "-f":
			if i < len(args)-1 {
				switch args[i+1] {
				case FormatOptionASCII:
					writer = writers.ASCIIWriter{}
				case FormatOptionMarkdown:
					writer = writers.MarkdownWriter{}
				case FormatOptionConfluence:
					writer = writers.ConfluenceWriter{}
				default:
					writer = writers.ASCIIWriter{}
				}
			}
		case "--header", "-H":
			shouldShowHeader = true
		case "--help", "-h":
			fmt.Fprintln(cli.outStream, cli.Help())
			return ExitCodeOK
		case "--version", "-v":
			fmt.Fprintln(cli.outStream, Version)
			return ExitCodeOK
		}
	}

	table := table.NewTable()
	scanner := bufio.NewScanner(cli.inStream)
	for i := 0; scanner.Scan(); i++ {
		if shouldShowHeader && i == 0 {
			table.AppendHeader(scanner.Text())
		} else {
			table.AppendRow(scanner.Text())
		}
	}

	if err := scanner.Err(); err != nil {
		fmt.Fprintln(cli.errStream, err)
		return ExitCodeError
	}

	fmt.Fprintf(cli.outStream, "%v", writer.Write(table))
	return ExitCodeOK
}

// Help returns help messages.
func (cli *CLI) Help() string {
	indent := strings.Repeat(" ", 2)

	lines := []string{}
	lines = append(lines, "Usage:")
	lines = append(lines, fmt.Sprintf("%stable [--header | -H] [--format | -f (ascii|markdown|confluence)]", indent))
	lines = append(lines, fmt.Sprintf("%stable --help | -h", indent))
	lines = append(lines, fmt.Sprintf("%stable --version | -v", indent))
	lines = append(lines, "")
	lines = append(lines, "Options:")
	lines = append(lines, fmt.Sprintf("%s--header, -H: Show header", indent))
	lines = append(lines, fmt.Sprintf("%s--format, -f [ascii|markdown|confluence]: Set format (default: ascii)", indent))
	lines = append(lines, fmt.Sprintf("%s--help, -h: Show version number", indent))
	lines = append(lines, fmt.Sprintf("%s--version, -v: Show version number", indent))

	return strings.Join(lines, "\n")
}
<commit_msg>Parse format option<commit_after>package main

import (
	"bufio"
	"fmt"
	"io"
	"strings"

	"github.com/naoty/table/table"
	"github.com/naoty/table/writers"
)

// Exit codes represent exit codes for particular situations.
const (
	ExitCodeOK = 0

	ExitCodeError = 10 + iota
)

// Format option values
const (
	FormatOptionASCII      = "ascii"
	FormatOptionMarkdown   = "markdown"
	FormatOptionConfluence = "confluence"
)

// CLI represents the CLI for this application.
type CLI struct {
	inStream             io.Reader
	outStream, errStream io.Writer
}

// Run runs commands with given args.
func (cli *CLI) Run(args []string) int {
	var writer writers.Writer = writers.ASCIIWriter{}
	shouldShowHeader := false

	for i, arg := range args {
		switch arg {
		case "--format", "-f":
			if i == len(args)-1 {
				continue
			}

			_, writerName := parseFormat(arg)

			switch writerName {
			case FormatOptionASCII:
				writer = writers.ASCIIWriter{}
			case FormatOptionMarkdown:
				writer = writers.MarkdownWriter{}
			case FormatOptionConfluence:
				writer = writers.ConfluenceWriter{}
			default:
				writer = writers.ASCIIWriter{}
			}
		case "--header", "-H":
			shouldShowHeader = true
		case "--help", "-h":
			fmt.Fprintln(cli.outStream, cli.Help())
			return ExitCodeOK
		case "--version", "-v":
			fmt.Fprintln(cli.outStream, Version)
			return ExitCodeOK
		}
	}

	table := table.NewTable()
	scanner := bufio.NewScanner(cli.inStream)
	for i := 0; scanner.Scan(); i++ {
		if shouldShowHeader && i == 0 {
			table.AppendHeader(scanner.Text())
		} else {
			table.AppendRow(scanner.Text())
		}
	}

	if err := scanner.Err(); err != nil {
		fmt.Fprintln(cli.errStream, err)
		return ExitCodeError
	}

	fmt.Fprintf(cli.outStream, "%v", writer.Write(table))
	return ExitCodeOK
}

// Help returns help messages.
func (cli *CLI) Help() string {
	indent := strings.Repeat(" ", 2)

	lines := []string{}
	lines = append(lines, "Usage:")
	lines = append(lines, fmt.Sprintf("%stable [--header | -H] [--format | -f (ascii|markdown|confluence)]", indent))
	lines = append(lines, fmt.Sprintf("%stable --help | -h", indent))
	lines = append(lines, fmt.Sprintf("%stable --version | -v", indent))
	lines = append(lines, "")
	lines = append(lines, "Options:")
	lines = append(lines, fmt.Sprintf("%s--header, -H: Show header", indent))
	lines = append(lines, fmt.Sprintf("%s--format, -f [ascii|markdown|confluence]: Set format (default: ascii)", indent))
	lines = append(lines, fmt.Sprintf("%s--help, -h: Show version number", indent))
	lines = append(lines, fmt.Sprintf("%s--version, -v: Show version number", indent))

	return strings.Join(lines, "\n")
}

func parseFormat(format string) (string, string) {
	items := strings.Split(format, ":")
	if len(items) == 0 {
		return "", ""
	} else if len(items) == 1 {
		return "", items[0]
	} else {
		return items[0], items[1]
	}
}
<|endoftext|>


<commit_before>// Copyright © 2015 Victor Antonovich <victor@antonovich.me>
//
// 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 main

import (
	"fmt"
	"os"
	"os/signal"
	"syscall"

	"github.com/golang/glog"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"

	"k8s.io/kubernetes/pkg/util"
)

const (
	FLAG_V             = "v"
	FLAG_STDERR_THRESH = "stderrthreshold"
	FLAG_RUN_ONCE      = "once"
	FLAG_DRY_RUN       = "dry-run"
	FLAG_SERVER        = "server"
	FLAG_CONFIG        = "config"
	FLAG_POLL_TIME     = "poll-time"
	FLAG_TEMPLATE      = "template"
)

func newCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "kube-template",
		Short: "kube-template",
		Long:  "Watches Kubernetes for updates, writing output of a series of templates to files.",
		Run:   runCmd,
	}
	initCmd(cmd)
	return cmd
}

func initCmd(cmd *cobra.Command) {
	// Command-related flags set
	f := cmd.Flags()
	f.Bool(FLAG_DRY_RUN, false, "don't write template output, dump result to stdout")
	f.Bool(FLAG_RUN_ONCE, false, "run template processing once and exit")
	f.StringP(FLAG_SERVER, "s", "", "the address and port of the Kubernetes API server")
	f.StringVarP(&cfgFile, FLAG_CONFIG, "c", "", fmt.Sprintf("config file (default is ./%s.(yaml|json))", CFG_FILE))
	f.StringSliceP(FLAG_TEMPLATE, "t", nil, `adds a new template to watch on disk in the format
		'templatePath:outputPath[:command]'. This option is additive
		and may be specified multiple times for multiple templates.`)
	// Merge glog-related flags
	// FIXME probably we shouldn't use k8s utils there
	pflag.CommandLine.AddFlagSet(f)
	util.InitFlags()
	util.InitLogs()
	defer util.FlushLogs()
}

func runCmd(cmd *cobra.Command, args []string) {
	config, err := newConfig(cmd)

	if err != nil {
		glog.Fatalf("configuration error: %v", err)
	}
	if len(config.TemplateDescriptors) == 0 {
		glog.Fatalf("no templates to process (use --help to get configuration options), exiting...")
	}

	// Start application
	app, err := newApp(config)
	if err != nil {
		glog.Fatalf("can't create application: %v", err)
	}

	go app.Start()

	// Listen for signals
	signalCh := make(chan os.Signal, 1)
	signal.Notify(signalCh,
		syscall.SIGHUP,
		syscall.SIGINT,
		syscall.SIGTERM,
		syscall.SIGQUIT,
	)

	// Event loop
EventLoop:
	for {
		select {
		case signal := <-signalCh:
			glog.V(2).Infof("received %v signal, stopping", signal)
			app.Stop()
		case <-app.doneCh:
			break EventLoop
		}
	}
}
<commit_msg>Returned back accidentally removed flag<commit_after>// Copyright © 2015 Victor Antonovich <victor@antonovich.me>
//
// 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 main

import (
	"fmt"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/golang/glog"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"

	"k8s.io/kubernetes/pkg/util"
)

const (
	FLAG_V             = "v"
	FLAG_STDERR_THRESH = "stderrthreshold"
	FLAG_RUN_ONCE      = "once"
	FLAG_DRY_RUN       = "dry-run"
	FLAG_SERVER        = "server"
	FLAG_CONFIG        = "config"
	FLAG_POLL_TIME     = "poll-time"
	FLAG_TEMPLATE      = "template"
)

func newCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "kube-template",
		Short: "kube-template",
		Long:  "Watches Kubernetes for updates, writing output of a series of templates to files.",
		Run:   runCmd,
	}
	initCmd(cmd)
	return cmd
}

func initCmd(cmd *cobra.Command) {
	// Command-related flags set
	f := cmd.Flags()
	f.Bool(FLAG_DRY_RUN, false, "don't write template output, dump result to stdout")
	f.Bool(FLAG_RUN_ONCE, false, "run template processing once and exit")
	f.StringP(FLAG_SERVER, "s", "", "the address and port of the Kubernetes API server")
	f.DurationP(FLAG_POLL_TIME, "p", 15*time.Second, "Kubernetes API server poll time")
	f.StringVarP(&cfgFile, FLAG_CONFIG, "c", "", fmt.Sprintf("config file (default is ./%s.(yaml|json))", CFG_FILE))
	f.StringSliceP(FLAG_TEMPLATE, "t", nil, `adds a new template to watch on disk in the format
		'templatePath:outputPath[:command]'. This option is additive
		and may be specified multiple times for multiple templates.`)
	// Merge glog-related flags
	// FIXME probably we shouldn't use k8s utils there
	pflag.CommandLine.AddFlagSet(f)
	util.InitFlags()
	util.InitLogs()
	defer util.FlushLogs()
}

func runCmd(cmd *cobra.Command, args []string) {
	config, err := newConfig(cmd)

	if err != nil {
		glog.Fatalf("configuration error: %v", err)
	}
	if len(config.TemplateDescriptors) == 0 {
		glog.Fatalf("no templates to process (use --help to get configuration options), exiting...")
	}

	// Start application
	app, err := newApp(config)
	if err != nil {
		glog.Fatalf("can't create application: %v", err)
	}

	go app.Start()

	// Listen for signals
	signalCh := make(chan os.Signal, 1)
	signal.Notify(signalCh,
		syscall.SIGHUP,
		syscall.SIGINT,
		syscall.SIGTERM,
		syscall.SIGQUIT,
	)

	// Event loop
EventLoop:
	for {
		select {
		case signal := <-signalCh:
			glog.V(2).Infof("received %v signal, stopping", signal)
			app.Stop()
		case <-app.doneCh:
			break EventLoop
		}
	}
}
<|endoftext|>


<commit_before>// the edgeworth system CPU - a simple 64-bit RISC cpu with a large compliment of registers, most of them purpose built
package edgeworth

import (
	"fmt"
)

const (
	RegisterCount = 256
)

type InvalidRegisterTypeError struct {
	Type string
}

func (e InvalidRegisterTypeError) Error() string {
	return fmt.Sprintf("Error: invalid register type: %s provided for integer register", e.Type)
}

type Register interface {
	GetValue() interface{}
	SetValue(interface{}) error
}

type IntegerRegister struct {
	value uint64
}

type FloatRegister struct {
	value float64
}

func (r IntegerRegister) GetValue() interface{} {
	return r.value
}

func (r IntegerRegister) SetValue(v interface{}) error {

	switch t := v.(type) {
	default:
		var q InvalidRegisterTypeError
		q.Type = fmt.Sprintf("%T", t)
		return &q
	case uint8:
		r.value = v.(uint64)
		return nil
	case int8:
		r.value = v.(uint64)
		return nil
	case uint16:
		r.value = v.(uint64)
		return nil
	case int16:
		r.value = v.(uint64)
		return nil
	case int32:
		r.value = v.(uint64)
		return nil
	case uint32:
		r.value = v.(uint64)
		return nil
	case int64:
		r.value = v.(uint64)
		return nil
	case uint64:
		r.value = v.(uint64)
		return nil
	}
}

func (r FloatRegister) GetValue() interface{} {
	return r.value
}

func (r FloatRegister) SetValue(v interface{}) error {

	switch t := v.(type) {
	default:
		var q InvalidRegisterTypeError
		q.Type = fmt.Sprintf("%T", t)
		return &q
	case float32:
		r.value = v.(float64)
		return nil
	case float64:
		r.value = v.(float64)
		return nil
	}
}
<commit_msg>Registers are now of a common type with a 64-bit raw bits backend<commit_after>// the edgeworth system CPU - a simple 64-bit RISC cpu with a large compliment of registers, most of them purpose built
package edgeworth

import (
	"fmt"
	"math"
)

const (
	RegisterCount = 256
)

const (
	RegisterTypeInteger = iota
	RegisterTypeFloat
	RegisterTypePackedFloat32
	RegisterTypePackedInt32
)

type PackedFloat32 struct {
	Lower float32
	Upper float32
}

type PackedInt32 struct {
	Lower uint32
	Upper uint32
}

type InvalidRegisterTypeError struct {
	Type string
}

func (e InvalidRegisterTypeError) Error() string {
	return fmt.Sprintf("Error: invalid register type: %s provided for integer register", e.Type)
}

type Register struct {
	Bits uint64 // raw bits
	Tag  byte   // tag bits
}

func (r *Register) SetValue(tag byte, bits uint64) {
	r.Tag = tag
	r.Bits = bits
}
func (r *Register) SetIntegerValue(value uint64) {
	r.SetValue(RegisterTypeInteger, value)
}

func (r *Register) SetFloatValue(value float64) {
	r.SetValue(RegisterTypeFloat, math.Float64bits(value))
}

func (r *Register) GetFloatValue() float64 {
	return math.Float64frombits(r.Bits)
}

func (r *Register) GetValue() interface{} {
	switch r.Tag {
	case RegisterTypeFloat:
		return r.GetFloatValue()
	case RegisterTypePackedFloat32:
		return r.GetFloat32Values()
	case RegisterTypePackedInt32:
		return r.GetInt32Values()
	case RegisterTypeInteger:
		fallthrough
	default:
		return r.Bits
	}
}

func (r *Register) GetFloat32Values() *PackedFloat32 {
	//get the raw bits for the upper and lower half
	lower := uint32(r.Bits)
	upper := uint32((r.Bits & 0xFFFFFFFF00000000) >> 32)
	return &PackedFloat32{Lower: math.Float32frombits(lower), Upper: math.Float32frombits(upper)}
}

func (r *Register) SetPackedFloat32Value(p *PackedFloat32) {
	blower := uint64(math.Float32bits(p.Lower))
	bupper := uint64(math.Float32bits(p.Upper))
	value := ((bupper << 32) | (blower & 0x00000000FFFFFFFF))
	r.SetValue(RegisterTypePackedFloat32, value)
}
func (r *Register) SetFromFloat32Values(lower, upper float32) {
	var p PackedFloat32
	p.Lower = lower
	p.Upper = upper
	r.SetPackedFloat32Value(&p)
}
func (r *Register) GetInt32Values() *PackedInt32 {
	//get the raw bits for the upper and lower half
	lower := uint32(r.Bits)
	upper := uint32(r.Bits >> 32)
	return &PackedInt32{Lower: lower, Upper: upper}
}

func (r *Register) SetPackedInt32Value(p *PackedInt32) {
	blower := uint64(p.Lower)
	bupper := uint64(p.Upper)
	value := ((bupper << 32) | (blower & 0x00000000FFFFFFFF))
	r.SetValue(RegisterTypePackedInt32, value)
}
func (r *Register) SetFromInt32Values(lower, upper uint32) {
	var p PackedInt32
	p.Lower = lower
	p.Upper = upper
	r.SetPackedInt32Value(&p)
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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.

// A helper that allows using gcsfuse with mount(8).
//
// Can be invoked using a command-line of the form expected for mount helpers.
// Calls the gcsfuse binary, which it finds from one of a list of expected
// locations, and waits for it to complete. The device and mount point are
// passed on as positional arguments, and other known options are converted to
// appropriate flags.
//
// This binary returns with exit code zero only after gcsfuse has reported that
// it has successfuly mounted the file system. Further output from gcsfuse is
// suppressed.
package main

// Example invocation on OS X:
//
//     mount -t porp -o foo=bar\ baz -o ro,blah bucket ~/tmp/mp
//
// becomes the following arguments:
//
//     Arg 0: "/sbin/mount_gcsfuse "
//     Arg 1: "-o"
//     Arg 2: "foo=bar baz"
//     Arg 3: "-o"
//     Arg 4: "ro"
//     Arg 5: "-o"
//     Arg 6: "blah"
//     Arg 7: "bucket"
//     Arg 8: "/path/to/mp"
//
// On Linux, the fstab entry
//
//     bucket /path/to/mp porp user,foo=bar\040baz
//
// becomes
//
//     Arg 0: "/sbin/mount.gcsfuse"
//     Arg 1: "bucket"
//     Arg 2: "/path/to/mp"
//     Arg 3: "-o"
//     Arg 4: "rw,noexec,nosuid,nodev,user,foo=bar baz"
//

import (
	"fmt"
	"os"
	"os/exec"
	"path"
	"strings"

	"github.com/googlecloudplatform/gcsfuse/internal/mount"
)

// Turn mount-style options into gcsfuse arguments. Skip known detritus that
// the mount command gives us.
//
// The result of this function should be appended to exec.Command.Args.
func makeGcsfuseArgs(
	device string,
	mountPoint string,
	opts map[string]string) (args []string, err error) {
	// Deal with options.
	for name, value := range opts {
		switch name {
		// Don't pass through options that are relevant to mount(8) but not to
		// gcsfuse, and that fusermount chokes on with "Invalid argument" on Linux.
		case "user", "nouser", "auto", "noauto", "_netdev", "no_netdev":

		// Special case: support mount-like formatting for gcsfuse bool flags.
		case "implicit_dirs":
			args = append(
				args,
				"--"+strings.Replace(name, "_", "-", -1),
			)

			// Special case: support mount-like formatting for gcsfuse string flags.
		case "dir_mode", "file_mode", "key_file", "temp_dir", "gid", "uid", "only_dir", "limit_ops_per_sec", "limit_bytes_per_sec":
			args = append(
				args,
				"--"+strings.Replace(name, "_", "-", -1),
				value,
			)

		// Pass through everything else.
		default:
			var formatted string
			if value == "" {
				formatted = name
			} else {
				formatted = fmt.Sprintf("%s=%s", name, value)
			}

			args = append(args, "-o", formatted)
		}
	}

	// Set the bucket and mount point.
	args = append(args, device, mountPoint)

	return
}

// Parse the supplied command-line arguments from a mount(8) invocation on OS X
// or Linux.
func parseArgs(
	args []string) (
	device string,
	mountPoint string,
	opts map[string]string,
	err error) {
	opts = make(map[string]string)

	// Process each argument in turn.
	positionalCount := 0
	for i, s := range args {
		switch {
		// Skip the program name.
		case i == 0:
			continue

		// "-o" is illegal only when at the end. We handle its argument in the case
		// below.
		case s == "-o":
			if i == len(args)-1 {
				err = fmt.Errorf("Unexpected -o at end of args.")
				return
			}

		// systemd passes -n (alias --no-mtab) to the mount helper. This seems to
		// be a result of the new setup on many Linux systems with /etc/mtab as a
		// symlink pointing to /proc/self/mounts. /proc/self/mounts is read-only,
		// so any helper that would normally write to /etc/mtab should be
		// configured not to do so. Because systemd does not provide a way to
		// disable this behavior for mount helpers that do not write to /etc/mtab,
		// we ignore the flag.
		case s == "-n":
			continue

		// Is this an options string following a "-o"?
		case i > 0 && args[i-1] == "-o":
			mount.ParseOptions(opts, s)

		// Is this the device?
		case positionalCount == 0:
			device = s
			positionalCount++

		// Is this the mount point?
		case positionalCount == 1:
			mountPoint = s
			positionalCount++

		default:
			err = fmt.Errorf("Unexpected arg %d: %q", i, s)
			return
		}
	}

	if positionalCount != 2 {
		err = fmt.Errorf("Expected two positional arguments; got %d.", positionalCount)
		return
	}

	return
}

func run(args []string) (err error) {
	// If invoked with a single "--help" argument, print a usage message and exit
	// successfully.
	if len(args) == 2 && args[1] == "--help" {
		fmt.Fprintf(
			os.Stderr,
			"Usage: %s [-o options] bucket_name mount_point\n",
			args[0])

		return
	}

	// Find the path to gcsfuse.
	gcsfusePath, err := findGcsfuse()
	if err != nil {
		err = fmt.Errorf("findGcsfuse: %v", err)
		return
	}

	// Find the path to fusermount.
	fusermountPath, err := findFusermount()
	if err != nil {
		err = fmt.Errorf("findFusermount: %v", err)
		return
	}

	// Attempt to parse arguments.
	device, mountPoint, opts, err := parseArgs(args)
	if err != nil {
		err = fmt.Errorf("parseArgs: %v", err)
		return
	}

	// Choose gcsfuse args.
	gcsfuseArgs, err := makeGcsfuseArgs(device, mountPoint, opts)
	if err != nil {
		err = fmt.Errorf("makeGcsfuseArgs: %v", err)
		return
	}

	fmt.Fprintf(
		os.Stderr,
		"Calling gcsfuse with arguments: %s\n",
		strings.Join(gcsfuseArgs, " "))

	// Run gcsfuse.
	cmd := exec.Command(gcsfusePath, gcsfuseArgs...)
	cmd.Env = append(cmd.Env, fmt.Sprintf("PATH=%s", path.Dir(fusermountPath)))
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	err = cmd.Run()
	if err != nil {
		err = fmt.Errorf("running gcsfuse: %v", err)
		return
	}

	return
}

func main() {
	err := run(os.Args)
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}
}
<commit_msg>Add stat-cache-ttl and type-cache-ttl<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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.

// A helper that allows using gcsfuse with mount(8).
//
// Can be invoked using a command-line of the form expected for mount helpers.
// Calls the gcsfuse binary, which it finds from one of a list of expected
// locations, and waits for it to complete. The device and mount point are
// passed on as positional arguments, and other known options are converted to
// appropriate flags.
//
// This binary returns with exit code zero only after gcsfuse has reported that
// it has successfuly mounted the file system. Further output from gcsfuse is
// suppressed.
package main

// Example invocation on OS X:
//
//     mount -t porp -o foo=bar\ baz -o ro,blah bucket ~/tmp/mp
//
// becomes the following arguments:
//
//     Arg 0: "/sbin/mount_gcsfuse "
//     Arg 1: "-o"
//     Arg 2: "foo=bar baz"
//     Arg 3: "-o"
//     Arg 4: "ro"
//     Arg 5: "-o"
//     Arg 6: "blah"
//     Arg 7: "bucket"
//     Arg 8: "/path/to/mp"
//
// On Linux, the fstab entry
//
//     bucket /path/to/mp porp user,foo=bar\040baz
//
// becomes
//
//     Arg 0: "/sbin/mount.gcsfuse"
//     Arg 1: "bucket"
//     Arg 2: "/path/to/mp"
//     Arg 3: "-o"
//     Arg 4: "rw,noexec,nosuid,nodev,user,foo=bar baz"
//

import (
	"fmt"
	"os"
	"os/exec"
	"path"
	"strings"

	"github.com/googlecloudplatform/gcsfuse/internal/mount"
)

// Turn mount-style options into gcsfuse arguments. Skip known detritus that
// the mount command gives us.
//
// The result of this function should be appended to exec.Command.Args.
func makeGcsfuseArgs(
	device string,
	mountPoint string,
	opts map[string]string) (args []string, err error) {
	// Deal with options.
	for name, value := range opts {
		switch name {
		// Don't pass through options that are relevant to mount(8) but not to
		// gcsfuse, and that fusermount chokes on with "Invalid argument" on Linux.
		case "user", "nouser", "auto", "noauto", "_netdev", "no_netdev":

		// Special case: support mount-like formatting for gcsfuse bool flags.
		case "implicit_dirs":
			args = append(
				args,
				"--"+strings.Replace(name, "_", "-", -1),
			)

			// Special case: support mount-like formatting for gcsfuse string flags.
		case "dir_mode", "file_mode", "key_file", "temp_dir", "gid", "uid", "only_dir", "limit_ops_per_sec", "limit_bytes_per_sec", "stat_cache_ttl", "type_cache_ttl":
			args = append(
				args,
				"--"+strings.Replace(name, "_", "-", -1),
				value,
			)

		// Pass through everything else.
		default:
			var formatted string
			if value == "" {
				formatted = name
			} else {
				formatted = fmt.Sprintf("%s=%s", name, value)
			}

			args = append(args, "-o", formatted)
		}
	}

	// Set the bucket and mount point.
	args = append(args, device, mountPoint)

	return
}

// Parse the supplied command-line arguments from a mount(8) invocation on OS X
// or Linux.
func parseArgs(
	args []string) (
	device string,
	mountPoint string,
	opts map[string]string,
	err error) {
	opts = make(map[string]string)

	// Process each argument in turn.
	positionalCount := 0
	for i, s := range args {
		switch {
		// Skip the program name.
		case i == 0:
			continue

		// "-o" is illegal only when at the end. We handle its argument in the case
		// below.
		case s == "-o":
			if i == len(args)-1 {
				err = fmt.Errorf("Unexpected -o at end of args.")
				return
			}

		// systemd passes -n (alias --no-mtab) to the mount helper. This seems to
		// be a result of the new setup on many Linux systems with /etc/mtab as a
		// symlink pointing to /proc/self/mounts. /proc/self/mounts is read-only,
		// so any helper that would normally write to /etc/mtab should be
		// configured not to do so. Because systemd does not provide a way to
		// disable this behavior for mount helpers that do not write to /etc/mtab,
		// we ignore the flag.
		case s == "-n":
			continue

		// Is this an options string following a "-o"?
		case i > 0 && args[i-1] == "-o":
			mount.ParseOptions(opts, s)

		// Is this the device?
		case positionalCount == 0:
			device = s
			positionalCount++

		// Is this the mount point?
		case positionalCount == 1:
			mountPoint = s
			positionalCount++

		default:
			err = fmt.Errorf("Unexpected arg %d: %q", i, s)
			return
		}
	}

	if positionalCount != 2 {
		err = fmt.Errorf("Expected two positional arguments; got %d.", positionalCount)
		return
	}

	return
}

func run(args []string) (err error) {
	// If invoked with a single "--help" argument, print a usage message and exit
	// successfully.
	if len(args) == 2 && args[1] == "--help" {
		fmt.Fprintf(
			os.Stderr,
			"Usage: %s [-o options] bucket_name mount_point\n",
			args[0])

		return
	}

	// Find the path to gcsfuse.
	gcsfusePath, err := findGcsfuse()
	if err != nil {
		err = fmt.Errorf("findGcsfuse: %v", err)
		return
	}

	// Find the path to fusermount.
	fusermountPath, err := findFusermount()
	if err != nil {
		err = fmt.Errorf("findFusermount: %v", err)
		return
	}

	// Attempt to parse arguments.
	device, mountPoint, opts, err := parseArgs(args)
	if err != nil {
		err = fmt.Errorf("parseArgs: %v", err)
		return
	}

	// Choose gcsfuse args.
	gcsfuseArgs, err := makeGcsfuseArgs(device, mountPoint, opts)
	if err != nil {
		err = fmt.Errorf("makeGcsfuseArgs: %v", err)
		return
	}

	fmt.Fprintf(
		os.Stderr,
		"Calling gcsfuse with arguments: %s\n",
		strings.Join(gcsfuseArgs, " "))

	// Run gcsfuse.
	cmd := exec.Command(gcsfusePath, gcsfuseArgs...)
	cmd.Env = append(cmd.Env, fmt.Sprintf("PATH=%s", path.Dir(fusermountPath)))
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	err = cmd.Run()
	if err != nil {
		err = fmt.Errorf("running gcsfuse: %v", err)
		return
	}

	return
}

func main() {
	err := run(os.Args)
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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.

// A helper that allows using gcsfuse with mount(8).
//
// Can be invoked using a command-line of the form expected for mount helpers.
// Calls the gcsfuse binary, which must be in $PATH, and waits for it to
// complete. The device and mount point are passed on as positional arguments,
// and other known options are converted to appropriate flags.
//
// This binary returns with exit code zero only after gcsfuse has reported that
// it has successfuly mounted the file system. Further output from gcsfuse is
// suppressed.
package main

// Example invocation on OS X:
//
//     mount -t porp -o foo=bar\ baz -o ro,blah bucket ~/tmp/mp
//
// becomes the following arguments:
//
//     Arg 0: "/sbin/mount_gcsfuse "
//     Arg 1: "-o"
//     Arg 2: "foo=bar baz"
//     Arg 3: "-o"
//     Arg 4: "ro"
//     Arg 5: "-o"
//     Arg 6: "blah"
//     Arg 7: "bucket"
//     Arg 8: "/path/to/mp"
//
// On Linux, the fstab entry
//
//     bucket /path/to/mp porp user,foo=bar\040baz
//
// becomes
//
//     Arg 0: "/sbin/mount.gcsfuse"
//     Arg 1: "bucket"
//     Arg 2: "/path/to/mp"
//     Arg 3: "-o"
//     Arg 4: "rw,noexec,nosuid,nodev,user,foo=bar baz"
//

import (
	"fmt"
	"os"
	"strings"

	"github.com/googlecloudplatform/gcsfuse/internal/daemon"
	"github.com/googlecloudplatform/gcsfuse/internal/mount"
)

// Turn mount-style options into gcsfuse arguments. Skip known detritus that
// the mount command gives us.
//
// The result of this function should be appended to exec.Command.Args.
func makeGcsfuseArgs(
	device string,
	mountPoint string,
	opts map[string]string) (args []string, err error) {
	// Deal with options.
	for name, value := range opts {
		switch name {
		case "fuse_debug":
			args = append(args, "--fuse.debug")

		case "gcs_debug":
			args = append(args, "--gcs.debug")

		case "uid":
			args = append(args, "--uid="+value)

		case "gid":
			args = append(args, "--gid="+value)

		case "file_mode":
			args = append(args, "--file-mode="+value)

		case "dir_mode":
			args = append(args, "--dir-mode="+value)

		// Don't pass through options that are relevant to mount(8) but not to
		// gcsfuse, and that fusermount chokes on with "Invalid argument" on Linux.
		case "user", "nouser", "auto", "noauto":

		// Pass through everything else.
		default:
			var formatted string
			if value == "" {
				formatted = name
			} else {
				formatted = fmt.Sprintf("%s=%s", name, value)
			}

			args = append(args, "-o", formatted)
		}
	}

	// Set the bucket and mount point.
	args = append(args, device, mountPoint)

	return
}

// Parse the supplied command-line arguments from a mount(8) invocation on OS X
// or Linux.
func parseArgs(
	args []string) (
	device string,
	mountPoint string,
	opts map[string]string,
	err error) {
	opts = make(map[string]string)

	// Process each argument in turn.
	positionalCount := 0
	for i, s := range args {
		switch {
		// Skip the program name.
		case i == 0:
			continue

		// "-o" is illegal only when at the end. We handle its argument in the case
		// below.
		case s == "-o":
			if i == len(args)-1 {
				err = fmt.Errorf("Unexpected -o at end of args.")
				return
			}

		// Is this an options string following a "-o"?
		case i > 0 && args[i-1] == "-o":
			mount.ParseOptions(opts, s)

		// Is this the device?
		case positionalCount == 0:
			device = s
			positionalCount++

		// Is this the mount point?
		case positionalCount == 1:
			mountPoint = s
			positionalCount++

		default:
			err = fmt.Errorf("Unexpected arg %d: %q", i, s)
			return
		}
	}

	if positionalCount != 2 {
		err = fmt.Errorf("Expected two positional arguments; got %d.", positionalCount)
		return
	}

	return
}

func run(args []string) (err error) {
	// If invoked with a single "--help" argument, print a usage message and exit
	// successfully.
	if len(args) == 2 && args[1] == "--help" {
		fmt.Fprintf(
			os.Stderr,
			"Usage: %s [-o options] bucket_name mount_point\n",
			args[0])

		return
	}

	// Attempt to parse arguments.
	device, mountPoint, opts, err := parseArgs(args)
	if err != nil {
		err = fmt.Errorf("parseArgs: %v", err)
		return
	}

	// Choose gcsfuse args.
	gcsfuseArgs, err := makeGcsfuseArgs(device, mountPoint, opts)
	if err != nil {
		err = fmt.Errorf("makeGcsfuseArgs: %v", err)
		return
	}

	fmt.Fprintf(
		os.Stderr,
		"Calling gcsfuse with arguments: %s\n",
		strings.Join(gcsfuseArgs, " "))

	// Call gcsfuse and wait for it to successfully mount.
	err = daemon.Mount(
		"gcsfuse",
		"fusermount",
		gcsfuseArgs,
		os.Stderr)

	if err != nil {
		err = fmt.Errorf("daemon.Mount: %v", err)
		return
	}

	return
}

func main() {
	err := run(os.Args)
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}
}
<commit_msg>Removed support for undocumented mount helper options.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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.

// A helper that allows using gcsfuse with mount(8).
//
// Can be invoked using a command-line of the form expected for mount helpers.
// Calls the gcsfuse binary, which must be in $PATH, and waits for it to
// complete. The device and mount point are passed on as positional arguments,
// and other known options are converted to appropriate flags.
//
// This binary returns with exit code zero only after gcsfuse has reported that
// it has successfuly mounted the file system. Further output from gcsfuse is
// suppressed.
package main

// Example invocation on OS X:
//
//     mount -t porp -o foo=bar\ baz -o ro,blah bucket ~/tmp/mp
//
// becomes the following arguments:
//
//     Arg 0: "/sbin/mount_gcsfuse "
//     Arg 1: "-o"
//     Arg 2: "foo=bar baz"
//     Arg 3: "-o"
//     Arg 4: "ro"
//     Arg 5: "-o"
//     Arg 6: "blah"
//     Arg 7: "bucket"
//     Arg 8: "/path/to/mp"
//
// On Linux, the fstab entry
//
//     bucket /path/to/mp porp user,foo=bar\040baz
//
// becomes
//
//     Arg 0: "/sbin/mount.gcsfuse"
//     Arg 1: "bucket"
//     Arg 2: "/path/to/mp"
//     Arg 3: "-o"
//     Arg 4: "rw,noexec,nosuid,nodev,user,foo=bar baz"
//

import (
	"fmt"
	"os"
	"strings"

	"github.com/googlecloudplatform/gcsfuse/internal/daemon"
	"github.com/googlecloudplatform/gcsfuse/internal/mount"
)

// Turn mount-style options into gcsfuse arguments. Skip known detritus that
// the mount command gives us.
//
// The result of this function should be appended to exec.Command.Args.
func makeGcsfuseArgs(
	device string,
	mountPoint string,
	opts map[string]string) (args []string, err error) {
	// Deal with options.
	for name, value := range opts {
		switch name {
		// Don't pass through options that are relevant to mount(8) but not to
		// gcsfuse, and that fusermount chokes on with "Invalid argument" on Linux.
		case "user", "nouser", "auto", "noauto":

		// Pass through everything else.
		default:
			var formatted string
			if value == "" {
				formatted = name
			} else {
				formatted = fmt.Sprintf("%s=%s", name, value)
			}

			args = append(args, "-o", formatted)
		}
	}

	// Set the bucket and mount point.
	args = append(args, device, mountPoint)

	return
}

// Parse the supplied command-line arguments from a mount(8) invocation on OS X
// or Linux.
func parseArgs(
	args []string) (
	device string,
	mountPoint string,
	opts map[string]string,
	err error) {
	opts = make(map[string]string)

	// Process each argument in turn.
	positionalCount := 0
	for i, s := range args {
		switch {
		// Skip the program name.
		case i == 0:
			continue

		// "-o" is illegal only when at the end. We handle its argument in the case
		// below.
		case s == "-o":
			if i == len(args)-1 {
				err = fmt.Errorf("Unexpected -o at end of args.")
				return
			}

		// Is this an options string following a "-o"?
		case i > 0 && args[i-1] == "-o":
			mount.ParseOptions(opts, s)

		// Is this the device?
		case positionalCount == 0:
			device = s
			positionalCount++

		// Is this the mount point?
		case positionalCount == 1:
			mountPoint = s
			positionalCount++

		default:
			err = fmt.Errorf("Unexpected arg %d: %q", i, s)
			return
		}
	}

	if positionalCount != 2 {
		err = fmt.Errorf("Expected two positional arguments; got %d.", positionalCount)
		return
	}

	return
}

func run(args []string) (err error) {
	// If invoked with a single "--help" argument, print a usage message and exit
	// successfully.
	if len(args) == 2 && args[1] == "--help" {
		fmt.Fprintf(
			os.Stderr,
			"Usage: %s [-o options] bucket_name mount_point\n",
			args[0])

		return
	}

	// Attempt to parse arguments.
	device, mountPoint, opts, err := parseArgs(args)
	if err != nil {
		err = fmt.Errorf("parseArgs: %v", err)
		return
	}

	// Choose gcsfuse args.
	gcsfuseArgs, err := makeGcsfuseArgs(device, mountPoint, opts)
	if err != nil {
		err = fmt.Errorf("makeGcsfuseArgs: %v", err)
		return
	}

	fmt.Fprintf(
		os.Stderr,
		"Calling gcsfuse with arguments: %s\n",
		strings.Join(gcsfuseArgs, " "))

	// Call gcsfuse and wait for it to successfully mount.
	err = daemon.Mount(
		"gcsfuse",
		"fusermount",
		gcsfuseArgs,
		os.Stderr)

	if err != nil {
		err = fmt.Errorf("daemon.Mount: %v", err)
		return
	}

	return
}

func main() {
	err := run(os.Args)
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>// Copyright © 2014, All rights reserved
// Joel Scoble, https://github.com/mohae/tomd
//
// This is licensed under The MIT License. Please refer to the included
// LICENSE file for more information. If the LICENSE file has not been
// included, please refer to the url above.
//
// 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
//
// tomd: to markdown, takes input and converts it to markdown
//
// Notes:
//	* This is not a general markdown processor. It is a package to provide
//      functions that allow things to be converted to their representation
//      in markdown.
//      Currently that means taking a .csv file and converting it to a table.
//	* Uses seelog for 'library logging', to enable logging see:
//        http://github.com/cihub/seelog/wiki/Writing-libraries-with-Seelog
package tomd

import (
	"encoding/csv"
	"fmt"
	"io"
	"os"
	"path/filepath"
	_ "strconv"
	"strings"
)

// CSV is a struct for representing and working with csv data.
type CSV struct {
	// Source is the source of the CSV data. It is currently assumed to be
	// a path location
	source string

	// destination is where the generated markdown should be put, if it is
	// to be put anywhere. When used, this setting is used in conjunction
	// with destinationType. Not all destinationTypes need to specify a
	// destinatin, bytes, for example.
	destination string

	// destinationType is the type of destination for the md, e.g. file.
	// If the destinationType requires specification of the destination,
	// the Destination variable should be set to that value.
	// Supported:
	//	[]byte	no destination needed
	//	file	destination optional, if not set the output will be
	//		`sourceFilename.md` instead of `sourceFilename.csv`.
	destinationType string

	// hasHeaderRows: whether the csv data includes a header row as its
	// first row. If the csv data does not include header data, the header
	// data must be provided via template, e.g. false implies
	// 'useFormat' == true. True does not have any implications on using
	// the format file.
	hasHeaderRow bool

	// headerRow contains the header row information. This is when a format
	// has been supplied, the header row information is set.
	headerRow []string

	// columnAlignment contains the alignment information for each column
	// in the table. This is supplied by the format
	columnAlignment []string

	// columnEmphasis contains the emphasis information, if any. for each
	// column. This is supplied by the format.
	columnEmphasis []string

	// formatSource: the location and name of the source file to use. It
	// can either be explicitely set, or TOMD will look for it as
	// `source.fmt`, for `source.csv`.
	formatSource string

	// whether for formatSource was autoset or not.
	formatSourceAutoset bool

	// useFormat: whether there's a format to use with the CSV or not. For
	// files, this is usually a file, with the same name and path as the
	// source, using the 'fmt' extension. This can also be set explicitely.
	// 'useFormat' == false implies 'hasHeaderRow' == true.
	useFormat bool

	// formatType:	the type of format to use. By default, this is in sync
	//		with the source type, but it can be set independently.
	// Supported:
	//	file	The format information is in a format file. By default,
	//		this is the source filename with the `.fmt` file
	//		extension, instead of the original extension. This can
	//		be set independently too.
	//	default Any setting other than another supported type will be
	//		interpreted as using the default, which is to manually
	//		set the different format information you wish to use
	//		in the marshal using their Setters.
	formatType string

	// table is the parsed csv data
	table [][]string

	// md holds the md representation of the csv data
	md []byte
}

// NewCSV returns an initialize CSV object. It still needs to be configured
// for use.
func NewCSV() *CSV {
	C := &CSV{
		hasHeaderRow: true,
		destinationType: "bytes",
		table: [][]string{},
	}
	return C
}

// NewSourceCSV creates a new *CSV with its source set and initialized.
func NewSourcesCSV(s, t string, b bool) *CSV {
	c := NewCSV()
	c.useFormat = b

	// currently anything that's not file uses the default "", which
	// means set it yourself to use it.
	switch t {
	case "bytes":
	case "file":
		c.formatType = "file"
	}

	c.SetSource(s)
	return c
}

// ToMDTable takes a reader for csv and converts the read csv to a markdown
// table. To get the md, call CSV.md()
func (c *CSV) ToMDTable(r io.Reader) error {
	var err error
	c.table, err = ReadCSV(r)
	if err != nil {
		logger.Error(err)
		return err
	}

	//Now convert the data to md
	c.toMD()
	return nil
}

// FileToMDTable takes a file and marshals it to a md table.
func (c *CSV) FileToMDTable(source string) error{
	logger.Debugf("FileToMDTable enter with: %s", source)
	var err error
	// Try to read the source
	c.table, err = ReadCSVFile(source)
	if err != nil {
		logger.Error(err)
		return err
	}

	var formatName string
	// otherwise see if  HasFormat
	if c.useFormat {
//		c.setFormatFile()
		if c.formatType == "file" {
			//derive the format filename
			filename := filepath.Base(source)
			if filename == "." {
				err = fmt.Errorf("unable to determine format filename")
				logger.Error(err)
				return err
			}

			dir := filepath.Dir(source)
			parts := strings.Split(filename, ".")
			formatName = parts[0] + ".fmt"
			if dir != "." {
				formatName = dir + formatName
			}
		}
	}

	if c.useFormat {
		err := c.formatFromFile()
		if err != nil {
			logger.Error(err)
			return err
		}
	}

	// Now convert the data to md
	c.toMD()

	logger.Debug("FileToMDTable exit with error: nil")
	return nil
}

// MD() returns the markdown as []byte
func (c *CSV) MD() []byte {
	return c.md
}

// ReadCSV takes a reader, and reads the data connected with it as CSV data.
// A slice of slice of type string, or an error, are returned. This reads the
// entire file, so if the file is very large and you don't have sufficent RAM
// you will not like the results. There may be a row or chunk oriented
// implementation in the future.
func ReadCSV(r io.Reader ) ([][]string, error) {
	cr := csv.NewReader(r)
	rows, err := cr.ReadAll()
	if err != nil {
		logger.Error(err)
		return nil, err
	}

	return rows, nil
}

// ReadCSVFile takes a path, reads the contents of the file and returns int.
func ReadCSVFile(f string) ([][]string, error) {
	file, err := os.Open(f)
	if err != nil {
		logger.Error(err)
		return nil, err
	}

	// because we don't want to forget or worry about hanldling close prior
	// to every return.
	defer file.Close()

	//
	data, err := ReadCSV(file)
	if err != nil {
		logger.Error(err)
		return nil, err
	}

	return data, nil
}

// tomd does table header processing then converts its table data to md,
func (c *CSV) toMD() ()  {
	// Process the header first
	c.addHeader()

	// for each row of table data, process it.
	for _, row := range c.table {
		c.rowToMD(row)
	}

	return
}

// rowTomd takes a csv table row and returns the md version of it consistent
// with its configuration.
func (c *CSV) rowToMD(cols []string) {
	c.appendColumnSeparator()

	for _, col := range cols {
		// TODO this is where column data decoration would occur
		// with templates
		bcol := []byte(col)
		c.md = append(c.md, bcol...)
		c.appendColumnSeparator()
	}

}

// setFormatSource sets the formatSource if it is not already set or if the
// previously set value was set by setFormatSource. The latter allows auto-
// generated default source name to be updated when the source is while
// preserving overrides.
func (c *CSV) autosetFormatFile() error {
	// if the source isn't set, nothing to do.
	if c.source == "" {
		logger.Trace("setFormatSource exit: source not set")
		return nil
	}

	// if formatSource isn't empty and wasn't set by setFormatSource,
	// nothing to do
	if c.formatSource != "" && !c.formatSourceAutoset {
		logger.Infof("setFormatSource exit: formatSource was already set to %s", c.formatSource)
		return nil
	}

	if c.formatType != "file" {
		logger.Trace("setFormatSource exit: not using format file, format type is %s", c.formatType)
		return nil
	}

	// Figure out the filename
	dir, file := filepath.Split(c.source)

	// break up the filename into its part, the last is extension.
	var fname string
	fParts := strings.Split(file, ".")

	if len(fParts) <= 2 {
		fname = fParts[0]
	} else {
		// Join all but the last part together for the name
		// This handles names with multiple `.`
		fname = strings.Join(fParts[0:len(fParts) - 2], ".")
	}

	fname += ".md"
	c.formatSource = dir + fname
	c.formatSourceAutoset = true
	return nil
}

// addHeader adds the table header row and the separator row that goes between
// the header row and the data.
func (c *CSV) addHeader() () {
	if c.hasHeaderRow {
		c.rowToMD(c.table[0])
		//remove the first row
		c.table = append(c.table[1:])
	} else {
		if c.useFormat {
			c.rowToMD(c.headerRow)
		}
	}

	c.appendHeaderSeparatorRow(len(c.table[0]))
	return
}

// appendHeaderSeparator adds the configured column  separator
func (c *CSV) appendHeaderSeparatorRow(cols int) {
	c.appendColumnSeparator()

	for i := 0; i < cols; i++ {
		var separator []byte

		if c.useFormat {
			switch c.columnAlignment[i] {
			case "left", "l":
				separator = mdLeftJustify
			case "center", "c":
				separator = mdCentered
			case "right", "r":
				separator = mdRightJustify
			default:
				separator = mdDontJustify
			}
		} else {
			separator = mdDontJustify
		}

		separator = append(separator, mdPipe...)

		c.md = append(c.md, separator...)
	}

	return

}

// appendColumnSeparator appends a pip to the md array
func (c *CSV) appendColumnSeparator() {
	c.md = append(c.md, mdPipe...)
}

// FormatFromFile loads the format file specified.
func (c *CSV) formatFromFile() error {
	// not really considering this an error that stops things, just one
	// that requirs error level logging. Is this right?
	if c.formatType != "file" {
		logger.Error("formatFromFile: nothing to do, formatType was %s, expected file", c.formatType)
		return nil
	}

	// if formatSource isn't set, nothing todo
	if c.formatSource == "" {
		logger.Error("formatFromFile: nothing to do, formatSource was not set", c.formatType)
		return nil
	}

	// Read from the format file
	table, err := ReadCSVFile(c.formatSource)
	if err != nil {
		logger.Error(err)
		return err
	}

	//Row 0 is the header information
	c.headerRow = table[0]
	c.columnAlignment = table[1]
	c.columnEmphasis = table[2]

	return nil
}

// Source returns the source of the CSV
func (c *CSV) Source() string {
	return c.source
}

// SetSource sets the source and has the formatFile updated, if applicable.
func (c *CSV) SetSource(s string) {
	c.source = s
	c.autosetFormatFile()
}

// Destination is the of destination for the output, if applicable.
func (c *CSV) Destination() string {
	return c.destination
}

// SetDestination sets the destination of the output, if applicable.
func (c *CSV) SetDestination(s string) {
	c.destination = s
}

// DestinationType is the type of destination for the output.
func (c *CSV) DestinationType() string {
	return c.destinationType
}

// SetDestinationType sets the destinationType.
func (c *CSV) SetDestinationType(s string) {
	c.destinationType = s
}

// HasHeaderRow returns whether, or not, this csv file has a format file to
// use.
func (c *CSV) HasHeaderRow() bool {
	return c.hasHeaderRow
}

// SetHasHeaderRow sets whether, or not, the source has a header row.
func (c *CSV) SetHasHeaderRow(b bool) {
	c.hasHeaderRow = b
}

// HeaderRow returns the column headers; i.e., the header row.
func (c *CSV) HeaderRow() []string {
	return c.headerRow
}

// SetHeaderRow sets the headerRow information.
func (c *CSV) SetHeaderRow(s []string) {
	c.headerRow = s
}

// ColumnAlignment returns the columnAlignment information. This can be set
// either explicitely or using a format file.
func (c *CSV) ColumnAlignment() []string {
	return c.columnAlignment
}

// SetColumnAlignment sets the columnAlignment informatin.
func (c *CSV) SetColumnAlignment(s []string) {
	c.columnAlignment = s
}

// ColumnEmphasis returns the columnEmphasis information. This can be set
// either explicitly or with a format file.
func (c *CSV) ColumnEmphasis() []string {
	return c.columnEmphasis
}

// SetColumnEmphasis sets columnEmphasis information.
func (c *CSV) SetColumnEmphasis(s []string) {
	c.columnEmphasis = s
}

// FormatSource returns the formatSource information.
func (c *CSV) FormatSource() string {
	return c.formatSource
}

// SetFormatSource sets formatSource information. A side-affect of this is that
// setting the format file will automatically set `useFormat` and
// `useFormatFile`.
func (c *CSV) SetFormatSource(s string) {
	c.formatSource = s
}

// UseFormat returns whether this csv file has a format file to use.
func (c *CSV) UseFormat() bool {
	return c.useFormat
}

// SetUseFormat sets whether a format should be used. This triggers a setting
// of the FormatFilename, if applicable.
func (c *CSV) SetUseFormat(b bool) {
	c.useFormat = b
	c.autosetFormatFile()
}
<commit_msg>changed parm ordering and naming of marshal method on csv<commit_after>// Copyright © 2014, All rights reserved
// Joel Scoble, https://github.com/mohae/tomd
//
// This is licensed under The MIT License. Please refer to the included
// LICENSE file for more information. If the LICENSE file has not been
// included, please refer to the url above.
//
// 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
//
// tomd: to markdown, takes input and converts it to markdown
//
// Notes:
//	* This is not a general markdown processor. It is a package to provide
//      functions that allow things to be converted to their representation
//      in markdown.
//      Currently that means taking a .csv file and converting it to a table.
//	* Uses seelog for 'library logging', to enable logging see:
//        http://github.com/cihub/seelog/wiki/Writing-libraries-with-Seelog
package tomd

import (
	"encoding/csv"
	"fmt"
	"io"
	"os"
	"path/filepath"
	_ "strconv"
	"strings"
)

// CSV is a struct for representing and working with csv data.
type CSV struct {
	// Source is the source of the CSV data. It is currently assumed to be
	// a path location
	source string

	// destination is where the generated markdown should be put, if it is
	// to be put anywhere. When used, this setting is used in conjunction
	// with destinationType. Not all destinationTypes need to specify a
	// destinatin, bytes, for example.
	destination string

	// destinationType is the type of destination for the md, e.g. file.
	// If the destinationType requires specification of the destination,
	// the Destination variable should be set to that value.
	// Supported:
	//	[]byte	no destination needed
	//	file	destination optional, if not set the output will be
	//		`sourceFilename.md` instead of `sourceFilename.csv`.
	destinationType string

	// hasHeaderRows: whether the csv data includes a header row as its
	// first row. If the csv data does not include header data, the header
	// data must be provided via template, e.g. false implies
	// 'useFormat' == true. True does not have any implications on using
	// the format file.
	hasHeaderRow bool

	// headerRow contains the header row information. This is when a format
	// has been supplied, the header row information is set.
	headerRow []string

	// columnAlignment contains the alignment information for each column
	// in the table. This is supplied by the format
	columnAlignment []string

	// columnEmphasis contains the emphasis information, if any. for each
	// column. This is supplied by the format.
	columnEmphasis []string

	// formatSource: the location and name of the source file to use. It
	// can either be explicitely set, or TOMD will look for it as
	// `source.fmt`, for `source.csv`.
	formatSource string

	// whether for formatSource was autoset or not.
	formatSourceAutoset bool

	// useFormat: whether there's a format to use with the CSV or not. For
	// files, this is usually a file, with the same name and path as the
	// source, using the 'fmt' extension. This can also be set explicitely.
	// 'useFormat' == false implies 'hasHeaderRow' == true.
	useFormat bool

	// formatType:	the type of format to use. By default, this is in sync
	//		with the source type, but it can be set independently.
	// Supported:
	//	file	The format information is in a format file. By default,
	//		this is the source filename with the `.fmt` file
	//		extension, instead of the original extension. This can
	//		be set independently too.
	//	default Any setting other than another supported type will be
	//		interpreted as using the default, which is to manually
	//		set the different format information you wish to use
	//		in the marshal using their Setters.
	formatType string

	// table is the parsed csv data
	table [][]string

	// md holds the md representation of the csv data
	md []byte
}

// NewCSV returns an initialize CSV object. It still needs to be configured
// for use.
func NewCSV() *CSV {
	C := &CSV{
		hasHeaderRow: true,
		destinationType: "bytes",
		table: [][]string{},
	}
	return C
}

// NewSourceCSV creates a new *CSV with its source set and initialized.
func NewSourcesCSV(t, s string, b bool) *CSV {
	c := NewCSV()
	c.useFormat = b

	// currently anything that's not file uses the default "", which
	// means set it yourself to use it.
	switch t {
	case "bytes":
	case "file":
		c.formatType = "file"
	}

	c.SetSource(s)
	return c
}

// MDTable takes a reader for csv and converts the read csv to a markdown
// table. To get the md, call CSV.md()
func (c *CSV) ToMDTable(r io.Reader) error {
	var err error
	c.table, err = ReadCSV(r)
	if err != nil {
		logger.Error(err)
		return err
	}

	//Now convert the data to md
	c.toMD()
	return nil
}

// MarshalTable marshals the CSV info to a markdown table.
func (c *CSV) MarshalTable() error{
	logger.Debugf("MarshalTable enter, source: %s", c.source)
	var err error
	// Try to read the source
	c.table, err = ReadCSVFile(c.source)
	if err != nil {
		logger.Error(err)
		return err
	}

	var formatName string
	// otherwise see if  HasFormat
	if c.useFormat {
//		c.setFormatFile()
		if c.formatType == "file" {
			//derive the format filename
			filename := filepath.Base(c.source)
			if filename == "." {
				err = fmt.Errorf("unable to determine format filename")
				logger.Error(err)
				return err
			}

			dir := filepath.Dir(c.source)
			parts := strings.Split(filename, ".")
			formatName = parts[0] + ".fmt"
			if dir != "." {
				formatName = dir + formatName
			}
		}
	}

	if c.useFormat {
		err := c.formatFromFile()
		if err != nil {
			logger.Error(err)
			return err
		}
	}

	// Now convert the data to md
	c.toMD()

	logger.Debug("FileToMDTable exit with error: nil")
	return nil
}

// MD() returns the markdown as []byte
func (c *CSV) MD() []byte {
	return c.md
}

// ReadCSV takes a reader, and reads the data connected with it as CSV data.
// A slice of slice of type string, or an error, are returned. This reads the
// entire file, so if the file is very large and you don't have sufficent RAM
// you will not like the results. There may be a row or chunk oriented
// implementation in the future.
func ReadCSV(r io.Reader ) ([][]string, error) {
	cr := csv.NewReader(r)
	rows, err := cr.ReadAll()
	if err != nil {
		logger.Error(err)
		return nil, err
	}

	return rows, nil
}

// ReadCSVFile takes a path, reads the contents of the file and returns int.
func ReadCSVFile(f string) ([][]string, error) {
	file, err := os.Open(f)
	if err != nil {
		logger.Error(err)
		return nil, err
	}

	// because we don't want to forget or worry about hanldling close prior
	// to every return.
	defer file.Close()

	//
	data, err := ReadCSV(file)
	if err != nil {
		logger.Error(err)
		return nil, err
	}

	return data, nil
}

// tomd does table header processing then converts its table data to md,
func (c *CSV) toMD() ()  {
	// Process the header first
	c.addHeader()

	// for each row of table data, process it.
	for _, row := range c.table {
		c.rowToMD(row)
	}

	return
}

// rowTomd takes a csv table row and returns the md version of it consistent
// with its configuration.
func (c *CSV) rowToMD(cols []string) {
	c.appendColumnSeparator()

	for _, col := range cols {
		// TODO this is where column data decoration would occur
		// with templates
		bcol := []byte(col)
		c.md = append(c.md, bcol...)
		c.appendColumnSeparator()
	}

}

// setFormatSource sets the formatSource if it is not already set or if the
// previously set value was set by setFormatSource. The latter allows auto-
// generated default source name to be updated when the source is while
// preserving overrides.
func (c *CSV) autosetFormatFile() error {
	// if the source isn't set, nothing to do.
	if c.source == "" {
		logger.Trace("setFormatSource exit: source not set")
		return nil
	}

	// if formatSource isn't empty and wasn't set by setFormatSource,
	// nothing to do
	if c.formatSource != "" && !c.formatSourceAutoset {
		logger.Infof("setFormatSource exit: formatSource was already set to %s", c.formatSource)
		return nil
	}

	if c.formatType != "file" {
		logger.Trace("setFormatSource exit: not using format file, format type is %s", c.formatType)
		return nil
	}

	// Figure out the filename
	dir, file := filepath.Split(c.source)

	// break up the filename into its part, the last is extension.
	var fname string
	fParts := strings.Split(file, ".")

	if len(fParts) <= 2 {
		fname = fParts[0]
	} else {
		// Join all but the last part together for the name
		// This handles names with multiple `.`
		fname = strings.Join(fParts[0:len(fParts) - 2], ".")
	}

	fname += ".md"
	c.formatSource = dir + fname
	c.formatSourceAutoset = true
	return nil
}

// addHeader adds the table header row and the separator row that goes between
// the header row and the data.
func (c *CSV) addHeader() () {
	if c.hasHeaderRow {
		c.rowToMD(c.table[0])
		//remove the first row
		c.table = append(c.table[1:])
	} else {
		if c.useFormat {
			c.rowToMD(c.headerRow)
		}
	}

	c.appendHeaderSeparatorRow(len(c.table[0]))
	return
}

// appendHeaderSeparator adds the configured column  separator
func (c *CSV) appendHeaderSeparatorRow(cols int) {
	c.appendColumnSeparator()

	for i := 0; i < cols; i++ {
		var separator []byte

		if c.useFormat {
			switch c.columnAlignment[i] {
			case "left", "l":
				separator = mdLeftJustify
			case "center", "c":
				separator = mdCentered
			case "right", "r":
				separator = mdRightJustify
			default:
				separator = mdDontJustify
			}
		} else {
			separator = mdDontJustify
		}

		separator = append(separator, mdPipe...)

		c.md = append(c.md, separator...)
	}

	return

}

// appendColumnSeparator appends a pip to the md array
func (c *CSV) appendColumnSeparator() {
	c.md = append(c.md, mdPipe...)
}

// FormatFromFile loads the format file specified.
func (c *CSV) formatFromFile() error {
	// not really considering this an error that stops things, just one
	// that requirs error level logging. Is this right?
	if c.formatType != "file" {
		logger.Error("formatFromFile: nothing to do, formatType was %s, expected file", c.formatType)
		return nil
	}

	// if formatSource isn't set, nothing todo
	if c.formatSource == "" {
		logger.Error("formatFromFile: nothing to do, formatSource was not set", c.formatType)
		return nil
	}

	// Read from the format file
	table, err := ReadCSVFile(c.formatSource)
	if err != nil {
		logger.Error(err)
		return err
	}

	//Row 0 is the header information
	c.headerRow = table[0]
	c.columnAlignment = table[1]
	c.columnEmphasis = table[2]

	return nil
}

// Source returns the source of the CSV
func (c *CSV) Source() string {
	return c.source
}

// SetSource sets the source and has the formatFile updated, if applicable.
func (c *CSV) SetSource(s string) {
	c.source = s
	c.autosetFormatFile()
}

// Destination is the of destination for the output, if applicable.
func (c *CSV) Destination() string {
	return c.destination
}

// SetDestination sets the destination of the output, if applicable.
func (c *CSV) SetDestination(s string) {
	c.destination = s
}

// DestinationType is the type of destination for the output.
func (c *CSV) DestinationType() string {
	return c.destinationType
}

// SetDestinationType sets the destinationType.
func (c *CSV) SetDestinationType(s string) {
	c.destinationType = s
}

// HasHeaderRow returns whether, or not, this csv file has a format file to
// use.
func (c *CSV) HasHeaderRow() bool {
	return c.hasHeaderRow
}

// SetHasHeaderRow sets whether, or not, the source has a header row.
func (c *CSV) SetHasHeaderRow(b bool) {
	c.hasHeaderRow = b
}

// HeaderRow returns the column headers; i.e., the header row.
func (c *CSV) HeaderRow() []string {
	return c.headerRow
}

// SetHeaderRow sets the headerRow information.
func (c *CSV) SetHeaderRow(s []string) {
	c.headerRow = s
}

// ColumnAlignment returns the columnAlignment information. This can be set
// either explicitely or using a format file.
func (c *CSV) ColumnAlignment() []string {
	return c.columnAlignment
}

// SetColumnAlignment sets the columnAlignment informatin.
func (c *CSV) SetColumnAlignment(s []string) {
	c.columnAlignment = s
}

// ColumnEmphasis returns the columnEmphasis information. This can be set
// either explicitly or with a format file.
func (c *CSV) ColumnEmphasis() []string {
	return c.columnEmphasis
}

// SetColumnEmphasis sets columnEmphasis information.
func (c *CSV) SetColumnEmphasis(s []string) {
	c.columnEmphasis = s
}

// FormatSource returns the formatSource information.
func (c *CSV) FormatSource() string {
	return c.formatSource
}

// SetFormatSource sets formatSource information. A side-affect of this is that
// setting the format file will automatically set `useFormat` and
// `useFormatFile`.
func (c *CSV) SetFormatSource(s string) {
	c.formatSource = s
}

// UseFormat returns whether this csv file has a format file to use.
func (c *CSV) UseFormat() bool {
	return c.useFormat
}

// SetUseFormat sets whether a format should be used. This triggers a setting
// of the FormatFilename, if applicable.
func (c *CSV) SetUseFormat(b bool) {
	c.useFormat = b
	c.autosetFormatFile()
}
<|endoftext|>


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

// A CSV implementation inspired by Python's CSV module. Supports custom CSV
// formats. Currently only writing CSV files is supported.
package csv

import (
	"bufio"
	"io"
	"strings"
)

// A helper interface for a general CSV writer. Adheres to encoding/csv Writer
// in the standard go library as well as the Writer implemented by this
// package.
type CsvWriter interface {
	// Currently no errors are possible.
	Error() error

	// Flush writes any buffered data to the underlying io.Writer.
	// To check if an error occurred during the Flush, call Error.
	Flush()

	// Writer writes a single CSV record to w along with any necessary quoting.
	// A record is a slice of strings with each string being one field.
	Write(record []string) error

	// WriteAll writes multiple CSV records to w using Write and then calls Flush.
	WriteAll(records [][]string) error
}

// Values Dialect.Quoting can take.
const (
	QuoteDefault    = iota // See DefaultQuoting.
	QuoteAll        = iota
	QuoteMinimal    = iota
	QuoteNonNumeric = iota
	QuoteNone       = iota
)

// Values Dialect.DoubleQuote can take.
const (
	DoubleQuoteDefault = iota // See DefaultDoubleQuote.
	DoDoubleQuote      = iota
	NoDoubleQuote      = iota
)

// Default dialect.
const (
	DefaultDelimiter      = " "
	DefaultQuoting        = QuoteMinimal
	DefaultDoubleQuote    = DoDoubleQuote
	DefaultEscapeChar     = '\\'
	DefaultQuoteChar      = '"'
	DefaultLineTerminator = "\n"
)

type Dialect struct {
	Delimiter      string
	Quoting        int
	DoubleQuote    int
	EscapeChar     rune
	QuoteChar      rune
	LineTerminator string
}

func (wo *Dialect) setDefaults() {
	if wo.Delimiter == "" {
		wo.Delimiter = DefaultDelimiter
	}
	if wo.Quoting == QuoteDefault {
		wo.Quoting = DefaultQuoting
	}
	if wo.LineTerminator == "" {
		wo.LineTerminator = DefaultLineTerminator
	}
	if wo.DoubleQuote == DoubleQuoteDefault {
		wo.DoubleQuote = DefaultDoubleQuote
	}
	if wo.QuoteChar == 0 {
		wo.QuoteChar = DefaultQuoteChar
	}
	if wo.EscapeChar == 0 {
		wo.EscapeChar = DefaultEscapeChar
	}
}

type Writer struct {
	opts Dialect
	w    *bufio.Writer
}

// Create a writer that adheres to the Golang CSV writer.
func NewWriter(w io.Writer) Writer {
	opts := Dialect{}
	opts.setDefaults()
	return Writer{
		opts: opts,
		w:    bufio.NewWriter(w),
	}
}

// Create a custom CSV writer.
func NewDialectWriter(w io.Writer, opts Dialect) Writer {
	opts.setDefaults()
	return Writer{
		opts: opts,
		w:    bufio.NewWriter(w),
	}
}

// Error reports any error that has occurred during a previous Write or Flush.
func (w Writer) Error() error {
	_, err := w.w.Write(nil)
	return err
}

// Flush writes any buffered data to the underlying io.Writer.
// To check if an error occurred during the Flush, call Error.
func (w Writer) Flush() {
	w.w.Flush()
}

// Helper function that ditches the first return value of w.w.WriteString().
// Simplifies code.
func (w Writer) writeString(s string) error {
	_, err := w.w.WriteString(s)
	return err
}

func (w Writer) writeDelimiter() error {
	return w.writeString(w.opts.Delimiter)
}

func isDigit(s rune) bool {
	return s >= '0' && s <= '9'
}

func isNumeric(s string) bool {
	if len(s) == 0 {
		return false
	}
	for _, r := range s {
		if !isDigit(r) {
			return false
		}
	}
	return true
}

func (w Writer) fieldNeedsQuote(field string) bool {
	switch w.opts.Quoting {
	case QuoteNone:
		return false
	case QuoteAll:
		return true
	case QuoteNonNumeric:
		return !isNumeric(field)
	case QuoteMinimal:
		// TODO: Can be improved by making a single search with trie.
		// See https://docs.python.org/2/library/csv.html#csv.QUOTE_MINIMAL for info on this.
		return strings.Contains(field, w.opts.LineTerminator) || strings.Contains(field, w.opts.Delimiter) || strings.ContainsRune(field, w.opts.QuoteChar)
	default:
		panic("Unexpected quoting.")
	}
}

func (w Writer) writeRune(r rune) error {
	_, err := w.w.WriteRune(r)
	return err
}

func (w Writer) writeEscapeChar(r rune) error {
	switch w.opts.DoubleQuote {
	case DoDoubleQuote:
		return w.writeRune(r)
	case NoDoubleQuote:
		return w.writeRune(w.opts.EscapeChar)
	default:
		panic("Unrecognized double quote type.")
	}
}

func (w Writer) writeQuotedRune(r rune) error {
	switch r {
	case w.opts.EscapeChar:
		if err := w.writeEscapeChar(r); err != nil {
			return err
		}
	case w.opts.QuoteChar:
		if err := w.writeEscapeChar(r); err != nil {
			return err
		}
	}
	return w.writeRune(r)
}

func (w Writer) writeQuoted(field string) error {
	if err := w.writeRune(w.opts.QuoteChar); err != nil {
		return err
	}
	for _, r := range field {
		if err := w.writeQuotedRune(r); err != nil {
			return err
		}
	}
	return w.writeRune(w.opts.QuoteChar)
}

func (w Writer) writeField(field string) error {
	if w.fieldNeedsQuote(field) {
		return w.writeQuoted(field)
	} else {
		return w.writeString(field)
	}
}

func (w Writer) writeNewline() error {
	return w.writeString(w.opts.LineTerminator)
}

// Writer writes a single CSV record to w along with any necessary quoting.
// A record is a slice of strings with each string being one field.
func (w Writer) Write(record []string) (err error) {
	for n, field := range record {
		if n > 0 {
			if err = w.writeDelimiter(); err != nil {
				return
			}
		}
		if err = w.writeField(field); err != nil {
			return
		}
	}
	err = w.writeNewline()
	return
}

// WriteAll writes multiple CSV records to w using Write and then calls Flush.
func (w Writer) WriteAll(records [][]string) (err error) {
	for _, record := range records {
		if err := w.Write(record); err != nil {
			return err
		}
	}
	return w.w.Flush()
}
<commit_msg>chore: reuse IsDigit logic from unicode<commit_after>// Copyright 2014 Jens Rantil. All rights reserved.  Use of this source code is
// governed by a BSD-style license that can be found in the LICENSE file.

// A CSV implementation inspired by Python's CSV module. Supports custom CSV
// formats. Currently only writing CSV files is supported.
package csv

import (
	"bufio"
	"io"
	"strings"
	"unicode"
)

// A helper interface for a general CSV writer. Adheres to encoding/csv Writer
// in the standard go library as well as the Writer implemented by this
// package.
type CsvWriter interface {
	// Currently no errors are possible.
	Error() error

	// Flush writes any buffered data to the underlying io.Writer.
	// To check if an error occurred during the Flush, call Error.
	Flush()

	// Writer writes a single CSV record to w along with any necessary quoting.
	// A record is a slice of strings with each string being one field.
	Write(record []string) error

	// WriteAll writes multiple CSV records to w using Write and then calls Flush.
	WriteAll(records [][]string) error
}

// Values Dialect.Quoting can take.
const (
	QuoteDefault    = iota // See DefaultQuoting.
	QuoteAll        = iota
	QuoteMinimal    = iota
	QuoteNonNumeric = iota
	QuoteNone       = iota
)

// Values Dialect.DoubleQuote can take.
const (
	DoubleQuoteDefault = iota // See DefaultDoubleQuote.
	DoDoubleQuote      = iota
	NoDoubleQuote      = iota
)

// Default dialect.
const (
	DefaultDelimiter      = " "
	DefaultQuoting        = QuoteMinimal
	DefaultDoubleQuote    = DoDoubleQuote
	DefaultEscapeChar     = '\\'
	DefaultQuoteChar      = '"'
	DefaultLineTerminator = "\n"
)

type Dialect struct {
	Delimiter      string
	Quoting        int
	DoubleQuote    int
	EscapeChar     rune
	QuoteChar      rune
	LineTerminator string
}

func (wo *Dialect) setDefaults() {
	if wo.Delimiter == "" {
		wo.Delimiter = DefaultDelimiter
	}
	if wo.Quoting == QuoteDefault {
		wo.Quoting = DefaultQuoting
	}
	if wo.LineTerminator == "" {
		wo.LineTerminator = DefaultLineTerminator
	}
	if wo.DoubleQuote == DoubleQuoteDefault {
		wo.DoubleQuote = DefaultDoubleQuote
	}
	if wo.QuoteChar == 0 {
		wo.QuoteChar = DefaultQuoteChar
	}
	if wo.EscapeChar == 0 {
		wo.EscapeChar = DefaultEscapeChar
	}
}

type Writer struct {
	opts Dialect
	w    *bufio.Writer
}

// Create a writer that adheres to the Golang CSV writer.
func NewWriter(w io.Writer) Writer {
	opts := Dialect{}
	opts.setDefaults()
	return Writer{
		opts: opts,
		w:    bufio.NewWriter(w),
	}
}

// Create a custom CSV writer.
func NewDialectWriter(w io.Writer, opts Dialect) Writer {
	opts.setDefaults()
	return Writer{
		opts: opts,
		w:    bufio.NewWriter(w),
	}
}

// Error reports any error that has occurred during a previous Write or Flush.
func (w Writer) Error() error {
	_, err := w.w.Write(nil)
	return err
}

// Flush writes any buffered data to the underlying io.Writer.
// To check if an error occurred during the Flush, call Error.
func (w Writer) Flush() {
	w.w.Flush()
}

// Helper function that ditches the first return value of w.w.WriteString().
// Simplifies code.
func (w Writer) writeString(s string) error {
	_, err := w.w.WriteString(s)
	return err
}

func (w Writer) writeDelimiter() error {
	return w.writeString(w.opts.Delimiter)
}

func isNumeric(s string) bool {
	if len(s) == 0 {
		return false
	}
	for _, r := range s {
		if !unicode.IsDigit(r) {
			return false
		}
	}
	return true
}

func (w Writer) fieldNeedsQuote(field string) bool {
	switch w.opts.Quoting {
	case QuoteNone:
		return false
	case QuoteAll:
		return true
	case QuoteNonNumeric:
		return !isNumeric(field)
	case QuoteMinimal:
		// TODO: Can be improved by making a single search with trie.
		// See https://docs.python.org/2/library/csv.html#csv.QUOTE_MINIMAL for info on this.
		return strings.Contains(field, w.opts.LineTerminator) || strings.Contains(field, w.opts.Delimiter) || strings.ContainsRune(field, w.opts.QuoteChar)
	default:
		panic("Unexpected quoting.")
	}
}

func (w Writer) writeRune(r rune) error {
	_, err := w.w.WriteRune(r)
	return err
}

func (w Writer) writeEscapeChar(r rune) error {
	switch w.opts.DoubleQuote {
	case DoDoubleQuote:
		return w.writeRune(r)
	case NoDoubleQuote:
		return w.writeRune(w.opts.EscapeChar)
	default:
		panic("Unrecognized double quote type.")
	}
}

func (w Writer) writeQuotedRune(r rune) error {
	switch r {
	case w.opts.EscapeChar:
		if err := w.writeEscapeChar(r); err != nil {
			return err
		}
	case w.opts.QuoteChar:
		if err := w.writeEscapeChar(r); err != nil {
			return err
		}
	}
	return w.writeRune(r)
}

func (w Writer) writeQuoted(field string) error {
	if err := w.writeRune(w.opts.QuoteChar); err != nil {
		return err
	}
	for _, r := range field {
		if err := w.writeQuotedRune(r); err != nil {
			return err
		}
	}
	return w.writeRune(w.opts.QuoteChar)
}

func (w Writer) writeField(field string) error {
	if w.fieldNeedsQuote(field) {
		return w.writeQuoted(field)
	} else {
		return w.writeString(field)
	}
}

func (w Writer) writeNewline() error {
	return w.writeString(w.opts.LineTerminator)
}

// Writer writes a single CSV record to w along with any necessary quoting.
// A record is a slice of strings with each string being one field.
func (w Writer) Write(record []string) (err error) {
	for n, field := range record {
		if n > 0 {
			if err = w.writeDelimiter(); err != nil {
				return
			}
		}
		if err = w.writeField(field); err != nil {
			return
		}
	}
	err = w.writeNewline()
	return
}

// WriteAll writes multiple CSV records to w using Write and then calls Flush.
func (w Writer) WriteAll(records [][]string) (err error) {
	for _, record := range records {
		if err := w.Write(record); err != nil {
			return err
		}
	}
	return w.w.Flush()
}
<|endoftext|>