<commit_before>package main

import (
  "bytes"
  "conf"
  "inotify"
  "log"
  "os"
  "os/exec"
  "strconv"
  "strings"
  "sync/atomic"
  "time"
)

var Config *conf.ConfigFile
var ConfigName string
var Home string
var Lock int64
var Locks []int64
var Paths []string
var RemoteCount int

func handleEventOld() {
  var cmd *exec.Cmd
  //var pid string
  //var pid_out bytes.Buffer
  var cmdStdout bytes.Buffer
  var e error

  if !atomic.CompareAndSwapInt64(&Lock, 0, 1) {
    return
  }

  /*// Get the pid and stop the server.
  log.Println("cat: preparing...")
  cmd = exec.Command("cat", "build/curve_https.pid")
  cmd.Stdout = &pid_out
  e = cmd.Run()
  if e != nil {
    log.Println("cat:", e)
    goto next
  }
  //pid, e := strconv.ParseInt(out.String(), 10, 32)
  //if e != nil { continue }
  pid = pid_out.String()
  log.Println("pid:", pid)
  log.Println("kill: preparing...")
  e = exec.Command("kill", pid).Run()
  if e != nil {
    log.Println("kill:", e)
    goto next
  }*/

  // Wait at least a short while before pulling the repo.
  log.Println("sleeping...")
  time.Sleep(5 * time.Second)

  // Pull from local repository.
  log.Println("git-pull: preparing...")
  e = exec.Command("git", "pull", "origin", "master").Run()
  if e != nil {
    log.Println("git-pull:", e)
    goto next
  }

  // Wait at least a short while before installing.
  log.Println("sleeping...")
  time.Sleep(5 * time.Second)

  // Build and install the code.
  log.Println("make: preparing...")
  cmd = exec.Command("make")
  cmd.Stdout = &cmdStdout
  e = cmd.Run()
  if e != nil {
    log.Println(cmdStdout.String())
    log.Println("make:", e)
  }

  // Start the server.
  log.Println("start: preparing...")
  e = exec.Command("foreman", "start").Start()
  if e != nil {
    log.Println("start:", e)
  }

next:
  atomic.CompareAndSwapInt64(&Lock, 1, 0)
  return
}

func handleEvent(ev *inotify.Event) {
  var cmd *exec.Cmd
  var cmdStdout bytes.Buffer
  var e error

  log.Println(ev)
  name := ev.Name

  log.Println("getting parent path...")
  idx := -1
  path := ""
  for i := 0; i < RemoteCount; i++ {
    if strings.HasPrefix(name, Paths[i]) {
      idx = i
      path = Paths[i]
      break
    }
  }
  if idx == -1 {
    log.Println("couldnt find parent path")
    return
  }

  log.Println("acquiring lock", idx)
  if !atomic.CompareAndSwapInt64(&Locks[idx], 0, 1) {
    log.Println("failed to acquire lock", idx)
    return
  }

  log.Println("sleeping...")
  time.Sleep(5 * time.Second)

  checkoutPath := strings.Join([]string{"/var/eggd", path}, "/")
  e = os.MkdirAll(checkoutPath, 0755)
  if e != nil {
    log.Println("os.MkdirAll:", e)
    goto next
  }
  e = os.Chdir(checkoutPath)
  if e != nil {
    log.Println("os.Chdir:", e)
    goto next
  }
  if os.IsNotExist(os.Chdir(".git")) {
    log.Println("running git-clone...")
    cmd = exec.Command("git", "clone", path, ".")
    cmd.Stdout = &cmdStdout
    e = cmd.Run()
    if e != nil {
      log.Println("git-clone:", e)
      log.Println(cmdStdout.String())
      goto next
    }
  } else {
    os.Chdir("..")
  }

  log.Println("running git-pull...")
  cmd = exec.Command("git", "pull", "origin", "master")
  cmd.Stdout = &cmdStdout
  cmd.Run()
  if e != nil {
    log.Println("git-pull:", e)
    log.Println(cmdStdout.String())
    goto next
  }

  //log.Println("sleeping...")
  //time.Sleep(5 * time.Second)

  log.Println("running make...")
  cmd = exec.Command("make")
  cmd.Stdout = &cmdStdout
  e = cmd.Run()
  if e != nil {
    log.Println("make:", e)
    log.Println(cmdStdout.String())
    goto next
  }

  log.Println("running foreman...")
  cmd = exec.Command("foreman", "start")
  cmd.Stdout = &cmdStdout
  e = cmd.Run()
  if e != nil {
    log.Println("foreman:", e)
    log.Println(cmdStdout.String())
    goto next
  }

next:
  log.Println("releasing lock", idx)
  atomic.CompareAndSwapInt64(&Locks[idx], 1, 0)
  return
}

func initConfig() {
  // Check that the configfile exists.
  Home = os.Getenv("HOME")
  //ConfigName = strings.Join([]string{Home, ".eggconfig"}, "/")
  ConfigName = "/etc/eggconfig"
  fd, e := os.OpenFile(ConfigName, os.O_RDWR | os.O_CREATE, 0644)
  if e != nil { log.Fatal(e) }
  e = fd.Close()
  if e != nil { log.Fatal(e) }

  // Make sure the configfile is set up correctly.
  Config, e := conf.ReadConfigFile(ConfigName)
  if e != nil { log.Fatal(e) }
  Config.AddSection("global")
  if !Config.HasOption("global", "count") {
    Config.AddOption("global", "count", "0")
  }

  // Modify the configfile through cmdline args.
  if len(os.Args) > 2 && os.Args[1] == "add" {
    log.Println("adding", os.Args[2], "to eggd path")
    count, _ := Config.GetInt("global", "count")
    count = count + 1
    Config.AddOption("global", "count", strconv.Itoa(count))
    section := strings.Join([]string{"remote-", strconv.Itoa(count)}, "")
    Config.AddOption(section, "path", os.Args[2])
    Config.WriteConfigFile(ConfigName, 0644, "")
    os.Exit(0)
  }

  RemoteCount, e = Config.GetInt("global", "count")
  if e != nil { log.Fatal(e) }
  Config.WriteConfigFile(ConfigName, 0644, "")
}

func startWatcher() {
  // Start the event loops. Use inotify to watch the configfile and each
  // repository path.
  Config, e := conf.ReadConfigFile(ConfigName)
  if e != nil { log.Fatal(e) }
  watcher, e := inotify.NewWatcher()
  if e != nil { log.Fatal(e) }
  log.Println("watching configfile")
  e = watcher.Watch(ConfigName)
  if e != nil { log.Fatal(e) }
  Locks = make([]int64, RemoteCount)
  Paths = make([]string, RemoteCount)
  for i := 1; i <= RemoteCount; i++ {
    section := strings.Join([]string{"remote-", strconv.Itoa(i)}, "")
    if !Config.HasOption(section, "path") {
      log.Println("doesnt have path")
      continue
    }
    path, e := Config.GetRawString(section, "path")
    if e != nil { log.Fatal(e) }
    log.Println("watching path", path)
    e = watcher.Watch(path)
    if e != nil { log.Fatal(e) }
    Locks[i-1] = 0
    Paths[i-1] = path
  }
  for {
    select {
      case ev := <-watcher.Event:
        go handleEvent(ev)
      case err := <-watcher.Error:
        log.Println("error:", err)
    }
  }
}

func main() {
  initConfig()
  startWatcher()
}
<commit_msg>Slightly less verbose logging, also more sleep()ing.<commit_after>package main

import (
  "bytes"
  "conf"
  "inotify"
  "log"
  "os"
  "os/exec"
  "strconv"
  "strings"
  "sync/atomic"
  "time"
)

var Config *conf.ConfigFile
var ConfigName string
var Home string
var Lock int64
var Locks []int64
var Paths []string
var RemoteCount int

func handleEventOld() {
  var cmd *exec.Cmd
  //var pid string
  //var pid_out bytes.Buffer
  var cmdStdout bytes.Buffer
  var e error

  if !atomic.CompareAndSwapInt64(&Lock, 0, 1) {
    return
  }

  /*// Get the pid and stop the server.
  log.Println("cat: preparing...")
  cmd = exec.Command("cat", "build/curve_https.pid")
  cmd.Stdout = &pid_out
  e = cmd.Run()
  if e != nil {
    log.Println("cat:", e)
    goto next
  }
  //pid, e := strconv.ParseInt(out.String(), 10, 32)
  //if e != nil { continue }
  pid = pid_out.String()
  log.Println("pid:", pid)
  log.Println("kill: preparing...")
  e = exec.Command("kill", pid).Run()
  if e != nil {
    log.Println("kill:", e)
    goto next
  }*/

  // Wait at least a short while before pulling the repo.
  log.Println("sleeping...")
  time.Sleep(5 * time.Second)

  // Pull from local repository.
  log.Println("git-pull: preparing...")
  e = exec.Command("git", "pull", "origin", "master").Run()
  if e != nil {
    log.Println("git-pull:", e)
    goto next
  }

  // Wait at least a short while before installing.
  log.Println("sleeping...")
  time.Sleep(5 * time.Second)

  // Build and install the code.
  log.Println("make: preparing...")
  cmd = exec.Command("make")
  cmd.Stdout = &cmdStdout
  e = cmd.Run()
  if e != nil {
    log.Println(cmdStdout.String())
    log.Println("make:", e)
  }

  // Start the server.
  log.Println("start: preparing...")
  e = exec.Command("foreman", "start").Start()
  if e != nil {
    log.Println("start:", e)
  }

next:
  atomic.CompareAndSwapInt64(&Lock, 1, 0)
  return
}

func handleEvent(ev *inotify.Event) {
  var cmd *exec.Cmd
  var cmdStdout bytes.Buffer
  var e error

  //log.Println(ev)
  name := ev.Name

  log.Println("getting parent path...")
  idx := -1
  path := ""
  for i := 0; i < RemoteCount; i++ {
    if strings.HasPrefix(name, Paths[i]) {
      idx = i
      path = Paths[i]
      break
    }
  }
  if idx == -1 {
    log.Println("couldnt find parent path")
    return
  }

  //log.Println("acquiring lock", idx)
  if !atomic.CompareAndSwapInt64(&Locks[idx], 0, 1) {
    //log.Println("failed to acquire lock", idx)
    return
  }
  log.Println("acquired lock", idx)

  log.Println("sleeping...")
  time.Sleep(5 * time.Second)

  checkoutPath := strings.Join([]string{"/var/eggd", path}, "/")
  e = os.MkdirAll(checkoutPath, 0755)
  if e != nil {
    log.Println("os.MkdirAll:", e)
    goto next
  }
  e = os.Chdir(checkoutPath)
  if e != nil {
    log.Println("os.Chdir:", e)
    goto next
  }
  if os.IsNotExist(os.Chdir(".git")) {
    log.Println("running git-clone...")
    cmd = exec.Command("git", "clone", path, ".")
    cmd.Stdout = &cmdStdout
    e = cmd.Run()
    if e != nil {
      log.Println("git-clone:", e)
      log.Println(cmdStdout.String())
      goto next
    }
  } else {
    os.Chdir("..")
  }

  log.Println("running git-pull...")
  cmd = exec.Command("git", "pull", "origin", "master")
  cmd.Stdout = &cmdStdout
  cmd.Run()
  if e != nil {
    log.Println("git-pull:", e)
    log.Println(cmdStdout.String())
    goto next
  }

  log.Println("sleeping...")
  time.Sleep(5 * time.Second)

  log.Println("running make...")
  cmd = exec.Command("make")
  cmd.Stdout = &cmdStdout
  e = cmd.Run()
  if e != nil {
    log.Println("make:", e)
    log.Println(cmdStdout.String())
    goto next
  }

  log.Println("running foreman...")
  cmd = exec.Command("foreman", "start")
  cmd.Stdout = &cmdStdout
  e = cmd.Run()
  if e != nil {
    log.Println("foreman:", e)
    log.Println(cmdStdout.String())
    goto next
  }

next:
  log.Println("releasing lock", idx)
  atomic.CompareAndSwapInt64(&Locks[idx], 1, 0)
  return
}

func initConfig() {
  // Check that the configfile exists.
  Home = os.Getenv("HOME")
  //ConfigName = strings.Join([]string{Home, ".eggconfig"}, "/")
  ConfigName = "/etc/eggconfig"
  fd, e := os.OpenFile(ConfigName, os.O_RDWR | os.O_CREATE, 0644)
  if e != nil { log.Fatal(e) }
  e = fd.Close()
  if e != nil { log.Fatal(e) }

  // Make sure the configfile is set up correctly.
  Config, e := conf.ReadConfigFile(ConfigName)
  if e != nil { log.Fatal(e) }
  Config.AddSection("global")
  if !Config.HasOption("global", "count") {
    Config.AddOption("global", "count", "0")
  }

  // Modify the configfile through cmdline args.
  if len(os.Args) > 2 && os.Args[1] == "add" {
    log.Println("adding", os.Args[2], "to eggd path")
    count, _ := Config.GetInt("global", "count")
    count = count + 1
    Config.AddOption("global", "count", strconv.Itoa(count))
    section := strings.Join([]string{"remote-", strconv.Itoa(count)}, "")
    Config.AddOption(section, "path", os.Args[2])
    Config.WriteConfigFile(ConfigName, 0644, "")
    os.Exit(0)
  }

  RemoteCount, e = Config.GetInt("global", "count")
  if e != nil { log.Fatal(e) }
  Config.WriteConfigFile(ConfigName, 0644, "")
}

func startWatcher() {
  // Start the event loops. Use inotify to watch the configfile and each
  // repository path.
  Config, e := conf.ReadConfigFile(ConfigName)
  if e != nil { log.Fatal(e) }
  watcher, e := inotify.NewWatcher()
  if e != nil { log.Fatal(e) }
  log.Println("watching configfile")
  e = watcher.Watch(ConfigName)
  if e != nil { log.Fatal(e) }
  Locks = make([]int64, RemoteCount)
  Paths = make([]string, RemoteCount)
  for i := 1; i <= RemoteCount; i++ {
    section := strings.Join([]string{"remote-", strconv.Itoa(i)}, "")
    if !Config.HasOption(section, "path") {
      log.Println("doesnt have path")
      continue
    }
    path, e := Config.GetRawString(section, "path")
    if e != nil { log.Fatal(e) }
    log.Println("watching path", path)
    e = watcher.Watch(path)
    if e != nil { log.Fatal(e) }
    Locks[i-1] = 0
    Paths[i-1] = path
  }
  for {
    select {
      case ev := <-watcher.Event:
        go handleEvent(ev)
      case err := <-watcher.Error:
        log.Println("error:", err)
    }
  }
}

func main() {
  initConfig()
  startWatcher()
}
<|endoftext|>


<commit_before>package texttable

import (
	"errors"
	"strings"
	"strconv"
	"github.com/mattn/go-runewidth"
	"regexp"
)

type cellAlignment int
const (
	ALIGN_LEFT cellAlignment = iota
	ALIGN_RIGHT
)

type rowType int
const (
	ROW_LINE rowType = iota
	ROW_CELLS
)

type cellUnit struct {
	content string
	alignment cellAlignment
}

type tableRow struct {
	cellUnits []*cellUnit
	kind rowType
}

type tableLine struct {}

type TextTable struct {
	header []*tableRow
	rows   []*tableRow
	width  int
	maxWidths []int
}

func (t *TextTable) updateColumnWidth(rows []*tableRow) {
	for _, row := range(rows) {
		for i, unit := range(row.cellUnits) {
			width := stringWidth(unit.content)
			if t.maxWidths[i] < width {
				t.maxWidths[i] = width
			}
		}
	}
}

func (t *TextTable) SetHeader(headers []string) error {
	if len(headers) == 0 {
		return errors.New("No header elements")
	}

	columnSize := len(headers)

	t.width = columnSize
	t.maxWidths = make([]int, columnSize)

	rows := stringsToTableRow(headers)
	t.updateColumnWidth(rows)

	t.header = rows

	return nil
}

func (t *TextTable) AddRow(strs []string) error {
	if len(strs) > t.width {
		return errors.New("row width should be less than header width")
	}

	padded := make([]string, t.width)
	copy(padded, strs)
	rows := stringsToTableRow(padded)
	t.rows = append(t.rows, rows...)

	t.updateColumnWidth(rows)

	return nil
}

func (t *TextTable) AddRowLine() error {
	rowLine := &tableRow{kind: ROW_LINE}
	t.rows = append(t.rows, rowLine)

	return nil
}

func (t *TextTable) borderString() string {
	borderString := "+"
	margin := 2

	for _, width := range(t.maxWidths) {
		for i := 0; i < width + margin; i++ {
			borderString += "-"
		}
		borderString += "+"
	}

	return borderString
}

func stringsToTableRow(strs []string) []*tableRow {
	maxHeight := calcMaxHeight(strs)
	strLines := make([][]string, maxHeight)

	for i := 0; i < maxHeight; i++ {
		strLines[i] = make([]string, len(strs))
	}

	alignments := make([]cellAlignment, len(strs))
	for i, str := range(strs) {
		alignments[i] = decideAlignment(str)
	}

	for i, str := range strs {
		divideds := strings.Split(str, "\n")
		for j, line := range divideds {
			strLines[j][i] = line
		}
	}

	rows := make([]*tableRow, maxHeight)
	for j := 0; j < maxHeight; j++ {
		row := new(tableRow)
		row.kind = ROW_CELLS
		for i := 0; i < len(strs); i++ {
			content := strLines[j][i]
			unit := &cellUnit{content: content}
			unit.alignment = alignments[i]
			row.cellUnits = append(row.cellUnits, unit)
		}

		rows[j] = row;
	}

	return rows
}

var hexRegexp = regexp.MustCompile("^0x")

func decideAlignment(str string) cellAlignment {
	_, err := strconv.ParseInt(str, 10, 64)
	if err == nil {
		return ALIGN_RIGHT
	}

	if hexRegexp.MatchString(str) {
		tmp := str[2:]
		_, err := strconv.ParseInt(tmp, 16, 64)
		if err == nil {
			return ALIGN_RIGHT
		}
	}

	_, err = strconv.ParseFloat(str, 64)
	if err == nil {
		return ALIGN_RIGHT
	}

	return ALIGN_LEFT
}

func calcMaxHeight(strs []string) int {
	max := -1

	for _, str := range strs {
		lines := strings.Split(str, "\n")
		height := len(lines)
		if height > max {
			max = height
		}
	}

	return max
}

func stringWidth(str string) int {
	return runewidth.StringWidth(str)
}

func (t *TextTable) Draw() string {
	drawedRows := make([]string, len(t.header) + len(t.rows) + 3)
	index := 0

	border := t.borderString()

	// top line
	drawedRows[index] = border
	index++

	for _, row := range(t.header) {
		drawedRows[index] = t.generateRowString(row)
		index++
	}

	drawedRows[index] = border
	index++

	for _, row := range(t.rows) {
		var rowStr string
		if row.kind == ROW_CELLS {
			rowStr = t.generateRowString(row)
		} else {
			rowStr = border
		}
		drawedRows[index] = rowStr
		index++
	}

	// bottom line
	if len(t.rows) != 0 {
		drawedRows[index] = border
		index++
	}

	return strings.Join(drawedRows[:index], "\n")
}

func formatCellUnit(unit *cellUnit, maxWidth int) string {
	str := unit.content
	width := stringWidth(unit.content)

	padding := strings.Repeat(" ", maxWidth - width)

	var ret string
	if unit.alignment == ALIGN_RIGHT {
		ret = padding + str
	} else {
		ret = str + padding
	}

	return " " + ret + " "
}

func (t *TextTable) generateRowString(row *tableRow) string {
	separator := "|"

	str := separator
	for i, unit := range(row.cellUnits) {
		str += formatCellUnit(unit, t.maxWidths[i])
		str += separator
	}

	return str
}
<commit_msg>Support hex number string without '0x' prefix<commit_after>package texttable

import (
	"errors"
	"strings"
	"strconv"
	"github.com/mattn/go-runewidth"
	"regexp"
)

type cellAlignment int
const (
	ALIGN_LEFT cellAlignment = iota
	ALIGN_RIGHT
)

type rowType int
const (
	ROW_LINE rowType = iota
	ROW_CELLS
)

type cellUnit struct {
	content string
	alignment cellAlignment
}

type tableRow struct {
	cellUnits []*cellUnit
	kind rowType
}

type tableLine struct {}

type TextTable struct {
	header []*tableRow
	rows   []*tableRow
	width  int
	maxWidths []int
}

func (t *TextTable) updateColumnWidth(rows []*tableRow) {
	for _, row := range(rows) {
		for i, unit := range(row.cellUnits) {
			width := stringWidth(unit.content)
			if t.maxWidths[i] < width {
				t.maxWidths[i] = width
			}
		}
	}
}

func (t *TextTable) SetHeader(headers []string) error {
	if len(headers) == 0 {
		return errors.New("No header elements")
	}

	columnSize := len(headers)

	t.width = columnSize
	t.maxWidths = make([]int, columnSize)

	rows := stringsToTableRow(headers)
	t.updateColumnWidth(rows)

	t.header = rows

	return nil
}

func (t *TextTable) AddRow(strs []string) error {
	if len(strs) > t.width {
		return errors.New("row width should be less than header width")
	}

	padded := make([]string, t.width)
	copy(padded, strs)
	rows := stringsToTableRow(padded)
	t.rows = append(t.rows, rows...)

	t.updateColumnWidth(rows)

	return nil
}

func (t *TextTable) AddRowLine() error {
	rowLine := &tableRow{kind: ROW_LINE}
	t.rows = append(t.rows, rowLine)

	return nil
}

func (t *TextTable) borderString() string {
	borderString := "+"
	margin := 2

	for _, width := range(t.maxWidths) {
		for i := 0; i < width + margin; i++ {
			borderString += "-"
		}
		borderString += "+"
	}

	return borderString
}

func stringsToTableRow(strs []string) []*tableRow {
	maxHeight := calcMaxHeight(strs)
	strLines := make([][]string, maxHeight)

	for i := 0; i < maxHeight; i++ {
		strLines[i] = make([]string, len(strs))
	}

	alignments := make([]cellAlignment, len(strs))
	for i, str := range(strs) {
		alignments[i] = decideAlignment(str)
	}

	for i, str := range strs {
		divideds := strings.Split(str, "\n")
		for j, line := range divideds {
			strLines[j][i] = line
		}
	}

	rows := make([]*tableRow, maxHeight)
	for j := 0; j < maxHeight; j++ {
		row := new(tableRow)
		row.kind = ROW_CELLS
		for i := 0; i < len(strs); i++ {
			content := strLines[j][i]
			unit := &cellUnit{content: content}
			unit.alignment = alignments[i]
			row.cellUnits = append(row.cellUnits, unit)
		}

		rows[j] = row;
	}

	return rows
}

var hexRegexp = regexp.MustCompile("^0x")

func decideAlignment(str string) cellAlignment {
	// decimal/octal number
	_, err := strconv.ParseInt(str, 10, 64)
	if err == nil {
		return ALIGN_RIGHT
	}

	// hex number
	_, err = strconv.ParseInt(str, 16, 64)
	if err == nil {
		return ALIGN_RIGHT
	}

	if hexRegexp.MatchString(str) {
		tmp := str[2:]
		_, err := strconv.ParseInt(tmp, 16, 64)
		if err == nil {
			return ALIGN_RIGHT
		}
	}

	_, err = strconv.ParseFloat(str, 64)
	if err == nil {
		return ALIGN_RIGHT
	}

	return ALIGN_LEFT
}

func calcMaxHeight(strs []string) int {
	max := -1

	for _, str := range strs {
		lines := strings.Split(str, "\n")
		height := len(lines)
		if height > max {
			max = height
		}
	}

	return max
}

func stringWidth(str string) int {
	return runewidth.StringWidth(str)
}

func (t *TextTable) Draw() string {
	drawedRows := make([]string, len(t.header) + len(t.rows) + 3)
	index := 0

	border := t.borderString()

	// top line
	drawedRows[index] = border
	index++

	for _, row := range(t.header) {
		drawedRows[index] = t.generateRowString(row)
		index++
	}

	drawedRows[index] = border
	index++

	for _, row := range(t.rows) {
		var rowStr string
		if row.kind == ROW_CELLS {
			rowStr = t.generateRowString(row)
		} else {
			rowStr = border
		}
		drawedRows[index] = rowStr
		index++
	}

	// bottom line
	if len(t.rows) != 0 {
		drawedRows[index] = border
		index++
	}

	return strings.Join(drawedRows[:index], "\n")
}

func formatCellUnit(unit *cellUnit, maxWidth int) string {
	str := unit.content
	width := stringWidth(unit.content)

	padding := strings.Repeat(" ", maxWidth - width)

	var ret string
	if unit.alignment == ALIGN_RIGHT {
		ret = padding + str
	} else {
		ret = str + padding
	}

	return " " + ret + " "
}

func (t *TextTable) generateRowString(row *tableRow) string {
	separator := "|"

	str := separator
	for i, unit := range(row.cellUnits) {
		str += formatCellUnit(unit, t.maxWidths[i])
		str += separator
	}

	return str
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"github.com/igm/pubsub"
	"gopkg.in/igm/sockjs-go.v2/sockjs"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
	"time"
)

type SysInfo struct {
	Cpu CpuInfo `json:"cpu"`
}

type CpuInfo struct {
	User    string `json:"user"`
	Nice    string `json:"nice"`
	System  string `json:"system"`
	Idle    string `json:"idle"`
	Iowait  string `json:"iowait"`
	Irq     string `json:"irq"`
	Softirq string `json:"softirq"`
}

var broadcaster pubsub.Publisher

func main() {
	fs := http.FileServer(http.Dir("../static"))
	http.Handle("/", fs)
	http.Handle("/ws/", sockjs.NewHandler("/ws", sockjs.DefaultOptions, wsHandler))
	log.Println("Listening...")
	go func() {
		poll()
	}()
	http.ListenAndServe(":3000", nil)
}

func wsHandler(session sockjs.Session) {
	log.Println("new sockjs session established")
	go func() {
		reader, _ := broadcaster.SubChannel(nil)
		for {
			status := <-reader
			if err := session.Send(status.(string)); err != nil {
				return
			}
		}
	}()
}

func poll() {
	for {
		status := read_status()
		system_status, err := json.Marshal(status)
		if err != nil {
			log.Println(err)
		}
		go func() {
			broadcaster.Publish(string(system_status))
		}()
		time.Sleep(time.Second)
	}
}

func read_status() SysInfo {
	system := SysInfo{read_cpu_info()}
	return system
}

func read_cpu_info() CpuInfo {
	data, err := ioutil.ReadFile("/proc/stat")
	if err != nil {
		panic(err)
	}
	cpu := split_on_newline(string(data))[0]
	fields := strings.Split(cpu, " ")
	cpu_info := CpuInfo{fields[0], fields[1], fields[2], fields[3], fields[4], fields[5], fields[6]}
	return cpu_info
}

func read_mem_info() string {
	data, err := ioutil.ReadFile("/proc/meminfo")
	if err != nil {
		panic(err)
	}
	return string(data)
}

func read_disk_info() string {
	data, err := ioutil.ReadFile("/proc/diskstats")
	if err != nil {
		panic(err)
	}
	return string(data)
}

func read_net_info() string {
	data, err := ioutil.ReadFile("/proc/net/dev")
	if err != nil {
		panic(err)
	}
	return string(data)
}

func split_on_newline(str string) []string {
	return strings.Split(str, "\n")
}
<commit_msg>fix cpu info fields<commit_after>package main

import (
	"encoding/json"
	"github.com/igm/pubsub"
	"gopkg.in/igm/sockjs-go.v2/sockjs"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
	"time"
)

type SysInfo struct {
	Cpu CpuInfo `json:"cpu"`
}

type CpuInfo struct {
	User   string `json:"user"`
	Nice   string `json:"nice"`
	System string `json:"system"`
	Idle   string `json:"idle"`
	Iowait string `json:"iowait"`
}

var broadcaster pubsub.Publisher

func main() {
	fs := http.FileServer(http.Dir("../static"))
	http.Handle("/", fs)
	http.Handle("/ws/", sockjs.NewHandler("/ws", sockjs.DefaultOptions, wsHandler))
	log.Println("Listening...")
	go func() {
		poll()
	}()
	http.ListenAndServe(":3000", nil)
}

func wsHandler(session sockjs.Session) {
	log.Println("new sockjs session established")
	go func() {
		reader, _ := broadcaster.SubChannel(nil)
		for {
			status := <-reader
			if err := session.Send(status.(string)); err != nil {
				return
			}
		}
	}()
}

func poll() {
	for {
		status := read_status()
		system_status, err := json.Marshal(status)
		if err != nil {
			log.Println(err)
		}
		go func() {
			broadcaster.Publish(string(system_status))
		}()
		time.Sleep(time.Second)
	}
}

func read_status() SysInfo {
	system := SysInfo{read_cpu_info()}
	return system
}

func read_cpu_info() CpuInfo {
	data, err := ioutil.ReadFile("/proc/stat")
	if err != nil {
		panic(err)
	}
	cpu := split_on_newline(string(data))[0]
	fields := strings.Fields(cpu)
	cpu_info := CpuInfo{fields[1], fields[2], fields[3], fields[4], fields[5]}
	return cpu_info
}

func read_mem_info() string {
	data, err := ioutil.ReadFile("/proc/meminfo")
	if err != nil {
		panic(err)
	}
	return string(data)
}

func read_disk_info() string {
	data, err := ioutil.ReadFile("/proc/diskstats")
	if err != nil {
		panic(err)
	}
	return string(data)
}

func read_net_info() string {
	data, err := ioutil.ReadFile("/proc/net/dev")
	if err != nil {
		panic(err)
	}
	return string(data)
}

func split_on_newline(str string) []string {
	return strings.Split(str, "\n")
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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 updateconfig

import (
	"bytes"
	"compress/gzip"
	"context"
	"fmt"
	"io/ioutil"
	"path"
	"path/filepath"
	"strings"
	"unicode/utf8"

	"github.com/mattn/go-zglob"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/sirupsen/logrus"
	coreapi "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"

	"k8s.io/test-infra/prow/config"
	"k8s.io/test-infra/prow/git/v2"
	"k8s.io/test-infra/prow/github"
	"k8s.io/test-infra/prow/kube"
	"k8s.io/test-infra/prow/pluginhelp"
	"k8s.io/test-infra/prow/plugins"
)

const (
	pluginName    = "config-updater"
	bootstrapMode = false
)

func init() {
	plugins.RegisterPullRequestHandler(pluginName, handlePullRequest, helpProvider)
}

func helpProvider(config *plugins.Configuration, enabledRepos []config.OrgRepo) (*pluginhelp.PluginHelp, error) {
	var configInfo map[string]string
	if len(enabledRepos) == 1 {
		msg := ""
		for configFileName, configMapSpec := range config.ConfigUpdater.Maps {
			msg = msg + fmt.Sprintf(
				"Files matching %s/%s are used to populate the %s ConfigMap in ",
				enabledRepos[0],
				configFileName,
				configMapSpec.Name,
			)
		}
		configInfo = map[string]string{"": msg}
	}
	return &pluginhelp.PluginHelp{
			Description: "The config-updater plugin automatically redeploys configuration and plugin configuration files when they change. The plugin watches for pull request merges that modify either of the config files and updates the cluster's configmap resources in response.",
			Config:      configInfo,
		},
		nil
}

type githubClient interface {
	CreateComment(owner, repo string, number int, comment string) error
	GetPullRequestChanges(org, repo string, number int) ([]github.PullRequestChange, error)
}

func handlePullRequest(pc plugins.Agent, pre github.PullRequestEvent) error {
	return handle(pc.GitHubClient, pc.GitClient, pc.KubernetesClient.CoreV1(), pc.BuildClusterCoreV1Clients, pc.Config.ProwJobNamespace, pc.Logger, pre, pc.PluginConfig.ConfigUpdater, pc.Metrics.ConfigMapGauges)
}

// FileGetter knows how to get the contents of a file by name
type FileGetter interface {
	GetFile(filename string) ([]byte, error)
}

type OSFileGetter struct {
	Root string
}

func (g *OSFileGetter) GetFile(filename string) ([]byte, error) {
	return ioutil.ReadFile(filepath.Join(g.Root, filename))
}

// Update updates the configmap with the data from the identified files.
// Existing configmap keys that are not included in the updates are left alone
// unless bootstrap is true in which case they are deleted.
func Update(fg FileGetter, kc corev1.ConfigMapInterface, name, namespace string, updates []ConfigMapUpdate, bootstrap bool, metrics *prometheus.GaugeVec, logger *logrus.Entry, sha string) error {
	cm, getErr := kc.Get(context.TODO(), name, metav1.GetOptions{})
	isNotFound := errors.IsNotFound(getErr)
	if getErr != nil && !isNotFound {
		return fmt.Errorf("failed to fetch current state of configmap: %w", getErr)
	}

	if cm == nil || isNotFound {
		cm = &coreapi.ConfigMap{
			ObjectMeta: metav1.ObjectMeta{
				Name:      name,
				Namespace: namespace,
				Labels: map[string]string{
					"app.kubernetes.io/name":      "prow",
					"app.kubernetes.io/component": "updateconfig-plugin",
				},
			},
		}
	}

	if cm.ObjectMeta.Labels == nil {
		cm.ObjectMeta.Labels = map[string]string{
			"app.kubernetes.io/name":      "prow",
			"app.kubernetes.io/component": "updateconfig-plugin",
		}
	}

	if cm.Data == nil || bootstrap {
		cm.Data = map[string]string{}
	}
	if sha != "" {
		cm.Data[config.ConfigVersionFileName] = sha
	}
	if cm.BinaryData == nil || bootstrap {
		cm.BinaryData = map[string][]byte{}
	}

	for _, upd := range updates {
		if upd.Filename == "" {
			logger.WithField("key", upd.Key).Debug("Deleting key.")
			delete(cm.Data, upd.Key)
			delete(cm.BinaryData, upd.Key)
			continue
		}

		content, err := fg.GetFile(upd.Filename)
		if err != nil {
			return fmt.Errorf("get file err: %w", err)
		}
		logger.WithFields(logrus.Fields{"key": upd.Key, "filename": upd.Filename}).Debug("Populating key.")
		value := content
		if upd.GZIP {
			buff := bytes.NewBuffer([]byte{})
			// TODO: this error is wildly unlikely for anything that
			// would actually fit in a configmap, we could just as well return
			// the error instead of falling back to the raw content
			z := gzip.NewWriter(buff)
			if _, err := z.Write(content); err != nil {
				logger.WithError(err).Error("failed to gzip content, falling back to raw")
			} else {
				if err := z.Close(); err != nil {
					logger.WithError(err).Error("failed to flush gzipped content (!?), falling back to raw")
				} else {
					value = buff.Bytes()
				}
			}
		}
		if utf8.ValidString(string(value)) {
			delete(cm.BinaryData, upd.Key)
			cm.Data[upd.Key] = string(value)
		} else {
			delete(cm.Data, upd.Key)
			cm.BinaryData[upd.Key] = value
		}
	}

	var updateErr error
	var verb string
	if getErr != nil && isNotFound {
		verb = "create"
		_, updateErr = kc.Create(context.TODO(), cm, metav1.CreateOptions{})
	} else {
		verb = "update"
		_, updateErr = kc.Update(context.TODO(), cm, metav1.UpdateOptions{})
	}
	if updateErr != nil {
		return fmt.Errorf("%s config map err: %w", verb, updateErr)
	}
	if metrics != nil {
		var size float64
		for _, data := range cm.Data {
			size += float64(len(data))
		}
		// in a strict sense this can race to update the value with other goroutines
		// handling other events, but as events are serialized due to the fact that
		// merges are serial in repositories, this is effectively not an issue here
		metrics.WithLabelValues(cm.Name, cm.Namespace).Set(size)
	}
	return nil
}

// ConfigMapUpdate is populated with information about a config map that should
// be updated.
type ConfigMapUpdate struct {
	Key, Filename string
	GZIP          bool
}

// FilterChanges determines which of the changes are relevant for config updating, returning mapping of
// config map to key to filename to update that key from.
func FilterChanges(cfg plugins.ConfigUpdater, changes []github.PullRequestChange, defaultNamespace string, log *logrus.Entry) map[plugins.ConfigMapID][]ConfigMapUpdate {
	toUpdate := map[plugins.ConfigMapID][]ConfigMapUpdate{}
	for _, change := range changes {
		var cm plugins.ConfigMapSpec
		found := false

		for key, configMap := range cfg.Maps {
			var matchErr error
			found, matchErr = zglob.Match(key, change.Filename)
			if matchErr != nil {
				// Should not happen, log matchErr and continue
				log.WithError(matchErr).Info("key matching error")
				continue
			}

			if found {
				cm = configMap
				break
			}
		}

		if !found {
			continue // This file does not define a configmap
		}

		// Yes, update the configmap with the contents of this file
		for cluster, namespaces := range cm.Clusters {
			for _, ns := range namespaces {
				id := plugins.ConfigMapID{Name: cm.Name, Namespace: ns, Cluster: cluster}
				key := cm.Key
				if key == "" {
					if cm.UseFullPathAsKey {
						key = strings.ReplaceAll(change.Filename, "/", "-")
					} else {
						key = path.Base(change.Filename)
					}
					// if the key changed, we need to remove the old key
					if change.Status == github.PullRequestFileRenamed {
						var oldKey string
						if cm.UseFullPathAsKey {
							oldKey = strings.ReplaceAll(change.PreviousFilename, "/", "-")
						} else {
							oldKey = path.Base(change.PreviousFilename)
						}
						// not setting the filename field will cause the key to be
						// deleted
						toUpdate[id] = append(toUpdate[id], ConfigMapUpdate{Key: oldKey})
					}
				}
				if change.Status == github.PullRequestFileRemoved {
					toUpdate[id] = append(toUpdate[id], ConfigMapUpdate{Key: key})
				} else {
					gzip := cfg.GZIP
					if cm.GZIP != nil {
						gzip = *cm.GZIP
					}
					toUpdate[id] = append(toUpdate[id], ConfigMapUpdate{Key: key, Filename: change.Filename, GZIP: gzip})
				}
			}
		}
	}
	return handleDefaultNamespace(toUpdate, defaultNamespace)
}

// handleDefaultNamespace ensures plugins.ConfigMapID.Namespace is not empty string
func handleDefaultNamespace(toUpdate map[plugins.ConfigMapID][]ConfigMapUpdate, defaultNamespace string) map[plugins.ConfigMapID][]ConfigMapUpdate {
	for cm, data := range toUpdate {
		if cm.Namespace == "" {
			key := plugins.ConfigMapID{Name: cm.Name, Namespace: defaultNamespace, Cluster: cm.Cluster}
			toUpdate[key] = append(toUpdate[key], data...)
			delete(toUpdate, cm)
		}
	}
	return toUpdate
}

func handle(gc githubClient, gitClient git.ClientFactory, kc corev1.ConfigMapsGetter, buildClusterCoreV1Clients map[string]corev1.CoreV1Interface, defaultNamespace string, log *logrus.Entry, pre github.PullRequestEvent, config plugins.ConfigUpdater, metrics *prometheus.GaugeVec) error {
	// Only consider newly merged PRs
	if pre.Action != github.PullRequestActionClosed {
		return nil
	}

	if len(config.Maps) == 0 { // Nothing to update
		return nil
	}

	pr := pre.PullRequest

	if !pr.Merged || pr.MergeSHA == nil || pr.Base.Repo.DefaultBranch != pr.Base.Ref {
		return nil
	}

	org := pr.Base.Repo.Owner.Login
	repo := pr.Base.Repo.Name

	// Which files changed in this PR?
	changes, err := gc.GetPullRequestChanges(org, repo, pr.Number)
	if err != nil {
		return err
	}

	message := func(name, cluster, namespace string, updates []ConfigMapUpdate, indent string) string {
		identifier := fmt.Sprintf("`%s` configmap", name)
		if namespace != "" {
			identifier = fmt.Sprintf("%s in namespace `%s`", identifier, namespace)
		}
		if cluster != "" {
			identifier = fmt.Sprintf("%s at cluster `%s`", identifier, cluster)
		}
		msg := fmt.Sprintf("%s using the following files:", identifier)
		for _, u := range updates {
			msg = fmt.Sprintf("%s\n%s- key `%s` using file `%s`", msg, indent, u.Key, u.Filename)
		}
		return msg
	}

	// Are any of the changes files ones that define a configmap we want to update?
	toUpdate := FilterChanges(config, changes, defaultNamespace, log)
	log.WithFields(logrus.Fields{
		"configmaps_to_update": len(toUpdate),
		"changes":              len(changes),
	}).Debug("Identified configmaps to update")

	var updated []string
	indent := " " // one space
	if len(toUpdate) > 1 {
		indent = "   " // three spaces for sub bullets
	}

	gitRepo, err := gitClient.ClientFor(org, repo)
	if err != nil {
		return err
	}
	defer func() {
		if err := gitRepo.Clean(); err != nil {
			log.WithError(err).Error("Could not clean up git repo cache.")
		}
	}()
	if err := gitRepo.Checkout(*pr.MergeSHA); err != nil {
		return err
	}

	var errs []error
	for cm, data := range toUpdate {
		logger := log.WithFields(logrus.Fields{"configmap": map[string]string{"name": cm.Name, "namespace": cm.Namespace, "cluster": cm.Cluster}})
		configMapClient, err := GetConfigMapClient(kc, cm.Namespace, buildClusterCoreV1Clients, cm.Cluster)
		if err != nil {
			log.WithError(err).Errorf("Failed to find configMap client")
			errs = append(errs, err)
			continue
		}
		if err := Update(&OSFileGetter{Root: gitRepo.Directory()}, configMapClient, cm.Name, cm.Namespace, data, bootstrapMode, metrics, logger, *pr.MergeSHA); err != nil {
			errs = append(errs, err)
			continue
		}
		updated = append(updated, message(cm.Name, cm.Cluster, cm.Namespace, data, indent))
	}

	var msg string
	switch n := len(updated); n {
	case 0:
		return utilerrors.NewAggregate(errs)
	case 1:
		msg = fmt.Sprintf("Updated the %s", updated[0])
	default:
		msg = fmt.Sprintf("Updated the following %d configmaps:\n", n)
		for _, updateMsg := range updated {
			msg += fmt.Sprintf(" * %s\n", updateMsg) // one space indent
		}
	}

	if err := gc.CreateComment(org, repo, pr.Number, plugins.FormatResponseRaw(pr.Body, pr.HTMLURL, pr.User.Login, msg)); err != nil {
		errs = append(errs, fmt.Errorf("comment err: %w", err))
	}
	return utilerrors.NewAggregate(errs)
}

// GetConfigMapClient returns a configMap interface according to the given cluster and namespace
func GetConfigMapClient(kc corev1.ConfigMapsGetter, namespace string, buildClusterCoreV1Clients map[string]corev1.CoreV1Interface, cluster string) (corev1.ConfigMapInterface, error) {
	configMapClient := kc.ConfigMaps(namespace)
	if cluster != kube.DefaultClusterAlias {
		if client, ok := buildClusterCoreV1Clients[cluster]; ok {
			configMapClient = client.ConfigMaps(namespace)
		} else {
			return nil, fmt.Errorf("no k8s client is found for build cluster: '%s'", cluster)
		}
	}
	return configMapClient, nil
}
<commit_msg>use variables<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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 updateconfig

import (
	"bytes"
	"compress/gzip"
	"context"
	"fmt"
	"io/ioutil"
	"path"
	"path/filepath"
	"strings"
	"unicode/utf8"

	"github.com/mattn/go-zglob"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/sirupsen/logrus"
	coreapi "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"

	"k8s.io/test-infra/prow/config"
	"k8s.io/test-infra/prow/git/v2"
	"k8s.io/test-infra/prow/github"
	"k8s.io/test-infra/prow/kube"
	"k8s.io/test-infra/prow/pluginhelp"
	"k8s.io/test-infra/prow/plugins"
)

const (
	pluginName    = "config-updater"
	bootstrapMode = false
)

func init() {
	plugins.RegisterPullRequestHandler(pluginName, handlePullRequest, helpProvider)
}

func helpProvider(config *plugins.Configuration, enabledRepos []config.OrgRepo) (*pluginhelp.PluginHelp, error) {
	var configInfo map[string]string
	if len(enabledRepos) == 1 {
		msg := ""
		for configFileName, configMapSpec := range config.ConfigUpdater.Maps {
			msg = msg + fmt.Sprintf(
				"Files matching %s/%s are used to populate the %s ConfigMap in ",
				enabledRepos[0],
				configFileName,
				configMapSpec.Name,
			)
		}
		configInfo = map[string]string{"": msg}
	}
	return &pluginhelp.PluginHelp{
			Description: "The config-updater plugin automatically redeploys configuration and plugin configuration files when they change. The plugin watches for pull request merges that modify either of the config files and updates the cluster's configmap resources in response.",
			Config:      configInfo,
		},
		nil
}

type githubClient interface {
	CreateComment(owner, repo string, number int, comment string) error
	GetPullRequestChanges(org, repo string, number int) ([]github.PullRequestChange, error)
}

func handlePullRequest(pc plugins.Agent, pre github.PullRequestEvent) error {
	return handle(pc.GitHubClient, pc.GitClient, pc.KubernetesClient.CoreV1(), pc.BuildClusterCoreV1Clients, pc.Config.ProwJobNamespace, pc.Logger, pre, pc.PluginConfig.ConfigUpdater, pc.Metrics.ConfigMapGauges)
}

// FileGetter knows how to get the contents of a file by name
type FileGetter interface {
	GetFile(filename string) ([]byte, error)
}

type OSFileGetter struct {
	Root string
}

func (g *OSFileGetter) GetFile(filename string) ([]byte, error) {
	return ioutil.ReadFile(filepath.Join(g.Root, filename))
}

// Update updates the configmap with the data from the identified files.
// Existing configmap keys that are not included in the updates are left alone
// unless bootstrap is true in which case they are deleted.
func Update(fg FileGetter, kc corev1.ConfigMapInterface, name, namespace string, updates []ConfigMapUpdate, bootstrap bool, metrics *prometheus.GaugeVec, logger *logrus.Entry, sha string) error {
	cm, getErr := kc.Get(context.TODO(), name, metav1.GetOptions{})
	isNotFound := errors.IsNotFound(getErr)
	if getErr != nil && !isNotFound {
		return fmt.Errorf("failed to fetch current state of configmap: %w", getErr)
	}

	labels := map[string]string{
		"app.kubernetes.io/name":      "prow",
		"app.kubernetes.io/component": "updateconfig-plugin",
	}

	if cm == nil || isNotFound {
		cm = &coreapi.ConfigMap{
			ObjectMeta: metav1.ObjectMeta{
				Name:      name,
				Namespace: namespace,
				Labels:    labels,
			},
		}
	}

	if cm.ObjectMeta.Labels == nil {
		cm.ObjectMeta.Labels = labels
	}

	if cm.Data == nil || bootstrap {
		cm.Data = map[string]string{}
	}
	if sha != "" {
		cm.Data[config.ConfigVersionFileName] = sha
	}
	if cm.BinaryData == nil || bootstrap {
		cm.BinaryData = map[string][]byte{}
	}

	for _, upd := range updates {
		if upd.Filename == "" {
			logger.WithField("key", upd.Key).Debug("Deleting key.")
			delete(cm.Data, upd.Key)
			delete(cm.BinaryData, upd.Key)
			continue
		}

		content, err := fg.GetFile(upd.Filename)
		if err != nil {
			return fmt.Errorf("get file err: %w", err)
		}
		logger.WithFields(logrus.Fields{"key": upd.Key, "filename": upd.Filename}).Debug("Populating key.")
		value := content
		if upd.GZIP {
			buff := bytes.NewBuffer([]byte{})
			// TODO: this error is wildly unlikely for anything that
			// would actually fit in a configmap, we could just as well return
			// the error instead of falling back to the raw content
			z := gzip.NewWriter(buff)
			if _, err := z.Write(content); err != nil {
				logger.WithError(err).Error("failed to gzip content, falling back to raw")
			} else {
				if err := z.Close(); err != nil {
					logger.WithError(err).Error("failed to flush gzipped content (!?), falling back to raw")
				} else {
					value = buff.Bytes()
				}
			}
		}
		if utf8.ValidString(string(value)) {
			delete(cm.BinaryData, upd.Key)
			cm.Data[upd.Key] = string(value)
		} else {
			delete(cm.Data, upd.Key)
			cm.BinaryData[upd.Key] = value
		}
	}

	var updateErr error
	var verb string
	if getErr != nil && isNotFound {
		verb = "create"
		_, updateErr = kc.Create(context.TODO(), cm, metav1.CreateOptions{})
	} else {
		verb = "update"
		_, updateErr = kc.Update(context.TODO(), cm, metav1.UpdateOptions{})
	}
	if updateErr != nil {
		return fmt.Errorf("%s config map err: %w", verb, updateErr)
	}
	if metrics != nil {
		var size float64
		for _, data := range cm.Data {
			size += float64(len(data))
		}
		// in a strict sense this can race to update the value with other goroutines
		// handling other events, but as events are serialized due to the fact that
		// merges are serial in repositories, this is effectively not an issue here
		metrics.WithLabelValues(cm.Name, cm.Namespace).Set(size)
	}
	return nil
}

// ConfigMapUpdate is populated with information about a config map that should
// be updated.
type ConfigMapUpdate struct {
	Key, Filename string
	GZIP          bool
}

// FilterChanges determines which of the changes are relevant for config updating, returning mapping of
// config map to key to filename to update that key from.
func FilterChanges(cfg plugins.ConfigUpdater, changes []github.PullRequestChange, defaultNamespace string, log *logrus.Entry) map[plugins.ConfigMapID][]ConfigMapUpdate {
	toUpdate := map[plugins.ConfigMapID][]ConfigMapUpdate{}
	for _, change := range changes {
		var cm plugins.ConfigMapSpec
		found := false

		for key, configMap := range cfg.Maps {
			var matchErr error
			found, matchErr = zglob.Match(key, change.Filename)
			if matchErr != nil {
				// Should not happen, log matchErr and continue
				log.WithError(matchErr).Info("key matching error")
				continue
			}

			if found {
				cm = configMap
				break
			}
		}

		if !found {
			continue // This file does not define a configmap
		}

		// Yes, update the configmap with the contents of this file
		for cluster, namespaces := range cm.Clusters {
			for _, ns := range namespaces {
				id := plugins.ConfigMapID{Name: cm.Name, Namespace: ns, Cluster: cluster}
				key := cm.Key
				if key == "" {
					if cm.UseFullPathAsKey {
						key = strings.ReplaceAll(change.Filename, "/", "-")
					} else {
						key = path.Base(change.Filename)
					}
					// if the key changed, we need to remove the old key
					if change.Status == github.PullRequestFileRenamed {
						var oldKey string
						if cm.UseFullPathAsKey {
							oldKey = strings.ReplaceAll(change.PreviousFilename, "/", "-")
						} else {
							oldKey = path.Base(change.PreviousFilename)
						}
						// not setting the filename field will cause the key to be
						// deleted
						toUpdate[id] = append(toUpdate[id], ConfigMapUpdate{Key: oldKey})
					}
				}
				if change.Status == github.PullRequestFileRemoved {
					toUpdate[id] = append(toUpdate[id], ConfigMapUpdate{Key: key})
				} else {
					gzip := cfg.GZIP
					if cm.GZIP != nil {
						gzip = *cm.GZIP
					}
					toUpdate[id] = append(toUpdate[id], ConfigMapUpdate{Key: key, Filename: change.Filename, GZIP: gzip})
				}
			}
		}
	}
	return handleDefaultNamespace(toUpdate, defaultNamespace)
}

// handleDefaultNamespace ensures plugins.ConfigMapID.Namespace is not empty string
func handleDefaultNamespace(toUpdate map[plugins.ConfigMapID][]ConfigMapUpdate, defaultNamespace string) map[plugins.ConfigMapID][]ConfigMapUpdate {
	for cm, data := range toUpdate {
		if cm.Namespace == "" {
			key := plugins.ConfigMapID{Name: cm.Name, Namespace: defaultNamespace, Cluster: cm.Cluster}
			toUpdate[key] = append(toUpdate[key], data...)
			delete(toUpdate, cm)
		}
	}
	return toUpdate
}

func handle(gc githubClient, gitClient git.ClientFactory, kc corev1.ConfigMapsGetter, buildClusterCoreV1Clients map[string]corev1.CoreV1Interface, defaultNamespace string, log *logrus.Entry, pre github.PullRequestEvent, config plugins.ConfigUpdater, metrics *prometheus.GaugeVec) error {
	// Only consider newly merged PRs
	if pre.Action != github.PullRequestActionClosed {
		return nil
	}

	if len(config.Maps) == 0 { // Nothing to update
		return nil
	}

	pr := pre.PullRequest

	if !pr.Merged || pr.MergeSHA == nil || pr.Base.Repo.DefaultBranch != pr.Base.Ref {
		return nil
	}

	org := pr.Base.Repo.Owner.Login
	repo := pr.Base.Repo.Name

	// Which files changed in this PR?
	changes, err := gc.GetPullRequestChanges(org, repo, pr.Number)
	if err != nil {
		return err
	}

	message := func(name, cluster, namespace string, updates []ConfigMapUpdate, indent string) string {
		identifier := fmt.Sprintf("`%s` configmap", name)
		if namespace != "" {
			identifier = fmt.Sprintf("%s in namespace `%s`", identifier, namespace)
		}
		if cluster != "" {
			identifier = fmt.Sprintf("%s at cluster `%s`", identifier, cluster)
		}
		msg := fmt.Sprintf("%s using the following files:", identifier)
		for _, u := range updates {
			msg = fmt.Sprintf("%s\n%s- key `%s` using file `%s`", msg, indent, u.Key, u.Filename)
		}
		return msg
	}

	// Are any of the changes files ones that define a configmap we want to update?
	toUpdate := FilterChanges(config, changes, defaultNamespace, log)
	log.WithFields(logrus.Fields{
		"configmaps_to_update": len(toUpdate),
		"changes":              len(changes),
	}).Debug("Identified configmaps to update")

	var updated []string
	indent := " " // one space
	if len(toUpdate) > 1 {
		indent = "   " // three spaces for sub bullets
	}

	gitRepo, err := gitClient.ClientFor(org, repo)
	if err != nil {
		return err
	}
	defer func() {
		if err := gitRepo.Clean(); err != nil {
			log.WithError(err).Error("Could not clean up git repo cache.")
		}
	}()
	if err := gitRepo.Checkout(*pr.MergeSHA); err != nil {
		return err
	}

	var errs []error
	for cm, data := range toUpdate {
		logger := log.WithFields(logrus.Fields{"configmap": map[string]string{"name": cm.Name, "namespace": cm.Namespace, "cluster": cm.Cluster}})
		configMapClient, err := GetConfigMapClient(kc, cm.Namespace, buildClusterCoreV1Clients, cm.Cluster)
		if err != nil {
			log.WithError(err).Errorf("Failed to find configMap client")
			errs = append(errs, err)
			continue
		}
		if err := Update(&OSFileGetter{Root: gitRepo.Directory()}, configMapClient, cm.Name, cm.Namespace, data, bootstrapMode, metrics, logger, *pr.MergeSHA); err != nil {
			errs = append(errs, err)
			continue
		}
		updated = append(updated, message(cm.Name, cm.Cluster, cm.Namespace, data, indent))
	}

	var msg string
	switch n := len(updated); n {
	case 0:
		return utilerrors.NewAggregate(errs)
	case 1:
		msg = fmt.Sprintf("Updated the %s", updated[0])
	default:
		msg = fmt.Sprintf("Updated the following %d configmaps:\n", n)
		for _, updateMsg := range updated {
			msg += fmt.Sprintf(" * %s\n", updateMsg) // one space indent
		}
	}

	if err := gc.CreateComment(org, repo, pr.Number, plugins.FormatResponseRaw(pr.Body, pr.HTMLURL, pr.User.Login, msg)); err != nil {
		errs = append(errs, fmt.Errorf("comment err: %w", err))
	}
	return utilerrors.NewAggregate(errs)
}

// GetConfigMapClient returns a configMap interface according to the given cluster and namespace
func GetConfigMapClient(kc corev1.ConfigMapsGetter, namespace string, buildClusterCoreV1Clients map[string]corev1.CoreV1Interface, cluster string) (corev1.ConfigMapInterface, error) {
	configMapClient := kc.ConfigMaps(namespace)
	if cluster != kube.DefaultClusterAlias {
		if client, ok := buildClusterCoreV1Clients[cluster]; ok {
			configMapClient = client.ConfigMaps(namespace)
		} else {
			return nil, fmt.Errorf("no k8s client is found for build cluster: '%s'", cluster)
		}
	}
	return configMapClient, nil
}
<|endoftext|>


<commit_before>package util

import (
	"reflect"
	"strconv"
	"strings"
)

func EncodeTag(tag string) (data map[string]string) {
	data = map[string]string{}
	if tag == "" {
		return
	}
	for _, item := range strings.Split(tag, " ") {
		if item == "" {
			continue
		}
		key := strings.Split(item, ":")[0]
		value := strings.Split(item, "\"")[1]
		data[key] = value
	}

	return
}

func MapListToObjList(obj interface{}, mappers []map[string]interface{}, useTag string) {

	pointer := reflect.Indirect(reflect.ValueOf(obj))
	typer := pointer.Type().Elem()

	for _, mapper := range mappers {
		item := reflect.New(typer)
		MapToObj(item.Interface(), mapper, useTag)
		pointer.Set(reflect.Append(pointer, reflect.Indirect(item)))
	}
}

func ObjListToMapList(obj interface{}, useTag string) (mappers []map[string]interface{}) {
	mappers = []map[string]interface{}{}

	value := reflect.ValueOf(obj)
	for i := 0; i < value.Len(); i = i + 1 {
		item := value.Index(i)
		mappers = append(mappers, ObjToMap(item.Interface(), useTag))
	}
	return
}

// 根据map的key=>value设置Obj的field=>fieldValue
// 如果传了useTag,那么就会根据obj的Tag的useTag的值获取mapValue并填充到field上,
// 返回设置成功的Fields列表字段
func MapToObj(obj interface{}, mapper map[string]interface{}, useTag string) (fields []string) {
	if mapper == nil || len(mapper) == 0 {
		return
	}
	pointer := reflect.Indirect(reflect.ValueOf(obj))
	var tag2field = map[string]string{}
	if useTag != "" {
		fieldTagMapper := GetTagMapperFromPool(obj)
		for k, v := range fieldTagMapper.GetFieldMapByTagName(useTag) {
			v = strings.Split(v, ",")[0]
			tag2field[v] = k
		}
	}

	fields = []string{}
	for fieldName, value := range mapper {
		if useTag != "" {
			fieldName = tag2field[fieldName]
		}
		field := pointer.FieldByName(fieldName)
		if field.IsValid() && field.CanInterface() {
			setFieldValue(field, value)
			fields = append(fields, fieldName)
		}
	}

	return
}

func MapStringToObj(obj interface{}, mapper map[string]string, useTag string) (fields []string) {
	mapper2 := map[string]interface{}{}
	for k, v := range mapper {
		mapper2[k] = v
	}
	return MapToObj(obj, mapper2, useTag)
}

func setFieldValue(field reflect.Value, value interface{}) {
	switch field.Interface().(type) {
	case bool:
		b, ok := Interface2Bool(value, false)
		if ok {
			field.SetBool(b)
		}
	case string:
		s, ok := Interface2String(value, false)
		if ok {
			field.SetString(s)
		}
	case int, int8, int16, int32, int64:
		i, ok := Interface2Int(value, false)
		if ok {
			field.SetInt(i)
		}
	case float32, float64:
		f, ok := Interface2Float(value, false)
		if ok {
			field.SetFloat(f)
		}
	default:
		println("not case type : " + field.Type().String())
		break
	}
}

func Interface2Int(value interface{}, strict bool) (v int64, ok bool) {
	switch value.(type) {
	case int:
		v, ok = int64(value.(int)), true
	case int8:
		v, ok = int64(value.(int8)), true
	case int32:
		v, ok = int64(value.(int32)), true
	case int64:
		v, ok = int64(value.(int64)), true
	}
	if ok {
		return
	}
	if strict {
		if !ok {
			return
		}
	}

	switch value.(type) {
	case string, []uint8:
		s, _ := Interface2String(value, true)
		i, err := strconv.ParseInt(s, 10, 64)
		if err == nil {
			v, ok = i, true
		}
	case float32, float64:
		f, _ := Interface2Float(value, true)
		v, ok = int64(f), true
	case bool:
		if value.(bool) {
			v = 1
		} else {
			v = 0
		}
		ok = true
	}
	return
}

func Interface2Bool(value interface{}, strict bool) (v bool, ok bool) {
	if strict {
		v, ok = value.(bool)
		return
	}
	switch value.(type) {
	case bool:
		v, ok = value.(bool), true
	case int8, int, int32, int64:
		i, _ := Interface2Int(value, true)
		v, ok = i == 1, true
	case float32, float64:
		i, _ := Interface2Float(value, true)
		v, ok = i == 1, true
	case string, []uint8:
		s, _ := Interface2String(value, true)
		s = strings.ToLower(s)
		v, ok = s == "1" || s == "true", true
	}

	return
}

func Interface2Float(value interface{}, strict bool) (v float64, ok bool) {
	switch value.(type) {
	case float32:
		v, ok = float64(value.(float32)), true
	case float64:
		v, ok = float64(value.(float64)), true
	}
	if ok {
		return
	}
	if strict {
		if !ok {
			return
		}
	}

	switch value.(type) {
	case int, int8, int32, int64:
		i, ok := Interface2Int(value, true)
		if ok {
			v, ok = float64(i), true
		}
	case string:
		f, err := strconv.ParseFloat(value.(string), 64)
		if err != nil {
			return 0, false
		}
		return f, true
	case []uint8:
		f, err := strconv.ParseFloat(string(value.([]uint8)), 64)
		if err != nil {
			return 0, false
		}
		return f, true
	}
	return
}

func Interface2String(value interface{}, strict bool) (v string, ok bool) {
	switch value.(type) {
	case string:
		v, ok = value.(string), true
	case []uint8:
		v, ok = string(value.([]uint8)), true
	}

	if ok {
		return
	}
	if strict {
		if !ok {
			return
		}
	}

	switch value.(type) {
	case int64, int8, int32, int:
		i, _ := Interface2Int(value, true)
		v, ok = strconv.FormatInt(i, 10), true
	case float64, float32:
		f, _ := Interface2Float(value, true)
		v, ok = strconv.FormatFloat(f, 'f', -1, 64), true
	case bool:
		v, ok = strconv.FormatBool(value.(bool)), true
	}
	return
}

func ObjToMap(obj interface{}, useTag string) map[string]interface{} {
	pointer := reflect.Indirect(reflect.ValueOf(obj))
	typer := pointer.Type()

	fieldNum := pointer.NumField()
	var fieldNameToTagName map[string]string
	if useTag != "" {
		fieldTagMapper := GetTagMapperFromPool(obj)
		fieldNameToTagName = fieldTagMapper.GetFieldMapByTagName(useTag)
	}

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

	for i := 0; i < fieldNum; i++ {
		field := pointer.Field(i)
		key := typer.Field(i).Name

		if !field.CanInterface(){
			continue
		}

		if useTag != "" {
			// 根据指定的tag的key重新映射
			key = fieldNameToTagName[key]
			// 如果有逗号 比如 json:"password,omitempty" 则只取逗号前面的第一个
			key = strings.Split(key, ",")[0]
			// 有值才填充
			if key != "" {
				data[key] = field.Interface()
			}
		} else {
			data[key] = field.Interface()
		}

	}

	return data
}

//判断一个array每一个原始是不是都在map的value里
func ArrayInMapValue(min []string, m map[string]string) (has bool, msg string) {
	if min == nil || len(min) == 0 {
		return true, ""
	}
	lenMin := len(min)
	for minI := 0; minI < lenMin; minI = minI + 1 {
		_, has = m[min[minI]]
		if !has {
			return false, min[minI]
		}
	}
	return true, ""
}

//获取map的keys
func GetMapKey(m map[string]string) (keys []string) {
	keys = []string{}
	for key := range m {
		keys = append(keys, key)
	}
	return keys
}

//判断一个array每一个元素是不是都在map的key里
func ArrayInMapKey(min []string, m map[string]string) (has bool, msg string) {
	if min == nil || len(min) == 0 {
		has = true
		return
	}
	if m == nil {
		has = false
		return
	}
	lenMin := len(min)
	for minI := 0; minI < lenMin; minI++ {
		_, has = m[min[minI]]
		if !has {
			msg = min[minI]
			return
		}
	}
	has = true
	return
}

func ArrayInArray(min []string, max []string) (has bool, msg string) {
	if min == nil || len(min) == 0 {
		return true, ""
	}

	lenMax := len(max)
	lenMin := len(min)
	for minI := 0; minI < lenMin; minI = minI + 1 {
		has := false
		for maxI := 0; maxI < lenMax; maxI = maxI + 1 {
			if max[maxI] == min[minI] {
				has = true
			}
		}
		if !has {
			return false, min[minI]
		}
	}
	return true, ""
}

// 判断item是否在数组里
// 如果数组为空则返回false
func ItemInArray(item string, max []string) (has bool) {
	return ArrayStringIndex(item, max) != -1
}

func ArrayStringIndex(item string, max []string) (index int) {
	index = -1
	if max == nil || len(max) == 0 {
		return
	}
	for i, l := 0, len(max); i < l; i++ {
		if max[i] == item {
			index = i
			return
		}
	}
	return
}

// 判断item是否在数组里
// 如果数组为空则返回false
func ItemInArrayInt(item int, max []int) (has bool) {

	if max == nil || len(max) == 0 {
		return false
	}

	lenMax := len(max)

	for maxI := 0; maxI < lenMax; maxI = maxI + 1 {
		if max[maxI] == item {
			return true
		}
	}

	return false
}

func IsEmptyValue(value interface{}) bool {
	v := reflect.ValueOf(value)
	switch v.Kind() {
	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
		return v.Len() == 0
	case reflect.Bool:
		return !v.Bool()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return v.Int() == 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return v.Uint() == 0
	case reflect.Float32, reflect.Float64:
		return v.Float() == 0
	case reflect.Interface, reflect.Ptr:
		return v.IsNil()
	}
	return false
}

func EmptyObject(obj interface{}) {
	pointer := reflect.Indirect(reflect.ValueOf(obj))
	fieldNum := pointer.NumField()

	for i := 0; i < fieldNum; i++ {
		v := pointer.Field(i)
		switch v.Kind() {
		case reflect.String:
			v.SetString("")
			break
		case reflect.Bool:
			v.SetBool(false)
			break
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			v.SetInt(0)
			break
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
			v.SetUint(0)
			break
		case reflect.Float32, reflect.Float64:
			v.SetFloat(0)
			break
		case reflect.Interface, reflect.Ptr:
			break
		}
	}
}

func MapInterface2MapString(m map[string]interface{}) map[string]string {
	set := map[string]string{}

	for key, value := range m {
		v, ok := Interface2String(value, false)
		if ok {
			set[key] = v
		}
	}
	return set
}<commit_msg>support slice for map2obj<commit_after>package util

import (
	"github.com/bysir-zl/bygo/log"
	"reflect"
	"strconv"
	"strings"
)

func EncodeTag(tag string) (data map[string]string) {
	data = map[string]string{}
	if tag == "" {
		return
	}
	for _, item := range strings.Split(tag, " ") {
		if item == "" {
			continue
		}
		key := strings.Split(item, ":")[0]
		value := strings.Split(item, "\"")[1]
		data[key] = value
	}

	return
}

func MapListToObjList(obj interface{}, mappers []map[string]interface{}, useTag string) {
	pointer := reflect.Indirect(reflect.ValueOf(obj))
	typer := pointer.Type().Elem()

	for _, mapper := range mappers {
		item := reflect.New(typer)
		MapToObj(item.Interface(), mapper, useTag)
		pointer.Set(reflect.Append(pointer, reflect.Indirect(item)))
	}
}

func ObjListToMapList(obj interface{}, useTag string) (mappers []map[string]interface{}) {
	mappers = []map[string]interface{}{}

	value := reflect.ValueOf(obj)
	for i := 0; i < value.Len(); i = i + 1 {
		item := value.Index(i)
		mappers = append(mappers, ObjToMap(item.Interface(), useTag))
	}
	return
}

// 根据map的key=>value设置Obj的field=>fieldValue
// 如果传了useTag,那么就会根据obj的Tag的useTag的值获取mapValue并填充到field上,
// 返回设置成功的Fields列表字段
func MapToObj(obj interface{}, mapper map[string]interface{}, useTag string) (fields []string) {
	if mapper == nil || len(mapper) == 0 {
		return
	}
	pointer := reflect.Indirect(reflect.ValueOf(obj))
	var tag2field = map[string]string{}
	if useTag != "" {
		fieldTagMapper := GetTagMapperFromPool(obj)
		for k, v := range fieldTagMapper.GetFieldMapByTagName(useTag) {
			v = strings.Split(v, ",")[0]
			tag2field[v] = k
		}
	}

	fields = []string{}
	for fieldName, value := range mapper {
		if useTag != "" {
			fieldName = tag2field[fieldName]
		}
		field := pointer.FieldByName(fieldName)
		if field.IsValid() && field.CanInterface() && field.CanSet() {
			setValue(field, value)
			fields = append(fields, fieldName)
		}
	}

	return
}

func MapStringToObj(obj interface{}, mapper map[string]string, useTag string) (fields []string) {
	mapper2 := map[string]interface{}{}
	for k, v := range mapper {
		mapper2[k] = v
	}
	return MapToObj(obj, mapper2, useTag)
}

func setValue(v reflect.Value, value interface{}) {
	switch v.Kind() {
	case reflect.Bool:
		b, ok := Interface2Bool(value, false)
		if ok {
			v.SetBool(b)
		}
	case reflect.String:
		s, ok := Interface2String(value, false)
		if ok {
			v.SetString(s)
		}
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		i, ok := Interface2Int(value, false)
		if ok {
			v.SetInt(i)
		}
	case reflect.Float32, reflect.Float64:
		f, ok := Interface2Float(value, false)
		if ok {
			v.SetFloat(f)
		}
	case reflect.Slice:
		vv := reflect.ValueOf(value)
		if vv.Kind() == reflect.Array || v.Kind() == reflect.Slice {
			l := vv.Len()
			newV := reflect.MakeSlice(v.Type(), l, l)
			for i := 0; i < l; i++ {
				setValue(newV.Index(i), vv.Index(i).Interface())
			}
			v.Set(newV)
		}
	default:
		log.Info("setValue", "not case "+v.Type().String())
		break
	}
}

func Interface2Int(value interface{}, strict bool) (v int64, ok bool) {
	switch value.(type) {
	case int:
		v, ok = int64(value.(int)), true
	case int8:
		v, ok = int64(value.(int8)), true
	case int32:
		v, ok = int64(value.(int32)), true
	case int64:
		v, ok = int64(value.(int64)), true
	}
	if ok {
		return
	}
	if strict {
		if !ok {
			return
		}
	}

	switch value.(type) {
	case string, []uint8:
		s, _ := Interface2String(value, true)
		i, err := strconv.ParseInt(s, 10, 64)
		if err == nil {
			v, ok = i, true
		}
	case float32, float64:
		f, _ := Interface2Float(value, true)
		v, ok = int64(f), true
	case bool:
		if value.(bool) {
			v = 1
		} else {
			v = 0
		}
		ok = true
	}
	return
}

func Interface2Bool(value interface{}, strict bool) (v bool, ok bool) {
	if strict {
		v, ok = value.(bool)
		return
	}
	switch value.(type) {
	case bool:
		v, ok = value.(bool), true
	case int8, int, int32, int64:
		i, _ := Interface2Int(value, true)
		v, ok = i == 1, true
	case float32, float64:
		i, _ := Interface2Float(value, true)
		v, ok = i == 1, true
	case string, []uint8:
		s, _ := Interface2String(value, true)
		s = strings.ToLower(s)
		v, ok = s == "1" || s == "true", true
	}

	return
}

func Interface2Float(value interface{}, strict bool) (v float64, ok bool) {
	switch value.(type) {
	case float32:
		v, ok = float64(value.(float32)), true
	case float64:
		v, ok = float64(value.(float64)), true
	}
	if ok {
		return
	}
	if strict {
		if !ok {
			return
		}
	}

	switch value.(type) {
	case int, int8, int32, int64:
		i, ok := Interface2Int(value, true)
		if ok {
			v, ok = float64(i), true
		}
	case string:
		f, err := strconv.ParseFloat(value.(string), 64)
		if err != nil {
			return 0, false
		}
		return f, true
	case []uint8:
		f, err := strconv.ParseFloat(string(value.([]uint8)), 64)
		if err != nil {
			return 0, false
		}
		return f, true
	}
	return
}

func Interface2String(value interface{}, strict bool) (v string, ok bool) {
	switch value.(type) {
	case string:
		v, ok = value.(string), true
	case []uint8:
		v, ok = string(value.([]uint8)), true
	}

	if ok {
		return
	}
	if strict {
		if !ok {
			return
		}
	}

	switch value.(type) {
	case int64, int8, int32, int:
		i, _ := Interface2Int(value, true)
		v, ok = strconv.FormatInt(i, 10), true
	case float64, float32:
		f, _ := Interface2Float(value, true)
		v, ok = strconv.FormatFloat(f, 'f', -1, 64), true
	case bool:
		v, ok = strconv.FormatBool(value.(bool)), true
	}
	return
}

func ObjToMap(obj interface{}, useTag string) map[string]interface{} {
	pointer := reflect.Indirect(reflect.ValueOf(obj))
	typer := pointer.Type()

	fieldNum := pointer.NumField()
	var fieldNameToTagName map[string]string
	if useTag != "" {
		fieldTagMapper := GetTagMapperFromPool(obj)
		fieldNameToTagName = fieldTagMapper.GetFieldMapByTagName(useTag)
	}

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

	for i := 0; i < fieldNum; i++ {
		field := pointer.Field(i)
		key := typer.Field(i).Name

		if !field.CanInterface() {
			continue
		}

		if useTag != "" {
			// 根据指定的tag的key重新映射
			key = fieldNameToTagName[key]
			// 如果有逗号 比如 json:"password,omitempty" 则只取逗号前面的第一个
			key = strings.Split(key, ",")[0]
			// 有值才填充
			if key != "" {
				data[key] = field.Interface()
			}
		} else {
			data[key] = field.Interface()
		}

	}

	return data
}

//判断一个array每一个原始是不是都在map的value里
func ArrayInMapValue(min []string, m map[string]string) (has bool, msg string) {
	if min == nil || len(min) == 0 {
		return true, ""
	}
	lenMin := len(min)
	for minI := 0; minI < lenMin; minI = minI + 1 {
		_, has = m[min[minI]]
		if !has {
			return false, min[minI]
		}
	}
	return true, ""
}

//获取map的keys
func GetMapKey(m map[string]string) (keys []string) {
	keys = []string{}
	for key := range m {
		keys = append(keys, key)
	}
	return keys
}

//判断一个array每一个元素是不是都在map的key里
func ArrayInMapKey(min []string, m map[string]string) (has bool, msg string) {
	if min == nil || len(min) == 0 {
		has = true
		return
	}
	if m == nil {
		has = false
		return
	}
	lenMin := len(min)
	for minI := 0; minI < lenMin; minI++ {
		_, has = m[min[minI]]
		if !has {
			msg = min[minI]
			return
		}
	}
	has = true
	return
}

func ArrayInArray(min []string, max []string) (has bool, msg string) {
	if min == nil || len(min) == 0 {
		return true, ""
	}

	lenMax := len(max)
	lenMin := len(min)
	for minI := 0; minI < lenMin; minI = minI + 1 {
		has := false
		for maxI := 0; maxI < lenMax; maxI = maxI + 1 {
			if max[maxI] == min[minI] {
				has = true
			}
		}
		if !has {
			return false, min[minI]
		}
	}
	return true, ""
}

// 判断item是否在数组里
// 如果数组为空则返回false
func ItemInArray(item string, max []string) (has bool) {
	return ArrayStringIndex(item, max) != -1
}

func ArrayStringIndex(item string, max []string) (index int) {
	index = -1
	if max == nil || len(max) == 0 {
		return
	}
	for i, l := 0, len(max); i < l; i++ {
		if max[i] == item {
			index = i
			return
		}
	}
	return
}

// 判断item是否在数组里
// 如果数组为空则返回false
func ItemInArrayInt(item int, max []int) (has bool) {

	if max == nil || len(max) == 0 {
		return false
	}

	lenMax := len(max)

	for maxI := 0; maxI < lenMax; maxI = maxI + 1 {
		if max[maxI] == item {
			return true
		}
	}

	return false
}

func IsEmptyValue(value interface{}) bool {
	v := reflect.ValueOf(value)
	switch v.Kind() {
	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
		return v.Len() == 0
	case reflect.Bool:
		return !v.Bool()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return v.Int() == 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return v.Uint() == 0
	case reflect.Float32, reflect.Float64:
		return v.Float() == 0
	case reflect.Interface, reflect.Ptr:
		return v.IsNil()
	}
	return false
}

func EmptyObject(obj interface{}) {
	pointer := reflect.Indirect(reflect.ValueOf(obj))
	fieldNum := pointer.NumField()

	for i := 0; i < fieldNum; i++ {
		v := pointer.Field(i)
		switch v.Kind() {
		case reflect.String:
			v.SetString("")
			break
		case reflect.Bool:
			v.SetBool(false)
			break
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			v.SetInt(0)
			break
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
			v.SetUint(0)
			break
		case reflect.Float32, reflect.Float64:
			v.SetFloat(0)
			break
		case reflect.Interface, reflect.Ptr:
			break
		}
	}
}

func MapInterface2MapString(m map[string]interface{}) map[string]string {
	set := map[string]string{}

	for key, value := range m {
		v, ok := Interface2String(value, false)
		if ok {
			set[key] = v
		}
	}
	return set
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"sort"
	"strings"

	"github.com/vattle/sqlboiler/bdb"
)

// imports defines the optional standard imports and
// thirdParty imports (from github for example)
type imports struct {
	standard   importList
	thirdParty importList
}

// importList is a list of import names
type importList []string

func (i importList) Len() int {
	return len(i)
}

func (i importList) Swap(k, j int) {
	i[k], i[j] = i[j], i[k]
}

func (i importList) Less(k, j int) bool {
	res := strings.Compare(strings.TrimLeft(i[k], "_ "), strings.TrimLeft(i[j], "_ "))
	if res <= 0 {
		return true
	}

	return false
}

func combineImports(a, b imports) imports {
	var c imports

	c.standard = removeDuplicates(combineStringSlices(a.standard, b.standard))
	c.thirdParty = removeDuplicates(combineStringSlices(a.thirdParty, b.thirdParty))

	sort.Sort(c.standard)
	sort.Sort(c.thirdParty)

	return c
}

func combineTypeImports(a imports, b map[string]imports, columns []bdb.Column) imports {
	tmpImp := imports{
		standard:   make(importList, len(a.standard)),
		thirdParty: make(importList, len(a.thirdParty)),
	}

	copy(tmpImp.standard, a.standard)
	copy(tmpImp.thirdParty, a.thirdParty)

	for _, col := range columns {
		for key, imp := range b {
			if col.Type == key {
				tmpImp.standard = append(tmpImp.standard, imp.standard...)
				tmpImp.thirdParty = append(tmpImp.thirdParty, imp.thirdParty...)
			}
		}
	}

	tmpImp.standard = removeDuplicates(tmpImp.standard)
	tmpImp.thirdParty = removeDuplicates(tmpImp.thirdParty)

	sort.Sort(tmpImp.standard)
	sort.Sort(tmpImp.thirdParty)

	return tmpImp
}

func buildImportString(imps imports) []byte {
	stdlen, thirdlen := len(imps.standard), len(imps.thirdParty)
	if stdlen+thirdlen < 1 {
		return []byte{}
	}

	if stdlen+thirdlen == 1 {
		var imp string
		if stdlen == 1 {
			imp = imps.standard[0]
		} else {
			imp = imps.thirdParty[0]
		}
		return []byte(fmt.Sprintf("import %s", imp))
	}

	buf := &bytes.Buffer{}
	buf.WriteString("import (")
	for _, std := range imps.standard {
		fmt.Fprintf(buf, "\n\t%s", std)
	}
	if stdlen != 0 && thirdlen != 0 {
		buf.WriteString("\n")
	}
	for _, third := range imps.thirdParty {
		fmt.Fprintf(buf, "\n\t%s", third)
	}
	buf.WriteString("\n)\n")

	return buf.Bytes()
}

func combineStringSlices(a, b []string) []string {
	c := make([]string, len(a)+len(b))
	if len(a) > 0 {
		copy(c, a)
	}
	if len(b) > 0 {
		copy(c[len(a):], b)
	}

	return c
}

func removeDuplicates(dedup []string) []string {
	if len(dedup) <= 1 {
		return dedup
	}

	for i := 0; i < len(dedup)-1; i++ {
		for j := i + 1; j < len(dedup); j++ {
			if dedup[i] != dedup[j] {
				continue
			}

			if j != len(dedup)-1 {
				dedup[j] = dedup[len(dedup)-1]
				j--
			}
			dedup = dedup[:len(dedup)-1]
		}
	}

	return dedup
}

var defaultTemplateImports = imports{
	standard: importList{
		`"fmt"`,
		`"strings"`,
		`"database/sql"`,
	},
	thirdParty: importList{
		`"github.com/pkg/errors"`,
		`"github.com/vattle/sqlboiler/boil"`,
		`"github.com/vattle/sqlboiler/boil/qm"`,
		`"github.com/vattle/sqlboiler/strmangle"`,
	},
}

var defaultSingletonTemplateImports = map[string]imports{
	"boil_helpers": {
		standard: importList{},
		thirdParty: importList{
			`"github.com/vattle/sqlboiler/boil"`,
			`"github.com/vattle/sqlboiler/boil/qm"`,
		},
	},
}

var defaultTestTemplateImports = imports{
	standard: importList{
		`"testing"`,
		`"reflect"`,
		`"time"`,
		`"fmt"`,
	},
	thirdParty: importList{
		`"github.com/pkg/errors"`,
		`"gopkg.in/nullbio/null.v4"`,
		`"github.com/vattle/sqlboiler/boil"`,
		`"github.com/vattle/sqlboiler/boil/qm"`,
		`"github.com/vattle/sqlboiler/strmangle"`,
	},
}

var defaultSingletonTestTemplateImports = map[string]imports{
	"boil_main_helpers": {
		standard: importList{
			`"database/sql"`,
			`"os"`,
			`"path/filepath"`,
		},
		thirdParty: importList{
			`"github.com/spf13/viper"`,
		},
	},
	"boil_helpers": {
		standard: importList{
			`"crypto/md5"`,
			`"fmt"`,
			`"os"`,
			`"strconv"`,
			`"math/rand"`,
			`"bytes"`,
		},
		thirdParty: importList{
			`"github.com/vattle/sqlboiler/boil"`,
		},
	},
}

var defaultTestMainImports = map[string]imports{
	"postgres": {
		standard: importList{
			`"testing"`,
			`"os"`,
			`"os/exec"`,
			`"flag"`,
			`"fmt"`,
			`"io/ioutil"`,
			`"bytes"`,
			`"database/sql"`,
			`"path/filepath"`,
			`"time"`,
			`"math/rand"`,
		},
		thirdParty: importList{
			`"github.com/kat-co/vala"`,
			`"github.com/pkg/errors"`,
			`"github.com/spf13/viper"`,
			`"github.com/vattle/sqlboiler/boil"`,
			`"github.com/vattle/sqlboiler/bdb/drivers"`,
			`_ "github.com/lib/pq"`,
		},
	},
}

// importsBasedOnType imports are only included in the template output if the
// database requires one of the following special types. Check
// TranslateColumnType to see the type assignments.
var importsBasedOnType = map[string]imports{
	"null.Float32": {
		thirdParty: importList{`"gopkg.in/nullbio/null.v4"`},
	},
	"null.Float64": {
		thirdParty: importList{`"gopkg.in/nullbio/null.v4"`},
	},
	"null.Int": {
		thirdParty: importList{`"gopkg.in/nullbio/null.v4"`},
	},
	"null.Int8": {
		thirdParty: importList{`"gopkg.in/nullbio/null.v4"`},
	},
	"null.Int16": {
		thirdParty: importList{`"gopkg.in/nullbio/null.v4"`},
	},
	"null.Int32": {
		thirdParty: importList{`"gopkg.in/nullbio/null.v4"`},
	},
	"null.Int64": {
		thirdParty: importList{`"gopkg.in/nullbio/null.v4"`},
	},
	"null.Uint": {
		thirdParty: importList{`"gopkg.in/nullbio/null.v4"`},
	},
	"null.Uint8": {
		thirdParty: importList{`"gopkg.in/nullbio/null.v4"`},
	},
	"null.Uint16": {
		thirdParty: importList{`"gopkg.in/nullbio/null.v4"`},
	},
	"null.Uint32": {
		thirdParty: importList{`"gopkg.in/nullbio/null.v4"`},
	},
	"null.Uint64": {
		thirdParty: importList{`"gopkg.in/nullbio/null.v4"`},
	},
	"null.String": {
		thirdParty: importList{`"gopkg.in/nullbio/null.v4"`},
	},
	"null.Bool": {
		thirdParty: importList{`"gopkg.in/nullbio/null.v4"`},
	},
	"null.Time": {
		thirdParty: importList{`"gopkg.in/nullbio/null.v4"`},
	},
	"time.Time": {
		standard: importList{`"time"`},
	},
}
<commit_msg>Fix missing import in generation<commit_after>package main

import (
	"bytes"
	"fmt"
	"sort"
	"strings"

	"github.com/vattle/sqlboiler/bdb"
)

// imports defines the optional standard imports and
// thirdParty imports (from github for example)
type imports struct {
	standard   importList
	thirdParty importList
}

// importList is a list of import names
type importList []string

func (i importList) Len() int {
	return len(i)
}

func (i importList) Swap(k, j int) {
	i[k], i[j] = i[j], i[k]
}

func (i importList) Less(k, j int) bool {
	res := strings.Compare(strings.TrimLeft(i[k], "_ "), strings.TrimLeft(i[j], "_ "))
	if res <= 0 {
		return true
	}

	return false
}

func combineImports(a, b imports) imports {
	var c imports

	c.standard = removeDuplicates(combineStringSlices(a.standard, b.standard))
	c.thirdParty = removeDuplicates(combineStringSlices(a.thirdParty, b.thirdParty))

	sort.Sort(c.standard)
	sort.Sort(c.thirdParty)

	return c
}

func combineTypeImports(a imports, b map[string]imports, columns []bdb.Column) imports {
	tmpImp := imports{
		standard:   make(importList, len(a.standard)),
		thirdParty: make(importList, len(a.thirdParty)),
	}

	copy(tmpImp.standard, a.standard)
	copy(tmpImp.thirdParty, a.thirdParty)

	for _, col := range columns {
		for key, imp := range b {
			if col.Type == key {
				tmpImp.standard = append(tmpImp.standard, imp.standard...)
				tmpImp.thirdParty = append(tmpImp.thirdParty, imp.thirdParty...)
			}
		}
	}

	tmpImp.standard = removeDuplicates(tmpImp.standard)
	tmpImp.thirdParty = removeDuplicates(tmpImp.thirdParty)

	sort.Sort(tmpImp.standard)
	sort.Sort(tmpImp.thirdParty)

	return tmpImp
}

func buildImportString(imps imports) []byte {
	stdlen, thirdlen := len(imps.standard), len(imps.thirdParty)
	if stdlen+thirdlen < 1 {
		return []byte{}
	}

	if stdlen+thirdlen == 1 {
		var imp string
		if stdlen == 1 {
			imp = imps.standard[0]
		} else {
			imp = imps.thirdParty[0]
		}
		return []byte(fmt.Sprintf("import %s", imp))
	}

	buf := &bytes.Buffer{}
	buf.WriteString("import (")
	for _, std := range imps.standard {
		fmt.Fprintf(buf, "\n\t%s", std)
	}
	if stdlen != 0 && thirdlen != 0 {
		buf.WriteString("\n")
	}
	for _, third := range imps.thirdParty {
		fmt.Fprintf(buf, "\n\t%s", third)
	}
	buf.WriteString("\n)\n")

	return buf.Bytes()
}

func combineStringSlices(a, b []string) []string {
	c := make([]string, len(a)+len(b))
	if len(a) > 0 {
		copy(c, a)
	}
	if len(b) > 0 {
		copy(c[len(a):], b)
	}

	return c
}

func removeDuplicates(dedup []string) []string {
	if len(dedup) <= 1 {
		return dedup
	}

	for i := 0; i < len(dedup)-1; i++ {
		for j := i + 1; j < len(dedup); j++ {
			if dedup[i] != dedup[j] {
				continue
			}

			if j != len(dedup)-1 {
				dedup[j] = dedup[len(dedup)-1]
				j--
			}
			dedup = dedup[:len(dedup)-1]
		}
	}

	return dedup
}

var defaultTemplateImports = imports{
	standard: importList{
		`"fmt"`,
		`"strings"`,
		`"database/sql"`,
	},
	thirdParty: importList{
		`"github.com/pkg/errors"`,
		`"github.com/vattle/sqlboiler/boil"`,
		`"github.com/vattle/sqlboiler/boil/qm"`,
		`"github.com/vattle/sqlboiler/strmangle"`,
	},
}

var defaultSingletonTemplateImports = map[string]imports{
	"boil_helpers": {
		thirdParty: importList{
			`"github.com/vattle/sqlboiler/boil"`,
			`"github.com/vattle/sqlboiler/boil/qm"`,
		},
	},
	"boil_types": {
		thirdParty: importList{
			`"github.com/pkg/errors"`,
		},
	},
}

var defaultTestTemplateImports = imports{
	standard: importList{
		`"testing"`,
		`"reflect"`,
		`"time"`,
		`"fmt"`,
	},
	thirdParty: importList{
		`"github.com/pkg/errors"`,
		`"gopkg.in/nullbio/null.v4"`,
		`"github.com/vattle/sqlboiler/boil"`,
		`"github.com/vattle/sqlboiler/boil/qm"`,
		`"github.com/vattle/sqlboiler/strmangle"`,
	},
}

var defaultSingletonTestTemplateImports = map[string]imports{
	"boil_main_helpers": {
		standard: importList{
			`"database/sql"`,
			`"os"`,
			`"path/filepath"`,
		},
		thirdParty: importList{
			`"github.com/spf13/viper"`,
		},
	},
	"boil_helpers": {
		standard: importList{
			`"crypto/md5"`,
			`"fmt"`,
			`"os"`,
			`"strconv"`,
			`"math/rand"`,
			`"bytes"`,
		},
		thirdParty: importList{
			`"github.com/vattle/sqlboiler/boil"`,
		},
	},
}

var defaultTestMainImports = map[string]imports{
	"postgres": {
		standard: importList{
			`"testing"`,
			`"os"`,
			`"os/exec"`,
			`"flag"`,
			`"fmt"`,
			`"io/ioutil"`,
			`"bytes"`,
			`"database/sql"`,
			`"path/filepath"`,
			`"time"`,
			`"math/rand"`,
		},
		thirdParty: importList{
			`"github.com/kat-co/vala"`,
			`"github.com/pkg/errors"`,
			`"github.com/spf13/viper"`,
			`"github.com/vattle/sqlboiler/boil"`,
			`"github.com/vattle/sqlboiler/bdb/drivers"`,
			`_ "github.com/lib/pq"`,
		},
	},
}

// importsBasedOnType imports are only included in the template output if the
// database requires one of the following special types. Check
// TranslateColumnType to see the type assignments.
var importsBasedOnType = map[string]imports{
	"null.Float32": {
		thirdParty: importList{`"gopkg.in/nullbio/null.v4"`},
	},
	"null.Float64": {
		thirdParty: importList{`"gopkg.in/nullbio/null.v4"`},
	},
	"null.Int": {
		thirdParty: importList{`"gopkg.in/nullbio/null.v4"`},
	},
	"null.Int8": {
		thirdParty: importList{`"gopkg.in/nullbio/null.v4"`},
	},
	"null.Int16": {
		thirdParty: importList{`"gopkg.in/nullbio/null.v4"`},
	},
	"null.Int32": {
		thirdParty: importList{`"gopkg.in/nullbio/null.v4"`},
	},
	"null.Int64": {
		thirdParty: importList{`"gopkg.in/nullbio/null.v4"`},
	},
	"null.Uint": {
		thirdParty: importList{`"gopkg.in/nullbio/null.v4"`},
	},
	"null.Uint8": {
		thirdParty: importList{`"gopkg.in/nullbio/null.v4"`},
	},
	"null.Uint16": {
		thirdParty: importList{`"gopkg.in/nullbio/null.v4"`},
	},
	"null.Uint32": {
		thirdParty: importList{`"gopkg.in/nullbio/null.v4"`},
	},
	"null.Uint64": {
		thirdParty: importList{`"gopkg.in/nullbio/null.v4"`},
	},
	"null.String": {
		thirdParty: importList{`"gopkg.in/nullbio/null.v4"`},
	},
	"null.Bool": {
		thirdParty: importList{`"gopkg.in/nullbio/null.v4"`},
	},
	"null.Time": {
		thirdParty: importList{`"gopkg.in/nullbio/null.v4"`},
	},
	"time.Time": {
		standard: importList{`"time"`},
	},
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/russross/blackfriday"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
)

const (
	HELP = `md2xml [-h] file.md
Transform a given Markdown file into XML.
-h        To print this help page.
file.md   The markdown file to convert.
Note: this program calls xsltproc that must have been installed.`
	STYLESHEET = `<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                version="1.0">

  <xsl:output method="xml" encoding="UTF-8"/>
  <xsl:param name="id">ID</xsl:param>
  <xsl:param name="date">DATE</xsl:param>
  <xsl:param name="title">TITLE</xsl:param>

  <xsl:template match="/xhtml">
    <xsl:text disable-output-escaping="yes">
    <!DOCTYPE weblog PUBLIC "-//CAFEBABE//DTD weblog 1.0//EN"
                               "../dtd/weblog.dtd">
    </xsl:text>
    <weblog>
      <xsl:attribute name="id"><xsl:value-of select="$id"/></xsl:attribute>
      <xsl:attribute name="date"><xsl:value-of select="$date"/></xsl:attribute>
      <title><xsl:value-of select="$title"/></title>
      <xsl:apply-templates/>
    </weblog>
  </xsl:template>

  <xsl:template match="h1">

<imp><xsl:apply-templates/></imp>

  </xsl:template>

  <xsl:template match="h2">

<imp><xsl:apply-templates/></imp>

  </xsl:template>

  <xsl:template match="h3">

<imp><xsl:apply-templates/></imp>

  </xsl:template>

  <xsl:template match="h4">

<imp><xsl:apply-templates/></imp>

  </xsl:template>

  <xsl:template match="h5">

<imp><xsl:apply-templates/></imp>

  </xsl:template>

  <xsl:template match="h6">

<imp><xsl:apply-templates/></imp>

  </xsl:template>

  <xsl:template match="p[count(text())=0 and count(code)=1]">
    <xsl:apply-templates select="code"/>
  </xsl:template>

  <xsl:template match="p[count(text())=1 and count(img)=1]">
    <xsl:apply-templates select="img"/>
  </xsl:template>

  <xsl:template match="img">

  </xsl:template>

  <xsl:template match="p">

<xsl:apply-templates/>

  </xsl:template>

  <xsl:template match="ul">
    <list><xsl:apply-templates/></list>
  </xsl:template>

  <xsl:template match="ol">
    <enum><xsl:apply-templates/></enum>
  </xsl:template>

  <xsl:template match="li">
    <item><xsl:apply-templates/></item>
  </xsl:template>

  <xsl:template match="table">

<xsl:apply-templates/>

  </xsl:template>

  <xsl:template match="th">
    <xsl:apply-templates/>
  </xsl:template>

  <xsl:template match="tr">

<xsl:apply-templates/>

  </xsl:template>

  <xsl:template match="td">
    <co><xsl:apply-templates/></co>
  </xsl:template>

  <xsl:template match="pre">
    <source><xsl:apply-templates/></source>
  </xsl:template>

  <xsl:template match="em">
    <term><xsl:apply-templates/></term>
  </xsl:template>

  <xsl:template match="strong">
    <imp><xsl:apply-templates/></imp>
  </xsl:template>

</xsl:stylesheet>`
	XHTML_HEADER = "<xhtml>\n"
	XHTML_FOOTER = "\n</xhtml>"
)

func processXsl(xmlFile string, data map[string]string) []byte {
	xslFile, err := ioutil.TempFile("/tmp", "md2xsl-")
	if err != nil {
		panic(err)
	}
	err = ioutil.WriteFile(xslFile.Name(), []byte(STYLESHEET), 0755)
	if err != nil {
		panic(err)
	}
	defer os.Remove(xslFile.Name())
	params := make([]string, 0, 2+3*len(data))
	for name, value := range data {
		params = append(params, "--stringparam")
		params = append(params, name)
		params = append(params, value)
	}
	params = append(params, xslFile.Name())
	params = append(params, xmlFile)
	command := exec.Command("xsltproc", params...)
	result, err := command.CombinedOutput()
	if err != nil {
		panic(err)
	}
	return result
}

func markdown2xhtml(markdown string) []byte {
	xhtml := blackfriday.MarkdownBasic([]byte(markdown))
	return []byte(XHTML_HEADER + string(xhtml) + XHTML_FOOTER)
}

func markdownData(text string) (map[string]string, string) {
	data := make(map[string]string)
	lines := strings.Split(text, "\n")
	var limit int
	for index, line := range lines {
		if strings.HasPrefix(line, "% ") && strings.Index(line, ":") >= 0 {
			name := strings.TrimSpace(line[2:strings.Index(line, ":")])
			value := strings.TrimSpace(line[strings.Index(line, ":")+1 : len(line)])
			data[name] = value
		} else {
			limit = index
			break
		}
	}
	return data, strings.Join(lines[limit:len(lines)], "\n")
}

func escapeXml(source string) string {
	source = strings.Replace(source, "&", "&", -1)
	source = strings.Replace(source, "<", "<", -1)
	return source
}

func processFile(filename string, printXhtml bool) string {
	source, err := ioutil.ReadFile(filename)
	if err != nil {
		panic(err)
	}
	data, markdown := markdownData(string(source))
	markdown = escapeXml(markdown)
	xhtml := markdown2xhtml(markdown)
	if printXhtml {
		return string(xhtml)
	}
	xmlFile, err := ioutil.TempFile("/tmp", "md2xml-")
	if err != nil {
		panic(err)
	}
	defer os.Remove(xmlFile.Name())
	ioutil.WriteFile(xmlFile.Name(), xhtml, 0755)
	result := processXsl(xmlFile.Name(), data)
	return string(result)
}

func main() {
	printXhtml := false
	if len(os.Args) < 2 {
		fmt.Println(HELP)
		os.Exit(1)
	}
	for _, arg := range os.Args[1:] {
		if arg == "-h" || os.Args[1] == "--help" {
			fmt.Println(HELP)
			os.Exit(0)
		} else if arg == "-x" || arg == "--xhtml" {
			printXhtml = true
		} else {
			fmt.Println(processFile(arg, printXhtml))
		}
	}
}
<commit_msg>Fixes<commit_after>package main

import (
	"fmt"
	"github.com/russross/blackfriday"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
)

const (
	HELP = `md2xml [-h] file.md
Transform a given Markdown file into XML.
-h        To print this help page.
file.md   The markdown file to convert.
Note: this program calls xsltproc that must have been installed.`
	STYLESHEET = `<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                version="1.0">

  <xsl:output method="xml" encoding="UTF-8"/>
  <xsl:param name="id">ID</xsl:param>
  <xsl:param name="date">DATE</xsl:param>
  <xsl:param name="title">TITLE</xsl:param>

  <xsl:template match="/xhtml">
    <xsl:text disable-output-escaping="yes">
    <!DOCTYPE blog PUBLIC "-//CAFEBABE//DTD blog 1.0//EN"
                             "../dtd/blog.dtd">
    </xsl:text>
    <blog>
      <xsl:attribute name="id"><xsl:value-of select="$id"/></xsl:attribute>
      <xsl:attribute name="date"><xsl:value-of select="$date"/></xsl:attribute>
      <title><xsl:value-of select="$title"/></title>
      <xsl:apply-templates/>
    </blog>
  </xsl:template>

  <xsl:template match="h1">

<imp><xsl:apply-templates/></imp>

  </xsl:template>

  <xsl:template match="h2">

<imp><xsl:apply-templates/></imp>

  </xsl:template>

  <xsl:template match="h3">

<imp><xsl:apply-templates/></imp>

  </xsl:template>

  <xsl:template match="h4">

<imp><xsl:apply-templates/></imp>

  </xsl:template>

  <xsl:template match="h5">

<imp><xsl:apply-templates/></imp>

  </xsl:template>

  <xsl:template match="h6">

<imp><xsl:apply-templates/></imp>

  </xsl:template>

  <xsl:template match="p[count(text())=0 and count(code)=1]">
    <source><xsl:apply-templates select="code"/></source>
  </xsl:template>

  <xsl:template match="p[count(text())=1 and count(img)=1]">
    <xsl:apply-templates select="img"/>
  </xsl:template>

  <xsl:template match="img">

  </xsl:template>

  <xsl:template match="p">

<xsl:apply-templates/>

  </xsl:template>

  <xsl:template match="ul">
    <list><xsl:apply-templates/></list>
  </xsl:template>

  <xsl:template match="ol">
    <enum><xsl:apply-templates/></enum>
  </xsl:template>

  <xsl:template match="li">
    <item><xsl:apply-templates/></item>
  </xsl:template>

  <xsl:template match="table">

<xsl:apply-templates/>

  </xsl:template>

  <xsl:template match="th">
    <xsl:apply-templates/>
  </xsl:template>

  <xsl:template match="tr">

<xsl:apply-templates/>

  </xsl:template>

  <xsl:template match="td">
    <co><xsl:apply-templates/></co>
  </xsl:template>

  <xsl:template match="pre">
    <source><xsl:apply-templates/></source>
  </xsl:template>

  <xsl:template match="em">
    <term><xsl:apply-templates/></term>
  </xsl:template>

  <xsl:template match="strong">
    <imp><xsl:apply-templates/></imp>
  </xsl:template>

</xsl:stylesheet>`
	XHTML_HEADER = "<xhtml>\n"
	XHTML_FOOTER = "\n</xhtml>"
)

func processXsl(xmlFile string, data map[string]string) []byte {
	xslFile, err := ioutil.TempFile("/tmp", "md2xsl-")
	if err != nil {
		panic(err)
	}
	err = ioutil.WriteFile(xslFile.Name(), []byte(STYLESHEET), 0755)
	if err != nil {
		panic(err)
	}
	defer os.Remove(xslFile.Name())
	params := make([]string, 0, 2+3*len(data))
	for name, value := range data {
		params = append(params, "--stringparam")
		params = append(params, name)
		params = append(params, value)
	}
	params = append(params, xslFile.Name())
	params = append(params, xmlFile)
	command := exec.Command("xsltproc", params...)
	result, err := command.CombinedOutput()
	if err != nil {
		panic(err)
	}
	return result
}

func markdown2xhtml(markdown string) []byte {
	xhtml := blackfriday.MarkdownBasic([]byte(markdown))
	return []byte(XHTML_HEADER + string(xhtml) + XHTML_FOOTER)
}

func markdownData(text string) (map[string]string, string) {
	data := make(map[string]string)
	lines := strings.Split(text, "\n")
	var limit int
	for index, line := range lines {
		if strings.HasPrefix(line, "% ") && strings.Index(line, ":") >= 0 {
			name := strings.TrimSpace(line[2:strings.Index(line, ":")])
			value := strings.TrimSpace(line[strings.Index(line, ":")+1 : len(line)])
			data[name] = value
		} else {
			limit = index
			break
		}
	}
	return data, strings.Join(lines[limit:len(lines)], "\n")
}

func escapeXml(source string) string {
	source = strings.Replace(source, "&", "&", -1)
	source = strings.Replace(source, "<", "<", -1)
	return source
}

func processFile(filename string, printXhtml bool) string {
	source, err := ioutil.ReadFile(filename)
	if err != nil {
		panic(err)
	}
	data, markdown := markdownData(string(source))
	markdown = escapeXml(markdown)
	xhtml := markdown2xhtml(markdown)
	if printXhtml {
		return string(xhtml)
	}
	xmlFile, err := ioutil.TempFile("/tmp", "md2xml-")
	if err != nil {
		panic(err)
	}
	defer os.Remove(xmlFile.Name())
	ioutil.WriteFile(xmlFile.Name(), xhtml, 0755)
	result := processXsl(xmlFile.Name(), data)
	return string(result)
}

func main() {
	printXhtml := false
	if len(os.Args) < 2 {
		fmt.Println(HELP)
		os.Exit(1)
	}
	for _, arg := range os.Args[1:] {
		if arg == "-h" || os.Args[1] == "--help" {
			fmt.Println(HELP)
			os.Exit(0)
		} else if arg == "-x" || arg == "--xhtml" {
			printXhtml = true
		} else {
			fmt.Println(processFile(arg, printXhtml))
		}
	}
}
<|endoftext|>


<commit_before><commit_msg>improved Context.Lock<commit_after><|endoftext|>