<commit_before>/*

Package rpio provides GPIO access on the Raspberry PI without any need
for external c libraries (ex: WiringPI or BCM2835).

Supports simple operations such as:
- Pin mode/direction (input/output)
- Pin write (high/low)
- Pin read (high/low)
- Pull up/down/off

Example of use:

	rpio.Open()
	defer rpio.Close()

	pin := rpio.Pin(4)
	pin.Output()

	for {
		pin.Toggle()
		time.Sleep(time.Second)
	}

The library use the raw BCM2835 pinouts, not the ports as they are mapped
on the output pins for the raspberry pi

   Rev 1 Raspberry Pi
+------+------+--------+
| GPIO | Phys | Name   |
+------+------+--------+
|   0  |   3  | SDA    |
|   1  |   5  | SCL    |
|   4  |   7  | GPIO 7 |
|   7  |  26  | CE1    |
|   8  |  24  | CE0    |
|   9  |  21  | MISO   |
|  10  |  19  | MOSI   |
|  11  |  23  | SCLK   |
|  14  |   8  | TxD    |
|  15  |  10  | RxD    |
|  17  |  11  | GPIO 0 |
|  18  |  12  | GPIO 1 |
|  21  |  13  | GPIO 2 |
|  22  |  15  | GPIO 3 |
|  23  |  16  | GPIO 4 |
|  24  |  18  | GPIO 5 |
|  25  |  22  | GPIO 6 |
+------+------+--------+

See the spec for full details of the BCM2835 controller:
http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf

*/

package rpio

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"os"
	"reflect"
	"sync"
	"syscall"
	"time"
	"unsafe"
)

type Direction uint8
type Pin uint8
type State uint8
type Pull uint8

// Memory offsets for gpio, see the spec for more details
const (
	bcm2835Base = 0x20000000
	pi1GPIOBase = bcm2835Base + 0x200000
	memLength   = 4096

	pinMask uint32 = 7 // 0b111 - pinmode is 3 bits
)

// Pin direction, a pin can be set in Input or Output mode
const (
	Input Direction = iota
	Output
)

// State of pin, High / Low
const (
	Low State = iota
	High
)

// Pull Up / Down / Off
const (
	PullOff Pull = iota
	PullDown
	PullUp
)

// Arrays for 8 / 32 bit access to memory and a semaphore for write locking
var (
	memlock sync.Mutex
	mem     []uint32
	mem8    []uint8
)

// Set pin as Input
func (pin Pin) Input() {
	PinMode(pin, Input)
}

// Set pin as Output
func (pin Pin) Output() {
	PinMode(pin, Output)
}

// Set pin High
func (pin Pin) High() {
	WritePin(pin, High)
}

// Set pin Low
func (pin Pin) Low() {
	WritePin(pin, Low)
}

// Toggle pin state
func (pin Pin) Toggle() {
	TogglePin(pin)
}

// Set pin Direction
func (pin Pin) Mode(dir Direction) {
	PinMode(pin, dir)
}

// Set pin state (high/low)
func (pin Pin) Write(state State) {
	WritePin(pin, state)
}

// Read pin state (high/low)
func (pin Pin) Read() State {
	return ReadPin(pin)
}

// Set a given pull up/down mode
func (pin Pin) Pull(pull Pull) {
	PullMode(pin, pull)
}

// Pull up pin
func (pin Pin) PullUp() {
	PullMode(pin, PullUp)
}

// Pull down pin
func (pin Pin) PullDown() {
	PullMode(pin, PullDown)
}

// Disable pullup/down on pin
func (pin Pin) PullOff() {
	PullMode(pin, PullOff)
}

// PinMode sets the direction of a given pin (Input or Output)
func PinMode(pin Pin, direction Direction) {

	// Pin fsel register, 0 or 1 depending on bank
	fsel := uint8(pin) / 10
	shift := (uint8(pin) % 10) * 3

	memlock.Lock()
	defer memlock.Unlock()

	if direction == Input {
		mem[fsel] = mem[fsel] &^ (pinMask << shift)
	} else {
		mem[fsel] = (mem[fsel] &^ (pinMask << shift)) | (1 << shift)
	}

}

// WritePin sets a given pin High or Low
// by setting the clear or set registers respectively
func WritePin(pin Pin, state State) {

	p := uint8(pin)

	// Clear register, 10 / 11 depending on bank
	// Set register, 7 / 8 depending on bank
	clearReg := p/32 + 10
	setReg := p/32 + 7

	memlock.Lock()
	defer memlock.Unlock()

	if state == Low {
		mem[clearReg] = 1 << (p & 31)
	} else {
		mem[setReg] = 1 << (p & 31)
	}

}

// Read the state of a pin
func ReadPin(pin Pin) State {
	// Input level register offset (13 / 14 depending on bank)
	levelReg := uint8(pin)/32 + 13

	if (mem[levelReg] & (1 << uint8(pin))) != 0 {
		return High
	}

	return Low
}

// Toggle a pin state (high -> low -> high)
// TODO: probably possible to do this much faster without read
func TogglePin(pin Pin) {
	switch ReadPin(pin) {
	case Low:
		pin.High()
	case High:
		pin.Low()
	}
}

func PullMode(pin Pin, pull Pull) {
	// Pull up/down/off register has offset 38 / 39, pull is 37
	pullClkReg := uint8(pin)/32 + 38
	pullReg := 37
	shift := (uint8(pin) % 32)

	memlock.Lock()
	defer memlock.Unlock()

	switch pull {
	case PullDown, PullUp:
		mem[pullReg] = mem[pullReg]&^3 | uint32(pull)
	case PullOff:
		mem[pullReg] = mem[pullReg] &^ 3
	}

	// Wait for value to clock in, this is ugly, sorry :(
	time.Sleep(time.Microsecond)

	mem[pullClkReg] = 1 << shift

	// Wait for value to clock in
	time.Sleep(time.Microsecond)

	mem[pullReg] = mem[pullReg] &^ 3
	mem[pullClkReg] = 0

}

// Open and memory map GPIO memory range from /dev/mem .
// Some reflection magic is used to convert it to a unsafe []uint32 pointer
func Open() (err error) {
	var file *os.File

	// Open fd for rw mem access
	file, err = os.OpenFile(
		"/dev/mem",
		os.O_RDWR|os.O_SYNC,
		0)

	if err != nil {
		return
	}

	// FD can be closed after memory mapping
	defer file.Close()

	memlock.Lock()
	defer memlock.Unlock()

	// Memory map GPIO registers to byte array
	mem8, err = syscall.Mmap(
		int(file.Fd()),
		getGPIOBase(),
		memLength,
		syscall.PROT_READ|syscall.PROT_WRITE,
		syscall.MAP_SHARED)

	if err != nil {
		return
	}

	// Convert mapped byte memory to unsafe []uint32 pointer, adjust length as needed
	header := *(*reflect.SliceHeader)(unsafe.Pointer(&mem8))
	header.Len /= (32 / 8) // (32 bit = 4 bytes)
	header.Cap /= (32 / 8)

	mem = *(*[]uint32)(unsafe.Pointer(&header))

	return nil
}

// Close unmaps GPIO memory
func Close() error {
	memlock.Lock()
	defer memlock.Unlock()
	return syscall.Munmap(mem8)
}

// Read /proc/cpuinfo and detect whether we're running on an RPI1 or RPI2.
// Returns 1 or 2 depending on platform.
func getGPIOBase() (base int64) {
	base = pi1GPIOBase
	ranges, err := os.Open("/proc/device-tree/soc/ranges")
	defer ranges.Close()
	if err != nil {
		return
	}
	b := make([]byte, 4)
	n, err := ranges.ReadAt(b, 4)
	if n != 4 || err != nil {
		return
	}
	buf := bytes.NewReader(b)
	var out uint32
	err = binary.Read(buf, binary.BigEndian, &out)
	if err != nil {
		return
	}
	fmt.Printf("%X", out)
	return int64(out + 0x200000)
}
<commit_msg>Fixed the comment for getGPIOBase()<commit_after>/*

Package rpio provides GPIO access on the Raspberry PI without any need
for external c libraries (ex: WiringPI or BCM2835).

Supports simple operations such as:
- Pin mode/direction (input/output)
- Pin write (high/low)
- Pin read (high/low)
- Pull up/down/off

Example of use:

	rpio.Open()
	defer rpio.Close()

	pin := rpio.Pin(4)
	pin.Output()

	for {
		pin.Toggle()
		time.Sleep(time.Second)
	}

The library use the raw BCM2835 pinouts, not the ports as they are mapped
on the output pins for the raspberry pi

   Rev 1 Raspberry Pi
+------+------+--------+
| GPIO | Phys | Name   |
+------+------+--------+
|   0  |   3  | SDA    |
|   1  |   5  | SCL    |
|   4  |   7  | GPIO 7 |
|   7  |  26  | CE1    |
|   8  |  24  | CE0    |
|   9  |  21  | MISO   |
|  10  |  19  | MOSI   |
|  11  |  23  | SCLK   |
|  14  |   8  | TxD    |
|  15  |  10  | RxD    |
|  17  |  11  | GPIO 0 |
|  18  |  12  | GPIO 1 |
|  21  |  13  | GPIO 2 |
|  22  |  15  | GPIO 3 |
|  23  |  16  | GPIO 4 |
|  24  |  18  | GPIO 5 |
|  25  |  22  | GPIO 6 |
+------+------+--------+

See the spec for full details of the BCM2835 controller:
http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf

*/

package rpio

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"os"
	"reflect"
	"sync"
	"syscall"
	"time"
	"unsafe"
)

type Direction uint8
type Pin uint8
type State uint8
type Pull uint8

// Memory offsets for gpio, see the spec for more details
const (
	bcm2835Base = 0x20000000
	pi1GPIOBase = bcm2835Base + 0x200000
	memLength   = 4096

	pinMask uint32 = 7 // 0b111 - pinmode is 3 bits
)

// Pin direction, a pin can be set in Input or Output mode
const (
	Input Direction = iota
	Output
)

// State of pin, High / Low
const (
	Low State = iota
	High
)

// Pull Up / Down / Off
const (
	PullOff Pull = iota
	PullDown
	PullUp
)

// Arrays for 8 / 32 bit access to memory and a semaphore for write locking
var (
	memlock sync.Mutex
	mem     []uint32
	mem8    []uint8
)

// Set pin as Input
func (pin Pin) Input() {
	PinMode(pin, Input)
}

// Set pin as Output
func (pin Pin) Output() {
	PinMode(pin, Output)
}

// Set pin High
func (pin Pin) High() {
	WritePin(pin, High)
}

// Set pin Low
func (pin Pin) Low() {
	WritePin(pin, Low)
}

// Toggle pin state
func (pin Pin) Toggle() {
	TogglePin(pin)
}

// Set pin Direction
func (pin Pin) Mode(dir Direction) {
	PinMode(pin, dir)
}

// Set pin state (high/low)
func (pin Pin) Write(state State) {
	WritePin(pin, state)
}

// Read pin state (high/low)
func (pin Pin) Read() State {
	return ReadPin(pin)
}

// Set a given pull up/down mode
func (pin Pin) Pull(pull Pull) {
	PullMode(pin, pull)
}

// Pull up pin
func (pin Pin) PullUp() {
	PullMode(pin, PullUp)
}

// Pull down pin
func (pin Pin) PullDown() {
	PullMode(pin, PullDown)
}

// Disable pullup/down on pin
func (pin Pin) PullOff() {
	PullMode(pin, PullOff)
}

// PinMode sets the direction of a given pin (Input or Output)
func PinMode(pin Pin, direction Direction) {

	// Pin fsel register, 0 or 1 depending on bank
	fsel := uint8(pin) / 10
	shift := (uint8(pin) % 10) * 3

	memlock.Lock()
	defer memlock.Unlock()

	if direction == Input {
		mem[fsel] = mem[fsel] &^ (pinMask << shift)
	} else {
		mem[fsel] = (mem[fsel] &^ (pinMask << shift)) | (1 << shift)
	}

}

// WritePin sets a given pin High or Low
// by setting the clear or set registers respectively
func WritePin(pin Pin, state State) {

	p := uint8(pin)

	// Clear register, 10 / 11 depending on bank
	// Set register, 7 / 8 depending on bank
	clearReg := p/32 + 10
	setReg := p/32 + 7

	memlock.Lock()
	defer memlock.Unlock()

	if state == Low {
		mem[clearReg] = 1 << (p & 31)
	} else {
		mem[setReg] = 1 << (p & 31)
	}

}

// Read the state of a pin
func ReadPin(pin Pin) State {
	// Input level register offset (13 / 14 depending on bank)
	levelReg := uint8(pin)/32 + 13

	if (mem[levelReg] & (1 << uint8(pin))) != 0 {
		return High
	}

	return Low
}

// Toggle a pin state (high -> low -> high)
// TODO: probably possible to do this much faster without read
func TogglePin(pin Pin) {
	switch ReadPin(pin) {
	case Low:
		pin.High()
	case High:
		pin.Low()
	}
}

func PullMode(pin Pin, pull Pull) {
	// Pull up/down/off register has offset 38 / 39, pull is 37
	pullClkReg := uint8(pin)/32 + 38
	pullReg := 37
	shift := (uint8(pin) % 32)

	memlock.Lock()
	defer memlock.Unlock()

	switch pull {
	case PullDown, PullUp:
		mem[pullReg] = mem[pullReg]&^3 | uint32(pull)
	case PullOff:
		mem[pullReg] = mem[pullReg] &^ 3
	}

	// Wait for value to clock in, this is ugly, sorry :(
	time.Sleep(time.Microsecond)

	mem[pullClkReg] = 1 << shift

	// Wait for value to clock in
	time.Sleep(time.Microsecond)

	mem[pullReg] = mem[pullReg] &^ 3
	mem[pullClkReg] = 0

}

// Open and memory map GPIO memory range from /dev/mem .
// Some reflection magic is used to convert it to a unsafe []uint32 pointer
func Open() (err error) {
	var file *os.File

	// Open fd for rw mem access
	file, err = os.OpenFile(
		"/dev/mem",
		os.O_RDWR|os.O_SYNC,
		0)

	if err != nil {
		return
	}

	// FD can be closed after memory mapping
	defer file.Close()

	memlock.Lock()
	defer memlock.Unlock()

	// Memory map GPIO registers to byte array
	mem8, err = syscall.Mmap(
		int(file.Fd()),
		getGPIOBase(),
		memLength,
		syscall.PROT_READ|syscall.PROT_WRITE,
		syscall.MAP_SHARED)

	if err != nil {
		return
	}

	// Convert mapped byte memory to unsafe []uint32 pointer, adjust length as needed
	header := *(*reflect.SliceHeader)(unsafe.Pointer(&mem8))
	header.Len /= (32 / 8) // (32 bit = 4 bytes)
	header.Cap /= (32 / 8)

	mem = *(*[]uint32)(unsafe.Pointer(&header))

	return nil
}

// Close unmaps GPIO memory
func Close() error {
	memlock.Lock()
	defer memlock.Unlock()
	return syscall.Munmap(mem8)
}

// Read /proc/device-tree/soc/ranges and determine the base address.
// Use the default Raspberry Pi 1 base address if this fails.
func getGPIOBase() (base int64) {
	base = pi1GPIOBase
	ranges, err := os.Open("/proc/device-tree/soc/ranges")
	defer ranges.Close()
	if err != nil {
		return
	}
	b := make([]byte, 4)
	n, err := ranges.ReadAt(b, 4)
	if n != 4 || err != nil {
		return
	}
	buf := bytes.NewReader(b)
	var out uint32
	err = binary.Read(buf, binary.BigEndian, &out)
	if err != nil {
		return
	}
	fmt.Printf("%X", out)
	return int64(out + 0x200000)
}
<|endoftext|>


<commit_before>package controllers

import (
	"chant/app/models"
	"encoding/json"
	"encoding/xml"
	"io"
	"net/http"
	"net/url"
	"regexp"

	"chant/app/chatroom"

	"github.com/revel/revel"
)

// APIv1 ...
type APIv1 struct {
	*revel.Controller
}

// RoomStamps とりあえず
func (c APIv1) RoomStamps(id, token string) revel.Result {
	if !chatroom.Exists(id) {
		return c.RenderJson(map[string]interface{}{
			"stamps": []interface{}{},
		})
	}
	room := chatroom.GetRoom(id, token)
	if room == nil {
		return c.RenderJson(map[string]interface{}{
			"stamps": []interface{}{},
		})
	}
	stamps := room.Repo.GetAllStamps()
	return c.RenderJson(map[string]interface{}{
		"stamps": stamps,
	})
}

// RoomMessages とりあえず
func (c APIv1) RoomMessages(id, token string) revel.Result {
	if !chatroom.Exists(id) {
		return c.RenderJson(map[string]interface{}{
			"messages": []interface{}{},
		})
	}
	room := chatroom.GetRoom(id, token)
	if room == nil {
		return c.RenderJson(map[string]interface{}{
			"stamps": []interface{}{},
		})
	}
	// メッセージアーカイブを、最新の、最大10件を取得する
	messages := room.Repo.GetMessages(10, -1)
	return c.RenderJson(map[string]interface{}{
		"messages": messages,
	})
}

// RoomSay とりあえず
func (c APIv1) RoomSay(id, token string) revel.Result {
	c.Request.Format = "json"
	// FIXME: めんどいからここで
	type params struct {
		Type  string       `json:"type"`
		Value string       `json:"value"`
		User  *models.User `json:"user"`
	}
	p := new(params)
	if err := json.NewDecoder(c.Request.Body).Decode(p); err != nil {
		c.Response.Status = http.StatusBadRequest
		return c.RenderError(err)
	}

	raw, err := json.Marshal(struct {
		Type string `json:"type"`
		Raw  string `json:"raw"`
	}{p.Type, p.Value})

	if err != nil {
		c.Response.Status = http.StatusBadRequest
		return revel.ErrorResult{
			Error: err,
		}
	}

	room := chatroom.GetRoom(id, chatroom.PrivilegeAPIToken)

	event, err := room.Say(p.User, string(raw))
	if err != nil {
		c.Response.Status = http.StatusBadRequest
		return revel.ErrorResult{
			Error: err,
		}
	}

	return c.RenderJson(map[string]interface{}{
		"params":  p,
		"created": event,
	})
}

// WebPreview ...
func (c APIv1) WebPreview(u string) revel.Result {
	res, err := http.Get(u)
	if err != nil {
		return c.RenderJson(map[string]interface{}{})
	}
	if regexp.MustCompile("^ima?ge?/.*").MatchString(res.Header.Get("Content-Type")) {
		return c.RenderJson(map[string]interface{}{
			"content": "image",
			"url":     u,
		})
	}
	page := new(HTMLPage)
	decoder(res.Body).Decode(page)

	return c.RenderJson(map[string]interface{}{
		"html":    page,
		"summary": page.Summarize(u),
	})
}

func decoder(reader io.Reader) *xml.Decoder {
	dec := xml.NewDecoder(reader)
	dec.Strict = false
	dec.AutoClose = xml.HTMLAutoClose
	dec.Entity = xml.HTMLEntity
	return dec
}

// Summary ...
type Summary struct {
	Title       string `json:"title"`
	Image       string `json:"image"`
	Description string `json:"description"`
	URL         string `json:"url"`
}

func (summary *Summary) setTitle(c string) {
	if len(summary.Title) != 0 || len(c) == 0 {
		return
	}
	summary.Title = c
}
func (summary *Summary) setImage(c string) {
	if len(summary.Image) != 0 || len(c) == 0 {
		return
	}
	summary.Image = c
}
func (summary *Summary) setDescription(c string) {
	if len(summary.Description) != 0 || len(c) == 0 {
		return
	}
	summary.Description = c
}

// HTMLPage ...
type HTMLPage struct {
	Head struct {
		Title string `json:"title" xml:"title"`
		Metas []Meta `json:"metas" xml:"meta"`
		Links []Link `json:"links" xml:"link"`
	} `json:"head" xml:"head"`
} // `xml:"html"`

// Meta ...
type Meta struct {
	Property string `json:"property" xml:"property,attr"`
	Name     string `json:"name"     xml:"name,attr"`
	Content  string `json:"content"  xml:"content,attr"`
}

// Link ...
type Link struct {
	Rel  string `json:"rel"  xml:"rel,attr"`
	Href string `json:"href" xml:"href,attr"`
}

// Summarize ...
func (hp *HTMLPage) Summarize(u string) *Summary {
	summary := new(Summary)
	summary.URL = u
	img := regexp.MustCompile("image")
	desc := regexp.MustCompile("description")
	title := regexp.MustCompile("title")
	icon := regexp.MustCompile("icon")
	for _, meta := range hp.Head.Metas {
		switch {
		case img.MatchString(meta.Property):
			summary.setImage(meta.Content)
		case desc.MatchString(meta.Property):
			summary.setDescription(meta.Content)
		case title.MatchString(meta.Property):
			summary.setTitle(meta.Content)
		}
	}
	summary.setTitle(hp.Head.Title)
	summary.setDescription(hp.Head.Title)
	if len(summary.Image) == 0 {
		for _, link := range hp.Head.Links {
			if icon.MatchString(link.Rel) {
				summary.setImage(link.Href)
				break
			}
		}
	}
	if len(summary.Image) == 0 {
		v, _ := url.Parse(u)
		summary.setImage(v.Scheme + "://" + v.Host + "/favicon.ico")
	}
	return summary
}
<commit_msg>Fix #145<commit_after>package controllers

import (
	"bytes"
	"chant/app/models"
	"encoding/json"
	"encoding/xml"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"regexp"

	"chant/app/chatroom"

	"github.com/revel/revel"
)

// APIv1 ...
type APIv1 struct {
	*revel.Controller
}

// RoomStamps とりあえず
func (c APIv1) RoomStamps(id, token string) revel.Result {
	if !chatroom.Exists(id) {
		return c.RenderJson(map[string]interface{}{
			"stamps": []interface{}{},
		})
	}
	room := chatroom.GetRoom(id, token)
	if room == nil {
		return c.RenderJson(map[string]interface{}{
			"stamps": []interface{}{},
		})
	}
	stamps := room.Repo.GetAllStamps()
	return c.RenderJson(map[string]interface{}{
		"stamps": stamps,
	})
}

// RoomMessages とりあえず
func (c APIv1) RoomMessages(id, token string) revel.Result {
	if !chatroom.Exists(id) {
		return c.RenderJson(map[string]interface{}{
			"messages": []interface{}{},
		})
	}
	room := chatroom.GetRoom(id, token)
	if room == nil {
		return c.RenderJson(map[string]interface{}{
			"stamps": []interface{}{},
		})
	}
	// メッセージアーカイブを、最新の、最大10件を取得する
	messages := room.Repo.GetMessages(10, -1)
	return c.RenderJson(map[string]interface{}{
		"messages": messages,
	})
}

// RoomSay とりあえず
func (c APIv1) RoomSay(id, token string) revel.Result {
	c.Request.Format = "json"
	// FIXME: めんどいからここで
	type params struct {
		Type  string       `json:"type"`
		Value string       `json:"value"`
		User  *models.User `json:"user"`
	}
	p := new(params)
	if err := json.NewDecoder(c.Request.Body).Decode(p); err != nil {
		c.Response.Status = http.StatusBadRequest
		return c.RenderError(err)
	}

	raw, err := json.Marshal(struct {
		Type string `json:"type"`
		Raw  string `json:"raw"`
	}{p.Type, p.Value})

	if err != nil {
		c.Response.Status = http.StatusBadRequest
		return revel.ErrorResult{
			Error: err,
		}
	}

	room := chatroom.GetRoom(id, chatroom.PrivilegeAPIToken)

	event, err := room.Say(p.User, string(raw))
	if err != nil {
		c.Response.Status = http.StatusBadRequest
		return revel.ErrorResult{
			Error: err,
		}
	}

	return c.RenderJson(map[string]interface{}{
		"params":  p,
		"created": event,
	})
}

// WebPreview ...
func (c APIv1) WebPreview(u string) revel.Result {
	res, err := http.Get(u)
	if err != nil {
		return c.RenderJson(map[string]interface{}{})
	}
	if regexp.MustCompile("^ima?ge?/.*").MatchString(res.Header.Get("Content-Type")) {
		return c.RenderJson(map[string]interface{}{
			"content": "image",
			"url":     u,
		})
	}

	// clean response body
	b, _ := ioutil.ReadAll(res.Body)
	b = regexp.MustCompile("\\<script[\\S\\s]+?\\</script\\>").ReplaceAll(b, []byte{})
	buf := bytes.NewBuffer(b)

	page := new(HTMLPage)
	err = decoder(buf).Decode(page)
	if err != nil {
		log.Println("[WebPreview]", err)
	}

	return c.RenderJson(map[string]interface{}{
		"html":    page,
		"summary": page.Summarize(u),
	})
}

func decoder(reader io.Reader) *xml.Decoder {
	dec := xml.NewDecoder(reader)
	dec.Strict = false
	dec.AutoClose = xml.HTMLAutoClose
	dec.Entity = xml.HTMLEntity
	return dec
}

// Summary ...
type Summary struct {
	Title       string `json:"title"`
	Image       string `json:"image"`
	Description string `json:"description"`
	URL         string `json:"url"`
}

func (summary *Summary) setTitle(c string) {
	if len(summary.Title) != 0 || len(c) == 0 {
		return
	}
	summary.Title = c
}
func (summary *Summary) setImage(c string) {
	if len(summary.Image) != 0 || len(c) == 0 {
		return
	}
	summary.Image = c
}
func (summary *Summary) setDescription(c string) {
	if len(summary.Description) != 0 || len(c) == 0 {
		return
	}
	summary.Description = c
}

// HTMLPage ...
type HTMLPage struct {
	Head struct {
		Title string `json:"title" xml:"title"`
		Metas []Meta `json:"metas" xml:"meta"`
		Links []Link `json:"links" xml:"link"`
	} `json:"head" xml:"head"`
} // `xml:"html"`

// Meta ...
type Meta struct {
	Property string `json:"property" xml:"property,attr"`
	Name     string `json:"name"     xml:"name,attr"`
	Content  string `json:"content"  xml:"content,attr"`
}

// Link ...
type Link struct {
	Rel  string `json:"rel"  xml:"rel,attr"`
	Href string `json:"href" xml:"href,attr"`
}

// Summarize ...
func (hp *HTMLPage) Summarize(u string) *Summary {
	summary := new(Summary)
	summary.URL = u
	img := regexp.MustCompile("image")
	desc := regexp.MustCompile("description")
	title := regexp.MustCompile("title")
	icon := regexp.MustCompile("icon")
	for _, meta := range hp.Head.Metas {
		switch {
		case img.MatchString(meta.Property):
			summary.setImage(meta.Content)
		case desc.MatchString(meta.Property):
			summary.setDescription(meta.Content)
		case title.MatchString(meta.Property):
			summary.setTitle(meta.Content)
		}
	}
	summary.setTitle(hp.Head.Title)
	summary.setDescription(hp.Head.Title)
	if len(summary.Image) == 0 {
		for _, link := range hp.Head.Links {
			if icon.MatchString(link.Rel) {
				summary.setImage(link.Href)
				break
			}
		}
	}
	if len(summary.Image) == 0 {
		v, _ := url.Parse(u)
		summary.setImage(v.Scheme + "://" + v.Host + "/favicon.ico")
	}
	return summary
}
<|endoftext|>


<commit_before>// Copyright (C) 2017 Damon Revoe. All rights reserved.
// Use of this source code is governed by the MIT
// license, which can be found in the LICENSE file.

package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"text/template"
)

type templateParams map[string]interface{}

type fileParams struct {
	filename string
	params   templateParams
}

type pathnameTemplateMultiplier struct {
	paramName    string
	paramValues  []string
	continuation pathnameTemplateText
}

type pathnameTemplateText struct {
	text string
	next *pathnameTemplateMultiplier
}

// Subst updates the 'pathnameTemplateText' receiver by replacing all
// instances of 'name' surrounded by braces with 'value', which can be
// either a string or a slice of strings.  In the latter case, the text
// in the receiver structure gets truncated by the substitution and the
// receiver structure gets extended by a new pathnameTemplateMultiplier
// structure.  Subst returns the number of substitution values.
func (t *pathnameTemplateText) subst(name string, value interface{}) int {
	if textValue, ok := value.(string); ok {
		t.text = strings.Replace(t.text, "{"+name+"}",
			textValue, -1)
	} else if arrayValue, ok := value.([]string); !ok {
		t.text = strings.Replace(t.text, "{"+name+"}",
			fmt.Sprint(value), -1)
	} else if pos := strings.Index(t.text, "{"+name+"}"); pos >= 0 {
		t.next = &pathnameTemplateMultiplier{name, arrayValue,
			pathnameTemplateText{t.text[pos+len(name)+2:], t.next}}
		t.text = t.text[:pos]
		return len(arrayValue)
	}

	return 1
}

// ExpandPathnameTemplate takes a pathname template and subsitutes
// template parameter names with their values. Parameter values can be
// either strings or slices of strings. Each template value that is a
// slice of strings multiplies the number of output strings by the number
// of strings in the slice.
func expandPathnameTemplate(pathname string,
	params templateParams) []fileParams {
	root := pathnameTemplateText{pathname, nil}

	resultSize := 1

	for name, value := range params {
		resultSize *= root.subst(name, value)

		for n := root.next; n != nil; n = n.continuation.next {
			resultSize *= n.continuation.subst(name, value)
		}
	}

	result := make([]fileParams, resultSize)

	for i := 0; i < resultSize; i++ {
		result[i].filename = root.text
		copyOfParams := templateParams{}
		for name, value := range params {
			copyOfParams[name] = value
		}
		result[i].params = copyOfParams
	}

	if resultSize == 0 {
		return result
	}

	sliceSize := 1

	for a := root.next; a != nil; a = a.continuation.next {
		numberOfValues := len(a.paramValues)

		continuationText := a.continuation.text

		for i := 0; i < resultSize; {
			for j := 0; j < numberOfValues; j++ {
				value := a.paramValues[j]
				filenameFragment := value + continuationText
				for k := 0; k < sliceSize; k++ {
					result[i].filename += filenameFragment
					result[i].params[a.paramName] = value
					i++
				}
			}
		}

		sliceSize *= numberOfValues
	}

	return result
}

func must(err error) {
	if err != nil {
		panic(err)
	}
}

func varName(arg string) string {
	return strings.Map(func(r rune) rune {
		if r >= 'A' && r <= 'Z' || r >= 'a' && r <= 'z' ||
			r >= '0' && r <= '9' {
			return r
		} else if r == '+' {
			return 'x'
		}
		return '_'
	}, arg)
}

func varNameUC(arg string) string {
	return strings.Map(func(r rune) rune {
		if r >= 'a' && r <= 'z' {
			return r - 'a' + 'A'
		} else if r >= 'A' && r <= 'Z' || r >= '0' && r <= '9' {
			return r
		} else if r == '+' {
			return 'X'
		}
		return '_'
	}, arg)
}

func libName(arg string) string {
	return strings.Map(func(r rune) rune {
		if r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' ||
			r >= '0' && r <= '9' ||
			r == '+' || r == '-' || r == '.' {
			return r
		}
		return '_'
	}, arg)
}

var funcMap template.FuncMap = template.FuncMap{
	"VarName":   varName,
	"VarNameUC": varNameUC,
	"LibName":   libName,
}

// GetTemplateWalkFunc returns a walker function for use with filepath.Walk().
// The returned function interprets each file it visits as a 'text/template'
// file and generates a new file with the same relative pathname in the output
// directory 'projectDir'.
func getTemplateWalkFunc(templateDir, projectDir string,
	params templateParams) filepath.WalkFunc {
	return func(templateFile string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() {
			return nil
		}

		// Panic if filepath.Walk() does not behave as expected.
		if !strings.HasPrefix(templateFile, templateDir) {
			panic(templateFile + " does not start with " +
				templateDir)
		}

		// Read the contents of the template file. Cannot use
		// template.ParseFiles() because a Funcs() call must be
		// made between New() and Parse().
		templateContents, err := ioutil.ReadFile(templateFile)
		if err != nil {
			return err
		}

		// Pathname of the template file relative to the
		// template directory.
		templateFile = templateFile[len(templateDir):]

		// Ignore package definition file for the template.
		if templateFile == filepath.Base(templateDir)+".yaml" {
			return nil
		}

		// Parse the template file. The parsed template will be
		// reused multiple times if expandPathnameTemplate()
		// returns more than one pathname expansion.
		t, err := template.New(filepath.Base(templateFile)).Funcs(
			funcMap).Parse(string(templateContents))
		if err != nil {
			return err
		}

		for _, fp := range expandPathnameTemplate(
			templateFile, params) {

			projectFile := projectDir + fp.filename

			if err = os.MkdirAll(filepath.Dir(projectFile),
				os.ModePerm); err != nil {
				return err
			}

			buffer := bytes.NewBufferString("")

			if err = t.Execute(buffer, fp.params); err != nil {
				return err
			}

			newContents := buffer.Bytes()

			oldContents, err := ioutil.ReadFile(projectFile)

			if os.IsNotExist(err) {
				fmt.Println("A", projectFile)
			} else if bytes.Compare(oldContents, newContents) != 0 {
				fmt.Println("U", projectFile)
			} else {
				return nil
			}

			if err = ioutil.WriteFile(projectFile, newContents,
				info.Mode()); err != nil {
				return err
			}
		}

		return nil
	}
}

// For each source file in 'templateDir', generateBuildFilesFromProjectTemplate
// generates an output file with the same relative pathname inside 'projectDir'.
func generateBuildFilesFromProjectTemplate(templateDir,
	projectDir string, params templateParams) error {

	templateDir = filepath.Clean(templateDir) + string(filepath.Separator)
	projectDir = filepath.Clean(projectDir) + string(filepath.Separator)

	if err := filepath.Walk(templateDir, getTemplateWalkFunc(templateDir,
		projectDir, params)); err != nil {
		return err
	}

	return nil
}
<commit_msg>Fix a typo<commit_after>// Copyright (C) 2017 Damon Revoe. All rights reserved.
// Use of this source code is governed by the MIT
// license, which can be found in the LICENSE file.

package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"text/template"
)

type templateParams map[string]interface{}

type fileParams struct {
	filename string
	params   templateParams
}

type pathnameTemplateMultiplier struct {
	paramName    string
	paramValues  []string
	continuation pathnameTemplateText
}

type pathnameTemplateText struct {
	text string
	next *pathnameTemplateMultiplier
}

// Subst updates the 'pathnameTemplateText' receiver by replacing all
// instances of 'name' surrounded by braces with 'value', which can be
// either a string or a slice of strings.  In the latter case, the text
// in the receiver structure gets truncated by the substitution and the
// receiver structure gets extended by a new pathnameTemplateMultiplier
// structure.  Subst returns the number of substitution values.
func (t *pathnameTemplateText) subst(name string, value interface{}) int {
	if textValue, ok := value.(string); ok {
		t.text = strings.Replace(t.text, "{"+name+"}",
			textValue, -1)
	} else if arrayValue, ok := value.([]string); !ok {
		t.text = strings.Replace(t.text, "{"+name+"}",
			fmt.Sprint(value), -1)
	} else if pos := strings.Index(t.text, "{"+name+"}"); pos >= 0 {
		t.next = &pathnameTemplateMultiplier{name, arrayValue,
			pathnameTemplateText{t.text[pos+len(name)+2:], t.next}}
		t.text = t.text[:pos]
		return len(arrayValue)
	}

	return 1
}

// ExpandPathnameTemplate takes a pathname template and substitutes
// template parameter names with their values. Parameter values can be
// either strings or slices of strings. Each template value that is a
// slice of strings multiplies the number of output strings by the number
// of strings in the slice.
func expandPathnameTemplate(pathname string,
	params templateParams) []fileParams {
	root := pathnameTemplateText{pathname, nil}

	resultSize := 1

	for name, value := range params {
		resultSize *= root.subst(name, value)

		for n := root.next; n != nil; n = n.continuation.next {
			resultSize *= n.continuation.subst(name, value)
		}
	}

	result := make([]fileParams, resultSize)

	for i := 0; i < resultSize; i++ {
		result[i].filename = root.text
		copyOfParams := templateParams{}
		for name, value := range params {
			copyOfParams[name] = value
		}
		result[i].params = copyOfParams
	}

	if resultSize == 0 {
		return result
	}

	sliceSize := 1

	for a := root.next; a != nil; a = a.continuation.next {
		numberOfValues := len(a.paramValues)

		continuationText := a.continuation.text

		for i := 0; i < resultSize; {
			for j := 0; j < numberOfValues; j++ {
				value := a.paramValues[j]
				filenameFragment := value + continuationText
				for k := 0; k < sliceSize; k++ {
					result[i].filename += filenameFragment
					result[i].params[a.paramName] = value
					i++
				}
			}
		}

		sliceSize *= numberOfValues
	}

	return result
}

func must(err error) {
	if err != nil {
		panic(err)
	}
}

func varName(arg string) string {
	return strings.Map(func(r rune) rune {
		if r >= 'A' && r <= 'Z' || r >= 'a' && r <= 'z' ||
			r >= '0' && r <= '9' {
			return r
		} else if r == '+' {
			return 'x'
		}
		return '_'
	}, arg)
}

func varNameUC(arg string) string {
	return strings.Map(func(r rune) rune {
		if r >= 'a' && r <= 'z' {
			return r - 'a' + 'A'
		} else if r >= 'A' && r <= 'Z' || r >= '0' && r <= '9' {
			return r
		} else if r == '+' {
			return 'X'
		}
		return '_'
	}, arg)
}

func libName(arg string) string {
	return strings.Map(func(r rune) rune {
		if r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' ||
			r >= '0' && r <= '9' ||
			r == '+' || r == '-' || r == '.' {
			return r
		}
		return '_'
	}, arg)
}

var funcMap template.FuncMap = template.FuncMap{
	"VarName":   varName,
	"VarNameUC": varNameUC,
	"LibName":   libName,
}

// GetTemplateWalkFunc returns a walker function for use with filepath.Walk().
// The returned function interprets each file it visits as a 'text/template'
// file and generates a new file with the same relative pathname in the output
// directory 'projectDir'.
func getTemplateWalkFunc(templateDir, projectDir string,
	params templateParams) filepath.WalkFunc {
	return func(templateFile string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() {
			return nil
		}

		// Panic if filepath.Walk() does not behave as expected.
		if !strings.HasPrefix(templateFile, templateDir) {
			panic(templateFile + " does not start with " +
				templateDir)
		}

		// Read the contents of the template file. Cannot use
		// template.ParseFiles() because a Funcs() call must be
		// made between New() and Parse().
		templateContents, err := ioutil.ReadFile(templateFile)
		if err != nil {
			return err
		}

		// Pathname of the template file relative to the
		// template directory.
		templateFile = templateFile[len(templateDir):]

		// Ignore package definition file for the template.
		if templateFile == filepath.Base(templateDir)+".yaml" {
			return nil
		}

		// Parse the template file. The parsed template will be
		// reused multiple times if expandPathnameTemplate()
		// returns more than one pathname expansion.
		t, err := template.New(filepath.Base(templateFile)).Funcs(
			funcMap).Parse(string(templateContents))
		if err != nil {
			return err
		}

		for _, fp := range expandPathnameTemplate(
			templateFile, params) {

			projectFile := projectDir + fp.filename

			if err = os.MkdirAll(filepath.Dir(projectFile),
				os.ModePerm); err != nil {
				return err
			}

			buffer := bytes.NewBufferString("")

			if err = t.Execute(buffer, fp.params); err != nil {
				return err
			}

			newContents := buffer.Bytes()

			oldContents, err := ioutil.ReadFile(projectFile)

			if os.IsNotExist(err) {
				fmt.Println("A", projectFile)
			} else if bytes.Compare(oldContents, newContents) != 0 {
				fmt.Println("U", projectFile)
			} else {
				return nil
			}

			if err = ioutil.WriteFile(projectFile, newContents,
				info.Mode()); err != nil {
				return err
			}
		}

		return nil
	}
}

// For each source file in 'templateDir', generateBuildFilesFromProjectTemplate
// generates an output file with the same relative pathname inside 'projectDir'.
func generateBuildFilesFromProjectTemplate(templateDir,
	projectDir string, params templateParams) error {

	templateDir = filepath.Clean(templateDir) + string(filepath.Separator)
	projectDir = filepath.Clean(projectDir) + string(filepath.Separator)

	if err := filepath.Walk(templateDir, getTemplateWalkFunc(templateDir,
		projectDir, params)); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package tmpl

import (
	"encoding/json"
	"html/template"
	"io"
	"log"
	"mime"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"sync"
	"time"

	"github.com/gorilla/mux"
)

// ArgGetter is the type of function that distills the template arguments from a request.
type ArgGetter func(*http.Request) (interface{}, error)

type handler struct {
	pattern string
	getArgs ArgGetter
	fm      template.FuncMap

	sync.Mutex
	lastParsed time.Time
	tmpl       *template.Template
	err        error
}

func (th *handler) recompileIfOlderThan(t time.Time) {
	th.Lock()
	defer th.Unlock()
	if th.lastParsed.After(t) {
		return
	}
	th.lastParsed = time.Now()
	th.tmpl, th.err = template.New("/").Funcs(th.fm).ParseGlob(th.pattern)
	if th.err != nil {
		log.Printf("Compiling templates %q: %v", th.pattern, th.err)
	} else {
		log.Printf("Compiled templates %q: %s", th.pattern, names(th.tmpl))
	}
}

func names(t *template.Template) []string {
	var r []string
	for _, v := range t.Templates() {
		r = append(r, v.Name())
	}
	return r
}

// NewHandler constructs a http.Handler that serves the html templates named by the files in the glob pattern.
//
// Ag must be a function that returns the argument object to template.Execute given a request,
// when nil, the handler will use the GetArgs function from this package.
// Beware that an error returned by ag will be rendered in the 400 response,
// so be sure not to leak sensitive state.
//
// Fm may contain extra functions for use in the templates.
// See https://golang.org/pkg/text/template/#Template.Funcs for more details.
func NewHandler(glob string, ag ArgGetter, fm template.FuncMap) http.Handler {
	if ag == nil {
		ag = GetArgs
	}
	th := &handler{
		pattern: glob,
		getArgs: ag,
		fm:      fm,
	}
	th.recompileIfOlderThan(time.Time{})
	return th
}

// GetArgs is the default function used by NewHandler.
// It constructs a map[string]interface{} with elements from the following sources:
//
// - If the request method is POST or PUT and the content type is application/json
// it will try to parse up to 64kb of json into an object, the elements of which
// become elements of the result. It is not an error if the body is empty.
//
// - Then it will call r.ParseForm to get all GET and POST form parameters,
// which are all of type []string.
//
// - Finally it will copy all gorilla mux.Vars from the request to the result object.
//
// In this list, later values overwrite the earlier ones, so a json object
// element will be overwritten by a mux.Var of the same name.
func GetArgs(r *http.Request) (interface{}, error) {
	args := make(map[string]interface{})

	if r.Method == "POST" || r.Method == "PUT" {
		ct := r.Header.Get("Content-Type")
		if ct == "" {
			ct = "application/octet-stream"
		}
		ct, _, _ = mime.ParseMediaType(ct)
		if ct == "application/json" {
			defer r.Body.Close()
			err := json.NewDecoder(io.LimitReader(r.Body, 64<<10)).Decode(&args)
			if err != nil && err != io.EOF {
				return nil, err
			}
		}
	}

	if err := r.ParseForm(); err != nil {
		return nil, err
	}
	for k, v := range r.Form { // note: these are []string!
		args[k] = v
	}

	for k, v := range mux.Vars(r) {
		args[k] = v
	}

	return args, nil
}

func lastModified(glob string) (time.Time, error) {
	var t time.Time
	fn, err := filepath.Glob(glob)
	if err != nil {
		return t, err
	}
	for _, v := range fn {
		fi, err := os.Stat(v)
		if err != nil {
			return t, err
		}
		if fi.ModTime().After(t) {
			t = fi.ModTime()
		}
	}
	return t, nil
}

var index = template.Must(template.New("").Parse(`<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">

	<meta charset="utf-8">
	<title>Templates</title>

{{range .}}
{{.}}

{{else}}
No templates found!

{{end}}

`))

// ServeHTTP serves the template named by the last component of r.URL.Path.
//
// If the path is '/', it executes the template named 'index'.  If there is no such
// template, one will be syntesized from the names of the defined templates.
// It is the responsability of the caller to ensure the permissions have been checked!
// The advantage of having the partial templates accessible directly is that with
// JQuery it is very easy to substitute part of a page dynamically from an XttpRequest.
func (th *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	lastMod, err := lastModified(th.pattern)
	if err != nil {
		log.Println("Stat templates: ", err)
		http.Error(w, "Missing templates?", http.StatusInternalServerError)
		return
	}
	th.recompileIfOlderThan(lastMod)
	if th.err != nil || th.tmpl == nil {
		http.Error(w, "Miscompiled templates.", http.StatusInternalServerError)
		return
	}

	name := path.Base(r.URL.Path)
	if name == "/" {
		name = "index"
	}

	t := th.tmpl.Lookup(name)
	if t == nil {
		if name != "index" {
			http.NotFound(w, r)
			return
		}
		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		if err := index.Execute(w, names(th.tmpl)); err != nil {
			log.Printf("Error rendering index template: %v", err)
		}
		return
	}

	args, err := th.getArgs(r)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	ww := countWriter{w: w}
	if err := t.Execute(&ww, args); err != nil {
		log.Printf("Executing template %q: %v", name, th.err)
		if ww.N == 0 {
			http.Error(w, "Error rendering template.", http.StatusInternalServerError)
			return
		}
	}
}

// a countwriter wraps any other writer and tracks how many bytes are written to it.
type countWriter struct {
	w io.Writer
	N int
}

func (w *countWriter) Write(b []byte) (int, error) {
	n, err := w.w.Write(b)
	w.N += n
	return n, err
}
<commit_msg>returned the wrong error<commit_after>package tmpl

import (
	"encoding/json"
	"html/template"
	"io"
	"log"
	"mime"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"sync"
	"time"

	"github.com/gorilla/mux"
)

// ArgGetter is the type of function that distills the template arguments from a request.
type ArgGetter func(*http.Request) (interface{}, error)

type handler struct {
	pattern string
	getArgs ArgGetter
	fm      template.FuncMap

	sync.Mutex
	lastParsed time.Time
	tmpl       *template.Template
	err        error
}

func (th *handler) recompileIfOlderThan(t time.Time) {
	th.Lock()
	defer th.Unlock()
	if th.lastParsed.After(t) {
		return
	}
	th.lastParsed = time.Now()
	th.tmpl, th.err = template.New("/").Funcs(th.fm).ParseGlob(th.pattern)
	if th.err != nil {
		log.Printf("Compiling templates %q: %v", th.pattern, th.err)
	} else {
		log.Printf("Compiled templates %q: %s", th.pattern, names(th.tmpl))
	}
}

func names(t *template.Template) []string {
	var r []string
	for _, v := range t.Templates() {
		r = append(r, v.Name())
	}
	return r
}

// NewHandler constructs a http.Handler that serves the html templates named by the files in the glob pattern.
//
// Ag must be a function that returns the argument object to template.Execute given a request,
// when nil, the handler will use the GetArgs function from this package.
// Beware that an error returned by ag will be rendered in the 400 response,
// so be sure not to leak sensitive state.
//
// Fm may contain extra functions for use in the templates.
// See https://golang.org/pkg/text/template/#Template.Funcs for more details.
func NewHandler(glob string, ag ArgGetter, fm template.FuncMap) http.Handler {
	if ag == nil {
		ag = GetArgs
	}
	th := &handler{
		pattern: glob,
		getArgs: ag,
		fm:      fm,
	}
	th.recompileIfOlderThan(time.Time{})
	return th
}

// GetArgs is the default function used by NewHandler.
// It constructs a map[string]interface{} with elements from the following sources:
//
// - If the request method is POST or PUT and the content type is application/json
// it will try to parse up to 64kb of json into an object, the elements of which
// become elements of the result. It is not an error if the body is empty.
//
// - Then it will call r.ParseForm to get all GET and POST form parameters,
// which are all of type []string.
//
// - Finally it will copy all gorilla mux.Vars from the request to the result object.
//
// In this list, later values overwrite the earlier ones, so a json object
// element will be overwritten by a mux.Var of the same name.
func GetArgs(r *http.Request) (interface{}, error) {
	args := make(map[string]interface{})

	if r.Method == "POST" || r.Method == "PUT" {
		ct := r.Header.Get("Content-Type")
		if ct == "" {
			ct = "application/octet-stream"
		}
		ct, _, _ = mime.ParseMediaType(ct)
		if ct == "application/json" {
			defer r.Body.Close()
			err := json.NewDecoder(io.LimitReader(r.Body, 64<<10)).Decode(&args)
			if err != nil && err != io.EOF {
				return nil, err
			}
		}
	}

	if err := r.ParseForm(); err != nil {
		return nil, err
	}
	for k, v := range r.Form { // note: these are []string!
		args[k] = v
	}

	for k, v := range mux.Vars(r) {
		args[k] = v
	}

	return args, nil
}

func lastModified(glob string) (time.Time, error) {
	var t time.Time
	fn, err := filepath.Glob(glob)
	if err != nil {
		return t, err
	}
	for _, v := range fn {
		fi, err := os.Stat(v)
		if err != nil {
			return t, err
		}
		if fi.ModTime().After(t) {
			t = fi.ModTime()
		}
	}
	return t, nil
}

var index = template.Must(template.New("").Parse(`<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">

	<meta charset="utf-8">
	<title>Templates</title>

{{range .}}
{{.}}

{{else}}
No templates found!

{{end}}

`))

// ServeHTTP serves the template named by the last component of r.URL.Path.
//
// If the path is '/', it executes the template named 'index'.  If there is no such
// template, one will be syntesized from the names of the defined templates.
// It is the responsability of the caller to ensure the permissions have been checked!
// The advantage of having the partial templates accessible directly is that with
// JQuery it is very easy to substitute part of a page dynamically from an XttpRequest.
func (th *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	lastMod, err := lastModified(th.pattern)
	if err != nil {
		log.Println("Stat templates: ", err)
		http.Error(w, "Missing templates?", http.StatusInternalServerError)
		return
	}
	th.recompileIfOlderThan(lastMod)
	if th.err != nil || th.tmpl == nil {
		http.Error(w, "Miscompiled templates.", http.StatusInternalServerError)
		return
	}

	name := path.Base(r.URL.Path)
	if name == "/" {
		name = "index"
	}

	t := th.tmpl.Lookup(name)
	if t == nil {
		if name != "index" {
			http.NotFound(w, r)
			return
		}
		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		if err := index.Execute(w, names(th.tmpl)); err != nil {
			log.Printf("Error rendering index template: %v", err)
		}
		return
	}

	args, err := th.getArgs(r)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	ww := countWriter{w: w}
	if err := t.Execute(&ww, args); err != nil {
		log.Printf("Executing template %q: %v", name, err)
		if ww.N == 0 {
			http.Error(w, "Error rendering template.", http.StatusInternalServerError)
			return
		}
	}
}

// a countwriter wraps any other writer and tracks how many bytes are written to it.
type countWriter struct {
	w io.Writer
	N int
}

func (w *countWriter) Write(b []byte) (int, error) {
	n, err := w.w.Write(b)
	w.N += n
	return n, err
}
<|endoftext|>


<commit_before>// Copyright (C) 2017 Damon Revoe. All rights reserved.
// Use of this source code is governed by the MIT
// license, which can be found in the LICENSE file.

package main

import (
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"text/template"
)

type templateParams map[string]interface{}

type fileParams struct {
	filename string
	params   templateParams
}

type pathnameTemplateMultiplier struct {
	paramName    string
	paramValues  []string
	continuation pathnameTemplateText
}

type pathnameTemplateText struct {
	text string
	next *pathnameTemplateMultiplier
}

// Subst updates the 'pathnameTemplateText' receiver by replacing all
// instances of 'name' surrounded by braces with 'value', which can be
// either a string or a slice of strings.  In the latter case, the text
// in the receiver structure gets truncated by the substitution and the
// receiver structure gets extended by a new pathnameTemplateMultiplier
// structure.  Subst returns the number of substitution values.
func (t *pathnameTemplateText) subst(name string, value interface{}) int {
	if textValue, ok := value.(string); ok {
		t.text = strings.Replace(t.text, "{"+name+"}",
			textValue, -1)
	} else if arrayValue, ok := value.([]string); !ok {
		t.text = strings.Replace(t.text, "{"+name+"}",
			fmt.Sprint(value), -1)
	} else if pos := strings.Index(t.text, "{"+name+"}"); pos >= 0 {
		t.next = &pathnameTemplateMultiplier{name, arrayValue,
			pathnameTemplateText{t.text[pos+len(name)+2:], t.next}}
		t.text = t.text[:pos]
		return len(arrayValue)
	}

	return 1
}

// ExpandPathnameTemplate takes a pathname template and substitutes
// template parameter names with their values. Parameter values can be
// either strings or slices of strings. Each template value that is a
// slice of strings multiplies the number of output strings by the number
// of strings in the slice.
func expandPathnameTemplate(pathname string,
	params templateParams) []fileParams {
	root := pathnameTemplateText{pathname, nil}

	resultSize := 1

	for name, value := range params {
		resultSize *= root.subst(name, value)

		for n := root.next; n != nil; n = n.continuation.next {
			resultSize *= n.continuation.subst(name, value)
		}
	}

	result := make([]fileParams, resultSize)

	for i := 0; i < resultSize; i++ {
		result[i].filename = root.text
		copyOfParams := templateParams{}
		for name, value := range params {
			copyOfParams[name] = value
		}
		result[i].params = copyOfParams
	}

	if resultSize == 0 {
		return result
	}

	sliceSize := 1

	for a := root.next; a != nil; a = a.continuation.next {
		numberOfValues := len(a.paramValues)

		continuationText := a.continuation.text

		for i := 0; i < resultSize; {
			for j := 0; j < numberOfValues; j++ {
				value := a.paramValues[j]
				filenameFragment := value + continuationText
				for k := 0; k < sliceSize; k++ {
					result[i].filename += filenameFragment
					result[i].params[a.paramName] = value
					i++
				}
			}
		}

		sliceSize *= numberOfValues
	}

	return result
}

func must(err error) {
	if err != nil {
		panic(err)
	}
}

func varName(arg string) string {
	return strings.Map(func(r rune) rune {
		if r >= 'A' && r <= 'Z' || r >= 'a' && r <= 'z' ||
			r >= '0' && r <= '9' {
			return r
		} else if r == '+' {
			return 'x'
		}
		return '_'
	}, arg)
}

func varNameUC(arg string) string {
	return strings.Map(func(r rune) rune {
		if r >= 'a' && r <= 'z' {
			return r - 'a' + 'A'
		} else if r >= 'A' && r <= 'Z' || r >= '0' && r <= '9' {
			return r
		} else if r == '+' {
			return 'X'
		}
		return '_'
	}, arg)
}

func libName(arg string) string {
	return strings.Map(func(r rune) rune {
		if r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' ||
			r >= '0' && r <= '9' ||
			r == '+' || r == '-' || r == '.' {
			return r
		}
		return '_'
	}, arg)
}

func stringList(elem ...string) []string {
	return elem
}

func templateExecutionError(errorMessage string) (string, error) {
	return "", errors.New(errorMessage)
}

var funcMap = template.FuncMap{
	"VarName":    varName,
	"VarNameUC":  varNameUC,
	"LibName":    libName,
	"StringList": stringList,
	"Error":      templateExecutionError,
}

func filterFileList(files *filesFromSourceDir, root, pattern string) []string {
	root += string(filepath.Separator)

	var filtered []string

	for sourceFile := range *files {
		if !strings.HasPrefix(sourceFile, root) {
			continue
		}

		relativePathname := sourceFile[len(root):]
		filename := filepath.Base(relativePathname)

		if matched, _ := filepath.Match(pattern, filename); matched {
			filtered = append(filtered, relativePathname)
		}
	}

	sort.Strings(filtered)

	return filtered
}

type filenameAndContents struct {
	filename string
	contents []byte
}

type templateExecutionResult []filenameAndContents

func executeFileTemplate(templatePathname string,
	templateContents []byte, params templateParams,
	sourceFiles filesFromSourceDir) ([]filenameAndContents, error) {

	// Parse the template file. The parsed template will be
	// reused multiple times if expandPathnameTemplate()
	// returns more than one pathname expansion.
	t := template.New(filepath.Base(templatePathname))
	t.Funcs(funcMap)

	t.Funcs(template.FuncMap{
		"FileList": func(root, pattern string) []string {
			return filterFileList(&sourceFiles, root, pattern)
		}})

	if _, err := t.Parse(string(templateContents)); err != nil {
		return nil, err
	}

	var result []filenameAndContents

	for _, fp := range expandPathnameTemplate(templatePathname, params) {
		buffer := bytes.NewBufferString("")

		if err := t.Execute(buffer, fp.params); err != nil {
			return nil, err
		}

		result = append(result, filenameAndContents{
			fp.filename, buffer.Bytes()})
	}

	return result, nil
}

func generateFilesFromFileTemplate(projectDir, templatePathname string,
	templateContents []byte, templateFileMode os.FileMode,
	params templateParams, sourceFiles filesFromSourceDir) error {

	outputFiles, err := executeFileTemplate(templatePathname,
		templateContents, params, sourceFiles)

	if err != nil {
		return err
	}

	for _, outputFile := range outputFiles {
		mode := "R"

		projectFile := filepath.Join(projectDir, outputFile.filename)

		existingFileInfo, err := os.Lstat(projectFile)
		if err != nil {
			if os.IsNotExist(err) {
				if err := os.MkdirAll(filepath.Dir(projectFile),
					os.ModePerm); err != nil {
					return err
				}

				mode = "A"
			}
		} else if (existingFileInfo.Mode() & os.ModeSymlink) == 0 {
			oldContents, err := ioutil.ReadFile(projectFile)
			if err == nil {
				if bytes.Compare(oldContents,
					outputFile.contents) == 0 {
					return nil
				}
				mode = "U"
			}
		}

		fmt.Println(mode, projectFile)
		if mode == "R" {
			if err = os.Remove(projectFile); err != nil {
				return err
			}
		}

		if err = ioutil.WriteFile(projectFile, outputFile.contents,
			templateFileMode); err != nil {
			return err
		}
	}

	return nil
}

type fileProcessor func(sourcePathname, relativePathname string,
	sourceFileInfo os.FileInfo) error

// GetFileGenerationWalkFunc returns a walker function for use with
// filepath.Walk(). The walker function skips directories and package
// definition files.
func getFileGenerationWalkFunc(sourceDir, targetDir string,
	processFile fileProcessor) filepath.WalkFunc {

	sourceDir = filepath.Clean(sourceDir) + string(filepath.Separator)

	return func(sourcePathname string, sourceFileInfo os.FileInfo,
		err error) error {
		if err != nil {
			return err
		}

		if sourceFileInfo.IsDir() {
			return nil
		}

		// Panic if filepath.Walk() does not behave as expected.
		if !strings.HasPrefix(sourcePathname, sourceDir) {
			panic(sourcePathname + " does not start with " +
				sourceDir)
		}

		// Relative pathname of the source file in the source
		// directory (and the target file in the target directory).
		relativePathname := sourcePathname[len(sourceDir):]

		// Ignore the package definition file.
		if relativePathname == packageDefinitionFilename {
			return nil
		}

		return processFile(sourcePathname, relativePathname,
			sourceFileInfo)
	}
}

type filesFromSourceDir map[string]struct{}

func linkFilesFromSourceDir(pd *packageDefinition,
	projectDir string) (filesFromSourceDir, error) {
	sourceFiles := make(filesFromSourceDir)
	sourceDir := filepath.Dir(pd.pathname)

	linkFile := func(sourcePathname, relativePathname string,
		sourceFileInfo os.FileInfo) error {
		sourceFiles[relativePathname] = struct{}{}
		targetPathname := filepath.Join(projectDir, relativePathname)
		targetFileInfo, err := os.Lstat(targetPathname)
		if err == nil {
			if (targetFileInfo.Mode() & os.ModeSymlink) != 0 {
				originalLink, err := os.Readlink(targetPathname)

				if err != nil {
					return err
				}

				if originalLink == sourcePathname {
					return nil
				}
			}

			if err = os.Remove(targetPathname); err != nil {
				return err
			}
		}

		fmt.Println("L", targetPathname)

		if err = os.MkdirAll(filepath.Dir(targetPathname),
			os.ModePerm); err != nil {
			return err
		}

		return os.Symlink(sourcePathname, targetPathname)
	}

	err := filepath.Walk(sourceDir,
		getFileGenerationWalkFunc(sourceDir, projectDir, linkFile))

	return sourceFiles, err
}

// For each source file in 'templateDir', generateBuildFilesFromProjectTemplate
// generates an output file with the same relative pathname inside 'projectDir'.
func generateBuildFilesFromProjectTemplate(templateDir,
	projectDir string, pd *packageDefinition) error {

	sourceFiles, err := linkFilesFromSourceDir(pd, projectDir)
	if err != nil {
		return err
	}

	generateFile := func(sourcePathname, relativePathname string,
		sourceFileInfo os.FileInfo) error {
		if _, sourceFile := sourceFiles[relativePathname]; sourceFile {
			return nil
		}

		// Read the contents of the template file. Cannot use
		// template.ParseFiles() because a Funcs() call must be
		// made between New() and Parse().
		templateContents, err := ioutil.ReadFile(sourcePathname)
		if err != nil {
			return err
		}

		if err = generateFilesFromFileTemplate(projectDir,
			relativePathname, templateContents,
			sourceFileInfo.Mode(),
			pd.params, sourceFiles); err != nil {
			return err
		}

		return nil

	}

	return filepath.Walk(templateDir,
		getFileGenerationWalkFunc(templateDir,
			projectDir, generateFile))
}

// FileTemplate defines the file mode and the contents
// of a single file that is a part of an embedded project template.
type fileTemplate struct {
	pathname string
	mode     os.FileMode
	contents []byte
}

// EmbeddedTemplate defines a build-in project template.
type embeddedProjectTemplate []fileTemplate

// GenerateBuildFilesFromEmbeddedTemplate generates project build
// files from a built-in template pointed to by the 't' parameter.
func generateBuildFilesFromEmbeddedTemplate(t *embeddedProjectTemplate,
	projectDir string, pd *packageDefinition) error {

	sourceFiles, err := linkFilesFromSourceDir(pd, projectDir)
	if err != nil {
		return err
	}

	for _, fileInfo := range *t {
		if _, exists := sourceFiles[fileInfo.pathname]; exists {
			continue
		}

		if err := generateFilesFromFileTemplate(projectDir,
			fileInfo.pathname, fileInfo.contents, fileInfo.mode,
			pd.params, sourceFiles); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>Delete unused function<commit_after>// Copyright (C) 2017 Damon Revoe. All rights reserved.
// Use of this source code is governed by the MIT
// license, which can be found in the LICENSE file.

package main

import (
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"text/template"
)

type templateParams map[string]interface{}

type fileParams struct {
	filename string
	params   templateParams
}

type pathnameTemplateMultiplier struct {
	paramName    string
	paramValues  []string
	continuation pathnameTemplateText
}

type pathnameTemplateText struct {
	text string
	next *pathnameTemplateMultiplier
}

// Subst updates the 'pathnameTemplateText' receiver by replacing all
// instances of 'name' surrounded by braces with 'value', which can be
// either a string or a slice of strings.  In the latter case, the text
// in the receiver structure gets truncated by the substitution and the
// receiver structure gets extended by a new pathnameTemplateMultiplier
// structure.  Subst returns the number of substitution values.
func (t *pathnameTemplateText) subst(name string, value interface{}) int {
	if textValue, ok := value.(string); ok {
		t.text = strings.Replace(t.text, "{"+name+"}",
			textValue, -1)
	} else if arrayValue, ok := value.([]string); !ok {
		t.text = strings.Replace(t.text, "{"+name+"}",
			fmt.Sprint(value), -1)
	} else if pos := strings.Index(t.text, "{"+name+"}"); pos >= 0 {
		t.next = &pathnameTemplateMultiplier{name, arrayValue,
			pathnameTemplateText{t.text[pos+len(name)+2:], t.next}}
		t.text = t.text[:pos]
		return len(arrayValue)
	}

	return 1
}

// ExpandPathnameTemplate takes a pathname template and substitutes
// template parameter names with their values. Parameter values can be
// either strings or slices of strings. Each template value that is a
// slice of strings multiplies the number of output strings by the number
// of strings in the slice.
func expandPathnameTemplate(pathname string,
	params templateParams) []fileParams {
	root := pathnameTemplateText{pathname, nil}

	resultSize := 1

	for name, value := range params {
		resultSize *= root.subst(name, value)

		for n := root.next; n != nil; n = n.continuation.next {
			resultSize *= n.continuation.subst(name, value)
		}
	}

	result := make([]fileParams, resultSize)

	for i := 0; i < resultSize; i++ {
		result[i].filename = root.text
		copyOfParams := templateParams{}
		for name, value := range params {
			copyOfParams[name] = value
		}
		result[i].params = copyOfParams
	}

	if resultSize == 0 {
		return result
	}

	sliceSize := 1

	for a := root.next; a != nil; a = a.continuation.next {
		numberOfValues := len(a.paramValues)

		continuationText := a.continuation.text

		for i := 0; i < resultSize; {
			for j := 0; j < numberOfValues; j++ {
				value := a.paramValues[j]
				filenameFragment := value + continuationText
				for k := 0; k < sliceSize; k++ {
					result[i].filename += filenameFragment
					result[i].params[a.paramName] = value
					i++
				}
			}
		}

		sliceSize *= numberOfValues
	}

	return result
}

func varName(arg string) string {
	return strings.Map(func(r rune) rune {
		if r >= 'A' && r <= 'Z' || r >= 'a' && r <= 'z' ||
			r >= '0' && r <= '9' {
			return r
		} else if r == '+' {
			return 'x'
		}
		return '_'
	}, arg)
}

func varNameUC(arg string) string {
	return strings.Map(func(r rune) rune {
		if r >= 'a' && r <= 'z' {
			return r - 'a' + 'A'
		} else if r >= 'A' && r <= 'Z' || r >= '0' && r <= '9' {
			return r
		} else if r == '+' {
			return 'X'
		}
		return '_'
	}, arg)
}

func libName(arg string) string {
	return strings.Map(func(r rune) rune {
		if r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' ||
			r >= '0' && r <= '9' ||
			r == '+' || r == '-' || r == '.' {
			return r
		}
		return '_'
	}, arg)
}

func stringList(elem ...string) []string {
	return elem
}

func templateExecutionError(errorMessage string) (string, error) {
	return "", errors.New(errorMessage)
}

var funcMap = template.FuncMap{
	"VarName":    varName,
	"VarNameUC":  varNameUC,
	"LibName":    libName,
	"StringList": stringList,
	"Error":      templateExecutionError,
}

func filterFileList(files *filesFromSourceDir, root, pattern string) []string {
	root += string(filepath.Separator)

	var filtered []string

	for sourceFile := range *files {
		if !strings.HasPrefix(sourceFile, root) {
			continue
		}

		relativePathname := sourceFile[len(root):]
		filename := filepath.Base(relativePathname)

		if matched, _ := filepath.Match(pattern, filename); matched {
			filtered = append(filtered, relativePathname)
		}
	}

	sort.Strings(filtered)

	return filtered
}

type filenameAndContents struct {
	filename string
	contents []byte
}

type templateExecutionResult []filenameAndContents

func executeFileTemplate(templatePathname string,
	templateContents []byte, params templateParams,
	sourceFiles filesFromSourceDir) ([]filenameAndContents, error) {

	// Parse the template file. The parsed template will be
	// reused multiple times if expandPathnameTemplate()
	// returns more than one pathname expansion.
	t := template.New(filepath.Base(templatePathname))
	t.Funcs(funcMap)

	t.Funcs(template.FuncMap{
		"FileList": func(root, pattern string) []string {
			return filterFileList(&sourceFiles, root, pattern)
		}})

	if _, err := t.Parse(string(templateContents)); err != nil {
		return nil, err
	}

	var result []filenameAndContents

	for _, fp := range expandPathnameTemplate(templatePathname, params) {
		buffer := bytes.NewBufferString("")

		if err := t.Execute(buffer, fp.params); err != nil {
			return nil, err
		}

		result = append(result, filenameAndContents{
			fp.filename, buffer.Bytes()})
	}

	return result, nil
}

func generateFilesFromFileTemplate(projectDir, templatePathname string,
	templateContents []byte, templateFileMode os.FileMode,
	params templateParams, sourceFiles filesFromSourceDir) error {

	outputFiles, err := executeFileTemplate(templatePathname,
		templateContents, params, sourceFiles)

	if err != nil {
		return err
	}

	for _, outputFile := range outputFiles {
		mode := "R"

		projectFile := filepath.Join(projectDir, outputFile.filename)

		existingFileInfo, err := os.Lstat(projectFile)
		if err != nil {
			if os.IsNotExist(err) {
				if err := os.MkdirAll(filepath.Dir(projectFile),
					os.ModePerm); err != nil {
					return err
				}

				mode = "A"
			}
		} else if (existingFileInfo.Mode() & os.ModeSymlink) == 0 {
			oldContents, err := ioutil.ReadFile(projectFile)
			if err == nil {
				if bytes.Compare(oldContents,
					outputFile.contents) == 0 {
					return nil
				}
				mode = "U"
			}
		}

		fmt.Println(mode, projectFile)
		if mode == "R" {
			if err = os.Remove(projectFile); err != nil {
				return err
			}
		}

		if err = ioutil.WriteFile(projectFile, outputFile.contents,
			templateFileMode); err != nil {
			return err
		}
	}

	return nil
}

type fileProcessor func(sourcePathname, relativePathname string,
	sourceFileInfo os.FileInfo) error

// GetFileGenerationWalkFunc returns a walker function for use with
// filepath.Walk(). The walker function skips directories and package
// definition files.
func getFileGenerationWalkFunc(sourceDir, targetDir string,
	processFile fileProcessor) filepath.WalkFunc {

	sourceDir = filepath.Clean(sourceDir) + string(filepath.Separator)

	return func(sourcePathname string, sourceFileInfo os.FileInfo,
		err error) error {
		if err != nil {
			return err
		}

		if sourceFileInfo.IsDir() {
			return nil
		}

		// Panic if filepath.Walk() does not behave as expected.
		if !strings.HasPrefix(sourcePathname, sourceDir) {
			panic(sourcePathname + " does not start with " +
				sourceDir)
		}

		// Relative pathname of the source file in the source
		// directory (and the target file in the target directory).
		relativePathname := sourcePathname[len(sourceDir):]

		// Ignore the package definition file.
		if relativePathname == packageDefinitionFilename {
			return nil
		}

		return processFile(sourcePathname, relativePathname,
			sourceFileInfo)
	}
}

type filesFromSourceDir map[string]struct{}

func linkFilesFromSourceDir(pd *packageDefinition,
	projectDir string) (filesFromSourceDir, error) {
	sourceFiles := make(filesFromSourceDir)
	sourceDir := filepath.Dir(pd.pathname)

	linkFile := func(sourcePathname, relativePathname string,
		sourceFileInfo os.FileInfo) error {
		sourceFiles[relativePathname] = struct{}{}
		targetPathname := filepath.Join(projectDir, relativePathname)
		targetFileInfo, err := os.Lstat(targetPathname)
		if err == nil {
			if (targetFileInfo.Mode() & os.ModeSymlink) != 0 {
				originalLink, err := os.Readlink(targetPathname)

				if err != nil {
					return err
				}

				if originalLink == sourcePathname {
					return nil
				}
			}

			if err = os.Remove(targetPathname); err != nil {
				return err
			}
		}

		fmt.Println("L", targetPathname)

		if err = os.MkdirAll(filepath.Dir(targetPathname),
			os.ModePerm); err != nil {
			return err
		}

		return os.Symlink(sourcePathname, targetPathname)
	}

	err := filepath.Walk(sourceDir,
		getFileGenerationWalkFunc(sourceDir, projectDir, linkFile))

	return sourceFiles, err
}

// For each source file in 'templateDir', generateBuildFilesFromProjectTemplate
// generates an output file with the same relative pathname inside 'projectDir'.
func generateBuildFilesFromProjectTemplate(templateDir,
	projectDir string, pd *packageDefinition) error {

	sourceFiles, err := linkFilesFromSourceDir(pd, projectDir)
	if err != nil {
		return err
	}

	generateFile := func(sourcePathname, relativePathname string,
		sourceFileInfo os.FileInfo) error {
		if _, sourceFile := sourceFiles[relativePathname]; sourceFile {
			return nil
		}

		// Read the contents of the template file. Cannot use
		// template.ParseFiles() because a Funcs() call must be
		// made between New() and Parse().
		templateContents, err := ioutil.ReadFile(sourcePathname)
		if err != nil {
			return err
		}

		if err = generateFilesFromFileTemplate(projectDir,
			relativePathname, templateContents,
			sourceFileInfo.Mode(),
			pd.params, sourceFiles); err != nil {
			return err
		}

		return nil

	}

	return filepath.Walk(templateDir,
		getFileGenerationWalkFunc(templateDir,
			projectDir, generateFile))
}

// FileTemplate defines the file mode and the contents
// of a single file that is a part of an embedded project template.
type fileTemplate struct {
	pathname string
	mode     os.FileMode
	contents []byte
}

// EmbeddedTemplate defines a build-in project template.
type embeddedProjectTemplate []fileTemplate

// GenerateBuildFilesFromEmbeddedTemplate generates project build
// files from a built-in template pointed to by the 't' parameter.
func generateBuildFilesFromEmbeddedTemplate(t *embeddedProjectTemplate,
	projectDir string, pd *packageDefinition) error {

	sourceFiles, err := linkFilesFromSourceDir(pd, projectDir)
	if err != nil {
		return err
	}

	for _, fileInfo := range *t {
		if _, exists := sourceFiles[fileInfo.pathname]; exists {
			continue
		}

		if err := generateFilesFromFileTemplate(projectDir,
			fileInfo.pathname, fileInfo.contents, fileInfo.mode,
			pd.params, sourceFiles); err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package main

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

	"github.com/codegangsta/cli"
	"github.com/joho/godotenv"
)

var (
	/*
	 * This map stores some useful global environment variables used
	 * internally by the logic of the tool (we avoid to clutter the source
	 * code with redundant calls to pkg/os functions).
	 */
	ENV = map[string]string{
		"PWD":  "",
		"HOME": "",
	}

	/*
	 * This map stores all the environment variables used by the todo.txt CLI
	 */
	TODOENV = map[string]string{
		"TODO_DIR":             "",
		"TODO_FILE":            "",
		"DONE_FILE":            "",
		"REPORT_FILE":          "",
		"TODO_ACTIONS_DIR":     "",
		"TODOTXT_SORT_COMMAND": "",
		"TODOTXT_FINAL_FILTER": "",
	}

	/* This slice defines all the possible paths for the configuration files.
	 * The slice defines also the exact order at which the conf files should be
	 * loaded by the godotenv library.
	 *
	 * $HOME/todo.cfg
	 * $HOME/.todo.cfg
	 * ./todo.cfg
	 * /etc/todo/config
	 */
	// TODO: transform the slice into a map[string]bool where the boolean value defines if the config file exists
	CFG = []string{
		"$HOME/todo.cfg",
		"$HOME/.todo.cfg",
		"./todo.cfg",
		"/etc/todo/config"}
)

// TODO: this is an ugly and hackish error handler that needs to be improved
func check(e error) {
	if e != nil {
		fmt.Errorf("%v", e)
		panic(e)
	}
}

// Exists returns true if the given path exists.
func Exists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

// Adds a task to a todo.txt file.
func addAction(task string) {
	// TODO: TODOENV["TODO_FILE"] path should be validated somehow
	// before to be stated by os.OpenFile
	//path.Clean(TODOENV["TODO_FILE"])

	// Open todo.txt in append mode only
	todoFile, err := os.OpenFile(TODOENV["TODO_FILE"], os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600)
	defer todoFile.Close()
	check(err)

	// add the task to the todo.txt file
	ret, err := todoFile.WriteString(task + "\n")
	check(err)
	fmt.Printf("added new task (%d bytes): \"%s\"\n", ret, task)

	// sync / flush todo.txt
	todoFile.Sync()
}

// Create a todo.txt structure at the specified location (default destination is ".")
func initAction(destination string) {

	var (
		FileName = map[string]string{
			"cfg":    "/todo.cfg",
			"todo":   "/todo.txt",
			"done":   "/done.txt",
			"report": "/report.txt",
		}

		FileTemplate = map[string]string{
			"cfg": `
# === EDIT FILE LOCATIONS BELOW ===

# Your todo.txt directory
#export TODO_DIR="$HOME/todo"
export TODO_DIR="."

# Your todo/done/report.txt locations
export TODO_FILE="$TODO_DIR/todo.txt"
export DONE_FILE="$TODO_DIR/done.txt"
export REPORT_FILE="$TODO_DIR/report.txt"

# You can customize your actions directory location
#export TODO_ACTIONS_DIR="$HOME/.todo.actions.d"`,
			"todo":   "",
			"done":   "",
			"report": "",
		}

		initiated = false
		message   = "Initialized a new"
	)

	// try to guess if the destination is an existing and
	// pre-configured todo.txt structure.
	for _, filename := range FileName {
		// sanitize the absolute path of the file
		filePath, err := filepath.Abs(destination + filename)
		//fmt.Printf("absolute path: %s\n", cfgFilePath)
		check(err)

		ret, _ := Exists(filePath)
		if ret {
			initiated = true
			break
		}
	}

	// if the destination is an existing todo.txt structure then
	// change the message accordingly
	if initiated {
		message = "Reinitialized an existing"
	}

	// sanitize the absolute path of the destination
	filePath, err := filepath.Abs(destination)
	check(err)

	// print first line of the action's summary
	fmt.Printf("%s todo.txt structure in %s\n", message, filePath)

	// create the missing files of the todo.txt structure
	for k, filename := range FileName {
		// sanitize the absolute path of the file
		filePath, err := filepath.Abs(destination + FileName[k])
		check(err)
		//fmt.Printf("absolute path: %s\n", filePath)

		// Open file
		file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
		defer file.Close()

		// if there aren't errors, write a new file with default values
		if err == nil {
			size, err := file.WriteString(FileTemplate[k])
			check(err)

			// sync / flush file
			file.Sync()

			// print a small summary
			fmt.Printf("%s [%s] (%d bytes)\n", filename, "new", size)
			continue
		}

		// file exists, there is nothing to write
		if os.IsExist(err) {
			// print a small summary
			fmt.Printf("%s [%s]\n", filename, "exists")
			continue
		}
	}
}

func main() {

	// Retrieve environment variables $HOME and $PWD
	pwd, err := os.Getwd()
	check(err)
	ENV["HOME"] = os.Getenv("HOME")
	ENV["PWD"] = pwd

	// Expand $HOME var inside CFG slice
	if ENV["HOME"] != "" {
		// $HOME isn't empty, we expand $HOME for CFG[0] and CFG[1]
		CFG[0] = strings.Replace(CFG[0], "$HOME", ENV["HOME"], -1)
		CFG[1] = strings.Replace(CFG[1], "$HOME", ENV["HOME"], -1)
	} else {
		// $HOME is empty, we unset CFG[0] and CFG[1]
		CFG[0] = ""
		CFG[1] = ""
	}

	// Load environment variables from all the configuration files
	// specified in the slice 'CFG'
	for _, filepath := range CFG {
		if filepath != "" {
			ret, err := Exists(filepath)
			check(err)
			//fmt.Printf("checking: %s (%t) \n", filepath, ret)

			// if the conf file exists, load it with godotenv
			if ret {
				err := godotenv.Load(filepath)
				check(err)
			}
		}
	}

	// Populate TODOENV map with environment variables
	for k, _ := range TODOENV {
		TODOENV[k] = os.Getenv(k)
	}
	//fmt.Println("map:", TODOENV)

	// Sanitize TODOENV map by expanding bash variables
	for k, v := range TODOENV {
		// expand $HOME var
		v = strings.Replace(v, "$HOME", ENV["HOME"], -1)
		v = strings.Replace(v, "${HOME}", ENV["HOME"], -1)

		// Expand $TODO_DIR var
		v = strings.Replace(v, "$TODO_DIR", TODOENV["TODO_DIR"], -1)
		v = strings.Replace(v, "${TODO_DIR}", TODOENV["TODO_DIR"], -1)

		// save the sanitized value
		TODOENV[k] = v
	}

	// Initialize the app CLI
	app := cli.NewApp()
	app.Name = "go-todo"
	app.Usage = "A simple and extensible utility for managing your todo.txt files"
	app.Version = "1.0.0"
	app.Author = "Mauro Toffanin"
	app.Email = "toffanin.mauro@gmail.com"
	app.EnableBashCompletion = true
	app.Commands = []cli.Command{
		{
			Name:            "env",
			Usage:           "Prints `todo` environment information.",
			Description:     "By default env prints information as a shell script.\n   The environment info will be dumped in straight-forward\n   form suitable for sourcing into a shell script.\n\n   If one or more variable names is given as arguments, env\n   prints the value of each named variable on its own line.",
			SkipFlagParsing: true,
			Action: func(c *cli.Context) {
				// collect all the user-submitted arguments in an array
				args := c.Args()

				// debugging
				/*fmt.Printf("pwd: %s\n", pwd)
				fmt.Printf("HOME=\"%s\"\n", ENV["HOME"])*/

				switch args.Present() {
				case true:
					// print only the required environment variables
					for _, arg := range args {
						fmt.Printf("%s=\"%s\"\n", arg, TODOENV[arg])
					}
				case false:
					// print all the environment variables
					for k, v := range TODOENV {
						fmt.Printf("%s=\"%s\"\n", k, v)
					}
				}
			},
		},
		{
			Name:  "init",
			Usage: "Initialize a new todo.txt structure with default values",
			Description: `
   This command creates all the template file required by the todo.txt and
   a configuration files with default values - basically, the values TODO_DIR,
   TODO_FILE, DONE_FILE, REPORT_FILE and TODO_ACTIONS_DIR are exported.

   If the option '--dest' is set then it specifies a path to use instead of
   the working directory as the destination path for the new structure.

   Running 'todo init' in a pre-initialized directory is safe; it will not
   overwrite things that are already there.`,
			Flags: []cli.Flag{
				cli.StringFlag{"dest, d", "/path/to/your/dir", "specifies a different destination path"},
			},
			Action: func(c *cli.Context) {
				destination := "."
				if c.String("dest") != "" {
					//fmt.Println("dest:", c.String("dest"))
					destination = c.String("dest")
				}
				initAction(destination)
			},
		},
		{
			Name:            "add",
			ShortName:       "a",
			Usage:           "Adds a task to your todo.txt file.",
			Description:     "add \"feed the cat\"\n   Adds \"feed the cat\" to your todo.txt file on its own line.",
			SkipFlagParsing: true,
			Action: func(c *cli.Context) {
				// collect all the user-submitted arguments in an array
				// and store its lenght for later usage
				args := c.Args()
				nargs := len(args)

				// debugging
				//fmt.Printf("args (%d): %s\n", nargs, args)

				// print only the required environment variables
				if 1 < nargs {
					fmt.Println("\nIncorrect Usage: cannot use two options with command \"add\"\n")
					cli.ShowCommandHelp(c, "add")
					return
				}

				// TODO: validating input as a task

				// TODO: task mangler
				task := fmt.Sprintf("%s", c.Args().First())
				r := strings.NewReplacer("\n", " ", "\t", " ", "\r", " ")
				task = r.Replace(task)
				task = strings.TrimSpace(task)

				// Add the new task
				fmt.Printf("task: |%s|\n", task)
				addAction(task)
			},
		},
		/*{
			Name:  "status",
			Usage: "Obtain a summary of the todo.txt structure",
			Flags: []cli.Flag{
				cli.StringFlag{"dest, d", "", "specifies a different destination path"},
			},
			Action: func(c *cli.Context) {
				//fmt.Println("status: ", c.Args().First())
			},
		},*/
	}
	err = app.Run(os.Args)
	check(err)
}
<commit_msg>Various enhancements for the 'add' command:<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/codegangsta/cli"
	"github.com/joho/godotenv"
)

var (
	/*
	 * This map stores some useful global environment variables used
	 * internally by the logic of the tool (we avoid to clutter the source
	 * code with redundant calls to pkg/os functions).
	 */
	ENV = map[string]string{
		"PWD":  "",
		"HOME": "",
	}

	/*
	 * This map stores all the environment variables used by the todo.txt CLI
	 */
	TODOENV = map[string]string{
		"TODO_DIR":             "",
		"TODO_FILE":            "",
		"DONE_FILE":            "",
		"REPORT_FILE":          "",
		"TODO_ACTIONS_DIR":     "",
		"TODOTXT_SORT_COMMAND": "",
		"TODOTXT_FINAL_FILTER": "",
	}

	/* This slice defines all the possible paths for the configuration files.
	 * The slice defines also the exact order at which the conf files should be
	 * loaded by the godotenv library.
	 *
	 * $HOME/todo.cfg
	 * $HOME/.todo.cfg
	 * ./todo.cfg
	 * /etc/todo/config
	 */
	// TODO: transform the slice into a map[string]bool where the boolean value defines if the config file exists
	CFG = []string{
		"$HOME/todo.cfg",
		"$HOME/.todo.cfg",
		"./todo.cfg",
		"/etc/todo/config"}
)

// TODO: this is an ugly and hackish error handler that needs to be improved
func check(e error) {
	if e != nil {
		fmt.Errorf("%v", e)
		panic(e)
	}
}

// Exists returns true if the given path exists.
func Exists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

// Adds a task to a todo.txt file.
func addAction(task string) {
	// TODO: TODOENV["TODO_FILE"] path should be validated somehow
	// before to be stated by os.OpenFile
	//path.Clean(TODOENV["TODO_FILE"])

	// determine the number of tasks in todo.txt
	// TODO: with NewReadWriter the code should be more compact
	fd, err := os.OpenFile(TODOENV["TODO_FILE"], os.O_RDONLY|os.O_CREATE, 0600)
	check(err)
	scanner := bufio.NewScanner(fd)
	ntasks := 1
	for scanner.Scan() {
		ntasks++
	}
	if err := scanner.Err(); err != nil {
		fd.Close()
		check(err)
	}
	//fmt.Printf("n. lines: %d\n", ntasks)
	err = fd.Close()
	check(err)

	// Open todo.txt in append mode only
	fd, err = os.OpenFile(TODOENV["TODO_FILE"], os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600)
	check(err)
	defer fd.Close()

	// use buffered I/O
	writer := bufio.NewWriter(fd)

	// add the task to todo.txt
	_, err = writer.WriteString(task + "\n")
	check(err)
	err = writer.Flush()
	check(err)

	// print summary
	fmt.Printf("%d: %s\n", ntasks, task)
	fmt.Printf("TODO: %d added\n", ntasks)
}

// Create a todo.txt structure at the specified location (default destination is ".")
func initAction(destination string) {

	var (
		FileName = map[string]string{
			"cfg":    "/todo.cfg",
			"todo":   "/todo.txt",
			"done":   "/done.txt",
			"report": "/report.txt",
		}

		FileTemplate = map[string]string{
			"cfg": `
# === EDIT FILE LOCATIONS BELOW ===

# Your todo.txt directory
#export TODO_DIR="$HOME/todo"
export TODO_DIR="."

# Your todo/done/report.txt locations
export TODO_FILE="$TODO_DIR/todo.txt"
export DONE_FILE="$TODO_DIR/done.txt"
export REPORT_FILE="$TODO_DIR/report.txt"

# You can customize your actions directory location
#export TODO_ACTIONS_DIR="$HOME/.todo.actions.d"`,
			"todo":   "",
			"done":   "",
			"report": "",
		}

		initiated = false
		message   = "Initialized a new"
	)

	// try to guess if the destination is an existing and
	// pre-configured todo.txt structure.
	for _, filename := range FileName {
		// sanitize the absolute path of the file
		filePath, err := filepath.Abs(destination + filename)
		//fmt.Printf("absolute path: %s\n", cfgFilePath)
		check(err)

		ret, _ := Exists(filePath)
		if ret {
			initiated = true
			break
		}
	}

	// if the destination is an existing todo.txt structure then
	// change the message accordingly
	if initiated {
		message = "Reinitialized an existing"
	}

	// sanitize the absolute path of the destination
	filePath, err := filepath.Abs(destination)
	check(err)

	// print first line of the action's summary
	fmt.Printf("%s todo.txt structure in %s\n", message, filePath)

	// create the missing files of the todo.txt structure
	for k, filename := range FileName {
		// sanitize the absolute path of the file
		filePath, err := filepath.Abs(destination + FileName[k])
		check(err)
		//fmt.Printf("absolute path: %s\n", filePath)

		// Open file
		file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
		defer file.Close()

		// if there aren't errors, write a new file with default values
		if err == nil {
			size, err := file.WriteString(FileTemplate[k])
			check(err)

			// sync / flush file
			file.Sync()

			// print a small summary
			fmt.Printf("%s [%s] (%d bytes)\n", filename, "new", size)
			continue
		}

		// file exists, there is nothing to write
		if os.IsExist(err) {
			// print a small summary
			fmt.Printf("%s [%s]\n", filename, "exists")
			continue
		}
	}
}

func main() {

	// Retrieve environment variables $HOME and $PWD
	pwd, err := os.Getwd()
	check(err)
	ENV["HOME"] = os.Getenv("HOME")
	ENV["PWD"] = pwd

	// Expand $HOME var inside CFG slice
	if ENV["HOME"] != "" {
		// $HOME isn't empty, we expand $HOME for CFG[0] and CFG[1]
		CFG[0] = strings.Replace(CFG[0], "$HOME", ENV["HOME"], -1)
		CFG[1] = strings.Replace(CFG[1], "$HOME", ENV["HOME"], -1)
	} else {
		// $HOME is empty, we unset CFG[0] and CFG[1]
		CFG[0] = ""
		CFG[1] = ""
	}

	// Load environment variables from all the configuration files
	// specified in the slice 'CFG'
	for _, filepath := range CFG {
		if filepath != "" {
			ret, err := Exists(filepath)
			check(err)
			//fmt.Printf("checking: %s (%t) \n", filepath, ret)

			// if the conf file exists, load it with godotenv
			if ret {
				err := godotenv.Load(filepath)
				check(err)
			}
		}
	}

	// Populate TODOENV map with environment variables
	for k, _ := range TODOENV {
		TODOENV[k] = os.Getenv(k)
	}
	//fmt.Println("map:", TODOENV)

	// Sanitize TODOENV map by expanding bash variables
	for k, v := range TODOENV {
		// expand $HOME var
		v = strings.Replace(v, "$HOME", ENV["HOME"], -1)
		v = strings.Replace(v, "${HOME}", ENV["HOME"], -1)

		// Expand $TODO_DIR var
		v = strings.Replace(v, "$TODO_DIR", TODOENV["TODO_DIR"], -1)
		v = strings.Replace(v, "${TODO_DIR}", TODOENV["TODO_DIR"], -1)

		// save the sanitized value
		TODOENV[k] = v
	}

	envCommand := cli.Command{
		Name:        "env",
		Usage:       "Prints `todo` environment information.",
		Description: "By default env prints information as a shell script.\n   The environment info will be dumped in straight-forward\n   form suitable for sourcing into a shell script.\n\n   If one or more variable names is given as arguments, env\n   prints the value of each named variable on its own line.",
		Action: func(c *cli.Context) {
			// collect all the user-submitted arguments in an array
			args := c.Args()

			// debugging
			/*fmt.Printf("pwd: %s\n", pwd)
			fmt.Printf("HOME=\"%s\"\n", ENV["HOME"])*/

			switch args.Present() {
			case true:
				// print only the required environment variables
				for _, arg := range args {
					fmt.Printf("%s=\"%s\"\n", arg, TODOENV[arg])
				}
			case false:
				// print all the environment variables
				for k, v := range TODOENV {
					fmt.Printf("%s=\"%s\"\n", k, v)
				}
			}
		},
	}

	initCommand := cli.Command{
		Name:  "init",
		Usage: "Initialize a new todo.txt structure with default values",
		Description: `
   This command creates all the template file required by the todo.txt and
   a configuration files with default values - basically, the values TODO_DIR,
   TODO_FILE, DONE_FILE, REPORT_FILE and TODO_ACTIONS_DIR are exported.

   If the option '--dest' is set then it specifies a path to use instead of
   the working directory as the destination path for the new structure.

   Running 'todo init' in a pre-initialized directory is safe; it will not
   overwrite things that are already there.`,
		Flags: []cli.Flag{
			cli.StringFlag{"dest, d", "/path/to/your/dir", "specifies a different destination path"},
		},
		Action: func(c *cli.Context) {
			destination := "."
			if c.IsSet("dest") {
				//fmt.Println("dest:", c.String("dest"))
				destination = c.String("dest")
			}
			initAction(destination)
		},
	}

	addCommand := cli.Command{
		Name:        "add",
		ShortName:   "a",
		Usage:       "Adds a task to your todo.txt file.",
		Description: "add \"feed the cat\"\n   Adds \"feed the cat\" to your todo.txt file on its own line.",
		Action: func(c *cli.Context) {
			// collect all the user-submitted arguments in an array
			args := c.Args()

			// debugging
			/*fmt.Printf("(add::Action) args (%d): %s\n", len(args), args)
			fmt.Printf("(add::Action) global flag: %s\n", c.GlobalString("t"))*/

			// check incorrect usage of the command
			switch {
			case len(args) == 0: // no options specified
				fmt.Print("\nIncorrect Usage: missing option with command \"add [task]\"\n\n")
				cli.ShowCommandHelp(c, "add")
				return
			}

			// TODO: validating input as a task

			/* task mangler
			 * - collect all the arguments into a single string
			 * - replace return carriage chars with spaces
			 * - trim the task from leading / ending spaces
			 * - honor the -t/-T global flag if present
			 */
			task := strings.Join(args[0:], " ")
			r := strings.NewReplacer("\n", " ", "\t", " ", "\r", " ")
			task = r.Replace(task)
			task = strings.TrimSpace(task)

			if c.GlobalBool("t") {
				date := time.Now().Format("2006-01-02 ")
				//fmt.Printf("(add::Action) date: %s\n", date)
				task = date + task
			}

			// save the new task
			//fmt.Printf("task: |%s|\n", task)
			addAction(task)
		},
	}

	// Initialize the app CLI
	app := cli.NewApp()
	app.Name = "go-todo"
	app.Usage = "A simple and extensible utility for managing your todo.txt files"
	app.Version = "1.0.0"
	app.Author = "Mauro Toffanin"
	app.Email = "toffanin.mauro@gmail.com"
	app.EnableBashCompletion = true
	app.Flags = []cli.Flag{
		cli.StringFlag{"t", "", "Prepend the current date to a task automatically when it's added"},
		cli.StringFlag{"T", "", "Do not prepend the current date to a task automatically when it's added."},
	}
	app.Action = func(c *cli.Context) {
		args := c.Args()

		// debugging
		/*fmt.Printf("(app) args (%d): %s\n", len(args), args)
		fmt.Printf("(app) global flag: %s\n", c.GlobalString("t"))*/

		switch c.GlobalString("t") {
		case "add":
			// inject all the arguments into a new flag set
			set := flag.NewFlagSet("add", 0)
			set.Parse([]string{"add", strings.Join(args[0:], " ")})

			// preserve the Global Flag by pushing the flag name as its value
			// (it's a hack, but it works)
			gset := flag.NewFlagSet("add", 0)
			gset.Bool("t", true, "")

			// create a new Context and run the relative Command
			c := cli.NewContext(app, set, gset)
			err := addCommand.Run(c)
			check(err)
			return
		default:
			// TODO: print error about misuse of the option -t missing 'add'
		}
		cli.ShowAppHelp(c)

	}
	app.Commands = []cli.Command{
		envCommand,
		initCommand,
		addCommand,
		/*{
			Name:  "status",
			Usage: "Obtain a summary of the todo.txt structure",
			Flags: []cli.Flag{
				cli.StringFlag{"dest, d", "", "specifies a different destination path"},
			},
			Action: func(c *cli.Context) {
				//fmt.Println("status: ", c.Args().First())
			},
		},*/
	}
	err = app.Run(os.Args)
	check(err)
}
<|endoftext|>