source: rxprtutl/trunk/rxprtutl.c

Last change on this file was 38, checked in by Alex Taylor, 9 years ago

Update version to 0.3.

File size: 97.5 KB
RevLine 
[3]1/******************************************************************************
2 * REXX OS/2 Printer Utility API (RXPRTUTL.DLL) *
[37]3 * (C) 2011,2016 Alex Taylor *
[3]4 * *
5 * LICENSE: *
6 * *
7 * Redistribution and use in source and binary forms, with or without *
8 * modification, are permitted provided that the following conditions are *
9 * met: *
10 * *
11 * 1. Redistributions of source code must retain the above copyright *
12 * notice, this list of conditions and the following disclaimer. *
13 * *
14 * 2. Redistributions in binary form must reproduce the above copyright *
15 * notice, this list of conditions and the following disclaimer in the *
16 * documentation and/or other materials provided with the distribution. *
17 * *
18 * 3. The name of the author may not be used to endorse or promote products *
19 * derived from this software without specific prior written permission. *
20 * *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR *
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED *
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
24 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, *
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, *
29 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN *
30 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *
31 * POSSIBILITY OF SUCH DAMAGE. *
32 * *
33 ******************************************************************************/
34
35#define INCL_DEV
36#define INCL_DOSERRORS
37#define INCL_DOSMISC
38#define INCL_DOSMODULEMGR
39#define INCL_DOSPROFILE
40#define INCL_SPL
41#define INCL_SPLDOSPRINT
42#define INCL_SPLERRORS
43#define INCL_PM
44#define INCL_WIN
45#define INCL_WINWORKPLACE
46#ifndef OS2_INCLUDED
47 #include <os2.h>
48#endif
49#include <ctype.h>
50#include <stdio.h>
51#include <stdlib.h>
52#include <string.h>
53#define INCL_RXSHV
54#define INCL_RXFUNC
55#include <rexxsaa.h>
56
[32]57#include "shfuncs.h"
[3]58
[32]59
[3]60// CONSTANTS
61
62#define SZ_LIBRARY_NAME "RXPRTUTL" // Name of this library
[38]63#define SZ_VERSION "0.3" // Current version of this library
[3]64
[37]65//This is now defined by the Makefile:
66//#define SZ_ERROR_NAME "RPUERROR" // REXX variable used to store error codes
67
[3]68#define APPNAME_LEAD_STR "PM_"
69#define APPNAME_PM_PORT_DRIVER "PM_PORT_DRIVER"
70#define APPNAME_PM_SPOOLER_PORT "PM_SPOOLER_PORT"
71#define APPNAME_PM_PRINT_OBJECT "PM_PrintObject"
[11]72#define APPNAME_PM_ABS_OBJECTS "PM_Abstract:Objects"
[3]73#define APPNAME_PM_WPS_LOCATION "PM_Workplace:Location"
74
[11]75#define ID_OBJINFO_QUEUENAME 3
76#define ID_OBJINFO_RPQUEUENAME 13
77
[3]78// Values that should be defined in pmsplb.h if it actually existed
[13]79#define TYPE_SHORT_WAIT 1
[3]80#define TYPE_LONG_WAIT 2
81#define BIDI_SET_PORTDRV 0x19
[13]82#define BIDI_Q_PORTDRV 0x8019
[3]83
84// Values used by WinOpenObject
85#define OPEN_DEFAULT 0
86#define OPEN_CONTENTS 1
87#define OPEN_SETTINGS 2
88#define OPEN_HELP 3
89#define OPEN_TREE 101
90#define OPEN_DETAILS 102
91
92// Maximum string lengths...
[32]93#ifdef NO_SHARED_SOURCE
94 #define US_COMPOUND_MAXZ 250 // ...of a compound variable
95 #define US_ERRSTR_MAXZ 250 // ...of an error string
96#endif
97
[3]98#define US_INTEGER_MAXZ 12 // ...of an integer string
99#define US_STEM_MAXZ ( US_COMPOUND_MAXZ - US_INTEGER_MAXZ ) // ...of a stem
100#define US_DRVINFO_MAXZ ( CCHMAXPATH + 8 + 32 ) // ...of an driver/port info string
101#define US_PRTINFO_MAXZ 180 // ...of a printer info string
102#define US_PORT_MAXZ 64 // ...of a port name
[8]103#define US_PRTDEV_MAXZ 9 // ...of a print device name
[3]104
[11]105
106// DATA TYPES
107
108/* Structures needed used to parse PM_Abstract:Object data
109 */
110#pragma pack(1)
111typedef struct _Object_Info
112{
113 USHORT cbName; // Size of szName, including terminator
114 USHORT cbData; // Number of additional data bytes following szName
115 CHAR szName[1]; // Name of object type
116} OBJINFO, *POBJINFO;
117
118typedef struct _Object_Tag_Info
119{
120 USHORT usTagFormat; // Tag data format
121 USHORT usTag; // Tag ID
122 USHORT cbTag; // Size of tag data
123} OITAG, *POITAG;
124#pragma pack()
125
126
127// GLOBALS
128
[3]129// List of functions to be registered by RPULoadFuncs
130static PSZ RxFunctionTbl[] = {
[32]131 "RPULoadFuncs", // drop only
[3]132 "RPUDropFuncs",
[15]133 "RPUDeviceDelete",
[5]134 "RPUEnumModels",
[3]135 "RPUEnumDrivers",
136 "RPUEnumPorts",
137 "RPUEnumPrinters",
[5]138 "RPUQueueDefault",
139 "RPUQueueHold",
140 "RPUOpenView",
[10]141 "RPUPortDelete",
[3]142 "RPUPortDialog",
[14]143 "RPUPortInfo",
[3]144 "RPUPortInstall",
[13]145 "RPUPortQuery",
[3]146 "RPUPortSet",
[5]147 "RPUPrinterCreate",
[7]148 "RPUPrinterDelete",
[3]149 "RPUPrinterQuery",
150 "RPUVersion"
151};
152
153
154// FUNCTION DECLARATIONS
155
156// Exported REXX functions
157RexxFunctionHandler RPULoadFuncs;
158RexxFunctionHandler RPUDropFuncs;
159RexxFunctionHandler RPUVersion;
[15]160RexxFunctionHandler RPUDeviceDelete;
[5]161RexxFunctionHandler RPUEnumModels;
[3]162RexxFunctionHandler RPUEnumDrivers;
163RexxFunctionHandler RPUEnumPorts;
164RexxFunctionHandler RPUEnumPrinters;
[5]165RexxFunctionHandler RPUOpenView;
[10]166RexxFunctionHandler RPUPortDelete;
[3]167RexxFunctionHandler RPUPortDialog;
[14]168RexxFunctionHandler RPUPortInfo;
[3]169RexxFunctionHandler RPUPortInstall;
[13]170RexxFunctionHandler RPUPortQuery;
[3]171RexxFunctionHandler RPUPortSet;
[5]172RexxFunctionHandler RPUPrinterCreate;
[7]173RexxFunctionHandler RPUPrinterDelete;
[3]174RexxFunctionHandler RPUPrinterQuery;
[5]175RexxFunctionHandler RPUQueueDefault;
176RexxFunctionHandler RPUQueueHold;
[3]177
178// TODO
179
180// Internal functions
181PSZ GetObjectID( PSZ pszHandle );
182HOBJECT PrinterObjectHandle( PSZ pszQueueName );
[8]183ULONG UniqueDeviceName( PSZ pszName );
[32]184#ifdef NO_SHARED_SOURCE
185 BOOL SaveResultString( PRXSTRING prsResult, PCH pchBytes, ULONG ulBytes );
186 BOOL WriteCompoundVariable( PSZ pszStem, PSZ pszTail, PSZ pszValue );
187// BOOL WriteSimpleVariable( PSZ pszName, PSZ pszValue );
188 BOOL WriteStemElement( PSZ pszStem, ULONG ulIndex, PSZ pszValue );
189 void WriteErrorCode( ULONG ulError, PSZ pszContext );
190#endif
[3]191
192
193/* ************************************************************************* *
194 * EXPORTED REXX FUNCTIONS *
195 * ************************************************************************* */
196
197/* ------------------------------------------------------------------------- *
198 * RPULoadFuncs *
199 * *
200 * Register all RPU* REXX functions except this one. *
201 * *
202 * REXX ARGUMENTS: None *
203 * REXX RETURN VALUE: "" *
204 * ------------------------------------------------------------------------- */
205ULONG APIENTRY RPULoadFuncs( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
206{
207 int entries,
208 i;
209
210 // Reset the error indicator
211 WriteErrorCode( 0, NULL );
212
213 if ( argc > 0 ) return ( 40 );
214 entries = sizeof(RxFunctionTbl) / sizeof(PSZ);
[32]215
216 // Start from 1 to skip RPULoadFuncs (we don't want to re-register ourselves)
217 for ( i = 1; i < entries; i++ )
[3]218 RexxRegisterFunctionDll( RxFunctionTbl[i], SZ_LIBRARY_NAME, RxFunctionTbl[i] );
219
[32]220 SaveResultString( prsResult, NULL, 0 );
[3]221 return ( 0 );
222}
223
224
225/* ------------------------------------------------------------------------- *
226 * RPUDropFuncs *
227 * *
228 * Deregister all RPU* REXX functions, including this one. *
229 * *
230 * REXX ARGUMENTS: None *
231 * REXX RETURN VALUE: "" *
232 * ------------------------------------------------------------------------- */
233ULONG APIENTRY RPUDropFuncs( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
234{
235 int entries,
236 i;
237
238 // Reset the error indicator
239 WriteErrorCode( 0, NULL );
240
241 if ( argc > 0 ) return ( 40 );
242 entries = sizeof(RxFunctionTbl) / sizeof(PSZ);
243 for ( i = 0; i < entries; i++ )
244 RexxDeregisterFunction( RxFunctionTbl[i] );
245
[32]246 SaveResultString( prsResult, NULL, 0 );
[3]247 return ( 0 );
248}
249
250
251/* ------------------------------------------------------------------------- *
252 * RPUVersion *
253 * *
254 * Returns the current library version. *
255 * *
256 * REXX ARGUMENTS: None *
257 * REXX RETURN VALUE: Current version in the form "major.minor.refresh" *
258 * ------------------------------------------------------------------------- */
259ULONG APIENTRY RPUVersion( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
260{
261 CHAR szVersion[ 12 ];
262
263 // Reset the error indicator
264 WriteErrorCode( 0, NULL );
265
266 if ( argc > 0 ) return ( 40 );
267 sprintf( szVersion, "%s", SZ_VERSION );
268
[32]269 SaveResultString( prsResult, szVersion, strlen(szVersion) );
[3]270 return ( 0 );
271}
272
273
274/* ------------------------------------------------------------------------- *
[15]275 * RPUDeviceDelete *
276 * *
277 * Deletes a local print device which lacks a printer queue. Can be useful *
278 * for cleaning up bad or corrupted printer definitions. *
279 * *
280 * REXX ARGUMENTS: *
281 * 1. The name of the print device to be deleted. (REQUIRED) *
282 * *
283 * REXX RETURN VALUE: *
284 * 1 on success, or 0 if an error occurred. *
285 * ------------------------------------------------------------------------- */
286ULONG APIENTRY RPUDeviceDelete( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
287{
288 PSZ pszDeviceName = NULL;
289 SPLERR rc;
290
291
292 // Reset the error indicator
293 WriteErrorCode( 0, NULL );
294
295 // Validate the REXX arguments
296 if (( argc != 1 ) || ( ! RXVALIDSTRING( argv[0] )))
297 return ( 40 );
298
299 pszDeviceName = argv[0].strptr;
300
301 rc = SplDeleteDevice( NULL, pszDeviceName );
302 if ( rc != NO_ERROR ) {
303 WriteErrorCode( rc, "SplDeleteDevice");
[32]304 SaveResultString( prsResult, PSZ_ZERO, 1 );
[15]305 return 0;
306 }
307
[32]308 SaveResultString( prsResult, PSZ_ONE, 1 );
[15]309 return ( 0 );
310}
311
312
313/* ------------------------------------------------------------------------- *
[5]314 * RPUEnumModels *
[3]315 * *
[5]316 * Gets a list of the printer models supported by the specified print *
317 * driver. *
[3]318 * *
319 * REXX ARGUMENTS: *
320 * 1. Filespec of the printer driver to query. (REQUIRED) *
321 * 2. The name of the stem in which to return the results. (REQUIRED) *
322 * *
323 * REXX RETURN VALUE: *
324 * 1 on success, or 0 if an error occurred. *
325 * ------------------------------------------------------------------------- */
[5]326ULONG APIENTRY RPUEnumModels( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
[3]327{
328 HAB hab; // desktop anchor-block handle
329 PSZ pszDriver; // print driver filespec
330 CHAR szStem[ US_STEM_MAXZ ], // buffers used for building stem
331 szNumber[ US_INTEGER_MAXZ ]; // ...
332 BOOL fSuccess; // success indicator
333 PSTR32 aDeviceName; // array of device names
334 PSTR64 aDeviceDesc; // array of device descriptions
335 PSTR16 aDataType; // array of data types
336 LONG pldn = 0L, // number of device names/descriptions
337 pldt = 0L, // number of data types
338 i;
[15]339 APIRET rc = 0;
[3]340
341 // Reset the error indicator
342 WriteErrorCode( 0, NULL );
343
344 // Make sure we have exactly two valid arguments
345 if (( argc != 2 ) ||
346 ( ! RXVALIDSTRING( argv[0] )) ||
347 ( ! RXVALIDSTRING( argv[1] )) ||
348 ( RXSTRLEN( argv[1] ) > US_STEM_MAXZ ))
349 return ( 40 );
350 pszDriver = argv[0].strptr;
351
352 if ( argv[1].strptr[ argv[1].strlength-1 ] == '.') argv[1].strlength--;
353 strncpy( szStem, argv[1].strptr, RXSTRLEN( argv[1] ));
354 szStem[ RXSTRLEN( argv[1] ) ] = '\0';
355
356 hab = WinInitialize( 0 );
357 if ( !hab ) {
358 WriteErrorCode( 0, "WinInitialize");
[32]359 SaveResultString( prsResult, PSZ_ZERO, 1 );
[3]360 return ( 0 );
361 }
362
363 // Query the size of the available data
364 fSuccess = DevQueryDeviceNames( hab, pszDriver, &pldn, NULL, NULL, &pldt, NULL );
365 if ( !fSuccess ) {
366 WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "DevQueryDeviceNames");
[32]367 SaveResultString( prsResult, PSZ_ZERO, 1 );
[3]368 goto cleanup;
369 }
370
371 // Now get the actual data
[32]372 /* (This originally used malloc() - switched to DosAllocMem() while in the
373 * process of trying to hunt down an intermittent trap. In fact, the trap
374 * was probably caused by a flaw in WriteStemElement() which has since been
375 * fixed.)
376 */
[15]377#if 1
378 rc = DosAllocMem( (PVOID) &aDeviceName, (ULONG) pldn * sizeof( STR32 ), PAG_WRITE | PAG_COMMIT );
379 if ( rc != NO_ERROR ) {
380 WriteErrorCode( rc, "DosAllocMem");
[32]381 SaveResultString( prsResult, PSZ_ZERO, 1 );
[15]382 goto cleanup;
383 }
384 rc = DosAllocMem( (PVOID) &aDeviceDesc, (ULONG) pldn * sizeof( STR64 ), PAG_WRITE | PAG_COMMIT );
385 if ( rc != NO_ERROR ) {
386 WriteErrorCode( rc, "DosAllocMem");
[32]387 SaveResultString( prsResult, PSZ_ZERO, 1 );
[15]388 DosFreeMem( aDeviceName );
389 goto cleanup;
390 }
391 rc = DosAllocMem( (PVOID) &aDataType, (ULONG) pldt * sizeof( STR16 ), PAG_WRITE | PAG_COMMIT );
392 if ( rc != NO_ERROR ) {
393 WriteErrorCode( rc, "DosAllocMem");
[32]394 SaveResultString( prsResult, PSZ_ZERO, 1 );
[15]395 DosFreeMem( aDeviceName );
396 DosFreeMem( aDeviceDesc );
397 goto cleanup;
398 }
399#else
[3]400 aDeviceName = malloc( pldn * sizeof( STR32 ));
[15]401 if ( !aDeviceName ) {
402 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
[32]403 SaveResultString( prsResult, PSZ_ZERO, 1 );
[15]404 goto cleanup;
405 }
[3]406 aDeviceDesc = malloc( pldn * sizeof( STR64 ));
[15]407 if ( !aDeviceDesc ) {
[3]408 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
[32]409 SaveResultString( prsResult, PSZ_ZERO, 1 );
[15]410 free( aDeviceName );
[3]411 goto cleanup;
412 }
[15]413 aDataType = malloc( pldt * sizeof( STR16 ));
414 if ( !aDataType ) {
415 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
[32]416 SaveResultString( prsResult, PSZ_ZERO, 1 );
[15]417 free( aDeviceName );
418 free( aDeviceDesc );
419 goto cleanup;
420 }
421#endif
[3]422 fSuccess = DevQueryDeviceNames( hab, pszDriver, &pldn, aDeviceName,
423 aDeviceDesc, &pldt, aDataType );
424 for ( i = 0; i < pldn; i++ ) {
425 WriteStemElement( szStem, i+1, (PSZ)aDeviceName[i] );
426 }
427 sprintf( szNumber, "%u", pldn );
428 WriteStemElement( szStem, 0, szNumber );
429
430 // Return 1 on success
[32]431 SaveResultString( prsResult, PSZ_ONE, 1 );
[3]432
[15]433#if 1
434 DosFreeMem( aDeviceName );
435 DosFreeMem( aDeviceDesc );
436 DosFreeMem( aDataType );
437#else
[3]438 free( aDeviceName );
439 free( aDeviceDesc );
440 free( aDataType );
[15]441#endif
[3]442
443cleanup:
444 WinTerminate( hab );
445 return ( 0 );
446}
447
448
449/* ------------------------------------------------------------------------- *
450 * RPUEnumPorts *
451 * *
452 * Gets a list of the printer ports currently defined on the system. Returns *
453 * a REXX array (stem) consisting of one string per port, of the form: *
454 * <port> <driver> <driver path> *
455 * *
456 * REXX ARGUMENTS: *
457 * 1. The name of the stem in which to return the results. (REQUIRED) *
458 * *
459 * REXX RETURN VALUE: *
460 * 1 on success, or 0 if an error occurred. *
461 * ------------------------------------------------------------------------- */
462ULONG APIENTRY RPUEnumPorts( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
463{
464 PPRPORTINFO1 pInfo;
465 PVOID pbuf;
466 ULONG cbBuf,
467 cTotal,
468 cReturned,
469 cbNeeded,
470 i;
471 CHAR szStem[ US_STEM_MAXZ ], // buffers used for building stem
472 szNumber[ US_INTEGER_MAXZ ], // ...
473 szInfo[ US_DRVINFO_MAXZ ];
474 SPLERR rc;
475
476 // Reset the error indicator
477 WriteErrorCode( 0, NULL );
478
479 // Make sure we have exactly one valid argument (the stem name)
480 if (( argc != 1 ) || ( ! RXVALIDSTRING( argv[0] )))
481 return ( 40 );
482 if ( argv[0].strptr[ argv[0].strlength-1 ] == '.') argv[0].strlength--;
483 strncpy( szStem, argv[0].strptr, RXSTRLEN( argv[0] ));
484 szStem[ RXSTRLEN( argv[0] ) ] = '\0';
485
486 // Query the amount of available data
487 rc = SplEnumPort( NULL, 1, NULL, 0L, &cReturned, &cTotal, &cbNeeded, NULL );
488 if (( rc != ERROR_MORE_DATA ) && ( rc != NERR_BufTooSmall )) {
489 WriteErrorCode( rc, "SplEnumPort");
[32]490 SaveResultString( prsResult, PSZ_ZERO, 1 );
[3]491 return ( 0 );
492 }
493
494 // Now get the actual data
495 pbuf = malloc( cbNeeded );
496 if ( !pbuf ) {
497 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
[32]498 SaveResultString( prsResult, PSZ_ZERO, 1 );
[3]499 return ( 0 );
500 }
501 cbBuf = cbNeeded;
502 rc = SplEnumPort( NULL, 1, pbuf, cbBuf,
503 &cReturned, &cTotal, &cbNeeded, NULL );
504 if ( rc == NO_ERROR ) {
505 pInfo = (PPRPORTINFO1) pbuf;
506 for ( i = 0; i < cReturned; i++ ) {
507 sprintf( szInfo, "%.32s %.8s %s",
508 pInfo->pszPortName,
509 pInfo->pszPortDriverName? pInfo->pszPortDriverName: "",
510 pInfo->pszPortDriverPathName? pInfo->pszPortDriverPathName: "");
511 WriteStemElement( szStem, i+1, szInfo );
512 pInfo++;
513 }
514 sprintf( szNumber, "%u", cReturned );
515 WriteStemElement( szStem, 0, szNumber );
[32]516 SaveResultString( prsResult, PSZ_ONE, 1 );
[3]517 }
518 else {
519 WriteErrorCode( rc, "SplEnumPort");
[32]520 SaveResultString( prsResult, PSZ_ZERO, 1 );
[3]521 }
522 free( pbuf );
523
524 return ( 0 );
525}
526
527
528/* ------------------------------------------------------------------------- *
529 * RPUEnumDrivers *
530 * *
531 * Gets a list of the printer drivers currently installed on the system. *
532 * Returns a REXX array (stem) with each item being a driver name string. *
533 * *
534 * REXX ARGUMENTS: *
535 * 1. The name of the stem in which to return the results. (REQUIRED) *
536 * *
537 * REXX RETURN VALUE: *
538 * 1 on success, or 0 if an error occurred. *
539 * ------------------------------------------------------------------------- */
540ULONG APIENTRY RPUEnumDrivers( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
541{
542 PSZ pszDriverName;
543 PBYTE pbuf;
544 ULONG cbBuf,
545 cTotal,
546 cReturned,
547 cbNeeded,
548 i;
549 CHAR szStem[ US_STEM_MAXZ ], // buffers used for building stem
550 szNumber[ US_INTEGER_MAXZ ]; // ...
551 SPLERR rc;
552
553
554 // Reset the error indicator
555 WriteErrorCode( 0, NULL );
556
557 // Make sure we have exactly one valid argument (the stem name)
558 if (( argc != 1 ) || ( ! RXVALIDSTRING( argv[0] )))
559 return ( 40 );
560 if ( argv[0].strptr[ argv[0].strlength-1 ] == '.') argv[0].strlength--;
561 strncpy( szStem, argv[0].strptr, RXSTRLEN( argv[0] ));
562 szStem[ RXSTRLEN( argv[0] ) ] = '\0';
563
564 // Query the amount of available data
565 rc = SplEnumDriver( NULL, 0L, NULL, 0L, &cReturned, &cTotal, &cbNeeded, NULL );
566 if (( rc != ERROR_MORE_DATA ) && ( rc != NERR_BufTooSmall )) {
567 WriteErrorCode( rc, "SplEnumDriver");
[32]568 SaveResultString( prsResult, PSZ_ZERO, 1 );
[3]569 return ( 0 );
570 }
571
572 // Now get the actual data
573 pbuf = (PBYTE) malloc( cbNeeded );
574 if ( !pbuf ) {
575 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
[32]576 SaveResultString( prsResult, PSZ_ZERO, 1 );
[3]577 return ( 0 );
578 }
579 cbBuf = cbNeeded;
580 rc = SplEnumDriver( NULL, 0L, pbuf, cbBuf, &cReturned ,&cTotal, &cbNeeded, NULL ) ;
581 if ( rc == NO_ERROR ) {
582 pszDriverName = (PSZ) pbuf;
583 for ( i = 0; i < cReturned ; i++ ) {
584 WriteStemElement( szStem, i+1, pszDriverName );
585 pszDriverName += DRIV_NAME_SIZE + DRIV_DEVICENAME_SIZE + 2;
586 }
587 sprintf( szNumber, "%u", cReturned );
588 WriteStemElement( szStem, 0, szNumber );
[32]589 SaveResultString( prsResult, PSZ_ONE, 1 );
[3]590 }
591 else {
592 WriteErrorCode( rc, "SplEnumDriver");
[32]593 SaveResultString( prsResult, PSZ_ZERO, 1 );
[3]594 }
595 free( pbuf );
596
597 return ( 0 );
598}
599
600
601/* ------------------------------------------------------------------------- *
602 * RPUEnumPrinters *
603 * *
604 * Gets a list of the printers currently defined on the system. Returns a *
605 * compound (stem) variable with the following format: *
606 * *
607 * (stem).0 Number of printers *
608 * (stem).i.!name Printer device (a.k.a. physical) name *
609 * (stem).i.!queue Printer queue name ('' for direct printers) *
610 * (stem).i.!host Name of LAN host where printer queue is located *
611 * ('' for non-LAN printers) *
612 * (stem).i.!description Printer description (name of WPS object) *
613 * (stem).i.!handle Printer's object handle ('' for direct printers) *
614 * (stem).i.!flags Zero or more of the following flags (any order): *
615 * D This is the default printer queue *
616 * P Printer queue is paused (held) *
617 * *
618 * REXX ARGUMENTS: *
619 * 1. The name of the stem in which to return the results. (REQUIRED) *
620 * *
621 * REXX RETURN VALUE: *
622 * 1 on success, or 0 if an error occurred. *
623 * ------------------------------------------------------------------------- */
624ULONG APIENTRY RPUEnumPrinters( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
625{
626 PPRINTERINFO pPInfo = NULL; // pointer to enumerated printer information
627 PPRQINFO6 pQInfo = NULL; // queue information (level 6)
628 PVOID pbEnum = NULL, // buffer for SplEnumPrinter
629 pbQueue = NULL; // buffer for SplQueryQueue
630 PSZ pszToken;
631 HOBJECT hObj; // printer WPS object handle
[13]632
633 /* Note: SPL_PR_LOCAL_ONLY apparently doesn't prevent SplEnumPrinter from
634 * including remote printers, it only stops it from probing them
635 * to query the remote queue information in addition to the local
636 * queue. Specifying it here prevents a 'stall' when the remote
637 * host is offline or unavailable.
638 */
[12]639 ULONG flType = SPL_PR_QUEUE | SPL_PR_DIRECT_DEVICE | SPL_PR_LOCAL_ONLY,
[3]640 cbBuf = 0,
641 cTotal = 0,
642 cReturned = 0,
643 cbNeeded = 0,
644 ulCount = 0, // number of printers found
645 i;
646 CHAR szFlags[ 3 ], // string of flag characters
647 szHandle[ 9 ], // string for hex object handle
648 // buffers used for building compound variables:
649 szStem[ US_STEM_MAXZ - US_INTEGER_MAXZ - 1 ],
650 szNumber[ US_INTEGER_MAXZ ],
651 szStemNode[ US_STEM_MAXZ ];
652 SPLERR rc;
653
654
655 // Reset the error indicator
656 WriteErrorCode( 0, NULL );
657
658 // Make sure we have exactly one valid argument (the stem name)
659 if (( argc != 1 ) || ( ! RXVALIDSTRING( argv[0] )))
660 return ( 40 );
661 if ( argv[0].strptr[ argv[0].strlength-1 ] == '.') argv[0].strlength--;
662 strncpy( szStem, argv[0].strptr, RXSTRLEN( argv[0] ));
663 szStem[ RXSTRLEN( argv[0] ) ] = '\0';
664
665
666 /* Our enumeration query asks for two types of devices: direct (non-queued)
667 * printers, and printer queues.
668 *
669 * In the latter case, it's important to note that we ask for the queues
670 * themselves rather than queued printers. Why? Because we'll need to
671 * gather information about both printers and their underlying queues (some
672 * things associated with printer objects actually belong to the queue
673 * rather than the printer device).
674 *
675 * The thing is, given a printer name, there doesn't seem to be an easy way
676 * to find out the queue associated with it. So we need to do it the other
677 * way around: get the queue names, then we can identify the printers
678 * connected to them.
679 */
680
681 // Query the amount of available data
682 rc = SplEnumPrinter( NULL, 0L, flType, NULL, 0L,
683 &cReturned, &cTotal, &cbNeeded, NULL );
684 if (( rc != ERROR_MORE_DATA ) && ( rc != NERR_BufTooSmall ))
685 // && ( rc != ERROR_INVALID_PARAMETER ))
686 {
687 WriteErrorCode( rc, "SplEnumPrinter");
[32]688 SaveResultString( prsResult, PSZ_ZERO, 1 );
[3]689 return ( 0 );
690 }
691
692 // Now get the actual data
693 pbEnum = malloc( cbNeeded );
694 if ( !pbEnum ) {
695 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
[32]696 SaveResultString( prsResult, PSZ_ZERO, 1 );
[3]697 return ( 0 );
698 }
699 cbBuf = cbNeeded;
700 rc = SplEnumPrinter( NULL, 0L, flType, pbEnum, cbBuf,
701 &cReturned, &cTotal, &cbNeeded, NULL );
702 if ( rc != NO_ERROR ) {
703 WriteErrorCode( rc, "SplEnumPrinter");
[32]704 SaveResultString( prsResult, PSZ_ZERO, 1 );
[3]705 goto cleanup;
706 }
707
708 pPInfo = (PPRINTERINFO) pbEnum;
709 for ( i = 0; i < cReturned; i++ ) {
710
711 if ( pPInfo->flType == SPL_PR_DIRECT_DEVICE ) {
712
713 /* Direct print device. No queue, no handle; flags do not apply.
714 * The destination name and description are those of the printer
715 * device itself, so we don't need any additional queries.
716 */
717 sprintf( szStemNode, "%s.%u", szStem, ++ulCount );
718
719 WriteCompoundVariable( szStemNode, "!name",
720 pPInfo->pszPrintDestinationName );
721 WriteCompoundVariable( szStemNode, "!description",
722 pPInfo->pszDescription );
723 WriteCompoundVariable( szStemNode, "!queue", "");
724 WriteCompoundVariable( szStemNode, "!flags", "");
725 WriteCompoundVariable( szStemNode, "!handle", "");
726
727 WriteCompoundVariable( szStemNode, "!host",
728 pPInfo->pszComputerName?
729 pPInfo->pszComputerName: "");
730 }
731 else if ( pPInfo->flType == SPL_PR_QUEUE ) {
732
733 /* Print queue. We need to query the queue to find out the details
734 * of the print device(s) connected to it.
735 */
[11]736 rc = SplQueryQueue( pPInfo->pszComputerName,
737 pPInfo->pszPrintDestinationName,
[3]738 6L, NULL, 0L, &cbNeeded );
739 if (( rc != NO_ERROR ) && ( rc != NERR_BufTooSmall )) {
740 WriteErrorCode( rc, "SplQueryQueue");
[32]741 SaveResultString( prsResult, PSZ_ZERO, 1 );
[11]742 //goto cleanup;
743 continue;
[3]744 }
745 pbQueue = malloc( cbNeeded );
746 if ( !pbQueue ) {
747 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
[32]748 SaveResultString( prsResult, PSZ_ZERO, 1 );
[11]749 //goto cleanup;
750 continue;
[3]751 }
752 cbBuf = cbNeeded;
[11]753 rc = SplQueryQueue( pPInfo->pszComputerName, pPInfo->pszPrintDestinationName,
[3]754 6L, pbQueue, cbBuf, &cbNeeded );
755 if ( rc != NO_ERROR ) {
756 WriteErrorCode( rc, "SplQueryQueue");
[32]757 SaveResultString( prsResult, PSZ_ZERO, 1 );
[3]758 free( pbQueue );
[11]759 //goto cleanup;
760 continue;
[3]761 }
762 pQInfo = (PPRQINFO6) pbQueue;
763
764 // Now go through the list of printers connected to this queue...
765 pszToken = strtok( pQInfo->pszPrinters, ",");
766 while ( pszToken ) {
767 sprintf( szStemNode, "%s.%u", szStem, ++ulCount );
768 WriteCompoundVariable( szStemNode, "!name", pszToken );
769 WriteCompoundVariable( szStemNode, "!description", pQInfo->pszComment );
770 WriteCompoundVariable( szStemNode, "!queue", pQInfo->pszName );
771 WriteCompoundVariable( szStemNode, "!driver", pQInfo->pszDriverName );
772 WriteCompoundVariable( szStemNode, "!host",
773 pQInfo->pszRemoteComputerName ?
774 pQInfo->pszRemoteComputerName: "");
775
776 hObj = PrinterObjectHandle( pQInfo->pszName );
777 if ( hObj )
778 sprintf( szHandle, "%X", hObj );
779 else
780 sprintf( szHandle, "");
781 WriteCompoundVariable( szStemNode, "!handle", szHandle );
782
783 memset( szFlags, 0, sizeof( szFlags ));
784 if ( pQInfo->fsType & PRQ3_TYPE_APPDEFAULT )
785 strcat( szFlags, "D");
786 if ( pQInfo->fsStatus & PRQ3_PAUSED )
787 strcat( szFlags, "P");
788 WriteCompoundVariable( szStemNode, "!flags", szFlags );
789
790 pszToken = strtok( NULL, ",");
791 } // while
792
793 free( pbQueue );
794 }
795
796 pPInfo++;
797 }
798 sprintf( szNumber, "%u", ulCount );
799 WriteStemElement( szStem, 0, szNumber );
[32]800 SaveResultString( prsResult, PSZ_ONE, 1 );
[3]801
802cleanup:
803 free( pbEnum );
804 return ( 0 );
805}
806
807
808/* ------------------------------------------------------------------------- *
[5]809 * RPUOpenView *
[3]810 * *
811 * Opens the requested view of the WPS object corresponding to the specified *
812 * print queue. (Note that WPS printer objects are associated with a queue *
813 * rather than a printer device, so it is the queue name that must be passed *
814 * to this function.) *
815 * *
816 * REXX ARGUMENTS: *
817 * 1. The name of the printer queue for the object to open. (REQUIRED) *
818 * 2. Optional view flag, one of: *
819 * O Object's default view (DEFAULT) *
820 * D Details view *
821 * I Icon view *
822 * S Settings *
823 * *
824 * REXX RETURN VALUE: *
825 * 1 on success, or 0 if an error occurred. *
826 * ------------------------------------------------------------------------- */
[5]827ULONG APIENTRY RPUOpenView( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
[3]828{
829 HOBJECT hObj;
830 ULONG ulView = OPEN_DEFAULT;
831 BOOL fRC;
832
833
834 // Reset the error indicator
835 WriteErrorCode( 0, NULL );
836
837 // Make sure we have at least one valid argument (the queue name)
838 if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
839 pszName = argv[0].strptr;
840
841 // Second argument: view (optional, but must be correct if specified)
842 if ( argc == 2 ) {
843 if ( RXVALIDSTRING(argv[1]) ) {
844 switch ( argv[1].strptr[0] ) {
845 case 'o':
846 case 'O': ulView = OPEN_DEFAULT; break;
847 case 'd':
848 case 'D': ulView = OPEN_DETAILS; break;
849 case 'i':
850 case 'I': ulView = OPEN_CONTENTS; break;
851 case 's':
852 case 'S': ulView = OPEN_SETTINGS; break;
853 default : return ( 40 );
854 }
855 }
856 else return ( 40 );
857 }
858 if ( argc > 2 ) return ( 40 );
859
860 hObj = PrinterObjectHandle( pszName );
[11]861 if ( !hObj ) {
[32]862 SaveResultString( prsResult, PSZ_ZERO, 1 );
[11]863 return ( 0 );
864 }
[3]865
866 fRC = WinOpenObject( hObj, ulView, TRUE );
867
[32]868 SaveResultString( prsResult, fRC? PSZ_ONE: PSZ_ZERO, 1 );
[3]869 return ( 0 );
870}
871
872
873/* ------------------------------------------------------------------------- *
[14]874 * RPUPortInfo *
875 * *
876 * Queries basic information about a local port. This is different from *
877 * RPUPortQuery() in that the latter returns port configuration information *
878 * from the port driver itself, whereas this function only returns what the *
879 * spooler itself knows about the port. *
880 * *
881 * REXX ARGUMENTS: *
882 * 1. The name of the port. (REQUIRED) *
883 * 2. The name of the stem in which to return the results: (REQUIRED) *
884 * (stem).!name The name of the port *
885 * (stem).!driver The name of the port driver *
886 * (stem).!converter The name of the protocol converter used *
[15]887 * TODO *
888 * (stem).!printer The device name of the printer(s) using this *
889 * port, if any ('' if not in use) *
[14]890 * *
891 * REXX RETURN VALUE: *
892 * 1 on success, or 0 if an error occurred. *
893 * ------------------------------------------------------------------------- */
894ULONG APIENTRY RPUPortInfo( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
895{
896 PPRPORTINFO2 pInfo = NULL;
897 PVOID pbuf = NULL;
898 ULONG cbBuf = 0,
899 cbNeeded = 0;
900 CHAR szStem[ US_STEM_MAXZ ]; // compound variable stem name
901 PSZ pszPortName;
902 SPLERR rc;
903
904
905 // Reset the error indicator
906 WriteErrorCode( 0, NULL );
907
908 // Validate the REXX arguments
909 if (( argc != 2 ) ||
910 ( ! RXVALIDSTRING( argv[0] )) || ( ! RXVALIDSTRING( argv[1] )))
911 return ( 40 );
912
913 pszPortName = argv[0].strptr;
914
915 // Initialize the result stem name
916 if ( RXSTRLEN(argv[1]) > US_STEM_MAXZ ) return ( 40 );
917 if ( argv[1].strptr[ argv[1].strlength-1 ] == '.') argv[1].strlength--;
918 strncpy( szStem, argv[1].strptr, RXSTRLEN( argv[1] ));
919 szStem[ RXSTRLEN( argv[1] ) ] = '\0';
920
921
922 // Query the amount of available data
923 rc = SplQueryPort( NULL, pszPortName, 2L, NULL, 0L, &cbNeeded );
924 if (( rc != ERROR_MORE_DATA ) && ( rc != NERR_BufTooSmall ))
925 {
926 WriteErrorCode( rc, "SplQueryPort");
[32]927 SaveResultString( prsResult, PSZ_ZERO, 1 );
[14]928 return ( 0 );
929 }
930
931 // Now get the actual data
932 pbuf = malloc( cbNeeded );
933 if ( !pbuf ) {
934 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
[32]935 SaveResultString( prsResult, PSZ_ZERO, 1 );
[14]936 return ( 0 );
937 }
938 cbBuf = cbNeeded;
939 rc = SplQueryPort( NULL, pszPortName, 2L, pbuf, cbBuf, &cbNeeded );
940 if ( rc == NO_ERROR ) {
941 pInfo = (PPRPORTINFO2) pbuf;
942 WriteCompoundVariable( szStem, "!name", pInfo->pszPortName );
943 WriteCompoundVariable( szStem, "!driver", pInfo->pszPortDriver );
944 WriteCompoundVariable( szStem, "!converter", pInfo->pszProtocolConverter );
[32]945 SaveResultString( prsResult, PSZ_ONE, 1 );
[14]946 }
947 else {
948 WriteErrorCode( rc, "SplQueryPort");
[32]949 SaveResultString( prsResult, PSZ_ZERO, 1 );
[14]950 }
951
952 free( pbuf );
953 return ( 0 );
954}
955
956
957/* ------------------------------------------------------------------------- *
[3]958 * RPUPortInstall *
959 * *
960 * Creates a new printer port. *
961 * *
962 * REXX ARGUMENTS: *
963 * 1. The name of the port driver without any extension. This must be *
964 * installed and registered in OS2.INI already. (REQUIRED) *
965 * 2. The name of the new port to be created. If not specified, the port *
[13]966 * driver will be responsible for using a default name. This should *
967 * generally be specified, as the driver is not guaranteed to support *
968 * omitting it. (DEFAULT: none) *
[3]969 * *
970 * REXX RETURN VALUE: *
971 * 1 on success, or 0 if an error occurred. *
972 * ------------------------------------------------------------------------- */
973ULONG APIENTRY RPUPortInstall( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
974{
975 HAB hab; // desktop anchor-block handle
976 HMODULE hPdr = NULLHANDLE; // handle to port driver module
977 PFN pfnInstallPort; // pointer to driver's SplPdInstallPort entrypoint
978 ULONG ulCB; // return value from PrfQueryProfileString()
979 PSZ pszPdrName = NULL, // name of the specified port driver
980 pszPortName = NULL; // name of the new port to create
981 CHAR szPathName[ CCHMAXPATH+1 ]; // FQN of the port driver module
982 APIRET rc = 0; // return code from Dos...() functions
983
984
985 // Reset the error indicator
986 WriteErrorCode( 0, NULL );
987
988 // Make sure we have at least one valid argument (the port driver name)
989 if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
990 pszPdrName = argv[0].strptr;
991
992 // Second argument: new port name (optional, but must be correct if specified)
993 if ( argc >= 2 ) {
994 if ( RXVALIDSTRING(argv[1]) )
995 pszPortName = strupr( argv[1].strptr );
996 else return ( 40 );
997 }
998
999 // Get the path to the installed port driver
1000 hab = WinInitialize( 0 );
1001 if ( !hab ) {
1002 WriteErrorCode( 0, "WinInitialize");
[32]1003 SaveResultString( prsResult, PSZ_ZERO, 1 );
[3]1004 return ( 0 );
1005 }
1006
1007 ulCB = PrfQueryProfileString( HINI_SYSTEMPROFILE, APPNAME_PM_PORT_DRIVER,
1008 pszPdrName, NULL, (PVOID) szPathName, CCHMAXPATH );
1009 if ( !ulCB ) {
1010 WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString");
[32]1011 SaveResultString( prsResult, PSZ_ZERO, 1 );
[3]1012 goto cleanup;
1013 }
1014
1015 // Load the port driver DLL and register its port install routine
1016 rc = DosLoadModule( NULL, 0, szPathName, &hPdr );
1017 if ( rc != NO_ERROR || !hPdr ) {
1018 WriteErrorCode( rc, "DosLoadModule");
[32]1019 SaveResultString( prsResult, PSZ_ZERO, 1 );
[3]1020 goto cleanup;
1021 }
1022 rc = DosQueryProcAddr( hPdr, 0, "SPLPDINSTALLPORT", &pfnInstallPort );
1023 if ( rc != NO_ERROR ) {
1024 WriteErrorCode( rc, "DosQueryProcAddr");
[32]1025 SaveResultString( prsResult, PSZ_ZERO, 1 );
[3]1026 goto finish;
1027 }
1028
1029 // Now create the new port (the driver will assign it default settings)
1030 rc = pfnInstallPort( hab, pszPortName );
1031 if ( rc == NO_ERROR ) {
1032 // Return 1 on success
[32]1033 SaveResultString( prsResult, PSZ_ONE, 1 );
[3]1034 }
1035 else {
1036 WriteErrorCode( rc, "SplPdInstallPort");
[32]1037 SaveResultString( prsResult, PSZ_ZERO, 1 );
[3]1038 }
1039
1040finish:
1041 DosFreeModule( hPdr );
1042cleanup:
1043 WinTerminate( hab );
1044 return ( 0 );
1045}
1046
1047
1048/* ------------------------------------------------------------------------- *
[13]1049 * RPUPortQuery *
1050 * *
1051 * Queries the specified port's configuration settings. IMPORTANT: not all *
1052 * port drivers support this; the standard OS/2 serial and parallel port *
1053 * drivers do NOT. When this API is not supported, the return value should *
1054 * be ''. Otherwise, the format of the returned configuration data is a *
1055 * string of binary data in port driver-specific format (the caller assumes *
1056 * responsibility for knowing how to interpret it). *
1057 * *
1058 * REXX ARGUMENTS: *
1059 * 1. The name of the port driver without any extension. This must be *
1060 * installed and registered in OS2.INI already. (REQUIRED) *
1061 * 2. The name of the port to be queried. (REQUIRED) *
1062 * *
1063 * REXX RETURN VALUE: *
1064 * Binary data representing the port configuration, in whatever format is *
1065 * returned by the driver's SplPdQuery->BIDI_Q_PORTDRV routine. This *
1066 * depends on the particular port driver; consult its API documentation. *
1067 * '' will be returned if an error occurred. *
1068 * ------------------------------------------------------------------------- */
1069ULONG APIENTRY RPUPortQuery( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1070{
1071 HAB hab; // desktop anchor-block handle
1072 HMODULE hPdr = NULLHANDLE; // handle to port driver module
1073 PFN pfnQueryPort; // pointer to driver's SplPdSet entrypoint
1074 ULONG ulCB, // return value from PrfQueryProfileString()
1075 cBuf; // size of configuration buffer
1076 PSZ pszPdrName = NULL, // name of the specified port driver
1077 pszPortName = NULL; // name of the new port to create
1078 CHAR szPathName[ CCHMAXPATH+1 ]; // FQN of the port driver module
1079 PBYTE pBuf; // Pointer to configuration buffer
1080 APIRET rc = 0; // return code from Dos...() functions
1081
1082
1083 // Reset the error indicator
1084 WriteErrorCode( 0, NULL );
1085
1086 // Make sure we have exactly two valid arguments
1087 if ( argc != 2 ||
1088 !RXVALIDSTRING( argv[0] ) ||
1089 !RXVALIDSTRING( argv[1] ))
1090 return ( 40 );
1091 pszPdrName = strupr( argv[0].strptr );
1092 pszPortName = strupr( argv[1].strptr );
1093
1094 // Get the path to the installed port driver
1095 hab = WinInitialize( 0 );
1096 if ( !hab ) {
1097 WriteErrorCode( 0, "WinInitialize");
[32]1098 SaveResultString( prsResult, NULL, 0 );
[13]1099 return ( 0 );
1100 }
1101
1102 ulCB = PrfQueryProfileString( HINI_SYSTEMPROFILE, APPNAME_PM_PORT_DRIVER,
1103 pszPdrName, NULL, (PVOID) szPathName, CCHMAXPATH );
1104 if ( !ulCB ) {
1105 WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString");
[32]1106 SaveResultString( prsResult, NULL, 0 );
[13]1107 goto cleanup;
1108 }
1109
1110 // Load the port driver DLL and register its query routine
1111 rc = DosLoadModule( NULL, 0, szPathName, &hPdr );
1112 if ( rc != NO_ERROR || !hPdr ) {
1113 WriteErrorCode( rc, "DosLoadModule");
[32]1114 SaveResultString( prsResult, NULL, 0 );
[13]1115 goto cleanup;
1116 }
1117 rc = DosQueryProcAddr( hPdr, 0, "SPLPDQUERY", &pfnQueryPort );
1118 if ( rc != NO_ERROR ) {
1119 WriteErrorCode( rc, "DosQueryProcAddr");
[32]1120 SaveResultString( prsResult, NULL, 0 );
[13]1121 goto finish;
1122 }
1123
1124 // Now get the port configuration
1125 rc = pfnQueryPort( pszPortName, TYPE_SHORT_WAIT, BIDI_Q_PORTDRV, NULL, 0, NULL, &cBuf );
1126 if ( cBuf && ( rc == NO_ERROR ) ||
1127 ( rc == ERROR_MORE_DATA ) || ( rc == NERR_BufTooSmall ))
1128 {
1129 pBuf = (PBYTE) malloc( cBuf );
1130 if ( pBuf ) {
1131 rc = pfnQueryPort( pszPortName, TYPE_SHORT_WAIT, BIDI_Q_PORTDRV, NULL, 0, pBuf, &cBuf );
1132 if ( rc == NO_ERROR ) {
1133 // Write the data contents to our return RXSTRING
1134 if ( !SaveResultString( prsResult, pBuf, cBuf ))
[32]1135 SaveResultString( prsResult, NULL, 0 );
[13]1136 }
1137 else {
1138 WriteErrorCode( rc, "SplPdQuery 2");
[32]1139 SaveResultString( prsResult, NULL, 0 );
[13]1140 }
1141 free( pBuf );
1142 }
1143 else {
1144 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
[32]1145 SaveResultString( prsResult, NULL, 0 );
[13]1146 }
1147 }
1148 else {
1149 WriteErrorCode( rc, "SplPdQuery 1");
[32]1150 SaveResultString( prsResult, NULL, 0 );
[13]1151 }
1152
1153finish:
1154 DosFreeModule( hPdr );
1155cleanup:
1156 WinTerminate( hab );
1157 return ( 0 );
1158}
1159
1160
1161/* ------------------------------------------------------------------------- *
[3]1162 * RPUPortSet *
1163 * *
1164 * Sets the specified port's configuration settings. IMPORTANT: not all *
1165 * port drivers support this; the standard OS/2 serial and parallel port *
1166 * drivers do NOT. Depending on the port driver, there are two possible *
1167 * outcomes when this API is not supported: *
1168 * A. Port driver does not have an entrypoint for SplPdSet. This is the *
1169 * case for any port driver using pre-Warp conventions. RPUPortSet will *
1170 * return 0 and RPUERROR will be "1: DosQueryProcAddr". *
1171 * B. Port driver supports SplPdSet but does not (correctly) implement the *
1172 * BIDI_SET_PORTDRV command. This is the case for some third-party port *
1173 * drivers. In this case, whether or not RPUPortSet returns a success *
1174 * code entirely depends on how conscientiously the port driver itself *
1175 * is written, but the "INITIALIZATION" key (and possibly others) in *
1176 * the port's entry in OS2SYS.INI will not be updated. The application *
1177 * should always check for this after calling RPUPortSet! *
1178 * If the application determines that either of these failure conditions has *
1179 * occurred, it is advisable to call RPUPortDialog as a fallback measure. *
1180 * *
1181 * Because of the above, use of this function is not generally encouraged *
1182 * except when absolutely necessary. *
1183 * *
1184 * REXX ARGUMENTS: *
1185 * 1. The name of the port driver without any extension. This must be *
1186 * installed and registered in OS2.INI already. (REQUIRED) *
1187 * 2. The name of the port to be configured. (REQUIRED) *
1188 * 3. Byte sequence representing the new port configuration. The format *
1189 * depends on the port driver (and it is up to the caller to know what *
1190 * it is). (REQUIRED) *
1191 * *
1192 * REXX RETURN VALUE: *
1193 * 1 on success, or 0 if an error occurred. *
1194 * ------------------------------------------------------------------------- */
1195ULONG APIENTRY RPUPortSet( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1196{
1197 HAB hab; // desktop anchor-block handle
1198 HMODULE hPdr = NULLHANDLE; // handle to port driver module
1199 PFN pfnSetPort; // pointer to driver's SplPdSet entrypoint
1200 ULONG ulCB, // return value from PrfQueryProfileString()
1201 cBuf; // size of configuration buffer
1202 PSZ pszPdrName = NULL, // name of the specified port driver
1203 pszPortName = NULL; // name of the new port to create
1204 CHAR szPathName[ CCHMAXPATH+1 ]; // FQN of the port driver module
1205 PBYTE pBuf; // Pointer to configuration buffer
1206 APIRET rc = 0; // return code from Dos...() functions
1207
1208
1209 // Reset the error indicator
1210 WriteErrorCode( 0, NULL );
1211
1212 // Make sure we have exactly three valid arguments
1213 if ( argc != 3 ||
1214 !RXVALIDSTRING( argv[0] ) ||
1215 !RXVALIDSTRING( argv[1] ) ||
1216 !RXVALIDSTRING( argv[2] ) )
1217 return ( 40 );
[13]1218 pszPdrName = strupr( argv[0].strptr );
[3]1219 pszPortName = strupr( argv[1].strptr );
1220 pBuf = argv[2].strptr;
1221 cBuf = argv[2].strlength;
1222
1223 // Get the path to the installed port driver
1224 hab = WinInitialize( 0 );
1225 if ( !hab ) {
1226 WriteErrorCode( 0, "WinInitialize");
[32]1227 SaveResultString( prsResult, PSZ_ZERO, 1 );
[3]1228 return ( 0 );
1229 }
1230
1231 ulCB = PrfQueryProfileString( HINI_SYSTEMPROFILE, APPNAME_PM_PORT_DRIVER,
1232 pszPdrName, NULL, (PVOID) szPathName, CCHMAXPATH );
1233 if ( !ulCB ) {
1234 WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString");
[32]1235 SaveResultString( prsResult, PSZ_ZERO, 1 );
[3]1236 goto cleanup;
1237 }
1238
1239 // Load the port driver DLL and register its configuration routine
1240 rc = DosLoadModule( NULL, 0, szPathName, &hPdr );
1241 if ( rc != NO_ERROR || !hPdr ) {
1242 WriteErrorCode( rc, "DosLoadModule");
[32]1243 SaveResultString( prsResult, PSZ_ZERO, 1 );
[3]1244 goto cleanup;
1245 }
1246 rc = DosQueryProcAddr( hPdr, 0, "SPLPDSET", &pfnSetPort );
1247 if ( rc != NO_ERROR ) {
1248 WriteErrorCode( rc, "DosQueryProcAddr");
[32]1249 SaveResultString( prsResult, PSZ_ZERO, 1 );
[3]1250 goto finish;
1251 }
1252
1253 // Now set the new port configuration
1254 rc = pfnSetPort( pszPortName, TYPE_LONG_WAIT, BIDI_SET_PORTDRV, pBuf, cBuf );
1255 if ( rc == NO_ERROR ) {
1256 // Return 1 on success
[32]1257 SaveResultString( prsResult, PSZ_ONE, 1 );
[3]1258 }
1259 else {
1260 WriteErrorCode( rc, "SplPdSet");
[32]1261 SaveResultString( prsResult, PSZ_ZERO, 1 );
[3]1262 }
1263
1264finish:
1265 DosFreeModule( hPdr );
1266cleanup:
1267 WinTerminate( hab );
1268 return ( 0 );
1269}
1270
1271
1272/* ------------------------------------------------------------------------- *
1273 * RPUPortDialog *
1274 * *
1275 * Brings up the specified port's configuration dialog. This function can *
1276 * only be used from a PM process. *
1277 * *
1278 * REXX ARGUMENTS: *
1279 * 1. The name of the port driver without any extension. This must be *
1280 * installed and registered in OS2.INI already. (REQUIRED) *
1281 * 2. The name of the port to be configured. (REQUIRED) *
1282 * *
1283 * REXX RETURN VALUE: *
1284 * 1 if the user modified the port configuration. *
1285 * 0 if no changes were made, or if an error occurred. *
1286 * ------------------------------------------------------------------------- */
1287ULONG APIENTRY RPUPortDialog( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1288{
1289 HAB hab; // desktop anchor-block handle
1290 HMODULE hPdr = NULLHANDLE; // handle to port driver module
1291 PFN pfnSetPort; // pointer to driver's SplPdSet entrypoint
1292 ULONG ulCB, // return value from PrfQueryProfileString()
1293 flModified = 0; // flag indicating settings were changed
1294 PSZ pszPdrName = NULL, // name of the specified port driver
1295 pszPortName = NULL; // name of the new port to create
1296 CHAR szPathName[ CCHMAXPATH+1 ]; // FQN of the port driver module
1297 APIRET rc = 0; // return code from Dos...() functions
1298
1299
1300 // Reset the error indicator
1301 WriteErrorCode( 0, NULL );
1302
1303 // Make sure we have exactly two valid arguments
1304 if ( argc != 2 ||
1305 !RXVALIDSTRING( argv[0] ) ||
1306 !RXVALIDSTRING( argv[1] ) )
1307 return ( 40 );
[13]1308 pszPdrName = strupr( argv[0].strptr );
[3]1309 pszPortName = strupr( argv[1].strptr );
1310
1311 // Get the path to the installed port driver
1312 hab = WinInitialize( 0 );
1313 if ( !hab ) {
1314 WriteErrorCode( 0, "WinInitialize");
[32]1315 SaveResultString( prsResult, PSZ_ZERO, 1 );
[3]1316 return ( 0 );
1317 }
1318
1319 ulCB = PrfQueryProfileString( HINI_SYSTEMPROFILE, APPNAME_PM_PORT_DRIVER,
1320 pszPdrName, NULL, (PVOID) szPathName, CCHMAXPATH );
1321 if ( !ulCB ) {
1322 WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString");
[32]1323 SaveResultString( prsResult, PSZ_ZERO, 1 );
[3]1324 goto cleanup;
1325 }
1326
1327 // Load the port driver DLL and register its dialog routine
1328 rc = DosLoadModule( NULL, 0, szPathName, &hPdr );
1329 if ( rc != NO_ERROR || !hPdr ) {
1330 WriteErrorCode( rc, "DosLoadModule");
[32]1331 SaveResultString( prsResult, PSZ_ZERO, 1 );
[3]1332 goto cleanup;
1333 }
1334 rc = DosQueryProcAddr( hPdr, 0, "SPLPDSETPORT", &pfnSetPort );
1335 if ( rc != NO_ERROR ) {
1336 WriteErrorCode( rc, "DosQueryProcAddr");
[32]1337 SaveResultString( prsResult, PSZ_ZERO, 1 );
[3]1338 goto finish;
1339 }
1340
1341 // Now show the port configuration dialog
1342 rc = pfnSetPort( hab, pszPortName, &flModified );
1343 if ( rc == NO_ERROR ) {
1344 // Return 1 if settings were modified
[32]1345 SaveResultString( prsResult, flModified? PSZ_ONE: PSZ_ZERO, 1 );
[3]1346 }
1347 else {
1348 WriteErrorCode( rc, "SplPdSetPort");
[32]1349 SaveResultString( prsResult, PSZ_ZERO, 1 );
[3]1350 }
1351
1352finish:
1353 DosFreeModule( hPdr );
1354cleanup:
1355 WinTerminate( hab );
1356 return ( 0 );
1357}
1358
1359
[5]1360/* ------------------------------------------------------------------------- *
[10]1361 * RPUPortDelete *
1362 * *
1363 * Deletes a printer port. *
1364 * *
1365 * REXX ARGUMENTS: *
1366 * 1. The name of the port to be deleted. (REQUIRED) *
1367 * *
1368 * REXX RETURN VALUE: *
1369 * 1 on success, or 0 if an error occurred. *
1370 * ------------------------------------------------------------------------- */
1371ULONG APIENTRY RPUPortDelete( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1372{
1373 PSZ pszPortName;
1374 SPLERR rc = 0;
1375
1376
1377 // Reset the error indicator
1378 WriteErrorCode( 0, NULL );
1379
1380 // Make sure we have at least one valid argument (the port name)
1381 if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
1382 pszPortName = strupr( argv[0].strptr );
1383
1384 rc = SplDeletePort( NULL, pszPortName );
1385 if ( rc == NO_ERROR ) {
1386 // Return 1 on success
[32]1387 SaveResultString( prsResult, PSZ_ONE, 1 );
[10]1388 }
1389 else {
1390 WriteErrorCode( rc, "SplDeletePort");
[32]1391 SaveResultString( prsResult, PSZ_ZERO, 1 );
[10]1392 }
1393
1394 return ( 0 );
1395}
1396
1397
1398/* ------------------------------------------------------------------------- *
[5]1399 * RPUPrinterCreate *
1400 * *
1401 * Creates a new local printer object. The associated print queue and *
[8]1402 * device are created automatically. However, the specified output port *
1403 * must exist, and the specified printer driver/model must have been *
1404 * installed already. *
[5]1405 * *
[8]1406 * The WPS object is created with default settings, and will be assigned an *
1407 * object-ID automatically by the WPS. *
[5]1408 * *
[8]1409 * NOTE: This function will NOT create a remote (LAN) printer object. *
[5]1410 * *
1411 * REXX ARGUMENTS: *
[8]1412 * 1. The printer description, used as the WPS object title. (REQUIRED) *
1413 * 2. The name of the underlying print queue. Must be a legal *
1414 * queue name according to OS/2 (but what is that...?) (REQUIRED) *
[5]1415 * 3. The name of the printer port, which must exist already. (REQUIRED) *
1416 * 4. The default printer driver.model to be associated with *
[8]1417 * the device. If specified, this must be installed in the *
1418 * system already. If not specified, IBMNULL is assumed. (OPTIONAL) *
[5]1419 * *
1420 * REXX RETURN VALUE: *
1421 * 1 on success, or 0 if an error occurred. *
1422 * ------------------------------------------------------------------------- */
1423ULONG APIENTRY RPUPrinterCreate( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1424{
1425 PRDINFO3 devinfo = {0};
1426 PRQINFO3 qinfo = {0};
1427 PSZ pszQueueName = NULL,
1428 pszPortName = NULL,
1429 pszModel = NULL,
[6]1430 pszTitle = NULL;
[8]1431 CHAR szDevice[ US_PRTDEV_MAXZ ] = {0};
[5]1432 SPLERR rc;
1433
1434
1435 // Reset the error indicator
1436 WriteErrorCode( 0, NULL );
1437
1438 // Validate the REXX arguments
[8]1439 if (( argc < 3 ) || ( argc > 4 ) || ( ! RXVALIDSTRING( argv[0] )) ||
[5]1440 ( ! RXVALIDSTRING( argv[1] )) || ( ! RXVALIDSTRING( argv[2] )))
1441 return ( 40 );
1442 if (( argc > 3 ) && ( ! RXVALIDSTRING( argv[3] )))
1443 return ( 40 );
1444
[8]1445 pszTitle = argv[0].strptr;
1446 pszQueueName = argv[1].strptr;
[5]1447 pszPortName = argv[2].strptr;
1448 if ( argc > 3 ) pszModel = argv[3].strptr;
1449
[8]1450 // Generate a suitable (unique) device name, based on the queue name
1451 strncpy( szDevice, pszQueueName, US_PRTDEV_MAXZ-1 );
1452 if (( rc = UniqueDeviceName( szDevice )) != NO_ERROR ) {
1453 if ( rc == 1 ) // shouldn't really happen
1454 WriteErrorCode( rc, "UniqueDeviceName");
1455 else
1456 WriteErrorCode( rc, "SplEnumDevice");
[32]1457 SaveResultString( prsResult, PSZ_ZERO, 1 );
[8]1458 return ( 0 );
1459 }
[5]1460
[8]1461 // Create the device
1462 devinfo.pszPrinterName = szDevice;
1463 devinfo.pszUserName = NULL;
1464 devinfo.pszLogAddr = pszPortName;
1465 devinfo.pszComment = pszTitle;
1466 devinfo.pszDrivers = pszModel ? pszModel : "IBMNULL";
1467 devinfo.usTimeOut = 45;
1468 rc = SplCreateDevice( NULL, 3, &devinfo, sizeof( devinfo ));
1469 if ( rc != NO_ERROR ) {
1470 WriteErrorCode( rc, "SplCreateDevice");
[32]1471 SaveResultString( prsResult, PSZ_ZERO, 1 );
[5]1472 return ( 0 );
1473 }
1474
[8]1475 // Create the queue (automatically creates printer object)
1476 qinfo.pszName = pszQueueName;
1477 qinfo.uPriority = PRQ_DEF_PRIORITY;
1478 qinfo.fsType = PRQ3_TYPE_RAW;
1479 qinfo.pszPrProc = "PMPRINT";
1480 qinfo.pszComment = pszTitle;
1481 qinfo.pszPrinters = szDevice;
1482 qinfo.pszDriverName = pszModel ? pszModel : "IBMNULL";
1483 rc = SplCreateQueue( NULL, 3, &qinfo, sizeof( qinfo ));
1484 if ( rc != NO_ERROR ) {
1485 WriteErrorCode( rc, "SplCreateQueue");
[32]1486 SaveResultString( prsResult, PSZ_ZERO, 1 );
[8]1487 SplDeleteDevice( NULL, szDevice );
[5]1488 return ( 0 );
1489 }
1490
[32]1491 SaveResultString( prsResult, PSZ_ONE, 1 );
[7]1492 return ( 0 );
1493}
[6]1494
[7]1495
1496/* ------------------------------------------------------------------------- *
1497 * RPUPrinterDelete *
1498 * *
1499 * Deletes a local printer queue and its associated print device definition. *
1500 * *
1501 * REXX ARGUMENTS: *
1502 * 1. The name of the printer queue to be deleted. (REQUIRED) *
1503 * *
1504 * REXX RETURN VALUE: *
1505 * 1 on success, or 0 if an error occurred. (1 will be returned even if *
1506 * the WPS printer object could not be deleted, so long as the queue and *
[10]1507 * device were destroyed successfully.) *
[7]1508 * ------------------------------------------------------------------------- */
1509ULONG APIENTRY RPUPrinterDelete( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1510{
1511 PPRQINFO3 pInfo = NULL;
1512 ULONG cbBuf = 0;
1513 PSZ pszQueueName = NULL,
1514 pszDeviceName = NULL;
1515 HOBJECT hObj;
1516 SPLERR rc;
1517
1518
1519 // Reset the error indicator
1520 WriteErrorCode( 0, NULL );
1521
1522 // Validate the REXX arguments
1523 if (( argc != 1 ) || ( ! RXVALIDSTRING( argv[0] )))
1524 return ( 40 );
1525
1526 pszQueueName = argv[0].strptr;
1527 hObj = PrinterObjectHandle( pszQueueName );
1528
1529 rc = SplQueryQueue( NULL, pszQueueName, 3, NULL, 0, &cbBuf );
1530 if (( rc != NO_ERROR ) && ( rc != NERR_BufTooSmall )) {
1531 WriteErrorCode( rc, "SplQueryQueue");
[32]1532 SaveResultString( prsResult, PSZ_ZERO, 1 );
[7]1533 return ( 0 );
[6]1534 }
[7]1535 pInfo = (PPRQINFO3) malloc( cbBuf );
1536 if ( !pInfo ) {
1537 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
[32]1538 SaveResultString( prsResult, PSZ_ZERO, 1 );
[5]1539 return ( 0 );
1540 }
[7]1541 rc = SplQueryQueue( NULL, pszQueueName, 3, pInfo, cbBuf, &cbBuf );
1542 if ( rc != NO_ERROR ) {
1543 WriteErrorCode( rc, "SplQueryQueue");
[32]1544 SaveResultString( prsResult, PSZ_ZERO, 1 );
[7]1545 goto cleanup;
1546 }
1547 pszDeviceName = strtok( pInfo->pszPrinters, ",");
1548 rc = SplDeleteDevice( NULL, pszDeviceName );
1549 if ( rc != NO_ERROR ) {
1550 WriteErrorCode( rc, "SplDeleteDevice");
[32]1551 SaveResultString( prsResult, PSZ_ZERO, 1 );
[7]1552 goto cleanup;
1553 }
1554
1555 // Try and destroy the WPS object
[13]1556 // - NB This causes a long delay when deleting an offline LAN printer
[7]1557 if ( hObj != NULLHANDLE ) WinDestroyObject( hObj );
1558
[32]1559 SaveResultString( prsResult, PSZ_ONE, 1 );
[7]1560cleanup:
1561 free( pInfo );
[5]1562 return ( 0 );
1563}
1564
1565
1566/* ------------------------------------------------------------------------- *
1567 * RPUPrinterQuery *
1568 * *
1569 * Gets information about the specified printer device. *
1570 * *
[15]1571 * (stem).!description Printer description (name of WPS object) *
1572 * (stem).!port Name of the port the printer is using *
1573 * (stem).!driver2 List of the drivers used by this printer *
1574 * (stem).!jobflags Zero or more of the following flags (any order): *
[5]1575 * E A printer error has occurred *
[12]1576 * H Printer destination is paused (held) *
[5]1577 * I Intervention required *
1578 * N Printer has raised a notification alert *
1579 * O Printer is offline *
1580 * P Printer is out of paper *
1581 * *
1582 * REXX ARGUMENTS: *
1583 * 1. The name of the printer device being queried. (REQUIRED) *
1584 * 2. The name of the stem in which to return the results. (REQUIRED) *
1585 * *
1586 * REXX RETURN VALUE: *
1587 * 1 on success, or 0 if an error occurred. *
1588 * ------------------------------------------------------------------------- */
1589ULONG APIENTRY RPUPrinterQuery( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1590{
1591 PPRDINFO3 pInfo = NULL;
1592 PVOID pbuf = NULL;
1593 ULONG cbBuf = 0,
1594 cbNeeded = 0;
1595 PSZ pszDeviceName;
[12]1596 CHAR szFlags[ 8 ] = {0},
1597 szStem[ US_STEM_MAXZ ]; // compound variable stem name
[5]1598 SPLERR rc;
1599
1600
1601 // Reset the error indicator
1602 WriteErrorCode( 0, NULL );
1603
1604 // Validate the REXX arguments
1605 if (( argc != 2 ) ||
1606 ( ! RXVALIDSTRING( argv[0] )) || ( ! RXVALIDSTRING( argv[1] )))
1607 return ( 40 );
1608
1609 pszDeviceName = argv[0].strptr;
1610
1611 // Initialize the result stem name
1612 if ( RXSTRLEN(argv[1]) > US_STEM_MAXZ ) return ( 40 );
1613 if ( argv[1].strptr[ argv[1].strlength-1 ] == '.') argv[1].strlength--;
1614 strncpy( szStem, argv[1].strptr, RXSTRLEN( argv[1] ));
1615 szStem[ RXSTRLEN( argv[1] ) ] = '\0';
1616
1617
1618 // Query the amount of available data
1619 rc = SplQueryDevice( NULL, pszDeviceName, 3L, NULL, 0L, &cbNeeded );
1620 if (( rc != ERROR_MORE_DATA ) && ( rc != NERR_BufTooSmall ))
1621 {
1622 WriteErrorCode( rc, "SplQueryDevice");
[32]1623 SaveResultString( prsResult, PSZ_ZERO, 1 );
[5]1624 return ( 0 );
1625 }
1626
1627 // Now get the actual data
1628 pbuf = malloc( cbNeeded );
1629 if ( !pbuf ) {
1630 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
[32]1631 SaveResultString( prsResult, PSZ_ZERO, 1 );
[5]1632 return ( 0 );
1633 }
1634 cbBuf = cbNeeded;
1635 rc = SplQueryDevice( NULL, pszDeviceName, 3L, pbuf, cbBuf, &cbNeeded );
1636 if ( rc == NO_ERROR ) {
1637 pInfo = (PPRDINFO3) pbuf;
1638 WriteCompoundVariable( szStem, "!name", pInfo->pszPrinterName );
1639 WriteCompoundVariable( szStem, "!port", pInfo->pszLogAddr );
1640 WriteCompoundVariable( szStem, "!description", pInfo->pszComment );
1641 WriteCompoundVariable( szStem, "!drivers", pInfo->pszDrivers );
[12]1642 if ( pInfo->fsStatus & PRJ_ERROR ) strcat( szFlags, "E");
1643 if ( pInfo->fsStatus & PRJ_DESTPAUSED ) strcat( szFlags, "H");
1644 if ( pInfo->fsStatus & PRJ_INTERV ) strcat( szFlags, "I");
1645 if ( pInfo->fsStatus & PRJ_NOTIFY ) strcat( szFlags, "N");
1646 if ( pInfo->fsStatus & PRJ_DESTOFFLINE ) strcat( szFlags, "O");
1647 if ( pInfo->fsStatus & PRJ_DESTNOPAPER ) strcat( szFlags, "P");
1648 WriteCompoundVariable( szStem, "!jobflags", szFlags );
[32]1649 SaveResultString( prsResult, PSZ_ONE, 1 );
[5]1650 }
1651 else {
1652 WriteErrorCode( rc, "SplQueryDevice");
[32]1653 SaveResultString( prsResult, PSZ_ZERO, 1 );
[5]1654 }
1655
1656 free( pbuf );
1657 return ( 0 );
1658}
1659
1660
1661/* ------------------------------------------------------------------------- *
1662 * RPUQueueDefault *
1663 * *
1664 * Sets the requested printer queue as the system default printer. *
1665 * *
1666 * REXX ARGUMENTS: *
1667 * 1. The name of the printer queue to set as default. (REQUIRED) *
1668 * *
1669 * REXX RETURN VALUE: *
1670 * 1 on success, or 0 if an error occurred. *
1671 * ------------------------------------------------------------------------- */
1672ULONG APIENTRY RPUQueueDefault( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1673{
1674 HOBJECT hObj;
1675 BOOL fRC;
1676
1677 // Reset the error indicator
1678 WriteErrorCode( 0, NULL );
1679
1680 // Make sure we have at least one valid argument (the queue name)
1681 if ( argc != 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
1682 pszName = argv[0].strptr;
1683
1684 hObj = PrinterObjectHandle( pszName );
1685 if ( !hObj ) return ( 40 );
1686
1687 fRC = WinSetObjectData( hObj, "APPDEFAULT=YES;");
1688
[32]1689 SaveResultString( prsResult, fRC? PSZ_ONE: PSZ_ZERO, 1 );
[5]1690 return ( 0 );
1691}
1692
1693
1694/* ------------------------------------------------------------------------- *
1695 * RPUQueueHold *
1696 * *
1697 * Holds or releases the specified print queue. *
1698 * *
1699 * REXX ARGUMENTS: *
1700 * 1. The name of the printer queue to hold or release. (REQUIRED) *
1701 * 2. Action flag, one of: *
1702 * Y Hold printer (DEFAULT) *
1703 * N Release printer *
1704 * *
1705 * REXX RETURN VALUE: *
1706 * 1 on success, or 0 if an error occurred. *
1707 * ------------------------------------------------------------------------- */
1708ULONG APIENTRY RPUQueueHold( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1709{
1710 PSZ pszQueueName; // Print queue name
1711 BOOL fHold = TRUE;
1712 SPLERR rc;
1713
1714
1715 // Reset the error indicator
1716 WriteErrorCode( 0, NULL );
1717
1718 // Make sure we have at least one valid argument (the queue name)
1719 if ( argc < 1 || argc > 2 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
1720 pszQueueName = argv[0].strptr;
1721
1722 // Second argument: view (optional, but must be correct if specified)
1723 if ( argc == 2 ) {
1724 if ( RXVALIDSTRING(argv[1]) ) {
1725 switch ( argv[1].strptr[0] ) {
1726 case 'y':
1727 case 'Y': fHold = TRUE; break;
1728 case 'n':
1729 case 'N': fHold = FALSE; break;
1730 default : return ( 40 );
1731 }
1732 }
1733 else return ( 40 );
1734 }
1735
1736 rc = fHold ? SplHoldQueue( NULL, pszQueueName ) :
1737 SplReleaseQueue( NULL, pszQueueName );
1738 if ( rc != NO_ERROR ) {
1739 WriteErrorCode( rc, fHold ? "SplHoldQueue" : "SplReleaseQueue");
[32]1740 SaveResultString( prsResult, PSZ_ZERO, 1 );
[5]1741 }
[32]1742 else
1743 SaveResultString( prsResult, PSZ_ONE, 1 );
[5]1744
1745 return ( 0 );
1746}
1747
1748
[3]1749/* ************************************************************************* *
1750 * INTERNAL PRINTER-RELATED FUNCTIONS *
1751 * ************************************************************************* */
1752
1753/* ------------------------------------------------------------------------- *
1754 * PrinterObjectHandle *
1755 * *
1756 * Given a print queue name, obtain the handle to the WPS printer object *
1757 * associated with that queue. This is done by querying OS2.INI for the *
1758 * queues defined in PM_PrintObject and reading the associated handle IDs. *
1759 * *
1760 * ARGUMENTS: *
1761 * PSZ pszQueueName: The printer queue name. This is NOT the same as the *
1762 * printer device name, although in many cases they may *
1763 * have the same value. *
1764 * *
1765 * RETURNS: HOBJECT *
1766 * WPS object handle of the printer object; NULLHANDLE if no object was *
1767 * found or an error occurred. *
1768 * ------------------------------------------------------------------------- */
1769HOBJECT PrinterObjectHandle( PSZ pszQueueName )
1770{
[11]1771 PVOID pbuf = NULL,
1772 pdata = NULL;
1773 PBYTE pb,
1774 pbEnd,
1775 pbGrpEnd;
1776 POBJINFO pInfo = NULL;
1777 POITAG pTag = NULL;
1778 PSZ psz,
1779 pszName;
1780 CHAR szValue[ 256 ],
1781 szObject[ 5 ];
[3]1782 ULONG cbTotal = 0,
[11]1783 cbActual = 0,
1784 ulHandle;
1785 HOBJECT hObj = 0;
1786 BOOL fFound,
1787 fRC;
[3]1788
1789
1790 fRC = PrfQueryProfileSize( HINI_USERPROFILE,
1791 APPNAME_PM_PRINT_OBJECT, NULL, &cbTotal );
1792 if ( !fRC || !cbTotal ) {
1793 //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileSize");
1794 return NULLHANDLE;
1795 }
1796 pbuf = malloc( cbTotal );
1797 if ( !pbuf ) {
1798 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
1799 return NULLHANDLE;
1800 }
1801
1802 cbActual = PrfQueryProfileString( HINI_USERPROFILE, APPNAME_PM_PRINT_OBJECT,
1803 NULL, NULL, pbuf, cbTotal );
1804 if ( !cbActual ) {
1805 //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString");
1806 return NULLHANDLE;
1807 }
1808
[11]1809 fFound = FALSE;
[3]1810 psz = (PSZ) pbuf;
[11]1811 while ( *psz && !fFound ) {
1812#if 1
1813
1814 /* Just checking the queue names associated with the handle in
1815 * PM_PrintObject is not reliable, because remote (LAN) printers
1816 * will store the name of the _remote_ queue there (which is not
1817 * only impossible to map to a local queue name, it may actually
1818 * be the same AS a local queue name... for a different printer).
1819 *
1820 * So what we do here is parse the queue name out of the object
1821 * settings (from PM_Abstract:Objects). We also have to handle
1822 * remote printers slightly differently from local ones.
1823 *
1824 * I am indebted to Henk Kelder's WPTOOLS source code for providing
1825 * the basic logic, although the code below is largely original.
1826 */
1827
1828 if ( sscanf( psz, "%u", &ulHandle ) && LOUSHORT( ulHandle )) {
1829 sprintf( szObject, "%X", LOUSHORT( ulHandle ));
1830 fRC = PrfQueryProfileSize( HINI_USERPROFILE, APPNAME_PM_ABS_OBJECTS,
1831 szObject, &cbTotal );
1832 if ( !fRC || ( cbTotal < 25 ) || ((pdata = malloc( cbTotal )) == NULL )) {
1833 psz += strlen( psz ) + 1;
1834 continue;
1835 }
1836 if ( PrfQueryProfileData( HINI_USERPROFILE, APPNAME_PM_ABS_OBJECTS,
1837 szObject, (PVOID) pdata, &cbTotal ))
1838 {
1839 BOOL fRemote;
1840 pbEnd = (PBYTE) pdata + cbTotal;
1841 pszName = (PSZ) pdata + sizeof( ULONG );
1842 fRemote = strcmp( pszName, "WPRPrinter") ? FALSE : TRUE;
1843 pb = (PBYTE) pszName + strlen( pszName ) + 17;
1844 while (( pb < pbEnd ) && !fFound ) {
1845 pInfo = (POBJINFO) pb;
1846 pb += sizeof( OBJINFO ) + strlen( pInfo->szName );
1847 pbGrpEnd = pb + pInfo->cbData;
1848 if ( pbGrpEnd > pbEnd ) pbGrpEnd = pbEnd;
1849 while (( pb < pbGrpEnd ) && !fFound ) {
1850 pTag = (POITAG) pb;
1851 pb += sizeof( OITAG );
1852 /* For network printers, tag ID 3 indicates the name of
1853 * the queue on the remote server. We want the local
1854 * queue name, which has tag ID 13.
1855 */
1856 if ( fRemote && ( pTag->usTag == ID_OBJINFO_RPQUEUENAME )) {
1857 memcpy( szValue, pb, pTag->cbTag );
1858 if ( !strcmp( szValue, pszQueueName )) {
1859 hObj = (HOBJECT) ulHandle;
1860 fFound = TRUE;
1861 }
1862 }
1863 // For local printers, just look for tag ID 3
1864 else if ( pTag->usTag == ID_OBJINFO_QUEUENAME ) {
1865 memcpy( szValue, pb, pTag->cbTag );
1866 if ( !strcmp( szValue, pszQueueName )) {
1867 hObj = (HOBJECT) ulHandle;
1868 fFound = TRUE;
1869 }
1870 }
1871 pb += pTag->cbTag;
1872 }
1873 }
1874 }
1875 free( pdata );
1876 }
1877#else
1878 /* Old method, do not use (unreliable for the reasons noted above).
1879 */
[3]1880 if ( !PrfQueryProfileString( HINI_USERPROFILE, APPNAME_PM_PRINT_OBJECT,
1881 psz, NULL, szValue, 255 ))
1882 {
1883 //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString");
1884 break;
1885 }
1886 if (( strcmp( szValue, pszQueueName ) == 0 ) &&
1887 ( sscanf( psz, "%u", (PULONG) &hObj ) == 1 ))
1888 {
[11]1889 fFound = TRUE;
[3]1890 break;
1891 }
[11]1892#endif
[3]1893 psz += strlen( psz ) + 1;
1894 }
1895
1896 free ( pbuf );
1897 return ( hObj );
1898}
1899
1900
1901/* ------------------------------------------------------------------------- *
1902 * GetObjectID *
1903 * *
1904 * Given a WPS object handle, find out the corresponding object ID if one *
1905 * exists. *
1906 * *
1907 * ARGUMENTS: *
1908 * PSZ pszHandle: The WPS object handle, as a hexadecimal string. This *
1909 * should have no prefix, and be all upper-case. *
1910 * *
1911 * RETURNS: PSZ *
1912 * The object ID string. It is up to the caller to free it when done. *
1913 * This will be NULL if no object ID was found. *
1914 * ------------------------------------------------------------------------- */
1915PSZ GetObjectID( PSZ pszHandle )
1916{
1917 PVOID pbuf = NULL;
1918 ULONG cbTotal = 0,
1919 cbActual = 0;
1920 CHAR szValue[ 9 ];
1921 PSZ psz,
1922 pszObjID = NULL;
1923 BOOL fRC;
1924
1925
1926 fRC = PrfQueryProfileSize( HINI_USERPROFILE,
1927 APPNAME_PM_WPS_LOCATION, NULL, &cbTotal );
1928 if ( !fRC || !cbTotal ) {
1929 //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileSize");
1930 return NULL;
1931 }
1932 pbuf = malloc( cbTotal );
1933 if ( !pbuf ) {
1934 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
1935 return NULL;
1936 }
1937
1938 cbActual = PrfQueryProfileString( HINI_USERPROFILE, APPNAME_PM_WPS_LOCATION,
1939 NULL, NULL, pbuf, cbTotal );
1940 if ( !cbActual ) {
1941 //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString");
1942 return NULL;
1943 }
1944
1945 psz = (PSZ) pbuf;
1946 while ( *psz ) {
1947 if ( !PrfQueryProfileString( HINI_USERPROFILE, APPNAME_PM_WPS_LOCATION,
1948 psz, NULL, szValue, 8 ))
1949 {
1950 //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString");
1951 break;
1952 }
1953 if (( strcmp( szValue, pszHandle ) == 0 ) &&
1954 (( pszObjID = strdup( psz )) != NULL ))
1955 {
1956 break;
1957 }
1958 psz += strlen( psz ) + 1;
1959 }
1960
1961 free ( pbuf );
1962 return ( pszObjID );
1963}
1964
1965
[8]1966/* ------------------------------------------------------------------------- *
1967 * UniqueDeviceName *
1968 * *
1969 * Check (and, if necessary, modify) the specified print device name to make *
1970 * sure it is unique. *
1971 * *
1972 * ARGUMENTS: *
1973 * PSZ pszName : Pointer to device name buffer (9-byte CHAR buffer) *
1974 * *
1975 * RETURNS: ULONG *
1976 * 0 on success, 1 if no unique name could be generated, or the return *
1977 * code from SplEnumDevice() if an error occurred. *
1978 * ------------------------------------------------------------------------- */
1979ULONG UniqueDeviceName( PSZ pszName )
1980{
1981 PBYTE pBuf;
1982 PPRDINFO3 pprd3;
1983 CHAR szNumber[ US_PRTDEV_MAXZ ] = {0};
1984 ULONG i, n, pos,
1985 ulNumber = 0,
1986 ulAvail = 0,
1987 cbBuf = 0;
1988 BOOL fUnique = FALSE;
1989 SPLERR rc;
1990
1991
1992 rc = SplEnumDevice( NULL, 3, NULL, 0, &ulNumber, &ulAvail, &cbBuf, NULL );
1993 if ( rc == ERROR_MORE_DATA || rc == NERR_BufTooSmall ) {
1994 pBuf = malloc( cbBuf );
1995 if ( pBuf ) {
1996 rc = SplEnumDevice( NULL, 3, pBuf, cbBuf, &ulNumber, &ulAvail, &cbBuf, NULL );
1997 if ( rc == NO_ERROR ) {
1998 n = 1;
1999 while ( !fUnique && ( n < 999 )) { // max 999 as a sanity check
2000 for ( i = 0; i < ulNumber; i++ ) {
2001 pprd3 = (PPRDINFO3) pBuf + i;
2002 if ( stricmp( pszName, pprd3->pszPrinterName ) == 0 ) break;
2003 }
2004 if ( i >= ulNumber ) fUnique = TRUE;
2005 else {
2006 sprintf( szNumber, "%u", n++ );
2007 pos = strlen( pszName ) - strlen( szNumber );
2008 pszName[ pos ] = '\0';
2009 strncat( pszName, szNumber, US_PRTDEV_MAXZ-1 );
2010 }
2011 }
2012 }
2013 free( pBuf );
[9]2014 if ( rc == NO_ERROR && !fUnique ) return 1;
[8]2015 }
2016 }
[9]2017 else if ( rc == NO_ERROR ) fUnique = TRUE;
2018 return rc;
[8]2019}
2020
2021
[3]2022/* ************************************************************************* *
2023 * INTERNAL REXX DLL UTILITY FUNCTIONS *
2024 * ************************************************************************* */
2025
[32]2026#ifdef NO_SHARED_SOURCE
[3]2027
2028/* ------------------------------------------------------------------------- *
2029 * SaveResultString *
2030 * *
2031 * Writes new string contents to the specified RXSTRING, allocating any *
2032 * additional memory that may be required. If the string to be written has *
2033 * zero length, nothing is done. *
2034 * *
[32]2035 * This function should be used in place of MAKERXSTRING to generate all *
2036 * REXX return values. *
[3]2037 * *
2038 * ARGUMENTS: *
2039 * PRXSTRING prsResult: Pointer to an existing RXSTRING for writing. *
2040 * PCH pchBytes : The string contents to write to prsResult. *
2041 * ULONG ulBytes : The number of bytes in pchBytes to write. *
2042 * *
2043 * RETURNS: BOOL *
2044 * TRUE if prsResult was successfully updated. FALSE otherwise. *
2045 * ------------------------------------------------------------------------- */
2046BOOL SaveResultString( PRXSTRING prsResult, PCH pchBytes, ULONG ulBytes )
2047{
2048 ULONG ulRC;
2049 PCH pchNew;
2050
2051 if ( ulBytes == 0 ) return ( FALSE );
2052 if ( ulBytes > 256 ) {
2053 // REXX provides 256 bytes by default; allocate more if necessary
2054 ulRC = DosAllocMem( (PVOID) &pchNew, ulBytes, PAG_WRITE | PAG_COMMIT );
2055 if ( ulRC != 0 ) {
2056 WriteErrorCode( ulRC, "DosAllocMem");
2057 return ( FALSE );
2058 }
2059 prsResult->strptr = pchNew;
2060 }
2061 memcpy( prsResult->strptr, pchBytes, ulBytes );
2062 prsResult->strlength = ulBytes;
2063
2064 return ( TRUE );
2065}
2066
2067
2068/* ------------------------------------------------------------------------- *
2069 * WriteStemElement *
2070 * *
2071 * Creates a stem element (compound variable) in the calling REXX program *
2072 * using the REXX shared variable pool interface. *
2073 * *
2074 * ARGUMENTS: *
2075 * PSZ pszStem : The name of the stem (before the '.') *
2076 * ULONG ulIndex : The number of the stem element (after the '.') *
2077 * PSZ pszValue : The value to write to the compound variable. *
2078 * *
2079 * RETURNS: BOOL *
2080 * TRUE on success, FALSE on failure. *
2081 * ------------------------------------------------------------------------- */
2082BOOL WriteStemElement( PSZ pszStem, ULONG ulIndex, PSZ pszValue )
2083{
2084 SHVBLOCK shvVar; // REXX shared variable pool block
2085 ULONG ulRc,
2086 ulBytes;
[27]2087 CHAR szCompoundName[ US_COMPOUND_MAXZ ];
[3]2088
2089 sprintf( szCompoundName, "%s.%d", pszStem, ulIndex );
2090 if ( pszValue == NULL ) {
[27]2091 pszValue = "";
[3]2092 ulBytes = 0;
2093 } else {
2094 ulBytes = strlen( pszValue );
2095 }
2096 MAKERXSTRING( shvVar.shvname, szCompoundName, strlen(szCompoundName) );
[27]2097 shvVar.shvvalue.strptr = pszValue;
[3]2098 shvVar.shvvalue.strlength = ulBytes;
2099 shvVar.shvnamelen = RXSTRLEN( shvVar.shvname );
2100 shvVar.shvvaluelen = RXSTRLEN( shvVar.shvvalue );
2101 shvVar.shvcode = RXSHV_SYSET;
2102 shvVar.shvnext = NULL;
2103 ulRc = RexxVariablePool( &shvVar );
2104 if ( ulRc > 1 ) {
2105 WriteErrorCode( shvVar.shvret, "RexxVariablePool (SHVBLOCK.shvret)");
2106 return FALSE;
2107 }
2108 return TRUE;
2109
2110}
2111
2112
2113/* ------------------------------------------------------------------------- *
2114 * WriteCompoundVariable *
2115 * *
2116 * Creates a compound variable in the calling REXX program using the REXX *
2117 * shared variable pool interface. *
2118 * *
2119 * ARGUMENTS: *
2120 * PSZ pszStem : The name of the stem (before the '.') *
2121 * PSZ pszTail : The name of the trailing portion (after the '.') *
2122 * PSZ pszValue : The value to write to the compound variable. *
2123 * *
2124 * RETURNS: BOOL *
2125 * TRUE on success, FALSE on failure. *
2126 * ------------------------------------------------------------------------- */
2127BOOL WriteCompoundVariable( PSZ pszStem, PSZ pszTail, PSZ pszValue )
2128{
2129 SHVBLOCK shvVar; // REXX shared variable pool block
2130 ULONG ulRc,
2131 ulBytes;
[27]2132 CHAR szCompoundName[ US_COMPOUND_MAXZ ];
[3]2133
2134 sprintf( szCompoundName, "%s.%s", pszStem, pszTail );
2135 if ( pszValue == NULL ) {
[27]2136 pszValue = "";
[3]2137 ulBytes = 0;
2138 } else {
2139 ulBytes = strlen( pszValue );
2140 }
2141 MAKERXSTRING( shvVar.shvname, szCompoundName, strlen(szCompoundName) );
[27]2142 shvVar.shvvalue.strptr = pszValue;
[3]2143 shvVar.shvvalue.strlength = ulBytes;
2144 shvVar.shvnamelen = RXSTRLEN( shvVar.shvname );
2145 shvVar.shvvaluelen = RXSTRLEN( shvVar.shvvalue );
2146 shvVar.shvcode = RXSHV_SYSET;
2147 shvVar.shvnext = NULL;
2148 ulRc = RexxVariablePool( &shvVar );
2149 if ( ulRc > 1 ) {
2150 WriteErrorCode( shvVar.shvret, "RexxVariablePool (SHVBLOCK.shvret)");
2151 return FALSE;
2152 }
2153 return TRUE;
2154}
2155
2156
2157/* ------------------------------------------------------------------------- *
[13]2158 * WriteSimpleVariable *
2159 * *
2160 * Creates a variable in the calling REXX program using the REXX shared *
2161 * variable pool interface. *
2162 * *
2163 * ARGUMENTS: *
2164 * PSZ pszName : The name of the variable to write. *
2165 * PSZ pszValue : The value to write to the variable. *
2166 * *
2167 * RETURNS: BOOL *
2168 * ------------------------------------------------------------------------- */
2169BOOL WriteSimpleVariable( PSZ pszName, PSZ pszValue )
2170{
2171 SHVBLOCK shvVar; // REXX shared variable pool block
2172 ULONG ulRc;
2173 CHAR szText[ US_COMPOUND_MAXZ ];
2174
2175 strncpy( szText, pszValue, US_COMPOUND_MAXZ-1 );
2176 MAKERXSTRING( shvVar.shvname, pszName, strlen(pszName) );
2177 MAKERXSTRING( shvVar.shvvalue, szText, strlen(szText) );
2178 shvVar.shvnamelen = RXSTRLEN( shvVar.shvname );
2179 shvVar.shvvaluelen = RXSTRLEN( shvVar.shvvalue );
2180 shvVar.shvcode = RXSHV_SYSET;
2181 shvVar.shvnext = NULL;
2182 ulRc = RexxVariablePool( &shvVar );
2183 if ( ulRc > 1 ) {
2184 WriteErrorCode( shvVar.shvret, "RexxVariablePool (SHVBLOCK.shvret)");
2185 return FALSE;
2186 }
2187 return TRUE;
2188}
2189
2190
2191/* ------------------------------------------------------------------------- *
[3]2192 * WriteErrorCode *
2193 * *
2194 * Writes an error code to a special variable in the calling REXX program *
2195 * using the REXX shared variable pool interface. This is used to return *
2196 * API error codes to the REXX program, since the REXX functions themselves *
2197 * normally return string values. *
2198 * *
2199 * ARGUMENTS: *
2200 * ULONG ulError : The error code returned by the failing API call. *
2201 * PSZ pszContext: A string describing the API call that failed. *
2202 * *
2203 * RETURNS: N/A *
2204 * ------------------------------------------------------------------------- */
2205void WriteErrorCode( ULONG ulError, PSZ pszContext )
2206{
2207 SHVBLOCK shvVar; // REXX shared variable pool block
2208 ULONG ulRc;
2209 CHAR szErrorText[ US_ERRSTR_MAXZ ];
2210
2211 if ( pszContext == NULL )
2212 sprintf( szErrorText, "%X", ulError );
2213 else
2214 sprintf( szErrorText, "%X: %s", ulError, pszContext );
2215 MAKERXSTRING( shvVar.shvname, SZ_ERROR_NAME, strlen(SZ_ERROR_NAME) );
2216 MAKERXSTRING( shvVar.shvvalue, szErrorText, strlen(szErrorText) );
2217 shvVar.shvnamelen = RXSTRLEN( shvVar.shvname );
2218 shvVar.shvvaluelen = RXSTRLEN( shvVar.shvvalue );
2219 shvVar.shvcode = RXSHV_SYSET;
2220 shvVar.shvnext = NULL;
2221 ulRc = RexxVariablePool( &shvVar );
2222 if ( ulRc > 1 )
2223 printf("Unable to set %s: rc = %d\n", shvVar.shvname.strptr, shvVar.shvret );
2224}
2225
[32]2226#endif
Note: See TracBrowser for help on using the repository browser.