Skip to content
Merged
2 changes: 1 addition & 1 deletion CheckList.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@
- [x] date
- [ ] shutdown
- [ ] choice
- [ ] timeout
- [x] timeout
- [ ] call
- [ ] start
- [ ] taskkill
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/mycmd/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ private CommandNames() {}
private static final String TASKLIST = "tasklist";
private static final String TELNET = "telnet";
private static final String TIME = "time";
private static final String TIMEOUT = "timeout";
private static final String TITLE = "title";
private static final String TOUCH = "touch";
private static final String TREE = "tree";
Expand Down Expand Up @@ -138,6 +139,7 @@ private static void registerCommands(Map<String, Command> commands) {
commands.put(CommandNames.TASKLIST, new TasklistCommand());
commands.put(CommandNames.TELNET, new TelnetCommand());
commands.put(CommandNames.TIME, new TimeCommand());
commands.put(CommandNames.TIMEOUT, new TimeoutCommand());
commands.put(CommandNames.TITLE, new TitleCommand());
commands.put(CommandNames.TOUCH, new TouchCommand());
commands.put(CommandNames.TREE, new TreeCommand());
Expand Down
149 changes: 149 additions & 0 deletions src/main/java/com/mycmd/commands/TimeoutCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package com.mycmd.commands;

import com.mycmd.Command;
import com.mycmd.ShellContext;
import java.io.IOException;

public class TimeoutCommand implements Command {
@Override
public void execute(String[] args, ShellContext context) {
int seconds = -1;
boolean hasSecondsArg = false;
boolean hasSlashT = false;
boolean noBreak = false;

for (int i = 0; i < args.length; i++) {
if (args[i].equalsIgnoreCase("/t")
&& i + 1 < args.length
&& args[i + 1].matches("[+-]?\\d+")) {
if (hasSlashT) {
System.out.println(
"Error: Invalid syntax. '/t' option is not allowed more than '1' time(s).");
return;
}
seconds = Integer.parseInt(args[i + 1]);
i++;
hasSecondsArg = true;
hasSlashT = true;
} else if (args[i].equalsIgnoreCase("/nobreak")) {
if (noBreak) {
System.out.println(
"Error: Invalid syntax. '/nobreak' option is not allowed more than '1' time(s).");
return;
}
noBreak = true;
} else if (args[i].matches("[+-]?\\d+")) {
if (hasSecondsArg) {
System.out.println(
"Error: Invalid syntax. Default option is not allowed more than '1' time(s).");
return;
}
seconds = Integer.parseInt(args[i]);
hasSecondsArg = true;
} else if (args[i].equalsIgnoreCase("/t")) {
System.out.println("Error: Invalid syntax. Value expected for '/t'.");
return;
}
}

if (!hasSecondsArg) {
System.out.println("Error: Invalid syntax. Seconds value is required.");
return;
}

if (seconds < -1 || seconds > 99999) {
System.out.println(
"Error: Invalid value for timeout specified. Valid range is 0-99999 seconds.");
return;
}

if (seconds == -1) {
System.out.println();
PauseCommand pauseCmd = new PauseCommand();
pauseCmd.execute(new String[0], context);
return;
}

AtomicBoolean interrupted = new AtomicBoolean(false);
Thread inputThread = null;

if (!noBreak) {
inputThread =
new Thread(
() -> {
try {
int r;
// Read until a newline is encountered so we only exit on Enter
while ((r = System.in.read()) != -1) {
if (r == '\n') {
interrupted.set(true);
break;
}
}
} catch (IOException e) {
// Ignore: if System.in is closed or an I/O error occurs we
// cannot reliably wait for Enter; treat as no-interrupt.
}
});
inputThread.setDaemon(true);
inputThread.start();
}
System.out.println();

for (; seconds > 0; seconds--) {
if (!noBreak && interrupted.get()) {
System.out.println("\r");
System.out.println();
if (inputThread != null && inputThread.isAlive()) {
inputThread.interrupt();
}
return;
}

System.out.print(
"\rWaiting for "
+ seconds
+ " seconds, press "
+ (noBreak ? "CTRL+C to quit ..." : "enter key to continue ..."));
System.out.flush();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
if (noBreak) {
continue;
}
System.out.println();
Thread.currentThread().interrupt();
break;
}
}

try {
// Drain any remaining bytes so subsequent commands don't immediately see
// leftover input. This is a best-effort drain; System.in.available() may
// not be supported on all streams, but for typical console streams it helps.
while (System.in.available() > 0) {
System.in.read();
}
} catch (IOException e) {
// Ignore: if we can't drain the stream it's non-fatal; any leftover input
// will be handled by the next read and is acceptable.
}

System.out.println("\r");
System.out.println();
}

@Override
public String description() {
return "Sets a timeout for command execution.";
}

@Override
public String usage() {
return "timeout <seconds>\n"
+ "timeout /t <seconds>\n"
+ "timeout /t <seconds> /nobreak\n"
+ "timeout /t -1";
}
}
Loading