diff --git a/.codecrafters/compile.sh b/.codecrafters/compile.sh new file mode 100755 index 0000000..968edea --- /dev/null +++ b/.codecrafters/compile.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# +# This script is used to compile your program on CodeCrafters +# +# This runs before .codecrafters/run.sh +# +# Learn more: https://codecrafters.io/program-interface + +set -e # Exit on failure + +mvn -B package -Ddir=/tmp/codecrafters-build-shell-java diff --git a/.codecrafters/run.sh b/.codecrafters/run.sh new file mode 100755 index 0000000..a0a1bd9 --- /dev/null +++ b/.codecrafters/run.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# +# This script is used to run your program on CodeCrafters +# +# This runs after .codecrafters/compile.sh +# +# Learn more: https://codecrafters.io/program-interface + +set -e # Exit on failure + +exec java -jar /tmp/codecrafters-build-shell-java/codecrafters-shell.jar "$@" diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..176a458 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a48b18a --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.jar +target/ +.idea/ diff --git a/README.md b/README.md index 2a48939..4b9591e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,35 @@ -# codecrafters-shell-java -Create a POSIX shell using java +[![progress-banner](https://backend.codecrafters.io/progress/shell/f47d6fb6-94e9-44fa-a664-fd92a07e7ef8)](https://app.codecrafters.io/users/codecrafters-bot?r=2qF) + +This is a starting point for Java solutions to the +["Build Your Own Shell" Challenge](https://app.codecrafters.io/courses/shell/overview). + +In this challenge, you'll build your own POSIX compliant shell that's capable of +interpreting shell commands, running external programs and builtin commands like +cd, pwd, echo and more. Along the way, you'll learn about shell command parsing, +REPLs, builtin commands, and more. + +**Note**: If you're viewing this repo on GitHub, head over to +[codecrafters.io](https://codecrafters.io) to try the challenge. + +# Passing the first stage + +The entry point for your `shell` implementation is in `src/main/java/Main.java`. +Study and uncomment the relevant code, and push your changes to pass the first +stage: + +```sh +git commit -am "pass 1st stage" # any msg +git push origin master +``` + +Time to move on to the next stage! + +# Stage 2 & beyond + +Note: This section is for stages 2 and beyond. + +1. Ensure you have `mvn` installed locally +1. Run `./your_program.sh` to run your program, which is implemented in + `src/main/java/Main.java`. +1. Commit your changes and run `git push origin master` to submit your solution + to CodeCrafters. Test output will be streamed to your terminal. diff --git a/codecrafters.yml b/codecrafters.yml new file mode 100644 index 0000000..915ee64 --- /dev/null +++ b/codecrafters.yml @@ -0,0 +1,12 @@ +# Set this to true if you want debug logs. +# Help me to master all my true abilites and make me understand myself and attain salvation +# +# These can be VERY verbose, so we suggest turning them off +# unless you really need them. +debug: true + +# Use this to change the Java version used to run your code +# on Codecrafters. +# +# Available versions: java-23 +language_pack: java-23 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..71b07b1 --- /dev/null +++ b/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + io.codecrafters + codecrafters-shell + 1.0 + + + 23 + 23 + UTF-8 + 23 + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + codecrafters-shell + + jar-with-dependencies + + false + + + true + + Main + + + ${dir} + + + + + make-assembly + package + + single + + + + + + + + \ No newline at end of file diff --git a/src/main/java/Main.java b/src/main/java/Main.java new file mode 100644 index 0000000..fbc40d9 --- /dev/null +++ b/src/main/java/Main.java @@ -0,0 +1,189 @@ +import java.util.Scanner; +import java.util.Arrays; +import java.io.*; + +public class Main { + public static void println(String str) { + + System.out.println(str); + } + + public static void print(String str) { + + System.out.print(str); + } + + public static void notFound(String str) { + + System.out.println(str + ": not found"); + } + + public static void debugOn(String str) { + System.out.printf("%s%n", str); + } + + public static String getCwd(String command) throws Exception { + File pwd = new File(command); + return pwd.getCanonicalFile().getParent(); + } + + public static String getPath(String typeString) { + // Boolean pathFlag = Boolean.FALSE; + String path = System.getenv("PATH"); + String typeStringLoc = typeString + ": not found"; + // String typeStringLocdebug = ""; + + if (path != null || !(path.equalsIgnoreCase("")) || typeString != null) { + // System.out.println("INSIDE PATH LIST TYPE "); + // System.out.printf("HELLO %s", path); + String[] paths = path.split(":"); + + // System.out.printf(Arrays.toString(paths)); + // System.out.println("BEFORE PRINTING PATHS "); + for (String p : paths) { + // pathFlag = Boolean.FALSE; + // System.out.printf("%s%n", p); + // System.out.println("FILES ->" + p); + + File file = new File(p); + File[] files = file.listFiles(); + + // System.out.printf(Arrays.toString(files)); + if (files != null) { + // System.out.println(Arrays.asList(files).contains(typeString)); + for (File f : files) { + // System.out.printf("%s%n", f); + String fstr = f.toString(); + String subFstr = fstr.substring(fstr.lastIndexOf(File.separator) + 1); + if (subFstr.equalsIgnoreCase(typeString)) { + // pathFlag = Boolean.TRUE; + typeStringLoc = typeString + " is " + f; + break; + } + } + } + } + } + return typeStringLoc; + } + + public static String getPlainPath(String typeString) { + // Boolean pathFlag = Boolean.FALSE; + String path = System.getenv("PATH"); + String typeStringLoc = typeString + ": not found"; + // String typeStringLocdebug = ""; + + if (path != null || !(path.equalsIgnoreCase("")) || typeString != null) { + // System.out.println("INSIDE PATH LIST TYPE "); + // System.out.printf("HELLO %s", path); + String[] paths = path.split(":"); + + // System.out.printf(Arrays.toString(paths)); + // System.out.println("BEFORE PRINTING PATHS "); + for (String p : paths) { + // pathFlag = Boolean.FALSE; + // System.out.printf("%s%n", p); + // System.out.println("FILES ->" + p); + + File file = new File(p); + File[] files = file.listFiles(); + + // System.out.printf(Arrays.toString(files)); + if (files != null) { + // System.out.println(Arrays.asList(files).contains(typeString)); + for (File f : files) { + // System.out.printf("%s%n", f); + String fstr = f.toString(); + String subFstr = fstr.substring(fstr.lastIndexOf(File.separator) + 1); + if (subFstr.equalsIgnoreCase(typeString)) { + // pathFlag = Boolean.TRUE; + typeStringLoc = f.toString(); + break; + } + } + } + } + } + return typeStringLoc; + } + + public static void executeCommand(String input) throws Exception { + // pass the entire command because the command might come with its own arguments + String command = input.split(" ")[0]; + // String commandArgs = input.split(" ")[1]; + // debugOn(command); + // debugOn(commandArgs); + String path = getPlainPath(command); + File fPath = new File(path); + // debugOn(path); + if (command.equalsIgnoreCase("pwd")) { + String cmd = "pwd"; + String printPWD = getCwd(cmd); + System.out.println(printPWD); + + } else if (!fPath.isFile()) { + println(command + ": command not found"); + } else { + // println("HELLO AM HERE"); + String commandArgs = input.split(" ")[1]; + // println("IS TERE AN ARG"); + // println(commandArgs); + // debugOn(command); + // debugOn(commandArgs); + String path0 = getPath(command); + File fPath0 = new File(path0); + + String fullPath = (!commandArgs.equalsIgnoreCase("")) ? command + " " + commandArgs + : path + input.substring(command.length()); + + // debugOn(fullPath); + // System.out.println(Arrays.toString(fullPath.split(" "))); + Process p = Runtime.getRuntime().exec(fullPath.split(" ")); + p.getInputStream().transferTo(System.out); + } + } + + public static void main(String[] args) throws Exception { + + String[] built_in_commands = { "echo", "exit", "type", "pwd" }; + Scanner scanner = new Scanner(System.in); + + while (true) { + print("$ "); + String input = scanner.nextLine(); + + if (!input.equalsIgnoreCase("exit 0")) { + + if (input.startsWith("echo")) { + + println(input.substring("echo".length() + 1, input.length())); + + } else if (input.startsWith("type")) { + + String typeString = input.substring("type".length() + 1, input.length()); + + if (Arrays.asList(built_in_commands).contains(typeString)) { + println(typeString + " is a shell builtin"); + // break; + + } else { + // debugOn("BEFORE PATH"); + String typePath = getPath(typeString); + println(typePath); + // debugOn("AFTER PATH" + typePath); + } + } else { + // debugOn("AM I HERE"); + // notFound(input); + // String typePath = getPath(input); + // System.out.println(typePath); + executeCommand(input); + } + } else { + break; + } + + // debugOn(input); + } + } +} diff --git a/your_program.sh b/your_program.sh new file mode 100755 index 0000000..ac33f36 --- /dev/null +++ b/your_program.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# +# Use this script to run your program LOCALLY. +# +# Note: Changing this script WILL NOT affect how CodeCrafters runs your program. +# +# Learn more: https://codecrafters.io/program-interface + +set -e # Exit early if any commands fail + +# Copied from .codecrafters/compile.sh +# +# - Edit this to change how your program compiles locally +# - Edit .codecrafters/compile.sh to change how your program compiles remotely +( + cd "$(dirname "$0")" # Ensure compile steps are run within the repository directory + mvn -B package -Ddir=/tmp/codecrafters-build-shell-java +) + +# Copied from .codecrafters/run.sh +# +# - Edit this to change how your program runs locally +# - Edit .codecrafters/run.sh to change how your program runs remotely +# print java_home as debug mode env is enabled +exec java -jar /tmp/codecrafters-build-shell-java/codecrafters-shell.jar "$@"