Fixed indentation.
[AROS.git] / workbench / c / WaitX.c
blob4740dad519416f704fa391aafca225d8aa8565d1
1 /*
2 Copyright © 2010, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Execute a command after a given time
6 Lang: English
7 */
9 /******************************************************************************
11 NAME
13 WaitX
15 SYNOPSIS
17 D=DATE/K,T=TIME/K,YR=YEARS/K/N,MN=MONTHS/K/N,DY=DAYS/K/N,H=HOURS/K/N,
18 M=MINS/K/N,S=SECS/K/N,L=LOOP/K/N,A=ALWAYS/S,V=VERBOSE/S,HELP/S,CMDLINE/F
20 LOCATION
24 FUNCTION
26 WaitX will wait for a given amount of time and then it
27 will execute the given command.
29 INPUTS
31 D=DATE -- Waits until DATE has been reached
32 T=TIME -- Waits until TIME has been reached
33 YR=YEARS -- How many years to wait
34 MN=MONTHS -- How many months to wait
35 DY=DAYS -- How many days to wait
36 H=HOURS -- How many hours to wait
37 M=MINS -- How many minutes to wait
38 S=SECS -- How many seconds to wait
39 L=LOOP -- How many times to execute CMDLINE
40 A=ALWAYS -- Execute CMDLINE every set interval/time/date
41 V=VERBOSE -- Print extra info on what waitx is doing
43 EXAMPLE
45 $ waitx TIME=12:34:12 echo "this is an example"
46 waitx waits until 12:34:12 is reached and will execute echo
48 $ waitx H=5 M=36 echo "this is an example"
49 waitx will wait 5 hours and 36 minutes and execute echo
51 $ waitx HOURS=2 MINS=12 SECS=59
52 waitx will wait 2 hours, 12 minutes and 59 seconds and then
53 returns to the prompt
55 $ waitx DY=1 L=5 echo "this is an example"
56 waitx will wait 1 day and execute echo,
57 then repeat this a total of 5 times
59 $ waitx M=15 ALWAYS echo "this is an example"
60 waitx will execute echo every 15 minutes
62 $ waitx D=12/9 T=16 L=0 echo "this is an example"
63 waitx will wait until September 12th 16:00 and execute echo,
64 and repeat forever
66 $ waitx echo "this is an example"
67 waitx will execute echo immediatly
69 RESULT
71 NOTES
73 Based on Public Domain WaitX:
74 http://aminet.net/package/util/cli/waitx
75 Programming: Sigbjørn Skjæret <cisc@c2i.net>
76 Idea & Docs: Nicholas Stallard <snowy@netphile.de>
78 EXAMPLE
80 BUGS
82 Will not return to prompt while waiting. This is intended.
84 SEE ALSO
86 INTERNALS
88 ******************************************************************************/
90 #define __USE_SYSBASE
91 #include <string.h>
92 #include <proto/dos.h>
93 #include <proto/exec.h>
94 #include <exec/memory.h>
95 #include <proto/timer.h>
96 #include <proto/utility.h>
97 #include <utility/date.h>
100 struct Interval
102 ULONG set;
103 ULONG years;
104 ULONG months;
105 ULONG seconds;
108 const char Version[] = "$VER: WaitX 2.1 (16.04.2008)";
110 int strtoi(STRPTR string);
112 LONG MainEntry(struct ExecBase *SysBase);
114 __startup static AROS_ENTRY(int, Start,
115 AROS_UFHA(char *, argstr, A0),
116 AROS_UFHA(ULONG, argsize, D0),
117 struct ExecBase *, sBase)
119 AROS_USERFUNC_INIT
120 return MainEntry(sBase);
121 AROS_USERFUNC_EXIT
125 LONG MainEntry(struct ExecBase *SysBase)
127 struct DosLibrary *DOSBase = NULL;
128 struct UtilityBase *UtilityBase = NULL;
129 struct Library *TimerBase;
131 BYTE TimerDevice = -1;
132 struct ClockData *clock = NULL;
133 struct Interval *interval = NULL;
134 struct MsgPort *TimerPort = NULL;
135 struct timerequest *TimerReq = NULL;
136 ULONG i, loop, step, unit, seconds = 0, signal = 0, timesig = 0, usersig = SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D | SIGBREAKF_CTRL_E;
138 BPTR StdErr = BNULL, StdIn;
139 char ProgName[256];
140 struct RDArgs *rdargs = NULL;
141 STRPTR Template = "D=DATE/K,T=TIME/K,YR=YEARS/K/N,MN=MONTHS/K/N,DY=DAYS/K/N,H=HOURS/K/N,M=MINS/K/N,S=SECS/K/N,L=LOOP/K/N,A=ALWAYS/S,V=VERBOSE/S,HELP/S,CMDLINE/F";
143 enum
145 TEM_DATE,
146 TEM_TIME,
147 TEM_YEARS,
148 TEM_MONTHS,
149 TEM_DAYS,
150 TEM_HOURS,
151 TEM_MINS,
152 TEM_SECS,
153 TEM_LOOP,
154 TEM_ALWAYS,
155 TEM_VERBOSE,
156 TEM_HELP,
157 TEM_CMDLINE,
158 TEM_NUMARGS
161 IPTR ArgArray[TEM_NUMARGS], ret;
162 STRPTR cmdline;
163 STRPTR tmpptr;
165 ret = 20; /* Fatal error if something fails here */
166 if (!(DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 36)))
167 goto exit;
168 if (!(UtilityBase = (struct UtilityBase *)OpenLibrary("utility.library", 36)))
169 goto exit;
170 if (!(StdErr = Open("CONSOLE:", MODE_NEWFILE)))
171 goto exit;
172 ret = 0; /* Clear return */
174 GetProgramName(ProgName, sizeof(ProgName));
175 for (i=0; i<TEM_NUMARGS; ArgArray[i++]=0)
177 rdargs = ReadArgs(Template, ArgArray, NULL);
179 if (ArgArray[TEM_HELP] || !rdargs)
181 FPrintf(StdErr, "%s\n\n", &Version[6]);
182 FPrintf(StdErr, "Usage: %s [DATE=<DD/MM/YYYY>] [TIME=<HH:MM:SS>] <commandline>\n", ProgName);
183 FPrintf(StdErr, "Usage: %s [HOURS=<hours>] [MINS=<mins>] [SECS=<secs>] <commandline>\n", ProgName);
184 goto exit;
187 loop = step = 1;
188 if (ArgArray[TEM_LOOP])
189 loop = *((ULONG *)ArgArray[TEM_LOOP]);
191 if (ArgArray[TEM_ALWAYS] || loop == 0)
193 loop = 1;
194 step = 0;
197 if (ArgArray[TEM_CMDLINE])
198 cmdline = (STRPTR)ArgArray[TEM_CMDLINE];
199 else
200 cmdline = ""; /* Prevented from Execute()ing later */
202 if (ArgArray[TEM_DATE] || ArgArray[TEM_TIME] || ArgArray[TEM_YEARS] || ArgArray[TEM_MONTHS])
204 unit = UNIT_WAITUNTIL;
205 if (!(clock = AllocMem(sizeof(struct ClockData), MEMF_PUBLIC | MEMF_CLEAR)))
207 FPrintf(StdErr, "Unable to allocate needed memory!\n");
208 ret = 10;
209 goto exit;
211 if (!(interval = AllocMem(sizeof(struct Interval), MEMF_PUBLIC | MEMF_CLEAR)))
213 FPrintf(StdErr, "Unable to allocate needed memory!\n");
214 ret = 10;
215 goto exit;
218 if (ArgArray[TEM_YEARS])
219 interval->years = *((ULONG *)ArgArray[TEM_YEARS]);
220 if (ArgArray[TEM_MONTHS])
221 interval->months = *((ULONG *)ArgArray[TEM_MONTHS]);
222 if (ArgArray[TEM_DAYS])
223 interval->seconds += *((ULONG *)ArgArray[TEM_DAYS]) * 86400;
224 if (ArgArray[TEM_HOURS])
225 interval->seconds += *((ULONG *)ArgArray[TEM_HOURS]) * 3600;
226 if (ArgArray[TEM_MINS])
227 interval->seconds += *((ULONG *)ArgArray[TEM_MINS]) * 60;
228 if (ArgArray[TEM_SECS])
229 interval->seconds += *((ULONG *)ArgArray[TEM_SECS]);
231 if (interval->years || interval->months || interval->seconds)
232 interval->set = 1;
234 else
236 unit = UNIT_VBLANK;
237 seconds = 0;
239 if (ArgArray[TEM_DAYS])
240 seconds += *((ULONG *)ArgArray[TEM_DAYS]) * 86400;
241 if (ArgArray[TEM_HOURS])
242 seconds += *((ULONG *)ArgArray[TEM_HOURS]) * 3600;
243 if (ArgArray[TEM_MINS])
244 seconds += *((ULONG *)ArgArray[TEM_MINS]) * 60;
245 if (ArgArray[TEM_SECS])
246 seconds += *((ULONG *)ArgArray[TEM_SECS]);
249 if (seconds > 0 || clock)
251 if (!(TimerPort = CreateMsgPort()))
253 FPrintf(StdErr, "Couldn't create Timer-MsgPort!\n");
254 ret = 10;
255 goto exit;
257 if (!(TimerReq = (struct timerequest *)CreateIORequest(TimerPort, sizeof(struct timerequest))))
259 FPrintf(StdErr, "Couldn't create Timer-IORequest!\n");
260 ret = 10;
261 goto exit;
263 if ((TimerDevice = OpenDevice(TIMERNAME, unit, (struct IORequest *)TimerReq, 0)))
265 FPrintf(StdErr, "Couldn't open %s!\n", TIMERNAME);
266 ret = 10;
267 goto exit;
270 TimerBase = (struct Library *)TimerReq->tr_node.io_Device;
271 timesig = 1L << TimerPort->mp_SigBit;
273 if (clock)
275 GetSysTime(&TimerReq->tr_time); /* Get current System Time */
276 Amiga2Date(TimerReq->tr_time.tv_secs, clock); /* Fill in current date/time as default */
278 if (ArgArray[TEM_DATE])
280 tmpptr = (STRPTR)ArgArray[TEM_DATE];
281 clock->mday = strtoi(tmpptr);
282 if ((tmpptr = strstr(tmpptr, "/")))
284 clock->month = strtoi(++tmpptr);
285 if ((tmpptr = strstr(tmpptr, "/")))
287 clock->year = strtoi(++tmpptr);
289 if (!interval->set)
291 interval->years = 1;
292 interval->set = 1;
295 else if (!interval->set)
297 interval->months = 1;
298 interval->set = 1;
301 if (clock->year < 100) /* Be nice to digit-challenged ppl. ;) */
303 if (clock->year < 78)
304 clock->year += 2000; /* If less than 78, assume 20xx */
305 else
306 clock->year += 1900;
309 else if (!interval->set)
311 interval->seconds = 86400;
312 interval->set = 1;
315 if (ArgArray[TEM_TIME])
317 tmpptr = (STRPTR)ArgArray[TEM_TIME];
318 clock->hour = strtoi(tmpptr);
319 clock->min = clock->sec = 0; /* Clear default time */
320 if ((tmpptr = strstr(tmpptr, ":")))
322 clock->min = strtoi(++tmpptr);
323 if ((tmpptr = strstr(tmpptr, ":")))
324 clock->sec = strtoi(++tmpptr);
328 if (!(seconds = CheckDate(clock)))
330 FPrintf(StdErr, "Invalid date/time!\n");
331 ret = 5;
332 goto exit;
334 if (seconds <= TimerReq->tr_time.tv_secs)
336 ULONG count = loop;
338 for (i=0; i<count; i+=step)
340 clock->year += interval->years;
341 clock->month += interval->months;
343 if (clock->month > 12) /* We need some annual magic */
345 clock->month %= 12;
346 clock->year += clock->month / 12;
349 if (!(seconds = CheckDate(clock)))
351 clock->mday -= 1; /* If date doesn't exist, try previous day */
352 if (!(seconds = CheckDate(clock)))
354 clock->mday -= 1;
355 if (!(seconds = CheckDate(clock)))
357 clock->mday -= 1;
358 if (!(seconds = CheckDate(clock)))
360 FPrintf(StdErr, "Invalid date/time!\n");
361 ret = 5;
362 goto exit;
363 } /* Give up */
364 clock->mday += 1;
366 clock->mday += 1;
368 clock->mday += 1; /* Restore day for future reference */
371 if (interval->seconds)
373 seconds += interval->seconds;
374 Amiga2Date(seconds, clock);
377 if (loop > 0 && step > 0)
378 loop--;
379 if (seconds > TimerReq->tr_time.tv_secs)
380 break;
383 if (seconds <= TimerReq->tr_time.tv_secs || loop == 0)
385 FPrintf(StdErr, "Date/time has already passed!\n");
386 ret = 5;
387 goto exit;
389 else if (ArgArray[TEM_VERBOSE])
391 FPrintf(StdErr, "Note: Schedule has been moved to %02lu/%02lu/%lu %02lu:%02lu:%02lu because the assigned date/time has already passed",
392 (ULONG)clock->mday, (ULONG)clock->month, (ULONG)clock->year, (ULONG)clock->hour, (ULONG)clock->min, (ULONG)clock->sec);
394 if (step == 0)
395 FPrintf(StdErr, ".\n");
396 else
397 FPrintf(StdErr, " (%lu loop(s) left).\n", loop);
403 if (IsInteractive((StdIn = Input())))
404 StdIn = BNULL; /* Don't use StdIn if it isn't redirected */
406 for (i=0; i<loop; i+=step)
408 if (seconds > 0)
410 TimerReq->tr_time.tv_secs = seconds;
411 TimerReq->tr_time.tv_micro = 0;
412 TimerReq->tr_node.io_Command = TR_ADDREQUEST;
414 SendIO((struct IORequest *)TimerReq);
415 signal = Wait(timesig | usersig);
417 if (signal & usersig)
419 AbortIO((struct IORequest *)TimerReq);
420 WaitIO((struct IORequest *)TimerReq);
421 SetSignal(0L, timesig | usersig); /* Clear signalbits since WaitIO most likely preserves them */
423 if (signal & SIGBREAKF_CTRL_C)
424 break;
428 if (cmdline[0] == '\0')
429 break; /* There's no point in going on */
430 if (!(signal & SIGBREAKF_CTRL_D))
432 if (!Execute(cmdline, StdIn, Output()))
434 FPrintf(StdErr, "Unable to execute \"%s\".\n", cmdline);
435 ret = 5;
436 goto exit;
439 if (seconds == 0)
440 break; /* Don't go into tight unbreakable loop */
442 if (clock)
444 clock->year += interval->years;
445 clock->month += interval->months;
447 if (clock->month > 12) /* We need some annual magic */
449 clock->month %= 12;
450 clock->year += clock->month / 12;
453 if (!(seconds = CheckDate(clock)))
455 clock->mday -= 1; /* If date doesn't exist, try previous day */
456 if (!(seconds = CheckDate(clock)))
458 clock->mday -= 1;
459 if (!(seconds = CheckDate(clock)))
461 clock->mday -= 1;
462 if (!(seconds = CheckDate(clock)))
464 FPrintf(StdErr, "Invalid date/time!\n");
465 ret = 5;
466 goto exit;
467 } /* Give up */
468 clock->mday += 1;
470 clock->mday += 1;
472 clock->mday += 1; /* Restore day for future reference */
475 if (interval->seconds)
477 seconds += interval->seconds;
478 Amiga2Date(seconds, clock);
482 if (ArgArray[TEM_VERBOSE] && (i+1<loop || step == 0))
484 FPrintf(StdErr, "Next scheduled execution ");
486 if (clock)
487 FPrintf(StdErr, "at %02lu/%02lu/%lu %02lu:%02lu:%02lu", (ULONG)clock->mday, (ULONG)clock->month, (ULONG)clock->year,
488 (ULONG)clock->hour, (ULONG)clock->min, (ULONG)clock->sec);
489 else
490 FPrintf(StdErr, "in %lu hours, %lu minutes and %lu seconds", seconds / 3600, (seconds % 3600) / 60, seconds % 60);
492 if (step == 0)
493 FPrintf(StdErr, ".\n");
494 else
495 FPrintf(StdErr, " (%lu loop(s) left).\n", loop-i-1);
499 exit:
500 if (clock)
501 FreeMem(clock, sizeof(struct ClockData));
502 if (interval)
503 FreeMem(interval, sizeof(struct Interval));
505 if (!TimerDevice)
506 CloseDevice((struct IORequest *)TimerReq);
507 if (TimerReq)
508 DeleteIORequest((struct IORequest *)TimerReq);
509 if (TimerPort)
510 DeleteMsgPort(TimerPort);
512 if (rdargs)
513 FreeArgs(rdargs);
515 Close(StdErr);
516 if (DOSBase)
517 CloseLibrary((struct Library *)DOSBase);
519 return ret;
523 /* A simple atoi()-alike function because it does what we need, and no more. */
524 int strtoi(STRPTR string)
526 int i, num;
528 for (i=0,num=0; string[i]>='0' && string[i]<='9'; ++i)
529 num = 10 * num + (string[i] - '0');
531 return num;