649 lines
13 KiB
C++
649 lines
13 KiB
C++
|
/*++
|
||
|
|
||
|
Copyright (c) 1996 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
applyinf.cxx
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
Merge HTML documents & localizable string .inf file
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Philippe Choquier ( Phillich ) 15-may-1996
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include <windows.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include <iis64.h>
|
||
|
|
||
|
typedef struct _SUBST_NODE {
|
||
|
LPSTR pszName;
|
||
|
LPSTR pszValue;
|
||
|
} SUBST_NODE;
|
||
|
|
||
|
#define MAX_NODES 8192
|
||
|
#define MAX_SIZE_NAME 256
|
||
|
#define MAX_SIZE_VALUE 8192
|
||
|
|
||
|
#define INF_SEEK_FIRST_CHAR_NAME 0
|
||
|
#define INF_SEEK_NAME 1
|
||
|
#define INF_SEEK_END_NAME 2
|
||
|
#define INF_SEEK_STR 3
|
||
|
#define INF_SEEK_END_STR 4
|
||
|
#define INF_SEEK_EOL 5
|
||
|
|
||
|
#define HT_SEEK_NAME 0
|
||
|
#define HT_SEEK_END_NAME 1
|
||
|
|
||
|
SUBST_NODE aNodes[MAX_NODES];
|
||
|
int cNodes = 0;
|
||
|
|
||
|
char achName[MAX_SIZE_NAME];
|
||
|
char achValue[MAX_SIZE_VALUE];
|
||
|
|
||
|
|
||
|
extern "C" int __cdecl
|
||
|
QsortStrCmp(
|
||
|
const void *pA,
|
||
|
const void *pB )
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Compare two SUBST_NODE structures base on their pszName field
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pA - ptr to 1st struct
|
||
|
pB - ptr to 2nd struct
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
-1 if *pA < *pB, 0 if *pA == *pB, 1 if *pA > *pB
|
||
|
based on strcmp of their pszName field
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
return strcmp( ((SUBST_NODE*)pA)->pszName, ((SUBST_NODE*)pB)->pszName );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
ParseINF(
|
||
|
LPSTR pszInf
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Parse the .inf file for localizable string substitutions
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pszInf - name of .inf file
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
TRUE if success, FALSE if error
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
BOOL fSt = TRUE;
|
||
|
int ch;
|
||
|
FILE *inf;
|
||
|
|
||
|
if ( ( inf = fopen(pszInf, "r") ) == NULL )
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
int iName = 0;
|
||
|
int iValue = 0;
|
||
|
BOOL fEsc;
|
||
|
|
||
|
int iState = INF_SEEK_FIRST_CHAR_NAME;
|
||
|
|
||
|
for ( ; (ch = fgetc( inf )) != EOF ; )
|
||
|
{
|
||
|
if ( ch == '\\' )
|
||
|
{
|
||
|
if ( (ch = fgetc( inf )) == EOF )
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
if ( ch == '\n' )
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
if ( ch == 'n' )
|
||
|
{
|
||
|
ch = '\n';
|
||
|
}
|
||
|
fEsc = TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fEsc = FALSE;
|
||
|
}
|
||
|
|
||
|
switch ( iState )
|
||
|
{
|
||
|
case INF_SEEK_FIRST_CHAR_NAME:
|
||
|
if ( !fEsc && ch == '#' )
|
||
|
{
|
||
|
iState = INF_SEEK_EOL;
|
||
|
break;
|
||
|
}
|
||
|
iState = INF_SEEK_NAME;
|
||
|
// fall-through
|
||
|
|
||
|
case INF_SEEK_NAME:
|
||
|
if ( !fEsc && ch == '^' )
|
||
|
{
|
||
|
iState = INF_SEEK_END_NAME;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case INF_SEEK_END_NAME:
|
||
|
if ( !fEsc && ch == '^' )
|
||
|
{
|
||
|
iState = INF_SEEK_STR;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
achName[ iName++ ] = (char)ch;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case INF_SEEK_STR:
|
||
|
if ( !fEsc && ch == '"' )
|
||
|
{
|
||
|
iState = INF_SEEK_END_STR;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case INF_SEEK_END_STR:
|
||
|
|
||
|
// handle "" as a single quote
|
||
|
|
||
|
if ( !fEsc && ch == '"' )
|
||
|
{
|
||
|
if ( (ch = fgetc( inf )) == EOF )
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
if ( ch == '"' )
|
||
|
{
|
||
|
fEsc = TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ungetc( ch, inf );
|
||
|
ch = '"';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// skip new lines char in stream
|
||
|
|
||
|
if ( !fEsc && ch == '\n' )
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( !fEsc && ch == '"' )
|
||
|
{
|
||
|
achName[ iName ] = '\0';
|
||
|
achValue[ iValue ] = '\0';
|
||
|
aNodes[ cNodes ].pszName = _strdup( achName );
|
||
|
aNodes[ cNodes ].pszValue = _strdup( achValue );
|
||
|
++cNodes;
|
||
|
iState = INF_SEEK_FIRST_CHAR_NAME;
|
||
|
iName = 0;
|
||
|
iValue = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
achValue[ iValue++ ] = (char)ch;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case INF_SEEK_EOL:
|
||
|
if ( !fEsc && ch == '\n' )
|
||
|
{
|
||
|
iState = INF_SEEK_FIRST_CHAR_NAME;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
qsort( aNodes, cNodes, sizeof(SUBST_NODE), QsortStrCmp );
|
||
|
|
||
|
fclose( inf );
|
||
|
|
||
|
return fSt;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
ParseHTML(
|
||
|
LPSTR pszIn,
|
||
|
LPSTR pszOut
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Parse a HTML document and generate an output document
|
||
|
based on a previously parsed .inf susbtitution file
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pszIn - name of input HTML document
|
||
|
pszOut - name of created localized HTML document
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
TRUE if success, FALSE if error
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
BOOL fSt = TRUE;
|
||
|
int ch;
|
||
|
FILE *in;
|
||
|
FILE *out;
|
||
|
|
||
|
if ( ( in = fopen(pszIn, "r") ) == NULL )
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
if ( ( out = fopen(pszOut, "w") ) == NULL )
|
||
|
{
|
||
|
fclose( in );
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
int iName = 0;
|
||
|
BOOL fEsc;
|
||
|
|
||
|
int iState = HT_SEEK_NAME;
|
||
|
|
||
|
for ( ; (ch = fgetc( in )) != EOF ; )
|
||
|
{
|
||
|
if ( ch == '\\' )
|
||
|
{
|
||
|
if ( (ch = fgetc( in )) == EOF )
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
if ( ch == '\n' )
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
fEsc = TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fEsc = FALSE;
|
||
|
}
|
||
|
|
||
|
switch ( iState )
|
||
|
{
|
||
|
case HT_SEEK_NAME:
|
||
|
if ( !fEsc && ch == '^' )
|
||
|
{
|
||
|
iState = HT_SEEK_END_NAME;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fputc( ch, out );
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case HT_SEEK_END_NAME:
|
||
|
if ( !fEsc && ch == '^' )
|
||
|
{
|
||
|
SUBST_NODE snSeek;
|
||
|
SUBST_NODE *pN;
|
||
|
snSeek.pszName = achName;
|
||
|
achName[ iName ] = '\0';
|
||
|
|
||
|
if ( (pN = (SUBST_NODE*)bsearch( &snSeek,
|
||
|
aNodes,
|
||
|
cNodes,
|
||
|
sizeof(SUBST_NODE),
|
||
|
QsortStrCmp ))
|
||
|
!= NULL )
|
||
|
{
|
||
|
fputs( pN->pszValue, out );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fprintf( stdout, "Can't find reference to %s in %s\n",
|
||
|
achName,
|
||
|
pszIn );
|
||
|
fflush( stdout );
|
||
|
}
|
||
|
iState = HT_SEEK_NAME;
|
||
|
iName = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
achName[ iName++ ] = (char)ch;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fclose( in );
|
||
|
fclose( out );
|
||
|
|
||
|
return fSt;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
BreakPath(
|
||
|
LPSTR pOut,
|
||
|
LPSTR pExt,
|
||
|
BOOL *pfIsExt
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Move a file extension from a file path to an extension buffer
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pOut - file path updated to remove file extension
|
||
|
pExt - buffer for file extension
|
||
|
pfIsExt - set to TRUE if file extension present
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
TRUE if success, FALSE if error
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
// if ends with '\\' is directory
|
||
|
// else extract extension
|
||
|
|
||
|
LPSTR pL = pOut + strlen(pOut);
|
||
|
LPSTR pE = NULL;
|
||
|
|
||
|
if ( pL[-1] == '\\' )
|
||
|
{
|
||
|
*pfIsExt = FALSE;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
while ( pL > pOut && pL[-1] != '\\' )
|
||
|
{
|
||
|
if ( pL[-1] == '.' && pE == NULL )
|
||
|
{
|
||
|
pE = pL;
|
||
|
}
|
||
|
--pL;
|
||
|
}
|
||
|
|
||
|
if ( pL == pOut )
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
*pL = '\0';
|
||
|
strcpy( pExt, pE );
|
||
|
*pfIsExt = TRUE;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
void
|
||
|
Usage(
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Display usage for this utility
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
Nothing
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
fprintf( stdout,
|
||
|
"\n"
|
||
|
"Usage: applyinf [source_file] [target_directory] [inf_file]\n"
|
||
|
" source_file : can contains wild card characters\n"
|
||
|
" target_directory : can contains a new extension to be\n"
|
||
|
" used, e.g. *.out\n"
|
||
|
" inf_file : name of the .inf files containing replacement strings\n"
|
||
|
"\n" );
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
Combine(
|
||
|
LPSTR pOut,
|
||
|
LPSTR pExt,
|
||
|
BOOL fIsExt,
|
||
|
LPSTR pFileName
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Combine file name & extension to a new file name
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pOut - output filename
|
||
|
pExt - contains file extension if fIsExt is TRUE
|
||
|
fIsExt - TRUE if pExt contains file extension
|
||
|
pFileName - filename to be combined with extension to generare pOut
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
TRUE if success, FALSE if error
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
LPSTR pL = pFileName + strlen(pFileName);
|
||
|
|
||
|
|
||
|
if ( fIsExt )
|
||
|
{
|
||
|
while ( pL > pFileName && pL[-1] != '.' )
|
||
|
--pL;
|
||
|
|
||
|
if ( pL == pFileName )
|
||
|
{
|
||
|
// no ext in filename
|
||
|
memcpy( pOut, pFileName, strlen(pFileName) );
|
||
|
pOut += strlen(pFileName );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
memcpy( pOut, pFileName, DIFF(pL - pFileName) - 1 );
|
||
|
pOut += pL - pFileName - 1;
|
||
|
}
|
||
|
*pOut ++ = '.';
|
||
|
strcpy( pOut, pExt );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
strcpy( pOut, pFileName );
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
int __cdecl
|
||
|
main(
|
||
|
int argc,
|
||
|
char *argv[]
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Entry point of this utility, parse command line
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
argc - nbr of command line parameters
|
||
|
argv - ptr to command line parameters
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
0 if success, else error code
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
char achIn[MAX_PATH]="";
|
||
|
char achOut[MAX_PATH]="";
|
||
|
char achInf[MAX_PATH]="";
|
||
|
char achExt[MAX_PATH];
|
||
|
BOOL fIsExt;
|
||
|
WIN32_FIND_DATA fdIn;
|
||
|
HANDLE hF;
|
||
|
int arg;
|
||
|
int iN = 0;
|
||
|
LPSTR pLastS;
|
||
|
LPSTR pOut;
|
||
|
|
||
|
for ( arg = 1 ; arg < argc ; ++arg )
|
||
|
{
|
||
|
if ( argv[arg][0] == '-' )
|
||
|
{
|
||
|
switch( argv[arg][1] )
|
||
|
{
|
||
|
case 'z':
|
||
|
default:
|
||
|
;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
switch ( iN )
|
||
|
{
|
||
|
case 0:
|
||
|
strcpy( achIn, argv[arg] );
|
||
|
break;
|
||
|
|
||
|
case 1:
|
||
|
strcpy( achOut, argv[arg] );
|
||
|
break;
|
||
|
|
||
|
case 2:
|
||
|
strcpy( achInf, argv[arg] );
|
||
|
break;
|
||
|
}
|
||
|
++iN;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( achIn[0] == '\0' )
|
||
|
{
|
||
|
fprintf( stdout, "No source directory specified\n" );
|
||
|
fflush( stdout );
|
||
|
Usage();
|
||
|
return 3;
|
||
|
}
|
||
|
|
||
|
if ( achOut[0] == '\0' )
|
||
|
{
|
||
|
fprintf( stdout, "No target directory specified\n" );
|
||
|
fflush( stdout );
|
||
|
Usage();
|
||
|
return 3;
|
||
|
}
|
||
|
|
||
|
if ( achInf[0] == '\0' )
|
||
|
{
|
||
|
fprintf( stdout, "No INF file specified\n" );
|
||
|
fflush( stdout );
|
||
|
Usage();
|
||
|
return 3;
|
||
|
}
|
||
|
|
||
|
for ( pLastS = achIn + strlen(achIn) ; pLastS > achIn ; --pLastS )
|
||
|
{
|
||
|
if ( pLastS[-1] == '\\' )
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( pLastS == achIn )
|
||
|
{
|
||
|
fprintf( stdout, "Invalid source directory : %s\n", achIn );
|
||
|
fflush( stdout );
|
||
|
return 5;
|
||
|
}
|
||
|
|
||
|
if ( !BreakPath( achOut, achExt, &fIsExt ) )
|
||
|
{
|
||
|
fprintf( stdout, "Invalid target directory : %s\n", achOut );
|
||
|
fflush( stdout );
|
||
|
return 6;
|
||
|
}
|
||
|
pOut = achOut + strlen( achOut );
|
||
|
|
||
|
if ( !ParseINF( achInf ) )
|
||
|
{
|
||
|
fprintf( stdout, "Can't parse INF file %s\n", achInf );
|
||
|
fflush( stdout );
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
// applyinf srcdir trgdirandext inffile
|
||
|
// e.g. applyinf c:\nt\*.htr c:\drop\*.htm html.inf
|
||
|
|
||
|
if ( (hF = FindFirstFile( achIn, &fdIn )) != INVALID_HANDLE_VALUE )
|
||
|
{
|
||
|
do {
|
||
|
strcpy( pLastS, fdIn.cFileName );
|
||
|
Combine( pOut, achExt, fIsExt, fdIn.cFileName );
|
||
|
|
||
|
if ( !ParseHTML( achIn, achOut) )
|
||
|
{
|
||
|
fprintf( stdout, "Can't generate %s from %s\n", achOut, achIn );
|
||
|
fflush( stdout );
|
||
|
return 2;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fprintf( stdout, "Parsed %s to %s\n", achIn, achOut );
|
||
|
fflush( stdout );
|
||
|
}
|
||
|
} while ( FindNextFile( hF, &fdIn ) );
|
||
|
|
||
|
FindClose( hF );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fprintf( stdout, "No file found in %s", achIn );
|
||
|
fflush( stdout );
|
||
|
return 4;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|