source: rxprtutl/trunk/rxprtutl.c@ 9

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

Fixed device name auto-generation on systems with no existing print devices.

File size: 76.3 KB
Line 
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
57
58// CONSTANTS
59
60#define SZ_LIBRARY_NAME "RXPRTUTL" // Name of this library
61#define SZ_ERROR_NAME "RPUERROR" // REXX variable used to store error codes
62#define SZ_VERSION "0.2.0" // Current version of this library
63
64#define APPNAME_LEAD_STR "PM_"
65#define APPNAME_PM_PORT_DRIVER "PM_PORT_DRIVER"
66#define APPNAME_PM_SPOOLER_PORT "PM_SPOOLER_PORT"
67#define APPNAME_PM_PRINT_OBJECT "PM_PrintObject"
68#define APPNAME_PM_WPS_LOCATION "PM_Workplace:Location"
69
70// Values that should be defined in pmsplb.h if it actually existed
71#define TYPE_LONG_WAIT 2
72#define BIDI_SET_PORTDRV 0x19
73
74// Values used by WinOpenObject
75#define OPEN_DEFAULT 0
76#define OPEN_CONTENTS 1
77#define OPEN_SETTINGS 2
78#define OPEN_HELP 3
79#define OPEN_TREE 101
80#define OPEN_DETAILS 102
81
82// Maximum string lengths...
83#define US_COMPOUND_MAXZ 250 // ...of a compound variable
84#define US_INTEGER_MAXZ 12 // ...of an integer string
85#define US_STEM_MAXZ ( US_COMPOUND_MAXZ - US_INTEGER_MAXZ ) // ...of a stem
86#define US_ERRSTR_MAXZ 250 // ...of an error string
87#define US_DRVINFO_MAXZ ( CCHMAXPATH + 8 + 32 ) // ...of an driver/port info string
88#define US_PRTINFO_MAXZ 180 // ...of a printer info string
89#define US_PORT_MAXZ 64 // ...of a port name
90#define US_PRTDEV_MAXZ 9 // ...of a print device name
91
92// List of functions to be registered by RPULoadFuncs
93static PSZ RxFunctionTbl[] = {
94 "RPUDropFuncs",
95 "RPUEnumModels",
96 "RPUEnumDrivers",
97 "RPUEnumPorts",
98 "RPUEnumPrinters",
99 "RPUQueueDefault",
100 "RPUQueueHold",
101 "RPUOpenView",
102 "RPUPortDialog",
103 "RPUPortInstall",
104 "RPUPortSet",
105 "RPUPrinterCreate",
106 "RPUPrinterDelete",
107 "RPUPrinterQuery",
108 "RPUVersion"
109};
110
111
112// FUNCTION DECLARATIONS
113
114// Exported REXX functions
115RexxFunctionHandler RPULoadFuncs;
116RexxFunctionHandler RPUDropFuncs;
117RexxFunctionHandler RPUVersion;
118RexxFunctionHandler RPUEnumModels;
119RexxFunctionHandler RPUEnumDrivers;
120RexxFunctionHandler RPUEnumPorts;
121RexxFunctionHandler RPUEnumPrinters;
122RexxFunctionHandler RPUOpenView;
123RexxFunctionHandler RPUPortDialog;
124RexxFunctionHandler RPUPortInstall;
125RexxFunctionHandler RPUPortSet;
126RexxFunctionHandler RPUPrinterCreate;
127RexxFunctionHandler RPUPrinterDelete;
128RexxFunctionHandler RPUPrinterQuery;
129RexxFunctionHandler RPUQueueDefault;
130RexxFunctionHandler RPUQueueHold;
131
132// TODO
133RexxFunctionHandler RPUPortDelete;
134// - function to delete a printer (object, device and queue)
135
136// Internal functions
137PSZ GetObjectID( PSZ pszHandle );
138HOBJECT PrinterObjectHandle( PSZ pszQueueName );
139BOOL SaveResultString( PRXSTRING prsResult, PCH pchBytes, ULONG ulBytes );
140ULONG UniqueDeviceName( PSZ pszName );
141BOOL WriteCompoundVariable( PSZ pszStem, PSZ pszTail, PSZ pszValue );
142BOOL WriteStemElement( PSZ pszStem, ULONG ulIndex, PSZ pszValue );
143void WriteErrorCode( ULONG ulError, PSZ pszContext );
144
145
146
147/* ************************************************************************* *
148 * EXPORTED REXX FUNCTIONS *
149 * ************************************************************************* */
150
151/* ------------------------------------------------------------------------- *
152 * RPULoadFuncs *
153 * *
154 * Register all RPU* REXX functions except this one. *
155 * *
156 * REXX ARGUMENTS: None *
157 * REXX RETURN VALUE: "" *
158 * ------------------------------------------------------------------------- */
159ULONG APIENTRY RPULoadFuncs( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
160{
161 int entries,
162 i;
163
164 // Reset the error indicator
165 WriteErrorCode( 0, NULL );
166
167 if ( argc > 0 ) return ( 40 );
168 entries = sizeof(RxFunctionTbl) / sizeof(PSZ);
169 for ( i = 0; i < entries; i++ )
170 RexxRegisterFunctionDll( RxFunctionTbl[i], SZ_LIBRARY_NAME, RxFunctionTbl[i] );
171
172 MAKERXSTRING( *prsResult, "", 0 );
173 return ( 0 );
174}
175
176
177/* ------------------------------------------------------------------------- *
178 * RPUDropFuncs *
179 * *
180 * Deregister all RPU* REXX functions, including this one. *
181 * *
182 * REXX ARGUMENTS: None *
183 * REXX RETURN VALUE: "" *
184 * ------------------------------------------------------------------------- */
185ULONG APIENTRY RPUDropFuncs( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
186{
187 int entries,
188 i;
189
190 // Reset the error indicator
191 WriteErrorCode( 0, NULL );
192
193 if ( argc > 0 ) return ( 40 );
194 entries = sizeof(RxFunctionTbl) / sizeof(PSZ);
195 for ( i = 0; i < entries; i++ )
196 RexxDeregisterFunction( RxFunctionTbl[i] );
197
198 MAKERXSTRING( *prsResult, "", 0 );
199 return ( 0 );
200}
201
202
203/* ------------------------------------------------------------------------- *
204 * RPUVersion *
205 * *
206 * Returns the current library version. *
207 * *
208 * REXX ARGUMENTS: None *
209 * REXX RETURN VALUE: Current version in the form "major.minor.refresh" *
210 * ------------------------------------------------------------------------- */
211ULONG APIENTRY RPUVersion( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
212{
213 CHAR szVersion[ 12 ];
214
215 // Reset the error indicator
216 WriteErrorCode( 0, NULL );
217
218 if ( argc > 0 ) return ( 40 );
219 sprintf( szVersion, "%s", SZ_VERSION );
220
221 MAKERXSTRING( *prsResult, szVersion, strlen(szVersion) );
222 return ( 0 );
223}
224
225
226/* ------------------------------------------------------------------------- *
227 * RPUEnumModels *
228 * *
229 * Gets a list of the printer models supported by the specified print *
230 * driver. *
231 * *
232 * REXX ARGUMENTS: *
233 * 1. Filespec of the printer driver to query. (REQUIRED) *
234 * 2. The name of the stem in which to return the results. (REQUIRED) *
235 * *
236 * REXX RETURN VALUE: *
237 * 1 on success, or 0 if an error occurred. *
238 * ------------------------------------------------------------------------- */
239ULONG APIENTRY RPUEnumModels( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
240{
241 HAB hab; // desktop anchor-block handle
242 PSZ pszDriver; // print driver filespec
243 CHAR szStem[ US_STEM_MAXZ ], // buffers used for building stem
244 szNumber[ US_INTEGER_MAXZ ]; // ...
245 BOOL fSuccess; // success indicator
246 PSTR32 aDeviceName; // array of device names
247 PSTR64 aDeviceDesc; // array of device descriptions
248 PSTR16 aDataType; // array of data types
249 LONG pldn = 0L, // number of device names/descriptions
250 pldt = 0L, // number of data types
251 i;
252
253
254 // Reset the error indicator
255 WriteErrorCode( 0, NULL );
256
257 // Make sure we have exactly two valid arguments
258 if (( argc != 2 ) ||
259 ( ! RXVALIDSTRING( argv[0] )) ||
260 ( ! RXVALIDSTRING( argv[1] )) ||
261 ( RXSTRLEN( argv[1] ) > US_STEM_MAXZ ))
262 return ( 40 );
263 pszDriver = argv[0].strptr;
264
265 if ( argv[1].strptr[ argv[1].strlength-1 ] == '.') argv[1].strlength--;
266 strncpy( szStem, argv[1].strptr, RXSTRLEN( argv[1] ));
267 szStem[ RXSTRLEN( argv[1] ) ] = '\0';
268
269 hab = WinInitialize( 0 );
270 if ( !hab ) {
271 WriteErrorCode( 0, "WinInitialize");
272 MAKERXSTRING( *prsResult, "0", 1 );
273 return ( 0 );
274 }
275
276 // Query the size of the available data
277 fSuccess = DevQueryDeviceNames( hab, pszDriver, &pldn, NULL, NULL, &pldt, NULL );
278 if ( !fSuccess ) {
279 WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "DevQueryDeviceNames");
280 MAKERXSTRING( *prsResult, "0", 1 );
281 goto cleanup;
282 }
283
284 // Now get the actual data
285 aDeviceName = malloc( pldn * sizeof( STR32 ));
286 aDeviceDesc = malloc( pldn * sizeof( STR64 ));
287 aDataType = malloc( pldt * sizeof( STR16 ));
288 if ( !aDeviceName || !aDeviceDesc || ! aDataType ) {
289 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
290 MAKERXSTRING( *prsResult, "0", 1 );
291 goto cleanup;
292 }
293 fSuccess = DevQueryDeviceNames( hab, pszDriver, &pldn, aDeviceName,
294 aDeviceDesc, &pldt, aDataType );
295 for ( i = 0; i < pldn; i++ ) {
296 WriteStemElement( szStem, i+1, (PSZ)aDeviceName[i] );
297 }
298 sprintf( szNumber, "%u", pldn );
299 WriteStemElement( szStem, 0, szNumber );
300
301 // Return 1 on success
302 MAKERXSTRING( *prsResult, "1", 1 );
303
304 free( aDeviceName );
305 free( aDeviceDesc );
306 free( aDataType );
307
308cleanup:
309 WinTerminate( hab );
310 return ( 0 );
311}
312
313
314/* ------------------------------------------------------------------------- *
315 * RPUEnumPorts *
316 * *
317 * Gets a list of the printer ports currently defined on the system. Returns *
318 * a REXX array (stem) consisting of one string per port, of the form: *
319 * <port> <driver> <driver path> *
320 * *
321 * REXX ARGUMENTS: *
322 * 1. The name of the stem in which to return the results. (REQUIRED) *
323 * *
324 * REXX RETURN VALUE: *
325 * 1 on success, or 0 if an error occurred. *
326 * ------------------------------------------------------------------------- */
327ULONG APIENTRY RPUEnumPorts( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
328{
329 PPRPORTINFO1 pInfo;
330 PVOID pbuf;
331 ULONG cbBuf,
332 cTotal,
333 cReturned,
334 cbNeeded,
335 i;
336 CHAR szStem[ US_STEM_MAXZ ], // buffers used for building stem
337 szNumber[ US_INTEGER_MAXZ ], // ...
338 szInfo[ US_DRVINFO_MAXZ ];
339 SPLERR rc;
340
341 // Reset the error indicator
342 WriteErrorCode( 0, NULL );
343
344 // Make sure we have exactly one valid argument (the stem name)
345 if (( argc != 1 ) || ( ! RXVALIDSTRING( argv[0] )))
346 return ( 40 );
347 if ( argv[0].strptr[ argv[0].strlength-1 ] == '.') argv[0].strlength--;
348 strncpy( szStem, argv[0].strptr, RXSTRLEN( argv[0] ));
349 szStem[ RXSTRLEN( argv[0] ) ] = '\0';
350
351 // Query the amount of available data
352 rc = SplEnumPort( NULL, 1, NULL, 0L, &cReturned, &cTotal, &cbNeeded, NULL );
353 if (( rc != ERROR_MORE_DATA ) && ( rc != NERR_BufTooSmall )) {
354 WriteErrorCode( rc, "SplEnumPort");
355 MAKERXSTRING( *prsResult, "0", 1 );
356 return ( 0 );
357 }
358
359 // Now get the actual data
360 pbuf = malloc( cbNeeded );
361 if ( !pbuf ) {
362 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
363 MAKERXSTRING( *prsResult, "0", 1 );
364 return ( 0 );
365 }
366 cbBuf = cbNeeded;
367 rc = SplEnumPort( NULL, 1, pbuf, cbBuf,
368 &cReturned, &cTotal, &cbNeeded, NULL );
369 if ( rc == NO_ERROR ) {
370 pInfo = (PPRPORTINFO1) pbuf;
371 for ( i = 0; i < cReturned; i++ ) {
372 sprintf( szInfo, "%.32s %.8s %s",
373 pInfo->pszPortName,
374 pInfo->pszPortDriverName? pInfo->pszPortDriverName: "",
375 pInfo->pszPortDriverPathName? pInfo->pszPortDriverPathName: "");
376 WriteStemElement( szStem, i+1, szInfo );
377 pInfo++;
378 }
379 sprintf( szNumber, "%u", cReturned );
380 WriteStemElement( szStem, 0, szNumber );
381 MAKERXSTRING( *prsResult, "1", 1 );
382 }
383 else {
384 WriteErrorCode( rc, "SplEnumPort");
385 MAKERXSTRING( *prsResult, "0", 1 );
386 }
387 free( pbuf );
388
389 return ( 0 );
390}
391
392
393/* ------------------------------------------------------------------------- *
394 * RPUEnumDrivers *
395 * *
396 * Gets a list of the printer drivers currently installed on the system. *
397 * Returns a REXX array (stem) with each item being a driver name string. *
398 * *
399 * REXX ARGUMENTS: *
400 * 1. The name of the stem in which to return the results. (REQUIRED) *
401 * *
402 * REXX RETURN VALUE: *
403 * 1 on success, or 0 if an error occurred. *
404 * ------------------------------------------------------------------------- */
405ULONG APIENTRY RPUEnumDrivers( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
406{
407 PSZ pszDriverName;
408 PBYTE pbuf;
409 ULONG cbBuf,
410 cTotal,
411 cReturned,
412 cbNeeded,
413 i;
414 CHAR szStem[ US_STEM_MAXZ ], // buffers used for building stem
415 szNumber[ US_INTEGER_MAXZ ]; // ...
416 SPLERR rc;
417
418
419 // Reset the error indicator
420 WriteErrorCode( 0, NULL );
421
422 // Make sure we have exactly one valid argument (the stem name)
423 if (( argc != 1 ) || ( ! RXVALIDSTRING( argv[0] )))
424 return ( 40 );
425 if ( argv[0].strptr[ argv[0].strlength-1 ] == '.') argv[0].strlength--;
426 strncpy( szStem, argv[0].strptr, RXSTRLEN( argv[0] ));
427 szStem[ RXSTRLEN( argv[0] ) ] = '\0';
428
429 // Query the amount of available data
430 rc = SplEnumDriver( NULL, 0L, NULL, 0L, &cReturned, &cTotal, &cbNeeded, NULL );
431 if (( rc != ERROR_MORE_DATA ) && ( rc != NERR_BufTooSmall )) {
432 WriteErrorCode( rc, "SplEnumDriver");
433 MAKERXSTRING( *prsResult, "0", 1 );
434 return ( 0 );
435 }
436
437 // Now get the actual data
438 pbuf = (PBYTE) malloc( cbNeeded );
439 if ( !pbuf ) {
440 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
441 MAKERXSTRING( *prsResult, "0", 1 );
442 return ( 0 );
443 }
444 cbBuf = cbNeeded;
445 rc = SplEnumDriver( NULL, 0L, pbuf, cbBuf, &cReturned ,&cTotal, &cbNeeded, NULL ) ;
446 if ( rc == NO_ERROR ) {
447 pszDriverName = (PSZ) pbuf;
448 for ( i = 0; i < cReturned ; i++ ) {
449 WriteStemElement( szStem, i+1, pszDriverName );
450 pszDriverName += DRIV_NAME_SIZE + DRIV_DEVICENAME_SIZE + 2;
451 }
452 sprintf( szNumber, "%u", cReturned );
453 WriteStemElement( szStem, 0, szNumber );
454 MAKERXSTRING( *prsResult, "1", 1 );
455 }
456 else {
457 WriteErrorCode( rc, "SplEnumDriver");
458 MAKERXSTRING( *prsResult, "0", 1 );
459 }
460 free( pbuf );
461
462 return ( 0 );
463}
464
465
466/* ------------------------------------------------------------------------- *
467 * RPUEnumPrinters *
468 * *
469 * Gets a list of the printers currently defined on the system. Returns a *
470 * compound (stem) variable with the following format: *
471 * *
472 * (stem).0 Number of printers *
473 * (stem).i.!name Printer device (a.k.a. physical) name *
474 * (stem).i.!queue Printer queue name ('' for direct printers) *
475 * (stem).i.!host Name of LAN host where printer queue is located *
476 * ('' for non-LAN printers) *
477 * (stem).i.!description Printer description (name of WPS object) *
478 * (stem).i.!handle Printer's object handle ('' for direct printers) *
479 * (stem).i.!flags Zero or more of the following flags (any order): *
480 * D This is the default printer queue *
481 * P Printer queue is paused (held) *
482 * *
483 * REXX ARGUMENTS: *
484 * 1. The name of the stem in which to return the results. (REQUIRED) *
485 * *
486 * REXX RETURN VALUE: *
487 * 1 on success, or 0 if an error occurred. *
488 * ------------------------------------------------------------------------- */
489ULONG APIENTRY RPUEnumPrinters( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
490{
491 PPRINTERINFO pPInfo = NULL; // pointer to enumerated printer information
492 PPRQINFO6 pQInfo = NULL; // queue information (level 6)
493 PVOID pbEnum = NULL, // buffer for SplEnumPrinter
494 pbQueue = NULL; // buffer for SplQueryQueue
495 PSZ pszToken;
496 HOBJECT hObj; // printer WPS object handle
497 ULONG flType = SPL_PR_QUEUE | SPL_PR_DIRECT_DEVICE,
498 cbBuf = 0,
499 cTotal = 0,
500 cReturned = 0,
501 cbNeeded = 0,
502 ulCount = 0, // number of printers found
503 i;
504 CHAR szFlags[ 3 ], // string of flag characters
505 szHandle[ 9 ], // string for hex object handle
506 // buffers used for building compound variables:
507 szStem[ US_STEM_MAXZ - US_INTEGER_MAXZ - 1 ],
508 szNumber[ US_INTEGER_MAXZ ],
509 szStemNode[ US_STEM_MAXZ ];
510 SPLERR rc;
511
512
513 // Reset the error indicator
514 WriteErrorCode( 0, NULL );
515
516 // Make sure we have exactly one valid argument (the stem name)
517 if (( argc != 1 ) || ( ! RXVALIDSTRING( argv[0] )))
518 return ( 40 );
519 if ( argv[0].strptr[ argv[0].strlength-1 ] == '.') argv[0].strlength--;
520 strncpy( szStem, argv[0].strptr, RXSTRLEN( argv[0] ));
521 szStem[ RXSTRLEN( argv[0] ) ] = '\0';
522
523
524 /* Our enumeration query asks for two types of devices: direct (non-queued)
525 * printers, and printer queues.
526 *
527 * In the latter case, it's important to note that we ask for the queues
528 * themselves rather than queued printers. Why? Because we'll need to
529 * gather information about both printers and their underlying queues (some
530 * things associated with printer objects actually belong to the queue
531 * rather than the printer device).
532 *
533 * The thing is, given a printer name, there doesn't seem to be an easy way
534 * to find out the queue associated with it. So we need to do it the other
535 * way around: get the queue names, then we can identify the printers
536 * connected to them.
537 */
538
539 // Query the amount of available data
540 rc = SplEnumPrinter( NULL, 0L, flType, NULL, 0L,
541 &cReturned, &cTotal, &cbNeeded, NULL );
542 if (( rc != ERROR_MORE_DATA ) && ( rc != NERR_BufTooSmall ))
543 // && ( rc != ERROR_INVALID_PARAMETER ))
544 {
545 WriteErrorCode( rc, "SplEnumPrinter");
546 MAKERXSTRING( *prsResult, "0", 1 );
547 return ( 0 );
548 }
549
550 // Now get the actual data
551 pbEnum = malloc( cbNeeded );
552 if ( !pbEnum ) {
553 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
554 MAKERXSTRING( *prsResult, "0", 1 );
555 return ( 0 );
556 }
557 cbBuf = cbNeeded;
558 rc = SplEnumPrinter( NULL, 0L, flType, pbEnum, cbBuf,
559 &cReturned, &cTotal, &cbNeeded, NULL );
560 if ( rc != NO_ERROR ) {
561 WriteErrorCode( rc, "SplEnumPrinter");
562 MAKERXSTRING( *prsResult, "0", 1 );
563 goto cleanup;
564 }
565
566 pPInfo = (PPRINTERINFO) pbEnum;
567 for ( i = 0; i < cReturned; i++ ) {
568
569 if ( pPInfo->flType == SPL_PR_DIRECT_DEVICE ) {
570
571 /* Direct print device. No queue, no handle; flags do not apply.
572 * The destination name and description are those of the printer
573 * device itself, so we don't need any additional queries.
574 */
575 sprintf( szStemNode, "%s.%u", szStem, ++ulCount );
576
577 WriteCompoundVariable( szStemNode, "!name",
578 pPInfo->pszPrintDestinationName );
579 WriteCompoundVariable( szStemNode, "!description",
580 pPInfo->pszDescription );
581 WriteCompoundVariable( szStemNode, "!queue", "");
582 WriteCompoundVariable( szStemNode, "!flags", "");
583 WriteCompoundVariable( szStemNode, "!handle", "");
584
585 WriteCompoundVariable( szStemNode, "!host",
586 pPInfo->pszComputerName?
587 pPInfo->pszComputerName: "");
588 }
589 else if ( pPInfo->flType == SPL_PR_QUEUE ) {
590
591 /* Print queue. We need to query the queue to find out the details
592 * of the print device(s) connected to it.
593 */
594 rc = SplQueryQueue( NULL, pPInfo->pszPrintDestinationName,
595 6L, NULL, 0L, &cbNeeded );
596 if (( rc != NO_ERROR ) && ( rc != NERR_BufTooSmall )) {
597 WriteErrorCode( rc, "SplQueryQueue");
598 MAKERXSTRING( *prsResult, "0", 1 );
599 goto cleanup;
600 }
601 pbQueue = malloc( cbNeeded );
602 if ( !pbQueue ) {
603 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
604 MAKERXSTRING( *prsResult, "0", 1 );
605 goto cleanup;
606 }
607 cbBuf = cbNeeded;
608 rc = SplQueryQueue( NULL, pPInfo->pszPrintDestinationName,
609 6L, pbQueue, cbBuf, &cbNeeded );
610 if ( rc != NO_ERROR ) {
611 WriteErrorCode( rc, "SplQueryQueue");
612 MAKERXSTRING( *prsResult, "0", 1 );
613 free( pbQueue );
614 goto cleanup;
615 }
616 pQInfo = (PPRQINFO6) pbQueue;
617
618 // Now go through the list of printers connected to this queue...
619 pszToken = strtok( pQInfo->pszPrinters, ",");
620 while ( pszToken ) {
621 sprintf( szStemNode, "%s.%u", szStem, ++ulCount );
622 WriteCompoundVariable( szStemNode, "!name", pszToken );
623 WriteCompoundVariable( szStemNode, "!description", pQInfo->pszComment );
624 WriteCompoundVariable( szStemNode, "!queue", pQInfo->pszName );
625 WriteCompoundVariable( szStemNode, "!driver", pQInfo->pszDriverName );
626 WriteCompoundVariable( szStemNode, "!host",
627 pQInfo->pszRemoteComputerName ?
628 pQInfo->pszRemoteComputerName: "");
629
630 hObj = PrinterObjectHandle( pQInfo->pszName );
631 if ( hObj )
632 sprintf( szHandle, "%X", hObj );
633 else
634 sprintf( szHandle, "");
635 WriteCompoundVariable( szStemNode, "!handle", szHandle );
636
637 memset( szFlags, 0, sizeof( szFlags ));
638 if ( pQInfo->fsType & PRQ3_TYPE_APPDEFAULT )
639 strcat( szFlags, "D");
640 if ( pQInfo->fsStatus & PRQ3_PAUSED )
641 strcat( szFlags, "P");
642 WriteCompoundVariable( szStemNode, "!flags", szFlags );
643
644 pszToken = strtok( NULL, ",");
645 } // while
646
647 free( pbQueue );
648 }
649
650 pPInfo++;
651 }
652 sprintf( szNumber, "%u", ulCount );
653 WriteStemElement( szStem, 0, szNumber );
654 MAKERXSTRING( *prsResult, "1", 1 );
655
656cleanup:
657 free( pbEnum );
658 return ( 0 );
659}
660
661
662/* ------------------------------------------------------------------------- *
663 * RPUOpenView *
664 * *
665 * Opens the requested view of the WPS object corresponding to the specified *
666 * print queue. (Note that WPS printer objects are associated with a queue *
667 * rather than a printer device, so it is the queue name that must be passed *
668 * to this function.) *
669 * *
670 * REXX ARGUMENTS: *
671 * 1. The name of the printer queue for the object to open. (REQUIRED) *
672 * 2. Optional view flag, one of: *
673 * O Object's default view (DEFAULT) *
674 * D Details view *
675 * I Icon view *
676 * S Settings *
677 * *
678 * REXX RETURN VALUE: *
679 * 1 on success, or 0 if an error occurred. *
680 * ------------------------------------------------------------------------- */
681ULONG APIENTRY RPUOpenView( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
682{
683 HOBJECT hObj;
684 ULONG ulView = OPEN_DEFAULT;
685 BOOL fRC;
686
687
688 // Reset the error indicator
689 WriteErrorCode( 0, NULL );
690
691 // Make sure we have at least one valid argument (the queue name)
692 if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
693 pszName = argv[0].strptr;
694
695 // Second argument: view (optional, but must be correct if specified)
696 if ( argc == 2 ) {
697 if ( RXVALIDSTRING(argv[1]) ) {
698 switch ( argv[1].strptr[0] ) {
699 case 'o':
700 case 'O': ulView = OPEN_DEFAULT; break;
701 case 'd':
702 case 'D': ulView = OPEN_DETAILS; break;
703 case 'i':
704 case 'I': ulView = OPEN_CONTENTS; break;
705 case 's':
706 case 'S': ulView = OPEN_SETTINGS; break;
707 default : return ( 40 );
708 }
709 }
710 else return ( 40 );
711 }
712 if ( argc > 2 ) return ( 40 );
713
714 hObj = PrinterObjectHandle( pszName );
715 if ( !hObj ) return ( 40 );
716
717 fRC = WinOpenObject( hObj, ulView, TRUE );
718
719 MAKERXSTRING( *prsResult, fRC? "1": "0", 1 );
720 return ( 0 );
721}
722
723
724/* ------------------------------------------------------------------------- *
725 * RPUPortInstall *
726 * *
727 * Creates a new printer port. *
728 * *
729 * REXX ARGUMENTS: *
730 * 1. The name of the port driver without any extension. This must be *
731 * installed and registered in OS2.INI already. (REQUIRED) *
732 * 2. The name of the new port to be created. If not specified, the port *
733 * driver will be responsible for using a default name. (DEFAULT: none) *
734 * *
735 * REXX RETURN VALUE: *
736 * 1 on success, or 0 if an error occurred. *
737 * ------------------------------------------------------------------------- */
738ULONG APIENTRY RPUPortInstall( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
739{
740 HAB hab; // desktop anchor-block handle
741 HMODULE hPdr = NULLHANDLE; // handle to port driver module
742 PFN pfnInstallPort; // pointer to driver's SplPdInstallPort entrypoint
743 ULONG ulCB; // return value from PrfQueryProfileString()
744 PSZ pszPdrName = NULL, // name of the specified port driver
745 pszPortName = NULL; // name of the new port to create
746 CHAR szPathName[ CCHMAXPATH+1 ]; // FQN of the port driver module
747 APIRET rc = 0; // return code from Dos...() functions
748
749
750 // Reset the error indicator
751 WriteErrorCode( 0, NULL );
752
753 // Make sure we have at least one valid argument (the port driver name)
754 if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
755 pszPdrName = argv[0].strptr;
756
757 // Second argument: new port name (optional, but must be correct if specified)
758 if ( argc >= 2 ) {
759 if ( RXVALIDSTRING(argv[1]) )
760 pszPortName = strupr( argv[1].strptr );
761 else return ( 40 );
762 }
763
764 // Get the path to the installed port driver
765 hab = WinInitialize( 0 );
766 if ( !hab ) {
767 WriteErrorCode( 0, "WinInitialize");
768 MAKERXSTRING( *prsResult, "0", 1 );
769 return ( 0 );
770 }
771
772 ulCB = PrfQueryProfileString( HINI_SYSTEMPROFILE, APPNAME_PM_PORT_DRIVER,
773 pszPdrName, NULL, (PVOID) szPathName, CCHMAXPATH );
774 if ( !ulCB ) {
775 WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString");
776 MAKERXSTRING( *prsResult, "0", 1 );
777 goto cleanup;
778 }
779
780 // Load the port driver DLL and register its port install routine
781 rc = DosLoadModule( NULL, 0, szPathName, &hPdr );
782 if ( rc != NO_ERROR || !hPdr ) {
783 WriteErrorCode( rc, "DosLoadModule");
784 MAKERXSTRING( *prsResult, "0", 1 );
785 goto cleanup;
786 }
787 rc = DosQueryProcAddr( hPdr, 0, "SPLPDINSTALLPORT", &pfnInstallPort );
788 if ( rc != NO_ERROR ) {
789 WriteErrorCode( rc, "DosQueryProcAddr");
790 MAKERXSTRING( *prsResult, "0", 1 );
791 goto finish;
792 }
793
794 // Now create the new port (the driver will assign it default settings)
795 rc = pfnInstallPort( hab, pszPortName );
796 if ( rc == NO_ERROR ) {
797 // Return 1 on success
798 MAKERXSTRING( *prsResult, "1", 1 );
799 }
800 else {
801 WriteErrorCode( rc, "SplPdInstallPort");
802 MAKERXSTRING( *prsResult, "0", 1 );
803 }
804
805finish:
806 DosFreeModule( hPdr );
807cleanup:
808 WinTerminate( hab );
809 return ( 0 );
810}
811
812
813/* ------------------------------------------------------------------------- *
814 * RPUPortSet *
815 * *
816 * Sets the specified port's configuration settings. IMPORTANT: not all *
817 * port drivers support this; the standard OS/2 serial and parallel port *
818 * drivers do NOT. Depending on the port driver, there are two possible *
819 * outcomes when this API is not supported: *
820 * A. Port driver does not have an entrypoint for SplPdSet. This is the *
821 * case for any port driver using pre-Warp conventions. RPUPortSet will *
822 * return 0 and RPUERROR will be "1: DosQueryProcAddr". *
823 * B. Port driver supports SplPdSet but does not (correctly) implement the *
824 * BIDI_SET_PORTDRV command. This is the case for some third-party port *
825 * drivers. In this case, whether or not RPUPortSet returns a success *
826 * code entirely depends on how conscientiously the port driver itself *
827 * is written, but the "INITIALIZATION" key (and possibly others) in *
828 * the port's entry in OS2SYS.INI will not be updated. The application *
829 * should always check for this after calling RPUPortSet! *
830 * If the application determines that either of these failure conditions has *
831 * occurred, it is advisable to call RPUPortDialog as a fallback measure. *
832 * *
833 * Because of the above, use of this function is not generally encouraged *
834 * except when absolutely necessary. *
835 * *
836 * REXX ARGUMENTS: *
837 * 1. The name of the port driver without any extension. This must be *
838 * installed and registered in OS2.INI already. (REQUIRED) *
839 * 2. The name of the port to be configured. (REQUIRED) *
840 * 3. Byte sequence representing the new port configuration. The format *
841 * depends on the port driver (and it is up to the caller to know what *
842 * it is). (REQUIRED) *
843 * *
844 * REXX RETURN VALUE: *
845 * 1 on success, or 0 if an error occurred. *
846 * ------------------------------------------------------------------------- */
847ULONG APIENTRY RPUPortSet( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
848{
849 HAB hab; // desktop anchor-block handle
850 HMODULE hPdr = NULLHANDLE; // handle to port driver module
851 PFN pfnSetPort; // pointer to driver's SplPdSet entrypoint
852 ULONG ulCB, // return value from PrfQueryProfileString()
853 cBuf; // size of configuration buffer
854 PSZ pszPdrName = NULL, // name of the specified port driver
855 pszPortName = NULL; // name of the new port to create
856 CHAR szPathName[ CCHMAXPATH+1 ]; // FQN of the port driver module
857 PBYTE pBuf; // Pointer to configuration buffer
858 APIRET rc = 0; // return code from Dos...() functions
859
860
861 // Reset the error indicator
862 WriteErrorCode( 0, NULL );
863
864 // Make sure we have exactly three valid arguments
865 if ( argc != 3 ||
866 !RXVALIDSTRING( argv[0] ) ||
867 !RXVALIDSTRING( argv[1] ) ||
868 !RXVALIDSTRING( argv[2] ) )
869 return ( 40 );
870 pszPdrName = argv[0].strptr;
871 pszPortName = strupr( argv[1].strptr );
872 pBuf = argv[2].strptr;
873 cBuf = argv[2].strlength;
874
875 // Get the path to the installed port driver
876 hab = WinInitialize( 0 );
877 if ( !hab ) {
878 WriteErrorCode( 0, "WinInitialize");
879 MAKERXSTRING( *prsResult, "0", 1 );
880 return ( 0 );
881 }
882
883 ulCB = PrfQueryProfileString( HINI_SYSTEMPROFILE, APPNAME_PM_PORT_DRIVER,
884 pszPdrName, NULL, (PVOID) szPathName, CCHMAXPATH );
885 if ( !ulCB ) {
886 WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString");
887 MAKERXSTRING( *prsResult, "0", 1 );
888 goto cleanup;
889 }
890
891 // Load the port driver DLL and register its configuration routine
892 rc = DosLoadModule( NULL, 0, szPathName, &hPdr );
893 if ( rc != NO_ERROR || !hPdr ) {
894 WriteErrorCode( rc, "DosLoadModule");
895 MAKERXSTRING( *prsResult, "0", 1 );
896 goto cleanup;
897 }
898 rc = DosQueryProcAddr( hPdr, 0, "SPLPDSET", &pfnSetPort );
899 if ( rc != NO_ERROR ) {
900 WriteErrorCode( rc, "DosQueryProcAddr");
901 MAKERXSTRING( *prsResult, "0", 1 );
902 goto finish;
903 }
904
905 // Now set the new port configuration
906 rc = pfnSetPort( pszPortName, TYPE_LONG_WAIT, BIDI_SET_PORTDRV, pBuf, cBuf );
907 if ( rc == NO_ERROR ) {
908 // Return 1 on success
909 MAKERXSTRING( *prsResult, "1", 1 );
910 }
911 else {
912 WriteErrorCode( rc, "SplPdSet");
913 MAKERXSTRING( *prsResult, "0", 1 );
914 }
915
916finish:
917 DosFreeModule( hPdr );
918cleanup:
919 WinTerminate( hab );
920 return ( 0 );
921}
922
923
924/* ------------------------------------------------------------------------- *
925 * RPUPortDialog *
926 * *
927 * Brings up the specified port's configuration dialog. This function can *
928 * only be used from a PM process. *
929 * *
930 * REXX ARGUMENTS: *
931 * 1. The name of the port driver without any extension. This must be *
932 * installed and registered in OS2.INI already. (REQUIRED) *
933 * 2. The name of the port to be configured. (REQUIRED) *
934 * *
935 * REXX RETURN VALUE: *
936 * 1 if the user modified the port configuration. *
937 * 0 if no changes were made, or if an error occurred. *
938 * ------------------------------------------------------------------------- */
939ULONG APIENTRY RPUPortDialog( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
940{
941 HAB hab; // desktop anchor-block handle
942 HMODULE hPdr = NULLHANDLE; // handle to port driver module
943 PFN pfnSetPort; // pointer to driver's SplPdSet entrypoint
944 ULONG ulCB, // return value from PrfQueryProfileString()
945 flModified = 0; // flag indicating settings were changed
946 PSZ pszPdrName = NULL, // name of the specified port driver
947 pszPortName = NULL; // name of the new port to create
948 CHAR szPathName[ CCHMAXPATH+1 ]; // FQN of the port driver module
949 APIRET rc = 0; // return code from Dos...() functions
950
951
952 // Reset the error indicator
953 WriteErrorCode( 0, NULL );
954
955 // Make sure we have exactly two valid arguments
956 if ( argc != 2 ||
957 !RXVALIDSTRING( argv[0] ) ||
958 !RXVALIDSTRING( argv[1] ) )
959 return ( 40 );
960 pszPdrName = argv[0].strptr;
961 pszPortName = strupr( argv[1].strptr );
962
963 // Get the path to the installed port driver
964 hab = WinInitialize( 0 );
965 if ( !hab ) {
966 WriteErrorCode( 0, "WinInitialize");
967 MAKERXSTRING( *prsResult, "0", 1 );
968 return ( 0 );
969 }
970
971 ulCB = PrfQueryProfileString( HINI_SYSTEMPROFILE, APPNAME_PM_PORT_DRIVER,
972 pszPdrName, NULL, (PVOID) szPathName, CCHMAXPATH );
973 if ( !ulCB ) {
974 WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString");
975 MAKERXSTRING( *prsResult, "0", 1 );
976 goto cleanup;
977 }
978
979 // Load the port driver DLL and register its dialog routine
980 rc = DosLoadModule( NULL, 0, szPathName, &hPdr );
981 if ( rc != NO_ERROR || !hPdr ) {
982 WriteErrorCode( rc, "DosLoadModule");
983 MAKERXSTRING( *prsResult, "0", 1 );
984 goto cleanup;
985 }
986 rc = DosQueryProcAddr( hPdr, 0, "SPLPDSETPORT", &pfnSetPort );
987 if ( rc != NO_ERROR ) {
988 WriteErrorCode( rc, "DosQueryProcAddr");
989 MAKERXSTRING( *prsResult, "0", 1 );
990 goto finish;
991 }
992
993 // Now show the port configuration dialog
994 rc = pfnSetPort( hab, pszPortName, &flModified );
995 if ( rc == NO_ERROR ) {
996 // Return 1 if settings were modified
997 MAKERXSTRING( *prsResult, flModified? "1": "0", 1 );
998 }
999 else {
1000 WriteErrorCode( rc, "SplPdSetPort");
1001 MAKERXSTRING( *prsResult, "0", 1 );
1002 }
1003
1004finish:
1005 DosFreeModule( hPdr );
1006cleanup:
1007 WinTerminate( hab );
1008 return ( 0 );
1009}
1010
1011
1012/* ------------------------------------------------------------------------- *
1013 * RPUPrinterCreate *
1014 * *
1015 * Creates a new local printer object. The associated print queue and *
1016 * device are created automatically. However, the specified output port *
1017 * must exist, and the specified printer driver/model must have been *
1018 * installed already. *
1019 * *
1020 * The WPS object is created with default settings, and will be assigned an *
1021 * object-ID automatically by the WPS. *
1022 * *
1023 * NOTE: This function will NOT create a remote (LAN) printer object. *
1024 * *
1025 * REXX ARGUMENTS: *
1026 * 1. The printer description, used as the WPS object title. (REQUIRED) *
1027 * 2. The name of the underlying print queue. Must be a legal *
1028 * queue name according to OS/2 (but what is that...?) (REQUIRED) *
1029 * 3. The name of the printer port, which must exist already. (REQUIRED) *
1030 * 4. The default printer driver.model to be associated with *
1031 * the device. If specified, this must be installed in the *
1032 * system already. If not specified, IBMNULL is assumed. (OPTIONAL) *
1033 * *
1034 * REXX RETURN VALUE: *
1035 * 1 on success, or 0 if an error occurred. *
1036 * ------------------------------------------------------------------------- */
1037ULONG APIENTRY RPUPrinterCreate( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1038{
1039 PRDINFO3 devinfo = {0};
1040 PRQINFO3 qinfo = {0};
1041 PSZ pszQueueName = NULL,
1042 pszPortName = NULL,
1043 pszModel = NULL,
1044 pszTitle = NULL;
1045 CHAR szDevice[ US_PRTDEV_MAXZ ] = {0};
1046 SPLERR rc;
1047
1048
1049 // Reset the error indicator
1050 WriteErrorCode( 0, NULL );
1051
1052 // Validate the REXX arguments
1053 if (( argc < 3 ) || ( argc > 4 ) || ( ! RXVALIDSTRING( argv[0] )) ||
1054 ( ! RXVALIDSTRING( argv[1] )) || ( ! RXVALIDSTRING( argv[2] )))
1055 return ( 40 );
1056 if (( argc > 3 ) && ( ! RXVALIDSTRING( argv[3] )))
1057 return ( 40 );
1058
1059 pszTitle = argv[0].strptr;
1060 pszQueueName = argv[1].strptr;
1061 pszPortName = argv[2].strptr;
1062 if ( argc > 3 ) pszModel = argv[3].strptr;
1063
1064 // Generate a suitable (unique) device name, based on the queue name
1065 strncpy( szDevice, pszQueueName, US_PRTDEV_MAXZ-1 );
1066 if (( rc = UniqueDeviceName( szDevice )) != NO_ERROR ) {
1067 if ( rc == 1 ) // shouldn't really happen
1068 WriteErrorCode( rc, "UniqueDeviceName");
1069 else
1070 WriteErrorCode( rc, "SplEnumDevice");
1071 MAKERXSTRING( *prsResult, "0", 1 );
1072 return ( 0 );
1073 }
1074
1075 // Create the device
1076 devinfo.pszPrinterName = szDevice;
1077 devinfo.pszUserName = NULL;
1078 devinfo.pszLogAddr = pszPortName;
1079 devinfo.pszComment = pszTitle;
1080 devinfo.pszDrivers = pszModel ? pszModel : "IBMNULL";
1081 devinfo.usTimeOut = 45;
1082 rc = SplCreateDevice( NULL, 3, &devinfo, sizeof( devinfo ));
1083 if ( rc != NO_ERROR ) {
1084 WriteErrorCode( rc, "SplCreateDevice");
1085 MAKERXSTRING( *prsResult, "0", 1 );
1086 return ( 0 );
1087 }
1088
1089 // Create the queue (automatically creates printer object)
1090 qinfo.pszName = pszQueueName;
1091 qinfo.uPriority = PRQ_DEF_PRIORITY;
1092 qinfo.fsType = PRQ3_TYPE_RAW;
1093 qinfo.pszPrProc = "PMPRINT";
1094 qinfo.pszComment = pszTitle;
1095 qinfo.pszPrinters = szDevice;
1096 qinfo.pszDriverName = pszModel ? pszModel : "IBMNULL";
1097 rc = SplCreateQueue( NULL, 3, &qinfo, sizeof( qinfo ));
1098 if ( rc != NO_ERROR ) {
1099 WriteErrorCode( rc, "SplCreateQueue");
1100 MAKERXSTRING( *prsResult, "0", 1 );
1101 SplDeleteDevice( NULL, szDevice );
1102 return ( 0 );
1103 }
1104
1105 MAKERXSTRING( *prsResult, "1", 1 );
1106 return ( 0 );
1107}
1108
1109
1110/* ------------------------------------------------------------------------- *
1111 * RPUPrinterDelete *
1112 * *
1113 * Deletes a local printer queue and its associated print device definition. *
1114 * *
1115 * REXX ARGUMENTS: *
1116 * 1. The name of the printer queue to be deleted. (REQUIRED) *
1117 * *
1118 * REXX RETURN VALUE: *
1119 * 1 on success, or 0 if an error occurred. (1 will be returned even if *
1120 * the WPS printer object could not be deleted, so long as the queue and *
1121 * device were dstroyed successfully.) *
1122 * ------------------------------------------------------------------------- */
1123ULONG APIENTRY RPUPrinterDelete( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1124{
1125 PPRQINFO3 pInfo = NULL;
1126 ULONG cbBuf = 0;
1127 PSZ pszQueueName = NULL,
1128 pszDeviceName = NULL;
1129 HOBJECT hObj;
1130 SPLERR rc;
1131
1132
1133 // Reset the error indicator
1134 WriteErrorCode( 0, NULL );
1135
1136 // Validate the REXX arguments
1137 if (( argc != 1 ) || ( ! RXVALIDSTRING( argv[0] )))
1138 return ( 40 );
1139
1140 pszQueueName = argv[0].strptr;
1141 hObj = PrinterObjectHandle( pszQueueName );
1142
1143 rc = SplQueryQueue( NULL, pszQueueName, 3, NULL, 0, &cbBuf );
1144 if (( rc != NO_ERROR ) && ( rc != NERR_BufTooSmall )) {
1145 WriteErrorCode( rc, "SplQueryQueue");
1146 MAKERXSTRING( *prsResult, "0", 1 );
1147 return ( 0 );
1148 }
1149 pInfo = (PPRQINFO3) malloc( cbBuf );
1150 if ( !pInfo ) {
1151 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
1152 MAKERXSTRING( *prsResult, "0", 1 );
1153 return ( 0 );
1154 }
1155 rc = SplQueryQueue( NULL, pszQueueName, 3, pInfo, cbBuf, &cbBuf );
1156 if ( rc != NO_ERROR ) {
1157 WriteErrorCode( rc, "SplQueryQueue");
1158 MAKERXSTRING( *prsResult, "0", 1 );
1159 goto cleanup;
1160 }
1161 pszDeviceName = strtok( pInfo->pszPrinters, ",");
1162 rc = SplDeleteDevice( NULL, pszDeviceName );
1163 if ( rc != NO_ERROR ) {
1164 WriteErrorCode( rc, "SplDeleteDevice");
1165 MAKERXSTRING( *prsResult, "0", 1 );
1166 goto cleanup;
1167 }
1168
1169 // Try and destroy the WPS object
1170 if ( hObj != NULLHANDLE ) WinDestroyObject( hObj );
1171
1172 MAKERXSTRING( *prsResult, "1", 1 );
1173cleanup:
1174 free( pInfo );
1175 return ( 0 );
1176}
1177
1178
1179/* ------------------------------------------------------------------------- *
1180 * RPUPrinterQuery *
1181 * *
1182 * Gets information about the specified printer device. *
1183 * *
1184 * (stem).i.!description Printer description (name of WPS object) *
1185 * (stem).i.!port Name of the port the printer is using *
1186 * (stem).i.!driver List of the drivers used by this printer *
1187 * TODO: *
1188 * (stem).i.!flags Zero or more of the following flags (any order): *
1189 * E A printer error has occurred *
1190 * H Printer destination is paused *
1191 * I Intervention required *
1192 * N Printer has raised a notification alert *
1193 * O Printer is offline *
1194 * P Printer is out of paper *
1195 * X Printer is not processing/paused *
1196 * *
1197 * REXX ARGUMENTS: *
1198 * 1. The name of the printer device being queried. (REQUIRED) *
1199 * 2. The name of the stem in which to return the results. (REQUIRED) *
1200 * *
1201 * REXX RETURN VALUE: *
1202 * 1 on success, or 0 if an error occurred. *
1203 * ------------------------------------------------------------------------- */
1204ULONG APIENTRY RPUPrinterQuery( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1205{
1206 PPRDINFO3 pInfo = NULL;
1207 PVOID pbuf = NULL;
1208 ULONG cbBuf = 0,
1209 cbNeeded = 0;
1210 PSZ pszDeviceName;
1211 CHAR szStem[ US_STEM_MAXZ ]; // compound variable stem name
1212 SPLERR rc;
1213
1214
1215 // Reset the error indicator
1216 WriteErrorCode( 0, NULL );
1217
1218 // Validate the REXX arguments
1219 if (( argc != 2 ) ||
1220 ( ! RXVALIDSTRING( argv[0] )) || ( ! RXVALIDSTRING( argv[1] )))
1221 return ( 40 );
1222
1223 pszDeviceName = argv[0].strptr;
1224//printf("Device: \"%s\"\n", pszDeviceName );
1225
1226 // Initialize the result stem name
1227 if ( RXSTRLEN(argv[1]) > US_STEM_MAXZ ) return ( 40 );
1228 if ( argv[1].strptr[ argv[1].strlength-1 ] == '.') argv[1].strlength--;
1229 strncpy( szStem, argv[1].strptr, RXSTRLEN( argv[1] ));
1230 szStem[ RXSTRLEN( argv[1] ) ] = '\0';
1231
1232
1233 // Query the amount of available data
1234 rc = SplQueryDevice( NULL, pszDeviceName, 3L, NULL, 0L, &cbNeeded );
1235 if (( rc != ERROR_MORE_DATA ) && ( rc != NERR_BufTooSmall ))
1236 {
1237 WriteErrorCode( rc, "SplQueryDevice");
1238 MAKERXSTRING( *prsResult, "0", 1 );
1239 return ( 0 );
1240 }
1241
1242 // Now get the actual data
1243 pbuf = malloc( cbNeeded );
1244 if ( !pbuf ) {
1245 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
1246 MAKERXSTRING( *prsResult, "0", 1 );
1247 return ( 0 );
1248 }
1249 cbBuf = cbNeeded;
1250 rc = SplQueryDevice( NULL, pszDeviceName, 3L, pbuf, cbBuf, &cbNeeded );
1251 if ( rc == NO_ERROR ) {
1252 pInfo = (PPRDINFO3) pbuf;
1253 WriteCompoundVariable( szStem, "!name", pInfo->pszPrinterName );
1254 WriteCompoundVariable( szStem, "!port", pInfo->pszLogAddr );
1255 WriteCompoundVariable( szStem, "!description", pInfo->pszComment );
1256 WriteCompoundVariable( szStem, "!drivers", pInfo->pszDrivers );
1257 MAKERXSTRING( *prsResult, "1", 1 );
1258 }
1259 else {
1260 WriteErrorCode( rc, "SplQueryDevice");
1261 MAKERXSTRING( *prsResult, "0", 1 );
1262 }
1263
1264 free( pbuf );
1265 return ( 0 );
1266}
1267
1268
1269/* ------------------------------------------------------------------------- *
1270 * RPUQueueDefault *
1271 * *
1272 * Sets the requested printer queue as the system default printer. *
1273 * *
1274 * REXX ARGUMENTS: *
1275 * 1. The name of the printer queue to set as default. (REQUIRED) *
1276 * *
1277 * REXX RETURN VALUE: *
1278 * 1 on success, or 0 if an error occurred. *
1279 * ------------------------------------------------------------------------- */
1280ULONG APIENTRY RPUQueueDefault( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1281{
1282 HOBJECT hObj;
1283 BOOL fRC;
1284
1285 // Reset the error indicator
1286 WriteErrorCode( 0, NULL );
1287
1288 // Make sure we have at least one valid argument (the queue name)
1289 if ( argc != 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
1290 pszName = argv[0].strptr;
1291
1292 hObj = PrinterObjectHandle( pszName );
1293 if ( !hObj ) return ( 40 );
1294
1295 fRC = WinSetObjectData( hObj, "APPDEFAULT=YES;");
1296
1297 MAKERXSTRING( *prsResult, fRC? "1": "0", 1 );
1298 return ( 0 );
1299}
1300
1301
1302/* ------------------------------------------------------------------------- *
1303 * RPUQueueHold *
1304 * *
1305 * Holds or releases the specified print queue. *
1306 * *
1307 * REXX ARGUMENTS: *
1308 * 1. The name of the printer queue to hold or release. (REQUIRED) *
1309 * 2. Action flag, one of: *
1310 * Y Hold printer (DEFAULT) *
1311 * N Release printer *
1312 * *
1313 * REXX RETURN VALUE: *
1314 * 1 on success, or 0 if an error occurred. *
1315 * ------------------------------------------------------------------------- */
1316ULONG APIENTRY RPUQueueHold( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1317{
1318 PSZ pszQueueName; // Print queue name
1319 BOOL fHold = TRUE;
1320 SPLERR rc;
1321
1322
1323 // Reset the error indicator
1324 WriteErrorCode( 0, NULL );
1325
1326 // Make sure we have at least one valid argument (the queue name)
1327 if ( argc < 1 || argc > 2 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
1328 pszQueueName = argv[0].strptr;
1329
1330 // Second argument: view (optional, but must be correct if specified)
1331 if ( argc == 2 ) {
1332 if ( RXVALIDSTRING(argv[1]) ) {
1333 switch ( argv[1].strptr[0] ) {
1334 case 'y':
1335 case 'Y': fHold = TRUE; break;
1336 case 'n':
1337 case 'N': fHold = FALSE; break;
1338 default : return ( 40 );
1339 }
1340 }
1341 else return ( 40 );
1342 }
1343
1344 rc = fHold ? SplHoldQueue( NULL, pszQueueName ) :
1345 SplReleaseQueue( NULL, pszQueueName );
1346 if ( rc != NO_ERROR ) {
1347 WriteErrorCode( rc, fHold ? "SplHoldQueue" : "SplReleaseQueue");
1348 MAKERXSTRING( *prsResult, "0", 1 );
1349 }
1350 else MAKERXSTRING( *prsResult, "1", 1 );
1351
1352 return ( 0 );
1353}
1354
1355
1356/* ************************************************************************* *
1357 * INTERNAL PRINTER-RELATED FUNCTIONS *
1358 * ************************************************************************* */
1359
1360/* ------------------------------------------------------------------------- *
1361 * PrinterObjectHandle *
1362 * *
1363 * Given a print queue name, obtain the handle to the WPS printer object *
1364 * associated with that queue. This is done by querying OS2.INI for the *
1365 * queues defined in PM_PrintObject and reading the associated handle IDs. *
1366 * *
1367 * ARGUMENTS: *
1368 * PSZ pszQueueName: The printer queue name. This is NOT the same as the *
1369 * printer device name, although in many cases they may *
1370 * have the same value. *
1371 * *
1372 * RETURNS: HOBJECT *
1373 * WPS object handle of the printer object; NULLHANDLE if no object was *
1374 * found or an error occurred. *
1375 * ------------------------------------------------------------------------- */
1376HOBJECT PrinterObjectHandle( PSZ pszQueueName )
1377{
1378 PVOID pbuf = NULL;
1379 PSZ psz;
1380 CHAR szValue[ 256 ];
1381 ULONG cbTotal = 0,
1382 cbActual = 0;
1383 HOBJECT hObj = 0;
1384 BOOL fRC;
1385
1386
1387 fRC = PrfQueryProfileSize( HINI_USERPROFILE,
1388 APPNAME_PM_PRINT_OBJECT, NULL, &cbTotal );
1389 if ( !fRC || !cbTotal ) {
1390 //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileSize");
1391 return NULLHANDLE;
1392 }
1393 pbuf = malloc( cbTotal );
1394 if ( !pbuf ) {
1395 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
1396 return NULLHANDLE;
1397 }
1398
1399 cbActual = PrfQueryProfileString( HINI_USERPROFILE, APPNAME_PM_PRINT_OBJECT,
1400 NULL, NULL, pbuf, cbTotal );
1401 if ( !cbActual ) {
1402 //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString");
1403 return NULLHANDLE;
1404 }
1405
1406 psz = (PSZ) pbuf;
1407 while ( *psz ) {
1408 if ( !PrfQueryProfileString( HINI_USERPROFILE, APPNAME_PM_PRINT_OBJECT,
1409 psz, NULL, szValue, 255 ))
1410 {
1411 //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString");
1412 break;
1413 }
1414 if (( strcmp( szValue, pszQueueName ) == 0 ) &&
1415 ( sscanf( psz, "%u", (PULONG) &hObj ) == 1 ))
1416 {
1417 break;
1418 }
1419 psz += strlen( psz ) + 1;
1420 }
1421
1422 free ( pbuf );
1423 return ( hObj );
1424}
1425
1426
1427/* ------------------------------------------------------------------------- *
1428 * GetObjectID *
1429 * *
1430 * Given a WPS object handle, find out the corresponding object ID if one *
1431 * exists. *
1432 * *
1433 * ARGUMENTS: *
1434 * PSZ pszHandle: The WPS object handle, as a hexadecimal string. This *
1435 * should have no prefix, and be all upper-case. *
1436 * *
1437 * RETURNS: PSZ *
1438 * The object ID string. It is up to the caller to free it when done. *
1439 * This will be NULL if no object ID was found. *
1440 * ------------------------------------------------------------------------- */
1441PSZ GetObjectID( PSZ pszHandle )
1442{
1443 PVOID pbuf = NULL;
1444 ULONG cbTotal = 0,
1445 cbActual = 0;
1446 CHAR szValue[ 9 ];
1447 PSZ psz,
1448 pszObjID = NULL;
1449 BOOL fRC;
1450
1451
1452 fRC = PrfQueryProfileSize( HINI_USERPROFILE,
1453 APPNAME_PM_WPS_LOCATION, NULL, &cbTotal );
1454 if ( !fRC || !cbTotal ) {
1455 //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileSize");
1456 return NULL;
1457 }
1458 pbuf = malloc( cbTotal );
1459 if ( !pbuf ) {
1460 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
1461 return NULL;
1462 }
1463
1464 cbActual = PrfQueryProfileString( HINI_USERPROFILE, APPNAME_PM_WPS_LOCATION,
1465 NULL, NULL, pbuf, cbTotal );
1466 if ( !cbActual ) {
1467 //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString");
1468 return NULL;
1469 }
1470
1471 psz = (PSZ) pbuf;
1472 while ( *psz ) {
1473 if ( !PrfQueryProfileString( HINI_USERPROFILE, APPNAME_PM_WPS_LOCATION,
1474 psz, NULL, szValue, 8 ))
1475 {
1476 //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString");
1477 break;
1478 }
1479 if (( strcmp( szValue, pszHandle ) == 0 ) &&
1480 (( pszObjID = strdup( psz )) != NULL ))
1481 {
1482 break;
1483 }
1484 psz += strlen( psz ) + 1;
1485 }
1486
1487 free ( pbuf );
1488 return ( pszObjID );
1489}
1490
1491
1492/* ------------------------------------------------------------------------- *
1493 * UniqueDeviceName *
1494 * *
1495 * Check (and, if necessary, modify) the specified print device name to make *
1496 * sure it is unique. *
1497 * *
1498 * ARGUMENTS: *
1499 * PSZ pszName : Pointer to device name buffer (9-byte CHAR buffer) *
1500 * *
1501 * RETURNS: ULONG *
1502 * 0 on success, 1 if no unique name could be generated, or the return *
1503 * code from SplEnumDevice() if an error occurred. *
1504 * ------------------------------------------------------------------------- */
1505ULONG UniqueDeviceName( PSZ pszName )
1506{
1507 PBYTE pBuf;
1508 PPRDINFO3 pprd3;
1509 CHAR szNumber[ US_PRTDEV_MAXZ ] = {0};
1510 ULONG i, n, pos,
1511 ulNumber = 0,
1512 ulAvail = 0,
1513 cbBuf = 0;
1514 BOOL fUnique = FALSE;
1515 SPLERR rc;
1516
1517
1518 rc = SplEnumDevice( NULL, 3, NULL, 0, &ulNumber, &ulAvail, &cbBuf, NULL );
1519 if ( rc == ERROR_MORE_DATA || rc == NERR_BufTooSmall ) {
1520 pBuf = malloc( cbBuf );
1521 if ( pBuf ) {
1522 rc = SplEnumDevice( NULL, 3, pBuf, cbBuf, &ulNumber, &ulAvail, &cbBuf, NULL );
1523 if ( rc == NO_ERROR ) {
1524 n = 1;
1525 while ( !fUnique && ( n < 999 )) { // max 999 as a sanity check
1526 for ( i = 0; i < ulNumber; i++ ) {
1527 pprd3 = (PPRDINFO3) pBuf + i;
1528 if ( stricmp( pszName, pprd3->pszPrinterName ) == 0 ) break;
1529 }
1530 if ( i >= ulNumber ) fUnique = TRUE;
1531 else {
1532 sprintf( szNumber, "%u", n++ );
1533 pos = strlen( pszName ) - strlen( szNumber );
1534 pszName[ pos ] = '\0';
1535 strncat( pszName, szNumber, US_PRTDEV_MAXZ-1 );
1536 }
1537 }
1538 }
1539 free( pBuf );
1540 if ( rc == NO_ERROR && !fUnique ) return 1;
1541 }
1542 }
1543 else if ( rc == NO_ERROR ) fUnique = TRUE;
1544 return rc;
1545}
1546
1547
1548/* ************************************************************************* *
1549 * INTERNAL REXX DLL UTILITY FUNCTIONS *
1550 * ************************************************************************* */
1551
1552
1553/* ------------------------------------------------------------------------- *
1554 * SaveResultString *
1555 * *
1556 * Writes new string contents to the specified RXSTRING, allocating any *
1557 * additional memory that may be required. If the string to be written has *
1558 * zero length, nothing is done. *
1559 * *
1560 * This function should be used in place of MAKERXSTRING if there is a *
1561 * possibility that the string contents could be longer than 256 characters. *
1562 * *
1563 * ARGUMENTS: *
1564 * PRXSTRING prsResult: Pointer to an existing RXSTRING for writing. *
1565 * PCH pchBytes : The string contents to write to prsResult. *
1566 * ULONG ulBytes : The number of bytes in pchBytes to write. *
1567 * *
1568 * RETURNS: BOOL *
1569 * TRUE if prsResult was successfully updated. FALSE otherwise. *
1570 * ------------------------------------------------------------------------- */
1571BOOL SaveResultString( PRXSTRING prsResult, PCH pchBytes, ULONG ulBytes )
1572{
1573 ULONG ulRC;
1574 PCH pchNew;
1575
1576 if ( ulBytes == 0 ) return ( FALSE );
1577 if ( ulBytes > 256 ) {
1578 // REXX provides 256 bytes by default; allocate more if necessary
1579 ulRC = DosAllocMem( (PVOID) &pchNew, ulBytes, PAG_WRITE | PAG_COMMIT );
1580 if ( ulRC != 0 ) {
1581 WriteErrorCode( ulRC, "DosAllocMem");
1582 return ( FALSE );
1583 }
1584 DosFreeMem( prsResult->strptr );
1585 prsResult->strptr = pchNew;
1586 }
1587 memcpy( prsResult->strptr, pchBytes, ulBytes );
1588 prsResult->strlength = ulBytes;
1589
1590 return ( TRUE );
1591}
1592
1593
1594/* ------------------------------------------------------------------------- *
1595 * WriteStemElement *
1596 * *
1597 * Creates a stem element (compound variable) in the calling REXX program *
1598 * using the REXX shared variable pool interface. *
1599 * *
1600 * ARGUMENTS: *
1601 * PSZ pszStem : The name of the stem (before the '.') *
1602 * ULONG ulIndex : The number of the stem element (after the '.') *
1603 * PSZ pszValue : The value to write to the compound variable. *
1604 * *
1605 * RETURNS: BOOL *
1606 * TRUE on success, FALSE on failure. *
1607 * ------------------------------------------------------------------------- */
1608BOOL WriteStemElement( PSZ pszStem, ULONG ulIndex, PSZ pszValue )
1609{
1610 SHVBLOCK shvVar; // REXX shared variable pool block
1611 ULONG ulRc,
1612 ulBytes;
1613 CHAR szCompoundName[ US_COMPOUND_MAXZ ],
1614 *pchValue;
1615
1616 sprintf( szCompoundName, "%s.%d", pszStem, ulIndex );
1617 if ( pszValue == NULL ) {
1618 pchValue = "";
1619 ulBytes = 0;
1620 } else {
1621 ulBytes = strlen( pszValue );
1622 ulRc = DosAllocMem( (PVOID) &pchValue, ulBytes + 1, PAG_WRITE | PAG_COMMIT );
1623 if ( ulRc != 0 ) {
1624 WriteErrorCode( ulRc, "DosAllocMem");
1625 return FALSE;
1626 }
1627 memcpy( pchValue, pszValue, ulBytes );
1628 }
1629 MAKERXSTRING( shvVar.shvname, szCompoundName, strlen(szCompoundName) );
1630 shvVar.shvvalue.strptr = pchValue;
1631 shvVar.shvvalue.strlength = ulBytes;
1632 shvVar.shvnamelen = RXSTRLEN( shvVar.shvname );
1633 shvVar.shvvaluelen = RXSTRLEN( shvVar.shvvalue );
1634 shvVar.shvcode = RXSHV_SYSET;
1635 shvVar.shvnext = NULL;
1636 ulRc = RexxVariablePool( &shvVar );
1637 if ( ulRc > 1 ) {
1638 WriteErrorCode( shvVar.shvret, "RexxVariablePool (SHVBLOCK.shvret)");
1639 return FALSE;
1640 }
1641 return TRUE;
1642
1643}
1644
1645
1646/* ------------------------------------------------------------------------- *
1647 * WriteCompoundVariable *
1648 * *
1649 * Creates a compound variable in the calling REXX program using the REXX *
1650 * shared variable pool interface. *
1651 * *
1652 * ARGUMENTS: *
1653 * PSZ pszStem : The name of the stem (before the '.') *
1654 * PSZ pszTail : The name of the trailing portion (after the '.') *
1655 * PSZ pszValue : The value to write to the compound variable. *
1656 * *
1657 * RETURNS: BOOL *
1658 * TRUE on success, FALSE on failure. *
1659 * ------------------------------------------------------------------------- */
1660BOOL WriteCompoundVariable( PSZ pszStem, PSZ pszTail, PSZ pszValue )
1661{
1662 SHVBLOCK shvVar; // REXX shared variable pool block
1663 ULONG ulRc,
1664 ulBytes;
1665 CHAR szCompoundName[ US_COMPOUND_MAXZ ],
1666 *pchValue;
1667
1668 sprintf( szCompoundName, "%s.%s", pszStem, pszTail );
1669 if ( pszValue == NULL ) {
1670 pchValue = "";
1671 ulBytes = 0;
1672 } else {
1673 ulBytes = strlen( pszValue );
1674 ulRc = DosAllocMem( (PVOID) &pchValue, ulBytes + 1, PAG_WRITE | PAG_COMMIT );
1675 if ( ulRc != 0 ) {
1676 WriteErrorCode( ulRc, "DosAllocMem");
1677 return FALSE;
1678 }
1679 memcpy( pchValue, pszValue, ulBytes );
1680 }
1681 MAKERXSTRING( shvVar.shvname, szCompoundName, strlen(szCompoundName) );
1682 shvVar.shvvalue.strptr = pchValue;
1683 shvVar.shvvalue.strlength = ulBytes;
1684 shvVar.shvnamelen = RXSTRLEN( shvVar.shvname );
1685 shvVar.shvvaluelen = RXSTRLEN( shvVar.shvvalue );
1686 shvVar.shvcode = RXSHV_SYSET;
1687 shvVar.shvnext = NULL;
1688 ulRc = RexxVariablePool( &shvVar );
1689 if ( ulRc > 1 ) {
1690 WriteErrorCode( shvVar.shvret, "RexxVariablePool (SHVBLOCK.shvret)");
1691 return FALSE;
1692 }
1693 return TRUE;
1694}
1695
1696
1697/* ------------------------------------------------------------------------- *
1698 * WriteErrorCode *
1699 * *
1700 * Writes an error code to a special variable in the calling REXX program *
1701 * using the REXX shared variable pool interface. This is used to return *
1702 * API error codes to the REXX program, since the REXX functions themselves *
1703 * normally return string values. *
1704 * *
1705 * ARGUMENTS: *
1706 * ULONG ulError : The error code returned by the failing API call. *
1707 * PSZ pszContext: A string describing the API call that failed. *
1708 * *
1709 * RETURNS: N/A *
1710 * ------------------------------------------------------------------------- */
1711void WriteErrorCode( ULONG ulError, PSZ pszContext )
1712{
1713 SHVBLOCK shvVar; // REXX shared variable pool block
1714 ULONG ulRc;
1715 CHAR szErrorText[ US_ERRSTR_MAXZ ];
1716
1717 if ( pszContext == NULL )
1718 sprintf( szErrorText, "%X", ulError );
1719 else
1720 sprintf( szErrorText, "%X: %s", ulError, pszContext );
1721 MAKERXSTRING( shvVar.shvname, SZ_ERROR_NAME, strlen(SZ_ERROR_NAME) );
1722 MAKERXSTRING( shvVar.shvvalue, szErrorText, strlen(szErrorText) );
1723 shvVar.shvnamelen = RXSTRLEN( shvVar.shvname );
1724 shvVar.shvvaluelen = RXSTRLEN( shvVar.shvvalue );
1725 shvVar.shvcode = RXSHV_SYSET;
1726 shvVar.shvnext = NULL;
1727 ulRc = RexxVariablePool( &shvVar );
1728 if ( ulRc > 1 )
1729 printf("Unable to set %s: rc = %d\n", shvVar.shvname.strptr, shvVar.shvret );
1730}
1731
1732
Note: See TracBrowser for help on using the repository browser.