source: rxprtutl/trunk/rxprtutl.c@ 32

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

Move several common functions into shared directory.

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