commit 15eeadf5d34275f44bc4759eba657b250394eb0a Author: Tobias P.L Wennberg Date: Fri Jan 10 13:29:56 2025 +0100 initial diff --git a/command/cat/cat.go b/command/cat/cat.go new file mode 100644 index 0000000..04bb674 --- /dev/null +++ b/command/cat/cat.go @@ -0,0 +1,22 @@ +package cat + +import ( + "bbash/environment" + "bbash/input_parser" + "fmt" + "os" + "path/filepath" +) + +func Cat(in input_parser.Input, env *environment.Env) { + file := filepath.Join(env.Path, in.Args) + bytea_str, err := os.ReadFile(file) + if err != nil { + fmt.Print(fmt.Sprintf("Error opening file: %s", err.Error())) + return + } + fmt.Print(string(bytea_str)) + +} + + diff --git a/command/cd/cd.go b/command/cd/cd.go new file mode 100644 index 0000000..945af2d --- /dev/null +++ b/command/cd/cd.go @@ -0,0 +1,34 @@ +package cd + +import ( + "bbash/environment" + "bbash/input_parser" + "fmt" + "os" + "path/filepath" +) + +func Cd(in input_parser.Input, env *environment.Env) { + var new_path string + if in.Args == "" { + new_path = env.Env["HOME"] + } else { + if in.Args[0] == '/' { + new_path = in.Args + } else { + new_path = filepath.Join(env.Path, in.Args) + } + } + file, err := os.Stat(new_path) + if err != nil { + fmt.Print(fmt.Sprintf("Error changing directory %s", err.Error())) + return + } + if !file.IsDir() { + fmt.Print("New path is not a directory") + return + } + env.Path = new_path +} + + diff --git a/command/command.go b/command/command.go new file mode 100644 index 0000000..f78dd68 --- /dev/null +++ b/command/command.go @@ -0,0 +1,89 @@ +package command + +import ( + "bbash/command/cat" + "bbash/command/cd" + "bbash/command/cp" + "bbash/command/echo" + "bbash/command/head" + "bbash/command/help" + "bbash/command/ls" + "bbash/command/man" + "bbash/command/mv" + "bbash/command/pwd" + "bbash/command/rm" + "bbash/command/touch" + "bbash/environment" + "bbash/input_parser" + "fmt" + "os" + "strings" +) +var path_command []string + +func Init(env *environment.Env) { + init_path(env) + +} + +func Run_command(in input_parser.Input, env *environment.Env) { + switch in.Instruction { + case "pwd": pwd.Pwd(in, env) + case "echo": echo.Echo(in, env) + case "ls": ls.Ls(in, env) + case "cd": cd.Cd(in, env) + case "man": man.Man(in, env) + case "cat": cat.Cat(in, env) + case "head": head.Head(in, env) + case "touch": touch.Touch(in, env) + case "rm": rm.Rm(in, env) + case "cp": cp.Cp(in, env) + case "mv": mv.Mv(in,env) + case "help": help.Help(in,env) + default: + if !run_by_path(in, env) { + fmt.Println(fmt.Sprintf("No such command! (%s)", in.Instruction)) + } + } +} +// Returns if anything was run or not +func run_by_path(in input_parser.Input, env *environment.Env) bool { + fmt.Print(env.Env["PATH"]) + return false +} + +func init_path(env *environment.Env) { + path := strings.Split(env.Env["PATH"], ":") + for _, a_path := range path { + recursive_executable_finder(a_path) + + } + +} +func recursive_executable_finder(path string) { + dir, err := os.ReadDir(path) + if err != nil { + return + } + for _, f := range dir { + if f.IsDir() { + recursive_executable_finder(f.Name()) + continue + } + if !is_executable(f.Name()) { + println("not exec") + continue + } + println(f.Name()) + path_command = append(path_command, f.Name()) + + } +} +func is_executable( name string) bool { + return true + stat, _ := os.Stat(name) + mode := stat.Mode() + return mode&(1<<0) != 0 || // Others' execute bit + mode&(1<<3) != 0 || // Group's execute bit + mode.Perm()&(1<<6) != 0 // Owner's execute bit +} diff --git a/command/command_test.go b/command/command_test.go new file mode 100644 index 0000000..78daa6d --- /dev/null +++ b/command/command_test.go @@ -0,0 +1,27 @@ +package command + +import( + "io/fs" + "testing" +) + +func Test_is_executable(t *testing.T) { + tests := []struct { + name string // description of this test case + // Named input parameters for target function. + mode fs.FileMode + want bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := is_executable(tt.mode) + // TODO: update the condition below to compare got with tt.want. + if true { + t.Errorf("is_executable() = %v, want %v", got, tt.want) + } + }) + } +} + diff --git a/command/cp/cp.go b/command/cp/cp.go new file mode 100644 index 0000000..7df86d7 --- /dev/null +++ b/command/cp/cp.go @@ -0,0 +1,36 @@ +package cp + +import ( + "bbash/environment" + "bbash/input_parser" + "fmt" + "os" + "path/filepath" + "strings" +) + +func Cp(in input_parser.Input, env *environment.Env) { + args := strings.SplitN(in.Args, " ", 2) + if len(args) != 2 { + fmt.Print("No right amount of args") + return + } + file_path_source := filepath.Join(env.Path, args[0]) + file_path_dest := filepath.Join(env.Path, args[1]) + source, err := os.ReadFile(file_path_source) + if err != nil { + fmt.Println(fmt.Sprintf("Error opening file: %s", err.Error())) + return + } + dest, err := os.Create(file_path_dest) + defer dest.Close() + if err != nil { + fmt.Println(fmt.Sprintf("Error opening file: %s", err.Error())) + return + } + dest.Write(source) + +} + + + diff --git a/command/echo/echo.go b/command/echo/echo.go new file mode 100644 index 0000000..a13453f --- /dev/null +++ b/command/echo/echo.go @@ -0,0 +1,12 @@ +package echo + +import ( + "bbash/input_parser" + "fmt" + "bbash/environment" +) + +func Echo(in input_parser.Input, _ *environment.Env) { + fmt.Print(in.Args) +} + diff --git a/command/head/head.go b/command/head/head.go new file mode 100644 index 0000000..68b951b --- /dev/null +++ b/command/head/head.go @@ -0,0 +1,50 @@ +package head + +import ( + "bbash/environment" + "bbash/input_parser" + "bufio" + "fmt" + "os" + "strconv" + "strings" +) + +func Head(in input_parser.Input, env *environment.Env) { + args := strings.SplitN(in.Args, " ", 2) + var file_path string + var lines int + if len(args) >= 1 { + file_path = strings.TrimSpace(args[0]) + lines = 10 + } else { + fmt.Print(fmt.Sprintf("No arguments provided")) + return + } + if len(args) == 2 { + lines1, err := strconv.Atoi(strings.TrimSpace(args[1])) + lines = lines1 + if err != nil { + fmt.Print(fmt.Sprintf("Second argument must be a integer")) + return + } + } + file, err := os.Open(file_path) + if err != nil { + fmt.Println(fmt.Sprintf("Error opening file: %s", err.Error())) + return + } + defer file.Close() + scanner := bufio.NewScanner(file) + for i := 0; i < lines-1; i++ { + if(!scanner.Scan()) { + return + } + fmt.Println(scanner.Text()) + } + scanner.Scan() + fmt.Print(scanner.Text()) +} + + + diff --git a/command/help/help.go b/command/help/help.go new file mode 100644 index 0000000..9cc0d9d --- /dev/null +++ b/command/help/help.go @@ -0,0 +1,28 @@ +package help + +import ( + "bbash/input_parser" + "fmt" + "bbash/environment" +) + +func Help(in input_parser.Input, _ *environment.Env) { + fmt.Print(` + Welcome to the bornagain bornagain shell! + The options are: +pwd: Output current working directory +echo: Echoes the input argument +ls: List Sources in the current working directory +cd: Change to directory given in the argument +man: Manual for the terminal options +cat: Outputs the contents of the given file +head: Outputs first lines of a given file. 10 if non provided. Argument +touch: Creates the given file +rm: Removes the given file +cp: Copies the given file. Argument: +mv: Moves a given file. Argument: +help: Prints this message + `) +} + + diff --git a/command/ls/ls.go b/command/ls/ls.go new file mode 100644 index 0000000..8a1a26d --- /dev/null +++ b/command/ls/ls.go @@ -0,0 +1,21 @@ +package ls + +import ( + "bbash/environment" + "bbash/input_parser" + "fmt" + "os" +) + +func Ls(in input_parser.Input, env *environment.Env) { + fs, err := os.ReadDir(env.Path) + if err != nil { + fmt.Print(fmt.Sprintf("Error opening directory %s", env.Path)) + } + for _, f := range fs { + fmt.Print(f.Name()) + fmt.Println() + } +} + + diff --git a/command/man/man.go b/command/man/man.go new file mode 100644 index 0000000..02d8a18 --- /dev/null +++ b/command/man/man.go @@ -0,0 +1,37 @@ +package man + +import ( + "bbash/environment" + "bbash/input_parser" + "fmt" +) + +func Man(in input_parser.Input, env *environment.Env) { + switch in.Instruction { + case "pwd": + fmt.Print("Output current working directory") + case "echo": + fmt.Print("Echoes the input argument") + case "ls": + fmt.Print("List Sources in the current working directory") + case "cd": + fmt.Print("Change to directory given in the argument") + case "man": + fmt.Print("Manual for the terminal options") + case "cat": + fmt.Print("Outputs the contents of the given file") + case "head": + fmt.Print("Outputs first lines of a given file. 10 if non provided. Argument ") + case "touch": + fmt.Print("Creates the given file") + case "rm": + fmt.Print("Removes the given file") + case "cp": + fmt.Print("Copies the given file. Argument: ") + case "mv": + fmt.Print("Moves a given file. Argument: ") + default: fmt.Println(fmt.Sprintf("No such command! (%s)", in.Instruction)) + } +} + + diff --git a/command/mv/mv.go b/command/mv/mv.go new file mode 100644 index 0000000..c0d64ef --- /dev/null +++ b/command/mv/mv.go @@ -0,0 +1,47 @@ +package mv + +import ( + "bbash/environment" + "bbash/input_parser" + "fmt" + "os" + "path/filepath" + "strings" +) + +func Mv(in input_parser.Input, env *environment.Env) { + args := strings.SplitN(in.Args, " ", 2) + if len(args) != 2 { + fmt.Print("No right amount of args") + return + } + file_path_source := filepath.Join(env.Path, args[0]) + file_path_dest := filepath.Join(env.Path, args[1]) + source, err := os.ReadFile(file_path_source) + if err != nil { + fmt.Println(fmt.Sprintf("Error opening file: %s", err.Error())) + return + } + dest, err := os.Create(file_path_dest) + defer dest.Close() + if err != nil { + fmt.Println(fmt.Sprintf("Error opening file: %s", err.Error())) + return + } + _, err = dest.Write(source) + if err != nil { + fmt.Println(fmt.Sprintf("Error writing destination: %s", err.Error())) + return + } + err = os.Remove(file_path_source) + if err != nil { + fmt.Println(fmt.Sprintf("Error removing source: %s", err.Error())) + return + } + + +} + + + + diff --git a/command/pwd/pwd.go b/command/pwd/pwd.go new file mode 100644 index 0000000..a1b87df --- /dev/null +++ b/command/pwd/pwd.go @@ -0,0 +1,12 @@ +package pwd + + +import ( + "bbash/input_parser" + "fmt" + "bbash/environment" +) + +func Pwd(in input_parser.Input, env *environment.Env) { + fmt.Print(env.Path) +} diff --git a/command/rm/rm.go b/command/rm/rm.go new file mode 100644 index 0000000..26d3ab3 --- /dev/null +++ b/command/rm/rm.go @@ -0,0 +1,31 @@ +package rm + +import ( + "bbash/environment" + "bbash/input_parser" + "fmt" + "os" + "path/filepath" +) + +func Rm(in input_parser.Input, env *environment.Env) { + file_path := filepath.Join(env.Path, in.Args) + stat, err := os.Stat(file_path) + if os.IsNotExist(err) { + fmt.Print(fmt.Sprintf("File does not exist")) + return + } + if stat.IsDir() { + fmt.Print(fmt.Sprintf("%s Is a folder, not a file!", file_path)) + return + } + err = os.Remove(file_path) + if err != nil { + fmt.Print(fmt.Sprintf("Error removing file or folder: %s", err.Error())) + return + } +} + + + + diff --git a/command/touch/touch.go b/command/touch/touch.go new file mode 100644 index 0000000..2db49bc --- /dev/null +++ b/command/touch/touch.go @@ -0,0 +1,27 @@ +package touch + +import ( + "bbash/environment" + "bbash/input_parser" + "fmt" + "os" + "path/filepath" +) + +func Touch(in input_parser.Input, env *environment.Env) { + file_path := filepath.Join(env.Path, in.Args) + _, err := os.Stat(file_path) + if !os.IsNotExist(err) { + fmt.Print(fmt.Sprintf("File alredy exist!")) + return + } + file, err := os.Create(file_path) + if err != nil { + fmt.Print(fmt.Sprintf("Error creating file: %s", err.Error())) + return + } + defer file.Close() +} + + + diff --git a/environment/env_test.go b/environment/env_test.go new file mode 100644 index 0000000..415ba18 --- /dev/null +++ b/environment/env_test.go @@ -0,0 +1,13 @@ +package environment + +import ( + "testing" + "fmt" +) + +func TestEnv(t *testing.T) { + env := Get_env() + fmt.Print("PATH: ") + fmt.Println(env.Path) + +} diff --git a/environment/environment.go b/environment/environment.go new file mode 100644 index 0000000..baf8916 --- /dev/null +++ b/environment/environment.go @@ -0,0 +1,30 @@ +package environment + +import ( + "log" + "os" + "strings" +) + +type Env struct { + Path string + + Env map[string]string +} + +func Get_env() Env { + pwd, err := os.Getwd() + if err != nil { + log.Fatal(err.Error()) + } + env := make(map[string]string) + for _, s_env := range os.Environ() { + split := strings.Split(s_env, "=") + env[split[0]] = split[1] + } + return Env{ + Path: pwd, + Env: env, + } + +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..e370850 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module bbash + +go 1.23.4 diff --git a/input_parser/input_parser.go b/input_parser/input_parser.go new file mode 100644 index 0000000..d066fa0 --- /dev/null +++ b/input_parser/input_parser.go @@ -0,0 +1,36 @@ +package input_parser + +import ( + "bufio" + "log" + "os" + "strings" +) +type Input struct { + // The instruction a.k.a first argument + Instruction string + + // The args, currently just the string after the instruction + Args string +} +func Parse() Input { + reader := bufio.NewReader(os.Stdin) + input, err := reader.ReadString('\n') + if err != nil { + log.Fatal(err.Error()) + } + split := strings.SplitN(string(input), " ", 2) + instruction := strings.TrimSpace(split[0]) + var arg string + if len(split) == 2 { + arg = strings.TrimSpace(split[1]) + } else { + arg = "" + } + + return Input { + Instruction: instruction, + Args: arg, + } +} + diff --git a/input_parser/test_test.go b/input_parser/test_test.go new file mode 100644 index 0000000..3d5a5e1 --- /dev/null +++ b/input_parser/test_test.go @@ -0,0 +1,16 @@ +package input_parser + +import ( + "testing" + "fmt" +) + +func TestParse(t *testing.T) { + Parse("") + Parse("hej") + got := Parse("echo \" jej asdsa dsa \"") + fmt.Println(got.Instruction) + fmt.Println(got.Args) + + +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..0f4688a --- /dev/null +++ b/main.go @@ -0,0 +1,21 @@ +package main + +import ( + "bbash/command" + "bbash/environment" + "bbash/input_parser" + "fmt" +) + + +func main() { + env := environment.Get_env() + command.Init(&env) + + for { + fmt.Print(fmt.Sprintf("%s > ", env.Path)) + in_parsed := input_parser.Parse() + command.Run_command(in_parsed, &env) + fmt.Println() + } +}