<commit_before>// dsrt.go -- directoy sort in reverse date order.  IE, newest is first.

package main

import (
	"bufio"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path/filepath"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"syscall"
)

const lastCompiled = "20 May 17"

/*
Revision History
----------------
20 Apr 17 -- Started writing dsize rtn, based on dirlist.go
21 Apr 17 -- Now tweaking the output format.  And used flag package.  One as a pointer and one as a value, just to learn them.
22 Apr 17 -- Coded the use of the first non flag commandline param,  which is all I need.  Note that the flag must appear before the non-flag param, else the flag is ignored.
22 Apr 17 -- Now writing dsrt, to function similarly to dsort.
24 Apr 17 -- Now adding file matching, like "dir" or "ls" does.
25 Apr 17 -- Now adding sort by size as an option, like -s, and commas
26 Apr 17 -- Noticed that the match routine is case sensitive.  I don't like that.
27 Apr 17 -- commandline now allows a file spec.  I intend this for Windows.  I'll see how it goes.
19 May 19 -- Will now show the uid:gid for linux.
*/

// FIS is a FileInfo slice, as in os.FileInfo
type FISlice []os.FileInfo
type FISliceDate []os.FileInfo
type FISliceSize []os.FileInfo

func (f FISliceDate) Less(i, j int) bool {
	return f[i].ModTime().UnixNano() > f[j].ModTime().UnixNano() // I want a reverse sort, newest first
}

func (f FISliceDate) Swap(i, j int) {
	f[i], f[j] = f[j], f[i]
}

func (f FISliceDate) Len() int {
	return len(f)
}

func (f FISliceSize) Less(i, j int) bool {
	return f[i].Size() > f[j].Size() // I want a reverse sort, largest first
}

func (f FISliceSize) Swap(i, j int) {
	f[i], f[j] = f[j], f[i]
}

func (f FISliceSize) Len() int {
	return len(f)
}

func main() {
	const numlines = 50
	var userptr *user.User
	var files FISlice
	var filesDate FISliceDate
	var filesSize FISliceSize
	var err error
	var count int

	var revflag = flag.Bool("r", false, "reverse the sort, ie, oldest or smallest is first") // Ptr

	var RevFlag bool
	flag.BoolVar(&RevFlag, "R", false, "Reverse the sort, ie, oldest or smallest is first") // Value

	var nlines = flag.Int("n", numlines, "number of lines to display") // Ptr

	var NLines int
	flag.IntVar(&NLines, "N", numlines, "number of lines to display") // Value

	var helpflag = flag.Bool("h", false, "print help message") // pointer
	var HelpFlag bool
	flag.BoolVar(&HelpFlag, "H", false, "print help message")

	var sizeflag = flag.Bool("s", false, "sort by size instead of by date") // pointer
	var SizeFlag bool
	flag.BoolVar(&SizeFlag, "S", false, "sort by size instead of by date")
	flag.Parse()

	fmt.Println(" dsrt will display a directory by date or size.  Written in Go.  LastCompiled ", lastCompiled)
	fmt.Println()

	if *helpflag || HelpFlag {
		flag.PrintDefaults()
	}

	Reverse := *revflag || RevFlag
	SizeSort := *sizeflag || SizeFlag

	NumLines := numlines
	if *nlines != numlines {
		NumLines = *nlines
	} else if NLines != numlines {
		NumLines = NLines
	}
	askforinput := true

	CleanDirName := "." + string(filepath.Separator)
	CleanFileName := ""
	commandline := flag.Arg(0) // this only gets the first non flag argument.  That's all I want
	if len(commandline) > 0 {
		//		CleanDirName = filepath.Clean(commandline)
		CleanDirName, CleanFileName = filepath.Split(commandline)
		CleanFileName = strings.ToUpper(CleanFileName)
		askforinput = false
	}

	if askforinput {
		// Asking for input so don't have to worry about command line globbing
		fmt.Print(" Enter input for globbing: ")
		scanner := bufio.NewScanner(os.Stdin)
		scanner.Scan()
		newtext := scanner.Text()
		if err = scanner.Err(); err != nil {
			fmt.Fprintln(os.Stderr, " reading std input: ", err)
			os.Exit(1)
		}
		if len(newtext) > 0 {
			// time to do the stuff I'm writing this pgm for
			CleanDirName, CleanFileName = filepath.Split(newtext)
			CleanFileName = strings.ToUpper(CleanFileName)
		}

	}

	if len(CleanDirName) == 0 {
		CleanDirName = "." + string(filepath.Separator)
	}

	if len(CleanFileName) == 0 {
		CleanFileName = "*"
	}

	if SizeSort {
		filesSize, err = ioutil.ReadDir(CleanDirName)
		if err != nil {
			log.Fatal(err)
		}
		if Reverse {
			sort.Sort(sort.Reverse(filesSize))
		} else {
			sort.Sort(filesSize)
		}
		files = FISlice(filesSize)
	} else {
		filesDate, err = ioutil.ReadDir(CleanDirName)
		if err != nil {
			log.Fatal(err)
		}
		if Reverse {
			sort.Sort(sort.Reverse(filesDate))
		} else {
			sort.Sort(filesDate)
		}
		files = FISlice(filesDate)
	}

	fmt.Println(" Dirname is", CleanDirName)

	uid := os.Getuid() // int
	gid := os.Getgid() // int
	systemStr := ""
	linuxflag := runtime.GOOS == "linux"
	if linuxflag {
		systemStr = "Linux"
	} else if runtime.GOOS == "windows" {
		systemStr = "Windows"
	} else {
		systemStr = "Mac, maybe"
	}

	userptr, err = user.Current()
	if err != nil {
		fmt.Println(" user.Current error is ", err, "Exiting.")
		os.Exit(1)
	}

	fmt.Printf("uid = %d, gid = %d, on a computer running %s for %s:%s Username %s, Name %s, HomeDir %s \n",
		uid, gid, systemStr, userptr.Uid, userptr.Gid, userptr.Username, userptr.Name, userptr.HomeDir)
	for _, f := range files {
		NAME := strings.ToUpper(f.Name())
		if BOOL, _ := filepath.Match(CleanFileName, NAME); BOOL {
			sysUID := int(f.Sys().(*syscall.Stat_t).Uid) // Stat_t is a uint32
			uidStr := strconv.Itoa(sysUID)
			sysGID := int(f.Sys().(*syscall.Stat_t).Gid) // Stat_t is a uint32
			gidStr := strconv.Itoa(sysGID)

			usernameStr := GetIDname(uidStr)
			groupnameStr := GetIDname(gidStr)

			s := f.ModTime().Format("Jan-02-2006 15:04:05")
			sizeint := int(f.Size())
			sizestr := strconv.Itoa(sizeint)
			if sizeint > 100000 {
				sizestr = AddCommas(sizestr)
			}
			//	old way:		fmt.Printf("%10v %11d %s %s\n", f.Mode(), f.Size(), s, f.Name())
			fmt.Printf("%10v %s:%s %15s %s %s\n", f.Mode(), usernameStr, groupnameStr, sizestr, s, f.Name())
			count++
			if count > NumLines {
				break
			}
		}
	}

} // end main dsrt

//-------------------------------------------------------------------- InsertByteSlice
func InsertIntoByteSlice(slice, insertion []byte, index int) []byte {
	return append(slice[:index], append(insertion, slice[index:]...)...)
}

//---------------------------------------------------------------------- AddCommas
func AddCommas(instr string) string {
	var Comma []byte = []byte{','}

	BS := make([]byte, 0, 15)
	BS = append(BS, instr...)

	i := len(BS)

	for NumberOfCommas := i / 3; (NumberOfCommas > 0) && (i > 3); NumberOfCommas-- {
		i -= 3
		BS = InsertIntoByteSlice(BS, Comma, i)
	}
	return string(BS)
} // AddCommas
//---------------------------------------------------------------------------------------------------

// ---------------------------- GetIDname -----------------------------------------------------------
func GetIDname(uidStr string) string {

	ptrToUser, err := user.LookupId(uidStr)
	if err != nil {
		panic("uid not found")
	}

	idname := ptrToUser.Username
	return idname

} // GetIDname

/*
package path
func Match

func Match(pattern, name string) (matched bool, err error)

Match reports whether name matches the shell file name pattern.  The pattern syntax is:

pattern:
	{ term }
term:
	'*'         matches any sequence of non-/ characters
	'?'         matches any single non-/ character
	'[' [ '^' ] { character-range } ']'
	            character class (must be non-empty)
	c           matches character c (c != '*', '?', '\\', '[')
	'\\' c      matches character c

character-range:
	c           matches character c (c != '\\', '-', ']')
	'\\' c      matches character c
	lo '-' hi   matches character c for lo <= c <= hi

Match requires pattern to match all of name, not just a substring.  The only possible returned error is ErrBadPattern, when pattern is malformed.

package os
type FileInfo

type FileInfo interface {
        Name() string       // base name of the file
        Size() int64        // length in bytes for regular files; system-dependent for others
        Mode() FileMode     // file mode bits
        ModTime() time.Time // modification time
        IsDir() bool        // abbreviation for Mode().IsDir()
        Sys() interface{}   // underlying data source (can return nil)
}

A FileInfo describes a file and is returned by Stat and Lstat.

func Lstat

func Lstat(name string) (FileInfo, error)

Lstat returns a FileInfo describing the named file.  If the file is a symbolic link, the returned FileInfo describes the symbolic link.  Lstat makes no attempt to follow the link.
If there is an error, it will be of type *PathError.

func Stat

func Stat(name string) (FileInfo, error)

Stat returns a FileInfo describing the named file.  If there is an error, it will be of type *PathError.

The insight I had with my append troubles that the 1 slice entries were empty, is that when I used append, it would do just that to the end of the slice, and ignore the empty slices.
I needed to make the slice as empty for this to work.  So I am directly assigning the DirEntries slice, and appending the FileNames slice, to make sure that these both are doing what I want.
This code is now doing exactly what I want.  I guess there is no substitute for playing with myself.  Wait, that didn't come out right.  Or did it.

*/
<commit_msg>modified:   dsrt/dsrt.go -- it works<commit_after>// dsrt.go -- directoy sort in reverse date order.  IE, newest is first.

package main

import (
	"bufio"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path/filepath"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"syscall"
)

const lastCompiled = "20 May 17"

/*
Revision History
----------------
20 Apr 17 -- Started writing dsize rtn, based on dirlist.go
21 Apr 17 -- Now tweaking the output format.  And used flag package.  One as a pointer and one as a value, just to learn them.
22 Apr 17 -- Coded the use of the first non flag commandline param,  which is all I need.  Note that the flag must appear before the non-flag param, else the flag is ignored.
22 Apr 17 -- Now writing dsrt, to function similarly to dsort.
24 Apr 17 -- Now adding file matching, like "dir" or "ls" does.
25 Apr 17 -- Now adding sort by size as an option, like -s, and commas
26 Apr 17 -- Noticed that the match routine is case sensitive.  I don't like that.
27 Apr 17 -- commandline now allows a file spec.  I intend this for Windows.  I'll see how it goes.
19 May 19 -- Will now show the uid:gid for linux.
*/

// FIS is a FileInfo slice, as in os.FileInfo
type FISlice []os.FileInfo
type FISliceDate []os.FileInfo
type FISliceSize []os.FileInfo

func (f FISliceDate) Less(i, j int) bool {
	return f[i].ModTime().UnixNano() > f[j].ModTime().UnixNano() // I want a reverse sort, newest first
}

func (f FISliceDate) Swap(i, j int) {
	f[i], f[j] = f[j], f[i]
}

func (f FISliceDate) Len() int {
	return len(f)
}

func (f FISliceSize) Less(i, j int) bool {
	return f[i].Size() > f[j].Size() // I want a reverse sort, largest first
}

func (f FISliceSize) Swap(i, j int) {
	f[i], f[j] = f[j], f[i]
}

func (f FISliceSize) Len() int {
	return len(f)
}

func main() {
	const numlines = 50
	var userptr *user.User
	var files FISlice
	var filesDate FISliceDate
	var filesSize FISliceSize
	var err error
	var count int

	var revflag = flag.Bool("r", false, "reverse the sort, ie, oldest or smallest is first") // Ptr

	var RevFlag bool
	flag.BoolVar(&RevFlag, "R", false, "Reverse the sort, ie, oldest or smallest is first") // Value

	var nlines = flag.Int("n", numlines, "number of lines to display") // Ptr

	var NLines int
	flag.IntVar(&NLines, "N", numlines, "number of lines to display") // Value

	var helpflag = flag.Bool("h", false, "print help message") // pointer
	var HelpFlag bool
	flag.BoolVar(&HelpFlag, "H", false, "print help message")

	var sizeflag = flag.Bool("s", false, "sort by size instead of by date") // pointer
	var SizeFlag bool
	flag.BoolVar(&SizeFlag, "S", false, "sort by size instead of by date")
	flag.Parse()

	fmt.Println(" dsrt will display a directory by date or size.  Written in Go.  LastCompiled ", lastCompiled)
	fmt.Println()

	if *helpflag || HelpFlag {
		flag.PrintDefaults()
	}

	Reverse := *revflag || RevFlag
	SizeSort := *sizeflag || SizeFlag

	NumLines := numlines
	if *nlines != numlines {
		NumLines = *nlines
	} else if NLines != numlines {
		NumLines = NLines
	}
	askforinput := true

	CleanDirName := "." + string(filepath.Separator)
	CleanFileName := ""
	commandline := flag.Arg(0) // this only gets the first non flag argument.  That's all I want
	if len(commandline) > 0 {
		//		CleanDirName = filepath.Clean(commandline)
		CleanDirName, CleanFileName = filepath.Split(commandline)
		CleanFileName = strings.ToUpper(CleanFileName)
		askforinput = false
	}

	if askforinput {
		// Asking for input so don't have to worry about command line globbing
		fmt.Print(" Enter input for globbing: ")
		scanner := bufio.NewScanner(os.Stdin)
		scanner.Scan()
		newtext := scanner.Text()
		if err = scanner.Err(); err != nil {
			fmt.Fprintln(os.Stderr, " reading std input: ", err)
			os.Exit(1)
		}
		if len(newtext) > 0 {
			// time to do the stuff I'm writing this pgm for
			CleanDirName, CleanFileName = filepath.Split(newtext)
			CleanFileName = strings.ToUpper(CleanFileName)
		}

	}

	if len(CleanDirName) == 0 {
		CleanDirName = "." + string(filepath.Separator)
	}

	if len(CleanFileName) == 0 {
		CleanFileName = "*"
	}

	if SizeSort {
		filesSize, err = ioutil.ReadDir(CleanDirName)
		if err != nil {
			log.Fatal(err)
		}
		if Reverse {
			sort.Sort(sort.Reverse(filesSize))
		} else {
			sort.Sort(filesSize)
		}
		files = FISlice(filesSize)
	} else {
		filesDate, err = ioutil.ReadDir(CleanDirName)
		if err != nil {
			log.Fatal(err)
		}
		if Reverse {
			sort.Sort(sort.Reverse(filesDate))
		} else {
			sort.Sort(filesDate)
		}
		files = FISlice(filesDate)
	}

	uid := os.Getuid() // int
	gid := os.Getgid() // int
	systemStr := ""
	linuxflag := runtime.GOOS == "linux"
	if linuxflag {
		systemStr = "Linux"
	} else if runtime.GOOS == "windows" {
		systemStr = "Windows"
	} else {
		systemStr = "Mac, maybe"
	}

	userptr, err = user.Current()
	if err != nil {
		fmt.Println(" user.Current error is ", err, "Exiting.")
		os.Exit(1)
	}

	fmt.Printf("uid = %d, gid = %d, on a computer running %s for %s:%s Username %s, Name %s, HomeDir %s \n",
		uid, gid, systemStr, userptr.Uid, userptr.Gid, userptr.Username, userptr.Name, userptr.HomeDir)

	fmt.Println(" Dirname is", CleanDirName)

	for _, f := range files {
		NAME := strings.ToUpper(f.Name())
		if BOOL, _ := filepath.Match(CleanFileName, NAME); BOOL {
			sysUID := int(f.Sys().(*syscall.Stat_t).Uid) // Stat_t is a uint32
			uidStr := strconv.Itoa(sysUID)
			sysGID := int(f.Sys().(*syscall.Stat_t).Gid) // Stat_t is a uint32
			gidStr := strconv.Itoa(sysGID)

			usernameStr := GetIDname(uidStr)
			groupnameStr := GetIDname(gidStr)

			s := f.ModTime().Format("Jan-02-2006 15:04:05")
			sizeint := int(f.Size())
			sizestr := strconv.Itoa(sizeint)
			if sizeint > 100000 {
				sizestr = AddCommas(sizestr)
			}
			//	old way:		fmt.Printf("%10v %11d %s %s\n", f.Mode(), f.Size(), s, f.Name())
			if linuxflag {
				fmt.Printf("%10v %s:%s %15s %s %s\n", f.Mode(), usernameStr, groupnameStr, sizestr, s, f.Name())
			} else {
				fmt.Printf("%10v %15s %s %s\n", f.Mode(), sizestr, s, f.Name())
			}
			count++
			if count > NumLines {
				break
			}
		}
	}

} // end main dsrt

//-------------------------------------------------------------------- InsertByteSlice
func InsertIntoByteSlice(slice, insertion []byte, index int) []byte {
	return append(slice[:index], append(insertion, slice[index:]...)...)
}

//---------------------------------------------------------------------- AddCommas
func AddCommas(instr string) string {
	var Comma []byte = []byte{','}

	BS := make([]byte, 0, 15)
	BS = append(BS, instr...)

	i := len(BS)

	for NumberOfCommas := i / 3; (NumberOfCommas > 0) && (i > 3); NumberOfCommas-- {
		i -= 3
		BS = InsertIntoByteSlice(BS, Comma, i)
	}
	return string(BS)
} // AddCommas
//---------------------------------------------------------------------------------------------------

// ---------------------------- GetIDname -----------------------------------------------------------
func GetIDname(uidStr string) string {

	ptrToUser, err := user.LookupId(uidStr)
	if err != nil {
		panic("uid not found")
	}

	idname := ptrToUser.Username
	return idname

} // GetIDname

/*
package path
func Match

func Match(pattern, name string) (matched bool, err error)

Match reports whether name matches the shell file name pattern.  The pattern syntax is:

pattern:
	{ term }
term:
	'*'         matches any sequence of non-/ characters
	'?'         matches any single non-/ character
	'[' [ '^' ] { character-range } ']'
	            character class (must be non-empty)
	c           matches character c (c != '*', '?', '\\', '[')
	'\\' c      matches character c

character-range:
	c           matches character c (c != '\\', '-', ']')
	'\\' c      matches character c
	lo '-' hi   matches character c for lo <= c <= hi

Match requires pattern to match all of name, not just a substring.  The only possible returned error is ErrBadPattern, when pattern is malformed.

package os
type FileInfo

type FileInfo interface {
        Name() string       // base name of the file
        Size() int64        // length in bytes for regular files; system-dependent for others
        Mode() FileMode     // file mode bits
        ModTime() time.Time // modification time
        IsDir() bool        // abbreviation for Mode().IsDir()
        Sys() interface{}   // underlying data source (can return nil)
}

A FileInfo describes a file and is returned by Stat and Lstat.

func Lstat

func Lstat(name string) (FileInfo, error)

Lstat returns a FileInfo describing the named file.  If the file is a symbolic link, the returned FileInfo describes the symbolic link.  Lstat makes no attempt to follow the link.
If there is an error, it will be of type *PathError.

func Stat

func Stat(name string) (FileInfo, error)

Stat returns a FileInfo describing the named file.  If there is an error, it will be of type *PathError.

The insight I had with my append troubles that the 1 slice entries were empty, is that when I used append, it would do just that to the end of the slice, and ignore the empty slices.
I needed to make the slice as empty for this to work.  So I am directly assigning the DirEntries slice, and appending the FileNames slice, to make sure that these both are doing what I want.
This code is now doing exactly what I want.  I guess there is no substitute for playing with myself.  Wait, that didn't come out right.  Or did it.

package os/user
type User struct {
  Uid string
  Gid string
  Username string // login name
  Name string     // full or display name.  It may be blank.
  HomeDir string
}
*/
<|endoftext|>


<commit_before>// dsrt.go -- directoy sort in reverse date order.  IE, newest is first.

package main

import (
	"bufio"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path/filepath"
	"runtime"
	"sort"
	"strconv"
	"strings"
)

const LastAltered = "13 Dec 2017"

/*
Revision History
----------------
20 Apr 17 -- Started writing dsize rtn, based on dirlist.go
21 Apr 17 -- Now tweaking the output format.  And used flag package.  One as a pointer and one as a value, just to learn them.
22 Apr 17 -- Coded the use of the first non flag commandline param,  which is all I need.  Note that the flag must appear before the non-flag param, else the flag is ignored.
22 Apr 17 -- Now writing dsrt, to function similarly to dsort.
24 Apr 17 -- Now adding file matching, like "dir" or "ls" does.
25 Apr 17 -- Now adding sort by size as an option, like -s, and commas
26 Apr 17 -- Noticed that the match routine is case sensitive.  I don't like that.
27 Apr 17 -- commandline now allows a file spec.  I intend this for Windows.  I'll see how it goes.
19 May 17 -- Will now show the uid:gid for linux.
20 May 17 -- Turns out that (*syscall.Stat_t) only compiles on linux.  Time for platform specific code.
21 May 17 -- Cross compiling to GOARCH=386, and the uid and User routines won't work.
 2 Sep 17 -- Added timestamp detection code I first wrote for gastricgo.
18 Oct 17 -- Added filesize totals
22 Oct 17 -- Made default numlines of 40.
23 Oct 17 -- Broadened the defaults so that linux default is 40 and windows default is 50.
12 Dec 17 -- Added -d and -D flags to mean directory and nofilename output, respectively.
*/

// FIS is a FileInfo slice, as in os.FileInfo
type FISlice []os.FileInfo
type FISliceDate []os.FileInfo
type FISliceSize []os.FileInfo

func (f FISliceDate) Less(i, j int) bool {
	return f[i].ModTime().UnixNano() > f[j].ModTime().UnixNano() // I want a reverse sort, newest first
}

func (f FISliceDate) Swap(i, j int) {
	f[i], f[j] = f[j], f[i]
}

func (f FISliceDate) Len() int {
	return len(f)
}

func (f FISliceSize) Less(i, j int) bool {
	return f[i].Size() > f[j].Size() // I want a reverse sort, largest first
}

func (f FISliceSize) Swap(i, j int) {
	f[i], f[j] = f[j], f[i]
}

func (f FISliceSize) Len() int {
	return len(f)
}

func main() {
	const defaultlineswin = 50
	const defaultlineslinux = 40
	var numlines int
	var userptr *user.User
	var files FISlice
	var filesDate FISliceDate
	var filesSize FISliceSize
	var err error
	var count int
	var SizeTotal int64

	uid := 0
	gid := 0
	systemStr := ""
	linuxflag := runtime.GOOS == "linux"
	if linuxflag {
		systemStr = "Linux"
		numlines = defaultlineslinux
	} else if runtime.GOOS == "windows" {
		systemStr = "Windows"
		numlines = defaultlineswin
	} else {
		systemStr = "Mac, maybe"
		numlines = defaultlineslinux
	}

	if runtime.GOARCH == "amd64" {
		uid = os.Getuid() // int
		gid = os.Getgid() // int
		userptr, err = user.Current()
		if err != nil {
			fmt.Println(" user.Current error is ", err, "Exiting.")
			os.Exit(1)
		}
	}

	var revflag = flag.Bool("r", false, "reverse the sort, ie, oldest or smallest is first") // Ptr

	var RevFlag bool
	flag.BoolVar(&RevFlag, "R", false, "Reverse the sort, ie, oldest or smallest is first") // Value

	var nlines = flag.Int("n", numlines, "number of lines to display") // Ptr

	var NLines int
	flag.IntVar(&NLines, "N", numlines, "number of lines to display") // Value

	var helpflag = flag.Bool("h", false, "print help message") // pointer
	var HelpFlag bool
	flag.BoolVar(&HelpFlag, "H", false, "print help message")

	var sizeflag = flag.Bool("s", false, "sort by size instead of by date") // pointer
	var SizeFlag bool
	flag.BoolVar(&SizeFlag, "S", false, "sort by size instead of by date")

	var DirListFlag = flag.Bool("d", false, "include directories in the output listing")
	var FilenameListFlag bool
	flag.BoolVar(&FilenameListFlag, "D", true, "include filenames in the output listing")

	flag.Parse()

	fmt.Println(" dsrt will display sorted by date or size.  Written in Go.  LastAltered ", LastAltered)
	execname, _ := os.Executable()
	ExecFI, _ := os.Stat(execname)
	ExecTimeStamp := ExecFI.ModTime().Format("Mon Jan 2 2006 15:04:05 MST")
	fmt.Println(ExecFI.Name(), "timestamp is", ExecTimeStamp, ".  Full exec is", execname)
	fmt.Println()

	if *helpflag || HelpFlag {
		flag.PrintDefaults()
		if runtime.GOARCH == "amd64" {
			fmt.Printf("uid=%d, gid=%d, on a computer running %s for %s:%s Username %s, Name %s, HomeDir %s \n",
				uid, gid, systemStr, userptr.Uid, userptr.Gid, userptr.Username, userptr.Name, userptr.HomeDir)
		}

	}

	Reverse := *revflag || RevFlag
	SizeSort := *sizeflag || SizeFlag

	NumLines := numlines
	if *nlines != numlines {
		NumLines = *nlines
	} else if NLines != numlines {
		NumLines = NLines
	}

	Dirlist := *DirListFlag || !FilenameListFlag // if -D entered then this expression also needs to be true.

	askforinput := true

	CleanDirName := "." + string(filepath.Separator)
	CleanFileName := ""
	commandline := flag.Arg(0) // this only gets the first non flag argument.  That's all I want
	if len(commandline) > 0 {
		//		CleanDirName = filepath.Clean(commandline)
		CleanDirName, CleanFileName = filepath.Split(commandline)
		CleanFileName = strings.ToUpper(CleanFileName)
		askforinput = false
	}

	if askforinput {
		// Asking for input so don't have to worry about command line globbing
		fmt.Print(" Enter input for globbing: ")
		scanner := bufio.NewScanner(os.Stdin)
		scanner.Scan()
		newtext := scanner.Text()
		if err = scanner.Err(); err != nil {
			fmt.Fprintln(os.Stderr, " reading std input: ", err)
			os.Exit(1)
		}
		if len(newtext) > 0 {
			// time to do the stuff I'm writing this pgm for
			CleanDirName, CleanFileName = filepath.Split(newtext)
			CleanFileName = strings.ToUpper(CleanFileName)
		}

	}

	if len(CleanDirName) == 0 {
		CleanDirName = "." + string(filepath.Separator)
	}

	if len(CleanFileName) == 0 {
		CleanFileName = "*"
	}

	if SizeSort {
		filesSize, err = ioutil.ReadDir(CleanDirName)
		if err != nil {
			log.Fatal(err)
		}
		if Reverse {
			sort.Sort(sort.Reverse(filesSize))
		} else {
			sort.Sort(filesSize)
		}
		files = FISlice(filesSize)
	} else {
		filesDate, err = ioutil.ReadDir(CleanDirName)
		if err != nil {
			log.Fatal(err)
		}
		if Reverse {
			sort.Sort(sort.Reverse(filesDate))
		} else {
			sort.Sort(filesDate)
		}
		files = FISlice(filesDate)
	}

	fmt.Println(" Dirname is", CleanDirName)

	for _, f := range files {
		NAME := strings.ToUpper(f.Name())
		//		if BOOL, _ := filepath.Match(CleanFileName, NAME); BOOL && f.Mode().IsRegular() { // altered
		if BOOL, _ := filepath.Match(CleanFileName, NAME); BOOL {
			s := f.ModTime().Format("Jan-02-2006 15:04:05")
			sizeint := 0
			sizestr := ""
			if f.Mode().IsRegular() { // only sum regular files, not dir or symlink entries.
				SizeTotal += f.Size()
				sizeint = int(f.Size())
				sizestr = strconv.Itoa(sizeint)
				if sizeint > 100000 {
					sizestr = AddCommas(sizestr)
				}
			}

			usernameStr, groupnameStr := "", ""
			if runtime.GOARCH == "amd64" {
				usernameStr, groupnameStr = GetUserGroupStr(f)
			}

			if linuxflag {
				if Dirlist && f.IsDir() {
					fmt.Printf("%10v %s:%s %15s %s <%s>\n", f.Mode(), usernameStr, groupnameStr, sizestr, s, f.Name())
				} else if FilenameListFlag && f.Mode().IsRegular() { // altered
					fmt.Printf("%10v %s:%s %15s %s %s\n", f.Mode(), usernameStr, groupnameStr, sizestr, s, f.Name())
				} else if Dirlist { // it's a symlink
					fmt.Printf("%10v %s:%s %15s %s (%s)\n", f.Mode(), usernameStr, groupnameStr, sizestr, s, f.Name())
				}
			} else { // must be windows because this won't compile on Mac.
				if Dirlist && f.IsDir() {
					fmt.Printf("%15s %s <%s>\n", sizestr, s, f.Name())
				} else if FilenameListFlag {
					fmt.Printf("%15s %s %s\n", sizestr, s, f.Name())
				}
			}
			count++
			if count > NumLines {
				break
			}
		}
	}

	s := fmt.Sprintf("%d", SizeTotal)
	if SizeTotal > 100000 {
		s = AddCommas(s)
	}
	fmt.Println(" File Size total =", s)
} // end main dsrt

//-------------------------------------------------------------------- InsertByteSlice
func InsertIntoByteSlice(slice, insertion []byte, index int) []byte {
	return append(slice[:index], append(insertion, slice[index:]...)...)
}

//---------------------------------------------------------------------- AddCommas
func AddCommas(instr string) string {
	var Comma []byte = []byte{','}

	BS := make([]byte, 0, 15)
	BS = append(BS, instr...)

	i := len(BS)

	for NumberOfCommas := i / 3; (NumberOfCommas > 0) && (i > 3); NumberOfCommas-- {
		i -= 3
		BS = InsertIntoByteSlice(BS, Comma, i)
	}
	return string(BS)
} // AddCommas
//---------------------------------------------------------------------------------------------------

// ---------------------------- GetIDname -----------------------------------------------------------
func GetIDname(uidStr string) string {

	if len(uidStr) == 0 {
		return ""
	}
	ptrToUser, err := user.LookupId(uidStr)
	if err != nil {
		panic("uid not found")
	}

	idname := ptrToUser.Username
	return idname

} // GetIDname

/*
package path
func Match

func Match(pattern, name string) (matched bool, err error)

Match reports whether name matches the shell file name pattern.  The pattern syntax is:

pattern:
	{ term }
term:
	'*'         matches any sequence of non-/ characters
	'?'         matches any single non-/ character
	'[' [ '^' ] { character-range } ']'
	            character class (must be non-empty)
	c           matches character c (c != '*', '?', '\\', '[')
	'\\' c      matches character c

character-range:
	c           matches character c (c != '\\', '-', ']')
	'\\' c      matches character c
	lo '-' hi   matches character c for lo <= c <= hi

Match requires pattern to match all of name, not just a substring.  The only possible returned error is ErrBadPattern, when pattern is malformed.

package os
type FileInfo

type FileInfo interface {
        Name() string       // base name of the file
        Size() int64        // length in bytes for regular files; system-dependent for others
        Mode() FileMode     // file mode bits
        ModTime() time.Time // modification time
        IsDir() bool        // abbreviation for Mode().IsDir()
        Sys() interface{}   // underlying data source (can return nil)
}

A FileInfo describes a file and is returned by Stat and Lstat.

func Lstat

func Lstat(name string) (FileInfo, error)

Lstat returns a FileInfo describing the named file.  If the file is a symbolic link, the returned FileInfo describes the symbolic link.  Lstat makes no attempt to follow the link.
If there is an error, it will be of type *PathError.

func Stat

func Stat(name string) (FileInfo, error)

Stat returns a FileInfo describing the named file.  If there is an error, it will be of type *PathError.

The insight I had with my append troubles that the 1 slice entries were empty, is that when I used append, it would do just that to the end of the slice, and ignore the empty slices.
I needed to make the slice as empty for this to work.  So I am directly assigning the DirEntries slice, and appending the FileNames slice, to make sure that these both are doing what I want.
This code is now doing exactly what I want.  I guess there is no substitute for playing with myself.  Wait, that didn't come out right.  Or did it.

package os/user
type User struct {
  Uid string
  Gid string
  Username string // login name
  Name string     // full or display name.  It may be blank.
  HomeDir string
}
*/
<commit_msg>modified:   dsrt/dsrt.go -- working as expected<commit_after>// dsrt.go -- directoy sort in reverse date order.  IE, newest is first.

package main

import (
	"bufio"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path/filepath"
	"runtime"
	"sort"
	"strconv"
	"strings"
)

const LastAltered = "13 Dec 2017"

/*
Revision History
----------------
20 Apr 17 -- Started writing dsize rtn, based on dirlist.go
21 Apr 17 -- Now tweaking the output format.  And used flag package.  One as a pointer and one as a value, just to learn them.
22 Apr 17 -- Coded the use of the first non flag commandline param,  which is all I need.  Note that the flag must appear before the non-flag param, else the flag is ignored.
22 Apr 17 -- Now writing dsrt, to function similarly to dsort.
24 Apr 17 -- Now adding file matching, like "dir" or "ls" does.
25 Apr 17 -- Now adding sort by size as an option, like -s, and commas
26 Apr 17 -- Noticed that the match routine is case sensitive.  I don't like that.
27 Apr 17 -- commandline now allows a file spec.  I intend this for Windows.  I'll see how it goes.
19 May 17 -- Will now show the uid:gid for linux.
20 May 17 -- Turns out that (*syscall.Stat_t) only compiles on linux.  Time for platform specific code.
21 May 17 -- Cross compiling to GOARCH=386, and the uid and User routines won't work.
 2 Sep 17 -- Added timestamp detection code I first wrote for gastricgo.
18 Oct 17 -- Added filesize totals
22 Oct 17 -- Made default numlines of 40.
23 Oct 17 -- Broadened the defaults so that linux default is 40 and windows default is 50.
12 Dec 17 -- Added -d and -D flags to mean directory and nofilename output, respectively.
*/

// FIS is a FileInfo slice, as in os.FileInfo
type FISlice []os.FileInfo
type FISliceDate []os.FileInfo
type FISliceSize []os.FileInfo

func (f FISliceDate) Less(i, j int) bool {
	return f[i].ModTime().UnixNano() > f[j].ModTime().UnixNano() // I want a reverse sort, newest first
}

func (f FISliceDate) Swap(i, j int) {
	f[i], f[j] = f[j], f[i]
}

func (f FISliceDate) Len() int {
	return len(f)
}

func (f FISliceSize) Less(i, j int) bool {
	return f[i].Size() > f[j].Size() // I want a reverse sort, largest first
}

func (f FISliceSize) Swap(i, j int) {
	f[i], f[j] = f[j], f[i]
}

func (f FISliceSize) Len() int {
	return len(f)
}

func main() {
	const defaultlineswin = 50
	const defaultlineslinux = 40
	var numlines int
	var userptr *user.User
	var files FISlice
	var filesDate FISliceDate
	var filesSize FISliceSize
	var err error
	var count int
	var SizeTotal int64

	uid := 0
	gid := 0
	systemStr := ""
	linuxflag := runtime.GOOS == "linux"
	if linuxflag {
		systemStr = "Linux"
		numlines = defaultlineslinux
	} else if runtime.GOOS == "windows" {
		systemStr = "Windows"
		numlines = defaultlineswin
	} else {
		systemStr = "Mac, maybe"
		numlines = defaultlineslinux
	}

	if runtime.GOARCH == "amd64" {
		uid = os.Getuid() // int
		gid = os.Getgid() // int
		userptr, err = user.Current()
		if err != nil {
			fmt.Println(" user.Current error is ", err, "Exiting.")
			os.Exit(1)
		}
	}

	var revflag = flag.Bool("r", false, "reverse the sort, ie, oldest or smallest is first") // Ptr

	var RevFlag bool
	flag.BoolVar(&RevFlag, "R", false, "Reverse the sort, ie, oldest or smallest is first") // Value

	var nlines = flag.Int("n", numlines, "number of lines to display") // Ptr

	var NLines int
	flag.IntVar(&NLines, "N", numlines, "number of lines to display") // Value

	var helpflag = flag.Bool("h", false, "print help message") // pointer
	var HelpFlag bool
	flag.BoolVar(&HelpFlag, "H", false, "print help message")

	var sizeflag = flag.Bool("s", false, "sort by size instead of by date") // pointer
	var SizeFlag bool
	flag.BoolVar(&SizeFlag, "S", false, "sort by size instead of by date")

	var DirListFlag = flag.Bool("d", false, "include directories in the output listing")
	var FilenameListFlag bool
	flag.BoolVar(&FilenameListFlag, "D", false, "include filenames in the output listing")

	flag.Parse()

	fmt.Println(" dsrt will display sorted by date or size.  Written in Go.  LastAltered ", LastAltered)
	execname, _ := os.Executable()
	ExecFI, _ := os.Stat(execname)
	ExecTimeStamp := ExecFI.ModTime().Format("Mon Jan 2 2006 15:04:05 MST")
	fmt.Println(ExecFI.Name(), "timestamp is", ExecTimeStamp, ".  Full exec is", execname)
	fmt.Println()

	if *helpflag || HelpFlag {
		flag.PrintDefaults()
		if runtime.GOARCH == "amd64" {
			fmt.Printf("uid=%d, gid=%d, on a computer running %s for %s:%s Username %s, Name %s, HomeDir %s \n",
				uid, gid, systemStr, userptr.Uid, userptr.Gid, userptr.Username, userptr.Name, userptr.HomeDir)
		}

	}

	Reverse := *revflag || RevFlag
	SizeSort := *sizeflag || SizeFlag

	NumLines := numlines
	if *nlines != numlines {
		NumLines = *nlines
	} else if NLines != numlines {
		NumLines = NLines
	}

	Dirlist := *DirListFlag || FilenameListFlag // if -D entered then this expression also needs to be true.
	FilenameList := !FilenameListFlag           // need to reverse the flag.

	askforinput := true

	CleanDirName := "." + string(filepath.Separator)
	CleanFileName := ""
	commandline := flag.Arg(0) // this only gets the first non flag argument.  That's all I want
	if len(commandline) > 0 {
		//		CleanDirName = filepath.Clean(commandline)
		CleanDirName, CleanFileName = filepath.Split(commandline)
		CleanFileName = strings.ToUpper(CleanFileName)
		askforinput = false
	}

	if askforinput {
		// Asking for input so don't have to worry about command line globbing
		fmt.Print(" Enter input for globbing: ")
		scanner := bufio.NewScanner(os.Stdin)
		scanner.Scan()
		newtext := scanner.Text()
		if err = scanner.Err(); err != nil {
			fmt.Fprintln(os.Stderr, " reading std input: ", err)
			os.Exit(1)
		}
		if len(newtext) > 0 {
			// time to do the stuff I'm writing this pgm for
			CleanDirName, CleanFileName = filepath.Split(newtext)
			CleanFileName = strings.ToUpper(CleanFileName)
		}

	}

	if len(CleanDirName) == 0 {
		CleanDirName = "." + string(filepath.Separator)
	}

	if len(CleanFileName) == 0 {
		CleanFileName = "*"
	}

	if SizeSort {
		filesSize, err = ioutil.ReadDir(CleanDirName)
		if err != nil {
			log.Fatal(err)
		}
		if Reverse {
			sort.Sort(sort.Reverse(filesSize))
		} else {
			sort.Sort(filesSize)
		}
		files = FISlice(filesSize)
	} else {
		filesDate, err = ioutil.ReadDir(CleanDirName)
		if err != nil {
			log.Fatal(err)
		}
		if Reverse {
			sort.Sort(sort.Reverse(filesDate))
		} else {
			sort.Sort(filesDate)
		}
		files = FISlice(filesDate)
	}

	fmt.Println(" Dirname is", CleanDirName)

	for _, f := range files {
		NAME := strings.ToUpper(f.Name())
		//		if BOOL, _ := filepath.Match(CleanFileName, NAME); BOOL && f.Mode().IsRegular() {
		if BOOL, _ := filepath.Match(CleanFileName, NAME); BOOL {
			s := f.ModTime().Format("Jan-02-2006 15:04:05")
			sizeint := 0
			sizestr := ""
			if f.Mode().IsRegular() { // only sum regular files, not dir or symlink entries.
				SizeTotal += f.Size()
				sizeint = int(f.Size())
				sizestr = strconv.Itoa(sizeint)
				if sizeint > 100000 {
					sizestr = AddCommas(sizestr)
				}
			}

			usernameStr, groupnameStr := "", ""
			if runtime.GOARCH == "amd64" {
				usernameStr, groupnameStr = GetUserGroupStr(f)
			}

			if linuxflag {
				if Dirlist && f.IsDir() {
					fmt.Printf("%10v %s:%s %15s %s <%s>\n", f.Mode(), usernameStr, groupnameStr, sizestr, s, f.Name())
				} else if FilenameList && f.Mode().IsRegular() { // altered
					fmt.Printf("%10v %s:%s %15s %s %s\n", f.Mode(), usernameStr, groupnameStr, sizestr, s, f.Name())
				} else if Dirlist && !f.Mode().IsRegular() { // it's a symlink
					fmt.Printf("%10v %s:%s %15s %s (%s)\n", f.Mode(), usernameStr, groupnameStr, sizestr, s, f.Name())
				}
			} else { // must be windows because this won't compile on Mac.
				if Dirlist && f.IsDir() {
					fmt.Printf("%15s %s <%s>\n", sizestr, s, f.Name())
				} else if FilenameList {
					fmt.Printf("%15s %s %s\n", sizestr, s, f.Name())
				}
			}
			count++
			if count > NumLines {
				break
			}
		}
	}

	s := fmt.Sprintf("%d", SizeTotal)
	if SizeTotal > 100000 {
		s = AddCommas(s)
	}
	fmt.Println(" File Size total =", s)
} // end main dsrt

//-------------------------------------------------------------------- InsertByteSlice
func InsertIntoByteSlice(slice, insertion []byte, index int) []byte {
	return append(slice[:index], append(insertion, slice[index:]...)...)
}

//---------------------------------------------------------------------- AddCommas
func AddCommas(instr string) string {
	var Comma []byte = []byte{','}

	BS := make([]byte, 0, 15)
	BS = append(BS, instr...)

	i := len(BS)

	for NumberOfCommas := i / 3; (NumberOfCommas > 0) && (i > 3); NumberOfCommas-- {
		i -= 3
		BS = InsertIntoByteSlice(BS, Comma, i)
	}
	return string(BS)
} // AddCommas
//---------------------------------------------------------------------------------------------------

// ---------------------------- GetIDname -----------------------------------------------------------
func GetIDname(uidStr string) string {

	if len(uidStr) == 0 {
		return ""
	}
	ptrToUser, err := user.LookupId(uidStr)
	if err != nil {
		panic("uid not found")
	}

	idname := ptrToUser.Username
	return idname

} // GetIDname

/*
package path
func Match

func Match(pattern, name string) (matched bool, err error)

Match reports whether name matches the shell file name pattern.  The pattern syntax is:

pattern:
	{ term }
term:
	'*'         matches any sequence of non-/ characters
	'?'         matches any single non-/ character
	'[' [ '^' ] { character-range } ']'
	            character class (must be non-empty)
	c           matches character c (c != '*', '?', '\\', '[')
	'\\' c      matches character c

character-range:
	c           matches character c (c != '\\', '-', ']')
	'\\' c      matches character c
	lo '-' hi   matches character c for lo <= c <= hi

Match requires pattern to match all of name, not just a substring.  The only possible returned error is ErrBadPattern, when pattern is malformed.

package os
type FileInfo

type FileInfo interface {
        Name() string       // base name of the file
        Size() int64        // length in bytes for regular files; system-dependent for others
        Mode() FileMode     // file mode bits
        ModTime() time.Time // modification time
        IsDir() bool        // abbreviation for Mode().IsDir()
        Sys() interface{}   // underlying data source (can return nil)
}

A FileInfo describes a file and is returned by Stat and Lstat.

func Lstat

func Lstat(name string) (FileInfo, error)

Lstat returns a FileInfo describing the named file.  If the file is a symbolic link, the returned FileInfo describes the symbolic link.  Lstat makes no attempt to follow the link.
If there is an error, it will be of type *PathError.

func Stat

func Stat(name string) (FileInfo, error)

Stat returns a FileInfo describing the named file.  If there is an error, it will be of type *PathError.

The insight I had with my append troubles that the 1 slice entries were empty, is that when I used append, it would do just that to the end of the slice, and ignore the empty slices.
I needed to make the slice as empty for this to work.  So I am directly assigning the DirEntries slice, and appending the FileNames slice, to make sure that these both are doing what I want.
This code is now doing exactly what I want.  I guess there is no substitute for playing with myself.  Wait, that didn't come out right.  Or did it.

package os/user
type User struct {
  Uid string
  Gid string
  Username string // login name
  Name string     // full or display name.  It may be blank.
  HomeDir string
}
*/
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strconv"
	"github.com/hyperledger/fabric/core/chaincode/shim"
	pb "github.com/hyperledger/fabric/protos/peer"
	"encoding/pem"
	"crypto/x509"
	"bytes"
	"errors"
	"encoding/json"
	"time"
)

// SimpleChaincode example simple Chaincode implementation
type SimpleChaincode struct {
}

type HistoryResponse struct {
	Wallet  string
	Amount  float64
	Message string
	Time    string
}

type ChargeRequest struct {
	FromWallet  string
	ToWallet  string
	Amount  float32
}

func ParseCreatorCertificate(stub shim.ChaincodeStubInterface) (string, error) {
	creator, err := stub.GetCreator()
	if err != nil {
		return "", err
	}
	if creator == nil {
		fmt.Print("No certificate found")
		return "", errors.New("No certificate found")
	}
	certStart := bytes.IndexAny(creator, "----BEGIN CERTIFICATE-----")
	if certStart == -1 {
		fmt.Print("No certificate found")
		return "", errors.New("No certificate found")
	}
	certText := creator[certStart:]
	block, _ := pem.Decode(certText)
	if block == nil {
		fmt.Printf("Error received on pem.Decode of certificate %s",  certText)
		return "", errors.New("Error received on pem.Decode of certificate")
	}

	uCert, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		fmt.Printf("Error received on ParseCertificate %s", err)
		return "", err
	}

	fmt.Printf("Common Name %s ", uCert.Subject.CommonName)
	return uCert.Subject.CommonName, nil
}

func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
	fmt.Println("Wallet Init")
	err := stub.PutState("total_amount", []byte("0"))
	if err != nil {
		return shim.Error(err.Error())
	}
	return shim.Success(nil)
}

func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
	fmt.Println("Wallet Invoke")
	function, args := stub.GetFunctionAndParameters()
	if function == "create" {
		// Create wallet
		return t.create(stub, args)
	} else if function == "charge" {
		// Make payment of X units from A to B
		return t.charge(stub, args)
	} else if function == "query" {
		// the old "Query" is now implemtned in invoke
		return t.getBalance(stub, args)
	} else if function == "queryHistory" {
		return t.queryHistory(stub, args)
	} else if function == "chargeMultiple" {
		return t.chargeMultiple(stub, args)
	}

	return shim.Error("Invalid invoke function name. Expecting \"invoke\" \"delete\" \"query\"")
}

func (t *SimpleChaincode) create(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	var accountName, cert string
	accountName = args[0]

	accountValBytes, err := stub.GetState(accountName + "_owner")
	if err != nil {
		return shim.Error("Failed to get state")
	}
	if accountValBytes != nil {
		return shim.Error("Entity already exists")
	}

	cert, err = ParseCreatorCertificate(stub)

	err = stub.PutState(accountName + "_owner", []byte(cert))
	if err != nil {
		return shim.Error(err.Error())
	}
	return shim.Success(nil)
}

// Transaction charges of X units from source to destination
func (t *SimpleChaincode) charge(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	var sourceAccount, destinationAccount, cert string    // Entities
	var sourceValue, destinationValue float64 // Asset holdings
	var chargeAmount float64          // Charge value
	var err error

	if len(args) != 3 {
		return shim.Error("Incorrect number of arguments. Expecting 3")
	}

	sourceAccount = args[0]
	destinationAccount = args[1]

	cert, err = ParseCreatorCertificate(stub)

	accountCertBytes, err := stub.GetState(sourceAccount + "_owner")

	if string(accountCertBytes) != cert {
		return shim.Error("Invalid certificate")
	}

	// Get the state from the ledger
	// TODO: will be nice to have a GetAllState call to ledger
	sourceValBytes, err := stub.GetState(sourceAccount + "." + destinationAccount)
	if err != nil {
		return shim.Error("Failed to get state")
	}

	if sourceValBytes == nil {
		sourceValue = 0
	} else {
		sourceValue, _ = strconv.ParseFloat(string(sourceValBytes), 64)
	}

	destinationValBytes, err := stub.GetState(destinationAccount + "." + sourceAccount)
	if err != nil {
		return shim.Error("Failed to get state")
	}
	if destinationValBytes == nil {
		destinationValue = 0
	} else {
		destinationValue, _ = strconv.ParseFloat(string(destinationValBytes), 64)
	}

	// Perform the execution
	chargeAmount, err = strconv.ParseFloat(args[2], 64)
	if err != nil {
		return shim.Error("Invalid transaction amount, expecting a integer value")
	}
	sourceValue = sourceValue - chargeAmount
	destinationValue = destinationValue + chargeAmount
	fmt.Printf("Aval = %d, Bval = %d\n", sourceValue, destinationValue)

	// Write the state back to the ledger
	err = stub.PutState(sourceAccount  + "." + destinationAccount, []byte(strconv.FormatFloat(sourceValue, 'f', 2, 64)))
	if err != nil {
		return shim.Error(err.Error())
	}

	err = stub.PutState(destinationAccount + "." + sourceAccount, []byte(strconv.FormatFloat(destinationValue, 'f', 2, 64)))
	if err != nil {
		return shim.Error(err.Error())
	}

	return shim.Success(nil)
}

func (t *SimpleChaincode) chargeMultiple(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	response := []ChargeRequest{}
	json.Unmarshal(args[0], &response)
	for _, elem := range response {
		chargeArgs := []string{elem.FromWallet, elem.ToWallet, elem.Amount}
		t.charge(stub, chargeArgs)
	}
	return shim.Success(nil)
}

// query callback representing the query of a chaincode
func (t *SimpleChaincode) getBalance(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	var sourceAccountName, destinationAccountName string // Entities
	var err error

	if len(args) != 2 {
		return shim.Error("Incorrect number of arguments. Expecting name of the person to query")
	}

	sourceAccountName = args[0]
	destinationAccountName = args[1]

	// Get the state from the ledger
	accountValBytes, err := stub.GetState(sourceAccountName + "." + destinationAccountName)
	if err != nil {
		jsonResp := "{\"Error\":\"Failed to get state for " + sourceAccountName + "." + destinationAccountName + "\"}"
		return shim.Error(jsonResp)
	}

	if accountValBytes == nil {
		accountValBytes = []byte("0")
	}

	jsonResp := "{\"Source\":\"" + sourceAccountName + "\"," +
		"\"Destination\":\"" + destinationAccountName + "\",\"" +
		"Amount\":\"" + string(accountValBytes) + "\"}"
	fmt.Printf("Query Response:%s\n", jsonResp)
	return shim.Success(accountValBytes)
}

func (t *SimpleChaincode) queryHistory(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	var sourceAccount, destinationAccount string
	sourceAccount = args[0]
	destinationAccount = args[1]
	iterator, err := stub.GetHistoryForKey(sourceAccount + "." + destinationAccount)
	if err != nil {
		jsonResp := "{\"Error\":\"Failed to get state for " + sourceAccount + "." + destinationAccount + "\"}"
		fmt.Print(jsonResp)
		return shim.Error(jsonResp)
	}
	var prevVal, amount float64
	var flag bool
	flag = false
	prevVal = 0
	response := []HistoryResponse{}
	for ;iterator.HasNext(); {
		qResult, err := iterator.Next()
		if err != nil {
			jsonResp := "{\"Error\":\"Failed to get state for " + sourceAccount + "." + destinationAccount + "\"}"
			fmt.Print(jsonResp)
			return shim.Error(jsonResp)
		}

		accountValBytes := qResult.Value
		value, err := strconv.ParseFloat(string(accountValBytes), 64)
		if err != nil {
			return shim.Error(err.Error())
		}
		if !flag {
			amount = value
			prevVal = amount
			flag = true
		} else {
			amount = value - prevVal
			prevVal = value
		}
		message := "INCOME"
		if amount < 0 {
			message = "SPENT"
		}
		response = append(response, HistoryResponse{
			Wallet:  destinationAccount,
			Amount:  amount,
			Message: message,
			Time:    time.Unix(qResult.Timestamp.GetSeconds(),
				int64(qResult.Timestamp.GetNanos())).Format("15.01.2006 15:04:05")})
	}
	jsonStr, err := json.Marshal(response)
	fmt.Print(jsonStr)
	return shim.Success(jsonStr)
}

func main() {
	err := shim.Start(new(SimpleChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}
<commit_msg>several charges in one block<commit_after>package main

import (
	"fmt"
	"strconv"
	"github.com/hyperledger/fabric/core/chaincode/shim"
	pb "github.com/hyperledger/fabric/protos/peer"
	"encoding/pem"
	"crypto/x509"
	"bytes"
	"errors"
	"encoding/json"
	"time"
)

// SimpleChaincode example simple Chaincode implementation
type SimpleChaincode struct {
}

type HistoryResponse struct {
	Wallet  string
	Amount  float64
	Message string
	Time    string
}

type ChargeRequest struct {
	FromWallet  string
	ToWallet  string
	Amount  float32
}

func ParseCreatorCertificate(stub shim.ChaincodeStubInterface) (string, error) {
	creator, err := stub.GetCreator()
	if err != nil {
		return "", err
	}
	if creator == nil {
		fmt.Print("No certificate found")
		return "", errors.New("No certificate found")
	}
	certStart := bytes.IndexAny(creator, "----BEGIN CERTIFICATE-----")
	if certStart == -1 {
		fmt.Print("No certificate found")
		return "", errors.New("No certificate found")
	}
	certText := creator[certStart:]
	block, _ := pem.Decode(certText)
	if block == nil {
		fmt.Printf("Error received on pem.Decode of certificate %s",  certText)
		return "", errors.New("Error received on pem.Decode of certificate")
	}

	uCert, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		fmt.Printf("Error received on ParseCertificate %s", err)
		return "", err
	}

	fmt.Printf("Common Name %s ", uCert.Subject.CommonName)
	return uCert.Subject.CommonName, nil
}

func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
	fmt.Println("Wallet Init")
	err := stub.PutState("total_amount", []byte("0"))
	if err != nil {
		return shim.Error(err.Error())
	}
	return shim.Success(nil)
}

func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
	fmt.Println("Wallet Invoke")
	function, args := stub.GetFunctionAndParameters()
	if function == "create" {
		// Create wallet
		return t.create(stub, args)
	} else if function == "charge" {
		// Make payment of X units from A to B
		return t.charge(stub, args)
	} else if function == "query" {
		// the old "Query" is now implemtned in invoke
		return t.getBalance(stub, args)
	} else if function == "queryHistory" {
		return t.queryHistory(stub, args)
	} else if function == "chargeMultiple" {
		return t.chargeMultiple(stub, args)
	}

	return shim.Error("Invalid invoke function name. Expecting \"invoke\" \"delete\" \"query\"")
}

func (t *SimpleChaincode) create(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	var accountName, cert string
	accountName = args[0]

	accountValBytes, err := stub.GetState(accountName + "_owner")
	if err != nil {
		return shim.Error("Failed to get state")
	}
	if accountValBytes != nil {
		return shim.Error("Entity already exists")
	}

	cert, err = ParseCreatorCertificate(stub)

	err = stub.PutState(accountName + "_owner", []byte(cert))
	if err != nil {
		return shim.Error(err.Error())
	}
	return shim.Success(nil)
}

// Transaction charges of X units from source to destination
func (t *SimpleChaincode) charge(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	var sourceAccount, destinationAccount, cert string    // Entities
	var sourceValue, destinationValue float64 // Asset holdings
	var chargeAmount float64          // Charge value
	var err error

	if len(args) != 3 {
		return shim.Error("Incorrect number of arguments. Expecting 3")
	}

	sourceAccount = args[0]
	destinationAccount = args[1]

	cert, err = ParseCreatorCertificate(stub)

	accountCertBytes, err := stub.GetState(sourceAccount + "_owner")

	if string(accountCertBytes) != cert {
		return shim.Error("Invalid certificate")
	}

	// Get the state from the ledger
	// TODO: will be nice to have a GetAllState call to ledger
	sourceValBytes, err := stub.GetState(sourceAccount + "." + destinationAccount)
	if err != nil {
		return shim.Error("Failed to get state")
	}

	if sourceValBytes == nil {
		sourceValue = 0
	} else {
		sourceValue, _ = strconv.ParseFloat(string(sourceValBytes), 64)
	}

	destinationValBytes, err := stub.GetState(destinationAccount + "." + sourceAccount)
	if err != nil {
		return shim.Error("Failed to get state")
	}
	if destinationValBytes == nil {
		destinationValue = 0
	} else {
		destinationValue, _ = strconv.ParseFloat(string(destinationValBytes), 64)
	}

	// Perform the execution
	chargeAmount, err = strconv.ParseFloat(args[2], 64)
	if err != nil {
		return shim.Error("Invalid transaction amount, expecting a integer value")
	}
	sourceValue = sourceValue - chargeAmount
	destinationValue = destinationValue + chargeAmount
	fmt.Printf("Aval = %d, Bval = %d\n", sourceValue, destinationValue)

	// Write the state back to the ledger
	err = stub.PutState(sourceAccount  + "." + destinationAccount, []byte(strconv.FormatFloat(sourceValue, 'f', 2, 64)))
	if err != nil {
		return shim.Error(err.Error())
	}

	err = stub.PutState(destinationAccount + "." + sourceAccount, []byte(strconv.FormatFloat(destinationValue, 'f', 2, 64)))
	if err != nil {
		return shim.Error(err.Error())
	}

	return shim.Success(nil)
}

func (t *SimpleChaincode) chargeMultiple(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	response := []ChargeRequest{}
	json.Unmarshal([]byte(args[0]), &response)
	for _, elem := range response {
		chargeArgs := []string{elem.FromWallet, elem.ToWallet, elem.Amount}
		t.charge(stub, chargeArgs)
	}
	return shim.Success(nil)
}

// query callback representing the query of a chaincode
func (t *SimpleChaincode) getBalance(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	var sourceAccountName, destinationAccountName string // Entities
	var err error

	if len(args) != 2 {
		return shim.Error("Incorrect number of arguments. Expecting name of the person to query")
	}

	sourceAccountName = args[0]
	destinationAccountName = args[1]

	// Get the state from the ledger
	accountValBytes, err := stub.GetState(sourceAccountName + "." + destinationAccountName)
	if err != nil {
		jsonResp := "{\"Error\":\"Failed to get state for " + sourceAccountName + "." + destinationAccountName + "\"}"
		return shim.Error(jsonResp)
	}

	if accountValBytes == nil {
		accountValBytes = []byte("0")
	}

	jsonResp := "{\"Source\":\"" + sourceAccountName + "\"," +
		"\"Destination\":\"" + destinationAccountName + "\",\"" +
		"Amount\":\"" + string(accountValBytes) + "\"}"
	fmt.Printf("Query Response:%s\n", jsonResp)
	return shim.Success(accountValBytes)
}

func (t *SimpleChaincode) queryHistory(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	var sourceAccount, destinationAccount string
	sourceAccount = args[0]
	destinationAccount = args[1]
	iterator, err := stub.GetHistoryForKey(sourceAccount + "." + destinationAccount)
	if err != nil {
		jsonResp := "{\"Error\":\"Failed to get state for " + sourceAccount + "." + destinationAccount + "\"}"
		fmt.Print(jsonResp)
		return shim.Error(jsonResp)
	}
	var prevVal, amount float64
	var flag bool
	flag = false
	prevVal = 0
	response := []HistoryResponse{}
	for ;iterator.HasNext(); {
		qResult, err := iterator.Next()
		if err != nil {
			jsonResp := "{\"Error\":\"Failed to get state for " + sourceAccount + "." + destinationAccount + "\"}"
			fmt.Print(jsonResp)
			return shim.Error(jsonResp)
		}

		accountValBytes := qResult.Value
		value, err := strconv.ParseFloat(string(accountValBytes), 64)
		if err != nil {
			return shim.Error(err.Error())
		}
		if !flag {
			amount = value
			prevVal = amount
			flag = true
		} else {
			amount = value - prevVal
			prevVal = value
		}
		message := "INCOME"
		if amount < 0 {
			message = "SPENT"
		}
		response = append(response, HistoryResponse{
			Wallet:  destinationAccount,
			Amount:  amount,
			Message: message,
			Time:    time.Unix(qResult.Timestamp.GetSeconds(),
				int64(qResult.Timestamp.GetNanos())).Format("15.01.2006 15:04:05")})
	}
	jsonStr, err := json.Marshal(response)
	fmt.Print(jsonStr)
	return shim.Success(jsonStr)
}

func main() {
	err := shim.Start(new(SimpleChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}
<|endoftext|>


<commit_before>package asock

// Copyright (c) 2014,2015 Shawn Boyette <shawn@firepear.net>. All
// rights reserved.  Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

// Socket code for asock

import (
	"log"
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"net"
	"time"

	"firepear.net/qsplit"
)

// sockAccept monitors the listener socket and spawns connections for
// clients.
func (a *Asock) sockAccept() {
	defer a.w.Done()
	var cn uint
	for cn = 1; true; cn++ {
		c, err := a.l.Accept()
		if err != nil {
			select {
			case <-a.q:
				// a.Quit() was invoked; close up shop
				a.Msgr <- &Msg{0, 0, 199, "Quit called: closing listener socket", nil}
				return
			default:
				// we've had a networking error
				a.Msgr <- &Msg{0, 0, 599, "read from listener socket failed", err}
				return
			}
		}
		// we have a new client
		a.w.Add(1)
		go a.connHandler(c, cn)
	}
}

// connHandler dispatches commands from, and sends reponses to, a client. It
// is launched, per-connection, from sockAccept().
func (a *Asock) connHandler(c net.Conn, cn uint) {
	defer a.w.Done()
	defer c.Close()
	// request counter for this connection
	var reqnum uint

	a.genMsg(cn, reqnum, 100, Conn, "client connected", nil)
	for {
		reqnum++
		// check if we're a one-shot connection, and if we're done
		if a.t < 0 && reqnum > 1 {
			a.genMsg(cn, reqnum, 197, Conn, "ending session", nil)
			return
		}

		// read the request
		log.Println("reading request")
		req, err := a.connRead(c, cn, reqnum)
		if err != nil {
			a.genMsg(cn, reqnum, 197, Conn, "ending session", nil)
			// TODO write "you're being dropped" msg
			return
		}
		log.Println("request: ", string(req))

		// dispatch the request and get the reply
		reply, err := a.reqDispatch(c, cn, reqnum, req)
		if err != nil {
			continue
		}

		// send reply
		err = a.sendMsg(c, cn, reqnum, reply)
		if err != nil {
			a.genMsg(cn, reqnum, 197, Conn, "ending session", nil)
			return
		}
		a.genMsg(cn, reqnum, 200, All, "reply sent", nil)
	}
}

func (a *Asock) connRead(c net.Conn, cn, reqnum uint) ([]byte, error) {
	// buffer 0 holds the message length
	b0 := make([]byte, 4)
	// buffer 1: network reads go here, 128B at a time
	b1 := make([]byte, 128)
	// buffer 2: data accumulates here; requests pulled from here
	var b2 []byte
	// message length
	var mlen int32
	// bytes read so far
	var bread int32

	// zero our byte-accumulator; set timeout deadline
	b2 = b2[:0]
	bread = 0

	// get the response message length
	a.setConnTimeout(c)
	n, err := c.Read(b0)
	log.Println("read", n, "bytes")
	if err != nil {
		a.genMsg(cn, reqnum, 501, Error, "failed to read mlen from socket", err)
		return nil, err
	}
	if  n != 4 {
		err = fmt.Errorf("too few bytes")
		a.genMsg(cn, reqnum, 501, Error, "short read on message length", err)
		return nil, err
	}
	buf := bytes.NewReader(b0)
	err = binary.Read(buf, binary.BigEndian, &mlen)
	if err != nil {
		a.genMsg(cn, reqnum, 501, Error, "could not decode message length", err)
		return nil, err
	}
	log.Println("mlen is", mlen)
	if mlen == 0 {
		a.sendMsg(c, cn, reqnum, []byte(fmt.Sprintf("Received empty request. Available commands: %v", a.help)))
		a.genMsg(cn, reqnum, 401, All, "nil request", nil)
		return nil, err
	}

	log.Println(bread, mlen)
	for bread < mlen {
		a.setConnTimeout(c)
		n, err := c.Read(b1)
		log.Println("read", n, "bytes")
		if err != nil {
			if err == io.EOF {
				a.genMsg(cn, reqnum, 198, Conn, "client disconnected", nil)
			} else {
				a.genMsg(cn, reqnum, 501, Error, "failed to read req from socket", err)
				return nil, err
			}
		}
		if n == 0 {
			a.sendMsg(c, cn, reqnum, []byte(fmt.Sprintf("Received empty request. Available commands: %v", a.help)))
			a.genMsg(cn, reqnum, 401, All, "nil request", nil)
			return nil, err
		}
		bread += int32(n)
		b2 = append(b2, b1[:n]...)
		log.Println(bread, mlen, string(b2))
	}
	return b2[:mlen], nil
}

func (a *Asock) reqDispatch(c net.Conn, cn, reqnum uint, req []byte) ([]byte, error) {
	cl := qsplit.Locations(req)
	dcmd := string(req[cl[0][0]:cl[0][1]])
	// now get the args
	var dargs []byte
	if len(cl) == 1 {
		dargs = nil
	} else {
		dargs = req[cl[1][0]:]
	}
	// send error and list of known commands if we don't
	// recognize the command
	dfunc, ok := a.d[dcmd]
	if !ok {
		err := a.sendMsg(c, cn, reqnum, []byte(fmt.Sprintf("Unknown command '%s'. Available commands: %s",	dcmd, a.help)))
		a.genMsg(cn, reqnum, 400, All, fmt.Sprintf("bad command '%s'", dcmd), nil)
		return nil, err
	}
	// ok, we know the command and we have its dispatch
	// func. call it and send response
	a.genMsg(cn, reqnum, 101, All, fmt.Sprintf("dispatching [%s]", dcmd), nil)
	var rs [][]byte // req, split by word
	switch dfunc.argmode {
	case "split":
		rs = qsplit.ToBytes(dargs)
	case "nosplit":
		rs = rs[:0]
		rs = append(rs, dargs)
	}
	resp, err := dfunc.df(rs)
	if err != nil {
		a.genMsg(cn, reqnum, 500, Error, "request failed", err)
		err = a.sendMsg(c, cn, reqnum, []byte("Sorry, an error occurred and your request could not be completed."))
		return nil, err
	}
	return resp, nil
}

func (a *Asock) sendMsg(c net.Conn, cn, reqnum uint, resp []byte) error {
	buf := new(bytes.Buffer)
	binary.Write(buf, binary.BigEndian, int32(len(resp)))
	a.setConnTimeout(c)
	_, err := c.Write(buf.Bytes())
	if err != nil {
		a.genMsg(cn, reqnum, 502, Error, "failed to write mlen to socket", err)
		return err
	}
	a.setConnTimeout(c)
	_, err = c.Write(resp)
	if err != nil {
		a.genMsg(cn, reqnum, 502, Error, "failed to write resp to socket", err)
	}
	return err
}

func (a *Asock) setConnTimeout(c net.Conn) {
	if a.t == 0 {
		return
	}
	var t time.Duration
	if a.t > 0 {
		t = time.Duration(a.t)
	} else {
		t = time.Duration(a.t - (a.t * 2))
	}
	_ = c.SetReadDeadline(time.Now().Add(t * time.Millisecond))
}
<commit_msg>small net handling improvements<commit_after>package asock

// Copyright (c) 2014,2015 Shawn Boyette <shawn@firepear.net>. All
// rights reserved.  Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

// Socket code for asock

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"net"
	"time"

	"firepear.net/qsplit"
)

// sockAccept monitors the listener socket and spawns connections for
// clients.
func (a *Asock) sockAccept() {
	defer a.w.Done()
	var cn uint
	for cn = 1; true; cn++ {
		c, err := a.l.Accept()
		if err != nil {
			select {
			case <-a.q:
				// a.Quit() was invoked; close up shop
				a.Msgr <- &Msg{0, 0, 199, "Quit called: closing listener socket", nil}
				return
			default:
				// we've had a networking error
				a.Msgr <- &Msg{0, 0, 599, "read from listener socket failed", err}
				return
			}
		}
		// we have a new client
		a.w.Add(1)
		go a.connHandler(c, cn)
	}
}

// connHandler dispatches commands from, and sends reponses to, a client. It
// is launched, per-connection, from sockAccept().
func (a *Asock) connHandler(c net.Conn, cn uint) {
	defer a.w.Done()
	defer c.Close()
	// request counter for this connection
	var reqnum uint

	a.genMsg(cn, reqnum, 100, Conn, "client connected", nil)
	for {
		reqnum++
		// check if we're a one-shot connection, and if we're done
		if a.t < 0 && reqnum > 1 {
			a.genMsg(cn, reqnum, 197, Conn, "ending session", nil)
			return
		}

		// read the request
		req, err := a.connRead(c, cn, reqnum)
		if err != nil {
			a.genMsg(cn, reqnum, 197, Conn, "ending session", nil)
			// TODO write "you're being dropped" msg
			return
		}
		if len(req) == 0 {
			a.sendMsg(c, cn, reqnum, []byte(fmt.Sprintf("Received empty request. Available commands: %v", a.help)))
			a.genMsg(cn, reqnum, 401, All, "nil request", nil)
			continue
		}

		// dispatch the request and get the reply
		reply, err := a.reqDispatch(c, cn, reqnum, req)
		if err != nil {
			continue
		}

		// send reply
		err = a.sendMsg(c, cn, reqnum, reply)
		if err != nil {
			a.genMsg(cn, reqnum, 197, Conn, "ending session", nil)
			return
		}
		a.genMsg(cn, reqnum, 200, All, "reply sent", nil)
	}
}

func (a *Asock) connRead(c net.Conn, cn, reqnum uint) ([]byte, error) {
	// buffer 0 holds the message length
	b0 := make([]byte, 4)
	// buffer 1: network reads go here, 128B at a time
	b1 := make([]byte, 128)
	// buffer 2: data accumulates here; requests pulled from here
	var b2 []byte
	// message length
	var mlen int32
	// bytes read so far
	var bread int32

	// zero our byte-accumulator; set timeout deadline
	b2 = b2[:0]
	bread = 0

	// get the response message length
	a.setConnTimeout(c)
	n, err := c.Read(b0)
	if err != nil {
		if err == io.EOF {
			a.genMsg(cn, reqnum, 198, Conn, "client disconnected", nil)
		} else {
			a.genMsg(cn, reqnum, 501, Conn, "failed to read mlen from socket", err)
		}
		return nil, err
	}
	if  n != 4 {
		err = fmt.Errorf("too few bytes")
		a.genMsg(cn, reqnum, 501, Conn, "short read on message length", err)
		return nil, err
	}
	buf := bytes.NewReader(b0)
	err = binary.Read(buf, binary.BigEndian, &mlen)
	if err != nil {
		a.genMsg(cn, reqnum, 501, Conn, "could not decode message length", err)
		return nil, err
	}

	for bread < mlen {
		a.setConnTimeout(c)
		n, err := c.Read(b1)
		if err != nil {
			if err == io.EOF {
				a.genMsg(cn, reqnum, 198, Conn, "client disconnected", nil)
			} else {
				a.genMsg(cn, reqnum, 501, Conn, "failed to read req from socket", err)
				return nil, err
			}
		}
		if n == 0 {
			// short-circuit just in case this ever manages to happen
			return b2[:mlen], err
		}
		bread += int32(n)
		b2 = append(b2, b1[:n]...)
	}
	return b2[:mlen], err
}

func (a *Asock) reqDispatch(c net.Conn, cn, reqnum uint, req []byte) ([]byte, error) {
	cl := qsplit.Locations(req)
	dcmd := string(req[cl[0][0]:cl[0][1]])
	// now get the args
	var dargs []byte
	if len(cl) == 1 {
		dargs = nil
	} else {
		dargs = req[cl[1][0]:]
	}
	// send error and list of known commands if we don't
	// recognize the command
	dfunc, ok := a.d[dcmd]
	if !ok {
		err := a.sendMsg(c, cn, reqnum, []byte(fmt.Sprintf("Unknown command '%s'. Available commands: %s",	dcmd, a.help)))
		a.genMsg(cn, reqnum, 400, All, fmt.Sprintf("bad command '%s'", dcmd), nil)
		return nil, err
	}
	// ok, we know the command and we have its dispatch
	// func. call it and send response
	a.genMsg(cn, reqnum, 101, All, fmt.Sprintf("dispatching [%s]", dcmd), nil)
	var rs [][]byte // req, split by word
	switch dfunc.argmode {
	case "split":
		rs = qsplit.ToBytes(dargs)
	case "nosplit":
		rs = rs[:0]
		rs = append(rs, dargs)
	}
	resp, err := dfunc.df(rs)
	if err != nil {
		a.genMsg(cn, reqnum, 500, Error, "request failed", err)
		err = a.sendMsg(c, cn, reqnum, []byte("Sorry, an error occurred and your request could not be completed."))
		return nil, err
	}
	return resp, nil
}

func (a *Asock) sendMsg(c net.Conn, cn, reqnum uint, resp []byte) error {
	buf := new(bytes.Buffer)
	binary.Write(buf, binary.BigEndian, int32(len(resp)))
	resp = append(buf.Bytes(), resp...)
	a.setConnTimeout(c)
	_, err := c.Write(resp)
	if err != nil {
		a.genMsg(cn, reqnum, 502, Error, "failed to write resp to socket", err)
	}
	return err
}

func (a *Asock) setConnTimeout(c net.Conn) {
	if a.t == 0 {
		return
	}
	var t time.Duration
	if a.t > 0 {
		t = time.Duration(a.t)
	} else {
		t = time.Duration(a.t - (a.t * 2))
	}
	_ = c.SetReadDeadline(time.Now().Add(t * time.Millisecond))
}
<|endoftext|>


<commit_before>// Simple utility for counting messages on a Kafka topic.
//
// Copyright (C) 2017 ENEO Tecnologia SL
// Author: Diego Fernández Barrera <bigomby@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package main

import (
	"errors"
	"reflect"
	"strconv"

	"github.com/Sirupsen/logrus"

	yaml "gopkg.in/yaml.v2"
)

// AppConfig contains the main application configuration.
type AppConfig struct {
	Counters struct {
		BatchTimeoutSeconds uint   `yaml:"batch_timeout_s" default:"5"`
		BatchMaxMessages    uint   `yaml:"batch_max_messages" default:"1000"`
		UUIDKey             string `yaml:"uuid_key"`
		Kafka               struct {
			ReadTopics      []string          `yaml:"read_topics" mandatory:"true"`
			WriteTopic      string            `yaml:"write_topic" mandatory:"true"`
			Attributes      map[string]string `yaml:"attributes"`
			TopicAttributes map[string]string `yaml:"topic_attributes"`
		}
	}

	Monitor struct {
		Timer struct {
			Period int64 `yaml:"period" default:"86400"`
			Offset int64 `yaml:"offset"`
		}
		Kafka struct {
			ReadTopics      []string          `yaml:"read_topics" mandatory:"true"`
			WriteTopic      string            `yaml:"write_topic" mandatory:"true"`
			Attributes      map[string]string `yaml:"attributes"`
			TopicAttributes map[string]string `yaml:"topic_attributes"`
		}
	}

	LicensesDirectory string `yaml:"licenses_directory" mandatory:"true"`

	Licenses []struct {
		UUID        string         `json:"uuid"`
		ExpireAt    int64          `json:"expire_at"`
		LimitBytes  int64          `json:"limit_bytes"`
		ClusterUUID string         `json:"cluster_uuid"`
		Sensors     map[string]int `json:"sensors"`
	}
}

// verify checks for fields with "mandatory" struc tag set to "true" and if
// the field does not have a value set will fail.
func (config *AppConfig) verify() error {
	values, fields := deepFields(config)

	for i := range fields {
		if !values[i].IsValid() {
			return errors.New("Invalid field")
		}

		if !values[i].CanSet() {
			return errors.New("Can't set field for " + fields[i].Name)
		}

		if tag := fields[i].Tag.Get("mandatory"); tag != "" {
			mandatory, err := strconv.ParseBool(tag)
			if err != nil {
				return errors.New("Invalid mandatory value " + tag)
			}

			if mandatory {
				switch values[i].Kind() {
				case reflect.String:
					if values[i].String() == "" {
						return errors.New("Field \"" + fields[i].Name + "\" must be provided")
					}

				case reflect.Int64:
					fallthrough
				case reflect.Int:
					if values[i].Int() == 0 {
						return errors.New("Field \"" + fields[i].Name + "\" must be provided")
					}

				case reflect.Uint:
					if values[i].Uint() == 0 {
						return errors.New("Field \"" + fields[i].Name + "\" must be provided")
					}
				}
			}
		}
	}

	return nil
}

// setDefaults looks for struct tags "default="<val>"" and set value of the fields
// to <val> if does not have a previous value.
func (config *AppConfig) setDefaults() error {
	values, fields := deepFields(config)

	for i := range fields {
		if !values[i].IsValid() {
			return errors.New("Invalid field")
		}

		if !values[i].CanSet() {
			return errors.New("Can't set field for " + fields[i].Name)
		}

		if tag := fields[i].Tag.Get("default"); tag != "" {
			setDefaultField(values[i], fields[i], tag)
		}
	}

	return nil
}

// ParseConfig parse a YAML formatted string and returns a AppConfig struct
// containing the parsed configuration.
func ParseConfig(raw []byte) (*AppConfig, error) {
	config := &AppConfig{}
	err := yaml.Unmarshal(raw, &config)
	if err != nil {
		return config, errors.New("Error: " + err.Error())
	}

	if err := config.setDefaults(); err != nil {
		logrus.Fatal("Error reading configuration: " + err.Error())
	}
	if err := config.verify(); err != nil {
		logrus.Fatal("Error reading configuration: " + err.Error())
	}

	return config, nil
}

func setDefaultField(v reflect.Value, t reflect.StructField, value string) {
	switch v.Kind().String() {
	case "int":
		if defaultValue, err := strconv.ParseInt(value, 10, 32); v.Int() == 0 && err == nil {
			logrus.Warnf("Defaulting \"%s\" to %d", t.Name, defaultValue)
			v.SetInt(defaultValue)
		}

	case "int64":
		if defaultValue, err := strconv.ParseInt(value, 10, 64); v.Int() == 0 && err == nil {
			logrus.Warnf("Defaulting \"%s\" to %d", t.Name, defaultValue)
			v.SetInt(defaultValue)
		}

	case "uint":
		if defaultValue, err := strconv.ParseUint(value, 10, 64); v.Uint() == 0 && err == nil {
			logrus.Warnf("Defaulting \"%s\" to %d", t.Name, defaultValue)
			v.SetUint(defaultValue)
		}

	case "string":
		if len(value) > 0 {
			logrus.Warnf("Defaulting \"%s\" to %s", t.Name, value)
			v.SetString(value)
		}

	default:
		logrus.Warnln(v.Kind().String())
	}
}

func deepFields(iface interface{}) ([]reflect.Value, []reflect.StructField) {
	values := make([]reflect.Value, 0)
	fields := make([]reflect.StructField, 0)

	elem := reflect.ValueOf(iface)
	for elem.Kind() == reflect.Ptr {
		elem = elem.Elem()
	}

	for i := 0; i < elem.NumField(); i++ {
		v := elem.Field(i)
		t := elem.Type().Field(i)

		switch v.Kind() {
		case reflect.Struct:
			nv, nf := deepFields(v.Addr().Interface())
			for _, value := range nv {
				values = append(values, value)
			}
			for _, field := range nf {
				fields = append(fields, field)
			}
		default:
			values = append(values, v)
			fields = append(fields, t)
		}
	}

	return values, fields
}
<commit_msg>:fire: Remove unused config<commit_after>// Simple utility for counting messages on a Kafka topic.
//
// Copyright (C) 2017 ENEO Tecnologia SL
// Author: Diego Fernández Barrera <bigomby@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package main

import (
	"errors"
	"reflect"
	"strconv"

	"github.com/Sirupsen/logrus"

	yaml "gopkg.in/yaml.v2"
)

// AppConfig contains the main application configuration.
type AppConfig struct {
	Counters struct {
		BatchTimeoutSeconds uint   `yaml:"batch_timeout_s" default:"5"`
		BatchMaxMessages    uint   `yaml:"batch_max_messages" default:"1000"`
		UUIDKey             string `yaml:"uuid_key"`
		Kafka               struct {
			ReadTopics      []string          `yaml:"read_topics" mandatory:"true"`
			WriteTopic      string            `yaml:"write_topic" mandatory:"true"`
			Attributes      map[string]string `yaml:"attributes"`
			TopicAttributes map[string]string `yaml:"topic_attributes"`
		}
	}

	Monitor struct {
		Timer struct {
			Period int64 `yaml:"period" default:"86400"`
			Offset int64 `yaml:"offset"`
		}
		Kafka struct {
			ReadTopics      []string          `yaml:"read_topics" mandatory:"true"`
			WriteTopic      string            `yaml:"write_topic" mandatory:"true"`
			Attributes      map[string]string `yaml:"attributes"`
			TopicAttributes map[string]string `yaml:"topic_attributes"`
		}
	}

	LicensesDirectory string `yaml:"licenses_directory" mandatory:"true"`
}

// verify checks for fields with "mandatory" struc tag set to "true" and if
// the field does not have a value set will fail.
func (config *AppConfig) verify() error {
	values, fields := deepFields(config)

	for i := range fields {
		if !values[i].IsValid() {
			return errors.New("Invalid field")
		}

		if !values[i].CanSet() {
			return errors.New("Can't set field for " + fields[i].Name)
		}

		if tag := fields[i].Tag.Get("mandatory"); tag != "" {
			mandatory, err := strconv.ParseBool(tag)
			if err != nil {
				return errors.New("Invalid mandatory value " + tag)
			}

			if mandatory {
				switch values[i].Kind() {
				case reflect.String:
					if values[i].String() == "" {
						return errors.New("Field \"" + fields[i].Name + "\" must be provided")
					}

				case reflect.Int64:
					fallthrough
				case reflect.Int:
					if values[i].Int() == 0 {
						return errors.New("Field \"" + fields[i].Name + "\" must be provided")
					}

				case reflect.Uint:
					if values[i].Uint() == 0 {
						return errors.New("Field \"" + fields[i].Name + "\" must be provided")
					}
				}
			}
		}
	}

	return nil
}

// setDefaults looks for struct tags "default="<val>"" and set value of the fields
// to <val> if does not have a previous value.
func (config *AppConfig) setDefaults() error {
	values, fields := deepFields(config)

	for i := range fields {
		if !values[i].IsValid() {
			return errors.New("Invalid field")
		}

		if !values[i].CanSet() {
			return errors.New("Can't set field for " + fields[i].Name)
		}

		if tag := fields[i].Tag.Get("default"); tag != "" {
			setDefaultField(values[i], fields[i], tag)
		}
	}

	return nil
}

// ParseConfig parse a YAML formatted string and returns a AppConfig struct
// containing the parsed configuration.
func ParseConfig(raw []byte) (*AppConfig, error) {
	config := &AppConfig{}
	err := yaml.Unmarshal(raw, &config)
	if err != nil {
		return config, errors.New("Error: " + err.Error())
	}

	if err := config.setDefaults(); err != nil {
		logrus.Fatal("Error reading configuration: " + err.Error())
	}
	if err := config.verify(); err != nil {
		logrus.Fatal("Error reading configuration: " + err.Error())
	}

	return config, nil
}

func setDefaultField(v reflect.Value, t reflect.StructField, value string) {
	switch v.Kind().String() {
	case "int":
		if defaultValue, err := strconv.ParseInt(value, 10, 32); v.Int() == 0 && err == nil {
			logrus.Warnf("Defaulting \"%s\" to %d", t.Name, defaultValue)
			v.SetInt(defaultValue)
		}

	case "int64":
		if defaultValue, err := strconv.ParseInt(value, 10, 64); v.Int() == 0 && err == nil {
			logrus.Warnf("Defaulting \"%s\" to %d", t.Name, defaultValue)
			v.SetInt(defaultValue)
		}

	case "uint":
		if defaultValue, err := strconv.ParseUint(value, 10, 64); v.Uint() == 0 && err == nil {
			logrus.Warnf("Defaulting \"%s\" to %d", t.Name, defaultValue)
			v.SetUint(defaultValue)
		}

	case "string":
		if len(value) > 0 {
			logrus.Warnf("Defaulting \"%s\" to %s", t.Name, value)
			v.SetString(value)
		}

	default:
		logrus.Warnln(v.Kind().String())
	}
}

func deepFields(iface interface{}) ([]reflect.Value, []reflect.StructField) {
	values := make([]reflect.Value, 0)
	fields := make([]reflect.StructField, 0)

	elem := reflect.ValueOf(iface)
	for elem.Kind() == reflect.Ptr {
		elem = elem.Elem()
	}

	for i := 0; i < elem.NumField(); i++ {
		v := elem.Field(i)
		t := elem.Type().Field(i)

		switch v.Kind() {
		case reflect.Struct:
			nv, nf := deepFields(v.Addr().Interface())
			for _, value := range nv {
				values = append(values, value)
			}
			for _, field := range nf {
				fields = append(fields, field)
			}
		default:
			values = append(values, v)
			fields = append(fields, t)
		}
	}

	return values, fields
}
<|endoftext|>


<commit_before>package cmd

import (
	"github.com/spf13/cobra"
	awscloud "github.com/wallix/awless/cloud/aws"
	"github.com/wallix/awless/scenario"
	"github.com/wallix/awless/scenario/driver"
	"github.com/wallix/awless/scenario/driver/aws"
)

func init() {
	createCmd.AddCommand(createInstanceCmd)

	RootCmd.AddCommand(createCmd)
}

var createCmd = &cobra.Command{
	Use:   "create",
	Short: "Create various type of resources by id: users, groups, instances, vpcs, ...",
}

var createInstanceCmd = &cobra.Command{
	Use:     "instance",
	Aliases: []string{"inst", "i"},
	Short:   "Create an instance",

	RunE: func(cmd *cobra.Command, args []string) error {
		raw := `CREATE VPC CIDR 10.0.0.0/16 REF vpc_1
CREATE SUBNET CIDR 10.0.0.0/16 REF subnet_1 REFERENCES vpc_1
CREATE INSTANCE COUNT 1 REFERENCES subnet_1
`
		lex := &scenario.Lexer{}
		scen := lex.ParseScenario(raw)

		runner := &driver.Runner{aws.NewAwsDriver(awscloud.InfraService)}

		return runner.Run(scen)
	},
}
<commit_msg>Simple instance creation through prompt/scenario<commit_after>package cmd

import (
	"bytes"
	"fmt"
	"strings"

	"github.com/spf13/cobra"
	awscloud "github.com/wallix/awless/cloud/aws"
	"github.com/wallix/awless/scenario"
	"github.com/wallix/awless/scenario/driver"
	"github.com/wallix/awless/scenario/driver/aws"
)

func init() {
	createCmd.AddCommand(createInstanceCmd)

	RootCmd.AddCommand(createCmd)
}

var createCmd = &cobra.Command{
	Use:   "create",
	Short: "Create various type of resources by id: users, groups, instances, vpcs, ...",
}

var createInstanceCmd = &cobra.Command{
	Use:     "instance",
	Aliases: []string{"inst", "i"},
	Short:   "Create an instance",

	RunE: func(cmd *cobra.Command, args []string) error {
		var buff bytes.Buffer

		buff.WriteString("CREATE INSTANCE")

		var count int
		fmt.Print("Number of instances? ")
		_, err := fmt.Scanln(&count)
		if err != nil {
			return err
		}
		buff.WriteString(fmt.Sprintf(" COUNT %d", count))

		types := []string{
			"t2.nano:   vCPU=1, CPU/hour=3, Mem Gio=0,5, EBS only",
			"t2.micro:  vCPU=1, CPU/hour=6, Mem Gio=1, EBS only",
			"t2.small:  vCPU=1, CPU/hour=12, Mem Gio=2, EBS only",
			"t2.medium: vCPU=2, CPU/hour=24, Mem Gio=4, EBS only",
			"t2.large:  vCPU=2, CPU/hour=36, Mem Gio=8, EBS only",
			"t2.xlarge: vCPU=4, CPU/hour=54, Mem Gio=16, EBS only",
			"t2.2xlarge: vCPU=8, CPU/hour=81, Mem Gio=32, EBS only",
		}

		var typ int
		fmt.Println()
		for index, typ := range types {
			fmt.Printf("%d. %s\n", index+1, typ)
		}
		fmt.Print("\nType of instance? ")

		_, err = fmt.Scanln(&typ)
		if err != nil {
			return err
		}

		mytype := strings.Split(types[typ], ":")[0]

		buff.WriteString(fmt.Sprintf(" TYPE %s", mytype))

		scen := buff.String()

		var yesorno string
		fmt.Print("\nDone\n\n")
		fmt.Print(scen)
		fmt.Print("\n\nAbout to run? (y/n): ")
		_, err = fmt.Scanln(&yesorno)
		if err != nil {
			return err
		}

		if strings.TrimSpace(yesorno) == "y" {
			lex := &scenario.Lexer{}
			scen := lex.ParseScenario(scen)

			runner := &driver.Runner{aws.NewAwsDriver(awscloud.InfraService)}

			return runner.Run(scen)
		}

		return nil
	},
}
<|endoftext|>