package bash import ( "fmt" "runtime" "sort" "strings" ) type shellState struct { cwd string username string hostname string fs *filesystem } type commandResult struct { output string exit bool } func dispatch(state *shellState, line string) commandResult { fields := strings.Fields(line) if len(fields) == 0 { return commandResult{} } cmd := fields[0] args := fields[1:] switch cmd { case "pwd": return commandResult{output: state.cwd} case "whoami": return commandResult{output: state.username} case "hostname": return commandResult{output: state.hostname} case "id": return cmdID(state) case "uname": return cmdUname(state, args) case "ls": return cmdLs(state, args) case "cd": return cmdCd(state, args) case "cat": return cmdCat(state, args) case "exit", "logout": return commandResult{exit: true} default: return commandResult{output: fmt.Sprintf("%s: command not found", cmd)} } } func cmdID(state *shellState) commandResult { return commandResult{ output: fmt.Sprintf("uid=0(%s) gid=0(%s) groups=0(%s)", state.username, state.username, state.username), } } func cmdUname(state *shellState, args []string) commandResult { if len(args) > 0 && args[0] == "-a" { return commandResult{ output: fmt.Sprintf("Linux %s 5.15.0-89-generic #99-Ubuntu SMP Mon Oct 30 20:42:41 UTC 2023 %s GNU/Linux", state.hostname, runtime.GOARCH), } } return commandResult{output: "Linux"} } func cmdLs(state *shellState, args []string) commandResult { target := state.cwd if len(args) > 0 { target = resolvePath(state.cwd, args[0]) } names, err := state.fs.list(target) if err != nil { return commandResult{output: err.Error()} } sort.Strings(names) return commandResult{output: strings.Join(names, " ")} } func cmdCd(state *shellState, args []string) commandResult { target := "/root" if len(args) > 0 { target = resolvePath(state.cwd, args[0]) } if !state.fs.exists(target) { return commandResult{output: fmt.Sprintf("bash: cd: %s: No such file or directory", args[0])} } if !state.fs.isDirectory(target) { return commandResult{output: fmt.Sprintf("bash: cd: %s: Not a directory", args[0])} } state.cwd = target return commandResult{} } func cmdCat(state *shellState, args []string) commandResult { if len(args) == 0 { return commandResult{} } var parts []string for _, arg := range args { p := resolvePath(state.cwd, arg) content, err := state.fs.read(p) if err != nil { parts = append(parts, err.Error()) } else { parts = append(parts, strings.TrimRight(content, "\n")) } } return commandResult{output: strings.Join(parts, "\n")} }