/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
hsplit.c
Abstract:
This is the main module for a header the file splitter. It will look
for various blocks marked with begin/end tags and treat them specially
based on the tag. Also, it looks for tags that are line based. The
list is show below. Public indicates is appears in the public header file
private in the private header file. Chicago only has one header file,
but private lines are marked as internal.
Author:
Sanford Staab (sanfords) 22-Apr-1992
Revision History:
sankar 05-Dec-1995 Make it work for Nashville.
stevefir 14-Dec-1995 Make it work for SUR & WINVER 4.1
--*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
typedef int boolean;
#define TRUE 1
#define FALSE 0
char *szIFDEF[7][2] = {
{
"#if(WINVER >= 0x0400)\n",
"#endif /* WINVER >= 0x0400 */\n"
},
{
"#if(WINVER < 0x0400)\n",
"#endif /* WINVER < 0x0400 */\n"
},
{
"#if(WINVER >= 0x040a)\n",
"#endif /* WINVER >= 0x040a */\n"
},
{
"#if(WINVER < 0x040a)\n",
"#endif /* WINVER < 0x040a */\n"
},
{
"#if(_WIN32_WINNT >= 0x0400)\n",
"#endif /* _WIN32_WINNT >= 0x0400 */\n"
},
{
"#if(_WIN32_WINNT < 0x0400)\n",
"#endif /* _WIN32_WINNT < 0x0400 */\n"
},
{
"#if(_WIN32_WINDOWS >= 0x040a)\n",
"#endif /* _WIN32_WINDOWS >= 0x040a */\n"
},
//
// we don't need _WIN32_WINDOWS < 0x040a
// since Chicago has no private *p.h headers.
//
};
//
// Global Data
//
boolean NT = TRUE; // says if nt flag is on command line
boolean Chicago = FALSE; // says if chicago flag is on command line
boolean Cairo = FALSE; // says if cairo flag is on command line
boolean SURPlus = FALSE; // says if SURPlus flag is on command line
boolean SplitOnly = FALSE; // says only do both and internal
#define MODE_PUBLICNT 0x01
#define MODE_PUBLICCHICAGO 0x02
#define MODE_PUBLICCAIRO 0x04
#define MODE_PUBLICSURPLUS 0x08
#define MODE_PUBLICALL 0x0F
#define MODE_PRIVATENT 0x10
#define MODE_PRIVATECHICAGO 0x20 // goes to public header and marked ;internal.
#define MODE_PRIVATECAIRO 0x40
#define MODE_PRIVATESURPLUS 0x80
#define MODE_PRIVATEALL 0xF0
#define MODE_BOTH 0xFF
#define MODE_NOWHERE 0
#define MODE_IFDEF_WV400 0x100
#define MODE_IFDEF_WV40a 0x200
#define MODE_IFDEF_W32WIN 0x400
#define MODE_IFDEF_W32WNT 0x800
#define MODE_IFDEFALL 0xf00
#define C_BLOCKDEFS 21
#define BLOCKDEF_BOTH 0
#define BLOCKDEF_INTERNAL 1
typedef struct tagBLOCKDEF {
char *pszLineMark;
char * pszBlockStart;
char * pszBlockEnd;
int dwFlags;
} BLOCKDEF, *PBLOCKDEF;
BLOCKDEF aBlockDef[C_BLOCKDEFS] = {
{
"both",
"begin_both",
"end_both",
MODE_BOTH
},
{
"internal",
"begin_internal",
"end_internal",
MODE_PRIVATEALL
},
{
"internal_NT",
"begin_internal_NT",
"end_internal_NT",
MODE_PRIVATENT | MODE_PRIVATECAIRO | MODE_PRIVATESURPLUS
},
{
"internal_win40",
"begin_internal_win40",
"end_internal_win40",
MODE_PRIVATECHICAGO | MODE_PRIVATECAIRO | MODE_PRIVATESURPLUS
},
{
"internal_win40a",
"begin_internal_win40a",
"end_internal_win40a",
MODE_PRIVATECHICAGO | MODE_PRIVATESURPLUS
},
{
"internal_chicago",
"begin_internal_chicago",
"end_internal_chicago",
MODE_PRIVATECHICAGO
},
{
"internal_NT_35",
"begin_internal_NT_35",
"end_internal_NT_35",
MODE_PRIVATENT
},
{
"internal_cairo",
"begin_internal_cairo",
"end_internal_cairo",
MODE_PRIVATECAIRO | MODE_PRIVATESURPLUS
},
{
"internal_surplus",
"begin_internal_surplus",
"end_internal_surplus",
MODE_PRIVATESURPLUS
},
{
"public_winver_400",
"begin_winver_400",
"end_winver_400",
MODE_PRIVATENT | MODE_PUBLICALL | MODE_IFDEF_WV400
},
{
"public_win40",
"begin_public_win40",
"end_public_win40",
MODE_PUBLICCAIRO | MODE_PUBLICCHICAGO
},
{
"public_win40a",
"begin_public_win40a",
"end_public_win40a",
MODE_PUBLICSURPLUS | MODE_PUBLICCHICAGO
},
{
"public_cairo",
"begin_public_cairo",
"end_public_cairo",
MODE_PUBLICCAIRO
},
{
"public_NT",
"begin_public_NT",
"end_public_NT",
MODE_PUBLICNT | MODE_PUBLICCAIRO
},
{
"public_NT_35",
"begin_public_NT_35",
"end_public_NT_35",
MODE_PUBLICNT
},
{
"public_chicago",
"begin_public_chicago",
"end_public_chicago",
MODE_PUBLICCHICAGO
},
{
"public_sur",
"begin_sur",
"end_sur",
MODE_PUBLICCAIRO | MODE_IFDEF_W32WNT
},
{
"public_surplus",
"begin_surplus",
"end_surplus",
MODE_PUBLICSURPLUS | MODE_IFDEF_W32WNT
},
{
"public_nashville",
"begin_nashville",
"end_nashville",
MODE_PUBLICCHICAGO | MODE_IFDEF_W32WIN
},
{
"public_winver_40a",
"begin_winver_40a",
"end_winver_40a",
MODE_PUBLICSURPLUS | MODE_PRIVATECAIRO | MODE_PUBLICCHICAGO | MODE_IFDEF_WV40a
},
{
"$",
"begin_$",
"end_$",
MODE_NOWHERE
}
};
char *CommentDelimiter = ";";
char *OutputFileName1;
char *OutputFileName2;
char *SourceFileName;
char **SourceFileList;
int SourceFileCount;
FILE *SourceFile, *OutputFile1, *OutputFile2;
#define STRING_BUFFER_SIZE 1024
char StringBuffer[STRING_BUFFER_SIZE];
#define BUILD_VER_COMMENT "/*++ BUILD Version: "
#define BUILD_VER_COMMENT_LENGTH (sizeof (BUILD_VER_COMMENT)-1)
int OutputVersion = 0;
#define DONE 1
#define NOT_DONE 0
//
// Function declarations
//
int
ProcessParameters(
int argc,
char *argv[]
);
void
ProcessSourceFile( void );
boolean
ExactMatch (
char *,
char *
);
char *laststrstr (
const char *,
const char *
);
boolean
CheckForSingleLine (
char *
);
int
ProcessLine (
char *Input,
char *LineTag,
int mode
);
void
AddString (
int mode,
char *string
);
void
AddIfDef (
int mode,
int fBegin
);
int
ProcessBlock (
char **s,
char *BlockTagStart,
char *BlockTagEnd,
int mode
);
void
DoOutput (
int mode,
char *string
);
int
_CRTAPI1 main( argc, argv )
int argc;
char *argv[];
{
if (!ProcessParameters( argc, argv )) {
fprintf( stderr, "usage: HSPLIT\n" );
fprintf( stderr, " [-?]\n" );
fprintf( stderr, " display this message\n" );
fprintf( stderr, "\n" );
fprintf( stderr, " <-o fname1 fname2>\n" );
fprintf( stderr, " supplies output file names\n" );
fprintf( stderr, "\n" );
fprintf( stderr, " [-lt2 string]\n" );
fprintf( stderr, " one line tag for output to second file only\n" );
fprintf( stderr, " default=\"%s\"\n",
aBlockDef[BLOCKDEF_INTERNAL].pszLineMark );
fprintf( stderr, "\n" );
fprintf( stderr, " [-bt2 string1 string2]\n" );
fprintf( stderr, " block tags for output to second file only\n" );
fprintf( stderr, " default=\"%s\",\"%s\"\n",
aBlockDef[BLOCKDEF_INTERNAL].pszBlockStart,
aBlockDef[BLOCKDEF_INTERNAL].pszBlockEnd );
fprintf( stderr, "\n" );
fprintf( stderr, " [-ltb string]\n" );
fprintf( stderr, " one line tag for output to both files\n" );
fprintf( stderr, " default=\"%s\"\n",
aBlockDef[BLOCKDEF_BOTH].pszLineMark );
fprintf( stderr, "\n" );
fprintf( stderr, " [-btb string1 string2]\n" );
fprintf( stderr, " block tags for output to both files\n" );
fprintf( stderr, " default=\"%s\",\"%s\"\n",
aBlockDef[BLOCKDEF_BOTH].pszBlockStart,
aBlockDef[BLOCKDEF_BOTH].pszBlockEnd );
fprintf( stderr, "\n" );
fprintf( stderr, " [-c comment delimiter]\n" );
fprintf( stderr, " default=\"%s\"\n", CommentDelimiter);
fprintf( stderr, "\n" );
fprintf( stderr, " [-n]\n" );
fprintf( stderr, " generate NT header (default)\n");
fprintf( stderr, "\n" );
fprintf( stderr, " [-4]\n" );
fprintf( stderr, " generate Chicago/Nashville header\n");
fprintf( stderr, "\n" );
fprintf( stderr, " [-e]\n" );
fprintf( stderr, " generate NT (SUR) header\n");
fprintf( stderr, "\n" );
fprintf( stderr, " [-p]\n" );
fprintf( stderr, " generate NT (SURPlus) header\n");
fprintf( stderr, "\n" );
fprintf( stderr, " [-s]\n" );
fprintf( stderr, " process only the ;both and ;internal\n");
fprintf( stderr, " tags, or those tags specified by the\n");
fprintf( stderr, " -lt* and -bt* options. All other tags\n");
fprintf( stderr, " are left intact.\n");
fprintf( stderr, "\n" );
fprintf( stderr, " filename1 filename2 ...\n" );
fprintf( stderr, " files to concat and split\n" );
fprintf( stderr, "\n" );
fprintf( stderr, " Untagged lines output to the first file only.\n" );
fprintf( stderr, " All tags must follow a comment delimiter.\n" );
fprintf( stderr, " Comments are not propagated to either output file.\n" );
fprintf( stderr, " Tag nesting is not supported.\n" );
return TRUE;
}
if ( (OutputFile1 = fopen(OutputFileName1,"w")) == 0) {
fprintf(stderr,"HSPLIT: Unable to open output file %s\n",
OutputFileName1);
return TRUE;
}
//
// Chicago doesn't ever have a second output file
//
if (!Chicago) {
if ( (OutputFile2 = fopen(OutputFileName2,"w")) == 0) {
fprintf(stderr,"HSPLIT: Unable to open output file %s\n",
OutputFileName2);
return TRUE;
}
}
while ( SourceFileCount-- ) {
SourceFileName = *SourceFileList++;
if ( (SourceFile = fopen(SourceFileName,"r")) == 0) {
fprintf(stderr,
"HSPLIT: Unable to open source file %s for read access\n",
SourceFileName);
return TRUE;
}
ProcessSourceFile();
fclose(SourceFile);
}
return( FALSE );
}
int
ProcessParameters(
int argc,
char *argv[]
)
{
char c, *p;
while (--argc) {
p = *++argv;
//
// if we have a delimiter for a parameter, case through the valid
// parameter. Otherwise, the rest of the parameters are the list of
// input files.
//
if (*p == '/' || *p == '-') {
//
// Switch on all the valid delimiters. If we don't get a valid
// one, return with an error.
//
c = *++p;
switch (toupper( c )) {
case 'C':
argc--, argv++;
CommentDelimiter = *argv;
break;
case 'O':
argc--, argv++;
OutputFileName1 = *argv;
argc--, argv++;
OutputFileName2 = *argv;
break;
case 'L':
c = *++p;
if ( (toupper ( c )) != 'T')
return FALSE;
c = *++p;
switch (toupper ( c )) {
case '2':
argc--, argv++;
aBlockDef[BLOCKDEF_INTERNAL].pszLineMark = *argv;
break;
case 'B':
argc--, argv++;
aBlockDef[BLOCKDEF_BOTH].pszLineMark = *argv;
break;
default:
return(FALSE);
}
break;
case 'B':
c = *++p;
if ( (toupper ( c )) != 'T')
return FALSE;
c = *++p;
switch (toupper ( c )) {
case '2':
argc--, argv++;
aBlockDef[BLOCKDEF_INTERNAL].pszBlockStart = *argv;
argc--, argv++;
aBlockDef[BLOCKDEF_INTERNAL].pszBlockEnd = *argv;
break;
case 'B':
argc--, argv++;
aBlockDef[BLOCKDEF_BOTH].pszBlockStart = *argv;
argc--, argv++;
aBlockDef[BLOCKDEF_BOTH].pszBlockEnd = *argv;
break;
default:
return(FALSE);
}
break;
case '4':
Chicago = TRUE;
NT = FALSE;
Cairo = FALSE;
SURPlus = FALSE;
SplitOnly = FALSE;
break;
case 'N':
NT = TRUE;
Chicago = FALSE;
SURPlus = FALSE;
Cairo = FALSE;
SplitOnly = FALSE;
break;
case 'S':
Cairo = FALSE;
NT = FALSE;
SURPlus = FALSE;
Chicago = FALSE;
SplitOnly = TRUE;
break;
case 'E':
Cairo = TRUE;
NT = FALSE;
SURPlus = FALSE;
SplitOnly = FALSE;
Chicago = FALSE;
break;
case 'P':
Cairo = FALSE;
NT = FALSE;
SURPlus = TRUE;
SplitOnly = FALSE;
Chicago = FALSE;
break;
default:
return FALSE;
}
} else {
//
// Make the assumption that we have a valid command line if and
// only if we have a list of filenames.
//
SourceFileList = argv;
SourceFileCount = argc;
return TRUE;
}
}
return FALSE;
}
void
ProcessSourceFile(
void
)
{
char *s;
int i;
s = fgets(StringBuffer,STRING_BUFFER_SIZE,SourceFile);
if (!strncmp( s, BUILD_VER_COMMENT, BUILD_VER_COMMENT_LENGTH )) {
OutputVersion += atoi( s + BUILD_VER_COMMENT_LENGTH );
}
while ( s ) {
for (i = 0; i < (SplitOnly ? (BLOCKDEF_INTERNAL + 1) : C_BLOCKDEFS); i++) {
if (ProcessBlock (&s,
aBlockDef[i].pszBlockStart,
aBlockDef[i].pszBlockEnd,
aBlockDef[i].dwFlags) == DONE) {
goto bottom;
}
}
if(!CheckForSingleLine(s)) {
// fprintf (stderr, "ProcessSouceFile: output by default\n");
fputs(s, OutputFile1);
}
bottom:
s = fgets(StringBuffer,STRING_BUFFER_SIZE,SourceFile);
}
}
//
// CheckForSingleLine - processes a line looking for a line tag.
//
// RETURNS: TRUE if a line tag was found and the line was dealt with.
//
// FALSE if no line tag was found.
//
boolean
CheckForSingleLine(
char *s
)
{
int i;
for (i = 0; i < (SplitOnly ? (BLOCKDEF_INTERNAL + 1) : C_BLOCKDEFS); i++) {
if (ProcessLine (s,
aBlockDef[i].pszLineMark,
aBlockDef[i].dwFlags) == DONE) {
return TRUE;
}
}
return(FALSE);
}
//
// ProcessLine - looks for a line tag in an input string and does the
// correct output corresponding to the bits passed in the
// mode parameter. If there is no line tag, there is no
// output generated.
//
// Input -
// LineTag -
// mode - MODE_PUBLICALL:
// MODE_PRIVATEALL:
// NTONLY:
// WIN4ONLY:
//
// RETURNS - DONE if found the LineTag in Input.
//
// NOTDONE if LineTag was not found in Input string.
int
ProcessLine (
char *Input,
char *LineTag,
int mode
)
{
char *comment;
char *tag;
// fprintf (stderr, "ProcessLine: Input=%s, LineTag=%s, mode=0x%x\n",
// Input, LineTag, mode);
//
// Check for a single line to output.
//
comment = laststrstr(Input,CommentDelimiter);
if ( comment ) {
// get past the comment delimiter and white space
tag = comment + strlen (CommentDelimiter);
while (isspace (*tag)) tag++;
if ( tag && ExactMatch (tag, LineTag)) {
char *p;
p = laststrstr(comment + 1, CommentDelimiter);
while (p != NULL && p < tag) {
comment = p;
p = laststrstr(comment + 1, CommentDelimiter);
}
if (NT || Cairo || SURPlus || (mode & MODE_PUBLICCHICAGO)) {
// lop off the line tag.
while (isspace(*(--comment)));
comment++;
*comment++ = '\n';
*comment = '\0';
} else {
// put the // before the CommentDelimter
char temp [STRING_BUFFER_SIZE];
strcpy (temp, comment);
*comment++ = '/';
*comment++ = '/';
*comment++ = ' ';
strcpy (comment, temp);
}
if (mode & MODE_IFDEFALL) {
AddIfDef (mode, TRUE);
}
AddString(mode, Input);
if (mode & MODE_IFDEFALL) {
AddIfDef (mode, FALSE);
}
return(DONE);
}
}
return (NOT_DONE);
}
//
// ExactMatch - performs an exact, case insensitive string compare between
// LookingFor and the first token in Input.
//
// RETURNS: TRUE if exact match, else FALSE
//
boolean
ExactMatch (char *Input, char *LookingFor)
{
char Save;
int Length;
boolean TheSame;
// if (*Input == '\0' || *LookingFor == '\0')
// fprintf (stderr, "\n\n\nExactMatch: Input='%s' and LookingFor='%s'\n",
// Input, LookingFor);
//
// Place a '\0' at the first space in the string, then compare, and restore
//
Length = 0;
while (Input [Length] != '\0' && !isspace (Input[Length])) {
// fprintf (stderr, "Input[%d]='0x%x', isspace=%s\n",
// Length,
// Input [Length],
// isspace (Input[Length])?"T":"F");
Length++;
}
Save = Input [Length];
Input [Length] = '\0';
TheSame = !_stricmp (Input, LookingFor);
// fprintf (stderr, "Comparing Input='%s' and LookingFor='%s', ret=%d\n",
// Input, LookingFor, TheSame);
Input [Length] = Save;
return (TheSame);
}
//
// laststrstr
//
// Finds the last occurence of string2 in string1
//
char *
laststrstr( const char *str1, const char *str2 )
{
const char *cp = str1 + strlen(str1) - strlen(str2);
const char *s1, *s2;
while(cp > str1) {
s1 = cp;
s2 = str2;
while( *s1 && *s2 && (*s1 == *s2) ) {
s1++, s2++;
}
//
// If the chars matched until s2 reached '\0', then we've
// found our substring.
//
if(*s2 == '\0') {
// fprintf (stderr, "laststrstr: found '%s' in '%s'\n",
// str2, str1);
return((char *) cp);
}
cp--;
}
// fprintf (stderr, "laststrstr: did not find '%s' in '%s'\n",
// str2, str1);
return(NULL);
}
int
ProcessBlock (
char **pInput,
char *BlockTagStart,
char *BlockTagEnd,
int mode
)
{
char *comment;
char *tag;
char *Input = *pInput;
// fprintf (stderr, "ProcessBlock: *pINput=%s, BlockTagStart=%s, BlockTagEnd=%s\n", *pInput, BlockTagStart, BlockTagEnd);
comment = strstr(Input,CommentDelimiter);
if ( comment ) {
// get past the comment delimiter and white space
tag = comment + strlen (CommentDelimiter);
while (isspace (*tag)) tag++;
//
// If we found a substring and the tag is identical to
// what we are looking for...
//
if ( tag && ExactMatch (tag, BlockTagStart)) {
//
// Now that we have found an opening tag, check each
// following line for the closing tag, and then include it
// in the output.
//
//
// For NT we set the string to be WINVER < 0x0400 so we
// don't interfere with the Cairo stuff
//
if(mode & MODE_IFDEFALL) {
AddIfDef (mode, TRUE);
}
Input = fgets(StringBuffer,STRING_BUFFER_SIZE,SourceFile);
while ( Input ) {
comment = strstr(Input,CommentDelimiter);
if ( comment ) {
tag = strstr(comment,BlockTagEnd);
if ( tag ) {
if(mode & MODE_IFDEFALL) {
AddIfDef (mode, FALSE);
}
return DONE;
}
}
DoOutput (mode, Input);
Input = fgets(StringBuffer,STRING_BUFFER_SIZE,SourceFile);
}
}
}
*pInput = Input;
return NOT_DONE;
}
//
// DoOuput - called to output a line during block processsing. Since
// some lines for Chicago's header files contain line tags,
// we need to do line processing on the line to see if
// it needs to be treated specially.
//
void
DoOutput (
int mode,
char *string
)
{
char *comment;
//
// When we do line processing on it, we will return if this
// line processing actually did the output already. Otherwise,
// drop into the output processing for lines within a block.
//
// fprintf (stderr, "DoOutput (%d,%s)\n", mode, string);
if (CheckForSingleLine (string)) {
// fprintf (stderr, "DoOutput: Called CheckForSingleLine and returning\n");
return;
}
if ((mode & MODE_PRIVATECHICAGO) && Chicago && !(mode & MODE_PUBLICCHICAGO)) {
//
// If this is for Chicago, outfile2 is not relavant
// but we have to add the internal comment
//
comment = string + strlen(string);
while(*(--comment) != '\n');
if(comment != string) {
*comment='\0';
strcat(string, "\t// ;internal\n");
}
}
AddString(mode, string);
}
void
AddString (
int mode,
char *string
)
{
if ((NT && (mode & MODE_PUBLICNT)) ||
(Chicago && ((mode & MODE_PUBLICCHICAGO) || (mode & MODE_PRIVATECHICAGO))) ||
(Cairo && (mode & MODE_PUBLICCAIRO)) ||
(SURPlus && (mode & MODE_PUBLICSURPLUS)) ||
(SplitOnly && (mode & MODE_PUBLICALL)))
fputs(string, OutputFile1);
if ((NT && (mode & MODE_PRIVATENT)) ||
(Cairo && (mode & MODE_PRIVATECAIRO)) ||
(SURPlus&& (mode & MODE_PRIVATESURPLUS)) ||
(SplitOnly && (mode & MODE_PRIVATEALL)))
fputs(string, OutputFile2);
}
void
AddIfDef (
int mode,
int fBegin
)
{
int iVerIndex = 0;
int iBeginEnd = 0;
//
// Chose index of the ifdef string
//
iBeginEnd = fBegin ? 0 : 1;
if (mode & MODE_IFDEF_WV400 ) iVerIndex = 0;
else if (mode & MODE_IFDEF_WV40a ) iVerIndex = 2;
else if (mode & MODE_IFDEF_W32WNT) iVerIndex = 4;
else if (mode & MODE_IFDEF_W32WIN) iVerIndex = 6;
else
fprintf (stderr, "AddIfDef: bad mode parameter\n");
//
// Write ifdef line to the correct files.
//
if ((NT && (mode & MODE_PUBLICNT)) ||
(Chicago && ((mode & MODE_PUBLICCHICAGO) || (mode & MODE_PRIVATECHICAGO))) ||
(Cairo && (mode & MODE_PUBLICCAIRO)) ||
(SURPlus && (mode & MODE_PUBLICSURPLUS)))
fputs(szIFDEF[iVerIndex][iBeginEnd], OutputFile1);
//
// Private NT material also goes into public, so reverse sense of #if check
// to avoid multiple defines.
//
if (NT && (mode & MODE_PRIVATENT))
fputs(szIFDEF[iVerIndex+1][iBeginEnd], OutputFile2);
//
// Not a problem for private cairo material, since it is not in public.
//
if (Cairo && (mode & MODE_PRIVATECAIRO))
fputs(szIFDEF[iVerIndex][iBeginEnd], OutputFile2);
//
// Not a problem for private SURPlus material, since it is not in public.
//
if (SURPlus && (mode & MODE_PRIVATESURPLUS))
fputs(szIFDEF[iVerIndex][iBeginEnd], OutputFile2);
}