1// Copyright 2013 Google Inc.  All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package getopt
6
7import (
8	"fmt"
9	"strconv"
10)
11
12type unsigned uint64
13
14type UnsignedLimit struct {
15	Base int    // Base for conversion as per strconv.ParseInt
16	Bits int    // Number of bits as per strconv.ParseInt
17	Min  uint64 // Minimum allowed value if both Min and Max are not 0
18	Max  uint64 // Maximum allowed value if both Min and Max are not 0
19}
20
21var unsignedLimits = make(map[*unsigned]*UnsignedLimit)
22
23func (n *unsigned) Set(value string, opt Option) error {
24	l := unsignedLimits[n]
25	if l == nil {
26		return fmt.Errorf("no limits defined for %s", opt.Name())
27	}
28	v, err := strconv.ParseUint(value, l.Base, l.Bits)
29	if err != nil {
30		if e, ok := err.(*strconv.NumError); ok {
31			switch e.Err {
32			case strconv.ErrRange:
33				err = fmt.Errorf("value out of range: %s", value)
34			case strconv.ErrSyntax:
35				err = fmt.Errorf("not a valid number: %s", value)
36			}
37		}
38		return err
39	}
40	if l.Min != 0 || l.Max != 0 {
41		if v < l.Min {
42			return fmt.Errorf("value out of range (<%v): %s", l.Min, value)
43		}
44		if v > l.Max {
45			return fmt.Errorf("value out of range (>%v): %s", l.Max, value)
46		}
47	}
48	*n = unsigned(v)
49	return nil
50}
51
52func (n *unsigned) String() string {
53	l := unsignedLimits[n]
54	if l != nil && l.Base != 0 {
55		return strconv.FormatUint(uint64(*n), l.Base)
56	}
57	return strconv.FormatUint(uint64(*n), 10)
58}
59
60// Unsigned creates an option that is stored in a uint64 and is
61// constrained by the limits pointed to by l.  The Max and Min values are only
62// used if at least one of the values are not 0.   If Base is 0, the base is
63// implied by the string's prefix: base 16 for "0x", base 8 for "0", and base
64// 10 otherwise.
65func Unsigned(name rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 {
66	return CommandLine.Unsigned(name, value, l, helpvalue...)
67}
68
69func (s *Set) Unsigned(name rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 {
70	return s.UnsignedLong("", name, value, l, helpvalue...)
71}
72
73func UnsignedLong(name string, short rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 {
74	return CommandLine.UnsignedLong(name, short, value, l, helpvalue...)
75}
76
77func (s *Set) UnsignedLong(name string, short rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 {
78	s.UnsignedVarLong(&value, name, short, l, helpvalue...)
79	return &value
80}
81
82func UnsignedVar(p *uint64, name rune, l *UnsignedLimit, helpvalue ...string) Option {
83	return CommandLine.UnsignedVar(p, name, l, helpvalue...)
84}
85
86func (s *Set) UnsignedVar(p *uint64, name rune, l *UnsignedLimit, helpvalue ...string) Option {
87	return s.UnsignedVarLong(p, "", name, l, helpvalue...)
88}
89
90func UnsignedVarLong(p *uint64, name string, short rune, l *UnsignedLimit, helpvalue ...string) Option {
91	return CommandLine.UnsignedVarLong(p, name, short, l, helpvalue...)
92}
93
94func (s *Set) UnsignedVarLong(p *uint64, name string, short rune, l *UnsignedLimit, helpvalue ...string) Option {
95	opt := s.VarLong((*unsigned)(p), name, short, helpvalue...)
96	if l.Base > 36 || l.Base == 1 || l.Base < 0 {
97		fmt.Fprintf(stderr, "invalid base for %s: %d\n", opt.Name(), l.Base)
98		exit(1)
99	}
100	if l.Bits < 0 || l.Bits > 64 {
101		fmt.Fprintf(stderr, "invalid bit size for %s: %d\n", opt.Name(), l.Bits)
102		exit(1)
103	}
104	if l.Min > l.Max {
105		fmt.Fprintf(stderr, "min greater than max for %s\n", opt.Name())
106		exit(1)
107	}
108	lim := *l
109	unsignedLimits[(*unsigned)(p)] = &lim
110	return opt
111}
112