<commit_before>// Library rx provides a Just-in-time Generics (jig) implementation of Reactive eXtensions (Rx).
// It's a generics library for composing asynchronous and event-based programs
// using observable sequences. The library consists of more than a 100 templates
// to enable type-safe programming with observable streams. To use it, you will
// need the jig tool https://github.com/reactivego/jig.
//
// Using the library is very simple. Import the library with the blank
// identifier `_` as the package name:
//	import _ "github.com/reactivego/rx"
// The side effect of this import is that
// generics from the library can now be accessed by the jig tool. Then start
// using generics from the library and run jig to generate code. Take a look at
// the Quick Start guide to see how it all fits together
// https://github.com/reactivego/rx/doc/quickstart.md.
//
// For an overview of all implemented operators, see
// https://github.com/reactivego/rx
package rx

// foo is the first metasyntactic type. Use the jig:type pragma to tell jig that
// Foo is the reference type name for actual type foo. Needed because we're
// generating code into rx.go for foo.

//jig:type Foo foo

type foo int

// bar is the second metasyntactic type. Use the jig:type pragma to tell jig that
// Bar is the reference type name for actual type bar. Needed because we're
// generating code into rx.go for bar.

//jig:type Bar bar

type bar int
<commit_msg>Fix url to QuickStart.<commit_after>// Library rx provides a Just-in-time Generics (jig) implementation of Reactive eXtensions (Rx).
// It's a generics library for composing asynchronous and event-based programs
// using observable sequences. The library consists of more than a 100 templates
// to enable type-safe programming with observable streams. To use it, you will
// need the jig tool https://github.com/reactivego/jig.
//
// Using the library is very simple. Import the library with the blank
// identifier `_` as the package name:
//	import _ "github.com/reactivego/rx"
// The side effect of this import is that
// generics from the library can now be accessed by the jig tool. Then start
// using generics from the library and run jig to generate code. Take a look at
// the Quick Start guide to see how it all fits together
// https://github.com/ReactiveGo/rx/blob/master/doc/QUICKSTART.md.
//
// For an overview of all implemented operators, see
// https://github.com/reactivego/rx
package rx

// foo is the first metasyntactic type. Use the jig:type pragma to tell jig that
// Foo is the reference type name for actual type foo. Needed because we're
// generating code into rx.go for foo.

//jig:type Foo foo

type foo int

// bar is the second metasyntactic type. Use the jig:type pragma to tell jig that
// Bar is the reference type name for actual type bar. Needed because we're
// generating code into rx.go for bar.

//jig:type Bar bar

type bar int
<|endoftext|>


<commit_before>package gol
<commit_msg>update doc<commit_after>// Package gol is a high performance go log tool
//
//
package gol
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Examples contains sample applications for trying out the concepts in Kubernetes.
package examples
<commit_msg>Use Go canonical import paths<commit_after>/*
Copyright 2014 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Examples contains sample applications for trying out the concepts in Kubernetes.
package examples // import "k8s.io/kubernetes/examples"
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013 Kurt Jung (Gmail: kurt.w.jung)
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
Package gofpdf implements a PDF document generator.

This package's code and documentation are closely derived from the FPDF library
created by Olivier Plathey, and a number of font and image resources are copied
directly from it. Drawing support is adapted from the FPDF geometric figures
script by David Hernández Sanz. Transparency support is adapted from the FPDF
transparency script by Martin Hall-May. Support for gradients and clipping is
adapted from FPDF scripts by Andreas Würmser. Support for outline bookmarks is
adapted from Olivier Plathey by Manuel Cornes.

The FPDF website is http://www.fpdf.org/.

Features

• Choice of measurement unit, page format and margins

• Page header and footer management

• Automatic page break

• Automatic line break and text justification

• Image support (JPEG, PNG and GIF)

• Colors, gradients and alpha channel transparency

• Internal and external links

• TrueType, Type1 and encoding support

• Page compression

• Drawing support (lines, Bézier curves, arcs, ellipses)

• Clipping

gofpdf has no dependencies other than the Go standard library. All tests pass
on Linux, Mac and Windows platforms. Like FPDF version 1.7, from which gofpdf
is derived, this package does not yet support UTF-8 source text. gofpdf is
copyrighted by Kurt Jung and is released under the MIT License.

Installation

To install the package on your system, run

	go get code.google.com/p/gofpdf

Later, to receive updates, run

	go get -u code.google.com/p/gofpdf

Quick Start

The following Go code generates a simple PDF.

	pdf := gofpdf.New("P", "mm", "A4", "../font")
	pdf.AddPage()
	pdf.SetFont("Arial", "B", 16)
	pdf.Cell(40, 10, "Hello, world")
	pdf.Output(os.Stdout)

See the tutorials in the fpdf_test.go file (shown as examples in this
documentation) for more advanced PDF examples.

Errors

If an error occurs in an Fpdf method, an internal error field is set. After
this occurs, Fpdf method calls typically return without performing any
operations and the error state is retained. This error management scheme
facilitates PDF generation since individual method calls do not need to be
examined for failure; it is generally sufficient to wait until after Output()
is called. For the same reason, if an error occurs in the calling application
during PDF generation, it may be desirable for the application to transfer the
error to the Fpdf instance by calling the SetError() method or the SetErrorf()
method. At any time during the life cycle of the Fpdf instance, the error state
can be determined with a call to Ok() or Err(). The error itself can be
retrieved with a call to Error().

Conversion Notes

This package is a relatively straightforward translation from the original FPDF
library written in PHP (despite the caveat in the introduction to Effective
Go). The API names have been retained even though the Go idiom would suggest
otherwise (for example, pdf.GetX() is used rather than simply pdf.X()). The
similarity of the two libraries makes the original FPDF website a good source
of information. It includes a forum and FAQ.

However, some internal changes have been made. Page content is built up using
buffers (of type bytes.Buffer) rather than repeated string concatenation.
Errors are handled as explained above rather than panicking. Output is
generated through an interface of type io.Writer or io.WriteCloser. A number of
the original PHP methods behave differently based on the type of the arguments
that are passed to them; in these cases additional methods have been exported
to provide similar functionality. Font definition files are produced in JSON
rather than PHP.

Tutorials

A side effect of running "go test" is the production of the tutorial PDFs.
These can be found in the gofpdf/pdf directory after the tests complete.

Nonstandard Fonts

Nothing special is required to use the standard PDF fonts (courier, helvetica,
times, zapfdingbats) in your documents other than calling SetFont().

In order to use a different TrueType or Type1 font, you will need to generate a
font definition file and, if the font will be embedded into PDFs, a compressed
version of the font file. This is done by calling the MakeFont function or
using the included makefont command line utility. To create the utility, cd
into the makefont subdirectory and run "go build". This will produce a
standalone executable named makefont. Select the appropriate encoding file from
the font subdirectory and run the command as in the following example.

	./makefont --embed --enc=../font/cp1252.map --dst=../font ../font/calligra.ttf

In your PDF generation code, call AddFont() to load the font and, as with the
standard fonts, SetFont() to begin using it. See tutorial 7 for an example.
Good sources of free, open-source fonts include http://www.google.com/fonts/
and http://dejavu-fonts.org/.

Roadmap

• Handle UTF-8 source text

• Improve test coverage as reported by gocov (https://github.com/axw/gocov)

*/
package gofpdf
<commit_msg>Add outline to feature list<commit_after>/*
 * Copyright (c) 2013 Kurt Jung (Gmail: kurt.w.jung)
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
Package gofpdf implements a PDF document generator.

This package's code and documentation are closely derived from the FPDF library
created by Olivier Plathey, and a number of font and image resources are copied
directly from it. Drawing support is adapted from the FPDF geometric figures
script by David Hernández Sanz. Transparency support is adapted from the FPDF
transparency script by Martin Hall-May. Support for gradients and clipping is
adapted from FPDF scripts by Andreas Würmser. Support for outline bookmarks is
adapted from Olivier Plathey by Manuel Cornes.

The FPDF website is http://www.fpdf.org/.

Features

• Choice of measurement unit, page format and margins

• Page header and footer management

• Automatic page break

• Automatic line break and text justification

• Image support (JPEG, PNG and GIF)

• Colors, gradients and alpha channel transparency

• Outline bookmarks

• Internal and external links

• TrueType, Type1 and encoding support

• Page compression

• Drawing support (lines, Bézier curves, arcs, ellipses)

• Clipping

gofpdf has no dependencies other than the Go standard library. All tests pass
on Linux, Mac and Windows platforms. Like FPDF version 1.7, from which gofpdf
is derived, this package does not yet support UTF-8 source text. gofpdf is
copyrighted by Kurt Jung and is released under the MIT License.

Installation

To install the package on your system, run

	go get code.google.com/p/gofpdf

Later, to receive updates, run

	go get -u code.google.com/p/gofpdf

Quick Start

The following Go code generates a simple PDF.

	pdf := gofpdf.New("P", "mm", "A4", "../font")
	pdf.AddPage()
	pdf.SetFont("Arial", "B", 16)
	pdf.Cell(40, 10, "Hello, world")
	pdf.Output(os.Stdout)

See the tutorials in the fpdf_test.go file (shown as examples in this
documentation) for more advanced PDF examples.

Errors

If an error occurs in an Fpdf method, an internal error field is set. After
this occurs, Fpdf method calls typically return without performing any
operations and the error state is retained. This error management scheme
facilitates PDF generation since individual method calls do not need to be
examined for failure; it is generally sufficient to wait until after Output()
is called. For the same reason, if an error occurs in the calling application
during PDF generation, it may be desirable for the application to transfer the
error to the Fpdf instance by calling the SetError() method or the SetErrorf()
method. At any time during the life cycle of the Fpdf instance, the error state
can be determined with a call to Ok() or Err(). The error itself can be
retrieved with a call to Error().

Conversion Notes

This package is a relatively straightforward translation from the original FPDF
library written in PHP (despite the caveat in the introduction to Effective
Go). The API names have been retained even though the Go idiom would suggest
otherwise (for example, pdf.GetX() is used rather than simply pdf.X()). The
similarity of the two libraries makes the original FPDF website a good source
of information. It includes a forum and FAQ.

However, some internal changes have been made. Page content is built up using
buffers (of type bytes.Buffer) rather than repeated string concatenation.
Errors are handled as explained above rather than panicking. Output is
generated through an interface of type io.Writer or io.WriteCloser. A number of
the original PHP methods behave differently based on the type of the arguments
that are passed to them; in these cases additional methods have been exported
to provide similar functionality. Font definition files are produced in JSON
rather than PHP.

Tutorials

A side effect of running "go test" is the production of the tutorial PDFs.
These can be found in the gofpdf/pdf directory after the tests complete.

Nonstandard Fonts

Nothing special is required to use the standard PDF fonts (courier, helvetica,
times, zapfdingbats) in your documents other than calling SetFont().

In order to use a different TrueType or Type1 font, you will need to generate a
font definition file and, if the font will be embedded into PDFs, a compressed
version of the font file. This is done by calling the MakeFont function or
using the included makefont command line utility. To create the utility, cd
into the makefont subdirectory and run "go build". This will produce a
standalone executable named makefont. Select the appropriate encoding file from
the font subdirectory and run the command as in the following example.

	./makefont --embed --enc=../font/cp1252.map --dst=../font ../font/calligra.ttf

In your PDF generation code, call AddFont() to load the font and, as with the
standard fonts, SetFont() to begin using it. See tutorial 7 for an example.
Good sources of free, open-source fonts include http://www.google.com/fonts/
and http://dejavu-fonts.org/.

Roadmap

• Handle UTF-8 source text

• Improve test coverage as reported by gocov (https://github.com/axw/gocov)

*/
package gofpdf
<|endoftext|>


<commit_before>package goftp

import (
	"bufio"
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"net"
	"os"
	"regexp"
	"strconv"
	"strings"
)

var REGEX_PWD_PATH *regexp.Regexp = regexp.MustCompile(`\"(.*)\"`)

type FTP struct {
	conn net.Conn

	addr string

	debug     bool
	tlsconfig *tls.Config

	reader *bufio.Reader
	writer *bufio.Writer
}

func (ftp *FTP) Close() {
	ftp.conn.Close()
}

type WalkFunc func(path string, info os.FileMode, err error) error
type RetrFunc func(r io.Reader) error

func parseLine(line string) (perm string, t string, filename string) {
	for _, v := range strings.Split(line, ";") {
		v2 := strings.Split(v, "=")

		switch v2[0] {
		case "perm":
			perm = v2[1]
		case "type":
			t = v2[1]
		default:
			filename = v[1 : len(v)-2]
		}
	}

	return
}

// walks recursively through path and call walkfunc for each file
func (ftp *FTP) Walk(path string, walkFn WalkFunc) (err error) {
	/*
		if err = walkFn(path, os.ModeDir, nil); err != nil {
			if err == filepath.SkipDir {
				return nil
			}
		}
	*/
	if ftp.debug {
		fmt.Printf("Walking: '%s'\n", path)
	}

	var lines []string

	if lines, err = ftp.List(path); err != nil {
		return
	}

	for _, line := range lines {
		_, t, subpath := parseLine(line)

		switch t {
		case "dir":
			if subpath == "." {
			} else if subpath == ".." {
			} else {
				if err = ftp.Walk(path+subpath+"/", walkFn); err != nil {
					return
				}
			}
		case "file":
			if err = walkFn(path+subpath, os.FileMode(0), nil); err != nil {
				return
			}
		default:
		}
	}

	return
}

func (ftp *FTP) Quit() (err error) {
	if _, err := ftp.cmd("221", "QUIT"); err != nil {
		return err
	}

	ftp.conn.Close()
	ftp.conn = nil

	return nil
}

// will send a NOOP (no operation) to the server
func (ftp *FTP) Noop() (err error) {
	_, err = ftp.cmd("200", "NOOP")
	return
}

// private function to send command and compare return code with expects
func (ftp *FTP) cmd(expects string, command string, args ...interface{}) (line string, err error) {
	if err = ftp.send(command, args...); err != nil {
		return
	}

	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, expects) {
		err = errors.New(line)
		return
	}

	return
}

func (ftp *FTP) Rename(from string, to string) (err error) {
	if _, err = ftp.cmd("350", "RNFR %s", from); err != nil {
		return
	}

	if _, err = ftp.cmd("250", "RNTO %s", to); err != nil {
		return
	}

	return
}
func (ftp *FTP) Mkd(path string) (err error) {
	_, err = ftp.cmd("257", "MKD %s", path)
	return
}

func (ftp *FTP) Pwd() (path string, err error) {
	var line string
	if line, err = ftp.cmd("257", "PWD"); err != nil {
		return
	}

	res := REGEX_PWD_PATH.FindAllStringSubmatch(line[4:], -1)

	path = res[0][1]
	return
}

func (ftp *FTP) Cwd(path string) (err error) {
	_, err = ftp.cmd("250", "CWD %s", path)
	return
}

func (ftp *FTP) Dele(path string) (err error) {
	if err = ftp.send("DELE %s", path); err != nil {
		return
	}

	var line string
	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "250") {
		return errors.New(line)
	}

	return
}

func (ftp *FTP) AuthTLS(config tls.Config) (err error) {
	if _, err = ftp.cmd("234", "AUTH TLS"); err != nil {
		return
	}

	// wrap tls on existing connection
	ftp.tlsconfig = &config

	ftp.conn = tls.Client(ftp.conn, &config)
	ftp.writer = bufio.NewWriter(ftp.conn)
	ftp.reader = bufio.NewReader(ftp.conn)

	if _, err = ftp.cmd("200", "PROT P"); err != nil {
		return
	}

	return
}

func (ftp *FTP) Type(t string) (err error) {
	_, err = ftp.cmd("200", "TYPE %s", t)
	return
}

func (ftp *FTP) receive() (line string, err error) {
	line, err = ftp.reader.ReadString('\n')

	if ftp.debug {
		fmt.Printf("< %s\n", line)
	}

	return
}

func (ftp *FTP) send(command string, arguments ...interface{}) (err error) {
	command = fmt.Sprintf(command, arguments...)
	command += "\r\n"

	if ftp.debug {
		fmt.Printf("> %s", command)
	}

	if _, err = ftp.writer.WriteString(command); err != nil {
		return
	}

	err = ftp.writer.Flush()

	return
}

func (ftp *FTP) Pasv() (port int, err error) {
	var line string
	if line, err = ftp.cmd("227", "PASV"); err != nil {
		return
	}

	re, err := regexp.Compile(`\((.*)\)`)

	res := re.FindAllStringSubmatch(line, -1)

	s := strings.Split(res[0][1], ",")

	l1, _ := strconv.Atoi(s[len(s)-2])
	l2, _ := strconv.Atoi(s[len(s)-1])

	port = l1<<8 + l2

	return
}

func (ftp *FTP) newConnection(port int) (conn net.Conn, err error) {
	if ftp.debug {
		fmt.Println(fmt.Sprintf("Connecting to %s:%d", ftp.addr, port))
	}

	if conn, err = net.Dial("tcp", fmt.Sprintf("%s:%d", ftp.addr, port)); err != nil {
		return
	}

	if ftp.tlsconfig != nil {
		conn = tls.Client(conn, ftp.tlsconfig)
	}

	return
}

func (ftp *FTP) Stor(path string, r io.Reader) (err error) {
	if err = ftp.Type("I"); err != nil {
		return
	}

	var port int
	if port, err = ftp.Pasv(); err != nil {
		return
	}

	if err = ftp.send("STOR %s", path); err != nil {
		return
	}

	var pconn net.Conn
	if pconn, err = ftp.newConnection(port); err != nil {
		return
	}

	var line string
	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "150") {
		err = errors.New(line)
		return
	}

	if _, err = io.Copy(pconn, r); err != nil {
		return
	}

	pconn.Close()

	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "226") {
		err = errors.New(line)
		return
	}

	return

}

func (ftp *FTP) Retr(path string, retrFn RetrFunc) (s string, err error) {
	if err = ftp.Type("I"); err != nil {
		return
	}

	var port int
	if port, err = ftp.Pasv(); err != nil {
		return
	}

	if err = ftp.send("RETR %s", path); err != nil {
		return
	}

	var pconn net.Conn
	if pconn, err = ftp.newConnection(port); err != nil {
		return
	}

	var line string
	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "150") {
		err = errors.New(line)
		return
	}

	if err = retrFn(pconn); err != nil {
		return
	}

	pconn.Close()

	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "226") {
		err = errors.New(line)
		return
	}

	return
}

func (ftp *FTP) List(path string) (files []string, err error) {
	if err = ftp.Type("A"); err != nil {
		return
	}

	var port int
	if port, err = ftp.Pasv(); err != nil {
		return
	}

	// _, err = ftp.writer.WriteString(fmt.Sprintf("LIST %s\r\n", path))
	// check for features LIST / MLSD
	if err = ftp.send("MLSD %s", path); err != nil {
		return
	}

	var pconn net.Conn
	if pconn, err = ftp.newConnection(port); err != nil {
		return
	}

	var line string
	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "150") {
		err = errors.New(line)
		return
	}

	reader := bufio.NewReader(pconn)

	for {
		line, err = reader.ReadString('\n')

		if err == io.EOF {
			break
		} else if err != nil {
			return
		}

		files = append(files, string(line))
	}

	pconn.Close()

	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "226") {
		err = errors.New(line)
		return
	}

	return
}

func (ftp *FTP) Login(username string, password string) (err error) {
	if _, err = ftp.cmd("331", "USER %s", username); err != nil {
		return
	}

	if _, err = ftp.cmd("230", "PASS %s", password); err != nil {
		return
	}

	return
}

func Connect(addr string) (*FTP, error) {
	var err error
	var conn net.Conn

	if conn, err = net.Dial("tcp", fmt.Sprintf("%s:%d", addr, 21)); err != nil {
		return nil, err
	}

	writer := bufio.NewWriter(conn)
	reader := bufio.NewReader(conn)

	var line string

	line, err = reader.ReadString('\n')
	fmt.Print(line)

	return &FTP{conn: conn, addr: addr, reader: reader, writer: writer, debug: true}, nil
}
<commit_msg>changed fmt.Print to log.Print<commit_after>package goftp

import (
	"bufio"
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"regexp"
	"strconv"
	"strings"
)

var REGEX_PWD_PATH *regexp.Regexp = regexp.MustCompile(`\"(.*)\"`)

type FTP struct {
	conn net.Conn

	addr string

	debug     bool
	tlsconfig *tls.Config

	reader *bufio.Reader
	writer *bufio.Writer
}

func (ftp *FTP) Close() {
	ftp.conn.Close()
}

type WalkFunc func(path string, info os.FileMode, err error) error
type RetrFunc func(r io.Reader) error

func parseLine(line string) (perm string, t string, filename string) {
	for _, v := range strings.Split(line, ";") {
		v2 := strings.Split(v, "=")

		switch v2[0] {
		case "perm":
			perm = v2[1]
		case "type":
			t = v2[1]
		default:
			filename = v[1 : len(v)-2]
		}
	}

	return
}

// walks recursively through path and call walkfunc for each file
func (ftp *FTP) Walk(path string, walkFn WalkFunc) (err error) {
	/*
		if err = walkFn(path, os.ModeDir, nil); err != nil {
			if err == filepath.SkipDir {
				return nil
			}
		}
	*/
	if ftp.debug {
		log.Printf("Walking: '%s'\n", path)
	}

	var lines []string

	if lines, err = ftp.List(path); err != nil {
		return
	}

	for _, line := range lines {
		_, t, subpath := parseLine(line)

		switch t {
		case "dir":
			if subpath == "." {
			} else if subpath == ".." {
			} else {
				if err = ftp.Walk(path+subpath+"/", walkFn); err != nil {
					return
				}
			}
		case "file":
			if err = walkFn(path+subpath, os.FileMode(0), nil); err != nil {
				return
			}
		default:
		}
	}

	return
}

func (ftp *FTP) Quit() (err error) {
	if _, err := ftp.cmd("221", "QUIT"); err != nil {
		return err
	}

	ftp.conn.Close()
	ftp.conn = nil

	return nil
}

// will send a NOOP (no operation) to the server
func (ftp *FTP) Noop() (err error) {
	_, err = ftp.cmd("200", "NOOP")
	return
}

// private function to send command and compare return code with expects
func (ftp *FTP) cmd(expects string, command string, args ...interface{}) (line string, err error) {
	if err = ftp.send(command, args...); err != nil {
		return
	}

	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, expects) {
		err = errors.New(line)
		return
	}

	return
}

func (ftp *FTP) Rename(from string, to string) (err error) {
	if _, err = ftp.cmd("350", "RNFR %s", from); err != nil {
		return
	}

	if _, err = ftp.cmd("250", "RNTO %s", to); err != nil {
		return
	}

	return
}
func (ftp *FTP) Mkd(path string) (err error) {
	_, err = ftp.cmd("257", "MKD %s", path)
	return
}

func (ftp *FTP) Pwd() (path string, err error) {
	var line string
	if line, err = ftp.cmd("257", "PWD"); err != nil {
		return
	}

	res := REGEX_PWD_PATH.FindAllStringSubmatch(line[4:], -1)

	path = res[0][1]
	return
}

func (ftp *FTP) Cwd(path string) (err error) {
	_, err = ftp.cmd("250", "CWD %s", path)
	return
}

func (ftp *FTP) Dele(path string) (err error) {
	if err = ftp.send("DELE %s", path); err != nil {
		return
	}

	var line string
	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "250") {
		return errors.New(line)
	}

	return
}

func (ftp *FTP) AuthTLS(config tls.Config) (err error) {
	if _, err = ftp.cmd("234", "AUTH TLS"); err != nil {
		return
	}

	// wrap tls on existing connection
	ftp.tlsconfig = &config

	ftp.conn = tls.Client(ftp.conn, &config)
	ftp.writer = bufio.NewWriter(ftp.conn)
	ftp.reader = bufio.NewReader(ftp.conn)

	if _, err = ftp.cmd("200", "PROT P"); err != nil {
		return
	}

	return
}

func (ftp *FTP) Type(t string) (err error) {
	_, err = ftp.cmd("200", "TYPE %s", t)
	return
}

func (ftp *FTP) receive() (line string, err error) {
	line, err = ftp.reader.ReadString('\n')

	if ftp.debug {
		log.Printf("< %s", line)
	}

	return
}

func (ftp *FTP) send(command string, arguments ...interface{}) (err error) {
	if ftp.debug {
		log.Printf("> %s", fmt.Sprintf(command, arguments...))
	}

	command = fmt.Sprintf(command, arguments...)
	command += "\r\n"

	if _, err = ftp.writer.WriteString(command); err != nil {
		return
	}

	err = ftp.writer.Flush()

	return
}

// enables passive data connection and returns port number
func (ftp *FTP) Pasv() (port int, err error) {
	var line string
	if line, err = ftp.cmd("227", "PASV"); err != nil {
		return
	}

	re, err := regexp.Compile(`\((.*)\)`)

	res := re.FindAllStringSubmatch(line, -1)

	s := strings.Split(res[0][1], ",")

	l1, _ := strconv.Atoi(s[len(s)-2])
	l2, _ := strconv.Atoi(s[len(s)-1])

	port = l1<<8 + l2

	return
}

func (ftp *FTP) newConnection(port int) (conn net.Conn, err error) {
	if ftp.debug {
		log.Printf("Connecting to %s\n", addr)
	}

	if conn, err = net.Dial("tcp", fmt.Sprintf("%s:%d", ftp.addr, port)); err != nil {
		return
	}

	if ftp.tlsconfig != nil {
		conn = tls.Client(conn, ftp.tlsconfig)
	}

	return
}

func (ftp *FTP) Stor(path string, r io.Reader) (err error) {
	if err = ftp.Type("I"); err != nil {
		return
	}

	var port int
	if port, err = ftp.Pasv(); err != nil {
		return
	}

	if err = ftp.send("STOR %s", path); err != nil {
		return
	}

	var pconn net.Conn
	if pconn, err = ftp.newConnection(port); err != nil {
		return
	}

	var line string
	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "150") {
		err = errors.New(line)
		return
	}

	if _, err = io.Copy(pconn, r); err != nil {
		return
	}

	pconn.Close()

	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "226") {
		err = errors.New(line)
		return
	}

	return

}

func (ftp *FTP) Retr(path string, retrFn RetrFunc) (s string, err error) {
	if err = ftp.Type("I"); err != nil {
		return
	}

	var port int
	if port, err = ftp.Pasv(); err != nil {
		return
	}

	if err = ftp.send("RETR %s", path); err != nil {
		return
	}

	var pconn net.Conn
	if pconn, err = ftp.newConnection(port); err != nil {
		return
	}

	var line string
	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "150") {
		err = errors.New(line)
		return
	}

	if err = retrFn(pconn); err != nil {
		return
	}

	pconn.Close()

	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "226") {
		err = errors.New(line)
		return
	}

	return
}

func (ftp *FTP) List(path string) (files []string, err error) {
	if err = ftp.Type("A"); err != nil {
		return
	}

	var port int
	if port, err = ftp.Pasv(); err != nil {
		return
	}

	// _, err = ftp.writer.WriteString(fmt.Sprintf("LIST %s\r\n", path))
	// check for features LIST / MLSD
	if err = ftp.send("MLSD %s", path); err != nil {
		return
	}

	var pconn net.Conn
	if pconn, err = ftp.newConnection(port); err != nil {
		return
	}

	var line string
	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "150") {
		err = errors.New(line)
		return
	}

	reader := bufio.NewReader(pconn)

	for {
		line, err = reader.ReadString('\n')

		if err == io.EOF {
			break
		} else if err != nil {
			return
		}

		files = append(files, string(line))
	}

	pconn.Close()

	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "226") {
		err = errors.New(line)
		return
	}

	return
}

func (ftp *FTP) Login(username string, password string) (err error) {
	if _, err = ftp.cmd("331", "USER %s", username); err != nil {
		return
	}

	if _, err = ftp.cmd("230", "PASS %s", password); err != nil {
		return
	}

	return
}

func Connect(addr string) (*FTP, error) {
	var err error
	var conn net.Conn

	if conn, err = net.Dial("tcp", fmt.Sprintf("%s:%d", addr, 21)); err != nil {
		return nil, err
	}

	writer := bufio.NewWriter(conn)
	reader := bufio.NewReader(conn)

	var line string

	line, err = reader.ReadString('\n')
	fmt.Print(line)

	return &FTP{conn: conn, addr: addr, reader: reader, writer: writer, debug: true}, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
)

type mpath struct {
	name string
	path string
}

type vdir string

func (vd vdir) trunc() string {
	s := string(vd)
	if len(s) > 0 {
		return s[:7]
	}
	return ""
}

var mpathre = regexp.MustCompile(`^\.(.*)###(.*)$`)
var vdirre = regexp.MustCompile(`^[a-f0-9]{64}$`)

func newmpath(mp string) *mpath {
	res := mpathre.FindAllStringSubmatch(mp, -1)
	var mres *mpath
	if res != nil && len(res) == 1 && len(res[0]) == 3 {
		name := res[0][1]
		path := res[0][2]
		path = strings.Replace(path, ",#,", "/", -1)
		mres = &mpath{name, path}
	}
	return mres
}

func (mp *mpath) String() string {
	return "." + mp.name + "###" + strings.Replace(mp.path, "/", ",#,", -1)
}

func newvdir(vd string) vdir {
	res := vdirre.FindString(vd)
	vres := vdir("")
	if res != "" && res == vd {
		vres = vdir(vd)
	}
	return vres
}

type marker struct {
	mp  *mpath
	dir vdir
}

func (mk *marker) String() string {
	return fmt.Sprintf("marker '%s'<%s->%s>", mk.dir.trunc(), mk.mp.name, mk.mp.path)
}

type markers []*marker

var allmarkers = markers{}

func (marks markers) getMarker(name string, path string, dir vdir) *marker {
	var mark *marker
	var pathdiff *marker
	for _, marker := range marks {
		if marker.mp.name == name {
			if marker.mp.path == path {
				mark = marker
				break
			} else {
				pathdiff = marker
			}
		}
	}
	if mark == nil {
		if pathdiff != nil {
			fmt.Printf("Invalid volume path detected: '%s' (not found in marker '%s')\n", path, pathdiff)
			return nil
		}
		mark = &marker{mp: &mpath{name: name, path: path}, dir: dir}
	}
	ldir := mark.dir
	if mark.dir != dir {
		// move dir and ln -s mark.dir dir
		ldir = dir
		cmd(fmt.Sprintf("sudo move /mnt/sda1/var/lib/docker/vfs/dir/%s /mnt/sda1/var/lib/docker/vfs/dir/_%s", dir, dir))
		fmt.Printf("Mark container named '%s' for path '%s' as link to '%s' (from '%s')\n", name, path, mark.dir, dir)
	}
	_, err := cmd(fmt.Sprintf("sudo ls -1L /mnt/sda1/var/lib/docker/vfs/dir/%s", mark.mp.String()))
	if err != nil {
		mustcmd(fmt.Sprintf("sudo ln -s %s /mnt/sda1/var/lib/docker/vfs/dir/%s", ldir, mark.mp.String()))
	}
	return mark
}

type volume struct {
	dir  vdir
	mark *marker
}

type volumes []*volume

var allvolumes = volumes{}

func (v *volume) String() string {
	return fmt.Sprintf("vol '%s'<%v>", v.dir.trunc(), v.mark)
}

func (vols volumes) getVolume(vd vdir, path string, name string) *volume {
	var vol *volume
	for _, volume := range vols {
		if string(volume.dir) == string(vd) {
			vol = volume
			if vol.mark == nil {
				vol.mark = allmarkers.getMarker(name, path, vol.dir)
			}
			if vol.mark == nil {
				return nil
			}
			break
		}
	}
	if vol == nil {
		vol = &volume{dir: vd}
		vol.mark = allmarkers.getMarker(name, path, vol.dir)
		if vol.mark == nil {
			return nil
		}
	}
	return vol
}

type container struct {
	name    string
	id      string
	stopped bool
	volumes []*volume
}

func (c *container) trunc() string {
	if len(c.id) > 0 {
		return c.id[:7]
	}
	return ""
}

func (c *container) String() string {
	return "cnt '" + c.name + "' (" + c.trunc() + ")" + fmt.Sprintf("[%v] - %d vol", c.stopped, len(c.volumes))
}

var containers = []*container{}

func mustcmd(acmd string) string {
	out, err := cmd(acmd)
	if err != nil {
		log.Fatal(fmt.Sprintf("out='%s', err='%s'", out, err))
	}
	return string(out)
}

type fcmd func(cmd string) (string, error)

var cmd = execcmd

func execcmd(cmd string) (string, error) {
	fmt.Println(cmd)
	out, err := exec.Command("sh", "-c", cmd).Output()
	return strings.TrimSpace(string(out)), err
}

func readVolumes() {
	out := mustcmd("sudo ls -a1F /mnt/sda1/var/lib/docker/vfs/dir")
	vollines := strings.Split(out, "\n")
	for _, volline := range vollines {
		dir := volline
		if dir == "./" || dir == "../" || dir == "" {
			continue
		}
		if strings.HasSuffix(dir, "@") {
			dir = dir[:len(dir)-1]
			fdir := fmt.Sprintf("/mnt/sda1/var/lib/docker/vfs/dir/%s", dir)
			mp := newmpath(dir)
			if mp == nil {
				fmt.Printf("Invalid marker detected: '%s'\n", dir)
				mustcmd("sudo rm " + fdir)
			} else {
				dirlink, err := cmd("sudo readlink " + fdir)
				fmt.Printf("---\ndir: '%s'\ndlk: '%s'\nerr='%v'\n", dir, dirlink, err)
				if err != nil {
					fmt.Printf("Invalid marker (no readlink) detected: '%s'\n", dir)
					mustcmd("sudo rm " + fdir)
				} else {
					_, err := cmd("sudo ls /mnt/sda1/var/lib/docker/vfs/dir/" + dirlink)
					if err != nil {
						fmt.Printf("Invalid marker (readlink no ls) detected: '%s'\n", dir)
						mustcmd("sudo rm " + fdir)
					} else {
						vd := newvdir(dirlink)
						if vd == "" {
							fmt.Printf("Invalid marker (readlink no vdir) detected: '%s'\n", dir)
							mustcmd("sudo rm " + fdir)
						} else {
							allmarkers = append(allmarkers, &marker{mp, vd})
						}
					}
				}
			}
		} else if strings.HasSuffix(dir, "/") {

			dir = dir[:len(dir)-1]
			fdir := fmt.Sprintf("/mnt/sda1/var/lib/docker/vfs/dir/%s", dir)
			vd := newvdir(dir)
			if vd == "" {
				fmt.Printf("Invalid volume folder detected: '%s'\n", dir)
				mustcmd("sudo rm " + fdir)
			} else {
				allvolumes = append(allvolumes, &volume{dir: vd})
			}
		} else {
			fdir := fmt.Sprintf("/mnt/sda1/var/lib/docker/vfs/dir/%s", dir)
			fmt.Printf("Invalid file detected: '%s'\n", dir)
			mustcmd("sudo rm " + fdir)
		}
	}
}

func readContainer() {

	out := mustcmd("docker ps -aq --no-trunc")
	contlines := strings.Split(out, "\n")
	// fmt.Println(contlines)
	for _, contline := range contlines {
		if contline == "" {
			continue
		}
		id := contline
		res := mustcmd("docker inspect -f '{{ .Name }},{{ range $key, $value := .Volumes }}{{ $key }},{{ $value }}##~#{{ end }}' " + id)
		// fmt.Println("res1: '" + res + "'")
		name := res[1:strings.Index(res, ",")]
		cont := &container{name: name, id: id}
		res = res[strings.Index(res, ",")+1:]
		// fmt.Println("res2: '" + res + "'")
		vols := strings.Split(res, "##~#")
		// fmt.Println(vols)
		for _, vol := range vols {
			elts := strings.Split(vol, ",")
			if len(elts) == 2 {
				// fmt.Printf("elts: '%v'\n", elts)
				path := elts[0]
				vfs := elts[1]
				if strings.Contains(vfs, "/var/lib/docker/vfs/dir/") {
					vd := newvdir(filepath.Base(vfs))
					if vd == "" {
						fmt.Printf("Invalid volume folder detected: '%s'\n", vfs)
						break
					}
					newvol := allvolumes.getVolume(vd, path, name)
					cont.volumes = append(cont.volumes, newvol)
				}
			}
		}
		containers = append(containers, cont)
		if len(cont.volumes) == 0 {
			fmt.Printf("Orphan container detected: '%s'\n", cont)
		}
	}
}

// docker run --rm -i -t -v `pwd`:`pwd` -w `pwd` --entrypoint="/bin/bash" go -c 'go build gcl.go'
func main() {
	readVolumes()
	readContainer()
}

// Containers returns all detected containers
func Containers() []*container {
	return containers
}

var orphanedContainers = []*container{}

// OrphanedContainers returns all detected containers with missing volumes
// They are not orphaned if they have no volumes
// They are orphaned if they have volumes which don't exist
func OrphanedContainers() []*container {
	return orphanedContainers
}

// Volumes returns all detected volumes
func Volumes() volumes {
	return allvolumes
}

var orphanedVolumes = volumes{}

// OrphanedVolumes returns all detected volumes with missing containers
// They are orphaned because no container reference them directly
// or through a marker
func OrphanedVolumes() volumes {
	return orphanedVolumes
}

// Markers returns all detected or created markers
func Markers() markers {
	return allmarkers
}
<commit_msg>gcl.go: volume detects if it is orphaned or not<commit_after>package main

import (
	"fmt"
	"log"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
)

type mpath struct {
	name string
	path string
}

type vdir string

func (vd vdir) trunc() string {
	s := string(vd)
	if len(s) > 0 {
		return s[:7]
	}
	return ""
}

var mpathre = regexp.MustCompile(`^\.(.*)###(.*)$`)
var vdirre = regexp.MustCompile(`^[a-f0-9]{64}$`)

func newmpath(mp string) *mpath {
	res := mpathre.FindAllStringSubmatch(mp, -1)
	var mres *mpath
	if res != nil && len(res) == 1 && len(res[0]) == 3 {
		name := res[0][1]
		path := res[0][2]
		path = strings.Replace(path, ",#,", "/", -1)
		mres = &mpath{name, path}
	}
	return mres
}

func (mp *mpath) String() string {
	return "." + mp.name + "###" + strings.Replace(mp.path, "/", ",#,", -1)
}

func newvdir(vd string) vdir {
	res := vdirre.FindString(vd)
	vres := vdir("")
	if res != "" && res == vd {
		vres = vdir(vd)
	}
	return vres
}

type marker struct {
	mp  *mpath
	dir vdir
}

func (mk *marker) String() string {
	return fmt.Sprintf("marker '%s'<%s->%s>", mk.dir.trunc(), mk.mp.name, mk.mp.path)
}

type markers []*marker

var allmarkers = markers{}

func (marks markers) getMarker(name string, path string, dir vdir) *marker {
	var mark *marker
	var pathdiff *marker
	for _, marker := range marks {
		if marker.mp.name == name {
			if marker.mp.path == path {
				mark = marker
				break
			} else {
				pathdiff = marker
			}
		}
	}
	if mark == nil {
		if pathdiff != nil {
			fmt.Printf("Invalid volume path detected: '%s' (not found in marker '%s')\n", path, pathdiff)
			return nil
		}
		mark = &marker{mp: &mpath{name: name, path: path}, dir: dir}
	}
	ldir := mark.dir
	if mark.dir != dir {
		// move dir and ln -s mark.dir dir
		ldir = dir
		cmd(fmt.Sprintf("sudo move /mnt/sda1/var/lib/docker/vfs/dir/%s /mnt/sda1/var/lib/docker/vfs/dir/_%s", dir, dir))
		fmt.Printf("Mark container named '%s' for path '%s' as link to '%s' (from '%s')\n", name, path, mark.dir, dir)
	}
	_, err := cmd(fmt.Sprintf("sudo ls -1L /mnt/sda1/var/lib/docker/vfs/dir/%s", mark.mp.String()))
	if err != nil {
		mustcmd(fmt.Sprintf("sudo ln -s %s /mnt/sda1/var/lib/docker/vfs/dir/%s", ldir, mark.mp.String()))
	}
	return mark
}

type volume struct {
	dir      vdir
	mark     *marker
	orphaned bool
}

type volumes []*volume

var allvolumes = volumes{}

func (v *volume) String() string {
	return fmt.Sprintf("vol '%s'<%v>", v.dir.trunc(), v.mark)
}

func (vols volumes) getVolume(vd vdir, path string, name string) *volume {
	var vol *volume
	for _, volume := range vols {
		if string(volume.dir) == string(vd) {
			vol = volume
			if vol.mark == nil {
				vol.mark = allmarkers.getMarker(name, path, vol.dir)
			}
			if vol.mark == nil {
				return nil
			}
			vol.orphaned = false
			break
		}
	}
	if vol == nil {
		vol = &volume{dir: vd, orphaned: true}
		vol.mark = allmarkers.getMarker(name, path, vol.dir)
		if vol.mark == nil {
			return nil
		}
		vol.orphaned = false
	}
	return vol
}

type container struct {
	name    string
	id      string
	stopped bool
	volumes []*volume
}

func (c *container) trunc() string {
	if len(c.id) > 0 {
		return c.id[:7]
	}
	return ""
}

func (c *container) String() string {
	return "cnt '" + c.name + "' (" + c.trunc() + ")" + fmt.Sprintf("[%v] - %d vol", c.stopped, len(c.volumes))
}

var containers = []*container{}

func mustcmd(acmd string) string {
	out, err := cmd(acmd)
	if err != nil {
		log.Fatal(fmt.Sprintf("out='%s', err='%s'", out, err))
	}
	return string(out)
}

type fcmd func(cmd string) (string, error)

var cmd = execcmd

func execcmd(cmd string) (string, error) {
	fmt.Println(cmd)
	out, err := exec.Command("sh", "-c", cmd).Output()
	return strings.TrimSpace(string(out)), err
}

func readVolumes() {
	out := mustcmd("sudo ls -a1F /mnt/sda1/var/lib/docker/vfs/dir")
	vollines := strings.Split(out, "\n")
	for _, volline := range vollines {
		dir := volline
		if dir == "./" || dir == "../" || dir == "" {
			continue
		}
		if strings.HasSuffix(dir, "@") {
			dir = dir[:len(dir)-1]
			fdir := fmt.Sprintf("/mnt/sda1/var/lib/docker/vfs/dir/%s", dir)
			mp := newmpath(dir)
			if mp == nil {
				fmt.Printf("Invalid marker detected: '%s'\n", dir)
				mustcmd("sudo rm " + fdir)
			} else {
				dirlink, err := cmd("sudo readlink " + fdir)
				fmt.Printf("---\ndir: '%s'\ndlk: '%s'\nerr='%v'\n", dir, dirlink, err)
				if err != nil {
					fmt.Printf("Invalid marker (no readlink) detected: '%s'\n", dir)
					mustcmd("sudo rm " + fdir)
				} else {
					_, err := cmd("sudo ls /mnt/sda1/var/lib/docker/vfs/dir/" + dirlink)
					if err != nil {
						fmt.Printf("Invalid marker (readlink no ls) detected: '%s'\n", dir)
						mustcmd("sudo rm " + fdir)
					} else {
						vd := newvdir(dirlink)
						if vd == "" {
							fmt.Printf("Invalid marker (readlink no vdir) detected: '%s'\n", dir)
							mustcmd("sudo rm " + fdir)
						} else {
							allmarkers = append(allmarkers, &marker{mp, vd})
						}
					}
				}
			}
		} else if strings.HasSuffix(dir, "/") {

			dir = dir[:len(dir)-1]
			fdir := fmt.Sprintf("/mnt/sda1/var/lib/docker/vfs/dir/%s", dir)
			vd := newvdir(dir)
			if vd == "" {
				fmt.Printf("Invalid volume folder detected: '%s'\n", dir)
				mustcmd("sudo rm " + fdir)
			} else {
				allvolumes = append(allvolumes, &volume{dir: vd, orphaned: true})
			}
		} else {
			fdir := fmt.Sprintf("/mnt/sda1/var/lib/docker/vfs/dir/%s", dir)
			fmt.Printf("Invalid file detected: '%s'\n", dir)
			mustcmd("sudo rm " + fdir)
		}
	}
}

func readContainer() {

	out := mustcmd("docker ps -aq --no-trunc")
	contlines := strings.Split(out, "\n")
	// fmt.Println(contlines)
	for _, contline := range contlines {
		if contline == "" {
			continue
		}
		id := contline
		res := mustcmd("docker inspect -f '{{ .Name }},{{ range $key, $value := .Volumes }}{{ $key }},{{ $value }}##~#{{ end }}' " + id)
		// fmt.Println("res1: '" + res + "'")
		name := res[1:strings.Index(res, ",")]
		cont := &container{name: name, id: id}
		res = res[strings.Index(res, ",")+1:]
		// fmt.Println("res2: '" + res + "'")
		vols := strings.Split(res, "##~#")
		// fmt.Println(vols)
		for _, vol := range vols {
			elts := strings.Split(vol, ",")
			if len(elts) == 2 {
				// fmt.Printf("elts: '%v'\n", elts)
				path := elts[0]
				vfs := elts[1]
				if strings.Contains(vfs, "/var/lib/docker/vfs/dir/") {
					vd := newvdir(filepath.Base(vfs))
					if vd == "" {
						fmt.Printf("Invalid volume folder detected: '%s'\n", vfs)
						break
					}
					newvol := allvolumes.getVolume(vd, path, name)
					cont.volumes = append(cont.volumes, newvol)
				}
			}
		}
		containers = append(containers, cont)
		if len(cont.volumes) == 0 {
			fmt.Printf("Orphan container detected: '%s'\n", cont)
		}
	}
}

// docker run --rm -i -t -v `pwd`:`pwd` -w `pwd` --entrypoint="/bin/bash" go -c 'go build gcl.go'
func main() {
	readVolumes()
	readContainer()
	for _, vol := range allvolumes {
		if vol.orphaned {
			orphanedVolumes = append(orphanedVolumes, vol)
		}
	}
}

// Containers returns all detected containers
func Containers() []*container {
	return containers
}

var orphanedContainers = []*container{}

// OrphanedContainers returns all detected containers with missing volumes
// They are not orphaned if they have no volumes
// They are orphaned if they have volumes which don't exist
func OrphanedContainers() []*container {
	return orphanedContainers
}

// Volumes returns all detected volumes
func Volumes() volumes {
	return allvolumes
}

var orphanedVolumes = volumes{}

// OrphanedVolumes returns all detected volumes with missing containers
// They are orphaned because no container reference them directly
// or through a marker
func OrphanedVolumes() volumes {
	return orphanedVolumes
}

// Markers returns all detected or created markers
func Markers() markers {
	return allmarkers
}
<|endoftext|>


<commit_before>//    Copyright 2017 drillbits
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.

package srgen

import (
	"bufio"
	"bytes"
	"fmt"
	"go/ast"
	"go/format"
	"go/parser"
	"go/token"
	"os"
	"sort"
	"strings"
	"text/template"
)

var servicesTmpl = `package {{.Package}}

import (
	"fmt"
	"strings"
)

var reg = &ServiceRegistry{}

// ServiceRegistry is an registry for services.
type ServiceRegistry struct {
	valid bool

{{- range .Services}}
	{{.}} {{.}}
{{- end}}
}

// Validate reports whether all services are fulfilled.
func (reg *ServiceRegistry) Validate() error {
	if reg.valid {
		return nil
	}

	var errs []string

{{- range .Services}}
	if reg.{{.}} == nil {
		errs = append(errs, "{{.}}")
	}
{{- end}}

	if len(errs) > 0 {
		return fmt.Errorf("nil service(s): %s", strings.Join(errs, ", "))
	}

	return nil
}

// InitServiceRegistry initializes the ServiceRegistry.
func InitServiceRegistry(
{{- range .Services}}
	{{.}} {{.}},
{{- end}}
) {
{{- range .Services}}
	reg.{{.}} = {{.}}
{{- end}}
}

// Services returns the ServiceRegistry.
func Services() *ServiceRegistry {
	if err := reg.Validate(); err != nil {
		panic(err)
	}
	return reg
}
`

func Generate(files []string, outfile string) error {
	var pkg string
	var services []string
	for _, file := range files {
		fset := token.NewFileSet()
		f, err := parser.ParseFile(fset, file, nil, parser.ParseComments)
		if err != nil {
			return err
		}

		if pkg == "" {
			pkg = f.Name.Name
		} else if pkg != f.Name.Name {
			return fmt.Errorf("multiple packages: %s, %s", pkg, f.Name.Name)
		}

		ast.Inspect(f, func(node ast.Node) bool {
			d, ok := node.(*ast.GenDecl)
			if !ok || d.Tok != token.TYPE {
				return true
			}

			if !findTag(d, "+srgen") {
				return true
			}

			for _, spec := range d.Specs {
				t := spec.(*ast.TypeSpec)
				_, ok := t.Type.(*ast.InterfaceType)
				if !ok {
					continue
				}
				services = append(services, t.Name.Name)
				return false
			}

			return true
		})
	}
	sort.Strings(services)

	if outfile == "" {
		outfile = "services.go"
	}

	var buf bytes.Buffer
	w := bufio.NewWriter(&buf)
	t := template.Must(template.New("services").Parse(servicesTmpl))
	t.Execute(w, struct {
		Package  string
		Services []string
	}{
		Package:  pkg,
		Services: services,
	})
	w.Flush()

	b, err := format.Source(buf.Bytes())
	if err != nil {
		return err
	}

	f, err := os.Create(outfile)
	if err != nil {
		return err
	}
	defer f.Close()

	w = bufio.NewWriter(f)
	defer w.Flush()
	w.Write(b)

	return nil
}

func findTag(d *ast.GenDecl, tag string) bool {
	if d.Doc == nil {
		return false
	}
	for _, c := range d.Doc.List {
		comment := strings.TrimSpace(strings.TrimLeft(c.Text, "//"))
		if strings.HasPrefix(comment, tag) {
			return true
		}
	}
	return false
}
<commit_msg>Add MustServices()<commit_after>//    Copyright 2017 drillbits
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.

package srgen

import (
	"bufio"
	"bytes"
	"fmt"
	"go/ast"
	"go/format"
	"go/parser"
	"go/token"
	"os"
	"sort"
	"strings"
	"text/template"
)

var servicesTmpl = `package {{.Package}}

import (
	"fmt"
	"strings"
)

var reg = &ServiceRegistry{}

// ServiceRegistry is an registry for services.
type ServiceRegistry struct {
	valid bool

{{- range .Services}}
	{{.}} {{.}}
{{- end}}
}

// Validate reports whether all services are fulfilled.
func (reg *ServiceRegistry) Validate() error {
	if reg.valid {
		return nil
	}

	var errs []string

{{- range .Services}}
	if reg.{{.}} == nil {
		errs = append(errs, "{{.}}")
	}
{{- end}}

	if len(errs) > 0 {
		return fmt.Errorf("nil service(s): %s", strings.Join(errs, ", "))
	}

	return nil
}

// InitServiceRegistry initializes the ServiceRegistry.
func InitServiceRegistry(
{{- range .Services}}
	{{.}} {{.}},
{{- end}}
) {
{{- range .Services}}
	reg.{{.}} = {{.}}
{{- end}}
}

// Services returns the ServiceRegistry.
func Services() *ServiceRegistry {
	return reg
}

// MustServices is like Services but panics if all services are not fulfilled.
func MustServices() *ServiceRegistry {
	if err := reg.Validate(); err != nil {
		panic(err)
	}
	return reg
}
`

func Generate(files []string, outfile string) error {
	var pkg string
	var services []string
	for _, file := range files {
		fset := token.NewFileSet()
		f, err := parser.ParseFile(fset, file, nil, parser.ParseComments)
		if err != nil {
			return err
		}

		if pkg == "" {
			pkg = f.Name.Name
		} else if pkg != f.Name.Name {
			return fmt.Errorf("multiple packages: %s, %s", pkg, f.Name.Name)
		}

		ast.Inspect(f, func(node ast.Node) bool {
			d, ok := node.(*ast.GenDecl)
			if !ok || d.Tok != token.TYPE {
				return true
			}

			if !findTag(d, "+srgen") {
				return true
			}

			for _, spec := range d.Specs {
				t := spec.(*ast.TypeSpec)
				_, ok := t.Type.(*ast.InterfaceType)
				if !ok {
					continue
				}
				services = append(services, t.Name.Name)
				return false
			}

			return true
		})
	}
	sort.Strings(services)

	if outfile == "" {
		outfile = "services.go"
	}

	var buf bytes.Buffer
	w := bufio.NewWriter(&buf)
	t := template.Must(template.New("services").Parse(servicesTmpl))
	t.Execute(w, struct {
		Package  string
		Services []string
	}{
		Package:  pkg,
		Services: services,
	})
	w.Flush()

	b, err := format.Source(buf.Bytes())
	if err != nil {
		return err
	}

	f, err := os.Create(outfile)
	if err != nil {
		return err
	}
	defer f.Close()

	w = bufio.NewWriter(f)
	defer w.Flush()
	w.Write(b)

	return nil
}

func findTag(d *ast.GenDecl, tag string) bool {
	if d.Doc == nil {
		return false
	}
	for _, c := range d.Doc.List {
		comment := strings.TrimSpace(strings.TrimLeft(c.Text, "//"))
		if strings.HasPrefix(comment, tag) {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package git

/*
#cgo pkg-config: libgit2
#include <git2.h>
#include <git2/errors.h>
*/
import "C"
import (
	"bytes"
	"errors"
	"runtime"
	"strings"
	"unsafe"
)

const (
	ITEROVER  = C.GIT_ITEROVER
	EEXISTS   = C.GIT_EEXISTS
	ENOTFOUND = C.GIT_ENOTFOUND
)

var (
	ErrIterOver = errors.New("Iteration is over")
)

func init() {
	C.git_threads_init()
}

// Oid
type Oid struct {
	bytes [20]byte
}

func newOidFromC(coid *C.git_oid) *Oid {
	if coid == nil {
		return nil
	}

	oid := new(Oid)
	copy(oid.bytes[0:20], C.GoBytes(unsafe.Pointer(coid), 20))
	return oid
}

func NewOid(b []byte) *Oid {
	oid := new(Oid)
	copy(oid.bytes[0:20], b[0:20])
	return oid
}

func (oid *Oid) toC() *C.git_oid {
	return (*C.git_oid)(unsafe.Pointer(&oid.bytes))
}

func NewOidFromString(s string) (*Oid, error) {
	o := new(Oid)
	cs := C.CString(s)
	defer C.free(unsafe.Pointer(cs))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	if ret := C.git_oid_fromstr(o.toC(), cs); ret < 0 {
		return nil, MakeGitError(ret)
	}

	return o, nil
}

func (oid *Oid) String() string {
	buf := make([]byte, 40)
	C.git_oid_fmt((*C.char)(unsafe.Pointer(&buf[0])), oid.toC())
	return string(buf)
}

func (oid *Oid) Bytes() []byte {
	return oid.bytes[0:]
}

func (oid *Oid) Cmp(oid2 *Oid) int {
	return bytes.Compare(oid.bytes[:], oid2.bytes[:])
}

func (oid *Oid) Copy() *Oid {
	ret := new(Oid)
	copy(ret.bytes[:], oid.bytes[:])
	return ret
}

func (oid *Oid) Equal(oid2 *Oid) bool {
	return bytes.Equal(oid.bytes[:], oid2.bytes[:])
}

func (oid *Oid) IsZero() bool {
	for _, a := range oid.bytes {
		if a != '0' {
			return false
		}
	}
	return true
}

func (oid *Oid) NCmp(oid2 *Oid, n uint) int {
	return bytes.Compare(oid.bytes[:n], oid2.bytes[:n])
}

func ShortenOids(ids []*Oid, minlen int) (int, error) {
	shorten := C.git_oid_shorten_new(C.size_t(minlen))
	if shorten == nil {
		panic("Out of memory")
	}
	defer C.git_oid_shorten_free(shorten)

	var ret C.int

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	for _, id := range ids {
		buf := make([]byte, 41)
		C.git_oid_fmt((*C.char)(unsafe.Pointer(&buf[0])), id.toC())
		buf[40] = 0
		ret = C.git_oid_shorten_add(shorten, (*C.char)(unsafe.Pointer(&buf[0])))
		if ret < 0 {
			return int(ret), MakeGitError(ret)
		}
	}
	return int(ret), nil
}

type GitError struct {
	Message   string
	Class     int
	ErrorCode int
}

func (e GitError) Error() string {
	return e.Message
}

func IsNotExist(err error) bool {
	return err.(*GitError).ErrorCode == C.GIT_ENOTFOUND
}

func IsExist(err error) bool {
	return err.(*GitError).ErrorCode == C.GIT_EEXISTS
}

func MakeGitError(errorCode C.int) error {
	err := C.giterr_last()
	if err == nil {
		return &GitError{"No message", C.GITERR_INVALID, C.GIT_ERROR}
	}
	return &GitError{C.GoString(err.message), int(err.klass), int(errorCode)}
}

func cbool(b bool) C.int {
	if b {
		return C.int(1)
	}
	return C.int(0)
}

func ucbool(b bool) C.uint {
	if b {
		return C.uint(1)
	}
	return C.uint(0)
}

func Discover(start string, across_fs bool, ceiling_dirs []string) (string, error) {
	ceildirs := C.CString(strings.Join(ceiling_dirs, string(C.GIT_PATH_LIST_SEPARATOR)))
	defer C.free(unsafe.Pointer(ceildirs))

	cstart := C.CString(start)
	defer C.free(unsafe.Pointer(cstart))

	var buf C.git_buf
	defer C.git_buf_free(&buf)

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_repository_discover(&buf, cstart, cbool(across_fs), ceildirs)
	if ret < 0 {
		return "", MakeGitError(ret)
	}

	return C.GoString(buf.ptr), nil
}
<commit_msg>Oid: make the type directly [20]byte<commit_after>package git

/*
#cgo pkg-config: libgit2
#include <git2.h>
#include <git2/errors.h>
*/
import "C"
import (
	"bytes"
	"errors"
	"runtime"
	"strings"
	"unsafe"
)

const (
	ITEROVER  = C.GIT_ITEROVER
	EEXISTS   = C.GIT_EEXISTS
	ENOTFOUND = C.GIT_ENOTFOUND
)

var (
	ErrIterOver = errors.New("Iteration is over")
)

func init() {
	C.git_threads_init()
}

// Oid
type Oid [20]byte

func newOidFromC(coid *C.git_oid) *Oid {
	if coid == nil {
		return nil
	}

	oid := new(Oid)
	copy(oid[0:20], C.GoBytes(unsafe.Pointer(coid), 20))
	return oid
}

func NewOid(b []byte) *Oid {
	oid := new(Oid)
	copy(oid[0:20], b[0:20])
	return oid
}

func (oid *Oid) toC() *C.git_oid {
	return (*C.git_oid)(unsafe.Pointer(oid))
}

func NewOidFromString(s string) (*Oid, error) {
	o := new(Oid)
	cs := C.CString(s)
	defer C.free(unsafe.Pointer(cs))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	if ret := C.git_oid_fromstr(o.toC(), cs); ret < 0 {
		return nil, MakeGitError(ret)
	}

	return o, nil
}

func (oid *Oid) String() string {
	buf := make([]byte, 40)
	C.git_oid_fmt((*C.char)(unsafe.Pointer(&buf[0])), oid.toC())
	return string(buf)
}

func (oid *Oid) Bytes() []byte {
	return oid[0:]
}

func (oid *Oid) Cmp(oid2 *Oid) int {
	return bytes.Compare(oid[:], oid2[:])
}

func (oid *Oid) Copy() *Oid {
	ret := new(Oid)
	copy(ret[:], oid[:])
	return ret
}

func (oid *Oid) Equal(oid2 *Oid) bool {
	return bytes.Equal(oid[:], oid2[:])
}

func (oid *Oid) IsZero() bool {
	for _, a := range oid {
		if a != '0' {
			return false
		}
	}
	return true
}

func (oid *Oid) NCmp(oid2 *Oid, n uint) int {
	return bytes.Compare(oid[:n], oid2[:n])
}

func ShortenOids(ids []*Oid, minlen int) (int, error) {
	shorten := C.git_oid_shorten_new(C.size_t(minlen))
	if shorten == nil {
		panic("Out of memory")
	}
	defer C.git_oid_shorten_free(shorten)

	var ret C.int

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	for _, id := range ids {
		buf := make([]byte, 41)
		C.git_oid_fmt((*C.char)(unsafe.Pointer(&buf[0])), id.toC())
		buf[40] = 0
		ret = C.git_oid_shorten_add(shorten, (*C.char)(unsafe.Pointer(&buf[0])))
		if ret < 0 {
			return int(ret), MakeGitError(ret)
		}
	}
	return int(ret), nil
}

type GitError struct {
	Message   string
	Class     int
	ErrorCode int
}

func (e GitError) Error() string {
	return e.Message
}

func IsNotExist(err error) bool {
	return err.(*GitError).ErrorCode == C.GIT_ENOTFOUND
}

func IsExist(err error) bool {
	return err.(*GitError).ErrorCode == C.GIT_EEXISTS
}

func MakeGitError(errorCode C.int) error {
	err := C.giterr_last()
	if err == nil {
		return &GitError{"No message", C.GITERR_INVALID, C.GIT_ERROR}
	}
	return &GitError{C.GoString(err.message), int(err.klass), int(errorCode)}
}

func cbool(b bool) C.int {
	if b {
		return C.int(1)
	}
	return C.int(0)
}

func ucbool(b bool) C.uint {
	if b {
		return C.uint(1)
	}
	return C.uint(0)
}

func Discover(start string, across_fs bool, ceiling_dirs []string) (string, error) {
	ceildirs := C.CString(strings.Join(ceiling_dirs, string(C.GIT_PATH_LIST_SEPARATOR)))
	defer C.free(unsafe.Pointer(ceildirs))

	cstart := C.CString(start)
	defer C.free(unsafe.Pointer(cstart))

	var buf C.git_buf
	defer C.git_buf_free(&buf)

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_repository_discover(&buf, cstart, cbool(across_fs), ceildirs)
	if ret < 0 {
		return "", MakeGitError(ret)
	}

	return C.GoString(buf.ptr), nil
}
<|endoftext|>


<commit_before>package git

import (
	"os"
	"strings"
	"sync"

	"github.com/desal/cmd"
	"github.com/desal/dsutil"
)

type Status int

const (
	Unknown Status = iota
	Clean
	Uncommitted
	Detached
	Unpushed
	NoUpstream
)

var (
	escapeWindows bool
	checked       bool
	mutex         sync.Mutex
)

type Context struct {
	output cmd.Output
}

func New(output cmd.Output) *Context {
	return &Context{output}
}

//Can't run this without a valid git repo, so defer it until we first see one
func (c *Context) checkEscape(path string, cmdContext *cmd.Context) {
	mutex.Lock()
	defer mutex.Unlock()
	if checked {
		return
	}

	//Some (but not all) versions of git on windows like the curly brackets
	//escaped. Rather than attempting to decipher this from the version of git
	//installed, I just retry the first time this runs with an escape, if that
	//works, then we switch over for remaining invokations

	_, err := cmdContext.Execf(`git rev-parse @{0}`)
	if err != nil {
		_, err := cmdContext.Execf(`git rev-parse @\{0\}`)
		if err != nil {
			errMsg := "Could not determine if git curly braces need to be escaped\n"
			errMsg += "git rev-parse @{0} failed both with and without quotes in"
			errMsg += path
			c.output.Error(errMsg)
			os.Exit(1)

		} else {
			escapeWindows = true
		}
	}

	checked = true

}

func escape(s string) string {
	if escapeWindows {
		left := strings.Replace(s, `{`, `\{`, -1)
		return strings.Replace(left, `}`, `\}`, -1)
	}
	return s
}

func (c *Context) Status(path string, must bool) (Status, error) {
	cmdContext := cmd.NewContext(path, c.output)
	var mustContext *cmd.Context
	if must {
		mustContext = cmd.NewContext(path, c.output, cmd.Must)
	} else {
		mustContext = cmdContext
	}

	c.checkEscape(path, cmdContext)

	if unchecked, err := mustContext.Execf("git status --porcelain"); err != nil {
		return Unknown, err
	} else if len(unchecked) != 0 {
		return Uncommitted, nil
	}

	if _, err := cmdContext.Execf("git symbolic-ref HEAD"); err != nil {
		return Detached, nil
	}

	if _, err := cmdContext.Execf(escape("git rev-parse --abrev-ref --symbolic-full-name @{upstream}")); err != nil {
		return NoUpstream, nil
	}

	if unpushed, err := mustContext.Execf(escape("git rev-list HEAD@{upstream}..HEAD")); err != nil {
		return Unknown, err
	} else if len(unpushed) != 0 {
		return Unpushed, nil
	}
	return Clean, nil
}

func (c *Context) cmdContext(path string, must bool) *cmd.Context {
	if must {
		return cmd.NewContext(path, c.output, cmd.Must)
	} else {
		return cmd.NewContext(path, c.output, cmd.Warn)
	}

}

func (c *Context) TopLevel(path string, must bool) (string, error) {
	cmdContext := c.cmdContext(path, must)

	output, err := cmdContext.Execf("git rev-parse --show-toplevel")
	if err != nil {
		return "", err
	}

	return dsutil.SanitisePath(cmdContext, dsutil.FirstLine(output))
}

func (c *Context) Clone(targetPath string, url string, must bool) error {
	err := os.MkdirAll(targetPath, 0755)
	if err != nil && must {
		c.output.Error("Could not create: %s", targetPath)
		os.Exit(1)
	} else if err != nil {
		return err
	}

	cmdContext := c.cmdContext(targetPath, must)

	_, err = cmdContext.Execf("git clone %s .", url)
	return err
}

func (c *Context) Checkout(targetPath, target string, must bool) error {
	cmdContext := c.cmdContext(targetPath, must)

	_, err := cmdContext.Execf("git checkout %s", target)
	return err
}

func (c *Context) Pull(targetPath string, must bool) error {
	cmdContext := c.cmdContext(targetPath, must)

	_, err := cmdContext.Execf("git pull")
	return err
}

func (c *Context) IsGit(targetPath string) bool {
	cmdContext := c.cmdContext(targetPath, false)

	_, err := cmdContext.Execf("git rev-parse")
	return err == nil
}

func (c *Context) SHA(targetPath string, must bool) (string, error) {
	cmdContext := c.cmdContext(targetPath, must)

	output, err := cmdContext.Execf("git rev-parse HEAD")
	return dsutil.FirstLine(output), err
}

func (c *Context) RemoteOriginUrl(targetPath string, must bool) (string, error) {
	cmdContext := c.cmdContext(targetPath, must)

	output, err := cmdContext.Execf("git config --get remote.origin.url")
	return dsutil.FirstLine(output), err
}
<commit_msg>Made TopLevel work under symlinks on linux<commit_after>package git

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/desal/cmd"
	"github.com/desal/dsutil"
)

type Status int

const (
	Unknown Status = iota
	Clean
	Uncommitted
	Detached
	Unpushed
	NoUpstream
)

var (
	escapeWindows bool
	checked       bool
	mutex         sync.Mutex
)

type Context struct {
	output cmd.Output
}

func New(output cmd.Output) *Context {
	return &Context{output}
}

//Can't run this without a valid git repo, so defer it until we first see one
func (c *Context) checkEscape(path string, cmdContext *cmd.Context) {
	mutex.Lock()
	defer mutex.Unlock()
	if checked {
		return
	}

	//Some (but not all) versions of git on windows like the curly brackets
	//escaped. Rather than attempting to decipher this from the version of git
	//installed, I just retry the first time this runs with an escape, if that
	//works, then we switch over for remaining invokations

	_, err := cmdContext.Execf(`git rev-parse @{0}`)
	if err != nil {
		_, err := cmdContext.Execf(`git rev-parse @\{0\}`)
		if err != nil {
			errMsg := "Could not determine if git curly braces need to be escaped\n"
			errMsg += "git rev-parse @{0} failed both with and without quotes in"
			errMsg += path
			c.output.Error(errMsg)
			os.Exit(1)

		} else {
			escapeWindows = true
		}
	}

	checked = true

}

func escape(s string) string {
	if escapeWindows {
		left := strings.Replace(s, `{`, `\{`, -1)
		return strings.Replace(left, `}`, `\}`, -1)
	}
	return s
}

func (c *Context) Status(path string, must bool) (Status, error) {
	cmdContext := cmd.NewContext(path, c.output)
	var mustContext *cmd.Context
	if must {
		mustContext = cmd.NewContext(path, c.output, cmd.Must)
	} else {
		mustContext = cmdContext
	}

	c.checkEscape(path, cmdContext)

	if unchecked, err := mustContext.Execf("git status --porcelain"); err != nil {
		return Unknown, err
	} else if len(unchecked) != 0 {
		return Uncommitted, nil
	}

	if _, err := cmdContext.Execf("git symbolic-ref HEAD"); err != nil {
		return Detached, nil
	}

	if _, err := cmdContext.Execf(escape("git rev-parse --abrev-ref --symbolic-full-name @{upstream}")); err != nil {
		return NoUpstream, nil
	}

	if unpushed, err := mustContext.Execf(escape("git rev-list HEAD@{upstream}..HEAD")); err != nil {
		return Unknown, err
	} else if len(unpushed) != 0 {
		return Unpushed, nil
	}
	return Clean, nil
}

func (c *Context) cmdContext(path string, must bool) *cmd.Context {
	if must {
		return cmd.NewContext(path, c.output, cmd.Must)
	} else {
		return cmd.NewContext(path, c.output, cmd.Warn)
	}

}

func (c *Context) TopLevel(path string, must bool) (string, error) {
	//Unfortunately you can't use "git rev-parse --show-toplevel", as it will unsymlinkify things.
	cmdContext := c.cmdContext(path, must)

	sanePath, err := dsutil.SanitisePath(cmdContext, dsutil.FirstLine(path))
	if err != nil {
		return "", err
	}
	splitPath := strings.Split(sanePath, "/")

	for i := len(splitPath); i >= 0; i-- {

		tryPath := filepath.Join(splitPath[0:i]...)
		if dsutil.CheckPath(filepath.Join(tryPath, ".git")) {
			return tryPath, nil
		}

	}
	return "", fmt.Errorf("Could not find .git in any parent directory.")

}

func (c *Context) Clone(targetPath string, url string, must bool) error {
	err := os.MkdirAll(targetPath, 0755)
	if err != nil && must {
		c.output.Error("Could not create: %s", targetPath)
		os.Exit(1)
	} else if err != nil {
		return err
	}

	cmdContext := c.cmdContext(targetPath, must)

	_, err = cmdContext.Execf("git clone %s .", url)
	return err
}

func (c *Context) Checkout(targetPath, target string, must bool) error {
	cmdContext := c.cmdContext(targetPath, must)

	_, err := cmdContext.Execf("git checkout %s", target)
	return err
}

func (c *Context) Pull(targetPath string, must bool) error {
	cmdContext := c.cmdContext(targetPath, must)

	_, err := cmdContext.Execf("git pull")
	return err
}

func (c *Context) IsGit(targetPath string) bool {
	cmdContext := c.cmdContext(targetPath, false)

	_, err := cmdContext.Execf("git rev-parse")
	return err == nil
}

func (c *Context) SHA(targetPath string, must bool) (string, error) {
	cmdContext := c.cmdContext(targetPath, must)

	output, err := cmdContext.Execf("git rev-parse HEAD")
	return dsutil.FirstLine(output), err
}

func (c *Context) RemoteOriginUrl(targetPath string, must bool) (string, error) {
	cmdContext := c.cmdContext(targetPath, must)

	output, err := cmdContext.Execf("git config --get remote.origin.url")
	return dsutil.FirstLine(output), err
}
<|endoftext|>


<commit_before>// Copyright 2012 The go-gl 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 glu

// #cgo darwin LDFLAGS: -framework Carbon -framework OpenGL -framework GLUT
// #cgo linux LDFLAGS: -lGLU
// #cgo windows LDFLAGS: -lglu32
//
// #ifdef __APPLE__
//   #include <OpenGL/glu.h>
// #else
//   #include <GL/glu.h>
// #endif
import "C"
import (
	"errors"
	"reflect"
	"unsafe"

	"github.com/go-gl/gl"
)

func ptr(v interface{}) unsafe.Pointer {

	if v == nil {
		return unsafe.Pointer(nil)
	}

	rv := reflect.ValueOf(v)
	var et reflect.Value
	switch rv.Type().Kind() {
	case reflect.Uintptr:
		offset, _ := v.(uintptr)
		return unsafe.Pointer(offset)
	case reflect.Ptr:
		et = rv.Elem()
	case reflect.Slice:
		et = rv.Index(0)
	default:
		panic("type must be a pointer, a slice, uintptr or nil")
	}

	return unsafe.Pointer(et.UnsafeAddr())
}

func ErrorString(error gl.GLenum) (string, error) {
	e := unsafe.Pointer(C.gluErrorString(C.GLenum(error)))
	if e == nil {
		return "", errors.New("Invalid GL error code")
	}
	return C.GoString((*C.char)(e)), nil
}

func Build2DMipmaps(target gl.GLenum, internalFormat int, width, height int, format, typ gl.GLenum, data interface{}) int {
	return int(C.gluBuild2DMipmaps(
		C.GLenum(target),
		C.GLint(internalFormat),
		C.GLint(width),
		C.GLint(height),
		C.GLenum(format),
		C.GLenum(typ),
		ptr(data),
	))
}

func Perspective(fovy, aspect, zNear, zFar float64) {
	C.gluPerspective(
		C.GLdouble(fovy),
		C.GLdouble(aspect),
		C.GLdouble(zNear),
		C.GLdouble(zFar),
	)
}

func LookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ float64) {
	C.gluLookAt(
		C.GLdouble(eyeX),
		C.GLdouble(eyeY),
		C.GLdouble(eyeZ),
		C.GLdouble(centerX),
		C.GLdouble(centerY),
		C.GLdouble(centerZ),
		C.GLdouble(upX),
		C.GLdouble(upY),
		C.GLdouble(upZ),
	)
}

func UnProject(winX, winY, winZ float64, model, proj *[16]float64, view *[4]int32) (float64, float64, float64) {
	var ox, oy, oz C.GLdouble

	m := (*C.GLdouble)(unsafe.Pointer(model))
	p := (*C.GLdouble)(unsafe.Pointer(proj))
	v := (*C.GLint)(unsafe.Pointer(view))

	C.gluUnProject(
		C.GLdouble(winX),
		C.GLdouble(winY),
		C.GLdouble(winZ),
		m,
		p,
		v,
		&ox,
		&oy,
		&oz,
	)

	return float64(ox), float64(oy), float64(oz)
}

func Project(projX, projY, projZ float64, model, proj *[16]float64, view *[4]int32) (float64, float64, float64) {
	var ox, oy, oz C.GLdouble

	m := (*C.GLdouble)(unsafe.Pointer(model))
	p := (*C.GLdouble)(unsafe.Pointer(proj))
	v := (*C.GLint)(unsafe.Pointer(view))

	C.gluProject(
		C.GLdouble(projX),
		C.GLdouble(projY),
		C.GLdouble(projZ),
		m,
		p,
		v,
		&ox,
		&oy,
		&oz,
	)

	return float64(ox), float64(oy), float64(oz)
}

func NewQuadric() unsafe.Pointer {
	return unsafe.Pointer(C.gluNewQuadric())
}

func Sphere(q unsafe.Pointer, radius float32, slices, stacks int) {
	C.gluSphere((*C.GLUquadric)(q), C.GLdouble(radius), C.GLint(slices), C.GLint(stacks))
}

func Cylinder(q unsafe.Pointer, base, top, height float32, slices, stacks int) {
	C.gluCylinder((*C.GLUquadric)(q), C.GLdouble(base), C.GLdouble(top), C.GLdouble(height), C.GLint(slices), C.GLint(stacks))
}

func Disk(q unsafe.Pointer, inner, outer float32, slices, loops int) {
	C.gluDisk((*C.GLUquadric)(q), C.GLdouble(inner), C.GLdouble(outer), C.GLint(slices), C.GLint(loops))
}

func PartialDisk(q unsafe.Pointer, inner, outer float32, slices, loops int, startAngle, sweepAngle float32) {
	C.gluPartialDisk((*C.GLUquadric)(q), C.GLdouble(inner), C.GLdouble(outer), C.GLint(slices), C.GLint(loops), C.GLdouble(startAngle), C.GLdouble(sweepAngle))
}
<commit_msg>Updated for compatibility<commit_after>// Copyright 2012 The go-gl 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 glu

// #cgo darwin LDFLAGS: -framework Carbon -framework OpenGL -framework GLUT
// #cgo linux LDFLAGS: -lGLU
// #cgo windows LDFLAGS: -lglu32
//
// #ifdef __APPLE__
//   #include <OpenGL/glu.h>
// #else
//   #include <GL/glu.h>
// #endif
import "C"
import (
	"errors"
	"reflect"
	"unsafe"

	"github.com/go-gl/gl"
)

func ptr(v interface{}) unsafe.Pointer {

	if v == nil {
		return unsafe.Pointer(nil)
	}

	rv := reflect.ValueOf(v)
	var et reflect.Value
	switch rv.Type().Kind() {
	case reflect.Uintptr:
		offset, _ := v.(uintptr)
		return unsafe.Pointer(offset)
	case reflect.Ptr:
		et = rv.Elem()
	case reflect.Slice:
		et = rv.Index(0)
	default:
		panic("type must be a pointer, a slice, uintptr or nil")
	}

	return unsafe.Pointer(et.UnsafeAddr())
}

func ErrorString(error gl.GLenum) (string, error) {
	e := unsafe.Pointer(C.gluErrorString(C.GLenum(error)))
	if e == nil {
		return "", errors.New("Invalid GL error code")
	}
	return C.GoString((*C.char)(e)), nil
}

func Build2DMipmaps(target gl.GLenum, internalFormat int, width, height int, format, typ gl.GLenum, data interface{}) int {
	return int(C.gluBuild2DMipmaps(
		C.GLenum(target),
		C.GLint(internalFormat),
		#ifdef __MINGW32__
		C.GLint(width),
		C.GLint(height),
		#else
		C.GLsizei(width),
		C.GLsizei(height),
		#endif
		C.GLenum(format),
		C.GLenum(typ),
		ptr(data),
	))
}

func Perspective(fovy, aspect, zNear, zFar float64) {
	C.gluPerspective(
		C.GLdouble(fovy),
		C.GLdouble(aspect),
		C.GLdouble(zNear),
		C.GLdouble(zFar),
	)
}

func LookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ float64) {
	C.gluLookAt(
		C.GLdouble(eyeX),
		C.GLdouble(eyeY),
		C.GLdouble(eyeZ),
		C.GLdouble(centerX),
		C.GLdouble(centerY),
		C.GLdouble(centerZ),
		C.GLdouble(upX),
		C.GLdouble(upY),
		C.GLdouble(upZ),
	)
}

func UnProject(winX, winY, winZ float64, model, proj *[16]float64, view *[4]int32) (float64, float64, float64) {
	var ox, oy, oz C.GLdouble

	m := (*C.GLdouble)(unsafe.Pointer(model))
	p := (*C.GLdouble)(unsafe.Pointer(proj))
	v := (*C.GLint)(unsafe.Pointer(view))

	C.gluUnProject(
		C.GLdouble(winX),
		C.GLdouble(winY),
		C.GLdouble(winZ),
		m,
		p,
		v,
		&ox,
		&oy,
		&oz,
	)

	return float64(ox), float64(oy), float64(oz)
}

func Project(projX, projY, projZ float64, model, proj *[16]float64, view *[4]int32) (float64, float64, float64) {
	var ox, oy, oz C.GLdouble

	m := (*C.GLdouble)(unsafe.Pointer(model))
	p := (*C.GLdouble)(unsafe.Pointer(proj))
	v := (*C.GLint)(unsafe.Pointer(view))

	C.gluProject(
		C.GLdouble(projX),
		C.GLdouble(projY),
		C.GLdouble(projZ),
		m,
		p,
		v,
		&ox,
		&oy,
		&oz,
	)

	return float64(ox), float64(oy), float64(oz)
}

func NewQuadric() unsafe.Pointer {
	return unsafe.Pointer(C.gluNewQuadric())
}

func Sphere(q unsafe.Pointer, radius float32, slices, stacks int) {
	C.gluSphere((*C.GLUquadric)(q), C.GLdouble(radius), C.GLint(slices), C.GLint(stacks))
}

func Cylinder(q unsafe.Pointer, base, top, height float32, slices, stacks int) {
	C.gluCylinder((*C.GLUquadric)(q), C.GLdouble(base), C.GLdouble(top), C.GLdouble(height), C.GLint(slices), C.GLint(stacks))
}

func Disk(q unsafe.Pointer, inner, outer float32, slices, loops int) {
	C.gluDisk((*C.GLUquadric)(q), C.GLdouble(inner), C.GLdouble(outer), C.GLint(slices), C.GLint(loops))
}

func PartialDisk(q unsafe.Pointer, inner, outer float32, slices, loops int, startAngle, sweepAngle float32) {
	C.gluPartialDisk((*C.GLUquadric)(q), C.GLdouble(inner), C.GLdouble(outer), C.GLint(slices), C.GLint(loops), C.GLdouble(startAngle), C.GLdouble(sweepAngle))
}
<|endoftext|>


<commit_before>package gogrs

//
//import (
//	"fmt"
//	"time"
//)
//
//func main() {
//	stock := StockRealTime{
//		No:        "2618",
//		Timestamp: time.Now().Unix(),
//		Date:      time.Date(2014, time.August, 11, 0, 0, 0, 0, time.Local),
//	}
//	fmt.Println(stock.GenStockUrl())
//	store_data := stock.GetData()
//	fmt.Printf("%+v\n", store_data)
//	fmt.Printf("%+v\n", store_data.QueryTime)
//	fmt.Printf("%s\n", store_data.QueryTime["sysTime"])
//}
<commit_msg>Remove unused file.<commit_after><|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"

	"github.com/codegangsta/cli"
	"github.com/fatih/color"
	"github.com/google/go-github/github"
	"github.com/hoisie/mustache"
	git "github.com/libgit2/git2go"
	"gopkg.in/yaml.v2"
)

var (
	version        = "0.1"
	suffix         = "-gut-template"
	templateFile   = "gut.template"
	templateConfig configuration
	cwd            string
	name           string
	fullPath       string
)

var (
	boldCyan  *color.Color
	boldWhite *color.Color
)

type configuration struct {
	Variables map[string]string `yaml:"variables"`
	Options   map[string]string `yaml:"options"`
	Commands  []string          `yaml:"commands"`
}

func main() {
	boldCyan = color.New(color.FgCyan).Add(color.Bold)
	boldWhite = color.New(color.FgWhite).Add(color.Bold)

	cwd, _ = filepath.Abs(filepath.Dir(os.Args[0]))

	app := cli.NewApp()
	app.Name = "gut"
	app.Usage = "tool that retrieves templates and compile them to scaffold projects and create standalone files"
	app.Version = version
	app.Author = "Federico Carrone"
	app.Email = "federico.carrone@gmail.com"

	app.Commands = []cli.Command{
		{
			Name:    "search",
			Aliases: []string{"s"},
			Usage:   "search templates",
			Action: func(c *cli.Context) {
				filter := c.Args().First()
				search(filter)
			},
		},
		{
			Name:    "new",
			Aliases: []string{"n"},
			Usage:   "new",
			Action: func(c *cli.Context) {
				if len(c.Args()) < 2 {
					fmt.Print("Not enough arguments")
				}
				templateName := c.Args().First()
				name = c.Args()[1]
				new(templateName)
			},
		},
		{
			Name:    "describe",
			Aliases: []string{"d"},
			Usage:   "describe",
			Action: func(c *cli.Context) {
				templateName := c.Args().First()
				describe(templateName)
			},
		},
		{
			Name:    "version",
			Aliases: []string{"v"},
			Usage:   "version",
			Action: func(c *cli.Context) {
				fmt.Printf("gut version %s\n", version)
			},
		},
	}

	app.Run(os.Args)
}

func search(filter string) {
	search := filter + suffix
	client := github.NewClient(nil)
	opt := &github.SearchOptions{
		Sort: "stars",
	}

	results, _, error := client.Search.Repositories(search, opt)

	if error != nil {
		log.Fatal(error)
	}

	for _, repository := range results.Repositories {
		repoName := *repository.Name
		repoUsername := *(*repository.Owner).Login
		repoDescription := *repository.Description
		repoStars := *repository.StargazersCount

		print := strings.HasSuffix(repoName, suffix)

		if print {
			repoName = strings.TrimSuffix(repoName, suffix)

			boldCyan.Printf("%s/", repoUsername)
			boldWhite.Printf("%s", repoName)
			fmt.Printf(" (%d)\n", repoStars)
			fmt.Printf("    %s\n", repoDescription)
		}
	}
}

func new(templateName string) {
	repoName := templateName + suffix
	repoURL := "https://github.com/" + repoName + ".git"

	fullPath = path.Join(cwd, name)
	gitopts := &git.CloneOptions{}

	boldWhite.Printf("Retrieving %s via %s\n", templateName, repoURL)
	_, err := git.Clone(repoURL, fullPath, gitopts)

	if err != nil {
		log.Fatal(err)
	} else {
		println()
	}

	config(name, path.Join(fullPath, templateFile))
	askForVariables()
	clean()
	compile()
	commands()
	fmt.Printf("Your %s project was created successfully.\n", templateName)
}

func config(name string, configPath string) {
	configFile, errFile := ioutil.ReadFile(configPath)

	if errFile != nil {
		log.Fatal("Configuration file not present inside template\nError: ", errFile)
	}

	errYaml := yaml.Unmarshal(configFile, &templateConfig)
	if errYaml != nil {
		log.Fatal("Configuration file is not correctly formated\nError: ", errYaml)
	}

	templateConfig.Variables["name"] = name
}

func askForVariables() {
	boldWhite.Println("Assign values to variables [default value]")
	for key, defaultValue := range templateConfig.Variables {
		if key != "name" {
			fmt.Printf("%s [%s]: ", key, defaultValue)

			input := ""
			fmt.Scanln(&input)

			if input != "" {
				templateConfig.Variables[key] = input
			} else {
				templateConfig.Variables[key] = defaultValue
			}
		}
	}
	println()
}

func clean() {
	os.Remove(path.Join(fullPath, templateFile))
	os.RemoveAll(path.Join(fullPath, ".git/"))
	os.Remove(path.Join(fullPath, ".gitignore"))
}

func compile() {
	boldWhite.Printf("Generating fresh 'gut new' %s project:\n", templateConfig.Variables["name"])
	filepath.Walk(fullPath, compileDirs)
	filepath.Walk(fullPath, compileFiles)
	println()
}

func compileDirs(fullPath string, f os.FileInfo, err error) error {
	if f.IsDir() {
		newPath := mustache.Render(fullPath, templateConfig.Variables)
		os.Remove(fullPath)
		os.Mkdir(newPath, f.Mode())
		printCreating(newPath)
	}

	return nil
}

func printCreating(path string) {
	relativePath, err := filepath.Rel(cwd, path)

	if err != nil {
		log.Fatal(err)
	}

	color.New(color.FgGreen).Print("* creating")
	fmt.Printf(" %s\n", relativePath)
}

func compileFiles(fullPath string, f os.FileInfo, err error) error {
	if !f.IsDir() {
		newPath := mustache.Render(fullPath, templateConfig.Variables)
		rendered := mustache.RenderFile(fullPath, templateConfig.Variables)

		os.Remove(fullPath)
		ioutil.WriteFile(newPath, []byte(rendered), f.Mode())

		printCreating(newPath)
	}

	return nil
}

func commands() {
	if len(templateConfig.Commands) != 0 {
		boldWhite.Printf("Template wants to run the following commands with path=%s:\n", fullPath)

		for n, command := range templateConfig.Commands {
			cmdCompiled := mustache.Render(command, templateConfig.Variables)
			templateConfig.Commands[n] = cmdCompiled

			fmt.Printf("%d. ", n+1)
			fmt.Printf("%s", cmdCompiled)
			println()
		}

		println()
		boldWhite.Printf("Do you want to run this commands? [N/y] ")
		input := ""
		fmt.Scanln(&input)

		if input == "y" || input == "Y" {
			for _, cmdTemplate := range templateConfig.Commands {
				color.New(color.FgGreen).Print("* running ")
				fmt.Printf("%s", cmdTemplate)
				println()

				cmdArray := strings.Split(cmdTemplate, " ")
				cmdString := cmdArray[0]
				arguments := cmdArray[1:len(cmdArray)]

				command := exec.Command(cmdString, arguments...)
				command.Dir = fullPath

				out, err := command.Output()
				if err != nil {
					log.Fatal(err)
				} else if string(out) != "" {
					fmt.Printf("> %s", out)
				}
			}
		}
		println()
	}
}

func describe(templateName string) {
	repoName := templateName + suffix
	repoURL := "https://github.com/" + repoName + ".git"

	//change fullpath to temporary directory
	fullPath = path.Join(cwd, name)
	gitopts := &git.CloneOptions{}

	_, err := git.Clone(repoURL, fullPath, gitopts)
	if err != nil {
		log.Fatal(err)
	} else {
		println()
	}

	config(name, path.Join(fullPath, templateFile))

	for key, defaultValue := range templateConfig.Variables {
		if key != "name" {
			fmt.Printf("%s [%s]: ", key, defaultValue)
		}
	}

	for n, command := range templateConfig.Commands {
		fmt.Printf("%d. ", n+1)
		fmt.Printf("%s", command)
		println()
	}
}

// inmediate tasks:
// add show/info/describe template subcommand. clone the repo into a tmp dir to delete after retrieving the information. should print variables, commands, description, creator, etc.
// support templates that remove the cloned folder using the delete folder option
// work on temporary directory? if yes, then use os tempdir function. think if this is useful
// add curl command in readme like docker compose has. generate gox builds
// add homebrew formulas
// add documentation in the readme
// add subcommand to compile current working dir. useful for developing templates
// develop erlang cowboy template
// add option to fill from shell like ./rebar3 new plugin name=demo author_name="Fred H."
// store templates in /home/.config/gut/templates/. only fetch if template not found in that path
// update stored templates
// clone templates with depth one
// paginate to get all answers in search
<commit_msg>Print logs while describing<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"

	"github.com/codegangsta/cli"
	"github.com/fatih/color"
	"github.com/google/go-github/github"
	"github.com/hoisie/mustache"
	git "github.com/libgit2/git2go"
	"gopkg.in/yaml.v2"
)

var (
	version        = "0.1"
	suffix         = "-gut-template"
	templateFile   = "gut.template"
	templateConfig configuration
	cwd            string
	name           string
	fullPath       string
)

var (
	boldCyan  *color.Color
	boldWhite *color.Color
)

type configuration struct {
	Variables map[string]string `yaml:"variables"`
	Options   map[string]string `yaml:"options"`
	Commands  []string          `yaml:"commands"`
}

func main() {
	boldCyan = color.New(color.FgCyan).Add(color.Bold)
	boldWhite = color.New(color.FgWhite).Add(color.Bold)

	cwd, _ = filepath.Abs(filepath.Dir(os.Args[0]))

	app := cli.NewApp()
	app.Name = "gut"
	app.Usage = "tool that retrieves templates and compile them to scaffold projects and create standalone files"
	app.Version = version
	app.Author = "Federico Carrone"
	app.Email = "federico.carrone@gmail.com"

	app.Commands = []cli.Command{
		{
			Name:    "search",
			Aliases: []string{"s"},
			Usage:   "search templates",
			Action: func(c *cli.Context) {
				filter := c.Args().First()
				search(filter)
			},
		},
		{
			Name:    "new",
			Aliases: []string{"n"},
			Usage:   "new",
			Action: func(c *cli.Context) {
				if len(c.Args()) < 2 {
					fmt.Print("Not enough arguments")
				}
				templateName := c.Args().First()
				name = c.Args()[1]
				new(templateName)
			},
		},
		{
			Name:    "describe",
			Aliases: []string{"d"},
			Usage:   "describe",
			Action: func(c *cli.Context) {
				templateName := c.Args().First()
				describe(templateName)
			},
		},
		{
			Name:    "version",
			Aliases: []string{"v"},
			Usage:   "version",
			Action: func(c *cli.Context) {
				fmt.Printf("gut version %s\n", version)
			},
		},
	}

	app.Run(os.Args)
}

func search(filter string) {
	search := filter + suffix
	client := github.NewClient(nil)
	opt := &github.SearchOptions{
		Sort: "stars",
	}

	results, _, error := client.Search.Repositories(search, opt)

	if error != nil {
		log.Fatal(error)
	}

	for _, repository := range results.Repositories {
		repoName := *repository.Name
		repoUsername := *(*repository.Owner).Login
		repoDescription := *repository.Description
		repoStars := *repository.StargazersCount

		print := strings.HasSuffix(repoName, suffix)

		if print {
			repoName = strings.TrimSuffix(repoName, suffix)

			boldCyan.Printf("%s/", repoUsername)
			boldWhite.Printf("%s", repoName)
			fmt.Printf(" (%d)\n", repoStars)
			fmt.Printf("    %s\n", repoDescription)
		}
	}
}

func new(templateName string) {
	repoName := templateName + suffix
	repoURL := "https://github.com/" + repoName + ".git"

	fullPath = path.Join(cwd, name)
	gitopts := &git.CloneOptions{}

	boldWhite.Printf("Retrieving %s via %s\n", templateName, repoURL)
	_, err := git.Clone(repoURL, fullPath, gitopts)

	if err != nil {
		log.Fatal(err)
	} else {
		println()
	}

	config(name, path.Join(fullPath, templateFile))
	askForVariables()
	clean()
	compile()
	commands()
	fmt.Printf("Your %s project was created successfully.\n", templateName)
}

func config(name string, configPath string) {
	configFile, errFile := ioutil.ReadFile(configPath)

	if errFile != nil {
		log.Fatal("Configuration file not present inside template\nError: ", errFile)
	}

	errYaml := yaml.Unmarshal(configFile, &templateConfig)
	if errYaml != nil {
		log.Fatal("Configuration file is not correctly formated\nError: ", errYaml)
	}

	templateConfig.Variables["name"] = name
}

func askForVariables() {
	boldWhite.Println("Assign values to variables [default value]")
	for key, defaultValue := range templateConfig.Variables {
		if key != "name" {
			fmt.Printf("%s [%s]: ", key, defaultValue)

			input := ""
			fmt.Scanln(&input)

			if input != "" {
				templateConfig.Variables[key] = input
			} else {
				templateConfig.Variables[key] = defaultValue
			}
		}
	}
	println()
}

func clean() {
	os.Remove(path.Join(fullPath, templateFile))
	os.RemoveAll(path.Join(fullPath, ".git/"))
	os.Remove(path.Join(fullPath, ".gitignore"))
}

func compile() {
	boldWhite.Printf("Generating fresh 'gut new' %s project:\n", templateConfig.Variables["name"])
	filepath.Walk(fullPath, compileDirs)
	filepath.Walk(fullPath, compileFiles)
	println()
}

func compileDirs(fullPath string, f os.FileInfo, err error) error {
	if f.IsDir() {
		newPath := mustache.Render(fullPath, templateConfig.Variables)
		os.Remove(fullPath)
		os.Mkdir(newPath, f.Mode())
		printCreating(newPath)
	}

	return nil
}

func printCreating(path string) {
	relativePath, err := filepath.Rel(cwd, path)

	if err != nil {
		log.Fatal(err)
	}

	color.New(color.FgGreen).Print("* creating")
	fmt.Printf(" %s\n", relativePath)
}

func compileFiles(fullPath string, f os.FileInfo, err error) error {
	if !f.IsDir() {
		newPath := mustache.Render(fullPath, templateConfig.Variables)
		rendered := mustache.RenderFile(fullPath, templateConfig.Variables)

		os.Remove(fullPath)
		ioutil.WriteFile(newPath, []byte(rendered), f.Mode())

		printCreating(newPath)
	}

	return nil
}

func commands() {
	if len(templateConfig.Commands) != 0 {
		boldWhite.Printf("Template wants to run the following commands with path=%s:\n", fullPath)

		for n, command := range templateConfig.Commands {
			cmdCompiled := mustache.Render(command, templateConfig.Variables)
			templateConfig.Commands[n] = cmdCompiled

			fmt.Printf("%d. ", n+1)
			fmt.Printf("%s", cmdCompiled)
			println()
		}

		println()
		boldWhite.Printf("Do you want to run this commands? [N/y] ")
		input := ""
		fmt.Scanln(&input)

		if input == "y" || input == "Y" {
			for _, cmdTemplate := range templateConfig.Commands {
				color.New(color.FgGreen).Print("* running ")
				fmt.Printf("%s", cmdTemplate)
				println()

				cmdArray := strings.Split(cmdTemplate, " ")
				cmdString := cmdArray[0]
				arguments := cmdArray[1:len(cmdArray)]

				command := exec.Command(cmdString, arguments...)
				command.Dir = fullPath

				out, err := command.Output()
				if err != nil {
					log.Fatal(err)
				} else if string(out) != "" {
					fmt.Printf("> %s", out)
				}
			}
		}
		println()
	}
}

func describe(templateName string) {
	repoName := templateName + suffix
	repoURL := "https://github.com/" + repoName + ".git"

	//change fullpath to temporary directory
	fullPath = path.Join(cwd, "prueba")
	gitopts := &git.CloneOptions{}

	_, err := git.Clone(repoURL, fullPath, gitopts)
	if err != nil {
		log.Fatal(err)
	} else {
		println()
	}

	config(name, path.Join(fullPath, templateFile))

	boldWhite.Println("Variables in template")
	for key, defaultValue := range templateConfig.Variables {
		if key != "name" {
			fmt.Printf("%s [%s]\n", key, defaultValue)
		}
	}

	println()

	boldWhite.Println("Commands in template")
	for n, command := range templateConfig.Commands {
		fmt.Printf("%d. ", n+1)
		fmt.Printf("%s", command)
		println()
	}
}

// inmediate tasks:
// clone to a temporary path with describe. print author, github description and url
// print help a la mix help.  execute command to check it.
// support templates that remove the cloned folder using the delete folder option
// work on temporary directory? if yes, then use os tempdir function. think if this is useful
// add curl command in readme like docker compose has. generate gox builds
// add homebrew formulas
// add documentation in the readme
// add subcommand to compile current working dir. useful for developing templates
// develop erlang cowboy template
// add option to fill from shell like ./rebar3 new plugin name=demo author_name="Fred H."
// store templates in /home/.config/gut/templates/. only fetch if template not found in that path
// update stored templates
// clone templates with depth one
// paginate to get all answers in search
<|endoftext|>


<commit_before>package htm

import (
	"azul3d.org/lmath.v1"
)

type Tree struct {
	Name     string
	Indices  [3]int
	Vertices *[]lmath.Vec3

	T0 *Tree
	T1 *Tree
	T2 *Tree
	T3 *Tree
}

func NewTree(name string, verts *[]lmath.Vec3, i0, i1, i2 int) *Tree {
	return &Tree{
		Name:     name,
		Indices:  [3]int{i0, i1, i2},
		Vertices: verts,
	}
}

func (t *Tree) SubDivide(level int) {
	if len(t.Name) > level {
		return
	}

	i0, i1, i2 := t.Indices[0], t.Indices[1], t.Indices[2]
	v0, v1, v2 := (*t.Vertices)[i0], (*t.Vertices)[i1], (*t.Vertices)[i2]

	w0, _ := v1.Add(v2).Normalized()
	w1, _ := v0.Add(v2).Normalized()
	w2, _ := v0.Add(v1).Normalized()

	*t.Vertices = append(*t.Vertices, w0, w1, w2)

	l := len(*t.Vertices)

	t.T0 = NewTree(t.Name+"0", t.Vertices, i0, l-1, l-2)  // v0, w2, w1
	t.T1 = NewTree(t.Name+"1", t.Vertices, i1, l-3, l-1)  // v1, w0, w2
	t.T2 = NewTree(t.Name+"2", t.Vertices, i2, l-2, l-3)  // v2, w1, w0
	t.T3 = NewTree(t.Name+"3", t.Vertices, l-3, l-2, l-1) // w0, w1, w2

	t.T0.SubDivide(level)
	t.T1.SubDivide(level)
	t.T2.SubDivide(level)
	t.T3.SubDivide(level)
}

func (t *Tree) CollectIndices(indices *[]uint32) {
	if t.T0 == nil {
		*indices = append(*indices, uint32(t.Indices[0]), uint32(t.Indices[1]), uint32(t.Indices[2]))
	} else {
		t.T0.CollectIndices(indices)
		t.T1.CollectIndices(indices)
		t.T2.CollectIndices(indices)
		t.T3.CollectIndices(indices)
	}
}

type HTM struct {
	Vertices *[]lmath.Vec3

	S0, S1, S2, S3 *Tree
	N0, N1, N2, N3 *Tree
}

func New() *HTM {
	verts := []lmath.Vec3{
		{0, 0, 1},
		{1, 0, 0},
		{0, 1, 0},
		{-1, 0, 0},
		{0, -1, 0},
		{0, 0, -1},
	}
	return &HTM{
		Vertices: &verts,

		S0: NewTree("S0", &verts, 1, 5, 2),
		S1: NewTree("S1", &verts, 2, 5, 3),
		S2: NewTree("S2", &verts, 3, 5, 4),
		S3: NewTree("S3", &verts, 4, 5, 1),
		N0: NewTree("N0", &verts, 1, 0, 4),
		N1: NewTree("N1", &verts, 4, 0, 3),
		N2: NewTree("N2", &verts, 3, 0, 2),
		N3: NewTree("N3", &verts, 2, 0, 1),
	}
}

func (h *HTM) SubDivide(level int) {
	h.S0.SubDivide(level)
	h.S1.SubDivide(level)
	h.S2.SubDivide(level)
	h.S3.SubDivide(level)
	h.N0.SubDivide(level)
	h.N1.SubDivide(level)
	h.N2.SubDivide(level)
	h.N3.SubDivide(level)
}

func (h *HTM) Indices() []uint32 {
	var indices []uint32
	h.S0.CollectIndices(&indices)
	h.S1.CollectIndices(&indices)
	h.S2.CollectIndices(&indices)
	h.S3.CollectIndices(&indices)
	h.N0.CollectIndices(&indices)
	h.N1.CollectIndices(&indices)
	h.N2.CollectIndices(&indices)
	h.N3.CollectIndices(&indices)
	return indices
}
<commit_msg>doc strings<commit_after>// package htm implements a hierarchical triangular mesh suitable for graphic display and querying.
package htm

import (
	"azul3d.org/lmath.v1"
)

// Tree represents a node in an HTM struct that can contain indices and be subdivided.
type Tree struct {
	Name     string
	Indices  [3]int
	Vertices *[]lmath.Vec3

	T0 *Tree
	T1 *Tree
	T2 *Tree
	T3 *Tree
}

// NewTree returns an initialized node by the given name with the given index values.
func NewTree(name string, verts *[]lmath.Vec3, i0, i1, i2 int) *Tree {
	return &Tree{
		Name:     name,
		Indices:  [3]int{i0, i1, i2},
		Vertices: verts,
	}
}

// SubDivide calculates the midpoints of the node's triangle and produces four derivative triangles.
func (t *Tree) SubDivide(level int) {
	if len(t.Name) > level {
		return
	}

	i0, i1, i2 := t.Indices[0], t.Indices[1], t.Indices[2]
	v0, v1, v2 := (*t.Vertices)[i0], (*t.Vertices)[i1], (*t.Vertices)[i2]

	_ = t.Indices[3]

	w0, _ := v1.Add(v2).Normalized()
	w1, _ := v0.Add(v2).Normalized()
	w2, _ := v0.Add(v1).Normalized()

	*t.Vertices = append(*t.Vertices, w0, w1, w2)

	l := len(*t.Vertices)

	t.T0 = NewTree(t.Name+"0", t.Vertices, i0, l-1, l-2)  // v0, w2, w1
	t.T1 = NewTree(t.Name+"1", t.Vertices, i1, l-3, l-1)  // v1, w0, w2
	t.T2 = NewTree(t.Name+"2", t.Vertices, i2, l-2, l-3)  // v2, w1, w0
	t.T3 = NewTree(t.Name+"3", t.Vertices, l-3, l-2, l-1) // w0, w1, w2

	t.T0.SubDivide(level)
	t.T1.SubDivide(level)
	t.T2.SubDivide(level)
	t.T3.SubDivide(level)
}

// CollectIndices appends the current node's indices to the slice pointer unless it should recurse.
func (t *Tree) CollectIndices(indices *[]uint32) {
	if t.T0 == nil {
		*indices = append(*indices, uint32(t.Indices[0]), uint32(t.Indices[1]), uint32(t.Indices[2]))
	} else {
		t.T0.CollectIndices(indices)
		t.T1.CollectIndices(indices)
		t.T2.CollectIndices(indices)
		t.T3.CollectIndices(indices)
	}
}

// HTM defines the initial octahedron and allows subdivision nodes.
type HTM struct {
	Vertices *[]lmath.Vec3

	S0, S1, S2, S3 *Tree
	N0, N1, N2, N3 *Tree
}

// New returns an HTM initialized with an initial octahedron.
func New() *HTM {
	verts := []lmath.Vec3{
		{0, 0, 1},
		{1, 0, 0},
		{0, 1, 0},
		{-1, 0, 0},
		{0, -1, 0},
		{0, 0, -1},
	}
	return &HTM{
		Vertices: &verts,

		S0: NewTree("S0", &verts, 1, 5, 2),
		S1: NewTree("S1", &verts, 2, 5, 3),
		S2: NewTree("S2", &verts, 3, 5, 4),
		S3: NewTree("S3", &verts, 4, 5, 1),
		N0: NewTree("N0", &verts, 1, 0, 4),
		N1: NewTree("N1", &verts, 4, 0, 3),
		N2: NewTree("N2", &verts, 3, 0, 2),
		N3: NewTree("N3", &verts, 2, 0, 1),
	}
}

// SubDivide starts a recursion along all root nodes.
func (h *HTM) SubDivide(level int) {
	h.S0.SubDivide(level)
	h.S1.SubDivide(level)
	h.S2.SubDivide(level)
	h.S3.SubDivide(level)
	h.N0.SubDivide(level)
	h.N1.SubDivide(level)
	h.N2.SubDivide(level)
	h.N3.SubDivide(level)
}

// Indices returns a flattened slice of all indices suitable for vertex lookup in native opengl calls.
func (h *HTM) Indices() []uint32 {
	var indices []uint32
	h.S0.CollectIndices(&indices)
	h.S1.CollectIndices(&indices)
	h.S2.CollectIndices(&indices)
	h.S3.CollectIndices(&indices)
	h.N0.CollectIndices(&indices)
	h.N1.CollectIndices(&indices)
	h.N2.CollectIndices(&indices)
	h.N3.CollectIndices(&indices)
	return indices
}
<|endoftext|>