/*++

Copyright (c) 1995 Microsoft Corporation

Module Name:

    dosnet.c

Abstract:

    This module implements a program that generates the [Files]
    section of dosnet.inf.

    The input consists of the layout inf; the output consists of
    an intermediary form of dosnet.inf.

Author:

    Ted Miller (tedm) 20-May-1995

Revision History:

--*/

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <setupapi.h>
#include <sputils.h>


//
// Define program result codes (returned from main()).
//
#define SUCCESS 0
#define FAILURE 1


//
// Keep statistics...
//
INT     ProcessedLines = 0;
INT     RemovedEntries = 0;
INT     DuplicateEntries = 0;
INT     PassThroughEntries = 0;

HINF    hExclusionInf = INVALID_HANDLE_VALUE;

BOOL
ParseArgs(
    IN int   argc,
    IN char *argv[]
    )
{
    return(argc > 5);
}


BOOL
IsEntryInFilterFile(
    IN HINF    hFilterinf,
    IN PCWSTR  Inputval
    )
{
INFCONTEXT Context,SectionContext;
PCWSTR     CabName;
WCHAR      SectionName[LINE_LEN];
UINT       Field,
           FieldCount;

    //
    // First make sure we actually have a filter inf.
    // If not, then the entry certainly isn't in there.
    //
    if( !hFilterinf ) {
        return( FALSE );
    }

    //
    // Now get the section names that we have to search.
    //
    if (!SetupFindFirstLineW( hFilterinf, L"Version", L"CabFiles", &SectionContext)) {
        return( FALSE );
    }

    //
    // Search the sections for our entry.
    //
    do {

        FieldCount = SetupGetFieldCount(&SectionContext);
        for( Field=1; Field<=FieldCount; Field++ ) {
            if(SetupGetStringFieldW(&SectionContext,Field,SectionName,LINE_LEN,NULL)
               && SetupFindFirstLineW(hFilterinf, SectionName, Inputval, &Context)) {
                //
                // we found a match
                //
                return( TRUE );
            }
        }

    } while (SetupFindNextMatchLine(&SectionContext,TEXT("CabFiles"),&SectionContext));

    //
    // If we get here, we didn't find a match.
    //
    return( FALSE );
}

void
AddToTempFile(
    PCWSTR FileName,
    PINFCONTEXT InfContext,
    FILE   *TempFile
    ){

    WCHAR BootFloppyval[LINE_LEN];
    UCHAR      line[MAX_INF_STRING_LENGTH];

    if(SetupGetStringFieldW(InfContext,7,BootFloppyval,LINE_LEN,NULL)
            && BootFloppyval[0]){

        WideCharToMultiByte(
                        CP_OEMCP,
                        0,
                        FileName,
                        -1,
                        line,
                        sizeof(line),
                        NULL,
                        NULL
                        );

        fprintf(TempFile,"%s\n",line);
        //fprintf(stderr,"xdosnet: Writing file %s\n",FileName);
    }

    return;

}


BOOL
InExclusionList(
    PCWSTR FileName,
    PINFCONTEXT InfContext
    )
{
    WCHAR BootFloppyval[LINE_LEN];
    INFCONTEXT Ctxt;


    //
    // first look in our global hardcoded exclusion list for the file
    //
    if (hExclusionInf != INVALID_HANDLE_VALUE) {
        if (SetupFindFirstLineW(hExclusionInf, L"Files", FileName, &Ctxt)) {
//            printf("excluding via inf file %ws\n",FileName);
            return(TRUE);
        }
    }

    //
    // now we need to see if this is a boot-disk file, which must be
    // excluded
    //
    if (SetupGetStringFieldW(InfContext,7,BootFloppyval,LINE_LEN,NULL)
            && !BootFloppyval[0]) {
        return(FALSE);
    }

//    printf("excluding boot file %ws\n",FileName);

    return(TRUE);

}

// Returns the DiskId of the file.  This is basically
// 1 or 2 for now.
void pGetDiskIdStr(
	IN INFCONTEXT InputContext,
	IN DWORD DiskID,
	IN PSTR StrDiskID,
	IN DWORD  StrLen
	)
{
	WCHAR      Tmp[20];

    if ( DiskID == -1 )
	{
		if(SetupGetStringFieldW(&InputContext,1,Tmp,sizeof(Tmp)/sizeof(WCHAR),NULL)) 
		{
            // Hack to make the CHS, CHT & KOR builds working.  They use 7 as the
            // diskid for some reason.  This means unless we do this hack the binaries
            // are marked to be on the 7th disk and that creates havoc with winnt.exe
            // not being able to copy stuff etc.
            // The hack is to see if the diskid is 7 and if it is then reset it to
            // 1.  This is what would have happened before the 2CD changes to
            // xdosnet.exe,  makefile.inc in MergedComponents\SetupInfs
            if ( ! lstrcmpW(Tmp,L"7") )
                lstrcpyW(Tmp, L"1");
		}
		else
		{
			// say it is d1 if the above fails
			lstrcpyW(Tmp,L"1");
		}
	}
	else
	{
		swprintf(Tmp, L"%d", DiskID);
	}
	
	WideCharToMultiByte(
		CP_OEMCP,
		0,
		Tmp,
		-1,
		StrDiskID,
		StrLen,
		NULL,
		NULL
		);
}

BOOL
DoSection(
    IN HINF    hInputinf,
    IN PCWSTR  InputSectionName,
    IN HINF    hFilterinf,
    IN DWORD   DiskID,
    IN FILE   *OutFile,
    IN FILE   *ExcludeFile,
    IN FILE   *TempFile
    )
{
#define VERBOSE 1
INFCONTEXT InputContext;
UCHAR      line[MAX_INF_STRING_LENGTH];
WCHAR      Inputval[MAX_INF_STRING_LENGTH];
BOOL       WriteEntry = TRUE;
UCHAR      StrDiskID[20];


    if(SetupFindFirstLineW(hInputinf,InputSectionName,NULL,&InputContext)) {

        do {

        //
        // Keep track of how many lines we process from the original inf (layout.inf)
        //
        ProcessedLines++;

            if(SetupGetStringFieldW(&InputContext,0,Inputval,MAX_INF_STRING_LENGTH,NULL)) {

                //
                // Assume the entry is good unless proven otherwise.
                //
                WriteEntry = TRUE;

                if( TempFile ) {
                    AddToTempFile( Inputval, &InputContext, TempFile );
                }
                    


                //
                // See if it's in the filter file.
                //
                if( IsEntryInFilterFile( hFilterinf, Inputval ) ) {
                    if (!InExclusionList(Inputval, &InputContext )) {
                        //
                        // It's in the exclusion list.  Skip it.
                        //
                        RemovedEntries++;
                        WriteEntry = FALSE;

                        if (ExcludeFile) {

                            if( WideCharToMultiByte(
                                CP_OEMCP,
                                0,
                                Inputval,
                                -1,
                                line,
                                sizeof(line),
                                NULL,
                                NULL
                                ) ){

                                fprintf(ExcludeFile,"%s\n",line);

                            }

                            

                        }
                    } else {
                        //
                        // It's a boot file.  Keep it.  Note that it's a
                        // duplicate and will appear both inside and outside
                        // the CAB.
                        //
                        DuplicateEntries++;
                    }
                } else {
                    //
                    // It's not even in the filter file.  Log it for
                    // statistics.
                    //
                    PassThroughEntries++;
                }

                //
                // Write the entry out only if it's not supposed to
                // be filtered out.
                //
                if( WriteEntry ) {
                    //
                    // Dosnet.inf is in OEM chars.
                    //
                    WideCharToMultiByte(
                        CP_OEMCP,
                        0,
                        Inputval,
                        -1,
                        line,
                        sizeof(line),
                        NULL,
                        NULL
                        );

					// We need to find the disk this file is on and add that
					// to the file description.
					pGetDiskIdStr(InputContext, DiskID, StrDiskID, sizeof(StrDiskID));

                    fprintf(OutFile,"d%s,%s\n",StrDiskID,line);
                }


            } else {
                fprintf(stderr,"A line in section %ws has no key\n",InputSectionName);
                return(FALSE);
            }
        } while(SetupFindNextLine(&InputContext,&InputContext));

    } else {
        fprintf(stderr,"Section %ws is empty or missing\n",InputSectionName);
        return(FALSE);
    }

    return(TRUE);
}

BOOL
DoIt(
    IN char *InFilename,
    IN char *FilterFilename,
    IN DWORD DiskID,
    IN FILE *OutFile,
    IN FILE *ExcludeFile,
    IN char *PlatformExtension,
    IN FILE *TempFile
    )
{
    PCWSTR inFilename;
    PCWSTR filterFilename;
    PCWSTR extension;
    HINF hInputinf,
         hFilterinf;
    BOOL b;
    WCHAR sectionName[256];
    INFCONTEXT Ctxt;

    b = FALSE;

    inFilename = pSetupAnsiToUnicode(InFilename);
    filterFilename = pSetupAnsiToUnicode(FilterFilename);

    //
    // Only proceed if we've got a file to work with.
    //
    if( inFilename ) {

        //
        // Only proceed if we've got a filter file to work with.
        //
        if( filterFilename ) {

            hInputinf = SetupOpenInfFileW(inFilename,NULL,INF_STYLE_WIN4,NULL);
            if(hInputinf != INVALID_HANDLE_VALUE) {

                //
                // If the filter-file fails, just keep going.  This will
                // result in a big dosnet.inf, which means we'll have files
                // present both inside and outside the driver CAB, but
                // that's not fatal.
                //
                hFilterinf = SetupOpenInfFileW(filterFilename,NULL,INF_STYLE_WIN4,NULL);
                if(hFilterinf == INVALID_HANDLE_VALUE) {
                    fprintf(stderr,"Unable to open inf file %s\n",FilterFilename);
                    hFilterinf = NULL;
                }


                //
                // We're actually ready to process the sections!
                //
                fprintf(OutFile,"[Files]\n");

                if (ExcludeFile) {
                    fprintf(ExcludeFile,"[Version]\n");
                    fprintf(ExcludeFile,"signature=\"$Windows NT$\"\n");
                    fprintf(ExcludeFile,"[Files]\n");
                }


                b = DoSection( hInputinf,
                               L"SourceDisksFiles",
                               hFilterinf,
                               DiskID,
                               OutFile,
                               ExcludeFile,
                               TempFile );

                if( b ) {

                    //
                    // Now process the x86-or-Alpha-specific section.
                    //
                    if(extension = pSetupAnsiToUnicode(PlatformExtension)) {

                        lstrcpyW(sectionName,L"SourceDisksFiles");
                        lstrcatW(sectionName,L".");
                        lstrcatW(sectionName,extension);
                        b = DoSection( hInputinf,
                                       sectionName,
                                       hFilterinf,
                                       DiskID,
                                       OutFile,
                                       ExcludeFile,
                                       TempFile );

                        pSetupFree(extension);
                    } else {
                        fprintf(stderr,"Unable to convert string %s to Unicode\n",PlatformExtension);
                    }
                }

                //Write the files in the input exclude INF to the [ForceCopyDriverCabFiles] section

                if (hExclusionInf != INVALID_HANDLE_VALUE) {

                    WCHAR Filename[LINE_LEN];
                    UCHAR line[MAX_INF_STRING_LENGTH];


                    if (SetupFindFirstLineW(hExclusionInf, L"Files", NULL, &Ctxt)){


                        fprintf(OutFile,"\n\n[ForceCopyDriverCabFiles]\n");


                        do{

                            if( SetupGetStringFieldW( &Ctxt, 1, Filename, LINE_LEN, NULL )){


                                //
                                // Dosnet.inf is in OEM chars.
                                //
                                WideCharToMultiByte(
                                    CP_OEMCP,
                                    0,
                                    Filename,
                                    -1,
                                    line,
                                    sizeof(line),
                                    NULL,
                                    NULL
                                    );

                                
                                fprintf(OutFile,"%s\n",line);

                            }


                        }while( SetupFindNextLine( &Ctxt, &Ctxt ));

                    }else{

                        fprintf(stderr,"Could not find the Files section in the Exclude INF file\n");
                    }

                    

                }


                //
                // Print Statistics...
                //
                fprintf( stderr, "                               Total lines processed: %6d\n", ProcessedLines );
                fprintf( stderr, "                     Entries removed via filter file: %6d\n", RemovedEntries );
                fprintf( stderr, "Entries appearing both inside and outside driver CAB: %6d\n", DuplicateEntries );
                fprintf( stderr, "                Entries not appearing in filter file: %6d\n", PassThroughEntries );

                //
                // Close up our inf handles.
                //
                if( hFilterinf ) {
                    SetupCloseInfFile( hFilterinf );
                }
                SetupCloseInfFile(hInputinf);

            } else {
                fprintf(stderr,"Unable to open inf file %s\n",InFilename);
            }

            pSetupFree( filterFilename );

        } else {
            fprintf(stderr,"Unable to convert filename %s to Unicode\n",FilterFilename);
        }
        pSetupFree(inFilename);
    } else {
        fprintf(stderr,"Unable to convert filename %s to Unicode\n",InFilename);
        return(FALSE);
    }

    return(b);
}


int
__cdecl
main(
    IN int   argc,
    IN char *argv[]
    )
{
    FILE *OutputFile,*ExcludeFile, *TempFile;
    BOOL b;
    DWORD DiskID;
    char input_filename_fullpath[MAX_PATH];
    char *p;

    //
    // Assume failure.
    //
    b = FALSE;

    if(!pSetupInitializeUtils()) {
        return FAILURE;
    }

    if(ParseArgs(argc,argv)) {

        //
        // Open the output file.
        //
        OutputFile = fopen(argv[4],"wt");
        
        if (argc >= 7) {
            ExcludeFile = fopen(argv[6],"wt");
        } else {
            ExcludeFile = NULL;
        }

        if (argc >= 8) {
            hExclusionInf = SetupOpenInfFileA(argv[7],NULL,INF_STYLE_WIN4,NULL);
            if (hExclusionInf != INVALID_HANDLE_VALUE) {
                fprintf(stderr,"xdosnet: Opened file %s\n",argv[7]);
            }
            
        } else {
            hExclusionInf = INVALID_HANDLE_VALUE;
        }

        if (argc >= 9) {
            TempFile = fopen(argv[8],"wt");
            if( !TempFile )
                fprintf(stderr,"%s: Unable to create temp file %s\n",argv[0],argv[8]);
            //fprintf(stderr,"xdosnet: Created file %s\n",argv[8]);
        }else{
            TempFile = NULL;
        }

		// Special case handling Disk 1, Disk 2 etc. for x86.  Since we want to process
		// all lines - this just means ignore the disk id specified on the command line
		// and pick up the Disk ID that is specified in the layout.inx entry itself.
        if ( argv[3][0] == '*' )
			DiskID = -1;
		else
			DiskID = atoi(argv[3]);

        GetFullPathName(
                argv[1],
                sizeof(input_filename_fullpath),
                input_filename_fullpath,
                &p);


        if(OutputFile) {

            fprintf(
                stdout,
                "%s: creating %s from %s and %s for %s (%s)\n",
                argv[0],
                argv[4],
                input_filename_fullpath,
                argv[2],
                argv[5],
                argv[6]);

            b = DoIt( input_filename_fullpath,
                      argv[2],
                      DiskID,
                      OutputFile,
                      ExcludeFile,
                      argv[5],
                      TempFile);

            fclose(OutputFile);

        } else {
            fprintf(stderr,"%s: Unable to create output file %s\n",argv[0],argv[3]);
        }

        if (ExcludeFile) {
	    fclose(ExcludeFile);
        }
        if (TempFile) {
	    fclose(TempFile);
        }

    } else {
        fprintf( stderr,"Merge 3 inf files.  Usage:\n" );
        fprintf( stderr,"%s  <input file1> <filter file> <diskid> <output file> <platform extension> <optional output exclude file> <optional input exclusion inf>\n", argv[0] );
        fprintf( stderr,"\n" );
        fprintf( stderr,"  <input file1> - original inf file (i.e. layout.inf)\n" );
        fprintf( stderr,"  <filter file> - contains a list of entries to be excluded\n" );
        fprintf( stderr,"                  from the final output file\n" );
        fprintf( stderr,"  <disk id>     - output disk id (i.e. 1 or 2)\n" );
        fprintf( stderr,"  <output file> - output inf (i.e. dosnet.inf)\n" );
        fprintf( stderr,"  <platform extension>\n" );
        fprintf( stderr,"  <output exclude file> - optional output file containing files that were filtered\n" );
        fprintf( stderr,"  <input exclusion inf> - optional input inf containing files that should never be filtered\n" );
        fprintf( stderr,"  <temp file> - optional file to be used to write boot file list into (IA64 temporary workaround)\n");
        fprintf( stderr,"\n" );
        fprintf( stderr,"\n" );

    }

    pSetupUninitializeUtils();

    return(b ? SUCCESS : FAILURE);
}