<commit_before>package game

import (
	"fmt"
	"io"
	"mud/database"
	"mud/engine"
	"mud/utils"
	"net"
	"strings"
)

func getToggleExitMenu(room database.Room) utils.Menu {

	onOrOff := func(direction database.ExitDirection) string {

		if room.HasExit(direction) {
			return "On"
		}

		return "Off"
	}

	menu := utils.NewMenu("Edit Exits")

	menu.AddAction("n", "[N]orth: "+onOrOff(database.DirectionNorth))
	menu.AddAction("e", "[E]ast: "+onOrOff(database.DirectionEast))
	menu.AddAction("s", "[S]outh: "+onOrOff(database.DirectionSouth))
	menu.AddAction("w", "[W]est: "+onOrOff(database.DirectionWest))
	menu.AddAction("u", "[U]p: "+onOrOff(database.DirectionUp))
	menu.AddAction("d", "[D]own: "+onOrOff(database.DirectionDown))

	return menu
}

func Exec(conn net.Conn, character database.Character) {

	room := engine.GetCharacterRoom(character)

	printString := func(data string) {
		io.WriteString(conn, data)
	}

	printLine := func(line string) {
		utils.WriteLine(conn, line)
	}

	printRoom := func() {
		printLine(room.ToString(database.ReadMode))
	}

	printRoomEditor := func() {
		printLine(room.ToString(database.EditMode))
	}

	prompt := func() string {
		return "> "
	}

	processAction := func(input string) {
		inputFields := strings.Fields(input)
		fieldCount := len(inputFields)
		action := inputFields[0]

		switch action {
		case "l":
			fallthrough
		case "look":
			if fieldCount == 1 {
				printRoom()
			} else if fieldCount == 2 {
				arg := database.StringToDirection(inputFields[1])

				if arg == database.DirectionNone {
					printLine("Nothing to see")
				} else {
					loc := room.Location.Next(arg)
					roomToSee, found := engine.GetRoomByLocation(loc)
					if found {
						printLine(roomToSee.ToString(database.ReadMode))
					} else {
						printLine("Nothing to see")
					}
				}
			}

		case "i":
			printLine("You aren't carrying anything")

		case "":
			fallthrough
		case "logout":
			return

		case "quit":
			fallthrough
		case "exit":
			printLine("Take luck!")
			conn.Close()
			panic("User quit")

		default:
			direction := database.StringToDirection(input)

			if direction != database.DirectionNone {
				if room.HasExit(direction) {
					var newRoom database.Room
					var err error
					character, newRoom, err = engine.MoveCharacter(character, direction)
					if err == nil {
						room = newRoom
						printRoom()
					} else {
						printLine(err.Error())
					}

				} else {
					printLine("You can't go that way")
				}
			} else {
				printLine("You can't do that")
			}
		}
	}

	processCommand := func(command string) {
		switch command {
		case "?":
			fallthrough
		case "help":
		case "dig":
		case "edit":
			printRoomEditor()

			for {
				input := utils.GetUserInput(conn, "Select a section to edit> ")

				switch input {
				case "":
					printRoom()
					return

				case "1":
					input = utils.GetRawUserInput(conn, "Enter new title: ")

					if input != "" {
						room.Title = input
						engine.UpdateRoom(room)
					}
					printRoomEditor()

				case "2":
					input = utils.GetRawUserInput(conn, "Enter new description: ")

					if input != "" {
						room.Description = input
						engine.UpdateRoom(room)
					}
					printRoomEditor()

				case "3":
					for {
						menu := getToggleExitMenu(room)
						choice, _ := menu.Exec(conn)

						toggleExit := func(direction database.ExitDirection) {
							enable := !room.HasExit(direction)
							room.SetExitEnabled(direction, enable)
							engine.UpdateRoom(room)
						}

						if choice == "" {
							break
						} else {
							direction := database.StringToDirection(choice)
							if direction != database.DirectionNone {
								toggleExit(direction)
							}
						}
					}

					printRoomEditor()

				default:
					printLine("Invalid selection")
				}
			}

		case "rebuild":
			input := utils.GetUserInput(conn, "Are you sure (delete all rooms and starts from scratch)? ")
			if input[0] == 'y' || input == "yes" {
				engine.GenerateDefaultMap()
			}

			room = engine.GetCharacterRoom(character)
			printRoom()

		case "loc":
			fallthrough
		case "location":
			printLine(fmt.Sprintf("%v", room.Location))

		case "map":
			width := 20 // Should be even

			startX := room.Location.X - (width / 2)
			startY := room.Location.Y - (width / 2)
			endX := startX + width
			endY := startY + width

			z := room.Location.Z

			for y := startY; y <= endY; y += 1 {
				printString("\n")
				for x := startX; x <= endX; x += 1 {
					currentRoom, found := engine.GetRoomByLocation(database.Coordinate{x, y, z})
					if found {
						if currentRoom == room {
							printString("*")
						} else {
							printString("#")
						}
					} else {
						printString(" ")
					}
				}
			}
			printString("\n")

		default:
			printLine("Unrecognized command")
		}
	}

	processEvent := func(event engine.Event) {
		message := ""

		switch event.Type() {
		case engine.MessageEventType:
			message = "Message: " + event.ToString()
		case engine.EnterEventType:
			enterEvent := event.(engine.EnterEvent)
			if enterEvent.RoomId == room.Id && enterEvent.Character != character {
				message = event.ToString()
			}
		case engine.LeaveEventType:
			moveEvent := event.(engine.LeaveEvent)
			if moveEvent.RoomId == room.Id && moveEvent.Character != character {
				message = event.ToString()
			}
		}

		if message != "" {
			printLine("\n" + message)
			printString(prompt())
		}
	}

	printLine("Welcome, " + utils.FormatName(character.Name))
	printRoom()

	userInputChannel := make(chan string)
	sync := make(chan bool)

	go func() {
		// TODO: Don't crash (due to panic) when client disconnects at the prompt
		for {
			input := utils.GetUserInput(conn, prompt())
			userInputChannel <- input
			<-sync
		}
	}()

	eventChannel := engine.Register()
	defer engine.Unregister(eventChannel)

	// Main loop
	for {
		select {
		case input := <-userInputChannel:
			if strings.HasPrefix(input, "/") {
				processCommand(input[1:len(input)])
			} else {
				processAction(input)
			}
			sync <- true
		case event := <-*eventChannel:
			processEvent(event)
		}
	}
}

// vim: nocindent
<commit_msg>Handle the user X'ing from in-game properly<commit_after>package game

import (
	"fmt"
	"io"
	"mud/database"
	"mud/engine"
	"mud/utils"
	"net"
	"strings"
)

func getToggleExitMenu(room database.Room) utils.Menu {

	onOrOff := func(direction database.ExitDirection) string {

		if room.HasExit(direction) {
			return "On"
		}

		return "Off"
	}

	menu := utils.NewMenu("Edit Exits")

	menu.AddAction("n", "[N]orth: "+onOrOff(database.DirectionNorth))
	menu.AddAction("e", "[E]ast: "+onOrOff(database.DirectionEast))
	menu.AddAction("s", "[S]outh: "+onOrOff(database.DirectionSouth))
	menu.AddAction("w", "[W]est: "+onOrOff(database.DirectionWest))
	menu.AddAction("u", "[U]p: "+onOrOff(database.DirectionUp))
	menu.AddAction("d", "[D]own: "+onOrOff(database.DirectionDown))

	return menu
}

func Exec(conn net.Conn, character database.Character) {

	room := engine.GetCharacterRoom(character)

	printString := func(data string) {
		io.WriteString(conn, data)
	}

	printLine := func(line string) {
		utils.WriteLine(conn, line)
	}

	printRoom := func() {
		printLine(room.ToString(database.ReadMode))
	}

	printRoomEditor := func() {
		printLine(room.ToString(database.EditMode))
	}

	prompt := func() string {
		return "> "
	}

	processAction := func(input string) {
		inputFields := strings.Fields(input)
		fieldCount := len(inputFields)
		action := inputFields[0]

		switch action {
		case "l":
			fallthrough
		case "look":
			if fieldCount == 1 {
				printRoom()
			} else if fieldCount == 2 {
				arg := database.StringToDirection(inputFields[1])

				if arg == database.DirectionNone {
					printLine("Nothing to see")
				} else {
					loc := room.Location.Next(arg)
					roomToSee, found := engine.GetRoomByLocation(loc)
					if found {
						printLine(roomToSee.ToString(database.ReadMode))
					} else {
						printLine("Nothing to see")
					}
				}
			}

		case "i":
			printLine("You aren't carrying anything")

		case "":
			fallthrough
		case "logout":
			return

		case "quit":
			fallthrough
		case "exit":
			printLine("Take luck!")
			conn.Close()
			panic("User quit")

		default:
			direction := database.StringToDirection(input)

			if direction != database.DirectionNone {
				if room.HasExit(direction) {
					var newRoom database.Room
					var err error
					character, newRoom, err = engine.MoveCharacter(character, direction)
					if err == nil {
						room = newRoom
						printRoom()
					} else {
						printLine(err.Error())
					}

				} else {
					printLine("You can't go that way")
				}
			} else {
				printLine("You can't do that")
			}
		}
	}

	processCommand := func(command string) {
		switch command {
		case "?":
			fallthrough
		case "help":
		case "dig":
		case "edit":
			printRoomEditor()

			for {
				input := utils.GetUserInput(conn, "Select a section to edit> ")

				switch input {
				case "":
					printRoom()
					return

				case "1":
					input = utils.GetRawUserInput(conn, "Enter new title: ")

					if input != "" {
						room.Title = input
						engine.UpdateRoom(room)
					}
					printRoomEditor()

				case "2":
					input = utils.GetRawUserInput(conn, "Enter new description: ")

					if input != "" {
						room.Description = input
						engine.UpdateRoom(room)
					}
					printRoomEditor()

				case "3":
					for {
						menu := getToggleExitMenu(room)
						choice, _ := menu.Exec(conn)

						toggleExit := func(direction database.ExitDirection) {
							enable := !room.HasExit(direction)
							room.SetExitEnabled(direction, enable)
							engine.UpdateRoom(room)
						}

						if choice == "" {
							break
						} else {
							direction := database.StringToDirection(choice)
							if direction != database.DirectionNone {
								toggleExit(direction)
							}
						}
					}

					printRoomEditor()

				default:
					printLine("Invalid selection")
				}
			}

		case "rebuild":
			input := utils.GetUserInput(conn, "Are you sure (delete all rooms and starts from scratch)? ")
			if input[0] == 'y' || input == "yes" {
				engine.GenerateDefaultMap()
			}

			room = engine.GetCharacterRoom(character)
			printRoom()

		case "loc":
			fallthrough
		case "location":
			printLine(fmt.Sprintf("%v", room.Location))

		case "map":
			width := 20 // Should be even

			startX := room.Location.X - (width / 2)
			startY := room.Location.Y - (width / 2)
			endX := startX + width
			endY := startY + width

			z := room.Location.Z

			for y := startY; y <= endY; y += 1 {
				printString("\n")
				for x := startX; x <= endX; x += 1 {
					currentRoom, found := engine.GetRoomByLocation(database.Coordinate{x, y, z})
					if found {
						if currentRoom == room {
							printString("*")
						} else {
							printString("#")
						}
					} else {
						printString(" ")
					}
				}
			}
			printString("\n")

		default:
			printLine("Unrecognized command")
		}
	}

	processEvent := func(event engine.Event) {
		message := ""

		switch event.Type() {
		case engine.MessageEventType:
			message = "Message: " + event.ToString()
		case engine.EnterEventType:
			enterEvent := event.(engine.EnterEvent)
			if enterEvent.RoomId == room.Id && enterEvent.Character != character {
				message = event.ToString()
			}
		case engine.LeaveEventType:
			moveEvent := event.(engine.LeaveEvent)
			if moveEvent.RoomId == room.Id && moveEvent.Character != character {
				message = event.ToString()
			}
		}

		if message != "" {
			printLine("\n" + message)
			printString(prompt())
		}
	}

	printLine("Welcome, " + utils.FormatName(character.Name))
	printRoom()

	userInputChannel := make(chan string)
	sync := make(chan bool)

	go func() {
		// TODO: Don't crash (due to panic) when client disconnects at the prompt
		for {
			input := utils.GetUserInput(conn, prompt())
			userInputChannel <- input
			<-sync
		}
	}()

	eventChannel := engine.Register()
	defer engine.Unregister(eventChannel)

	// Main loop
	for {
		select {
		case input := <-userInputChannel:
			if input == "" {
				return
			}
			if strings.HasPrefix(input, "/") {
				processCommand(input[1:len(input)])
			} else {
				processAction(input)
			}
			sync <- true
		case event := <-*eventChannel:
			processEvent(event)
		}
	}
}

// vim: nocindent
<|endoftext|>


<commit_before>package templatepipe

import (
	"strings"
	"testing"
)

func TestDotted(t *testing.T) {
	abc := "a.b.c"
	words := strings.Split(abc, ".")
	d := dotted{}
	d.append(words, nil)
	d.append(strings.Split(abc+".1", "."), nil)
	d.append(strings.Split(abc+".2", "."), nil)
	if x := d.find(append(words, "z")); x != nil {
		t.Errorf("find returned non-nil")
	}
	if x := d.find(words); x == nil {
		t.Errorf("find returned nil")
	} else if _, _, s := x.notation(); s != abc {
		t.Errorf("notation: %q (expected %q)", s, abc)
	}
}

func TestEncurl(t *testing.T) {
	words := strings.Split("a.b.c", ".")
	d := dotted{}
	d.append(words, nil)
	l1 := d.find(words)
	if l1 == nil {
		t.Errorf("dotted.find returned nil")
	}
	l1.ranged = true
	l1.keys = []string{"z"}
	l1.decl = "DECL"

	w2 := strings.Split("a.b.z", ".")
	d.append(w2, nil)
	if l2 := d.find(w2); l2 != nil {
		l2.ranged = true
	}

	v := encurl(curlyX, d, -1)
	c := v.(Nota)["a"].(Nota)["b"].(Nota)["c"].([]map[string]Nota)[0]
	if expected := "{DECL.z}"; c["z"].String() != expected {
		t.Errorf("encurl result mismatch: %q (expected %q)", c["z"], expected)
	}
	if z := v.(Nota)["a"].(Nota)["b"].(Nota)["z"].([]string); len(z) != 0 {
		t.Errorf("z is expected to be empty: %+v\n", z)
	}
}
<commit_msg>test fix<commit_after>package templatepipe

import (
	"strings"
	"testing"
)

func TestDotted(t *testing.T) {
	abc := "a.b.c"
	words := strings.Split(abc, ".")
	d := dotted{}
	d.append(words, nil)
	d.append(strings.Split(abc+".1", "."), nil)
	d.append(strings.Split(abc+".2", "."), nil)
	if x := d.find(append(words, "z")); x != nil {
		t.Errorf("find returned non-nil")
	}
	if x := d.find(words); x == nil {
		t.Errorf("find returned nil")
	} else if _, _, s := x.notation(); s != abc {
		t.Errorf("notation: %q (expected %q)", s, abc)
	}
}

func TestEncurl(t *testing.T) {
	words := strings.Split("a.b.c", ".")
	d := dotted{}
	d.append(words, nil)
	l1 := d.find(words)
	if l1 == nil {
		t.Errorf("dotted.find returned nil")
	}
	l1.ranged = true
	l1.keys = []string{"z"}
	l1.decl = "DECL"

	w2 := strings.Split("a.b.z", ".")
	d.append(w2, nil)
	if l2 := d.find(w2); l2 != nil {
		l2.ranged = true
	}

	v := encurl(curly, d, -1)
	c := v.(Nota)["a"].(Nota)["b"].(Nota)["c"].([]map[string]Nota)[0]
	if expected := "{DECL.z}"; c["z"].String() != expected {
		t.Errorf("encurl result mismatch: %q (expected %q)", c["z"], expected)
	}
	if z := v.(Nota)["a"].(Nota)["b"].(Nota)["z"].([]string); len(z) != 0 {
		t.Errorf("z is expected to be empty: %+v\n", z)
	}
}
<|endoftext|>


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

// Helper code for daemonizing gcsfuse, synchronizing on successful mount.
//
// The details of this package are subject to change.
package daemon

import (
	"errors"
	"io"
)

// For use by gcsfuse: signal that mounting was successful (allowing the caller
// of the process to return in success) or that there was a failure to mount
// the file system (allowing the caller of the process to display an
// appropriate error message).
//
// Do nothing if the process wasn't invoked with Mount.
func SignalOutcome(outcome error) (err error) {
	err = errors.New("TODO")
	return
}

// For use by gcsfuse: return a writer that should be used for logging status
// messages while in the process of mounting.
//
// The returned writer must not be written to after calling SignalOutcome.
func StatusWriter() (w io.Writer) {
	panic("TODO")
}

// Invoke gcsfuse with the supplied arguments, waiting until it successfully
// mounts or reports that is has failed. Write status updates while mounting
// into the supplied writer (which may be nil for silence). Return nil only if
// it mounts successfully.
func Mount(
	gcsfusePath string,
	args []string,
	status io.Writer) (err error) {
	err = errors.New("TODO")
	return
}
<commit_msg>Sort of defined the package daemon protocol.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Helper code for daemonizing gcsfuse, synchronizing on successful mount.
//
// The details of this package are subject to change.
package daemon

import (
	"errors"
	"io"
)

// The name of an environment variable used to communicate a file descriptor
// set up by Mount to the gcsfuse subprocess. Gob encoding is used to
// communicate back to Mount.
const envVar = "MOUNT_STATUS_FD"

// A message containing logging output for the process of mounting the file
// system.
type logMsg struct {
	Msg []byte
}

// A message indicating the outcome of the process of mounting the file system.
// The receiver ignores further messages.
type outcomeMsg struct {
	Succesful bool

	// Meaningful only if !Succesful.
	ErrorMsg string
}

// For use by gcsfuse: signal that mounting was successful (allowing the caller
// of the process to return in success) or that there was a failure to mount
// the file system (allowing the caller of the process to display an
// appropriate error message).
//
// Do nothing if the process wasn't invoked with Mount.
func SignalOutcome(outcome error) (err error) {
	err = errors.New("TODO")
	return
}

// For use by gcsfuse: return a writer that should be used for logging status
// messages while in the process of mounting.
//
// The returned writer must not be written to after calling SignalOutcome.
func StatusWriter() (w io.Writer) {
	panic("TODO")
}

// Invoke gcsfuse with the supplied arguments, waiting until it successfully
// mounts or reports that is has failed. Write status updates while mounting
// into the supplied writer (which may be nil for silence). Return nil only if
// it mounts successfully.
func Mount(
	gcsfusePath string,
	args []string,
	status io.Writer) (err error) {
	err = errors.New("TODO")
	return
}
<|endoftext|>


<commit_before>// Copyright 2017 CoreOS, Inc.
//
// 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 distro

import (
	"fmt"
	"os"
)

// Distro-specific settings that can be overridden at link time with e.g.
// -X github.com/coreos/ignition/v2/internal/distro.mdadmCmd=/opt/bin/mdadm
var (
	// Device node directories and paths
	diskByIDDir       = "/dev/disk/by-id"
	diskByLabelDir    = "/dev/disk/by-label"
	diskByPartUUIDDir = "/dev/disk/by-partuuid"

	// File paths
	kernelCmdlinePath = "/proc/cmdline"
	// initramfs directory containing distro-provided base config
	systemConfigDir = "/usr/lib/ignition"

	// Helper programs
	groupaddCmd = "groupadd"
	groupdelCmd = "groupdel"
	mdadmCmd    = "mdadm"
	mountCmd    = "mount"
	sgdiskCmd   = "sgdisk"
	modprobeCmd = "modprobe"
	udevadmCmd  = "udevadm"
	usermodCmd  = "usermod"
	useraddCmd  = "useradd"
	userdelCmd  = "userdel"
	setfilesCmd = "setfiles"
	wipefsCmd   = "wipefs"

	// Filesystem tools
	btrfsMkfsCmd = "mkfs.btrfs"
	ext4MkfsCmd  = "mkfs.ext4"
	swapMkfsCmd  = "mkswap"
	vfatMkfsCmd  = "mkfs.vfat"
	xfsMkfsCmd   = "mkfs.xfs"

	//zVM programs
	vmurCmd      = "vmur"
	chccwdevCmd  = "chccwdev"
	cioIgnoreCmd = "cio_ignore"

	// LUKS programs
	clevisCmd     = "clevis"
	cryptsetupCmd = "cryptsetup"

	// kargs programs
	kargsCmd = "ignition-kargs-helper"

	// Flags
	selinuxRelabel  = "true"
	blackboxTesting = "false"
	// writeAuthorizedKeysFragment indicates whether to write SSH keys
	// specified in the Ignition config as a fragment to
	// ".ssh/authorized_keys.d/ignition" ("true"), or to
	// ".ssh/authorized_keys" ("false").
	writeAuthorizedKeysFragment = "true"

	luksInitramfsKeyFilePath = "/run/ignition/luks-keyfiles/"
	luksRealRootKeyFilePath  = "/etc/luks/"
)

func DiskByIDDir() string       { return diskByIDDir }
func DiskByLabelDir() string    { return diskByLabelDir }
func DiskByPartUUIDDir() string { return diskByPartUUIDDir }

func KernelCmdlinePath() string { return kernelCmdlinePath }
func SystemConfigDir() string   { return fromEnv("SYSTEM_CONFIG_DIR", systemConfigDir) }

func GroupaddCmd() string { return groupaddCmd }
func GroupdelCmd() string { return groupdelCmd }
func MdadmCmd() string    { return mdadmCmd }
func MountCmd() string    { return mountCmd }
func SgdiskCmd() string   { return sgdiskCmd }
func ModprobeCmd() string { return modprobeCmd }
func UdevadmCmd() string  { return udevadmCmd }
func UsermodCmd() string  { return usermodCmd }
func UseraddCmd() string  { return useraddCmd }
func UserdelCmd() string  { return userdelCmd }
func SetfilesCmd() string { return setfilesCmd }
func WipefsCmd() string   { return wipefsCmd }

func BtrfsMkfsCmd() string { return btrfsMkfsCmd }
func Ext4MkfsCmd() string  { return ext4MkfsCmd }
func SwapMkfsCmd() string  { return swapMkfsCmd }
func VfatMkfsCmd() string  { return vfatMkfsCmd }
func XfsMkfsCmd() string   { return xfsMkfsCmd }

func VmurCmd() string      { return vmurCmd }
func ChccwdevCmd() string  { return chccwdevCmd }
func CioIgnoreCmd() string { return cioIgnoreCmd }

func ClevisCmd() string     { return clevisCmd }
func CryptsetupCmd() string { return cryptsetupCmd }

func KargsCmd() string { return kargsCmd }

func LuksInitramfsKeyFilePath() string { return luksInitramfsKeyFilePath }
func LuksRealRootKeyFilePath() string  { return luksRealRootKeyFilePath }

func SelinuxRelabel() bool  { return bakedStringToBool(selinuxRelabel) && !BlackboxTesting() }
func BlackboxTesting() bool { return bakedStringToBool(blackboxTesting) }
func WriteAuthorizedKeysFragment() bool {
	return bakedStringToBool(fromEnv("WRITE_AUTHORIZED_KEYS_FRAGMENT", writeAuthorizedKeysFragment))
}

func fromEnv(nameSuffix, defaultValue string) string {
	value := os.Getenv("IGNITION_" + nameSuffix)
	if value != "" {
		return value
	}
	return defaultValue
}

func bakedStringToBool(s string) bool {
	// the linker only supports string args, so do some basic bool sensing
	if s == "true" || s == "1" {
		return true
	} else if s == "false" || s == "0" {
		return false
	} else {
		// if we got a bad compile flag, just crash and burn rather than assume
		panic(fmt.Sprintf("value '%s' cannot be interpreted as a boolean", s))
	}
}
<commit_msg>internal/distro: drop DiskByIDDir<commit_after>// Copyright 2017 CoreOS, Inc.
//
// 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 distro

import (
	"fmt"
	"os"
)

// Distro-specific settings that can be overridden at link time with e.g.
// -X github.com/coreos/ignition/v2/internal/distro.mdadmCmd=/opt/bin/mdadm
var (
	// Device node directories and paths
	diskByLabelDir    = "/dev/disk/by-label"
	diskByPartUUIDDir = "/dev/disk/by-partuuid"

	// File paths
	kernelCmdlinePath = "/proc/cmdline"
	// initramfs directory containing distro-provided base config
	systemConfigDir = "/usr/lib/ignition"

	// Helper programs
	groupaddCmd = "groupadd"
	groupdelCmd = "groupdel"
	mdadmCmd    = "mdadm"
	mountCmd    = "mount"
	sgdiskCmd   = "sgdisk"
	modprobeCmd = "modprobe"
	udevadmCmd  = "udevadm"
	usermodCmd  = "usermod"
	useraddCmd  = "useradd"
	userdelCmd  = "userdel"
	setfilesCmd = "setfiles"
	wipefsCmd   = "wipefs"

	// Filesystem tools
	btrfsMkfsCmd = "mkfs.btrfs"
	ext4MkfsCmd  = "mkfs.ext4"
	swapMkfsCmd  = "mkswap"
	vfatMkfsCmd  = "mkfs.vfat"
	xfsMkfsCmd   = "mkfs.xfs"

	//zVM programs
	vmurCmd      = "vmur"
	chccwdevCmd  = "chccwdev"
	cioIgnoreCmd = "cio_ignore"

	// LUKS programs
	clevisCmd     = "clevis"
	cryptsetupCmd = "cryptsetup"

	// kargs programs
	kargsCmd = "ignition-kargs-helper"

	// Flags
	selinuxRelabel  = "true"
	blackboxTesting = "false"
	// writeAuthorizedKeysFragment indicates whether to write SSH keys
	// specified in the Ignition config as a fragment to
	// ".ssh/authorized_keys.d/ignition" ("true"), or to
	// ".ssh/authorized_keys" ("false").
	writeAuthorizedKeysFragment = "true"

	luksInitramfsKeyFilePath = "/run/ignition/luks-keyfiles/"
	luksRealRootKeyFilePath  = "/etc/luks/"
)

func DiskByLabelDir() string    { return diskByLabelDir }
func DiskByPartUUIDDir() string { return diskByPartUUIDDir }

func KernelCmdlinePath() string { return kernelCmdlinePath }
func SystemConfigDir() string   { return fromEnv("SYSTEM_CONFIG_DIR", systemConfigDir) }

func GroupaddCmd() string { return groupaddCmd }
func GroupdelCmd() string { return groupdelCmd }
func MdadmCmd() string    { return mdadmCmd }
func MountCmd() string    { return mountCmd }
func SgdiskCmd() string   { return sgdiskCmd }
func ModprobeCmd() string { return modprobeCmd }
func UdevadmCmd() string  { return udevadmCmd }
func UsermodCmd() string  { return usermodCmd }
func UseraddCmd() string  { return useraddCmd }
func UserdelCmd() string  { return userdelCmd }
func SetfilesCmd() string { return setfilesCmd }
func WipefsCmd() string   { return wipefsCmd }

func BtrfsMkfsCmd() string { return btrfsMkfsCmd }
func Ext4MkfsCmd() string  { return ext4MkfsCmd }
func SwapMkfsCmd() string  { return swapMkfsCmd }
func VfatMkfsCmd() string  { return vfatMkfsCmd }
func XfsMkfsCmd() string   { return xfsMkfsCmd }

func VmurCmd() string      { return vmurCmd }
func ChccwdevCmd() string  { return chccwdevCmd }
func CioIgnoreCmd() string { return cioIgnoreCmd }

func ClevisCmd() string     { return clevisCmd }
func CryptsetupCmd() string { return cryptsetupCmd }

func KargsCmd() string { return kargsCmd }

func LuksInitramfsKeyFilePath() string { return luksInitramfsKeyFilePath }
func LuksRealRootKeyFilePath() string  { return luksRealRootKeyFilePath }

func SelinuxRelabel() bool  { return bakedStringToBool(selinuxRelabel) && !BlackboxTesting() }
func BlackboxTesting() bool { return bakedStringToBool(blackboxTesting) }
func WriteAuthorizedKeysFragment() bool {
	return bakedStringToBool(fromEnv("WRITE_AUTHORIZED_KEYS_FRAGMENT", writeAuthorizedKeysFragment))
}

func fromEnv(nameSuffix, defaultValue string) string {
	value := os.Getenv("IGNITION_" + nameSuffix)
	if value != "" {
		return value
	}
	return defaultValue
}

func bakedStringToBool(s string) bool {
	// the linker only supports string args, so do some basic bool sensing
	if s == "true" || s == "1" {
		return true
	} else if s == "false" || s == "0" {
		return false
	} else {
		// if we got a bad compile flag, just crash and burn rather than assume
		panic(fmt.Sprintf("value '%s' cannot be interpreted as a boolean", s))
	}
}
<|endoftext|>


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

package logger

import (
	"context"
	"encoding/hex"
	"fmt"
	"go/build"
	"path/filepath"
	"reflect"
	"runtime"
	"strings"
	"time"

	"github.com/minio/highwayhash"
	"github.com/minio/madmin-go"
	"github.com/minio/minio-go/v7/pkg/set"
	xhttp "github.com/minio/minio/internal/http"
	"github.com/minio/minio/internal/logger/message/log"
)

// HighwayHash key for logging in anonymous mode
var magicHighwayHash256Key = []byte("\x4b\xe7\x34\xfa\x8e\x23\x8a\xcd\x26\x3e\x83\xe6\xbb\x96\x85\x52\x04\x0f\x93\x5d\xa3\x9f\x44\x14\x97\xe0\x9d\x13\x22\xde\x36\xa0")

// LogLevel type
type LogLevel int8

// Enumerated level types
const (
	InfoLvl LogLevel = iota + 1
	ErrorLvl
	FatalLvl

	Application = madmin.LogKindApplication
	Minio       = madmin.LogKindMinio
	All         = madmin.LogKindAll
)

// MinimumLogLevel holds the minimum logging level to print - info by default
var MinimumLogLevel = InfoLvl

var trimStrings []string

// TimeFormat - logging time format.
const TimeFormat string = "15:04:05 MST 01/02/2006"

var matchingFuncNames = [...]string{
	"http.HandlerFunc.ServeHTTP",
	"cmd.serverMain",
	// add more here ..
}

func (level LogLevel) String() string {
	switch level {
	case InfoLvl:
		return "INFO"
	case ErrorLvl:
		return "ERROR"
	case FatalLvl:
		return "FATAL"
	}
	return ""
}

// quietFlag: Hide startup messages if enabled
// jsonFlag: Display in JSON format, if enabled
var (
	quietFlag, jsonFlag, anonFlag bool
	// Custom function to format error
	errorFmtFunc func(string, error, bool) string
)

// EnableQuiet - turns quiet option on.
func EnableQuiet() {
	quietFlag = true
}

// EnableJSON - outputs logs in json format.
func EnableJSON() {
	jsonFlag = true
	quietFlag = true
}

// EnableAnonymous - turns anonymous flag
// to avoid printing sensitive information.
func EnableAnonymous() {
	anonFlag = true
}

// IsJSON - returns true if jsonFlag is true
func IsJSON() bool {
	return jsonFlag
}

// IsQuiet - returns true if quietFlag is true
func IsQuiet() bool {
	return quietFlag
}

// RegisterError registers the specified rendering function. This latter
// will be called for a pretty rendering of fatal errors.
func RegisterError(f func(string, error, bool) string) {
	errorFmtFunc = f
}

// Remove any duplicates and return unique entries.
func uniqueEntries(paths []string) []string {
	m := make(set.StringSet)
	for _, p := range paths {
		if !m.Contains(p) {
			m.Add(p)
		}
	}
	return m.ToSlice()
}

// Init sets the trimStrings to possible GOPATHs
// and GOROOT directories. Also append github.com/minio/minio
// This is done to clean up the filename, when stack trace is
// displayed when an error happens.
func Init(goPath string, goRoot string) {
	var goPathList []string
	var goRootList []string
	var defaultgoPathList []string
	var defaultgoRootList []string
	pathSeperator := ":"
	// Add all possible GOPATH paths into trimStrings
	// Split GOPATH depending on the OS type
	if runtime.GOOS == "windows" {
		pathSeperator = ";"
	}

	goPathList = strings.Split(goPath, pathSeperator)
	goRootList = strings.Split(goRoot, pathSeperator)
	defaultgoPathList = strings.Split(build.Default.GOPATH, pathSeperator)
	defaultgoRootList = strings.Split(build.Default.GOROOT, pathSeperator)

	// Add trim string "{GOROOT}/src/" into trimStrings
	trimStrings = []string{filepath.Join(runtime.GOROOT(), "src") + string(filepath.Separator)}

	// Add all possible path from GOPATH=path1:path2...:pathN
	// as "{path#}/src/" into trimStrings
	for _, goPathString := range goPathList {
		trimStrings = append(trimStrings, filepath.Join(goPathString, "src")+string(filepath.Separator))
	}

	for _, goRootString := range goRootList {
		trimStrings = append(trimStrings, filepath.Join(goRootString, "src")+string(filepath.Separator))
	}

	for _, defaultgoPathString := range defaultgoPathList {
		trimStrings = append(trimStrings, filepath.Join(defaultgoPathString, "src")+string(filepath.Separator))
	}

	for _, defaultgoRootString := range defaultgoRootList {
		trimStrings = append(trimStrings, filepath.Join(defaultgoRootString, "src")+string(filepath.Separator))
	}

	// Remove duplicate entries.
	trimStrings = uniqueEntries(trimStrings)

	// Add "github.com/minio/minio" as the last to cover
	// paths like "{GOROOT}/src/github.com/minio/minio"
	// and "{GOPATH}/src/github.com/minio/minio"
	trimStrings = append(trimStrings, filepath.Join("github.com", "minio", "minio")+string(filepath.Separator))
}

func trimTrace(f string) string {
	for _, trimString := range trimStrings {
		f = strings.TrimPrefix(filepath.ToSlash(f), filepath.ToSlash(trimString))
	}
	return filepath.FromSlash(f)
}

func getSource(level int) string {
	pc, file, lineNumber, ok := runtime.Caller(level)
	if ok {
		// Clean up the common prefixes
		file = trimTrace(file)
		_, funcName := filepath.Split(runtime.FuncForPC(pc).Name())
		return fmt.Sprintf("%v:%v:%v()", file, lineNumber, funcName)
	}
	return ""
}

// getTrace method - creates and returns stack trace
func getTrace(traceLevel int) []string {
	var trace []string
	pc, file, lineNumber, ok := runtime.Caller(traceLevel)

	for ok && file != "" {
		// Clean up the common prefixes
		file = trimTrace(file)
		// Get the function name
		_, funcName := filepath.Split(runtime.FuncForPC(pc).Name())
		// Skip duplicate traces that start with file name, "<autogenerated>"
		// and also skip traces with function name that starts with "runtime."
		if !strings.HasPrefix(file, "<autogenerated>") &&
			!strings.HasPrefix(funcName, "runtime.") {
			// Form and append a line of stack trace into a
			// collection, 'trace', to build full stack trace
			trace = append(trace, fmt.Sprintf("%v:%v:%v()", file, lineNumber, funcName))

			// Ignore trace logs beyond the following conditions
			for _, name := range matchingFuncNames {
				if funcName == name {
					return trace
				}
			}
		}
		traceLevel++
		// Read stack trace information from PC
		pc, file, lineNumber, ok = runtime.Caller(traceLevel)
	}
	return trace
}

// Return the highway hash of the passed string
func hashString(input string) string {
	hh, _ := highwayhash.New(magicHighwayHash256Key)
	hh.Write([]byte(input))
	return hex.EncodeToString(hh.Sum(nil))
}

// LogAlwaysIf prints a detailed error message during
// the execution of the server.
func LogAlwaysIf(ctx context.Context, err error, errKind ...interface{}) {
	if err == nil {
		return
	}

	logIf(ctx, err, errKind...)
}

// LogIf prints a detailed error message during
// the execution of the server, if it is not an
// ignored error.
func LogIf(ctx context.Context, err error, errKind ...interface{}) {
	if logIgnoreError(err) {
		return
	}
	logIf(ctx, err, errKind...)
}

func errToEntry(ctx context.Context, err error, errKind ...interface{}) log.Entry {
	logKind := madmin.LogKindAll
	if len(errKind) > 0 {
		if ek, ok := errKind[0].(madmin.LogKind); ok {
			logKind = ek
		}
	}
	req := GetReqInfo(ctx)

	if req == nil {
		req = &ReqInfo{API: "SYSTEM"}
	}
	req.RLock()
	defer req.RUnlock()

	API := "SYSTEM"
	if req.API != "" {
		API = req.API
	}

	kv := req.GetTags()
	tags := make(map[string]interface{}, len(kv))
	for _, entry := range kv {
		tags[entry.Key] = entry.Val
	}

	// Get full stack trace
	trace := getTrace(3)

	// Get the cause for the Error
	message := fmt.Sprintf("%v (%T)", err, err)
	if req.DeploymentID == "" {
		req.DeploymentID = xhttp.GlobalDeploymentID
	}

	objects := make([]log.ObjectVersion, 0, len(req.Objects))
	for _, ov := range req.Objects {
		objects = append(objects, log.ObjectVersion{
			ObjectName: ov.ObjectName,
			VersionID:  ov.VersionID,
		})
	}

	entry := log.Entry{
		DeploymentID: req.DeploymentID,
		Level:        ErrorLvl.String(),
		LogKind:      logKind,
		RemoteHost:   req.RemoteHost,
		Host:         req.Host,
		RequestID:    req.RequestID,
		UserAgent:    req.UserAgent,
		Time:         time.Now().UTC(),
		API: &log.API{
			Name: API,
			Args: &log.Args{
				Bucket:    req.BucketName,
				Object:    req.ObjectName,
				VersionID: req.VersionID,
				Objects:   objects,
			},
		},
		Trace: &log.Trace{
			Message:   message,
			Source:    trace,
			Variables: tags,
		},
	}

	if anonFlag {
		entry.API.Args.Bucket = hashString(entry.API.Args.Bucket)
		entry.API.Args.Object = hashString(entry.API.Args.Object)
		entry.RemoteHost = hashString(entry.RemoteHost)
		entry.Trace.Message = reflect.TypeOf(err).String()
		entry.Trace.Variables = make(map[string]interface{})
	}

	return entry
}

// consoleLogIf prints a detailed error message during
// the execution of the server.
func consoleLogIf(ctx context.Context, err error, errKind ...interface{}) {
	if MinimumLogLevel > ErrorLvl {
		return
	}

	if consoleTgt != nil {
		entry := errToEntry(ctx, err, errKind...)
		consoleTgt.Send(entry)
	}
}

// logIf prints a detailed error message during
// the execution of the server.
func logIf(ctx context.Context, err error, errKind ...interface{}) {
	if MinimumLogLevel > ErrorLvl {
		return
	}

	systemTgts := SystemTargets()
	if len(systemTgts) == 0 {
		return
	}

	entry := errToEntry(ctx, err, errKind...)
	// Iterate over all logger targets to send the log entry
	for _, t := range systemTgts {
		if err := t.Send(entry); err != nil {
			if consoleTgt != nil {
				entry.Trace.Message = fmt.Sprintf("event(%#v) was not sent to Logger target (%#v): %#v", entry, t, err)
				consoleTgt.Send(entry)
			}
		}
	}
}

// ErrCritical is the value panic'd whenever CriticalIf is called.
var ErrCritical struct{}

// CriticalIf logs the provided error on the console. It fails the
// current go-routine by causing a `panic(ErrCritical)`.
func CriticalIf(ctx context.Context, err error, errKind ...interface{}) {
	if err != nil {
		LogIf(ctx, err, errKind...)
		panic(ErrCritical)
	}
}

// FatalIf is similar to Fatal() but it ignores passed nil error
func FatalIf(err error, msg string, data ...interface{}) {
	if err == nil {
		return
	}
	fatal(err, msg, data...)
}
<commit_msg>save deploymentID to avoid mutating request entry in Audit (#16053)<commit_after>// Copyright (c) 2015-2021 MinIO, Inc.
//
// This file is part of MinIO Object Storage stack
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package logger

import (
	"context"
	"encoding/hex"
	"fmt"
	"go/build"
	"path/filepath"
	"reflect"
	"runtime"
	"strings"
	"time"

	"github.com/minio/highwayhash"
	"github.com/minio/madmin-go"
	"github.com/minio/minio-go/v7/pkg/set"
	xhttp "github.com/minio/minio/internal/http"
	"github.com/minio/minio/internal/logger/message/log"
)

// HighwayHash key for logging in anonymous mode
var magicHighwayHash256Key = []byte("\x4b\xe7\x34\xfa\x8e\x23\x8a\xcd\x26\x3e\x83\xe6\xbb\x96\x85\x52\x04\x0f\x93\x5d\xa3\x9f\x44\x14\x97\xe0\x9d\x13\x22\xde\x36\xa0")

// LogLevel type
type LogLevel int8

// Enumerated level types
const (
	InfoLvl LogLevel = iota + 1
	ErrorLvl
	FatalLvl

	Application = madmin.LogKindApplication
	Minio       = madmin.LogKindMinio
	All         = madmin.LogKindAll
)

// MinimumLogLevel holds the minimum logging level to print - info by default
var MinimumLogLevel = InfoLvl

var trimStrings []string

// TimeFormat - logging time format.
const TimeFormat string = "15:04:05 MST 01/02/2006"

var matchingFuncNames = [...]string{
	"http.HandlerFunc.ServeHTTP",
	"cmd.serverMain",
	// add more here ..
}

func (level LogLevel) String() string {
	switch level {
	case InfoLvl:
		return "INFO"
	case ErrorLvl:
		return "ERROR"
	case FatalLvl:
		return "FATAL"
	}
	return ""
}

// quietFlag: Hide startup messages if enabled
// jsonFlag: Display in JSON format, if enabled
var (
	quietFlag, jsonFlag, anonFlag bool
	// Custom function to format error
	errorFmtFunc func(string, error, bool) string
)

// EnableQuiet - turns quiet option on.
func EnableQuiet() {
	quietFlag = true
}

// EnableJSON - outputs logs in json format.
func EnableJSON() {
	jsonFlag = true
	quietFlag = true
}

// EnableAnonymous - turns anonymous flag
// to avoid printing sensitive information.
func EnableAnonymous() {
	anonFlag = true
}

// IsJSON - returns true if jsonFlag is true
func IsJSON() bool {
	return jsonFlag
}

// IsQuiet - returns true if quietFlag is true
func IsQuiet() bool {
	return quietFlag
}

// RegisterError registers the specified rendering function. This latter
// will be called for a pretty rendering of fatal errors.
func RegisterError(f func(string, error, bool) string) {
	errorFmtFunc = f
}

// Remove any duplicates and return unique entries.
func uniqueEntries(paths []string) []string {
	m := make(set.StringSet)
	for _, p := range paths {
		if !m.Contains(p) {
			m.Add(p)
		}
	}
	return m.ToSlice()
}

// Init sets the trimStrings to possible GOPATHs
// and GOROOT directories. Also append github.com/minio/minio
// This is done to clean up the filename, when stack trace is
// displayed when an error happens.
func Init(goPath string, goRoot string) {
	var goPathList []string
	var goRootList []string
	var defaultgoPathList []string
	var defaultgoRootList []string
	pathSeperator := ":"
	// Add all possible GOPATH paths into trimStrings
	// Split GOPATH depending on the OS type
	if runtime.GOOS == "windows" {
		pathSeperator = ";"
	}

	goPathList = strings.Split(goPath, pathSeperator)
	goRootList = strings.Split(goRoot, pathSeperator)
	defaultgoPathList = strings.Split(build.Default.GOPATH, pathSeperator)
	defaultgoRootList = strings.Split(build.Default.GOROOT, pathSeperator)

	// Add trim string "{GOROOT}/src/" into trimStrings
	trimStrings = []string{filepath.Join(runtime.GOROOT(), "src") + string(filepath.Separator)}

	// Add all possible path from GOPATH=path1:path2...:pathN
	// as "{path#}/src/" into trimStrings
	for _, goPathString := range goPathList {
		trimStrings = append(trimStrings, filepath.Join(goPathString, "src")+string(filepath.Separator))
	}

	for _, goRootString := range goRootList {
		trimStrings = append(trimStrings, filepath.Join(goRootString, "src")+string(filepath.Separator))
	}

	for _, defaultgoPathString := range defaultgoPathList {
		trimStrings = append(trimStrings, filepath.Join(defaultgoPathString, "src")+string(filepath.Separator))
	}

	for _, defaultgoRootString := range defaultgoRootList {
		trimStrings = append(trimStrings, filepath.Join(defaultgoRootString, "src")+string(filepath.Separator))
	}

	// Remove duplicate entries.
	trimStrings = uniqueEntries(trimStrings)

	// Add "github.com/minio/minio" as the last to cover
	// paths like "{GOROOT}/src/github.com/minio/minio"
	// and "{GOPATH}/src/github.com/minio/minio"
	trimStrings = append(trimStrings, filepath.Join("github.com", "minio", "minio")+string(filepath.Separator))
}

func trimTrace(f string) string {
	for _, trimString := range trimStrings {
		f = strings.TrimPrefix(filepath.ToSlash(f), filepath.ToSlash(trimString))
	}
	return filepath.FromSlash(f)
}

func getSource(level int) string {
	pc, file, lineNumber, ok := runtime.Caller(level)
	if ok {
		// Clean up the common prefixes
		file = trimTrace(file)
		_, funcName := filepath.Split(runtime.FuncForPC(pc).Name())
		return fmt.Sprintf("%v:%v:%v()", file, lineNumber, funcName)
	}
	return ""
}

// getTrace method - creates and returns stack trace
func getTrace(traceLevel int) []string {
	var trace []string
	pc, file, lineNumber, ok := runtime.Caller(traceLevel)

	for ok && file != "" {
		// Clean up the common prefixes
		file = trimTrace(file)
		// Get the function name
		_, funcName := filepath.Split(runtime.FuncForPC(pc).Name())
		// Skip duplicate traces that start with file name, "<autogenerated>"
		// and also skip traces with function name that starts with "runtime."
		if !strings.HasPrefix(file, "<autogenerated>") &&
			!strings.HasPrefix(funcName, "runtime.") {
			// Form and append a line of stack trace into a
			// collection, 'trace', to build full stack trace
			trace = append(trace, fmt.Sprintf("%v:%v:%v()", file, lineNumber, funcName))

			// Ignore trace logs beyond the following conditions
			for _, name := range matchingFuncNames {
				if funcName == name {
					return trace
				}
			}
		}
		traceLevel++
		// Read stack trace information from PC
		pc, file, lineNumber, ok = runtime.Caller(traceLevel)
	}
	return trace
}

// Return the highway hash of the passed string
func hashString(input string) string {
	hh, _ := highwayhash.New(magicHighwayHash256Key)
	hh.Write([]byte(input))
	return hex.EncodeToString(hh.Sum(nil))
}

// LogAlwaysIf prints a detailed error message during
// the execution of the server.
func LogAlwaysIf(ctx context.Context, err error, errKind ...interface{}) {
	if err == nil {
		return
	}

	logIf(ctx, err, errKind...)
}

// LogIf prints a detailed error message during
// the execution of the server, if it is not an
// ignored error.
func LogIf(ctx context.Context, err error, errKind ...interface{}) {
	if logIgnoreError(err) {
		return
	}
	logIf(ctx, err, errKind...)
}

func errToEntry(ctx context.Context, err error, errKind ...interface{}) log.Entry {
	logKind := madmin.LogKindAll
	if len(errKind) > 0 {
		if ek, ok := errKind[0].(madmin.LogKind); ok {
			logKind = ek
		}
	}
	req := GetReqInfo(ctx)

	if req == nil {
		req = &ReqInfo{API: "SYSTEM"}
	}
	req.RLock()
	defer req.RUnlock()

	API := "SYSTEM"
	if req.API != "" {
		API = req.API
	}

	kv := req.GetTags()
	tags := make(map[string]interface{}, len(kv))
	for _, entry := range kv {
		tags[entry.Key] = entry.Val
	}

	// Get full stack trace
	trace := getTrace(3)

	// Get the cause for the Error
	message := fmt.Sprintf("%v (%T)", err, err)
	deploymentID := req.DeploymentID
	if req.DeploymentID == "" {
		deploymentID = xhttp.GlobalDeploymentID
	}

	objects := make([]log.ObjectVersion, 0, len(req.Objects))
	for _, ov := range req.Objects {
		objects = append(objects, log.ObjectVersion{
			ObjectName: ov.ObjectName,
			VersionID:  ov.VersionID,
		})
	}

	entry := log.Entry{
		DeploymentID: deploymentID,
		Level:        ErrorLvl.String(),
		LogKind:      logKind,
		RemoteHost:   req.RemoteHost,
		Host:         req.Host,
		RequestID:    req.RequestID,
		UserAgent:    req.UserAgent,
		Time:         time.Now().UTC(),
		API: &log.API{
			Name: API,
			Args: &log.Args{
				Bucket:    req.BucketName,
				Object:    req.ObjectName,
				VersionID: req.VersionID,
				Objects:   objects,
			},
		},
		Trace: &log.Trace{
			Message:   message,
			Source:    trace,
			Variables: tags,
		},
	}

	if anonFlag {
		entry.API.Args.Bucket = hashString(entry.API.Args.Bucket)
		entry.API.Args.Object = hashString(entry.API.Args.Object)
		entry.RemoteHost = hashString(entry.RemoteHost)
		entry.Trace.Message = reflect.TypeOf(err).String()
		entry.Trace.Variables = make(map[string]interface{})
	}

	return entry
}

// consoleLogIf prints a detailed error message during
// the execution of the server.
func consoleLogIf(ctx context.Context, err error, errKind ...interface{}) {
	if MinimumLogLevel > ErrorLvl {
		return
	}

	if consoleTgt != nil {
		entry := errToEntry(ctx, err, errKind...)
		consoleTgt.Send(entry)
	}
}

// logIf prints a detailed error message during
// the execution of the server.
func logIf(ctx context.Context, err error, errKind ...interface{}) {
	if MinimumLogLevel > ErrorLvl {
		return
	}

	systemTgts := SystemTargets()
	if len(systemTgts) == 0 {
		return
	}

	entry := errToEntry(ctx, err, errKind...)
	// Iterate over all logger targets to send the log entry
	for _, t := range systemTgts {
		if err := t.Send(entry); err != nil {
			if consoleTgt != nil {
				entry.Trace.Message = fmt.Sprintf("event(%#v) was not sent to Logger target (%#v): %#v", entry, t, err)
				consoleTgt.Send(entry)
			}
		}
	}
}

// ErrCritical is the value panic'd whenever CriticalIf is called.
var ErrCritical struct{}

// CriticalIf logs the provided error on the console. It fails the
// current go-routine by causing a `panic(ErrCritical)`.
func CriticalIf(ctx context.Context, err error, errKind ...interface{}) {
	if err != nil {
		LogIf(ctx, err, errKind...)
		panic(ErrCritical)
	}
}

// FatalIf is similar to Fatal() but it ignores passed nil error
func FatalIf(err error, msg string, data ...interface{}) {
	if err == nil {
		return
	}
	fatal(err, msg, data...)
}
<|endoftext|>


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

package markup

import (
	"bytes"
	"fmt"
	"io"
	"strings"

	"github.com/unknwon/com"
	"golang.org/x/net/html"

	"gogs.io/gogs/internal/conf"
	"gogs.io/gogs/internal/lazyregexp"
	"gogs.io/gogs/internal/tool"
)

// IsReadmeFile reports whether name looks like a README file based on its extension.
func IsReadmeFile(name string) bool {
	return strings.HasPrefix(strings.ToLower(name), "readme")
}

// IsIPythonNotebook reports whether name looks like a IPython notebook based on its extension.
func IsIPythonNotebook(name string) bool {
	return strings.HasSuffix(name, ".ipynb")
}

const (
	IssueNameStyleNumeric      = "numeric"
	IssueNameStyleAlphanumeric = "alphanumeric"
)

var (
	// MentionPattern matches string that mentions someone, e.g. @Unknwon
	MentionPattern = lazyregexp.New(`(\s|^|\W)@[0-9a-zA-Z-_\.]+`)

	// CommitPattern matches link to certain commit with or without trailing hash,
	// e.g. https://try.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2
	CommitPattern = lazyregexp.New(`(\s|^)https?.*commit/[0-9a-zA-Z]+(#+[0-9a-zA-Z-]*)?`)

	// IssueFullPattern matches link to an issue with or without trailing hash,
	// e.g. https://try.gogs.io/gogs/gogs/issues/4#issue-685
	IssueFullPattern = lazyregexp.New(`(\s|^)https?.*issues/[0-9]+(#+[0-9a-zA-Z-]*)?`)
	// IssueNumericPattern matches string that references to a numeric issue, e.g. #1287
	IssueNumericPattern = lazyregexp.New(`( |^|\(|\[)#[0-9]+\b`)
	// IssueAlphanumericPattern matches string that references to an alphanumeric issue, e.g. ABC-1234
	IssueAlphanumericPattern = lazyregexp.New(`( |^|\(|\[)[A-Z]{1,10}-[1-9][0-9]*\b`)
	// CrossReferenceIssueNumericPattern matches string that references a numeric issue in a difference repository
	// e.g. gogs/gogs#12345
	CrossReferenceIssueNumericPattern = lazyregexp.New(`( |^)[0-9a-zA-Z-_\.]+/[0-9a-zA-Z-_\.]+#[0-9]+\b`)

	// Sha1CurrentPattern matches string that represents a commit SHA, e.g. d8a994ef243349f321568f9e36d5c3f444b99cae
	// FIXME: this pattern matches pure numbers as well, right now we do a hack to check in RenderSha1CurrentPattern by converting string to a number.
	Sha1CurrentPattern = lazyregexp.New(`\b[0-9a-f]{7,40}\b`)
)

// FindAllMentions matches mention patterns in given content
// and returns a list of found user names without @ prefix.
func FindAllMentions(content string) []string {
	mentions := MentionPattern.FindAllString(content, -1)
	for i := range mentions {
		mentions[i] = mentions[i][strings.Index(mentions[i], "@")+1:] // Strip @ character
	}
	return mentions
}

// cutoutVerbosePrefix cutouts URL prefix including sub-path to
// return a clean unified string of request URL path.
func cutoutVerbosePrefix(prefix string) string {
	if len(prefix) == 0 || prefix[0] != '/' {
		return prefix
	}
	count := 0
	for i := 0; i < len(prefix); i++ {
		if prefix[i] == '/' {
			count++
		}
		if count >= 3+conf.Server.SubpathDepth {
			return prefix[:i]
		}
	}
	return prefix
}

// RenderIssueIndexPattern renders issue indexes to corresponding links.
func RenderIssueIndexPattern(rawBytes []byte, urlPrefix string, metas map[string]string) []byte {
	urlPrefix = cutoutVerbosePrefix(urlPrefix)

	pattern := IssueNumericPattern
	if metas["style"] == IssueNameStyleAlphanumeric {
		pattern = IssueAlphanumericPattern
	}

	ms := pattern.FindAll(rawBytes, -1)
	for _, m := range ms {
		if m[0] == ' ' || m[0] == '(' || m[0] == '[' {
			// ignore leading space, opening parentheses, or opening square brackets
			m = m[1:]
		}
		var link string
		if metas == nil || metas["format"] == "" {
			link = fmt.Sprintf(`<a href="%s/issues/%s">%s</a>`, urlPrefix, m[1:], m)
		} else {
			// Support for external issue tracker
			if metas["style"] == IssueNameStyleAlphanumeric {
				metas["index"] = string(m)
			} else {
				metas["index"] = string(m[1:])
			}
			link = fmt.Sprintf(`<a href="%s">%s</a>`, com.Expand(metas["format"], metas), m)
		}
		rawBytes = bytes.Replace(rawBytes, m, []byte(link), 1)
	}
	return rawBytes
}

// Note: this section is for purpose of increase performance and
// reduce memory allocation at runtime since they are constant literals.
var pound = []byte("#")

// RenderCrossReferenceIssueIndexPattern renders issue indexes from other repositories to corresponding links.
func RenderCrossReferenceIssueIndexPattern(rawBytes []byte, _ string, _ map[string]string) []byte {
	ms := CrossReferenceIssueNumericPattern.FindAll(rawBytes, -1)
	for _, m := range ms {
		if m[0] == ' ' || m[0] == '(' {
			m = m[1:] // ignore leading space or opening parentheses
		}

		delimIdx := bytes.Index(m, pound)
		repo := string(m[:delimIdx])
		index := string(m[delimIdx+1:])

		link := fmt.Sprintf(`<a href="%s%s/issues/%s">%s</a>`, conf.Server.ExternalURL, repo, index, m)
		rawBytes = bytes.Replace(rawBytes, m, []byte(link), 1)
	}
	return rawBytes
}

// RenderSha1CurrentPattern renders SHA1 strings to corresponding links that assumes in the same repository.
func RenderSha1CurrentPattern(rawBytes []byte, urlPrefix string) []byte {
	return []byte(Sha1CurrentPattern.ReplaceAllStringFunc(string(rawBytes[:]), func(m string) string {
		if com.StrTo(m).MustInt() > 0 {
			return m
		}

		return fmt.Sprintf(`<a href="%s/commit/%s"><code>%s</code></a>`, urlPrefix, m, tool.ShortSHA1(m))
	}))
}

// RenderSpecialLink renders mentions, indexes and SHA1 strings to corresponding links.
func RenderSpecialLink(rawBytes []byte, urlPrefix string, metas map[string]string) []byte {
	ms := MentionPattern.FindAll(rawBytes, -1)
	for _, m := range ms {
		m = m[bytes.Index(m, []byte("@")):]
		rawBytes = bytes.Replace(rawBytes, m,
			[]byte(fmt.Sprintf(`<a href="%s/%s">%s</a>`, conf.Server.Subpath, m[1:], m)), -1)
	}

	rawBytes = RenderIssueIndexPattern(rawBytes, urlPrefix, metas)
	rawBytes = RenderCrossReferenceIssueIndexPattern(rawBytes, urlPrefix, metas)
	rawBytes = RenderSha1CurrentPattern(rawBytes, metas["repoLink"])
	return rawBytes
}

var (
	leftAngleBracket  = []byte("</")
	rightAngleBracket = []byte(">")
)

var noEndTags = []string{"input", "br", "hr", "img"}

// wrapImgWithLink warps link to standalone <img> tags.
func wrapImgWithLink(urlPrefix string, buf *bytes.Buffer, token html.Token) {
	// Extract "src" and "alt" attributes
	var src, alt string
	for i := range token.Attr {
		switch token.Attr[i].Key {
		case "src":
			src = token.Attr[i].Val
		case "alt":
			alt = token.Attr[i].Val
		}
	}

	// Skip in case the "src" is empty
	if len(src) == 0 {
		buf.WriteString(token.String())
		return
	}

	// Skip in case the "src" is data url
	if strings.HasPrefix(src, "data:") {
		buf.WriteString(token.String())
		return
	}

	// Prepend repository base URL for internal links
	needPrepend := !isLink([]byte(src))
	if needPrepend {
		urlPrefix = strings.Replace(urlPrefix, "/src/", "/raw/", 1)
		if src[0] != '/' {
			urlPrefix += "/"
		}
	}

	buf.WriteString(`<a href="`)
	if needPrepend {
		buf.WriteString(urlPrefix)
		buf.WriteString(src)
	} else {
		buf.WriteString(src)
	}
	buf.WriteString(`">`)

	if needPrepend {
		src = strings.Replace(urlPrefix+src, " ", "%20", -1)
		buf.WriteString(`<img src="`)
		buf.WriteString(src)
		buf.WriteString(`"`)

		if len(alt) > 0 {
			buf.WriteString(` alt="`)
			buf.WriteString(alt)
			buf.WriteString(`"`)
		}

		buf.WriteString(`>`)

	} else {
		buf.WriteString(token.String())
	}

	buf.WriteString(`</a>`)
}

// postProcessHTML treats different types of HTML differently,
// and only renders special links for plain text blocks.
func postProcessHTML(rawHTML []byte, urlPrefix string, metas map[string]string) []byte {
	startTags := make([]string, 0, 5)
	buf := bytes.NewBuffer(nil)
	tokenizer := html.NewTokenizer(bytes.NewReader(rawHTML))

outerLoop:
	for html.ErrorToken != tokenizer.Next() {
		token := tokenizer.Token()
		switch token.Type {
		case html.TextToken:
			buf.Write(RenderSpecialLink([]byte(token.String()), urlPrefix, metas))

		case html.StartTagToken:
			tagName := token.Data

			if tagName == "img" {
				wrapImgWithLink(urlPrefix, buf, token)
				continue outerLoop
			}

			buf.WriteString(token.String())
			// If this is an excluded tag, we skip processing all output until a close tag is encountered.
			if strings.EqualFold("a", tagName) || strings.EqualFold("code", tagName) || strings.EqualFold("pre", tagName) {
				stackNum := 1
				for html.ErrorToken != tokenizer.Next() {
					token = tokenizer.Token()

					// Copy the token to the output verbatim
					buf.WriteString(token.String())

					// Stack number doesn't increase for tags without end tags.
					if token.Type == html.StartTagToken && !com.IsSliceContainsStr(noEndTags, token.Data) {
						stackNum++
					}

					// If this is the close tag to the outer-most, we are done
					if token.Type == html.EndTagToken {
						stackNum--
						if stackNum <= 0 && strings.EqualFold(tagName, token.Data) {
							break
						}
					}
				}
				continue outerLoop
			}

			if !com.IsSliceContainsStr(noEndTags, tagName) {
				startTags = append(startTags, tagName)
			}

		case html.EndTagToken:
			if len(startTags) == 0 {
				buf.WriteString(token.String())
				break
			}

			buf.Write(leftAngleBracket)
			buf.WriteString(startTags[len(startTags)-1])
			buf.Write(rightAngleBracket)
			startTags = startTags[:len(startTags)-1]
		default:
			buf.WriteString(token.String())
		}
	}

	if io.EOF == tokenizer.Err() {
		return buf.Bytes()
	}

	// If we are not at the end of the input, then some other parsing error has occurred,
	// so return the input verbatim.
	return rawHTML
}

type Type string

const (
	TypeUnrecognized    Type = "unrecognized"
	TypeMarkdown        Type = "markdown"
	TypeOrgMode         Type = "orgmode"
	TypeIPythonNotebook Type = "ipynb"
)

// Detect returns best guess of a markup type based on file name.
func Detect(filename string) Type {
	switch {
	case IsMarkdownFile(filename):
		return TypeMarkdown
	case IsOrgModeFile(filename):
		return TypeOrgMode
	case IsIPythonNotebook(filename):
		return TypeIPythonNotebook
	default:
		return TypeUnrecognized
	}
}

// Render takes a string or []byte and renders to sanitized HTML in given type of syntax with special links.
func Render(typ Type, input interface{}, urlPrefix string, metas map[string]string) []byte {
	var rawBytes []byte
	switch v := input.(type) {
	case []byte:
		rawBytes = v
	case string:
		rawBytes = []byte(v)
	default:
		panic(fmt.Sprintf("unrecognized input content type: %T", input))
	}

	urlPrefix = strings.TrimRight(strings.Replace(urlPrefix, " ", "%20", -1), "/")
	var rawHTML []byte
	switch typ {
	case TypeMarkdown:
		rawHTML = RawMarkdown(rawBytes, urlPrefix)
	case TypeOrgMode:
		rawHTML = RawOrgMode(rawBytes, urlPrefix)
	default:
		return rawBytes // Do nothing if syntax type is not recognized
	}

	rawHTML = postProcessHTML(rawHTML, urlPrefix, metas)
	return SanitizeBytes(rawHTML)
}
<commit_msg>autofix: simplify slice expression to sliced value itself (#6801)<commit_after>// Copyright 2017 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package markup

import (
	"bytes"
	"fmt"
	"io"
	"strings"

	"github.com/unknwon/com"
	"golang.org/x/net/html"

	"gogs.io/gogs/internal/conf"
	"gogs.io/gogs/internal/lazyregexp"
	"gogs.io/gogs/internal/tool"
)

// IsReadmeFile reports whether name looks like a README file based on its extension.
func IsReadmeFile(name string) bool {
	return strings.HasPrefix(strings.ToLower(name), "readme")
}

// IsIPythonNotebook reports whether name looks like a IPython notebook based on its extension.
func IsIPythonNotebook(name string) bool {
	return strings.HasSuffix(name, ".ipynb")
}

const (
	IssueNameStyleNumeric      = "numeric"
	IssueNameStyleAlphanumeric = "alphanumeric"
)

var (
	// MentionPattern matches string that mentions someone, e.g. @Unknwon
	MentionPattern = lazyregexp.New(`(\s|^|\W)@[0-9a-zA-Z-_\.]+`)

	// CommitPattern matches link to certain commit with or without trailing hash,
	// e.g. https://try.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2
	CommitPattern = lazyregexp.New(`(\s|^)https?.*commit/[0-9a-zA-Z]+(#+[0-9a-zA-Z-]*)?`)

	// IssueFullPattern matches link to an issue with or without trailing hash,
	// e.g. https://try.gogs.io/gogs/gogs/issues/4#issue-685
	IssueFullPattern = lazyregexp.New(`(\s|^)https?.*issues/[0-9]+(#+[0-9a-zA-Z-]*)?`)
	// IssueNumericPattern matches string that references to a numeric issue, e.g. #1287
	IssueNumericPattern = lazyregexp.New(`( |^|\(|\[)#[0-9]+\b`)
	// IssueAlphanumericPattern matches string that references to an alphanumeric issue, e.g. ABC-1234
	IssueAlphanumericPattern = lazyregexp.New(`( |^|\(|\[)[A-Z]{1,10}-[1-9][0-9]*\b`)
	// CrossReferenceIssueNumericPattern matches string that references a numeric issue in a difference repository
	// e.g. gogs/gogs#12345
	CrossReferenceIssueNumericPattern = lazyregexp.New(`( |^)[0-9a-zA-Z-_\.]+/[0-9a-zA-Z-_\.]+#[0-9]+\b`)

	// Sha1CurrentPattern matches string that represents a commit SHA, e.g. d8a994ef243349f321568f9e36d5c3f444b99cae
	// FIXME: this pattern matches pure numbers as well, right now we do a hack to check in RenderSha1CurrentPattern by converting string to a number.
	Sha1CurrentPattern = lazyregexp.New(`\b[0-9a-f]{7,40}\b`)
)

// FindAllMentions matches mention patterns in given content
// and returns a list of found user names without @ prefix.
func FindAllMentions(content string) []string {
	mentions := MentionPattern.FindAllString(content, -1)
	for i := range mentions {
		mentions[i] = mentions[i][strings.Index(mentions[i], "@")+1:] // Strip @ character
	}
	return mentions
}

// cutoutVerbosePrefix cutouts URL prefix including sub-path to
// return a clean unified string of request URL path.
func cutoutVerbosePrefix(prefix string) string {
	if len(prefix) == 0 || prefix[0] != '/' {
		return prefix
	}
	count := 0
	for i := 0; i < len(prefix); i++ {
		if prefix[i] == '/' {
			count++
		}
		if count >= 3+conf.Server.SubpathDepth {
			return prefix[:i]
		}
	}
	return prefix
}

// RenderIssueIndexPattern renders issue indexes to corresponding links.
func RenderIssueIndexPattern(rawBytes []byte, urlPrefix string, metas map[string]string) []byte {
	urlPrefix = cutoutVerbosePrefix(urlPrefix)

	pattern := IssueNumericPattern
	if metas["style"] == IssueNameStyleAlphanumeric {
		pattern = IssueAlphanumericPattern
	}

	ms := pattern.FindAll(rawBytes, -1)
	for _, m := range ms {
		if m[0] == ' ' || m[0] == '(' || m[0] == '[' {
			// ignore leading space, opening parentheses, or opening square brackets
			m = m[1:]
		}
		var link string
		if metas == nil || metas["format"] == "" {
			link = fmt.Sprintf(`<a href="%s/issues/%s">%s</a>`, urlPrefix, m[1:], m)
		} else {
			// Support for external issue tracker
			if metas["style"] == IssueNameStyleAlphanumeric {
				metas["index"] = string(m)
			} else {
				metas["index"] = string(m[1:])
			}
			link = fmt.Sprintf(`<a href="%s">%s</a>`, com.Expand(metas["format"], metas), m)
		}
		rawBytes = bytes.Replace(rawBytes, m, []byte(link), 1)
	}
	return rawBytes
}

// Note: this section is for purpose of increase performance and
// reduce memory allocation at runtime since they are constant literals.
var pound = []byte("#")

// RenderCrossReferenceIssueIndexPattern renders issue indexes from other repositories to corresponding links.
func RenderCrossReferenceIssueIndexPattern(rawBytes []byte, _ string, _ map[string]string) []byte {
	ms := CrossReferenceIssueNumericPattern.FindAll(rawBytes, -1)
	for _, m := range ms {
		if m[0] == ' ' || m[0] == '(' {
			m = m[1:] // ignore leading space or opening parentheses
		}

		delimIdx := bytes.Index(m, pound)
		repo := string(m[:delimIdx])
		index := string(m[delimIdx+1:])

		link := fmt.Sprintf(`<a href="%s%s/issues/%s">%s</a>`, conf.Server.ExternalURL, repo, index, m)
		rawBytes = bytes.Replace(rawBytes, m, []byte(link), 1)
	}
	return rawBytes
}

// RenderSha1CurrentPattern renders SHA1 strings to corresponding links that assumes in the same repository.
func RenderSha1CurrentPattern(rawBytes []byte, urlPrefix string) []byte {
	return []byte(Sha1CurrentPattern.ReplaceAllStringFunc(string(rawBytes), func(m string) string {
		if com.StrTo(m).MustInt() > 0 {
			return m
		}

		return fmt.Sprintf(`<a href="%s/commit/%s"><code>%s</code></a>`, urlPrefix, m, tool.ShortSHA1(m))
	}))
}

// RenderSpecialLink renders mentions, indexes and SHA1 strings to corresponding links.
func RenderSpecialLink(rawBytes []byte, urlPrefix string, metas map[string]string) []byte {
	ms := MentionPattern.FindAll(rawBytes, -1)
	for _, m := range ms {
		m = m[bytes.Index(m, []byte("@")):]
		rawBytes = bytes.Replace(rawBytes, m,
			[]byte(fmt.Sprintf(`<a href="%s/%s">%s</a>`, conf.Server.Subpath, m[1:], m)), -1)
	}

	rawBytes = RenderIssueIndexPattern(rawBytes, urlPrefix, metas)
	rawBytes = RenderCrossReferenceIssueIndexPattern(rawBytes, urlPrefix, metas)
	rawBytes = RenderSha1CurrentPattern(rawBytes, metas["repoLink"])
	return rawBytes
}

var (
	leftAngleBracket  = []byte("</")
	rightAngleBracket = []byte(">")
)

var noEndTags = []string{"input", "br", "hr", "img"}

// wrapImgWithLink warps link to standalone <img> tags.
func wrapImgWithLink(urlPrefix string, buf *bytes.Buffer, token html.Token) {
	// Extract "src" and "alt" attributes
	var src, alt string
	for i := range token.Attr {
		switch token.Attr[i].Key {
		case "src":
			src = token.Attr[i].Val
		case "alt":
			alt = token.Attr[i].Val
		}
	}

	// Skip in case the "src" is empty
	if len(src) == 0 {
		buf.WriteString(token.String())
		return
	}

	// Skip in case the "src" is data url
	if strings.HasPrefix(src, "data:") {
		buf.WriteString(token.String())
		return
	}

	// Prepend repository base URL for internal links
	needPrepend := !isLink([]byte(src))
	if needPrepend {
		urlPrefix = strings.Replace(urlPrefix, "/src/", "/raw/", 1)
		if src[0] != '/' {
			urlPrefix += "/"
		}
	}

	buf.WriteString(`<a href="`)
	if needPrepend {
		buf.WriteString(urlPrefix)
		buf.WriteString(src)
	} else {
		buf.WriteString(src)
	}
	buf.WriteString(`">`)

	if needPrepend {
		src = strings.Replace(urlPrefix+src, " ", "%20", -1)
		buf.WriteString(`<img src="`)
		buf.WriteString(src)
		buf.WriteString(`"`)

		if len(alt) > 0 {
			buf.WriteString(` alt="`)
			buf.WriteString(alt)
			buf.WriteString(`"`)
		}

		buf.WriteString(`>`)

	} else {
		buf.WriteString(token.String())
	}

	buf.WriteString(`</a>`)
}

// postProcessHTML treats different types of HTML differently,
// and only renders special links for plain text blocks.
func postProcessHTML(rawHTML []byte, urlPrefix string, metas map[string]string) []byte {
	startTags := make([]string, 0, 5)
	buf := bytes.NewBuffer(nil)
	tokenizer := html.NewTokenizer(bytes.NewReader(rawHTML))

outerLoop:
	for html.ErrorToken != tokenizer.Next() {
		token := tokenizer.Token()
		switch token.Type {
		case html.TextToken:
			buf.Write(RenderSpecialLink([]byte(token.String()), urlPrefix, metas))

		case html.StartTagToken:
			tagName := token.Data

			if tagName == "img" {
				wrapImgWithLink(urlPrefix, buf, token)
				continue outerLoop
			}

			buf.WriteString(token.String())
			// If this is an excluded tag, we skip processing all output until a close tag is encountered.
			if strings.EqualFold("a", tagName) || strings.EqualFold("code", tagName) || strings.EqualFold("pre", tagName) {
				stackNum := 1
				for html.ErrorToken != tokenizer.Next() {
					token = tokenizer.Token()

					// Copy the token to the output verbatim
					buf.WriteString(token.String())

					// Stack number doesn't increase for tags without end tags.
					if token.Type == html.StartTagToken && !com.IsSliceContainsStr(noEndTags, token.Data) {
						stackNum++
					}

					// If this is the close tag to the outer-most, we are done
					if token.Type == html.EndTagToken {
						stackNum--
						if stackNum <= 0 && strings.EqualFold(tagName, token.Data) {
							break
						}
					}
				}
				continue outerLoop
			}

			if !com.IsSliceContainsStr(noEndTags, tagName) {
				startTags = append(startTags, tagName)
			}

		case html.EndTagToken:
			if len(startTags) == 0 {
				buf.WriteString(token.String())
				break
			}

			buf.Write(leftAngleBracket)
			buf.WriteString(startTags[len(startTags)-1])
			buf.Write(rightAngleBracket)
			startTags = startTags[:len(startTags)-1]
		default:
			buf.WriteString(token.String())
		}
	}

	if io.EOF == tokenizer.Err() {
		return buf.Bytes()
	}

	// If we are not at the end of the input, then some other parsing error has occurred,
	// so return the input verbatim.
	return rawHTML
}

type Type string

const (
	TypeUnrecognized    Type = "unrecognized"
	TypeMarkdown        Type = "markdown"
	TypeOrgMode         Type = "orgmode"
	TypeIPythonNotebook Type = "ipynb"
)

// Detect returns best guess of a markup type based on file name.
func Detect(filename string) Type {
	switch {
	case IsMarkdownFile(filename):
		return TypeMarkdown
	case IsOrgModeFile(filename):
		return TypeOrgMode
	case IsIPythonNotebook(filename):
		return TypeIPythonNotebook
	default:
		return TypeUnrecognized
	}
}

// Render takes a string or []byte and renders to sanitized HTML in given type of syntax with special links.
func Render(typ Type, input interface{}, urlPrefix string, metas map[string]string) []byte {
	var rawBytes []byte
	switch v := input.(type) {
	case []byte:
		rawBytes = v
	case string:
		rawBytes = []byte(v)
	default:
		panic(fmt.Sprintf("unrecognized input content type: %T", input))
	}

	urlPrefix = strings.TrimRight(strings.Replace(urlPrefix, " ", "%20", -1), "/")
	var rawHTML []byte
	switch typ {
	case TypeMarkdown:
		rawHTML = RawMarkdown(rawBytes, urlPrefix)
	case TypeOrgMode:
		rawHTML = RawOrgMode(rawBytes, urlPrefix)
	default:
		return rawBytes // Do nothing if syntax type is not recognized
	}

	rawHTML = postProcessHTML(rawHTML, urlPrefix, metas)
	return SanitizeBytes(rawHTML)
}
<|endoftext|>


<commit_before>package runner

import (
	"fmt"
	"io"
	"strings"
	"sync"
	"time"

	pb "github.com/ii/xds-test-harness/api/adapter"
	"github.com/ii/xds-test-harness/internal/parser"

	core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
	discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
	"github.com/rs/zerolog/log"
	"google.golang.org/grpc"
)

var (
	opts []grpc.DialOption = []grpc.DialOption{
		grpc.WithInsecure(),
		grpc.WithBlock(),
		grpc.WithTimeout(time.Second * 10),
	}
)

type ClientConfig struct {
	Port string
	Conn *grpc.ClientConn
}

type Cache struct {
	StartState     *pb.Snapshot
	StateSnapshots []*pb.Snapshot
	FinalResponse  *discovery.DiscoveryResponse
}

type Validate struct {
	RequestCount  int
	ResponseCount int
	Resources     map[string]map[string]string
}

func NewValidate() *Validate {
	resources := make(map[string]map[string]string)
	return &Validate{
		RequestCount:  0,
		ResponseCount: 0,
		Resources:     resources,
	}
}

type Runner struct {
	Adapter          *ClientConfig
	Target           *ClientConfig
	NodeID           string
	Cache            *Cache
	Aggregated       bool
	Service          *XDSService
	SubscribeRequest *discovery.DiscoveryRequest
	Validate         *Validate
}

func FreshRunner(current ...*Runner) *Runner {
	var (
		adapter    = &ClientConfig{}
		target     = &ClientConfig{}
		nodeID     = ""
		aggregated = false
	)

	if len(current) > 0 {
		adapter = current[0].Adapter
		target = current[0].Target
		nodeID = current[0].NodeID
		aggregated = current[0].Aggregated

	}

	validate := NewValidate()

	return &Runner{
		Adapter:    adapter,
		Target:     target,
		NodeID:     nodeID,
		Cache:      &Cache{},
		Service:    &XDSService{},
		Aggregated: aggregated,
		Validate:   validate,
	}
}

func (r *Runner) ConnectClient(server, address string) error {
	var client *ClientConfig
	if server == "target" {
		client = r.Target
	}
	if server == "adapter" {
		client = r.Adapter
	}
	if strings.HasPrefix(address, ":") {
		client.Port = address
	} else {
		client.Port = ":" + address
	}
	conn, err := connectViaGRPC(client, server)
	if err != nil {
		return err
	}
	client.Conn = conn
	return nil
}

func (r *Runner) Ack(service *XDSService) {
	service.Channels.Req <- r.SubscribeRequest
	service.Cache.Requests = append(service.Cache.Requests, r.SubscribeRequest)
	for {
		select {
		case res := <-service.Channels.Res:
			service.Cache.Responses = append(service.Cache.Responses, res)
			ack := newAckFromResponse(res, r.SubscribeRequest)
			log.Debug().
				Msgf("Sending Ack: %v", ack)
			service.Channels.Req <- ack
			service.Cache.Requests = append(service.Cache.Requests, ack)
		case <-service.Channels.Done:
			log.Debug().
				Msg("Received Done signal, shutting down request channel")
			close(service.Channels.Req)
			return
		}
	}
}

func (r *Runner) Stream(service *XDSService) error {
	defer service.Context.cancel()
	defer close(service.Channels.Err)

	var wg sync.WaitGroup
	go func() {
		for {
			wg.Add(1)
			in, err := service.Stream.Recv()
			if err == io.EOF {
				log.Debug().
					Msgf("No more Discovery Responses from %v stream", r.Service.Name)
				close(service.Channels.Res)
				return
			}
			if err != nil {
				log.Err(err).Msgf("error receiving responses on %v stream", r.Service.Name)
				service.Channels.Err <- err
				return
			}
			log.Debug().
				Msgf("Received discovery response: %v", in)
			r.Validate.ResponseCount++
			service.Channels.Res <- in
		}
	}()

	for req := range service.Channels.Req {
		if err := service.Stream.Send(req); err != nil {
			log.Err(err).
				Msg("Error sending discovery request")
			service.Channels.Err <- err
		}
		r.Validate.RequestCount++
	}
	service.Stream.CloseSend()
	wg.Wait()
	return nil
}

func connectViaGRPC(client *ClientConfig, server string) (conn *grpc.ClientConn, err error) {
	conn, err = grpc.Dial(client.Port, opts...)
	if err != nil {
		err = fmt.Errorf("Cannot connect at %v: %v\n", client.Port, err)
		return nil, err
	}
	log.Debug().
		Msgf("Runner connected to %v", server)
	return conn, nil
}

func newAckFromResponse(res *discovery.DiscoveryResponse, initReq *discovery.DiscoveryRequest) *discovery.DiscoveryRequest {
	// Only the first request should need the node ID,
	// so we do not include it in the followups.  If this
	// causes an error, it's a non-conformant error.
	request := &discovery.DiscoveryRequest{
		VersionInfo:   res.VersionInfo,
		ResourceNames: initReq.ResourceNames,
		TypeUrl:       initReq.TypeUrl,
		ResponseNonce: res.Nonce,
	}
	return request
}

func newRequest(resourceNames []string, typeURL, nodeID string) *discovery.DiscoveryRequest {
	return &discovery.DiscoveryRequest{
		VersionInfo: "",
		Node: &core.Node{
			Id: nodeID,
		},
		ResourceNames: resourceNames,
		TypeUrl:       typeURL,
	}
}
<commit_msg>use discovery responses to adjust resource map<commit_after>package runner

import (
	"fmt"
	"io"
	"strings"
	"sync"
	"time"

	pb "github.com/ii/xds-test-harness/api/adapter"
	"github.com/ii/xds-test-harness/internal/parser"

	core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
	discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
	"github.com/rs/zerolog/log"
	"google.golang.org/grpc"
)

var (
	opts []grpc.DialOption = []grpc.DialOption{
		grpc.WithInsecure(),
		grpc.WithBlock(),
		grpc.WithTimeout(time.Second * 10),
	}
)

type ClientConfig struct {
	Port string
	Conn *grpc.ClientConn
}

type Cache struct {
	StartState     *pb.Snapshot
	StateSnapshots []*pb.Snapshot
	FinalResponse  *discovery.DiscoveryResponse
}

type Validate struct {
	RequestCount  int
	ResponseCount int
	Resources     map[string]map[string]string
}

func NewValidate() *Validate {
	resources := make(map[string]map[string]string)
	return &Validate{
		RequestCount:  0,
		ResponseCount: 0,
		Resources:     resources,
	}
}

type Runner struct {
	Adapter          *ClientConfig
	Target           *ClientConfig
	NodeID           string
	Cache            *Cache
	Aggregated       bool
	Service          *XDSService
	SubscribeRequest *discovery.DiscoveryRequest
	Validate         *Validate
}

func FreshRunner(current ...*Runner) *Runner {
	var (
		adapter    = &ClientConfig{}
		target     = &ClientConfig{}
		nodeID     = ""
		aggregated = false
	)

	if len(current) > 0 {
		adapter = current[0].Adapter
		target = current[0].Target
		nodeID = current[0].NodeID
		aggregated = current[0].Aggregated

	}

	validate := NewValidate()

	return &Runner{
		Adapter:    adapter,
		Target:     target,
		NodeID:     nodeID,
		Cache:      &Cache{},
		Service:    &XDSService{},
		Aggregated: aggregated,
		Validate:   validate,
	}
}

func (r *Runner) ConnectClient(server, address string) error {
	var client *ClientConfig
	if server == "target" {
		client = r.Target
	}
	if server == "adapter" {
		client = r.Adapter
	}
	if strings.HasPrefix(address, ":") {
		client.Port = address
	} else {
		client.Port = ":" + address
	}
	conn, err := connectViaGRPC(client, server)
	if err != nil {
		return err
	}
	client.Conn = conn
	return nil
}

func (r *Runner) Ack(service *XDSService) {
	service.Channels.Req <- r.SubscribeRequest
	service.Cache.Requests = append(service.Cache.Requests, r.SubscribeRequest)
	for {
		select {
		case res := <-service.Channels.Res:
			service.Cache.Responses = append(service.Cache.Responses, res)
			ack := newAckFromResponse(res, r.SubscribeRequest)
			log.Debug().
				Msgf("Sending Ack: %v", ack)
			service.Channels.Req <- ack
			service.Cache.Requests = append(service.Cache.Requests, ack)
		case <-service.Channels.Done:
			log.Debug().
				Msg("Received Done signal, shutting down request channel")
			close(service.Channels.Req)
			return
		}
	}
}

func (r *Runner) Stream(service *XDSService) error {
	defer service.Context.cancel()
	defer close(service.Channels.Err)

	var wg sync.WaitGroup
	go func() {
		for {
			wg.Add(1)
			in, err := service.Stream.Recv()
			if err == io.EOF {
				log.Debug().
					Msgf("No more Discovery Responses from %v stream", r.Service.Name)
				close(service.Channels.Res)
				return
			}
			if err != nil {
				log.Err(err).Msgf("error receiving responses on %v stream", r.Service.Name)
				service.Channels.Err <- err
				return
			}
			log.Debug().
				Msgf("Received discovery response: %v", in)
			resources, err := parser.ResourceNames(in)
			if err != nil {
				log.Err(err).Msg("Could not gather resource names from response")
				service.Channels.Err <- err
				return
			}
			for _, resource := range resources {
				r.Validate.Resources[in.TypeUrl][resource] = in.VersionInfo
			}
			r.Validate.ResponseCount++
			service.Channels.Res <- in
		}
	}()

	for req := range service.Channels.Req {
		if err := service.Stream.Send(req); err != nil {
			log.Err(err).
				Msg("Error sending discovery request")
			service.Channels.Err <- err
		}
		r.Validate.RequestCount++
	}
	service.Stream.CloseSend()
	wg.Wait()
	return nil
}

func connectViaGRPC(client *ClientConfig, server string) (conn *grpc.ClientConn, err error) {
	conn, err = grpc.Dial(client.Port, opts...)
	if err != nil {
		err = fmt.Errorf("Cannot connect at %v: %v\n", client.Port, err)
		return nil, err
	}
	log.Debug().
		Msgf("Runner connected to %v", server)
	return conn, nil
}

func newAckFromResponse(res *discovery.DiscoveryResponse, initReq *discovery.DiscoveryRequest) *discovery.DiscoveryRequest {
	// Only the first request should need the node ID,
	// so we do not include it in the followups.  If this
	// causes an error, it's a non-conformant error.
	request := &discovery.DiscoveryRequest{
		VersionInfo:   res.VersionInfo,
		ResourceNames: initReq.ResourceNames,
		TypeUrl:       initReq.TypeUrl,
		ResponseNonce: res.Nonce,
	}
	return request
}

func newRequest(resourceNames []string, typeURL, nodeID string) *discovery.DiscoveryRequest {
	return &discovery.DiscoveryRequest{
		VersionInfo: "",
		Node: &core.Node{
			Id: nodeID,
		},
		ResourceNames: resourceNames,
		TypeUrl:       typeURL,
	}
}
<|endoftext|>


<commit_before>package virt

import (
	"io"

	"github.com/jimmyfrasche/etlite/internal/internal/errint"
	"github.com/jimmyfrasche/etlite/internal/virt/internal/builder"
)

//TODO create an enum of current states and track transitions within methods and instructions
//so we can report an internal error if things are used out of sequence

//InitDecoder reads the header from the current input device with the current decoder.
func (m *Machine) InitDecoder(header []string) (string, []string, error) {
	// make sure we have a decoder and an input
	dec := m.decoder
	if dec == nil {
		return "", nil, errint.New("no decoder when attempting to init decoder")
	}
	in := m.input
	if in == nil {
		return "", nil, errint.New("no input when attempting to init decoder")
	}

	return dec.ReadHeader(header, in)
}

//CreateTable initializes the decoder then creates table name with header.
//
//InitDecoder must be called before this.
//
//See MkCreateTableFrom and BulkInsert.
func (m *Machine) CreateTable(temp bool, name string, header []string) error {
	//create ddl
	b := builder.New("CREATE")

	if temp {
		b.Push("TEMPORARY")
	}

	b.Push("TABLE", name, "(")

	b.CSV(header, func(h string) {
		b.Push(h, "TEXT")
	})

	b.Push(")")

	return m.exec(b.Join(" "))
}

//BulkInsert from the current decoder into table name with header.
//
//Table name must exist and should have been created by
//either CreateTableFrom or CreateTable, with no intervening
//reads to or changes of the decoder.
func (m *Machine) BulkInsert(name string, header []string, limit, offset int) error {
	//make sure we have a decoder
	dec := m.decoder
	if dec == nil {
		return errint.Newf("no decoder when attempting to import %s", name)
	}

	//build the bulk loader
	b := builder.New("INSERT INTO", name, "(")

	b.CSV(header, func(h string) {
		b.Push(h)
	})

	b.Push(") VALUES (")

	b.CSV(header, func(string) {
		b.Push("?")
	})
	b.Push(")")

	p, err := m.conn.Prepare(b.Join(" "))
	if err != nil {
		return err
	}
	defer p.Close()

	bulk, err := p.Loader()
	if err != nil {
		return err
	}

	if offset > 0 {
		if err := dec.Skip(offset); err != nil {
			return err
		}
	}

	for rows := 0; limit > 0 && rows == limit; rows++ {
		row, err := dec.ReadRow()
		if err == io.EOF {
			break
		}
		if err != nil {
			return err
		}

		if err := bulk.Load(row); err != nil {
			return err
		}
	}

	return bulk.Close()
}
<commit_msg>fixed logic error in bulk insert loop<commit_after>package virt

import (
	"io"

	"github.com/jimmyfrasche/etlite/internal/internal/errint"
	"github.com/jimmyfrasche/etlite/internal/virt/internal/builder"
)

//TODO create an enum of current states and track transitions within methods and instructions
//so we can report an internal error if things are used out of sequence

//InitDecoder reads the header from the current input device with the current decoder.
func (m *Machine) InitDecoder(header []string) (string, []string, error) {
	// make sure we have a decoder and an input
	dec := m.decoder
	if dec == nil {
		return "", nil, errint.New("no decoder when attempting to init decoder")
	}
	in := m.input
	if in == nil {
		return "", nil, errint.New("no input when attempting to init decoder")
	}

	return dec.ReadHeader(header, in)
}

//CreateTable initializes the decoder then creates table name with header.
//
//InitDecoder must be called before this.
//
//See MkCreateTableFrom and BulkInsert.
func (m *Machine) CreateTable(temp bool, name string, header []string) error {
	//create ddl
	b := builder.New("CREATE")

	if temp {
		b.Push("TEMPORARY")
	}

	b.Push("TABLE", name, "(")

	b.CSV(header, func(h string) {
		b.Push(h, "TEXT")
	})

	b.Push(")")

	return m.exec(b.Join(" "))
}

//BulkInsert from the current decoder into table name with header.
//
//Table name must exist and should have been created by
//either CreateTableFrom or CreateTable, with no intervening
//reads to or changes of the decoder.
func (m *Machine) BulkInsert(name string, header []string, limit, offset int) error {
	//make sure we have a decoder
	dec := m.decoder
	if dec == nil {
		return errint.Newf("no decoder when attempting to import %s", name)
	}

	//build the bulk loader
	b := builder.New("INSERT INTO", name, "(")

	b.CSV(header, func(h string) {
		b.Push(h)
	})

	b.Push(") VALUES (")

	b.CSV(header, func(string) {
		b.Push("?")
	})
	b.Push(")")

	p, err := m.conn.Prepare(b.Join(" "))
	if err != nil {
		return err
	}
	defer p.Close()

	bulk, err := p.Loader()
	if err != nil {
		return err
	}

	if offset > 0 {
		if err := dec.Skip(offset); err != nil {
			return err
		}
	}

	for rows := 0; limit > 0 && rows == limit || true; rows++ {
		row, err := dec.ReadRow()
		if err == io.EOF {
			break
		}
		if err != nil {
			return err
		}

		if err := bulk.Load(row); err != nil {
			return err
		}
	}

	return bulk.Close()
}
<|endoftext|>


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

package main

import (
	"bytes"
	"expvar"
	"flag"
	"fmt"
	"http"
	"io"
	"log"
	"os"
	"strconv"
)

// hello world, the web server
var helloRequests = expvar.NewInt("hello-requests")

func HelloServer(w http.ResponseWriter, req *http.Request) {
	helloRequests.Add(1)
	io.WriteString(w, "hello, world!\n")
}

// Simple counter server. POSTing to it will set the value.
type Counter struct {
	n int
}

// This makes Counter satisfy the expvar.Var interface, so we can export
// it directly.
func (ctr *Counter) String() string { return fmt.Sprintf("%d", ctr.n) }

func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	switch req.Method {
	case "GET":
		ctr.n++
	case "POST":
		buf := new(bytes.Buffer)
		io.Copy(buf, req.Body)
		body := buf.String()
		if n, err := strconv.Atoi(body); err != nil {
			fmt.Fprintf(w, "bad POST: %v\nbody: [%v]\n", err, body)
		} else {
			ctr.n = n
			fmt.Fprint(w, "counter reset\n")
		}
	}
	fmt.Fprintf(w, "counter = %d\n", ctr.n)
}

// simple flag server
var booleanflag = flag.Bool("boolean", true, "another flag for testing")

func FlagServer(w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	fmt.Fprint(w, "Flags:\n")
	flag.VisitAll(func(f *flag.Flag) {
		if f.Value.String() != f.DefValue {
			fmt.Fprintf(w, "%s = %s [default = %s]\n", f.Name, f.Value.String(), f.DefValue)
		} else {
			fmt.Fprintf(w, "%s = %s\n", f.Name, f.Value.String())
		}
	})
}

// simple argument server
func ArgServer(w http.ResponseWriter, req *http.Request) {
	for _, s := range os.Args {
		fmt.Fprint(w, s, " ")
	}
}

// a channel (just for the fun of it)
type Chan chan int

func ChanCreate() Chan {
	c := make(Chan)
	go func(c Chan) {
		for x := 0; ; x++ {
			c <- x
		}
	}(c)
	return c
}

func (ch Chan) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	io.WriteString(w, fmt.Sprintf("channel send #%d\n", <-ch))
}

// exec a program, redirecting output
func DateServer(rw http.ResponseWriter, req *http.Request) {
	rw.Header().Set("Content-Type", "text/plain; charset=utf-8")
	r, w, err := os.Pipe()
	if err != nil {
		fmt.Fprintf(rw, "pipe: %s\n", err)
		return
	}

	p, err := os.StartProcess("/bin/date", []string{"date"}, &os.ProcAttr{Files: []*os.File{nil, w, w}})
	defer r.Close()
	w.Close()
	if err != nil {
		fmt.Fprintf(rw, "fork/exec: %s\n", err)
		return
	}
	defer p.Release()
	io.Copy(rw, r)
	wait, err := p.Wait(0)
	if err != nil {
		fmt.Fprintf(rw, "wait: %s\n", err)
		return
	}
	if !wait.Exited() || wait.ExitStatus() != 0 {
		fmt.Fprintf(rw, "date: %v\n", wait)
		return
	}
}

func Logger(w http.ResponseWriter, req *http.Request) {
	log.Print(req.URL.Raw)
	w.WriteHeader(404)
	w.Write([]byte("oops"))
}

var webroot = flag.String("root", "/home/rsc", "web root directory")

func main() {
	flag.Parse()

	// The counter is published as a variable directly.
	ctr := new(Counter)
	http.Handle("/counter", ctr)
	expvar.Publish("counter", ctr)

	http.Handle("/", http.HandlerFunc(Logger))
	http.Handle("/go/", http.FileServer(*webroot, "/go/"))
	http.Handle("/flags", http.HandlerFunc(FlagServer))
	http.Handle("/args", http.HandlerFunc(ArgServer))
	http.Handle("/go/hello", http.HandlerFunc(HelloServer))
	http.Handle("/chan", ChanCreate())
	http.Handle("/date", http.HandlerFunc(DateServer))
	err := http.ListenAndServe(":12345", nil)
	if err != nil {
		log.Panicln("ListenAndServe:", err)
	}
}
<commit_msg>http: update triv.go with gofix<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"expvar"
	"flag"
	"fmt"
	"http"
	"io"
	"log"
	"os"
	"strconv"
)

// hello world, the web server
var helloRequests = expvar.NewInt("hello-requests")

func HelloServer(w http.ResponseWriter, req *http.Request) {
	helloRequests.Add(1)
	io.WriteString(w, "hello, world!\n")
}

// Simple counter server. POSTing to it will set the value.
type Counter struct {
	n int
}

// This makes Counter satisfy the expvar.Var interface, so we can export
// it directly.
func (ctr *Counter) String() string { return fmt.Sprintf("%d", ctr.n) }

func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	switch req.Method {
	case "GET":
		ctr.n++
	case "POST":
		buf := new(bytes.Buffer)
		io.Copy(buf, req.Body)
		body := buf.String()
		if n, err := strconv.Atoi(body); err != nil {
			fmt.Fprintf(w, "bad POST: %v\nbody: [%v]\n", err, body)
		} else {
			ctr.n = n
			fmt.Fprint(w, "counter reset\n")
		}
	}
	fmt.Fprintf(w, "counter = %d\n", ctr.n)
}

// simple flag server
var booleanflag = flag.Bool("boolean", true, "another flag for testing")

func FlagServer(w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	fmt.Fprint(w, "Flags:\n")
	flag.VisitAll(func(f *flag.Flag) {
		if f.Value.String() != f.DefValue {
			fmt.Fprintf(w, "%s = %s [default = %s]\n", f.Name, f.Value.String(), f.DefValue)
		} else {
			fmt.Fprintf(w, "%s = %s\n", f.Name, f.Value.String())
		}
	})
}

// simple argument server
func ArgServer(w http.ResponseWriter, req *http.Request) {
	for _, s := range os.Args {
		fmt.Fprint(w, s, " ")
	}
}

// a channel (just for the fun of it)
type Chan chan int

func ChanCreate() Chan {
	c := make(Chan)
	go func(c Chan) {
		for x := 0; ; x++ {
			c <- x
		}
	}(c)
	return c
}

func (ch Chan) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	io.WriteString(w, fmt.Sprintf("channel send #%d\n", <-ch))
}

// exec a program, redirecting output
func DateServer(rw http.ResponseWriter, req *http.Request) {
	rw.Header().Set("Content-Type", "text/plain; charset=utf-8")
	r, w, err := os.Pipe()
	if err != nil {
		fmt.Fprintf(rw, "pipe: %s\n", err)
		return
	}

	p, err := os.StartProcess("/bin/date", []string{"date"}, &os.ProcAttr{Files: []*os.File{nil, w, w}})
	defer r.Close()
	w.Close()
	if err != nil {
		fmt.Fprintf(rw, "fork/exec: %s\n", err)
		return
	}
	defer p.Release()
	io.Copy(rw, r)
	wait, err := p.Wait(0)
	if err != nil {
		fmt.Fprintf(rw, "wait: %s\n", err)
		return
	}
	if !wait.Exited() || wait.ExitStatus() != 0 {
		fmt.Fprintf(rw, "date: %v\n", wait)
		return
	}
}

func Logger(w http.ResponseWriter, req *http.Request) {
	log.Print(req.URL.Raw)
	w.WriteHeader(404)
	w.Write([]byte("oops"))
}

var webroot = flag.String("root", "/home/rsc", "web root directory")

func main() {
	flag.Parse()

	// The counter is published as a variable directly.
	ctr := new(Counter)
	http.Handle("/counter", ctr)
	expvar.Publish("counter", ctr)

	http.Handle("/", http.HandlerFunc(Logger))
	http.Handle("/go/", http.StripPrefix("/go/", http.FileServer(http.Dir(*webroot))))
	http.Handle("/flags", http.HandlerFunc(FlagServer))
	http.Handle("/args", http.HandlerFunc(ArgServer))
	http.Handle("/go/hello", http.HandlerFunc(HelloServer))
	http.Handle("/chan", ChanCreate())
	http.Handle("/date", http.HandlerFunc(DateServer))
	err := http.ListenAndServe(":12345", nil)
	if err != nil {
		log.Panicln("ListenAndServe:", err)
	}
}
<|endoftext|>


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

package math

/*
	The algorithm is based in part on "Optimal Partitioning of
	Newton's Method for Calculating Roots", by Gunter Meinardus
	and G. D. Taylor, Mathematics of Computation © 1980 American
	Mathematical Society.
	(http://www.jstor.org/stable/2006387?seq=9, accessed 11-Feb-2010)
*/

// Cbrt returns the cube root of its argument.
//
// Special cases are:
//	Cbrt(±0) = ±0
//	Cbrt(±Inf) = ±Inf
//	Cbrt(NaN) = NaN
func Cbrt(x float64) float64 {
	const (
		A1 = 1.662848358e-01
		A2 = 1.096040958e+00
		A3 = 4.105032829e-01
		A4 = 5.649335816e-01
		B1 = 2.639607233e-01
		B2 = 8.699282849e-01
		B3 = 1.629083358e-01
		B4 = 2.824667908e-01
		C1 = 4.190115298e-01
		C2 = 6.904625373e-01
		C3 = 6.46502159e-02
		C4 = 1.412333954e-01
	)
	// TODO(rsc): Remove manual inlining of IsNaN, IsInf
	// when compiler does it for us
	// special cases
	switch {
	case x == 0 || x != x || x < -MaxFloat64 || x > MaxFloat64: // x == 0 || IsNaN(x) || IsInf(x, 0):
		return x
	}
	sign := false
	if x < 0 {
		x = -x
		sign = true
	}
	// Reduce argument
	f, e := Frexp(x)
	m := e % 3
	if m > 0 {
		m -= 3
		e -= m // e is multiple of 3
	}
	f = Ldexp(f, m) // 0.125 <= f < 1.0

	// Estimate cube root
	switch m {
	case 0: // 0.5 <= f < 1.0
		f = A1*f + A2 - A3/(A4+f)
	case -1: // 0.25 <= f < 0.5
		f = B1*f + B2 - B3/(B4+f)
	default: // 0.125 <= f < 0.25
		f = C1*f + C2 - C3/(C4+f)
	}
	y := Ldexp(f, e/3) // e/3 = exponent of cube root

	// Iterate
	s := y * y * y
	t := s + x
	y *= (t + x) / (s + t)
	// Reiterate
	s = (y*y*y - x) / x
	y -= y * (((14.0/81.0)*s-(2.0/9.0))*s + (1.0 / 3.0)) * s
	if sign {
		y = -y
	}
	return y
}
<commit_msg>math: faster Cbrt<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package math

/*
	The algorithm is based in part on "Optimal Partitioning of
	Newton's Method for Calculating Roots", by Gunter Meinardus
	and G. D. Taylor, Mathematics of Computation © 1980 American
	Mathematical Society.
	(http://www.jstor.org/stable/2006387?seq=9, accessed 11-Feb-2010)
*/

// Cbrt returns the cube root of its argument.
//
// Special cases are:
//	Cbrt(±0) = ±0
//	Cbrt(±Inf) = ±Inf
//	Cbrt(NaN) = NaN
func Cbrt(x float64) float64 {
	const (
		A1 = 1.662848358e-01
		A2 = 1.096040958e+00
		A3 = 4.105032829e-01
		A4 = 5.649335816e-01
		B1 = 2.639607233e-01
		B2 = 8.699282849e-01
		B3 = 1.629083358e-01
		B4 = 2.824667908e-01
		C1 = 4.190115298e-01
		C2 = 6.904625373e-01
		C3 = 6.46502159e-02
		C4 = 1.412333954e-01
	)
	// TODO(rsc): Remove manual inlining of IsNaN, IsInf
	// when compiler does it for us
	// special cases
	switch {
	case x == 0 || x != x || x < -MaxFloat64 || x > MaxFloat64: // x == 0 || IsNaN(x) || IsInf(x, 0):
		return x
	}
	sign := false
	if x < 0 {
		x = -x
		sign = true
	}
	// Reduce argument and estimate cube root
	f, e := Frexp(x) // 0.5 <= f < 1.0
	m := e % 3
	if m > 0 {
		m -= 3
		e -= m // e is multiple of 3
	}
	switch m {
	case 0: // 0.5 <= f < 1.0
		f = A1*f + A2 - A3/(A4+f)
	case -1:
		f *= 0.5 // 0.25 <= f < 0.5
		f = B1*f + B2 - B3/(B4+f)
	default: // m == -2
		f *= 0.25 // 0.125 <= f < 0.25
		f = C1*f + C2 - C3/(C4+f)
	}
	y := Ldexp(f, e/3) // e/3 = exponent of cube root

	// Iterate
	s := y * y * y
	t := s + x
	y *= (t + x) / (s + t)
	// Reiterate
	s = (y*y*y - x) / x
	y -= y * (((14.0/81.0)*s-(2.0/9.0))*s + (1.0 / 3.0)) * s
	if sign {
		y = -y
	}
	return y
}
<|endoftext|>