/****************************************************************************** * REXX OS/2 Printer Utility API (RXPRTUTL.DLL) * * (C) 2011 Alex Taylor * * * * LICENSE: * * * * Redistribution and use in source and binary forms, with or without * * modification, are permitted provided that the following conditions are * * met: * * * * 1. Redistributions of source code must retain the above copyright * * notice, this list of conditions and the following disclaimer. * * * * 2. Redistributions in binary form must reproduce the above copyright * * notice, this list of conditions and the following disclaimer in the * * documentation and/or other materials provided with the distribution. * * * * 3. The name of the author may not be used to endorse or promote products * * derived from this software without specific prior written permission. * * * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR * * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * * POSSIBILITY OF SUCH DAMAGE. * * * ******************************************************************************/ #define INCL_DEV #define INCL_DOSERRORS #define INCL_DOSMISC #define INCL_DOSMODULEMGR #define INCL_DOSPROFILE #define INCL_SPL #define INCL_SPLDOSPRINT #define INCL_SPLERRORS #define INCL_PM #define INCL_WIN #define INCL_WINWORKPLACE #ifndef OS2_INCLUDED #include #endif #include #include #include #include #define INCL_RXSHV #define INCL_RXFUNC #include // CONSTANTS #define SZ_LIBRARY_NAME "RXPRTUTL" // Name of this library #define SZ_ERROR_NAME "RPUERROR" // REXX variable used to store error codes #define SZ_VERSION "0.2.0" // Current version of this library #define APPNAME_LEAD_STR "PM_" #define APPNAME_PM_PORT_DRIVER "PM_PORT_DRIVER" #define APPNAME_PM_SPOOLER_PORT "PM_SPOOLER_PORT" #define APPNAME_PM_PRINT_OBJECT "PM_PrintObject" #define APPNAME_PM_WPS_LOCATION "PM_Workplace:Location" // Values that should be defined in pmsplb.h if it actually existed #define TYPE_LONG_WAIT 2 #define BIDI_SET_PORTDRV 0x19 // Values used by WinOpenObject #define OPEN_DEFAULT 0 #define OPEN_CONTENTS 1 #define OPEN_SETTINGS 2 #define OPEN_HELP 3 #define OPEN_TREE 101 #define OPEN_DETAILS 102 // Maximum string lengths... #define US_COMPOUND_MAXZ 250 // ...of a compound variable #define US_INTEGER_MAXZ 12 // ...of an integer string #define US_STEM_MAXZ ( US_COMPOUND_MAXZ - US_INTEGER_MAXZ ) // ...of a stem #define US_ERRSTR_MAXZ 250 // ...of an error string #define US_DRVINFO_MAXZ ( CCHMAXPATH + 8 + 32 ) // ...of an driver/port info string #define US_PRTINFO_MAXZ 180 // ...of a printer info string #define US_PORT_MAXZ 64 // ...of a port name #define US_PRTDEV_MAXZ 9 // ...of a print device name // List of functions to be registered by RPULoadFuncs static PSZ RxFunctionTbl[] = { "RPUDropFuncs", "RPUEnumModels", "RPUEnumDrivers", "RPUEnumPorts", "RPUEnumPrinters", "RPUQueueDefault", "RPUQueueHold", "RPUOpenView", "RPUPortDialog", "RPUPortInstall", "RPUPortSet", "RPUPrinterCreate", "RPUPrinterDelete", "RPUPrinterQuery", "RPUVersion" }; // FUNCTION DECLARATIONS // Exported REXX functions RexxFunctionHandler RPULoadFuncs; RexxFunctionHandler RPUDropFuncs; RexxFunctionHandler RPUVersion; RexxFunctionHandler RPUEnumModels; RexxFunctionHandler RPUEnumDrivers; RexxFunctionHandler RPUEnumPorts; RexxFunctionHandler RPUEnumPrinters; RexxFunctionHandler RPUOpenView; RexxFunctionHandler RPUPortDialog; RexxFunctionHandler RPUPortInstall; RexxFunctionHandler RPUPortSet; RexxFunctionHandler RPUPrinterCreate; RexxFunctionHandler RPUPrinterDelete; RexxFunctionHandler RPUPrinterQuery; RexxFunctionHandler RPUQueueDefault; RexxFunctionHandler RPUQueueHold; // TODO RexxFunctionHandler RPUPortDelete; // - function to delete a printer (object, device and queue) // Internal functions PSZ GetObjectID( PSZ pszHandle ); HOBJECT PrinterObjectHandle( PSZ pszQueueName ); BOOL SaveResultString( PRXSTRING prsResult, PCH pchBytes, ULONG ulBytes ); ULONG UniqueDeviceName( PSZ pszName ); BOOL WriteCompoundVariable( PSZ pszStem, PSZ pszTail, PSZ pszValue ); BOOL WriteStemElement( PSZ pszStem, ULONG ulIndex, PSZ pszValue ); void WriteErrorCode( ULONG ulError, PSZ pszContext ); /* ************************************************************************* * * EXPORTED REXX FUNCTIONS * * ************************************************************************* */ /* ------------------------------------------------------------------------- * * RPULoadFuncs * * * * Register all RPU* REXX functions except this one. * * * * REXX ARGUMENTS: None * * REXX RETURN VALUE: "" * * ------------------------------------------------------------------------- */ ULONG APIENTRY RPULoadFuncs( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { int entries, i; // Reset the error indicator WriteErrorCode( 0, NULL ); if ( argc > 0 ) return ( 40 ); entries = sizeof(RxFunctionTbl) / sizeof(PSZ); for ( i = 0; i < entries; i++ ) RexxRegisterFunctionDll( RxFunctionTbl[i], SZ_LIBRARY_NAME, RxFunctionTbl[i] ); MAKERXSTRING( *prsResult, "", 0 ); return ( 0 ); } /* ------------------------------------------------------------------------- * * RPUDropFuncs * * * * Deregister all RPU* REXX functions, including this one. * * * * REXX ARGUMENTS: None * * REXX RETURN VALUE: "" * * ------------------------------------------------------------------------- */ ULONG APIENTRY RPUDropFuncs( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { int entries, i; // Reset the error indicator WriteErrorCode( 0, NULL ); if ( argc > 0 ) return ( 40 ); entries = sizeof(RxFunctionTbl) / sizeof(PSZ); for ( i = 0; i < entries; i++ ) RexxDeregisterFunction( RxFunctionTbl[i] ); MAKERXSTRING( *prsResult, "", 0 ); return ( 0 ); } /* ------------------------------------------------------------------------- * * RPUVersion * * * * Returns the current library version. * * * * REXX ARGUMENTS: None * * REXX RETURN VALUE: Current version in the form "major.minor.refresh" * * ------------------------------------------------------------------------- */ ULONG APIENTRY RPUVersion( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { CHAR szVersion[ 12 ]; // Reset the error indicator WriteErrorCode( 0, NULL ); if ( argc > 0 ) return ( 40 ); sprintf( szVersion, "%s", SZ_VERSION ); MAKERXSTRING( *prsResult, szVersion, strlen(szVersion) ); return ( 0 ); } /* ------------------------------------------------------------------------- * * RPUEnumModels * * * * Gets a list of the printer models supported by the specified print * * driver. * * * * REXX ARGUMENTS: * * 1. Filespec of the printer driver to query. (REQUIRED) * * 2. The name of the stem in which to return the results. (REQUIRED) * * * * REXX RETURN VALUE: * * 1 on success, or 0 if an error occurred. * * ------------------------------------------------------------------------- */ ULONG APIENTRY RPUEnumModels( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { HAB hab; // desktop anchor-block handle PSZ pszDriver; // print driver filespec CHAR szStem[ US_STEM_MAXZ ], // buffers used for building stem szNumber[ US_INTEGER_MAXZ ]; // ... BOOL fSuccess; // success indicator PSTR32 aDeviceName; // array of device names PSTR64 aDeviceDesc; // array of device descriptions PSTR16 aDataType; // array of data types LONG pldn = 0L, // number of device names/descriptions pldt = 0L, // number of data types i; // Reset the error indicator WriteErrorCode( 0, NULL ); // Make sure we have exactly two valid arguments if (( argc != 2 ) || ( ! RXVALIDSTRING( argv[0] )) || ( ! RXVALIDSTRING( argv[1] )) || ( RXSTRLEN( argv[1] ) > US_STEM_MAXZ )) return ( 40 ); pszDriver = argv[0].strptr; if ( argv[1].strptr[ argv[1].strlength-1 ] == '.') argv[1].strlength--; strncpy( szStem, argv[1].strptr, RXSTRLEN( argv[1] )); szStem[ RXSTRLEN( argv[1] ) ] = '\0'; hab = WinInitialize( 0 ); if ( !hab ) { WriteErrorCode( 0, "WinInitialize"); MAKERXSTRING( *prsResult, "0", 1 ); return ( 0 ); } // Query the size of the available data fSuccess = DevQueryDeviceNames( hab, pszDriver, &pldn, NULL, NULL, &pldt, NULL ); if ( !fSuccess ) { WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "DevQueryDeviceNames"); MAKERXSTRING( *prsResult, "0", 1 ); goto cleanup; } // Now get the actual data aDeviceName = malloc( pldn * sizeof( STR32 )); aDeviceDesc = malloc( pldn * sizeof( STR64 )); aDataType = malloc( pldt * sizeof( STR16 )); if ( !aDeviceName || !aDeviceDesc || ! aDataType ) { WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc"); MAKERXSTRING( *prsResult, "0", 1 ); goto cleanup; } fSuccess = DevQueryDeviceNames( hab, pszDriver, &pldn, aDeviceName, aDeviceDesc, &pldt, aDataType ); for ( i = 0; i < pldn; i++ ) { WriteStemElement( szStem, i+1, (PSZ)aDeviceName[i] ); } sprintf( szNumber, "%u", pldn ); WriteStemElement( szStem, 0, szNumber ); // Return 1 on success MAKERXSTRING( *prsResult, "1", 1 ); free( aDeviceName ); free( aDeviceDesc ); free( aDataType ); cleanup: WinTerminate( hab ); return ( 0 ); } /* ------------------------------------------------------------------------- * * RPUEnumPorts * * * * Gets a list of the printer ports currently defined on the system. Returns * * a REXX array (stem) consisting of one string per port, of the form: * * * * * * REXX ARGUMENTS: * * 1. The name of the stem in which to return the results. (REQUIRED) * * * * REXX RETURN VALUE: * * 1 on success, or 0 if an error occurred. * * ------------------------------------------------------------------------- */ ULONG APIENTRY RPUEnumPorts( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { PPRPORTINFO1 pInfo; PVOID pbuf; ULONG cbBuf, cTotal, cReturned, cbNeeded, i; CHAR szStem[ US_STEM_MAXZ ], // buffers used for building stem szNumber[ US_INTEGER_MAXZ ], // ... szInfo[ US_DRVINFO_MAXZ ]; SPLERR rc; // Reset the error indicator WriteErrorCode( 0, NULL ); // Make sure we have exactly one valid argument (the stem name) if (( argc != 1 ) || ( ! RXVALIDSTRING( argv[0] ))) return ( 40 ); if ( argv[0].strptr[ argv[0].strlength-1 ] == '.') argv[0].strlength--; strncpy( szStem, argv[0].strptr, RXSTRLEN( argv[0] )); szStem[ RXSTRLEN( argv[0] ) ] = '\0'; // Query the amount of available data rc = SplEnumPort( NULL, 1, NULL, 0L, &cReturned, &cTotal, &cbNeeded, NULL ); if (( rc != ERROR_MORE_DATA ) && ( rc != NERR_BufTooSmall )) { WriteErrorCode( rc, "SplEnumPort"); MAKERXSTRING( *prsResult, "0", 1 ); return ( 0 ); } // Now get the actual data pbuf = malloc( cbNeeded ); if ( !pbuf ) { WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc"); MAKERXSTRING( *prsResult, "0", 1 ); return ( 0 ); } cbBuf = cbNeeded; rc = SplEnumPort( NULL, 1, pbuf, cbBuf, &cReturned, &cTotal, &cbNeeded, NULL ); if ( rc == NO_ERROR ) { pInfo = (PPRPORTINFO1) pbuf; for ( i = 0; i < cReturned; i++ ) { sprintf( szInfo, "%.32s %.8s %s", pInfo->pszPortName, pInfo->pszPortDriverName? pInfo->pszPortDriverName: "", pInfo->pszPortDriverPathName? pInfo->pszPortDriverPathName: ""); WriteStemElement( szStem, i+1, szInfo ); pInfo++; } sprintf( szNumber, "%u", cReturned ); WriteStemElement( szStem, 0, szNumber ); MAKERXSTRING( *prsResult, "1", 1 ); } else { WriteErrorCode( rc, "SplEnumPort"); MAKERXSTRING( *prsResult, "0", 1 ); } free( pbuf ); return ( 0 ); } /* ------------------------------------------------------------------------- * * RPUEnumDrivers * * * * Gets a list of the printer drivers currently installed on the system. * * Returns a REXX array (stem) with each item being a driver name string. * * * * REXX ARGUMENTS: * * 1. The name of the stem in which to return the results. (REQUIRED) * * * * REXX RETURN VALUE: * * 1 on success, or 0 if an error occurred. * * ------------------------------------------------------------------------- */ ULONG APIENTRY RPUEnumDrivers( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { PSZ pszDriverName; PBYTE pbuf; ULONG cbBuf, cTotal, cReturned, cbNeeded, i; CHAR szStem[ US_STEM_MAXZ ], // buffers used for building stem szNumber[ US_INTEGER_MAXZ ]; // ... SPLERR rc; // Reset the error indicator WriteErrorCode( 0, NULL ); // Make sure we have exactly one valid argument (the stem name) if (( argc != 1 ) || ( ! RXVALIDSTRING( argv[0] ))) return ( 40 ); if ( argv[0].strptr[ argv[0].strlength-1 ] == '.') argv[0].strlength--; strncpy( szStem, argv[0].strptr, RXSTRLEN( argv[0] )); szStem[ RXSTRLEN( argv[0] ) ] = '\0'; // Query the amount of available data rc = SplEnumDriver( NULL, 0L, NULL, 0L, &cReturned, &cTotal, &cbNeeded, NULL ); if (( rc != ERROR_MORE_DATA ) && ( rc != NERR_BufTooSmall )) { WriteErrorCode( rc, "SplEnumDriver"); MAKERXSTRING( *prsResult, "0", 1 ); return ( 0 ); } // Now get the actual data pbuf = (PBYTE) malloc( cbNeeded ); if ( !pbuf ) { WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc"); MAKERXSTRING( *prsResult, "0", 1 ); return ( 0 ); } cbBuf = cbNeeded; rc = SplEnumDriver( NULL, 0L, pbuf, cbBuf, &cReturned ,&cTotal, &cbNeeded, NULL ) ; if ( rc == NO_ERROR ) { pszDriverName = (PSZ) pbuf; for ( i = 0; i < cReturned ; i++ ) { WriteStemElement( szStem, i+1, pszDriverName ); pszDriverName += DRIV_NAME_SIZE + DRIV_DEVICENAME_SIZE + 2; } sprintf( szNumber, "%u", cReturned ); WriteStemElement( szStem, 0, szNumber ); MAKERXSTRING( *prsResult, "1", 1 ); } else { WriteErrorCode( rc, "SplEnumDriver"); MAKERXSTRING( *prsResult, "0", 1 ); } free( pbuf ); return ( 0 ); } /* ------------------------------------------------------------------------- * * RPUEnumPrinters * * * * Gets a list of the printers currently defined on the system. Returns a * * compound (stem) variable with the following format: * * * * (stem).0 Number of printers * * (stem).i.!name Printer device (a.k.a. physical) name * * (stem).i.!queue Printer queue name ('' for direct printers) * * (stem).i.!host Name of LAN host where printer queue is located * * ('' for non-LAN printers) * * (stem).i.!description Printer description (name of WPS object) * * (stem).i.!handle Printer's object handle ('' for direct printers) * * (stem).i.!flags Zero or more of the following flags (any order): * * D This is the default printer queue * * P Printer queue is paused (held) * * * * REXX ARGUMENTS: * * 1. The name of the stem in which to return the results. (REQUIRED) * * * * REXX RETURN VALUE: * * 1 on success, or 0 if an error occurred. * * ------------------------------------------------------------------------- */ ULONG APIENTRY RPUEnumPrinters( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { PPRINTERINFO pPInfo = NULL; // pointer to enumerated printer information PPRQINFO6 pQInfo = NULL; // queue information (level 6) PVOID pbEnum = NULL, // buffer for SplEnumPrinter pbQueue = NULL; // buffer for SplQueryQueue PSZ pszToken; HOBJECT hObj; // printer WPS object handle ULONG flType = SPL_PR_QUEUE | SPL_PR_DIRECT_DEVICE, cbBuf = 0, cTotal = 0, cReturned = 0, cbNeeded = 0, ulCount = 0, // number of printers found i; CHAR szFlags[ 3 ], // string of flag characters szHandle[ 9 ], // string for hex object handle // buffers used for building compound variables: szStem[ US_STEM_MAXZ - US_INTEGER_MAXZ - 1 ], szNumber[ US_INTEGER_MAXZ ], szStemNode[ US_STEM_MAXZ ]; SPLERR rc; // Reset the error indicator WriteErrorCode( 0, NULL ); // Make sure we have exactly one valid argument (the stem name) if (( argc != 1 ) || ( ! RXVALIDSTRING( argv[0] ))) return ( 40 ); if ( argv[0].strptr[ argv[0].strlength-1 ] == '.') argv[0].strlength--; strncpy( szStem, argv[0].strptr, RXSTRLEN( argv[0] )); szStem[ RXSTRLEN( argv[0] ) ] = '\0'; /* Our enumeration query asks for two types of devices: direct (non-queued) * printers, and printer queues. * * In the latter case, it's important to note that we ask for the queues * themselves rather than queued printers. Why? Because we'll need to * gather information about both printers and their underlying queues (some * things associated with printer objects actually belong to the queue * rather than the printer device). * * The thing is, given a printer name, there doesn't seem to be an easy way * to find out the queue associated with it. So we need to do it the other * way around: get the queue names, then we can identify the printers * connected to them. */ // Query the amount of available data rc = SplEnumPrinter( NULL, 0L, flType, NULL, 0L, &cReturned, &cTotal, &cbNeeded, NULL ); if (( rc != ERROR_MORE_DATA ) && ( rc != NERR_BufTooSmall )) // && ( rc != ERROR_INVALID_PARAMETER )) { WriteErrorCode( rc, "SplEnumPrinter"); MAKERXSTRING( *prsResult, "0", 1 ); return ( 0 ); } // Now get the actual data pbEnum = malloc( cbNeeded ); if ( !pbEnum ) { WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc"); MAKERXSTRING( *prsResult, "0", 1 ); return ( 0 ); } cbBuf = cbNeeded; rc = SplEnumPrinter( NULL, 0L, flType, pbEnum, cbBuf, &cReturned, &cTotal, &cbNeeded, NULL ); if ( rc != NO_ERROR ) { WriteErrorCode( rc, "SplEnumPrinter"); MAKERXSTRING( *prsResult, "0", 1 ); goto cleanup; } pPInfo = (PPRINTERINFO) pbEnum; for ( i = 0; i < cReturned; i++ ) { if ( pPInfo->flType == SPL_PR_DIRECT_DEVICE ) { /* Direct print device. No queue, no handle; flags do not apply. * The destination name and description are those of the printer * device itself, so we don't need any additional queries. */ sprintf( szStemNode, "%s.%u", szStem, ++ulCount ); WriteCompoundVariable( szStemNode, "!name", pPInfo->pszPrintDestinationName ); WriteCompoundVariable( szStemNode, "!description", pPInfo->pszDescription ); WriteCompoundVariable( szStemNode, "!queue", ""); WriteCompoundVariable( szStemNode, "!flags", ""); WriteCompoundVariable( szStemNode, "!handle", ""); WriteCompoundVariable( szStemNode, "!host", pPInfo->pszComputerName? pPInfo->pszComputerName: ""); } else if ( pPInfo->flType == SPL_PR_QUEUE ) { /* Print queue. We need to query the queue to find out the details * of the print device(s) connected to it. */ rc = SplQueryQueue( NULL, pPInfo->pszPrintDestinationName, 6L, NULL, 0L, &cbNeeded ); if (( rc != NO_ERROR ) && ( rc != NERR_BufTooSmall )) { WriteErrorCode( rc, "SplQueryQueue"); MAKERXSTRING( *prsResult, "0", 1 ); goto cleanup; } pbQueue = malloc( cbNeeded ); if ( !pbQueue ) { WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc"); MAKERXSTRING( *prsResult, "0", 1 ); goto cleanup; } cbBuf = cbNeeded; rc = SplQueryQueue( NULL, pPInfo->pszPrintDestinationName, 6L, pbQueue, cbBuf, &cbNeeded ); if ( rc != NO_ERROR ) { WriteErrorCode( rc, "SplQueryQueue"); MAKERXSTRING( *prsResult, "0", 1 ); free( pbQueue ); goto cleanup; } pQInfo = (PPRQINFO6) pbQueue; // Now go through the list of printers connected to this queue... pszToken = strtok( pQInfo->pszPrinters, ","); while ( pszToken ) { sprintf( szStemNode, "%s.%u", szStem, ++ulCount ); WriteCompoundVariable( szStemNode, "!name", pszToken ); WriteCompoundVariable( szStemNode, "!description", pQInfo->pszComment ); WriteCompoundVariable( szStemNode, "!queue", pQInfo->pszName ); WriteCompoundVariable( szStemNode, "!driver", pQInfo->pszDriverName ); WriteCompoundVariable( szStemNode, "!host", pQInfo->pszRemoteComputerName ? pQInfo->pszRemoteComputerName: ""); hObj = PrinterObjectHandle( pQInfo->pszName ); if ( hObj ) sprintf( szHandle, "%X", hObj ); else sprintf( szHandle, ""); WriteCompoundVariable( szStemNode, "!handle", szHandle ); memset( szFlags, 0, sizeof( szFlags )); if ( pQInfo->fsType & PRQ3_TYPE_APPDEFAULT ) strcat( szFlags, "D"); if ( pQInfo->fsStatus & PRQ3_PAUSED ) strcat( szFlags, "P"); WriteCompoundVariable( szStemNode, "!flags", szFlags ); pszToken = strtok( NULL, ","); } // while free( pbQueue ); } pPInfo++; } sprintf( szNumber, "%u", ulCount ); WriteStemElement( szStem, 0, szNumber ); MAKERXSTRING( *prsResult, "1", 1 ); cleanup: free( pbEnum ); return ( 0 ); } /* ------------------------------------------------------------------------- * * RPUOpenView * * * * Opens the requested view of the WPS object corresponding to the specified * * print queue. (Note that WPS printer objects are associated with a queue * * rather than a printer device, so it is the queue name that must be passed * * to this function.) * * * * REXX ARGUMENTS: * * 1. The name of the printer queue for the object to open. (REQUIRED) * * 2. Optional view flag, one of: * * O Object's default view (DEFAULT) * * D Details view * * I Icon view * * S Settings * * * * REXX RETURN VALUE: * * 1 on success, or 0 if an error occurred. * * ------------------------------------------------------------------------- */ ULONG APIENTRY RPUOpenView( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { HOBJECT hObj; ULONG ulView = OPEN_DEFAULT; BOOL fRC; // Reset the error indicator WriteErrorCode( 0, NULL ); // Make sure we have at least one valid argument (the queue name) if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 ); pszName = argv[0].strptr; // Second argument: view (optional, but must be correct if specified) if ( argc == 2 ) { if ( RXVALIDSTRING(argv[1]) ) { switch ( argv[1].strptr[0] ) { case 'o': case 'O': ulView = OPEN_DEFAULT; break; case 'd': case 'D': ulView = OPEN_DETAILS; break; case 'i': case 'I': ulView = OPEN_CONTENTS; break; case 's': case 'S': ulView = OPEN_SETTINGS; break; default : return ( 40 ); } } else return ( 40 ); } if ( argc > 2 ) return ( 40 ); hObj = PrinterObjectHandle( pszName ); if ( !hObj ) return ( 40 ); fRC = WinOpenObject( hObj, ulView, TRUE ); MAKERXSTRING( *prsResult, fRC? "1": "0", 1 ); return ( 0 ); } /* ------------------------------------------------------------------------- * * RPUPortInstall * * * * Creates a new printer port. * * * * REXX ARGUMENTS: * * 1. The name of the port driver without any extension. This must be * * installed and registered in OS2.INI already. (REQUIRED) * * 2. The name of the new port to be created. If not specified, the port * * driver will be responsible for using a default name. (DEFAULT: none) * * * * REXX RETURN VALUE: * * 1 on success, or 0 if an error occurred. * * ------------------------------------------------------------------------- */ ULONG APIENTRY RPUPortInstall( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { HAB hab; // desktop anchor-block handle HMODULE hPdr = NULLHANDLE; // handle to port driver module PFN pfnInstallPort; // pointer to driver's SplPdInstallPort entrypoint ULONG ulCB; // return value from PrfQueryProfileString() PSZ pszPdrName = NULL, // name of the specified port driver pszPortName = NULL; // name of the new port to create CHAR szPathName[ CCHMAXPATH+1 ]; // FQN of the port driver module APIRET rc = 0; // return code from Dos...() functions // Reset the error indicator WriteErrorCode( 0, NULL ); // Make sure we have at least one valid argument (the port driver name) if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 ); pszPdrName = argv[0].strptr; // Second argument: new port name (optional, but must be correct if specified) if ( argc >= 2 ) { if ( RXVALIDSTRING(argv[1]) ) pszPortName = strupr( argv[1].strptr ); else return ( 40 ); } // Get the path to the installed port driver hab = WinInitialize( 0 ); if ( !hab ) { WriteErrorCode( 0, "WinInitialize"); MAKERXSTRING( *prsResult, "0", 1 ); return ( 0 ); } ulCB = PrfQueryProfileString( HINI_SYSTEMPROFILE, APPNAME_PM_PORT_DRIVER, pszPdrName, NULL, (PVOID) szPathName, CCHMAXPATH ); if ( !ulCB ) { WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString"); MAKERXSTRING( *prsResult, "0", 1 ); goto cleanup; } // Load the port driver DLL and register its port install routine rc = DosLoadModule( NULL, 0, szPathName, &hPdr ); if ( rc != NO_ERROR || !hPdr ) { WriteErrorCode( rc, "DosLoadModule"); MAKERXSTRING( *prsResult, "0", 1 ); goto cleanup; } rc = DosQueryProcAddr( hPdr, 0, "SPLPDINSTALLPORT", &pfnInstallPort ); if ( rc != NO_ERROR ) { WriteErrorCode( rc, "DosQueryProcAddr"); MAKERXSTRING( *prsResult, "0", 1 ); goto finish; } // Now create the new port (the driver will assign it default settings) rc = pfnInstallPort( hab, pszPortName ); if ( rc == NO_ERROR ) { // Return 1 on success MAKERXSTRING( *prsResult, "1", 1 ); } else { WriteErrorCode( rc, "SplPdInstallPort"); MAKERXSTRING( *prsResult, "0", 1 ); } finish: DosFreeModule( hPdr ); cleanup: WinTerminate( hab ); return ( 0 ); } /* ------------------------------------------------------------------------- * * RPUPortSet * * * * Sets the specified port's configuration settings. IMPORTANT: not all * * port drivers support this; the standard OS/2 serial and parallel port * * drivers do NOT. Depending on the port driver, there are two possible * * outcomes when this API is not supported: * * A. Port driver does not have an entrypoint for SplPdSet. This is the * * case for any port driver using pre-Warp conventions. RPUPortSet will * * return 0 and RPUERROR will be "1: DosQueryProcAddr". * * B. Port driver supports SplPdSet but does not (correctly) implement the * * BIDI_SET_PORTDRV command. This is the case for some third-party port * * drivers. In this case, whether or not RPUPortSet returns a success * * code entirely depends on how conscientiously the port driver itself * * is written, but the "INITIALIZATION" key (and possibly others) in * * the port's entry in OS2SYS.INI will not be updated. The application * * should always check for this after calling RPUPortSet! * * If the application determines that either of these failure conditions has * * occurred, it is advisable to call RPUPortDialog as a fallback measure. * * * * Because of the above, use of this function is not generally encouraged * * except when absolutely necessary. * * * * REXX ARGUMENTS: * * 1. The name of the port driver without any extension. This must be * * installed and registered in OS2.INI already. (REQUIRED) * * 2. The name of the port to be configured. (REQUIRED) * * 3. Byte sequence representing the new port configuration. The format * * depends on the port driver (and it is up to the caller to know what * * it is). (REQUIRED) * * * * REXX RETURN VALUE: * * 1 on success, or 0 if an error occurred. * * ------------------------------------------------------------------------- */ ULONG APIENTRY RPUPortSet( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { HAB hab; // desktop anchor-block handle HMODULE hPdr = NULLHANDLE; // handle to port driver module PFN pfnSetPort; // pointer to driver's SplPdSet entrypoint ULONG ulCB, // return value from PrfQueryProfileString() cBuf; // size of configuration buffer PSZ pszPdrName = NULL, // name of the specified port driver pszPortName = NULL; // name of the new port to create CHAR szPathName[ CCHMAXPATH+1 ]; // FQN of the port driver module PBYTE pBuf; // Pointer to configuration buffer APIRET rc = 0; // return code from Dos...() functions // Reset the error indicator WriteErrorCode( 0, NULL ); // Make sure we have exactly three valid arguments if ( argc != 3 || !RXVALIDSTRING( argv[0] ) || !RXVALIDSTRING( argv[1] ) || !RXVALIDSTRING( argv[2] ) ) return ( 40 ); pszPdrName = argv[0].strptr; pszPortName = strupr( argv[1].strptr ); pBuf = argv[2].strptr; cBuf = argv[2].strlength; // Get the path to the installed port driver hab = WinInitialize( 0 ); if ( !hab ) { WriteErrorCode( 0, "WinInitialize"); MAKERXSTRING( *prsResult, "0", 1 ); return ( 0 ); } ulCB = PrfQueryProfileString( HINI_SYSTEMPROFILE, APPNAME_PM_PORT_DRIVER, pszPdrName, NULL, (PVOID) szPathName, CCHMAXPATH ); if ( !ulCB ) { WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString"); MAKERXSTRING( *prsResult, "0", 1 ); goto cleanup; } // Load the port driver DLL and register its configuration routine rc = DosLoadModule( NULL, 0, szPathName, &hPdr ); if ( rc != NO_ERROR || !hPdr ) { WriteErrorCode( rc, "DosLoadModule"); MAKERXSTRING( *prsResult, "0", 1 ); goto cleanup; } rc = DosQueryProcAddr( hPdr, 0, "SPLPDSET", &pfnSetPort ); if ( rc != NO_ERROR ) { WriteErrorCode( rc, "DosQueryProcAddr"); MAKERXSTRING( *prsResult, "0", 1 ); goto finish; } // Now set the new port configuration rc = pfnSetPort( pszPortName, TYPE_LONG_WAIT, BIDI_SET_PORTDRV, pBuf, cBuf ); if ( rc == NO_ERROR ) { // Return 1 on success MAKERXSTRING( *prsResult, "1", 1 ); } else { WriteErrorCode( rc, "SplPdSet"); MAKERXSTRING( *prsResult, "0", 1 ); } finish: DosFreeModule( hPdr ); cleanup: WinTerminate( hab ); return ( 0 ); } /* ------------------------------------------------------------------------- * * RPUPortDialog * * * * Brings up the specified port's configuration dialog. This function can * * only be used from a PM process. * * * * REXX ARGUMENTS: * * 1. The name of the port driver without any extension. This must be * * installed and registered in OS2.INI already. (REQUIRED) * * 2. The name of the port to be configured. (REQUIRED) * * * * REXX RETURN VALUE: * * 1 if the user modified the port configuration. * * 0 if no changes were made, or if an error occurred. * * ------------------------------------------------------------------------- */ ULONG APIENTRY RPUPortDialog( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { HAB hab; // desktop anchor-block handle HMODULE hPdr = NULLHANDLE; // handle to port driver module PFN pfnSetPort; // pointer to driver's SplPdSet entrypoint ULONG ulCB, // return value from PrfQueryProfileString() flModified = 0; // flag indicating settings were changed PSZ pszPdrName = NULL, // name of the specified port driver pszPortName = NULL; // name of the new port to create CHAR szPathName[ CCHMAXPATH+1 ]; // FQN of the port driver module APIRET rc = 0; // return code from Dos...() functions // Reset the error indicator WriteErrorCode( 0, NULL ); // Make sure we have exactly two valid arguments if ( argc != 2 || !RXVALIDSTRING( argv[0] ) || !RXVALIDSTRING( argv[1] ) ) return ( 40 ); pszPdrName = argv[0].strptr; pszPortName = strupr( argv[1].strptr ); // Get the path to the installed port driver hab = WinInitialize( 0 ); if ( !hab ) { WriteErrorCode( 0, "WinInitialize"); MAKERXSTRING( *prsResult, "0", 1 ); return ( 0 ); } ulCB = PrfQueryProfileString( HINI_SYSTEMPROFILE, APPNAME_PM_PORT_DRIVER, pszPdrName, NULL, (PVOID) szPathName, CCHMAXPATH ); if ( !ulCB ) { WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString"); MAKERXSTRING( *prsResult, "0", 1 ); goto cleanup; } // Load the port driver DLL and register its dialog routine rc = DosLoadModule( NULL, 0, szPathName, &hPdr ); if ( rc != NO_ERROR || !hPdr ) { WriteErrorCode( rc, "DosLoadModule"); MAKERXSTRING( *prsResult, "0", 1 ); goto cleanup; } rc = DosQueryProcAddr( hPdr, 0, "SPLPDSETPORT", &pfnSetPort ); if ( rc != NO_ERROR ) { WriteErrorCode( rc, "DosQueryProcAddr"); MAKERXSTRING( *prsResult, "0", 1 ); goto finish; } // Now show the port configuration dialog rc = pfnSetPort( hab, pszPortName, &flModified ); if ( rc == NO_ERROR ) { // Return 1 if settings were modified MAKERXSTRING( *prsResult, flModified? "1": "0", 1 ); } else { WriteErrorCode( rc, "SplPdSetPort"); MAKERXSTRING( *prsResult, "0", 1 ); } finish: DosFreeModule( hPdr ); cleanup: WinTerminate( hab ); return ( 0 ); } /* ------------------------------------------------------------------------- * * RPUPrinterCreate * * * * Creates a new local printer object. The associated print queue and * * device are created automatically. However, the specified output port * * must exist, and the specified printer driver/model must have been * * installed already. * * * * The WPS object is created with default settings, and will be assigned an * * object-ID automatically by the WPS. * * * * NOTE: This function will NOT create a remote (LAN) printer object. * * * * REXX ARGUMENTS: * * 1. The printer description, used as the WPS object title. (REQUIRED) * * 2. The name of the underlying print queue. Must be a legal * * queue name according to OS/2 (but what is that...?) (REQUIRED) * * 3. The name of the printer port, which must exist already. (REQUIRED) * * 4. The default printer driver.model to be associated with * * the device. If specified, this must be installed in the * * system already. If not specified, IBMNULL is assumed. (OPTIONAL) * * * * REXX RETURN VALUE: * * 1 on success, or 0 if an error occurred. * * ------------------------------------------------------------------------- */ ULONG APIENTRY RPUPrinterCreate( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { PRDINFO3 devinfo = {0}; PRQINFO3 qinfo = {0}; PSZ pszQueueName = NULL, pszPortName = NULL, pszModel = NULL, pszTitle = NULL; CHAR szDevice[ US_PRTDEV_MAXZ ] = {0}; SPLERR rc; // Reset the error indicator WriteErrorCode( 0, NULL ); // Validate the REXX arguments if (( argc < 3 ) || ( argc > 4 ) || ( ! RXVALIDSTRING( argv[0] )) || ( ! RXVALIDSTRING( argv[1] )) || ( ! RXVALIDSTRING( argv[2] ))) return ( 40 ); if (( argc > 3 ) && ( ! RXVALIDSTRING( argv[3] ))) return ( 40 ); pszTitle = argv[0].strptr; pszQueueName = argv[1].strptr; pszPortName = argv[2].strptr; if ( argc > 3 ) pszModel = argv[3].strptr; // Generate a suitable (unique) device name, based on the queue name strncpy( szDevice, pszQueueName, US_PRTDEV_MAXZ-1 ); if (( rc = UniqueDeviceName( szDevice )) != NO_ERROR ) { if ( rc == 1 ) // shouldn't really happen WriteErrorCode( rc, "UniqueDeviceName"); else WriteErrorCode( rc, "SplEnumDevice"); MAKERXSTRING( *prsResult, "0", 1 ); return ( 0 ); } // Create the device devinfo.pszPrinterName = szDevice; devinfo.pszUserName = NULL; devinfo.pszLogAddr = pszPortName; devinfo.pszComment = pszTitle; devinfo.pszDrivers = pszModel ? pszModel : "IBMNULL"; devinfo.usTimeOut = 45; rc = SplCreateDevice( NULL, 3, &devinfo, sizeof( devinfo )); if ( rc != NO_ERROR ) { WriteErrorCode( rc, "SplCreateDevice"); MAKERXSTRING( *prsResult, "0", 1 ); return ( 0 ); } // Create the queue (automatically creates printer object) qinfo.pszName = pszQueueName; qinfo.uPriority = PRQ_DEF_PRIORITY; qinfo.fsType = PRQ3_TYPE_RAW; qinfo.pszPrProc = "PMPRINT"; qinfo.pszComment = pszTitle; qinfo.pszPrinters = szDevice; qinfo.pszDriverName = pszModel ? pszModel : "IBMNULL"; rc = SplCreateQueue( NULL, 3, &qinfo, sizeof( qinfo )); if ( rc != NO_ERROR ) { WriteErrorCode( rc, "SplCreateQueue"); MAKERXSTRING( *prsResult, "0", 1 ); SplDeleteDevice( NULL, szDevice ); return ( 0 ); } MAKERXSTRING( *prsResult, "1", 1 ); return ( 0 ); } /* ------------------------------------------------------------------------- * * RPUPrinterDelete * * * * Deletes a local printer queue and its associated print device definition. * * * * REXX ARGUMENTS: * * 1. The name of the printer queue to be deleted. (REQUIRED) * * * * REXX RETURN VALUE: * * 1 on success, or 0 if an error occurred. (1 will be returned even if * * the WPS printer object could not be deleted, so long as the queue and * * device were dstroyed successfully.) * * ------------------------------------------------------------------------- */ ULONG APIENTRY RPUPrinterDelete( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { PPRQINFO3 pInfo = NULL; ULONG cbBuf = 0; PSZ pszQueueName = NULL, pszDeviceName = NULL; HOBJECT hObj; SPLERR rc; // Reset the error indicator WriteErrorCode( 0, NULL ); // Validate the REXX arguments if (( argc != 1 ) || ( ! RXVALIDSTRING( argv[0] ))) return ( 40 ); pszQueueName = argv[0].strptr; hObj = PrinterObjectHandle( pszQueueName ); rc = SplQueryQueue( NULL, pszQueueName, 3, NULL, 0, &cbBuf ); if (( rc != NO_ERROR ) && ( rc != NERR_BufTooSmall )) { WriteErrorCode( rc, "SplQueryQueue"); MAKERXSTRING( *prsResult, "0", 1 ); return ( 0 ); } pInfo = (PPRQINFO3) malloc( cbBuf ); if ( !pInfo ) { WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc"); MAKERXSTRING( *prsResult, "0", 1 ); return ( 0 ); } rc = SplQueryQueue( NULL, pszQueueName, 3, pInfo, cbBuf, &cbBuf ); if ( rc != NO_ERROR ) { WriteErrorCode( rc, "SplQueryQueue"); MAKERXSTRING( *prsResult, "0", 1 ); goto cleanup; } pszDeviceName = strtok( pInfo->pszPrinters, ","); rc = SplDeleteDevice( NULL, pszDeviceName ); if ( rc != NO_ERROR ) { WriteErrorCode( rc, "SplDeleteDevice"); MAKERXSTRING( *prsResult, "0", 1 ); goto cleanup; } // Try and destroy the WPS object if ( hObj != NULLHANDLE ) WinDestroyObject( hObj ); MAKERXSTRING( *prsResult, "1", 1 ); cleanup: free( pInfo ); return ( 0 ); } /* ------------------------------------------------------------------------- * * RPUPrinterQuery * * * * Gets information about the specified printer device. * * * * (stem).i.!description Printer description (name of WPS object) * * (stem).i.!port Name of the port the printer is using * * (stem).i.!driver List of the drivers used by this printer * * TODO: * * (stem).i.!flags Zero or more of the following flags (any order): * * E A printer error has occurred * * H Printer destination is paused * * I Intervention required * * N Printer has raised a notification alert * * O Printer is offline * * P Printer is out of paper * * X Printer is not processing/paused * * * * REXX ARGUMENTS: * * 1. The name of the printer device being queried. (REQUIRED) * * 2. The name of the stem in which to return the results. (REQUIRED) * * * * REXX RETURN VALUE: * * 1 on success, or 0 if an error occurred. * * ------------------------------------------------------------------------- */ ULONG APIENTRY RPUPrinterQuery( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { PPRDINFO3 pInfo = NULL; PVOID pbuf = NULL; ULONG cbBuf = 0, cbNeeded = 0; PSZ pszDeviceName; CHAR szStem[ US_STEM_MAXZ ]; // compound variable stem name SPLERR rc; // Reset the error indicator WriteErrorCode( 0, NULL ); // Validate the REXX arguments if (( argc != 2 ) || ( ! RXVALIDSTRING( argv[0] )) || ( ! RXVALIDSTRING( argv[1] ))) return ( 40 ); pszDeviceName = argv[0].strptr; //printf("Device: \"%s\"\n", pszDeviceName ); // Initialize the result stem name if ( RXSTRLEN(argv[1]) > US_STEM_MAXZ ) return ( 40 ); if ( argv[1].strptr[ argv[1].strlength-1 ] == '.') argv[1].strlength--; strncpy( szStem, argv[1].strptr, RXSTRLEN( argv[1] )); szStem[ RXSTRLEN( argv[1] ) ] = '\0'; // Query the amount of available data rc = SplQueryDevice( NULL, pszDeviceName, 3L, NULL, 0L, &cbNeeded ); if (( rc != ERROR_MORE_DATA ) && ( rc != NERR_BufTooSmall )) { WriteErrorCode( rc, "SplQueryDevice"); MAKERXSTRING( *prsResult, "0", 1 ); return ( 0 ); } // Now get the actual data pbuf = malloc( cbNeeded ); if ( !pbuf ) { WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc"); MAKERXSTRING( *prsResult, "0", 1 ); return ( 0 ); } cbBuf = cbNeeded; rc = SplQueryDevice( NULL, pszDeviceName, 3L, pbuf, cbBuf, &cbNeeded ); if ( rc == NO_ERROR ) { pInfo = (PPRDINFO3) pbuf; WriteCompoundVariable( szStem, "!name", pInfo->pszPrinterName ); WriteCompoundVariable( szStem, "!port", pInfo->pszLogAddr ); WriteCompoundVariable( szStem, "!description", pInfo->pszComment ); WriteCompoundVariable( szStem, "!drivers", pInfo->pszDrivers ); MAKERXSTRING( *prsResult, "1", 1 ); } else { WriteErrorCode( rc, "SplQueryDevice"); MAKERXSTRING( *prsResult, "0", 1 ); } free( pbuf ); return ( 0 ); } /* ------------------------------------------------------------------------- * * RPUQueueDefault * * * * Sets the requested printer queue as the system default printer. * * * * REXX ARGUMENTS: * * 1. The name of the printer queue to set as default. (REQUIRED) * * * * REXX RETURN VALUE: * * 1 on success, or 0 if an error occurred. * * ------------------------------------------------------------------------- */ ULONG APIENTRY RPUQueueDefault( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { HOBJECT hObj; BOOL fRC; // Reset the error indicator WriteErrorCode( 0, NULL ); // Make sure we have at least one valid argument (the queue name) if ( argc != 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 ); pszName = argv[0].strptr; hObj = PrinterObjectHandle( pszName ); if ( !hObj ) return ( 40 ); fRC = WinSetObjectData( hObj, "APPDEFAULT=YES;"); MAKERXSTRING( *prsResult, fRC? "1": "0", 1 ); return ( 0 ); } /* ------------------------------------------------------------------------- * * RPUQueueHold * * * * Holds or releases the specified print queue. * * * * REXX ARGUMENTS: * * 1. The name of the printer queue to hold or release. (REQUIRED) * * 2. Action flag, one of: * * Y Hold printer (DEFAULT) * * N Release printer * * * * REXX RETURN VALUE: * * 1 on success, or 0 if an error occurred. * * ------------------------------------------------------------------------- */ ULONG APIENTRY RPUQueueHold( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { PSZ pszQueueName; // Print queue name BOOL fHold = TRUE; SPLERR rc; // Reset the error indicator WriteErrorCode( 0, NULL ); // Make sure we have at least one valid argument (the queue name) if ( argc < 1 || argc > 2 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 ); pszQueueName = argv[0].strptr; // Second argument: view (optional, but must be correct if specified) if ( argc == 2 ) { if ( RXVALIDSTRING(argv[1]) ) { switch ( argv[1].strptr[0] ) { case 'y': case 'Y': fHold = TRUE; break; case 'n': case 'N': fHold = FALSE; break; default : return ( 40 ); } } else return ( 40 ); } rc = fHold ? SplHoldQueue( NULL, pszQueueName ) : SplReleaseQueue( NULL, pszQueueName ); if ( rc != NO_ERROR ) { WriteErrorCode( rc, fHold ? "SplHoldQueue" : "SplReleaseQueue"); MAKERXSTRING( *prsResult, "0", 1 ); } else MAKERXSTRING( *prsResult, "1", 1 ); return ( 0 ); } /* ************************************************************************* * * INTERNAL PRINTER-RELATED FUNCTIONS * * ************************************************************************* */ /* ------------------------------------------------------------------------- * * PrinterObjectHandle * * * * Given a print queue name, obtain the handle to the WPS printer object * * associated with that queue. This is done by querying OS2.INI for the * * queues defined in PM_PrintObject and reading the associated handle IDs. * * * * ARGUMENTS: * * PSZ pszQueueName: The printer queue name. This is NOT the same as the * * printer device name, although in many cases they may * * have the same value. * * * * RETURNS: HOBJECT * * WPS object handle of the printer object; NULLHANDLE if no object was * * found or an error occurred. * * ------------------------------------------------------------------------- */ HOBJECT PrinterObjectHandle( PSZ pszQueueName ) { PVOID pbuf = NULL; PSZ psz; CHAR szValue[ 256 ]; ULONG cbTotal = 0, cbActual = 0; HOBJECT hObj = 0; BOOL fRC; fRC = PrfQueryProfileSize( HINI_USERPROFILE, APPNAME_PM_PRINT_OBJECT, NULL, &cbTotal ); if ( !fRC || !cbTotal ) { //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileSize"); return NULLHANDLE; } pbuf = malloc( cbTotal ); if ( !pbuf ) { WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc"); return NULLHANDLE; } cbActual = PrfQueryProfileString( HINI_USERPROFILE, APPNAME_PM_PRINT_OBJECT, NULL, NULL, pbuf, cbTotal ); if ( !cbActual ) { //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString"); return NULLHANDLE; } psz = (PSZ) pbuf; while ( *psz ) { if ( !PrfQueryProfileString( HINI_USERPROFILE, APPNAME_PM_PRINT_OBJECT, psz, NULL, szValue, 255 )) { //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString"); break; } if (( strcmp( szValue, pszQueueName ) == 0 ) && ( sscanf( psz, "%u", (PULONG) &hObj ) == 1 )) { break; } psz += strlen( psz ) + 1; } free ( pbuf ); return ( hObj ); } /* ------------------------------------------------------------------------- * * GetObjectID * * * * Given a WPS object handle, find out the corresponding object ID if one * * exists. * * * * ARGUMENTS: * * PSZ pszHandle: The WPS object handle, as a hexadecimal string. This * * should have no prefix, and be all upper-case. * * * * RETURNS: PSZ * * The object ID string. It is up to the caller to free it when done. * * This will be NULL if no object ID was found. * * ------------------------------------------------------------------------- */ PSZ GetObjectID( PSZ pszHandle ) { PVOID pbuf = NULL; ULONG cbTotal = 0, cbActual = 0; CHAR szValue[ 9 ]; PSZ psz, pszObjID = NULL; BOOL fRC; fRC = PrfQueryProfileSize( HINI_USERPROFILE, APPNAME_PM_WPS_LOCATION, NULL, &cbTotal ); if ( !fRC || !cbTotal ) { //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileSize"); return NULL; } pbuf = malloc( cbTotal ); if ( !pbuf ) { WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc"); return NULL; } cbActual = PrfQueryProfileString( HINI_USERPROFILE, APPNAME_PM_WPS_LOCATION, NULL, NULL, pbuf, cbTotal ); if ( !cbActual ) { //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString"); return NULL; } psz = (PSZ) pbuf; while ( *psz ) { if ( !PrfQueryProfileString( HINI_USERPROFILE, APPNAME_PM_WPS_LOCATION, psz, NULL, szValue, 8 )) { //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString"); break; } if (( strcmp( szValue, pszHandle ) == 0 ) && (( pszObjID = strdup( psz )) != NULL )) { break; } psz += strlen( psz ) + 1; } free ( pbuf ); return ( pszObjID ); } /* ------------------------------------------------------------------------- * * UniqueDeviceName * * * * Check (and, if necessary, modify) the specified print device name to make * * sure it is unique. * * * * ARGUMENTS: * * PSZ pszName : Pointer to device name buffer (9-byte CHAR buffer) * * * * RETURNS: ULONG * * 0 on success, 1 if no unique name could be generated, or the return * * code from SplEnumDevice() if an error occurred. * * ------------------------------------------------------------------------- */ ULONG UniqueDeviceName( PSZ pszName ) { PBYTE pBuf; PPRDINFO3 pprd3; CHAR szNumber[ US_PRTDEV_MAXZ ] = {0}; ULONG i, n, pos, ulNumber = 0, ulAvail = 0, cbBuf = 0; BOOL fUnique = FALSE; SPLERR rc; rc = SplEnumDevice( NULL, 3, NULL, 0, &ulNumber, &ulAvail, &cbBuf, NULL ); if ( rc == ERROR_MORE_DATA || rc == NERR_BufTooSmall ) { pBuf = malloc( cbBuf ); if ( pBuf ) { rc = SplEnumDevice( NULL, 3, pBuf, cbBuf, &ulNumber, &ulAvail, &cbBuf, NULL ); if ( rc == NO_ERROR ) { n = 1; while ( !fUnique && ( n < 999 )) { // max 999 as a sanity check for ( i = 0; i < ulNumber; i++ ) { pprd3 = (PPRDINFO3) pBuf + i; if ( stricmp( pszName, pprd3->pszPrinterName ) == 0 ) break; } if ( i >= ulNumber ) fUnique = TRUE; else { sprintf( szNumber, "%u", n++ ); pos = strlen( pszName ) - strlen( szNumber ); pszName[ pos ] = '\0'; strncat( pszName, szNumber, US_PRTDEV_MAXZ-1 ); } } } free( pBuf ); } } if ( rc == NO_ERROR && !fUnique ) return 1; else return rc; } /* ************************************************************************* * * INTERNAL REXX DLL UTILITY FUNCTIONS * * ************************************************************************* */ /* ------------------------------------------------------------------------- * * SaveResultString * * * * Writes new string contents to the specified RXSTRING, allocating any * * additional memory that may be required. If the string to be written has * * zero length, nothing is done. * * * * This function should be used in place of MAKERXSTRING if there is a * * possibility that the string contents could be longer than 256 characters. * * * * ARGUMENTS: * * PRXSTRING prsResult: Pointer to an existing RXSTRING for writing. * * PCH pchBytes : The string contents to write to prsResult. * * ULONG ulBytes : The number of bytes in pchBytes to write. * * * * RETURNS: BOOL * * TRUE if prsResult was successfully updated. FALSE otherwise. * * ------------------------------------------------------------------------- */ BOOL SaveResultString( PRXSTRING prsResult, PCH pchBytes, ULONG ulBytes ) { ULONG ulRC; PCH pchNew; if ( ulBytes == 0 ) return ( FALSE ); if ( ulBytes > 256 ) { // REXX provides 256 bytes by default; allocate more if necessary ulRC = DosAllocMem( (PVOID) &pchNew, ulBytes, PAG_WRITE | PAG_COMMIT ); if ( ulRC != 0 ) { WriteErrorCode( ulRC, "DosAllocMem"); return ( FALSE ); } DosFreeMem( prsResult->strptr ); prsResult->strptr = pchNew; } memcpy( prsResult->strptr, pchBytes, ulBytes ); prsResult->strlength = ulBytes; return ( TRUE ); } /* ------------------------------------------------------------------------- * * WriteStemElement * * * * Creates a stem element (compound variable) in the calling REXX program * * using the REXX shared variable pool interface. * * * * ARGUMENTS: * * PSZ pszStem : The name of the stem (before the '.') * * ULONG ulIndex : The number of the stem element (after the '.') * * PSZ pszValue : The value to write to the compound variable. * * * * RETURNS: BOOL * * TRUE on success, FALSE on failure. * * ------------------------------------------------------------------------- */ BOOL WriteStemElement( PSZ pszStem, ULONG ulIndex, PSZ pszValue ) { SHVBLOCK shvVar; // REXX shared variable pool block ULONG ulRc, ulBytes; CHAR szCompoundName[ US_COMPOUND_MAXZ ], *pchValue; sprintf( szCompoundName, "%s.%d", pszStem, ulIndex ); if ( pszValue == NULL ) { pchValue = ""; ulBytes = 0; } else { ulBytes = strlen( pszValue ); ulRc = DosAllocMem( (PVOID) &pchValue, ulBytes + 1, PAG_WRITE | PAG_COMMIT ); if ( ulRc != 0 ) { WriteErrorCode( ulRc, "DosAllocMem"); return FALSE; } memcpy( pchValue, pszValue, ulBytes ); } MAKERXSTRING( shvVar.shvname, szCompoundName, strlen(szCompoundName) ); shvVar.shvvalue.strptr = pchValue; shvVar.shvvalue.strlength = ulBytes; shvVar.shvnamelen = RXSTRLEN( shvVar.shvname ); shvVar.shvvaluelen = RXSTRLEN( shvVar.shvvalue ); shvVar.shvcode = RXSHV_SYSET; shvVar.shvnext = NULL; ulRc = RexxVariablePool( &shvVar ); if ( ulRc > 1 ) { WriteErrorCode( shvVar.shvret, "RexxVariablePool (SHVBLOCK.shvret)"); return FALSE; } return TRUE; } /* ------------------------------------------------------------------------- * * WriteCompoundVariable * * * * Creates a compound variable in the calling REXX program using the REXX * * shared variable pool interface. * * * * ARGUMENTS: * * PSZ pszStem : The name of the stem (before the '.') * * PSZ pszTail : The name of the trailing portion (after the '.') * * PSZ pszValue : The value to write to the compound variable. * * * * RETURNS: BOOL * * TRUE on success, FALSE on failure. * * ------------------------------------------------------------------------- */ BOOL WriteCompoundVariable( PSZ pszStem, PSZ pszTail, PSZ pszValue ) { SHVBLOCK shvVar; // REXX shared variable pool block ULONG ulRc, ulBytes; CHAR szCompoundName[ US_COMPOUND_MAXZ ], *pchValue; sprintf( szCompoundName, "%s.%s", pszStem, pszTail ); if ( pszValue == NULL ) { pchValue = ""; ulBytes = 0; } else { ulBytes = strlen( pszValue ); ulRc = DosAllocMem( (PVOID) &pchValue, ulBytes + 1, PAG_WRITE | PAG_COMMIT ); if ( ulRc != 0 ) { WriteErrorCode( ulRc, "DosAllocMem"); return FALSE; } memcpy( pchValue, pszValue, ulBytes ); } MAKERXSTRING( shvVar.shvname, szCompoundName, strlen(szCompoundName) ); shvVar.shvvalue.strptr = pchValue; shvVar.shvvalue.strlength = ulBytes; shvVar.shvnamelen = RXSTRLEN( shvVar.shvname ); shvVar.shvvaluelen = RXSTRLEN( shvVar.shvvalue ); shvVar.shvcode = RXSHV_SYSET; shvVar.shvnext = NULL; ulRc = RexxVariablePool( &shvVar ); if ( ulRc > 1 ) { WriteErrorCode( shvVar.shvret, "RexxVariablePool (SHVBLOCK.shvret)"); return FALSE; } return TRUE; } /* ------------------------------------------------------------------------- * * WriteErrorCode * * * * Writes an error code to a special variable in the calling REXX program * * using the REXX shared variable pool interface. This is used to return * * API error codes to the REXX program, since the REXX functions themselves * * normally return string values. * * * * ARGUMENTS: * * ULONG ulError : The error code returned by the failing API call. * * PSZ pszContext: A string describing the API call that failed. * * * * RETURNS: N/A * * ------------------------------------------------------------------------- */ void WriteErrorCode( ULONG ulError, PSZ pszContext ) { SHVBLOCK shvVar; // REXX shared variable pool block ULONG ulRc; CHAR szErrorText[ US_ERRSTR_MAXZ ]; if ( pszContext == NULL ) sprintf( szErrorText, "%X", ulError ); else sprintf( szErrorText, "%X: %s", ulError, pszContext ); MAKERXSTRING( shvVar.shvname, SZ_ERROR_NAME, strlen(SZ_ERROR_NAME) ); MAKERXSTRING( shvVar.shvvalue, szErrorText, strlen(szErrorText) ); shvVar.shvnamelen = RXSTRLEN( shvVar.shvname ); shvVar.shvvaluelen = RXSTRLEN( shvVar.shvvalue ); shvVar.shvcode = RXSHV_SYSET; shvVar.shvnext = NULL; ulRc = RexxVariablePool( &shvVar ); if ( ulRc > 1 ) printf("Unable to set %s: rc = %d\n", shvVar.shvname.strptr, shvVar.shvret ); }