135 lines
5.7 KiB
Kotlin
135 lines
5.7 KiB
Kotlin
package solutions.tretter.githugandroid
|
|
|
|
import java.io.File
|
|
|
|
internal class GitHelperCommands(
|
|
private val processRunner: GitProcessRunner,
|
|
) {
|
|
fun executeExecutableShortcut(
|
|
sandboxRoot: File,
|
|
workingDir: File,
|
|
currentRepo: RepoState,
|
|
tokens: List<String>,
|
|
): Pair<RepoState, List<String>>? {
|
|
val executable = tokens.firstOrNull() ?: return null
|
|
if (!executable.startsWith("./") || executable.length <= 2) return null
|
|
return executeShellScript(sandboxRoot, workingDir, currentRepo, listOf("sh", executable) + tokens.drop(1))
|
|
}
|
|
|
|
fun execute(
|
|
sandboxRoot: File,
|
|
workingDir: File,
|
|
currentRepo: RepoState,
|
|
tokens: List<String>,
|
|
): Pair<RepoState, List<String>> {
|
|
return when (tokens.first()) {
|
|
"ls", "dir" -> currentRepo to workingDir.listFiles()
|
|
?.sortedBy { it.name }
|
|
?.map { it.name }
|
|
.orEmpty()
|
|
"pwd" -> currentRepo to listOf(
|
|
"/sandbox" + if (workingDir == sandboxRoot) "" else "/${workingDir.relativeTo(sandboxRoot).path}"
|
|
)
|
|
"cat" -> {
|
|
val target = tokens.getOrNull(1) ?: return currentRepo to listOf("usage: cat <file>")
|
|
val file = File(workingDir, target)
|
|
if (!file.exists() || file.isDirectory) currentRepo to listOf("cat: $target: No such file")
|
|
else currentRepo to file.readLines().ifEmpty { listOf("") }
|
|
}
|
|
"sh" -> executeShellScript(sandboxRoot, workingDir, currentRepo, tokens)
|
|
"touch" -> {
|
|
val target = tokens.getOrNull(1) ?: return currentRepo to listOf("usage: touch <file>")
|
|
val file = File(workingDir, target)
|
|
if (file.exists()) {
|
|
currentRepo to listOf("$target already exists")
|
|
} else {
|
|
file.parentFile?.mkdirs()
|
|
file.writeText("")
|
|
currentRepo to emptyList()
|
|
}
|
|
}
|
|
"mkdir", "md" -> {
|
|
val target = tokens.getOrNull(1) ?: return currentRepo to listOf("usage: mkdir <dir>")
|
|
val dir = File(workingDir, target)
|
|
if (dir.exists()) currentRepo to listOf("mkdir: $target: File exists") else {
|
|
dir.mkdirs()
|
|
currentRepo to emptyList()
|
|
}
|
|
}
|
|
"cd", "cd.." -> {
|
|
val target = if (tokens.first() == "cd..") ".." else tokens.getOrNull(1)
|
|
?: return currentRepo to listOf("usage: cd <dir>")
|
|
val dir = File(workingDir, target).canonicalFile
|
|
when {
|
|
!dir.exists() -> currentRepo to listOf("cd: $target: does not exist")
|
|
!dir.isDirectory -> currentRepo to listOf("cd: $target: Not a directory")
|
|
!dir.path.startsWith(sandboxRoot.path) -> currentRepo to listOf("cd: $target: Permission denied")
|
|
else -> {
|
|
val relativeDir = sandboxRoot.toPath().relativize(dir.toPath()).toString().ifEmpty { "." }
|
|
currentRepo.copy(currentDir = relativeDir) to emptyList()
|
|
}
|
|
}
|
|
}
|
|
"rm", "del" -> {
|
|
val targets = tokens.drop(1)
|
|
if (targets.isEmpty()) return currentRepo to listOf("usage: rm <path>")
|
|
val errors = targets.mapNotNull { target ->
|
|
val file = File(workingDir, target)
|
|
if (!file.exists()) {
|
|
"${tokens.first()}: $target: No such file or directory"
|
|
} else {
|
|
file.deleteRecursively()
|
|
null
|
|
}
|
|
}
|
|
currentRepo to errors
|
|
}
|
|
"echo" -> executeEcho(workingDir, currentRepo, tokens)
|
|
else -> currentRepo to listOf("Command not supported in prototype runtime. Try a git command or helper command.")
|
|
}
|
|
}
|
|
|
|
private fun executeShellScript(
|
|
sandboxRoot: File,
|
|
workingDir: File,
|
|
currentRepo: RepoState,
|
|
tokens: List<String>,
|
|
): Pair<RepoState, List<String>> {
|
|
val script = tokens.getOrNull(1) ?: return currentRepo to listOf("usage: sh <script>")
|
|
val scriptFile = File(workingDir, script).canonicalFile
|
|
if (!scriptFile.path.startsWith(sandboxRoot.path)) {
|
|
return currentRepo to listOf("sh: $script: Permission denied")
|
|
}
|
|
if (!scriptFile.isFile) {
|
|
return currentRepo to listOf("sh: $script: No such file")
|
|
}
|
|
val result = processRunner.runShellProcess(
|
|
File(processRunner.shellExecutable()),
|
|
workingDir,
|
|
tokens.drop(1),
|
|
)
|
|
return currentRepo to result.outputLines
|
|
}
|
|
|
|
private fun executeEcho(workingDir: File, currentRepo: RepoState, tokens: List<String>): Pair<RepoState, List<String>> {
|
|
val redirectIndex = tokens.indexOfFirst { it == ">" || it == ">>" }
|
|
if (redirectIndex == -1 || redirectIndex == tokens.lastIndex) {
|
|
return currentRepo to listOf(tokens.drop(1).joinToString(" "))
|
|
}
|
|
|
|
val file = File(workingDir, tokens[redirectIndex + 1])
|
|
file.parentFile?.mkdirs()
|
|
val content = tokens.subList(1, redirectIndex).joinToString(" ")
|
|
if (tokens[redirectIndex] == ">>") {
|
|
if (file.exists() && file.length() > 0L) {
|
|
file.appendText("\n$content")
|
|
} else {
|
|
file.writeText(content)
|
|
}
|
|
} else {
|
|
file.writeText(content)
|
|
}
|
|
return currentRepo to emptyList()
|
|
}
|
|
}
|