]> git.saurik.com Git - apple/mdnsresponder.git/blobdiff - Clients/PrinterSetupWizard/ThirdPage.cpp
mDNSResponder-878.200.35.tar.gz
[apple/mdnsresponder.git] / Clients / PrinterSetupWizard / ThirdPage.cpp
index 76978cdd4bf3ebbe8364c986f5049f87e1f9a17b..6caf55cb29207c46313c46ccd23c12a75ad476f0 100644 (file)
-/*
+/* -*- Mode: C; tab-width: 4 -*-
+ *
  * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
  *
- * @APPLE_LICENSE_HEADER_START@
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  * 
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
+ *     http://www.apache.org/licenses/LICENSE-2.0
  * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
  * limitations under the License.
- * 
- * @APPLE_LICENSE_HEADER_END@
-
-    Change History (most recent first):
-    
-$Log: ThirdPage.cpp,v $
-Revision 1.14  2005/01/25 08:55:54  shersche
-<rdar://problem/3911084> Load icons at run-time from resource DLL
-Bug #: 3911084
-
-Revision 1.13  2005/01/06 08:15:45  shersche
-Append queue name to end of LPR port name, correctly build port name when queue name is absent
-
-Revision 1.12  2005/01/05 01:06:12  shersche
-<rdar://problem/3841218> Strip the first substring off the product key if an initial match can't be found with the whole product key.
-Bug #: 3841218
-
-Revision 1.11  2004/12/29 18:53:38  shersche
-<rdar://problem/3725106>
-<rdar://problem/3737413> Added support for LPR and IPP protocols as well as support for obtaining multiple text records. Reorganized and simplified codebase.
-Bug #: 3725106, 3737413
-
-Revision 1.10  2004/10/11 22:55:34  shersche
-<rdar://problem/3827624> Use the IP port number when deriving the printer port name.
-Bug #: 3827624
-
-Revision 1.9  2004/06/27 23:08:00  shersche
-code cleanup, make sure EnumPrintDrivers returns non-zero value, ignore comments in inf files
-
-Revision 1.8  2004/06/27 08:06:45  shersche
-Parse [Strings] section of inf file
-
-Revision 1.7  2004/06/26 04:00:05  shersche
-fix warnings compiling in debug mode
-Submitted by: herscher
-
-Revision 1.6  2004/06/26 03:19:57  shersche
-clean up warning messages
-
-Submitted by: herscher
-
-Revision 1.5  2004/06/25 05:06:02  shersche
-Trim whitespace from key/value pairs when parsing inf files
-Submitted by: herscher
-
-Revision 1.4  2004/06/25 02:44:13  shersche
-Tweaked code to handle Xerox Phaser printer identification
-Submitted by: herscher
-
-Revision 1.3  2004/06/25 02:27:58  shersche
-Do a CListCtrl::FindItem() before calling CListCtrl::SetItemState().
-Submitted by: herscher
-
-Revision 1.2  2004/06/23 18:09:23  shersche
-Normalize tag names when parsing inf files.
-Submitted by: herscher
-
-Revision 1.1  2004/06/18 04:36:58  rpantos
-First checked in
-
-
-*/
+ */
 
 #include "stdafx.h"
 #include "PrinterSetupWizardApp.h"
 #include "PrinterSetupWizardSheet.h"
 #include "ThirdPage.h"
-#include "StdioFileEx.h"
+#include "tcpxcv.h"
 #include <dns_sd.h>
-#include <tcpxcv.h>
 #include <winspool.h>
+#include <setupapi.h>
 
 // local variable is initialize but not referenced
 #pragma warning(disable:4189)
 
-
 //
 // This is the printer description file that is shipped
-// with Windows
+// with Windows XP and below
 //
 #define kNTPrintFile           L"inf\\ntprint.inf"
 
 //
-// These are pre-defined names for Generic manufacturer and model
+// Windows Vista ships with a set of prn*.inf files
 //
-#define kGenericManufacturer   L"Generic"
-#define kGenericModel                  L"Generic / Text Only"
+#define kVistaPrintFiles       L"inf\\prn*.inf"
 
 //
-// states for parsing ntprint.inf
+// These are pre-defined names for Generic manufacturer and model
 //
-enum PrinterParsingState
-{
-       Looking,
-       ParsingManufacturers,
-       ParsingModels,
-       ParsingStrings
-};
+#define kGenericManufacturer           L"Generic"
+#define kGenericText                           L"Generic / Text Only"
+#define kGenericPostscript                     L"Generic / Postscript"
+#define kGenericPCL                                    L"Generic / PCL"
+#define kPDLPostscriptKey                      L"application/postscript"
+#define kPDLPCLKey                                     L"application/vnd.hp-pcl"
+#define kGenericPSColorDriver          L"HP Color LaserJet 4550 PS"
+#define kGenericPSDriver                       L"HP LaserJet 4050 Series PS"
+#define kGenericPCLColorDriver         L"HP Color LaserJet 4550 PCL"
+#define kGenericPCLDriver                      L"HP LaserJet 4050 Series PCL"
 
 
 // CThirdPage dialog
@@ -122,50 +58,113 @@ enum PrinterParsingState
 IMPLEMENT_DYNAMIC(CThirdPage, CPropertyPage)
 CThirdPage::CThirdPage()
        : CPropertyPage(CThirdPage::IDD),
+               m_manufacturerSelected( NULL ),
+               m_modelSelected( NULL ),
+               m_genericPostscript( NULL ),
+               m_genericPCL( NULL ),
                m_initialized(false),
                m_printerImage( NULL )
 {
+       static const int        bufferSize      = 32768;
+       TCHAR                           windowsDirectory[bufferSize];
+       CString                         header;
+       WIN32_FIND_DATA         findFileData;
+       HANDLE                          findHandle;
+       CString                         prnFiles;
+       CString                         ntPrint;
+       OSStatus                        err;
+       BOOL                            ok;
+
        m_psp.dwFlags &= ~(PSP_HASHELP);
        m_psp.dwFlags |= PSP_DEFAULT|PSP_USEHEADERTITLE|PSP_USEHEADERSUBTITLE;
        
        m_psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_INSTALL_TITLE);
        m_psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_INSTALL_SUBTITLE);
-}
 
-
-CThirdPage::~CThirdPage()
-{
        //
-       // clean up all the printer manufacturers
+       // load printers from ntprint.inf
        //
-       while (m_manufacturers.size())
+       ok = GetWindowsDirectory( windowsDirectory, bufferSize );
+       err = translate_errno( ok, errno_compat(), kUnknownErr );
+       require_noerr( err, exit );
+       //
+       // <rdar://problem/4826126>
+       //
+       // If there are no *prn.inf files, we'll assume that the information
+       // is in ntprint.inf
+       //
+       prnFiles.Format( L"%s\\%s", windowsDirectory, kVistaPrintFiles );
+       findHandle = FindFirstFile( prnFiles, &findFileData );
+       if ( findHandle != INVALID_HANDLE_VALUE )
        {
-               Manufacturers::iterator iter = m_manufacturers.begin();
+               CString absolute;
 
-               while (iter->second->models.size())
+               absolute.Format( L"%s\\inf\\%s", windowsDirectory, findFileData.cFileName );
+               err = LoadPrintDriverDefsFromFile( m_manufacturers, absolute, false );
+               require_noerr( err, exit );
+
+               while ( FindNextFile( findHandle, &findFileData ) )
                {
-                       Models::iterator it = iter->second->models.begin();
+                       absolute.Format( L"%s\\inf\\%s", windowsDirectory, findFileData.cFileName );
+                       err = LoadPrintDriverDefsFromFile( m_manufacturers, absolute, false );
+                       require_noerr( err, exit );
+               }
 
-                       Model * model = *it;
+               FindClose( findHandle );
+       }
+       else
+       {
+               ntPrint.Format(L"%s\\%s", windowsDirectory, kNTPrintFile);
+               err = LoadPrintDriverDefsFromFile( m_manufacturers, ntPrint, false );
+               require_noerr(err, exit);
+       }
 
-                       delete model;
+       //
+       // load printer drivers that have been installed on this machine
+       //
+       err = LoadPrintDriverDefs( m_manufacturers );
+       require_noerr(err, exit);
 
-                       iter->second->models.erase(it);
-               }
+       //
+       // load our own special generic printer defs
+       //
+       err = LoadGenericPrintDriverDefs( m_manufacturers );
+       require_noerr( err, exit );
 
-               delete iter->second;
+exit:
 
-               m_manufacturers.erase(iter);
+       return;
+}
+
+
+void
+CThirdPage::FreeManufacturers( Manufacturers & manufacturers )
+{
+       for ( Manufacturers::iterator it = manufacturers.begin(); it != manufacturers.end(); it++ )
+       {
+               for ( Models::iterator it2 = it->second->models.begin(); it2 != it->second->models.end(); it2++ )
+               {
+                       delete *it2;
+               }
+
+               delete it->second;
        }
 }
 
 
+CThirdPage::~CThirdPage()
+{
+       FreeManufacturers( m_manufacturers );
+}
+
 // ----------------------------------------------------
 // SelectMatch
 //
 // SelectMatch will do all the UI work associated with
 // selected a manufacturer and model of printer.  It also
-// makes sure the printer object is update with the 
+// makes sure the printer object is update with the
 // latest settings
 //
 // ----------------------------------------------------
@@ -179,11 +178,6 @@ CThirdPage::SelectMatch(Printer * printer, Service * service, Manufacturer * man
        check( manufacturer != NULL );
        check( model != NULL );
 
-       Manufacturers manufacturers;
-       manufacturers[manufacturer->name] = manufacturer;
-
-       PopulateUI( manufacturers );
-
        //
        // select the manufacturer
        //
@@ -195,21 +189,24 @@ CThirdPage::SelectMatch(Printer * printer, Service * service, Manufacturer * man
        if (nIndex != -1)
        {
                m_manufacturerListCtrl.SetItemState(nIndex, LVIS_SELECTED, LVIS_SELECTED);
-               m_manufacturerListCtrl.EnsureVisible(nIndex, FALSE);
+               //
+               //<rdar://problem/4528853> mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle
+               //
+               AutoScroll(m_manufacturerListCtrl, nIndex);
        }
 
        //
        // select the model
        //
        info.flags      = LVFI_STRING;
-       info.psz        = model->name;
+       info.psz        = model->displayName;
 
        nIndex = m_modelListCtrl.FindItem(&info);
 
        if (nIndex != -1)
        {
                m_modelListCtrl.SetItemState(nIndex, LVIS_SELECTED, LVIS_SELECTED);
-               m_modelListCtrl.EnsureVisible(nIndex, FALSE);
+               AutoScroll( m_modelListCtrl, nIndex );
 
                m_modelListCtrl.SetFocus();
        }
@@ -217,6 +214,13 @@ CThirdPage::SelectMatch(Printer * printer, Service * service, Manufacturer * man
        CopyPrinterSettings( printer, service, manufacturer, model );
 }
 
+void
+CThirdPage::SelectMatch(Manufacturers & manufacturers, Printer * printer, Service * service, Manufacturer * manufacturer, Model * model)
+{
+       PopulateUI( manufacturers );
+
+       SelectMatch( printer, service, manufacturer, model );
+}
 
 // --------------------------------------------------------
 // CopyPrinterSettings
@@ -228,8 +232,11 @@ CThirdPage::SelectMatch(Printer * printer, Service * service, Manufacturer * man
 void
 CThirdPage::CopyPrinterSettings( Printer * printer, Service * service, Manufacturer * manufacturer, Model * model )
 {
+       DWORD portNameLen;
+
        printer->manufacturer           =       manufacturer->name;
-       printer->model                          =       model->name;
+       printer->displayModelName       =       model->displayName;
+       printer->modelName                      =       model->name;
        printer->driverInstalled        =       model->driverInstalled;
        printer->infFileName            =       model->infFileName;
 
@@ -261,7 +268,7 @@ CThirdPage::CopyPrinterSettings( Printer * printer, Service * service, Manufactu
 
                if ( q->name.GetLength() > 0 )
                {
-                       printer->portName.Format(L"http://%s:%d/printers/%s", static_cast<LPCTSTR>(service->hostname), service->portNumber, static_cast<LPCTSTR>(q->name) );
+                       printer->portName.Format(L"http://%s:%d/%s", static_cast<LPCTSTR>(service->hostname), service->portNumber, static_cast<LPCTSTR>(q->name) );
                }
                else
                {
@@ -270,31 +277,80 @@ CThirdPage::CopyPrinterSettings( Printer * printer, Service * service, Manufactu
 
                service->protocol = L"IPP";
        }
+
+       // If it's not an IPP printr, truncate the portName so that it's valid
+
+       if ( service->type != kIPPServiceType )
+       {
+               portNameLen = printer->portName.GetLength() + 1;
+               
+               if ( portNameLen > MAX_PORTNAME_LEN )
+               {
+                       printer->portName.Delete( MAX_PORTNAME_LEN - 1, ( portNameLen - MAX_PORTNAME_LEN ) );
+               }
+       }
+}
+
+// --------------------------------------------------------
+// DefaultPrinterExists
+//
+// Checks to see if a default printer has been configured
+// on this machine
+// --------------------------------------------------------
+BOOL
+CThirdPage::DefaultPrinterExists()
+{
+       CPrintDialog dlg(FALSE);
+       
+       dlg.m_pd.Flags |= PD_RETURNDEFAULT;
+
+       return dlg.GetDefaults();
 }
 
+// --------------------------------------------------------
+// AutoScroll
+//
+// Ensure selected item is in middle of list
+// --------------------------------------------------------
+void
+CThirdPage::AutoScroll( CListCtrl & list, int nIndex )
+{
+       //
+       //<rdar://problem/4528853> mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle
+       //
+
+       int             top;
+       int             count;
+
+       list.EnsureVisible( nIndex, FALSE );
+       
+       top             = list.GetTopIndex();
+       count   = list.GetCountPerPage();
+
+       if ( ( nIndex == top ) || ( ( nIndex + 1 ) == ( top + count ) ) )
+       {
+               CRect   rect;
+               int             rows;
+               
+               rows = ( count / 2 );
+
+               if ( nIndex == top )
+               {
+                       list.GetItemRect(0, rect, LVIR_BOUNDS);
+                       list.Scroll( CPoint( 0, rows * rect.Height() * -1 ) );
+               }
+               else
+               {
+                       list.GetItemRect(0, rect, LVIR_BOUNDS);
+                       list.Scroll( CPoint( 0, rows * rect.Height() ) );
+               }
+       }
+}
 
 // ------------------------------------------------------
 // LoadPrintDriverDefsFromFile
 //
-// This function does all the heavy lifting in parsing inf
-// files.  It is called to parse both ntprint.inf, and driver
-// files that might be shipped on a printer's installation
-// disk
-//
-// The inf file is not totally parsed.  I only want to determine
-// the manufacturer and models that are involved. I leave it
-// to printui.dll to actually copy the driver files to the
-// right places.
-//
-// I was aiming to parse as little as I could so as not to
-// duplicate the parsing code that is contained in Windows.  There
-// are no public APIs for parsing inf files.
-//
-// That part of the inf file that we're interested in has a fairly
-// easy format.  Tags are strings that are enclosed in brackets.
-// We are only interested in [MANUFACTURERS] and models.
-//
-// The only potentially opaque thing about this function is the 
+// The only potentially opaque thing about this function is the
 // checkForDuplicateModels flag.  The problem here is that ntprint.inf
 // doesn't contain duplicate models, and it has hundreds of models
 // listed.  You wouldn't check for duplicates there.  But oftentimes,
@@ -306,304 +362,190 @@ CThirdPage::CopyPrinterSettings( Printer * printer, Service * service, Manufactu
 OSStatus
 CThirdPage::LoadPrintDriverDefsFromFile(Manufacturers & manufacturers, const CString & filename, bool checkForDuplicateModels )
 {
-       PrinterParsingState             state           = Looking;
-       Manufacturers::iterator iter            = manufacturers.end();
-       CStdioFileEx                    file;
-       CFileException                  feError;
-       CString                                 s;
-       OSStatus                                err;
-       BOOL                                    ok;
-
-       typedef std::map<CString, CString> StringMap;
+       HINF                    handle  = INVALID_HANDLE_VALUE;
+       const TCHAR *   section = TEXT( "Manufacturer" );
+       LONG                    sectionCount;
+       TCHAR                   line[ 1000 ];
+       CString                 klass;
+       INFCONTEXT              manufacturerContext;
+       BOOL                    ok;
+       OSStatus                err             = 0;
+       
+       // Make sure we can open the file
+       handle = SetupOpenInfFile( filename, NULL, INF_STYLE_WIN4, NULL );
+       translate_errno( handle != INVALID_HANDLE_VALUE, GetLastError(), kUnknownErr );
+       require_noerr( err, exit );
 
-       StringMap                               strings;
-       ok = file.Open( filename,  CFile::modeRead|CFile::typeText, &feError);
-       err = translate_errno( ok, errno_compat(), kUnknownErr );
+       // Make sure it's a printer file
+       ok = SetupGetLineText( NULL, handle, TEXT( "Version" ), TEXT( "Class" ), line, sizeof( line ), NULL );
+       translate_errno( ok, GetLastError(), kUnknownErr );
        require_noerr( err, exit );
+       klass = line;
+       require_action( klass == TEXT( "Printer" ), exit, err = kUnknownErr );
 
-       check ( state == Looking );
-       check ( iter == manufacturers.end() );
+       sectionCount = SetupGetLineCount( handle, section );
+       translate_errno( sectionCount != -1, GetLastError(), kUnknownErr );
+       require_noerr( err, exit );
 
-       //
-       // first, parse the file looking for string sections
-       //
-       while (file.ReadString(s))
+       memset( &manufacturerContext, 0, sizeof( manufacturerContext ) );
+                       
+       for ( LONG i = 0; i < sectionCount; i++ )
        {
-               //
-               // check for comment
-               //
-               if (s.Find(';') == 0)
-               {
-                       continue;
-               }
-
-               //
-               // check for tag
-               //
-               else if (s.Find('[') == 0)
+               Manufacturers::iterator iter;
+               Manufacturer    *       manufacturer;
+               CString                         manufacturerName;
+               CString                         temp;
+               CStringList                     modelSectionNameDecl;
+               CString                         modelSectionName;
+               CString                         baseModelName;
+               CString                         model;
+               INFCONTEXT                      modelContext;
+               LONG                            modelCount;
+               POSITION                        p;
+
+               if ( i == 0 )
                {
-                       //
-                       // handle any capitalization issues here
-                       //
-                       CString tag = s;
-
-                       tag.MakeLower();
-
-                       if (tag == L"[strings]")
-                       {
-                               state = ParsingStrings;
-                       }
-                       else
-                       {
-                               state = Looking;
-                       }
+                       ok = SetupFindFirstLine( handle, section, NULL, &manufacturerContext );
+                       err = translate_errno( ok, GetLastError(), kUnknownErr );
+                       require_noerr( err, exit );
                }
                else
                {
-                       switch (state)
-                       {
-                               case ParsingStrings:
-                               {
-                                       int     curPos = 0;
-
-                                       if (s.GetLength() > 0)
-                                       {
-                                               CString key = s.Tokenize(L"=",curPos);
-                                               CString val = s.Tokenize(L"=",curPos);
-
-                                               //
-                                               // get rid of all delimiters
-                                               //
-                                               key.Trim();
-                                               val.Remove('"');
-       
-                                               //
-                                               // and store it
-                                               //
-                                               strings[key] = val;
-                                       }
-                               }
-                               break;
-                       }
+                       ok = SetupFindNextLine( &manufacturerContext, &manufacturerContext );
+                       err = translate_errno( ok, GetLastError(), kUnknownErr );
+                       require_noerr( err, exit );
                }
-       }
-
-       file.Close();
 
-       ok = file.Open( filename,  CFile::modeRead|CFile::typeText, &feError);
-       err = translate_errno( ok, errno_compat(), kUnknownErr );
-       require_noerr( err, exit );
-
-       state = Looking;
+               ok = SetupGetStringField( &manufacturerContext, 0, line, sizeof( line ), NULL );
+               err = translate_errno( ok, GetLastError(), kUnknownErr );
+               require_noerr( err, exit );
+               manufacturerName = line;
 
-       check ( iter == manufacturers.end() );
+               ok = SetupGetLineText( &manufacturerContext, handle, NULL, NULL, line, sizeof( line ), NULL );
+               err = translate_errno( ok, GetLastError(), kUnknownErr );
+               require_noerr( err, exit );
 
-       while (file.ReadString(s))
-       {
-               //
-               // check for comment
+               // Try to find some model section name that has entries. Explanation of int file structure
+               // can be found at:
                //
-               if (s.Find(';') == 0)
+               // <http://msdn.microsoft.com/en-us/library/ms794359.aspx>
+               Split( line, ',', modelSectionNameDecl );
+
+               p                                       = modelSectionNameDecl.GetHeadPosition();
+               modelSectionName        = modelSectionNameDecl.GetNext( p );
+               modelCount                      = SetupGetLineCount( handle, modelSectionName );
+               baseModelName           = modelSectionName;
+               
+               while ( modelCount <= 0 && p )
                {
-                       continue;
+                       CString targetOSVersion;
+
+                       targetOSVersion         = modelSectionNameDecl.GetNext( p );
+                       modelSectionName        = baseModelName + TEXT( "." ) + targetOSVersion;
+                       modelCount                      = SetupGetLineCount( handle, modelSectionName );
                }
 
-               //
-               // check for tag
-               //
-               else if (s.Find('[') == 0)
+               if ( modelCount > 0 )
                {
-                       //
-                       // handle any capitalization issues here
-                       //
-                       CString tag = s;
+                       manufacturerName = NormalizeManufacturerName( manufacturerName );
 
-                       tag.MakeLower();
+                       iter = manufacturers.find( manufacturerName );
 
-                       if (tag == L"[manufacturer]")
+                       if ( iter != manufacturers.end() )
                        {
-                               state = ParsingManufacturers;
+                               manufacturer = iter->second;
+                               require_action( manufacturer, exit, err = kUnknownErr );
                        }
                        else
                        {
-                               // remove the leading and trailing delimiters
-                               //
-                               s.Remove('[');
-                               s.Remove(']');
-
-                               // check to see if this is a printer entry
-                               //
-                               iter = manufacturers.find(s);
-
-                               if (iter != manufacturers.end())
+                               try
                                {
-                                       state = ParsingModels;
+                                       manufacturer = new Manufacturer;
                                }
-                               else
+                               catch (...)
                                {
-                                       state = Looking;
+                                       manufacturer = NULL;
                                }
-                       }
-               }
-               //
-               // only look at this if the line isn't empty, or
-               // if it isn't a comment
-               //
-               else if ((s.GetLength() > 0) && (s.Find(';') != 0))
-               {
-                       switch (state)
-                       {
-                               //
-                               // if we're parsing manufacturers, then we will parse
-                               // an entry of the form key=val, where key is a delimited
-                               // string specifying a manufacturer name, and val is
-                               // a tag that is used later in the file.  the key is 
-                               // delimited by either '"' (quotes) or '%' (percent sign).
-                               //
-                               // the tag is used further down the file when models are
-                               // declared.  this allows multiple manufacturers to exist
-                               // in a single inf file.
-                               //
-                               case ParsingManufacturers:
-                               {
-                                       Manufacturer    *       manufacturer;
-                                       int                                     curPos = 0;
-
-                                       CString key = s.Tokenize(L"=",curPos);
-                                       CString val = s.Tokenize(L"=",curPos);
 
-                                       try
-                                       {
-                                               manufacturer = new Manufacturer;
-                                       }
-                                       catch (...)
-                                       {
-                                               manufacturer = NULL;
-                                       }
-
-                                       require_action( manufacturer, exit, err = kNoMemoryErr );
-
-                                       //
-                                       // if it's a variable, look it up
-                                       //
-                                       if (key.Find('%') == 0)
-                                       {
-                                               StringMap::iterator it;
-
-                                               key.Remove('%');
-
-                                               it = strings.find(key);
-
-                                               if (it != strings.end())
-                                               {
-                                                       key = it->second;
-                                               }
-                                       }
-                                       else
-                                       {
-                                               key.Remove('"');
-                                       }
-
-                                       val.TrimLeft();
-                                       val.TrimRight();
+                               require_action( manufacturer, exit, err = kNoMemoryErr );
 
-                                       //
-                                       // why is there no consistency in inf files?
-                                       //
-                                       if (val.GetLength() == 0)
-                                       {
-                                               val = key;
-                                       }
+                               manufacturer->name                                      = manufacturerName;
+                               manufacturers[ manufacturerName ]       = manufacturer;
+                       }
 
-                                       //
-                                       // fix the manufacturer name if necessary
-                                       //
-                                       curPos  =       0;
-                                       val             =       val.Tokenize(L",", curPos);
+                       memset( &modelContext, 0, sizeof( modelContext ) );
 
-                                       manufacturer->name = NormalizeManufacturerName( key );
-                                       manufacturer->tag  = val;
+                       for ( LONG j = 0; j < modelCount; j++ )
+                       {
+                               CString modelName;
+                               Model * model;
 
-                                       manufacturers[val] = manufacturer;
+                               if ( j == 0 )
+                               {
+                                       ok = SetupFindFirstLine( handle, modelSectionName, NULL, &modelContext );
+                                       err = translate_errno( ok, GetLastError(), kUnknownErr );
+                                       require_noerr( err, exit );
                                }
-                               break;
-
-                               case ParsingModels:
+                               else
                                {
-                                       check( iter != manufacturers.end() );
-
-                                       Model   *       model;
-                                       int                     curPos = 0;
-
-                                       CString name            = s.Tokenize(L"=",curPos);
-                                       CString description = s.Tokenize(L"=",curPos);
-                                       
-                                       if (name.Find('%') == 0)
-                                       {
-                                               StringMap::iterator it;
+                                       SetupFindNextLine( &modelContext, &modelContext );
+                                       err = translate_errno( ok, GetLastError(), kUnknownErr );
+                                       require_noerr( err, exit );
+                               }
 
-                                               name.Remove('%');
+                               ok = SetupGetStringField( &modelContext, 0, line, sizeof( line ), NULL );
+                               err = translate_errno( ok, GetLastError(), kUnknownErr );
+                               require_noerr( err, exit );
 
-                                               it = strings.find(name);
+                               modelName = line;
 
-                                               if (it != strings.end())
-                                               {
-                                                       name = it->second;
-                                               }
-                                       }
-                                       else
-                                       {
-                                               name.Remove('"');
-                                       }
-
-                                       name.Trim();
-                                       description.Trim();
-                                       
-                                       //
-                                       // If true, see if we've seen this guy before
-                                       //
-                                       if (checkForDuplicateModels == true)
-                                       {
-                                               if ( MatchModel( iter->second, ConvertToModelName( name ) ) != NULL )
-                                               {
-                                                       continue;
-                                               }
-                                       }
-
-                                       try
-                                       {
-                                               model = new Model;
-                                       }
-                                       catch (...)
+                               if (checkForDuplicateModels == true)
+                               {
+                                       if ( MatchModel( manufacturer, ConvertToModelName( modelName ) ) != NULL )
                                        {
-                                               model = NULL;
+                                               continue;
                                        }
+                               }
 
-                                       require_action( model, exit, err = kNoMemoryErr );
-
-                                       model->infFileName              =       filename;
-                                       model->name                             =       name;
-                                       model->driverInstalled  =       false;
-
-                                       iter->second->models.push_back(model);
+                               //
+                               // Stock Vista printer inf files embed guids in the model
+                               // declarations for Epson printers. Let's ignore those.
+                               //
+                               if ( modelName.Find( TEXT( "{" ), 0 ) != -1 )
+                               {
+                                       continue;
                                }
-                               break;
 
-                               default:
+                               try
                                {
-                                       // pay no attention if we are in any other state
+                                       model = new Model;
                                }
-                               break;
+                               catch (...)
+                               {
+                                       model = NULL;
+                               }
+
+                               require_action( model, exit, err = kNoMemoryErr );
+
+                               model->infFileName              =       filename;
+                               model->displayName              =       modelName;
+                               model->name                             =       modelName;
+                               model->driverInstalled  =       false;
+
+                               manufacturer->models.push_back(model);
                        }
                }
        }
 
 exit:
 
-       file.Close();
+       if ( handle != INVALID_HANDLE_VALUE )
+       {
+               SetupCloseInfFile( handle );
+               handle = NULL;
+       }
 
-       return (err);
+       return err;
 }
 
 
@@ -659,7 +601,7 @@ CThirdPage::LoadPrintDriverDefs( Manufacturers & manufacturers )
        
                        //
                        // skip over anything that doesn't have a manufacturer field.  This
-                       // fixes a bug that I noticed that occurred after I installed 
+                       // fixes a bug that I noticed that occurred after I installed
                        // ProComm.  This program add a print driver with no manufacturer
                        // that screwed up this wizard.
                        //
@@ -722,6 +664,7 @@ CThirdPage::LoadPrintDriverDefs( Manufacturers & manufacturers )
        
                                require_action( model, exit, err = kNoMemoryErr );
        
+                               model->displayName              =       info[i].pName;
                                model->name                             =       info[i].pName;
                                model->driverInstalled  =       true;
        
@@ -740,11 +683,140 @@ exit:
        return err;
 }
 
+// -------------------------------------------------------
+// LoadGenericPrintDriverDefs
+//
+// This function is responsible for loading polymorphic
+// generic print drivers defs.  The UI will read
+// something like "Generic / Postscript" and we can map
+// that to any print driver we want.
+// -------------------------------------------------------
+OSStatus
+CThirdPage::LoadGenericPrintDriverDefs( Manufacturers & manufacturers )
+{
+       Manufacturer            *       manufacturer;
+       Model                           *       model;
+       Manufacturers::iterator iter;
+       CString                                 psDriverName;
+       CString                                 pclDriverName;
+       OSStatus                                err     = 0;
+
+       // <rdar://problem/4030388> Generic drivers don't do color
+
+       // First try and find our generic driver names
+
+       iter = m_manufacturers.find(L"HP");
+       require_action( iter != m_manufacturers.end(), exit, err = kUnknownErr );
+       manufacturer = iter->second;
+
+       // Look for Postscript
+
+       model = manufacturer->find( kGenericPSColorDriver );
+
+       if ( !model )
+       {
+               model = manufacturer->find( kGenericPSDriver );
+       }
+
+       if ( model )
+       {
+               psDriverName = model->name;
+       }
+
+       // Look for PCL
+       
+       model = manufacturer->find( kGenericPCLColorDriver );
+
+       if ( !model )
+       {
+               model = manufacturer->find( kGenericPCLDriver );
+       }
+
+       if ( model )
+       {
+               pclDriverName = model->name;
+       }
+
+       // If we found either a generic PS driver, or a generic PCL driver,
+       // then add them to the list
+
+       if ( psDriverName.GetLength() || pclDriverName.GetLength() )
+       {
+               // Try and find generic manufacturer if there is one
+
+               iter = manufacturers.find(L"Generic");
+               
+               if (iter != manufacturers.end())
+               {
+                       manufacturer = iter->second;
+               }
+               else
+               {
+                       try
+                       {
+                               manufacturer = new Manufacturer;
+                       }
+                       catch (...)
+                       {
+                               manufacturer = NULL;
+                       }
+               
+                       require_action( manufacturer, exit, err = kNoMemoryErr );
+               
+                       manufacturer->name                                      =       "Generic";
+                       manufacturers[manufacturer->name]       =       manufacturer;
+               }
+
+               if ( psDriverName.GetLength() > 0 )
+               {
+                       try
+                       {
+                               m_genericPostscript = new Model;
+                       }
+                       catch (...)
+                       {
+                               m_genericPostscript = NULL;
+                       }
+                       
+                       require_action( m_genericPostscript, exit, err = kNoMemoryErr );
+
+                       m_genericPostscript->displayName                =       kGenericPostscript;
+                       m_genericPostscript->name                               =       psDriverName;
+                       m_genericPostscript->driverInstalled    =       false;
+
+                       manufacturer->models.push_back( m_genericPostscript );
+               }
+
+               if ( pclDriverName.GetLength() > 0 )
+               {
+                       try
+                       {
+                               m_genericPCL = new Model;
+                       }
+                       catch (...)
+                       {
+                               m_genericPCL = NULL;
+                       }
+                       
+                       require_action( m_genericPCL, exit, err = kNoMemoryErr );
+
+                       m_genericPCL->displayName               =       kGenericPCL;
+                       m_genericPCL->name                              =       pclDriverName;
+                       m_genericPCL->driverInstalled   =       false;
+
+                       manufacturer->models.push_back( m_genericPCL );
+               }
+       }
+
+exit:
+
+       return err;
+}
 
 // ------------------------------------------------------
 // ConvertToManufacturerName
 //
-// This function is responsible for tweaking the 
+// This function is responsible for tweaking the
 // name so that subsequent string operations won't fail because
 // of capitalizations/different names for the same manufacturer
 // (i.e.  Hewlett-Packard/HP/Hewlett Packard)
@@ -781,7 +853,6 @@ CThirdPage::ConvertToManufacturerName( const CString & name )
        return lower;
 }
 
-
 // ------------------------------------------------------
 // ConvertToModelName
 //
@@ -802,7 +873,6 @@ CThirdPage::ConvertToModelName( const CString & name )
        return lower;
 }
 
-
 // ------------------------------------------------------
 // NormalizeManufacturerName
 //
@@ -827,7 +897,6 @@ CThirdPage::NormalizeManufacturerName( const CString & name )
        return normalized;
 }
 
-
 // -------------------------------------------------------
 // MatchPrinter
 //
@@ -836,29 +905,38 @@ CThirdPage::NormalizeManufacturerName( const CString & name )
 // MatchManufacturer and MatchModel in turn.
 //
 
-OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * printer, Service * service)
+OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * printer, Service * service, bool useCUPSWorkaround)
 {
        CString                                 normalizedProductName;
-       Manufacturer            *       manufacturer    =       NULL;
-       Model                           *       model                   =       NULL;
-       bool                                    found                   =       false;
+       Manufacturer            *       manufacturer            =       NULL;
+       Manufacturer            *       genericManufacturer     =       NULL;
+       Model                           *       model                           =       NULL;
+       Model                           *       genericModel            =       NULL;
+       bool                                    found                           =       false;
        CString                                 text;
-       OSStatus                                err                             =       kNoErr;
+       OSStatus                                err                                     =       kNoErr;
+
+       check( printer );
+       check( service );
+
+       Queue * q = service->SelectedQueue();
+
+       check( q );
 
        //
        // first look to see if we have a usb_MFG descriptor
        //
-       if (service->usb_MFG.GetLength() > 0)
+       if ( q->usb_MFG.GetLength() > 0)
        {
-               manufacturer = MatchManufacturer( manufacturers, ConvertToManufacturerName ( service->usb_MFG ) );
+               manufacturer = MatchManufacturer( manufacturers, ConvertToManufacturerName ( q->usb_MFG ) );
        }
 
        if ( manufacturer == NULL )
        {
-               service->product.Remove('(');
-               service->product.Remove(')');
+               q->product.Remove('(');
+               q->product.Remove(')');
 
-               manufacturer = MatchManufacturer( manufacturers, ConvertToManufacturerName ( service->product ) );
+               manufacturer = MatchManufacturer( manufacturers, ConvertToManufacturerName ( q->product ) );
        }
        
        //
@@ -866,22 +944,57 @@ OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * print
        //
        if ( manufacturer != NULL )
        {
-               if (service->usb_MDL.GetLength() > 0)
+               if ( q->usb_MDL.GetLength() > 0 )
                {
-                       model = MatchModel ( manufacturer, ConvertToModelName ( service->usb_MDL ) );
+                       model = MatchModel ( manufacturer, ConvertToModelName ( q->usb_MDL ) );
                }
 
-               if ( model == NULL )
+               if ( ( model == NULL ) && ( q->product.GetLength() > 0 ) )
                {
-                       service->product.Remove('(');
-                       service->product.Remove(')');
+                       q->product.Remove('(');
+                       q->product.Remove(')');
 
-                       model = MatchModel ( manufacturer, ConvertToModelName ( service->product ) );
+                       model = MatchModel ( manufacturer, ConvertToModelName ( q->product ) );
                }
 
                if ( model != NULL )
                {
-                       SelectMatch(printer, service, manufacturer, model);
+                       // <rdar://problem/4124524> Offer Generic printers if printer advertises Postscript or PCL.  Workaround
+                       // bug in OS X CUPS printer sharing by selecting Generic driver instead of matched printer.
+                       bool hasGenericDriver = false;
+
+                       if ( MatchGeneric( manufacturers, printer, service, &genericManufacturer, &genericModel ) )
+                       {
+                               hasGenericDriver = true;
+                       }
+
+                       // <rdar://problem/4190104> Use "application/octet-stream" to determine if CUPS
+                       // shared queue supports raw
+
+                       if ( q->pdl.Find( L"application/octet-stream" ) != -1 )
+                       {
+                               useCUPSWorkaround = false;
+                       }
+
+                       if ( useCUPSWorkaround && printer->isCUPSPrinter && hasGenericDriver )
+                       {
+                               //
+                               // <rdar://problem/4496652> mDNS: Don't allow user to choose non-working driver
+                               //
+                               Manufacturers genericManufacturers;
+
+                               LoadGenericPrintDriverDefs( genericManufacturers );
+
+                               SelectMatch( genericManufacturers, printer, service, genericManufacturer, genericModel );
+
+                               FreeManufacturers( genericManufacturers );
+                       }
+                       else
+                       {
+                               SelectMatch(manufacturers, printer, service, manufacturer, model);
+                       }
+
                        found = true;
                }
        }
@@ -893,6 +1006,32 @@ OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * print
        if (found)
        {
                text.LoadString(IDS_PRINTER_MATCH_GOOD);
+               err = kNoErr;
+       }
+       else if ( MatchGeneric( manufacturers, printer, service, &genericManufacturer, &genericModel ) )
+       {
+               if ( printer->isCUPSPrinter )
+               {
+                       //
+                       // <rdar://problem/4496652> mDNS: Don't allow user to choose non-working driver
+                       //
+                       Manufacturers genericManufacturers;
+
+                       LoadGenericPrintDriverDefs( genericManufacturers );
+
+                       SelectMatch( genericManufacturers, printer, service, genericManufacturer, genericModel );
+                       
+                       text.LoadString(IDS_PRINTER_MATCH_GOOD);
+
+                       FreeManufacturers( genericManufacturers );
+               }
+               else
+               {
+                       SelectMatch( manufacturers, printer, service, genericManufacturer, genericModel );
+                       text.LoadString(IDS_PRINTER_MATCH_MAYBE);
+               }
+
+               err = kNoErr;
        }
        else
        {
@@ -922,9 +1061,15 @@ OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * print
                        if (nIndex != -1)
                        {
                                m_manufacturerListCtrl.SetItemState(nIndex, LVIS_SELECTED, LVIS_SELECTED);
-                               m_manufacturerListCtrl.EnsureVisible(nIndex, FALSE);
+
+                               //
+                               //<rdar://problem/4528853> mDNS: When auto-highlighting items in lists, scroll list so highlighted item is in the middle
+                               //
+                               AutoScroll(m_manufacturerListCtrl, nIndex);
                        }
                }
+
+               err = kUnknownErr;
        }
 
        m_printerSelectionText.SetWindowText(text);
@@ -932,7 +1077,6 @@ OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * print
        return err;
 }
 
-
 // ------------------------------------------------------
 // MatchManufacturer
 //
@@ -969,7 +1113,6 @@ CThirdPage::MatchManufacturer( Manufacturers & manufacturers, const CString & na
        return NULL;
 }
 
-
 // -------------------------------------------------------
 // MatchModel
 //
@@ -1020,7 +1163,50 @@ CThirdPage::MatchModel(Manufacturer * manufacturer, const CString & name)
        return NULL;
 }
 
+// -------------------------------------------------------
+// MatchGeneric
+//
+// This function will attempt to find a generic printer
+// driver for a printer that we weren't able to match
+// specifically
+//
+BOOL
+CThirdPage::MatchGeneric( Manufacturers & manufacturers, Printer * printer, Service * service, Manufacturer ** manufacturer, Model ** model )
+{
+       CString pdl;
+       BOOL    ok = FALSE;
+
+       DEBUG_UNUSED( printer );
+
+       check( service );
+
+       Queue * q = service->SelectedQueue();
+
+       check( q );
 
+       Manufacturers::iterator iter = manufacturers.find( kGenericManufacturer );
+       require_action_quiet( iter != manufacturers.end(), exit, ok = FALSE );
+
+       *manufacturer = iter->second;
+
+       pdl = q->pdl;
+       pdl.MakeLower();
+
+       if ( m_genericPCL && ( pdl.Find( kPDLPCLKey ) != -1 ) )
+       {
+               *model  = m_genericPCL;
+               ok              = TRUE;
+       }
+       else if ( m_genericPostscript && ( pdl.Find( kPDLPostscriptKey ) != -1 ) )
+       {
+               *model  = m_genericPostscript;
+               ok              = TRUE;
+       }
+
+exit:
+
+       return ok;
+}
 
 // -----------------------------------------------------------
 // OnInitPage
@@ -1031,18 +1217,14 @@ CThirdPage::MatchModel(Manufacturer * manufacturer, const CString & name)
 
 OSStatus CThirdPage::OnInitPage()
 {
-       static const int                bufferSize      = 32768;
-       TCHAR                                   windowsDirectory[bufferSize];
-       CString                                 header;
-       CString                                 ntPrint;
-       OSStatus                                err;
-       BOOL                                    ok;
-
-       // Load printer icon\r
-\r
+       CString         header;
+       CString         ntPrint;
+       OSStatus        err = kNoErr;
+
+       // Load printer icon
        check( m_printerImage == NULL );
        
-       m_printerImage = (CStatic*) GetDlgItem( IDR_MANIFEST );
+       m_printerImage = (CStatic*) GetDlgItem( 1 );    // 1 == IDR_MANIFEST
        check( m_printerImage );
 
        if ( m_printerImage != NULL )
@@ -1060,43 +1242,23 @@ OSStatus CThirdPage::OnInitPage()
        //
 
        //
-       // we have to make sure that we only do this once.  Typically, 
+       // we have to make sure that we only do this once.  Typically,
        // we would do this in something like OnInitDialog, but we don't
        // have this in Wizards, because the window is a PropertySheet.
        // We're considered fully initialized when we receive the first
        // selection notice
        //
        header.LoadString(IDS_MANUFACTURER_HEADING);
-       m_manufacturerListCtrl.InsertColumn(0, header, LVCFMT_LEFT, 138);
+       m_manufacturerListCtrl.InsertColumn(0, header, LVCFMT_LEFT, -1 );
        m_manufacturerSelected = NULL;
 
        header.LoadString(IDS_MODEL_HEADING);
-       m_modelListCtrl.InsertColumn(0, header, LVCFMT_LEFT, 247);
+       m_modelListCtrl.InsertColumn(0, header, LVCFMT_LEFT, -1 );
        m_modelSelected = NULL;
 
-       //
-       // load printers from ntprint.inf
-       //
-       ok = GetWindowsDirectory( windowsDirectory, bufferSize );
-       err = translate_errno( ok, errno_compat(), kUnknownErr );
-       require_noerr( err, exit );
-       ntPrint.Format(L"%s\\%s", windowsDirectory, kNTPrintFile);
-       err = LoadPrintDriverDefsFromFile( m_manufacturers, ntPrint, false );
-       require_noerr(err, exit);
-
-       //
-       // load printer drivers that have been installed on this machine
-       //
-       err = LoadPrintDriverDefs( m_manufacturers );
-       require_noerr(err, exit);
-
-exit:
-
        return (err);
 }
 
-
 void CThirdPage::DoDataExchange(CDataExchange* pDX)
 {
        CPropertyPage::DoDataExchange(pDX);
@@ -1104,9 +1266,9 @@ void CThirdPage::DoDataExchange(CDataExchange* pDX)
        DDX_Control(pDX, IDC_PRINTER_MODEL, m_modelListCtrl);
        DDX_Control(pDX, IDC_PRINTER_NAME, m_printerName);
        DDX_Control(pDX, IDC_DEFAULT_PRINTER, m_defaultPrinterCtrl);
-       DDX_Control(pDX, IDC_PRINTER_SELECTION_TEXT, m_printerSelectionText);\r
-}
+       DDX_Control(pDX, IDC_PRINTER_SELECTION_TEXT, m_printerSelectionText);
 
+}
 
 // ----------------------------------------------------------
 // OnSetActive
@@ -1125,15 +1287,7 @@ CThirdPage::OnSetActive()
        psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
        require_quiet( psheet, exit );
    
-       if ((m_manufacturerListCtrl.GetFirstSelectedItemPosition() != NULL) &&
-           (m_modelListCtrl.GetFirstSelectedItemPosition() != NULL))
-       {
-               psheet->SetWizardButtons( PSWIZB_BACK|PSWIZB_NEXT );
-       }
-       else
-       {
-               psheet->SetWizardButtons( PSWIZB_BACK );
-       }
+       psheet->SetWizardButtons( PSWIZB_BACK );
 
        printer = psheet->GetSelectedPrinter();
        require_quiet( printer, exit );
@@ -1150,6 +1304,20 @@ CThirdPage::OnSetActive()
                m_initialized = true;
        }
 
+       //
+       // <rdar://problem/4580061> mDNS: Printers added using Bonjour should be set as the default printer.
+       //
+       if ( DefaultPrinterExists() )
+       {
+               m_defaultPrinterCtrl.SetCheck( BST_UNCHECKED );
+               printer->deflt = false;
+       }
+       else
+       {
+               m_defaultPrinterCtrl.SetCheck( BST_CHECKED );
+               printer->deflt = true;
+       }
+
        //
        // update the UI with the printer name
        //
@@ -1164,13 +1332,40 @@ CThirdPage::OnSetActive()
        //
        // and try and match the printer
        //
-       MatchPrinter( m_manufacturers, printer, service );
+
+       if ( psheet->GetLastPage() == psheet->GetPage(0) )
+       {
+               MatchPrinter( m_manufacturers, printer, service, true );
+
+               if ( ( m_manufacturerSelected != NULL ) && ( m_modelSelected != NULL  ) )
+               {
+                       GetParent()->PostMessage(PSM_SETCURSEL, 2 );
+               }
+       }
+       else
+       {
+               SelectMatch(printer, service, m_manufacturerSelected, m_modelSelected);
+       }
 
 exit:
 
        return CPropertyPage::OnSetActive();
 }
 
+BOOL
+CThirdPage::OnKillActive()
+{
+       CPrinterSetupWizardSheet * psheet;
+
+       psheet = reinterpret_cast<CPrinterSetupWizardSheet*>(GetParent());
+       require_quiet( psheet, exit );
+   
+       psheet->SetLastPage(this);
+
+exit:
+
+       return CPropertyPage::OnKillActive();
+}
 
 // -------------------------------------------------------
 // PopulateUI
@@ -1193,12 +1388,13 @@ CThirdPage::PopulateUI(Manufacturers & manufacturers)
                nIndex = m_manufacturerListCtrl.InsertItem(0, manufacturer->name);
 
                m_manufacturerListCtrl.SetItemData(nIndex, (DWORD_PTR) manufacturer);
+
+               m_manufacturerListCtrl.SetColumnWidth( 0, LVSCW_AUTOSIZE_USEHEADER );
        }
 
        return 0;
 }
 
-
 BEGIN_MESSAGE_MAP(CThirdPage, CPropertyPage)
        ON_NOTIFY(LVN_ITEMCHANGED, IDC_PRINTER_MANUFACTURER, OnLvnItemchangedManufacturer)
        ON_NOTIFY(LVN_ITEMCHANGED, IDC_PRINTER_MODEL, OnLvnItemchangedPrinterModel)
@@ -1206,7 +1402,6 @@ BEGIN_MESSAGE_MAP(CThirdPage, CPropertyPage)
        ON_BN_CLICKED(IDC_HAVE_DISK, OnBnClickedHaveDisk)
 END_MESSAGE_MAP()
 
-
 // CThirdPage message handlers
 void CThirdPage::OnLvnItemchangedManufacturer(NMHDR *pNMHDR, LRESULT *pResult)
 {
@@ -1230,9 +1425,11 @@ void CThirdPage::OnLvnItemchangedManufacturer(NMHDR *pNMHDR, LRESULT *pResult)
                {
                        Model * model = *iter;
 
-                       int nItem = m_modelListCtrl.InsertItem(0, model->name);
+                       int nItem = m_modelListCtrl.InsertItem( 0, model->displayName );
 
                        m_modelListCtrl.SetItemData(nItem, (DWORD_PTR) model);
+
+                       m_modelListCtrl.SetColumnWidth( 0, LVSCW_AUTOSIZE_USEHEADER );
                }
 
                m_modelListCtrl.SetRedraw(TRUE);
@@ -1281,7 +1478,6 @@ exit:
        *pResult = 0;
 }
 
-
 void CThirdPage::OnBnClickedDefaultPrinter()
 {
        CPrinterSetupWizardSheet        *       psheet;
@@ -1293,7 +1489,7 @@ void CThirdPage::OnBnClickedDefaultPrinter()
        printer = psheet->GetSelectedPrinter();
        require_quiet( printer, exit );
 
-       printer->deflt = m_defaultPrinterCtrl.GetState() ? true : false;
+       printer->deflt = ( m_defaultPrinterCtrl.GetCheck() == BST_CHECKED ) ? true : false;
 
 exit:
 
@@ -1305,6 +1501,7 @@ void CThirdPage::OnBnClickedHaveDisk()
        CPrinterSetupWizardSheet        *       psheet;
        Printer                                         *       printer;
        Service                                         *       service;
+       Manufacturers                                   manufacturers;
 
        CFileDialog dlg(TRUE, NULL, NULL, OFN_HIDEREADONLY|OFN_FILEMUSTEXIST, L"Setup Information (*.inf)|*.inf||", this);
 
@@ -1317,19 +1514,70 @@ void CThirdPage::OnBnClickedHaveDisk()
        service = printer->services.front();
        require_quiet( service, exit );
 
-       if ( dlg.DoModal() == IDOK )
+       for ( ;; )
        {
-               Manufacturers   manufacturers;
-               CString                 filename = dlg.GetPathName();
+               if ( dlg.DoModal() == IDOK )
+               {
+                       CString filename = dlg.GetPathName();
 
-               LoadPrintDriverDefsFromFile( manufacturers, filename, true );
+                       LoadPrintDriverDefsFromFile( manufacturers, filename, true );
    
-               PopulateUI( manufacturers );
+                       // Sanity check
+
+                       if ( manufacturers.size() > 0 )
+                       {
+                               PopulateUI( manufacturers );
+
+                               if ( MatchPrinter( manufacturers, printer, service, false ) != kNoErr )
+                               {
+                                       CString errorMessage;
+                                       CString errorCaption;
+                                       
+                                       errorMessage.LoadString( IDS_NO_MATCH_INF_FILE );
+                                       errorCaption.LoadString( IDS_NO_MATCH_INF_FILE_CAPTION );
+
+                                       MessageBox( errorMessage, errorCaption, MB_OK );
+                               }
 
-               MatchPrinter( manufacturers, printer, service );
+                               break;
+                       }
+                       else
+                       {
+                               CString errorMessage;
+                               CString errorCaption;
+
+                               errorMessage.LoadString( IDS_BAD_INF_FILE );
+                               errorCaption.LoadString( IDS_BAD_INF_FILE_CAPTION );
+
+                               MessageBox( errorMessage, errorCaption, MB_OK );
+                       }
+               }
+               else
+               {
+                       break;
+               }
        }
 
 exit:
 
+       FreeManufacturers( manufacturers );
        return;
 }
+
+
+void
+CThirdPage::Split( const CString & string, TCHAR ch, CStringList & components )
+{
+       CString temp;
+       int             n;
+
+       temp = string;
+       
+       while ( ( n = temp.Find( ch ) ) != -1 )
+       {
+               components.AddTail( temp.Left( n ) );
+               temp = temp.Right( temp.GetLength() - ( n + 1 ) );
+       }
+
+       components.AddTail( temp );
+}