Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions bash/src/main/kotlin/hse/nedikov/bash/Environment.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package hse.nedikov.bash

import hse.nedikov.bash.Environment.State.*
import java.io.File

/**
* Environment of the interpreter
Expand All @@ -12,6 +13,7 @@ class Environment {

private val varMap = HashMap<String, String>()
private var state: State = Working
private var workingDirectory = File("./")

/**
* Map of the local variables
Expand All @@ -38,4 +40,20 @@ class Environment {
* Returns true iff interpreter is still working
*/
fun isWorking(): Boolean = state == Working

fun changeDirectory(newPath: String): Boolean {
val newDirectory = getCanonicalFile(newPath)
if (newDirectory.isDirectory) {
workingDirectory = newDirectory
}
return newDirectory.isDirectory
}

fun getCanonicalPath(path: String): String {
return getCanonicalFile(path).canonicalPath
}

fun getCanonicalFile(path: String): File {
return workingDirectory.resolve(path)
}
}
12 changes: 7 additions & 5 deletions bash/src/main/kotlin/hse/nedikov/bash/logic/Command.kt
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,14 @@ abstract class Command {
}
return when (name) {
"echo" -> Echo(args)
"wc" -> WordCount(args)
"pwd" -> Pwd()
"wc" -> WordCount(args, env)
"pwd" -> Pwd(env)
"exit" -> Exit(env)
"cat" -> Cat(args)
"grep" -> Grep(args)
else -> OuterCommand(name, args)
"cat" -> Cat(args, env)
"grep" -> Grep(args, env)
"cd" -> Cd(args, env)
"ls" -> Ls(args, env)
else -> OuterCommand(name, args, env)
}
}
}
Expand Down
7 changes: 4 additions & 3 deletions bash/src/main/kotlin/hse/nedikov/bash/logic/commands/Cat.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package hse.nedikov.bash.logic.commands

import hse.nedikov.bash.logic.Command
import hse.nedikov.bash.Environment
import hse.nedikov.bash.logic.EnvironmentCommand
import java.io.*
import java.lang.Exception

/**
* cat command which prints files entries to the output stream
*/
class Cat(private val arguments: ArrayList<String>) : Command() {
class Cat(private val arguments: ArrayList<String>, override val env: Environment) : EnvironmentCommand(env) {
/**
* Prints input to the output if has no arguments and prints entries of files from arguments otherwise
*/
Expand All @@ -22,7 +23,7 @@ class Cat(private val arguments: ArrayList<String>) : Command() {
override fun execute(output: PipedWriter) {
for (arg in arguments) {
try {
FileReader(arg).forEachLine { output.write(arg) }
FileReader(env.getCanonicalPath(arg)).forEachLine { output.write(it + System.lineSeparator()) }
} catch (e: Exception) {
output.write("cat: ${e.message}")
}
Expand Down
31 changes: 31 additions & 0 deletions bash/src/main/kotlin/hse/nedikov/bash/logic/commands/Cd.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package hse.nedikov.bash.logic.commands

import hse.nedikov.bash.Environment
import hse.nedikov.bash.logic.Command
import java.io.PipedReader
import java.io.PipedWriter
import java.lang.RuntimeException

/**
* cd command which changes the working directory
*/
class Cd(private val arguments: ArrayList<String>, private val env: Environment) : Command() {
/**
* Changes the working directory
*/
override fun execute(input: PipedReader, output: PipedWriter) {
return execute(output)
}

/**
* Changes the working directory
*/
override fun execute(output: PipedWriter) {
if (arguments.size > 1) {
throw RuntimeException("cd: too many arguments")
}
if (!env.changeDirectory(arguments.getOrElse(0) { System.getProperty("user.home") })) {
throw RuntimeException("cd: directory not found")
}
}
}
16 changes: 9 additions & 7 deletions bash/src/main/kotlin/hse/nedikov/bash/logic/commands/Grep.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ package hse.nedikov.bash.logic.commands
import com.xenomachina.argparser.ArgParser
import com.xenomachina.argparser.ShowHelpException
import com.xenomachina.argparser.default
import hse.nedikov.bash.logic.Command
import hse.nedikov.bash.Environment
import hse.nedikov.bash.logic.EnvironmentCommand
import java.io.*
import java.lang.IllegalArgumentException
import java.util.ArrayList

/**
* grep command which prints all lines with pattern matched substring
*/
class Grep(private val arguments: ArrayList<String>) : Command() {
class Grep(private val arguments: ArrayList<String>, override val env: Environment) : EnvironmentCommand(env) {
/**
* Takes lines for grep from input
*/
Expand All @@ -25,7 +26,8 @@ class Grep(private val arguments: ArrayList<String>) : Command() {
*/
override fun execute(output: PipedWriter) {
val args = parseArguments(::GrepArgs, output) ?: return
grep(args.file, output, args)
val input = FileReader(env.getCanonicalPath(args.file))
grep(input, output, args)
}

private fun <T> parseArguments(constructor: (ArgParser) -> T, output: Writer): T? {
Expand All @@ -35,19 +37,19 @@ class Grep(private val arguments: ArrayList<String>) : Command() {
} catch (e: ShowHelpException) {
println(e.printUserMessage(output, "grep", 80))
}
return null;
return null
}

private fun grep(input: Reader, output: PipedWriter, args: PipedGrepArgs) {
var linesToWrite = 0;
var linesToWrite = 0
input.forEachLine {
val matchString = if (args.ignoreCase) it.toLowerCase() else it
val matchPattern = if (args.ignoreCase) args.pattern.toLowerCase() else args.pattern
val wordEnd = if (args.wordRegexp) "(\\s|^|\$)" else ""
val regex = Regex(".*?$wordEnd$matchPattern$wordEnd.*")
if (matchString.matches(regex)) {
output.write("$it\n")
linesToWrite = args.afterContext;
linesToWrite = args.afterContext
} else if (linesToWrite > 0) {
output.write("$it\n")
linesToWrite--
Expand All @@ -67,5 +69,5 @@ private open class PipedGrepArgs(parser: ArgParser) {
}

private class GrepArgs(parser: ArgParser) : PipedGrepArgs(parser) {
val file by parser.positional("FILE") { FileReader(this) }
val file by parser.positional("FILE") { this }
}
37 changes: 37 additions & 0 deletions bash/src/main/kotlin/hse/nedikov/bash/logic/commands/Ls.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package hse.nedikov.bash.logic.commands

import hse.nedikov.bash.Environment
import hse.nedikov.bash.logic.Command
import java.io.File
import java.io.PipedReader
import java.io.PipedWriter

/**
* ls command which prints all files in current directory
*/
class Ls(private val arguments: ArrayList<String>, private val env: Environment) : Command() {
/**
* Prints current directory
*/
override fun execute(input: PipedReader, output: PipedWriter) {
return execute(output)
}

/**
* Prints current directory
*/
override fun execute(output: PipedWriter) {
if (arguments.size > 1) {
throw RuntimeException("ls: too many arguments")
}
printFile(env.getCanonicalFile(arguments.getOrElse(0) { "./" }), output)
}

private fun printFile(file: File, output: PipedWriter) {
when {
file.isFile -> output.write(file.name + System.lineSeparator())
file.isDirectory -> file.listFiles().sorted().forEach { output.write(it.name + System.lineSeparator()) }
else -> throw RuntimeException("ls: file or directory not found" + System.lineSeparator())
}
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
package hse.nedikov.bash.logic.commands

import hse.nedikov.bash.logic.Command
import hse.nedikov.bash.Environment
import hse.nedikov.bash.logic.EnvironmentCommand
import java.io.*
import java.util.concurrent.Executors
import java.io.InputStreamReader
import java.io.BufferedReader
import java.util.*
import java.util.concurrent.TimeUnit

/**
* Class for calling commands in outer interpreter
* @param name name of the command
*/
class OuterCommand(private val name: String, private val arguments: ArrayList<String>) : Command() {
class OuterCommand(private val name: String,
private val arguments: ArrayList<String>,
override val env: Environment) : EnvironmentCommand(env) {
/**
* Calls the command in outer interpreter and print theirs output or error to the output
* in case when the command is executed in less than 10 seconds
Expand All @@ -22,7 +21,7 @@ class OuterCommand(private val name: String, private val arguments: ArrayList<St

val processWriter = OutputStreamWriter(process.outputStream)
input.readLines().forEach { processWriter.write("$it\n") }
processWriter.flush()
processWriter.close()

startProcess(process, output)
}
Expand All @@ -32,12 +31,9 @@ class OuterCommand(private val name: String, private val arguments: ArrayList<St
* in case when the command is executed in less than 10 seconds
*/
private fun startProcess(process: Process, output: PipedWriter) {
val streamGobbler = StreamGobbler(process.inputStream) { s -> output.write("$s\n") }
val errorGobbler = StreamGobbler(process.errorStream) { s -> output.write("$s\n") }
val executor = Executors.newSingleThreadExecutor()
executor.submit(streamGobbler)
executor.submit(errorGobbler)
process.waitFor(10, TimeUnit.SECONDS)
process.inputStream.bufferedReader().copyTo(output)
process.errorStream.bufferedReader().copyTo(output)
process.waitFor()
}

override fun execute(output: PipedWriter) {
Expand All @@ -46,15 +42,9 @@ class OuterCommand(private val name: String, private val arguments: ArrayList<St
}

private fun createProcess(): Process {
val environmentStart = if (isWindows) "cmd.exe /c" else "sh -c"
val environmentStart = if (isWindows) "cmd.exe /c" else ""
val command = StringJoiner(" ", "$name ", "").also { joiner -> arguments.forEach { joiner.add(it) } }.toString()
return Runtime.getRuntime().exec("$environmentStart $command")
}

private class StreamGobbler(private val inputStream: InputStream, private val consumer: (String) -> Unit) : Runnable {
override fun run() {
BufferedReader(InputStreamReader(inputStream)).lines().forEach(consumer)
}
return Runtime.getRuntime().exec("$environmentStart $command", null, env.getCanonicalFile("./"))
}

companion object {
Expand Down
7 changes: 4 additions & 3 deletions bash/src/main/kotlin/hse/nedikov/bash/logic/commands/Pwd.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package hse.nedikov.bash.logic.commands

import hse.nedikov.bash.logic.Command
import hse.nedikov.bash.Environment
import hse.nedikov.bash.logic.EnvironmentCommand
import java.io.*

/**
* pwd command which prints current working directory
*/
class Pwd : Command() {
class Pwd(override val env: Environment) : EnvironmentCommand(env) {
/**
* Prints current working directory to the output
*/
Expand All @@ -19,7 +20,7 @@ class Pwd : Command() {
* Prints current working directory to the output
*/
override fun execute(output: PipedWriter) {
output.write(System.getProperty("user.dir") + "\n")
output.write(env.getCanonicalPath("./") + System.lineSeparator())
}

}
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package hse.nedikov.bash.logic.commands

import hse.nedikov.bash.logic.Command
import hse.nedikov.bash.Environment
import hse.nedikov.bash.logic.EnvironmentCommand
import java.io.*
import java.lang.Exception

/**
* wc command which calculates count of lines, words and bytes in files or input
*/
class WordCount(private val arguments: ArrayList<String>) : Command() {
class WordCount(private val arguments: ArrayList<String>, override val env: Environment) : EnvironmentCommand(env) {
/**
* Calculates count of lines, words and bytes in input if arguments is empty and in files otherwise
*/
Expand All @@ -27,7 +28,7 @@ class WordCount(private val arguments: ArrayList<String>) : Command() {
val result = WCResult()
for (arg in arguments) {
try {
val r = calcInput(FileReader(arg))
val r = calcInput(FileReader(env.getCanonicalPath(arg)))
output.write("${r.lines} ${r.words} ${r.bytes} $arg\n")
} catch (e: Exception) {
output.write("wc: ${e.message}\n")
Expand Down
Loading