/**
* Copyright (C) 2010-2017 Structr GmbH
*
* This file is part of Structr <http://structr.org>.
*
* Structr is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Structr is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Structr. If not, see <http://www.gnu.org/licenses/>.
*/
package org.structr.files.ssh.shell;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.structr.common.Permission;
import org.structr.common.error.FrameworkException;
import org.structr.core.app.App;
import org.structr.core.app.StructrApp;
import org.structr.core.graph.Tx;
import org.structr.dynamic.File;
import org.structr.files.ssh.StructrShellCommand;
import org.structr.web.entity.AbstractFile;
import org.structr.web.entity.Folder;
/**
*
*
*/
public class CatCommand extends NonInteractiveShellCommand {
private static final Logger logger = LoggerFactory.getLogger(CatCommand.class.getName());
@Override
public void execute(final StructrShellCommand parent) throws IOException {
final App app = StructrApp.getInstance();
try (final Tx tx = app.tx()) {
final Folder currentFolder = parent.getCurrentFolder();
if (currentFolder != null) {
listFolder(parent, currentFolder.getProperty(AbstractFile.children));
} else {
listFolder(parent, app.nodeQuery(AbstractFile.class).and(AbstractFile.parent, null).getAsList());
}
tx.success();
} catch (FrameworkException fex) {
logger.warn("", fex);
}
}
@Override
public void handleTabCompletion(final StructrShellCommand parent, final String line, final int tabCount) throws IOException {
if (line.contains(" ") && line.length() >= 3) {
String incompletePath = line.substring(line.indexOf(" ") + 1);
Folder baseFolder = null;
String lastPathPart = null;
if (incompletePath.startsWith("\"")) {
incompletePath = incompletePath.substring(1);
}
final App app = StructrApp.getInstance();
if ("..".equals(incompletePath)) {
term.handleCharacter('/');
return;
}
if (incompletePath.startsWith("/")) {
incompletePath = incompletePath.substring(1);
} else {
baseFolder = parent.getCurrentFolder();
}
// identify full path parts and find folders
final String[] parts = incompletePath.split("[/]+");
final int partCount = parts.length;
try (final Tx tx = app.tx()) {
// only a single path part
if (partCount == 1) {
lastPathPart = parts[0];
} else {
lastPathPart = parts[partCount-1];
// more than a single path part, find preceding folders
for (int i=0; i<partCount-1; i++) {
// skip empty path parts
if (StringUtils.isNotBlank(parts[i])) {
baseFolder = app.nodeQuery(Folder.class).and(AbstractFile.parent, baseFolder).and(Folder.name, parts[i]).getFirst();
if (baseFolder == null) {
return;
}
}
}
}
final List<AbstractFile> allFiles = new LinkedList<>();
final List<AbstractFile> files = new LinkedList<>();
if (baseFolder != null) {
allFiles.addAll(baseFolder.getProperty(AbstractFile.children));
} else {
allFiles.addAll(app.nodeQuery(AbstractFile.class).and(File.parent, null).getAsList());
}
for (final AbstractFile file : allFiles) {
if (file.getName().startsWith(lastPathPart)) {
files.add(file);
}
}
if (files.size() > 1) {
// only display autocomplete suggestions after second tab
if (tabCount > 1) {
displayAutocompleteSuggestions(parent, files, line);
}
} else if (!files.isEmpty()) {
final AbstractFile file = files.get(0);
if (file instanceof Folder) {
final Folder folder = (Folder)file;
if (parent.isAllowed(folder, Permission.read, false)) {
if (lastPathPart.equals(folder.getName())) {
// only display autocomplete suggestions after second tab
if (tabCount > 1) {
displayAutocompleteSuggestions(parent, folder.getProperty(Folder.children), line);
} else {
if (!line.endsWith("/")) {
term.handleCharacter('/');
}
}
} else {
displayAutocompleteItem(folder, lastPathPart);
}
}
} else {
final AbstractFile existingFile = files.get(0);
if (parent.isAllowed(existingFile, Permission.read, false)) {
if (!lastPathPart.equals(existingFile.getName())) {
displayAutocompleteItem(existingFile, lastPathPart);
}
}
}
}
tx.success();
} catch (FrameworkException fex) {
logger.warn("", fex);
}
}
}
// ----- private methods -----
private void listFolder(final StructrShellCommand parent, final List<AbstractFile> folder) throws FrameworkException, IOException {
for (final AbstractFile child : folder) {
if (parent.isAllowed(child, Permission.read, false)) {
if (child instanceof Folder) {
term.setBold(true);
term.setTextColor(4);
term.print(child.getName() + " ");
term.setTextColor(7);
term.setBold(false);
} else {
term.print(child.getName() + " ");
}
}
}
if (!folder.isEmpty()) {
term.println();
}
}
private void displayAutocompleteItem(final AbstractFile file, final String part) throws IOException {
final String name = file.getName();
if (name.startsWith(part)) {
final String remainder = file.getName().substring(part.length());
if (StringUtils.isNotEmpty(remainder)) {
term.handleString(remainder);
if (file instanceof Folder) {
term.handleCharacter('/');
}
}
}
}
private void displayAutocompleteSuggestions(final StructrShellCommand parent, final List<AbstractFile> files, final String line) throws IOException {
if (!files.isEmpty()) {
final StringBuilder buf = new StringBuilder();
for (final AbstractFile file : files) {
if (parent.isAllowed(file, Permission.read, false)) {
buf.append(file.getName());
if (file instanceof Folder) {
buf.append("/ ");
}
}
}
if (buf.length() > 0) {
term.println();
term.print(buf.toString());
term.println();
parent.displayPrompt();
term.print(line);
}
}
}
}