<commit_before>package vime
import "fmt"
import "math/rand"
import "time"

type Vime struct {
    points int
    player_x int
    player_y int
    field [][]string
    instruction string
    result string
    last string
    lost bool
    auto bool
    launch_count int
    death string

    Win_condition int
    Field_limit int

    Obstruction string
    Objective string
    Danger string
    Platform string
    Penalty string
    Empty string
    Launcher_r string
    Launcher_l string
    Launcher_u string
    Launcher_d string
    Player string
    Player_alt string

    Obstruction_prob int
    Objective_prob int
    Danger_prob int
    Platform_prob int
    Penalty_prob int
    Launcher_r_prob int
    Launcher_l_prob int
    Launcher_u_prob int
    Launcher_d_prob int
}
func (this *Vime) Initialize() {
    this.lost = false
    this.auto = false
    rand.Seed(time.Now().UTC().UnixNano())

    if this.Field_limit == 0 { this.Field_limit = 31 }
    if this.player_x == 0 { this.player_x = this.Field_limit / 2 }
    if this.player_y == 0 { this.player_y = this.Field_limit / 2 }
    if this.Win_condition == 0 { this.Win_condition = 20 }

    if this.Empty == "" { this.Empty = " " }
    if this.Player == "" { this.Player = "⍟" }
    if this.Player_alt == "" { this.Player_alt = "✪" }
    if this.Obstruction == "" { this.Obstruction = "𝌆" }
    if this.Objective == "" { this.Objective = "+" }
    if this.Danger == "" { this.Danger = "⚠" }
    if this.Penalty == "" { this.Penalty = "-" }
    if this.Platform == "" { this.Platform = "⛀" }
    if this.Launcher_r == "" { this.Launcher_r = "→" }
    if this.Launcher_l == "" { this.Launcher_l = "←" }
    if this.Launcher_u == "" { this.Launcher_u = "↑" }
    if this.Launcher_d == "" { this.Launcher_d = "↓" }

    if this.Danger_prob == 0 { this.Danger_prob = 10 }
    if this.Penalty_prob == 0 { this.Penalty_prob = 20 }
    if this.Obstruction_prob == 0 { this.Obstruction_prob = 15 }
    if this.Platform_prob == 0 { this.Platform_prob = 5 }
    if this.Launcher_l_prob == 0 { this.Launcher_l_prob = 2 }
    if this.Launcher_l_prob == 0 { this.Launcher_l_prob = 2 }
    if this.Launcher_l_prob == 0 { this.Launcher_l_prob = 2 }
    if this.Launcher_l_prob == 0 { this.Launcher_l_prob = 2 }
    if this.Objective_prob == 0 { this.Objective_prob = 4 }

    this.last = this.Empty
    this.field = make([][]string,this.Field_limit)
    for i := 0; i < this.Field_limit; i++ {
        this.field[i] = make([]string,this.Field_limit)
    }
    var objectives_generated int
    objectives_generated = 0
    for i := 0; i < this.Field_limit; i++ {
        for j := 0; j < this.Field_limit; j++ {
            this.field[i][j] = this.Empty
            if rand.Intn(100-0) < this.Danger_prob { this.field[i][j] = this.Danger }
            if rand.Intn(100-0) < this.Penalty_prob { this.field[i][j] = this.Penalty }
            if rand.Intn(100-0) < this.Obstruction_prob { this.field[i][j] = this.Obstruction }
            if rand.Intn(100-0) < this.Platform_prob { this.field[i][j] = this.Platform }
            if rand.Intn(100-0) < this.Launcher_r_prob { this.field[i][j] = this.Launcher_r }
            if rand.Intn(100-0) < this.Launcher_l_prob { this.field[i][j] = this.Launcher_l }
            if rand.Intn(100-0) < this.Launcher_u_prob { this.field[i][j] = this.Launcher_u }
            if rand.Intn(100-0) < this.Launcher_d_prob { this.field[i][j] = this.Launcher_d }
            if rand.Intn(100-0) < this.Objective_prob { this.field[i][j] = this.Objective; objectives_generated += 1 }
            if i < 2 || i >= this.Field_limit - 2 || j < 2 || j >= this.Field_limit - 2 {
                this.field[i][j] = this.Obstruction
            }
        }
    }
    this.field[this.player_x][this.player_y] = this.Player
    if objectives_generated < this.Win_condition + 2 { this.Initialize() }
}
func (this *Vime) flush() {
    for i := 0; i < 100; i++ { fmt.Println("") }
}
func (this *Vime) ping(input int) {
    for i := 0; i < input; i++ {
        this.flush()
            this.field[this.player_y][this.player_x] = this.Player_alt
            this.status()
            time.Sleep(200000000)
            this.field[this.player_y][this.player_x] = this.Player
            this.status()
            time.Sleep(200000000)
    }
}
func (this *Vime) step_on() {
    switch this.result {
        case this.Objective: this.points += 1
        case this.Penalty: this.points -= 1
        case this.Danger: this.lost = true; this.death = "danger"
        case this.Launcher_r, this.Launcher_l, this.Launcher_u, this.Launcher_d:
            this.auto = true
            this.launch_count += 1
    }
    this.field[this.player_y][this.player_x] = this.last
}
func (this *Vime) step_off() {
    this.last = this.field[this.player_y][this.player_x]
    switch this.last {
        case this.Penalty, this.Objective: this.last = this.Empty
        case this.Platform: this.last = this.Obstruction
    }
    this.field[this.player_y][this.player_x] = this.Player
}
func (this *Vime) right(distance int) {
    this.result = this.field[this.player_y][this.player_x+distance]
    if this.result != this.Obstruction {
        this.step_on()
        this.player_x += distance
        this.step_off()
    }
    this.automove()
}
func (this *Vime) down(distance int) {
    this.result = this.field[this.player_y+distance][this.player_x]
    if this.result != this.Obstruction {
        this.step_on()
        this.player_y += distance
        this.step_off()
    }
    this.automove()
}
func (this *Vime) left(distance int) {
    this.result = this.field[this.player_y][this.player_x-distance]
    if this.result != this.Obstruction {
        this.step_on()
        this.player_x -= distance
        this.step_off()
    }
    this.automove()
}
func (this *Vime) up(distance int) {
    this.result = this.field[this.player_y-distance][this.player_x]
    if this.result != this.Obstruction {
        this.step_on()
        this.player_y -= distance
        this.step_off()
    }
    this.automove()
}
func (this *Vime) automove() {
    if this.launch_count > 1000 {
        this.auto = false
        this.lost = true
        this.death = "launch"
    }
    if this.result == this.Obstruction && this.auto == true {
        this.auto = false
        this.lost = true
        this.death = "obstruction"
    }
    if this.auto == true {
        switch this.result {
            case this.Launcher_r:
                if this.last != this.Launcher_l {
                    this.right(1)
                } else {
                    this.lost = true
                    this.death = "launch"
                }
            case this.Launcher_l:
                if this.last != this.Launcher_r {
                    this.left(1)
                } else {
                    this.lost = true
                    this.death = "launch"
                }
            case this.Launcher_u:
                if this.last != this.Launcher_d {
                    this.up(1)
                } else {
                    this.lost = true
                    this.death = "launch"
                }
            case this.Launcher_d:
                if this.last != this.Launcher_u {
                    this.down(1)
                } else {
                    this.lost = true
                    this.death = "launch"
                }
        }
    this.auto = false
    }
}
func (this *Vime) execute() {
    var final_letter string = string(this.instruction[len(this.instruction)-1])
    switch final_letter {
    case "h": this.left(1)
    case "l": this.right(1)
    case "k": this.up(1)
    case "j": this.down(1)
    case "H": this.left(2); this.instruction = "h"
    case "L": this.right(2); this.instruction = "l"
    case "K": this.up(2); this.instruction = "k"
    case "J": this.down(2); this.instruction = "j"
    case "q": this.lost = true
    case "p": this.ping(3)
    }
    this.launch_count = 0
}
func (this *Vime) status() {
        this.flush()
        for i := 0; i < this.Field_limit; i++ {
            switch i {
                case 0: fmt.Println(this.field[i], " ", "Objective: collect", this.Win_condition)
                case 1: fmt.Println(this.field[i], " ", this.Objective, "is your objective")
                case 2: fmt.Println(this.field[i], " ", this.Obstruction, "blocks the way")
                case 3: fmt.Println(this.field[i], " ", this.Penalty, "will harm you")
                case 4: fmt.Println(this.field[i], " ", this.Danger, "will end you")
                case 5: fmt.Println(this.field[i], " ", this.Platform, "will allow you once")
                case 6: fmt.Println(this.field[i], " ", this.Launcher_r, this.Launcher_l, this.Launcher_d, this.Launcher_u, "will move you")
                case 7: fmt.Println(this.field[i], " ", "Move with h j k l")
                case 8: fmt.Println(this.field[i], " ", "Hop with H J K L")
                case 9: fmt.Println(this.field[i], " ", "Quit with q")
                case 10: fmt.Println(this.field[i], " ", "Ping yourself with with p")
                case 11: fmt.Println(this.field[i], " ", "Execute action with \"Enter\"")
                case 12: fmt.Println(this.field[i], " ", "Points:", this.points)
                default: fmt.Println(this.field[i])
            }
        }
}
func (this *Vime) Run() {
    this.ping(5)
    for {
        if this.points >= this.Win_condition { break }
        if this.lost { break }
        this.status()
        fmt.Scanf("%s",&this.instruction)
        this.execute()
    }
        this.flush()
    if this.points >= this.Win_condition {
        fmt.Println("You Win")
    } else {
        switch this.death {
            case "danger":
                fmt.Println("You were ended.")
                fmt.Scanf("%s",&this.instruction)
            case "obstruction":
                fmt.Println("You were launched up against a wall until you lost conciousness.")
                fmt.Scanf("%s",&this.instruction)
            case "launch":
                fmt.Println("As you endlessly bounce between the launchers, you slowly resign yourself to your strange fate.")
                fmt.Println("You are absolutely sure that there are ways to die that are more stupid and trivial than this, but you cannot seem to think of any.")
                fmt.Println("Oh well, plenty of time for that now.")
                fmt.Scanf("%s",&this.instruction)
            default:
                fmt.Println("Game Over")
                fmt.Scanf("%s",&this.instruction)
        }
    }
}
<commit_msg>Made commands customizable.<commit_after>package vime
import "fmt"
import "math/rand"
import "time"
import "strings"

type Vime struct {
    points int
    player_x int
    player_y int
    field [][]string
    instruction string
    result string
    last string
    lost bool
    auto bool
    launch_count int
    death string

    Field_limit int
    Win_condition int

    Obstruction string
    Objective string
    Danger string
    Platform string
    Penalty string
    Empty string
    Launcher_r string
    Launcher_l string
    Launcher_u string
    Launcher_d string
    Player string
    Player_alt string

    // Set these to -1 to disable them.
    // A 0 value gives them default values during initialization.
    Obstruction_prob int
    Objective_prob int
    Danger_prob int
    Platform_prob int
    Penalty_prob int
    Launcher_r_prob int
    Launcher_l_prob int
    Launcher_u_prob int
    Launcher_d_prob int

    Key_r string
    Key_l string
    Key_u string
    Key_d string
    Key_R string
    Key_L string
    Key_U string
    Key_D string
    Key_ping string
    Key_quit string
}
func (this *Vime) Initialize() {
    this.lost = false
    this.auto = false
    rand.Seed(time.Now().UTC().UnixNano())

    if this.Field_limit == 0 { this.Field_limit = 31 }
    if this.player_x == 0 { this.player_x = this.Field_limit / 2 }
    if this.player_y == 0 { this.player_y = this.Field_limit / 2 }
    if this.Win_condition == 0 { this.Win_condition = 20 }

    if this.Empty == "" { this.Empty = " " }
    if this.Player == "" { this.Player = "⍟" }
    if this.Danger == "" { this.Danger = "⚠" }
    if this.Penalty == "" { this.Penalty = "-" }
    if this.Platform == "" { this.Platform = "⛀" }
    if this.Objective == "" { this.Objective = "+" }
    if this.Launcher_r == "" { this.Launcher_r = "→" }
    if this.Launcher_l == "" { this.Launcher_l = "←" }
    if this.Launcher_u == "" { this.Launcher_u = "↑" }
    if this.Launcher_d == "" { this.Launcher_d = "↓" }
    if this.Player_alt == "" { this.Player_alt = "✪" }
    if this.Obstruction == "" { this.Obstruction = "𝌆" }

    if this.Danger_prob == 0 { this.Danger_prob = 10 }
    if this.Penalty_prob == 0 { this.Penalty_prob = 20 }
    if this.Platform_prob == 0 { this.Platform_prob = 5 }
    if this.Objective_prob == 0 { this.Objective_prob = 4 }
    if this.Launcher_l_prob == 0 { this.Launcher_l_prob = 2 }
    if this.Launcher_l_prob == 0 { this.Launcher_l_prob = 2 }
    if this.Launcher_l_prob == 0 { this.Launcher_l_prob = 2 }
    if this.Launcher_l_prob == 0 { this.Launcher_l_prob = 2 }
    if this.Obstruction_prob == 0 { this.Obstruction_prob = 15 }

    if this.Key_r == "" { this.Key_r = "ldfu" }
    if this.Key_l == "" { this.Key_l = "habo" }
    if this.Key_u == "" { this.Key_u = "kwp." }
    if this.Key_d == "" { this.Key_d = "jsne" }
    if this.Key_R == "" { this.Key_R = "LDFU" }
    if this.Key_L == "" { this.Key_L = "HABO" }
    if this.Key_U == "" { this.Key_U = "KWP>" }
    if this.Key_D == "" { this.Key_D = "JSNE" }
    if this.Key_ping == "" { this.Key_ping = "zZ" }
    if this.Key_quit == "" { this.Key_quit = "qQ" }

    this.last = this.Empty
    this.field = make([][]string,this.Field_limit)
    for i := 0; i < this.Field_limit; i++ {
        this.field[i] = make([]string,this.Field_limit)
    }
    var objectives_generated int
    objectives_generated = 0
    for i := 0; i < this.Field_limit; i++ {
        for j := 0; j < this.Field_limit; j++ {
            this.field[i][j] = this.Empty
            if rand.Intn(100-0) < this.Danger_prob { this.field[i][j] = this.Danger }
            if rand.Intn(100-0) < this.Penalty_prob { this.field[i][j] = this.Penalty }
            if rand.Intn(100-0) < this.Obstruction_prob { this.field[i][j] = this.Obstruction }
            if rand.Intn(100-0) < this.Platform_prob { this.field[i][j] = this.Platform }
            if rand.Intn(100-0) < this.Launcher_r_prob { this.field[i][j] = this.Launcher_r }
            if rand.Intn(100-0) < this.Launcher_l_prob { this.field[i][j] = this.Launcher_l }
            if rand.Intn(100-0) < this.Launcher_u_prob { this.field[i][j] = this.Launcher_u }
            if rand.Intn(100-0) < this.Launcher_d_prob { this.field[i][j] = this.Launcher_d }
            if rand.Intn(100-0) < this.Objective_prob { this.field[i][j] = this.Objective; objectives_generated += 1 }
            if i < 2 || i >= this.Field_limit - 2 || j < 2 || j >= this.Field_limit - 2 {
                if this.field[i][j] == this.Objective { objectives_generated -= 1 }
                this.field[i][j] = this.Obstruction
            }
        }
    }
    this.field[this.player_x][this.player_y] = this.Player
    if objectives_generated < this.Win_condition + 2 { this.Initialize() }
}
func (this *Vime) flush() {
    for i := 0; i < 100; i++ { fmt.Println("") }
}
func (this *Vime) ping(input int) {
    for i := 0; i < input; i++ {
        this.flush()
            this.field[this.player_y][this.player_x] = this.Player_alt
            this.status()
            time.Sleep(200000000)
            this.field[this.player_y][this.player_x] = this.Player
            this.status()
            time.Sleep(200000000)
    }
}
func (this *Vime) step_on() {
    switch this.result {
        case this.Objective: this.points += 1
        case this.Penalty: this.points -= 1
        case this.Danger: this.lost = true; this.death = "danger"
        case this.Launcher_r, this.Launcher_l, this.Launcher_u, this.Launcher_d:
            this.auto = true
            this.launch_count += 1
    }
    this.field[this.player_y][this.player_x] = this.last
}
func (this *Vime) step_off() {
    this.last = this.field[this.player_y][this.player_x]
    switch this.last {
        case this.Penalty, this.Objective: this.last = this.Empty
        case this.Platform: this.last = this.Obstruction
    }
    this.field[this.player_y][this.player_x] = this.Player
}
func (this *Vime) right(distance int) {
    this.result = this.field[this.player_y][this.player_x+distance]
    if this.result != this.Obstruction {
        this.step_on()
        this.player_x += distance
        this.step_off()
    }
    this.automove()
}
func (this *Vime) down(distance int) {
    this.result = this.field[this.player_y+distance][this.player_x]
    if this.result != this.Obstruction {
        this.step_on()
        this.player_y += distance
        this.step_off()
    }
    this.automove()
}
func (this *Vime) left(distance int) {
    this.result = this.field[this.player_y][this.player_x-distance]
    if this.result != this.Obstruction {
        this.step_on()
        this.player_x -= distance
        this.step_off()
    }
    this.automove()
}
func (this *Vime) up(distance int) {
    this.result = this.field[this.player_y-distance][this.player_x]
    if this.result != this.Obstruction {
        this.step_on()
        this.player_y -= distance
        this.step_off()
    }
    this.automove()
}
func (this *Vime) automove() {
    if this.launch_count > 1000 {
        this.auto = false
        this.lost = true
        this.death = "launch"
    }
    if this.result == this.Obstruction && this.auto == true {
        this.auto = false
        this.lost = true
        this.death = "obstruction"
    }
    if this.auto == true {
        switch this.result {
            case this.Launcher_r:
                if this.last != this.Launcher_l {
                    this.right(1)
                } else {
                    this.lost = true
                    this.death = "launch"
                }
            case this.Launcher_l:
                if this.last != this.Launcher_r {
                    this.left(1)
                } else {
                    this.lost = true
                    this.death = "launch"
                }
            case this.Launcher_u:
                if this.last != this.Launcher_d {
                    this.up(1)
                } else {
                    this.lost = true
                    this.death = "launch"
                }
            case this.Launcher_d:
                if this.last != this.Launcher_u {
                    this.down(1)
                } else {
                    this.lost = true
                    this.death = "launch"
                }
        }
    this.auto = false
    }
}
func (this *Vime) execute() {
    var final_letter string = string(this.instruction[len(this.instruction)-1])
    if strings.Contains(this.Key_r, final_letter) { this.right(1) }
    if strings.Contains(this.Key_l, final_letter) { this.left(1) }
    if strings.Contains(this.Key_u, final_letter) { this.up(1) }
    if strings.Contains(this.Key_d, final_letter) { this.down(1) }
    if strings.Contains(this.Key_R, final_letter) { this.right(2); this.instruction = this.Key_r }
    if strings.Contains(this.Key_L, final_letter) { this.left(2); this.instruction = this.Key_l }
    if strings.Contains(this.Key_U, final_letter) { this.up(2); this.instruction = this.Key_u }
    if strings.Contains(this.Key_D, final_letter) { this.down(2); this.instruction = this.Key_d }
    if strings.Contains(this.Key_ping, final_letter) { this.ping(3) }
    if strings.Contains(this.Key_quit, final_letter) { this.lost = true }
    this.launch_count = 0
}
func (this *Vime) status() {
        this.flush()
        for i := 0; i < this.Field_limit; i++ {
            switch i {
                case 0: fmt.Println(this.field[i], " ", "Objective: collect", this.Win_condition)
                case 1: fmt.Println(this.field[i], " ", this.Objective, "is your objective")
                case 2: fmt.Println(this.field[i], " ", this.Obstruction, "obstructs you")
                case 3: fmt.Println(this.field[i], " ", this.Penalty, "is counterproductive")
                case 4: fmt.Println(this.field[i], " ", this.Danger, "will end you")
                case 5: fmt.Println(this.field[i], " ", this.Platform, "will allow you once")
                case 6: fmt.Println(this.field[i], " ", this.Launcher_r, this.Launcher_l, this.Launcher_d, this.Launcher_u, "will move you")
                case 7: fmt.Println(this.field[i], " ", "Quit with q (unless changed)")
                case 8: fmt.Println(this.field[i], " ", "Ping yourself with with z (unless changed)")
                case 9: fmt.Println(this.field[i], " ", "Execute action with \"Enter\"")
                case 10: fmt.Println(this.field[i], " ", "Points:", this.points)
                default: fmt.Println(this.field[i])
            }
        }
}
func (this *Vime) Run() {
    this.ping(5)
    for {
        if this.points >= this.Win_condition { break }
        if this.lost { break }
        this.status()
        fmt.Scanf("%s",&this.instruction)
        this.execute()
    }
        this.flush()
    if this.points >= this.Win_condition {
        fmt.Println("You Win")
    } else {
        switch this.death {
            case "danger":
                fmt.Println("You were ended.")
                fmt.Scanf("%s",&this.instruction)
            case "obstruction":
                fmt.Println("You were launched up against a wall until you lost conciousness.")
                fmt.Scanf("%s",&this.instruction)
            case "launch":
                fmt.Println("As you endlessly bounce between the launchers, you slowly resign yourself to your strange fate.")
                fmt.Println("You are absolutely sure that there are ways to die that are more stupid and trivial than this, but you cannot seem to think of any.")
                fmt.Println("Oh well, plenty of time for that now.")
                fmt.Scanf("%s",&this.instruction)
            default:
                fmt.Println("Game Over")
                fmt.Scanf("%s",&this.instruction)
        }
    }
}
<|endoftext|>


<commit_before>package usbtmc

import (
	"errors"
	"regexp"
	"strconv"
	"strings"
)

type VisaResource struct {
	resourceString string
	interfaceType  string
	boardIndex     uint16
	manufacturerId uint16
	modelCode      uint16
	serialNumber   string
	interfaceIndex uint16
	resourceClass  string
}

func NewVisaResource(resourceString string) (visa *VisaResource, err error) {
	visa = &VisaResource{
		resourceString: resourceString,
		interfaceType:  "",
		boardIndex:     0,
		manufacturerId: 0,
		modelCode:      0,
		serialNumber:   "",
		interfaceIndex: 0,
		resourceClass:  "",
	}
	regString := `^(?P<interfaceType>[A-Za-z]+)(?P<boardIndex>\d*)::` +
		`(?P<manufacturerId>[^\s:]+)::` +
		`(?P<modelCode>[^\s:]+)` +
		`(::(?P<serialNumber>[^\s:]+))?` +
		`::(?P<resourceClass>[^\s:]+)$`

	re := regexp.MustCompile(regString)
	res := re.FindStringSubmatch(resourceString)
	subexpNames := re.SubexpNames()
	matchMap := map[string]string{}
	for i, n := range res {
		matchMap[subexpNames[i]] = string(n)
	}

	if strings.ToUpper(matchMap["interfaceType"]) != "USB" {
		return visa, errors.New("visa: interface type was not usb")
	} else {
		visa.interfaceType = "USB"
	}

	if matchMap["boardIndex"] != "" {
		boardIndex, err := strconv.ParseUint(matchMap["boardIndex"], 0, 0)
		if err != nil {
			return visa, errors.New("visa: boardIndex error")
		}
		visa.boardIndex = uint16(boardIndex)
	}

	if matchMap["manufacturerId"] != "" {
		manufacturerId, err := strconv.ParseUint(matchMap["manufacturerId"], 0, 0)
		if err != nil {
			return visa, errors.New("visa: manufacturerId error")
		}
		visa.manufacturerId = uint16(manufacturerId)
	}

	if matchMap["modelCode"] != "" {
		modelCode, err := strconv.ParseUint(matchMap["modelCode"], 0, 0)
		if err != nil {
			return visa, errors.New("visa: modelCode error")
		}
		visa.modelCode = uint16(modelCode)
	}

	visa.serialNumber = matchMap["serialNumber"]

	if strings.ToUpper(matchMap["resourceClass"]) != "INSTR" {
		return visa, errors.New("visa: resource class was not instr")
	} else {
		visa.resourceClass = "INSTR"
	}

	return visa, nil

}
<commit_msg>Expect 16 bits when parsing visa numbers<commit_after>package usbtmc

import (
	"errors"
	"regexp"
	"strconv"
	"strings"
)

type VisaResource struct {
	resourceString string
	interfaceType  string
	boardIndex     uint16
	manufacturerId uint16
	modelCode      uint16
	serialNumber   string
	interfaceIndex uint16
	resourceClass  string
}

func NewVisaResource(resourceString string) (visa *VisaResource, err error) {
	visa = &VisaResource{
		resourceString: resourceString,
		interfaceType:  "",
		boardIndex:     0,
		manufacturerId: 0,
		modelCode:      0,
		serialNumber:   "",
		interfaceIndex: 0,
		resourceClass:  "",
	}
	regString := `^(?P<interfaceType>[A-Za-z]+)(?P<boardIndex>\d*)::` +
		`(?P<manufacturerId>[^\s:]+)::` +
		`(?P<modelCode>[^\s:]+)` +
		`(::(?P<serialNumber>[^\s:]+))?` +
		`::(?P<resourceClass>[^\s:]+)$`

	re := regexp.MustCompile(regString)
	res := re.FindStringSubmatch(resourceString)
	subexpNames := re.SubexpNames()
	matchMap := map[string]string{}
	for i, n := range res {
		matchMap[subexpNames[i]] = string(n)
	}

	if strings.ToUpper(matchMap["interfaceType"]) != "USB" {
		return visa, errors.New("visa: interface type was not usb")
	} else {
		visa.interfaceType = "USB"
	}

	if matchMap["boardIndex"] != "" {
		boardIndex, err := strconv.ParseUint(matchMap["boardIndex"], 0, 16)
		if err != nil {
			return visa, errors.New("visa: boardIndex error")
		}
		visa.boardIndex = uint16(boardIndex)
	}

	if matchMap["manufacturerId"] != "" {
		manufacturerId, err := strconv.ParseUint(matchMap["manufacturerId"], 0, 16)
		if err != nil {
			return visa, errors.New("visa: manufacturerId error")
		}
		visa.manufacturerId = uint16(manufacturerId)
	}

	if matchMap["modelCode"] != "" {
		modelCode, err := strconv.ParseUint(matchMap["modelCode"], 0, 16)
		if err != nil {
			return visa, errors.New("visa: modelCode error")
		}
		visa.modelCode = uint16(modelCode)
	}

	visa.serialNumber = matchMap["serialNumber"]

	if strings.ToUpper(matchMap["resourceClass"]) != "INSTR" {
		return visa, errors.New("visa: resource class was not instr")
	} else {
		visa.resourceClass = "INSTR"
	}

	return visa, nil

}
<|endoftext|>


<commit_before>package main

import (
	"html/template"
	"io/ioutil"
	"net/http"
)

const (
	viewPath = "/view/"
	editPath = "/edit/"
	savePath = "/save/"
)

type Page struct {
	Title string
	Body  []byte
}

func (p *Page) save() error {
	filename := p.Title + ".txt"
	return ioutil.WriteFile(filename, p.Body, 0600)
}

func loadPage(title string) (*Page, error) {
	filename := title + ".txt"
	body, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}

	return &Page{title, body}, err
}

func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
	t, _ := template.ParseFiles(tmpl)
	t.Execute(w, p)
}

func viewHandler(w http.ResponseWriter, r *http.Request) {
	title := r.URL.Path[len(viewPath):]
	page, err := loadPage(title)
	if err !=  nil {
		http.Redirect(w, r, editPath+title, http.StatusFound)
		return
	}

	renderTemplate(w, "view.html", page)
}

func editHandler(w http.ResponseWriter, r *http.Request) {
	title := r.URL.Path[len(editPath):]
	page, err := loadPage(title)
	if err != nil {
		page = &Page{title, nil}
	}

	renderTemplate(w, "edit.html", page)
}

func saveHandler(w http.ResponseWriter, r *http.Request) {
}

func main() {
	page := &Page{"TestPage", []byte("This is a sample page.")}
	page.save()

	http.HandleFunc(viewPath, viewHandler)
	http.HandleFunc(editPath, editHandler)
	http.HandleFunc(savePath, saveHandler)
	http.ListenAndServe("localhost:8080", nil)
}
<commit_msg>Add /save/ functionality<commit_after>package main

import (
	"html/template"
	"io/ioutil"
	"net/http"
)

const (
	viewPath = "/view/"
	editPath = "/edit/"
	savePath = "/save/"
)

type Page struct {
	Title string
	Body  []byte
}

func (p *Page) save() error {
	filename := p.Title + ".txt"
	return ioutil.WriteFile(filename, p.Body, 0600)
}

func loadPage(title string) (*Page, error) {
	filename := title + ".txt"
	body, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}

	return &Page{title, body}, err
}

func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
	t, _ := template.ParseFiles(tmpl)
	t.Execute(w, p)
}

func viewHandler(w http.ResponseWriter, r *http.Request) {
	title := r.URL.Path[len(viewPath):]
	page, err := loadPage(title)
	if err != nil {
		http.Redirect(w, r, editPath+title, http.StatusFound)
		return
	}

	renderTemplate(w, "view.html", page)
}

func editHandler(w http.ResponseWriter, r *http.Request) {
	title := r.URL.Path[len(editPath):]
	page, err := loadPage(title)
	if err != nil {
		page = &Page{title, nil}
	}

	renderTemplate(w, "edit.html", page)
}

func saveHandler(w http.ResponseWriter, r *http.Request) {
	title := r.URL.Path[len(savePath):]
	body := r.FormValue("body")

	p := &Page{title, []byte(body)}
	p.save()

	http.Redirect(w, r, "/view/"+title, http.StatusFound)
}

func main() {
	page := &Page{"TestPage", []byte("This is a sample page.")}
	page.save()

	http.HandleFunc(viewPath, viewHandler)
	http.HandleFunc(editPath, editHandler)
	http.HandleFunc(savePath, saveHandler)
	http.ListenAndServe("localhost:8080", nil)
}
<|endoftext|>


<commit_before>// Copyright 2010 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 (
	"html/template"
	"io/ioutil"
	"net/http"
)

type Page struct {
	Title string
	Body  []byte
}

func (p *Page) save() error {
	filename := p.Title + ".txt"
	return ioutil.WriteFile(filename, p.Body, 0600)
}

func loadPage(title string) (*Page, error) {
	filename := title + ".txt"
	body, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	return &Page{Title: title, Body: body}, nil
}

var templates = template.Must(template.ParseFiles("edit.html", "view.html"))

func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
	err := templates.ExecuteTemplate(w, tmpl+".html", p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func viewHandler(w http.ResponseWriter, r *http.Request) {
	title := r.URL.Path[len("/view/"):]
	p, err := loadPage(title)
	if err != nil {
		http.Redirect(w, r, "/edit/"+title, http.StatusFound)
		return
	}
	renderTemplate(w, "view", p)
}

func editHandler(w http.ResponseWriter, r *http.Request) {
	title := r.URL.Path[len("/edit/"):]
	p, err := loadPage(title)
	if err != nil {
		p = &Page{Title: title}
	}
	renderTemplate(w, "edit", p)
}

func saveHandler(w http.ResponseWriter, r *http.Request) {
	title := r.URL.Path[len("/save/"):]
	body := r.FormValue("body")
	p := &Page{Title: title, Body: []byte(body)}
	err := p.save()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	http.Redirect(w, r, "/view/"+title, http.StatusFound)
}

func main() {
	http.HandleFunc("/view/", viewHandler)
	http.HandleFunc("/edit/", editHandler)
	http.HandleFunc("/save/", saveHandler)
	http.ListenAndServe(":8080", nil)
}
<commit_msg>Add URL validation<commit_after>// Copyright 2010 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 (
	"html/template"
	"io/ioutil"
	"net/http"
	"regexp"
	"errors"
)

type Page struct {
	Title string
	Body  []byte
}

func (p *Page) save() error {
	filename := p.Title + ".txt"
	return ioutil.WriteFile(filename, p.Body, 0600)
}

func loadPage(title string) (*Page, error) {
	filename := title + ".txt"
	body, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	return &Page{Title: title, Body: body}, nil
}

var templates = template.Must(template.ParseFiles("edit.html", "view.html"))

func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
	err := templates.ExecuteTemplate(w, tmpl+".html", p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func viewHandler(w http.ResponseWriter, r *http.Request) {
	title, err := getTitle(w, r)
	if err != nil {
		return
	}
	p, err := loadPage(title)
	if err != nil {
		http.Redirect(w, r, "/edit/"+title, http.StatusFound)
		return
	}
	renderTemplate(w, "view", p)
}

func editHandler(w http.ResponseWriter, r *http.Request) {
	title, err := getTitle(w, r)
	if err != nil {
		return
	}
	p, err := loadPage(title)
	if err != nil {
		p = &Page{Title: title}
	}
	renderTemplate(w, "edit", p)
}

func saveHandler(w http.ResponseWriter, r *http.Request) {
	title, err := getTitle(w, r)
	if err != nil {
		return
	}
	body := r.FormValue("body")
	p := &Page{Title: title, Body: []byte(body)}
	err = p.save()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	http.Redirect(w, r, "/view/"+title, http.StatusFound)
}

var validPath = regexp.MustCompile("^/(edit|save|view)/([a-zA-Z0-9]+)$")

func getTitle(w http.ResponseWriter, r *http.Request) (string, error) {
	m := validPath.FindStringSubmatch(r.URL.Path)
	if m == nil {
		http.NotFound(w, r)
		return "", errors.New("Invalid Page Title")
	}
	return m[2], nil // The title is the second subexpression.
}

func main() {
	http.HandleFunc("/view/", viewHandler)
	http.HandleFunc("/edit/", editHandler)
	http.HandleFunc("/save/", saveHandler)
	http.ListenAndServe(":8080", nil)
}
<|endoftext|>


<commit_before>// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://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 wire contains directives for Wire code generation.
// For an overview of working with Wire, see the user guide at
// https://github.com/google/wire/blob/master/docs/guide.md
//
// The directives in this package are used as input to the Wire code generation
// tool. The entry point of Wire's analysis are injector functions: function
// templates denoted by only containing a call to Build. The arguments to Build
// describes a set of providers and the Wire code generation tool builds a
// directed acylic graph of the providers' output types. The generated code will
// fill in the function template by using the providers from the provider set to
// instantiate any needed types.
package wire

// ProviderSet is a marker type that collects a group of providers.
type ProviderSet struct{}

// NewSet creates a new provider set that includes the providers in its
// arguments. Each argument is a function value, a provider set, a call to
// Struct, a call to Bind, a call to Value, a call to InterfaceValue or a call
// to FieldsOf.
//
// Passing a function value to NewSet declares that the function's first
// return value type will be provided by calling the function. The arguments
// to the function will come from the providers for their types. As such, all
// the function's parameters must be of non-identical types. The function may
// optionally return an error as its last return value and a cleanup function
// as the second return value. A cleanup function must be of type func() and is
// guaranteed to be called before the cleanup function of any of the
// provider's inputs. If any provider returns an error, the injector function
// will call all the appropriate cleanup functions and return the error from
// the injector function.
//
// Passing a ProviderSet to NewSet is the same as if the set's contents
// were passed as arguments to NewSet directly.
//
// The behavior of passing the result of a call to other functions in this
// package are described in their respective doc comments.
//
// For compatibility with older versions of Wire, passing a struct value of type
// S to NewSet declares that both S and *S will be provided by creating a new
// value of the appropriate type by filling in each field of S using the
// provider of the field's type. This form is deprecated and will be removed in
// a future version of Wire: new providers sets should use wire.Struct.
func NewSet(...interface{}) ProviderSet {
	return ProviderSet{}
}

// Build is placed in the body of an injector function template to declare the
// providers to use. The Wire code generation tool will fill in an
// implementation of the function. The arguments to Build are interpreted the
// same as NewSet: they determine the provider set presented to Wire's
// dependency graph. Build returns an error message that can be sent to a call
// to panic().
//
// The parameters of the injector function are used as inputs in the dependency
// graph.
//
// Similar to provider functions passed into NewSet, the first return value is
// the output of the injector function, the optional second return value is a
// cleanup function, and the optional last return value is an error. If any of
// the provider functions in the injector function's provider set return errors
// or cleanup functions, the corresponding return value must be present in the
// injector function template.
//
// Examples:
//
//	func injector(ctx context.Context) (*sql.DB, error) {
//		wire.Build(otherpkg.FooSet, myProviderFunc)
//		return nil, nil
//	}
//
//	func injector(ctx context.Context) (*sql.DB, error) {
//		panic(wire.Build(otherpkg.FooSet, myProviderFunc))
//	}
func Build(...interface{}) string {
	return "implementation not generated, run wire"
}

// A Binding maps an interface to a concrete type.
type Binding struct{}

// Bind declares that a concrete type should be used to satisfy a dependency on
// the type of iface. iface must be a pointer to an interface type, to must be a
// pointer to a concrete type.
//
// Example:
//
//	type Fooer interface {
//		Foo()
//	}
//
//	type MyFoo struct{}
//
//	func (MyFoo) Foo() {}
//
//	var MySet = wire.NewSet(
//		wire.Struct(new(MyFoo))
//		wire.Bind(new(Fooer), new(MyFoo)))
func Bind(iface, to interface{}) Binding {
	return Binding{}
}

// bindToUsePointer is detected by the wire tool to indicate that Bind's second argument should take a pointer.
// See https://github.com/google/wire/issues/120 for details.
const bindToUsePointer = true

// A ProvidedValue is an expression that is copied to the generated injector.
type ProvidedValue struct{}

// Value binds an expression to provide the type of the expression.
// The expression may not be an interface value; use InterfaceValue for that.
//
// Example:
//
//	var MySet = wire.NewSet(wire.Value([]string(nil)))
func Value(interface{}) ProvidedValue {
	return ProvidedValue{}
}

// InterfaceValue binds an expression to provide a specific interface type.
// The first argument is a pointer to the interface which user wants to provide.
// The second argument is the actual variable value whose type implements the
// interface.
//
// Example:
//
//	var MySet = wire.NewSet(wire.InterfaceValue(new(io.Reader), os.Stdin))
func InterfaceValue(typ interface{}, x interface{}) ProvidedValue {
	return ProvidedValue{}
}

// A StructProvider represents a named struct.
type StructProvider struct{}

// Struct specifies that the given struct type will be provided by filling in the fields
// in the struct that have the names given. Each of the arguments must be a name
// to the field they wish to reference. As a special case, if a single name "*"
// is given, then all of the fields in the struct will be filled in.
//
// For example:
//
//  type S struct {
//    MyFoo *Foo
//    MyBar *Bar
//  }
//  var Set = wire.NewSet(wire.Struct(new(S), "MyFoo")) -> inject only S.MyFoo
//  var Set = wire.NewSet(wire.Struct(new(S), "*")) -> inject all fields
func Struct(structType interface{}, fieldNames ...string) StructProvider {
	return StructProvider{}
}

// StructFields is a collection of the fields from a struct.
type StructFields struct{}

// FieldsOf declares that the fields named of the given struct type will be used
// to provide the types of those fields. The structType argument must be a
// pointer to the struct or a pointer to a pointer to the struct it wishes to reference.
//
// The following example would provide *Foo and *Bar using S.MyFoo and S.MyBar respectively:
//
//  type S struct {
//  	MyFoo *Foo
//  	MyBar *Bar
//  }
//
//  func NewStruct() S { /* ... */ }
//  var Set = wire.NewSet(wire.FieldsOf(new(S), "MyFoo", "MyBar"))
//
//  or
//
//  func NewStruct() *S { /* ... */ }
//  var Set = wire.NewSet(wire.FieldsOf(new(*S), "MyFoo", "MyBar"))
func FieldsOf(structType interface{}, fieldNames ...string) StructFields {
	return StructFields{}
}
<commit_msg>document wire.Struct first argument (#197)<commit_after>// Copyright 2018 The Wire Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://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 wire contains directives for Wire code generation.
// For an overview of working with Wire, see the user guide at
// https://github.com/google/wire/blob/master/docs/guide.md
//
// The directives in this package are used as input to the Wire code generation
// tool. The entry point of Wire's analysis are injector functions: function
// templates denoted by only containing a call to Build. The arguments to Build
// describes a set of providers and the Wire code generation tool builds a
// directed acylic graph of the providers' output types. The generated code will
// fill in the function template by using the providers from the provider set to
// instantiate any needed types.
package wire

// ProviderSet is a marker type that collects a group of providers.
type ProviderSet struct{}

// NewSet creates a new provider set that includes the providers in its
// arguments. Each argument is a function value, a provider set, a call to
// Struct, a call to Bind, a call to Value, a call to InterfaceValue or a call
// to FieldsOf.
//
// Passing a function value to NewSet declares that the function's first
// return value type will be provided by calling the function. The arguments
// to the function will come from the providers for their types. As such, all
// the function's parameters must be of non-identical types. The function may
// optionally return an error as its last return value and a cleanup function
// as the second return value. A cleanup function must be of type func() and is
// guaranteed to be called before the cleanup function of any of the
// provider's inputs. If any provider returns an error, the injector function
// will call all the appropriate cleanup functions and return the error from
// the injector function.
//
// Passing a ProviderSet to NewSet is the same as if the set's contents
// were passed as arguments to NewSet directly.
//
// The behavior of passing the result of a call to other functions in this
// package are described in their respective doc comments.
//
// For compatibility with older versions of Wire, passing a struct value of type
// S to NewSet declares that both S and *S will be provided by creating a new
// value of the appropriate type by filling in each field of S using the
// provider of the field's type. This form is deprecated and will be removed in
// a future version of Wire: new providers sets should use wire.Struct.
func NewSet(...interface{}) ProviderSet {
	return ProviderSet{}
}

// Build is placed in the body of an injector function template to declare the
// providers to use. The Wire code generation tool will fill in an
// implementation of the function. The arguments to Build are interpreted the
// same as NewSet: they determine the provider set presented to Wire's
// dependency graph. Build returns an error message that can be sent to a call
// to panic().
//
// The parameters of the injector function are used as inputs in the dependency
// graph.
//
// Similar to provider functions passed into NewSet, the first return value is
// the output of the injector function, the optional second return value is a
// cleanup function, and the optional last return value is an error. If any of
// the provider functions in the injector function's provider set return errors
// or cleanup functions, the corresponding return value must be present in the
// injector function template.
//
// Examples:
//
//	func injector(ctx context.Context) (*sql.DB, error) {
//		wire.Build(otherpkg.FooSet, myProviderFunc)
//		return nil, nil
//	}
//
//	func injector(ctx context.Context) (*sql.DB, error) {
//		panic(wire.Build(otherpkg.FooSet, myProviderFunc))
//	}
func Build(...interface{}) string {
	return "implementation not generated, run wire"
}

// A Binding maps an interface to a concrete type.
type Binding struct{}

// Bind declares that a concrete type should be used to satisfy a dependency on
// the type of iface. iface must be a pointer to an interface type, to must be a
// pointer to a concrete type.
//
// Example:
//
//	type Fooer interface {
//		Foo()
//	}
//
//	type MyFoo struct{}
//
//	func (MyFoo) Foo() {}
//
//	var MySet = wire.NewSet(
//		wire.Struct(new(MyFoo))
//		wire.Bind(new(Fooer), new(MyFoo)))
func Bind(iface, to interface{}) Binding {
	return Binding{}
}

// bindToUsePointer is detected by the wire tool to indicate that Bind's second argument should take a pointer.
// See https://github.com/google/wire/issues/120 for details.
const bindToUsePointer = true

// A ProvidedValue is an expression that is copied to the generated injector.
type ProvidedValue struct{}

// Value binds an expression to provide the type of the expression.
// The expression may not be an interface value; use InterfaceValue for that.
//
// Example:
//
//	var MySet = wire.NewSet(wire.Value([]string(nil)))
func Value(interface{}) ProvidedValue {
	return ProvidedValue{}
}

// InterfaceValue binds an expression to provide a specific interface type.
// The first argument is a pointer to the interface which user wants to provide.
// The second argument is the actual variable value whose type implements the
// interface.
//
// Example:
//
//	var MySet = wire.NewSet(wire.InterfaceValue(new(io.Reader), os.Stdin))
func InterfaceValue(typ interface{}, x interface{}) ProvidedValue {
	return ProvidedValue{}
}

// A StructProvider represents a named struct.
type StructProvider struct{}

// Struct specifies that the given struct type will be provided by filling in the fields in the struct that have the names given.
//
// The first argument must be a pointer to the struct type. For a struct type
// Foo, Wire will use field-filling to provide both Foo and *Foo. The remaining
// arguments are field names to fill in. As a special case, if a single name "*"
// is given, then all of the fields in the struct will be filled in.
//
// For example:
//
//  type S struct {
//    MyFoo *Foo
//    MyBar *Bar
//  }
//  var Set = wire.NewSet(wire.Struct(new(S), "MyFoo")) -> inject only S.MyFoo
//  var Set = wire.NewSet(wire.Struct(new(S), "*")) -> inject all fields
func Struct(structType interface{}, fieldNames ...string) StructProvider {
	return StructProvider{}
}

// StructFields is a collection of the fields from a struct.
type StructFields struct{}

// FieldsOf declares that the fields named of the given struct type will be used
// to provide the types of those fields. The structType argument must be a
// pointer to the struct or a pointer to a pointer to the struct it wishes to reference.
//
// The following example would provide *Foo and *Bar using S.MyFoo and S.MyBar respectively:
//
//  type S struct {
//  	MyFoo *Foo
//  	MyBar *Bar
//  }
//
//  func NewStruct() S { /* ... */ }
//  var Set = wire.NewSet(wire.FieldsOf(new(S), "MyFoo", "MyBar"))
//
//  or
//
//  func NewStruct() *S { /* ... */ }
//  var Set = wire.NewSet(wire.FieldsOf(new(*S), "MyFoo", "MyBar"))
func FieldsOf(structType interface{}, fieldNames ...string) StructFields {
	return StructFields{}
}
<|endoftext|>


<commit_before>// Package work eases concurrency patterns by providing often used helpers.
package work

import (
	"runtime"
	"sync"
	"sync/atomic"
)

// NumRoutines defines the maximum number of goroutines to be used per helper.
// It defaults to GOMAXPROCS.
var NumRoutines int

func init() {
	NumRoutines = runtime.GOMAXPROCS(0)
}

// do spawns workers with index 0 to n-1, limiting their numbers by NumRoutines.
func do(n int, worker func(int)) {
	var wg sync.WaitGroup
	if n <= NumRoutines {
		// spawn as many goroutines as number of workers
		wg.Add(n)
		for i := 0; i < n; i++ {
			go func(idx int) {
				worker(idx)
				wg.Done()
			}(i)
		}
		wg.Wait()
		return
	}

	// spawn the maximum number of goroutines
	wg.Add(NumRoutines)
	for i := 0; i < NumRoutines; i++ {
		go func(idx int) {
			for ; idx < n; idx += NumRoutines {
				worker(idx)
			}
			wg.Done()
		}(i)
	}
	wg.Wait()
}

// doWithError spawns workers with index 0 to n-1, limiting their numbers by NumRoutines.
// Similar to do but with error handling.
// The first error encountered aborts all processing and is then returned.
func doWithError(n int, worker func(int) error) error {
	var (
		errv atomic.Value // worker error
		wg   sync.WaitGroup
	)

	if n <= NumRoutines {
		// spawn as many goroutines as number of workers
		wg.Add(n)
		for i := 0; i < n; i++ {
			go func(idx int) {
				if errv.Load() == nil {
					if err := worker(idx); err != nil {
						errv.Store(err)
					}
				}
				wg.Done()
			}(i)
		}
		wg.Wait()

		if err := errv.Load(); err != nil {
			return err.(error)
		}
		return nil
	}

	// spawn the maximum number of goroutines
	wg.Add(NumRoutines)
	for i := 0; i < NumRoutines; i++ {
		go func(idx int) {
			for ; idx < n && errv.Load() == nil; idx += NumRoutines {
				if err := worker(idx); err != nil {
					errv.Store(err)
					break
				}
			}
			wg.Done()
		}(i)
	}
	wg.Wait()

	if err := errv.Load(); err != nil {
		return err.(error)
	}
	return nil
}

// Do spawns workers with index 0 to n-1, limiting their numbers by NumRoutines.
// If finalizer is set, then it is called on the processed items, in increasing index order.
func Do(n int, worker, finalizer func(idx int)) {
	switch n {
	case 0:
		return
	case 1:
		worker(0)
		if finalizer != nil {
			finalizer(0)
		}
		return
	}

	if finalizer == nil {
		do(n, worker)
		return
	}

	var (
		donec   = make(chan struct{}, NumRoutines) // worker throttling
		workc   = make(chan int)                   // results from workers
		wg, wgf sync.WaitGroup
	)

	// initialize the go routine managing the results and
	// dispatching to the finalizer in order
	wgf.Add(1)
	go func() {
		defer wgf.Done()
		// buffer holds results that cannot be finalized yet.
		buffer := make(map[int]struct{})
		pos := 0
		for idx := range workc {
			if pos < idx {
				// out of order result
				buffer[idx] = struct{}{}
				continue
			}
			finalizer(idx)
			// process the results that were already received
			// ensuring they are processed in order
			for {
				pos++
				if pos == n {
					return
				}
				if _, ok := buffer[pos]; !ok {
					// no more result for the current position
					break
				}
				delete(buffer, pos)
				finalizer(pos)
			}
		}
	}()

	// process all items in the list, with a concurrency of NumRoutines
	for i := 0; i < n; i++ {
		wg.Add(1)
		go func(idx int) {
			worker(idx)
			workc <- idx
			<-donec
			wg.Done()
		}(i)
		// throttling
		donec <- struct{}{}
	}

	// done when all go routines are
	wg.Wait()
	close(workc)
	wgf.Wait()

	return
}

// DoWithError spawns workers with index 0 to n-1, limiting their numbers by NumRoutines.
// Similar to Do but with error handling.
// The first error encountered aborts all processing and is then returned.
// If finalizer is set, then it is called on the processed items, in increasing index order.
func DoWithError(n int, worker, finalizer func(idx int) error) error {
	switch n {
	case 0:
		return nil
	case 1:
		if err := worker(0); err != nil {
			return err
		}
		if finalizer != nil {
			return finalizer(0)
		}
		return nil
	}

	if finalizer == nil {
		return doWithError(n, worker)
	}

	var (
		errv    atomic.Value                       // worker/finalizer error
		donec   = make(chan struct{}, NumRoutines) // worker done channel
		workc   = make(chan int)                   // results from workers
		wg, wgf sync.WaitGroup
	)

	// initialize the go routine managing the results and
	// dispatching to the finalizer in order
	wgf.Add(1)
	go func() {
		// buffer holds results that cannot be finalized yet.
		buffer := make(map[int]struct{})
		// current index to be processed
		pos := 0
		// the finalizer routine exits when the channel is closed
		// or when it has completed all work
		for idx := range workc {
			if errv.Load() != nil {
				// upon error, wait for the workers to complete
				// which will close the channel
				continue
			}
			if pos < idx {
				// out of order result
				buffer[idx] = struct{}{}
				continue
			}
			if err := finalizer(pos); err != nil {
				errv.Store(err)
				continue
			}
			// process the results that were already received
			// ensuring they are processed in order
			for {
				pos++
				if pos == n || errv.Load() != nil {
					break
				}
				if _, ok := buffer[pos]; !ok {
					// no more result for the current position
					break
				}
				delete(buffer, pos)
				if err := finalizer(pos); err != nil {
					errv.Store(err)
					break
				}
			}
		}
		wgf.Done()
	}()

	// process all items in the list, with a concurrency of NumRoutines
	for i := 0; i < n; i++ {
		wg.Add(1)
		go func(idx int) {
			if errv.Load() == nil {
				if err := worker(idx); err != nil {
					errv.Store(err)
				} else {
					workc <- idx
				}
			}
			<-donec
			wg.Done()
		}(i)
		// throttling
		donec <- struct{}{}
		if errv.Load() != nil {
			break
		}
	}

	// wait for workers
	wg.Wait()
	// since workc is blocking, the finalizer has received all items
	// so we can safely close it and shutdown the finalizer routine
	close(workc)

	// wait for finalizer
	wgf.Wait()

	if err := errv.Load(); err != nil {
		return err.(error)
	}
	return nil
}
<commit_msg>added DoN and DoNWithError; removed exposed NumRoutines<commit_after>// Package work eases concurrency patterns by providing often used helpers.
package work

import (
	"runtime"
	"sync"
	"sync/atomic"
)

// numRoutines defines the default maximum number of goroutines based on GOMAXPROCS.
var numRoutines int

func init() {
	numRoutines = runtime.GOMAXPROCS(0)
}

// do spawns workers with index 0 to n-1, limiting their numbers by max.
func do(n int, worker func(int), max int) {
	var wg sync.WaitGroup
	if n <= max {
		// spawn as many goroutines as number of workers
		wg.Add(n)
		for i := 0; i < n; i++ {
			go func(idx int) {
				worker(idx)
				wg.Done()
			}(i)
		}
		wg.Wait()
		return
	}

	// spawn the maximum number of goroutines
	wg.Add(max)
	for i := 0; i < max; i++ {
		go func(idx int) {
			for ; idx < n; idx += max {
				worker(idx)
			}
			wg.Done()
		}(i)
	}
	wg.Wait()
}

// doWithError spawns workers with index 0 to n-1, limiting their numbers by max.
// Similar to do but with error handling.
// The first error encountered aborts all processing and is then returned.
func doWithError(n int, worker func(int) error, max int) error {
	var (
		errv atomic.Value // worker error
		wg   sync.WaitGroup
	)

	if n <= max {
		// spawn as many goroutines as number of workers
		wg.Add(n)
		for i := 0; i < n; i++ {
			go func(idx int) {
				if errv.Load() == nil {
					if err := worker(idx); err != nil {
						errv.Store(err)
					}
				}
				wg.Done()
			}(i)
		}
		wg.Wait()

		if err := errv.Load(); err != nil {
			return err.(error)
		}
		return nil
	}

	// spawn the maximum number of goroutines
	wg.Add(max)
	for i := 0; i < max; i++ {
		go func(idx int) {
			for ; idx < n && errv.Load() == nil; idx += max {
				if err := worker(idx); err != nil {
					errv.Store(err)
					break
				}
			}
			wg.Done()
		}(i)
	}
	wg.Wait()

	if err := errv.Load(); err != nil {
		return err.(error)
	}
	return nil
}

// Do spawns workers with index 0 to n-1, limiting their numbers by GOMAXPROCS.
// If finalizer is set, then it is called on the processed items, in increasing index order.
func Do(n int, worker, finalizer func(idx int)) {
	DoN(n, worker, finalizer, numRoutines)
}

// DoN spawns workers with index 0 to n-1, limiting their numbers by max.
// If finalizer is set, then it is called on the processed items, in increasing index order.
func DoN(n int, worker, finalizer func(idx int), max int) {
	switch n {
	case 0:
		return
	case 1:
		worker(0)
		if finalizer != nil {
			finalizer(0)
		}
		return
	}

	if finalizer == nil {
		do(n, worker, max)
		return
	}

	var (
		donec   = make(chan struct{}, max) // worker throttling
		workc   = make(chan int)           // results from workers
		wg, wgf sync.WaitGroup
	)

	// initialize the go routine managing the results and
	// dispatching to the finalizer in order
	wgf.Add(1)
	go func() {
		defer wgf.Done()
		// buffer holds results that cannot be finalized yet.
		buffer := make(map[int]struct{})
		pos := 0
		for idx := range workc {
			if pos < idx {
				// out of order result
				buffer[idx] = struct{}{}
				continue
			}
			finalizer(idx)
			// process the results that were already received
			// ensuring they are processed in order
			for {
				pos++
				if pos == n {
					return
				}
				if _, ok := buffer[pos]; !ok {
					// no more result for the current position
					break
				}
				delete(buffer, pos)
				finalizer(pos)
			}
		}
	}()

	// process all items in the list, with a concurrency of max
	for i := 0; i < n; i++ {
		wg.Add(1)
		go func(idx int) {
			worker(idx)
			workc <- idx
			<-donec
			wg.Done()
		}(i)
		// throttling
		donec <- struct{}{}
	}

	// done when all go routines are
	wg.Wait()
	close(workc)
	wgf.Wait()

	return
}

// DoWithError spawns workers with index 0 to n-1, limiting their numbers by max.
// Similar to Do but with error handling.
// The first error encountered aborts all processing and is then returned.
// If finalizer is set, then it is called on the processed items, in increasing index order.
func DoWithError(n int, worker, finalizer func(idx int) error) error {
	return DoNWithError(n, worker, finalizer, numRoutines)
}

// DoNWithError spawns workers with index 0 to n-1, limiting their numbers by GOMAXPROCS.
// Similar to DoN but with error handling.
// The first error encountered aborts all processing and is then returned.
// If finalizer is set, then it is called on the processed items, in increasing index order.
func DoNWithError(n int, worker, finalizer func(idx int) error, max int) error {
	switch n {
	case 0:
		return nil
	case 1:
		if err := worker(0); err != nil {
			return err
		}
		if finalizer != nil {
			return finalizer(0)
		}
		return nil
	}

	if finalizer == nil {
		return doWithError(n, worker, max)
	}

	var (
		errv    atomic.Value               // worker/finalizer error
		donec   = make(chan struct{}, max) // worker done channel
		workc   = make(chan int)           // results from workers
		wg, wgf sync.WaitGroup
	)

	// initialize the go routine managing the results and
	// dispatching to the finalizer in order
	wgf.Add(1)
	go func() {
		// buffer holds results that cannot be finalized yet.
		buffer := make(map[int]struct{})
		// current index to be processed
		pos := 0
		// the finalizer routine exits when the channel is closed
		// or when it has completed all work
		for idx := range workc {
			if errv.Load() != nil {
				// upon error, wait for the workers to complete
				// which will close the channel
				continue
			}
			if pos < idx {
				// out of order result
				buffer[idx] = struct{}{}
				continue
			}
			if err := finalizer(pos); err != nil {
				errv.Store(err)
				continue
			}
			// process the results that were already received
			// ensuring they are processed in order
			for {
				pos++
				if pos == n || errv.Load() != nil {
					break
				}
				if _, ok := buffer[pos]; !ok {
					// no more result for the current position
					break
				}
				delete(buffer, pos)
				if err := finalizer(pos); err != nil {
					errv.Store(err)
					break
				}
			}
		}
		wgf.Done()
	}()

	// process all items in the list, with a concurrency of max
	for i := 0; i < n; i++ {
		wg.Add(1)
		go func(idx int) {
			if errv.Load() == nil {
				if err := worker(idx); err != nil {
					errv.Store(err)
				} else {
					workc <- idx
				}
			}
			<-donec
			wg.Done()
		}(i)
		// throttling
		donec <- struct{}{}
		if errv.Load() != nil {
			break
		}
	}

	// wait for workers
	wg.Wait()
	// since workc is blocking, the finalizer has received all items
	// so we can safely close it and shutdown the finalizer routine
	close(workc)

	// wait for finalizer
	wgf.Wait()

	if err := errv.Load(); err != nil {
		return err.(error)
	}
	return nil
}
<|endoftext|>


<commit_before>package sms

import (
	"encoding/xml"
	"fmt"
	"log"
	"os"

	xco "github.com/mndrix/go-xco"
)

// xmppProcess is the piece which interacts with the XMPP network and
// converts those communications into values which the rest of
// sms-over-xmpp can understand.
type xmppProcess struct {
	// where to connect to the XMPP server
	host string
	port int

	// credentials for XMPP auth
	name   string
	secret string

	// channels for communicating with the Gateway process
	gatewayRx chan<- *xco.Message
	gatewayTx <-chan *xco.Message
}

// runXmppComponent creates a goroutine for sending and receiving XMPP
// stanzas.  it returns a channel for monitoring the goroutine's
// health.  if that channel closes, the XMPP process has died.
func (x *xmppProcess) run() <-chan struct{} {
	opts := xco.Options{
		Name:         x.name,
		SharedSecret: x.secret,
		Address:      fmt.Sprintf("%s:%d", x.host, x.port),
		Logger:       log.New(os.Stderr, "", log.LstdFlags),
	}

	healthCh := make(chan struct{})
	go x.loop(opts, healthCh)
	return healthCh
}

func (x *xmppProcess) loop(opts xco.Options, healthCh chan<- struct{}) {
	defer func() { close(healthCh) }()

	c, err := xco.NewComponent(opts)
	if err != nil {
		log.Printf("can't create internal XMPP component: %s", err)
		return
	}

	tx, rx, errx := c.RunAsync()
	for {
		select {
		case st := <-rx:
			switch stanza := st.(type) {
			case *xco.Message:
				log.Printf("Message: %+v", stanza)
				if stanza.Body == "" {
					log.Printf("  ignoring message with empty body")
					break
				}
				go func() { x.gatewayRx <- stanza }()
			case *xco.Iq:
				if stanza.IsDiscoInfo() {
					log.Printf("Disco: %+v", stanza)
					ids, features := x.describeService()
					stanza, err = stanza.DiscoInfoReply(ids, features)
					if err == nil {
						go func() { tx <- stanza }()
					}
				} else {
					log.Printf("Iq: %+v", stanza)
				}
			case *xco.Presence:
				log.Printf("Presence: %+v", stanza)
				switch stanza.Type {
				case "probe":
					stanza, err = x.presenceAvailable(stanza)
					if err == nil {
						go func() { tx <- stanza }()
					}
				case "subscribe", "unsubscribe":
					var stanzas []*xco.Presence
					stanzas, err = x.handleSubscription(stanza)
					if err == nil {
						go func() {
							for _, stanza := range stanzas {
								tx <- stanza
							}
						}()
					}
				}
			case *xml.StartElement:
				log.Printf("Unknown: %+v", stanza)
			default:
				panic(fmt.Sprintf("Unexpected stanza type: %#v", st))
			}
		case stanza := <-x.gatewayTx:
			go func() { tx <- stanza }()
		case err = <-errx:
		}

		if err != nil {
			break
		}
	}

	log.Printf("lost XMPP connection: %s", err)
}

func (x *xmppProcess) describeService() ([]xco.DiscoIdentity, []xco.DiscoFeature) {
	ids := []xco.DiscoIdentity{
		{
			Category: "gateway",
			Type:     "sms",
			Name:     "SMS over XMPP",
		},
	}
	features := []xco.DiscoFeature{
		{
			Var: "urn:xmpp:receipts",
		},
	}
	return ids, features
}

func (x *xmppProcess) presenceAvailable(p *xco.Presence) (*xco.Presence, error) {
	stanza := &xco.Presence{
		Header: xco.Header{
			From: p.Header.To,
			To:   p.Header.From,
			ID:   NewId(),
		},
	}
	return stanza, nil
}

func (x *xmppProcess) handleSubscription(p *xco.Presence) ([]*xco.Presence, error) {
	var err error

	// RFC says to use full JIDs
	p.Header.To.ResourcePart = ""
	p.Header.From.ResourcePart = ""

	stanzas := make([]*xco.Presence, 0, 2)
	stanza := &xco.Presence{
		Header: xco.Header{
			From: p.Header.To,
			To:   p.Header.From,
			ID:   NewId(),
		},
	}
	switch p.Type {
	case "subscribe":
		stanza.Type = "subscribed"
		stanzas = append(stanzas, stanza)

		// let user know that we're available
		stanza, err = x.presenceAvailable(p)

		// request a reciprocal subscription
		if err == nil {
			stanzas = append(stanzas, stanza)
			stanza, err = x.requestSubscription(p)
		}
	case "unsubscribe":
		stanza.Type = "unavailable"
	}
	stanzas = append(stanzas, stanza)

	if err != nil {
		return nil, err
	}
	return stanzas, nil
}

func (x *xmppProcess) requestSubscription(p *xco.Presence) (*xco.Presence, error) {
	stanza := &xco.Presence{
		Header: xco.Header{
			From: p.Header.To,
			To:   p.Header.From,
			ID:   NewId(),
		},
		Type: "subscribe",
	}
	return stanza, nil
}
<commit_msg>Remove unused err from return values<commit_after>package sms

import (
	"encoding/xml"
	"fmt"
	"log"
	"os"

	xco "github.com/mndrix/go-xco"
)

// xmppProcess is the piece which interacts with the XMPP network and
// converts those communications into values which the rest of
// sms-over-xmpp can understand.
type xmppProcess struct {
	// where to connect to the XMPP server
	host string
	port int

	// credentials for XMPP auth
	name   string
	secret string

	// channels for communicating with the Gateway process
	gatewayRx chan<- *xco.Message
	gatewayTx <-chan *xco.Message
}

// runXmppComponent creates a goroutine for sending and receiving XMPP
// stanzas.  it returns a channel for monitoring the goroutine's
// health.  if that channel closes, the XMPP process has died.
func (x *xmppProcess) run() <-chan struct{} {
	opts := xco.Options{
		Name:         x.name,
		SharedSecret: x.secret,
		Address:      fmt.Sprintf("%s:%d", x.host, x.port),
		Logger:       log.New(os.Stderr, "", log.LstdFlags),
	}

	healthCh := make(chan struct{})
	go x.loop(opts, healthCh)
	return healthCh
}

func (x *xmppProcess) loop(opts xco.Options, healthCh chan<- struct{}) {
	defer func() { close(healthCh) }()

	c, err := xco.NewComponent(opts)
	if err != nil {
		log.Printf("can't create internal XMPP component: %s", err)
		return
	}

	tx, rx, errx := c.RunAsync()
	for {
		select {
		case st := <-rx:
			switch stanza := st.(type) {
			case *xco.Message:
				log.Printf("Message: %+v", stanza)
				if stanza.Body == "" {
					log.Printf("  ignoring message with empty body")
					break
				}
				go func() { x.gatewayRx <- stanza }()
			case *xco.Iq:
				if stanza.IsDiscoInfo() {
					log.Printf("Disco: %+v", stanza)
					ids, features := x.describeService()
					stanza, err = stanza.DiscoInfoReply(ids, features)
					if err == nil {
						go func() { tx <- stanza }()
					}
				} else {
					log.Printf("Iq: %+v", stanza)
				}
			case *xco.Presence:
				log.Printf("Presence: %+v", stanza)
				switch stanza.Type {
				case "probe":
					stanza = x.presenceAvailable(stanza)
					go func() { tx <- stanza }()
				case "subscribe", "unsubscribe":
					stanzas := x.handleSubscription(stanza)
					go func() {
						for _, stanza := range stanzas {
							tx <- stanza
						}
					}()
				}
			case *xml.StartElement:
				log.Printf("Unknown: %+v", stanza)
			default:
				panic(fmt.Sprintf("Unexpected stanza type: %#v", st))
			}
		case stanza := <-x.gatewayTx:
			go func() { tx <- stanza }()
		case err = <-errx:
		}

		if err != nil {
			break
		}
	}

	log.Printf("lost XMPP connection: %s", err)
}

func (x *xmppProcess) describeService() ([]xco.DiscoIdentity, []xco.DiscoFeature) {
	ids := []xco.DiscoIdentity{
		{
			Category: "gateway",
			Type:     "sms",
			Name:     "SMS over XMPP",
		},
	}
	features := []xco.DiscoFeature{
		{
			Var: "urn:xmpp:receipts",
		},
	}
	return ids, features
}

func (x *xmppProcess) presenceAvailable(p *xco.Presence) *xco.Presence {
	stanza := &xco.Presence{
		Header: xco.Header{
			From: p.Header.To,
			To:   p.Header.From,
			ID:   NewId(),
		},
	}
	return stanza
}

func (x *xmppProcess) handleSubscription(p *xco.Presence) []*xco.Presence {
	// RFC says to use full JIDs
	p.Header.To.ResourcePart = ""
	p.Header.From.ResourcePart = ""

	stanzas := make([]*xco.Presence, 0, 2)
	stanza := &xco.Presence{
		Header: xco.Header{
			From: p.Header.To,
			To:   p.Header.From,
			ID:   NewId(),
		},
	}
	switch p.Type {
	case "subscribe":
		stanza.Type = "subscribed"
		stanzas = append(stanzas, stanza)

		// let user know that we're available
		stanza = x.presenceAvailable(p)
		stanzas = append(stanzas, stanza)

		// request a reciprocal subscription
		stanza = x.requestSubscription(p)
	case "unsubscribe":
		stanza.Type = "unavailable"
	}
	stanzas = append(stanzas, stanza)

	return stanzas
}

func (x *xmppProcess) requestSubscription(p *xco.Presence) *xco.Presence {
	stanza := &xco.Presence{
		Header: xco.Header{
			From: p.Header.To,
			To:   p.Header.From,
			ID:   NewId(),
		},
		Type: "subscribe",
	}
	return stanza
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"

	"github.com/jessevdk/go-flags"
	"github.com/sjoerdsimons/fakemachine"

	"gopkg.in/yaml.v2"
)

func CleanPathAt(path, at string) string {
	if filepath.IsAbs(path) {
		return filepath.Clean(path)
	}

	return filepath.Join(at, path)
}

func CleanPath(path string) string {
	cwd, _ := os.Getwd()
	return CleanPathAt(path, cwd)
}

func CopyFile(src, dst string, mode os.FileMode) error {
	in, err := os.Open(src)
	if err != nil {
		return err
	}
	defer in.Close()
	tmp, err := ioutil.TempFile(filepath.Dir(dst), "")
	if err != nil {
		return err
	}
	_, err = io.Copy(tmp, in)
	if err != nil {
		tmp.Close()
		os.Remove(tmp.Name())
		return err
	}
	if err = tmp.Close(); err != nil {
		os.Remove(tmp.Name())
		return err
	}
	if err = os.Chmod(tmp.Name(), mode); err != nil {
		os.Remove(tmp.Name())
		return err
	}
	return os.Rename(tmp.Name(), dst)
}

func CopyTree(sourcetree, desttree string) {
	fmt.Printf("Overlaying %s on %s\n", sourcetree, desttree)
	walker := func(p string, info os.FileInfo, err error) error {

		if err != nil {
			return err
		}

		suffix, _ := filepath.Rel(sourcetree, p)
		target := path.Join(desttree, suffix)
		switch info.Mode() & os.ModeType {
		case 0:
			fmt.Printf("F> %s\n", p)
			CopyFile(p, target, info.Mode())
		case os.ModeDir:
			fmt.Printf("D> %s -> %s\n", p, target)
			os.Mkdir(target, info.Mode())
		case os.ModeSymlink:
			link, err := os.Readlink(p)
			if err != nil {
				log.Panic("Failed to read symlink %s: %v", suffix, err)
			}
			os.Symlink(link, target)
		default:
			log.Panicf("Not handled /%s %v", suffix, info.Mode())
		}

		return nil
	}

	filepath.Walk(sourcetree, walker)
}

type QemuHelper struct {
	qemusrc    string
	qemutarget string
}

func NewQemuHelper(context YaibContext) QemuHelper {
	q := QemuHelper{}

	switch context.Architecture {
	case "armhf", "armel", "arm":
		q.qemusrc = "/usr/bin/qemu-arm-static"
	case "arm64":
		q.qemusrc = "/usr/bin/qemu-aarch64-static"
	case "amd64":
		/* Dummy, no qemu */
	default:
		log.Panicf("Don't know qemu for Architecture %s", context.Architecture)
	}

	if q.qemusrc != "" {
		q.qemutarget = path.Join(context.rootdir, q.qemusrc)
	}

	return q
}

func (q QemuHelper) Setup() error {
	if q.qemusrc == "" {
		return nil
	}
	return CopyFile(q.qemusrc, q.qemutarget, 755)
}

func (q QemuHelper) Cleanup() {
	if q.qemusrc != "" {
		os.Remove(q.qemutarget)
	}
}

func RunCommand(label, command string, arg ...string) error {
	cmd := exec.Command(command, arg...)

	output, _ := cmd.StdoutPipe()
	stderr, _ := cmd.StderrPipe()

	cmd.Start()

	fmt.Printf("Running %s: %s %v\n", label, command, arg)

	scanner := bufio.NewScanner(output)
	for scanner.Scan() {
		fmt.Printf("%s | %s\n", label, scanner.Text())
	}

	reader := bufio.NewReader(stderr)

	for {
		line, _, err := reader.ReadLine()

		if err == io.EOF {
			break
		} else if err != nil {
			fmt.Printf("FAILED: %v\n", err)
			break
		}
		fmt.Printf("%s E | %s\n", label, line)
	}

	err := cmd.Wait()

	return err
}

func RunCommandInChroot(context YaibContext, label, command string, arg ...string) error {
	options := []string{"-q", "-D", context.rootdir, command}
	options = append(options, arg...)

	q := NewQemuHelper(context)
	q.Setup()
	defer q.Cleanup()

	return RunCommand(label, "systemd-nspawn", options...)
}

type YaibContext struct {
	rootdir      string
	artifactdir  string
	image        string
	imageMntDir  string
	recipeDir    string
	Architecture string
}

type Action interface {
	Verify(context YaibContext)
	PreMachine(context *YaibContext, m *fakemachine.Machine, args *[]string)
	Run(context *YaibContext)
	Cleanup(context YaibContext)
	PostMachine(context YaibContext)
}

type BaseAction struct{}

func (b *BaseAction) Verify(context YaibContext) {}
func (b *BaseAction) PreMachine(context *YaibContext,
	m *fakemachine.Machine,
	args *[]string) {
}
func (b *BaseAction) Run(context *YaibContext)        {}
func (b *BaseAction) Cleanup(context YaibContext)     {}
func (b *BaseAction) PostMachine(context YaibContext) {}

/* the YamlAction just embed the Action interface and implements the
 * UnmarshalYAML function so it can select the concrete implementer of a
 * specific action at unmarshaling time */
type YamlAction struct {
	Action
}

func (y *YamlAction) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var aux struct {
		Action string
	}
	err := unmarshal(&aux)
	if err != nil {
		return err
	}

	switch aux.Action {
	case "debootstrap":
		y.Action = &DebootstrapAction{}
	case "pack":
		y.Action = &PackAction{}
	case "unpack":
		y.Action = &UnpackAction{}
	case "run":
		y.Action = &RunAction{}
	case "apt":
		y.Action = &AptAction{}
	case "overlay":
		y.Action = &OverlayAction{}
	case "setup-image":
		y.Action = &SetupImage{}
	case "raw":
		y.Action = &RawAction{}
	default:
		log.Fatalf("Unknown action: %v", aux.Action)
	}

	unmarshal(y.Action)

	return nil
}

type Recipe struct {
	Architecture string
	Actions      []YamlAction
}

func main() {
	var context YaibContext
	var options struct {
		ArtifactDir   string `long:"artifactdir"`
		InternalImage string `long:"internal-image" hidden:"true"`
	}

	parser := flags.NewParser(&options, flags.Default)
	args, err := parser.Parse()

	if err != nil {
		flagsErr, ok := err.(*flags.Error)
		if ok && flagsErr.Type == flags.ErrHelp {
			os.Exit(0)
		} else {
			fmt.Printf("%v\n", flagsErr)
			os.Exit(1)
		}
	}

	file := args[0]
	file = CleanPath(file)

	context.rootdir = "/scratch/rootdir"
	context.artifactdir = options.ArtifactDir
	context.image = options.InternalImage
	context.recipeDir = path.Dir(file)

	data, err := ioutil.ReadFile(file)
	if err != nil {
		panic(err)
	}

	r := Recipe{}

	err = yaml.Unmarshal(data, &r)
	if err != nil {
		panic(err)
	}

	context.Architecture = r.Architecture

	for _, a := range r.Actions {
		a.Verify(context)
	}

	if !fakemachine.InMachine() {
		m := fakemachine.NewMachine()
		var args []string

		if context.artifactdir == "" {
			context.artifactdir, _ = os.Getwd()
		}

		context.artifactdir = CleanPath(context.artifactdir)

		m.AddVolume(context.artifactdir)
		args = append(args, "--artifactdir", context.artifactdir)

		m.AddVolume(context.recipeDir)
		args = append(args, file)

		for _, a := range r.Actions {
			a.PreMachine(&context, m, &args)
		}

		ret := m.RunInMachineWithArgs(args)

		for _, a := range r.Actions {
			a.PostMachine(context)
		}

		os.Exit(ret)
	}

	for _, a := range r.Actions {
		a.Run(&context)
	}

	for _, a := range r.Actions {
		a.Cleanup(context)
	}
}
<commit_msg>Fail if no recipe given<commit_after>package main

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"

	"github.com/jessevdk/go-flags"
	"github.com/sjoerdsimons/fakemachine"

	"gopkg.in/yaml.v2"
)

func CleanPathAt(path, at string) string {
	if filepath.IsAbs(path) {
		return filepath.Clean(path)
	}

	return filepath.Join(at, path)
}

func CleanPath(path string) string {
	cwd, _ := os.Getwd()
	return CleanPathAt(path, cwd)
}

func CopyFile(src, dst string, mode os.FileMode) error {
	in, err := os.Open(src)
	if err != nil {
		return err
	}
	defer in.Close()
	tmp, err := ioutil.TempFile(filepath.Dir(dst), "")
	if err != nil {
		return err
	}
	_, err = io.Copy(tmp, in)
	if err != nil {
		tmp.Close()
		os.Remove(tmp.Name())
		return err
	}
	if err = tmp.Close(); err != nil {
		os.Remove(tmp.Name())
		return err
	}
	if err = os.Chmod(tmp.Name(), mode); err != nil {
		os.Remove(tmp.Name())
		return err
	}
	return os.Rename(tmp.Name(), dst)
}

func CopyTree(sourcetree, desttree string) {
	fmt.Printf("Overlaying %s on %s\n", sourcetree, desttree)
	walker := func(p string, info os.FileInfo, err error) error {

		if err != nil {
			return err
		}

		suffix, _ := filepath.Rel(sourcetree, p)
		target := path.Join(desttree, suffix)
		switch info.Mode() & os.ModeType {
		case 0:
			fmt.Printf("F> %s\n", p)
			CopyFile(p, target, info.Mode())
		case os.ModeDir:
			fmt.Printf("D> %s -> %s\n", p, target)
			os.Mkdir(target, info.Mode())
		case os.ModeSymlink:
			link, err := os.Readlink(p)
			if err != nil {
				log.Panic("Failed to read symlink %s: %v", suffix, err)
			}
			os.Symlink(link, target)
		default:
			log.Panicf("Not handled /%s %v", suffix, info.Mode())
		}

		return nil
	}

	filepath.Walk(sourcetree, walker)
}

type QemuHelper struct {
	qemusrc    string
	qemutarget string
}

func NewQemuHelper(context YaibContext) QemuHelper {
	q := QemuHelper{}

	switch context.Architecture {
	case "armhf", "armel", "arm":
		q.qemusrc = "/usr/bin/qemu-arm-static"
	case "arm64":
		q.qemusrc = "/usr/bin/qemu-aarch64-static"
	case "amd64":
		/* Dummy, no qemu */
	default:
		log.Panicf("Don't know qemu for Architecture %s", context.Architecture)
	}

	if q.qemusrc != "" {
		q.qemutarget = path.Join(context.rootdir, q.qemusrc)
	}

	return q
}

func (q QemuHelper) Setup() error {
	if q.qemusrc == "" {
		return nil
	}
	return CopyFile(q.qemusrc, q.qemutarget, 755)
}

func (q QemuHelper) Cleanup() {
	if q.qemusrc != "" {
		os.Remove(q.qemutarget)
	}
}

func RunCommand(label, command string, arg ...string) error {
	cmd := exec.Command(command, arg...)

	output, _ := cmd.StdoutPipe()
	stderr, _ := cmd.StderrPipe()

	cmd.Start()

	fmt.Printf("Running %s: %s %v\n", label, command, arg)

	scanner := bufio.NewScanner(output)
	for scanner.Scan() {
		fmt.Printf("%s | %s\n", label, scanner.Text())
	}

	reader := bufio.NewReader(stderr)

	for {
		line, _, err := reader.ReadLine()

		if err == io.EOF {
			break
		} else if err != nil {
			fmt.Printf("FAILED: %v\n", err)
			break
		}
		fmt.Printf("%s E | %s\n", label, line)
	}

	err := cmd.Wait()

	return err
}

func RunCommandInChroot(context YaibContext, label, command string, arg ...string) error {
	options := []string{"-q", "-D", context.rootdir, command}
	options = append(options, arg...)

	q := NewQemuHelper(context)
	q.Setup()
	defer q.Cleanup()

	return RunCommand(label, "systemd-nspawn", options...)
}

type YaibContext struct {
	rootdir      string
	artifactdir  string
	image        string
	imageMntDir  string
	recipeDir    string
	Architecture string
}

type Action interface {
	Verify(context YaibContext)
	PreMachine(context *YaibContext, m *fakemachine.Machine, args *[]string)
	Run(context *YaibContext)
	Cleanup(context YaibContext)
	PostMachine(context YaibContext)
}

type BaseAction struct{}

func (b *BaseAction) Verify(context YaibContext) {}
func (b *BaseAction) PreMachine(context *YaibContext,
	m *fakemachine.Machine,
	args *[]string) {
}
func (b *BaseAction) Run(context *YaibContext)        {}
func (b *BaseAction) Cleanup(context YaibContext)     {}
func (b *BaseAction) PostMachine(context YaibContext) {}

/* the YamlAction just embed the Action interface and implements the
 * UnmarshalYAML function so it can select the concrete implementer of a
 * specific action at unmarshaling time */
type YamlAction struct {
	Action
}

func (y *YamlAction) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var aux struct {
		Action string
	}
	err := unmarshal(&aux)
	if err != nil {
		return err
	}

	switch aux.Action {
	case "debootstrap":
		y.Action = &DebootstrapAction{}
	case "pack":
		y.Action = &PackAction{}
	case "unpack":
		y.Action = &UnpackAction{}
	case "run":
		y.Action = &RunAction{}
	case "apt":
		y.Action = &AptAction{}
	case "overlay":
		y.Action = &OverlayAction{}
	case "setup-image":
		y.Action = &SetupImage{}
	case "raw":
		y.Action = &RawAction{}
	default:
		log.Fatalf("Unknown action: %v", aux.Action)
	}

	unmarshal(y.Action)

	return nil
}

type Recipe struct {
	Architecture string
	Actions      []YamlAction
}

func main() {
	var context YaibContext
	var options struct {
		ArtifactDir   string `long:"artifactdir"`
		InternalImage string `long:"internal-image" hidden:"true"`
	}

	parser := flags.NewParser(&options, flags.Default)
	args, err := parser.Parse()

	if err != nil {
		flagsErr, ok := err.(*flags.Error)
		if ok && flagsErr.Type == flags.ErrHelp {
			os.Exit(0)
		} else {
			fmt.Printf("%v\n", flagsErr)
			os.Exit(1)
		}
	}

	if len(args) != 1 {
		log.Fatal("No recipe given!")
	}

	file := args[0]
	file = CleanPath(file)

	context.rootdir = "/scratch/rootdir"
	context.artifactdir = options.ArtifactDir
	context.image = options.InternalImage
	context.recipeDir = path.Dir(file)

	data, err := ioutil.ReadFile(file)
	if err != nil {
		panic(err)
	}

	r := Recipe{}

	err = yaml.Unmarshal(data, &r)
	if err != nil {
		panic(err)
	}

	context.Architecture = r.Architecture

	for _, a := range r.Actions {
		a.Verify(context)
	}

	if !fakemachine.InMachine() {
		m := fakemachine.NewMachine()
		var args []string

		if context.artifactdir == "" {
			context.artifactdir, _ = os.Getwd()
		}

		context.artifactdir = CleanPath(context.artifactdir)

		m.AddVolume(context.artifactdir)
		args = append(args, "--artifactdir", context.artifactdir)

		m.AddVolume(context.recipeDir)
		args = append(args, file)

		for _, a := range r.Actions {
			a.PreMachine(&context, m, &args)
		}

		ret := m.RunInMachineWithArgs(args)

		for _, a := range r.Actions {
			a.PostMachine(context)
		}

		os.Exit(ret)
	}

	for _, a := range r.Actions {
		a.Run(&context)
	}

	for _, a := range r.Actions {
		a.Cleanup(context)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"

	"github.com/go-telegram-bot-api/telegram-bot-api"
)

var ssl = flag.Int("ssl", 0, "0:no ssl, 1:SelfSign, 2:CA-cert")
var webhook = flag.Bool("webhook", false, "webhook mode")
var debug = flag.Bool("debug", false, "debug")
var noisy = flag.Bool("noisy", false, "noisy")
var token = flag.String("token", "", "token")
var pubip = flag.String("pubip", "", "public ip, get with 'curl -s https://ipinfo.io/ip'")
var port = flag.Int("port", 8443, "webhook server port")
var cert = flag.String("cert", "cert.pem", "cert for webhook https server")
var key = flag.String("key", "key.pem", "priv key for webhook https server")
var pathAd = flag.String("Ad", func() string { p, _ := exec.LookPath("Ad"); return p }(), "path to Ad")
var path7z = flag.String("7z", func() string { p, _ := exec.LookPath("7z"); return p }(), "path to 7z")

func handleUpdate(bot *tgbotapi.BotAPI, update tgbotapi.Update) {

	if update.Message == nil {
		return
	}
	Noisy := *noisy
	if update.Message.From.UserName == "sehari24jam" {
		Noisy = true
	}

	log.Printf("[%s] %s", update.Message.From.UserName, update.Message.Text)

	msg := tgbotapi.NewMessage(update.Message.Chat.ID, "Failed")
	msg.ReplyToMessageID = update.Message.MessageID

	switch update.Message.Text {
	case "/start":
		msg.Text = fmt.Sprintf("Welcome %s (%s %s).\n"+
			"You may send me asciidoc (.adoc) file.\n"+
			"Or you can pack whole *.adoc and its included images + sub-adoc into a single zip file.",
			update.Message.From.UserName, update.Message.From.FirstName, update.Message.From.LastName)
		bot.Send(msg)
		return
	default:
		if update.Message.Document == nil {
			msg.Text = "Send me asciidoc file (.adoc). I don't understand: " + update.Message.Text
			bot.Send(msg)
			return
		}
	}

	f, err := bot.GetFile(tgbotapi.FileConfig{FileID: update.Message.Document.FileID})
	//log.Printf("DocFile: %s", update.Message.Document.FileName)
	if err != nil {
		log.Print(err)
		if Noisy {
			msg.Text = "Failed to proceed uploaded file"
		}
		bot.Send(msg)
		return
	}

	ext := path.Ext(update.Message.Document.FileName)
	tmp := path.Join(os.TempDir(), "ybot."+update.Message.Chat.UserName)
	zipped := false
	//tararc := false
	switch strings.ToLower(ext) {

	case ".zip", ".rar", ".7z":
		zipped = true
		tmp, err = ioutil.TempDir("", "ybot-")
		if err != nil {
			log.Print(err)
			log.Print(os.RemoveAll(tmp))
			if Noisy {
				msg.Text = "Unable to create temp"
			}
			bot.Send(msg)
			return
		}
		msg.Text = "Looks good, let me work on this zip file"
		bot.Send(msg)

	case ".adoc":
		msg.Text = "Looks good, let me work on this file"
		bot.Send(msg)

	default:
		msg.Text = "Document is not an adoc"
		bot.Send(msg)
		return
	}
	workfolder := path.Join(tmp, path.Dir(f.FilePath))
	//lfile := path.Join("/tmp", f.FilePath)
	pdffile := path.Join(workfolder, strings.TrimSuffix(update.Message.Document.FileName, ext)+".pdf")
	workfile := path.Join(workfolder, update.Message.Document.FileName)

	// get WorkFile from TG
	response, err := http.Get(f.Link(*token))
	if err != nil {
		log.Print(err)
		if zipped {
			log.Print(os.RemoveAll(tmp))
		}
		if Noisy {
			msg.Text = "Failed to get uploaded file"
		}
		bot.Send(msg)
		return
	}
	defer response.Body.Close()

	// create sub folder as necessary
	os.MkdirAll(workfolder, os.ModePerm)

	// save WorkFile
	file, err := os.Create(workfile)
	if err != nil {
		log.Print(err)
		if zipped {
			log.Print(os.RemoveAll(tmp))
		}
		if Noisy {
			msg.Text = "Unable to create new file"
		}
		bot.Send(msg)
		return
	}
	// Use io.Copy to just dump the response body to the file. This supports huge files
	_, err = io.Copy(file, response.Body)
	file.Close()
	if err != nil {
		log.Print(err)
		if zipped {
			log.Print(os.RemoveAll(tmp))
		}
		if Noisy {
			msg.Text = "Unable to buffer uploaded file"
		}
		bot.Send(msg)
		return
	}

	go func() {
		if zipped {
			cmd := exec.Command("7z", "x", workfile)
			cmd.Dir = workfolder
			out, err := cmd.CombinedOutput()
			if err != nil {
				log.Print(err)
				if zipped {
					log.Print(os.RemoveAll(tmp))
				}
				if Noisy {
					msg.Text = fmt.Sprintf("Failed %v\n%v", string(out), err)
				}
				bot.Send(msg)
				return
			}
			workfile = "*.adoc"
		}

		cmd := exec.Command("Ad", workfile)
		cmd.Dir = workfolder
		out, err := cmd.CombinedOutput()
		if err != nil {
			log.Print(err)
			if zipped {
				log.Print(os.RemoveAll(tmp))
			}
			if Noisy {
				msg.Text = fmt.Sprintf("Failed %v\n%v", string(out), err)
			}
			bot.Send(msg)
			return
		}

		if zipped {
			files, err := filepath.Glob(path.Join(workfolder, "*.pdf"))
			if err != nil {
				log.Print(err)
				if zipped {
					log.Print(os.RemoveAll(tmp))
				}
				if Noisy {
					msg.Text = fmt.Sprintf("Failed %v..", err)
				}
				bot.Send(msg)
				return
			}

			for _, f := range files {
				bot.Send(tgbotapi.NewDocumentUpload(msg.ChatID, f))
			}
			if Noisy {
				msg.Text = fmt.Sprintf("Success %v..", string(out))
			} else {
				msg.Text = "Success.."
			}
			bot.Send(msg)
			log.Print(os.RemoveAll(tmp))
		} else {
			if Noisy {
				msg.Text = fmt.Sprintf("Success %v", string(out))
			} else {
				msg.Text = "Success"
			}
			bot.Send(msg)
			bot.Send(tgbotapi.NewDocumentUpload(msg.ChatID, pdffile))
		}

	}()

}

func main() {

	flag.Parse()

	if *token == "" {
		*token = os.Getenv("YBOTTOKEN")
	}
	bot, err := tgbotapi.NewBotAPI(*token)
	if err != nil {
		log.Panic(err)
	}

	bot.Debug = *debug

	log.Printf("Authorized on account %s", bot.Self.UserName)

	if *webhook {

		var updates tgbotapi.UpdatesChannel
		switch *ssl {
		case 1:
			url := fmt.Sprintf("https://%s:%d/%s", *pubip, *port, bot.Token)
			log.Print("Webhook URL: " + url)
			_, err = bot.SetWebhook(tgbotapi.NewWebhookWithCert(url, *cert))
			if err != nil {
				log.Fatal(err)
			}

			updates = bot.ListenForWebhook("/" + bot.Token)
			go http.ListenAndServeTLS(fmt.Sprintf("0.0.0.0:%d", *port), *cert, *key, nil)

		case 2:
			url := fmt.Sprintf("https://%s:%d/%s", *pubip, *port, bot.Token)
			log.Print("Webhook URL: " + url)
			_, err = bot.SetWebhook(tgbotapi.NewWebhook(url))
			if err != nil {
				log.Fatal(err)
			}

			updates = bot.ListenForWebhook("/" + bot.Token)
			go http.ListenAndServeTLS(fmt.Sprintf("0.0.0.0:%d", *port), *cert, *key, nil)

		default:
			url := fmt.Sprintf("http://%s:%d/%s", *pubip, *port, bot.Token)
			log.Print("Webhook URL: " + url)
			_, err = bot.SetWebhook(tgbotapi.NewWebhook(url))
			if err != nil {
				log.Fatal(err)
			}

			updates = bot.ListenForWebhook("/" + bot.Token)
			go http.ListenAndServe(fmt.Sprintf("0.0.0.0:%d", *port), nil)
		}

		log.Printf("Starting Collect Update from WebHook")
		for update := range updates {
			handleUpdate(bot, update)
			//log.Printf("%+v\n", update)
		}

	} else {

		u := tgbotapi.NewUpdate(0)
		u.Timeout = 60

		updates, err := bot.GetUpdatesChan(u)

		if err != nil {
			log.Panic(err)
		}

		log.Printf("Starting GetUpdate")
		for update := range updates {
			handleUpdate(bot, update)
		}
	}

}
<commit_msg>double zip<commit_after>package main

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"

	"github.com/go-telegram-bot-api/telegram-bot-api"
)

var ssl = flag.Int("ssl", 0, "0:no ssl, 1:SelfSign, 2:CA-cert")
var webhook = flag.Bool("webhook", false, "webhook mode")
var debug = flag.Bool("debug", false, "debug")
var noisy = flag.Bool("noisy", false, "noisy")
var token = flag.String("token", "", "token")
var pubip = flag.String("pubip", "", "public ip, get with 'curl -s https://ipinfo.io/ip'")
var port = flag.Int("port", 8443, "webhook server port")
var cert = flag.String("cert", "cert.pem", "cert for webhook https server")
var key = flag.String("key", "key.pem", "priv key for webhook https server")
var pathAd = flag.String("Ad", func() string { p, _ := exec.LookPath("Ad"); return p }(), "path to Ad")
var path7z = flag.String("7z", func() string { p, _ := exec.LookPath("7z"); return p }(), "path to 7z")

func handleUpdate(bot *tgbotapi.BotAPI, update tgbotapi.Update) {

	if update.Message == nil {
		return
	}
	Noisy := *noisy
	if update.Message.From.UserName == "sehari24jam" {
		Noisy = true
	}

	log.Printf("[%s] %s", update.Message.From.UserName, update.Message.Text)

	msg := tgbotapi.NewMessage(update.Message.Chat.ID, "Failed")
	msg.ReplyToMessageID = update.Message.MessageID

	switch update.Message.Text {
	case "/start":
		msg.Text = fmt.Sprintf("Welcome %s (%s %s).\n"+
			"You may send me asciidoc (.adoc) file.\n"+
			"Or you can pack whole *.adoc and its included images + sub-adoc into a single compressed file.",
			update.Message.From.UserName, update.Message.From.FirstName, update.Message.From.LastName)
		bot.Send(msg)
		return
	default:
		if update.Message.Document == nil {
			msg.Text = "Send me asciidoc file (.adoc). I don't understand: " + update.Message.Text
			bot.Send(msg)
			return
		}
	}

	f, err := bot.GetFile(tgbotapi.FileConfig{FileID: update.Message.Document.FileID})
	//log.Printf("DocFile: %s", update.Message.Document.FileName)
	if err != nil {
		log.Print(err)
		if Noisy {
			msg.Text = "Failed to proceed uploaded file"
		}
		bot.Send(msg)
		return
	}

	ext := path.Ext(update.Message.Document.FileName)
	tmp := path.Join(os.TempDir(), "ybot."+update.Message.Chat.UserName)
	packed := false
	dpacked := false

	switch strings.ToLower(ext) {

	case ".zip", ".rar", ".7z":
		packed = true
		msg.Text = "Looks good, let me work on this compressed file"

	case ".tgz", ".tbz2", ".txz":
		dpacked = true
		msg.Text = "Looks good, let me work on this compressed file."

	case ".gz", ".bz2", ".xz":
		ftar := strings.TrimSuffix(update.Message.Document.FileName, ext)
		exttar := path.Ext(ftar)
		if exttar == ".tar" {
			dpacked = true
			msg.Text = "Looks good, let me work on this compressed file.."
			ext = exttar + ext
		} else {
			msg.Text = "Document is not an adoc"
			bot.Send(msg)
			return
		}

	case ".adoc":
		msg.Text = "Looks good, let me work on this file"

	default:
		msg.Text = "Document is not an adoc"
		bot.Send(msg)
		return
	}

	if packed || dpacked {
		tmp, err = ioutil.TempDir("", "ybot-")
		if err != nil {
			log.Print(err)
			log.Print(os.RemoveAll(tmp))
			if Noisy {
				msg.Text = "Unable to create temp"
			}
			bot.Send(msg)
			return
		}
	}
	bot.Send(msg)

	workfolder := path.Join(tmp, path.Dir(f.FilePath))
	//lfile := path.Join("/tmp", f.FilePath)
	pdffile := path.Join(workfolder, strings.TrimSuffix(update.Message.Document.FileName, ext)+".pdf")
	workfile := path.Join(workfolder, update.Message.Document.FileName)

	// get WorkFile from TG
	response, err := http.Get(f.Link(*token))
	if err != nil {
		log.Print(err)
		if packed || dpacked {
			log.Print(os.RemoveAll(tmp))
		}
		if Noisy {
			msg.Text = "Failed to get uploaded file"
		}
		bot.Send(msg)
		return
	}
	defer response.Body.Close()

	// create sub folder as necessary
	os.MkdirAll(workfolder, os.ModePerm)

	// save WorkFile
	file, err := os.Create(workfile)
	if err != nil {
		log.Print(err)
		if packed || dpacked {
			log.Print(os.RemoveAll(tmp))
		}
		if Noisy {
			msg.Text = "Unable to create new file"
		}
		bot.Send(msg)
		return
	}
	// Use io.Copy to just dump the response body to the file. This supports huge files
	_, err = io.Copy(file, response.Body)
	file.Close()
	if err != nil {
		log.Print(err)
		if packed || dpacked {
			log.Print(os.RemoveAll(tmp))
		}
		if Noisy {
			msg.Text = "Unable to buffer uploaded file"
		}
		bot.Send(msg)
		return
	}

	go func() {
		///////////////////////////////////// extraction
		switch {
		case packed:
			cmd := exec.Command("7z", "x", workfile)
			cmd.Dir = workfolder
			out, err := cmd.CombinedOutput()
			if err != nil {
				log.Print(err)
				if packed {
					log.Print(os.RemoveAll(tmp))
				}
				if Noisy {
					msg.Text = fmt.Sprintf("Failed %v\n%v", string(out), err)
				}
				bot.Send(msg)
				return
			}
			workfile = "*.adoc"
		case dpacked:
			var out bytes.Buffer
			xcmd1 := exec.Command("7z", "x", workfile, "-so")
			xcmd1.Dir = workfolder
			xcmd2 := exec.Command("7z", "x", "-si", "-ttar", "-y")
			xcmd2.Dir = workfolder
			xcmd2.Stdin, _ = xcmd1.StdoutPipe()
			xcmd2.Stdout = &out
			_ = xcmd2.Start()
			_ = xcmd1.Run()
			err := xcmd2.Wait()
			if err != nil {
				log.Print(err)
				if packed {
					log.Print(os.RemoveAll(tmp))
				}
				if Noisy {
					msg.Text = fmt.Sprintf("Failed %v\n%v", string(out.Bytes()), err)
				}
				bot.Send(msg)
				return
			}
			workfile = "*.adoc"
		}

		///////////////////////////////////// conversion
		cmd := exec.Command("Ad", workfile)
		cmd.Dir = workfolder
		out, err := cmd.CombinedOutput()
		if err != nil {
			log.Print(err)
			if packed {
				log.Print(os.RemoveAll(tmp))
			}
			if Noisy {
				msg.Text = fmt.Sprintf("Failed %v\n%v", string(out), err)
			}
			bot.Send(msg)
			return
		}

		if packed || dpacked {
			files, err := filepath.Glob(path.Join(workfolder, "*.pdf"))
			if err != nil {
				log.Print(err)
				log.Print(os.RemoveAll(tmp))
				if Noisy {
					msg.Text = fmt.Sprintf("Failed %v..", err)
				}
				bot.Send(msg)
				return
			}

			for _, f := range files {
				bot.Send(tgbotapi.NewDocumentUpload(msg.ChatID, f))
			}
			if Noisy {
				msg.Text = fmt.Sprintf("Success %v..", string(out))
			} else {
				msg.Text = "Success.."
			}
			bot.Send(msg)
			log.Print(os.RemoveAll(tmp))
		} else {
			if Noisy {
				msg.Text = fmt.Sprintf("Success %v", string(out))
			} else {
				msg.Text = "Success"
			}
			bot.Send(msg)
			bot.Send(tgbotapi.NewDocumentUpload(msg.ChatID, pdffile))
		}

	}()

}

func main() {

	flag.Parse()

	if *token == "" {
		*token = os.Getenv("YBOTTOKEN")
	}
	bot, err := tgbotapi.NewBotAPI(*token)
	if err != nil {
		log.Panic(err)
	}

	bot.Debug = *debug

	log.Printf("Authorized on account %s", bot.Self.UserName)

	if *webhook {

		var updates tgbotapi.UpdatesChannel
		switch *ssl {
		case 1:
			url := fmt.Sprintf("https://%s:%d/%s", *pubip, *port, bot.Token)
			log.Print("Webhook URL: " + url)
			_, err = bot.SetWebhook(tgbotapi.NewWebhookWithCert(url, *cert))
			if err != nil {
				log.Fatal(err)
			}

			updates = bot.ListenForWebhook("/" + bot.Token)
			go http.ListenAndServeTLS(fmt.Sprintf("0.0.0.0:%d", *port), *cert, *key, nil)

		case 2:
			url := fmt.Sprintf("https://%s:%d/%s", *pubip, *port, bot.Token)
			log.Print("Webhook URL: " + url)
			_, err = bot.SetWebhook(tgbotapi.NewWebhook(url))
			if err != nil {
				log.Fatal(err)
			}

			updates = bot.ListenForWebhook("/" + bot.Token)
			go http.ListenAndServeTLS(fmt.Sprintf("0.0.0.0:%d", *port), *cert, *key, nil)

		default:
			url := fmt.Sprintf("http://%s:%d/%s", *pubip, *port, bot.Token)
			log.Print("Webhook URL: " + url)
			_, err = bot.SetWebhook(tgbotapi.NewWebhook(url))
			if err != nil {
				log.Fatal(err)
			}

			updates = bot.ListenForWebhook("/" + bot.Token)
			go http.ListenAndServe(fmt.Sprintf("0.0.0.0:%d", *port), nil)
		}

		log.Printf("Starting Collect Update from WebHook")
		for update := range updates {
			handleUpdate(bot, update)
			//log.Printf("%+v\n", update)
		}

	} else {

		u := tgbotapi.NewUpdate(0)
		u.Timeout = 60

		updates, err := bot.GetUpdatesChan(u)

		if err != nil {
			log.Panic(err)
		}

		log.Printf("Starting GetUpdate")
		for update := range updates {
			handleUpdate(bot, update)
		}
	}

}
<|endoftext|>