<commit_before>package lexer

import (
	"errors"
	"fmt"
	"github.com/moechat/parser/token"
	"regexp"
	"strconv"
)

// A Matcher pairs a set of regexps and a set of tokens.
type Matcher interface {
	Exprs() []string

	Flags() int
	Type() int

	// A function that modifies arguments (i.e. a function that converts a username to a user ID in @tagging)
	ArgModFunc(args []string, namesById map[string]int) ([]string, map[string]int)
	IsValid(args *token.TokenArgs) bool
	BuildTokens(args *token.TokenArgs) []token.Token
}

type matcherWrap struct {
	Matcher

	id      int              // A unique ID for this matcher
	regexps []*regexp.Regexp // The compiled regexps
}

/*
This is an implementation of the a Lexer, used to convert text into tokens
(http://en.wikipedia.org/wiki/Lexical_analysis) using the regexp package.

Because the regexp package is implemented using a NFA
(http://en.wikipedia.org/wiki/Nondeterministic_finite_automaton),
it's very effective for this use case.
*/
type Lexer struct {
	matchersById map[int]*matcherWrap // All matchers by their ID
	matchers     map[*matcherWrap]int // All matchers and their corresponding capture group ID

	exprs map[string]bool // The set of regexps that are matched

	expr   string         // The main regexp expression
	regexp *regexp.Regexp // The regexp used to match tags

	nextId int // The ID of the next matcher to be added
}

// Creates a new Lexer.
func New() *Lexer {
	return &Lexer{matchers: make(map[*matcherWrap]int), exprs: make(map[string]bool)}
}

func MustCompile(matchers ...Matcher) *Lexer {
	l, err := Compile(matchers...)
	if err != nil {
		panic(err)
	}

	return l
}

func Compile(matchers ...Matcher) (*Lexer, error) {
	ret := New()
	err := ret.AddMatchers(matchers...)
	if err != nil {
		return nil, err
	}

	err = ret.Compile()
	if err != nil {
		return nil, err
	}
	return ret, nil
}

// Adds the passed matcher/matchers to the lexer
func (l *Lexer) AddMatchers(matchers ...Matcher) error {
	for _, matcher := range matchers {
		wrap := &matcherWrap{Matcher: matcher, id: l.nextId}
		l.nextId++
		for _, expr := range matcher.Exprs() {
			if l.exprs[expr] {
				err := errors.New("Another matcher has an identical regexp!")
				return err
			}
		}

		for _, expr := range matcher.Exprs() {
			l.exprs[expr] = true
		}

		l.matchersById[wrap.id] = wrap
		l.matchers[wrap] = -1
	}

	return nil
}

// Removes all instances of the token class specified from the token class map.
func (l *Lexer) RemoveMatchers(matchers ...Matcher) {
	for _, matcher := range matchers {
		for wrap := range l.matchers {
			if Matcher(wrap) == matcher {
				for _, expr := range wrap.Exprs() {
					delete(l.exprs, expr)
				}

				delete(l.matchersById, wrap.id)
				delete(l.matchers, wrap)
			}
		}
	}
}

func (l *Lexer) MustCompile() {
	err := l.Compile()
	if err != nil {
		panic(err)
	}
}

// Creates the and compiles the regexp used by the Lexer.
//
// It must be run after adding or removing token classes in order for changes to take effect.
func (l *Lexer) Compile() error {
	var err error

	l.expr = ""
	for matcher := range l.matchers {
		matcher.regexps = make([]*regexp.Regexp, len(matcher.Exprs()))
		for i, expr := range matcher.Exprs() {
			matcher.regexps[i], err = regexp.Compile(expr)
			if err != nil {
				return err
			}
			l.expr += fmt.Sprintf("(?P<_%02x%x>%s)|", i, matcher.id, expr)
		}
	}
	l.expr = l.expr[:len(l.expr)-1]

	l.regexp, err = regexp.Compile(l.expr)
	if err != nil {
		return err
	}

	names := l.regexp.SubexpNames()
	usedNames := make(map[string]bool)

	for i, name := range names {
		if name != "" {
			if name[0] == '_' {
				matcherId64, _ := strconv.ParseInt(name[3:], 16, 0)
				matcherId := int(matcherId64)
				matcher, ok := l.matchersById[matcherId]
				if !ok {
					return errors.New("lexer: capture group names starting with _ are reserved for use by the lexer! Your name is " + name)
				}

				usedNames[name] = true
				l.matchers[matcher] = i
			}
		}
	}

	return nil
}

// Converts an input string into Tokens.
//
// If the tree has not been built, tokenize will run BuildCharTree().
// BuildCharTree() *must* be run if you call AddTokenClass or RemoveTokenClass between Tokenize()'s
func (l *Lexer) Tokenize(data string) []token.Token {
	ret := make([]token.Token, 0)
	subexpNames := l.regexp.SubexpNames()

	for data != "" {
		indices := l.regexp.FindStringSubmatchIndex(data)
		if indices == nil {
			ret = append(ret, token.NewTextToken(data))
			data = ""
			break
		}

		for matcher, i := range l.matchers {
			if indices[i*2] >= 0 {
				if indices[i*2] != 0 {
					ret = append(ret, token.NewTextToken(data[:indices[i*2]]))
				}

				exprId, _ := strconv.ParseInt(subexpNames[i][1:3], 16, 8)
				currRe := matcher.regexps[exprId]
				args := []string(currRe.FindStringSubmatch(data[indices[i*2]:indices[i*2+1]]))

				idByName := make(map[string]int)
				for i, name := range currRe.SubexpNames() {
					if name != "" {
						idByName[name] = i
					}
				}

				args, idByName = matcher.ArgModFunc(args, idByName)
				tokenArgs := token.NewTokenArgs(args, idByName)

				if matcher.IsValid(tokenArgs) {
					ret = append(ret, matcher.BuildTokens(tokenArgs)...)
				} else {
					ret = append(ret, token.NewTextToken(data[indices[i*2]:indices[i*2+1]]))
				}
				data = data[indices[i*2+1]:]
			}
		}
	}

	return ret
}
<commit_msg>finish up the lexer<commit_after>package lexer

import (
	"errors"
	"fmt"
	"github.com/moechat/parser/token"
	"regexp"
)

// A Matcher pairs a set of regexps and a set of tokens.
type Matcher interface {
	Name() string // A unique name for this matcher
	Expr() string // The regular expressions that match this token

	IsValid(args *token.TokenArgs) bool
	BuildToken(args *token.TokenArgs) token.Token
}

/*
 * This is an implementation of the a Lexer, used to convert text into tokens
 * (http://en.wikipedia.org/wiki/Lexical_analysis) using the regexp package.

 * Because the regexp package is implemented using a NFA
 * (http://en.wikipedia.org/wiki/Nondeterministic_finite_automaton),
 * it's very effective for this use case.
 */
type Lexer struct {
	matchers  map[string]Matcher
	regexps   map[string]*regexp.Regexp
	subexpIds map[string]int

	expr   string         // The main regexp expression
	regexp *regexp.Regexp // The regexp used to match tags
}

func Must(l *Lexer, err error) *Lexer {
	if err != nil {
		panic(err)
	}
	return l
}

func New(matchers ...Matcher) (*Lexer, error) {
	var err error
	l := &Lexer{
		matchers:  make(map[string]Matcher),
		regexps:   make(map[string]*regexp.Regexp),
		subexpIds: make(map[string]int),
	}

	for _, matcher := range matchers {
		l.matchers[matcher.Name()] = matcher
		l.expr += fmt.Sprintf("(?P<_%s>%s)|", matcher.Name(), matcher.Expr())
		l.regexps[matcher.Name()], err = regexp.Compile(matcher.Expr())
		if err != nil {
			return nil, err
		}
	}

	l.expr = l.expr[:len(l.expr)-1] // Cut off the trailing '|'
	l.regexp, err = regexp.Compile(l.expr)
	if err != nil {
		return nil, err
	}

	for i, name := range l.regexp.SubexpNames() {
		if name != "" && name[0] == '_' {
			name = name[1:] // Remove the '_' prefix
			_, ok := l.matchers[name]
			if !ok {
				return nil, errors.New("lexer: capture group names starting with _ are reserved for use by the lexer! Your name is " + name)
			}

			l.subexpIds[name] = i
		}
	}

	return l, nil
}

/*
 * Converts an input string into Tokens.
 */
func (l *Lexer) Tokenize(data string) []token.Token {
	ret := make([]token.Token, 0)

	for data != "" {
		indices := l.regexp.FindStringSubmatchIndex(data)
		if indices == nil {
			ret = append(ret, token.TextToken{data})
			break
		}

		for name, matcher := range l.matchers {
			i := l.subexpIds[name]
			if indices[i*2] >= 0 {
				if indices[i*2] != 0 {
					ret = append(ret, token.TextToken{data[:indices[i*2]]})
				}

				args := []string(l.regexps[name].FindStringSubmatch(data[indices[0]:indices[1]]))

				tokenArgs := token.NewTokenArgs(args, l.regexps[name].SubexpNames())

				if matcher.IsValid(tokenArgs) {
					ret = append(ret, matcher.BuildToken(tokenArgs))
				} else {
					ret = append(ret, token.TextToken{data[indices[i*2]:indices[i*2+1]]})
				}
				data = data[indices[i*2+1]:]
			}
		}
	}

	return ret
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the MPL
*  License. If a copy of the same was not distributed with this
*  file, You can obtain one at
*  https://github.com/AkhilHector/pubundsci/blob/master/LICENSE.
 */

package numgo

type NdArray struct {
	Size int
}

func (n NdArray) Init() []int {
	array := make([]int, n.Size)
	return array
}

func extract_parameters(args []int) []int {
	var parameters []int

	// store the parameters in a list
	for _, p := range args {
		parameters = append(parameters, p)
	}
	return parameters
}

func (n NdArray) Range(start, end int) []int {
	var array []int
	for i := start; i < end; i++ {
		array = append(array, i)
	}
	return array
}

func (n NdArray) Xrange(args ...int) []int {
	var array []int
	number_of_args := len(args)
	parameters := extract_parameters(args)

	// now generate the array basiing on the number of arguments
	if number_of_args == 1 {
		for i := 0; i < parameters[0]; i++ {
			array = append(array, i)
		}
	} else if number_of_args == 2 {
		for i := parameters[0]; i < parameters[1]; i++ {
			array = append(array, i)
		}
	} else {
		for i := parameters[0]; i < parameters[1]; i++ {
			array = append(array, i)
		}
	}
	return array
}
<commit_msg>Init method for creating ndimensional arrays<commit_after>/* This Source Code Form is subject to the terms of the MPL
*  License. If a copy of the same was not distributed with this
*  file, You can obtain one at
*  https://github.com/AkhilHector/pubundsci/blob/master/LICENSE.
 */

package numgo

type NdArray struct {
	Size int
}

func (n NdArray) Init() []int {
	array := make([]int, n.Size)
	return array
}

func extract_parameters(args []int) []int {
	var parameters []int

	// store the parameters in a list
	for _, p := range args {
		parameters = append(parameters, p)
	}
	return parameters
}

func (n NdArray) Array(data []string, details ...int) (narray *NdArray) {
	//parameters := extract_parameters(details)

	// depending on the given parameters create the appropriate array
	return narray
}

func (n NdArray) Range(start, end int) []int {
	var array []int
	for i := start; i < end; i++ {
		array = append(array, i)
	}
	return array
}

func (n NdArray) Xrange(args ...int) []int {
	var array []int
	number_of_args := len(args)
	parameters := extract_parameters(args)

	// now generate the array basiing on the number of arguments
	if number_of_args == 1 {
		for i := 0; i < parameters[0]; i++ {
			array = append(array, i)
		}
	} else if number_of_args == 2 {
		for i := parameters[0]; i < parameters[1]; i++ {
			array = append(array, i)
		}
	} else {
		for i := parameters[0]; i < parameters[1]; i++ {
			array = append(array, i)
		}
	}
	return array
}
<|endoftext|>


<commit_before>package chatroom

import (
	"container/list"
	"time"
)

type Event struct {
	Type      string // "join", "leave", or "message"
	User      string
	Timestamp int    // Unix timestmap (secs)
	Text      string // What the user said (if Type == "say")
}

type Subscription struct {
	Archive []Event      // All the events from the archive.
	New     <-chan Event // New events coming in.
}

// Owner of a subscription must cancel it when they stop listening to events.
func (s Subscription) Cancel() {
	unsubscribe <- s.New // Unsubscribe the channel.
	drain(s.New)         // Drain it, just in case there was a pending publish.
}

func newEvent(typ, user, msg string) Event {
	return Event{typ, user, int(time.Now().Unix()), msg}
}

func Subscribe() Subscription {
	resp := make(chan Subscription)
	subscribe <- resp
	return <-resp
}

func Join(user string) {
	publish <- newEvent("join", user, "")
}

func Say(user, message string) {
	publish <- newEvent("message", user, message)
}

func Leave(user string) {
	publish <- newEvent("leave", user, "")
}

const archiveSize = 10

var (
	// Send a channel here to get room events back.  It will send the entire
	// archive initially, and then new messages as they come in.
	subscribe = make(chan (chan<- Subscription), 10)
	// Send a channel here to unsubscribe.
	unsubscribe = make(chan (<-chan Event), 10)
	// Send events here to publish them.
	publish = make(chan Event, 10)
)

// This function loops forever, handling the chat room pubsub
func chatroom() {
	archive := list.New()
	subscribers := list.New()

	for {
		select {
		case ch := <-subscribe:
			var events []Event
			for e := archive.Front(); e != nil; e = e.Next() {
				events = append(events, e.Value.(Event))
			}
			subscriber := make(chan Event, 10)
			subscribers.PushBack(subscriber)
			ch <- Subscription{events, subscriber}

		case event := <-publish:
			for ch := subscribers.Front(); ch != nil; ch = ch.Next() {
				ch.Value.(chan Event) <- event
			}
			if archive.Len() >= archiveSize {
				archive.Remove(archive.Front())
			}
			archive.PushBack(event)

		case unsub := <-unsubscribe:
			for ch := subscribers.Front(); ch != nil; ch = ch.Next() {
				if ch.Value.(chan Event) == unsub {
					subscribers.Remove(ch)
				}
			}
		}
	}
}

func init() {
	go chatroom()
}

// Helpers

// Drains a given channel of any messages.
func drain(ch <-chan Event) {
	for {
		select {
		case _, ok := <-ch:
			if !ok {
				return
			}
		default:
			return
		}
	}
}
<commit_msg>Chat app fix for Ubuntu<commit_after>package chatroom

import (
	"container/list"
	"time"
)

type Event struct {
	Type      string // "join", "leave", or "message"
	User      string
	Timestamp int    // Unix timestmap (secs)
	Text      string // What the user said (if Type == "say")
}

type Subscription struct {
	Archive []Event      // All the events from the archive.
	New     <-chan Event // New events coming in.
}

// Owner of a subscription must cancel it when they stop listening to events.
func (s Subscription) Cancel() {
	unsubscribe <- s.New // Unsubscribe the channel.
	drain(s.New)         // Drain it, just in case there was a pending publish.
}

func newEvent(typ, user, msg string) Event {
	return Event{typ, user, int(time.Now().Unix()), msg}
}

func Subscribe() Subscription {
	resp := make(chan Subscription)
	subscribe <- resp
	return <-resp
}

func Join(user string) {
	publish <- newEvent("join", user, "")
}

func Say(user, message string) {
	publish <- newEvent("message", user, message)
}

func Leave(user string) {
	publish <- newEvent("leave", user, "")
}

const archiveSize = 10

var (
	// Send a channel here to get room events back.  It will send the entire
	// archive initially, and then new messages as they come in.
	subscribe = make(chan (chan<- Subscription), 10)
	// Send a channel here to unsubscribe.
	unsubscribe = make(chan (<-chan Event), 10)
	// Send events here to publish them.
	publish = make(chan Event, 10)
)

// This function loops forever, handling the chat room pubsub
func chatroom() {
	archive := list.New()
	subscribers := list.New()

	for {
		select {
		case ch := <-subscribe:
			var events []Event
			for e := archive.Front(); e != nil; e = e.Next() {
				events = append(events, e.Value.(Event))
			}
			subscriber := make(chan Event, 10)
			subscribers.PushBack(subscriber)
			ch <- Subscription{events, subscriber}

		case event := <-publish:
			for ch := subscribers.Front(); ch != nil; ch = ch.Next() {
				ch.Value.(chan Event) <- event
			}
			if archive.Len() >= archiveSize {
				archive.Remove(archive.Front())
			}
			archive.PushBack(event)

		case unsub := <-unsubscribe:
			for ch := subscribers.Front(); ch != nil; ch = ch.Next() {
				if ch.Value.(chan Event) == unsub {
					subscribers.Remove(ch)
					break
				}
			}
		}
	}
}

func init() {
	go chatroom()
}

// Helpers

// Drains a given channel of any messages.
func drain(ch <-chan Event) {
	for {
		select {
		case _, ok := <-ch:
			if !ok {
				return
			}
		default:
			return
		}
	}
}
<|endoftext|>


<commit_before>package paperless

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

	"github.com/jmoiron/sqlx"
	_ "github.com/mattn/go-sqlite3"

	"github.com/kopoli/go-util"
)

type db struct {
	file string
	*sqlx.DB
}

type dbTx struct {
	*sqlx.Tx
}

// Pagination support
type Page struct {
	// Id that was the last of the previous page
	SinceId int

	// Count is the number of items in the page
	Count int
}

type Search struct {
	Where   string
	OrderBy string
	Match   string
}

func openDbFile(dbfile string) (ret *db, err error) {
	create := false

	dbfile = filepath.Clean(dbfile)

	i, err := os.Stat(dbfile)
	if err == nil && i.IsDir() {
		err = util.E.New("Given path is a directory")
		return
	}

	err = MkdirParents(dbfile)
	if err != nil {
		err = util.E.Annotate(err,
			"Could not create dbfile's parent directories")
		return
	}

	if _, err = os.Stat(dbfile); os.IsNotExist(err) {
		create = true
		err = nil
	}

	d, err := sqlx.Open("sqlite3", fmt.Sprintf("file:%s?cache=shared&mode=rwc", dbfile))
	if err != nil {
		err = util.E.Annotate(err, "Opening sqlite dbfile failed")
		return
	}

	if create {
		_, err = d.Exec(`
CREATE TABLE IF NOT EXISTS tag (
  id INTEGER PRIMARY KEY ASC AUTOINCREMENT,
  name TEXT DEFAULT "" NOT NULL UNIQUE ON CONFLICT ABORT,
  comment TEXT DEFAULT ""
);

-- The image data
CREATE TABLE IF NOT EXISTS image (
  id INTEGER PRIMARY KEY ASC AUTOINCREMENT,
  checksum TEXT UNIQUE NOT NULL ON CONFLICT ABORT,-- checksum of the file
  fileid TEXT DEFAULT "",                       -- used to construct the processed image,
						--   thumbnail and text files
  scandate DATETIME,                            -- timestamp when it was scanned
  adddate  DATETIME DEFAULT CURRENT_TIMESTAMP,  -- timestamp when it was created in db
  interpretdate DATETIME,                       -- timestamp when it was interpret

  processlog TEXT DEFAULT "",                   -- Log of processing
  filename TEXT DEFAULT ""                     -- The original filename
);

CREATE VIRTUAL TABLE IF NOT EXISTS imgtext USING fts4 (
  text DEFAULT "",				-- the OCR'd text
  comment DEFAULT ""				-- freeform comment
);

-- Tags for an image
CREATE TABLE IF NOT EXISTS imgtag (
  tagid INTEGER REFERENCES tag(id) NOT NULL,
  imgid INTEGER REFERENCES img(id) NOT NULL,
  UNIQUE (tagid, imgid)
);

-- Script for processing the images
CREATE TABLE IF NOT EXISTS script (
  id INTEGER PRIMARY KEY ASC AUTOINCREMENT,
  name TEXT UNIQUE ON CONFLICT ABORT,
  script TEXT DEFAULT ""
);

`)
		if err != nil {
			goto initfail
		}

	}
	d.Exec("PRAGMA busy_timeout=2000")
	if err != nil {
		goto initfail
	}

	ret = &db{dbfile, d}
	return

initfail:
	d.Close()
	err = util.E.Annotate(err, "Initializing the database failed")
	ret = nil
	return
}

func (db *db) getTag(id int) (ret Tag, err error) {
	err = db.Get(&ret, "SELECT * from tag WHERE id = $1", id)
	return
}

func (db *db) getScript(id int) (ret Script, err error) {
	err = db.Get(&ret, "SELECT * from script WHERE id = $1", id)
	return
}

func (db *db) getImage(id int) (ret Image, err error) {
	if id < 0 {
		err = util.E.New("Negative ID for image is invalid")
		return
	}

	imgs, err := db.getImages(nil, &Search{Where: fmt.Sprintf("AND image.id = %d", id)})
	if err != nil {
		return
	}
	if len(imgs) > 1 {
		err = util.E.New("Internal error: Multiple images with the same id")
		return
	}
	ret = imgs[0]
	return
}

func (db *db) getTags(p *Page) (ret []Tag, err error) {
	query := "SELECT * from tag"
	order := " ORDER BY name ASC"
	sel := func() error {
		return db.Select(&ret, query+order)
	}

	if p != nil {
		query += " WHERE (id > ?) " + order + " LIMIT ?"
		sel = func() error {
			return db.Select(&ret, query, p.SinceId, p.Count)
		}
	}

	err = sel()
	return
}

func (db *db) addTag(t Tag) (ret Tag, err error) {
	_, err = db.Exec("INSERT INTO tag(name, comment) VALUES($1, $2)", t.Name, t.Comment)
	if err != nil {
		return
	}
	err = db.Get(&ret, "SELECT * FROM tag WHERE name = $1", t.Name)

	return
}

func (db *db) updateTag(t Tag) (err error) {
	_, err = db.Exec("UPDATE tag SET comment = $1 WHERE name = $2", t.Comment, t.Name)
	return
}

func (db *db) deleteTag(t Tag) (err error) {
	_, err = db.Exec("DELETE FROM tag WHERE name = $1", t.Name)
	return
}

func (db *db) getScripts(p *Page) (ret []Script, err error) {
	query := "SELECT * from script"
	order := " ORDER BY name ASC"
	sel := func() error {
		return db.Select(&ret, query+order)
	}

	if p != nil {
		query += " WHERE (id > ?) " + order + " LIMIT ?"
		sel = func() error {
			return db.Select(&ret, query, p.SinceId, p.Count)
		}
	}

	err = sel()
	return
}

func (db *db) addScript(s Script) (ret Script, err error) {
	_, err = db.Exec("INSERT INTO script(name, script) VALUES($1, $2)", s.Name, s.Script)
	if err != nil {
		return
	}

	err = db.Get(&ret, "SELECT * FROM script WHERE name = $1", s.Name)
	return
}

func (db *db) updateScript(s Script) (err error) {
	_, err = db.Exec("UPDATE script SET script = $1 WHERE name = $2", s.Script, s.Name)
	return
}

func (db *db) deleteScript(s Script) (err error) {
	_, err = db.Exec("DELETE FROM script WHERE name = $1", s.Name)
	return
}

func withTx(db *db, f func(*sqlx.Tx) error) (err error) {
	tx, err := db.Beginx()
	if err != nil {
		return
	}

	err = f(tx)
	if err != nil {
		tx.Rollback()
		return
	}

	err = tx.Commit()
	return
}

func (db *db) getImages(p *Page, s *Search) (ret []Image, err error) {
	query := "SELECT * FROM image, imgtext"
	order := " ORDER BY image.id ASC"

	where := " WHERE imgtext.rowid = image.id"

	args := map[string]interface{}{}

	if s != nil {
		if s.Where != "" {
			where = where + " :where"
			args["where"] = s.Where
		}
		if s.Match != "" {
			where = where + " AND imgtext.text MATCH :match"
			args["match"] = s.Match
		}
		if s.OrderBy != "" {
			order = " ORDER BY :order ASC"
			args["order"] = s.OrderBy
		}
	}
	if p != nil {
		where = where + " AND (image.id > :id)"
		args["id"] = fmt.Sprintf("%d", p.SinceId)
		order = order + " LIMIT :limit"
		args["limit"] = fmt.Sprintf("%d", p.Count)
	}

	query = query + where + order

	nstmt, err := db.PrepareNamed(query)
	if err != nil {
		return
	}
	defer nstmt.Close()

	err = nstmt.Select(&ret, args)
	if err != nil {
		return
	}

	err = withTx(db, func(tx *sqlx.Tx) (err error) {
		for i := range ret {
			err = tx.Select(&ret[i].Tags, `SELECT tag.id, tag.name, tag.comment FROM tag, imgtag
                                                       WHERE imgtag.tagid = tag.id AND imgtag.imgid = $1 `, ret[i].Id)
			if err != nil {
				return
			}
		}
		return
	})
	return
}

func syncTagsToImage(tx *sqlx.Tx, i Image) (err error) {
	_, err = tx.NamedExec(`DELETE FROM imgtag WHERE imgid = :id`, i)
	if err != nil {
		return
	}

	for _, t := range i.Tags {
		_, err = tx.Exec(`INSERT INTO imgtag(imgid, tagid) SELECT $1, tag.id FROM tag WHERE tag.name = $2`, i.Id, t.Name)
		if err != nil {
			return
		}
	}
	return
}

func (db *db) addImage(i Image) (ret Image, err error) {
	err = withTx(db, func(tx *sqlx.Tx) (err error) {
		_, err = tx.NamedExec(`INSERT INTO
                   image(  checksum,  fileid,  scandate,  adddate,  interpretdate,  processlog,  filename)
                   VALUES(:checksum, :fileid, :scandate, :adddate, :interpretdate, :processlog, :filename)`, i)
		if err != nil {
			return
		}

		var id int
		err = tx.Get(&id, "SELECT id FROM image WHERE checksum=$1", i.Checksum)
		if err != nil {
			return
		}
		i.Id = id

		_, err = tx.NamedExec(`INSERT INTO imgtext(rowid, text, comment) VALUES (:id, :text, :comment)`, i)
		if err != nil {
			return
		}

		err = syncTagsToImage(tx, i)
		ret = i
		return
	})
	return
}

func (db *db) updateImage(i Image) (err error) {
	err = withTx(db, func(tx *sqlx.Tx) (err error) {
		_, err = tx.NamedExec(`UPDATE image SET
                      interpretdate = :interpretdate,
                      processlog = :processlog
                      WHERE image.id = :id`, i)
		if err != nil {
			return
		}

		_, err = tx.NamedExec(`UPDATE imgtext SET
                      text = :text,
                      comment = :comment
                      WHERE rowid = :id`, i)
		if err != nil {
			return
		}

		err = syncTagsToImage(tx, i)
		return
	})
	return
}

func (db *db) deleteImage(s Image) (err error) {
	err = withTx(db, func(tx *sqlx.Tx) (err error) {
		_, err = tx.Exec(`DELETE FROM imgtag WHERE imgid IN
                                  (SELECT id FROM image WHERE image.checksum = $1)`, s.Checksum)
		if err != nil {
			return
		}
		_, err = tx.Exec(`DELETE FROM imgtext WHERE rowid IN
                                  (SELECT id FROM image WHERE image.checksum = $1)`, s.Checksum)
		if err != nil {
			return
		}
		_, err = tx.Exec(`DELETE FROM image WHERE image.checksum = $1`, s.Checksum)
		return
	})
	return
}
<commit_msg>sqlitedb: fix getting single image<commit_after>package paperless

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

	"github.com/jmoiron/sqlx"
	_ "github.com/mattn/go-sqlite3"

	"github.com/kopoli/go-util"
)

type db struct {
	file string
	*sqlx.DB
}

type dbTx struct {
	*sqlx.Tx
}

// Pagination support
type Page struct {
	// Id that was the last of the previous page
	SinceId int

	// Count is the number of items in the page
	Count int
}

type Search struct {
	ID      int
	OrderBy string
	Match   string
}

func openDbFile(dbfile string) (ret *db, err error) {
	create := false

	dbfile = filepath.Clean(dbfile)

	i, err := os.Stat(dbfile)
	if err == nil && i.IsDir() {
		err = util.E.New("Given path is a directory")
		return
	}

	err = MkdirParents(dbfile)
	if err != nil {
		err = util.E.Annotate(err,
			"Could not create dbfile's parent directories")
		return
	}

	if _, err = os.Stat(dbfile); os.IsNotExist(err) {
		create = true
		err = nil
	}

	d, err := sqlx.Open("sqlite3", fmt.Sprintf("file:%s?cache=shared&mode=rwc", dbfile))
	if err != nil {
		err = util.E.Annotate(err, "Opening sqlite dbfile failed")
		return
	}

	if create {
		_, err = d.Exec(`
CREATE TABLE IF NOT EXISTS tag (
  id INTEGER PRIMARY KEY ASC AUTOINCREMENT,
  name TEXT DEFAULT "" NOT NULL UNIQUE ON CONFLICT ABORT,
  comment TEXT DEFAULT ""
);

-- The image data
CREATE TABLE IF NOT EXISTS image (
  id INTEGER PRIMARY KEY ASC AUTOINCREMENT,
  checksum TEXT UNIQUE NOT NULL ON CONFLICT ABORT,-- checksum of the file
  fileid TEXT DEFAULT "",                       -- used to construct the processed image,
						--   thumbnail and text files
  scandate DATETIME,                            -- timestamp when it was scanned
  adddate  DATETIME DEFAULT CURRENT_TIMESTAMP,  -- timestamp when it was created in db
  interpretdate DATETIME,                       -- timestamp when it was interpret

  processlog TEXT DEFAULT "",                   -- Log of processing
  filename TEXT DEFAULT ""                     -- The original filename
);

CREATE VIRTUAL TABLE IF NOT EXISTS imgtext USING fts4 (
  text DEFAULT "",				-- the OCR'd text
  comment DEFAULT ""				-- freeform comment
);

-- Tags for an image
CREATE TABLE IF NOT EXISTS imgtag (
  tagid INTEGER REFERENCES tag(id) NOT NULL,
  imgid INTEGER REFERENCES img(id) NOT NULL,
  UNIQUE (tagid, imgid)
);

-- Script for processing the images
CREATE TABLE IF NOT EXISTS script (
  id INTEGER PRIMARY KEY ASC AUTOINCREMENT,
  name TEXT UNIQUE ON CONFLICT ABORT,
  script TEXT DEFAULT ""
);

`)
		if err != nil {
			goto initfail
		}

	}
	d.Exec("PRAGMA busy_timeout=2000")
	if err != nil {
		goto initfail
	}

	ret = &db{dbfile, d}
	return

initfail:
	d.Close()
	err = util.E.Annotate(err, "Initializing the database failed")
	ret = nil
	return
}

func (db *db) getTag(id int) (ret Tag, err error) {
	err = db.Get(&ret, "SELECT * from tag WHERE id = $1", id)
	return
}

func (db *db) getScript(id int) (ret Script, err error) {
	err = db.Get(&ret, "SELECT * from script WHERE id = $1", id)
	return
}

func (db *db) getImage(id int) (ret Image, err error) {
	if id < 0 {
		err = util.E.New("Negative ID for image is invalid")
		return
	}

	imgs, err := db.getImages(nil, &Search{ID: id})
	if err != nil {
		return
	}

	if len(imgs) == 0 {
		err = util.E.New("No image found with id %d", id)
		return
	}
	if len(imgs) > 1 {
		err = util.E.New("Internal error: Multiple images with the same id")
		return
	}
	ret = imgs[0]
	return
}

func (db *db) getTags(p *Page) (ret []Tag, err error) {
	query := "SELECT * from tag"
	order := " ORDER BY name ASC"
	sel := func() error {
		return db.Select(&ret, query+order)
	}

	if p != nil {
		query += " WHERE (id > ?) " + order + " LIMIT ?"
		sel = func() error {
			return db.Select(&ret, query, p.SinceId, p.Count)
		}
	}

	err = sel()
	return
}

func (db *db) addTag(t Tag) (ret Tag, err error) {
	_, err = db.Exec("INSERT INTO tag(name, comment) VALUES($1, $2)", t.Name, t.Comment)
	if err != nil {
		return
	}
	err = db.Get(&ret, "SELECT * FROM tag WHERE name = $1", t.Name)

	return
}

func (db *db) updateTag(t Tag) (err error) {
	_, err = db.Exec("UPDATE tag SET comment = $1 WHERE name = $2", t.Comment, t.Name)
	return
}

func (db *db) deleteTag(t Tag) (err error) {
	_, err = db.Exec("DELETE FROM tag WHERE name = $1", t.Name)
	return
}

func (db *db) getScripts(p *Page) (ret []Script, err error) {
	query := "SELECT * from script"
	order := " ORDER BY name ASC"
	sel := func() error {
		return db.Select(&ret, query+order)
	}

	if p != nil {
		query += " WHERE (id > ?) " + order + " LIMIT ?"
		sel = func() error {
			return db.Select(&ret, query, p.SinceId, p.Count)
		}
	}

	err = sel()
	return
}

func (db *db) addScript(s Script) (ret Script, err error) {
	_, err = db.Exec("INSERT INTO script(name, script) VALUES($1, $2)", s.Name, s.Script)
	if err != nil {
		return
	}

	err = db.Get(&ret, "SELECT * FROM script WHERE name = $1", s.Name)
	return
}

func (db *db) updateScript(s Script) (err error) {
	_, err = db.Exec("UPDATE script SET script = $1 WHERE name = $2", s.Script, s.Name)
	return
}

func (db *db) deleteScript(s Script) (err error) {
	_, err = db.Exec("DELETE FROM script WHERE name = $1", s.Name)
	return
}

func withTx(db *db, f func(*sqlx.Tx) error) (err error) {
	tx, err := db.Beginx()
	if err != nil {
		return
	}

	err = f(tx)
	if err != nil {
		tx.Rollback()
		return
	}

	err = tx.Commit()
	return
}

func (db *db) getImages(p *Page, s *Search) (ret []Image, err error) {
	query := "SELECT * FROM image, imgtext"
	order := " ORDER BY image.id ASC"

	where := " WHERE imgtext.rowid = image.id"

	args := map[string]interface{}{}

	if s != nil {
		if s.ID != 0 {
			where = where + " AND image.id = :id"
			args["id"] = fmt.Sprintf("%d", s.ID)
		}
		if s.Match != "" {
			where = where + " AND imgtext.text MATCH :match"
			args["match"] = s.Match
		}
		if s.OrderBy != "" {
			order = " ORDER BY :order ASC"
			args["order"] = s.OrderBy
		}
	}
	if p != nil {
		where = where + " AND (image.id > :id)"
		args["id"] = fmt.Sprintf("%d", p.SinceId)
		order = order + " LIMIT :limit"
		args["limit"] = fmt.Sprintf("%d", p.Count)
	}

	query = query + where + order

	nstmt, err := db.PrepareNamed(query)
	if err != nil {
		return
	}
	defer nstmt.Close()

	err = nstmt.Select(&ret, args)
	if err != nil {
		return
	}

	err = withTx(db, func(tx *sqlx.Tx) (err error) {
		for i := range ret {
			err = tx.Select(&ret[i].Tags, `SELECT tag.id, tag.name, tag.comment FROM tag, imgtag
                                                       WHERE imgtag.tagid = tag.id AND imgtag.imgid = $1 `, ret[i].Id)
			if err != nil {
				return
			}
		}
		return
	})
	return
}

func syncTagsToImage(tx *sqlx.Tx, i Image) (err error) {
	_, err = tx.NamedExec(`DELETE FROM imgtag WHERE imgid = :id`, i)
	if err != nil {
		return
	}

	for _, t := range i.Tags {
		_, err = tx.Exec(`INSERT INTO imgtag(imgid, tagid) SELECT $1, tag.id FROM tag WHERE tag.name = $2`, i.Id, t.Name)
		if err != nil {
			return
		}
	}
	return
}

func (db *db) addImage(i Image) (ret Image, err error) {
	err = withTx(db, func(tx *sqlx.Tx) (err error) {
		_, err = tx.NamedExec(`INSERT INTO
                   image(  checksum,  fileid,  scandate,  adddate,  interpretdate,  processlog,  filename)
                   VALUES(:checksum, :fileid, :scandate, :adddate, :interpretdate, :processlog, :filename)`, i)
		if err != nil {
			return
		}

		var id int
		err = tx.Get(&id, "SELECT id FROM image WHERE checksum=$1", i.Checksum)
		if err != nil {
			return
		}
		i.Id = id

		_, err = tx.NamedExec(`INSERT INTO imgtext(rowid, text, comment) VALUES (:id, :text, :comment)`, i)
		if err != nil {
			return
		}

		err = syncTagsToImage(tx, i)
		ret = i
		return
	})
	return
}

func (db *db) updateImage(i Image) (err error) {
	err = withTx(db, func(tx *sqlx.Tx) (err error) {
		_, err = tx.NamedExec(`UPDATE image SET
                      interpretdate = :interpretdate,
                      processlog = :processlog
                      WHERE image.id = :id`, i)
		if err != nil {
			return
		}

		_, err = tx.NamedExec(`UPDATE imgtext SET
                      text = :text,
                      comment = :comment
                      WHERE rowid = :id`, i)
		if err != nil {
			return
		}

		err = syncTagsToImage(tx, i)
		return
	})
	return
}

func (db *db) deleteImage(s Image) (err error) {
	err = withTx(db, func(tx *sqlx.Tx) (err error) {
		_, err = tx.Exec(`DELETE FROM imgtag WHERE imgid IN
                                  (SELECT id FROM image WHERE image.checksum = $1)`, s.Checksum)
		if err != nil {
			return
		}
		_, err = tx.Exec(`DELETE FROM imgtext WHERE rowid IN
                                  (SELECT id FROM image WHERE image.checksum = $1)`, s.Checksum)
		if err != nil {
			return
		}
		_, err = tx.Exec(`DELETE FROM image WHERE image.checksum = $1`, s.Checksum)
		return
	})
	return
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"path"
	"sort"
)

func check(e error) {
	if e != nil {
		panic(e)
	}
}

type DebugInfo struct {
	PC     uint64 `json:"pc"`
	Object string `json:"object"`
	Offset uint64 `json:"offset"`
	Symbol string `json:"symbol"`
	Source string `json:"source_file"`
	Line   string `json:"line_number"`
}

type Index struct {
	data      []*DebugInfo
	PCMap     map[uint64]*DebugInfo
	ObjectMap map[string]map[uint64]*DebugInfo
}

func (self *Index) New(pc uint64, object string, offset uint64) {
	if self.PCMap[pc] == nil {
		debinf := &DebugInfo{
			PC:     pc,
			Object: object,
			Offset: offset,
		}
		self.data = append(self.data, debinf)
		self.PCMap[pc] = debinf
		if self.ObjectMap[object] == nil {
			self.ObjectMap[object] = make(map[uint64]*DebugInfo)
		}
		self.ObjectMap[object][offset] = debinf
	}
}

func NewIndex() Index {
	return Index{
		PCMap:     make(map[uint64]*DebugInfo),
		ObjectMap: make(map[string]map[uint64]*DebugInfo),
	}
}

type DumpCall struct {
	UnmappedCall
	Count uint64 `json:"count"`
}

type ByCallCount []DumpCall

func (p ByCallCount) Len() int           { return len(p) }
func (p ByCallCount) Less(i, j int) bool { return p[i].Count > p[j].Count }
func (p ByCallCount) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

func Dump(writer io.Writer, acc *map[UnmappedCall]uint64, index *Index) {
	outgoing := struct {
		Calls []DumpCall   `json:"calls"`
		Index []*DebugInfo `json:"index"`
	}{Index: index.data}
	for call, count := range *acc {
		outgoing.Calls = append(outgoing.Calls, DumpCall{call, count})
	}
	sort.Sort(ByCallCount(outgoing.Calls))
	check(json.NewEncoder(writer).Encode(outgoing))
}

type ByPC []*DebugInfo

func (p ByPC) Len() int           { return len(p) }
func (p ByPC) Less(i, j int) bool { return p[i].PC < p[j].PC }
func (p ByPC) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

var concurrency = flag.Int("j", 1, "number of addr2line workers to run simultaneously")
var object_directory = flag.String("objdir", "objects", "directory to find compiled objects in")

func Usage() {
	fmt.Fprintf(os.Stderr, "Usage: %s [options] DIRECTORY\n\n", os.Args[0])
	fmt.Fprintln(os.Stderr, "  DIRECTORY")
	fmt.Fprintln(os.Stderr, "    \tdirectory to find profile data")
	flag.PrintDefaults()
}

func main() {
	flag.Usage = Usage
	flag.Parse()

	remaining_args := flag.Args()

	if len(flag.Args()) != 1 {
		flag.Usage()
		os.Exit(2)
	}

	directory := remaining_args[0]
	maps_fn := path.Join(directory, "maps")
	map_, err := ParseMapX(maps_fn)
	check(err)
	unmapped_calls_fn := path.Join(directory, "unmapped-calls")

	acc := ReadFile(unmapped_calls_fn)

	pb := ProgressBar(
		len(acc),
		maps_fn)
	misses := 0
	index := NewIndex()
	for call := range acc {
		caller := map_.Translate(call.Caller)
		callee := map_.Translate(call.Callee)
		if caller == nil || callee == nil {
			misses++
			pb.Other = fmt.Sprintf(", misses: %d", misses)
		} else {
			index.New(call.Caller, caller.Pathname, caller.Offset)
			index.New(call.Callee, callee.Pathname, callee.Offset)
		}
		pb.Inc()
	}

	sort.Sort(ByPC(index.data))

	for pathname, offsets := range index.ObjectMap {
		log.Printf("%d addresses to be resolved in %v\n", len(offsets), pathname)
		var addresses []uint64
		for offset := range offsets {
			addresses = append(addresses, offset)
		}
		results := Addr2Line(path.Join("objects", path.Base(pathname)), addresses, *concurrency)
		for _, result := range results {
			address := result.Address
			offsets[address].Symbol = result.Symbol
			offsets[address].Source = result.Filename
			offsets[address].Line = result.Line
		}
	}

	Dump(os.Stdout, &acc, &index)
}
<commit_msg>fix: -objdir is ignored<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"path"
	"sort"
)

func check(e error) {
	if e != nil {
		panic(e)
	}
}

type DebugInfo struct {
	PC     uint64 `json:"pc"`
	Object string `json:"object"`
	Offset uint64 `json:"offset"`
	Symbol string `json:"symbol"`
	Source string `json:"source_file"`
	Line   string `json:"line_number"`
}

type Index struct {
	data      []*DebugInfo
	PCMap     map[uint64]*DebugInfo
	ObjectMap map[string]map[uint64]*DebugInfo
}

func (self *Index) New(pc uint64, object string, offset uint64) {
	if self.PCMap[pc] == nil {
		debinf := &DebugInfo{
			PC:     pc,
			Object: object,
			Offset: offset,
		}
		self.data = append(self.data, debinf)
		self.PCMap[pc] = debinf
		if self.ObjectMap[object] == nil {
			self.ObjectMap[object] = make(map[uint64]*DebugInfo)
		}
		self.ObjectMap[object][offset] = debinf
	}
}

func NewIndex() Index {
	return Index{
		PCMap:     make(map[uint64]*DebugInfo),
		ObjectMap: make(map[string]map[uint64]*DebugInfo),
	}
}

type DumpCall struct {
	UnmappedCall
	Count uint64 `json:"count"`
}

type ByCallCount []DumpCall

func (p ByCallCount) Len() int           { return len(p) }
func (p ByCallCount) Less(i, j int) bool { return p[i].Count > p[j].Count }
func (p ByCallCount) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

func Dump(writer io.Writer, acc *map[UnmappedCall]uint64, index *Index) {
	outgoing := struct {
		Calls []DumpCall   `json:"calls"`
		Index []*DebugInfo `json:"index"`
	}{Index: index.data}
	for call, count := range *acc {
		outgoing.Calls = append(outgoing.Calls, DumpCall{call, count})
	}
	sort.Sort(ByCallCount(outgoing.Calls))
	check(json.NewEncoder(writer).Encode(outgoing))
}

type ByPC []*DebugInfo

func (p ByPC) Len() int           { return len(p) }
func (p ByPC) Less(i, j int) bool { return p[i].PC < p[j].PC }
func (p ByPC) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

var concurrency = flag.Int("j", 1, "number of addr2line workers to run simultaneously")
var object_directory = flag.String("objdir", "objects", "directory to find compiled objects in")

func Usage() {
	fmt.Fprintf(os.Stderr, "Usage: %s [options] DIRECTORY\n\n", os.Args[0])
	fmt.Fprintln(os.Stderr, "  DIRECTORY")
	fmt.Fprintln(os.Stderr, "    \tdirectory to find profile data")
	flag.PrintDefaults()
}

func main() {
	flag.Usage = Usage
	flag.Parse()

	remaining_args := flag.Args()

	if len(flag.Args()) != 1 {
		flag.Usage()
		os.Exit(2)
	}

	directory := remaining_args[0]
	maps_fn := path.Join(directory, "maps")
	map_, err := ParseMapX(maps_fn)
	check(err)
	unmapped_calls_fn := path.Join(directory, "unmapped-calls")

	acc := ReadFile(unmapped_calls_fn)

	pb := ProgressBar(
		len(acc),
		maps_fn)
	misses := 0
	index := NewIndex()
	for call := range acc {
		caller := map_.Translate(call.Caller)
		callee := map_.Translate(call.Callee)
		if caller == nil || callee == nil {
			misses++
			pb.Other = fmt.Sprintf(", misses: %d", misses)
		} else {
			index.New(call.Caller, caller.Pathname, caller.Offset)
			index.New(call.Callee, callee.Pathname, callee.Offset)
		}
		pb.Inc()
	}

	sort.Sort(ByPC(index.data))

	for pathname, offsets := range index.ObjectMap {
		log.Printf("%d addresses to be resolved in %v\n", len(offsets), pathname)
		var addresses []uint64
		for offset := range offsets {
			addresses = append(addresses, offset)
		}
		results := Addr2Line(path.Join(*object_directory, path.Base(pathname)), addresses, *concurrency)
		for _, result := range results {
			address := result.Address
			offsets[address].Symbol = result.Symbol
			offsets[address].Source = result.Filename
			offsets[address].Line = result.Line
		}
	}

	Dump(os.Stdout, &acc, &index)
}
<|endoftext|>


<commit_before>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2016 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package cmd

import (
	"bytes"
	"testing"

	"github.com/spf13/afero"
	"github.com/stretchr/testify/assert"
)

const testHAR = `
{
	"log": {
		"version": "1.2",
		"creator": {
		"name": "WebInspector",
		"version": "537.36"
		},
		"pages": [
		{
			"startedDateTime": "2018-01-21T19:48:40.432Z",
			"id": "page_2",
			"title": "https://golang.org/",
			"pageTimings": {
			"onContentLoad": 590.3389999875799,
			"onLoad": 1593.1009999476373
			}
		}
		],
		"entries": [
		{
			"startedDateTime": "2018-01-21T19:48:40.587Z",
			"time": 147.5899999756366,
			"request": {
				"method": "GET",
				"url": "https://golang.org/",
				"httpVersion": "http/2.0+quic/39",
				"headers": [
					{
					"name": "pragma",
					"value": "no-cache"
					}
				],
				"queryString": [],
				"cookies": [],
				"headersSize": -1,
				"bodySize": 0
			},
			"cache": {},
			"timings": {
				"blocked": 0.43399997614324004,
				"dns": -1,
				"ssl": -1,
				"connect": -1,
				"send": 0.12700003571808005,
				"wait": 149.02899996377528,
				"receive": 0,
				"_blocked_queueing": -1
			},
			"serverIPAddress": "172.217.22.177",
			"pageref": "page_2"
		}
		]
	}
}
`

const testHARConvertResult = `import { group, sleep } from 'k6';
import http from 'k6/http';

// Version: 1.2
// Creator: WebInspector

export default function() {

	group("page_2 - https://golang.org/", function() {
		let req, res;
		req = [{
			"method": "get",
			"url": "https://golang.org/",
			"params": {
				"headers": {
					"pragma": "no-cache"
				}
			}
		}];
		res = http.batch(req);
		// Random sleep between 2s and 4s
		sleep(Math.floor(Math.random()*3+2));
	});

}
`

func TestIntegrationConvertCmd(t *testing.T) {
	t.Run("Stdout", func(t *testing.T) {
		defaultFs = afero.NewMemMapFs()
		afero.WriteFile(defaultFs, "/input.har", []byte(testHAR), 0644)

		buf := &bytes.Buffer{}
		defaultWriter = buf

		err := convertCmd.RunE(convertCmd, []string{"/input.har"})
		assert.NoError(t, err)
		assert.Equal(t, testHARConvertResult, buf.String())
	})
	t.Run("Output file", func(t *testing.T) {
		defaultFs = afero.NewMemMapFs()
		afero.WriteFile(defaultFs, "/input.har", []byte(testHAR), 0644)

		convertCmd.Flags().Set("output", "/output.js")
		err := convertCmd.RunE(convertCmd, []string{"/input.har"})
		assert.NoError(t, err)

		output, err := afero.ReadFile(defaultFs, "/output.js")
		assert.Equal(t, testHARConvertResult, string(output))
	})
}
<commit_msg>Check error<commit_after>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2016 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package cmd

import (
	"bytes"
	"testing"

	"github.com/spf13/afero"
	"github.com/stretchr/testify/assert"
)

const testHAR = `
{
	"log": {
		"version": "1.2",
		"creator": {
		"name": "WebInspector",
		"version": "537.36"
		},
		"pages": [
		{
			"startedDateTime": "2018-01-21T19:48:40.432Z",
			"id": "page_2",
			"title": "https://golang.org/",
			"pageTimings": {
			"onContentLoad": 590.3389999875799,
			"onLoad": 1593.1009999476373
			}
		}
		],
		"entries": [
		{
			"startedDateTime": "2018-01-21T19:48:40.587Z",
			"time": 147.5899999756366,
			"request": {
				"method": "GET",
				"url": "https://golang.org/",
				"httpVersion": "http/2.0+quic/39",
				"headers": [
					{
					"name": "pragma",
					"value": "no-cache"
					}
				],
				"queryString": [],
				"cookies": [],
				"headersSize": -1,
				"bodySize": 0
			},
			"cache": {},
			"timings": {
				"blocked": 0.43399997614324004,
				"dns": -1,
				"ssl": -1,
				"connect": -1,
				"send": 0.12700003571808005,
				"wait": 149.02899996377528,
				"receive": 0,
				"_blocked_queueing": -1
			},
			"serverIPAddress": "172.217.22.177",
			"pageref": "page_2"
		}
		]
	}
}
`

const testHARConvertResult = `import { group, sleep } from 'k6';
import http from 'k6/http';

// Version: 1.2
// Creator: WebInspector

export default function() {

	group("page_2 - https://golang.org/", function() {
		let req, res;
		req = [{
			"method": "get",
			"url": "https://golang.org/",
			"params": {
				"headers": {
					"pragma": "no-cache"
				}
			}
		}];
		res = http.batch(req);
		// Random sleep between 2s and 4s
		sleep(Math.floor(Math.random()*3+2));
	});

}
`

func TestIntegrationConvertCmd(t *testing.T) {
	t.Run("Stdout", func(t *testing.T) {
		defaultFs = afero.NewMemMapFs()
		afero.WriteFile(defaultFs, "/input.har", []byte(testHAR), 0644)

		buf := &bytes.Buffer{}
		defaultWriter = buf

		err := convertCmd.RunE(convertCmd, []string{"/input.har"})
		assert.NoError(t, err)
		assert.Equal(t, testHARConvertResult, buf.String())
	})
	t.Run("Output file", func(t *testing.T) {
		defaultFs = afero.NewMemMapFs()
		afero.WriteFile(defaultFs, "/input.har", []byte(testHAR), 0644)

		convertCmd.Flags().Set("output", "/output.js")
		err := convertCmd.RunE(convertCmd, []string{"/input.har"})
		assert.NoError(t, err)

		output, err := afero.ReadFile(defaultFs, "/output.js")
		assert.NoError(t, err)
		assert.Equal(t, testHARConvertResult, string(output))
	})
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/ecc1/dexcom"
)

const (
	csvFormat  = "csv"
	textFormat = "text"
	jsonFormat = "json"
)

var (
	all         = flag.Bool("a", false, "get all records")
	duration    = flag.Duration("d", time.Hour, "get `duration` worth of previous records")
	format      = flag.String("f", textFormat, "format in which to print records (csv, json, or text)")
	egv         = flag.Bool("g", true, "include glucose records")
	sensor      = flag.Bool("s", false, "include sensor records")
	calibration = flag.Bool("c", false, "include calibration records")
	meter       = flag.Bool("m", false, "include meter records")

	recordTypes = []struct {
		flag *bool
		page dexcom.PageType
	}{
		{egv, dexcom.EGVData},
		{sensor, dexcom.SensorData},
		{calibration, dexcom.CalibrationData},
		{meter, dexcom.MeterData},
	}
)

func main() {
	flag.Parse()
	switch *format {
	case csvFormat, jsonFormat, textFormat:
	default:
		flag.Usage()
		return
	}
	var cutoff time.Time
	cgm := dexcom.Open()
	if cgm.Error() != nil {
		log.Fatal(cgm.Error())
	}
	if *all {
		log.Printf("retrieving entire record history")
		*egv = true
		*sensor = true
		*calibration = true
		*meter = true
	} else {
		cutoff = time.Now().Add(-*duration)
		log.Printf("retrieving records since %s", cutoff.Format(dexcom.UserTimeLayout))
	}
	scans := scanRecords(cgm, cutoff)
	results := dexcom.MergeHistory(scans...)
	if *format == jsonFormat {
		e := json.NewEncoder(os.Stdout)
		e.SetIndent("", "  ")
		err := e.Encode(results)
		if err != nil {
			log.Fatal(err)
		}
		return
	}
	if *format == csvFormat {
		fmt.Printf("Time,Type,Glucose,Sensor,Slope,Intercept,Scale,Decay\n")
	}
	for _, r := range results {
		printRecord(r)
	}
}

func scanRecords(cgm *dexcom.CGM, cutoff time.Time) []dexcom.Records {
	var scans []dexcom.Records
	for _, t := range recordTypes {
		if !*t.flag {
			continue
		}
		var v dexcom.Records
		// Special case when both EGV and sensor records are requested.
		if t.page == dexcom.EGVData && *sensor {
			v = cgm.GlucoseReadings(cutoff)
		} else if t.page == dexcom.SensorData && *egv {
			continue
		} else {
			v = cgm.ReadHistory(t.page, cutoff)
		}
		if len(v) != 0 {
			scans = append(scans, v)
		}
	}
	if cgm.Error() != nil {
		log.Fatal(cgm.Error())
	}
	if len(scans) == 0 {
		log.Fatal("no records found")
	}
	return scans
}

func printRecord(r dexcom.Record) {
	t := r.Time().Format(dexcom.UserTimeLayout)
	switch info := r.Info.(type) {
	case dexcom.SensorInfo:
		printSensor(t, info)
	case dexcom.EGVInfo:
		printEGV(t, info)
	case dexcom.BGInfo:
		printBG(t, info)
	case dexcom.CalibrationInfo:
		printCalibration(t, info)
	case dexcom.MeterInfo:
		printMeter(t, info)
	default:
		panic(fmt.Sprintf("unexpected record %+v", r))
	}
}

func printSensor(t string, s dexcom.SensorInfo) {
	switch *format {
	case csvFormat:
		fmt.Printf("%s,G,,%d\n", t, s.Unfiltered)
	case textFormat:
		fmt.Printf("%s            %6d  %6d  %3d\n", t, s.Unfiltered, s.Filtered, s.RSSI)
	}
}

func printEGV(t string, e dexcom.EGVInfo) {
	switch *format {
	case csvFormat:
		fmt.Printf("%s,G,%d,\n", t, e.Glucose)
	case textFormat:
		fmt.Printf("%s  %3d  %3d\n", t, e.Glucose, e.Noise)
	}
}

func printBG(t string, bg dexcom.BGInfo) {
	printEGV(t, bg.EGV)
	printSensor(t, bg.Sensor)
}

func printCalibration(t string, cal dexcom.CalibrationInfo) {
	switch *format {
	case csvFormat:
		fmt.Printf("%s,%s,,,%g,%g,%g,%g\n", t, "C", cal.Slope, cal.Intercept, cal.Scale, cal.Decay)
		for _, d := range cal.Data {
			t = d.TimeEntered.Format(dexcom.UserTimeLayout)
			fmt.Printf("%s,%s,%d,%d\n", t, "D", d.Glucose, d.Raw)
		}
	case textFormat:
		fmt.Printf("%s  %-5s  %g  %g  %g  %g\n", t, "CAL", cal.Slope, cal.Intercept, cal.Scale, cal.Decay)
		for _, d := range cal.Data {
			t = d.TimeEntered.Format(dexcom.UserTimeLayout)
			fmt.Printf("%s  %-5s  %3d  %6d\n", t, "DATA", d.Glucose, d.Raw)
		}
	}
}

func printMeter(t string, m dexcom.MeterInfo) {
	switch *format {
	case csvFormat:
		fmt.Printf("%s,%s,%d\n", t, "M", m.Glucose)
	case textFormat:
		fmt.Printf("%s  %-5s  %3d\n", t, "METER", m.Glucose)
	}
}
<commit_msg>Add ns format<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/ecc1/dexcom"
)

const (
	csvFormat  = "csv"
	jsonFormat = "json"
	nsFormat   = "ns"
	textFormat = "text"
)

var (
	all      = flag.Bool("a", false, "get all records")
	duration = flag.Duration("d", time.Hour, "get `duration` worth of previous records")
	format   = flag.String("f", textFormat, "format in which to print records (csv, json, ns, or text)")

	egv         = flag.Bool("e", true, "include EGV records")
	sensor      = flag.Bool("s", false, "include sensor records")
	calibration = flag.Bool("c", false, "include calibration records")
	meter       = flag.Bool("m", false, "include meter records")

	recordTypes = []struct {
		flag *bool
		page dexcom.PageType
	}{
		{egv, dexcom.EGVData},
		{sensor, dexcom.SensorData},
		{calibration, dexcom.CalibrationData},
		{meter, dexcom.MeterData},
	}
)

func main() {
	flag.Parse()
	switch *format {
	case csvFormat, jsonFormat, nsFormat, textFormat:
	default:
		flag.Usage()
		return
	}
	var cutoff time.Time
	cgm := dexcom.Open()
	if cgm.Error() != nil {
		log.Fatal(cgm.Error())
	}
	if *all {
		log.Printf("retrieving entire record history")
		*egv = true
		*sensor = true
		*calibration = true
		*meter = true
	} else {
		cutoff = time.Now().Add(-*duration)
		log.Printf("retrieving records since %s", cutoff.Format(dexcom.UserTimeLayout))
	}
	scans := scanRecords(cgm, cutoff)
	results := dexcom.MergeHistory(scans...)
	if len(results) == 0 {
		return
	}
	if *format == nsFormat {
		printJSON(dexcom.NightscoutEntries(results))
		return
	}
	if *format == jsonFormat {
		printJSON(results)
		return
	}
	if *format == csvFormat {
		fmt.Printf("Time,Type,Glucose,Sensor,Slope,Intercept,Scale,Decay\n")
	}
	for _, r := range results {
		printRecord(r)
	}
}

func scanRecords(cgm *dexcom.CGM, cutoff time.Time) []dexcom.Records {
	var scans []dexcom.Records
	for _, t := range recordTypes {
		if !*t.flag {
			continue
		}
		scans = append(scans, cgm.ReadHistory(t.page, cutoff))
	}
	if cgm.Error() != nil {
		log.Fatal(cgm.Error())
	}
	if len(scans) == 0 {
		log.Fatal("no records found")
	}
	return scans
}

func printJSON(v interface{}) {
	e := json.NewEncoder(os.Stdout)
	e.SetIndent("", "  ")
	err := e.Encode(v)
	if err != nil {
		log.Fatal(err)
	}
}

func printRecord(r dexcom.Record) {
	t := r.Time().Format(dexcom.UserTimeLayout)
	switch info := r.Info.(type) {
	case dexcom.SensorInfo:
		printSensor(t, info)
	case dexcom.EGVInfo:
		printEGV(t, info)
	case dexcom.CalibrationInfo:
		printCalibration(t, info)
	case dexcom.MeterInfo:
		printMeter(t, info)
	default:
		panic(fmt.Sprintf("unexpected record %+v", r))
	}
}

func printSensor(t string, s dexcom.SensorInfo) {
	switch *format {
	case csvFormat:
		fmt.Printf("%s,G,,%d\n", t, s.Unfiltered)
	case textFormat:
		fmt.Printf("%s            %6d  %6d  %3d\n", t, s.Unfiltered, s.Filtered, s.RSSI)
	}
}

func printEGV(t string, e dexcom.EGVInfo) {
	switch *format {
	case csvFormat:
		fmt.Printf("%s,G,%d,\n", t, e.Glucose)
	case textFormat:
		fmt.Printf("%s  %3d  %3d\n", t, e.Glucose, e.Noise)
	}
}

func printCalibration(t string, cal dexcom.CalibrationInfo) {
	switch *format {
	case csvFormat:
		fmt.Printf("%s,%s,,,%g,%g,%g,%g\n", t, "C", cal.Slope, cal.Intercept, cal.Scale, cal.Decay)
		for _, d := range cal.Data {
			t = d.TimeEntered.Format(dexcom.UserTimeLayout)
			fmt.Printf("%s,%s,%d,%d\n", t, "D", d.Glucose, d.Raw)
		}
	case textFormat:
		fmt.Printf("%s  %-5s  %g  %g  %g  %g\n", t, "CAL", cal.Slope, cal.Intercept, cal.Scale, cal.Decay)
		for _, d := range cal.Data {
			t = d.TimeEntered.Format(dexcom.UserTimeLayout)
			fmt.Printf("%s  %-5s  %3d  %6d\n", t, "DATA", d.Glucose, d.Raw)
		}
	}
}

func printMeter(t string, m dexcom.MeterInfo) {
	switch *format {
	case csvFormat:
		fmt.Printf("%s,%s,%d\n", t, "M", m.Glucose)
	case textFormat:
		fmt.Printf("%s  %-5s  %3d\n", t, "METER", m.Glucose)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

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

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

package main

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

	"github.com/spf13/cobra"

	"k8s.io/helm/cmd/helm/helmpath"
	"k8s.io/helm/pkg/plugin"
)

const pluginEnvVar = "HELM_PLUGIN"

// loadPlugins loads plugins into the command list.
//
// This follows a different pattern than the other commands because it has
// to inspect its environment and then add commands to the base command
// as it finds them.
func loadPlugins(baseCmd *cobra.Command, home helmpath.Home, out io.Writer) {
	plugdirs := os.Getenv(pluginEnvVar)
	if plugdirs == "" {
		plugdirs = home.Plugins()
	}

	found, err := findPlugins(plugdirs)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to load plugins: %s", err)
		return
	}

	// Now we create commands for all of these.
	for _, plug := range found {
		plug := plug
		md := plug.Metadata
		if md.Usage == "" {
			md.Usage = fmt.Sprintf("the %q plugin", md.Name)
		}

		c := &cobra.Command{
			Use:   md.Name,
			Short: md.Usage,
			Long:  md.Description,
			RunE: func(cmd *cobra.Command, args []string) error {

				k, u := manuallyProcessArgs(args)
				if err := cmd.ParseFlags(k); err != nil {
					return err
				}

				// Call setupEnv before PrepareCommand because
				// PrepareCommand uses os.ExpandEnv and expects the
				// setupEnv vars.
				setupEnv(md.Name, plug.Dir, plugdirs, home)
				main, argv := plug.PrepareCommand(u)

				prog := exec.Command(main, argv...)
				prog.Env = os.Environ()
				prog.Stdout = out
				prog.Stderr = os.Stderr
				if err := prog.Run(); err != nil {
					eerr := err.(*exec.ExitError)
					os.Stderr.Write(eerr.Stderr)
					return fmt.Errorf("plugin %q exited with error", md.Name)
				}
				return nil
			},
			// This passes all the flags to the subcommand.
			DisableFlagParsing: true,
		}

		if md.UseTunnel {
			c.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
				// Parse the parent flag, but not the local flags.
				k, _ := manuallyProcessArgs(args)
				if err := c.Parent().ParseFlags(k); err != nil {
					return err
				}
				return setupConnection(cmd, args)
			}
		}

		// TODO: Make sure a command with this name does not already exist.
		baseCmd.AddCommand(c)
	}
}

// manuallyProcessArgs processes an arg array, removing special args.
//
// Returns two sets of args: known and unknown (in that order)
func manuallyProcessArgs(args []string) ([]string, []string) {
	known := []string{}
	unknown := []string{}
	kvargs := []string{"--host", "--kube-context", "--home"}
	knownArg := func(a string) bool {
		for _, pre := range kvargs {
			if strings.HasPrefix(a, pre+"=") {
				return true
			}
		}
		return false
	}
	for i := 0; i < len(args); i++ {
		switch a := args[i]; a {
		case "--debug":
			known = append(known, a)
		case "--host", "--kube-context", "--home":
			known = append(known, a, args[i+1])
			i++
		default:
			if knownArg(a) {
				known = append(known, a)
				continue
			}
			unknown = append(unknown, a)
		}
	}
	return known, unknown
}

// findPlugins returns a list of YAML files that describe plugins.
func findPlugins(plugdirs string) ([]*plugin.Plugin, error) {
	found := []*plugin.Plugin{}
	// Let's get all UNIXy and allow path separators
	for _, p := range filepath.SplitList(plugdirs) {
		matches, err := plugin.LoadAll(p)
		if err != nil {
			return matches, err
		}
		found = append(found, matches...)
	}
	return found, nil
}

// setupEnv prepares os.Env for plugins. It operates on os.Env because
// the plugin subsystem itself needs access to the environment variables
// created here.
func setupEnv(shortname, base, plugdirs string, home helmpath.Home) {
	// Set extra env vars:
	for key, val := range map[string]string{
		"HELM_PLUGIN_NAME": shortname,
		"HELM_PLUGIN_DIR":  base,
		"HELM_BIN":         os.Args[0],

		// Set vars that may not have been set, and save client the
		// trouble of re-parsing.
		pluginEnvVar: plugdirs,
		homeEnvVar:   home.String(),

		// Set vars that convey common information.
		"HELM_PATH_REPOSITORY":       home.Repository(),
		"HELM_PATH_REPOSITORY_FILE":  home.RepositoryFile(),
		"HELM_PATH_CACHE":            home.Cache(),
		"HELM_PATH_LOCAL_REPOSITORY": home.LocalRepository(),
		//"HELM_PATH_STARTER":          home.Starter(),

		"TILLER_HOST": tillerHost,
	} {
		os.Setenv(key, val)
	}
}
<commit_msg>fix(helm): handle errors when plugin command is not found<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

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

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

package main

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

	"github.com/spf13/cobra"

	"k8s.io/helm/cmd/helm/helmpath"
	"k8s.io/helm/pkg/plugin"
)

const pluginEnvVar = "HELM_PLUGIN"

// loadPlugins loads plugins into the command list.
//
// This follows a different pattern than the other commands because it has
// to inspect its environment and then add commands to the base command
// as it finds them.
func loadPlugins(baseCmd *cobra.Command, home helmpath.Home, out io.Writer) {
	plugdirs := os.Getenv(pluginEnvVar)
	if plugdirs == "" {
		plugdirs = home.Plugins()
	}

	found, err := findPlugins(plugdirs)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to load plugins: %s", err)
		return
	}

	// Now we create commands for all of these.
	for _, plug := range found {
		plug := plug
		md := plug.Metadata
		if md.Usage == "" {
			md.Usage = fmt.Sprintf("the %q plugin", md.Name)
		}

		c := &cobra.Command{
			Use:   md.Name,
			Short: md.Usage,
			Long:  md.Description,
			RunE: func(cmd *cobra.Command, args []string) error {

				k, u := manuallyProcessArgs(args)
				if err := cmd.ParseFlags(k); err != nil {
					return err
				}

				// Call setupEnv before PrepareCommand because
				// PrepareCommand uses os.ExpandEnv and expects the
				// setupEnv vars.
				setupEnv(md.Name, plug.Dir, plugdirs, home)
				main, argv := plug.PrepareCommand(u)

				prog := exec.Command(main, argv...)
				prog.Env = os.Environ()
				prog.Stdout = out
				prog.Stderr = os.Stderr
				if err := prog.Run(); err != nil {
					if eerr, ok := err.(*exec.ExitError); ok {
						os.Stderr.Write(eerr.Stderr)
						return fmt.Errorf("plugin %q exited with error", md.Name)
					}
					return err
				}
				return nil
			},
			// This passes all the flags to the subcommand.
			DisableFlagParsing: true,
		}

		if md.UseTunnel {
			c.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
				// Parse the parent flag, but not the local flags.
				k, _ := manuallyProcessArgs(args)
				if err := c.Parent().ParseFlags(k); err != nil {
					return err
				}
				return setupConnection(cmd, args)
			}
		}

		// TODO: Make sure a command with this name does not already exist.
		baseCmd.AddCommand(c)
	}
}

// manuallyProcessArgs processes an arg array, removing special args.
//
// Returns two sets of args: known and unknown (in that order)
func manuallyProcessArgs(args []string) ([]string, []string) {
	known := []string{}
	unknown := []string{}
	kvargs := []string{"--host", "--kube-context", "--home"}
	knownArg := func(a string) bool {
		for _, pre := range kvargs {
			if strings.HasPrefix(a, pre+"=") {
				return true
			}
		}
		return false
	}
	for i := 0; i < len(args); i++ {
		switch a := args[i]; a {
		case "--debug":
			known = append(known, a)
		case "--host", "--kube-context", "--home":
			known = append(known, a, args[i+1])
			i++
		default:
			if knownArg(a) {
				known = append(known, a)
				continue
			}
			unknown = append(unknown, a)
		}
	}
	return known, unknown
}

// findPlugins returns a list of YAML files that describe plugins.
func findPlugins(plugdirs string) ([]*plugin.Plugin, error) {
	found := []*plugin.Plugin{}
	// Let's get all UNIXy and allow path separators
	for _, p := range filepath.SplitList(plugdirs) {
		matches, err := plugin.LoadAll(p)
		if err != nil {
			return matches, err
		}
		found = append(found, matches...)
	}
	return found, nil
}

// setupEnv prepares os.Env for plugins. It operates on os.Env because
// the plugin subsystem itself needs access to the environment variables
// created here.
func setupEnv(shortname, base, plugdirs string, home helmpath.Home) {
	// Set extra env vars:
	for key, val := range map[string]string{
		"HELM_PLUGIN_NAME": shortname,
		"HELM_PLUGIN_DIR":  base,
		"HELM_BIN":         os.Args[0],

		// Set vars that may not have been set, and save client the
		// trouble of re-parsing.
		pluginEnvVar: plugdirs,
		homeEnvVar:   home.String(),

		// Set vars that convey common information.
		"HELM_PATH_REPOSITORY":       home.Repository(),
		"HELM_PATH_REPOSITORY_FILE":  home.RepositoryFile(),
		"HELM_PATH_CACHE":            home.Cache(),
		"HELM_PATH_LOCAL_REPOSITORY": home.LocalRepository(),
		//"HELM_PATH_STARTER":          home.Starter(),

		"TILLER_HOST": tillerHost,
	} {
		os.Setenv(key, val)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"errors"
	"fmt"
	"io"
	"os"
	"strings"

	platform "github.com/influxdata/influxdb"
	"github.com/influxdata/influxdb/http"
	"github.com/influxdata/influxdb/kit/signals"
	"github.com/influxdata/influxdb/models"
	"github.com/influxdata/influxdb/write"
	"github.com/spf13/cobra"
)

const (
	inputFormatCsv          = "csv"
	inputFormatLineProtocol = "lp"
)

var writeFlags struct {
	OrgID     string
	Org       string
	BucketID  string
	Bucket    string
	Precision string
	Format    string
	File      string
}

func cmdWrite(f *globalFlags, opt genericCLIOpts) *cobra.Command {
	cmd := opt.newCmd("write line protocol or csv file", fluxWriteF, true)
	cmd.Args = cobra.MaximumNArgs(1)
	cmd.Short = "Write points to InfluxDB"
	cmd.Long = `Write line protocol or CSV data to InfluxDB.`

	opts := flagOpts{
		{
			DestP:      &writeFlags.OrgID,
			Flag:       "org-id",
			Desc:       "The ID of the organization that owns the bucket",
			Persistent: true,
		},
		{
			DestP:      &writeFlags.Org,
			Flag:       "org",
			Short:      'o',
			Desc:       "The name of the organization that owns the bucket",
			Persistent: true,
		},
		{
			DestP:      &writeFlags.BucketID,
			Flag:       "bucket-id",
			Desc:       "The ID of destination bucket",
			Persistent: true,
		},
		{
			DestP:      &writeFlags.Bucket,
			Flag:       "bucket",
			Short:      'b',
			EnvVar:     "BUCKET_NAME",
			Desc:       "The name of destination bucket",
			Persistent: true,
		},
		{
			DestP:      &writeFlags.Precision,
			Flag:       "precision",
			Short:      'p',
			Default:    "ns",
			Desc:       "Precision of the timestamps of the lines",
			Persistent: true,
		},
	}
	opts.mustRegister(cmd)
	cmd.Flags().StringVar(&writeFlags.Format, "format", "", "Input format, either lp (Line Protocol) or csv (Comma Separated Values). Defaults to lp unless '.csv' extension")
	cmd.Flags().StringVarP(&writeFlags.File, "file", "f", "", "The path to the file to import")

	return cmd
}

func fluxWriteF(cmd *cobra.Command, args []string) error {
	ctx := context.Background()

	var bucketID, orgID platform.ID
	// validate flags unless writing to stdout
	if flags.Host != "-" {
		if writeFlags.Org != "" && writeFlags.OrgID != "" {
			return fmt.Errorf("please specify one of org or org-id")
		}

		if writeFlags.Bucket != "" && writeFlags.BucketID != "" {
			return fmt.Errorf("please specify one of bucket or bucket-id")
		}

		if !models.ValidPrecision(writeFlags.Precision) {
			return fmt.Errorf("invalid precision")
		}

		bs, err := newBucketService()
		if err != nil {
			return err
		}

		var filter platform.BucketFilter
		if writeFlags.BucketID != "" {
			filter.ID, err = platform.IDFromString(writeFlags.BucketID)
			if err != nil {
				return fmt.Errorf("failed to decode bucket-id: %v", err)
			}
		}
		if writeFlags.Bucket != "" {
			filter.Name = &writeFlags.Bucket
		}

		if writeFlags.OrgID != "" {
			filter.OrganizationID, err = platform.IDFromString(writeFlags.OrgID)
			if err != nil {
				return fmt.Errorf("failed to decode org-id id: %v", err)
			}
		}
		if writeFlags.Org != "" {
			filter.Org = &writeFlags.Org
		}

		buckets, n, err := bs.FindBuckets(ctx, filter)
		if err != nil {
			return fmt.Errorf("failed to retrieve buckets: %v", err)
		}

		if n == 0 {
			if writeFlags.Bucket != "" {
				return fmt.Errorf("bucket %q was not found", writeFlags.Bucket)
			}

			if writeFlags.BucketID != "" {
				return fmt.Errorf("bucket with id %q does not exist", writeFlags.BucketID)
			}
		}

		bucketID, orgID = buckets[0].ID, buckets[0].OrgID
	}

	var r io.Reader
	if len(args) == 0 {
		if len(writeFlags.File) == 0 {
			return errors.New("requires at least one argument or a --file option")
		}
	} else if args[0][0] == '@' {
		// backward compatibility
		writeFlags.File = args[0][1:]
	}

	if len(writeFlags.File) > 0 {
		f, err := os.Open(writeFlags.File)
		if err != nil {
			return fmt.Errorf("failed to open %q: %v", writeFlags.File, err)
		}
		defer f.Close()
		r = f
		if len(writeFlags.Format) == 0 && strings.HasSuffix(writeFlags.File, ".csv") {
			writeFlags.Format = inputFormatCsv
		}
	} else if args[0] == "-" {
		r = os.Stdin
	} else {
		r = strings.NewReader(args[0])
	}
	// validate input format
	if len(writeFlags.Format) > 0 && writeFlags.Format != inputFormatLineProtocol && writeFlags.Format != inputFormatCsv {
		return fmt.Errorf("unsupported input format: %s", writeFlags.Format)
	}

	if writeFlags.Format == inputFormatCsv {
		r = write.CsvToProtocolLines(r)
	}

	if flags.Host == "-" {
		// write lines to tdout
		_, err := io.Copy(os.Stdout, r)
		if err != nil {
			return fmt.Errorf("failed: %v", err)
		}
	} else {
		s := write.Batcher{
			Service: &http.WriteService{
				Addr:               flags.Host,
				Token:              flags.Token,
				Precision:          writeFlags.Precision,
				InsecureSkipVerify: flags.skipVerify,
			},
		}

		ctx = signals.WithStandardSignals(ctx)
		if err := s.Write(ctx, orgID, bucketID, r); err != nil && err != context.Canceled {
			return fmt.Errorf("failed to write data: %v", err)
		}
	}

	return nil
}
<commit_msg>chore(cmd/write): use stdin when no file or argument is supplied<commit_after>package main

import (
	"context"
	"fmt"
	"io"
	"os"
	"strings"

	platform "github.com/influxdata/influxdb"
	"github.com/influxdata/influxdb/http"
	"github.com/influxdata/influxdb/kit/signals"
	"github.com/influxdata/influxdb/models"
	"github.com/influxdata/influxdb/write"
	"github.com/spf13/cobra"
)

const (
	inputFormatCsv          = "csv"
	inputFormatLineProtocol = "lp"
)

var writeFlags struct {
	OrgID     string
	Org       string
	BucketID  string
	Bucket    string
	Precision string
	Format    string
	File      string
}

func cmdWrite(f *globalFlags, opt genericCLIOpts) *cobra.Command {
	cmd := opt.newCmd("write", fluxWriteF, true)
	cmd.Args = cobra.MaximumNArgs(1)
	cmd.Short = "Write points to InfluxDB"
	cmd.Long = `Write data to InfluxDB via stdin, or add an entire file specified with the -f flag`

	opts := flagOpts{
		{
			DestP:      &writeFlags.OrgID,
			Flag:       "org-id",
			Desc:       "The ID of the organization that owns the bucket",
			Persistent: true,
		},
		{
			DestP:      &writeFlags.Org,
			Flag:       "org",
			Short:      'o',
			Desc:       "The name of the organization that owns the bucket",
			Persistent: true,
		},
		{
			DestP:      &writeFlags.BucketID,
			Flag:       "bucket-id",
			Desc:       "The ID of destination bucket",
			Persistent: true,
		},
		{
			DestP:      &writeFlags.Bucket,
			Flag:       "bucket",
			Short:      'b',
			EnvVar:     "BUCKET_NAME",
			Desc:       "The name of destination bucket",
			Persistent: true,
		},
		{
			DestP:      &writeFlags.Precision,
			Flag:       "precision",
			Short:      'p',
			Default:    "ns",
			Desc:       "Precision of the timestamps of the lines",
			Persistent: true,
		},
	}
	opts.mustRegister(cmd)
	cmd.Flags().StringVar(&writeFlags.Format, "format", "", "Input format, either lp (Line Protocol) or csv (Comma Separated Values). Defaults to lp unless '.csv' extension")
	cmd.Flags().StringVarP(&writeFlags.File, "file", "f", "", "The path to the file to import")

	return cmd
}

func fluxWriteF(cmd *cobra.Command, args []string) error {
	ctx := context.Background()

	var bucketID, orgID platform.ID
	// validate flags unless writing to stdout
	if flags.Host != "-" {
		if writeFlags.Org != "" && writeFlags.OrgID != "" {
			return fmt.Errorf("please specify one of org or org-id")
		}

		if writeFlags.Bucket != "" && writeFlags.BucketID != "" {
			return fmt.Errorf("please specify one of bucket or bucket-id")
		}

		if !models.ValidPrecision(writeFlags.Precision) {
			return fmt.Errorf("invalid precision")
		}

		bs, err := newBucketService()
		if err != nil {
			return err
		}

		var filter platform.BucketFilter
		if writeFlags.BucketID != "" {
			filter.ID, err = platform.IDFromString(writeFlags.BucketID)
			if err != nil {
				return fmt.Errorf("failed to decode bucket-id: %v", err)
			}
		}
		if writeFlags.Bucket != "" {
			filter.Name = &writeFlags.Bucket
		}

		if writeFlags.OrgID != "" {
			filter.OrganizationID, err = platform.IDFromString(writeFlags.OrgID)
			if err != nil {
				return fmt.Errorf("failed to decode org-id id: %v", err)
			}
		}
		if writeFlags.Org != "" {
			filter.Org = &writeFlags.Org
		}

		buckets, n, err := bs.FindBuckets(ctx, filter)
		if err != nil {
			return fmt.Errorf("failed to retrieve buckets: %v", err)
		}

		if n == 0 {
			if writeFlags.Bucket != "" {
				return fmt.Errorf("bucket %q was not found", writeFlags.Bucket)
			}

			if writeFlags.BucketID != "" {
				return fmt.Errorf("bucket with id %q does not exist", writeFlags.BucketID)
			}
		}

		bucketID, orgID = buckets[0].ID, buckets[0].OrgID
	}

	var r io.Reader
	if len(args) > 0 && args[0][0] == '@' {
		// backward compatibility
		writeFlags.File = args[0][1:]
	}

	if len(writeFlags.File) > 0 {
		f, err := os.Open(writeFlags.File)
		if err != nil {
			return fmt.Errorf("failed to open %q: %v", writeFlags.File, err)
		}
		defer f.Close()
		r = f
		if len(writeFlags.Format) == 0 && strings.HasSuffix(writeFlags.File, ".csv") {
			writeFlags.Format = inputFormatCsv
		}
	} else if len(args) == 0 || args[0] == "-" {
		r = os.Stdin
	} else {
		r = strings.NewReader(args[0])
	}
	// validate input format
	if len(writeFlags.Format) > 0 && writeFlags.Format != inputFormatLineProtocol && writeFlags.Format != inputFormatCsv {
		return fmt.Errorf("unsupported input format: %s", writeFlags.Format)
	}

	if writeFlags.Format == inputFormatCsv {
		r = write.CsvToProtocolLines(r)
	}

	if flags.Host == "-" {
		// write lines to tdout
		_, err := io.Copy(os.Stdout, r)
		if err != nil {
			return fmt.Errorf("failed: %v", err)
		}
	} else {
		s := write.Batcher{
			Service: &http.WriteService{
				Addr:               flags.Host,
				Token:              flags.Token,
				Precision:          writeFlags.Precision,
				InsecureSkipVerify: flags.skipVerify,
			},
		}

		ctx = signals.WithStandardSignals(ctx)
		if err := s.Write(ctx, orgID, bucketID, r); err != nil && err != context.Canceled {
			return fmt.Errorf("failed to write data: %v", err)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package cmd

import (
	"context"
	"fmt"
	"strings"

	"github.com/urfave/cli"

	"github.com/arigatomachine/cli/api"
	"github.com/arigatomachine/cli/config"
	"github.com/arigatomachine/cli/identity"
)

const orgInviteFailed = "Could not send invitation to org, please try again."

func invitesSend(ctx *cli.Context) error {
	usage := usageString(ctx)

	args := ctx.Args()
	if len(args) < 1 || args[0] == "" {
		text := "Missing email\n\n"
		text += usage
		return cli.NewExitError(text, -1)
	}
	if len(args) > 1 {
		text := "Too many arguments\n\n"
		text += usage
		return cli.NewExitError(text, -1)
	}
	email := args[0]

	cfg, err := config.LoadConfig()
	if err != nil {
		return err
	}

	client := api.NewClient(cfg)

	org, err := client.Orgs.GetByName(context.Background(), ctx.String("org"))
	if err != nil {
		return cli.NewExitError(orgInviteFailed, -1)
	}
	if org == nil {
		return cli.NewExitError("Org not found", -1)
	}

	// Identify the user attempting the command
	user, err := client.Users.Self(context.Background())
	if err != nil {
		return cli.NewExitError(orgInviteFailed, -1)
	}

	// Retrieve teams for our target org
	teams, err := client.Teams.GetByOrg(context.Background(), org.ID)
	if err != nil {
		return cli.NewExitError(orgInviteFailed, -1)
	}

	matchTeams := ctx.StringSlice("team")

	// Verify all team names supplied exist for this org
	teamIDs := make([]identity.ID, len(matchTeams))
	var missingTeams []string

TeamSearch:
	for i, teamName := range matchTeams {
		for _, team := range teams {
			if team.Body.Name == teamName {
				teamIDs[i] = *team.ID
				continue TeamSearch
			}
		}
		missingTeams = append(missingTeams, teamName)
	}

	// One of the supplied teams is not known to this org
	if len(missingTeams) > 0 {
		missingTeamNames := strings.Join(missingTeams, ", ")
		return cli.NewExitError("Unknown team(s): "+missingTeamNames, -1)
	}
	if len(teamIDs) < 1 {
		return cli.NewExitError(orgInviteFailed, -1)
	}

	err = client.Invites.Send(context.Background(), email, *org.ID, *user.ID, teamIDs)
	if err != nil {
		if strings.Contains(err.Error(), "resource exists") {
			return cli.NewExitError(email+" has already been invited to the "+org.Body.Name+" org", -1)
		}
		return cli.NewExitError(orgInviteFailed, -1)
	}

	fmt.Println("Invitation to join the " + org.Body.Name + " organization has been sent to " + email + ".")
	fmt.Println("\nThey will be added to the following teams once their invite has been confirmed:")
	fmt.Println("\n\t" + strings.Join(matchTeams, "\n\t"))
	fmt.Println("\nThey will receive an e-mail with instructions.")

	return nil
}
<commit_msg>Always add invitees to the member team<commit_after>package cmd

import (
	"context"
	"fmt"
	"strings"

	"github.com/urfave/cli"

	"github.com/arigatomachine/cli/api"
	"github.com/arigatomachine/cli/config"
	"github.com/arigatomachine/cli/identity"
)

const orgInviteFailed = "Could not send invitation to org, please try again."

func invitesSend(ctx *cli.Context) error {
	usage := usageString(ctx)

	args := ctx.Args()
	if len(args) < 1 || args[0] == "" {
		text := "Missing email\n\n"
		text += usage
		return cli.NewExitError(text, -1)
	}
	if len(args) > 1 {
		text := "Too many arguments\n\n"
		text += usage
		return cli.NewExitError(text, -1)
	}
	email := args[0]

	cfg, err := config.LoadConfig()
	if err != nil {
		return err
	}

	client := api.NewClient(cfg)

	org, err := client.Orgs.GetByName(context.Background(), ctx.String("org"))
	if err != nil {
		return cli.NewExitError(orgInviteFailed, -1)
	}
	if org == nil {
		return cli.NewExitError("Org not found", -1)
	}

	// Identify the user attempting the command
	user, err := client.Users.Self(context.Background())
	if err != nil {
		return cli.NewExitError(orgInviteFailed, -1)
	}

	// Retrieve teams for our target org
	teams, err := client.Teams.GetByOrg(context.Background(), org.ID)
	if err != nil {
		return cli.NewExitError(orgInviteFailed, -1)
	}

	matchTeams := ctx.StringSlice("team")

	// ensure that even with custom teams, users are always invited to the
	// member team
	const memberTeam = "member"
	memberFound := false
	for _, team := range matchTeams {
		if team == memberTeam {
			memberFound = true
			break
		}
	}
	if !memberFound {
		matchTeams = append(matchTeams, memberTeam)
	}

	// Verify all team names supplied exist for this org
	teamIDs := make([]identity.ID, len(matchTeams))
	var missingTeams []string

TeamSearch:
	for i, teamName := range matchTeams {
		for _, team := range teams {
			if team.Body.Name == teamName {
				teamIDs[i] = *team.ID
				continue TeamSearch
			}
		}
		missingTeams = append(missingTeams, teamName)
	}

	// One of the supplied teams is not known to this org
	if len(missingTeams) > 0 {
		missingTeamNames := strings.Join(missingTeams, ", ")
		return cli.NewExitError("Unknown team(s): "+missingTeamNames, -1)
	}
	if len(teamIDs) < 1 {
		return cli.NewExitError(orgInviteFailed, -1)
	}

	err = client.Invites.Send(context.Background(), email, *org.ID, *user.ID, teamIDs)
	if err != nil {
		if strings.Contains(err.Error(), "resource exists") {
			return cli.NewExitError(email+" has already been invited to the "+org.Body.Name+" org", -1)
		}
		return cli.NewExitError(orgInviteFailed, -1)
	}

	fmt.Println("Invitation to join the " + org.Body.Name + " organization has been sent to " + email + ".")
	fmt.Println("\nThey will be added to the following teams once their invite has been confirmed:")
	fmt.Println("\n\t" + strings.Join(matchTeams, "\n\t"))
	fmt.Println("\nThey will receive an e-mail with instructions.")

	return nil
}
<|endoftext|>


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

package html

import (
	"bytes"
	"strings"
	"unicode/utf8"
)

type writer interface {
	WriteString(string) (int, error)
}

// These replacements permit compatibility with old numeric entities that
// assumed Windows-1252 encoding.
// http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#consume-a-character-reference
var replacementTable = [...]rune{
	'\u20AC', // First entry is what 0x80 should be replaced with.
	'\u0081',
	'\u201A',
	'\u0192',
	'\u201E',
	'\u2026',
	'\u2020',
	'\u2021',
	'\u02C6',
	'\u2030',
	'\u0160',
	'\u2039',
	'\u0152',
	'\u008D',
	'\u017D',
	'\u008F',
	'\u0090',
	'\u2018',
	'\u2019',
	'\u201C',
	'\u201D',
	'\u2022',
	'\u2013',
	'\u2014',
	'\u02DC',
	'\u2122',
	'\u0161',
	'\u203A',
	'\u0153',
	'\u009D',
	'\u017E',
	'\u0178', // Last entry is 0x9F.
	// 0x00->'\uFFFD' is handled programmatically.
	// 0x0D->'\u000D' is a no-op.
}

// unescapeEntity reads an entity like "<" from b[src:] and writes the
// corresponding "<" to b[dst:], returning the incremented dst and src cursors.
// Precondition: b[src] == '&' && dst <= src.
// attribute should be true if parsing an attribute value.
func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) {
	// http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#consume-a-character-reference

	// i starts at 1 because we already know that s[0] == '&'.
	i, s := 1, b[src:]

	if len(s) <= 1 {
		b[dst] = b[src]
		return dst + 1, src + 1
	}

	if s[i] == '#' {
		if len(s) <= 3 { // We need to have at least "&#.".
			b[dst] = b[src]
			return dst + 1, src + 1
		}
		i++
		c := s[i]
		hex := false
		if c == 'x' || c == 'X' {
			hex = true
			i++
		}

		x := '\x00'
		for i < len(s) {
			c = s[i]
			i++
			if hex {
				if '0' <= c && c <= '9' {
					x = 16*x + rune(c) - '0'
					continue
				} else if 'a' <= c && c <= 'f' {
					x = 16*x + rune(c) - 'a' + 10
					continue
				} else if 'A' <= c && c <= 'F' {
					x = 16*x + rune(c) - 'A' + 10
					continue
				}
			} else if '0' <= c && c <= '9' {
				x = 10*x + rune(c) - '0'
				continue
			}
			if c != ';' {
				i--
			}
			break
		}

		if i <= 3 { // No characters matched.
			b[dst] = b[src]
			return dst + 1, src + 1
		}

		if 0x80 <= x && x <= 0x9F {
			// Replace characters from Windows-1252 with UTF-8 equivalents.
			x = replacementTable[x-0x80]
		} else if x == 0 || (0xD800 <= x && x <= 0xDFFF) || x > 0x10FFFF {
			// Replace invalid characters with the replacement character.
			x = '\uFFFD'
		}

		return dst + utf8.EncodeRune(b[dst:], x), src + i
	}

	// Consume the maximum number of characters possible, with the
	// consumed characters matching one of the named references.

	for i < len(s) {
		c := s[i]
		i++
		// Lower-cased characters are more common in entities, so we check for them first.
		if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' {
			continue
		}
		if c != ';' {
			i--
		}
		break
	}

	entityName := string(s[1:i])
	if entityName == "" {
		// No-op.
	} else if attribute && entityName[len(entityName)-1] != ';' && len(s) > i && s[i] == '=' {
		// No-op.
	} else if x := entity[entityName]; x != 0 {
		return dst + utf8.EncodeRune(b[dst:], x), src + i
	} else if x := entity2[entityName]; x[0] != 0 {
		dst1 := dst + utf8.EncodeRune(b[dst:], x[0])
		return dst1 + utf8.EncodeRune(b[dst1:], x[1]), src + i
	} else if !attribute {
		maxLen := len(entityName) - 1
		if maxLen > longestEntityWithoutSemicolon {
			maxLen = longestEntityWithoutSemicolon
		}
		for j := maxLen; j > 1; j-- {
			if x := entity[entityName[:j]]; x != 0 {
				return dst + utf8.EncodeRune(b[dst:], x), src + j + 1
			}
		}
	}

	dst1, src1 = dst+i, src+i
	copy(b[dst:dst1], b[src:src1])
	return dst1, src1
}

// unescape unescapes b's entities in-place, so that "a<b" becomes "a<b".
func unescape(b []byte) []byte {
	for i, c := range b {
		if c == '&' {
			dst, src := unescapeEntity(b, i, i, false)
			for src < len(b) {
				c := b[src]
				if c == '&' {
					dst, src = unescapeEntity(b, dst, src, false)
				} else {
					b[dst] = c
					dst, src = dst+1, src+1
				}
			}
			return b[0:dst]
		}
	}
	return b
}

// lower lower-cases the A-Z bytes in b in-place, so that "aBc" becomes "abc".
func lower(b []byte) []byte {
	for i, c := range b {
		if 'A' <= c && c <= 'Z' {
			b[i] = c + 'a' - 'A'
		}
	}
	return b
}

const escapedChars = `&'<>"`

func escape(w writer, s string) error {
	i := strings.IndexAny(s, escapedChars)
	for i != -1 {
		if _, err := w.WriteString(s[:i]); err != nil {
			return err
		}
		var esc string
		switch s[i] {
		case '&':
			esc = "&"
		case '\'':
			esc = "'"
		case '<':
			esc = "<"
		case '>':
			esc = ">"
		case '"':
			esc = """
		default:
			panic("unrecognized escape character")
		}
		s = s[i+1:]
		if _, err := w.WriteString(esc); err != nil {
			return err
		}
		i = strings.IndexAny(s, escapedChars)
	}
	_, err := w.WriteString(s)
	return err
}

// EscapeString escapes special characters like "<" to become "<". It
// escapes only five such characters: amp, apos, lt, gt and quot.
// UnescapeString(EscapeString(s)) == s always holds, but the converse isn't
// always true.
func EscapeString(s string) string {
	if strings.IndexAny(s, escapedChars) == -1 {
		return s
	}
	buf := bytes.NewBuffer(nil)
	escape(buf, s)
	return buf.String()
}

// UnescapeString unescapes entities like "<" to become "<". It unescapes a
// larger range of entities than EscapeString escapes. For example, "á"
// unescapes to "á", as does "á" and "&xE1;".
// UnescapeString(EscapeString(s)) == s always holds, but the converse isn't
// always true.
func UnescapeString(s string) string {
	for _, c := range s {
		if c == '&' {
			return string(unescape([]byte(s)))
		}
	}
	return s
}
<commit_msg>html: add package doc.<commit_after>// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package html provides functions for escaping and unescaping HTML text.
package html

import (
	"bytes"
	"strings"
	"unicode/utf8"
)

type writer interface {
	WriteString(string) (int, error)
}

// These replacements permit compatibility with old numeric entities that
// assumed Windows-1252 encoding.
// http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#consume-a-character-reference
var replacementTable = [...]rune{
	'\u20AC', // First entry is what 0x80 should be replaced with.
	'\u0081',
	'\u201A',
	'\u0192',
	'\u201E',
	'\u2026',
	'\u2020',
	'\u2021',
	'\u02C6',
	'\u2030',
	'\u0160',
	'\u2039',
	'\u0152',
	'\u008D',
	'\u017D',
	'\u008F',
	'\u0090',
	'\u2018',
	'\u2019',
	'\u201C',
	'\u201D',
	'\u2022',
	'\u2013',
	'\u2014',
	'\u02DC',
	'\u2122',
	'\u0161',
	'\u203A',
	'\u0153',
	'\u009D',
	'\u017E',
	'\u0178', // Last entry is 0x9F.
	// 0x00->'\uFFFD' is handled programmatically.
	// 0x0D->'\u000D' is a no-op.
}

// unescapeEntity reads an entity like "<" from b[src:] and writes the
// corresponding "<" to b[dst:], returning the incremented dst and src cursors.
// Precondition: b[src] == '&' && dst <= src.
// attribute should be true if parsing an attribute value.
func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) {
	// http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#consume-a-character-reference

	// i starts at 1 because we already know that s[0] == '&'.
	i, s := 1, b[src:]

	if len(s) <= 1 {
		b[dst] = b[src]
		return dst + 1, src + 1
	}

	if s[i] == '#' {
		if len(s) <= 3 { // We need to have at least "&#.".
			b[dst] = b[src]
			return dst + 1, src + 1
		}
		i++
		c := s[i]
		hex := false
		if c == 'x' || c == 'X' {
			hex = true
			i++
		}

		x := '\x00'
		for i < len(s) {
			c = s[i]
			i++
			if hex {
				if '0' <= c && c <= '9' {
					x = 16*x + rune(c) - '0'
					continue
				} else if 'a' <= c && c <= 'f' {
					x = 16*x + rune(c) - 'a' + 10
					continue
				} else if 'A' <= c && c <= 'F' {
					x = 16*x + rune(c) - 'A' + 10
					continue
				}
			} else if '0' <= c && c <= '9' {
				x = 10*x + rune(c) - '0'
				continue
			}
			if c != ';' {
				i--
			}
			break
		}

		if i <= 3 { // No characters matched.
			b[dst] = b[src]
			return dst + 1, src + 1
		}

		if 0x80 <= x && x <= 0x9F {
			// Replace characters from Windows-1252 with UTF-8 equivalents.
			x = replacementTable[x-0x80]
		} else if x == 0 || (0xD800 <= x && x <= 0xDFFF) || x > 0x10FFFF {
			// Replace invalid characters with the replacement character.
			x = '\uFFFD'
		}

		return dst + utf8.EncodeRune(b[dst:], x), src + i
	}

	// Consume the maximum number of characters possible, with the
	// consumed characters matching one of the named references.

	for i < len(s) {
		c := s[i]
		i++
		// Lower-cased characters are more common in entities, so we check for them first.
		if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' {
			continue
		}
		if c != ';' {
			i--
		}
		break
	}

	entityName := string(s[1:i])
	if entityName == "" {
		// No-op.
	} else if attribute && entityName[len(entityName)-1] != ';' && len(s) > i && s[i] == '=' {
		// No-op.
	} else if x := entity[entityName]; x != 0 {
		return dst + utf8.EncodeRune(b[dst:], x), src + i
	} else if x := entity2[entityName]; x[0] != 0 {
		dst1 := dst + utf8.EncodeRune(b[dst:], x[0])
		return dst1 + utf8.EncodeRune(b[dst1:], x[1]), src + i
	} else if !attribute {
		maxLen := len(entityName) - 1
		if maxLen > longestEntityWithoutSemicolon {
			maxLen = longestEntityWithoutSemicolon
		}
		for j := maxLen; j > 1; j-- {
			if x := entity[entityName[:j]]; x != 0 {
				return dst + utf8.EncodeRune(b[dst:], x), src + j + 1
			}
		}
	}

	dst1, src1 = dst+i, src+i
	copy(b[dst:dst1], b[src:src1])
	return dst1, src1
}

// unescape unescapes b's entities in-place, so that "a<b" becomes "a<b".
func unescape(b []byte) []byte {
	for i, c := range b {
		if c == '&' {
			dst, src := unescapeEntity(b, i, i, false)
			for src < len(b) {
				c := b[src]
				if c == '&' {
					dst, src = unescapeEntity(b, dst, src, false)
				} else {
					b[dst] = c
					dst, src = dst+1, src+1
				}
			}
			return b[0:dst]
		}
	}
	return b
}

// lower lower-cases the A-Z bytes in b in-place, so that "aBc" becomes "abc".
func lower(b []byte) []byte {
	for i, c := range b {
		if 'A' <= c && c <= 'Z' {
			b[i] = c + 'a' - 'A'
		}
	}
	return b
}

const escapedChars = `&'<>"`

func escape(w writer, s string) error {
	i := strings.IndexAny(s, escapedChars)
	for i != -1 {
		if _, err := w.WriteString(s[:i]); err != nil {
			return err
		}
		var esc string
		switch s[i] {
		case '&':
			esc = "&"
		case '\'':
			esc = "'"
		case '<':
			esc = "<"
		case '>':
			esc = ">"
		case '"':
			esc = """
		default:
			panic("unrecognized escape character")
		}
		s = s[i+1:]
		if _, err := w.WriteString(esc); err != nil {
			return err
		}
		i = strings.IndexAny(s, escapedChars)
	}
	_, err := w.WriteString(s)
	return err
}

// EscapeString escapes special characters like "<" to become "<". It
// escapes only five such characters: amp, apos, lt, gt and quot.
// UnescapeString(EscapeString(s)) == s always holds, but the converse isn't
// always true.
func EscapeString(s string) string {
	if strings.IndexAny(s, escapedChars) == -1 {
		return s
	}
	buf := bytes.NewBuffer(nil)
	escape(buf, s)
	return buf.String()
}

// UnescapeString unescapes entities like "<" to become "<". It unescapes a
// larger range of entities than EscapeString escapes. For example, "á"
// unescapes to "á", as does "á" and "&xE1;".
// UnescapeString(EscapeString(s)) == s always holds, but the converse isn't
// always true.
func UnescapeString(s string) string {
	for _, c := range s {
		if c == '&' {
			return string(unescape([]byte(s)))
		}
	}
	return s
}
<|endoftext|>