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

package godoc

import (
	"bytes"
	"fmt"
	"net/http"
	"regexp"
	"strings"
)

type SearchResult struct {
	Query string
	Alert string // error or warning message

	// identifier matches
	Pak HitList       // packages matching Query
	Hit *LookupResult // identifier matches of Query
	Alt *AltWords     // alternative identifiers to look for

	// textual matches
	Found    int         // number of textual occurrences found
	Textual  []FileLines // textual matches of Query
	Complete bool        // true if all textual occurrences of Query are reported
	Idents   map[SpotKind][]Ident
}

func (c *Corpus) Lookup(query string) SearchResult {
	result := &SearchResult{Query: query}

	index, timestamp := c.CurrentIndex()
	if index != nil {
		// identifier search
		var err error
		result, err = index.Lookup(query)
		if err != nil && !c.IndexFullText {
			// ignore the error if full text search is enabled
			// since the query may be a valid regular expression
			result.Alert = "Error in query string: " + err.Error()
			return *result
		}

		// full text search
		if c.IndexFullText && query != "" {
			rx, err := regexp.Compile(query)
			if err != nil {
				result.Alert = "Error in query regular expression: " + err.Error()
				return *result
			}
			// If we get maxResults+1 results we know that there are more than
			// maxResults results and thus the result may be incomplete (to be
			// precise, we should remove one result from the result set, but
			// nobody is going to count the results on the result page).
			result.Found, result.Textual = index.LookupRegexp(rx, c.MaxResults+1)
			result.Complete = result.Found <= c.MaxResults
			if !result.Complete {
				result.Found-- // since we looked for maxResults+1
			}
		}
	}

	// is the result accurate?
	if c.IndexEnabled {
		if ts := c.FSModifiedTime(); timestamp.Before(ts) {
			// The index is older than the latest file system change under godoc's observation.
			result.Alert = "Indexing in progress: result may be inaccurate"
		}
	} else {
		result.Alert = "Search index disabled: no results available"
	}

	return *result
}

// SearchResultDoc optionally specifies a function returning an HTML body
// displaying search results matching godoc documentation.
func (p *Presentation) SearchResultDoc(result SearchResult) []byte {
	return applyTemplate(p.SearchDocHTML, "searchDocHTML", result)
}

// SearchResultCode optionally specifies a function returning an HTML body
// displaying search results matching source code.
func (p *Presentation) SearchResultCode(result SearchResult) []byte {
	return applyTemplate(p.SearchCodeHTML, "searchCodeHTML", result)
}

// SearchResultTxt optionally specifies a function returning an HTML body
// displaying search results of textual matches.
func (p *Presentation) SearchResultTxt(result SearchResult) []byte {
	return applyTemplate(p.SearchTxtHTML, "searchTxtHTML", result)
}

// HandleSearch obtains results for the requested search and returns a page
// to display them.
func (p *Presentation) HandleSearch(w http.ResponseWriter, r *http.Request) {
	query := strings.TrimSpace(r.FormValue("q"))
	result := p.Corpus.Lookup(query)

	if p.GetPageInfoMode(r)&NoHTML != 0 {
		p.ServeText(w, applyTemplate(p.SearchText, "searchText", result))
		return
	}
	contents := bytes.Buffer{}
	for _, f := range p.SearchResults {
		contents.Write(f(p, result))
	}

	var title string
	if haveResults := contents.Len() > 0; haveResults {
		title = fmt.Sprintf(`Results for query %q`, query)
		if !p.Corpus.IndexEnabled {
			result.Alert = ""
		}
	} else {
		title = fmt.Sprintf(`No results found for query %q`, query)
	}

	body := bytes.NewBuffer(applyTemplate(p.SearchHTML, "searchHTML", result))
	body.Write(contents.Bytes())

	p.ServePage(w, Page{
		Title:    title,
		Tabtitle: query,
		Query:    query,
		Body:     body.Bytes(),
	})
}

func (p *Presentation) serveSearchDesc(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/opensearchdescription+xml")
	data := map[string]interface{}{
		"BaseURL": fmt.Sprintf("http://%s", r.Host),
	}
	applyTemplateToResponseWriter(w, p.SearchDescXML, &data)
}
<commit_msg>godoc: Fix dereference of nil *SearchResult. Fixes golang/go#7364.<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package godoc

import (
	"bytes"
	"fmt"
	"net/http"
	"regexp"
	"strings"
)

type SearchResult struct {
	Query string
	Alert string // error or warning message

	// identifier matches
	Pak HitList       // packages matching Query
	Hit *LookupResult // identifier matches of Query
	Alt *AltWords     // alternative identifiers to look for

	// textual matches
	Found    int         // number of textual occurrences found
	Textual  []FileLines // textual matches of Query
	Complete bool        // true if all textual occurrences of Query are reported
	Idents   map[SpotKind][]Ident
}

func (c *Corpus) Lookup(query string) SearchResult {
	result := &SearchResult{Query: query}

	index, timestamp := c.CurrentIndex()
	if index != nil {
		// identifier search
		if r, err := index.Lookup(query); err == nil {
			result = r
		} else if err != nil && !c.IndexFullText {
			// ignore the error if full text search is enabled
			// since the query may be a valid regular expression
			result.Alert = "Error in query string: " + err.Error()
			return *result
		}

		// full text search
		if c.IndexFullText && query != "" {
			rx, err := regexp.Compile(query)
			if err != nil {
				result.Alert = "Error in query regular expression: " + err.Error()
				return *result
			}
			// If we get maxResults+1 results we know that there are more than
			// maxResults results and thus the result may be incomplete (to be
			// precise, we should remove one result from the result set, but
			// nobody is going to count the results on the result page).
			result.Found, result.Textual = index.LookupRegexp(rx, c.MaxResults+1)
			result.Complete = result.Found <= c.MaxResults
			if !result.Complete {
				result.Found-- // since we looked for maxResults+1
			}
		}
	}

	// is the result accurate?
	if c.IndexEnabled {
		if ts := c.FSModifiedTime(); timestamp.Before(ts) {
			// The index is older than the latest file system change under godoc's observation.
			result.Alert = "Indexing in progress: result may be inaccurate"
		}
	} else {
		result.Alert = "Search index disabled: no results available"
	}

	return *result
}

// SearchResultDoc optionally specifies a function returning an HTML body
// displaying search results matching godoc documentation.
func (p *Presentation) SearchResultDoc(result SearchResult) []byte {
	return applyTemplate(p.SearchDocHTML, "searchDocHTML", result)
}

// SearchResultCode optionally specifies a function returning an HTML body
// displaying search results matching source code.
func (p *Presentation) SearchResultCode(result SearchResult) []byte {
	return applyTemplate(p.SearchCodeHTML, "searchCodeHTML", result)
}

// SearchResultTxt optionally specifies a function returning an HTML body
// displaying search results of textual matches.
func (p *Presentation) SearchResultTxt(result SearchResult) []byte {
	return applyTemplate(p.SearchTxtHTML, "searchTxtHTML", result)
}

// HandleSearch obtains results for the requested search and returns a page
// to display them.
func (p *Presentation) HandleSearch(w http.ResponseWriter, r *http.Request) {
	query := strings.TrimSpace(r.FormValue("q"))
	result := p.Corpus.Lookup(query)

	if p.GetPageInfoMode(r)&NoHTML != 0 {
		p.ServeText(w, applyTemplate(p.SearchText, "searchText", result))
		return
	}
	contents := bytes.Buffer{}
	for _, f := range p.SearchResults {
		contents.Write(f(p, result))
	}

	var title string
	if haveResults := contents.Len() > 0; haveResults {
		title = fmt.Sprintf(`Results for query %q`, query)
		if !p.Corpus.IndexEnabled {
			result.Alert = ""
		}
	} else {
		title = fmt.Sprintf(`No results found for query %q`, query)
	}

	body := bytes.NewBuffer(applyTemplate(p.SearchHTML, "searchHTML", result))
	body.Write(contents.Bytes())

	p.ServePage(w, Page{
		Title:    title,
		Tabtitle: query,
		Query:    query,
		Body:     body.Bytes(),
	})
}

func (p *Presentation) serveSearchDesc(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/opensearchdescription+xml")
	data := map[string]interface{}{
		"BaseURL": fmt.Sprintf("http://%s", r.Host),
	}
	applyTemplateToResponseWriter(w, p.SearchDescXML, &data)
}
<|endoftext|>


<commit_before><commit_msg>Implemented transition tests for file swap and case check.<commit_after><|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"sort"

	"github.com/generaltso/git4go"
	"github.com/generaltso/linguist"
)

func checkErr(err error) {
	if err != nil {
		if output_debug {
			log.Panicln(err)
		} else {
			fmt.Println(err)
			os.Exit(1)
		}
	}
}

// flag vars
var (
	input_mode_git          bool
	input_mode_fs           bool
	input_git_tree          string
	output_json             bool
	output_json_with_colors bool
	output_limit            int
	output_debug            bool
	unignore_filenames      bool
	unignore_contents       bool
)

// used for displaying results
type (
	language struct {
		Language string  `json:"language"`
		Percent  float64 `json:"percent"`
	}

	language_color struct {
		Language string  `json:"language"`
		Percent  float64 `json:"percent"`
		Color    string  `json:"color"`
	}
)

type sortableResult []*language

func (s sortableResult) Len() int {
	return len(s)
}

func (s sortableResult) Less(i, j int) bool {
	return s[i].Percent < s[j].Percent
}

func (s sortableResult) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

var (
	langs         map[string]int = make(map[string]int)
	total_size    int            = 0
	num_files     int            = 0
	max_len       int            = 0
	ignored_paths int            = 0
)

func putResult(language string, size int) {
	langs[language] += size
	total_size += size
	num_files++
	if len(language) > max_len {
		max_len = len(language)
	}
}
func pluralize(num int) string {
	if num == 1 {
		return ""
	}
	return "s"
}

func main() {
	flag.BoolVar(
		&output_debug,
		"debug", false,
		"Print debug information.",
	)
	flag.BoolVar(
		&input_mode_git,
		"git", false,
		"Scan for files using git ls-tree and cat-file, rather than filesystem.",
	)
	flag.BoolVar(
		&input_mode_fs,
		"fs", false,
		"Scan for files using filesystem.",
	)
	flag.StringVar(
		&input_git_tree,
		"git-tree", "HEAD",
		"tree-ish root to scan. See also man git(1).",
	)
	flag.BoolVar(
		&output_json,
		"json", false,
		"Output results in JSON format.",
	)
	flag.BoolVar(
		&output_json_with_colors,
		"json-with-colors", false,
		"Output results in JSON format, including any HTML color codes defined for associated languages.",
	)
	flag.IntVar(
		&output_limit,
		"limit", 10,
		"Limit number of languages to n results. n <= 0 for unlimited.",
	)
	flag.BoolVar(
		&unignore_filenames,
		"unignore-filenames", false,
		"Do NOT skip processing ignored file types based on filename (NOT RECOMMENDED)",
	)
	flag.BoolVar(
		&unignore_contents,
		"unignore-contents", false,
		"Do NOT skip processing ignored file types based on contents (NOT RECOMMENDED)",
	)

	flag.Parse()

	output_json = output_json || output_json_with_colors

	if !output_debug {
		log.SetOutput(ioutil.Discard)
	}

	var (
		default_input_mode_git bool
		default_input_mode_fs  bool
	)

	if !input_mode_fs && findGitDir() { // side-effect: cd's to GIT_DIR!
		default_input_mode_git = true
		default_input_mode_fs = false
	} else {
		default_input_mode_git = false
		default_input_mode_fs = true
	}

	if !input_mode_git && !input_mode_fs {
		input_mode_git = default_input_mode_git
		input_mode_fs = default_input_mode_fs
	}

	if !input_mode_git && input_git_tree != "HEAD" {
		input_mode_git = true
		input_mode_fs = false
	}

	if input_mode_git && input_mode_fs {
		fmt.Println("Please choose one of -git or -fs as flags, but not both.")
		fmt.Println("You can omit the flags to get the default behavior,")
		fmt.Printf("which for the current directory is %s\n", func() string {
			switch {
			case default_input_mode_git:
				return "git"
			case default_input_mode_fs:
				return "fs"
			}
			return "undefined"
		}())
		os.Exit(1)
	}

	if input_mode_fs {
		initGitIgnore()
		processDir(".")
	}

	if input_mode_git {
		repo, err := git4go.OpenRepository(".")
		checkErr(err)
		ref, err := repo.DwimReference(input_git_tree)
		checkErr(err)
		resolved, err := ref.Resolve()
		checkErr(err)
		odb, err := repo.Odb()
		checkErr(err)
		processTree(repo, odb, resolved.Target(), []string{})
	}

	results := []*language{}
	for lang, size := range langs {
		results = append(results, &language{
			Language: lang,
			Percent:  (float64(size) / float64(total_size)) * 100.0,
		})
	}

	sort.Sort(sort.Reverse(sortableResult(results)))

	if output_json {
		out := []interface{}{}
		for i, lang := range results {
			if output_limit > 0 && i >= output_limit {
				break
			}
			var l interface{}
			if output_json_with_colors {
				l = &language_color{lang.Language, lang.Percent, linguist.LanguageColor(lang.Language)}
			} else {
				l = lang
			}
			out = append(out, l)
		}
		j, err := json.MarshalIndent(out, "", "  ")
		checkErr(err)
		fmt.Println(string(j))
		os.Exit(0)
	}
	fmtstr := fmt.Sprintf("%% %ds", max_len)
	fmtstr += ": %07.4f%%\n"

	for i, l := range results {
		if output_limit > 0 && i >= output_limit {
			break
		}
		fmt.Printf(fmtstr, l.Language, l.Percent)
	}

	fmt.Printf("\n%d language%s detected in %d file%s\n", len(results), pluralize(len(results)), num_files, pluralize(num_files))
	fmt.Printf("%d ignored path%s\n", ignored_paths, pluralize(ignored_paths))
}
<commit_msg>cmd/l: show % of other languages when number of results exceeds output_limit<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"sort"

	"github.com/generaltso/git4go"
	"github.com/generaltso/linguist"
)

func checkErr(err error) {
	if err != nil {
		if output_debug {
			log.Panicln(err)
		} else {
			fmt.Println(err)
			os.Exit(1)
		}
	}
}

// flag vars
var (
	input_mode_git          bool
	input_mode_fs           bool
	input_git_tree          string
	output_json             bool
	output_json_with_colors bool
	output_limit            int
	output_debug            bool
	unignore_filenames      bool
	unignore_contents       bool
)

// used for displaying results
type (
	language struct {
		Language string  `json:"language"`
		Percent  float64 `json:"percent"`
	}

	language_color struct {
		Language string  `json:"language"`
		Percent  float64 `json:"percent"`
		Color    string  `json:"color"`
	}
)

type sortableResult []*language

func (s sortableResult) Len() int {
	return len(s)
}

func (s sortableResult) Less(i, j int) bool {
	return s[i].Percent < s[j].Percent
}

func (s sortableResult) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

var (
	langs         map[string]int = make(map[string]int)
	total_size    int            = 0
	num_files     int            = 0
	max_len       int            = 0
	ignored_paths int            = 0
)

func putResult(language string, size int) {
	langs[language] += size
	total_size += size
	num_files++
	if len(language) > max_len {
		max_len = len(language)
	}
}
func pluralize(num int) string {
	if num == 1 {
		return ""
	}
	return "s"
}

func main() {
	flag.BoolVar(
		&output_debug,
		"debug", false,
		"Print debug information.",
	)
	flag.BoolVar(
		&input_mode_git,
		"git", false,
		"Scan for files using git ls-tree and cat-file, rather than filesystem.",
	)
	flag.BoolVar(
		&input_mode_fs,
		"fs", false,
		"Scan for files using filesystem.",
	)
	flag.StringVar(
		&input_git_tree,
		"git-tree", "HEAD",
		"tree-ish root to scan. See also man git(1).",
	)
	flag.BoolVar(
		&output_json,
		"json", false,
		"Output results in JSON format.",
	)
	flag.BoolVar(
		&output_json_with_colors,
		"json-with-colors", false,
		"Output results in JSON format, including any HTML color codes defined for associated languages.",
	)
	flag.IntVar(
		&output_limit,
		"limit", 10,
		"Limit number of languages to n results. n <= 0 for unlimited.",
	)
	flag.BoolVar(
		&unignore_filenames,
		"unignore-filenames", false,
		"Do NOT skip processing ignored file types based on filename (NOT RECOMMENDED)",
	)
	flag.BoolVar(
		&unignore_contents,
		"unignore-contents", false,
		"Do NOT skip processing ignored file types based on contents (NOT RECOMMENDED)",
	)

	flag.Parse()

	output_json = output_json || output_json_with_colors

	if !output_debug {
		log.SetOutput(ioutil.Discard)
	}

	var (
		default_input_mode_git bool
		default_input_mode_fs  bool
	)

	if !input_mode_fs && findGitDir() { // side-effect: cd's to GIT_DIR!
		default_input_mode_git = true
		default_input_mode_fs = false
	} else {
		default_input_mode_git = false
		default_input_mode_fs = true
	}

	if !input_mode_git && !input_mode_fs {
		input_mode_git = default_input_mode_git
		input_mode_fs = default_input_mode_fs
	}

	if !input_mode_git && input_git_tree != "HEAD" {
		input_mode_git = true
		input_mode_fs = false
	}

	if input_mode_git && input_mode_fs {
		fmt.Println("Please choose one of -git or -fs as flags, but not both.")
		fmt.Println("You can omit the flags to get the default behavior,")
		fmt.Printf("which for the current directory is %s\n", func() string {
			switch {
			case default_input_mode_git:
				return "git"
			case default_input_mode_fs:
				return "fs"
			}
			return "undefined"
		}())
		os.Exit(1)
	}

	if input_mode_fs {
		initGitIgnore()
		processDir(".")
	}

	if input_mode_git {
		repo, err := git4go.OpenRepository(".")
		checkErr(err)
		ref, err := repo.DwimReference(input_git_tree)
		checkErr(err)
		resolved, err := ref.Resolve()
		checkErr(err)
		odb, err := repo.Odb()
		checkErr(err)
		processTree(repo, odb, resolved.Target(), []string{})
	}

	results := []*language{}
	for lang, size := range langs {
		results = append(results, &language{
			Language: lang,
			Percent:  (float64(size) / float64(total_size)) * 100.0,
		})
	}

	sort.Sort(sort.Reverse(sortableResult(results)))

	if output_limit > 0 && len(results) > output_limit {
		other := &language{
			Language: "Other",
		}
		for i := output_limit; i < len(results); i++ {
			other.Percent += results[i].Percent
		}
		results = append(results[0:output_limit], other)
	}

	if output_json {
		out := []interface{}{}
		for _, lang := range results {
			var l interface{}
			if output_json_with_colors {
				l = &language_color{lang.Language, lang.Percent, linguist.LanguageColor(lang.Language)}
			} else {
				l = lang
			}
			out = append(out, l)
		}
		j, err := json.MarshalIndent(out, "", "  ")
		checkErr(err)
		fmt.Println(string(j))
		os.Exit(0)
	}
	fmtstr := fmt.Sprintf("%% %ds", max_len)
	fmtstr += ": %07.4f%%\n"

	for _, l := range results {
		fmt.Printf(fmtstr, l.Language, l.Percent)
	}

	fmt.Printf("\n%d language%s detected in %d file%s\n", len(results), pluralize(len(results)), num_files, pluralize(num_files))
	fmt.Printf("%d ignored path%s\n", ignored_paths, pluralize(ignored_paths))
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"gopkg.in/readline.v1"
)

func main() {
	repl()
}

func repl() {
	p := NewPrsr()
	rl, err := readline.New("rocket> ")
	if err != nil {
		panic(err)
	}
	defer rl.Close()
	r1 := 0
	for {
		gline, err := rl.Readline()
		if err != nil {
			panic(err)
		}
		r1 = p.Run(gline)
		fmt.Println(r1)
	}

}
<commit_msg>fixed rocket.go newPrsr<commit_after>package main

import (
	"fmt"
	"github/fabulousduck/rocket"
	"gopkg.in/readline.v1"
)

func main() {
	repl()
}

func repl() {
	p := rocket.NewPrsr()
	rl, err := readline.New("rocket> ")
	if err != nil {
		panic(err)
	}
	defer rl.Close()
	r1 := 0
	for {
		gline, err := rl.Readline()
		if err != nil {
			panic(err)
		}
		r1 = p.Run(gline)
		fmt.Println(r1)
	}

}
<|endoftext|>


<commit_before>// Copyright © 2017-2018 Mikael Berthe <mikael@lilotux.net>
//
// Licensed under the MIT license.
// Please see the LICENSE file is this directory.

package cmd

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

	"github.com/pkg/errors"
	"github.com/spf13/cobra"
	flag "github.com/spf13/pflag"

	"github.com/McKael/madon"
)

var statusPostFlags *flag.FlagSet

var statusOpts struct {
	statusID int64
	unset    bool

	// The following fields are used for the post/toot command
	visibility     string
	sensitive      bool
	spoiler        string
	inReplyToID    int64
	mediaIDs       string
	mediaFilePath  string
	textFilePath   string
	stdin          bool
	addMentions    bool
	sameVisibility bool

	// Used for several subcommands to limit the number of results
	limit, keep uint
	//sinceID, maxID int64
	all bool
}

func init() {
	RootCmd.AddCommand(statusCmd)

	// Subcommands
	statusCmd.AddCommand(statusSubcommands...)

	// Global flags
	statusCmd.PersistentFlags().Int64VarP(&statusOpts.statusID, "status-id", "s", 0, "Status ID number")
	statusCmd.PersistentFlags().UintVarP(&statusOpts.limit, "limit", "l", 0, "Limit number of API results")
	statusCmd.PersistentFlags().UintVarP(&statusOpts.keep, "keep", "k", 0, "Limit number of results")
	//statusCmd.PersistentFlags().Int64Var(&statusOpts.sinceID, "since-id", 0, "Request IDs greater than a value")
	//statusCmd.PersistentFlags().Int64Var(&statusOpts.maxID, "max-id", 0, "Request IDs less (or equal) than a value")
	statusCmd.PersistentFlags().BoolVar(&statusOpts.all, "all", false, "Fetch all results (for reblogged-by/favourited-by)")

	statusCmd.MarkPersistentFlagRequired("status-id")

	// Subcommand flags
	statusReblogSubcommand.Flags().BoolVar(&statusOpts.unset, "unset", false, "Unreblog the status")
	statusFavouriteSubcommand.Flags().BoolVar(&statusOpts.unset, "unset", false, "Remove the status from the favourites")
	statusPinSubcommand.Flags().BoolVar(&statusOpts.unset, "unset", false, "Unpin the status")
	statusPostSubcommand.Flags().BoolVar(&statusOpts.sensitive, "sensitive", false, "Mark post as sensitive (NSFW)")
	statusPostSubcommand.Flags().StringVar(&statusOpts.visibility, "visibility", "", "Visibility (direct|private|unlisted|public)")
	statusPostSubcommand.Flags().StringVar(&statusOpts.spoiler, "spoiler", "", "Spoiler warning (CW)")
	statusPostSubcommand.Flags().StringVar(&statusOpts.mediaIDs, "media-ids", "", "Comma-separated list of media IDs")
	statusPostSubcommand.Flags().StringVarP(&statusOpts.mediaFilePath, "file", "f", "", "Media file name")
	statusPostSubcommand.Flags().StringVar(&statusOpts.textFilePath, "text-file", "", "Text file name (message content)")
	statusPostSubcommand.Flags().Int64VarP(&statusOpts.inReplyToID, "in-reply-to", "r", 0, "Status ID to reply to")
	statusPostSubcommand.Flags().BoolVar(&statusOpts.stdin, "stdin", false, "Read message content from standard input")
	statusPostSubcommand.Flags().BoolVar(&statusOpts.addMentions, "add-mentions", false, "Add mentions when replying")
	statusPostSubcommand.Flags().BoolVar(&statusOpts.sameVisibility, "same-visibility", false, "Use same visibility as original message (for replies)")

	// Flag completion
	annotation := make(map[string][]string)
	annotation[cobra.BashCompCustom] = []string{"__madonctl_visibility"}

	statusPostSubcommand.Flags().Lookup("visibility").Annotations = annotation

	// This one will be used to check if the options were explicitly set or not
	statusPostFlags = statusPostSubcommand.Flags()
}

// statusCmd represents the status command
// This command does nothing without a subcommand
var statusCmd = &cobra.Command{
	Use:     "status --status-id ID subcommand",
	Aliases: []string{"st"},
	Short:   "Get status details",
	//Long:    `TBW...`, // TODO
	PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
		// This is common to status and all status subcommands but "post"
		if statusOpts.statusID < 1 && cmd.Name() != "post" {
			return errors.New("missing status ID")
		}
		return madonInit(true)
	},
}

var statusSubcommands = []*cobra.Command{
	&cobra.Command{
		Use:     "show",
		Aliases: []string{"display"},
		Short:   "Get the status",
		RunE: func(cmd *cobra.Command, args []string) error {
			return statusSubcommandRunE(cmd.Name(), args)
		},
	},
	&cobra.Command{
		Use:   "context",
		Short: "Get the status context",
		RunE: func(cmd *cobra.Command, args []string) error {
			return statusSubcommandRunE(cmd.Name(), args)
		},
	},
	&cobra.Command{
		Use:   "card",
		Short: "Get the status card",
		RunE: func(cmd *cobra.Command, args []string) error {
			return statusSubcommandRunE(cmd.Name(), args)
		},
	},
	&cobra.Command{
		Use:   "reblogged-by",
		Short: "Display accounts which reblogged the status",
		RunE: func(cmd *cobra.Command, args []string) error {
			return statusSubcommandRunE(cmd.Name(), args)
		},
	},
	&cobra.Command{
		Use:     "favourited-by",
		Aliases: []string{"favorited-by"},
		Short:   "Display accounts which favourited the status",
		RunE: func(cmd *cobra.Command, args []string) error {
			return statusSubcommandRunE(cmd.Name(), args)
		},
	},
	&cobra.Command{
		Use:     "delete",
		Aliases: []string{"rm"},
		Short:   "Delete the status",
		RunE: func(cmd *cobra.Command, args []string) error {
			return statusSubcommandRunE(cmd.Name(), args)
		},
	},
	&cobra.Command{
		Use:     "mute-conversation",
		Aliases: []string{"mute"},
		Short:   "Mute the conversation containing the status",
		RunE: func(cmd *cobra.Command, args []string) error {
			return statusSubcommandRunE(cmd.Name(), args)
		},
	},
	&cobra.Command{
		Use:     "unmute-conversation",
		Aliases: []string{"unmute"},
		Short:   "Unmute the conversation containing the status",
		RunE: func(cmd *cobra.Command, args []string) error {
			return statusSubcommandRunE(cmd.Name(), args)
		},
	},
	statusReblogSubcommand,
	statusFavouriteSubcommand,
	statusPinSubcommand,
	statusPostSubcommand,
}

var statusReblogSubcommand = &cobra.Command{
	Use:     "boost",
	Aliases: []string{"reblog"},
	Short:   "Boost (reblog) or unreblog the status",
	RunE: func(cmd *cobra.Command, args []string) error {
		return statusSubcommandRunE(cmd.Name(), args)
	},
}

var statusFavouriteSubcommand = &cobra.Command{
	Use:     "favourite",
	Aliases: []string{"favorite", "fave"},
	Short:   "Mark/unmark the status as favourite",
	RunE: func(cmd *cobra.Command, args []string) error {
		return statusSubcommandRunE(cmd.Name(), args)
	},
}

var statusPinSubcommand = &cobra.Command{
	Use:   "pin",
	Short: "Pin/unpin the status",
	RunE: func(cmd *cobra.Command, args []string) error {
		return statusSubcommandRunE(cmd.Name(), args)
	},
}

var statusPostSubcommand = &cobra.Command{
	Use:     "post",
	Aliases: []string{"toot", "pouet"},
	Short:   "Post a message (same as 'madonctl toot')",
	Example: `  madonctl status post --spoiler Warning "Hello, World"
  madonctl status toot --sensitive --file image.jpg Image
  madonctl status post --media-ids ID1,ID2,ID3 Image
  madonctl status toot --text-file message.txt
  madonctl status post --in-reply-to STATUSID "@user response"
  madonctl status post --in-reply-to STATUSID --add-mentions "response"
  echo "Hello from #madonctl" | madonctl status toot --stdin

The default visibility can be set in the configuration file with the option
'default_visibility' (or with an environmnent variable).`,
	RunE: func(cmd *cobra.Command, args []string) error {
		return statusSubcommandRunE(cmd.Name(), args)
	},
}

func statusSubcommandRunE(subcmd string, args []string) error {
	opt := statusOpts

	var obj interface{}
	var err error

	var limOpts *madon.LimitParams
	if opt.all || opt.limit > 0 /* || opt.sinceID > 0 || opt.maxID > 0 */ {
		limOpts = new(madon.LimitParams)
		limOpts.All = opt.all
	}

	if opt.limit > 0 {
		limOpts.Limit = int(opt.limit)
	}
	/*
		if opt.maxID > 0 {
			limOpts.MaxID = int64(opt.maxID)
		}
		if opt.sinceID > 0 {
			limOpts.SinceID = int64(opt.sinceID)
		}
	*/

	switch subcmd {
	case "show":
		var status *madon.Status
		status, err = gClient.GetStatus(opt.statusID)
		obj = status
	case "context":
		var context *madon.Context
		context, err = gClient.GetStatusContext(opt.statusID)
		obj = context
	case "card":
		var context *madon.Card
		context, err = gClient.GetStatusCard(opt.statusID)
		obj = context
	case "reblogged-by":
		var accountList []madon.Account
		accountList, err = gClient.GetStatusRebloggedBy(opt.statusID, limOpts)
		if opt.keep > 0 && len(accountList) > int(opt.keep) {
			accountList = accountList[:opt.keep]
		}
		obj = accountList
	case "favourited-by":
		var accountList []madon.Account
		accountList, err = gClient.GetStatusFavouritedBy(opt.statusID, limOpts)
		if opt.keep > 0 && len(accountList) > int(opt.keep) {
			accountList = accountList[:opt.keep]
		}
		obj = accountList
	case "delete":
		err = gClient.DeleteStatus(opt.statusID)
	case "boost":
		if opt.unset {
			err = gClient.UnreblogStatus(opt.statusID)
		} else {
			err = gClient.ReblogStatus(opt.statusID)
		}
	case "favourite":
		if opt.unset {
			err = gClient.UnfavouriteStatus(opt.statusID)
		} else {
			err = gClient.FavouriteStatus(opt.statusID)
		}
	case "pin":
		if opt.unset {
			err = gClient.UnpinStatus(opt.statusID)
		} else {
			err = gClient.PinStatus(opt.statusID)
		}
	case "mute-conversation":
		var s *madon.Status
		s, err = gClient.MuteConversation(opt.statusID)
		obj = s
	case "unmute-conversation":
		var s *madon.Status
		s, err = gClient.UnmuteConversation(opt.statusID)
		obj = s
	case "post": // toot
		var s *madon.Status
		text := strings.Join(args, " ")
		if opt.textFilePath != "" {
			var b []byte
			if b, err = ioutil.ReadFile(opt.textFilePath); err != nil {
				break
			}
			text = string(b)
		} else if opt.stdin {
			var b []byte
			if b, err = ioutil.ReadAll(os.Stdin); err != nil {
				break
			}
			text = string(b)
		}
		s, err = toot(text)
		obj = s
	default:
		return errors.New("statusSubcommand: internal error")
	}

	if err != nil {
		errPrint("Error: %s", err.Error())
		os.Exit(1)
	}
	if obj == nil {
		return nil
	}

	p, err := getPrinter()
	if err != nil {
		errPrint("Error: %s", err.Error())
		os.Exit(1)
	}
	return p.printObj(obj)
}
<commit_msg>Bugfix: Remove required flag constraint<commit_after>// Copyright © 2017-2018 Mikael Berthe <mikael@lilotux.net>
//
// Licensed under the MIT license.
// Please see the LICENSE file is this directory.

package cmd

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

	"github.com/pkg/errors"
	"github.com/spf13/cobra"
	flag "github.com/spf13/pflag"

	"github.com/McKael/madon"
)

var statusPostFlags *flag.FlagSet

var statusOpts struct {
	statusID int64
	unset    bool

	// The following fields are used for the post/toot command
	visibility     string
	sensitive      bool
	spoiler        string
	inReplyToID    int64
	mediaIDs       string
	mediaFilePath  string
	textFilePath   string
	stdin          bool
	addMentions    bool
	sameVisibility bool

	// Used for several subcommands to limit the number of results
	limit, keep uint
	//sinceID, maxID int64
	all bool
}

func init() {
	RootCmd.AddCommand(statusCmd)

	// Subcommands
	statusCmd.AddCommand(statusSubcommands...)

	// Global flags
	statusCmd.PersistentFlags().Int64VarP(&statusOpts.statusID, "status-id", "s", 0, "Status ID number")
	statusCmd.PersistentFlags().UintVarP(&statusOpts.limit, "limit", "l", 0, "Limit number of API results")
	statusCmd.PersistentFlags().UintVarP(&statusOpts.keep, "keep", "k", 0, "Limit number of results")
	//statusCmd.PersistentFlags().Int64Var(&statusOpts.sinceID, "since-id", 0, "Request IDs greater than a value")
	//statusCmd.PersistentFlags().Int64Var(&statusOpts.maxID, "max-id", 0, "Request IDs less (or equal) than a value")
	statusCmd.PersistentFlags().BoolVar(&statusOpts.all, "all", false, "Fetch all results (for reblogged-by/favourited-by)")

	// Subcommand flags
	statusReblogSubcommand.Flags().BoolVar(&statusOpts.unset, "unset", false, "Unreblog the status")
	statusFavouriteSubcommand.Flags().BoolVar(&statusOpts.unset, "unset", false, "Remove the status from the favourites")
	statusPinSubcommand.Flags().BoolVar(&statusOpts.unset, "unset", false, "Unpin the status")
	statusPostSubcommand.Flags().BoolVar(&statusOpts.sensitive, "sensitive", false, "Mark post as sensitive (NSFW)")
	statusPostSubcommand.Flags().StringVar(&statusOpts.visibility, "visibility", "", "Visibility (direct|private|unlisted|public)")
	statusPostSubcommand.Flags().StringVar(&statusOpts.spoiler, "spoiler", "", "Spoiler warning (CW)")
	statusPostSubcommand.Flags().StringVar(&statusOpts.mediaIDs, "media-ids", "", "Comma-separated list of media IDs")
	statusPostSubcommand.Flags().StringVarP(&statusOpts.mediaFilePath, "file", "f", "", "Media file name")
	statusPostSubcommand.Flags().StringVar(&statusOpts.textFilePath, "text-file", "", "Text file name (message content)")
	statusPostSubcommand.Flags().Int64VarP(&statusOpts.inReplyToID, "in-reply-to", "r", 0, "Status ID to reply to")
	statusPostSubcommand.Flags().BoolVar(&statusOpts.stdin, "stdin", false, "Read message content from standard input")
	statusPostSubcommand.Flags().BoolVar(&statusOpts.addMentions, "add-mentions", false, "Add mentions when replying")
	statusPostSubcommand.Flags().BoolVar(&statusOpts.sameVisibility, "same-visibility", false, "Use same visibility as original message (for replies)")

	// Flag completion
	annotation := make(map[string][]string)
	annotation[cobra.BashCompCustom] = []string{"__madonctl_visibility"}

	statusPostSubcommand.Flags().Lookup("visibility").Annotations = annotation

	// This one will be used to check if the options were explicitly set or not
	statusPostFlags = statusPostSubcommand.Flags()
}

// statusCmd represents the status command
// This command does nothing without a subcommand
var statusCmd = &cobra.Command{
	Use:     "status --status-id ID subcommand",
	Aliases: []string{"st"},
	Short:   "Get status details",
	//Long:    `TBW...`, // TODO
	PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
		// This is common to status and all status subcommands but "post"
		if statusOpts.statusID < 1 && cmd.Name() != "post" {
			return errors.New("missing status ID")
		}
		return madonInit(true)
	},
}

var statusSubcommands = []*cobra.Command{
	&cobra.Command{
		Use:     "show",
		Aliases: []string{"display"},
		Short:   "Get the status",
		RunE: func(cmd *cobra.Command, args []string) error {
			return statusSubcommandRunE(cmd.Name(), args)
		},
	},
	&cobra.Command{
		Use:   "context",
		Short: "Get the status context",
		RunE: func(cmd *cobra.Command, args []string) error {
			return statusSubcommandRunE(cmd.Name(), args)
		},
	},
	&cobra.Command{
		Use:   "card",
		Short: "Get the status card",
		RunE: func(cmd *cobra.Command, args []string) error {
			return statusSubcommandRunE(cmd.Name(), args)
		},
	},
	&cobra.Command{
		Use:   "reblogged-by",
		Short: "Display accounts which reblogged the status",
		RunE: func(cmd *cobra.Command, args []string) error {
			return statusSubcommandRunE(cmd.Name(), args)
		},
	},
	&cobra.Command{
		Use:     "favourited-by",
		Aliases: []string{"favorited-by"},
		Short:   "Display accounts which favourited the status",
		RunE: func(cmd *cobra.Command, args []string) error {
			return statusSubcommandRunE(cmd.Name(), args)
		},
	},
	&cobra.Command{
		Use:     "delete",
		Aliases: []string{"rm"},
		Short:   "Delete the status",
		RunE: func(cmd *cobra.Command, args []string) error {
			return statusSubcommandRunE(cmd.Name(), args)
		},
	},
	&cobra.Command{
		Use:     "mute-conversation",
		Aliases: []string{"mute"},
		Short:   "Mute the conversation containing the status",
		RunE: func(cmd *cobra.Command, args []string) error {
			return statusSubcommandRunE(cmd.Name(), args)
		},
	},
	&cobra.Command{
		Use:     "unmute-conversation",
		Aliases: []string{"unmute"},
		Short:   "Unmute the conversation containing the status",
		RunE: func(cmd *cobra.Command, args []string) error {
			return statusSubcommandRunE(cmd.Name(), args)
		},
	},
	statusReblogSubcommand,
	statusFavouriteSubcommand,
	statusPinSubcommand,
	statusPostSubcommand,
}

var statusReblogSubcommand = &cobra.Command{
	Use:     "boost",
	Aliases: []string{"reblog"},
	Short:   "Boost (reblog) or unreblog the status",
	RunE: func(cmd *cobra.Command, args []string) error {
		return statusSubcommandRunE(cmd.Name(), args)
	},
}

var statusFavouriteSubcommand = &cobra.Command{
	Use:     "favourite",
	Aliases: []string{"favorite", "fave"},
	Short:   "Mark/unmark the status as favourite",
	RunE: func(cmd *cobra.Command, args []string) error {
		return statusSubcommandRunE(cmd.Name(), args)
	},
}

var statusPinSubcommand = &cobra.Command{
	Use:   "pin",
	Short: "Pin/unpin the status",
	RunE: func(cmd *cobra.Command, args []string) error {
		return statusSubcommandRunE(cmd.Name(), args)
	},
}

var statusPostSubcommand = &cobra.Command{
	Use:     "post",
	Aliases: []string{"toot", "pouet"},
	Short:   "Post a message (same as 'madonctl toot')",
	Example: `  madonctl status post --spoiler Warning "Hello, World"
  madonctl status toot --sensitive --file image.jpg Image
  madonctl status post --media-ids ID1,ID2,ID3 Image
  madonctl status toot --text-file message.txt
  madonctl status post --in-reply-to STATUSID "@user response"
  madonctl status post --in-reply-to STATUSID --add-mentions "response"
  echo "Hello from #madonctl" | madonctl status toot --stdin

The default visibility can be set in the configuration file with the option
'default_visibility' (or with an environmnent variable).`,
	RunE: func(cmd *cobra.Command, args []string) error {
		return statusSubcommandRunE(cmd.Name(), args)
	},
}

func statusSubcommandRunE(subcmd string, args []string) error {
	opt := statusOpts

	var obj interface{}
	var err error

	var limOpts *madon.LimitParams
	if opt.all || opt.limit > 0 /* || opt.sinceID > 0 || opt.maxID > 0 */ {
		limOpts = new(madon.LimitParams)
		limOpts.All = opt.all
	}

	if opt.limit > 0 {
		limOpts.Limit = int(opt.limit)
	}
	/*
		if opt.maxID > 0 {
			limOpts.MaxID = int64(opt.maxID)
		}
		if opt.sinceID > 0 {
			limOpts.SinceID = int64(opt.sinceID)
		}
	*/

	switch subcmd {
	case "show":
		var status *madon.Status
		status, err = gClient.GetStatus(opt.statusID)
		obj = status
	case "context":
		var context *madon.Context
		context, err = gClient.GetStatusContext(opt.statusID)
		obj = context
	case "card":
		var context *madon.Card
		context, err = gClient.GetStatusCard(opt.statusID)
		obj = context
	case "reblogged-by":
		var accountList []madon.Account
		accountList, err = gClient.GetStatusRebloggedBy(opt.statusID, limOpts)
		if opt.keep > 0 && len(accountList) > int(opt.keep) {
			accountList = accountList[:opt.keep]
		}
		obj = accountList
	case "favourited-by":
		var accountList []madon.Account
		accountList, err = gClient.GetStatusFavouritedBy(opt.statusID, limOpts)
		if opt.keep > 0 && len(accountList) > int(opt.keep) {
			accountList = accountList[:opt.keep]
		}
		obj = accountList
	case "delete":
		err = gClient.DeleteStatus(opt.statusID)
	case "boost":
		if opt.unset {
			err = gClient.UnreblogStatus(opt.statusID)
		} else {
			err = gClient.ReblogStatus(opt.statusID)
		}
	case "favourite":
		if opt.unset {
			err = gClient.UnfavouriteStatus(opt.statusID)
		} else {
			err = gClient.FavouriteStatus(opt.statusID)
		}
	case "pin":
		if opt.unset {
			err = gClient.UnpinStatus(opt.statusID)
		} else {
			err = gClient.PinStatus(opt.statusID)
		}
	case "mute-conversation":
		var s *madon.Status
		s, err = gClient.MuteConversation(opt.statusID)
		obj = s
	case "unmute-conversation":
		var s *madon.Status
		s, err = gClient.UnmuteConversation(opt.statusID)
		obj = s
	case "post": // toot
		var s *madon.Status
		text := strings.Join(args, " ")
		if opt.textFilePath != "" {
			var b []byte
			if b, err = ioutil.ReadFile(opt.textFilePath); err != nil {
				break
			}
			text = string(b)
		} else if opt.stdin {
			var b []byte
			if b, err = ioutil.ReadAll(os.Stdin); err != nil {
				break
			}
			text = string(b)
		}
		s, err = toot(text)
		obj = s
	default:
		return errors.New("statusSubcommand: internal error")
	}

	if err != nil {
		errPrint("Error: %s", err.Error())
		os.Exit(1)
	}
	if obj == nil {
		return nil
	}

	p, err := getPrinter()
	if err != nil {
		errPrint("Error: %s", err.Error())
		os.Exit(1)
	}
	return p.printObj(obj)
}
<|endoftext|>


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

// Print process information.
//
// Synopsis:
//     ps [-Aaex] [aux]
//
// Description:
//     ps reads the /proc filesystem and prints nice things about what it
//     finds.  /proc in linux has grown by a process of Evilution, so it's
//     messy.
//
// Options:
//     -A: select all processes. Identical to -e.
//     -e: select all processes. Identical to -A.
//     -x: BSD-Like style, with STAT Column and long CommandLine
//     -a: print all process except whose are session leaders or unlinked with terminal
//    aux: see every process on the system using BSD syntax
package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"sort"
	"strings"
)

var (
	flags struct {
		all     bool
		nSidTty bool
		x       bool
		aux     bool
	}
	cmd  = "ps [-Aaex] [aux]"
	eUID = os.Geteuid()
)

func init() {
	defUsage := flag.Usage
	flag.Usage = func() {
		os.Args[0] = cmd
		defUsage()
	}
	flag.BoolVar(&flags.all, "A", false, "Select all processes.  Identical to -e.")
	flag.BoolVar(&flags.all, "e", false, "Select all processes.  Identical to -A.")
	flag.BoolVar(&flags.x, "x", false, "BSD-Like style, with STAT Column and long CommandLine")
	flag.BoolVar(&flags.nSidTty, "a", false, "Print all process except whose are session leaders or unlinked with terminal")

	if len(os.Args) > 1 {
		if isPermutation(os.Args[1], "aux") {
			flags.aux = true
			flags.all = true
		}
	}
}

// ProcessTable holds all the information needed for ps
type ProcessTable struct {
	table    []*Process
	mProc    *Process
	headers  []string // each column to print
	fields   []string // which fields of process to print, on order
	fstring  []string // formated strings
	maxwidth int      // DEPRECATED: reason -> remove terminal stuff
}

// NewProcessTable creates an empty process table
func NewProcessTable() *ProcessTable {
	return &ProcessTable{}
}

// Len returns the number of processes in the ProcessTable.
func (pT ProcessTable) Len() int {
	return len(pT.table)
}

// to use on sort.Sort
func (pT ProcessTable) Less(i, j int) bool {
	return pT.table[i].Pidno < pT.table[j].Pidno
}

// to use on sort.Sort
func (pT ProcessTable) Swap(i, j int) {
	pT.table[i], pT.table[j] = pT.table[j], pT.table[i]
}

// Return the biggest value in a slice of ints.
func max(slice []int) int {
	max := slice[0]
	for _, value := range slice {
		if value > max {
			max = value
		}
	}
	return max
}

// MaxLength returns the longest string of a field of ProcessTable
func (pT ProcessTable) MaxLength(field string) int {
	slice := make([]int, 0)
	for _, p := range pT.table {
		slice = append(slice, len(p.Search(field)))
	}

	return max(slice)
}

// PrintHeader prints the header for ps, with correct spacing.
func (pT ProcessTable) PrintHeader(w io.Writer) {
	var row string
	for index, field := range pT.headers {
		formated := pT.fstring[index]
		row += fmt.Sprintf(formated, field)
	}

	fmt.Fprintf(w, "%v\n", row)
}

// PrintProcess prints information about one process.
func (pT ProcessTable) PrintProcess(index int, w io.Writer) {
	var row string
	p := pT.table[index]
	for index, f := range pT.fields {
		field := p.Search(f)
		formated := pT.fstring[index]
		row += fmt.Sprintf(formated, field)

	}

	fmt.Fprintf(w, "%v\n", row)
}

// PrepareString figures out how to lay out a process table print
func (pT *ProcessTable) PrepareString() {
	var (
		fstring  []string
		formated string
		PID      = pT.MaxLength("Pid")
		TTY      = pT.MaxLength("Ctty")
		STAT     = 4 | pT.MaxLength("State") // min : 4
		TIME     = pT.MaxLength("Time")
		CMD      = pT.MaxLength("Cmd")
	)
	for _, f := range pT.headers {
		switch f {
		case "PID":
			formated = fmt.Sprintf("%%%dv ", PID)
		case "TTY":
			formated = fmt.Sprintf("%%-%dv    ", TTY)
		case "STAT":
			formated = fmt.Sprintf("%%-%dv    ", STAT)
		case "TIME":
			formated = fmt.Sprintf("%%%dv ", TIME)
		case "CMD":
			formated = fmt.Sprintf("%%-%dv ", CMD)
		}
		fstring = append(fstring, formated)
	}

	pT.fstring = fstring
}

func isPermutation(check string, ref string) bool {
	if len(check) != len(ref) {
		return false
	}
	checkArray := strings.Split(check, "")
	refArray := strings.Split(ref, "")

	sort.Strings(checkArray)
	sort.Strings(refArray)

	for i := range check {
		if checkArray[i] != refArray[i] {
			return false
		}
	}
	return true
}

// For now, just read /proc/pid/stat and dump its brains.
// TODO: a nice clean way to turn /proc/pid/stat into a struct. (trying now)
// There has to be a way.
func ps(pT *ProcessTable, w io.Writer) error {
	if len(pT.table) == 0 {
		return nil
	}
	// sorting ProcessTable by PID
	sort.Sort(pT)

	switch {
	case flags.aux:
		pT.headers = []string{"PID", "PGRP", "SID", "TTY", "STAT", "TIME", "COMMAND"}
		pT.fields = []string{"Pid", "Pgrp", "Sid", "Ctty", "State", "Time", "Cmd"}
	case flags.x:
		pT.headers = []string{"PID", "TTY", "STAT", "TIME", "COMMAND"}
		pT.fields = []string{"Pid", "Ctty", "State", "Time", "Cmd"}
	default:
		pT.headers = []string{"PID", "TTY", "TIME", "CMD"}
		pT.fields = []string{"Pid", "Ctty", "Time", "Cmd"}
	}

	pT.PrepareString()
	pT.PrintHeader(w)
	for index, p := range pT.table {
		switch {
		case flags.nSidTty:
			// no session leaders and no unlinked terminals
			if p.Sid == p.Pid || p.Ctty == "?" {
				continue
			}

		case flags.x:
			// print only process with same eUID of caller
			if eUID != p.uid {
				continue
			}

		case flags.all:
			// pass, print all

		default:
			// default for no flags only same session
			// and same uid process
			if p.Sid != pT.mProc.Sid || eUID != p.uid {
				continue
			}
		}

		pT.PrintProcess(index, w)
	}

	return nil

}

func main() {
	flag.Parse()
	pT := NewProcessTable()
	if err := pT.LoadTable(); err != nil {
		log.Fatal(err)
	}

	err := ps(pT, os.Stderr)
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>ps: adopt spf13 flag package, remove permutation stuff<commit_after>// Copyright 2013-2018 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Print process information.
//
// Synopsis:
//     ps [-Aaex] [aux]
//
// Description:
//     ps reads the /proc filesystem and prints nice things about what it
//     finds.  /proc in linux has grown by a process of Evilution, so it's
//     messy.
//
// Options:
//     -A: select all processes. Identical to -e.
//     -e: select all processes. Identical to -A.
//     -x: BSD-Like style, with STAT Column and long CommandLine
//     -a: print all process except whose are session leaders or unlinked with terminal
//    aux: see every process on the system using BSD syntax
package main

import (
	"fmt"
	flag "github.com/spf13/pflag"
	"io"
	"log"
	"os"
	"sort"
)

var (
	flags struct {
		all     bool
		nSidTty bool
		x       bool
		aux     bool
	}
	cmd  = "ps [-Aaex] [aux]"
	eUID = os.Geteuid()
)

func init() {
	defUsage := flag.Usage
	flag.Usage = func() {
		os.Args[0] = cmd
		defUsage()
	}
	flag.BoolVarP(&flags.all, "All", "A", false, "Select all processes.  Identical to -e.")
	flag.BoolVarP(&flags.all, "every", "e", false, "Select all processes.  Identical to -A.")
	flag.BoolVarP(&flags.x, "bsd", "x", false, "BSD-Like style, with STAT Column and long CommandLine")
	flag.BoolVarP(&flags.nSidTty, "nSIDTTY", "a", false, "Print all process except whose are session leaders or unlinked with terminal")
}

// ProcessTable holds all the information needed for ps
type ProcessTable struct {
	table    []*Process
	mProc    *Process
	headers  []string // each column to print
	fields   []string // which fields of process to print, on order
	fstring  []string // formated strings
	maxwidth int      // DEPRECATED: reason -> remove terminal stuff
}

// NewProcessTable creates an empty process table
func NewProcessTable() *ProcessTable {
	return &ProcessTable{}
}

// Len returns the number of processes in the ProcessTable.
func (pT ProcessTable) Len() int {
	return len(pT.table)
}

// to use on sort.Sort
func (pT ProcessTable) Less(i, j int) bool {
	return pT.table[i].Pidno < pT.table[j].Pidno
}

// to use on sort.Sort
func (pT ProcessTable) Swap(i, j int) {
	pT.table[i], pT.table[j] = pT.table[j], pT.table[i]
}

// Return the biggest value in a slice of ints.
func max(slice []int) int {
	max := slice[0]
	for _, value := range slice {
		if value > max {
			max = value
		}
	}
	return max
}

// MaxLength returns the longest string of a field of ProcessTable
func (pT ProcessTable) MaxLength(field string) int {
	slice := make([]int, 0)
	for _, p := range pT.table {
		slice = append(slice, len(p.Search(field)))
	}

	return max(slice)
}

// PrintHeader prints the header for ps, with correct spacing.
func (pT ProcessTable) PrintHeader(w io.Writer) {
	var row string
	for index, field := range pT.headers {
		formated := pT.fstring[index]
		row += fmt.Sprintf(formated, field)
	}

	fmt.Fprintf(w, "%v\n", row)
}

// PrintProcess prints information about one process.
func (pT ProcessTable) PrintProcess(index int, w io.Writer) {
	var row string
	p := pT.table[index]
	for index, f := range pT.fields {
		field := p.Search(f)
		formated := pT.fstring[index]
		row += fmt.Sprintf(formated, field)

	}

	fmt.Fprintf(w, "%v\n", row)
}

// PrepareString figures out how to lay out a process table print
func (pT *ProcessTable) PrepareString() {
	var (
		fstring  []string
		formated string
		PID      = pT.MaxLength("Pid")
		TTY      = pT.MaxLength("Ctty")
		STAT     = 4 | pT.MaxLength("State") // min : 4
		TIME     = pT.MaxLength("Time")
		CMD      = pT.MaxLength("Cmd")
	)
	for _, f := range pT.headers {
		switch f {
		case "PID":
			formated = fmt.Sprintf("%%%dv ", PID)
		case "TTY":
			formated = fmt.Sprintf("%%-%dv    ", TTY)
		case "STAT":
			formated = fmt.Sprintf("%%-%dv    ", STAT)
		case "TIME":
			formated = fmt.Sprintf("%%%dv ", TIME)
		case "CMD":
			formated = fmt.Sprintf("%%-%dv ", CMD)
		}
		fstring = append(fstring, formated)
	}

	pT.fstring = fstring
}

// For now, just read /proc/pid/stat and dump its brains.
func ps(pT *ProcessTable, w io.Writer) error {
	if len(pT.table) == 0 {
		return nil
	}
	// sorting ProcessTable by PID
	sort.Sort(pT)

	switch {
	case flags.aux:
		pT.headers = []string{"PID", "PGRP", "SID", "TTY", "STAT", "TIME", "COMMAND"}
		pT.fields = []string{"Pid", "Pgrp", "Sid", "Ctty", "State", "Time", "Cmd"}
	case flags.x:
		pT.headers = []string{"PID", "TTY", "STAT", "TIME", "COMMAND"}
		pT.fields = []string{"Pid", "Ctty", "State", "Time", "Cmd"}
	default:
		pT.headers = []string{"PID", "TTY", "TIME", "CMD"}
		pT.fields = []string{"Pid", "Ctty", "Time", "Cmd"}
	}

	pT.PrepareString()
	pT.PrintHeader(w)
	for index, p := range pT.table {
		switch {
		case flags.nSidTty:
			// no session leaders and no unlinked terminals
			if p.Sid == p.Pid || p.Ctty == "?" {
				continue
			}

		case flags.x:
			// print only process with same eUID of caller
			if eUID != p.uid {
				continue
			}

		case flags.all:
			// pass, print all

		default:
			// default for no flags only same session
			// and same uid process
			if p.Sid != pT.mProc.Sid || eUID != p.uid {
				continue
			}
		}

		pT.PrintProcess(index, w)
	}

	return nil

}

func usage() {
	log.Printf("Uage: ps [flags] [aux]")
	flag.Usage()
	os.Exit(1)
}

func main() {
	flag.Parse()
	// The original ps was designed before many flag conventions existed.
	// It had switchwes not needing a -. Try to emulate that.
	// It's pretty awful, however :-)
	for _, a := range flag.Args() {
		switch a {
		case "aux":
			flags.all, flags.aux = true, true
		default:
			usage()
		}
	}
	pT := NewProcessTable()
	if err := pT.LoadTable(); err != nil {
		log.Fatal(err)
	}

	err := ps(pT, os.Stderr)
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package cuckoo

import (
	"log"
	"math/rand"
	"os"
)

type Comparable interface {
	Compare(other Comparable) int
}

type BucketLocation struct {
	Bucket1 int
	Bucket2 int
}

type Bucket struct {
	// `entries` and `filled` must be the same size
	data      []Comparable     // Stores actual data. Validity of an entry determined by `filled`
	bucketLoc []BucketLocation // Stores the 2 bucket locations for each entry
	filled    []bool           // False if cell is empty. Only read `t.entries[i]` if `t.filled[i]==true`
}

type Table struct {
	log        *log.Logger
	name       string
	numBuckets int // Number of buckets
	depth      int // Capacity of each bucket
	rand       *rand.Rand

	buckets []*Bucket // Data
}

// Creates a brand new cuckoo table
// Two cuckoo tables will have identical state iff,
// 1. the same randSeed is used
// 2. the same operations are applied in the same order
// numBuckets = number of buckets
// depth = the number of entries per bucket
// randSeed = seed for PRNG
func NewTable(name string, numBuckets int, depth int, randSeed int64) *Table {
	t := &Table{}
	t.log = log.New(os.Stdout, "[Cuckoo:"+name+"] ", log.Ldate|log.Ltime|log.Lshortfile)
	t.name = name
	t.numBuckets = numBuckets
	t.depth = depth
	t.rand = rand.New(rand.NewSource(randSeed))

	t.buckets = make([]*Bucket, numBuckets)
	for i := 0; i < numBuckets; i++ {
		t.buckets[i] = &Bucket{}
		t.buckets[i].data = make([]Comparable, depth)
		t.buckets[i].bucketLoc = make([]BucketLocation, depth)
		// We assume this will be filled with `false` as per bool's default value
		t.buckets[i].filled = make([]bool, depth)
	}
	return t
}

/********************
 * PUBLIC METHODS
 ********************/

// Checks if value exists in specified buckets
// Returns true if `value.Equals(...)` returns true
func (t *Table) Contains(bucket1 int, bucket2 int, value Comparable) bool {
	result := false
	if bucket1 < t.numBuckets {
		result = result || t.isInBucket(bucket1, value)
	}
	if bucket2 < t.numBuckets {
		result = result || t.isInBucket(bucket2, value)
	}
	return result
}

/**
// Inserts the value into the cuckoo table
//   fails if either bucket already contains value
// Returns true on success, false if not inserted
// Even if false is returned, the underlying data structure might be different (e.g. rebuilt)
func (t *Table) Insert(bucket1, bucket2, value Comparable) bool {
	coin := rand.Int31()
	ok := t.tryInsertToBucket(e.Bucket1, e)
	if ok {
		return true
	}
	ok = t.tryInsertToBucket(e.Bucket2, e)
	if ok {
		return true
	}
	// @todo Evict

}

// Removes the entry from the cuckoo table
func (t *Table) Remove(bucket1 int, bucket2 int, target Comparable) {
	t.removeFromBucket(target.Bucket1, target)
	t.removeFromBucket(target.Bucket2, target)
}
**/

/********************
 * PRIVATE METHODS
 ********************/

// Checks if the `value` is in a specified bucket
// Returns true if `value.Equals(...)` returns true
func (t *Table) isInBucket(bucketIndex int, value Comparable) bool {
	bucket := t.buckets[bucketIndex]
	for i := 0; i < t.depth; i++ {
		if bucket.filled[i] && value.Compare(bucket.data[i]) == 0 {
			return true
		}
	}
	return false
}

/**
// Tries to inserts `target` into specified bucket
// bucketIndex must be either `target.Bucket1` or `target.Bucket2` or nothing happens
// If the bucket is already full, skip
// Returns true if success, false if bucket already full
func (t *Table) tryInsertToBucket(bucketIndex int, target *Entry) bool {
	// Assert bucketIndex is part of `target`
	if target.Bucket1 != bucketIndex && target.Bucket2 != bucketIndex {
		return false
	}

	// Search for an empty slot
	bucket := t.buckets[bucketIndex]
	for i, filled := range bucket.filled {
		if !filled {
			bucket.filled[i] = true
			bucket.entries[i] = target
			return true
		}
	}

	return false
}

func (t *Table) evictAndInsert(bucketIndex int, target *Entry) *Entry {
}

// Removes all copies of `target` from the specified bucket
// `target` matches against any entry where all fields match
func (t *Table) removeFromBucket(bucketIndex int, target *Entry) bool {

}
**/
<commit_msg>first attempt at eviction loop<commit_after>package cuckoo

import (
	"log"
	"math/rand"
	"os"
)

const MAX_EVICTIONS int = 500

type Comparable interface {
	Compare(other Comparable) int
}

type BucketLocation struct {
	Bucket1 int
	Bucket2 int
}

type Bucket struct {
	// `entries` and `filled` must be the same size
	data      []Comparable     // Stores actual data. Validity of an entry determined by `filled`
	bucketLoc []BucketLocation // Stores the 2 bucket locations for each entry
	filled    []bool           // False if cell is empty. Only read `t.entries[i]` if `t.filled[i]==true`
}

type Table struct {
	log        *log.Logger
	name       string
	numBuckets int // Number of buckets
	depth      int // Capacity of each bucket
	rand       *rand.Rand

	buckets []*Bucket // Data
}

// Creates a brand new cuckoo table
// Two cuckoo tables will have identical state iff,
// 1. the same randSeed is used
// 2. the same operations are applied in the same order
// numBuckets = number of buckets
// depth = the number of entries per bucket
// randSeed = seed for PRNG
func NewTable(name string, numBuckets int, depth int, randSeed int64) *Table {
	t := &Table{}
	t.log = log.New(os.Stdout, "[Cuckoo:"+name+"] ", log.Ldate|log.Ltime|log.Lshortfile)
	t.name = name
	t.numBuckets = numBuckets
	t.depth = depth
	t.rand = rand.New(rand.NewSource(randSeed))

	t.buckets = make([]*Bucket, numBuckets)
	for i := 0; i < numBuckets; i++ {
		t.buckets[i] = &Bucket{}
		t.buckets[i].data = make([]Comparable, depth)
		t.buckets[i].bucketLoc = make([]BucketLocation, depth)
		// We assume this will be filled with `false` as per bool's default value
		t.buckets[i].filled = make([]bool, depth)
	}
	return t
}

/********************
 * PUBLIC METHODS
 ********************/

// Checks if value exists in specified buckets
// Returns true if `value.Equals(...)` returns true
func (t *Table) Contains(bucket1 int, bucket2 int, value Comparable) bool {
	result := false
	if bucket1 < t.numBuckets {
		result = result || t.isInBucket(bucket1, value)
	}
	if bucket2 < t.numBuckets {
		result = result || t.isInBucket(bucket2, value)
	}
	return result
}

// Inserts the value into the cuckoo table
// Returns true on success, false on failure
//   fails if insertion cannot complete because reached MAX_EVICTIONS
//	 failures return an evicted value and must trigger a rebuild by the caller
func (t *Table) Insert(bucket1 int, bucket2 int, value Comparable) (int, int, Comparable, bool) {
	var ok bool
	var nextBucket int
	currBucketLoc := BucketLocation{bucket1, bucket2}
	currVal := value

	// Randomly select 1 bucket first
	coin := t.rand.Int() % 2 // Coin could be -1, 0 or 1
	if coin == 0 {
		ok = t.tryInsertToBucket(bucket1, currBucketLoc, currVal)
		nextBucket = bucket2
	} else {
		ok = t.tryInsertToBucket(bucket2, currBucketLoc, currVal)
		nextBucket = bucket1
	}
	if ok {
		return -1, -1, nil, true
	}

	// Then try the other bucket, starting the eviction loop
	for i := 0; i < MAX_EVICTIONS; i++ {
		nextBucket, currBucketLoc, currVal, ok = t.insertAndEvict(nextBucket, currBucketLoc, currVal)
		if ok {
			return -1, -1, nil, true
		}
	}

	return currBucketLoc.Bucket1, currBucketLoc.Bucket2, currVal, false
}

/**
// Removes the entry from the cuckoo table
func (t *Table) Remove(bucket1 int, bucket2 int, target Comparable) {
	t.removeFromBucket(target.Bucket1, target)
	t.removeFromBucket(target.Bucket2, target)
}
**/

/********************
 * PRIVATE METHODS
 ********************/

// Checks if the `value` is in a specified bucket
// Returns true if `value.Equals(...)` returns true
func (t *Table) isInBucket(bucketIndex int, value Comparable) bool {
	bucket := t.buckets[bucketIndex]
	for i := 0; i < t.depth; i++ {
		if bucket.filled[i] && value.Compare(bucket.data[i]) == 0 {
			return true
		}
	}
	return false
}

// Tries to inserts `bucketLoc, value` into specified bucket
// If the bucket is already full, skip
// Returns true if success, false if bucket already full
func (t *Table) tryInsertToBucket(bucketIndex int, bucketLoc BucketLocation, value Comparable) bool {
	// Search for an empty slot
	bucket := t.buckets[bucketIndex]
	for i := 0; i < t.depth; i++ {
		if !bucket.filled[i] {
			bucket.data[i] = value
			bucket.bucketLoc[i] = bucketLoc
			bucket.filled[i] = true
			return true
		}
	}

	return false
}

// Tries to insert `bucketLoc, value` into specified bucket
// Returns -1, BucketLocation{}, nil, true if there's empty space and succeeds
// Returns false if insertion triggered an eviction
//   other values contain the evicted item's alternate bucket, BucketLocation pair, and value
func (t *Table) insertAndEvict(bucketIndex int, bucketLoc BucketLocation, value Comparable) (int, BucketLocation, Comparable, bool) {
	ok := t.tryInsertToBucket(bucketIndex, bucketLoc, value)
	if ok {
		return -1, BucketLocation{}, nil, true
	}

	// Eviction
	randIndex := t.rand.Int() % t.depth
	if randIndex < 0 {
		randIndex *= -1
	}
	bucket := t.buckets[bucketIndex]
	evictedBucketLoc := bucket.bucketLoc[randIndex]
	evictedValue := bucket.data[randIndex]
	bucket.bucketLoc[randIndex] = bucketLoc
	bucket.data[randIndex] = value
	bucket.filled[randIndex] = true
	if bucketIndex == evictedBucketLoc.Bucket1 {
		return evictedBucketLoc.Bucket2, evictedBucketLoc, evictedValue, false
	} else if bucketIndex == evictedBucketLoc.Bucket2 {
		return evictedBucketLoc.Bucket1, evictedBucketLoc, evictedValue, false
	} else {
		t.log.Fatalf("insertAndEvict: misplaced value! bucketIndex=%v does not match %v", bucketIndex, evictedBucketLoc)
		return -1, BucketLocation{}, nil, false
	}
}

/**
// Removes all copies of `target` from the specified bucket
// `target` matches against any entry where all fields match
func (t *Table) removeFromBucket(bucketIndex int, target *Entry) bool {

}
**/
<|endoftext|>


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

package exec

import (
	"errors"
	"os"
	"strings"
)

// ErrNotFound is the error resulting if a path search failed to find an executable file.
var ErrNotFound = errors.New("executable file not found in %PATH%")

func chkStat(file string) error {
	d, err := os.Stat(file)
	if err != nil {
		return err
	}
	if d.IsDir() {
		return os.EPERM
	}
	return nil
}

func findExecutable(file string, exts []string) (string, error) {
	if len(exts) == 0 {
		return file, chkStat(file)
	}
	f := strings.ToLower(file)
	for _, e := range exts {
		if strings.HasSuffix(f, e) {
			return file, chkStat(file)
		}
	}
	for _, e := range exts {
		if f := file + e; chkStat(f) == nil {
			return f, nil
		}
	}
	return ``, os.ENOENT
}

func LookPath(file string) (f string, err error) {
	x := os.Getenv(`PATHEXT`)
	if x == `` {
		x = `.COM;.EXE;.BAT;.CMD`
	}
	exts := []string{}
	for _, e := range strings.Split(strings.ToLower(x), `;`) {
		if e == "" {
			continue
		}
		if e[0] != '.' {
			e = "." + e
		}
		exts = append(exts, e)
	}
	if strings.IndexAny(file, `:\/`) != -1 {
		if f, err = findExecutable(file, exts); err == nil {
			return
		}
		return ``, &Error{file, err}
	}
	if pathenv := os.Getenv(`PATH`); pathenv == `` {
		if f, err = findExecutable(`.\`+file, exts); err == nil {
			return
		}
	} else {
		for _, dir := range strings.Split(pathenv, `;`) {
			if f, err = findExecutable(dir+`\`+file, exts); err == nil {
				return
			}
		}
	}
	return ``, &Error{file, ErrNotFound}
}
<commit_msg>os/exec: make LookPath always search the current directory under Windows.<commit_after>// Copyright 2010 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package exec

import (
	"errors"
	"os"
	"strings"
)

// ErrNotFound is the error resulting if a path search failed to find an executable file.
var ErrNotFound = errors.New("executable file not found in %PATH%")

func chkStat(file string) error {
	d, err := os.Stat(file)
	if err != nil {
		return err
	}
	if d.IsDir() {
		return os.EPERM
	}
	return nil
}

func findExecutable(file string, exts []string) (string, error) {
	if len(exts) == 0 {
		return file, chkStat(file)
	}
	f := strings.ToLower(file)
	for _, e := range exts {
		if strings.HasSuffix(f, e) {
			return file, chkStat(file)
		}
	}
	for _, e := range exts {
		if f := file + e; chkStat(f) == nil {
			return f, nil
		}
	}
	return ``, os.ENOENT
}

func LookPath(file string) (f string, err error) {
	x := os.Getenv(`PATHEXT`)
	if x == `` {
		x = `.COM;.EXE;.BAT;.CMD`
	}
	exts := []string{}
	for _, e := range strings.Split(strings.ToLower(x), `;`) {
		if e == "" {
			continue
		}
		if e[0] != '.' {
			e = "." + e
		}
		exts = append(exts, e)
	}
	if strings.IndexAny(file, `:\/`) != -1 {
		if f, err = findExecutable(file, exts); err == nil {
			return
		}
		return ``, &Error{file, err}
	}
	if f, err = findExecutable(`.\`+file, exts); err == nil {
		return
	}
	if pathenv := os.Getenv(`PATH`); pathenv != `` {
		for _, dir := range strings.Split(pathenv, `;`) {
			if f, err = findExecutable(dir+`\`+file, exts); err == nil {
				return
			}
		}
	}
	return ``, &Error{file, ErrNotFound}
}
<|endoftext|>


<commit_before>package main

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

	"github.com/appc/spec/schema/types"
	"github.com/blablacar/dgr/dgr/common"
	"github.com/n0rad/go-erlog/errs"
	"github.com/n0rad/go-erlog/logs"
	"gopkg.in/yaml.v2"
)

const pathTestsTarget = "/tests-target"
const pathTestsResult = "/tests-result"
const mountAcname = "test-result"
const statusSuffix = "_status"
const fileEndOfTests = "end-of-tests"

func (aci *Aci) Test() error {
	defer aci.giveBackUserRightsToTarget()
	hashAcis, err := aci.Install()
	if err != nil {
		return err
	}

	logs.WithF(aci.fields).Info("Testing")

	ImportInternalTesterIfNeeded(aci.manifest)

	logs.WithF(aci.fields).Info("Building test aci")
	hashTestAci, err := aci.buildTestAci()
	if err != nil {
		return err
	}

	logs.WithF(aci.fields).Info("Running test aci")
	if err := aci.runTestAci(hashTestAci, hashAcis); err != nil {
		return err
	}

	logs.WithF(aci.fields).Info("Checking result")
	if err := aci.checkResult(); err != nil {
		return err
	}
	return nil
}

func (aci *Aci) checkResult() error {
	files, err := ioutil.ReadDir(aci.target + pathTestsResult)
	if err != nil {
		return errs.WithEF(err, aci.fields, "Cannot read test result directory")
	}
	testFound := false
	getToTheEnd := false
	for _, f := range files {
		if f.Name() == fileEndOfTests {
			getToTheEnd = true
			continue
		}
		testFields := aci.fields.WithField("file", f.Name())
		fullPath := aci.target + pathTestsResult + "/" + f.Name()
		content, err := ioutil.ReadFile(fullPath)
		if err != nil {
			return errs.WithEF(err, testFields, "Cannot read result file")
		}
		if !strings.HasSuffix(f.Name(), statusSuffix) {
			if testFound == false && string(content) != "1..0\n" {
				testFound = true
			}
			continue
		}
		if string(content) != "0\n" {
			return errs.WithEF(err, testFields, "Failed test")
		}
	}
	if !getToTheEnd {
		return errs.WithF(aci.fields, "Something goes wrong while running tests")
	}

	if Args.NoTestFail && !testFound {
		return errs.WithEF(err, aci.fields, "No tests found")
	}
	return nil
}

func (aci *Aci) runTestAci(testerHash string, hashAcis []string) error {
	os.MkdirAll(aci.target+pathTestsResult, 0777)

	defer aci.cleanupTest(testerHash, hashAcis)
	if err := Home.Rkt.Run([]string{"--set-env=" + common.EnvLogLevel + "=" + logs.GetLevel().String(),
		"--net=default",
		"--mds-register=false",
		"--uuid-file-save=" + aci.target + pathTesterUuid,
		"--volume=" + mountAcname + ",kind=host,source=" + aci.target + pathTestsResult,
		testerHash,
		"--exec", "/test",
	}); err != nil {
		return errs.WithEF(err, aci.fields, "Run of test aci failed")
	}
	return nil
}

func (aci *Aci) cleanupTest(testerHash string, hashAcis []string) {
	if !Args.KeepBuilder {
		if _, _, err := Home.Rkt.RmFromFile(aci.target + pathTesterUuid); err != nil {
			logs.WithEF(err, aci.fields).Warn("Failed to remove test container")
		}
	}

	for _, hash := range hashAcis {
		if err := Home.Rkt.ImageRm(hash); err != nil {
			logs.WithEF(err, aci.fields.WithField("hash", hash)).Warn("Failed to remove container image")
		}
	}

	if err := Home.Rkt.ImageRm(testerHash); err != nil {
		logs.WithEF(err, aci.fields.WithField("hash", testerHash)).Warn("Failed to remove test container image")
	}
}

func (aci *Aci) buildTestAci() (string, error) {
	manifest, err := common.ExtractManifestFromAci(aci.target + common.PathImageAci)
	if err != nil {
		return "", errs.WithEF(err, aci.fields.WithField("file", aci.target+common.PathImageAci), "Failed to extract manifest from aci")
	}

	name := prefixTest + manifest.Name.String()
	if version, ok := manifest.Labels.Get("version"); ok {
		name += ":" + version
	}

	fullname := common.NewACFullName(name)
	resultMountName, _ := types.NewACName(mountAcname)

	isolators := aci.manifest.Tester.Aci.App.Isolators

	if len(isolators) == 0 {
		isolators = aci.manifest.Aci.App.Isolators
	}

	aciManifest := &common.AciManifest{
		Builder: aci.manifest.Tester.Builder,
		Aci: common.AciDefinition{
			App: common.DgrApp{
				Exec:              aci.manifest.Aci.App.Exec,
				MountPoints:       []types.MountPoint{{Path: pathTestsResult, Name: *resultMountName}},
				WorkingDirectory:  aci.manifest.Aci.App.WorkingDirectory,
				User:              aci.manifest.Aci.App.User,
				Group:             aci.manifest.Aci.App.Group,
				SupplementaryGIDs: aci.manifest.Aci.App.SupplementaryGIDs,
				Environment:       aci.manifest.Aci.App.Environment,
				Ports:             aci.manifest.Aci.App.Ports,
				Isolators:         isolators,
			},
			Dependencies:  append(aci.manifest.Tester.Aci.Dependencies, *common.NewACFullName(name[len(prefixTest):])),
			Annotations:   aci.manifest.Aci.Annotations,
			PathWhitelist: aci.manifest.Aci.PathWhitelist,
		},
		NameAndVersion: *fullname,
	}

	content, err := yaml.Marshal(aciManifest)
	if err != nil {
		return "", errs.WithEF(err, aci.fields, "Failed to marshall manifest for test aci")
	}

	testAci, err := NewAciWithManifest(aci.path, aci.args, string(content), aci.checkWg)
	if err != nil {
		return "", errs.WithEF(err, aci.fields, "Failed to prepare test's build aci")
	}

	testAci.FullyResolveDep = false // this is required to run local tests without discovery
	testAci.target = aci.target + pathTestsTarget

	if err := testAci.CleanAndBuild(); err != nil {
		return "", errs.WithEF(err, aci.fields, "Build of test aci failed")
	}
	hash, err := Home.Rkt.Fetch(aci.target + pathTestsTarget + pathImageAci)
	if err != nil {
		return "", errs.WithEF(err, aci.fields, "fetch of test aci failed")
	}
	return hash, nil
}
<commit_msg>Add environment variable when running dgr test<commit_after>package main

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

	"github.com/appc/spec/schema/types"
	"github.com/blablacar/dgr/dgr/common"
	"github.com/n0rad/go-erlog/errs"
	"github.com/n0rad/go-erlog/logs"
	"gopkg.in/yaml.v2"
)

const pathTestsTarget = "/tests-target"
const pathTestsResult = "/tests-result"
const mountAcname = "test-result"
const statusSuffix = "_status"
const fileEndOfTests = "end-of-tests"

func (aci *Aci) Test() error {
	defer aci.giveBackUserRightsToTarget()
	hashAcis, err := aci.Install()
	if err != nil {
		return err
	}

	logs.WithF(aci.fields).Info("Testing")

	ImportInternalTesterIfNeeded(aci.manifest)

	logs.WithF(aci.fields).Info("Building test aci")
	hashTestAci, err := aci.buildTestAci()
	if err != nil {
		return err
	}

	logs.WithF(aci.fields).Info("Running test aci")
	if err := aci.runTestAci(hashTestAci, hashAcis); err != nil {
		return err
	}

	logs.WithF(aci.fields).Info("Checking result")
	if err := aci.checkResult(); err != nil {
		return err
	}
	return nil
}

func (aci *Aci) checkResult() error {
	files, err := ioutil.ReadDir(aci.target + pathTestsResult)
	if err != nil {
		return errs.WithEF(err, aci.fields, "Cannot read test result directory")
	}
	testFound := false
	getToTheEnd := false
	for _, f := range files {
		if f.Name() == fileEndOfTests {
			getToTheEnd = true
			continue
		}
		testFields := aci.fields.WithField("file", f.Name())
		fullPath := aci.target + pathTestsResult + "/" + f.Name()
		content, err := ioutil.ReadFile(fullPath)
		if err != nil {
			return errs.WithEF(err, testFields, "Cannot read result file")
		}
		if !strings.HasSuffix(f.Name(), statusSuffix) {
			if testFound == false && string(content) != "1..0\n" {
				testFound = true
			}
			continue
		}
		if string(content) != "0\n" {
			return errs.WithEF(err, testFields, "Failed test")
		}
	}
	if !getToTheEnd {
		return errs.WithF(aci.fields, "Something goes wrong while running tests")
	}

	if Args.NoTestFail && !testFound {
		return errs.WithEF(err, aci.fields, "No tests found")
	}
	return nil
}

func (aci *Aci) runTestAci(testerHash string, hashAcis []string) error {
	os.MkdirAll(aci.target+pathTestsResult, 0777)

	defer aci.cleanupTest(testerHash, hashAcis)
	if err := Home.Rkt.Run([]string{"--set-env=" + common.EnvLogLevel + "=" + logs.GetLevel().String(),
		"--set-env=TESTER=TRUE",
		"--net=default",
		"--mds-register=false",
		"--uuid-file-save=" + aci.target + pathTesterUuid,
		"--volume=" + mountAcname + ",kind=host,source=" + aci.target + pathTestsResult,
		testerHash,
		"--exec", "/test",
	}); err != nil {
		return errs.WithEF(err, aci.fields, "Run of test aci failed")
	}
	return nil
}

func (aci *Aci) cleanupTest(testerHash string, hashAcis []string) {
	if !Args.KeepBuilder {
		if _, _, err := Home.Rkt.RmFromFile(aci.target + pathTesterUuid); err != nil {
			logs.WithEF(err, aci.fields).Warn("Failed to remove test container")
		}
	}

	for _, hash := range hashAcis {
		if err := Home.Rkt.ImageRm(hash); err != nil {
			logs.WithEF(err, aci.fields.WithField("hash", hash)).Warn("Failed to remove container image")
		}
	}

	if err := Home.Rkt.ImageRm(testerHash); err != nil {
		logs.WithEF(err, aci.fields.WithField("hash", testerHash)).Warn("Failed to remove test container image")
	}
}

func (aci *Aci) buildTestAci() (string, error) {
	manifest, err := common.ExtractManifestFromAci(aci.target + common.PathImageAci)
	if err != nil {
		return "", errs.WithEF(err, aci.fields.WithField("file", aci.target+common.PathImageAci), "Failed to extract manifest from aci")
	}

	name := prefixTest + manifest.Name.String()
	if version, ok := manifest.Labels.Get("version"); ok {
		name += ":" + version
	}

	fullname := common.NewACFullName(name)
	resultMountName, _ := types.NewACName(mountAcname)

	isolators := aci.manifest.Tester.Aci.App.Isolators

	if len(isolators) == 0 {
		isolators = aci.manifest.Aci.App.Isolators
	}

	aciManifest := &common.AciManifest{
		Builder: aci.manifest.Tester.Builder,
		Aci: common.AciDefinition{
			App: common.DgrApp{
				Exec:              aci.manifest.Aci.App.Exec,
				MountPoints:       []types.MountPoint{{Path: pathTestsResult, Name: *resultMountName}},
				WorkingDirectory:  aci.manifest.Aci.App.WorkingDirectory,
				User:              aci.manifest.Aci.App.User,
				Group:             aci.manifest.Aci.App.Group,
				SupplementaryGIDs: aci.manifest.Aci.App.SupplementaryGIDs,
				Environment:       aci.manifest.Aci.App.Environment,
				Ports:             aci.manifest.Aci.App.Ports,
				Isolators:         isolators,
			},
			Dependencies:  append(aci.manifest.Tester.Aci.Dependencies, *common.NewACFullName(name[len(prefixTest):])),
			Annotations:   aci.manifest.Aci.Annotations,
			PathWhitelist: aci.manifest.Aci.PathWhitelist,
		},
		NameAndVersion: *fullname,
	}

	content, err := yaml.Marshal(aciManifest)
	if err != nil {
		return "", errs.WithEF(err, aci.fields, "Failed to marshall manifest for test aci")
	}

	testAci, err := NewAciWithManifest(aci.path, aci.args, string(content), aci.checkWg)
	if err != nil {
		return "", errs.WithEF(err, aci.fields, "Failed to prepare test's build aci")
	}

	testAci.FullyResolveDep = false // this is required to run local tests without discovery
	testAci.target = aci.target + pathTestsTarget

	if err := testAci.CleanAndBuild(); err != nil {
		return "", errs.WithEF(err, aci.fields, "Build of test aci failed")
	}
	hash, err := Home.Rkt.Fetch(aci.target + pathTestsTarget + pathImageAci)
	if err != nil {
		return "", errs.WithEF(err, aci.fields, "fetch of test aci failed")
	}
	return hash, nil
}
<|endoftext|>


<commit_before>package discordgo

import (
	"os"
	"runtime"
	"testing"
	"time"
)

//////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////// VARS NEEDED FOR TESTING
var (
	dg *Session // Stores global discordgo session

	envToken    string = os.Getenv("DG_TOKEN")    // Token to use when authenticating
	envEmail    string = os.Getenv("DG_EMAIL")    // Email to use when authenticating
	envPassword string = os.Getenv("DG_PASSWORD") // Password to use when authenticating
	envGuild    string = os.Getenv("DG_GUILD")    // Guild ID to use for tests
	envChannel  string = os.Getenv("DG_CHANNEL")  // Channel ID to use for tests
	envUser     string = os.Getenv("DG_USER")     // User ID to use for tests
	envAdmin    string = os.Getenv("DG_ADMIN")    // User ID of admin user to use for tests
)

func init() {
	if envEmail == "" || envPassword == "" || envToken == "" {
		return
	}

	if d, err := New(envEmail, envPassword, envToken); err == nil {
		dg = d
	}
}

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////// HELPER FUNCTIONS USED FOR TESTING

// This waits x time for the check bool to be the want bool
func waitBoolEqual(timeout time.Duration, check *bool, want bool) bool {

	start := time.Now()
	for {
		if *check == want {
			return true
		}

		if time.Since(start) > timeout {
			return false
		}

		runtime.Gosched()
	}
}

// Checks if we're connected to Discord
func isConnected() bool {

	if dg == nil {
		return false
	}

	if dg.Token == "" {
		return false
	}

	// Need a way to see if the ws connection is nil

	if !waitBoolEqual(10*time.Second, &dg.DataReady, true) {
		return false
	}

	return true
}

//////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////// START OF TESTS

// TestNew tests the New() function without any arguments.  This should return
// a valid Session{} struct and no errors.
func TestNew(t *testing.T) {

	_, err := New()
	if err != nil {
		t.Errorf("New() returned error: %+v", err)
	}
}

// TestInvalidToken tests the New() function with an invalid token
func TestInvalidToken(t *testing.T) {
	_, err := New("asjkldhflkjasdh")
	if err != nil {
		t.Fatalf("New(InvalidToken) returned error: %+v", err)
	}
}

// TestInvalidUserPass tests the New() function with an invalid Email and Pass
func TestInvalidEmailPass(t *testing.T) {

	_, err := New("invalidemail", "invalidpassword")
	if err == nil {
		t.Errorf("New(InvalidEmail, InvalidPass) returned nil error.")
	}

}

// TestInvalidPass tests the New() function with an invalid Password
func TestInvalidPass(t *testing.T) {

	if envEmail == "" {
		t.Skip("Skipping New(username,InvalidPass), DG_EMAIL not set")
		return
	}
	_, err := New(envEmail, "invalidpassword")
	if err == nil {
		t.Errorf("New(Email, InvalidPass) returned nil error.")
	}
}

// TestNewUserPass tests the New() function with a username and password.
// This should return a valid Session{}, a valid Session.Token, and open
// a websocket connection to Discord.
func TestNewUserPass(t *testing.T) {

	if envEmail == "" || envPassword == "" {
		t.Skip("Skipping New(username,password), DG_EMAIL or DG_PASSWORD not set")
		return
	}

	d, err := New(envEmail, envPassword)
	if err != nil {
		t.Fatalf("New(user,pass) returned error: %+v", err)
	}

	if d == nil {
		t.Fatal("New(user,pass), d is nil, should be Session{}")
	}

	if d.Token == "" {
		t.Fatal("New(user,pass), d.Token is empty, should be a valid Token.")
	}
}

// TestNewToken tests the New() function with a Token.  This should return
// the same as the TestNewUserPass function.
func TestNewToken(t *testing.T) {

	if envToken == "" {
		t.Skip("Skipping New(token), DG_TOKEN not set")
	}

	d, err := New(envToken)
	if err != nil {
		t.Fatalf("New(envToken) returned error: %+v", err)
	}

	if d == nil {
		t.Fatal("New(envToken), d is nil, should be Session{}")
	}

	if d.Token == "" {
		t.Fatal("New(envToken), d.Token is empty, should be a valid Token.")
	}
}

func TestOpenClose(t *testing.T) {
	if envToken == "" {
		t.Skip("Skipping TestClose, DG_TOKEN not set")
	}

	d, err := New(envToken)
	if err != nil {
		t.Fatalf("TestClose, New(envToken) returned error: %+v", err)
	}

	if err = d.OpenAndListen(); err != nil {
		t.Fatalf("TestClose, d.OpenAndListen failed: %+v", err)
	}

	if err = d.Close(); err != nil {
		t.Fatalf("TestClose, d.Close failed: %+v", err)
	}
}
<commit_msg>Make InvalidToken test useful again.<commit_after>package discordgo

import (
	"os"
	"runtime"
	"testing"
	"time"
)

//////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////// VARS NEEDED FOR TESTING
var (
	dg *Session // Stores global discordgo session

	envToken    string = os.Getenv("DG_TOKEN")    // Token to use when authenticating
	envEmail    string = os.Getenv("DG_EMAIL")    // Email to use when authenticating
	envPassword string = os.Getenv("DG_PASSWORD") // Password to use when authenticating
	envGuild    string = os.Getenv("DG_GUILD")    // Guild ID to use for tests
	envChannel  string = os.Getenv("DG_CHANNEL")  // Channel ID to use for tests
	envUser     string = os.Getenv("DG_USER")     // User ID to use for tests
	envAdmin    string = os.Getenv("DG_ADMIN")    // User ID of admin user to use for tests
)

func init() {
	if envEmail == "" || envPassword == "" || envToken == "" {
		return
	}

	if d, err := New(envEmail, envPassword, envToken); err == nil {
		dg = d
	}
}

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////// HELPER FUNCTIONS USED FOR TESTING

// This waits x time for the check bool to be the want bool
func waitBoolEqual(timeout time.Duration, check *bool, want bool) bool {

	start := time.Now()
	for {
		if *check == want {
			return true
		}

		if time.Since(start) > timeout {
			return false
		}

		runtime.Gosched()
	}
}

// Checks if we're connected to Discord
func isConnected() bool {

	if dg == nil {
		return false
	}

	if dg.Token == "" {
		return false
	}

	// Need a way to see if the ws connection is nil

	if !waitBoolEqual(10*time.Second, &dg.DataReady, true) {
		return false
	}

	return true
}

//////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////// START OF TESTS

// TestNew tests the New() function without any arguments.  This should return
// a valid Session{} struct and no errors.
func TestNew(t *testing.T) {

	_, err := New()
	if err != nil {
		t.Errorf("New() returned error: %+v", err)
	}
}

// TestInvalidToken tests the New() function with an invalid token
func TestInvalidToken(t *testing.T) {
	d, err := New("asjkldhflkjasdh")
	if err != nil {
		t.Fatalf("New(InvalidToken) returned error: %+v", err)
	}

	// New with just a token does not do any communication, so attempt an api call.
	_, err = d.UserSettings()
	if err == nil {
		t.Errorf("New(InvalidToken), d.UserSettings returned nil error.")
	}
}

// TestInvalidUserPass tests the New() function with an invalid Email and Pass
func TestInvalidEmailPass(t *testing.T) {

	_, err := New("invalidemail", "invalidpassword")
	if err == nil {
		t.Errorf("New(InvalidEmail, InvalidPass) returned nil error.")
	}

}

// TestInvalidPass tests the New() function with an invalid Password
func TestInvalidPass(t *testing.T) {

	if envEmail == "" {
		t.Skip("Skipping New(username,InvalidPass), DG_EMAIL not set")
		return
	}
	_, err := New(envEmail, "invalidpassword")
	if err == nil {
		t.Errorf("New(Email, InvalidPass) returned nil error.")
	}
}

// TestNewUserPass tests the New() function with a username and password.
// This should return a valid Session{}, a valid Session.Token, and open
// a websocket connection to Discord.
func TestNewUserPass(t *testing.T) {

	if envEmail == "" || envPassword == "" {
		t.Skip("Skipping New(username,password), DG_EMAIL or DG_PASSWORD not set")
		return
	}

	d, err := New(envEmail, envPassword)
	if err != nil {
		t.Fatalf("New(user,pass) returned error: %+v", err)
	}

	if d == nil {
		t.Fatal("New(user,pass), d is nil, should be Session{}")
	}

	if d.Token == "" {
		t.Fatal("New(user,pass), d.Token is empty, should be a valid Token.")
	}
}

// TestNewToken tests the New() function with a Token.  This should return
// the same as the TestNewUserPass function.
func TestNewToken(t *testing.T) {

	if envToken == "" {
		t.Skip("Skipping New(token), DG_TOKEN not set")
	}

	d, err := New(envToken)
	if err != nil {
		t.Fatalf("New(envToken) returned error: %+v", err)
	}

	if d == nil {
		t.Fatal("New(envToken), d is nil, should be Session{}")
	}

	if d.Token == "" {
		t.Fatal("New(envToken), d.Token is empty, should be a valid Token.")
	}
}

func TestOpenClose(t *testing.T) {
	if envToken == "" {
		t.Skip("Skipping TestClose, DG_TOKEN not set")
	}

	d, err := New(envToken)
	if err != nil {
		t.Fatalf("TestClose, New(envToken) returned error: %+v", err)
	}

	if err = d.OpenAndListen(); err != nil {
		t.Fatalf("TestClose, d.OpenAndListen failed: %+v", err)
	}

	if err = d.Close(); err != nil {
		t.Fatalf("TestClose, d.Close failed: %+v", err)
	}
}
<|endoftext|>