source: rxprtutl/trunk/rxprtutl.c@ 10

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

RPUPortDelete function added.

File size: 77.8 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 "RPUPortDelete",
103 "RPUPortDialog",
104 "RPUPortInstall",
105 "RPUPortSet",
106 "RPUPrinterCreate",
107 "RPUPrinterDelete",
108 "RPUPrinterQuery",
109 "RPUVersion"
110};
111
112
113// FUNCTION DECLARATIONS
114
115// Exported REXX functions
116RexxFunctionHandler RPULoadFuncs;
117RexxFunctionHandler RPUDropFuncs;
118RexxFunctionHandler RPUVersion;
119RexxFunctionHandler RPUEnumModels;
120RexxFunctionHandler RPUEnumDrivers;
121RexxFunctionHandler RPUEnumPorts;
122RexxFunctionHandler RPUEnumPrinters;
123RexxFunctionHandler RPUOpenView;
124RexxFunctionHandler RPUPortDelete;
125RexxFunctionHandler RPUPortDialog;
126RexxFunctionHandler RPUPortInstall;
127RexxFunctionHandler RPUPortSet;
128RexxFunctionHandler RPUPrinterCreate;
129RexxFunctionHandler RPUPrinterDelete;
130RexxFunctionHandler RPUPrinterQuery;
131RexxFunctionHandler RPUQueueDefault;
132RexxFunctionHandler RPUQueueHold;
133
134// TODO
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 * RPUPortDelete *
1014 * *
1015 * Deletes a printer port. *
1016 * *
1017 * REXX ARGUMENTS: *
1018 * 1. The name of the port to be deleted. (REQUIRED) *
1019 * *
1020 * REXX RETURN VALUE: *
1021 * 1 on success, or 0 if an error occurred. *
1022 * ------------------------------------------------------------------------- */
1023ULONG APIENTRY RPUPortDelete( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1024{
1025 PSZ pszPortName;
1026 SPLERR rc = 0;
1027
1028
1029 // Reset the error indicator
1030 WriteErrorCode( 0, NULL );
1031
1032 // Make sure we have at least one valid argument (the port name)
1033 if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
1034 pszPortName = strupr( argv[0].strptr );
1035
1036 rc = SplDeletePort( NULL, pszPortName );
1037 if ( rc == NO_ERROR ) {
1038 // Return 1 on success
1039 MAKERXSTRING( *prsResult, "1", 1 );
1040 }
1041 else {
1042 WriteErrorCode( rc, "SplDeletePort");
1043 MAKERXSTRING( *prsResult, "0", 1 );
1044 }
1045
1046 return ( 0 );
1047}
1048
1049
1050/* ------------------------------------------------------------------------- *
1051 * RPUPrinterCreate *
1052 * *
1053 * Creates a new local printer object. The associated print queue and *
1054 * device are created automatically. However, the specified output port *
1055 * must exist, and the specified printer driver/model must have been *
1056 * installed already. *
1057 * *
1058 * The WPS object is created with default settings, and will be assigned an *
1059 * object-ID automatically by the WPS. *
1060 * *
1061 * NOTE: This function will NOT create a remote (LAN) printer object. *
1062 * *
1063 * REXX ARGUMENTS: *
1064 * 1. The printer description, used as the WPS object title. (REQUIRED) *
1065 * 2. The name of the underlying print queue. Must be a legal *
1066 * queue name according to OS/2 (but what is that...?) (REQUIRED) *
1067 * 3. The name of the printer port, which must exist already. (REQUIRED) *
1068 * 4. The default printer driver.model to be associated with *
1069 * the device. If specified, this must be installed in the *
1070 * system already. If not specified, IBMNULL is assumed. (OPTIONAL) *
1071 * *
1072 * REXX RETURN VALUE: *
1073 * 1 on success, or 0 if an error occurred. *
1074 * ------------------------------------------------------------------------- */
1075ULONG APIENTRY RPUPrinterCreate( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1076{
1077 PRDINFO3 devinfo = {0};
1078 PRQINFO3 qinfo = {0};
1079 PSZ pszQueueName = NULL,
1080 pszPortName = NULL,
1081 pszModel = NULL,
1082 pszTitle = NULL;
1083 CHAR szDevice[ US_PRTDEV_MAXZ ] = {0};
1084 SPLERR rc;
1085
1086
1087 // Reset the error indicator
1088 WriteErrorCode( 0, NULL );
1089
1090 // Validate the REXX arguments
1091 if (( argc < 3 ) || ( argc > 4 ) || ( ! RXVALIDSTRING( argv[0] )) ||
1092 ( ! RXVALIDSTRING( argv[1] )) || ( ! RXVALIDSTRING( argv[2] )))
1093 return ( 40 );
1094 if (( argc > 3 ) && ( ! RXVALIDSTRING( argv[3] )))
1095 return ( 40 );
1096
1097 pszTitle = argv[0].strptr;
1098 pszQueueName = argv[1].strptr;
1099 pszPortName = argv[2].strptr;
1100 if ( argc > 3 ) pszModel = argv[3].strptr;
1101
1102 // Generate a suitable (unique) device name, based on the queue name
1103 strncpy( szDevice, pszQueueName, US_PRTDEV_MAXZ-1 );
1104 if (( rc = UniqueDeviceName( szDevice )) != NO_ERROR ) {
1105 if ( rc == 1 ) // shouldn't really happen
1106 WriteErrorCode( rc, "UniqueDeviceName");
1107 else
1108 WriteErrorCode( rc, "SplEnumDevice");
1109 MAKERXSTRING( *prsResult, "0", 1 );
1110 return ( 0 );
1111 }
1112
1113 // Create the device
1114 devinfo.pszPrinterName = szDevice;
1115 devinfo.pszUserName = NULL;
1116 devinfo.pszLogAddr = pszPortName;
1117 devinfo.pszComment = pszTitle;
1118 devinfo.pszDrivers = pszModel ? pszModel : "IBMNULL";
1119 devinfo.usTimeOut = 45;
1120 rc = SplCreateDevice( NULL, 3, &devinfo, sizeof( devinfo ));
1121 if ( rc != NO_ERROR ) {
1122 WriteErrorCode( rc, "SplCreateDevice");
1123 MAKERXSTRING( *prsResult, "0", 1 );
1124 return ( 0 );
1125 }
1126
1127 // Create the queue (automatically creates printer object)
1128 qinfo.pszName = pszQueueName;
1129 qinfo.uPriority = PRQ_DEF_PRIORITY;
1130 qinfo.fsType = PRQ3_TYPE_RAW;
1131 qinfo.pszPrProc = "PMPRINT";
1132 qinfo.pszComment = pszTitle;
1133 qinfo.pszPrinters = szDevice;
1134 qinfo.pszDriverName = pszModel ? pszModel : "IBMNULL";
1135 rc = SplCreateQueue( NULL, 3, &qinfo, sizeof( qinfo ));
1136 if ( rc != NO_ERROR ) {
1137 WriteErrorCode( rc, "SplCreateQueue");
1138 MAKERXSTRING( *prsResult, "0", 1 );
1139 SplDeleteDevice( NULL, szDevice );
1140 return ( 0 );
1141 }
1142
1143 MAKERXSTRING( *prsResult, "1", 1 );
1144 return ( 0 );
1145}
1146
1147
1148/* ------------------------------------------------------------------------- *
1149 * RPUPrinterDelete *
1150 * *
1151 * Deletes a local printer queue and its associated print device definition. *
1152 * *
1153 * REXX ARGUMENTS: *
1154 * 1. The name of the printer queue to be deleted. (REQUIRED) *
1155 * *
1156 * REXX RETURN VALUE: *
1157 * 1 on success, or 0 if an error occurred. (1 will be returned even if *
1158 * the WPS printer object could not be deleted, so long as the queue and *
1159 * device were destroyed successfully.) *
1160 * ------------------------------------------------------------------------- */
1161ULONG APIENTRY RPUPrinterDelete( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1162{
1163 PPRQINFO3 pInfo = NULL;
1164 ULONG cbBuf = 0;
1165 PSZ pszQueueName = NULL,
1166 pszDeviceName = NULL;
1167 HOBJECT hObj;
1168 SPLERR rc;
1169
1170
1171 // Reset the error indicator
1172 WriteErrorCode( 0, NULL );
1173
1174 // Validate the REXX arguments
1175 if (( argc != 1 ) || ( ! RXVALIDSTRING( argv[0] )))
1176 return ( 40 );
1177
1178 pszQueueName = argv[0].strptr;
1179 hObj = PrinterObjectHandle( pszQueueName );
1180
1181 rc = SplQueryQueue( NULL, pszQueueName, 3, NULL, 0, &cbBuf );
1182 if (( rc != NO_ERROR ) && ( rc != NERR_BufTooSmall )) {
1183 WriteErrorCode( rc, "SplQueryQueue");
1184 MAKERXSTRING( *prsResult, "0", 1 );
1185 return ( 0 );
1186 }
1187 pInfo = (PPRQINFO3) malloc( cbBuf );
1188 if ( !pInfo ) {
1189 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
1190 MAKERXSTRING( *prsResult, "0", 1 );
1191 return ( 0 );
1192 }
1193 rc = SplQueryQueue( NULL, pszQueueName, 3, pInfo, cbBuf, &cbBuf );
1194 if ( rc != NO_ERROR ) {
1195 WriteErrorCode( rc, "SplQueryQueue");
1196 MAKERXSTRING( *prsResult, "0", 1 );
1197 goto cleanup;
1198 }
1199 pszDeviceName = strtok( pInfo->pszPrinters, ",");
1200 rc = SplDeleteDevice( NULL, pszDeviceName );
1201 if ( rc != NO_ERROR ) {
1202 WriteErrorCode( rc, "SplDeleteDevice");
1203 MAKERXSTRING( *prsResult, "0", 1 );
1204 goto cleanup;
1205 }
1206
1207 // Try and destroy the WPS object
1208 if ( hObj != NULLHANDLE ) WinDestroyObject( hObj );
1209
1210 MAKERXSTRING( *prsResult, "1", 1 );
1211cleanup:
1212 free( pInfo );
1213 return ( 0 );
1214}
1215
1216
1217/* ------------------------------------------------------------------------- *
1218 * RPUPrinterQuery *
1219 * *
1220 * Gets information about the specified printer device. *
1221 * *
1222 * (stem).i.!description Printer description (name of WPS object) *
1223 * (stem).i.!port Name of the port the printer is using *
1224 * (stem).i.!driver List of the drivers used by this printer *
1225 * TODO: *
1226 * (stem).i.!flags Zero or more of the following flags (any order): *
1227 * E A printer error has occurred *
1228 * H Printer destination is paused *
1229 * I Intervention required *
1230 * N Printer has raised a notification alert *
1231 * O Printer is offline *
1232 * P Printer is out of paper *
1233 * X Printer is not processing/paused *
1234 * *
1235 * REXX ARGUMENTS: *
1236 * 1. The name of the printer device being queried. (REQUIRED) *
1237 * 2. The name of the stem in which to return the results. (REQUIRED) *
1238 * *
1239 * REXX RETURN VALUE: *
1240 * 1 on success, or 0 if an error occurred. *
1241 * ------------------------------------------------------------------------- */
1242ULONG APIENTRY RPUPrinterQuery( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1243{
1244 PPRDINFO3 pInfo = NULL;
1245 PVOID pbuf = NULL;
1246 ULONG cbBuf = 0,
1247 cbNeeded = 0;
1248 PSZ pszDeviceName;
1249 CHAR szStem[ US_STEM_MAXZ ]; // compound variable stem name
1250 SPLERR rc;
1251
1252
1253 // Reset the error indicator
1254 WriteErrorCode( 0, NULL );
1255
1256 // Validate the REXX arguments
1257 if (( argc != 2 ) ||
1258 ( ! RXVALIDSTRING( argv[0] )) || ( ! RXVALIDSTRING( argv[1] )))
1259 return ( 40 );
1260
1261 pszDeviceName = argv[0].strptr;
1262//printf("Device: \"%s\"\n", pszDeviceName );
1263
1264 // Initialize the result stem name
1265 if ( RXSTRLEN(argv[1]) > US_STEM_MAXZ ) return ( 40 );
1266 if ( argv[1].strptr[ argv[1].strlength-1 ] == '.') argv[1].strlength--;
1267 strncpy( szStem, argv[1].strptr, RXSTRLEN( argv[1] ));
1268 szStem[ RXSTRLEN( argv[1] ) ] = '\0';
1269
1270
1271 // Query the amount of available data
1272 rc = SplQueryDevice( NULL, pszDeviceName, 3L, NULL, 0L, &cbNeeded );
1273 if (( rc != ERROR_MORE_DATA ) && ( rc != NERR_BufTooSmall ))
1274 {
1275 WriteErrorCode( rc, "SplQueryDevice");
1276 MAKERXSTRING( *prsResult, "0", 1 );
1277 return ( 0 );
1278 }
1279
1280 // Now get the actual data
1281 pbuf = malloc( cbNeeded );
1282 if ( !pbuf ) {
1283 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
1284 MAKERXSTRING( *prsResult, "0", 1 );
1285 return ( 0 );
1286 }
1287 cbBuf = cbNeeded;
1288 rc = SplQueryDevice( NULL, pszDeviceName, 3L, pbuf, cbBuf, &cbNeeded );
1289 if ( rc == NO_ERROR ) {
1290 pInfo = (PPRDINFO3) pbuf;
1291 WriteCompoundVariable( szStem, "!name", pInfo->pszPrinterName );
1292 WriteCompoundVariable( szStem, "!port", pInfo->pszLogAddr );
1293 WriteCompoundVariable( szStem, "!description", pInfo->pszComment );
1294 WriteCompoundVariable( szStem, "!drivers", pInfo->pszDrivers );
1295 MAKERXSTRING( *prsResult, "1", 1 );
1296 }
1297 else {
1298 WriteErrorCode( rc, "SplQueryDevice");
1299 MAKERXSTRING( *prsResult, "0", 1 );
1300 }
1301
1302 free( pbuf );
1303 return ( 0 );
1304}
1305
1306
1307/* ------------------------------------------------------------------------- *
1308 * RPUQueueDefault *
1309 * *
1310 * Sets the requested printer queue as the system default printer. *
1311 * *
1312 * REXX ARGUMENTS: *
1313 * 1. The name of the printer queue to set as default. (REQUIRED) *
1314 * *
1315 * REXX RETURN VALUE: *
1316 * 1 on success, or 0 if an error occurred. *
1317 * ------------------------------------------------------------------------- */
1318ULONG APIENTRY RPUQueueDefault( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1319{
1320 HOBJECT hObj;
1321 BOOL fRC;
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 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
1328 pszName = argv[0].strptr;
1329
1330 hObj = PrinterObjectHandle( pszName );
1331 if ( !hObj ) return ( 40 );
1332
1333 fRC = WinSetObjectData( hObj, "APPDEFAULT=YES;");
1334
1335 MAKERXSTRING( *prsResult, fRC? "1": "0", 1 );
1336 return ( 0 );
1337}
1338
1339
1340/* ------------------------------------------------------------------------- *
1341 * RPUQueueHold *
1342 * *
1343 * Holds or releases the specified print queue. *
1344 * *
1345 * REXX ARGUMENTS: *
1346 * 1. The name of the printer queue to hold or release. (REQUIRED) *
1347 * 2. Action flag, one of: *
1348 * Y Hold printer (DEFAULT) *
1349 * N Release printer *
1350 * *
1351 * REXX RETURN VALUE: *
1352 * 1 on success, or 0 if an error occurred. *
1353 * ------------------------------------------------------------------------- */
1354ULONG APIENTRY RPUQueueHold( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1355{
1356 PSZ pszQueueName; // Print queue name
1357 BOOL fHold = TRUE;
1358 SPLERR rc;
1359
1360
1361 // Reset the error indicator
1362 WriteErrorCode( 0, NULL );
1363
1364 // Make sure we have at least one valid argument (the queue name)
1365 if ( argc < 1 || argc > 2 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
1366 pszQueueName = argv[0].strptr;
1367
1368 // Second argument: view (optional, but must be correct if specified)
1369 if ( argc == 2 ) {
1370 if ( RXVALIDSTRING(argv[1]) ) {
1371 switch ( argv[1].strptr[0] ) {
1372 case 'y':
1373 case 'Y': fHold = TRUE; break;
1374 case 'n':
1375 case 'N': fHold = FALSE; break;
1376 default : return ( 40 );
1377 }
1378 }
1379 else return ( 40 );
1380 }
1381
1382 rc = fHold ? SplHoldQueue( NULL, pszQueueName ) :
1383 SplReleaseQueue( NULL, pszQueueName );
1384 if ( rc != NO_ERROR ) {
1385 WriteErrorCode( rc, fHold ? "SplHoldQueue" : "SplReleaseQueue");
1386 MAKERXSTRING( *prsResult, "0", 1 );
1387 }
1388 else MAKERXSTRING( *prsResult, "1", 1 );
1389
1390 return ( 0 );
1391}
1392
1393
1394/* ************************************************************************* *
1395 * INTERNAL PRINTER-RELATED FUNCTIONS *
1396 * ************************************************************************* */
1397
1398/* ------------------------------------------------------------------------- *
1399 * PrinterObjectHandle *
1400 * *
1401 * Given a print queue name, obtain the handle to the WPS printer object *
1402 * associated with that queue. This is done by querying OS2.INI for the *
1403 * queues defined in PM_PrintObject and reading the associated handle IDs. *
1404 * *
1405 * ARGUMENTS: *
1406 * PSZ pszQueueName: The printer queue name. This is NOT the same as the *
1407 * printer device name, although in many cases they may *
1408 * have the same value. *
1409 * *
1410 * RETURNS: HOBJECT *
1411 * WPS object handle of the printer object; NULLHANDLE if no object was *
1412 * found or an error occurred. *
1413 * ------------------------------------------------------------------------- */
1414HOBJECT PrinterObjectHandle( PSZ pszQueueName )
1415{
1416 PVOID pbuf = NULL;
1417 PSZ psz;
1418 CHAR szValue[ 256 ];
1419 ULONG cbTotal = 0,
1420 cbActual = 0;
1421 HOBJECT hObj = 0;
1422 BOOL fRC;
1423
1424
1425 fRC = PrfQueryProfileSize( HINI_USERPROFILE,
1426 APPNAME_PM_PRINT_OBJECT, NULL, &cbTotal );
1427 if ( !fRC || !cbTotal ) {
1428 //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileSize");
1429 return NULLHANDLE;
1430 }
1431 pbuf = malloc( cbTotal );
1432 if ( !pbuf ) {
1433 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
1434 return NULLHANDLE;
1435 }
1436
1437 cbActual = PrfQueryProfileString( HINI_USERPROFILE, APPNAME_PM_PRINT_OBJECT,
1438 NULL, NULL, pbuf, cbTotal );
1439 if ( !cbActual ) {
1440 //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString");
1441 return NULLHANDLE;
1442 }
1443
1444 psz = (PSZ) pbuf;
1445 while ( *psz ) {
1446 if ( !PrfQueryProfileString( HINI_USERPROFILE, APPNAME_PM_PRINT_OBJECT,
1447 psz, NULL, szValue, 255 ))
1448 {
1449 //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString");
1450 break;
1451 }
1452 if (( strcmp( szValue, pszQueueName ) == 0 ) &&
1453 ( sscanf( psz, "%u", (PULONG) &hObj ) == 1 ))
1454 {
1455 break;
1456 }
1457 psz += strlen( psz ) + 1;
1458 }
1459
1460 free ( pbuf );
1461 return ( hObj );
1462}
1463
1464
1465/* ------------------------------------------------------------------------- *
1466 * GetObjectID *
1467 * *
1468 * Given a WPS object handle, find out the corresponding object ID if one *
1469 * exists. *
1470 * *
1471 * ARGUMENTS: *
1472 * PSZ pszHandle: The WPS object handle, as a hexadecimal string. This *
1473 * should have no prefix, and be all upper-case. *
1474 * *
1475 * RETURNS: PSZ *
1476 * The object ID string. It is up to the caller to free it when done. *
1477 * This will be NULL if no object ID was found. *
1478 * ------------------------------------------------------------------------- */
1479PSZ GetObjectID( PSZ pszHandle )
1480{
1481 PVOID pbuf = NULL;
1482 ULONG cbTotal = 0,
1483 cbActual = 0;
1484 CHAR szValue[ 9 ];
1485 PSZ psz,
1486 pszObjID = NULL;
1487 BOOL fRC;
1488
1489
1490 fRC = PrfQueryProfileSize( HINI_USERPROFILE,
1491 APPNAME_PM_WPS_LOCATION, NULL, &cbTotal );
1492 if ( !fRC || !cbTotal ) {
1493 //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileSize");
1494 return NULL;
1495 }
1496 pbuf = malloc( cbTotal );
1497 if ( !pbuf ) {
1498 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
1499 return NULL;
1500 }
1501
1502 cbActual = PrfQueryProfileString( HINI_USERPROFILE, APPNAME_PM_WPS_LOCATION,
1503 NULL, NULL, pbuf, cbTotal );
1504 if ( !cbActual ) {
1505 //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString");
1506 return NULL;
1507 }
1508
1509 psz = (PSZ) pbuf;
1510 while ( *psz ) {
1511 if ( !PrfQueryProfileString( HINI_USERPROFILE, APPNAME_PM_WPS_LOCATION,
1512 psz, NULL, szValue, 8 ))
1513 {
1514 //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString");
1515 break;
1516 }
1517 if (( strcmp( szValue, pszHandle ) == 0 ) &&
1518 (( pszObjID = strdup( psz )) != NULL ))
1519 {
1520 break;
1521 }
1522 psz += strlen( psz ) + 1;
1523 }
1524
1525 free ( pbuf );
1526 return ( pszObjID );
1527}
1528
1529
1530/* ------------------------------------------------------------------------- *
1531 * UniqueDeviceName *
1532 * *
1533 * Check (and, if necessary, modify) the specified print device name to make *
1534 * sure it is unique. *
1535 * *
1536 * ARGUMENTS: *
1537 * PSZ pszName : Pointer to device name buffer (9-byte CHAR buffer) *
1538 * *
1539 * RETURNS: ULONG *
1540 * 0 on success, 1 if no unique name could be generated, or the return *
1541 * code from SplEnumDevice() if an error occurred. *
1542 * ------------------------------------------------------------------------- */
1543ULONG UniqueDeviceName( PSZ pszName )
1544{
1545 PBYTE pBuf;
1546 PPRDINFO3 pprd3;
1547 CHAR szNumber[ US_PRTDEV_MAXZ ] = {0};
1548 ULONG i, n, pos,
1549 ulNumber = 0,
1550 ulAvail = 0,
1551 cbBuf = 0;
1552 BOOL fUnique = FALSE;
1553 SPLERR rc;
1554
1555
1556 rc = SplEnumDevice( NULL, 3, NULL, 0, &ulNumber, &ulAvail, &cbBuf, NULL );
1557 if ( rc == ERROR_MORE_DATA || rc == NERR_BufTooSmall ) {
1558 pBuf = malloc( cbBuf );
1559 if ( pBuf ) {
1560 rc = SplEnumDevice( NULL, 3, pBuf, cbBuf, &ulNumber, &ulAvail, &cbBuf, NULL );
1561 if ( rc == NO_ERROR ) {
1562 n = 1;
1563 while ( !fUnique && ( n < 999 )) { // max 999 as a sanity check
1564 for ( i = 0; i < ulNumber; i++ ) {
1565 pprd3 = (PPRDINFO3) pBuf + i;
1566 if ( stricmp( pszName, pprd3->pszPrinterName ) == 0 ) break;
1567 }
1568 if ( i >= ulNumber ) fUnique = TRUE;
1569 else {
1570 sprintf( szNumber, "%u", n++ );
1571 pos = strlen( pszName ) - strlen( szNumber );
1572 pszName[ pos ] = '\0';
1573 strncat( pszName, szNumber, US_PRTDEV_MAXZ-1 );
1574 }
1575 }
1576 }
1577 free( pBuf );
1578 if ( rc == NO_ERROR && !fUnique ) return 1;
1579 }
1580 }
1581 else if ( rc == NO_ERROR ) fUnique = TRUE;
1582 return rc;
1583}
1584
1585
1586/* ************************************************************************* *
1587 * INTERNAL REXX DLL UTILITY FUNCTIONS *
1588 * ************************************************************************* */
1589
1590
1591/* ------------------------------------------------------------------------- *
1592 * SaveResultString *
1593 * *
1594 * Writes new string contents to the specified RXSTRING, allocating any *
1595 * additional memory that may be required. If the string to be written has *
1596 * zero length, nothing is done. *
1597 * *
1598 * This function should be used in place of MAKERXSTRING if there is a *
1599 * possibility that the string contents could be longer than 256 characters. *
1600 * *
1601 * ARGUMENTS: *
1602 * PRXSTRING prsResult: Pointer to an existing RXSTRING for writing. *
1603 * PCH pchBytes : The string contents to write to prsResult. *
1604 * ULONG ulBytes : The number of bytes in pchBytes to write. *
1605 * *
1606 * RETURNS: BOOL *
1607 * TRUE if prsResult was successfully updated. FALSE otherwise. *
1608 * ------------------------------------------------------------------------- */
1609BOOL SaveResultString( PRXSTRING prsResult, PCH pchBytes, ULONG ulBytes )
1610{
1611 ULONG ulRC;
1612 PCH pchNew;
1613
1614 if ( ulBytes == 0 ) return ( FALSE );
1615 if ( ulBytes > 256 ) {
1616 // REXX provides 256 bytes by default; allocate more if necessary
1617 ulRC = DosAllocMem( (PVOID) &pchNew, ulBytes, PAG_WRITE | PAG_COMMIT );
1618 if ( ulRC != 0 ) {
1619 WriteErrorCode( ulRC, "DosAllocMem");
1620 return ( FALSE );
1621 }
1622 DosFreeMem( prsResult->strptr );
1623 prsResult->strptr = pchNew;
1624 }
1625 memcpy( prsResult->strptr, pchBytes, ulBytes );
1626 prsResult->strlength = ulBytes;
1627
1628 return ( TRUE );
1629}
1630
1631
1632/* ------------------------------------------------------------------------- *
1633 * WriteStemElement *
1634 * *
1635 * Creates a stem element (compound variable) in the calling REXX program *
1636 * using the REXX shared variable pool interface. *
1637 * *
1638 * ARGUMENTS: *
1639 * PSZ pszStem : The name of the stem (before the '.') *
1640 * ULONG ulIndex : The number of the stem element (after the '.') *
1641 * PSZ pszValue : The value to write to the compound variable. *
1642 * *
1643 * RETURNS: BOOL *
1644 * TRUE on success, FALSE on failure. *
1645 * ------------------------------------------------------------------------- */
1646BOOL WriteStemElement( PSZ pszStem, ULONG ulIndex, PSZ pszValue )
1647{
1648 SHVBLOCK shvVar; // REXX shared variable pool block
1649 ULONG ulRc,
1650 ulBytes;
1651 CHAR szCompoundName[ US_COMPOUND_MAXZ ],
1652 *pchValue;
1653
1654 sprintf( szCompoundName, "%s.%d", pszStem, ulIndex );
1655 if ( pszValue == NULL ) {
1656 pchValue = "";
1657 ulBytes = 0;
1658 } else {
1659 ulBytes = strlen( pszValue );
1660 ulRc = DosAllocMem( (PVOID) &pchValue, ulBytes + 1, PAG_WRITE | PAG_COMMIT );
1661 if ( ulRc != 0 ) {
1662 WriteErrorCode( ulRc, "DosAllocMem");
1663 return FALSE;
1664 }
1665 memcpy( pchValue, pszValue, ulBytes );
1666 }
1667 MAKERXSTRING( shvVar.shvname, szCompoundName, strlen(szCompoundName) );
1668 shvVar.shvvalue.strptr = pchValue;
1669 shvVar.shvvalue.strlength = ulBytes;
1670 shvVar.shvnamelen = RXSTRLEN( shvVar.shvname );
1671 shvVar.shvvaluelen = RXSTRLEN( shvVar.shvvalue );
1672 shvVar.shvcode = RXSHV_SYSET;
1673 shvVar.shvnext = NULL;
1674 ulRc = RexxVariablePool( &shvVar );
1675 if ( ulRc > 1 ) {
1676 WriteErrorCode( shvVar.shvret, "RexxVariablePool (SHVBLOCK.shvret)");
1677 return FALSE;
1678 }
1679 return TRUE;
1680
1681}
1682
1683
1684/* ------------------------------------------------------------------------- *
1685 * WriteCompoundVariable *
1686 * *
1687 * Creates a compound variable in the calling REXX program using the REXX *
1688 * shared variable pool interface. *
1689 * *
1690 * ARGUMENTS: *
1691 * PSZ pszStem : The name of the stem (before the '.') *
1692 * PSZ pszTail : The name of the trailing portion (after the '.') *
1693 * PSZ pszValue : The value to write to the compound variable. *
1694 * *
1695 * RETURNS: BOOL *
1696 * TRUE on success, FALSE on failure. *
1697 * ------------------------------------------------------------------------- */
1698BOOL WriteCompoundVariable( PSZ pszStem, PSZ pszTail, PSZ pszValue )
1699{
1700 SHVBLOCK shvVar; // REXX shared variable pool block
1701 ULONG ulRc,
1702 ulBytes;
1703 CHAR szCompoundName[ US_COMPOUND_MAXZ ],
1704 *pchValue;
1705
1706 sprintf( szCompoundName, "%s.%s", pszStem, pszTail );
1707 if ( pszValue == NULL ) {
1708 pchValue = "";
1709 ulBytes = 0;
1710 } else {
1711 ulBytes = strlen( pszValue );
1712 ulRc = DosAllocMem( (PVOID) &pchValue, ulBytes + 1, PAG_WRITE | PAG_COMMIT );
1713 if ( ulRc != 0 ) {
1714 WriteErrorCode( ulRc, "DosAllocMem");
1715 return FALSE;
1716 }
1717 memcpy( pchValue, pszValue, ulBytes );
1718 }
1719 MAKERXSTRING( shvVar.shvname, szCompoundName, strlen(szCompoundName) );
1720 shvVar.shvvalue.strptr = pchValue;
1721 shvVar.shvvalue.strlength = ulBytes;
1722 shvVar.shvnamelen = RXSTRLEN( shvVar.shvname );
1723 shvVar.shvvaluelen = RXSTRLEN( shvVar.shvvalue );
1724 shvVar.shvcode = RXSHV_SYSET;
1725 shvVar.shvnext = NULL;
1726 ulRc = RexxVariablePool( &shvVar );
1727 if ( ulRc > 1 ) {
1728 WriteErrorCode( shvVar.shvret, "RexxVariablePool (SHVBLOCK.shvret)");
1729 return FALSE;
1730 }
1731 return TRUE;
1732}
1733
1734
1735/* ------------------------------------------------------------------------- *
1736 * WriteErrorCode *
1737 * *
1738 * Writes an error code to a special variable in the calling REXX program *
1739 * using the REXX shared variable pool interface. This is used to return *
1740 * API error codes to the REXX program, since the REXX functions themselves *
1741 * normally return string values. *
1742 * *
1743 * ARGUMENTS: *
1744 * ULONG ulError : The error code returned by the failing API call. *
1745 * PSZ pszContext: A string describing the API call that failed. *
1746 * *
1747 * RETURNS: N/A *
1748 * ------------------------------------------------------------------------- */
1749void WriteErrorCode( ULONG ulError, PSZ pszContext )
1750{
1751 SHVBLOCK shvVar; // REXX shared variable pool block
1752 ULONG ulRc;
1753 CHAR szErrorText[ US_ERRSTR_MAXZ ];
1754
1755 if ( pszContext == NULL )
1756 sprintf( szErrorText, "%X", ulError );
1757 else
1758 sprintf( szErrorText, "%X: %s", ulError, pszContext );
1759 MAKERXSTRING( shvVar.shvname, SZ_ERROR_NAME, strlen(SZ_ERROR_NAME) );
1760 MAKERXSTRING( shvVar.shvvalue, szErrorText, strlen(szErrorText) );
1761 shvVar.shvnamelen = RXSTRLEN( shvVar.shvname );
1762 shvVar.shvvaluelen = RXSTRLEN( shvVar.shvvalue );
1763 shvVar.shvcode = RXSHV_SYSET;
1764 shvVar.shvnext = NULL;
1765 ulRc = RexxVariablePool( &shvVar );
1766 if ( ulRc > 1 )
1767 printf("Unable to set %s: rc = %d\n", shvVar.shvname.strptr, shvVar.shvret );
1768}
1769
1770
Note: See TracBrowser for help on using the repository browser.