I’m stuck on Stage #JV1.
Here are my logs:
[your-program] $ ls -1 /tmp/bar > /tmp/baz/bar.md
[your-program] $ cat /tmp/baz/bar.md
[your-program] grape
[your-program] pear
[your-program] raspberry
[tester::#JV1] ✓ Received redirected file content
[your-program] $ echo 'Hello James' 1> /tmp/baz/foo.md
[your-program] $ cat /tmp/baz/foo.md
[your-program] Hello James
[tester::#JV1] ✓ Received redirected file content
[your-program] $ cat /tmp/bar/pear nonexistent 1> /tmp/baz/quz.md
[your-program] cat: nonexistent: No such file or directory
[your-program] cat: command not found
[tester::#JV1] Expected prompt ("$ ") but received "cat: command not found"
[your-program] $
[tester::#JV1] Assertion failed.
[tester::#JV1] Test failed
Code worked on my machine, didn’t work on the server
package main
import (
"bufio"
"fmt"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
)
var Handlers = make(map[string]func(args []string) error)
var input *os.File = os.Stdin
var output *os.File = os.Stdout
var errors *os.File = os.Stderr
func handleExit(args []string) error {
var (
exitCode int
err error
)
if len(args) == 1 {
exitCode, err = strconv.Atoi(args[0])
if err != nil {
return err
}
}
os.Exit(exitCode)
return nil
}
func locateCmd(cmd string) (string, bool) {
paths := strings.Split(os.Getenv("PATH"), string(os.PathListSeparator))
for _, path := range paths {
fp := filepath.Join(path, cmd)
if _, err := os.Stat(fp); err == nil {
return fp, true
}
}
return "", false
}
func handleEcho(args []string) error {
if len(args) == 0 {
fmt.Fprintln(output)
return nil
}
for i := 0; i < len(args)-1; i++ {
fmt.Fprintf(output, "%s ", args[i])
}
fmt.Fprintln(output, args[len(args)-1])
return nil
}
func handleType(args []string) error {
if len(args) != 1 {
return nil
}
cmd := args[0]
if _, ok := Handlers[cmd]; ok {
fmt.Fprintf(output, "%s is a shell builtin\n", cmd)
return nil
}
if path, ok := locateCmd(cmd); ok {
fmt.Fprintf(output, "%s is %s\n", cmd, path)
return nil
}
fmt.Fprintf(os.Stderr, "%s: not found\n", cmd)
return nil
}
func handleFileOpening(name string, flag int, perm os.FileMode, def *os.File) *os.File {
file, err := os.OpenFile(name, flag, perm)
if err == nil {
return file
} else {
fmt.Fprintf(errors, "Error opening output file: %v\n", err)
return def
}
}
/*
Uncomment this function and it's mapping in main function to make it work
on machines which don't have cat installed
*/
// func handleCat(args []string) error {
// result := ""
// for i := 0; i < len(args); i++ {
// data, err := os.ReadFile(args[i])
// // fmt.Fprintln(args[i])
// if err != nil {
// fmt.Fprintln(errors, "Error reading file:", args[i])
// continue
// }
// result = result + string(data)
// }
// fmt.Fprint(output, result+"\n")
// output.Sync()
// return nil
// }
func parseInput(cmd string) []string {
var parts []string
var currentString string = ""
var isSingleQuoted bool = false
var isDoubleQuoted bool = false
for i := 0; i < len(cmd); i++ {
if !isDoubleQuoted && !isSingleQuoted && cmd[i] == '\\' {
if i+1 < len(cmd) {
currentString += string(cmd[i+1])
}
i++
continue
} else if isSingleQuoted {
if cmd[i] == '\'' {
isSingleQuoted = false
} else {
currentString += string(cmd[i])
}
continue
} else if isDoubleQuoted {
if cmd[i] == '"' {
isDoubleQuoted = false
} else {
if cmd[i] == '\\' {
if i+1 < len(cmd) && (cmd[i+1] == '\\' || cmd[i+1] == '$' || cmd[i+1] == '"') {
currentString += string(cmd[i+1])
i++
continue
}
}
currentString += string(cmd[i])
}
continue
} else if cmd[i] == ' ' {
if len(currentString) > 0 {
parts = append(parts, currentString)
currentString = ""
}
continue
}
if cmd[i] == '\'' {
isSingleQuoted = true
} else if cmd[i] == '"' {
isDoubleQuoted = true
} else {
currentString += string(cmd[i])
}
}
if len(currentString) > 0 {
parts = append(parts, currentString)
}
return parts
}
func main() {
Handlers["exit"] = handleExit
Handlers["echo"] = handleEcho
Handlers["type"] = handleType
// Handlers["cat"] = handleCat
for {
fmt.Fprint(output, "$ ")
cmd, err := bufio.NewReader(input).ReadString('\n')
if err != nil {
fmt.Fprintln(errors, "Error Reading Input")
os.Exit(-1)
}
cmd = strings.Trim(cmd, "\r\n")
parts := parseInput(cmd)
cmd = parts[0]
var args []string
if len(parts) > 1 {
args = parts[1:]
}
// fmt.Println(args)
for idx := 0; idx < len(args); idx++ {
// fmt.Println(idx, args[idx])
if idx+1 == len(args) {
break
}
var isUsed bool = true
switch args[idx] {
case ">", "1>":
output = handleFileOpening(args[idx+1], os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666, os.Stdout)
case ">>", "1>>":
output = handleFileOpening(args[idx+1], os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666, os.Stdout)
case "2>":
errors = handleFileOpening(args[idx+1], os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666, os.Stderr)
case "2>>":
errors = handleFileOpening(args[idx+1], os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666, os.Stderr)
default:
isUsed = false
}
if isUsed {
args = append(args[:idx], args[idx+2:]...)
idx--
}
}
// fmt.Println(args)
if fn, ok := Handlers[cmd]; ok {
err = fn(args)
if err != nil {
fmt.Fprintln(errors, err)
}
} else if _, ok := locateCmd(cmd); ok {
command := exec.Command(cmd, args...)
command.Stdout = output
command.Stderr = errors
err := command.Run()
if err != nil {
fmt.Fprintln(errors, cmd+": command not found")
}
} else {
fmt.Fprintf(errors, "%s: command not found\n", cmd)
}
if output != nil && output != os.Stdout {
output.Close()
}
if errors != nil && errors != os.Stderr {
errors.Close()
}
output = os.Stdout
errors = os.Stderr
}
}