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

// This file holds the basic scanning routines that separate a source file
// into the various tokens

package main

import "./libgogo/_obj/libgogo"
import "fmt"

// Token struct holding the relevant data of a parsed token.
type Token struct {
    id uint64; // The id. Is one of TOKEN_*
    intValue uint64; // value storing the integer value if the token is TOKEN_INTEGER
    newValue string; // Value that should be used instead of byte arrays
    nextChar byte; // Sometime the next char is already read. It is stored here to be re-assigned in the next GetNextToken() round
};

func GetNextTokenRaw(fd uint64, tok *Token) {
    var singleChar byte; // Byte holding the last read value
    // Flag indicating whether we are in a comment.
    // 0 for no comment
    // 1 for a single line comment
    // 2 for a multi line comment
    var inComment uint64;
    var done uint64; // Flag indicating whether a cycle (Token) is finsihed
    var spaceDone uint64; // Flag indicating whether an abolishment cycle is finished

    // Initialize variables
    done = 0;
    spaceDone = 0;
    inComment = 0;

    // If the previous cycle had to read the next char (and stored it), it is
    // now used as first read
    if tok.nextChar == 0 {
        singleChar = libgogo.GetChar(fd)
    } else {
        singleChar = tok.nextChar;
        tok.nextChar = 0;
    }

    // check if it is a valid read, or an EOF
    if singleChar == 0 {
        tok.id = TOKEN_EOS;
        done = 1;
        spaceDone = 1;
    }

    //
    // Cleaning Tasks
    // The next part strips out spaces, newlines, tabs, and comments
    // Comments can either be single line with double slashes (//) or multiline
    // using C++ syntax /* */
    //
    for ; spaceDone != 1; {

        // check whether a comment is starting
        if singleChar == '/' {
            // if we are in a comment skip the rest, get the next char otherwise
            if inComment == 0 {
                singleChar = libgogo.GetChar(fd);
                if singleChar == '/' {
                    // we are in a single line comment (until newline is found)
                    inComment = 1;
                } else {
                    if singleChar == '*' {
                        // we are in a multiline comment (until ending is found)
                        inComment = 2;
                    } else {
                        libgogo.ExitError(">> Scanner: Unkown character combination for comments. Exiting.",1);
                    }
                }
            }
        }

        // check whether a multi-line comment is ending
        if singleChar == '*' {
            singleChar = libgogo.GetChar(fd);
            if singleChar == '/' {
                if inComment == 2 {
                    inComment = 0;
                    singleChar = libgogo.GetChar(fd);
                }
            }
        }

        // if character is a newline:
        //  *) if in a singleline comment, exit the comment
        //  *) skip otherwise
        if singleChar == 10 {
            if inComment == 1 {
                inComment = 0;
            }
        }

        // handle everything that is not a space,tab,newline
        if singleChar != ' ' && singleChar != 9 && singleChar != 10 {
            // if not in a comment we have our current valid char
            if inComment == 0 {
                spaceDone = 1;
            }

            // check if GetChar() returned EOF while skipping
            if singleChar == 0 {
                tok.id = TOKEN_EOS;
                spaceDone = 1;
                done = 1;
            }
        }

        // if we are not done until now, get a new character and start another abolishing cycle
        if spaceDone == 0 {
            singleChar=libgogo.GetChar(fd);
        }
    }

    //
    // Actual scanning part starts here
    //

    // Catch identifiers
    // identifier = letter { letter | digit }.
    if (done != 1) && (singleChar >= 'A' && singleChar <= 'Z') || (singleChar >= 'a' && singleChar <= 'z') || singleChar == '_' { // check for letter or _
        tok.id = TOKEN_IDENTIFIER;
        // preceding characters may be letter,_, or a number
        for ; (singleChar >= 'A' && singleChar <= 'Z') || (singleChar >= 'a' && singleChar <= 'z') || singleChar == '_' || (singleChar >= '0' && singleChar <= '9'); singleChar = libgogo.GetChar(fd) {
            tmp_StrAppend(tok.newValue,singleChar);
        }
        // save the last read character for the next GetNextToken() cycle
        tok.nextChar = singleChar;
        done = 1;
    }

    // string "..."
    if (done != 1) && singleChar == '"' {
        tok.id = TOKEN_STRING;
        for singleChar = libgogo.GetChar(fd); singleChar != '"' &&singleChar > 31 && singleChar < 127;singleChar = libgogo.GetChar(fd) {
            tmp_StrAppend(tok.newValue,singleChar);
        }
        if singleChar != '"' {
            libgogo.ExitError(">> Scanner: String not closing. Exiting.",1);
        }
        done = 1;
    }

    // Single Quoted Character
    if (done != 1) && singleChar == 39 {
        singleChar = libgogo.GetChar(fd);
        if singleChar != 39 && singleChar > 31 && singleChar < 127 {
            tok.id = TOKEN_INTEGER;
            tok.intValue = tmp_toInt(singleChar);
        } else {
            libgogo.ExitError(">> Scanner: Unknown character. Exiting.",1);
        }
        singleChar = libgogo.GetChar(fd);
        if singleChar != 39 {
            libgogo.ExitError(">> Scanner: Only single characters allowed. Use corresponding integer for special characters. Exiting.",1);
        }
        done = 1;
    }

    // left brace (
    if (done != 1) && singleChar == '(' {
        tok.id = TOKEN_LBRAC;
        done = 1;
    }

    // right brace )
    if (done != 1) && singleChar == ')' {
        tok.id = TOKEN_RBRAC;
        done = 1;
    }

    // left square bracket [
    if (done != 1) && singleChar == '[' {
        tok.id = TOKEN_LSBRAC;
        done = 1;
    }

    // right square bracket ]
    if (done != 1) && singleChar == ']' {
        tok.id = TOKEN_RSBRAC;
        done = 1;
    }

    // integer
    if (done != 1) && singleChar > 47 && singleChar < 58 {
        var byteBuf [255]byte;
        var i uint64;

        for i = 0; singleChar > 47 && singleChar < 58 ; singleChar = libgogo.GetChar(fd) {
            byteBuf[i] = singleChar;
            i = i +1;
        }

        tok.nextChar = singleChar;
        tok.id = TOKEN_INTEGER;
        tok.intValue = libgogo.ByteBufToInt(byteBuf,i);

        done = 1;
    }

    // Left curly bracket '{'
    if (done != 1) && singleChar == '{' {
        tok.id = TOKEN_LCBRAC;
        done = 1;
    }

    // Right curly bracket '}'
    if (done != 1) && singleChar == '}' {
        tok.id = TOKEN_RCBRAC;
        done = 1;
    }

    // Point '.'
    if (done != 1) && singleChar == '.' {
        tok.id = TOKEN_PT;
        done = 1;
    }

    // Not ('!') or Not Equal ('!=')
    if (done != 1) && singleChar == '!' {
        singleChar = libgogo.GetChar(fd);
        if singleChar == '=' {
            tok.id = TOKEN_NOTEQUAL;
        } else {
            tok.id = TOKEN_NOT;
            tok.nextChar = singleChar;
        }
        done = 1;
    }

    // Semicolon ';'
    if (done != 1) && singleChar == ';' {
        tok.id = TOKEN_SEMICOLON;
        done = 1;
    }

    // Colon ','
    if (done != 1) && singleChar == ',' {
        tok.id = TOKEN_COLON;
        done = 1;
    }

    // Assignment '=' or Equals comparison '=='
    if (done != 1) && singleChar == '=' {
        singleChar = libgogo.GetChar(fd);
        if singleChar == '=' {
            tok.id = TOKEN_EQUALS;
        } else {
            tok.id = TOKEN_ASSIGN;
            tok.nextChar = singleChar;
        }
        done = 1;
    }

    // AND Relation '&&'
    if (done != 1) && singleChar == '&' {
        singleChar = libgogo.GetChar(fd);
        if singleChar == '&' {
            tok.id = TOKEN_REL_AND;
        } else {
            tok.id = TOKEN_OP_ADR;
            tok.nextChar = singleChar;
        }
        done = 1;
    }

    // OR Relation '||'
    if (done != 1) && singleChar == '|' {
        singleChar = libgogo.GetChar(fd);
        if singleChar == '|' {
            tok.id = TOKEN_REL_OR;
        } else {
            libgogo.ExitError(">> Scanner: No binary OR (|) supported. Only ||.",1);
        }
        done = 1;
    }

    // Greater and Greater-Than relation
    if (done != 1) && singleChar == '>' {
        singleChar = libgogo.GetChar(fd);
        if singleChar == '=' {
            tok.id = TOKEN_REL_GTOE;
        } else {
            tok.id = TOKEN_REL_GT;
            tok.nextChar = singleChar;
        }
        done = 1;
    }

    // Less and Less-Than relation
    if (done != 1) && singleChar == '<' {
        singleChar = libgogo.GetChar(fd);
        if singleChar == '=' {
            tok.id = TOKEN_REL_LTOE;
        } else {
            tok.id = TOKEN_REL_LT;
            tok.nextChar = singleChar;
        }
        done = 1;
    }

    if (done != 1) && singleChar == '+' {
        tok.id = TOKEN_ARITH_PLUS;
        done = 1;
    }

    if (done != 1) && singleChar == '-' {
        tok.id = TOKEN_ARITH_MINUS;
        done = 1;
    }

    if (done != 1) && singleChar == '*' {
        tok.id = TOKEN_ARITH_MUL;
        done = 1;
    }

    if (done != 1) && singleChar == '/' {
        tok.id = TOKEN_ARITH_DIV;
        done = 1;
    }

    if done != 1 {

        libgogo.PrintString(">> Scanner: Unkown char '");
        libgogo.PrintChar(singleChar);
        libgogo.PrintString("'. ");
        libgogo.ExitError("Exiting.",1);
    }
}

//
// GetNextToken should be called by the parser. It bascially fetches the next
// token by calling GetNextTokenRaw() and filters the identifiers for known
// keywords.
//
func GetNextToken(fd uint64, tok *Token) {
    GetNextTokenRaw(fd,tok)

    // Convert identifier to keyworded tokens
    if tok.id == TOKEN_IDENTIFIER {
        if tmp_StrCmp("if",tok.newValue) == 0 {
            tok.id = TOKEN_IF;
        }
        if tmp_StrCmp("for",tok.newValue) == 0 {
            tok.id = TOKEN_FOR;
        }
        if tmp_StrCmp("type",tok.newValue) == 0 {
            tok.id = TOKEN_TYPE;
        }
        if tmp_StrCmp("const",tok.newValue) == 0 {
            tok.id = TOKEN_CONST;
        }
        if tmp_StrCmp("var",tok.newValue) == 0 {
            tok.id = TOKEN_VAR;
        }
        if tmp_StrCmp("struct", tok.newValue) == 0 {
            tok.id = TOKEN_STRUCT;
        }
        if tmp_StrCmp("return", tok.newValue) == 0 {
            tok.id = TOKEN_RETURN;
        }
        if tmp_StrCmp("func", tok.newValue) == 0 {
            tok.id = TOKEN_FUNC;
        }
        if tmp_StrCmp("import", tok.newValue) == 0 {
            tok.id = TOKEN_IMPORT;
        }
        if tmp_StrCmp("package", tok.newValue) == 0 {
            tok.id = TOKEN_PACKAGE;
        }
    }
}

//
// Debugging and temporary functions
//

// Temporary test function
func ScannerTest(fd uint64) {
    var tok Token;

    tok.id = 0;
    tok.nextChar = 0;

    for GetNextToken(fd,&tok); tok.id != TOKEN_EOS; GetNextToken(fd,&tok) {
        fmt.Printf("%d\n",tok.id);
    }
}

// libgogo ...
func tmp_StrAppend(str string, b byte) {
    str += string(b);
}

// libgogo ...
func tmp_StrLen(str string) int {
    return len(str);
}

// libgogo ...
func tmp_StrCmp(str1 string, str2 string) uint64 {
    var ret uint64;
    if str1 == str2 {
        ret = 0;
    } else {
        ret = 1;
    }
    return ret;
}

// libgogo ...
func tmp_toInt(b byte) uint64 {
    return uint64(b);
}
<commit_msg>scanner: Fix str append bug. Needs further cleanup.<commit_after>// Copyright 2010 The GoGo Authors. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

// This file holds the basic scanning routines that separate a source file
// into the various tokens

package main

import "./libgogo/_obj/libgogo"
import "fmt"

// Token struct holding the relevant data of a parsed token.
type Token struct {
    id uint64; // The id. Is one of TOKEN_*
    intValue uint64; // value storing the integer value if the token is TOKEN_INTEGER
    newValue string; // Value that should be used instead of byte arrays
    nextChar byte; // Sometime the next char is already read. It is stored here to be re-assigned in the next GetNextToken() round
};

func GetNextTokenRaw(fd uint64, tok *Token) {
    var singleChar byte; // Byte holding the last read value
    // Flag indicating whether we are in a comment.
    // 0 for no comment
    // 1 for a single line comment
    // 2 for a multi line comment
    var inComment uint64;
    var done uint64; // Flag indicating whether a cycle (Token) is finsihed
    var spaceDone uint64; // Flag indicating whether an abolishment cycle is finished

    // Initialize variables
    done = 0;
    spaceDone = 0;
    inComment = 0;

    tok.newValue = "";

    // If the previous cycle had to read the next char (and stored it), it is
    // now used as first read
    if tok.nextChar == 0 {
        singleChar = libgogo.GetChar(fd)
    } else {
        singleChar = tok.nextChar;
        tok.nextChar = 0;
    }

    // check if it is a valid read, or an EOF
    if singleChar == 0 {
        tok.id = TOKEN_EOS;
        done = 1;
        spaceDone = 1;
    }

    //
    // Cleaning Tasks
    // The next part strips out spaces, newlines, tabs, and comments
    // Comments can either be single line with double slashes (//) or multiline
    // using C++ syntax /* */
    //
    for ; spaceDone != 1; {

        // check whether a comment is starting
        if singleChar == '/' {
            // if we are in a comment skip the rest, get the next char otherwise
            if inComment == 0 {
                singleChar = libgogo.GetChar(fd);
                if singleChar == '/' {
                    // we are in a single line comment (until newline is found)
                    inComment = 1;
                } else {
                    if singleChar == '*' {
                        // we are in a multiline comment (until ending is found)
                        inComment = 2;
                    } else {
                        libgogo.ExitError(">> Scanner: Unkown character combination for comments. Exiting.",1);
                    }
                }
            }
        }

        // check whether a multi-line comment is ending
        if singleChar == '*' {
            singleChar = libgogo.GetChar(fd);
            if singleChar == '/' {
                if inComment == 2 {
                    inComment = 0;
                    singleChar = libgogo.GetChar(fd);
                }
            }
        }

        // if character is a newline:
        //  *) if in a singleline comment, exit the comment
        //  *) skip otherwise
        if singleChar == 10 {
            if inComment == 1 {
                inComment = 0;
            }
        }

        // handle everything that is not a space,tab,newline
        if singleChar != ' ' && singleChar != 9 && singleChar != 10 {
            // if not in a comment we have our current valid char
            if inComment == 0 {
                spaceDone = 1;
            }

            // check if GetChar() returned EOF while skipping
            if singleChar == 0 {
                tok.id = TOKEN_EOS;
                spaceDone = 1;
                done = 1;
            }
        }

        // if we are not done until now, get a new character and start another abolishing cycle
        if spaceDone == 0 {
            singleChar=libgogo.GetChar(fd);
        }
    }

    //
    // Actual scanning part starts here
    //

    // Catch identifiers
    // identifier = letter { letter | digit }.
    if (done != 1) && (singleChar >= 'A' && singleChar <= 'Z') || (singleChar >= 'a' && singleChar <= 'z') || singleChar == '_' { // check for letter or _
        tok.id = TOKEN_IDENTIFIER;
        // preceding characters may be letter,_, or a number
        for ; (singleChar >= 'A' && singleChar <= 'Z') || (singleChar >= 'a' && singleChar <= 'z') || singleChar == '_' || (singleChar >= '0' && singleChar <= '9'); singleChar = libgogo.GetChar(fd) {
            // TODO
            tok.newValue += string(singleChar);
        }
        // save the last read character for the next GetNextToken() cycle
        tok.nextChar = singleChar;
        done = 1;
    }

    // string "..."
    if (done != 1) && singleChar == '"' {
        tok.id = TOKEN_STRING;
        for singleChar = libgogo.GetChar(fd); singleChar != '"' &&singleChar > 31 && singleChar < 127;singleChar = libgogo.GetChar(fd) {
            tok.newValue += string(singleChar);
        }
        if singleChar != '"' {
            libgogo.ExitError(">> Scanner: String not closing. Exiting.",1);
        }
        done = 1;
    }

    // Single Quoted Character
    if (done != 1) && singleChar == 39 {
        singleChar = libgogo.GetChar(fd);
        if singleChar != 39 && singleChar > 31 && singleChar < 127 {
            tok.id = TOKEN_INTEGER;
            tok.intValue = tmp_toInt(singleChar);
        } else {
            libgogo.ExitError(">> Scanner: Unknown character. Exiting.",1);
        }
        singleChar = libgogo.GetChar(fd);
        if singleChar != 39 {
            libgogo.ExitError(">> Scanner: Only single characters allowed. Use corresponding integer for special characters. Exiting.",1);
        }
        done = 1;
    }

    // left brace (
    if (done != 1) && singleChar == '(' {
        tok.id = TOKEN_LBRAC;
        done = 1;
    }

    // right brace )
    if (done != 1) && singleChar == ')' {
        tok.id = TOKEN_RBRAC;
        done = 1;
    }

    // left square bracket [
    if (done != 1) && singleChar == '[' {
        tok.id = TOKEN_LSBRAC;
        done = 1;
    }

    // right square bracket ]
    if (done != 1) && singleChar == ']' {
        tok.id = TOKEN_RSBRAC;
        done = 1;
    }

    // integer
    if (done != 1) && singleChar > 47 && singleChar < 58 {
        var byteBuf [255]byte;
        var i uint64;

        for i = 0; singleChar > 47 && singleChar < 58 ; singleChar = libgogo.GetChar(fd) {
            byteBuf[i] = singleChar;
            i = i +1;
        }

        tok.nextChar = singleChar;
        tok.id = TOKEN_INTEGER;
        tok.intValue = libgogo.ByteBufToInt(byteBuf,i);

        done = 1;
    }

    // Left curly bracket '{'
    if (done != 1) && singleChar == '{' {
        tok.id = TOKEN_LCBRAC;
        done = 1;
    }

    // Right curly bracket '}'
    if (done != 1) && singleChar == '}' {
        tok.id = TOKEN_RCBRAC;
        done = 1;
    }

    // Point '.'
    if (done != 1) && singleChar == '.' {
        tok.id = TOKEN_PT;
        done = 1;
    }

    // Not ('!') or Not Equal ('!=')
    if (done != 1) && singleChar == '!' {
        singleChar = libgogo.GetChar(fd);
        if singleChar == '=' {
            tok.id = TOKEN_NOTEQUAL;
        } else {
            tok.id = TOKEN_NOT;
            tok.nextChar = singleChar;
        }
        done = 1;
    }

    // Semicolon ';'
    if (done != 1) && singleChar == ';' {
        tok.id = TOKEN_SEMICOLON;
        done = 1;
    }

    // Colon ','
    if (done != 1) && singleChar == ',' {
        tok.id = TOKEN_COLON;
        done = 1;
    }

    // Assignment '=' or Equals comparison '=='
    if (done != 1) && singleChar == '=' {
        singleChar = libgogo.GetChar(fd);
        if singleChar == '=' {
            tok.id = TOKEN_EQUALS;
        } else {
            tok.id = TOKEN_ASSIGN;
            tok.nextChar = singleChar;
        }
        done = 1;
    }

    // AND Relation '&&'
    if (done != 1) && singleChar == '&' {
        singleChar = libgogo.GetChar(fd);
        if singleChar == '&' {
            tok.id = TOKEN_REL_AND;
        } else {
            tok.id = TOKEN_OP_ADR;
            tok.nextChar = singleChar;
        }
        done = 1;
    }

    // OR Relation '||'
    if (done != 1) && singleChar == '|' {
        singleChar = libgogo.GetChar(fd);
        if singleChar == '|' {
            tok.id = TOKEN_REL_OR;
        } else {
            libgogo.ExitError(">> Scanner: No binary OR (|) supported. Only ||.",1);
        }
        done = 1;
    }

    // Greater and Greater-Than relation
    if (done != 1) && singleChar == '>' {
        singleChar = libgogo.GetChar(fd);
        if singleChar == '=' {
            tok.id = TOKEN_REL_GTOE;
        } else {
            tok.id = TOKEN_REL_GT;
            tok.nextChar = singleChar;
        }
        done = 1;
    }

    // Less and Less-Than relation
    if (done != 1) && singleChar == '<' {
        singleChar = libgogo.GetChar(fd);
        if singleChar == '=' {
            tok.id = TOKEN_REL_LTOE;
        } else {
            tok.id = TOKEN_REL_LT;
            tok.nextChar = singleChar;
        }
        done = 1;
    }

    if (done != 1) && singleChar == '+' {
        tok.id = TOKEN_ARITH_PLUS;
        done = 1;
    }

    if (done != 1) && singleChar == '-' {
        tok.id = TOKEN_ARITH_MINUS;
        done = 1;
    }

    if (done != 1) && singleChar == '*' {
        tok.id = TOKEN_ARITH_MUL;
        done = 1;
    }

    if (done != 1) && singleChar == '/' {
        tok.id = TOKEN_ARITH_DIV;
        done = 1;
    }

    if done != 1 {

        libgogo.PrintString(">> Scanner: Unkown char '");
        libgogo.PrintChar(singleChar);
        libgogo.PrintString("'. ");
        libgogo.ExitError("Exiting.",1);
    }
}

//
// GetNextToken should be called by the parser. It bascially fetches the next
// token by calling GetNextTokenRaw() and filters the identifiers for known
// keywords.
//
func GetNextToken(fd uint64, tok *Token) {
    GetNextTokenRaw(fd,tok)

    // Convert identifier to keyworded tokens
    if tok.id == TOKEN_IDENTIFIER {
        if tmp_StrCmp("if",tok.newValue) == 0 {
            tok.id = TOKEN_IF;
        }
        if tmp_StrCmp("for",tok.newValue) == 0 {
            tok.id = TOKEN_FOR;
        }
        if tmp_StrCmp("type",tok.newValue) == 0 {
            tok.id = TOKEN_TYPE;
        }
        if tmp_StrCmp("const",tok.newValue) == 0 {
            tok.id = TOKEN_CONST;
        }
        if tmp_StrCmp("var",tok.newValue) == 0 {
            tok.id = TOKEN_VAR;
        }
        if tmp_StrCmp("struct", tok.newValue) == 0 {
            tok.id = TOKEN_STRUCT;
        }
        if tmp_StrCmp("return", tok.newValue) == 0 {
            tok.id = TOKEN_RETURN;
        }
        if tmp_StrCmp("func", tok.newValue) == 0 {
            tok.id = TOKEN_FUNC;
        }
        if tmp_StrCmp("import", tok.newValue) == 0 {
            tok.id = TOKEN_IMPORT;
        }
        if tmp_StrCmp("package", tok.newValue) == 0 {
            tok.id = TOKEN_PACKAGE;
        }
    }
}

//
// Debugging and temporary functions
//

// Temporary test function
func ScannerTest(fd uint64) {
    var tok Token;

    tok.id = 0;
    tok.nextChar = 0;

    for GetNextToken(fd,&tok); tok.id != TOKEN_EOS; GetNextToken(fd,&tok) {
        fmt.Printf("%d\n",tok.id);
    }
}

// libgogo ...
func tmp_StrAppend(str string, b byte) {
    str += "x";
}

// libgogo ...
func tmp_StrLen(str string) int {
    return len(str);
}

// libgogo ...
func tmp_StrCmp(str1 string, str2 string) uint64 {
    var ret uint64;
    if str1 == str2 {
        ret = 0;
    } else {
        ret =1;
    }
    return ret;
}

// libgogo ...
func tmp_toInt(b byte) uint64 {
    return uint64(b);
}
<|endoftext|>


<commit_before>package srt

import (
	"bufio"
	"errors"
	"os"
	"strconv"
	"strings"
)

//LoadSrt loads the provided file into the given object.
//It fixes the \ufeff problem that some parsers have.
func LoadSrt(v *SubRip, filepath string) error {
	f, err := os.Open(filepath)
	if err != nil {
		return (err)
	}
	scanner := bufio.NewScanner(f)
	z := &Subtitle{}
	for scanner.Scan() {
		line := scanner.Text()
		//Ufeff problem fix
		if strings.HasPrefix(line, "\ufeff") {
			line = strings.Replace(line, "\ufeff", "", -1)
		}
		//The most jackass SRT parser ever. Appends to an object.
		i, err := strconv.Atoi(line)
		if err == nil && z.Id == 0 {
			z.Id = int(i)
			//A bit more jackassery, because if z.Start and z.End are set, then welp.
		} else if strings.Contains(line, "-->") && z.Start == "" && z.End == "" {
			split := strings.Split(line, "-->")

			//Oh hey, we occasionally can pick up extra whitespace, let's strip it
			z.Start = strings.Replace(split[0], " ", "", -1)
			z.End = strings.Replace(split[1], " ", "", -1)
		} else if line != "" && z.Start != "" && z.End != "" && z.Id != 0 {
			z.Line = append(z.Line, line)
		} else if line == "" {
			//Clear object on newline
			if z.Start != "" && z.End != "" && z.Line != nil {
				v.Subtitle.Content = append(v.Subtitle.Content, *z)
			}
			z = &Subtitle{}
		} else {
			//At some point, we need to start actually returning errors.
			//Wouldn't that be nice?
			return errors.New("Error parsing .srt. Stray newline?")
		}

	}
	//Since the last subtitle often won't have a newline, append everything and clear object one last time
	v.Subtitle.Content = append(v.Subtitle.Content, *z)
	z = &Subtitle{}
	defer f.Close()
	return nil
}

//ParseSrt is the loader for srt files. Takes the path of the file being opened as the argument.
func ParseSrt(filename string) (*SubRip, error) {
	v := &SubRip{}
	err := LoadSrt(v, filename)
	if err != nil {
		return v, err
	}
	return v, nil
}
<commit_msg>updating srt comments<commit_after>package srt

import (
	"bufio"
	"errors"
	"os"
	"strconv"
	"strings"
)

//LoadSrt loads the provided file into the given object.
//It fixes the \ufeff problem that some parsers have.
func LoadSrt(v *SubRip, filepath string) error {
	f, err := os.Open(filepath)
	if err != nil {
		return (err)
	}
	scanner := bufio.NewScanner(f)
	z := &Subtitle{}
	for scanner.Scan() {
		line := scanner.Text()
		//Ufeff problem fix
		if strings.HasPrefix(line, "\ufeff") {
			line = strings.Replace(line, "\ufeff", "", -1)
		}
		//The most jackass SRT parser ever. Appends to an object.
		i, err := strconv.Atoi(line)
		if err == nil && z.Id == 0 {
			z.Id = int(i)
			//A bit more jackassery, because if z.Start and z.End are set, then welp.
		} else if strings.Contains(line, "-->") && z.Start == "" && z.End == "" {
			split := strings.Split(line, "-->")

			//Oh hey, we occasionally can pick up extra whitespace, let's strip it
			z.Start = strings.Replace(split[0], " ", "", -1)
			z.End = strings.Replace(split[1], " ", "", -1)
		} else if line != "" && z.Start != "" && z.End != "" && z.Id != 0 {
			z.Line = append(z.Line, line)
		} else if line == "" {
			//Clear object on newline
			//But only append non-empty subtitles
			if z.Start != "" && z.End != "" && z.Line != nil {
				v.Subtitle.Content = append(v.Subtitle.Content, *z)
			}
			z = &Subtitle{}
		} else {
			//At some point, we need to start actually returning errors.
			//Wouldn't that be nice?
			return errors.New("Error parsing .srt. Stray newline?")
		}

	}
	//Since the last subtitle often won't have a newline, append everything and clear object one last time
	v.Subtitle.Content = append(v.Subtitle.Content, *z)
	z = &Subtitle{}
	defer f.Close()
	return nil
}

//ParseSrt is the loader for srt files. Takes the path of the file being opened as the argument.
func ParseSrt(filename string) (*SubRip, error) {
	v := &SubRip{}
	err := LoadSrt(v, filename)
	if err != nil {
		return v, err
	}
	return v, nil
}
<|endoftext|>


<commit_before>/*
Package stacksignal registers a signal handler for SIGUSR1.
Whenever the importing program receives SIGUSR1, a full
stacktrace of all goroutines will be printed to StdErr.

Since there are no functions to use, the import line should
be

	import _ "github.com/surma/stacksignal"
*/
package stacksignal

import (
	"fmt"
	"os"
	"os/signal"
	"runtime"
	"syscall"
)

const (
	VERSION = "1.1.0"
)

func init() {
	go func() {
		c := make(chan os.Signal)
		signal.Notify(c, syscall.SIGUSR1)
		n := 512
		for _ = range c {
			buf := make([]byte, n)
			for {
				m := runtime.Stack(buf, true)
				if m < n {
					break
				}
				n *= 2
				buf = make([]byte, n)
			}
			fmt.Fprintln(os.Stderr, "=== Begin stack trace")
			fmt.Fprintln(os.Stderr, string(buf))
			fmt.Fprintln(os.Stderr, "=== End stack trace")
		}
	}()
}
<commit_msg>Slice buffer to content length (fixes #1)<commit_after>/*
Package stacksignal registers a signal handler for SIGUSR1.
Whenever the importing program receives SIGUSR1, a full
stacktrace of all goroutines will be printed to StdErr.

Since there are no functions to use, the import line should
be

	import _ "github.com/surma/stacksignal"
*/
package stacksignal

import (
	"fmt"
	"os"
	"os/signal"
	"runtime"
	"syscall"
)

const (
	VERSION = "1.1.0"
)

func init() {
	go func() {
		c := make(chan os.Signal)
		signal.Notify(c, syscall.SIGUSR1)
		n := 512
		for _ = range c {
			buf := make([]byte, n)
			for {
				m := runtime.Stack(buf, true)
				if m < n {
					buf = buf[:m]
					break
				}
				n *= 2
				buf = make([]byte, n)
			}
			fmt.Fprintln(os.Stderr, "=== Begin stack trace")
			fmt.Fprintln(os.Stderr, string(buf))
			fmt.Fprintln(os.Stderr, "=== End stack trace")
		}
	}()
}
<|endoftext|>


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

package state

import (
	"io"
)

// State that should be saved between runs of the program.
type State struct {
	// The set of scores that are known to already exist in the blob store,
	// represented in hex. It is unnecessary to again store any blob with one of
	// these scores.
	ExistingScores StringSet

	// A version number for the above set of scores. Any time state is saved,
	// this version should first be updated to a random number and saved to the
	// backup registry, making sure that the old version was still the current
	// one. This protects us from drifting out of date if another process is
	// concurrently adding scores to the blob store.
	ExistingScoresVersion uint64
}

func LoadState(r io.Reader) (state State, err error)
func SaveState(w io.Writer, state State) (err error)
<commit_msg>Implemented LoadState and SaveState.<commit_after>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package state

import (
	"encoding/gob"
	"io"
)

// State that should be saved between runs of the program.
type State struct {
	// The set of scores that are known to already exist in the blob store,
	// represented in hex. It is unnecessary to again store any blob with one of
	// these scores.
	ExistingScores StringSet

	// A version number for the above set of scores. Any time state is saved,
	// this version should first be updated to a random number and saved to the
	// backup registry, making sure that the old version was still the current
	// one. This protects us from drifting out of date if another process is
	// concurrently adding scores to the blob store.
	ExistingScoresVersion uint64
}

func LoadState(r io.Reader) (state State, err error) {
	decoder := gob.NewDecoder(r)
	err = decoder.Decode(&state)
	return
}

func SaveState(w io.Writer, state State) (err error) {
	encoder := gob.NewEncoder(w)
	err = encoder.Encode(state)
	return
}
<|endoftext|>


<commit_before>package stats

import (
	"errors"
	"fmt"
	"sort"
	"strconv"
	"strings"
	"time"
)

const (
	counterString = `"counter"`
	gaugeString   = `"gauge"`
	trendString   = `"trend"`
	rateString    = `"rate"`

	defaultString = `"default"`
	timeString    = `"time"`
)

// Possible values for MetricType.
const (
	NoType  = MetricType(iota) // No type; metrics like this are ignored
	Counter                    // A counter that sums its data points
	Gauge                      // A gauge that displays the latest value
	Trend                      // A trend, min/max/avg/med are interesting
	Rate                       // A rate, displays % of values that aren't 0
)

// Possible values for ValueType.
const (
	Default = ValueType(iota) // Values are presented as-is
	Time                      // Values are timestamps (nanoseconds)
)

// The serialized metric type is invalid.
var ErrInvalidMetricType = errors.New("Invalid metric type")

// The serialized value type is invalid.
var ErrInvalidValueType = errors.New("Invalid value type")

// A MetricType specifies the type of a metric.
type MetricType int

// MarshalJSON serializes a MetricType as a human readable string.
func (t MetricType) MarshalJSON() ([]byte, error) {
	switch t {
	case Counter:
		return []byte(counterString), nil
	case Gauge:
		return []byte(gaugeString), nil
	case Trend:
		return []byte(trendString), nil
	case Rate:
		return []byte(rateString), nil
	default:
		return nil, ErrInvalidMetricType
	}
}

// UnmarshalJSON deserializes a MetricType from a string representation.
func (t *MetricType) UnmarshalJSON(data []byte) error {
	switch string(data) {
	case counterString:
		*t = Counter
	case gaugeString:
		*t = Gauge
	case trendString:
		*t = Trend
	case rateString:
		*t = Rate
	default:
		return ErrInvalidMetricType
	}

	return nil
}

func (t MetricType) String() string {
	switch t {
	case Counter:
		return counterString
	case Gauge:
		return gaugeString
	case Trend:
		return trendString
	case Rate:
		return rateString
	default:
		return "[INVALID]"
	}
}

// The type of values a metric contains.
type ValueType int

// MarshalJSON serializes a ValueType as a human readable string.
func (t ValueType) MarshalJSON() ([]byte, error) {
	switch t {
	case Default:
		return []byte(defaultString), nil
	case Time:
		return []byte(timeString), nil
	default:
		return nil, ErrInvalidValueType
	}
}

// UnmarshalJSON deserializes a ValueType from a string representation.
func (t *ValueType) UnmarshalJSON(data []byte) error {
	switch string(data) {
	case defaultString:
		*t = Default
	case timeString:
		*t = Time
	default:
		return ErrInvalidValueType
	}

	return nil
}

func (t ValueType) String() string {
	switch t {
	case Default:
		return defaultString
	case Time:
		return timeString
	default:
		return "[INVALID]"
	}
}

// A Sample is a single measurement.
type Sample struct {
	Metric *Metric
	Time   time.Time
	Tags   map[string]string
	Value  float64
}

// A Metric defines the shape of a set of data.
type Metric struct {
	Name     string     `json:"-"`
	Type     MetricType `json:"type"`
	Contains ValueType  `json:"contains"`

	// Filled in by the API when requested, the server side cannot count on its presence.
	Sample map[string]float64 `json:"sample"`

	// Set to true if the metric has failed a threshold.
	Tainted bool
}

func New(name string, typ MetricType, t ...ValueType) *Metric {
	vt := Default
	if len(t) > 0 {
		vt = t[0]
	}
	return &Metric{Name: name, Type: typ, Contains: vt}
}

func (m Metric) Humanize() string {
	sample := m.Sample
	switch len(sample) {
	case 0:
		return ""
	case 1:
		for _, v := range sample {
			return m.HumanizeValue(v)
		}
		return ""
	default:
		parts := make([]string, 0, len(m.Sample))
		for key, val := range m.Sample {
			parts = append(parts, fmt.Sprintf("%s=%s", key, m.HumanizeValue(val)))
		}
		sort.Strings(parts)
		return strings.Join(parts, ", ")
	}
}

func (m Metric) HumanizeValue(v float64) string {
	switch m.Type {
	case Rate:
		return strconv.FormatFloat(100*v, 'f', 2, 64) + "%"
	default:
		switch m.Contains {
		case Time:
			d := time.Duration(v)
			switch {
			case d > time.Minute:
				d -= d % (1 * time.Second)
			case d > time.Second:
				d -= d % (10 * time.Millisecond)
			case d > time.Millisecond:
				d -= d % (10 * time.Microsecond)
			case d > time.Microsecond:
				d -= d % (10 * time.Nanosecond)
			}
			return d.String()
		default:
			return strconv.FormatFloat(v, 'f', -1, 64)
		}
	}
}

func (m Metric) GetID() string {
	return m.Name
}

func (m *Metric) SetID(id string) error {
	m.Name = id
	return nil
}
<commit_msg>[refactor] Dropping NoType metrics<commit_after>package stats

import (
	"errors"
	"fmt"
	"sort"
	"strconv"
	"strings"
	"time"
)

const (
	counterString = `"counter"`
	gaugeString   = `"gauge"`
	trendString   = `"trend"`
	rateString    = `"rate"`

	defaultString = `"default"`
	timeString    = `"time"`
)

// Possible values for MetricType.
const (
	Counter = MetricType(iota) // A counter that sums its data points
	Gauge                      // A gauge that displays the latest value
	Trend                      // A trend, min/max/avg/med are interesting
	Rate                       // A rate, displays % of values that aren't 0
)

// Possible values for ValueType.
const (
	Default = ValueType(iota) // Values are presented as-is
	Time                      // Values are timestamps (nanoseconds)
)

// The serialized metric type is invalid.
var ErrInvalidMetricType = errors.New("Invalid metric type")

// The serialized value type is invalid.
var ErrInvalidValueType = errors.New("Invalid value type")

// A MetricType specifies the type of a metric.
type MetricType int

// MarshalJSON serializes a MetricType as a human readable string.
func (t MetricType) MarshalJSON() ([]byte, error) {
	switch t {
	case Counter:
		return []byte(counterString), nil
	case Gauge:
		return []byte(gaugeString), nil
	case Trend:
		return []byte(trendString), nil
	case Rate:
		return []byte(rateString), nil
	default:
		return nil, ErrInvalidMetricType
	}
}

// UnmarshalJSON deserializes a MetricType from a string representation.
func (t *MetricType) UnmarshalJSON(data []byte) error {
	switch string(data) {
	case counterString:
		*t = Counter
	case gaugeString:
		*t = Gauge
	case trendString:
		*t = Trend
	case rateString:
		*t = Rate
	default:
		return ErrInvalidMetricType
	}

	return nil
}

func (t MetricType) String() string {
	switch t {
	case Counter:
		return counterString
	case Gauge:
		return gaugeString
	case Trend:
		return trendString
	case Rate:
		return rateString
	default:
		return "[INVALID]"
	}
}

// The type of values a metric contains.
type ValueType int

// MarshalJSON serializes a ValueType as a human readable string.
func (t ValueType) MarshalJSON() ([]byte, error) {
	switch t {
	case Default:
		return []byte(defaultString), nil
	case Time:
		return []byte(timeString), nil
	default:
		return nil, ErrInvalidValueType
	}
}

// UnmarshalJSON deserializes a ValueType from a string representation.
func (t *ValueType) UnmarshalJSON(data []byte) error {
	switch string(data) {
	case defaultString:
		*t = Default
	case timeString:
		*t = Time
	default:
		return ErrInvalidValueType
	}

	return nil
}

func (t ValueType) String() string {
	switch t {
	case Default:
		return defaultString
	case Time:
		return timeString
	default:
		return "[INVALID]"
	}
}

// A Sample is a single measurement.
type Sample struct {
	Metric *Metric
	Time   time.Time
	Tags   map[string]string
	Value  float64
}

// A Metric defines the shape of a set of data.
type Metric struct {
	Name     string     `json:"-"`
	Type     MetricType `json:"type"`
	Contains ValueType  `json:"contains"`

	// Filled in by the API when requested, the server side cannot count on its presence.
	Sample map[string]float64 `json:"sample"`

	// Set to true if the metric has failed a threshold.
	Tainted bool
}

func New(name string, typ MetricType, t ...ValueType) *Metric {
	vt := Default
	if len(t) > 0 {
		vt = t[0]
	}
	return &Metric{Name: name, Type: typ, Contains: vt}
}

func (m Metric) Humanize() string {
	sample := m.Sample
	switch len(sample) {
	case 0:
		return ""
	case 1:
		for _, v := range sample {
			return m.HumanizeValue(v)
		}
		return ""
	default:
		parts := make([]string, 0, len(m.Sample))
		for key, val := range m.Sample {
			parts = append(parts, fmt.Sprintf("%s=%s", key, m.HumanizeValue(val)))
		}
		sort.Strings(parts)
		return strings.Join(parts, ", ")
	}
}

func (m Metric) HumanizeValue(v float64) string {
	switch m.Type {
	case Rate:
		return strconv.FormatFloat(100*v, 'f', 2, 64) + "%"
	default:
		switch m.Contains {
		case Time:
			d := time.Duration(v)
			switch {
			case d > time.Minute:
				d -= d % (1 * time.Second)
			case d > time.Second:
				d -= d % (10 * time.Millisecond)
			case d > time.Millisecond:
				d -= d % (10 * time.Microsecond)
			case d > time.Microsecond:
				d -= d % (10 * time.Nanosecond)
			}
			return d.String()
		default:
			return strconv.FormatFloat(v, 'f', -1, 64)
		}
	}
}

func (m Metric) GetID() string {
	return m.Name
}

func (m *Metric) SetID(id string) error {
	m.Name = id
	return nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package tchannel_test

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"strings"
	"testing"
	"time"

	. "github.com/uber/tchannel-go"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/uber/tchannel-go/testutils"
	"github.com/uber/tchannel-go/testutils/goroutines"
	"golang.org/x/net/context"
)

const streamRequestError = byte(255)

func makeRepeatedBytes(n byte) []byte {
	data := make([]byte, int(n))
	for i := byte(0); i < n; i++ {
		data[i] = n
	}
	return data
}

type streamHelper struct {
	t *testing.T
}

// startCall starts a call to echoStream and returns the arg3 reader and writer.
func (h streamHelper) startCall(ctx context.Context, ch *Channel, hostPort, serviceName string) (ArgWriter, ArgReader) {
	call, err := ch.BeginCall(ctx, hostPort, serviceName, "echoStream", nil)
	require.NoError(h.t, err, "BeginCall to echoStream failed")

	// Write empty headers
	require.NoError(h.t, NewArgWriter(call.Arg2Writer()).Write(nil), "Write empty headers failed")

	// Flush arg3 to force the call to start without any arg3.
	writer, err := call.Arg3Writer()
	require.NoError(h.t, err, "Arg3Writer failed")
	require.NoError(h.t, writer.Flush(), "Arg3Writer flush failed")

	// Read empty Headers
	response := call.Response()
	var arg2 []byte
	require.NoError(h.t, NewArgReader(response.Arg2Reader()).Read(&arg2), "Read headers failed")
	require.False(h.t, response.ApplicationError(), "echoStream failed due to application error")

	reader, err := response.Arg3Reader()
	require.NoError(h.t, err, "Arg3Reader failed")

	return writer, reader
}

// streamPartialHandler returns a streaming handler that has the following contract:
// read a byte, write N bytes where N = the byte that was read.
// The results are be written as soon as the byte is read.
func streamPartialHandler(t *testing.T, reportErrors bool) HandlerFunc {
	return func(ctx context.Context, call *InboundCall) {
		response := call.Response()
		onError := func(err error) {
			if reportErrors {
				t.Errorf("Handler error: %v", err)
			}
			response.SendSystemError(fmt.Errorf("failed to read arg2"))
		}

		var arg2 []byte
		if err := NewArgReader(call.Arg2Reader()).Read(&arg2); err != nil {
			onError(fmt.Errorf("failed to read arg2"))
			return
		}

		if err := NewArgWriter(response.Arg2Writer()).Write(nil); err != nil {
			onError(fmt.Errorf(""))
			return
		}

		argReader, err := call.Arg3Reader()
		if err != nil {
			onError(fmt.Errorf("failed to read arg3"))
			return
		}

		argWriter, err := response.Arg3Writer()
		if err != nil {
			onError(fmt.Errorf("arg3 writer failed"))
			return
		}

		// Flush arg3 which will force a frame with just arg2 to be sent.
		// The test reads arg2 before arg3 has been sent.
		if err := argWriter.Flush(); err != nil {
			onError(fmt.Errorf("arg3 flush failed"))
			return
		}

		arg3 := make([]byte, 1)
		for {
			n, err := argReader.Read(arg3)
			if err == io.EOF {
				break
			}
			if n == 0 && err == nil {
				err = fmt.Errorf("read 0 bytes")
			}
			if err != nil {
				onError(fmt.Errorf("arg3 Read failed: %v", err))
				return
			}

			// Magic number to cause a failure
			if arg3[0] == streamRequestError {
				// Make sure that the reader is closed.
				if err := argReader.Close(); err != nil {
					onError(fmt.Errorf("request error failed to close argReader: %v", err))
					return
				}

				response.SendSystemError(errors.New("intentional failure"))
				return
			}

			// Write the number of bytes as specified by arg3[0]
			if _, err := argWriter.Write(makeRepeatedBytes(arg3[0])); err != nil {
				onError(fmt.Errorf("argWriter Write failed: %v", err))
				return
			}
			if err := argWriter.Flush(); err != nil {
				onError(fmt.Errorf("argWriter flush failed: %v", err))
				return
			}
		}

		if err := argReader.Close(); err != nil {
			onError(fmt.Errorf("argReader Close failed: %v", err))
			return
		}

		if err := argWriter.Close(); err != nil {
			onError(fmt.Errorf("arg3writer Close failed: %v", err))
			return
		}
	}
}

func testStreamArg(t *testing.T, f func(argWriter ArgWriter, argReader ArgReader)) {
	defer testutils.SetTimeout(t, 2*time.Second)()
	ctx, cancel := NewContext(time.Second)
	defer cancel()

	helper := streamHelper{t}
	WithVerifiedServer(t, nil, func(ch *Channel, hostPort string) {
		ch.Register(streamPartialHandler(t, true /* report errors */), "echoStream")

		argWriter, argReader := helper.startCall(ctx, ch, hostPort, ch.ServiceName())
		verifyBytes := func(n byte) {
			_, err := argWriter.Write([]byte{n})
			require.NoError(t, err, "arg3 write failed")
			require.NoError(t, argWriter.Flush(), "arg3 flush failed")

			arg3 := make([]byte, int(n))
			_, err = io.ReadFull(argReader, arg3)
			require.NoError(t, err, "arg3 read failed")

			assert.Equal(t, makeRepeatedBytes(n), arg3, "arg3 result mismatch")
		}

		verifyBytes(0)
		verifyBytes(5)
		verifyBytes(100)
		verifyBytes(1)

		f(argWriter, argReader)
	})
}

func TestStreamPartialArg(t *testing.T) {
	testStreamArg(t, func(argWriter ArgWriter, argReader ArgReader) {
		require.NoError(t, argWriter.Close(), "arg3 close failed")

		// Once closed, we expect the reader to return EOF
		n, err := io.Copy(ioutil.Discard, argReader)
		assert.Equal(t, int64(0), n, "arg2 reader expected to EOF after arg3 writer is closed")
		assert.NoError(t, err, "Copy should not fail")
		assert.NoError(t, argReader.Close(), "close arg reader failed")
	})
}

func TestStreamSendError(t *testing.T) {
	testStreamArg(t, func(argWriter ArgWriter, argReader ArgReader) {
		// Send the magic number to request an error.
		_, err := argWriter.Write([]byte{streamRequestError})
		require.NoError(t, err, "arg3 write failed")
		require.NoError(t, argWriter.Close(), "arg3 close failed")

		// Now we expect an error on our next read.
		_, err = ioutil.ReadAll(argReader)
		assert.Error(t, err, "ReadAll should fail")
		assert.True(t, strings.Contains(err.Error(), "intentional failure"), "err %v unexpected", err)
	})
}

func TestStreamCancelled(t *testing.T) {
	server := testutils.NewServer(t, nil)
	server.Register(streamPartialHandler(t, false /* report errors */), "echoStream")

	ctx, cancel := NewContext(testutils.Timeout(20 * time.Millisecond))
	defer cancel()

	helper := streamHelper{t}
	WithVerifiedServer(t, nil, func(ch *Channel, _ string) {
		callCtx, callCancel := context.WithCancel(ctx)
		cancelContext := make(chan struct{})

		arg3Writer, arg3Reader := helper.startCall(callCtx, ch, server.PeerInfo().HostPort, server.ServiceName())
		go func() {
			for i := 0; i < 10; i++ {
				_, err := arg3Writer.Write([]byte{1})
				assert.NoError(t, err, "Write failed")
				assert.NoError(t, arg3Writer.Flush(), "Flush failed")
			}

			// Our reads and writes should fail now.
			<-cancelContext
			callCancel()

			_, err := arg3Writer.Write([]byte{1})
			// The write will succeed since it's buffered.
			assert.NoError(t, err, "Write after fail should be buffered")
			assert.Error(t, arg3Writer.Flush(), "writer.Flush should fail after cancel")
			assert.Error(t, arg3Writer.Close(), "writer.Close should fail after cancel")
		}()

		for i := 0; i < 10; i++ {
			arg3 := make([]byte, 1)
			n, err := arg3Reader.Read(arg3)
			assert.Equal(t, 1, n, "Read did not correct number of bytes")
			assert.NoError(t, err, "Read failed")
		}

		close(cancelContext)

		n, err := io.Copy(ioutil.Discard, arg3Reader)
		assert.EqualValues(t, 0, n, "Read should not read any bytes after cancel")
		assert.Error(t, err, "Read should fail after cancel")
		assert.Error(t, arg3Reader.Close(), "reader.Close should fail after cancel")
	})

	// TODO(prashant): Once calls are cancelled when the connection is closed, this
	// can be removed, since the calls should fail.

	<-ctx.Done()

	server.Close()
	waitForChannelClose(t, server)
	goroutines.VerifyNoLeaks(t, nil)
}
<commit_msg>Increase timeout for TestStreamCancelled<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package tchannel_test

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"strings"
	"testing"
	"time"

	. "github.com/uber/tchannel-go"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/uber/tchannel-go/testutils"
	"github.com/uber/tchannel-go/testutils/goroutines"
	"golang.org/x/net/context"
)

const streamRequestError = byte(255)

func makeRepeatedBytes(n byte) []byte {
	data := make([]byte, int(n))
	for i := byte(0); i < n; i++ {
		data[i] = n
	}
	return data
}

type streamHelper struct {
	t *testing.T
}

// startCall starts a call to echoStream and returns the arg3 reader and writer.
func (h streamHelper) startCall(ctx context.Context, ch *Channel, hostPort, serviceName string) (ArgWriter, ArgReader) {
	call, err := ch.BeginCall(ctx, hostPort, serviceName, "echoStream", nil)
	require.NoError(h.t, err, "BeginCall to echoStream failed")

	// Write empty headers
	require.NoError(h.t, NewArgWriter(call.Arg2Writer()).Write(nil), "Write empty headers failed")

	// Flush arg3 to force the call to start without any arg3.
	writer, err := call.Arg3Writer()
	require.NoError(h.t, err, "Arg3Writer failed")
	require.NoError(h.t, writer.Flush(), "Arg3Writer flush failed")

	// Read empty Headers
	response := call.Response()
	var arg2 []byte
	require.NoError(h.t, NewArgReader(response.Arg2Reader()).Read(&arg2), "Read headers failed")
	require.False(h.t, response.ApplicationError(), "echoStream failed due to application error")

	reader, err := response.Arg3Reader()
	require.NoError(h.t, err, "Arg3Reader failed")

	return writer, reader
}

// streamPartialHandler returns a streaming handler that has the following contract:
// read a byte, write N bytes where N = the byte that was read.
// The results are be written as soon as the byte is read.
func streamPartialHandler(t *testing.T, reportErrors bool) HandlerFunc {
	return func(ctx context.Context, call *InboundCall) {
		response := call.Response()
		onError := func(err error) {
			if reportErrors {
				t.Errorf("Handler error: %v", err)
			}
			response.SendSystemError(fmt.Errorf("failed to read arg2"))
		}

		var arg2 []byte
		if err := NewArgReader(call.Arg2Reader()).Read(&arg2); err != nil {
			onError(fmt.Errorf("failed to read arg2"))
			return
		}

		if err := NewArgWriter(response.Arg2Writer()).Write(nil); err != nil {
			onError(fmt.Errorf(""))
			return
		}

		argReader, err := call.Arg3Reader()
		if err != nil {
			onError(fmt.Errorf("failed to read arg3"))
			return
		}

		argWriter, err := response.Arg3Writer()
		if err != nil {
			onError(fmt.Errorf("arg3 writer failed"))
			return
		}

		// Flush arg3 which will force a frame with just arg2 to be sent.
		// The test reads arg2 before arg3 has been sent.
		if err := argWriter.Flush(); err != nil {
			onError(fmt.Errorf("arg3 flush failed"))
			return
		}

		arg3 := make([]byte, 1)
		for {
			n, err := argReader.Read(arg3)
			if err == io.EOF {
				break
			}
			if n == 0 && err == nil {
				err = fmt.Errorf("read 0 bytes")
			}
			if err != nil {
				onError(fmt.Errorf("arg3 Read failed: %v", err))
				return
			}

			// Magic number to cause a failure
			if arg3[0] == streamRequestError {
				// Make sure that the reader is closed.
				if err := argReader.Close(); err != nil {
					onError(fmt.Errorf("request error failed to close argReader: %v", err))
					return
				}

				response.SendSystemError(errors.New("intentional failure"))
				return
			}

			// Write the number of bytes as specified by arg3[0]
			if _, err := argWriter.Write(makeRepeatedBytes(arg3[0])); err != nil {
				onError(fmt.Errorf("argWriter Write failed: %v", err))
				return
			}
			if err := argWriter.Flush(); err != nil {
				onError(fmt.Errorf("argWriter flush failed: %v", err))
				return
			}
		}

		if err := argReader.Close(); err != nil {
			onError(fmt.Errorf("argReader Close failed: %v", err))
			return
		}

		if err := argWriter.Close(); err != nil {
			onError(fmt.Errorf("arg3writer Close failed: %v", err))
			return
		}
	}
}

func testStreamArg(t *testing.T, f func(argWriter ArgWriter, argReader ArgReader)) {
	defer testutils.SetTimeout(t, 2*time.Second)()
	ctx, cancel := NewContext(time.Second)
	defer cancel()

	helper := streamHelper{t}
	WithVerifiedServer(t, nil, func(ch *Channel, hostPort string) {
		ch.Register(streamPartialHandler(t, true /* report errors */), "echoStream")

		argWriter, argReader := helper.startCall(ctx, ch, hostPort, ch.ServiceName())
		verifyBytes := func(n byte) {
			_, err := argWriter.Write([]byte{n})
			require.NoError(t, err, "arg3 write failed")
			require.NoError(t, argWriter.Flush(), "arg3 flush failed")

			arg3 := make([]byte, int(n))
			_, err = io.ReadFull(argReader, arg3)
			require.NoError(t, err, "arg3 read failed")

			assert.Equal(t, makeRepeatedBytes(n), arg3, "arg3 result mismatch")
		}

		verifyBytes(0)
		verifyBytes(5)
		verifyBytes(100)
		verifyBytes(1)

		f(argWriter, argReader)
	})
}

func TestStreamPartialArg(t *testing.T) {
	testStreamArg(t, func(argWriter ArgWriter, argReader ArgReader) {
		require.NoError(t, argWriter.Close(), "arg3 close failed")

		// Once closed, we expect the reader to return EOF
		n, err := io.Copy(ioutil.Discard, argReader)
		assert.Equal(t, int64(0), n, "arg2 reader expected to EOF after arg3 writer is closed")
		assert.NoError(t, err, "Copy should not fail")
		assert.NoError(t, argReader.Close(), "close arg reader failed")
	})
}

func TestStreamSendError(t *testing.T) {
	testStreamArg(t, func(argWriter ArgWriter, argReader ArgReader) {
		// Send the magic number to request an error.
		_, err := argWriter.Write([]byte{streamRequestError})
		require.NoError(t, err, "arg3 write failed")
		require.NoError(t, argWriter.Close(), "arg3 close failed")

		// Now we expect an error on our next read.
		_, err = ioutil.ReadAll(argReader)
		assert.Error(t, err, "ReadAll should fail")
		assert.True(t, strings.Contains(err.Error(), "intentional failure"), "err %v unexpected", err)
	})
}

func TestStreamCancelled(t *testing.T) {
	server := testutils.NewServer(t, nil)
	server.Register(streamPartialHandler(t, false /* report errors */), "echoStream")

	ctx, cancel := NewContext(testutils.Timeout(50 * time.Millisecond))
	defer cancel()

	helper := streamHelper{t}
	WithVerifiedServer(t, nil, func(ch *Channel, _ string) {
		callCtx, callCancel := context.WithCancel(ctx)
		cancelContext := make(chan struct{})

		arg3Writer, arg3Reader := helper.startCall(callCtx, ch, server.PeerInfo().HostPort, server.ServiceName())
		go func() {
			for i := 0; i < 10; i++ {
				_, err := arg3Writer.Write([]byte{1})
				assert.NoError(t, err, "Write failed")
				assert.NoError(t, arg3Writer.Flush(), "Flush failed")
			}

			// Our reads and writes should fail now.
			<-cancelContext
			callCancel()

			_, err := arg3Writer.Write([]byte{1})
			// The write will succeed since it's buffered.
			assert.NoError(t, err, "Write after fail should be buffered")
			assert.Error(t, arg3Writer.Flush(), "writer.Flush should fail after cancel")
			assert.Error(t, arg3Writer.Close(), "writer.Close should fail after cancel")
		}()

		for i := 0; i < 10; i++ {
			arg3 := make([]byte, 1)
			n, err := arg3Reader.Read(arg3)
			assert.Equal(t, 1, n, "Read did not correct number of bytes")
			assert.NoError(t, err, "Read failed")
		}

		close(cancelContext)

		n, err := io.Copy(ioutil.Discard, arg3Reader)
		assert.EqualValues(t, 0, n, "Read should not read any bytes after cancel")
		assert.Error(t, err, "Read should fail after cancel")
		assert.Error(t, arg3Reader.Close(), "reader.Close should fail after cancel")
	})

	// TODO(prashant): Once calls are cancelled when the connection is closed, this
	// can be removed, since the calls should fail.

	<-ctx.Done()

	server.Close()
	waitForChannelClose(t, server)
	goroutines.VerifyNoLeaks(t, nil)
}
<|endoftext|>


<commit_before>// ------------ format struct to url form -----------------------------------
func StructToUrlValues(st interface{}) url.Values {

	s := reflect.ValueOf(st)
	typeOf := s.Type()

	// get length of struct
	length := s.NumField()

	// create map of url.Values
	m := make(map[string][]string)

	// loop over struct
	for i := 0; i < length; i++ {

		var value string
		val := s.Field(i)

		// check struct value type and convert it to string
		switch s.Field(i).Kind() {
		case reflect.Int:
			value = strconv.FormatInt(val.Int(), 10)
		case reflect.Float64:
			value = strconv.FormatFloat(val.Float(), 'f', 2, 64)
		case reflect.Float32:
			value = strconv.FormatFloat(val.Float(), 'f', 2, 32)
		case reflect.Bool:
			value = strconv.FormatBool(val.Bool())
		case reflect.String:
			value = val.String()
		}

		// get struct Tag
		field := typeOf.Field(i).Tag.Get("urlVal")

		// update map
		if field == "" {
			m[typeOf.Field(i).Name] = append(m[field], value)
		} else {
			m[field] = append(m[typeOf.Field(i).Name], value)
		}
	}
	// return struct converted to map
	return m
}
<commit_msg>fix missing package declaration<commit_after>package httpUtil

// ------------ format struct to url form -----------------------------------
func StructToUrlValues(st interface{}) url.Values {

	s := reflect.ValueOf(st)
	typeOf := s.Type()

	// get length of struct
	length := s.NumField()

	// create map of url.Values
	m := make(map[string][]string)

	// loop over struct
	for i := 0; i < length; i++ {

		var value string
		val := s.Field(i)

		// check struct value type and convert it to string
		switch s.Field(i).Kind() {
		case reflect.Int:
			value = strconv.FormatInt(val.Int(), 10)
		case reflect.Float64:
			value = strconv.FormatFloat(val.Float(), 'f', 2, 64)
		case reflect.Float32:
			value = strconv.FormatFloat(val.Float(), 'f', 2, 32)
		case reflect.Bool:
			value = strconv.FormatBool(val.Bool())
		case reflect.String:
			value = val.String()
		}

		// get struct Tag
		field := typeOf.Field(i).Tag.Get("urlVal")

		// update map
		if field == "" {
			m[typeOf.Field(i).Name] = append(m[field], value)
		} else {
			m[field] = append(m[typeOf.Field(i).Name], value)
		}
	}
	// return struct converted to map
	return m
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"time"

	"gopkg.in/urfave/cli.v1"

	"gopkg.in/urfave/cli.v1/altsrc"
)

func main() {
	app := cli.NewApp()

	var (
		testInt64    int64
		testUint64   uint64
		testUint     uint
		testInt      int
		testInt2     int
		testDuration time.Duration
	)

	app.Flags = []cli.Flag{
		altsrc.NewInt64Flag(cli.Int64Flag{
			Name:        "test-int-64",
			Destination: &testInt64,
		}),
		altsrc.NewUint64Flag(cli.Uint64Flag{
			Name:        "test-uint",
			Destination: &testUint64,
		}),
		altsrc.NewIntFlag(cli.IntFlag{
			Name:        "test-int",
			Destination: &testInt,
		}),
		altsrc.NewDurationFlag(cli.DurationFlag{
			Name:        "test-duration",
			Destination: &testDuration,
		}),
		cli.StringFlag{
			Name: "config, c",
		},
	}
	app.Commands = []cli.Command{
		cli.Command{
			Name: "sub",
			Flags: []cli.Flag{cli.IntFlag{
				Name:        "test-int2",
				Destination: &testInt,
			}},
			Action: func(c *cli.Context) {
				fmt.Println(testInt)
			},
		},
	}

	app.Action = func(c *cli.Context) error {
		fmt.Println(testInt)
		fmt.Println(testInt2)
		fmt.Println(testInt64)
		fmt.Println(testUint64)
		fmt.Println(testUint)
		fmt.Println(testDuration)
		return nil
	}

	// // TOML:
	// app.Before = altsrc.InitInputSourceWithContext(app.Flags, altsrc.NewTomlSourceFromFlagFunc("config"))

	// YAML:
	// app.Before = altsrc.InitInputSourceWithContext(app.Flags, altsrc.NewYamlSourceFromFlagFunc("config"))
	app.Run(os.Args)
}
<commit_msg>remove old example<commit_after><|endoftext|>


<commit_before>//
// Copyright (c) 2016 Jon Carlson.  All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
//

package main

import "fmt"
import "sort"
import "database/sql"
import "github.com/joncrlsn/pgutil"
import "github.com/joncrlsn/misc"

// ==================================
// TriggerRows definition
// ==================================

// TriggerRows is a sortable slice of string maps
type TriggerRows []map[string]string

func (slice TriggerRows) Len() int {
	return len(slice)
}

func (slice TriggerRows) Less(i, j int) bool {
	if slice[i]["table_name"] != slice[j]["table_name"] {
		return slice[i]["table_name"] < slice[j]["table_name"]
	}
	return slice[i]["trigger_name"] < slice[j]["trigger_name"]
}

func (slice TriggerRows) Swap(i, j int) {
	slice[i], slice[j] = slice[j], slice[i]
}

// TriggerSchema holds a channel streaming trigger information from one of the databases as well as
// a reference to the current row of data we're viewing.
//
// TriggerSchema implements the Schema interface defined in pgdiff.go
type TriggerSchema struct {
	rows   TriggerRows
	rowNum int
	done   bool
}

// get returns the value from the current row for the given key
func (c *TriggerSchema) get(key string) string {
	if c.rowNum >= len(c.rows) {
		return ""
	}
	return c.rows[c.rowNum][key]
}

// NextRow increments the rowNum and tells you whether or not there are more
func (c *TriggerSchema) NextRow() bool {
	if c.rowNum >= len(c.rows)-1 {
		c.done = true
	}
	c.rowNum = c.rowNum + 1
	return !c.done
}

// Compare tells you, in one pass, whether or not the first row matches, is less than, or greater than the second row
func (c *TriggerSchema) Compare(obj interface{}) int {
	c2, ok := obj.(*TriggerSchema)
	if !ok {
		fmt.Println("Error!!!, Compare(obj) needs a TriggerSchema instance", c2)
		return +999
	}

	val := misc.CompareStrings(c.get("table_name"), c2.get("table_name"))
	if val != 0 {
		return val
	}
	val = misc.CompareStrings(c.get("trigger_name"), c2.get("trigger_name"))
	return val
}

// Add returns SQL to create the trigger
func (c TriggerSchema) Add() {
	fmt.Printf("%s;\n", c.get("definition"))
}

// Drop returns SQL to drop the trigger
func (c TriggerSchema) Drop() {
	fmt.Printf("DROP TRIGGER %s ON %s;\n", c.get("trigger_name"), c.get("table_name"))
}

// Change handles the case where the trigger names match, but the definition does not
func (c TriggerSchema) Change(obj interface{}) {
	c2, ok := obj.(*TriggerSchema)
	if !ok {
		fmt.Println("Error!!!, Change needs a TriggerSchema instance", c2)
	}
	if c.get("definition") != c2.get("definition") {
		fmt.Println("-- This function looks different so we'll recreate it:")
		// The definition column has everything needed to rebuild the function
		fmt.Println("-- STATEMENT-BEGIN")
		fmt.Println(c.get("definition"))
		fmt.Println("-- STATEMENT-END")
	}
}

// compareTriggers outputs SQL to make the triggers match between DBs
func compareTriggers(conn1 *sql.DB, conn2 *sql.DB) {
	sql := `
    SELECT tbl.nspname || '.' || tbl.relname AS table_name
    	, t.tgname AS trigger_name
    	, pg_catalog.pg_get_triggerdef(t.oid, true) AS definition
    	, t.tgenabled AS enabled
    FROM pg_catalog.pg_trigger t
    INNER JOIN (
    	SELECT c.oid, n.nspname, c.relname
    	FROM pg_catalog.pg_class c
    	JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace AND n.nspname NOT LIKE 'pg_%')
    	WHERE pg_catalog.pg_table_is_visible(c.oid)) AS tbl
    	ON (tbl.oid = t.tgrelid)
    AND NOT t.tgisinternal
    ORDER BY 1;
	`

	rowChan1, _ := pgutil.QueryStrings(conn1, sql)
	rowChan2, _ := pgutil.QueryStrings(conn2, sql)

	rows1 := make(TriggerRows, 0)
	for row := range rowChan1 {
		rows1 = append(rows1, row)
	}
	sort.Sort(rows1)

	rows2 := make(TriggerRows, 0)
	for row := range rowChan2 {
		rows2 = append(rows2, row)
	}
	sort.Sort(rows2)

	// We must explicitly type this as Schema here
	var schema1 Schema = &TriggerSchema{rows: rows1, rowNum: -1}
	var schema2 Schema = &TriggerSchema{rows: rows2, rowNum: -1}

	// Compare the triggers
	doDiff(schema1, schema2)
}
<commit_msg>Improved trigger query for PG 9.5<commit_after>//
// Copyright (c) 2016 Jon Carlson.  All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
//

package main

import "fmt"
import "sort"
import "database/sql"
import "github.com/joncrlsn/pgutil"
import "github.com/joncrlsn/misc"

// ==================================
// TriggerRows definition
// ==================================

// TriggerRows is a sortable slice of string maps
type TriggerRows []map[string]string

func (slice TriggerRows) Len() int {
	return len(slice)
}

func (slice TriggerRows) Less(i, j int) bool {
	if slice[i]["table_name"] != slice[j]["table_name"] {
		return slice[i]["table_name"] < slice[j]["table_name"]
	}
	return slice[i]["trigger_name"] < slice[j]["trigger_name"]
}

func (slice TriggerRows) Swap(i, j int) {
	slice[i], slice[j] = slice[j], slice[i]
}

// TriggerSchema holds a channel streaming trigger information from one of the databases as well as
// a reference to the current row of data we're viewing.
//
// TriggerSchema implements the Schema interface defined in pgdiff.go
type TriggerSchema struct {
	rows   TriggerRows
	rowNum int
	done   bool
}

// get returns the value from the current row for the given key
func (c *TriggerSchema) get(key string) string {
	if c.rowNum >= len(c.rows) {
		return ""
	}
	return c.rows[c.rowNum][key]
}

// NextRow increments the rowNum and tells you whether or not there are more
func (c *TriggerSchema) NextRow() bool {
	if c.rowNum >= len(c.rows)-1 {
		c.done = true
	}
	c.rowNum = c.rowNum + 1
	return !c.done
}

// Compare tells you, in one pass, whether or not the first row matches, is less than, or greater than the second row
func (c *TriggerSchema) Compare(obj interface{}) int {
	c2, ok := obj.(*TriggerSchema)
	if !ok {
		fmt.Println("Error!!!, Compare(obj) needs a TriggerSchema instance", c2)
		return +999
	}

	val := misc.CompareStrings(c.get("table_name"), c2.get("table_name"))
	if val != 0 {
		return val
	}
	val = misc.CompareStrings(c.get("trigger_name"), c2.get("trigger_name"))
	return val
}

// Add returns SQL to create the trigger
func (c TriggerSchema) Add() {
	fmt.Printf("%s;\n", c.get("definition"))
}

// Drop returns SQL to drop the trigger
func (c TriggerSchema) Drop() {
	fmt.Printf("DROP TRIGGER %s ON %s;\n", c.get("trigger_name"), c.get("table_name"))
}

// Change handles the case where the trigger names match, but the definition does not
func (c TriggerSchema) Change(obj interface{}) {
	c2, ok := obj.(*TriggerSchema)
	if !ok {
		fmt.Println("Error!!!, Change needs a TriggerSchema instance", c2)
	}
	if c.get("definition") != c2.get("definition") {
		fmt.Println("-- This function looks different so we'll recreate it:")
		// The definition column has everything needed to rebuild the function
		fmt.Println("-- STATEMENT-BEGIN")
		fmt.Println(c.get("definition"))
		fmt.Println("-- STATEMENT-END")
	}
}

// compareTriggers outputs SQL to make the triggers match between DBs
func compareTriggers(conn1 *sql.DB, conn2 *sql.DB) {
	sql := `
    SELECT n.nspname || '.' || c.relname AS table_name
       , t.tgname AS trigger_name
       , pg_catalog.pg_get_triggerdef(t.oid, true) AS definition
       , t.tgenabled AS enabled
    FROM pg_catalog.pg_trigger t
    INNER JOIN pg_catalog.pg_class c ON (c.oid = t.tgrelid)
    INNER JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace AND n.nspname NOT LIKE 'pg_%');
	`

	rowChan1, _ := pgutil.QueryStrings(conn1, sql)
	rowChan2, _ := pgutil.QueryStrings(conn2, sql)

	rows1 := make(TriggerRows, 0)
	for row := range rowChan1 {
		rows1 = append(rows1, row)
	}
	sort.Sort(rows1)

	rows2 := make(TriggerRows, 0)
	for row := range rowChan2 {
		rows2 = append(rows2, row)
	}
	sort.Sort(rows2)

	// We must explicitly type this as Schema here
	var schema1 Schema = &TriggerSchema{rows: rows1, rowNum: -1}
	var schema2 Schema = &TriggerSchema{rows: rows2, rowNum: -1}

	// Compare the triggers
	doDiff(schema1, schema2)
}
<|endoftext|>


<commit_before>package main

import (
	"strings"
	"testing"
	"unicode"

	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app"
	"github.com/BytemarkHosting/bytemark-client/util/log"
	"github.com/urfave/cli"
)

// This test ensures that all commands have an Action, Description, Usage and UsageText
// and that all their subcommands do too.
func TestCommandsComplete(t *testing.T) {
	traverseAllCommands(commands, func(c cli.Command) {
		emptyThings := make([]string, 0, 4)
		if c.Name == "" {
			log.Log("There is a command with an empty Name.")
			t.Fail()
		}
		if c.Usage == "" {
			emptyThings = append(emptyThings, "Usage")
		}
		if c.UsageText == "" {
			emptyThings = append(emptyThings, "UsageText")
		}
		if c.Description == "" {
			emptyThings = append(emptyThings, "Description")
		}
		if c.Action == nil {
			emptyThings = append(emptyThings, "Action")
		}
		if len(emptyThings) > 0 {
			t.Fail()
			log.Logf("Command %s has empty %s.\r\n", c.FullName(), strings.Join(emptyThings, ", "))
		}
	})

}

type stringPredicate func(string) bool

func checkFlagUsage(f cli.Flag, predicate stringPredicate) bool {
	switch f := f.(type) {
	case cli.BoolFlag:
		return predicate(f.Usage)
	case cli.BoolTFlag:
		return predicate(f.Usage)
	case cli.DurationFlag:
		return predicate(f.Usage)
	case cli.Float64Flag:
		return predicate(f.Usage)
	case cli.GenericFlag:
		return predicate(f.Usage)
	case cli.StringFlag:
		return predicate(f.Usage)
	case cli.StringSliceFlag:
		return predicate(f.Usage)
	}
	return false
}

func isEmpty(s string) bool {
	return s == ""
}
func notEmpty(s string) bool {
	return s != ""
}

func firstIsUpper(s string) bool {
	runes := []rune(s)
	return unicode.IsUpper(runes[0])
}

func hasFullStop(s string) bool {
	return strings.Contains(s, ".")
}

func TestFlagsHaveUsage(t *testing.T) {
	traverseAllCommands(commands, func(c cli.Command) {
		for _, f := range c.Flags {
			if checkFlagUsage(f, isEmpty) {
				t.Errorf("Command %s's flag %s has empty usage\r\n", c.FullName(), f.GetName())
			}
		}
	})
	for _, flag := range app.GlobalFlags() {
		if checkFlagUsage(flag, isEmpty) {
			t.Errorf("Global flag %s doesn't have usage.", flag.GetName())
		}
	}
}

func TestUsageStyleConformance(t *testing.T) {
	traverseAllCommands(commands, func(c cli.Command) {
		if firstIsUpper(c.Usage) {
			t.Errorf("Command %s's Usage begins with an uppercase letter. Please change it - Usages should be lowercase.\r\n", c.FullName())
		}

		if hasFullStop(c.Usage) {
			t.Errorf("Command %s's Usage has a full-stop. Get rid of it.\r\n", c.FullName())
		}
	})
}

// Tests for commands which have subcommands having the correct Description format
// the first line should start lowercase and end without a full stop, and the second
// should be blank
func TestSubcommandStyleConformance(t *testing.T) {
	traverseAllCommands(commands, func(c cli.Command) {
		if c.Subcommands == nil {
			return
		}
		if len(c.Subcommands) == 0 {
			return
		}
		lines := strings.Split(c.Description, "\n")
		desc := []rune(lines[0])
		if unicode.IsUpper(desc[0]) {
			log.Logf("Command %s's Description begins with an uppercase letter, but it has subcommands, so should be lowercase.\r\n", c.FullName())
			t.Fail()
		}
		if strings.Contains(lines[0], ".") {
			log.Logf("The first line of Command %s's Description contains a full stop. It shouldn't.\r\n", c.FullName())
			t.Fail()
		}
		if len(lines) > 1 {
			if len(strings.TrimSpace(lines[1])) > 0 {
				log.Logf("The second line of Command %s's Description should be blank.\r\n", c.FullName())
				t.Fail()
			}
		}

	})
}
<commit_msg>- use new tarverse commands with context method to output more meaningful commands that have failed conformity tests - update method of getting commands list to include admin commands - caught bug where it crashed on trying to find first character of a sub commands usage or description that does not have either of those defined<commit_after>package main

import (
	"reflect"
	"runtime"
	"strings"
	"testing"
	"unicode"

	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app"
	"github.com/BytemarkHosting/bytemark-client/util/log"
	"github.com/urfave/cli"
)

// This test ensures that all commands have an Action, Description, Usage and UsageText
// and that all their subcommands do too.
func TestCommandsComplete(t *testing.T) {

	// TODO: Add descriptions to admin commands. it's necessary now
	t.Skip("Need to add descriptions for admin commands.")
	traverseAllCommands(Commands(true), func(c cli.Command) {
		emptyThings := make([]string, 0, 4)
		if c.Name == "" {
			log.Log("There is a command with an empty Name.")
			t.Fail()
		}
		// if a command is only usable via its sub commands, and its usage is built from the
		// subcommands usage, its not necessary to check it.
		// incredibly hacky because this asks for the name of the method, and if it matches, just ignore it
		f := runtime.FuncForPC(reflect.ValueOf(c.Action).Pointer()).Name()
		if f == "github.com/BytemarkHosting/bytemark-client/vendor/github.com/urfave/cli.ShowSubcommandHelp" {
			return
		}

		if c.Usage == "" {
			emptyThings = append(emptyThings, "Usage")
		}
		if c.UsageText == "" {
			emptyThings = append(emptyThings, "UsageText")
		}
		if c.Description == "" {
			emptyThings = append(emptyThings, "Description")
		}
		if c.Action == nil {
			emptyThings = append(emptyThings, "Action")
		}
		if len(emptyThings) > 0 {
			t.Fail()
			log.Logf("Command %s has empty %s.\r\n", c.FullName(), strings.Join(emptyThings, ", "))
		}
	})

}

type stringPredicate func(string) bool

func checkFlagUsage(f cli.Flag, predicate stringPredicate) bool {
	switch f := f.(type) {
	case cli.BoolFlag:
		return predicate(f.Usage)
	case cli.BoolTFlag:
		return predicate(f.Usage)
	case cli.DurationFlag:
		return predicate(f.Usage)
	case cli.Float64Flag:
		return predicate(f.Usage)
	case cli.GenericFlag:
		return predicate(f.Usage)
	case cli.StringFlag:
		return predicate(f.Usage)
	case cli.StringSliceFlag:
		return predicate(f.Usage)
	}
	return false
}

func isEmpty(s string) bool {
	return s == ""
}
func notEmpty(s string) bool {
	return s != ""
}

func firstIsUpper(s string) bool {
	if s == "" {
		return false
	}

	runes := []rune(s)
	return unicode.IsUpper(runes[0])
}

func hasFullStop(s string) bool {
	return strings.Contains(s, ".")
}

func TestFlagsHaveUsage(t *testing.T) {
	traverseAllCommands(Commands(true), func(c cli.Command) {
		for _, f := range c.Flags {
			if checkFlagUsage(f, isEmpty) {
				t.Errorf("Command %s's flag %s has empty usage\r\n", c.FullName(), f.GetName())
			}
		}
	})
	for _, flag := range app.GlobalFlags() {
		if checkFlagUsage(flag, isEmpty) {
			t.Errorf("Global flag %s doesn't have usage.", flag.GetName())
		}
	}
}

func TestUsageStyleConformance(t *testing.T) {
	traverseAllCommandsWithContext(Commands(true), "", func(name string, c cli.Command) {
		// TODO: see if this will actually just ruin tests. pretty sure it will

		if firstIsUpper(c.Usage) {
			t.Errorf("Command %s's Usage begins with an uppercase letter. Please change it - Usages should be lowercase.\r\n", name)
		}

		if hasFullStop(c.Usage) {
			t.Errorf("Command %s's Usage has a full-stop. Get rid of it.\r\n", name)
		}
	})
}

// Tests for commands which have subcommands having the correct Description format
// the first line should start lowercase and end without a full stop, and the second
// should be blank
func TestSubcommandStyleConformance(t *testing.T) {
	traverseAllCommands(Commands(true), func(c cli.Command) {
		if c.Subcommands == nil {
			return
		}
		if len(c.Subcommands) == 0 {
			return
		}
		if c.Description == "" {
			return
		}
		lines := strings.Split(c.Description, "\n")
		desc := []rune(lines[0])
		if unicode.IsUpper(desc[0]) {
			log.Logf("Command %s's Description begins with an uppercase letter, but it has subcommands, so should be lowercase.\r\n", c.FullName())
			t.Fail()
		}
		if strings.Contains(lines[0], ".") {
			log.Logf("The first line of Command %s's Description contains a full stop. It shouldn't.\r\n", c.FullName())
			t.Fail()
		}
		if len(lines) > 1 {
			if len(strings.TrimSpace(lines[1])) > 0 {
				log.Logf("The second line of Command %s's Description should be blank.\r\n", c.FullName())
				t.Fail()
			}
		}

	})
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/pinzolo/csvutil"
)

var cmdAppend = &Command{
	Run:       runAppend,
	UsageLine: "append [OPTIONS...] [FILE]",
	Short:     "Append empty values to CSV each line.",
	Long: `DESCRIPTION
        Append empty values to CSV each line.

ARGUMENTS
        FILE
            Source CSV file.
            Without FILE argument, read from STDIN.

OPTIONS
        -w, --overwrite
            Overwrite source file by replaced CSV.
            This option does not work when file is not given.

        -H, --no-header
            Tel given CSV does not have header line.

        -b, --backup
            Create backup file before replace.
            This option should be used with --overwrite option.

        -e, --encoding
            Encoding of source file.
            This option accepts 'sjis' or 'eucjp'.
            Without this option, csvutil treats CSV file is encoded by UTF-8.

        -h, --header
            Appending header text.
            To target multi headers, use semicolon separated value like foo:bar.
            If this option is not given, new header texts are set with column1, column2...

        -s, --size
            Appending column size.
            If size is less than header length, use header size.
            If size is greater than header length, use this.
	`,
}

type cmdAppendOption struct {
	csvutil.AppendOption
	// Overwrite to source. (default false)
	Overwrite bool
	// Backup source file. (default false)
	Backup bool
	// Header symbols.
	Header string
}

var appendOpt = cmdAppendOption{}

func init() {
	cmdAppend.Flag.BoolVar(&appendOpt.Overwrite, "overwrite", false, "Overwrite to source.")
	cmdAppend.Flag.BoolVar(&appendOpt.Overwrite, "w", false, "Overwrite to source.")
	cmdAppend.Flag.BoolVar(&appendOpt.NoHeader, "no-header", false, "Source file does not have header line.")
	cmdAppend.Flag.BoolVar(&appendOpt.NoHeader, "H", false, "Source file does not have header line.")
	cmdAppend.Flag.BoolVar(&appendOpt.Backup, "backup", false, "Backup source file.")
	cmdAppend.Flag.BoolVar(&appendOpt.Backup, "b", false, "Backup source file.")
	cmdAppend.Flag.StringVar(&appendOpt.Encoding, "encoding", "utf8", "Encoding of source file")
	cmdAppend.Flag.StringVar(&appendOpt.Encoding, "e", "utf8", "Encoding of source file")
	cmdAppend.Flag.StringVar(&appendOpt.Header, "header", "", "Appending header(s)")
	cmdAppend.Flag.StringVar(&appendOpt.Header, "h", "", "Appending header(s)")
	cmdAppend.Flag.IntVar(&appendOpt.Size, "size", 0, "Appending column size")
	cmdAppend.Flag.IntVar(&appendOpt.Size, "s", 0, "Appending column size")
}

// runAppend executes append command and return exit code.
func runAppend(args []string) int {
	success := false
	path, err := path(args)
	if err != nil {
		return handleError(err)
	}

	w, wf, err := writer(path, appendOpt.Overwrite)
	if err != nil {
		return handleError(err)
	}
	if wf != nil {
		defer wf(&success, appendOpt.Backup)
	}

	r, rf, err := reader(path)
	if err != nil {
		return handleError(err)
	}
	if rf != nil {
		defer rf()
	}

	opt := appendOpt.AppendOption
	opt.Headers = split(appendOpt.Header)
	err = csvutil.Append(r, w, opt)
	if err != nil {
		return handleError(err)
	}

	success = true
	return 0
}
<commit_msg>Update `append` command.<commit_after>package main

import (
	"github.com/pinzolo/csvutil"
)

var cmdAppend = &Command{
	Run:       runAppend,
	UsageLine: "append [OPTIONS...] [FILE]",
	Short:     "Append empty values to CSV each line.",
	Long: `DESCRIPTION
        Append empty values to CSV each line.

ARGUMENTS
        FILE
            Source CSV file.
            Without FILE argument, read from STDIN.

OPTIONS
        -w, --overwrite
            Overwrite source file by replaced CSV.
            This option does not work when file is not given.

        -H, --no-header
            Tel given CSV does not have header line.

        -b, --backup
            Create backup file before replace.
            This option should be used with --overwrite option.

        -e, --encoding
            Encoding of source file.
            This option accepts 'sjis' or 'eucjp'.
            Without this option, csvutil treats CSV file is encoded by UTF-8.

        -h, --header
            Appending header text.
            To target multi headers, use semicolon separated value like foo:bar.
            If this option is not given, new header texts are set with column1, column2...

        -s, --size
            Appending column size. Default is 1
            If size is less than header length, ignore unused header(s).
            If size is greater than header length, append default header(s).
	`,
}

type cmdAppendOption struct {
	csvutil.AppendOption
	// Overwrite to source. (default false)
	Overwrite bool
	// Backup source file. (default false)
	Backup bool
	// Header symbols.
	Header string
}

var appendOpt = cmdAppendOption{}

func init() {
	cmdAppend.Flag.BoolVar(&appendOpt.Overwrite, "overwrite", false, "Overwrite to source.")
	cmdAppend.Flag.BoolVar(&appendOpt.Overwrite, "w", false, "Overwrite to source.")
	cmdAppend.Flag.BoolVar(&appendOpt.NoHeader, "no-header", false, "Source file does not have header line.")
	cmdAppend.Flag.BoolVar(&appendOpt.NoHeader, "H", false, "Source file does not have header line.")
	cmdAppend.Flag.BoolVar(&appendOpt.Backup, "backup", false, "Backup source file.")
	cmdAppend.Flag.BoolVar(&appendOpt.Backup, "b", false, "Backup source file.")
	cmdAppend.Flag.StringVar(&appendOpt.Encoding, "encoding", "utf8", "Encoding of source file")
	cmdAppend.Flag.StringVar(&appendOpt.Encoding, "e", "utf8", "Encoding of source file")
	cmdAppend.Flag.StringVar(&appendOpt.Header, "header", "", "Appending header(s)")
	cmdAppend.Flag.StringVar(&appendOpt.Header, "h", "", "Appending header(s)")
	cmdAppend.Flag.IntVar(&appendOpt.Size, "size", 1, "Appending column size")
	cmdAppend.Flag.IntVar(&appendOpt.Size, "s", 1, "Appending column size")
}

// runAppend executes append command and return exit code.
func runAppend(args []string) int {
	success := false
	path, err := path(args)
	if err != nil {
		return handleError(err)
	}

	w, wf, err := writer(path, appendOpt.Overwrite)
	if err != nil {
		return handleError(err)
	}
	if wf != nil {
		defer wf(&success, appendOpt.Backup)
	}

	r, rf, err := reader(path)
	if err != nil {
		return handleError(err)
	}
	if rf != nil {
		defer rf()
	}

	opt := appendOpt.AppendOption
	opt.Headers = split(appendOpt.Header)
	err = csvutil.Append(r, w, opt)
	if err != nil {
		return handleError(err)
	}

	success = true
	return 0
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"os/signal"
	"time"

	"bitbucket.org/anacrolix/go.torrent/dht"
	"bitbucket.org/anacrolix/go.torrent/util"
	_ "bitbucket.org/anacrolix/go.torrent/util/profile"
)

type pingResponse struct {
	addr string
	krpc dht.Msg
}

var (
	tableFileName = flag.String("tableFile", "", "name of file for storing node info")
	serveAddr     = flag.String("serveAddr", ":0", "local UDP address")
	infoHash      = flag.String("infoHash", "", "torrent infohash")

	s *dht.Server
)

func loadTable() error {
	if *tableFileName == "" {
		return nil
	}
	f, err := os.Open(*tableFileName)
	if os.IsNotExist(err) {
		return nil
	}
	if err != nil {
		return fmt.Errorf("error opening table file: %s", err)
	}
	defer f.Close()
	added := 0
	for {
		b := make([]byte, dht.CompactNodeInfoLen)
		_, err := io.ReadFull(f, b)
		if err == io.EOF {
			break
		}
		if err != nil {
			return fmt.Errorf("error reading table file: %s", err)
		}
		var ni dht.NodeInfo
		err = ni.UnmarshalCompact(b)
		if err != nil {
			return fmt.Errorf("error unmarshaling compact node info: %s", err)
		}
		s.AddNode(ni)
		added++
	}
	log.Printf("loaded %d nodes from table file", added)
	return nil
}

func init() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	flag.Parse()
	switch len(*infoHash) {
	case 20:
	case 40:
		_, err := fmt.Sscanf(*infoHash, "%x", infoHash)
		if err != nil {
			log.Fatal(err)
		}
	default:
		log.Fatal("require 20 byte infohash")
	}
	var err error
	s, err = dht.NewServer(&dht.ServerConfig{
		Addr: *serveAddr,
	})
	if err != nil {
		log.Fatal(err)
	}
	err = loadTable()
	if err != nil {
		log.Fatalf("error loading table: %s", err)
	}
	log.Printf("dht server on %s, ID is %q", s.LocalAddr(), s.IDString())
	setupSignals()
}

func saveTable() error {
	goodNodes := s.Nodes()
	if *tableFileName == "" {
		if len(goodNodes) != 0 {
			log.Printf("discarding %d good nodes!", len(goodNodes))
		}
		return nil
	}
	f, err := os.OpenFile(*tableFileName, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
	if err != nil {
		return fmt.Errorf("error opening table file: %s", err)
	}
	defer f.Close()
	for _, nodeInfo := range goodNodes {
		var b [dht.CompactNodeInfoLen]byte
		err := nodeInfo.PutCompact(b[:])
		if err != nil {
			return fmt.Errorf("error compacting node info: %s", err)
		}
		_, err = f.Write(b[:])
		if err != nil {
			return fmt.Errorf("error writing compact node info: %s", err)
		}
	}
	log.Printf("saved %d nodes to table file", len(goodNodes))
	return nil
}

func setupSignals() {
	ch := make(chan os.Signal)
	signal.Notify(ch, os.Interrupt)
	go func() {
		<-ch
		s.Close()
	}()
}

func main() {
	seen := make(map[util.CompactPeer]struct{})
	for {
		ps, err := s.GetPeers(*infoHash)
		if err != nil {
			log.Fatal(err)
		}
		go func() {
			for sl := range ps.Values {
				for _, p := range sl {
					if _, ok := seen[p]; ok {
						continue
					}
					seen[p] = struct{}{}
					fmt.Println((&net.UDPAddr{
						IP:   p.IP[:],
						Port: int(p.Port),
					}).String())
				}
			}
		}()
		time.Sleep(15 * time.Second)
	}
	if err := saveTable(); err != nil {
		log.Printf("error saving node table: %s", err)
	}
}
<commit_msg>cmd/dht-get-peers: Fix lockup on SIGINT<commit_after>package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"os/signal"
	"time"

	"bitbucket.org/anacrolix/go.torrent/dht"
	"bitbucket.org/anacrolix/go.torrent/util"
	_ "bitbucket.org/anacrolix/go.torrent/util/profile"
)

type pingResponse struct {
	addr string
	krpc dht.Msg
}

var (
	tableFileName = flag.String("tableFile", "", "name of file for storing node info")
	serveAddr     = flag.String("serveAddr", ":0", "local UDP address")
	infoHash      = flag.String("infoHash", "", "torrent infohash")

	s        *dht.Server
	quitting = make(chan struct{})
)

func loadTable() error {
	if *tableFileName == "" {
		return nil
	}
	f, err := os.Open(*tableFileName)
	if os.IsNotExist(err) {
		return nil
	}
	if err != nil {
		return fmt.Errorf("error opening table file: %s", err)
	}
	defer f.Close()
	added := 0
	for {
		b := make([]byte, dht.CompactNodeInfoLen)
		_, err := io.ReadFull(f, b)
		if err == io.EOF {
			break
		}
		if err != nil {
			return fmt.Errorf("error reading table file: %s", err)
		}
		var ni dht.NodeInfo
		err = ni.UnmarshalCompact(b)
		if err != nil {
			return fmt.Errorf("error unmarshaling compact node info: %s", err)
		}
		s.AddNode(ni)
		added++
	}
	log.Printf("loaded %d nodes from table file", added)
	return nil
}

func init() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	flag.Parse()
	switch len(*infoHash) {
	case 20:
	case 40:
		_, err := fmt.Sscanf(*infoHash, "%x", infoHash)
		if err != nil {
			log.Fatal(err)
		}
	default:
		log.Fatal("require 20 byte infohash")
	}
	var err error
	s, err = dht.NewServer(&dht.ServerConfig{
		Addr: *serveAddr,
	})
	if err != nil {
		log.Fatal(err)
	}
	err = loadTable()
	if err != nil {
		log.Fatalf("error loading table: %s", err)
	}
	log.Printf("dht server on %s, ID is %q", s.LocalAddr(), s.IDString())
	setupSignals()
}

func saveTable() error {
	goodNodes := s.Nodes()
	if *tableFileName == "" {
		if len(goodNodes) != 0 {
			log.Print("good nodes were discarded because you didn't specify a table file")
		}
		return nil
	}
	f, err := os.OpenFile(*tableFileName, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
	if err != nil {
		return fmt.Errorf("error opening table file: %s", err)
	}
	defer f.Close()
	for _, nodeInfo := range goodNodes {
		var b [dht.CompactNodeInfoLen]byte
		err := nodeInfo.PutCompact(b[:])
		if err != nil {
			return fmt.Errorf("error compacting node info: %s", err)
		}
		_, err = f.Write(b[:])
		if err != nil {
			return fmt.Errorf("error writing compact node info: %s", err)
		}
	}
	log.Printf("saved %d nodes to table file", len(goodNodes))
	return nil
}

func setupSignals() {
	ch := make(chan os.Signal)
	signal.Notify(ch, os.Interrupt)
	go func() {
		<-ch
		close(quitting)
		s.Close()
	}()
}

func main() {
	seen := make(map[util.CompactPeer]struct{})
getPeers:
	for {
		ps, err := s.GetPeers(*infoHash)
		if err != nil {
			log.Fatal(err)
		}
		go func() {
			for sl := range ps.Values {
				for _, p := range sl {
					if _, ok := seen[p]; ok {
						continue
					}
					seen[p] = struct{}{}
					fmt.Println((&net.UDPAddr{
						IP:   p.IP[:],
						Port: int(p.Port),
					}).String())
				}
			}
		}()
		select {
		case <-time.After(15 * time.Second):
		case <-quitting:
			break getPeers
		}
	}
	if err := saveTable(); err != nil {
		log.Printf("error saving node table: %s", err)
	}
}
<|endoftext|>