source: rxprtutl/trunk/rxprtutl.c@ 11

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

Rewrote PrinterObjectHandle function.

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