Skip to content

Commit 207d805

Browse files
committed
Merge upstream: resolved TimeoutCommand conflict, kept robust implementation
2 parents 1b056d2 + add7dbb commit 207d805

File tree

3 files changed

+233
-2
lines changed

3 files changed

+233
-2
lines changed

.github/workflows/formatter.yml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: Apply Spotless Formatting
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- main
7+
push:
8+
branches:
9+
- main
10+
11+
jobs:
12+
format:
13+
runs-on: ubuntu-latest
14+
concurrency:
15+
group: ${{ github.workflow }}-${{ github.head_ref || github.ref_name }}
16+
cancel-in-progress: true
17+
18+
steps:
19+
- name: Checkout repository
20+
uses: actions/checkout@v4
21+
with:
22+
ref: ${{ github.head_ref || github.ref_name }}
23+
fetch-depth: 0
24+
25+
- name: Set up JDK 17
26+
uses: actions/setup-java@v4
27+
with:
28+
java-version: '17'
29+
distribution: 'temurin'
30+
cache: maven
31+
32+
- name: Apply Spotless formatting
33+
run: mvn spotless:apply
34+
35+
- name: Commit changes if any
36+
run: |
37+
git config --global user.name "github-actions[bot]"
38+
git config --global user.email "github-actions[bot]@users.noreply.github.com"
39+
git add .
40+
if ! git diff --quiet --staged; then
41+
git commit -m "Apply Spotless formatting"
42+
git pull --rebase origin ${{ github.head_ref || github.ref_name }}
43+
git push origin HEAD:${{ github.head_ref || github.ref_name }}
44+
else
45+
echo "No formatting changes to commit."
46+
fi

CheckList.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,8 @@
115115
- [x] color
116116
- [x] time
117117
- [x] date
118-
- [x] shutdown
119-
- [x] choice
118+
- [ ] shutdown
119+
- [ ] choice
120120
- [x] timeout
121121
- [ ] call
122122
- [x] start

src/main/java/com/mycmd/commands/TimeoutCommand.java

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.mycmd.Command;
44
import com.mycmd.ShellContext;
5+
<<<<<<< HEAD
56

67
import java.io.IOException;
78

@@ -89,5 +90,189 @@ public String description() {
8990
@Override
9091
public String usage() {
9192
return "timeout [/T] seconds [/NOBREAK]";
93+
=======
94+
import java.io.IOException;
95+
import java.util.concurrent.atomic.AtomicBoolean;
96+
97+
public class TimeoutCommand implements Command {
98+
/**
99+
* Execute the timeout command.
100+
*
101+
* <p>This command will wait for the specified number of seconds before continuing. If the user
102+
* presses Enter before the timeout expires, the command will terminate immediately.
103+
*
104+
* @param args The arguments to the command.
105+
* @param context The context of the shell.
106+
* @throws IOException If an I/O error occurs.
107+
*/
108+
@Override
109+
public void execute(String[] args, ShellContext context) {
110+
int seconds = -1;
111+
boolean hasSecondsArg = false;
112+
boolean hasSlashT = false;
113+
boolean noBreak = false;
114+
115+
for (int i = 0; i < args.length; i++) {
116+
if (args[i].equalsIgnoreCase("/t")
117+
&& i + 1 < args.length
118+
&& args[i + 1].matches("[+-]?\\d+")) {
119+
if (hasSlashT) {
120+
System.out.println(
121+
"Error: Invalid syntax. '/t' option is not allowed more than '1' time(s).");
122+
return;
123+
}
124+
seconds = Integer.parseInt(args[i + 1]);
125+
i++;
126+
hasSecondsArg = true;
127+
hasSlashT = true;
128+
} else if (args[i].equalsIgnoreCase("/nobreak")) {
129+
if (noBreak) {
130+
System.out.println(
131+
"Error: Invalid syntax. '/nobreak' option is not allowed more than '1' time(s).");
132+
return;
133+
}
134+
noBreak = true;
135+
} else if (args[i].matches("[+-]?\\d+")) {
136+
if (hasSecondsArg) {
137+
System.out.println(
138+
"Error: Invalid syntax. Default option is not allowed more than '1' time(s).");
139+
return;
140+
}
141+
seconds = Integer.parseInt(args[i]);
142+
hasSecondsArg = true;
143+
} else if (args[i].equalsIgnoreCase("/t")) {
144+
System.out.println("Error: Invalid syntax. Value expected for '/t'.");
145+
return;
146+
} else {
147+
System.out.println("Error: Invalid syntax. Unrecognized argument: " + args[i]);
148+
return;
149+
}
150+
}
151+
152+
if (!hasSecondsArg) {
153+
System.out.println("Error: Invalid syntax. Seconds value is required.");
154+
return;
155+
}
156+
157+
if (seconds < -1 || seconds > 99999) {
158+
System.out.println(
159+
"Error: Invalid value for timeout specified. Valid range is 0-99999 seconds.");
160+
return;
161+
}
162+
163+
if (seconds == -1) {
164+
System.out.println();
165+
PauseCommand pauseCmd = new PauseCommand();
166+
pauseCmd.execute(new String[0], context);
167+
return;
168+
}
169+
170+
AtomicBoolean interrupted = new AtomicBoolean(false);
171+
AtomicBoolean stopInput = new AtomicBoolean(false);
172+
Thread inputThread = null;
173+
174+
if (!noBreak) {
175+
inputThread =
176+
new Thread(
177+
() -> {
178+
try {
179+
// Poll non-blocking so we can stop the thread
180+
// deterministically.
181+
while (!stopInput.get()) {
182+
if (System.in.available() > 0) {
183+
int r = System.in.read();
184+
// Treat CR or LF as Enter across platforms
185+
if (r == '\n' || r == '\r') {
186+
interrupted.set(true);
187+
break;
188+
}
189+
} else {
190+
try {
191+
Thread.sleep(25);
192+
} catch (InterruptedException ie) {
193+
Thread.currentThread().interrupt();
194+
break;
195+
}
196+
}
197+
}
198+
} catch (IOException e) {
199+
// Best-effort only; fall through.
200+
}
201+
});
202+
inputThread.setDaemon(true);
203+
inputThread.start();
204+
}
205+
System.out.println();
206+
207+
for (; seconds > 0; seconds--) {
208+
if (!noBreak && interrupted.get()) {
209+
System.out.println("\r");
210+
System.out.println();
211+
if (inputThread != null) {
212+
stopInput.set(true);
213+
try {
214+
inputThread.join(200);
215+
} catch (InterruptedException ie) {
216+
Thread.currentThread().interrupt();
217+
}
218+
}
219+
return;
220+
}
221+
222+
System.out.print(
223+
"\rWaiting for "
224+
+ seconds
225+
+ " seconds, press "
226+
+ (noBreak ? "CTRL+C to quit ..." : "enter key to continue ..."));
227+
System.out.flush();
228+
try {
229+
Thread.sleep(1000);
230+
} catch (InterruptedException e) {
231+
if (noBreak) {
232+
continue;
233+
}
234+
System.out.println();
235+
Thread.currentThread().interrupt();
236+
break;
237+
}
238+
}
239+
240+
// Normal completion: stop the input thread before draining.
241+
if (!noBreak && inputThread != null) {
242+
stopInput.set(true);
243+
try {
244+
inputThread.join(200);
245+
} catch (InterruptedException ie) {
246+
Thread.currentThread().interrupt();
247+
}
248+
}
249+
try {
250+
// Drain any remaining bytes so subsequent commands don't immediately see
251+
// leftover input. This is a best-effort drain; System.in.available() may
252+
// not be supported on all streams, but for typical console streams it helps.
253+
while (System.in.available() > 0) {
254+
System.in.read();
255+
}
256+
} catch (IOException e) {
257+
// Ignore: if we can't drain the stream it's non-fatal; any leftover input
258+
// will be handled by the next read and is acceptable.
259+
}
260+
261+
System.out.println("\r");
262+
System.out.println();
263+
}
264+
265+
@Override
266+
public String description() {
267+
return "Sets a timeout for command execution.";
268+
}
269+
270+
@Override
271+
public String usage() {
272+
return "timeout <seconds>\n"
273+
+ "timeout /t <seconds>\n"
274+
+ "timeout /t <seconds> /nobreak\n"
275+
+ "timeout /t -1";
276+
>>>>>>> upstream/main
92277
}
93278
}

0 commit comments

Comments
 (0)