diff options
Diffstat (limited to 'private/net/svcdlls/at/atcmd')
-rw-r--r-- | private/net/svcdlls/at/atcmd/at.rc | 12 | ||||
-rw-r--r-- | private/net/svcdlls/at/atcmd/atcmd.c | 2290 | ||||
-rw-r--r-- | private/net/svcdlls/at/atcmd/lmatmsg.mc | 193 | ||||
-rw-r--r-- | private/net/svcdlls/at/atcmd/makefile | 6 | ||||
-rw-r--r-- | private/net/svcdlls/at/atcmd/makefile.inc | 4 | ||||
-rw-r--r-- | private/net/svcdlls/at/atcmd/sources | 32 |
6 files changed, 2537 insertions, 0 deletions
diff --git a/private/net/svcdlls/at/atcmd/at.rc b/private/net/svcdlls/at/atcmd/at.rc new file mode 100644 index 000000000..22e35e70a --- /dev/null +++ b/private/net/svcdlls/at/atcmd/at.rc @@ -0,0 +1,12 @@ +#include <windows.h> +#include <ntverp.h> + +#define VER_FILETYPE VFT_APP +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Schedule service command line interface" +#define VER_INTERNALNAME_STR "AT.EXE" +#define VER_ORIGINALFILENAME_STR "AT.EXE" + +#include "common.ver" +#include "lmatmsg.rc" + diff --git a/private/net/svcdlls/at/atcmd/atcmd.c b/private/net/svcdlls/at/atcmd/atcmd.c new file mode 100644 index 000000000..29dc9455d --- /dev/null +++ b/private/net/svcdlls/at/atcmd/atcmd.c @@ -0,0 +1,2290 @@ +/*++ + +Copyright (c) 1987-1992 Microsoft Corporation + +Module Name: + + atcmd.c + +Abstract: + + Code for AT command, to be used with SCHEDULE service on Windows NT. + + The module was taken from LanManager\at.c and then modified considerably + to work with NT Schedule service. + +Author: + + Vladimir Z. Vulovic (vladimv) 06 - November - 1992 + +Environment: + + User Mode - Win32 + +Revision History: + + 06-Nov-1992 vladimv + Created + + 20-Feb-1993 yihsins + Get rid of hard coded strings and parse/print time according + to user profile + + 25-May-1993 RonaldM + Convert strings to OEM before printing to the console, since + the console doesn't yet support unicode. + + 28-Jun-1993 RonaldM + Added the "confirm" yes and no strings, which are meant to be + localised. The original yes and no strings cannot be localised, + because this would create batch file incompatibilities. + + 07-Jul-1994 vladimv + Added support for interactive switch. Replaced "confirm" strings + with APE2_GEN_* strings - to eliminate redundancy. The rule is + that switches are not internationalizable while switch values are. + +--*/ + +#include <nt.h> // DbgPrint prototype +#include <ntrtl.h> // DbgPrint +#include <nturtl.h> // Needed by winbase.h + +#include <windows.h> +#include <winnls.h> +#include <shellapi.h> + +#include <lmcons.h> // NET_API_STATUS +#include <lmerr.h> // NetError codes +#include <icanon.h> // NetpNameValidate + +#include "lmatmsg.h" // private AT error codes & messages +#include <lmat.h> // AT_INFO +#include <stdlib.h> // exit() +#include <stdio.h> // printf +#include <wchar.h> // wcslen +#include <apperr.h> // APE_AT_USAGE +#include <apperr2.h> // APE2_GEN_MONDAY + APE2_* +#include <lmapibuf.h> // NetApiBufferFree +#include <timelib.h> // NetpGetTimeFormat +#include <luidate.h> // LUI_ParseTimeSinceStartOfDay + + +#define YES_FLAG 0 +#define NO_FLAG 1 +#define INVALID_FLAG -1 + +#define DUMP_ALL 0 +#define DUMP_ID 1 +#define ADD_TO_SCHEDULE 2 +#define ADD_ONETIME 3 +#define DEL_ID 4 +#define DEL_ALL 5 +#define ACTION_USAGE 6 +#define MAX_COMMAND_LEN 128 +#define MAX_SCHED_FIELD_LENGTH 24 + + +#define PutNewLine() GenOutput( TEXT("\n")) +#define PutNewLine2() GenOutput( TEXT("\n\n")) + +#define MAX_MSG_BUFFER 1024 + +WCHAR ConBuf[MAX_MSG_BUFFER]; + +#define GenOutput(fmt) \ + {wcscpy(ConBuf, fmt); \ + ConsolePrint(ConBuf, wcslen(ConBuf));} + +#define GenOutputArg(fmt, a1) \ + {wsprintf(ConBuf, fmt, a1); \ + ConsolePrint(ConBuf, wcslen(ConBuf));} +// +// Formats used by printf. +// +#define DUMP_FMT1 TEXT("%-7.7ws") + +// +// DUMP_FMT2 is chosen so that the most common case (id numbers less than 100) +// looks good: two spaces for a number, three spaces for blanks. +// Larger numbers just like in LM21 will result to shifted display. +// +#define DUMP_FMT2 TEXT("%2d ") +#define MAX_TIME_FIELD_LENGTH 14 +#define DUMP_FMT3 TEXT("%ws") // for printing JobTime + +#define NULLC L'\0' +#define BLANK L' ' +#define SLASH L'/' +#define BACKSLASH L'\\' +#define ELLIPSIS L"..." + +#define QUESTION_SW L"/?" +#define QUESTION_SW_TOO L"-?" +#define SCHED_TOK_DELIM L"," // string of valid delimiters for days & dates +#define ARG_SEP_CHR L':' + +typedef struct _SEARCH_LIST { + WCHAR * String; + DWORD MessageId; + DWORD Value; +} SEARCH_LIST, *PSEARCH_LIST, *LPSEARCH_LIST; + +// +// All of the values below must be bitmasks. MatchString() depends on that! +// +#define AT_YES_VALUE 0x0001 +#define AT_DELETE_VALUE 0x0002 +#define AT_EVERY_VALUE 0x0004 +#define AT_NEXT_VALUE 0x0008 +#define AT_NO_VALUE 0x0010 +#define AT_CONFIRM_YES_VALUE 0x0020 +#define AT_CONFIRM_NO_VALUE 0x0040 +#define AT_INTERACTIVE 0x0080 + +SEARCH_LIST GlobalListTable[] = { + { NULL, IDS_YES, AT_YES_VALUE}, + { NULL, IDS_DELETE, AT_DELETE_VALUE}, + { NULL, IDS_EVERY, AT_EVERY_VALUE}, + { NULL, IDS_NEXT, AT_NEXT_VALUE}, + { NULL, IDS_NO, AT_NO_VALUE}, + { NULL, APE2_GEN_YES, AT_CONFIRM_YES_VALUE}, + { NULL, APE2_GEN_NO, AT_CONFIRM_NO_VALUE}, + { NULL, IDS_INTERACTIVE, AT_INTERACTIVE}, + { NULL, 0, 0 } +}; + +SEARCH_LIST DaysOfWeekSearchList[] = { + { NULL, APE2_GEN_MONDAY_ABBREV, 0}, + { NULL, APE2_GEN_TUESDAY_ABBREV, 1}, + { NULL, APE2_GEN_WEDNSDAY_ABBREV, 2}, + { NULL, APE2_GEN_THURSDAY_ABBREV, 3}, + { NULL, APE2_GEN_FRIDAY_ABBREV, 4}, + { NULL, APE2_GEN_SATURDAY_ABBREV, 5}, + { NULL, APE2_TIME_SATURDAY_ABBREV2, 5}, + { NULL, APE2_GEN_SUNDAY_ABBREV, 6}, + { NULL, APE2_GEN_MONDAY, 0}, + { NULL, APE2_GEN_TUESDAY, 1}, + { NULL, APE2_GEN_WEDNSDAY, 2}, + { NULL, APE2_GEN_THURSDAY, 3}, + { NULL, APE2_GEN_FRIDAY, 4}, + { NULL, APE2_GEN_SATURDAY, 5}, + { NULL, APE2_GEN_SUNDAY, 6}, + { NULL, APE2_GEN_NONLOCALIZED_MONDAY_ABBREV, 0}, + { NULL, APE2_GEN_NONLOCALIZED_TUESDAY_ABBREV, 1}, + { NULL, APE2_GEN_NONLOCALIZED_WEDNSDAY_ABBREV, 2}, + { NULL, APE2_GEN_NONLOCALIZED_THURSDAY_ABBREV, 3}, + { NULL, APE2_GEN_NONLOCALIZED_FRIDAY_ABBREV, 4}, + { NULL, APE2_GEN_NONLOCALIZED_SATURDAY_ABBREV, 5}, + { NULL, APE2_GEN_NONLOCALIZED_SATURDAY_ABBREV2, 5}, + { NULL, APE2_GEN_NONLOCALIZED_SUNDAY_ABBREV, 6}, + { NULL, APE2_GEN_NONLOCALIZED_MONDAY, 0}, + { NULL, APE2_GEN_NONLOCALIZED_TUESDAY, 1}, + { NULL, APE2_GEN_NONLOCALIZED_WEDNSDAY, 2}, + { NULL, APE2_GEN_NONLOCALIZED_THURSDAY, 3}, + { NULL, APE2_GEN_NONLOCALIZED_FRIDAY, 4}, + { NULL, APE2_GEN_NONLOCALIZED_SATURDAY, 5}, + { NULL, APE2_GEN_NONLOCALIZED_SUNDAY, 6}, + { NULL, 0, 0 } + }; + +BOOL +AreYouSure( + VOID + ); +BOOL +ArgIsServerName( + WCHAR * string + ); +BOOL +ArgIsTime( + IN WCHAR * timestr, + OUT PDWORD pJobTime + ); +BOOL +ArgIsDecimalString( + IN WCHAR * pDecimalString, + OUT PDWORD pNumber + ); +DWORD +ConsolePrint( + IN LPWSTR pch, + IN int cch + ); +int +FileIsConsole( + int fh + ); +BOOL +IsDayOfMonth( + IN WCHAR * pToken, + OUT PDWORD pDay + ); +BOOL +IsDayOfWeek( + IN WCHAR * pToken, + OUT PDWORD pDay + ); +NET_API_STATUS +JobAdd( + VOID + ); +NET_API_STATUS +JobEnum( + VOID + ); +NET_API_STATUS +JobGetInfo( + VOID + ); +DWORD +MatchString( + WCHAR * name, + DWORD mask + ); +DWORD +MessageGet( + IN DWORD MessageId, + IN LPWSTR *buffer, + IN DWORD Size + ); +DWORD +MessagePrint( + IN DWORD MessageId, + ... + ); +BOOL +ParseJobIdArgs( + WCHAR ** argv, + int argc, + int argno, + PBOOL pDeleteFound + ); +BOOL +ParseTimeArgs( + WCHAR ** argv, + int argc, + int argno, + int * pargno + ); +VOID +PrintDay( + int type, + DWORD DaysOfMonth, + UCHAR DaysOfWeek, + UCHAR Flags + ); +VOID +PrintLine( + VOID + ); +VOID +PrintTime( + DWORD JobTime + ); +BOOL +TraverseSearchList( + PWCHAR String, + PSEARCH_LIST SearchList, + PDWORD pValue + ); +VOID +Usage( + BOOL GoodCommand + ); +BOOL +ValidateCommand( + IN int argc, + IN WCHAR ** argv, + OUT int * pCommand + ); + +VOID +GetTimeString( + DWORD Time, + WCHAR *Buffer, + int BufferLength + ); + +BOOL +InitList( + PSEARCH_LIST SearchList + ); + +VOID +TermList( + PSEARCH_LIST SearchList + ); + +DWORD +GetStringColumn( + WCHAR * + ); + +AT_INFO GlobalAtInfo; // buffer for scheduling new jobs +WCHAR GlobalAtInfoCommand[ MAX_COMMAND_LEN + 1]; + +DWORD GlobalJobId; // id of job in question +PWSTR GlobalServerName; +HANDLE GlobalMessageHandle; +BOOL GlobalYes; +BOOL GlobalDeleteAll; +BOOL GlobalErrorReported; +BOOL bDBCS; + +CHAR ** GlobalCharArgv; // keeps original input + +NET_TIME_FORMAT GlobalTimeFormat = {0}; + +// In OS/2 it used to be OK to call "exit()" with a negative number. In +// NT however, "exit()" should be called with a positive number only (a +// valid windows error code?!). Note that OS/2 AT command used to call +// exit(+1) for bad user input, and exit(-1) where -1 would get mapped to +// 255 for other errors. To keep things simple and to avoid calling exit() +// with a negative number, NT AT command calls exit(+1) for all possible +// errors. + +#define AT_GENERIC_ERROR 1 + + +VOID _CRTAPI1 +main( + int argc, + CHAR ** charArgv + ) +/*++ + +Routine Description: + + Main module. Note that strings (for now) arrive as asciiz even + if you compile app for UNICODE. + +Arguments: + + argc - argument count + charArgv - array of ascii strings + +Return Value: + + None. + +--*/ +{ + NET_API_STATUS status = NERR_Success; + int command; // what to do + WCHAR ** argv; + DWORD cp; + CPINFO CurrentCPInfo; + + GlobalYes = FALSE; + GlobalDeleteAll = FALSE; + GlobalErrorReported = FALSE; + GlobalCharArgv = charArgv; + + /* + Added for bilingual message support. This is needed for FormatMessage + to work correctly. (Called from DosGetMessage). + Get current CodePage Info. We need this to decide whether + or not to use half-width characters. + */ + + + GetCPInfo(cp=GetConsoleOutputCP(), &CurrentCPInfo); + switch ( cp ) { + case 932: + case 936: + case 949: + case 950: + SetThreadLocale( + MAKELCID( + MAKELANGID( + PRIMARYLANGID(GetSystemDefaultLangID()), + SUBLANG_ENGLISH_US ), + SORT_DEFAULT + ) + ); + bDBCS = TRUE; + break; + + default: + SetThreadLocale( + MAKELCID( + MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US ), + SORT_DEFAULT + ) + ); + bDBCS = FALSE; + break; + } + + GlobalMessageHandle = LoadLibrary( L"netmsg.dll"); + if ( GlobalMessageHandle == NULL) { + MessagePrint( IDS_LOAD_LIBRARY_FAILURE, GetLastError()); + exit( AT_GENERIC_ERROR); + } + + if ( ( argv = CommandLineToArgvW( GetCommandLineW(), &argc)) == NULL) { + MessagePrint( IDS_UNABLE_TO_MAP_TO_UNICODE ); + exit( AT_GENERIC_ERROR); + } + + if ( ValidateCommand( argc, argv, &command) == FALSE) { + Usage( FALSE); + exit( AT_GENERIC_ERROR); + } + + switch( command) { + + case DUMP_ALL: + status = JobEnum(); + break; + + case DUMP_ID: + status = JobGetInfo(); + break; + + case ADD_TO_SCHEDULE: + status = JobAdd(); + break; + + case DEL_ALL: + if ( AreYouSure() == FALSE) { + break; + } + status = NetScheduleJobDel( + GlobalServerName, + 0, + (DWORD)-1 + ); + if ( status == NERR_Success || status == APE_AT_ID_NOT_FOUND) { + break; + } + MessagePrint( status ); + break; + + case DEL_ID: + status = NetScheduleJobDel( + GlobalServerName, + GlobalJobId, + GlobalJobId + ); + if ( status == NERR_Success) { + break; + } + MessagePrint( status ); + break; + + case ACTION_USAGE: + Usage( TRUE); + status = NERR_Success; + break; + } + + TermList( GlobalListTable); + TermList( DaysOfWeekSearchList); + LocalFree( GlobalTimeFormat.AMString ); + LocalFree( GlobalTimeFormat.PMString ); + LocalFree( GlobalTimeFormat.DateFormat ); + LocalFree( GlobalTimeFormat.TimeSeparator ); + exit( status == NERR_Success ? ERROR_SUCCESS : AT_GENERIC_ERROR); +} + + + +BOOL +AreYouSure( + VOID + ) +/*++ + +Routine Description: + + Make sure user really wants to delete all jobs. + +Arguments: + + None. + +Return Value: + + TRUE if user really wants to go ahead. + FALSE otherwise. + +--*/ +{ + register int retries = 0; + WCHAR rbuf[ 16]; + WCHAR * smallBuffer = NULL; + DWORD Value; + int cch; + int retc; + + if ( GlobalYes == TRUE) { + return( TRUE); + } + + if ( MessagePrint( APE2_AT_DEL_WARNING ) == 0) { + exit( AT_GENERIC_ERROR); + } + + for ( ; ;) { + + if ( MessageGet( + APE2_GEN_DEFAULT_NO, // MessageId + &smallBuffer, // lpBuffer + 0 + ) == 0) { + exit( AT_GENERIC_ERROR); + } + + if ( MessagePrint( APE_OkToProceed, smallBuffer) == 0) { + exit( AT_GENERIC_ERROR); + } + + LocalFree( smallBuffer ); + + if (FileIsConsole(STD_INPUT_HANDLE)) { + retc = ReadConsole(GetStdHandle(STD_INPUT_HANDLE),rbuf,16,&cch,0); + if (retc) { + // + // Get rid of cr/lf + // + if (wcschr(rbuf, TEXT('\r')) == NULL) { + if (wcschr(rbuf, TEXT('\n'))) + *wcschr(rbuf, TEXT('\n')) = NULLC; + } + else + *wcschr(rbuf, TEXT('\r')) = NULLC; + } + } + else { + CHAR oemBuf[ 16 ]; + + retc = (int)gets(oemBuf); +#if DBG + fprintf(stderr, "got >%s<\n", oemBuf); +#endif + cch = 0; + if (retc || strlen(oemBuf)+1 > 16) + cch = MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, + oemBuf, strlen(oemBuf)+1, rbuf, 16); + } + +#if DBG + fprintf(stderr, "cch = %d, retc = %d\n", cch, retc); +#endif + if (!retc || cch == 0) + return( FALSE); +#if DBG + fprintf(stderr, "converted to >%ws<\n", rbuf); +#endif + + Value = MatchString(_wcsupr(rbuf), AT_CONFIRM_NO_VALUE | AT_CONFIRM_YES_VALUE); + + if ( Value == AT_CONFIRM_NO_VALUE) { + return( FALSE); + } else if ( Value == AT_CONFIRM_YES_VALUE) { + break; + } + + if ( ++retries >= 3) { + MessagePrint( APE_NoGoodResponse ); + return( FALSE); + } + + if ( MessagePrint( APE_UtilInvalidResponse ) == 0) { + exit( AT_GENERIC_ERROR); + } + } + return( TRUE); +} + + + +BOOL +ArgIsServerName( + WCHAR * string + ) +/*++ + +Routine Description: + + Checks if string is a server name. Validation is really primitive, eg + strings like "\\\threeslashes" pass the test. + +Arguments: + + string - pointer to string that may represent a server name + +Return Value: + + TRUE - string is (or might be) a valid server name + FALSE - string is not a valid server name + +--*/ +{ + + NET_API_STATUS ApiStatus; + + if (string[0] == BACKSLASH && string[1] == BACKSLASH && string[2] != 0) { + ApiStatus = NetpNameValidate( + NULL, // no server name. + &string[2], // name to validate + NAMETYPE_COMPUTER, + LM2X_COMPATIBLE); // flags + if (ApiStatus != NO_ERROR) { + return (FALSE); + } + GlobalServerName = string; + return( TRUE); + } + + return( FALSE); // GlobalServerName is NULL at load time +} + + + +BOOL +ArgIsTime( + IN WCHAR * timestr, + OUT PDWORD pJobTime + ) +/*++ + +Routine Description: + + Determines whether string is a time or not. Validates that string + passed into it is in the form of HH:MM. It searches the string for + a ":" and then validates that the preceeding data is numeric & in a + valid range for hours. It then validates the string after the ":" + is numeric & in a validate range for minutes. If all the tests are + passed the TRUE is returned. + +Arguments: + + timestr - string to check whether it is a time + JobTime - ptr to number of miliseconds + +Return Value: + + TRUE - timestr was a time in HH:MM format + FALSE - timestr wasn't at time + +--*/ +{ + CHAR buffer[MAX_TIME_SIZE]; + USHORT ParseLen; + BOOL fDummy; + + if ( timestr == NULL ) + return FALSE; + + if ( !WideCharToMultiByte( CP_ACP, + 0, + timestr, + -1, + buffer, + sizeof( buffer )/sizeof(CHAR), + NULL, + &fDummy )) + { + return FALSE; + } + + if ( LUI_ParseTimeSinceStartOfDay( buffer, pJobTime, &ParseLen, 0) ) + return FALSE; + + // LUI_ParseTimeSinceStartOfDay returns the time in seconds. + // Hence, we need to convert it to microseconds. + *pJobTime *= 1000; + + return( TRUE); +} + + + +BOOL +ArgIsDecimalString( + IN WCHAR * pDecimalString, + OUT PDWORD pNumber + ) +/*++ + +Routine Description: + + This routine converts a string into a DWORD if it possibly can. + The conversion is successful if string is decimal numeric and + does not lead to an overflow. + +Arguments: + + pDecimalString ptr to decimal string + pNumber ptr to number + +Return Value: + + FALSE invalid number + TRUE valid number + +--*/ +{ + DWORD Value; + DWORD OldValue; + DWORD digit; + + if ( pDecimalString == NULL || *pDecimalString == 0) { + return( FALSE); + } + + Value = 0; + + while ( (digit = *pDecimalString++) != 0) { + + if ( digit < L'0' || digit > L'9') { + return( FALSE); // not a decimal string + } + + OldValue = Value; + Value = digit - L'0' + 10 * Value; + if ( Value < OldValue) { + return( FALSE); // overflow + } + } + + *pNumber = Value; + return( TRUE); +} + + + +BOOL +IsDayOfMonth( + IN WCHAR * pToken, + OUT PDWORD pDay + ) +/*++ + +Routine Description: + + Converts a string into a number for the day of the month, if it can + possibly do so. Note that "first" == 1, ... + +Arguments: + + pToken pointer to schedule token for the day of the month + pDay pointer to index of day in a month + +Return Value: + + TRUE if a valid schedule token + FALSE otherwise + +--*/ +{ + return ( ArgIsDecimalString( pToken, pDay) == TRUE && *pDay >= 1 + && *pDay <= 31); +} + + + +BOOL +IsDayOfWeek( + WCHAR * pToken, + PDWORD pDay + ) +/*++ + +Routine Description: + + This routine converts a string day of the week into a integer + offset into the week if it possibly can. Note that Monday==0, + ..., Sunday == 6. + +Arguments: + + pToken pointer to schedule token for the day of a week + pDay pointer to index of day in a month + +Return Value: + + TRUE if a valid schedule token + FALSE otherwise + +--*/ +{ + if ( !InitList( DaysOfWeekSearchList ) ) + { + // Error already reported + exit( -1 ); + } + + return( TraverseSearchList( + pToken, + DaysOfWeekSearchList, + pDay + )); +} + + + +NET_API_STATUS +JobAdd( + VOID + ) +/*++ + +Routine Description: + + Adds a new item to schedule. + +Arguments: + + None. Uses globals. + +Return Value: + + NET_API_STATUS return value of remote api call + +--*/ +{ + NET_API_STATUS status; + + for ( ; ; ) { + status = NetScheduleJobAdd( + GlobalServerName, + (LPBYTE)&GlobalAtInfo, + &GlobalJobId + ); + if ( status == ERROR_INVALID_PARAMETER && + GlobalAtInfo.Flags & JOB_NONINTERACTIVE) { + // + // We may have failed because we are talking to a down level + // server that does not know about JOB_NONINTERACTIVE bit. + // Clear the bit, and try again. + // A better approach would be to check the version of the + // server before making NetScheduleJobAdd() call, adjust the + // bit appropriately and only then call NetScheduleJobAdd(). + // + GlobalAtInfo.Flags &= ~JOB_NONINTERACTIVE; + } else { + break; + } + + } + if ( status == NERR_Success) { + MessagePrint( IDS_ADD_NEW_JOB, GlobalJobId ); + } else { + if ( MessagePrint( status ) == 0) { + exit( AT_GENERIC_ERROR); + } + } + + return( status); +} + + + +NET_API_STATUS +JobEnum( + VOID + ) +/*++ + +Routine Description: + + This does all of the processing necessary to dump out the entire + schedule file. It loops through on each record and formats its + information for printing and then goes to the next. + +Arguments: + + None. Uses globals. + +Return Value: + + ERROR_SUCCESS if everything enumerated OK + error returned by remote api otherwise + +--*/ +{ + BOOL first = TRUE; + DWORD ResumeJobId = 0; + NET_API_STATUS status = NERR_Success; + PAT_ENUM pAtEnum; + DWORD EntriesRead; + DWORD TotalEntries; + LPVOID EnumBuffer; + DWORD length; + WCHAR * smallBuffer = NULL; + + for ( ; ;) { + + status = NetScheduleJobEnum( + GlobalServerName, + (LPBYTE *)&EnumBuffer, + (DWORD)-1, + &EntriesRead, + &TotalEntries, + &ResumeJobId + ); + + if ( status != ERROR_SUCCESS && status != ERROR_MORE_DATA) { + length = MessagePrint( status ); + if ( length == 0) { + exit( AT_GENERIC_ERROR); + } + return( status); + } + + ASSERT( status == ERROR_SUCCESS ? TotalEntries == EntriesRead + : TotalEntries > EntriesRead); + + if ( TotalEntries == 0) { + break; // no items found + } + + if ( first == TRUE) { + length = MessagePrint( APE2_AT_DUMP_HEADER ); + if ( length == 0) { + exit( AT_GENERIC_ERROR); + } + PrintLine(); // line across screen + first = FALSE; + } + + for ( pAtEnum = EnumBuffer; EntriesRead-- > 0; pAtEnum++) { + if ( pAtEnum->Flags & JOB_EXEC_ERROR) { + if ( MessageGet( APE2_GEN_ERROR, &smallBuffer, 0 ) == 0) { + // error reported already + exit( AT_GENERIC_ERROR); + } + GenOutputArg( DUMP_FMT1, smallBuffer ); + LocalFree( smallBuffer ); + } else { + GenOutputArg( DUMP_FMT1, L""); + } + GenOutputArg( DUMP_FMT2, pAtEnum->JobId); + PrintDay( DUMP_ALL, pAtEnum->DaysOfMonth, pAtEnum->DaysOfWeek, + pAtEnum->Flags); + PrintTime( pAtEnum->JobTime); + GenOutputArg( TEXT("%ws\n"), pAtEnum->Command); + } + + if ( EnumBuffer != NULL) { + (VOID)NetApiBufferFree( (LPVOID)EnumBuffer); + EnumBuffer = NULL; + } + + if ( status == ERROR_SUCCESS) { + break; // we have read & displayed all the items + } + } + + if ( first == TRUE) { + MessagePrint( APE_EmptyList ); + } + + return( ERROR_SUCCESS); +} + + + +NET_API_STATUS +JobGetInfo( + VOID + ) +/*++ + +Routine Description: + + This prints out the schedule of an individual items schedule. + +Arguments: + + None. Uses globals. + +Return Value: + + NET_API_STATUS value returned by remote api + +--*/ + +{ + PAT_INFO pAtInfo = NULL; + NET_API_STATUS status; + + status = NetScheduleJobGetInfo( + GlobalServerName, + GlobalJobId, + (LPBYTE *)&pAtInfo + ); + if ( status != NERR_Success) { + MessagePrint( status ); + return( status); + } + + PutNewLine(); + MessagePrint( APE2_AT_DI_TASK ); + GenOutputArg( TEXT("%d"), GlobalJobId); + PutNewLine(); + + MessagePrint( APE2_AT_DI_STATUS ); + MessagePrint( (pAtInfo->Flags & JOB_EXEC_ERROR) != 0 ? + APE2_GEN_ERROR : APE2_GEN_OK ); + PutNewLine(); + + MessagePrint( APE2_AT_DI_SCHEDULE ); + PrintDay( DUMP_ID, pAtInfo->DaysOfMonth, pAtInfo->DaysOfWeek, + pAtInfo->Flags); + PutNewLine(); + + MessagePrint( APE2_AT_DI_TIMEOFDAY ); + PrintTime( pAtInfo->JobTime); + PutNewLine(); + + MessagePrint( APE2_AT_DI_INTERACTIVE); + MessagePrint( (pAtInfo->Flags & JOB_NONINTERACTIVE) == 0 ? + APE2_GEN_YES : APE2_GEN_NO ); + PutNewLine(); + + MessagePrint( APE2_AT_DI_COMMAND ); + GenOutputArg( TEXT("%ws\n"), pAtInfo->Command); + PutNewLine2(); + + (VOID)NetApiBufferFree( (LPVOID)pAtInfo); + + return( NERR_Success); +} + + + +DWORD +MatchString( + WCHAR * name, + DWORD Values + ) +/*++ +Routine Description: + + Parses switch string and returns NULL for an invalid switch, + and -1 for an ambiguous switch. + +Arguments: + + name - pointer to string we need to examine + Values - bitmask of values of interest + +Return Value: + + Pointer to command, or NULL or -1. +--*/ +{ + WCHAR * String; + PSEARCH_LIST pCurrentList; + WCHAR * CurrentString; + DWORD FoundValue; + int nmatches; + int longest; + + if ( !InitList( GlobalListTable ) ) + { + // Error already reported + exit( -1 ); + } + + for ( pCurrentList = GlobalListTable, + longest = nmatches = 0, + FoundValue = 0; + (CurrentString = pCurrentList->String) != NULL; + pCurrentList++) { + + if ( (Values & pCurrentList->Value) == 0) { + continue; // skip this List + } + + for ( String = name; *String == *CurrentString++; String++) { + if ( *String == 0) { + return( pCurrentList->Value); // exact match + } + } + + if ( !*String) { + + if ( String - name > longest) { + + longest = String - name; + nmatches = 1; + FoundValue = pCurrentList->Value; + + } else if ( String - name == longest) { + + nmatches++; + } + } + } + + // 0 corresponds to no match at all (invalid List) + // while -1 corresponds to multiple match (ambiguous List). + + if ( nmatches != 1) { + return ( (nmatches == 0) ? 0 : -1); + } + + return( FoundValue); +} + + +DWORD +MessageGet( + IN DWORD MessageId, + OUT LPWSTR *buffer, + IN DWORD Size + ) +/*++ + +Routine Description: + + Fills in the unicode message corresponding to a given message id, + provided that a message can be found and that it fits in a supplied + buffer. + +Arguments: + + MessageId - message id + buffer - pointer to caller supplied buffer + Size - size (always in bytes) of supplied buffer, + If size is 0, buffer will be allocated by FormatMessage. + +Return Value: + + Count of characters, not counting the terminating null character, + returned in the buffer. Zero return value indicates failure. + +--*/ +{ + DWORD length; + LPVOID lpSource; + DWORD dwFlags; + + if ( MessageId < NERR_BASE) { + // + // Get message from system. + // + lpSource = NULL; // redundant step according to FormatMessage() spec + dwFlags = FORMAT_MESSAGE_FROM_SYSTEM; + + } else if ( ( MessageId >= APE2_AT_DEL_WARNING + && MessageId <= APE2_AT_DI_INTERACTIVE) + || ( MessageId >= IDS_LOAD_LIBRARY_FAILURE + && MessageId <= IDS_INTERACTIVE )) { + // + // Get message from this module. + // + lpSource = NULL; + dwFlags = FORMAT_MESSAGE_FROM_HMODULE; + + } else { + // + // Get message from netmsg.dll. + // + lpSource = GlobalMessageHandle; + dwFlags = FORMAT_MESSAGE_FROM_HMODULE; + } + + if ( Size == 0 ) + dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; + + length = FormatMessage( + dwFlags, // dwFlags + lpSource, // lpSource + MessageId, // MessageId + 0, // dwLanguageId + (LPWSTR) buffer, // lpBuffer + Size, // nSize + NULL // lpArguments + ); + + if ( length == 0) { + MessagePrint( IDS_MESSAGE_GET_ERROR, MessageId, GetLastError()); + } + return( length); + +} // MessageGet() + + + +int +FileIsConsole( + int fh + ) +{ + unsigned htype ; + + htype = GetFileType(GetStdHandle(fh)); + htype &= ~FILE_TYPE_REMOTE; + return htype == FILE_TYPE_CHAR; +} + + + +DWORD +ConsolePrint( + LPWSTR pch, + int cch + ) +{ + int cchOut; + int err; + CHAR *pchOemBuffer; + + if (FileIsConsole(STD_OUTPUT_HANDLE)) { + err = WriteConsole( + GetStdHandle(STD_OUTPUT_HANDLE), + pch, cch, + &cchOut, NULL); + if (!err || cchOut != cch) + goto try_again; + } + else if ( cch != 0) { +try_again: + cchOut = WideCharToMultiByte(CP_OEMCP, 0, pch, cch, NULL, 0, NULL,NULL); + if (cchOut == 0) + return 0; + + if ((pchOemBuffer = (CHAR *)malloc(cchOut)) != NULL) { + WideCharToMultiByte(CP_OEMCP, 0, pch, cch, + pchOemBuffer, cchOut, NULL, NULL); + WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), + pchOemBuffer, cchOut, &cch, NULL); + free(pchOemBuffer); + } + } + return cchOut; +} + + + +DWORD +MessagePrint( + IN DWORD MessageId, + ... + ) +/*++ + +Routine Description: + + Finds the unicode message corresponding to the supplied message id, + merges it with caller supplied string(s), and prints the resulting + string. + +Arguments: + + MessageId - message id + +Return Value: + + Count of characters, not counting the terminating null character, + printed by this routine. Zero return value indicates failure. + +--*/ +{ + va_list arglist; + WCHAR * buffer = NULL; + DWORD length; + LPVOID lpSource; + DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER; + + + va_start( arglist, MessageId ); + + if ( MessageId < NERR_BASE) { + // + // Get message from system. + // + lpSource = NULL; // redundant step according to FormatMessage() spec + dwFlags |= FORMAT_MESSAGE_FROM_SYSTEM; + + } else if ( ( MessageId >= APE2_AT_DEL_WARNING + && MessageId <= APE2_AT_DI_INTERACTIVE) + || ( MessageId >= IDS_LOAD_LIBRARY_FAILURE + && MessageId <= IDS_INTERACTIVE )) { + // + // Get message from this module. + // + lpSource = NULL; + dwFlags |= FORMAT_MESSAGE_FROM_HMODULE; + + } else { + // + // Get message from netmsg.dll. + // + lpSource = GlobalMessageHandle; + dwFlags |= FORMAT_MESSAGE_FROM_HMODULE; + } + + length = FormatMessage( + dwFlags, // dwFlags + lpSource, // lpSource + MessageId, // MessageId + 0L, // dwLanguageId + (LPTSTR)&buffer, // lpBuffer + 0, // size + &arglist // lpArguments + ); + + length = ConsolePrint(buffer, length); + + LocalFree(buffer); + + return( length); + +} // MessagePrint() + + + +BOOL +ParseJobIdArgs( + WCHAR ** argv, + int argc, + int argno, + PBOOL pDeleteFound + ) +/*++ + +Routine Description: + + Parses arguments for commands containing JobId (these can be JobGetInfo + and JobDel commands). It loops through JobId arguments making sure that + we have at most one "yes-no" switch and at most one "delete" switch and + nothing else. + +Arguments: + + argv argument list + argc number of arguments to parse + argno index of argument to begin parsing from + pDeleteFound did we find a delete switch or not + +Return Value: + + FALSE invalid argument found + TRUE valid arguments + +--*/ +{ + BOOL FoundDeleteSwitch; + + for ( FoundDeleteSwitch = FALSE; argno < argc; argno++) { + + WCHAR * argp; + DWORD length; + DWORD Value; + + argp = argv[ argno]; + + if ( *argp++ != SLASH) { + return( FALSE); // not a switch + } + + _wcsupr( argp); + length = wcslen( argp); + + Value = MatchString( argp, AT_YES_VALUE | AT_DELETE_VALUE); + + if ( Value == AT_YES_VALUE) { + + if ( GlobalYes == TRUE) { + return( FALSE); // multiple instances of yes switch + } + + GlobalYes = TRUE; + continue; + } + + if ( Value == AT_DELETE_VALUE) { + + if ( FoundDeleteSwitch == TRUE) { + return( FALSE); // duplicate delete switch + } + FoundDeleteSwitch = TRUE; + continue; + } + + return( FALSE); // an unknown switch + } + + *pDeleteFound = FoundDeleteSwitch; + return( TRUE); +} // ParseJobIdArgs() + + + +BOOL +ParseTimeArgs( + WCHAR ** argv, + int argc, + int argno, + int * pargno + ) +/*++ + +Routine Description: + + Parses arguments for command addition. + +Arguments: + + argv argument list + argc count of args + argno index of the first arg to validate + pargno ptr to the index of the first non-switch arg + +Return Value: + + TRUE all arguments are valid + FALSE otherwise + +--*/ +{ + DWORD day_no; // day number for scheduling + DWORD NextCount = 0; // count of next switches + DWORD EveryCount = 0; // count of every switches + WCHAR * argp; // ptr to arg string + WCHAR * schedp; // work ptr to arg string + DWORD Value; // bitmask + + for ( NOTHING; argno < argc; argno++) { + + argp = argv[ argno]; + + if ( *argp++ != SLASH) { + break; // found non-switch, we are done + } + + + schedp = wcschr( argp, ARG_SEP_CHR); + + if ( schedp == NULL) { + return( FALSE); + } + + _wcsupr( argp); // upper case entire input, not just the switch name + + *schedp = 0; + + Value = MatchString( argp, AT_NEXT_VALUE | AT_EVERY_VALUE); + + if ( Value == AT_NEXT_VALUE) { + + NextCount++; + + } else if ( Value == AT_EVERY_VALUE) { + + EveryCount++; + GlobalAtInfo.Flags |= JOB_RUN_PERIODICALLY; + + } else { + + return( FALSE); // an unexpected switch + } + + if ( NextCount + EveryCount > 1) { + return( FALSE); // repeated switch option + } + + *schedp++ = ARG_SEP_CHR; + + schedp = wcstok( schedp, SCHED_TOK_DELIM); + + if ( schedp == NULL) { + GlobalAtInfo.Flags |= JOB_ADD_CURRENT_DATE; + continue; + } + + while( schedp != NULL) { + + if ( IsDayOfMonth( schedp, &day_no) == TRUE) { + + GlobalAtInfo.DaysOfMonth |= (1 << (day_no - 1)); + + } else if ( IsDayOfWeek( schedp, &day_no) == TRUE) { + + GlobalAtInfo.DaysOfWeek |= (1 << day_no); + + } else { + MessagePrint( APE_InvalidSwitchArg ); + GlobalErrorReported = TRUE; + return( FALSE); + } + + schedp = wcstok( NULL, SCHED_TOK_DELIM); + } + } + + if ( argno == argc) { + return( FALSE); // all switches, no command + } + + *pargno = argno; + return( TRUE); +} + + + +BOOL +ParseInteractiveArg( + IN OUT WCHAR * argp + ) +/*++ + +Routine Description: + + Returns TRUE if argp is an interactive switch. + +--*/ +{ + DWORD Value; // bitmask + + if ( *argp++ != SLASH) { + return( FALSE); // not a switch + } + + _wcsupr( argp); // all AT command switches can be safely uppercased + + Value = MatchString( argp, AT_INTERACTIVE); + + if ( Value == AT_INTERACTIVE) { + GlobalAtInfo.Flags &= ~JOB_NONINTERACTIVE; // clear noninteractive flag + return( TRUE); + } + + return( FALSE); // some other switch +} + + + +VOID +PrintDay( + int type, + DWORD DaysOfMonth, + UCHAR DaysOfWeek, + UCHAR Flags + ) +/*++ + +Routine Description: + + Print out schedule days. This routine converts a schedule bit map + to the literals that represent the schedule. + +Arguments: + + type whether this is for JobEnum or not + DaysOfMonth bitmask for days of month + DaysOfWeek bitmaks for days of week + Flags extra info about the job + +Return Value: + + None. + +--*/ +{ + int i; + WCHAR Buffer[ 128]; + DWORD BufferLength; + DWORD Length; + DWORD TotalLength = 0; + DWORD TotalColumnLength = 0; + WCHAR * LastSpace; + DWORD MessageId; + BOOL OverFlow = TRUE; + static int Ape2GenWeekdayLong[] = { + APE2_GEN_MONDAY, + APE2_GEN_TUESDAY, + APE2_GEN_WEDNSDAY, + APE2_GEN_THURSDAY, + APE2_GEN_FRIDAY, + APE2_GEN_SATURDAY, + APE2_GEN_SUNDAY + }; + static int Ape2GenWeekdayAbbrev[] = { + APE2_GEN_MONDAY_ABBREV, + APE2_GEN_TUESDAY_ABBREV, + APE2_GEN_WEDNSDAY_ABBREV, + APE2_GEN_THURSDAY_ABBREV, + APE2_GEN_FRIDAY_ABBREV, + APE2_GEN_SATURDAY_ABBREV, + APE2_GEN_SUNDAY_ABBREV + }; + + // + // Subtract 4 to guard against days of week or days of month overflow. + // + BufferLength = sizeof( Buffer)/ sizeof( WCHAR) - 4; + if ( type == DUMP_ALL && BufferLength > MAX_SCHED_FIELD_LENGTH) { + BufferLength = MAX_SCHED_FIELD_LENGTH; + } + + // + // First do the descriptive bit (eg. EACH, NEXT, etc) with the days. + // + + if ( Flags & JOB_RUN_PERIODICALLY) { + + MessageId = APE2_AT_EACH; + + } else if ( (DaysOfWeek != 0) || (DaysOfMonth != 0)) { + + MessageId = APE2_AT_NEXT; + + } else if ( Flags & JOB_RUNS_TODAY) { + + MessageId = APE2_AT_TODAY; + + } else { + + MessageId = APE2_AT_TOMORROW; + } + + Length = MessageGet( + MessageId, + (LPWSTR *) &Buffer[TotalLength], + BufferLength + ); + if ( Length == 0) { + goto PrintDay_exit; // Assume this is due to lack of space + } + TotalColumnLength = GetStringColumn( &Buffer[TotalLength] ); + TotalLength = Length; + + if ( DaysOfWeek != 0) { + + for ( i = 0; i < 7; i++) { + + if ( ( DaysOfWeek & (1 << i)) != 0) { + + if( bDBCS ) { + Length = MessageGet( + Ape2GenWeekdayLong[ i], + (LPWSTR *) &Buffer[TotalLength], + BufferLength - TotalLength + ); + } else { + Length = MessageGet( + Ape2GenWeekdayAbbrev[ i], + (LPWSTR *) &Buffer[TotalLength], + BufferLength - TotalLength + ); + } + if ( Length == 0) { + // + // Not enough room for WeekDay symbol + // + goto PrintDay_exit; + + } + // + // Get how many columns will be needed for display. + // + TotalColumnLength += GetStringColumn( &Buffer[TotalLength] ); + + if ( TotalColumnLength >= BufferLength) { + // + // Not enough room for space following WeekDay symbol + // + goto PrintDay_exit; + } + TotalLength +=Length; + Buffer[ TotalLength++] = BLANK; + TotalColumnLength++; + } + } + } + + if ( DaysOfMonth != 0) { + + for ( i = 0; i < 31; i++) { + + if ( ( DaysOfMonth & (1L << i)) != 0) { + + Length = swprintf( + &Buffer[ TotalLength], + L"%d ", + i + 1 + ); + if ( TotalLength + Length > BufferLength) { + // + // Not enough room for MonthDay symbol followed by space + // + goto PrintDay_exit; + } + TotalLength +=Length; + TotalColumnLength +=Length; + } + } + } + + OverFlow = FALSE; + +PrintDay_exit: + + Buffer[ TotalLength] = NULLC; + + if ( OverFlow == TRUE) { + + if ( TotalLength > 0 && Buffer[ TotalLength - 1] == BLANK) { + // + // Eliminate trailing space if there is one. + // + Buffer[ TotalLength - 1] = NULLC; + } + + // + // Then get rid of the rightmost token (or even whole thing). + // + + LastSpace = wcsrchr( Buffer, BLANK); + + wcscpy( LastSpace != NULL ? LastSpace : Buffer, ELLIPSIS); + + TotalLength = wcslen( Buffer); + + } + + if ( type == DUMP_ALL) { + while( TotalColumnLength++ < MAX_SCHED_FIELD_LENGTH) { + Buffer[ TotalLength++] = BLANK; + } + Buffer[ TotalLength] = UNICODE_NULL; + } + + GenOutputArg( TEXT("%ws"), Buffer); +} + + +VOID +PrintLine( + VOID + ) +/*++ + +Routine Description: + + Prints a line accross screen. + +Arguments: + + None. + +Return Value: + + None. + +Note: + + BUGBUG Is this treatment valid for UniCode? See also LUI_PrintLine() + BUGBUG in ui\common\src\lui\lui\border.c + +--*/ +#define SINGLE_HORIZONTAL L'\x02d' +#define SCREEN_WIDTH 79 +{ + WCHAR string[ SCREEN_WIDTH + 1]; + DWORD offset; + + + for ( offset = 0; offset < SCREEN_WIDTH; offset++) { + string[ offset] = SINGLE_HORIZONTAL; + } + + string[ SCREEN_WIDTH] = NULLC; + GenOutputArg(TEXT("%ws\n"), string); +} + + +VOID +PrintTime( + DWORD JobTime + ) +/*++ + +Routine Description: + + Prints time of a job in HH:MM{A,P}M format. + +Arguments: + + JobTime - time in miliseconds (measured from midnight) + +Return Value: + + None. + +Note: + + BUGBUG this does not make sure that JobTime is within the bounds. + BUGBUG Also, there is nothing unicode about printing this output. + +--*/ +{ + WCHAR Buffer[15]; + + GetTimeString( JobTime, Buffer, sizeof( Buffer)/sizeof( WCHAR) ); + GenOutputArg( DUMP_FMT3, Buffer ); +} + + + +BOOL +TraverseSearchList( + IN PWCHAR String, + IN PSEARCH_LIST SearchList, + OUT PDWORD pValue + ) +/*++ + +Routine Description: + + Examines search list until it find the correct entry, then returns + the value corresponding to this entry. + +Arguments: + + String - string to match + SearchList - array of entries containing valid strings + pValue - value corresponding to a matching valid string + +Return Value: + + TRUE a matching entry was found + FALSE otherwise + +--*/ +{ + if ( SearchList != NULL) { + + for ( NOTHING; SearchList->String != NULL; SearchList++) { + + if ( _wcsicmp( String, SearchList->String) == 0) { + *pValue = SearchList->Value; + return( TRUE) ; + } + } + } + return( FALSE) ; +} + + + +VOID +Usage( + BOOL GoodCommand + ) +/*++ + +Routine Description: + + Usage of AT command. + +Arguments: + + GoodCommand - TRUE if we have a good command input (request for help) + FALSE if we have a bad command input + +Return Value: + + None. + +--*/ +{ + if ( GlobalErrorReported == TRUE) { + PutNewLine(); + } else if ( GoodCommand == FALSE) { + MessagePrint( IDS_INVALID_COMMAND ); + } + + MessagePrint( IDS_USAGE ); +} + +#define REG_SCHEDULE_PARMS TEXT("System\\CurrentControlSet\\Services\\Schedule\\Parameters") + +#define REG_SCHEDULE_USE_OLD TEXT("UseOldParsing") + +BOOL +UseOldParsing() +/*++ + +Routine Description: + + Checks the registry for + + HKLM\CurrentControlSet\Services\Schedule\parameters\UseOldParsing + + If present and equal to 1, then revert to 3.51 level of command line + parsing. Spaces in filenames will not work with this option. This is + intended as a migration path for customers who cannot change all their + command scripts that use AT.EXE right away. + +--*/ +{ + BOOL fUseOld = FALSE; + LONG err = 0; + + do { // Error breakout loop + + HKEY hkeyScheduleParms; + DWORD dwType; + DWORD dwData = 0; + DWORD cbData = sizeof(dwData); + + // Break out on any error and use the default, FALSE. + + if (err = RegOpenKeyEx(HKEY_LOCAL_MACHINE, + REG_SCHEDULE_PARMS, + 0, + KEY_READ, + &hkeyScheduleParms)) + { + break; + } + if (err = RegQueryValueEx(hkeyScheduleParms, + REG_SCHEDULE_USE_OLD, + NULL, + &dwType, + (LPBYTE)&dwData, + &cbData )) + { + RegCloseKey( hkeyScheduleParms ); + break; + } + + if ( dwType == REG_DWORD && dwData == 1 ) + { + fUseOld = TRUE; + } + + RegCloseKey( hkeyScheduleParms ); + + + } while (FALSE) ; + + return fUseOld; + +} + + + +BOOL +ValidateCommand( + IN int argc, + IN WCHAR ** argv, + OUT int * pCommand + ) +/*++ + +Routine Description: + + Examines command line to see what to do. This validates the command + line passed into the AT command processor. If this routine finds any + invalid data, the program exits with an appropriate error message. + +Arguments: + + pCommand - pointer to command + argc - count of arguments + argv - pointer to table of arguments + +Return Value: + + FALSE - if failure, i.e. command will not be executed + TRUE - if success + +Comment: + + Parsing assumes: + + non-switch (positional) parameters come first and order among these + parameters is important + + switch parameters come second and order among these parameters is + NOT important + + command (if present) comes last + +--*/ +{ + int i; // loop index + int next; // index of Time or JobId argument + int argno; // where to start in arg string + BOOL DeleteFound; // did we find a delete switch + WCHAR * recdatap; // ptr used to build atr_command + DWORD recdata_len; // len of arg to put in atr_command + DWORD JobTime; + WCHAR wszTmp[MAX_COMMAND_LEN]; + BOOL fUseOldParsing = FALSE; + + if (argc == 1) { + *pCommand = DUMP_ALL; + return( TRUE); + } + + // First look for a help switch on the command line. + + for ( i = 1; i < argc; i++ ) { + + if ( !_wcsicmp( argv[i], QUESTION_SW) + || !_wcsicmp( argv[i], QUESTION_SW_TOO)) { + + *pCommand = ACTION_USAGE; + return( TRUE); + } + } + + next = ( ArgIsServerName( argv[ 1]) == TRUE) ? 2 : 1; + if ( argc == next) { + *pCommand = DUMP_ALL; + return( TRUE); + } + + if ( (ArgIsDecimalString( argv[ next], &GlobalJobId)) == TRUE) { + + if ( argc == next + 1) { + *pCommand = DUMP_ID; + return( TRUE); + } + + if ( ParseJobIdArgs( argv, argc, next + 1, &DeleteFound) == FALSE) { + return( FALSE); // an invalid argument + } + + *pCommand = (DeleteFound == FALSE) ? DUMP_ID : DEL_ID; + return( TRUE); + } + + // + // Try some variation of "AT [\\ServerName [/DELETE]" + // + if ( ParseJobIdArgs( argv, argc, next, &DeleteFound) == TRUE) { + *pCommand = (DeleteFound == FALSE) ? DUMP_ALL : DEL_ALL; + return( TRUE); + } + + if ( ArgIsTime( argv[ next], &JobTime) == TRUE) { + + *pCommand = ADD_TO_SCHEDULE; + + if ( argc < next + 2) { + return( FALSE); // need something to do, not just time + } + + memset( (PBYTE)&GlobalAtInfo, '\0', sizeof(GlobalAtInfo)); // initialize + GlobalAtInfo.Flags |= JOB_NONINTERACTIVE; // the default + + if ( ParseInteractiveArg( argv[ next + 1])) { + next++; + } + if ( argc < next + 2) { + return( FALSE); // once more with feeling + } + + if ( ParseTimeArgs( argv, argc, next + 1, &argno) == FALSE) { + return( FALSE); + } + + // Copy argument strings to record. + + recdatap = GlobalAtInfo.Command = GlobalAtInfoCommand; + recdata_len = 0; + + fUseOldParsing = UseOldParsing(); + + for ( i = argno; i < argc; i++) { + + DWORD temp; + + // + // Fix for bug 22068 "AT command does not handle filenames with + // spaces." The command processor takes a quoted command line arg + // and puts everything between the quotes into one argv string. + // The quotes are stripped out. Thus, if any of the string args + // contain whitespace, then they must be requoted before being + // concatonated into the command value. + // + if (!fUseOldParsing && wcschr(argv[i], L' ') != NULL) + { + wcscpy(wszTmp, L"\""); + wcscat(wszTmp, argv[i]); + wcscat(wszTmp, L"\""); + } + else + { + wcscpy(wszTmp, argv[i]); + } + + temp = wcslen(wszTmp) + 1; + + recdata_len += temp; + + if ( recdata_len > MAX_COMMAND_LEN) { + MessagePrint( APE_AT_COMMAND_TOO_LONG ); + return( FALSE); + } + + wcscpy( recdatap, wszTmp); + recdatap += temp; + + // To construct lpszCommandLine argument to CreateProcess call + // we replace nuls with spaces. + + *(recdatap - 1) = BLANK; + + } + + // Reset space back to null on last argument in string. + + *(recdatap - 1) = NULLC; + GlobalAtInfo.JobTime = JobTime; + return( TRUE); + } + + return( FALSE); +} + + +VOID +GetTimeString( + DWORD Time, + WCHAR *Buffer, + int BufferLength + ) +/*++ + +Routine Description: + + This function converts a dword time to an ASCII string. + +Arguments: + + Time - Time difference in dword from start of the day (i.e. 12am + midnight ) in milliseconds + + Buffer - Pointer to the buffer to place the ASCII representation. + + BufferLength - The length of buffer in bytes. + +Return Value: + + None. + +--*/ +#define MINUTES_IN_HOUR 60 +#define SECONDS_IN_MINUTE 60 +{ + WCHAR szTimeString[MAX_TIME_SIZE]; + WCHAR *p = &szTimeString[1]; + DWORD seconds, minutes, hours; + int numChars; + DWORD flags; + SYSTEMTIME st; + + GetSystemTime(&st); + *p = NULLC; + + // Check if the time format is initialized. If not, initialize it. + if ( GlobalTimeFormat.AMString == NULL ) + NetpGetTimeFormat( &GlobalTimeFormat ); + + // Convert the time to hours, minutes, seconds + seconds = (Time/1000); + hours = seconds / (MINUTES_IN_HOUR * SECONDS_IN_MINUTE ); + seconds -= hours * MINUTES_IN_HOUR * SECONDS_IN_MINUTE; + minutes = seconds / SECONDS_IN_MINUTE; + seconds -= minutes * SECONDS_IN_MINUTE; + + st.wHour = (WORD)(hours); + st.wMinute = (WORD)(minutes); + st.wSecond = (WORD)(seconds); + st.wMilliseconds = 0; + + flags = TIME_NOSECONDS; + if (!GlobalTimeFormat.TwelveHour) + flags |= TIME_FORCE24HOURFORMAT; + + numChars = GetTimeFormatW(GetThreadLocale(), + flags, &st, NULL, p, MAX_TIME_SIZE-1); + + if ( numChars > BufferLength ) + numChars = BufferLength; + + if (*(p+1) == ARG_SEP_CHR && GlobalTimeFormat.LeadingZero) { + *(--p) = TEXT('0'); + numChars++; + } + wcsncpy( Buffer, p, numChars ); + // Append spece for align print format. column based. + { + DWORD ColumnLength; + + // character counts -> array index. + numChars--; + + ColumnLength = GetStringColumn( Buffer ); + + while( ColumnLength++ < MAX_TIME_FIELD_LENGTH) { + Buffer[ numChars++] = BLANK; + } + Buffer[ numChars] = UNICODE_NULL; + } +} + + +BOOL +InitList( PSEARCH_LIST SearchList ) +{ + if ( SearchList != NULL) { + + if ( SearchList->String != NULL ) // Already initialized + return TRUE; + + for ( NOTHING; SearchList->MessageId != 0; SearchList++) { + if ( MessageGet( SearchList->MessageId, + &SearchList->String, + 0 ) == 0 ) + { + return FALSE; + } + } + } + + return TRUE; +} + + +VOID +TermList( PSEARCH_LIST SearchList ) +{ + if ( SearchList != NULL) { + + if ( SearchList->String == NULL ) // Not initialized + return; + + for ( NOTHING; SearchList->String != NULL; SearchList++) { + LocalFree( SearchList->String ); + } + } +} + + +DWORD +GetStringColumn( WCHAR *lpwstr ) +{ + int cchNeed; + + cchNeed = WideCharToMultiByte( GetConsoleOutputCP() , 0 , + lpwstr , -1 , + NULL , 0 , + NULL , NULL ); + + return( (DWORD) cchNeed - 1 ); // - 1 : remove NULL +} diff --git a/private/net/svcdlls/at/atcmd/lmatmsg.mc b/private/net/svcdlls/at/atcmd/lmatmsg.mc new file mode 100644 index 000000000..edd658fcf --- /dev/null +++ b/private/net/svcdlls/at/atcmd/lmatmsg.mc @@ -0,0 +1,193 @@ +;/*++ +; +;Copyright (c) 1992 Microsoft Corporation +; +;Module Name: +; +; lmatmsg.h +; +;Abstract: +; +; Definitions for AT command private errors. +; +;Author: +; +; Vladimir Z. Vulovic (vladimv) 06 - November - 1992 +; +;Revision History: +; +; 06-Nov-1992 vladimv +; Created +; 20-Feb-1993 yihsins +; Move hard-coded strings from atcmd.c to here. +; +;Notes: +; +; This file is generated by the MC tool from the lmatmsg.mc file. +; +; +; In lanman2.1 and before, messages for AT utility used to be defined in +; common\h\apperr2.h. In NT windows, error codes belonging to AT utility +; have been reused by redir statistics. Therefore, we have to invent a +; whole new message file for these lost messages! +; +; Note that %1 was removed from messages for APE2_AT_EACH & APE2_AT_NEXT. +; +;--*/ + +MessageId=4630 SymbolicName=APE2_AT_DEL_WARNING +Language=English +This operation will delete all scheduled jobs. +. + +MessageId=+1 SymbolicName=APE2_AT_SCHED_FILE_CLEARED +Language=English +The AT schedule file was cleared. +. + +MessageId=+1 SymbolicName=APE2_AT_TODAY +Language=English +Today %0 +. + +MessageId=+1 SymbolicName=APE2_AT_TOMORROW +Language=English +Tomorrow %0 +. + +MessageId=+1 SymbolicName=APE2_AT_NEXT +Language=English +Next %0 +. + +MessageId=+1 SymbolicName=APE2_AT_EACH +Language=English +Each %0 +. + +;// ++++++++++++++++++++++++++++ WARNING ++++++++++++++++++++++++++ +;// +;// The following messages up to APE2_AT_DI_INTERACTIVE are aligned to +;// look right when printed on the screen (they appear in a table). +;// If they are changed, make sure they occupy the same number of +;// chars (text+space). In the case of AT_DUMP_HEADER, the column +;// headers should start at the same place (left justified). +;// +;// --------------------------------------------------------------- + + +MessageId=+1 SymbolicName=APE2_AT_DUMP_HEADER +Language=English +Status ID Day Time Command Line +. + +MessageId=+1 SymbolicName=APE2_AT_DI_TASK +Language=English +Task ID: %0 +. + +MessageId=+1 SymbolicName=APE2_AT_DI_STATUS +Language=English +Status: %0 +. + +MessageId=+1 SymbolicName=APE2_AT_DI_SCHEDULE +Language=English +Schedule: %0 +. + +MessageId=+1 SymbolicName=APE2_AT_DI_TIMEOFDAY +Language=English +Time of day: %0 +. + +MessageId=+1 SymbolicName=APE2_AT_DI_COMMAND +Language=English +Command: %0 +. + +MessageId=+1 SymbolicName=APE2_AT_DI_INTERACTIVE +Language=English +Interactive: %0 +. + +MessageId=10000 SymbolicName=IDS_LOAD_LIBRARY_FAILURE +Language=English +LoadLibrary() fails with winError = %1!d! +. +MessageId=10001 SymbolicName=IDS_UNABLE_TO_MAP_TO_UNICODE +Language=English +Failed to convert input strings to unicode strings. +. +MessageId=10002 SymbolicName=IDS_UNABLE_TO_MAKE_STRING_TO_UNICODE +Language=English +Failed to make unicode string out of %1. +. +MessageId=10003 SymbolicName=IDS_ADD_NEW_JOB +Language=English +Added a new job with job ID = %1!d! +. +MessageId=10004 SymbolicName=IDS_MESSAGE_GET_ERROR +Language=English +FormatMessage(%1!d!) fails with winError = %2!d! +. +MessageId=10005 SymbolicName=IDS_MESSAGE_PRINT_ERROR +Language=English +FormatMessage(%1!d!) fails with winError = %2!d! +. +MessageId=10006 SymbolicName=IDS_INVALID_COMMAND +Language=English +Invalid command.%n +. +MessageId=10007 SymbolicName=IDS_YES +Language=English +YES%0 +. +MessageId=10008 SymbolicName=IDS_DELETE +Language=English +DELETE%0 +. +MessageId=10009 SymbolicName=IDS_EVERY +Language=English +EVERY%0 +. +MessageId=10010 SymbolicName=IDS_NEXT +Language=English +NEXT%0 +. +MessageId=10011 SymbolicName=IDS_NO +Language=English +NO%0 +. +MessageId=10014 SymbolicName=IDS_USAGE +Language=English +The AT command schedules commands and programs to run on a computer at +a specified time and date. The Schedule service must be running to use +the AT command.%n +AT [\\computername] [ [id] [/DELETE] | /DELETE [/YES]] +AT [\\computername] time [/INTERACTIVE] + [ /EVERY:date[,...] | /NEXT:date[,...]] "command"%n +\\computername Specifies a remote computer. Commands are scheduled on the + local computer if this parameter is omitted. +id Is an identification number assigned to a scheduled + command. +/delete Cancels a scheduled command. If id is omitted, all the + scheduled commands on the computer are canceled. +/yes Used with cancel all jobs command when no further + confirmation is desired. +time Specifies the time when command is to run. +/interactive Allows the job to interact with the desktop of the user + who is logged on at the time the job runs. +/every:date[,...] Runs the command on each specified day(s) of the week or + month. If date is omitted, the current day of the month + is assumed. +/next:date[,...] Runs the specified command on the next occurrence of the + day (for example, next Thursday). If date is omitted, the + current day of the month is assumed. +"command" Is the Windows NT command, or batch program to be run.%n +. +MessageId=10015 SymbolicName=IDS_INTERACTIVE +Language=English +INTERACTIVE%0 +. +
\ No newline at end of file diff --git a/private/net/svcdlls/at/atcmd/makefile b/private/net/svcdlls/at/atcmd/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/net/svcdlls/at/atcmd/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/net/svcdlls/at/atcmd/makefile.inc b/private/net/svcdlls/at/atcmd/makefile.inc new file mode 100644 index 000000000..5157fa663 --- /dev/null +++ b/private/net/svcdlls/at/atcmd/makefile.inc @@ -0,0 +1,4 @@ +lmatmsg.rc: msg00001.bin + +lmatmsg.h msg00001.bin: lmatmsg.mc + mc -v lmatmsg.mc diff --git a/private/net/svcdlls/at/atcmd/sources b/private/net/svcdlls/at/atcmd/sources new file mode 100644 index 000000000..d931aa3e6 --- /dev/null +++ b/private/net/svcdlls/at/atcmd/sources @@ -0,0 +1,32 @@ +MAJORCOMP = bozo +MINORCOMP = bozo + +TARGETNAME=at + +TARGETPATH=obj +TARGETTYPE=PROGRAM + +INCLUDES=..;..\..\..\inc;..\..\..\..\inc + +C_DEFINES=-DRPC_NO_WINDOWS_H -DUNICODE -D_UNICODE + +USE_CRTDLL=1 + +WARNING_LEVEL=-W4 + +SOURCES= \ + atcmd.c \ + at.rc + +UMTYPE=console + +TARGETLIBS= \ + $(BASEDIR)\public\sdk\lib\*\ntdll.lib \ + $(BASEDIR)\Public\Sdk\Lib\*\netlib.lib \ + $(BASEDIR)\public\sdk\lib\*\netapi32.lib \ + $(BASEDIR)\Public\Sdk\Lib\*\shell32.lib \ + $(BASEDIR)\public\sdk\lib\*\user32.lib + +NTTARGETFILE0= \ + lmatmsg.h \ + lmatmsg.mc |