<commit_before>// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package gdb

import (
	"context"
	"fmt"
	"github.com/gogf/gf/text/gregex"
	"time"

	"github.com/gogf/gf/text/gstr"
)

// Model is the DAO for ORM.
type Model struct {
	db            DB             // Underlying DB interface.
	tx            *TX            // Underlying TX interface.
	schema        string         // Custom database schema.
	linkType      int            // Mark for operation on master or slave.
	tablesInit    string         // Table names when model initialization.
	tables        string         // Operation table names, which can be more than one table names and aliases, like: "user", "user u", "user u, user_detail ud".
	fields        string         // Operation fields, multiple fields joined using char ','.
	fieldsEx      string         // Excluded operation fields, multiple fields joined using char ','.
	withArray     []interface{}  // Arguments for With feature.
	withAll       bool           // Enable model association operations on all objects that have "with" tag in the struct.
	extraArgs     []interface{}  // Extra custom arguments for sql.
	whereHolder   []*whereHolder // Condition strings for where operation.
	groupBy       string         // Used for "group by" statement.
	orderBy       string         // Used for "order by" statement.
	having        []interface{}  // Used for "having..." statement.
	start         int            // Used for "select ... start, limit ..." statement.
	limit         int            // Used for "select ... start, limit ..." statement.
	option        int            // Option for extra operation features.
	offset        int            // Offset statement for some databases grammar.
	data          interface{}    // Data for operation, which can be type of map/[]map/struct/*struct/string, etc.
	batch         int            // Batch number for batch Insert/Replace/Save operations.
	filter        bool           // Filter data and where key-value pairs according to the fields of the table.
	distinct      string         // Force the query to only return distinct results.
	lockInfo      string         // Lock for update or in shared lock.
	cacheEnabled  bool           // Enable sql result cache feature.
	cacheDuration time.Duration  // Cache TTL duration.
	cacheName     string         // Cache name for custom operation.
	unscoped      bool           // Disables soft deleting features when select/delete operations.
	safe          bool           // If true, it clones and returns a new model object whenever operation done; or else it changes the attribute of current model.
}

// whereHolder is the holder for where condition preparing.
type whereHolder struct {
	operator int           // Operator for this holder.
	where    interface{}   // Where parameter.
	args     []interface{} // Arguments for where parameter.
}

const (
	OPTION_OMITEMPTY  = 1 // Deprecated, use OptionOmitEmpty instead.
	OPTION_ALLOWEMPTY = 2 // Deprecated, use OptionAllowEmpty instead.
	OptionOmitEmpty   = 1
	OptionAllowEmpty  = 2
	linkTypeMaster    = 1
	linkTypeSlave     = 2
	whereHolderWhere  = 1
	whereHolderAnd    = 2
	whereHolderOr     = 3
)

// Table is alias of Core.Model.
// See Core.Model.
// Deprecated, use Model instead.
func (c *Core) Table(tableNameOrStruct ...interface{}) *Model {
	return c.db.Model(tableNameOrStruct...)
}

// Model creates and returns a new ORM model from given schema.
// The parameter `tableNameOrStruct` can be more than one table names, and also alias name, like:
// 1. Model names:
//    Model("user")
//    Model("user u")
//    Model("user, user_detail")
//    Model("user u, user_detail ud")
// 2. Model name with alias: Model("user", "u")
func (c *Core) Model(tableNameOrStruct ...interface{}) *Model {
	// With feature checks.
	if len(tableNameOrStruct) > 0 {
		if _, ok := tableNameOrStruct[0].(string); !ok {
			return c.With(tableNameOrStruct...)
		}
	}
	// Normal model creation.
	var (
		tableStr   = ""
		tableNames = make([]string, len(tableNameOrStruct))
	)
	for k, v := range tableNameOrStruct {
		if s, ok := v.(string); ok {
			tableNames[k] = s
			continue
		}
	}

	if len(tableNames) > 1 {
		tableStr = fmt.Sprintf(
			`%s AS %s`, c.db.QuotePrefixTableName(tableNames[0]), c.db.QuoteWord(tableNames[1]),
		)
	} else if len(tableNames) == 1 {
		tableStr = c.db.QuotePrefixTableName(tableNames[0])
	}
	return &Model{
		db:         c.db,
		tablesInit: tableStr,
		tables:     tableStr,
		fields:     "*",
		start:      -1,
		offset:     -1,
		option:     OptionAllowEmpty,
		filter:     true,
	}
}

// With creates and returns an ORM model based on meta data of given object.
func (c *Core) With(objects ...interface{}) *Model {
	return c.db.Model().With(objects...)
}

// Table is alias of tx.Model.
// Deprecated, use Model instead.
func (tx *TX) Table(tableNameOrStruct ...interface{}) *Model {
	return tx.Model(tableNameOrStruct...)
}

// Model acts like Core.Model except it operates on transaction.
// See Core.Model.
func (tx *TX) Model(tableNameOrStruct ...interface{}) *Model {
	model := tx.db.Model(tableNameOrStruct...)
	model.db = tx.db
	model.tx = tx
	return model
}

// With acts like Core.With except it operates on transaction.
// See Core.With.
func (tx *TX) With(object interface{}) *Model {
	return tx.Model().With(object)
}

// Ctx sets the context for current operation.
func (m *Model) Ctx(ctx context.Context) *Model {
	if ctx == nil {
		return m
	}
	model := m.getModel()
	model.db = model.db.Ctx(ctx)
	return model
}

// As sets an alias name for current table.
func (m *Model) As(as string) *Model {
	if m.tables != "" {
		model := m.getModel()
		split := " JOIN "
		if gstr.Contains(model.tables, split) {
			// For join table.
			array := gstr.Split(model.tables, split)
			array[len(array)-1], _ = gregex.ReplaceString(`(.+) ON`, fmt.Sprintf(`$1 AS %s ON`, as), array[len(array)-1])
			model.tables = gstr.Join(array, split)
		} else {
			// For base table.
			model.tables = gstr.TrimRight(model.tables) + " AS " + as
		}
		return model
	}
	return m
}

// DB sets/changes the db object for current operation.
func (m *Model) DB(db DB) *Model {
	model := m.getModel()
	model.db = db
	return model
}

// TX sets/changes the transaction for current operation.
func (m *Model) TX(tx *TX) *Model {
	model := m.getModel()
	model.db = tx.db
	model.tx = tx
	return model
}

// Schema sets the schema for current operation.
func (m *Model) Schema(schema string) *Model {
	model := m.getModel()
	model.schema = schema
	return model
}

// Clone creates and returns a new model which is a clone of current model.
// Note that it uses deep-copy for the clone.
func (m *Model) Clone() *Model {
	newModel := (*Model)(nil)
	if m.tx != nil {
		newModel = m.tx.Model(m.tablesInit)
	} else {
		newModel = m.db.Model(m.tablesInit)
	}
	*newModel = *m
	// Shallow copy slice attributes.
	if n := len(m.extraArgs); n > 0 {
		newModel.extraArgs = make([]interface{}, n)
		copy(newModel.extraArgs, m.extraArgs)
	}
	if n := len(m.whereHolder); n > 0 {
		newModel.whereHolder = make([]*whereHolder, n)
		copy(newModel.whereHolder, m.whereHolder)
	}
	if n := len(m.withArray); n > 0 {
		newModel.withArray = make([]interface{}, n)
		copy(newModel.withArray, m.withArray)
	}
	return newModel
}

// Master marks the following operation on master node.
func (m *Model) Master() *Model {
	model := m.getModel()
	model.linkType = linkTypeMaster
	return model
}

// Slave marks the following operation on slave node.
// Note that it makes sense only if there's any slave node configured.
func (m *Model) Slave() *Model {
	model := m.getModel()
	model.linkType = linkTypeSlave
	return model
}

// Safe marks this model safe or unsafe. If safe is true, it clones and returns a new model object
// whenever the operation done, or else it changes the attribute of current model.
func (m *Model) Safe(safe ...bool) *Model {
	if len(safe) > 0 {
		m.safe = safe[0]
	} else {
		m.safe = true
	}
	return m
}

// Args sets custom arguments for model operation.
func (m *Model) Args(args ...interface{}) *Model {
	model := m.getModel()
	model.extraArgs = append(model.extraArgs, args)
	return model
}
<commit_msg>improve Model function for struct parameter that can retrieve table name tag from<commit_after>// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package gdb

import (
	"context"
	"fmt"
	"github.com/gogf/gf/text/gregex"
	"time"

	"github.com/gogf/gf/text/gstr"
)

// Model is the DAO for ORM.
type Model struct {
	db            DB             // Underlying DB interface.
	tx            *TX            // Underlying TX interface.
	schema        string         // Custom database schema.
	linkType      int            // Mark for operation on master or slave.
	tablesInit    string         // Table names when model initialization.
	tables        string         // Operation table names, which can be more than one table names and aliases, like: "user", "user u", "user u, user_detail ud".
	fields        string         // Operation fields, multiple fields joined using char ','.
	fieldsEx      string         // Excluded operation fields, multiple fields joined using char ','.
	withArray     []interface{}  // Arguments for With feature.
	withAll       bool           // Enable model association operations on all objects that have "with" tag in the struct.
	extraArgs     []interface{}  // Extra custom arguments for sql.
	whereHolder   []*whereHolder // Condition strings for where operation.
	groupBy       string         // Used for "group by" statement.
	orderBy       string         // Used for "order by" statement.
	having        []interface{}  // Used for "having..." statement.
	start         int            // Used for "select ... start, limit ..." statement.
	limit         int            // Used for "select ... start, limit ..." statement.
	option        int            // Option for extra operation features.
	offset        int            // Offset statement for some databases grammar.
	data          interface{}    // Data for operation, which can be type of map/[]map/struct/*struct/string, etc.
	batch         int            // Batch number for batch Insert/Replace/Save operations.
	filter        bool           // Filter data and where key-value pairs according to the fields of the table.
	distinct      string         // Force the query to only return distinct results.
	lockInfo      string         // Lock for update or in shared lock.
	cacheEnabled  bool           // Enable sql result cache feature.
	cacheDuration time.Duration  // Cache TTL duration.
	cacheName     string         // Cache name for custom operation.
	unscoped      bool           // Disables soft deleting features when select/delete operations.
	safe          bool           // If true, it clones and returns a new model object whenever operation done; or else it changes the attribute of current model.
}

// whereHolder is the holder for where condition preparing.
type whereHolder struct {
	operator int           // Operator for this holder.
	where    interface{}   // Where parameter.
	args     []interface{} // Arguments for where parameter.
}

const (
	OPTION_OMITEMPTY  = 1 // Deprecated, use OptionOmitEmpty instead.
	OPTION_ALLOWEMPTY = 2 // Deprecated, use OptionAllowEmpty instead.
	OptionOmitEmpty   = 1
	OptionAllowEmpty  = 2
	linkTypeMaster    = 1
	linkTypeSlave     = 2
	whereHolderWhere  = 1
	whereHolderAnd    = 2
	whereHolderOr     = 3
)

// Table is alias of Core.Model.
// See Core.Model.
// Deprecated, use Model instead.
func (c *Core) Table(tableNameOrStruct ...interface{}) *Model {
	return c.db.Model(tableNameOrStruct...)
}

// Model creates and returns a new ORM model from given schema.
// The parameter `tableNameOrStruct` can be more than one table names, and also alias name, like:
// 1. Model names:
//    Model("user")
//    Model("user u")
//    Model("user, user_detail")
//    Model("user u, user_detail ud")
// 2. Model name with alias: Model("user", "u")
func (c *Core) Model(tableNameOrStruct ...interface{}) *Model {
	var (
		tableStr   = ""
		tableName  = ""
		tableNames = make([]string, len(tableNameOrStruct))
	)
	for k, v := range tableNameOrStruct {
		if s, ok := v.(string); ok {
			tableNames[k] = s
		} else if tableName = getTableNameFromOrmTag(v); tableName != "" {
			tableNames[k] = tableName
		}
	}

	if len(tableNames) > 1 {
		tableStr = fmt.Sprintf(
			`%s AS %s`, c.db.QuotePrefixTableName(tableNames[0]), c.db.QuoteWord(tableNames[1]),
		)
	} else if len(tableNames) == 1 {
		tableStr = c.db.QuotePrefixTableName(tableNames[0])
	}
	return &Model{
		db:         c.db,
		tablesInit: tableStr,
		tables:     tableStr,
		fields:     "*",
		start:      -1,
		offset:     -1,
		option:     OptionAllowEmpty,
		filter:     true,
	}
}

// With creates and returns an ORM model based on meta data of given object.
func (c *Core) With(objects ...interface{}) *Model {
	return c.db.Model().With(objects...)
}

// Table is alias of tx.Model.
// Deprecated, use Model instead.
func (tx *TX) Table(tableNameOrStruct ...interface{}) *Model {
	return tx.Model(tableNameOrStruct...)
}

// Model acts like Core.Model except it operates on transaction.
// See Core.Model.
func (tx *TX) Model(tableNameOrStruct ...interface{}) *Model {
	model := tx.db.Model(tableNameOrStruct...)
	model.db = tx.db
	model.tx = tx
	return model
}

// With acts like Core.With except it operates on transaction.
// See Core.With.
func (tx *TX) With(object interface{}) *Model {
	return tx.Model().With(object)
}

// Ctx sets the context for current operation.
func (m *Model) Ctx(ctx context.Context) *Model {
	if ctx == nil {
		return m
	}
	model := m.getModel()
	model.db = model.db.Ctx(ctx)
	return model
}

// As sets an alias name for current table.
func (m *Model) As(as string) *Model {
	if m.tables != "" {
		model := m.getModel()
		split := " JOIN "
		if gstr.Contains(model.tables, split) {
			// For join table.
			array := gstr.Split(model.tables, split)
			array[len(array)-1], _ = gregex.ReplaceString(`(.+) ON`, fmt.Sprintf(`$1 AS %s ON`, as), array[len(array)-1])
			model.tables = gstr.Join(array, split)
		} else {
			// For base table.
			model.tables = gstr.TrimRight(model.tables) + " AS " + as
		}
		return model
	}
	return m
}

// DB sets/changes the db object for current operation.
func (m *Model) DB(db DB) *Model {
	model := m.getModel()
	model.db = db
	return model
}

// TX sets/changes the transaction for current operation.
func (m *Model) TX(tx *TX) *Model {
	model := m.getModel()
	model.db = tx.db
	model.tx = tx
	return model
}

// Schema sets the schema for current operation.
func (m *Model) Schema(schema string) *Model {
	model := m.getModel()
	model.schema = schema
	return model
}

// Clone creates and returns a new model which is a clone of current model.
// Note that it uses deep-copy for the clone.
func (m *Model) Clone() *Model {
	newModel := (*Model)(nil)
	if m.tx != nil {
		newModel = m.tx.Model(m.tablesInit)
	} else {
		newModel = m.db.Model(m.tablesInit)
	}
	*newModel = *m
	// Shallow copy slice attributes.
	if n := len(m.extraArgs); n > 0 {
		newModel.extraArgs = make([]interface{}, n)
		copy(newModel.extraArgs, m.extraArgs)
	}
	if n := len(m.whereHolder); n > 0 {
		newModel.whereHolder = make([]*whereHolder, n)
		copy(newModel.whereHolder, m.whereHolder)
	}
	if n := len(m.withArray); n > 0 {
		newModel.withArray = make([]interface{}, n)
		copy(newModel.withArray, m.withArray)
	}
	return newModel
}

// Master marks the following operation on master node.
func (m *Model) Master() *Model {
	model := m.getModel()
	model.linkType = linkTypeMaster
	return model
}

// Slave marks the following operation on slave node.
// Note that it makes sense only if there's any slave node configured.
func (m *Model) Slave() *Model {
	model := m.getModel()
	model.linkType = linkTypeSlave
	return model
}

// Safe marks this model safe or unsafe. If safe is true, it clones and returns a new model object
// whenever the operation done, or else it changes the attribute of current model.
func (m *Model) Safe(safe ...bool) *Model {
	if len(safe) > 0 {
		m.safe = safe[0]
	} else {
		m.safe = true
	}
	return m
}

// Args sets custom arguments for model operation.
func (m *Model) Args(args ...interface{}) *Model {
	model := m.getModel()
	model.extraArgs = append(model.extraArgs, args)
	return model
}
<|endoftext|>


<commit_before>/*
 * Copyright ©1998-2022 by Richard A. Wilkes. All rights reserved.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, version 2.0. If a copy of the MPL was not distributed with
 * this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This Source Code Form is "Incompatible With Secondary Licenses", as
 * defined by the Mozilla Public License, version 2.0.
 */

package ux

import (
	"fmt"

	"github.com/richardwilkes/gcs/v5/model"
	"github.com/richardwilkes/gcs/v5/model/fxp"
	"github.com/richardwilkes/toolbox/i18n"
	"github.com/richardwilkes/toolbox/log/jot"
	"github.com/richardwilkes/unison"
)

// PrimaryAttrPanel holds the contents of the primary attributes block on the sheet.
type PrimaryAttrPanel struct {
	unison.Panel
	entity    *model.Entity
	targetMgr *TargetMgr
	prefix    string
	crc       uint64
}

// NewPrimaryAttrPanel creates a new primary attributes panel.
func NewPrimaryAttrPanel(entity *model.Entity, targetMgr *TargetMgr) *PrimaryAttrPanel {
	p := &PrimaryAttrPanel{
		entity:    entity,
		targetMgr: targetMgr,
		prefix:    targetMgr.NextPrefix(),
	}
	p.Self = p
	p.SetLayout(&unison.FlexLayout{
		Columns:  3,
		HSpacing: 4,
	})
	p.SetLayoutData(&unison.FlexLayoutData{
		HAlign: unison.FillAlignment,
		VAlign: unison.FillAlignment,
	})
	p.SetBorder(unison.NewCompoundBorder(&TitledBorder{Title: i18n.Text("Primary Attributes")}, unison.NewEmptyBorder(unison.Insets{
		Top:    1,
		Left:   2,
		Bottom: 1,
		Right:  2,
	})))
	p.DrawCallback = func(gc *unison.Canvas, rect unison.Rect) {
		gc.DrawRect(rect, unison.ContentColor.Paint(gc, rect, unison.Fill))
	}
	attrs := model.SheetSettingsFor(p.entity).Attributes
	p.crc = attrs.CRC64()
	p.rebuild(attrs)
	return p
}

func (p *PrimaryAttrPanel) rebuild(attrs *model.AttributeDefs) {
	focusRefKey := p.targetMgr.CurrentFocusRef()
	p.RemoveAllChildren()
	for _, def := range attrs.List(false) {
		if def.Primary() {
			if def.Type == model.PrimarySeparatorAttributeType {
				p.AddChild(NewPageInternalHeader(def.Name, 3))
			} else {
				attr, ok := p.entity.Attributes.Set[def.ID()]
				if !ok {
					jot.Warnf("unable to locate attribute data for '%s'", def.ID())
					continue
				}
				p.AddChild(p.createPointsField(attr))
				p.AddChild(p.createValueField(def, attr))
				p.AddChild(NewPageLabel(def.CombinedName()))
			}
		}
	}
	if p.targetMgr != nil {
		if sheet := unison.Ancestor[*Sheet](p); sheet != nil {
			p.targetMgr.ReacquireFocus(focusRefKey, sheet.toolbar, sheet.scroll.Content())
		}
	}
}

func (p *PrimaryAttrPanel) createPointsField(attr *model.Attribute) *NonEditablePageField {
	field := NewNonEditablePageFieldEnd(func(f *NonEditablePageField) {
		if text := "[" + attr.PointCost().String() + "]"; text != f.Text {
			f.Text = text
			MarkForLayoutWithinDockable(f)
		}
		if def := attr.AttributeDef(); def != nil {
			f.Tooltip = unison.NewTooltipWithText(fmt.Sprintf(i18n.Text("Points spent on %s"), def.CombinedName()))
		}
	})
	field.Font = model.PageFieldSecondaryFont
	return field
}

func (p *PrimaryAttrPanel) createValueField(def *model.AttributeDef, attr *model.Attribute) *DecimalField {
	field := NewDecimalPageField(p.targetMgr, p.prefix+attr.AttrID, def.CombinedName(),
		func() fxp.Int { return attr.Maximum() },
		func(v fxp.Int) { attr.SetMaximum(v) }, fxp.Min, fxp.Max, true)
	return field
}

// Sync the panel to the current data.
func (p *PrimaryAttrPanel) Sync() {
	attrs := model.SheetSettingsFor(p.entity).Attributes
	if crc := attrs.CRC64(); crc != p.crc {
		p.crc = crc
		p.rebuild(attrs)
		MarkForLayoutWithinDockable(p)
	}
}
<commit_msg>Include primary attributes in display-only support<commit_after>/*
 * Copyright ©1998-2022 by Richard A. Wilkes. All rights reserved.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, version 2.0. If a copy of the MPL was not distributed with
 * this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This Source Code Form is "Incompatible With Secondary Licenses", as
 * defined by the Mozilla Public License, version 2.0.
 */

package ux

import (
	"fmt"

	"github.com/richardwilkes/gcs/v5/model"
	"github.com/richardwilkes/gcs/v5/model/fxp"
	"github.com/richardwilkes/toolbox/i18n"
	"github.com/richardwilkes/toolbox/log/jot"
	"github.com/richardwilkes/unison"
)

// PrimaryAttrPanel holds the contents of the primary attributes block on the sheet.
type PrimaryAttrPanel struct {
	unison.Panel
	entity    *model.Entity
	targetMgr *TargetMgr
	prefix    string
	crc       uint64
}

// NewPrimaryAttrPanel creates a new primary attributes panel.
func NewPrimaryAttrPanel(entity *model.Entity, targetMgr *TargetMgr) *PrimaryAttrPanel {
	p := &PrimaryAttrPanel{
		entity:    entity,
		targetMgr: targetMgr,
		prefix:    targetMgr.NextPrefix(),
	}
	p.Self = p
	p.SetLayout(&unison.FlexLayout{
		Columns:  3,
		HSpacing: 4,
	})
	p.SetLayoutData(&unison.FlexLayoutData{
		HAlign: unison.FillAlignment,
		VAlign: unison.FillAlignment,
	})
	p.SetBorder(unison.NewCompoundBorder(&TitledBorder{Title: i18n.Text("Primary Attributes")}, unison.NewEmptyBorder(unison.Insets{
		Top:    1,
		Left:   2,
		Bottom: 1,
		Right:  2,
	})))
	p.DrawCallback = func(gc *unison.Canvas, rect unison.Rect) {
		gc.DrawRect(rect, unison.ContentColor.Paint(gc, rect, unison.Fill))
	}
	attrs := model.SheetSettingsFor(p.entity).Attributes
	p.crc = attrs.CRC64()
	p.rebuild(attrs)
	return p
}

func (p *PrimaryAttrPanel) rebuild(attrs *model.AttributeDefs) {
	focusRefKey := p.targetMgr.CurrentFocusRef()
	p.RemoveAllChildren()
	for _, def := range attrs.List(false) {
		if def.Primary() {
			if def.Type == model.PrimarySeparatorAttributeType {
				p.AddChild(NewPageInternalHeader(def.Name, 3))
			} else {
				attr, ok := p.entity.Attributes.Set[def.ID()]
				if !ok {
					jot.Warnf("unable to locate attribute data for '%s'", def.ID())
					continue
				}
				if def.Type == model.IntegerRefAttributeType || def.Type == model.DecimalRefAttributeType {
					field := NewNonEditablePageFieldEnd(func(field *NonEditablePageField) {
						field.Text = attr.Maximum().String()
					})
					field.SetLayoutData(&unison.FlexLayoutData{
						HSpan:  2,
						HAlign: unison.FillAlignment,
						VAlign: unison.MiddleAlignment,
					})
					p.AddChild(field)
				} else {
					p.AddChild(p.createPointsField(attr))
					p.AddChild(p.createValueField(def, attr))
				}
				p.AddChild(NewPageLabel(def.CombinedName()))
			}
		}
	}
	if p.targetMgr != nil {
		if sheet := unison.Ancestor[*Sheet](p); sheet != nil {
			p.targetMgr.ReacquireFocus(focusRefKey, sheet.toolbar, sheet.scroll.Content())
		}
	}
}

func (p *PrimaryAttrPanel) createPointsField(attr *model.Attribute) *NonEditablePageField {
	field := NewNonEditablePageFieldEnd(func(f *NonEditablePageField) {
		if text := "[" + attr.PointCost().String() + "]"; text != f.Text {
			f.Text = text
			MarkForLayoutWithinDockable(f)
		}
		if def := attr.AttributeDef(); def != nil {
			f.Tooltip = unison.NewTooltipWithText(fmt.Sprintf(i18n.Text("Points spent on %s"), def.CombinedName()))
		}
	})
	field.Font = model.PageFieldSecondaryFont
	return field
}

func (p *PrimaryAttrPanel) createValueField(def *model.AttributeDef, attr *model.Attribute) *DecimalField {
	return NewDecimalPageField(p.targetMgr, p.prefix+attr.AttrID, def.CombinedName(),
		func() fxp.Int { return attr.Maximum() },
		func(v fxp.Int) { attr.SetMaximum(v) }, fxp.Min, fxp.Max, true)
}

// Sync the panel to the current data.
func (p *PrimaryAttrPanel) Sync() {
	attrs := model.SheetSettingsFor(p.entity).Attributes
	if crc := attrs.CRC64(); crc != p.crc {
		p.crc = crc
		p.rebuild(attrs)
		MarkForLayoutWithinDockable(p)
	}
}
<|endoftext|>


<commit_before>package s3

import (
	"fmt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/corehandlers"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/request"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/smira/aptly/aptly"
	"github.com/smira/aptly/files"
	"github.com/smira/go-aws-auth"
	"os"
	"path/filepath"
	"strings"
)

// PublishedStorage abstract file system with published files (actually hosted on S3)
type PublishedStorage struct {
	s3               *s3.S3
	config           *aws.Config
	bucket           string
	acl              string
	prefix           string
	storageClass     string
	encryptionMethod string
	plusWorkaround   bool
	disableMultiDel  bool
	pathCache        map[string]string
}

// Check interface
var (
	_ aptly.PublishedStorage = (*PublishedStorage)(nil)
)

// NewPublishedStorageRaw creates published storage from raw aws credentials
func NewPublishedStorageRaw(
	bucket, defaultACL, prefix, storageClass, encryptionMethod string,
	plusWorkaround, disabledMultiDel bool,
	config *aws.Config,
) (*PublishedStorage, error) {
	if defaultACL == "" {
		defaultACL = "private"
	}

	if storageClass == "STANDARD" {
		storageClass = ""
	}

	sess := session.New(config)

	result := &PublishedStorage{
		s3:               s3.New(sess),
		bucket:           bucket,
		config:           config,
		acl:              defaultACL,
		prefix:           prefix,
		storageClass:     storageClass,
		encryptionMethod: encryptionMethod,
		plusWorkaround:   plusWorkaround,
		disableMultiDel:  disabledMultiDel,
	}

	return result, nil
}

func signV2(req *request.Request) {
	awsauth.SignS3(req.HTTPRequest)
}

// NewPublishedStorage creates new instance of PublishedStorage with specified S3 access
// keys, region and bucket name
func NewPublishedStorage(accessKey, secretKey, sessionToken, region, endpoint, bucket, defaultACL, prefix,
	storageClass, encryptionMethod string, plusWorkaround, disableMultiDel, forceSigV2, debug bool) (*PublishedStorage, error) {

	config := &aws.Config{
		Region: aws.String(region),
	}

	if endpoint != "" {
		config = config.WithEndpoint(endpoint).WithS3ForcePathStyle(true)
	}

	if accessKey != "" {
		config.Credentials = credentials.NewStaticCredentials(accessKey, secretKey, sessionToken)
	}

	if debug {
		config = config.WithLogLevel(aws.LogDebug)
	}

	result, err := NewPublishedStorageRaw(bucket, defaultACL, prefix, storageClass,
		encryptionMethod, plusWorkaround, disableMultiDel, config)

	if err == nil && forceSigV2 {
		result.s3.Handlers.Sign.Clear()
		result.s3.Handlers.Sign.PushBackNamed(corehandlers.BuildContentLengthHandler)
		result.s3.Handlers.Sign.PushBack(signV2)
	}

	return result, err
}

// String
func (storage *PublishedStorage) String() string {
	return fmt.Sprintf("S3: %s:%s/%s", *storage.config.Region, storage.bucket, storage.prefix)
}

// MkDir creates directory recursively under public path
func (storage *PublishedStorage) MkDir(path string) error {
	// no op for S3
	return nil
}

// PutFile puts file into published storage at specified path
func (storage *PublishedStorage) PutFile(path string, sourceFilename string) error {
	var (
		source *os.File
		err    error
	)
	source, err = os.Open(sourceFilename)
	if err != nil {
		return err
	}
	defer source.Close()

	params := &s3.PutObjectInput{
		Bucket: aws.String(storage.bucket),
		Key:    aws.String(filepath.Join(storage.prefix, path)),
		Body:   source,
		ACL:    aws.String(storage.acl),
	}
	if storage.storageClass != "" {
		params.StorageClass = aws.String(storage.storageClass)
	}
	if storage.encryptionMethod != "" {
		params.ServerSideEncryption = aws.String(storage.encryptionMethod)
	}

	_, err = storage.s3.PutObject(params)
	if err != nil {
		return fmt.Errorf("error uploading %s to %s: %s", sourceFilename, storage, err)
	}

	if storage.plusWorkaround && strings.Index(path, "+") != -1 {
		return storage.PutFile(strings.Replace(path, "+", " ", -1), sourceFilename)
	}
	return nil
}

// Remove removes single file under public path
func (storage *PublishedStorage) Remove(path string) error {
	params := &s3.DeleteObjectInput{
		Bucket: aws.String(storage.bucket),
		Key:    aws.String(path),
	}
	_, err := storage.s3.DeleteObject(params)
	if err != nil {
		return fmt.Errorf("error deleting %s from %s: %s", path, storage, err)
	}

	if storage.plusWorkaround && strings.Index(path, "+") != -1 {
		// try to remove workaround version, but don't care about result
		_ = storage.Remove(strings.Replace(path, "+", " ", -1))
	}
	return nil
}

// RemoveDirs removes directory structure under public path
func (storage *PublishedStorage) RemoveDirs(path string, progress aptly.Progress) error {
	const page = 1000

	filelist, _, err := storage.internalFilelist(path, false)
	if err != nil {
		return err
	}

	if storage.disableMultiDel {
		for i := range filelist {
			params := &s3.DeleteObjectInput{
				Bucket: aws.String(storage.bucket),
				Key:    aws.String(filepath.Join(storage.prefix, path, filelist[i])),
			}
			_, err := storage.s3.DeleteObject(params)
			if err != nil {
				return fmt.Errorf("error deleting path %s from %s: %s", filelist[i], storage, err)
			}
		}
	} else {
		numParts := (len(filelist) + page - 1) / page

		for i := 0; i < numParts; i++ {
			var part []string
			if i == numParts-1 {
				part = filelist[i*page:]
			} else {
				part = filelist[i*page : (i+1)*page]
			}
			paths := make([]*s3.ObjectIdentifier, len(part))

			for i := range part {
				paths[i] = &s3.ObjectIdentifier{
					Key: aws.String(filepath.Join(storage.prefix, path, part[i])),
				}
			}

			params := &s3.DeleteObjectsInput{
				Bucket: aws.String(storage.bucket),
				Delete: &s3.Delete{
					Objects: paths,
					Quiet:   aws.Bool(true),
				},
			}

			_, err := storage.s3.DeleteObjects(params)
			if err != nil {
				return fmt.Errorf("error deleting multiple paths from %s: %s", storage, err)
			}
		}
	}

	return nil
}

// LinkFromPool links package file from pool to dist's pool location
//
// publishedDirectory is desired location in pool (like prefix/pool/component/liba/libav/)
// sourcePool is instance of aptly.PackagePool
// sourcePath is filepath to package file in package pool
//
// LinkFromPool returns relative path for the published file to be included in package index
func (storage *PublishedStorage) LinkFromPool(publishedDirectory string, sourcePool aptly.PackagePool,
	sourcePath, sourceMD5 string, force bool) error {
	// verify that package pool is local pool in filesystem
	_ = sourcePool.(*files.PackagePool)

	baseName := filepath.Base(sourcePath)
	relPath := filepath.Join(publishedDirectory, baseName)
	poolPath := filepath.Join(storage.prefix, relPath)

	var (
		err error
	)

	if storage.pathCache == nil {
		paths, md5s, err := storage.internalFilelist(storage.prefix, true)
		if err != nil {
			return fmt.Errorf("error caching paths under prefix: %s", err)
		}

		storage.pathCache = make(map[string]string, len(paths))

		for i := range paths {
			storage.pathCache[paths[i]] = md5s[i]
		}
	}

	destinationMD5, exists := storage.pathCache[relPath]

	if exists {
		if destinationMD5 == sourceMD5 {
			return nil
		}

		if !force && destinationMD5 != sourceMD5 {
			return fmt.Errorf("error putting file to %s: file already exists and is different: %s", poolPath, storage)

		}
	}

	err = storage.PutFile(relPath, sourcePath)
	if err == nil {
		storage.pathCache[relPath] = sourceMD5
	}

	return err
}

// Filelist returns list of files under prefix
func (storage *PublishedStorage) Filelist(prefix string) ([]string, error) {
	paths, _, err := storage.internalFilelist(prefix, true)
	return paths, err
}

func (storage *PublishedStorage) internalFilelist(prefix string, hidePlusWorkaround bool) (paths []string, md5s []string, err error) {
	paths = make([]string, 0, 1024)
	md5s = make([]string, 0, 1024)
	marker := ""
	prefix = filepath.Join(storage.prefix, prefix)
	if prefix != "" {
		prefix += "/"
	}
	for {
		params := &s3.ListObjectsInput{
			Bucket:  aws.String(storage.bucket),
			Prefix:  aws.String(prefix),
			MaxKeys: aws.Int64(1000),
		}

		contents, err := storage.s3.ListObjects(params)
		if err != nil {
			return nil, nil, fmt.Errorf("error listing under prefix %s in %s: %s", prefix, storage, err)
		}
		lastKey := ""
		for _, key := range contents.Contents {
			lastKey = *key.Key
			if storage.plusWorkaround && hidePlusWorkaround && strings.Index(lastKey, " ") != -1 {
				// if we use plusWorkaround, we want to hide those duplicates
				/// from listing
				continue
			}

			if prefix == "" {
				paths = append(paths, *key.Key)
			} else {
				paths = append(paths, (*key.Key)[len(prefix):])
			}
			md5s = append(md5s, strings.Replace(*key.ETag, "\"", "", -1))
		}
		if contents.IsTruncated != nil && *contents.IsTruncated {
			marker = ""
			if contents.NextMarker != nil {
				marker = *contents.NextMarker
			}
			if marker == "" {
				// From the s3 docs: If response does not include the
				// NextMarker and it is truncated, you can use the value of the
				// last Key in the response as the marker in the subsequent
				// request to get the next set of object keys.
				marker = lastKey
			}
		} else {
			break
		}
	}

	return paths, md5s, nil
}

// RenameFile renames (moves) file
func (storage *PublishedStorage) RenameFile(oldName, newName string) error {
	source := fmt.Sprintf("/%s/%s", storage.bucket, filepath.Join(storage.prefix, oldName))

	params := &s3.CopyObjectInput{
		Bucket:     aws.String(storage.bucket),
		CopySource: aws.String(source),
		Key:        aws.String(filepath.Join(storage.prefix, newName)),
		ACL:        aws.String(storage.acl),
	}

	_, err := storage.s3.CopyObject(params)
	if err != nil {
		return fmt.Errorf("error copying %s -> %s in %s: %s", oldName, newName, storage, err)
	}

	return storage.Remove(oldName)
}
<commit_msg>Replace object listing with SDK-standard iteration.<commit_after>package s3

import (
	"fmt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/corehandlers"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/request"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/smira/aptly/aptly"
	"github.com/smira/aptly/files"
	"github.com/smira/go-aws-auth"
	"os"
	"path/filepath"
	"strings"
)

// PublishedStorage abstract file system with published files (actually hosted on S3)
type PublishedStorage struct {
	s3               *s3.S3
	config           *aws.Config
	bucket           string
	acl              string
	prefix           string
	storageClass     string
	encryptionMethod string
	plusWorkaround   bool
	disableMultiDel  bool
	pathCache        map[string]string
}

// Check interface
var (
	_ aptly.PublishedStorage = (*PublishedStorage)(nil)
)

// NewPublishedStorageRaw creates published storage from raw aws credentials
func NewPublishedStorageRaw(
	bucket, defaultACL, prefix, storageClass, encryptionMethod string,
	plusWorkaround, disabledMultiDel bool,
	config *aws.Config,
) (*PublishedStorage, error) {
	if defaultACL == "" {
		defaultACL = "private"
	}

	if storageClass == "STANDARD" {
		storageClass = ""
	}

	sess := session.New(config)

	result := &PublishedStorage{
		s3:               s3.New(sess),
		bucket:           bucket,
		config:           config,
		acl:              defaultACL,
		prefix:           prefix,
		storageClass:     storageClass,
		encryptionMethod: encryptionMethod,
		plusWorkaround:   plusWorkaround,
		disableMultiDel:  disabledMultiDel,
	}

	return result, nil
}

func signV2(req *request.Request) {
	awsauth.SignS3(req.HTTPRequest)
}

// NewPublishedStorage creates new instance of PublishedStorage with specified S3 access
// keys, region and bucket name
func NewPublishedStorage(accessKey, secretKey, sessionToken, region, endpoint, bucket, defaultACL, prefix,
	storageClass, encryptionMethod string, plusWorkaround, disableMultiDel, forceSigV2, debug bool) (*PublishedStorage, error) {

	config := &aws.Config{
		Region: aws.String(region),
	}

	if endpoint != "" {
		config = config.WithEndpoint(endpoint).WithS3ForcePathStyle(true)
	}

	if accessKey != "" {
		config.Credentials = credentials.NewStaticCredentials(accessKey, secretKey, sessionToken)
	}

	if debug {
		config = config.WithLogLevel(aws.LogDebug)
	}

	result, err := NewPublishedStorageRaw(bucket, defaultACL, prefix, storageClass,
		encryptionMethod, plusWorkaround, disableMultiDel, config)

	if err == nil && forceSigV2 {
		result.s3.Handlers.Sign.Clear()
		result.s3.Handlers.Sign.PushBackNamed(corehandlers.BuildContentLengthHandler)
		result.s3.Handlers.Sign.PushBack(signV2)
	}

	return result, err
}

// String
func (storage *PublishedStorage) String() string {
	return fmt.Sprintf("S3: %s:%s/%s", *storage.config.Region, storage.bucket, storage.prefix)
}

// MkDir creates directory recursively under public path
func (storage *PublishedStorage) MkDir(path string) error {
	// no op for S3
	return nil
}

// PutFile puts file into published storage at specified path
func (storage *PublishedStorage) PutFile(path string, sourceFilename string) error {
	var (
		source *os.File
		err    error
	)
	source, err = os.Open(sourceFilename)
	if err != nil {
		return err
	}
	defer source.Close()

	params := &s3.PutObjectInput{
		Bucket: aws.String(storage.bucket),
		Key:    aws.String(filepath.Join(storage.prefix, path)),
		Body:   source,
		ACL:    aws.String(storage.acl),
	}
	if storage.storageClass != "" {
		params.StorageClass = aws.String(storage.storageClass)
	}
	if storage.encryptionMethod != "" {
		params.ServerSideEncryption = aws.String(storage.encryptionMethod)
	}

	_, err = storage.s3.PutObject(params)
	if err != nil {
		return fmt.Errorf("error uploading %s to %s: %s", sourceFilename, storage, err)
	}

	if storage.plusWorkaround && strings.Index(path, "+") != -1 {
		return storage.PutFile(strings.Replace(path, "+", " ", -1), sourceFilename)
	}
	return nil
}

// Remove removes single file under public path
func (storage *PublishedStorage) Remove(path string) error {
	params := &s3.DeleteObjectInput{
		Bucket: aws.String(storage.bucket),
		Key:    aws.String(path),
	}
	_, err := storage.s3.DeleteObject(params)
	if err != nil {
		return fmt.Errorf("error deleting %s from %s: %s", path, storage, err)
	}

	if storage.plusWorkaround && strings.Index(path, "+") != -1 {
		// try to remove workaround version, but don't care about result
		_ = storage.Remove(strings.Replace(path, "+", " ", -1))
	}
	return nil
}

// RemoveDirs removes directory structure under public path
func (storage *PublishedStorage) RemoveDirs(path string, progress aptly.Progress) error {
	const page = 1000

	filelist, _, err := storage.internalFilelist(path, false)
	if err != nil {
		return err
	}

	if storage.disableMultiDel {
		for i := range filelist {
			params := &s3.DeleteObjectInput{
				Bucket: aws.String(storage.bucket),
				Key:    aws.String(filepath.Join(storage.prefix, path, filelist[i])),
			}
			_, err := storage.s3.DeleteObject(params)
			if err != nil {
				return fmt.Errorf("error deleting path %s from %s: %s", filelist[i], storage, err)
			}
		}
	} else {
		numParts := (len(filelist) + page - 1) / page

		for i := 0; i < numParts; i++ {
			var part []string
			if i == numParts-1 {
				part = filelist[i*page:]
			} else {
				part = filelist[i*page : (i+1)*page]
			}
			paths := make([]*s3.ObjectIdentifier, len(part))

			for i := range part {
				paths[i] = &s3.ObjectIdentifier{
					Key: aws.String(filepath.Join(storage.prefix, path, part[i])),
				}
			}

			params := &s3.DeleteObjectsInput{
				Bucket: aws.String(storage.bucket),
				Delete: &s3.Delete{
					Objects: paths,
					Quiet:   aws.Bool(true),
				},
			}

			_, err := storage.s3.DeleteObjects(params)
			if err != nil {
				return fmt.Errorf("error deleting multiple paths from %s: %s", storage, err)
			}
		}
	}

	return nil
}

// LinkFromPool links package file from pool to dist's pool location
//
// publishedDirectory is desired location in pool (like prefix/pool/component/liba/libav/)
// sourcePool is instance of aptly.PackagePool
// sourcePath is filepath to package file in package pool
//
// LinkFromPool returns relative path for the published file to be included in package index
func (storage *PublishedStorage) LinkFromPool(publishedDirectory string, sourcePool aptly.PackagePool,
	sourcePath, sourceMD5 string, force bool) error {
	// verify that package pool is local pool in filesystem
	_ = sourcePool.(*files.PackagePool)

	baseName := filepath.Base(sourcePath)
	relPath := filepath.Join(publishedDirectory, baseName)
	poolPath := filepath.Join(storage.prefix, relPath)

	var (
		err error
	)

	if storage.pathCache == nil {
		paths, md5s, err := storage.internalFilelist(storage.prefix, true)
		if err != nil {
			return fmt.Errorf("error caching paths under prefix: %s", err)
		}

		storage.pathCache = make(map[string]string, len(paths))

		for i := range paths {
			storage.pathCache[paths[i]] = md5s[i]
		}
	}

	destinationMD5, exists := storage.pathCache[relPath]

	if exists {
		if destinationMD5 == sourceMD5 {
			return nil
		}

		if !force && destinationMD5 != sourceMD5 {
			return fmt.Errorf("error putting file to %s: file already exists and is different: %s", poolPath, storage)

		}
	}

	err = storage.PutFile(relPath, sourcePath)
	if err == nil {
		storage.pathCache[relPath] = sourceMD5
	}

	return err
}

// Filelist returns list of files under prefix
func (storage *PublishedStorage) Filelist(prefix string) ([]string, error) {
	paths, _, err := storage.internalFilelist(prefix, true)
	return paths, err
}

func (storage *PublishedStorage) internalFilelist(prefix string, hidePlusWorkaround bool) (paths []string, md5s []string, err error) {
	paths = make([]string, 0, 1024)
	md5s = make([]string, 0, 1024)
	prefix = filepath.Join(storage.prefix, prefix)
	if prefix != "" {
		prefix += "/"
	}

	params := &s3.ListObjectsInput{
		Bucket:  aws.String(storage.bucket),
		Prefix:  aws.String(prefix),
		MaxKeys: aws.Int64(1000),
	}

	err = storage.s3.ListObjectsPages(params, func(contents *s3.ListObjectsOutput, lastPage bool) bool {
		for _, key := range contents.Contents {
			if storage.plusWorkaround && hidePlusWorkaround && strings.Index(*key.Key, " ") != -1 {
				// if we use plusWorkaround, we want to hide those duplicates
				/// from listing
				continue
			}

			if prefix == "" {
				paths = append(paths, *key.Key)
			} else {
				paths = append(paths, (*key.Key)[len(prefix):])
			}
			md5s = append(md5s, strings.Replace(*key.ETag, "\"", "", -1))
		}

		return true
	})

	if err != nil {
		return nil, nil, fmt.Errorf("error listing under prefix %s in %s: %s", prefix, storage, err)
	}

	return paths, md5s, nil
}

// RenameFile renames (moves) file
func (storage *PublishedStorage) RenameFile(oldName, newName string) error {
	source := fmt.Sprintf("/%s/%s", storage.bucket, filepath.Join(storage.prefix, oldName))

	params := &s3.CopyObjectInput{
		Bucket:     aws.String(storage.bucket),
		CopySource: aws.String(source),
		Key:        aws.String(filepath.Join(storage.prefix, newName)),
		ACL:        aws.String(storage.acl),
	}

	_, err := storage.s3.CopyObject(params)
	if err != nil {
		return fmt.Errorf("error copying %s -> %s in %s: %s", oldName, newName, storage, err)
	}

	return storage.Remove(oldName)
}
<|endoftext|>


<commit_before>// gich  a cross platform which tool written in Go

// Copyright (c) 2010 Joseph D Poirier
// Distributable under the terms of The New BSD License
// that can be found in the LICENSE file.

package main

import (
	"flag"
	"fmt"
	"os"
	"syscall"
	"strings"
)

func init() {
	flag.BoolVar(&aFlag, "l", false, allMsg)
	flag.BoolVar(&sFlag, "s", false, statMsg)
	flag.BoolVar(&hFlag, "h", false, prntMsg)
	flag.BoolVar(&helpFlag, "help", false, prntMsg)
	if syscall.OS == "windows" {
		sepChar = `;`
		// TODO: Is this necessary? windows handles forward slashes
		// to what level, ie is it different between user and
		// kernel level, and does it matter?
		sepPath = `\`
		// TODO: Is this necessary? Any difference between
		// cmd.exe and command.com?
		eol = "\r\n"
		winFlag = true
	}
}

var usage = func() {
	fmt.Print(helpMsg)
	os.Exit(0)
}

func process(files, paths, exts []string) {
	userMsg := ""
outer:	for _, file := range files {
		if strings.Index(file, `\`) >= 0 || strings.Index(file, `/`) >= 0 {
			continue
		}
inner:		for _, path := range paths {
			if len(exts) != 0 {
				f := strings.ToLower(file)
				for _, e := range exts {
					ff := path + sepPath + file
					if !strings.HasSuffix(f, e) {
						ff += e
					}
					if _, err := os.Stat(ff); err == nil {
						if sFlag {
							userMsg = "Found\n"
							break outer
						}
						if aFlag {
							userMsg += ff + eol
							continue
						}
						userMsg += ff + eol
						continue outer
					}
				}
			} else {
				f := path + sepPath + file
				if _, err := os.Stat(f); err == nil {
					if sFlag {
						userMsg = "Found\n"
						break outer
					}
					if aFlag {
						userMsg += (f + eol)
						continue
					}
					userMsg += f + eol
					continue outer
				}
			}
		}
	}
	if sFlag && userMsg == "" {
		userMsg = "None\n"
	}
	fmt.Print(userMsg)
}

func prolog(files []string) {
	path := os.Getenv("PATH")
	if path == "" {
		return
	}
	paths := []string{}
	exts := []string{}
	if winFlag {
// TODO: Check for functionality differences between the
// DOS (command.com) and NT (cmd.exe) shells
//		path = strings.Replace(path, `\`, `\\`, -1)
		pathext := os.Getenv("PATHEXT")
		if pathext != "" {
			exts = strings.Split(strings.ToLower(pathext), sepChar, -1)
			for i, e := range exts {
				if e == "" || e[0] != '.' {
					exts[i] = "." + e
				}
			}
		}
// TODO: Check for functionality differences between the
// DOS (command.com) and NT (cmd.exe) shells
//		paths = strings.Split(path, sepChar, -1)
//		for i, p := range paths {
//			paths[i] = `"` + p + `"`
//		}
	}
	paths = strings.Split(path, sepChar, -1)
	process(files, paths, exts)
}

func main() {
	flag.Usage = usage
	flag.Parse()
	if len(os.Args) == 1 || hFlag || helpFlag || aFlag && sFlag {
		usage()
	}
	x := 1
	if aFlag || sFlag {
		x += 1
	}
	if (len(os.Args) - x) < 1 {
		usage()
	}
	prolog(os.Args[x:])
	os.Exit(0)
}
<commit_msg>removed unused inner label<commit_after>// gich  a cross platform which tool written in Go

// Copyright (c) 2010 Joseph D Poirier
// Distributable under the terms of The New BSD License
// that can be found in the LICENSE file.

package main

import (
	"flag"
	"fmt"
	"os"
	"syscall"
	"strings"
)

func init() {
	flag.BoolVar(&aFlag, "l", false, allMsg)
	flag.BoolVar(&sFlag, "s", false, statMsg)
	flag.BoolVar(&hFlag, "h", false, prntMsg)
	flag.BoolVar(&helpFlag, "help", false, prntMsg)
	if syscall.OS == "windows" {
		sepChar = `;`
		// TODO: Is this necessary? windows handles forward slashes
		// to what level, ie is it different between user and
		// kernel level, and does it matter?
		sepPath = `\`
		// TODO: Is this necessary? Any difference between
		// cmd.exe and command.com?
		eol = "\r\n"
		winFlag = true
	}
}

var usage = func() {
	fmt.Print(helpMsg)
	os.Exit(0)
}

func process(files, paths, exts []string) {
	userMsg := ""
outer:	for _, file := range files {
		if strings.Index(file, `\`) >= 0 || strings.Index(file, `/`) >= 0 {
			continue
		}
		for _, path := range paths {
			if len(exts) != 0 {
				f := strings.ToLower(file)
				for _, e := range exts {
					ff := path + sepPath + file
					if !strings.HasSuffix(f, e) {
						ff += e
					}
					if _, err := os.Stat(ff); err == nil {
						if sFlag {
							userMsg = "Found\n"
							break outer
						}
						if aFlag {
							userMsg += ff + eol
							continue
						}
						userMsg += ff + eol
						continue outer
					}
				}
			} else {
				f := path + sepPath + file
				if _, err := os.Stat(f); err == nil {
					if sFlag {
						userMsg = "Found\n"
						break outer
					}
					if aFlag {
						userMsg += (f + eol)
						continue
					}
					userMsg += f + eol
					continue outer
				}
			}
		}
	}
	if sFlag && userMsg == "" {
		userMsg = "None\n"
	}
	fmt.Print(userMsg)
}

func prolog(files []string) {
	path := os.Getenv("PATH")
	if path == "" {
		return
	}
	paths := []string{}
	exts := []string{}
	if winFlag {
// TODO: Check for functionality differences between the
// DOS (command.com) and NT (cmd.exe) shells
//		path = strings.Replace(path, `\`, `\\`, -1)
		pathext := os.Getenv("PATHEXT")
		if pathext != "" {
			exts = strings.Split(strings.ToLower(pathext), sepChar, -1)
			for i, e := range exts {
				if e == "" || e[0] != '.' {
					exts[i] = "." + e
				}
			}
		}
// TODO: Check for functionality differences between the
// DOS (command.com) and NT (cmd.exe) shells
//		paths = strings.Split(path, sepChar, -1)
//		for i, p := range paths {
//			paths[i] = `"` + p + `"`
//		}
	}
	paths = strings.Split(path, sepChar, -1)
	process(files, paths, exts)
}

func main() {
	flag.Usage = usage
	flag.Parse()
	if len(os.Args) == 1 || hFlag || helpFlag || aFlag && sFlag {
		usage()
	}
	x := 1
	if aFlag || sFlag {
		x += 1
	}
	if (len(os.Args) - x) < 1 {
		usage()
	}
	prolog(os.Args[x:])
	os.Exit(0)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"errors"
	"flag"
	"fmt"
	"log"
	"os"
	"regexp"
	"strconv"
	"strings"
)

const NOT_SPACE = "[^\\s]"

func readLine(scanner *bufio.Scanner, replace_lines IntSet, replace_strings StrSlice) {

	scanner.Split(bufio.ScanLines)

	line := 1

	ra, _ := regexp.Compile(NOT_SPACE)
	if len(replace_strings) > 0 {
		for i, e := range replace_strings {
			fmt.Println(i, e, "xxxxxxxxxx")
		}
	}

	for scanner.Scan() {
		_, s := replace_lines[line]
		if s {
			fmt.Printf("%d: %v\n", line, ra.ReplaceAllString(scanner.Text(), "-"))
		} else {
			if len(replace_strings) > 0 {
				fmt.Printf("%d: %v\n", line, ra.ReplaceAllString(scanner.Text(), "-"))

			} else {
				fmt.Printf("%d: %v\n", line, scanner.Text())
			}
		}
		line += 1
	}

	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}

}

func Exists(name string) bool {
	if _, err := os.Stat(name); err != nil {
		if os.IsNotExist(err) {
			return false
		}
	}
	return true
}

// set of ints
type IntSet map[int]struct{}

// set of strings
type StrSlice []string

func (i *IntSet) String() string {
	return fmt.Sprint(*i)
}

func (i *IntSet) Set(value string) error {
	if len(*i) > 0 {
		return errors.New("line flag already set")
	}
	for _, n := range strings.Split(value, ",") {
		num, err := strconv.Atoi(n)
		if err != nil {
			continue
		}
		if _, found := (*i)[num]; found {
			continue
		}
		(*i)[num] = struct{}{}
	}
	return nil
}

func (s *StrSlice) String() string {
	return fmt.Sprint(*s)
}

func (s *StrSlice) Set(value string) error {
	*s = append(*s, value)
	return nil
}

func main() {

	var replace_lines = IntSet{}
	var replace_strings StrSlice

	flag.Var(&replace_lines, "l", ">>>>>>>>>>>>>>>>> l")
	flag.Var(&replace_strings, "r", ">>>>>>>>>>>>>>> r")

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [-lr] file\n\n", os.Args[0])
		fmt.Printf("  example: %s -l 3,7 -r secret -r 'my passphrase' file.conf\n\n", os.Args[0])
		fmt.Println("  -l: Number of the line(s) to be replaced, comma separated")
		fmt.Println("  -r: Word to be replaced, can be used multiple times")
	}

	flag.Parse()

	if flag.NArg() != 1 {
		fmt.Fprintf(os.Stderr, "Usage: %s [-lrh] file, use -h for more info\n\n", os.Args[0])
		os.Exit(1)
	}

	stat, _ := os.Stdin.Stat()
	if (stat.Mode() & os.ModeCharDevice) == 0 {
		readLine(bufio.NewScanner(os.Stdin), replace_lines, replace_strings)
	} else {
		f := flag.Arg(0)
		if Exists(f) {
			file, err := os.Open(f)
			defer file.Close()
			if err != nil {
				log.Fatal(err)
			}
			readLine(bufio.NewScanner(file), replace_lines, replace_strings)
		} else {
			fmt.Printf("Cannot read file: %s\n", f)
		}
	}
}
<commit_msg>	modified:   gist.go<commit_after>package main

import (
	"bufio"
	"errors"
	"flag"
	"fmt"
	"log"
	"os"
	"regexp"
	"strconv"
	"strings"
)

const NOT_SPACE = "[^\\s]"

func readLine(scanner *bufio.Scanner, replace_lines IntSet, replace_strings StrSlice) {

	scanner.Split(bufio.ScanLines)

	line := 1

	ra, _ := regexp.Compile(NOT_SPACE)
	if len(replace_strings) > 0 {
		for i, e := range replace_strings {
			fmt.Println(i, e, "xxxxxxxxxx")
		}
	}

	for scanner.Scan() {
		_, s := replace_lines[line]
		if s {
			fmt.Printf("%d: %v\n", line, ra.ReplaceAllString(scanner.Text(), "-"))
		} else {
			if len(replace_strings) > 0 {
				fmt.Printf("%d: %v\n", line, ra.ReplaceAllString(scanner.Text(), "-"))

			} else {
				fmt.Printf("%d: %v\n", line, scanner.Text())
			}
		}
		line += 1
	}

	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}

}

func Exists(name string) bool {
	if _, err := os.Stat(name); err != nil {
		if os.IsNotExist(err) {
			return false
		}
	}
	return true
}

// set of ints
type IntSet map[int]struct{}

// set of strings
type StrSlice []string

func (i *IntSet) String() string {
	return fmt.Sprint(*i)
}

func (i *IntSet) Set(value string) error {
	if len(*i) > 0 {
		return errors.New("line flag already set")
	}
	for _, n := range strings.Split(value, ",") {
		num, err := strconv.Atoi(n)
		if err != nil {
			continue
		}
		if _, found := (*i)[num]; found {
			continue
		}
		(*i)[num] = struct{}{}
	}
	return nil
}

func (s *StrSlice) String() string {
	return fmt.Sprint(*s)
}

func (s *StrSlice) Set(value string) error {
	*s = append(*s, value)
	return nil
}

func main() {

	var replace_lines = IntSet{}
	var replace_strings StrSlice

	flag.Var(&replace_lines, "l", ">>>>>>>>>>>>>>>>> l")
	flag.Var(&replace_strings, "r", ">>>>>>>>>>>>>>> r")

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [-lr] file\n\n", os.Args[0])
		fmt.Printf("  example: %s -l 3,7 -r secret -r 'my passphrase' file.conf\n\n", os.Args[0])
		fmt.Println("  -l: Number of the line(s) to be replaced, comma separated.")
		fmt.Println("  -r: Word to be replaced, can be used multiple times.")
	}

	flag.Parse()

	if flag.NArg() != 1 {
		fmt.Fprintf(os.Stderr, "Usage: %s [-lrh] file, use -h for more info\n\n", os.Args[0])
		os.Exit(1)
	}

	stat, _ := os.Stdin.Stat()
	if (stat.Mode() & os.ModeCharDevice) == 0 {
		readLine(bufio.NewScanner(os.Stdin), replace_lines, replace_strings)
	} else {
		f := flag.Arg(0)
		if Exists(f) {
			file, err := os.Open(f)
			defer file.Close()
			if err != nil {
				log.Fatal(err)
			}
			readLine(bufio.NewScanner(file), replace_lines, replace_strings)
		} else {
			fmt.Printf("Cannot read file: %s\n", f)
		}
	}
}
<|endoftext|>


<commit_before>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package avm

import (
	"encoding/binary"
	"errors"
	"fmt"

	"github.com/ava-labs/avalanchego/database/linkeddb"
	"github.com/ava-labs/avalanchego/database/prefixdb"
	"github.com/ava-labs/avalanchego/vms/secp256k1fx"

	"github.com/ava-labs/avalanchego/cache"
	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/snow/choices"
	"github.com/ava-labs/avalanchego/snow/consensus/snowstorm"
	"github.com/ava-labs/avalanchego/vms/components/avax"
)

var (
	errAssetIDMismatch = errors.New("asset IDs in the input don't match the utxo")
	errWrongAssetID    = errors.New("asset ID must be AVAX in the atomic tx")
	errMissingUTXO     = errors.New("missing utxo")
	errUnknownTx       = errors.New("transaction is unknown")
	errRejectedTx      = errors.New("transaction is rejected")
)

var (
	_ snowstorm.Tx    = &UniqueTx{}
	_ cache.Evictable = &UniqueTx{}
)

// UniqueTx provides a de-duplication service for txs. This only provides a
// performance boost
type UniqueTx struct {
	*TxCachedState

	vm   *VM
	txID ids.ID
}

type TxCachedState struct {
	*Tx

	unique, verifiedTx, verifiedState bool
	validity                          error

	inputs     []ids.ID
	inputUTXOs []*avax.UTXOID
	utxos      []*avax.UTXO
	deps       []snowstorm.Tx

	status choices.Status
}

func (tx *UniqueTx) refresh() {
	tx.vm.numTxRefreshes.Inc()

	if tx.TxCachedState == nil {
		tx.TxCachedState = &TxCachedState{}
	}
	if tx.unique {
		return
	}
	unique := tx.vm.state.DeduplicateTx(tx)
	prevTx := tx.Tx
	if unique == tx {
		tx.vm.numTxRefreshMisses.Inc()

		// If no one was in the cache, make sure that there wasn't an
		// intermediate object whose state I must reflect
		if status, err := tx.vm.state.GetStatus(tx.ID()); err == nil {
			tx.status = status
		}
		tx.unique = true
	} else {
		tx.vm.numTxRefreshHits.Inc()

		// If someone is in the cache, they must be up to date

		// This ensures that every unique tx object points to the same tx state
		tx.TxCachedState = unique.TxCachedState
	}

	if tx.Tx != nil {
		return
	}

	if prevTx == nil {
		if innerTx, err := tx.vm.state.GetTx(tx.ID()); err == nil {
			tx.Tx = innerTx
		}
	} else {
		tx.Tx = prevTx
	}
}

// Evict is called when this UniqueTx will no longer be returned from a cache
// lookup
func (tx *UniqueTx) Evict() {
	// Lock is already held here
	tx.unique = false
	tx.deps = nil
}

func (tx *UniqueTx) setStatus(status choices.Status) error {
	tx.refresh()
	if tx.status == status {
		return nil
	}
	tx.status = status
	return tx.vm.state.PutStatus(tx.ID(), status)
}

// ID returns the wrapped txID
func (tx *UniqueTx) ID() ids.ID       { return tx.txID }
func (tx *UniqueTx) Key() interface{} { return tx.txID }

// getAddress returns a ids.ShortID address given a transaction. This function returns either a
// ids.ShortEmpty ID or an address from the transaction object. An address is returned if the
// tx object has UTXO with a singular receiver.
// Should we check if the value of funds is > 0 too? 🤔❓
var valExists = struct{}{}

func getAddresses(tx *UniqueTx) map[ids.ShortID]map[ids.ID]struct{} {
	// map of address => [...AssetID => bool]
	addresses := map[ids.ShortID]map[ids.ID]struct{}{}

	// go through all transfer outputs, assert they are secp transfer outputs
	for _, utxo := range tx.UTXOs() {
		// todo what do we do about inputs?
		out, ok := utxo.Out.(*secp256k1fx.TransferOutput)
		if !ok {
			continue
		}

		assetID := utxo.AssetID()
		for _, addr := range out.OutputOwners.Addrs {
			if _, exists := addresses[addr]; !exists {
				addresses[addr] = make(map[ids.ID]struct{})
			}
			addresses[addr][assetID] = valExists
		}
	}
	return addresses
}

var idxKey = []byte("idx")

// Accept is called when the transaction was finalized as accepted by consensus
func (tx *UniqueTx) Accept() error {
	tx.vm.ctx.Log.Info("Transaction got accepted!")

	if s := tx.Status(); s != choices.Processing {
		tx.vm.ctx.Log.Error("Failed to accept tx %s because the tx is in state %s", tx.txID, s)
		return fmt.Errorf("transaction has invalid status: %s", s)
	}

	defer tx.vm.db.Abort()

	// Remove spent utxos
	for _, utxo := range tx.InputUTXOs() {
		if utxo.Symbolic() {
			// If the UTXO is symbolic, it can't be spent
			continue
		}
		utxoID := utxo.InputID()
		if err := tx.vm.state.DeleteUTXO(utxoID); err != nil {
			tx.vm.ctx.Log.Error("Failed to spend utxo %s due to %s", utxoID, err)
			return err
		}
	}

	// Add new utxos
	for _, utxo := range tx.UTXOs() {
		if err := tx.vm.state.PutUTXO(utxo.InputID(), utxo); err != nil {
			tx.vm.ctx.Log.Error("Failed to fund utxo %s due to %s", utxo.InputID(), err)
			return err
		}
	}

	if err := tx.setStatus(choices.Accepted); err != nil {
		tx.vm.ctx.Log.Error("Failed to accept tx %s due to %s", tx.txID, err)
		return err
	}

	txID := tx.ID()

	// Get transaction address and proceed with indexing the transaction against the prefix DB
	// associated with the address if the returned address is not empty
	// should this be enabled on a config flag? Like indexing? 🤔❓
	addresses := getAddresses(tx)
	tx.vm.ctx.Log.Debug("Retrieved address data %s", addresses)
	for address, assetIDMap := range addresses {
		addressPrefixDB := prefixdb.New(address[:], tx.vm.db)
		for assetID := range assetIDMap {
			assetPrefixDB := linkeddb.NewDefault(prefixdb.New(assetID[:], addressPrefixDB))

			var idx uint64 = 1
			idxBytes := make([]byte, 8)
			binary.BigEndian.PutUint64(idxBytes, idx)

			exists, err := assetPrefixDB.Has(idxKey)
			tx.vm.ctx.Log.Debug("Processing address, assetID %s, %s, idx exists?", address, assetID, exists)
			if err != nil {
				tx.vm.ctx.Log.Error("Error checking idx value exists: %s", err)
				return err
			}

			if exists {
				idxBytes, err = assetPrefixDB.Get(idxKey)
				idx = binary.BigEndian.Uint64(idxBytes)
				tx.vm.ctx.Log.Debug("fetched index %d", idx)
				if err != nil {
					// index exists but we can't read it? return error
					tx.vm.ctx.Log.Error("Error reading idx value: %s", err)
					return err
				}
			}
			tx.vm.ctx.Log.Debug("Writing at index %d txID %s", idx, txID)
			err = assetPrefixDB.Put(idxBytes, txID[:])
			if err != nil {
				tx.vm.ctx.Log.Error("Failed to save transaction to the address, assetID prefix DB %s", err)
			}

			idx++
			binary.BigEndian.PutUint64(idxBytes, idx)
			tx.vm.ctx.Log.Debug("New index %d", idx)
			err = assetPrefixDB.Put(idxKey, idxBytes)
			if err != nil {
				tx.vm.ctx.Log.Error("Failed to save transaction index to the address, assetID prefix DB: %s", err)
			}
		}
	}

	commitBatch, err := tx.vm.db.CommitBatch()
	if err != nil {
		tx.vm.ctx.Log.Error("Failed to calculate CommitBatch for %s due to %s", txID, err)
		return err
	}

	if err := tx.ExecuteWithSideEffects(tx.vm, commitBatch); err != nil {
		tx.vm.ctx.Log.Error("Failed to commit accept %s due to %s", txID, err)
		return err
	}

	tx.vm.ctx.Log.Verbo("Accepted Tx: %s", txID)

	tx.vm.pubsub.Publish(txID, NewPubSubFilterer(tx.Tx))
	tx.vm.walletService.decided(txID)

	tx.deps = nil // Needed to prevent a memory leak

	return nil
}

// Reject is called when the transaction was finalized as rejected by consensus
func (tx *UniqueTx) Reject() error {
	defer tx.vm.db.Abort()

	if err := tx.setStatus(choices.Rejected); err != nil {
		tx.vm.ctx.Log.Error("Failed to reject tx %s due to %s", tx.txID, err)
		return err
	}

	txID := tx.ID()
	tx.vm.ctx.Log.Debug("Rejecting Tx: %s", txID)

	if err := tx.vm.db.Commit(); err != nil {
		tx.vm.ctx.Log.Error("Failed to commit reject %s due to %s", tx.txID, err)
		return err
	}

	tx.vm.walletService.decided(txID)

	tx.deps = nil // Needed to prevent a memory leak

	return nil
}

// Status returns the current status of this transaction
func (tx *UniqueTx) Status() choices.Status {
	tx.refresh()
	return tx.status
}

// Dependencies returns the set of transactions this transaction builds on
func (tx *UniqueTx) Dependencies() []snowstorm.Tx {
	tx.refresh()
	if tx.Tx == nil || len(tx.deps) != 0 {
		return tx.deps
	}

	txIDs := ids.Set{}
	for _, in := range tx.InputUTXOs() {
		if in.Symbolic() {
			continue
		}
		txID, _ := in.InputSource()
		if txIDs.Contains(txID) {
			continue
		}
		txIDs.Add(txID)
		tx.deps = append(tx.deps, &UniqueTx{
			vm:   tx.vm,
			txID: txID,
		})
	}
	consumedIDs := tx.Tx.ConsumedAssetIDs()
	for assetID := range tx.Tx.AssetIDs() {
		if consumedIDs.Contains(assetID) || txIDs.Contains(assetID) {
			continue
		}
		txIDs.Add(assetID)
		tx.deps = append(tx.deps, &UniqueTx{
			vm:   tx.vm,
			txID: assetID,
		})
	}
	return tx.deps
}

// InputIDs returns the set of utxoIDs this transaction consumes
func (tx *UniqueTx) InputIDs() []ids.ID {
	tx.refresh()
	if tx.Tx == nil || len(tx.inputs) != 0 {
		return tx.inputs
	}

	inputUTXOs := tx.InputUTXOs()
	tx.inputs = make([]ids.ID, len(inputUTXOs))
	for i, utxo := range inputUTXOs {
		tx.inputs[i] = utxo.InputID()
	}
	return tx.inputs
}

// InputUTXOs returns the utxos that will be consumed on tx acceptance
func (tx *UniqueTx) InputUTXOs() []*avax.UTXOID {
	tx.refresh()
	if tx.Tx == nil || len(tx.inputUTXOs) != 0 {
		return tx.inputUTXOs
	}
	tx.inputUTXOs = tx.Tx.InputUTXOs()
	return tx.inputUTXOs
}

// UTXOs returns the utxos that will be added to the UTXO set on tx acceptance
func (tx *UniqueTx) UTXOs() []*avax.UTXO {
	tx.refresh()
	if tx.Tx == nil || len(tx.utxos) != 0 {
		return tx.utxos
	}
	tx.utxos = tx.Tx.UTXOs()
	return tx.utxos
}

// Bytes returns the binary representation of this transaction
func (tx *UniqueTx) Bytes() []byte {
	tx.refresh()
	return tx.Tx.Bytes()
}

func (tx *UniqueTx) verifyWithoutCacheWrites() error {
	switch status := tx.Status(); status {
	case choices.Unknown:
		return errUnknownTx
	case choices.Accepted:
		return nil
	case choices.Rejected:
		return errRejectedTx
	default:
		return tx.SemanticVerify()
	}
}

// Verify the validity of this transaction
func (tx *UniqueTx) Verify() error {
	if err := tx.verifyWithoutCacheWrites(); err != nil {
		return err
	}

	tx.verifiedState = true
	return nil
}

// SyntacticVerify verifies that this transaction is well formed
func (tx *UniqueTx) SyntacticVerify() error {
	tx.refresh()

	if tx.Tx == nil {
		return errUnknownTx
	}

	if tx.verifiedTx {
		return tx.validity
	}

	tx.verifiedTx = true
	tx.validity = tx.Tx.SyntacticVerify(
		tx.vm.ctx,
		tx.vm.codec,
		tx.vm.feeAssetID,
		tx.vm.txFee,
		tx.vm.creationTxFee,
		len(tx.vm.fxs),
	)
	return tx.validity
}

// SemanticVerify the validity of this transaction
func (tx *UniqueTx) SemanticVerify() error {
	// SyntacticVerify sets the error on validity and is checked in the next
	// statement
	_ = tx.SyntacticVerify()

	if tx.validity != nil || tx.verifiedState {
		return tx.validity
	}

	return tx.Tx.SemanticVerify(tx.vm, tx.UnsignedTx)
}
<commit_msg>add comment<commit_after>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package avm

import (
	"encoding/binary"
	"errors"
	"fmt"

	"github.com/ava-labs/avalanchego/database/linkeddb"
	"github.com/ava-labs/avalanchego/database/prefixdb"
	"github.com/ava-labs/avalanchego/vms/secp256k1fx"

	"github.com/ava-labs/avalanchego/cache"
	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/snow/choices"
	"github.com/ava-labs/avalanchego/snow/consensus/snowstorm"
	"github.com/ava-labs/avalanchego/vms/components/avax"
)

var (
	errAssetIDMismatch = errors.New("asset IDs in the input don't match the utxo")
	errWrongAssetID    = errors.New("asset ID must be AVAX in the atomic tx")
	errMissingUTXO     = errors.New("missing utxo")
	errUnknownTx       = errors.New("transaction is unknown")
	errRejectedTx      = errors.New("transaction is rejected")
)

var (
	_ snowstorm.Tx    = &UniqueTx{}
	_ cache.Evictable = &UniqueTx{}
)

// UniqueTx provides a de-duplication service for txs. This only provides a
// performance boost
type UniqueTx struct {
	*TxCachedState

	vm   *VM
	txID ids.ID
}

type TxCachedState struct {
	*Tx

	unique, verifiedTx, verifiedState bool
	validity                          error

	inputs     []ids.ID
	inputUTXOs []*avax.UTXOID
	utxos      []*avax.UTXO
	deps       []snowstorm.Tx

	status choices.Status
}

func (tx *UniqueTx) refresh() {
	tx.vm.numTxRefreshes.Inc()

	if tx.TxCachedState == nil {
		tx.TxCachedState = &TxCachedState{}
	}
	if tx.unique {
		return
	}
	unique := tx.vm.state.DeduplicateTx(tx)
	prevTx := tx.Tx
	if unique == tx {
		tx.vm.numTxRefreshMisses.Inc()

		// If no one was in the cache, make sure that there wasn't an
		// intermediate object whose state I must reflect
		if status, err := tx.vm.state.GetStatus(tx.ID()); err == nil {
			tx.status = status
		}
		tx.unique = true
	} else {
		tx.vm.numTxRefreshHits.Inc()

		// If someone is in the cache, they must be up to date

		// This ensures that every unique tx object points to the same tx state
		tx.TxCachedState = unique.TxCachedState
	}

	if tx.Tx != nil {
		return
	}

	if prevTx == nil {
		if innerTx, err := tx.vm.state.GetTx(tx.ID()); err == nil {
			tx.Tx = innerTx
		}
	} else {
		tx.Tx = prevTx
	}
}

// Evict is called when this UniqueTx will no longer be returned from a cache
// lookup
func (tx *UniqueTx) Evict() {
	// Lock is already held here
	tx.unique = false
	tx.deps = nil
}

func (tx *UniqueTx) setStatus(status choices.Status) error {
	tx.refresh()
	if tx.status == status {
		return nil
	}
	tx.status = status
	return tx.vm.state.PutStatus(tx.ID(), status)
}

// ID returns the wrapped txID
func (tx *UniqueTx) ID() ids.ID       { return tx.txID }
func (tx *UniqueTx) Key() interface{} { return tx.txID }

// getAddress returns a ids.ShortID address given a transaction. This function returns either a
// ids.ShortEmpty ID or an address from the transaction object. An address is returned if the
// tx object has UTXO with a singular receiver.
// Should we check if the value of funds is > 0 too? 🤔❓
var valExists = struct{}{}

func getAddresses(tx *UniqueTx) map[ids.ShortID]map[ids.ID]struct{} {
	// map of address => [...AssetID => bool]
	addresses := map[ids.ShortID]map[ids.ID]struct{}{}

	// go through all transfer outputs, assert they are secp transfer outputs
	for _, utxo := range tx.UTXOs() {
		// todo what do we do about inputs?
		out, ok := utxo.Out.(*secp256k1fx.TransferOutput)
		if !ok {
			continue
		}

		assetID := utxo.AssetID()
		for _, addr := range out.OutputOwners.Addrs {
			if _, exists := addresses[addr]; !exists {
				addresses[addr] = make(map[ids.ID]struct{})
			}
			addresses[addr][assetID] = valExists
		}
	}
	return addresses
}

var idxKey = []byte("idx")

// Accept is called when the transaction was finalized as accepted by consensus
func (tx *UniqueTx) Accept() error {
	tx.vm.ctx.Log.Info("Transaction got accepted!")

	if s := tx.Status(); s != choices.Processing {
		tx.vm.ctx.Log.Error("Failed to accept tx %s because the tx is in state %s", tx.txID, s)
		return fmt.Errorf("transaction has invalid status: %s", s)
	}

	defer tx.vm.db.Abort()

	// Remove spent utxos
	for _, utxo := range tx.InputUTXOs() {
		if utxo.Symbolic() {
			// If the UTXO is symbolic, it can't be spent
			continue
		}
		utxoID := utxo.InputID()
		if err := tx.vm.state.DeleteUTXO(utxoID); err != nil {
			tx.vm.ctx.Log.Error("Failed to spend utxo %s due to %s", utxoID, err)
			return err
		}
	}

	// Add new utxos
	for _, utxo := range tx.UTXOs() {
		if err := tx.vm.state.PutUTXO(utxo.InputID(), utxo); err != nil {
			tx.vm.ctx.Log.Error("Failed to fund utxo %s due to %s", utxo.InputID(), err)
			return err
		}
	}

	if err := tx.setStatus(choices.Accepted); err != nil {
		tx.vm.ctx.Log.Error("Failed to accept tx %s due to %s", tx.txID, err)
		return err
	}

	txID := tx.ID()

	// Get transaction address and proceed with indexing the transaction against the prefix DB
	// associated with the address if the returned address is not empty
	// should this be enabled on a config flag? Like indexing? 🤔❓
	addresses := getAddresses(tx)
	tx.vm.ctx.Log.Debug("Retrieved address data %s", addresses)
	for address, assetIDMap := range addresses {
		addressPrefixDB := prefixdb.New(address[:], tx.vm.db)
		for assetID := range assetIDMap {
			assetPrefixDB := linkeddb.NewDefault(prefixdb.New(assetID[:], addressPrefixDB))

			var idx uint64 = 1
			idxBytes := make([]byte, 8)
			binary.BigEndian.PutUint64(idxBytes, idx)

			exists, err := assetPrefixDB.Has(idxKey)
			tx.vm.ctx.Log.Debug("Processing address, assetID %s, %s, idx exists?", address, assetID, exists)
			if err != nil {
				tx.vm.ctx.Log.Error("Error checking idx value exists: %s", err)
				return err
			}

			if exists {
				idxBytes, err = assetPrefixDB.Get(idxKey)
				idx = binary.BigEndian.Uint64(idxBytes)
				tx.vm.ctx.Log.Debug("fetched index %d", idx)
				if err != nil {
					// index exists but we can't read it? return error
					tx.vm.ctx.Log.Error("Error reading idx value: %s", err)
					return err
				}
			}
			tx.vm.ctx.Log.Debug("Writing at index %d txID %s", idx, txID)
			err = assetPrefixDB.Put(idxBytes, txID[:])
			if err != nil {
				tx.vm.ctx.Log.Error("Failed to save transaction to the address, assetID prefix DB %s", err)
			}

			idx++
			binary.BigEndian.PutUint64(idxBytes, idx)
			tx.vm.ctx.Log.Debug("New index %d", idx)
			// why does the following call always fail at index 1?!
			err = assetPrefixDB.Put(idxKey, idxBytes)
			if err != nil {
				tx.vm.ctx.Log.Error("Failed to save transaction index to the address, assetID prefix DB: %s", err)
			}
		}
	}

	commitBatch, err := tx.vm.db.CommitBatch()
	if err != nil {
		tx.vm.ctx.Log.Error("Failed to calculate CommitBatch for %s due to %s", txID, err)
		return err
	}

	if err := tx.ExecuteWithSideEffects(tx.vm, commitBatch); err != nil {
		tx.vm.ctx.Log.Error("Failed to commit accept %s due to %s", txID, err)
		return err
	}

	tx.vm.ctx.Log.Verbo("Accepted Tx: %s", txID)

	tx.vm.pubsub.Publish(txID, NewPubSubFilterer(tx.Tx))
	tx.vm.walletService.decided(txID)

	tx.deps = nil // Needed to prevent a memory leak

	return nil
}

// Reject is called when the transaction was finalized as rejected by consensus
func (tx *UniqueTx) Reject() error {
	defer tx.vm.db.Abort()

	if err := tx.setStatus(choices.Rejected); err != nil {
		tx.vm.ctx.Log.Error("Failed to reject tx %s due to %s", tx.txID, err)
		return err
	}

	txID := tx.ID()
	tx.vm.ctx.Log.Debug("Rejecting Tx: %s", txID)

	if err := tx.vm.db.Commit(); err != nil {
		tx.vm.ctx.Log.Error("Failed to commit reject %s due to %s", tx.txID, err)
		return err
	}

	tx.vm.walletService.decided(txID)

	tx.deps = nil // Needed to prevent a memory leak

	return nil
}

// Status returns the current status of this transaction
func (tx *UniqueTx) Status() choices.Status {
	tx.refresh()
	return tx.status
}

// Dependencies returns the set of transactions this transaction builds on
func (tx *UniqueTx) Dependencies() []snowstorm.Tx {
	tx.refresh()
	if tx.Tx == nil || len(tx.deps) != 0 {
		return tx.deps
	}

	txIDs := ids.Set{}
	for _, in := range tx.InputUTXOs() {
		if in.Symbolic() {
			continue
		}
		txID, _ := in.InputSource()
		if txIDs.Contains(txID) {
			continue
		}
		txIDs.Add(txID)
		tx.deps = append(tx.deps, &UniqueTx{
			vm:   tx.vm,
			txID: txID,
		})
	}
	consumedIDs := tx.Tx.ConsumedAssetIDs()
	for assetID := range tx.Tx.AssetIDs() {
		if consumedIDs.Contains(assetID) || txIDs.Contains(assetID) {
			continue
		}
		txIDs.Add(assetID)
		tx.deps = append(tx.deps, &UniqueTx{
			vm:   tx.vm,
			txID: assetID,
		})
	}
	return tx.deps
}

// InputIDs returns the set of utxoIDs this transaction consumes
func (tx *UniqueTx) InputIDs() []ids.ID {
	tx.refresh()
	if tx.Tx == nil || len(tx.inputs) != 0 {
		return tx.inputs
	}

	inputUTXOs := tx.InputUTXOs()
	tx.inputs = make([]ids.ID, len(inputUTXOs))
	for i, utxo := range inputUTXOs {
		tx.inputs[i] = utxo.InputID()
	}
	return tx.inputs
}

// InputUTXOs returns the utxos that will be consumed on tx acceptance
func (tx *UniqueTx) InputUTXOs() []*avax.UTXOID {
	tx.refresh()
	if tx.Tx == nil || len(tx.inputUTXOs) != 0 {
		return tx.inputUTXOs
	}
	tx.inputUTXOs = tx.Tx.InputUTXOs()
	return tx.inputUTXOs
}

// UTXOs returns the utxos that will be added to the UTXO set on tx acceptance
func (tx *UniqueTx) UTXOs() []*avax.UTXO {
	tx.refresh()
	if tx.Tx == nil || len(tx.utxos) != 0 {
		return tx.utxos
	}
	tx.utxos = tx.Tx.UTXOs()
	return tx.utxos
}

// Bytes returns the binary representation of this transaction
func (tx *UniqueTx) Bytes() []byte {
	tx.refresh()
	return tx.Tx.Bytes()
}

func (tx *UniqueTx) verifyWithoutCacheWrites() error {
	switch status := tx.Status(); status {
	case choices.Unknown:
		return errUnknownTx
	case choices.Accepted:
		return nil
	case choices.Rejected:
		return errRejectedTx
	default:
		return tx.SemanticVerify()
	}
}

// Verify the validity of this transaction
func (tx *UniqueTx) Verify() error {
	if err := tx.verifyWithoutCacheWrites(); err != nil {
		return err
	}

	tx.verifiedState = true
	return nil
}

// SyntacticVerify verifies that this transaction is well formed
func (tx *UniqueTx) SyntacticVerify() error {
	tx.refresh()

	if tx.Tx == nil {
		return errUnknownTx
	}

	if tx.verifiedTx {
		return tx.validity
	}

	tx.verifiedTx = true
	tx.validity = tx.Tx.SyntacticVerify(
		tx.vm.ctx,
		tx.vm.codec,
		tx.vm.feeAssetID,
		tx.vm.txFee,
		tx.vm.creationTxFee,
		len(tx.vm.fxs),
	)
	return tx.validity
}

// SemanticVerify the validity of this transaction
func (tx *UniqueTx) SemanticVerify() error {
	// SyntacticVerify sets the error on validity and is checked in the next
	// statement
	_ = tx.SyntacticVerify()

	if tx.validity != nil || tx.verifiedState {
		return tx.validity
	}

	return tx.Tx.SemanticVerify(tx.vm, tx.UnsignedTx)
}
<|endoftext|>


<commit_before>// +build ignore

package main

import (
	"log"
	"text/template"

	"../../../gen"
)

func main() {
	t, err := template.New("").Parse(tmpl)
	if err != nil {
		log.Fatal(err)
	}
	var j js
	if err := gen.Gen("clock", &j, t); err != nil {
		log.Fatal(err)
	}
}

// The JSON structure we expect to be able to umarshal into
type js struct {
	Cases []struct {
		Description string
		Cases       []OneCase
	}
}

type OneCase struct {
	Description string
	Property    string
	Hour        int // "create"/"add" cases
	Minute      int // "create"/"add" cases
	Add         int // "add" cases only

	Clock1   struct{ Hour, Minute int } // "equal" cases only
	Clock2   struct{ Hour, Minute int } // "equal" cases only
	Expected interface{}                // string or bool
}

func (c OneCase) IsTimeCase() bool  { return c.Property == "create" }
func (c OneCase) IsAddCase() bool   { return c.Property == "add" }
func (c OneCase) IsEqualCase() bool { return c.Property == "equal" }

var tmpl = `package clock

{{.Header}}

// Test creating a new clock with an initial time.
var timeTests = []struct {
	h, m int
	want string
}{ {{range .J.Cases}} {{range .Cases}}
{{if .IsTimeCase}}{ {{.Hour}}, {{.Minute}}, {{.Expected | printf "%#v"}}}, // {{.Description}}
{{- end}}{{end}}{{end}} }

// Test adding and subtracting minutes.
var addTests = []struct {
	h, m, a int
	want string
}{ {{range .J.Cases}} {{range .Cases}}
{{if .IsAddCase}}{ {{.Hour}}, {{.Minute}}, {{.Add}}, {{.Expected | printf "%#v"}}}, // {{.Description}}
{{- end}}{{end}}{{end}} }

// Construct two separate clocks, set times, test if they are equal.
type hm struct{ h, m int }

var eqTests = []struct {
	c1, c2 hm
	want   bool
}{ {{range .J.Cases}} {{range .Cases}}
{{if .IsEqualCase}} // {{.Description}}
{
	hm{ {{.Clock1.Hour}}, {{.Clock1.Minute}}},
	hm{ {{.Clock2.Hour}}, {{.Clock2.Minute}}},
	{{.Expected}},
}, {{- end}}{{end}}{{end}}
}
`
<commit_msg>clock: update generator for test group comment<commit_after>// +build ignore

package main

import (
	"log"
	"text/template"

	"../../../gen"
)

func main() {
	t, err := template.New("").Parse(tmpl)
	if err != nil {
		log.Fatal(err)
	}
	var j js
	if err := gen.Gen("clock", &j, t); err != nil {
		log.Fatal(err)
	}
}

// The JSON structure we expect to be able to umarshal into
type js struct {
	Groups TestGroups `json:"Cases"`
}

type TestGroups []struct {
	Description string
	Cases       []OneCase
}

type OneCase struct {
	Description string
	Property    string
	Hour        int // "create"/"add" cases
	Minute      int // "create"/"add" cases
	Add         int // "add" cases only

	Clock1   struct{ Hour, Minute int } // "equal" cases only
	Clock2   struct{ Hour, Minute int } // "equal" cases only
	Expected interface{}                // string or bool
}

func (c OneCase) IsTimeCase() bool  { return c.Property == "create" }
func (c OneCase) IsAddCase() bool   { return c.Property == "add" }
func (c OneCase) IsEqualCase() bool { return c.Property == "equal" }

func (groups TestGroups) GroupComment(property string) string {
	for _, group := range groups {
		propertyGroupMatch := true
		for _, testcase := range group.Cases {
			if testcase.Property != property {
				propertyGroupMatch = false
				break
			}
		}
		if propertyGroupMatch {
			return group.Description
		}
	}
	return "Note: Apparent inconsistent use of \"property\": \"" + property + "\" within test case group!"
}

var tmpl = `package clock

{{.Header}}

{{with .J.Groups}}
    // {{ .GroupComment "create"}}
{{end}} var timeTests = []struct {
	h, m int
	want string
}{ {{range .J.Groups}} {{range .Cases}}
{{if .IsTimeCase}}{ {{.Hour}}, {{.Minute}}, {{.Expected | printf "%#v"}}}, // {{.Description}}
{{- end}}{{end}}{{end}} }

{{with .J.Groups}}
    // {{ .GroupComment "add"}}
{{end}} var addTests = []struct {
	h, m, a int
	want string
}{ {{range .J.Groups}} {{range .Cases}}
{{if .IsAddCase}}{ {{.Hour}}, {{.Minute}}, {{.Add}}, {{.Expected | printf "%#v"}}}, // {{.Description}}
{{- end}}{{end}}{{end}} }

{{with .J.Groups}}
    // {{ .GroupComment "equal"}}
{{end}} type hm struct{ h, m int }

var eqTests = []struct {
	c1, c2 hm
	want   bool
}{ {{range .J.Groups}} {{range .Cases}}
{{if .IsEqualCase}} // {{.Description}}
{
	hm{ {{.Clock1.Hour}}, {{.Clock1.Minute}}},
	hm{ {{.Clock2.Hour}}, {{.Clock2.Minute}}},
	{{.Expected}},
}, {{- end}}{{end}}{{end}}
}
`
<|endoftext|>