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

// Read static host/IP entries from /etc/hosts.

package net

import (
	"os"
	"sync"
)

const cacheMaxAge = int64(300) // 5 minutes.

// hostsPath points to the file with static IP/address entries.
var hostsPath = "/etc/hosts"

// Simple cache.
var hosts struct {
	sync.Mutex
	byName map[string][]string
	byAddr map[string][]string
	time   int64
	path   string
}

func readHosts() {
	now, _, _ := os.Time()
	hp := hostsPath
	if len(hosts.byName) == 0 || hosts.time+cacheMaxAge <= now || hosts.path != hp {
		hs := make(map[string][]string)
		is := make(map[string][]string)
		var file *file
		if file, _ = open(hp); file == nil {
			return
		}
		for line, ok := file.readLine(); ok; line, ok = file.readLine() {
			if i := byteIndex(line, '#'); i >= 0 {
				// Discard comments.
				line = line[0:i]
			}
			f := getFields(line)
			if len(f) < 2 || ParseIP(f[0]) == nil {
				continue
			}
			for i := 1; i < len(f); i++ {
				h := f[i]
				hs[h] = append(hs[h], f[0])
				is[f[0]] = append(is[f[0]], h)
			}
		}
		// Update the data cache.
		hosts.time, _, _ = os.Time()
		hosts.path = hp
		hosts.byName = hs
		hosts.byAddr = is
		file.close()
	}
}

// lookupStaticHosts looks up the addresses for the given host from /etc/hosts.
func lookupStaticHost(host string) []string {
	hosts.Lock()
	defer hosts.Unlock()
	readHosts()
	if len(hosts.byName) != 0 {
		if ips, ok := hosts.byName[host]; ok {
			return ips
		}
	}
	return nil
}

// rlookupStaticHosts looks up the hosts for the given address from /etc/hosts.
func lookupStaticAddr(addr string) []string {
	hosts.Lock()
	defer hosts.Unlock()
	readHosts()
	if len(hosts.byAddr) != 0 {
		if hosts, ok := hosts.byAddr[addr]; ok {
			return hosts
		}
	}
	return nil
}
<commit_msg>net: fix typo<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Read static host/IP entries from /etc/hosts.

package net

import (
	"os"
	"sync"
)

const cacheMaxAge = int64(300) // 5 minutes.

// hostsPath points to the file with static IP/address entries.
var hostsPath = "/etc/hosts"

// Simple cache.
var hosts struct {
	sync.Mutex
	byName map[string][]string
	byAddr map[string][]string
	time   int64
	path   string
}

func readHosts() {
	now, _, _ := os.Time()
	hp := hostsPath
	if len(hosts.byName) == 0 || hosts.time+cacheMaxAge <= now || hosts.path != hp {
		hs := make(map[string][]string)
		is := make(map[string][]string)
		var file *file
		if file, _ = open(hp); file == nil {
			return
		}
		for line, ok := file.readLine(); ok; line, ok = file.readLine() {
			if i := byteIndex(line, '#'); i >= 0 {
				// Discard comments.
				line = line[0:i]
			}
			f := getFields(line)
			if len(f) < 2 || ParseIP(f[0]) == nil {
				continue
			}
			for i := 1; i < len(f); i++ {
				h := f[i]
				hs[h] = append(hs[h], f[0])
				is[f[0]] = append(is[f[0]], h)
			}
		}
		// Update the data cache.
		hosts.time, _, _ = os.Time()
		hosts.path = hp
		hosts.byName = hs
		hosts.byAddr = is
		file.close()
	}
}

// lookupStaticHost looks up the addresses for the given host from /etc/hosts.
func lookupStaticHost(host string) []string {
	hosts.Lock()
	defer hosts.Unlock()
	readHosts()
	if len(hosts.byName) != 0 {
		if ips, ok := hosts.byName[host]; ok {
			return ips
		}
	}
	return nil
}

// lookupStaticAddr looks up the hosts for the given address from /etc/hosts.
func lookupStaticAddr(addr string) []string {
	hosts.Lock()
	defer hosts.Unlock()
	readHosts()
	if len(hosts.byAddr) != 0 {
		if hosts, ok := hosts.byAddr[addr]; ok {
			return hosts
		}
	}
	return nil
}
<|endoftext|>


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

package rpc

/*
	Some HTML presented at http://machine:port/debug/rpc
	Lists services, their methods, and some statistics, still rudimentary.
*/

import (
	"fmt"
	"http"
	"sort"
	"template"
)

const debugText = `

	<title>Services</title>
	{.repeated section @}

	Service {Name}

		Method	Calls
		{.repeated section Method}

			{Name}({Type.ArgType}, {Type.ReplyType}) os.Error
			{Type.NumCalls}

		{.end}

	{.end}

	`

var debug = template.MustParse(debugText, nil)

type debugMethod struct {
	Type *methodType
	Name string
}

type methodArray []debugMethod

type debugService struct {
	Service *service
	Name    string
	Method  methodArray
}

type serviceArray []debugService

func (s serviceArray) Len() int           { return len(s) }
func (s serviceArray) Less(i, j int) bool { return s[i].Name < s[j].Name }
func (s serviceArray) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }

func (m methodArray) Len() int           { return len(m) }
func (m methodArray) Less(i, j int) bool { return m[i].Name < m[j].Name }
func (m methodArray) Swap(i, j int)      { m[i], m[j] = m[j], m[i] }

type debugHTTP struct {
	*Server
}

// Runs at /debug/rpc
func (server debugHTTP) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	// Build a sorted version of the data.
	var services = make(serviceArray, len(server.serviceMap))
	i := 0
	server.Lock()
	for sname, service := range server.serviceMap {
		services[i] = debugService{service, sname, make(methodArray, len(service.method))}
		j := 0
		for mname, method := range service.method {
			services[i].Method[j] = debugMethod{method, mname}
			j++
		}
		sort.Sort(services[i].Method)
		i++
	}
	server.Unlock()
	sort.Sort(services)
	err := debug.Execute(w, services)
	if err != nil {
		fmt.Fprintln(w, "rpc: error executing template:", err.String())
	}
}
<commit_msg>rpc: convert /debug/rpc handler to exp/template<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package rpc

/*
	Some HTML presented at http://machine:port/debug/rpc
	Lists services, their methods, and some statistics, still rudimentary.
*/

import (
	"exp/template"
	"fmt"
	"http"
	"sort"
)

const debugText = `

	<title>Services</title>
	{{range .}}

	Service {{.Name}}

		Method	Calls
		{{range .Method}}

			{{.Name}}({{.Type.ArgType}}, {{.Type.ReplyType}}) os.Error
			{{.Type.NumCalls}}

		{{end}}

	{{end}}

	`

var debug = template.New("RPC debug").MustParse(debugText)

type debugMethod struct {
	Type *methodType
	Name string
}

type methodArray []debugMethod

type debugService struct {
	Service *service
	Name    string
	Method  methodArray
}

type serviceArray []debugService

func (s serviceArray) Len() int           { return len(s) }
func (s serviceArray) Less(i, j int) bool { return s[i].Name < s[j].Name }
func (s serviceArray) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }

func (m methodArray) Len() int           { return len(m) }
func (m methodArray) Less(i, j int) bool { return m[i].Name < m[j].Name }
func (m methodArray) Swap(i, j int)      { m[i], m[j] = m[j], m[i] }

type debugHTTP struct {
	*Server
}

// Runs at /debug/rpc
func (server debugHTTP) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	// Build a sorted version of the data.
	var services = make(serviceArray, len(server.serviceMap))
	i := 0
	server.Lock()
	for sname, service := range server.serviceMap {
		services[i] = debugService{service, sname, make(methodArray, len(service.method))}
		j := 0
		for mname, method := range service.method {
			services[i].Method[j] = debugMethod{method, mname}
			j++
		}
		sort.Sort(services[i].Method)
		i++
	}
	server.Unlock()
	sort.Sort(services)
	err := debug.Execute(w, services)
	if err != nil {
		fmt.Fprintln(w, "rpc: error executing template:", err.String())
	}
}
<|endoftext|>


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

package que

// Thanks to RhodiumToad in #postgresql for help with the job lock CTE.
const (
	sqlLockJob = `
WITH RECURSIVE job AS (
  SELECT (j).*, pg_try_advisory_lock((j).job_id) AS locked
  FROM (
    SELECT j
    FROM que_jobs AS j
    WHERE queue = $1::text
    AND run_at <= now()
    ORDER BY priority, run_at, job_id
    LIMIT 1
  ) AS t1
  UNION ALL (
    SELECT (j).*, pg_try_advisory_lock((j).job_id) AS locked
    FROM (
      SELECT (
        SELECT j
        FROM que_jobs AS j
        WHERE queue = $1::text
        AND run_at <= now()
        AND (priority, run_at, job_id) > (job.priority, job.run_at, job.job_id)
        ORDER BY priority, run_at, job_id
        LIMIT 1
      ) AS j
      FROM job
      WHERE NOT job.locked
      LIMIT 1
    ) AS t1
  )
)
SELECT queue, priority, run_at, job_id, job_class, args, error_count
FROM job
WHERE locked
LIMIT 1
`

	sqlUnlockJob = `
SELECT pg_advisory_unlock($1)
`

	sqlCheckJob = `
SELECT true AS exists
FROM   que_jobs
WHERE  queue    = $1::text
AND    priority = $2::smallint
AND    run_at   = $3::timestamptz
AND    job_id   = $4::bigint
`

	sqlSetError = `
UPDATE que_jobs
SET error_count = $1::integer,
    run_at      = now() + $2::bigint * '1 second'::interval,
    last_error  = $3::text
WHERE queue     = $4::text
AND   priority  = $5::smallint
AND   run_at    = $6::timestamptz
AND   job_id    = $7::bigint
`

	sqlInsertJob = `
INSERT INTO que_jobs
(queue, priority, run_at, job_class, args)
VALUES
(coalesce($1::text, ''::text), coalesce($2::smallint, 100::smallint), coalesce($3::timestamptz, now()::timestamptz), $4::text, coalesce($5::json, '[]'::json))
`

	sqlDeleteJob = `
DELETE FROM que_jobs
WHERE queue    = $1::text
AND   priority = $2::smallint
AND   run_at   = $3::timestamptz
AND   job_id   = $4::bigint
`

	sqlJobStats = `
SELECT queue,
       job_class,
       count(*)                    AS count,
       count(locks.job_id)         AS count_working,
       sum((error_count > 0)::int) AS count_errored,
       max(error_count)            AS highest_error_count,
       min(run_at)                 AS oldest_run_at
FROM que_jobs
LEFT JOIN (
  SELECT (classid::bigint << 32) + objid::bigint AS job_id
  FROM pg_locks
  WHERE locktype = 'advisory'
) locks USING (job_id)
GROUP BY queue, job_class
ORDER BY count(*) DESC
`

	sqlWorkerStates = `
SELECT que_jobs.*,
       pg.pid          AS pg_backend_pid,
       pg.state        AS pg_state,
       pg.state_change AS pg_state_changed_at,
       pg.query        AS pg_last_query,
       pg.query_start  AS pg_last_query_started_at,
       pg.xact_start   AS pg_transaction_started_at,
       pg.waiting      AS pg_waiting_on_lock
FROM que_jobs
JOIN (
  SELECT (classid::bigint << 32) + objid::bigint AS job_id, pg_stat_activity.*
  FROM pg_locks
  JOIN pg_stat_activity USING (pid)
  WHERE locktype = 'advisory'
) pg USING (job_id)
`
)
<commit_msg>small tweaks to job lock query to match que (#11)<commit_after>// Copyright (c) 2013 Chris Hanks
//
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package que

// Thanks to RhodiumToad in #postgresql for help with the job lock CTE.
const (
	sqlLockJob = `
WITH RECURSIVE jobs AS (
  SELECT (j).*, pg_try_advisory_lock((j).job_id) AS locked
  FROM (
    SELECT j
    FROM que_jobs AS j
    WHERE queue = $1::text
    AND run_at <= now()
    ORDER BY priority, run_at, job_id
    LIMIT 1
  ) AS t1
  UNION ALL (
    SELECT (j).*, pg_try_advisory_lock((j).job_id) AS locked
    FROM (
      SELECT (
        SELECT j
        FROM que_jobs AS j
        WHERE queue = $1::text
        AND run_at <= now()
        AND (priority, run_at, job_id) > (jobs.priority, jobs.run_at, jobs.job_id)
        ORDER BY priority, run_at, job_id
        LIMIT 1
      ) AS j
      FROM jobs
      WHERE jobs.job_id IS NOT NULL
      LIMIT 1
    ) AS t1
  )
)
SELECT queue, priority, run_at, job_id, job_class, args, error_count
FROM jobs
WHERE locked
LIMIT 1
`

	sqlUnlockJob = `
SELECT pg_advisory_unlock($1)
`

	sqlCheckJob = `
SELECT true AS exists
FROM   que_jobs
WHERE  queue    = $1::text
AND    priority = $2::smallint
AND    run_at   = $3::timestamptz
AND    job_id   = $4::bigint
`

	sqlSetError = `
UPDATE que_jobs
SET error_count = $1::integer,
    run_at      = now() + $2::bigint * '1 second'::interval,
    last_error  = $3::text
WHERE queue     = $4::text
AND   priority  = $5::smallint
AND   run_at    = $6::timestamptz
AND   job_id    = $7::bigint
`

	sqlInsertJob = `
INSERT INTO que_jobs
(queue, priority, run_at, job_class, args)
VALUES
(coalesce($1::text, ''::text), coalesce($2::smallint, 100::smallint), coalesce($3::timestamptz, now()::timestamptz), $4::text, coalesce($5::json, '[]'::json))
`

	sqlDeleteJob = `
DELETE FROM que_jobs
WHERE queue    = $1::text
AND   priority = $2::smallint
AND   run_at   = $3::timestamptz
AND   job_id   = $4::bigint
`

	sqlJobStats = `
SELECT queue,
       job_class,
       count(*)                    AS count,
       count(locks.job_id)         AS count_working,
       sum((error_count > 0)::int) AS count_errored,
       max(error_count)            AS highest_error_count,
       min(run_at)                 AS oldest_run_at
FROM que_jobs
LEFT JOIN (
  SELECT (classid::bigint << 32) + objid::bigint AS job_id
  FROM pg_locks
  WHERE locktype = 'advisory'
) locks USING (job_id)
GROUP BY queue, job_class
ORDER BY count(*) DESC
`

	sqlWorkerStates = `
SELECT que_jobs.*,
       pg.pid          AS pg_backend_pid,
       pg.state        AS pg_state,
       pg.state_change AS pg_state_changed_at,
       pg.query        AS pg_last_query,
       pg.query_start  AS pg_last_query_started_at,
       pg.xact_start   AS pg_transaction_started_at,
       pg.waiting      AS pg_waiting_on_lock
FROM que_jobs
JOIN (
  SELECT (classid::bigint << 32) + objid::bigint AS job_id, pg_stat_activity.*
  FROM pg_locks
  JOIN pg_stat_activity USING (pid)
  WHERE locktype = 'advisory'
) pg USING (job_id)
`
)
<|endoftext|>


<commit_before>// Copyright (c) 2015-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package wallet

import (
	"errors"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/btcsuite/btcd/chaincfg"
	"github.com/btcsuite/btcwallet/internal/prompt"
	"github.com/btcsuite/btcwallet/waddrmgr"
	"github.com/btcsuite/btcwallet/walletdb"
)

const (
	// WalletDBName specified the database filename for the wallet.
	WalletDBName = "wallet.db"

	// DefaultDBTimeout is the default timeout value when opening the wallet
	// database.
	DefaultDBTimeout = 60 * time.Second
)

var (
	// ErrLoaded describes the error condition of attempting to load or
	// create a wallet when the loader has already done so.
	ErrLoaded = errors.New("wallet already loaded")

	// ErrNotLoaded describes the error condition of attempting to close a
	// loaded wallet when a wallet has not been loaded.
	ErrNotLoaded = errors.New("wallet is not loaded")

	// ErrExists describes the error condition of attempting to create a new
	// wallet when one exists already.
	ErrExists = errors.New("wallet already exists")
)

// Loader implements the creating of new and opening of existing wallets, while
// providing a callback system for other subsystems to handle the loading of a
// wallet.  This is primarily intended for use by the RPC servers, to enable
// methods and services which require the wallet when the wallet is loaded by
// another subsystem.
//
// Loader is safe for concurrent access.
type Loader struct {
	callbacks      []func(*Wallet)
	chainParams    *chaincfg.Params
	dbDirPath      string
	noFreelistSync bool
	timeout        time.Duration
	recoveryWindow uint32
	wallet         *Wallet
	db             walletdb.DB
	mu             sync.Mutex
}

// NewLoader constructs a Loader with an optional recovery window. If the
// recovery window is non-zero, the wallet will attempt to recovery addresses
// starting from the last SyncedTo height.
func NewLoader(chainParams *chaincfg.Params, dbDirPath string,
	noFreelistSync bool, timeout time.Duration,
	recoveryWindow uint32) *Loader {

	return &Loader{
		chainParams:    chainParams,
		dbDirPath:      dbDirPath,
		noFreelistSync: noFreelistSync,
		timeout:        timeout,
		recoveryWindow: recoveryWindow,
	}
}

// onLoaded executes each added callback and prevents loader from loading any
// additional wallets.  Requires mutex to be locked.
func (l *Loader) onLoaded(w *Wallet, db walletdb.DB) {
	for _, fn := range l.callbacks {
		fn(w)
	}

	l.wallet = w
	l.db = db
	l.callbacks = nil // not needed anymore
}

// RunAfterLoad adds a function to be executed when the loader creates or opens
// a wallet.  Functions are executed in a single goroutine in the order they are
// added.
func (l *Loader) RunAfterLoad(fn func(*Wallet)) {
	l.mu.Lock()
	if l.wallet != nil {
		w := l.wallet
		l.mu.Unlock()
		fn(w)
	} else {
		l.callbacks = append(l.callbacks, fn)
		l.mu.Unlock()
	}
}

// CreateNewWallet creates a new wallet using the provided public and private
// passphrases.  The seed is optional.  If non-nil, addresses are derived from
// this seed.  If nil, a secure random seed is generated.
func (l *Loader) CreateNewWallet(pubPassphrase, privPassphrase, seed []byte,
	bday time.Time) (*Wallet, error) {

	return l.createNewWallet(
		pubPassphrase, privPassphrase, seed, bday, false,
	)
}

// CreateNewWatchingOnlyWallet creates a new wallet using the provided
// public passphrase.  No seed or private passphrase may be provided
// since the wallet is watching-only.
func (l *Loader) CreateNewWatchingOnlyWallet(pubPassphrase []byte,
	bday time.Time) (*Wallet, error) {

	return l.createNewWallet(
		pubPassphrase, nil, nil, bday, true,
	)
}

func (l *Loader) createNewWallet(pubPassphrase, privPassphrase,
	seed []byte, bday time.Time, isWatchingOnly bool) (*Wallet, error) {

	defer l.mu.Unlock()
	l.mu.Lock()

	if l.wallet != nil {
		return nil, ErrLoaded
	}

	dbPath := filepath.Join(l.dbDirPath, WalletDBName)
	exists, err := fileExists(dbPath)
	if err != nil {
		return nil, err
	}
	if exists {
		return nil, ErrExists
	}

	// Create the wallet database backed by bolt db.
	err = os.MkdirAll(l.dbDirPath, 0700)
	if err != nil {
		return nil, err
	}
	db, err := walletdb.Create("bdb", dbPath, l.noFreelistSync, l.timeout)
	if err != nil {
		return nil, err
	}

	// Initialize the newly created database for the wallet before opening.
	if isWatchingOnly {
		err = CreateWatchingOnly(db, pubPassphrase, l.chainParams, bday)
		if err != nil {
			return nil, err
		}
	} else {
		err = Create(
			db, pubPassphrase, privPassphrase, seed, l.chainParams, bday,
		)
		if err != nil {
			return nil, err
		}
	}

	// Open the newly-created wallet.
	w, err := Open(db, pubPassphrase, nil, l.chainParams, l.recoveryWindow)
	if err != nil {
		return nil, err
	}
	w.Start()

	l.onLoaded(w, db)
	return w, nil
}

var errNoConsole = errors.New("db upgrade requires console access for additional input")

func noConsole() ([]byte, error) {
	return nil, errNoConsole
}

// OpenExistingWallet opens the wallet from the loader's wallet database path
// and the public passphrase.  If the loader is being called by a context where
// standard input prompts may be used during wallet upgrades, setting
// canConsolePrompt will enables these prompts.
func (l *Loader) OpenExistingWallet(pubPassphrase []byte, canConsolePrompt bool) (*Wallet, error) {
	defer l.mu.Unlock()
	l.mu.Lock()

	if l.wallet != nil {
		return nil, ErrLoaded
	}

	// Ensure that the network directory exists.
	if err := checkCreateDir(l.dbDirPath); err != nil {
		return nil, err
	}

	// Open the database using the boltdb backend.
	dbPath := filepath.Join(l.dbDirPath, WalletDBName)
	db, err := walletdb.Open("bdb", dbPath, l.noFreelistSync, l.timeout)
	if err != nil {
		log.Errorf("Failed to open database: %v", err)
		return nil, err
	}

	var cbs *waddrmgr.OpenCallbacks
	if canConsolePrompt {
		cbs = &waddrmgr.OpenCallbacks{
			ObtainSeed:        prompt.ProvideSeed,
			ObtainPrivatePass: prompt.ProvidePrivPassphrase,
		}
	} else {
		cbs = &waddrmgr.OpenCallbacks{
			ObtainSeed:        noConsole,
			ObtainPrivatePass: noConsole,
		}
	}
	w, err := Open(db, pubPassphrase, cbs, l.chainParams, l.recoveryWindow)
	if err != nil {
		// If opening the wallet fails (e.g. because of wrong
		// passphrase), we must close the backing database to
		// allow future calls to walletdb.Open().
		e := db.Close()
		if e != nil {
			log.Warnf("Error closing database: %v", e)
		}
		return nil, err
	}
	w.Start()

	l.onLoaded(w, db)
	return w, nil
}

// WalletExists returns whether a file exists at the loader's database path.
// This may return an error for unexpected I/O failures.
func (l *Loader) WalletExists() (bool, error) {
	dbPath := filepath.Join(l.dbDirPath, WalletDBName)
	return fileExists(dbPath)
}

// LoadedWallet returns the loaded wallet, if any, and a bool for whether the
// wallet has been loaded or not.  If true, the wallet pointer should be safe to
// dereference.
func (l *Loader) LoadedWallet() (*Wallet, bool) {
	l.mu.Lock()
	w := l.wallet
	l.mu.Unlock()
	return w, w != nil
}

// UnloadWallet stops the loaded wallet, if any, and closes the wallet database.
// This returns ErrNotLoaded if the wallet has not been loaded with
// CreateNewWallet or LoadExistingWallet.  The Loader may be reused if this
// function returns without error.
func (l *Loader) UnloadWallet() error {
	defer l.mu.Unlock()
	l.mu.Lock()

	if l.wallet == nil {
		return ErrNotLoaded
	}

	l.wallet.Stop()
	l.wallet.WaitForShutdown()
	err := l.db.Close()
	if err != nil {
		return err
	}

	l.wallet = nil
	l.db = nil
	return nil
}

func fileExists(filePath string) (bool, error) {
	_, err := os.Stat(filePath)
	if err != nil {
		if os.IsNotExist(err) {
			return false, nil
		}
		return false, err
	}
	return true, nil
}
<commit_msg>wallet: support for external wallet DB<commit_after>// Copyright (c) 2015-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package wallet

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/btcsuite/btcd/chaincfg"
	"github.com/btcsuite/btcwallet/internal/prompt"
	"github.com/btcsuite/btcwallet/waddrmgr"
	"github.com/btcsuite/btcwallet/walletdb"
)

const (
	// WalletDBName specified the database filename for the wallet.
	WalletDBName = "wallet.db"

	// DefaultDBTimeout is the default timeout value when opening the wallet
	// database.
	DefaultDBTimeout = 60 * time.Second
)

var (
	// ErrLoaded describes the error condition of attempting to load or
	// create a wallet when the loader has already done so.
	ErrLoaded = errors.New("wallet already loaded")

	// ErrNotLoaded describes the error condition of attempting to close a
	// loaded wallet when a wallet has not been loaded.
	ErrNotLoaded = errors.New("wallet is not loaded")

	// ErrExists describes the error condition of attempting to create a new
	// wallet when one exists already.
	ErrExists = errors.New("wallet already exists")
)

// Loader implements the creating of new and opening of existing wallets, while
// providing a callback system for other subsystems to handle the loading of a
// wallet.  This is primarily intended for use by the RPC servers, to enable
// methods and services which require the wallet when the wallet is loaded by
// another subsystem.
//
// Loader is safe for concurrent access.
type Loader struct {
	callbacks      []func(*Wallet)
	chainParams    *chaincfg.Params
	dbDirPath      string
	noFreelistSync bool
	timeout        time.Duration
	recoveryWindow uint32
	wallet         *Wallet
	localDB        bool
	walletExists   func() (bool, error)
	db             walletdb.DB
	mu             sync.Mutex
}

// NewLoader constructs a Loader with an optional recovery window. If the
// recovery window is non-zero, the wallet will attempt to recovery addresses
// starting from the last SyncedTo height.
func NewLoader(chainParams *chaincfg.Params, dbDirPath string,
	noFreelistSync bool, timeout time.Duration,
	recoveryWindow uint32) *Loader {

	return &Loader{
		chainParams:    chainParams,
		dbDirPath:      dbDirPath,
		noFreelistSync: noFreelistSync,
		timeout:        timeout,
		recoveryWindow: recoveryWindow,
		localDB:        true,
	}
}

// NewLoaderWithDB constructs a Loader with an externally provided DB. This way
// users are free to use their own walletdb implementation (eg. leveldb, etcd)
// to store the wallet. Given that the external DB may be shared an additional
// function is also passed which will override Loader.WalletExists().
func NewLoaderWithDB(chainParams *chaincfg.Params, recoveryWindow uint32,
	db walletdb.DB, walletExists func() (bool, error)) (*Loader, error) {

	if db == nil {
		return nil, fmt.Errorf("no DB provided")
	}

	if walletExists == nil {
		return nil, fmt.Errorf("unable to check if wallet exists")
	}

	return &Loader{
		chainParams:    chainParams,
		recoveryWindow: recoveryWindow,
		localDB:        false,
		walletExists:   walletExists,
		db:             db,
	}, nil
}

// onLoaded executes each added callback and prevents loader from loading any
// additional wallets.  Requires mutex to be locked.
func (l *Loader) onLoaded(w *Wallet) {
	for _, fn := range l.callbacks {
		fn(w)
	}

	l.wallet = w
	l.callbacks = nil // not needed anymore
}

// RunAfterLoad adds a function to be executed when the loader creates or opens
// a wallet.  Functions are executed in a single goroutine in the order they are
// added.
func (l *Loader) RunAfterLoad(fn func(*Wallet)) {
	l.mu.Lock()
	if l.wallet != nil {
		w := l.wallet
		l.mu.Unlock()
		fn(w)
	} else {
		l.callbacks = append(l.callbacks, fn)
		l.mu.Unlock()
	}
}

// CreateNewWallet creates a new wallet using the provided public and private
// passphrases.  The seed is optional.  If non-nil, addresses are derived from
// this seed.  If nil, a secure random seed is generated.
func (l *Loader) CreateNewWallet(pubPassphrase, privPassphrase, seed []byte,
	bday time.Time) (*Wallet, error) {

	return l.createNewWallet(
		pubPassphrase, privPassphrase, seed, bday, false,
	)
}

// CreateNewWatchingOnlyWallet creates a new wallet using the provided
// public passphrase.  No seed or private passphrase may be provided
// since the wallet is watching-only.
func (l *Loader) CreateNewWatchingOnlyWallet(pubPassphrase []byte,
	bday time.Time) (*Wallet, error) {

	return l.createNewWallet(
		pubPassphrase, nil, nil, bday, true,
	)
}

func (l *Loader) createNewWallet(pubPassphrase, privPassphrase,
	seed []byte, bday time.Time, isWatchingOnly bool) (*Wallet, error) {

	defer l.mu.Unlock()
	l.mu.Lock()

	if l.wallet != nil {
		return nil, ErrLoaded
	}

	exists, err := l.WalletExists()
	if err != nil {
		return nil, err
	}
	if exists {
		return nil, ErrExists
	}

	if l.localDB {
		dbPath := filepath.Join(l.dbDirPath, WalletDBName)

		// Create the wallet database backed by bolt db.
		err = os.MkdirAll(l.dbDirPath, 0700)
		if err != nil {
			return nil, err
		}
		l.db, err = walletdb.Create(
			"bdb", dbPath, l.noFreelistSync, l.timeout,
		)
		if err != nil {
			return nil, err
		}
	}

	// Initialize the newly created database for the wallet before opening.
	if isWatchingOnly {
		err := CreateWatchingOnly(
			l.db, pubPassphrase, l.chainParams, bday,
		)
		if err != nil {
			return nil, err
		}
	} else {
		err := Create(
			l.db, pubPassphrase, privPassphrase, seed,
			l.chainParams, bday,
		)
		if err != nil {
			return nil, err
		}
	}

	// Open the newly-created wallet.
	w, err := Open(l.db, pubPassphrase, nil, l.chainParams, l.recoveryWindow)
	if err != nil {
		return nil, err
	}
	w.Start()

	l.onLoaded(w)
	return w, nil
}

var errNoConsole = errors.New("db upgrade requires console access for additional input")

func noConsole() ([]byte, error) {
	return nil, errNoConsole
}

// OpenExistingWallet opens the wallet from the loader's wallet database path
// and the public passphrase.  If the loader is being called by a context where
// standard input prompts may be used during wallet upgrades, setting
// canConsolePrompt will enables these prompts.
func (l *Loader) OpenExistingWallet(pubPassphrase []byte, canConsolePrompt bool) (*Wallet, error) {
	defer l.mu.Unlock()
	l.mu.Lock()

	if l.wallet != nil {
		return nil, ErrLoaded
	}

	if l.localDB {
		var err error
		// Ensure that the network directory exists.
		if err = checkCreateDir(l.dbDirPath); err != nil {
			return nil, err
		}

		// Open the database using the boltdb backend.
		dbPath := filepath.Join(l.dbDirPath, WalletDBName)
		l.db, err = walletdb.Open(
			"bdb", dbPath, l.noFreelistSync, l.timeout,
		)
		if err != nil {
			log.Errorf("Failed to open database: %v", err)
			return nil, err
		}
	}

	var cbs *waddrmgr.OpenCallbacks
	if canConsolePrompt {
		cbs = &waddrmgr.OpenCallbacks{
			ObtainSeed:        prompt.ProvideSeed,
			ObtainPrivatePass: prompt.ProvidePrivPassphrase,
		}
	} else {
		cbs = &waddrmgr.OpenCallbacks{
			ObtainSeed:        noConsole,
			ObtainPrivatePass: noConsole,
		}
	}
	w, err := Open(l.db, pubPassphrase, cbs, l.chainParams, l.recoveryWindow)
	if err != nil {
		// If opening the wallet fails (e.g. because of wrong
		// passphrase), we must close the backing database to
		// allow future calls to walletdb.Open().
		if l.localDB {
			e := l.db.Close()
			if e != nil {
				log.Warnf("Error closing database: %v", e)
			}
		}

		return nil, err
	}
	w.Start()

	l.onLoaded(w)
	return w, nil
}

// WalletExists returns whether a file exists at the loader's database path.
// This may return an error for unexpected I/O failures.
func (l *Loader) WalletExists() (bool, error) {
	if l.localDB {
		dbPath := filepath.Join(l.dbDirPath, WalletDBName)
		return fileExists(dbPath)
	}

	return l.walletExists()
}

// LoadedWallet returns the loaded wallet, if any, and a bool for whether the
// wallet has been loaded or not.  If true, the wallet pointer should be safe to
// dereference.
func (l *Loader) LoadedWallet() (*Wallet, bool) {
	l.mu.Lock()
	w := l.wallet
	l.mu.Unlock()
	return w, w != nil
}

// UnloadWallet stops the loaded wallet, if any, and closes the wallet database.
// This returns ErrNotLoaded if the wallet has not been loaded with
// CreateNewWallet or LoadExistingWallet.  The Loader may be reused if this
// function returns without error.
func (l *Loader) UnloadWallet() error {
	defer l.mu.Unlock()
	l.mu.Lock()

	if l.wallet == nil {
		return ErrNotLoaded
	}

	l.wallet.Stop()
	l.wallet.WaitForShutdown()
	if l.localDB {
		err := l.db.Close()
		if err != nil {
			return err
		}
	}

	l.wallet = nil
	l.db = nil
	return nil
}

func fileExists(filePath string) (bool, error) {
	_, err := os.Stat(filePath)
	if err != nil {
		if os.IsNotExist(err) {
			return false, nil
		}
		return false, err
	}
	return true, nil
}
<|endoftext|>


<commit_before>// 23 august 2012
package main

import (
	"fmt"
	"strings"
	"github.com/ziutek/mymysql/mysql"
	_ "github.com/ziutek/mymysql/thrsafe"
	"log"
	"sort"
	"unicode"
)

var db mysql.Conn
var getconsoles, getgames, getwikitext, getredirect, getcatlist mysql.Stmt

func sql_init() {
	db = mysql.New("tcp", "", config.DBServer, config.DBUsername, config.DBPassword, config.DBDatabase)
	err := db.Connect()
	if err != nil {
		log.Fatalf("could not connect to database: %v", err)
	}

	getconsoles, err = db.Prepare(
		`SELECT cat_title
			FROM wiki_category
			WHERE cat_title LIKE "%games"
				AND cat_pages > 0
			ORDER BY cat_title ASC;`)
	if err != nil {
		log.Fatalf("could not prepare console list query: %v", err)
	}

	getgames, err = db.Prepare(
		`SELECT wiki_page.page_title
			FROM wiki_page, wiki_categorylinks
			WHERE wiki_categorylinks.cl_to = ?
				AND wiki_page.page_id = wiki_categorylinks.cl_from
				AND wiki_page.page_namespace = 0
			ORDER BY wiki_page.page_title ASC;`)
	if err != nil {
		log.Fatalf("could not prepare game list query: %v", err)
	}

	getwikitext, err = db.Prepare(
		`SELECT wiki_text.old_text, wiki_page.page_id
			FROM wiki_page, wiki_revision, wiki_text
			WHERE wiki_page.page_namespace = 0
				AND wiki_page.page_title = ?
				AND wiki_page.page_latest = wiki_revision.rev_id
				AND wiki_revision.rev_text_id = wiki_text.old_id;`)
	if err != nil {
		log.Fatalf("could not prepare wikitext query (for scan list): %v", err)
	}

	getredirect, err = db.Prepare(
		`SELECT rd_title
			FROM wiki_redirect
			WHERE rd_from = ?
				AND rd_interwiki = "";`)	// don't cross sites
	if err != nil {
		log.Fatalf("could not prepare redirect query (for scan list): %v", err)
	}

	getcatlist, err = db.Prepare(
		`SELECT wiki_categorylinks.cl_to
			FROM wiki_page, wiki_categorylinks
			WHERE wiki_page.page_namespace = 6
				AND wiki_page.page_title = ?
				AND wiki_categorylinks.cl_from = wiki_page.page_id;`)
	if err != nil {
		log.Fatalf("could not prepare category list query (for checking a scan): %v", err)
	}
}

func init() {
	addInit(sql_init)
}

func canonicalize(pageName string) string {
	pageName = strings.Replace(pageName, " ", "_", -1)
	k := []rune(pageName)		// force first letter uppercase
	k[0] = unicode.ToUpper(k[0])
	return string(k)
}

func sql_getconsoles(filter func(string) bool) ([]string, error) {
	var consoles []string

	res, err := getconsoles.Run()
	if err != nil {
		return nil, fmt.Errorf("could not run console list query: %v", err)
	}
	gl, err := res.GetRows()
	if err != nil {
		return nil, fmt.Errorf("could not get console list result rows: %v", err)
	}
	for _, v := range gl {
		c := string(v[0].([]byte))
		// make human readable and drop _games
		c = strings.Replace(c, "_", " ", -1)
		c = c[:len(c) - len(" games")]
		if filter(c) {
			consoles = append(consoles, c)
		}
	}
	sort.Strings(consoles)
	return consoles, nil
}

func sql_getgames(console string) ([]string, error) {
	var games []string

	category := canonicalize(console)
	res, err := getgames.Run(category)
	if err != nil {
		return nil, fmt.Errorf("could not run game list query: %v", err)
	}
	gl, err := res.GetRows()
	if err != nil {
		return nil, fmt.Errorf("could not get game list result rows: %v", err)
	}
	for _, v := range gl {
		games = append(games, string(v[0].([]byte)))
	}
	return games, nil
}

// get wikitext, following all redirects
func sql_getwikitext(page string) ([]byte, error) {
	var wikitext []byte

	curTitle := canonicalize(page)
	for {
		res, err := getwikitext.Run(curTitle)
		if err != nil {
			return nil, fmt.Errorf("could not run wikitext query (for scan list): %v", err)
		}
		wt, err := res.GetRows()
		if err != nil {
			return nil, fmt.Errorf("could not get wikitext result rows (for scan list): %v", err)
		}
		textField := res.Map("old_text")
		if textField < 0 {
			return nil, fmt.Errorf("could not locate page text (for scan list): %v", err)
		}
		wikitext = wt[0][textField].([]byte)
		idField := res.Map("page_id")
		if idField < 0 {
			return nil, fmt.Errorf("could not locate page id (for scan list): %v", err)
		}
		id := wt[0][idField].(uint32)
		redir_res, err := getredirect.Run(id)
		if err != nil {
			return nil, fmt.Errorf("could not get redirect result rows (for scan list): %v", err)
		}
		rd, err := redir_res.GetRows()
		if err != nil {
			return nil, fmt.Errorf("could not get redirect result rows (for scan list): %v", err)
		}
		if len(rd) == 0 {					// no redirect, so finished
			break
		}
		curTitle = string(rd[0][0].([]byte))	// not finished; follow redirect
	}
	return wikitext, nil
}

func sql_getcatlist(file string) ([]string, error) {
	var categories []string

	res, err := getcatlist.Run(canonicalize(file))
	if err != nil {
		return nil, fmt.Errorf("could not run category list query (For checking a scan): %v", err)
	}
	cl, err := res.GetRows()
	if err != nil {
		return nil, fmt.Errorf("could not get category list result rows (for checking a scan): %v", err)
	}
	for _, v := range cl {
		categories = append(categories, string(v[0].([]byte)))
	}
	return categories, nil
}

/*
func main() {
	games, err := sql_getgames("Mega Drive")
	if err != nil {
		fmt.Printf("error grabbing game list: %v\n", err)
	} else {
		fmt.Println(strings.Join(games, "\n"))
	}

	wt, err := sql_getwikitext("Thunder_Force_IV")
	if err != nil {
		fmt.Printf("error grabbing wikitext: %v\n", err)
	} else {
		fmt.Println(wt)
	}

	categories, err := sql_getcatlist("ThunderForce4 MD JP Box.jpg")
	if err != nil {
		fmt.Printf("error grabbing category list: %v\n", err)
	} else {
		fmt.Println(strings.Join(categories, "\n"))
	}
}
*/
<commit_msg>Made SQL database connections a self-contained type. This is the first step of the process to have one connection per request; right now we are still using a global database connection.<commit_after>// 23 august 2012
package main

import (
	"fmt"
	"strings"
	"github.com/ziutek/mymysql/mysql"
	_ "github.com/ziutek/mymysql/thrsafe"
	"log"
	"sort"
	"unicode"
)

type SQL struct {
	db			mysql.Conn
	getconsoles	mysql.Stmt
	getgames		mysql.Stmt
	getwikitext	mysql.Stmt
	getredirect	mysql.Stmt
	getcatlist		mysql.Stmt
}

var globsql *SQL

func NewSQL() *SQL {
	s := new(SQL)

	s.db = mysql.New("tcp", "", config.DBServer, config.DBUsername, config.DBPassword, config.DBDatabase)
	err := s.db.Connect()
	if err != nil {
		log.Fatalf("could not connect to database: %v", err)
	}

	s.getconsoles, err = s.db.Prepare(
		`SELECT cat_title
			FROM wiki_category
			WHERE cat_title LIKE "%games"
				AND cat_pages > 0
			ORDER BY cat_title ASC;`)
	if err != nil {
		log.Fatalf("could not prepare console list query: %v", err)
	}

	s.getgames, err = s.db.Prepare(
		`SELECT wiki_page.page_title
			FROM wiki_page, wiki_categorylinks
			WHERE wiki_categorylinks.cl_to = ?
				AND wiki_page.page_id = wiki_categorylinks.cl_from
				AND wiki_page.page_namespace = 0
			ORDER BY wiki_page.page_title ASC;`)
	if err != nil {
		log.Fatalf("could not prepare game list query: %v", err)
	}

	s.getwikitext, err = s.db.Prepare(
		`SELECT wiki_text.old_text, wiki_page.page_id
			FROM wiki_page, wiki_revision, wiki_text
			WHERE wiki_page.page_namespace = 0
				AND wiki_page.page_title = ?
				AND wiki_page.page_latest = wiki_revision.rev_id
				AND wiki_revision.rev_text_id = wiki_text.old_id;`)
	if err != nil {
		log.Fatalf("could not prepare wikitext query (for scan list): %v", err)
	}

	s.getredirect, err = s.db.Prepare(
		`SELECT rd_title
			FROM wiki_redirect
			WHERE rd_from = ?
				AND rd_interwiki = "";`)	// don't cross sites
	if err != nil {
		log.Fatalf("could not prepare redirect query (for scan list): %v", err)
	}

	s.getcatlist, err = s.db.Prepare(
		`SELECT wiki_categorylinks.cl_to
			FROM wiki_page, wiki_categorylinks
			WHERE wiki_page.page_namespace = 6
				AND wiki_page.page_title = ?
				AND wiki_categorylinks.cl_from = wiki_page.page_id;`)
	if err != nil {
		log.Fatalf("could not prepare category list query (for checking a scan): %v", err)
	}

	return s
}

func init() {
	addInit(func() {
		globsql = NewSQL()
	})
}

func canonicalize(pageName string) string {
	pageName = strings.Replace(pageName, " ", "_", -1)
	k := []rune(pageName)		// force first letter uppercase
	k[0] = unicode.ToUpper(k[0])
	return string(k)
}

func sql_getconsoles(filter func(string) bool) ([]string, error) {
	return globsql.GetConsoleList(filter)
}

func (s *SQL) GetConsoleList(filter func(string) bool) ([]string, error) {
	var consoles []string

	res, err := s.getconsoles.Run()
	if err != nil {
		return nil, fmt.Errorf("could not run console list query: %v", err)
	}
	gl, err := res.GetRows()
	if err != nil {
		return nil, fmt.Errorf("could not get console list result rows: %v", err)
	}
	for _, v := range gl {
		c := string(v[0].([]byte))
		// make human readable and drop _games
		c = strings.Replace(c, "_", " ", -1)
		c = c[:len(c) - len(" games")]
		if filter(c) {
			consoles = append(consoles, c)
		}
	}
	sort.Strings(consoles)
	return consoles, nil
}

func sql_getgames(console string) ([]string, error) {
	return globsql.GetGameList(console)
}

func (s *SQL) GetGameList(console string) ([]string, error) {
	var games []string

	category := canonicalize(console)
	res, err := s.getgames.Run(category)
	if err != nil {
		return nil, fmt.Errorf("could not run game list query: %v", err)
	}
	gl, err := res.GetRows()
	if err != nil {
		return nil, fmt.Errorf("could not get game list result rows: %v", err)
	}
	for _, v := range gl {
		games = append(games, string(v[0].([]byte)))
	}
	return games, nil
}

func sql_getwikitext(page string) ([]byte, error) {
	return globsql.GetWikitext(page)
}

// get wikitext, following all redirects
func (s *SQL) GetWikitext(page string) ([]byte, error) {
	var wikitext []byte

	curTitle := canonicalize(page)
	for {
		res, err := s.getwikitext.Run(curTitle)
		if err != nil {
			return nil, fmt.Errorf("could not run wikitext query (for scan list): %v", err)
		}
		wt, err := res.GetRows()
		if err != nil {
			return nil, fmt.Errorf("could not get wikitext result rows (for scan list): %v", err)
		}
		textField := res.Map("old_text")
		if textField < 0 {
			return nil, fmt.Errorf("could not locate page text (for scan list): %v", err)
		}
		wikitext = wt[0][textField].([]byte)
		idField := res.Map("page_id")
		if idField < 0 {
			return nil, fmt.Errorf("could not locate page id (for scan list): %v", err)
		}
		id := wt[0][idField].(uint32)
		redir_res, err := s.getredirect.Run(id)
		if err != nil {
			return nil, fmt.Errorf("could not get redirect result rows (for scan list): %v", err)
		}
		rd, err := redir_res.GetRows()
		if err != nil {
			return nil, fmt.Errorf("could not get redirect result rows (for scan list): %v", err)
		}
		if len(rd) == 0 {					// no redirect, so finished
			break
		}
		curTitle = string(rd[0][0].([]byte))	// not finished; follow redirect
	}
	return wikitext, nil
}

func sql_getcatlist(file string) ([]string, error) {
	return globsql.GetFileCategories(file)
}

func (s *SQL) GetFileCategories(file string) ([]string, error) {
	var categories []string

	res, err := s.getcatlist.Run(canonicalize(file))
	if err != nil {
		return nil, fmt.Errorf("could not run category list query (For checking a scan): %v", err)
	}
	cl, err := res.GetRows()
	if err != nil {
		return nil, fmt.Errorf("could not get category list result rows (for checking a scan): %v", err)
	}
	for _, v := range cl {
		categories = append(categories, string(v[0].([]byte)))
	}
	return categories, nil
}

/*
func main() {
	games, err := sql_getgames("Mega Drive")
	if err != nil {
		fmt.Printf("error grabbing game list: %v\n", err)
	} else {
		fmt.Println(strings.Join(games, "\n"))
	}

	wt, err := sql_getwikitext("Thunder_Force_IV")
	if err != nil {
		fmt.Printf("error grabbing wikitext: %v\n", err)
	} else {
		fmt.Println(wt)
	}

	categories, err := sql_getcatlist("ThunderForce4 MD JP Box.jpg")
	if err != nil {
		fmt.Printf("error grabbing category list: %v\n", err)
	} else {
		fmt.Println(strings.Join(categories, "\n"))
	}
}
*/
<|endoftext|>


<commit_before>package delayd

import (
	"errors"
	"fmt"
	"strconv"
	"sync"
	"time"

	"github.com/crowdmob/goamz/aws"
	"github.com/crowdmob/goamz/sqs"
)

// SQSConsumer represents general SQS consumer.
type SQSConsumer struct {
	Config   SQSConfig
	Queue    *sqs.Queue
	Messages chan *sqs.Message

	shutdown chan struct{}
	paused   bool
	mu       sync.Mutex
}

// NewSQSConsumer creates a consumer for SQS and returns SQSConsumer instance.
func NewSQSConsumer(sc SQSConfig, s *sqs.SQS) (*SQSConsumer, error) {
	q, err := s.GetQueue(sc.Queue)
	if err != nil {
		return nil, err
	}
	c := &SQSConsumer{
		Config: sc,
		Queue:  q,

		paused:   true,
		Messages: make(chan *sqs.Message, sc.MaxNumberOfMessages),
		shutdown: make(chan struct{}),
	}

	return c, nil
}

func (c *SQSConsumer) poll() {
	Infof("sqs: start consuming queue %s", c.Config.Queue)
	for {
		if c.paused {
			Infof("sqs: consuming queue %s is now stopped", c.Config.Queue)
			return
		}

		resp, err := c.Queue.ReceiveMessage(c.Config.MaxNumberOfMessages)
		if err != nil {
			Warn("sqs: failed to receive messages:", err)
			continue
		}

		if len(resp.Messages) < 1 {
			continue
		}

		for i := range resp.Messages {
			c.Messages <- &resp.Messages[i]
		}
	}
}

// Start or restart poller on the queue
func (c *SQSConsumer) Start() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	if !c.paused {
		return nil
	}

	c.paused = false

	go c.poll()
	return nil
}

// Pause pauses polling on the queue
func (c *SQSConsumer) Pause() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.paused {
		return nil
	}
	c.paused = true

	return nil
}

// Close closes the shutdown channel to notify shutting down.
func (c *SQSConsumer) Close() {
	c.Pause()
	close(c.shutdown)
}

// SQSReceiver receives delayd messages over SQS.
type SQSReceiver struct {
	*SQSConsumer

	c chan Message
}

// NewSQSReceiver returns new SQSReceiver instances.
func NewSQSReceiver(sc SQSConfig, s *sqs.SQS) (*SQSReceiver, error) {
	c, err := NewSQSConsumer(sc, s)
	if err != nil {
		return nil, err
	}

	r := &SQSReceiver{
		SQSConsumer: c,

		c: make(chan Message),
	}

	go r.messageLoop()

	return r, nil
}

// MessageCh returns a receiving-only channel returns Message
func (r *SQSReceiver) MessageCh() <-chan Message {
	return r.c
}

func (r *SQSReceiver) messageLoop() {
	for {
		select {
		case <-r.shutdown:
			Debug("sqs: received signal to quit reading sqs, exiting goroutine")
			return
		case msg := <-r.Messages:
			deliverer := &SQSDeliverer{
				queue: r.Queue,
				msg:   msg,
			}

			delayAttr := findMessageAttribute(msg.MessageAttribute, "delayd-delay")
			// FIXME: A type of delay attribute should be Number.
			// Current SQS implementation in Go does not allow us to set an attributes in Number...
			delay, err := strconv.ParseInt(delayAttr.Value.StringValue, 10, 64)
			if err != nil {
				Warn(delayAttr)
				Warn("sqs: bad/missing delay. discarding message:", err)
				deliverer.Ack()
				continue
			}
			entry := &Entry{
				SendAt: time.Now().Add(time.Duration(delay) * time.Millisecond),
			}

			targetAttr := findMessageAttribute(msg.MessageAttribute, "delayd-target")
			if targetAttr.Value.DataType != "String" || targetAttr.Value.StringValue == "" {
				Warn("sqs: bad/missing target. discarding message")
				deliverer.Ack()
				continue
			}

			entry.Target = targetAttr.Value.StringValue
			entry.SQS = &SQSMessage{
				MessageID: msg.MessageId,
			}
			entry.Body = []byte(msg.Body)

			r.c <- Message{
				Entry:            entry,
				MessageDeliverer: deliverer,
			}
		}
	}
}

// Close closes a message ch and calls SQSConsumer's Close().
func (r *SQSReceiver) Close() {
	close(r.c)
	r.SQSConsumer.Close()
}

// SQSSender manages *sqs.SQS instance and a mapping of queue name to *sqs.Queue.
type SQSSender struct {
	sqs *sqs.SQS

	mu     sync.RWMutex
	queues map[string]*sqs.Queue
}

// NewSQSSender returns a SQSSender instance.
func NewSQSSender(s *sqs.SQS) *SQSSender {
	return &SQSSender{
		sqs:    s,
		queues: make(map[string]*sqs.Queue),
	}
}

func (s *SQSSender) addQueue(n string, q *sqs.Queue) {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.queues[n] = q
}

func (s *SQSSender) queueExists(n string) bool {
	s.mu.RLock()
	defer s.mu.RUnlock()
	_, found := s.queues[n]
	return found
}

// Send sends a delayd entry over SQS, using the entry's Target as the queue name.
func (s *SQSSender) Send(e *Entry) error {
	if e.SQS == nil {
		return errors.New("sqs: invalid entry")
	}

	if !s.queueExists(e.Target) {
		q, err := s.sqs.GetQueue(e.Target)
		if err != nil {
			return err
		}
		s.addQueue(e.Target, q)
	}

	_, err := s.queues[e.Target].SendMessage(string(e.Body))
	return err
}

// Close is nop for SQS.
func (s *SQSSender) Close() {
}

func findMessageAttribute(attrs []sqs.MessageAttribute, name string) sqs.MessageAttribute {
	for _, ma := range attrs {
		if ma.Name == name {
			return ma
		}
	}
	return sqs.MessageAttribute{}
}

// NewSQS returns a SQS instance from Config.
func NewSQS(config Config) (*sqs.SQS, error) {
	auth, err := aws.GetAuth("", "", "", time.Now())
	if err != nil {
		return nil, err
	}

	region, found := aws.Regions[config.SQS.Region]
	if !found {
		return nil, fmt.Errorf("region %s is not valid", config.SQS.Region)
	}
	return sqs.New(auth, region), nil
}
<commit_msg>Fix #55 Add x-delayd-date message attribute to message<commit_after>package delayd

import (
	"errors"
	"fmt"
	"strconv"
	"sync"
	"time"

	"github.com/crowdmob/goamz/aws"
	"github.com/crowdmob/goamz/sqs"
)

// SQSConsumer represents general SQS consumer.
type SQSConsumer struct {
	Config   SQSConfig
	Queue    *sqs.Queue
	Messages chan *sqs.Message

	shutdown chan struct{}
	paused   bool
	mu       sync.Mutex
}

// NewSQSConsumer creates a consumer for SQS and returns SQSConsumer instance.
func NewSQSConsumer(sc SQSConfig, s *sqs.SQS) (*SQSConsumer, error) {
	q, err := s.GetQueue(sc.Queue)
	if err != nil {
		return nil, err
	}
	c := &SQSConsumer{
		Config: sc,
		Queue:  q,

		paused:   true,
		Messages: make(chan *sqs.Message, sc.MaxNumberOfMessages),
		shutdown: make(chan struct{}),
	}

	return c, nil
}

func (c *SQSConsumer) poll() {
	Infof("sqs: start consuming queue %s", c.Config.Queue)
	for {
		if c.paused {
			Infof("sqs: consuming queue %s is now stopped", c.Config.Queue)
			return
		}

		resp, err := c.Queue.ReceiveMessage(c.Config.MaxNumberOfMessages)
		if err != nil {
			Warn("sqs: failed to receive messages:", err)
			continue
		}

		if len(resp.Messages) < 1 {
			continue
		}

		for i := range resp.Messages {
			c.Messages <- &resp.Messages[i]
		}
	}
}

// Start or restart poller on the queue
func (c *SQSConsumer) Start() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	if !c.paused {
		return nil
	}

	c.paused = false

	go c.poll()
	return nil
}

// Pause pauses polling on the queue
func (c *SQSConsumer) Pause() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.paused {
		return nil
	}
	c.paused = true

	return nil
}

// Close closes the shutdown channel to notify shutting down.
func (c *SQSConsumer) Close() {
	c.Pause()
	close(c.shutdown)
}

// SQSReceiver receives delayd messages over SQS.
type SQSReceiver struct {
	*SQSConsumer

	c chan Message
}

// NewSQSReceiver returns new SQSReceiver instances.
func NewSQSReceiver(sc SQSConfig, s *sqs.SQS) (*SQSReceiver, error) {
	c, err := NewSQSConsumer(sc, s)
	if err != nil {
		return nil, err
	}

	r := &SQSReceiver{
		SQSConsumer: c,

		c: make(chan Message),
	}

	go r.messageLoop()

	return r, nil
}

// MessageCh returns a receiving-only channel returns Message
func (r *SQSReceiver) MessageCh() <-chan Message {
	return r.c
}

func (r *SQSReceiver) messageLoop() {
	for {
		select {
		case <-r.shutdown:
			Debug("sqs: received signal to quit reading sqs, exiting goroutine")
			return
		case msg := <-r.Messages:
			deliverer := &SQSDeliverer{
				queue: r.Queue,
				msg:   msg,
			}

			delayAttr := findMessageAttribute(msg.MessageAttribute, "delayd-delay")
			// FIXME: A type of delay attribute should be Number.
			// Current SQS implementation in Go does not allow us to set an attributes in Number...
			delay, err := strconv.ParseInt(delayAttr.Value.StringValue, 10, 64)
			if err != nil {
				Warn(delayAttr)
				Warn("sqs: bad/missing delay. discarding message:", err)
				deliverer.Ack()
				continue
			}
			entry := &Entry{
				SendAt: time.Now().Add(time.Duration(delay) * time.Millisecond),
			}

			targetAttr := findMessageAttribute(msg.MessageAttribute, "delayd-target")
			if targetAttr.Value.DataType != "String" || targetAttr.Value.StringValue == "" {
				Warn("sqs: bad/missing target. discarding message")
				deliverer.Ack()
				continue
			}

			entry.Target = targetAttr.Value.StringValue
			entry.SQS = &SQSMessage{
				MessageID: msg.MessageId,
			}
			entry.Body = []byte(msg.Body)

			r.c <- Message{
				Entry:            entry,
				MessageDeliverer: deliverer,
			}
		}
	}
}

// Close closes a message ch and calls SQSConsumer's Close().
func (r *SQSReceiver) Close() {
	close(r.c)
	r.SQSConsumer.Close()
}

// SQSSender manages *sqs.SQS instance and a mapping of queue name to *sqs.Queue.
type SQSSender struct {
	sqs *sqs.SQS

	mu     sync.RWMutex
	queues map[string]*sqs.Queue
}

// NewSQSSender returns a SQSSender instance.
func NewSQSSender(s *sqs.SQS) *SQSSender {
	return &SQSSender{
		sqs:    s,
		queues: make(map[string]*sqs.Queue),
	}
}

func (s *SQSSender) addQueue(n string, q *sqs.Queue) {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.queues[n] = q
}

func (s *SQSSender) queueExists(n string) bool {
	s.mu.RLock()
	defer s.mu.RUnlock()
	_, found := s.queues[n]
	return found
}

// Send sends a delayd entry over SQS, using the entry's Target as the queue name.
func (s *SQSSender) Send(e *Entry) error {
	if e.SQS == nil {
		return errors.New("sqs: invalid entry")
	}

	if !s.queueExists(e.Target) {
		q, err := s.sqs.GetQueue(e.Target)
		if err != nil {
			return err
		}
		s.addQueue(e.Target, q)
	}

	// Use x-delayd-date to distinguish whether a messsage comes from delayd or not.
	attrs := map[string]string{
		"x-delayd-date": time.Now().Format(time.RFC1123Z),
	}

	_, err := s.queues[e.Target].SendMessageWithAttributes(string(e.Body), attrs)
	return err
}

// Close is nop for SQS.
func (s *SQSSender) Close() {
}

func findMessageAttribute(attrs []sqs.MessageAttribute, name string) sqs.MessageAttribute {
	for _, ma := range attrs {
		if ma.Name == name {
			return ma
		}
	}
	return sqs.MessageAttribute{}
}

// NewSQS returns a SQS instance from Config.
func NewSQS(config Config) (*sqs.SQS, error) {
	auth, err := aws.GetAuth("", "", "", time.Now())
	if err != nil {
		return nil, err
	}

	region, found := aws.Regions[config.SQS.Region]
	if !found {
		return nil, fmt.Errorf("region %s is not valid", config.SQS.Region)
	}
	return sqs.New(auth, region), nil
}
<|endoftext|>


<commit_before>package sync

import (
	"encoding/hex"
	"fmt"
	"hash/crc32"
	"io"
	"path"
	"path/filepath"
	"strings"
	"sync"

	"github.com/igungor/go-putio/putio"
)

// Task represent a download task, which is closely associated with a Put.io
// file.
type Task struct {
	state  *State
	cwd    string
	chunks []*chunk
}

// NewTask creates a new Task, with a fresh internal state.
func NewTask(f putio.File, cwd, downloadTo string) *Task {
	savedTo := filepath.Join(downloadTo, cwd)
	return &Task{
		state: NewState(f, savedTo),
		cwd:   cwd,
	}
}

// String implements fmt.Stringer interface for the Task.
func (t Task) String() string {
	return fmt.Sprintf("task<name: %q, size: %v, chunks: %v, bitfield: %v>",
		trimPath(path.Join(t.cwd, t.state.FileName)),
		t.state.FileLength,
		t.chunks,
		t.state.Bitfield.Len(),
	)
}

// Verify checks bitfield integrity and computes CRC32 of the task.
func (t *Task) Verify(r io.Reader) error {
	if !t.state.Bitfield.All() {
		return fmt.Errorf("Not all bits are downloaded for task: %q\n", t)
	}

	h := crc32.NewIEEE()
	_, err := io.Copy(h, r)
	if err != nil {
		return err
	}

	sum := h.Sum(nil)
	sumHex := hex.EncodeToString(sum)
	if sumHex != t.state.CRC32 {
		return fmt.Errorf("CRC32 check failed. got: %x want: %v", sumHex, t.state.CRC32)
	}

	return nil
}

// Tasks stores active tasks.
type Tasks struct {
	sync.Mutex
	s map[int64]*Task
}

// NewTasks allocated a new Tasks, which stores active tasks.
func NewTasks() *Tasks {
	return &Tasks{s: make(map[int64]*Task)}
}

// Add adds given task to the store.
func (m *Tasks) Add(t *Task) {
	m.Lock()
	defer m.Unlock()

	m.s[t.state.FileID] = t
}

// Remove removes task from the store.
func (m *Tasks) Remove(t *Task) {
	m.Lock()
	defer m.Unlock()

	delete(m.s, t.state.FileID)
}

// Exists reports whether given task is in the store.
func (m *Tasks) Exists(t *Task) bool {
	m.Lock()
	defer m.Unlock()

	_, ok := m.s[t.state.FileID]
	return ok
}

// Empty reports whether there are active tasks.
func (m *Tasks) Empty() bool {
	m.Lock()
	defer m.Unlock()

	return len(m.s) == 0
}

// trimPath trims the given path.
// E.g. /usr/local/bin/foo becomes /u/l/b/foo.
func trimPath(p string) string {
	if len(p) < 60 {
		return p
	}
	p = filepath.Clean(p)
	parts := strings.Split(p, string(filepath.Separator))
	for i, part := range parts {
		if part == "" {
			parts[i] = string(filepath.Separator)
			continue
		}
		// skip last element
		if i == len(parts)-1 {
			continue
		}
		parts[i] = string(part[0])
	}

	return filepath.Join(parts...)
}
<commit_msg>sync: task constructor calculates the chunks beforehand<commit_after>package sync

import (
	"encoding/hex"
	"fmt"
	"hash/crc32"
	"io"
	"path"
	"path/filepath"
	"strings"
	"sync"
)

// Task represent a download task, which is closely associated with a Put.io
// file.
type Task struct {
	state  *State
	cwd    string
	chunks []*chunk
}

// NewTask creates a new Task, with a fresh internal state.
func NewTask(state *State, cwd string, segmentnum uint) *Task {
	chunks := calculateChunks(state, segmentnum)
	return &Task{
		state:  state,
		cwd:    cwd,
		chunks: chunks,
	}
}

// String implements fmt.Stringer interface for the Task.
func (t Task) String() string {
	return fmt.Sprintf("task<name: %q, size: %v, chunks: %v, bitfield: %v>",
		trimPath(path.Join(t.cwd, t.state.FileName)),
		t.state.FileLength,
		t.chunks,
		t.state.Bitfield.Len(),
	)
}

// Verify checks bitfield integrity and computes CRC32 of the task.
func (t *Task) Verify(r io.Reader) error {
	if !t.state.Bitfield.All() {
		return fmt.Errorf("Not all bits are downloaded for task: %q\n", t)
	}

	h := crc32.NewIEEE()
	_, err := io.Copy(h, r)
	if err != nil {
		return err
	}

	sum := h.Sum(nil)
	sumHex := hex.EncodeToString(sum)
	if sumHex != t.state.CRC32 {
		return fmt.Errorf("CRC32 check failed. got: %x want: %v", sumHex, t.state.CRC32)
	}

	return nil
}

// Tasks stores active tasks.
type Tasks struct {
	sync.Mutex
	s map[int64]*Task
}

// NewTasks allocated a new Tasks, which stores active tasks.
func NewTasks() *Tasks {
	return &Tasks{s: make(map[int64]*Task)}
}

// Add adds given task to the store.
func (m *Tasks) Add(t *Task) {
	m.Lock()
	defer m.Unlock()

	m.s[t.state.FileID] = t
}

// Remove removes task from the store.
func (m *Tasks) Remove(t *Task) {
	m.Lock()
	defer m.Unlock()

	delete(m.s, t.state.FileID)
}

// Exists reports whether given task is in the store.
func (m *Tasks) Exists(t *Task) bool {
	m.Lock()
	defer m.Unlock()

	_, ok := m.s[t.state.FileID]
	return ok
}

// Empty reports whether there are active tasks.
func (m *Tasks) Empty() bool {
	m.Lock()
	defer m.Unlock()

	return len(m.s) == 0
}

// trimPath trims the given path.
// E.g. /usr/local/bin/foo becomes /u/l/b/foo.
func trimPath(p string) string {
	if len(p) < 60 {
		return p
	}
	p = filepath.Clean(p)
	parts := strings.Split(p, string(filepath.Separator))
	for i, part := range parts {
		if part == "" {
			parts[i] = string(filepath.Separator)
			continue
		}
		// skip last element
		if i == len(parts)-1 {
			continue
		}
		parts[i] = string(part[0])
	}

	return filepath.Join(parts...)
}
<|endoftext|>


<commit_before>package log

import "fmt"
import "os"
import "sync"
import "time"
import "runtime"

const (
	debug int = iota
	info
	warn
	error
	critical
)

// Logger impl.
type loggerImpl struct {
	module string
	level  int
	sync.Mutex
}

var defaultLogger = makeDefaultLogger()

// Creates default logger.
func makeDefaultLogger() Logger {
	var module = os.Args[0]
	var level = debug

	if val := os.Getenv("LOG_LEVEL"); val != "" {
		var levels = map[string]int{
			"debug":    debug,
			"info":     info,
			"warning":  warn,
			"warn":     warn,
			"error":    error,
			"critical": critical,
			"fatal":    critical,
		}
		level = levels[val]
	}

	return &loggerImpl{module: module, level: level}
}

// Debug log.
func (l *loggerImpl) Debug(msg string, args ...interface{}) {
	l.print(debug, "DEBU", msg, args...)
}

// Info log.
func (l *loggerImpl) Info(msg string, args ...interface{}) {
	l.print(info, "INFO", msg, args...)
}

// Warning log.
func (l *loggerImpl) Warning(msg string, args ...interface{}) {
	l.print(warn, "WARN", msg, args...)
}

// Error log.
func (l *loggerImpl) Error(msg string, args ...interface{}) {
	l.print(error, "ERRO", msg, args...)
}

// Fatal log.
func (l *loggerImpl) Fatal(msg string, args ...interface{}) {
	l.print(critical, "FATA", msg, args...)
}

func color(v int) string {
	return fmt.Sprintf("\033[%dm", 30+v)
}

// console colors
var (
	black   = color(0)
	red     = color(1)
	green   = color(2)
	yellow  = color(3)
	blue    = color(4)
	magenta = color(5)
	cyan    = color(6)
	white   = color(7)
	reset   = "\033[0m"
	colors  = []string{
		debug:    cyan,
		info:     green,
		warn:     yellow,
		error:    red,
		critical: magenta,
	}
)

// Print a message.
func (l *loggerImpl) print(level int, lstr string, msg string, args ...interface{}) {
	l.Lock()
	defer l.Unlock()

	if l.level > level {
		return
	}

	var t = time.Now().Format("15:04:05.000000")
	var p string
	if runtime.GOOS == "windows" {
		p = fmt.Sprintf("[%s] %s %s ", l.module, t, lstr)
	} else {
		p = fmt.Sprintf("[%s] %s%s %s%s%s ", l.module, magenta, t, colors[level], lstr, reset)
	}
	var s = fmt.Sprintf(msg, args...)
	fmt.Println(p, s)
}
<commit_msg>isWindows var<commit_after>package log

import "fmt"
import "os"
import "sync"
import "time"
import "runtime"

const (
	debug int = iota
	info
	warn
	error
	critical
)

// Logger impl.
type loggerImpl struct {
	module string
	level  int
	sync.Mutex
}

var defaultLogger = makeDefaultLogger()

// Creates default logger.
func makeDefaultLogger() Logger {
	var module = os.Args[0]
	var level = debug

	if val := os.Getenv("LOG_LEVEL"); val != "" {
		var levels = map[string]int{
			"debug":    debug,
			"info":     info,
			"warning":  warn,
			"warn":     warn,
			"error":    error,
			"critical": critical,
			"fatal":    critical,
		}
		level = levels[val]
	}

	return &loggerImpl{module: module, level: level}
}

// Debug log.
func (l *loggerImpl) Debug(msg string, args ...interface{}) {
	l.print(debug, "DEBU", msg, args...)
}

// Info log.
func (l *loggerImpl) Info(msg string, args ...interface{}) {
	l.print(info, "INFO", msg, args...)
}

// Warning log.
func (l *loggerImpl) Warning(msg string, args ...interface{}) {
	l.print(warn, "WARN", msg, args...)
}

// Error log.
func (l *loggerImpl) Error(msg string, args ...interface{}) {
	l.print(error, "ERRO", msg, args...)
}

// Fatal log.
func (l *loggerImpl) Fatal(msg string, args ...interface{}) {
	l.print(critical, "FATA", msg, args...)
}

func color(v int) string {
	return fmt.Sprintf("\033[%dm", 30+v)
}

// console colors
var (
	black   = color(0)
	red     = color(1)
	green   = color(2)
	yellow  = color(3)
	blue    = color(4)
	magenta = color(5)
	cyan    = color(6)
	white   = color(7)
	reset   = "\033[0m"
	colors  = []string{
		debug:    cyan,
		info:     green,
		warn:     yellow,
		error:    red,
		critical: magenta,
	}
	isWindows = runtime.GOOS == "windows"
)

// Print a message.
func (l *loggerImpl) print(level int, lstr string, msg string, args ...interface{}) {
	l.Lock()
	defer l.Unlock()

	if l.level > level {
		return
	}

	var t = time.Now().Format("15:04:05.000000")
	var p string
	if isWindows {
		p = fmt.Sprintf("[%s] %s %s ", l.module, t, lstr)
	} else {
		p = fmt.Sprintf("[%s] %s%s %s%s%s ", l.module, magenta, t, colors[level], lstr, reset)
	}
	var s = fmt.Sprintf(msg, args...)
	fmt.Println(p, s)
}
<|endoftext|>


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

package table

import (
	"github.com/osrg/gobgp/packet"
	"reflect"
)

type AdjRib struct {
	id       string
	accepted map[bgp.RouteFamily]int
	table    map[bgp.RouteFamily]map[string]*Path
}

func NewAdjRib(id string, rfList []bgp.RouteFamily) *AdjRib {
	table := make(map[bgp.RouteFamily]map[string]*Path)
	for _, rf := range rfList {
		table[rf] = make(map[string]*Path)
	}
	return &AdjRib{
		id:       id,
		table:    table,
		accepted: make(map[bgp.RouteFamily]int),
	}
}

func (adj *AdjRib) Update(pathList []*Path) {
	for _, path := range pathList {
		if path == nil {
			continue
		}
		rf := path.GetRouteFamily()
		key := path.getPrefix()
		old, found := adj.table[rf][key]
		if path.IsWithdraw {
			if found {
				delete(adj.table[rf], key)
				if old.Filtered(adj.id) > POLICY_DIRECTION_NONE {
					adj.accepted[rf]--
				}
			}
		} else {
			n := path.Filtered(adj.id)
			if found {
				o := old.Filtered(adj.id)
				if o == POLICY_DIRECTION_IN && n == POLICY_DIRECTION_NONE {
					adj.accepted[rf]++
				} else if o == POLICY_DIRECTION_NONE && n == POLICY_DIRECTION_IN {
					adj.accepted[rf]--
				}
			} else {
				if n == POLICY_DIRECTION_NONE {
					adj.accepted[rf]++
				}
			}
			if found && reflect.DeepEqual(old.GetPathAttrs(), path.GetPathAttrs()) {
				path.setTimestamp(old.GetTimestamp())
			}
			adj.table[rf][key] = path
		}
	}
}

func (adj *AdjRib) PathList(rfList []bgp.RouteFamily, accepted bool) []*Path {
	pathList := make([]*Path, 0, adj.Count(rfList))
	for _, rf := range rfList {
		for _, rr := range adj.table[rf] {
			if accepted && rr.Filtered(adj.id) > POLICY_DIRECTION_NONE {
				continue
			}
			pathList = append(pathList, rr)
		}
	}
	return pathList
}

func (adj *AdjRib) Count(rfList []bgp.RouteFamily) int {
	count := 0
	for _, rf := range rfList {
		if table, ok := adj.table[rf]; ok {
			count += len(table)
		}
	}
	return count
}

func (adj *AdjRib) Accepted(rfList []bgp.RouteFamily) int {
	count := 0
	for _, rf := range rfList {
		if n, ok := adj.accepted[rf]; ok {
			count += n
		}
	}
	return count
}

func (adj *AdjRib) Drop(rfList []bgp.RouteFamily) {
	for _, rf := range rfList {
		if _, ok := adj.table[rf]; ok {
			adj.table[rf] = make(map[string]*Path)
			adj.accepted[rf] = 0
		}
	}
}
<commit_msg>table: fix the number of accepted<commit_after>// Copyright (C) 2015 Nippon Telegraph and Telephone Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package table

import (
	"github.com/osrg/gobgp/packet"
	"reflect"
)

type AdjRib struct {
	id       string
	accepted map[bgp.RouteFamily]int
	table    map[bgp.RouteFamily]map[string]*Path
}

func NewAdjRib(id string, rfList []bgp.RouteFamily) *AdjRib {
	table := make(map[bgp.RouteFamily]map[string]*Path)
	for _, rf := range rfList {
		table[rf] = make(map[string]*Path)
	}
	return &AdjRib{
		id:       id,
		table:    table,
		accepted: make(map[bgp.RouteFamily]int),
	}
}

func (adj *AdjRib) Update(pathList []*Path) {
	for _, path := range pathList {
		if path == nil {
			continue
		}
		rf := path.GetRouteFamily()
		key := path.getPrefix()
		old, found := adj.table[rf][key]
		if path.IsWithdraw {
			if found {
				delete(adj.table[rf], key)
				if old.Filtered(adj.id) == POLICY_DIRECTION_NONE {
					adj.accepted[rf]--
				}
			}
		} else {
			n := path.Filtered(adj.id)
			if found {
				o := old.Filtered(adj.id)
				if o == POLICY_DIRECTION_IN && n == POLICY_DIRECTION_NONE {
					adj.accepted[rf]++
				} else if o == POLICY_DIRECTION_NONE && n == POLICY_DIRECTION_IN {
					adj.accepted[rf]--
				}
			} else {
				if n == POLICY_DIRECTION_NONE {
					adj.accepted[rf]++
				}
			}
			if found && reflect.DeepEqual(old.GetPathAttrs(), path.GetPathAttrs()) {
				path.setTimestamp(old.GetTimestamp())
			}
			adj.table[rf][key] = path
		}
	}
}

func (adj *AdjRib) PathList(rfList []bgp.RouteFamily, accepted bool) []*Path {
	pathList := make([]*Path, 0, adj.Count(rfList))
	for _, rf := range rfList {
		for _, rr := range adj.table[rf] {
			if accepted && rr.Filtered(adj.id) > POLICY_DIRECTION_NONE {
				continue
			}
			pathList = append(pathList, rr)
		}
	}
	return pathList
}

func (adj *AdjRib) Count(rfList []bgp.RouteFamily) int {
	count := 0
	for _, rf := range rfList {
		if table, ok := adj.table[rf]; ok {
			count += len(table)
		}
	}
	return count
}

func (adj *AdjRib) Accepted(rfList []bgp.RouteFamily) int {
	count := 0
	for _, rf := range rfList {
		if n, ok := adj.accepted[rf]; ok {
			count += n
		}
	}
	return count
}

func (adj *AdjRib) Drop(rfList []bgp.RouteFamily) {
	for _, rf := range rfList {
		if _, ok := adj.table[rf]; ok {
			adj.table[rf] = make(map[string]*Path)
			adj.accepted[rf] = 0
		}
	}
}
<|endoftext|>


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

package main

import "./libgogo/_obj/libgogo"

//
// List of global objects and declared types
//
var GlobalObjects *libgogo.ObjectDesc = nil;
var GlobalTypes *libgogo.TypeDesc = nil;

//
// List of function-local objects
//
var LocalObjects *libgogo.ObjectDesc = nil;

//
// List of currently processed objects and types
//
var CurrentType *libgogo.TypeDesc;
var CurrentObject *libgogo.ObjectDesc;

func NewType(name string) uint64 {
    var dontAddType uint64 = 0;
    var tempType *libgogo.TypeDesc;
    var temp byte;
    tempType = libgogo.GetType(name, CurrentPackage, GlobalTypes, 1);
    if  tempType != nil { //Check for duplicates
        temp = libgogo.IsForwardDecl(tempType);
        if temp != 0 { //Separate handling of forward declarations => unset forward declaration flag
            libgogo.UnsetForwardDecl(tempType);
            CurrentType = tempType;
            dontAddType = 1;
        } else { //Real duplicate
            SymbolTableError("duplicate type", name, "in package", CurrentPackage);
            CurrentType = tempType; //For stability/sanity of parser when continuing
            dontAddType = 1; //For stability/sanity of parser when continuing
        }
    } else {
        CurrentType = libgogo.NewType(name, CurrentPackage, 0, 0, nil);
    }
    return dontAddType;
}

func AddType(dontAddType uint64) {
    if dontAddType == 0 {
        GlobalTypes = libgogo.AppendType(CurrentType, GlobalTypes);
    }
}

func AddStructField(fieldname string) {
    var temp uint64;
    temp = libgogo.HasField(fieldname, CurrentType);
    if temp != 0 {
        SymbolTableError("duplicate", "", "field", fieldname);
    } else {
        CurrentObject = libgogo.NewObject(fieldname, "", libgogo.CLASS_FIELD); //A field has no package name
        libgogo.AddFields(CurrentObject, CurrentType);
    }
}

func SetCurrentObjectTypeToPointer() {
    libgogo.FlagObjectTypeAsPointer(CurrentObject); //Type is pointer
}

func SetCurrentObjectType(typename string, packagename string, arraydim uint64) {
    var basetype *libgogo.TypeDesc;
    var temptype *libgogo.TypeDesc;
    var CurrentTypeName string;
    var tempstr string = "";
    var temp byte;
    var boolFlag uint64;

    if InsideFunctionVarDecl == 0 {
        if InsideStructDecl == 1 { //Allow types in struct declarations which are already forward declared
            basetype = libgogo.GetType(typename, packagename, GlobalTypes, 1);
        } else {
            basetype = libgogo.GetType(typename, packagename, GlobalTypes, 0);
        }
        if basetype == nil {
            if InsideStructDecl == 1 {
                CurrentTypeName = libgogo.GetTypeName(CurrentType);
                boolFlag = libgogo.StringCompare(typename, CurrentTypeName);
                if boolFlag == 0 {
                    temp = libgogo.IsPointerType(CurrentObject);
                    if temp == 1 { //Allow pointer to own type
                        basetype = CurrentType;
                    } else {
                        SymbolTableError("A type cannot contain itself,", "", "type", typename);
                    }
                } else { //Forward declaration
                    basetype = libgogo.NewType(typename, packagename, 1, 0, nil);
                    GlobalTypes = libgogo.AppendType(basetype, GlobalTypes); //Add forward declared type to global list
                }
            } else {
                libgogo.StringAppend(&tempstr, packagename);
                libgogo.CharAppend(&tempstr, '.');
                libgogo.StringAppend(&tempstr, typename);
                SymbolTableError("Unknown", "", "type", tempstr);
            }
        }
        if arraydim == 0 { //No array
            libgogo.SetObjType(CurrentObject, basetype);
        } else { //Array
            if basetype != nil {
                CurrentTypeName = libgogo.GetTypeName(basetype);
                libgogo.StringAppend(&tempstr, CurrentTypeName);
            }
            libgogo.StringAppend(&tempstr, "Array");
            CurrentTypeName = libgogo.IntToString(arraydim); //Reuse CurrentTypeName as string representation of arraydim
            libgogo.StringAppend(&tempstr, CurrentTypeName);
            temptype = libgogo.NewType(tempstr, packagename, 0, arraydim, basetype);
            libgogo.SetObjType(CurrentObject, temptype); //Don't add array type to global list to avoid duplicate type name errors
        }
    }
}

func NewVariable(name string) {
    var TempObject *libgogo.ObjectDesc;
    CurrentObject = libgogo.NewObject(name, CurrentPackage, libgogo.CLASS_VAR);
    if InsideFunction == 0 { //Global objects
        TempObject = libgogo.GetObject(name, CurrentPackage, GlobalObjects);
        if TempObject != nil {
            SymbolTableError("duplicate", "global", "identifier", name);
        }
        GlobalObjects = libgogo.AppendObject(CurrentObject, GlobalObjects);
    } else { //Function-local objects
        TempObject = libgogo.GetObject(name, CurrentPackage, LocalObjects);
        if TempObject != nil {
            SymbolTableError("duplicate", "local", "identifier", name);
        }
        LocalObjects = libgogo.AppendObject(CurrentObject, LocalObjects);
    }
}

func EndOfFunction() {
    var temp uint64;
    temp = CheckDebugLevel(100);
    if temp == 1 { //Function-local symbol table
        libgogo.PrintString("\nFunction-local symbol table until line ");
        libgogo.PrintNumber(fileInfo[curFileIndex].lineCounter);
        libgogo.PrintString(" of ");
        libgogo.PrintString(fileInfo[curFileIndex].filename);
        libgogo.PrintString(":\n----------------------------------------------------------------------------\n");
        libgogo.PrintObjects(LocalObjects);
    }
    LocalObjects = nil; //Delete local objects
}
<commit_msg>symtable.go: Documentation<commit_after>// Copyright 2010 The GoGo Authors. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package main

import "./libgogo/_obj/libgogo"

//
// List of global objects and declared types
//
var GlobalObjects *libgogo.ObjectDesc = nil;
var GlobalTypes *libgogo.TypeDesc = nil;

//
// List of function-local objects
//
var LocalObjects *libgogo.ObjectDesc = nil;

//
// List of currently processed objects and types
//
var CurrentType *libgogo.TypeDesc;
var CurrentObject *libgogo.ObjectDesc;

//
// Creates a new type with the specified name to the symbol table
// The newly created type is not added to the global symbol table,
// but a flag is returned whether this has to be done later when
// the whole type is specified using AddType
//
func NewType(name string) uint64 {
    var dontAddType uint64 = 0;
    var tempType *libgogo.TypeDesc;
    var temp byte;
    tempType = libgogo.GetType(name, CurrentPackage, GlobalTypes, 1);
    if  tempType != nil { //Check for duplicates
        temp = libgogo.IsForwardDecl(tempType);
        if temp != 0 { //Separate handling of forward declarations => unset forward declaration flag
            libgogo.UnsetForwardDecl(tempType);
            CurrentType = tempType;
            dontAddType = 1;
        } else { //Real duplicate
            SymbolTableError("duplicate type", name, "in package", CurrentPackage);
            CurrentType = tempType; //For stability/sanity of parser when continuing
            dontAddType = 1; //For stability/sanity of parser when continuing
        }
    } else {
        CurrentType = libgogo.NewType(name, CurrentPackage, 0, 0, nil);
    }
    return dontAddType;
}

//
// Adds a newly created type built by NewType when all of its fields
// have been added. The parameter dontAddType specifies whether the
// the type actually needs to be added to the global symbol tables
//
func AddType(dontAddType uint64) {
    if dontAddType == 0 {
        GlobalTypes = libgogo.AppendType(CurrentType, GlobalTypes);
    }
}

//
// Adds a fields with the speicified name to a new type created by
// NewType. The field's type has to be set using SetCurrentObjectType
//
func AddStructField(fieldname string) {
    var temp uint64;
    temp = libgogo.HasField(fieldname, CurrentType);
    if temp != 0 {
        SymbolTableError("duplicate", "", "field", fieldname);
    } else {
        CurrentObject = libgogo.NewObject(fieldname, "", libgogo.CLASS_FIELD); //A field has no package name
        libgogo.AddFields(CurrentObject, CurrentType);
    }
}

//
// Flags the type the current object or field to be a pointer and
// not the type itself
//
func SetCurrentObjectTypeToPointer() {
    libgogo.FlagObjectTypeAsPointer(CurrentObject); //Type is pointer
}

//
// Sets the type of the current object or field using type
// [arraydim]packagename.typename where arraydim and packagename
// are optional. In case of arraydim != 0, a new type is created
// to represent an array type of the specified size
//
func SetCurrentObjectType(typename string, packagename string, arraydim uint64) {
    var basetype *libgogo.TypeDesc;
    var temptype *libgogo.TypeDesc;
    var CurrentTypeName string;
    var tempstr string = "";
    var temp byte;
    var boolFlag uint64;

    if InsideFunctionVarDecl == 0 {
        if InsideStructDecl == 1 { //Allow types in struct declarations which are already forward declared
            basetype = libgogo.GetType(typename, packagename, GlobalTypes, 1);
        } else {
            basetype = libgogo.GetType(typename, packagename, GlobalTypes, 0);
        }
        if basetype == nil {
            if InsideStructDecl == 1 {
                CurrentTypeName = libgogo.GetTypeName(CurrentType);
                boolFlag = libgogo.StringCompare(typename, CurrentTypeName);
                if boolFlag == 0 {
                    temp = libgogo.IsPointerType(CurrentObject);
                    if temp == 1 { //Allow pointer to own type
                        basetype = CurrentType;
                    } else {
                        SymbolTableError("A type cannot contain itself,", "", "type", typename);
                    }
                } else { //Forward declaration
                    basetype = libgogo.NewType(typename, packagename, 1, 0, nil);
                    GlobalTypes = libgogo.AppendType(basetype, GlobalTypes); //Add forward declared type to global list
                }
            } else {
                libgogo.StringAppend(&tempstr, packagename);
                libgogo.CharAppend(&tempstr, '.');
                libgogo.StringAppend(&tempstr, typename);
                SymbolTableError("Unknown", "", "type", tempstr);
            }
        }
        if arraydim == 0 { //No array
            libgogo.SetObjType(CurrentObject, basetype);
        } else { //Array
            if basetype != nil {
                CurrentTypeName = libgogo.GetTypeName(basetype);
                libgogo.StringAppend(&tempstr, CurrentTypeName);
            }
            libgogo.StringAppend(&tempstr, "Array");
            CurrentTypeName = libgogo.IntToString(arraydim); //Reuse CurrentTypeName as string representation of arraydim
            libgogo.StringAppend(&tempstr, CurrentTypeName);
            temptype = libgogo.NewType(tempstr, packagename, 0, arraydim, basetype);
            libgogo.SetObjType(CurrentObject, temptype); //Don't add array type to global list to avoid duplicate type name errors
        }
    }
}

//
// Adds a new variable to the global symbol table with the specified
// name. The variable type has to be set using SetCurrentObjectType
//
func NewVariable(name string) {
    var TempObject *libgogo.ObjectDesc;
    CurrentObject = libgogo.NewObject(name, CurrentPackage, libgogo.CLASS_VAR);
    if InsideFunction == 0 { //Global objects
        TempObject = libgogo.GetObject(name, CurrentPackage, GlobalObjects);
        if TempObject != nil {
            SymbolTableError("duplicate", "global", "identifier", name);
        }
        GlobalObjects = libgogo.AppendObject(CurrentObject, GlobalObjects);
    } else { //Function-local objects
        TempObject = libgogo.GetObject(name, CurrentPackage, LocalObjects);
        if TempObject != nil {
            SymbolTableError("duplicate", "local", "identifier", name);
        }
        LocalObjects = libgogo.AppendObject(CurrentObject, LocalObjects);
    }
}

//
// When called when the reaching the end of a function during parsing, the
// local symbol table is purged and optional debug information about the
// symbol table before purging are printed
//
func EndOfFunction() {
    var temp uint64;
    temp = CheckDebugLevel(100);
    if temp == 1 { //Function-local symbol table
        libgogo.PrintString("\nFunction-local symbol table until line ");
        libgogo.PrintNumber(fileInfo[curFileIndex].lineCounter);
        libgogo.PrintString(" of ");
        libgogo.PrintString(fileInfo[curFileIndex].filename);
        libgogo.PrintString(":\n----------------------------------------------------------------------------\n");
        libgogo.PrintObjects(LocalObjects);
    }
    LocalObjects = nil; //Delete local objects
}
<|endoftext|>


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

package id3v2

import (
	"bytes"
	"io"
	"io/ioutil"
	"os"

	"github.com/bogem/id3v2/bbpool"
	"github.com/bogem/id3v2/util"
)

// Tag stores all frames of opened file.
type Tag struct {
	frames    map[string]Framer
	sequences map[string]sequencer
	ids       map[string]string

	file         *os.File
	originalSize int64
	version      byte
}

func (t *Tag) AddFrame(id string, f Framer) {
	switch id {
	case t.ID("Attached picture"):
		pf := f.(PictureFrame)
		t.AddAttachedPicture(pf)
	case t.ID("Comments"):
		cf := f.(CommentFrame)
		t.AddCommentFrame(cf)
	case t.ID("Unsynchronised lyrics/text transcription"):
		uslf := f.(UnsynchronisedLyricsFrame)
		t.AddUnsynchronisedLyricsFrame(uslf)
	default:
		t.frames[id] = f
	}
}

func (t *Tag) AddAttachedPicture(pf PictureFrame) {
	id := t.ID("Attached picture")
	t.checkExistenceOfSequence(id, newPictureSequence)
	t.addFrameToSequence(id, pf)
}

func (t *Tag) AddCommentFrame(cf CommentFrame) {
	id := t.ID("Comments")
	t.checkExistenceOfSequence(id, newCommentSequence)
	t.addFrameToSequence(id, cf)
}

func (t *Tag) AddUnsynchronisedLyricsFrame(uslf UnsynchronisedLyricsFrame) {
	id := t.ID("Unsynchronised lyrics/text transcription")
	t.checkExistenceOfSequence(id, newUSLFSequence)
	t.addFrameToSequence(id, uslf)
}

func (t *Tag) checkExistenceOfSequence(id string, newSequence func() sequencer) {
	if t.sequences[id] == nil {
		t.sequences[id] = newSequence()
	}
}

func (t *Tag) addFrameToSequence(id string, f Framer) {
	t.sequences[id].AddFrame(f)
}

// ID returns ID3v2.3 or ID3v2.4 (in appropriate to version of Tag) frame ID
// from given description.
// For example, ID("Language") will return "TLAN".
func (t Tag) ID(description string) string {
	return t.ids[description]
}

// AllFrames returns map, that contains all frames in tag, that could be parsed.
// The key of this map is an ID of frame and value is an array of frames.
func (t *Tag) AllFrames() map[string][]Framer {
	frames := make(map[string][]Framer)

	for id, frame := range t.frames {
		frames[id] = append(frames[id], frame)
	}

	for id, sequence := range t.sequences {
		frames[id] = append(frames[id], sequence.Frames()...)
	}

	return frames
}

// DeleteAllFrames deletes all frames in tag.
func (t *Tag) DeleteAllFrames() {
	t.frames = make(map[string]Framer)
	t.sequences = make(map[string]sequencer)
}

// DeleteFrames deletes frames in tag with given id.
func (t *Tag) DeleteFrames(id string) {
	delete(t.frames, id)
	delete(t.sequences, id)
}

// GetLastFrame returns last frame from slice, that is returned from GetFrames function.
// GetLastFrame is suitable for frames, that can be only one in whole tag.
// For example, for text frames.
//
// Example of usage:
//	bpmFramer := t.GetLastFrame(t.ID("BPM"))
//	if bpmFramer != nil {
//		bpm, ok := bpmFramer.(id3v2.TextFrame)
//		if !ok {
//			log.Fatal("Couldn't assert bpm frame")
//		}
//		fmt.Println(bpm.Text)
//	}
func (t *Tag) GetLastFrame(id string) Framer {
	fs := t.GetFrames(id)
	if len(fs) == 0 || fs == nil {
		return nil
	}
	return fs[len(fs)-1]
}

// GetFrames returns frames with corresponding id.
//
// Example of usage:
//	pictures := tag.GetFrames(tag.ID("Attached picture"))
//	if pictures != nil {
//		for _, f := range pictures {
//			pic, ok := f.(id3v2.PictureFrame)
//			if !ok {
//				log.Fatal("Couldn't assert picture frame")
//			}
//
//			// Do some operations with picture frame:
//			fmt.Println(pic.Description) // For example, print description of picture frame
//			image, err := ioutil.ReadAll(pic.Picture) // Or read a picture from picture frame
//			if err != nil {
//				log.Fatal("Error while reading a picture from picture frame: ", err)
//			}
//		}
//	}
func (t *Tag) GetFrames(id string) []Framer {
	if f, exists := t.frames[id]; exists {
		return []Framer{f}
	} else if s, exists := t.sequences[id]; exists {
		return s.Frames()
	}

	return nil
}

// GetTextFrame returns text frame with corresponding id.
func (t Tag) GetTextFrame(id string) TextFrame {
	f := t.GetLastFrame(id)
	if f == nil {
		return TextFrame{}
	}
	tf := f.(TextFrame)
	return tf
}

func (t Tag) Title() string {
	f := t.GetTextFrame(t.ID("Title/Songname/Content description"))
	return f.Text
}

func (t *Tag) SetTitle(title string) {
	t.AddFrame(t.ID("Title/Songname/Content description"), TextFrame{Encoding: ENUTF8, Text: title})
}

func (t Tag) Artist() string {
	f := t.GetTextFrame(t.ID("Lead artist/Lead performer/Soloist/Performing group"))
	return f.Text
}

func (t *Tag) SetArtist(artist string) {
	t.AddFrame(t.ID("Lead artist/Lead performer/Soloist/Performing group"), TextFrame{Encoding: ENUTF8, Text: artist})
}

func (t Tag) Album() string {
	f := t.GetTextFrame(t.ID("Album/Movie/Show title"))
	return f.Text
}

func (t *Tag) SetAlbum(album string) {
	t.AddFrame(t.ID("Album/Movie/Show title"), TextFrame{Encoding: ENUTF8, Text: album})
}

func (t Tag) Year() string {
	f := t.GetTextFrame(t.ID("Year"))
	return f.Text
}

func (t *Tag) SetYear(year string) {
	t.AddFrame(t.ID("Year"), TextFrame{Encoding: ENUTF8, Text: year})
}

func (t Tag) Genre() string {
	f := t.GetTextFrame(t.ID("Content type"))
	return f.Text
}

func (t *Tag) SetGenre(genre string) {
	t.AddFrame(t.ID("Content type"), TextFrame{Encoding: ENUTF8, Text: genre})
}

// Save writes tag to the file.
func (t *Tag) Save() error {
	// Forming new frames
	frames := t.formAllFrames()

	// Forming size of new frames
	framesSize := util.FormSize(int64(len(frames)))

	// Creating a temp file for mp3 file, which will contain new tag
	newFile, err := ioutil.TempFile("", "")
	if err != nil {
		return err
	}

	// Make sure we clean up the temp file if it is still around.
	defer os.Remove(newFile.Name())

	// Writing to new file new tag header
	if _, err = newFile.Write(formTagHeader(framesSize, t.version)); err != nil {
		return err
	}

	// Writing to new file new frames
	if _, err = newFile.Write(frames); err != nil {
		return err
	}

	// Seeking to a music part of mp3
	originalFile := t.file
	defer originalFile.Close()
	if _, err = originalFile.Seek(t.originalSize, os.SEEK_SET); err != nil {
		return err
	}

	// Writing to new file the music part
	if _, err = io.Copy(newFile, originalFile); err != nil {
		return err
	}

	// Getting original file mode
	originalFileStat, err := originalFile.Stat()
	if err != nil {
		return err
	}
	originalFileMode := originalFileStat.Mode()

	// Setting new file mode
	if err = newFile.Chmod(originalFileMode); err != nil {
		return err
	}

	// Replacing original file with new file
	if err = os.Rename(newFile.Name(), originalFile.Name()); err != nil {
		return err
	}
	t.file = newFile

	return nil
}

// Close closes the tag's file, rendering it unusable for I/O.
// It returns an error, if any.
func (t *Tag) Close() error {
	return t.file.Close()
}

func (t Tag) formAllFrames() []byte {
	framesBuffer := bbpool.Get()
	defer bbpool.Put(framesBuffer)

	t.writeFrames(framesBuffer)

	return framesBuffer.Bytes()
}

func (t Tag) writeFrames(w io.Writer) {
	for id, frames := range t.AllFrames() {
		for _, f := range frames {
			w.Write(formFrame(id, f))
		}
	}
}

func formFrame(id string, frame Framer) []byte {
	if id == "" {
		panic("there is blank ID in frames")
	}

	frameBuffer := bbpool.Get()
	defer bbpool.Put(frameBuffer)

	frameBody := frame.Body()
	writeFrameHeader(frameBuffer, id, int64(len(frameBody)))
	frameBuffer.Write(frameBody)

	return frameBuffer.Bytes()
}

func writeFrameHeader(buf *bytes.Buffer, id string, frameSize int64) {
	buf.WriteString(id)
	buf.Write(util.FormSize(frameSize))
	buf.Write([]byte{0, 0})
}
<commit_msg>Use present simple tense for commentaries in Save method<commit_after>// Copyright 2016 Albert Nigmatzianov. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package id3v2

import (
	"bytes"
	"io"
	"io/ioutil"
	"os"

	"github.com/bogem/id3v2/bbpool"
	"github.com/bogem/id3v2/util"
)

// Tag stores all frames of opened file.
type Tag struct {
	frames    map[string]Framer
	sequences map[string]sequencer
	ids       map[string]string

	file         *os.File
	originalSize int64
	version      byte
}

func (t *Tag) AddFrame(id string, f Framer) {
	switch id {
	case t.ID("Attached picture"):
		pf := f.(PictureFrame)
		t.AddAttachedPicture(pf)
	case t.ID("Comments"):
		cf := f.(CommentFrame)
		t.AddCommentFrame(cf)
	case t.ID("Unsynchronised lyrics/text transcription"):
		uslf := f.(UnsynchronisedLyricsFrame)
		t.AddUnsynchronisedLyricsFrame(uslf)
	default:
		t.frames[id] = f
	}
}

func (t *Tag) AddAttachedPicture(pf PictureFrame) {
	id := t.ID("Attached picture")
	t.checkExistenceOfSequence(id, newPictureSequence)
	t.addFrameToSequence(id, pf)
}

func (t *Tag) AddCommentFrame(cf CommentFrame) {
	id := t.ID("Comments")
	t.checkExistenceOfSequence(id, newCommentSequence)
	t.addFrameToSequence(id, cf)
}

func (t *Tag) AddUnsynchronisedLyricsFrame(uslf UnsynchronisedLyricsFrame) {
	id := t.ID("Unsynchronised lyrics/text transcription")
	t.checkExistenceOfSequence(id, newUSLFSequence)
	t.addFrameToSequence(id, uslf)
}

func (t *Tag) checkExistenceOfSequence(id string, newSequence func() sequencer) {
	if t.sequences[id] == nil {
		t.sequences[id] = newSequence()
	}
}

func (t *Tag) addFrameToSequence(id string, f Framer) {
	t.sequences[id].AddFrame(f)
}

// ID returns ID3v2.3 or ID3v2.4 (in appropriate to version of Tag) frame ID
// from given description.
// For example, ID("Language") will return "TLAN".
func (t Tag) ID(description string) string {
	return t.ids[description]
}

// AllFrames returns map, that contains all frames in tag, that could be parsed.
// The key of this map is an ID of frame and value is an array of frames.
func (t *Tag) AllFrames() map[string][]Framer {
	frames := make(map[string][]Framer)

	for id, frame := range t.frames {
		frames[id] = append(frames[id], frame)
	}

	for id, sequence := range t.sequences {
		frames[id] = append(frames[id], sequence.Frames()...)
	}

	return frames
}

// DeleteAllFrames deletes all frames in tag.
func (t *Tag) DeleteAllFrames() {
	t.frames = make(map[string]Framer)
	t.sequences = make(map[string]sequencer)
}

// DeleteFrames deletes frames in tag with given id.
func (t *Tag) DeleteFrames(id string) {
	delete(t.frames, id)
	delete(t.sequences, id)
}

// GetLastFrame returns last frame from slice, that is returned from GetFrames function.
// GetLastFrame is suitable for frames, that can be only one in whole tag.
// For example, for text frames.
//
// Example of usage:
//	bpmFramer := t.GetLastFrame(t.ID("BPM"))
//	if bpmFramer != nil {
//		bpm, ok := bpmFramer.(id3v2.TextFrame)
//		if !ok {
//			log.Fatal("Couldn't assert bpm frame")
//		}
//		fmt.Println(bpm.Text)
//	}
func (t *Tag) GetLastFrame(id string) Framer {
	fs := t.GetFrames(id)
	if len(fs) == 0 || fs == nil {
		return nil
	}
	return fs[len(fs)-1]
}

// GetFrames returns frames with corresponding id.
//
// Example of usage:
//	pictures := tag.GetFrames(tag.ID("Attached picture"))
//	if pictures != nil {
//		for _, f := range pictures {
//			pic, ok := f.(id3v2.PictureFrame)
//			if !ok {
//				log.Fatal("Couldn't assert picture frame")
//			}
//
//			// Do some operations with picture frame:
//			fmt.Println(pic.Description) // For example, print description of picture frame
//			image, err := ioutil.ReadAll(pic.Picture) // Or read a picture from picture frame
//			if err != nil {
//				log.Fatal("Error while reading a picture from picture frame: ", err)
//			}
//		}
//	}
func (t *Tag) GetFrames(id string) []Framer {
	if f, exists := t.frames[id]; exists {
		return []Framer{f}
	} else if s, exists := t.sequences[id]; exists {
		return s.Frames()
	}

	return nil
}

// GetTextFrame returns text frame with corresponding id.
func (t Tag) GetTextFrame(id string) TextFrame {
	f := t.GetLastFrame(id)
	if f == nil {
		return TextFrame{}
	}
	tf := f.(TextFrame)
	return tf
}

func (t Tag) Title() string {
	f := t.GetTextFrame(t.ID("Title/Songname/Content description"))
	return f.Text
}

func (t *Tag) SetTitle(title string) {
	t.AddFrame(t.ID("Title/Songname/Content description"), TextFrame{Encoding: ENUTF8, Text: title})
}

func (t Tag) Artist() string {
	f := t.GetTextFrame(t.ID("Lead artist/Lead performer/Soloist/Performing group"))
	return f.Text
}

func (t *Tag) SetArtist(artist string) {
	t.AddFrame(t.ID("Lead artist/Lead performer/Soloist/Performing group"), TextFrame{Encoding: ENUTF8, Text: artist})
}

func (t Tag) Album() string {
	f := t.GetTextFrame(t.ID("Album/Movie/Show title"))
	return f.Text
}

func (t *Tag) SetAlbum(album string) {
	t.AddFrame(t.ID("Album/Movie/Show title"), TextFrame{Encoding: ENUTF8, Text: album})
}

func (t Tag) Year() string {
	f := t.GetTextFrame(t.ID("Year"))
	return f.Text
}

func (t *Tag) SetYear(year string) {
	t.AddFrame(t.ID("Year"), TextFrame{Encoding: ENUTF8, Text: year})
}

func (t Tag) Genre() string {
	f := t.GetTextFrame(t.ID("Content type"))
	return f.Text
}

func (t *Tag) SetGenre(genre string) {
	t.AddFrame(t.ID("Content type"), TextFrame{Encoding: ENUTF8, Text: genre})
}

// Save writes tag to the file.
func (t *Tag) Save() error {
	// Form new frames
	frames := t.formAllFrames()

	// Form size of new frames
	framesSize := util.FormSize(int64(len(frames)))

	// Create a temp file for mp3 file, which will contain new tag
	newFile, err := ioutil.TempFile("", "")
	if err != nil {
		return err
	}

	// Make sure we clean up the temp file if it's still around
	defer os.Remove(newFile.Name())

	// Write to new file new tag header
	if _, err = newFile.Write(formTagHeader(framesSize, t.version)); err != nil {
		return err
	}

	// Write to new file new frames
	if _, err = newFile.Write(frames); err != nil {
		return err
	}

	// Seek to a music part of original file
	originalFile := t.file
	defer originalFile.Close()
	if _, err = originalFile.Seek(t.originalSize, os.SEEK_SET); err != nil {
		return err
	}

	// Write to new file the music part
	if _, err = io.Copy(newFile, originalFile); err != nil {
		return err
	}

	// Get original file mode
	originalFileStat, err := originalFile.Stat()
	if err != nil {
		return err
	}
	originalFileMode := originalFileStat.Mode()

	// Set new file mode
	if err = newFile.Chmod(originalFileMode); err != nil {
		return err
	}

	// Replace original file with new file
	if err = os.Rename(newFile.Name(), originalFile.Name()); err != nil {
		return err
	}
	t.file = newFile

	return nil
}

// Close closes the tag's file, rendering it unusable for I/O.
// It returns an error, if any.
func (t *Tag) Close() error {
	return t.file.Close()
}

func (t Tag) formAllFrames() []byte {
	framesBuffer := bbpool.Get()
	defer bbpool.Put(framesBuffer)

	t.writeFrames(framesBuffer)

	return framesBuffer.Bytes()
}

func (t Tag) writeFrames(w io.Writer) {
	for id, frames := range t.AllFrames() {
		for _, f := range frames {
			w.Write(formFrame(id, f))
		}
	}
}

func formFrame(id string, frame Framer) []byte {
	if id == "" {
		panic("there is blank ID in frames")
	}

	frameBuffer := bbpool.Get()
	defer bbpool.Put(frameBuffer)

	frameBody := frame.Body()
	writeFrameHeader(frameBuffer, id, int64(len(frameBody)))
	frameBuffer.Write(frameBody)

	return frameBuffer.Bytes()
}

func writeFrameHeader(buf *bytes.Buffer, id string, frameSize int64) {
	buf.WriteString(id)
	buf.Write(util.FormSize(frameSize))
	buf.Write([]byte{0, 0})
}
<|endoftext|>