/********************************************************************/ /** 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 #include #include #include #include #include #include #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 * IsNumber() 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; if (!IsNumber(msgid)) { ErrorExitInsTxt(APE_BAD_MSGID, msgid); } 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) { // // Help file isn't Unicode // ErrorExit(APE_HelpFileError); } } 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 + 1] = {0}; PTCHAR pch; // // Get the fully qualified path name of where the exe lives // if (!GetModuleFileName(NULL, ExeFileName, DIMENSION(ExeFileName) - 1)) { 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); } }