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

package store

import (
	"fmt"
	"path"
	"path/filepath"
	"strings"

	goldmarktoc "github.com/abhinav/goldmark-toc"
	"github.com/pkg/errors"
	"gopkg.in/ini.v1"

	"github.com/asoul-sig/asouldocs/internal/osutil"
)

// TOC represents documentation hierarchy for a specific language.
type TOC struct {
	Language string  // The language of the documentation
	Nodes    []*Node // Directories of the documentation
	Pages    []*Node // Individuals pages of the documentation

	nodes map[string]*Node // Key is the Node.Path
}

// Node is a node in the documentation hierarchy.
type Node struct {
	Category  string // The category (name) of the node, for directories and single pages, categories are empty
	Path      string // The URL path
	LocalPath string // Full path with .md extension

	Content  []byte            // The content of the node
	Title    string            // The title of the document in the given language
	Headings goldmarktoc.Items // Headings in the node

	Nodes    []*Node   // The list of sub-nodes
	Previous *PageLink // The previous page
	Next     *PageLink // The next page
}

// PageLink is a link to another page.
type PageLink struct {
	Title string // The title of the page
	Path  string // the path to the page
}

// Reload reloads and converts the content from local disk.
func (n *Node) Reload(baseURLPath string) error {
	pathPrefix := path.Join(baseURLPath, strings.SplitN(n.Path, "/", 2)[0])
	content, meta, headings, err := convertFile(pathPrefix, n.LocalPath)
	if err != nil {
		return err
	}
	n.Content = content
	n.Title = fmt.Sprintf("%v", meta["title"])
	n.Headings = headings

	previous, ok := meta["previous"].(map[interface{}]interface{})
	if ok {
		n.Previous = &PageLink{
			Title: fmt.Sprintf("%v", previous["title"]),
			Path:  string(convertRelativeLink(pathPrefix, []byte(fmt.Sprintf("%v", previous["path"])))),
		}
	}
	next, ok := meta["next"].(map[interface{}]interface{})
	if ok {
		n.Next = &PageLink{
			Title: fmt.Sprintf("%v", next["title"]),
			Path:  string(convertRelativeLink(pathPrefix, []byte(fmt.Sprintf("%v", next["path"])))),
		}
	}
	return nil
}

const readme = "README"

// initTocs initializes documentation hierarchy for given languages in the given
// root directory.
func initTocs(root string, languages []string, baseURLPath string) (map[string]*TOC, error) {
	tocPath := filepath.Join(root, "toc.ini")
	tocCfg, err := ini.Load(tocPath)
	if err != nil {
		return nil, errors.Wrapf(err, "load %q", tocPath)
	}

	tocs := make(map[string]*TOC)
	for i, lang := range languages {
		fmt.Println("***", lang, "***")

		toc := &TOC{
			Language: lang,
			nodes:    make(map[string]*Node),
		}

		var previous *Node
		setPrevious := func(n *Node) {
			defer func() {
				previous = n
			}()
			if previous == nil {
				return
			}

			if n.Previous == nil {
				n.Previous = &PageLink{
					Title: previous.Title,
					Path:  previous.Path,
				}
			}
			if previous.Next == nil {
				previous.Next = &PageLink{
					Title: n.Title,
					Path:  n.Path,
				}
			}
		}

		dirs := tocCfg.Section("").KeyStrings()
		toc.Nodes = make([]*Node, 0, len(dirs))
		for _, dir := range dirs {
			dirname := tocCfg.Section("").Key(dir).String()
			files := tocCfg.Section(dirname).KeyStrings()
			// Skip empty directory
			if len(files) == 0 {
				continue
			}
			fmt.Println(dirname + "/")

			dirNode := &Node{
				Path:  dirname,
				Nodes: make([]*Node, 0, len(files)-1),
			}
			toc.Nodes = append(toc.Nodes, dirNode)
			toc.nodes[dirNode.Path] = dirNode

			if tocCfg.Section(dirname).HasValue(readme) {
				localpath := filepath.Join(root, lang, dirNode.Path, readme+".md")
				if i > 0 && !osutil.IsFile(localpath) {
					continue // It is OK to have missing file for non-default language
				}

				dirNode.LocalPath = localpath
				err = dirNode.Reload(baseURLPath)
				if err != nil {
					return nil, errors.Wrapf(err, "reload node from %q", dirNode.LocalPath)
				}

				if len(dirNode.Content) > 0 {
					setPrevious(dirNode)
				}
			}

			for _, file := range files {
				filename := tocCfg.Section(dirname).Key(file).String()
				if filename == readme {
					continue
				}

				localpath := filepath.Join(root, lang, dirname, filename) + ".md"
				if i > 0 && !osutil.IsFile(localpath) {
					continue // It is OK to have missing file for non-default language
				}

				node := &Node{
					Category:  dirNode.Title,
					Path:      path.Join(dirname, filename),
					LocalPath: localpath,
				}
				dirNode.Nodes = append(dirNode.Nodes, node)
				toc.nodes[node.Path] = node

				err = node.Reload(baseURLPath)
				if err != nil {
					return nil, errors.Wrapf(err, "reload node from %q", node.LocalPath)
				}

				setPrevious(node)
				fmt.Println(strings.Repeat(" ", len(dirname))+"|__", filename)
			}
		}

		// Single pages
		pages := tocCfg.Section("pages").KeysHash()
		toc.Pages = make([]*Node, 0, len(pages))
		for _, page := range pages {
			fmt.Println(page)

			node := &Node{
				Path:      page,
				LocalPath: filepath.Join(root, lang, page) + ".md",
			}
			toc.Pages = append(toc.Pages, node)
			toc.nodes[node.Path] = node

			err = node.Reload("")
			if err != nil {
				return nil, errors.Wrapf(err, "reload node from %q", node.LocalPath)
			}
		}

		tocs[lang] = toc
	}
	return tocs, nil
}
<commit_msg>store: improve toc print to not break by logs<commit_after>// Copyright 2022 ASoulDocs. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package store

import (
	"bytes"
	"fmt"
	"path"
	"path/filepath"
	"strings"

	goldmarktoc "github.com/abhinav/goldmark-toc"
	"github.com/pkg/errors"
	"gopkg.in/ini.v1"

	"github.com/asoul-sig/asouldocs/internal/osutil"
)

// TOC represents documentation hierarchy for a specific language.
type TOC struct {
	Language string  // The language of the documentation
	Nodes    []*Node // Directories of the documentation
	Pages    []*Node // Individuals pages of the documentation

	nodes map[string]*Node // Key is the Node.Path
}

// Node is a node in the documentation hierarchy.
type Node struct {
	Category  string // The category (name) of the node, for directories and single pages, categories are empty
	Path      string // The URL path
	LocalPath string // Full path with .md extension

	Content  []byte            // The content of the node
	Title    string            // The title of the document in the given language
	Headings goldmarktoc.Items // Headings in the node

	Nodes    []*Node   // The list of sub-nodes
	Previous *PageLink // The previous page
	Next     *PageLink // The next page
}

// PageLink is a link to another page.
type PageLink struct {
	Title string // The title of the page
	Path  string // the path to the page
}

// Reload reloads and converts the content from local disk.
func (n *Node) Reload(baseURLPath string) error {
	pathPrefix := path.Join(baseURLPath, strings.SplitN(n.Path, "/", 2)[0])
	content, meta, headings, err := convertFile(pathPrefix, n.LocalPath)
	if err != nil {
		return err
	}
	n.Content = content
	n.Title = fmt.Sprintf("%v", meta["title"])
	n.Headings = headings

	previous, ok := meta["previous"].(map[interface{}]interface{})
	if ok {
		n.Previous = &PageLink{
			Title: fmt.Sprintf("%v", previous["title"]),
			Path:  string(convertRelativeLink(pathPrefix, []byte(fmt.Sprintf("%v", previous["path"])))),
		}
	}
	next, ok := meta["next"].(map[interface{}]interface{})
	if ok {
		n.Next = &PageLink{
			Title: fmt.Sprintf("%v", next["title"]),
			Path:  string(convertRelativeLink(pathPrefix, []byte(fmt.Sprintf("%v", next["path"])))),
		}
	}
	return nil
}

const readme = "README"

// initTocs initializes documentation hierarchy for given languages in the given
// root directory.
func initTocs(root string, languages []string, baseURLPath string) (map[string]*TOC, error) {
	tocPath := filepath.Join(root, "toc.ini")
	tocCfg, err := ini.Load(tocPath)
	if err != nil {
		return nil, errors.Wrapf(err, "load %q", tocPath)
	}

	var tocprint bytes.Buffer
	tocs := make(map[string]*TOC)
	for i, lang := range languages {
		tocprint.WriteString(lang)
		tocprint.WriteString(":\n")

		toc := &TOC{
			Language: lang,
			nodes:    make(map[string]*Node),
		}

		var previous *Node
		setPrevious := func(n *Node) {
			defer func() {
				previous = n
			}()
			if previous == nil {
				return
			}

			if n.Previous == nil {
				n.Previous = &PageLink{
					Title: previous.Title,
					Path:  previous.Path,
				}
			}
			if previous.Next == nil {
				previous.Next = &PageLink{
					Title: n.Title,
					Path:  n.Path,
				}
			}
		}

		dirs := tocCfg.Section("").KeyStrings()
		toc.Nodes = make([]*Node, 0, len(dirs))
		for _, dir := range dirs {
			dirname := tocCfg.Section("").Key(dir).String()
			files := tocCfg.Section(dirname).KeyStrings()
			// Skip empty directory
			if len(files) == 0 {
				continue
			}
			tocprint.WriteString(dirname)
			tocprint.WriteString("/\n")

			dirNode := &Node{
				Path:  dirname,
				Nodes: make([]*Node, 0, len(files)-1),
			}
			toc.Nodes = append(toc.Nodes, dirNode)
			toc.nodes[dirNode.Path] = dirNode

			if tocCfg.Section(dirname).HasValue(readme) {
				localpath := filepath.Join(root, lang, dirNode.Path, readme+".md")
				if i > 0 && !osutil.IsFile(localpath) {
					continue // It is OK to have missing file for non-default language
				}

				dirNode.LocalPath = localpath
				err = dirNode.Reload(baseURLPath)
				if err != nil {
					return nil, errors.Wrapf(err, "reload node from %q", dirNode.LocalPath)
				}

				if len(dirNode.Content) > 0 {
					setPrevious(dirNode)
				}
			}

			for _, file := range files {
				filename := tocCfg.Section(dirname).Key(file).String()
				if filename == readme {
					continue
				}

				localpath := filepath.Join(root, lang, dirname, filename) + ".md"
				if i > 0 && !osutil.IsFile(localpath) {
					continue // It is OK to have missing file for non-default language
				}

				node := &Node{
					Category:  dirNode.Title,
					Path:      path.Join(dirname, filename),
					LocalPath: localpath,
				}
				dirNode.Nodes = append(dirNode.Nodes, node)
				toc.nodes[node.Path] = node

				err = node.Reload(baseURLPath)
				if err != nil {
					return nil, errors.Wrapf(err, "reload node from %q", node.LocalPath)
				}

				setPrevious(node)
				tocprint.WriteString(strings.Repeat(" ", len(dirname)))
				tocprint.WriteString("|__")
				tocprint.WriteString(filename)
				tocprint.WriteString("\n")
			}
		}

		// Single pages
		pages := tocCfg.Section("pages").KeysHash()
		toc.Pages = make([]*Node, 0, len(pages))
		for _, page := range pages {
			tocprint.WriteString(page)
			tocprint.WriteString("\n")

			node := &Node{
				Path:      page,
				LocalPath: filepath.Join(root, lang, page) + ".md",
			}
			toc.Pages = append(toc.Pages, node)
			toc.nodes[node.Path] = node

			err = node.Reload("")
			if err != nil {
				return nil, errors.Wrapf(err, "reload node from %q", node.LocalPath)
			}
		}

		tocs[lang] = toc
	}

	fmt.Print(tocprint.String())
	return tocs, nil
}
<|endoftext|>


<commit_before>package samtun

import (
  "bitbucket.org/majestrate/sam3"
  "fmt"
  "io/ioutil"
  "log"
  "os"
  "strconv"
  "strings"
  "time"
)

// determine if this file exists and we can write to it
func checkfile(fname string) bool {
  _, err := os.Stat(fname)
  return err == nil
}

func Run() {
  // defaults
  fname := "samtun.ini"

  if len(os.Args) > 1 {
    if os.Args[1] == "-h" || os.Args[1] == "--help" {
      // print usage and return
      fmt.Fprintf(os.Stdout, "usage: %s [config.ini]\n")
      return
    } else {
      fname = os.Args[1]
    }
  }
  if ! checkfile(fname) {
    // generate a config we don't have it there
    genConfig(fname)
  }
  conf, err := loadConfig(fname)
  if err == nil {
    // we loaded the config right
    err = conf.validate()
    if err == nil {
      // we are configured correctly
      samaddr := conf["sam"]
      ifname := conf["ifname"]
      keyfile := conf["keyfile"]
      remote := conf["remote"]
      mtu_str := conf["mtu"]
      session := conf["session"]
      localaddr := conf["srcaddr"]
      remoteaddr := conf["dstaddr"]
      mtu, err := strconv.ParseInt(mtu_str, 10, 32)
      iface, err := newTun(ifname, localaddr, remoteaddr, int(mtu))
      defer iface.Close()
      if err == nil {
        log.Println("connecting to", samaddr)
        sam, err := sam3.NewSAM(samaddr)
        if err == nil {
          // we have a sam connection
          var keys sam3.I2PKeys
          if ! checkfile(keyfile) {
            // we have not generated private keys yet
            log.Println("generating keys")
            keys, err = sam.NewKeys()
            log.Println("we are", keys.Addr().Base32())
            ioutil.WriteFile(keyfile, []byte(keys.String()+" "+keys.Addr().String()), 0600)
          }
          key_bytes, err := ioutil.ReadFile(keyfile)
          if err == nil {
            // read the keys
            key_str := string(key_bytes)
            privkey := strings.Split(key_str, " ")[0]
            pubkey := strings.Split(key_str, " ")[1]
            keys := sam3.NewKeys(sam3.I2PAddr(pubkey), privkey)
            // create our datagram session
            log.Println("creating session")
            dg, err := sam.NewDatagramSession(session, keys, sam3.Options_Fat, 0)
            if err == nil {
              our_addr := sam3.I2PAddr(pubkey)
              log.Println("we are", our_addr.Base32())
              // look up remote destination
              for {
                if len(remote) == 0 {
                  // not set
                  log.Println("please set the remote destination in the config")
                  return
                }
                log.Println("looking up", remote)
                remote_addr, err := sam.Lookup(remote)
                if err == nil {
                  log.Println(remote, "resolved to", remote_addr.Base32())
                  break
                } else {
                  log.Println("failed", err)
                }
                time.Sleep(time.Second)
              }
              for {
                select {
                }
              }
              log.Println("closing")
              dg.Close()
            } else {
              log.Fatal(err)
            }
          } else {
            log.Fatal(err)
          }

        } else {
          log.Fatal(err)
        }
      } else {
        log.Fatal(err)
      }
    } else {
      log.Fatal(err)
    }
  }
}
<commit_msg>try fixing samtun<commit_after>package samtun

import (
  "bitbucket.org/majestrate/sam3"
  "fmt"
  "io/ioutil"
  "log"
  "os"
  "strconv"
  "strings"
  "time"
)

// determine if this file exists and we can write to it
func checkfile(fname string) bool {
  _, err := os.Stat(fname)
  return err == nil
}

func Run() {
  // defaults
  fname := "samtun.ini"

  if len(os.Args) > 1 {
    if os.Args[1] == "-h" || os.Args[1] == "--help" {
      // print usage and return
      fmt.Fprintf(os.Stdout, "usage: %s [config.ini]\n")
      return
    } else {
      fname = os.Args[1]
    }
  }
  if ! checkfile(fname) {
    // generate a config we don't have it there
    genConfig(fname)
  }
  conf, err := loadConfig(fname)
  if err == nil {
    // we loaded the config right
    err = conf.validate()
    if err == nil {
      // we are configured correctly
      samaddr := conf["sam"]
      ifname := conf["ifname"]
      keyfile := conf["keyfile"]
      remote := conf["remote"]
      mtu_str := conf["mtu"]
      session := conf["session"]
      localaddr := conf["srcaddr"]
      remoteaddr := conf["dstaddr"]
      mtu, err := strconv.ParseInt(mtu_str, 10, 32)
      if err == nil {
        log.Println("connecting to", samaddr)
        sam, err := sam3.NewSAM(samaddr)
        if err == nil {
          // we have a sam connection
          var keys sam3.I2PKeys
          if ! checkfile(keyfile) {
            // we have not generated private keys yet
            log.Println("generating keys")
            keys, err = sam.NewKeys()
            log.Println("we are", keys.Addr().Base32())
            ioutil.WriteFile(keyfile, []byte(keys.String()+" "+keys.Addr().String()), 0600)
          }
          key_bytes, err := ioutil.ReadFile(keyfile)
          if err == nil {
            // read the keys
            key_str := string(key_bytes)
            privkey := strings.Split(key_str, " ")[0]
            pubkey := strings.Split(key_str, " ")[1]
            keys := sam3.NewKeys(sam3.I2PAddr(pubkey), privkey)
            // create our datagram session
            log.Println("creating session")
            dg, err := sam.NewDatagramSession(session, keys, sam3.Options_Fat, 0)
            if err == nil {
              addr, err := sam.Lookup("ME")
              if err != nil {
                log.Println("failed to lookup our own destination")
                return
              }
              log.Println("we are", addr.Base32())
              // look up remote destination
              for {
                if len(remote) == 0 {
                  // not set
                  log.Println("please set the remote destination in the config")
                  return
                }
                log.Println("looking up", remote)
                remote_addr, err := sam.Lookup(remote)
                if err == nil {
                  log.Println(remote, "resolved to", remote_addr.Base32())
                  break
                } else {
                  log.Println("failed", err)
                }
                time.Sleep(time.Second)
              }
              log.Println("open", ifname)
              iface, err := newTun(ifname, localaddr, remoteaddr, int(mtu))
              defer iface.Close()
              for {
                select {
                }
              }
              log.Println("closing")
              dg.Close()
            } else {
              log.Fatal(err)
            }
          } else {
            log.Fatal(err)
          }

        } else {
          log.Fatal(err)
        }
      } else {
        log.Fatal(err)
      }
    } else {
      log.Fatal(err)
    }
  }
}
<|endoftext|>


<commit_before>// Tests for GoAWK interpreter.
package interp_test

import (
	"bytes"
	"flag"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"testing"

	"github.com/benhoyt/goawk/interp"
	"github.com/benhoyt/goawk/parser"
)

var (
	awkExe string
)

func TestMain(m *testing.M) {
	flag.StringVar(&awkExe, "awk", "awk", "awk executable name")
	flag.Parse()
	os.Exit(m.Run())
}

// Note: a lot of these are really parser tests too.
func TestInterp(t *testing.T) {
	tests := []struct {
		src    string
		in     string
		out    string
		err    string
		awkErr string
	}{
		{`{ print toupper($1 $2) }`, "Fo o\nB aR", "FOO\nBAR\n", "", ""},

		// Greater than operator requires parentheses in print statement,
		// otherwise it's a redirection directive
		{`BEGIN { print "x" > "out" }`, "", "", "", ""},
		{`BEGIN { printf "x" > "out" }`, "", "", "", ""},
		{`BEGIN { print("x" > "out") }`, "", "1\n", "", ""},
		{`BEGIN { printf("x" > "out") }`, "", "1", "", ""},

		// Grammar should allow blocks wherever statements are allowed
		{`BEGIN { if (1) printf "x"; else printf "y" }`, "", "x", "", ""},
		{`BEGIN { printf "x"; { printf "y"; printf "z" } }`, "", "xyz", "", ""},

		// Conditional expressions parse and work correctly
		{`BEGIN { print 0?"t":"f" }`, "", "f\n", "", ""},
		{`BEGIN { print 1?"t":"f" }`, "", "t\n", "", ""},
		{`BEGIN { print (1+2)?"t":"f" }`, "", "t\n", "", ""},
		{`BEGIN { print (1+2?"t":"f") }`, "", "t\n", "", ""},
		{`BEGIN { print(1 ? x="t" : "f"); print x; }`, "", "t\nt\n", "", ""},

		// Ensure certain odd syntax matches awk behaviour
		{`BEGIN { printf "x" }; BEGIN { printf "y" }`, "", "xy", "", ""},
		{`BEGIN { printf "x" };; BEGIN { printf "y" }`, "", "xy", "", ""},

		// Ensure syntax errors result in errors
		{`BEGIN { }'`, "", "", `parse error at 1:10: unexpected '\''`, "syntax error"},
		{`{ $1 = substr($1, 1, 3) print $1 }`, "", "", "ERROR", "syntax error"},
	}
	for _, test := range tests {
		testName := test.src
		if len(testName) > 70 {
			testName = testName[:70]
		}
		// Run it through external awk program first
		t.Run("awk_"+testName, func(t *testing.T) {
			srcFile, err := ioutil.TempFile("", "goawktest_")
			if err != nil {
				t.Fatalf("error creating temp file: %v", err)
			}
			defer os.Remove(srcFile.Name())
			_, err = srcFile.Write([]byte(test.src))
			if err != nil {
				t.Fatalf("error writing temp file: %v", err)
			}
			cmd := exec.Command(awkExe, "-f", srcFile.Name(), "-")
			if test.in != "" {
				stdin, err := cmd.StdinPipe()
				if err != nil {
					t.Fatalf("error fetching stdin pipe: %v", err)
				}
				go func() {
					defer stdin.Close()
					stdin.Write([]byte(test.in))
				}()
			}
			out, err := cmd.CombinedOutput()
			if err != nil {
				if test.awkErr != "" {
					if strings.Contains(string(out), test.awkErr) {
						return
					}
					t.Fatalf("expected error %q, got:\n%s", test.awkErr, out)
				} else {
					t.Fatalf("error running %s: %v:\n%s", awkExe, err, out)
				}
			}
			if test.awkErr != "" {
				t.Fatalf(`expected error %q, got ""`, test.awkErr)
			}
			if string(out) != test.out {
				t.Fatalf("expected %q, got %q", test.out, out)
			}
		})

		// Then test it in GoAWK
		t.Run(testName, func(t *testing.T) {
			prog, err := parser.ParseProgram([]byte(test.src))
			if err != nil {
				if test.err != "" {
					if err.Error() == test.err {
						return
					}
					t.Fatalf("expected error %q, got %q", test.err, err.Error())
				}
				t.Fatal(err)
			}
			outBuf := &bytes.Buffer{}
			errBuf := &bytes.Buffer{}
			p := interp.New(prog, outBuf, errBuf)
			err = p.Exec(strings.NewReader(test.in), nil)
			if err != nil {
				if test.err != "" {
					if err.Error() == test.err {
						return
					}
					t.Fatalf("expected error %q, got %q", test.err, err.Error())
				}
				t.Fatal(err)
			}
			if test.err != "" {
				t.Fatalf(`expected error %q, got ""`, test.err)
			}
			out := outBuf.String() + errBuf.String()
			if out != test.out {
				t.Fatalf("expected %q, got %q", test.out, out)
			}
		})
	}
	_ = os.Remove("out")
}
<commit_msg>Add some interpreter benchmarks<commit_after>// Tests for GoAWK interpreter.
package interp_test

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"testing"

	"github.com/benhoyt/goawk/interp"
	"github.com/benhoyt/goawk/parser"
)

var (
	awkExe string
)

func TestMain(m *testing.M) {
	flag.StringVar(&awkExe, "awk", "awk", "awk executable name")
	flag.Parse()
	os.Exit(m.Run())
}

// Note: a lot of these are really parser tests too.
func TestInterp(t *testing.T) {
	tests := []struct {
		src    string
		in     string
		out    string
		err    string
		awkErr string
	}{
		{`{ print toupper($1 $2) }`, "Fo o\nB aR", "FOO\nBAR\n", "", ""},

		// Greater than operator requires parentheses in print statement,
		// otherwise it's a redirection directive
		{`BEGIN { print "x" > "out" }`, "", "", "", ""},
		{`BEGIN { printf "x" > "out" }`, "", "", "", ""},
		{`BEGIN { print("x" > "out") }`, "", "1\n", "", ""},
		{`BEGIN { printf("x" > "out") }`, "", "1", "", ""},

		// Grammar should allow blocks wherever statements are allowed
		{`BEGIN { if (1) printf "x"; else printf "y" }`, "", "x", "", ""},
		// {`BEGIN { printf "x"; { printf "y"; printf "z" } }`, "", "xyz", "", ""},

		// Conditional expressions parse and work correctly
		{`BEGIN { print 0?"t":"f" }`, "", "f\n", "", ""},
		{`BEGIN { print 1?"t":"f" }`, "", "t\n", "", ""},
		{`BEGIN { print (1+2)?"t":"f" }`, "", "t\n", "", ""},
		{`BEGIN { print (1+2?"t":"f") }`, "", "t\n", "", ""},
		{`BEGIN { print(1 ? x="t" : "f"); print x; }`, "", "t\nt\n", "", ""},

		// Ensure certain odd syntax matches awk behaviour
		// {`BEGIN { printf "x" }; BEGIN { printf "y" }`, "", "xy", "", ""},
		// {`BEGIN { printf "x" };; BEGIN { printf "y" }`, "", "xy", "", ""},

		// Ensure syntax errors result in errors
		{`BEGIN { }'`, "", "", `parse error at 1:10: unexpected '\''`, "syntax error"},
		// {`{ $1 = substr($1, 1, 3) print $1 }`, "", "", "ERROR", "syntax error"},
	}
	for _, test := range tests {
		testName := test.src
		if len(testName) > 70 {
			testName = testName[:70]
		}
		// Run it through external awk program first
		t.Run("awk_"+testName, func(t *testing.T) {
			srcFile, err := ioutil.TempFile("", "goawktest_")
			if err != nil {
				t.Fatalf("error creating temp file: %v", err)
			}
			defer os.Remove(srcFile.Name())
			_, err = srcFile.Write([]byte(test.src))
			if err != nil {
				t.Fatalf("error writing temp file: %v", err)
			}
			cmd := exec.Command(awkExe, "-f", srcFile.Name(), "-")
			if test.in != "" {
				stdin, err := cmd.StdinPipe()
				if err != nil {
					t.Fatalf("error fetching stdin pipe: %v", err)
				}
				go func() {
					defer stdin.Close()
					stdin.Write([]byte(test.in))
				}()
			}
			out, err := cmd.CombinedOutput()
			if err != nil {
				if test.awkErr != "" {
					if strings.Contains(string(out), test.awkErr) {
						return
					}
					t.Fatalf("expected error %q, got:\n%s", test.awkErr, out)
				} else {
					t.Fatalf("error running %s: %v:\n%s", awkExe, err, out)
				}
			}
			if test.awkErr != "" {
				t.Fatalf(`expected error %q, got ""`, test.awkErr)
			}
			if string(out) != test.out {
				t.Fatalf("expected %q, got %q", test.out, out)
			}
		})

		// Then test it in GoAWK
		t.Run(testName, func(t *testing.T) {
			prog, err := parser.ParseProgram([]byte(test.src))
			if err != nil {
				if test.err != "" {
					if err.Error() == test.err {
						return
					}
					t.Fatalf("expected error %q, got %q", test.err, err.Error())
				}
				t.Fatal(err)
			}
			outBuf := &bytes.Buffer{}
			errBuf := &bytes.Buffer{}
			p := interp.New(prog, outBuf, errBuf)
			err = p.Exec(strings.NewReader(test.in), nil)
			if err != nil {
				if test.err != "" {
					if err.Error() == test.err {
						return
					}
					t.Fatalf("expected error %q, got %q", test.err, err.Error())
				}
				t.Fatal(err)
			}
			if test.err != "" {
				t.Fatalf(`expected error %q, got ""`, test.err)
			}
			out := outBuf.String() + errBuf.String()
			if out != test.out {
				t.Fatalf("expected %q, got %q", test.out, out)
			}
		})
	}
	_ = os.Remove("out")
}

func benchmarkProgram(b *testing.B, n int, input, expected, srcFormat string, args ...interface{}) {
	b.StopTimer()
	src := fmt.Sprintf(srcFormat, args...)
	prog, err := parser.ParseProgram([]byte(src))
	if err != nil {
		b.Fatalf("error parsing %s: %v", b.Name(), err)
	}
	outBuf := &bytes.Buffer{}
	p := interp.New(prog, outBuf, ioutil.Discard)
	if expected != "" {
		expected += "\n"
	}
	for i := 0; i < n; i++ {
		outBuf.Reset()
		b.StartTimer()
		err := p.Exec(strings.NewReader(input), nil)
		b.StopTimer()
		if err != nil {
			b.Fatalf("error interpreting %s: %v", b.Name(), err)
		}
		if outBuf.String() != expected {
			b.Fatalf("expected %q, got %q", expected, outBuf.String())
		}
	}
}

func BenchmarkRecursiveFunc(b *testing.B) {
	benchmarkProgram(b, b.N, "", "13", `
function fib(n) {
  if (n <= 2) {
    return 1
  }
  return fib(n-1) + fib(n-2)
}

BEGIN {
  print fib(7)
}
`)
}

func BenchmarkFuncCall(b *testing.B) {
	b.StopTimer()
	sum := 0
	for i := 0; i < b.N; i++ {
		sum += i
	}
	benchmarkProgram(b, 1, "", fmt.Sprintf("%d", sum), `
function add(a, b) {
  return a + b
}

BEGIN {
  for (i = 0; i < %d; i++) {
    sum = add(sum, i)
  }
  print sum
}
`, b.N)
}

func BenchmarkForLoop(b *testing.B) {
	b.StopTimer()
	sum := 0
	for i := 0; i < b.N; i++ {
		sum += i
	}
	benchmarkProgram(b, 1, "", fmt.Sprintf("%d", sum), `
BEGIN {
  for (i = 0; i < %d; i++) {
    sum += i
  }
  print sum
}
`, b.N)
}

func BenchmarkSimplePattern(b *testing.B) {
	b.StopTimer()
	inputLines := []string{}
	expectedLines := []string{}
	for i := 0; i < b.N; i++ {
		if i != 0 && i%2 == 0 {
			line := fmt.Sprintf("%d", i)
			inputLines = append(inputLines, line)
			expectedLines = append(expectedLines, line)
		} else {
			inputLines = append(inputLines, "")
		}
	}
	input := strings.Join(inputLines, "\n")
	expected := strings.Join(expectedLines, "\n")
	benchmarkProgram(b, 1, input, expected, "$0")
}

func BenchmarkFields(b *testing.B) {
	b.StopTimer()
	inputLines := []string{}
	expectedLines := []string{}
	for i := 1; i < b.N+1; i++ {
		inputLines = append(inputLines, fmt.Sprintf("%d %d %d", i, i*2, i*3))
		expectedLines = append(expectedLines, fmt.Sprintf("%d %d", i, i*3))
	}
	input := strings.Join(inputLines, "\n")
	expected := strings.Join(expectedLines, "\n")
	benchmarkProgram(b, 1, input, expected, "{ print $1, $3 }")
}
<|endoftext|>


<commit_before>package interpreter

import (
	"bytes"
	"errors"
	"fmt"
	"os"
	"regexp"
	"strconv"
	"strings"
	"unicode"

	"../dos"
)

type StatementT struct {
	Argv     []string
	Redirect []*Redirecter
	Term     string
}

var prefix []string = []string{" 0<", " 1>", " 2>"}

var PercentFunc = map[string]func() string{
	"CD": func() string {
		wd, err := os.Getwd()
		if err == nil {
			return wd
		} else {
			return ""
		}
	},
	"ERRORLEVEL": func() string {
		return ErrorLevelStr
	},
}

var rxUnicode = regexp.MustCompile("^[uU]\\+?([0-9a-fA-F]+)$")

func OurGetEnv(name string) (string, bool) {
	value := os.Getenv(name)
	if value != "" {
		return value, true
	} else if m := rxUnicode.FindStringSubmatch(name); m != nil {
		ucode, _ := strconv.ParseInt(m[1], 16, 32)
		return fmt.Sprintf("%c", rune(ucode)), true
	} else if f, ok := PercentFunc[strings.ToUpper(name)]; ok {
		return f(), true
	} else {
		return "", false
	}
}

func chomp(buffer *bytes.Buffer) {
	original := buffer.String()
	buffer.Reset()
	var lastchar rune
	for i, ch := range original {
		if i > 0 {
			buffer.WriteRune(lastchar)
		}
		lastchar = ch
	}
}

const NOTQUOTED = '\000'

var EmptyCommandFound = errors.New("Empty command found")

func dequote(source *bytes.Buffer) string {
	var buffer bytes.Buffer

	lastchar := ' '
	quoteNow := NOTQUOTED
	yenCount := 0
	for {
		ch, _, err := source.ReadRune()
		if err != nil {
			break
		}
		if ch == '~' && unicode.IsSpace(lastchar) {
			if home := dos.GetHome(); home != "" {
				buffer.WriteString(home)
			} else {
				buffer.WriteRune('~')
			}
			lastchar = '~'
			continue
		}
		if ch == '%' && quoteNow != '\'' && yenCount%2 == 0 {
			var nameBuf bytes.Buffer
			for {
				ch, _, err = source.ReadRune()
				if err != nil {
					buffer.WriteRune('%')
					buffer.WriteString(nameBuf.String())
					return buffer.String()
				}
				if ch == '%' {
					nameStr := nameBuf.String()
					if value, ok := OurGetEnv(nameStr); ok {
						buffer.WriteString(value)
					} else {
						buffer.WriteRune('%')
						buffer.WriteString(nameStr)
						buffer.WriteRune('%')
					}
					break
				}
				if !unicode.IsLower(ch) && !unicode.IsUpper(ch) && !unicode.IsNumber(ch) && ch != '_' && ch != '+' {
					source.UnreadRune()
					buffer.WriteRune('%')
					buffer.WriteString(nameBuf.String())
					break
				}
				nameBuf.WriteRune(ch)
			}
			continue
		}

		if quoteNow != NOTQUOTED && ch == quoteNow && yenCount%2 == 0 {
			// Close Quotation.
			for ; yenCount >= 2; yenCount -= 2 {
				buffer.WriteRune('\\')
			}
			quoteNow = NOTQUOTED
		} else if (ch == '\'' || ch == '"') && quoteNow == NOTQUOTED && yenCount%2 == 0 {
			// Open Qutation.
			for ; yenCount >= 2; yenCount -= 2 {
				buffer.WriteRune('\\')
			}
			quoteNow = ch
			if ch == lastchar {
				buffer.WriteRune(ch)
			}
		} else {
			if ch == '\\' {
				yenCount++
			} else if ch == '\'' || ch == '"' {
				for ; yenCount >= 2; yenCount -= 2 {
					buffer.WriteRune('\\')
				}
				yenCount = 0
				buffer.WriteRune(ch)
			} else {
				for ; yenCount > 0; yenCount-- {
					buffer.WriteRune('\\')
				}
				buffer.WriteRune(ch)
			}
		}
		lastchar = ch
	}
	for ; yenCount > 0; yenCount-- {
		buffer.WriteRune('\\')
	}
	return buffer.String()
}

func terminate(statements *[]*StatementT,
	isRedirected *bool,
	redirect *[]*Redirecter,
	buffer *bytes.Buffer,
	argv *[]string,
	term string) error {

	statement1 := new(StatementT)
	if buffer.Len() > 0 {
		if *isRedirected && len(*redirect) > 0 {
			(*redirect)[len(*redirect)-1].SetPath(dequote(buffer))
			*isRedirected = false
			statement1.Argv = *argv
		} else {
			statement1.Argv = append(*argv, dequote(buffer))
		}
		buffer.Reset()
	} else if len(*argv) <= 0 {
		return EmptyCommandFound
	} else {
		statement1.Argv = *argv
	}
	statement1.Redirect = *redirect
	*redirect = make([]*Redirecter, 0, 3)
	*argv = make([]string, 0)
	statement1.Term = term
	*statements = append(*statements, statement1)
	return nil
}

func parse1(text string) ([]*StatementT, error) {
	quoteNow := NOTQUOTED
	yenCount := 0
	statements := make([]*StatementT, 0)
	argv := make([]string, 0)
	lastchar := ' '
	var buffer bytes.Buffer
	isNextRedirect := false
	redirect := make([]*Redirecter, 0, 3)

	TermWord := func() {
		if isNextRedirect && len(redirect) > 0 {
			redirect[len(redirect)-1].SetPath(dequote(&buffer))
		} else {
			if buffer.Len() > 0 {
				argv = append(argv, dequote(&buffer))
			}
		}
		buffer.Reset()
	}

	reader := strings.NewReader(text)
	for reader.Len() > 0 {
		ch, chSize, chErr := reader.ReadRune()
		if chSize <= 0 {
			break
		}
		if chErr != nil {
			return nil, chErr
		}
		if quoteNow == NOTQUOTED {
			if yenCount%2 == 0 && (ch == '"' || ch == '\'') {
				quoteNow = ch
			}
		} else if yenCount%2 == 0 && ch == quoteNow {
			quoteNow = NOTQUOTED
		}
		if quoteNow != NOTQUOTED {
			buffer.WriteRune(ch)
		} else if ch == ' ' {
			if buffer.Len() > 0 {
				TermWord()
				isNextRedirect = false
			}
		} else if lastchar == ' ' && ch == ';' {
			if err := terminate(&statements, &isNextRedirect, &redirect, &buffer, &argv, ";"); err != nil {
				return nil, err
			}
		} else if ch == '|' {
			if lastchar == '|' {
				if len(statements) <= 0 {
					return nil, EmptyCommandFound
				}
				statements[len(statements)-1].Term = "||"
			} else {
				if err := terminate(&statements, &isNextRedirect, &redirect, &buffer, &argv, "|"); err != nil {
					return nil, err
				}
			}
		} else if ch == '&' {
			switch lastchar {
			case '&':
				if len(statements) <= 0 {
					return nil, EmptyCommandFound
				}
				statements[len(statements)-1].Term = "&&"
			case '|':
				if len(statements) <= 0 {
					return nil, EmptyCommandFound
				}
				statements[len(statements)-1].Term = "|&"
			case '>':
				// >&[n]
				ch2, ch2siz, ch2err := reader.ReadRune()
				if ch2err != nil {
					return nil, ch2err
				}
				if ch2siz <= 0 {
					return nil, errors.New("Too Near EOF for >&")
				}
				red := redirect[len(redirect)-1]
				switch ch2 {
				case '1':
					red.DupFrom(1)
				case '2':
					red.DupFrom(2)
				default:
					return nil, errors.New("Syntax error after >&")
				}
				isNextRedirect = false
			default:
				if err := terminate(&statements, &isNextRedirect, &redirect, &buffer, &argv, "&"); err != nil {
					return nil, err
				}
			}
		} else if ch == '>' {
			switch lastchar {
			case '1':
				// 1>
				chomp(&buffer)
				TermWord()
				redirect = append(redirect, NewRedirecter(1))
			case '2':
				// 2>
				chomp(&buffer)
				TermWord()
				redirect = append(redirect, NewRedirecter(2))
			case '>':
				// >>
				TermWord()
				if len(redirect) >= 0 {
					redirect[len(redirect)-1].SetAppend()
				}
			default:
				// >
				TermWord()
				redirect = append(redirect, NewRedirecter(1))
			}
			isNextRedirect = true
		} else if ch == '<' {
			TermWord()
			redirect = append(redirect, NewRedirecter(0))
			isNextRedirect = true
		} else {
			buffer.WriteRune(ch)
		}
		if ch == '\\' {
			yenCount++
		} else {
			yenCount = 0
		}
		lastchar = ch
	}
	if err := terminate(&statements, &isNextRedirect, &redirect, &buffer, &argv, " "); err != nil {
		return nil, err
	}
	return statements, nil
}

// Make arrays whose elements are pipelines
func parse2(statements []*StatementT) [][]*StatementT {
	result := make([][]*StatementT, 1)
	for _, statement1 := range statements {
		result[len(result)-1] = append(result[len(result)-1], statement1)
		switch statement1.Term {
		case "|", "|&":

		default:
			result = append(result, make([]*StatementT, 0))
		}
	}
	if len(result[len(result)-1]) <= 0 {
		result = result[0 : len(result)-1]
	}
	return result
}

func Parse(text string) ([][]*StatementT, error) {
	result1, err := parse1(text)
	if err != nil {
		return nil, err
	}
	result2 := parse2(result1)
	return result2, nil
}
<commit_msg>Revert "Fix: 'A | (empty)' was not errored and A was executed." for #100<commit_after>package interpreter

import (
	"bytes"
	"errors"
	"fmt"
	"os"
	"regexp"
	"strconv"
	"strings"
	"unicode"

	"../dos"
)

type StatementT struct {
	Argv     []string
	Redirect []*Redirecter
	Term     string
}

var prefix []string = []string{" 0<", " 1>", " 2>"}

var PercentFunc = map[string]func() string{
	"CD": func() string {
		wd, err := os.Getwd()
		if err == nil {
			return wd
		} else {
			return ""
		}
	},
	"ERRORLEVEL": func() string {
		return ErrorLevelStr
	},
}

var rxUnicode = regexp.MustCompile("^[uU]\\+?([0-9a-fA-F]+)$")

func OurGetEnv(name string) (string, bool) {
	value := os.Getenv(name)
	if value != "" {
		return value, true
	} else if m := rxUnicode.FindStringSubmatch(name); m != nil {
		ucode, _ := strconv.ParseInt(m[1], 16, 32)
		return fmt.Sprintf("%c", rune(ucode)), true
	} else if f, ok := PercentFunc[strings.ToUpper(name)]; ok {
		return f(), true
	} else {
		return "", false
	}
}

func chomp(buffer *bytes.Buffer) {
	original := buffer.String()
	buffer.Reset()
	var lastchar rune
	for i, ch := range original {
		if i > 0 {
			buffer.WriteRune(lastchar)
		}
		lastchar = ch
	}
}

const NOTQUOTED = '\000'

const EMPTY_COMMAND_FOUND = "Empty command found"

func dequote(source *bytes.Buffer) string {
	var buffer bytes.Buffer

	lastchar := ' '
	quoteNow := NOTQUOTED
	yenCount := 0
	for {
		ch, _, err := source.ReadRune()
		if err != nil {
			break
		}
		if ch == '~' && unicode.IsSpace(lastchar) {
			if home := dos.GetHome(); home != "" {
				buffer.WriteString(home)
			} else {
				buffer.WriteRune('~')
			}
			lastchar = '~'
			continue
		}
		if ch == '%' && quoteNow != '\'' && yenCount%2 == 0 {
			var nameBuf bytes.Buffer
			for {
				ch, _, err = source.ReadRune()
				if err != nil {
					buffer.WriteRune('%')
					buffer.WriteString(nameBuf.String())
					return buffer.String()
				}
				if ch == '%' {
					nameStr := nameBuf.String()
					if value, ok := OurGetEnv(nameStr); ok {
						buffer.WriteString(value)
					} else {
						buffer.WriteRune('%')
						buffer.WriteString(nameStr)
						buffer.WriteRune('%')
					}
					break
				}
				if !unicode.IsLower(ch) && !unicode.IsUpper(ch) && !unicode.IsNumber(ch) && ch != '_' && ch != '+' {
					source.UnreadRune()
					buffer.WriteRune('%')
					buffer.WriteString(nameBuf.String())
					break
				}
				nameBuf.WriteRune(ch)
			}
			continue
		}

		if quoteNow != NOTQUOTED && ch == quoteNow && yenCount%2 == 0 {
			// Close Quotation.
			for ; yenCount >= 2; yenCount -= 2 {
				buffer.WriteRune('\\')
			}
			quoteNow = NOTQUOTED
		} else if (ch == '\'' || ch == '"') && quoteNow == NOTQUOTED && yenCount%2 == 0 {
			// Open Qutation.
			for ; yenCount >= 2; yenCount -= 2 {
				buffer.WriteRune('\\')
			}
			quoteNow = ch
			if ch == lastchar {
				buffer.WriteRune(ch)
			}
		} else {
			if ch == '\\' {
				yenCount++
			} else if ch == '\'' || ch == '"' {
				for ; yenCount >= 2; yenCount -= 2 {
					buffer.WriteRune('\\')
				}
				yenCount = 0
				buffer.WriteRune(ch)
			} else {
				for ; yenCount > 0; yenCount-- {
					buffer.WriteRune('\\')
				}
				buffer.WriteRune(ch)
			}
		}
		lastchar = ch
	}
	for ; yenCount > 0; yenCount-- {
		buffer.WriteRune('\\')
	}
	return buffer.String()
}

func terminate(statements *[]*StatementT,
	isRedirected *bool,
	redirect *[]*Redirecter,
	buffer *bytes.Buffer,
	argv *[]string,
	term string) {

	statement1 := new(StatementT)
	if buffer.Len() > 0 {
		if *isRedirected && len(*redirect) > 0 {
			(*redirect)[len(*redirect)-1].SetPath(dequote(buffer))
			*isRedirected = false
			statement1.Argv = *argv
		} else {
			statement1.Argv = append(*argv, dequote(buffer))
		}
		buffer.Reset()
	} else if len(*argv) <= 0 {
		return
	} else {
		statement1.Argv = *argv
	}
	statement1.Redirect = *redirect
	*redirect = make([]*Redirecter, 0, 3)
	*argv = make([]string, 0)
	statement1.Term = term
	*statements = append(*statements, statement1)
}

func parse1(text string) ([]*StatementT, error) {
	quoteNow := NOTQUOTED
	yenCount := 0
	statements := make([]*StatementT, 0)
	argv := make([]string, 0)
	lastchar := ' '
	var buffer bytes.Buffer
	isNextRedirect := false
	redirect := make([]*Redirecter, 0, 3)

	TermWord := func() {
		if isNextRedirect && len(redirect) > 0 {
			redirect[len(redirect)-1].SetPath(dequote(&buffer))
		} else {
			if buffer.Len() > 0 {
				argv = append(argv, dequote(&buffer))
			}
		}
		buffer.Reset()
	}

	reader := strings.NewReader(text)
	for reader.Len() > 0 {
		ch, chSize, chErr := reader.ReadRune()
		if chSize <= 0 {
			break
		}
		if chErr != nil {
			return nil, chErr
		}
		if quoteNow == NOTQUOTED {
			if yenCount%2 == 0 && (ch == '"' || ch == '\'') {
				quoteNow = ch
			}
		} else if yenCount%2 == 0 && ch == quoteNow {
			quoteNow = NOTQUOTED
		}
		if quoteNow != NOTQUOTED {
			buffer.WriteRune(ch)
		} else if ch == ' ' {
			if buffer.Len() > 0 {
				TermWord()
				isNextRedirect = false
			}
		} else if lastchar == ' ' && ch == ';' {
			terminate(&statements, &isNextRedirect, &redirect, &buffer, &argv, ";")
		} else if ch == '|' {
			if lastchar == '|' {
				if len(statements) <= 0 {
					return nil, errors.New(EMPTY_COMMAND_FOUND)
				}
				statements[len(statements)-1].Term = "||"
			} else {
				terminate(&statements, &isNextRedirect, &redirect, &buffer, &argv, "|")
			}
		} else if ch == '&' {
			switch lastchar {
			case '&':
				if len(statements) <= 0 {
					return nil, errors.New(EMPTY_COMMAND_FOUND)
				}
				statements[len(statements)-1].Term = "&&"
			case '|':
				if len(statements) <= 0 {
					return nil, errors.New(EMPTY_COMMAND_FOUND)
				}
				statements[len(statements)-1].Term = "|&"
			case '>':
				// >&[n]
				ch2, ch2siz, ch2err := reader.ReadRune()
				if ch2err != nil {
					return nil, ch2err
				}
				if ch2siz <= 0 {
					return nil, errors.New("Too Near EOF for >&")
				}
				red := redirect[len(redirect)-1]
				switch ch2 {
				case '1':
					red.DupFrom(1)
				case '2':
					red.DupFrom(2)
				default:
					return nil, errors.New("Syntax error after >&")
				}
				isNextRedirect = false
			default:
				terminate(&statements, &isNextRedirect, &redirect, &buffer, &argv, "&")
			}
		} else if ch == '>' {
			switch lastchar {
			case '1':
				// 1>
				chomp(&buffer)
				TermWord()
				redirect = append(redirect, NewRedirecter(1))
			case '2':
				// 2>
				chomp(&buffer)
				TermWord()
				redirect = append(redirect, NewRedirecter(2))
			case '>':
				// >>
				TermWord()
				if len(redirect) >= 0 {
					redirect[len(redirect)-1].SetAppend()
				}
			default:
				// >
				TermWord()
				redirect = append(redirect, NewRedirecter(1))
			}
			isNextRedirect = true
		} else if ch == '<' {
			TermWord()
			redirect = append(redirect, NewRedirecter(0))
			isNextRedirect = true
		} else {
			buffer.WriteRune(ch)
		}
		if ch == '\\' {
			yenCount++
		} else {
			yenCount = 0
		}
		lastchar = ch
	}
	terminate(&statements, &isNextRedirect, &redirect, &buffer, &argv, " ")
	return statements, nil
}

// Make arrays whose elements are pipelines
func parse2(statements []*StatementT) [][]*StatementT {
	result := make([][]*StatementT, 1)
	for _, statement1 := range statements {
		result[len(result)-1] = append(result[len(result)-1], statement1)
		switch statement1.Term {
		case "|", "|&":

		default:
			result = append(result, make([]*StatementT, 0))
		}
	}
	if len(result[len(result)-1]) <= 0 {
		result = result[0 : len(result)-1]
	}
	return result
}

func Parse(text string) ([][]*StatementT, error) {
	result1, err := parse1(text)
	if err != nil {
		return nil, err
	}
	result2 := parse2(result1)
	return result2, nil
}
<|endoftext|>


<commit_before>package bbs

//go:generate counterfeiter -o fake_bbs/fake_converger_bbs.go . ConvergerBBS

import (
	"time"

	"github.com/cloudfoundry-incubator/runtime-schema/bbs/lock_bbs"
	"github.com/cloudfoundry-incubator/runtime-schema/bbs/lrp_bbs"
	"github.com/cloudfoundry-incubator/runtime-schema/bbs/services_bbs"
	"github.com/cloudfoundry-incubator/runtime-schema/bbs/task_bbs"
	"github.com/cloudfoundry-incubator/runtime-schema/cb"
	"github.com/cloudfoundry-incubator/runtime-schema/models"
	"github.com/cloudfoundry/gunk/timeprovider"
	"github.com/cloudfoundry/storeadapter"
	"github.com/pivotal-golang/lager"
	"github.com/tedsuo/ifrit"
)

//Bulletin Board System/Store

type ReceptorBBS interface {
	//task
	DesireTask(models.Task) error
	Tasks() ([]models.Task, error)
	TasksByDomain(domain string) ([]models.Task, error)
	TaskByGuid(taskGuid string) (*models.Task, error)
	ResolvingTask(taskGuid string) error
	ResolveTask(taskGuid string) error
	CancelTask(taskGuid string) error

	//desired lrp
	DesireLRP(models.DesiredLRP) error
	UpdateDesiredLRP(processGuid string, update models.DesiredLRPUpdate) error
	RemoveDesiredLRPByProcessGuid(processGuid string) error
	DesiredLRPs() ([]models.DesiredLRP, error)
	DesiredLRPsByDomain(domain string) ([]models.DesiredLRP, error)
	DesiredLRPByProcessGuid(processGuid string) (*models.DesiredLRP, error)

	//actual lrp
	ActualLRPs() ([]models.ActualLRP, error)
	ActualLRPsByDomain(domain string) ([]models.ActualLRP, error)
	ActualLRPsByProcessGuid(string) ([]models.ActualLRP, error)
	ActualLRPByProcessGuidAndIndex(string, int) (*models.ActualLRP, error)
	RequestStopLRPInstance(stopInstances models.ActualLRP) error

	// cells
	Cells() ([]models.CellPresence, error)

	// freshness
	BumpFreshness(models.Freshness) error
	Freshnesses() ([]models.Freshness, error)

	//services
	NewReceptorHeartbeat(models.ReceptorPresence, time.Duration) ifrit.Runner
}

type RepBBS interface {
	//services
	NewCellHeartbeat(cellPresence models.CellPresence, interval time.Duration) ifrit.Runner

	//task
	StartTask(taskGuid string, cellID string) error
	TaskByGuid(taskGuid string) (*models.Task, error)
	TasksByCellID(cellID string) ([]models.Task, error)
	CompleteTask(taskGuid string, failed bool, failureReason string, result string) error

	//lrp
	ActualLRPsByCellID(cellID string) ([]models.ActualLRP, error)
	ClaimActualLRP(models.ActualLRPKey, models.ActualLRPContainerKey, lager.Logger) error
	StartActualLRP(models.ActualLRPKey, models.ActualLRPContainerKey, models.ActualLRPNetInfo, lager.Logger) error
	RemoveActualLRP(models.ActualLRPKey, models.ActualLRPContainerKey, lager.Logger) error
}

type ConvergerBBS interface {
	//lrp
	ConvergeLRPs(time.Duration)
	ActualLRPsByProcessGuid(string) ([]models.ActualLRP, error)
	WatchForDesiredLRPChanges() (<-chan models.DesiredLRPChange, chan<- bool, <-chan error)
	CreateActualLRP(models.DesiredLRP, int, lager.Logger) error
	RetireActualLRPs([]models.ActualLRP, lager.Logger) error

	//task
	ConvergeTasks(timeToClaim, convergenceInterval, timeToResolve time.Duration)

	//lock
	NewConvergeLock(convergerID string, interval time.Duration) ifrit.Runner
}

type NsyncBBS interface {
	//lock
	NewNsyncBulkerLock(bulkerID string, interval time.Duration) ifrit.Runner
	NewNsyncListenerLock(listenerID string, interval time.Duration) ifrit.Runner
}

type AuctioneerBBS interface {
	//services
	Cells() ([]models.CellPresence, error)

	// task
	CompleteTask(taskGuid string, failed bool, failureReason string, result string) error

	//lock
	NewAuctioneerLock(auctioneerPresence models.AuctioneerPresence, interval time.Duration) (ifrit.Runner, error)
}

type MetricsBBS interface {
	//task
	Tasks() ([]models.Task, error)

	//services
	ServiceRegistrations() (models.ServiceRegistrations, error)

	//lrps
	Freshnesses() ([]models.Freshness, error)
	DesiredLRPs() ([]models.DesiredLRP, error)
	ActualLRPs() ([]models.ActualLRP, error)

	//lock
	NewRuntimeMetricsLock(runtimeMetricsID string, interval time.Duration) ifrit.Runner
}

type RouteEmitterBBS interface {
	// lrp
	WatchForDesiredLRPChanges() (<-chan models.DesiredLRPChange, chan<- bool, <-chan error)
	WatchForActualLRPChanges() (<-chan models.ActualLRPChange, chan<- bool, <-chan error)
	DesiredLRPs() ([]models.DesiredLRP, error)
	RunningActualLRPs() ([]models.ActualLRP, error)

	//lock
	NewRouteEmitterLock(emitterID string, interval time.Duration) ifrit.Runner
}

type VeritasBBS interface {
	//task
	Tasks() ([]models.Task, error)

	//lrp
	DesiredLRPs() ([]models.DesiredLRP, error)
	ActualLRPs() ([]models.ActualLRP, error)
	DesireLRP(models.DesiredLRP) error
	RemoveDesiredLRPByProcessGuid(guid string) error
	Freshnesses() ([]models.Freshness, error)

	//services
	Cells() ([]models.CellPresence, error)
}

func NewReceptorBBS(store storeadapter.StoreAdapter, timeProvider timeprovider.TimeProvider, logger lager.Logger) ReceptorBBS {
	return NewBBS(store, timeProvider, logger)
}

func NewRepBBS(store storeadapter.StoreAdapter, timeProvider timeprovider.TimeProvider, logger lager.Logger) RepBBS {
	return NewBBS(store, timeProvider, logger)
}

func NewConvergerBBS(store storeadapter.StoreAdapter, timeProvider timeprovider.TimeProvider, logger lager.Logger) ConvergerBBS {
	return NewBBS(store, timeProvider, logger)
}

func NewNsyncBBS(store storeadapter.StoreAdapter, timeProvider timeprovider.TimeProvider, logger lager.Logger) NsyncBBS {
	return NewBBS(store, timeProvider, logger)
}

func NewAuctioneerBBS(store storeadapter.StoreAdapter, timeProvider timeprovider.TimeProvider, logger lager.Logger) AuctioneerBBS {
	return NewBBS(store, timeProvider, logger)
}

func NewMetricsBBS(store storeadapter.StoreAdapter, timeProvider timeprovider.TimeProvider, logger lager.Logger) MetricsBBS {
	return NewBBS(store, timeProvider, logger)
}

func NewRouteEmitterBBS(store storeadapter.StoreAdapter, timeProvider timeprovider.TimeProvider, logger lager.Logger) RouteEmitterBBS {
	return NewBBS(store, timeProvider, logger)
}

func NewVeritasBBS(store storeadapter.StoreAdapter, timeProvider timeprovider.TimeProvider, logger lager.Logger) VeritasBBS {
	return NewBBS(store, timeProvider, logger)
}

func NewBBS(store storeadapter.StoreAdapter, timeProvider timeprovider.TimeProvider, logger lager.Logger) *BBS {
	services := services_bbs.New(store, logger.Session("services-bbs"))
	auctioneerClient := cb.NewAuctioneerClient()

	return &BBS{
		LockBBS:     lock_bbs.New(store, logger.Session("lock-bbs")),
		LRPBBS:      lrp_bbs.New(store, timeProvider, cb.NewCellClient(), auctioneerClient, services, logger.Session("lrp-bbs")),
		ServicesBBS: services,
		TaskBBS:     task_bbs.New(store, timeProvider, cb.NewTaskClient(), auctioneerClient, services, logger.Session("task-bbs")),
	}
}

type BBS struct {
	*lock_bbs.LockBBS
	*lrp_bbs.LRPBBS
	*services_bbs.ServicesBBS
	*task_bbs.TaskBBS
}
<commit_msg>add auctioneer address to veritas bbs<commit_after>package bbs

//go:generate counterfeiter -o fake_bbs/fake_converger_bbs.go . ConvergerBBS

import (
	"time"

	"github.com/cloudfoundry-incubator/runtime-schema/bbs/lock_bbs"
	"github.com/cloudfoundry-incubator/runtime-schema/bbs/lrp_bbs"
	"github.com/cloudfoundry-incubator/runtime-schema/bbs/services_bbs"
	"github.com/cloudfoundry-incubator/runtime-schema/bbs/task_bbs"
	"github.com/cloudfoundry-incubator/runtime-schema/cb"
	"github.com/cloudfoundry-incubator/runtime-schema/models"
	"github.com/cloudfoundry/gunk/timeprovider"
	"github.com/cloudfoundry/storeadapter"
	"github.com/pivotal-golang/lager"
	"github.com/tedsuo/ifrit"
)

//Bulletin Board System/Store

type ReceptorBBS interface {
	//task
	DesireTask(models.Task) error
	Tasks() ([]models.Task, error)
	TasksByDomain(domain string) ([]models.Task, error)
	TaskByGuid(taskGuid string) (*models.Task, error)
	ResolvingTask(taskGuid string) error
	ResolveTask(taskGuid string) error
	CancelTask(taskGuid string) error

	//desired lrp
	DesireLRP(models.DesiredLRP) error
	UpdateDesiredLRP(processGuid string, update models.DesiredLRPUpdate) error
	RemoveDesiredLRPByProcessGuid(processGuid string) error
	DesiredLRPs() ([]models.DesiredLRP, error)
	DesiredLRPsByDomain(domain string) ([]models.DesiredLRP, error)
	DesiredLRPByProcessGuid(processGuid string) (*models.DesiredLRP, error)

	//actual lrp
	ActualLRPs() ([]models.ActualLRP, error)
	ActualLRPsByDomain(domain string) ([]models.ActualLRP, error)
	ActualLRPsByProcessGuid(string) ([]models.ActualLRP, error)
	ActualLRPByProcessGuidAndIndex(string, int) (*models.ActualLRP, error)
	RequestStopLRPInstance(stopInstances models.ActualLRP) error

	// cells
	Cells() ([]models.CellPresence, error)

	// freshness
	BumpFreshness(models.Freshness) error
	Freshnesses() ([]models.Freshness, error)

	//services
	NewReceptorHeartbeat(models.ReceptorPresence, time.Duration) ifrit.Runner
}

type RepBBS interface {
	//services
	NewCellHeartbeat(cellPresence models.CellPresence, interval time.Duration) ifrit.Runner

	//task
	StartTask(taskGuid string, cellID string) error
	TaskByGuid(taskGuid string) (*models.Task, error)
	TasksByCellID(cellID string) ([]models.Task, error)
	CompleteTask(taskGuid string, failed bool, failureReason string, result string) error

	//lrp
	ActualLRPsByCellID(cellID string) ([]models.ActualLRP, error)
	ClaimActualLRP(models.ActualLRPKey, models.ActualLRPContainerKey, lager.Logger) error
	StartActualLRP(models.ActualLRPKey, models.ActualLRPContainerKey, models.ActualLRPNetInfo, lager.Logger) error
	RemoveActualLRP(models.ActualLRPKey, models.ActualLRPContainerKey, lager.Logger) error
}

type ConvergerBBS interface {
	//lrp
	ConvergeLRPs(time.Duration)
	ActualLRPsByProcessGuid(string) ([]models.ActualLRP, error)
	WatchForDesiredLRPChanges() (<-chan models.DesiredLRPChange, chan<- bool, <-chan error)
	CreateActualLRP(models.DesiredLRP, int, lager.Logger) error
	RetireActualLRPs([]models.ActualLRP, lager.Logger) error

	//task
	ConvergeTasks(timeToClaim, convergenceInterval, timeToResolve time.Duration)

	//lock
	NewConvergeLock(convergerID string, interval time.Duration) ifrit.Runner
}

type NsyncBBS interface {
	//lock
	NewNsyncBulkerLock(bulkerID string, interval time.Duration) ifrit.Runner
	NewNsyncListenerLock(listenerID string, interval time.Duration) ifrit.Runner
}

type AuctioneerBBS interface {
	//services
	Cells() ([]models.CellPresence, error)

	// task
	CompleteTask(taskGuid string, failed bool, failureReason string, result string) error

	//lock
	NewAuctioneerLock(auctioneerPresence models.AuctioneerPresence, interval time.Duration) (ifrit.Runner, error)
}

type MetricsBBS interface {
	//task
	Tasks() ([]models.Task, error)

	//services
	ServiceRegistrations() (models.ServiceRegistrations, error)

	//lrps
	Freshnesses() ([]models.Freshness, error)
	DesiredLRPs() ([]models.DesiredLRP, error)
	ActualLRPs() ([]models.ActualLRP, error)

	//lock
	NewRuntimeMetricsLock(runtimeMetricsID string, interval time.Duration) ifrit.Runner
}

type RouteEmitterBBS interface {
	// lrp
	WatchForDesiredLRPChanges() (<-chan models.DesiredLRPChange, chan<- bool, <-chan error)
	WatchForActualLRPChanges() (<-chan models.ActualLRPChange, chan<- bool, <-chan error)
	DesiredLRPs() ([]models.DesiredLRP, error)
	RunningActualLRPs() ([]models.ActualLRP, error)

	//lock
	NewRouteEmitterLock(emitterID string, interval time.Duration) ifrit.Runner
}

type VeritasBBS interface {
	//task
	Tasks() ([]models.Task, error)

	//lrp
	DesiredLRPs() ([]models.DesiredLRP, error)
	ActualLRPs() ([]models.ActualLRP, error)
	DesireLRP(models.DesiredLRP) error
	RemoveDesiredLRPByProcessGuid(guid string) error
	Freshnesses() ([]models.Freshness, error)

	//services
	Cells() ([]models.CellPresence, error)
	AuctioneerAddress() (string, error)
}

func NewReceptorBBS(store storeadapter.StoreAdapter, timeProvider timeprovider.TimeProvider, logger lager.Logger) ReceptorBBS {
	return NewBBS(store, timeProvider, logger)
}

func NewRepBBS(store storeadapter.StoreAdapter, timeProvider timeprovider.TimeProvider, logger lager.Logger) RepBBS {
	return NewBBS(store, timeProvider, logger)
}

func NewConvergerBBS(store storeadapter.StoreAdapter, timeProvider timeprovider.TimeProvider, logger lager.Logger) ConvergerBBS {
	return NewBBS(store, timeProvider, logger)
}

func NewNsyncBBS(store storeadapter.StoreAdapter, timeProvider timeprovider.TimeProvider, logger lager.Logger) NsyncBBS {
	return NewBBS(store, timeProvider, logger)
}

func NewAuctioneerBBS(store storeadapter.StoreAdapter, timeProvider timeprovider.TimeProvider, logger lager.Logger) AuctioneerBBS {
	return NewBBS(store, timeProvider, logger)
}

func NewMetricsBBS(store storeadapter.StoreAdapter, timeProvider timeprovider.TimeProvider, logger lager.Logger) MetricsBBS {
	return NewBBS(store, timeProvider, logger)
}

func NewRouteEmitterBBS(store storeadapter.StoreAdapter, timeProvider timeprovider.TimeProvider, logger lager.Logger) RouteEmitterBBS {
	return NewBBS(store, timeProvider, logger)
}

func NewVeritasBBS(store storeadapter.StoreAdapter, timeProvider timeprovider.TimeProvider, logger lager.Logger) VeritasBBS {
	return NewBBS(store, timeProvider, logger)
}

func NewBBS(store storeadapter.StoreAdapter, timeProvider timeprovider.TimeProvider, logger lager.Logger) *BBS {
	services := services_bbs.New(store, logger.Session("services-bbs"))
	auctioneerClient := cb.NewAuctioneerClient()

	return &BBS{
		LockBBS:     lock_bbs.New(store, logger.Session("lock-bbs")),
		LRPBBS:      lrp_bbs.New(store, timeProvider, cb.NewCellClient(), auctioneerClient, services, logger.Session("lrp-bbs")),
		ServicesBBS: services,
		TaskBBS:     task_bbs.New(store, timeProvider, cb.NewTaskClient(), auctioneerClient, services, logger.Session("task-bbs")),
	}
}

type BBS struct {
	*lock_bbs.LockBBS
	*lrp_bbs.LRPBBS
	*services_bbs.ServicesBBS
	*task_bbs.TaskBBS
}
<|endoftext|>


<commit_before>/*
 *    Copyright (C) 2014-2017 Christian Muehlhaeuser
 *                  2014      Michael Wendland
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Affero General Public License as published
 *    by the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *    Authors:
 *      Christian Muehlhaeuser <muesli@gmail.com>
 *      Michael Wendland <michiwend@michiwend.com>
 *      Johannes Fürmann <johannes@weltraumpflege.org>
 */

package main

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"os/signal"
	"syscall"

	log "github.com/Sirupsen/logrus"

	"github.com/muesli/beehive/api"
	"github.com/muesli/beehive/app"
	_ "github.com/muesli/beehive/filters"
	_ "github.com/muesli/beehive/filters/template"

	"github.com/muesli/beehive/bees"
)

var (
	configFile string
)

// Config contains an entire configuration set for Beehive
type Config struct {
	Bees    []bees.BeeConfig
	Actions []bees.Action
	Chains  []bees.Chain
}

// Loads chains from config
func loadConfig() Config {
	config := Config{}

	j, err := ioutil.ReadFile(configFile)
	if err == nil {
		err = json.Unmarshal(j, &config)
	}

	if err != nil {
		// log.Fatal(err)
	}

	return config
}

// Saves chains to config
func saveConfig(c Config) {
	j, err := json.MarshalIndent(c, "", "  ")
	if err == nil {
		err = ioutil.WriteFile(configFile, j, 0644)
	}

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

func main() {
	app.AddFlags([]app.CliFlag{
		{
			V:     &configFile,
			Name:  "config",
			Value: "./beehive.conf",
			Desc:  "Config-file to use",
		},
	})

	// Parse command-line args for all registered bees
	app.Run()
	api.Run()

	log.Println()
	log.Println("Beehive is buzzing...")

	config := loadConfig()

	// Initialize bees
	bees.StartBees(config.Bees)
	// Load actions from config
	bees.SetActions(config.Actions)
	// Load chains from config
	bees.SetChains(config.Chains)

	// Wait for signals
	ch := make(chan os.Signal, 1)
	signal.Notify(ch, syscall.SIGHUP, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL)

	for s := range ch {
		log.Println("Got signal:", s)

		abort := false
		switch s {
		case syscall.SIGHUP:
			config = loadConfig()
			bees.RestartBees(config.Bees)
			bees.SetChains(config.Chains)

		case syscall.SIGTERM:
			fallthrough
		case syscall.SIGKILL:
			fallthrough
		case syscall.SIGINT:
			abort = true
			break
		}

		if abort {
			break
		}
	}

	// Save chains to config
	log.Println("Storing config...")
	config.Bees = bees.BeeConfigs()
	config.Chains = bees.GetChains()
	config.Actions = bees.GetActions()
	saveConfig(config)
}
<commit_msg>Use os.Interrupt instead of the platform specific syscall.SIGINT signal<commit_after>/*
 *    Copyright (C) 2014-2017 Christian Muehlhaeuser
 *                  2014      Michael Wendland
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Affero General Public License as published
 *    by the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *    Authors:
 *      Christian Muehlhaeuser <muesli@gmail.com>
 *      Michael Wendland <michiwend@michiwend.com>
 *      Johannes Fürmann <johannes@weltraumpflege.org>
 */

package main

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"os/signal"
	"syscall"

	log "github.com/Sirupsen/logrus"

	"github.com/muesli/beehive/api"
	"github.com/muesli/beehive/app"
	_ "github.com/muesli/beehive/filters"
	_ "github.com/muesli/beehive/filters/template"

	"github.com/muesli/beehive/bees"
)

var (
	configFile string
)

// Config contains an entire configuration set for Beehive
type Config struct {
	Bees    []bees.BeeConfig
	Actions []bees.Action
	Chains  []bees.Chain
}

// Loads chains from config
func loadConfig() Config {
	config := Config{}

	j, err := ioutil.ReadFile(configFile)
	if err == nil {
		err = json.Unmarshal(j, &config)
	}

	if err != nil {
		// log.Fatal(err)
	}

	return config
}

// Saves chains to config
func saveConfig(c Config) {
	j, err := json.MarshalIndent(c, "", "  ")
	if err == nil {
		err = ioutil.WriteFile(configFile, j, 0644)
	}

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

func main() {
	app.AddFlags([]app.CliFlag{
		{
			V:     &configFile,
			Name:  "config",
			Value: "./beehive.conf",
			Desc:  "Config-file to use",
		},
	})

	// Parse command-line args for all registered bees
	app.Run()
	api.Run()

	log.Println()
	log.Println("Beehive is buzzing...")

	config := loadConfig()

	// Initialize bees
	bees.StartBees(config.Bees)
	// Load actions from config
	bees.SetActions(config.Actions)
	// Load chains from config
	bees.SetChains(config.Chains)

	// Wait for signals
	ch := make(chan os.Signal, 1)
	signal.Notify(ch, os.Interrupt, syscall.SIGHUP, syscall.SIGTERM, syscall.SIGKILL)

	for s := range ch {
		log.Println("Got signal:", s)

		abort := false
		switch s {
		case syscall.SIGHUP:
			config = loadConfig()
			bees.RestartBees(config.Bees)
			bees.SetActions(config.Actions)
			bees.SetChains(config.Chains)

		case syscall.SIGTERM:
			fallthrough
		case syscall.SIGKILL:
			fallthrough
		case os.Interrupt:
			abort = true
			break
		}

		if abort {
			break
		}
	}

	// Save chains to config
	log.Println("Storing config...")
	config.Bees = bees.BeeConfigs()
	config.Chains = bees.GetChains()
	config.Actions = bees.GetActions()
	saveConfig(config)
}
<|endoftext|>


<commit_before>package irmaclient

import (
	"encoding/json"
	"encoding/xml"
	"html"
	"io/ioutil"
	"math/big"
	"time"

	"github.com/go-errors/errors"
	"github.com/mhe/gabi"
	"github.com/privacybydesign/irmago"
	"github.com/privacybydesign/irmago/internal/fs"
)

// This file contains the update mechanism for Client
// as well as updates themselves.

type update struct {
	When    irma.Timestamp
	Number  int
	Success bool
	Error   *string
}

var clientUpdates = []func(client *Client) error{
	// Convert old cardemu.xml Android storage to our own storage format
	func(client *Client) error {
		_, err := client.ParseAndroidStorage()
		return err
	},

	// Adding scheme manager index, signature and public key
	// Check the signatures of all scheme managers, if any is not ok,
	// copy the entire irma_configuration folder from assets
	nil, // made irrelevant by irma_configuration-autocopying

	// Rename config -> preferences
	func(client *Client) (err error) {
		exists, err := fs.PathExists(client.storage.path("config"))
		if !exists || err != nil {
			return
		}
		oldStruct := &struct {
			SendCrashReports bool
		}{}
		// Load old file, convert to new struct, and save
		err = client.storage.load(oldStruct, "config")
		if err != nil {
			return err
		}
		client.Preferences = Preferences{
			EnableCrashReporting: oldStruct.SendCrashReports,
		}
		return client.storage.StorePreferences(client.Preferences)
	},

	// Copy new irma_configuration out of assets
	nil, // made irrelevant by irma_configuration-autocopying

	// For each keyshare server, include in its struct the identifier of its scheme manager
	func(client *Client) (err error) {
		keyshareServers, err := client.storage.LoadKeyshareServers()
		if err != nil {
			return err
		}
		for smi, kss := range keyshareServers {
			kss.SchemeManagerIdentifier = smi
		}
		return client.storage.StoreKeyshareServers(keyshareServers)
	},

	func(client *Client) (err error) {
		return client.Configuration.DeleteSchemeManager(irma.NewSchemeManagerIdentifier("test"))
	},
}

// update performs any function from clientUpdates that has not
// already been executed in the past, keeping track of previously executed updates
// in the file at updatesFile.
func (client *Client) update() error {
	// Load and parse file containing info about already performed updates
	var err error
	if client.updates, err = client.storage.LoadUpdates(); err != nil {
		return err
	}

	// Perform all new updates
	for i := len(client.updates); i < len(clientUpdates); i++ {
		err = nil
		if clientUpdates[i] != nil {
			err = clientUpdates[i](client)
		}
		u := update{
			When:    irma.Timestamp(time.Now()),
			Number:  i,
			Success: err == nil,
		}
		if err != nil {
			str := err.Error()
			u.Error = &str
		}
		client.updates = append(client.updates, u)
		if err != nil {
			break
		}
	}

	storeErr := client.storage.StoreUpdates(client.updates)
	if storeErr != nil {
		return storeErr
	}
	return err
}

// ParseAndroidStorage parses an Android cardemu.xml shared preferences file
// from the old Android IRMA app, parsing its credentials into the current instance,
// and saving them to storage.
// CAREFUL: this method overwrites any existing secret keys and attributes on storage.
func (client *Client) ParseAndroidStorage() (present bool, err error) {
	if client.androidStoragePath == "" {
		return false, nil
	}

	cardemuXML := client.androidStoragePath + "/shared_prefs/cardemu.xml"
	present, err = fs.PathExists(cardemuXML)
	if err != nil || !present {
		return
	}
	present = true

	bytes, err := ioutil.ReadFile(cardemuXML)
	if err != nil {
		return
	}
	parsedxml := struct {
		Strings []struct {
			Name    string `xml:"name,attr"`
			Content string `xml:",chardata"`
		} `xml:"string"`
	}{}
	if err = xml.Unmarshal(bytes, &parsedxml); err != nil {
		return
	}

	parsedjson := make(map[string][]*struct {
		Signature    *gabi.CLSignature `json:"signature"`
		Pk           *gabi.PublicKey   `json:"-"`
		Attributes   []*big.Int        `json:"attributes"`
		SharedPoints []*big.Int        `json:"public_sks"`
	})
	client.keyshareServers = make(map[irma.SchemeManagerIdentifier]*keyshareServer)
	for _, xmltag := range parsedxml.Strings {
		if xmltag.Name == "credentials" {
			jsontag := html.UnescapeString(xmltag.Content)
			if err = json.Unmarshal([]byte(jsontag), &parsedjson); err != nil {
				return
			}
		}
		if xmltag.Name == "keyshare" {
			jsontag := html.UnescapeString(xmltag.Content)
			if err = json.Unmarshal([]byte(jsontag), &client.keyshareServers); err != nil {
				return
			}
		}
		if xmltag.Name == "KeyshareKeypairs" {
			jsontag := html.UnescapeString(xmltag.Content)
			keys := make([]*paillierPrivateKey, 0, 3)
			if err = json.Unmarshal([]byte(jsontag), &keys); err != nil {
				return
			}
			client.paillierKeyCache = keys[0]
		}
	}

	for _, list := range parsedjson {
		client.secretkey = &secretKey{Key: list[0].Attributes[0]}
		for _, oldcred := range list {
			gabicred := &gabi.Credential{
				Attributes: oldcred.Attributes,
				Signature:  oldcred.Signature,
			}
			if oldcred.SharedPoints != nil && len(oldcred.SharedPoints) > 0 {
				gabicred.Signature.KeyshareP = oldcred.SharedPoints[0]
			}
			var cred *credential
			if cred, err = newCredential(gabicred, client.Configuration); err != nil {
				return
			}
			if cred.CredentialType() == nil {
				err = errors.New("cannot add unknown credential type")
				return
			}

			if err = client.addCredential(cred, false); err != nil {
				return
			}
		}
	}

	if len(client.credentials) > 0 {
		if err = client.storage.StoreAttributes(client.attributes); err != nil {
			return
		}
		if err = client.storage.StoreSecretKey(client.secretkey); err != nil {
			return
		}
	}

	if len(client.keyshareServers) > 0 {
		if err = client.storage.StoreKeyshareServers(client.keyshareServers); err != nil {
			return
		}
	}
	client.UnenrolledSchemeManagers = client.unenrolledSchemeManagers()

	if err = client.storage.StorePaillierKeys(client.paillierKeyCache); err != nil {
		return
	}
	if client.paillierKeyCache == nil {
		client.paillierKey(false) // trigger calculating a new one
	}
	return
}
<commit_msg>Remove hotfix update function that removed test scheme manager<commit_after>package irmaclient

import (
	"encoding/json"
	"encoding/xml"
	"html"
	"io/ioutil"
	"math/big"
	"time"

	"github.com/go-errors/errors"
	"github.com/mhe/gabi"
	"github.com/privacybydesign/irmago"
	"github.com/privacybydesign/irmago/internal/fs"
)

// This file contains the update mechanism for Client
// as well as updates themselves.

type update struct {
	When    irma.Timestamp
	Number  int
	Success bool
	Error   *string
}

var clientUpdates = []func(client *Client) error{
	// Convert old cardemu.xml Android storage to our own storage format
	func(client *Client) error {
		_, err := client.ParseAndroidStorage()
		return err
	},

	// Adding scheme manager index, signature and public key
	// Check the signatures of all scheme managers, if any is not ok,
	// copy the entire irma_configuration folder from assets
	nil, // made irrelevant by irma_configuration-autocopying

	// Rename config -> preferences
	func(client *Client) (err error) {
		exists, err := fs.PathExists(client.storage.path("config"))
		if !exists || err != nil {
			return
		}
		oldStruct := &struct {
			SendCrashReports bool
		}{}
		// Load old file, convert to new struct, and save
		err = client.storage.load(oldStruct, "config")
		if err != nil {
			return err
		}
		client.Preferences = Preferences{
			EnableCrashReporting: oldStruct.SendCrashReports,
		}
		return client.storage.StorePreferences(client.Preferences)
	},

	// Copy new irma_configuration out of assets
	nil, // made irrelevant by irma_configuration-autocopying

	// For each keyshare server, include in its struct the identifier of its scheme manager
	func(client *Client) (err error) {
		keyshareServers, err := client.storage.LoadKeyshareServers()
		if err != nil {
			return err
		}
		for smi, kss := range keyshareServers {
			kss.SchemeManagerIdentifier = smi
		}
		return client.storage.StoreKeyshareServers(keyshareServers)
	},

	// Remove the test scheme manager which was erroneously included in a production build
	nil, // No longer necessary, also broke many unit tests
}

// update performs any function from clientUpdates that has not
// already been executed in the past, keeping track of previously executed updates
// in the file at updatesFile.
func (client *Client) update() error {
	// Load and parse file containing info about already performed updates
	var err error
	if client.updates, err = client.storage.LoadUpdates(); err != nil {
		return err
	}

	// Perform all new updates
	for i := len(client.updates); i < len(clientUpdates); i++ {
		err = nil
		if clientUpdates[i] != nil {
			err = clientUpdates[i](client)
		}
		u := update{
			When:    irma.Timestamp(time.Now()),
			Number:  i,
			Success: err == nil,
		}
		if err != nil {
			str := err.Error()
			u.Error = &str
		}
		client.updates = append(client.updates, u)
		if err != nil {
			break
		}
	}

	storeErr := client.storage.StoreUpdates(client.updates)
	if storeErr != nil {
		return storeErr
	}
	return err
}

// ParseAndroidStorage parses an Android cardemu.xml shared preferences file
// from the old Android IRMA app, parsing its credentials into the current instance,
// and saving them to storage.
// CAREFUL: this method overwrites any existing secret keys and attributes on storage.
func (client *Client) ParseAndroidStorage() (present bool, err error) {
	if client.androidStoragePath == "" {
		return false, nil
	}

	cardemuXML := client.androidStoragePath + "/shared_prefs/cardemu.xml"
	present, err = fs.PathExists(cardemuXML)
	if err != nil || !present {
		return
	}
	present = true

	bytes, err := ioutil.ReadFile(cardemuXML)
	if err != nil {
		return
	}
	parsedxml := struct {
		Strings []struct {
			Name    string `xml:"name,attr"`
			Content string `xml:",chardata"`
		} `xml:"string"`
	}{}
	if err = xml.Unmarshal(bytes, &parsedxml); err != nil {
		return
	}

	parsedjson := make(map[string][]*struct {
		Signature    *gabi.CLSignature `json:"signature"`
		Pk           *gabi.PublicKey   `json:"-"`
		Attributes   []*big.Int        `json:"attributes"`
		SharedPoints []*big.Int        `json:"public_sks"`
	})
	client.keyshareServers = make(map[irma.SchemeManagerIdentifier]*keyshareServer)
	for _, xmltag := range parsedxml.Strings {
		if xmltag.Name == "credentials" {
			jsontag := html.UnescapeString(xmltag.Content)
			if err = json.Unmarshal([]byte(jsontag), &parsedjson); err != nil {
				return
			}
		}
		if xmltag.Name == "keyshare" {
			jsontag := html.UnescapeString(xmltag.Content)
			if err = json.Unmarshal([]byte(jsontag), &client.keyshareServers); err != nil {
				return
			}
		}
		if xmltag.Name == "KeyshareKeypairs" {
			jsontag := html.UnescapeString(xmltag.Content)
			keys := make([]*paillierPrivateKey, 0, 3)
			if err = json.Unmarshal([]byte(jsontag), &keys); err != nil {
				return
			}
			client.paillierKeyCache = keys[0]
		}
	}

	for _, list := range parsedjson {
		client.secretkey = &secretKey{Key: list[0].Attributes[0]}
		for _, oldcred := range list {
			gabicred := &gabi.Credential{
				Attributes: oldcred.Attributes,
				Signature:  oldcred.Signature,
			}
			if oldcred.SharedPoints != nil && len(oldcred.SharedPoints) > 0 {
				gabicred.Signature.KeyshareP = oldcred.SharedPoints[0]
			}
			var cred *credential
			if cred, err = newCredential(gabicred, client.Configuration); err != nil {
				return
			}
			if cred.CredentialType() == nil {
				err = errors.New("cannot add unknown credential type")
				return
			}

			if err = client.addCredential(cred, false); err != nil {
				return
			}
		}
	}

	if len(client.credentials) > 0 {
		if err = client.storage.StoreAttributes(client.attributes); err != nil {
			return
		}
		if err = client.storage.StoreSecretKey(client.secretkey); err != nil {
			return
		}
	}

	if len(client.keyshareServers) > 0 {
		if err = client.storage.StoreKeyshareServers(client.keyshareServers); err != nil {
			return
		}
	}
	client.UnenrolledSchemeManagers = client.unenrolledSchemeManagers()

	if err = client.storage.StorePaillierKeys(client.paillierKeyCache); err != nil {
		return
	}
	if client.paillierKeyCache == nil {
		client.paillierKey(false) // trigger calculating a new one
	}
	return
}
<|endoftext|>


<commit_before>// +build windows

package daemon

import (
	"fmt"
	"sort"

	"github.com/docker/docker/container"
	"github.com/docker/docker/volume"
)

// setupMounts configures the mount points for a container by appending each
// of the configured mounts on the container to the OCI mount structure
// which will ultimately be passed into the oci runtime during container creation.
// It also ensures each of the mounts are lexographically sorted.

// BUGBUG TODO Windows containerd. This would be much better if it returned
// an array of windowsoci mounts, not container mounts. Then no need to
// do multiple transitions.

func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, error) {
	var mnts []container.Mount
	for _, mount := range c.MountPoints { // type is volume.MountPoint
		if err := daemon.lazyInitializeVolume(c.ID, mount); err != nil {
			return nil, err
		}
		// If there is no source, take it from the volume path
		s := mount.Source
		if s == "" && mount.Volume != nil {
			s = mount.Volume.Path()
		}
		if s == "" {
			return nil, fmt.Errorf("No source for mount name '%s' driver %q destination '%s'", mount.Name, mount.Driver, mount.Destination)
		}
		mnts = append(mnts, container.Mount{
			Source:      s,
			Destination: mount.Destination,
			Writable:    mount.RW,
		})
	}

	sort.Sort(mounts(mnts))
	return mnts, nil
}

// setBindModeIfNull is platform specific processing which is a no-op on
// Windows.
func setBindModeIfNull(bind *volume.MountPoint) {
	return
}
<commit_msg>Add missing call to "VolumeDriver.Mount" during container start.<commit_after>// +build windows

package daemon

import (
	"sort"

	"github.com/docker/docker/container"
	"github.com/docker/docker/volume"
)

// setupMounts configures the mount points for a container by appending each
// of the configured mounts on the container to the OCI mount structure
// which will ultimately be passed into the oci runtime during container creation.
// It also ensures each of the mounts are lexographically sorted.

// BUGBUG TODO Windows containerd. This would be much better if it returned
// an array of windowsoci mounts, not container mounts. Then no need to
// do multiple transitions.

func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, error) {
	var mnts []container.Mount
	for _, mount := range c.MountPoints { // type is volume.MountPoint
		if err := daemon.lazyInitializeVolume(c.ID, mount); err != nil {
			return nil, err
		}
		s, err := mount.Setup(c.MountLabel, 0, 0)
		if err != nil {
			return nil, err
		}

		mnts = append(mnts, container.Mount{
			Source:      s,
			Destination: mount.Destination,
			Writable:    mount.RW,
		})
	}

	sort.Sort(mounts(mnts))
	return mnts, nil
}

// setBindModeIfNull is platform specific processing which is a no-op on
// Windows.
func setBindModeIfNull(bind *volume.MountPoint) {
	return
}
<|endoftext|>


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

// TODO(adg): packages at weekly/release
// TODO(adg): some means to register new packages

// +build appengine

package build

import (
	"bytes"
	"errors"
	"html/template"
	"net/http"
	"regexp"
	"sort"
	"strconv"
	"strings"

	"appengine"
	"appengine/datastore"
	"cache"
)

func init() {
	for _, d := range dashboards {
		http.HandleFunc(d.RelPath, uiHandler)
	}
}

// uiHandler draws the build status page.
func uiHandler(w http.ResponseWriter, r *http.Request) {
	d := dashboardForRequest(r)
	c := d.Context(appengine.NewContext(r))
	now := cache.Now(c)
	const key = "build-ui"

	page, _ := strconv.Atoi(r.FormValue("page"))
	if page < 0 {
		page = 0
	}
	repo := r.FormValue("repo")
	useCache := page == 0 && repo == ""

	// Used cached version of front page, if available.
	if useCache {
		var b []byte
		if cache.Get(r, now, key, &b) {
			w.Write(b)
			return
		}
	}

	pkg := &Package{} // empty package is the main repository
	if repo != "" {
		var err error
		pkg, err = GetPackage(c, repo)
		if err != nil {
			logErr(w, r, err)
			return
		}
	}
	commits, err := dashCommits(c, pkg, page)
	if err != nil {
		logErr(w, r, err)
		return
	}
	builders := commitBuilders(commits)

	var tipState *TagState
	if pkg.Kind == "" && page == 0 {
		// only show sub-repo state on first page of normal repo view
		tipState, err = TagStateByName(c, "tip")
		if err != nil {
			logErr(w, r, err)
			return
		}
	}

	p := &Pagination{}
	if len(commits) == commitsPerPage {
		p.Next = page + 1
	}
	if page > 0 {
		p.Prev = page - 1
		p.HasPrev = true
	}
	data := &uiTemplateData{d, pkg, commits, builders, tipState, p}

	var buf bytes.Buffer
	if err := uiTemplate.Execute(&buf, data); err != nil {
		logErr(w, r, err)
		return
	}

	// Cache the front page.
	if useCache {
		cache.Set(r, now, key, buf.Bytes())
	}

	buf.WriteTo(w)
}

type Pagination struct {
	Next, Prev int
	HasPrev    bool
}

// dashCommits gets a slice of the latest Commits to the current dashboard.
// If page > 0 it paginates by commitsPerPage.
func dashCommits(c appengine.Context, pkg *Package, page int) ([]*Commit, error) {
	q := datastore.NewQuery("Commit").
		Ancestor(pkg.Key(c)).
		Order("-Num").
		Limit(commitsPerPage).
		Offset(page * commitsPerPage)
	var commits []*Commit
	_, err := q.GetAll(c, &commits)
	return commits, err
}

// commitBuilders returns the names of the builders that provided
// Results for the provided commits.
func commitBuilders(commits []*Commit) []string {
	builders := make(map[string]bool)
	for _, commit := range commits {
		for _, r := range commit.Results() {
			builders[r.Builder] = true
		}
	}
	k := keys(builders)
	sort.Sort(builderOrder(k))
	return k
}

func keys(m map[string]bool) (s []string) {
	for k := range m {
		s = append(s, k)
	}
	sort.Strings(s)
	return
}

// builderOrder implements sort.Interface, sorting builder names
// ("darwin-amd64", etc) first by builderPriority and then alphabetically.
type builderOrder []string

func (s builderOrder) Len() int      { return len(s) }
func (s builderOrder) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s builderOrder) Less(i, j int) bool {
	pi, pj := builderPriority(s[i]), builderPriority(s[j])
	if pi == pj {
		return s[i] < s[j]
	}
	return pi < pj
}

func builderPriority(builder string) int {
	// Group race builders together.
	if isRace(builder) {
		return 1
	}
	// Put supported OSes first.
	if supportedOS[builderOS(builder)] {
		return 0
	}
	// Everyone else.
	return 2
}

func isRace(s string) bool {
	return strings.Contains(s, "-race-") || strings.HasSuffix(s, "-race")
}

// Operating systems that should appear first on the dashboard.
var supportedOS = map[string]bool{
	"darwin":  true,
	"freebsd": true,
	"linux":   true,
	"windows": true,
}

// TagState represents the state of all Packages at a Tag.
type TagState struct {
	Tag      *Commit
	Packages []*PackageState
}

// PackageState represents the state of a Package at a Tag.
type PackageState struct {
	Package *Package
	Commit  *Commit
}

// TagStateByName fetches the results for all Go subrepos at the specified Tag.
func TagStateByName(c appengine.Context, name string) (*TagState, error) {
	tag, err := GetTag(c, name)
	if err != nil {
		return nil, err
	}
	pkgs, err := Packages(c, "subrepo")
	if err != nil {
		return nil, err
	}
	var st TagState
	for _, pkg := range pkgs {
		com, err := pkg.LastCommit(c)
		if err != nil {
			c.Warningf("%v: no Commit found: %v", pkg, err)
			continue
		}
		st.Packages = append(st.Packages, &PackageState{pkg, com})
	}
	st.Tag, err = tag.Commit(c)
	if err != nil {
		return nil, err
	}
	return &st, nil
}

type uiTemplateData struct {
	Dashboard  *Dashboard
	Package    *Package
	Commits    []*Commit
	Builders   []string
	TipState   *TagState
	Pagination *Pagination
}

var uiTemplate = template.Must(
	template.New("ui.html").Funcs(tmplFuncs).ParseFiles("build/ui.html"),
)

var tmplFuncs = template.FuncMap{
	"builderSubheading": builderSubheading,
	"builderTitle":      builderTitle,
	"builderSpans":      builderSpans,
	"buildDashboards":   buildDashboards,
	"repoURL":           repoURL,
	"shortDesc":         shortDesc,
	"shortHash":         shortHash,
	"shortUser":         shortUser,
	"tail":              tail,
}

func splitDash(s string) (string, string) {
	i := strings.Index(s, "-")
	if i >= 0 {
		return s[:i], s[i+1:]
	}
	return s, ""
}

// builderOS returns the os tag for a builder string
func builderOS(s string) string {
	os, _ := splitDash(s)
	return os
}

// builderOSOrRace returns the builder OS or, if it is a race builder, "race".
func builderOSOrRace(s string) string {
	if isRace(s) {
		return "race"
	}
	return builderOS(s)
}

// builderArch returns the arch tag for a builder string
func builderArch(s string) string {
	_, arch := splitDash(s)
	arch, _ = splitDash(arch) // chop third part
	return arch
}

// builderSubheading returns a short arch tag for a builder string
// or, if it is a race builder, the builder OS.
func builderSubheading(s string) string {
	if isRace(s) {
		return builderOS(s)
	}
	arch := builderArch(s)
	switch arch {
	case "amd64":
		return "x64"
	}
	return arch
}

// builderArchChar returns the architecture letter for a builder string
func builderArchChar(s string) string {
	arch := builderArch(s)
	switch arch {
	case "386":
		return "8"
	case "amd64":
		return "6"
	case "arm":
		return "5"
	}
	return arch
}

type builderSpan struct {
	N  int
	OS string
}

// builderSpans creates a list of tags showing
// the builder's operating system names, spanning
// the appropriate number of columns.
func builderSpans(s []string) []builderSpan {
	var sp []builderSpan
	for len(s) > 0 {
		i := 1
		os := builderOSOrRace(s[0])
		for i < len(s) && builderOSOrRace(s[i]) == os {
			i++
		}
		sp = append(sp, builderSpan{i, os})
		s = s[i:]
	}
	return sp
}

// builderTitle formats "linux-amd64-foo" as "linux amd64 foo".
func builderTitle(s string) string {
	return strings.Replace(s, "-", " ", -1)
}

// buildDashboards returns the known public dashboards.
func buildDashboards() []*Dashboard {
	return dashboards
}

// shortDesc returns the first line of a description.
func shortDesc(desc string) string {
	if i := strings.Index(desc, "\n"); i != -1 {
		desc = desc[:i]
	}
	return desc
}

// shortHash returns a short version of a hash.
func shortHash(hash string) string {
	if len(hash) > 12 {
		hash = hash[:12]
	}
	return hash
}

// shortUser returns a shortened version of a user string.
func shortUser(user string) string {
	if i, j := strings.Index(user, "<"), strings.Index(user, ">"); 0 <= i && i < j {
		user = user[i+1 : j]
	}
	if i := strings.Index(user, "@"); i >= 0 {
		return user[:i]
	}
	return user
}

// repoRe matches Google Code repositories and subrepositories (without paths).
var repoRe = regexp.MustCompile(`^code\.google\.com/p/([a-z0-9\-]+)(\.[a-z0-9\-]+)?$`)

// repoURL returns the URL of a change at a Google Code repository or subrepo.
func repoURL(dashboard, hash, packagePath string) (string, error) {
	if packagePath == "" {
		if dashboard == "Gccgo" {
			return "https://code.google.com/p/gofrontend/source/detail?r=" + hash, nil
		}
		return "https://code.google.com/p/go/source/detail?r=" + hash, nil
	}
	m := repoRe.FindStringSubmatch(packagePath)
	if m == nil {
		return "", errors.New("unrecognized package: " + packagePath)
	}
	url := "https://code.google.com/p/" + m[1] + "/source/detail?r=" + hash
	if len(m) > 2 {
		url += "&repo=" + m[2][1:]
	}
	return url, nil
}

// tail returns the trailing n lines of s.
func tail(n int, s string) string {
	lines := strings.Split(s, "\n")
	if len(lines) < n {
		return s
	}
	return strings.Join(lines[len(lines)-n:], "\n")
}
<commit_msg>go.tools/dashboard/app: tweak build column order<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// TODO(adg): packages at weekly/release
// TODO(adg): some means to register new packages

// +build appengine

package build

import (
	"bytes"
	"errors"
	"html/template"
	"net/http"
	"regexp"
	"sort"
	"strconv"
	"strings"

	"cache"

	"appengine"
	"appengine/datastore"
)

func init() {
	for _, d := range dashboards {
		http.HandleFunc(d.RelPath, uiHandler)
	}
}

// uiHandler draws the build status page.
func uiHandler(w http.ResponseWriter, r *http.Request) {
	d := dashboardForRequest(r)
	c := d.Context(appengine.NewContext(r))
	now := cache.Now(c)
	const key = "build-ui"

	page, _ := strconv.Atoi(r.FormValue("page"))
	if page < 0 {
		page = 0
	}
	repo := r.FormValue("repo")
	useCache := page == 0 && repo == ""

	// Used cached version of front page, if available.
	if useCache {
		var b []byte
		if cache.Get(r, now, key, &b) {
			w.Write(b)
			return
		}
	}

	pkg := &Package{} // empty package is the main repository
	if repo != "" {
		var err error
		pkg, err = GetPackage(c, repo)
		if err != nil {
			logErr(w, r, err)
			return
		}
	}
	commits, err := dashCommits(c, pkg, page)
	if err != nil {
		logErr(w, r, err)
		return
	}
	builders := commitBuilders(commits)

	var tipState *TagState
	if pkg.Kind == "" && page == 0 {
		// only show sub-repo state on first page of normal repo view
		tipState, err = TagStateByName(c, "tip")
		if err != nil {
			logErr(w, r, err)
			return
		}
	}

	p := &Pagination{}
	if len(commits) == commitsPerPage {
		p.Next = page + 1
	}
	if page > 0 {
		p.Prev = page - 1
		p.HasPrev = true
	}
	data := &uiTemplateData{d, pkg, commits, builders, tipState, p}

	var buf bytes.Buffer
	if err := uiTemplate.Execute(&buf, data); err != nil {
		logErr(w, r, err)
		return
	}

	// Cache the front page.
	if useCache {
		cache.Set(r, now, key, buf.Bytes())
	}

	buf.WriteTo(w)
}

type Pagination struct {
	Next, Prev int
	HasPrev    bool
}

// dashCommits gets a slice of the latest Commits to the current dashboard.
// If page > 0 it paginates by commitsPerPage.
func dashCommits(c appengine.Context, pkg *Package, page int) ([]*Commit, error) {
	q := datastore.NewQuery("Commit").
		Ancestor(pkg.Key(c)).
		Order("-Num").
		Limit(commitsPerPage).
		Offset(page * commitsPerPage)
	var commits []*Commit
	_, err := q.GetAll(c, &commits)
	return commits, err
}

// commitBuilders returns the names of the builders that provided
// Results for the provided commits.
func commitBuilders(commits []*Commit) []string {
	builders := make(map[string]bool)
	for _, commit := range commits {
		for _, r := range commit.Results() {
			builders[r.Builder] = true
		}
	}
	k := keys(builders)
	sort.Sort(builderOrder(k))
	return k
}

func keys(m map[string]bool) (s []string) {
	for k := range m {
		s = append(s, k)
	}
	sort.Strings(s)
	return
}

// builderOrder implements sort.Interface, sorting builder names
// ("darwin-amd64", etc) first by builderPriority and then alphabetically.
type builderOrder []string

func (s builderOrder) Len() int      { return len(s) }
func (s builderOrder) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s builderOrder) Less(i, j int) bool {
	pi, pj := builderPriority(s[i]), builderPriority(s[j])
	if pi == pj {
		return s[i] < s[j]
	}
	return pi < pj
}

func builderPriority(builder string) int {
	// Group race builders together.
	if isRace(builder) {
		return 1
	}
	// If the OS has a specified priority, use it.
	if p, ok := osPriority[builderOS(builder)]; ok {
		return p
	}
	// The rest.
	return 10
}

func isRace(s string) bool {
	return strings.Contains(s, "-race-") || strings.HasSuffix(s, "-race")
}

// Priorities for specific operating systems.
var osPriority = map[string]int{
	"darwin":  0,
	"freebsd": 0,
	"linux":   0,
	"windows": 0,
	// race == 1
	"openbsd":   2,
	"netbsd":    3,
	"dragonfly": 4,
}

// TagState represents the state of all Packages at a Tag.
type TagState struct {
	Tag      *Commit
	Packages []*PackageState
}

// PackageState represents the state of a Package at a Tag.
type PackageState struct {
	Package *Package
	Commit  *Commit
}

// TagStateByName fetches the results for all Go subrepos at the specified Tag.
func TagStateByName(c appengine.Context, name string) (*TagState, error) {
	tag, err := GetTag(c, name)
	if err != nil {
		return nil, err
	}
	pkgs, err := Packages(c, "subrepo")
	if err != nil {
		return nil, err
	}
	var st TagState
	for _, pkg := range pkgs {
		com, err := pkg.LastCommit(c)
		if err != nil {
			c.Warningf("%v: no Commit found: %v", pkg, err)
			continue
		}
		st.Packages = append(st.Packages, &PackageState{pkg, com})
	}
	st.Tag, err = tag.Commit(c)
	if err != nil {
		return nil, err
	}
	return &st, nil
}

type uiTemplateData struct {
	Dashboard  *Dashboard
	Package    *Package
	Commits    []*Commit
	Builders   []string
	TipState   *TagState
	Pagination *Pagination
}

var uiTemplate = template.Must(
	template.New("ui.html").Funcs(tmplFuncs).ParseFiles("build/ui.html"),
)

var tmplFuncs = template.FuncMap{
	"builderSubheading": builderSubheading,
	"builderTitle":      builderTitle,
	"builderSpans":      builderSpans,
	"buildDashboards":   buildDashboards,
	"repoURL":           repoURL,
	"shortDesc":         shortDesc,
	"shortHash":         shortHash,
	"shortUser":         shortUser,
	"tail":              tail,
}

func splitDash(s string) (string, string) {
	i := strings.Index(s, "-")
	if i >= 0 {
		return s[:i], s[i+1:]
	}
	return s, ""
}

// builderOS returns the os tag for a builder string
func builderOS(s string) string {
	os, _ := splitDash(s)
	return os
}

// builderOSOrRace returns the builder OS or, if it is a race builder, "race".
func builderOSOrRace(s string) string {
	if isRace(s) {
		return "race"
	}
	return builderOS(s)
}

// builderArch returns the arch tag for a builder string
func builderArch(s string) string {
	_, arch := splitDash(s)
	arch, _ = splitDash(arch) // chop third part
	return arch
}

// builderSubheading returns a short arch tag for a builder string
// or, if it is a race builder, the builder OS.
func builderSubheading(s string) string {
	if isRace(s) {
		return builderOS(s)
	}
	arch := builderArch(s)
	switch arch {
	case "amd64":
		return "x64"
	}
	return arch
}

// builderArchChar returns the architecture letter for a builder string
func builderArchChar(s string) string {
	arch := builderArch(s)
	switch arch {
	case "386":
		return "8"
	case "amd64":
		return "6"
	case "arm":
		return "5"
	}
	return arch
}

type builderSpan struct {
	N  int
	OS string
}

// builderSpans creates a list of tags showing
// the builder's operating system names, spanning
// the appropriate number of columns.
func builderSpans(s []string) []builderSpan {
	var sp []builderSpan
	for len(s) > 0 {
		i := 1
		os := builderOSOrRace(s[0])
		for i < len(s) && builderOSOrRace(s[i]) == os {
			i++
		}
		sp = append(sp, builderSpan{i, os})
		s = s[i:]
	}
	return sp
}

// builderTitle formats "linux-amd64-foo" as "linux amd64 foo".
func builderTitle(s string) string {
	return strings.Replace(s, "-", " ", -1)
}

// buildDashboards returns the known public dashboards.
func buildDashboards() []*Dashboard {
	return dashboards
}

// shortDesc returns the first line of a description.
func shortDesc(desc string) string {
	if i := strings.Index(desc, "\n"); i != -1 {
		desc = desc[:i]
	}
	return desc
}

// shortHash returns a short version of a hash.
func shortHash(hash string) string {
	if len(hash) > 12 {
		hash = hash[:12]
	}
	return hash
}

// shortUser returns a shortened version of a user string.
func shortUser(user string) string {
	if i, j := strings.Index(user, "<"), strings.Index(user, ">"); 0 <= i && i < j {
		user = user[i+1 : j]
	}
	if i := strings.Index(user, "@"); i >= 0 {
		return user[:i]
	}
	return user
}

// repoRe matches Google Code repositories and subrepositories (without paths).
var repoRe = regexp.MustCompile(`^code\.google\.com/p/([a-z0-9\-]+)(\.[a-z0-9\-]+)?$`)

// repoURL returns the URL of a change at a Google Code repository or subrepo.
func repoURL(dashboard, hash, packagePath string) (string, error) {
	if packagePath == "" {
		if dashboard == "Gccgo" {
			return "https://code.google.com/p/gofrontend/source/detail?r=" + hash, nil
		}
		return "https://code.google.com/p/go/source/detail?r=" + hash, nil
	}
	m := repoRe.FindStringSubmatch(packagePath)
	if m == nil {
		return "", errors.New("unrecognized package: " + packagePath)
	}
	url := "https://code.google.com/p/" + m[1] + "/source/detail?r=" + hash
	if len(m) > 2 {
		url += "&repo=" + m[2][1:]
	}
	return url, nil
}

// tail returns the trailing n lines of s.
func tail(n int, s string) string {
	lines := strings.Split(s, "\n")
	if len(lines) < n {
		return s
	}
	return strings.Join(lines[len(lines)-n:], "\n")
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/emgee/go-xmpp/src/xmpp"
	"log"
)

type Command struct {
	cmdName string
}

func main() {
	jid, _ := xmpp.ParseJID("jabmud.localhost")
	stream, _ := xmpp.NewStream("localhost:5275", nil)
	X, _ := xmpp.NewComponentXMPP(stream, jid, "secret")
	log.Printf("created component JID %v at %v\n", jid, X)

	for i := range X.In {
		switch v := i.(type) {
		case error:
			log.Printf("error: %v\n", v)
		case *xmpp.Message:
			log.Printf("msg: %s says %s\n", v.From, v.Body)
			// for fun, send a response
			X.Out <- xmpp.Message{Body: "hi!", To: v.From, From: v.To}
		case *xmpp.Iq:
			log.Printf("iq: ", v.Payload)
			/* doesn't work
			foo := Command {}
			v.PayloadDecode(foo)
			log.Printf("decoded: %v", foo)
			*/
		default:
			log.Printf("%T: %v\n", v, v)
		}
	}
}
<commit_msg>opens webserver on 8888<commit_after>package main

import (
	"github.com/emgee/go-xmpp/src/xmpp"
	"log"
	"net/http"
//	"github.com/braintree/manners"
	"fmt"
)

type Command struct {
	cmdName string
}

func main() {
	go connectHttpServer()
	connectComponent()
}

func connectHttpServer() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello, this is %s", r.URL.Path[1:])
	})
	http.ListenAndServe(":8888", nil)
}

func connectComponent() {
	// connect as component
	jid, _ := xmpp.ParseJID("jabmud.localhost")
	stream, _ := xmpp.NewStream("localhost:5275", nil)
	X, _ := xmpp.NewComponentXMPP(stream, jid, "secret")
	log.Printf("created component JID %v at %v\n", jid, X)

	for i := range X.In {
		switch v := i.(type) {
		case error:
			log.Printf("error: %v\n", v)
		case *xmpp.Message:
			log.Printf("msg: %s says %s\n", v.From, v.Body)
			// for fun, send a response
			X.Out <- xmpp.Message{Body: "hi!", To: v.From, From: v.To}
		case *xmpp.Iq:
			log.Printf("iq: ", v.Payload)
		/* doesn't work
		foo := Command {}
		v.PayloadDecode(foo)
		log.Printf("decoded: %v", foo)
		*/
		default:
			log.Printf("%T: %v\n", v, v)
		}
	}
}
<|endoftext|>