diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index eafb6a9..388e42b 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -3,11 +3,7 @@ name: Java CI with Gradle -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] +on: [push, pull_request] jobs: build: @@ -16,26 +12,19 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up JDK 1.8 + - name: Set up JDK 11 uses: actions/setup-java@v1 with: - java-version: 1.8 + java-version: 11 + - name: Grant execute permission for gradlew - run: chmod +x gradlew + run: chmod +x ./gradlew + + - name: Run command + run: echo Hello + - name: Build with Gradle run: ./gradlew build - - test: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Set up JDK 1.8 - uses: actions/setup-java@v1 - with: - java-version: 1.8 - - name: Grant execute permission for gradlew - run: chmod +x gradlew - - name: Test with Gradle + + - name: Test with Gradlew run: ./gradlew test diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..bd7cd2b --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,42 @@ +plugins { + java + application +} + +repositories { + mavenCentral() +} + +dependencies { + implementation("org.jetbrains:annotations:16.0.2") + testImplementation(platform("org.junit:junit-bom:5.7.0")) + testImplementation("org.junit.jupiter:junit-jupiter") +} + +java { + sourceSets { + main { + java.setSrcDirs(listOf("src/main/java")) + resources.setSrcDirs(listOf("src/resources")) + } + test { + java.setSrcDirs(listOf("src/test/java")) + } + } +} + +tasks.getByName("run") { + standardInput = System.`in` +} + +application{ + mainClass.set("commandline.CommandLine") +} + +tasks.compileJava { + options.release.set(11) +} + +tasks.test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e708b1c Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..be52383 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..cd77b62 --- /dev/null +++ b/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1.txt to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1.txt) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..ac1b06f --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..2a591ed --- /dev/null +++ b/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'CLI' + diff --git a/src/main/java/commandline/CommandLine.java b/src/main/java/commandline/CommandLine.java new file mode 100644 index 0000000..324bbbc --- /dev/null +++ b/src/main/java/commandline/CommandLine.java @@ -0,0 +1,88 @@ +package commandline; + +import commandline.commands.Command; +import commandline.utils.CommandBuilder; +import commandline.utils.Parser; +import commandline.utils.ShellCommand; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; + +/* + Main Class + until get "exit": + read the line, split it by |, + send each part to the parser, + get the command name and arguments, + send the name to the commandBuilder, + then execute the command by run method +*/ +public class CommandLine { + private static final List results = new ArrayList<>(); + + // main function for CommandLine + private static String work(InputStream inputStream) { + Scanner scanner = new Scanner(inputStream); + boolean cycle = true; + StringBuilder output = new StringBuilder(); + while (cycle) { + String userInput = scanner.nextLine(); + String[] commands = userInput.split("\\s*\\|\\s*"); + for (String str : commands) { + List list = Parser.parse(str); + if (!list.isEmpty()) { + if (list.get(0).matches("exit")) { + cycle = false; + break; + } + Command command = CommandBuilder.getCommand(list.get(0)); + if (command == null) { + try { + StringBuilder input = new StringBuilder(); + for (String elem : list) { + input.append(elem); + } + String res = ShellCommand.executeCommand(input.toString()); + results.add(res); + if (res.isEmpty()) { + output.append("Wrong command: ").append(input); + System.out.println("Wrong command: " + input); + break; + } + } catch (InterruptedException | IOException e) { + output.append("Wrong command: ").append(list.get(0)).append("\n"); + System.out.println("Wrong command: " + list.get(0)); + break; + } + } else { + List s = list.subList(1, list.size()); + if (!results.isEmpty()) { + String before = results.remove(results.size() - 1); + results.add(command.run(s, before)); + } else { + results.add(command.run(s, "")); + } + } + } + } + if (!results.isEmpty()) { + String result = results.remove(0); + output.append(result).append("\n"); + System.out.println(result); + } + } + return output.toString(); + } + + public static void main(String[] args) { + work(System.in); + } + + // func only for test + public static String forTest(InputStream input) { + return work(input); + } +} \ No newline at end of file diff --git a/src/main/java/commandline/commands/Cat.java b/src/main/java/commandline/commands/Cat.java new file mode 100644 index 0000000..caceabd --- /dev/null +++ b/src/main/java/commandline/commands/Cat.java @@ -0,0 +1,34 @@ +package commandline.commands; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.List; + +/* Function Cat + * if the command hasn't received any args, then it returns previous + * calculation, else returns the contents of args + */ +public class Cat implements Command { + @Override + public String run(List args, String before) { + if (args.isEmpty()) { + return before; + } + StringBuilder output = new StringBuilder(); + for (String str : args) { + if (!str.equals(" ")) { + try (BufferedReader br = new BufferedReader(new FileReader(str))) { + String line; + while ((line = br.readLine()) != null) { + output.append(line); + output.append("\n"); + } + } catch (IOException e) { + return "File not found"; + } + } + } + return output.deleteCharAt(output.length() - 1).toString(); + } +} diff --git a/src/main/java/commandline/commands/Command.java b/src/main/java/commandline/commands/Command.java new file mode 100644 index 0000000..a53b9e6 --- /dev/null +++ b/src/main/java/commandline/commands/Command.java @@ -0,0 +1,11 @@ +package commandline.commands; + +import java.util.List; + +/* Interface for all commands with a single method, + * that executes the command + */ +public interface Command { + // takes args and result of previous calculation + String run(List args, String before); +} \ No newline at end of file diff --git a/src/main/java/commandline/commands/Echo.java b/src/main/java/commandline/commands/Echo.java new file mode 100644 index 0000000..ce3f323 --- /dev/null +++ b/src/main/java/commandline/commands/Echo.java @@ -0,0 +1,22 @@ +package commandline.commands; + +import java.util.List; + +/* Function echo + * if it has args then returns them + * else returns empty string + */ +public class Echo implements Command { + @Override + public String run(List args, String before) { + if (args.isEmpty()) { + return ""; + } + StringBuilder result = new StringBuilder(); + for (String s : args) { + result.append(s).append(" "); + } + result.deleteCharAt(result.length()-1); + return result.toString(); + } +} diff --git a/src/main/java/commandline/commands/Pwd.java b/src/main/java/commandline/commands/Pwd.java new file mode 100644 index 0000000..426236e --- /dev/null +++ b/src/main/java/commandline/commands/Pwd.java @@ -0,0 +1,11 @@ +package commandline.commands; + +import java.util.List; + +//returns user.dir +public class Pwd implements Command { + @Override + public String run(List args, String before) { + return System.getProperty("user.dir"); + } +} diff --git a/src/main/java/commandline/commands/Wc.java b/src/main/java/commandline/commands/Wc.java new file mode 100644 index 0000000..01fbd23 --- /dev/null +++ b/src/main/java/commandline/commands/Wc.java @@ -0,0 +1,80 @@ +package commandline.commands; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.List; + +/* Function wc + * if it has arguments, then it counts the number of lines, words and bytes in files + * else it counts the same in result of previous calculations + */ +public class Wc implements Command { + private long totalByteCount = 0; + private long totalLineCount = 0; + private long totalWordCount = 0; + private long wordCount = 0; + private long lineCount = 0; + private long byteCount = 0; + + // counts all in result of previous calculations + private String countArgsEmpty(String before) { + byte[] b = before.getBytes(StandardCharsets.UTF_8); + byteCount = b.length; + wordCount = before.replaceAll("\\s{2,}", " ").trim().split("\\s").length; + lineCount = before.chars().filter(ch -> ch == '\n').count() + 1; + return lineCount + " " + wordCount + " " + (byteCount + 1); + } + + // counts all in files + private String countArgsNoEmpty(List args) { + StringBuilder result = new StringBuilder(); + for (String s : args) { + if (!s.equals(" ")) { + wordCount = 0; + lineCount = 0; + File f = new File(s); + byteCount = f.length(); + totalByteCount += byteCount; + try (BufferedReader br = new BufferedReader(new FileReader(s))) { + String line; + while ((line = br.readLine()) != null) { + if (!line.isEmpty()) { + wordCount += line.trim().split("\\s+").length; + } + lineCount += 1; + } + } catch (IOException e) { + return "wc: "+ s + ": No such file or directory"; + } + totalLineCount += lineCount; + totalWordCount += wordCount; + result.append(lineCount).append(" "); + result.append(wordCount).append(" "); + result.append(byteCount).append(" "); + if (args.size() > 1) { + result.append(s).append("\n"); + } else { + result.append(s); + } + } + } + if (args.size() == 1) + return result.toString(); + result.append(totalLineCount).append(" "); + result.append(totalWordCount).append(" "); + result.append(totalByteCount).append(" total"); + return result.toString(); + } + + @Override + // main function + public String run(List args, String before) { + if (args.isEmpty()) { + return countArgsEmpty(before); + } + return countArgsNoEmpty(args); + } +} \ No newline at end of file diff --git a/src/main/java/commandline/utils/CommandBuilder.java b/src/main/java/commandline/utils/CommandBuilder.java new file mode 100644 index 0000000..1b0f0a6 --- /dev/null +++ b/src/main/java/commandline/utils/CommandBuilder.java @@ -0,0 +1,26 @@ +package commandline.utils; + +import commandline.commands.*; + +/* Takes command's name + * if command is found then returns the instance of the command class + * else returns null + */ +public class CommandBuilder { + private CommandBuilder() { + } + + public static Command getCommand(String commandName) { + if (commandName.equals("pwd")) { + return new Pwd(); + } else if (commandName.equalsIgnoreCase("cat")) { + return new Cat(); + } else if (commandName.equalsIgnoreCase("echo")) { + return new Echo(); + } else if (commandName.equalsIgnoreCase("wc")) { + return new Wc(); + } else { + return null; + } + } +} diff --git a/src/main/java/commandline/utils/Parser.java b/src/main/java/commandline/utils/Parser.java new file mode 100644 index 0000000..4affcad --- /dev/null +++ b/src/main/java/commandline/utils/Parser.java @@ -0,0 +1,80 @@ +package commandline.utils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/* + * Class for parsing commands using regular expressions +*/ +public class Parser { + private static final Variables variables = new Variables(); + private static final Set commands = Set.of("echo", "wc", "pwd", "exit", "cat"); + + private Parser() { + } + + //parse string in " " + private static String parseInQuotes(String input) { + StringBuilder resultS = new StringBuilder(input.replaceAll("\"", "")); + int i = 0; + while (i < resultS.length()) { + int d = resultS.indexOf("$"); + if (d != -1 && d != resultS.length() - 1) { + for (i = d + 1; i < resultS.length(); i++) { + if (resultS.charAt(i) == ' ' || resultS.charAt(i) == '\'') { + break; + } + } + String var = resultS.substring(d + 1, i); + resultS.replace(d, i, variables.getVar(var)); + } else { + break; + } + } + return resultS.toString(); + } + + // main function for parsing + public static List parse(String str) { + List list = new ArrayList<>(); + Matcher m = Pattern.compile("(\\w+=\\w+)|('.+')|(\".+\")|(\\S+)|(\\$\\w+)").matcher(str); + while (m.find()) { + String currentS = m.group(0); + if (currentS.charAt(0) == '\'' && currentS.charAt(m.group(0).length() - 1) == '\'') { + list.add(currentS.substring(1, currentS.length() - 1)); + } else if (currentS.matches("\\w+=\\w+")) { + String[] split = currentS.split("="); + variables.putVar(split[0], split[1]); + } else if (currentS.matches("\\$\\w+(\\$\\w+)*")) { + String[] vars = currentS.split("\\$"); + if (vars.length > 2) { + StringBuilder maybeCommand = new StringBuilder(); + for (String one : vars) { + if (!one.isEmpty()) { + maybeCommand.append(variables.getVar(one)); + } + } + list.add(maybeCommand.toString()); + } + else { + list.add(variables.getVar(currentS.substring(1))); + } + } else if (currentS.charAt(0) == '\"' && currentS.charAt(currentS.length() - 1) == '\"') { + list.add(parseInQuotes(currentS)); + } else if (commands.contains(m.group(0))) { + list.add(m.group(0)); + } else { + list.add(m.group(0)); + list.add(" "); + } + } + if (!list.isEmpty() && list.get(list.size() - 1).equals(" ")) { + list.remove(list.size() - 1); + } + + return list; + } +} diff --git a/src/main/java/commandline/utils/ShellCommand.java b/src/main/java/commandline/utils/ShellCommand.java new file mode 100644 index 0000000..83538eb --- /dev/null +++ b/src/main/java/commandline/utils/ShellCommand.java @@ -0,0 +1,27 @@ +package commandline.utils; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +/* Class for running shell commands */ +public class ShellCommand { + private ShellCommand() { + } + + public static String executeCommand(String command) throws IOException, InterruptedException { + StringBuilder output = new StringBuilder(); + Process p = Runtime.getRuntime().exec(command); + p.waitFor(); + try ( + BufferedReader reader = + new BufferedReader(new InputStreamReader(p.getInputStream())) + ) { + String line; + while ((line = reader.readLine()) != null) { + output.append(line).append("\n"); + } + } + return output.toString(); + } +} diff --git a/src/main/java/commandline/utils/Variables.java b/src/main/java/commandline/utils/Variables.java new file mode 100644 index 0000000..88cf193 --- /dev/null +++ b/src/main/java/commandline/utils/Variables.java @@ -0,0 +1,17 @@ +package commandline.utils; + +import java.util.HashMap; +import java.util.Map; + +/* Class for storing variables */ +public class Variables { + private final Map vars = new HashMap<>(); + + public void putVar(String name, String value) { + vars.put(name, value); + } + + public String getVar(String name) { + return vars.getOrDefault(name, ""); + } +} diff --git a/src/test/java/commandline/CommandLineTest.java b/src/test/java/commandline/CommandLineTest.java new file mode 100644 index 0000000..64c5dee --- /dev/null +++ b/src/test/java/commandline/CommandLineTest.java @@ -0,0 +1,135 @@ +package commandline; + +import commandline.utils.ShellCommand; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayInputStream; +import java.io.IOException; + +// Testing +class CommandLineTest { + // helper + private String out(String input) { + ByteArrayInputStream in = new ByteArrayInputStream(input.getBytes()); + System.setIn(in); + return CommandLine.forTest(in); + } + + /* Test echo + * echo 12345 + * echo '123 45' + * a=10; echo $a + * a=10; echo "a = $a" + * a=10; b=100; echo echo "a = $a and b = $b and c == end" + * echo + * echo 10 | echo 20 + * echo 10 | wc + * echo 10 | pwd + * echo 10 | cat + * x=10 echo "aasd'$x'asd" == "aasd'10'asd" + * echo "aasd'$c'asd" == "aasd''asd" + * */ + @Test + void testEcho() throws IOException, InterruptedException { + String input = "echo 12345 \n exit"; + Assertions.assertEquals("12345\n", out(input)); + input = "echo '123 45' \n exit"; + Assertions.assertEquals("123 45\n", out(input)); + input = "a=10 \n echo $a \n exit"; + Assertions.assertEquals("10\n", out(input)); + input = "a=10 \n echo \"a = $a\" \n exit"; + Assertions.assertEquals("a = 10\n", out(input)); + input = "a=10 \n b=100 \n echo \"a = $a and b = $b and c == end\" \n exit"; + Assertions.assertEquals("a = 10 and b = 100 and c == end\n", out(input)); + input = "echo \n exit"; + Assertions.assertEquals("\n", out(input)); + input = "echo 10 | echo 20 \n exit"; + Assertions.assertEquals("20\n", out(input)); + input = "echo 10 | wc \n exit"; + Assertions.assertEquals("1 1 3\n", out(input)); + input = "echo 10 | pwd \n exit"; + Assertions.assertEquals(ShellCommand.executeCommand("pwd"), out(input)); + input = "echo 10 | cat \n exit"; + Assertions.assertEquals("10\n", out(input)); + input = "x=10 \n echo \"aasd'$x'asd\"\n exit"; + Assertions.assertEquals("aasd'10'asd\n", out(input)); + input = "echo \"aasd'$c'asd\"\n exit"; + Assertions.assertEquals("aasd''asd\n", out(input)); + System.setIn(System.in); + } + + /*Test variables + * a=p b=wd $a$b == command pwd + * a=ec b=ho $a$b 5 == echo 5 == 5 + * a=ec b=ho echo $a$b == echo echo == echo + * a=ec b=ho echo $a $b == echo ec ho == ec ho + * */ + @Test + void testVars() throws IOException, InterruptedException { + String input = "a=p \n b=wd \n $a$b \n exit"; + Assertions.assertEquals(ShellCommand.executeCommand("pwd"), out(input)); + input = "a=ec \n b=ho \n $a$b 5 \n exit"; + Assertions.assertEquals("5\n", out(input)); + input = "a=ec \n b=ho \n echo $a$b \n exit"; + Assertions.assertEquals("echo\n", out(input)); + input = "a=ec \n b=ho \n echo $a $b \n exit"; + Assertions.assertEquals("ec ho\n", out(input)); + System.setIn(System.in); + } + + /* Test pwd */ + @Test + void testPwd() throws IOException, InterruptedException { + String input = "pwd \n exit"; + Assertions.assertEquals(ShellCommand.executeCommand("pwd"), out(input)); + System.setIn(System.in); + } + + /* Test cat + * cat gradlew.bat + * cat 1.txt 123.txt + * cat go wrong + * */ + @Test + void testCat() throws IOException, InterruptedException { + String input = "cat gradlew.bat \n exit"; + Assertions.assertEquals(ShellCommand.executeCommand(input), out(input)); + input = "cat testFiles/1.txt testFiles/123.txt \n exit"; + Assertions.assertEquals(ShellCommand.executeCommand(input), out(input)); + input = "cat nofile.nofile \n exit"; + Assertions.assertEquals("File not found\n", out(input)); + System.setIn(System.in); + } + + /* + * wc gradlew.bat + * wc 1.txt 123.txt + * cat 1.txt | wc + * wc ololo == wc: ololo: No such file or directory + * */ + @Test + void testWc() { + String input = "wc gradlew.bat \n exit"; + Assertions.assertEquals("89 357 2763 gradlew.bat\n", out(input)); + input = "wc testFiles/1.txt testFiles/123.txt \n exit"; + Assertions.assertEquals("3 3 6 testFiles/1.txt\n" + "3 3 25 testFiles/123.txt\n" + + "6 6 31 total\n", out(input)); + input = "cat testFiles/1.txt | wc \n exit"; + Assertions.assertEquals("3 3 6\n", out(input)); + System.setIn(System.in); + input = "wc ololo \n exit"; + Assertions.assertEquals("wc: ololo: No such file or directory\n" + , out(input)); + } + + // Test commands which is not in the project + @Test + void testOtherCommands() { + String input = "python \n exit"; + Assertions.assertEquals("Wrong command: python\n", out(input)); + System.setIn(System.in); + input = "git lolol \n exit"; + Assertions.assertEquals("Wrong command: git lolol\n", out(input)); + } +} diff --git a/testFiles/1.txt b/testFiles/1.txt new file mode 100644 index 0000000..01e79c3 --- /dev/null +++ b/testFiles/1.txt @@ -0,0 +1,3 @@ +1 +2 +3 diff --git a/testFiles/123.txt b/testFiles/123.txt new file mode 100644 index 0000000..692827c --- /dev/null +++ b/testFiles/123.txt @@ -0,0 +1,3 @@ +laskjdlask +asdasdasd +123