Rewrote EnumPrintersA, added CriticalSection around Registry reads and
implemented PRINTER_INFO_2 request.

diff --git a/misc/printdrv.c b/misc/printdrv.c
index 0d3f7a0..611a032 100644
--- a/misc/printdrv.c
+++ b/misc/printdrv.c
@@ -3,6 +3,7 @@
  * 
  * Copyright 1996 John Harvey
  * Copyright 1998 Andreas Mohr
+ * Copyright 1999 Klaas van Gend
  */
 
 #include <stdlib.h>
@@ -14,6 +15,8 @@
 #include "winreg.h"
 #include "debug.h"
 
+CRITICAL_SECTION PRINT32_RegistryBlocker;
+
 static char PrinterModel[]	= "Printer Model";
 static char DefaultDevMode[]	= "Default DevMode";
 static char PrinterDriverData[] = "PrinterDriverData";
@@ -402,6 +405,210 @@
 }
 
 
+
+
+/******************************************************************
+ *              ENUMPRINTERS_GetDWORDFromRegistryA    internal
+ *
+ * Reads a DWORD from registry KeyName 
+ *
+ * RETURNS
+ *    value on OK or NULL on error
+ */
+DWORD ENUMPRINTERS_GetDWORDFromRegistryA(
+				HKEY  hPrinterSettings,  /* handle to registry key */
+				LPSTR KeyName			 /* name key to retrieve string from*/
+){                   
+ DWORD DataSize=8;
+ DWORD DataType;
+ BYTE  Data[8];
+ DWORD Result=684;
+
+ if (RegQueryValueExA(hPrinterSettings, KeyName, NULL, &DataType,
+  					Data, &DataSize)!=ERROR_SUCCESS)
+	FIXME(print, "Query of register didn't succeed?");                     
+ if (DataType == REG_DWORD_LITTLE_ENDIAN)
+ 	Result = Data[0] + (Data[1]<<8) + (Data[2]<<16) + (Data[3]<<24);
+ if (DataType == REG_DWORD_BIG_ENDIAN)
+ 	Result = Data[3] + (Data[2]<<8) + (Data[1]<<16) + (Data[0]<<24);
+ return(Result);
+}
+
+
+/******************************************************************
+ *              ENUMPRINTERS_AddStringFromRegistryA    internal
+ *
+ * Reads a string from registry KeyName and writes it at
+ * lpbPrinters[dwNextStringPos]. Store reference to string in Dest.
+ *
+ * RETURNS
+ *    FALSE if there is still space left in the buffer.
+ */
+BOOL ENUMPRINTERS_AddStringFromRegistryA(
+				HKEY  hPrinterSettings,  /* handle to registry key */
+				LPSTR KeyName,			 /* name key to retrieve string from*/
+                LPSTR* Dest,		     /* pointer to write string addres to */
+                LPBYTE lpbPrinters,      /* buffer which receives info*/
+                LPDWORD dwNextStringPos, /* pos in buffer for next string */
+			    DWORD  dwBufSize,        /* max size of buffer in bytes */
+                BOOL   bCalcSpaceOnly    /* TRUE if out-of-space in buffer */
+){                   
+ DWORD DataSize=34;
+ DWORD DataType;
+ LPSTR Data = (LPSTR) malloc(DataSize*sizeof(char));
+
+ while(RegQueryValueExA(hPrinterSettings, KeyName, NULL, &DataType,
+  					Data, &DataSize)==ERROR_MORE_DATA)
+    {
+     Data = (LPSTR) realloc(Data, DataSize+2);
+    }
+
+ if (DataType == REG_SZ)
+ 	{                   
+	 *Dest = &lpbPrinters[*dwNextStringPos];
+	 *dwNextStringPos += DataSize+1;
+	 if (*dwNextStringPos > dwBufSize)
+	 	bCalcSpaceOnly=TRUE;
+	 if (bCalcSpaceOnly==FALSE)
+        {
+         if (DataSize==0)		/* DataSize = 0 means empty string, even though*/
+         	*Dest[0]=0;			/* the data itself needs not to be empty */
+         else
+	         strcpy(*Dest, Data);
+        }
+ 	}
+ else
+ 	WARN(print,"Expected string setting, got something else from registry");
+    
+ if (Data)
+    free(Data);
+ return(bCalcSpaceOnly);
+}
+
+
+
+/******************************************************************
+ *              ENUMPRINTERS_AddInfo2A        internal
+ *
+ *    Creates a PRINTER_INFO_2A structure at:  lpbPrinters[dwNextStructPos]
+ *    for printer PrinterNameKey.
+ *    Note that there is no check whether the information really fits!
+ *
+ * RETURNS
+ *    FALSE if there is still space left in the buffer.
+ *
+ * BUGS:
+ *    This function should not only read the registry but also ask the driver
+ *    for information.
+ */
+BOOL ENUMPRINTERS_AddInfo2A(
+                   LPSTR lpszPrinterName,/* name of printer to fill struct for*/
+                   LPBYTE lpbPrinters,   /* buffer which receives info*/
+                   DWORD  dwNextStructPos,  /* pos in buffer for struct */
+                   LPDWORD dwNextStringPos, /* pos in buffer for next string */
+				   DWORD  dwBufSize,        /* max size of buffer in bytes */
+                   BOOL   bCalcSpaceOnly    /* TRUE if out-of-space in buffer */
+){                   
+ HKEY  hPrinterSettings;
+ DWORD DevSize=0;
+ DWORD DataType;
+ LPSTR lpszPrinterSettings = (LPSTR) malloc(strlen(Printers)+
+  										   	   strlen(lpszPrinterName)+2);
+ LPPRINTER_INFO_2A lpPInfo2 = (LPPRINTER_INFO_2A) &lpbPrinters[dwNextStructPos];
+
+ /* open the registry to find the attributes, etc of the printer */
+ if (lpszPrinterSettings!=NULL)
+ 	{
+     strcpy(lpszPrinterSettings,Printers);
+     strcat(lpszPrinterSettings,lpszPrinterName);
+    }
+ if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, lpszPrinterSettings, 0, 
+ 						KEY_READ, &hPrinterSettings) != ERROR_SUCCESS)
+	{
+     WARN(print, "The registry did not contain my printer anymore?\n");
+    }
+ else
+    {
+     lpPInfo2->pServerName = NULL;
+     bCalcSpaceOnly = ENUMPRINTERS_AddStringFromRegistryA(hPrinterSettings, 
+     			                  "Name", &(lpPInfo2->pPrinterName), 
+                                  lpbPrinters, dwNextStringPos, 
+                                  dwBufSize, bCalcSpaceOnly);
+     bCalcSpaceOnly = ENUMPRINTERS_AddStringFromRegistryA(hPrinterSettings, 
+     			                  "Share Name", &(lpPInfo2->pShareName), 
+                                  lpbPrinters, dwNextStringPos, 
+                                  dwBufSize, bCalcSpaceOnly);
+     bCalcSpaceOnly = ENUMPRINTERS_AddStringFromRegistryA(hPrinterSettings, 
+     			                  "Port", &(lpPInfo2->pPortName), 
+                                  lpbPrinters, dwNextStringPos, 
+                                  dwBufSize, bCalcSpaceOnly);
+     bCalcSpaceOnly = ENUMPRINTERS_AddStringFromRegistryA(hPrinterSettings, 
+     			                  "Printer Driver", &(lpPInfo2->pDriverName), 
+                                  lpbPrinters, dwNextStringPos, 
+                                  dwBufSize, bCalcSpaceOnly);
+     bCalcSpaceOnly = ENUMPRINTERS_AddStringFromRegistryA(hPrinterSettings, 
+     			                  "Description", &(lpPInfo2->pComment), 
+                                  lpbPrinters, dwNextStringPos, 
+                                  dwBufSize, bCalcSpaceOnly);
+     bCalcSpaceOnly = ENUMPRINTERS_AddStringFromRegistryA(hPrinterSettings, 
+     			                  "Location", &(lpPInfo2->pLocation), 
+                                  lpbPrinters, dwNextStringPos, 
+                                  dwBufSize, bCalcSpaceOnly);
+
+     bCalcSpaceOnly = ENUMPRINTERS_AddStringFromRegistryA(hPrinterSettings, 
+     			                  "Separator File", &(lpPInfo2->pSepFile), 
+                                  lpbPrinters, dwNextStringPos, 
+                                  dwBufSize, bCalcSpaceOnly);
+     bCalcSpaceOnly = ENUMPRINTERS_AddStringFromRegistryA(hPrinterSettings, 
+     			                  "Print Processor", &(lpPInfo2->pPrintProcessor), 
+                                  lpbPrinters, dwNextStringPos, 
+                                  dwBufSize, bCalcSpaceOnly);
+     bCalcSpaceOnly = ENUMPRINTERS_AddStringFromRegistryA(hPrinterSettings, 
+     			                  "Datatype", &(lpPInfo2->pDatatype), 
+                                  lpbPrinters, dwNextStringPos, 
+                                  dwBufSize, bCalcSpaceOnly);
+     bCalcSpaceOnly = ENUMPRINTERS_AddStringFromRegistryA(hPrinterSettings, 
+     			                  "Parameters", &(lpPInfo2->pParameters), 
+                                  lpbPrinters, dwNextStringPos, 
+                                  dwBufSize, bCalcSpaceOnly);
+     lpPInfo2->pSecurityDescriptor = NULL; /* EnumPrinters doesn't return this*/
+
+     					  /* FIXME: Attributes gets LOCAL as no REMOTE exists*/
+	 lpPInfo2->Attributes = ENUMPRINTERS_GetDWORDFromRegistryA(hPrinterSettings,
+     								"Attributes") +PRINTER_ATTRIBUTE_LOCAL; 
+	 lpPInfo2->Priority   = ENUMPRINTERS_GetDWORDFromRegistryA(hPrinterSettings,
+     								"Priority"); 
+	 lpPInfo2->DefaultPriority = ENUMPRINTERS_GetDWORDFromRegistryA(
+     								hPrinterSettings, "Default Priority"); 
+	 lpPInfo2->StartTime  = ENUMPRINTERS_GetDWORDFromRegistryA(hPrinterSettings,
+     								"StartTime"); 
+	 lpPInfo2->UntilTime  = ENUMPRINTERS_GetDWORDFromRegistryA(hPrinterSettings,
+     								"UntilTime"); 
+	 lpPInfo2->Status     = ENUMPRINTERS_GetDWORDFromRegistryA(hPrinterSettings,
+     								"Status"); 
+	 lpPInfo2->cJobs      = 0;    /* FIXME: according to MSDN, this does not 
+     							   * reflect the TotalJobs Key ??? */
+	 lpPInfo2->AveragePPM = 0;    /* FIXME: according to MSDN, this does not 
+     							   * reflect the TotalPages Key ??? */
+
+     /* and read the devModes structure... */
+      RegQueryValueExA(hPrinterSettings, "pDevMode", NULL, &DataType,
+  					NULL, &DevSize); /* should return ERROR_MORE_DATA */
+      lpPInfo2->pDevMode = &lpbPrinters[*dwNextStringPos];
+      *dwNextStringPos += DevSize + 1;      
+ if (*dwNextStringPos > dwBufSize)
+ 	bCalcSpaceOnly=TRUE;
+ if (bCalcSpaceOnly==FALSE)
+	      RegQueryValueExA(hPrinterSettings, "pDevMode", NULL, &DataType,
+  					(LPBYTE)lpPInfo2->pDevMode, &DevSize); 
+	}                                 
+
+ if (lpszPrinterSettings)
+	 free(lpszPrinterSettings);
+
+ return(bCalcSpaceOnly);     
+}
+
 /******************************************************************
  *              ENUMPRINTERS_AddInfo4A        internal
  *
@@ -424,21 +631,9 @@
                    BOOL   bCalcSpaceOnly    /* TRUE if out-of-space in buffer */
 ){                   
  HKEY  hPrinterSettings;
- DWORD DataType;
- DWORD DataSize=8;
- char  Data[8];
- DWORD* DataPointer= (DWORD*) Data;
  LPSTR lpszPrinterSettings = (LPSTR) malloc(strlen(Printers)+
   										   	   strlen(lpszPrinterName)+2);
  LPPRINTER_INFO_4A lpPInfo4 = (LPPRINTER_INFO_4A) &lpbPrinters[dwNextStructPos];
-
- lpPInfo4->pPrinterName = &lpbPrinters[*dwNextStringPos];
- *dwNextStringPos += strlen(lpszPrinterName)+1;
- if (*dwNextStringPos > dwBufSize)
- 	bCalcSpaceOnly=TRUE;
- if (bCalcSpaceOnly==FALSE)
-     strcpy(lpPInfo4->pPrinterName, lpszPrinterName);
- lpPInfo4->pServerName = NULL;
  
  /* open the registry to find the attributes of the printer */
  if (lpszPrinterSettings!=NULL)
@@ -449,15 +644,17 @@
  if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, lpszPrinterSettings, 0, 
  						KEY_READ, &hPrinterSettings) != ERROR_SUCCESS)
 	{
-     FIXME(print, "The registry did not contain my printer anymore?\n");
-     lpPInfo4->Attributes = 0;
+     WARN(print, "The registry did not contain my printer anymore?\n");
     }
  else
     {
-	 RegQueryValueExA(hPrinterSettings, "Attributes", NULL, &DataType,
-  					Data, &DataSize);
-	 if (DataType==REG_DWORD) 
-  	 lpPInfo4->Attributes = PRINTER_ATTRIBUTE_LOCAL + *DataPointer; 
+     bCalcSpaceOnly = ENUMPRINTERS_AddStringFromRegistryA(hPrinterSettings, 
+     			                  "Name", &(lpPInfo4->pPrinterName), 
+                                  lpbPrinters, dwNextStringPos, 
+                                  dwBufSize, bCalcSpaceOnly);
+     					  /* FIXME: Attributes gets LOCAL as no REMOTE exists*/
+	 lpPInfo4->Attributes = ENUMPRINTERS_GetDWORDFromRegistryA(hPrinterSettings,
+     								"Attributes") +PRINTER_ATTRIBUTE_LOCAL; 
     }
  if (lpszPrinterSettings)
 	 free(lpszPrinterSettings);
@@ -484,22 +681,10 @@
                    BOOL   bCalcSpaceOnly    /* TRUE if out-of-space in buffer */
 ){                   
  HKEY  hPrinterSettings;
- DWORD DataType;
- DWORD DataSize=255;
- char  Data[255];
- DWORD* DataPointer= (DWORD*) Data;
  LPSTR lpszPrinterSettings = (LPSTR) malloc(strlen(Printers)+
   										   	   strlen(lpszPrinterName)+2);
  LPPRINTER_INFO_5A lpPInfo5 = (LPPRINTER_INFO_5A) &lpbPrinters[dwNextStructPos];
 
- /* copy the PrinterName into the structure */
- lpPInfo5->pPrinterName = &lpbPrinters[*dwNextStringPos];
- *dwNextStringPos += strlen(lpszPrinterName)+1;
- if (*dwNextStringPos > dwBufSize)
- 	bCalcSpaceOnly=TRUE;
- if (bCalcSpaceOnly==FALSE)
-     strcpy(lpPInfo5->pPrinterName, lpszPrinterName);
-
  /* open the registry to find the attributes, etc of the printer */
  if (lpszPrinterSettings!=NULL)
  	{
@@ -509,41 +694,26 @@
  if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, lpszPrinterSettings, 0, 
  						KEY_READ, &hPrinterSettings) != ERROR_SUCCESS)
 	{
-     FIXME(print, "The registry did not contain my printer anymore?\n");
-     lpPInfo5->Attributes = 0;
-     lpPInfo5->pPortName = NULL;
-     lpPInfo5->Attributes = 0;  
-     lpPInfo5->DeviceNotSelectedTimeOut =0;
-     lpPInfo5->TransmissionRetryTimeout = 0; 
+     WARN(print, "The registry did not contain my printer anymore?\n");
     }
  else
     {
-	 RegQueryValueExA(hPrinterSettings, "Attributes", NULL, &DataType,
-  					Data, &DataSize);
-	 if (DataType==REG_DWORD) 
-  	 	lpPInfo5->Attributes = PRINTER_ATTRIBUTE_LOCAL + *DataPointer; 
-
-     DataSize=255;
-	 RegQueryValueExA(hPrinterSettings, "txTimeout", NULL, &DataType,
-  					Data, &DataSize);
-	 if (DataType==REG_DWORD) 
- 		lpPInfo5->DeviceNotSelectedTimeOut = *DataPointer;
-
-     DataSize=255;
-	 RegQueryValueExA(hPrinterSettings, "dnsTimeout", NULL, &DataType,
-  					Data, &DataSize);
-	 if (DataType==REG_DWORD) 
-		 lpPInfo5->TransmissionRetryTimeout = *DataPointer; 
-         
-     DataSize=255;
-	 RegQueryValueExA(hPrinterSettings, "Port", NULL, &DataType,
-  					Data, &DataSize);
- 	 lpPInfo5->pPortName = &lpbPrinters[*dwNextStringPos];
-     *dwNextStringPos += DataSize+1;
- 	 if (*dwNextStringPos > dwBufSize)
-	 	bCalcSpaceOnly=TRUE;
-	 if (bCalcSpaceOnly==FALSE)
-    	 strcpy(lpPInfo5->pPortName, Data);         
+     bCalcSpaceOnly = ENUMPRINTERS_AddStringFromRegistryA(hPrinterSettings, 
+     			                  "Name", &(lpPInfo5->pPrinterName), 
+                                  lpbPrinters, dwNextStringPos, 
+                                  dwBufSize, bCalcSpaceOnly);
+     bCalcSpaceOnly = ENUMPRINTERS_AddStringFromRegistryA(hPrinterSettings, 
+     			                  "Port", &(lpPInfo5->pPortName), lpbPrinters,
+                                  dwNextStringPos, dwBufSize, bCalcSpaceOnly);
+     					  /* FIXME: Attributes gets LOCAL as no REMOTE exists*/
+	 lpPInfo5->Attributes = ENUMPRINTERS_GetDWORDFromRegistryA(hPrinterSettings,
+     								"Attributes") +PRINTER_ATTRIBUTE_LOCAL; 
+	 lpPInfo5->DeviceNotSelectedTimeOut 
+     					  = ENUMPRINTERS_GetDWORDFromRegistryA(hPrinterSettings,
+     								"txTimeout"); 
+	 lpPInfo5->TransmissionRetryTimeout
+     					  = ENUMPRINTERS_GetDWORDFromRegistryA(hPrinterSettings,
+     								"dnsTimeout"); 
     }
     
  if (lpszPrinterSettings)
@@ -562,12 +732,17 @@
  * RETURNS:
  *
  *    If level is set to 1:
- *    If level is set to 2:
  *      Not implemented yet! 
  *      Returns TRUE with an empty list.
  *
+ *    If level is set to 2:
+ *		Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
+ *      Returns an array of PRINTER_INFO_2 data structures in the 
+ *      lpbPrinters buffer. Note that according to MSDN also an 
+ *      OpenPrinter should be performed on every remote printer.
+ *
  *    If level is set to 4 (officially WinNT only):
- *		Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL).
+ *		Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
  *      Fast: Only the registry is queried to retrieve printer names,
  *      no connection to the driver is made.
  *      Returns an array of PRINTER_INFO_4 data structures in the 
@@ -587,9 +762,12 @@
  *
  * BUGS:
  *    - Only PRINTER_ENUM_LOCAL and PRINTER_ENUM_NAME are implemented.
- *    - Only level 4 and 5 are implemented at the moment.
+ *    - Only levels 2, 4 and 5 are implemented at the moment.
  *    - 16-bit printer drivers are not enumerated.
- *    - returned bytes used/needed do not match the real Windoze implementation
+ *    - Returned amount of bytes used/needed does not match the real Windoze 
+ *      implementation (as in this implementation, all strings are part 
+ *      of the buffer, whereas Win32 keeps them somewhere else)
+ *    - At level 2, EnumPrinters should also call OpenPrinter for remote printers.
  *
  * NOTE:
  *    - In a regular Wine installation, no registry settings for printers
@@ -616,30 +794,41 @@
  
  TRACE(print, "EnumPrintersA\n");
 
+ /* zero out the data area, and initialise some returns to zero,
+  * to prevent problems 
+  */
+ {
+  int i;
+  for (i=0; i<cbBuf; i++)
+  	  lpbPrinters[i]=0;
+ }
+ *lpdwReturned=0;
+ *lpdwNeeded = 0;
+
  /* check for valid Flags */
  if (dwType != PRINTER_ENUM_LOCAL && dwType != PRINTER_ENUM_NAME)
  	{
      SetLastError(ERROR_INVALID_FLAGS);
      return(0);
     }
- if (dwLevel == 1 || dwLevel == 2)
-     return(TRUE);
- if (dwLevel != 4 && dwLevel != 5)
+ switch(dwLevel)
  	{
+     case 1:
+	     return(TRUE);
+     case 2:
+     case 4:
+     case 5:
+     	 break;
+     default:
      SetLastError(ERROR_INVALID_PARAMETER);
-     return(0);
+	     return(FALSE);
     } 	
 
- /* zero out the data area, and initialise some returns to zero,
-  * to prevent problems 
+ /* Enter critical section to prevent AddPrinters() et al. to
+  * modify whilst we're reading in the registry
  */
-{
-  int i;
-  for (i=0; i<cbBuf; i++)
-  	  lpbPrinters[i]=0;
- }
-    *lpdwReturned=0;
-    *lpdwNeeded = 0;
+ InitializeCriticalSection(&PRINT32_RegistryBlocker);
+ EnterCriticalSection(&PRINT32_RegistryBlocker);
  
  /* get a pointer to a list of all printer names in the registry */ 
  if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, Printers, 0, KEY_READ,
@@ -649,7 +838,6 @@
       * (which is a valid configuration anyway)
       */
      TRACE(print, "No entries in the Printers part of the registry\n");
-     return(TRUE);
     }
 
  /* count the number of entries and check if it fits in the buffer
@@ -711,7 +899,9 @@
          	/* FIXME: unimplemented */
             break;
          case 2:
-            /* FIXME: unimplemented */
+            bCalcSpaceOnly = ENUMPRINTERS_AddInfo2A(PrinterName, lpbPrinters,
+            					dwIndex*dwStructPrinterInfoSize,
+                                &dwNextStringPos, cbBuf, bCalcSpaceOnly);
             break;
          case 4:
             bCalcSpaceOnly = ENUMPRINTERS_AddInfo4A(PrinterName, lpbPrinters,
@@ -735,7 +925,7 @@
 	  	  lpbPrinters[i]=0;
      *lpdwReturned=0;    
     } 
- 
+ LeaveCriticalSection(&PRINT32_RegistryBlocker); 
  return(TRUE);
 }