Vendored external dependencies. Thx SamWhited.

http_upload
Martin Dosch 6 years ago
parent 17a4d864bb
commit f9af5a75d3

@ -0,0 +1,5 @@
language: go
go:
- tip
script:
- go test

@ -0,0 +1,27 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -0,0 +1,6 @@
go-xmpp
=======
go xmpp library (original was written by russ cox )
[Documentation](https://godoc.org/github.com/mattn/go-xmpp)

@ -0,0 +1,112 @@
package main
import (
"crypto/tls"
"github.com/mattn/go-gtk/gtk"
"github.com/mattn/go-xmpp"
"log"
"os"
"strings"
)
func main() {
gtk.Init(&os.Args)
window := gtk.NewWindow(gtk.WINDOW_TOPLEVEL)
window.SetTitle("GoTalk")
window.Connect("destroy", func() {
gtk.MainQuit()
})
vbox := gtk.NewVBox(false, 1)
scrolledwin := gtk.NewScrolledWindow(nil, nil)
textview := gtk.NewTextView()
textview.SetEditable(false)
textview.SetCursorVisible(false)
scrolledwin.Add(textview)
vbox.Add(scrolledwin)
buffer := textview.GetBuffer()
entry := gtk.NewEntry()
vbox.PackEnd(entry, false, false, 0)
window.Add(vbox)
window.SetSizeRequest(300, 400)
window.ShowAll()
dialog := gtk.NewDialog()
dialog.SetTitle(window.GetTitle())
sgroup := gtk.NewSizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
hbox := gtk.NewHBox(false, 1)
dialog.GetVBox().Add(hbox)
label := gtk.NewLabel("username:")
sgroup.AddWidget(label)
hbox.Add(label)
username := gtk.NewEntry()
hbox.Add(username)
hbox = gtk.NewHBox(false, 1)
dialog.GetVBox().Add(hbox)
label = gtk.NewLabel("password:")
sgroup.AddWidget(label)
hbox.Add(label)
password := gtk.NewEntry()
password.SetVisibility(false)
hbox.Add(password)
dialog.AddButton(gtk.STOCK_OK, gtk.RESPONSE_OK)
dialog.AddButton(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
dialog.SetDefaultResponse(gtk.RESPONSE_OK)
dialog.SetTransientFor(window)
dialog.ShowAll()
res := dialog.Run()
username_ := username.GetText()
password_ := password.GetText()
dialog.Destroy()
if res != gtk.RESPONSE_OK {
os.Exit(0)
}
xmpp.DefaultConfig = tls.Config{
ServerName: "talk.google.com",
InsecureSkipVerify: false,
}
talk, err := xmpp.NewClient("talk.google.com:443", username_, password_, false)
if err != nil {
log.Fatal(err)
}
entry.Connect("activate", func() {
text := entry.GetText()
tokens := strings.SplitN(text, " ", 2)
if len(tokens) == 2 {
func() {
defer recover()
talk.Send(xmpp.Chat{Remote: tokens[0], Type: "chat", Text: tokens[1]})
entry.SetText("")
}()
}
})
go func() {
for {
func() {
defer recover()
chat, err := talk.Recv()
if err != nil {
log.Fatal(err)
}
var iter gtk.TextIter
buffer.GetStartIter(&iter)
if msg, ok := chat.(xmpp.Chat); ok {
buffer.Insert(&iter, msg.Remote+": "+msg.Text+"\n")
}
}()
}
}()
gtk.Main()
}

@ -0,0 +1,94 @@
package main
import (
"bufio"
"crypto/tls"
"flag"
"fmt"
"github.com/mattn/go-xmpp"
"log"
"os"
"strings"
)
var server = flag.String("server", "talk.google.com:443", "server")
var username = flag.String("username", "", "username")
var password = flag.String("password", "", "password")
var status = flag.String("status", "xa", "status")
var statusMessage = flag.String("status-msg", "I for one welcome our new codebot overlords.", "status message")
var notls = flag.Bool("notls", false, "No TLS")
var debug = flag.Bool("debug", false, "debug output")
var session = flag.Bool("session", false, "use server session")
func serverName(host string) string {
return strings.Split(host, ":")[0]
}
func main() {
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "usage: example [options]\n")
flag.PrintDefaults()
os.Exit(2)
}
flag.Parse()
if *username == "" || *password == "" {
if *debug && *username == "" && *password == "" {
fmt.Fprintf(os.Stderr, "no username or password were given; attempting ANONYMOUS auth\n")
} else if *username != "" || *password != "" {
flag.Usage()
}
}
if !*notls {
xmpp.DefaultConfig = tls.Config{
ServerName: serverName(*server),
InsecureSkipVerify: false,
}
}
var talk *xmpp.Client
var err error
options := xmpp.Options{Host: *server,
User: *username,
Password: *password,
NoTLS: *notls,
Debug: *debug,
Session: *session,
Status: *status,
StatusMessage: *statusMessage,
}
talk, err = options.NewClient()
if err != nil {
log.Fatal(err)
}
go func() {
for {
chat, err := talk.Recv()
if err != nil {
log.Fatal(err)
}
switch v := chat.(type) {
case xmpp.Chat:
fmt.Println(v.Remote, v.Text)
case xmpp.Presence:
fmt.Println(v.From, v.Show)
}
}
}()
for {
in := bufio.NewReader(os.Stdin)
line, err := in.ReadString('\n')
if err != nil {
continue
}
line = strings.TrimRight(line, "\n")
tokens := strings.SplitN(line, " ", 2)
if len(tokens) == 2 {
talk.Send(xmpp.Chat{Remote: tokens[0], Type: "chat", Text: tokens[1]})
}
}
}

@ -0,0 +1,965 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// TODO(rsc):
// More precise error handling.
// Presence functionality.
// TODO(mattn):
// Add proxy authentication.
// Package xmpp implements a simple Google Talk client
// using the XMPP protocol described in RFC 3920 and RFC 3921.
package xmpp
import (
"bufio"
"bytes"
"crypto/md5"
"crypto/rand"
"crypto/tls"
"encoding/base64"
"encoding/binary"
"encoding/xml"
"errors"
"fmt"
"io"
"math/big"
"net"
"net/http"
"net/url"
"os"
"strings"
"time"
)
const (
nsStream = "http://etherx.jabber.org/streams"
nsTLS = "urn:ietf:params:xml:ns:xmpp-tls"
nsSASL = "urn:ietf:params:xml:ns:xmpp-sasl"
nsBind = "urn:ietf:params:xml:ns:xmpp-bind"
nsClient = "jabber:client"
nsSession = "urn:ietf:params:xml:ns:xmpp-session"
)
// Default TLS configuration options
var DefaultConfig tls.Config
// DebugWriter is the writer used to write debugging output to.
var DebugWriter io.Writer = os.Stderr
// Cookie is a unique XMPP session identifier
type Cookie uint64
func getCookie() Cookie {
var buf [8]byte
if _, err := rand.Reader.Read(buf[:]); err != nil {
panic("Failed to read random bytes: " + err.Error())
}
return Cookie(binary.LittleEndian.Uint64(buf[:]))
}
// Client holds XMPP connection opitons
type Client struct {
conn net.Conn // connection to server
jid string // Jabber ID for our connection
domain string
p *xml.Decoder
}
func (c *Client) JID() string {
return c.jid
}
func containsIgnoreCase(s, substr string) bool {
s, substr = strings.ToUpper(s), strings.ToUpper(substr)
return strings.Contains(s, substr)
}
func connect(host, user, passwd string) (net.Conn, error) {
addr := host
if strings.TrimSpace(host) == "" {
a := strings.SplitN(user, "@", 2)
if len(a) == 2 {
addr = a[1]
}
}
a := strings.SplitN(host, ":", 2)
if len(a) == 1 {
addr += ":5222"
}
proxy := os.Getenv("HTTP_PROXY")
if proxy == "" {
proxy = os.Getenv("http_proxy")
}
// test for no proxy, takes a comma separated list with substrings to match
if proxy != "" {
noproxy := os.Getenv("NO_PROXY")
if noproxy == "" {
noproxy = os.Getenv("no_proxy")
}
if noproxy != "" {
nplist := strings.Split(noproxy, ",")
for _, s := range nplist {
if containsIgnoreCase(addr, s) {
proxy = ""
break
}
}
}
}
if proxy != "" {
url, err := url.Parse(proxy)
if err == nil {
addr = url.Host
}
}
c, err := net.Dial("tcp", addr)
if err != nil {
return nil, err
}
if proxy != "" {
fmt.Fprintf(c, "CONNECT %s HTTP/1.1\r\n", host)
fmt.Fprintf(c, "Host: %s\r\n", host)
fmt.Fprintf(c, "\r\n")
br := bufio.NewReader(c)
req, _ := http.NewRequest("CONNECT", host, nil)
resp, err := http.ReadResponse(br, req)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
f := strings.SplitN(resp.Status, " ", 2)
return nil, errors.New(f[1])
}
}
return c, nil
}
// Options are used to specify additional options for new clients, such as a Resource.
type Options struct {
// Host specifies what host to connect to, as either "hostname" or "hostname:port"
// If host is not specified, the DNS SRV should be used to find the host from the domainpart of the JID.
// Default the port to 5222.
Host string
// User specifies what user to authenticate to the remote server.
User string
// Password supplies the password to use for authentication with the remote server.
Password string
// Resource specifies an XMPP client resource, like "bot", instead of accepting one
// from the server. Use "" to let the server generate one for your client.
Resource string
// OAuthScope provides go-xmpp the required scope for OAuth2 authentication.
OAuthScope string
// OAuthToken provides go-xmpp with the required OAuth2 token used to authenticate
OAuthToken string
// OAuthXmlNs provides go-xmpp with the required namespaced used for OAuth2 authentication. This is
// provided to the server as the xmlns:auth attribute of the OAuth2 authentication request.
OAuthXmlNs string
// TLS Config
TLSConfig *tls.Config
// InsecureAllowUnencryptedAuth permits authentication over a TCP connection that has not been promoted to
// TLS by STARTTLS; this could leak authentication information over the network, or permit man in the middle
// attacks.
InsecureAllowUnencryptedAuth bool
// NoTLS directs go-xmpp to not use TLS initially to contact the server; instead, a plain old unencrypted
// TCP connection should be used. (Can be combined with StartTLS to support STARTTLS-based servers.)
NoTLS bool
// StartTLS directs go-xmpp to STARTTLS if the server supports it; go-xmpp will automatically STARTTLS
// if the server requires it regardless of this option.
StartTLS bool
// Debug output
Debug bool
// Use server sessions
Session bool
// Presence Status
Status string
// Status message
StatusMessage string
}
// NewClient establishes a new Client connection based on a set of Options.
func (o Options) NewClient() (*Client, error) {
host := o.Host
c, err := connect(host, o.User, o.Password)
if err != nil {
return nil, err
}
if strings.LastIndex(o.Host, ":") > 0 {
host = host[:strings.LastIndex(o.Host, ":")]
}
client := new(Client)
if o.NoTLS {
client.conn = c
} else {
var tlsconn *tls.Conn
if o.TLSConfig != nil {
tlsconn = tls.Client(c, o.TLSConfig)
} else {
DefaultConfig.ServerName = host
newconfig := DefaultConfig
newconfig.ServerName = host
tlsconn = tls.Client(c, &newconfig)
}
if err = tlsconn.Handshake(); err != nil {
return nil, err
}
insecureSkipVerify := DefaultConfig.InsecureSkipVerify
if o.TLSConfig != nil {
insecureSkipVerify = o.TLSConfig.InsecureSkipVerify
}
if !insecureSkipVerify {
if err = tlsconn.VerifyHostname(host); err != nil {
return nil, err
}
}
client.conn = tlsconn
}
if err := client.init(&o); err != nil {
client.Close()
return nil, err
}
return client, nil
}
// NewClient creates a new connection to a host given as "hostname" or "hostname:port".
// If host is not specified, the DNS SRV should be used to find the host from the domainpart of the JID.
// Default the port to 5222.
func NewClient(host, user, passwd string, debug bool) (*Client, error) {
opts := Options{
Host: host,
User: user,
Password: passwd,
Debug: debug,
Session: false,
}
return opts.NewClient()
}
// NewClientNoTLS creates a new client without TLS
func NewClientNoTLS(host, user, passwd string, debug bool) (*Client, error) {
opts := Options{
Host: host,
User: user,
Password: passwd,
NoTLS: true,
Debug: debug,
Session: false,
}
return opts.NewClient()
}
// Close closes the XMPP connection
func (c *Client) Close() error {
if c.conn != (*tls.Conn)(nil) {
return c.conn.Close()
}
return nil
}
func saslDigestResponse(username, realm, passwd, nonce, cnonceStr, authenticate, digestURI, nonceCountStr string) string {
h := func(text string) []byte {
h := md5.New()
h.Write([]byte(text))
return h.Sum(nil)
}
hex := func(bytes []byte) string {
return fmt.Sprintf("%x", bytes)
}
kd := func(secret, data string) []byte {
return h(secret + ":" + data)
}
a1 := string(h(username+":"+realm+":"+passwd)) + ":" + nonce + ":" + cnonceStr
a2 := authenticate + ":" + digestURI
response := hex(kd(hex(h(a1)), nonce+":"+nonceCountStr+":"+cnonceStr+":auth:"+hex(h(a2))))
return response
}
func cnonce() string {
randSize := big.NewInt(0)
randSize.Lsh(big.NewInt(1), 64)
cn, err := rand.Int(rand.Reader, randSize)
if err != nil {
return ""
}
return fmt.Sprintf("%016x", cn)
}
func (c *Client) init(o *Options) error {
var domain string
var user string
a := strings.SplitN(o.User, "@", 2)
if len(o.User) > 0 {
if len(a) != 2 {
return errors.New("xmpp: invalid username (want user@domain): " + o.User)
}
user = a[0]
domain = a[1]
} // Otherwise, we'll be attempting ANONYMOUS
// Declare intent to be a jabber client and gather stream features.
f, err := c.startStream(o, domain)
if err != nil {
return err
}
// If the server requires we STARTTLS, attempt to do so.
if f, err = c.startTLSIfRequired(f, o, domain); err != nil {
return err
}
if o.User == "" && o.Password == "" {
foundAnonymous := false
for _, m := range f.Mechanisms.Mechanism {
if m == "ANONYMOUS" {
fmt.Fprintf(c.conn, "<auth xmlns='%s' mechanism='ANONYMOUS' />\n", nsSASL)
foundAnonymous = true
break
}
}
if !foundAnonymous {
return fmt.Errorf("ANONYMOUS authentication is not an option and username and password were not specified")
}
} else {
// Even digest forms of authentication are unsafe if we do not know that the host
// we are talking to is the actual server, and not a man in the middle playing
// proxy.
if !c.IsEncrypted() && !o.InsecureAllowUnencryptedAuth {
return errors.New("refusing to authenticate over unencrypted TCP connection")
}
mechanism := ""
for _, m := range f.Mechanisms.Mechanism {
if m == "X-OAUTH2" && o.OAuthToken != "" && o.OAuthScope != "" {
mechanism = m
// Oauth authentication: send base64-encoded \x00 user \x00 token.
raw := "\x00" + user + "\x00" + o.OAuthToken
enc := make([]byte, base64.StdEncoding.EncodedLen(len(raw)))
base64.StdEncoding.Encode(enc, []byte(raw))
fmt.Fprintf(c.conn, "<auth xmlns='%s' mechanism='X-OAUTH2' auth:service='oauth2' "+
"xmlns:auth='%s'>%s</auth>\n", nsSASL, o.OAuthXmlNs, enc)
break
}
if m == "PLAIN" {
mechanism = m
// Plain authentication: send base64-encoded \x00 user \x00 password.
raw := "\x00" + user + "\x00" + o.Password
enc := make([]byte, base64.StdEncoding.EncodedLen(len(raw)))
base64.StdEncoding.Encode(enc, []byte(raw))
fmt.Fprintf(c.conn, "<auth xmlns='%s' mechanism='PLAIN'>%s</auth>\n", nsSASL, enc)
break
}
if m == "DIGEST-MD5" {
mechanism = m
// Digest-MD5 authentication
fmt.Fprintf(c.conn, "<auth xmlns='%s' mechanism='DIGEST-MD5'/>\n", nsSASL)
var ch saslChallenge
if err = c.p.DecodeElement(&ch, nil); err != nil {
return errors.New("unmarshal <challenge>: " + err.Error())
}
b, err := base64.StdEncoding.DecodeString(string(ch))
if err != nil {
return err
}
tokens := map[string]string{}
for _, token := range strings.Split(string(b), ",") {
kv := strings.SplitN(strings.TrimSpace(token), "=", 2)
if len(kv) == 2 {
if kv[1][0] == '"' && kv[1][len(kv[1])-1] == '"' {
kv[1] = kv[1][1 : len(kv[1])-1]
}
tokens[kv[0]] = kv[1]
}
}
realm, _ := tokens["realm"]
nonce, _ := tokens["nonce"]
qop, _ := tokens["qop"]
charset, _ := tokens["charset"]
cnonceStr := cnonce()
digestURI := "xmpp/" + domain
nonceCount := fmt.Sprintf("%08x", 1)
digest := saslDigestResponse(user, realm, o.Password, nonce, cnonceStr, "AUTHENTICATE", digestURI, nonceCount)
message := "username=\"" + user + "\", realm=\"" + realm + "\", nonce=\"" + nonce + "\", cnonce=\"" + cnonceStr +
"\", nc=" + nonceCount + ", qop=" + qop + ", digest-uri=\"" + digestURI + "\", response=" + digest + ", charset=" + charset
fmt.Fprintf(c.conn, "<response xmlns='%s'>%s</response>\n", nsSASL, base64.StdEncoding.EncodeToString([]byte(message)))
var rspauth saslRspAuth
if err = c.p.DecodeElement(&rspauth, nil); err != nil {
return errors.New("unmarshal <challenge>: " + err.Error())
}
b, err = base64.StdEncoding.DecodeString(string(rspauth))
if err != nil {
return err
}
fmt.Fprintf(c.conn, "<response xmlns='%s'/>\n", nsSASL)
break
}
}
if mechanism == "" {
return fmt.Errorf("PLAIN authentication is not an option: %v", f.Mechanisms.Mechanism)
}
}
// Next message should be either success or failure.
name, val, err := next(c.p)
if err != nil {
return err
}
switch v := val.(type) {
case *saslSuccess:
case *saslFailure:
errorMessage := v.Text
if errorMessage == "" {
// v.Any is type of sub-element in failure,
// which gives a description of what failed if there was no text element
errorMessage = v.Any.Local
}
return errors.New("auth failure: " + errorMessage)
default:
return errors.New("expected <success> or <failure>, got <" + name.Local + "> in " + name.Space)
}
// Now that we're authenticated, we're supposed to start the stream over again.
// Declare intent to be a jabber client.
if f, err = c.startStream(o, domain); err != nil {
return err
}
// Generate a unique cookie
cookie := getCookie()
// Send IQ message asking to bind to the local user name.
if o.Resource == "" {
fmt.Fprintf(c.conn, "<iq type='set' id='%x'><bind xmlns='%s'></bind></iq>\n", cookie, nsBind)
} else {
fmt.Fprintf(c.conn, "<iq type='set' id='%x'><bind xmlns='%s'><resource>%s</resource></bind></iq>\n", cookie, nsBind, o.Resource)
}
var iq clientIQ
if err = c.p.DecodeElement(&iq, nil); err != nil {
return errors.New("unmarshal <iq>: " + err.Error())
}
if &iq.Bind == nil {
return errors.New("<iq> result missing <bind>")
}
c.jid = iq.Bind.Jid // our local id
c.domain = domain
if o.Session {
//if server support session, open it
fmt.Fprintf(c.conn, "<iq to='%s' type='set' id='%x'><session xmlns='%s'/></iq>", xmlEscape(domain), cookie, nsSession)
}
// We're connected and can now receive and send messages.
fmt.Fprintf(c.conn, "<presence xml:lang='en'><show>%s</show><status>%s</status></presence>", o.Status, o.StatusMessage)
return nil
}
// startTlsIfRequired examines the server's stream features and, if STARTTLS is required or supported, performs the TLS handshake.
// f will be updated if the handshake completes, as the new stream's features are typically different from the original.
func (c *Client) startTLSIfRequired(f *streamFeatures, o *Options, domain string) (*streamFeatures, error) {
// whether we start tls is a matter of opinion: the server's and the user's.
switch {
case f.StartTLS == nil:
// the server does not support STARTTLS
return f, nil
case f.StartTLS.Required != nil:
// the server requires STARTTLS.
case !o.StartTLS:
// the user wants STARTTLS and the server supports it.
}
var err error
fmt.Fprintf(c.conn, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>\n")
var k tlsProceed
if err = c.p.DecodeElement(&k, nil); err != nil {
return f, errors.New("unmarshal <proceed>: " + err.Error())
}
tc := o.TLSConfig
if tc == nil {
tc = new(tls.Config)
*tc = DefaultConfig
//TODO(scott): we should consider using the server's address or reverse lookup
tc.ServerName = domain
}
t := tls.Client(c.conn, tc)
if err = t.Handshake(); err != nil {
return f, errors.New("starttls handshake: " + err.Error())
}
c.conn = t
// restart our declaration of XMPP stream intentions.
tf, err := c.startStream(o, domain)
if err != nil {
return f, err
}
return tf, nil
}
// startStream will start a new XML decoder for the connection, signal the start of a stream to the server and verify that the server has
// also started the stream; if o.Debug is true, startStream will tee decoded XML data to stderr. The features advertised by the server
// will be returned.
func (c *Client) startStream(o *Options, domain string) (*streamFeatures, error) {
if o.Debug {
c.p = xml.NewDecoder(tee{c.conn, DebugWriter})
} else {
c.p = xml.NewDecoder(c.conn)
}
_, err := fmt.Fprintf(c.conn, "<?xml version='1.0'?>\n"+
"<stream:stream to='%s' xmlns='%s'\n"+
" xmlns:stream='%s' version='1.0'>\n",
xmlEscape(domain), nsClient, nsStream)
if err != nil {
return nil, err
}
// We expect the server to start a <stream>.
se, err := nextStart(c.p)
if err != nil {
return nil, err
}
if se.Name.Space != nsStream || se.Name.Local != "stream" {
return nil, fmt.Errorf("expected <stream> but got <%v> in %v", se.Name.Local, se.Name.Space)
}
// Now we're in the stream and can use Unmarshal.
// Next message should be <features> to tell us authentication options.
// See section 4.6 in RFC 3920.
f := new(streamFeatures)
if err = c.p.DecodeElement(f, nil); err != nil {
return f, errors.New("unmarshal <features>: " + err.Error())
}
return f, nil
}
// IsEncrypted will return true if the client is connected using a TLS transport, either because it used.
// TLS to connect from the outset, or because it successfully used STARTTLS to promote a TCP connection to TLS.
func (c *Client) IsEncrypted() bool {
_, ok := c.conn.(*tls.Conn)
return ok
}
// Chat is an incoming or outgoing XMPP chat message.
type Chat struct {
Remote string
Type string
Text string
Subject string
Thread string
Roster Roster
Other []string
OtherElem []XMLElement
Stamp time.Time
}
type Roster []Contact
type Contact struct {
Remote string
Name string
Group []string
}
// Presence is an XMPP presence notification.
type Presence struct {
From string
To string
Type string
Show string
Status string
}
type IQ struct {
ID string
From string
To string
Type string
Query []byte
}
// Recv waits to receive the next XMPP stanza.
// Return type is either a presence notification or a chat message.
func (c *Client) Recv() (stanza interface{}, err error) {
for {
_, val, err := next(c.p)
if err != nil {
return Chat{}, err
}
switch v := val.(type) {
case *clientMessage:
stamp, _ := time.Parse(
"2006-01-02T15:04:05Z",
v.Delay.Stamp,
)
chat := Chat{
Remote: v.From,
Type: v.Type,
Text: v.Body,
Subject: v.Subject,
Thread: v.Thread,
Other: v.OtherStrings(),
OtherElem: v.Other,
Stamp: stamp,
}
return chat, nil
case *clientQuery:
var r Roster
for _, item := range v.Item {
r = append(r, Contact{item.Jid, item.Name, item.Group})
}
return Chat{Type: "roster", Roster: r}, nil
case *clientPresence:
return Presence{v.From, v.To, v.Type, v.Show, v.Status}, nil
case *clientIQ:
// TODO check more strictly
if bytes.Equal(bytes.TrimSpace(v.Query), []byte(`<ping xmlns='urn:xmpp:ping'/>`)) || bytes.Equal(bytes.TrimSpace(v.Query), []byte(`<ping xmlns="urn:xmpp:ping"/>`)) {
err := c.SendResultPing(v.ID, v.From)
if err != nil {
return Chat{}, err
}
}
return IQ{ID: v.ID, From: v.From, To: v.To, Type: v.Type, Query: v.Query}, nil
}
}
}
// Send sends the message wrapped inside an XMPP message stanza body.
func (c *Client) Send(chat Chat) (n int, err error) {
var subtext = ``
var thdtext = ``
if chat.Subject != `` {
subtext = `<subject>` + xmlEscape(chat.Subject) + `</subject>`
}
if chat.Thread != `` {
thdtext = `<thread>` + xmlEscape(chat.Thread) + `</thread>`
}
stanza := "<message to='%s' type='%s' id='%s' xml:lang='en'>" + subtext + "<body>%s</body>" + thdtext + "</message>"
return fmt.Fprintf(c.conn, stanza,
xmlEscape(chat.Remote), xmlEscape(chat.Type), cnonce(), xmlEscape(chat.Text))
}
// SendOrg sends the original text without being wrapped in an XMPP message stanza.
func (c *Client) SendOrg(org string) (n int, err error) {
return fmt.Fprint(c.conn, org)
}
func (c *Client) SendPresence(presence Presence) (n int, err error) {
return fmt.Fprintf(c.conn, "<presence from='%s' to='%s'/>", xmlEscape(presence.From), xmlEscape(presence.To))
}
// SendKeepAlive sends a "whitespace keepalive" as described in chapter 4.6.1 of RFC6120.
func (c *Client) SendKeepAlive() (n int, err error) {
return fmt.Fprintf(c.conn, " ")
}
// SendHtml sends the message as HTML as defined by XEP-0071
func (c *Client) SendHtml(chat Chat) (n int, err error) {
return fmt.Fprintf(c.conn, "<message to='%s' type='%s' xml:lang='en'>"+
"<body>%s</body>"+
"<html xmlns='http://jabber.org/protocol/xhtml-im'><body xmlns='http://www.w3.org/1999/xhtml'>%s</body></html></message>",
xmlEscape(chat.Remote), xmlEscape(chat.Type), xmlEscape(chat.Text), chat.Text)
}
// Roster asks for the chat roster.
func (c *Client) Roster() error {
fmt.Fprintf(c.conn, "<iq from='%s' type='get' id='roster1'><query xmlns='jabber:iq:roster'/></iq>\n", xmlEscape(c.jid))
return nil
}
// RFC 3920 C.1 Streams name space
type streamFeatures struct {
XMLName xml.Name `xml:"http://etherx.jabber.org/streams features"`
StartTLS *tlsStartTLS
Mechanisms saslMechanisms
Bind bindBind
Session bool
}
type streamError struct {
XMLName xml.Name `xml:"http://etherx.jabber.org/streams error"`
Any xml.Name
Text string
}
// RFC 3920 C.3 TLS name space
type tlsStartTLS struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-tls starttls"`
Required *string `xml:"required"`
}
type tlsProceed struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-tls proceed"`
}
type tlsFailure struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-tls failure"`
}
// RFC 3920 C.4 SASL name space
type saslMechanisms struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl mechanisms"`
Mechanism []string `xml:"mechanism"`
}
type saslAuth struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl auth"`
Mechanism string `xml:",attr"`
}
type saslChallenge string
type saslRspAuth string
type saslResponse string
type saslAbort struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl abort"`
}
type saslSuccess struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl success"`
}
type saslFailure struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl failure"`
Any xml.Name `xml:",any"`
Text string `xml:"text"`
}
// RFC 3920 C.5 Resource binding name space
type bindBind struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-bind bind"`
Resource string
Jid string `xml:"jid"`
}
// RFC 3921 B.1 jabber:client
type clientMessage struct {
XMLName xml.Name `xml:"jabber:client message"`
From string `xml:"from,attr"`
ID string `xml:"id,attr"`
To string `xml:"to,attr"`
Type string `xml:"type,attr"` // chat, error, groupchat, headline, or normal
// These should technically be []clientText, but string is much more convenient.
Subject string `xml:"subject"`
Body string `xml:"body"`
Thread string `xml:"thread"`
// Any hasn't matched element
Other []XMLElement `xml:",any"`
Delay Delay `xml:"delay"`
}
func (m *clientMessage) OtherStrings() []string {
a := make([]string, len(m.Other))
for i, e := range m.Other {
a[i] = e.String()
}
return a
}
type XMLElement struct {
XMLName xml.Name
InnerXML string `xml:",innerxml"`
}
func (e *XMLElement) String() string {
r := bytes.NewReader([]byte(e.InnerXML))
d := xml.NewDecoder(r)
var buf bytes.Buffer
for {
tok, err := d.Token()
if err != nil {
break
}
switch v := tok.(type) {
case xml.StartElement:
err = d.Skip()
case xml.CharData:
_, err = buf.Write(v)
}
if err != nil {
break
}
}
return buf.String()
}
type Delay struct {
Stamp string `xml:"stamp,attr"`
}
type clientText struct {
Lang string `xml:",attr"`
Body string `xml:"chardata"`
}
type clientPresence struct {
XMLName xml.Name `xml:"jabber:client presence"`
From string `xml:"from,attr"`
ID string `xml:"id,attr"`
To string `xml:"to,attr"`
Type string `xml:"type,attr"` // error, probe, subscribe, subscribed, unavailable, unsubscribe, unsubscribed
Lang string `xml:"lang,attr"`
Show string `xml:"show"` // away, chat, dnd, xa
Status string `xml:"status"` // sb []clientText
Priority string `xml:"priority,attr"`
Error *clientError
}
type clientIQ struct {
// info/query
XMLName xml.Name `xml:"jabber:client iq"`
From string `xml:"from,attr"`
ID string `xml:"id,attr"`
To string `xml:"to,attr"`
Type string `xml:"type,attr"` // error, get, result, set
Query []byte `xml:",innerxml"`
Error clientError
Bind bindBind
}
type clientError struct {
XMLName xml.Name `xml:"jabber:client error"`
Code string `xml:",attr"`
Type string `xml:",attr"`
Any xml.Name
Text string
}
type clientQuery struct {
Item []rosterItem
}
type rosterItem struct {
XMLName xml.Name `xml:"jabber:iq:roster item"`
Jid string `xml:",attr"`
Name string `xml:",attr"`
Subscription string `xml:",attr"`
Group []string
}
// Scan XML token stream to find next StartElement.
func nextStart(p *xml.Decoder) (xml.StartElement, error) {
for {
t, err := p.Token()
if err != nil || t == nil {
return xml.StartElement{}, err
}
switch t := t.(type) {
case xml.StartElement:
return t, nil
}
}
}
// Scan XML token stream for next element and save into val.
// If val == nil, allocate new element based on proto map.
// Either way, return val.
func next(p *xml.Decoder) (xml.Name, interface{}, error) {
// Read start element to find out what type we want.
se, err := nextStart(p)
if err != nil {
return xml.Name{}, nil, err
}
// Put it in an interface and allocate one.
var nv interface{}
switch se.Name.Space + " " + se.Name.Local {
case nsStream + " features":
nv = &streamFeatures{}
case nsStream + " error":
nv = &streamError{}
case nsTLS + " starttls":
nv = &tlsStartTLS{}
case nsTLS + " proceed":
nv = &tlsProceed{}
case nsTLS + " failure":
nv = &tlsFailure{}
case nsSASL + " mechanisms":
nv = &saslMechanisms{}
case nsSASL + " challenge":
nv = ""
case nsSASL + " response":
nv = ""
case nsSASL + " abort":
nv = &saslAbort{}
case nsSASL + " success":
nv = &saslSuccess{}
case nsSASL + " failure":
nv = &saslFailure{}
case nsBind + " bind":
nv = &bindBind{}
case nsClient + " message":
nv = &clientMessage{}
case nsClient + " presence":
nv = &clientPresence{}
case nsClient + " iq":
nv = &clientIQ{}
case nsClient + " error":
nv = &clientError{}
default:
return xml.Name{}, nil, errors.New("unexpected XMPP message " +
se.Name.Space + " <" + se.Name.Local + "/>")
}
// Unmarshal into that storage.
if err = p.DecodeElement(nv, &se); err != nil {
return xml.Name{}, nil, err
}
return se.Name, nv, err
}
func xmlEscape(s string) string {
var b bytes.Buffer
xml.Escape(&b, []byte(s))
return b.String()
}
type tee struct {
r io.Reader
w io.Writer
}
func (t tee) Read(p []byte) (n int, err error) {
n, err = t.r.Read(p)
if n > 0 {
t.w.Write(p[0:n])
t.w.Write([]byte("\n"))
}
return
}

@ -0,0 +1,31 @@
package xmpp
import (
"fmt"
"strconv"
)
const IQTypeGet = "get"
const IQTypeSet = "set"
const IQTypeResult = "result"
func (c *Client) Discovery() (string, error) {
const namespace = "http://jabber.org/protocol/disco#items"
// use getCookie for a pseudo random id.
reqID := strconv.FormatUint(uint64(getCookie()), 10)
return c.RawInformationQuery(c.jid, c.domain, reqID, IQTypeGet, namespace, "")
}
// RawInformationQuery sends an information query request to the server.
func (c *Client) RawInformationQuery(from, to, id, iqType, requestNamespace, body string) (string, error) {
const xmlIQ = "<iq from='%s' to='%s' id='%s' type='%s'><query xmlns='%s'>%s</query></iq>"
_, err := fmt.Fprintf(c.conn, xmlIQ, xmlEscape(from), xmlEscape(to), id, iqType, requestNamespace, body)
return id, err
}
// rawInformation send a IQ request with the the payload body to the server
func (c *Client) RawInformation(from, to, id, iqType, body string) (string, error) {
const xmlIQ = "<iq from='%s' to='%s' id='%s' type='%s'>%s</iq>"
_, err := fmt.Fprintf(c.conn, xmlIQ, xmlEscape(from), xmlEscape(to), id, iqType, body)
return id, err
}

@ -0,0 +1,135 @@
// Copyright 2013 Flo Lauber <dev@qatfy.at>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// TODO(flo):
// - support password protected MUC rooms
// - cleanup signatures of join/leave functions
package xmpp
import (
"errors"
"fmt"
"time"
)
const (
nsMUC = "http://jabber.org/protocol/muc"
nsMUCUser = "http://jabber.org/protocol/muc#user"
NoHistory = 0
CharHistory = 1
StanzaHistory = 2
SecondsHistory = 3
SinceHistory = 4
)
// Send sends room topic wrapped inside an XMPP message stanza body.
func (c *Client) SendTopic(chat Chat) (n int, err error) {
return fmt.Fprintf(c.conn, "<message to='%s' type='%s' xml:lang='en'>"+"<subject>%s</subject></message>",
xmlEscape(chat.Remote), xmlEscape(chat.Type), xmlEscape(chat.Text))
}
func (c *Client) JoinMUCNoHistory(jid, nick string) (n int, err error) {
if nick == "" {
nick = c.jid
}
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n"+
"<x xmlns='%s'>"+
"<history maxchars='0'/></x>\n"+
"</presence>",
xmlEscape(jid), xmlEscape(nick), nsMUC)
}
// xep-0045 7.2
func (c *Client) JoinMUC(jid, nick string, history_type, history int, history_date *time.Time) (n int, err error) {
if nick == "" {
nick = c.jid
}
switch history_type {
case NoHistory:
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n"+
"<x xmlns='%s' />\n"+
"</presence>",
xmlEscape(jid), xmlEscape(nick), nsMUC)
case CharHistory:
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n"+
"<x xmlns='%s'>\n"+
"<history maxchars='%d'/></x>\n"+
"</presence>",
xmlEscape(jid), xmlEscape(nick), nsMUC, history)
case StanzaHistory:
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n"+
"<x xmlns='%s'>\n"+
"<history maxstanzas='%d'/></x>\n"+
"</presence>",
xmlEscape(jid), xmlEscape(nick), nsMUC, history)
case SecondsHistory:
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n"+
"<x xmlns='%s'>\n"+
"<history seconds='%d'/></x>\n"+
"</presence>",
xmlEscape(jid), xmlEscape(nick), nsMUC, history)
case SinceHistory:
if history_date != nil {
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n"+
"<x xmlns='%s'>\n"+
"<history since='%s'/></x>\n"+
"</presence>",
xmlEscape(jid), xmlEscape(nick), nsMUC, history_date.Format(time.RFC3339))
}
}
return 0, errors.New("Unknown history option")
}
// xep-0045 7.2.6
func (c *Client) JoinProtectedMUC(jid, nick string, password string, history_type, history int, history_date *time.Time) (n int, err error) {
if nick == "" {
nick = c.jid
}
switch history_type {
case NoHistory:
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n"+
"<x xmlns='%s'>\n"+
"<password>%s</password>"+
"</x>\n"+
"</presence>",
xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password))
case CharHistory:
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n"+
"<x xmlns='%s'>\n"+
"<password>%s</password>\n"+
"<history maxchars='%d'/></x>\n"+
"</presence>",
xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
case StanzaHistory:
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n"+
"<x xmlns='%s'>\n"+
"<password>%s</password>\n"+
"<history maxstanzas='%d'/></x>\n"+
"</presence>",
xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
case SecondsHistory:
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n"+
"<x xmlns='%s'>\n"+
"<password>%s</password>\n"+
"<history seconds='%d'/></x>\n"+
"</presence>",
xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
case SinceHistory:
if history_date != nil {
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n"+
"<x xmlns='%s'>\n"+
"<password>%s</password>\n"+
"<history since='%s'/></x>\n"+
"</presence>",
xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history_date.Format(time.RFC3339))
}
}
return 0, errors.New("Unknown history option")
}
// xep-0045 7.14
func (c *Client) LeaveMUC(jid string) (n int, err error) {
return fmt.Fprintf(c.conn, "<presence from='%s' to='%s' type='unavailable' />",
c.jid, xmlEscape(jid))
}

@ -0,0 +1,33 @@
package xmpp
import (
"fmt"
)
func (c *Client) PingC2S(jid, server string) error {
if jid == "" {
jid = c.jid
}
if server == "" {
server = c.domain
}
_, err := fmt.Fprintf(c.conn, "<iq from='%s' to='%s' id='c2s1' type='get'>\n"+
"<ping xmlns='urn:xmpp:ping'/>\n"+
"</iq>",
xmlEscape(jid), xmlEscape(server))
return err
}
func (c *Client) PingS2S(fromServer, toServer string) error {
_, err := fmt.Fprintf(c.conn, "<iq from='%s' to='%s' id='s2s1' type='get'>\n"+
"<ping xmlns='urn:xmpp:ping'/>\n"+
"</iq>",
xmlEscape(fromServer), xmlEscape(toServer))
return err
}
func (c *Client) SendResultPing(id, toServer string) error {
_, err := fmt.Fprintf(c.conn, "<iq type='result' to='%s' id='%s'/>",
xmlEscape(toServer), xmlEscape(id))
return err
}

@ -0,0 +1,20 @@
package xmpp
import (
"fmt"
)
func (c *Client) ApproveSubscription(jid string) {
fmt.Fprintf(c.conn, "<presence to='%s' type='subscribed'/>",
xmlEscape(jid))
}
func (c *Client) RevokeSubscription(jid string) {
fmt.Fprintf(c.conn, "<presence to='%s' type='unsubscribed'/>",
xmlEscape(jid))
}
func (c *Client) RequestSubscription(jid string) {
fmt.Fprintf(c.conn, "<presence to='%s' type='subscribe'/>",
xmlEscape(jid))
}

@ -0,0 +1,116 @@
package xmpp
import (
"bytes"
"encoding/xml"
"io"
"net"
"reflect"
"strings"
"testing"
"time"
)
type localAddr struct{}
func (a *localAddr) Network() string {
return "tcp"
}
func (addr *localAddr) String() string {
return "localhost:5222"
}
type testConn struct {
*bytes.Buffer
}
func tConnect(s string) net.Conn {
var conn testConn
conn.Buffer = bytes.NewBufferString(s)
return &conn
}
func (*testConn) Close() error {
return nil
}
func (*testConn) LocalAddr() net.Addr {
return &localAddr{}
}
func (*testConn) RemoteAddr() net.Addr {
return &localAddr{}
}
func (*testConn) SetDeadline(time.Time) error {
return nil
}
func (*testConn) SetReadDeadline(time.Time) error {
return nil
}
func (*testConn) SetWriteDeadline(time.Time) error {
return nil
}
var text = strings.TrimSpace(`
<message xmlns="jabber:client" id="3" type="error" to="123456789@gcm.googleapis.com/ABC">
<gcm xmlns="google:mobile:data">
{"random": "&lt;text&gt;"}
</gcm>
<error code="400" type="modify">
<bad-request xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>
<text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas">
InvalidJson: JSON_PARSING_ERROR : Missing Required Field: message_id\n
</text>
</error>
</message>
`)
func TestStanzaError(t *testing.T) {
var c Client
c.conn = tConnect(text)
c.p = xml.NewDecoder(c.conn)
v, err := c.Recv()
if err != nil {
t.Fatalf("Recv() = %v", err)
}
chat := Chat{
Type: "error",
Other: []string{
"\n\t\t{\"random\": \"<text>\"}\n\t",
"\n\t\t\n\t\t\n\t",
},
OtherElem: []XMLElement{
XMLElement{
XMLName: xml.Name{Space: "google:mobile:data", Local: "gcm"},
InnerXML: "\n\t\t{\"random\": \"&lt;text&gt;\"}\n\t",
},
XMLElement{
XMLName: xml.Name{Space: "jabber:client", Local: "error"},
InnerXML: `
<bad-request xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>
<text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas">
InvalidJson: JSON_PARSING_ERROR : Missing Required Field: message_id\n
</text>
`,
},
},
}
if !reflect.DeepEqual(v, chat) {
t.Errorf("Recv() = %#v; want %#v", v, chat)
}
}
func TestEOFError(t *testing.T) {
var c Client
c.conn = tConnect("")
c.p = xml.NewDecoder(c.conn)
_, err := c.Recv()
if err != io.EOF {
t.Errorf("Recv() did not return io.EOF on end of input stream")
}
}

@ -0,0 +1 @@
Paul Borman <paul@borman.com>

@ -0,0 +1,10 @@
# How to contribute
We definitely welcome patches and contribution to this project!
### Legal requirements
In order to protect both you and ourselves, you will need to sign the
[Contributor License Agreement](https://cla.developers.google.com/clas).
You may have already signed it for other Google projects.

@ -0,0 +1,27 @@
Copyright (c) 2017 Google Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google, nor the names of other
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -0,0 +1,226 @@
# getopt
Package getopt provides traditional getopt processing for implementing
commands that use traditional command lines. The standard Go flag package
cannot be used to write a program that parses flags the way ls or ssh does,
for example. There are two versions, v1 and v2, both named getopt, that
use the following import paths:
```
"github.com/pborman/getopt" // version 1
"github.com/pborman/getopt/v2" // version 2
```
This README describes version 2 of the package, which has a simplified API.
## Usage
Getopt supports functionality found in both the standard BSD getopt as well
as (one of the many versions of) the GNU getopt_long. Being a Go package,
this package makes common usage easy, but still enables more controlled usage
if needed.
Typical usage:
```
Declare flags and have getopt return pointers to the values.
helpFlag := getopt.Bool('?', "display help")
cmdFlag := getopt.StringLong("command", 'c', "default", "the command)
Declare flags against existing variables.
var {
fileName = "/the/default/path"
timeout = time.Second * 5
verbose bool
}
func init() {
getopt.Flag(&verbose, 'v', "be verbose")
getopt.FlagLong(&fileName, "path", 0, "the path")
getopt.FlagLong(&timeout, "timeout", 't', "some timeout")
}
func main() {
Parse the program arguments
getopt.Parse()
Get the remaining positional parameters
args := getopt.Args()
...
```
If you don't want the program to exit on error, use getopt.Getopt:
```
err := getopt.Getopt(nil)
if err != nil {
code to handle error
fmt.Fprintln(os.Stderr, err)
}
```
## Flag Syntax
Support is provided for both short (-f) and long (--flag) options. A single
option may have both a short and a long name. Each option may be a flag or a
value. A value takes an argument.
Declaring no long names causes this package to process arguments like the
traditional BSD getopt.
Short flags may be combined into a single parameter. For example, "-a -b -c"
may also be expressed "-abc". Long flags must stand on their own "--alpha
--beta"
Values require an argument. For short options the argument may either be
immediately following the short name or as the next argument. Only one short
value may be combined with short flags in a single argument; the short value
must be after all short flags. For example, if f is a flag and v is a value,
then:
```
-vvalue (sets v to "value")
-v value (sets v to "value")
-fvvalue (sets f, and sets v to "value")
-fv value (sets f, and sets v to "value")
-vf value (set v to "f" and value is the first parameter)
```
For the long value option val:
```
--val value (sets val to "value")
--val=value (sets val to "value")
--valvalue (invalid option "valvalue")
```
Values with an optional value only set the value if the value is part of the
same argument. In any event, the option count is increased and the option is
marked as seen.
```
-v -f (sets v and f as being seen)
-vvalue -f (sets v to "value" and sets f)
--val -f (sets v and f as being seen)
--val=value -f (sets v to "value" and sets f)
```
There is no convience function defined for making the value optional. The
SetOptional method must be called on the actual Option.
```
v := String("val", 'v', "", "the optional v")
Lookup("v").SetOptional()
var s string
FlagLong(&s, "val", 'v', "the optional v).SetOptional()
```
Parsing continues until the first non-option or "--" is encountered.
The short name "-" can be used, but it either is specified as "-" or as part
of a group of options, for example "-f-". If there are no long options
specified then "--f" could also be used. If "-" is not declared as an option
then the single "-" will also terminate the option processing but unlike
"--", the "-" will be part of the remaining arguments.
## Advanced Usage
Normally the parsing is performed by calling the Parse function. If it is
important to see the order of the options then the Getopt function should be
used. The standard Parse function does the equivalent of:
```
func Parse() {
if err := getopt.Getopt(os.Args, nil); err != nil {
fmt.Fprintln(os.Stderr, err)
s.usage()
os.Exit(1)
}
}
```
When calling Getopt it is the responsibility of the caller to print any
errors.
Normally the default option set, CommandLine, is used. Other option sets may
be created with New.
After parsing, the sets Args will contain the non-option arguments. If an
error is encountered then Args will begin with argument that caused the
error.
It is valid to call a set's Parse a second time to amend the current set of
flags or values. As an example:
```
var a = getopt.Bool('a', "", "The a flag")
var b = getopt.Bool('b', "", "The a flag")
var cmd = ""
var opts = getopt.CommandLine
opts.Parse(os.Args)
if opts.NArgs() > 0 {
cmd = opts.Arg(0)
opts.Parse(opts.Args())
}
```
If called with set to { "prog", "-a", "cmd", "-b", "arg" } then both and and
b would be set, cmd would be set to "cmd", and opts.Args() would return {
"arg" }.
Unless an option type explicitly prohibits it, an option may appear more than
once in the arguments. The last value provided to the option is the value.
## Builtin Types
The Flag and FlagLong functions support most standard Go types. For the
list, see the description of FlagLong below for a list of supported types.
There are also helper routines to allow single line flag declarations. These
types are: Bool, Counter, Duration, Enum, Int16, Int32, Int64, Int, List,
Signed, String, Uint16, Uint32, Uint64, Uint, and Unsigned.
Each comes in a short and long flavor, e.g., Bool and BoolLong and include
functions to set the flags on the standard command line or for a specific Set
of flags.
Except for the Counter, Enum, Signed and Unsigned types, all of these types
can be declared using Flag and FlagLong by passing in a pointer to the
appropriate type.
## Declaring New Flag Types
A pointer to any type that implements the Value interface may be passed to
Flag or FlagLong.
## VALUEHELP
All non-flag options are created with a "valuehelp" as the last parameter.
Valuehelp should be 0, 1, or 2 strings. The first string, if provided, is
the usage message for the option. If the second string, if provided, is the
name to use for the value when displaying the usage. If not provided the
term "value" is assumed.
The usage message for the option created with
```
StringLong("option", 'o', "defval", "a string of letters")
```
is
```
-o, -option=value
```
while the usage message for the option created with
```
StringLong("option", 'o', "defval", "a string of letters", "string")
```
is
```
-o, -option=string
```

@ -0,0 +1,74 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strings"
)
type boolValue bool
func (b *boolValue) Set(value string, opt Option) error {
switch strings.ToLower(value) {
case "", "1", "true", "on", "t":
*b = true
case "0", "false", "off", "f":
*b = false
default:
return fmt.Errorf("invalid value for bool %s: %q", opt.Name(), value)
}
return nil
}
func (b *boolValue) String() string {
if *b {
return "true"
}
return "false"
}
// Bool creates a flag option that is a bool. Bools normally do not take a
// value however one can be assigned by using the long form of the option:
//
// --option=true
// --o=false
//
// Its value is case insenstive and one of true, false, t, f, on, off, t and 0.
func Bool(name rune, helpvalue ...string) *bool {
return CommandLine.Bool(name, helpvalue...)
}
func (s *Set) Bool(name rune, helpvalue ...string) *bool {
var p bool
s.BoolVarLong(&p, "", name, helpvalue...)
return &p
}
func BoolLong(name string, short rune, helpvalue ...string) *bool {
return CommandLine.BoolLong(name, short, helpvalue...)
}
func (s *Set) BoolLong(name string, short rune, helpvalue ...string) *bool {
var p bool
s.BoolVarLong(&p, name, short, helpvalue...)
return &p
}
func BoolVar(p *bool, name rune, helpvalue ...string) Option {
return CommandLine.BoolVar(p, name, helpvalue...)
}
func (s *Set) BoolVar(p *bool, name rune, helpvalue ...string) Option {
return s.BoolVarLong(p, "", name, helpvalue...)
}
func BoolVarLong(p *bool, name string, short rune, helpvalue ...string) Option {
return CommandLine.BoolVarLong(p, name, short, helpvalue...)
}
func (s *Set) BoolVarLong(p *bool, name string, short rune, helpvalue ...string) Option {
return s.VarLong((*boolValue)(p), name, short, helpvalue...).SetFlag()
}

@ -0,0 +1,106 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strings"
"testing"
)
var boolTests = []struct {
where string
in []string
f bool
fc int
opt bool
optc int
err string
}{
{
loc(),
[]string{},
false, 0,
false, 0,
"",
},
{
loc(),
[]string{"test", "-f", "--opt"},
true, 1,
true, 1,
"",
},
{
loc(),
[]string{"test", "--f", "--opt"},
true, 1,
true, 1,
"",
},
{
loc(),
[]string{"test", "-ff", "-f", "--opt", "--opt"},
true, 3,
true, 2,
"",
},
{
loc(),
[]string{"test", "--opt", "--opt=false"},
false, 0,
false, 2,
"",
},
{
loc(),
[]string{"test", "-f", "false"},
true, 1,
false, 0,
"",
},
{
loc(),
[]string{"test", "-f=false"},
true, 1,
false, 0,
"test: unknown option: -=\n",
},
{
loc(),
[]string{"test", "-f", "false"},
true, 1,
false, 0,
"",
},
}
func TestBool(t *testing.T) {
for x, tt := range boolTests {
reset()
f := Bool('f')
opt := BoolLong("opt", 0)
if strings.Index(tt.where, ":-") > 0 {
tt.where = fmt.Sprintf("#%d", x)
}
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if got, want := *f, tt.f; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
if got, want := *opt, tt.opt; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
if got, want := GetCount('f'), tt.fc; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
if got, want := GetCount("opt"), tt.optc; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
}
}

@ -0,0 +1,34 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"testing"
)
var breakupTests = []struct {
in string
max int
out []string
}{
{"", 8, []string{}},
{"a fox", 8, []string{"a fox"}},
{"a foxhound is sly", 2, []string{"a", "foxhound", "is", "sly"}},
{"a foxhound is sly", 5, []string{"a", "foxhound", "is", "sly"}},
{"a foxhound is sly", 6, []string{"a", "foxhound", "is sly"}},
{"a foxhound is sly", 7, []string{"a", "foxhound", "is sly"}},
{"a foxhound is sly", 8, []string{"a", "foxhound", "is sly"}},
{"a foxhound is sly", 9, []string{"a", "foxhound", "is sly"}},
{"a foxhound is sly", 10, []string{"a foxhound", "is sly"}},
}
func TestBreakup(t *testing.T) {
for x, tt := range breakupTests {
out := breakup(tt.in, tt.max)
if badSlice(out, tt.out) {
t.Errorf("#%d: got %v, want %v", x, out, tt.out)
}
}
}

@ -0,0 +1,81 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strconv"
)
type counterValue int
func (b *counterValue) Set(value string, opt Option) error {
if value == "" {
*b++
} else {
v, err := strconv.ParseInt(value, 0, strconv.IntSize)
if err != nil {
if e, ok := err.(*strconv.NumError); ok {
switch e.Err {
case strconv.ErrRange:
err = fmt.Errorf("value out of range: %s", value)
case strconv.ErrSyntax:
err = fmt.Errorf("not a valid number: %s", value)
}
}
return err
}
*b = counterValue(v)
}
return nil
}
func (b *counterValue) String() string {
return strconv.Itoa(int(*b))
}
// Counter creates a counting flag stored as an int. Each time the option
// is seen while parsing the value is incremented. The value of the counter
// may be explicitly set by using the long form:
//
// --counter=5
// --c=5
//
// Further instances of the option will increment from the set value.
func Counter(name rune, helpvalue ...string) *int {
return CommandLine.Counter(name, helpvalue...)
}
func (s *Set) Counter(name rune, helpvalue ...string) *int {
var p int
s.CounterVarLong(&p, "", name, helpvalue...)
return &p
}
func CounterLong(name string, short rune, helpvalue ...string) *int {
return CommandLine.CounterLong(name, short, helpvalue...)
}
func (s *Set) CounterLong(name string, short rune, helpvalue ...string) *int {
var p int
s.CounterVarLong(&p, name, short, helpvalue...)
return &p
}
func CounterVar(p *int, name rune, helpvalue ...string) Option {
return CommandLine.CounterVar(p, name, helpvalue...)
}
func (s *Set) CounterVar(p *int, name rune, helpvalue ...string) Option {
return s.CounterVarLong(p, "", name, helpvalue...)
}
func CounterVarLong(p *int, name string, short rune, helpvalue ...string) Option {
return CommandLine.CounterVarLong(p, name, short, helpvalue...)
}
func (s *Set) CounterVarLong(p *int, name string, short rune, helpvalue ...string) Option {
return s.VarLong((*counterValue)(p), name, short, helpvalue...).SetFlag()
}

@ -0,0 +1,91 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strings"
"testing"
)
var counterTests = []struct {
where string
in []string
c int
cnt int
err string
}{
{
loc(),
[]string{},
0,
0,
"",
},
{
loc(),
[]string{"test", "-c", "--cnt"},
1,
1,
"",
},
{
loc(),
[]string{"test", "-cc", "-c", "--cnt", "--cnt"},
3,
2,
"",
},
{
loc(),
[]string{"test", "--c=17", "--cnt=42"},
17,
42,
"",
},
{
loc(),
[]string{"test", "--cnt=false"},
0, 0,
"test: not a valid number: false\n",
},
}
func TestCounter(t *testing.T) {
for x, tt := range counterTests {
reset()
c := Counter('c')
cnt := CounterLong("cnt", 0)
if strings.Index(tt.where, ":-") > 0 {
tt.where = fmt.Sprintf("#%d", x)
}
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if got, want := *c, tt.c; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
if got, want := *cnt, tt.cnt; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
}
reset()
c := 5
opt := CounterVar(&c, 'c')
parse([]string{"test", "-c"})
if c != 6 {
t.Errorf("got %d, want 6", c)
}
if opt.Count() != 1 {
t.Errorf("got %d, want 1", c)
}
Reset()
if c != 5 {
t.Errorf("got %d, want 5", c)
}
}

@ -0,0 +1,56 @@
// Copyright 2015 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import "time"
type durationValue time.Duration
func (d *durationValue) Set(value string, opt Option) error {
v, err := time.ParseDuration(value)
if err != nil {
return err
}
*d = durationValue(v)
return nil
}
func (d *durationValue) String() string {
return time.Duration(*d).String()
}
// Duration creates an option that parses its value as a time.Duration.
func Duration(name rune, value time.Duration, helpvalue ...string) *time.Duration {
return CommandLine.Duration(name, value, helpvalue...)
}
func (s *Set) Duration(name rune, value time.Duration, helpvalue ...string) *time.Duration {
return s.DurationLong("", name, value, helpvalue...)
}
func DurationLong(name string, short rune, value time.Duration, helpvalue ...string) *time.Duration {
return CommandLine.DurationLong(name, short, value, helpvalue...)
}
func (s *Set) DurationLong(name string, short rune, value time.Duration, helpvalue ...string) *time.Duration {
s.DurationVarLong(&value, name, short, helpvalue...)
return &value
}
func DurationVar(p *time.Duration, name rune, helpvalue ...string) Option {
return CommandLine.DurationVar(p, name, helpvalue...)
}
func (s *Set) DurationVar(p *time.Duration, name rune, helpvalue ...string) Option {
return s.DurationVarLong(p, "", name, helpvalue...)
}
func DurationVarLong(p *time.Duration, name string, short rune, helpvalue ...string) Option {
return CommandLine.DurationVarLong(p, name, short, helpvalue...)
}
func (s *Set) DurationVarLong(p *time.Duration, name string, short rune, helpvalue ...string) Option {
return s.VarLong((*durationValue)(p), name, short, helpvalue...)
}

@ -0,0 +1,73 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strings"
"testing"
"time"
)
var durationTests = []struct {
where string
in []string
d time.Duration
dur time.Duration
err string
}{
{
loc(),
[]string{},
17, 42,
"",
},
{
loc(),
[]string{"test", "-d", "1s", "--duration", "2s"},
time.Second, 2 * time.Second,
"",
},
{
loc(),
[]string{"test", "-d1s", "-d2s"},
2 * time.Second, 42,
"",
},
{
loc(),
[]string{"test", "-d1"},
17, 42,
"test: time: missing unit in duration 1\n",
},
{
loc(),
[]string{"test", "--duration", "foo"},
17, 42,
"test: time: invalid duration foo\n",
},
}
func TestDuration(t *testing.T) {
for x, tt := range durationTests {
reset()
d := Duration('d', 17)
opt := DurationLong("duration", 0, 42)
if strings.Index(tt.where, ":-") > 0 {
tt.where = fmt.Sprintf("#%d", x)
}
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if got, want := *d, tt.d; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
if got, want := *opt, tt.dur; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
}
}

@ -0,0 +1,73 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import "errors"
type enumValue string
var enumValues = make(map[*enumValue]map[string]struct{})
func (s *enumValue) Set(value string, opt Option) error {
es, ok := enumValues[s]
if !ok || es == nil {
return errors.New("this option has no values")
}
if _, ok := es[value]; !ok {
return errors.New("invalid value: " + value)
}
*s = enumValue(value)
return nil
}
func (s *enumValue) String() string {
return string(*s)
}
// Enum creates an option that can only be set to one of the enumerated strings
// passed in values. Passing nil or an empty slice results in an option that
// will always fail.
func Enum(name rune, values []string, helpvalue ...string) *string {
return CommandLine.Enum(name, values, helpvalue...)
}
func (s *Set) Enum(name rune, values []string, helpvalue ...string) *string {
var p string
s.EnumVarLong(&p, "", name, values, helpvalue...)
return &p
}
func EnumLong(name string, short rune, values []string, helpvalue ...string) *string {
return CommandLine.EnumLong(name, short, values, helpvalue...)
}
func (s *Set) EnumLong(name string, short rune, values []string, helpvalue ...string) *string {
var p string
s.EnumVarLong(&p, name, short, values, helpvalue...)
return &p
}
// EnumVar creates an enum option that defaults to the starting value of *p.
// If *p is not found in values then a reset of this option will fail.
func EnumVar(p *string, name rune, values []string, helpvalue ...string) Option {
return CommandLine.EnumVar(p, name, values, helpvalue...)
}
func (s *Set) EnumVar(p *string, name rune, values []string, helpvalue ...string) Option {
return s.EnumVarLong(p, "", name, values, helpvalue...)
}
func EnumVarLong(p *string, name string, short rune, values []string, helpvalue ...string) Option {
return CommandLine.EnumVarLong(p, name, short, values, helpvalue...)
}
func (s *Set) EnumVarLong(p *string, name string, short rune, values []string, helpvalue ...string) Option {
m := make(map[string]struct{})
for _, v := range values {
m[v] = struct{}{}
}
enumValues[(*enumValue)(p)] = m
return s.VarLong((*enumValue)(p), name, short, helpvalue...)
}

@ -0,0 +1,66 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strings"
"testing"
)
var enumTests = []struct {
where string
in []string
values []string
out string
err string
}{
{
loc(),
nil,
[]string{},
"",
"",
},
{
loc(),
[]string{"test", "-e", "val1"},
[]string{"val1", "val2"},
"val1",
"",
},
{
loc(),
[]string{"test", "-e", "val1", "-e", "val2"},
[]string{"val1", "val2"},
"val2",
"",
},
{
loc(),
[]string{"test", "-e", "val3"},
[]string{"val1", "val2"},
"",
"test: invalid value: val3\n",
},
}
func TestEnum(t *testing.T) {
for x, tt := range enumTests {
if strings.Index(tt.where, ":-") > 0 {
tt.where = fmt.Sprintf("#%d", x)
}
reset()
e := Enum('e', tt.values)
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if *e != tt.out {
t.Errorf("%s: got %v, want %v", tt.where, *e, tt.out)
}
}
}

@ -0,0 +1,93 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import "fmt"
// An Error is returned by Getopt when it encounters an error.
type Error struct {
ErrorCode // General reason of failure.
Err error // The actual error.
Parameter string // Parameter passed to option, if any
Name string // Option that cause error, if any
}
// Error returns the error message, implementing the error interface.
func (i *Error) Error() string { return i.Err.Error() }
// An ErrorCode indicates what sort of error was encountered.
type ErrorCode int
const (
NoError = ErrorCode(iota)
UnknownOption // an invalid option was encountered
MissingParameter // the options parameter is missing
ExtraParameter // a value was set to a long flag
Invalid // attempt to set an invalid value
)
func (e ErrorCode) String() string {
switch e {
case UnknownOption:
return "unknow option"
case MissingParameter:
return "missing argument"
case ExtraParameter:
return "unxpected value"
case Invalid:
return "error setting value"
}
return "unknown error"
}
// unknownOption returns an Error indicating an unknown option was
// encountered.
func unknownOption(name interface{}) *Error {
i := &Error{ErrorCode: UnknownOption}
switch n := name.(type) {
case rune:
if n == '-' {
i.Name = "-"
} else {
i.Name = "-" + string(n)
}
case string:
i.Name = "--" + n
}
i.Err = fmt.Errorf("unknown option: %s", i.Name)
return i
}
// missingArg returns an Error inidicating option o was not passed
// a required paramter.
func missingArg(o Option) *Error {
return &Error{
ErrorCode: MissingParameter,
Name: o.Name(),
Err: fmt.Errorf("missing parameter for %s", o.Name()),
}
}
// extraArg returns an Error inidicating option o was passed the
// unexpected paramter value.
func extraArg(o Option, value string) *Error {
return &Error{
ErrorCode: ExtraParameter,
Name: o.Name(),
Parameter: value,
Err: fmt.Errorf("unexpected parameter passed to %s: %q", o.Name(), value),
}
}
// setError returns an Error inidicating option o and the specified
// error while setting it to value.
func setError(o Option, value string, err error) *Error {
return &Error{
ErrorCode: Invalid,
Name: o.Name(),
Parameter: value,
Err: err,
}
}

@ -0,0 +1,536 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package getopt (v1) provides traditional getopt processing for implementing
// commands that use traditional command lines. The standard Go flag package
// cannot be used to write a program that parses flags the way ls or ssh does,
// for example.
//
// A new version of this package (v2) (whose package name is also getopt) is
// available as:
//
// "github.com/pborman/getopt/v2"
//
// Getopt supports functionality found in both the standard BSD getopt as well
// as (one of the many versions of) the GNU getopt_long. Being a Go package,
// this package makes common usage easy, but still enables more controlled usage
// if needed.
//
// Typical usage:
//
// // Declare the flags to be used
// helpFlag := getopt.Bool('?', "display help")
// cmdFlag := getopt.StringLong("command", 'c', "", "the command)
//
// func main() {
// // Parse the program arguments
// getopt.Parse()
// // Get the remaining positional parameters
// args := getopt.Args()
//
// If you don't want the program to exit on error, use getopt.Getopt:
//
// err := getopt.Getopt(nil)
// if err != nil {
// // code to handle error
// fmt.Fprintln(os.Stderr, err)
// }
//
// Support is provided for both short (-f) and long (--flag) options. A single
// option may have both a short and a long name. Each option may be a flag or a
// value. A value takes an argument.
//
// Declaring no long names causes this package to process arguments like the
// traditional BSD getopt.
//
// Short flags may be combined into a single parameter. For example, "-a -b -c"
// may also be expressed "-abc". Long flags must stand on their own "--alpha
// --beta"
//
// Values require an argument. For short options the argument may either be
// immediately following the short name or as the next argument. Only one short
// value may be combined with short flags in a single argument; the short value
// must be after all short flags. For example, if f is a flag and v is a value,
// then:
//
// -vvalue (sets v to "value")
// -v value (sets v to "value")
// -fvvalue (sets f, and sets v to "value")
// -fv value (sets f, and sets v to "value")
// -vf value (set v to "f" and value is the first parameter)
//
// For the long value option val:
//
// --val value (sets val to "value")
// --val=value (sets val to "value")
// --valvalue (invalid option "valvalue")
//
// Values with an optional value only set the value if the value is part of the
// same argument. In any event, the option count is increased and the option is
// marked as seen.
//
// -v -f (sets v and f as being seen)
// -vvalue -f (sets v to "value" and sets f)
// --val -f (sets v and f as being seen)
// --val=value -f (sets v to "value" and sets f)
//
// There is no convience function defined for making the value optional. The
// SetOptional method must be called on the actual Option.
//
// v := String("val", 'v', "", "the optional v")
// Lookup("v").SetOptional()
//
// var s string
// StringVar(&s, "val", 'v', "the optional v).SetOptional()
//
// Parsing continues until the first non-option or "--" is encountered.
//
// The short name "-" can be used, but it either is specified as "-" or as part
// of a group of options, for example "-f-". If there are no long options
// specified then "--f" could also be used. If "-" is not declared as an option
// then the single "-" will also terminate the option processing but unlike
// "--", the "-" will be part of the remaining arguments.
//
// Normally the parsing is performed by calling the Parse function. If it is
// important to see the order of the options then the Getopt function should be
// used. The standard Parse function does the equivalent of:
//
// func Parse() {
// if err := getopt.Getopt(os.Args, nil); err != nil {
// fmt.Fprintln(os.Stderr, err)
// s.usage()
// os.Exit(1)
// }
//
// When calling Getopt it is the responsibility of the caller to print any
// errors.
//
// Normally the default option set, CommandLine, is used. Other option sets may
// be created with New.
//
// After parsing, the sets Args will contain the non-option arguments. If an
// error is encountered then Args will begin with argument that caused the
// error.
//
// It is valid to call a set's Parse a second time to amend the current set of
// flags or values. As an example:
//
// var a = getopt.Bool('a', "", "The a flag")
// var b = getopt.Bool('b', "", "The a flag")
// var cmd = ""
//
// var opts = getopt.CommandLine
//
// opts.Parse(os.Args)
// if opts.NArgs() > 0 {
// cmd = opts.Arg(0)
// opts.Parse(opts.Args())
// }
//
// If called with set to { "prog", "-a", "cmd", "-b", "arg" } then both and and
// b would be set, cmd would be set to "cmd", and opts.Args() would return {
// "arg" }.
//
// Unless an option type explicitly prohibits it, an option may appear more than
// once in the arguments. The last value provided to the option is the value.
//
// SYNTAX
//
// For each option type there are an unfortunately large number of ways, 8, to
// initialize the option. This number is derived from three attributes:
//
// 1) Short or Long name
// 2) Normal vs Var
// 3) Command Line vs Option Set
//
// The first two variations provide 4 signature:
//
// Option(name rune, [value type,] helpvalue... string)
// OptionLong(name string, short rune, [value type,] helpvalue... string)
// OptionVar(p *type, name rune, helpvalue... string)
// OptionVarLong(p *type, name string, short rune, helpvalue... string)
//
// Foo can actually be expressed in terms of FooLong:
//
// func Foo(name rune, value type, helpvalue... string) *type {
// return FooLong("", name, value, helpvalue...)
// }
//
// Normally Foo is used, unless long options are needed. Setting short to 0
// creates only a long option.
//
// The difference bentween Foo and FooVar is that you pass a pointer, p, to the
// location of the value to FooVar. The default value is simply *p. The
// initial value of *p is the defaut value of the option.
//
// Foo is actually a wrapper around FooVar:
//
// func Foo(name rune, value type, helpvalue... string) *type {
// p := value
// FooVar(&p, name, helpvalue... string)
// return &p
// }
//
//
// The third variation provides a top-level function and a method on a Set:
//
// func Option(...)
// func (s *Set) Option(...)
//
// The top-level function is simply:
//
// func Option(...) *type {
// return CommandLine.Option(...) {
// }
//
// To simplfy documentation, typically only the main top-level function is fully
// documented. The others will have documentation when there is something
// special about them.
//
// VALUEHELP
//
// All non-flag options are created with a "valuehelp" as the last parameter.
// Valuehelp should be 0, 1, or 2 strings. The first string, if provided, is
// the usage message for the option. If the second string, if provided, is the
// name to use for the value when displaying the usage. If not provided the
// term "value" is assumed.
//
// The usage message for the option created with
//
// StringLong("option", 'o', "defval", "a string of letters")
//
// is
//
// -o, -option=value
//
// StringLong("option", 'o', "defval", "a string of letters", "string")
//
// is
//
// -o, -option=string
package getopt
import (
"fmt"
"io"
"os"
"path"
"sort"
"strings"
)
// stderr allows tests to capture output to standard error.
var stderr io.Writer = os.Stderr
// exit allows tests to capture an os.Exit call
var exit = os.Exit
// DisplayWidth is used to determine where to split usage long lines.
var DisplayWidth = 80
// HelpColumn is the maximum column position that help strings start to display
// at. If the option usage is too long then the help string will be displayed
// on the next line. For example:
//
// -a this is the a flag
// -u, --under=location
// the u flag's usage is quite long
var HelpColumn = 20
// PrintUsage prints the usage of the program to w.
func (s *Set) PrintUsage(w io.Writer) {
sort.Sort(s.options)
flags := ""
// Build up the list of short flag names and also compute
// how to display the option in the longer help listing.
// We also keep track of the longest option usage string
// that is no more than HelpColumn-3 bytes (at which point
// we use two lines to display the help). The three
// is for the leading space and the two spaces before the
// help string.
for _, opt := range s.options {
if opt.name == "" {
opt.name = "value"
}
if opt.uname == "" {
opt.uname = opt.usageName()
}
if opt.flag && opt.short != 0 && opt.short != '-' {
flags += string(opt.short)
}
}
var opts []string
// The short option - is special
if s.shortOptions['-'] != nil {
opts = append(opts, "-")
}
// If we have a bundle of flags, add them to the list
if flags != "" {
opts = append(opts, "-"+flags)
}
// Now append all the long options and options that require
// values.
for _, opt := range s.options {
if opt.flag {
if opt.short != 0 {
continue
}
flags = "--" + opt.long
} else if opt.short != 0 {
flags = "-" + string(opt.short) + " " + opt.name
} else {
flags = "--" + string(opt.long) + " " + opt.name
}
opts = append(opts, flags)
}
flags = strings.Join(opts, "] [")
if flags != "" {
flags = " [" + flags + "]"
}
if s.parameters != "" {
flags += " " + s.parameters
}
fmt.Fprintf(w, "Usage: %s%s\n", s.program, flags)
s.PrintOptions(w)
}
// PrintOptions prints the list of options in s to w.
func (s *Set) PrintOptions(w io.Writer) {
sort.Sort(s.options)
max := 4
for _, opt := range s.options {
if opt.name == "" {
opt.name = "value"
}
if opt.uname == "" {
opt.uname = opt.usageName()
}
if max < len(opt.uname) && len(opt.uname) <= HelpColumn-3 {
max = len(opt.uname)
}
}
// Now print one or more usage lines per option.
for _, opt := range s.options {
if opt.uname != "" {
opt.help = strings.TrimSpace(opt.help)
if len(opt.help) == 0 {
fmt.Fprintf(w, " %s\n", opt.uname)
continue
}
help := strings.Split(opt.help, "\n")
// If they did not put in newlines then we will insert
// them to keep the help messages from wrapping.
if len(help) == 1 {
help = breakup(help[0], DisplayWidth-HelpColumn)
}
if len(opt.uname) <= max {
fmt.Fprintf(w, " %-*s %s\n", max, opt.uname, help[0])
help = help[1:]
} else {
fmt.Fprintf(w, " %s\n", opt.uname)
}
for _, s := range help {
fmt.Fprintf(w, " %-*s %s\n", max, " ", s)
}
}
}
}
// breakup breaks s up into strings no longer than max bytes.
func breakup(s string, max int) []string {
var a []string
for {
// strip leading spaces
for len(s) > 0 && s[0] == ' ' {
s = s[1:]
}
// If the option is no longer than the max just return it
if len(s) <= max {
if len(s) != 0 {
a = append(a, s)
}
return a
}
x := max
for s[x] != ' ' {
// the first word is too long?!
if x == 0 {
x = max
for x < len(s) && s[x] != ' ' {
x++
}
if x == len(s) {
x--
}
break
}
x--
}
for s[x] == ' ' {
x--
}
a = append(a, s[:x+1])
s = s[x+1:]
}
}
// Parse uses Getopt to parse args using the options set for s. The first
// element of args is used to assign the program for s if it is not yet set. On
// error, Parse displays the error message as well as a usage message on
// standard error and then exits the program.
func (s *Set) Parse(args []string) {
if err := s.Getopt(args, nil); err != nil {
fmt.Fprintln(stderr, err)
s.usage()
exit(1)
}
}
// Parse uses Getopt to parse args using the options set for s. The first
// element of args is used to assign the program for s if it is not yet set.
// Getop calls fn, if not nil, for each option parsed.
//
// Getopt returns nil when all options have been processed (a non-option
// argument was encountered, "--" was encountered, or fn returned false).
//
// On error getopt returns a refernce to an InvalidOption (which implements
// the error interface).
func (s *Set) Getopt(args []string, fn func(Option) bool) (err error) {
s.State = InProgress
defer func() {
if s.State == InProgress {
switch {
case err != nil:
s.State = Failure
case len(s.args) == 0:
s.State = EndOfArguments
default:
s.State = Unknown
}
}
}()
if fn == nil {
fn = func(Option) bool { return true }
}
if len(args) == 0 {
return nil
}
if s.program == "" {
s.program = path.Base(args[0])
}
args = args[1:]
Parsing:
for len(args) > 0 {
arg := args[0]
s.args = args
args = args[1:]
// end of options?
if arg == "" || arg[0] != '-' {
s.State = EndOfOptions
return nil
}
if arg == "-" {
goto ShortParsing
}
// explicitly request end of options?
if arg == "--" {
s.args = args
s.State = DashDash
return nil
}
// Long option processing
if len(s.longOptions) > 0 && arg[1] == '-' {
e := strings.IndexRune(arg, '=')
var value string
if e > 0 {
value = arg[e+1:]
arg = arg[:e]
}
opt := s.longOptions[arg[2:]]
// If we are processing long options then --f is -f
// if f is not defined as a long option.
// This lets you say --f=false
if opt == nil && len(arg[2:]) == 1 {
opt = s.shortOptions[rune(arg[2])]
}
if opt == nil {
return unknownOption(arg[2:])
}
opt.isLong = true
// If we require an option and did not have an =
// then use the next argument as an option.
if !opt.flag && e < 0 && !opt.optional {
if len(args) == 0 {
return missingArg(opt)
}
value = args[0]
args = args[1:]
}
opt.count++
if err := opt.value.Set(value, opt); err != nil {
return setError(opt, value, err)
}
if !fn(opt) {
s.State = Terminated
return nil
}
continue Parsing
}
// Short option processing
arg = arg[1:] // strip -
ShortParsing:
for i, c := range arg {
opt := s.shortOptions[c]
if opt == nil {
// In traditional getopt, if - is not registered
// as an option, a lone - is treated as
// if there were a -- in front of it.
if arg == "-" {
s.State = Dash
return nil
}
return unknownOption(c)
}
opt.isLong = false
opt.count++
var value string
if !opt.flag {
value = arg[1+i:]
if value == "" && !opt.optional {
if len(args) == 0 {
return missingArg(opt)
}
value = args[0]
args = args[1:]
}
}
if err := opt.value.Set(value, opt); err != nil {
return setError(opt, value, err)
}
if !fn(opt) {
s.State = Terminated
return nil
}
if !opt.flag {
continue Parsing
}
}
}
s.args = []string{}
return nil
}

@ -0,0 +1,67 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strconv"
)
type intValue int
func (i *intValue) Set(value string, opt Option) error {
v, err := strconv.ParseInt(value, 0, strconv.IntSize)
if err != nil {
if e, ok := err.(*strconv.NumError); ok {
switch e.Err {
case strconv.ErrRange:
err = fmt.Errorf("value out of range: %s", value)
case strconv.ErrSyntax:
err = fmt.Errorf("not a valid number: %s", value)
}
}
return err
}
*i = intValue(v)
return nil
}
func (i *intValue) String() string {
return strconv.FormatInt(int64(*i), 10)
}
// Int creates an option that parses its value as an integer.
func Int(name rune, value int, helpvalue ...string) *int {
return CommandLine.Int(name, value, helpvalue...)
}
func (s *Set) Int(name rune, value int, helpvalue ...string) *int {
return s.IntLong("", name, value, helpvalue...)
}
func IntLong(name string, short rune, value int, helpvalue ...string) *int {
return CommandLine.IntLong(name, short, value, helpvalue...)
}
func (s *Set) IntLong(name string, short rune, value int, helpvalue ...string) *int {
s.IntVarLong(&value, name, short, helpvalue...)
return &value
}
func IntVar(p *int, name rune, helpvalue ...string) Option {
return CommandLine.IntVar(p, name, helpvalue...)
}
func (s *Set) IntVar(p *int, name rune, helpvalue ...string) Option {
return s.IntVarLong(p, "", name, helpvalue...)
}
func IntVarLong(p *int, name string, short rune, helpvalue ...string) Option {
return CommandLine.IntVarLong(p, name, short, helpvalue...)
}
func (s *Set) IntVarLong(p *int, name string, short rune, helpvalue ...string) Option {
return s.VarLong((*intValue)(p), name, short, helpvalue...)
}

@ -0,0 +1,67 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strconv"
)
type int16Value int16
func (i *int16Value) Set(value string, opt Option) error {
v, err := strconv.ParseInt(value, 0, 16)
if err != nil {
if e, ok := err.(*strconv.NumError); ok {
switch e.Err {
case strconv.ErrRange:
err = fmt.Errorf("value out of range: %s", value)
case strconv.ErrSyntax:
err = fmt.Errorf("not a valid number: %s", value)
}
}
return err
}
*i = int16Value(v)
return nil
}
func (i *int16Value) String() string {
return strconv.FormatInt(int64(*i), 10)
}
// Int16 creates an option that parses its value as an int16.
func Int16(name rune, value int16, helpvalue ...string) *int16 {
return CommandLine.Int16(name, value, helpvalue...)
}
func (s *Set) Int16(name rune, value int16, helpvalue ...string) *int16 {
return s.Int16Long("", name, value, helpvalue...)
}
func Int16Long(name string, short rune, value int16, helpvalue ...string) *int16 {
return CommandLine.Int16Long(name, short, value, helpvalue...)
}
func (s *Set) Int16Long(name string, short rune, value int16, helpvalue ...string) *int16 {
s.Int16VarLong(&value, name, short, helpvalue...)
return &value
}
func Int16Var(p *int16, name rune, helpvalue ...string) Option {
return CommandLine.Int16Var(p, name, helpvalue...)
}
func (s *Set) Int16Var(p *int16, name rune, helpvalue ...string) Option {
return s.Int16VarLong(p, "", name, helpvalue...)
}
func Int16VarLong(p *int16, name string, short rune, helpvalue ...string) Option {
return CommandLine.Int16VarLong(p, name, short, helpvalue...)
}
func (s *Set) Int16VarLong(p *int16, name string, short rune, helpvalue ...string) Option {
return s.VarLong((*int16Value)(p), name, short, helpvalue...)
}

@ -0,0 +1,84 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strings"
"testing"
)
var int16Tests = []struct {
where string
in []string
i int16
int16 int16
err string
}{
{
loc(),
[]string{},
17, 42,
"",
},
{
loc(),
[]string{"test", "-i", "1", "--int16", "2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "--int16=2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "-i2"},
2, 42,
"",
},
{
loc(),
[]string{"test", "-i=1"},
17, 42,
"test: not a valid number: =1\n",
},
{
loc(),
[]string{"test", "-i0x20"},
0x20, 42,
"",
},
{
loc(),
[]string{"test", "-i010"},
8, 42,
"",
},
}
func TestInt16(t *testing.T) {
for x, tt := range int16Tests {
reset()
i := Int16('i', 17)
opt := Int16Long("int16", 0, 42)
if strings.Index(tt.where, ":-") > 0 {
tt.where = fmt.Sprintf("#%d", x)
}
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if got, want := *i, tt.i; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
if got, want := *opt, tt.int16; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
}
}

@ -0,0 +1,67 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strconv"
)
type int32Value int32
func (i *int32Value) Set(value string, opt Option) error {
v, err := strconv.ParseInt(value, 0, 32)
if err != nil {
if e, ok := err.(*strconv.NumError); ok {
switch e.Err {
case strconv.ErrRange:
err = fmt.Errorf("value out of range: %s", value)
case strconv.ErrSyntax:
err = fmt.Errorf("not a valid number: %s", value)
}
}
return err
}
*i = int32Value(v)
return nil
}
func (i *int32Value) String() string {
return strconv.FormatInt(int64(*i), 10)
}
// Int32 creates an option that parses its value as an int32.
func Int32(name rune, value int32, helpvalue ...string) *int32 {
return CommandLine.Int32(name, value, helpvalue...)
}
func (s *Set) Int32(name rune, value int32, helpvalue ...string) *int32 {
return s.Int32Long("", name, value, helpvalue...)
}
func Int32Long(name string, short rune, value int32, helpvalue ...string) *int32 {
return CommandLine.Int32Long(name, short, value, helpvalue...)
}
func (s *Set) Int32Long(name string, short rune, value int32, helpvalue ...string) *int32 {
s.Int32VarLong(&value, name, short, helpvalue...)
return &value
}
func Int32Var(p *int32, name rune, helpvalue ...string) Option {
return CommandLine.Int32Var(p, name, helpvalue...)
}
func (s *Set) Int32Var(p *int32, name rune, helpvalue ...string) Option {
return s.Int32VarLong(p, "", name, helpvalue...)
}
func Int32VarLong(p *int32, name string, short rune, helpvalue ...string) Option {
return CommandLine.Int32VarLong(p, name, short, helpvalue...)
}
func (s *Set) Int32VarLong(p *int32, name string, short rune, helpvalue ...string) Option {
return s.VarLong((*int32Value)(p), name, short, helpvalue...)
}

@ -0,0 +1,84 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strings"
"testing"
)
var int32Tests = []struct {
where string
in []string
i int32
int32 int32
err string
}{
{
loc(),
[]string{},
17, 42,
"",
},
{
loc(),
[]string{"test", "-i", "1", "--int32", "2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "--int32=2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "-i2"},
2, 42,
"",
},
{
loc(),
[]string{"test", "-i=1"},
17, 42,
"test: not a valid number: =1\n",
},
{
loc(),
[]string{"test", "-i0x20"},
0x20, 42,
"",
},
{
loc(),
[]string{"test", "-i010"},
8, 42,
"",
},
}
func TestInt32(t *testing.T) {
for x, tt := range int32Tests {
reset()
i := Int32('i', 17)
opt := Int32Long("int32", 0, 42)
if strings.Index(tt.where, ":-") > 0 {
tt.where = fmt.Sprintf("#%d", x)
}
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if got, want := *i, tt.i; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
if got, want := *opt, tt.int32; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
}
}

@ -0,0 +1,67 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strconv"
)
type int64Value int64
func (i *int64Value) Set(value string, opt Option) error {
v, err := strconv.ParseInt(value, 0, 64)
if err != nil {
if e, ok := err.(*strconv.NumError); ok {
switch e.Err {
case strconv.ErrRange:
err = fmt.Errorf("value out of range: %s", value)
case strconv.ErrSyntax:
err = fmt.Errorf("not a valid number: %s", value)
}
}
return err
}
*i = int64Value(v)
return nil
}
func (i *int64Value) String() string {
return strconv.FormatInt(int64(*i), 10)
}
// Int64 creates an option that parses its value as an int64.
func Int64(name rune, value int64, helpvalue ...string) *int64 {
return CommandLine.Int64(name, value, helpvalue...)
}
func (s *Set) Int64(name rune, value int64, helpvalue ...string) *int64 {
return s.Int64Long("", name, value, helpvalue...)
}
func Int64Long(name string, short rune, value int64, helpvalue ...string) *int64 {
return CommandLine.Int64Long(name, short, value, helpvalue...)
}
func (s *Set) Int64Long(name string, short rune, value int64, helpvalue ...string) *int64 {
s.Int64VarLong(&value, name, short, helpvalue...)
return &value
}
func Int64Var(p *int64, name rune, helpvalue ...string) Option {
return CommandLine.Int64Var(p, name, helpvalue...)
}
func (s *Set) Int64Var(p *int64, name rune, helpvalue ...string) Option {
return s.Int64VarLong(p, "", name, helpvalue...)
}
func Int64VarLong(p *int64, name string, short rune, helpvalue ...string) Option {
return CommandLine.Int64VarLong(p, name, short, helpvalue...)
}
func (s *Set) Int64VarLong(p *int64, name string, short rune, helpvalue ...string) Option {
return s.VarLong((*int64Value)(p), name, short, helpvalue...)
}

@ -0,0 +1,84 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strings"
"testing"
)
var int64Tests = []struct {
where string
in []string
i int64
int64 int64
err string
}{
{
loc(),
[]string{},
17, 42,
"",
},
{
loc(),
[]string{"test", "-i", "1", "--int64", "2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "--int64=2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "-i2"},
2, 42,
"",
},
{
loc(),
[]string{"test", "-i=1"},
17, 42,
"test: not a valid number: =1\n",
},
{
loc(),
[]string{"test", "-i0x20"},
0x20, 42,
"",
},
{
loc(),
[]string{"test", "-i010"},
8, 42,
"",
},
}
func TestInt64(t *testing.T) {
for x, tt := range int64Tests {
reset()
i := Int64('i', 17)
opt := Int64Long("int64", 0, 42)
if strings.Index(tt.where, ":-") > 0 {
tt.where = fmt.Sprintf("#%d", x)
}
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if got, want := *i, tt.i; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
if got, want := *opt, tt.int64; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
}
}

@ -0,0 +1,84 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strings"
"testing"
)
var intTests = []struct {
where string
in []string
i int
int int
err string
}{
{
loc(),
[]string{},
17, 42,
"",
},
{
loc(),
[]string{"test", "-i", "1", "--int", "2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "--int=2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "-i2"},
2, 42,
"",
},
{
loc(),
[]string{"test", "-i=1"},
17, 42,
"test: not a valid number: =1\n",
},
{
loc(),
[]string{"test", "-i0x20"},
0x20, 42,
"",
},
{
loc(),
[]string{"test", "-i010"},
8, 42,
"",
},
}
func TestInt(t *testing.T) {
for x, tt := range intTests {
reset()
i := Int('i', 17)
opt := IntLong("int", 0, 42)
if strings.Index(tt.where, ":-") > 0 {
tt.where = fmt.Sprintf("#%d", x)
}
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if got, want := *i, tt.i; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
if got, want := *opt, tt.int; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
}
}

@ -0,0 +1,69 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import "strings"
type listValue []string
func (s *listValue) Set(value string, opt Option) error {
a := strings.Split(value, ",")
// If this is the first time we are seen then nil out the
// default value.
if opt.Count() <= 1 {
*s = nil
}
*s = append(*s, a...)
return nil
}
func (s *listValue) String() string {
return strings.Join([]string(*s), ",")
}
// List creates an option that returns a slice of strings. The parameters
// passed are converted from a comma seperated value list into a slice.
// Subsequent occurrences append to the list.
func List(name rune, helpvalue ...string) *[]string {
return CommandLine.List(name, helpvalue...)
}
func (s *Set) List(name rune, helpvalue ...string) *[]string {
p := []string{}
s.ListVar(&p, name, helpvalue...)
return &p
}
func ListLong(name string, short rune, helpvalue ...string) *[]string {
return CommandLine.ListLong(name, short, helpvalue...)
}
func (s *Set) ListLong(name string, short rune, helpvalue ...string) *[]string {
p := []string{}
s.ListVarLong(&p, name, short, helpvalue...)
return &p
}
// ListVar creats a list option and places the values in p. If p is pointing
// to a list of values then those are considered the default values. The first
// time name is seen in the options the list will be set to list specified by
// the parameter to the option. Subsequent instances of the option will append
// to the list.
func ListVar(p *[]string, name rune, helpvalue ...string) Option {
return CommandLine.ListVar(p, name, helpvalue...)
}
func (s *Set) ListVar(p *[]string, name rune, helpvalue ...string) Option {
return s.ListVarLong(p, "", name, helpvalue...)
}
func ListVarLong(p *[]string, name string, short rune, helpvalue ...string) Option {
return CommandLine.ListVarLong(p, name, short, helpvalue...)
}
func (s *Set) ListVarLong(p *[]string, name string, short rune, helpvalue ...string) Option {
opt := s.VarLong((*listValue)(p), name, short, helpvalue...)
return opt
}

@ -0,0 +1,99 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import "testing"
var listTests = []struct {
where string
in []string
l, list []string
err string
}{
{
loc(),
[]string{},
nil, nil,
"",
},
{
loc(),
[]string{"test", "-l", "one"},
[]string{"one"}, nil,
"",
},
{
loc(),
[]string{"test", "-lone", "-ltwo"},
[]string{"one", "two"}, nil,
"",
},
{
loc(),
[]string{"test", "--list", "one"},
nil, []string{"one"},
"",
},
{
loc(),
[]string{"test", "--list=one", "--list=two"},
nil, []string{"one", "two"},
"",
},
{
loc(),
[]string{"test", "--list=one,two"},
nil, []string{"one", "two"},
"",
},
}
func TestList(t *testing.T) {
for _, tt := range listTests {
reset()
l := List('l')
list := ListLong("list", 0)
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if badSlice(*l, tt.l) {
t.Errorf("%s: got s = %q, want %q", tt.where, *l, tt.l)
}
if badSlice(*list, tt.list) {
t.Errorf("%s: got s = %q, want %q", tt.where, *list, tt.list)
}
}
}
func TestDefaultList(t *testing.T) {
reset()
list := []string{"d1", "d2", "d3"}
ListVar(&list, 'l')
parse([]string{"test"})
want := []string{"d1", "d2", "d3"}
if badSlice(list, want) {
t.Errorf("got s = %q, want %q", list, want)
}
parse([]string{"test", "-l", "one"})
want = []string{"one"}
if badSlice(list, want) {
t.Errorf("got s = %q, want %q", list, want)
}
parse([]string{"test", "-l", "two"})
want = []string{"one", "two"}
if badSlice(list, want) {
t.Errorf("got s = %q, want %q", list, want)
}
Lookup('l').Reset()
want = []string{"d1", "d2", "d3"}
if badSlice(list, want) {
t.Errorf("got s = %q, want %q", list, want)
}
}

@ -0,0 +1,193 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strings"
)
// An Option can be either a Flag or a Value
type Option interface {
// Name returns the name of the option. If the option has been seen
// then the last way it was referenced (short or long) is returned
// otherwise if there is a short name then this will be the short name
// as a string, else it will be the long name.
Name() string
// IsFlag returns true if Option is a flag.
IsFlag() bool
// Seen returns true if the flag was seen.
Seen() bool
// Count returns the number of times the flag was seen.
Count() int
// String returns the last value the option was set to.
String() string
// Value returns the Value of the option.
Value() Value
// SetOptional makes the value optional. The option and value are
// always a single argument. Either --option or --option=value. In
// the former case the value of the option does not change but the Set()
// will return true and the value returned by Count() is incremented.
// The short form is either -o or -ovalue. SetOptional returns
// the Option
SetOptional() Option
// SetFlag makes the value a flag. Flags are boolean values and
// normally do not taken a value. They are set to true when seen.
// If a value is passed in the long form then it must be on, case
// insenstive, one of "true", "false", "t", "f", "on", "off", "1", "0".
// SetFlag returns the Option
SetFlag() Option
// Reset resets the state of the option so it appears it has not
// yet been seen, including resetting the value of the option
// to its original default state.
Reset()
}
type option struct {
short rune // 0 means no short name
long string // "" means no long name
isLong bool // True if they used the long name
flag bool // true if a boolean flag
defval string // default value
optional bool // true if we take an optional value
help string // help message
where string // file where the option was defined
value Value // current value of option
count int // number of times we have seen this option
name string // name of the value (for usage)
uname string // name of the option (for usage)
}
// usageName returns the name of the option for printing usage lines in one
// of the following forms:
//
// -f
// --flag
// -f, --flag
// -s value
// --set=value
// -s, --set=value
func (o *option) usageName() string {
// Don't print help messages if we have none and there is only one
// way to specify the option.
if o.help == "" && (o.short == 0 || o.long == "") {
return ""
}
n := ""
switch {
case o.short != 0 && o.long == "":
n = "-" + string(o.short)
case o.short == 0 && o.long != "":
n = " --" + o.long
case o.short != 0 && o.long != "":
n = "-" + string(o.short) + ", --" + o.long
}
switch {
case o.flag:
return n
case o.optional:
return n + "[=" + o.name + "]"
case o.long != "":
return n + "=" + o.name
}
return n + " " + o.name
}
// sortName returns the name to sort the option on.
func (o *option) sortName() string {
if o.short != 0 {
return string(o.short) + o.long
}
return o.long[:1] + o.long
}
func (o *option) Seen() bool { return o.count > 0 }
func (o *option) Count() int { return o.count }
func (o *option) IsFlag() bool { return o.flag }
func (o *option) String() string { return o.value.String() }
func (o *option) SetOptional() Option { o.optional = true; return o }
func (o *option) SetFlag() Option { o.flag = true; return o }
func (o *option) Value() Value {
if o == nil {
return nil
}
return o.value
}
func (o *option) Name() string {
if !o.isLong && o.short != 0 {
return "-" + string(o.short)
}
return "--" + o.long
}
// Reset rests an option so that it appears it has not yet been seen.
func (o *option) Reset() {
o.isLong = false
o.count = 0
o.value.Set(o.defval, o)
}
type optionList []*option
func (ol optionList) Len() int { return len(ol) }
func (ol optionList) Swap(i, j int) { ol[i], ol[j] = ol[j], ol[i] }
func (ol optionList) Less(i, j int) bool {
// first check the short names (or the first letter of the long name)
// If they are not equal (case insensitive) then we have our answer
n1 := ol[i].sortName()
n2 := ol[j].sortName()
l1 := strings.ToLower(n1)
l2 := strings.ToLower(n2)
if l1 < l2 {
return true
}
if l2 < l1 {
return false
}
return n1 < n2
}
// AddOption add the option o to set CommandLine if o is not already in set
// CommandLine.
func AddOption(o Option) {
CommandLine.AddOption(o)
}
// AddOption add the option o to set s if o is not already in set s.
func (s *Set) AddOption(o Option) {
opt := o.(*option)
for _, eopt := range s.options {
if opt == eopt {
return
}
}
if opt.short != 0 {
if oo, ok := s.shortOptions[opt.short]; ok {
fmt.Fprintf(stderr, "%s: -%c already declared at %s\n", opt.where, opt.short, oo.where)
exit(1)
}
s.shortOptions[opt.short] = opt
}
if opt.long != "" {
if oo, ok := s.longOptions[opt.long]; ok {
fmt.Fprintf(stderr, "%s: --%s already declared at %s\n", opt.where, opt.long, oo.where)
exit(1)
}
s.longOptions[opt.long] = opt
}
s.options = append(s.options, opt)
}

@ -0,0 +1,268 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"io"
"os"
"sort"
)
// A Termination says why Getopt returned.
type State int
const (
InProgress = State(iota) // Getopt is still running
Dash // Returned on "-"
DashDash // Returned on "--"
EndOfOptions // End of options reached
EndOfArguments // No more arguments
Terminated // Terminated by callback function
Failure // Terminated due to error
Unknown // Indicates internal error
)
type Set struct {
State // State of getopt
// args are the parameters remaining after parsing the optoins.
args []string
// program is the name of the program for usage and error messages.
// If not set it will automatically be set to the base name of the
// first argument passed to parse.
program string
// parameters is what is displayed on the usage line after displaying
// the various options.
parameters string
usage func() // usage should print the programs usage and exit.
shortOptions map[rune]*option
longOptions map[string]*option
options optionList
}
// New returns a newly created option set.
func New() *Set {
s := &Set{
shortOptions: make(map[rune]*option),
longOptions: make(map[string]*option),
parameters: "[parameters ...]",
}
s.usage = func() {
s.PrintUsage(stderr)
}
return s
}
// The default set of command-line options.
var CommandLine = New()
// PrintUsage calls PrintUsage in the default option set.
func PrintUsage(w io.Writer) { CommandLine.PrintUsage(w) }
// Usage calls the usage function in the default option set.
func Usage() { CommandLine.usage() }
// Parse calls Parse in the default option set with the command line arguments
// found in os.Args.
func Parse() { CommandLine.Parse(os.Args) }
// Getops returns the result of calling Getop in the default option set with the
// command line arguments found in os.Args. The fn function, which may be nil,
// is passed to Getopt.
func Getopt(fn func(Option) bool) error { return CommandLine.Getopt(os.Args, fn) }
// Arg returns the n'th command-line argument. Arg(0) is the first remaining
// argument after options have been processed.
func Arg(n int) string {
if n >= 0 && n < len(CommandLine.args) {
return CommandLine.args[n]
}
return ""
}
// Arg returns the n'th argument. Arg(0) is the first remaining
// argument after options have been processed.
func (s *Set) Arg(n int) string {
if n >= 0 && n < len(s.args) {
return s.args[n]
}
return ""
}
// Args returns the non-option command line arguments.
func Args() []string {
return CommandLine.args
}
// Args returns the non-option arguments.
func (s *Set) Args() []string {
return s.args
}
// NArgs returns the number of non-option command line arguments.
func NArgs() int {
return len(CommandLine.args)
}
// NArgs returns the number of non-option arguments.
func (s *Set) NArgs() int {
return len(s.args)
}
// SetParameters sets the parameters string for printing the command line
// usage. It defaults to "[parameters ...]"
func SetParameters(parameters string) {
CommandLine.parameters = parameters
}
// SetParameters sets the parameters string for printing the s's usage.
// It defaults to "[parameters ...]"
func (s *Set) SetParameters(parameters string) {
s.parameters = parameters
}
// SetProgram sets the program name to program. Nomrally it is determined
// from the zeroth command line argument (see os.Args).
func SetProgram(program string) {
CommandLine.program = program
}
// SetProgram sets s's program name to program. Nomrally it is determined
// from the zeroth argument passed to Getopt or Parse.
func (s *Set) SetProgram(program string) {
s.program = program
}
// SetUsage sets the function used by Parse to display the commands usage
// on error. It defaults to calling PrintUsage(os.Stderr).
func SetUsage(usage func()) {
CommandLine.usage = usage
}
// SetUsage sets the function used by Parse to display usage on error. It
// defaults to calling f.PrintUsage(os.Stderr).
func (s *Set) SetUsage(usage func()) {
s.usage = usage
}
// Lookup returns the Option associated with name. Name should either be
// a rune (the short name) or a string (the long name).
func Lookup(name interface{}) Option {
return CommandLine.Lookup(name)
}
// Lookup returns the Option associated with name in s. Name should either be
// a rune (the short name) or a string (the long name).
func (s *Set) Lookup(name interface{}) Option {
switch v := name.(type) {
case rune:
return s.shortOptions[v]
case int:
return s.shortOptions[rune(v)]
case string:
return s.longOptions[v]
}
return nil
}
// IsSet returns true if the Option associated with name was seen while
// parsing the command line arguments. Name should either be a rune (the
// short name) or a string (the long name).
func IsSet(name interface{}) bool {
return CommandLine.IsSet(name)
}
// IsSet returns true if the Option associated with name was seen while
// parsing s. Name should either be a rune (the short name) or a string (the
// long name).
func (s *Set) IsSet(name interface{}) bool {
if opt := s.Lookup(name); opt != nil {
return opt.Seen()
}
return false
}
// GetCount returns the number of times the Option associated with name has been
// seen while parsing the command line arguments. Name should either be a rune
// (the short name) or a string (the long name).
func GetCount(name interface{}) int {
return CommandLine.GetCount(name)
}
// GetCount returns the number of times the Option associated with name has been
// seen while parsing s's arguments. Name should either be a rune (the short
// name) or a string (the long name).
func (s *Set) GetCount(name interface{}) int {
if opt := s.Lookup(name); opt != nil {
return opt.Count()
}
return 0
}
// GetValue returns the final value set to the command-line Option with name.
// If the option has not been seen while parsing s then the default value is
// returned. Name should either be a rune (the short name) or a string (the
// long name).
func GetValue(name interface{}) string {
return CommandLine.GetValue(name)
}
// GetValue returns the final value set to the Option in s associated with name.
// If the option has not been seen while parsing s then the default value is
// returned. Name should either be a rune (the short name) or a string (the
// long name).
func (s *Set) GetValue(name interface{}) string {
if opt := s.Lookup(name); opt != nil {
return opt.String()
}
return ""
}
// Visit visits the command-line options in lexicographical order, calling fn
// for each. It visits only those options that have been set.
func Visit(fn func(Option)) { CommandLine.Visit(fn) }
// Visit visits the options in s in lexicographical order, calling fn
// for each. It visits only those options that have been set.
func (s *Set) Visit(fn func(Option)) {
sort.Sort(s.options)
for _, opt := range s.options {
if opt.count > 0 {
fn(opt)
}
}
}
// VisitAll visits the options in s in lexicographical order, calling fn
// for each. It visits all options, even those not set.
func VisitAll(fn func(Option)) { CommandLine.VisitAll(fn) }
// VisitAll visits the command-line flags in lexicographical order, calling fn
// for each. It visits all flags, even those not set.
func (s *Set) VisitAll(fn func(Option)) {
sort.Sort(s.options)
for _, opt := range s.options {
fn(opt)
}
}
// Reset resets all the command line options to the initial state so it
// appears none of them have been seen.
func Reset() {
CommandLine.Reset()
}
// Reset resets all the options in s to the initial state so it
// appears none of them have been seen.
func (s *Set) Reset() {
for _, opt := range s.options {
opt.Reset()
}
}

@ -0,0 +1,110 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strconv"
)
type signed int64
type SignedLimit struct {
Base int // Base for conversion as per strconv.ParseInt
Bits int // Number of bits as per strconv.ParseInt
Min int64 // Minimum allowed value if both Min and Max are not 0
Max int64 // Maximum allowed value if both Min and Max are not 0
}
var signedLimits = make(map[*signed]*SignedLimit)
func (n *signed) Set(value string, opt Option) error {
l := signedLimits[n]
if l == nil {
return fmt.Errorf("no limits defined for %s", opt.Name())
}
v, err := strconv.ParseInt(value, l.Base, l.Bits)
if err != nil {
if e, ok := err.(*strconv.NumError); ok {
switch e.Err {
case strconv.ErrRange:
err = fmt.Errorf("value out of range: %s", value)
case strconv.ErrSyntax:
err = fmt.Errorf("not a valid number: %s", value)
}
}
return err
}
if l.Min != 0 || l.Max != 0 {
if v < l.Min {
return fmt.Errorf("value out of range (<%v): %s", l.Min, value)
}
if v > l.Max {
return fmt.Errorf("value out of range (>%v): %s", l.Max, value)
}
}
*n = signed(v)
return nil
}
func (n *signed) String() string {
l := signedLimits[n]
if l != nil && l.Base != 0 {
return strconv.FormatInt(int64(*n), l.Base)
}
return strconv.FormatInt(int64(*n), 10)
}
// Signed creates an option that is stored in an int64 and is constrained
// by the limits pointed to by l. The Max and Min values are only used if
// at least one of the values are not 0. If Base is 0, the base is implied by
// the string's prefix: base 16 for "0x", base 8 for "0", and base 10 otherwise.
func Signed(name rune, value int64, l *SignedLimit, helpvalue ...string) *int64 {
return CommandLine.Signed(name, value, l, helpvalue...)
}
func (s *Set) Signed(name rune, value int64, l *SignedLimit, helpvalue ...string) *int64 {
return s.SignedLong("", name, value, l, helpvalue...)
}
func SignedLong(name string, short rune, value int64, l *SignedLimit, helpvalue ...string) *int64 {
return CommandLine.SignedLong(name, short, value, l, helpvalue...)
}
func (s *Set) SignedLong(name string, short rune, value int64, l *SignedLimit, helpvalue ...string) *int64 {
s.SignedVarLong(&value, name, short, l, helpvalue...)
return &value
}
func SignedVar(p *int64, name rune, l *SignedLimit, helpvalue ...string) Option {
return CommandLine.SignedVar(p, name, l, helpvalue...)
}
func (s *Set) SignedVar(p *int64, name rune, l *SignedLimit, helpvalue ...string) Option {
return s.SignedVarLong(p, "", name, l, helpvalue...)
}
func SignedVarLong(p *int64, name string, short rune, l *SignedLimit, helpvalue ...string) Option {
return CommandLine.SignedVarLong(p, name, short, l, helpvalue...)
}
func (s *Set) SignedVarLong(p *int64, name string, short rune, l *SignedLimit, helpvalue ...string) Option {
opt := s.VarLong((*signed)(p), name, short, helpvalue...)
if l.Base > 36 || l.Base == 1 || l.Base < 0 {
fmt.Fprintf(stderr, "invalid base for %s: %d\n", opt.Name(), l.Base)
exit(1)
}
if l.Bits < 0 || l.Bits > 64 {
fmt.Fprintf(stderr, "invalid bit size for %s: %d\n", opt.Name(), l.Bits)
exit(1)
}
if l.Min > l.Max {
fmt.Fprintf(stderr, "min greater than max for %s\n", opt.Name())
exit(1)
}
lim := *l
signedLimits[(*signed)(p)] = &lim
return opt
}

@ -0,0 +1,97 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strings"
"testing"
)
var signedNumberTests = []struct {
where string
in []string
l SignedLimit
out int64
err string
}{
{
where: loc(),
},
{
loc(),
[]string{"test", "-n", "1010"},
SignedLimit{Base: 2, Bits: 5},
10,
"",
},
{
loc(),
[]string{"test", "-n", "1010"},
SignedLimit{Base: 2, Bits: 4},
0,
"test: value out of range: 1010\n",
},
{
loc(),
[]string{"test", "-n", "-1000"},
SignedLimit{Base: 2, Bits: 4},
-8,
"",
},
{
loc(),
[]string{"test", "-n", "3"},
SignedLimit{Min: 4, Max: 6},
0,
"test: value out of range (<4): 3\n",
},
{
loc(),
[]string{"test", "-n", "4"},
SignedLimit{Min: 4, Max: 6},
4,
"",
},
{
loc(),
[]string{"test", "-n", "5"},
SignedLimit{Min: 4, Max: 6},
5,
"",
},
{
loc(),
[]string{"test", "-n", "6"},
SignedLimit{Min: 4, Max: 6},
6,
"",
},
{
loc(),
[]string{"test", "-n", "7"},
SignedLimit{Min: 4, Max: 6},
0,
"test: value out of range (>6): 7\n",
},
}
func TestSigneds(t *testing.T) {
for x, tt := range signedNumberTests {
if strings.Index(tt.where, ":-") > 0 {
tt.where = fmt.Sprintf("#%d", x)
}
reset()
n := Signed('n', 0, &tt.l)
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if *n != tt.out {
t.Errorf("%s: got %v, want %v", tt.where, *n, tt.out)
}
}
}

@ -0,0 +1,53 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
type stringValue string
func (s *stringValue) Set(value string, opt Option) error {
*s = stringValue(value)
return nil
}
func (s *stringValue) String() string {
return string(*s)
}
// String returns a value option that stores is value as a string. The
// initial value of the string is passed in value.
func String(name rune, value string, helpvalue ...string) *string {
return CommandLine.String(name, value, helpvalue...)
}
func (s *Set) String(name rune, value string, helpvalue ...string) *string {
p := value
s.StringVarLong(&p, "", name, helpvalue...)
return &p
}
func StringLong(name string, short rune, value string, helpvalue ...string) *string {
return CommandLine.StringLong(name, short, value, helpvalue...)
}
func (s *Set) StringLong(name string, short rune, value string, helpvalue ...string) *string {
s.StringVarLong(&value, name, short, helpvalue...)
return &value
}
func StringVar(p *string, name rune, helpvalue ...string) Option {
return CommandLine.StringVar(p, name, helpvalue...)
}
func (s *Set) StringVar(p *string, name rune, helpvalue ...string) Option {
return s.VarLong((*stringValue)(p), "", name, helpvalue...)
}
func StringVarLong(p *string, name string, short rune, helpvalue ...string) Option {
return CommandLine.StringVarLong(p, name, short, helpvalue...)
}
func (s *Set) StringVarLong(p *string, name string, short rune, helpvalue ...string) Option {
return s.VarLong((*stringValue)(p), name, short, helpvalue...)
}

@ -0,0 +1,77 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import "testing"
var stringTests = []struct {
where string
in []string
sout string
optout string
err string
}{
{
loc(),
[]string{},
"one",
"two",
"",
},
{
loc(),
[]string{"test", "-s"},
"one",
"two",
"test: missing parameter for -s\n",
},
{
loc(),
[]string{"test", "--opt"},
"one",
"two",
"test: missing parameter for --opt\n",
},
{
loc(),
[]string{"test", "-svalue", "--opt=option"},
"value",
"option",
"",
},
{
loc(),
[]string{"test", "-s", "value", "--opt", "option"},
"value",
"option",
"",
},
{
loc(),
[]string{"test", "-swrong", "--opt=wrong", "-s", "value", "--opt", "option"},
"value",
"option",
"",
},
}
func TestString(t *testing.T) {
for _, tt := range stringTests {
reset()
s := String('s', "one")
opt := StringLong("opt", 0, "two")
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if *s != tt.sout {
t.Errorf("%s: got s = %q, want %q", tt.where, *s, tt.sout)
}
if *opt != tt.optout {
t.Errorf("%s: got opt = %q, want %q", tt.where, *opt, tt.optout)
}
}
}

@ -0,0 +1,67 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strconv"
)
type uintValue uint
func (i *uintValue) Set(value string, opt Option) error {
v, err := strconv.ParseUint(value, 0, strconv.IntSize)
if err != nil {
if e, ok := err.(*strconv.NumError); ok {
switch e.Err {
case strconv.ErrRange:
err = fmt.Errorf("value out of range: %s", value)
case strconv.ErrSyntax:
err = fmt.Errorf("not a valid number: %s", value)
}
}
return err
}
*i = uintValue(v)
return nil
}
func (i *uintValue) String() string {
return strconv.FormatUint(uint64(*i), 10)
}
// Uint creates an option that parses its value as an unsigned integer.
func Uint(name rune, value uint, helpvalue ...string) *uint {
return CommandLine.Uint(name, value, helpvalue...)
}
func (s *Set) Uint(name rune, value uint, helpvalue ...string) *uint {
return s.UintLong("", name, value, helpvalue...)
}
func UintLong(name string, short rune, value uint, helpvalue ...string) *uint {
return CommandLine.UintLong(name, short, value, helpvalue...)
}
func (s *Set) UintLong(name string, short rune, value uint, helpvalue ...string) *uint {
s.UintVarLong(&value, name, short, helpvalue...)
return &value
}
func UintVar(p *uint, name rune, helpvalue ...string) Option {
return CommandLine.UintVar(p, name, helpvalue...)
}
func (s *Set) UintVar(p *uint, name rune, helpvalue ...string) Option {
return s.UintVarLong(p, "", name, helpvalue...)
}
func UintVarLong(p *uint, name string, short rune, helpvalue ...string) Option {
return CommandLine.UintVarLong(p, name, short, helpvalue...)
}
func (s *Set) UintVarLong(p *uint, name string, short rune, helpvalue ...string) Option {
return s.VarLong((*uintValue)(p), name, short, helpvalue...)
}

@ -0,0 +1,67 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strconv"
)
type uint16Value uint16
func (i *uint16Value) Set(value string, opt Option) error {
v, err := strconv.ParseUint(value, 0, 16)
if err != nil {
if e, ok := err.(*strconv.NumError); ok {
switch e.Err {
case strconv.ErrRange:
err = fmt.Errorf("value out of range: %s", value)
case strconv.ErrSyntax:
err = fmt.Errorf("not a valid number: %s", value)
}
}
return err
}
*i = uint16Value(v)
return nil
}
func (i *uint16Value) String() string {
return strconv.FormatUint(uint64(*i), 10)
}
// Uint16 creates an option that parses its value as an uint16.
func Uint16(name rune, value uint16, helpvalue ...string) *uint16 {
return CommandLine.Uint16(name, value, helpvalue...)
}
func (s *Set) Uint16(name rune, value uint16, helpvalue ...string) *uint16 {
return s.Uint16Long("", name, value, helpvalue...)
}
func Uint16Long(name string, short rune, value uint16, helpvalue ...string) *uint16 {
return CommandLine.Uint16Long(name, short, value, helpvalue...)
}
func (s *Set) Uint16Long(name string, short rune, value uint16, helpvalue ...string) *uint16 {
s.Uint16VarLong(&value, name, short, helpvalue...)
return &value
}
func Uint16Var(p *uint16, name rune, helpvalue ...string) Option {
return CommandLine.Uint16Var(p, name, helpvalue...)
}
func (s *Set) Uint16Var(p *uint16, name rune, helpvalue ...string) Option {
return s.Uint16VarLong(p, "", name, helpvalue...)
}
func Uint16VarLong(p *uint16, name string, short rune, helpvalue ...string) Option {
return CommandLine.Uint16VarLong(p, name, short, helpvalue...)
}
func (s *Set) Uint16VarLong(p *uint16, name string, short rune, helpvalue ...string) Option {
return s.VarLong((*uint16Value)(p), name, short, helpvalue...)
}

@ -0,0 +1,84 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strings"
"testing"
)
var uint16Tests = []struct {
where string
in []string
i uint16
uint16 uint16
err string
}{
{
loc(),
[]string{},
17, 42,
"",
},
{
loc(),
[]string{"test", "-i", "1", "--uint16", "2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "--uint16=2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "-i2"},
2, 42,
"",
},
{
loc(),
[]string{"test", "-i=1"},
17, 42,
"test: not a valid number: =1\n",
},
{
loc(),
[]string{"test", "-i0x20"},
0x20, 42,
"",
},
{
loc(),
[]string{"test", "-i010"},
8, 42,
"",
},
}
func TestUint16(t *testing.T) {
for x, tt := range uint16Tests {
reset()
i := Uint16('i', 17)
opt := Uint16Long("uint16", 0, 42)
if strings.Index(tt.where, ":-") > 0 {
tt.where = fmt.Sprintf("#%d", x)
}
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if got, want := *i, tt.i; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
if got, want := *opt, tt.uint16; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
}
}

@ -0,0 +1,67 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strconv"
)
type uint32Value uint32
func (i *uint32Value) Set(value string, opt Option) error {
v, err := strconv.ParseUint(value, 0, 32)
if err != nil {
if e, ok := err.(*strconv.NumError); ok {
switch e.Err {
case strconv.ErrRange:
err = fmt.Errorf("value out of range: %s", value)
case strconv.ErrSyntax:
err = fmt.Errorf("not a valid number: %s", value)
}
}
return err
}
*i = uint32Value(v)
return nil
}
func (i *uint32Value) String() string {
return strconv.FormatUint(uint64(*i), 10)
}
// Uint32 creates an option that parses its value as an uint32.
func Uint32(name rune, value uint32, helpvalue ...string) *uint32 {
return CommandLine.Uint32(name, value, helpvalue...)
}
func (s *Set) Uint32(name rune, value uint32, helpvalue ...string) *uint32 {
return s.Uint32Long("", name, value, helpvalue...)
}
func Uint32Long(name string, short rune, value uint32, helpvalue ...string) *uint32 {
return CommandLine.Uint32Long(name, short, value, helpvalue...)
}
func (s *Set) Uint32Long(name string, short rune, value uint32, helpvalue ...string) *uint32 {
s.Uint32VarLong(&value, name, short, helpvalue...)
return &value
}
func Uint32Var(p *uint32, name rune, helpvalue ...string) Option {
return CommandLine.Uint32Var(p, name, helpvalue...)
}
func (s *Set) Uint32Var(p *uint32, name rune, helpvalue ...string) Option {
return s.Uint32VarLong(p, "", name, helpvalue...)
}
func Uint32VarLong(p *uint32, name string, short rune, helpvalue ...string) Option {
return CommandLine.Uint32VarLong(p, name, short, helpvalue...)
}
func (s *Set) Uint32VarLong(p *uint32, name string, short rune, helpvalue ...string) Option {
return s.VarLong((*uint32Value)(p), name, short, helpvalue...)
}

@ -0,0 +1,84 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strings"
"testing"
)
var uint32Tests = []struct {
where string
in []string
i uint32
uint32 uint32
err string
}{
{
loc(),
[]string{},
17, 42,
"",
},
{
loc(),
[]string{"test", "-i", "1", "--uint32", "2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "--uint32=2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "-i2"},
2, 42,
"",
},
{
loc(),
[]string{"test", "-i=1"},
17, 42,
"test: not a valid number: =1\n",
},
{
loc(),
[]string{"test", "-i0x20"},
0x20, 42,
"",
},
{
loc(),
[]string{"test", "-i010"},
8, 42,
"",
},
}
func TestUint32(t *testing.T) {
for x, tt := range uint32Tests {
reset()
i := Uint32('i', 17)
opt := Uint32Long("uint32", 0, 42)
if strings.Index(tt.where, ":-") > 0 {
tt.where = fmt.Sprintf("#%d", x)
}
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if got, want := *i, tt.i; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
if got, want := *opt, tt.uint32; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
}
}

@ -0,0 +1,67 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strconv"
)
type uint64Value uint64
func (i *uint64Value) Set(value string, opt Option) error {
v, err := strconv.ParseUint(value, 0, 64)
if err != nil {
if e, ok := err.(*strconv.NumError); ok {
switch e.Err {
case strconv.ErrRange:
err = fmt.Errorf("value out of range: %s", value)
case strconv.ErrSyntax:
err = fmt.Errorf("not a valid number: %s", value)
}
}
return err
}
*i = uint64Value(v)
return nil
}
func (i *uint64Value) String() string {
return strconv.FormatUint(uint64(*i), 10)
}
// Uint64 creates an option that parses its value as a uint64.
func Uint64(name rune, value uint64, helpvalue ...string) *uint64 {
return CommandLine.Uint64(name, value, helpvalue...)
}
func (s *Set) Uint64(name rune, value uint64, helpvalue ...string) *uint64 {
return s.Uint64Long("", name, value, helpvalue...)
}
func Uint64Long(name string, short rune, value uint64, helpvalue ...string) *uint64 {
return CommandLine.Uint64Long(name, short, value, helpvalue...)
}
func (s *Set) Uint64Long(name string, short rune, value uint64, helpvalue ...string) *uint64 {
s.Uint64VarLong(&value, name, short, helpvalue...)
return &value
}
func Uint64Var(p *uint64, name rune, helpvalue ...string) Option {
return CommandLine.Uint64Var(p, name, helpvalue...)
}
func (s *Set) Uint64Var(p *uint64, name rune, helpvalue ...string) Option {
return s.Uint64VarLong(p, "", name, helpvalue...)
}
func Uint64VarLong(p *uint64, name string, short rune, helpvalue ...string) Option {
return CommandLine.Uint64VarLong(p, name, short, helpvalue...)
}
func (s *Set) Uint64VarLong(p *uint64, name string, short rune, helpvalue ...string) Option {
return s.VarLong((*uint64Value)(p), name, short, helpvalue...)
}

@ -0,0 +1,84 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strings"
"testing"
)
var uint64Tests = []struct {
where string
in []string
i uint64
uint64 uint64
err string
}{
{
loc(),
[]string{},
17, 42,
"",
},
{
loc(),
[]string{"test", "-i", "1", "--uint64", "2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "--uint64=2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "-i2"},
2, 42,
"",
},
{
loc(),
[]string{"test", "-i=1"},
17, 42,
"test: not a valid number: =1\n",
},
{
loc(),
[]string{"test", "-i0x20"},
0x20, 42,
"",
},
{
loc(),
[]string{"test", "-i010"},
8, 42,
"",
},
}
func TestUint64(t *testing.T) {
for x, tt := range uint64Tests {
reset()
i := Uint64('i', 17)
opt := Uint64Long("uint64", 0, 42)
if strings.Index(tt.where, ":-") > 0 {
tt.where = fmt.Sprintf("#%d", x)
}
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if got, want := *i, tt.i; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
if got, want := *opt, tt.uint64; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
}
}

@ -0,0 +1,84 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strings"
"testing"
)
var uintTests = []struct {
where string
in []string
i uint
uint uint
err string
}{
{
loc(),
[]string{},
17, 42,
"",
},
{
loc(),
[]string{"test", "-i", "1", "--uint", "2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "--uint=2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "-i2"},
2, 42,
"",
},
{
loc(),
[]string{"test", "-i=1"},
17, 42,
"test: not a valid number: =1\n",
},
{
loc(),
[]string{"test", "-i0x20"},
0x20, 42,
"",
},
{
loc(),
[]string{"test", "-i010"},
8, 42,
"",
},
}
func TestUint(t *testing.T) {
for x, tt := range uintTests {
reset()
i := Uint('i', 17)
opt := UintLong("uint", 0, 42)
if strings.Index(tt.where, ":-") > 0 {
tt.where = fmt.Sprintf("#%d", x)
}
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if got, want := *i, tt.i; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
if got, want := *opt, tt.uint; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
}
}

@ -0,0 +1,111 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strconv"
)
type unsigned uint64
type UnsignedLimit struct {
Base int // Base for conversion as per strconv.ParseInt
Bits int // Number of bits as per strconv.ParseInt
Min uint64 // Minimum allowed value if both Min and Max are not 0
Max uint64 // Maximum allowed value if both Min and Max are not 0
}
var unsignedLimits = make(map[*unsigned]*UnsignedLimit)
func (n *unsigned) Set(value string, opt Option) error {
l := unsignedLimits[n]
if l == nil {
return fmt.Errorf("no limits defined for %s", opt.Name())
}
v, err := strconv.ParseUint(value, l.Base, l.Bits)
if err != nil {
if e, ok := err.(*strconv.NumError); ok {
switch e.Err {
case strconv.ErrRange:
err = fmt.Errorf("value out of range: %s", value)
case strconv.ErrSyntax:
err = fmt.Errorf("not a valid number: %s", value)
}
}
return err
}
if l.Min != 0 || l.Max != 0 {
if v < l.Min {
return fmt.Errorf("value out of range (<%v): %s", l.Min, value)
}
if v > l.Max {
return fmt.Errorf("value out of range (>%v): %s", l.Max, value)
}
}
*n = unsigned(v)
return nil
}
func (n *unsigned) String() string {
l := unsignedLimits[n]
if l != nil && l.Base != 0 {
return strconv.FormatUint(uint64(*n), l.Base)
}
return strconv.FormatUint(uint64(*n), 10)
}
// Unsigned creates an option that is stored in a uint64 and is
// constrained by the limits pointed to by l. The Max and Min values are only
// used if at least one of the values are not 0. If Base is 0, the base is
// implied by the string's prefix: base 16 for "0x", base 8 for "0", and base
// 10 otherwise.
func Unsigned(name rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 {
return CommandLine.Unsigned(name, value, l, helpvalue...)
}
func (s *Set) Unsigned(name rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 {
return s.UnsignedLong("", name, value, l, helpvalue...)
}
func UnsignedLong(name string, short rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 {
return CommandLine.UnsignedLong(name, short, value, l, helpvalue...)
}
func (s *Set) UnsignedLong(name string, short rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 {
s.UnsignedVarLong(&value, name, short, l, helpvalue...)
return &value
}
func UnsignedVar(p *uint64, name rune, l *UnsignedLimit, helpvalue ...string) Option {
return CommandLine.UnsignedVar(p, name, l, helpvalue...)
}
func (s *Set) UnsignedVar(p *uint64, name rune, l *UnsignedLimit, helpvalue ...string) Option {
return s.UnsignedVarLong(p, "", name, l, helpvalue...)
}
func UnsignedVarLong(p *uint64, name string, short rune, l *UnsignedLimit, helpvalue ...string) Option {
return CommandLine.UnsignedVarLong(p, name, short, l, helpvalue...)
}
func (s *Set) UnsignedVarLong(p *uint64, name string, short rune, l *UnsignedLimit, helpvalue ...string) Option {
opt := s.VarLong((*unsigned)(p), name, short, helpvalue...)
if l.Base > 36 || l.Base == 1 || l.Base < 0 {
fmt.Fprintf(stderr, "invalid base for %s: %d\n", opt.Name(), l.Base)
exit(1)
}
if l.Bits < 0 || l.Bits > 64 {
fmt.Fprintf(stderr, "invalid bit size for %s: %d\n", opt.Name(), l.Bits)
exit(1)
}
if l.Min > l.Max {
fmt.Fprintf(stderr, "min greater than max for %s\n", opt.Name())
exit(1)
}
lim := *l
unsignedLimits[(*unsigned)(p)] = &lim
return opt
}

@ -0,0 +1,97 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strings"
"testing"
)
var unsignedTests = []struct {
where string
in []string
l UnsignedLimit
out uint64
err string
}{
{
where: loc(),
},
{
loc(),
[]string{"test", "-n", "1010"},
UnsignedLimit{Base: 2, Bits: 5},
10,
"",
},
{
loc(),
[]string{"test", "-n", "1010"},
UnsignedLimit{Base: 2, Bits: 4},
10,
"",
},
{
loc(),
[]string{"test", "-n", "1010"},
UnsignedLimit{Base: 2, Bits: 3},
0,
"test: value out of range: 1010\n",
},
{
loc(),
[]string{"test", "-n", "3"},
UnsignedLimit{Min: 4, Max: 6},
0,
"test: value out of range (<4): 3\n",
},
{
loc(),
[]string{"test", "-n", "4"},
UnsignedLimit{Min: 4, Max: 6},
4,
"",
},
{
loc(),
[]string{"test", "-n", "5"},
UnsignedLimit{Min: 4, Max: 6},
5,
"",
},
{
loc(),
[]string{"test", "-n", "6"},
UnsignedLimit{Min: 4, Max: 6},
6,
"",
},
{
loc(),
[]string{"test", "-n", "7"},
UnsignedLimit{Min: 4, Max: 6},
0,
"test: value out of range (>6): 7\n",
},
}
func TestUnsigneds(t *testing.T) {
for x, tt := range unsignedTests {
if strings.Index(tt.where, ":-") > 0 {
tt.where = fmt.Sprintf("#%d", x)
}
reset()
n := Unsigned('n', 0, &tt.l)
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if *n != tt.out {
t.Errorf("%s: got %v, want %v", tt.where, *n, tt.out)
}
}
}

@ -0,0 +1,87 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"bytes"
"fmt"
"path"
"reflect"
"runtime"
"strings"
)
var errorString string
func reset() {
CommandLine.shortOptions = make(map[rune]*option)
CommandLine.longOptions = make(map[string]*option)
CommandLine.options = nil
CommandLine.args = nil
CommandLine.program = ""
errorString = ""
}
func parse(args []string) {
err := CommandLine.Getopt(args, nil)
if err != nil {
b := &bytes.Buffer{}
fmt.Fprintln(b, CommandLine.program+": "+err.Error())
CommandLine.PrintUsage(b)
errorString = b.String()
}
}
func badSlice(a, b []string) bool {
if len(a) != len(b) {
return true
}
for x, v := range a {
if b[x] != v {
return true
}
}
return false
}
func loc() string {
_, file, line, _ := runtime.Caller(1)
return fmt.Sprintf("%s:%d", path.Base(file), line)
}
func (o *option) Equal(opt *option) bool {
if o.value != nil && opt.value == nil {
return false
}
if o.value == nil && opt.value != nil {
return false
}
if o.value != nil && o.value.String() != opt.value.String() {
return false
}
oc := *o
optc := *opt
oc.value = nil
optc.value = nil
return reflect.DeepEqual(&oc, &optc)
}
func newStringValue(s string) *stringValue { return (*stringValue)(&s) }
func checkError(err string) string {
switch {
case err == errorString:
return ""
case err == "":
return fmt.Sprintf("unexpected error %q", errorString)
case errorString == "":
return fmt.Sprintf("did not get expected error %q", err)
case !strings.HasPrefix(errorString, err):
return fmt.Sprintf("got error %q, want %q", errorString, err)
}
return ""
}

@ -0,0 +1,36 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
// Bool creates a flag option that is a bool. Bools normally do not take a
// value however one can be assigned by using the long form of the option:
//
// --option=true
// --o=false
//
// The value is case insensitive and one of true, false, t, f, on, off, t and 0.
func Bool(name rune, helpvalue ...string) *bool {
var b bool
CommandLine.Flag(&b, name, helpvalue...)
return &b
}
func BoolLong(name string, short rune, helpvalue ...string) *bool {
var p bool
CommandLine.FlagLong(&p, name, short, helpvalue...)
return &p
}
func (s *Set) Bool(name rune, helpvalue ...string) *bool {
var b bool
s.Flag(&b, name, helpvalue...)
return &b
}
func (s *Set) BoolLong(name string, short rune, helpvalue ...string) *bool {
var p bool
s.FlagLong(&p, name, short, helpvalue...)
return &p
}

@ -0,0 +1,106 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strings"
"testing"
)
var boolTests = []struct {
where string
in []string
f bool
fc int
opt bool
optc int
err string
}{
{
loc(),
[]string{},
false, 0,
false, 0,
"",
},
{
loc(),
[]string{"test", "-f", "--opt"},
true, 1,
true, 1,
"",
},
{
loc(),
[]string{"test", "--f", "--opt"},
true, 1,
true, 1,
"",
},
{
loc(),
[]string{"test", "-ff", "-f", "--opt", "--opt"},
true, 3,
true, 2,
"",
},
{
loc(),
[]string{"test", "--opt", "--opt=false"},
false, 0,
false, 2,
"",
},
{
loc(),
[]string{"test", "-f", "false"},
true, 1,
false, 0,
"",
},
{
loc(),
[]string{"test", "-f=false"},
true, 1,
false, 0,
"test: unknown option: -=\n",
},
{
loc(),
[]string{"test", "-f", "false"},
true, 1,
false, 0,
"",
},
}
func TestBool(t *testing.T) {
for x, tt := range boolTests {
reset()
f := Bool('f')
opt := BoolLong("opt", 0)
if strings.Index(tt.where, ":-") > 0 {
tt.where = fmt.Sprintf("#%d", x)
}
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if got, want := *f, tt.f; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
if got, want := *opt, tt.opt; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
if got, want := GetCount('f'), tt.fc; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
if got, want := GetCount("opt"), tt.optc; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
}
}

@ -0,0 +1,34 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"testing"
)
var breakupTests = []struct {
in string
max int
out []string
}{
{"", 8, []string{}},
{"a fox", 8, []string{"a fox"}},
{"a foxhound is sly", 2, []string{"a", "foxhound", "is", "sly"}},
{"a foxhound is sly", 5, []string{"a", "foxhound", "is", "sly"}},
{"a foxhound is sly", 6, []string{"a", "foxhound", "is sly"}},
{"a foxhound is sly", 7, []string{"a", "foxhound", "is sly"}},
{"a foxhound is sly", 8, []string{"a", "foxhound", "is sly"}},
{"a foxhound is sly", 9, []string{"a", "foxhound", "is sly"}},
{"a foxhound is sly", 10, []string{"a foxhound", "is sly"}},
}
func TestBreakup(t *testing.T) {
for x, tt := range breakupTests {
out := breakup(tt.in, tt.max)
if badSlice(out, tt.out) {
t.Errorf("#%d: got %v, want %v", x, out, tt.out)
}
}
}

@ -0,0 +1,69 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strconv"
)
type counterValue int
func (b *counterValue) Set(value string, opt Option) error {
if value == "" {
*b++
} else {
v, err := strconv.ParseInt(value, 0, strconv.IntSize)
if err != nil {
if e, ok := err.(*strconv.NumError); ok {
switch e.Err {
case strconv.ErrRange:
err = fmt.Errorf("value out of range: %s", value)
case strconv.ErrSyntax:
err = fmt.Errorf("not a valid number: %s", value)
}
}
return err
}
*b = counterValue(v)
}
return nil
}
func (b *counterValue) String() string {
return strconv.Itoa(int(*b))
}
// Counter creates a counting flag stored as an int. Each time the option
// is seen while parsing the value is incremented. The value of the counter
// may be explicitly set by using the long form:
//
// --counter=5
// --c=5
//
// Further instances of the option will increment from the set value.
func Counter(name rune, helpvalue ...string) *int {
var p int
CommandLine.FlagLong((*counterValue)(&p), "", name, helpvalue...).SetFlag()
return &p
}
func (s *Set) Counter(name rune, helpvalue ...string) *int {
var p int
s.FlagLong((*counterValue)(&p), "", name, helpvalue...).SetFlag()
return &p
}
func CounterLong(name string, short rune, helpvalue ...string) *int {
var p int
CommandLine.FlagLong((*counterValue)(&p), name, short, helpvalue...).SetFlag()
return &p
}
func (s *Set) CounterLong(name string, short rune, helpvalue ...string) *int {
var p int
s.FlagLong((*counterValue)(&p), name, short, helpvalue...).SetFlag()
return &p
}

@ -0,0 +1,76 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strings"
"testing"
)
var counterTests = []struct {
where string
in []string
c int
cnt int
err string
}{
{
loc(),
[]string{},
0,
0,
"",
},
{
loc(),
[]string{"test", "-c", "--cnt"},
1,
1,
"",
},
{
loc(),
[]string{"test", "-cc", "-c", "--cnt", "--cnt"},
3,
2,
"",
},
{
loc(),
[]string{"test", "--c=17", "--cnt=42"},
17,
42,
"",
},
{
loc(),
[]string{"test", "--cnt=false"},
0, 0,
"test: not a valid number: false\n",
},
}
func TestCounter(t *testing.T) {
for x, tt := range counterTests {
reset()
c := Counter('c')
cnt := CounterLong("cnt", 0)
if strings.Index(tt.where, ":-") > 0 {
tt.where = fmt.Sprintf("#%d", x)
}
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if got, want := *c, tt.c; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
if got, want := *cnt, tt.cnt; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
}
}

@ -0,0 +1,28 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import "time"
// Duration creates an option that parses its value as a time.Duration.
func Duration(name rune, value time.Duration, helpvalue ...string) *time.Duration {
CommandLine.FlagLong(&value, "", name, helpvalue...)
return &value
}
func (s *Set) Duration(name rune, value time.Duration, helpvalue ...string) *time.Duration {
s.FlagLong(&value, "", name, helpvalue...)
return &value
}
func DurationLong(name string, short rune, value time.Duration, helpvalue ...string) *time.Duration {
CommandLine.FlagLong(&value, name, short, helpvalue...)
return &value
}
func (s *Set) DurationLong(name string, short rune, value time.Duration, helpvalue ...string) *time.Duration {
s.FlagLong(&value, name, short, helpvalue...)
return &value
}

@ -0,0 +1,73 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strings"
"testing"
"time"
)
var durationTests = []struct {
where string
in []string
d time.Duration
dur time.Duration
err string
}{
{
loc(),
[]string{},
17, 42,
"",
},
{
loc(),
[]string{"test", "-d", "1s", "--duration", "2s"},
time.Second, 2 * time.Second,
"",
},
{
loc(),
[]string{"test", "-d1s", "-d2s"},
2 * time.Second, 42,
"",
},
{
loc(),
[]string{"test", "-d1"},
17, 42,
"test: time: missing unit in duration 1\n",
},
{
loc(),
[]string{"test", "--duration", "foo"},
17, 42,
"test: time: invalid duration foo\n",
},
}
func TestDuration(t *testing.T) {
for x, tt := range durationTests {
reset()
d := Duration('d', 17)
opt := DurationLong("duration", 0, 42)
if strings.Index(tt.where, ":-") > 0 {
tt.where = fmt.Sprintf("#%d", x)
}
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if got, want := *d, tt.d; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
if got, want := *opt, tt.dur; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
}
}

@ -0,0 +1,79 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"errors"
"fmt"
"sync"
)
type enumValue string
var (
enumValuesMu sync.Mutex
enumValues = make(map[*enumValue]map[string]struct{})
)
func (s *enumValue) Set(value string, opt Option) error {
enumValuesMu.Lock()
es, ok := enumValues[s]
enumValuesMu.Unlock()
if !ok || es == nil {
return errors.New("this option has no values")
}
if _, ok := es[value]; !ok {
return errors.New("invalid value: " + value)
}
*s = enumValue(value)
return nil
}
func (s *enumValue) String() string {
return string(*s)
}
// Enum creates an option that can only be set to one of the enumerated strings
// passed in values. Passing nil or an empty slice results in an option that
// will always fail. If not "", value is the default value of the enum. If
// value is not listed in values then Enum will produce an error on standard
// error and then exit the program with a status of 1.
func Enum(name rune, values []string, value string, helpvalue ...string) *string {
return CommandLine.Enum(name, values, value, helpvalue...)
}
func (s *Set) Enum(name rune, values []string, value string, helpvalue ...string) *string {
var p enumValue
p.define(values, value, &option{short: name})
s.FlagLong(&p, "", name, helpvalue...)
return (*string)(&p)
}
func EnumLong(name string, short rune, values []string, value string, helpvalue ...string) *string {
return CommandLine.EnumLong(name, short, values, value, helpvalue...)
}
func (s *Set) EnumLong(name string, short rune, values []string, value string, helpvalue ...string) *string {
var p enumValue
p.define(values, value, &option{short: short, long: name})
s.FlagLong(&p, name, short, helpvalue...)
return (*string)(&p)
}
func (e *enumValue) define(values []string, def string, opt Option) {
m := make(map[string]struct{})
for _, v := range values {
m[v] = struct{}{}
}
enumValuesMu.Lock()
enumValues[e] = m
enumValuesMu.Unlock()
if def != "" {
if err := e.Set(def, nil); err != nil {
fmt.Fprintf(stderr, "setting default for %s: %v\n", opt.Name(), err)
exit(1)
}
}
}

@ -0,0 +1,79 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strings"
"testing"
)
var enumTests = []struct {
where string
in []string
values []string
def string
out string
err string
}{
{
loc(),
nil,
[]string{},
"",
"",
"",
},
{
loc(),
[]string{"test", "-e", "val1"},
[]string{"val1", "val2"},
"",
"val1",
"",
},
{
loc(),
[]string{"test", "-e", "val1", "-e", "val2"},
[]string{"val1", "val2"},
"",
"val2",
"",
},
{
loc(),
[]string{"test"},
[]string{"val1", "val2"},
"val2",
"val2",
"",
},
{
loc(),
[]string{"test", "-e", "val3"},
[]string{"val1", "val2"},
"",
"",
"test: invalid value: val3\n",
},
}
func TestEnum(t *testing.T) {
for x, tt := range enumTests {
if strings.Index(tt.where, ":-") > 0 {
tt.where = fmt.Sprintf("#%d", x)
}
reset()
e := Enum('e', tt.values, tt.def)
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if *e != tt.out {
t.Errorf("%s: got %v, want %v", tt.where, *e, tt.out)
}
}
}

@ -0,0 +1,93 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import "fmt"
// An Error is returned by Getopt when it encounters an error.
type Error struct {
ErrorCode // General reason of failure.
Err error // The actual error.
Parameter string // Parameter passed to option, if any
Name string // Option that cause error, if any
}
// Error returns the error message, implementing the error interface.
func (i *Error) Error() string { return i.Err.Error() }
// An ErrorCode indicates what sort of error was encountered.
type ErrorCode int
const (
NoError = ErrorCode(iota)
UnknownOption // an invalid option was encountered
MissingParameter // the options parameter is missing
ExtraParameter // a value was set to a long flag
Invalid // attempt to set an invalid value
)
func (e ErrorCode) String() string {
switch e {
case UnknownOption:
return "unknow option"
case MissingParameter:
return "missing argument"
case ExtraParameter:
return "unxpected value"
case Invalid:
return "error setting value"
}
return "unknown error"
}
// unknownOption returns an Error indicating an unknown option was
// encountered.
func unknownOption(name interface{}) *Error {
i := &Error{ErrorCode: UnknownOption}
switch n := name.(type) {
case rune:
if n == '-' {
i.Name = "-"
} else {
i.Name = "-" + string(n)
}
case string:
i.Name = "--" + n
}
i.Err = fmt.Errorf("unknown option: %s", i.Name)
return i
}
// missingArg returns an Error inidicating option o was not passed
// a required paramter.
func missingArg(o Option) *Error {
return &Error{
ErrorCode: MissingParameter,
Name: o.Name(),
Err: fmt.Errorf("missing parameter for %s", o.Name()),
}
}
// extraArg returns an Error inidicating option o was passed the
// unexpected paramter value.
func extraArg(o Option, value string) *Error {
return &Error{
ErrorCode: ExtraParameter,
Name: o.Name(),
Parameter: value,
Err: fmt.Errorf("unexpected parameter passed to %s: %q", o.Name(), value),
}
}
// setError returns an Error inidicating option o and the specified
// error while setting it to value.
func setError(o Option, value string, err error) *Error {
return &Error{
ErrorCode: Invalid,
Name: o.Name(),
Parameter: value,
Err: err,
}
}

@ -0,0 +1,238 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strconv"
"strings"
"time"
)
type generic struct {
p interface{}
}
// Flag is shorthand for CommandLine.Flag.
func Flag(v interface{}, short rune, helpvalue ...string) Option {
return CommandLine.long(v, "", short, helpvalue...)
}
// FlagLong is shorthand for CommandLine.LongFlag.
func FlagLong(v interface{}, long string, short rune, helpvalue ...string) Option {
return CommandLine.long(v, long, short, helpvalue...)
}
// Flag calls FlagLong with only a short flag name.
func (s *Set) Flag(v interface{}, short rune, helpvalue ...string) Option {
return s.long(v, "", short, helpvalue...)
}
// FlagLong returns an Option in Set s for setting v. If long is not "" then
// the option has a long name, and if short is not 0, the option has a short
// name. v must either be of type getopt.Value or a pointer to one of the
// supported builtin types:
//
// bool, string, []string
// int, int8, int16, int32, int64
// uint, uint8, uint16, uint32, uint64
// float32, float64
// time.Duration
//
// FlagLong will panic if v is not a getopt.Value or one of the supported
// builtin types.
//
// The default value of the flag is the value of v at the time FlagLong is
// called.
func (s *Set) FlagLong(v interface{}, long string, short rune, helpvalue ...string) Option {
return s.long(v, long, short, helpvalue...)
}
func (s *Set) long(v interface{}, long string, short rune, helpvalue ...string) (opt Option) {
// Fix up our location when we return.
if where := calledFrom(); where != "" {
defer func() {
if opt, ok := opt.(*option); ok {
opt.where = where
}
}()
}
switch p := v.(type) {
case Value:
return s.addFlag(p, long, short, helpvalue...)
case *bool:
return s.addFlag(&generic{v}, long, short, helpvalue...).SetFlag()
case *string, *[]string:
return s.addFlag(&generic{v}, long, short, helpvalue...)
case *int, *int8, *int16, *int32, *int64:
return s.addFlag(&generic{v}, long, short, helpvalue...)
case *uint, *uint8, *uint16, *uint32, *uint64:
return s.addFlag(&generic{v}, long, short, helpvalue...)
case *float32, *float64:
return s.addFlag(&generic{v}, long, short, helpvalue...)
case *time.Duration:
return s.addFlag(&generic{v}, long, short, helpvalue...)
default:
panic(fmt.Sprintf("unsupported flag type: %T", v))
}
}
func (g *generic) Set(value string, opt Option) error {
strconvErr := func(err error) error {
if e, ok := err.(*strconv.NumError); ok {
switch e.Err {
case strconv.ErrRange:
err = fmt.Errorf("value out of range: %s", value)
case strconv.ErrSyntax:
err = fmt.Errorf("not a valid number: %s", value)
}
}
return err
}
switch p := g.p.(type) {
case *bool:
switch strings.ToLower(value) {
case "", "1", "true", "on", "t":
*p = true
case "0", "false", "off", "f":
*p = false
default:
return fmt.Errorf("invalid value for bool %s: %q", opt.Name(), value)
}
return nil
case *string:
*p = value
return nil
case *[]string:
a := strings.Split(value, ",")
// If this is the first time we are seen then nil out the
// default value.
if opt.Count() <= 1 {
*p = nil
}
*p = append(*p, a...)
return nil
case *int:
i64, err := strconv.ParseInt(value, 0, strconv.IntSize)
if err == nil {
*p = int(i64)
}
return strconvErr(err)
case *int8:
i64, err := strconv.ParseInt(value, 0, 8)
if err == nil {
*p = int8(i64)
}
return strconvErr(err)
case *int16:
i64, err := strconv.ParseInt(value, 0, 16)
if err == nil {
*p = int16(i64)
}
return strconvErr(err)
case *int32:
i64, err := strconv.ParseInt(value, 0, 32)
if err == nil {
*p = int32(i64)
}
return strconvErr(err)
case *int64:
i64, err := strconv.ParseInt(value, 0, 64)
if err == nil {
*p = i64
}
return strconvErr(err)
case *uint:
u64, err := strconv.ParseUint(value, 0, strconv.IntSize)
if err == nil {
*p = uint(u64)
}
return strconvErr(err)
case *uint8:
u64, err := strconv.ParseUint(value, 0, 8)
if err == nil {
*p = uint8(u64)
}
return strconvErr(err)
case *uint16:
u64, err := strconv.ParseUint(value, 0, 16)
if err == nil {
*p = uint16(u64)
}
return strconvErr(err)
case *uint32:
u64, err := strconv.ParseUint(value, 0, 32)
if err == nil {
*p = uint32(u64)
}
return strconvErr(err)
case *uint64:
u64, err := strconv.ParseUint(value, 0, 64)
if err == nil {
*p = u64
}
return strconvErr(err)
case *float32:
f64, err := strconv.ParseFloat(value, 32)
if err == nil {
*p = float32(f64)
}
return strconvErr(err)
case *float64:
f64, err := strconv.ParseFloat(value, 64)
if err == nil {
*p = f64
}
return strconvErr(err)
case *time.Duration:
v, err := time.ParseDuration(value)
if err == nil {
*p = v
}
return err
}
panic("internal error")
}
func (g *generic) String() string {
switch p := g.p.(type) {
case *bool:
if *p {
return "true"
}
return "false"
case *string:
return *p
case *[]string:
return strings.Join([]string(*p), ",")
case *int:
return strconv.FormatInt(int64(*p), 10)
case *int8:
return strconv.FormatInt(int64(*p), 10)
case *int16:
return strconv.FormatInt(int64(*p), 10)
case *int32:
return strconv.FormatInt(int64(*p), 10)
case *int64:
return strconv.FormatInt(*p, 10)
case *uint:
return strconv.FormatUint(uint64(*p), 10)
case *uint8:
return strconv.FormatUint(uint64(*p), 10)
case *uint16:
return strconv.FormatUint(uint64(*p), 10)
case *uint32:
return strconv.FormatUint(uint64(*p), 10)
case *uint64:
return strconv.FormatUint(*p, 10)
case *float32:
return strconv.FormatFloat(float64(*p), 'g', -1, 32)
case *float64:
return strconv.FormatFloat(*p, 'g', -1, 64)
case *time.Duration:
return p.String()
}
panic("internal error")
}

@ -0,0 +1,319 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"bytes"
"fmt"
"os"
"reflect"
"runtime"
"strings"
"testing"
"time"
)
func TestGeneric(t *testing.T) {
const (
shortTest = iota
longTest
bothTest
)
for _, tt := range []struct {
where string
kind int
val interface{}
str string
def interface{}
in []string
err string
}{
// Do all four tests for string, the rest can mostly just use
// shortTest (the 0 value).
{
where: loc(),
kind: shortTest,
val: "42",
str: "42",
in: []string{"test", "-s", "42"},
},
{
where: loc(),
kind: longTest,
val: "42",
str: "42",
in: []string{"test", "--long", "42"},
},
{
where: loc(),
kind: bothTest,
val: "42",
str: "42",
in: []string{"test", "--both", "42"},
},
{
where: loc(),
kind: bothTest,
val: "42",
str: "42",
in: []string{"test", "-b", "42"},
},
{
where: loc(),
val: "42",
def: "42",
str: "42",
in: []string{"test"},
},
{
where: loc(),
val: "42",
def: "43",
str: "42",
in: []string{"test", "-s", "42"},
},
{
where: loc(),
val: true,
str: "true",
in: []string{"test", "-s"},
},
{
where: loc(),
val: true,
def: true,
str: "true",
in: []string{"test"},
},
{
where: loc(),
kind: longTest,
val: false,
str: "false",
in: []string{"test", "--long=false"},
},
{
where: loc(),
kind: longTest,
val: false,
def: true,
str: "false",
in: []string{"test", "--long=false"},
},
{
where: loc(),
val: int(42),
str: "42",
in: []string{"test", "-s", "42"},
},
{
where: loc(),
val: int8(42),
str: "42",
in: []string{"test", "-s", "42"},
},
{
where: loc(),
val: int16(42),
str: "42",
in: []string{"test", "-s", "42"},
},
{
where: loc(),
val: int32(42),
str: "42",
in: []string{"test", "-s", "42"},
},
{
where: loc(),
val: int64(42),
str: "42",
in: []string{"test", "-s", "42"},
},
{
where: loc(),
val: uint(42),
str: "42",
in: []string{"test", "-s", "42"},
},
{
where: loc(),
val: uint8(42),
str: "42",
in: []string{"test", "-s", "42"},
},
{
where: loc(),
val: uint16(42),
str: "42",
in: []string{"test", "-s", "42"},
},
{
where: loc(),
val: uint32(42),
str: "42",
in: []string{"test", "-s", "42"},
},
{
where: loc(),
val: uint64(42),
str: "42",
in: []string{"test", "-s", "42"},
},
{
where: loc(),
val: float32(4.2),
str: "4.2",
in: []string{"test", "-s", "4.2"},
},
{
where: loc(),
val: float64(4.2),
str: "4.2",
in: []string{"test", "-s", "4.2"},
},
{
where: loc(),
val: time.Duration(time.Second * 42),
def: time.Second * 2,
str: "42s",
in: []string{"test", "-s", "42s"},
},
{
where: loc(),
val: time.Duration(time.Second * 42),
def: time.Second * 2,
str: "42s",
in: []string{"test", "-s42s"},
},
{
where: loc(),
val: time.Duration(time.Second * 2),
def: time.Second * 2,
in: []string{"test", "-s42"},
str: "2s",
err: "test: time: missing unit in duration 42",
},
{
where: loc(),
val: []string{"42", "."},
str: "42,.",
def: []string{"one", "two", "three"},
in: []string{"test", "-s42", "-s."},
},
{
where: loc(),
val: []string{"42", "."},
str: "42,.",
def: []string{"one", "two", "three"},
in: []string{"test", "-s42,."},
},
{
where: loc(),
val: []string{"one", "two", "three"},
def: []string{"one", "two", "three"},
str: "one,two,three",
in: []string{"test"},
},
} {
reset()
var opt Option
val := reflect.New(reflect.TypeOf(tt.val)).Interface()
if tt.def != nil {
reflect.ValueOf(val).Elem().Set(reflect.ValueOf(tt.def))
}
switch tt.kind {
case shortTest:
opt = Flag(val, 's')
case longTest:
opt = FlagLong(val, "long", 0)
case bothTest:
opt = FlagLong(val, "both", 'b')
}
_ = opt
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
continue
}
got := reflect.ValueOf(val).Elem().Interface()
want := reflect.ValueOf(tt.val).Interface()
if !reflect.DeepEqual(got, want) {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
if str := opt.String(); str != tt.str {
t.Errorf("%s: got string %q, want %q", tt.where, str, tt.str)
}
}
}
func TestGenericDup(t *testing.T) {
defer func() {
stderr = os.Stderr
exit = os.Exit
}()
reset()
var v1, v2 string
type myPanic struct{}
var errbuf bytes.Buffer
stderr = &errbuf
_, file, line, _ := runtime.Caller(0)
Flag(&v1, 's')
line++ // line is now the line number of the first call to Flag.
exit = func(i int) { panic(myPanic{}) }
defer func() {
p := recover()
if _, ok := p.(myPanic); ok {
err := errbuf.String()
if !strings.Contains(err, "-s already declared") || !strings.Contains(err, fmt.Sprintf("%s:%d", file, line)) {
t.Errorf("unexpected error: %q\nshould contain \"-s already declared\" and \"%s:%d\"", err, file, line)
}
} else if p == nil {
t.Errorf("Second call to Flag did not fail")
} else {
t.Errorf("panic %v", p)
}
}()
Flag(&v2, 's')
}
func TestGenericDupNested(t *testing.T) {
defer func() {
stderr = os.Stderr
exit = os.Exit
}()
reset()
type myPanic struct{}
var errbuf bytes.Buffer
stderr = &errbuf
_, file, line, _ := runtime.Caller(0)
String('s', "default")
line++ // line is now the line number of the first call to Flag.
exit = func(i int) { panic(myPanic{}) }
defer func() {
p := recover()
if _, ok := p.(myPanic); ok {
err := errbuf.String()
if !strings.Contains(err, "-s already declared") || !strings.Contains(err, fmt.Sprintf("%s:%d", file, line)) {
t.Errorf("unexpected error: %q\nshould contain \"-s already declared\" and \"%s:%d\"", err, file, line)
}
} else if p == nil {
t.Errorf("Second call to Flag did not fail")
} else {
t.Errorf("panic %v", p)
}
}()
String('s', "default")
}

@ -0,0 +1,534 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package getopt (v2) provides traditional getopt processing for implementing
// commands that use traditional command lines. The standard Go flag package
// cannot be used to write a program that parses flags the way ls or ssh does,
// for example. Version 2 of this package has a simplified API.
//
// See the github.com/pborman/options package for a simple structure based
// interface to this package.
//
// USAGE
//
// Getopt supports functionality found in both the standard BSD getopt as well
// as (one of the many versions of) the GNU getopt_long. Being a Go package,
// this package makes common usage easy, but still enables more controlled usage
// if needed.
//
// Typical usage:
//
// // Declare flags and have getopt return pointers to the values.
// helpFlag := getopt.Bool('?', "display help")
// cmdFlag := getopt.StringLong("command", 'c', "default", "the command)
//
// // Declare flags against existing variables.
// var {
// fileName = "/the/default/path"
// timeout = time.Second * 5
// verbose bool
// }
// func init() {
// getopt.Flag(&verbose, 'v', "be verbose")
// getopt.FlagLong(&fileName, "path", 0, "the path")
// getopt.FlagLong(&timeout, "timeout", 't', "some timeout")
// }
//
// func main() {
// // Parse the program arguments
// getopt.Parse()
// // Get the remaining positional parameters
// args := getopt.Args()
// ...
//
// If you don't want the program to exit on error, use getopt.Getopt:
//
// err := getopt.Getopt(nil)
// if err != nil {
// // code to handle error
// fmt.Fprintln(os.Stderr, err)
// }
//
// FLAG SYNTAX
//
// Support is provided for both short (-f) and long (--flag) options. A single
// option may have both a short and a long name. Each option may be a flag or a
// value. A value takes an argument.
//
// Declaring no long names causes this package to process arguments like the
// traditional BSD getopt.
//
// Short flags may be combined into a single parameter. For example, "-a -b -c"
// may also be expressed "-abc". Long flags must stand on their own "--alpha
// --beta"
//
// Values require an argument. For short options the argument may either be
// immediately following the short name or as the next argument. Only one short
// value may be combined with short flags in a single argument; the short value
// must be after all short flags. For example, if f is a flag and v is a value,
// then:
//
// -vvalue (sets v to "value")
// -v value (sets v to "value")
// -fvvalue (sets f, and sets v to "value")
// -fv value (sets f, and sets v to "value")
// -vf value (set v to "f" and value is the first parameter)
//
// For the long value option val:
//
// --val value (sets val to "value")
// --val=value (sets val to "value")
// --valvalue (invalid option "valvalue")
//
// Values with an optional value only set the value if the value is part of the
// same argument. In any event, the option count is increased and the option is
// marked as seen.
//
// -v -f (sets v and f as being seen)
// -vvalue -f (sets v to "value" and sets f)
// --val -f (sets v and f as being seen)
// --val=value -f (sets v to "value" and sets f)
//
// There is no convenience function defined for making the value optional. The
// SetOptional method must be called on the actual Option.
//
// v := String("val", 'v', "", "the optional v")
// Lookup("v").SetOptional()
//
// var s string
// FlagLong(&s, "val", 'v', "the optional v).SetOptional()
//
// Parsing continues until the first non-option or "--" is encountered.
//
// The short name "-" can be used, but it either is specified as "-" or as part
// of a group of options, for example "-f-". If there are no long options
// specified then "--f" could also be used. If "-" is not declared as an option
// then the single "-" will also terminate the option processing but unlike
// "--", the "-" will be part of the remaining arguments.
//
// ADVANCED USAGE
//
// Normally the parsing is performed by calling the Parse function. If it is
// important to see the order of the options then the Getopt function should be
// used. The standard Parse function does the equivalent of:
//
// func Parse() {
// if err := getopt.Getopt(os.Args, nil); err != nil {
// fmt.Fprintln(os.Stderr, err)
// s.usage()
// os.Exit(1)
// }
//
// When calling Getopt it is the responsibility of the caller to print any
// errors.
//
// Normally the default option set, CommandLine, is used. Other option sets may
// be created with New.
//
// After parsing, the sets Args will contain the non-option arguments. If an
// error is encountered then Args will begin with argument that caused the
// error.
//
// It is valid to call a set's Parse a second time to amen flags or values. As
// an example:
//
// var a = getopt.Bool('a', "", "The a flag")
// var b = getopt.Bool('b', "", "The a flag")
// var cmd = ""
//
// var opts = getopt.CommandLine
//
// opts.Parse(os.Args)
// if opts.NArgs() > 0 {
// cmd = opts.Arg(0)
// opts.Parse(opts.Args())
// }
//
// If called with set to { "prog", "-a", "cmd", "-b", "arg" } then both and and
// b would be set, cmd would be set to "cmd", and opts.Args() would return {
// "arg" }.
//
// Unless an option type explicitly prohibits it, an option may appear more than
// once in the arguments. The last value provided to the option is the value.
//
// BUILTIN TYPES
//
// The Flag and FlagLong functions support most standard Go types. For the
// list, see the description of FlagLong below for a list of supported types.
//
// There are also helper routines to allow single line flag declarations. These
// types are: Bool, Counter, Duration, Enum, Int16, Int32, Int64, Int, List,
// Signed, String, Uint16, Uint32, Uint64, Uint, and Unsigned.
//
// Each comes in a short and long flavor, e.g., Bool and BoolLong and include
// functions to set the flags on the standard command line or for a specific Set
// of flags.
//
// Except for the Counter, Enum, Signed and Unsigned types, all of these types
// can be declared using Flag and FlagLong by passing in a pointer to the
// appropriate type.
//
// DECLARING NEW FLAG TYPES
//
// A pointer to any type that implements the Value interface may be passed to
// Flag or FlagLong.
//
// VALUEHELP
//
// All non-flag options are created with a "valuehelp" as the last parameter.
// Valuehelp should be 0, 1, or 2 strings. The first string, if provided, is
// the usage message for the option. If the second string, if provided, is the
// name to use for the value when displaying the usage. If not provided the
// term "value" is assumed.
//
// The usage message for the option created with
//
// StringLong("option", 'o', "defval", "a string of letters")
//
// is
//
// -o, -option=value
//
// StringLong("option", 'o', "defval", "a string of letters", "string")
//
// is
//
// -o, -option=string
package getopt
import (
"fmt"
"io"
"os"
"path"
"sort"
"strings"
)
// stderr allows tests to capture output to standard error.
var stderr io.Writer = os.Stderr
// exit allows tests to capture an os.Exit call
var exit = os.Exit
// DisplayWidth is used to determine where to split usage long lines.
var DisplayWidth = 80
// HelpColumn is the maximum column position that help strings start to display
// at. If the option usage is too long then the help string will be displayed
// on the next line. For example:
//
// -a this is the a flag
// -u, --under=location
// the u flag's usage is quite long
var HelpColumn = 20
// PrintUsage prints the usage line and set of options of set S to w.
func (s *Set) PrintUsage(w io.Writer) {
parts := make([]string, 2, 4)
parts[0] = "Usage:"
parts[1] = s.program
if usage := s.UsageLine(); usage != "" {
parts = append(parts, usage)
}
if s.parameters != "" {
parts = append(parts, s.parameters)
}
fmt.Fprintln(w, strings.Join(parts, " "))
s.PrintOptions(w)
}
// UsageLine returns the usage line for the set s. The set's program name and
// parameters, if any, are not included.
func (s *Set) UsageLine() string {
sort.Sort(s.options)
flags := ""
// Build up the list of short flag names and also compute
// how to display the option in the longer help listing.
// We also keep track of the longest option usage string
// that is no more than HelpColumn-3 bytes (at which point
// we use two lines to display the help). The three
// is for the leading space and the two spaces before the
// help string.
for _, opt := range s.options {
if opt.name == "" {
opt.name = "value"
}
if opt.uname == "" {
opt.uname = opt.usageName()
}
if opt.flag && opt.short != 0 && opt.short != '-' {
flags += string(opt.short)
}
}
var opts []string
// The short option - is special
if s.shortOptions['-'] != nil {
opts = append(opts, "-")
}
// If we have a bundle of flags, add them to the list
if flags != "" {
opts = append(opts, "-"+flags)
}
// Now append all the long options and options that require
// values.
for _, opt := range s.options {
if opt.flag {
if opt.short != 0 {
continue
}
flags = "--" + opt.long
} else if opt.short != 0 {
flags = "-" + string(opt.short) + " " + opt.name
} else {
flags = "--" + string(opt.long) + " " + opt.name
}
opts = append(opts, flags)
}
flags = strings.Join(opts, "] [")
if flags != "" {
flags = "[" + flags + "]"
}
return flags
}
// PrintOptions prints the list of options in s to w.
func (s *Set) PrintOptions(w io.Writer) {
sort.Sort(s.options)
max := 4
for _, opt := range s.options {
if opt.name == "" {
opt.name = "value"
}
if opt.uname == "" {
opt.uname = opt.usageName()
}
if max < len(opt.uname) && len(opt.uname) <= HelpColumn-3 {
max = len(opt.uname)
}
}
// Now print one or more usage lines per option.
for _, opt := range s.options {
if opt.uname != "" {
opt.help = strings.TrimSpace(opt.help)
if len(opt.help) == 0 {
fmt.Fprintf(w, " %s\n", opt.uname)
continue
}
help := strings.Split(opt.help, "\n")
// If they did not put in newlines then we will insert
// them to keep the help messages from wrapping.
if len(help) == 1 {
help = breakup(help[0], DisplayWidth-HelpColumn)
}
if len(opt.uname) <= max {
fmt.Fprintf(w, " %-*s %s\n", max, opt.uname, help[0])
help = help[1:]
} else {
fmt.Fprintf(w, " %s\n", opt.uname)
}
for _, s := range help {
fmt.Fprintf(w, " %-*s %s\n", max, " ", s)
}
}
}
}
// breakup breaks s up into strings no longer than max bytes.
func breakup(s string, max int) []string {
var a []string
for {
// strip leading spaces
for len(s) > 0 && s[0] == ' ' {
s = s[1:]
}
// If the option is no longer than the max just return it
if len(s) <= max {
if len(s) != 0 {
a = append(a, s)
}
return a
}
x := max
for s[x] != ' ' {
// the first word is too long?!
if x == 0 {
x = max
for x < len(s) && s[x] != ' ' {
x++
}
if x == len(s) {
x--
}
break
}
x--
}
for s[x] == ' ' {
x--
}
a = append(a, s[:x+1])
s = s[x+1:]
}
}
// Parse uses Getopt to parse args using the options set for s. The first
// element of args is used to assign the program for s if it is not yet set. On
// error, Parse displays the error message as well as a usage message on
// standard error and then exits the program.
func (s *Set) Parse(args []string) {
if err := s.Getopt(args, nil); err != nil {
fmt.Fprintln(stderr, err)
s.usage()
exit(1)
}
}
// Parse uses Getopt to parse args using the options set for s. The first
// element of args is used to assign the program for s if it is not yet set.
// Getop calls fn, if not nil, for each option parsed.
//
// Getopt returns nil when all options have been processed (a non-option
// argument was encountered, "--" was encountered, or fn returned false).
//
// On error getopt returns a reference to an InvalidOption (which implements the
// error interface).
func (s *Set) Getopt(args []string, fn func(Option) bool) (err error) {
s.setState(InProgress)
defer func() {
if s.State() == InProgress {
switch {
case err != nil:
s.setState(Failure)
case len(s.args) == 0:
s.setState(EndOfArguments)
default:
s.setState(Unknown)
}
}
}()
if fn == nil {
fn = func(Option) bool { return true }
}
if len(args) == 0 {
return nil
}
if s.program == "" {
s.program = path.Base(args[0])
}
args = args[1:]
Parsing:
for len(args) > 0 {
arg := args[0]
s.args = args
args = args[1:]
// end of options?
if arg == "" || arg[0] != '-' {
s.setState(EndOfOptions)
return nil
}
if arg == "-" {
goto ShortParsing
}
// explicitly request end of options?
if arg == "--" {
s.args = args
s.setState(DashDash)
return nil
}
// Long option processing
if len(s.longOptions) > 0 && arg[1] == '-' {
e := strings.IndexRune(arg, '=')
var value string
if e > 0 {
value = arg[e+1:]
arg = arg[:e]
}
opt := s.longOptions[arg[2:]]
// If we are processing long options then --f is -f
// if f is not defined as a long option.
// This lets you say --f=false
if opt == nil && len(arg[2:]) == 1 {
opt = s.shortOptions[rune(arg[2])]
}
if opt == nil {
return unknownOption(arg[2:])
}
opt.isLong = true
// If we require an option and did not have an =
// then use the next argument as an option.
if !opt.flag && e < 0 && !opt.optional {
if len(args) == 0 {
return missingArg(opt)
}
value = args[0]
args = args[1:]
}
opt.count++
if err := opt.value.Set(value, opt); err != nil {
return setError(opt, value, err)
}
if !fn(opt) {
s.setState(Terminated)
return nil
}
continue Parsing
}
// Short option processing
arg = arg[1:] // strip -
ShortParsing:
for i, c := range arg {
opt := s.shortOptions[c]
if opt == nil {
// In traditional getopt, if - is not registered
// as an option, a lone - is treated as
// if there were a -- in front of it.
if arg == "-" {
s.setState(Dash)
return nil
}
return unknownOption(c)
}
opt.isLong = false
opt.count++
var value string
if !opt.flag {
value = arg[1+i:]
if value == "" && !opt.optional {
if len(args) == 0 {
return missingArg(opt)
}
value = args[0]
args = args[1:]
}
}
if err := opt.value.Set(value, opt); err != nil {
return setError(opt, value, err)
}
if !fn(opt) {
s.setState(Terminated)
return nil
}
if !opt.flag {
continue Parsing
}
}
}
s.args = []string{}
return nil
}

@ -0,0 +1,157 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
// Int creates an option that parses its value as an integer.
func Int(name rune, value int, helpvalue ...string) *int {
return CommandLine.Int(name, value, helpvalue...)
}
func (s *Set) Int(name rune, value int, helpvalue ...string) *int {
s.Flag(&value, name, helpvalue...)
return &value
}
func IntLong(name string, short rune, value int, helpvalue ...string) *int {
return CommandLine.IntLong(name, short, value, helpvalue...)
}
func (s *Set) IntLong(name string, short rune, value int, helpvalue ...string) *int {
s.FlagLong(&value, name, short, helpvalue...)
return &value
}
// Int16 creates an option that parses its value as a 16 bit integer.
func Int16(name rune, value int16, helpvalue ...string) *int16 {
return CommandLine.Int16(name, value, helpvalue...)
}
func (s *Set) Int16(name rune, value int16, helpvalue ...string) *int16 {
s.Flag(&value, name, helpvalue...)
return &value
}
func Int16Long(name string, short rune, value int16, helpvalue ...string) *int16 {
return CommandLine.Int16Long(name, short, value, helpvalue...)
}
func (s *Set) Int16Long(name string, short rune, value int16, helpvalue ...string) *int16 {
s.FlagLong(&value, name, short, helpvalue...)
return &value
}
// Int32 creates an option that parses its value as a 32 bit integer.
func Int32(name rune, value int32, helpvalue ...string) *int32 {
return CommandLine.Int32(name, value, helpvalue...)
}
func (s *Set) Int32(name rune, value int32, helpvalue ...string) *int32 {
s.Flag(&value, name, helpvalue...)
return &value
}
func Int32Long(name string, short rune, value int32, helpvalue ...string) *int32 {
return CommandLine.Int32Long(name, short, value, helpvalue...)
}
func (s *Set) Int32Long(name string, short rune, value int32, helpvalue ...string) *int32 {
s.FlagLong(&value, name, short, helpvalue...)
return &value
}
// Int64 creates an option that parses its value as a 64 bit integer.
func Int64(name rune, value int64, helpvalue ...string) *int64 {
return CommandLine.Int64(name, value, helpvalue...)
}
func (s *Set) Int64(name rune, value int64, helpvalue ...string) *int64 {
s.Flag(&value, name, helpvalue...)
return &value
}
func Int64Long(name string, short rune, value int64, helpvalue ...string) *int64 {
return CommandLine.Int64Long(name, short, value, helpvalue...)
}
func (s *Set) Int64Long(name string, short rune, value int64, helpvalue ...string) *int64 {
s.FlagLong(&value, name, short, helpvalue...)
return &value
}
// Uint creates an option that parses its value as an unsigned integer.
func Uint(name rune, value uint, helpvalue ...string) *uint {
return CommandLine.Uint(name, value, helpvalue...)
}
func (s *Set) Uint(name rune, value uint, helpvalue ...string) *uint {
s.Flag(&value, name, helpvalue...)
return &value
}
func UintLong(name string, short rune, value uint, helpvalue ...string) *uint {
return CommandLine.UintLong(name, short, value, helpvalue...)
}
func (s *Set) UintLong(name string, short rune, value uint, helpvalue ...string) *uint {
s.FlagLong(&value, name, short, helpvalue...)
return &value
}
// Uint16 creates an option that parses its value as a 16 bit unsigned integer.
func Uint16(name rune, value uint16, helpvalue ...string) *uint16 {
return CommandLine.Uint16(name, value, helpvalue...)
}
func (s *Set) Uint16(name rune, value uint16, helpvalue ...string) *uint16 {
s.Flag(&value, name, helpvalue...)
return &value
}
func Uint16Long(name string, short rune, value uint16, helpvalue ...string) *uint16 {
return CommandLine.Uint16Long(name, short, value, helpvalue...)
}
func (s *Set) Uint16Long(name string, short rune, value uint16, helpvalue ...string) *uint16 {
s.FlagLong(&value, name, short, helpvalue...)
return &value
}
// Uint32 creates an option that parses its value as a 32 bit unsigned integer.
func Uint32(name rune, value uint32, helpvalue ...string) *uint32 {
return CommandLine.Uint32(name, value, helpvalue...)
}
func (s *Set) Uint32(name rune, value uint32, helpvalue ...string) *uint32 {
s.Flag(&value, name, helpvalue...)
return &value
}
func Uint32Long(name string, short rune, value uint32, helpvalue ...string) *uint32 {
return CommandLine.Uint32Long(name, short, value, helpvalue...)
}
func (s *Set) Uint32Long(name string, short rune, value uint32, helpvalue ...string) *uint32 {
s.FlagLong(&value, name, short, helpvalue...)
return &value
}
// Uint64 creates an option that parses its value as a 64 bit unsigned integer.
func Uint64(name rune, value uint64, helpvalue ...string) *uint64 {
return CommandLine.Uint64(name, value, helpvalue...)
}
func (s *Set) Uint64(name rune, value uint64, helpvalue ...string) *uint64 {
s.Flag(&value, name, helpvalue...)
return &value
}
func Uint64Long(name string, short rune, value uint64, helpvalue ...string) *uint64 {
return CommandLine.Uint64Long(name, short, value, helpvalue...)
}
func (s *Set) Uint64Long(name string, short rune, value uint64, helpvalue ...string) *uint64 {
s.FlagLong(&value, name, short, helpvalue...)
return &value
}

@ -0,0 +1,595 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strings"
"testing"
)
var intTests = []struct {
where string
in []string
i int
int int
err string
}{
{
loc(),
[]string{},
17, 42,
"",
},
{
loc(),
[]string{"test", "-i", "1", "--int", "2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "--int=2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "-i2"},
2, 42,
"",
},
{
loc(),
[]string{"test", "-i=1"},
17, 42,
"test: not a valid number: =1\n",
},
{
loc(),
[]string{"test", "-i0x20"},
0x20, 42,
"",
},
{
loc(),
[]string{"test", "-i010"},
8, 42,
"",
},
}
func TestInt(t *testing.T) {
for x, tt := range intTests {
reset()
i := Int('i', 17)
opt := IntLong("int", 0, 42)
if strings.Index(tt.where, ":-") > 0 {
tt.where = fmt.Sprintf("#%d", x)
}
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if got, want := *i, tt.i; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
if got, want := *opt, tt.int; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
}
}
var int16Tests = []struct {
where string
in []string
i int16
int16 int16
err string
}{
{
loc(),
[]string{},
17, 42,
"",
},
{
loc(),
[]string{"test", "-i", "1", "--int16", "2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "--int16=2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "-i2"},
2, 42,
"",
},
{
loc(),
[]string{"test", "-i=1"},
17, 42,
"test: not a valid number: =1\n",
},
{
loc(),
[]string{"test", "-i0x20"},
0x20, 42,
"",
},
{
loc(),
[]string{"test", "-i010"},
8, 42,
"",
},
}
func TestInt16(t *testing.T) {
for x, tt := range int16Tests {
reset()
i := Int16('i', 17)
opt := Int16Long("int16", 0, 42)
if strings.Index(tt.where, ":-") > 0 {
tt.where = fmt.Sprintf("#%d", x)
}
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if got, want := *i, tt.i; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
if got, want := *opt, tt.int16; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
}
}
var int32Tests = []struct {
where string
in []string
i int32
int32 int32
err string
}{
{
loc(),
[]string{},
17, 42,
"",
},
{
loc(),
[]string{"test", "-i", "1", "--int32", "2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "--int32=2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "-i2"},
2, 42,
"",
},
{
loc(),
[]string{"test", "-i=1"},
17, 42,
"test: not a valid number: =1\n",
},
{
loc(),
[]string{"test", "-i0x20"},
0x20, 42,
"",
},
{
loc(),
[]string{"test", "-i010"},
8, 42,
"",
},
}
func TestInt32(t *testing.T) {
for x, tt := range int32Tests {
reset()
i := Int32('i', 17)
opt := Int32Long("int32", 0, 42)
if strings.Index(tt.where, ":-") > 0 {
tt.where = fmt.Sprintf("#%d", x)
}
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if got, want := *i, tt.i; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
if got, want := *opt, tt.int32; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
}
}
var int64Tests = []struct {
where string
in []string
i int64
int64 int64
err string
}{
{
loc(),
[]string{},
17, 42,
"",
},
{
loc(),
[]string{"test", "-i", "1", "--int64", "2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "--int64=2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "-i2"},
2, 42,
"",
},
{
loc(),
[]string{"test", "-i=1"},
17, 42,
"test: not a valid number: =1\n",
},
{
loc(),
[]string{"test", "-i0x20"},
0x20, 42,
"",
},
{
loc(),
[]string{"test", "-i010"},
8, 42,
"",
},
}
func TestInt64(t *testing.T) {
for x, tt := range int64Tests {
reset()
i := Int64('i', 17)
opt := Int64Long("int64", 0, 42)
if strings.Index(tt.where, ":-") > 0 {
tt.where = fmt.Sprintf("#%d", x)
}
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if got, want := *i, tt.i; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
if got, want := *opt, tt.int64; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
}
}
var uintTests = []struct {
where string
in []string
i uint
uint uint
err string
}{
{
loc(),
[]string{},
17, 42,
"",
},
{
loc(),
[]string{"test", "-i", "1", "--uint", "2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "--uint=2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "-i2"},
2, 42,
"",
},
{
loc(),
[]string{"test", "-i=1"},
17, 42,
"test: not a valid number: =1\n",
},
{
loc(),
[]string{"test", "-i0x20"},
0x20, 42,
"",
},
{
loc(),
[]string{"test", "-i010"},
8, 42,
"",
},
}
func TestUint(t *testing.T) {
for x, tt := range uintTests {
reset()
i := Uint('i', 17)
opt := UintLong("uint", 0, 42)
if strings.Index(tt.where, ":-") > 0 {
tt.where = fmt.Sprintf("#%d", x)
}
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if got, want := *i, tt.i; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
if got, want := *opt, tt.uint; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
}
}
var uint16Tests = []struct {
where string
in []string
i uint16
uint16 uint16
err string
}{
{
loc(),
[]string{},
17, 42,
"",
},
{
loc(),
[]string{"test", "-i", "1", "--uint16", "2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "--uint16=2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "-i2"},
2, 42,
"",
},
{
loc(),
[]string{"test", "-i=1"},
17, 42,
"test: not a valid number: =1\n",
},
{
loc(),
[]string{"test", "-i0x20"},
0x20, 42,
"",
},
{
loc(),
[]string{"test", "-i010"},
8, 42,
"",
},
}
func TestUint16(t *testing.T) {
for x, tt := range uint16Tests {
reset()
i := Uint16('i', 17)
opt := Uint16Long("uint16", 0, 42)
if strings.Index(tt.where, ":-") > 0 {
tt.where = fmt.Sprintf("#%d", x)
}
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if got, want := *i, tt.i; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
if got, want := *opt, tt.uint16; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
}
}
var uint32Tests = []struct {
where string
in []string
i uint32
uint32 uint32
err string
}{
{
loc(),
[]string{},
17, 42,
"",
},
{
loc(),
[]string{"test", "-i", "1", "--uint32", "2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "--uint32=2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "-i2"},
2, 42,
"",
},
{
loc(),
[]string{"test", "-i=1"},
17, 42,
"test: not a valid number: =1\n",
},
{
loc(),
[]string{"test", "-i0x20"},
0x20, 42,
"",
},
{
loc(),
[]string{"test", "-i010"},
8, 42,
"",
},
}
func TestUint32(t *testing.T) {
for x, tt := range uint32Tests {
reset()
i := Uint32('i', 17)
opt := Uint32Long("uint32", 0, 42)
if strings.Index(tt.where, ":-") > 0 {
tt.where = fmt.Sprintf("#%d", x)
}
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if got, want := *i, tt.i; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
if got, want := *opt, tt.uint32; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
}
}
var uint64Tests = []struct {
where string
in []string
i uint64
uint64 uint64
err string
}{
{
loc(),
[]string{},
17, 42,
"",
},
{
loc(),
[]string{"test", "-i", "1", "--uint64", "2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "--uint64=2"},
1, 2,
"",
},
{
loc(),
[]string{"test", "-i1", "-i2"},
2, 42,
"",
},
{
loc(),
[]string{"test", "-i=1"},
17, 42,
"test: not a valid number: =1\n",
},
{
loc(),
[]string{"test", "-i0x20"},
0x20, 42,
"",
},
{
loc(),
[]string{"test", "-i010"},
8, 42,
"",
},
}
func TestUint64(t *testing.T) {
for x, tt := range uint64Tests {
reset()
i := Uint64('i', 17)
opt := Uint64Long("uint64", 0, 42)
if strings.Index(tt.where, ":-") > 0 {
tt.where = fmt.Sprintf("#%d", x)
}
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if got, want := *i, tt.i; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
if got, want := *opt, tt.uint64; got != want {
t.Errorf("%s: got %v, want %v", tt.where, got, want)
}
}
}

@ -0,0 +1,32 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
// List creates an option that returns a slice of strings. The parameters
// passed are converted from a comma separated value list into a slice.
// Subsequent occurrences append to the list.
func List(name rune, helpvalue ...string) *[]string {
p := []string{}
CommandLine.Flag(&p, name, helpvalue...)
return &p
}
func (s *Set) List(name rune, helpvalue ...string) *[]string {
p := []string{}
s.Flag(&p, name, helpvalue...)
return &p
}
func ListLong(name string, short rune, helpvalue ...string) *[]string {
p := []string{}
CommandLine.FlagLong(&p, name, short, helpvalue...)
return &p
}
func (s *Set) ListLong(name string, short rune, helpvalue ...string) *[]string {
p := []string{}
s.FlagLong(&p, name, short, helpvalue...)
return &p
}

@ -0,0 +1,99 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import "testing"
var listTests = []struct {
where string
in []string
l, list []string
err string
}{
{
loc(),
[]string{},
nil, nil,
"",
},
{
loc(),
[]string{"test", "-l", "one"},
[]string{"one"}, nil,
"",
},
{
loc(),
[]string{"test", "-lone", "-ltwo"},
[]string{"one", "two"}, nil,
"",
},
{
loc(),
[]string{"test", "--list", "one"},
nil, []string{"one"},
"",
},
{
loc(),
[]string{"test", "--list=one", "--list=two"},
nil, []string{"one", "two"},
"",
},
{
loc(),
[]string{"test", "--list=one,two"},
nil, []string{"one", "two"},
"",
},
}
func TestList(t *testing.T) {
for _, tt := range listTests {
reset()
l := List('l')
list := ListLong("list", 0)
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if badSlice(*l, tt.l) {
t.Errorf("%s: got s = %q, want %q", tt.where, *l, tt.l)
}
if badSlice(*list, tt.list) {
t.Errorf("%s: got s = %q, want %q", tt.where, *list, tt.list)
}
}
}
func TestDefaultList(t *testing.T) {
reset()
list := []string{"d1", "d2", "d3"}
Flag(&list, 'l')
parse([]string{"test"})
want := []string{"d1", "d2", "d3"}
if badSlice(list, want) {
t.Errorf("got s = %q, want %q", list, want)
}
parse([]string{"test", "-l", "one"})
want = []string{"one"}
if badSlice(list, want) {
t.Errorf("got s = %q, want %q", list, want)
}
parse([]string{"test", "-l", "two"})
want = []string{"one", "two"}
if badSlice(list, want) {
t.Errorf("got s = %q, want %q", list, want)
}
Lookup('l').Reset()
want = []string{"d1", "d2", "d3"}
if badSlice(list, want) {
t.Errorf("got s = %q, want %q", list, want)
}
}

@ -0,0 +1,212 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strings"
)
// An Option can be either a Flag or a Value
type Option interface {
// Name returns the name of the option. If the option has been seen
// then the last way it was referenced (short or long) is returned
// otherwise if there is a short name then this will be the short name
// as a string, else it will be the long name.
Name() string
// ShortName always returns the short name of the option, or "" if there
// is no short name. The name does not include the "-".
ShortName() string
// LongName always returns the long name of the option, or "" if there
// is no long name. The name does not include the "--".
LongName() string
// IsFlag returns true if Option is a flag.
IsFlag() bool
// Seen returns true if the flag was seen.
Seen() bool
// Count returns the number of times the flag was seen.
Count() int
// String returns the last value the option was set to.
String() string
// Value returns the Value of the option.
Value() Value
// SetOptional makes the value optional. The option and value are
// always a single argument. Either --option or --option=value. In
// the former case the value of the option does not change but the Set()
// will return true and the value returned by Count() is incremented.
// The short form is either -o or -ovalue. SetOptional returns
// the Option
SetOptional() Option
// SetFlag makes the value a flag. Flags are boolean values and
// normally do not taken a value. They are set to true when seen.
// If a value is passed in the long form then it must be on, case
// insensitivinsensitive, one of "true", "false", "t", "f", "on", "off", "1", "0".
// SetFlag returns the Option
SetFlag() Option
// Reset resets the state of the option so it appears it has not
// yet been seen, including resetting the value of the option
// to its original default state.
Reset()
}
type option struct {
short rune // 0 means no short name
long string // "" means no long name
isLong bool // True if they used the long name
flag bool // true if a boolean flag
defval string // default value
optional bool // true if we take an optional value
help string // help message
where string // file where the option was defined
value Value // current value of option
count int // number of times we have seen this option
name string // name of the value (for usage)
uname string // name of the option (for usage)
}
// usageName returns the name of the option for printing usage lines in one
// of the following forms:
//
// -f
// --flag
// -f, --flag
// -s value
// --set=value
// -s, --set=value
func (o *option) usageName() string {
// Don't print help messages if we have none and there is only one
// way to specify the option.
if o.help == "" && (o.short == 0 || o.long == "") {
return ""
}
n := ""
switch {
case o.short != 0 && o.long == "":
n = "-" + string(o.short)
case o.short == 0 && o.long != "":
n = " --" + o.long
case o.short != 0 && o.long != "":
n = "-" + string(o.short) + ", --" + o.long
}
switch {
case o.flag:
return n
case o.optional:
return n + "[=" + o.name + "]"
case o.long != "":
return n + "=" + o.name
}
return n + " " + o.name
}
// sortName returns the name to sort the option on.
func (o *option) sortName() string {
if o.short != 0 {
return string(o.short) + o.long
}
return o.long[:1] + o.long
}
func (o *option) Seen() bool { return o.count > 0 }
func (o *option) Count() int { return o.count }
func (o *option) IsFlag() bool { return o.flag }
func (o *option) String() string { return o.value.String() }
func (o *option) SetOptional() Option { o.optional = true; return o }
func (o *option) SetFlag() Option { o.flag = true; return o }
func (o *option) Value() Value {
if o == nil {
return nil
}
return o.value
}
func (o *option) Name() string {
if !o.isLong && o.short != 0 {
return "-" + string(o.short)
}
return "--" + o.long
}
func (o *option) ShortName() string {
if o.short != 0 {
return string(o.short)
}
return ""
}
func (o *option) LongName() string {
return o.long
}
// Reset rests an option so that it appears it has not yet been seen.
func (o *option) Reset() {
o.isLong = false
o.count = 0
o.value.Set(o.defval, o)
}
type optionList []*option
func (ol optionList) Len() int { return len(ol) }
func (ol optionList) Swap(i, j int) { ol[i], ol[j] = ol[j], ol[i] }
func (ol optionList) Less(i, j int) bool {
// first check the short names (or the first letter of the long name)
// If they are not equal (case insensitive) then we have our answer
n1 := ol[i].sortName()
n2 := ol[j].sortName()
l1 := strings.ToLower(n1)
l2 := strings.ToLower(n2)
if l1 < l2 {
return true
}
if l2 < l1 {
return false
}
return n1 < n2
}
// AddOption add the option o to set CommandLine if o is not already in set
// CommandLine.
func AddOption(o Option) {
CommandLine.AddOption(o)
}
// AddOption add the option o to set s if o is not already in set s.
func (s *Set) AddOption(o Option) {
opt := o.(*option)
for _, eopt := range s.options {
if opt == eopt {
return
}
}
if opt.short != 0 {
if oo, ok := s.shortOptions[opt.short]; ok {
fmt.Fprintf(stderr, "%s: -%c already declared at %s\n", opt.where, opt.short, oo.where)
exit(1)
}
s.shortOptions[opt.short] = opt
}
if opt.long != "" {
if oo, ok := s.longOptions[opt.long]; ok {
fmt.Fprintf(stderr, "%s: --%s already declared at %s\n", opt.where, opt.long, oo.where)
exit(1)
}
s.longOptions[opt.long] = opt
}
s.options = append(s.options, opt)
}

@ -0,0 +1,291 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"io"
"os"
"sort"
"sync"
)
// A State is why the Getopt returned.
type State int
const (
InProgress = State(iota) // Getopt is still running
Dash // Returned on "-"
DashDash // Returned on "--"
EndOfOptions // End of options reached
EndOfArguments // No more arguments
Terminated // Terminated by callback function
Failure // Terminated due to error
Unknown // Indicates internal error
)
type Set struct {
stateMu sync.Mutex
state State
// args are the parameters remaining after parsing the optoins.
args []string
// program is the name of the program for usage and error messages.
// If not set it will automatically be set to the base name of the
// first argument passed to parse.
program string
// parameters is what is displayed on the usage line after displaying
// the various options.
parameters string
usage func() // usage should print the programs usage and exit.
shortOptions map[rune]*option
longOptions map[string]*option
options optionList
}
// New returns a newly created option set.
func New() *Set {
s := &Set{
shortOptions: make(map[rune]*option),
longOptions: make(map[string]*option),
parameters: "[parameters ...]",
}
s.usage = func() {
s.PrintUsage(stderr)
}
return s
}
func (s *Set) setState(state State) {
s.stateMu.Lock()
s.state = state
s.stateMu.Unlock()
}
// State returns the current state of the Set s. The state is normally the
// reason the most recent call to Getopt returned.
func (s *Set) State() State {
s.stateMu.Lock()
defer s.stateMu.Unlock()
return s.state
}
// The default set of command-line options.
var CommandLine = New()
// PrintUsage calls PrintUsage in the default option set.
func PrintUsage(w io.Writer) { CommandLine.PrintUsage(w) }
// Usage calls the usage function in the default option set.
func Usage() { CommandLine.usage() }
// Parse calls Parse in the default option set with the command line arguments
// found in os.Args.
func Parse() { CommandLine.Parse(os.Args) }
// Getops returns the result of calling Getop in the default option set with the
// command line arguments found in os.Args. The fn function, which may be nil,
// is passed to Getopt.
func Getopt(fn func(Option) bool) error { return CommandLine.Getopt(os.Args, fn) }
// Arg returns the n'th command-line argument. Arg(0) is the first remaining
// argument after options have been processed.
func Arg(n int) string {
if n >= 0 && n < len(CommandLine.args) {
return CommandLine.args[n]
}
return ""
}
// Arg returns the n'th argument. Arg(0) is the first remaining
// argument after options have been processed.
func (s *Set) Arg(n int) string {
if n >= 0 && n < len(s.args) {
return s.args[n]
}
return ""
}
// Args returns the non-option command line arguments.
func Args() []string {
return CommandLine.args
}
// Args returns the non-option arguments.
func (s *Set) Args() []string {
return s.args
}
// NArgs returns the number of non-option command line arguments.
func NArgs() int {
return len(CommandLine.args)
}
// NArgs returns the number of non-option arguments.
func (s *Set) NArgs() int {
return len(s.args)
}
// SetParameters sets the parameters string for printing the command line
// usage. It defaults to "[parameters ...]"
func SetParameters(parameters string) {
CommandLine.parameters = parameters
}
// SetParameters sets the parameters string for printing the s's usage.
// It defaults to "[parameters ...]"
func (s *Set) SetParameters(parameters string) {
s.parameters = parameters
}
// Parameters returns the parameters set by SetParameters on s.
func (s *Set) Parameters() string { return s.parameters }
// SetProgram sets the program name to program. Normally it is determined
// from the zeroth command line argument (see os.Args).
func SetProgram(program string) {
CommandLine.program = program
}
// SetProgram sets s's program name to program. Normally it is determined
// from the zeroth argument passed to Getopt or Parse.
func (s *Set) SetProgram(program string) {
s.program = program
}
// Program returns the program name associated with Set s.
func (s *Set) Program() string { return s.program }
// SetUsage sets the function used by Parse to display the commands usage
// on error. It defaults to calling PrintUsage(os.Stderr).
func SetUsage(usage func()) {
CommandLine.usage = usage
}
// SetUsage sets the function used by Parse to display usage on error. It
// defaults to calling f.PrintUsage(os.Stderr).
func (s *Set) SetUsage(usage func()) {
s.usage = usage
}
// Lookup returns the Option associated with name. Name should either be
// a rune (the short name) or a string (the long name).
func Lookup(name interface{}) Option {
return CommandLine.Lookup(name)
}
// Lookup returns the Option associated with name in s. Name should either be
// a rune (the short name) or a string (the long name).
func (s *Set) Lookup(name interface{}) Option {
switch v := name.(type) {
case rune:
return s.shortOptions[v]
case int:
return s.shortOptions[rune(v)]
case string:
return s.longOptions[v]
}
return nil
}
// IsSet returns true if the Option associated with name was seen while
// parsing the command line arguments. Name should either be a rune (the
// short name) or a string (the long name).
func IsSet(name interface{}) bool {
return CommandLine.IsSet(name)
}
// IsSet returns true if the Option associated with name was seen while
// parsing s. Name should either be a rune (the short name) or a string (the
// long name).
func (s *Set) IsSet(name interface{}) bool {
if opt := s.Lookup(name); opt != nil {
return opt.Seen()
}
return false
}
// GetCount returns the number of times the Option associated with name has been
// seen while parsing the command line arguments. Name should either be a rune
// (the short name) or a string (the long name).
func GetCount(name interface{}) int {
return CommandLine.GetCount(name)
}
// GetCount returns the number of times the Option associated with name has been
// seen while parsing s's arguments. Name should either be a rune (the short
// name) or a string (the long name).
func (s *Set) GetCount(name interface{}) int {
if opt := s.Lookup(name); opt != nil {
return opt.Count()
}
return 0
}
// GetValue returns the final value set to the command-line Option with name.
// If the option has not been seen while parsing s then the default value is
// returned. Name should either be a rune (the short name) or a string (the
// long name).
func GetValue(name interface{}) string {
return CommandLine.GetValue(name)
}
// GetValue returns the final value set to the Option in s associated with name.
// If the option has not been seen while parsing s then the default value is
// returned. Name should either be a rune (the short name) or a string (the
// long name).
func (s *Set) GetValue(name interface{}) string {
if opt := s.Lookup(name); opt != nil {
return opt.String()
}
return ""
}
// Visit visits the command-line options in lexicographical order, calling fn
// for each. It visits only those options that have been set.
func Visit(fn func(Option)) { CommandLine.Visit(fn) }
// Visit visits the options in s in lexicographical order, calling fn
// for each. It visits only those options that have been set.
func (s *Set) Visit(fn func(Option)) {
sort.Sort(s.options)
for _, opt := range s.options {
if opt.count > 0 {
fn(opt)
}
}
}
// VisitAll visits the options in s in lexicographical order, calling fn
// for each. It visits all options, even those not set.
func VisitAll(fn func(Option)) { CommandLine.VisitAll(fn) }
// VisitAll visits the command-line flags in lexicographical order, calling fn
// for each. It visits all flags, even those not set.
func (s *Set) VisitAll(fn func(Option)) {
sort.Sort(s.options)
for _, opt := range s.options {
fn(opt)
}
}
// Reset resets all the command line options to the initial state so it
// appears none of them have been seen.
func Reset() {
CommandLine.Reset()
}
// Reset resets all the options in s to the initial state so it
// appears none of them have been seen.
func (s *Set) Reset() {
for _, opt := range s.options {
opt.Reset()
}
}

@ -0,0 +1,110 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strconv"
"sync"
)
type signed int64
type SignedLimit struct {
Base int // Base for conversion as per strconv.ParseInt
Bits int // Number of bits as per strconv.ParseInt
Min int64 // Minimum allowed value if both Min and Max are not 0
Max int64 // Maximum allowed value if both Min and Max are not 0
}
var (
signedLimitsMu sync.Mutex
signedLimits = make(map[*signed]*SignedLimit)
)
func (n *signed) Set(value string, opt Option) error {
signedLimitsMu.Lock()
l := signedLimits[n]
signedLimitsMu.Unlock()
if l == nil {
return fmt.Errorf("no limits defined for %s", opt.Name())
}
v, err := strconv.ParseInt(value, l.Base, l.Bits)
if err != nil {
if e, ok := err.(*strconv.NumError); ok {
switch e.Err {
case strconv.ErrRange:
err = fmt.Errorf("value out of range: %s", value)
case strconv.ErrSyntax:
err = fmt.Errorf("not a valid number: %s", value)
}
}
return err
}
if l.Min != 0 || l.Max != 0 {
if v < l.Min {
return fmt.Errorf("value out of range (<%v): %s", l.Min, value)
}
if v > l.Max {
return fmt.Errorf("value out of range (>%v): %s", l.Max, value)
}
}
*n = signed(v)
return nil
}
func (n *signed) String() string {
signedLimitsMu.Lock()
l := signedLimits[n]
signedLimitsMu.Unlock()
if l != nil && l.Base != 0 {
return strconv.FormatInt(int64(*n), l.Base)
}
return strconv.FormatInt(int64(*n), 10)
}
// Signed creates an option that is stored in an int64 and is constrained
// by the limits pointed to by l. The Max and Min values are only used if
// at least one of the values are not 0. If Base is 0, the base is implied by
// the string's prefix: base 16 for "0x", base 8 for "0", and base 10 otherwise.
func Signed(name rune, value int64, l *SignedLimit, helpvalue ...string) *int64 {
CommandLine.signedOption(&value, "", name, l, helpvalue...)
return &value
}
func (s *Set) Signed(name rune, value int64, l *SignedLimit, helpvalue ...string) *int64 {
s.signedOption(&value, "", name, l, helpvalue...)
return &value
}
func SignedLong(name string, short rune, value int64, l *SignedLimit, helpvalue ...string) *int64 {
CommandLine.signedOption(&value, name, short, l, helpvalue...)
return &value
}
func (s *Set) SignedLong(name string, short rune, value int64, l *SignedLimit, helpvalue ...string) *int64 {
s.signedOption(&value, name, short, l, helpvalue...)
return &value
}
func (s *Set) signedOption(p *int64, name string, short rune, l *SignedLimit, helpvalue ...string) {
opt := s.FlagLong((*signed)(p), name, short, helpvalue...)
if l.Base > 36 || l.Base == 1 || l.Base < 0 {
fmt.Fprintf(stderr, "invalid base for %s: %d\n", opt.Name(), l.Base)
exit(1)
}
if l.Bits < 0 || l.Bits > 64 {
fmt.Fprintf(stderr, "invalid bit size for %s: %d\n", opt.Name(), l.Bits)
exit(1)
}
if l.Min > l.Max {
fmt.Fprintf(stderr, "min greater than max for %s\n", opt.Name())
exit(1)
}
lim := *l
signedLimitsMu.Lock()
signedLimits[(*signed)(p)] = &lim
signedLimitsMu.Unlock()
}

@ -0,0 +1,97 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strings"
"testing"
)
var signedNumberTests = []struct {
where string
in []string
l SignedLimit
out int64
err string
}{
{
where: loc(),
},
{
loc(),
[]string{"test", "-n", "1010"},
SignedLimit{Base: 2, Bits: 5},
10,
"",
},
{
loc(),
[]string{"test", "-n", "1010"},
SignedLimit{Base: 2, Bits: 4},
0,
"test: value out of range: 1010\n",
},
{
loc(),
[]string{"test", "-n", "-1000"},
SignedLimit{Base: 2, Bits: 4},
-8,
"",
},
{
loc(),
[]string{"test", "-n", "3"},
SignedLimit{Min: 4, Max: 6},
0,
"test: value out of range (<4): 3\n",
},
{
loc(),
[]string{"test", "-n", "4"},
SignedLimit{Min: 4, Max: 6},
4,
"",
},
{
loc(),
[]string{"test", "-n", "5"},
SignedLimit{Min: 4, Max: 6},
5,
"",
},
{
loc(),
[]string{"test", "-n", "6"},
SignedLimit{Min: 4, Max: 6},
6,
"",
},
{
loc(),
[]string{"test", "-n", "7"},
SignedLimit{Min: 4, Max: 6},
0,
"test: value out of range (>6): 7\n",
},
}
func TestSigneds(t *testing.T) {
for x, tt := range signedNumberTests {
if strings.Index(tt.where, ":-") > 0 {
tt.where = fmt.Sprintf("#%d", x)
}
reset()
n := Signed('n', 0, &tt.l)
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if *n != tt.out {
t.Errorf("%s: got %v, want %v", tt.where, *n, tt.out)
}
}
}

@ -0,0 +1,27 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
// String returns a value option that stores is value as a string. The
// initial value of the string is passed in value.
func String(name rune, value string, helpvalue ...string) *string {
CommandLine.Flag(&value, name, helpvalue...)
return &value
}
func (s *Set) String(name rune, value string, helpvalue ...string) *string {
s.Flag(&value, name, helpvalue...)
return &value
}
func StringLong(name string, short rune, value string, helpvalue ...string) *string {
CommandLine.FlagLong(&value, name, short, helpvalue...)
return &value
}
func (s *Set) StringLong(name string, short rune, value string, helpvalue ...string) *string {
s.FlagLong(&value, name, short, helpvalue...)
return &value
}

@ -0,0 +1,77 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import "testing"
var stringTests = []struct {
where string
in []string
sout string
optout string
err string
}{
{
loc(),
[]string{},
"one",
"two",
"",
},
{
loc(),
[]string{"test", "-s"},
"one",
"two",
"test: missing parameter for -s\n",
},
{
loc(),
[]string{"test", "--opt"},
"one",
"two",
"test: missing parameter for --opt\n",
},
{
loc(),
[]string{"test", "-svalue", "--opt=option"},
"value",
"option",
"",
},
{
loc(),
[]string{"test", "-s", "value", "--opt", "option"},
"value",
"option",
"",
},
{
loc(),
[]string{"test", "-swrong", "--opt=wrong", "-s", "value", "--opt", "option"},
"value",
"option",
"",
},
}
func TestString(t *testing.T) {
for _, tt := range stringTests {
reset()
s := String('s', "one")
opt := StringLong("opt", 0, "two")
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if *s != tt.sout {
t.Errorf("%s: got s = %q, want %q", tt.where, *s, tt.sout)
}
if *opt != tt.optout {
t.Errorf("%s: got opt = %q, want %q", tt.where, *opt, tt.optout)
}
}
}

@ -0,0 +1,111 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strconv"
"sync"
)
type unsigned uint64
type UnsignedLimit struct {
Base int // Base for conversion as per strconv.ParseInt
Bits int // Number of bits as per strconv.ParseInt
Min uint64 // Minimum allowed value if both Min and Max are not 0
Max uint64 // Maximum allowed value if both Min and Max are not 0
}
var (
unsignedLimitsMu sync.Mutex
unsignedLimits = make(map[*unsigned]*UnsignedLimit)
)
func (n *unsigned) Set(value string, opt Option) error {
unsignedLimitsMu.Lock()
l := unsignedLimits[n]
unsignedLimitsMu.Unlock()
if l == nil {
return fmt.Errorf("no limits defined for %s", opt.Name())
}
v, err := strconv.ParseUint(value, l.Base, l.Bits)
if err != nil {
if e, ok := err.(*strconv.NumError); ok {
switch e.Err {
case strconv.ErrRange:
err = fmt.Errorf("value out of range: %s", value)
case strconv.ErrSyntax:
err = fmt.Errorf("not a valid number: %s", value)
}
}
return err
}
if l.Min != 0 || l.Max != 0 {
if v < l.Min {
return fmt.Errorf("value out of range (<%v): %s", l.Min, value)
}
if v > l.Max {
return fmt.Errorf("value out of range (>%v): %s", l.Max, value)
}
}
*n = unsigned(v)
return nil
}
func (n *unsigned) String() string {
unsignedLimitsMu.Lock()
l := unsignedLimits[n]
unsignedLimitsMu.Unlock()
if l != nil && l.Base != 0 {
return strconv.FormatUint(uint64(*n), l.Base)
}
return strconv.FormatUint(uint64(*n), 10)
}
// Unsigned creates an option that is stored in a uint64 and is
// constrained by the limits pointed to by l. The Max and Min values are only
// used if at least one of the values are not 0. If Base is 0, the base is
// implied by the string's prefix: base 16 for "0x", base 8 for "0", and base
// 10 otherwise.
func Unsigned(name rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 {
CommandLine.unsignedOption(&value, "", name, l, helpvalue...)
return &value
}
func (s *Set) Unsigned(name rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 {
s.unsignedOption(&value, "", name, l, helpvalue...)
return &value
}
func UnsignedLong(name string, short rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 {
CommandLine.unsignedOption(&value, name, short, l, helpvalue...)
return &value
}
func (s *Set) UnsignedLong(name string, short rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 {
s.unsignedOption(&value, name, short, l, helpvalue...)
return &value
}
func (s *Set) unsignedOption(p *uint64, name string, short rune, l *UnsignedLimit, helpvalue ...string) {
opt := s.FlagLong((*unsigned)(p), name, short, helpvalue...)
if l.Base > 36 || l.Base == 1 || l.Base < 0 {
fmt.Fprintf(stderr, "invalid base for %s: %d\n", opt.Name(), l.Base)
exit(1)
}
if l.Bits < 0 || l.Bits > 64 {
fmt.Fprintf(stderr, "invalid bit size for %s: %d\n", opt.Name(), l.Bits)
exit(1)
}
if l.Min > l.Max {
fmt.Fprintf(stderr, "min greater than max for %s\n", opt.Name())
exit(1)
}
lim := *l
unsignedLimitsMu.Lock()
unsignedLimits[(*unsigned)(p)] = &lim
unsignedLimitsMu.Unlock()
}

@ -0,0 +1,97 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"strings"
"testing"
)
var unsignedTests = []struct {
where string
in []string
l UnsignedLimit
out uint64
err string
}{
{
where: loc(),
},
{
loc(),
[]string{"test", "-n", "1010"},
UnsignedLimit{Base: 2, Bits: 5},
10,
"",
},
{
loc(),
[]string{"test", "-n", "1010"},
UnsignedLimit{Base: 2, Bits: 4},
10,
"",
},
{
loc(),
[]string{"test", "-n", "1010"},
UnsignedLimit{Base: 2, Bits: 3},
0,
"test: value out of range: 1010\n",
},
{
loc(),
[]string{"test", "-n", "3"},
UnsignedLimit{Min: 4, Max: 6},
0,
"test: value out of range (<4): 3\n",
},
{
loc(),
[]string{"test", "-n", "4"},
UnsignedLimit{Min: 4, Max: 6},
4,
"",
},
{
loc(),
[]string{"test", "-n", "5"},
UnsignedLimit{Min: 4, Max: 6},
5,
"",
},
{
loc(),
[]string{"test", "-n", "6"},
UnsignedLimit{Min: 4, Max: 6},
6,
"",
},
{
loc(),
[]string{"test", "-n", "7"},
UnsignedLimit{Min: 4, Max: 6},
0,
"test: value out of range (>6): 7\n",
},
}
func TestUnsigneds(t *testing.T) {
for x, tt := range unsignedTests {
if strings.Index(tt.where, ":-") > 0 {
tt.where = fmt.Sprintf("#%d", x)
}
reset()
n := Unsigned('n', 0, &tt.l)
parse(tt.in)
if s := checkError(tt.err); s != "" {
t.Errorf("%s: %s", tt.where, s)
}
if *n != tt.out {
t.Errorf("%s: got %v, want %v", tt.where, *n, tt.out)
}
}
}

@ -0,0 +1,85 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"bytes"
"fmt"
"path"
"reflect"
"runtime"
"strings"
)
var errorString string
func reset() {
CommandLine.shortOptions = make(map[rune]*option)
CommandLine.longOptions = make(map[string]*option)
CommandLine.options = nil
CommandLine.args = nil
CommandLine.program = ""
errorString = ""
}
func parse(args []string) {
err := CommandLine.Getopt(args, nil)
if err != nil {
b := &bytes.Buffer{}
fmt.Fprintln(b, CommandLine.program+": "+err.Error())
CommandLine.PrintUsage(b)
errorString = b.String()
}
}
func badSlice(a, b []string) bool {
if len(a) != len(b) {
return true
}
for x, v := range a {
if b[x] != v {
return true
}
}
return false
}
func loc() string {
_, file, line, _ := runtime.Caller(1)
return fmt.Sprintf("%s:%d", path.Base(file), line)
}
func (o *option) Equal(opt *option) bool {
if o.value != nil && opt.value == nil {
return false
}
if o.value == nil && opt.value != nil {
return false
}
if o.value != nil && o.value.String() != opt.value.String() {
return false
}
oc := *o
optc := *opt
oc.value = nil
optc.value = nil
return reflect.DeepEqual(&oc, &optc)
}
func checkError(err string) string {
switch {
case err == errorString:
return ""
case err == "":
return fmt.Sprintf("unexpected error %q", errorString)
case errorString == "":
return fmt.Sprintf("did not get expected error %q", err)
case !strings.HasPrefix(errorString, err):
return fmt.Sprintf("got error %q, want %q", errorString, err)
}
return ""
}

@ -0,0 +1,100 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"runtime"
"strings"
)
// Value is the interface to the dynamic value stored in a flag. Flags of type
// Value are declared using the Flag and FlagLong functions.
type Value interface {
// Set converts value into the appropriate type and assigns it to the
// receiver value. Option details are provided via opt (such as the
// flags name).
//
// Set is used to reset the value of an option to its default value
// (which is stored in string form internally).
Set(value string, opt Option) error
// String returns the value of the flag as a string.
String() string
}
var thisPackage string
// init initializes thisPackage to our full package with the trailing .
// included.
func init() {
pc, _, _, ok := runtime.Caller(0)
if !ok {
return
}
f := runtime.FuncForPC(pc)
if f == nil {
return
}
thisPackage = f.Name()
x := strings.LastIndex(thisPackage, "/")
if x < 0 {
return
}
y := strings.Index(thisPackage[x:], ".")
if y < 0 {
return
}
// thisPackage includes the trailing . after the package name.
thisPackage = thisPackage[:x+y+1]
}
// calledFrom returns a string containing the file and linenumber of the first
// stack frame above us that is not part of this package and is not a test.
// This is used to determine where a flag was initialized.
func calledFrom() string {
for i := 2; ; i++ {
pc, file, line, ok := runtime.Caller(i)
if !ok {
return ""
}
if !strings.HasSuffix(file, "_test.go") {
f := runtime.FuncForPC(pc)
if f != nil && strings.HasPrefix(f.Name(), thisPackage) {
continue
}
}
return fmt.Sprintf("%s:%d", file, line)
}
}
func (s *Set) addFlag(p Value, name string, short rune, helpvalue ...string) Option {
opt := &option{
short: short,
long: name,
value: p,
defval: p.String(),
}
switch len(helpvalue) {
case 2:
opt.name = helpvalue[1]
fallthrough
case 1:
opt.help = helpvalue[0]
case 0:
default:
panic("Too many strings for String helpvalue")
}
if where := calledFrom(); where != "" {
opt.where = where
}
if opt.short == 0 && opt.long == "" {
fmt.Fprintf(stderr, opt.where+": no short or long option given")
exit(1)
}
s.AddOption(opt)
return opt
}

@ -0,0 +1,63 @@
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package getopt
import (
"fmt"
"runtime"
)
// Value is the interface to the dynamic value stored in a flag. (The default
// value is represented as a string.) Set is passed the string to set the
// value to as well as the Option that is being processed.
type Value interface {
Set(string, Option) error
String() string
}
// Var creates an option of the specified name. The type and value of the option
// are represented by the first argument, of type Value, which typically holds a
// user-defined implementation of Value. All options are ultimately created
// as a Var.
func Var(p Value, name rune, helpvalue ...string) Option {
return CommandLine.VarLong(p, "", name, helpvalue...)
}
func VarLong(p Value, name string, short rune, helpvalue ...string) Option {
return CommandLine.VarLong(p, name, short, helpvalue...)
}
func (s *Set) Var(p Value, name rune, helpvalue ...string) Option {
return s.VarLong(p, "", name, helpvalue...)
}
func (s *Set) VarLong(p Value, name string, short rune, helpvalue ...string) Option {
opt := &option{
short: short,
long: name,
value: p,
defval: p.String(),
}
switch len(helpvalue) {
case 2:
opt.name = helpvalue[1]
fallthrough
case 1:
opt.help = helpvalue[0]
case 0:
default:
panic("Too many strings for String helpvalue")
}
if _, file, line, ok := runtime.Caller(1); ok {
opt.where = fmt.Sprintf("%s:%d", file, line)
}
if opt.short == 0 && opt.long == "" {
fmt.Fprintf(stderr, opt.where+": no short or long option given")
exit(1)
}
s.AddOption(opt)
return opt
}
Loading…
Cancel
Save