commit f6140a1fc394d938167d75b77630617ab2da03e8 Author: JISAUAY Date: Tue Jun 24 16:02:09 2025 -0500 init diff --git a/cmd/cat.go b/cmd/cat.go new file mode 100644 index 0000000..a1b91f8 --- /dev/null +++ b/cmd/cat.go @@ -0,0 +1,18 @@ +package cmd + +import ( + "gosh/types" + "os" + + "github.com/fatih/color" +) + +func GoshCat(sh *types.Shell, filename string) types.CmdOutput { + data, err := os.ReadFile(filename) + red := color.New(color.FgRed).SprintFunc() + if err != nil { + return types.CmdOutput{Id: 1, Output: red(err)} + } + + return types.CmdOutput{Id: 0, Output: string(data)} +} diff --git a/cmd/cd.go b/cmd/cd.go new file mode 100644 index 0000000..b8faa46 --- /dev/null +++ b/cmd/cd.go @@ -0,0 +1,22 @@ +package cmd + +import ( + "os" + + "gosh/types" + + "github.com/fatih/color" +) + +func GoshCd(sh *types.Shell, dir string) types.CmdOutput { + err := os.Chdir(dir) + red := color.New(color.FgRed).SprintFunc() + if err != nil { + return types.CmdOutput{Id: 1, Output: red(err)} + } + + dir, _ = os.Getwd() + sh.Cd = dir + + return types.CmdOutput{Id: 0, Output: ""} +} diff --git a/cmd/clear.go b/cmd/clear.go new file mode 100644 index 0000000..59eede8 --- /dev/null +++ b/cmd/clear.go @@ -0,0 +1,7 @@ +package cmd + +import "fmt" + +func GoshClear() { + fmt.Print("\033[H\033[2J") +} diff --git a/cmd/grep.go b/cmd/grep.go new file mode 100644 index 0000000..b87291b --- /dev/null +++ b/cmd/grep.go @@ -0,0 +1,60 @@ +package cmd + +import ( + "gosh/types" + "regexp" + "strconv" + "strings" + + "github.com/fatih/color" +) + +func GoshGrep(sh *types.Shell, args map[string]string, input string, regex string) types.CmdOutput { + re := regexp.MustCompile(regex) + lines := strings.Split(input, "\n") + + red := color.New(color.FgRed).SprintFunc() + + value, ok := args["-C"] + var err error + context_lines := 0 + + if ok { + context_lines, err = strconv.Atoi(value) + if err != nil { + return types.CmdOutput{Id: 1, Output: red(err)} + } + context_lines++ + } + + var output strings.Builder + for i, line := range lines { + if re.MatchString(line) { + // Print behind + if context_lines != 0 { + for bi := i - context_lines; bi < i; bi++ { + if bi < 0 { + continue + } else { + output.WriteString(lines[bi] + "\n") + } + } + } + + output.WriteString(line + "\n") + + // Print after + if context_lines != 0 { + for ai := i + 1; ai < i+context_lines; ai++ { + if ai > len(lines)-1 { + continue + } else { + output.WriteString(lines[ai] + "\n") + } + } + } + } + } + + return types.CmdOutput{Id: 0, Output: output.String()} +} diff --git a/cmd/ls.go b/cmd/ls.go new file mode 100644 index 0000000..17dcd93 --- /dev/null +++ b/cmd/ls.go @@ -0,0 +1,28 @@ +package cmd + +import ( + "gosh/types" + "os" + "strings" + + "github.com/fatih/color" +) + +func GoshLs(sh *types.Shell) types.CmdOutput { + children, err := os.ReadDir(sh.Cd) + red := color.New(color.FgRed).SprintFunc() + if err != nil { + return types.CmdOutput{Id: 1, Output: red(err)} + } + + var output strings.Builder + for _, child := range children { + if child.IsDir() { + output.WriteString(color.CyanString(child.Name()+"/") + "\n") + } else { + output.WriteString(child.Name() + "\n") + } + } + + return types.CmdOutput{Id: 0, Output: output.String()} +} diff --git a/cmd/pwd.go b/cmd/pwd.go new file mode 100644 index 0000000..b816068 --- /dev/null +++ b/cmd/pwd.go @@ -0,0 +1,9 @@ +package cmd + +import ( + "gosh/types" +) + +func GoshPwd(sh *types.Shell) types.CmdOutput { + return types.CmdOutput{Id: 0, Output: sh.Cd} +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..60be4cd --- /dev/null +++ b/go.mod @@ -0,0 +1,10 @@ +module gosh + +go 1.21.5 + +require ( + github.com/fatih/color v1.18.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + golang.org/x/sys v0.25.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..33148a4 --- /dev/null +++ b/go.sum @@ -0,0 +1,11 @@ +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/main.go b/main.go new file mode 100644 index 0000000..3a7ec1f --- /dev/null +++ b/main.go @@ -0,0 +1,126 @@ +package main + +import ( + "bufio" + "fmt" + "gosh/cmd" + "gosh/types" + "os" + "strings" + + "github.com/fatih/color" +) + +func gosh_print_output(cmdo types.CmdOutput) { + switch cmdo.Id { + case 0: + fmt.Printf(cmdo.Output) + case 1: + fmt.Printf("Error: %s\n", cmdo.Output) + } +} + +func parse_flags(parts []string) (map[string]string, []string) { + flags := make(map[string]string) + args := []string{} + i := 0 + for i < len(parts) { + if strings.HasPrefix(parts[i], "-") { + key := parts[i] + val := "" + if i+1 < len(parts) && !strings.HasPrefix(parts[i+1], "-") { + val = parts[i+1] + i++ + } + flags[key] = val + } else { + args = append(args, parts[i]) + } + i++ + } + return flags, args +} + +func gosh_process_input(sh *types.Shell, input string) { + // Split by pipe and trim spaces + commands := strings.Split(input, "|") + var prevOutput types.CmdOutput + + for _, cmdStr := range commands { + cmdStr = strings.TrimSpace(cmdStr) + parts := strings.Fields(cmdStr) + if len(parts) == 0 { + continue + } + + switch parts[0] { + case "pwd": + prevOutput = cmd.GoshPwd(sh) + case "cd": + if len(parts) < 2 { + fmt.Println("Usage: cd ") + return + } + prevOutput = cmd.GoshCd(sh, parts[1]) + case "ls": + prevOutput = cmd.GoshLs(sh) + case "cat": + if len(parts) < 2 { + fmt.Println("Usage: cat ") + return + } + prevOutput = cmd.GoshCat(sh, parts[1]) + case "grep": + if len(parts) < 2 { + fmt.Println("Usage: grep ") + return + } + + flags, args := parse_flags(parts) + + // Feed previous output to grep + prevOutput = cmd.GoshGrep(sh, flags, prevOutput.Output, args[len(args)-1]) + case "clear": + cmd.GoshClear() + default: + fmt.Println("Unknown command: ", parts[0]) + return + } + } + + gosh_print_output(prevOutput) +} + +func main() { + reader := bufio.NewReader(os.Stdin) + + c := color.New(color.FgCyan) + + dir, _ := os.Getwd() + sh := types.Shell{Cd: dir} + + var should_exit = false + for { + c.Print(sh.Cd) + fmt.Print("> ") + + input, err := reader.ReadString('\n') + + if err != nil { + fmt.Println("Error reading input: ", err) + } + + input = strings.TrimSpace(input) + + switch input { + case "exit": + should_exit = true + default: + gosh_process_input(&sh, input) + } + + if should_exit { + break + } + } +} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..5aa7522 --- /dev/null +++ b/readme.md @@ -0,0 +1,7 @@ +# GoLSh + +Go Lang Shell + +## Commands + +`cd`, `ls`, `pwd`, `grep`, `cat`, `clear` diff --git a/types/shell.go b/types/shell.go new file mode 100644 index 0000000..8ddba9e --- /dev/null +++ b/types/shell.go @@ -0,0 +1,10 @@ +package types + +type Shell struct { + Cd string +} + +type CmdOutput struct { + Id int + Output string +}