summaryrefslogtreecommitdiffstats
path: root/vendor/modernc.org/libc/etc.go
diff options
context:
space:
mode:
authorWim <wim@42.be>2022-01-31 00:27:37 +0100
committerWim <wim@42.be>2022-03-20 14:57:48 +0100
commite3cafeaf9292f67459ff1d186f68283bfaedf2ae (patch)
treeb69c39620aa91dba695b3b935c6651c0fb37ce75 /vendor/modernc.org/libc/etc.go
parente7b193788a56ee7cdb02a87a9db0ad6724ef66d5 (diff)
downloadmatterbridge-msglm-e3cafeaf9292f67459ff1d186f68283bfaedf2ae.tar.gz
matterbridge-msglm-e3cafeaf9292f67459ff1d186f68283bfaedf2ae.tar.bz2
matterbridge-msglm-e3cafeaf9292f67459ff1d186f68283bfaedf2ae.zip
Add dependencies/vendor (whatsapp)
Diffstat (limited to 'vendor/modernc.org/libc/etc.go')
-rw-r--r--vendor/modernc.org/libc/etc.go914
1 files changed, 914 insertions, 0 deletions
diff --git a/vendor/modernc.org/libc/etc.go b/vendor/modernc.org/libc/etc.go
new file mode 100644
index 00000000..6e687055
--- /dev/null
+++ b/vendor/modernc.org/libc/etc.go
@@ -0,0 +1,914 @@
+// Copyright 2020 The Libc Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package libc // import "modernc.org/libc"
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+ "runtime"
+ "runtime/debug"
+ "sort"
+ "strconv"
+ "strings"
+ "sync"
+ "sync/atomic"
+ "syscall"
+ "time"
+ "unsafe"
+
+ "modernc.org/libc/errno"
+ "modernc.org/libc/signal"
+ "modernc.org/libc/sys/types"
+)
+
+const (
+ allocatorPageOverhead = 4 * unsafe.Sizeof(int(0))
+ stackHeaderSize = unsafe.Sizeof(stackHeader{})
+ stackSegmentSize = 1<<12 - allocatorPageOverhead
+ uintptrSize = unsafe.Sizeof(uintptr(0))
+)
+
+var (
+ Covered = map[uintptr]struct{}{}
+ CoveredC = map[string]struct{}{}
+ fToken uintptr
+ tid int32
+
+ atExit []func()
+ atExitMu sync.Mutex
+
+ signals [signal.NSIG]uintptr
+ signalsMu sync.Mutex
+
+ objectMu sync.Mutex
+ objects = map[uintptr]interface{}{}
+
+ tlsBalance int32
+
+ _ = origin
+ _ = trc
+)
+
+func init() {
+ if n := stackHeaderSize; n%16 != 0 {
+ panic(fmt.Errorf("internal error: stackHeaderSize %v == %v (mod 16)", n, n%16))
+ }
+}
+
+func origin(skip int) string {
+ pc, fn, fl, _ := runtime.Caller(skip)
+ f := runtime.FuncForPC(pc)
+ var fns string
+ if f != nil {
+ fns = f.Name()
+ if x := strings.LastIndex(fns, "."); x > 0 {
+ fns = fns[x+1:]
+ }
+ }
+ return fmt.Sprintf("%s:%d:%s", filepath.Base(fn), fl, fns)
+}
+
+func trc(s string, args ...interface{}) string { //TODO-
+ switch {
+ case s == "":
+ s = fmt.Sprintf(strings.Repeat("%v ", len(args)), args...)
+ default:
+ s = fmt.Sprintf(s, args...)
+ }
+ r := fmt.Sprintf("%s: TRC %s", origin(2), s)
+ fmt.Fprintf(os.Stdout, "%s\n", r)
+ os.Stdout.Sync()
+ return r
+}
+
+func todo(s string, args ...interface{}) string { //TODO-
+ switch {
+ case s == "":
+ s = fmt.Sprintf(strings.Repeat("%v ", len(args)), args...)
+ default:
+ s = fmt.Sprintf(s, args...)
+ }
+ r := fmt.Sprintf("%s: TODOTODO %s", origin(2), s) //TODOOK
+ if dmesgs {
+ dmesg("%s", r)
+ }
+ fmt.Fprintf(os.Stdout, "%s\n", r)
+ fmt.Fprintf(os.Stdout, "%s\n", debug.Stack()) //TODO-
+ os.Stdout.Sync()
+ os.Exit(1)
+ panic("unrechable")
+}
+
+var coverPCs [1]uintptr //TODO not concurrent safe
+
+func Cover() {
+ runtime.Callers(2, coverPCs[:])
+ Covered[coverPCs[0]] = struct{}{}
+}
+
+func CoverReport(w io.Writer) error {
+ var a []string
+ pcs := make([]uintptr, 1)
+ for pc := range Covered {
+ pcs[0] = pc
+ frame, _ := runtime.CallersFrames(pcs).Next()
+ a = append(a, fmt.Sprintf("%s:%07d:%s", filepath.Base(frame.File), frame.Line, frame.Func.Name()))
+ }
+ sort.Strings(a)
+ _, err := fmt.Fprintf(w, "%s\n", strings.Join(a, "\n"))
+ return err
+}
+
+func CoverC(s string) {
+ CoveredC[s] = struct{}{}
+}
+
+func CoverCReport(w io.Writer) error {
+ var a []string
+ for k := range CoveredC {
+ a = append(a, k)
+ }
+ sort.Strings(a)
+ _, err := fmt.Fprintf(w, "%s\n", strings.Join(a, "\n"))
+ return err
+}
+
+func token() uintptr { return atomic.AddUintptr(&fToken, 1) }
+
+func addObject(o interface{}) uintptr {
+ t := token()
+ objectMu.Lock()
+ objects[t] = o
+ objectMu.Unlock()
+ return t
+}
+
+func getObject(t uintptr) interface{} {
+ objectMu.Lock()
+ o := objects[t]
+ if o == nil {
+ panic(todo("", t))
+ }
+
+ objectMu.Unlock()
+ return o
+}
+
+func removeObject(t uintptr) {
+ objectMu.Lock()
+ if _, ok := objects[t]; !ok {
+ panic(todo(""))
+ }
+
+ delete(objects, t)
+ objectMu.Unlock()
+}
+
+func (t *TLS) setErrno(err interface{}) {
+ if memgrind {
+ if atomic.SwapInt32(&t.reentryGuard, 1) != 0 {
+ panic(todo("concurrent use of TLS instance %p", t))
+ }
+
+ defer func() {
+ if atomic.SwapInt32(&t.reentryGuard, 0) != 1 {
+ panic(todo("concurrent use of TLS instance %p", t))
+ }
+ }()
+ }
+ // if dmesgs {
+ // dmesg("%v: %T(%v)\n%s", origin(1), err, err, debug.Stack())
+ // }
+again:
+ switch x := err.(type) {
+ case int:
+ *(*int32)(unsafe.Pointer(t.errnop)) = int32(x)
+ case int32:
+ *(*int32)(unsafe.Pointer(t.errnop)) = x
+ case *os.PathError:
+ err = x.Err
+ goto again
+ case syscall.Errno:
+ *(*int32)(unsafe.Pointer(t.errnop)) = int32(x)
+ case *os.SyscallError:
+ err = x.Err
+ goto again
+ default:
+ panic(todo("%T", x))
+ }
+}
+
+// Close frees the resources of t.
+func (t *TLS) Close() {
+ t.Free(int(unsafe.Sizeof(int32(0))))
+ if memgrind {
+ if t.stackHeaderBalance != 0 {
+ panic(todo("non zero stack header balance: %d", t.stackHeaderBalance))
+ }
+
+ atomic.AddInt32(&tlsBalance, -1)
+ }
+}
+
+// Alloc allocates n bytes of thread-local storage. It must be paired with a
+// call to t.Free(n), using the same n. The order matters. This is ok:
+//
+// t.Alloc(11)
+// t.Alloc(22)
+// t.Free(22)
+// t.Free(11)
+//
+// This is not correct:
+//
+// t.Alloc(11)
+// t.Alloc(22)
+// t.Free(11)
+// t.Free(22)
+func (t *TLS) Alloc(n int) (r uintptr) {
+ if memgrind {
+ if atomic.SwapInt32(&t.reentryGuard, 1) != 0 {
+ panic(todo("concurrent use of TLS instance %p", t))
+ }
+
+ defer func() {
+ if atomic.SwapInt32(&t.reentryGuard, 0) != 1 {
+ panic(todo("concurrent use of TLS instance %p", t))
+ }
+ }()
+ }
+ n += 15
+ n &^= 15
+ if t.stack.free >= n {
+ r = t.stack.sp
+ t.stack.free -= n
+ t.stack.sp += uintptr(n)
+ return r
+ }
+ //if we have a next stack
+ if nstack := t.stack.next; nstack != 0 {
+ if (*stackHeader)(unsafe.Pointer(nstack)).free >= n {
+ *(*stackHeader)(unsafe.Pointer(t.stack.page)) = t.stack
+ t.stack = *(*stackHeader)(unsafe.Pointer(nstack))
+ r = t.stack.sp
+ t.stack.free -= n
+ t.stack.sp += uintptr(n)
+ return r
+ }
+ nstack := *(*stackHeader)(unsafe.Pointer(t.stack.next))
+ for ; ; nstack = *(*stackHeader)(unsafe.Pointer(nstack.next)) {
+ if memgrind {
+ if atomic.AddInt32(&t.stackHeaderBalance, -1) < 0 {
+ panic(todo("negative stack header balance"))
+ }
+ }
+ Xfree(t, nstack.page)
+ if nstack.next == 0 {
+ break
+ }
+ }
+ t.stack.next = 0
+ }
+
+ if t.stack.page != 0 {
+ *(*stackHeader)(unsafe.Pointer(t.stack.page)) = t.stack
+ }
+
+ rq := n + int(stackHeaderSize)
+ if rq%int(stackSegmentSize) != 0 {
+ rq -= rq % int(stackSegmentSize)
+ rq += int(stackSegmentSize)
+ }
+ t.stack.free = rq - int(stackHeaderSize)
+ t.stack.prev = t.stack.page
+
+ rq += 15
+ rq &^= 15
+ t.stack.page = Xmalloc(t, types.Size_t(rq))
+ if t.stack.page == 0 {
+ panic("OOM")
+ }
+
+ if memgrind {
+ atomic.AddInt32(&t.stackHeaderBalance, 1)
+ }
+ t.stack.sp = t.stack.page + stackHeaderSize
+
+ r = t.stack.sp
+ t.stack.free -= n
+ t.stack.sp += uintptr(n)
+ if t.stack.prev != 0 {
+ (*stackHeader)(unsafe.Pointer(t.stack.prev)).next = t.stack.page
+ }
+
+ return r
+}
+
+//this declares how many stack frames are kept alive before being freed
+const stackFrameKeepalive = 2
+
+// Free deallocates n bytes of thread-local storage. See TLS.Alloc for details
+// on correct usage.
+func (t *TLS) Free(n int) {
+ if memgrind {
+ if atomic.SwapInt32(&t.reentryGuard, 1) != 0 {
+ panic(todo("concurrent use of TLS instance %p", t))
+ }
+
+ defer func() {
+ if atomic.SwapInt32(&t.reentryGuard, 0) != 1 {
+ panic(todo("concurrent use of TLS instance %p", t))
+ }
+ }()
+ }
+ n += 15
+ n &^= 15
+ t.stack.free += n
+ t.stack.sp -= uintptr(n)
+ if t.stack.sp != t.stack.page+stackHeaderSize {
+ return
+ }
+
+ nstack := t.stack
+
+ //if we are the first one, just free all of them
+ if t.stack.prev == 0 {
+ for ; ; nstack = *(*stackHeader)(unsafe.Pointer(nstack.next)) {
+ if memgrind {
+ if atomic.AddInt32(&t.stackHeaderBalance, -1) < 0 {
+ panic(todo("negative stack header balance"))
+ }
+ }
+ Xfree(t, nstack.page)
+ if nstack.next == 0 {
+ break
+ }
+ }
+ t.stack = stackHeader{}
+ return
+ }
+
+ //look if we are in the last n stackframes (n=stackFrameKeepalive)
+ //if we find something just return and set the current stack pointer to the previous one
+ for i := 0; i < stackFrameKeepalive; i++ {
+ if nstack.next == 0 {
+ *((*stackHeader)(unsafe.Pointer(t.stack.page))) = t.stack
+ t.stack = *(*stackHeader)(unsafe.Pointer(t.stack.prev))
+ return
+ }
+ nstack = *(*stackHeader)(unsafe.Pointer(nstack.next))
+ }
+
+ //else only free the last
+ if memgrind {
+ if atomic.AddInt32(&t.stackHeaderBalance, -1) < 0 {
+ panic(todo("negative stack header balance"))
+ }
+ }
+ Xfree(t, nstack.page)
+ (*stackHeader)(unsafe.Pointer(nstack.prev)).next = 0
+ *(*stackHeader)(unsafe.Pointer(t.stack.page)) = t.stack
+ t.stack = *(*stackHeader)(unsafe.Pointer(t.stack.prev))
+}
+
+type stackHeader struct {
+ free int // bytes left in page
+ page uintptr // stack page
+ prev uintptr // prev stack page = prev stack header
+ next uintptr // next stack page = next stack header
+ sp uintptr // next allocation address
+ _ stackHeaderPadding
+}
+
+func cString(t *TLS, s string) uintptr { //TODO-
+ n := len(s)
+ p := Xmalloc(t, types.Size_t(n)+1)
+ if p == 0 {
+ panic("OOM")
+ }
+
+ copy((*RawMem)(unsafe.Pointer(p))[:n:n], s)
+ *(*byte)(unsafe.Pointer(p + uintptr(n))) = 0
+ return p
+}
+
+// VaList fills a varargs list at p with args and returns p. The list must
+// have been allocated by caller and it must not be in Go managed memory, ie.
+// it must be pinned. Caller is responsible for freeing the list.
+//
+// Individual arguments must be one of int, uint, int32, uint32, int64, uint64,
+// float64, uintptr or Intptr. Other types will panic.
+//
+// This function supports code generated by ccgo/v3. For manually constructed
+// var args it's recommended to use the NewVaList function instead.
+//
+// Note: The C translated to Go varargs ABI alignment for all types is 8 on all
+// architectures.
+func VaList(p uintptr, args ...interface{}) (r uintptr) {
+ if p&7 != 0 {
+ panic("internal error")
+ }
+
+ r = p
+ for _, v := range args {
+ switch x := v.(type) {
+ case int:
+ *(*int64)(unsafe.Pointer(p)) = int64(x)
+ case int32:
+ *(*int64)(unsafe.Pointer(p)) = int64(x)
+ case int64:
+ *(*int64)(unsafe.Pointer(p)) = x
+ case uint:
+ *(*uint64)(unsafe.Pointer(p)) = uint64(x)
+ case uint16:
+ *(*uint64)(unsafe.Pointer(p)) = uint64(x)
+ case uint32:
+ *(*uint64)(unsafe.Pointer(p)) = uint64(x)
+ case uint64:
+ *(*uint64)(unsafe.Pointer(p)) = x
+ case float64:
+ *(*float64)(unsafe.Pointer(p)) = x
+ case uintptr:
+ *(*uintptr)(unsafe.Pointer(p)) = x
+ default:
+ panic(todo("invalid VaList argument type: %T", x))
+ }
+ p += 8
+ }
+ return r
+}
+
+// NewVaListN returns a newly allocated va_list for n items. The caller of
+// NewVaListN is responsible for freeing the va_list.
+func NewVaListN(n int) (va_list uintptr) {
+ return Xmalloc(nil, types.Size_t(8*n))
+}
+
+// NewVaList is like VaList but automatically allocates the correct amount of
+// memory for all of the items in args.
+//
+// The va_list return value is used to pass the constructed var args to var
+// args accepting functions. The caller of NewVaList is responsible for freeing
+// the va_list.
+func NewVaList(args ...interface{}) (va_list uintptr) {
+ return VaList(NewVaListN(len(args)), args...)
+}
+
+func VaInt32(app *uintptr) int32 {
+ ap := *(*uintptr)(unsafe.Pointer(app))
+ if ap == 0 {
+ return 0
+ }
+
+ ap = roundup(ap, 8)
+ v := int32(*(*int64)(unsafe.Pointer(ap)))
+ ap += 8
+ *(*uintptr)(unsafe.Pointer(app)) = ap
+ return v
+}
+
+func VaUint32(app *uintptr) uint32 {
+ ap := *(*uintptr)(unsafe.Pointer(app))
+ if ap == 0 {
+ return 0
+ }
+
+ ap = roundup(ap, 8)
+ v := uint32(*(*uint64)(unsafe.Pointer(ap)))
+ ap += 8
+ *(*uintptr)(unsafe.Pointer(app)) = ap
+ return v
+}
+
+func VaInt64(app *uintptr) int64 {
+ ap := *(*uintptr)(unsafe.Pointer(app))
+ if ap == 0 {
+ return 0
+ }
+
+ ap = roundup(ap, 8)
+ v := *(*int64)(unsafe.Pointer(ap))
+ ap += 8
+ *(*uintptr)(unsafe.Pointer(app)) = ap
+ return v
+}
+
+func VaUint64(app *uintptr) uint64 {
+ ap := *(*uintptr)(unsafe.Pointer(app))
+ if ap == 0 {
+ return 0
+ }
+
+ ap = roundup(ap, 8)
+ v := *(*uint64)(unsafe.Pointer(ap))
+ ap += 8
+ *(*uintptr)(unsafe.Pointer(app)) = ap
+ return v
+}
+
+func VaFloat32(app *uintptr) float32 {
+ ap := *(*uintptr)(unsafe.Pointer(app))
+ if ap == 0 {
+ return 0
+ }
+
+ ap = roundup(ap, 8)
+ v := *(*float64)(unsafe.Pointer(ap))
+ ap += 8
+ *(*uintptr)(unsafe.Pointer(app)) = ap
+ return float32(v)
+}
+
+func VaFloat64(app *uintptr) float64 {
+ ap := *(*uintptr)(unsafe.Pointer(app))
+ if ap == 0 {
+ return 0
+ }
+
+ ap = roundup(ap, 8)
+ v := *(*float64)(unsafe.Pointer(ap))
+ ap += 8
+ *(*uintptr)(unsafe.Pointer(app)) = ap
+ return v
+}
+
+func VaUintptr(app *uintptr) uintptr {
+ ap := *(*uintptr)(unsafe.Pointer(app))
+ if ap == 0 {
+ return 0
+ }
+
+ ap = roundup(ap, 8)
+ v := *(*uintptr)(unsafe.Pointer(ap))
+ ap += 8
+ *(*uintptr)(unsafe.Pointer(app)) = ap
+ return v
+}
+
+func roundup(n, to uintptr) uintptr {
+ if r := n % to; r != 0 {
+ return n + to - r
+ }
+
+ return n
+}
+
+func GoString(s uintptr) string {
+ if s == 0 {
+ return ""
+ }
+
+ var buf []byte
+ for {
+ b := *(*byte)(unsafe.Pointer(s))
+ if b == 0 {
+ return string(buf)
+ }
+
+ buf = append(buf, b)
+ s++
+ }
+}
+
+// GoBytes returns a byte slice from a C char* having length len bytes.
+func GoBytes(s uintptr, len int) []byte {
+ if len == 0 {
+ return nil
+ }
+
+ return (*RawMem)(unsafe.Pointer(s))[:len:len]
+}
+
+func Bool32(b bool) int32 {
+ if b {
+ return 1
+ }
+
+ return 0
+}
+
+func Bool64(b bool) int64 {
+ if b {
+ return 1
+ }
+
+ return 0
+}
+
+type sorter struct {
+ len int
+ base uintptr
+ sz uintptr
+ f func(*TLS, uintptr, uintptr) int32
+ t *TLS
+}
+
+func (s *sorter) Len() int { return s.len }
+
+func (s *sorter) Less(i, j int) bool {
+ return s.f(s.t, s.base+uintptr(i)*s.sz, s.base+uintptr(j)*s.sz) < 0
+}
+
+func (s *sorter) Swap(i, j int) {
+ p := uintptr(s.base + uintptr(i)*s.sz)
+ q := uintptr(s.base + uintptr(j)*s.sz)
+ for i := 0; i < int(s.sz); i++ {
+ *(*byte)(unsafe.Pointer(p)), *(*byte)(unsafe.Pointer(q)) = *(*byte)(unsafe.Pointer(q)), *(*byte)(unsafe.Pointer(p))
+ p++
+ q++
+ }
+}
+
+func CString(s string) (uintptr, error) {
+ n := len(s)
+ p := Xmalloc(nil, types.Size_t(n)+1)
+ if p == 0 {
+ return 0, fmt.Errorf("CString: cannot allocate %d bytes", n+1)
+ }
+
+ copy((*RawMem)(unsafe.Pointer(p))[:n:n], s)
+ *(*byte)(unsafe.Pointer(p + uintptr(n))) = 0
+ return p, nil
+}
+
+func GetEnviron() (r []string) {
+ for p := Environ(); ; p += unsafe.Sizeof(p) {
+ q := *(*uintptr)(unsafe.Pointer(p))
+ if q == 0 {
+ return r
+ }
+
+ r = append(r, GoString(q))
+ }
+}
+
+func strToUint64(t *TLS, s uintptr, base int32) (seenDigits, neg bool, next uintptr, n uint64, err int32) {
+ var c byte
+out:
+ for {
+ c = *(*byte)(unsafe.Pointer(s))
+ switch c {
+ case ' ', '\t', '\n', '\r', '\v', '\f':
+ s++
+ case '+':
+ s++
+ break out
+ case '-':
+ s++
+ neg = true
+ break out
+ default:
+ break out
+ }
+ }
+ for {
+ c = *(*byte)(unsafe.Pointer(s))
+ var digit uint64
+ switch base {
+ case 10:
+ switch {
+ case c >= '0' && c <= '9':
+ seenDigits = true
+ digit = uint64(c) - '0'
+ default:
+ return seenDigits, neg, s, n, 0
+ }
+ case 16:
+ if c >= 'A' && c <= 'F' {
+ c = c + ('a' - 'A')
+ }
+ switch {
+ case c >= '0' && c <= '9':
+ seenDigits = true
+ digit = uint64(c) - '0'
+ case c >= 'a' && c <= 'f':
+ seenDigits = true
+ digit = uint64(c) - 'a' + 10
+ default:
+ return seenDigits, neg, s, n, 0
+ }
+ default:
+ panic(todo("", base))
+ }
+ n0 := n
+ n = uint64(base)*n + digit
+ if n < n0 { // overflow
+ return seenDigits, neg, s, n0, errno.ERANGE
+ }
+
+ s++
+ }
+}
+
+func strToFloatt64(t *TLS, s uintptr, bits int) (n float64, errno int32) {
+ var b []byte
+ var neg bool
+
+ defer func() {
+ var err error
+ if n, err = strconv.ParseFloat(string(b), bits); err != nil {
+ panic(todo(""))
+ }
+
+ if neg {
+ n = -n
+ }
+ }()
+
+ var c byte
+out:
+ for {
+ c = *(*byte)(unsafe.Pointer(s))
+ switch c {
+ case ' ', '\t', '\n', '\r', '\v', '\f':
+ s++
+ case '+':
+ s++
+ break out
+ case '-':
+ s++
+ neg = true
+ break out
+ default:
+ break out
+ }
+ }
+ for {
+ c = *(*byte)(unsafe.Pointer(s))
+ switch {
+ case c >= '0' && c <= '9':
+ b = append(b, c)
+ case c == '.':
+ b = append(b, c)
+ s++
+ for {
+ c = *(*byte)(unsafe.Pointer(s))
+ switch {
+ case c >= '0' && c <= '9':
+ b = append(b, c)
+ case c == 'e' || c == 'E':
+ b = append(b, c)
+ s++
+ for {
+ c = *(*byte)(unsafe.Pointer(s))
+ switch {
+ case c == '+' || c == '-':
+ b = append(b, c)
+ s++
+ for {
+ c = *(*byte)(unsafe.Pointer(s))
+ switch {
+ case c >= '0' && c <= '9':
+ b = append(b, c)
+ default:
+ return
+ }
+
+ s++
+ }
+ default:
+ panic(todo("%q %q", b, string(c)))
+ }
+ }
+ default:
+ return
+ }
+
+ s++
+ }
+ default:
+ panic(todo("%q %q", b, string(c)))
+ }
+
+ s++
+ }
+}
+
+func parseZone(s string) (name string, off int) {
+ _, name, off, _ = parseZoneOffset(s, false)
+ return name, off
+}
+
+func parseZoneOffset(s string, offOpt bool) (string, string, int, bool) {
+ s0 := s
+ name := s
+ for len(s) != 0 {
+ switch c := s[0]; {
+ case c >= 'A' && c <= 'Z', c >= 'a' && c <= 'z', c == '_', c == '/':
+ s = s[1:]
+ default:
+ name = name[:len(name)-len(s)]
+ if len(name) < 3 {
+ panic(todo("%q", s0))
+ }
+
+ if offOpt {
+ if len(s) == 0 {
+ return "", name, 0, false
+ }
+
+ if c := s[0]; (c < '0' || c > '9') && c != '+' && c != '-' {
+ return s, name, 0, false
+ }
+ }
+
+ s, off := parseOffset(s)
+ return s, name, off, true
+ }
+ }
+ return "", s0, 0, true
+}
+
+// [+|-]hh[:mm[:ss]]
+func parseOffset(s string) (string, int) {
+ if len(s) == 0 {
+ panic(todo(""))
+ }
+
+ k := 1
+ switch s[0] {
+ case '+':
+ // nop
+ s = s[1:]
+ case '-':
+ k = -1
+ s = s[1:]
+ }
+ s, hh, ok := parseUint(s)
+ if !ok {
+ panic(todo(""))
+ }
+
+ n := hh * 3600
+ if len(s) == 0 || s[0] != ':' {
+ return s, k * n
+ }
+
+ s = s[1:] // ':'
+ if len(s) == 0 {
+ panic(todo(""))
+ }
+
+ s, mm, ok := parseUint(s)
+ if !ok {
+ panic(todo(""))
+ }
+
+ n += mm * 60
+ if len(s) == 0 || s[0] != ':' {
+ return s, k * n
+ }
+
+ s = s[1:] // ':'
+ if len(s) == 0 {
+ panic(todo(""))
+ }
+
+ s, ss, _ := parseUint(s)
+ return s, k * (n + ss)
+}
+
+func parseUint(s string) (string, int, bool) {
+ var ok bool
+ var r int
+ for len(s) != 0 {
+ switch c := s[0]; {
+ case c >= '0' && c <= '9':
+ ok = true
+ r0 := r
+ r = 10*r + int(c) - '0'
+ if r < r0 {
+ panic(todo(""))
+ }
+
+ s = s[1:]
+ default:
+ return s, r, ok
+ }
+ }
+ return s, r, ok
+}
+
+// https://stackoverflow.com/a/53052382
+//
+// isTimeDST returns true if time t occurs within daylight saving time
+// for its time zone.
+func isTimeDST(t time.Time) bool {
+ // If the most recent (within the last year) clock change
+ // was forward then assume the change was from std to dst.
+ hh, mm, _ := t.UTC().Clock()
+ tClock := hh*60 + mm
+ for m := -1; m > -12; m-- {
+ // assume dst lasts for at least one month
+ hh, mm, _ := t.AddDate(0, m, 0).UTC().Clock()
+ clock := hh*60 + mm
+ if clock != tClock {
+ return clock > tClock
+ }
+ }
+ // assume no dst
+ return false
+}