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

package gonum

import (
	"math"

	"gonum.org/v1/gonum/blas/blas64"
)

// Dlasy2 solves the Sylvester matrix equation where the matrices are of order 1
// or 2. It computes the unknown n1×n2 matrix X so that
//  TL*X   + sgn*X*TR  = scale*B  if tranl == false and tranr == false,
//  TLᵀ*X + sgn*X*TR   = scale*B  if tranl == true  and tranr == false,
//  TL*X   + sgn*X*TRᵀ = scale*B  if tranl == false and tranr == true,
//  TLᵀ*X + sgn*X*TRᵀ  = scale*B  if tranl == true  and tranr == true,
// where TL is n1×n1, TR is n2×n2, B is n1×n2, and 1 <= n1,n2 <= 2.
//
// isgn must be 1 or -1, and n1 and n2 must be 0, 1, or 2, but these conditions
// are not checked.
//
// Dlasy2 returns three values, a scale factor that is chosen less than or equal
// to 1 to prevent the solution overflowing, the infinity norm of the solution,
// and an indicator of success. If ok is false, TL and TR have eigenvalues that
// are too close, so TL or TR is perturbed to get a non-singular equation.
//
// Dlasy2 is an internal routine. It is exported for testing purposes.
func (impl Implementation) Dlasy2(tranl, tranr bool, isgn, n1, n2 int, tl []float64, ldtl int, tr []float64, ldtr int, b []float64, ldb int, x []float64, ldx int) (scale, xnorm float64, ok bool) {
	// TODO(vladimir-ch): Add input validation checks conditionally skipped
	// using the build tag mechanism.

	ok = true
	// Quick return if possible.
	if n1 == 0 || n2 == 0 {
		return scale, xnorm, ok
	}

	// Set constants to control overflow.
	eps := dlamchP
	smlnum := dlamchS / eps
	sgn := float64(isgn)

	if n1 == 1 && n2 == 1 {
		// 1×1 case: TL11*X + sgn*X*TR11 = B11.
		tau1 := tl[0] + sgn*tr[0]
		bet := math.Abs(tau1)
		if bet <= smlnum {
			tau1 = smlnum
			bet = smlnum
			ok = false
		}
		scale = 1
		gam := math.Abs(b[0])
		if smlnum*gam > bet {
			scale = 1 / gam
		}
		x[0] = b[0] * scale / tau1
		xnorm = math.Abs(x[0])
		return scale, xnorm, ok
	}

	if n1+n2 == 3 {
		// 1×2 or 2×1 case.
		var (
			smin float64
			tmp  [4]float64 // tmp is used as a 2×2 row-major matrix.
			btmp [2]float64
		)
		if n1 == 1 && n2 == 2 {
			// 1×2 case: TL11*[X11 X12] + sgn*[X11 X12]*op[TR11 TR12] = [B11 B12].
			//                                            [TR21 TR22]
			smin = math.Abs(tl[0])
			smin = math.Max(smin, math.Max(math.Abs(tr[0]), math.Abs(tr[1])))
			smin = math.Max(smin, math.Max(math.Abs(tr[ldtr]), math.Abs(tr[ldtr+1])))
			smin = math.Max(eps*smin, smlnum)
			tmp[0] = tl[0] + sgn*tr[0]
			tmp[3] = tl[0] + sgn*tr[ldtr+1]
			if tranr {
				tmp[1] = sgn * tr[1]
				tmp[2] = sgn * tr[ldtr]
			} else {
				tmp[1] = sgn * tr[ldtr]
				tmp[2] = sgn * tr[1]
			}
			btmp[0] = b[0]
			btmp[1] = b[1]
		} else {
			// 2×1 case: op[TL11 TL12]*[X11] + sgn*[X11]*TR11 = [B11].
			//             [TL21 TL22]*[X21]       [X21]        [B21]
			smin = math.Abs(tr[0])
			smin = math.Max(smin, math.Max(math.Abs(tl[0]), math.Abs(tl[1])))
			smin = math.Max(smin, math.Max(math.Abs(tl[ldtl]), math.Abs(tl[ldtl+1])))
			smin = math.Max(eps*smin, smlnum)
			tmp[0] = tl[0] + sgn*tr[0]
			tmp[3] = tl[ldtl+1] + sgn*tr[0]
			if tranl {
				tmp[1] = tl[ldtl]
				tmp[2] = tl[1]
			} else {
				tmp[1] = tl[1]
				tmp[2] = tl[ldtl]
			}
			btmp[0] = b[0]
			btmp[1] = b[ldb]
		}

		// Solve 2×2 system using complete pivoting.
		// Set pivots less than smin to smin.

		bi := blas64.Implementation()
		ipiv := bi.Idamax(len(tmp), tmp[:], 1)
		// Compute the upper triangular matrix [u11 u12].
		//                                     [  0 u22]
		u11 := tmp[ipiv]
		if math.Abs(u11) <= smin {
			ok = false
			u11 = smin
		}
		locu12 := [4]int{1, 0, 3, 2} // Index in tmp of the element on the same row as the pivot.
		u12 := tmp[locu12[ipiv]]
		locl21 := [4]int{2, 3, 0, 1} // Index in tmp of the element on the same column as the pivot.
		l21 := tmp[locl21[ipiv]] / u11
		locu22 := [4]int{3, 2, 1, 0} // Index in tmp of the remaining element.
		u22 := tmp[locu22[ipiv]] - l21*u12
		if math.Abs(u22) <= smin {
			ok = false
			u22 = smin
		}
		if ipiv&0x2 != 0 { // true for ipiv equal to 2 and 3.
			// The pivot was in the second row, swap the elements of
			// the right-hand side.
			btmp[0], btmp[1] = btmp[1], btmp[0]-l21*btmp[1]
		} else {
			btmp[1] -= l21 * btmp[0]
		}
		scale = 1
		if 2*smlnum*math.Abs(btmp[1]) > math.Abs(u22) || 2*smlnum*math.Abs(btmp[0]) > math.Abs(u11) {
			scale = 0.5 / math.Max(math.Abs(btmp[0]), math.Abs(btmp[1]))
			btmp[0] *= scale
			btmp[1] *= scale
		}
		// Solve the system [u11 u12] [x21] = [ btmp[0] ].
		//                  [  0 u22] [x22]   [ btmp[1] ]
		x22 := btmp[1] / u22
		x21 := btmp[0]/u11 - (u12/u11)*x22
		if ipiv&0x1 != 0 { // true for ipiv equal to 1 and 3.
			// The pivot was in the second column, swap the elements
			// of the solution.
			x21, x22 = x22, x21
		}
		x[0] = x21
		if n1 == 1 {
			x[1] = x22
			xnorm = math.Abs(x[0]) + math.Abs(x[1])
		} else {
			x[ldx] = x22
			xnorm = math.Max(math.Abs(x[0]), math.Abs(x[ldx]))
		}
		return scale, xnorm, ok
	}

	// 2×2 case: op[TL11 TL12]*[X11 X12] + SGN*[X11 X12]*op[TR11 TR12] = [B11 B12].
	//             [TL21 TL22] [X21 X22]       [X21 X22]   [TR21 TR22]   [B21 B22]
	//
	// Solve equivalent 4×4 system using complete pivoting.
	// Set pivots less than smin to smin.

	smin := math.Max(math.Abs(tr[0]), math.Abs(tr[1]))
	smin = math.Max(smin, math.Max(math.Abs(tr[ldtr]), math.Abs(tr[ldtr+1])))
	smin = math.Max(smin, math.Max(math.Abs(tl[0]), math.Abs(tl[1])))
	smin = math.Max(smin, math.Max(math.Abs(tl[ldtl]), math.Abs(tl[ldtl+1])))
	smin = math.Max(eps*smin, smlnum)

	var t [4][4]float64
	t[0][0] = tl[0] + sgn*tr[0]
	t[1][1] = tl[0] + sgn*tr[ldtr+1]
	t[2][2] = tl[ldtl+1] + sgn*tr[0]
	t[3][3] = tl[ldtl+1] + sgn*tr[ldtr+1]
	if tranl {
		t[0][2] = tl[ldtl]
		t[1][3] = tl[ldtl]
		t[2][0] = tl[1]
		t[3][1] = tl[1]
	} else {
		t[0][2] = tl[1]
		t[1][3] = tl[1]
		t[2][0] = tl[ldtl]
		t[3][1] = tl[ldtl]
	}
	if tranr {
		t[0][1] = sgn * tr[1]
		t[1][0] = sgn * tr[ldtr]
		t[2][3] = sgn * tr[1]
		t[3][2] = sgn * tr[ldtr]
	} else {
		t[0][1] = sgn * tr[ldtr]
		t[1][0] = sgn * tr[1]
		t[2][3] = sgn * tr[ldtr]
		t[3][2] = sgn * tr[1]
	}

	var btmp [4]float64
	btmp[0] = b[0]
	btmp[1] = b[1]
	btmp[2] = b[ldb]
	btmp[3] = b[ldb+1]

	// Perform elimination.
	var jpiv [4]int // jpiv records any column swaps for pivoting.
	for i := 0; i < 3; i++ {
		var (
			xmax       float64
			ipsv, jpsv int
		)
		for ip := i; ip < 4; ip++ {
			for jp := i; jp < 4; jp++ {
				if math.Abs(t[ip][jp]) >= xmax {
					xmax = math.Abs(t[ip][jp])
					ipsv = ip
					jpsv = jp
				}
			}
		}
		if ipsv != i {
			// The pivot is not in the top row of the unprocessed
			// block, swap rows ipsv and i of t and btmp.
			t[ipsv], t[i] = t[i], t[ipsv]
			btmp[ipsv], btmp[i] = btmp[i], btmp[ipsv]
		}
		if jpsv != i {
			// The pivot is not in the left column of the
			// unprocessed block, swap columns jpsv and i of t.
			for k := 0; k < 4; k++ {
				t[k][jpsv], t[k][i] = t[k][i], t[k][jpsv]
			}
		}
		jpiv[i] = jpsv
		if math.Abs(t[i][i]) < smin {
			ok = false
			t[i][i] = smin
		}
		for k := i + 1; k < 4; k++ {
			t[k][i] /= t[i][i]
			btmp[k] -= t[k][i] * btmp[i]
			for j := i + 1; j < 4; j++ {
				t[k][j] -= t[k][i] * t[i][j]
			}
		}
	}
	if math.Abs(t[3][3]) < smin {
		ok = false
		t[3][3] = smin
	}
	scale = 1
	if 8*smlnum*math.Abs(btmp[0]) > math.Abs(t[0][0]) ||
		8*smlnum*math.Abs(btmp[1]) > math.Abs(t[1][1]) ||
		8*smlnum*math.Abs(btmp[2]) > math.Abs(t[2][2]) ||
		8*smlnum*math.Abs(btmp[3]) > math.Abs(t[3][3]) {

		maxbtmp := math.Max(math.Abs(btmp[0]), math.Abs(btmp[1]))
		maxbtmp = math.Max(maxbtmp, math.Max(math.Abs(btmp[2]), math.Abs(btmp[3])))
		scale = 1 / 8 / maxbtmp
		btmp[0] *= scale
		btmp[1] *= scale
		btmp[2] *= scale
		btmp[3] *= scale
	}
	// Compute the solution of the upper triangular system t * tmp = btmp.
	var tmp [4]float64
	for i := 3; i >= 0; i-- {
		temp := 1 / t[i][i]
		tmp[i] = btmp[i] * temp
		for j := i + 1; j < 4; j++ {
			tmp[i] -= temp * t[i][j] * tmp[j]
		}
	}
	for i := 2; i >= 0; i-- {
		if jpiv[i] != i {
			tmp[i], tmp[jpiv[i]] = tmp[jpiv[i]], tmp[i]
		}
	}
	x[0] = tmp[0]
	x[1] = tmp[1]
	x[ldx] = tmp[2]
	x[ldx+1] = tmp[3]
	xnorm = math.Max(math.Abs(tmp[0])+math.Abs(tmp[1]), math.Abs(tmp[2])+math.Abs(tmp[3]))
	return scale, xnorm, ok
}
<commit_msg>lapack/gonum: prevent division of untyped ints always yielding zero in Dlasy2 (#1653)<commit_after>// Copyright ©2016 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gonum

import (
	"math"

	"gonum.org/v1/gonum/blas/blas64"
)

// Dlasy2 solves the Sylvester matrix equation where the matrices are of order 1
// or 2. It computes the unknown n1×n2 matrix X so that
//  TL*X   + sgn*X*TR  = scale*B  if tranl == false and tranr == false,
//  TLᵀ*X + sgn*X*TR   = scale*B  if tranl == true  and tranr == false,
//  TL*X   + sgn*X*TRᵀ = scale*B  if tranl == false and tranr == true,
//  TLᵀ*X + sgn*X*TRᵀ  = scale*B  if tranl == true  and tranr == true,
// where TL is n1×n1, TR is n2×n2, B is n1×n2, and 1 <= n1,n2 <= 2.
//
// isgn must be 1 or -1, and n1 and n2 must be 0, 1, or 2, but these conditions
// are not checked.
//
// Dlasy2 returns three values, a scale factor that is chosen less than or equal
// to 1 to prevent the solution overflowing, the infinity norm of the solution,
// and an indicator of success. If ok is false, TL and TR have eigenvalues that
// are too close, so TL or TR is perturbed to get a non-singular equation.
//
// Dlasy2 is an internal routine. It is exported for testing purposes.
func (impl Implementation) Dlasy2(tranl, tranr bool, isgn, n1, n2 int, tl []float64, ldtl int, tr []float64, ldtr int, b []float64, ldb int, x []float64, ldx int) (scale, xnorm float64, ok bool) {
	// TODO(vladimir-ch): Add input validation checks conditionally skipped
	// using the build tag mechanism.

	ok = true
	// Quick return if possible.
	if n1 == 0 || n2 == 0 {
		return scale, xnorm, ok
	}

	// Set constants to control overflow.
	eps := dlamchP
	smlnum := dlamchS / eps
	sgn := float64(isgn)

	if n1 == 1 && n2 == 1 {
		// 1×1 case: TL11*X + sgn*X*TR11 = B11.
		tau1 := tl[0] + sgn*tr[0]
		bet := math.Abs(tau1)
		if bet <= smlnum {
			tau1 = smlnum
			bet = smlnum
			ok = false
		}
		scale = 1
		gam := math.Abs(b[0])
		if smlnum*gam > bet {
			scale = 1 / gam
		}
		x[0] = b[0] * scale / tau1
		xnorm = math.Abs(x[0])
		return scale, xnorm, ok
	}

	if n1+n2 == 3 {
		// 1×2 or 2×1 case.
		var (
			smin float64
			tmp  [4]float64 // tmp is used as a 2×2 row-major matrix.
			btmp [2]float64
		)
		if n1 == 1 && n2 == 2 {
			// 1×2 case: TL11*[X11 X12] + sgn*[X11 X12]*op[TR11 TR12] = [B11 B12].
			//                                            [TR21 TR22]
			smin = math.Abs(tl[0])
			smin = math.Max(smin, math.Max(math.Abs(tr[0]), math.Abs(tr[1])))
			smin = math.Max(smin, math.Max(math.Abs(tr[ldtr]), math.Abs(tr[ldtr+1])))
			smin = math.Max(eps*smin, smlnum)
			tmp[0] = tl[0] + sgn*tr[0]
			tmp[3] = tl[0] + sgn*tr[ldtr+1]
			if tranr {
				tmp[1] = sgn * tr[1]
				tmp[2] = sgn * tr[ldtr]
			} else {
				tmp[1] = sgn * tr[ldtr]
				tmp[2] = sgn * tr[1]
			}
			btmp[0] = b[0]
			btmp[1] = b[1]
		} else {
			// 2×1 case: op[TL11 TL12]*[X11] + sgn*[X11]*TR11 = [B11].
			//             [TL21 TL22]*[X21]       [X21]        [B21]
			smin = math.Abs(tr[0])
			smin = math.Max(smin, math.Max(math.Abs(tl[0]), math.Abs(tl[1])))
			smin = math.Max(smin, math.Max(math.Abs(tl[ldtl]), math.Abs(tl[ldtl+1])))
			smin = math.Max(eps*smin, smlnum)
			tmp[0] = tl[0] + sgn*tr[0]
			tmp[3] = tl[ldtl+1] + sgn*tr[0]
			if tranl {
				tmp[1] = tl[ldtl]
				tmp[2] = tl[1]
			} else {
				tmp[1] = tl[1]
				tmp[2] = tl[ldtl]
			}
			btmp[0] = b[0]
			btmp[1] = b[ldb]
		}

		// Solve 2×2 system using complete pivoting.
		// Set pivots less than smin to smin.

		bi := blas64.Implementation()
		ipiv := bi.Idamax(len(tmp), tmp[:], 1)
		// Compute the upper triangular matrix [u11 u12].
		//                                     [  0 u22]
		u11 := tmp[ipiv]
		if math.Abs(u11) <= smin {
			ok = false
			u11 = smin
		}
		locu12 := [4]int{1, 0, 3, 2} // Index in tmp of the element on the same row as the pivot.
		u12 := tmp[locu12[ipiv]]
		locl21 := [4]int{2, 3, 0, 1} // Index in tmp of the element on the same column as the pivot.
		l21 := tmp[locl21[ipiv]] / u11
		locu22 := [4]int{3, 2, 1, 0} // Index in tmp of the remaining element.
		u22 := tmp[locu22[ipiv]] - l21*u12
		if math.Abs(u22) <= smin {
			ok = false
			u22 = smin
		}
		if ipiv&0x2 != 0 { // true for ipiv equal to 2 and 3.
			// The pivot was in the second row, swap the elements of
			// the right-hand side.
			btmp[0], btmp[1] = btmp[1], btmp[0]-l21*btmp[1]
		} else {
			btmp[1] -= l21 * btmp[0]
		}
		scale = 1
		if 2*smlnum*math.Abs(btmp[1]) > math.Abs(u22) || 2*smlnum*math.Abs(btmp[0]) > math.Abs(u11) {
			scale = 0.5 / math.Max(math.Abs(btmp[0]), math.Abs(btmp[1]))
			btmp[0] *= scale
			btmp[1] *= scale
		}
		// Solve the system [u11 u12] [x21] = [ btmp[0] ].
		//                  [  0 u22] [x22]   [ btmp[1] ]
		x22 := btmp[1] / u22
		x21 := btmp[0]/u11 - (u12/u11)*x22
		if ipiv&0x1 != 0 { // true for ipiv equal to 1 and 3.
			// The pivot was in the second column, swap the elements
			// of the solution.
			x21, x22 = x22, x21
		}
		x[0] = x21
		if n1 == 1 {
			x[1] = x22
			xnorm = math.Abs(x[0]) + math.Abs(x[1])
		} else {
			x[ldx] = x22
			xnorm = math.Max(math.Abs(x[0]), math.Abs(x[ldx]))
		}
		return scale, xnorm, ok
	}

	// 2×2 case: op[TL11 TL12]*[X11 X12] + SGN*[X11 X12]*op[TR11 TR12] = [B11 B12].
	//             [TL21 TL22] [X21 X22]       [X21 X22]   [TR21 TR22]   [B21 B22]
	//
	// Solve equivalent 4×4 system using complete pivoting.
	// Set pivots less than smin to smin.

	smin := math.Max(math.Abs(tr[0]), math.Abs(tr[1]))
	smin = math.Max(smin, math.Max(math.Abs(tr[ldtr]), math.Abs(tr[ldtr+1])))
	smin = math.Max(smin, math.Max(math.Abs(tl[0]), math.Abs(tl[1])))
	smin = math.Max(smin, math.Max(math.Abs(tl[ldtl]), math.Abs(tl[ldtl+1])))
	smin = math.Max(eps*smin, smlnum)

	var t [4][4]float64
	t[0][0] = tl[0] + sgn*tr[0]
	t[1][1] = tl[0] + sgn*tr[ldtr+1]
	t[2][2] = tl[ldtl+1] + sgn*tr[0]
	t[3][3] = tl[ldtl+1] + sgn*tr[ldtr+1]
	if tranl {
		t[0][2] = tl[ldtl]
		t[1][3] = tl[ldtl]
		t[2][0] = tl[1]
		t[3][1] = tl[1]
	} else {
		t[0][2] = tl[1]
		t[1][3] = tl[1]
		t[2][0] = tl[ldtl]
		t[3][1] = tl[ldtl]
	}
	if tranr {
		t[0][1] = sgn * tr[1]
		t[1][0] = sgn * tr[ldtr]
		t[2][3] = sgn * tr[1]
		t[3][2] = sgn * tr[ldtr]
	} else {
		t[0][1] = sgn * tr[ldtr]
		t[1][0] = sgn * tr[1]
		t[2][3] = sgn * tr[ldtr]
		t[3][2] = sgn * tr[1]
	}

	var btmp [4]float64
	btmp[0] = b[0]
	btmp[1] = b[1]
	btmp[2] = b[ldb]
	btmp[3] = b[ldb+1]

	// Perform elimination.
	var jpiv [4]int // jpiv records any column swaps for pivoting.
	for i := 0; i < 3; i++ {
		var (
			xmax       float64
			ipsv, jpsv int
		)
		for ip := i; ip < 4; ip++ {
			for jp := i; jp < 4; jp++ {
				if math.Abs(t[ip][jp]) >= xmax {
					xmax = math.Abs(t[ip][jp])
					ipsv = ip
					jpsv = jp
				}
			}
		}
		if ipsv != i {
			// The pivot is not in the top row of the unprocessed
			// block, swap rows ipsv and i of t and btmp.
			t[ipsv], t[i] = t[i], t[ipsv]
			btmp[ipsv], btmp[i] = btmp[i], btmp[ipsv]
		}
		if jpsv != i {
			// The pivot is not in the left column of the
			// unprocessed block, swap columns jpsv and i of t.
			for k := 0; k < 4; k++ {
				t[k][jpsv], t[k][i] = t[k][i], t[k][jpsv]
			}
		}
		jpiv[i] = jpsv
		if math.Abs(t[i][i]) < smin {
			ok = false
			t[i][i] = smin
		}
		for k := i + 1; k < 4; k++ {
			t[k][i] /= t[i][i]
			btmp[k] -= t[k][i] * btmp[i]
			for j := i + 1; j < 4; j++ {
				t[k][j] -= t[k][i] * t[i][j]
			}
		}
	}
	if math.Abs(t[3][3]) < smin {
		ok = false
		t[3][3] = smin
	}
	scale = 1
	if 8*smlnum*math.Abs(btmp[0]) > math.Abs(t[0][0]) ||
		8*smlnum*math.Abs(btmp[1]) > math.Abs(t[1][1]) ||
		8*smlnum*math.Abs(btmp[2]) > math.Abs(t[2][2]) ||
		8*smlnum*math.Abs(btmp[3]) > math.Abs(t[3][3]) {

		maxbtmp := math.Max(math.Abs(btmp[0]), math.Abs(btmp[1]))
		maxbtmp = math.Max(maxbtmp, math.Max(math.Abs(btmp[2]), math.Abs(btmp[3])))
		scale = (1.0 / 8.0) / maxbtmp
		btmp[0] *= scale
		btmp[1] *= scale
		btmp[2] *= scale
		btmp[3] *= scale
	}
	// Compute the solution of the upper triangular system t * tmp = btmp.
	var tmp [4]float64
	for i := 3; i >= 0; i-- {
		temp := 1 / t[i][i]
		tmp[i] = btmp[i] * temp
		for j := i + 1; j < 4; j++ {
			tmp[i] -= temp * t[i][j] * tmp[j]
		}
	}
	for i := 2; i >= 0; i-- {
		if jpiv[i] != i {
			tmp[i], tmp[jpiv[i]] = tmp[jpiv[i]], tmp[i]
		}
	}
	x[0] = tmp[0]
	x[1] = tmp[1]
	x[ldx] = tmp[2]
	x[ldx+1] = tmp[3]
	xnorm = math.Max(math.Abs(tmp[0])+math.Abs(tmp[1]), math.Abs(tmp[2])+math.Abs(tmp[3]))
	return scale, xnorm, ok
}
<|endoftext|>


<commit_before>package dotmatrix

import (
	"bytes"
	"fmt"
	"image"
	"image/color"
	"image/gif"
	"io"
	"time"
)

/*
PlayGIF is an experimental function that will draw each frame of a gif to
the given writer (usually os.Stdout). Terminal codes are used to reposition
the cursor at the beginning of each frame. Delays and disposal methods are
respected.
*/
func PlayGIF(w io.Writer, giff *gif.GIF) error {
	var screen *image.Paletted

	for c := 0; giff.LoopCount == 0 || c < giff.LoopCount; c++ {
		for i := 0; i < len(giff.Image); i++ {
			delay := time.After(time.Duration(giff.Delay[i]) * time.Second / 100)
			frame := convert(giff.Image[i])

			// Always draw the first frame from scratch
			if i == 0 {
				screen = frame
			}

			switch giff.Disposal[i] {
			// Dispose previous essentially means draw then undo
			case gif.DisposalPrevious:
				previous := image.NewPaletted(screen.Bounds(), screen.Palette)
				copy(previous.Pix, screen.Pix)
				drawFrame(screen, frame)
				if err := flush(w, screen); err != nil {
					return err
				}
				screen = previous
				<-delay
			// Dispose background replaces everything just drawn with the background canvas
			case gif.DisposalBackground:
				background := image.NewPaletted(frame.Bounds(), frame.Palette)
				for i := 0; i < len(background.Pix); i++ {
					background.Pix[i] = 2
				}
				drawFrame(screen, frame)
				if err := flush(w, screen); err != nil {
					return err
				}
				<-delay
				drawFrame(screen, background)
				if err := flush(w, screen); err != nil {
					return err
				}
			// Dispose none or undefined means we just draw what we got over top
			default:
				drawFrame(screen, frame)
				if err := flush(w, screen); err != nil {
					return err
				}
				<-delay
			}
		}
	}
	return nil
}

func drawFrame(target *image.Paletted, source image.Image) {
	bounds := source.Bounds()
	for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
		for x := bounds.Min.X; x < bounds.Max.X; x++ {
			c := source.At(x, y)
			if c == color.Transparent {
				continue
			}
			target.Set(x, y, c)
		}
	}
}

func flush(w io.Writer, img image.Image) error {
	var buf bytes.Buffer
	if err := Encode(&buf, img); err != nil {
		return err
	}
	var height int
	for {
		c, err := buf.ReadByte()
		if err == io.EOF {
			break
		}
		if err != nil {
			return err
		}
		if c == '\n' {
			height++
		}
		w.Write([]byte{c})
	}
	w.Write([]byte("\033[999D"))                     // Move the cursor to the beginning of the line
	w.Write([]byte(fmt.Sprintf("\033[%dA", height))) // Move the cursor to the top of the image
	return nil
}
<commit_msg>fixes background gif disponsal<commit_after>package dotmatrix

import (
	"bytes"
	"fmt"
	"image"
	"image/color"
	"image/gif"
	"io"
	"time"
)

/*
PlayGIF is an experimental function that will draw each frame of a gif to
the given writer (usually os.Stdout). Terminal codes are used to reposition
the cursor at the beginning of each frame. Delays and disposal methods are
respected.
*/
func PlayGIF(w io.Writer, giff *gif.GIF) error {
	if len(giff.Image) < 1 {
		return nil
	}

	// The screen is what we flush to the writer on each iteration
	var screen *image.Paletted
	// Only used if we see background disposal methods
	bgPallette := color.Palette{color.Transparent}
	if p, ok := giff.Config.ColorModel.(color.Palette); ok {
		bgPallette = p
	}

	for c := 0; giff.LoopCount == 0 || c < giff.LoopCount; c++ {
		for i := 0; i < len(giff.Image); i++ {
			delay := time.After(time.Duration(giff.Delay[i]) * time.Second / 100)
			frame := convert(giff.Image[i])

			// Always draw the first frame from scratch
			if i == 0 {
				screen = convert(image.NewPaletted(frame.Bounds(), bgPallette))
			}

			switch giff.Disposal[i] {

			// Dispose previous essentially means draw then undo
			case gif.DisposalPrevious:
				temp := image.NewPaletted(screen.Bounds(), screen.Palette)
				copy(temp.Pix, screen.Pix)

				drawOver(screen, frame)
				if err := flush(w, screen); err != nil {
					return err
				}
				<-delay

				screen = temp

			// Dispose background replaces everything just drawn with the background canvas
			case gif.DisposalBackground:
				background := convert(image.NewPaletted(frame.Bounds(), bgPallette))
				drawExact(screen, background)
				temp := image.NewPaletted(screen.Bounds(), screen.Palette)
				copy(temp.Pix, screen.Pix)

				drawOver(screen, frame)
				if err := flush(w, screen); err != nil {
					return err
				}
				<-delay

				screen = temp

			// Dispose none or undefined means we just draw what we got over top
			default:
				drawOver(screen, frame)
				if err := flush(w, screen); err != nil {
					return err
				}
				<-delay
			}
		}
	}
	return nil
}

// Draws any non-transparent pixels into target
func drawOver(target *image.Paletted, source image.Image) {
	bounds := source.Bounds()
	for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
		for x := bounds.Min.X; x < bounds.Max.X; x++ {
			c := source.At(x, y)
			if c == color.Transparent {
				continue
			}
			target.Set(x, y, c)
		}
	}
}

// Draws pixels into target, including transparent ones.
func drawExact(target *image.Paletted, source image.Image) {
	bounds := source.Bounds()
	for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
		for x := bounds.Min.X; x < bounds.Max.X; x++ {
			target.Set(x, y, source.At(x, y))
		}
	}
}

func flush(w io.Writer, img image.Image) error {
	var buf bytes.Buffer
	if err := Encode(&buf, img); err != nil {
		return err
	}
	var height int
	for {
		c, err := buf.ReadByte()
		if err == io.EOF {
			break
		}
		if err != nil {
			return err
		}
		if c == '\n' {
			height++
		}
		w.Write([]byte{c})
	}
	w.Write([]byte("\033[999D"))                     // Move the cursor to the beginning of the line
	w.Write([]byte(fmt.Sprintf("\033[%dA", height))) // Move the cursor to the top of the image
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/xorpaul/uiprogress"
)

func resolveGitRepositories(uniqueGitModules map[string]GitModule) {
	defer timeTrack(time.Now(), funcName())
	if len(uniqueGitModules) <= 0 {
		Debugf("uniqueGitModules[] is empty, skipping...")
		return
	}
	bar := uiprogress.AddBar(len(uniqueGitModules)).AppendCompleted().PrependElapsed()
	bar.PrependFunc(func(b *uiprogress.Bar) string {
		return fmt.Sprintf("Resolving Git modules (%d/%d)", b.Current(), len(uniqueGitModules))
	})
	// Dummy channel to coordinate the number of concurrent goroutines.
	// This channel should be buffered otherwise we will be immediately blocked
	// when trying to fill it.

	Debugf("Resolving " + strconv.Itoa(len(uniqueGitModules)) + " Git modules with " + strconv.Itoa(config.Maxworker) + " workers")
	concurrentGoroutines := make(chan struct{}, config.Maxworker)
	// Fill the dummy channel with config.Maxworker empty struct.
	for i := 0; i < config.Maxworker; i++ {
		concurrentGoroutines <- struct{}{}
	}

	// The done channel indicates when a single goroutine has finished its job.
	done := make(chan bool)
	// The waitForAllJobs channel allows the main program
	// to wait until we have indeed done all the jobs.
	waitForAllJobs := make(chan bool)
	// Collect all the jobs, and since the job is finished, we can
	// release another spot for a goroutine.
	go func() {
		for _, gm := range uniqueGitModules {
			go func(gm GitModule) {
				<-done
				// Say that another goroutine can now start.
				concurrentGoroutines <- struct{}{}
			}(gm)
		}
		// We have collected all the jobs, the program can now terminate
		waitForAllJobs <- true
	}()
	wg := sync.WaitGroup{}
	wg.Add(len(uniqueGitModules))

	for url, gm := range uniqueGitModules {
		Debugf("git repo url " + url)
		privateKey := gm.privateKey
		go func(url string, gm GitModule, bar *uiprogress.Bar) {
			// Try to receive from the concurrentGoroutines channel. When we have something,
			// it means we can start a new goroutine because another one finished.
			// Otherwise, it will block the execution until an execution
			// spot is available.
			<-concurrentGoroutines
			defer bar.Incr()
			defer wg.Done()

			if len(gm.privateKey) > 0 {
				Debugf("git repo url " + url + " with ssh key " + privateKey)
			} else {
				Debugf("git repo url " + url + " without ssh key")
			}

			//log.Println(config)
			// create save directory name from Git repo name
			repoDir := strings.Replace(strings.Replace(url, "/", "_", -1), ":", "-", -1)
			workDir := filepath.Join(config.ModulesCacheDir, repoDir)

			success := doMirrorOrUpdate(gm, workDir, 0)
			if !success && config.UseCacheFallback == false {
				Fatalf("Fatal: Could not reach git repository " + url)
			}
			//	doCloneOrPull(source, workDir, targetDir, sa.Remote, branch, sa.PrivateKey)
			done <- true
		}(url, gm, bar)
	}

	// Wait for all jobs to finish
	<-waitForAllJobs
	wg.Wait()
}

func doMirrorOrUpdate(gitModule GitModule, workDir string, retryCount int) bool {
	isControlRepo := strings.HasPrefix(workDir, config.EnvCacheDir)
	isInModulesCacheDir := strings.HasPrefix(workDir, config.ModulesCacheDir)

	needSSHKey := true
	if len(gitModule.privateKey) == 0 || strings.Contains(gitModule.git, "github.com") {
		if isControlRepo {
			needSSHKey = true
		} else {
			needSSHKey = false
		}
	}
	er := ExecResult{}
	gitCmd := "git clone --mirror " + gitModule.git + " " + workDir
	if config.CloneGitModules && !isControlRepo && !isInModulesCacheDir {
		//fmt.Printf("%+v\n", gitModule)
		gitCmd = "git clone --single-branch --branch " + gitModule.tree + " " + gitModule.git + " " + workDir
	}
	if isDir(workDir) {
		gitCmd = "git --git-dir " + workDir + " remote update --prune"
	}

	if needSSHKey {
		er = executeCommand("ssh-agent bash -c 'ssh-add "+gitModule.privateKey+"; "+gitCmd+"'", config.Timeout, gitModule.ignoreUnreachable)
	} else {
		er = executeCommand(gitCmd, config.Timeout, gitModule.ignoreUnreachable)
	}

	if er.returnCode != 0 {
		if config.UseCacheFallback {
			Warnf("WARN: git repository " + gitModule.git + " does not exist or is unreachable at this moment!")
			Warnf("WARN: Trying to use cache for " + gitModule.git + " git repository")
			return false
		} else if config.RetryGitCommands && retryCount > -1 {
			Warnf("WARN: git command failed: " + gitCmd + " deleting local cached repository and retrying...")
			purgeDir(workDir, "doMirrorOrUpdate, because git command failed, retrying")
			return doMirrorOrUpdate(gitModule, workDir, retryCount-1)
		}
		Warnf("WARN: git repository " + gitModule.git + " does not exist or is unreachable at this moment!")
		return false
	}
	return true
}

func syncToModuleDir(gitModule GitModule, srcDir string, targetDir string, correspondingPuppetEnvironment string) bool {
	startedAt := time.Now()
	mutex.Lock()
	syncGitCount++
	mutex.Unlock()
	if !isDir(srcDir) {
		if config.UseCacheFallback {
			Fatalf("Could not find cached git module " + srcDir)
		}
	}
	logCmd := "git --git-dir " + srcDir + " rev-parse --verify '" + gitModule.tree
	if config.GitObjectSyntaxNotSupported != true {
		logCmd = logCmd + "^{object}'"
	} else {
		logCmd = logCmd + "'"
	}

	isControlRepo := strings.HasPrefix(srcDir, config.EnvCacheDir)

	er := executeCommand(logCmd, config.Timeout, gitModule.ignoreUnreachable)
	hashFile := filepath.Join(targetDir, ".latest_commit")
	deployFile := filepath.Join(targetDir, ".g10k-deploy.json")
	needToSync := true
	if er.returnCode != 0 {
		if gitModule.ignoreUnreachable {
			Debugf("Failed to populate module " + targetDir + " but ignore-unreachable is set. Continuing...")
			purgeDir(targetDir, "syncToModuleDir, because ignore-unreachable is set for this module")
		}
		return false
	}

	if len(er.output) > 0 {
		if strings.HasPrefix(srcDir, config.EnvCacheDir) {
			mutex.Lock()
			desiredContent = append(desiredContent, deployFile)
			mutex.Unlock()
			if fileExists(deployFile) {
				dr := readDeployResultFile(deployFile)
				if dr.Signature == strings.TrimSuffix(er.output, "\n") {
					needToSync = false
					// need to get the content of the git repository to detect and purge unmanaged files
					addDesiredContent(srcDir, gitModule.tree, targetDir)
				}
			}
		} else {
			Debugf("adding path to managed content: " + targetDir)
			mutex.Lock()
			desiredContent = append(desiredContent, hashFile)
			desiredContent = append(desiredContent, targetDir)
			mutex.Unlock()
			targetHashByte, _ := ioutil.ReadFile(hashFile)
			targetHash := string(targetHashByte)
			if targetHash == strings.TrimSuffix(er.output, "\n") {
				needToSync = false
				mutex.Lock()
				unchangedModuleDirs = append(unchangedModuleDirs, targetDir)
				mutex.Unlock()
				//Debugf("Skipping, because no diff found between " + srcDir + "(" + er.output + ") and " + targetDir + "(" + string(targetHash) + ")")
			}
		}

	}
	if needToSync && er.returnCode == 0 {
		Infof("Need to sync " + targetDir)
		mutex.Lock()
		needSyncDirs = append(needSyncDirs, targetDir)
		if _, ok := needSyncEnvs[correspondingPuppetEnvironment]; !ok {
			needSyncEnvs[correspondingPuppetEnvironment] = empty
		}
		needSyncGitCount++
		mutex.Unlock()

		if !dryRun && !config.CloneGitModules || isControlRepo {
			if pfMode {
				purgeDir(targetDir, "git dir with changes in -puppetfile mode")
			}
			checkDirAndCreate(targetDir, "git dir")
			gitArchiveArgs := []string{"--git-dir", srcDir, "archive", gitModule.tree}
			cmd := exec.Command("git", gitArchiveArgs...)
			Debugf("Executing git --git-dir " + srcDir + " archive " + gitModule.tree)
			cmdOut, err := cmd.StdoutPipe()
			if err != nil {
				if !gitModule.ignoreUnreachable {
					Infof("Failed to populate module " + targetDir + " but ignore-unreachable is set. Continuing...")
				} else {
					return false
				}
				Fatalf("syncToModuleDir(): Failed to execute command: git --git-dir " + srcDir + " archive " + gitModule.tree + " Error: " + err.Error())
			}
			cmd.Start()

			before := time.Now()
			unTar(cmdOut, targetDir)
			duration := time.Since(before).Seconds()
			mutex.Lock()
			ioGitTime += duration
			mutex.Unlock()

			err = cmd.Wait()
			if err != nil {
				Fatalf("syncToModuleDir(): Failed to execute command: git --git-dir " + srcDir + " archive " + gitModule.tree + " Error: " + err.Error())
				//"\nIf you are using GitLab please ensure that you've added your deploy key to your repository." +
				//"\nThe Puppet environment which is using this unresolveable repository is " + correspondingPuppetEnvironment)
			}

			Verbosef("syncToModuleDir(): Executing git --git-dir " + srcDir + " archive " + gitModule.tree + " took " + strconv.FormatFloat(duration, 'f', 5, 64) + "s")

			er = executeCommand(logCmd, config.Timeout, false)
			if er.returnCode != 0 {
				Fatalf("executeCommand(): git command failed: " + logCmd + " " + err.Error() + "\nOutput: " + er.output)
			}
			if len(er.output) > 0 {
				commitHash := strings.TrimSuffix(er.output, "\n")
				if isControlRepo {
					Debugf("Writing to deploy file " + deployFile)
					dr := DeployResult{
						Name:      gitModule.tree,
						Signature: commitHash,
						StartedAt: startedAt,
					}
					writeStructJSONFile(deployFile, dr)
				} else {
					Debugf("Writing hash " + commitHash + " from command " + logCmd + " to " + hashFile)
					f, _ := os.Create(hashFile)
					defer f.Close()
					f.WriteString(commitHash)
					f.Sync()
				}

			}
		} else if config.CloneGitModules {
			return doMirrorOrUpdate(gitModule, targetDir, 0)
		}
	}
	return true
}

// addDesiredContent takes the given git repository directory and the
// relevant reference (branch, commit hash, tag) and adds its content to
// the global desiredContent slice so that it doesn't get purged by g10k
func addDesiredContent(gitDir string, tree string, targetDir string) {
	treeCmd := "git --git-dir " + gitDir + " ls-tree --full-tree -r -t --name-only " + tree
	er := executeCommand(treeCmd, config.Timeout, false)
	foundGitFiles := strings.Split(er.output, "\n")
	mutex.Lock()
	for _, desiredFile := range foundGitFiles[:len(foundGitFiles)-1] {
		desiredContent = append(desiredContent, filepath.Join(targetDir, desiredFile))

		// because we're using -r which prints git managed files in subfolders like this: foo/test3
		// we have to split up the given string and add the possible parent directories (foo in this case)
		parentDirs := strings.Split(desiredFile, "/")
		if len(parentDirs) > 1 {
			for _, dir := range parentDirs[:len(parentDirs)-1] {
				desiredContent = append(desiredContent, filepath.Join(targetDir, dir))
			}
		}
	}
	mutex.Unlock()

}
<commit_msg>add error output<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/xorpaul/uiprogress"
)

func resolveGitRepositories(uniqueGitModules map[string]GitModule) {
	defer timeTrack(time.Now(), funcName())
	if len(uniqueGitModules) <= 0 {
		Debugf("uniqueGitModules[] is empty, skipping...")
		return
	}
	bar := uiprogress.AddBar(len(uniqueGitModules)).AppendCompleted().PrependElapsed()
	bar.PrependFunc(func(b *uiprogress.Bar) string {
		return fmt.Sprintf("Resolving Git modules (%d/%d)", b.Current(), len(uniqueGitModules))
	})
	// Dummy channel to coordinate the number of concurrent goroutines.
	// This channel should be buffered otherwise we will be immediately blocked
	// when trying to fill it.

	Debugf("Resolving " + strconv.Itoa(len(uniqueGitModules)) + " Git modules with " + strconv.Itoa(config.Maxworker) + " workers")
	concurrentGoroutines := make(chan struct{}, config.Maxworker)
	// Fill the dummy channel with config.Maxworker empty struct.
	for i := 0; i < config.Maxworker; i++ {
		concurrentGoroutines <- struct{}{}
	}

	// The done channel indicates when a single goroutine has finished its job.
	done := make(chan bool)
	// The waitForAllJobs channel allows the main program
	// to wait until we have indeed done all the jobs.
	waitForAllJobs := make(chan bool)
	// Collect all the jobs, and since the job is finished, we can
	// release another spot for a goroutine.
	go func() {
		for _, gm := range uniqueGitModules {
			go func(gm GitModule) {
				<-done
				// Say that another goroutine can now start.
				concurrentGoroutines <- struct{}{}
			}(gm)
		}
		// We have collected all the jobs, the program can now terminate
		waitForAllJobs <- true
	}()
	wg := sync.WaitGroup{}
	wg.Add(len(uniqueGitModules))

	for url, gm := range uniqueGitModules {
		Debugf("git repo url " + url)
		privateKey := gm.privateKey
		go func(url string, gm GitModule, bar *uiprogress.Bar) {
			// Try to receive from the concurrentGoroutines channel. When we have something,
			// it means we can start a new goroutine because another one finished.
			// Otherwise, it will block the execution until an execution
			// spot is available.
			<-concurrentGoroutines
			defer bar.Incr()
			defer wg.Done()

			if len(gm.privateKey) > 0 {
				Debugf("git repo url " + url + " with ssh key " + privateKey)
			} else {
				Debugf("git repo url " + url + " without ssh key")
			}

			//log.Println(config)
			// create save directory name from Git repo name
			repoDir := strings.Replace(strings.Replace(url, "/", "_", -1), ":", "-", -1)
			workDir := filepath.Join(config.ModulesCacheDir, repoDir)

			success := doMirrorOrUpdate(gm, workDir, 0)
			if !success && config.UseCacheFallback == false {
				Fatalf("Fatal: Could not reach git repository " + url)
			}
			//	doCloneOrPull(source, workDir, targetDir, sa.Remote, branch, sa.PrivateKey)
			done <- true
		}(url, gm, bar)
	}

	// Wait for all jobs to finish
	<-waitForAllJobs
	wg.Wait()
}

func doMirrorOrUpdate(gitModule GitModule, workDir string, retryCount int) bool {
	isControlRepo := strings.HasPrefix(workDir, config.EnvCacheDir)
	isInModulesCacheDir := strings.HasPrefix(workDir, config.ModulesCacheDir)

	needSSHKey := true
	if len(gitModule.privateKey) == 0 || strings.Contains(gitModule.git, "github.com") {
		if isControlRepo {
			needSSHKey = true
		} else {
			needSSHKey = false
		}
	}
	er := ExecResult{}
	gitCmd := "git clone --mirror " + gitModule.git + " " + workDir
	if config.CloneGitModules && !isControlRepo && !isInModulesCacheDir {
		//fmt.Printf("%+v\n", gitModule)
		gitCmd = "git clone --single-branch --branch " + gitModule.tree + " " + gitModule.git + " " + workDir
	}
	if isDir(workDir) {
		gitCmd = "git --git-dir " + workDir + " remote update --prune"
	}

	if needSSHKey {
		er = executeCommand("ssh-agent bash -c 'ssh-add "+gitModule.privateKey+"; "+gitCmd+"'", config.Timeout, gitModule.ignoreUnreachable)
	} else {
		er = executeCommand(gitCmd, config.Timeout, gitModule.ignoreUnreachable)
	}

	if er.returnCode != 0 {
		if config.UseCacheFallback {
			Warnf("WARN: git repository " + gitModule.git + " does not exist or is unreachable at this moment!")
			Warnf("WARN: Trying to use cache for " + gitModule.git + " git repository")
			return false
		} else if config.RetryGitCommands && retryCount > -1 {
			Warnf("WARN: git command failed: " + gitCmd + " deleting local cached repository and retrying...")
			purgeDir(workDir, "doMirrorOrUpdate, because git command failed, retrying")
			return doMirrorOrUpdate(gitModule, workDir, retryCount-1)
		}
		Warnf("WARN: git repository " + gitModule.git + " does not exist or is unreachable at this moment! Error: " + er.output)
		return false
	}
	return true
}

func syncToModuleDir(gitModule GitModule, srcDir string, targetDir string, correspondingPuppetEnvironment string) bool {
	startedAt := time.Now()
	mutex.Lock()
	syncGitCount++
	mutex.Unlock()
	if !isDir(srcDir) {
		if config.UseCacheFallback {
			Fatalf("Could not find cached git module " + srcDir)
		}
	}
	logCmd := "git --git-dir " + srcDir + " rev-parse --verify '" + gitModule.tree
	if config.GitObjectSyntaxNotSupported != true {
		logCmd = logCmd + "^{object}'"
	} else {
		logCmd = logCmd + "'"
	}

	isControlRepo := strings.HasPrefix(srcDir, config.EnvCacheDir)

	er := executeCommand(logCmd, config.Timeout, gitModule.ignoreUnreachable)
	hashFile := filepath.Join(targetDir, ".latest_commit")
	deployFile := filepath.Join(targetDir, ".g10k-deploy.json")
	needToSync := true
	if er.returnCode != 0 {
		if gitModule.ignoreUnreachable {
			Debugf("Failed to populate module " + targetDir + " but ignore-unreachable is set. Continuing...")
			purgeDir(targetDir, "syncToModuleDir, because ignore-unreachable is set for this module")
		}
		return false
	}

	if len(er.output) > 0 {
		if strings.HasPrefix(srcDir, config.EnvCacheDir) {
			mutex.Lock()
			desiredContent = append(desiredContent, deployFile)
			mutex.Unlock()
			if fileExists(deployFile) {
				dr := readDeployResultFile(deployFile)
				if dr.Signature == strings.TrimSuffix(er.output, "\n") {
					needToSync = false
					// need to get the content of the git repository to detect and purge unmanaged files
					addDesiredContent(srcDir, gitModule.tree, targetDir)
				}
			}
		} else {
			Debugf("adding path to managed content: " + targetDir)
			mutex.Lock()
			desiredContent = append(desiredContent, hashFile)
			desiredContent = append(desiredContent, targetDir)
			mutex.Unlock()
			targetHashByte, _ := ioutil.ReadFile(hashFile)
			targetHash := string(targetHashByte)
			if targetHash == strings.TrimSuffix(er.output, "\n") {
				needToSync = false
				mutex.Lock()
				unchangedModuleDirs = append(unchangedModuleDirs, targetDir)
				mutex.Unlock()
				//Debugf("Skipping, because no diff found between " + srcDir + "(" + er.output + ") and " + targetDir + "(" + string(targetHash) + ")")
			}
		}

	}
	if needToSync && er.returnCode == 0 {
		Infof("Need to sync " + targetDir)
		mutex.Lock()
		needSyncDirs = append(needSyncDirs, targetDir)
		if _, ok := needSyncEnvs[correspondingPuppetEnvironment]; !ok {
			needSyncEnvs[correspondingPuppetEnvironment] = empty
		}
		needSyncGitCount++
		mutex.Unlock()

		if !dryRun && !config.CloneGitModules || isControlRepo {
			if pfMode {
				purgeDir(targetDir, "git dir with changes in -puppetfile mode")
			}
			checkDirAndCreate(targetDir, "git dir")
			gitArchiveArgs := []string{"--git-dir", srcDir, "archive", gitModule.tree}
			cmd := exec.Command("git", gitArchiveArgs...)
			Debugf("Executing git --git-dir " + srcDir + " archive " + gitModule.tree)
			cmdOut, err := cmd.StdoutPipe()
			if err != nil {
				if !gitModule.ignoreUnreachable {
					Infof("Failed to populate module " + targetDir + " but ignore-unreachable is set. Continuing...")
				} else {
					return false
				}
				Fatalf("syncToModuleDir(): Failed to execute command: git --git-dir " + srcDir + " archive " + gitModule.tree + " Error: " + err.Error())
			}
			cmd.Start()

			before := time.Now()
			unTar(cmdOut, targetDir)
			duration := time.Since(before).Seconds()
			mutex.Lock()
			ioGitTime += duration
			mutex.Unlock()

			err = cmd.Wait()
			if err != nil {
				Fatalf("syncToModuleDir(): Failed to execute command: git --git-dir " + srcDir + " archive " + gitModule.tree + " Error: " + err.Error())
				//"\nIf you are using GitLab please ensure that you've added your deploy key to your repository." +
				//"\nThe Puppet environment which is using this unresolveable repository is " + correspondingPuppetEnvironment)
			}

			Verbosef("syncToModuleDir(): Executing git --git-dir " + srcDir + " archive " + gitModule.tree + " took " + strconv.FormatFloat(duration, 'f', 5, 64) + "s")

			er = executeCommand(logCmd, config.Timeout, false)
			if er.returnCode != 0 {
				Fatalf("executeCommand(): git command failed: " + logCmd + " " + err.Error() + "\nOutput: " + er.output)
			}
			if len(er.output) > 0 {
				commitHash := strings.TrimSuffix(er.output, "\n")
				if isControlRepo {
					Debugf("Writing to deploy file " + deployFile)
					dr := DeployResult{
						Name:      gitModule.tree,
						Signature: commitHash,
						StartedAt: startedAt,
					}
					writeStructJSONFile(deployFile, dr)
				} else {
					Debugf("Writing hash " + commitHash + " from command " + logCmd + " to " + hashFile)
					f, _ := os.Create(hashFile)
					defer f.Close()
					f.WriteString(commitHash)
					f.Sync()
				}

			}
		} else if config.CloneGitModules {
			return doMirrorOrUpdate(gitModule, targetDir, 0)
		}
	}
	return true
}

// addDesiredContent takes the given git repository directory and the
// relevant reference (branch, commit hash, tag) and adds its content to
// the global desiredContent slice so that it doesn't get purged by g10k
func addDesiredContent(gitDir string, tree string, targetDir string) {
	treeCmd := "git --git-dir " + gitDir + " ls-tree --full-tree -r -t --name-only " + tree
	er := executeCommand(treeCmd, config.Timeout, false)
	foundGitFiles := strings.Split(er.output, "\n")
	mutex.Lock()
	for _, desiredFile := range foundGitFiles[:len(foundGitFiles)-1] {
		desiredContent = append(desiredContent, filepath.Join(targetDir, desiredFile))

		// because we're using -r which prints git managed files in subfolders like this: foo/test3
		// we have to split up the given string and add the possible parent directories (foo in this case)
		parentDirs := strings.Split(desiredFile, "/")
		if len(parentDirs) > 1 {
			for _, dir := range parentDirs[:len(parentDirs)-1] {
				desiredContent = append(desiredContent, filepath.Join(targetDir, dir))
			}
		}
	}
	mutex.Unlock()

}
<|endoftext|>


<commit_before>package git

/*
#include <git2.h>
#include <git2/sys/openssl.h>
*/
import "C"
import (
	"bytes"
	"encoding/hex"
	"errors"
	"runtime"
	"strings"
	"unsafe"
)

type ErrorClass int

const (
	ErrClassNone       ErrorClass = C.GITERR_NONE
	ErrClassNoMemory   ErrorClass = C.GITERR_NOMEMORY
	ErrClassOs         ErrorClass = C.GITERR_OS
	ErrClassInvalid    ErrorClass = C.GITERR_INVALID
	ErrClassReference  ErrorClass = C.GITERR_REFERENCE
	ErrClassZlib       ErrorClass = C.GITERR_ZLIB
	ErrClassRepository ErrorClass = C.GITERR_REPOSITORY
	ErrClassConfig     ErrorClass = C.GITERR_CONFIG
	ErrClassRegex      ErrorClass = C.GITERR_REGEX
	ErrClassOdb        ErrorClass = C.GITERR_ODB
	ErrClassIndex      ErrorClass = C.GITERR_INDEX
	ErrClassObject     ErrorClass = C.GITERR_OBJECT
	ErrClassNet        ErrorClass = C.GITERR_NET
	ErrClassTag        ErrorClass = C.GITERR_TAG
	ErrClassTree       ErrorClass = C.GITERR_TREE
	ErrClassIndexer    ErrorClass = C.GITERR_INDEXER
	ErrClassSSL        ErrorClass = C.GITERR_SSL
	ErrClassSubmodule  ErrorClass = C.GITERR_SUBMODULE
	ErrClassThread     ErrorClass = C.GITERR_THREAD
	ErrClassStash      ErrorClass = C.GITERR_STASH
	ErrClassCheckout   ErrorClass = C.GITERR_CHECKOUT
	ErrClassFetchHead  ErrorClass = C.GITERR_FETCHHEAD
	ErrClassMerge      ErrorClass = C.GITERR_MERGE
	ErrClassSsh        ErrorClass = C.GITERR_SSH
	ErrClassFilter     ErrorClass = C.GITERR_FILTER
	ErrClassRevert     ErrorClass = C.GITERR_REVERT
	ErrClassCallback   ErrorClass = C.GITERR_CALLBACK
	ErrClassRebase     ErrorClass = C.GITERR_REBASE
)

type ErrorCode int

const (

	// No error
	ErrOk ErrorCode = C.GIT_OK

	// Generic error
	ErrGeneric ErrorCode = C.GIT_ERROR
	// Requested object could not be found
	ErrNotFound ErrorCode = C.GIT_ENOTFOUND
	// Object exists preventing operation
	ErrExists ErrorCode = C.GIT_EEXISTS
	// More than one object matches
	ErrAmbiguous ErrorCode = C.GIT_EAMBIGUOUS
	// (backwards compatibility misspelling)
	ErrAmbigious ErrorCode = C.GIT_EAMBIGUOUS
	// Output buffer too short to hold data
	ErrBuffs ErrorCode = C.GIT_EBUFS

	// GIT_EUSER is a special error that is never generated by libgit2
	// code.  You can return it from a callback (e.g to stop an iteration)
	// to know that it was generated by the callback and not by libgit2.
	ErrUser ErrorCode = C.GIT_EUSER

	// Operation not allowed on bare repository
	ErrBareRepo ErrorCode = C.GIT_EBAREREPO
	// HEAD refers to branch with no commits
	ErrUnbornBranch ErrorCode = C.GIT_EUNBORNBRANCH
	// Merge in progress prevented operation
	ErrUnmerged ErrorCode = C.GIT_EUNMERGED
	// Reference was not fast-forwardable
	ErrNonFastForward ErrorCode = C.GIT_ENONFASTFORWARD
	// Name/ref spec was not in a valid format
	ErrInvalidSpec ErrorCode = C.GIT_EINVALIDSPEC
	// Checkout conflicts prevented operation
	ErrConflict ErrorCode = C.GIT_ECONFLICT
	// Lock file prevented operation
	ErrLocked ErrorCode = C.GIT_ELOCKED
	// Reference value does not match expected
	ErrModified ErrorCode = C.GIT_EMODIFIED
	// Authentication failed
	ErrAuth ErrorCode = C.GIT_EAUTH
	// Server certificate is invalid
	ErrCertificate ErrorCode = C.GIT_ECERTIFICATE
	// Patch/merge has already been applied
	ErrApplied ErrorCode = C.GIT_EAPPLIED
	// The requested peel operation is not possible
	ErrPeel ErrorCode = C.GIT_EPEEL
	// Unexpected EOF
	ErrEOF ErrorCode = C.GIT_EEOF
	// Uncommitted changes in index prevented operation
	ErrUncommitted ErrorCode = C.GIT_EUNCOMMITTED
	// The operation is not valid for a directory
	ErrDirectory ErrorCode = C.GIT_EDIRECTORY
	// A merge conflict exists and cannot continue
	ErrMergeConflict ErrorCode = C.GIT_EMERGECONFLICT

	// Internal only
	ErrPassthrough ErrorCode = C.GIT_PASSTHROUGH
	// Signals end of iteration with iterator
	ErrIterOver ErrorCode = C.GIT_ITEROVER
)

var (
	ErrInvalid = errors.New("Invalid state for operation")
)

var pointerHandles *HandleList

func init() {
	pointerHandles = NewHandleList()

	C.git_libgit2_init()

	// Due to the multithreaded nature of Go and its interaction with
	// calling C functions, we cannot work with a library that was not built
	// with multi-threading support. The most likely outcome is a segfault
	// or panic at an incomprehensible time, so let's make it easy by
	// panicking right here.
	if Features()&FeatureThreads == 0 {
		panic("libgit2 was not built with threading support")
	}

	// This is not something we should be doing, as we may be
	// stomping all over someone else's setup. The user should do
	// this themselves or use some binding/wrapper which does it
	// in such a way that they can be sure they're the only ones
	// setting it up.
	C.git_openssl_set_locking()
}

// Shutdown frees all the resources acquired by libgit2. Make sure no
// references to any git2go objects are live before calling this.
// After this is called, invoking any function from this library will result in
// undefined behavior, so make sure this is called carefully.
func Shutdown() {
	pointerHandles.Clear()

	C.git_libgit2_shutdown()
}

// Oid represents the id for a Git object.
type Oid [20]byte

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

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

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

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

func NewOid(s string) (*Oid, error) {
	if len(s) > C.GIT_OID_HEXSZ {
		return nil, errors.New("string is too long for oid")
	}

	o := new(Oid)

	slice, error := hex.DecodeString(s)
	if error != nil {
		return nil, error
	}

	if len(slice) != 20 {
		return nil, &GitError{"Invalid Oid", ErrClassNone, ErrGeneric}
	}

	copy(o[:], slice[:20])
	return o, nil
}

func (oid *Oid) String() string {
	return hex.EncodeToString(oid[:])
}

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

func (oid *Oid) Copy() *Oid {
	ret := *oid
	return &ret
}

func (oid *Oid) Equal(oid2 *Oid) bool {
	return *oid == *oid2
}

func (oid *Oid) IsZero() bool {
	return *oid == Oid{}
}

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

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

	var ret C.int

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

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

type GitError struct {
	Message string
	Class   ErrorClass
	Code    ErrorCode
}

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

func IsErrorClass(err error, c ErrorClass) bool {

	if err == nil {
		return false
	}
	if gitError, ok := err.(*GitError); ok {
		return gitError.Class == c
	}
	return false
}

func IsErrorCode(err error, c ErrorCode) bool {
	if err == nil {
		return false
	}
	if gitError, ok := err.(*GitError); ok {
		return gitError.Code == c
	}
	return false
}

func MakeGitError(errorCode C.int) error {

	var errMessage string
	var errClass ErrorClass
	if errorCode != C.GIT_ITEROVER {
		err := C.giterr_last()
		if err != nil {
			errMessage = C.GoString(err.message)
			errClass = ErrorClass(err.klass)
		} else {
			errClass = ErrClassInvalid
		}
	}
	return &GitError{errMessage, errClass, ErrorCode(errorCode)}
}

func MakeGitError2(err int) error {
	return MakeGitError(C.int(err))
}

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

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

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

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

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

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

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

	return C.GoString(buf.ptr), nil
}
<commit_msg>Add a ReInit function (#647)<commit_after>package git

/*
#include <git2.h>
#include <git2/sys/openssl.h>
*/
import "C"
import (
	"bytes"
	"encoding/hex"
	"errors"
	"runtime"
	"strings"
	"unsafe"
)

type ErrorClass int

const (
	ErrClassNone       ErrorClass = C.GITERR_NONE
	ErrClassNoMemory   ErrorClass = C.GITERR_NOMEMORY
	ErrClassOs         ErrorClass = C.GITERR_OS
	ErrClassInvalid    ErrorClass = C.GITERR_INVALID
	ErrClassReference  ErrorClass = C.GITERR_REFERENCE
	ErrClassZlib       ErrorClass = C.GITERR_ZLIB
	ErrClassRepository ErrorClass = C.GITERR_REPOSITORY
	ErrClassConfig     ErrorClass = C.GITERR_CONFIG
	ErrClassRegex      ErrorClass = C.GITERR_REGEX
	ErrClassOdb        ErrorClass = C.GITERR_ODB
	ErrClassIndex      ErrorClass = C.GITERR_INDEX
	ErrClassObject     ErrorClass = C.GITERR_OBJECT
	ErrClassNet        ErrorClass = C.GITERR_NET
	ErrClassTag        ErrorClass = C.GITERR_TAG
	ErrClassTree       ErrorClass = C.GITERR_TREE
	ErrClassIndexer    ErrorClass = C.GITERR_INDEXER
	ErrClassSSL        ErrorClass = C.GITERR_SSL
	ErrClassSubmodule  ErrorClass = C.GITERR_SUBMODULE
	ErrClassThread     ErrorClass = C.GITERR_THREAD
	ErrClassStash      ErrorClass = C.GITERR_STASH
	ErrClassCheckout   ErrorClass = C.GITERR_CHECKOUT
	ErrClassFetchHead  ErrorClass = C.GITERR_FETCHHEAD
	ErrClassMerge      ErrorClass = C.GITERR_MERGE
	ErrClassSsh        ErrorClass = C.GITERR_SSH
	ErrClassFilter     ErrorClass = C.GITERR_FILTER
	ErrClassRevert     ErrorClass = C.GITERR_REVERT
	ErrClassCallback   ErrorClass = C.GITERR_CALLBACK
	ErrClassRebase     ErrorClass = C.GITERR_REBASE
)

type ErrorCode int

const (

	// No error
	ErrOk ErrorCode = C.GIT_OK

	// Generic error
	ErrGeneric ErrorCode = C.GIT_ERROR
	// Requested object could not be found
	ErrNotFound ErrorCode = C.GIT_ENOTFOUND
	// Object exists preventing operation
	ErrExists ErrorCode = C.GIT_EEXISTS
	// More than one object matches
	ErrAmbiguous ErrorCode = C.GIT_EAMBIGUOUS
	// (backwards compatibility misspelling)
	ErrAmbigious ErrorCode = C.GIT_EAMBIGUOUS
	// Output buffer too short to hold data
	ErrBuffs ErrorCode = C.GIT_EBUFS

	// GIT_EUSER is a special error that is never generated by libgit2
	// code.  You can return it from a callback (e.g to stop an iteration)
	// to know that it was generated by the callback and not by libgit2.
	ErrUser ErrorCode = C.GIT_EUSER

	// Operation not allowed on bare repository
	ErrBareRepo ErrorCode = C.GIT_EBAREREPO
	// HEAD refers to branch with no commits
	ErrUnbornBranch ErrorCode = C.GIT_EUNBORNBRANCH
	// Merge in progress prevented operation
	ErrUnmerged ErrorCode = C.GIT_EUNMERGED
	// Reference was not fast-forwardable
	ErrNonFastForward ErrorCode = C.GIT_ENONFASTFORWARD
	// Name/ref spec was not in a valid format
	ErrInvalidSpec ErrorCode = C.GIT_EINVALIDSPEC
	// Checkout conflicts prevented operation
	ErrConflict ErrorCode = C.GIT_ECONFLICT
	// Lock file prevented operation
	ErrLocked ErrorCode = C.GIT_ELOCKED
	// Reference value does not match expected
	ErrModified ErrorCode = C.GIT_EMODIFIED
	// Authentication failed
	ErrAuth ErrorCode = C.GIT_EAUTH
	// Server certificate is invalid
	ErrCertificate ErrorCode = C.GIT_ECERTIFICATE
	// Patch/merge has already been applied
	ErrApplied ErrorCode = C.GIT_EAPPLIED
	// The requested peel operation is not possible
	ErrPeel ErrorCode = C.GIT_EPEEL
	// Unexpected EOF
	ErrEOF ErrorCode = C.GIT_EEOF
	// Uncommitted changes in index prevented operation
	ErrUncommitted ErrorCode = C.GIT_EUNCOMMITTED
	// The operation is not valid for a directory
	ErrDirectory ErrorCode = C.GIT_EDIRECTORY
	// A merge conflict exists and cannot continue
	ErrMergeConflict ErrorCode = C.GIT_EMERGECONFLICT

	// Internal only
	ErrPassthrough ErrorCode = C.GIT_PASSTHROUGH
	// Signals end of iteration with iterator
	ErrIterOver ErrorCode = C.GIT_ITEROVER
)

var (
	ErrInvalid = errors.New("Invalid state for operation")
)

var pointerHandles *HandleList

func init() {
	initLibGit2()
}

func initLibGit2() {
	pointerHandles = NewHandleList()

	C.git_libgit2_init()

	// Due to the multithreaded nature of Go and its interaction with
	// calling C functions, we cannot work with a library that was not built
	// with multi-threading support. The most likely outcome is a segfault
	// or panic at an incomprehensible time, so let's make it easy by
	// panicking right here.
	if Features()&FeatureThreads == 0 {
		panic("libgit2 was not built with threading support")
	}

	// This is not something we should be doing, as we may be
	// stomping all over someone else's setup. The user should do
	// this themselves or use some binding/wrapper which does it
	// in such a way that they can be sure they're the only ones
	// setting it up.
	C.git_openssl_set_locking()
}

// Shutdown frees all the resources acquired by libgit2. Make sure no
// references to any git2go objects are live before calling this.
// After this is called, invoking any function from this library will result in
// undefined behavior, so make sure this is called carefully.
func Shutdown() {
	pointerHandles.Clear()

	C.git_libgit2_shutdown()
}

// ReInit reinitializes the global state, this is useful if the effective user
// id has changed and you want to update the stored search paths for gitconfig
// files. This function frees any references to objects, so it should be called
// before any other functions are called.
func ReInit() {
	Shutdown()
	initLibGit2()
}

// Oid represents the id for a Git object.
type Oid [20]byte

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

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

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

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

func NewOid(s string) (*Oid, error) {
	if len(s) > C.GIT_OID_HEXSZ {
		return nil, errors.New("string is too long for oid")
	}

	o := new(Oid)

	slice, error := hex.DecodeString(s)
	if error != nil {
		return nil, error
	}

	if len(slice) != 20 {
		return nil, &GitError{"Invalid Oid", ErrClassNone, ErrGeneric}
	}

	copy(o[:], slice[:20])
	return o, nil
}

func (oid *Oid) String() string {
	return hex.EncodeToString(oid[:])
}

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

func (oid *Oid) Copy() *Oid {
	ret := *oid
	return &ret
}

func (oid *Oid) Equal(oid2 *Oid) bool {
	return *oid == *oid2
}

func (oid *Oid) IsZero() bool {
	return *oid == Oid{}
}

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

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

	var ret C.int

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

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

type GitError struct {
	Message string
	Class   ErrorClass
	Code    ErrorCode
}

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

func IsErrorClass(err error, c ErrorClass) bool {

	if err == nil {
		return false
	}
	if gitError, ok := err.(*GitError); ok {
		return gitError.Class == c
	}
	return false
}

func IsErrorCode(err error, c ErrorCode) bool {
	if err == nil {
		return false
	}
	if gitError, ok := err.(*GitError); ok {
		return gitError.Code == c
	}
	return false
}

func MakeGitError(errorCode C.int) error {

	var errMessage string
	var errClass ErrorClass
	if errorCode != C.GIT_ITEROVER {
		err := C.giterr_last()
		if err != nil {
			errMessage = C.GoString(err.message)
			errClass = ErrorClass(err.klass)
		} else {
			errClass = ErrClassInvalid
		}
	}
	return &GitError{errMessage, errClass, ErrorCode(errorCode)}
}

func MakeGitError2(err int) error {
	return MakeGitError(C.int(err))
}

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

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

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

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

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

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

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

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


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

package git

import (
	"fmt"
	"strings"
	"time"
)

const _VERSION = "0.6.6"

func Version() string {
	return _VERSION
}

var (
	// Debug enables verbose logging on everything.
	// This should be false in case Gogs starts in SSH mode.
	Debug  = false
	Prefix = "[git-module] "
)

func log(format string, args ...interface{}) {
	if !Debug {
		return
	}

	fmt.Print(Prefix)
	if len(args) == 0 {
		fmt.Println(format)
	} else {
		fmt.Printf(format+"\n", args...)
	}
}

var gitVersion string

// Version returns current Git version from shell.
func BinVersion() (string, error) {
	if len(gitVersion) > 0 {
		return gitVersion, nil
	}

	stdout, err := NewCommand("version").Run()
	if err != nil {
		return "", err
	}

	fields := strings.Fields(stdout)
	if len(fields) < 3 {
		return "", fmt.Errorf("not enough output: %s", stdout)
	}

	// Handle special case on Windows.
	i := strings.Index(fields[2], "windows")
	if i >= 1 {
		gitVersion = fields[2][:i-1]
		return gitVersion, nil
	}

	gitVersion = fields[2]
	return gitVersion, nil
}

func init() {
	BinVersion()
}

// Fsck verifies the connectivity and validity of the objects in the database
func Fsck(repoPath string, timeout time.Duration, args ...string) error {
	// Make sure timeout makes sense.
	if timeout <= 0 {
		timeout = -1
	}
	_, err := NewCommand("fsck").AddArguments(args...).RunInDirTimeout(timeout, repoPath)
	return err
}
<commit_msg>Bump version<commit_after>// Copyright 2015 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package git

import (
	"fmt"
	"strings"
	"time"
)

const _VERSION = "0.6.7"

func Version() string {
	return _VERSION
}

var (
	// Debug enables verbose logging on everything.
	// This should be false in case Gogs starts in SSH mode.
	Debug  = false
	Prefix = "[git-module] "
)

func log(format string, args ...interface{}) {
	if !Debug {
		return
	}

	fmt.Print(Prefix)
	if len(args) == 0 {
		fmt.Println(format)
	} else {
		fmt.Printf(format+"\n", args...)
	}
}

var gitVersion string

// Version returns current Git version from shell.
func BinVersion() (string, error) {
	if len(gitVersion) > 0 {
		return gitVersion, nil
	}

	stdout, err := NewCommand("version").Run()
	if err != nil {
		return "", err
	}

	fields := strings.Fields(stdout)
	if len(fields) < 3 {
		return "", fmt.Errorf("not enough output: %s", stdout)
	}

	// Handle special case on Windows.
	i := strings.Index(fields[2], "windows")
	if i >= 1 {
		gitVersion = fields[2][:i-1]
		return gitVersion, nil
	}

	gitVersion = fields[2]
	return gitVersion, nil
}

func init() {
	BinVersion()
}

// Fsck verifies the connectivity and validity of the objects in the database
func Fsck(repoPath string, timeout time.Duration, args ...string) error {
	// Make sure timeout makes sense.
	if timeout <= 0 {
		timeout = -1
	}
	_, err := NewCommand("fsck").AddArguments(args...).RunInDirTimeout(timeout, repoPath)
	return err
}
<|endoftext|>


<commit_before>package issues

import (
	// Stdlib
	"bytes"
	"fmt"
	"io"
	"regexp"
)

// ReviewIssueCommonBody represents the issue body part that is shared by all issue types,
// i.e. by both story review issues and commit review issues.
type ReviewIssueCommonBody struct {
	*CommitList
	*ReviewBlockerList

	UserContent string
}

func newReviewIssueCommonBody() *ReviewIssueCommonBody {
	return &ReviewIssueCommonBody{
		CommitList:        &CommitList{},
		ReviewBlockerList: &ReviewBlockerList{},
	}
}

// Formatting ------------------------------------------------------------------

const userContentSeparator = "----------"

var reviewIssueCommonBodyTemplate = fmt.Sprintf(`The commits to be reviewed are following:
{{range .CommitList.CommitItems}}- {{if .Reviewed}}[x]{{else}}[ ]{{end}} {{.CommitSHA}}: {{.CommitTitle}}
{{end}}
{{with .ReviewBlockerList.ReviewBlockerItems}}The following review blockers were opened by the reviewer:{{range .}}
- {{if .Fixed}}[x]{{else}}[ ]{{end}} [blocker {{.BlockerNumber}}]({{.CommentURL}}) (commit {{.CommitSHA}}): {{.BlockerSummary}}{{end}}
{{end}}
%v
{{if .UserContent}}{{.UserContent}}{{else}}
The content above was generated by SalsaFlow.
You can insert custom description here, but not above the separator.
{{end}}`, userContentSeparator)

func (body *ReviewIssueCommonBody) execTemplate(w io.Writer) {
	execTemplate(w, "review issue common body", reviewIssueCommonBodyTemplate, body)
}

// Parsing ---------------------------------------------------------------------

var (
	commonBodyCommitItemRegexp  = regexp.MustCompile(`^- \[([ xX])\] ([0-9a-f]+): (.+)$`)
	commonBodyBlockerItemRegexp = regexp.MustCompile(`^- \[([ xX])\] \[blocker ([0-9]+)\]\(([^)]+)\) \(commit ([0-9a-f]+)\): (.+)$`)
)

func parseRemainingIssueBody(err *error, scanner *bodyScanner) *ReviewIssueCommonBody {
	// For optimization.
	if *err != nil {
		return nil
	}

	// Parse the review issue common body part.
	// We pass a pointer to error to the parsing functions,
	// so in case there is an error, it just falls through
	// so that the error value can be checked at the very end.
	//
	// Also in case the user content separator is encountered,
	// it falls through and the user content is collected at the end.

	// Parse the commit list.
	commitList := parseCommonBodyCommitList(err, scanner)

	// An empty line follows. It is already set as the current line
	// in the scanner since that is what marks the end of the commit list.

	// Parse the review blocker list.
	reviewBlockerList := parseCommonBodyBlockerList(err, scanner)

	// And empty line follows. It is already set as the current line
	// in the scanner since that is what marks the end of the blocker list.

	// Parse the user content separator.
	readCommonBodySeparator(err, scanner)

	// Parse user content.
	userContent := parseCommonBodyUserContent(err, scanner)

	// Check for errors.
	if *err != nil {
		return nil
	}

	// Return the common body object on success.
	return &ReviewIssueCommonBody{
		CommitList:        commitList,
		ReviewBlockerList: reviewBlockerList,
		UserContent:       userContent,
	}
}

func parseCommonBodyCommitList(err *error, scanner *bodyScanner) *CommitList {
	if *err != nil {
		return nil
	}

	// Read the list heading.
	_, _, ex := scanner.ReadLine()
	if ex != nil {
		*err = ex
		return nil
	}

	// Read the list items.
	commitList := &CommitList{}
	for {
		// Read the next line.
		line, _, ex := scanner.ReadLine()
		if ex != nil {
			*err = ex
			return nil
		}

		// In case this is an empty line, we are done.
		if line == "" || line == userContentSeparator {
			return commitList
		}

		// Parse the line as a commit item.
		match := commonBodyCommitItemRegexp.FindStringSubmatch(line)
		if len(match) == 0 {
			*err = scanner.CurrentLineInvalid()
			return nil
		}

		// Add the commit to the commit list.
		reviewed := match[1] != " "
		commitSHA, commitTitle := match[2], match[3]
		commitList.AddCommit(reviewed, commitSHA, commitTitle)
	}
}

func parseCommonBodyBlockerList(err *error, scanner *bodyScanner) *ReviewBlockerList {
	if *err != nil {
		return nil
	}

	// Check the current line for the user content separator.
	line, _, _ := scanner.CurrentLine()
	if line == userContentSeparator {
		// Return empty blocker list.
		return &ReviewBlockerList{}
	}

	// Drop the list heading line.
	// We can be reading the user content separator here as well.
	line, _, ex := scanner.ReadLine()
	if ex != nil {
		*err = ex
		return nil
	}
	if line == userContentSeparator {
		// Return empty blocker list.
		return &ReviewBlockerList{}
	}

	// Read the list items.
	reviewBlockerList := &ReviewBlockerList{}
	for {
		// Read the next line.
		line, _, ex := scanner.ReadLine()
		if ex != nil {
			*err = ex
			return nil
		}

		// In case this is an empty line, we are done.
		if line == "" || line == userContentSeparator {
			return reviewBlockerList
		}

		// Parse the line as a review blocker item.
		match := commonBodyBlockerItemRegexp.FindStringSubmatch(line)
		if len(match) == 0 {
			*err = scanner.CurrentLineInvalid()
			return nil
		}

		// Add the blocker to the blocker list.
		fixed := match[1] != " "
		commentURL, commitSHA, blockerSummary := match[3], match[4], match[5]
		reviewBlockerList.AddReviewBlocker(fixed, commentURL, commitSHA, blockerSummary)
	}
}

func readCommonBodySeparator(err *error, scanner *bodyScanner) {
	if *err != nil {
		return
	}

	// Check the current line for the user content separator.
	if line, _, _ := scanner.CurrentLine(); line == userContentSeparator {
		return
	}

	// Read the input until the separator is encountered.
	// We can read the whole input here, but that is ok.
	// ReadLine returns io.EOF in that case and we return.
	for {
		line, _, ex := scanner.ReadLine()
		if ex != nil {
			*err = ex
			return
		}

		if line == userContentSeparator {
			return
		}
	}
}

func parseCommonBodyUserContent(err *error, scanner *bodyScanner) string {
	if *err != nil {
		return ""
	}

	// Prepare a buffer for the user content.
	buffer := bytes.NewBuffer(nil)

	writeLine := func(line string) {
		buffer.WriteString("\n")
		buffer.WriteString(line)
	}

	// Read the user content.
	// Make sure there is a single leading empty line.
	droppingEmptyLines := true
	for {
		line, _, ex := scanner.ReadLine()
		switch ex {
		case nil:
			// Drop leading empty lines.
			if droppingEmptyLines {
				if line == "" {
					continue
				}
				droppingEmptyLines = false
			}

			// Write the user content.
			writeLine(line)

		case io.EOF:
			// In case the error is io.EOF, we are done.
			return buffer.String()

		default:
			// Otherwise return the error.
			*err = ex
			return ""
		}
	}
}
<commit_msg>github/issues: Fix review issue parser<commit_after>package issues

import (
	// Stdlib
	"bytes"
	"fmt"
	"io"
	"regexp"
)

// ReviewIssueCommonBody represents the issue body part that is shared by all issue types,
// i.e. by both story review issues and commit review issues.
type ReviewIssueCommonBody struct {
	*CommitList
	*ReviewBlockerList

	UserContent string
}

func newReviewIssueCommonBody() *ReviewIssueCommonBody {
	return &ReviewIssueCommonBody{
		CommitList:        &CommitList{},
		ReviewBlockerList: &ReviewBlockerList{},
	}
}

// Formatting ------------------------------------------------------------------

const userContentSeparator = "----------"

var reviewIssueCommonBodyTemplate = fmt.Sprintf(`The commits to be reviewed are following:
{{range .CommitList.CommitItems}}- {{if .Reviewed}}[x]{{else}}[ ]{{end}} {{.CommitSHA}}: {{.CommitTitle}}
{{end}}
{{with .ReviewBlockerList.ReviewBlockerItems}}The following review blockers were opened by the reviewer:{{range .}}
- {{if .Fixed}}[x]{{else}}[ ]{{end}} [blocker {{.BlockerNumber}}]({{.CommentURL}}) (commit {{.CommitSHA}}): {{.BlockerSummary}}{{end}}
{{end}}
%v
{{if .UserContent}}{{.UserContent}}{{else}}
The content above was generated by SalsaFlow.
You can insert custom description here, but not above the separator.
{{end}}`, userContentSeparator)

func (body *ReviewIssueCommonBody) execTemplate(w io.Writer) {
	execTemplate(w, "review issue common body", reviewIssueCommonBodyTemplate, body)
}

// Parsing ---------------------------------------------------------------------

var (
	commonBodyCommitItemRegexp  = regexp.MustCompile(`^- \[([ xX])\] ([0-9a-f]+): (.+)$`)
	commonBodyBlockerItemRegexp = regexp.MustCompile(`^- \[([ xX])\] \[blocker ([0-9]+)\]\(([^)]+)\) \(commit ([0-9a-f]+)\): (.+)$`)
)

func parseRemainingIssueBody(err *error, scanner *bodyScanner) *ReviewIssueCommonBody {
	// For optimization.
	if *err != nil {
		return nil
	}

	// Parse the review issue common body part.
	// We pass a pointer to error to the parsing functions,
	// so in case there is an error, it just falls through
	// so that the error value can be checked at the very end.
	//
	// Also in case the user content separator is encountered,
	// it falls through and the user content is collected at the end.

	// Parse the commit list.
	commitList := parseCommonBodyCommitList(err, scanner)

	// An empty line follows. It is already set as the current line
	// in the scanner since that is what marks the end of the commit list.

	// Parse the review blocker list.
	reviewBlockerList := parseCommonBodyBlockerList(err, scanner)

	// And empty line follows. It is already set as the current line
	// in the scanner since that is what marks the end of the blocker list.

	// Parse the user content separator.
	readCommonBodySeparator(err, scanner)

	// Parse user content.
	userContent := parseCommonBodyUserContent(err, scanner)

	// Check for errors, but ignore io.EOF since that is ok.
	// The parsing functions return some reasonable empty values
	// even when there is an error.
	if ex := *err; ex != nil {
		if ex != io.EOF {
			return nil
		}
		*err = nil
	}

	// Return the common body object on success.
	return &ReviewIssueCommonBody{
		CommitList:        commitList,
		ReviewBlockerList: reviewBlockerList,
		UserContent:       userContent,
	}
}

func parseCommonBodyCommitList(err *error, scanner *bodyScanner) *CommitList {
	emptyList := &CommitList{}

	if *err != nil {
		return emptyList
	}

	// Read the list heading.
	_, _, ex := scanner.ReadLine()
	if ex != nil {
		*err = ex
		return emptyList
	}

	// Read the list items.
	commitList := &CommitList{}
	for {
		// Read the next line.
		line, _, ex := scanner.ReadLine()
		if ex != nil {
			*err = ex
			return commitList
		}

		// In case this is an empty line, we are done.
		if line == "" || line == userContentSeparator {
			return commitList
		}

		// Parse the line as a commit item.
		match := commonBodyCommitItemRegexp.FindStringSubmatch(line)
		if len(match) == 0 {
			*err = scanner.CurrentLineInvalid()
			return commitList
		}

		// Add the commit to the commit list.
		reviewed := match[1] != " "
		commitSHA, commitTitle := match[2], match[3]
		commitList.AddCommit(reviewed, commitSHA, commitTitle)
	}
}

func parseCommonBodyBlockerList(err *error, scanner *bodyScanner) *ReviewBlockerList {
	emptyList := &ReviewBlockerList{}

	if *err != nil {
		return emptyList
	}

	// Check the current line for the user content separator.
	line, _, _ := scanner.CurrentLine()
	if line == userContentSeparator {
		return emptyList
	}

	// Drop the list heading line.
	// We can be reading the user content separator here as well.
	line, _, ex := scanner.ReadLine()
	if ex != nil {
		*err = ex
		return emptyList
	}
	if line == userContentSeparator {
		return emptyList
	}

	// Read the list items.
	reviewBlockerList := &ReviewBlockerList{}
	for {
		// Read the next line.
		line, _, ex := scanner.ReadLine()
		if ex != nil {
			*err = ex
			return reviewBlockerList
		}

		// In case this is an empty line, we are done.
		if line == "" || line == userContentSeparator {
			return reviewBlockerList
		}

		// Parse the line as a review blocker item.
		match := commonBodyBlockerItemRegexp.FindStringSubmatch(line)
		if len(match) == 0 {
			*err = scanner.CurrentLineInvalid()
			return reviewBlockerList
		}

		// Add the blocker to the blocker list.
		fixed := match[1] != " "
		commentURL, commitSHA, blockerSummary := match[3], match[4], match[5]
		reviewBlockerList.AddReviewBlocker(fixed, commentURL, commitSHA, blockerSummary)
	}
}

func readCommonBodySeparator(err *error, scanner *bodyScanner) {
	if *err != nil {
		return
	}

	// Check the current line for the user content separator.
	if line, _, _ := scanner.CurrentLine(); line == userContentSeparator {
		return
	}

	// Read the input until the separator is encountered.
	// We can read the whole input here, but that is ok.
	// ReadLine returns io.EOF in that case and we return.
	for {
		line, _, ex := scanner.ReadLine()
		if ex != nil {
			*err = ex
			return
		}

		if line == userContentSeparator {
			return
		}
	}
}

func parseCommonBodyUserContent(err *error, scanner *bodyScanner) string {
	if *err != nil {
		return ""
	}

	// Prepare a buffer for the user content.
	buffer := bytes.NewBuffer(nil)

	writeLine := func(line string) {
		buffer.WriteString("\n")
		buffer.WriteString(line)
	}

	// Read the user content.
	// Make sure there is a single leading empty line.
	droppingEmptyLines := true
	for {
		line, _, ex := scanner.ReadLine()
		switch ex {
		case nil:
			// Drop leading empty lines.
			if droppingEmptyLines {
				if line == "" {
					continue
				}
				droppingEmptyLines = false
			}

			// Write the user content.
			writeLine(line)

		case io.EOF:
			// In case the error is io.EOF, we are done.
			return buffer.String()

		default:
			// Otherwise return the error.
			*err = ex
			return ""
		}
	}
}
<|endoftext|>


<commit_before>package gocui

import (
	"errors"

	"github.com/jroimartin/termbox-go"
)

var ErrorQuit = errors.New("quit")

type Gui struct {
	events           chan termbox.Event
	views            []*View
	currentView      *View
	BgColor, FgColor termbox.Attribute
}

func NewGui() (g *Gui) {
	return &Gui{}
}

func (g *Gui) Init() (err error) {
	g.events = make(chan termbox.Event, 20)
	g.BgColor = termbox.ColorWhite
	g.FgColor = termbox.ColorBlack
	return termbox.Init()
}

func (g *Gui) Close() {
	termbox.Close()
}

func (g *Gui) Size() (x, y int) {
	return termbox.Size()
}

func (g *Gui) AddView(name string, x0, y0, x1, y1 int) (v *View, err error) {
	maxX, maxY := termbox.Size()

	if x0 < -1 || y0 < -1 || x1 < -1 || y1 < -1 ||
		x0 > maxX || y0 > maxY || x1 > maxX || y1 > maxY ||
		x0 >= x1 || y0 >= y1 {
		return nil, errors.New("invalid points")
	}

	for _, v := range g.views {
		if name == v.Name {
			return nil, errors.New("invalid name")
		}
	}

	v = NewView(name, x0, y0, x1, y1)
	g.views = append(g.views, v)
	return v, nil
}

func (g *Gui) MainLoop() (err error) {
	go func() {
		for {
			g.events <- termbox.PollEvent()
		}
	}()

	if err := g.resize(); err != nil {
		return err
	}
	if err := g.draw(); err != nil {
		return err
	}
	// TODO: Set initial cursor position
	//termbox.SetCursor(10, 10)
	termbox.Flush()

	for {
		select {
		case ev := <-g.events:
			if err := g.handleEvent(&ev); err != nil {
				return err
			}
			if err := g.consumeevents(); err != nil {
				return err
			}
			if err := g.draw(); err != nil {
				return err
			}
			termbox.Flush()
		}
	}
	return nil
}

func (g *Gui) SetCell(x, y int, ch rune) (err error) {
	maxX, maxY := termbox.Size()
	if x < 0 || y < 0 || x >= maxX || y >= maxY {
		return errors.New("invalid point")
	}
	termbox.SetCell(x, y, ch, g.FgColor, g.BgColor)
	return nil
}

func (g *Gui) GetCell(x, y int) (ch rune, err error) {
	maxX, maxY := termbox.Size()
	if x < 0 || y < 0 || x >= maxX || y >= maxY {
		return 0, errors.New("invalid point")
	}
	c := termbox.CellBuffer()[y*maxX+x]
	return c.Ch, nil
}

func (g *Gui) consumeevents() (err error) {
	for {
		select {
		case ev := <-g.events:
			if err := g.handleEvent(&ev); err != nil {
				return err
			}
		default:
			return nil
		}
	}
}

func (g *Gui) handleEvent(ev *termbox.Event) (err error) {
	switch ev.Type {
	case termbox.EventKey:
		return g.onKey(ev)
	case termbox.EventResize:
		return g.resize()
	case termbox.EventError:
		return ev.Err
	default:
		return nil
	}
}

func (g *Gui) draw() (err error) {
	for _, v := range g.views {
		if err := g.drawView(v); err != nil {
			return err
		}
	}
	return nil
}

func (g *Gui) drawView(v *View) (err error) {
	return nil
}

func (g *Gui) resize() (err error) {
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
	if err := g.resizeViews(); err != nil {
		return err
	}
	if err := g.drawFrames(); err != nil {
		return err
	}
	if err := g.drawIntersections(); err != nil {
		return err
	}
	return nil

}

func (g *Gui) drawFrames() (err error) {
	maxX, maxY := termbox.Size()
	for _, v := range g.views {
		for x := v.X0 + 1; x < v.X1; x++ {
			if v.Y0 != -1 {
				g.SetCell(x, v.Y0, '─')
			}
			if v.Y1 != maxY {
				g.SetCell(x, v.Y1, '─')
			}
		}
		for y := v.Y0 + 1; y < v.Y1; y++ {
			if v.X0 != -1 {
				g.SetCell(v.X0, y, '│')
			}
			if v.X1 != maxX {
				g.SetCell(v.X1, y, '│')
			}
		}
	}
	return nil
}

func (g *Gui) drawIntersections() (err error) {
	for _, v := range g.views {
		if ch, ok := g.getIntersectionRune(v.X0, v.Y0); ok {
			g.SetCell(v.X0, v.Y0, ch)
		}
		if ch, ok := g.getIntersectionRune(v.X0, v.Y1); ok {
			g.SetCell(v.X0, v.Y1, ch)
		}
		if ch, ok := g.getIntersectionRune(v.X1, v.Y0); ok {
			g.SetCell(v.X1, v.Y0, ch)
		}
		if ch, ok := g.getIntersectionRune(v.X1, v.Y1); ok {
			g.SetCell(v.X1, v.Y1, ch)
		}
	}
	return nil
}

func (g *Gui) getIntersectionRune(x, y int) (ch rune, ok bool) {
	maxX, maxY := termbox.Size()
	if x < 0 || y < 0 || x >= maxX || y >= maxY {
		return 0, false
	}

	_chTop, _ := g.GetCell(x, y-1)
	chTop := verticalRune(_chTop)
	_chBottom, _ := g.GetCell(x, y+1)
	chBottom := verticalRune(_chBottom)
	_chLeft, _ := g.GetCell(x-1, y)
	chLeft := horizontalRune(_chLeft)
	_chRight, _ := g.GetCell(x+1, y)
	chRight := horizontalRune(_chRight)

	switch {
	case !chTop && chBottom && !chLeft && chRight:
		ch = '┌'
	case !chTop && chBottom && chLeft && !chRight:
		ch = '┐'
	case chTop && !chBottom && !chLeft && chRight:
		ch = '└'
	case chTop && !chBottom && chLeft && !chRight:
		ch = '┘'
	case chTop && chBottom && chLeft && chRight:
		ch = '┼'
	case chTop && chBottom && !chLeft && chRight:
		ch = '├'
	case chTop && chBottom && chLeft && !chRight:
		ch = '┤'
	case !chTop && chBottom && chLeft && chRight:
		ch = '┬'
	case chTop && !chBottom && chLeft && chRight:
		ch = '┴'
	default:
		return 0, false
	}
	return ch, true
}

func verticalRune(ch rune) bool {
	if ch == '│' || ch == '┼' || ch == '├' || ch == '┤' {
		return true
	}
	return false
}

func horizontalRune(ch rune) bool {
	if ch == '─' || ch == '┼' || ch == '┬' || ch == '┴' {
		return true
	}
	return false
}

func (g *Gui) resizeViews() (err error) {
	return nil
}

func (g *Gui) onKey(ev *termbox.Event) (err error) {
	switch ev.Key {
	case termbox.KeyCtrlC:
		return ErrorQuit
	default:
		return nil
	}
}
<commit_msg>More simplifications<commit_after>package gocui

import (
	"errors"

	"github.com/jroimartin/termbox-go"
)

var ErrorQuit = errors.New("quit")

type Gui struct {
	events           chan termbox.Event
	views            []*View
	currentView      *View
	BgColor, FgColor termbox.Attribute
}

func NewGui() (g *Gui) {
	return &Gui{}
}

func (g *Gui) Init() (err error) {
	g.events = make(chan termbox.Event, 20)
	g.BgColor = termbox.ColorWhite
	g.FgColor = termbox.ColorBlack
	return termbox.Init()
}

func (g *Gui) Close() {
	termbox.Close()
}

func (g *Gui) Size() (x, y int) {
	return termbox.Size()
}

func (g *Gui) AddView(name string, x0, y0, x1, y1 int) (v *View, err error) {
	maxX, maxY := termbox.Size()

	if x0 < -1 || y0 < -1 || x1 < -1 || y1 < -1 ||
		x0 > maxX || y0 > maxY || x1 > maxX || y1 > maxY ||
		x0 >= x1 || y0 >= y1 {
		return nil, errors.New("invalid points")
	}

	for _, v := range g.views {
		if name == v.Name {
			return nil, errors.New("invalid name")
		}
	}

	v = NewView(name, x0, y0, x1, y1)
	g.views = append(g.views, v)
	return v, nil
}

func (g *Gui) MainLoop() (err error) {
	go func() {
		for {
			g.events <- termbox.PollEvent()
		}
	}()

	if err := g.resize(); err != nil {
		return err
	}
	if err := g.draw(); err != nil {
		return err
	}
	// TODO: Set initial cursor position
	//termbox.SetCursor(10, 10)
	termbox.Flush()

	for {
		select {
		case ev := <-g.events:
			if err := g.handleEvent(&ev); err != nil {
				return err
			}
			if err := g.consumeevents(); err != nil {
				return err
			}
			if err := g.draw(); err != nil {
				return err
			}
			termbox.Flush()
		}
	}
	return nil
}

func (g *Gui) SetCell(x, y int, ch rune) (err error) {
	maxX, maxY := termbox.Size()
	if x < 0 || y < 0 || x >= maxX || y >= maxY {
		return errors.New("invalid point")
	}
	termbox.SetCell(x, y, ch, g.FgColor, g.BgColor)
	return nil
}

func (g *Gui) GetCell(x, y int) (ch rune, err error) {
	maxX, maxY := termbox.Size()
	if x < 0 || y < 0 || x >= maxX || y >= maxY {
		return 0, errors.New("invalid point")
	}
	c := termbox.CellBuffer()[y*maxX+x]
	return c.Ch, nil
}

func (g *Gui) consumeevents() (err error) {
	for {
		select {
		case ev := <-g.events:
			if err := g.handleEvent(&ev); err != nil {
				return err
			}
		default:
			return nil
		}
	}
}

func (g *Gui) handleEvent(ev *termbox.Event) (err error) {
	switch ev.Type {
	case termbox.EventKey:
		return g.onKey(ev)
	case termbox.EventResize:
		return g.resize()
	case termbox.EventError:
		return ev.Err
	default:
		return nil
	}
}

func (g *Gui) draw() (err error) {
	for _, v := range g.views {
		if err := g.drawView(v); err != nil {
			return err
		}
	}
	return nil
}

func (g *Gui) drawView(v *View) (err error) {
	return nil
}

func (g *Gui) resize() (err error) {
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
	if err := g.resizeViews(); err != nil {
		return err
	}
	if err := g.drawFrames(); err != nil {
		return err
	}
	if err := g.drawIntersections(); err != nil {
		return err
	}
	return nil

}

func (g *Gui) drawFrames() (err error) {
	maxX, maxY := termbox.Size()
	for _, v := range g.views {
		for x := v.X0 + 1; x < v.X1; x++ {
			if v.Y0 != -1 {
				g.SetCell(x, v.Y0, '─')
			}
			if v.Y1 != maxY {
				g.SetCell(x, v.Y1, '─')
			}
		}
		for y := v.Y0 + 1; y < v.Y1; y++ {
			if v.X0 != -1 {
				g.SetCell(v.X0, y, '│')
			}
			if v.X1 != maxX {
				g.SetCell(v.X1, y, '│')
			}
		}
	}
	return nil
}

func (g *Gui) drawIntersections() (err error) {
	for _, v := range g.views {
		if ch, ok := g.getIntersectionRune(v.X0, v.Y0); ok {
			g.SetCell(v.X0, v.Y0, ch)
		}
		if ch, ok := g.getIntersectionRune(v.X0, v.Y1); ok {
			g.SetCell(v.X0, v.Y1, ch)
		}
		if ch, ok := g.getIntersectionRune(v.X1, v.Y0); ok {
			g.SetCell(v.X1, v.Y0, ch)
		}
		if ch, ok := g.getIntersectionRune(v.X1, v.Y1); ok {
			g.SetCell(v.X1, v.Y1, ch)
		}
	}
	return nil
}

func (g *Gui) getIntersectionRune(x, y int) (ch rune, ok bool) {
	maxX, maxY := termbox.Size()
	if x < 0 || y < 0 || x >= maxX || y >= maxY {
		return 0, false
	}

	chTop, _ := g.GetCell(x, y-1)
	top := verticalRune(chTop)
	chBottom, _ := g.GetCell(x, y+1)
	bottom := verticalRune(chBottom)
	chLeft, _ := g.GetCell(x-1, y)
	left := horizontalRune(chLeft)
	chRight, _ := g.GetCell(x+1, y)
	right := horizontalRune(chRight)

	switch {
	case !top && bottom && !left && right:
		ch = '┌'
	case !top && bottom && left && !right:
		ch = '┐'
	case top && !bottom && !left && right:
		ch = '└'
	case top && !bottom && left && !right:
		ch = '┘'
	case top && bottom && left && right:
		ch = '┼'
	case top && bottom && !left && right:
		ch = '├'
	case top && bottom && left && !right:
		ch = '┤'
	case !top && bottom && left && right:
		ch = '┬'
	case top && !bottom && left && right:
		ch = '┴'
	default:
		return 0, false
	}
	return ch, true
}

func verticalRune(ch rune) bool {
	if ch == '│' || ch == '┼' || ch == '├' || ch == '┤' {
		return true
	}
	return false
}

func horizontalRune(ch rune) bool {
	if ch == '─' || ch == '┼' || ch == '┬' || ch == '┴' {
		return true
	}
	return false
}

func (g *Gui) resizeViews() (err error) {
	return nil
}

func (g *Gui) onKey(ev *termbox.Event) (err error) {
	switch ev.Key {
	case termbox.KeyCtrlC:
		return ErrorQuit
	default:
		return nil
	}
}
<|endoftext|>