diff --git a/Makefile b/Makefile index f09e56f..1669773 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ COVER_DIR=cover # Package lists TOPLEVEL_PKG := jsh -CMD_LIST := jsh/ps +CMD_LIST := jsh/ps jsh/free jsh/ls # List building ALL_LIST = $(TOPLEVEL_PKG) $(CMD_LIST) diff --git a/common.go b/common.go index 9b7f21b..964addb 100644 --- a/common.go +++ b/common.go @@ -4,25 +4,27 @@ package jsh import ( "encoding/json" + "fmt" ) -type JshOutput struct { +type JshFrame struct { StdOut interface{} StdErr interface{} } // Size prefixes as integers, using binary representation type Unit int + const ( - B Unit = 2 ^ 0 + B Unit = 2 ^ 0 KB Unit = 2 ^ 10 MB Unit = 2 ^ 20 GB Unit = 2 ^ 30 TB Unit = 2 ^ 40 ) -// Converts a Jshoutput into a JSON string -func (j JshOutput) ToJson() *string { +// Converts a JshFrame into a JSON string +func (j JshFrame) ToJson() *string { jsonOut, err := json.Marshal(j) if err != nil { panic(err) @@ -30,3 +32,25 @@ func (j JshOutput) ToJson() *string { jsonString := string(jsonOut) return &jsonString } + +// goroutine for outputing frames. Pass it a channel of pointers to JshFrames, +// and it will send "true" to the done channel once you close the queue channel. +func OutputFrames(queue chan *JshFrame, done chan bool) { + fmt.Printf("[") + isFirst := true + for { + frame, more := <-queue + if more { + if !isFirst { + fmt.Printf(",") + } else { + isFirst = false + } + fmt.Printf(*frame.ToJson()) + } else { + fmt.Printf("]\n") + done <- true + return + } + } +} diff --git a/free.go b/free.go index 69a5a49..4760d7f 100644 --- a/free.go +++ b/free.go @@ -1,8 +1,8 @@ -/* File for free-related structs and methods */ - -package jsh - -type MemStat struct { - Stat int - Units Unit -} +/* File for free-related structs and methods */ + +package jsh + +type MemStat struct { + Stat int + Units Unit +} diff --git a/free/main.go b/free/main.go index b84ccd8..0849aef 100644 --- a/free/main.go +++ b/free/main.go @@ -1,93 +1,102 @@ -package main - -import ( - "bufio" - "errors" - "flag" - "fmt" - "jsh" - "regexp" - "os" - "strconv" -) - -func convertUnit(stringUnit string) (jsh.Unit, error) { - switch stringUnit { - case "B": - return jsh.B, nil - case "kB": - return jsh.KB, nil - case "mB": - return jsh.MB, nil - case "gB": - return jsh.GB, nil - case "tB": - return jsh.TB, nil - default: - return 0, errors.New(fmt.Sprintln("Unknown unit %s", stringUnit)) - } -} - -func parseLine(line string) (string, jsh.MemStat, error) { - // Recognizes a alphanumeric or () word, the : character, whitespace, the number we're looking for, - // more whitespace, and possibly the unit - lineRegex := regexp.MustCompile("(?P^[\\w()]+):\\s+(?P\\d+)(\\s+)?(?P\\w+)?") - - // Parse the line, and construct a map of the valid matches - parsedLine := lineRegex.FindStringSubmatch(line) - names := lineRegex.SubexpNames() - matchedVals := map[string]string{} - for i, n := range parsedLine { - matchedVals[names[i]] = n - } - - key := matchedVals["key"] - val, err := strconv.Atoi(matchedVals["val"]) - if err != nil { - return "", jsh.MemStat{}, err - } - unit, err := convertUnit(matchedVals["unit"]) - if err != nil { - return "", jsh.MemStat{}, err - } - return key, jsh.MemStat{val, unit}, nil -} - -func parseMemInfo() jsh.JshOutput { - file, err := os.Open("/proc/meminfo") - if err != nil { - return jsh.JshOutput{[]string{}, []error{err}} - } - defer file.Close() - - scanner := bufio.NewScanner(file) - memInfo := make(map[string]jsh.MemStat) - errors := []error{} - // Read in each line of the meminfo file, and place it in the map - for scanner.Scan() { - key, val, err := parseLine(scanner.Text()) - if err != nil { - errors = append(errors, err) - } - memInfo[key] = val - } - - finalOut := jsh.JshOutput{memInfo, errors} - return finalOut -} - -func runJsonMode() { - output := parseMemInfo() - fmt.Println("%s", *output.ToJson()) -} - -func main() { - jsonModePtr := flag.Bool("json", false, "whether to use json mode for output") - flag.Parse() - - if !*jsonModePtr { - fmt.Printf("%s", jsh.Fallback("/usr/bin/free")) - } else { - runJsonMode() - } -} +package main + +import ( + "bufio" + "errors" + "flag" + "fmt" + "jsh" + "os" + "regexp" + "strconv" +) + +func convertUnit(stringUnit string) (jsh.Unit, error) { + switch stringUnit { + case "B": + return jsh.B, nil + case "kB": + return jsh.KB, nil + case "mB": + return jsh.MB, nil + case "gB": + return jsh.GB, nil + case "tB": + return jsh.TB, nil + case "": + return jsh.B, nil + default: + return 0, errors.New(fmt.Sprintf("Unknown unit: %s\n", stringUnit)) + } +} + +func parseLine(line string) (string, jsh.MemStat, error) { + // Recognizes a alphanumeric or () word, the : character, whitespace, the number we're looking for, + // more whitespace, and possibly the unit + lineRegex := regexp.MustCompile("(?P^[\\w()]+):\\s+(?P\\d+)(\\s+)?(?P\\w+)?") + + // Parse the line, and construct a map of the valid matches + parsedLine := lineRegex.FindStringSubmatch(line) + names := lineRegex.SubexpNames() + matchedVals := map[string]string{} + for i, n := range parsedLine { + matchedVals[names[i]] = n + } + + key := matchedVals["key"] + val, err := strconv.Atoi(matchedVals["val"]) + if err != nil { + return "", jsh.MemStat{}, err + } + unit, err := convertUnit(matchedVals["unit"]) + if err != nil { + return "", jsh.MemStat{}, err + } + return key, jsh.MemStat{val, unit}, nil +} + +func parseMemInfo() jsh.JshFrame { + file, err := os.Open("/proc/meminfo") + if err != nil { + return jsh.JshFrame{[]string{}, []error{err}} + } + defer file.Close() + + scanner := bufio.NewScanner(file) + memInfo := make(map[string]jsh.MemStat) + errors := []error{} + // Read in each line of the meminfo file, and place it in the map + for scanner.Scan() { + key, val, err := parseLine(scanner.Text()) + if err != nil { + errors = append(errors, err) + } + memInfo[key] = val + } + + // Wrap with array + stdOut := []map[string]jsh.MemStat{memInfo} + finalOut := jsh.JshFrame{stdOut, errors} + return finalOut +} + +func runJsonMode() { + output := parseMemInfo() + queue := make(chan *jsh.JshFrame) + done := make(chan bool) + go jsh.OutputFrames(queue, done) + queue <- &output + close(queue) + <-done +} + +func main() { + jsonModePtr := flag.Bool("json", false, "whether to use json mode for output") + flag.Parse() + + if !*jsonModePtr { + fmt.Printf("%s", jsh.Fallback("/usr/bin/free")) + } else { + runJsonMode() + } +} diff --git a/ls/main.go b/ls/main.go index 42cadd1..954bd0e 100644 --- a/ls/main.go +++ b/ls/main.go @@ -1,76 +1,75 @@ package main import ( - "io/ioutil" - "fmt" - "flag" - "log" - "syscall" - "strconv" + "flag" + "fmt" + "io/ioutil" + "log" + "strconv" + "syscall" ) - -func get_fileinfo(f string, size bool, mode bool, inode bool) string{ - var stat syscall.Stat_t - var ret string - ret = "{\"name\":\"" + f + "\"" - if err := syscall.Stat(f, &stat); err != nil { - log.Fatal(err) - } - if(size){ - ret = ret + ", \"size\":" + strconv.FormatInt(stat.Size,10) - } - if(mode){ - ret = ret + ", \"mode\":" + strconv.Itoa(int(stat.Mode)) - } - if(inode){ - ret = ret + ", \"inode\":" + strconv.FormatUint(stat.Ino,10) - } - ret = ret + "}" - return ret +func get_fileinfo(f string, size bool, mode bool, inode bool) string { + var stat syscall.Stat_t + var ret string + ret = "{\"files\": [{\"name\":\"" + f + "\"" + if err := syscall.Stat(f, &stat); err != nil { + log.Fatal(err) + } + if size { + ret = ret + ", \"size\":" + strconv.FormatInt(stat.Size, 10) + } + if mode { + ret = ret + ", \"mode\":" + strconv.Itoa(int(stat.Mode)) + } + if inode { + ret = ret + ", \"inode\":" + strconv.FormatUint(stat.Ino, 10) + } + ret = ret + "}]}" + return ret } func main() { - // here be the ls flags + // here be the ls flags - var a_flag bool // all files, even ones starting with . - var mode_flag bool // flags - var inode_flag bool // inode - var size_flag bool // size - var first = true + var a_flag bool // all files, even ones starting with . + var mode_flag bool // flags + var inode_flag bool // inode + var size_flag bool // size + var first = true - flag.BoolVar(&a_flag, "a", false, "lists all files in directory, even hidden ones") - flag.BoolVar(&mode_flag, "f", false, "include flags for file") - flag.BoolVar(&inode_flag, "i", false, "include flags for file") - flag.BoolVar(&size_flag, "s", false, "include flags for file") - // end ls flag + flag.BoolVar(&a_flag, "a", false, "lists all files in directory, even hidden ones") + flag.BoolVar(&mode_flag, "f", false, "include flags for file") + flag.BoolVar(&inode_flag, "i", false, "include flags for file") + flag.BoolVar(&size_flag, "s", false, "include flags for file") + // end ls flag - flag.Parse() - root := "."//flag.Arg(0) - dir,_ := ioutil.ReadDir(root) + flag.Parse() + root := "." //flag.Arg(0) + dir, _ := ioutil.ReadDir(root) - fmt.Printf("[\n") - - if(!a_flag){ - for _,entry := range dir{ - if(entry.Name()[0]!='.'){ - if(!first){ - fmt.Printf(",") - }else{ - first = false + fmt.Printf("[{\"StdOut\": [\n") + + if !a_flag { + for _, entry := range dir { + if entry.Name()[0] != '.' { + if !first { + fmt.Printf(",") + } else { + first = false + } + fmt.Printf("%s\n", get_fileinfo(entry.Name(), size_flag, mode_flag, inode_flag)) + } + } + } else { + for _, entry := range dir { + if !first { + fmt.Printf(",") + } else { + first = false + } + fmt.Printf("%s\n", get_fileinfo(entry.Name(), size_flag, mode_flag, inode_flag)) + } } - fmt.Printf("%s\n", get_fileinfo(entry.Name(), size_flag, mode_flag, inode_flag)) - } - } - }else{ - for _,entry := range dir{ - if(!first){ - fmt.Printf(",") - }else{ - first = false - } - fmt.Printf("%s\n", get_fileinfo(entry.Name(), size_flag, mode_flag, inode_flag)) - } - } - fmt.Printf("]\n") + fmt.Printf("], \"StdErr\": []}]\n") } diff --git a/ps/main.go b/ps/main.go index 8d8d181..9df6a7b 100644 --- a/ps/main.go +++ b/ps/main.go @@ -25,11 +25,16 @@ func PsOutputToProcesses(out string) *[]jsh.Process { func runJsonMode() { // Run procps-ng "ps" with full output - psOut := string(*jsh.FallbackWithArgs("/usr/bin/ps", []string{"auxww"})) - + psOut := string(*jsh.FallbackWithArgs("/usr/bin/ps", []string{"auxww"})) processesPtr := PsOutputToProcesses(psOut) - finalOut := jsh.JshOutput{*processesPtr, []string{}} - fmt.Printf("%s", *finalOut.ToJson()) + frame := jsh.JshFrame{*processesPtr, []string{}} + + queue := make(chan *jsh.JshFrame) + done := make(chan bool) + go jsh.OutputFrames(queue, done) + queue <- &frame + close(queue) + <-done } func main() { diff --git a/utils.go b/utils.go index 87d21f9..7f6561e 100644 --- a/utils.go +++ b/utils.go @@ -2,9 +2,9 @@ package jsh import ( "math" - "unicode" "os" "os/exec" + "unicode" ) // FieldsN slices s into substrings after each instance of a whitespace @@ -56,7 +56,7 @@ func FallbackWithArgs(program string, args []string) *[]byte { if err != nil { panic(err) } - return &out + return &out } func Fallback(program string) *[]byte {