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

package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
)

type grepResult struct {
	match bool
	c     *grepCommand
	line  *string
}

type grepCommand struct {
	name string
	*os.File
}

type oneGrep struct {
	c chan *grepResult
}

var (
	Match       = flag.Bool("v", true, "Print only non-matching lines")
	recursive   = flag.Bool("r", false, "recursive")
	noshowmatch = flag.Bool("l", false, "list only files")
	showname    = false
	allGrep     = make(chan *oneGrep)
	nGrep		= 0
)

func grep(f *grepCommand, re *regexp.Regexp) {
	nGrep++
	r := bufio.NewReader(f)
	res := make(chan *grepResult, 1)
	allGrep <- &oneGrep{res}
	for {
		if i, err := r.ReadString('\n'); err == nil {
			m := re.Match([]byte(i))
			if m == *Match {
				res <- &grepResult{re.Match([]byte(i)), f, &i}
				if (*noshowmatch) {
					break
				}
			}
		} else {
			break
		}
	}
	close(res)
	f.Close()
}

func printmatch(r *grepResult) {
	if showname {
		fmt.Printf("%v", r.c.name)
	}
	if *noshowmatch {
		return
	} else if showname {
		fmt.Printf(":")
	}
	if r.match == *Match {
		fmt.Printf("%v", *r.line)
	}
}

func main() {
	r := ".*"
	flag.Parse()
	a := flag.Args()
	if len(a) > 0 {
		r = a[0]
	}
	re := regexp.MustCompile(r)
	// very special case, just stdin ...
	if len(a) < 2 {
		go grep(&grepCommand{"<stdin>", os.Stdin}, re)
	} else {
		showname = len(a[1:]) > 1
		// generate a chan of file names, bounded by the size of the chan. This in turn
		// throttles the opens.
		treenames := make(chan string, 128)
		go func() {
			for _, v := range a[1:] {
				// we could parallelize the open part but people might want
				// things to be in order. I don't care but who knows.
				// just ignore the errors. If there is not a single one that works,
				// then all the sizes will be 0 and we'll just fall through.
				filepath.Walk(v, func(name string, fi os.FileInfo, err error) error {
					if fi.IsDir() && !*recursive {
						fmt.Printf("grep: %v: Is a directory\n", name)
						return filepath.SkipDir
					}
					if err != nil {
						fmt.Printf("%v: %v\n", name, err)
						return err
					}
					treenames <- name
					return nil
				})
			}
			close(treenames)
		}()

		files := make(chan *grepCommand)
		// convert the file names to a stream of os.File
		go func() {
			for i := range treenames {
				fp, err := os.Open(i)
				if err != nil {
					fmt.Fprintf(os.Stderr, "can't open %s: %v\n", i, err)
					continue
				}
				files <- &grepCommand{i, fp}
			}
			close(files)
		}()
		// now kick off the greps

		for f := range files {
			go grep(f, re)
		}
	}

	for c := range allGrep {
		for r := range c.c {
			printmatch(r)
		}
		nGrep--
		if nGrep == 0 {
			break
		}
	}
}
<commit_msg>cleanup<commit_after>// Copyright 2012 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
)

type grepResult struct {
	match bool
	c     *grepCommand
	line  *string
}

type grepCommand struct {
	name string
	*os.File
}

type oneGrep struct {
	c chan *grepResult
}

var (
	Match       = flag.Bool("v", true, "Print only non-matching lines")
	recursive   = flag.Bool("r", false, "recursive")
	noshowmatch = flag.Bool("l", false, "list only files")
	showname    = false
	allGrep     = make(chan *oneGrep)
	nGrep       = 0
)

func grep(f *grepCommand, re *regexp.Regexp) {
	nGrep++
	r := bufio.NewReader(f)
	res := make(chan *grepResult, 1)
	allGrep <- &oneGrep{res}
	for {
		if i, err := r.ReadString('\n'); err == nil {
			m := re.Match([]byte(i))
			if m == *Match {
				res <- &grepResult{re.Match([]byte(i)), f, &i}
				if *noshowmatch {
					break
				}
			}
		} else {
			break
		}
	}
	close(res)
	f.Close()
}

func printmatch(r *grepResult) {
	if showname {
		fmt.Printf("%v", r.c.name)
	}
	if *noshowmatch {
		return
	} else if showname {
		fmt.Printf(":")
	}
	if r.match == *Match {
		fmt.Printf("%v", *r.line)
	}
}

func main() {
	r := ".*"
	flag.Parse()
	a := flag.Args()
	if len(a) > 0 {
		r = a[0]
	}
	re := regexp.MustCompile(r)
	// very special case, just stdin ...
	if len(a) < 2 {
		go grep(&grepCommand{"<stdin>", os.Stdin}, re)
	} else {
		showname = len(a[1:]) > 1
		// generate a chan of file names, bounded by the size of the chan. This in turn
		// throttles the opens.
		treenames := make(chan string, 128)
		go func() {
			for _, v := range a[1:] {
				// we could parallelize the open part but people might want
				// things to be in order. I don't care but who knows.
				// just ignore the errors. If there is not a single one that works,
				// then all the sizes will be 0 and we'll just fall through.
				filepath.Walk(v, func(name string, fi os.FileInfo, err error) error {
					if fi.IsDir() && !*recursive {
						fmt.Fprintf(os.Stderr, "grep: %v: Is a directory\n", name)
						return filepath.SkipDir
					}
					if err != nil {
						fmt.Fprintf(os.Stderr, "%v: %v\n", name, err)
						return err
					}
					treenames <- name
					return nil
				})
			}
			close(treenames)
		}()

		files := make(chan *grepCommand)
		// convert the file names to a stream of os.File
		go func() {
			for i := range treenames {
				fp, err := os.Open(i)
				if err != nil {
					fmt.Fprintf(os.Stderr, "can't open %s: %v\n", i, err)
					continue
				}
				files <- &grepCommand{i, fp}
			}
			close(files)
		}()
		// now kick off the greps

		for f := range files {
			go grep(f, re)
		}
	}

	for c := range allGrep {
		for r := range c.c {
			printmatch(r)
		}
		nGrep--
		if nGrep == 0 {
			break
		}
	}
}
<|endoftext|>


<commit_before>package fly

import "github.com/EngineerBetter/concourse-up/config"

// Pipeline is interface for self update pipeline
type Pipeline interface {
	BuildPipelineParams(config config.Config) (Pipeline, error)
	GetConfigTemplate() string
	Indent(countStr, field string) string
}

const selfUpdateResources = `
resources:
- name: concourse-up-release
  type: github-release
  source:
    user: engineerbetter
    repository: concourse-up
    pre_release: true
- name: every-month
  type: time
  source: {interval: 24h}
`

const renewCertsDateCheck = `
          now_seconds=$(date +%s)
          expires_on=$(concourse-up-linux-amd64 info $DEPLOYMENT --cert-expiry)
          expires_on_seconds=$(date --date="$expires_on" +%s)
          let "seconds_until_expiry = $expires_on_seconds - $now_seconds"
          let "days_until_expiry = $seconds_until_expiry / 60 / 60 / 24"
          if [ $days_until_expiry -gt 31 ]; then
	          echo Not renewing certs, as they do not expire in the next month.
	          exit 0
          fi
`
<commit_msg>BRING BACK XML<commit_after>package fly

import "github.com/EngineerBetter/concourse-up/config"

// Pipeline is interface for self update pipeline
type Pipeline interface {
	BuildPipelineParams(config config.Config) (Pipeline, error)
	GetConfigTemplate() string
	Indent(countStr, field string) string
}

const selfUpdateResources = `
resources:
- name: concourse-up-release
  type: github-release
  source:
    user: engineerbetter
    repository: concourse-up
    pre_release: true
- name: every-month
  type: time
  source: {interval: 24h}
`

const renewCertsDateCheck = `
          now_seconds=$(date +%s)
          expires_on=$(concourse-up-linux-amd64 info $DEPLOYMENT --cert-expiry)
          expires_on_seconds=$(date --date="$expires_on" +%s)
          let "seconds_until_expiry = $expires_on_seconds - $now_seconds"
          let "days_until_expiry = $seconds_until_expiry / 60 / 60 / 24"
          if [ $days_until_expiry -gt 31 ]; then
            echo Not renewing certs, as they do not expire in the next month.
            exit 0
          fi
`
<|endoftext|>


<commit_before>// Copyright 2017 Jeff Foley. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package format

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/OWASP/Amass/v3/net"
	"github.com/OWASP/Amass/v3/requests"
	"github.com/fatih/color"
)

// Banner is the ASCII art logo used within help output.
const Banner = `
        .+++:.            :                             .+++.
      +W@@@@@@8        &+W@#               o8W8:      +W@@@@@@#.   oW@@@W#+
     &@#+   .o@##.    .@@@o@W.o@@o       :@@#&W8o    .@#:  .:oW+  .@#+++&#&
    +@&        &@&     #@8 +@W@&8@+     :@W.   +@8   +@:          .@8
    8@          @@     8@o  8@8  WW    .@W      W@+  .@W.          o@#:
    WW          &@o    &@:  o@+  o@+   #@.      8@o   +W@#+.        +W@8:
    #@          :@W    &@+  &@+   @8  :@o       o@o     oW@@W+        oW@8
    o@+          @@&   &@+  &@+   #@  &@.      .W@W       .+#@&         o@W.
     WW         +@W@8. &@+  :&    o@+ #@      :@W&@&         &@:  ..     :@o
     :@W:      o@# +Wo &@+        :W: +@W&o++o@W. &@&  8@#o+&@W.  #@:    o@+
      :W@@WWWW@@8       +              :&W@@@@&    &W  .o#@@W&.   :W@WWW@@&
        +o&&&&+.                                                    +oooo.
`

const (
	// Version is used to display the current version of Amass.
	Version = "v3.2.3"

	// Author is used to display the Amass Project Team.
	Author = "OWASP Amass Project - @owaspamass"

	// Description is the slogan for the Amass Project.
	Description = "In-depth Attack Surface Mapping and Asset Discovery"
)

var (
	// Colors used to ease the reading of program output
	y      = color.New(color.FgHiYellow)
	g      = color.New(color.FgHiGreen)
	r      = color.New(color.FgHiRed)
	b      = color.New(color.FgHiBlue)
	fgR    = color.New(color.FgRed)
	fgY    = color.New(color.FgYellow)
	yellow = color.New(color.FgHiYellow).SprintFunc()
	green  = color.New(color.FgHiGreen).SprintFunc()
	blue   = color.New(color.FgHiBlue).SprintFunc()
)

// ASNSummaryData stores information related to discovered ASs and netblocks.
type ASNSummaryData struct {
	Name      string
	Netblocks map[string]int
}

// UpdateSummaryData updates the summary maps using the provided requests.Output data.
func UpdateSummaryData(output *requests.Output, tags map[string]int, asns map[int]*ASNSummaryData) {
	tags[output.Tag]++

	for _, addr := range output.Addresses {
		data, found := asns[addr.ASN]
		if !found {
			asns[addr.ASN] = &ASNSummaryData{
				Name:      addr.Description,
				Netblocks: make(map[string]int),
			}
			data = asns[addr.ASN]
		}
		// Increment how many IPs were in this netblock
		data.Netblocks[addr.Netblock.String()]++
	}
}

// PrintEnumerationSummary outputs the summary information utilized by the command-line tools.
func PrintEnumerationSummary(total int, tags map[string]int, asns map[int]*ASNSummaryData, demo bool) {
	pad := func(num int, chr string) {
		for i := 0; i < num; i++ {
			b.Fprint(color.Error, chr)
		}
	}

	fmt.Fprintln(color.Error)
	// Print the header information
	title := "OWASP Amass "
	site := "https://github.com/OWASP/Amass"
	b.Fprint(color.Error, title+Version)
	num := 80 - (len(title) + len(Version) + len(site))
	pad(num, " ")
	b.Fprintf(color.Error, "%s\n", site)
	pad(8, "----------")
	fmt.Fprintf(color.Error, "\n%s%s", yellow(strconv.Itoa(total)), green(" names discovered - "))
	// Print the stats using tag information
	num, length := 1, len(tags)
	for k, v := range tags {
		fmt.Fprintf(color.Error, "%s: %s", green(k), yellow(strconv.Itoa(v)))
		if num < length {
			g.Fprint(color.Error, ", ")
		}
		num++
	}
	fmt.Fprintln(color.Error)

	if len(asns) == 0 {
		return
	}
	// Another line gets printed
	pad(8, "----------")
	fmt.Fprintln(color.Error)
	// Print the ASN and netblock information
	for asn, data := range asns {
		asnstr := strconv.Itoa(asn)
		datastr := data.Name

		if demo && asn > 0 {
			asnstr = censorString(asnstr, 0, len(asnstr))
			datastr = censorString(datastr, 0, len(datastr))
		}

		fmt.Fprintf(color.Error, "%s%s %s %s\n",
			blue("ASN: "), yellow(asnstr), green("-"), green(datastr))

		for cidr, ips := range data.Netblocks {
			countstr := strconv.Itoa(ips)
			cidrstr := cidr

			if demo {
				cidrstr = censorNetBlock(cidrstr)
			}

			countstr = fmt.Sprintf("\t%-4s", countstr)
			cidrstr = fmt.Sprintf("\t%-18s", cidrstr)

			fmt.Fprintf(color.Error, "%s%s %s\n",
				yellow(cidrstr), yellow(countstr), blue("Subdomain Name(s)"))
		}
	}
}

// PrintBanner outputs the Amass banner the same for all tools.
func PrintBanner() {
	y := color.New(color.FgHiYellow)
	r := color.New(color.FgHiRed)
	rightmost := 76

	pad := func(num int) {
		for i := 0; i < num; i++ {
			fmt.Fprint(color.Error, " ")
		}
	}
	r.Fprintln(color.Error, Banner)
	pad(rightmost - len(Version))
	y.Fprintln(color.Error, Version)
	pad(rightmost - len(Author))
	y.Fprintln(color.Error, Author)
	pad(rightmost - len(Description))
	y.Fprintf(color.Error, "%s\n\n\n", Description)
}

func censorDomain(input string) string {
	return censorString(input, strings.Index(input, "."), len(input))
}

func censorIP(input string) string {
	return censorString(input, 0, strings.LastIndex(input, "."))
}

func censorNetBlock(input string) string {
	return censorString(input, 0, strings.Index(input, "/"))
}

func censorString(input string, start, end int) string {
	runes := []rune(input)
	for i := start; i < end; i++ {
		if runes[i] == '.' ||
			runes[i] == '/' ||
			runes[i] == '-' ||
			runes[i] == ' ' {
			continue
		}
		runes[i] = 'x'
	}
	return string(runes)
}

// OutputLineParts returns the parts of a line to be printed for a requests.Output.
func OutputLineParts(out *requests.Output, src, addrs, demo bool) (source, name, ips string) {
	if src {
		source = fmt.Sprintf("%-18s", "["+out.Source+"] ")
	}
	if addrs {
		for i, a := range out.Addresses {
			if i != 0 {
				ips += ","
			}
			if demo {
				ips += censorIP(a.Address.String())
			} else {
				ips += a.Address.String()
			}
		}
		if ips == "" {
			ips = "N/A"
		}
	}
	name = out.Name
	if demo {
		name = censorDomain(name)
	}
	return
}

// DesiredAddrTypes removes undesired address types from the AddressInfo slice.
func DesiredAddrTypes(addrs []requests.AddressInfo, ipv4, ipv6 bool) []requests.AddressInfo {
	if !ipv4 && !ipv6 {
		return addrs
	}

	var keep []requests.AddressInfo
	for _, addr := range addrs {
		if net.IsIPv4(addr.Address) && !ipv4 {
			continue
		} else if net.IsIPv6(addr.Address) && !ipv6 {
			continue
		}
		keep = append(keep, addr)
	}
	return keep
}
<commit_msg>v3.3.0 release<commit_after>// Copyright 2017 Jeff Foley. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package format

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/OWASP/Amass/v3/net"
	"github.com/OWASP/Amass/v3/requests"
	"github.com/fatih/color"
)

// Banner is the ASCII art logo used within help output.
const Banner = `
        .+++:.            :                             .+++.
      +W@@@@@@8        &+W@#               o8W8:      +W@@@@@@#.   oW@@@W#+
     &@#+   .o@##.    .@@@o@W.o@@o       :@@#&W8o    .@#:  .:oW+  .@#+++&#&
    +@&        &@&     #@8 +@W@&8@+     :@W.   +@8   +@:          .@8
    8@          @@     8@o  8@8  WW    .@W      W@+  .@W.          o@#:
    WW          &@o    &@:  o@+  o@+   #@.      8@o   +W@#+.        +W@8:
    #@          :@W    &@+  &@+   @8  :@o       o@o     oW@@W+        oW@8
    o@+          @@&   &@+  &@+   #@  &@.      .W@W       .+#@&         o@W.
     WW         +@W@8. &@+  :&    o@+ #@      :@W&@&         &@:  ..     :@o
     :@W:      o@# +Wo &@+        :W: +@W&o++o@W. &@&  8@#o+&@W.  #@:    o@+
      :W@@WWWW@@8       +              :&W@@@@&    &W  .o#@@W&.   :W@WWW@@&
        +o&&&&+.                                                    +oooo.
`

const (
	// Version is used to display the current version of Amass.
	Version = "v3.3.0"

	// Author is used to display the Amass Project Team.
	Author = "OWASP Amass Project - @owaspamass"

	// Description is the slogan for the Amass Project.
	Description = "In-depth Attack Surface Mapping and Asset Discovery"
)

var (
	// Colors used to ease the reading of program output
	y      = color.New(color.FgHiYellow)
	g      = color.New(color.FgHiGreen)
	r      = color.New(color.FgHiRed)
	b      = color.New(color.FgHiBlue)
	fgR    = color.New(color.FgRed)
	fgY    = color.New(color.FgYellow)
	yellow = color.New(color.FgHiYellow).SprintFunc()
	green  = color.New(color.FgHiGreen).SprintFunc()
	blue   = color.New(color.FgHiBlue).SprintFunc()
)

// ASNSummaryData stores information related to discovered ASs and netblocks.
type ASNSummaryData struct {
	Name      string
	Netblocks map[string]int
}

// UpdateSummaryData updates the summary maps using the provided requests.Output data.
func UpdateSummaryData(output *requests.Output, tags map[string]int, asns map[int]*ASNSummaryData) {
	tags[output.Tag]++

	for _, addr := range output.Addresses {
		data, found := asns[addr.ASN]
		if !found {
			asns[addr.ASN] = &ASNSummaryData{
				Name:      addr.Description,
				Netblocks: make(map[string]int),
			}
			data = asns[addr.ASN]
		}
		// Increment how many IPs were in this netblock
		data.Netblocks[addr.Netblock.String()]++
	}
}

// PrintEnumerationSummary outputs the summary information utilized by the command-line tools.
func PrintEnumerationSummary(total int, tags map[string]int, asns map[int]*ASNSummaryData, demo bool) {
	pad := func(num int, chr string) {
		for i := 0; i < num; i++ {
			b.Fprint(color.Error, chr)
		}
	}

	fmt.Fprintln(color.Error)
	// Print the header information
	title := "OWASP Amass "
	site := "https://github.com/OWASP/Amass"
	b.Fprint(color.Error, title+Version)
	num := 80 - (len(title) + len(Version) + len(site))
	pad(num, " ")
	b.Fprintf(color.Error, "%s\n", site)
	pad(8, "----------")
	fmt.Fprintf(color.Error, "\n%s%s", yellow(strconv.Itoa(total)), green(" names discovered - "))
	// Print the stats using tag information
	num, length := 1, len(tags)
	for k, v := range tags {
		fmt.Fprintf(color.Error, "%s: %s", green(k), yellow(strconv.Itoa(v)))
		if num < length {
			g.Fprint(color.Error, ", ")
		}
		num++
	}
	fmt.Fprintln(color.Error)

	if len(asns) == 0 {
		return
	}
	// Another line gets printed
	pad(8, "----------")
	fmt.Fprintln(color.Error)
	// Print the ASN and netblock information
	for asn, data := range asns {
		asnstr := strconv.Itoa(asn)
		datastr := data.Name

		if demo && asn > 0 {
			asnstr = censorString(asnstr, 0, len(asnstr))
			datastr = censorString(datastr, 0, len(datastr))
		}

		fmt.Fprintf(color.Error, "%s%s %s %s\n",
			blue("ASN: "), yellow(asnstr), green("-"), green(datastr))

		for cidr, ips := range data.Netblocks {
			countstr := strconv.Itoa(ips)
			cidrstr := cidr

			if demo {
				cidrstr = censorNetBlock(cidrstr)
			}

			countstr = fmt.Sprintf("\t%-4s", countstr)
			cidrstr = fmt.Sprintf("\t%-18s", cidrstr)

			fmt.Fprintf(color.Error, "%s%s %s\n",
				yellow(cidrstr), yellow(countstr), blue("Subdomain Name(s)"))
		}
	}
}

// PrintBanner outputs the Amass banner the same for all tools.
func PrintBanner() {
	y := color.New(color.FgHiYellow)
	r := color.New(color.FgHiRed)
	rightmost := 76

	pad := func(num int) {
		for i := 0; i < num; i++ {
			fmt.Fprint(color.Error, " ")
		}
	}
	r.Fprintln(color.Error, Banner)
	pad(rightmost - len(Version))
	y.Fprintln(color.Error, Version)
	pad(rightmost - len(Author))
	y.Fprintln(color.Error, Author)
	pad(rightmost - len(Description))
	y.Fprintf(color.Error, "%s\n\n\n", Description)
}

func censorDomain(input string) string {
	return censorString(input, strings.Index(input, "."), len(input))
}

func censorIP(input string) string {
	return censorString(input, 0, strings.LastIndex(input, "."))
}

func censorNetBlock(input string) string {
	return censorString(input, 0, strings.Index(input, "/"))
}

func censorString(input string, start, end int) string {
	runes := []rune(input)
	for i := start; i < end; i++ {
		if runes[i] == '.' ||
			runes[i] == '/' ||
			runes[i] == '-' ||
			runes[i] == ' ' {
			continue
		}
		runes[i] = 'x'
	}
	return string(runes)
}

// OutputLineParts returns the parts of a line to be printed for a requests.Output.
func OutputLineParts(out *requests.Output, src, addrs, demo bool) (source, name, ips string) {
	if src {
		source = fmt.Sprintf("%-18s", "["+out.Source+"] ")
	}
	if addrs {
		for i, a := range out.Addresses {
			if i != 0 {
				ips += ","
			}
			if demo {
				ips += censorIP(a.Address.String())
			} else {
				ips += a.Address.String()
			}
		}
		if ips == "" {
			ips = "N/A"
		}
	}
	name = out.Name
	if demo {
		name = censorDomain(name)
	}
	return
}

// DesiredAddrTypes removes undesired address types from the AddressInfo slice.
func DesiredAddrTypes(addrs []requests.AddressInfo, ipv4, ipv6 bool) []requests.AddressInfo {
	if !ipv4 && !ipv6 {
		return addrs
	}

	var keep []requests.AddressInfo
	for _, addr := range addrs {
		if net.IsIPv4(addr.Address) && !ipv4 {
			continue
		} else if net.IsIPv6(addr.Address) && !ipv6 {
			continue
		}
		keep = append(keep, addr)
	}
	return keep
}
<|endoftext|>


<commit_before>// Copyright 2017 Jeff Foley. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package format

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/OWASP/Amass/net"
	"github.com/OWASP/Amass/requests"
	"github.com/fatih/color"
)

// Banner is the ASCII art logo used within help output.
const Banner = `
        .+++:.            :                             .+++.
      +W@@@@@@8        &+W@#               o8W8:      +W@@@@@@#.   oW@@@W#+
     &@#+   .o@##.    .@@@o@W.o@@o       :@@#&W8o    .@#:  .:oW+  .@#+++&#&
    +@&        &@&     #@8 +@W@&8@+     :@W.   +@8   +@:          .@8
    8@          @@     8@o  8@8  WW    .@W      W@+  .@W.          o@#:
    WW          &@o    &@:  o@+  o@+   #@.      8@o   +W@#+.        +W@8:
    #@          :@W    &@+  &@+   @8  :@o       o@o     oW@@W+        oW@8
    o@+          @@&   &@+  &@+   #@  &@.      .W@W       .+#@&         o@W.
     WW         +@W@8. &@+  :&    o@+ #@      :@W&@&         &@:  ..     :@o
     :@W:      o@# +Wo &@+        :W: +@W&o++o@W. &@&  8@#o+&@W.  #@:    o@+
      :W@@WWWW@@8       +              :&W@@@@&    &W  .o#@@W&.   :W@WWW@@&
        +o&&&&+.                                                    +oooo.
`

const (
	// Version is used to display the current version of Amass.
	Version = "v3.1.0"

	// Author is used to display the Amass Project Team.
	Author = "OWASP Amass Project - @owaspamass"

	// Description is the slogan for the Amass Project.
	Description = "In-depth Attack Surface Mapping and Asset Discovery"
)

var (
	// Colors used to ease the reading of program output
	y      = color.New(color.FgHiYellow)
	g      = color.New(color.FgHiGreen)
	r      = color.New(color.FgHiRed)
	b      = color.New(color.FgHiBlue)
	fgR    = color.New(color.FgRed)
	fgY    = color.New(color.FgYellow)
	yellow = color.New(color.FgHiYellow).SprintFunc()
	green  = color.New(color.FgHiGreen).SprintFunc()
	blue   = color.New(color.FgHiBlue).SprintFunc()
)

// ASNSummaryData stores information related to discovered ASs and netblocks.
type ASNSummaryData struct {
	Name      string
	Netblocks map[string]int
}

// UpdateSummaryData updates the summary maps using the provided requests.Output data.
func UpdateSummaryData(output *requests.Output, tags map[string]int, asns map[int]*ASNSummaryData) {
	tags[output.Tag]++

	for _, addr := range output.Addresses {
		data, found := asns[addr.ASN]
		if !found {
			asns[addr.ASN] = &ASNSummaryData{
				Name:      addr.Description,
				Netblocks: make(map[string]int),
			}
			data = asns[addr.ASN]
		}
		// Increment how many IPs were in this netblock
		data.Netblocks[addr.Netblock.String()]++
	}
}

// PrintEnumerationSummary outputs the summary information utilized by the command-line tools.
func PrintEnumerationSummary(total int, tags map[string]int, asns map[int]*ASNSummaryData, demo bool) {
	pad := func(num int, chr string) {
		for i := 0; i < num; i++ {
			b.Fprint(color.Error, chr)
		}
	}

	fmt.Fprintln(color.Error)
	// Print the header information
	title := "OWASP Amass "
	site := "https://github.com/OWASP/Amass"
	b.Fprint(color.Error, title+Version)
	num := 80 - (len(title) + len(Version) + len(site))
	pad(num, " ")
	b.Fprintf(color.Error, "%s\n", site)
	pad(8, "----------")
	fmt.Fprintf(color.Error, "\n%s%s", yellow(strconv.Itoa(total)), green(" names discovered - "))
	// Print the stats using tag information
	num, length := 1, len(tags)
	for k, v := range tags {
		fmt.Fprintf(color.Error, "%s: %s", green(k), yellow(strconv.Itoa(v)))
		if num < length {
			g.Fprint(color.Error, ", ")
		}
		num++
	}
	fmt.Fprintln(color.Error)

	if len(asns) == 0 {
		return
	}
	// Another line gets printed
	pad(8, "----------")
	fmt.Fprintln(color.Error)
	// Print the ASN and netblock information
	for asn, data := range asns {
		asnstr := strconv.Itoa(asn)
		datastr := data.Name

		if demo && asn > 0 {
			asnstr = censorString(asnstr, 0, len(asnstr))
			datastr = censorString(datastr, 0, len(datastr))
		}

		fmt.Fprintf(color.Error, "%s%s %s %s\n",
			blue("ASN: "), yellow(asnstr), green("-"), green(datastr))

		for cidr, ips := range data.Netblocks {
			countstr := strconv.Itoa(ips)
			cidrstr := cidr

			if demo {
				cidrstr = censorNetBlock(cidrstr)
			}

			countstr = fmt.Sprintf("\t%-4s", countstr)
			cidrstr = fmt.Sprintf("\t%-18s", cidrstr)

			fmt.Fprintf(color.Error, "%s%s %s\n",
				yellow(cidrstr), yellow(countstr), blue("Subdomain Name(s)"))
		}
	}
}

// PrintBanner outputs the Amass banner the same for all tools.
func PrintBanner() {
	y := color.New(color.FgHiYellow)
	r := color.New(color.FgHiRed)
	rightmost := 76

	pad := func(num int) {
		for i := 0; i < num; i++ {
			fmt.Fprint(color.Error, " ")
		}
	}
	r.Fprintln(color.Error, Banner)
	pad(rightmost - len(Version))
	y.Fprintln(color.Error, Version)
	pad(rightmost - len(Author))
	y.Fprintln(color.Error, Author)
	pad(rightmost - len(Description))
	y.Fprintf(color.Error, "%s\n\n\n", Description)
}

func censorDomain(input string) string {
	return censorString(input, strings.Index(input, "."), len(input))
}

func censorIP(input string) string {
	return censorString(input, 0, strings.LastIndex(input, "."))
}

func censorNetBlock(input string) string {
	return censorString(input, 0, strings.Index(input, "/"))
}

func censorString(input string, start, end int) string {
	runes := []rune(input)
	for i := start; i < end; i++ {
		if runes[i] == '.' ||
			runes[i] == '/' ||
			runes[i] == '-' ||
			runes[i] == ' ' {
			continue
		}
		runes[i] = 'x'
	}
	return string(runes)
}

// OutputLineParts returns the parts of a line to be printed for a requests.Output.
func OutputLineParts(out *requests.Output, src, addrs, demo bool) (source, name, ips string) {
	if src {
		source = fmt.Sprintf("%-18s", "["+out.Source+"] ")
	}
	if addrs {
		for i, a := range out.Addresses {
			if i != 0 {
				ips += ","
			}
			if demo {
				ips += censorIP(a.Address.String())
			} else {
				ips += a.Address.String()
			}
		}
		if ips == "" {
			ips = "N/A"
		}
	}
	name = out.Name
	if demo {
		name = censorDomain(name)
	}
	return
}

// DesiredAddrTypes removes undesired address types from the AddressInfo slice.
func DesiredAddrTypes(addrs []requests.AddressInfo, ipv4, ipv6 bool) []requests.AddressInfo {
	if !ipv4 && !ipv6 {
		return addrs
	}

	var keep []requests.AddressInfo
	for _, addr := range addrs {
		if net.IsIPv4(addr.Address) && !ipv4 {
			continue
		} else if net.IsIPv6(addr.Address) && !ipv6 {
			continue
		}
		keep = append(keep, addr)
	}
	return keep
}
<commit_msg>v3.1.1 release<commit_after>// Copyright 2017 Jeff Foley. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package format

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/OWASP/Amass/net"
	"github.com/OWASP/Amass/requests"
	"github.com/fatih/color"
)

// Banner is the ASCII art logo used within help output.
const Banner = `
        .+++:.            :                             .+++.
      +W@@@@@@8        &+W@#               o8W8:      +W@@@@@@#.   oW@@@W#+
     &@#+   .o@##.    .@@@o@W.o@@o       :@@#&W8o    .@#:  .:oW+  .@#+++&#&
    +@&        &@&     #@8 +@W@&8@+     :@W.   +@8   +@:          .@8
    8@          @@     8@o  8@8  WW    .@W      W@+  .@W.          o@#:
    WW          &@o    &@:  o@+  o@+   #@.      8@o   +W@#+.        +W@8:
    #@          :@W    &@+  &@+   @8  :@o       o@o     oW@@W+        oW@8
    o@+          @@&   &@+  &@+   #@  &@.      .W@W       .+#@&         o@W.
     WW         +@W@8. &@+  :&    o@+ #@      :@W&@&         &@:  ..     :@o
     :@W:      o@# +Wo &@+        :W: +@W&o++o@W. &@&  8@#o+&@W.  #@:    o@+
      :W@@WWWW@@8       +              :&W@@@@&    &W  .o#@@W&.   :W@WWW@@&
        +o&&&&+.                                                    +oooo.
`

const (
	// Version is used to display the current version of Amass.
	Version = "v3.1.1"

	// Author is used to display the Amass Project Team.
	Author = "OWASP Amass Project - @owaspamass"

	// Description is the slogan for the Amass Project.
	Description = "In-depth Attack Surface Mapping and Asset Discovery"
)

var (
	// Colors used to ease the reading of program output
	y      = color.New(color.FgHiYellow)
	g      = color.New(color.FgHiGreen)
	r      = color.New(color.FgHiRed)
	b      = color.New(color.FgHiBlue)
	fgR    = color.New(color.FgRed)
	fgY    = color.New(color.FgYellow)
	yellow = color.New(color.FgHiYellow).SprintFunc()
	green  = color.New(color.FgHiGreen).SprintFunc()
	blue   = color.New(color.FgHiBlue).SprintFunc()
)

// ASNSummaryData stores information related to discovered ASs and netblocks.
type ASNSummaryData struct {
	Name      string
	Netblocks map[string]int
}

// UpdateSummaryData updates the summary maps using the provided requests.Output data.
func UpdateSummaryData(output *requests.Output, tags map[string]int, asns map[int]*ASNSummaryData) {
	tags[output.Tag]++

	for _, addr := range output.Addresses {
		data, found := asns[addr.ASN]
		if !found {
			asns[addr.ASN] = &ASNSummaryData{
				Name:      addr.Description,
				Netblocks: make(map[string]int),
			}
			data = asns[addr.ASN]
		}
		// Increment how many IPs were in this netblock
		data.Netblocks[addr.Netblock.String()]++
	}
}

// PrintEnumerationSummary outputs the summary information utilized by the command-line tools.
func PrintEnumerationSummary(total int, tags map[string]int, asns map[int]*ASNSummaryData, demo bool) {
	pad := func(num int, chr string) {
		for i := 0; i < num; i++ {
			b.Fprint(color.Error, chr)
		}
	}

	fmt.Fprintln(color.Error)
	// Print the header information
	title := "OWASP Amass "
	site := "https://github.com/OWASP/Amass"
	b.Fprint(color.Error, title+Version)
	num := 80 - (len(title) + len(Version) + len(site))
	pad(num, " ")
	b.Fprintf(color.Error, "%s\n", site)
	pad(8, "----------")
	fmt.Fprintf(color.Error, "\n%s%s", yellow(strconv.Itoa(total)), green(" names discovered - "))
	// Print the stats using tag information
	num, length := 1, len(tags)
	for k, v := range tags {
		fmt.Fprintf(color.Error, "%s: %s", green(k), yellow(strconv.Itoa(v)))
		if num < length {
			g.Fprint(color.Error, ", ")
		}
		num++
	}
	fmt.Fprintln(color.Error)

	if len(asns) == 0 {
		return
	}
	// Another line gets printed
	pad(8, "----------")
	fmt.Fprintln(color.Error)
	// Print the ASN and netblock information
	for asn, data := range asns {
		asnstr := strconv.Itoa(asn)
		datastr := data.Name

		if demo && asn > 0 {
			asnstr = censorString(asnstr, 0, len(asnstr))
			datastr = censorString(datastr, 0, len(datastr))
		}

		fmt.Fprintf(color.Error, "%s%s %s %s\n",
			blue("ASN: "), yellow(asnstr), green("-"), green(datastr))

		for cidr, ips := range data.Netblocks {
			countstr := strconv.Itoa(ips)
			cidrstr := cidr

			if demo {
				cidrstr = censorNetBlock(cidrstr)
			}

			countstr = fmt.Sprintf("\t%-4s", countstr)
			cidrstr = fmt.Sprintf("\t%-18s", cidrstr)

			fmt.Fprintf(color.Error, "%s%s %s\n",
				yellow(cidrstr), yellow(countstr), blue("Subdomain Name(s)"))
		}
	}
}

// PrintBanner outputs the Amass banner the same for all tools.
func PrintBanner() {
	y := color.New(color.FgHiYellow)
	r := color.New(color.FgHiRed)
	rightmost := 76

	pad := func(num int) {
		for i := 0; i < num; i++ {
			fmt.Fprint(color.Error, " ")
		}
	}
	r.Fprintln(color.Error, Banner)
	pad(rightmost - len(Version))
	y.Fprintln(color.Error, Version)
	pad(rightmost - len(Author))
	y.Fprintln(color.Error, Author)
	pad(rightmost - len(Description))
	y.Fprintf(color.Error, "%s\n\n\n", Description)
}

func censorDomain(input string) string {
	return censorString(input, strings.Index(input, "."), len(input))
}

func censorIP(input string) string {
	return censorString(input, 0, strings.LastIndex(input, "."))
}

func censorNetBlock(input string) string {
	return censorString(input, 0, strings.Index(input, "/"))
}

func censorString(input string, start, end int) string {
	runes := []rune(input)
	for i := start; i < end; i++ {
		if runes[i] == '.' ||
			runes[i] == '/' ||
			runes[i] == '-' ||
			runes[i] == ' ' {
			continue
		}
		runes[i] = 'x'
	}
	return string(runes)
}

// OutputLineParts returns the parts of a line to be printed for a requests.Output.
func OutputLineParts(out *requests.Output, src, addrs, demo bool) (source, name, ips string) {
	if src {
		source = fmt.Sprintf("%-18s", "["+out.Source+"] ")
	}
	if addrs {
		for i, a := range out.Addresses {
			if i != 0 {
				ips += ","
			}
			if demo {
				ips += censorIP(a.Address.String())
			} else {
				ips += a.Address.String()
			}
		}
		if ips == "" {
			ips = "N/A"
		}
	}
	name = out.Name
	if demo {
		name = censorDomain(name)
	}
	return
}

// DesiredAddrTypes removes undesired address types from the AddressInfo slice.
func DesiredAddrTypes(addrs []requests.AddressInfo, ipv4, ipv6 bool) []requests.AddressInfo {
	if !ipv4 && !ipv6 {
		return addrs
	}

	var keep []requests.AddressInfo
	for _, addr := range addrs {
		if net.IsIPv4(addr.Address) && !ipv4 {
			continue
		} else if net.IsIPv6(addr.Address) && !ipv6 {
			continue
		}
		keep = append(keep, addr)
	}
	return keep
}
<|endoftext|>


<commit_before>package msgs

import (
	"fmt"
	color "github.com/acmacalister/skittles"
	"github.com/synw/microb/libmicrob/types"
	"github.com/synw/terr"
)

func Warning(txt string) {
	msg := "[" + color.Magenta("Warning") + "] " + txt
	fmt.Println(msg)
}

func Status(txt string) {
	msg := "[" + color.Blue("Status") + "] " + txt
	fmt.Println(msg)
}

func State(txt string) {
	msg := "[" + color.Yellow("State") + "] " + txt
	fmt.Println(msg)
}

func Ready(txt string) {
	msg := "[" + color.BoldGreen("Ready") + "] " + txt
	fmt.Println(msg)
}

func Msg(txt string) {
	fmt.Println(txt)
}

func Ok(txt string) {
	msg := "[" + color.Green("Ok") + "] " + txt
	fmt.Println(msg)
}

func Timeout(txt string) {
	msg := "[" + color.BoldRed("Timeout") + "] " + txt
	fmt.Println(msg)
}

func Error(txt string) {
	msg := "[" + color.BoldRed("Error") + "] " + txt
	fmt.Println(msg)
}

func Tr(tr *terr.Trace) {
	tr.Printc()
}

func Debug(obj ...interface{}) {
	for i, el := range obj {
		msg := "[" + color.BoldRed("Debug") + "]"
		fmt.Println(msg, i, el)
	}
}

func Bold(txt string) string {
	txt = color.BoldWhite(txt)
	return txt
}

func PrintEvent(event *types.Event) {
	if event.Class == "state" {
		State(event.Msg)
	} else if event.Class == "status" {
		Status(event.Msg)
	} else if event.Class == "error" {
		Error(event.Msg)
		if event.Trace != nil {
			Tr(event.Trace)
		}
	} else if event.Class == "command_in" {
		msg := " => " + color.Blue("Incoming command") + " " + event.Msg
		fmt.Println(msg)
	} else if event.Class == "command_out" {
		status := event.Cmd.Status
		if status == "error" {
			status = color.BoldRed("Error")
			fmt.Println("  |->", status, event.Cmd.Trace.Format())
		} else if status == "success" {
			status = color.Green("Success")
			fmt.Println("  |->", status, event.Cmd.ReturnValues)
		}
	} else {
		msg := "[" + color.Blue(event.Class) + "] " + event.Msg
		fmt.Println(msg)
	}

}
<commit_msg>Add an output type parameter to messages generators<commit_after>package msgs

import (
	"fmt"
	color "github.com/acmacalister/skittles"
	"github.com/synw/microb/libmicrob/types"
	"github.com/synw/terr"
)

func Warning(txt string, output ...string) {
	msg := "[" + color.Magenta("Warning") + "] " + txt
	fmt.Println(msg)
}

func Status(txt string, output ...string) {
	msg := "[" + color.Blue("Status") + "] " + txt
	fmt.Println(msg)
}

func State(txt string, output ...string) {
	msg := "[" + color.Yellow("State") + "] " + txt
	fmt.Println(msg)
}

func Ready(txt string, output ...string) {
	msg := "[" + color.BoldGreen("Ready") + "] " + txt
	fmt.Println(msg)
}

func Msg(txt string, output ...string) {
	fmt.Println(txt)
}

func Ok(txt string, output ...string) {
	msg := "[" + color.Green("Ok") + "] " + txt
	fmt.Println(msg)
}

func Timeout(txt string, output ...string) {
	msg := "[" + color.BoldRed("Timeout") + "] " + txt
	fmt.Println(msg)
}

func Error(txt string, output ...string) {
	msg := "[" + color.BoldRed("Error") + "] " + txt
	fmt.Println(msg)
}

func Tr(tr *terr.Trace) {
	tr.Printc()
}

func Debug(obj ...interface{}) {
	for i, el := range obj {
		msg := "[" + color.BoldRed("Debug") + "]"
		fmt.Println(msg, i, el)
	}
}

func Bold(txt string, output ...string) string {
	txt = color.BoldWhite(txt)
	return txt
}

func PrintEvent(event *types.Event) {
	if event.Class == "state" {
		State(event.Msg)
	} else if event.Class == "status" {
		Status(event.Msg)
	} else if event.Class == "error" {
		Error(event.Msg)
		if event.Trace != nil {
			Tr(event.Trace)
		}
	} else if event.Class == "command_in" {
		if event.Cmd.From == "cli" {
			msg := " => " + color.Blue("Incoming command") + " " + event.Msg
			fmt.Println(msg)
		}
	} else if event.Class == "command_out" {
		status := event.Cmd.Status
		if status == "error" {
			if event.Cmd.From == "cli" {
				status = color.BoldRed("Error")
				fmt.Println("  |->", status, event.Cmd.Trace.Format())
			}
		} else if status == "success" {
			if event.Cmd.From == "cli" {
				status = color.Green("Success")
				fmt.Println("  |->", status, event.Cmd.ReturnValues)
			}
		}
	} else {
		if event.Cmd.From == "cli" {
			msg := "[" + color.Blue(event.Class) + "] " + event.Msg
			fmt.Println(msg)
		}
	}

}
<|endoftext|>


<commit_before>// +build monitoring

package monitoring

import (
	"net/http"
	"sync"

	"google.golang.org/grpc"

	"github.com/grpc-ecosystem/go-grpc-prometheus"
	"github.com/lightningnetwork/lnd/lncfg"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

var started sync.Once

// GetPromInterceptors returns the set of interceptors for Prometheus
// monitoring.
func GetPromInterceptors() ([]grpc.UnaryServerInterceptor, []grpc.StreamServerInterceptor) {
	unaryInterceptors := []grpc.UnaryServerInterceptor{
		grpc_prometheus.UnaryServerInterceptor,
	}
	streamInterceptors := []grpc.StreamServerInterceptor{
		grpc_prometheus.StreamServerInterceptor,
	}
	return unaryInterceptors, streamInterceptors
}

// ExportPrometheusMetrics sets server options, registers gRPC metrics and
// launches the Prometheus exporter on the specified address.
func ExportPrometheusMetrics(grpcServer *grpc.Server, cfg lncfg.Prometheus) error {
	started.Do(func() {
		log.Infof("Prometheus exporter started on %v/metrics", cfg.Listen)

		grpc_prometheus.Register(grpcServer)

		http.Handle("/metrics", promhttp.Handler())
		go func() {
			http.ListenAndServe(cfg.Listen, nil)
		}()
	})

	return nil
}
<commit_msg>monitoring: run go fmt<commit_after>// +build monitoring

package monitoring

import (
	"net/http"
	"sync"

	"google.golang.org/grpc"

	grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
	"github.com/lightningnetwork/lnd/lncfg"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

var started sync.Once

// GetPromInterceptors returns the set of interceptors for Prometheus
// monitoring.
func GetPromInterceptors() ([]grpc.UnaryServerInterceptor, []grpc.StreamServerInterceptor) {
	unaryInterceptors := []grpc.UnaryServerInterceptor{
		grpc_prometheus.UnaryServerInterceptor,
	}
	streamInterceptors := []grpc.StreamServerInterceptor{
		grpc_prometheus.StreamServerInterceptor,
	}
	return unaryInterceptors, streamInterceptors
}

// ExportPrometheusMetrics sets server options, registers gRPC metrics and
// launches the Prometheus exporter on the specified address.
func ExportPrometheusMetrics(grpcServer *grpc.Server, cfg lncfg.Prometheus) error {
	started.Do(func() {
		log.Infof("Prometheus exporter started on %v/metrics", cfg.Listen)

		grpc_prometheus.Register(grpcServer)

		http.Handle("/metrics", promhttp.Handler())
		go func() {
			http.ListenAndServe(cfg.Listen, nil)
		}()
	})

	return nil
}
<|endoftext|>


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

package fs

import (
	"io"
	"os"
	"path/filepath"
	"strings"
	"syscall"
	"time"
)

func isSlash(c uint8) bool { return c == '\\' || c == '/' }

func absPath(path string) (string, error) {
	if filepath.IsAbs(path) {
		return filepath.Clean(path), nil
	}
	wd, err := os.Getwd()
	if err != nil {
		return "", err
	}
	if len(path) > 0 && isSlash(path[0]) {
		return filepath.Join(filepath.VolumeName(wd), path), nil
	}
	return filepath.Join(wd, path), nil
}

func winPath(path string) (string, error) {
	p, err := absPath(path)
	if err != nil {
		return "", err
	}
	if len(p) >= syscall.MAX_PATH-2 {
		if !strings.HasPrefix(p, `\\?\`) {
			p = `\\?\` + p
		}
		return p, nil
	}
	return path, nil
}

func newPathError(op, path string, err error) error {
	return &os.PathError{
		Op:   "fs: " + op,
		Path: path,
		Err:  err,
	}
}

func newLinkError(op, oldname, newname string, err error) error {
	return &os.LinkError{
		Op:  "fs: " + op,
		Old: oldname,
		New: newname,
		Err: err,
	}
}

func chdir(dir string) error {
	p, err := winPath(dir)
	if err != nil {
		return newPathError("chdir", dir, err)
	}
	return os.Chdir(p)
}

func chmod(name string, mode os.FileMode) error {
	p, err := winPath(name)
	if err != nil {
		return newPathError("chmod", name, err)
	}
	return os.Chmod(p, mode)
}

func chown(name string, uid, gid int) error {
	p, err := winPath(name)
	if err != nil {
		return newPathError("chown", name, err)
	}
	return os.Chown(p, uid, gid)
}

func chtimes(name string, atime time.Time, mtime time.Time) error {
	p, err := winPath(name)
	if err != nil {
		return newPathError("chtimes", name, err)
	}
	return os.Chtimes(p, atime, mtime)
}

func lchown(name string, uid, gid int) error {
	p, err := winPath(name)
	if err != nil {
		return newPathError("lchown", name, err)
	}
	return os.Lchown(p, uid, gid)
}

func link(oldname, newname string) error {
	op, err := winPath(oldname)
	if err != nil {
		return newLinkError("link", oldname, newname, err)
	}
	np, err := winPath(newname)
	if err != nil {
		return newLinkError("link", oldname, newname, err)
	}
	return os.Link(op, np)
}

func mkdir(name string, perm os.FileMode) error {
	p, err := winPath(name)
	if err != nil {
		return newPathError("mkdir", name, err)
	}
	return os.Mkdir(p, perm)
}

func mkdirall(path string, perm os.FileMode) error {
	p, err := winPath(path)
	if err != nil {
		return err
	}
	return os.MkdirAll(p, perm)
}

func readlink(name string) (string, error) {
	p, err := winPath(name)
	if err != nil {
		return "", newPathError("readlink", name, err)
	}
	return os.Readlink(p)
}

func remove(name string) error {
	p, err := winPath(name)
	if err != nil {
		return newPathError("remove", name, err)
	}
	return os.Remove(p)
}

func removeall(name string) error {
	path, err := winPath(name)
	if err != nil {
		return newPathError("remove", path, err)
	}
	// Simple case: if Remove works, we're done.
	err = os.Remove(path)
	if err == nil || os.IsNotExist(err) {
		return nil
	}

	// Otherwise, is this a directory we need to recurse into?
	dir, serr := Lstat(path)
	if serr != nil {
		if serr, ok := serr.(*os.PathError); ok && (os.IsNotExist(serr.Err) || serr.Err == syscall.ENOTDIR) {
			return nil
		}
		return serr
	}
	if !dir.IsDir() {
		// Not a directory; return the error from Remove.
		return err
	}

	// Directory.
	fd, err := Open(path)
	if err != nil {
		if os.IsNotExist(err) {
			// Race. It was deleted between the Lstat and Open.
			// Return nil per RemoveAll's docs.
			return nil
		}
		return err
	}

	// Remove contents & return first error.
	err = nil
	for {
		names, err1 := fd.Readdirnames(100)
		for _, name := range names {
			err1 := RemoveAll(path + string(os.PathSeparator) + name)
			if err == nil {
				err = err1
			}
		}
		if err1 == io.EOF {
			break
		}
		// If Readdirnames returned an error, use it.
		if err == nil {
			err = err1
		}
		if len(names) == 0 {
			break
		}
	}

	// Close directory, because windows won't remove opened directory.
	fd.Close()

	// Remove directory.
	err1 := Remove(path)
	if err1 == nil || os.IsNotExist(err1) {
		return nil
	}
	if err == nil {
		err = err1
	}
	return err
}

func rename(oldpath, newpath string) error {
	op, err := winPath(oldpath)
	if err != nil {
		return newLinkError("rename", oldpath, newpath, err)
	}
	np, err := winPath(newpath)
	if err != nil {
		return newLinkError("rename", oldpath, newpath, err)
	}
	return os.Rename(op, np)
}

func symlink(oldname, newname string) error {
	op, err := winPath(oldname)
	if err != nil {
		return newLinkError("symlink", oldname, newname, err)
	}
	np, err := winPath(newname)
	if err != nil {
		return newLinkError("symlink", oldname, newname, err)
	}
	return os.Symlink(op, np)
}

func create(name string) (*os.File, error) {
	p, err := winPath(name)
	if err != nil {
		return nil, newPathError("create", name, err)
	}
	return os.Create(p)
}

func newfile(fd uintptr, name string) *os.File {
	p, err := winPath(name)
	if err != nil {
		return os.NewFile(fd, name)
	}
	return os.NewFile(fd, p)
}

func open(name string) (*os.File, error) {
	p, err := winPath(name)
	if err != nil {
		return nil, newPathError("open", name, err)
	}
	return os.Open(p)
}

func openfile(name string, flag int, perm os.FileMode) (*os.File, error) {
	p, err := winPath(name)
	if err != nil {
		return nil, newPathError("openfile", name, err)
	}
	return os.OpenFile(p, flag, perm)
}

func lstat(name string) (os.FileInfo, error) {
	p, err := winPath(name)
	if err != nil {
		return nil, newPathError("lstat", name, err)
	}
	return os.Lstat(p)
}

func stat(name string) (os.FileInfo, error) {
	p, err := winPath(name)
	if err != nil {
		return nil, newPathError("stat", name, err)
	}
	return os.Stat(p)
}
<commit_msg>Max directory path windows is 248<commit_after>// The below code uses portions of the Go standard library.
// The full license can be found in fs.go.
//
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package fs

import (
	"io"
	"os"
	"path/filepath"
	"strings"
	"syscall"
	"time"
)

func isSlash(c uint8) bool { return c == '\\' || c == '/' }

func absPath(path string) (string, error) {
	if filepath.IsAbs(path) {
		return filepath.Clean(path), nil
	}
	wd, err := os.Getwd()
	if err != nil {
		return "", err
	}
	if len(path) > 0 && isSlash(path[0]) {
		return filepath.Join(filepath.VolumeName(wd), path), nil
	}
	return filepath.Join(wd, path), nil
}

func winPath(path string) (string, error) {
	p, err := absPath(path)
	if err != nil {
		return "", err
	}
	//https://msdn.microsoft.com/en-us/library/system.io.path.getfullpath(v=vs.110).aspx
	//260 - 12 = 248
	if len(p) >= syscall.MAX_PATH-12 {
		if !strings.HasPrefix(p, `\\?\`) {
			p = `\\?\` + p
		}
		return p, nil
	}
	return path, nil
}

func newPathError(op, path string, err error) error {
	return &os.PathError{
		Op:   "fs: " + op,
		Path: path,
		Err:  err,
	}
}

func newLinkError(op, oldname, newname string, err error) error {
	return &os.LinkError{
		Op:  "fs: " + op,
		Old: oldname,
		New: newname,
		Err: err,
	}
}

func chdir(dir string) error {
	p, err := winPath(dir)
	if err != nil {
		return newPathError("chdir", dir, err)
	}
	return os.Chdir(p)
}

func chmod(name string, mode os.FileMode) error {
	p, err := winPath(name)
	if err != nil {
		return newPathError("chmod", name, err)
	}
	return os.Chmod(p, mode)
}

func chown(name string, uid, gid int) error {
	p, err := winPath(name)
	if err != nil {
		return newPathError("chown", name, err)
	}
	return os.Chown(p, uid, gid)
}

func chtimes(name string, atime time.Time, mtime time.Time) error {
	p, err := winPath(name)
	if err != nil {
		return newPathError("chtimes", name, err)
	}
	return os.Chtimes(p, atime, mtime)
}

func lchown(name string, uid, gid int) error {
	p, err := winPath(name)
	if err != nil {
		return newPathError("lchown", name, err)
	}
	return os.Lchown(p, uid, gid)
}

func link(oldname, newname string) error {
	op, err := winPath(oldname)
	if err != nil {
		return newLinkError("link", oldname, newname, err)
	}
	np, err := winPath(newname)
	if err != nil {
		return newLinkError("link", oldname, newname, err)
	}
	return os.Link(op, np)
}

func mkdir(name string, perm os.FileMode) error {
	p, err := winPath(name)
	if err != nil {
		return newPathError("mkdir", name, err)
	}
	return os.Mkdir(p, perm)
}

func mkdirall(path string, perm os.FileMode) error {
	p, err := winPath(path)
	if err != nil {
		return err
	}
	return os.MkdirAll(p, perm)
}

func readlink(name string) (string, error) {
	p, err := winPath(name)
	if err != nil {
		return "", newPathError("readlink", name, err)
	}
	return os.Readlink(p)
}

func remove(name string) error {
	p, err := winPath(name)
	if err != nil {
		return newPathError("remove", name, err)
	}
	return os.Remove(p)
}

func removeall(name string) error {
	path, err := winPath(name)
	if err != nil {
		return newPathError("remove", path, err)
	}
	// Simple case: if Remove works, we're done.
	err = os.Remove(path)
	if err == nil || os.IsNotExist(err) {
		return nil
	}

	// Otherwise, is this a directory we need to recurse into?
	dir, serr := Lstat(path)
	if serr != nil {
		if serr, ok := serr.(*os.PathError); ok && (os.IsNotExist(serr.Err) || serr.Err == syscall.ENOTDIR) {
			return nil
		}
		return serr
	}
	if !dir.IsDir() {
		// Not a directory; return the error from Remove.
		return err
	}

	// Directory.
	fd, err := Open(path)
	if err != nil {
		if os.IsNotExist(err) {
			// Race. It was deleted between the Lstat and Open.
			// Return nil per RemoveAll's docs.
			return nil
		}
		return err
	}

	// Remove contents & return first error.
	err = nil
	for {
		names, err1 := fd.Readdirnames(100)
		for _, name := range names {
			err1 := RemoveAll(path + string(os.PathSeparator) + name)
			if err == nil {
				err = err1
			}
		}
		if err1 == io.EOF {
			break
		}
		// If Readdirnames returned an error, use it.
		if err == nil {
			err = err1
		}
		if len(names) == 0 {
			break
		}
	}

	// Close directory, because windows won't remove opened directory.
	fd.Close()

	// Remove directory.
	err1 := Remove(path)
	if err1 == nil || os.IsNotExist(err1) {
		return nil
	}
	if err == nil {
		err = err1
	}
	return err
}

func rename(oldpath, newpath string) error {
	op, err := winPath(oldpath)
	if err != nil {
		return newLinkError("rename", oldpath, newpath, err)
	}
	np, err := winPath(newpath)
	if err != nil {
		return newLinkError("rename", oldpath, newpath, err)
	}
	return os.Rename(op, np)
}

func symlink(oldname, newname string) error {
	op, err := winPath(oldname)
	if err != nil {
		return newLinkError("symlink", oldname, newname, err)
	}
	np, err := winPath(newname)
	if err != nil {
		return newLinkError("symlink", oldname, newname, err)
	}
	return os.Symlink(op, np)
}

func create(name string) (*os.File, error) {
	p, err := winPath(name)
	if err != nil {
		return nil, newPathError("create", name, err)
	}
	return os.Create(p)
}

func newfile(fd uintptr, name string) *os.File {
	p, err := winPath(name)
	if err != nil {
		return os.NewFile(fd, name)
	}
	return os.NewFile(fd, p)
}

func open(name string) (*os.File, error) {
	p, err := winPath(name)
	if err != nil {
		return nil, newPathError("open", name, err)
	}
	return os.Open(p)
}

func openfile(name string, flag int, perm os.FileMode) (*os.File, error) {
	p, err := winPath(name)
	if err != nil {
		return nil, newPathError("openfile", name, err)
	}
	return os.OpenFile(p, flag, perm)
}

func lstat(name string) (os.FileInfo, error) {
	p, err := winPath(name)
	if err != nil {
		return nil, newPathError("lstat", name, err)
	}
	return os.Lstat(p)
}

func stat(name string) (os.FileInfo, error) {
	p, err := winPath(name)
	if err != nil {
		return nil, newPathError("stat", name, err)
	}
	return os.Stat(p)
}
<|endoftext|>


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

// +build freebsd openbsd netbsd darwin

//Package fsnotify implements filesystem notification.
package fsnotify

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"syscall"
)

type FileEvent struct {
	mask   uint32 // Mask of events
	Name   string // File name (optional)
	create bool   // set by fsnotify package if found new file
}

// IsCreate reports whether the FileEvent was triggerd by a creation
func (e *FileEvent) IsCreate() bool { return e.create }

// IsDelete reports whether the FileEvent was triggerd by a delete
func (e *FileEvent) IsDelete() bool { return (e.mask & NOTE_DELETE) == NOTE_DELETE }

// IsModify reports whether the FileEvent was triggerd by a file modification
func (e *FileEvent) IsModify() bool {
	return ((e.mask&NOTE_WRITE) == NOTE_WRITE || (e.mask&NOTE_ATTRIB) == NOTE_ATTRIB)
}

// IsRename reports whether the FileEvent was triggerd by a change name
func (e *FileEvent) IsRename() bool { return (e.mask & NOTE_RENAME) == NOTE_RENAME }

type Watcher struct {
	kq            int                 // File descriptor (as returned by the kqueue() syscall)
	watches       map[string]int      // Map of watched file diescriptors (key: path)
	fsnFlags      map[string]uint32   // Map of watched files to flags used for filter
	paths         map[int]string      // Map of watched paths (key: watch descriptor)
	finfo         map[int]os.FileInfo // Map of file information (isDir, isReg; key: watch descriptor)
	Error         chan error          // Errors are sent on this channel
	internalEvent chan *FileEvent     // Events are queued on this channel
	Event         chan *FileEvent     // Events are returned on this channel
	done          chan bool           // Channel for sending a "quit message" to the reader goroutine
	isClosed      bool                // Set to true when Close() is first called
	kbuf          [1]syscall.Kevent_t // An event buffer for Add/Remove watch
}

// NewWatcher creates and returns a new kevent instance using kqueue(2)
func NewWatcher() (*Watcher, error) {
	fd, errno := syscall.Kqueue()
	if fd == -1 {
		return nil, os.NewSyscallError("kqueue", errno)
	}
	w := &Watcher{
		kq:            fd,
		watches:       make(map[string]int),
		fsnFlags:      make(map[string]uint32),
		paths:         make(map[int]string),
		finfo:         make(map[int]os.FileInfo),
		internalEvent: make(chan *FileEvent),
		Event:         make(chan *FileEvent),
		Error:         make(chan error),
		done:          make(chan bool, 1),
	}

	go w.readEvents()
	go w.purgeEvents()
	return w, nil
}

// Close closes a kevent watcher instance
// It sends a message to the reader goroutine to quit and removes all watches
// associated with the kevent instance
func (w *Watcher) Close() error {
	if w.isClosed {
		return nil
	}
	w.isClosed = true

	// Send "quit" message to the reader goroutine
	w.done <- true
	for path := range w.watches {
		w.removeWatch(path)
	}

	return nil
}

// AddWatch adds path to the watched file set.
// The flags are interpreted as described in kevent(2).
func (w *Watcher) addWatch(path string, flags uint32) error {
	if w.isClosed {
		return errors.New("kevent instance already closed")
	}

	watchEntry := &w.kbuf[0]
	watchEntry.Fflags = flags

	watchfd, found := w.watches[path]
	if !found {
		fi, errstat := os.Lstat(path)
		if errstat != nil {
			return errstat
		}

		// Follow Symlinks
		// Unfortunately, Linux can add bogus symlinks to watch list without
		// issue, and Windows can't do symlinks period (AFAIK). To  maintain
		// consistency, we will act like everything is fine. There will simply
		// be no file events for broken symlinks.
		// Hence the returns of nil on errors.
		if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
			path, err := filepath.EvalSymlinks(path)
			if err != nil {
				return nil
			}

			fi, errstat = os.Lstat(path)
			if errstat != nil {
				return nil
			}
		}

		fd, errno := syscall.Open(path, syscall.O_NONBLOCK|syscall.O_RDONLY, 0700)
		if fd == -1 {
			return errno
		}
		watchfd = fd

		w.watches[path] = watchfd
		w.paths[watchfd] = path

		w.finfo[watchfd] = fi
		if fi.IsDir() {
			errdir := w.watchDirectoryFiles(path)
			if errdir != nil {
				return errdir
			}
		}
	}
	syscall.SetKevent(watchEntry, watchfd, syscall.EVFILT_VNODE, syscall.EV_ADD|syscall.EV_CLEAR)

	wd, errno := syscall.Kevent(w.kq, w.kbuf[:], nil, nil)
	if wd == -1 {
		return errno
	} else if (watchEntry.Flags & syscall.EV_ERROR) == syscall.EV_ERROR {
		return errors.New("kevent add error")
	}

	return nil
}

// Watch adds path to the watched file set, watching all events.
func (w *Watcher) watch(path string) error {
	return w.addWatch(path, NOTE_ALLEVENTS)
}

// RemoveWatch removes path from the watched file set.
func (w *Watcher) removeWatch(path string) error {
	watchfd, ok := w.watches[path]
	if !ok {
		return errors.New(fmt.Sprintf("can't remove non-existent kevent watch for: %s", path))
	}
	watchEntry := &w.kbuf[0]
	syscall.SetKevent(watchEntry, w.watches[path], syscall.EVFILT_VNODE, syscall.EV_DELETE)
	success, errno := syscall.Kevent(w.kq, w.kbuf[:], nil, nil)
	if success == -1 {
		return os.NewSyscallError("kevent_rm_watch", errno)
	} else if (watchEntry.Flags & syscall.EV_ERROR) == syscall.EV_ERROR {
		return errors.New("kevent rm error")
	}
	syscall.Close(watchfd)
	delete(w.watches, path)
	return nil
}

// readEvents reads from the kqueue file descriptor, converts the
// received events into Event objects and sends them via the Event channel
func (w *Watcher) readEvents() {
	var (
		eventbuf [10]syscall.Kevent_t // Event buffer
		events   []syscall.Kevent_t   // Received events
		twait    *syscall.Timespec    // Time to block waiting for events
		n        int                  // Number of events returned from kevent
		errno    error                // Syscall errno
	)
	events = eventbuf[0:0]
	twait = new(syscall.Timespec)
	*twait = syscall.NsecToTimespec(keventWaitTime)

	for {
		// See if there is a message on the "done" channel
		var done bool
		select {
		case done = <-w.done:
		default:
		}

		// If "done" message is received
		if done {
			errno := syscall.Close(w.kq)
			if errno != nil {
				w.Error <- os.NewSyscallError("close", errno)
			}
			close(w.internalEvent)
			close(w.Error)
			return
		}

		// Get new events
		if len(events) == 0 {
			n, errno = syscall.Kevent(w.kq, nil, eventbuf[:], twait)

			// EINTR is okay, basically the syscall was interrupted before
			// timeout expired.
			if errno != nil && errno != syscall.EINTR {
				w.Error <- os.NewSyscallError("kevent", errno)
				continue
			}

			// Received some events
			if n > 0 {
				events = eventbuf[0:n]
			}
		}

		// Flush the events we recieved to the events channel
		for len(events) > 0 {
			fileEvent := new(FileEvent)
			watchEvent := &events[0]
			fileEvent.mask = uint32(watchEvent.Fflags)
			fileEvent.Name = w.paths[int(watchEvent.Ident)]

			fileInfo := w.finfo[int(watchEvent.Ident)]
			if fileInfo.IsDir() && fileEvent.IsModify() {
				w.sendDirectoryChangeEvents(fileEvent.Name)
			} else {
				// Send the event on the events channel
				w.internalEvent <- fileEvent
			}

			// Move to next event
			events = events[1:]

			if fileEvent.IsRename() {
				w.removeWatch(fileEvent.Name)
			}
		}
	}
}

func (w *Watcher) watchDirectoryFiles(dirPath string) error {
	// Get all files
	files, err := ioutil.ReadDir(dirPath)
	if err != nil {
		return err
	}

	// Search for new files
	for _, fileInfo := range files {
		if fileInfo.IsDir() == false {
			filePath := filepath.Join(dirPath, fileInfo.Name())
			// Watch file to mimic linux fsnotify
			e := w.addWatch(filePath, NOTE_DELETE|NOTE_WRITE|NOTE_RENAME)
			w.fsnFlags[filePath] = FSN_ALL
			if e != nil {
				return e
			}
		}
	}

	return nil
}

// sendDirectoryEvents searches the directory for newly created files
// and sends them over the event channel. This functionality is to have
// the BSD version of fsnotify mach linux fsnotify which provides a
// create event for files created in a watched directory.
func (w *Watcher) sendDirectoryChangeEvents(dirPath string) {
	// Get all files
	files, err := ioutil.ReadDir(dirPath)
	if err != nil {
		w.Error <- err
	}

	// Search for new files
	for _, fileInfo := range files {
		if fileInfo.IsDir() == false {
			filePath := filepath.Join(dirPath, fileInfo.Name())
			_, watchFound := w.watches[filePath]
			if watchFound == false {
				w.fsnFlags[filePath] = FSN_ALL
				// Send create event
				fileEvent := new(FileEvent)
				fileEvent.Name = filePath
				fileEvent.create = true
				w.internalEvent <- fileEvent
			}
		}
	}
	w.watchDirectoryFiles(dirPath)
}

const (
	// Flags (from <sys/event.h>)
	NOTE_DELETE = 0x0001 /* vnode was removed */
	NOTE_WRITE  = 0x0002 /* data contents changed */
	NOTE_EXTEND = 0x0004 /* size increased */
	NOTE_ATTRIB = 0x0008 /* attributes changed */
	NOTE_LINK   = 0x0010 /* link count changed */
	NOTE_RENAME = 0x0020 /* vnode was renamed */
	NOTE_REVOKE = 0x0040 /* vnode access was revoked */

	// Watch all events
	NOTE_ALLEVENTS = NOTE_DELETE | NOTE_WRITE | NOTE_ATTRIB | NOTE_RENAME

	// Block for 100 ms on each call to kevent
	keventWaitTime = 100e6
)
<commit_msg>BSD - Now receive events for created directories<commit_after>// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build freebsd openbsd netbsd darwin

//Package fsnotify implements filesystem notification.
package fsnotify

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"syscall"
)

type FileEvent struct {
	mask   uint32 // Mask of events
	Name   string // File name (optional)
	create bool   // set by fsnotify package if found new file
}

// IsCreate reports whether the FileEvent was triggerd by a creation
func (e *FileEvent) IsCreate() bool { return e.create }

// IsDelete reports whether the FileEvent was triggerd by a delete
func (e *FileEvent) IsDelete() bool { return (e.mask & NOTE_DELETE) == NOTE_DELETE }

// IsModify reports whether the FileEvent was triggerd by a file modification
func (e *FileEvent) IsModify() bool {
	return ((e.mask&NOTE_WRITE) == NOTE_WRITE || (e.mask&NOTE_ATTRIB) == NOTE_ATTRIB)
}

// IsRename reports whether the FileEvent was triggerd by a change name
func (e *FileEvent) IsRename() bool { return (e.mask & NOTE_RENAME) == NOTE_RENAME }

type Watcher struct {
	kq            int                 // File descriptor (as returned by the kqueue() syscall)
	watches       map[string]int      // Map of watched file diescriptors (key: path)
	fsnFlags      map[string]uint32   // Map of watched files to flags used for filter
	paths         map[int]string      // Map of watched paths (key: watch descriptor)
	finfo         map[int]os.FileInfo // Map of file information (isDir, isReg; key: watch descriptor)
	Error         chan error          // Errors are sent on this channel
	internalEvent chan *FileEvent     // Events are queued on this channel
	Event         chan *FileEvent     // Events are returned on this channel
	done          chan bool           // Channel for sending a "quit message" to the reader goroutine
	isClosed      bool                // Set to true when Close() is first called
	kbuf          [1]syscall.Kevent_t // An event buffer for Add/Remove watch
}

// NewWatcher creates and returns a new kevent instance using kqueue(2)
func NewWatcher() (*Watcher, error) {
	fd, errno := syscall.Kqueue()
	if fd == -1 {
		return nil, os.NewSyscallError("kqueue", errno)
	}
	w := &Watcher{
		kq:            fd,
		watches:       make(map[string]int),
		fsnFlags:      make(map[string]uint32),
		paths:         make(map[int]string),
		finfo:         make(map[int]os.FileInfo),
		internalEvent: make(chan *FileEvent),
		Event:         make(chan *FileEvent),
		Error:         make(chan error),
		done:          make(chan bool, 1),
	}

	go w.readEvents()
	go w.purgeEvents()
	return w, nil
}

// Close closes a kevent watcher instance
// It sends a message to the reader goroutine to quit and removes all watches
// associated with the kevent instance
func (w *Watcher) Close() error {
	if w.isClosed {
		return nil
	}
	w.isClosed = true

	// Send "quit" message to the reader goroutine
	w.done <- true
	for path := range w.watches {
		w.removeWatch(path)
	}

	return nil
}

// AddWatch adds path to the watched file set.
// The flags are interpreted as described in kevent(2).
func (w *Watcher) addWatch(path string, flags uint32) error {
	if w.isClosed {
		return errors.New("kevent instance already closed")
	}

	watchEntry := &w.kbuf[0]
	watchEntry.Fflags = flags

	watchfd, found := w.watches[path]
	if !found {
		fi, errstat := os.Lstat(path)
		if errstat != nil {
			return errstat
		}

		// Follow Symlinks
		// Unfortunately, Linux can add bogus symlinks to watch list without
		// issue, and Windows can't do symlinks period (AFAIK). To  maintain
		// consistency, we will act like everything is fine. There will simply
		// be no file events for broken symlinks.
		// Hence the returns of nil on errors.
		if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
			path, err := filepath.EvalSymlinks(path)
			if err != nil {
				return nil
			}

			fi, errstat = os.Lstat(path)
			if errstat != nil {
				return nil
			}
		}

		fd, errno := syscall.Open(path, syscall.O_NONBLOCK|syscall.O_RDONLY, 0700)
		if fd == -1 {
			return errno
		}
		watchfd = fd

		w.watches[path] = watchfd
		w.paths[watchfd] = path

		w.finfo[watchfd] = fi
		if fi.IsDir() {
			errdir := w.watchDirectoryFiles(path)
			if errdir != nil {
				return errdir
			}
		}
	}
	syscall.SetKevent(watchEntry, watchfd, syscall.EVFILT_VNODE, syscall.EV_ADD|syscall.EV_CLEAR)

	wd, errno := syscall.Kevent(w.kq, w.kbuf[:], nil, nil)
	if wd == -1 {
		return errno
	} else if (watchEntry.Flags & syscall.EV_ERROR) == syscall.EV_ERROR {
		return errors.New("kevent add error")
	}

	return nil
}

// Watch adds path to the watched file set, watching all events.
func (w *Watcher) watch(path string) error {
	return w.addWatch(path, NOTE_ALLEVENTS)
}

// RemoveWatch removes path from the watched file set.
func (w *Watcher) removeWatch(path string) error {
	watchfd, ok := w.watches[path]
	if !ok {
		return errors.New(fmt.Sprintf("can't remove non-existent kevent watch for: %s", path))
	}
	watchEntry := &w.kbuf[0]
	syscall.SetKevent(watchEntry, w.watches[path], syscall.EVFILT_VNODE, syscall.EV_DELETE)
	success, errno := syscall.Kevent(w.kq, w.kbuf[:], nil, nil)
	if success == -1 {
		return os.NewSyscallError("kevent_rm_watch", errno)
	} else if (watchEntry.Flags & syscall.EV_ERROR) == syscall.EV_ERROR {
		return errors.New("kevent rm error")
	}
	syscall.Close(watchfd)
	delete(w.watches, path)
	return nil
}

// readEvents reads from the kqueue file descriptor, converts the
// received events into Event objects and sends them via the Event channel
func (w *Watcher) readEvents() {
	var (
		eventbuf [10]syscall.Kevent_t // Event buffer
		events   []syscall.Kevent_t   // Received events
		twait    *syscall.Timespec    // Time to block waiting for events
		n        int                  // Number of events returned from kevent
		errno    error                // Syscall errno
	)
	events = eventbuf[0:0]
	twait = new(syscall.Timespec)
	*twait = syscall.NsecToTimespec(keventWaitTime)

	for {
		// See if there is a message on the "done" channel
		var done bool
		select {
		case done = <-w.done:
		default:
		}

		// If "done" message is received
		if done {
			errno := syscall.Close(w.kq)
			if errno != nil {
				w.Error <- os.NewSyscallError("close", errno)
			}
			close(w.internalEvent)
			close(w.Error)
			return
		}

		// Get new events
		if len(events) == 0 {
			n, errno = syscall.Kevent(w.kq, nil, eventbuf[:], twait)

			// EINTR is okay, basically the syscall was interrupted before
			// timeout expired.
			if errno != nil && errno != syscall.EINTR {
				w.Error <- os.NewSyscallError("kevent", errno)
				continue
			}

			// Received some events
			if n > 0 {
				events = eventbuf[0:n]
			}
		}

		// Flush the events we recieved to the events channel
		for len(events) > 0 {
			fileEvent := new(FileEvent)
			watchEvent := &events[0]
			fileEvent.mask = uint32(watchEvent.Fflags)
			fileEvent.Name = w.paths[int(watchEvent.Ident)]

			fileInfo := w.finfo[int(watchEvent.Ident)]
			if fileInfo.IsDir() && fileEvent.IsModify() {
				w.sendDirectoryChangeEvents(fileEvent.Name)
			} else {
				// Send the event on the events channel
				w.internalEvent <- fileEvent
			}

			// Move to next event
			events = events[1:]

			if fileEvent.IsRename() {
				w.removeWatch(fileEvent.Name)
			}
		}
	}
}

func (w *Watcher) watchDirectoryFiles(dirPath string) error {
	// Get all files
	files, err := ioutil.ReadDir(dirPath)
	if err != nil {
		return err
	}

	// Search for new files
	for _, fileInfo := range files {
		if fileInfo.IsDir() == false {
			filePath := filepath.Join(dirPath, fileInfo.Name())
			// Watch file to mimic linux fsnotify
			e := w.addWatch(filePath, NOTE_DELETE|NOTE_WRITE|NOTE_RENAME)
			w.fsnFlags[filePath] = FSN_ALL
			if e != nil {
				return e
			}
		}
	}

	return nil
}

// sendDirectoryEvents searches the directory for newly created files
// and sends them over the event channel. This functionality is to have
// the BSD version of fsnotify mach linux fsnotify which provides a
// create event for files created in a watched directory.
func (w *Watcher) sendDirectoryChangeEvents(dirPath string) {
	// Get all files
	files, err := ioutil.ReadDir(dirPath)
	if err != nil {
		w.Error <- err
	}

	// Search for new files
	for _, fileInfo := range files {
		filePath := filepath.Join(dirPath, fileInfo.Name())
		_, watchFound := w.watches[filePath]
		if watchFound == false {
			w.fsnFlags[filePath] = FSN_ALL
			// Send create event
			fileEvent := new(FileEvent)
			fileEvent.Name = filePath
			fileEvent.create = true
			w.internalEvent <- fileEvent
		}
	}
	w.watchDirectoryFiles(dirPath)
}

const (
	// Flags (from <sys/event.h>)
	NOTE_DELETE = 0x0001 /* vnode was removed */
	NOTE_WRITE  = 0x0002 /* data contents changed */
	NOTE_EXTEND = 0x0004 /* size increased */
	NOTE_ATTRIB = 0x0008 /* attributes changed */
	NOTE_LINK   = 0x0010 /* link count changed */
	NOTE_RENAME = 0x0020 /* vnode was renamed */
	NOTE_REVOKE = 0x0040 /* vnode access was revoked */

	// Watch all events
	NOTE_ALLEVENTS = NOTE_DELETE | NOTE_WRITE | NOTE_ATTRIB | NOTE_RENAME

	// Block for 100 ms on each call to kevent
	keventWaitTime = 100e6
)
<|endoftext|>


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

package generic

import (
	"reflect"
	"sort"
)

// Sort sorts v in increasing order. v must implement sort.Interface
// or must be a slice, array, or pointer to array whose element type
// is orderable.
func Sort(v interface{}) {
	sort.Sort(Sorter(v))
}

// Sorter returns a sort.Interface for sorting v. v must implement
// sort.Interface or must be a slice, array, or pointer to array whose
// element type is orderable.
func Sorter(v interface{}) sort.Interface {
	switch v := v.(type) {
	case []int:
		return sort.IntSlice(v)
	case []float64:
		return sort.Float64Slice(v)
	case []string:
		return sort.StringSlice(v)
	case sort.Interface:
		return v
	}

	rv := sequence(v)
	switch rv.Type().Elem().Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return sortIntSlice{rv}
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return sortUintSlice{rv}
	case reflect.Float32, reflect.Float64:
		return sortFloatSlice{rv}
	case reflect.String:
		return sortStringSlice{rv}
	}
	panic(&TypeError{rv.Type().Elem(), nil, "is not orderable"})
}

type sortIntSlice struct {
	reflect.Value
}

func (s sortIntSlice) Len() int {
	return s.Len()
}

func (s sortIntSlice) Less(i, j int) bool {
	return s.Index(i).Int() < s.Index(j).Int()
}

func (s sortIntSlice) Swap(i, j int) {
	t := s.Index(j)
	s.Index(j).Set(s.Index(i))
	s.Index(i).Set(t)
}

type sortUintSlice struct {
	reflect.Value
}

func (s sortUintSlice) Len() int {
	return s.Len()
}

func (s sortUintSlice) Less(i, j int) bool {
	return s.Index(i).Uint() < s.Index(j).Uint()
}

func (s sortUintSlice) Swap(i, j int) {
	t := s.Index(j)
	s.Index(j).Set(s.Index(i))
	s.Index(i).Set(t)
}

type sortFloatSlice struct {
	reflect.Value
}

func (s sortFloatSlice) Len() int {
	return s.Len()
}

func (s sortFloatSlice) Less(i, j int) bool {
	return s.Index(i).Float() < s.Index(j).Float()
}

func (s sortFloatSlice) Swap(i, j int) {
	t := s.Index(j)
	s.Index(j).Set(s.Index(i))
	s.Index(i).Set(t)
}

type sortStringSlice struct {
	reflect.Value
}

func (s sortStringSlice) Len() int {
	return s.Len()
}

func (s sortStringSlice) Less(i, j int) bool {
	return s.Index(i).String() < s.Index(j).String()
}

func (s sortStringSlice) Swap(i, j int) {
	t := s.Index(j)
	s.Index(j).Set(s.Index(i))
	s.Index(i).Set(t)
}
<commit_msg>generic: fix recursive Len call in specialized sorts<commit_after>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package generic

import (
	"reflect"
	"sort"
)

// Sort sorts v in increasing order. v must implement sort.Interface
// or must be a slice, array, or pointer to array whose element type
// is orderable.
func Sort(v interface{}) {
	sort.Sort(Sorter(v))
}

// Sorter returns a sort.Interface for sorting v. v must implement
// sort.Interface or must be a slice, array, or pointer to array whose
// element type is orderable.
func Sorter(v interface{}) sort.Interface {
	switch v := v.(type) {
	case []int:
		return sort.IntSlice(v)
	case []float64:
		return sort.Float64Slice(v)
	case []string:
		return sort.StringSlice(v)
	case sort.Interface:
		return v
	}

	rv := sequence(v)
	switch rv.Type().Elem().Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return sortIntSlice{rv}
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return sortUintSlice{rv}
	case reflect.Float32, reflect.Float64:
		return sortFloatSlice{rv}
	case reflect.String:
		return sortStringSlice{rv}
	}
	panic(&TypeError{rv.Type().Elem(), nil, "is not orderable"})
}

type sortIntSlice struct {
	reflect.Value
}

func (s sortIntSlice) Len() int {
	return s.Value.Len()
}

func (s sortIntSlice) Less(i, j int) bool {
	return s.Index(i).Int() < s.Index(j).Int()
}

func (s sortIntSlice) Swap(i, j int) {
	t := s.Index(j)
	s.Index(j).Set(s.Index(i))
	s.Index(i).Set(t)
}

type sortUintSlice struct {
	reflect.Value
}

func (s sortUintSlice) Len() int {
	return s.Value.Len()
}

func (s sortUintSlice) Less(i, j int) bool {
	return s.Index(i).Uint() < s.Index(j).Uint()
}

func (s sortUintSlice) Swap(i, j int) {
	t := s.Index(j)
	s.Index(j).Set(s.Index(i))
	s.Index(i).Set(t)
}

type sortFloatSlice struct {
	reflect.Value
}

func (s sortFloatSlice) Len() int {
	return s.Value.Len()
}

func (s sortFloatSlice) Less(i, j int) bool {
	return s.Index(i).Float() < s.Index(j).Float()
}

func (s sortFloatSlice) Swap(i, j int) {
	t := s.Index(j)
	s.Index(j).Set(s.Index(i))
	s.Index(i).Set(t)
}

type sortStringSlice struct {
	reflect.Value
}

func (s sortStringSlice) Len() int {
	return s.Value.Len()
}

func (s sortStringSlice) Less(i, j int) bool {
	return s.Index(i).String() < s.Index(j).String()
}

func (s sortStringSlice) Swap(i, j int) {
	t := s.Index(j)
	s.Index(j).Set(s.Index(i))
	s.Index(i).Set(t)
}
<|endoftext|>


<commit_before>// Package geoJSON converts geoJSON TO s2
// 2016 giulio <giulioungaretti@me.com>
package geoJSON

import (
	"fmt"
	"sort"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/giulioungaretti/geo/s2"
	"github.com/giulioungaretti/globo/point"
)

//Coordinate is a [longitude, latitude]
type Coordinate [2]float64

//Coordinates is a set of coordinate
type Coordinates []Coordinate

// Point rapresent a geoJSON point geometry object
type Point struct {
	Type       string `json:"type"`
	Coordinate `json:"coordinates"`
}

// Polygon rapresent a geoJSON polygon geometry object
type Polygon struct {
	Type string        `json:"type"`
	C    []Coordinates `json:"coordinates"`
}

// MultiPolygon rapresent a geoJSON mulitpolygon  geometry object
type MultiPolygon struct {
	Type string          `json:"type"`
	C    [][]Coordinates `json:"coordinates"`
}

// Prop is a geoJSON property
type Prop map[string]string

// Feature is a geoJSON feature
type Feature struct {
	Type       string      `json:"type"`
	Geometry   interface{} `json:"geometry"`
	Properties Prop        `json:"properties"`
}

//FeatureCollection is a geoJSON Feature colelction
type FeatureCollection struct {
	Type string    `json:"type"`
	Feat []Feature `json:"features"`
}

// geoJSON is the interface that allows any geojson to be unmarshaled
// converted to s2, and marhsaled back to be visualized
type geoJSON interface {
	ToS2(int) ([][]uint64, []s2.Loop, error)
	ToGeoJSON(int) (FeatureCollection, error)
}

func (c Coordinate) tos2point() s2.Point {
	ll := s2.LatLngFromDegrees(c[1], c[0])
	p := s2.PointFromLatLng(ll)
	return p
}

// IsValid vaildates coordinates (approximate)
func (c Coordinate) IsValid() bool {
	if c[0] == 0 || c[1] == 0 {
		return false
	}
	return true
}

// tos2 transforms Coordinates to a s2 loop and
// checks if the loop is CCW.
func (cc Coordinates) tos2() (s2.Loop, error) {
	pts := []s2.Point{}
	for _, c := range cc {
		p := c.tos2point()
		pts = append(pts, p)
	}
	//origin := s2.OriginPoint()
	//for i := range pts {
	//j := 1 + i
	//k := 2 + i
	//if i == len(pts)-2 {
	//k = 0
	//}
	//if i == len(pts)-1 {
	//j = 0
	//k = 1
	//}
	//if !s2.OrderedCCW(pts[i], pts[j], pts[k], origin) {
	//err := fmt.Errorf("Polygon not ordered")
	//log.Errorf("%v,%v,%v, %v", i, j, k, err)
	////return *s2.LoopFromPoints(pts), err
	//}
	//}
	return *s2.LoopFromPoints(pts), nil
}

// ToS2 converts a Point to S2Cellid.
// If precision is different than 30 (max) then the
// parent cellid at specified level is returned.
// levels go from 0 to 30:
// for reference :
// 30 covers 0.48cm2
// 12 covers 3.31km2
// 0 covers 85,011,012 km2
func (p Point) ToS2(precision int) (ids [][]uint64, loops []s2.Loop, err error) {
	c := p.Coordinate
	ll := s2.LatLngFromDegrees(c[1], c[0])
	cellID := s2.CellIDFromLatLng(ll)
	// approximate returns
	if precision != 30 {
		log.Debug("Rounding down")
		return [][]uint64{[]uint64{uint64(cellID.Parent(precision))}}, loops, nil
	}
	return [][]uint64{[]uint64{uint64(cellID)}}, loops, nil
}

// ToGeoJSON converts point to geoJSON
func (p Point) ToGeoJSON(precision int) (ff FeatureCollection, err error) {
	// ingore loops, as a point has obviously no loops
	in, _, err := p.ToS2(precision)
	if err != nil {
		return
	}
	ff = FeatureCollection{}
	ff.Type = "FeatureCollection"
	var features []Feature
	for _, id := range in[0] {
		// add center point
		feature := cellIDToCenterPoint(id)
		features = append(features, feature)
		// add bounding box
		feature = cellIDToPolygon(id)
		features = append(features, feature)
	}
	ff.Feat = features
	return
}

// innerLoop(s) return an s2.loop representation of the inner loop of the
// geoJSON polygon or multi polygon. We do not support holes in polygons, so the inner ring of the geojson is discarded.
// At this point winding order of the loops is not specified but it **must** be
// counterclockwise, else we return an error.

func (p Polygon) innerLoops() (loops []s2.Loop, err error) {
	cds := p.C[0]
	loop, err := cds.tos2()
	loops = append(loops, loop)
	return loops, err
}

func (pp MultiPolygon) innerLoops() ([]s2.Loop, error) {
	var loops []s2.Loop
	var err error
	for _, c := range pp.C {
		cds := c[0]
		loop, err := cds.tos2()
		if err != nil {
			return loops, err
		}
		loops = append(loops, loop)
	}
	return loops, err
}

// Contains check if the polygon contains the  point
// TODO needs polish
func (p Polygon) Contains(point point.Point) bool {
	// TODO this has a bug
	// maybe is the point to s2 conversion
	var contains bool
	ll, cell := point.ToCell()
	loop, err := p.innerLoops()
	if err != nil {
		log.Error(err)
		return false
	}

	contains = loop[0].RectBound().ContainsCell(cell)
	if !contains {
		return false
	}
	contains = loop[0].ContainsPoint(s2.PointFromLatLng(ll))
	return contains
}

func cellIDToPolygon(id uint64) (f Feature) {
	cellid := s2.CellID(id)
	cell := s2.CellFromCellID(cellid)
	rect := cell.RectBound()
	var coordinates Coordinates
	var coordinates2 []Coordinates
	for i := 0; i < 4; i++ {
		ll := rect.Vertex(i)
		// ll is the vertex of the  cellid
		var ld Coordinate
		ld = [2]float64{ll.Lng.Degrees(), ll.Lat.Degrees()}
		coordinates = append(coordinates, ld)
	}
	// add first point as last point to close polygon
	coordinates = append(coordinates, coordinates[0])
	coordinates2 = append(coordinates2, coordinates)
	polygon := Polygon{}
	polygon.Type = "Polygon"
	polygon.C = coordinates2
	f.Type = "Feature"
	prop := make(Prop)
	prop["cellid"] = fmt.Sprintf("%v", cellid)
	f.Properties = prop
	f.Geometry = polygon
	return f
}

func cellIDToCenterPoint(id uint64) (f Feature) {
	cellid := s2.CellID(id)
	// ll is the center of the  cellid
	ll := cellid.LatLng()
	var ld Coordinate
	ld = [2]float64{ll.Lng.Degrees(), ll.Lat.Degrees()}
	point := Point{}
	point.Type = "Point"
	point.Coordinate = ld
	f.Type = "Feature"
	prop := make(Prop)
	prop["is_center"] = "true"
	f.Properties = prop
	f.Geometry = point
	return f
}

func boundingbox(loop s2.Loop) (f Feature) {
	rect := loop.RectBound()
	var coordinates Coordinates
	var coordinates2 []Coordinates
	for i := 0; i < 4; i++ {
		ll := rect.Vertex(i)
		// ll is the vertex of the  cellid
		var ld Coordinate
		ld = [2]float64{ll.Lng.Degrees(), ll.Lat.Degrees()}
		coordinates = append(coordinates, ld)
	}
	// add last vertex
	coordinates = append(coordinates, coordinates[0])
	coordinates2 = append(coordinates2, coordinates)
	polygon := Polygon{}
	polygon.Type = "Polygon"
	polygon.C = coordinates2
	f = Feature{}
	f.Type = "Feature"
	prop := make(Prop)
	prop["boundingbox"] = "true"
	f.Properties = prop
	f.Geometry = polygon
	return
}

// transforms a loop to a sorted cellID collection
func loopCoverer(loop s2.Loop, precision int) []uint64 {
	debug := false
	// now for bounds
	rc := &s2.RegionCoverer{MinLevel: 0, MaxLevel: precision, MaxCells: 100}
	r := s2.Region(loop.RectBound())
	covering := rc.Covering(r)
	// now approximate the bounding rect
	var polygon []uint64
	for _, val := range covering {
		polygon = append(polygon, uint64(val))
	}
	// NOTE ++ TODO
	// is this 100% correct ?
	// in theory it is.
	sort.Sort(intArray(polygon))
	// NOTE this will be the actual polygonsponse
	// when I/google finish updating the s2 library
	// right now the region coverer for loops
	// is buggy in the sense that it reutns
	// just the boundary of the loop.
	// by setting debug to true, one can
	// visualize the json
	if debug {
		min := polygon[0]
		max := polygon[len(polygon)-1]
		rc := &s2.RegionCoverer{MinLevel: 15, MaxLevel: 30, MaxCells: 50}
		covering := rc.Covering(s2.Region(loop))
		var boundary []uint64
		// now approximate the polygon
		for _, val := range covering {
			boundary = append(boundary, uint64(val))
		}
		for _, val := range boundary {
			if val > max {
				log.Printf("strange %v", val)
			}
			if val < min {
				log.Printf("strange %v", val)
			}
		}
		return boundary
	}
	return polygon
}

// ToS2 converts a geoJSON polygon to a set of cellUnions
func (p Polygon) ToS2(precision int) (ids [][]uint64, loops []s2.Loop, err error) {
	var polygons [][]uint64
	loops, err = p.innerLoops()
	if err != nil {
		return polygons, loops, err
	}
	polygon := loopCoverer(loops[0], precision)
	polygons = append(polygons, polygon)
	return polygons, loops, err
}

// ToS2 converts a geoJSON multi polygon to a set of cellUnions
func (pp MultiPolygon) ToS2(precision int) (ids [][]uint64, loops []s2.Loop, err error) {
	var polygons [][]uint64
	loops, err = pp.innerLoops()
	if err != nil {
		return polygons, loops, err
	}
	for _, loop := range loops {
		polygon := loopCoverer(loop, precision)
		polygons = append(polygons, polygon)
	}
	return polygons, loops, err
}

// ToGeoJSON converts polygon to geoJSON
func (p Polygon) ToGeoJSON(precision int) (ff FeatureCollection, err error) {
	in, loops, err := p.ToS2(precision)
	if err != nil {
		return
	}
	// NOTE  we expect one polygon/ one loop
	polygon := in[0]
	inner := loops[0]
	ff = FeatureCollection{}
	ff.Type = "FeatureCollection"
	var features []Feature
	// add bbox
	for _, id := range polygon {
		feature := cellIDToPolygon(id)
		features = append(features, feature)
	}
	bbox := boundingbox(inner)
	features = append(features, bbox)
	ff.Feat = features
	return
}

// ToGeoJSON converts back a multipolygon to geoJSON
func (pp MultiPolygon) ToGeoJSON(precision int) (ff FeatureCollection, err error) {
	in, loops, err := pp.ToS2(precision)
	if err != nil {
		return
	}
	ff = FeatureCollection{}
	ff.Type = "FeatureCollection"
	var features []Feature
	for _, polygon := range in {
		for _, id := range polygon {
			feature := cellIDToPolygon(id)
			features = append(features, feature)
		}
	}
	// add bbox
	for _, inner := range loops {
		bbox := boundingbox(inner)
		features = append(features, bbox)
	}
	ff.Feat = features
	return
}

// HELPERS
// sortable array
type intArray []uint64

func (s intArray) Len() int           { return len(s) }
func (s intArray) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s intArray) Less(i, j int) bool { return s[i] < s[j] }

// convert to string token
func toToken(ci uint64) string {
	s := strings.TrimRight(fmt.Sprintf("%016x", uint64(ci)), "0")
	if len(s) == 0 {
		return "X"
	}
	return s
}
<commit_msg>Save polygon id as well.<commit_after>// Package geoJSON converts geoJSON TO s2
// 2016 giulio <giulioungaretti@me.com>
package geoJSON

import (
	"fmt"
	"sort"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/giulioungaretti/geo/s2"
	"github.com/giulioungaretti/globo/point"
)

//Coordinate is a [longitude, latitude]
type Coordinate [2]float64

//Coordinates is a set of coordinate
type Coordinates []Coordinate

// Point rapresent a geoJSON point geometry object
type Point struct {
	Type       string `json:"type"`
	Coordinate `json:"coordinates"`
}

// Polygon rapresent a geoJSON polygon geometry object
type Polygon struct {
	Type string        `json:"type"`
	C    []Coordinates `json:"coordinates"`
}

// MultiPolygon rapresent a geoJSON mulitpolygon  geometry object
type MultiPolygon struct {
	Type string          `json:"type"`
	C    [][]Coordinates `json:"coordinates"`
}

// Prop is a geoJSON property
type Prop map[string]string

// Feature is a geoJSON feature
type Feature struct {
	Type       string      `json:"type"`
	Geometry   interface{} `json:"geometry"`
	Properties Prop        `json:"properties"`
}

//FeatureCollection is a geoJSON Feature colelction
type FeatureCollection struct {
	Type string    `json:"type"`
	Feat []Feature `json:"features"`
}

// geoJSON is the interface that allows any geojson to be unmarshaled
// converted to s2, and marhsaled back to be visualized
type geoJSON interface {
	ToS2(int) ([][]uint64, []s2.Loop, error)
	ToGeoJSON(int) (FeatureCollection, error)
}

func (c Coordinate) tos2point() s2.Point {
	ll := s2.LatLngFromDegrees(c[1], c[0])
	p := s2.PointFromLatLng(ll)
	return p
}

// IsValid vaildates coordinates (approximate)
func (c Coordinate) IsValid() bool {
	if c[0] == 0 || c[1] == 0 {
		return false
	}
	return true
}

// tos2 transforms Coordinates to a s2 loop and
// checks if the loop is CCW.
func (cc Coordinates) tos2() (s2.Loop, error) {
	pts := []s2.Point{}
	for _, c := range cc {
		p := c.tos2point()
		pts = append(pts, p)
	}
	//origin := s2.OriginPoint()
	//for i := range pts {
	//j := 1 + i
	//k := 2 + i
	//if i == len(pts)-2 {
	//k = 0
	//}
	//if i == len(pts)-1 {
	//j = 0
	//k = 1
	//}
	//if !s2.OrderedCCW(pts[i], pts[j], pts[k], origin) {
	//err := fmt.Errorf("Polygon not ordered")
	//log.Errorf("%v,%v,%v, %v", i, j, k, err)
	////return *s2.LoopFromPoints(pts), err
	//}
	//}
	return *s2.LoopFromPoints(pts), nil
}

// ToS2 converts a Point to S2Cellid.
// If precision is different than 30 (max) then the
// parent cellid at specified level is returned.
// levels go from 0 to 30:
// for reference :
// 30 covers 0.48cm2
// 12 covers 3.31km2
// 0 covers 85,011,012 km2
func (p Point) ToS2(precision int) (ids [][]uint64, loops []s2.Loop, err error) {
	c := p.Coordinate
	ll := s2.LatLngFromDegrees(c[1], c[0])
	cellID := s2.CellIDFromLatLng(ll)
	// approximate returns
	if precision != 30 {
		log.Debug("Rounding down")
		return [][]uint64{[]uint64{uint64(cellID.Parent(precision))}}, loops, nil
	}
	return [][]uint64{[]uint64{uint64(cellID)}}, loops, nil
}

// ToGeoJSON converts point to geoJSON
func (p Point) ToGeoJSON(precision int) (ff FeatureCollection, err error) {
	// ingore loops, as a point has obviously no loops
	in, _, err := p.ToS2(precision)
	if err != nil {
		return
	}
	ff = FeatureCollection{}
	ff.Type = "FeatureCollection"
	var features []Feature
	for _, id := range in[0] {
		// add center point
		feature := cellIDToCenterPoint(id)
		features = append(features, feature)
		// add bounding box
		feature = cellIDToPolygon(id)
		features = append(features, feature)
	}
	ff.Feat = features
	return
}

// innerLoop(s) return an s2.loop representation of the inner loop of the
// geoJSON polygon or multi polygon. We do not support holes in polygons, so the inner ring of the geojson is discarded.
// At this point winding order of the loops is not specified but it **must** be
// counterclockwise, else we return an error.

func (p Polygon) innerLoops() (loops []s2.Loop, err error) {
	cds := p.C[0]
	loop, err := cds.tos2()
	loops = append(loops, loop)
	return loops, err
}

func (pp MultiPolygon) innerLoops() ([]s2.Loop, error) {
	var loops []s2.Loop
	var err error
	for _, c := range pp.C {
		cds := c[0]
		loop, err := cds.tos2()
		if err != nil {
			return loops, err
		}
		loops = append(loops, loop)
	}
	return loops, err
}

// Contains check if the polygon contains the  point
// TODO needs polish
func (p Polygon) Contains(point point.Point) bool {
	// TODO this has a bug
	// maybe is the point to s2 conversion
	var contains bool
	ll, cell := point.ToCell()
	loop, err := p.innerLoops()
	if err != nil {
		log.Error(err)
		return false
	}

	contains = loop[0].RectBound().ContainsCell(cell)
	if !contains {
		return false
	}
	contains = loop[0].ContainsPoint(s2.PointFromLatLng(ll))
	return contains
}

func cellIDToPolygon(id uint64) (f Feature) {
	cellid := s2.CellID(id)
	cell := s2.CellFromCellID(cellid)
	rect := cell.RectBound()
	var coordinates Coordinates
	var coordinates2 []Coordinates
	for i := 0; i < 4; i++ {
		ll := rect.Vertex(i)
		// ll is the vertex of the  cellid
		var ld Coordinate
		ld = [2]float64{ll.Lng.Degrees(), ll.Lat.Degrees()}
		coordinates = append(coordinates, ld)
	}
	// add first point as last point to close polygon
	coordinates = append(coordinates, coordinates[0])
	coordinates2 = append(coordinates2, coordinates)
	polygon := Polygon{}
	polygon.Type = "Polygon"
	polygon.C = coordinates2
	f.Type = "Feature"
	prop := make(Prop)
	prop["cellid"] = fmt.Sprintf("%v", cellid)
	f.Properties = prop
	f.Geometry = polygon
	return f
}

func cellIDToCenterPoint(id uint64) (f Feature) {
	cellid := s2.CellID(id)
	// ll is the center of the  cellid
	ll := cellid.LatLng()
	var ld Coordinate
	ld = [2]float64{ll.Lng.Degrees(), ll.Lat.Degrees()}
	point := Point{}
	point.Type = "Point"
	point.Coordinate = ld
	f.Type = "Feature"
	prop := make(Prop)
	prop["is_center"] = "true"
	f.Properties = prop
	f.Geometry = point
	return f
}

func boundingbox(loop s2.Loop, id int) (f Feature) {
	rect := loop.RectBound()
	var coordinates Coordinates
	var coordinates2 []Coordinates
	for i := 0; i < 4; i++ {
		ll := rect.Vertex(i)
		// ll is the vertex of the  cellid
		var ld Coordinate
		ld = [2]float64{ll.Lng.Degrees(), ll.Lat.Degrees()}
		coordinates = append(coordinates, ld)
	}
	// add last vertex
	coordinates = append(coordinates, coordinates[0])
	coordinates2 = append(coordinates2, coordinates)
	polygon := Polygon{}
	polygon.Type = "Polygon"
	polygon.C = coordinates2
	f = Feature{}
	f.Type = "Feature"
	prop := make(Prop)
	prop["boundingbox"] = fmt.Sprint(id)
	f.Properties = prop
	f.Geometry = polygon
	return
}

// transforms a loop to a sorted cellID collection
func loopCoverer(loop s2.Loop, precision int) []uint64 {
	debug := false
	// now for bounds
	rc := &s2.RegionCoverer{MinLevel: 0, MaxLevel: precision, MaxCells: 100}
	r := s2.Region(loop.RectBound())
	covering := rc.Covering(r)
	// now approximate the bounding rect
	var polygon []uint64
	for _, val := range covering {
		polygon = append(polygon, uint64(val))
	}
	// NOTE ++ TODO
	// is this 100% correct ?
	// in theory it is.
	sort.Sort(intArray(polygon))
	// NOTE this will be the actual polygonsponse
	// when I/google finish updating the s2 library
	// right now the region coverer for loops
	// is buggy in the sense that it reutns
	// just the boundary of the loop.
	// by setting debug to true, one can
	// visualize the json
	if debug {
		min := polygon[0]
		max := polygon[len(polygon)-1]
		rc := &s2.RegionCoverer{MinLevel: 15, MaxLevel: 30, MaxCells: 50}
		covering := rc.Covering(s2.Region(loop))
		var boundary []uint64
		// now approximate the polygon
		for _, val := range covering {
			boundary = append(boundary, uint64(val))
		}
		for _, val := range boundary {
			if val > max {
				log.Printf("strange %v", val)
			}
			if val < min {
				log.Printf("strange %v", val)
			}
		}
		return boundary
	}
	return polygon
}

// ToS2 converts a geoJSON polygon to a set of cellUnions
func (p Polygon) ToS2(precision int) (ids [][]uint64, loops []s2.Loop, err error) {
	var polygons [][]uint64
	loops, err = p.innerLoops()
	if err != nil {
		return polygons, loops, err
	}
	polygon := loopCoverer(loops[0], precision)
	polygons = append(polygons, polygon)
	return polygons, loops, err
}

// ToS2 converts a geoJSON multi polygon to a set of cellUnions
func (pp MultiPolygon) ToS2(precision int) (ids [][]uint64, loops []s2.Loop, err error) {
	var polygons [][]uint64
	loops, err = pp.innerLoops()
	if err != nil {
		return polygons, loops, err
	}
	for _, loop := range loops {
		polygon := loopCoverer(loop, precision)
		polygons = append(polygons, polygon)
	}
	return polygons, loops, err
}

// ToGeoJSON converts polygon to geoJSON
func (p Polygon) ToGeoJSON(precision int) (ff FeatureCollection, err error) {
	in, loops, err := p.ToS2(precision)
	if err != nil {
		return
	}
	// NOTE  we expect one polygon/ one loop
	polygon := in[0]
	inner := loops[0]
	ff = FeatureCollection{}
	ff.Type = "FeatureCollection"
	var features []Feature
	// add bbox
	for _, id := range polygon {
		feature := cellIDToPolygon(id)
		features = append(features, feature)
	}
	bbox := boundingbox(inner, 0)
	features = append(features, bbox)
	ff.Feat = features
	return
}

// ToGeoJSON converts back a multipolygon to geoJSON
func (pp MultiPolygon) ToGeoJSON(precision int) (ff FeatureCollection, err error) {
	in, loops, err := pp.ToS2(precision)
	if err != nil {
		return
	}
	ff = FeatureCollection{}
	ff.Type = "FeatureCollection"
	var features []Feature
	for _, polygon := range in {
		for _, id := range polygon {
			feature := cellIDToPolygon(id)
			features = append(features, feature)
		}
	}
	// add bbox
	for i, inner := range loops {
		bbox := boundingbox(inner, i)
		features = append(features, bbox)
	}
	ff.Feat = features
	return
}

// HELPERS
// sortable array
type intArray []uint64

func (s intArray) Len() int           { return len(s) }
func (s intArray) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s intArray) Less(i, j int) bool { return s[i] < s[j] }

// convert to string token
func toToken(ci uint64) string {
	s := strings.TrimRight(fmt.Sprintf("%016x", uint64(ci)), "0")
	if len(s) == 0 {
		return "X"
	}
	return s
}
<|endoftext|>