<commit_before>//  The MIT License (MIT) - http://opensource.org/licenses/MIT
//
//  Copyright (c) 2014 slowfei
//
//  Create on 2014-09-10
//  Update on 2015-01-15
//  Email  slowfei#foxmail.com
//  Home   http://www.slowfei.com

//
package gosfdoc

import (
	"bytes"
	"fmt"
	"github.com/slowfei/gosfcore/utils/strings"
	"os"
	"regexp"
	"strings"
)

const (
	DOC_FILE_SUFFIX = ".dc"      // document file suffix(document comments)
	NIL_DOC_NAME    = "document" // nilDocParser struct use

)

var (

	//  主要用于去除注释的前缀
	_prefixFilterTags = [][]byte{
		[]byte(" *\t"),
		[]byte(" *  "),
		[]byte(" * "),
		[]byte("//\t"),
		[]byte("//  "),
		[]byte("// "),
		[]byte("//"),
	}

	_tagStar   = []byte("*")  // comments (*)
	_tagDSlash = []byte("//") // double slash
)

/**
 *	nil document parser
 *	specifically for .doc file serve
 */
type nilDocParser struct {
}

/**
 *	see DocParser interface
 */
func (n *nilDocParser) Name() string {
	return NIL_DOC_NAME
}

/**
 *	see DocParser interface
 */
func (n *nilDocParser) CheckFile(path string, info os.FileInfo) bool {
	return strings.HasSuffix(path, DOC_FILE_SUFFIX)
}

/**
 *	see DocParser interface
 */
func (n *nilDocParser) EachIndexFile(filebuf *FileBuf) {

}

/**
 *	see DocParser interface
 */
func (n *nilDocParser) ParsePreview(filebuf *FileBuf) []Preview {
	return nil
}

/**
 *	see DocParser interface
 */
func (n *nilDocParser) ParseCodeblock(filebuf *FileBuf) []CodeBlock {
	return nil
}

/**
 *	see DocParser interface
 */
func (n *nilDocParser) ParsePackageInfo(filebuf *FileBuf) string {
	return ""
}

/**
 *	see DocParser interface
 */
func (n *nilDocParser) ParseStart(config MainConfig) {

}

/**
 *	see DocParser interface
 */
func (n *nilDocParser) ParseEnd() {

}

/**
 *	prese Preview Document CodeBlock array to markdown
 *
 *	@param `documents` after sorting
 *	@param `previews`  after sorting
 *	@param `blocks`	   after sorting
 *	@param `filesName` file names
 *	@param `version`   output document version
 *	@param `relPath`   browse code file name join path
 *	@return bytes
 */
func ParseMarkdown(documents []Document, previews []Preview, blocks []CodeBlock,
	filesName []string, version, relPath string) []byte {

	relPath = strings.TrimPrefix(relPath, "/")
	relPath = strings.TrimSuffix(relPath, "/")
	joinSymbol := ""
	if 0 != len(relPath) {
		joinSymbol = "/"
	}
	isWrite := false
	buf := bytes.NewBuffer([]byte{'\n'})

	for _, doc := range documents {
		isWrite = true
		// ## Welcome to gosfdoc
		// ------
		//
		// markdown syntax content
		//
		buf.WriteString("## " + doc.Title + "\n------\n")
		buf.WriteString(doc.Content)
		buf.WriteByte('\n')
	}

	if 0 != len(previews) {
		isWrite = true
		// ## Preview
		// ------
		// > [func Main()][#]

		// > [type TestStruct struct][#]

		// > implements: [Test][#]

		// >>[func (* TestStruct) hello(str string) string](#func_TestStruct.hello)

		// >>[func (* TestStruct) hello2() string][#]

		buf.WriteString("## Preview\n------\n")
		for _, pre := range previews {
			buf.WriteByte('\n')
			angle := ">"
			for i := 0; i
"
			}

			anchor := ""
			if 0 == len(pre.Anchor) {
				anchor = "(javascript:;)"
				// [show text](javascript:;)
			} else {
				anchor = fmt.Sprintf("(#f_%s)", pre.Anchor, pre.Anchor)
				// [show test](#f_anchor)

			}
			buf.WriteString(angle + " [" + pre.ShowText + "]" + anchor + "\n")

			if 0 != len(pre.DescText) {
				buf.WriteByte('\n')
				buf.WriteString(angle + " " + pre.DescText + "\n")
			}
		}
		buf.WriteByte('\n')
	}

	// out associate files
	if 0 != len(filesName) {
		isWrite = true
		// ###Package files
		// [a.go](#) [b.go](#) [c.go](#)
		//  a.go
		buf.WriteString("
\n### Directory files\n")
		for _, name := range filesName {
			joinPath := relPath + joinSymbol + name
			// buf.WriteString(fmt.Sprintf("[%s](src.html?v=%s&f=%s) ", name, version, joinPath))
			buf.WriteString(fmt.Sprintf("[%s](%s)", name, joinPath))
		}
		buf.WriteByte('\n')
	}

	if 0 != len(blocks) {
		isWrite = true
		buf.WriteByte('\n')
		isLinkCode := 0 != len(filesName)
		// ## Func Details
		// ------
		// ###[func (*TestStruct) hello](src.html?v=1.0.1&f=gosfdoc.go&L=12-16)  [↩](#preview_TestStruct.hello)|[#](#func_TestStruct.hello)
		// > 函数介绍描述

		// >

		// > @param `str` 字符串传递

		// > @return `v1` 返回参数v1

		// > @return v2 返回参数v2

		//
		// ```go
		// func (* TestStruct) hello(str string) (v1,v2 string)
		// ```
		currentMenuTitle := ""

		for _, block := range blocks {

			if 0 != len(block.MenuTitle) && currentMenuTitle != block.MenuTitle {
				buf.WriteString("## " + block.MenuTitle + "\n------\n")
				currentMenuTitle = block.MenuTitle
			}

			if 0 != len(block.Title) {
				linkPath := "javascript:;"
				if isLinkCode && 0 != len(block.SourceFileName) {
					lineStr := ""

					//	href = ../../../../../../../gosfdoc.go#L10-L16
					lineLen := len(block.FileLines)
					if 1 == lineLen {
						lineStr = fmt.Sprintf("#L%d", block.FileLines[0])
					} else if 2 == lineLen {
						lineStr = fmt.Sprintf("#L%d-L%d", block.FileLines[0], block.FileLines[1])
					}

					// linkPath = fmt.Sprintf("src.html?v=%s&f=%s%s%s%s", version, relPath, joinSymbol, block.SourceFileName, lineStr)
					linkPath = relPath + joinSymbol + block.SourceFileName + lineStr
				}

				anchor := ""
				if 0 != len(block.Anchor) {
					anchor = fmt.Sprintf(" [↩](#p_%s) | [#](#f_%s)", block.Anchor, block.Anchor, block.Anchor)
				}

				buf.WriteString(fmt.Sprintf("### [%s](%s) %s\n", block.Title, linkPath, anchor))
			}

			if 0 != len(block.Desc) {
				//	content description
				descLines := strings.Split(block.Desc, "\n")
				for _, desc := range descLines {
					buf.WriteString(fmt.Sprintf("> %s
\n", desc))
				}
				buf.WriteByte('\n')
			}

			// code
			if 0 != len(block.Code) {
				buf.WriteString(fmt.Sprintf("\n
%s
\n\n", block.CodeLang, block.Code))
			}

			buf.WriteByte('\n')
		}
	}

	if isWrite {
		return buf.Bytes()
	} else {
		return nil
	}
}

/**
 *  parse public document content
 *
 *  @param `fileBuf`
 *  @return document array
 */
func ParseDocument(fileBuf *FileBuf) []Document {
	var resultDocs []Document = nil

	docsBuf := fileBuf.FindAll(REGDocument)
	docsCount := len(docsBuf)

	if 0 == docsCount {
		return resultDocs
	}

	resultDocs = make([]Document, 0, docsCount)

	for i := 0; i < docsCount; i++ {
		docStruct := Document{}
		buf := docsBuf[i]

		lines := bytes.Split(buf, []byte("\n"))
		linesCount := len(lines)

		//  title and index parse
		indexTitleLine := lines[0]
		indexTitleMatch := REGDocIndexTitle.FindSubmatch(indexTitleLine)
		//  index 0 is source string
		//  index 1 is "///" || "/***"
		//  index 2 is "index-" index string
		//  index 3 is title

		if 4 == len(indexTitleMatch) {
			// extract title and z-index
			docStruct.SortTag = SFStringsUtil.ToInt(string(indexTitleMatch[2]))
			docStruct.Title = string(indexTitleMatch[3])
		}

		//  content parse
		contentBuf := bytes.NewBuffer(nil)
		var prefixTag []byte = nil
		prefixLen := 0

		for i := 1; i < linesCount-1; i++ {
			newLine := lines[i]

			if i == 1 {
				prefixTag = findPrefixFilterTag(newLine)
				prefixLen = len(prefixTag)
			}

			if nil != prefixTag {

				if 0 == bytes.Index(newLine, prefixTag) {
					contentBuf.Write(newLine[prefixLen:])
				} else {
					trimed := bytes.TrimSpace(newLine)
					// 有可能是空行，所需需要判断这行是否只有（ "*" || "//" ），如果不是则添加追加这一行内容
					if !bytes.Equal(trimed, _tagStar) && !bytes.Equal(trimed, _tagDSlash) {
						contentBuf.Write(newLine)
					}
				}

			} else {
				contentBuf.Write(newLine)
			}

			contentBuf.WriteByte('\n')
		}
		docStruct.Content = contentBuf.String()

		if 0 != len(docStruct.Content) {
			resultDocs = append(resultDocs, docStruct)
		}

	}

	return resultDocs
}

/**
 *  commons parse file about content
 *
 *  @param `fileBuf`
 *  @return about content
 */
func ParseAbout(fileBuf *FileBuf) *About {
	data := parseAboutAndIntro(fileBuf, REGAbout)

	var result *About = nil
	if 0 != len(data) {
		result = &About{Content: data}
	}

	return result
}

/**
 *  commons parse file introduction content
 *
 *  @param `fileBuf`
 *  @return introduction content
 */
func ParseIntro(fileBuf *FileBuf) *Intro {
	data := parseAboutAndIntro(fileBuf, REGIntro)

	var result *Intro = nil
	if 0 != len(data) {
		result = &Intro{Content: data}
	}

	return result
}

/**
 *  commons about intro parse
 *
 *  @param fileBuf
 *  @param rex
 */
func parseAboutAndIntro(fileBuf *FileBuf, rex *regexp.Regexp) []byte {
	var result []byte = nil
	var prefixTag []byte = nil
	prefixLen := 0

	buf := fileBuf.Find(rex)

	if 0 < len(buf) {
		appendLine := bytes.NewBuffer(nil)

		lines := bytes.Split(buf, []byte("\n"))
		linesCount := len(lines)

		for i := 1; i < linesCount-1; i++ {
			newLine := lines[i]

			if i == 1 {
				//  记录第一个前缀的标识，以第一个为准，后面的根据要求都要是相同的注释前缀。
				/**
				  (*)remove prefix tag
				  (*)
				  (*)
				*/
				prefixTag = findPrefixFilterTag(newLine)
				prefixLen = len(prefixTag)
			}

			if 0 != len(prefixTag) {

				if 0 == bytes.Index(newLine, prefixTag) {
					appendLine.Write(newLine[prefixLen:])
				} else {
					trimed := bytes.TrimSpace(newLine)
					// 有可能是空行，所需需要判断这行是否只有（ "*" || "//" ），如果不是则添加追加这一行内容
					if !bytes.Equal(trimed, _tagStar) && !bytes.Equal(trimed, _tagDSlash) {
						appendLine.Write(newLine)
					}
				}

			} else {
				appendLine.Write(newLine)
			}

			appendLine.WriteByte('\n')
		}

		if 0 < appendLine.Len() {
			result = appendLine.Bytes()
		}
	}

	return result
}

/**
 *  find prefix filter tag index
 *  //
 *  // content ("// ") is prefix tag
 *  //
 *  see var _prefixFilterTags
 */
func findPrefixFilterTag(src []byte) []byte {
	var pftCount = len(_prefixFilterTags)

	for i := 0; i < pftCount; i++ {
		prefix := _prefixFilterTags[i]
		if 0 == bytes.Index(src, prefix) {
			return prefix
		}
	}

	return nil
}
<commit_msg>修改：findPrefixFilterTag为公开函数<commit_after>//  The MIT License (MIT) - http://opensource.org/licenses/MIT
//
//  Copyright (c) 2014 slowfei
//
//  Create on 2014-09-10
//  Update on 2015-01-27
//  Email  slowfei#foxmail.com
//  Home   http://www.slowfei.com

//
package gosfdoc

import (
	"bytes"
	"fmt"
	"github.com/slowfei/gosfcore/utils/strings"
	"os"
	"regexp"
	"strings"
)

const (
	DOC_FILE_SUFFIX = ".dc"      // document file suffix(document comments)
	NIL_DOC_NAME    = "document" // nilDocParser struct use

)

var (

	//  主要用于去除注释的前缀
	_prefixFilterTags = [][]byte{
		[]byte(" *\t"),
		[]byte(" *    "),
		[]byte(" *  "),
		[]byte(" * "),
		[]byte(" //\t"),
		[]byte(" //    "),
		[]byte(" //  "),
		[]byte(" // "),
		[]byte(" //"),
		[]byte("//\t"),
		[]byte("//    "),
		[]byte("//  "),
		[]byte("// "),
		[]byte("//"),
	}

	_tagStar   = []byte("*")  // comments (*)
	_tagDSlash = []byte("//") // double slash
)

/**
 *	nil document parser
 *	specifically for .doc file serve
 */
type nilDocParser struct {
}

/**
 *	see DocParser interface
 */
func (n *nilDocParser) Name() string {
	return NIL_DOC_NAME
}

/**
 *	see DocParser interface
 */
func (n *nilDocParser) CheckFile(path string, info os.FileInfo) bool {
	return strings.HasSuffix(path, DOC_FILE_SUFFIX)
}

/**
 *	see DocParser interface
 */
func (n *nilDocParser) EachIndexFile(filebuf *FileBuf) {

}

/**
 *	see DocParser interface
 */
func (n *nilDocParser) ParsePreview(filebuf *FileBuf) []Preview {
	return nil
}

/**
 *	see DocParser interface
 */
func (n *nilDocParser) ParseCodeblock(filebuf *FileBuf) []CodeBlock {
	return nil
}

/**
 *	see DocParser interface
 */
func (n *nilDocParser) ParsePackageInfo(filebuf *FileBuf) string {
	return ""
}

/**
 *	see DocParser interface
 */
func (n *nilDocParser) ParseStart(config MainConfig) {

}

/**
 *	see DocParser interface
 */
func (n *nilDocParser) ParseEnd() {

}

/**
 *	prese Preview Document CodeBlock array to markdown
 *
 *	@param `documents` after sorting
 *	@param `previews`  after sorting
 *	@param `blocks`	   after sorting
 *	@param `filesName` file names
 *	@param `version`   output document version
 *	@param `relPath`   browse code file name join path
 *	@return bytes
 */
func ParseMarkdown(documents []Document, previews []Preview, blocks []CodeBlock,
	filesName []string, version, relPath string) []byte {

	relPath = strings.TrimPrefix(relPath, "/")
	relPath = strings.TrimSuffix(relPath, "/")
	joinSymbol := ""
	if 0 != len(relPath) {
		joinSymbol = "/"
	}
	isWrite := false
	buf := bytes.NewBuffer([]byte{'\n'})

	for _, doc := range documents {
		isWrite = true
		// ## Welcome to gosfdoc
		// ------
		//
		// markdown syntax content
		//
		buf.WriteString("## " + doc.Title + "\n------\n")
		buf.WriteString(doc.Content)
		buf.WriteByte('\n')
	}

	if 0 != len(previews) {
		isWrite = true
		// ## Preview
		// ------
		// > [func Main()][#]

		// > [type TestStruct struct][#]

		// > implements: [Test][#]

		// >>[func (* TestStruct) hello(str string) string](#func_TestStruct.hello)

		// >>[func (* TestStruct) hello2() string][#]

		buf.WriteString("## Preview\n------\n")
		for _, pre := range previews {
			buf.WriteByte('\n')
			angle := ">"
			for i := 0; i
"
			}

			anchor := ""
			if 0 == len(pre.Anchor) {
				anchor = "(javascript:;)"
				// [show text](javascript:;)
			} else {
				anchor = fmt.Sprintf("(#f_%s)", pre.Anchor, pre.Anchor)
				// [show test](#f_anchor)

			}
			buf.WriteString(angle + " [" + pre.ShowText + "]" + anchor + "\n")

			if 0 != len(pre.DescText) {
				buf.WriteByte('\n')
				buf.WriteString(angle + " " + pre.DescText + "\n")
			}
		}
		buf.WriteByte('\n')
	}

	// out associate files
	if 0 != len(filesName) {
		isWrite = true
		// ###Package files
		// [a.go](#) [b.go](#) [c.go](#)
		//  a.go
		buf.WriteString("
\n### Directory files\n")
		for _, name := range filesName {
			joinPath := relPath + joinSymbol + name
			// buf.WriteString(fmt.Sprintf("[%s](src.html?v=%s&f=%s) ", name, version, joinPath))
			buf.WriteString(fmt.Sprintf("[%s](%s)", name, joinPath))
		}
		buf.WriteByte('\n')
	}

	if 0 != len(blocks) {
		isWrite = true
		buf.WriteByte('\n')
		isLinkCode := 0 != len(filesName)
		// ## Func Details
		// ------
		// ###[func (*TestStruct) hello](src.html?v=1.0.1&f=gosfdoc.go&L=12-16)  [↩](#preview_TestStruct.hello)|[#](#func_TestStruct.hello)
		// > 函数介绍描述

		// >

		// > @param `str` 字符串传递

		// > @return `v1` 返回参数v1

		// > @return v2 返回参数v2

		//
		// ```go
		// func (* TestStruct) hello(str string) (v1,v2 string)
		// ```
		currentMenuTitle := ""

		for _, block := range blocks {

			if 0 != len(block.MenuTitle) && currentMenuTitle != block.MenuTitle {
				buf.WriteString("## " + block.MenuTitle + "\n------\n")
				currentMenuTitle = block.MenuTitle
			}

			if 0 != len(block.Title) {
				linkPath := "javascript:;"
				if isLinkCode && 0 != len(block.SourceFileName) {
					lineStr := ""

					//	href = ../../../../../../../gosfdoc.go#L10-L16
					lineLen := len(block.FileLines)
					if 1 == lineLen {
						lineStr = fmt.Sprintf("#L%d", block.FileLines[0])
					} else if 2 == lineLen {
						lineStr = fmt.Sprintf("#L%d-L%d", block.FileLines[0], block.FileLines[1])
					}

					// linkPath = fmt.Sprintf("src.html?v=%s&f=%s%s%s%s", version, relPath, joinSymbol, block.SourceFileName, lineStr)
					linkPath = relPath + joinSymbol + block.SourceFileName + lineStr
				}

				anchor := ""
				if 0 != len(block.Anchor) {
					anchor = fmt.Sprintf(" [↩](#p_%s) | [#](#f_%s)", block.Anchor, block.Anchor, block.Anchor)
				}

				buf.WriteString(fmt.Sprintf("### [%s](%s) %s\n", block.Title, linkPath, anchor))
			}

			if 0 != len(block.Desc) {
				//	content description
				descLines := strings.Split(block.Desc, "\n")
				for _, desc := range descLines {
					buf.WriteString(fmt.Sprintf("> %s
\n", desc))
				}
				buf.WriteByte('\n')
			}

			// code
			if 0 != len(block.Code) {
				buf.WriteString(fmt.Sprintf("\n
%s
\n\n", block.CodeLang, block.Code))
			}

			buf.WriteByte('\n')
		}
	}

	if isWrite {
		return buf.Bytes()
	} else {
		return nil
	}
}

/**
 *  parse public document content
 *
 *  @param `fileBuf`
 *  @return document array
 */
func ParseDocument(fileBuf *FileBuf) []Document {
	var resultDocs []Document = nil

	docsBuf := fileBuf.FindAll(REGDocument)
	docsCount := len(docsBuf)

	if 0 == docsCount {
		return resultDocs
	}

	resultDocs = make([]Document, 0, docsCount)

	for i := 0; i < docsCount; i++ {
		docStruct := Document{}
		buf := docsBuf[i]

		lines := bytes.Split(buf, []byte("\n"))
		linesCount := len(lines)

		//  title and index parse
		indexTitleLine := lines[0]
		indexTitleMatch := REGDocIndexTitle.FindSubmatch(indexTitleLine)
		//  index 0 is source string
		//  index 1 is "///" || "/***"
		//  index 2 is "index-" index string
		//  index 3 is title

		if 4 == len(indexTitleMatch) {
			// extract title and z-index
			docStruct.SortTag = SFStringsUtil.ToInt(string(indexTitleMatch[2]))
			docStruct.Title = string(indexTitleMatch[3])
		}

		//  content parse
		contentBuf := bytes.NewBuffer(nil)
		var prefixTag []byte = nil
		prefixLen := 0

		for i := 1; i < linesCount-1; i++ {
			newLine := lines[i]

			if i == 1 {
				prefixTag = FindPrefixFilterTag(newLine)
				prefixLen = len(prefixTag)
			}

			if nil != prefixTag {

				if 0 == bytes.Index(newLine, prefixTag) {
					contentBuf.Write(newLine[prefixLen:])
				} else {
					trimed := bytes.TrimSpace(newLine)
					// 有可能是空行，所需需要判断这行是否只有（ "*" || "//" ），如果不是则添加追加这一行内容
					if !bytes.Equal(trimed, _tagStar) && !bytes.Equal(trimed, _tagDSlash) {
						contentBuf.Write(newLine)
					}
				}

			} else {
				contentBuf.Write(newLine)
			}

			contentBuf.WriteByte('\n')
		}
		docStruct.Content = contentBuf.String()

		if 0 != len(docStruct.Content) {
			resultDocs = append(resultDocs, docStruct)
		}

	}

	return resultDocs
}

/**
 *  commons parse file about content
 *
 *  @param `fileBuf`
 *  @return about content
 */
func ParseAbout(fileBuf *FileBuf) *About {
	data := parseAboutAndIntro(fileBuf, REGAbout)

	var result *About = nil
	if 0 != len(data) {
		result = &About{Content: data}
	}

	return result
}

/**
 *  commons parse file introduction content
 *
 *  @param `fileBuf`
 *  @return introduction content
 */
func ParseIntro(fileBuf *FileBuf) *Intro {
	data := parseAboutAndIntro(fileBuf, REGIntro)

	var result *Intro = nil
	if 0 != len(data) {
		result = &Intro{Content: data}
	}

	return result
}

/**
 *  commons about intro parse
 *
 *  @param fileBuf
 *  @param rex
 */
func parseAboutAndIntro(fileBuf *FileBuf, rex *regexp.Regexp) []byte {
	var result []byte = nil
	var prefixTag []byte = nil
	prefixLen := 0

	buf := fileBuf.Find(rex)

	if 0 < len(buf) {
		appendLine := bytes.NewBuffer(nil)

		lines := bytes.Split(buf, []byte("\n"))
		linesCount := len(lines)

		for i := 1; i < linesCount-1; i++ {
			newLine := lines[i]

			if i == 1 {
				//  记录第一个前缀的标识，以第一个为准，后面的根据要求都要是相同的注释前缀。
				/**
				  (*)remove prefix tag
				  (*)
				  (*)
				*/
				prefixTag = FindPrefixFilterTag(newLine)
				prefixLen = len(prefixTag)
			}

			if 0 != len(prefixTag) {

				if 0 == bytes.Index(newLine, prefixTag) {
					appendLine.Write(newLine[prefixLen:])
				} else {
					trimed := bytes.TrimSpace(newLine)
					// 有可能是空行，所需需要判断这行是否只有（ "*" || "//" ），如果不是则添加追加这一行内容
					if !bytes.Equal(trimed, _tagStar) && !bytes.Equal(trimed, _tagDSlash) {
						appendLine.Write(newLine)
					}
				}

			} else {
				appendLine.Write(newLine)
			}

			appendLine.WriteByte('\n')
		}

		if 0 < appendLine.Len() {
			result = appendLine.Bytes()
		}
	}

	return result
}

/**
 *  find prefix filter tag index
 *  //
 *  // content ("// ") is prefix tag
 *  //
 *  see var _prefixFilterTags
 */
func FindPrefixFilterTag(src []byte) []byte {
	var pftCount = len(_prefixFilterTags)

	for i := 0; i < pftCount; i++ {
		prefix := _prefixFilterTags[i]
		if 0 == bytes.Index(src, prefix) {
			return prefix
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package irmago

import "encoding/xml"

// SchemeManagerDescription describes a scheme manager.
type SchemeManagerDescription struct {
	Name              string `xml:"Id"`
	URL               string `xml:"Contact"`
	HRName            string `xml:"Name"`
	Description       string
	KeyshareServer    string
	KeyshareWebsite   string
	KeyshareAttribute string
	XMLversion        int      `xml:"version,attr"`
	XMLName           xml.Name `xml:"SchemeManager"`
}

// IssuerDescription describes an issuer.
type IssuerDescription struct {
	HRName            string `xml:"Name"`
	HRShortName       string `xml:"ShortName"`
	Name              string `xml:"ID"`
	SchemeManagerName string `xml:"SchemeManager"`
	ContactAddress    string
	ContactEMail      string
	URL               string `xml:"baseURL"`
	XMLversion        int    `xml:"version,attr"`
	identifier        *IssuerIdentifier
}

// CredentialDescription is a description of a credential type, specifying (a.o.) its name, issuer, and attributes.
type CredentialDescription struct {
	HRName            string `xml:"Name"`
	HRShortName       string `xml:"ShortName"`
	IssuerName        string `xml:"IssuerID"`
	SchemeManagerName string `xml:"SchemeManager"`
	Name              string `xml:"CredentialID"`
	IsSingleton       bool   `xml:"ShouldBeSingleton"`
	Description       string
	Attributes        []AttributeDesscription `xml:"Attributes>Attribute"`
	XMLversion        int                     `xml:"version,attr"`
	XMLName           xml.Name                `xml:"IssueSpecification"`
	identifier        *CredentialTypeIdentifier
}

// AttributeDesscription is a description of an attribute within a credential type.
type AttributeDesscription struct {
	Name        string
	Description string
}

// Identifier returns the identifier of the specified credential type.
func (cd *CredentialDescription) Identifier() *CredentialTypeIdentifier {
	if cd.identifier == nil {
		cd.identifier = NewCredentialTypeIdentifier(cd.SchemeManagerName + "." + cd.IssuerName + "." + cd.Name)
	}
	return cd.identifier
}

// Identifier returns the identifier of the specified issuer description.
func (id *IssuerDescription) Identifier() *IssuerIdentifier {
	if id.identifier == nil {
		id.identifier = NewIssuerIdentifier(id.SchemeManagerName + "." + id.Name)
	}
	return id.identifier
}
<commit_msg>Rename things<commit_after>package irmago

import "encoding/xml"

// SchemeManagerDescription describes a scheme manager.
type SchemeManagerDescription struct {
	Name              string `xml:"Id"`
	URL               string `xml:"Contact"`
	HRName            string `xml:"Name"`
	Description       string
	KeyshareServer    string
	KeyshareWebsite   string
	KeyshareAttribute string
	XMLVersion        int      `xml:"version,attr"`
	XMLName           xml.Name `xml:"SchemeManager"`
}

// IssuerDescription describes an issuer.
type IssuerDescription struct {
	HRName            string `xml:"Name"`
	HRShortName       string `xml:"ShortName"`
	Name              string `xml:"ID"`
	SchemeManagerName string `xml:"SchemeManager"`
	ContactAddress    string
	ContactEMail      string
	URL               string `xml:"baseURL"`
	XMLVersion        int    `xml:"version,attr"`
	identifier        *IssuerIdentifier
}

// CredentialDescription is a description of a credential type, specifying (a.o.) its name, issuer, and attributes.
type CredentialDescription struct {
	HRName            string `xml:"Name"`
	HRShortName       string `xml:"ShortName"`
	IssuerName        string `xml:"IssuerID"`
	SchemeManagerName string `xml:"SchemeManager"`
	Name              string `xml:"CredentialID"`
	IsSingleton       bool   `xml:"ShouldBeSingleton"`
	Description       string
	Attributes        []AttributeDescription `xml:"Attributes>Attribute"`
	XMLVersion        int                    `xml:"version,attr"`
	XMLName           xml.Name               `xml:"IssueSpecification"`
	identifier        *CredentialTypeIdentifier
}

// AttributeDescription is a description of an attribute within a credential type.
type AttributeDescription struct {
	Name        string
	Description string
}

// Identifier returns the identifier of the specified credential type.
func (cd *CredentialDescription) Identifier() *CredentialTypeIdentifier {
	if cd.identifier == nil {
		cd.identifier = NewCredentialTypeIdentifier(cd.SchemeManagerName + "." + cd.IssuerName + "." + cd.Name)
	}
	return cd.identifier
}

// Identifier returns the identifier of the specified issuer description.
func (id *IssuerDescription) Identifier() *IssuerIdentifier {
	if id.identifier == nil {
		id.identifier = NewIssuerIdentifier(id.SchemeManagerName + "." + id.Name)
	}
	return id.identifier
}
<|endoftext|>


<commit_before>package imgurl

import (
	"github.com/nfnt/resize"
	"net/http"
	"image/jpeg"
	"image/png"
	"image/gif"
	"image"
	"fmt"
	"errors"
	"encoding/base64"
	"bytes"
	"io"
	"log"
)

type Filter func(image.Image) (image.Image,interface{})

// UrilfyC fetches the image referenced by the given url, scales it to the given sizes keeping the aspect ratio
// and transcods it to a base64 encoded data url.
// Hereby the given http Client is used.
func UrlifyC(c *http.Client, url string, maxwidth,maxheight int,filters ...Filter) (ret string, tags []interface{}, err error) {
	resp, err := c.Get(url)
	if err != nil {
		return
	}

	defer resp.Body.Close()

	mt := resp.Header["Content-Type"]

	return UrlifyR(resp.Body,mt[0],maxwidth,maxheight,filters...)
}

// UrlifyR reads the image fromt he given reader, scales it to the given sizes keeping the apsect ratio
// and transcods it to a base74 encoded data url.
func UrlifyR(source io.Reader,mt string, maxwidth, maxheight int,filters ...Filter) (ret string, tags []interface{}, err error) {
	defer func() {
			if r := recover(); r != nil {
				err = errors.New(fmt.Sprintf("Panic in Urlify: %s",r))
				log.Println(err)
			}
		}()
	img,err := Decode(source,mt,maxwidth,maxheight)
	if err != nil {
		return
	}

	tags = make([]interface{},len(filters))
	for i,f := range filters {
		img,tags[i] = f(img)
	}

	ret,err = encode(img)
	return
}

// Urilfy fetches the image referenced by the given url, scales it to the given sizes keeping the aspect ratio
// and transcods it to a base64 encoded data url.
func Urlify(url string, maxwidth,maxheight int,filters ...Filter) (ret string, tags []interface{}, err error) {
	return UrlifyC(http.DefaultClient,url,maxwidth,maxheight,filters...)
}

// Decode reads the given image and scales it to the given size keeping the aspect ratio
func Decode(source io.Reader,mt string,maxwidth,maxheight int) (i image.Image, err error) {
	var img image.Image
	switch mt {
		case "image/jpeg","image/jpg":
			img, err = jpeg.Decode(source)
		case "image/png":
			img, err = png.Decode(source)
		case "image/gif":
			img, err = gif.Decode(source)
		default:
			return nil,errors.New(fmt.Sprintf("Unsupported content type: %s",mt))
	}
	scaled := resize.Thumbnail(uint(maxwidth),uint(maxheight),img,resize.Bilinear)
	return scaled, nil
}

// encode ecnodes the image into a base64 png data url.
func encode(source image.Image) (ret string, err error) {
	buf:= new(bytes.Buffer)
	err = png.Encode(buf,source)
	if err != nil {
		return
	}

	ret = fmt.Sprintf("data:image/png;base64,%s",base64.StdEncoding.EncodeToString(buf.Bytes()))
	return
}
<commit_msg>Added convenience function.<commit_after>package imgurl

import (
	"github.com/nfnt/resize"
	"net/http"
	"image/jpeg"
	"image/png"
	"image/gif"
	"image"
	"fmt"
	"errors"
	"encoding/base64"
	"bytes"
	"io"
	"log"
)

type Filter func(image.Image) (image.Image,interface{})

// UrilfyC fetches the image referenced by the given url, scales it to the given sizes keeping the aspect ratio
// and transcods it to a base64 encoded data url.
// Hereby the given http Client is used.
func UrlifyC(c *http.Client, url string, maxwidth,maxheight int,filters ...Filter) (ret string, tags []interface{}, err error) {
	resp, err := c.Get(url)
	if err != nil {
		return
	}

	defer resp.Body.Close()

	mt := resp.Header["Content-Type"]

	return UrlifyR(resp.Body,mt[0],maxwidth,maxheight,filters...)
}

// UrlifyR reads the image fromt he given reader, scales it to the given sizes keeping the apsect ratio
// and transcods it to a base74 encoded data url.
func UrlifyR(source io.Reader,mt string, maxwidth, maxheight int,filters ...Filter) (ret string, tags []interface{}, err error) {
	defer func() {
			if r := recover(); r != nil {
				err = errors.New(fmt.Sprintf("Panic in Urlify: %s",r))
				log.Println(err)
			}
		}()
	img,err := Decode(source,mt,maxwidth,maxheight)
	if err != nil {
		return
	}

	tags = make([]interface{},len(filters))
	for i,f := range filters {
		img,tags[i] = f(img)
	}

	ret,err = encode(img)
	return
}

// Urilfy fetches the image referenced by the given url, scales it to the given sizes keeping the aspect ratio
// and transcods it to a base64 encoded data url.
func Urlify(url string, maxwidth,maxheight int,filters ...Filter) (ret string, tags []interface{}, err error) {
	return UrlifyC(http.DefaultClient,url,maxwidth,maxheight,filters...)
}

// Decode reads the given image and scales it to the given size keeping the aspect ratio
func Decode(source io.Reader,mt string,mwh ...int) (i image.Image, err error) {
	var img image.Image
	switch mt {
		case "image/jpeg","image/jpg":
			img, err = jpeg.Decode(source)
		case "image/png":
			img, err = png.Decode(source)
		case "image/gif":
			img, err = gif.Decode(source)
		default:
			return nil,errors.New(fmt.Sprintf("Unsupported content type: %s",mt))
	}
	if len(mwh) > 0{
		var mw,mh int
		mw = mwh[0]
		if len(mwh) > 1 {
			mh = mwh[1]
		}

		if (mw > 0 && mh > 0) {
			return resize.Thumbnail(uint(mw),uint(mh),img,resize.Bilinear), err
		} else {
			return img,err
		}
	} else {
		return img, err
	}
}

// encode ecnodes the image into a base64 png data url.
func encode(source image.Image) (ret string, err error) {
	buf:= new(bytes.Buffer)
	err = png.Encode(buf,source)
	if err != nil {
		return
	}

	ret = fmt.Sprintf("data:image/png;base64,%s",base64.StdEncoding.EncodeToString(buf.Bytes()))
	return
}
<|endoftext|>


<commit_before>/* Copyright (c) 2014, Daniel Martí <mvdan@mvdan.cc> */
/* See LICENSE for licensing information */

package main

import (
	"compress/zlib"
	"errors"
	"flag"
	"fmt"
	"html/template"
	"io"
	"log"
	"math/rand"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"time"
)

const (
	idSize    = 8 // should be between 6 and 256
	indexTmpl = "index.html"
	chars     = "abcdefghijklmnopqrstuvwxyz0123456789"

	// GET error messages
	invalidId     = "Invalid paste id."
	pasteNotFound = "Paste doesn't exist."
	unknownError  = "Something went terribly wrong."
	// POST error messages
	missingForm = "Paste could not be found inside the posted form."
)

var (
	siteUrl     = flag.String("u", "http://localhost:9090", "URL of the site")
	listen      = flag.String("l", "localhost:9090", "Host and port to listen to")
	dataDir     = flag.String("d", "data", "Directory to store all the pastes in")
	lifeTimeStr = flag.String("t", "12h", "Lifetime of the pastes (units: s,m,h)")
	maxSizeStr  = flag.String("s", "1M", "Maximum size of POSTs in bytes (units: B,K,M)")

	lifeTime time.Duration
	maxSize  ByteSize

	validId       = regexp.MustCompile("^[a-zA-Z0-9]{" + strconv.FormatInt(idSize, 10) + "}$")
	regexByteSize = regexp.MustCompile(`^([\d\.]+)\s*([KM]?B|[BKM])$`)
	indexTemplate *template.Template
)

func pathId(id string) string {
	return path.Join(id[0:2], id[2:4], id[4:])
}

type ByteSize int64

const (
	B ByteSize = 1 << (10 * iota)
	KB
	MB
)

func (b ByteSize) String() string {
	switch {
	case b >= MB:
		return fmt.Sprintf("%.2f MB", float64(b)/float64(MB))
	case b >= KB:
		return fmt.Sprintf("%.2f KB", float64(b)/float64(KB))
	}
	return fmt.Sprintf("%d B", b)
}

func parseByteSize(str string) (ByteSize, error) {
	if !regexByteSize.MatchString(str) {
		return 0, errors.New("Could not parse size in bytes")
	}
	parts := regexByteSize.FindStringSubmatch(str)
	size, _ := strconv.ParseFloat(string(parts[1]), 64)

	switch string(parts[2]) {
	case "KB", "K":
		size *= float64(KB)
	case "MB", "M":
		size *= float64(MB)
	}
	return ByteSize(size), nil
}

func randomId() string {
	s := make([]byte, idSize)
	var offset uint = 0
	for {
		r := rand.Int63()
		for i := 0; i < 8; i++ {
			randbyte := int(r&0xff) % len(chars)
			s[offset] = chars[randbyte]
			offset++
			if offset == idSize {
				return string(s)
			}
			r >>= 8
		}
	}
	return strings.Repeat(chars[0:1], idSize)
}

func endLife(path string) {
	err := os.Remove(path)
	if err == nil {
		log.Printf("Removed paste: %s", path)
	} else {
		log.Printf("Could not end the life of %s: %s", path, err)
		programDeath(path, 2*time.Minute)
	}
}

func programDeath(path string, after time.Duration) {
	timer := time.NewTimer(after)
	go func() {
		<-timer.C
		endLife(path)
	}()
}

func handler(w http.ResponseWriter, r *http.Request) {
	var err error
	switch r.Method {
	case "GET":
		var id, pastePath string
		id = r.URL.Path[1:]
		if len(id) == 0 {
			indexTemplate.Execute(w, *siteUrl)
			return
		}
		if !validId.MatchString(id) {
			w.WriteHeader(http.StatusBadRequest)
			fmt.Fprintf(w, "%s\n", invalidId)
			return
		}
		id = strings.ToLower(id)
		pastePath = pathId(id)
		pasteFile, err := os.Open(pastePath)
		if err != nil {
			w.WriteHeader(http.StatusNotFound)
			fmt.Fprintf(w, "%s\n", pasteNotFound)
			return
		}
		compReader, err := zlib.NewReader(pasteFile)
		if err != nil {
			log.Printf("Could not open a compression reader for %s: %s", pastePath, err)
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintf(w, "%s\n", unknownError)
			return
		}
		io.Copy(w, compReader)
		compReader.Close()
		pasteFile.Close()

	case "POST":
		r.Body = http.MaxBytesReader(w, r.Body, int64(maxSize))
		var id, pastePath string
		for {
			id = randomId()
			pastePath = pathId(id)
			if _, err := os.Stat(pastePath); os.IsNotExist(err) {
				break
			}
		}
		if err = r.ParseMultipartForm(int64(maxSize)); err != nil {
			log.Printf("Could not parse POST multipart form: %s", err)
			w.WriteHeader(http.StatusBadRequest)
			fmt.Fprintf(w, "%s\n", err)
			return
		}
		var content string
		if vs, found := r.Form["paste"]; found {
			content = vs[0]
		} else {
			w.WriteHeader(http.StatusBadRequest)
			fmt.Fprintf(w, "%s\n", missingForm)
			return
		}
		dir, _ := path.Split(pastePath)
		if err = os.MkdirAll(dir, 0700); err != nil {
			log.Printf("Could not create directories leading to %s: %s", pastePath, err)
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintf(w, "%s\n", unknownError)
			return
		}
		programDeath(pastePath, lifeTime)
		pasteFile, err := os.OpenFile(pastePath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
		if err != nil {
			log.Printf("Could not create new paste pasteFile %s: %s", pastePath, err)
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintf(w, "%s\n", unknownError)
			return
		}
		compWriter := zlib.NewWriter(pasteFile)
		b, err := io.WriteString(compWriter, content)
		compWriter.Close()
		pasteFile.Close()
		if err != nil {
			log.Printf("Could not write compressed data into %s: %s", pastePath, err)
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintf(w, "%s\n", unknownError)
			return
		}
		writtenSize := ByteSize(b)
		log.Printf("Created a new paste: %s (%s)", pastePath, writtenSize)
		fmt.Fprintf(w, "%s/%s\n", *siteUrl, id)
	}
}

func walkFunc(path string, info os.FileInfo, err error) error {
	if err != nil {
		return err
	}
	if info.IsDir() {
		return nil
	}
	deathTime := info.ModTime().Add(lifeTime)
	now := time.Now()
	if deathTime.Before(now) {
		go endLife(path)
		return nil
	}
	var lifeLeft time.Duration
	if deathTime.After(now.Add(lifeTime)) {
		lifeLeft = lifeTime
	} else {
		lifeLeft = deathTime.Sub(now)
	}
	log.Printf("Recovered paste %s has %s left", path, lifeLeft)
	programDeath(path, lifeLeft)
	return nil
}

func main() {
	var err error
	flag.Parse()
	if lifeTime, err = time.ParseDuration(*lifeTimeStr); err != nil {
		log.Printf("Invalid lifetime '%s': %s", lifeTimeStr, err)
		return
	}
	if maxSize, err = parseByteSize(*maxSizeStr); err != nil {
		log.Printf("Invalid max size '%s': %s", maxSizeStr, err)
		return
	}
	if indexTemplate, err = template.ParseFiles(indexTmpl); err != nil {
		log.Printf("Could not load template %s: %s", indexTmpl, err)
		return
	}
	if err = os.MkdirAll(*dataDir, 0700); err != nil {
		log.Printf("Could not create data directory %s: %s", *dataDir, err)
		return
	}
	if err = os.Chdir(*dataDir); err != nil {
		log.Printf("Could not enter data directory %s: %s", *dataDir, err)
		return
	}
	if err = filepath.Walk(".", walkFunc); err != nil {
		log.Printf("Could not recover data directory %s: %s", *dataDir, err)
		return
	}
	log.Printf("idSize   = %d", idSize)
	log.Printf("maxSize  = %s", maxSize)
	log.Printf("siteUrl  = %s", *siteUrl)
	log.Printf("listen   = %s", *listen)
	log.Printf("dataDir  = %s", *dataDir)
	log.Printf("lifeTime = %s", lifeTime)
	http.HandleFunc("/", handler)
	log.Fatal(http.ListenAndServe(*listen, nil))
}
<commit_msg>No need to use explicit returns in main()<commit_after>/* Copyright (c) 2014, Daniel Martí <mvdan@mvdan.cc> */
/* See LICENSE for licensing information */

package main

import (
	"compress/zlib"
	"errors"
	"flag"
	"fmt"
	"html/template"
	"io"
	"log"
	"math/rand"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"time"
)

const (
	idSize    = 8 // should be between 6 and 256
	indexTmpl = "index.html"
	chars     = "abcdefghijklmnopqrstuvwxyz0123456789"

	// GET error messages
	invalidId     = "Invalid paste id."
	pasteNotFound = "Paste doesn't exist."
	unknownError  = "Something went terribly wrong."
	// POST error messages
	missingForm = "Paste could not be found inside the posted form."
)

var (
	siteUrl     = flag.String("u", "http://localhost:9090", "URL of the site")
	listen      = flag.String("l", "localhost:9090", "Host and port to listen to")
	dataDir     = flag.String("d", "data", "Directory to store all the pastes in")
	lifeTimeStr = flag.String("t", "12h", "Lifetime of the pastes (units: s,m,h)")
	maxSizeStr  = flag.String("s", "1M", "Maximum size of POSTs in bytes (units: B,K,M)")

	lifeTime time.Duration
	maxSize  ByteSize

	validId       = regexp.MustCompile("^[a-zA-Z0-9]{" + strconv.FormatInt(idSize, 10) + "}$")
	regexByteSize = regexp.MustCompile(`^([\d\.]+)\s*([KM]?B|[BKM])$`)
	indexTemplate *template.Template
)

func pathId(id string) string {
	return path.Join(id[0:2], id[2:4], id[4:])
}

type ByteSize int64

const (
	B ByteSize = 1 << (10 * iota)
	KB
	MB
)

func (b ByteSize) String() string {
	switch {
	case b >= MB:
		return fmt.Sprintf("%.2f MB", float64(b)/float64(MB))
	case b >= KB:
		return fmt.Sprintf("%.2f KB", float64(b)/float64(KB))
	}
	return fmt.Sprintf("%d B", b)
}

func parseByteSize(str string) (ByteSize, error) {
	if !regexByteSize.MatchString(str) {
		return 0, errors.New("Could not parse size in bytes")
	}
	parts := regexByteSize.FindStringSubmatch(str)
	size, _ := strconv.ParseFloat(string(parts[1]), 64)

	switch string(parts[2]) {
	case "KB", "K":
		size *= float64(KB)
	case "MB", "M":
		size *= float64(MB)
	}
	return ByteSize(size), nil
}

func randomId() string {
	s := make([]byte, idSize)
	var offset uint = 0
	for {
		r := rand.Int63()
		for i := 0; i < 8; i++ {
			randbyte := int(r&0xff) % len(chars)
			s[offset] = chars[randbyte]
			offset++
			if offset == idSize {
				return string(s)
			}
			r >>= 8
		}
	}
	return strings.Repeat(chars[0:1], idSize)
}

func endLife(path string) {
	err := os.Remove(path)
	if err == nil {
		log.Printf("Removed paste: %s", path)
	} else {
		log.Printf("Could not end the life of %s: %s", path, err)
		programDeath(path, 2*time.Minute)
	}
}

func programDeath(path string, after time.Duration) {
	timer := time.NewTimer(after)
	go func() {
		<-timer.C
		endLife(path)
	}()
}

func handler(w http.ResponseWriter, r *http.Request) {
	var err error
	switch r.Method {
	case "GET":
		var id, pastePath string
		id = r.URL.Path[1:]
		if len(id) == 0 {
			indexTemplate.Execute(w, *siteUrl)
			return
		}
		if !validId.MatchString(id) {
			w.WriteHeader(http.StatusBadRequest)
			fmt.Fprintf(w, "%s\n", invalidId)
			return
		}
		id = strings.ToLower(id)
		pastePath = pathId(id)
		pasteFile, err := os.Open(pastePath)
		if err != nil {
			w.WriteHeader(http.StatusNotFound)
			fmt.Fprintf(w, "%s\n", pasteNotFound)
			return
		}
		compReader, err := zlib.NewReader(pasteFile)
		if err != nil {
			log.Printf("Could not open a compression reader for %s: %s", pastePath, err)
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintf(w, "%s\n", unknownError)
			return
		}
		io.Copy(w, compReader)
		compReader.Close()
		pasteFile.Close()

	case "POST":
		r.Body = http.MaxBytesReader(w, r.Body, int64(maxSize))
		var id, pastePath string
		for {
			id = randomId()
			pastePath = pathId(id)
			if _, err := os.Stat(pastePath); os.IsNotExist(err) {
				break
			}
		}
		if err = r.ParseMultipartForm(int64(maxSize)); err != nil {
			log.Printf("Could not parse POST multipart form: %s", err)
			w.WriteHeader(http.StatusBadRequest)
			fmt.Fprintf(w, "%s\n", err)
			return
		}
		var content string
		if vs, found := r.Form["paste"]; found {
			content = vs[0]
		} else {
			w.WriteHeader(http.StatusBadRequest)
			fmt.Fprintf(w, "%s\n", missingForm)
			return
		}
		dir, _ := path.Split(pastePath)
		if err = os.MkdirAll(dir, 0700); err != nil {
			log.Printf("Could not create directories leading to %s: %s", pastePath, err)
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintf(w, "%s\n", unknownError)
			return
		}
		programDeath(pastePath, lifeTime)
		pasteFile, err := os.OpenFile(pastePath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
		if err != nil {
			log.Printf("Could not create new paste pasteFile %s: %s", pastePath, err)
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintf(w, "%s\n", unknownError)
			return
		}
		compWriter := zlib.NewWriter(pasteFile)
		b, err := io.WriteString(compWriter, content)
		compWriter.Close()
		pasteFile.Close()
		if err != nil {
			log.Printf("Could not write compressed data into %s: %s", pastePath, err)
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintf(w, "%s\n", unknownError)
			return
		}
		writtenSize := ByteSize(b)
		log.Printf("Created a new paste: %s (%s)", pastePath, writtenSize)
		fmt.Fprintf(w, "%s/%s\n", *siteUrl, id)
	}
}

func walkFunc(path string, info os.FileInfo, err error) error {
	if err != nil {
		return err
	}
	if info.IsDir() {
		return nil
	}
	deathTime := info.ModTime().Add(lifeTime)
	now := time.Now()
	if deathTime.Before(now) {
		go endLife(path)
		return nil
	}
	var lifeLeft time.Duration
	if deathTime.After(now.Add(lifeTime)) {
		lifeLeft = lifeTime
	} else {
		lifeLeft = deathTime.Sub(now)
	}
	log.Printf("Recovered paste %s has %s left", path, lifeLeft)
	programDeath(path, lifeLeft)
	return nil
}

func main() {
	var err error
	flag.Parse()
	if lifeTime, err = time.ParseDuration(*lifeTimeStr); err != nil {
		log.Fatalf("Invalid lifetime '%s': %s", lifeTimeStr, err)
	}
	if maxSize, err = parseByteSize(*maxSizeStr); err != nil {
		log.Fatalf("Invalid max size '%s': %s", maxSizeStr, err)
	}
	if indexTemplate, err = template.ParseFiles(indexTmpl); err != nil {
		log.Fatalf("Could not load template %s: %s", indexTmpl, err)
	}
	if err = os.MkdirAll(*dataDir, 0700); err != nil {
		log.Fatalf("Could not create data directory %s: %s", *dataDir, err)
	}
	if err = os.Chdir(*dataDir); err != nil {
		log.Fatalf("Could not enter data directory %s: %s", *dataDir, err)
	}
	if err = filepath.Walk(".", walkFunc); err != nil {
		log.Fatalf("Could not recover data directory %s: %s", *dataDir, err)
	}
	log.Printf("idSize   = %d", idSize)
	log.Printf("maxSize  = %s", maxSize)
	log.Printf("siteUrl  = %s", *siteUrl)
	log.Printf("listen   = %s", *listen)
	log.Printf("dataDir  = %s", *dataDir)
	log.Printf("lifeTime = %s", lifeTime)
	http.HandleFunc("/", handler)
	log.Fatal(http.ListenAndServe(*listen, nil))
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"compress/gzip"
	"crypto/sha256"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"os"
	"os/exec"
	"runtime"
	"time"

	"github.com/flynn/flynn/Godeps/_workspace/src/bitbucket.org/kardianos/osext"
	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/inconshreveable/go-update"
	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/kr/binarydist"
	"github.com/flynn/flynn/controller/client"
)

var cmdUpdate = &Command{
	Run:      runUpdate,
	Usage:    "update",
	NoClient: true,
	Long: `
Update downloads and installs the next version of flynn-cli.

This command is unlisted, since users never have to run it directly.
`,
}

func runUpdate(cmd *Command, args []string, client *controller.Client) error {
	if updater == nil {
		return errors.New("Dev builds don't support auto-updates")
	}
	return updater.update()
}

const (
	upcktimePath = "cktime"
	plat         = runtime.GOOS + "-" + runtime.GOARCH
)

var ErrHashMismatch = errors.New("new file hash mismatch after patch")

// Update protocol.
//
//   GET flynn-cli.herokuapp.com/flynn/current/linux-amd64.json
//
//   200 ok
//   {
//       "Version": "2",
//       "Sha256": "..." // base64
//   }
//
// then
//
//   GET flynn-cli-patch.s3.amazonaws.com/flynn/1/2/linux-amd64
//
//   200 ok
//   [bsdiff data]
//
// or
//
//   GET flynn-cli-dist.s3.amazonaws.com/flynn/2/linux-amd64.gz
//
//   200 ok
//   [gzipped executable data]
type Updater struct {
	apiURL  string
	cmdName string
	binURL  string
	diffURL string
	dir     string
	info    struct {
		Version string
		Sha256  []byte
	}
}

func (u *Updater) backgroundRun() {
	os.MkdirAll(u.dir, 0777)
	if u.wantUpdate() {
		if err := update.SanityCheck(); err != nil {
			// fail
			return
		}
		self, err := osext.Executable()
		if err != nil {
			// fail update, couldn't figure out path to self
			return
		}
		// TODO(bgentry): logger isn't on Windows. Replace w/ proper error reports.
		l := exec.Command("logger", "-tflynn")
		c := exec.Command(self, "update")
		if w, err := l.StdinPipe(); err == nil && l.Start() == nil {
			c.Stdout = w
			c.Stderr = w
		}
		c.Start()
	}
}

func (u *Updater) wantUpdate() bool {
	path := u.dir + upcktimePath
	if Version == "dev" || readTime(path).After(time.Now()) {
		return false
	}
	wait := 12*time.Hour + randDuration(8*time.Hour)
	return writeTime(path, time.Now().Add(wait))
}

func (u *Updater) update() error {
	path, err := osext.Executable()
	if err != nil {
		return err
	}
	old, err := os.Open(path)
	if err != nil {
		return err
	}
	defer old.Close()

	err = u.fetchInfo()
	if err != nil {
		return err
	}
	if u.info.Version == Version {
		return nil
	}
	bin, err := u.fetchAndVerifyPatch(old)
	if err != nil {
		switch err {
		case ErrNoPatchAvailable:
			log.Println("update: no patch available, falling back to full binary")
		case ErrHashMismatch:
			log.Println("update: hash mismatch from patched binary")
		default:
			log.Println("update: patching binary,", err)
		}
		bin, err = u.fetchAndVerifyFullBin()
		if err != nil {
			if err == ErrHashMismatch {
				log.Println("update: hash mismatch from full binary")
			} else {
				log.Println("update: fetching full binary,", err)
			}
			return err
		}
	}

	// close the old binary before installing because on windows
	// it can't be renamed if a handle to the file is still open
	old.Close()

	err, errRecover := update.FromStream(bytes.NewBuffer(bin))
	if errRecover != nil {
		return fmt.Errorf("update and recovery errors: %q %q", err, errRecover)
	}
	if err != nil {
		return err
	}
	log.Printf("Updated v%s -> v%s.", Version, u.info.Version)
	return nil
}

func (u *Updater) fetchInfo() error {
	r, err := fetch(u.apiURL + u.cmdName + "/current/" + plat + ".json")
	if err != nil {
		return err
	}
	defer r.Close()
	err = json.NewDecoder(r).Decode(&u.info)
	if err != nil {
		return err
	}
	if len(u.info.Sha256) != sha256.Size {
		return errors.New("bad cmd hash in info")
	}
	return nil
}

func (u *Updater) fetchAndVerifyPatch(old io.Reader) ([]byte, error) {
	bin, err := u.fetchAndApplyPatch(old)
	if err != nil {
		return nil, err
	}
	if !verifySha(bin, u.info.Sha256) {
		return nil, ErrHashMismatch
	}
	return bin, nil
}

func (u *Updater) fetchAndApplyPatch(old io.Reader) ([]byte, error) {
	r, err := fetch(u.diffURL + u.cmdName + "/" + Version + "/" + u.info.Version + "/" + plat)
	if err != nil {
		return nil, err
	}
	defer r.Close()
	var buf bytes.Buffer
	err = binarydist.Patch(old, &buf, r)
	return buf.Bytes(), err
}

func (u *Updater) fetchAndVerifyFullBin() ([]byte, error) {
	bin, err := u.fetchBin()
	if err != nil {
		return nil, err
	}
	verified := verifySha(bin, u.info.Sha256)
	if !verified {
		return nil, ErrHashMismatch
	}
	return bin, nil
}

func (u *Updater) fetchBin() ([]byte, error) {
	r, err := fetch(u.binURL + u.cmdName + "/" + u.info.Version + "/" + plat + ".gz")
	if err != nil {
		return nil, err
	}
	defer r.Close()
	buf := new(bytes.Buffer)
	gz, err := gzip.NewReader(r)
	if err != nil {
		return nil, err
	}
	if _, err = io.Copy(buf, gz); err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

// returns a random duration in [0,n).
func randDuration(n time.Duration) time.Duration {
	return time.Duration(rand.Int63n(int64(n)))
}

var ErrNoPatchAvailable = errors.New("no patch available")

func fetch(url string) (io.ReadCloser, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	switch resp.StatusCode {
	case 200:
		return resp.Body, nil
	case 401, 403, 404:
		return nil, ErrNoPatchAvailable
	default:
		return nil, fmt.Errorf("bad http status from %s: %v", url, resp.Status)
	}
	panic("not reached")
}

func readTime(path string) time.Time {
	p, err := ioutil.ReadFile(path)
	if os.IsNotExist(err) {
		return time.Time{}
	}
	if err != nil {
		return time.Now().Add(1000 * time.Hour)
	}
	t, err := time.Parse(time.RFC3339, string(p))
	if err != nil {
		return time.Now().Add(1000 * time.Hour)
	}
	return t
}

func verifySha(bin []byte, sha []byte) bool {
	h := sha256.New()
	h.Write(bin)
	return bytes.Equal(h.Sum(nil), sha)
}

func writeTime(path string, t time.Time) bool {
	return ioutil.WriteFile(path, []byte(t.Format(time.RFC3339)), 0644) == nil
}
<commit_msg>cli: Remove unreachable code<commit_after>package main

import (
	"bytes"
	"compress/gzip"
	"crypto/sha256"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"os"
	"os/exec"
	"runtime"
	"time"

	"github.com/flynn/flynn/Godeps/_workspace/src/bitbucket.org/kardianos/osext"
	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/inconshreveable/go-update"
	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/kr/binarydist"
	"github.com/flynn/flynn/controller/client"
)

var cmdUpdate = &Command{
	Run:      runUpdate,
	Usage:    "update",
	NoClient: true,
	Long: `
Update downloads and installs the next version of flynn-cli.

This command is unlisted, since users never have to run it directly.
`,
}

func runUpdate(cmd *Command, args []string, client *controller.Client) error {
	if updater == nil {
		return errors.New("Dev builds don't support auto-updates")
	}
	return updater.update()
}

const (
	upcktimePath = "cktime"
	plat         = runtime.GOOS + "-" + runtime.GOARCH
)

var ErrHashMismatch = errors.New("new file hash mismatch after patch")

// Update protocol.
//
//   GET flynn-cli.herokuapp.com/flynn/current/linux-amd64.json
//
//   200 ok
//   {
//       "Version": "2",
//       "Sha256": "..." // base64
//   }
//
// then
//
//   GET flynn-cli-patch.s3.amazonaws.com/flynn/1/2/linux-amd64
//
//   200 ok
//   [bsdiff data]
//
// or
//
//   GET flynn-cli-dist.s3.amazonaws.com/flynn/2/linux-amd64.gz
//
//   200 ok
//   [gzipped executable data]
type Updater struct {
	apiURL  string
	cmdName string
	binURL  string
	diffURL string
	dir     string
	info    struct {
		Version string
		Sha256  []byte
	}
}

func (u *Updater) backgroundRun() {
	os.MkdirAll(u.dir, 0777)
	if u.wantUpdate() {
		if err := update.SanityCheck(); err != nil {
			// fail
			return
		}
		self, err := osext.Executable()
		if err != nil {
			// fail update, couldn't figure out path to self
			return
		}
		// TODO(bgentry): logger isn't on Windows. Replace w/ proper error reports.
		l := exec.Command("logger", "-tflynn")
		c := exec.Command(self, "update")
		if w, err := l.StdinPipe(); err == nil && l.Start() == nil {
			c.Stdout = w
			c.Stderr = w
		}
		c.Start()
	}
}

func (u *Updater) wantUpdate() bool {
	path := u.dir + upcktimePath
	if Version == "dev" || readTime(path).After(time.Now()) {
		return false
	}
	wait := 12*time.Hour + randDuration(8*time.Hour)
	return writeTime(path, time.Now().Add(wait))
}

func (u *Updater) update() error {
	path, err := osext.Executable()
	if err != nil {
		return err
	}
	old, err := os.Open(path)
	if err != nil {
		return err
	}
	defer old.Close()

	err = u.fetchInfo()
	if err != nil {
		return err
	}
	if u.info.Version == Version {
		return nil
	}
	bin, err := u.fetchAndVerifyPatch(old)
	if err != nil {
		switch err {
		case ErrNoPatchAvailable:
			log.Println("update: no patch available, falling back to full binary")
		case ErrHashMismatch:
			log.Println("update: hash mismatch from patched binary")
		default:
			log.Println("update: patching binary,", err)
		}
		bin, err = u.fetchAndVerifyFullBin()
		if err != nil {
			if err == ErrHashMismatch {
				log.Println("update: hash mismatch from full binary")
			} else {
				log.Println("update: fetching full binary,", err)
			}
			return err
		}
	}

	// close the old binary before installing because on windows
	// it can't be renamed if a handle to the file is still open
	old.Close()

	err, errRecover := update.FromStream(bytes.NewBuffer(bin))
	if errRecover != nil {
		return fmt.Errorf("update and recovery errors: %q %q", err, errRecover)
	}
	if err != nil {
		return err
	}
	log.Printf("Updated v%s -> v%s.", Version, u.info.Version)
	return nil
}

func (u *Updater) fetchInfo() error {
	r, err := fetch(u.apiURL + u.cmdName + "/current/" + plat + ".json")
	if err != nil {
		return err
	}
	defer r.Close()
	err = json.NewDecoder(r).Decode(&u.info)
	if err != nil {
		return err
	}
	if len(u.info.Sha256) != sha256.Size {
		return errors.New("bad cmd hash in info")
	}
	return nil
}

func (u *Updater) fetchAndVerifyPatch(old io.Reader) ([]byte, error) {
	bin, err := u.fetchAndApplyPatch(old)
	if err != nil {
		return nil, err
	}
	if !verifySha(bin, u.info.Sha256) {
		return nil, ErrHashMismatch
	}
	return bin, nil
}

func (u *Updater) fetchAndApplyPatch(old io.Reader) ([]byte, error) {
	r, err := fetch(u.diffURL + u.cmdName + "/" + Version + "/" + u.info.Version + "/" + plat)
	if err != nil {
		return nil, err
	}
	defer r.Close()
	var buf bytes.Buffer
	err = binarydist.Patch(old, &buf, r)
	return buf.Bytes(), err
}

func (u *Updater) fetchAndVerifyFullBin() ([]byte, error) {
	bin, err := u.fetchBin()
	if err != nil {
		return nil, err
	}
	verified := verifySha(bin, u.info.Sha256)
	if !verified {
		return nil, ErrHashMismatch
	}
	return bin, nil
}

func (u *Updater) fetchBin() ([]byte, error) {
	r, err := fetch(u.binURL + u.cmdName + "/" + u.info.Version + "/" + plat + ".gz")
	if err != nil {
		return nil, err
	}
	defer r.Close()
	buf := new(bytes.Buffer)
	gz, err := gzip.NewReader(r)
	if err != nil {
		return nil, err
	}
	if _, err = io.Copy(buf, gz); err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

// returns a random duration in [0,n).
func randDuration(n time.Duration) time.Duration {
	return time.Duration(rand.Int63n(int64(n)))
}

var ErrNoPatchAvailable = errors.New("no patch available")

func fetch(url string) (io.ReadCloser, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	switch resp.StatusCode {
	case 200:
		return resp.Body, nil
	case 401, 403, 404:
		return nil, ErrNoPatchAvailable
	default:
		return nil, fmt.Errorf("bad http status from %s: %v", url, resp.Status)
	}
}

func readTime(path string) time.Time {
	p, err := ioutil.ReadFile(path)
	if os.IsNotExist(err) {
		return time.Time{}
	}
	if err != nil {
		return time.Now().Add(1000 * time.Hour)
	}
	t, err := time.Parse(time.RFC3339, string(p))
	if err != nil {
		return time.Now().Add(1000 * time.Hour)
	}
	return t
}

func verifySha(bin []byte, sha []byte) bool {
	h := sha256.New()
	h.Write(bin)
	return bytes.Equal(h.Sum(nil), sha)
}

func writeTime(path string, t time.Time) bool {
	return ioutil.WriteFile(path, []byte(t.Format(time.RFC3339)), 0644) == nil
}
<|endoftext|>


<commit_before>package picol

import (
	"fmt"
	"strconv"
	"strings"
)

const (
	PICOL_OK = iota
	PICOL_ERR
	PICOL_RETURN
	PICOL_BREAK
	PICOL_CONTINUE
)

type Var string
type CmdFunc func(i *Interp, argv []string, privdata interface{}) int
type Cmd struct {
	fn       CmdFunc
	privdata interface{}
}
type CallFrame struct {
	vars   map[string]Var
	parent *CallFrame
}
type Interp struct {
	level     int
	callframe *CallFrame
	commands  map[string]Cmd
	Result    string
}

func InitInterp() *Interp {
	return &Interp{
		level:     0,
		callframe: &CallFrame{vars: make(map[string]Var)},
		commands:  make(map[string]Cmd),
		Result:    ""}
}

func (i *Interp) Var(name string) (Var, bool) {
	v, ok := i.callframe.vars[name]
	return v, ok
}

func (i *Interp) SetVar(name, val string) {
	i.callframe.vars[name] = Var(val)
}

func (i *Interp) Command(name string) *Cmd {
	v, ok := i.commands[name]
	if !ok {
		return nil
	}
	return &v
}

func (i *Interp) RegisterCommand(name string, fn CmdFunc, privdata interface{}) int {
	c := i.Command(name)
	if c != nil {
		i.Result = fmt.Sprintf("Command '%s' already defined", name)
		return PICOL_ERR
	}

	i.commands[name] = Cmd{fn, privdata}
	return PICOL_OK
}

/* EVAL! */
func (i *Interp) Eval(t string) int {
	p := InitParser(t)
	i.Result = ""

	argv := []string{}

	for {
		prevtype := p.type_
		// XXX
		t = p.GetToken()
		if p.type_ == PT_EOF {
			break
		}

		switch p.type_ {
		case PT_VAR:
			v, ok := i.Var(t)
			if !ok {
				i.Result = fmt.Sprintf("No such variable '%s'", t)
				return PICOL_ERR
			}
			t = string(v)
		case PT_CMD:
			if code := i.Eval(t); code != PICOL_OK {
				return code
			}
			t = i.Result
		case PT_ESC:
			// XXX: escape handling missing!
		case PT_SEP:
			prevtype = p.type_
			continue
		}

		// We have a complete command + args. Call it!
		if p.type_ == PT_EOL {
			prevtype = p.type_
			if len(argv) != 0 {
				c := i.Command(argv[0])
				if c == nil {
					i.Result = fmt.Sprintf("No such command '%s'", argv[0])
					return PICOL_ERR
				}
				if code := c.fn(i, argv, c.privdata); code != PICOL_OK {
					return code
				}
			}
			// Prepare for the next command
			argv = []string{}
			continue
		}

		// We have a new token, append to the previous or as new arg?
		if prevtype == PT_SEP || prevtype == PT_EOL {
			argv = append(argv, t)
		} else { // Interpolation
			argv[len(argv)-1] = strings.Join([]string{argv[len(argv)-1], t}, "")
		}
		prevtype = p.type_
	}
	return PICOL_OK
}

/* ACTUAL COMMANDS! */
func ArityErr(i *Interp, name string, argv []string) int {
	i.Result = fmt.Sprintf("Wrong number of args for %s %s", name, argv)
	return PICOL_ERR
}

func CommandMath(i *Interp, argv []string, pd interface{}) int {
	if len(argv) != 3 {
		return ArityErr(i, argv[0], argv)
	}
	a, _ := strconv.Atoi(argv[1])
	b, _ := strconv.Atoi(argv[2])
	var c int
	switch {
	case argv[0] == "+":
		c = a + b
	case argv[0] == "-":
		c = a - b
	case argv[0] == "*":
		c = a * b
	case argv[0] == "/":
		c = a / b
	case argv[0] == ">":
		if a > b {
			c = 1
		}
	case argv[0] == ">=":
		if a >= b {
			c = 1
		}
	case argv[0] == "<":
		if a < b {
			c = 1
		}
	case argv[0] == "<=":
		if a <= b {
			c = 1
		}
	case argv[0] == "==":
		if a == b {
			c = 1
		}
	case argv[0] == "!=":
		if a != b {
			c = 1
		}
	default: // FIXME I hate warnings
		c = 0
	}
	i.Result = fmt.Sprintf("%d", c)
	return PICOL_OK
}

func CommandSet(i *Interp, argv []string, pd interface{}) int {
	if len(argv) != 3 {
		return ArityErr(i, argv[0], argv)
	}
	i.SetVar(argv[1], argv[2])
	i.Result = argv[2]
	return PICOL_OK
}

func CommandIf(i *Interp, argv []string, pd interface{}) int {
	if len(argv) != 3 && len(argv) != 5 {
		return ArityErr(i, argv[0], argv)
	}
	if retcode := i.Eval(argv[1]); retcode != PICOL_OK {
		return retcode
	}
	if r, _ := strconv.Atoi(i.Result); r != 0 {
		return i.Eval(argv[2])
	} else if len(argv) == 5 {
		return i.Eval(argv[4])
	}
	return PICOL_OK
}

func CommandWhile(i *Interp, argv []string, pd interface{}) int {
	if len(argv) != 3 {
		return ArityErr(i, argv[0], argv)
	}
	for {
		retcode := i.Eval(argv[1])
		if retcode != PICOL_OK {
			return retcode
		}
		if r, _ := strconv.Atoi(i.Result); r != 0 {
			retcode = i.Eval(argv[2])
			switch retcode {
			case PICOL_CONTINUE, PICOL_OK:
				//pass
			case PICOL_BREAK:
				return PICOL_OK
			default:
				return retcode
			}
		} else {
			return PICOL_OK
		}
	}
}

func CommandRetCodes(i *Interp, argv []string, pd interface{}) int {
	if len(argv) != 1 {
		return ArityErr(i, argv[0], argv)
	}
	switch argv[0] {
	case "break":
		return PICOL_BREAK
	case "continue":
		return PICOL_CONTINUE
	}
	return PICOL_OK
}

func DropCallFrame(i *Interp) {
	i.callframe = i.callframe.parent
}

func CommandCallProc(i *Interp, argv []string, pd interface{}) int {
	var x []string

	if pd, ok := pd.([]string); ok {
		x = pd
	} else {
		return PICOL_OK
	}

	alist := x[0]
	body := x[1]
	p := alist[:]
	arity := 0

	done := false
	i.callframe = &CallFrame{vars: make(map[string]Var), parent: i.callframe}
	defer DropCallFrame(i) // remove the called proc callframe

	err := 0

	for {
		start := p
		for len(p) != 0 && p[0] != ' ' {
			p = p[1:]
		}
		if len(p) != 0 && p == start {
			p = p[1:]
			continue
		}

		if p == start {
			break
		}
		if len(p) == 0 {
			done = true
		} else {
			p = p[1:1]
		}
		arity++
		if arity > len(argv)-1 {
			i.Result = fmt.Sprintf("Proc '%s' called with wrong arg num", argv[0])
			return PICOL_ERR
		}
		i.SetVar(start, argv[arity])
		if len(p) != 0 {
			p = p[1:]
		}
		if done {
			break
		}
	}

	if arity != len(argv)-1 {
		i.Result = fmt.Sprintf("Proc '%s' called with wrong arg num", argv[0])
		return PICOL_ERR
	}
	err = i.Eval(body)
	if err == PICOL_RETURN {
		err = PICOL_OK
	}
	return err
}

func CommandProc(i *Interp, argv []string, pd interface{}) int {
	if len(argv) != 4 {
		return ArityErr(i, argv[0], argv)
	}
	return i.RegisterCommand(argv[1], CommandCallProc, []string{argv[2], argv[3]})
}

func CommandReturn(i *Interp, argv []string, pd interface{}) int {
	if len(argv) != 1 && len(argv) != 2 {
		return ArityErr(i, argv[0], argv)
	}
	var r string
	if len(argv) == 2 {
		r = argv[1]
	}
	i.Result = r
	return PICOL_RETURN
}

func (i *Interp) RegisterCoreCommands() {
	name := [...]string{"+", "-", "*", "/", ">", ">=", "<", "<=", "==", "!="}
	for _, n := range name {
		i.RegisterCommand(n, CommandMath, nil)
	}
	i.RegisterCommand("set", CommandSet, nil)
	i.RegisterCommand("if", CommandIf, nil)
	i.RegisterCommand("while", CommandWhile, nil)
	i.RegisterCommand("break", CommandRetCodes, nil)
	i.RegisterCommand("continue", CommandRetCodes, nil)
	i.RegisterCommand("proc", CommandProc, nil)
	i.RegisterCommand("return", CommandReturn, nil)
}
<commit_msg>rm arityerr func from global<commit_after>package picol

import (
	"fmt"
	"strconv"
	"strings"
)

const (
	PICOL_OK = iota
	PICOL_ERR
	PICOL_RETURN
	PICOL_BREAK
	PICOL_CONTINUE
)

type Var string
type CmdFunc func(i *Interp, argv []string, privdata interface{}) int
type Cmd struct {
	fn       CmdFunc
	privdata interface{}
}
type CallFrame struct {
	vars   map[string]Var
	parent *CallFrame
}
type Interp struct {
	level     int
	callframe *CallFrame
	commands  map[string]Cmd
	Result    string
}

func InitInterp() *Interp {
	return &Interp{
		level:     0,
		callframe: &CallFrame{vars: make(map[string]Var)},
		commands:  make(map[string]Cmd),
		Result:    ""}
}

func (i *Interp) Var(name string) (Var, bool) {
	v, ok := i.callframe.vars[name]
	return v, ok
}

func (i *Interp) SetVar(name, val string) {
	i.callframe.vars[name] = Var(val)
}

func (i *Interp) Command(name string) *Cmd {
	v, ok := i.commands[name]
	if !ok {
		return nil
	}
	return &v
}

func (i *Interp) RegisterCommand(name string, fn CmdFunc, privdata interface{}) int {
	c := i.Command(name)
	if c != nil {
		i.Result = fmt.Sprintf("Command '%s' already defined", name)
		return PICOL_ERR
	}

	i.commands[name] = Cmd{fn, privdata}
	return PICOL_OK
}

/* EVAL! */
func (i *Interp) Eval(t string) int {
	p := InitParser(t)
	i.Result = ""

	argv := []string{}

	for {
		prevtype := p.type_
		// XXX
		t = p.GetToken()
		if p.type_ == PT_EOF {
			break
		}

		switch p.type_ {
		case PT_VAR:
			v, ok := i.Var(t)
			if !ok {
				i.Result = fmt.Sprintf("No such variable '%s'", t)
				return PICOL_ERR
			}
			t = string(v)
		case PT_CMD:
			if code := i.Eval(t); code != PICOL_OK {
				return code
			}
			t = i.Result
		case PT_ESC:
			// XXX: escape handling missing!
		case PT_SEP:
			prevtype = p.type_
			continue
		}

		// We have a complete command + args. Call it!
		if p.type_ == PT_EOL {
			prevtype = p.type_
			if len(argv) != 0 {
				c := i.Command(argv[0])
				if c == nil {
					i.Result = fmt.Sprintf("No such command '%s'", argv[0])
					return PICOL_ERR
				}
				if code := c.fn(i, argv, c.privdata); code != PICOL_OK {
					return code
				}
			}
			// Prepare for the next command
			argv = []string{}
			continue
		}

		// We have a new token, append to the previous or as new arg?
		if prevtype == PT_SEP || prevtype == PT_EOL {
			argv = append(argv, t)
		} else { // Interpolation
			argv[len(argv)-1] = strings.Join([]string{argv[len(argv)-1], t}, "")
		}
		prevtype = p.type_
	}
	return PICOL_OK
}

/* ACTUAL COMMANDS! */
func arityErr(i *Interp, name string, argv []string) int {
	i.Result = fmt.Sprintf("Wrong number of args for %s %s", name, argv)
	return PICOL_ERR
}

func CommandMath(i *Interp, argv []string, pd interface{}) int {
	if len(argv) != 3 {
		return arityErr(i, argv[0], argv)
	}
	a, _ := strconv.Atoi(argv[1])
	b, _ := strconv.Atoi(argv[2])
	var c int
	switch {
	case argv[0] == "+":
		c = a + b
	case argv[0] == "-":
		c = a - b
	case argv[0] == "*":
		c = a * b
	case argv[0] == "/":
		c = a / b
	case argv[0] == ">":
		if a > b {
			c = 1
		}
	case argv[0] == ">=":
		if a >= b {
			c = 1
		}
	case argv[0] == "<":
		if a < b {
			c = 1
		}
	case argv[0] == "<=":
		if a <= b {
			c = 1
		}
	case argv[0] == "==":
		if a == b {
			c = 1
		}
	case argv[0] == "!=":
		if a != b {
			c = 1
		}
	default: // FIXME I hate warnings
		c = 0
	}
	i.Result = fmt.Sprintf("%d", c)
	return PICOL_OK
}

func CommandSet(i *Interp, argv []string, pd interface{}) int {
	if len(argv) != 3 {
		return arityErr(i, argv[0], argv)
	}
	i.SetVar(argv[1], argv[2])
	i.Result = argv[2]
	return PICOL_OK
}

func CommandIf(i *Interp, argv []string, pd interface{}) int {
	if len(argv) != 3 && len(argv) != 5 {
		return arityErr(i, argv[0], argv)
	}
	if retcode := i.Eval(argv[1]); retcode != PICOL_OK {
		return retcode
	}
	if r, _ := strconv.Atoi(i.Result); r != 0 {
		return i.Eval(argv[2])
	} else if len(argv) == 5 {
		return i.Eval(argv[4])
	}
	return PICOL_OK
}

func CommandWhile(i *Interp, argv []string, pd interface{}) int {
	if len(argv) != 3 {
		return arityErr(i, argv[0], argv)
	}
	for {
		retcode := i.Eval(argv[1])
		if retcode != PICOL_OK {
			return retcode
		}
		if r, _ := strconv.Atoi(i.Result); r != 0 {
			retcode = i.Eval(argv[2])
			switch retcode {
			case PICOL_CONTINUE, PICOL_OK:
				//pass
			case PICOL_BREAK:
				return PICOL_OK
			default:
				return retcode
			}
		} else {
			return PICOL_OK
		}
	}
}

func CommandRetCodes(i *Interp, argv []string, pd interface{}) int {
	if len(argv) != 1 {
		return arityErr(i, argv[0], argv)
	}
	switch argv[0] {
	case "break":
		return PICOL_BREAK
	case "continue":
		return PICOL_CONTINUE
	}
	return PICOL_OK
}

func DropCallFrame(i *Interp) {
	i.callframe = i.callframe.parent
}

func CommandCallProc(i *Interp, argv []string, pd interface{}) int {
	var x []string

	if pd, ok := pd.([]string); ok {
		x = pd
	} else {
		return PICOL_OK
	}

	alist := x[0]
	body := x[1]
	p := alist[:]
	arity := 0

	done := false
	i.callframe = &CallFrame{vars: make(map[string]Var), parent: i.callframe}
	defer DropCallFrame(i) // remove the called proc callframe

	err := 0

	for {
		start := p
		for len(p) != 0 && p[0] != ' ' {
			p = p[1:]
		}
		if len(p) != 0 && p == start {
			p = p[1:]
			continue
		}

		if p == start {
			break
		}
		if len(p) == 0 {
			done = true
		} else {
			p = p[1:1]
		}
		arity++
		if arity > len(argv)-1 {
			i.Result = fmt.Sprintf("Proc '%s' called with wrong arg num", argv[0])
			return PICOL_ERR
		}
		i.SetVar(start, argv[arity])
		if len(p) != 0 {
			p = p[1:]
		}
		if done {
			break
		}
	}

	if arity != len(argv)-1 {
		i.Result = fmt.Sprintf("Proc '%s' called with wrong arg num", argv[0])
		return PICOL_ERR
	}
	err = i.Eval(body)
	if err == PICOL_RETURN {
		err = PICOL_OK
	}
	return err
}

func CommandProc(i *Interp, argv []string, pd interface{}) int {
	if len(argv) != 4 {
		return arityErr(i, argv[0], argv)
	}
	return i.RegisterCommand(argv[1], CommandCallProc, []string{argv[2], argv[3]})
}

func CommandReturn(i *Interp, argv []string, pd interface{}) int {
	if len(argv) != 1 && len(argv) != 2 {
		return arityErr(i, argv[0], argv)
	}
	var r string
	if len(argv) == 2 {
		r = argv[1]
	}
	i.Result = r
	return PICOL_RETURN
}

func (i *Interp) RegisterCoreCommands() {
	name := [...]string{"+", "-", "*", "/", ">", ">=", "<", "<=", "==", "!="}
	for _, n := range name {
		i.RegisterCommand(n, CommandMath, nil)
	}
	i.RegisterCommand("set", CommandSet, nil)
	i.RegisterCommand("if", CommandIf, nil)
	i.RegisterCommand("while", CommandWhile, nil)
	i.RegisterCommand("break", CommandRetCodes, nil)
	i.RegisterCommand("continue", CommandRetCodes, nil)
	i.RegisterCommand("proc", CommandProc, nil)
	i.RegisterCommand("return", CommandReturn, nil)
}
<|endoftext|>


<commit_before>package cbfsclient

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"mime"
	"net/http"
	"os"
	"strconv"
	"strings"
)

// Options for storing data.
type PutOptions struct {
	// If true, do a fast, unsafe store
	Unsafe bool
	// Expiration time
	Expiration int
	// Hash to verify ("" for no verification)
	Hash string
	// Content type (detected if not specified)
	ContentType string
	// Optional reader transform (e.g. for encryption)
	ContentTransform func(r io.Reader) io.Reader

	keeprevs   int
	keeprevset bool
}

// Specify the number of revs to keep of this object.
func (p *PutOptions) SetKeepRevs(to int) {
	p.keeprevs = to
	p.keeprevset = true
}

func recognizeTypeByName(n, def string) string {
	byname := mime.TypeByExtension(n)
	switch {
	case byname != "":
		return byname
	case strings.HasSuffix(n, ".js"):
		return "application/javascript"
	case strings.HasSuffix(n, ".json"):
		return "application/json"
	case strings.HasSuffix(n, ".css"):
		return "text/css"
	}
	return def
}

// Put some content in CBFS.
//
// The sourcename is optional and is used for content type detection
// (if not specified in options).
//
// The dest filename is the name that the file will have in cbfs.
//
// The io.Reader is the source of content.
//
// Options are optional.
func (c Client) Put(srcname, dest string, r io.Reader, opts PutOptions) error {
	someBytes := make([]byte, 512)
	n, err := r.Read(someBytes)
	if err != nil && err != io.EOF {
		return err
	}
	someBytes = someBytes[:n]

	length := int64(-1)
	if s, ok := r.(io.Seeker); r != os.Stdin && ok {
		length, err = s.Seek(0, 2)
		if err != nil {
			return err
		}

		_, err = s.Seek(0, 0)
		if err != nil {
			return err
		}
	} else {
		r = io.MultiReader(bytes.NewReader(someBytes), r)
	}

	if opts.ContentTransform != nil {
		oldr := r
		r = opts.ContentTransform(r)
		// On a content transformation, we don't know the
		// length.
		if oldr != r {
			length = -1
		}
	}

	du := c.URLFor(dest)
	preq, err := http.NewRequest("PUT", du, r)
	if err != nil {
		return err
	}
	if opts.keeprevset {
		preq.Header.Set("X-CBFS-KeepRevs",
			strconv.Itoa(opts.keeprevs))
	}
	if opts.Unsafe {
		preq.Header.Set("X-CBFS-Unsafe", "true")
	}
	if opts.Expiration > 0 {
		preq.Header.Set("X-CBFS-Expiration",
			strconv.Itoa(opts.Expiration))
	}

	ctype := opts.ContentType
	if ctype == "" {
		ctype := http.DetectContentType(someBytes)
		if strings.HasPrefix(ctype, "text/plain") ||
			strings.HasPrefix(ctype, "application/octet-stream") {
			ctype = recognizeTypeByName(srcname, ctype)
		}
	}

	if length >= 0 {
		preq.Header.Set("Content-Length", strconv.FormatInt(length, 10))
	}
	preq.Header.Set("Content-Type", ctype)
	if opts.Hash != "" {
		preq.Header.Set("X-CBFS-Hash", opts.Hash)
	}

	resp, err := http.DefaultClient.Do(preq)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != 201 {
		r, _ := ioutil.ReadAll(io.LimitReader(resp.Body, 512))
		return fmt.Errorf("HTTP Error:  %v: %s", resp.Status, r)
	}

	return nil
}
<commit_msg>Upload to a RandomNode<commit_after>package cbfsclient

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"mime"
	"net/http"
	"os"
	"strconv"
	"strings"
)

// Options for storing data.
type PutOptions struct {
	// If true, do a fast, unsafe store
	Unsafe bool
	// Expiration time
	Expiration int
	// Hash to verify ("" for no verification)
	Hash string
	// Content type (detected if not specified)
	ContentType string
	// Optional reader transform (e.g. for encryption)
	ContentTransform func(r io.Reader) io.Reader

	keeprevs   int
	keeprevset bool
}

// Specify the number of revs to keep of this object.
func (p *PutOptions) SetKeepRevs(to int) {
	p.keeprevs = to
	p.keeprevset = true
}

func recognizeTypeByName(n, def string) string {
	byname := mime.TypeByExtension(n)
	switch {
	case byname != "":
		return byname
	case strings.HasSuffix(n, ".js"):
		return "application/javascript"
	case strings.HasSuffix(n, ".json"):
		return "application/json"
	case strings.HasSuffix(n, ".css"):
		return "text/css"
	}
	return def
}

// Put some content in CBFS.
//
// The sourcename is optional and is used for content type detection
// (if not specified in options).
//
// The dest filename is the name that the file will have in cbfs.
//
// The io.Reader is the source of content.
//
// Options are optional.
func (c Client) Put(srcname, dest string, r io.Reader, opts PutOptions) error {
	someBytes := make([]byte, 512)
	n, err := r.Read(someBytes)
	if err != nil && err != io.EOF {
		return err
	}
	someBytes = someBytes[:n]

	length := int64(-1)
	if s, ok := r.(io.Seeker); r != os.Stdin && ok {
		length, err = s.Seek(0, 2)
		if err != nil {
			return err
		}

		_, err = s.Seek(0, 0)
		if err != nil {
			return err
		}
	} else {
		r = io.MultiReader(bytes.NewReader(someBytes), r)
	}

	if opts.ContentTransform != nil {
		oldr := r
		r = opts.ContentTransform(r)
		// On a content transformation, we don't know the
		// length.
		if oldr != r {
			length = -1
		}
	}

	_, rn, err := c.RandomNode()
	if err != nil {
		return err
	}

	du := rn.URLFor(dest)
	preq, err := http.NewRequest("PUT", du, r)
	if err != nil {
		return err
	}
	if opts.keeprevset {
		preq.Header.Set("X-CBFS-KeepRevs",
			strconv.Itoa(opts.keeprevs))
	}
	if opts.Unsafe {
		preq.Header.Set("X-CBFS-Unsafe", "true")
	}
	if opts.Expiration > 0 {
		preq.Header.Set("X-CBFS-Expiration",
			strconv.Itoa(opts.Expiration))
	}

	ctype := opts.ContentType
	if ctype == "" {
		ctype := http.DetectContentType(someBytes)
		if strings.HasPrefix(ctype, "text/plain") ||
			strings.HasPrefix(ctype, "application/octet-stream") {
			ctype = recognizeTypeByName(srcname, ctype)
		}
	}

	if length >= 0 {
		preq.Header.Set("Content-Length", strconv.FormatInt(length, 10))
	}
	preq.Header.Set("Content-Type", ctype)
	if opts.Hash != "" {
		preq.Header.Set("X-CBFS-Hash", opts.Hash)
	}

	resp, err := http.DefaultClient.Do(preq)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != 201 {
		r, _ := ioutil.ReadAll(io.LimitReader(resp.Body, 512))
		return fmt.Errorf("HTTP Error:  %v: %s", resp.Status, r)
	}

	return nil
}
<|endoftext|>