Update to latest master (#1)

Update to latest master

Co-authored-by: Jesse Duffield <jessedduffield@gmail.com>
Co-authored-by: Jan Krüger <jan@krueger-jan.de>
Co-authored-by: jony montana <hy352144278@gmail.com>
Co-authored-by: null <callum.mcintyre@procensus.com>
Co-authored-by: Mikhail Swift <mswift@novetta.com>
Co-authored-by: Devin <devin@doolin.io>
This commit is contained in:
Mark Kopenga 2019-07-02 08:36:30 +02:00 committed by GitHub
commit ee53d2447c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
69 changed files with 2968 additions and 1965 deletions

32
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View file

@ -0,0 +1,32 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behaviour**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. Windows]
- Lazydocker Version [e.g. v0.1.45]
- The last commit id if you built project from sources (run : ```git-rev parse HEAD```)
**Additional context**
Add any other context about the problem here.

View file

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

5
.gitignore vendored
View file

@ -1,3 +1,4 @@
lazydocker*
TODO.md
Lazydocker.code-workspace
Lazydocker.code-workspace
.vscode

20
Gopkg.lock generated
View file

@ -113,6 +113,14 @@
revision = "7c29201646fa3de8506f701213473dd407f19646"
version = "v0.3.7"
[[projects]]
digest = "1:9cf73012dbe62f96cd8cdd210a1123a360401967a4530284dcd2990888747e3d"
name = "github.com/integrii/flaggy"
packages = ["."]
pruneopts = "NUT"
revision = "07ea7eb774047dab1bdcc7cad0f825fa419df47d"
version = "1.2.1"
[[projects]]
branch = "master"
digest = "1:7b2ab5c6de135d57d3a594b53068e2712da4d376b2f78097dec7b46d18deaa78"
@ -129,14 +137,6 @@
pruneopts = "NUT"
revision = "117e4fb79d29e3908cb7a7bed5e991a321fde7fd"
[[projects]]
branch = "master"
digest = "1:a46c2f4863e5284ddb255c28750298e04bc8c0fc896bed6056e947673168b7be"
name = "github.com/jesseduffield/pty"
packages = ["."]
pruneopts = "NUT"
revision = "02db52c7e406c7abec44c717a173c7715e4c1b62"
[[projects]]
branch = "master"
digest = "1:7429d1589cc1454e9e97ba48cb7c5025a085b32eae4d3163c517e3e5d6c7a7a0"
@ -151,7 +151,7 @@
name = "github.com/jesseduffield/rollrus"
packages = ["."]
pruneopts = "NUT"
revision = "7086c0b1b1bd21ef1f502e914ff2505a68c7d638"
revision = "dd028cb0bfd75da456151b389d1a32b68cc57360"
[[projects]]
branch = "master"
@ -327,9 +327,9 @@
"github.com/fatih/color",
"github.com/go-errors/errors",
"github.com/imdario/mergo",
"github.com/integrii/flaggy",
"github.com/jesseduffield/asciigraph",
"github.com/jesseduffield/gocui",
"github.com/jesseduffield/pty",
"github.com/jesseduffield/rollrus",
"github.com/jesseduffield/yaml",
"github.com/mcuadros/go-lookup",

View file

@ -2,7 +2,7 @@
<img src="https://user-images.githubusercontent.com/8456633/59972109-8e9c8480-95cc-11e9-8350-38f7f86ba76d.png">
</p>
A simple terminal UI for docker and docker-compose, written in Go with the [gocui](https://github.com/jroimartin/gocui 'gocui') library.
A simple terminal UI for both docker and docker-compose, written in Go with the [gocui](https://github.com/jroimartin/gocui 'gocui') library.
[![CircleCI](https://circleci.com/gh/jesseduffield/lazydocker.svg?style=svg)](https://circleci.com/gh/jesseduffield/lazydocker) [![Go Report Card](https://goreportcard.com/badge/github.com/jesseduffield/lazydocker)](https://goreportcard.com/report/github.com/jesseduffield/lazydocker) [![GolangCI](https://golangci.com/badges/github.com/jesseduffield/lazydocker.svg)](https://golangci.com) [![GoDoc](https://godoc.org/github.com/jesseduffield/lazydocker?status.svg)](http://godoc.org/github.com/jesseduffield/lazydocker) [![GitHub tag](https://img.shields.io/github/tag/jesseduffield/lazydocker.svg)]()
@ -48,12 +48,24 @@ required go version: 1.12
go get github.com/jesseduffield/lazydocker
```
### Arch Linux AUR
You can install lazydocker using your AUR package manager of choice or by running:
```sh
git clone https://aur.archlinux.org/lazydocker.git ~/lazydocker
cd ~/lazydocker
makepkg --install
```
A development version of the AUR package is also [available](https://aur.archlinux.org/lazydocker-git.git)
## Usage
Call `lazydocker` in your terminal. I personally use this a lot so I've made an alias for it like so:
```
echo "alias ld='lazydocker'" >> ~/.zshrc
echo "alias lzd='lazydocker'" >> ~/.zshrc
```
(you can substitute .zshrc for whatever rc file you're using)
@ -80,7 +92,7 @@ everything is one keypress away (or one click away! Mouse support FTW):
There is still a lot of work to go! Please check out the [contributing guide](CONTRIBUTING.md).
For contributor discussion about things not better discussed here in the repo, join the slack channel
[![Slack](/docs/resources/slack_rgb.png)](https://join.slack.com/t/lazydocker/shared_invite/enQtNDE3MjIwNTYyMDA0LTM3Yjk3NzdiYzhhNTA1YjM4Y2M4MWNmNDBkOTI0YTE4YjQ1ZmI2YWRhZTgwNjg2YzhhYjg3NDBlMmQyMTI5N2M)
[![Slack](/docs/resources/slack_rgb.png)](https://join.slack.com/t/lazydocker/shared_invite/enQtNjgwMjc0Njk3MzgwLTM0NThlMTZiZmNkNWJkY2VlYWYwZmY1NWYyYWViZmE0ZTcxMWZjMTFjNTU1ZTEwMDBiNWIxZTIxYzkwNDgyY2M)
## Donate

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,68 @@
# Lazydocker menu
## Status
<pre>
<kbd>e</kbd>: bearbeite lazydocker Konfiguration
<kbd>o</kbd>: öffne lazydocker Konfiguration
<kbd>[</kbd>: vorheriges Tab
<kbd>]</kbd>: nächstes tab
<kbd>m</kbd>: zeige Protokoll
<kbd>enter</kbd>: fokusieren aufs Hauptpanel
</pre>
## Containers
<pre>
<kbd>[</kbd>: vorheriges Tab
<kbd>]</kbd>: nächstes Tab
<kbd>d</kbd>: entfernen
<kbd>s</kbd>: anhalten
<kbd>r</kbd>: neustarten
<kbd>a</kbd>: anbinden
<kbd>D</kbd>: entferne verlassene Container
<kbd>m</kbd>: zeige Protokolle
<kbd>c</kbd>: führe vordefinierten benutzerdefinierten Befehl aus
<kbd>enter</kbd>: fokusieren aufs Hauptpanel
</pre>
## Services
<pre>
<kbd>d</kbd>: entferne Container
<kbd>s</kbd>: anhalten
<kbd>r</kbd>: neustarten
<kbd>a</kbd>: anbinden
<kbd>m</kbd>: zeige Protokolle
<kbd>[</kbd>: vorheriges Tab
<kbd>]</kbd>: nächstes Tab
<kbd>R</kbd>: zeige Neustartoptionen
<kbd>c</kbd>: führe vordefinierten benutzerdefinierten Befehl aus
<kbd>enter</kbd>: fokusieren aufs Hauptpanel
</pre>
## Images
<pre>
<kbd>[</kbd>: vorheriges Tab
<kbd>]</kbd>: nächstes Tab
<kbd>d</kbd>: entferne Image
<kbd>D</kbd>: entferne unbenutzte Images
<kbd>enter</kbd>: fokusieren aufs Hauptpanel
</pre>
## Volumes
<pre>
<kbd>[</kbd>: vorheriges Tab
<kbd>]</kbd>: nächstes Tab
<kbd>d</kbd>: entferne Volumen
<kbd>D</kbd>: entferne unbenutzte Volumen
<kbd>enter</kbd>: fokusieren aufs Hauptpanel
</pre>
## Main
<pre>
<kbd>esc</kbd>: zurück
</pre>

View file

@ -7,7 +7,6 @@
<kbd>o</kbd>: open lazydocker config
<kbd>[</kbd>: previous tab
<kbd>]</kbd>: next tab
<kbd></kbd>: next tab
<kbd>m</kbd>: view logs
<kbd>enter</kbd>: focus main panel
</pre>

View file

@ -3,67 +3,66 @@
## Status
<pre>
<kbd>e</kbd>: edit lazydocker config
<kbd>o</kbd>: open lazydocker config
<kbd>[</kbd>: previous tab
<kbd>]</kbd>: next tab
<kbd></kbd>: next tab
<kbd>m</kbd>: view logs
<kbd>enter</kbd>: focus main panel
<kbd>e</kbd>: verander lazydocker configuratie
<kbd>o</kbd>: open lazydocker configuratie
<kbd>[</kbd>: vorige tab
<kbd>]</kbd>: volgende tab
<kbd>m</kbd>: bekijk logs
<kbd>enter</kbd>: focus hooft panneel
</pre>
## Containers
<pre>
<kbd>[</kbd>: previous tab
<kbd>]</kbd>: next tab
<kbd>d</kbd>: remove
<kbd>[</kbd>: vorige tab
<kbd>]</kbd>: volgende tab
<kbd>d</kbd>: verwijder
<kbd>s</kbd>: stop
<kbd>r</kbd>: restart
<kbd>a</kbd>: attach
<kbd>D</kbd>: prune exited containers
<kbd>m</kbd>: view logs
<kbd>c</kbd>: run predefined custom command
<kbd>enter</kbd>: focus main panel
<kbd>r</kbd>: herstart
<kbd>a</kbd>: vastmaken
<kbd>D</kbd>: verwijder verlaten containers
<kbd>m</kbd>: bekijk logs
<kbd>c</kbd>: draai een vooraf bedacht aangepaste opdracht
<kbd>enter</kbd>: focus hooft panneel
</pre>
## Services
<pre>
<kbd>d</kbd>: remove containers
<kbd>d</kbd>: verwijder containers
<kbd>s</kbd>: stop
<kbd>r</kbd>: restart
<kbd>a</kbd>: attach
<kbd>m</kbd>: view logs
<kbd>[</kbd>: previous tab
<kbd>]</kbd>: next tab
<kbd>R</kbd>: view restart options
<kbd>c</kbd>: run predefined custom command
<kbd>enter</kbd>: focus main panel
<kbd>r</kbd>: herstart
<kbd>a</kbd>: vermaken
<kbd>m</kbd>: bekijk logs
<kbd>[</kbd>: vorige tab
<kbd>]</kbd>: volgende tab
<kbd>R</kbd>: bekijk herstart opties
<kbd>c</kbd>: draai een vooraf bedacht aangepaste opdracht
<kbd>enter</kbd>: focus hooft panneel
</pre>
## Images
<pre>
<kbd>[</kbd>: previous tab
<kbd>]</kbd>: next tab
<kbd>d</kbd>: remove image
<kbd>D</kbd>: prune unused images
<kbd>enter</kbd>: focus main panel
<kbd>[</kbd>: vorige tab
<kbd>]</kbd>: volgende tab
<kbd>d</kbd>: verwijder image
<kbd>D</kbd>: verwijder nietgebruikte images
<kbd>enter</kbd>: focus hooft panneel
</pre>
## Volumes
<pre>
<kbd>[</kbd>: previous tab
<kbd>]</kbd>: next tab
<kbd>d</kbd>: remove volume
<kbd>D</kbd>: prune unused volumes
<kbd>enter</kbd>: focus main panel
<kbd>[</kbd>: vorige tab
<kbd>]</kbd>: volgende tab
<kbd>d</kbd>: verwijder volume
<kbd>D</kbd>: verwijder nietgebruikte images
<kbd>enter</kbd>: focus hooft panneel
</pre>
## Main
<pre>
<kbd>esc</kbd>: return
<kbd>esc</kbd>: terug
</pre>

View file

@ -7,7 +7,6 @@
<kbd>o</kbd>: open lazydocker config
<kbd>[</kbd>: previous tab
<kbd>]</kbd>: next tab
<kbd></kbd>: next tab
<kbd>m</kbd>: view logs
<kbd>enter</kbd>: focus main panel
</pre>

31
main.go
View file

@ -2,7 +2,6 @@ package main
import (
"bytes"
"flag"
"fmt"
"log"
"os"
@ -10,6 +9,7 @@ import (
"github.com/docker/docker/client"
"github.com/go-errors/errors"
"github.com/integrii/flaggy"
"github.com/jesseduffield/lazydocker/pkg/app"
"github.com/jesseduffield/lazydocker/pkg/config"
"github.com/jesseduffield/yaml"
@ -21,29 +21,32 @@ var (
date string
buildSource = "unknown"
configFlag = flag.Bool("config", false, "Print the current default config")
debuggingFlag = flag.Bool("debug", false, "a boolean")
versionFlag = flag.Bool("v", false, "Print the current version")
configFlag = false
debuggingFlag = false
composeFiles []string
)
func main() {
flag.Parse()
if *versionFlag {
fmt.Printf("commit=%s, build date=%s, build source=%s, version=%s, os=%s, arch=%s\n", commit, date, buildSource, version, runtime.GOOS, runtime.GOARCH)
os.Exit(0)
}
if *configFlag {
flaggy.SetName("lazydocker")
flaggy.SetDescription("The lazier way to manage everything docker")
flaggy.DefaultParser.AdditionalHelpPrepend = "https://github.com/jesseduffield/lazydocker"
flaggy.Bool(&configFlag, "c", "config", "Print the current default config")
flaggy.Bool(&debuggingFlag, "d", "debug", "a boolean")
flaggy.StringSlice(&composeFiles, "f", "file", "Specify alternate compose files")
flaggy.SetVersion(fmt.Sprintf("commit=%s, build date=%s, build source=%s, version=%s, os=%s, arch=%s\n", commit, date, buildSource, version, runtime.GOOS, runtime.GOARCH))
flaggy.Parse()
if configFlag {
var buf bytes.Buffer
yaml.NewEncoder(&buf).Encode(config.GetDefaultConfig())
fmt.Printf("%v\n", buf.String())
os.Exit(0)
}
// for now we're always in debug mode so we're not passing *debuggingFlag
*debuggingFlag = true
appConfig, err := config.NewAppConfig("lazydocker", version, commit, date, buildSource, *debuggingFlag)
appConfig, err := config.NewAppConfig("lazydocker", version, commit, date, buildSource, debuggingFlag, composeFiles)
if err != nil {
log.Fatal(err.Error())
}

View file

@ -56,17 +56,6 @@ func (app *App) Run() error {
return err
}
// Close closes any resources
func (app *App) Close() error {
for _, closer := range app.closers {
err := closer.Close()
if err != nil {
return err
}
}
return nil
}
type errorMapping struct {
originalError string
newError string

View file

@ -1,100 +0,0 @@
// +build !windows
package commands
import (
"bufio"
"bytes"
"os"
"strings"
"unicode/utf8"
"github.com/go-errors/errors"
"github.com/jesseduffield/pty"
"github.com/mgutz/str"
)
// RunCommandWithOutputLiveWrapper runs a command and return every word that gets written in stdout
// Output is a function that executes by every word that gets read by bufio
// As return of output you need to give a string that will be written to stdin
// NOTE: If the return data is empty it won't written anything to stdin
func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(string) string) error {
splitCmd := str.ToArgv(command)
cmd := c.command(splitCmd[0], splitCmd[1:]...)
cmd.Env = os.Environ()
cmd.Env = append(cmd.Env, "LANG=en_US.UTF-8", "LC_ALL=en_US.UTF-8")
var stderr bytes.Buffer
cmd.Stderr = &stderr
ptmx, err := pty.Start(cmd)
if err != nil {
return err
}
go func() {
scanner := bufio.NewScanner(ptmx)
scanner.Split(scanWordsWithNewLines)
for scanner.Scan() {
toOutput := strings.Trim(scanner.Text(), " ")
_, _ = ptmx.WriteString(output(toOutput))
}
}()
err = cmd.Wait()
ptmx.Close()
if err != nil {
return errors.New(stderr.String())
}
return nil
}
// scanWordsWithNewLines is a copy of bufio.ScanWords but this also captures new lines
// For specific comments about this function take a look at: bufio.ScanWords
func scanWordsWithNewLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
start := 0
for width := 0; start < len(data); start += width {
var r rune
r, width = utf8.DecodeRune(data[start:])
if !isSpace(r) {
break
}
}
for width, i := 0, start; i < len(data); i += width {
var r rune
r, width = utf8.DecodeRune(data[i:])
if isSpace(r) {
return i + width, data[start:i], nil
}
}
if atEOF && len(data) > start {
return len(data), data[start:], nil
}
return start, nil, nil
}
// isSpace is also copied from the bufio package and has been modified to also captures new lines
// For specific comments about this function take a look at: bufio.isSpace
func isSpace(r rune) bool {
if r <= '\u00FF' {
switch r {
case ' ', '\t', '\v', '\f':
return true
case '\u0085', '\u00A0':
return true
}
return false
}
if '\u2000' <= r && r <= '\u200a' {
return true
}
switch r {
case '\u1680', '\u2028', '\u2029', '\u202f', '\u205f', '\u3000':
return true
}
return false
}

View file

@ -1,9 +0,0 @@
// +build windows
package commands
// RunCommandWithOutputLiveWrapper runs a command live but because of windows compatibility this command can't be ran there
// TODO: Remove this hack and replace it with a proper way to run commands live on windows
func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(string) string) error {
return c.RunCommand(command)
}

View file

@ -68,7 +68,7 @@ func (l *Layer) GetDisplayStrings(isFocused bool) []string {
createdBy = utils.ColoredString(split[0], color.FgYellow) + " " + strings.Join(split[1:], " ")
}
createdBy = strings.ReplaceAll(createdBy, "\t", " ")
createdBy = strings.Replace(createdBy, "\t", " ", -1)
size := utils.FormatBinaryBytes(int(l.Size))
sizeColor := color.FgWhite

View file

@ -6,7 +6,6 @@ import (
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"sync"
"syscall"
@ -87,36 +86,6 @@ func (c *OSCommand) ExecutableFromString(commandStr string) *exec.Cmd {
return c.command(splitCmd[0], splitCmd[1:]...)
}
// RunCommandWithOutputLive runs RunCommandWithOutputLiveWrapper
func (c *OSCommand) RunCommandWithOutputLive(command string, output func(string) string) error {
return RunCommandWithOutputLiveWrapper(c, command, output)
}
// DetectUnamePass detect a username / password question in a command
// ask is a function that gets executen when this function detect you need to fillin a password
// The ask argument will be "username" or "password" and expects the user's password or username back
func (c *OSCommand) DetectUnamePass(command string, ask func(string) string) error {
ttyText := ""
errMessage := c.RunCommandWithOutputLive(command, func(word string) string {
ttyText = ttyText + " " + word
prompts := map[string]string{
"password": `Password\s*for\s*'.+':`,
"username": `Username\s*for\s*'.+':`,
}
for askFor, pattern := range prompts {
if match, _ := regexp.MatchString(pattern, ttyText); match {
ttyText = ""
return ask(askFor)
}
}
return ""
})
return errMessage
}
// RunCommand runs a command and just returns the error
func (c *OSCommand) RunCommand(command string) error {
_, err := c.RunCommandWithOutput(command)

View file

@ -16,6 +16,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
yaml "github.com/jesseduffield/yaml"
@ -364,7 +365,7 @@ type AppConfig struct {
}
// NewAppConfig makes a new app config
func NewAppConfig(name, version, commit, date string, buildSource string, debuggingFlag bool) (*AppConfig, error) {
func NewAppConfig(name, version, commit, date string, buildSource string, debuggingFlag bool, composeFiles []string) (*AppConfig, error) {
configDir, err := findOrCreateConfigDir(name)
if err != nil {
return nil, err
@ -375,6 +376,11 @@ func NewAppConfig(name, version, commit, date string, buildSource string, debugg
return nil, err
}
// Pass compose files as individual -f flags to docker-compose
if len(composeFiles) > 0 {
userConfig.CommandTemplates.DockerCompose += " -f " + strings.Join(composeFiles, " -f ")
}
appConfig := &AppConfig{
Name: name,
Version: version,

View file

@ -0,0 +1,50 @@
package config
import (
"testing"
)
func TestDockerComposeCommandNoFiles(t *testing.T) {
composeFiles := []string{}
conf, err := NewAppConfig("name", "version", "commit", "date", "buildSource", false, composeFiles)
if err != nil {
t.Fatalf("Unexpected error: %s", err)
}
actual := conf.UserConfig.CommandTemplates.DockerCompose
expected := "docker-compose"
if actual != expected {
t.Fatalf("Expected %s but got %s", expected, actual)
}
}
func TestDockerComposeCommandSingleFile(t *testing.T) {
composeFiles := []string{"one.yml"}
conf, err := NewAppConfig("name", "version", "commit", "date", "buildSource", false, composeFiles)
if err != nil {
t.Fatalf("Unexpected error: %s", err)
}
actual := conf.UserConfig.CommandTemplates.DockerCompose
expected := "docker-compose -f one.yml"
if actual != expected {
t.Fatalf("Expected %s but got %s", expected, actual)
}
}
func TestDockerComposeCommandMultipleFiles(t *testing.T) {
composeFiles := []string{"one.yml", "two.yml", "three.yml"}
conf, err := NewAppConfig("name", "version", "commit", "date", "buildSource", false, composeFiles)
if err != nil {
t.Fatalf("Unexpected error: %s", err)
}
actual := conf.UserConfig.CommandTemplates.DockerCompose
expected := "docker-compose -f one.yml -f two.yml -f three.yml"
if actual != expected {
t.Fatalf("Expected %s but got %s", expected, actual)
}
}

View file

@ -98,10 +98,6 @@ func (gui *Gui) onNewPopupPanel() {
}
}
func (gui *Gui) createLoaderPanel(g *gocui.Gui, currentView *gocui.View, prompt string) error {
return gui.createPopupPanel(g, currentView, "", prompt, true, nil, nil)
}
// it is very important that within this function we never include the original prompt in any error messages, because it may contain e.g. a user password
func (gui *Gui) createConfirmationPanel(g *gocui.Gui, currentView *gocui.View, title, prompt string, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error {
return gui.createPopupPanel(g, currentView, title, prompt, false, handleConfirm, handleClose)
@ -148,10 +144,6 @@ func (gui *Gui) setKeyBindings(g *gocui.Gui, handleConfirm, handleClose func(*go
return nil
}
func (gui *Gui) createMessagePanel(g *gocui.Gui, currentView *gocui.View, title, prompt string) error {
return gui.createPopupPanel(g, currentView, title, prompt, false, nil, nil)
}
// createSpecificErrorPanel allows you to create an error popup, specifying the
// view to be focused when the user closes the popup, and a boolean specifying
// whether we will log the error. If the message may include a user password,

View file

@ -131,14 +131,8 @@ type guiState struct {
// NewGui builds a new gui handler
func NewGui(log *logrus.Entry, dockerCommand *commands.DockerCommand, oSCommand *commands.OSCommand, tr *i18n.TranslationSet, config *config.AppConfig, errorChan chan error) (*Gui, error) {
previousView := "containers"
if dockerCommand.InDockerComposeProject {
previousView = "services"
}
initialState := guiState{
PreviousView: previousView,
Platform: *oSCommand.Platform,
Platform: *oSCommand.Platform,
Panels: &panelStates{
Services: &servicePanelState{SelectedLine: -1, ContextIndex: 0},
Containers: &containerPanelState{SelectedLine: -1, ContextIndex: 0},
@ -177,10 +171,6 @@ func NewGui(log *logrus.Entry, dockerCommand *commands.DockerCommand, oSCommand
return gui, nil
}
func (gui *Gui) handleRefresh(g *gocui.Gui, v *gocui.View) error {
return gui.refreshSidePanels(g)
}
func max(a, b int) int {
if a > b {
return a
@ -394,3 +384,10 @@ func (gui *Gui) shouldRefresh(key string) bool {
gui.State.Panels.Main.ObjectKey = key
return true
}
func (gui *Gui) initiallyFocusedViewName() string {
if gui.DockerCommand.InDockerComposeProject {
return "services"
}
return "containers"
}

View file

@ -154,11 +154,10 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Description: gui.Tr.NextContext,
},
{
ViewName: "status",
Key: gocui.MouseLeft,
Modifier: gocui.ModNone,
Handler: gui.handleStatusClick,
Description: gui.Tr.NextContext,
ViewName: "status",
Key: gocui.MouseLeft,
Modifier: gocui.ModNone,
Handler: gui.handleStatusClick,
},
{
ViewName: "status",

View file

@ -275,7 +275,11 @@ func (gui *Gui) layout(g *gocui.Gui) error {
if gui.g.CurrentView() == nil {
v, err := gui.g.View(gui.State.PreviousView)
if err != nil {
v = gui.getServicesView()
viewName := gui.initiallyFocusedViewName()
v, err = gui.g.View(viewName)
if err != nil {
return err
}
}
if err := gui.switchFocus(gui.g, nil, v); err != nil {

View file

@ -91,6 +91,10 @@ func (gui *Gui) handleExitMain(g *gocui.Gui, v *gocui.View) error {
}
func (gui *Gui) handleMainClick(g *gocui.Gui, v *gocui.View) error {
if gui.popupPanelFocused() {
return nil
}
currentView := gui.g.CurrentView()
v.ParentView = currentView

View file

@ -144,11 +144,6 @@ func (gui *Gui) switchFocus(g *gocui.Gui, oldView, newView *gocui.View) error {
return gui.newLineFocused(newView)
}
func (gui *Gui) resetOrigin(v *gocui.View) error {
_ = v.SetCursor(0, 0)
return v.SetOrigin(0, 0)
}
// if the cursor down past the last item, move it to the last line
func (gui *Gui) focusPoint(selectedX int, selectedY int, lineCount int, v *gocui.View) error {
if selectedY < 0 || selectedY > lineCount {
@ -317,28 +312,6 @@ func (gui *Gui) changeSelectedLine(line *int, total int, up bool) {
}
}
func (gui *Gui) refreshSelectedLine(line *int, total int) {
if *line == -1 && total > 0 {
*line = 0
} else if total-1 < *line {
*line = total - 1
}
}
func (gui *Gui) renderListPanel(v *gocui.View, items interface{}) error {
gui.g.Update(func(g *gocui.Gui) error {
isFocused := gui.g.CurrentView().Name() == v.Name()
list, err := utils.RenderList(items, utils.IsFocused(isFocused))
if err != nil {
return gui.createErrorPanel(gui.g, err.Error())
}
v.Clear()
fmt.Fprint(v, list)
return nil
})
return nil
}
func (gui *Gui) renderPanelOptions() error {
currentView := gui.g.CurrentView()
switch currentView.Name() {
@ -348,11 +321,6 @@ func (gui *Gui) renderPanelOptions() error {
return gui.renderGlobalOptions()
}
func (gui *Gui) handleFocusView(g *gocui.Gui, v *gocui.View) error {
_, err := gui.g.SetCurrentView(v.Name())
return err
}
func (gui *Gui) isPopupPanel(viewName string) bool {
return viewName == "confirmation" || viewName == "menu"
}

View file

@ -1,5 +1,90 @@
package i18n
func dutchSet() TranslationSet {
return TranslationSet{}
return TranslationSet{
PruningStatus: "vernietigen",
RemovingStatus: "verwijderen",
RestartingStatus: "herstarten",
StoppingStatus: "stoppen",
RunningCustomCommandStatus: "Aangepast commando draaien",
RunningSubprocess: "subprocess draaien",
NoViewMachingNewLineFocusedSwitchStatement: "No view matching newLineFocused switch statement",
ErrorOccurred: "Er is iets fout gegaan! Zou je hier een issue aan willen maken: https://github.com/jesseduffield/lazydocker/issues",
ConnectionFailed: "connectie naar de docker client mislukt. Het zou kunnen dat je de docker client moet herstarten",
UnattachableContainerError: "Container heeft geen ondersteuning voor vastmaken. Je zou de service met het '-it' argument kunnen draaien of stop dit in je `stdin_open: true, tty: true` docker-compose.yml",
CannotAttachStoppedContainerError: "Je kan niet een vastgemaakte container stoppen, je moet het eerst starten (dit kan je doen met de 'r' toets) (ja ik ben te leu om dat voor je te doen automatisch)",
CannotAccessDockerSocketError: "Kan de docker socket niet bereiken: unix:///var/run/docker.sock\nDraai lazydocker als root of lees https://docs.docker.com/install/linux/linux-postinstall/",
Donate: "Doneer",
Confirm: "Bevestigen",
Return: "terug",
FocusMain: "focus hooft panneel",
Navigate: "navigeer",
Execute: "voer uit",
Close: "sluit",
Menu: "menu",
Scroll: "scroll",
OpenConfig: "open de lazydocker configuratie",
EditConfig: "verander de lazydocker configuratie",
Cancel: "annuleren",
Remove: "verwijder",
ForceRemove: "geforceerd verwijderen",
RemoveWithVolumes: "verwijder met volumes",
RemoveService: "verwijder containers",
Stop: "stop",
Restart: "herstart",
Rebuild: "herbouw",
Recreate: "hercreëer",
PreviousContext: "vorige tab",
NextContext: "volgende tab",
Attach: "verbinden",
ViewLogs: "bekijk logs",
RemoveImage: "verwijder image",
RemoveVolume: "verwijder volume",
RemoveWithoutPrune: "verwijder zonder de ongelabeld ouders te verwijderen",
PruneContainers: "vernietig bestaande containers",
PruneVolumes: "vernietig ongebruikte volumes",
PruneImages: "vernietig ongebruikte images",
ViewRestartOptions: "beijk herstart opties",
RunCustomCommand: "draai een vooraf bedacht aangepaste opdracht",
AnonymousReportingTitle: "Help mee met lazydocker beter maken",
AnonymousReportingPrompt: "Zou je anonieme data rapportage willen aanzetten om lazygit beter te kunnen maken? (enter/esc)",
GlobalTitle: "Globaal",
MainTitle: "Hooft",
StatusTitle: "Staat",
ServicesTitle: "Diensten",
ContainersTitle: "Containers",
StandaloneContainersTitle: "Alleenstaande Containers",
ImagesTitle: "Images",
VolumesTitle: "Volumes",
CustomCommandTitle: "Aangepast commando:",
ErrorTitle: "Fout",
LogsTitle: "Logs",
ConfigTitle: "Config",
DockerComposeConfigTitle: "Docker-Compose Configuratie",
TopTitle: "Top",
StatsTitle: "Stats",
CreditsTitle: "Over",
ContainerConfigTitle: "Container Configuratie",
NoContainers: "Geen containers",
NoContainer: "Geen container",
NoImages: "Geen images",
NoVolumes: "Geen volumes",
ConfirmQuit: "Weet je zeker dat je weg wil gaan?",
MustForceToRemoveContainer: "Je kan geen draaiende container verwijderen tenzij je het forceert, Wil je het forceren?",
NotEnoughSpace: "Niet genoeg ruimte om de panelen te renderen",
ConfirmPruneImages: "Weet je zeker dat je alle niet gebruikte images wil vernietigen?",
ConfirmPruneContainers: "Weet je zeker dat je alle niet gestopte containers wil vernietigen?",
ConfirmPruneVolumes: "Weet je zeker dat je alle niet gebruikte volumes wil vernietigen?",
StopService: "Weet je zeker dat je deze service zijn containers wil stoppen (enter/esc)?",
StopContainer: "Weet je zeker dat je deze container wil stoppen?",
PressEnterToReturn: "Druk op enter om terug te gaan naar lazydocker (Deze popup kan uit gezet worden door in de config dit neer te zetten `gui.returnImmediately: true`)",
}
}

90
pkg/i18n/german.go Normal file
View file

@ -0,0 +1,90 @@
package i18n
func germanSet() TranslationSet {
return TranslationSet{
PruningStatus: "zerstören",
RemovingStatus: "entfernen",
RestartingStatus: "neustarten",
StoppingStatus: "anhalten",
RunningCustomCommandStatus: "führt benutzerdefinierten Befehl aus",
RunningSubprocess: "Unterprozess ausführen",
NoViewMachingNewLineFocusedSwitchStatement: "No view matching newLineFocused switch statement",
ErrorOccurred: "Es ist ein Fehler aufgetreten! Bitte erstelle ein Issue hier: https://github.com/jesseduffield/lazydocker/issues",
ConnectionFailed: "Verbindung zum Docker Client fehlgeschlagen. Du musst ggf. den Docker Client neustarten.",
UnattachableContainerError: "Der Container bietet keine Unterstützung für das Anbinden. Du musst den Dienst entweder mit der '-it' Flagge benutzen oder `stdin_open: true, tty: true` in der docker-compose.yml Datei setzen.",
CannotAttachStoppedContainerError: "Du kannst keinen angehaltenen Container anbinden. Du musst ihn erst starten (was du tun kannst, indem du 'r' drückst), (ja, ich bin zu faul um das zu automatisieren) (aber ist schon cool, dass ich so eine Konversation durch eine Fehlermeldung mit dir führen kann)",
CannotAccessDockerSocketError: "Kann nicht auf den Socket zugreifen: unix:///var/run/docker.sock\nFühre lazydocker als root aus oder lese https://docs.docker.com/install/linux/linux-postinstall/",
Donate: "Spenden",
Confirm: "Bestätigen",
Return: "zurück",
FocusMain: "fokusieren aufs Hauptpanel",
Navigate: "navigieren",
Execute: "ausführen",
Close: "schließen",
Menu: "Menü",
Scroll: "scrollen",
OpenConfig: "öffne lazydocker Konfiguration",
EditConfig: "bearbeite lazydocker Konfiguration",
Cancel: "abbrechen",
Remove: "entfernen",
ForceRemove: "Entfernen erzwingen",
RemoveWithVolumes: "entferne mit Volumen",
RemoveService: "entferne Container",
Stop: "anhalten",
Restart: "neustarten",
Rebuild: "neubauen",
Recreate: "neuerstellen",
PreviousContext: "vorheriges Tab",
NextContext: "nächstes Tab",
Attach: "anbinden",
ViewLogs: "zeige Protokolle",
RemoveImage: "entferne Image",
RemoveVolume: "entferne Volumen",
RemoveWithoutPrune: "entfernen, ohne die unmarkierten Eltern zu entfernen",
PruneContainers: "entferne verlassene Container",
PruneVolumes: "entferne unbenutzte Volumen",
PruneImages: "entferne unbenutzte Images",
ViewRestartOptions: "zeige Neustartoptionen",
RunCustomCommand: "führe vordefinierten benutzerdefinierten Befehl aus",
AnonymousReportingTitle: "Mache dabei mit lazydocker besser zu machen",
AnonymousReportingPrompt: "Möchtest du anonymiserte Datensammlung erlauben, um zu helfen lazydocker zu erbessern? (enter/esc)",
GlobalTitle: "Global",
MainTitle: "Haupt",
StatusTitle: "Status",
ServicesTitle: "Dienste",
ContainersTitle: "Container",
StandaloneContainersTitle: "Alleinstehende Container",
ImagesTitle: "Images",
VolumesTitle: "Volumen",
CustomCommandTitle: "Benutzerdefinierter Befehl",
ErrorTitle: "Fehler",
LogsTitle: "Protokoll",
ConfigTitle: "Konfiguration",
DockerComposeConfigTitle: "Docker-Compose Konfiguration",
TopTitle: "Top",
StatsTitle: "Statistiken",
CreditsTitle: "Über Uns",
ContainerConfigTitle: "Container Konfiguration",
NoContainers: "Keine Container",
NoContainer: "Kein Container",
NoImages: "Keine Images",
NoVolumes: "Keine Volumen",
ConfirmQuit: "Bist du dir sicher, dass du verlassen möchtest?",
MustForceToRemoveContainer: "Du kannst keinen Container entfernen, der noch ausgeführt wird außer du erzwingst es. Möchtest du es erzwingen?",
NotEnoughSpace: "Nicht genug Platz um die Panele darzustellen",
ConfirmPruneImages: "Bist du dir sicher, dass du alle unbenutzten Images entfernen möchtest?",
ConfirmPruneContainers: "Bist du dir sicher, dass du alle angehaltenen Container entfernen möchtes?",
ConfirmPruneVolumes: "Bist du dir sicher, dass du alle unbenutzen Volumen entfernen möchtest?",
StopService: "Bist du dir sicher, dass du den Dienst dieses Containers anhalten möchtest? (enter/esc)",
StopContainer: "Bist du dir sicher, dass du den Container anhalten möchtest?",
PressEnterToReturn: "Drücke Eingabe um zu lazydocker zurück zu kehren. (Diese Nachfrage kann in Deiner Konfiguration deaktiviert werden, indem du folgenden Wert setzt: `gui.returnImmediately: true`)",
}
}

View file

@ -31,7 +31,11 @@ func NewTranslationSet(log *logrus.Entry) *TranslationSet {
}
if strings.HasPrefix(userLang, "nl") {
mergo.Merge(&set, polishSet(), mergo.WithOverride)
mergo.Merge(&set, dutchSet(), mergo.WithOverride)
}
if strings.HasPrefix(userLang, "de") {
mergo.Merge(&set, germanSet(), mergo.WithOverride)
}
return &set

View file

@ -1,45 +0,0 @@
package test
import (
"fmt"
"os/exec"
"strings"
"testing"
"github.com/mgutz/str"
"github.com/stretchr/testify/assert"
)
// CommandSwapper takes a command, verifies that it is what it's expected to be
// and then returns a replacement command that will actually be called by the os
type CommandSwapper struct {
Expect string
Replace string
}
// SwapCommand verifies the command is what we expected, and swaps it out for a different command
func (i *CommandSwapper) SwapCommand(t *testing.T, cmd string, args []string) *exec.Cmd {
splitCmd := str.ToArgv(i.Expect)
assert.EqualValues(t, splitCmd[0], cmd, fmt.Sprintf("received command: %s %s", cmd, strings.Join(args, " ")))
if len(splitCmd) > 1 {
assert.EqualValues(t, splitCmd[1:], args, fmt.Sprintf("received command: %s %s", cmd, strings.Join(args, " ")))
}
splitCmd = str.ToArgv(i.Replace)
return exec.Command(splitCmd[0], splitCmd[1:]...)
}
// CreateMockCommand creates a command function that will verify its receiving the right sequence of commands from lazydocker
func CreateMockCommand(t *testing.T, swappers []*CommandSwapper) func(cmd string, args ...string) *exec.Cmd {
commandIndex := 0
return func(cmd string, args ...string) *exec.Cmd {
var command *exec.Cmd
if commandIndex > len(swappers)-1 {
assert.Fail(t, fmt.Sprintf("too many commands run. This command was (%s %s)", cmd, strings.Join(args, " ")))
}
command = swappers[commandIndex].SwapCommand(t, cmd, args)
commandIndex++
return command
}
}

View file

@ -2,7 +2,6 @@ package utils
import (
"bytes"
"encoding/json"
"fmt"
"html/template"
"math"
@ -59,15 +58,6 @@ func ColoredStringDirect(str string, colour *color.Color) string {
return colour.SprintFunc()(fmt.Sprint(str))
}
// TrimTrailingNewline - Trims the trailing newline
// TODO: replace with `chomp` after refactor
func TrimTrailingNewline(str string) string {
if strings.HasSuffix(str, "\n") {
return str[:len(str)-1]
}
return str
}
// NormalizeLinefeeds - Removes all Windows and Mac style line feeds
func NormalizeLinefeeds(str string) string {
str = strings.Replace(str, "\r\n", "\n", -1)
@ -92,14 +82,6 @@ func ResolvePlaceholderString(str string, arguments map[string]string) string {
return str
}
// Min returns the minimum of two integers
func Min(x, y int) int {
if x < y {
return x
}
return y
}
// Max returns the maximum of two integers
func Max(x, y int) int {
if x > y {
@ -240,42 +222,6 @@ func getDisplayStringArrays(displayables []Displayable, isFocused bool) [][]stri
return stringArrays
}
// IncludesString if the list contains the string
func IncludesString(list []string, a string) bool {
for _, b := range list {
if b == a {
return true
}
}
return false
}
// NextIndex returns the index of the element that comes after the given number
func NextIndex(numbers []int, currentNumber int) int {
for index, number := range numbers {
if number > currentNumber {
return index
}
}
return 0
}
// PrevIndex returns the index that comes before the given number, cycling if we reach the end
func PrevIndex(numbers []int, currentNumber int) int {
end := len(numbers) - 1
for i := end; i >= 0; i -= 1 {
if numbers[i] < currentNumber {
return i
}
}
return end
}
func AsJson(i interface{}) string {
bytes, _ := json.MarshalIndent(i, "", " ")
return string(bytes)
}
func FormatBinaryBytes(b int) string {
n := float64(b)
units := []string{"B", "kiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"}

View file

@ -62,29 +62,6 @@ func TestWithPadding(t *testing.T) {
}
}
// TestTrimTrailingNewline is a function.
func TestTrimTrailingNewline(t *testing.T) {
type scenario struct {
str string
expected string
}
scenarios := []scenario{
{
"hello world !\n",
"hello world !",
},
{
"hello world !",
"hello world !",
},
}
for _, s := range scenarios {
assert.EqualValues(t, s.expected, TrimTrailingNewline(s.str))
}
}
// TestNormalizeLinefeeds is a function.
func TestNormalizeLinefeeds(t *testing.T) {
type scenario struct {
@ -403,173 +380,3 @@ func TestGetPadWidths(t *testing.T) {
assert.EqualValues(t, s.expected, getPadWidths(s.stringArrays))
}
}
// TestMin is a function.
func TestMin(t *testing.T) {
type scenario struct {
a int
b int
expected int
}
scenarios := []scenario{
{
1,
1,
1,
},
{
1,
2,
1,
},
{
2,
1,
1,
},
}
for _, s := range scenarios {
assert.EqualValues(t, s.expected, Min(s.a, s.b))
}
}
// TestIncludesString is a function.
func TestIncludesString(t *testing.T) {
type scenario struct {
list []string
element string
expected bool
}
scenarios := []scenario{
{
[]string{"a", "b"},
"a",
true,
},
{
[]string{"a", "b"},
"c",
false,
},
{
[]string{"a", "b"},
"",
false,
},
{
[]string{""},
"",
true,
},
}
for _, s := range scenarios {
assert.EqualValues(t, s.expected, IncludesString(s.list, s.element))
}
}
func TestNextIndex(t *testing.T) {
type scenario struct {
testName string
list []int
element int
expected int
}
scenarios := []scenario{
{
// I'm not really fussed about how it behaves here
"no elements",
[]int{},
1,
0,
},
{
"one element",
[]int{1},
1,
0,
},
{
"two elements",
[]int{1, 2},
1,
1,
},
{
"two elements, giving second one",
[]int{1, 2},
2,
0,
},
{
"three elements, giving second one",
[]int{1, 2, 3},
2,
2,
},
}
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
assert.EqualValues(t, s.expected, NextIndex(s.list, s.element))
})
}
}
func TestPrevIndex(t *testing.T) {
type scenario struct {
testName string
list []int
element int
expected int
}
scenarios := []scenario{
{
// I'm not really fussed about how it behaves here
"no elements",
[]int{},
1,
-1,
},
{
"one element",
[]int{1},
1,
0,
},
{
"two elements",
[]int{1, 2},
1,
1,
},
{
"three elements, giving second one",
[]int{1, 2, 3},
2,
0,
},
}
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
assert.EqualValues(t, s.expected, PrevIndex(s.list, s.element))
})
}
}
func TestAsJson(t *testing.T) {
type myStruct struct {
a string
}
output := AsJson(&myStruct{a: "foo"})
// no idea why this is returning empty hashes but it's works in the app ¯\_(ツ)_/¯
assert.EqualValues(t, "{}", output)
}

24
vendor/github.com/integrii/flaggy/LICENSE generated vendored Normal file
View file

@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org>

29
vendor/github.com/integrii/flaggy/argumentParser.go generated vendored Normal file
View file

@ -0,0 +1,29 @@
package flaggy
// setValueForParsers sets the value for a specified key in the
// specified parsers (which normally include a Parser and Subcommand).
// The return values represent the key being set, and any errors
// returned when setting the key, such as failures to convert the string
// into the appropriate flag value. We stop assigning values as soon
// as we find a parser that accepts it.
func setValueForParsers(key string, value string, parsers ...ArgumentParser) (bool, error) {
var valueWasSet bool
for _, p := range parsers {
valueWasSet, err := p.SetValueForKey(key, value)
if err != nil {
return valueWasSet, err
}
if valueWasSet {
break
}
}
return valueWasSet, nil
}
// ArgumentParser represents a parser or subcommand
type ArgumentParser interface {
SetValueForKey(key string, value string) (bool, error)
}

622
vendor/github.com/integrii/flaggy/flag.go generated vendored Normal file
View file

@ -0,0 +1,622 @@
package flaggy
import (
"errors"
"fmt"
"net"
"reflect"
"strconv"
"strings"
"time"
)
// Flag holds the base methods for all flag types
type Flag struct {
ShortName string
LongName string
Description string
rawValue string // the value as a string before being parsed
Hidden bool // indicates this flag should be hidden from help and suggestions
AssignmentVar interface{}
defaultValue string // the value (as a string), that was set by default before any parsing and assignment
parsed bool // indicates that this flag has already been parsed
}
// HasName indicates that this flag's short or long name matches the
// supplied name string
func (f *Flag) HasName(name string) bool {
name = strings.TrimSpace(name)
if f.ShortName == name || f.LongName == name {
return true
}
return false
}
// identifyAndAssignValue identifies the type of the incoming value
// and assigns it to the AssignmentVar pointer's target value. If
// the value is a type that needs parsing, that is performed as well.
func (f *Flag) identifyAndAssignValue(value string) error {
var err error
// Only parse this flag default value once. This keeps us from
// overwriting the default value in help output
if !f.parsed {
f.parsed = true
// parse the default value as a string and remember it for help output
f.defaultValue, err = f.returnAssignmentVarValueAsString()
if err != nil {
return err
}
}
debugPrint("attempting to assign value", value, "to flag", f.LongName)
f.rawValue = value // remember the raw value
// depending on the type of the assignment variable, we convert the
// incoming string and assign it. We only use pointers to variables
// in flagy. No returning vars by value.
switch f.AssignmentVar.(type) {
case *string:
v, _ := (f.AssignmentVar).(*string)
*v = value
case *[]string:
v := f.AssignmentVar.(*[]string)
splitString := strings.Split(value, ",")
new := append(*v, splitString...)
*v = new
case *bool:
v, err := strconv.ParseBool(value)
if err != nil {
return err
}
a, _ := (f.AssignmentVar).(*bool)
*a = v
case *[]bool:
// parse the incoming bool
b, err := strconv.ParseBool(value)
if err != nil {
return err
}
// cast the assignment var
existing := f.AssignmentVar.(*[]bool)
// deref the assignment var and append to it
v := append(*existing, b)
// pointer the new value and assign it
a, _ := (f.AssignmentVar).(*[]bool)
*a = v
case *time.Duration:
v, err := time.ParseDuration(value)
if err != nil {
return err
}
a, _ := (f.AssignmentVar).(*time.Duration)
*a = v
case *[]time.Duration:
t, err := time.ParseDuration(value)
if err != nil {
return err
}
existing := f.AssignmentVar.(*[]time.Duration)
// deref the assignment var and append to it
v := append(*existing, t)
// pointer the new value and assign it
a, _ := (f.AssignmentVar).(*[]time.Duration)
*a = v
case *float32:
v, err := strconv.ParseFloat(value, 32)
if err != nil {
return err
}
float := float32(v)
a, _ := (f.AssignmentVar).(*float32)
*a = float
case *[]float32:
v, err := strconv.ParseFloat(value, 32)
if err != nil {
return err
}
float := float32(v)
existing := f.AssignmentVar.(*[]float32)
new := append(*existing, float)
*existing = new
case *float64:
v, err := strconv.ParseFloat(value, 64)
if err != nil {
return err
}
a, _ := (f.AssignmentVar).(*float64)
*a = v
case *[]float64:
v, err := strconv.ParseFloat(value, 64)
if err != nil {
return err
}
existing := f.AssignmentVar.(*[]float64)
new := append(*existing, v)
*existing = new
case *int:
v, err := strconv.Atoi(value)
if err != nil {
return err
}
e := f.AssignmentVar.(*int)
*e = v
case *[]int:
v, err := strconv.Atoi(value)
if err != nil {
return err
}
existing := f.AssignmentVar.(*[]int)
new := append(*existing, v)
*existing = new
case *uint:
v, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return err
}
existing := f.AssignmentVar.(*uint)
*existing = uint(v)
case *[]uint:
v, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return err
}
existing := f.AssignmentVar.(*[]uint)
new := append(*existing, uint(v))
*existing = new
case *uint64:
v, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return err
}
existing := f.AssignmentVar.(*uint64)
*existing = v
case *[]uint64:
v, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return err
}
existing := f.AssignmentVar.(*[]uint64)
new := append(*existing, v)
*existing = new
case *uint32:
v, err := strconv.ParseUint(value, 10, 32)
if err != nil {
return err
}
existing := f.AssignmentVar.(*uint32)
*existing = uint32(v)
case *[]uint32:
v, err := strconv.ParseUint(value, 10, 32)
if err != nil {
return err
}
existing := f.AssignmentVar.(*[]uint32)
new := append(*existing, uint32(v))
*existing = new
case *uint16:
v, err := strconv.ParseUint(value, 10, 16)
if err != nil {
return err
}
val := uint16(v)
existing := f.AssignmentVar.(*uint16)
*existing = val
case *[]uint16:
v, err := strconv.ParseUint(value, 10, 16)
if err != nil {
return err
}
existing := f.AssignmentVar.(*[]uint16)
new := append(*existing, uint16(v))
*existing = new
case *uint8:
v, err := strconv.ParseUint(value, 10, 8)
if err != nil {
return err
}
val := uint8(v)
existing := f.AssignmentVar.(*uint8)
*existing = val
case *[]uint8:
var newSlice []uint8
v, err := strconv.ParseUint(value, 10, 8)
if err != nil {
return err
}
newV := uint8(v)
existing := f.AssignmentVar.(*[]uint8)
newSlice = append(*existing, newV)
*existing = newSlice
case *int64:
v, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return err
}
existing := f.AssignmentVar.(*int64)
*existing = v
case *[]int64:
v, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return err
}
existingSlice := f.AssignmentVar.(*[]int64)
newSlice := append(*existingSlice, v)
*existingSlice = newSlice
case *int32:
v, err := strconv.ParseInt(value, 10, 32)
if err != nil {
return err
}
converted := int32(v)
existing := f.AssignmentVar.(*int32)
*existing = converted
case *[]int32:
v, err := strconv.ParseInt(value, 10, 32)
if err != nil {
return err
}
existingSlice := f.AssignmentVar.(*[]int32)
newSlice := append(*existingSlice, int32(v))
*existingSlice = newSlice
case *int16:
v, err := strconv.ParseInt(value, 10, 16)
if err != nil {
return err
}
converted := int16(v)
existing := f.AssignmentVar.(*int16)
*existing = converted
case *[]int16:
v, err := strconv.ParseInt(value, 10, 16)
if err != nil {
return err
}
existingSlice := f.AssignmentVar.(*[]int16)
newSlice := append(*existingSlice, int16(v))
*existingSlice = newSlice
case *int8:
v, err := strconv.ParseInt(value, 10, 8)
if err != nil {
return err
}
converted := int8(v)
existing := f.AssignmentVar.(*int8)
*existing = converted
case *[]int8:
v, err := strconv.ParseInt(value, 10, 8)
if err != nil {
return err
}
existingSlice := f.AssignmentVar.(*[]int8)
newSlice := append(*existingSlice, int8(v))
*existingSlice = newSlice
case *net.IP:
v := net.ParseIP(value)
existing := f.AssignmentVar.(*net.IP)
*existing = v
case *[]net.IP:
v := net.ParseIP(value)
existing := f.AssignmentVar.(*[]net.IP)
new := append(*existing, v)
*existing = new
case *net.HardwareAddr:
v, err := net.ParseMAC(value)
if err != nil {
return err
}
existing := f.AssignmentVar.(*net.HardwareAddr)
*existing = v
case *[]net.HardwareAddr:
v, err := net.ParseMAC(value)
if err != nil {
return err
}
existing := f.AssignmentVar.(*[]net.HardwareAddr)
new := append(*existing, v)
*existing = new
case *net.IPMask:
v := net.IPMask(net.ParseIP(value).To4())
existing := f.AssignmentVar.(*net.IPMask)
*existing = v
case *[]net.IPMask:
v := net.IPMask(net.ParseIP(value).To4())
existing := f.AssignmentVar.(*[]net.IPMask)
new := append(*existing, v)
*existing = new
default:
return errors.New("Unknown flag assignmentVar supplied in flag " + f.LongName + " " + f.ShortName)
}
return err
}
const argIsPositional = "positional" // subcommand or positional value
const argIsFlagWithSpace = "flagWithSpace" // -f path or --file path
const argIsFlagWithValue = "flagWithValue" // -f=path or --file=path
const argIsFinal = "final" // the final argument only '--'
// determineArgType determines if the specified arg is a flag with space
// separated value, a flag with a connected value, or neither (positional)
func determineArgType(arg string) string {
// if the arg is --, then its the final arg
if arg == "--" {
return argIsFinal
}
// if it has the prefix --, then its a long flag
if strings.HasPrefix(arg, "--") {
// if it contains an equals, it is a joined value
if strings.Contains(arg, "=") {
return argIsFlagWithValue
}
return argIsFlagWithSpace
}
// if it has the prefix -, then its a short flag
if strings.HasPrefix(arg, "-") {
// if it contains an equals, it is a joined value
if strings.Contains(arg, "=") {
return argIsFlagWithValue
}
return argIsFlagWithSpace
}
return argIsPositional
}
// parseArgWithValue parses a key=value concatenated argument into a key and
// value
func parseArgWithValue(arg string) (key string, value string) {
// remove up to two minuses from start of flag
arg = strings.TrimPrefix(arg, "-")
arg = strings.TrimPrefix(arg, "-")
// debugPrint("parseArgWithValue parsing", arg)
// break at the equals
args := strings.SplitN(arg, "=", 2)
// if its a bool arg, with no explicit value, we return a blank
if len(args) == 1 {
return args[0], ""
}
// if its a key and value pair, we return those
if len(args) == 2 {
// debugPrint("parseArgWithValue parsed", args[0], args[1])
return args[0], args[1]
}
fmt.Println("Warning: attempted to parseArgWithValue but did not have correct parameter count.", arg, "->", args)
return "", ""
}
// parseFlagToName parses a flag with space value down to a key name:
// --path -> path
// -p -> p
func parseFlagToName(arg string) string {
// remove minus from start
arg = strings.TrimLeft(arg, "-")
arg = strings.TrimLeft(arg, "-")
return arg
}
// flagIsBool determines if the flag is a bool within the specified parser
// and subcommand's context
func flagIsBool(sc *Subcommand, p *Parser, key string) bool {
for _, f := range append(sc.Flags, p.Flags...) {
if f.HasName(key) {
_, isBool := f.AssignmentVar.(*bool)
_, isBoolSlice := f.AssignmentVar.(*[]bool)
if isBool || isBoolSlice {
return true
}
}
}
// by default, the answer is false
return false
}
// returnAssignmentVarValueAsString returns the value of the flag's
// assignment variable as a string. This is used to display the
// default value of flags before they are assigned (like when help is output).
func (f *Flag) returnAssignmentVarValueAsString() (string, error) {
debugPrint("returning current value of assignment var of flag", f.LongName)
var err error
// depending on the type of the assignment variable, we convert the
// incoming string and assign it. We only use pointers to variables
// in flagy. No returning vars by value.
switch f.AssignmentVar.(type) {
case *string:
v, _ := (f.AssignmentVar).(*string)
return *v, err
case *[]string:
v := f.AssignmentVar.(*[]string)
return strings.Join(*v, ","), err
case *bool:
a, _ := (f.AssignmentVar).(*bool)
return strconv.FormatBool(*a), err
case *[]bool:
value := f.AssignmentVar.(*[]bool)
var ss []string
for _, b := range *value {
ss = append(ss, strconv.FormatBool(b))
}
return strings.Join(ss, ","), err
case *time.Duration:
a := f.AssignmentVar.(*time.Duration)
return (*a).String(), err
case *[]time.Duration:
tds := f.AssignmentVar.(*[]time.Duration)
var asSlice []string
for _, td := range *tds {
asSlice = append(asSlice, td.String())
}
return strings.Join(asSlice, ","), err
case *float32:
a := f.AssignmentVar.(*float32)
return strconv.FormatFloat(float64(*a), 'f', 2, 32), err
case *[]float32:
v := f.AssignmentVar.(*[]float32)
var strSlice []string
for _, f := range *v {
formatted := strconv.FormatFloat(float64(f), 'f', 2, 32)
strSlice = append(strSlice, formatted)
}
return strings.Join(strSlice, ","), err
case *float64:
a := f.AssignmentVar.(*float64)
return strconv.FormatFloat(float64(*a), 'f', 2, 64), err
case *[]float64:
v := f.AssignmentVar.(*[]float64)
var strSlice []string
for _, f := range *v {
formatted := strconv.FormatFloat(float64(f), 'f', 2, 64)
strSlice = append(strSlice, formatted)
}
return strings.Join(strSlice, ","), err
case *int:
a := f.AssignmentVar.(*int)
return strconv.Itoa(*a), err
case *[]int:
val := f.AssignmentVar.(*[]int)
var strSlice []string
for _, i := range *val {
str := strconv.Itoa(i)
strSlice = append(strSlice, str)
}
return strings.Join(strSlice, ","), err
case *uint:
v := f.AssignmentVar.(*uint)
return strconv.FormatUint(uint64(*v), 10), err
case *[]uint:
values := f.AssignmentVar.(*[]uint)
var strVars []string
for _, i := range *values {
strVars = append(strVars, strconv.FormatUint(uint64(i), 10))
}
return strings.Join(strVars, ","), err
case *uint64:
v := f.AssignmentVar.(*uint64)
return strconv.FormatUint(*v, 10), err
case *[]uint64:
values := f.AssignmentVar.(*[]uint64)
var strVars []string
for _, i := range *values {
strVars = append(strVars, strconv.FormatUint(i, 10))
}
return strings.Join(strVars, ","), err
case *uint32:
v := f.AssignmentVar.(*uint32)
return strconv.FormatUint(uint64(*v), 10), err
case *[]uint32:
values := f.AssignmentVar.(*[]uint32)
var strVars []string
for _, i := range *values {
strVars = append(strVars, strconv.FormatUint(uint64(i), 10))
}
return strings.Join(strVars, ","), err
case *uint16:
v := f.AssignmentVar.(*uint16)
return strconv.FormatUint(uint64(*v), 10), err
case *[]uint16:
values := f.AssignmentVar.(*[]uint16)
var strVars []string
for _, i := range *values {
strVars = append(strVars, strconv.FormatUint(uint64(i), 10))
}
return strings.Join(strVars, ","), err
case *uint8:
v := f.AssignmentVar.(*uint8)
return strconv.FormatUint(uint64(*v), 10), err
case *[]uint8:
values := f.AssignmentVar.(*[]uint8)
var strVars []string
for _, i := range *values {
strVars = append(strVars, strconv.FormatUint(uint64(i), 10))
}
return strings.Join(strVars, ","), err
case *int64:
v := f.AssignmentVar.(*int64)
return strconv.FormatInt(int64(*v), 10), err
case *[]int64:
values := f.AssignmentVar.(*[]int64)
var strVars []string
for _, i := range *values {
strVars = append(strVars, strconv.FormatInt(i, 10))
}
return strings.Join(strVars, ","), err
case *int32:
v := f.AssignmentVar.(*int32)
return strconv.FormatInt(int64(*v), 10), err
case *[]int32:
values := f.AssignmentVar.(*[]int32)
var strVars []string
for _, i := range *values {
strVars = append(strVars, strconv.FormatInt(int64(i), 10))
}
return strings.Join(strVars, ","), err
case *int16:
v := f.AssignmentVar.(*int16)
return strconv.FormatInt(int64(*v), 10), err
case *[]int16:
values := f.AssignmentVar.(*[]int16)
var strVars []string
for _, i := range *values {
strVars = append(strVars, strconv.FormatInt(int64(i), 10))
}
return strings.Join(strVars, ","), err
case *int8:
v := f.AssignmentVar.(*int8)
return strconv.FormatInt(int64(*v), 10), err
case *[]int8:
values := f.AssignmentVar.(*[]int8)
var strVars []string
for _, i := range *values {
strVars = append(strVars, strconv.FormatInt(int64(i), 10))
}
return strings.Join(strVars, ","), err
case *net.IP:
val := f.AssignmentVar.(*net.IP)
return val.String(), err
case *[]net.IP:
val := f.AssignmentVar.(*[]net.IP)
var strSlice []string
for _, ip := range *val {
strSlice = append(strSlice, ip.String())
}
return strings.Join(strSlice, ","), err
case *net.HardwareAddr:
val := f.AssignmentVar.(*net.HardwareAddr)
return val.String(), err
case *[]net.HardwareAddr:
val := f.AssignmentVar.(*[]net.HardwareAddr)
var strSlice []string
for _, mac := range *val {
strSlice = append(strSlice, mac.String())
}
return strings.Join(strSlice, ","), err
case *net.IPMask:
val := f.AssignmentVar.(*net.IPMask)
return val.String(), err
case *[]net.IPMask:
val := f.AssignmentVar.(*[]net.IPMask)
var strSlice []string
for _, m := range *val {
strSlice = append(strSlice, m.String())
}
return strings.Join(strSlice, ","), err
default:
return "", errors.New("Unknown flag assignmentVar found in flag " + f.LongName + " " + f.ShortName + ". Type not supported: " + reflect.TypeOf(f.AssignmentVar).String())
}
}

23
vendor/github.com/integrii/flaggy/help.go generated vendored Normal file
View file

@ -0,0 +1,23 @@
package flaggy
// defaultHelpTemplate is the help template used by default
// {{if (or (or (gt (len .StringFlags) 0) (gt (len .IntFlags) 0)) (gt (len .BoolFlags) 0))}}
// {{if (or (gt (len .StringFlags) 0) (gt (len .BoolFlags) 0))}}
const defaultHelpTemplate = `{{.CommandName}}{{if .Description}} - {{.Description}}{{end}}{{if .PrependMessage}}
{{.PrependMessage}}{{end}}
{{if .UsageString}}
Usage:
{{.UsageString}}{{end}}{{if .Positionals}}
Positional Variables: {{range .Positionals}}
{{.Name}}{{if .Required}} (Required){{end}}{{if .Description}} - {{.Description}}{{end}}{{if .DefaultValue}} (default: {{.DefaultValue}}){{end}}{{end}}{{end}}{{if .Subcommands}}
Subcommands: {{range .Subcommands}}
{{.LongName}}{{if .ShortName}} ({{.ShortName}}){{end}}{{if .Position}}{{if gt .Position 1}} (position {{.Position}}){{end}}{{end}}{{if .Description}} - {{.Description}}{{end}}{{end}}
{{end}}{{if (gt (len .Flags) 0)}}
Flags: {{if .Flags}}{{range .Flags}}
{{if .ShortName}}-{{.ShortName}} {{else}} {{end}}{{if .LongName}}--{{.LongName}} {{end}}{{if .Description}} {{.Description}}{{if .DefaultValue}} (default: {{.DefaultValue}}){{end}}{{end}}{{end}}{{end}}
{{end}}{{if .AppendMessage}}{{.AppendMessage}}
{{end}}{{if .Message}}
{{.Message}}{{end}}
`

215
vendor/github.com/integrii/flaggy/helpValues.go generated vendored Normal file
View file

@ -0,0 +1,215 @@
package flaggy
// Help represents the values needed to render a Help page
type Help struct {
Subcommands []HelpSubcommand
Positionals []HelpPositional
Flags []HelpFlag
UsageString string
CommandName string
PrependMessage string
AppendMessage string
Message string
Description string
}
// HelpSubcommand is used to template subcommand Help output
type HelpSubcommand struct {
ShortName string
LongName string
Description string
Position int
}
// HelpPositional is used to template positional Help output
type HelpPositional struct {
Name string
Description string
Required bool
Position int
DefaultValue string
}
// HelpFlag is used to template string flag Help output
type HelpFlag struct {
ShortName string
LongName string
Description string
DefaultValue string
}
// ExtractValues extracts Help template values from a subcommand and its parent
// parser. The parser is required in order to detect default flag settings
// for help and version outut.
func (h *Help) ExtractValues(p *Parser, message string) {
// accept message string for output
h.Message = message
// extract Help values from the current subcommand in context
// prependMessage string
h.PrependMessage = p.subcommandContext.AdditionalHelpPrepend
// appendMessage string
h.AppendMessage = p.subcommandContext.AdditionalHelpAppend
// command name
h.CommandName = p.subcommandContext.Name
// description
h.Description = p.subcommandContext.Description
// subcommands []HelpSubcommand
for _, cmd := range p.subcommandContext.Subcommands {
if cmd.Hidden {
continue
}
newHelpSubcommand := HelpSubcommand{
ShortName: cmd.ShortName,
LongName: cmd.Name,
Description: cmd.Description,
Position: cmd.Position,
}
h.Subcommands = append(h.Subcommands, newHelpSubcommand)
}
// parse positional flags into help output structs
for _, pos := range p.subcommandContext.PositionalFlags {
if pos.Hidden {
continue
}
newHelpPositional := HelpPositional{
Name: pos.Name,
Position: pos.Position,
Description: pos.Description,
Required: pos.Required,
DefaultValue: pos.defaultValue,
}
h.Positionals = append(h.Positionals, newHelpPositional)
}
// if the built-in version flag is enabled, then add it as a help flag
if p.ShowVersionWithVersionFlag {
defaultVersionFlag := HelpFlag{
ShortName: "",
LongName: versionFlagLongName,
Description: "Displays the program version string.",
DefaultValue: "",
}
h.Flags = append(h.Flags, defaultVersionFlag)
}
// if the built-in help flag exists, then add it as a help flag
if p.ShowHelpWithHFlag {
defaultHelpFlag := HelpFlag{
ShortName: helpFlagShortName,
LongName: helpFlagLongName,
Description: "Displays help with available flag, subcommand, and positional value parameters.",
DefaultValue: "",
}
h.Flags = append(h.Flags, defaultHelpFlag)
}
// go through every flag in the subcommand and add it to help output
h.parseFlagsToHelpFlags(p.subcommandContext.Flags)
// go through every flag in the parent parser and add it to help output
h.parseFlagsToHelpFlags(p.Flags)
// formulate the usage string
// first, we capture all the command and positional names by position
commandsByPosition := make(map[int]string)
for _, pos := range p.subcommandContext.PositionalFlags {
if pos.Hidden {
continue
}
if len(commandsByPosition[pos.Position]) > 0 {
commandsByPosition[pos.Position] = commandsByPosition[pos.Position] + "|" + pos.Name
} else {
commandsByPosition[pos.Position] = pos.Name
}
}
for _, cmd := range p.subcommandContext.Subcommands {
if cmd.Hidden {
continue
}
if len(commandsByPosition[cmd.Position]) > 0 {
commandsByPosition[cmd.Position] = commandsByPosition[cmd.Position] + "|" + cmd.Name
} else {
commandsByPosition[cmd.Position] = cmd.Name
}
}
// find the highest position count in the map
var highestPosition int
for i := range commandsByPosition {
if i > highestPosition {
highestPosition = i
}
}
// only have a usage string if there are positional items
var usageString string
if highestPosition > 0 {
// find each positional value and make our final string
usageString = p.subcommandContext.Name
for i := 1; i <= highestPosition; i++ {
if len(commandsByPosition[i]) > 0 {
usageString = usageString + " [" + commandsByPosition[i] + "]"
} else {
// dont keep listing after the first position without any properties
// it will be impossible to reach anything beyond here anyway
break
}
}
}
h.UsageString = usageString
}
// parseFlagsToHelpFlags parses the specified slice of flags into
// help flags on the the calling help command
func (h *Help) parseFlagsToHelpFlags(flags []*Flag) {
for _, f := range flags {
if f.Hidden {
continue
}
// determine the default value based on the assignment variable
defaultValue := f.defaultValue
// dont show nils
if defaultValue == "<nil>" {
defaultValue = ""
}
// for bools, dont show a default of false
_, isBool := f.AssignmentVar.(*bool)
if isBool {
b := f.AssignmentVar.(*bool)
if *b == false {
defaultValue = ""
}
}
newHelpFlag := HelpFlag{
ShortName: f.ShortName,
LongName: f.LongName,
Description: f.Description,
DefaultValue: defaultValue,
}
h.AddFlagToHelp(newHelpFlag)
}
}
// AddFlagToHelp adds a flag to help output if it does not exist
func (h *Help) AddFlagToHelp(f HelpFlag) {
for _, existingFlag := range h.Flags {
if len(existingFlag.ShortName) > 0 && existingFlag.ShortName == f.ShortName {
return
}
if len(existingFlag.LongName) > 0 && existingFlag.LongName == f.LongName {
return
}
}
h.Flags = append(h.Flags, f)
}

341
vendor/github.com/integrii/flaggy/main.go generated vendored Normal file
View file

@ -0,0 +1,341 @@
// Package flaggy is a input flag parsing package that supports recursive
// subcommands, positional values, and any-position flags without
// unnecessary complexeties.
//
// For a getting started tutorial and full feature list, check out the
// readme at https://github.com/integrii/flaggy.
package flaggy // import "github.com/integrii/flaggy"
import (
"fmt"
"log"
"net"
"os"
"strconv"
"strings"
"time"
)
// strings used for builtin help and version flags both short and long
const versionFlagLongName = "version"
const helpFlagLongName = "help"
const helpFlagShortName = "h"
// defaultVersion is applied to parsers when they are created
const defaultVersion = "0.0.0"
// DebugMode indicates that debug output should be enabled
var DebugMode bool
// DefaultHelpTemplate is the help template that will be used
// for newly created subcommands and commands
var DefaultHelpTemplate = defaultHelpTemplate
// DefaultParser is the default parser that is used with the package-level public
// functions
var DefaultParser *Parser
// TrailingArguments holds trailing arguments in the main parser after parsing
// has been run.
var TrailingArguments []string
func init() {
// set the default help template
// allow usage like flaggy.StringVar by enabling a default Parser
ResetParser()
}
// ResetParser resets the default parser to a fresh instance. Uses the
// name of the binary executing as the program name by default.
func ResetParser() {
if len(os.Args) > 0 {
chunks := strings.Split(os.Args[0], "/")
DefaultParser = NewParser(chunks[len(chunks)-1])
} else {
DefaultParser = NewParser("default")
}
}
// Parse parses flags as requested in the default package parser
func Parse() {
err := DefaultParser.Parse()
TrailingArguments = DefaultParser.TrailingArguments
if err != nil {
log.Panicln("Error from argument parser:", err)
}
}
// ParseArgs parses the passed args as if they were the arguments to the
// running binary. Targets the default main parser for the package.
func ParseArgs(args []string) {
err := DefaultParser.ParseArgs(args)
TrailingArguments = DefaultParser.TrailingArguments
if err != nil {
log.Panicln("Error from argument parser:", err)
}
}
// String adds a new string flag
func String(assignmentVar *string, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// StringSlice adds a new slice of strings flag
// Specify the flag multiple times to fill the slice
func StringSlice(assignmentVar *[]string, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// Bool adds a new bool flag
func Bool(assignmentVar *bool, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// BoolSlice adds a new slice of bools flag
// Specify the flag multiple times to fill the slice
func BoolSlice(assignmentVar *[]bool, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// ByteSlice adds a new slice of bytes flag
// Specify the flag multiple times to fill the slice. Takes hex as input.
func ByteSlice(assignmentVar *[]byte, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// Duration adds a new time.Duration flag.
// Input format is described in time.ParseDuration().
// Example values: 1h, 1h50m, 32s
func Duration(assignmentVar *time.Duration, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// DurationSlice adds a new time.Duration flag.
// Input format is described in time.ParseDuration().
// Example values: 1h, 1h50m, 32s
// Specify the flag multiple times to fill the slice.
func DurationSlice(assignmentVar *[]time.Duration, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// Float32 adds a new float32 flag.
func Float32(assignmentVar *float32, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// Float32Slice adds a new float32 flag.
// Specify the flag multiple times to fill the slice.
func Float32Slice(assignmentVar *[]float32, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// Float64 adds a new float64 flag.
func Float64(assignmentVar *float64, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// Float64Slice adds a new float64 flag.
// Specify the flag multiple times to fill the slice.
func Float64Slice(assignmentVar *[]float64, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// Int adds a new int flag
func Int(assignmentVar *int, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// IntSlice adds a new int slice flag.
// Specify the flag multiple times to fill the slice.
func IntSlice(assignmentVar *[]int, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// UInt adds a new uint flag
func UInt(assignmentVar *uint, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// UIntSlice adds a new uint slice flag.
// Specify the flag multiple times to fill the slice.
func UIntSlice(assignmentVar *[]uint, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// UInt64 adds a new uint64 flag
func UInt64(assignmentVar *uint64, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// UInt64Slice adds a new uint64 slice flag.
// Specify the flag multiple times to fill the slice.
func UInt64Slice(assignmentVar *[]uint64, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// UInt32 adds a new uint32 flag
func UInt32(assignmentVar *uint32, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// UInt32Slice adds a new uint32 slice flag.
// Specify the flag multiple times to fill the slice.
func UInt32Slice(assignmentVar *[]uint32, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// UInt16 adds a new uint16 flag
func UInt16(assignmentVar *uint16, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// UInt16Slice adds a new uint16 slice flag.
// Specify the flag multiple times to fill the slice.
func UInt16Slice(assignmentVar *[]uint16, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// UInt8 adds a new uint8 flag
func UInt8(assignmentVar *uint8, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// UInt8Slice adds a new uint8 slice flag.
// Specify the flag multiple times to fill the slice.
func UInt8Slice(assignmentVar *[]uint8, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// Int64 adds a new int64 flag
func Int64(assignmentVar *int64, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// Int64Slice adds a new int64 slice flag.
// Specify the flag multiple times to fill the slice.
func Int64Slice(assignmentVar *[]int64, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// Int32 adds a new int32 flag
func Int32(assignmentVar *int32, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// Int32Slice adds a new int32 slice flag.
// Specify the flag multiple times to fill the slice.
func Int32Slice(assignmentVar *[]int32, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// Int16 adds a new int16 flag
func Int16(assignmentVar *int16, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// Int16Slice adds a new int16 slice flag.
// Specify the flag multiple times to fill the slice.
func Int16Slice(assignmentVar *[]int16, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// Int8 adds a new int8 flag
func Int8(assignmentVar *int8, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// Int8Slice adds a new int8 slice flag.
// Specify the flag multiple times to fill the slice.
func Int8Slice(assignmentVar *[]int8, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// IP adds a new net.IP flag.
func IP(assignmentVar *net.IP, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// IPSlice adds a new int8 slice flag.
// Specify the flag multiple times to fill the slice.
func IPSlice(assignmentVar *[]net.IP, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// HardwareAddr adds a new net.HardwareAddr flag.
func HardwareAddr(assignmentVar *net.HardwareAddr, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// HardwareAddrSlice adds a new net.HardwareAddr slice flag.
// Specify the flag multiple times to fill the slice.
func HardwareAddrSlice(assignmentVar *[]net.HardwareAddr, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// IPMask adds a new net.IPMask flag. IPv4 Only.
func IPMask(assignmentVar *net.IPMask, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// IPMaskSlice adds a new net.HardwareAddr slice flag. IPv4 only.
// Specify the flag multiple times to fill the slice.
func IPMaskSlice(assignmentVar *[]net.IPMask, shortName string, longName string, description string) {
DefaultParser.add(assignmentVar, shortName, longName, description)
}
// AttachSubcommand adds a subcommand for parsing
func AttachSubcommand(subcommand *Subcommand, relativePosition int) {
DefaultParser.AttachSubcommand(subcommand, relativePosition)
}
// ShowHelp shows parser help
func ShowHelp(message string) {
DefaultParser.ShowHelpWithMessage(message)
}
// SetDescription sets the description of the default package command parser
func SetDescription(description string) {
DefaultParser.Description = description
}
// SetVersion sets the version of the default package command parser
func SetVersion(version string) {
DefaultParser.Version = version
}
// SetName sets the name of the default package command parser
func SetName(name string) {
DefaultParser.Name = name
}
// ShowHelpAndExit shows parser help and exits with status code 2
func ShowHelpAndExit(message string) {
ShowHelp(message)
exitOrPanic(2)
}
// PanicInsteadOfExit is used when running tests
var PanicInsteadOfExit bool
// exitOrPanic panics instead of calling os.Exit so that tests can catch
// more failures
func exitOrPanic(code int) {
if PanicInsteadOfExit {
panic("Panic instead of exit with code: " + strconv.Itoa(code))
}
os.Exit(code)
}
// AddPositionalValue adds a positional value to the main parser at the global
// context
func AddPositionalValue(assignmentVar *string, name string, relativePosition int, required bool, description string) {
DefaultParser.AddPositionalValue(assignmentVar, name, relativePosition, required, description)
}
// debugPrint prints if debugging is enabled
func debugPrint(i ...interface{}) {
if DebugMode {
fmt.Println(i...)
}
}

111
vendor/github.com/integrii/flaggy/parser.go generated vendored Normal file
View file

@ -0,0 +1,111 @@
package flaggy
import (
"errors"
"fmt"
"os"
"text/template"
)
// Parser represents the set of vars and subcommands we are expecting
// from our input args, and the parser than handles them all.
type Parser struct {
Subcommand
Version string // the optional version of the parser.
ShowHelpWithHFlag bool // display help when -h or --help passed
ShowVersionWithVersionFlag bool // display the version when --version passed
ShowHelpOnUnexpected bool // display help when an unexpected flag is passed
TrailingArguments []string // everything after a -- is placed here
HelpTemplate *template.Template // template for Help output
trailingArgumentsExtracted bool // indicates that trailing args have been parsed and should not be appended again
parsed bool // indicates this parser has parsed
subcommandContext *Subcommand // points to the most specific subcommand being used
}
// NewParser creates a new ArgumentParser ready to parse inputs
func NewParser(name string) *Parser {
// this can not be done inline because of struct embedding
p := &Parser{}
p.Name = name
p.Version = defaultVersion
p.ShowHelpOnUnexpected = true
p.ShowHelpWithHFlag = true
p.ShowVersionWithVersionFlag = true
p.SetHelpTemplate(DefaultHelpTemplate)
p.subcommandContext = &Subcommand{}
return p
}
// ParseArgs parses as if the passed args were the os.Args, but without the
// binary at the 0 position in the array. An error is returned if there
// is a low level issue converting flags to their proper type. No error
// is returned for invalid arguments or missing require subcommands.
func (p *Parser) ParseArgs(args []string) error {
if p.parsed {
return errors.New("Parser.Parse() called twice on parser with name: " + " " + p.Name + " " + p.ShortName)
}
p.parsed = true
// debugPrint("Kicking off parsing with args:", args)
return p.parse(p, args, 0)
}
// ShowVersionAndExit shows the version of this parser
func (p *Parser) ShowVersionAndExit() {
fmt.Println("Version:", p.Version)
exitOrPanic(0)
}
// SetHelpTemplate sets the go template this parser will use when rendering
// Help.
func (p *Parser) SetHelpTemplate(tmpl string) error {
var err error
p.HelpTemplate = template.New(helpFlagLongName)
p.HelpTemplate, err = p.HelpTemplate.Parse(tmpl)
if err != nil {
return err
}
return nil
}
// Parse calculates all flags and subcommands
func (p *Parser) Parse() error {
err := p.ParseArgs(os.Args[1:])
if err != nil {
return err
}
return nil
}
// ShowHelp shows Help without an error message
func (p *Parser) ShowHelp() {
debugPrint("showing help for", p.subcommandContext.Name)
p.ShowHelpWithMessage("")
}
// ShowHelpAndExit shows parser help and exits with status code 2
func (p *Parser) ShowHelpAndExit(message string) {
p.ShowHelpWithMessage(message)
exitOrPanic(2)
}
// ShowHelpWithMessage shows the Help for this parser with an optional string error
// message as a header. The supplied subcommand will be the context of Help
// displayed to the user.
func (p *Parser) ShowHelpWithMessage(message string) {
// create a new Help values template and extract values into it
help := Help{}
help.ExtractValues(p, message)
err := p.HelpTemplate.Execute(os.Stderr, help)
if err != nil {
fmt.Fprintln(os.Stderr, "Error rendering Help template:", err)
}
}
// Disable show version with --version. It is enabled by default.
func (p *Parser) DisableShowVersionWithVersion() {
p.ShowVersionWithVersionFlag = false
}

14
vendor/github.com/integrii/flaggy/positionalValue.go generated vendored Normal file
View file

@ -0,0 +1,14 @@
package flaggy
// PositionalValue represents a value which is determined by its position
// relative to where a subcommand was detected.
type PositionalValue struct {
Name string // used in documentation only
Description string
AssignmentVar *string // the var that will get this variable
Position int // the position, not including switches, of this variable
Required bool // this subcommand must always be specified
Found bool // was this positional found during parsing?
Hidden bool // indicates this positional value should be hidden from help
defaultValue string // used for help output
}

691
vendor/github.com/integrii/flaggy/subCommand.go generated vendored Normal file
View file

@ -0,0 +1,691 @@
package flaggy
import (
"fmt"
"log"
"net"
"os"
"strconv"
"strings"
"time"
)
// Subcommand represents a subcommand which contains a set of child
// subcommands along with a set of flags relevant to it. Parsing
// runs until a subcommand is detected by matching its name and
// position. Once a matching subcommand is found, the next set
// of parsing occurs within that matched subcommand.
type Subcommand struct {
Name string
ShortName string
Description string
Position int // the position of this subcommand, not including flags
Subcommands []*Subcommand
Flags []*Flag
PositionalFlags []*PositionalValue
AdditionalHelpPrepend string // additional prepended message when Help is displayed
AdditionalHelpAppend string // additional appended message when Help is displayed
Used bool // indicates this subcommand was found and parsed
Hidden bool // indicates this subcommand should be hidden from help
}
// NewSubcommand creates a new subcommand that can have flags or PositionalFlags
// added to it. The position starts with 1, not 0
func NewSubcommand(name string) *Subcommand {
newSC := &Subcommand{
Name: name,
}
return newSC
}
// parseAllFlagsFromArgs parses the non-positional flags such as -f or -v=value
// out of the supplied args and returns the positional items in order.
func (sc *Subcommand) parseAllFlagsFromArgs(p *Parser, args []string) ([]string, bool, error) {
var err error
var positionalOnlyArguments []string
var helpRequested bool // indicates the user has supplied -h and we
// should render help if we are the last subcommand
// indicates we should skip the next argument, like when parsing a flag
// that separates key and value by space
var skipNext bool
// endArgfound indicates that a -- was found and everything
// remaining should be added to the trailing arguments slices
var endArgFound bool
// find all the normal flags (not positional) and parse them out
for i, a := range args {
debugPrint("parsing arg", 1, a)
// evaluate if there is a following arg to avoid panics
var nextArgExists bool
var nextArg string
if len(args)-1 >= i+1 {
nextArgExists = true
nextArg = args[i+1]
}
// if end arg -- has been found, just add everything to TrailingArguments
if endArgFound {
if !p.trailingArgumentsExtracted {
p.TrailingArguments = append(p.TrailingArguments, a)
}
continue
}
// skip this run if specified
if skipNext {
skipNext = false
debugPrint("skipping flag because it is an arg:", a)
continue
}
// parse the flag into its name for consideration without dashes
flagName := parseFlagToName(a)
// if the flag being passed is version or v and the option to display
// version with version flags, then display version
if p.ShowVersionWithVersionFlag {
if flagName == versionFlagLongName {
p.ShowVersionAndExit()
}
}
// if the show Help on h flag option is set, then show Help when h or Help
// is passed as an option
if p.ShowHelpWithHFlag {
if flagName == helpFlagShortName || flagName == helpFlagLongName {
// Ensure this is the last subcommand passed so we give the correct
// help output
helpRequested = true
continue
}
}
// determine what kind of flag this is
argType := determineArgType(a)
// strip flags from arg
// debugPrint("Parsing flag named", a, "of type", argType)
// depending on the flag type, parse the key and value out, then apply it
switch argType {
case argIsFinal:
// debugPrint("Arg", i, "is final:", a)
endArgFound = true
case argIsPositional:
// debugPrint("Arg is positional or subcommand:", a)
// this positional argument into a slice of their own, so that
// we can determine if its a subcommand or positional value later
positionalOnlyArguments = append(positionalOnlyArguments, a)
case argIsFlagWithSpace:
a = parseFlagToName(a)
// debugPrint("Arg", i, "is flag with space:", a)
// parse next arg as value to this flag and apply to subcommand flags
// if the flag is a bool flag, then we check for a following positional
// and skip it if necessary
if flagIsBool(sc, p, a) {
debugPrint(sc.Name, "bool flag", a, "next var is:", nextArg)
_, err = setValueForParsers(a, "true", p, sc)
// if an error occurs, just return it and quit parsing
if err != nil {
return []string{}, false, err
}
// by default, we just assign the next argument to the value and continue
continue
}
skipNext = true
debugPrint(sc.Name, "NOT bool flag", a)
// if the next arg was not found, then show a Help message
if !nextArgExists {
p.ShowHelpWithMessage("Expected a following arg for flag " + a + ", but it did not exist.")
exitOrPanic(2)
}
_, err = setValueForParsers(a, nextArg, p, sc)
if err != nil {
return []string{}, false, err
}
case argIsFlagWithValue:
// debugPrint("Arg", i, "is flag with value:", a)
a = parseFlagToName(a)
// parse flag into key and value and apply to subcommand flags
key, val := parseArgWithValue(a)
_, err = setValueForParsers(key, val, p, sc)
if err != nil {
return []string{}, false, err
}
// if this flag type was found and not set, and the parser is set to show
// Help when an unknown flag is found, then show Help and exit.
}
}
return positionalOnlyArguments, helpRequested, nil
}
// Parse causes the argument parser to parse based on the supplied []string.
// depth specifies the non-flag subcommand positional depth
func (sc *Subcommand) parse(p *Parser, args []string, depth int) error {
debugPrint("- Parsing subcommand", sc.Name, "with depth of", depth, "and args", args)
// if a command is parsed, its used
sc.Used = true
// as subcommands are used, they become the context of the parser. This helps
// us understand how to display help based on which subcommand is being used
p.subcommandContext = sc
// ensure that help and version flags are not used if the parser has the
// built-in help and version flags enabled
if p.ShowHelpWithHFlag {
sc.ensureNoConflictWithBuiltinHelp()
}
if p.ShowVersionWithVersionFlag {
sc.ensureNoConflictWithBuiltinVersion()
}
// Parse the normal flags out of the argument list and retain the positionals.
// Apply the flags to the parent parser and the current subcommand context.
// ./command -f -z subcommand someVar -b becomes ./command subcommand somevar
positionalOnlyArguments, helpRequested, err := sc.parseAllFlagsFromArgs(p, args)
if err != nil {
return err
}
// indicate that trailing arguments have been extracted, so that they aren't
// appended a second time
p.trailingArgumentsExtracted = true
// loop over positional values and look for their matching positional
// parameter, or their positional command. If neither are found, then
// we throw an error
var parsedArgCount int
for pos, v := range positionalOnlyArguments {
// the first relative positional argument will be human natural at position 1
// but offset for the depth of relative commands being parsed for currently.
relativeDepth := pos - depth + 1
// debugPrint("Parsing positional only position", relativeDepth, "with value", v)
if relativeDepth < 1 {
// debugPrint(sc.Name, "skipped value:", v)
continue
}
parsedArgCount++
// determine subcommands and parse them by positional value and name
for _, cmd := range sc.Subcommands {
// debugPrint("Subcommand being compared", relativeDepth, "==", cmd.Position, "and", v, "==", cmd.Name, "==", cmd.ShortName)
if relativeDepth == cmd.Position && (v == cmd.Name || v == cmd.ShortName) {
debugPrint("Decending into positional subcommand", cmd.Name, "at relativeDepth", relativeDepth, "and absolute depth", depth+1)
return cmd.parse(p, args, depth+parsedArgCount) // continue recursive positional parsing
}
}
// determine positional args and parse them by positional value and name
var foundPositional bool
for _, val := range sc.PositionalFlags {
if relativeDepth == val.Position {
debugPrint("Found a positional value at relativePos:", relativeDepth, "value:", v)
// set original value for help output
val.defaultValue = *val.AssignmentVar
// defrerence the struct pointer, then set the pointer property within it
*val.AssignmentVar = v
// debugPrint("set positional to value", *val.AssignmentVar)
foundPositional = true
val.Found = true
break
}
}
// if there aren't any positional flags but there are subcommands that
// were not used, display a useful message with subcommand options.
if !foundPositional && p.ShowHelpOnUnexpected {
debugPrint("No positional at position", relativeDepth)
var foundSubcommandAtDepth bool
for _, cmd := range sc.Subcommands {
if cmd.Position == relativeDepth {
foundSubcommandAtDepth = true
}
}
// if there is a subcommand here but it was not specified, display them all
// as a suggestion to the user before exiting.
if foundSubcommandAtDepth {
// determine which name to use in upcoming help output
fmt.Fprintln(os.Stderr, sc.Name+":", "No subcommand or positional value found at position", strconv.Itoa(relativeDepth)+".")
var output string
for _, cmd := range sc.Subcommands {
if cmd.Hidden {
continue
}
output = output + " " + cmd.Name
}
// if there are available subcommands, let the user know
if len(output) > 0 {
output = strings.TrimLeft(output, " ")
fmt.Println("Available subcommands:", output)
}
exitOrPanic(2)
}
// if there were not any flags or subcommands at this position at all, then
// throw an error (display Help if necessary)
p.ShowHelpWithMessage("Unexpected argument: " + v)
exitOrPanic(2)
}
}
// if help was requested and we should show help when h is passed,
if helpRequested && p.ShowHelpWithHFlag {
p.ShowHelp()
exitOrPanic(0)
}
// find any positionals that were not used on subcommands that were
// found and throw help (unknown argument)
for _, pv := range p.PositionalFlags {
if pv.Required && !pv.Found {
p.ShowHelpWithMessage("Required global positional variable " + pv.Name + " not found at position " + strconv.Itoa(pv.Position))
exitOrPanic(2)
}
}
for _, pv := range sc.PositionalFlags {
if pv.Required && !pv.Found {
p.ShowHelpWithMessage("Required positional of subcommand " + sc.Name + " named " + pv.Name + " not found at position " + strconv.Itoa(pv.Position))
exitOrPanic(2)
}
}
return nil
}
// FlagExists lets you know if the flag name exists as either a short or long
// name in the (sub)command
func (sc *Subcommand) FlagExists(name string) bool {
for _, f := range sc.Flags {
if f.HasName(name) {
return true
}
}
return false
}
// AttachSubcommand adds a possible subcommand to the Parser.
func (sc *Subcommand) AttachSubcommand(newSC *Subcommand, relativePosition int) {
// assign the depth of the subcommand when its attached
newSC.Position = relativePosition
// ensure no subcommands at this depth with this name
for _, other := range sc.Subcommands {
if newSC.Position == other.Position {
if newSC.Name != "" {
if newSC.Name == other.Name {
log.Panicln("Unable to add subcommand because one already exists at position" + strconv.Itoa(newSC.Position) + " with name " + other.Name)
}
}
if newSC.ShortName != "" {
if newSC.ShortName == other.ShortName {
log.Panicln("Unable to add subcommand because one already exists at position" + strconv.Itoa(newSC.Position) + " with name " + other.ShortName)
}
}
}
}
// ensure no positionals at this depth
for _, other := range sc.PositionalFlags {
if newSC.Position == other.Position {
log.Panicln("Unable to add subcommand because a positional value already exists at position " + strconv.Itoa(newSC.Position) + ": " + other.Name)
}
}
sc.Subcommands = append(sc.Subcommands, newSC)
}
// add is a "generic" to add flags of any type. Checks the supplied parent
// parser to ensure that the user isn't setting version or help flags that
// conflict with the built-in help and version flag behavior.
func (sc *Subcommand) add(assignmentVar interface{}, shortName string, longName string, description string) {
// if the flag is already used, throw an error
for _, existingFlag := range sc.Flags {
if longName != "" && existingFlag.LongName == longName {
log.Panicln("Flag " + longName + " added to subcommand " + sc.Name + " but the name is already assigned.")
}
if shortName != "" && existingFlag.ShortName == shortName {
log.Panicln("Flag " + shortName + " added to subcommand " + sc.Name + " but the short name is already assigned.")
}
}
newFlag := Flag{
AssignmentVar: assignmentVar,
ShortName: shortName,
LongName: longName,
Description: description,
}
sc.Flags = append(sc.Flags, &newFlag)
}
// String adds a new string flag
func (sc *Subcommand) String(assignmentVar *string, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// StringSlice adds a new slice of strings flag
// Specify the flag multiple times to fill the slice
func (sc *Subcommand) StringSlice(assignmentVar *[]string, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// Bool adds a new bool flag
func (sc *Subcommand) Bool(assignmentVar *bool, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// BoolSlice adds a new slice of bools flag
// Specify the flag multiple times to fill the slice
func (sc *Subcommand) BoolSlice(assignmentVar *[]bool, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// ByteSlice adds a new slice of bytes flag
// Specify the flag multiple times to fill the slice. Takes hex as input.
func (sc *Subcommand) ByteSlice(assignmentVar *[]byte, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// Duration adds a new time.Duration flag.
// Input format is described in time.ParseDuration().
// Example values: 1h, 1h50m, 32s
func (sc *Subcommand) Duration(assignmentVar *time.Duration, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// DurationSlice adds a new time.Duration flag.
// Input format is described in time.ParseDuration().
// Example values: 1h, 1h50m, 32s
// Specify the flag multiple times to fill the slice.
func (sc *Subcommand) DurationSlice(assignmentVar *[]time.Duration, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// Float32 adds a new float32 flag.
func (sc *Subcommand) Float32(assignmentVar *float32, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// Float32Slice adds a new float32 flag.
// Specify the flag multiple times to fill the slice.
func (sc *Subcommand) Float32Slice(assignmentVar *[]float32, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// Float64 adds a new float64 flag.
func (sc *Subcommand) Float64(assignmentVar *float64, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// Float64Slice adds a new float64 flag.
// Specify the flag multiple times to fill the slice.
func (sc *Subcommand) Float64Slice(assignmentVar *[]float64, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// Int adds a new int flag
func (sc *Subcommand) Int(assignmentVar *int, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// IntSlice adds a new int slice flag.
// Specify the flag multiple times to fill the slice.
func (sc *Subcommand) IntSlice(assignmentVar *[]int, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// UInt adds a new uint flag
func (sc *Subcommand) UInt(assignmentVar *uint, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// UIntSlice adds a new uint slice flag.
// Specify the flag multiple times to fill the slice.
func (sc *Subcommand) UIntSlice(assignmentVar *[]uint, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// UInt64 adds a new uint64 flag
func (sc *Subcommand) UInt64(assignmentVar *uint64, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// UInt64Slice adds a new uint64 slice flag.
// Specify the flag multiple times to fill the slice.
func (sc *Subcommand) UInt64Slice(assignmentVar *[]uint64, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// UInt32 adds a new uint32 flag
func (sc *Subcommand) UInt32(assignmentVar *uint32, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// UInt32Slice adds a new uint32 slice flag.
// Specify the flag multiple times to fill the slice.
func (sc *Subcommand) UInt32Slice(assignmentVar *[]uint32, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// UInt16 adds a new uint16 flag
func (sc *Subcommand) UInt16(assignmentVar *uint16, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// UInt16Slice adds a new uint16 slice flag.
// Specify the flag multiple times to fill the slice.
func (sc *Subcommand) UInt16Slice(assignmentVar *[]uint16, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// UInt8 adds a new uint8 flag
func (sc *Subcommand) UInt8(assignmentVar *uint8, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// UInt8Slice adds a new uint8 slice flag.
// Specify the flag multiple times to fill the slice.
func (sc *Subcommand) UInt8Slice(assignmentVar *[]uint8, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// Int64 adds a new int64 flag.
func (sc *Subcommand) Int64(assignmentVar *int64, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// Int64Slice adds a new int64 slice flag.
// Specify the flag multiple times to fill the slice.
func (sc *Subcommand) Int64Slice(assignmentVar *[]int64, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// Int32 adds a new int32 flag
func (sc *Subcommand) Int32(assignmentVar *int32, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// Int32Slice adds a new int32 slice flag.
// Specify the flag multiple times to fill the slice.
func (sc *Subcommand) Int32Slice(assignmentVar *[]int32, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// Int16 adds a new int16 flag
func (sc *Subcommand) Int16(assignmentVar *int16, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// Int16Slice adds a new int16 slice flag.
// Specify the flag multiple times to fill the slice.
func (sc *Subcommand) Int16Slice(assignmentVar *[]int16, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// Int8 adds a new int8 flag
func (sc *Subcommand) Int8(assignmentVar *int8, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// Int8Slice adds a new int8 slice flag.
// Specify the flag multiple times to fill the slice.
func (sc *Subcommand) Int8Slice(assignmentVar *[]int8, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// IP adds a new net.IP flag.
func (sc *Subcommand) IP(assignmentVar *net.IP, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// IPSlice adds a new int8 slice flag.
// Specify the flag multiple times to fill the slice.
func (sc *Subcommand) IPSlice(assignmentVar *[]net.IP, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// HardwareAddr adds a new net.HardwareAddr flag.
func (sc *Subcommand) HardwareAddr(assignmentVar *net.HardwareAddr, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// HardwareAddrSlice adds a new net.HardwareAddr slice flag.
// Specify the flag multiple times to fill the slice.
func (sc *Subcommand) HardwareAddrSlice(assignmentVar *[]net.HardwareAddr, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// IPMask adds a new net.IPMask flag. IPv4 Only.
func (sc *Subcommand) IPMask(assignmentVar *net.IPMask, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// IPMaskSlice adds a new net.HardwareAddr slice flag. IPv4 only.
// Specify the flag multiple times to fill the slice.
func (sc *Subcommand) IPMaskSlice(assignmentVar *[]net.IPMask, shortName string, longName string, description string) {
sc.add(assignmentVar, shortName, longName, description)
}
// AddPositionalValue adds a positional value to the subcommand. the
// relativePosition starts at 1 and is relative to the subcommand it belongs to
func (sc *Subcommand) AddPositionalValue(assignmentVar *string, name string, relativePosition int, required bool, description string) {
// ensure no other positionals are at this depth
for _, other := range sc.PositionalFlags {
if relativePosition == other.Position {
log.Panicln("Unable to add positional value because one already exists at position: " + strconv.Itoa(relativePosition))
}
}
// ensure no subcommands at this depth
for _, other := range sc.Subcommands {
if relativePosition == other.Position {
log.Panicln("Unable to add positional value a subcommand already exists at position: " + strconv.Itoa(relativePosition))
}
}
newPositionalValue := PositionalValue{
Name: name,
Position: relativePosition,
AssignmentVar: assignmentVar,
Required: required,
Description: description,
}
sc.PositionalFlags = append(sc.PositionalFlags, &newPositionalValue)
}
// SetValueForKey sets the value for the specified key. If setting a bool
// value, then send "true" or "false" as strings. The returned bool indicates
// that a value was set.
func (sc *Subcommand) SetValueForKey(key string, value string) (bool, error) {
// debugPrint("Looking to set key", key, "to value", value)
// check for and assign flags that match the key
for _, f := range sc.Flags {
// debugPrint("Evaluating string flag", f.ShortName, "==", key, "||", f.LongName, "==", key)
if f.ShortName == key || f.LongName == key {
// debugPrint("Setting string value for", key, "to", value)
f.identifyAndAssignValue(value)
return true, nil
}
}
// debugPrint(sc.Name, "was unable to find a key named", key, "to set to value", value)
return false, nil
}
// ensureNoConflictWithBuiltinHelp ensures that the flags on this subcommand do
// not conflict with the builtin help flags (-h or --help). Exits the program
// if a conflict is found.
func (sc *Subcommand) ensureNoConflictWithBuiltinHelp() {
for _, f := range sc.Flags {
if f.LongName == helpFlagLongName {
sc.exitBecauseOfHelpFlagConflict(f.LongName)
}
if f.LongName == helpFlagShortName {
sc.exitBecauseOfHelpFlagConflict(f.LongName)
}
if f.ShortName == helpFlagLongName {
sc.exitBecauseOfHelpFlagConflict(f.ShortName)
}
if f.ShortName == helpFlagShortName {
sc.exitBecauseOfHelpFlagConflict(f.ShortName)
}
}
}
// ensureNoConflictWithBuiltinVersion ensures that the flags on this subcommand do
// not conflict with the builtin version flag (--version). Exits the program
// if a conflict is found.
func (sc *Subcommand) ensureNoConflictWithBuiltinVersion() {
for _, f := range sc.Flags {
if f.LongName == versionFlagLongName {
sc.exitBecauseOfVersionFlagConflict(f.LongName)
}
if f.ShortName == versionFlagLongName {
sc.exitBecauseOfVersionFlagConflict(f.ShortName)
}
}
}
// exitBecauseOfVersionFlagConflict exits the program with a message about how to prevent
// flags being defined from conflicting with the builtin flags.
func (sc *Subcommand) exitBecauseOfVersionFlagConflict(flagName string) {
fmt.Println(`Flag with name '` + flagName + `' conflicts with the internal --version flag in flaggy.
You must either change the flag's name, or disable flaggy's internal version
flag with 'flaggy.DefaultParser.ShowVersionWithVersionFlag = false'. If you are using
a custom parser, you must instead set '.ShowVersionWithVersionFlag = false' on it.`)
exitOrPanic(1)
}
// exitBecauseOfHelpFlagConflict exits the program with a message about how to prevent
// flags being defined from conflicting with the builtin flags.
func (sc *Subcommand) exitBecauseOfHelpFlagConflict(flagName string) {
fmt.Println(`Flag with name '` + flagName + `' conflicts with the internal --help or -h flag in flaggy.
You must either change the flag's name, or disable flaggy's internal help
flag with 'flaggy.DefaultParser.ShowHelpWithHFlag = false'. If you are using
a custom parser, you must instead set '.ShowHelpWithHFlag = false' on it.`)
exitOrPanic(1)
}

View file

@ -1,23 +0,0 @@
Copyright (c) 2011 Keith Rarick
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall
be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -1,16 +0,0 @@
// Package pty provides functions for working with Unix terminals.
package pty
import (
"errors"
"os"
)
// ErrUnsupported is returned if a function is not
// available on the current platform.
var ErrUnsupported = errors.New("unsupported")
// Opens a pty and its corresponding tty.
func Open() (pty, tty *os.File, err error) {
return open()
}

View file

@ -1,13 +0,0 @@
// +build !windows
package pty
import "syscall"
func ioctl(fd, cmd, ptr uintptr) error {
_, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, cmd, ptr)
if e != 0 {
return e
}
return nil
}

View file

@ -1,39 +0,0 @@
// +build darwin dragonfly freebsd netbsd openbsd
package pty
// from <sys/ioccom.h>
const (
_IOC_VOID uintptr = 0x20000000
_IOC_OUT uintptr = 0x40000000
_IOC_IN uintptr = 0x80000000
_IOC_IN_OUT uintptr = _IOC_OUT | _IOC_IN
_IOC_DIRMASK = _IOC_VOID | _IOC_OUT | _IOC_IN
_IOC_PARAM_SHIFT = 13
_IOC_PARAM_MASK = (1 << _IOC_PARAM_SHIFT) - 1
)
func _IOC_PARM_LEN(ioctl uintptr) uintptr {
return (ioctl >> 16) & _IOC_PARAM_MASK
}
func _IOC(inout uintptr, group byte, ioctl_num uintptr, param_len uintptr) uintptr {
return inout | (param_len&_IOC_PARAM_MASK)<<16 | uintptr(group)<<8 | ioctl_num
}
func _IO(group byte, ioctl_num uintptr) uintptr {
return _IOC(_IOC_VOID, group, ioctl_num, 0)
}
func _IOR(group byte, ioctl_num uintptr, param_len uintptr) uintptr {
return _IOC(_IOC_OUT, group, ioctl_num, param_len)
}
func _IOW(group byte, ioctl_num uintptr, param_len uintptr) uintptr {
return _IOC(_IOC_IN, group, ioctl_num, param_len)
}
func _IOWR(group byte, ioctl_num uintptr, param_len uintptr) uintptr {
return _IOC(_IOC_IN_OUT, group, ioctl_num, param_len)
}

View file

@ -1,65 +0,0 @@
package pty
import (
"errors"
"os"
"syscall"
"unsafe"
)
func open() (pty, tty *os.File, err error) {
pFD, err := syscall.Open("/dev/ptmx", syscall.O_RDWR|syscall.O_CLOEXEC, 0)
if err != nil {
return nil, nil, err
}
p := os.NewFile(uintptr(pFD), "/dev/ptmx")
// In case of error after this point, make sure we close the ptmx fd.
defer func() {
if err != nil {
_ = p.Close() // Best effort.
}
}()
sname, err := ptsname(p)
if err != nil {
return nil, nil, err
}
if err := grantpt(p); err != nil {
return nil, nil, err
}
if err := unlockpt(p); err != nil {
return nil, nil, err
}
t, err := os.OpenFile(sname, os.O_RDWR, 0)
if err != nil {
return nil, nil, err
}
return p, t, nil
}
func ptsname(f *os.File) (string, error) {
n := make([]byte, _IOC_PARM_LEN(syscall.TIOCPTYGNAME))
err := ioctl(f.Fd(), syscall.TIOCPTYGNAME, uintptr(unsafe.Pointer(&n[0])))
if err != nil {
return "", err
}
for i, c := range n {
if c == 0 {
return string(n[:i]), nil
}
}
return "", errors.New("TIOCPTYGNAME string not NUL-terminated")
}
func grantpt(f *os.File) error {
return ioctl(f.Fd(), syscall.TIOCPTYGRANT, 0)
}
func unlockpt(f *os.File) error {
return ioctl(f.Fd(), syscall.TIOCPTYUNLK, 0)
}

View file

@ -1,80 +0,0 @@
package pty
import (
"errors"
"os"
"strings"
"syscall"
"unsafe"
)
// same code as pty_darwin.go
func open() (pty, tty *os.File, err error) {
p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
if err != nil {
return nil, nil, err
}
// In case of error after this point, make sure we close the ptmx fd.
defer func() {
if err != nil {
_ = p.Close() // Best effort.
}
}()
sname, err := ptsname(p)
if err != nil {
return nil, nil, err
}
if err := grantpt(p); err != nil {
return nil, nil, err
}
if err := unlockpt(p); err != nil {
return nil, nil, err
}
t, err := os.OpenFile(sname, os.O_RDWR, 0)
if err != nil {
return nil, nil, err
}
return p, t, nil
}
func grantpt(f *os.File) error {
_, err := isptmaster(f.Fd())
return err
}
func unlockpt(f *os.File) error {
_, err := isptmaster(f.Fd())
return err
}
func isptmaster(fd uintptr) (bool, error) {
err := ioctl(fd, syscall.TIOCISPTMASTER, 0)
return err == nil, err
}
var (
emptyFiodgnameArg fiodgnameArg
ioctl_FIODNAME = _IOW('f', 120, unsafe.Sizeof(emptyFiodgnameArg))
)
func ptsname(f *os.File) (string, error) {
name := make([]byte, _C_SPECNAMELEN)
fa := fiodgnameArg{Name: (*byte)(unsafe.Pointer(&name[0])), Len: _C_SPECNAMELEN, Pad_cgo_0: [4]byte{0, 0, 0, 0}}
err := ioctl(f.Fd(), ioctl_FIODNAME, uintptr(unsafe.Pointer(&fa)))
if err != nil {
return "", err
}
for i, c := range name {
if c == 0 {
s := "/dev/" + string(name[:i])
return strings.Replace(s, "ptm", "pts", -1), nil
}
}
return "", errors.New("TIOCPTYGNAME string not NUL-terminated")
}

View file

@ -1,78 +0,0 @@
package pty
import (
"errors"
"os"
"syscall"
"unsafe"
)
func posixOpenpt(oflag int) (fd int, err error) {
r0, _, e1 := syscall.Syscall(syscall.SYS_POSIX_OPENPT, uintptr(oflag), 0, 0)
fd = int(r0)
if e1 != 0 {
err = e1
}
return fd, err
}
func open() (pty, tty *os.File, err error) {
fd, err := posixOpenpt(syscall.O_RDWR | syscall.O_CLOEXEC)
if err != nil {
return nil, nil, err
}
p := os.NewFile(uintptr(fd), "/dev/pts")
// In case of error after this point, make sure we close the pts fd.
defer func() {
if err != nil {
_ = p.Close() // Best effort.
}
}()
sname, err := ptsname(p)
if err != nil {
return nil, nil, err
}
t, err := os.OpenFile("/dev/"+sname, os.O_RDWR, 0)
if err != nil {
return nil, nil, err
}
return p, t, nil
}
func isptmaster(fd uintptr) (bool, error) {
err := ioctl(fd, syscall.TIOCPTMASTER, 0)
return err == nil, err
}
var (
emptyFiodgnameArg fiodgnameArg
ioctlFIODGNAME = _IOW('f', 120, unsafe.Sizeof(emptyFiodgnameArg))
)
func ptsname(f *os.File) (string, error) {
master, err := isptmaster(f.Fd())
if err != nil {
return "", err
}
if !master {
return "", syscall.EINVAL
}
const n = _C_SPECNAMELEN + 1
var (
buf = make([]byte, n)
arg = fiodgnameArg{Len: n, Buf: (*byte)(unsafe.Pointer(&buf[0]))}
)
if err := ioctl(f.Fd(), ioctlFIODGNAME, uintptr(unsafe.Pointer(&arg))); err != nil {
return "", err
}
for i, c := range buf {
if c == 0 {
return string(buf[:i]), nil
}
}
return "", errors.New("FIODGNAME string not NUL-terminated")
}

View file

@ -1,51 +0,0 @@
package pty
import (
"os"
"strconv"
"syscall"
"unsafe"
)
func open() (pty, tty *os.File, err error) {
p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
if err != nil {
return nil, nil, err
}
// In case of error after this point, make sure we close the ptmx fd.
defer func() {
if err != nil {
_ = p.Close() // Best effort.
}
}()
sname, err := ptsname(p)
if err != nil {
return nil, nil, err
}
if err := unlockpt(p); err != nil {
return nil, nil, err
}
t, err := os.OpenFile(sname, os.O_RDWR|syscall.O_NOCTTY, 0)
if err != nil {
return nil, nil, err
}
return p, t, nil
}
func ptsname(f *os.File) (string, error) {
var n _C_uint
err := ioctl(f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n)))
if err != nil {
return "", err
}
return "/dev/pts/" + strconv.Itoa(int(n)), nil
}
func unlockpt(f *os.File) error {
var u _C_int
// use TIOCSPTLCK with a zero valued arg to clear the slave pty lock
return ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u)))
}

View file

@ -1,33 +0,0 @@
package pty
import (
"os"
"syscall"
"unsafe"
)
func open() (pty, tty *os.File, err error) {
/*
* from ptm(4):
* The PTMGET command allocates a free pseudo terminal, changes its
* ownership to the caller, revokes the access privileges for all previous
* users, opens the file descriptors for the master and slave devices and
* returns them to the caller in struct ptmget.
*/
p, err := os.OpenFile("/dev/ptm", os.O_RDWR|syscall.O_CLOEXEC, 0)
if err != nil {
return nil, nil, err
}
defer p.Close()
var ptm ptmget
if err := ioctl(p.Fd(), uintptr(ioctl_PTMGET), uintptr(unsafe.Pointer(&ptm))); err != nil {
return nil, nil, err
}
pty = os.NewFile(uintptr(ptm.Cfd), "/dev/ptm")
tty = os.NewFile(uintptr(ptm.Sfd), "/dev/ptm")
return pty, tty, nil
}

View file

@ -1,11 +0,0 @@
// +build !linux,!darwin,!freebsd,!dragonfly,!openbsd
package pty
import (
"os"
)
func open() (pty, tty *os.File, err error) {
return nil, nil, ErrUnsupported
}

View file

@ -1,54 +0,0 @@
// +build !windows
package pty
import (
"os"
"os/exec"
"syscall"
)
// Start assigns a pseudo-terminal tty os.File to c.Stdin, c.Stdout,
// and c.Stderr, calls c.Start, and returns the File of the tty's
// corresponding pty.
func Start(c *exec.Cmd) (pty *os.File, err error) {
return StartWithSize(c, nil)
}
// StartWithSize assigns a pseudo-terminal tty os.File to c.Stdin, c.Stdout,
// and c.Stderr, calls c.Start, and returns the File of the tty's
// corresponding pty.
//
// This will resize the pty to the specified size before starting the command
func StartWithSize(c *exec.Cmd, sz *Winsize) (pty *os.File, err error) {
pty, tty, err := Open()
if err != nil {
return nil, err
}
defer tty.Close()
if sz != nil {
err = Setsize(pty, sz)
if err != nil {
pty.Close()
return nil, err
}
}
if c.Stdout == nil {
c.Stdout = tty
}
if c.Stderr == nil {
c.Stderr = tty
}
c.Stdin = tty
if c.SysProcAttr == nil {
c.SysProcAttr = &syscall.SysProcAttr{}
}
c.SysProcAttr.Setctty = true
c.SysProcAttr.Setsid = true
err = c.Start()
if err != nil {
pty.Close()
return nil, err
}
return pty, err
}

View file

@ -1,10 +0,0 @@
// +build ignore
package pty
import "C"
type (
_C_int C.int
_C_uint C.uint
)

View file

@ -1,17 +0,0 @@
// +build ignore
package pty
/*
#define _KERNEL
#include <sys/conf.h>
#include <sys/param.h>
#include <sys/filio.h>
*/
import "C"
const (
_C_SPECNAMELEN = C.SPECNAMELEN /* max length of devicename */
)
type fiodgnameArg C.struct_fiodname_args

View file

@ -1,15 +0,0 @@
// +build ignore
package pty
/*
#include <sys/param.h>
#include <sys/filio.h>
*/
import "C"
const (
_C_SPECNAMELEN = C.SPECNAMELEN /* max length of devicename */
)
type fiodgnameArg C.struct_fiodgname_arg

View file

@ -1,14 +0,0 @@
// +build ignore
package pty
/*
#include <sys/time.h>
#include <stdlib.h>
#include <sys/tty.h>
*/
import "C"
type ptmget C.struct_ptmget
var ioctl_PTMGET = C.PTMGET

View file

@ -1,64 +0,0 @@
// +build !windows
package pty
import (
"os"
"syscall"
"unsafe"
)
// InheritSize applies the terminal size of master to slave. This should be run
// in a signal handler for syscall.SIGWINCH to automatically resize the slave when
// the master receives a window size change notification.
func InheritSize(master, slave *os.File) error {
size, err := GetsizeFull(master)
if err != nil {
return err
}
err = Setsize(slave, size)
if err != nil {
return err
}
return nil
}
// Setsize resizes t to s.
func Setsize(t *os.File, ws *Winsize) error {
return windowRectCall(ws, t.Fd(), syscall.TIOCSWINSZ)
}
// GetsizeFull returns the full terminal size description.
func GetsizeFull(t *os.File) (size *Winsize, err error) {
var ws Winsize
err = windowRectCall(&ws, t.Fd(), syscall.TIOCGWINSZ)
return &ws, err
}
// Getsize returns the number of rows (lines) and cols (positions
// in each line) in terminal t.
func Getsize(t *os.File) (rows, cols int, err error) {
ws, err := GetsizeFull(t)
return int(ws.Rows), int(ws.Cols), err
}
// Winsize describes the terminal size.
type Winsize struct {
Rows uint16 // ws_row: Number of rows (in cells)
Cols uint16 // ws_col: Number of columns (in cells)
X uint16 // ws_xpixel: Width in pixels
Y uint16 // ws_ypixel: Height in pixels
}
func windowRectCall(ws *Winsize, fd, a2 uintptr) error {
_, _, errno := syscall.Syscall(
syscall.SYS_IOCTL,
fd,
a2,
uintptr(unsafe.Pointer(ws)),
)
if errno != 0 {
return syscall.Errno(errno)
}
return nil
}

View file

@ -1,9 +0,0 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types.go
package pty
type (
_C_int int32
_C_uint uint32
)

View file

@ -1,9 +0,0 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types.go
package pty
type (
_C_int int32
_C_uint uint32
)

View file

@ -1,9 +0,0 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types.go
package pty
type (
_C_int int32
_C_uint uint32
)

View file

@ -1,11 +0,0 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types.go
// +build arm64
package pty
type (
_C_int int32
_C_uint uint32
)

View file

@ -1,14 +0,0 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_dragonfly.go
package pty
const (
_C_SPECNAMELEN = 0x3f
)
type fiodgnameArg struct {
Name *byte
Len uint32
Pad_cgo_0 [4]byte
}

View file

@ -1,13 +0,0 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_freebsd.go
package pty
const (
_C_SPECNAMELEN = 0x3f
)
type fiodgnameArg struct {
Len int32
Buf *byte
}

View file

@ -1,14 +0,0 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_freebsd.go
package pty
const (
_C_SPECNAMELEN = 0x3f
)
type fiodgnameArg struct {
Len int32
Pad_cgo_0 [4]byte
Buf *byte
}

View file

@ -1,13 +0,0 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_freebsd.go
package pty
const (
_C_SPECNAMELEN = 0x3f
)
type fiodgnameArg struct {
Len int32
Buf *byte
}

View file

@ -1,12 +0,0 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types.go
// +build linux
// +build mips mipsle mips64 mips64le
package pty
type (
_C_int int32
_C_uint uint32
)

View file

@ -1,13 +0,0 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_openbsd.go
package pty
type ptmget struct {
Cfd int32
Sfd int32
Cn [16]int8
Sn [16]int8
}
var ioctl_PTMGET = 0x40287401

View file

@ -1,13 +0,0 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_openbsd.go
package pty
type ptmget struct {
Cfd int32
Sfd int32
Cn [16]int8
Sn [16]int8
}
var ioctl_PTMGET = 0x40287401

View file

@ -1,11 +0,0 @@
// +build ppc64
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types.go
package pty
type (
_C_int int32
_C_uint uint32
)

View file

@ -1,11 +0,0 @@
// +build ppc64le
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types.go
package pty
type (
_C_int int32
_C_uint uint32
)

View file

@ -1,11 +0,0 @@
// +build s390x
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types.go
package pty
type (
_C_int int32
_C_uint uint32
)