<commit_before>// Copyright 2018 The Hugo 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.

// +build extended

package scss

import (
	"fmt"
	"io"
	"path"
	"strings"

	"github.com/bep/go-tocss/scss"
	"github.com/bep/go-tocss/scss/libsass"
	"github.com/bep/go-tocss/tocss"
	"github.com/gohugoio/hugo/helpers"
	"github.com/gohugoio/hugo/media"
	"github.com/gohugoio/hugo/resource"
)

// Used in tests. This feature requires Hugo to be built with the extended tag.
func Supports() bool {
	return true
}

func (t *toCSSTransformation) Transform(ctx *resource.ResourceTransformationCtx) error {
	ctx.OutMediaType = media.CSSType

	var outName string
	if t.options.from.TargetPath != "" {
		ctx.OutPath = t.options.from.TargetPath
	} else {
		ctx.ReplaceOutPathExtension(".css")
	}

	outName = path.Base(ctx.OutPath)

	options := t.options

	// We may allow the end user to add IncludePaths later, if we find a use
	// case for that.
	options.to.IncludePaths = t.c.sfs.RealDirs(path.Dir(ctx.SourcePath))

	if ctx.InMediaType.SubType == media.SASSType.SubType {
		options.to.SassSyntax = true
	}

	if options.from.EnableSourceMap {

		options.to.SourceMapFilename = outName + ".map"
		options.to.SourceMapRoot = t.c.rs.WorkingDir

		// Setting this to the relative input filename will get the source map
		// more correct for the main entry path (main.scss typically), but
		// it will mess up the import mappings. As a workaround, we do a replacement
		// in the source map itself (see below).
		//options.InputPath = inputPath
		options.to.OutputPath = outName
		options.to.SourceMapContents = true
		options.to.OmitSourceMapURL = false
		options.to.EnableEmbeddedSourceMap = false
	}

	res, err := t.c.toCSS(options.to, ctx.To, ctx.From)
	if err != nil {
		return err
	}

	if options.from.EnableSourceMap && res.SourceMapContent != "" {
		sourcePath := t.c.sfs.RealFilename(ctx.SourcePath)

		if strings.HasPrefix(sourcePath, t.c.rs.WorkingDir) {
			sourcePath = strings.TrimPrefix(sourcePath, t.c.rs.WorkingDir+helpers.FilePathSeparator)
		}

		// This is a workaround for what looks like a bug in Libsass. But
		// getting this resolution correct in tools like Chrome Workspaces
		// is important enough to go this extra mile.
		mapContent := strings.Replace(res.SourceMapContent, `stdin",`, fmt.Sprintf("%s\",", sourcePath), 1)

		return ctx.PublishSourceMap(mapContent)
	}
	return nil
}

func (c *Client) toCSS(options scss.Options, dst io.Writer, src io.Reader) (tocss.Result, error) {
	var res tocss.Result

	transpiler, err := libsass.New(options)
	if err != nil {
		return res, err
	}

	res, err = transpiler.Execute(dst, src)
	if err != nil {
		return res, fmt.Errorf("SCSS processing failed: %s", err)
	}

	return res, nil
}
<commit_msg>resource/scss: Fix source maps on Windows<commit_after>// Copyright 2018 The Hugo 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.

// +build extended

package scss

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

	"github.com/bep/go-tocss/scss"
	"github.com/bep/go-tocss/scss/libsass"
	"github.com/bep/go-tocss/tocss"
	"github.com/gohugoio/hugo/helpers"
	"github.com/gohugoio/hugo/media"
	"github.com/gohugoio/hugo/resource"
)

// Used in tests. This feature requires Hugo to be built with the extended tag.
func Supports() bool {
	return true
}

func (t *toCSSTransformation) Transform(ctx *resource.ResourceTransformationCtx) error {
	ctx.OutMediaType = media.CSSType

	var outName string
	if t.options.from.TargetPath != "" {
		ctx.OutPath = t.options.from.TargetPath
	} else {
		ctx.ReplaceOutPathExtension(".css")
	}

	outName = path.Base(ctx.OutPath)

	options := t.options

	// We may allow the end user to add IncludePaths later, if we find a use
	// case for that.
	options.to.IncludePaths = t.c.sfs.RealDirs(path.Dir(ctx.SourcePath))

	if ctx.InMediaType.SubType == media.SASSType.SubType {
		options.to.SassSyntax = true
	}

	if options.from.EnableSourceMap {

		options.to.SourceMapFilename = outName + ".map"
		options.to.SourceMapRoot = t.c.rs.WorkingDir

		// Setting this to the relative input filename will get the source map
		// more correct for the main entry path (main.scss typically), but
		// it will mess up the import mappings. As a workaround, we do a replacement
		// in the source map itself (see below).
		//options.InputPath = inputPath
		options.to.OutputPath = outName
		options.to.SourceMapContents = true
		options.to.OmitSourceMapURL = false
		options.to.EnableEmbeddedSourceMap = false
	}

	res, err := t.c.toCSS(options.to, ctx.To, ctx.From)
	if err != nil {
		return err
	}

	if options.from.EnableSourceMap && res.SourceMapContent != "" {
		sourcePath := t.c.sfs.RealFilename(ctx.SourcePath)

		if strings.HasPrefix(sourcePath, t.c.rs.WorkingDir) {
			sourcePath = strings.TrimPrefix(sourcePath, t.c.rs.WorkingDir+helpers.FilePathSeparator)
		}

		// This needs to be Unix-style slashes, even on Windows.
		// See https://github.com/gohugoio/hugo/issues/4968
		sourcePath = filepath.ToSlash(sourcePath)

		// This is a workaround for what looks like a bug in Libsass. But
		// getting this resolution correct in tools like Chrome Workspaces
		// is important enough to go this extra mile.
		mapContent := strings.Replace(res.SourceMapContent, `stdin",`, fmt.Sprintf("%s\",", sourcePath), 1)

		return ctx.PublishSourceMap(mapContent)
	}
	return nil
}

func (c *Client) toCSS(options scss.Options, dst io.Writer, src io.Reader) (tocss.Result, error) {
	var res tocss.Result

	transpiler, err := libsass.New(options)
	if err != nil {
		return res, err
	}

	res, err = transpiler.Execute(dst, src)
	if err != nil {
		return res, fmt.Errorf("SCSS processing failed: %s", err)
	}

	return res, nil
}
<|endoftext|>


<commit_before>package mpb

import (
	"container/heap"
	"context"
	"io"
	"io/ioutil"
	"log"
	"os"
	"sync"
	"time"

	"github.com/vbauerster/mpb/v4/cwriter"
)

const (
	// default RefreshRate
	prr = 120 * time.Millisecond
	// default width
	pwidth = 80
)

// Progress represents the container that renders Progress bars
type Progress struct {
	ctx          context.Context
	uwg          *sync.WaitGroup
	cwg          *sync.WaitGroup
	bwg          *sync.WaitGroup
	operateState chan func(*pState)
	done         chan struct{}
	forceRefresh chan time.Time
	once         sync.Once
	dlogger      *log.Logger
}

type pState struct {
	bHeap            priorityQueue
	heapUpdated      bool
	pMatrix          map[int][]chan int
	aMatrix          map[int][]chan int
	barShutdownQueue []*Bar
	barPopQueue      []*Bar

	// following are provided/overrided by user
	idCount          int
	width            int
	popCompleted     bool
	rr               time.Duration
	uwg              *sync.WaitGroup
	manualRefreshCh  <-chan time.Time
	shutdownNotifier chan struct{}
	parkedBars       map[*Bar]*Bar
	output           io.Writer
	debugOut         io.Writer
}

// New creates new Progress container instance. It's not possible to
// reuse instance after *Progress.Wait() method has been called.
func New(options ...ContainerOption) *Progress {
	return NewWithContext(context.Background(), options...)
}

// NewWithContext creates new Progress container instance with provided
// context. It's not possible to reuse instance after *Progress.Wait()
// method has been called.
func NewWithContext(ctx context.Context, options ...ContainerOption) *Progress {

	s := &pState{
		bHeap:      priorityQueue{},
		width:      pwidth,
		rr:         prr,
		parkedBars: make(map[*Bar]*Bar),
		output:     os.Stdout,
		debugOut:   ioutil.Discard,
	}

	for _, opt := range options {
		if opt != nil {
			opt(s)
		}
	}

	p := &Progress{
		ctx:          ctx,
		uwg:          s.uwg,
		cwg:          new(sync.WaitGroup),
		bwg:          new(sync.WaitGroup),
		operateState: make(chan func(*pState)),
		forceRefresh: make(chan time.Time),
		done:         make(chan struct{}),
		dlogger:      log.New(s.debugOut, "[mpb] ", log.Lshortfile),
	}
	p.cwg.Add(1)
	go p.serve(s, cwriter.New(s.output))
	return p
}

// AddBar creates a new progress bar and adds to the container.
func (p *Progress) AddBar(total int64, options ...BarOption) *Bar {
	return p.Add(total, newDefaultBarFiller(), options...)
}

// AddSpinner creates a new spinner bar and adds to the container.
func (p *Progress) AddSpinner(total int64, alignment SpinnerAlignment, options ...BarOption) *Bar {
	filler := &spinnerFiller{
		frames:    defaultSpinnerStyle,
		alignment: alignment,
	}
	return p.Add(total, filler, options...)
}

// Add creates a bar which renders itself by provided filler.
// Set total to 0, if you plan to update it later.
func (p *Progress) Add(total int64, filler Filler, options ...BarOption) *Bar {
	if filler == nil {
		filler = newDefaultBarFiller()
	}
	p.bwg.Add(1)
	result := make(chan *Bar)
	select {
	case p.operateState <- func(ps *pState) {
		bs := &bState{
			total:    total,
			filler:   filler,
			priority: ps.idCount,
			id:       ps.idCount,
			width:    ps.width,
			debugOut: ps.debugOut,
		}
		for _, opt := range options {
			if opt != nil {
				opt(bs)
			}
		}
		bar := newBar(p, bs)
		if bs.runningBar != nil {
			bs.runningBar.noPop = true
			ps.parkedBars[bs.runningBar] = bar
		} else {
			heap.Push(&ps.bHeap, bar)
			ps.heapUpdated = true
		}
		ps.idCount++
		result <- bar
	}:
		return <-result
	case <-p.done:
		p.bwg.Done()
		return nil
	}
}

func (p *Progress) dropBar(b *Bar) {
	select {
	case p.operateState <- func(s *pState) {
		if b.index < 0 {
			return
		}
		heap.Remove(&s.bHeap, b.index)
		s.heapUpdated = true
	}:
	case <-p.done:
	}
}

func (p *Progress) setBarPriority(b *Bar, priority int) {
	select {
	case p.operateState <- func(s *pState) {
		if b.index < 0 {
			return
		}
		b.priority = priority
		heap.Fix(&s.bHeap, b.index)
	}:
	case <-p.done:
	}
}

// UpdateBarPriority same as *Bar.SetPriority.
func (p *Progress) UpdateBarPriority(b *Bar, priority int) {
	p.setBarPriority(b, priority)
}

// BarCount returns bars count
func (p *Progress) BarCount() int {
	result := make(chan int, 1)
	select {
	case p.operateState <- func(s *pState) { result <- s.bHeap.Len() }:
		return <-result
	case <-p.done:
		return 0
	}
}

// Wait waits far all bars to complete and finally shutdowns container.
// After this method has been called, there is no way to reuse *Progress
// instance.
func (p *Progress) Wait() {
	if p.uwg != nil {
		// wait for user wg
		p.uwg.Wait()
	}

	// wait for bars to quit, if any
	p.bwg.Wait()

	p.once.Do(p.shutdown)

	// wait for container to quit
	p.cwg.Wait()
}

func (p *Progress) shutdown() {
	close(p.done)
}

func (p *Progress) serve(s *pState, cw *cwriter.Writer) {
	defer p.cwg.Done()

	manualOrTickCh, cleanUp := s.manualOrTick()
	defer cleanUp()

	refreshCh := fanInRefreshSrc(p.done, p.forceRefresh, manualOrTickCh)

	for {
		select {
		case op := <-p.operateState:
			op(s)
		case _, ok := <-refreshCh:
			if !ok {
				if s.shutdownNotifier != nil {
					close(s.shutdownNotifier)
				}
				return
			}
			if err := s.render(cw); err != nil {
				p.dlogger.Println(err)
			}
		}
	}
}

func (s *pState) render(cw *cwriter.Writer) error {
	if s.heapUpdated {
		s.updateSyncMatrix()
		s.heapUpdated = false
	}
	syncWidth(s.pMatrix)
	syncWidth(s.aMatrix)

	tw, err := cw.GetWidth()
	if err != nil {
		tw = s.width
	}
	for i := 0; i < s.bHeap.Len(); i++ {
		bar := s.bHeap[i]
		go bar.render(tw)
	}

	return s.flush(cw)
}

func (s *pState) flush(cw *cwriter.Writer) error {
	var lineCount int
	bm := make(map[*Bar]struct{}, s.bHeap.Len())
	for s.bHeap.Len() > 0 {
		b := heap.Pop(&s.bHeap).(*Bar)
		defer func() {
			if b.toShutdown {
				// shutdown at next flush, in other words decrement underlying WaitGroup
				// only after the bar with completed state has been flushed. this
				// ensures no bar ends up with less than 100% rendered.
				s.barShutdownQueue = append(s.barShutdownQueue, b)
				if !b.noPop && s.popCompleted {
					b.priority = -1
				}
			}
		}()
		cw.ReadFrom(<-b.frameCh)
		lineCount += b.extendedLines + 1
		bm[b] = struct{}{}
	}

	for _, b := range s.barShutdownQueue {
		if parkedBar := s.parkedBars[b]; parkedBar != nil {
			parkedBar.priority = b.priority
			heap.Push(&s.bHeap, parkedBar)
			delete(s.parkedBars, b)
			b.toDrop = true
		}
		if b.toDrop {
			delete(bm, b)
			s.heapUpdated = true
		} else if s.popCompleted {
			if b := b; !b.noPop {
				defer func() {
					s.barPopQueue = append(s.barPopQueue, b)
				}()
			}
		}
		b.cancel()
	}
	s.barShutdownQueue = s.barShutdownQueue[0:0]

	for _, b := range s.barPopQueue {
		delete(bm, b)
		s.heapUpdated = true
		lineCount -= b.extendedLines + 1
	}
	s.barPopQueue = s.barPopQueue[0:0]

	for b := range bm {
		heap.Push(&s.bHeap, b)
	}

	return cw.Flush(lineCount)
}

func (s *pState) manualOrTick() (<-chan time.Time, func()) {
	if s.manualRefreshCh != nil {
		return s.manualRefreshCh, func() {}
	}
	ticker := time.NewTicker(s.rr)
	return ticker.C, ticker.Stop
}

func (s *pState) updateSyncMatrix() {
	s.pMatrix = make(map[int][]chan int)
	s.aMatrix = make(map[int][]chan int)
	for i := 0; i < s.bHeap.Len(); i++ {
		bar := s.bHeap[i]
		table := bar.wSyncTable()
		pRow, aRow := table[0], table[1]

		for i, ch := range pRow {
			s.pMatrix[i] = append(s.pMatrix[i], ch)
		}

		for i, ch := range aRow {
			s.aMatrix[i] = append(s.aMatrix[i], ch)
		}
	}
}

func syncWidth(matrix map[int][]chan int) {
	for _, column := range matrix {
		column := column
		go func() {
			var maxWidth int
			for _, ch := range column {
				if w := <-ch; w > maxWidth {
					maxWidth = w
				}
			}
			for _, ch := range column {
				ch <- maxWidth
			}
		}()
	}
}

func fanInRefreshSrc(done <-chan struct{}, channels ...<-chan time.Time) <-chan time.Time {
	var wg sync.WaitGroup
	multiplexedStream := make(chan time.Time)

	multiplex := func(c <-chan time.Time) {
		defer wg.Done()
		// source channels are never closed (time.Ticker never closes associated
		// channel), so we cannot simply range over a c, instead we use select
		// inside infinite loop
		for {
			select {
			case v := <-c:
				select {
				case multiplexedStream <- v:
				case <-done:
					return
				}
			case <-done:
				return
			}
		}
	}

	wg.Add(len(channels))
	for _, c := range channels {
		go multiplex(c)
	}

	go func() {
		wg.Wait()
		close(multiplexedStream)
	}()

	return multiplexedStream
}
<commit_msg>refactoring: defer inside if<commit_after>package mpb

import (
	"container/heap"
	"context"
	"io"
	"io/ioutil"
	"log"
	"os"
	"sync"
	"time"

	"github.com/vbauerster/mpb/v4/cwriter"
)

const (
	// default RefreshRate
	prr = 120 * time.Millisecond
	// default width
	pwidth = 80
)

// Progress represents the container that renders Progress bars
type Progress struct {
	ctx          context.Context
	uwg          *sync.WaitGroup
	cwg          *sync.WaitGroup
	bwg          *sync.WaitGroup
	operateState chan func(*pState)
	done         chan struct{}
	forceRefresh chan time.Time
	once         sync.Once
	dlogger      *log.Logger
}

type pState struct {
	bHeap            priorityQueue
	heapUpdated      bool
	pMatrix          map[int][]chan int
	aMatrix          map[int][]chan int
	barShutdownQueue []*Bar
	barPopQueue      []*Bar

	// following are provided/overrided by user
	idCount          int
	width            int
	popCompleted     bool
	rr               time.Duration
	uwg              *sync.WaitGroup
	manualRefreshCh  <-chan time.Time
	shutdownNotifier chan struct{}
	parkedBars       map[*Bar]*Bar
	output           io.Writer
	debugOut         io.Writer
}

// New creates new Progress container instance. It's not possible to
// reuse instance after *Progress.Wait() method has been called.
func New(options ...ContainerOption) *Progress {
	return NewWithContext(context.Background(), options...)
}

// NewWithContext creates new Progress container instance with provided
// context. It's not possible to reuse instance after *Progress.Wait()
// method has been called.
func NewWithContext(ctx context.Context, options ...ContainerOption) *Progress {

	s := &pState{
		bHeap:      priorityQueue{},
		width:      pwidth,
		rr:         prr,
		parkedBars: make(map[*Bar]*Bar),
		output:     os.Stdout,
		debugOut:   ioutil.Discard,
	}

	for _, opt := range options {
		if opt != nil {
			opt(s)
		}
	}

	p := &Progress{
		ctx:          ctx,
		uwg:          s.uwg,
		cwg:          new(sync.WaitGroup),
		bwg:          new(sync.WaitGroup),
		operateState: make(chan func(*pState)),
		forceRefresh: make(chan time.Time),
		done:         make(chan struct{}),
		dlogger:      log.New(s.debugOut, "[mpb] ", log.Lshortfile),
	}
	p.cwg.Add(1)
	go p.serve(s, cwriter.New(s.output))
	return p
}

// AddBar creates a new progress bar and adds to the container.
func (p *Progress) AddBar(total int64, options ...BarOption) *Bar {
	return p.Add(total, newDefaultBarFiller(), options...)
}

// AddSpinner creates a new spinner bar and adds to the container.
func (p *Progress) AddSpinner(total int64, alignment SpinnerAlignment, options ...BarOption) *Bar {
	filler := &spinnerFiller{
		frames:    defaultSpinnerStyle,
		alignment: alignment,
	}
	return p.Add(total, filler, options...)
}

// Add creates a bar which renders itself by provided filler.
// Set total to 0, if you plan to update it later.
func (p *Progress) Add(total int64, filler Filler, options ...BarOption) *Bar {
	if filler == nil {
		filler = newDefaultBarFiller()
	}
	p.bwg.Add(1)
	result := make(chan *Bar)
	select {
	case p.operateState <- func(ps *pState) {
		bs := &bState{
			total:    total,
			filler:   filler,
			priority: ps.idCount,
			id:       ps.idCount,
			width:    ps.width,
			debugOut: ps.debugOut,
		}
		for _, opt := range options {
			if opt != nil {
				opt(bs)
			}
		}
		bar := newBar(p, bs)
		if bs.runningBar != nil {
			bs.runningBar.noPop = true
			ps.parkedBars[bs.runningBar] = bar
		} else {
			heap.Push(&ps.bHeap, bar)
			ps.heapUpdated = true
		}
		ps.idCount++
		result <- bar
	}:
		return <-result
	case <-p.done:
		p.bwg.Done()
		return nil
	}
}

func (p *Progress) dropBar(b *Bar) {
	select {
	case p.operateState <- func(s *pState) {
		if b.index < 0 {
			return
		}
		heap.Remove(&s.bHeap, b.index)
		s.heapUpdated = true
	}:
	case <-p.done:
	}
}

func (p *Progress) setBarPriority(b *Bar, priority int) {
	select {
	case p.operateState <- func(s *pState) {
		if b.index < 0 {
			return
		}
		b.priority = priority
		heap.Fix(&s.bHeap, b.index)
	}:
	case <-p.done:
	}
}

// UpdateBarPriority same as *Bar.SetPriority.
func (p *Progress) UpdateBarPriority(b *Bar, priority int) {
	p.setBarPriority(b, priority)
}

// BarCount returns bars count
func (p *Progress) BarCount() int {
	result := make(chan int, 1)
	select {
	case p.operateState <- func(s *pState) { result <- s.bHeap.Len() }:
		return <-result
	case <-p.done:
		return 0
	}
}

// Wait waits far all bars to complete and finally shutdowns container.
// After this method has been called, there is no way to reuse *Progress
// instance.
func (p *Progress) Wait() {
	if p.uwg != nil {
		// wait for user wg
		p.uwg.Wait()
	}

	// wait for bars to quit, if any
	p.bwg.Wait()

	p.once.Do(p.shutdown)

	// wait for container to quit
	p.cwg.Wait()
}

func (p *Progress) shutdown() {
	close(p.done)
}

func (p *Progress) serve(s *pState, cw *cwriter.Writer) {
	defer p.cwg.Done()

	manualOrTickCh, cleanUp := s.manualOrTick()
	defer cleanUp()

	refreshCh := fanInRefreshSrc(p.done, p.forceRefresh, manualOrTickCh)

	for {
		select {
		case op := <-p.operateState:
			op(s)
		case _, ok := <-refreshCh:
			if !ok {
				if s.shutdownNotifier != nil {
					close(s.shutdownNotifier)
				}
				return
			}
			if err := s.render(cw); err != nil {
				p.dlogger.Println(err)
			}
		}
	}
}

func (s *pState) render(cw *cwriter.Writer) error {
	if s.heapUpdated {
		s.updateSyncMatrix()
		s.heapUpdated = false
	}
	syncWidth(s.pMatrix)
	syncWidth(s.aMatrix)

	tw, err := cw.GetWidth()
	if err != nil {
		tw = s.width
	}
	for i := 0; i < s.bHeap.Len(); i++ {
		bar := s.bHeap[i]
		go bar.render(tw)
	}

	return s.flush(cw)
}

func (s *pState) flush(cw *cwriter.Writer) error {
	var lineCount int
	bm := make(map[*Bar]struct{}, s.bHeap.Len())
	for s.bHeap.Len() > 0 {
		b := heap.Pop(&s.bHeap).(*Bar)
		cw.ReadFrom(<-b.frameCh)
		if b.toShutdown {
			// shutdown at next flush
			// this ensures no bar ends up with less than 100% rendered
			defer func() {
				s.barShutdownQueue = append(s.barShutdownQueue, b)
				if !b.noPop && s.popCompleted {
					b.priority = -1
				}
			}()
		}
		lineCount += b.extendedLines + 1
		bm[b] = struct{}{}
	}

	for _, b := range s.barShutdownQueue {
		if parkedBar := s.parkedBars[b]; parkedBar != nil {
			parkedBar.priority = b.priority
			heap.Push(&s.bHeap, parkedBar)
			delete(s.parkedBars, b)
			b.toDrop = true
		}
		if b.toDrop {
			delete(bm, b)
			s.heapUpdated = true
		} else if s.popCompleted {
			if b := b; !b.noPop {
				defer func() {
					s.barPopQueue = append(s.barPopQueue, b)
				}()
			}
		}
		b.cancel()
	}
	s.barShutdownQueue = s.barShutdownQueue[0:0]

	for _, b := range s.barPopQueue {
		delete(bm, b)
		s.heapUpdated = true
		lineCount -= b.extendedLines + 1
	}
	s.barPopQueue = s.barPopQueue[0:0]

	for b := range bm {
		heap.Push(&s.bHeap, b)
	}

	return cw.Flush(lineCount)
}

func (s *pState) manualOrTick() (<-chan time.Time, func()) {
	if s.manualRefreshCh != nil {
		return s.manualRefreshCh, func() {}
	}
	ticker := time.NewTicker(s.rr)
	return ticker.C, ticker.Stop
}

func (s *pState) updateSyncMatrix() {
	s.pMatrix = make(map[int][]chan int)
	s.aMatrix = make(map[int][]chan int)
	for i := 0; i < s.bHeap.Len(); i++ {
		bar := s.bHeap[i]
		table := bar.wSyncTable()
		pRow, aRow := table[0], table[1]

		for i, ch := range pRow {
			s.pMatrix[i] = append(s.pMatrix[i], ch)
		}

		for i, ch := range aRow {
			s.aMatrix[i] = append(s.aMatrix[i], ch)
		}
	}
}

func syncWidth(matrix map[int][]chan int) {
	for _, column := range matrix {
		column := column
		go func() {
			var maxWidth int
			for _, ch := range column {
				if w := <-ch; w > maxWidth {
					maxWidth = w
				}
			}
			for _, ch := range column {
				ch <- maxWidth
			}
		}()
	}
}

func fanInRefreshSrc(done <-chan struct{}, channels ...<-chan time.Time) <-chan time.Time {
	var wg sync.WaitGroup
	multiplexedStream := make(chan time.Time)

	multiplex := func(c <-chan time.Time) {
		defer wg.Done()
		// source channels are never closed (time.Ticker never closes associated
		// channel), so we cannot simply range over a c, instead we use select
		// inside infinite loop
		for {
			select {
			case v := <-c:
				select {
				case multiplexedStream <- v:
				case <-done:
					return
				}
			case <-done:
				return
			}
		}
	}

	wg.Add(len(channels))
	for _, c := range channels {
		go multiplex(c)
	}

	go func() {
		wg.Wait()
		close(multiplexedStream)
	}()

	return multiplexedStream
}
<|endoftext|>


<commit_before>// THIS IS A WORK IN PROGRESS
// based on http://zguide.zeromq.org/php:chapter8#Detecting-Disappearances

package brokerless

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"net"
	"strconv"
	"sync"
	"time"

	"github.com/quintans/gomsg"
	"github.com/quintans/toolkit"
	"github.com/quintans/toolkit/log"
)

var logger = log.LoggerFor("github.com/quintans/gomsg/brokerless")

// defaults
const (
	UuidSize              = 16
	PING                  = "PING"
	beaconMaxDatagramSize = 1024
	beaconAddr            = "224.0.0.1:9999"
	beaconName            = "PEER"
	beaconInterval        = time.Second
	beaconMaxInterval     = beaconInterval * 2
	// BeaconCountdown is the number of consecutive pings inside the ping window
	// to reactivate the UDP health check
	beaconCountdown = 3
)

type node struct {
	uuid            string
	client          *gomsg.Client
	debouncer       *toolkit.Debouncer
	beaconLastTime  time.Time
	beaconCountdown int
}

type Config struct {
	Uuid                  []byte
	BeaconAddr            string
	BeaconName            string
	BeaconMaxDatagramSize int
	BeaconInterval        time.Duration
	BeaconMaxInterval     time.Duration
	// BeaconCountdown is the number of consecutive pings inside the ping window
	// to reactivate the UDP health check
	BeaconCountdown int
}

type Peer struct {
	sync.RWMutex

	cfg            Config
	tcpAddr        string
	requestTimeout time.Duration
	// peers ones will be used to listen
	peers map[string]*node
	// the server will be used to send
	server       *gomsg.Server
	udpConn      *net.UDPConn
	handlers     map[string][]interface{}
	beaconTicker *toolkit.Ticker
}

func NewPeer(cfg Config) *Peer {
	if cfg.Uuid == nil {
		panic("Uuid must be defined")
	}
	// apply defaults
	if cfg.BeaconAddr == "" {
		cfg.BeaconAddr = beaconAddr
	}
	if cfg.BeaconName == "" {
		cfg.BeaconName = beaconName
	}
	if cfg.BeaconMaxDatagramSize == 0 {
		cfg.BeaconMaxDatagramSize = beaconMaxDatagramSize
	}
	if cfg.BeaconInterval == 0 {
		cfg.BeaconInterval = beaconInterval
	}
	if cfg.BeaconMaxInterval == 0 {
		cfg.BeaconMaxInterval = beaconMaxInterval
	}
	if cfg.BeaconCountdown == 0 {
		cfg.BeaconCountdown = beaconCountdown
	}

	peer := &Peer{
		cfg:            cfg,
		peers:          make(map[string]*node),
		handlers:       make(map[string][]interface{}),
		requestTimeout: time.Second,
	}

	return peer
}

func (peer *Peer) SetRequestTimeout(timeout time.Duration) {
	peer.requestTimeout = timeout
}

func (peer *Peer) Connect(tcpAddr string) {
	logger.Infof("Binding peer %X at %s", peer.cfg.Uuid, tcpAddr)
	peer.server = gomsg.NewServer()

	// special case where we receive a targeted request
	// when a peer tries to check if I exist
	// because it did not received the beacon in time
	peer.server.Handle(PING, func() {})

	peer.tcpAddr = tcpAddr
	peer.serveUDP(peer.cfg.BeaconAddr, peer.beaconHandler)
	peer.server.OnBind = func(l net.Listener) {
		fmt.Println("==========> Binded")
		peer.startBeacon(peer.cfg.BeaconAddr)
	}

	peer.server.Listen(tcpAddr)
}

func (peer *Peer) checkPeer(uuid string, addr string) {
	peer.Lock()
	defer peer.Unlock()

	if n := peer.peers[uuid]; n != nil {
		if addr != n.client.Address() {
			logger.Infof("%X - Registering OLD peer %s at %s", peer.cfg.Uuid, uuid, addr)
			// client reconnected with another address
			n.client.Destroy()
			n.debouncer.Kill()
			delete(peer.peers, uuid)
			peer.connectPeer(uuid, addr)
		} else {
			peer.checkBeacon(n)
		}
	} else {
		logger.Infof("%X - Registering NEW peer %s at %s", peer.cfg.Uuid, uuid, addr)
		peer.connectPeer(uuid, addr)
	}
}

func (peer *Peer) checkBeacon(n *node) {
	if n.beaconCountdown == 0 {
		// this debouncer is only for UDP beacon when beaconCountdown == 0
		n.debouncer.Delay(nil)
	} else {
		println("check " + n.uuid)
		var now = time.Now()
		if now.Sub(n.beaconLastTime) < peer.cfg.BeaconMaxInterval {
			n.beaconCountdown--
		} else {
			n.beaconCountdown = peer.cfg.BeaconCountdown
		}
		if n.beaconCountdown == 0 {
			// the client responded, switching to UDP
			logger.Infof("%X - Peer %s at %s responded. Switching to UDP listening", peer.cfg.Uuid, n.uuid, n.client.Address())
			// kill the TCP health check
			n.debouncer.Kill()
			peer.healthCheckByUDP(n)
		}
		n.beaconLastTime = now
	}
}

func (peer *Peer) connectPeer(uuid string, addr string) error {
	var cli = gomsg.NewClient()
	var e = <-cli.Connect(addr)
	if e != nil {
		logger.Errorf("%X - unable to connect to %s at %s", peer.cfg.Uuid, uuid, addr)
		return e
	}
	var n = &node{
		uuid:   uuid,
		client: cli,
	}
	peer.healthCheckByUDP(n)
	peer.peers[uuid] = n

	// apply all handlers
	for k, v := range peer.handlers {
		cli.Handle(k, v...)
	}
	return nil
}

func (peer *Peer) dropPeer(n *node) {
	peer.Lock()
	defer peer.Unlock()

	logger.Infof("%X - Purging unresponsive peer %s at %s", peer.cfg.Uuid, n.uuid, n.client.Address())
	n.client.Destroy()
	n.debouncer = nil
	delete(peer.peers, n.uuid)
}

// healthCheckByIP is the client that checks actively the remote peer
func (peer *Peer) healthCheckByTCP(n *node) {
	var ticker = toolkit.NewTicker(peer.cfg.BeaconInterval, func(t time.Time) {
		<-n.client.RequestTimeout(PING, nil, func() {
			n.debouncer.Delay(nil)
		}, peer.cfg.BeaconInterval)
	})
	n.debouncer = toolkit.NewDebounce(peer.cfg.BeaconMaxInterval, func(o interface{}) {
		peer.dropPeer(n)
	})
	n.debouncer.OnExit = func() {
		ticker.Stop()
	}
}

func (peer *Peer) healthCheckByUDP(n *node) {
	n.debouncer = toolkit.NewDebounce(peer.cfg.BeaconMaxInterval, func(o interface{}) {
		// the client did not responded, switching to TCP
		logger.Infof("%X - Silent peer %s at %s. Switching to TCP ping", peer.cfg.Uuid, n.uuid, n.client.Address())
		n.beaconCountdown = peer.cfg.BeaconCountdown
		peer.healthCheckByTCP(n)
	})
}

func (peer *Peer) beaconHandler(src *net.UDPAddr, n int, b []byte) {
	// starts with tag
	if bytes.HasPrefix(b, []byte(peer.cfg.BeaconName)) {
		var r = bytes.NewReader(b)
		r.Seek(int64(len(peer.cfg.BeaconName)), io.SeekStart)
		var uuid = make([]byte, UuidSize)
		r.Read(uuid)
		// ignore self
		if bytes.Compare(uuid, peer.cfg.Uuid) != 0 {
			var buf16 = make([]byte, 2)
			r.Read(buf16)
			var port = int(binary.LittleEndian.Uint16(buf16))
			peer.checkPeer(fmt.Sprintf("%X", uuid), src.IP.String()+":"+strconv.Itoa(port))
		}
	}
}

func (peer *Peer) Destroy() {
	peer.server.Destroy()
	var conn = peer.udpConn
	peer.udpConn = nil
	if conn != nil {
		conn.Close()
	}
	peer.Lock()
	defer peer.Unlock()
	for _, v := range peer.peers {
		v.debouncer.Kill()
		v.client.Destroy()
	}
	peer.peers = make(map[string]*node)
	if peer.beaconTicker != nil {
		peer.beaconTicker.Stop()
	}
	peer.beaconTicker = nil
}

func (peer *Peer) startBeacon(a string) error {
	addr, err := net.ResolveUDPAddr("udp", a)
	if err != nil {
		return nil
	}
	c, err := net.DialUDP("udp", nil, addr)
	if err != nil {
		return nil
	}
	var buf16 = make([]byte, 2)
	var port = uint16(peer.server.BindPort())
	binary.LittleEndian.PutUint16(buf16, port)

	var buf bytes.Buffer
	buf.WriteString(peer.cfg.BeaconName)
	buf.Write(peer.cfg.Uuid)
	buf.Write(buf16)

	var data = buf.Bytes()
	if peer.beaconTicker != nil {
		peer.beaconTicker.Stop()
	}
	peer.beaconTicker = toolkit.NewDelayedTicker(0, peer.cfg.BeaconInterval, func(t time.Time) {
		c.Write(data)
	})

	return nil
}

func (peer *Peer) serveUDP(a string, hnd func(*net.UDPAddr, int, []byte)) error {
	addr, err := net.ResolveUDPAddr("udp", a)
	if err != nil {
		return err
	}

	l, err := net.ListenUDP("udp", addr)
	if err != nil {
		return err
	}
	l.SetReadBuffer(peer.cfg.BeaconMaxDatagramSize)
	peer.udpConn = l
	go func() {
		for {
			var payload = make([]byte, peer.cfg.BeaconMaxDatagramSize)
			n, src, err := l.ReadFromUDP(payload)
			if peer.udpConn == nil {
				return
			} else if err != nil {
				logger.Errorf("%X - ReadFromUDP failed: %s", peer.cfg.Uuid, err)
				return
			}
			hnd(src, n, payload)
		}
	}()
	return nil
}

func (peer *Peer) Handle(name string, hnd ...interface{}) {
	peer.Lock()
	defer peer.Unlock()

	peer.handlers[name] = hnd
	for _, v := range peer.peers {
		v.client.Handle(name, hnd)
	}
}

func (peer *Peer) Cancel(name string) {
	peer.Lock()
	defer peer.Unlock()

	for _, v := range peer.peers {
		v.client.Cancel(name)
	}
}

func (peer *Peer) Publish(name string, payload interface{}) <-chan error {
	return peer.server.Publish(name, payload)
}

func (peer *Peer) Push(name string, payload interface{}) <-chan error {
	return peer.server.Push(name, payload)
}

func (peer *Peer) Request(name string, payload interface{}, handler interface{}) <-chan error {
	return peer.server.RequestTimeout(name, payload, handler, peer.requestTimeout)
}

func (peer *Peer) RequestAll(name string, payload interface{}, handler interface{}) <-chan error {
	return peer.server.RequestAll(name, payload, handler, peer.requestTimeout)
}
<commit_msg>composing with gomsg.Server<commit_after>// THIS IS A WORK IN PROGRESS
// based on http://zguide.zeromq.org/php:chapter8#Detecting-Disappearances

package brokerless

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"net"
	"strconv"
	"sync"
	"time"

	"github.com/quintans/gomsg"
	"github.com/quintans/toolkit"
	"github.com/quintans/toolkit/log"
)

var logger = log.LoggerFor("github.com/quintans/gomsg/brokerless")

// defaults
const (
	UuidSize              = 16
	PING                  = "PING"
	beaconMaxDatagramSize = 1024
	beaconAddr            = "224.0.0.1:9999"
	beaconName            = "PEER"
	beaconInterval        = time.Second
	beaconMaxInterval     = beaconInterval * 2
	// BeaconCountdown is the number of consecutive pings inside the ping window
	// to reactivate the UDP health check
	beaconCountdown = 3
)

type node struct {
	uuid            string
	client          *gomsg.Client
	debouncer       *toolkit.Debouncer
	beaconLastTime  time.Time
	beaconCountdown int
}

type Config struct {
	Uuid                  []byte
	BeaconAddr            string
	BeaconName            string
	BeaconMaxDatagramSize int
	BeaconInterval        time.Duration
	BeaconMaxInterval     time.Duration
	// BeaconCountdown is the number of consecutive pings inside the ping window
	// to reactivate the UDP health check
	BeaconCountdown int
}

type Peer struct {
	sync.RWMutex
	*gomsg.Server

	cfg            Config
	tcpAddr        string
	requestTimeout time.Duration
	// peers ones will be used to listen
	peers map[string]*node
	// the server will be used to send
	udpConn      *net.UDPConn
	handlers     map[string][]interface{}
	beaconTicker *toolkit.Ticker
}

func NewPeer(cfg Config) *Peer {
	if cfg.Uuid == nil {
		panic("Uuid must be defined")
	}
	// apply defaults
	if cfg.BeaconAddr == "" {
		cfg.BeaconAddr = beaconAddr
	}
	if cfg.BeaconName == "" {
		cfg.BeaconName = beaconName
	}
	if cfg.BeaconMaxDatagramSize == 0 {
		cfg.BeaconMaxDatagramSize = beaconMaxDatagramSize
	}
	if cfg.BeaconInterval == 0 {
		cfg.BeaconInterval = beaconInterval
	}
	if cfg.BeaconMaxInterval == 0 {
		cfg.BeaconMaxInterval = beaconMaxInterval
	}
	if cfg.BeaconCountdown == 0 {
		cfg.BeaconCountdown = beaconCountdown
	}

	peer := &Peer{
		Server:         gomsg.NewServer(),
		cfg:            cfg,
		peers:          make(map[string]*node),
		handlers:       make(map[string][]interface{}),
		requestTimeout: time.Second,
	}

	return peer
}

func (peer *Peer) SetRequestTimeout(timeout time.Duration) {
	peer.requestTimeout = timeout
}

func (peer *Peer) Connect(tcpAddr string) {
	logger.Infof("Binding peer %X at %s", peer.cfg.Uuid, tcpAddr)
	// special case where we receive a targeted request
	// when a peer tries to check if I exist
	// because it did not received the beacon in time
	peer.Server.Handle(PING, func() {})

	peer.tcpAddr = tcpAddr
	peer.serveUDP(peer.cfg.BeaconAddr, peer.beaconHandler)
	peer.Server.OnBind = func(l net.Listener) {
		peer.startBeacon(peer.cfg.BeaconAddr)
	}

	peer.Server.Listen(tcpAddr)
}

func (peer *Peer) checkPeer(uuid string, addr string) {
	peer.Lock()
	defer peer.Unlock()

	if n := peer.peers[uuid]; n != nil {
		if addr != n.client.Address() {
			logger.Infof("%X - Registering OLD peer %s at %s", peer.cfg.Uuid, uuid, addr)
			// client reconnected with another address
			n.client.Destroy()
			n.debouncer.Kill()
			delete(peer.peers, uuid)
			peer.connectPeer(uuid, addr)
		} else {
			peer.checkBeacon(n)
		}
	} else {
		logger.Infof("%X - Registering NEW peer %s at %s", peer.cfg.Uuid, uuid, addr)
		peer.connectPeer(uuid, addr)
	}
}

func (peer *Peer) checkBeacon(n *node) {
	if n.beaconCountdown == 0 {
		// this debouncer is only for UDP beacon when beaconCountdown == 0
		n.debouncer.Delay(nil)
	} else {
		println("check " + n.uuid)
		var now = time.Now()
		if now.Sub(n.beaconLastTime) < peer.cfg.BeaconMaxInterval {
			n.beaconCountdown--
		} else {
			n.beaconCountdown = peer.cfg.BeaconCountdown
		}
		if n.beaconCountdown == 0 {
			// the client responded, switching to UDP
			logger.Infof("%X - Peer %s at %s responded. Switching to UDP listening", peer.cfg.Uuid, n.uuid, n.client.Address())
			// kill the TCP health check
			n.debouncer.Kill()
			peer.healthCheckByUDP(n)
		}
		n.beaconLastTime = now
	}
}

func (peer *Peer) connectPeer(uuid string, addr string) error {
	var cli = gomsg.NewClient()
	var e = <-cli.Connect(addr)
	if e != nil {
		logger.Errorf("%X - unable to connect to %s at %s", peer.cfg.Uuid, uuid, addr)
		return e
	}
	var n = &node{
		uuid:   uuid,
		client: cli,
	}
	peer.healthCheckByUDP(n)
	peer.peers[uuid] = n

	// apply all handlers
	for k, v := range peer.handlers {
		cli.Handle(k, v...)
	}
	return nil
}

func (peer *Peer) dropPeer(n *node) {
	peer.Lock()
	defer peer.Unlock()

	logger.Infof("%X - Purging unresponsive peer %s at %s", peer.cfg.Uuid, n.uuid, n.client.Address())
	n.client.Destroy()
	n.debouncer = nil
	delete(peer.peers, n.uuid)
}

// healthCheckByIP is the client that checks actively the remote peer
func (peer *Peer) healthCheckByTCP(n *node) {
	var ticker = toolkit.NewTicker(peer.cfg.BeaconInterval, func(t time.Time) {
		<-n.client.RequestTimeout(PING, nil, func() {
			n.debouncer.Delay(nil)
		}, peer.cfg.BeaconInterval)
	})
	n.debouncer = toolkit.NewDebounce(peer.cfg.BeaconMaxInterval, func(o interface{}) {
		peer.dropPeer(n)
	})
	n.debouncer.OnExit = func() {
		ticker.Stop()
	}
}

func (peer *Peer) healthCheckByUDP(n *node) {
	n.debouncer = toolkit.NewDebounce(peer.cfg.BeaconMaxInterval, func(o interface{}) {
		// the client did not responded, switching to TCP
		logger.Infof("%X - Silent peer %s at %s. Switching to TCP ping", peer.cfg.Uuid, n.uuid, n.client.Address())
		n.beaconCountdown = peer.cfg.BeaconCountdown
		peer.healthCheckByTCP(n)
	})
}

func (peer *Peer) beaconHandler(src *net.UDPAddr, n int, b []byte) {
	// starts with tag
	if bytes.HasPrefix(b, []byte(peer.cfg.BeaconName)) {
		var r = bytes.NewReader(b)
		r.Seek(int64(len(peer.cfg.BeaconName)), io.SeekStart)
		var uuid = make([]byte, UuidSize)
		r.Read(uuid)
		// ignore self
		if bytes.Compare(uuid, peer.cfg.Uuid) != 0 {
			var buf16 = make([]byte, 2)
			r.Read(buf16)
			var port = int(binary.LittleEndian.Uint16(buf16))
			peer.checkPeer(fmt.Sprintf("%X", uuid), src.IP.String()+":"+strconv.Itoa(port))
		}
	}
}

func (peer *Peer) Destroy() {
	peer.Server.Destroy()
	var conn = peer.udpConn
	peer.udpConn = nil
	if conn != nil {
		conn.Close()
	}
	peer.Lock()
	defer peer.Unlock()
	for _, v := range peer.peers {
		v.debouncer.Kill()
		v.client.Destroy()
	}
	peer.peers = make(map[string]*node)
	if peer.beaconTicker != nil {
		peer.beaconTicker.Stop()
	}
	peer.beaconTicker = nil
}

func (peer *Peer) startBeacon(a string) error {
	addr, err := net.ResolveUDPAddr("udp", a)
	if err != nil {
		return nil
	}
	c, err := net.DialUDP("udp", nil, addr)
	if err != nil {
		return nil
	}
	var buf16 = make([]byte, 2)
	var port = uint16(peer.Server.BindPort())
	binary.LittleEndian.PutUint16(buf16, port)

	var buf bytes.Buffer
	buf.WriteString(peer.cfg.BeaconName)
	buf.Write(peer.cfg.Uuid)
	buf.Write(buf16)

	var data = buf.Bytes()
	if peer.beaconTicker != nil {
		peer.beaconTicker.Stop()
	}
	peer.beaconTicker = toolkit.NewDelayedTicker(0, peer.cfg.BeaconInterval, func(t time.Time) {
		c.Write(data)
	})

	return nil
}

func (peer *Peer) serveUDP(a string, hnd func(*net.UDPAddr, int, []byte)) error {
	addr, err := net.ResolveUDPAddr("udp", a)
	if err != nil {
		return err
	}

	l, err := net.ListenUDP("udp", addr)
	if err != nil {
		return err
	}
	l.SetReadBuffer(peer.cfg.BeaconMaxDatagramSize)
	peer.udpConn = l
	go func() {
		for {
			var payload = make([]byte, peer.cfg.BeaconMaxDatagramSize)
			n, src, err := l.ReadFromUDP(payload)
			if peer.udpConn == nil {
				return
			} else if err != nil {
				logger.Errorf("%X - ReadFromUDP failed: %s", peer.cfg.Uuid, err)
				return
			}
			hnd(src, n, payload)
		}
	}()
	return nil
}

func (peer *Peer) Handle(name string, hnd ...interface{}) {
	peer.Lock()
	defer peer.Unlock()

	peer.handlers[name] = hnd
	for _, v := range peer.peers {
		v.client.Handle(name, hnd)
	}
}

func (peer *Peer) Cancel(name string) {
	peer.Lock()
	defer peer.Unlock()

	for _, v := range peer.peers {
		v.client.Cancel(name)
	}
}

func (peer *Peer) Request(name string, payload interface{}, handler interface{}) <-chan error {
	return peer.Server.RequestTimeout(name, payload, handler, peer.requestTimeout)
}

func (peer *Peer) RequestAll(name string, payload interface{}, handler interface{}) <-chan error {
	return peer.Server.RequestAll(name, payload, handler, peer.requestTimeout)
}
<|endoftext|>


<commit_before>package docker

import (
	"encoding/json"
	"fmt"
	"github.com/dotcloud/docker/auth"
	"io"
	"io/ioutil"
	"net/http"
	"path"
	"strings"
)

//FIXME: Set the endpoint in a conf file or via commandline
//const REGISTRY_ENDPOINT = "http://registry-creack.dotcloud.com/v1"
const REGISTRY_ENDPOINT = auth.REGISTRY_SERVER + "/v1"

// Build an Image object from raw json data
func NewImgJson(src []byte) (*Image, error) {
	ret := &Image{}

	fmt.Printf("Json string: {%s}\n", src)
	// FIXME: Is there a cleaner way to "puryfy" the input json?
	src = []byte(strings.Replace(string(src), "null", "\"\"", -1))

	if err := json.Unmarshal(src, ret); err != nil {
		return nil, err
	}
	return ret, nil
}

// Build an Image object list from a raw json data
// FIXME: Do this in "stream" mode
func NewMultipleImgJson(src []byte) ([]*Image, error) {
	ret := []*Image{}

	dec := json.NewDecoder(strings.NewReader(strings.Replace(string(src), "null", "\"\"", -1)))
	for {
		m := &Image{}
		if err := dec.Decode(m); err == io.EOF {
			break
		} else if err != nil {
			return nil, err
		}
		ret = append(ret, m)
	}
	return ret, nil
}

// Retrieve the history of a given image from the Registry.
// Return a list of the parent's json (requested image included)
func (graph *Graph) getRemoteHistory(imgId string, authConfig *auth.AuthConfig) ([]*Image, error) {
	client := &http.Client{}

	req, err := http.NewRequest("GET", REGISTRY_ENDPOINT+"/images/"+imgId+"/history", nil)
	if err != nil {
		return nil, err
	}
	req.SetBasicAuth(authConfig.Username, authConfig.Password)
	res, err := client.Do(req)
	if err != nil || res.StatusCode != 200 {
		if res != nil {
			return nil, fmt.Errorf("Internal server error: %d trying to fetch remote history for %s", res.StatusCode, imgId)
		}
		return nil, err
	}
	defer res.Body.Close()

	jsonString, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, fmt.Errorf("Error while reading the http response: %s\n", err)
	}

	history, err := NewMultipleImgJson(jsonString)
	if err != nil {
		return nil, fmt.Errorf("Error while parsing the json: %s\n", err)
	}
	return history, nil
}

// Check if an image exists in the Registry
func (graph *Graph) LookupRemoteImage(imgId string, authConfig *auth.AuthConfig) bool {
	rt := &http.Transport{Proxy: http.ProxyFromEnvironment}

	req, err := http.NewRequest("GET", REGISTRY_ENDPOINT+"/images/"+imgId+"/json", nil)
	if err != nil {
		return false
	}
	req.SetBasicAuth(authConfig.Username, authConfig.Password)
	res, err := rt.RoundTrip(req)
	if err != nil || res.StatusCode != 307 {
		return false
	}
	return res.StatusCode == 307
}

// Retrieve an image from the Registry.
// Returns the Image object as well as the layer as an Archive (io.Reader)
func (graph *Graph) getRemoteImage(imgId string, authConfig *auth.AuthConfig) (*Image, Archive, error) {
	client := &http.Client{}

	// Get the Json
	req, err := http.NewRequest("GET", REGISTRY_ENDPOINT+"/images/"+imgId+"/json", nil)
	if err != nil {
		return nil, nil, fmt.Errorf("Error while getting from the server: %s\n", err)
	}
	req.SetBasicAuth(authConfig.Username, authConfig.Password)
	res, err := client.Do(req)
	if err != nil || res.StatusCode != 200 {
		if res != nil {
			return nil, nil, fmt.Errorf("Internal server error: %d trying to get image %s", res.StatusCode, imgId)
		}
		return nil, nil, err
	}
	defer res.Body.Close()

	jsonString, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, nil, fmt.Errorf("Error while reading the http response: %s\n", err)
	}

	img, err := NewImgJson(jsonString)
	if err != nil {
		return nil, nil, fmt.Errorf("Error while parsing the json: %s\n", err)
	}
	img.Id = imgId

	// Get the layer
	req, err = http.NewRequest("GET", REGISTRY_ENDPOINT+"/images/"+imgId+"/layer", nil)
	if err != nil {
		return nil, nil, fmt.Errorf("Error while getting from the server: %s\n", err)
	}
	req.SetBasicAuth(authConfig.Username, authConfig.Password)
	res, err = client.Do(req)
	if err != nil {
		return nil, nil, err
	}
	return img, res.Body, nil
}

func (graph *Graph) PullImage(imgId string, authConfig *auth.AuthConfig) error {
	history, err := graph.getRemoteHistory(imgId, authConfig)
	if err != nil {
		return err
	}
	// FIXME: Try to stream the images?
	// FIXME: Lunch the getRemoteImage() in goroutines
	for _, j := range history {
		if !graph.Exists(j.Id) {
			img, layer, err := graph.getRemoteImage(j.Id, authConfig)
			if err != nil {
				// FIXME: Keep goging in case of error?
				return err
			}
			if err = graph.Register(layer, img); err != nil {
				return err
			}
		}
	}
	return nil
}

// FIXME: Handle the askedTag parameter
func (graph *Graph) PullRepository(remote, askedTag string, repositories *TagStore, authConfig *auth.AuthConfig) error {
	client := &http.Client{}

	fmt.Printf("Pulling repo: %s\n", REGISTRY_ENDPOINT+"/users/"+remote)

	req, err := http.NewRequest("GET", REGISTRY_ENDPOINT+"/users/"+remote, nil)
	if err != nil {
		return err
	}
	req.SetBasicAuth(authConfig.Username, authConfig.Password)
	res, err := client.Do(req)
	if err != nil || res.StatusCode != 200 {
		if res != nil {
			return fmt.Errorf("Internal server error: %d trying to pull %s", res.StatusCode, remote)
		}
		return err
	}
	defer res.Body.Close()
	rawJson, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return err
	}
	t := map[string]string{}
	if err = json.Unmarshal(rawJson, &t); err != nil {
		return err
	}
	for tag, rev := range t {
		if err = graph.PullImage(rev, authConfig); err != nil {
			return err
		}
		if err = repositories.Set(remote, tag, rev, true); err != nil {
			return err
		}
	}
	if err = repositories.Save(); err != nil {
		return err
	}
	return nil
}

// Push a local image to the registry with its history if needed
func (graph *Graph) PushImage(imgOrig *Image, authConfig *auth.AuthConfig) error {
	client := &http.Client{}

	// FIXME: Factorize the code
	// FIXME: Do the puts in goroutines
	if err := imgOrig.WalkHistory(func(img *Image) error {

		jsonRaw, err := ioutil.ReadFile(path.Join(graph.Root, img.Id, "json"))
		if err != nil {
			return fmt.Errorf("Error while retreiving the path for {%s}: %s", img.Id, err)
		}
		// FIXME: try json with UTF8
		jsonData := strings.NewReader(string(jsonRaw))
		req, err := http.NewRequest("PUT", REGISTRY_ENDPOINT+"/images/"+img.Id+"/json", jsonData)
		if err != nil {
			return err
		}
		req.Header.Add("Content-type", "application/json")
		req.SetBasicAuth(authConfig.Username, authConfig.Password)
		res, err := client.Do(req)
		if err != nil || res.StatusCode != 200 {
			if res == nil {
				return fmt.Errorf(
					"Error: Internal server error trying to push image {%s} (json): %s",
					img.Id, err)
			}
			fmt.Printf("Pushing return status: %d\n", res.StatusCode)
			switch res.StatusCode {
			case 204:
				// Case where the image is already on the Registry
				// FIXME: Do not be silent?
				return nil
			case 400:
				return fmt.Errorf("Error: Invalid Json")
			default:
				return fmt.Errorf(
					"Error: Internal server error: %d trying to push image {%s} (json): %s\n",
					res.StatusCode, img.Id, err)
			}
		}

		req2, err := http.NewRequest("PUT", REGISTRY_ENDPOINT+"/images/"+img.Id+"/layer", nil)
		req2.SetBasicAuth(authConfig.Username, authConfig.Password)
		res2, err := client.Do(req2)
		if err != nil || res2.StatusCode != 307 {
			return fmt.Errorf(
				"Internal server error trying to push image {%s} (layer 1): %s\n",
				img.Id, err)
		}
		url, err := res2.Location()
		if err != nil || url == nil {
			return fmt.Errorf(
				"Fail to retrieve layer storage URL for image {%s}: %s\n",
				img.Id, err)
		}
		// FIXME: Don't do this :D. Check the S3 requierement and implement chunks of 5MB
		// FIXME2: I won't stress it enough, DON'T DO THIS! very high priority
		layerData2, err := Tar(path.Join(graph.Root, img.Id, "layer"), Gzip)
		layerData, err := Tar(path.Join(graph.Root, img.Id, "layer"), Gzip)
		if err != nil {
			return fmt.Errorf(
				"Error while retrieving layer for {%s}: %s\n",
				img.Id, err)
		}
		req3, err := http.NewRequest("PUT", url.String(), layerData)
		if err != nil {
			return err
		}
		tmp, err := ioutil.ReadAll(layerData2)
		if err != nil {
			return err
		}
		req3.ContentLength = int64(len(tmp))

		req3.TransferEncoding = []string{"none"}
		res3, err := client.Do(req3)
		if err != nil || res3.StatusCode != 200 {
			if res3 == nil {
				return fmt.Errorf(
					"Error trying to push image {%s} (layer 2): %s\n",
					img.Id, err)
			}
			return fmt.Errorf(
				"Error trying to push image {%s} (layer 2): %s (%d)\n",
				img.Id, err, res3.StatusCode)
		}
		return nil
	}); err != nil {
		return err
	}
	return nil
}

// push a tag on the registry.
// Remote has the format '<user>/<repo>
func (graph *Graph) pushTag(remote, revision, tag string, authConfig *auth.AuthConfig) error {

	// Keep this for backward compatibility
	if tag == "" {
		tag = "lastest"
	}

	// "jsonify" the string
	revision = "\"" + revision + "\""

	client := &http.Client{}
	req, err := http.NewRequest("PUT", REGISTRY_ENDPOINT+"/users/"+remote+"/"+tag, strings.NewReader(revision))
	req.Header.Add("Content-type", "application/json")
	req.SetBasicAuth(authConfig.Username, authConfig.Password)
	res, err := client.Do(req)
	if err != nil || (res.StatusCode != 200 && res.StatusCode != 201) {
		if res != nil {
			return fmt.Errorf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, remote)
		}
		return err
	}
	fmt.Printf("Result of push tag: %d\n", res.StatusCode)
	switch res.StatusCode {
	default:
		return fmt.Errorf("Error %d\n", res.StatusCode)
	case 200:
	case 201:
	}
	return nil
}

// Push a repository to the registry.
// Remote has the format '<user>/<repo>
func (graph *Graph) PushRepository(remote string, localRepo Repository, authConfig *auth.AuthConfig) error {
	for tag, imgId := range localRepo {
		fmt.Printf("tag: %s, imgId: %s\n", tag, imgId)
		img, err := graph.Get(imgId)
		if err != nil {
			return err
		}
		if err = graph.PushImage(img, authConfig); err != nil {
			return err
		}
		if err = graph.pushTag(remote, imgId, tag, authConfig); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>Make sure the remote repository exists prior to push<commit_after>package docker

import (
	"encoding/json"
	"fmt"
	"github.com/dotcloud/docker/auth"
	"io"
	"io/ioutil"
	"net/http"
	"path"
	"strings"
)

//FIXME: Set the endpoint in a conf file or via commandline
//const REGISTRY_ENDPOINT = "http://registry-creack.dotcloud.com/v1"
const REGISTRY_ENDPOINT = auth.REGISTRY_SERVER + "/v1"

// Build an Image object from raw json data
func NewImgJson(src []byte) (*Image, error) {
	ret := &Image{}

	fmt.Printf("Json string: {%s}\n", src)
	// FIXME: Is there a cleaner way to "puryfy" the input json?
	src = []byte(strings.Replace(string(src), "null", "\"\"", -1))

	if err := json.Unmarshal(src, ret); err != nil {
		return nil, err
	}
	return ret, nil
}

// Build an Image object list from a raw json data
// FIXME: Do this in "stream" mode
func NewMultipleImgJson(src []byte) ([]*Image, error) {
	ret := []*Image{}

	dec := json.NewDecoder(strings.NewReader(strings.Replace(string(src), "null", "\"\"", -1)))
	for {
		m := &Image{}
		if err := dec.Decode(m); err == io.EOF {
			break
		} else if err != nil {
			return nil, err
		}
		ret = append(ret, m)
	}
	return ret, nil
}

// Retrieve the history of a given image from the Registry.
// Return a list of the parent's json (requested image included)
func (graph *Graph) getRemoteHistory(imgId string, authConfig *auth.AuthConfig) ([]*Image, error) {
	client := &http.Client{}

	req, err := http.NewRequest("GET", REGISTRY_ENDPOINT+"/images/"+imgId+"/history", nil)
	if err != nil {
		return nil, err
	}
	req.SetBasicAuth(authConfig.Username, authConfig.Password)
	res, err := client.Do(req)
	if err != nil || res.StatusCode != 200 {
		if res != nil {
			return nil, fmt.Errorf("Internal server error: %d trying to fetch remote history for %s", res.StatusCode, imgId)
		}
		return nil, err
	}
	defer res.Body.Close()

	jsonString, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, fmt.Errorf("Error while reading the http response: %s\n", err)
	}

	history, err := NewMultipleImgJson(jsonString)
	if err != nil {
		return nil, fmt.Errorf("Error while parsing the json: %s\n", err)
	}
	return history, nil
}

// Check if an image exists in the Registry
func (graph *Graph) LookupRemoteImage(imgId string, authConfig *auth.AuthConfig) bool {
	rt := &http.Transport{Proxy: http.ProxyFromEnvironment}

	req, err := http.NewRequest("GET", REGISTRY_ENDPOINT+"/images/"+imgId+"/json", nil)
	if err != nil {
		return false
	}
	req.SetBasicAuth(authConfig.Username, authConfig.Password)
	res, err := rt.RoundTrip(req)
	if err != nil || res.StatusCode != 307 {
		return false
	}
	return res.StatusCode == 307
}

// Retrieve an image from the Registry.
// Returns the Image object as well as the layer as an Archive (io.Reader)
func (graph *Graph) getRemoteImage(imgId string, authConfig *auth.AuthConfig) (*Image, Archive, error) {
	client := &http.Client{}

	// Get the Json
	req, err := http.NewRequest("GET", REGISTRY_ENDPOINT+"/images/"+imgId+"/json", nil)
	if err != nil {
		return nil, nil, fmt.Errorf("Error while getting from the server: %s\n", err)
	}
	req.SetBasicAuth(authConfig.Username, authConfig.Password)
	res, err := client.Do(req)
	if err != nil || res.StatusCode != 200 {
		if res != nil {
			return nil, nil, fmt.Errorf("Internal server error: %d trying to get image %s", res.StatusCode, imgId)
		}
		return nil, nil, err
	}
	defer res.Body.Close()

	jsonString, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, nil, fmt.Errorf("Error while reading the http response: %s\n", err)
	}

	img, err := NewImgJson(jsonString)
	if err != nil {
		return nil, nil, fmt.Errorf("Error while parsing the json: %s\n", err)
	}
	img.Id = imgId

	// Get the layer
	req, err = http.NewRequest("GET", REGISTRY_ENDPOINT+"/images/"+imgId+"/layer", nil)
	if err != nil {
		return nil, nil, fmt.Errorf("Error while getting from the server: %s\n", err)
	}
	req.SetBasicAuth(authConfig.Username, authConfig.Password)
	res, err = client.Do(req)
	if err != nil {
		return nil, nil, err
	}
	return img, res.Body, nil
}

func (graph *Graph) PullImage(imgId string, authConfig *auth.AuthConfig) error {
	history, err := graph.getRemoteHistory(imgId, authConfig)
	if err != nil {
		return err
	}
	// FIXME: Try to stream the images?
	// FIXME: Lunch the getRemoteImage() in goroutines
	for _, j := range history {
		if !graph.Exists(j.Id) {
			img, layer, err := graph.getRemoteImage(j.Id, authConfig)
			if err != nil {
				// FIXME: Keep goging in case of error?
				return err
			}
			if err = graph.Register(layer, img); err != nil {
				return err
			}
		}
	}
	return nil
}

// FIXME: Handle the askedTag parameter
func (graph *Graph) PullRepository(remote, askedTag string, repositories *TagStore, authConfig *auth.AuthConfig) error {
	client := &http.Client{}

	fmt.Printf("Pulling repo: %s\n", REGISTRY_ENDPOINT+"/users/"+remote)

	req, err := http.NewRequest("GET", REGISTRY_ENDPOINT+"/users/"+remote, nil)
	if err != nil {
		return err
	}
	req.SetBasicAuth(authConfig.Username, authConfig.Password)
	res, err := client.Do(req)
	if err != nil || res.StatusCode != 200 {
		if res != nil {
			return fmt.Errorf("Internal server error: %d trying to pull %s", res.StatusCode, remote)
		}
		return err
	}
	defer res.Body.Close()
	rawJson, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return err
	}
	t := map[string]string{}
	if err = json.Unmarshal(rawJson, &t); err != nil {
		return err
	}
	for tag, rev := range t {
		if err = graph.PullImage(rev, authConfig); err != nil {
			return err
		}
		if err = repositories.Set(remote, tag, rev, true); err != nil {
			return err
		}
	}
	if err = repositories.Save(); err != nil {
		return err
	}
	return nil
}

// Push a local image to the registry with its history if needed
func (graph *Graph) PushImage(imgOrig *Image, authConfig *auth.AuthConfig) error {
	client := &http.Client{}

	// FIXME: Factorize the code
	// FIXME: Do the puts in goroutines
	if err := imgOrig.WalkHistory(func(img *Image) error {

		jsonRaw, err := ioutil.ReadFile(path.Join(graph.Root, img.Id, "json"))
		if err != nil {
			return fmt.Errorf("Error while retreiving the path for {%s}: %s", img.Id, err)
		}
		// FIXME: try json with UTF8
		jsonData := strings.NewReader(string(jsonRaw))
		req, err := http.NewRequest("PUT", REGISTRY_ENDPOINT+"/images/"+img.Id+"/json", jsonData)
		if err != nil {
			return err
		}
		req.Header.Add("Content-type", "application/json")
		req.SetBasicAuth(authConfig.Username, authConfig.Password)
		res, err := client.Do(req)
		if err != nil || res.StatusCode != 200 {
			if res == nil {
				return fmt.Errorf(
					"Error: Internal server error trying to push image {%s} (json): %s",
					img.Id, err)
			}
			fmt.Printf("Pushing return status: %d\n", res.StatusCode)
			switch res.StatusCode {
			case 204:
				// Case where the image is already on the Registry
				// FIXME: Do not be silent?
				return nil
			case 400:
				return fmt.Errorf("Error: Invalid Json")
			default:
				return fmt.Errorf(
					"Error: Internal server error: %d trying to push image {%s} (json): %s\n",
					res.StatusCode, img.Id, err)
			}
		}

		req2, err := http.NewRequest("PUT", REGISTRY_ENDPOINT+"/images/"+img.Id+"/layer", nil)
		req2.SetBasicAuth(authConfig.Username, authConfig.Password)
		res2, err := client.Do(req2)
		if err != nil || res2.StatusCode != 307 {
			return fmt.Errorf(
				"Internal server error trying to push image {%s} (layer 1): %s\n",
				img.Id, err)
		}
		url, err := res2.Location()
		if err != nil || url == nil {
			return fmt.Errorf(
				"Fail to retrieve layer storage URL for image {%s}: %s\n",
				img.Id, err)
		}
		// FIXME: Don't do this :D. Check the S3 requierement and implement chunks of 5MB
		// FIXME2: I won't stress it enough, DON'T DO THIS! very high priority
		layerData2, err := Tar(path.Join(graph.Root, img.Id, "layer"), Gzip)
		layerData, err := Tar(path.Join(graph.Root, img.Id, "layer"), Gzip)
		if err != nil {
			return fmt.Errorf(
				"Error while retrieving layer for {%s}: %s\n",
				img.Id, err)
		}
		req3, err := http.NewRequest("PUT", url.String(), layerData)
		if err != nil {
			return err
		}
		tmp, err := ioutil.ReadAll(layerData2)
		if err != nil {
			return err
		}
		req3.ContentLength = int64(len(tmp))

		req3.TransferEncoding = []string{"none"}
		res3, err := client.Do(req3)
		if err != nil || res3.StatusCode != 200 {
			if res3 == nil {
				return fmt.Errorf(
					"Error trying to push image {%s} (layer 2): %s\n",
					img.Id, err)
			}
			return fmt.Errorf(
				"Error trying to push image {%s} (layer 2): %s (%d)\n",
				img.Id, err, res3.StatusCode)
		}
		return nil
	}); err != nil {
		return err
	}
	return nil
}

// push a tag on the registry.
// Remote has the format '<user>/<repo>
func (graph *Graph) pushTag(remote, revision, tag string, authConfig *auth.AuthConfig) error {

	// Keep this for backward compatibility
	if tag == "" {
		tag = "lastest"
	}

	// "jsonify" the string
	revision = "\"" + revision + "\""

	client := &http.Client{}
	req, err := http.NewRequest("PUT", REGISTRY_ENDPOINT+"/users/"+remote+"/"+tag, strings.NewReader(revision))
	req.Header.Add("Content-type", "application/json")
	req.SetBasicAuth(authConfig.Username, authConfig.Password)
	res, err := client.Do(req)
	if err != nil || (res.StatusCode != 200 && res.StatusCode != 201) {
		if res != nil {
			return fmt.Errorf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, remote)
		}
		return err
	}
	fmt.Printf("Result of push tag: %d\n", res.StatusCode)
	switch res.StatusCode {
	default:
		return fmt.Errorf("Error %d\n", res.StatusCode)
	case 200:
	case 201:
	}
	return nil
}

func (graph *Graph) LookupRemoteRepository(remote string, authConfig *auth.AuthConfig) bool {
	rt := &http.Transport{Proxy: http.ProxyFromEnvironment}

	req, err := http.NewRequest("GET", REGISTRY_ENDPOINT+"/users/"+remote, nil)
	if err != nil {
		return false
	}
	req.SetBasicAuth(authConfig.Username, authConfig.Password)
	res, err := rt.RoundTrip(req)
	if err != nil || res.StatusCode != 200 {
		return false
	}
	return true
}

func (graph *Graph) pushPrimitive(remote, tag, imgId string, authConfig *auth.AuthConfig) error {
	// CHeck if the local impage exists
	img, err := graph.Get(imgId)
	if err != nil {
		return err
	}
	// Push the image
	if err = graph.PushImage(img, authConfig); err != nil {
		return err
	}
	// And then the tag
	if err = graph.pushTag(remote, imgId, tag, authConfig); err != nil {
		return err
	}
	return nil
}

// Push a repository to the registry.
// Remote has the format '<user>/<repo>
func (graph *Graph) PushRepository(remote string, localRepo Repository, authConfig *auth.AuthConfig) error {
	// Check if the remote repository exists
	if !graph.LookupRemoteRepository(remote, authConfig) {
		return fmt.Errorf("The remote repository %s does not exist\n", remote)
	}

	// For each image within the repo, push them
	for tag, imgId := range localRepo {
		if err := graph.pushPrimitive(remote, tag, imgId, authConfig); err != nil {
			// FIXME: Continue on error?
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// 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 renderer

import (
	"strings"
)

type Log interface {
	ExistsInGoodWords() bool
	ExistsInWarnWords() bool
	ExistsInGoodLines() bool
	ExistsInBadLines() bool
}

type HTTP struct {
	GoodWords []string `json:"good_words"`
	GoodLines []string `json:"good_lines"`
	WarnWords []string `json:"warn_words"`
	BadLines  []string `json:"bad_lines"`
}

type FTP struct {
	GoodWords []string `json:"good_words"`
	WarnWords []string `json:"warn_words"`
	BadLines  []string `json:"bad_lines"`
}

func (h *HTTP) ExistsInGoodWords(word string) bool {
	for _, i := range h.GoodWords {
		if strings.Contains(word, i) {
			return true
		}
	}
	return false
}

func (h *HTTP) ExistsInWarnWords(word string) bool {
	for _, i := range h.WarnWords {
		if strings.Contains(word, i) {
			return true
		}
	}
	return false
}

func (h *HTTP) ExistsInGoodWords() bool {
	for _, i := range h.GoodWords {
		if strings.Contains(word, i) {
			return true
		}
	}
	return false
}

func (h *HTTP) ExistsInBadLines() bool {
	for _, i := range h.BadLines {
		if strings.Contains(word, i) {
			return true
		}
	}
	return false
}
<commit_msg>fix bug with interface functions not having the needed argument<commit_after>// 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 renderer

import (
	"strings"
)

type Log interface {
	ExistsInGoodWords(word string) bool
	ExistsInWarnWords(word string) bool
	ExistsInGoodLines(word string) bool
	ExistsInBadLines(word string) bool
}

type HTTP struct {
	GoodWords []string `json:"good_words"`
	GoodLines []string `json:"good_lines"`
	WarnWords []string `json:"warn_words"`
	BadLines  []string `json:"bad_lines"`
}

type FTP struct {
	GoodWords []string `json:"good_words"`
	WarnWords []string `json:"warn_words"`
	BadLines  []string `json:"bad_lines"`
}

func (h *HTTP) ExistsInGoodWords(word string) bool {
	for _, i := range h.GoodWords {
		if strings.Contains(word, i) {
			return true
		}
	}
	return false
}

func (h *HTTP) ExistsInWarnWords(word string) bool {
	for _, i := range h.WarnWords {
		if strings.Contains(word, i) {
			return true
		}
	}
	return false
}

func (h *HTTP) ExistsInGoodWords() bool {
	for _, i := range h.GoodWords {
		if strings.Contains(word, i) {
			return true
		}
	}
	return false
}

func (h *HTTP) ExistsInBadLines() bool {
	for _, i := range h.BadLines {
		if strings.Contains(word, i) {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>// Package prompter is utility for easy prompting
package prompter

import (
	"bufio"
	"fmt"
	"os"
	"regexp"
	"strings"

	"github.com/mattn/go-isatty"
	"golang.org/x/crypto/ssh/terminal"
)

// VERSION version of prompter
const VERSION = "0.2.0"

// Prompter is object for prompting
type Prompter struct {
	Message string
	// choices of answer
	Choices    []string
	IgnoreCase bool
	Default    string
	// specify answer pattern by regexp. When both Choices and Regexp are specified, Regexp takes a priority.
	Regexp *regexp.Regexp
	// for passwords and so on.
	NoEcho     bool
	UseDefault bool
	reg        *regexp.Regexp
}

// Prompt displays a prompt and returns answer
func (p *Prompter) Prompt() string {
	fmt.Print(p.msg())
	if p.UseDefault || skip() {
		return p.Default
	}
	input := ""
	if p.NoEcho {
		b, err := terminal.ReadPassword(int(os.Stdin.Fd()))
		if err == nil {
			input = string(b)
		}
		fmt.Print("\n")
	} else {
		scanner := bufio.NewScanner(os.Stdin)
		ok := scanner.Scan()
		if ok {
			input = strings.TrimRight(scanner.Text(), "\r\n")
		}
	}
	if input == "" {
		input = p.Default
	}
	if !p.inputIsValid(input) {
		fmt.Println(p.errorMsg())
		return p.Prompt()
	}
	return input
}

func skip() bool {
	if os.Getenv("GO_PROMPTER_USE_DEFAULT") != "" {
		return true
	}
	return !isatty.IsTerminal(os.Stdin.Fd()) || !isatty.IsTerminal(os.Stdout.Fd())
}

func (p *Prompter) msg() string {
	msg := p.Message
	if p.Choices != nil && len(p.Choices) > 0 {
		msg += fmt.Sprintf(" (%s)", strings.Join(p.Choices, "/"))
	}
	if p.Default != "" {
		msg += " [" + p.Default + "]"
	}
	return msg + ": "
}

func (p *Prompter) errorMsg() string {
	if p.Regexp != nil {
		return fmt.Sprintf("# Answer should match /%s/", p.Regexp)
	}
	if p.Choices != nil && len(p.Choices) > 0 {
		if len(p.Choices) == 1 {
			return fmt.Sprintf("# Enter `%s`", p.Choices[0])
		}
		choices := make([]string, len(p.Choices)-1)
		for i, v := range p.Choices[:len(p.Choices)-1] {
			choices[i] = "`" + v + "`"
		}
		return fmt.Sprintf("# Enter %s or `%s`", strings.Join(choices, ", "), p.Choices[len(p.Choices)-1])
	}
	return ""
}

func (p *Prompter) inputIsValid(input string) bool {
	return p.regexp().MatchString(input)
}

var allReg = regexp.MustCompile(`.*`)

func (p *Prompter) regexp() *regexp.Regexp {
	if p.Regexp != nil {
		return p.Regexp
	}
	if p.reg != nil {
		return p.reg
	}
	if p.Choices == nil || len(p.Choices) == 0 {
		p.reg = allReg
		return p.reg
	}

	choices := make([]string, len(p.Choices))
	for i, v := range p.Choices {
		choices[i] = regexp.QuoteMeta(v)
	}
	ignoreReg := ""
	if p.IgnoreCase {
		ignoreReg = "(?i)"
	}
	p.reg = regexp.MustCompile(fmt.Sprintf(`%s\A(?:%s)\z`, ignoreReg, strings.Join(choices, "|")))
	return p.reg
}
<commit_msg>support cygwin<commit_after>// Package prompter is utility for easy prompting
package prompter

import (
	"bufio"
	"fmt"
	"os"
	"regexp"
	"strings"

	"github.com/mattn/go-isatty"
	"golang.org/x/crypto/ssh/terminal"
)

// VERSION version of prompter
const VERSION = "0.2.0"

// Prompter is object for prompting
type Prompter struct {
	Message string
	// choices of answer
	Choices    []string
	IgnoreCase bool
	Default    string
	// specify answer pattern by regexp. When both Choices and Regexp are specified, Regexp takes a priority.
	Regexp *regexp.Regexp
	// for passwords and so on.
	NoEcho     bool
	UseDefault bool
	reg        *regexp.Regexp
}

// Prompt displays a prompt and returns answer
func (p *Prompter) Prompt() string {
	fmt.Print(p.msg())
	if p.UseDefault || skip() {
		return p.Default
	}
	input := ""
	if p.NoEcho {
		b, err := terminal.ReadPassword(int(os.Stdin.Fd()))
		if err == nil {
			input = string(b)
		}
		fmt.Print("\n")
	} else {
		scanner := bufio.NewScanner(os.Stdin)
		ok := scanner.Scan()
		if ok {
			input = strings.TrimRight(scanner.Text(), "\r\n")
		}
	}
	if input == "" {
		input = p.Default
	}
	if !p.inputIsValid(input) {
		fmt.Println(p.errorMsg())
		return p.Prompt()
	}
	return input
}

func skip() bool {
	if os.Getenv("GO_PROMPTER_USE_DEFAULT") != "" {
		return true
	}
	return !isatty.IsTerminal(os.Stdin.Fd()) ||
		!isatty.IsCygwinTerminal(os.Stdin.Fd()) ||
		!isatty.IsTerminal(os.Stdout.Fd()) ||
		!isatty.IsCygwinTerminal(os.Stdout.Fd())

}

func (p *Prompter) msg() string {
	msg := p.Message
	if p.Choices != nil && len(p.Choices) > 0 {
		msg += fmt.Sprintf(" (%s)", strings.Join(p.Choices, "/"))
	}
	if p.Default != "" {
		msg += " [" + p.Default + "]"
	}
	return msg + ": "
}

func (p *Prompter) errorMsg() string {
	if p.Regexp != nil {
		return fmt.Sprintf("# Answer should match /%s/", p.Regexp)
	}
	if p.Choices != nil && len(p.Choices) > 0 {
		if len(p.Choices) == 1 {
			return fmt.Sprintf("# Enter `%s`", p.Choices[0])
		}
		choices := make([]string, len(p.Choices)-1)
		for i, v := range p.Choices[:len(p.Choices)-1] {
			choices[i] = "`" + v + "`"
		}
		return fmt.Sprintf("# Enter %s or `%s`", strings.Join(choices, ", "), p.Choices[len(p.Choices)-1])
	}
	return ""
}

func (p *Prompter) inputIsValid(input string) bool {
	return p.regexp().MatchString(input)
}

var allReg = regexp.MustCompile(`.*`)

func (p *Prompter) regexp() *regexp.Regexp {
	if p.Regexp != nil {
		return p.Regexp
	}
	if p.reg != nil {
		return p.reg
	}
	if p.Choices == nil || len(p.Choices) == 0 {
		p.reg = allReg
		return p.reg
	}

	choices := make([]string, len(p.Choices))
	for i, v := range p.Choices {
		choices[i] = regexp.QuoteMeta(v)
	}
	ignoreReg := ""
	if p.IgnoreCase {
		ignoreReg = "(?i)"
	}
	p.reg = regexp.MustCompile(fmt.Sprintf(`%s\A(?:%s)\z`, ignoreReg, strings.Join(choices, "|")))
	return p.reg
}
<|endoftext|>


<commit_before>package outreach

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strconv"
)

type Prospect struct {
	Data ProspectData `json:"data"`
}

type ProspectData struct {
	Attributes ProspectAttributes `json:"attributes"`
}

type ProspectAttributes struct {
	Address  ProspectAddress  `json:"address,omitempty"`
	Company  ProspectCompany  `json:"company,omitempty"`
	Contact  ProspectContact  `json:"contact"`
	Personal ProspectPersonal `json:"personal"`
	Social   ProspectSocial   `json:"socila,omitempty"`
	Meta     ProspectMeta     `json:"metadata,omitempty"`
}

type ProspectAddress struct {
	City    string   `json:"city,omitempty"`
	State   string   `json:"state,omitempty"`
	Country string   `json:"country,omitempty"`
	Street  []string `json:"street,omitempty"`
	Zip     string   `json:"zip,omitempty"`
}

type ProspectCompany struct {
	Name     string `json:"name,omitempty"`
	Type     string `json:"type,omitempty"`
	Industry string `json:"industry,omitempty"`
	Size     string `json:"size,omitempty"`
	Locality string `json:"locality,omitempty"`
}

type ProspectContact struct {
	Timezone string        `json:"timezone,omitempty"`
	Email    string        `json:"email"`
	Phone    ProspectPhone `json:"phone,omitempty"`
}

type ProspectPhone struct {
	Personal string `json:"personal,omitempty"`
	Work     string `json:"work,omitempty"`
}

type ProspectPersonal struct {
	Name       ProspectName `json:"name"`
	Gender     string       `json:"gender,omitempty"`
	Occupation string       `json:"occupation,omitempty"`
	Title      string       `json:"title,omitempty"`
}

type ProspectName struct {
	First string `json:"first"`
	Last  string `json:"last"`
}

type ProspectSocial struct {
	Website  string `json:"website,omitempty"`
	Facebook string `json:"facebook,omitempty"`
	LinkedIn string `json:"linkedin,omitempty"`
	Plus     string `json:"plus,omitempty"`
	Quora    string `json:"quora,omitempty"`
	Twitter  string `json:"twitter,omitempty"`
}

type ProspectMeta struct {
	OptOut bool     `json:"opted_out"`
	Source string   `json:"source,omitempty"`
	Notes  []string `json:"notes,omitempty"`
	Tags   []string `json:"tags,omitempty"`
	Custom []string `json:"custom,omitempty"`
}

type ProspectResponse struct {
	Data   ProspectResponseData
	Errors []map[string]interface{}
}

type ProspectResponseData struct {
	ID int
}

type ProspectInstance struct {
	Client *http.Client
}

func (p *Prospect) Read(t []byte) (int, error) {
	j, e := json.Marshal(p)
	if e != nil {
		return 0, e
	}

	if len(t) < len(j) {
		return len(t), nil
	}

	copy(t, j)
	return len(j), io.EOF
}

func (p *Prospect) Close() error {
	return nil
}

func (i *ProspectInstance) Get(id int) (Prospect, error) {
	p := Prospect{}

	if i.Client == nil {
		return p, errors.New("You must assign a HTTP client.")
	}

	resp, e := i.Client.Get("https://api.outreach.io/1.0/prospect/" + strconv.Itoa(id))
	if e != nil {
		return p, e
	}

	body, e := ioutil.ReadAll(resp.Body)
	if e != nil {
		return p, e
	}

	e = json.Unmarshal(body, &p)
	if e != nil {
		return p, e
	}

	return p, nil
}

func (i *ProspectInstance) Post(p Prospect) (ProspectResponse, error) {
	pr := ProspectResponse{}

	if i.Client == nil {
		return pr, errors.New("You must assign a HTTP client.")
	}

	resp, e := i.Client.Post("https://api.outreach.io/1.0/prospect", "application/json", &p)
	if e != nil {
		return pr, e
	}

	body, e := ioutil.ReadAll(resp.Body)
	if e != nil {
		return pr, e
	}

	e = json.Unmarshal(body, &pr)
	if e != nil {
		return pr, e
	}

	if len(pr.Errors) > 0 {
		return pr, fmt.Errorf("Got error response: %+v\n", pr.Errors)
	}

	return pr, nil
}
<commit_msg>Fixed socila -> social typo in prospect json tags<commit_after>package outreach

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strconv"
)

type Prospect struct {
	Data ProspectData `json:"data"`
}

type ProspectData struct {
	Attributes ProspectAttributes `json:"attributes"`
}

type ProspectAttributes struct {
	Address  ProspectAddress  `json:"address,omitempty"`
	Company  ProspectCompany  `json:"company,omitempty"`
	Contact  ProspectContact  `json:"contact"`
	Personal ProspectPersonal `json:"personal"`
	Social   ProspectSocial   `json:"social,omitempty"`
	Meta     ProspectMeta     `json:"metadata,omitempty"`
}

type ProspectAddress struct {
	City    string   `json:"city,omitempty"`
	State   string   `json:"state,omitempty"`
	Country string   `json:"country,omitempty"`
	Street  []string `json:"street,omitempty"`
	Zip     string   `json:"zip,omitempty"`
}

type ProspectCompany struct {
	Name     string `json:"name,omitempty"`
	Type     string `json:"type,omitempty"`
	Industry string `json:"industry,omitempty"`
	Size     string `json:"size,omitempty"`
	Locality string `json:"locality,omitempty"`
}

type ProspectContact struct {
	Timezone string        `json:"timezone,omitempty"`
	Email    string        `json:"email"`
	Phone    ProspectPhone `json:"phone,omitempty"`
}

type ProspectPhone struct {
	Personal string `json:"personal,omitempty"`
	Work     string `json:"work,omitempty"`
}

type ProspectPersonal struct {
	Name       ProspectName `json:"name"`
	Gender     string       `json:"gender,omitempty"`
	Occupation string       `json:"occupation,omitempty"`
	Title      string       `json:"title,omitempty"`
}

type ProspectName struct {
	First string `json:"first"`
	Last  string `json:"last"`
}

type ProspectSocial struct {
	Website  string `json:"website,omitempty"`
	Facebook string `json:"facebook,omitempty"`
	LinkedIn string `json:"linkedin,omitempty"`
	Plus     string `json:"plus,omitempty"`
	Quora    string `json:"quora,omitempty"`
	Twitter  string `json:"twitter,omitempty"`
}

type ProspectMeta struct {
	OptOut bool     `json:"opted_out"`
	Source string   `json:"source,omitempty"`
	Notes  []string `json:"notes,omitempty"`
	Tags   []string `json:"tags,omitempty"`
	Custom []string `json:"custom,omitempty"`
}

type ProspectResponse struct {
	Data   ProspectResponseData
	Errors []map[string]interface{}
}

type ProspectResponseData struct {
	ID int
}

type ProspectInstance struct {
	Client *http.Client
}

func (p *Prospect) Read(t []byte) (int, error) {
	j, e := json.Marshal(p)
	if e != nil {
		return 0, e
	}

	if len(t) < len(j) {
		return len(t), nil
	}

	copy(t, j)
	return len(j), io.EOF
}

func (p *Prospect) Close() error {
	return nil
}

func (i *ProspectInstance) Get(id int) (Prospect, error) {
	p := Prospect{}

	if i.Client == nil {
		return p, errors.New("You must assign a HTTP client.")
	}

	resp, e := i.Client.Get("https://api.outreach.io/1.0/prospect/" + strconv.Itoa(id))
	if e != nil {
		return p, e
	}

	body, e := ioutil.ReadAll(resp.Body)
	if e != nil {
		return p, e
	}

	e = json.Unmarshal(body, &p)
	if e != nil {
		return p, e
	}

	return p, nil
}

func (i *ProspectInstance) Post(p Prospect) (ProspectResponse, error) {
	pr := ProspectResponse{}

	if i.Client == nil {
		return pr, errors.New("You must assign a HTTP client.")
	}

	resp, e := i.Client.Post("https://api.outreach.io/1.0/prospect", "application/json", &p)
	if e != nil {
		return pr, e
	}

	body, e := ioutil.ReadAll(resp.Body)
	if e != nil {
		return pr, e
	}

	e = json.Unmarshal(body, &pr)
	if e != nil {
		return pr, e
	}

	if len(pr.Errors) > 0 {
		return pr, fmt.Errorf("Got error response: %+v\n", pr.Errors)
	}

	return pr, nil
}
<|endoftext|>


<commit_before>package main

import (
    "bufio"
    "container/list"
    "fmt"
    "os"
    "regexp"
    "strconv"
    "strings"
)

type Editor struct {
    buffer *list.List
    filename string
    line int
    modified bool
    err string
    commands map[rune]func(int, int, rune, string)
}

func NewEditor() *Editor {
    e := &Editor{}
    e.buffer = list.New()
    e.line = 1

    e.commands = map[rune]func(int, int, rune, string){
        'p': e.Print,
        'n': e.Print,
        'i': e.Insert,
        'a': e.Insert,
        'd': e.Delete,
        'c': e.Change,
        'e': e.OpenWrapper,
        'E': e.OpenWrapper,
        's': e.ReSub,
        'w': e.Write,
        'h': e.Help,
        'q': e.Quit,
        'Q': e.Quit,
    }

    return e
}

func (e *Editor) isModified() bool {
    if e.modified {
        e.Error("warning: file modified")
        e.modified = false
        return true
    } else {
        return false
    }
}

func (e *Editor) Index(idx int) *list.Element {
    for i, l := 0, e.buffer.Front(); l != nil; i, l = i+1, l.Next() {
        if i == idx {
            return l
        }
    }

    return nil
}

func (e *Editor) OpenWrapper(start, end int, cmd rune, text string) {
    args := strings.Split(text, " ")
    filename := ""

    if len(args) == 1 {
        return
    }

    if cmd != 'E' {
        if e.isModified() {
            return
        }
    }

    filename = args[1]
    e.Open(filename)
}

func (e *Editor) Open(filename string) {
    file, err := os.Open(filename)
    defer file.Close()

    if err != nil {
        fmt.Println(err)
        e.Error("cannot open input file")
        return
    }

    e.buffer = list.New()
    e.filename = filename
    e.modified = false
    size := 0

    scanner := bufio.NewScanner(file)

    for i := 1; scanner.Scan(); i++ {
        text := scanner.Text()
        size += len(text) + 1
        e.buffer.PushBack(text)

        e.line = i
    }

    fmt.Println(size)
}

func (e *Editor) Write(start, end int, cmd rune, text string) {
    args := strings.Split(text, " ")

    if len(args) > 1 {
        e.filename = args[1]
    }

    if len(e.filename) == 0 {
        e.Error("no current filename")
        return
    }

    file, err := os.Create(e.filename)
    defer file.Close()

    if err != nil {
        fmt.Println(err)
        e.Error("cannot write to file")
        return
    }

    size := 0

    for l := e.buffer.Front(); l != nil; l = l.Next() {
        text := l.Value.(string)
        count, _ := file.WriteString(text + "\n")
        size += count
    }

    e.modified = false
    fmt.Println(size)
}

func (e *Editor) Print(start, end int, cmd rune, text string) {
    for i, l := 1, e.buffer.Front(); l != nil; i, l = i+1, l.Next() {
        if i >= start && i <= end {
            if cmd == 'n' {
                fmt.Printf("%d\t%s\n", i, l.Value)
            } else {
                fmt.Println(l.Value)
            }

            e.line = i
        }
    }
}

func readLine() string {
    scanner := bufio.NewScanner(os.Stdin)
    scanner.Scan()
    return scanner.Text()
}

func readLines() *list.List {
    input := list.New()

    scanner := bufio.NewScanner(os.Stdin)
    for scanner.Scan() {
        text := scanner.Text()

        if text == "." {
            break
        }

        input.PushBack(text)
    }

    return input
}

func (e *Editor) InsertBefore(other *list.List, line int) {
    node := e.Index(line-1)

    for i, l := other.Len(), other.Back(); i > 0; i, l = i-1, l.Prev() {
        e.buffer.InsertBefore(l.Value, node)
        node = node.Prev()
    }

    e.setLine(e.line + other.Len() - 1)
}

func (e *Editor) InsertAfter(other *list.List, line int) {
    node := e.Index(line-1)

    for i, l := 0, other.Front(); i < other.Len(); i, l = i+1, l.Next() {
        e.buffer.InsertAfter(l.Value, node)
        node = node.Next()
        e.setLine(e.line+1)
    }

    e.setLine(other.Len() + e.line)
}

func (e *Editor) Insert(start, end int, cmd rune, text string) {
    input := readLines()
    e.setLine(end)

    if e.buffer.Len() == 0 {
        e.buffer.PushBackList(input)
        e.setLine(e.line + input.Len())
    } else {
        if cmd == 'i' {
            // edge case
            if end >= e.buffer.Len() {
                e.buffer.PushBackList(input)
                e.setLine(e.line + input.Len())
            } else {
                e.InsertBefore(input, end)
            }
        } else {
            e.InsertAfter(input, end)
        }
    }

    e.modified = true
}

func (e *Editor) setLine(line int) {
    if line > e.buffer.Len() {
        e.line = e.buffer.Len()
    } else if line <= 0 {
        e.line = 1
    } else {
        e.line = line
    }
}

func (e *Editor) Delete(start, end int, cmd rune, text string) {
    curr := e.Index(start-1)

    for i := start; i <= end; i++ {
        next := curr.Next()
        e.buffer.Remove(curr)
        curr = next
    }

    e.setLine(start)
    e.modified = true
}

func (e *Editor) Change(start, end int, cmd rune, text string) {
    e.Delete(start, end, cmd, text)
    e.Insert(start, end, 'i', text)
}

func (e *Editor) Error(msg string) {
    e.err = msg
    fmt.Println("?")
}

func (e *Editor) replaceMacros(text string) string {
    macros := map[string]int{
        ".": e.line,
        "+": e.line+1,
        "-": e.line-1,
        "$": e.buffer.Len(),
    }

    for key, value := range macros {
        text = strings.Replace(text, key, strconv.Itoa(value), -1)
    }

    return text
}

func (e *Editor) ReSub(start, end int, cmd rune, text string) {
    parts := strings.Split(text, "/")

    if len(parts) != 4 {
        e.Error("no match")
        return
    }

    match := parts[1]
    replace := parts[2]
    flags := parts[3]

    if strings.ContainsRune(flags, 'i') {
        match = "(?i)" + match
    }

    re, err := regexp.Compile(match)

    if err != nil {
        e.Error("no match")
        return
    }

    for i, l := 1, e.buffer.Front(); l != nil; i, l = i+1, l.Next() {
        if i >= start && i <= end {
            line := l.Value.(string)
            l.Value = re.ReplaceAllString(line, replace)

            e.line = i
        }
    }

    e.modified = true
}

func (e *Editor) Quit(start, end int, cmd rune, text string) {
    if cmd == 'Q' || !e.isModified() {
        os.Exit(0)
    }
}

func (e *Editor) Help(start, end int, cmd rune, text string) {
    if len(e.err) > 0 {
        fmt.Println(e.err)
    }
}

func (e *Editor) Parse(text string) (int, int, string) {
    if len(text) == 0 {
        return e.line+1, e.line+1, "p"
    }

    index := -1
    for i, c := range text {
        if _, ok := e.commands[c]; ok {
            index = i
            break
        }
    }

    if index == 0 {
        return e.line, e.line, text
    }

    var nrange, rest string

    if index == -1 {
        nrange = text
    } else {
        nrange = text[:index]
    }

    nrange = e.replaceMacros(nrange)

    nums := strings.Split(nrange, ",")
    start := 0
    end := 0

    if nrange == "," {
        start = 1
        end = e.buffer.Len()
    } else if len(nums) == 2 {
        start, _ = strconv.Atoi(nums[0])
        end, _ = strconv.Atoi(nums[1])
    } else if len(nums) == 1 {
        start, _ = strconv.Atoi(nums[0])
        end = start
    }

    if start == 0 && end == 0 {
        // Invalid input
    } else if index == -1 {
        rest = "p"
    } else {
        rest = text[index:]
    }

    return start, end, rest
}

func (e *Editor) Prompt() {
    text := readLine()
    start, end, text := e.Parse(text)

    if text == "" {
        e.Error("unknown command")
        return
    }

    // Special check when working on an empty buffer
    if (e.buffer.Len() != 0 && start != 1) &&
           (start < 1 || end > e.buffer.Len() ||
           start > end) {
        e.Error("invalid address")
        return
    }

    command := rune(text[0])

    if fn, ok := e.commands[command]; ok {
        fn(start, end, command, text)
    } else {
        e.Error("unknown command")
    }
}

func main() {
    editor := NewEditor()

    if len(os.Args) > 1 {
        editor.Open(os.Args[1])
    }

    for {
        editor.Prompt()
    }
}
<commit_msg>can use % to represent all lines<commit_after>package main

import (
    "bufio"
    "container/list"
    "fmt"
    "os"
    "regexp"
    "strconv"
    "strings"
)

type Editor struct {
    buffer *list.List
    filename string
    line int
    modified bool
    err string
    commands map[rune]func(int, int, rune, string)
}

func NewEditor() *Editor {
    e := &Editor{}
    e.buffer = list.New()
    e.line = 1

    e.commands = map[rune]func(int, int, rune, string){
        'p': e.Print,
        'n': e.Print,
        'i': e.Insert,
        'a': e.Insert,
        'd': e.Delete,
        'c': e.Change,
        'e': e.OpenWrapper,
        'E': e.OpenWrapper,
        's': e.ReSub,
        'w': e.Write,
        'h': e.Help,
        'q': e.Quit,
        'Q': e.Quit,
    }

    return e
}

func (e *Editor) isModified() bool {
    if e.modified {
        e.Error("warning: file modified")
        e.modified = false
        return true
    } else {
        return false
    }
}

func (e *Editor) Index(idx int) *list.Element {
    for i, l := 0, e.buffer.Front(); l != nil; i, l = i+1, l.Next() {
        if i == idx {
            return l
        }
    }

    return nil
}

func (e *Editor) OpenWrapper(start, end int, cmd rune, text string) {
    args := strings.Split(text, " ")
    filename := ""

    if len(args) == 1 {
        return
    }

    if cmd != 'E' {
        if e.isModified() {
            return
        }
    }

    filename = args[1]
    e.Open(filename)
}

func (e *Editor) Open(filename string) {
    file, err := os.Open(filename)
    defer file.Close()

    if err != nil {
        fmt.Println(err)
        e.Error("cannot open input file")
        return
    }

    e.buffer = list.New()
    e.filename = filename
    e.modified = false
    size := 0

    scanner := bufio.NewScanner(file)

    for i := 1; scanner.Scan(); i++ {
        text := scanner.Text()
        size += len(text) + 1
        e.buffer.PushBack(text)

        e.line = i
    }

    fmt.Println(size)
}

func (e *Editor) Write(start, end int, cmd rune, text string) {
    args := strings.Split(text, " ")

    if len(args) > 1 {
        e.filename = args[1]
    }

    if len(e.filename) == 0 {
        e.Error("no current filename")
        return
    }

    file, err := os.Create(e.filename)
    defer file.Close()

    if err != nil {
        fmt.Println(err)
        e.Error("cannot write to file")
        return
    }

    size := 0

    for l := e.buffer.Front(); l != nil; l = l.Next() {
        text := l.Value.(string)
        count, _ := file.WriteString(text + "\n")
        size += count
    }

    e.modified = false
    fmt.Println(size)
}

func (e *Editor) Print(start, end int, cmd rune, text string) {
    for i, l := 1, e.buffer.Front(); l != nil; i, l = i+1, l.Next() {
        if i >= start && i <= end {
            if cmd == 'n' {
                fmt.Printf("%d\t%s\n", i, l.Value)
            } else {
                fmt.Println(l.Value)
            }

            e.line = i
        }
    }
}

func readLine() string {
    scanner := bufio.NewScanner(os.Stdin)
    scanner.Scan()
    return scanner.Text()
}

func readLines() *list.List {
    input := list.New()

    scanner := bufio.NewScanner(os.Stdin)
    for scanner.Scan() {
        text := scanner.Text()

        if text == "." {
            break
        }

        input.PushBack(text)
    }

    return input
}

func (e *Editor) InsertBefore(other *list.List, line int) {
    node := e.Index(line-1)

    for i, l := other.Len(), other.Back(); i > 0; i, l = i-1, l.Prev() {
        e.buffer.InsertBefore(l.Value, node)
        node = node.Prev()
    }

    e.setLine(e.line + other.Len() - 1)
}

func (e *Editor) InsertAfter(other *list.List, line int) {
    node := e.Index(line-1)

    for i, l := 0, other.Front(); i < other.Len(); i, l = i+1, l.Next() {
        e.buffer.InsertAfter(l.Value, node)
        node = node.Next()
        e.setLine(e.line+1)
    }

    e.setLine(other.Len() + e.line)
}

func (e *Editor) Insert(start, end int, cmd rune, text string) {
    input := readLines()
    e.setLine(end)

    if e.buffer.Len() == 0 {
        e.buffer.PushBackList(input)
        e.setLine(e.line + input.Len())
    } else {
        if cmd == 'i' {
            // edge case
            if end >= e.buffer.Len() {
                e.buffer.PushBackList(input)
                e.setLine(e.line + input.Len())
            } else {
                e.InsertBefore(input, end)
            }
        } else {
            e.InsertAfter(input, end)
        }
    }

    e.modified = true
}

func (e *Editor) setLine(line int) {
    if line > e.buffer.Len() {
        e.line = e.buffer.Len()
    } else if line <= 0 {
        e.line = 1
    } else {
        e.line = line
    }
}

func (e *Editor) Delete(start, end int, cmd rune, text string) {
    curr := e.Index(start-1)

    for i := start; i <= end; i++ {
        next := curr.Next()
        e.buffer.Remove(curr)
        curr = next
    }

    e.setLine(start)
    e.modified = true
}

func (e *Editor) Change(start, end int, cmd rune, text string) {
    e.Delete(start, end, cmd, text)
    e.Insert(start, end, 'i', text)
}

func (e *Editor) Error(msg string) {
    e.err = msg
    fmt.Println("?")
}

func (e *Editor) replaceMacros(text string) string {
    macros := map[string]int{
        ".": e.line,
        "+": e.line+1,
        "-": e.line-1,
        "$": e.buffer.Len(),
    }

    for key, value := range macros {
        text = strings.Replace(text, key, strconv.Itoa(value), -1)
    }

    return text
}

func (e *Editor) ReSub(start, end int, cmd rune, text string) {
    parts := strings.Split(text, "/")

    if len(parts) != 4 {
        e.Error("no match")
        return
    }

    match := parts[1]
    replace := parts[2]
    flags := parts[3]

    if strings.ContainsRune(flags, 'i') {
        match = "(?i)" + match
    }

    re, err := regexp.Compile(match)

    if err != nil {
        e.Error("no match")
        return
    }

    for i, l := 1, e.buffer.Front(); l != nil; i, l = i+1, l.Next() {
        if i >= start && i <= end {
            line := l.Value.(string)
            l.Value = re.ReplaceAllString(line, replace)

            e.line = i
        }
    }

    e.modified = true
}

func (e *Editor) Quit(start, end int, cmd rune, text string) {
    if cmd == 'Q' || !e.isModified() {
        os.Exit(0)
    }
}

func (e *Editor) Help(start, end int, cmd rune, text string) {
    if len(e.err) > 0 {
        fmt.Println(e.err)
    }
}

func (e *Editor) Parse(text string) (int, int, string) {
    if len(text) == 0 {
        return e.line+1, e.line+1, "p"
    }

    index := -1
    for i, c := range text {
        if _, ok := e.commands[c]; ok {
            index = i
            break
        }
    }

    if index == 0 {
        return e.line, e.line, text
    }

    var nrange, rest string

    if index == -1 {
        nrange = text
    } else {
        nrange = text[:index]
    }

    nrange = e.replaceMacros(nrange)

    nums := strings.Split(nrange, ",")
    start := 0
    end := 0

    if nrange == "," || nrange == "%" {
        start = 1
        end = e.buffer.Len()
    } else if len(nums) == 2 {
        start, _ = strconv.Atoi(nums[0])
        end, _ = strconv.Atoi(nums[1])
    } else if len(nums) == 1 {
        start, _ = strconv.Atoi(nums[0])
        end = start
    }

    if start == 0 && end == 0 {
        // Invalid input
    } else if index == -1 {
        rest = "p"
    } else {
        rest = text[index:]
    }

    return start, end, rest
}

func (e *Editor) Prompt() {
    text := readLine()
    start, end, text := e.Parse(text)

    if text == "" {
        e.Error("unknown command")
        return
    }

    // Special check when working on an empty buffer
    if (e.buffer.Len() != 0 && start != 1) &&
           (start < 1 || end > e.buffer.Len() ||
           start > end) {
        e.Error("invalid address")
        return
    }

    command := rune(text[0])

    if fn, ok := e.commands[command]; ok {
        fn(start, end, command, text)
    } else {
        e.Error("unknown command")
    }
}

func main() {
    editor := NewEditor()

    if len(os.Args) > 1 {
        editor.Open(os.Args[1])
    }

    for {
        editor.Prompt()
    }
}
<|endoftext|>