func (c *Client) DeleteField(field *Field) error {
	span := c.tracer.StartSpan("Client.DeleteField")
	defer span.Finish()

	path := fmt.Sprintf("/index/%s/field/%s", field.index.name, field.name)
	_, _, err := c.httpRequest("DELETE", path, nil, nil, false)
	return err
}

func ( c * Client ) DeleteField ( field * Field ) error { span := c . tracer . StartSpan ( " " ) defer span . Finish ( ) path := fmt . Sprintf ( " " , field . index . name , field . name ) _ , _ , err := c . httpRequest ( " " , path , nil , nil , false ) return err }

// DeleteField deletes a field on the server.

DeleteField deletes a field on the server .


func (c *Client) SyncSchema(schema *Schema) error {
	span := c.tracer.StartSpan("Client.SyncSchema")
	defer span.Finish()

	serverSchema, err := c.Schema()
	if err != nil {
		return err
	}

	return c.syncSchema(schema, serverSchema)
}

func ( c * Client ) SyncSchema ( schema * Schema ) error { span := c . tracer . StartSpan ( " " ) defer span . Finish ( ) serverSchema , err := c . Schema ( ) if err != nil { return err } return c . syncSchema ( schema , serverSchema ) }

// SyncSchema updates a schema with the indexes and fields on the server and
// creates the indexes and fields in the schema on the server side.
// This function does not delete indexes and the fields on the server side nor in the schema.

SyncSchema updates a schema with the indexes and fields on the server and creates the indexes and fields in the schema on the server side . This function does not delete indexes and the fields on the server side nor in the schema .


func (c *Client) Schema() (*Schema, error) {
	span := c.tracer.StartSpan("Client.Schema")
	defer span.Finish()

	var indexes []SchemaIndex
	indexes, err := c.readSchema()
	if err != nil {
		return nil, err
	}
	schema := NewSchema()
	for _, indexInfo := range indexes {
		index := schema.indexWithOptions(indexInfo.Name, indexInfo.ShardWidth, indexInfo.Options.asIndexOptions())
		for _, fieldInfo := range indexInfo.Fields {
			index.fieldWithOptions(fieldInfo.Name, fieldInfo.Options.asFieldOptions())
		}
	}
	return schema, nil
}

func ( c * Client ) Schema ( ) ( * Schema , error ) { span := c . tracer . StartSpan ( " " ) defer span . Finish ( ) var indexes [ ] SchemaIndex indexes , err := c . readSchema ( ) if err != nil { return nil , err } schema := NewSchema ( ) for _ , indexInfo := range indexes { index := schema . indexWithOptions ( indexInfo . Name , indexInfo . ShardWidth , indexInfo . Options . asIndexOptions ( ) ) for _ , fieldInfo := range indexInfo . Fields { index . fieldWithOptions ( fieldInfo . Name , fieldInfo . Options . asFieldOptions ( ) ) } } return schema , nil }

// Schema returns the indexes and fields on the server.

Schema returns the indexes and fields on the server .


func (c *Client) ImportField(field *Field, iterator RecordIterator, options ...ImportOption) error {
	span := c.tracer.StartSpan("Client.ImportField")
	defer span.Finish()

	importOptions := ImportOptions{}
	for _, option := range options {
		if err := option(&importOptions); err != nil {
			return err
		}
	}
	importOptions = importOptions.withDefaults()
	if importOptions.importRecordsFunction == nil {
		// if import records function was not already set, set it.
		if field.options != nil && field.options.fieldType == FieldTypeInt {
			importRecordsFunction(c.importValues)(&importOptions)
		} else {
			// Check whether roaring imports is available
			if importOptions.wantRoaring != nil && *importOptions.wantRoaring == true {
				importOptions.hasRoaring = c.hasRoaringImportSupport(field)
			}
			importRecordsFunction(c.importColumns)(&importOptions)
		}
	}
	// if the index doesn't have its shardWidth set, we need to get it from the schema
	if field.index.shardWidth == 0 {
		indexes, err := c.readSchema()
		if err != nil {
			return err
		}
		for _, index := range indexes {
			if index.Name != field.index.name {
				continue
			}
			indexCopy := field.index.copy()
			indexCopy.shardWidth = index.ShardWidth
			field = field.copy()
			field.index = indexCopy
			break
		}
		if field.index.shardWidth == 0 {
			// the index does not have shard width, use the default
			field.index.shardWidth = DefaultShardWidth
		}
	}
	return c.importManager.run(field, iterator, importOptions)
}

// ImportField imports records from the given iterator.

ImportField imports records from the given iterator .


func (c *Client) ExportField(field *Field) (io.Reader, error) {
	span := c.tracer.StartSpan("Client.ExportField")
	defer span.Finish()

	var shardsMax map[string]uint64
	var err error

	status, err := c.Status()
	if err != nil {
		return nil, err
	}
	shardsMax, err = c.shardsMax()
	if err != nil {
		return nil, err
	}
	status.indexMaxShard = shardsMax
	shardURIs, err := c.statusToNodeShardsForIndex(status, field.index.Name())
	if err != nil {
		return nil, err
	}

	return newExportReader(c, shardURIs, field), nil
}

func ( c * Client ) ExportField ( field * Field ) ( io . Reader , error ) { span := c . tracer . StartSpan ( " " ) defer span . Finish ( ) var shardsMax map [ string ] uint64 var err error status , err := c . Status ( ) if err != nil { return nil , err } shardsMax , err = c . shardsMax ( ) if err != nil { return nil , err } status . indexMaxShard = shardsMax shardURIs , err := c . statusToNodeShardsForIndex ( status , field . index . Name ( ) ) if err != nil { return nil , err } return newExportReader ( c , shardURIs , field ) , nil }

// ExportField exports columns for a field.

ExportField exports columns for a field .


func (c *Client) Status() (Status, error) {
	span := c.tracer.StartSpan("Client.Status")
	defer span.Finish()

	_, data, err := c.httpRequest("GET", "/status", nil, nil, false)
	if err != nil {
		return Status{}, errors.Wrap(err, "requesting /status")
	}
	status := Status{}
	err = json.Unmarshal(data, &status)
	if err != nil {
		return Status{}, errors.Wrap(err, "unmarshaling /status data")
	}
	return status, nil
}

func ( c * Client ) Status ( ) ( Status , error ) { span := c . tracer . StartSpan ( " " ) defer span . Finish ( ) _ , data , err := c . httpRequest ( " " , " " , nil , nil , false ) if err != nil { return Status { } , errors . Wrap ( err , " " ) } status := Status { } err = json . Unmarshal ( data , & status ) if err != nil { return Status { } , errors . Wrap ( err , " " ) } return status , nil }


func (c *Client) httpRequest(method string, path string, data []byte, headers map[string]string, useCoordinator bool) (*http.Response, []byte, error) {
	if data == nil {
		data = []byte{}
	}

	// try at most maxHosts non-failed hosts; protect against broken cluster.removeHost
	var response *http.Response
	var err error
	for i := 0; i < maxHosts; i++ {
		host, err := c.host(useCoordinator)
		if err != nil {
			return nil, nil, err
		}
		response, err = c.doRequest(host, method, path, c.augmentHeaders(headers), bytes.NewReader(data))
		if err == nil {
			break
		}
		if c.manualServerURI == nil {
			if useCoordinator {
				c.coordinatorLock.Lock()
				c.coordinatorURI = nil
				c.coordinatorLock.Unlock()
			} else {
				c.cluster.RemoveHost(host)
			}
		}
		// TODO: exponential backoff
		time.Sleep(1 * time.Second)
	}
	if response == nil {
		return nil, nil, ErrTriedMaxHosts
	}
	defer response.Body.Close()
	warning := response.Header.Get("warning")
	if warning != "" {
		c.logger.Println(warning)
	}
	// TODO: Optimize buffer creation
	buf, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, nil, err
	}
	if response.StatusCode < 200 || response.StatusCode >= 300 {
		err := NewError(fmt.Sprintf("Server error (%d) %s: %s", response.StatusCode, response.Status, string(buf)))
		return response, buf, err
	}
	return response, buf, nil
}

// httpRequest makes a request to the cluster - use this when you want the
// client to choose a host, and it doesn't matter if the request goes to a
// specific host

httpRequest makes a request to the cluster - use this when you want the client to choose a host and it doesn t matter if the request goes to a specific host


func (c *Client) doRequest(host *URI, method, path string, headers map[string]string, reader io.Reader) (*http.Response, error) {
	req, err := makeRequest(host, method, path, headers, reader)
	if err != nil {
		return nil, errors.Wrap(err, "building request")
	}
	return c.client.Do(req)
}

func ( c * Client ) doRequest ( host * URI , method , path string , headers map [ string ] string , reader io . Reader ) ( * http . Response , error ) { req , err := makeRequest ( host , method , path , headers , reader ) if err != nil { return nil , errors . Wrap ( err , " " ) } return c . client . Do ( req ) }

// doRequest creates and performs an http request.

doRequest creates and performs an http request .


func (c *Client) statusToNodeShardsForIndex(status Status, indexName string) (map[uint64]*URI, error) {
	result := make(map[uint64]*URI)
	if maxShard, ok := status.indexMaxShard[indexName]; ok {
		for shard := 0; shard <= int(maxShard); shard++ {
			fragmentNodes, err := c.fetchFragmentNodes(indexName, uint64(shard))
			if err != nil {
				return nil, err
			}
			if len(fragmentNodes) == 0 {
				return nil, ErrNoFragmentNodes
			}
			node := fragmentNodes[0]
			uri := &URI{
				host:   node.Host,
				port:   node.Port,
				scheme: node.Scheme,
			}

			result[uint64(shard)] = uri
		}
	} else {
		return nil, ErrNoShard
	}
	return result, nil
}

func ( c * Client ) statusToNodeShardsForIndex ( status Status , indexName string ) ( map [ uint64 ] * URI , error ) { result := make ( map [ uint64 ] * URI ) if maxShard , ok := status . indexMaxShard [ indexName ] ; ok { for shard := 0 ; shard <= int ( maxShard ) ; shard ++ { fragmentNodes , err := c . fetchFragmentNodes ( indexName , uint64 ( shard ) ) if err != nil { return nil , err } if len ( fragmentNodes ) == 0 { return nil , ErrNoFragmentNodes } node := fragmentNodes [ 0 ] uri := & URI { host : node . Host , port : node . Port , scheme : node . Scheme , } result [ uint64 ( shard ) ] = uri } } else { return nil , ErrNoShard } return result , nil }

// statusToNodeShardsForIndex finds the hosts which contains shards for the given index


func (c *Client) ExperimentalReplayImport(r io.Reader, concurrency int) error {
	span := c.tracer.StartSpan("Client.ExperimentalReplayImport")
	defer span.Finish()

	// make work channel
	work := make(chan *importLog, concurrency*2)

	// spawn <concurrency> workers to read from channel
	eg := &errgroup.Group{}
	for i := 0; i < concurrency; i++ {
		eg.Go(func() error {
			for log := range work {
				// regular import doesn't forward to replicas, so we have to get all
				// the nodes.
				nodes, err := c.fetchFragmentNodes(log.Index, log.Shard)
				if err != nil {
					return errors.Wrap(err, "fetching fragment nodes")
				}

				if !log.IsRoaring {
					for _, node := range nodes {
						resp, err := c.doRequest(node.URI(), "POST", log.Path, defaultProtobufHeaders(), bytes.NewReader(log.Data))
						if err = anyError(resp, err); err != nil {
							return errors.Wrap(err, "doing import")
						}
						resp.Body.Close()
					}
				} else {
					// import-roaring forwards on to all replicas, so we only import to
					// one node.
					resp, err := c.doRequest(nodes[0].URI(), "POST", log.Path, defaultProtobufHeaders(), bytes.NewReader(log.Data))
					if err = anyError(resp, err); err != nil {
						return errors.Wrap(err, "doing import")
					}
					resp.Body.Close()
				}
			}
			return nil
		})
	}

	// wait on importers asynchronously in case they return early with errors
	waitErrCh := make(chan error)
	go func() {
		waitErrCh <- eg.Wait()
		close(waitErrCh)
	}()

	// populate work channel
	dec := newImportLogDecoder(r)
	var err error
	for {
		log := importLog{}
		err = dec.Decode(&log)
		if err != nil {
			err = errors.Wrap(err, "decoding")
			break
		}
		select {
		case work <- &log:
			continue
		case err = <-waitErrCh:
			err = errors.Wrap(err, "waiting")
		}
		break
	}

	// close work channel (now workers can exit)
	close(work)
	<-waitErrCh // make sure workers are complete
	if errors.Cause(err) == io.EOF {
		return nil
	}
	return err
}

// ExperimentalReplayImport takes a data stream which was previously recorded by the import
// logging functionality and imports it to Pilosa. The target cluster need not
// be of the same size as the original cluster, but it must already have the
// necessary schema in place. It is an experimental method and may be changed or
// removed.

ExperimentalReplayImport takes a data stream which was previously recorded by the import logging functionality and imports it to Pilosa . The target cluster need not be of the same size as the original cluster but it must already have the necessary schema in place . It is an experimental method and may be changed or removed .


func viewByTimeUnit(t time.Time, unit rune) string {
	switch unit {
	case 'Y':
		return t.Format("2006")
	case 'M':
		return t.Format("200601")
	case 'D':
		return t.Format("20060102")
	case 'H':
		return t.Format("2006010215")
	default:
		return ""
	}
}

func viewByTimeUnit ( t time . Time , unit rune ) string { switch unit { case 'Y' : return t . Format ( " " ) case 'M' : return t . Format ( " " ) case 'D' : return t . Format ( " " ) case 'H' : return t . Format ( " " ) default : return " " } }

// viewByTimeUnit returns the view name for time with a given quantum unit.

viewByTimeUnit returns the view name for time with a given quantum unit .


func OptClientSocketTimeout(timeout time.Duration) ClientOption {
	return func(options *ClientOptions) error {
		options.SocketTimeout = timeout
		return nil
	}
}

func OptClientSocketTimeout ( timeout time . Duration ) ClientOption { return func ( options * ClientOptions ) error { options . SocketTimeout = timeout return nil } }

// OptClientSocketTimeout is the maximum idle socket time in nanoseconds


func OptClientConnectTimeout(timeout time.Duration) ClientOption {
	return func(options *ClientOptions) error {
		options.ConnectTimeout = timeout
		return nil
	}
}

func OptClientConnectTimeout ( timeout time . Duration ) ClientOption { return func ( options * ClientOptions ) error { options . ConnectTimeout = timeout return nil } }

// OptClientConnectTimeout is the maximum time to connect in nanoseconds.

OptClientConnectTimeout is the maximum time to connect in nanoseconds .


func OptClientPoolSizePerRoute(size int) ClientOption {
	return func(options *ClientOptions) error {
		options.PoolSizePerRoute = size
		return nil
	}
}

func OptClientPoolSizePerRoute ( size int ) ClientOption { return func ( options * ClientOptions ) error { options . PoolSizePerRoute = size return nil } }

// OptClientPoolSizePerRoute is the maximum number of active connections in the pool to a host.

OptClientPoolSizePerRoute is the maximum number of active connections in the pool to a host .


func OptClientTotalPoolSize(size int) ClientOption {
	return func(options *ClientOptions) error {
		options.TotalPoolSize = size
		return nil
	}
}

func OptClientTotalPoolSize ( size int ) ClientOption { return func ( options * ClientOptions ) error { options . TotalPoolSize = size return nil } }

// OptClientTotalPoolSize is the maximum number of connections in the pool.

OptClientTotalPoolSize is the maximum number of connections in the pool .


func OptClientTLSConfig(config *tls.Config) ClientOption {
	return func(options *ClientOptions) error {
		options.TLSConfig = config
		return nil
	}
}

func OptClientTLSConfig ( config * tls . Config ) ClientOption { return func ( options * ClientOptions ) error { options . TLSConfig = config return nil } }

// OptClientTLSConfig contains the TLS configuration.

OptClientTLSConfig contains the TLS configuration .


func OptClientManualServerAddress(enabled bool) ClientOption {
	return func(options *ClientOptions) error {
		options.manualServerAddress = enabled
		return nil
	}
}

func OptClientManualServerAddress ( enabled bool ) ClientOption { return func ( options * ClientOptions ) error { options . manualServerAddress = enabled return nil } }

// OptClientManualServerAddress forces the client use only the manual server address


func ExperimentalOptClientLogImports(loc io.Writer) ClientOption {
	return func(options *ClientOptions) error {
		options.importLogWriter = loc
		return nil
	}
}

func ExperimentalOptClientLogImports ( loc io . Writer ) ClientOption { return func ( options * ClientOptions ) error { options . importLogWriter = loc return nil } }

// ExperimentalOptClientLogImports writes all imports to the given writer in
// such a way that they can easily be replayed into another Pilosa cluster. It
// is an experimental option and may be changed or removed.

ExperimentalOptClientLogImports writes all imports to the given writer in such a way that they can easily be replayed into another Pilosa cluster . It is an experimental option and may be changed or removed .


func OptQueryColumnAttrs(enable bool) QueryOption {
	return func(options *QueryOptions) error {
		options.ColumnAttrs = enable
		return nil
	}
}

func OptQueryColumnAttrs ( enable bool ) QueryOption { return func ( options * QueryOptions ) error { options . ColumnAttrs = enable return nil } }

// OptQueryColumnAttrs enables returning column attributes in the result.

OptQueryColumnAttrs enables returning column attributes in the result .


func OptQueryShards(shards ...uint64) QueryOption {
	return func(options *QueryOptions) error {
		options.Shards = append(options.Shards, shards...)
		return nil
	}
}

func OptQueryShards ( shards ... uint64 ) QueryOption { return func ( options * QueryOptions ) error { options . Shards = append ( options . Shards , shards ... ) return nil } }

// OptQueryShards restricts the set of shards on which a query operates.

OptQueryShards restricts the set of shards on which a query operates .


func OptQueryExcludeAttrs(enable bool) QueryOption {
	return func(options *QueryOptions) error {
		options.ExcludeRowAttrs = enable
		return nil
	}
}

func OptQueryExcludeAttrs ( enable bool ) QueryOption { return func ( options * QueryOptions ) error { options . ExcludeRowAttrs = enable return nil } }

// OptQueryExcludeAttrs enables discarding attributes from a result,


func OptQueryExcludeColumns(enable bool) QueryOption {
	return func(options *QueryOptions) error {
		options.ExcludeColumns = enable
		return nil
	}
}

func OptQueryExcludeColumns ( enable bool ) QueryOption { return func ( options * QueryOptions ) error { options . ExcludeColumns = enable return nil } }

// OptQueryExcludeColumns enables discarding columns from a result,


func OptImportThreadCount(count int) ImportOption {
	return func(options *ImportOptions) error {
		options.threadCount = count
		return nil
	}
}

func OptImportThreadCount ( count int ) ImportOption { return func ( options * ImportOptions ) error { options . threadCount = count return nil } }

// OptImportThreadCount is the number of goroutines allocated for import.

OptImportThreadCount is the number of goroutines allocated for import .


func OptImportBatchSize(batchSize int) ImportOption {
	return func(options *ImportOptions) error {
		options.batchSize = batchSize
		return nil
	}
}

func OptImportBatchSize ( batchSize int ) ImportOption { return func ( options * ImportOptions ) error { options . batchSize = batchSize return nil } }

// OptImportBatchSize is the number of records read before importing them.

OptImportBatchSize is the number of records read before importing them .


func OptImportStatusChannel(statusChan chan<- ImportStatusUpdate) ImportOption {
	return func(options *ImportOptions) error {
		options.statusChan = statusChan
		return nil
	}
}

func OptImportStatusChannel ( statusChan chan <- ImportStatusUpdate ) ImportOption { return func ( options * ImportOptions ) error { options . statusChan = statusChan return nil } }

// OptImportStatusChannel is a channel which carry import status information.
// Make sure to read from this channel, otherwise the import process will stall.

OptImportStatusChannel is a channel which carry import status information . Make sure to read from this channel otherwise the import process will stall .


func OptImportClear(clear bool) ImportOption {
	return func(options *ImportOptions) error {
		options.clear = clear
		return nil
	}
}

func OptImportClear ( clear bool ) ImportOption { return func ( options * ImportOptions ) error { options . clear = clear return nil } }

// OptImportClear sets clear import, which clears bits instead of setting them.

OptImportClear sets clear import which clears bits instead of setting them .


func OptImportRoaring(enable bool) ImportOption {
	return func(options *ImportOptions) error {
		options.wantRoaring = &enable
		return nil
	}
}

func OptImportRoaring ( enable bool ) ImportOption { return func ( options * ImportOptions ) error { options . wantRoaring = & enable return nil } }

// OptImportRoaring enables importing using roaring bitmaps which is more performant.

OptImportRoaring enables importing using roaring bitmaps which is more performant .


func OptImportSort(sorting bool) ImportOption {
	return func(options *ImportOptions) error {
		// skipSort is expressed negatively because we want to
		// keep sorting enabled by default, so the zero value should
		// be that default behavior. The client option expresses it
		// positively because that's easier for API users.
		options.skipSort = !sorting
		return nil
	}
}

// OptImportSort tells the importer whether or not to sort batches of records, on
// by default. Sorting imposes some performance cost, especially on data that's
// already sorted, but dramatically improves performance in pathological
// cases. It is enabled by default because the pathological cases are awful,
// and the performance hit is comparatively small, but the performance cost can
// be significant if you know your data is sorted.

OptImportSort tells the importer whether or not to sort batches of records on by default . Sorting imposes some performance cost especially on data that s already sorted but dramatically improves performance in pathological cases . It is enabled by default because the pathological cases are awful and the performance hit is comparatively small but the performance cost can be significant if you know your data is sorted .


func (r *exportReader) Read(p []byte) (n int, err error) {
	if r.currentShard >= r.shardCount {
		err = io.EOF
		return
	}
	if r.body == nil {
		uri, _ := r.shardURIs[r.currentShard]
		headers := map[string]string{
			"Accept": "text/csv",
		}
		path := fmt.Sprintf("/export?index=%s&field=%s&shard=%d",
			r.field.index.Name(), r.field.Name(), r.currentShard)
		resp, err := r.client.doRequest(uri, "GET", path, headers, nil)
		if err = anyError(resp, err); err != nil {
			return 0, errors.Wrap(err, "doing export request")
		}
		defer resp.Body.Close()
		r.body, err = ioutil.ReadAll(resp.Body)
		if err != nil {
			return 0, errors.Wrap(err, "reading response body")
		}
		r.bodyIndex = 0
	}
	n = copy(p, r.body[r.bodyIndex:])
	r.bodyIndex += n
	if n >= len(r.body) {
		r.body = nil
		r.currentShard++
	}
	return
}

func ( r * exportReader ) Read ( p [ ] byte ) ( n int , err error ) { if r . currentShard >= r . shardCount { err = io . EOF return } if r . body == nil { uri , _ := r . shardURIs [ r . currentShard ] headers := map [ string ] string { " " : " " , } path := fmt . Sprintf ( " " , r . field . index . Name ( ) , r . field . Name ( ) , r . currentShard ) resp , err := r . client . doRequest ( uri , " " , path , headers , nil ) if err = anyError ( resp , err ) ; err != nil { return 0 , errors . Wrap ( err , " " ) } defer resp . Body . Close ( ) r . body , err = ioutil . ReadAll ( resp . Body ) if err != nil { return 0 , errors . Wrap ( err , " " ) } r . bodyIndex = 0 } n = copy ( p , r . body [ r . bodyIndex : ] ) r . bodyIndex += n if n >= len ( r . body ) { r . body = nil r . currentShard ++ } return }

// Read updates the passed array with the exported CSV data and returns the number of bytes read


func ColumnUnmarshallerWithTimestamp(format Format, timestampFormat string) RecordUnmarshaller {
	return func(text string) (pilosa.Record, error) {
		var err error
		column := pilosa.Column{}
		parts := strings.Split(text, ",")
		if len(parts) < 2 {
			return nil, errors.New("Invalid CSV line")
		}

		hasRowKey := format == RowKeyColumnID || format == RowKeyColumnKey
		hasColumnKey := format == RowIDColumnKey || format == RowKeyColumnKey

		if hasRowKey {
			column.RowKey = parts[0]
		} else {
			column.RowID, err = strconv.ParseUint(parts[0], 10, 64)
			if err != nil {
				return nil, errors.New("Invalid row ID")
			}
		}

		if hasColumnKey {
			column.ColumnKey = parts[1]
		} else {
			column.ColumnID, err = strconv.ParseUint(parts[1], 10, 64)
			if err != nil {
				return nil, errors.New("Invalid column ID")
			}
		}

		timestamp := 0
		if len(parts) == 3 {
			if timestampFormat == "" {
				timestamp, err = strconv.Atoi(parts[2])
				if err != nil {
					return nil, err
				}
			} else {
				t, err := time.Parse(timestampFormat, parts[2])
				if err != nil {
					return nil, err
				}
				timestamp = int(t.Unix())
			}
		}
		column.Timestamp = int64(timestamp)

		return column, nil
	}
}

func ColumnUnmarshallerWithTimestamp ( format Format , timestampFormat string ) RecordUnmarshaller { return func ( text string ) ( pilosa . Record , error ) { var err error column := pilosa . Column { } parts := strings . Split ( text , " " ) if len ( parts ) < 2 { return nil , errors . New ( " " ) } hasRowKey := format == RowKeyColumnID || format == RowKeyColumnKey hasColumnKey := format == RowIDColumnKey || format == RowKeyColumnKey if hasRowKey { column . RowKey = parts [ 0 ] } else { column . RowID , err = strconv . ParseUint ( parts [ 0 ] , 10 , 64 ) if err != nil { return nil , errors . New ( " " ) } } if hasColumnKey { column . ColumnKey = parts [ 1 ] } else { column . ColumnID , err = strconv . ParseUint ( parts [ 1 ] , 10 , 64 ) if err != nil { return nil , errors . New ( " " ) } } timestamp := 0 if len ( parts ) == 3 { if timestampFormat == " " { timestamp , err = strconv . Atoi ( parts [ 2 ] ) if err != nil { return nil , err } } else { t , err := time . Parse ( timestampFormat , parts [ 2 ] ) if err != nil { return nil , err } timestamp = int ( t . Unix ( ) ) } } column . Timestamp = int64 ( timestamp ) return column , nil } }

// ColumnUnmarshallerWithTimestamp creates a RecordUnmarshaller for importing columns with the given format and timestamp format.

ColumnUnmarshallerWithTimestamp creates a RecordUnmarshaller for importing columns with the given format and timestamp format .


func NewIterator(reader io.Reader, unmarshaller RecordUnmarshaller) *Iterator {
	return &Iterator{
		reader:       reader,
		line:         0,
		scanner:      bufio.NewScanner(reader),
		unmarshaller: unmarshaller,
	}
}

func NewIterator ( reader io . Reader , unmarshaller RecordUnmarshaller ) * Iterator { return & Iterator { reader : reader , line : 0 , scanner : bufio . NewScanner ( reader ) , unmarshaller : unmarshaller , } }

// NewIterator creates a CSVIterator from a Reader.

NewIterator creates a CSVIterator from a Reader .


func NewColumnIterator(format Format, reader io.Reader) *Iterator {
	return NewIterator(reader, ColumnUnmarshaller(format))
}

func NewColumnIterator ( format Format , reader io . Reader ) * Iterator { return NewIterator ( reader , ColumnUnmarshaller ( format ) ) }

// NewColumnIterator creates a new iterator for column data.

NewColumnIterator creates a new iterator for column data .


func NewColumnIteratorWithTimestampFormat(format Format, reader io.Reader, timestampFormat string) *Iterator {
	return NewIterator(reader, ColumnUnmarshallerWithTimestamp(format, timestampFormat))
}

func NewColumnIteratorWithTimestampFormat ( format Format , reader io . Reader , timestampFormat string ) * Iterator { return NewIterator ( reader , ColumnUnmarshallerWithTimestamp ( format , timestampFormat ) ) }

// NewColumnIteratorWithTimestampFormat creates a new iterator for column data with timestamp.

NewColumnIteratorWithTimestampFormat creates a new iterator for column data with timestamp .


func NewValueIterator(format Format, reader io.Reader) *Iterator {
	return NewIterator(reader, FieldValueUnmarshaller(format))
}

func NewValueIterator ( format Format , reader io . Reader ) * Iterator { return NewIterator ( reader , FieldValueUnmarshaller ( format ) ) }

// NewValueIterator creates a new iterator for value data.

NewValueIterator creates a new iterator for value data .


func (c *Iterator) NextRecord() (pilosa.Record, error) {
	if ok := c.scanner.Scan(); ok {
		c.line++
		text := strings.TrimSpace(c.scanner.Text())
		if text != "" {
			rc, err := c.unmarshaller(text)
			if err != nil {
				return nil, fmt.Errorf("%s at line: %d", err.Error(), c.line)
			}
			return rc, nil
		}
	}
	err := c.scanner.Err()
	if err != nil {
		return nil, err
	}
	return nil, io.EOF
}

func ( c * Iterator ) NextRecord ( ) ( pilosa . Record , error ) { if ok := c . scanner . Scan ( ) ; ok { c . line ++ text := strings . TrimSpace ( c . scanner . Text ( ) ) if text != " " { rc , err := c . unmarshaller ( text ) if err != nil { return nil , fmt . Errorf ( " " , err . Error ( ) , c . line ) } return rc , nil } } err := c . scanner . Err ( ) if err != nil { return nil , err } return nil , io . EOF }

// NextRecord iterates on lines of a Reader.
// Returns io.EOF on end of iteration.

NextRecord iterates on lines of a Reader . Returns io . EOF on end of iteration .


func FieldValueUnmarshaller(format Format) RecordUnmarshaller {
	return func(text string) (pilosa.Record, error) {
		parts := strings.Split(text, ",")
		if len(parts) < 2 {
			return nil, errors.New("Invalid CSV")
		}
		value, err := strconv.ParseInt(parts[1], 10, 64)
		if err != nil {
			return nil, errors.New("Invalid value")
		}
		switch format {
		case ColumnID:
			columnID, err := strconv.ParseUint(parts[0], 10, 64)
			if err != nil {
				return nil, errors.New("Invalid column ID at line: %d")
			}
			return pilosa.FieldValue{
				ColumnID: uint64(columnID),
				Value:    value,
			}, nil
		case ColumnKey:
			return pilosa.FieldValue{
				ColumnKey: parts[0],
				Value:     value,
			}, nil
		default:
			return nil, fmt.Errorf("Invalid format: %d", format)
		}
	}
}

func FieldValueUnmarshaller ( format Format ) RecordUnmarshaller { return func ( text string ) ( pilosa . Record , error ) { parts := strings . Split ( text , " " ) if len ( parts ) < 2 { return nil , errors . New ( " " ) } value , err := strconv . ParseInt ( parts [ 1 ] , 10 , 64 ) if err != nil { return nil , errors . New ( " " ) } switch format { case ColumnID : columnID , err := strconv . ParseUint ( parts [ 0 ] , 10 , 64 ) if err != nil { return nil , errors . New ( " " ) } return pilosa . FieldValue { ColumnID : uint64 ( columnID ) , Value : value , } , nil case ColumnKey : return pilosa . FieldValue { ColumnKey : parts [ 0 ] , Value : value , } , nil default : return nil , fmt . Errorf ( " " , format ) } } }

// FieldValueUnmarshaller is a function which creates a Record from a CSV file line with value data.

FieldValueUnmarshaller is a function which creates a Record from a CSV file line with value data .


func MergeConfig(a, b *Config) *Config {
	// Return quickly if either side was nil
	if a == nil {
		return b
	}
	if b == nil {
		return a
	}

	var result Config = *a

	if b.Delim != "" {
		result.Delim = b.Delim
	}
	if b.Glue != "" {
		result.Glue = b.Glue
	}
	if b.Prefix != "" {
		result.Prefix = b.Prefix
	}
	if b.Empty != "" {
		result.Empty = b.Empty
	}
	if b.NoTrim {
		result.NoTrim = true
	}

	return &result
}

// MergeConfig merges two config objects together and returns the resulting
// configuration. Values from the right take precedence over the left side.

MergeConfig merges two config objects together and returns the resulting configuration . Values from the right take precedence over the left side .


func stringFormat(c *Config, widths []int, columns int) string {
	// Create the buffer with an estimate of the length
	buf := bytes.NewBuffer(make([]byte, 0, (6+len(c.Glue))*columns))

	// Start with the prefix, if any was given. The buffer will not return an
	// error so it does not need to be handled
	buf.WriteString(c.Prefix)

	// Create the format string from the discovered widths
	for i := 0; i < columns && i < len(widths); i++ {
		if i == columns-1 {
			buf.WriteString("%s\n")
		} else {
			fmt.Fprintf(buf, "%%-%ds%s", widths[i], c.Glue)
		}
	}
	return buf.String()
}

// stringFormat, given a set of column widths and the number of columns in
// the current line, returns a sprintf-style format string which can be used
// to print output aligned properly with other lines using the same widths set.

stringFormat given a set of column widths and the number of columns in the current line returns a sprintf - style format string which can be used to print output aligned properly with other lines using the same widths set .


func elementsFromLine(config *Config, line string) []interface{} {
	separated := strings.Split(line, config.Delim)
	elements := make([]interface{}, len(separated))
	for i, field := range separated {
		value := field
		if !config.NoTrim {
			value = strings.TrimSpace(field)
		}

		// Apply the empty value, if configured.
		if value == "" && config.Empty != "" {
			value = config.Empty
		}
		elements[i] = value
	}
	return elements
}

// elementsFromLine returns a list of elements, each representing a single
// item which will belong to a column of output.

elementsFromLine returns a list of elements each representing a single item which will belong to a column of output .


func widthsFromLines(config *Config, lines []string) []int {
	widths := make([]int, 0, 8)

	for _, line := range lines {
		elems := elementsFromLine(config, line)
		for i := 0; i < len(elems); i++ {
			l := runeLen(elems[i].(string))
			if len(widths) <= i {
				widths = append(widths, l)
			} else if widths[i] < l {
				widths[i] = l
			}
		}
	}
	return widths
}

func widthsFromLines ( config * Config , lines [ ] string ) [ ] int { widths := make ( [ ] int , 0 , 8 ) for _ , line := range lines { elems := elementsFromLine ( config , line ) for i := 0 ; i < len ( elems ) ; i ++ { l := runeLen ( elems [ i ] . ( string ) ) if len ( widths ) <= i { widths = append ( widths , l ) } else if widths [ i ] < l { widths [ i ] = l } } } return widths }

// widthsFromLines examines a list of strings and determines how wide each
// column should be considering all of the elements that need to be printed
// within it.

widthsFromLines examines a list of strings and determines how wide each column should be considering all of the elements that need to be printed within it .


func Format(lines []string, config *Config) string {
	conf := MergeConfig(DefaultConfig(), config)
	widths := widthsFromLines(conf, lines)

	// Estimate the buffer size
	glueSize := len(conf.Glue)
	var size int
	for _, w := range widths {
		size += w + glueSize
	}
	size *= len(lines)

	// Create the buffer
	buf := bytes.NewBuffer(make([]byte, 0, size))

	// Create a cache for the string formats
	fmtCache := make(map[int]string, 16)

	// Create the formatted output using the format string
	for _, line := range lines {
		elems := elementsFromLine(conf, line)

		// Get the string format using cache
		numElems := len(elems)
		stringfmt, ok := fmtCache[numElems]
		if !ok {
			stringfmt = stringFormat(conf, widths, numElems)
			fmtCache[numElems] = stringfmt
		}

		fmt.Fprintf(buf, stringfmt, elems...)
	}

	// Get the string result
	result := buf.String()

	// Remove trailing newline without removing leading/trailing space
	if n := len(result); n > 0 && result[n-1] == '\n' {
		result = result[:n-1]
	}

	return result
}

// Format is the public-facing interface that takes a list of strings and
// returns nicely aligned column-formatted text.

Format is the public - facing interface that takes a list of strings and returns nicely aligned column - formatted text .


func (m *GorillaHost) Build(u *url.URL, values url.Values) error {
	host, err := m.RevertValid(values)
	if err == nil {
		if u.Scheme == "" {
			u.Scheme = "http"
		}
		u.Host = host
	}
	return err
}

func ( m * GorillaHost ) Build ( u * url . URL , values url . Values ) error { host , err := m . RevertValid ( values ) if err == nil { if u . Scheme == " " { u . Scheme = " " } u . Host = host } return err }

// Build builds the URL host using the given positional and named variables,
// and writes it to the given URL.

Build builds the URL host using the given positional and named variables and writes it to the given URL .


func (m *GorillaPathPrefix) Build(u *url.URL, values url.Values) error {
	path, err := m.RevertValid(values)
	if err == nil {
		u.Path = path
	}
	return err
}

func ( m * GorillaPathPrefix ) Build ( u * url . URL , values url . Values ) error { path , err := m . RevertValid ( values ) if err == nil { u . Path = path } return err }

// Build builds the URL path using the given positional and named variables,
// and writes it to the given URL.

Build builds the URL path using the given positional and named variables and writes it to the given URL .


func braceIndices(s string) ([]int, error) {
	var level, idx int
	idxs := make([]int, 0)
	for i := 0; i < len(s); i++ {
		switch s[i] {
		case '{':
			if level++; level == 1 {
				idx = i
			}
		case '}':
			if level--; level == 0 {
				idxs = append(idxs, idx, i+1)
			} else if level < 0 {
				return nil, fmt.Errorf("mux: unbalanced braces in %q", s)
			}
		}
	}
	if level != 0 {
		return nil, fmt.Errorf("mux: unbalanced braces in %q", s)
	}
	return idxs, nil
}

func braceIndices ( s string ) ( [ ] int , error ) { var level , idx int idxs := make ( [ ] int , 0 ) for i := 0 ; i < len ( s ) ; i ++ { switch s [ i ] { case '{' : if level ++ ; level == 1 { idx = i } case '}' : if level -- ; level == 0 { idxs = append ( idxs , idx , i + 1 ) } else if level < 0 { return nil , fmt . Errorf ( " " , s ) } } } if level != 0 { return nil , fmt . Errorf ( " " , s ) } return idxs , nil }

// braceIndices returns the first level curly brace indices from a string.
// It returns an error in case of unbalanced braces.

braceIndices returns the first level curly brace indices from a string . It returns an error in case of unbalanced braces .


func (m *RegexpHost) Extract(result *Result, r *http.Request) {
	result.Values = mergeValues(result.Values, m.Values(getHost(r)))
}

func ( m * RegexpHost ) Extract ( result * Result , r * http . Request ) { result . Values = mergeValues ( result . Values , m . Values ( getHost ( r ) ) ) }

// Extract returns positional and named variables extracted from the URL host.

Extract returns positional and named variables extracted from the URL host .


func mergeValues(u1, u2 url.Values) url.Values {
	if u1 == nil {
		return u2
	}
	if u2 == nil {
		return u1
	}
	for k, v := range u2 {
		u1[k] = append(u1[k], v...)
	}
	return u1
}

func mergeValues ( u1 , u2 url . Values ) url . Values { if u1 == nil { return u2 } if u2 == nil { return u1 } for k , v := range u2 { u1 [ k ] = append ( u1 [ k ] , v ... ) } return u1 }

// mergeValues returns the result of merging two url.Values.

mergeValues returns the result of merging two url . Values .


func redirectPath(path string, r *http.Request) http.Handler {
	t1 := strings.HasSuffix(path, "/")
	t2 := strings.HasSuffix(r.URL.Path, "/")
	if t1 != t2 {
		u, _ := url.Parse(r.URL.String())
		if t1 {
			u.Path += "/"
		} else {
			u.Path = u.Path[:len(u.Path)-1]
		}
		return http.RedirectHandler(u.String(), 301)
	}
	return nil
}

func redirectPath ( path string , r * http . Request ) http . Handler { t1 := strings . HasSuffix ( path , " " ) t2 := strings . HasSuffix ( r . URL . Path , " " ) if t1 != t2 { u , _ := url . Parse ( r . URL . String ( ) ) if t1 { u . Path += " " } else { u . Path = u . Path [ : len ( u . Path ) - 1 ] } return http . RedirectHandler ( u . String ( ) , 301 ) } return nil }

// redirectPath returns a handler that redirects if the path trailing slash
// differs from the request URL path.

redirectPath returns a handler that redirects if the path trailing slash differs from the request URL path .


func CompileRegexp(pattern string) (*Regexp, error) {
	compiled, err := regexp.Compile(pattern)
	if err != nil {
		return nil, err
	}
	re, err := syntax.Parse(pattern, syntax.Perl)
	if err != nil {
		return nil, err
	}
	tpl := &template{buffer: new(bytes.Buffer)}
	tpl.write(re)
	return &Regexp{
		compiled: compiled,
		template: tpl.buffer.String(),
		groups:   tpl.groups,
		indices:  tpl.indices,
	}, nil
}

func CompileRegexp ( pattern string ) ( * Regexp , error ) { compiled , err := regexp . Compile ( pattern ) if err != nil { return nil , err } re , err := syntax . Parse ( pattern , syntax . Perl ) if err != nil { return nil , err } tpl := & template { buffer : new ( bytes . Buffer ) } tpl . write ( re ) return & Regexp { compiled : compiled , template : tpl . buffer . String ( ) , groups : tpl . groups , indices : tpl . indices , } , nil }

// CompileRegexp compiles a regular expression pattern and creates a template
// to revert it.

CompileRegexp compiles a regular expression pattern and creates a template to revert it .


func (r *Regexp) MatchString(s string) bool {
	return r.compiled.MatchString(s)
}

func ( r * Regexp ) MatchString ( s string ) bool { return r . compiled . MatchString ( s ) }

// Match returns whether the regexp matches the given string.

Match returns whether the regexp matches the given string .


func (r *Regexp) Values(s string) url.Values {
	match := r.compiled.FindStringSubmatch(s)
	if match != nil {
		values := url.Values{}
		for k, v := range r.groups {
			values.Add(v, match[r.indices[k]])
		}
		return values
	}
	return nil
}

func ( r * Regexp ) Values ( s string ) url . Values { match := r . compiled . FindStringSubmatch ( s ) if match != nil { values := url . Values { } for k , v := range r . groups { values . Add ( v , match [ r . indices [ k ] ] ) } return values } return nil }

// Values matches the regexp and returns the results for positional and
// named groups. Positional values are stored using an empty string as key.
// If the string doesn't match it returns nil.

Values matches the regexp and returns the results for positional and named groups . Positional values are stored using an empty string as key . If the string doesn t match it returns nil .


func (r *Regexp) Revert(values url.Values) (string, error) {
	vars := make([]interface{}, len(r.groups))
	for k, v := range r.groups {
		if len(values[v]) == 0 {
			return "", fmt.Errorf(
				"Missing key %q to revert the regexp "+
					"(expected a total of %d variables)", v, len(r.groups))
		}
		vars[k] = values[v][0]
		values[v] = values[v][1:]
	}
	return fmt.Sprintf(r.template, vars...), nil
}

func ( r * Regexp ) Revert ( values url . Values ) ( string , error ) { vars := make ( [ ] interface { } , len ( r . groups ) ) for k , v := range r . groups { if len ( values [ v ] ) == 0 { return " " , fmt . Errorf ( " " + " " , v , len ( r . groups ) ) } vars [ k ] = values [ v ] [ 0 ] values [ v ] = values [ v ] [ 1 : ] } return fmt . Sprintf ( r . template , vars ... ) , nil }

// Revert builds a string for this regexp using the given values. Positional
// values use an empty string as key.
//
// The values are modified in place, and only the unused ones are left.

Revert builds a string for this regexp using the given values . Positional values use an empty string as key . The values are modified in place and only the unused ones are left .


func (r *Regexp) RevertValid(values url.Values) (string, error) {
	reverse, err := r.Revert(values)
	if err != nil {
		return "", err
	}
	if !r.compiled.MatchString(reverse) {
		return "", fmt.Errorf("Resulting string doesn't match the regexp: %q",
			reverse)
	}
	return reverse, nil
}

func ( r * Regexp ) RevertValid ( values url . Values ) ( string , error ) { reverse , err := r . Revert ( values ) if err != nil { return " " , err } if ! r . compiled . MatchString ( reverse ) { return " " , fmt . Errorf ( " " , reverse ) } return reverse , nil }

// RevertValid is the same as Revert but it also validates the resulting
// string matching it against the compiled regexp.
//
// The values are modified in place, and only the unused ones are left.

RevertValid is the same as Revert but it also validates the resulting string matching it against the compiled regexp . The values are modified in place and only the unused ones are left .


func (t *template) write(re *syntax.Regexp) {
	switch re.Op {
	case syntax.OpLiteral:
		if t.level == 0 {
			for _, r := range re.Rune {
				t.buffer.WriteRune(r)
				if r == '%' {
					t.buffer.WriteRune('%')
				}
			}
		}
	case syntax.OpCapture:
		t.level++
		t.index++
		if t.level == 1 {
			t.groups = append(t.groups, re.Name)
			t.indices = append(t.indices, t.index)
			t.buffer.WriteString("%s")
		}
		for _, sub := range re.Sub {
			t.write(sub)
		}
		t.level--
	case syntax.OpConcat:
		for _, sub := range re.Sub {
			t.write(sub)
		}
	}
}

func ( t * template ) write ( re * syntax . Regexp ) { switch re . Op { case syntax . OpLiteral : if t . level == 0 { for _ , r := range re . Rune { t . buffer . WriteRune ( r ) if r == '%' { t . buffer . WriteRune ( '%' ) } } } case syntax . OpCapture : t . level ++ t . index ++ if t . level == 1 { t . groups = append ( t . groups , re . Name ) t . indices = append ( t . indices , t . index ) t . buffer . WriteString ( " " ) } for _ , sub := range re . Sub { t . write ( sub ) } t . level -- case syntax . OpConcat : for _ , sub := range re . Sub { t . write ( sub ) } } }

// write writes a reverse template to the buffer.

write writes a reverse template to the buffer .


func NewMultiple(buf []byte, options Options, h hash.Hash, compareOnMatch bool) *Cmp {
	c := &Cmp{
		Opt:              options,
		hashType:         h,
		hashMatchCompare: compareOnMatch,
		hashTable:        map[string]hashSum{},
		buf:              buf,
	}
	if c.buf == nil || len(c.buf) == 0 {
		c.buf = make([]byte, defaultBufSize)
	}
	c.debugf("New: bufSize=%d\n", len(c.buf))
	return c
}

func NewMultiple ( buf [ ] byte , options Options , h hash . Hash , compareOnMatch bool ) * Cmp { c := & Cmp { Opt : options , hashType : h , hashMatchCompare : compareOnMatch , hashTable : map [ string ] hashSum { } , buf : buf , } if c . buf == nil || len ( c . buf ) == 0 { c . buf = make ( [ ] byte , defaultBufSize ) } c . debugf ( " \n " , len ( c . buf ) ) return c }

// New creates Cmp for multiple comparison mode.

New creates Cmp for multiple comparison mode .


func New(buf []byte, options Options) *Cmp {
	return NewMultiple(buf, options, nil, true)
}

func New ( buf [ ] byte , options Options ) * Cmp { return NewMultiple ( buf , options , nil , true ) }

// New creates Cmp for single comparison mode.

New creates Cmp for single comparison mode .


func (c *Cmp) CompareFile(path1, path2 string) (bool, error) {

	if c.Opt.MaxSize < 0 {
		return false, fmt.Errorf("negative MaxSize")
	}

	r1, openErr1 := os.Open(path1)
	if openErr1 != nil {
		return false, openErr1
	}
	defer r1.Close()
	info1, statErr1 := r1.Stat()
	if statErr1 != nil {
		return false, statErr1
	}

	r2, openErr2 := os.Open(path2)
	if openErr2 != nil {
		return false, openErr2
	}
	defer r2.Close()
	info2, statErr2 := r2.Stat()
	if statErr2 != nil {
		return false, statErr2
	}

	if !c.Opt.ForceFileRead {
		// shortcut: ask the filesystem: are these files the same? (link, pathname, etc)
		if os.SameFile(info1, info2) {
			c.debugf("CompareFile(%s,%s): os reported same file\n", path1, path2)
			return true, nil
		}
	}

	if info1.Mode().IsRegular() && info2.Mode().IsRegular() {
		if info1.Size() != info2.Size() {
			c.debugf("CompareFile(%s,%s): distinct file sizes\n", path1, path2)
			return false, nil
		}
	}

	// If Opt.MaxSize not initialized, set maxSize to an appropriate value
	// for comparing regular files or streams (pipes, devices), etc.
	// Pass maxSize to getHash to ensure the hash is computed only up to
	// the user specified MaxSize amount.
	maxSize := c.Opt.MaxSize
	if maxSize == 0 {
		// If comparing regular to non-regular file, sizes may not
		// agree...  Use the larger value.
		maxSize = info1.Size()
		if maxSize < info2.Size() {
			maxSize = info2.Size()
		}
		if maxSize == 0 { // possible non-regular files
			maxSize = defaultMaxSize
		}
	}

	if c.multipleMode() {
		h1, err1 := c.getHash(path1, maxSize)
		if err1 != nil {
			return false, err1
		}
		h2, err2 := c.getHash(path2, maxSize)
		if err2 != nil {
			return false, err2
		}
		if !bytes.Equal(h1, h2) {
			return false, nil // hashes mismatch
		}
		// hashes match
		if !c.hashMatchCompare {
			return true, nil // accept hash match without byte-by-byte comparison
		}
		// do byte-by-byte comparison
		c.debugf("CompareFile(%s,%s): hash match, will compare bytes\n", path1, path2)
	}

	// Use our maxSize to avoid triggering the defaultMaxSize for files.
	// We still need to preserve the error returning properties of the
	// input amount exceeding MaxSize, so we can't use LimitedReader.
	c.resetDebugging()

	eq, err := c.compareReader(r1, r2, maxSize)

	c.printDebugCompareReader()

	return eq, err
}

// CompareFile verifies that files with names path1, path2 have same contents.

CompareFile verifies that files with names path1 path2 have same contents .


func readPartial(c *Cmp, r io.Reader, buf []byte, n1, n2 int) (int, error) {
	for n1 < n2 {
		n, err := c.read(r, buf[n1:n2])
		n1 += n
		if err != nil {
			return n1, err
		}
	}
	return n1, nil
}

func readPartial ( c * Cmp , r io . Reader , buf [ ] byte , n1 , n2 int ) ( int , error ) { for n1 < n2 { n , err := c . read ( r , buf [ n1 : n2 ] ) n1 += n if err != nil { return n1 , err } } return n1 , nil }

// readPartial keeps reading from reader into provided buffer,
// until buffer size reaches exactly n2. n1 is initial buffer size.
// useful to ensure we get an specific buffer size from reader,
// withstanding partial reads.

readPartial keeps reading from reader into provided buffer until buffer size reaches exactly n2 . n1 is initial buffer size . useful to ensure we get an specific buffer size from reader withstanding partial reads .


func postEOFCheck(c *Cmp, r io.Reader, buf []byte) bool {
	tmpLR, isLR := r.(*io.LimitedReader)
	if isLR {
		// If the limit wasn't reached, then we don't need to check for
		// more data after the EOF
		if tmpLR.N > 0 {
			return true
		}

		// Use the internal Reader for checking for more data
		r = tmpLR.R
	} else {
		c.debugf("compareReader: A type assertion of LimitedReader unexpectedly failed\n")
	}

	// Attempt to read more bytes from the original readers, to determine
	// if we should return an error for exceeding the MaxSize read limit.
	n, _ := readPartial(c, r, buf, 0, len(buf))
	return n == 0
}

// postEOFCheck returns false if there is more data in a LimitedReader after
// hitting EOF

postEOFCheck returns false if there is more data in a LimitedReader after hitting EOF


func (h *HAProxyClient) Stats() (stats []*Stat, err error) {
	res, err := h.RunCommand("show stat")
	if err != nil {
		return nil, err
	}

	reader := csv.NewReader(res)
	reader.TrailingComma = true
	err = gocsv.UnmarshalCSV(reader, &stats)
	if err != nil {
		return nil, fmt.Errorf("error reading csv: %s", err)
	}

	//	for _, s := range allStats {
	//		switch s.SvName {
	//		case "FRONTEND":
	//			services.Frontends = append(services.Frontends, s)
	//		case "BACKEND":
	//			services.Backends = append(services.Backends, s)
	//		default:
	//			services.Listeners = append(services.Listeners, s)
	//		}
	//	}

	return stats, nil
}

// Equivalent to HAProxy "show stat" command.

Equivalent to HAProxy show stat command .


func (h *HAProxyClient) Info() (*Info, error) {
	res, err := h.RunCommand("show info")
	if err != nil {
		return nil, err
	}
	info := &Info{}
	err = kvcodec.Unmarshal(res, info)
	if err != nil {
		return nil, fmt.Errorf("error decoding response: %s", err)
	}
	return info, nil
}

func ( h * HAProxyClient ) Info ( ) ( * Info , error ) { res , err := h . RunCommand ( " " ) if err != nil { return nil , err } info := & Info { } err = kvcodec . Unmarshal ( res , info ) if err != nil { return nil , fmt . Errorf ( " " , err ) } return info , nil }

// Equivalent to HAProxy "show info" command.

Equivalent to HAProxy show info command .


func (h *HAProxyClient) RunCommand(cmd string) (*bytes.Buffer, error) {
	err := h.dial()
	if err != nil {
		return nil, err
	}
	defer h.conn.Close()

	result := bytes.NewBuffer(nil)

	_, err = h.conn.Write([]byte(cmd + "\n"))
	if err != nil {
		return nil, err
	}

	_, err = io.Copy(result, h.conn)
	if err != nil {
		return nil, err
	}

	if strings.HasPrefix(result.String(), "Unknown command") {
		return nil, fmt.Errorf("Unknown command: %s", cmd)
	}

	return result, nil
}

func ( h * HAProxyClient ) RunCommand ( cmd string ) ( * bytes . Buffer , error ) { err := h . dial ( ) if err != nil { return nil , err } defer h . conn . Close ( ) result := bytes . NewBuffer ( nil ) _ , err = h . conn . Write ( [ ] byte ( cmd + " \n " ) ) if err != nil { return nil , err } _ , err = io . Copy ( result , h . conn ) if err != nil { return nil , err } if strings . HasPrefix ( result . String ( ) , " " ) { return nil , fmt . Errorf ( " " , cmd ) } return result , nil }

// RunCommand is the entrypoint to the client. Sends an arbitray command string to HAProxy.

RunCommand is the entrypoint to the client . Sends an arbitray command string to HAProxy .


func IsExist(err error) bool {
	if err == ErrUserExist || err == ErrGroupExist {
		return true
	}
	return false
}

func IsExist ( err error ) bool { if err == ErrUserExist || err == ErrGroupExist { return true } return false }

// IsExist returns whether the error is known to report that an user or group
// already exists. It is satisfied by ErrUserExist and ErrGroupExist.

IsExist returns whether the error is known to report that an user or group already exists . It is satisfied by ErrUserExist and ErrGroupExist .


func exist(file string) (bool, error) {
	_, err := os.Stat(file)
	if err != nil {
		if err == os.ErrNotExist {
			return false, nil
		}
		return false, err
	}
	return true, nil
}

func exist ( file string ) ( bool , error ) { _ , err := os . Stat ( file ) if err != nil { if err == os . ErrNotExist { return false , nil } return false , err } return true , nil }


func Contain(filename string, b []byte) (bool, error) {
	f, err := os.Open(filename)
	if err != nil {
		return false, fmt.Errorf("Contain: %s", err)
	}
	defer f.Close()

	buf := bufio.NewReader(f)

	for {
		line, err := buf.ReadBytes('\n')
		if err == io.EOF {
			break
		}
		if bytes.Contains(line, b) {
			return true, nil
		}
	}
	return false, nil
}

func Contain ( filename string , b [ ] byte ) ( bool , error ) { f , err := os . Open ( filename ) if err != nil { return false , fmt . Errorf ( " " , err ) } defer f . Close ( ) buf := bufio . NewReader ( f ) for { line , err := buf . ReadBytes ( '\n' ) if err == io . EOF { break } if bytes . Contains ( line , b ) { return true , nil } } return false , nil }

// Contain returns whether the named file contains the byte slice b. The
// return value is a boolean.

Contain returns whether the named file contains the byte slice b . The return value is a boolean .


func ContainString(filename, s string) (bool, error) {
	f, err := os.Open(filename)
	if err != nil {
		return false, fmt.Errorf("ContainString: %s", err)
	}
	defer f.Close()

	buf := bufio.NewReader(f)

	for {
		line, err := buf.ReadString('\n')
		if err == io.EOF {
			break
		}
		if strings.Contains(line, s) {
			return true, nil
		}
	}
	return false, nil
}

func ContainString ( filename , s string ) ( bool , error ) { f , err := os . Open ( filename ) if err != nil { return false , fmt . Errorf ( " " , err ) } defer f . Close ( ) buf := bufio . NewReader ( f ) for { line , err := buf . ReadString ( '\n' ) if err == io . EOF { break } if strings . Contains ( line , s ) { return true , nil } } return false , nil }

// ContainString returns whether the named file contains the string s. The
// return value is a boolean.

ContainString returns whether the named file contains the string s . The return value is a boolean .


func init() {
	log.SetFlags(0)
	log.SetPrefix("ERROR: ")

	if BOOT {
		_ENV = []string{"PATH=" + PATH} // from file boot
	} else {
		_ENV = os.Environ()
		_HOME = os.Getenv("HOME")
	}

	/*if path := os.Getenv("PATH"); path == "" {
		if err = os.Setenv("PATH", PATH); err != nil {
			log.Print(err)
		}
	}*/
}

// Sets environment variables and a null logger.

Sets environment variables and a null logger .


func StartLogger() {
	var err error

	if BOOT {
		if logFile, err = os.OpenFile(_LOG_FILE, os.O_WRONLY|os.O_TRUNC, 0); err != nil {
			log.Print(err)
		} else {
			Log = log.New(logFile, "", log.Lshortfile)
		}
	} else {
		if Log, err = syslog.NewLogger(syslog.LOG_NOTICE, log.Lshortfile); err != nil {
			log.Fatal(err)
		}
	}
}

func StartLogger ( ) { var err error if BOOT { if logFile , err = os . OpenFile ( _LOG_FILE , os . O_WRONLY | os . O_TRUNC , 0 ) ; err != nil { log . Print ( err ) } else { Log = log . New ( logFile , " " , log . Lshortfile ) } } else { if Log , err = syslog . NewLogger ( syslog . LOG_NOTICE , log . Lshortfile ) ; err != nil { log . Fatal ( err ) } } }

// StartLogger initializes the log file.


func openDBFile(filename string, flag int) (*dbfile, error) {
	f, err := os.OpenFile(filename, flag, 0)
	if err != nil {
		return nil, err
	}

	db := &dbfile{file: f, rd: bufio.NewReader(f)}
	db.Lock()
	return db, nil
}

func openDBFile ( filename string , flag int ) ( * dbfile , error ) { f , err := os . OpenFile ( filename , flag , 0 ) if err != nil { return nil , err } db := & dbfile { file : f , rd : bufio . NewReader ( f ) } db . Lock ( ) return db , nil }


func backup(filename string) error {
	if DO_BACKUP {
		if _, ok := _FILES_BACKUPED[filename]; !ok {
			if err := file.Backup(filename); err != nil {
				return err
			}
			_FILES_BACKUPED[filename] = struct{}{}
		}
	}
	return nil
}

func backup ( filename string ) error { if DO_BACKUP { if _ , ok := _FILES_BACKUPED [ filename ] ; ! ok { if err := file . Backup ( filename ) ; err != nil { return err } _FILES_BACKUPED [ filename ] = struct { } { } } } return nil }


func New(pkg PackageType) Packager {
	switch pkg {
	case Deb:
		return new(deb)
	case RPM:
		return new(rpm)
	case Pacman:
		return new(pacman)
	case Ebuild:
		return new(ebuild)
	case ZYpp:
		return new(zypp)
	}
	panic("unreachable")
}

func New ( pkg PackageType ) Packager { switch pkg { case Deb : return new ( deb ) case RPM : return new ( rpm ) case Pacman : return new ( pacman ) case Ebuild : return new ( ebuild ) case ZYpp : return new ( zypp ) } panic ( " " ) }

// New returns the interface to handle the package manager.

New returns the interface to handle the package manager .


func RegisterCrypt(c Crypt, f func() Crypter, prefix string) {
	if c >= maxCrypt {
		panic("crypt: RegisterHash of unknown crypt function")
	}
	crypts[c] = f
	cryptPrefixes[c] = prefix
}

func RegisterCrypt ( c Crypt , f func ( ) Crypter , prefix string ) { if c >= maxCrypt { panic ( " " ) } crypts [ c ] = f cryptPrefixes [ c ] = prefix }

// RegisterCrypt registers a function that returns a new instance of the given
// crypt function. This is intended to be called from the init function in
// packages that implement crypt functions.

RegisterCrypt registers a function that returns a new instance of the given crypt function . This is intended to be called from the init function in packages that implement crypt functions .


func New(c Crypt) Crypter {
	f := crypts[c]
	if f != nil {
		return f()
	}
	panic("crypt: requested crypt function is unavailable")
}

func New ( c Crypt ) Crypter { f := crypts [ c ] if f != nil { return f ( ) } panic ( " " ) }


func NewFromHash(hashedKey string) Crypter {
	var f func() Crypter

	if strings.HasPrefix(hashedKey, cryptPrefixes[SHA512]) {
		f = crypts[SHA512]
	} else if strings.HasPrefix(hashedKey, cryptPrefixes[SHA256]) {
		f = crypts[SHA256]
	} else if strings.HasPrefix(hashedKey, cryptPrefixes[MD5]) {
		f = crypts[MD5]
	} else if strings.HasPrefix(hashedKey, cryptPrefixes[APR1]) {
		f = crypts[APR1]
	} else {
		toks := strings.SplitN(hashedKey, "$", 3)
		prefix := "$" + toks[1] + "$"
		panic("crypt: unknown cryp function from prefix: " + prefix)
	}

	if f != nil {
		return f()
	}
	panic("crypt: requested cryp function is unavailable")
}

func NewFromHash ( hashedKey string ) Crypter { var f func ( ) Crypter if strings . HasPrefix ( hashedKey , cryptPrefixes [ SHA512 ] ) { f = crypts [ SHA512 ] } else if strings . HasPrefix ( hashedKey , cryptPrefixes [ SHA256 ] ) { f = crypts [ SHA256 ] } else if strings . HasPrefix ( hashedKey , cryptPrefixes [ MD5 ] ) { f = crypts [ MD5 ] } else if strings . HasPrefix ( hashedKey , cryptPrefixes [ APR1 ] ) { f = crypts [ APR1 ] } else { toks := strings . SplitN ( hashedKey , " " , 3 ) prefix := " " + toks [ 1 ] + " " panic ( " " + prefix ) } if f != nil { return f ( ) } panic ( " " ) }

// NewFromHash returns a new Crypter using the prefix in the given hashed key.

NewFromHash returns a new Crypter using the prefix in the given hashed key .


func (s *Scanner) Text() (key, value string) {
	return string(s.key), string(s.value)
}

func ( s * Scanner ) Text ( ) ( key , value string ) { return string ( s . key ) , string ( s . value ) }

// Text returns the most recents tokens generated by a call to Scan as a newly
// allocated string holding its bytes.

Text returns the most recents tokens generated by a call to Scan as a newly allocated string holding its bytes .


func Exec(cmd string, args ...string) error {
	c := exec.Command(cmd, args...)
	c.Stdin = os.Stdin
	c.Stdout = os.Stdout
	c.Stderr = os.Stderr

	if err := c.Run(); err != nil {
		return err
	}
	return nil
}

func Exec ( cmd string , args ... string ) error { c := exec . Command ( cmd , args ... ) c . Stdin = os . Stdin c . Stdout = os . Stdout c . Stderr = os . Stderr if err := c . Run ( ) ; err != nil { return err } return nil }

// Exec executes a command setting both standard input, output and error.

Exec executes a command setting both standard input output and error .


func ExecSudo(cmd string, args ...string) error {
	return Exec("sudo", append([]string{cmd}, args...)...)
}

func ExecSudo ( cmd string , args ... string ) error { return Exec ( " " , append ( [ ] string { cmd } , args ... ) ... ) }

// ExecSudo executes a command under "sudo".

ExecSudo executes a command under sudo .


func nextUID(isSystem bool) (db *dbfile, uid int, err error) {
	loadConfig()

	db, err = openDBFile(_USER_FILE, os.O_RDWR)
	if err != nil {
		return
	}

	// Seek to file half size.

	info, err := db.file.Stat()
	if err != nil {
		db.close()
		return nil, 0, err
	}
	if _, err = db.file.Seek(info.Size()/2, os.SEEK_SET); err != nil {
		db.close()
		return nil, 0, err
	}
	// To starting to read from a new line
	if _, _, err = db.rd.ReadLine(); err != nil {
		db.close()
		return nil, 0, err
	}

	var minUid, maxUid int
	if isSystem {
		minUid, maxUid = config.login.SYS_UID_MIN, config.login.SYS_UID_MAX
	} else {
		minUid, maxUid = config.login.UID_MIN, config.login.UID_MAX
	}

	for {
		line, _, err := db.rd.ReadLine()
		if err == io.EOF {
			break
		}

		u, err := parseUser(string(line))
		if err != nil {
			db.close()
			return nil, 0, err
		}
		if u.UID >= minUid && u.UID <= maxUid {
			uid = u.UID
		}
	}

	uid++
	if uid == maxUid {
		return nil, 0, &IdRangeError{maxUid, isSystem, true}
	}
	return
}

func
nextUID
(
isSystem
bool
)
(
db
*
dbfile
,
uid
int
,
err
error
)
{
loadConfig
(
)
db
,
err
=
openDBFile
(
_USER_FILE
,
os
.
O_RDWR
)
if
err
!=
{
return
}
// Seek to file half size.
info
,
err
:=
db
.
file
.
Stat
(
)
if
err
!=
{
db
.
close
(
)
return
,
0
,
err
}
if
_
,
err
=
db
.
file
.
Seek
(
info
.
Size
(
)
/
2
,
os
.
SEEK_SET
)
;
err
!=
{
db
.
close
(
)
return
,
0
,
err
}
// To starting to read from a new line
if
_
,
_
,
err
=
db
.
rd
.
ReadLine
(
)
;
err
!=
{
db
.
close
(
)
return
,
0
,
err
}
var
minUid
,
maxUid
int
if
isSystem
{
minUid
,
maxUid
=
config
.
login
.
SYS_UID_MIN
,
config
.
login
.
SYS_UID_MAX
}
else
{
minUid
,
maxUid
=
config
.
login
.
UID_MIN
,
config
.
login
.
UID_MAX
}
for
{
line
,
_
,
err
:=
db
.
rd
.
ReadLine
(
)
if
err
==
io
.
EOF
{
break
}
u
,
err
:=
parseUser
(
string
(
line
)
)
if
err
!=
{
db
.
close
(
)
return
,
0
,
err
}
if
u
.
UID
>=
minUid
&&
u
.
UID
<=
maxUid
{
uid
=
u
.
UID
}
}
uid
++
if
uid
==
maxUid
{
return
,
0
,
&
IdRangeError
{
maxUid
,
isSystem
,
true
}
}
return
}

// nextUID returns the next free user id to use, according to whether it is a
// system's user.

nextUID returns the next free user id to use according to whether it is a system s user .


func nextGUID(isSystem bool) (db *dbfile, gid int, err error) {
	loadConfig()

	db, err = openDBFile(_GROUP_FILE, os.O_RDWR)
	if err != nil {
		return
	}

	// Seek to file half size.

	info, err := db.file.Stat()
	if err != nil {
		db.close()
		return nil, 0, err
	}
	if _, err = db.file.Seek(info.Size()/2, os.SEEK_SET); err != nil {
		db.close()
		return nil, 0, err
	}
	// To starting to read from a new line
	if _, _, err = db.rd.ReadLine(); err != nil {
		db.close()
		return nil, 0, err
	}

	var minGid, maxGid int
	if isSystem {
		minGid, maxGid = config.login.SYS_GID_MIN, config.login.SYS_GID_MAX
	} else {
		minGid, maxGid = config.login.GID_MIN, config.login.GID_MAX
	}

	for {
		line, _, err := db.rd.ReadLine()
		if err == io.EOF {
			break
		}

		gr, err := parseGroup(string(line))
		if err != nil {
			db.close()
			return nil, 0, err
		}
		if gr.GID >= minGid && gr.GID <= maxGid {
			gid = gr.GID
		}
	}

	gid++
	if gid == maxGid {
		return nil, 0, &IdRangeError{maxGid, isSystem, false}
	}
	return
}

func
nextGUID
(
isSystem
bool
)
(
db
*
dbfile
,
gid
int
,
err
error
)
{
loadConfig
(
)
db
,
err
=
openDBFile
(
_GROUP_FILE
,
os
.
O_RDWR
)
if
err
!=
{
return
}
// Seek to file half size.
info
,
err
:=
db
.
file
.
Stat
(
)
if
err
!=
{
db
.
close
(
)
return
,
0
,
err
}
if
_
,
err
=
db
.
file
.
Seek
(
info
.
Size
(
)
/
2
,
os
.
SEEK_SET
)
;
err
!=
{
db
.
close
(
)
return
,
0
,
err
}
// To starting to read from a new line
if
_
,
_
,
err
=
db
.
rd
.
ReadLine
(
)
;
err
!=
{
db
.
close
(
)
return
,
0
,
err
}
var
minGid
,
maxGid
int
if
isSystem
{
minGid
,
maxGid
=
config
.
login
.
SYS_GID_MIN
,
config
.
login
.
SYS_GID_MAX
}
else
{
minGid
,
maxGid
=
config
.
login
.
GID_MIN
,
config
.
login
.
GID_MAX
}
for
{
line
,
_
,
err
:=
db
.
rd
.
ReadLine
(
)
if
err
==
io
.
EOF
{
break
}
gr
,
err
:=
parseGroup
(
string
(
line
)
)
if
err
!=
{
db
.
close
(
)
return
,
0
,
err
}
if
gr
.
GID
>=
minGid
&&
gr
.
GID
<=
maxGid
{
gid
=
gr
.
GID
}
}
gid
++
if
gid
==
maxGid
{
return
,
0
,
&
IdRangeError
{
maxGid
,
isSystem
,
false
}
}
return
}

// nextGUID returns the next free group id to use, according to whether it is a
// system's group.

nextGUID returns the next free group id to use according to whether it is a system s group .


func NextSystemUID() (int, error) {
	db, uid, err := nextUID(true)
	db.close()
	return uid, err
}

func NextSystemUID ( ) ( int , error ) { db , uid , err := nextUID ( true ) db . close ( ) return uid , err }

// NextSystemUID returns the next free system user id to use.

NextSystemUID returns the next free system user id to use .


func NextSystemGID() (int, error) {
	db, gid, err := nextGUID(true)
	db.close()
	return gid, err
}

func NextSystemGID ( ) ( int , error ) { db , gid , err := nextGUID ( true ) db . close ( ) return gid , err }

// NextSystemGID returns the next free system group id to use.

NextSystemGID returns the next free system group id to use .


func NextUID() (int, error) {
	db, uid, err := nextUID(false)
	db.close()
	return uid, err
}

func NextUID ( ) ( int , error ) { db , uid , err := nextUID ( false ) db . close ( ) return uid , err }

// NextUID returns the next free user id to use.

NextUID returns the next free user id to use .


func NextGID() (int, error) {
	db, gid, err := nextGUID(false)
	db.close()
	return gid, err
}

func NextGID ( ) ( int , error ) { db , gid , err := nextGUID ( false ) db . close ( ) return gid , err }

// NextGID returns the next free group id to use.

NextGID returns the next free group id to use .


func NewShadow(username string) *Shadow {
	loadConfig()

	return &Shadow{
		Name:    username,
		changed: _ENABLE_AGING,
		Min:     config.login.PASS_MIN_DAYS,
		Max:     config.login.PASS_MAX_DAYS,
		Warn:    config.login.PASS_WARN_AGE,
	}
}

func NewShadow ( username string ) * Shadow { loadConfig ( ) return & Shadow { Name : username , changed : _ENABLE_AGING , Min : config . login . PASS_MIN_DAYS , Max : config . login . PASS_MAX_DAYS , Warn : config . login . PASS_WARN_AGE , } }

// NewShadow returns a structure Shadow with fields "Min", "Max" and "Warn"
// got from the system configuration, and enabling the features of password aging.

NewShadow returns a structure Shadow with fields Min Max and Warn got from the system configuration and enabling the features of password aging .


func parseShadow(row string) (*Shadow, error) {
	fields := strings.Split(row, ":")
	if len(fields) != 9 {
		return nil, rowError{_SHADOW_FILE, row}
	}

	var inactive, expire, flag int

	changed, err := parseChange(fields[2])
	if err != nil {
		return nil, atoiError{_SHADOW_FILE, row, "changed"}
	}
	min, err := strconv.Atoi(fields[3])
	if err != nil {
		return nil, atoiError{_SHADOW_FILE, row, "Min"}
	}
	max, err := strconv.Atoi(fields[4])
	if err != nil {
		return nil, atoiError{_SHADOW_FILE, row, "Max"}
	}
	warn, err := strconv.Atoi(fields[5])
	if err != nil {
		return nil, atoiError{_SHADOW_FILE, row, "Warn"}
	}

	// Optional fields

	if fields[6] != "" {
		if inactive, err = strconv.Atoi(fields[6]); err != nil {
			return nil, atoiError{_SHADOW_FILE, row, "Inactive"}
		}
	}
	if fields[7] != "" {
		if expire, err = strconv.Atoi(fields[7]); err != nil {
			return nil, atoiError{_SHADOW_FILE, row, "expire"}
		}
	}
	if fields[8] != "" {
		if flag, err = strconv.Atoi(fields[8]); err != nil {
			return nil, atoiError{_SHADOW_FILE, row, "flag"}
		}
	}

	return &Shadow{
		fields[0],
		fields[1],
		changed,
		min,
		max,
		warn,
		inactive,
		expire,
		flag,
	}, nil
}

func
parseShadow
(
row
string
)
(
*
Shadow
,
error
)
{
fields
:=
strings
.
Split
(
row
,
"
"
)
if
len
(
fields
)
!=
9
{
return
,
rowError
{
_SHADOW_FILE
,
row
}
}
var
inactive
,
expire
,
flag
int
changed
,
err
:=
parseChange
(
fields
[
2
]
)
if
err
!=
{
return
,
atoiError
{
_SHADOW_FILE
,
row
,
"
"
}
}
min
,
err
:=
strconv
.
Atoi
(
fields
[
3
]
)
if
err
!=
{
return
,
atoiError
{
_SHADOW_FILE
,
row
,
"
"
}
}
max
,
err
:=
strconv
.
Atoi
(
fields
[
4
]
)
if
err
!=
{
return
,
atoiError
{
_SHADOW_FILE
,
row
,
"
"
}
}
warn
,
err
:=
strconv
.
Atoi
(
fields
[
5
]
)
if
err
!=
{
return
,
atoiError
{
_SHADOW_FILE
,
row
,
"
"
}
}
// Optional fields
if
fields
[
6
]
!=
"
"
{
if
inactive
,
err
=
strconv
.
Atoi
(
fields
[
6
]
)
;
err
!=
{
return
,
atoiError
{
_SHADOW_FILE
,
row
,
"
"
}
}
}
if
fields
[
7
]
!=
"
"
{
if
expire
,
err
=
strconv
.
Atoi
(
fields
[
7
]
)
;
err
!=
{
return
,
atoiError
{
_SHADOW_FILE
,
row
,
"
"
}
}
}
if
fields
[
8
]
!=
"
"
{
if
flag
,
err
=
strconv
.
Atoi
(
fields
[
8
]
)
;
err
!=
{
return
,
atoiError
{
_SHADOW_FILE
,
row
,
"
"
}
}
}
return
&
Shadow
{
fields
[
0
]
,
fields
[
1
]
,
changed
,
min
,
max
,
warn
,
inactive
,
expire
,
flag
,
}
,
}

// parseShadow parses the row of a shadowed password.

parseShadow parses the row of a shadowed password .


func LookupShadow(name string) (*Shadow, error) {
	entries, err := LookupInShadow(S_NAME, name, 1)
	if err != nil {
		return nil, err
	}

	return entries[0], err
}

func LookupShadow ( name string ) ( * Shadow , error ) { entries , err := LookupInShadow ( S_NAME , name , 1 ) if err != nil { return nil , err } return entries [ 0 ] , err }

// LookupShadow looks for the entry for the given user name.

LookupShadow looks for the entry for the given user name .


func (s *Shadow) Add(key []byte) (err error) {
	loadConfig()

	shadow, err := LookupShadow(s.Name)
	if err != nil {
		if _, ok := err.(NoFoundError); !ok {
			return
		}
	}
	if shadow != nil {
		return ErrUserExist
	}

	if s.Name == "" {
		return RequiredError("Name")
	}
	if s.Max == 0 {
		return RequiredError("Max")
	}
	if s.Warn == 0 {
		return RequiredError("Warn")
	}

	// Backup
	if err = backup(_SHADOW_FILE); err != nil {
		return
	}

	db, err := openDBFile(_SHADOW_FILE, os.O_WRONLY|os.O_APPEND)
	if err != nil {
		return
	}
	defer func() {
		e := db.close()
		if e != nil && err == nil {
			err = e
		}
	}()

	if key != nil {
		s.password, _ = config.crypter.Generate(key, nil)
		if s.changed == _ENABLE_AGING {
			s.setChange()
		}
	} else {
		s.password = "*" // Password disabled.
	}

	_, err = db.file.WriteString(s.String())
	return
}

func
(
s
*
Shadow
)
Add
(
key
[
]
byte
)
(
err
error
)
{
loadConfig
(
)
shadow
,
err
:=
LookupShadow
(
s
.
Name
)
if
err
!=
{
if
_
,
ok
:=
err
.
(
NoFoundError
)
;
!
ok
{
return
}
}
if
shadow
!=
{
return
ErrUserExist
}
if
s
.
Name
==
"
"
{
return
RequiredError
(
"
"
)
}
if
s
.
Max
==
0
{
return
RequiredError
(
"
"
)
}
if
s
.
Warn
==
0
{
return
RequiredError
(
"
"
)
}
// Backup
if
err
=
backup
(
_SHADOW_FILE
)
;
err
!=
{
return
}
db
,
err
:=
openDBFile
(
_SHADOW_FILE
,
os
.
O_WRONLY
|
os
.
O_APPEND
)
if
err
!=
{
return
}
defer
func
(
)
{
e
:=
db
.
close
(
)
if
e
!=
&&
err
==
{
err
=
e
}
}
(
)
if
key
!=
{
s
.
password
,
_
=
config
.
crypter
.
Generate
(
key
,
)
if
s
.
changed
==
_ENABLE_AGING
{
s
.
setChange
(
)
}
}
else
{
s
.
password
=
"
"
// Password disabled.
}
_
,
err
=
db
.
file
.
WriteString
(
s
.
String
(
)
)
return
}

// == Editing
//
// Add adds a new shadowed user.
// If the key is not nil, generates a hashed password.
//
// It is created a backup before of modify the original file.

== Editing Add adds a new shadowed user . If the key is not nil generates a hashed password . It is created a backup before of modify the original file .


func Copy(source, dest string) (err error) {
	// Don't backup files of backup.
	if dest[len(dest)-1] != '~' {
		if err = Backup(dest); err != nil {
			return
		}
	}

	srcFile, err := os.Open(source)
	if err != nil {
		return err
	}
	defer func() {
		err2 := srcFile.Close()
		if err2 != nil && err == nil {
			err = err2
		}
	}()

	srcInfo, err := os.Stat(source)
	if err != nil {
		return err
	}

	dstFile, err := os.OpenFile(dest, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, srcInfo.Mode().Perm())
	if err != nil {
		return err
	}

	_, err = io.Copy(dstFile, srcFile)
	err2 := dstFile.Close()
	if err2 != nil && err == nil {
		err = err2
	}
	return
}

func
Copy
(
source
,
dest
string
)
(
err
error
)
{
// Don't backup files of backup.
if
dest
[
len
(
dest
)
1
]
!=
'~'
{
if
err
=
Backup
(
dest
)
;
err
!=
{
return
}
}
srcFile
,
err
:=
os
.
Open
(
source
)
if
err
!=
{
return
err
}
defer
func
(
)
{
err2
:=
srcFile
.
Close
(
)
if
err2
!=
&&
err
==
{
err
=
err2
}
}
(
)
srcInfo
,
err
:=
os
.
Stat
(
source
)
if
err
!=
{
return
err
}
dstFile
,
err
:=
os
.
OpenFile
(
dest
,
os
.
O_WRONLY
|
os
.
O_CREATE
|
os
.
O_TRUNC
,
srcInfo
.
Mode
(
)
.
Perm
(
)
)
if
err
!=
{
return
err
}
_
,
err
=
io
.
Copy
(
dstFile
,
srcFile
)
err2
:=
dstFile
.
Close
(
)
if
err2
!=
&&
err
==
{
err
=
err2
}
return
}

// Copy copies file in source to file in dest preserving the mode attributes.

Copy copies file in source to file in dest preserving the mode attributes .


func Create(filename string, b []byte) (err error) {
	file, err := os.Create(filename)
	if err != nil {
		return err
	}

	_, err = file.Write(b)
	err2 := file.Close()
	if err2 != nil && err == nil {
		err = err2
	}
	return
}

func Create ( filename string , b [ ] byte ) ( err error ) { file , err := os . Create ( filename ) if err != nil { return err } _ , err = file . Write ( b ) err2 := file . Close ( ) if err2 != nil && err == nil { err = err2 } return }

// Create creates a new file with b bytes.

Create creates a new file with b bytes .


func loadConfig() {
	config.Do(func() {
		//checkRoot()
		if err := config.init(); err != nil {
			panic(err)
		}
	})
}

func loadConfig ( ) { config . Do ( func ( ) { //checkRoot() if err := config . init ( ) ; err != nil { panic ( err ) } } ) }

// loadConfig loads user configuration.
// It has to be loaded before of edit some file.

loadConfig loads user configuration . It has to be loaded before of edit some file .


func NewEdit(filename string) (*edit, error) {
	if err := Backup(filename); err != nil {
		return nil, err
	}

	file, err := os.OpenFile(filename, os.O_RDWR, 0666)
	if err != nil {
		return nil, err
	}

	return &edit{
		_editDefault,
		file,
		bufio.NewReadWriter(bufio.NewReader(file), bufio.NewWriter(file)),
	}, nil
}

func NewEdit ( filename string ) ( * edit , error ) { if err := Backup ( filename ) ; err != nil { return nil , err } file , err := os . OpenFile ( filename , os . O_RDWR , 0666 ) if err != nil { return nil , err } return & edit { _editDefault , file , bufio . NewReadWriter ( bufio . NewReader ( file ) , bufio . NewWriter ( file ) ) , } , nil }

// NewEdit opens a file to edit; it is created a backup.

NewEdit opens a file to edit ; it is created a backup .


func (e *edit) AppendString(s string) error {
	return e.Append([]byte(s))
}

func ( e * edit ) AppendString ( s string ) error { return e . Append ( [ ] byte ( s ) ) }

// AppendString is like Append, but writes the contents of string s rather than
// an array of bytes.

AppendString is like Append but writes the contents of string s rather than an array of bytes .


func (e *edit) Comment(reLine []string) error {
	allReSearch := make([]*regexp.Regexp, len(reLine))

	for i, v := range reLine {
		if re, err := regexp.Compile(v); err != nil {
			return err
		} else {
			allReSearch[i] = re
		}
	}

	if _, err := e.file.Seek(0, os.SEEK_SET); err != nil {
		return err
	}

	char := []byte(e.CommentChar + " ")
	isNew := false
	buf := new(bytes.Buffer)

	// Check every line.
	for {
		line, err := e.buf.ReadBytes('\n')
		if err == io.EOF {
			break
		}

		for _, v := range allReSearch {
			if v.Match(line) {
				line = append(char, line...)

				if !isNew {
					isNew = true
				}
				break
			}
		}

		if _, err = buf.Write(line); err != nil {
			return err
		}
	}

	if isNew {
		return e.rewrite(buf.Bytes())
	}
	return nil
}

func
(
e
*
edit
)
Comment
(
reLine
[
]
string
)
error
{
allReSearch
:=
make
(
[
]
*
regexp
.
Regexp
,
len
(
reLine
)
)
for
i
,
v
:=
range
reLine
{
if
re
,
err
:=
regexp
.
Compile
(
v
)
;
err
!=
{
return
err
}
else
{
allReSearch
[
i
]
=
re
}
}
if
_
,
err
:=
e
.
file
.
Seek
(
0
,
os
.
SEEK_SET
)
;
err
!=
{
return
err
}
char
:=
[
]
byte
(
e
.
CommentChar
+
"
"
)
isNew
:=
false
buf
:=
new
(
bytes
.
Buffer
)
// Check every line.
for
{
line
,
err
:=
e
.
buf
.
ReadBytes
(
'\n'
)
if
err
==
io
.
EOF
{
break
}
for
_
,
v
:=
range
allReSearch
{
if
v
.
Match
(
line
)
{
line
=
append
(
char
,
line
...
)
if
!
isNew
{
isNew
=
true
}
break
}
}
if
_
,
err
=
buf
.
Write
(
line
)
;
err
!=
{
return
err
}
}
if
isNew
{
return
e
.
rewrite
(
buf
.
Bytes
(
)
)
}
return
}

// Comment inserts the comment character in lines that mach any regular expression
// in reLine.

Comment inserts the comment character in lines that mach any regular expression in reLine .


func (e *edit) CommentOut(reLine []string) error {
	allSearch := make([]ReplacerAtLine, len(reLine))

	for i, v := range reLine {
		allSearch[i] = ReplacerAtLine{
			v,
			"[[:space:]]*" + e.CommentChar + "[[:space:]]*",
			"",
		}
	}

	return e.ReplaceAtLineN(allSearch, 1)
}

func ( e * edit ) CommentOut ( reLine [ ] string ) error { allSearch := make ( [ ] ReplacerAtLine , len ( reLine ) ) for i , v := range reLine { allSearch [ i ] = ReplacerAtLine { v , " " + e . CommentChar + " " , " " , } } return e . ReplaceAtLineN ( allSearch , 1 ) }

// CommentOut removes the comment character of lines that mach any regular expression
// in reLine.

CommentOut removes the comment character of lines that mach any regular expression in reLine .


func CommentM(filename string, reLine []string) error {
	e, err := NewEdit(filename)
	if err != nil {
		return err
	}

	err = e.Comment(reLine)
	err2 := e.Close()
	if err != nil {
		return err
	}
	return err2
}

func CommentM ( filename string , reLine [ ] string ) error { e , err := NewEdit ( filename ) if err != nil { return err } err = e . Comment ( reLine ) err2 := e . Close ( ) if err != nil { return err } return err2 }

// CommentM inserts the comment character in lines that mach any regular expression
// in reLine, in the named file.

CommentM inserts the comment character in lines that mach any regular expression in reLine in the named file .


func CommentOutM(filename string, reLine []string) error {
	e, err := NewEdit(filename)
	if err != nil {
		return err
	}

	err = e.CommentOut(reLine)
	err2 := e.Close()
	if err != nil {
		return err
	}
	return err2
}

func CommentOutM ( filename string , reLine [ ] string ) error { e , err := NewEdit ( filename ) if err != nil { return err } err = e . CommentOut ( reLine ) err2 := e . Close ( ) if err != nil { return err } return err2 }

// CommentOutM removes the comment character of lines that mach any regular expression
// in reLine, in the named file.

CommentOutM removes the comment character of lines that mach any regular expression in reLine in the named file .


func ReplaceN(filename string, r []Replacer, n int) error {
	e, err := NewEdit(filename)
	if err != nil {
		return err
	}

	err = e.genReplace(r, n)
	err2 := e.Close()
	if err != nil {
		return err
	}
	return err2
}

func ReplaceN ( filename string , r [ ] Replacer , n int ) error { e , err := NewEdit ( filename ) if err != nil { return err } err = e . genReplace ( r , n ) err2 := e . Close ( ) if err != nil { return err } return err2 }

// ReplaceN replaces a number of regular expressions mathed in r for the named
// file.

ReplaceN replaces a number of regular expressions mathed in r for the named file .


func ReplaceAtLine(filename string, r []ReplacerAtLine) error {
	e, err := NewEdit(filename)
	if err != nil {
		return err
	}

	err = e.genReplaceAtLine(r, -1)
	err2 := e.Close()
	if err != nil {
		return err
	}
	return err2
}

func ReplaceAtLine ( filename string , r [ ] ReplacerAtLine ) error { e , err := NewEdit ( filename ) if err != nil { return err } err = e . genReplaceAtLine ( r , - 1 ) err2 := e . Close ( ) if err != nil { return err } return err2 }

// ReplaceAtLine replaces all regular expressions mathed in r for the named
// file, if the line is matched at the first.

ReplaceAtLine replaces all regular expressions mathed in r for the named file if the line is matched at the first .


func ReplaceAtLineN(filename string, r []ReplacerAtLine, n int) error {
	e, err := NewEdit(filename)
	if err != nil {
		return err
	}

	err = e.genReplaceAtLine(r, n)
	err2 := e.Close()
	if err != nil {
		return err
	}
	return err2
}

func ReplaceAtLineN ( filename string , r [ ] ReplacerAtLine , n int ) error { e , err := NewEdit ( filename ) if err != nil { return err } err = e . genReplaceAtLine ( r , n ) err2 := e . Close ( ) if err != nil { return err } return err2 }

// ReplaceAtLineN replaces a number of regular expressions mathed in r for the
// named file, if the line is matched at the first.

ReplaceAtLineN replaces a number of regular expressions mathed in r for the named file if the line is matched at the first .


func init() {
	var err error

	_SETTINGS, err = getSettingsForUid(os.Getuid())
	if err != nil {
		panic(err)
	}
}

func init ( ) { var err error _SETTINGS , err = getSettingsForUid ( os . Getuid ( ) ) if err != nil { panic ( err ) } }

// Sets the settings files of the caller.


func getSettingsForUid(id int) (settings, error) {
	u, err := user.LookupUID(id)
	if err != nil {
		return settings{}, err
	}
	if u.Shell == "" {
		return settings{}, NoShellError(u.Name)
	}
	if u.Dir == "" {
		return settings{}, NoHomeError(u.Name)
	}

	shell := path.Base(u.Shell)

	_settings := settings{
		global: systemFile[_SHELL_SH],
		user:   path.Join(u.Dir, userFile[_SHELL_SH]),
		kde:    path.Join(u.Dir, kdeFile),
	}
	info, err := os.Stat(_settings.kde)
	if err == nil && info.IsDir() {
		_settings.useKDE = true
	}

	for _, v := range shFamily {
		if v == shell {
			return _settings, nil
		}
	}
	/*for _, v := range cshFamily {
		if v == shell {
			return _settings, nil
		}
	}*/

	return settings{}, fmt.Errorf("shell unsopported: %s", shell)
}

// getSettingsForUid returns the settings files of the given user id.

getSettingsForUid returns the settings files of the given user id .


func _Set(filename, key, value string) error {
	// Check if the key is already used.
	conf, err := shconf.ParseFile(filename)
	if err != nil {
		if err != os.ErrNotExist {
			return err
		}
		println("ErrNotExist") //TODO: remove
	} else {
		if _, err = conf.Get(key); err != shconf.ErrKey {
			panic("OPS")
		}
	}

	return file.AppendString(filename, key+string(conf.Separator())+value)
}

func
_Set
(
filename
,
key
,
value
string
)
error
{
// Check if the key is already used.
conf
,
err
:=
shconf
.
ParseFile
(
filename
)
if
err
!=
{
if
err
!=
os
.
ErrNotExist
{
return
err
}
println
(
"
"
)
//TODO: remove
}
else
{
if
_
,
err
=
conf
.
Get
(
key
)
;
err
!=
shconf
.
ErrKey
{
panic
(
"
"
)
}
}
return
file
.
AppendString
(
filename
,
key
+
string
(
conf
.
Separator
(
)
)
+
value
)
}

// == Set variables
//
// _Set sets the value named by the key in the given filename.

== Set variables _Set sets the value named by the key in the given filename .


func _MSet(filename string, keys, values []string) error {
	if len(keys) != len(values) {
		return fmt.Errorf("number of keys is different to number of values")
	}

	// Check if the key is already used.
	conf, err := shconf.ParseFile(filename)
	if err != nil {
		if err != os.ErrNotExist {
			return err
		}
		println("ErrNotExist") //TODO: remove
	}

	var buf bytes.Buffer
	for i, key := range keys {
		if _, err = conf.Get(key); err != nil {
			continue // TODO: log key already set.
		}

		buf.WriteString(key)
		buf.Write(conf.Separator())
		buf.WriteString(values[i])
		buf.WriteByte('\n')
	}

	return file.Append(filename, buf.Bytes())
}

func
_MSet
(
filename
string
,
keys
,
values
[
]
string
)
error
{
if
len
(
keys
)
!=
len
(
values
)
{
return
fmt
.
Errorf
(
"
"
)
}
// Check if the key is already used.
conf
,
err
:=
shconf
.
ParseFile
(
filename
)
if
err
!=
{
if
err
!=
os
.
ErrNotExist
{
return
err
}
println
(
"
"
)
//TODO: remove
}
var
buf
bytes
.
Buffer
for
i
,
key
:=
range
keys
{
if
_
,
err
=
conf
.
Get
(
key
)
;
err
!=
{
continue
// TODO: log key already set.
}
buf
.
WriteString
(
key
)
buf
.
Write
(
conf
.
Separator
(
)
)
buf
.
WriteString
(
values
[
i
]
)
buf
.
WriteByte
(
'\n'
)
}
return
file
.
Append
(
filename
,
buf
.
Bytes
(
)
)
}

// _MSet sets multiple values named by the keys in the given filename.

_MSet sets multiple values named by the keys in the given filename .


func Set(key, value string) error {
	err := _Set(_SETTINGS.user, key, value)
	if err != nil {
		return err
	}

	if _SETTINGS.useKDE {
		return _Set(_SETTINGS.kde, key, value)
	}
	return nil
}

func Set ( key , value string ) error { err := _Set ( _SETTINGS . user , key , value ) if err != nil { return err } if _SETTINGS . useKDE { return _Set ( _SETTINGS . kde , key , value ) } return nil }

// == Set session-wide variables
// Set sets the value of the environment variable named by the key that
// affects the current user.
// It returns an error, if any.

== Set session - wide variables Set sets the value of the environment variable named by the key that affects the current user . It returns an error if any .


func MSet(keys, values []string) error {
	err := _MSet(_SETTINGS.user, keys, values)
	if err != nil {
		return err
	}

	if _SETTINGS.useKDE {
		return _MSet(_SETTINGS.kde, keys, values)
	}
	return nil
}

func MSet ( keys , values [ ] string ) error { err := _MSet ( _SETTINGS . user , keys , values ) if err != nil { return err } if _SETTINGS . useKDE { return _MSet ( _SETTINGS . kde , keys , values ) } return nil }

// MSet sets multiple values of the environment variables named by the keys
// that affects the current user.
// It returns an error, if any.

MSet sets multiple values of the environment variables named by the keys that affects the current user . It returns an error if any .


func SetForUid(id int, key, value string) error {
	_settings, err := getSettingsForUid(id)
	if err != nil {
		return err
	}

	if err = _Set(_settings.user, key, value); err != nil {
		return err
	}
	if _settings.useKDE {
		return _Set(_settings.kde, key, value)
	}
	return nil
}

func SetForUid ( id int , key , value string ) error { _settings , err := getSettingsForUid ( id ) if err != nil { return err } if err = _Set ( _settings . user , key , value ) ; err != nil { return err } if _settings . useKDE { return _Set ( _settings . kde , key , value ) } return nil }

// SetForUid sets the value of the environment variable named by the key that
// affects a particular user.
// It returns an error, if any.

SetForUid sets the value of the environment variable named by the key that affects a particular user . It returns an error if any .


func MSetForUid(id int, keys, values []string) error {
	_settings, err := getSettingsForUid(id)
	if err != nil {
		return err
	}

	if err = _MSet(_settings.user, keys, values); err != nil {
		return err
	}
	if _settings.useKDE {
		return _MSet(_settings.kde, keys, values)
	}
	return nil
}

func MSetForUid ( id int , keys , values [ ] string ) error { _settings , err := getSettingsForUid ( id ) if err != nil { return err } if err = _MSet ( _settings . user , keys , values ) ; err != nil { return err } if _settings . useKDE { return _MSet ( _settings . kde , keys , values ) } return nil }

// MSetForUid sets multiple values of the environment variables named by the
// keys that affects a particular user.
// It returns an error, if any.

MSetForUid sets multiple values of the environment variables named by the keys that affects a particular user . It returns an error if any .


func Setsys(key, value string) error {
	if !_IS_ROOT {
		return ErrNoRoot
	}
	return _Set(_SETTINGS.global, key, value)
}

func Setsys ( key , value string ) error { if ! _IS_ROOT { return ErrNoRoot } return _Set ( _SETTINGS . global , key , value ) }

// == Set system-wide variables
// Setsys sets the value of the environment variable named by the key that
// affects the system as a whole. You must be Root.
// It returns an error, if any.

== Set system - wide variables Setsys sets the value of the environment variable named by the key that affects the system as a whole . You must be Root . It returns an error if any .


func MSetsys(keys, values []string) error {
	if !_IS_ROOT {
		return ErrNoRoot
	}
	return _MSet(_SETTINGS.global, keys, values)
}

func MSetsys ( keys , values [ ] string ) error { if ! _IS_ROOT { return ErrNoRoot } return _MSet ( _SETTINGS . global , keys , values ) }

// MSetsys sets multiple values of the environment variables named by the keys
// that affects the system as a whole. You must be Root.
// It returns an error, if any.

MSetsys sets multiple values of the environment variables named by the keys that affects the system as a whole . You must be Root . It returns an error if any .


func SetsysForUid(id int, key, value string) error {
	if !_IS_ROOT {
		return ErrNoRoot
	}

	_settings, err := getSettingsForUid(id)
	if err != nil {
		return err
	}
	return _Set(_settings.global, key, value)
}

func SetsysForUid ( id int , key , value string ) error { if ! _IS_ROOT { return ErrNoRoot } _settings , err := getSettingsForUid ( id ) if err != nil { return err } return _Set ( _settings . global , key , value ) }

// SetsysForUid sets the value of the environment variable named by the key that
// affects the system as a whole. You must be Root.
// It returns an error, if any.

SetsysForUid sets the value of the environment variable named by the key that affects the system as a whole . You must be Root . It returns an error if any .


func MSetsysForUid(id int, keys, values []string) error {
	if !_IS_ROOT {
		return ErrNoRoot
	}

	_settings, err := getSettingsForUid(id)
	if err != nil {
		return err
	}
	return _MSet(_settings.global, keys, values)
}

func MSetsysForUid ( id int , keys , values [ ] string ) error { if ! _IS_ROOT { return ErrNoRoot } _settings , err := getSettingsForUid ( id ) if err != nil { return err } return _MSet ( _settings . global , keys , values ) }

// MSetsysForUid sets multiple values of the environment variables named by the
// keys that affects the system as a whole. You must be Root.
// It returns an error, if any.

MSetsysForUid sets multiple values of the environment variables named by the keys that affects the system as a whole . You must be Root . It returns an error if any .


func NewGShadow(username string, members ...string) *GShadow {
	return &GShadow{
		Name:     username,
		UserList: members,
	}
}

func NewGShadow ( username string , members ... string ) * GShadow { return & GShadow { Name : username , UserList : members , } }


func parseGShadow(row string) (*GShadow, error) {
	fields := strings.Split(row, ":")
	if len(fields) != 4 {
		return nil, rowError{_GSHADOW_FILE, row}
	}

	return &GShadow{
		fields[0],
		fields[1],
		strings.Split(fields[2], ","),
		strings.Split(fields[3], ","),
	}, nil
}

func parseGShadow ( row string ) ( * GShadow , error ) { fields := strings . Split ( row , " " ) if len ( fields ) != 4 { return nil , rowError { _GSHADOW_FILE , row } } return & GShadow { fields [ 0 ] , fields [ 1 ] , strings . Split ( fields [ 2 ] , " " ) , strings . Split ( fields [ 3 ] , " " ) , } , nil }

// parseGShadow parses the row of a group shadow.

parseGShadow parses the row of a group shadow .


func (*GShadow) lookUp(line string, f field, value interface{}) interface{} {
	_field := f.(gshadowField)
	_value := value.(string)
	allField := strings.Split(line, ":")
	arrayField := make(map[int][]string)

	arrayField[2] = strings.Split(allField[2], ",")
	arrayField[3] = strings.Split(allField[3], ",")

	// Check fields
	var isField bool
	if GS_NAME&_field != 0 && allField[0] == _value {
		isField = true
	} else if GS_PASSWD&_field != 0 && allField[1] == _value {
		isField = true
	} else if GS_ADMIN&_field != 0 && checkGroup(arrayField[2], _value) {
		isField = true
	} else if GS_MEMBER&_field != 0 && checkGroup(arrayField[3], _value) {
		isField = true
	} else if GS_ALL&_field != 0 {
		isField = true
	}

	if isField {
		return &GShadow{
			allField[0],
			allField[1],
			arrayField[2],
			arrayField[3],
		}
	}
	return nil
}

func
(
*
GShadow
)
lookUp
(
line
string
,
f
field
,
value
interface
{
}
)
interface
{
}
{
_field
:=
f
.
(
gshadowField
)
_value
:=
value
.
(
string
)
allField
:=
strings
.
Split
(
line
,
"
"
)
arrayField
:=
make
(
map
[
int
]
[
]
string
)
arrayField
[
2
]
=
strings
.
Split
(
allField
[
2
]
,
"
"
)
arrayField
[
3
]
=
strings
.
Split
(
allField
[
3
]
,
"
"
)
// Check fields
var
isField
bool
if
GS_NAME
&
_field
!=
0
&&
allField
[
0
]
==
_value
{
isField
=
true
}
else
if
GS_PASSWD
&
_field
!=
0
&&
allField
[
1
]
==
_value
{
isField
=
true
}
else
if
GS_ADMIN
&
_field
!=
0
&&
checkGroup
(
arrayField
[
2
]
,
_value
)
{
isField
=
true
}
else
if
GS_MEMBER
&
_field
!=
0
&&
checkGroup
(
arrayField
[
3
]
,
_value
)
{
isField
=
true
}
else
if
GS_ALL
&
_field
!=
0
{
isField
=
true
}
if
isField
{
return
&
GShadow
{
allField
[
0
]
,
allField
[
1
]
,
arrayField
[
2
]
,
arrayField
[
3
]
,
}
}
return
}

// == Lookup
//
// lookUp parses the shadowed group line searching a value into the field.
// Returns nil if it isn't found.

== Lookup lookUp parses the shadowed group line searching a value into the field . Returns nil if it isn t found .


func LookupGShadow(name string) (*GShadow, error) {
	entries, err := LookupInGShadow(GS_NAME, name, 1)
	if err != nil {
		return nil, err
	}

	return entries[0], err
}

func LookupGShadow ( name string ) ( * GShadow , error ) { entries , err := LookupInGShadow ( GS_NAME , name , 1 ) if err != nil { return nil , err } return entries [ 0 ] , err }

// LookupGShadow looks up a shadowed group by name.

LookupGShadow looks up a shadowed group by name .


func (gs *GShadow) Add(key []byte) (err error) {
	loadConfig()

	gshadow, err := LookupGShadow(gs.Name)
	if err != nil {
		if _, ok := err.(NoFoundError); !ok {
			return
		}
	}
	if gshadow != nil {
		return ErrGroupExist
	}

	if gs.Name == "" {
		return RequiredError("Name")
	}

	// Backup
	if err = backup(_GSHADOW_FILE); err != nil {
		return
	}

	db, err := openDBFile(_GSHADOW_FILE, os.O_WRONLY|os.O_APPEND)
	if err != nil {
		return
	}
	defer func() {
		e := db.close()
		if e != nil && err == nil {
			err = e
		}
	}()

	if key != nil {
		gs.password, _ = config.crypter.Generate(key, nil)
	} else {
		gs.password = "*" // Password disabled.
	}

	_, err = db.file.WriteString(gs.String())
	return
}

func
(
gs
*
GShadow
)
Add
(
key
[
]
byte
)
(
err
error
)
{
loadConfig
(
)
gshadow
,
err
:=
LookupGShadow
(
gs
.
Name
)
if
err
!=
{
if
_
,
ok
:=
err
.
(
NoFoundError
)
;
!
ok
{
return
}
}
if
gshadow
!=
{
return
ErrGroupExist
}
if
gs
.
Name
==
"
"
{
return
RequiredError
(
"
"
)
}
// Backup
if
err
=
backup
(
_GSHADOW_FILE
)
;
err
!=
{
return
}
db
,
err
:=
openDBFile
(
_GSHADOW_FILE
,
os
.
O_WRONLY
|
os
.
O_APPEND
)
if
err
!=
{
return
}
defer
func
(
)
{
e
:=
db
.
close
(
)
if
e
!=
&&
err
==
{
err
=
e
}
}
(
)
if
key
!=
{
gs
.
password
,
_
=
config
.
crypter
.
Generate
(
key
,
)
}
else
{
gs
.
password
=
"
"
// Password disabled.
}
_
,
err
=
db
.
file
.
WriteString
(
gs
.
String
(
)
)
return
}

// == Editing
//
// Add adds a new shadowed group.
// If the key is not nil, generates a hashed password.
//
// It is created a backup before of modify the original file.

== Editing Add adds a new shadowed group . If the key is not nil generates a hashed password . It is created a backup before of modify the original file .


func NewUser(name string, gid int) *User {
	loadConfig()

	return &User{
		Name:  name,
		Dir:   path.Join(config.useradd.HOME, name),
		Shell: config.useradd.SHELL,
		UID:   -1,
		GID:   gid,
	}
}

func NewUser ( name string , gid int ) * User { loadConfig ( ) return & User { Name : name , Dir : path . Join ( config . useradd . HOME , name ) , Shell : config . useradd . SHELL , UID : - 1 , GID : gid , } }

// NewUser returns a new User with both fields "Dir" and "Shell" got from
// the system configuration.

NewUser returns a new User with both fields Dir and Shell got from the system configuration .


func NewSystemUser(name, homeDir string, gid int) *User {
	return &User{
		Name:  name,
		Dir:   homeDir,
		Shell: "/bin/false",
		UID:   -1,
		GID:   gid,

		addSystemUser: true,
	}
}

func NewSystemUser ( name , homeDir string , gid int ) * User { return & User { Name : name , Dir : homeDir , Shell : " " , UID : - 1 , GID : gid , addSystemUser : true , } }

// NewSystemUser returns a new system user.

NewSystemUser returns a new system user .


func (u *User) IsOfSystem() bool {
	//loadConfig()

	if u.UID > config.login.SYS_UID_MIN && u.UID < config.login.SYS_UID_MAX {
		return true
	}
	return false
}

func ( u * User ) IsOfSystem ( ) bool { //loadConfig() if u . UID > config . login . SYS_UID_MIN && u . UID < config . login . SYS_UID_MAX { return true } return false }

// IsOfSystem indicates whether it is a system user.

IsOfSystem indicates whether it is a system user .