xref: /reactos/base/applications/cmdutils/at/at.c (revision 50cf16b3)
1 /*
2  * PROJECT:     ReactOS AT utility
3  * COPYRIGHT:   See COPYING in the top level directory
4  * FILE:        base/applications/cmdutils/at/at.c
5  * PURPOSE:     ReactOS AT utility
6  * PROGRAMMERS: Eric Kohl <eric.kohl@reactos.org>
7  */
8 
9 #include <stdlib.h>
10 #include <stdio.h>
11 
12 #include <windef.h>
13 #include <winbase.h>
14 #include <winuser.h>
15 #include <wincon.h>
16 #include <winnls.h>
17 #include <lm.h>
18 
19 #include <conutils.h>
20 
21 #include "resource.h"
22 
23 
24 PWSTR pszDaysOfWeekArray[7] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
25 
26 
27 static
28 VOID
29 FreeDaysOfWeekArray(VOID)
30 {
31     INT i;
32 
33     for (i = 0; i < 7; i++)
34     {
35         if (pszDaysOfWeekArray[i] != NULL)
36             HeapFree(GetProcessHeap(), 0, pszDaysOfWeekArray[i]);
37     }
38 }
39 
40 
41 static
42 BOOL
43 InitDaysOfWeekArray(VOID)
44 {
45     INT i, nLength;
46 
47     for (i = 0; i < 7; i++)
48     {
49         nLength = GetLocaleInfo(LOCALE_USER_DEFAULT,
50                                 LOCALE_SABBREVDAYNAME1 + i,
51                                 NULL,
52                                 0);
53 
54         pszDaysOfWeekArray[i] = HeapAlloc(GetProcessHeap(),
55                                           HEAP_ZERO_MEMORY,
56                                           nLength * sizeof(WCHAR));
57         if (pszDaysOfWeekArray[i] == NULL)
58         {
59             FreeDaysOfWeekArray();
60             return FALSE;
61         }
62 
63         GetLocaleInfo(LOCALE_USER_DEFAULT,
64                       LOCALE_SABBREVDAYNAME1 + i,
65                       pszDaysOfWeekArray[i],
66                       nLength);
67     }
68 
69     return TRUE;
70 }
71 
72 
73 static
74 BOOL
75 ParseTime(
76     PWSTR pszTime,
77     PULONG pulJobHour,
78     PULONG pulJobMinute)
79 {
80     WCHAR szHour[3], szMinute[3], szAmPm[5];
81     PWSTR startPtr, endPtr;
82     ULONG ulHour = 0, ulMinute = 0;
83     INT nLength;
84 
85     if (pszTime == NULL)
86         return FALSE;
87 
88     startPtr = pszTime;
89 
90     /* Extract the hour string */
91     nLength = 0;
92     while (*startPtr != L'\0' && iswdigit(*startPtr))
93     {
94         if (nLength >= 2)
95             return FALSE;
96 
97         szHour[nLength] = *startPtr;
98         nLength++;
99 
100         startPtr++;
101     }
102     szHour[nLength] = L'\0';
103 
104     /* Check for a valid time separator */
105     if (*startPtr != L':')
106         return FALSE;
107 
108     /* Skip the time separator */
109     startPtr++;
110 
111     /* Extract the minute string */
112     nLength = 0;
113     while (*startPtr != L'\0' && iswdigit(*startPtr))
114     {
115         if (nLength >= 2)
116             return FALSE;
117 
118         szMinute[nLength] = *startPtr;
119         nLength++;
120 
121         startPtr++;
122     }
123     szMinute[nLength] = L'\0';
124 
125     /* Extract the optional AM/PM indicator string */
126     nLength = 0;
127     while (*startPtr != L'\0')
128     {
129         if (nLength >= 4)
130             return FALSE;
131 
132         if (!iswspace(*startPtr))
133         {
134             szAmPm[nLength] = *startPtr;
135             nLength++;
136         }
137 
138         startPtr++;
139     }
140     szAmPm[nLength] = L'\0';
141 
142     /* Convert the hour string */
143     ulHour = wcstoul(szHour, &endPtr, 10);
144     if (ulHour == 0 && *endPtr != UNICODE_NULL)
145         return FALSE;
146 
147     /* Convert the minute string */
148     ulMinute = wcstoul(szMinute, &endPtr, 10);
149     if (ulMinute == 0 && *endPtr != UNICODE_NULL)
150         return FALSE;
151 
152     /* Check for valid AM/PM indicator */
153     if (wcslen(szAmPm) > 0 &&
154         _wcsicmp(szAmPm, L"a") != 0 &&
155         _wcsicmp(szAmPm, L"am") != 0 &&
156         _wcsicmp(szAmPm, L"p") != 0 &&
157         _wcsicmp(szAmPm, L"pm") != 0)
158         return FALSE;
159 
160     /* Check for the valid minute range [0-59] */
161     if (ulMinute > 59)
162         return FALSE;
163 
164     if (wcslen(szAmPm) > 0)
165     {
166         /* 12 hour time format */
167 
168          /* Check for the valid hour range [1-12] */
169         if (ulHour == 0 || ulHour > 12)
170             return FALSE;
171 
172         /* Convert 12 hour format to 24 hour format */
173         if (_wcsicmp(szAmPm, L"a") == 0 ||
174             _wcsicmp(szAmPm, L"am") == 0)
175         {
176             if (ulHour == 12)
177                 ulHour = 0;
178         }
179         else
180         {
181             if (ulHour >= 1 && ulHour <= 11)
182                 ulHour += 12;
183         }
184     }
185     else
186     {
187         /* 24 hour time format */
188 
189         /* Check for the valid hour range [0-23] */
190         if (ulHour > 23)
191             return FALSE;
192     }
193 
194     if (pulJobHour != NULL)
195         *pulJobHour = ulHour;
196 
197     if (pulJobMinute != NULL)
198         *pulJobMinute = ulMinute;
199 
200     return TRUE;
201 }
202 
203 
204 static
205 BOOL
206 ParseId(
207     PWSTR pszId,
208     PULONG pulId)
209 {
210     PWSTR startPtr, endPtr;
211     ULONG ulId = 0;
212     BOOL bResult = FALSE;
213 
214     startPtr = pszId;
215     endPtr = NULL;
216     ulId = wcstoul(startPtr, &endPtr, 10);
217     if (endPtr != NULL && *endPtr == UNICODE_NULL)
218     {
219         bResult = TRUE;
220 
221         if (pulId != NULL)
222             *pulId = ulId;
223     }
224 
225     return bResult;
226 }
227 
228 
229 static
230 BOOL
231 ParseDaysOfMonth(
232     PWSTR pszBuffer,
233     PULONG pulDaysOfMonth)
234 {
235     PWSTR startPtr, endPtr;
236     ULONG ulValue;
237 
238     if (wcslen(pszBuffer) == 0)
239         return FALSE;
240 
241     startPtr = pszBuffer;
242     endPtr = NULL;
243     for (;;)
244     {
245         ulValue = wcstoul(startPtr, &endPtr, 10);
246         if (ulValue == 0)
247             return FALSE;
248 
249         if (ulValue > 0 && ulValue <= 31)
250             *pulDaysOfMonth |= (1 << (ulValue - 1));
251 
252         if (endPtr != NULL && *endPtr == UNICODE_NULL)
253             return TRUE;
254 
255         startPtr = endPtr + 1;
256         endPtr = NULL;
257     }
258 
259     return FALSE;
260 }
261 
262 
263 static
264 BOOL
265 ParseDaysOfWeek(
266     PWSTR pszBuffer,
267     PUCHAR pucDaysOfWeek)
268 {
269     PWSTR startPtr, endPtr;
270     INT nLength, i;
271 
272     if (wcslen(pszBuffer) == 0)
273         return FALSE;
274 
275     startPtr = pszBuffer;
276     endPtr = NULL;
277     for (;;)
278     {
279         endPtr = wcschr(startPtr, L',');
280         if (endPtr == NULL)
281             nLength = wcslen(startPtr);
282         else
283             nLength = (INT)((ULONG_PTR)endPtr - (ULONG_PTR)startPtr) / sizeof(WCHAR);
284 
285         for (i = 0; i < 7; i++)
286         {
287             if (nLength == wcslen(pszDaysOfWeekArray[i]) &&
288                 _wcsnicmp(startPtr, pszDaysOfWeekArray[i], nLength) == 0)
289             {
290                 *pucDaysOfWeek |= (1 << i);
291                 break;
292             }
293         }
294 
295         if (endPtr == NULL)
296             return TRUE;
297 
298         startPtr = endPtr + 1;
299         endPtr = NULL;
300     }
301 
302     return FALSE;
303 }
304 
305 
306 static
307 VOID
308 PrintErrorMessage(
309     DWORD dwError)
310 {
311     PWSTR pszBuffer = NULL;
312 
313     FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
314                    NULL,
315                    dwError,
316                    0,
317                    (PWSTR)&pszBuffer,
318                    0,
319                    NULL);
320 
321     ConPrintf(StdErr, L"%s\n", pszBuffer);
322     LocalFree(pszBuffer);
323 }
324 
325 
326 static
327 VOID
328 PrintHorizontalLine(VOID)
329 {
330     WCHAR szBuffer[80];
331     INT i;
332 
333     for (i = 0; i < 79; i++)
334         szBuffer[i] = L'-';
335     szBuffer[79] = UNICODE_NULL;
336 
337     ConPrintf(StdOut, L"%s\n", szBuffer);
338 }
339 
340 
341 static
342 BOOL
343 Confirm(VOID)
344 {
345     HINSTANCE hInstance;
346     WCHAR szYesBuffer[8];
347     WCHAR szNoBuffer[8];
348     WCHAR szInput[80];
349     DWORD dwOldMode;
350     DWORD dwRead = 0;
351     BOOL ret = FALSE;
352     HANDLE hFile;
353 
354     hInstance = GetModuleHandleW(NULL);
355     LoadStringW(hInstance, IDS_CONFIRM_YES, szYesBuffer, _countof(szYesBuffer));
356     LoadStringW(hInstance, IDS_CONFIRM_NO, szNoBuffer, _countof(szNoBuffer));
357 
358     ZeroMemory(szInput, sizeof(szInput));
359 
360     hFile = GetStdHandle(STD_INPUT_HANDLE);
361     GetConsoleMode(hFile, &dwOldMode);
362 
363     SetConsoleMode(hFile, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
364 
365     for (;;)
366     {
367         ConResPrintf(StdOut, IDS_CONFIRM_QUESTION);
368 
369         ReadConsoleW(hFile, szInput, _countof(szInput), &dwRead, NULL);
370 
371         szInput[0] = towupper(szInput[0]);
372         if (szInput[0] == szYesBuffer[0])
373         {
374             ret = TRUE;
375             break;
376         }
377         else if (szInput[0] == 13 || szInput[0] == szNoBuffer[0])
378         {
379             ret = FALSE;
380             break;
381         }
382 
383         ConResPrintf(StdOut, IDS_CONFIRM_INVALID);
384     }
385 
386     SetConsoleMode(hFile, dwOldMode);
387 
388     return ret;
389 }
390 
391 
392 static
393 DWORD_PTR
394 GetTimeAsJobTime(VOID)
395 {
396     SYSTEMTIME Time;
397     DWORD_PTR JobTime;
398 
399     GetLocalTime(&Time);
400 
401     JobTime = (DWORD_PTR)Time.wHour * 3600000 +
402               (DWORD_PTR)Time.wMinute * 60000;
403 
404     return JobTime;
405 }
406 
407 
408 static
409 ULONG
410 GetCurrentDayOfMonth(VOID)
411 {
412     SYSTEMTIME Time;
413 
414     GetLocalTime(&Time);
415 
416     return 1UL << (Time.wDay - 1);
417 }
418 
419 
420 static
421 VOID
422 JobTimeToTimeString(
423     PWSTR pszBuffer,
424     INT cchBuffer,
425     WORD wHour,
426     WORD wMinute)
427 {
428     SYSTEMTIME Time = {0, 0, 0, 0, 0, 0, 0, 0};
429 
430     Time.wHour = wHour;
431     Time.wMinute = wMinute;
432 
433     GetTimeFormat(LOCALE_USER_DEFAULT,
434                   TIME_NOSECONDS,
435                   &Time,
436                   NULL,
437                   pszBuffer,
438                   cchBuffer);
439 }
440 
441 
442 static
443 INT
444 PrintJobDetails(
445     PWSTR pszComputerName,
446     ULONG ulJobId)
447 {
448     PAT_INFO pBuffer = NULL;
449     DWORD_PTR CurrentTime;
450     WCHAR szStatusBuffer[16];
451     WCHAR szScheduleBuffer[60];
452     WCHAR szTimeBuffer[16];
453     WCHAR szInteractiveBuffer[16];
454     WCHAR szDateBuffer[8];
455     INT i, nDateLength, nScheduleLength;
456     HINSTANCE hInstance;
457     NET_API_STATUS Status;
458 
459     Status = NetScheduleJobGetInfo(pszComputerName,
460                                    ulJobId,
461                                    (PBYTE *)&pBuffer);
462     if (Status != NERR_Success)
463     {
464         PrintErrorMessage(Status);
465         return 1;
466     }
467 
468     hInstance = GetModuleHandle(NULL);
469 
470     if (pBuffer->Flags & JOB_EXEC_ERROR)
471         LoadStringW(hInstance, IDS_ERROR, szStatusBuffer, _countof(szStatusBuffer));
472     else
473         LoadStringW(hInstance, IDS_OK, szStatusBuffer, _countof(szStatusBuffer));
474 
475     if (pBuffer->DaysOfMonth != 0)
476     {
477         if (pBuffer->Flags & JOB_RUN_PERIODICALLY)
478             LoadStringW(hInstance, IDS_EVERY, szScheduleBuffer, _countof(szScheduleBuffer));
479         else
480             LoadStringW(hInstance, IDS_NEXT, szScheduleBuffer, _countof(szScheduleBuffer));
481 
482         nScheduleLength = wcslen(szScheduleBuffer);
483         for (i = 0; i < 31; i++)
484         {
485             if (pBuffer->DaysOfMonth & (1 << i))
486             {
487                 swprintf(szDateBuffer, L" %d", i + 1);
488                 nDateLength = wcslen(szDateBuffer);
489                 if (nScheduleLength + nDateLength <= 55)
490                 {
491                     wcscat(szScheduleBuffer, szDateBuffer);
492                     nScheduleLength += nDateLength;
493                 }
494                 else
495                 {
496                     wcscat(szScheduleBuffer, L"...");
497                     break;
498                 }
499             }
500         }
501     }
502     else if (pBuffer->DaysOfWeek != 0)
503     {
504         if (pBuffer->Flags & JOB_RUN_PERIODICALLY)
505             LoadStringW(hInstance, IDS_EVERY, szScheduleBuffer, _countof(szScheduleBuffer));
506         else
507             LoadStringW(hInstance, IDS_NEXT, szScheduleBuffer, _countof(szScheduleBuffer));
508 
509         nScheduleLength = wcslen(szScheduleBuffer);
510         for (i = 0; i < 7; i++)
511         {
512             if (pBuffer->DaysOfWeek & (1 << i))
513             {
514                 swprintf(szDateBuffer, L" %s", pszDaysOfWeekArray[i]);
515                 nDateLength = wcslen(szDateBuffer);
516                 if (nScheduleLength + nDateLength <= 55)
517                 {
518                     wcscat(szScheduleBuffer, szDateBuffer);
519                     nScheduleLength += nDateLength;
520                 }
521                 else
522                 {
523                     wcscat(szScheduleBuffer, L"...");
524                     break;
525                 }
526             }
527         }
528     }
529     else
530     {
531         CurrentTime = GetTimeAsJobTime();
532         if (CurrentTime > pBuffer->JobTime)
533             LoadStringW(hInstance, IDS_TOMORROW, szScheduleBuffer, _countof(szScheduleBuffer));
534         else
535             LoadStringW(hInstance, IDS_TODAY, szScheduleBuffer, _countof(szScheduleBuffer));
536     }
537 
538     JobTimeToTimeString(szTimeBuffer,
539                         _countof(szTimeBuffer),
540                         (WORD)(pBuffer->JobTime / 3600000),
541                         (WORD)((pBuffer->JobTime % 3600000) / 60000));
542 
543     if (pBuffer->Flags & JOB_NONINTERACTIVE)
544         LoadStringW(hInstance, IDS_NO, szInteractiveBuffer, _countof(szInteractiveBuffer));
545     else
546         LoadStringW(hInstance, IDS_YES, szInteractiveBuffer, _countof(szInteractiveBuffer));
547 
548     ConResPrintf(StdOut, IDS_TASKID, ulJobId);
549     ConResPrintf(StdOut, IDS_STATUS, szStatusBuffer);
550     ConResPrintf(StdOut, IDS_SCHEDULE, szScheduleBuffer);
551     ConResPrintf(StdOut, IDS_TIME, szTimeBuffer);
552     ConResPrintf(StdOut, IDS_INTERACTIVE, szInteractiveBuffer);
553     ConResPrintf(StdOut, IDS_COMMAND, pBuffer->Command);
554 
555     NetApiBufferFree(pBuffer);
556 
557     return 0;
558 }
559 
560 
561 static
562 INT
563 PrintAllJobs(
564     PWSTR pszComputerName)
565 {
566     PAT_ENUM pBuffer = NULL;
567     DWORD dwRead = 0, dwTotal = 0;
568     DWORD dwResume = 0, i;
569     DWORD_PTR CurrentTime;
570     NET_API_STATUS Status;
571 
572     WCHAR szScheduleBuffer[32];
573     WCHAR szTimeBuffer[16];
574     WCHAR szDateBuffer[8];
575     HINSTANCE hInstance;
576     INT j, nDateLength, nScheduleLength;
577 
578     Status = NetScheduleJobEnum(pszComputerName,
579                                 (PBYTE *)&pBuffer,
580                                 MAX_PREFERRED_LENGTH,
581                                 &dwRead,
582                                 &dwTotal,
583                                 &dwResume);
584     if (Status != NERR_Success)
585     {
586         PrintErrorMessage(Status);
587         return 1;
588     }
589 
590     if (dwTotal == 0)
591     {
592         ConResPrintf(StdOut, IDS_NO_ENTRIES);
593         return 0;
594     }
595 
596     ConResPrintf(StdOut, IDS_JOBS_LIST);
597     PrintHorizontalLine();
598 
599     hInstance = GetModuleHandle(NULL);
600 
601     for (i = 0; i < dwRead; i++)
602     {
603         if (pBuffer[i].DaysOfMonth != 0)
604         {
605             if (pBuffer[i].Flags & JOB_RUN_PERIODICALLY)
606                 LoadStringW(hInstance, IDS_EVERY, szScheduleBuffer, _countof(szScheduleBuffer));
607             else
608                 LoadStringW(hInstance, IDS_NEXT, szScheduleBuffer, _countof(szScheduleBuffer));
609 
610             nScheduleLength = wcslen(szScheduleBuffer);
611             for (j = 0; j < 31; j++)
612             {
613                 if (pBuffer[i].DaysOfMonth & (1 << j))
614                 {
615                     swprintf(szDateBuffer, L" %d", j + 1);
616                     nDateLength = wcslen(szDateBuffer);
617                     if (nScheduleLength + nDateLength <= 19)
618                     {
619                         wcscat(szScheduleBuffer, szDateBuffer);
620                         nScheduleLength += nDateLength;
621                     }
622                     else
623                     {
624                         wcscat(szScheduleBuffer, L"...");
625                         break;
626                     }
627                 }
628             }
629         }
630         else if (pBuffer[i].DaysOfWeek != 0)
631         {
632             if (pBuffer[i].Flags & JOB_RUN_PERIODICALLY)
633                 LoadStringW(hInstance, IDS_EVERY, szScheduleBuffer, _countof(szScheduleBuffer));
634             else
635                 LoadStringW(hInstance, IDS_NEXT, szScheduleBuffer, _countof(szScheduleBuffer));
636 
637             nScheduleLength = wcslen(szScheduleBuffer);
638             for (j = 0; j < 7; j++)
639             {
640                 if (pBuffer[i].DaysOfWeek & (1 << j))
641                 {
642                     swprintf(szDateBuffer, L" %s", pszDaysOfWeekArray[j]);
643                     nDateLength = wcslen(szDateBuffer);
644                     if (nScheduleLength + nDateLength <= 55)
645                     {
646                         wcscat(szScheduleBuffer, szDateBuffer);
647                         nScheduleLength += nDateLength;
648                     }
649                     else
650                     {
651                         wcscat(szScheduleBuffer, L"...");
652                         break;
653                     }
654                 }
655             }
656         }
657         else
658         {
659             CurrentTime = GetTimeAsJobTime();
660             if (CurrentTime > pBuffer[i].JobTime)
661                 LoadStringW(hInstance, IDS_TOMORROW, szScheduleBuffer, _countof(szScheduleBuffer));
662             else
663                 LoadStringW(hInstance, IDS_TODAY, szScheduleBuffer, _countof(szScheduleBuffer));
664         }
665 
666         JobTimeToTimeString(szTimeBuffer,
667                             _countof(szTimeBuffer),
668                             (WORD)(pBuffer[i].JobTime / 3600000),
669                             (WORD)((pBuffer[i].JobTime % 3600000) / 60000));
670 
671         ConPrintf(StdOut,
672                   L"   %6lu   %-21s   %-11s   %s\n",
673                   pBuffer[i].JobId,
674                   szScheduleBuffer,
675                   szTimeBuffer,
676                   pBuffer[i].Command);
677     }
678 
679     NetApiBufferFree(pBuffer);
680 
681     return 0;
682 }
683 
684 
685 static
686 INT
687 AddJob(
688     PWSTR pszComputerName,
689     ULONG ulJobHour,
690     ULONG ulJobMinute,
691     ULONG ulDaysOfMonth,
692     UCHAR ucDaysOfWeek,
693     BOOL bInteractiveJob,
694     BOOL bPeriodicJob,
695     PWSTR pszCommand)
696 {
697     AT_INFO InfoBuffer;
698     ULONG ulJobId = 0;
699     NET_API_STATUS Status;
700 
701     InfoBuffer.JobTime = (DWORD_PTR)ulJobHour * 3600000 +
702                          (DWORD_PTR)ulJobMinute * 60000;
703     InfoBuffer.DaysOfMonth = ulDaysOfMonth;
704     InfoBuffer.DaysOfWeek = ucDaysOfWeek;
705     InfoBuffer.Flags = (bInteractiveJob ? 0 : JOB_NONINTERACTIVE) |
706                        (bPeriodicJob ? JOB_RUN_PERIODICALLY : 0);
707     InfoBuffer.Command = pszCommand;
708 
709     Status = NetScheduleJobAdd(pszComputerName,
710                                (PBYTE)&InfoBuffer,
711                                &ulJobId);
712     if (Status != NERR_Success)
713     {
714         PrintErrorMessage(Status);
715         return 1;
716     }
717 
718     ConResPrintf(StdOut, IDS_NEW_JOB, ulJobId);
719 
720     return 0;
721 }
722 
723 
724 static
725 INT
726 DeleteJob(
727     PWSTR pszComputerName,
728     ULONG ulJobId,
729     BOOL bForceDelete)
730 {
731     NET_API_STATUS Status;
732 
733     if (ulJobId == (ULONG)-1 && bForceDelete == FALSE)
734     {
735         ConResPrintf(StdOut, IDS_DELETE_ALL);
736         if (!Confirm())
737             return 0;
738     }
739 
740     Status = NetScheduleJobDel(pszComputerName,
741                                (ulJobId == (ULONG)-1) ? 0 : ulJobId,
742                                (ulJobId == (ULONG)-1) ? -1 : ulJobId);
743     if (Status != NERR_Success)
744     {
745         PrintErrorMessage(Status);
746         return 1;
747     }
748 
749     return 0;
750 }
751 
752 
753 int wmain(int argc, WCHAR **argv)
754 {
755     PWSTR pszComputerName = NULL;
756     PWSTR pszCommand = NULL;
757     ULONG ulJobId = (ULONG)-1;
758     ULONG ulJobHour = (ULONG)-1;
759     ULONG ulJobMinute = (ULONG)-1;
760     BOOL bDeleteJob = FALSE, bForceDelete = FALSE;
761     BOOL bInteractiveJob = FALSE, bPeriodicJob = FALSE;
762     BOOL bPrintUsage = FALSE;
763     ULONG ulDaysOfMonth = 0;
764     UCHAR ucDaysOfWeek = 0;
765     INT nResult = 0;
766     INT i, minIdx;
767 
768     /* Initialize the Console Standard Streams */
769     ConInitStdStreams();
770 
771     if (!InitDaysOfWeekArray())
772         return 1;
773 
774     /* Parse the computer name */
775     i = 1;
776     minIdx = 1;
777     if (i < argc &&
778         argv[i][0] == L'\\' &&
779         argv[i][1] == L'\\')
780     {
781         pszComputerName = argv[i];
782         i++;
783         minIdx++;
784     }
785 
786     /* Parse the time or job id */
787     if (i < argc && argv[i][0] != L'/')
788     {
789         if (ParseTime(argv[i], &ulJobHour, &ulJobMinute))
790         {
791             i++;
792             minIdx++;
793         }
794         else if (ParseId(argv[i], &ulJobId))
795         {
796             i++;
797             minIdx++;
798         }
799     }
800 
801     /* Parse the options */
802     for (; i < argc; i++)
803     {
804         if (argv[i][0] == L'/')
805         {
806             if (_wcsicmp(argv[i], L"/?") == 0)
807             {
808                 bPrintUsage = TRUE;
809                 goto done;
810             }
811             else if (_wcsicmp(argv[i], L"/delete") == 0)
812             {
813                 bDeleteJob = TRUE;
814             }
815             else if (_wcsicmp(argv[i], L"/yes") == 0)
816             {
817                 bForceDelete = TRUE;
818             }
819             else if (_wcsicmp(argv[i], L"/interactive") == 0)
820             {
821                 bInteractiveJob = TRUE;
822             }
823             else if (_wcsnicmp(argv[i], L"/every:", 7) == 0)
824             {
825                 bPeriodicJob = TRUE;
826                 if (ParseDaysOfMonth(&(argv[i][7]), &ulDaysOfMonth) == FALSE)
827                 {
828                     if (ParseDaysOfWeek(&(argv[i][7]), &ucDaysOfWeek) == FALSE)
829                     {
830                         ulDaysOfMonth = GetCurrentDayOfMonth();
831                     }
832                 }
833             }
834             else if (_wcsnicmp(argv[i], L"/next:", 6) == 0)
835             {
836                 bPeriodicJob = FALSE;
837                 if (ParseDaysOfMonth(&(argv[i][6]), &ulDaysOfMonth) == FALSE)
838                 {
839                     if (ParseDaysOfWeek(&(argv[i][6]), &ucDaysOfWeek) == FALSE)
840                     {
841                         ulDaysOfMonth = GetCurrentDayOfMonth();
842                     }
843                 }
844             }
845             else
846             {
847                 bPrintUsage = TRUE;
848                 nResult = 1;
849                 goto done;
850             }
851         }
852     }
853 
854     /* Parse the command */
855     if (argc > minIdx && argv[argc - 1][0] != L'/')
856     {
857         pszCommand = argv[argc - 1];
858     }
859 
860     if (bDeleteJob == TRUE)
861     {
862         /* Check for invalid options or arguments */
863         if (bInteractiveJob == TRUE ||
864             ulJobHour != (ULONG)-1 ||
865             ulJobMinute != (ULONG)-1 ||
866             ulDaysOfMonth != 0 ||
867             ucDaysOfWeek != 0 ||
868             pszCommand != NULL)
869         {
870             bPrintUsage = TRUE;
871             nResult = 1;
872             goto done;
873         }
874 
875         nResult = DeleteJob(pszComputerName,
876                             ulJobId,
877                             bForceDelete);
878     }
879     else
880     {
881         if (ulJobHour != (ULONG)-1 && ulJobMinute != (ULONG)-1)
882         {
883             /* Check for invalid options or arguments */
884             if (bForceDelete == TRUE ||
885                 pszCommand == NULL)
886             {
887                 bPrintUsage = TRUE;
888                 nResult = 1;
889                 goto done;
890             }
891 
892             nResult = AddJob(pszComputerName,
893                              ulJobHour,
894                              ulJobMinute,
895                              ulDaysOfMonth,
896                              ucDaysOfWeek,
897                              bInteractiveJob,
898                              bPeriodicJob,
899                              pszCommand);
900         }
901         else
902         {
903             /* Check for invalid options or arguments */
904             if (bForceDelete == TRUE ||
905                 bInteractiveJob == TRUE ||
906                 ulDaysOfMonth != 0 ||
907                 ucDaysOfWeek != 0 ||
908                 pszCommand != NULL)
909             {
910                 bPrintUsage = TRUE;
911                 nResult = 1;
912                 goto done;
913             }
914 
915             if (ulJobId == (ULONG)-1)
916             {
917                 nResult = PrintAllJobs(pszComputerName);
918             }
919             else
920             {
921                 nResult = PrintJobDetails(pszComputerName,
922                                           ulJobId);
923             }
924         }
925     }
926 
927 done:
928     FreeDaysOfWeekArray();
929 
930     if (bPrintUsage == TRUE)
931         ConResPuts(StdOut, IDS_USAGE);
932 
933     return nResult;
934 }
935 
936 /* EOF */
937