windows-nt/Source/XPSP1/NT/ds/netapi/netcmd/common/help.c
2020-09-26 16:20:57 +08:00

844 lines
19 KiB
C

/********************************************************************/
/** Microsoft LAN Manager **/
/** Copyright(c) Microsoft Corp., 1987-1990 **/
/********************************************************************/
/***
* help.c
* Functions that give access to the text in the file net.hlp
*
* Format of the file net.hlp
*
* History
* ??/??/??, stevero, initial code
* 10/31/88, erichn, uses OS2.H instead of DOSCALLS
* 01/04/89, erichn, filenames now MAX_PATH LONG
* 05/02/89, erichn, NLS conversion
* 06/08/89, erichn, canonicalization sweep
* 02/20/91, danhi, change to use lm 16/32 mapping layer
***/
/* Include files */
#define INCL_ERRORS
#define INCL_NOCOMMON
#define INCL_DOSPROCESS
#include <os2.h>
#include <lmcons.h>
#include <apperr.h>
#include <apperr2.h>
#include <lmerr.h>
#include <stdio.h>
#include <malloc.h>
#include "lui.h"
#include "netcmds.h"
#include "msystem.h"
/* Constants */
#define ENTRY_NOT_FOUND -1
#define NEXT_RECORD 0
#define WHITE_SPACE TEXT("\t\n\x0B\x0C\r ")
#define LINE_LEN 82
#define OPTION_MAX_LEN 512
#define DELS TEXT(":,\n")
#define CNTRL (text[0] == DOT || text[0] == COLON || text[0] == POUND|| text[0] == DOLLAR)
#define FCNTRL (text[0] == DOT || text[0] == COLON)
#define HEADER (text[0] == PERCENT || text[0] == DOT || text[0] == BANG)
#define ALIAS (text[0] == PERCENT)
#define ADDCOM (text[0] == BANG)
/* Static variables */
TCHAR text[LINE_LEN+1];
TCHAR *options; /* must be sure to malloc! */
TCHAR *Arg_P[10];
FILE *hfile;
/* Forward declarations */
int find_entry( int, int, HANDLE, int *);
VOID print_syntax( HANDLE, int );
VOID print_help( int );
VOID print_options( int );
VOID seek_data( int, int );
LPWSTR skipwtspc( TCHAR FAR * );
LPWSTR fgetsW(LPWSTR buf, int len, FILE *fh);
DWORD GetHelpFileName(LPTSTR HelpFileName, DWORD BufferLength);
DWORD GetFileName(LPTSTR FileName, DWORD BufferLength, LPTSTR FilePartName);
/* help_help -
*/
VOID NEAR pascal
help_help( SHORT ali, SHORT amt)
{
DWORD err;
int option_level = 1;
int r;
int found = 0;
int out_len = 0;
int offset;
int arg_cnt;
int k;
SHORT pind = 0;
TCHAR file_path[MAX_PATH];
TCHAR str[10];
TCHAR *Ap;
TCHAR *tmp;
TCHAR *stmp;
TCHAR *t2;
HANDLE outfile;
if (!(options = malloc(OPTION_MAX_LEN + 1)))
ErrorExit(ERROR_NOT_ENOUGH_MEMORY);
*options = NULLC;
Arg_P[0] = NET_KEYWORD;
if (amt == USAGE_ONLY)
{
outfile = g_hStdErr;
}
else
{
outfile = g_hStdOut;
}
/* use offset to keep base of Arg_P relative to base of ArgList */
offset = ali;
/* increment ali in for loop so you can't get an ali of 0 */
for (arg_cnt = 0; ArgList[ali++]; arg_cnt < 8 ? arg_cnt++ : 0)
{
str[arg_cnt] = (TCHAR)ali;
}
str[arg_cnt] = NULLC;
str[arg_cnt+1] = NULLC; /* just in case the last argument is the first found */
if (err = GetHelpFileName(file_path, MAX_PATH))
{
ErrorExit(err);
}
/*
we need to open help files in binary mode
because unicode text might contain 0x1a but
it's not EOF.
*/
if ( (hfile = _wfopen(file_path, L"rb")) == 0 )
{
ErrorExit(APE_HelpFileDoesNotExist);
}
if (!(fgetsW(text, LINE_LEN+1, hfile)))
{
ErrorExit(APE_HelpFileEmpty);
}
/* comment loop - read and ignore comments */
while (!HEADER)
{
if (!fgetsW(text, LINE_LEN+1, hfile))
{
ErrorExit(APE_HelpFileError);
}
}
/* get the list of commands that net help provides help for that are
not specifically net commands
*/
/* ALIAS Loop */
while (ALIAS) {
/* the first token read from text is the Real Object (the non alias) */
tmp = skipwtspc(&text[2]);
Ap = _tcstok(tmp, DELS);
/* get each alias for the obove object and compare it to the arg_cnt
number of args in ArgList */
while ((tmp = _tcstok(NULL, DELS)) && arg_cnt) {
tmp = skipwtspc(tmp);
for (k = 0; k < arg_cnt; k++) {
/* if there a match store the Objects real name in Arg_P */
if (!_tcsicmp(tmp, ArgList[(int)(str[k]-1)])) {
if (!(Arg_P[((int)str[k])-offset] = _tcsdup(Ap)))
ErrorExit(APE_HelpFileError);
/* delete the pointer to this argument from the list
of pointers so the number of compares is reduced */
stmp = &str[k];
*stmp++ = NULLC;
_tcscat(str, stmp);
arg_cnt--;
break;
}
}
}
if (!fgetsW(text, LINE_LEN+1, hfile))
ErrorExit(APE_HelpFileError);
}
/* if there were any args that weren't aliased copy them into Arg_P */
for (k = 0; k < arg_cnt; k++)
Arg_P[((int)str[k])-offset] = ArgList[(int)(str[k]-1)];
/* check for blank lines between alias declaration and command declarations */
while (!HEADER) {
if (!fgetsW(text, LINE_LEN+1, hfile))
ErrorExit(APE_HelpFileError);
}
while (ADDCOM) {
if ((arg_cnt) && (!found)) {
tmp = skipwtspc(&text[2]);
t2 = _tcschr(tmp, NEWLINE);
*t2 = NULLC;
if (!_tcsicmp(tmp, Arg_P[1])) {
pind = 1;
found = -1;
}
}
if (!fgetsW(text, LINE_LEN+1, hfile))
ErrorExit(APE_HelpFileError);
}
/* check for blank lines between command declarations and data */
while (!FCNTRL) {
if (!fgetsW(text, LINE_LEN+1, hfile))
ErrorExit(APE_HelpFileError);
}
if (outfile == g_hStdOut) {
if (amt == OPTIONS_ONLY)
InfoPrint(APE_Options);
else
InfoPrint(APE_Syntax);
}
else {
if (amt == OPTIONS_ONLY)
InfoPrintInsHandle(APE_Options, 0, g_hStdErr);
else
InfoPrintInsHandle(APE_Syntax, 0, g_hStdErr);
}
ali = pind;
GenOutput(outfile, TEXT("\r\n"));
/* look for the specific entry (or path) and find its corresponding data */
/* KKBUGFIX */
/* U.S. bug. find_entry strcat's to options but options is
uninitialized. The U.S. version is lucky that the malloc
returns memory with mostly zeroes so this works. With recent
changes things are a little different and a malloc returns
memory with no zeroes so find_entry overwrites the buffer. */
options[0] = '\0';
while ((r = find_entry(option_level, ali, outfile, &out_len)) >= 0) {
if (r) {
options[0] = NULLC;
if (Arg_P[++ali]) {
option_level++;
if (!fgetsW(text, LINE_LEN+1, hfile))
ErrorExit(APE_HelpFileError);
}
else {
seek_data(option_level, 1);
break;
}
}
}
r = (r < 0) ? (option_level - 1) : r;
switch(amt) {
case ALL:
/* print the syntax data that was found for this level */
print_syntax(outfile, out_len);
print_help(r);
NetcmdExit(0);
break;
case USAGE_ONLY:
print_syntax(outfile, out_len);
GenOutput(outfile, TEXT("\r\n"));
NetcmdExit(1);
break;
case OPTIONS_ONLY:
//fflush( outfile );
print_options(r);
NetcmdExit(0);
break;
}
}
/* find_entry - each invocation of find_entry either finds a match at the
specified level or advances through the file to the next entry at
the specified level. If the level requested is greater than the
next level read ENTRY_NOT_FOUND is returned. */
int
find_entry(
int level,
int ali,
HANDLE out,
int *out_len
)
{
static TCHAR level_key[] = {TEXT(".0")};
TCHAR *tmp;
TCHAR *t2;
level_key[1] = (TCHAR) (TEXT('0') + (TCHAR)level);
if (level_key[1] > text[1])
return (ENTRY_NOT_FOUND | ali);
else {
tmp = skipwtspc(&text[2]);
t2 = _tcschr(tmp, NEWLINE);
if (t2 == NULL)
{
//
// A line in the help file is longer than LINE_LEN
// so there's no newline character. Bail out.
//
ErrorExit(APE_HelpFileError);
}
*t2 = NULLC;
if (!_tcsicmp(Arg_P[ali], tmp)) {
*t2++ = BLANK;
*t2 = NULLC;
GenOutput1(out, TEXT("%s"), tmp);
*out_len += _tcslen(tmp);
return level;
}
else {
*t2++ = BLANK;
*t2 = NULLC;
_tcscat(options, tmp);
_tcscat(options, TEXT("| "));
seek_data(level, 0);
do {
if (!fgetsW(text, LINE_LEN+1, hfile))
ErrorExit(APE_HelpFileError);
} while (!FCNTRL);
return NEXT_RECORD;
}
}
}
VOID
print_syntax(
HANDLE out,
int out_len
)
{
TCHAR *tmp,
*rtmp,
*otmp,
tchar;
int off,
pg_wdth = LINE_LEN - 14;
tmp = skipwtspc(&text[2]);
if (_tcslen(tmp) < 2)
{
//
// Used only for syntax of NET (e.g., if user types
// in "net foo")
//
if (_tcslen(options))
{
otmp = _tcsrchr(options, PIPE);
if (otmp == NULL)
{
ErrorExit(APE_HelpFileError);
}
*otmp = NULLC;
GenOutput(out, TEXT("[ "));
out_len += 2;
tmp = options;
otmp = tmp;
off = pg_wdth - out_len;
while (((int)_tcslen(tmp) + out_len) > pg_wdth)
{
if ((tmp + off) > &options[OPTION_MAX_LEN])
rtmp = (TCHAR*) (&options[OPTION_MAX_LEN]);
else
rtmp = (tmp + off);
/* save TCHAR about to be stomped by null */
tchar = *++rtmp;
*rtmp = NULLC;
/* use _tcsrchr to find last occurance of a space (kanji compatible) */
if ( ! ( tmp = _tcsrchr(tmp, PIPE) ) ) {
ErrorExit(APE_HelpFileError);
}
/* replace stomped TCHAR */
*rtmp = tchar;
rtmp = tmp;
/* replace 'found space' with null for fprintf */
*++rtmp = NULLC;
rtmp++;
GenOutput1(out, TEXT("%s\r\n"), otmp);
/* indent next line */
tmp = rtmp - out_len;
otmp = tmp;
while (rtmp != tmp)
{
*tmp++ = BLANK;
}
}
GenOutput1(out, TEXT("%s]\r\n"), otmp);
*tmp = NULLC;
}
}
else
{
GenOutput(out, TEXT("\r\n"));
}
do
{
if (*tmp)
GenOutput1(out, TEXT("%s"), tmp);
if(!(tmp = fgetsW(text, LINE_LEN+1, hfile)))
ErrorExit(APE_HelpFileError);
if (_tcslen(tmp) > 3)
tmp += 3;
}
while (!CNTRL);
}
VOID
print_help(
int level
)
{
static TCHAR help_key[] = {TEXT("#0")};
TCHAR *tmp;
help_key[1] = (TCHAR)(level) + TEXT('0');
while (!(text[0] == POUND))
if(!fgetsW(text, LINE_LEN+1, hfile))
ErrorExit(APE_HelpFileError);
while (text[1] > help_key[1]) {
help_key[1]--;
seek_data(--level, 0);
do {
if (!fgetsW(text, LINE_LEN+1, hfile))
ErrorExit(APE_HelpFileError);
} while(!(text[0] == POUND));
}
tmp = &text[2];
*tmp = NEWLINE;
do {
WriteToCon(TEXT("%s"), tmp);
if (!(tmp = fgetsW(text, LINE_LEN+1, hfile)))
ErrorExit(APE_HelpFileError);
if (_tcslen(tmp) > 3)
tmp = &text[3];
} while (!CNTRL);
}
VOID
print_options(int level)
{
static TCHAR help_key[] = {TEXT("$0")};
TCHAR *tmp;
help_key[1] = (TCHAR)(level) + TEXT('0');
while (!(text[0] == DOLLAR))
if(!fgetsW(text, LINE_LEN+1, hfile))
ErrorExit(APE_HelpFileError);
while (text[1] > help_key[1]) {
help_key[1]--;
seek_data(--level, 0);
do {
if (!fgetsW(text, LINE_LEN+1, hfile))
ErrorExit(APE_HelpFileError);
} while(!(text[0] == DOLLAR));
}
tmp = &text[2];
*tmp = NEWLINE;
do {
WriteToCon(TEXT("%s"), tmp);
if (!(tmp = fgetsW(text, LINE_LEN+1, hfile)))
ErrorExit(APE_HelpFileError);
if (_tcslen(tmp) > 3)
tmp = &text[3];
} while (!CNTRL);
}
VOID
seek_data(int level, int opt_trace)
{
static TCHAR data_key[] = {TEXT(":0")};
static TCHAR option_key[] = {TEXT(".0")};
TCHAR *tmp;
TCHAR *t2;
data_key[1] = (TCHAR)(level) + TEXT('0');
option_key[1] = (TCHAR)(level) + TEXT('1');
do {
if (!(fgetsW(text, LINE_LEN+1, hfile)))
ErrorExit(APE_HelpFileError);
if (opt_trace &&
(!(_tcsncmp(option_key, text, DIMENSION(option_key)-1)))) {
tmp = skipwtspc(&text[2]);
t2 = _tcschr(tmp, NEWLINE);
if (t2 == NULL)
{
//
// A line in the help file is longer than LINE_LEN
// so there's no newline character. Not good.
//
ErrorExit(APE_HelpFileError);
}
*t2++ = BLANK;
*t2 = NULLC;
_tcscat(options, tmp);
_tcscat(options, TEXT("| "));
}
} while (_tcsncmp(data_key, text, DIMENSION(data_key)-1));
}
TCHAR FAR *
skipwtspc(TCHAR FAR *s)
{
s += _tcsspn(s, WHITE_SPACE);
return s;
}
/* help_helpmsg() -- front end for helpmsg utility
*
* This function acts as a front end for the OS/2 HELPMSG.EXE
* utility for NET errors only. It takes as a parameter a string
* that contains a VALID message id; i.e., of the form NETxxxx
* or xxxx. The string is assumed to have been screened by the
* IsMsgid() function in grammar.c before coming here.
JonN 3/31/00 98273: NETCMD: Need to fix the mapping of error 3521
Before the checkin for 22391, NET1.EXE read errors
NERR_BASE (2100) <= err < APPERR2_BASE (4300) from NETMSG.DLL,
and all others from FORMAT_MESSAGE_FROM_SYSTEM.
After the checkin for 22391, NET1.EXE read errors
NERR_BASE (2100) < err < MAX_NERR (2999) from NETMSG.DLL,
and all others from FORMAT_MESSAGE_FROM_SYSTEM.
On closer examination, NETMSG.DLL currently appears to contain messages from
0x836 (2102) to 0x169F (5791).
This is consistent with lmcons.h:
#define MIN_LANMAN_MESSAGE_ID NERR_BASE
#define MAX_LANMAN_MESSAGE_ID 5799
It looks like we have a basic contradiction here:
3001:
FORMAT_MESSAGE_FROM_SYSTEM: The specified printer driver is currently in use.
NETMSG.DLL: *** errors were logged in the last *** minutes.
3521:
FORMAT_MESSAGE_FROM_SYSTEM: not found
NETMSG.DLL: The *** service is not started.
So what do we do with the error messages in the range
MAX_NERR (2999) < err <= MAX_LANMAN_MESSAGE_ID
? The best error message could be in either one.
Perhaps we should try FORMAT_MESSAGE_FROM_SYSTEM, and if that fails
fall back to NETMSG.DLL.
*/
VOID NEAR pascal
help_helpmsg(TCHAR *msgid)
{
USHORT err;
TCHAR * temp = msgid;
/* first, filter out non-NET error msgs */
/* if msgid begins with a string */
if (!IsNumber(msgid)) { /* compare it against NET */
if (_tcsnicmp(msgid, NET_KEYWORD, NET_KEYWORD_SIZE)) {
ErrorExitInsTxt(APE_BAD_MSGID, msgid);
}
else
temp += NET_KEYWORD_SIZE;
}
if (n_atou(temp, &err))
ErrorExitInsTxt(APE_BAD_MSGID, msgid);
/* First try FORMAT_MESSAGE_FROM_SYSTEM unless the error is in the range
NERR_BASE <= err <= MAX_NERR */
if (err < NERR_BASE || err > MAX_NERR)
{
LPWSTR lpMessage = NULL ;
if (!FormatMessageW(
FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
err,
0,
(LPWSTR)&lpMessage,
1024,
NULL))
{
// defer error message and fall back to NETMSG.DLL
}
else
{
WriteToCon(TEXT("\r\n%s\r\n"), lpMessage);
(void) LocalFree((HLOCAL)lpMessage) ;
return ;
}
}
/* skip NETMSG.DLL if error message is way out of range */
if (err < NERR_BASE || err > MAX_MSGID) {
ErrorExitInsTxt(APE_BAD_MSGID, msgid);
}
/* read from NETMSG.DLL */
PrintNL();
//
// If PrintMessage can't find the message id, don't try the expl
//
if (PrintMessage(g_hStdOut, MESSAGE_FILENAME, err, StarStrings, 9) == NO_ERROR)
{
PrintNL();
PrintMessageIfFound(g_hStdOut, HELP_MSG_FILENAME, err, StarStrings, 9);
}
}
/*
* returns line from file (no CRLFs); returns NULL if EOF
*/
LPWSTR
fgetsW(
LPWSTR buf,
int len,
FILE *fh
)
{
int c = 0;
TCHAR *pch;
int cchline;
DWORD cchRead;
pch = buf;
cchline = 0;
if (ftell(fh) == 0) {
fread(&c, sizeof(TCHAR), 1, fh);
if (c != 0xfeff)
GenOutput(g_hStdErr, TEXT("help file not Unicode\r\n"));
}
while (TRUE)
{
/*
* for now read in the buffer in ANSI form until Unicode is more
* widely accepted - dee
*
*/
cchRead = fread(&c, sizeof(TCHAR), 1, fh);
//
// if there are no more characters, end the line
//
if (cchRead < 1)
{
c = EOF;
break;
}
//
// if we see a \r, we ignore it
//
if (c == TEXT('\r'))
continue;
//
// if we see a \n, we end the line
//
if (c == TEXT('\n')) {
*pch++ = (TCHAR) c;
break;
}
//
// if the char is not a tab, store it
//
if (c != TEXT('\t'))
{
*pch = (TCHAR) c;
pch++;
cchline++;
}
//
// if the line is too long, end it now
//
if (cchline >= len - 1) {
break;
}
}
//
// end the line
//
*pch = (TCHAR) 0;
//
// return NULL at EOF with nothing read
//
return ((c == EOF) && (pch == buf)) ? NULL : buf;
}
//
// Build the fully qualified path name of a file that lives with the exe
// Used by LUI_GetHelpFileName
//
DWORD
GetFileName(
LPTSTR FileName,
DWORD BufferLength,
LPTSTR FilePartName
)
{
TCHAR ExeFileName[MAX_PATH];
PTCHAR pch;
//
// Get the fully qualified path name of where the exe lives
//
if (!GetModuleFileName(NULL, ExeFileName, DIMENSION(ExeFileName)))
{
return 1;
}
//
// get rid of the file name part
//
pch = _tcsrchr(ExeFileName, '\\');
if (!pch)
{
return 1;
}
*(pch+1) = NULLC;
//
// Copy the path name into the string and add the help filename part
// but first make sure it's not too big for the user's buffer
//
if (_tcslen(ExeFileName) + _tcslen(FilePartName) + 1 > BufferLength)
{
return 1;
}
_tcscpy(FileName, ExeFileName);
_tcscat(FileName, FilePartName);
return 0;
}
//
// Get the help file name
//
DWORD
GetHelpFileName(
LPTSTR HelpFileName,
DWORD BufferLength
)
{
TCHAR LocalizedFileName[MAX_PATH];
DWORD LocalizedFileNameID;
switch(GetConsoleOutputCP()) {
case 932:
case 936:
case 949:
case 950:
LocalizedFileNameID = APE2_FE_NETCMD_HELP_FILE;
break;
default:
LocalizedFileNameID = APE2_US_NETCMD_HELP_FILE;
break;
}
if (LUI_GetMsg(LocalizedFileName, DIMENSION(LocalizedFileName),
LocalizedFileNameID))
{
return GetFileName(HelpFileName, BufferLength, TEXT("NET.HLP"));
}
else
{
return GetFileName(HelpFileName, BufferLength, LocalizedFileName);
}
}