Implement the AT command:
- The /every and /next options are not supported yet.
- The 12 hour time format cannot be parsed yet.

svn path=/trunk/; revision=74190
This commit is contained in:
Eric Kohl 2017-03-19 00:11:31 +00:00
parent e49ac92a99
commit a9d1e30a2a
7 changed files with 655 additions and 0 deletions

View file

@ -1,3 +1,4 @@
add_subdirectory(at)
add_subdirectory(clip)
add_subdirectory(comp)
add_subdirectory(cscript)

View file

@ -0,0 +1,8 @@
include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils)
add_executable(at at.c at.rc)
set_module_type(at win32cui UNICODE)
target_link_libraries(at conutils ${PSEH_LIB})
add_importlibs(at msvcrt kernel32 user32 netapi32)
add_cd_file(TARGET at DESTINATION reactos/system32 FOR all)

View file

@ -0,0 +1,515 @@
/*
* PROJECT: ReactOS AT utility
* COPYRIGHT: See COPYING in the top level directory
* FILE: base/applications/cmdutils/at/at.c
* PURPOSE: ReactOS AT utility
* PROGRAMMERS: Eric Kohl <eric.kohl@reactos.org>
*/
#include <stdlib.h>
#include <stdio.h>
#include <windef.h>
#include <winbase.h>
#include <winuser.h>
#include <wincon.h>
#include <winnls.h>
#include <lm.h>
#include <conutils.h>
#include "resource.h"
static
BOOL
ParseTime(
PWSTR pszTime,
PULONG pulJobHour,
PULONG pulJobMinute)
{
PWSTR startPtr, endPtr;
ULONG ulHour = 0, ulMinute = 0;
BOOL bResult = FALSE;
startPtr = pszTime;
endPtr = NULL;
ulHour = wcstoul(startPtr, &endPtr, 10);
if (ulHour < 24 && endPtr != NULL && *endPtr == L':')
{
startPtr = endPtr + 1;
endPtr = NULL;
ulMinute = wcstoul(startPtr, &endPtr, 10);
if (ulMinute < 60 && endPtr != NULL && *endPtr == UNICODE_NULL)
{
bResult = TRUE;
if (pulJobHour != NULL)
*pulJobHour = ulHour;
if (pulJobMinute != NULL)
*pulJobMinute = ulMinute;
}
}
return bResult;
}
static
BOOL
ParseId(
PWSTR pszId,
PULONG pulId)
{
PWSTR startPtr, endPtr;
ULONG ulId = 0;
BOOL bResult = FALSE;
startPtr = pszId;
endPtr = NULL;
ulId = wcstoul(startPtr, &endPtr, 10);
if (endPtr != NULL && *endPtr == UNICODE_NULL)
{
bResult = TRUE;
if (pulId != NULL)
*pulId = ulId;
}
return bResult;
}
static
VOID
PrintErrorMessage(
DWORD dwError)
{
PWSTR pszBuffer = NULL;
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
dwError,
0,
(PWSTR)&pszBuffer,
0,
NULL);
ConPuts(StdErr, pszBuffer);
LocalFree(pszBuffer);
}
static
VOID
PrintHorizontalLine(VOID)
{
WCHAR szBuffer[80];
INT i;
for (i = 0; i < 79; i++)
szBuffer[i] = L'-';
szBuffer[79] = UNICODE_NULL;
ConPuts(StdOut, szBuffer);
}
static
DWORD_PTR
GetTimeAsJobTime(VOID)
{
SYSTEMTIME Time;
DWORD_PTR JobTime;
GetLocalTime(&Time);
JobTime = (DWORD_PTR)Time.wHour * 3600000 +
(DWORD_PTR)Time.wMinute * 60000;
return JobTime;
}
static
VOID
JobTimeToTimeString(
PWSTR pszBuffer,
INT cchBuffer,
WORD wHour,
WORD wMinute)
{
SYSTEMTIME Time = {0, 0, 0, 0, 0, 0, 0, 0};
Time.wHour = wHour;
Time.wMinute = wMinute;
GetTimeFormat(LOCALE_USER_DEFAULT,
TIME_NOSECONDS,
&Time,
NULL,
pszBuffer,
cchBuffer);
}
static
INT
PrintJobDetails(
PWSTR pszComputerName,
ULONG ulJobId)
{
AT_INFO *pBuffer = NULL;
DWORD_PTR CurrentTime;
WCHAR szStatusBuffer[16];
WCHAR szDayBuffer[32];
WCHAR szTimeBuffer[16];
WCHAR szInteractiveBuffer[16];
HINSTANCE hInstance;
NET_API_STATUS Status;
hInstance = GetModuleHandle(NULL);
Status = NetScheduleJobGetInfo(pszComputerName,
ulJobId,
(PBYTE *)&pBuffer);
if (Status != NERR_Success)
{
PrintErrorMessage(Status);
return 1;
}
if (pBuffer->Flags & JOB_EXEC_ERROR)
LoadStringW(hInstance, IDS_ERROR, szStatusBuffer, ARRAYSIZE(szStatusBuffer));
else
LoadStringW(hInstance, IDS_OK, szStatusBuffer, ARRAYSIZE(szStatusBuffer));
if (pBuffer->DaysOfMonth != 0)
{
wcscpy(szDayBuffer, L"TODO: DaysOfMonth!");
}
else if (pBuffer->DaysOfWeek != 0)
{
wcscpy(szDayBuffer, L"TODO: DaysOfWeek!");
}
else
{
CurrentTime = GetTimeAsJobTime();
if (CurrentTime > pBuffer->JobTime)
LoadStringW(hInstance, IDS_TOMORROW, szDayBuffer, ARRAYSIZE(szDayBuffer));
else
LoadStringW(hInstance, IDS_TODAY, szDayBuffer, ARRAYSIZE(szDayBuffer));
}
JobTimeToTimeString(szTimeBuffer,
ARRAYSIZE(szTimeBuffer),
(WORD)(pBuffer->JobTime / 3600000),
(WORD)((pBuffer->JobTime % 3600000) / 60000));
if (pBuffer->Flags & JOB_NONINTERACTIVE)
LoadStringW(hInstance, IDS_NO, szInteractiveBuffer, ARRAYSIZE(szInteractiveBuffer));
else
LoadStringW(hInstance, IDS_YES, szInteractiveBuffer, ARRAYSIZE(szInteractiveBuffer));
ConResPrintf(StdOut, IDS_TASKID, ulJobId);
ConResPrintf(StdOut, IDS_STATUS, szStatusBuffer);
ConResPrintf(StdOut, IDS_SCHEDULE, szDayBuffer);
ConResPrintf(StdOut, IDS_TIME, szTimeBuffer);
ConResPrintf(StdOut, IDS_INTERACTIVE, szInteractiveBuffer);
ConResPrintf(StdOut, IDS_COMMAND, pBuffer->Command);
NetApiBufferFree(pBuffer);
return 0;
}
static
INT
PrintAllJobs(
PWSTR pszComputerName)
{
PAT_ENUM pBuffer = NULL;
DWORD dwRead = 0, dwTotal = 0;
DWORD dwResume = 0, i;
DWORD_PTR CurrentTime;
NET_API_STATUS Status;
WCHAR szDayBuffer[32];
WCHAR szTimeBuffer[16];
HINSTANCE hInstance;
Status = NetScheduleJobEnum(pszComputerName,
(PBYTE *)&pBuffer,
MAX_PREFERRED_LENGTH,
&dwRead,
&dwTotal,
&dwResume);
if (Status != NERR_Success)
{
PrintErrorMessage(Status);
return 1;
}
if (dwTotal == 0)
{
ConResPrintf(StdOut, IDS_NO_ENTRIES);
return 0;
}
ConResPrintf(StdOut, IDS_JOBS_LIST);
PrintHorizontalLine();
hInstance = GetModuleHandle(NULL);
for (i = 0; i < dwRead; i++)
{
if (pBuffer[i].DaysOfMonth != 0)
{
wcscpy(szDayBuffer, L"TODO: DaysOfMonth");
}
else if (pBuffer[i].DaysOfWeek != 0)
{
wcscpy(szDayBuffer, L"TODO: DaysOfWeek");
}
else
{
CurrentTime = GetTimeAsJobTime();
if (CurrentTime > pBuffer[i].JobTime)
LoadStringW(hInstance, IDS_TOMORROW, szDayBuffer, ARRAYSIZE(szDayBuffer));
else
LoadStringW(hInstance, IDS_TODAY, szDayBuffer, ARRAYSIZE(szDayBuffer));
}
JobTimeToTimeString(szTimeBuffer,
ARRAYSIZE(szTimeBuffer),
(WORD)(pBuffer[i].JobTime / 3600000),
(WORD)((pBuffer[i].JobTime % 3600000) / 60000));
ConPrintf(StdOut,
L" %7lu %-22s %-12s %s\n",
pBuffer[i].JobId,
szDayBuffer,
szTimeBuffer,
pBuffer[i].Command);
}
NetApiBufferFree(pBuffer);
return 0;
}
static
INT
AddJob(
PWSTR pszComputerName,
ULONG ulJobHour,
ULONG ulJobMinute,
BOOL bInteractiveJob,
PWSTR pszCommand)
{
AT_INFO InfoBuffer;
ULONG ulJobId = 0;
NET_API_STATUS Status;
InfoBuffer.JobTime = (DWORD_PTR)ulJobHour * 3600000 +
(DWORD_PTR)ulJobMinute * 60000;
InfoBuffer.DaysOfMonth = 0;
InfoBuffer.DaysOfWeek = 0;
InfoBuffer.Flags = bInteractiveJob ? 0 : JOB_NONINTERACTIVE;
InfoBuffer.Command = pszCommand;
Status = NetScheduleJobAdd(pszComputerName,
(PBYTE)&InfoBuffer,
&ulJobId);
if (Status != NERR_Success)
{
PrintErrorMessage(Status);
return 1;
}
ConResPrintf(StdOut, IDS_NEW_JOB, ulJobId);
return 0;
}
static
INT
DeleteJob(
PWSTR pszComputerName,
ULONG ulJobId,
BOOL bForceDelete)
{
NET_API_STATUS Status;
if (ulJobId == (ULONG)-1 && bForceDelete == FALSE)
{
ConResPrintf(StdOut, IDS_CONFIRM_DELETE);
return 0;
}
Status = NetScheduleJobDel(pszComputerName,
(ulJobId == (ULONG)-1) ? 0 : ulJobId,
(ulJobId == (ULONG)-1) ? -1 : ulJobId);
if (Status != NERR_Success)
{
PrintErrorMessage(Status);
return 1;
}
return 0;
}
int wmain(int argc, WCHAR **argv)
{
PWSTR pszComputerName = NULL;
PWSTR pszCommand = NULL;
ULONG ulJobId = (ULONG)-1;
ULONG ulJobHour = (ULONG)-1;
ULONG ulJobMinute = (ULONG)-1;
BOOL bDeleteJob = FALSE, bForceDelete = FALSE;
BOOL bInteractiveJob = FALSE;
BOOL bPrintUsage = FALSE;
INT nResult = 0;
INT i, minIdx;
/* Initialize the Console Standard Streams */
ConInitStdStreams();
/* Parse the computer name */
i = 1;
minIdx = 1;
if (i < argc &&
argv[i][0] == L'\\' &&
argv[i][1] == L'\\')
{
pszComputerName = argv[i];
i++;
minIdx++;
}
/* Parse the time or job id */
if (i < argc && argv[i][0] != L'/')
{
if (ParseTime(argv[i], &ulJobHour, &ulJobMinute))
{
i++;
minIdx++;
}
else if (ParseId(argv[i], &ulJobId))
{
i++;
minIdx++;
}
}
/* Parse the options */
for (; i < argc; i++)
{
if (argv[i][0] == L'/')
{
if (_wcsicmp(argv[i], L"/?") == 0)
{
bPrintUsage = TRUE;
goto done;
}
else if (_wcsicmp(argv[i], L"/delete") == 0)
{
bDeleteJob = TRUE;
}
else if (_wcsicmp(argv[i], L"/yes") == 0)
{
bForceDelete = TRUE;
}
else if (_wcsicmp(argv[i], L"/interactive") == 0)
{
bInteractiveJob = TRUE;
}
/*
else if (_wcsnicmp(argv[i], L"/every:", 7) == 0)
{
}
else if (_wcsnicmp(argv[i], L"/next:", 6) == 0)
{
}
*/
else
{
bPrintUsage = TRUE;
nResult = 1;
goto done;
}
}
}
/* Parse the command */
if (argc > minIdx && argv[argc - 1][0] != L'/')
{
pszCommand = argv[argc - 1];
}
if (bDeleteJob == TRUE)
{
/* Check for invalid options or arguments */
if (bInteractiveJob == TRUE ||
ulJobHour != (ULONG)-1 ||
ulJobMinute != (ULONG)-1 ||
pszCommand != NULL)
{
bPrintUsage = TRUE;
nResult = 1;
goto done;
}
nResult = DeleteJob(pszComputerName, ulJobId, bForceDelete);
}
else
{
if (ulJobHour != (ULONG)-1 && ulJobMinute != (ULONG)-1)
{
/* Check for invalid options or arguments */
if (bForceDelete == TRUE || pszCommand == NULL)
{
bPrintUsage = TRUE;
nResult = 1;
goto done;
}
nResult = AddJob(pszComputerName,
ulJobHour,
ulJobMinute,
bInteractiveJob,
pszCommand);
}
else
{
/* Check for invalid options or arguments */
if (bForceDelete == TRUE || bInteractiveJob == TRUE)
{
bPrintUsage = TRUE;
nResult = 1;
goto done;
}
if (ulJobId == (ULONG)-1)
{
nResult = PrintAllJobs(pszComputerName);
}
else
{
nResult = PrintJobDetails(pszComputerName, ulJobId);
}
}
}
done:
if (bPrintUsage == TRUE)
ConResPuts(StdOut, IDS_USAGE);
return nResult;
}
/* EOF */

View file

@ -0,0 +1,17 @@
#include <windef.h>
#include "resource.h"
#define REACTOS_STR_FILE_DESCRIPTION "ReactOS AT Command"
#define REACTOS_STR_INTERNAL_NAME "at"
#define REACTOS_STR_ORIGINAL_FILENAME "at.exe"
#include <reactos/version.rc>
/* UTF-8 */
#pragma code_page(65001)
#ifdef LANGUAGE_DE_DE
#include "lang/de-DE.rc"
#endif
#ifdef LANGUAGE_EN_US
#include "lang/en-US.rc"
#endif

View file

@ -0,0 +1,40 @@
LANGUAGE LANG_GERMAN, SUBLANG_NEUTRAL
STRINGTABLE
BEGIN
IDS_USAGE "Mit dem AT Befeht können Befehle und Programme zu einem vorbestimmten\n\
Termin gestartet werden. Der Zeitplandienst muss gestartet sein, um den\n\
Befeht AT zu verwenden.\n\n\
AT [\\\\Computername] [ [Kennung] [/DELETE] | /DELETE [/YES]]\n\
AT [\\\\Computername] Zeit [/INTERACTIVE]\n\
[ /EVERY:Datum[,...] | /NEXT:Datum[,...]] ""Befehl""\n\n\
\\\\Computername ...\n\
Kennung ...\n\
/DELETE ...\n\
/YES ...\n\
Zeit Gibt die Zeit an, zu der ein Befehl ausgeführt werden soll.\n\
/INTERACTIVE ...\n\
/EVERY:Datum[,...] ...\n\
/NEXT:Datum[,...] ...\n\
""Befehl"" Ist der auszuführende Befehl oder das Stapelprogramm.\n"
IDS_CONFIRM_DELETE "Dieser Vorgang wird alle geplanten Aufräge löschen.\nMöchten Sie diesen Vorgang fortsetzen? (J/N) [N]"
IDS_NEW_JOB "Neuer Auftrag hinzugefügt. Kennung = %lu\n"
IDS_JOBS_LIST "Statuskenn. Tag Zeit Befehlszeile\n"
IDS_NO_ENTRIES "Es sind keine Einträge in der Liste.\n"
IDS_TODAY "Heute"
IDS_TOMORROW "Morgen"
IDS_YES "Ja"
IDS_NO "Nein"
IDS_ERROR "ERROR"
IDS_OK "OK"
IDS_TASKID "Taskkennung: %lu\n"
IDS_STATUS "Status: %s\n"
IDS_SCHEDULE "Zeitplan: %s\n"
IDS_TIME "Zeit: %s\n"
IDS_INTERACTIVE "Interaktiv: %s\n"
IDS_COMMAND "Befehl: %s\n"
END

View file

@ -0,0 +1,50 @@
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
STRINGTABLE
BEGIN
IDS_USAGE "The AT command schedules commands and programs to run on a computer at\n\
a specified time and date. The Schedule service must be running to use\n\
the AT command.\n\n\
AT [\\\\computername] [ [id] [/DELETE] | /DELETE [/YES]]\n\
AT [\\\\computername] time [/INTERACTIVE]\n\
[ /EVERY:date[,...] | /NEXT:date[,...]] ""command""\n\n\
\\\\computername Specifies a remote computer. Commands are scheduled on the \n\
local computer if this parameter is omitted.\n\
id Is an identification number assigned to a scheduled\n\
command.\n\
/DELETE Cancels a scheduled command. If id is omitted, all the\n\
scheduled commands on the computer are canceled.\n\
/YES Used with cancel all jobs command when no further\n\
confirmation is desired.\n\
Zeit Specifies the time when command is to run.\n\
/INTERACTIVE Allows the ob to interact with the desktop of the user\n\
who is logged on at the time the job runs.\n\
/EVERY:date[,...] Runs the command on each specified day(s) of the week or\n\
month. If date is omitted, the current day of the month\n\
is assumed.\n\
/NEXT:date[,...] Runs the specified command on the next occurrence of the\n\
day (for example, next Thursday). If date is omitted, the \n\
current day of the month is assumed.\n\
""command"" Is the command or batch program to be run.\n"
IDS_CONFIRM_DELETE "This operation will delete all seduled jobs.\nDo you want to continue this operation? (Y/N) [N]: "
IDS_NEW_JOB "Added a new job with job ID = %lu\n"
IDS_JOBS_LIST "Statuskenn. Tag Zeit Befehlszeile\n"
IDS_JOBS_LIST "Status ID Day Time Command Line\n"
IDS_NO_ENTRIES "There are no entries in the list.\n"
IDS_TODAY "Today"
IDS_TOMORROW "Tomorrow"
IDS_YES "Yes"
IDS_NO "No"
IDS_ERROR "ERROR"
IDS_OK "OK"
IDS_TASKID "Task ID: %lu\n"
IDS_STATUS "Status: %s\n"
IDS_SCHEDULE "Schedule: %s\n"
IDS_TIME "Time of day: %s\n"
IDS_INTERACTIVE "Interactive: %s\n"
IDS_COMMAND "Command: %s\n"
END

View file

@ -0,0 +1,24 @@
#pragma once
#define IDS_USAGE 100
#define IDS_CONFIRM_DELETE 105
#define IDS_NEW_JOB 106
#define IDS_JOBS_LIST 107
#define IDS_NO_ENTRIES 108
#define IDS_TODAY 109
#define IDS_TOMORROW 110
#define IDS_YES 101
#define IDS_NO 102
#define IDS_ERROR 103
#define IDS_OK 104
#define IDS_TASKID 122
#define IDS_STATUS 123
#define IDS_SCHEDULE 124
#define IDS_TIME 125
#define IDS_INTERACTIVE 126
#define IDS_COMMAND 127