<commit_before>package main

import (
	"time"

	xp "github.com/BurntSushi/xgb/xproto"
)

const (
	// wmKeysym is the key to trigger taowm actions. For other possible
	// values, such as xkSuperL for the 'Windows' key that is typically
	// between the left Control and Alt keys, see keysym.go.
	wmKeysym = xkCapsLock

	// colorXxx are taowm's text and border colors. We assume 24-bit RGB.
	colorBaseUnfocused  = 0x1f3f1f
	colorBaseFocused    = 0x3f7f3f
	colorPulseUnfocused = 0x3f7f3f
	colorPulseFocused   = 0x7fff7f
	colorQuitUnfocused  = 0x7f1f1f
	colorQuitFocused    = 0xff3f3f

	// dpi is the Dots Per Inch screen resolution. Hard-coding 96 DPI is the
	// same as what 2012-era gnome-settings-daemon does. For significantly
	// higher resolution screens, this value should be larger. Integer
	// multiples of 96 work best.
	dpi = 96

	// pulseXxx are the animation durations.
	pulseFrameDuration = 50 * time.Millisecond
	pulseTotalDuration = 1000 * time.Millisecond

	// quitDuration is the grace period, when quitting, for programs to exit
	// cleanly.
	quitDuration = 60 * time.Second

	showBatteryPercentage = false
)

var (
	// fontXxx are the font name and metrics, for the 6x13 font by default.
	// fontHeight1 is the vertical offset for the first line of text.
	fontName    = "6x13"
	fontHeight  = 16
	fontHeight1 = 9
	fontWidth   = 6
)

func init() {
	if dpi >= 2*96 {
		fontName = "12x24"
		fontHeight = 30
		fontHeight1 = 20
		fontWidth = 12
	}
}

// xSettings is the key/value pairs to announce via the XSETTINGS mechanism.
// In particular, these include font and theme configuration parameters picked
// up by GTK+ programs such as gnome-terminal.
//
// The dump_xsettings program from http://code.google.com/p/xsettingsd/ will
// show the XSETTINGS key/value pairs set by other desktop environments such
// as GNOME.
//
// If this array is empty, then taowm will not try to own the XSETTINGS list,
// allowing another program such as gnome-settings-daemon to do so.
var xSettings = [...]struct {
	name  string
	value interface{}
}{
	{"Net/IconThemeName", "Tango"},
	{"Net/ThemeName", "Clearlooks"},
	{"Xft/Antialias", 1},
	{"Xft/DPI", dpi * 1024},
	{"Xft/Hinting", 1},
	{"Xft/HintStyle", "hintslight"},
	{"Xft/RGBA", "none"},
}

const doAudioActions = true

// actions lists the action to be performed for each key press. The do function
// returns whether to pulsate the frames' borders to acknowledge the key press.
//
// The map keys are X11 keysyms as int32s. The unary +/^ means whether the
// shift modifier needs to be absent/present.
var actions = map[int32]struct {
	do  func(*workspace, interface{}) bool
	arg interface{}
}{
	+' ':      {doExec, []string{"google-chrome"}},
	^' ':      {doExec, []string{"google-chrome", "--incognito"}},
	^'|':      {doExec, []string{"gnome-screensaver-command", "-l"}},
	+xkReturn: {doExec, []string{"gnome-terminal"}}, // "taote" is another option.
	^xkReturn: {doExec, []string{"dmenu_run", "-nb", "#0f0f0f", "-nf", "#3f7f3f",
		"-sb", "#0f0f0f", "-sf", "#7fff7f", "-l", "10"}},

	+xkAudioLowerVolume: {doAudio, []string{"pactl", "set-sink-volume", "@DEFAULT_SINK@", "-5%"}},
	+xkAudioRaiseVolume: {doAudio, []string{"pactl", "set-sink-volume", "@DEFAULT_SINK@", "+5%"}},
	+xkAudioMute:        {doAudio, []string{"pactl", "set-sink-mute", "@DEFAULT_SINK@", "toggle"}},

	+xkBackspace: {doWindowDelete, nil},
	^xkEscape:    {doQuit, nil},

	+'`':          {doScreen, next},
	^'~':          {doScreen, prev},
	+xkTab:        {doFrame, next},
	^xkISOLeftTab: {doFrame, prev},

	+'q': {doList, listWorkspaces},
	+'w': {doWorkspaceMigrate, nil},
	+'e': {doWorkspace, prev},
	^'E': {doWorkspaceNudge, prev},
	+'r': {doWorkspace, next},
	^'R': {doWorkspaceNudge, next},
	+'t': {doWorkspaceNew, nil},
	^'T': {doWorkspaceDelete, nil},

	+'a': {doList, listWindows},
	+'s': {doWindowSelect, false},
	^'S': {doWindowSelect, true},
	+'d': {doWindow, prev},
	^'D': {doWindowNudge, prev},
	+'f': {doWindow, next},
	^'F': {doWindowNudge, next},
	+'g': {doFullscreen, nil},
	^'G': {doHide, nil},

	+'-': {doSplit, horizontal},
	+'=': {doSplit, vertical},
	^'+': {doMerge, nil},

	+'1': {doWindowN, 0},
	+'2': {doWindowN, 1},
	+'3': {doWindowN, 2},
	+'4': {doWindowN, 3},
	+'5': {doWindowN, 4},
	+'6': {doWindowN, 5},
	+'7': {doWindowN, 6},
	+'8': {doWindowN, 7},
	+'9': {doWindowN, 8},
	+'0': {doWindowN, 9},

	+xkF1:  {doWorkspaceN, 0},
	+xkF2:  {doWorkspaceN, 1},
	+xkF3:  {doWorkspaceN, 2},
	+xkF4:  {doWorkspaceN, 3},
	+xkF5:  {doWorkspaceN, 4},
	+xkF6:  {doWorkspaceN, 5},
	+xkF7:  {doWorkspaceN, 6},
	+xkF8:  {doWorkspaceN, 7},
	+xkF9:  {doWorkspaceN, 8},
	+xkF10: {doWorkspaceN, 9},
	+xkF11: {doWorkspaceN, 10},
	+xkF12: {doWorkspaceN, 11},

	+'i': {doSynthetic, xp.Button(4)},
	^'I': {doSynthetic, xp.Button(4)},
	+'m': {doSynthetic, xp.Button(5)},
	^'M': {doSynthetic, xp.Button(5)},
	+'y': {doSynthetic, xp.Keysym(xkHome)},
	^'Y': {doSynthetic, xp.Keysym(xkHome)},
	+'u': {doSynthetic, xp.Keysym(xkPageUp)},
	^'U': {doSynthetic, xp.Keysym(xkPageUp)},
	+'h': {doSynthetic, xp.Keysym(xkLeft)},
	^'H': {doSynthetic, xp.Keysym(xkLeft)},
	+'j': {doSynthetic, xp.Keysym(xkDown)},
	^'J': {doSynthetic, xp.Keysym(xkDown)},
	+'k': {doSynthetic, xp.Keysym(xkUp)},
	^'K': {doSynthetic, xp.Keysym(xkUp)},
	+'l': {doSynthetic, xp.Keysym(xkRight)},
	^'L': {doSynthetic, xp.Keysym(xkRight)},
	+'b': {doSynthetic, xp.Keysym(xkEnd)},
	^'B': {doSynthetic, xp.Keysym(xkEnd)},
	+'n': {doSynthetic, xp.Keysym(xkPageDown)},
	^'N': {doSynthetic, xp.Keysym(xkPageDown)},
	+',': {doSynthetic, xp.Keysym(xkBackspace)},
	^'<': {doSynthetic, xp.Keysym(xkBackspace)},
	+'.': {doSynthetic, xp.Keysym(xkDelete)},
	^'>': {doSynthetic, xp.Keysym(xkDelete)},

	+'/':  {doProgramAction, paTabNew},
	^'?':  {doProgramAction, paTabClose},
	+'c':  {doProgramAction, paTabPrev},
	^'C':  {doProgramAction, paTabNudgePrev},
	+'v':  {doProgramAction, paTabNext},
	^'V':  {doProgramAction, paTabNudgeNext},
	+'\'': {doProgramAction, paSearch},
	+'o':  {doProgramAction, paCopy},
	^'O':  {doProgramAction, paCut},
	+'p':  {doProgramAction, paPaste},
	^'P':  {doProgramAction, paPasteSpecial},
	+'z':  {doProgramAction, paZoomIn},
	^'Z':  {doProgramAction, paZoomReset},
	+'x':  {doProgramAction, paZoomOut},
	+';':  {doProgramAction, paThemeNext},
	^':':  {doProgramAction, paThemePrev},
}

// programAction is an action for a particular program to invoke, as opposed
// to a window management action or generic left/down/up/right synthetic key.
type programAction int

const (
	paTabNew programAction = iota
	paTabClose
	paTabPrev
	paTabNext
	paTabNudgePrev
	paTabNudgeNext
	paSearch
	paCut
	paCopy
	paPaste
	paPasteSpecial
	paZoomIn
	paZoomOut
	paZoomReset
	paThemePrev
	paThemeNext
	nProgramActions
)

// programActions defines the program-specific synthetic key combination to
// send to perform a generic program action. For example, the 'copy' action is
// Control-C for some programs and Control-Shift-C for others.
//
// The map keys are based on a window's WM_CLASS. To configure a program that
// isn't listed here, run "xprop | grep WM_CLASS", click on a window from that
// program, and use the first quoted value as the map key here.
var programActions = map[string][nProgramActions]struct {
	state  uint16
	keysym xp.Keysym
}{
	"google-chrome": {
		paTabNew:       {xp.ModMaskControl, 't'},
		paTabClose:     {xp.ModMaskControl, 'w'},
		paTabPrev:      {xp.ModMaskControl, xkPageUp},
		paTabNext:      {xp.ModMaskControl, xkPageDown},
		paSearch:       {xp.ModMaskControl, 'f'},
		paCut:          {xp.ModMaskControl, 'x'},
		paCopy:         {xp.ModMaskControl, 'c'},
		paPaste:        {xp.ModMaskControl, 'v'},
		paPasteSpecial: {xp.ModMaskControl | xp.ModMaskShift, 'V'},
		paZoomIn:       {xp.ModMaskControl | xp.ModMaskShift, '+'},
		paZoomOut:      {xp.ModMaskControl, '-'},
		paZoomReset:    {xp.ModMaskControl, '0'},
	},

	"gnome-terminal-server": {
		paTabNew:       {xp.ModMaskControl | xp.ModMaskShift, 'T'},
		paTabClose:     {xp.ModMaskControl | xp.ModMaskShift, 'W'},
		paTabPrev:      {xp.ModMaskControl, xkPageUp},
		paTabNext:      {xp.ModMaskControl, xkPageDown},
		paSearch:       {xp.ModMaskControl | xp.ModMaskShift, 'F'},
		paCut:          {xp.ModMaskControl | xp.ModMaskShift, 'C'},
		paCopy:         {xp.ModMaskControl | xp.ModMaskShift, 'C'},
		paPaste:        {xp.ModMaskControl | xp.ModMaskShift, 'V'},
		paPasteSpecial: {xp.ModMaskControl | xp.ModMaskShift, 'V'},
		paZoomIn:       {xp.ModMaskControl | xp.ModMaskShift, '+'},
		paZoomOut:      {xp.ModMaskControl, '-'},
		paZoomReset:    {xp.ModMaskControl, '0'},
	},

	// taote is https://github.com/nigeltao/taote
	"taote": {
		paTabNew:       {xp.ModMaskControl | xp.ModMaskShift, 'T'},
		paTabClose:     {xp.ModMaskControl | xp.ModMaskShift, 'Y'},
		paTabPrev:      {xp.ModMaskControl | xp.ModMaskShift, xkPageUp},
		paTabNext:      {xp.ModMaskControl | xp.ModMaskShift, xkPageDown},
		paTabNudgePrev: {xp.ModMaskControl | xp.ModMaskShift, xkHome},
		paTabNudgeNext: {xp.ModMaskControl | xp.ModMaskShift, xkEnd},
		paSearch:       {xp.ModMaskControl | xp.ModMaskShift, 'F'},
		paCut:          {xp.ModMaskControl | xp.ModMaskShift, 'C'},
		paCopy:         {xp.ModMaskControl | xp.ModMaskShift, 'C'},
		paPaste:        {xp.ModMaskControl | xp.ModMaskShift, 'V'},
		paPasteSpecial: {xp.ModMaskControl | xp.ModMaskShift, 'V'},
		paZoomIn:       {xp.ModMaskControl | xp.ModMaskShift, '+'},
		paZoomOut:      {xp.ModMaskControl | xp.ModMaskShift, '_'},
		paZoomReset:    {xp.ModMaskControl | xp.ModMaskShift, ')'},
		paThemePrev:    {xp.ModMaskControl | xp.ModMaskShift, '<'},
		paThemeNext:    {xp.ModMaskControl | xp.ModMaskShift, '>'},
	},
}
<commit_msg>Drop large-font threshold from 192 to 144 DPI<commit_after>package main

import (
	"time"

	xp "github.com/BurntSushi/xgb/xproto"
)

const (
	// wmKeysym is the key to trigger taowm actions. For other possible
	// values, such as xkSuperL for the 'Windows' key that is typically
	// between the left Control and Alt keys, see keysym.go.
	wmKeysym = xkCapsLock

	// colorXxx are taowm's text and border colors. We assume 24-bit RGB.
	colorBaseUnfocused  = 0x1f3f1f
	colorBaseFocused    = 0x3f7f3f
	colorPulseUnfocused = 0x3f7f3f
	colorPulseFocused   = 0x7fff7f
	colorQuitUnfocused  = 0x7f1f1f
	colorQuitFocused    = 0xff3f3f

	// dpi is the Dots Per Inch screen resolution. Hard-coding 96 DPI is the
	// same as what 2012-era gnome-settings-daemon does. For significantly
	// higher resolution screens, this value should be larger. Integer
	// multiples of 96 work best.
	dpi = 96

	// pulseXxx are the animation durations.
	pulseFrameDuration = 50 * time.Millisecond
	pulseTotalDuration = 1000 * time.Millisecond

	// quitDuration is the grace period, when quitting, for programs to exit
	// cleanly.
	quitDuration = 60 * time.Second

	showBatteryPercentage = false
)

var (
	// fontXxx are the font name and metrics, for the 6x13 font by default.
	// fontHeight1 is the vertical offset for the first line of text.
	fontName    = "6x13"
	fontHeight  = 16
	fontHeight1 = 9
	fontWidth   = 6
)

func init() {
	if dpi >= 1.5*96 {
		fontName = "12x24"
		fontHeight = 30
		fontHeight1 = 20
		fontWidth = 12
	}
}

// xSettings is the key/value pairs to announce via the XSETTINGS mechanism.
// In particular, these include font and theme configuration parameters picked
// up by GTK+ programs such as gnome-terminal.
//
// The dump_xsettings program from http://code.google.com/p/xsettingsd/ will
// show the XSETTINGS key/value pairs set by other desktop environments such
// as GNOME.
//
// If this array is empty, then taowm will not try to own the XSETTINGS list,
// allowing another program such as gnome-settings-daemon to do so.
var xSettings = [...]struct {
	name  string
	value interface{}
}{
	{"Net/IconThemeName", "Tango"},
	{"Net/ThemeName", "Clearlooks"},
	{"Xft/Antialias", 1},
	{"Xft/DPI", dpi * 1024},
	{"Xft/Hinting", 1},
	{"Xft/HintStyle", "hintslight"},
	{"Xft/RGBA", "none"},
}

const doAudioActions = true

// actions lists the action to be performed for each key press. The do function
// returns whether to pulsate the frames' borders to acknowledge the key press.
//
// The map keys are X11 keysyms as int32s. The unary +/^ means whether the
// shift modifier needs to be absent/present.
var actions = map[int32]struct {
	do  func(*workspace, interface{}) bool
	arg interface{}
}{
	+' ':      {doExec, []string{"google-chrome"}},
	^' ':      {doExec, []string{"google-chrome", "--incognito"}},
	^'|':      {doExec, []string{"gnome-screensaver-command", "-l"}},
	+xkReturn: {doExec, []string{"gnome-terminal"}}, // "taote" is another option.
	^xkReturn: {doExec, []string{"dmenu_run", "-nb", "#0f0f0f", "-nf", "#3f7f3f",
		"-sb", "#0f0f0f", "-sf", "#7fff7f", "-l", "10"}},

	+xkAudioLowerVolume: {doAudio, []string{"pactl", "set-sink-volume", "@DEFAULT_SINK@", "-5%"}},
	+xkAudioRaiseVolume: {doAudio, []string{"pactl", "set-sink-volume", "@DEFAULT_SINK@", "+5%"}},
	+xkAudioMute:        {doAudio, []string{"pactl", "set-sink-mute", "@DEFAULT_SINK@", "toggle"}},

	+xkBackspace: {doWindowDelete, nil},
	^xkEscape:    {doQuit, nil},

	+'`':          {doScreen, next},
	^'~':          {doScreen, prev},
	+xkTab:        {doFrame, next},
	^xkISOLeftTab: {doFrame, prev},

	+'q': {doList, listWorkspaces},
	+'w': {doWorkspaceMigrate, nil},
	+'e': {doWorkspace, prev},
	^'E': {doWorkspaceNudge, prev},
	+'r': {doWorkspace, next},
	^'R': {doWorkspaceNudge, next},
	+'t': {doWorkspaceNew, nil},
	^'T': {doWorkspaceDelete, nil},

	+'a': {doList, listWindows},
	+'s': {doWindowSelect, false},
	^'S': {doWindowSelect, true},
	+'d': {doWindow, prev},
	^'D': {doWindowNudge, prev},
	+'f': {doWindow, next},
	^'F': {doWindowNudge, next},
	+'g': {doFullscreen, nil},
	^'G': {doHide, nil},

	+'-': {doSplit, horizontal},
	+'=': {doSplit, vertical},
	^'+': {doMerge, nil},

	+'1': {doWindowN, 0},
	+'2': {doWindowN, 1},
	+'3': {doWindowN, 2},
	+'4': {doWindowN, 3},
	+'5': {doWindowN, 4},
	+'6': {doWindowN, 5},
	+'7': {doWindowN, 6},
	+'8': {doWindowN, 7},
	+'9': {doWindowN, 8},
	+'0': {doWindowN, 9},

	+xkF1:  {doWorkspaceN, 0},
	+xkF2:  {doWorkspaceN, 1},
	+xkF3:  {doWorkspaceN, 2},
	+xkF4:  {doWorkspaceN, 3},
	+xkF5:  {doWorkspaceN, 4},
	+xkF6:  {doWorkspaceN, 5},
	+xkF7:  {doWorkspaceN, 6},
	+xkF8:  {doWorkspaceN, 7},
	+xkF9:  {doWorkspaceN, 8},
	+xkF10: {doWorkspaceN, 9},
	+xkF11: {doWorkspaceN, 10},
	+xkF12: {doWorkspaceN, 11},

	+'i': {doSynthetic, xp.Button(4)},
	^'I': {doSynthetic, xp.Button(4)},
	+'m': {doSynthetic, xp.Button(5)},
	^'M': {doSynthetic, xp.Button(5)},
	+'y': {doSynthetic, xp.Keysym(xkHome)},
	^'Y': {doSynthetic, xp.Keysym(xkHome)},
	+'u': {doSynthetic, xp.Keysym(xkPageUp)},
	^'U': {doSynthetic, xp.Keysym(xkPageUp)},
	+'h': {doSynthetic, xp.Keysym(xkLeft)},
	^'H': {doSynthetic, xp.Keysym(xkLeft)},
	+'j': {doSynthetic, xp.Keysym(xkDown)},
	^'J': {doSynthetic, xp.Keysym(xkDown)},
	+'k': {doSynthetic, xp.Keysym(xkUp)},
	^'K': {doSynthetic, xp.Keysym(xkUp)},
	+'l': {doSynthetic, xp.Keysym(xkRight)},
	^'L': {doSynthetic, xp.Keysym(xkRight)},
	+'b': {doSynthetic, xp.Keysym(xkEnd)},
	^'B': {doSynthetic, xp.Keysym(xkEnd)},
	+'n': {doSynthetic, xp.Keysym(xkPageDown)},
	^'N': {doSynthetic, xp.Keysym(xkPageDown)},
	+',': {doSynthetic, xp.Keysym(xkBackspace)},
	^'<': {doSynthetic, xp.Keysym(xkBackspace)},
	+'.': {doSynthetic, xp.Keysym(xkDelete)},
	^'>': {doSynthetic, xp.Keysym(xkDelete)},

	+'/':  {doProgramAction, paTabNew},
	^'?':  {doProgramAction, paTabClose},
	+'c':  {doProgramAction, paTabPrev},
	^'C':  {doProgramAction, paTabNudgePrev},
	+'v':  {doProgramAction, paTabNext},
	^'V':  {doProgramAction, paTabNudgeNext},
	+'\'': {doProgramAction, paSearch},
	+'o':  {doProgramAction, paCopy},
	^'O':  {doProgramAction, paCut},
	+'p':  {doProgramAction, paPaste},
	^'P':  {doProgramAction, paPasteSpecial},
	+'z':  {doProgramAction, paZoomIn},
	^'Z':  {doProgramAction, paZoomReset},
	+'x':  {doProgramAction, paZoomOut},
	+';':  {doProgramAction, paThemeNext},
	^':':  {doProgramAction, paThemePrev},
}

// programAction is an action for a particular program to invoke, as opposed
// to a window management action or generic left/down/up/right synthetic key.
type programAction int

const (
	paTabNew programAction = iota
	paTabClose
	paTabPrev
	paTabNext
	paTabNudgePrev
	paTabNudgeNext
	paSearch
	paCut
	paCopy
	paPaste
	paPasteSpecial
	paZoomIn
	paZoomOut
	paZoomReset
	paThemePrev
	paThemeNext
	nProgramActions
)

// programActions defines the program-specific synthetic key combination to
// send to perform a generic program action. For example, the 'copy' action is
// Control-C for some programs and Control-Shift-C for others.
//
// The map keys are based on a window's WM_CLASS. To configure a program that
// isn't listed here, run "xprop | grep WM_CLASS", click on a window from that
// program, and use the first quoted value as the map key here.
var programActions = map[string][nProgramActions]struct {
	state  uint16
	keysym xp.Keysym
}{
	"google-chrome": {
		paTabNew:       {xp.ModMaskControl, 't'},
		paTabClose:     {xp.ModMaskControl, 'w'},
		paTabPrev:      {xp.ModMaskControl, xkPageUp},
		paTabNext:      {xp.ModMaskControl, xkPageDown},
		paSearch:       {xp.ModMaskControl, 'f'},
		paCut:          {xp.ModMaskControl, 'x'},
		paCopy:         {xp.ModMaskControl, 'c'},
		paPaste:        {xp.ModMaskControl, 'v'},
		paPasteSpecial: {xp.ModMaskControl | xp.ModMaskShift, 'V'},
		paZoomIn:       {xp.ModMaskControl | xp.ModMaskShift, '+'},
		paZoomOut:      {xp.ModMaskControl, '-'},
		paZoomReset:    {xp.ModMaskControl, '0'},
	},

	"gnome-terminal-server": {
		paTabNew:       {xp.ModMaskControl | xp.ModMaskShift, 'T'},
		paTabClose:     {xp.ModMaskControl | xp.ModMaskShift, 'W'},
		paTabPrev:      {xp.ModMaskControl, xkPageUp},
		paTabNext:      {xp.ModMaskControl, xkPageDown},
		paSearch:       {xp.ModMaskControl | xp.ModMaskShift, 'F'},
		paCut:          {xp.ModMaskControl | xp.ModMaskShift, 'C'},
		paCopy:         {xp.ModMaskControl | xp.ModMaskShift, 'C'},
		paPaste:        {xp.ModMaskControl | xp.ModMaskShift, 'V'},
		paPasteSpecial: {xp.ModMaskControl | xp.ModMaskShift, 'V'},
		paZoomIn:       {xp.ModMaskControl | xp.ModMaskShift, '+'},
		paZoomOut:      {xp.ModMaskControl, '-'},
		paZoomReset:    {xp.ModMaskControl, '0'},
	},

	// taote is https://github.com/nigeltao/taote
	"taote": {
		paTabNew:       {xp.ModMaskControl | xp.ModMaskShift, 'T'},
		paTabClose:     {xp.ModMaskControl | xp.ModMaskShift, 'Y'},
		paTabPrev:      {xp.ModMaskControl | xp.ModMaskShift, xkPageUp},
		paTabNext:      {xp.ModMaskControl | xp.ModMaskShift, xkPageDown},
		paTabNudgePrev: {xp.ModMaskControl | xp.ModMaskShift, xkHome},
		paTabNudgeNext: {xp.ModMaskControl | xp.ModMaskShift, xkEnd},
		paSearch:       {xp.ModMaskControl | xp.ModMaskShift, 'F'},
		paCut:          {xp.ModMaskControl | xp.ModMaskShift, 'C'},
		paCopy:         {xp.ModMaskControl | xp.ModMaskShift, 'C'},
		paPaste:        {xp.ModMaskControl | xp.ModMaskShift, 'V'},
		paPasteSpecial: {xp.ModMaskControl | xp.ModMaskShift, 'V'},
		paZoomIn:       {xp.ModMaskControl | xp.ModMaskShift, '+'},
		paZoomOut:      {xp.ModMaskControl | xp.ModMaskShift, '_'},
		paZoomReset:    {xp.ModMaskControl | xp.ModMaskShift, ')'},
		paThemePrev:    {xp.ModMaskControl | xp.ModMaskShift, '<'},
		paThemeNext:    {xp.ModMaskControl | xp.ModMaskShift, '>'},
	},
}
<|endoftext|>


<commit_before>package confoo

import (
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
	"regexp"
	"strconv"
	"strings"

	yaml "gopkg.in/yaml.v2"
)

const confVar = "CONFOO_CONFIG or CONFOO_CONFIG_FILE"

func errorPanic(format string, a ...interface{}) {
	m := fmt.Sprintf(format, a...)
	m2 := "CONFOO - " + m
	panic(m2)
}

var confData map[interface{}]interface{}
var confId string
var confWithIdRegex *regexp.Regexp

func init() {
	confFile, confReceived := os.LookupEnv("CONFOO_CONFIG")
	if !confReceived {
		confFile, confReceived = os.LookupEnv("CONFOO_CONFIG_FILE")

		if !confReceived {
			errorPanic(confVar + " is not set")
		}
	}

	confId, _ = os.LookupEnv("CONFOO_ID")

	data, err := ioutil.ReadFile(confFile)
	if err != nil {
		errorPanic(err.Error())
	}

	confData = make(map[interface{}]interface{})
	err = yaml.Unmarshal(data, &confData)
	if err != nil {
		errorPanic("cannot decode yaml data")
	}

	confWithIdRegex = regexp.MustCompile(`(.*)\[(.*)\]$`)
}

//Configure loads the value of the path of the yml of the CONFOO_CONFIG_FILE into target
func Configure(path string, target interface{}) {
	subConf := getSubConf(path, confData)
	if subConf != nil {
		configPath(path, reflect.ValueOf(target), subConf)
	}
}

//ConfigureFromFile reads ymlFile and loads the value of the path into target
func ConfigureFromFile(ymlFile, path string, target interface{}) error {

	data, err := ioutil.ReadFile(ymlFile)
	if err != nil {
		return err
	}

	confData := make(map[interface{}]interface{})
	err = yaml.Unmarshal(data, &confData)
	if err != nil {
		return err
	}

	subConf := getSubConf(path, confData)
	if subConf != nil {
		configPath(path, reflect.ValueOf(target), subConf)
	}
	return nil
}

func coerceIdentity(conf interface{}) {
	confMap, ok := conf.(map[interface{}]interface{})
	if ok {
		for key, value := range confMap {
			if _, ok := key.(string); !ok {
				continue
			}

			coerceIdentity(value)
			match := confWithIdRegex.FindSubmatch([]byte(key.(string)))
			if match == nil {
				continue
			}
			delete(confMap, key)
			realKey := string(match[1])
			identity := string(match[2])
			if identity == confId {
				confMap[realKey] = value
			}
		}
	}
	confList, ok := conf.([]interface{})
	if ok {
		for _, entry := range confList {
			coerceIdentity(entry)
		}
	}
}

func getSubConf(path string, conf interface{}) interface{} {

	coerceIdentity(conf)
	subConf := conf
	for _, p := range strings.Split(path, ".") {
		m, ok := subConf.(map[interface{}]interface{})
		if !ok {
			errorPanic("%s: path not found", path)
		}
		subConf, ok = m[p]
		if !ok {
			return nil
		}
	}
	return subConf
}

func normalizeKey(s string) string {
	parts := strings.Split(s, "_")
	for i, p := range parts {
		parts[i] = strings.Title(p)
	}
	return strings.Join(parts, "")
}

func replaceKey(s string) string {
	if strings.Contains(s, "$hostname") {
		hostname, error := os.Hostname()
		if error != nil {
			errorPanic("Error while retrieving the hostname: %s", error)
		}

		return strings.Replace(s, "$hostname", hostname, -1)
	}

	return s
}

func replaceValue(s string) string {
	if strings.Contains(s, "$public_hostname") {
		content, err := ioutil.ReadFile("/etc/public_hostname")
		if err != nil {
			fmt.Println(err)
			return s
		}

		public_hostname := strings.TrimSuffix(string(content), "\n")
		return strings.Replace(s, "$public_hostname", public_hostname, -1)
	}

	return s
}

func configStruct(path string, dest reflect.Value, conf interface{}) {
	if conf == nil {
		return
	}
	confMap, ok := conf.(map[interface{}]interface{})
	if !ok {
		errorPanic("%s: expected map not found", path)
	}
	for k, subConf := range confMap {
		kk, ok := k.(string)
		if !ok {
			//FIXME log event if CONFOO_DEBUG is set
			//errorPanic("%s.%v: map key is not a string", path, k)
			continue
		}
		fieldName := normalizeKey(kk)
		fieldVal := dest.FieldByName(fieldName)
		if fieldVal.Kind() == reflect.Invalid {
			//FIXME log event if CONFOO_DEBUG is set
			//errorPanic("%s.%v: field not present in target struct", path, k)
			continue
		}
		configPath(path+"."+kk, dest.FieldByName(fieldName), subConf)
	}
}

func configPath(path string, dest reflect.Value, conf interface{}) {
	destKind := dest.Kind()
	switch destKind {
	case reflect.Ptr:
		if dest.Type().Elem().Kind() == reflect.Struct && dest.IsNil() {
			dest.Set(reflect.New(dest.Type().Elem()))
		}
		configPath(path, dest.Elem(), conf)
	case reflect.Interface:
		dest.Set(reflect.ValueOf(conf))
	case reflect.Struct:
		configStruct(path, dest, conf)
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Float32, reflect.Float64, reflect.Bool:
		confValue := reflect.ValueOf(conf)
		confKind := confValue.Kind()
		if confKind != destKind {
			dType := dest.Type()
			cType := confValue.Type()
			errorPanic("%s: target type %v != conf type %v", path, dType, cType)
		}
		dest.Set(confValue)
	case reflect.String:
		conf = replaceValue(conf.(string))
		confValue := reflect.ValueOf(conf)
		confKind := confValue.Kind()
		if confKind != destKind {
			dType := dest.Type()
			cType := confValue.Type()
			errorPanic("%s: target type %v != conf type %v", path, dType, cType)
		}
		dest.Set(confValue)
	case reflect.Slice:
		dest.Set(dest.Slice(0, 0))
		for i, el := range conf.([]interface{}) {
			idx := strconv.Itoa(i)
			elVal := reflect.New(dest.Type().Elem())
			configPath(path+"."+idx, elVal, el)
			dest.Set(reflect.Append(dest, elVal.Elem()))
		}
	case reflect.Map:
		dest.Set(reflect.MakeMap(dest.Type()))
		for k, el := range conf.(map[interface{}]interface{}) {
			kk := k.(string)
			kk = replaceKey(kk)
			elVal := reflect.New(dest.Type().Elem())
			configPath(path+"."+kk, elVal, el)
			dest.SetMapIndex(reflect.ValueOf(kk), elVal.Elem())
		}
	default:
		errorPanic("%s: conf type %v not handled", path, dest.Type())
	}
}
<commit_msg>more explicit handling of panics<commit_after>package confoo

import (
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
	"regexp"
	"runtime/debug"
	"strconv"
	"strings"

	yaml "gopkg.in/yaml.v2"
)

const confVar = "CONFOO_CONFIG or CONFOO_CONFIG_FILE"

func errorPanic(format string, a ...interface{}) {
	m := fmt.Sprintf(format, a...)
	m2 := "CONFOO - " + m
	panic(m2)
}

var confData map[interface{}]interface{}
var confId string
var confWithIdRegex *regexp.Regexp

func init() {
	confFile, confReceived := os.LookupEnv("CONFOO_CONFIG")
	if !confReceived {
		confFile, confReceived = os.LookupEnv("CONFOO_CONFIG_FILE")

		if !confReceived {
			errorPanic(confVar + " is not set")
		}
	}

	confId, _ = os.LookupEnv("CONFOO_ID")

	data, err := ioutil.ReadFile(confFile)
	if err != nil {
		errorPanic(err.Error())
	}

	confData = make(map[interface{}]interface{})
	err = yaml.Unmarshal(data, &confData)
	if err != nil {
		errorPanic("cannot decode yaml data")
	}

	confWithIdRegex = regexp.MustCompile(`(.*)\[(.*)\]$`)
}

//Configure loads the value of the path of the yml of the CONFOO_CONFIG_FILE into target
func Configure(path string, target interface{}) {
	subConf := getSubConf(path, confData)
	if subConf != nil {
		configPath(path, reflect.ValueOf(target), subConf)
	}
}

//ConfigureFromFile reads ymlFile and loads the value of the path into target
func ConfigureFromFile(ymlFile, path string, target interface{}) error {

	data, err := ioutil.ReadFile(ymlFile)
	if err != nil {
		return err
	}

	confData := make(map[interface{}]interface{})
	err = yaml.Unmarshal(data, &confData)
	if err != nil {
		return err
	}

	subConf := getSubConf(path, confData)
	if subConf != nil {
		configPath(path, reflect.ValueOf(target), subConf)
	}
	return nil
}

func coerceIdentity(conf interface{}) {
	confMap, ok := conf.(map[interface{}]interface{})
	if ok {
		for key, value := range confMap {
			if _, ok := key.(string); !ok {
				continue
			}

			coerceIdentity(value)
			match := confWithIdRegex.FindSubmatch([]byte(key.(string)))
			if match == nil {
				continue
			}
			delete(confMap, key)
			realKey := string(match[1])
			identity := string(match[2])
			if identity == confId {
				confMap[realKey] = value
			}
		}
	}
	confList, ok := conf.([]interface{})
	if ok {
		for _, entry := range confList {
			coerceIdentity(entry)
		}
	}
}

func getSubConf(path string, conf interface{}) interface{} {

	coerceIdentity(conf)
	subConf := conf
	for _, p := range strings.Split(path, ".") {
		m, ok := subConf.(map[interface{}]interface{})
		if !ok {
			errorPanic("%s: path not found", path)
		}
		subConf, ok = m[p]
		if !ok {
			return nil
		}
	}
	return subConf
}

func normalizeKey(s string) string {
	parts := strings.Split(s, "_")
	for i, p := range parts {
		parts[i] = strings.Title(p)
	}
	return strings.Join(parts, "")
}

func replaceKey(s string) string {
	if strings.Contains(s, "$hostname") {
		hostname, error := os.Hostname()
		if error != nil {
			errorPanic("Error while retrieving the hostname: %s", error)
		}

		return strings.Replace(s, "$hostname", hostname, -1)
	}

	return s
}

func replaceValue(s string) string {
	if strings.Contains(s, "$public_hostname") {
		content, err := ioutil.ReadFile("/etc/public_hostname")
		if err != nil {
			fmt.Println(err)
			return s
		}

		public_hostname := strings.TrimSuffix(string(content), "\n")
		return strings.Replace(s, "$public_hostname", public_hostname, -1)
	}

	return s
}

func configStruct(path string, dest reflect.Value, conf interface{}) {
	if conf == nil {
		return
	}
	confMap, ok := conf.(map[interface{}]interface{})
	if !ok {
		errorPanic("%s: expected map not found", path)
	}
	for k, subConf := range confMap {
		kk, ok := k.(string)
		if !ok {
			//FIXME log event if CONFOO_DEBUG is set
			//errorPanic("%s.%v: map key is not a string", path, k)
			continue
		}
		fieldName := normalizeKey(kk)
		fieldVal := dest.FieldByName(fieldName)
		if fieldVal.Kind() == reflect.Invalid {
			//FIXME log event if CONFOO_DEBUG is set
			//errorPanic("%s.%v: field not present in target struct", path, k)
			continue
		}
		configPath(path+"."+kk, dest.FieldByName(fieldName), subConf)
	}
}

func configPath(path string, dest reflect.Value, conf interface{}) {
	defer func() {
		if r := recover(); r != nil {
			fmt.Fprintf(os.Stderr, "panic while handling path %s: %v\n", path, r)
			fmt.Fprintf(os.Stderr, string(debug.Stack()))
			os.Exit(1)
		}
	}()
	destKind := dest.Kind()
	switch destKind {
	case reflect.Ptr:
		if dest.Type().Elem().Kind() == reflect.Struct && dest.IsNil() {
			dest.Set(reflect.New(dest.Type().Elem()))
		}
		configPath(path, dest.Elem(), conf)
	case reflect.Interface:
		dest.Set(reflect.ValueOf(conf))
	case reflect.Struct:
		configStruct(path, dest, conf)
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Float32, reflect.Float64, reflect.Bool:
		confValue := reflect.ValueOf(conf)
		confKind := confValue.Kind()
		if confKind != destKind {
			dType := dest.Type()
			cType := confValue.Type()
			errorPanic("%s: target type %v != conf type %v", path, dType, cType)
		}
		dest.Set(confValue)
	case reflect.String:
		conf = replaceValue(conf.(string))
		confValue := reflect.ValueOf(conf)
		confKind := confValue.Kind()
		if confKind != destKind {
			dType := dest.Type()
			cType := confValue.Type()
			errorPanic("%s: target type %v != conf type %v", path, dType, cType)
		}
		dest.Set(confValue)
	case reflect.Slice:
		dest.Set(dest.Slice(0, 0))
		for i, el := range conf.([]interface{}) {
			idx := strconv.Itoa(i)
			elVal := reflect.New(dest.Type().Elem())
			configPath(path+"."+idx, elVal, el)
			dest.Set(reflect.Append(dest, elVal.Elem()))
		}
	case reflect.Map:
		dest.Set(reflect.MakeMap(dest.Type()))
		for k, el := range conf.(map[interface{}]interface{}) {
			kk := k.(string)
			kk = replaceKey(kk)
			elVal := reflect.New(dest.Type().Elem())
			configPath(path+"."+kk, elVal, el)
			dest.SetMapIndex(reflect.ValueOf(kk), elVal.Elem())
		}
	default:
		errorPanic("%s: conf type %v not handled", path, dest.Type())
	}
}
<|endoftext|>


<commit_before>package gochecks

import (
	"fmt"
	"net"
	"strings"
	"time"

	"io/ioutil"
	"net/http"
	"net/url"

	"github.com/streadway/amqp"
	"github.com/tatsushid/go-fastping"
)

import (
	"database/sql"

	_ "github.com/go-sql-driver/mysql"
)

const (
	sysName     = "1.3.6.1.2.1.1.5.0"
	maxPingTime = 4 * time.Second
)

// CheckFunction type for a function that return a event
type CheckFunction func() Event
// MultiCheckFunction type for a function that return an array of events
type MultiCheckFunction func() []Event

// Tags  returns a new check function that adds the given tags to the result
// generated by the initial check function
func (f CheckFunction) Tags(tags ...string) CheckFunction {
	return func() Event {
		result := f()
		result.Tags = tags
		return result
	}
}

// Attributtes  returns a new check function that adds the attributes map to the result
// generated by the initial check function
func (f CheckFunction) Attributes(attributes map[string]string) CheckFunction {
	return func() Event {
		result := f()
		result.Attributes = attributes
		return result
	}
}

// Attributtes  returns a new check function that adds the given TTL time (in seconds) to the result
// generated by the initial check function
func (f CheckFunction) TTL(ttl float32) CheckFunction {
	return func() Event {
		result := f()
		result.TTL = ttl
		return result
	}
}

// Retry returns a new check funciont that execute the given function up to a given retry times or
// until the first execution that returns a ok (whichever comes first). The new function will return
// the event of the last execution
func (f CheckFunction) Retry(times int, sleep time.Duration) CheckFunction {
	return func() Event {
		var result Event
		for i := 0; i < times; i++ {
			result = f()
			if result.State == "ok" {
				return result
			}
			time.Sleep(sleep)
		}
		return result
	}
}

// NewPingChecker returns a check function that can check if a host answer to a ICMP Ping
func NewPingChecker(host, service, ip string) CheckFunction {
	return func() Event {
		var retRtt time.Duration
		var result = Event{Host: host, Service: service, State: "critical"}

		p := fastping.NewPinger()
		p.MaxRTT = maxPingTime
		ra, err := net.ResolveIPAddr("ip4:icmp", ip)
		if err != nil {
			result.Description = err.Error()
		}

		p.AddIPAddr(ra)
		p.OnRecv = func(addr *net.IPAddr, rtt time.Duration) {
			result.State = "ok"
			result.Metric = float32(retRtt.Nanoseconds() / 1e6)
		}

		err = p.Run()
		if err != nil {
			result.Description = err.Error()
		}
		return result
	}
}

// NewTCPPortChecker returns a check function that can check if a host have a tcp port open
func NewTCPPortChecker(host, service, ip string, port int, timeout time.Duration) CheckFunction {
	return func() Event {
		var err error
		var conn net.Conn

		var t1 = time.Now()
		conn, err = net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), timeout)
		if err == nil {
			conn.Close()
			milliseconds := float32((time.Now().Sub(t1)).Nanoseconds() / 1e6)
			return Event{Host: host, Service: service, State: "ok", Metric: milliseconds}
		}
		return Event{Host: host, Service: service, State: "critical"}
	}
}

// ValidateHTTPResponseFunction function type that should validate a http response and return the state (ok, critical, warning) and error description for a check. (Used with NewGenericHTTPChecker)
type ValidateHTTPResponseFunction func(resp *http.Response) (state, description string)

func BodyGreaterThan(minLength int) ValidateHTTPResponseFunction {
	return func(httpResp *http.Response) (state, description string) {
		if httpResp.StatusCode != 200 {
			return "critical", fmt.Sprintf("Response %d", httpResp.StatusCode)
		}
		if httpResp.Body == nil {
			return "critical", fmt.Sprintf("Empty body")
		}
		body, err := ioutil.ReadAll(httpResp.Body)
		if err != nil {
			return "critical", fmt.Sprintf("Error geting body")
		}
		if len(body) < minLength {
			return "critical", fmt.Sprintf("Obtained %d bytes, expected more than %d", len(body), minLength)
		}
		return "ok", ""
	}
}

func NewGenericHTTPChecker(host, service, url string, validationFunc ValidateHTTPResponseFunction) CheckFunction {
	return func() Event {
		var t1 = time.Now()

		response, err := http.Get(url)
		milliseconds := float32((time.Now().Sub(t1)).Nanoseconds() / 1e6)
		result := Event{Host: host, Service: service, State: "critical", Metric: milliseconds}
		if err != nil {
			result.Description = err.Error()
		} else {
			if response.Body != nil {
				defer response.Body.Close()
			}
			result.State, result.Description = validationFunc(response)
		}
		return result
	}
}

func NewHTTPChecker(host, service, url string, expectedStatusCode int) CheckFunction {
	return NewGenericHTTPChecker(host, service, url,
		func(httpResp *http.Response) (string, string) {
			if httpResp.StatusCode == expectedStatusCode {
				return "ok", ""
			}
			return "critical", fmt.Sprintf("Response %d", httpResp.StatusCode)
		})
}

type SnmpCheckerConf struct {
	retries    int
	timeout    time.Duration
	oidToCheck string
}

var DefaultSnmpCheckConf = SnmpCheckerConf{
	retries:    1,
	timeout:    1 * time.Second,
	oidToCheck: sysName,
}

func NewSnmpChecker(host, service, ip, community string, conf SnmpCheckerConf) CheckFunction {
	return func() Event {

		_, err := snmpGet(ip, community, []string{conf.oidToCheck}, conf.timeout, conf.retries)
		if err == nil {
			return Event{Host: host, Service: service, State: "ok", Description: err.Error()}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

func NewC4CMTSTempChecker(host, service, ip, community string, maxAllowedTemp int) CheckFunction {
	return func() Event {

		result, err := snmpWalk(ip, community, "1.3.6.1.4.1.4998.1.1.10.1.4.2.1.29", 2*time.Second, 1)

		if err == nil {
			max := 0
			for _, r := range result {
				if r.Value.(int) != 999 && r.Value.(int) > max {
					max = r.Value.(int)
				}
			}
			var state = "critical"
			if max < maxAllowedTemp {
				state = "ok"
			}
			return Event{Host: host, Service: service, State: state, Metric: float32(max)}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

func getMaxValueFromSnmpWalk(oid, ip, community string) (uint, error) {
	result, err := snmpWalk(ip, community, oid, 2*time.Second, 1)
	if err == nil {
		max := uint(0)
		for _, r := range result {
			if r.Value.(uint) > max {
				max = r.Value.(uint)
			}
		}
		return max, nil
	}
	return 0, err
}

func NewJuniperTempChecker(host, service, ip, community string, maxAllowedTemp uint) CheckFunction {
	return func() Event {
		max, err := getMaxValueFromSnmpWalk("1.3.6.1.4.1.2636.3.1.13.1.7", ip, community)
		if err == nil {
			var state = "critical"
			if max < maxAllowedTemp {
				state = "ok"
			}
			return Event{Host: host, Service: service, State: state, Metric: float32(max)}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

func NewJuniperCPUChecker(host, service, ip, community string, maxAllowedTemp uint) CheckFunction {
	return func() Event {
		max, err := getMaxValueFromSnmpWalk("1.3.6.1.4.1.2636.3.1.13.1.8", ip, community)
		if err == nil {
			var state = "critical"
			if max < maxAllowedTemp {
				state = "ok"
			}
			return Event{Host: host, Service: service, State: state, Metric: float32(max)}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

func NewRabbitMQQueueLenCheck(host, service, amqpuri, queue string, max int) CheckFunction {
	return func() Event {
		result := Event{Host: host, Service: service}

		conn, err := amqp.Dial(amqpuri)
		if err != nil {
			result.State = "critical"
			result.Description = err.Error()
			return result
		}

		ch, err := conn.Channel()
		if err != nil {
			result.State = "critical"
			result.Description = err.Error()
			return result
		}
		defer ch.Close()
		defer conn.Close()

		queueInfo, err := ch.QueueInspect(queue)
		if err != nil {
			result.State = "critical"
			result.Description = err.Error()
			return result
		}

		var state = "critical"
		if queueInfo.Messages <= max {
			state = "ok"
		}
		return Event{Host: host, Service: service, State: state, Metric: float32(queueInfo.Messages)}
	}
}

func NewMysqlConnectionCheck(host, service, mysqluri string) CheckFunction {
	return func() Event {
		u, err := url.Parse(mysqluri)
		if err != nil {
			return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
		}

		if u.User == nil {
			return Event{Host: host, Service: service, State: "critical", Description: "No user defined"}
		}
		password, hasPassword := u.User.Password()
		if !hasPassword {
			return Event{Host: host, Service: service, State: "critical", Description: "No password defined"}
		}
		hostAndPort := u.Host
		if !strings.Contains(hostAndPort, ":") {
			hostAndPort = hostAndPort + ":3306"
		}
		var t1 = time.Now()
		con, err := sql.Open("mysql", u.User.Username()+":"+password+"@"+"tcp("+hostAndPort+")"+u.Path)
		defer con.Close()
		if err != nil {
			return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
		}
		q := `select CURTIME()`
		row := con.QueryRow(q)
		var date string
		err = row.Scan(&date)
		milliseconds := float32((time.Now().Sub(t1)).Nanoseconds() / 1e6)
		if err != nil {
			return Event{Host: host, Service: service, State: "critical", Description: err.Error(), Metric: milliseconds}
		}
		return Event{Host: host, Service: service, State: "ok", Metric: milliseconds}
	}
}

type ObtainMetricFunction func() float32
type CalculateStateFunction func(float32) string

func NewGenericCheck(host, service string, metricFunc ObtainMetricFunction, stateFunc CalculateStateFunction) CheckFunction {
	return func() Event {
		value := metricFunc()
		var state = stateFunc(value)
		return Event{Host: host, Service: service, State: state, Metric: value}
	}
}
<commit_msg>Added a bit doc<commit_after>package gochecks

import (
	"fmt"
	"net"
	"strings"
	"time"

	"io/ioutil"
	"net/http"
	"net/url"

	"github.com/streadway/amqp"
	"github.com/tatsushid/go-fastping"
)

import (
	"database/sql"

	_ "github.com/go-sql-driver/mysql"
)

const (
	sysName     = "1.3.6.1.2.1.1.5.0"
	maxPingTime = 4 * time.Second
)

// CheckFunction type for a function that return a event
type CheckFunction func() Event
// MultiCheckFunction type for a function that return an array of events
type MultiCheckFunction func() []Event

// Tags  returns a new check function that adds the given tags to the result
// generated by the initial check function
func (f CheckFunction) Tags(tags ...string) CheckFunction {
	return func() Event {
		result := f()
		result.Tags = tags
		return result
	}
}

// Attributtes  returns a new check function that adds the attributes map to the result
// generated by the initial check function
func (f CheckFunction) Attributes(attributes map[string]string) CheckFunction {
	return func() Event {
		result := f()
		result.Attributes = attributes
		return result
	}
}

// Attributtes  returns a new check function that adds the given TTL time (in seconds) to the result
// generated by the initial check function
func (f CheckFunction) TTL(ttl float32) CheckFunction {
	return func() Event {
		result := f()
		result.TTL = ttl
		return result
	}
}

// Retry returns a new check funciont that execute the given function up to a given retry times or
// until the first execution that returns a ok (whichever comes first). The new function will return
// the event of the last execution
func (f CheckFunction) Retry(times int, sleep time.Duration) CheckFunction {
	return func() Event {
		var result Event
		for i := 0; i < times; i++ {
			result = f()
			if result.State == "ok" {
				return result
			}
			time.Sleep(sleep)
		}
		return result
	}
}

// NewPingChecker returns a check function that can check if a host answer to a ICMP Ping
func NewPingChecker(host, service, ip string) CheckFunction {
	return func() Event {
		var retRtt time.Duration
		var result = Event{Host: host, Service: service, State: "critical"}

		p := fastping.NewPinger()
		p.MaxRTT = maxPingTime
		ra, err := net.ResolveIPAddr("ip4:icmp", ip)
		if err != nil {
			result.Description = err.Error()
		}

		p.AddIPAddr(ra)
		p.OnRecv = func(addr *net.IPAddr, rtt time.Duration) {
			result.State = "ok"
			result.Metric = float32(retRtt.Nanoseconds() / 1e6)
		}

		err = p.Run()
		if err != nil {
			result.Description = err.Error()
		}
		return result
	}
}

// NewTCPPortChecker returns a check function that can check if a host have a tcp port open
func NewTCPPortChecker(host, service, ip string, port int, timeout time.Duration) CheckFunction {
	return func() Event {
		var err error
		var conn net.Conn

		var t1 = time.Now()
		conn, err = net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), timeout)
		if err == nil {
			conn.Close()
			milliseconds := float32((time.Now().Sub(t1)).Nanoseconds() / 1e6)
			return Event{Host: host, Service: service, State: "ok", Metric: milliseconds}
		}
		return Event{Host: host, Service: service, State: "critical"}
	}
}

// ValidateHTTPResponseFunction function type that should validate a http response and return the state (ok, critical, warning) and error description for a check. (Used with NewGenericHTTPChecker)
type ValidateHTTPResponseFunction func(resp *http.Response) (state, description string)

func BodyGreaterThan(minLength int) ValidateHTTPResponseFunction {
	return func(httpResp *http.Response) (state, description string) {
		if httpResp.StatusCode != 200 {
			return "critical", fmt.Sprintf("Response %d", httpResp.StatusCode)
		}
		if httpResp.Body == nil {
			return "critical", fmt.Sprintf("Empty body")
		}
		body, err := ioutil.ReadAll(httpResp.Body)
		if err != nil {
			return "critical", fmt.Sprintf("Error geting body")
		}
		if len(body) < minLength {
			return "critical", fmt.Sprintf("Obtained %d bytes, expected more than %d", len(body), minLength)
		}
		return "ok", ""
	}
}

func NewGenericHTTPChecker(host, service, url string, validationFunc ValidateHTTPResponseFunction) CheckFunction {
	return func() Event {
		var t1 = time.Now()

		response, err := http.Get(url)
		milliseconds := float32((time.Now().Sub(t1)).Nanoseconds() / 1e6)
		result := Event{Host: host, Service: service, State: "critical", Metric: milliseconds}
		if err != nil {
			result.Description = err.Error()
		} else {
			if response.Body != nil {
				defer response.Body.Close()
			}
			result.State, result.Description = validationFunc(response)
		}
		return result
	}
}

// NewHTTPChecker returns a check function that get a given url and validate if the return code is the expected one
func NewHTTPChecker(host, service, url string, expectedStatusCode int) CheckFunction {
	return NewGenericHTTPChecker(host, service, url,
		func(httpResp *http.Response) (string, string) {
			if httpResp.StatusCode == expectedStatusCode {
				return "ok", ""
			}
			return "critical", fmt.Sprintf("Response %d", httpResp.StatusCode)
		})
}

type SnmpCheckerConf struct {
	retries    int
	timeout    time.Duration
	oidToCheck string
}

var DefaultSnmpCheckConf = SnmpCheckerConf{
	retries:    1,
	timeout:    1 * time.Second,
	oidToCheck: sysName,
}

func NewSnmpChecker(host, service, ip, community string, conf SnmpCheckerConf) CheckFunction {
	return func() Event {

		_, err := snmpGet(ip, community, []string{conf.oidToCheck}, conf.timeout, conf.retries)
		if err == nil {
			return Event{Host: host, Service: service, State: "ok", Description: err.Error()}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

func NewC4CMTSTempChecker(host, service, ip, community string, maxAllowedTemp int) CheckFunction {
	return func() Event {

		result, err := snmpWalk(ip, community, "1.3.6.1.4.1.4998.1.1.10.1.4.2.1.29", 2*time.Second, 1)

		if err == nil {
			max := 0
			for _, r := range result {
				if r.Value.(int) != 999 && r.Value.(int) > max {
					max = r.Value.(int)
				}
			}
			var state = "critical"
			if max < maxAllowedTemp {
				state = "ok"
			}
			return Event{Host: host, Service: service, State: state, Metric: float32(max)}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

func getMaxValueFromSnmpWalk(oid, ip, community string) (uint, error) {
	result, err := snmpWalk(ip, community, oid, 2*time.Second, 1)
	if err == nil {
		max := uint(0)
		for _, r := range result {
			if r.Value.(uint) > max {
				max = r.Value.(uint)
			}
		}
		return max, nil
	}
	return 0, err
}

func NewJuniperTempChecker(host, service, ip, community string, maxAllowedTemp uint) CheckFunction {
	return func() Event {
		max, err := getMaxValueFromSnmpWalk("1.3.6.1.4.1.2636.3.1.13.1.7", ip, community)
		if err == nil {
			var state = "critical"
			if max < maxAllowedTemp {
				state = "ok"
			}
			return Event{Host: host, Service: service, State: state, Metric: float32(max)}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

func NewJuniperCPUChecker(host, service, ip, community string, maxAllowedTemp uint) CheckFunction {
	return func() Event {
		max, err := getMaxValueFromSnmpWalk("1.3.6.1.4.1.2636.3.1.13.1.8", ip, community)
		if err == nil {
			var state = "critical"
			if max < maxAllowedTemp {
				state = "ok"
			}
			return Event{Host: host, Service: service, State: state, Metric: float32(max)}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

func NewRabbitMQQueueLenCheck(host, service, amqpuri, queue string, max int) CheckFunction {
	return func() Event {
		result := Event{Host: host, Service: service}

		conn, err := amqp.Dial(amqpuri)
		if err != nil {
			result.State = "critical"
			result.Description = err.Error()
			return result
		}

		ch, err := conn.Channel()
		if err != nil {
			result.State = "critical"
			result.Description = err.Error()
			return result
		}
		defer ch.Close()
		defer conn.Close()

		queueInfo, err := ch.QueueInspect(queue)
		if err != nil {
			result.State = "critical"
			result.Description = err.Error()
			return result
		}

		var state = "critical"
		if queueInfo.Messages <= max {
			state = "ok"
		}
		return Event{Host: host, Service: service, State: state, Metric: float32(queueInfo.Messages)}
	}
}

func NewMysqlConnectionCheck(host, service, mysqluri string) CheckFunction {
	return func() Event {
		u, err := url.Parse(mysqluri)
		if err != nil {
			return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
		}

		if u.User == nil {
			return Event{Host: host, Service: service, State: "critical", Description: "No user defined"}
		}
		password, hasPassword := u.User.Password()
		if !hasPassword {
			return Event{Host: host, Service: service, State: "critical", Description: "No password defined"}
		}
		hostAndPort := u.Host
		if !strings.Contains(hostAndPort, ":") {
			hostAndPort = hostAndPort + ":3306"
		}
		var t1 = time.Now()
		con, err := sql.Open("mysql", u.User.Username()+":"+password+"@"+"tcp("+hostAndPort+")"+u.Path)
		defer con.Close()
		if err != nil {
			return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
		}
		q := `select CURTIME()`
		row := con.QueryRow(q)
		var date string
		err = row.Scan(&date)
		milliseconds := float32((time.Now().Sub(t1)).Nanoseconds() / 1e6)
		if err != nil {
			return Event{Host: host, Service: service, State: "critical", Description: err.Error(), Metric: milliseconds}
		}
		return Event{Host: host, Service: service, State: "ok", Metric: milliseconds}
	}
}

type ObtainMetricFunction func() float32
type CalculateStateFunction func(float32) string

func NewGenericCheck(host, service string, metricFunc ObtainMetricFunction, stateFunc CalculateStateFunction) CheckFunction {
	return func() Event {
		value := metricFunc()
		var state = stateFunc(value)
		return Event{Host: host, Service: service, State: state, Metric: value}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net"
	"net/http"
	"os"
	"runtime"
	"strings"
	"time"

	"github.com/fukata/golang-stats-api-handler"
	"github.com/jessevdk/go-flags"
	"github.com/kazeburo/chocon/proxy"
	"github.com/lestrrat/go-apache-logformat"
	"github.com/lestrrat/go-file-rotatelogs"
	"github.com/lestrrat/go-server-starter-listener"
)

var (
	Version string
)

type cmdOpts struct {
	Listen           string `short:"l" long:"listen" default:"0.0.0.0" description:"address to bind"`
	Port             string `short:"p" long:"port" default:"3000" description:"Port number to bind"`
	LogDir           string `long:"access-log-dir" default:"" description:"directory to store logfiles"`
	LogRotate        int64  `long:"access-log-rotate" default:"30" description:"Number of day before remove logs"`
	Version          bool   `short:"v" long:"version" description:"Show version"`
	KeepaliveConns   int    `short:"c" default:"2" long:"keepalive-conns" description:"maximum keepalive connections for upstream"`
	ReadTimeout      int    `long:"read-timeout" default:"30" description:"timeout of reading request"`
	WriteTimeout     int    `long:"write-timeout" default:"90" description:"timeout of writing response"`
	ProxyReadTimeout int    `long:"proxy-read-timeout" default:"60" description:"timeout of reading response from upstream"`
}

func addStatsHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if strings.Index(r.URL.Path, "/.api/stats") == 0 {
			stats_api.Handler(w, r)
		} else {
			h.ServeHTTP(w, r)
		}
	})
}

func addLogHandler(h http.Handler, logDir string, logRotate int64) http.Server {
	apacheLog, err := apachelog.New(`%h %l %u %t "%r" %>s %b "%v" %T.%{msec_frac}t %{X-Chocon-Req}i`)
	if err != nil {
		panic(fmt.Sprintf("could not create logger: %v", err))
	}

	if logDir == "stdout" {
		return http.Server{
			Handler: apacheLog.Wrap(h, os.Stdout),
		}
	} else if logDir == "" {
		return http.Server{
			Handler: apacheLog.Wrap(h, os.Stderr),
		}
	} else if logDir == "none" {
		return http.Server{
			Handler: h,
		}
	}

	logFile := logDir
	linkName := logDir
	if !strings.HasSuffix(logDir, "/") {
		logFile += "/"
		linkName += "/"

	}
	logFile += "access_log.%Y%m%d%H%M"
	linkName += "current"

	rl, err := rotatelogs.New(
		logFile,
		rotatelogs.WithLinkName(linkName),
		rotatelogs.WithMaxAge(time.Duration(logRotate)*86400*time.Second),
		rotatelogs.WithRotationTime(time.Second*86400),
	)
	if err != nil {
		panic(fmt.Sprintf("rotatelogs.New failed: %v", err))
	}

	return http.Server{
		Handler: apacheLog.Wrap(h, rl),
	}
}

func main() {
	opts := cmdOpts{}
	psr := flags.NewParser(&opts, flags.Default)
	_, err := psr.Parse()
	if err != nil {
		os.Exit(1)
	}

	if opts.Version {
		fmt.Printf(`chocon %s
Compiler: %s %s
`,
			Version,
			runtime.Compiler,
			runtime.Version())
		return

	}

	requestConverter := func(r *http.Request, pr *http.Request, ps *proxy.ProxyStatus) {
		if r.Host == "" {
			ps.Status = http.StatusBadRequest
			return
		}
		host := strings.Split(r.Host, ":")[0]
		hostSplited := strings.Split(host, ".")
		lastPartIndex := 0
		for i, hostPart := range hostSplited {
			if hostPart == "ccnproxy-ssl" || hostPart == "ccnproxy-secure" || hostPart == "ccnproxy-https" || hostPart == "ccnproxy" {
				lastPartIndex = i
			}
		}
		if lastPartIndex == 0 {
			ps.Status = http.StatusBadRequest
			return
		}

		pr.URL.Host = strings.Join(hostSplited[0:lastPartIndex], ".")
		pr.Host = pr.URL.Host
		if hostSplited[lastPartIndex] == "ccnproxy-https" || hostSplited[lastPartIndex] == "ccnproxy-secure" || hostSplited[lastPartIndex] == "ccnproxy-ssl" {
			pr.URL.Scheme = "https"
		}
	}

	var transport http.RoundTripper = &http.Transport{
		// inherited http.DefaultTransport
		Proxy: http.ProxyFromEnvironment,
		DialContext: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		}).DialContext,
		IdleConnTimeout:       90 * time.Second,
		TLSHandshakeTimeout:   10 * time.Second,
		ExpectContinueTimeout: 1 * time.Second,
		// self-customized values
		MaxIdleConnsPerHost:   opts.KeepaliveConns,
		ResponseHeaderTimeout: time.Duration(opts.ProxyReadTimeout) * time.Second,
	}

	proxyHandler := addStatsHandler(proxy.NewProxyWithRequestConverter(requestConverter, &transport))

	l, err := ss.NewListener()
	if l == nil || err != nil {
		// Fallback if not running under Server::Starter
		l, err = net.Listen("tcp", fmt.Sprintf("%s:%s", opts.Listen, opts.Port))
		if err != nil {
			panic(fmt.Sprintf("Failed to listen to port %s:%s", opts.Listen, opts.Port))
		}
	}

	server := addLogHandler(proxyHandler, opts.LogDir, opts.LogRotate)
	server.ReadTimeout = time.Duration(opts.ReadTimeout) * time.Second
	server.WriteTimeout = time.Duration(opts.WriteTimeout) * time.Second
	server.Serve(l)
}
<commit_msg>Fix grammar split past tense<commit_after>package main

import (
	"fmt"
	"net"
	"net/http"
	"os"
	"runtime"
	"strings"
	"time"

	"github.com/fukata/golang-stats-api-handler"
	"github.com/jessevdk/go-flags"
	"github.com/kazeburo/chocon/proxy"
	"github.com/lestrrat/go-apache-logformat"
	"github.com/lestrrat/go-file-rotatelogs"
	"github.com/lestrrat/go-server-starter-listener"
)

var (
	Version string
)

type cmdOpts struct {
	Listen           string `short:"l" long:"listen" default:"0.0.0.0" description:"address to bind"`
	Port             string `short:"p" long:"port" default:"3000" description:"Port number to bind"`
	LogDir           string `long:"access-log-dir" default:"" description:"directory to store logfiles"`
	LogRotate        int64  `long:"access-log-rotate" default:"30" description:"Number of day before remove logs"`
	Version          bool   `short:"v" long:"version" description:"Show version"`
	KeepaliveConns   int    `short:"c" default:"2" long:"keepalive-conns" description:"maximum keepalive connections for upstream"`
	ReadTimeout      int    `long:"read-timeout" default:"30" description:"timeout of reading request"`
	WriteTimeout     int    `long:"write-timeout" default:"90" description:"timeout of writing response"`
	ProxyReadTimeout int    `long:"proxy-read-timeout" default:"60" description:"timeout of reading response from upstream"`
}

func addStatsHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if strings.Index(r.URL.Path, "/.api/stats") == 0 {
			stats_api.Handler(w, r)
		} else {
			h.ServeHTTP(w, r)
		}
	})
}

func addLogHandler(h http.Handler, logDir string, logRotate int64) http.Server {
	apacheLog, err := apachelog.New(`%h %l %u %t "%r" %>s %b "%v" %T.%{msec_frac}t %{X-Chocon-Req}i`)
	if err != nil {
		panic(fmt.Sprintf("could not create logger: %v", err))
	}

	if logDir == "stdout" {
		return http.Server{
			Handler: apacheLog.Wrap(h, os.Stdout),
		}
	} else if logDir == "" {
		return http.Server{
			Handler: apacheLog.Wrap(h, os.Stderr),
		}
	} else if logDir == "none" {
		return http.Server{
			Handler: h,
		}
	}

	logFile := logDir
	linkName := logDir
	if !strings.HasSuffix(logDir, "/") {
		logFile += "/"
		linkName += "/"

	}
	logFile += "access_log.%Y%m%d%H%M"
	linkName += "current"

	rl, err := rotatelogs.New(
		logFile,
		rotatelogs.WithLinkName(linkName),
		rotatelogs.WithMaxAge(time.Duration(logRotate)*86400*time.Second),
		rotatelogs.WithRotationTime(time.Second*86400),
	)
	if err != nil {
		panic(fmt.Sprintf("rotatelogs.New failed: %v", err))
	}

	return http.Server{
		Handler: apacheLog.Wrap(h, rl),
	}
}

func main() {
	opts := cmdOpts{}
	psr := flags.NewParser(&opts, flags.Default)
	_, err := psr.Parse()
	if err != nil {
		os.Exit(1)
	}

	if opts.Version {
		fmt.Printf(`chocon %s
Compiler: %s %s
`,
			Version,
			runtime.Compiler,
			runtime.Version())
		return

	}

	requestConverter := func(r *http.Request, pr *http.Request, ps *proxy.ProxyStatus) {
		if r.Host == "" {
			ps.Status = http.StatusBadRequest
			return
		}
		host := strings.Split(r.Host, ":")[0]
		hostSplit := strings.Split(host, ".")
		lastPartIndex := 0
		for i, hostPart := range hostSplit {
			if hostPart == "ccnproxy-ssl" || hostPart == "ccnproxy-secure" || hostPart == "ccnproxy-https" || hostPart == "ccnproxy" {
				lastPartIndex = i
			}
		}
		if lastPartIndex == 0 {
			ps.Status = http.StatusBadRequest
			return
		}

		pr.URL.Host = strings.Join(hostSplit[0:lastPartIndex], ".")
		pr.Host = pr.URL.Host
		if hostSplit[lastPartIndex] == "ccnproxy-https" || hostSplit[lastPartIndex] == "ccnproxy-secure" || hostSplit[lastPartIndex] == "ccnproxy-ssl" {
			pr.URL.Scheme = "https"
		}
	}

	var transport http.RoundTripper = &http.Transport{
		// inherited http.DefaultTransport
		Proxy: http.ProxyFromEnvironment,
		DialContext: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		}).DialContext,
		IdleConnTimeout:       90 * time.Second,
		TLSHandshakeTimeout:   10 * time.Second,
		ExpectContinueTimeout: 1 * time.Second,
		// self-customized values
		MaxIdleConnsPerHost:   opts.KeepaliveConns,
		ResponseHeaderTimeout: time.Duration(opts.ProxyReadTimeout) * time.Second,
	}

	proxyHandler := addStatsHandler(proxy.NewProxyWithRequestConverter(requestConverter, &transport))

	l, err := ss.NewListener()
	if l == nil || err != nil {
		// Fallback if not running under Server::Starter
		l, err = net.Listen("tcp", fmt.Sprintf("%s:%s", opts.Listen, opts.Port))
		if err != nil {
			panic(fmt.Sprintf("Failed to listen to port %s:%s", opts.Listen, opts.Port))
		}
	}

	server := addLogHandler(proxyHandler, opts.LogDir, opts.LogRotate)
	server.ReadTimeout = time.Duration(opts.ReadTimeout) * time.Second
	server.WriteTimeout = time.Duration(opts.WriteTimeout) * time.Second
	server.Serve(l)
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013 Kurt Jung (Gmail: kurt.w.jung)
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package gofpdf

import (
	"bytes"
)

// Version of FPDF from which this package is derived
const (
	FPDF_VERSION = "1.7"
)

type blendModeType struct {
	strokeStr, fillStr, modeStr string
	objNum                      int
}

type gradientType struct {
	tp                int // 2: linear, 3: radial
	clr1Str, clr2Str  string
	x1, y1, x2, y2, r float64
	objNum            int
}

// Wd and Ht specify the horizontal and vertical extents of a document page.
type SizeType struct {
	Wd, Ht float64
}

// X and Y specify the horizontal and vertical coordinates of a point,
// typically used in drawing.
type PointType struct {
	X, Y float64
}

type ImageInfoType struct {
	data  []byte
	smask []byte
	i     int
	n     int
	w     float64
	h     float64
	cs    string
	pal   []byte
	bpc   int
	f     string
	dp    string
	trns  []int
}

// The width of the image in the units of the Fpdf object.
func (info *ImageInfoType) Width(f *Fpdf) float64 {
	return info.w/f.k
}

// The height of the image in the units of the Fpdf object.
func (info *ImageInfoType) Height(f *Fpdf) float64 {
	return info.h/f.k
}

type fontFileType struct {
	length1, length2 int64
	n                int
}

type linkType struct {
	x, y, wd, ht float64
	link         int    // Auto-generated internal link ID or...
	linkStr      string // ...application-provided external link string
}

type intLinkType struct {
	page int
	y    float64
}

// outlineType is used for a sidebar outline of bookmarks
type outlineType struct {
	text                                   string
	level, parent, first, last, next, prev int
	y                                      float64
	p                                      int
}

// InitType is used with NewCustom() to customize an Fpdf instance.
// OrientationStr, UnitStr, SizeStr and FontDirStr correspond to the arguments
// accepted by New(). If the Wd and Ht fields of Size are each greater than
// zero, Size will be used to set the default page size rather than SizeStr. Wd
// and Ht are specified in the units of measure indicated by UnitStr.
type InitType struct {
	OrientationStr string
	UnitStr        string
	SizeStr        string
	Size           SizeType
	FontDirStr     string
}

// Principal structure for creating a single PDF document
type Fpdf struct {
	page             int                      // current page number
	n                int                      // current object number
	offsets          []int                    // array of object offsets
	buffer           fmtBuffer                // buffer holding in-memory PDF
	pages            []*bytes.Buffer          // slice[page] of page content; 1-based
	state            int                      // current document state
	compress         bool                     // compression flag
	k                float64                  // scale factor (number of points in user unit)
	defOrientation   string                   // default orientation
	curOrientation   string                   // current orientation
	stdPageSizes     map[string]SizeType      // standard page sizes
	defPageSize      SizeType                 // default page size
	curPageSize      SizeType                 // current page size
	pageSizes        map[int]SizeType         // used for pages with non default sizes or orientations
	unitStr          string                   // unit of measure for all rendered objects except fonts
	wPt, hPt         float64                  // dimensions of current page in points
	w, h             float64                  // dimensions of current page in user unit
	lMargin          float64                  // left margin
	tMargin          float64                  // top margin
	rMargin          float64                  // right margin
	bMargin          float64                  // page break margin
	cMargin          float64                  // cell margin
	x, y             float64                  // current position in user unit
	lasth            float64                  // height of last printed cell
	lineWidth        float64                  // line width in user unit
	fontpath         string                   // path containing fonts
	coreFonts        map[string]bool          // array of core font names
	fonts            map[string]fontDefType   // array of used fonts
	fontFiles        map[string]fontFileType  // array of font files
	diffs            []string                 // array of encoding differences
	fontFamily       string                   // current font family
	fontStyle        string                   // current font style
	underline        bool                     // underlining flag
	currentFont      fontDefType              // current font info
	fontSizePt       float64                  // current font size in points
	fontSize         float64                  // current font size in user unit
	drawColor        string                   // commands for drawing color
	fillColor        string                   // commands for filling color
	textColor        string                   // commands for text color
	colorFlag        bool                     // indicates whether fill and text colors are different
	ws               float64                  // word spacing
	images           map[string]*ImageInfoType// array of used images
	pageLinks        [][]linkType             // pageLinks[page][link], both 1-based
	links            []intLinkType            // array of internal links
	outlines         []outlineType            // array of outlines
	outlineRoot      int                      // root of outlines
	autoPageBreak    bool                     // automatic page breaking
	acceptPageBreak  func() bool              // returns true to accept page break
	pageBreakTrigger float64                  // threshold used to trigger page breaks
	inHeader         bool                     // flag set when processing header
	headerFnc        func()                   // function provided by app and called to write header
	inFooter         bool                     // flag set when processing footer
	footerFnc        func()                   // function provided by app and called to write footer
	zoomMode         string                   // zoom display mode
	layoutMode       string                   // layout display mode
	title            string                   // title
	subject          string                   // subject
	author           string                   // author
	keywords         string                   // keywords
	creator          string                   // creator
	aliasNbPagesStr  string                   // alias for total number of pages
	pdfVersion       string                   // PDF version number
	fontDirStr       string                   // location of font definition files
	capStyle         int                      // line cap style: butt 0, round 1, square 2
	joinStyle        int                      // line segment join style: miter 0, round 1, bevel 2
	blendList        []blendModeType          // slice[idx] of alpha transparency modes, 1-based
	blendMap         map[string]int           // map into blendList
	gradientList     []gradientType           // slice[idx] of gradient records
	clipNest         int                      // Number of active clipping contexts
	transformNest    int                      // Number of active transformation contexts
	err              error                    // Set if error occurs during life cycle of instance
}

type encType struct {
	uv   int
	name string
}

type encListType [256]encType

type fontBoxType struct {
	Xmin, Ymin, Xmax, Ymax int
}

type fontDescType struct {
	Ascent       int
	Descent      int
	CapHeight    int
	Flags        int
	FontBBox     fontBoxType
	ItalicAngle  int
	StemV        int
	MissingWidth int
}

type fontDefType struct {
	Tp           string       // "Core", "TrueType", ...
	Name         string       // "Courier-Bold", ...
	Desc         fontDescType // Font descriptor
	Up           int          // Underline position
	Ut           int          // Underline thickness
	Cw           [256]int     // Character width by ordinal
	Enc          string       // "cp1252", ...
	Diff         string       // Differences from reference encoding
	File         string       // "Redressed.z"
	Size1, Size2 int          // Type1 values
	OriginalSize int          // Size of uncompressed font file
	I            int          // 1-based position in font list, set by font loader, not this program
	N            int          // Set by font loader
	DiffN        int          // Position of diff in app array, set by font loader
}

type fontInfoType struct {
	Data               []byte
	File               string
	OriginalSize       int
	FontName           string
	Bold               bool
	IsFixedPitch       bool
	UnderlineThickness int
	UnderlinePosition  int
	Widths             [256]int
	Size1, Size2       uint32
	Desc               fontDescType
}
<commit_msg>Go fmt one file.<commit_after>/*
 * Copyright (c) 2013 Kurt Jung (Gmail: kurt.w.jung)
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package gofpdf

import (
	"bytes"
)

// Version of FPDF from which this package is derived
const (
	FPDF_VERSION = "1.7"
)

type blendModeType struct {
	strokeStr, fillStr, modeStr string
	objNum                      int
}

type gradientType struct {
	tp                int // 2: linear, 3: radial
	clr1Str, clr2Str  string
	x1, y1, x2, y2, r float64
	objNum            int
}

// Wd and Ht specify the horizontal and vertical extents of a document page.
type SizeType struct {
	Wd, Ht float64
}

// X and Y specify the horizontal and vertical coordinates of a point,
// typically used in drawing.
type PointType struct {
	X, Y float64
}

type ImageInfoType struct {
	data  []byte
	smask []byte
	i     int
	n     int
	w     float64
	h     float64
	cs    string
	pal   []byte
	bpc   int
	f     string
	dp    string
	trns  []int
}

// The width of the image in the units of the Fpdf object.
func (info *ImageInfoType) Width(f *Fpdf) float64 {
	return info.w / f.k
}

// The height of the image in the units of the Fpdf object.
func (info *ImageInfoType) Height(f *Fpdf) float64 {
	return info.h / f.k
}

type fontFileType struct {
	length1, length2 int64
	n                int
}

type linkType struct {
	x, y, wd, ht float64
	link         int    // Auto-generated internal link ID or...
	linkStr      string // ...application-provided external link string
}

type intLinkType struct {
	page int
	y    float64
}

// outlineType is used for a sidebar outline of bookmarks
type outlineType struct {
	text                                   string
	level, parent, first, last, next, prev int
	y                                      float64
	p                                      int
}

// InitType is used with NewCustom() to customize an Fpdf instance.
// OrientationStr, UnitStr, SizeStr and FontDirStr correspond to the arguments
// accepted by New(). If the Wd and Ht fields of Size are each greater than
// zero, Size will be used to set the default page size rather than SizeStr. Wd
// and Ht are specified in the units of measure indicated by UnitStr.
type InitType struct {
	OrientationStr string
	UnitStr        string
	SizeStr        string
	Size           SizeType
	FontDirStr     string
}

// Principal structure for creating a single PDF document
type Fpdf struct {
	page             int                       // current page number
	n                int                       // current object number
	offsets          []int                     // array of object offsets
	buffer           fmtBuffer                 // buffer holding in-memory PDF
	pages            []*bytes.Buffer           // slice[page] of page content; 1-based
	state            int                       // current document state
	compress         bool                      // compression flag
	k                float64                   // scale factor (number of points in user unit)
	defOrientation   string                    // default orientation
	curOrientation   string                    // current orientation
	stdPageSizes     map[string]SizeType       // standard page sizes
	defPageSize      SizeType                  // default page size
	curPageSize      SizeType                  // current page size
	pageSizes        map[int]SizeType          // used for pages with non default sizes or orientations
	unitStr          string                    // unit of measure for all rendered objects except fonts
	wPt, hPt         float64                   // dimensions of current page in points
	w, h             float64                   // dimensions of current page in user unit
	lMargin          float64                   // left margin
	tMargin          float64                   // top margin
	rMargin          float64                   // right margin
	bMargin          float64                   // page break margin
	cMargin          float64                   // cell margin
	x, y             float64                   // current position in user unit
	lasth            float64                   // height of last printed cell
	lineWidth        float64                   // line width in user unit
	fontpath         string                    // path containing fonts
	coreFonts        map[string]bool           // array of core font names
	fonts            map[string]fontDefType    // array of used fonts
	fontFiles        map[string]fontFileType   // array of font files
	diffs            []string                  // array of encoding differences
	fontFamily       string                    // current font family
	fontStyle        string                    // current font style
	underline        bool                      // underlining flag
	currentFont      fontDefType               // current font info
	fontSizePt       float64                   // current font size in points
	fontSize         float64                   // current font size in user unit
	drawColor        string                    // commands for drawing color
	fillColor        string                    // commands for filling color
	textColor        string                    // commands for text color
	colorFlag        bool                      // indicates whether fill and text colors are different
	ws               float64                   // word spacing
	images           map[string]*ImageInfoType // array of used images
	pageLinks        [][]linkType              // pageLinks[page][link], both 1-based
	links            []intLinkType             // array of internal links
	outlines         []outlineType             // array of outlines
	outlineRoot      int                       // root of outlines
	autoPageBreak    bool                      // automatic page breaking
	acceptPageBreak  func() bool               // returns true to accept page break
	pageBreakTrigger float64                   // threshold used to trigger page breaks
	inHeader         bool                      // flag set when processing header
	headerFnc        func()                    // function provided by app and called to write header
	inFooter         bool                      // flag set when processing footer
	footerFnc        func()                    // function provided by app and called to write footer
	zoomMode         string                    // zoom display mode
	layoutMode       string                    // layout display mode
	title            string                    // title
	subject          string                    // subject
	author           string                    // author
	keywords         string                    // keywords
	creator          string                    // creator
	aliasNbPagesStr  string                    // alias for total number of pages
	pdfVersion       string                    // PDF version number
	fontDirStr       string                    // location of font definition files
	capStyle         int                       // line cap style: butt 0, round 1, square 2
	joinStyle        int                       // line segment join style: miter 0, round 1, bevel 2
	blendList        []blendModeType           // slice[idx] of alpha transparency modes, 1-based
	blendMap         map[string]int            // map into blendList
	gradientList     []gradientType            // slice[idx] of gradient records
	clipNest         int                       // Number of active clipping contexts
	transformNest    int                       // Number of active transformation contexts
	err              error                     // Set if error occurs during life cycle of instance
}

type encType struct {
	uv   int
	name string
}

type encListType [256]encType

type fontBoxType struct {
	Xmin, Ymin, Xmax, Ymax int
}

type fontDescType struct {
	Ascent       int
	Descent      int
	CapHeight    int
	Flags        int
	FontBBox     fontBoxType
	ItalicAngle  int
	StemV        int
	MissingWidth int
}

type fontDefType struct {
	Tp           string       // "Core", "TrueType", ...
	Name         string       // "Courier-Bold", ...
	Desc         fontDescType // Font descriptor
	Up           int          // Underline position
	Ut           int          // Underline thickness
	Cw           [256]int     // Character width by ordinal
	Enc          string       // "cp1252", ...
	Diff         string       // Differences from reference encoding
	File         string       // "Redressed.z"
	Size1, Size2 int          // Type1 values
	OriginalSize int          // Size of uncompressed font file
	I            int          // 1-based position in font list, set by font loader, not this program
	N            int          // Set by font loader
	DiffN        int          // Position of diff in app array, set by font loader
}

type fontInfoType struct {
	Data               []byte
	File               string
	OriginalSize       int
	FontName           string
	Bold               bool
	IsFixedPitch       bool
	UnderlineThickness int
	UnderlinePosition  int
	Widths             [256]int
	Size1, Size2       uint32
	Desc               fontDescType
}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/go.tools/go/vcs"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"sort"
	"strings"
)

// Godeps describes what a package needs to be rebuilt reproducibly.
// It's the same information stored in file Godeps.
type Godeps struct {
	ImportPath string
	GoVersion  string
	Packages   []string `json:",omitempty"` // Arguments to save, if any.
	Deps       []Dependency

	outerRoot string
}

// A Dependency is a specific revision of a package.
type Dependency struct {
	ImportPath string
	Comment    string `json:",omitempty"` // Description of commit, if present.
	Rev        string // VCS-specific commit ID.

	// used by command save
	pkg *Package

	// used by command go
	outerRoot string // dir, if present, in outer GOPATH
	repoRoot  *vcs.RepoRoot
	vcs       *VCS
}

// pkgs is the list of packages to read dependencies
func (g *Godeps) Load(pkgs []*Package) error {
	var err1 error
	var path, seen []string
	for _, p := range pkgs {
		if p.Standard {
			log.Println("ignoring stdlib package:", p.ImportPath)
			continue
		}
		if p.Error.Err != "" {
			log.Println(p.Error.Err)
			err1 = errors.New("error loading packages")
			continue
		}
		seen = append(seen, p.ImportPath)
		path = append(path, p.Deps...)
	}
	var testImports []string
	for _, p := range pkgs {
		testImports = append(testImports, p.TestImports...)
	}
	for _, p := range MustLoadPackages(testImports...) {
		if p.Standard {
			continue
		}
		if p.Error.Err != "" {
			log.Println(p.Error.Err)
			err1 = errors.New("error loading packages")
			continue
		}
		path = append(path, p.ImportPath)
		path = append(path, p.Deps...)
	}
	sort.Strings(path)
	path = uniq(path)
	for _, pkg := range MustLoadPackages(path...) {
		if pkg.Error.Err != "" {
			log.Println(pkg.Error.Err)
			err1 = errors.New("error loading dependencies")
			continue
		}
		if pkg.Standard {
			continue
		}
		vcs, rr, err := VCSForImportPath(pkg.ImportPath)
		if err != nil {
			log.Println(err)
			err1 = errors.New("error loading dependencies")
			continue
		}
		if contains(seen, rr.Root) {
			continue
		}
		seen = append(seen, rr.Root)
		id, err := vcs.identify(pkg.Dir)
		if err != nil {
			log.Println(err)
			err1 = errors.New("error loading dependencies")
			continue
		}
		if vcs.isDirty(pkg.Dir, id) {
			log.Println("dirty working tree:", pkg.Dir)
			err1 = errors.New("error loading dependencies")
			continue
		}
		comment := vcs.describe(pkg.Dir, id)
		g.Deps = append(g.Deps, Dependency{
			ImportPath: pkg.ImportPath,
			Rev:        id,
			Comment:    comment,
			pkg:        pkg,
			vcs:        vcs,
		})
	}
	return err1
}

func ReadGodeps(path string) (*Godeps, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	g := new(Godeps)
	err = json.NewDecoder(f).Decode(g)
	if err != nil {
		return nil, err
	}
	err = g.loadGoList()
	if err != nil {
		return nil, err
	}

	for i := range g.Deps {
		d := &g.Deps[i]
		d.vcs, d.repoRoot, err = VCSForImportPath(d.ImportPath)
		if err != nil {
			return nil, err
		}
	}
	return g, nil
}

func (g *Godeps) loadGoList() error {
	a := []string{g.ImportPath}
	for _, d := range g.Deps {
		a = append(a, d.ImportPath)
	}
	ps, err := LoadPackages(a...)
	if err != nil {
		return err
	}
	g.outerRoot = ps[0].Root
	for i, p := range ps[1:] {
		g.Deps[i].outerRoot = p.Root
	}
	return nil
}

func (g *Godeps) WriteTo(w io.Writer) (int, error) {
	b, err := json.MarshalIndent(g, "", "\t")
	if err != nil {
		return 0, err
	}
	return w.Write(append(b, '\n'))
}

// Returns a path to the local copy of d's repository.
// E.g.
//
//   ImportPath             RepoPath
//   github.com/kr/s3       $spool/github.com/kr/s3
//   github.com/lib/pq/oid  $spool/github.com/lib/pq
func (d Dependency) RepoPath() string {
	return filepath.Join(spool, "repo", d.repoRoot.Root)
}

// Returns a URL for the remote copy of the repository.
func (d Dependency) RemoteURL() string {
	return d.repoRoot.Repo
}

// Returns the url of a local disk clone of the repo, if any.
func (d Dependency) FastRemotePath() string {
	if d.outerRoot != "" {
		return d.outerRoot + "/src/" + d.repoRoot.Root
	}
	return ""
}

// Returns a path to the checked-out copy of d's commit.
func (d Dependency) Workdir() string {
	return filepath.Join(d.Gopath(), "src", d.ImportPath)
}

// Returns a path to the checked-out copy of d's repo root.
func (d Dependency) WorkdirRoot() string {
	return filepath.Join(d.Gopath(), "src", d.repoRoot.Root)
}

// Returns a path to a parent of Workdir such that using
// Gopath in GOPATH makes d available to the go tool.
func (d Dependency) Gopath() string {
	return filepath.Join(spool, "rev", d.Rev[:2], d.Rev[2:])
}

// Creates an empty repo in d.RepoPath().
func (d Dependency) CreateRepo(fastRemote, mainRemote string) error {
	if err := os.MkdirAll(d.RepoPath(), 0777); err != nil {
		return err
	}
	if err := d.vcs.create(d.RepoPath()); err != nil {
		return err
	}
	if err := d.link(fastRemote, d.FastRemotePath()); err != nil {
		return err
	}
	return d.link(mainRemote, d.RemoteURL())
}

func (d Dependency) link(remote, url string) error {
	return d.vcs.link(d.RepoPath(), remote, url)
}

func (d Dependency) fetchAndCheckout(remote string) error {
	if err := d.fetch(remote); err != nil {
		return fmt.Errorf("fetch: %s", err)
	}
	if err := d.checkout(); err != nil {
		return fmt.Errorf("checkout: %s", err)
	}
	return nil
}

func (d Dependency) fetch(remote string) error {
	return d.vcs.fetch(d.RepoPath(), remote)
}

func (d Dependency) checkout() error {
	dir := d.WorkdirRoot()
	if exists(dir) {
		return nil
	}
	if !d.vcs.exists(d.RepoPath(), d.Rev) {
		return fmt.Errorf("unknown rev %s for %s", d.Rev, d.ImportPath)
	}
	if err := os.MkdirAll(dir, 0777); err != nil {
		return err
	}
	return d.vcs.checkout(dir, d.Rev, d.RepoPath())
}

func contains(a []string, s string) bool {
	for _, p := range a {
		if s == p {
			return true
		}
	}
	return false
}

func uniq(a []string) []string {
	i := 0
	s := ""
	for _, t := range a {
		if t != s {
			a[i] = t
			i++
			s = t
		}
	}
	return a[:i]
}

// mustGoVersion returns the version string of the Go compiler
// currently installed, e.g. "go1.1rc3".
func mustGoVersion() string {
	// Godep might have been compiled with a different
	// version, so we can't just use runtime.Version here.
	cmd := exec.Command("go", "version")
	cmd.Stderr = os.Stderr
	out, err := cmd.Output()
	if err != nil {
		log.Fatal(err)
	}
	s := strings.TrimSpace(string(out))
	s = strings.TrimSuffix(s, " "+runtime.GOOS+"/"+runtime.GOARCH)
	s = strings.TrimPrefix(s, "go version ")
	return s
}
<commit_msg>go vet<commit_after>package main

import (
	"code.google.com/p/go.tools/go/vcs"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"sort"
	"strings"
)

// Godeps describes what a package needs to be rebuilt reproducibly.
// It's the same information stored in file Godeps.
type Godeps struct {
	ImportPath string
	GoVersion  string
	Packages   []string `json:",omitempty"` // Arguments to save, if any.
	Deps       []Dependency

	outerRoot string
}

// A Dependency is a specific revision of a package.
type Dependency struct {
	ImportPath string
	Comment    string `json:",omitempty"` // Description of commit, if present.
	Rev        string // VCS-specific commit ID.

	// used by command save
	pkg *Package

	// used by command go
	outerRoot string // dir, if present, in outer GOPATH
	repoRoot  *vcs.RepoRoot
	vcs       *VCS
}

// pkgs is the list of packages to read dependencies
func (g *Godeps) Load(pkgs []*Package) error {
	var err1 error
	var path, seen []string
	for _, p := range pkgs {
		if p.Standard {
			log.Println("ignoring stdlib package:", p.ImportPath)
			continue
		}
		if p.Error.Err != "" {
			log.Println(p.Error.Err)
			err1 = errors.New("error loading packages")
			continue
		}
		seen = append(seen, p.ImportPath)
		path = append(path, p.Deps...)
	}
	var testImports []string
	for _, p := range pkgs {
		testImports = append(testImports, p.TestImports...)
	}
	for _, p := range MustLoadPackages(testImports...) {
		if p.Standard {
			continue
		}
		if p.Error.Err != "" {
			log.Println(p.Error.Err)
			err1 = errors.New("error loading packages")
			continue
		}
		path = append(path, p.ImportPath)
		path = append(path, p.Deps...)
	}
	sort.Strings(path)
	path = uniq(path)
	for _, pkg := range MustLoadPackages(path...) {
		if pkg.Error.Err != "" {
			log.Println(pkg.Error.Err)
			err1 = errors.New("error loading dependencies")
			continue
		}
		if pkg.Standard {
			continue
		}
		vcs, rr, err := VCSForImportPath(pkg.ImportPath)
		if err != nil {
			log.Println(err)
			err1 = errors.New("error loading dependencies")
			continue
		}
		if contains(seen, rr.Root) {
			continue
		}
		seen = append(seen, rr.Root)
		id, err := vcs.identify(pkg.Dir)
		if err != nil {
			log.Println(err)
			err1 = errors.New("error loading dependencies")
			continue
		}
		if vcs.isDirty(pkg.Dir, id) {
			log.Println("dirty working tree:", pkg.Dir)
			err1 = errors.New("error loading dependencies")
			continue
		}
		comment := vcs.describe(pkg.Dir, id)
		g.Deps = append(g.Deps, Dependency{
			ImportPath: pkg.ImportPath,
			Rev:        id,
			Comment:    comment,
			pkg:        pkg,
			vcs:        vcs,
		})
	}
	return err1
}

func ReadGodeps(path string) (*Godeps, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	g := new(Godeps)
	err = json.NewDecoder(f).Decode(g)
	if err != nil {
		return nil, err
	}
	err = g.loadGoList()
	if err != nil {
		return nil, err
	}

	for i := range g.Deps {
		d := &g.Deps[i]
		d.vcs, d.repoRoot, err = VCSForImportPath(d.ImportPath)
		if err != nil {
			return nil, err
		}
	}
	return g, nil
}

func (g *Godeps) loadGoList() error {
	a := []string{g.ImportPath}
	for _, d := range g.Deps {
		a = append(a, d.ImportPath)
	}
	ps, err := LoadPackages(a...)
	if err != nil {
		return err
	}
	g.outerRoot = ps[0].Root
	for i, p := range ps[1:] {
		g.Deps[i].outerRoot = p.Root
	}
	return nil
}

func (g *Godeps) WriteTo(w io.Writer) (int64, error) {
	b, err := json.MarshalIndent(g, "", "\t")
	if err != nil {
		return 0, err
	}
	n, err := w.Write(append(b, '\n'))
	return int64(n), err
}

// Returns a path to the local copy of d's repository.
// E.g.
//
//   ImportPath             RepoPath
//   github.com/kr/s3       $spool/github.com/kr/s3
//   github.com/lib/pq/oid  $spool/github.com/lib/pq
func (d Dependency) RepoPath() string {
	return filepath.Join(spool, "repo", d.repoRoot.Root)
}

// Returns a URL for the remote copy of the repository.
func (d Dependency) RemoteURL() string {
	return d.repoRoot.Repo
}

// Returns the url of a local disk clone of the repo, if any.
func (d Dependency) FastRemotePath() string {
	if d.outerRoot != "" {
		return d.outerRoot + "/src/" + d.repoRoot.Root
	}
	return ""
}

// Returns a path to the checked-out copy of d's commit.
func (d Dependency) Workdir() string {
	return filepath.Join(d.Gopath(), "src", d.ImportPath)
}

// Returns a path to the checked-out copy of d's repo root.
func (d Dependency) WorkdirRoot() string {
	return filepath.Join(d.Gopath(), "src", d.repoRoot.Root)
}

// Returns a path to a parent of Workdir such that using
// Gopath in GOPATH makes d available to the go tool.
func (d Dependency) Gopath() string {
	return filepath.Join(spool, "rev", d.Rev[:2], d.Rev[2:])
}

// Creates an empty repo in d.RepoPath().
func (d Dependency) CreateRepo(fastRemote, mainRemote string) error {
	if err := os.MkdirAll(d.RepoPath(), 0777); err != nil {
		return err
	}
	if err := d.vcs.create(d.RepoPath()); err != nil {
		return err
	}
	if err := d.link(fastRemote, d.FastRemotePath()); err != nil {
		return err
	}
	return d.link(mainRemote, d.RemoteURL())
}

func (d Dependency) link(remote, url string) error {
	return d.vcs.link(d.RepoPath(), remote, url)
}

func (d Dependency) fetchAndCheckout(remote string) error {
	if err := d.fetch(remote); err != nil {
		return fmt.Errorf("fetch: %s", err)
	}
	if err := d.checkout(); err != nil {
		return fmt.Errorf("checkout: %s", err)
	}
	return nil
}

func (d Dependency) fetch(remote string) error {
	return d.vcs.fetch(d.RepoPath(), remote)
}

func (d Dependency) checkout() error {
	dir := d.WorkdirRoot()
	if exists(dir) {
		return nil
	}
	if !d.vcs.exists(d.RepoPath(), d.Rev) {
		return fmt.Errorf("unknown rev %s for %s", d.Rev, d.ImportPath)
	}
	if err := os.MkdirAll(dir, 0777); err != nil {
		return err
	}
	return d.vcs.checkout(dir, d.Rev, d.RepoPath())
}

func contains(a []string, s string) bool {
	for _, p := range a {
		if s == p {
			return true
		}
	}
	return false
}

func uniq(a []string) []string {
	i := 0
	s := ""
	for _, t := range a {
		if t != s {
			a[i] = t
			i++
			s = t
		}
	}
	return a[:i]
}

// mustGoVersion returns the version string of the Go compiler
// currently installed, e.g. "go1.1rc3".
func mustGoVersion() string {
	// Godep might have been compiled with a different
	// version, so we can't just use runtime.Version here.
	cmd := exec.Command("go", "version")
	cmd.Stderr = os.Stderr
	out, err := cmd.Output()
	if err != nil {
		log.Fatal(err)
	}
	s := strings.TrimSpace(string(out))
	s = strings.TrimSuffix(s, " "+runtime.GOOS+"/"+runtime.GOARCH)
	s = strings.TrimPrefix(s, "go version ")
	return s
}
<|endoftext|>


<commit_before>// Copyright 2016 Mender Software AS
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
package main

import "time"
import "github.com/mendersoftware/log"

var daemonQuit = make(chan bool)

const (
	// pull data from server every 5 minutes by default
	defaultServerPullInterval = time.Duration(5) * time.Minute
	defaultServerAddress      = "127.0.0.1"
	defaultDeviceId           = "1234:5678:90ab:cdef"
)

type daemonConfigType struct {
	serverPullInterval time.Duration
	server             string
	deviceId           string
}

func (config *daemonConfigType) setPullInterval(interval time.Duration) {
	config.serverPullInterval = interval
}

func (config *daemonConfigType) setServerAddress(server string) {
	//TODO: check if starts with https://
	config.server = server
}

func (config *daemonConfigType) setDeviceId() {
	//TODO: get it from somewhere
	config.deviceId = defaultDeviceId
}

func runAsDemon(config daemonConfigType, client *Client) error {
	// create channels for timer and stopping daemon
	ticker := time.NewTicker(config.serverPullInterval)

	for {
		select {
		case <-ticker.C:
			// do job here
			log.Debug("Timer expired. Pulling server to check update.")
			err, response := client.sendRequest(GET, config.server+"/"+config.deviceId+"/update")
			if err != nil {
				log.Error(err)
				continue
			}
			client.parseUpdateTesponse(response)
			//quit <- true
		case <-daemonQuit:
			log.Debug("Attempting to stop daemon.")
			// exit daemon
			ticker.Stop()
			return nil
		}
	}

	return nil
}
<commit_msg>Fix compilation error and remove unreachable code.<commit_after>// Copyright 2016 Mender Software AS
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
package main

import "time"
import "github.com/mendersoftware/log"

var daemonQuit = make(chan bool)

const (
	// pull data from server every 5 minutes by default
	defaultServerPullInterval = time.Duration(5) * time.Minute
	defaultServerAddress      = "127.0.0.1"
	defaultDeviceId           = "1234:5678:90ab:cdef"
)

type daemonConfigType struct {
	serverPullInterval time.Duration
	server             string
	deviceId           string
}

func (config *daemonConfigType) setPullInterval(interval time.Duration) {
	config.serverPullInterval = interval
}

func (config *daemonConfigType) setServerAddress(server string) {
	//TODO: check if starts with https://
	config.server = server
}

func (config *daemonConfigType) setDeviceId() {
	//TODO: get it from somewhere
	config.deviceId = defaultDeviceId
}

func runAsDemon(config daemonConfigType, client *Client) error {
	// create channels for timer and stopping daemon
	ticker := time.NewTicker(config.serverPullInterval)

	for {
		select {
		case <-ticker.C:
			// do job here
			log.Debug("Timer expired. Pulling server to check update.")
			err, response := client.sendRequest(GET, config.server+"/"+config.deviceId+"/update")
			if err != nil {
				log.Error(err)
				continue
			}
			client.parseUpdateTesponse(response)
			//quit <- true
		case <-daemonQuit:
			log.Debug("Attempting to stop daemon.")
			// exit daemon
			ticker.Stop()
			return nil
		}
	}
}
<|endoftext|>