disable debug.
[AROS.git] / workbench / c / WaitX.c
blob59da127e488c5540ad68a0e8769d414c15fa65af
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_PROCH(Start, argstr, argsize, sBase)
116 AROS_PROCFUNC_INIT
117 return MainEntry(sBase);
118 AROS_PROCFUNC_EXIT
122 LONG MainEntry(struct ExecBase *SysBase)
124 struct DosLibrary *DOSBase = NULL;
125 struct UtilityBase *UtilityBase = NULL;
126 struct Library *TimerBase;
128 BYTE TimerDevice = -1;
129 struct ClockData *clock = NULL;
130 struct Interval *interval = NULL;
131 struct MsgPort *TimerPort = NULL;
132 struct timerequest *TimerReq = NULL;
133 ULONG i, loop, step, unit, seconds = 0, signal = 0, timesig = 0, usersig = SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D | SIGBREAKF_CTRL_E;
135 BPTR StdErr = BNULL, StdIn;
136 char ProgName[256];
137 struct RDArgs *rdargs = NULL;
138 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";
140 enum
142 TEM_DATE,
143 TEM_TIME,
144 TEM_YEARS,
145 TEM_MONTHS,
146 TEM_DAYS,
147 TEM_HOURS,
148 TEM_MINS,
149 TEM_SECS,
150 TEM_LOOP,
151 TEM_ALWAYS,
152 TEM_VERBOSE,
153 TEM_HELP,
154 TEM_CMDLINE,
155 TEM_NUMARGS
158 IPTR ArgArray[TEM_NUMARGS], ret;
159 STRPTR cmdline;
160 STRPTR tmpptr;
162 ret = 20; /* Fatal error if something fails here */
163 if (!(DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 36)))
164 goto exit;
165 if (!(UtilityBase = (struct UtilityBase *)OpenLibrary("utility.library", 36)))
166 goto exit;
167 if (!(StdErr = Open("CONSOLE:", MODE_NEWFILE)))
168 goto exit;
169 ret = 0; /* Clear return */
171 GetProgramName(ProgName, sizeof(ProgName));
172 for (i=0; i<TEM_NUMARGS; ArgArray[i++]=0)
174 rdargs = ReadArgs(Template, ArgArray, NULL);
176 if (ArgArray[TEM_HELP] || !rdargs)
178 FPrintf(StdErr, "%s\n\n", &Version[6]);
179 FPrintf(StdErr, "Usage: %s [DATE=<DD/MM/YYYY>] [TIME=<HH:MM:SS>] <commandline>\n", ProgName);
180 FPrintf(StdErr, "Usage: %s [HOURS=<hours>] [MINS=<mins>] [SECS=<secs>] <commandline>\n", ProgName);
181 goto exit;
184 loop = step = 1;
185 if (ArgArray[TEM_LOOP])
186 loop = *((ULONG *)ArgArray[TEM_LOOP]);
188 if (ArgArray[TEM_ALWAYS] || loop == 0)
190 loop = 1;
191 step = 0;
194 if (ArgArray[TEM_CMDLINE])
195 cmdline = (STRPTR)ArgArray[TEM_CMDLINE];
196 else
197 cmdline = ""; /* Prevented from Execute()ing later */
199 if (ArgArray[TEM_DATE] || ArgArray[TEM_TIME] || ArgArray[TEM_YEARS] || ArgArray[TEM_MONTHS])
201 unit = UNIT_WAITUNTIL;
202 if (!(clock = AllocMem(sizeof(struct ClockData), MEMF_PUBLIC | MEMF_CLEAR)))
204 FPrintf(StdErr, "Unable to allocate needed memory!\n");
205 ret = 10;
206 goto exit;
208 if (!(interval = AllocMem(sizeof(struct Interval), MEMF_PUBLIC | MEMF_CLEAR)))
210 FPrintf(StdErr, "Unable to allocate needed memory!\n");
211 ret = 10;
212 goto exit;
215 if (ArgArray[TEM_YEARS])
216 interval->years = *((ULONG *)ArgArray[TEM_YEARS]);
217 if (ArgArray[TEM_MONTHS])
218 interval->months = *((ULONG *)ArgArray[TEM_MONTHS]);
219 if (ArgArray[TEM_DAYS])
220 interval->seconds += *((ULONG *)ArgArray[TEM_DAYS]) * 86400;
221 if (ArgArray[TEM_HOURS])
222 interval->seconds += *((ULONG *)ArgArray[TEM_HOURS]) * 3600;
223 if (ArgArray[TEM_MINS])
224 interval->seconds += *((ULONG *)ArgArray[TEM_MINS]) * 60;
225 if (ArgArray[TEM_SECS])
226 interval->seconds += *((ULONG *)ArgArray[TEM_SECS]);
228 if (interval->years || interval->months || interval->seconds)
229 interval->set = 1;
231 else
233 unit = UNIT_VBLANK;
234 seconds = 0;
236 if (ArgArray[TEM_DAYS])
237 seconds += *((ULONG *)ArgArray[TEM_DAYS]) * 86400;
238 if (ArgArray[TEM_HOURS])
239 seconds += *((ULONG *)ArgArray[TEM_HOURS]) * 3600;
240 if (ArgArray[TEM_MINS])
241 seconds += *((ULONG *)ArgArray[TEM_MINS]) * 60;
242 if (ArgArray[TEM_SECS])
243 seconds += *((ULONG *)ArgArray[TEM_SECS]);
246 if (seconds > 0 || clock)
248 if (!(TimerPort = CreateMsgPort()))
250 FPrintf(StdErr, "Couldn't create Timer-MsgPort!\n");
251 ret = 10;
252 goto exit;
254 if (!(TimerReq = (struct timerequest *)CreateIORequest(TimerPort, sizeof(struct timerequest))))
256 FPrintf(StdErr, "Couldn't create Timer-IORequest!\n");
257 ret = 10;
258 goto exit;
260 if ((TimerDevice = OpenDevice(TIMERNAME, unit, (struct IORequest *)TimerReq, 0)))
262 FPrintf(StdErr, "Couldn't open %s!\n", TIMERNAME);
263 ret = 10;
264 goto exit;
267 TimerBase = (struct Library *)TimerReq->tr_node.io_Device;
268 timesig = 1L << TimerPort->mp_SigBit;
270 if (clock)
272 GetSysTime(&TimerReq->tr_time); /* Get current System Time */
273 Amiga2Date(TimerReq->tr_time.tv_secs, clock); /* Fill in current date/time as default */
275 if (ArgArray[TEM_DATE])
277 tmpptr = (STRPTR)ArgArray[TEM_DATE];
278 clock->mday = strtoi(tmpptr);
279 if ((tmpptr = strstr(tmpptr, "/")))
281 clock->month = strtoi(++tmpptr);
282 if ((tmpptr = strstr(tmpptr, "/")))
284 clock->year = strtoi(++tmpptr);
286 if (!interval->set)
288 interval->years = 1;
289 interval->set = 1;
292 else if (!interval->set)
294 interval->months = 1;
295 interval->set = 1;
298 if (clock->year < 100) /* Be nice to digit-challenged ppl. ;) */
300 if (clock->year < 78)
301 clock->year += 2000; /* If less than 78, assume 20xx */
302 else
303 clock->year += 1900;
306 else if (!interval->set)
308 interval->seconds = 86400;
309 interval->set = 1;
312 if (ArgArray[TEM_TIME])
314 tmpptr = (STRPTR)ArgArray[TEM_TIME];
315 clock->hour = strtoi(tmpptr);
316 clock->min = clock->sec = 0; /* Clear default time */
317 if ((tmpptr = strstr(tmpptr, ":")))
319 clock->min = strtoi(++tmpptr);
320 if ((tmpptr = strstr(tmpptr, ":")))
321 clock->sec = strtoi(++tmpptr);
325 if (!(seconds = CheckDate(clock)))
327 FPrintf(StdErr, "Invalid date/time!\n");
328 ret = 5;
329 goto exit;
331 if (seconds <= TimerReq->tr_time.tv_secs)
333 ULONG count = loop;
335 for (i=0; i<count; i+=step)
337 clock->year += interval->years;
338 clock->month += interval->months;
340 if (clock->month > 12) /* We need some annual magic */
342 clock->month %= 12;
343 clock->year += clock->month / 12;
346 if (!(seconds = CheckDate(clock)))
348 clock->mday -= 1; /* If date doesn't exist, try previous day */
349 if (!(seconds = CheckDate(clock)))
351 clock->mday -= 1;
352 if (!(seconds = CheckDate(clock)))
354 clock->mday -= 1;
355 if (!(seconds = CheckDate(clock)))
357 FPrintf(StdErr, "Invalid date/time!\n");
358 ret = 5;
359 goto exit;
360 } /* Give up */
361 clock->mday += 1;
363 clock->mday += 1;
365 clock->mday += 1; /* Restore day for future reference */
368 if (interval->seconds)
370 seconds += interval->seconds;
371 Amiga2Date(seconds, clock);
374 if (loop > 0 && step > 0)
375 loop--;
376 if (seconds > TimerReq->tr_time.tv_secs)
377 break;
380 if (seconds <= TimerReq->tr_time.tv_secs || loop == 0)
382 FPrintf(StdErr, "Date/time has already passed!\n");
383 ret = 5;
384 goto exit;
386 else if (ArgArray[TEM_VERBOSE])
388 FPrintf(StdErr, "Note: Schedule has been moved to %02lu/%02lu/%lu %02lu:%02lu:%02lu because the assigned date/time has already passed",
389 (ULONG)clock->mday, (ULONG)clock->month, (ULONG)clock->year, (ULONG)clock->hour, (ULONG)clock->min, (ULONG)clock->sec);
391 if (step == 0)
392 FPrintf(StdErr, ".\n");
393 else
394 FPrintf(StdErr, " (%lu loop(s) left).\n", loop);
400 if (IsInteractive((StdIn = Input())))
401 StdIn = BNULL; /* Don't use StdIn if it isn't redirected */
403 for (i=0; i<loop; i+=step)
405 if (seconds > 0)
407 TimerReq->tr_time.tv_secs = seconds;
408 TimerReq->tr_time.tv_micro = 0;
409 TimerReq->tr_node.io_Command = TR_ADDREQUEST;
411 SendIO((struct IORequest *)TimerReq);
412 signal = Wait(timesig | usersig);
414 if (signal & usersig)
416 AbortIO((struct IORequest *)TimerReq);
417 WaitIO((struct IORequest *)TimerReq);
418 SetSignal(0L, timesig | usersig); /* Clear signalbits since WaitIO most likely preserves them */
420 if (signal & SIGBREAKF_CTRL_C)
421 break;
425 if (cmdline[0] == '\0')
426 break; /* There's no point in going on */
427 if (!(signal & SIGBREAKF_CTRL_D))
429 if (!Execute(cmdline, StdIn, Output()))
431 FPrintf(StdErr, "Unable to execute \"%s\".\n", cmdline);
432 ret = 5;
433 goto exit;
436 if (seconds == 0)
437 break; /* Don't go into tight unbreakable loop */
439 if (clock)
441 clock->year += interval->years;
442 clock->month += interval->months;
444 if (clock->month > 12) /* We need some annual magic */
446 clock->month %= 12;
447 clock->year += clock->month / 12;
450 if (!(seconds = CheckDate(clock)))
452 clock->mday -= 1; /* If date doesn't exist, try previous day */
453 if (!(seconds = CheckDate(clock)))
455 clock->mday -= 1;
456 if (!(seconds = CheckDate(clock)))
458 clock->mday -= 1;
459 if (!(seconds = CheckDate(clock)))
461 FPrintf(StdErr, "Invalid date/time!\n");
462 ret = 5;
463 goto exit;
464 } /* Give up */
465 clock->mday += 1;
467 clock->mday += 1;
469 clock->mday += 1; /* Restore day for future reference */
472 if (interval->seconds)
474 seconds += interval->seconds;
475 Amiga2Date(seconds, clock);
479 if (ArgArray[TEM_VERBOSE] && (i+1<loop || step == 0))
481 FPrintf(StdErr, "Next scheduled execution ");
483 if (clock)
484 FPrintf(StdErr, "at %02lu/%02lu/%lu %02lu:%02lu:%02lu", (ULONG)clock->mday, (ULONG)clock->month, (ULONG)clock->year,
485 (ULONG)clock->hour, (ULONG)clock->min, (ULONG)clock->sec);
486 else
487 FPrintf(StdErr, "in %lu hours, %lu minutes and %lu seconds", seconds / 3600, (seconds % 3600) / 60, seconds % 60);
489 if (step == 0)
490 FPrintf(StdErr, ".\n");
491 else
492 FPrintf(StdErr, " (%lu loop(s) left).\n", loop-i-1);
496 exit:
497 if (clock)
498 FreeMem(clock, sizeof(struct ClockData));
499 if (interval)
500 FreeMem(interval, sizeof(struct Interval));
502 if (!TimerDevice)
503 CloseDevice((struct IORequest *)TimerReq);
504 if (TimerReq)
505 DeleteIORequest((struct IORequest *)TimerReq);
506 if (TimerPort)
507 DeleteMsgPort(TimerPort);
509 if (rdargs)
510 FreeArgs(rdargs);
512 Close(StdErr);
513 if (DOSBase)
514 CloseLibrary((struct Library *)DOSBase);
516 return ret;
520 /* A simple atoi()-alike function because it does what we need, and no more. */
521 int strtoi(STRPTR string)
523 int i, num;
525 for (i=0,num=0; string[i]>='0' && string[i]<='9'; ++i)
526 num = 10 * num + (string[i] - '0');
528 return num;