source: rxprtutl/trunk/rxprtutl.c@ 12

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

RPUEnumPrinters: Do not attempt to read the remote printer queue of LAN printers.
RPUPrinterQuery: Return additional job flags.

File size: 83.7 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 | SPL_PR_LOCAL_ONLY,
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 pPInfo->pszPrintDestinationName);
635 WriteCompoundVariable( szStemNode, "!flags", "");
636 WriteCompoundVariable( szStemNode, "!handle", "");
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 * (stem).i.!jobflags Zero or more of the following flags (any order): *
1282 * E A printer error has occurred *
1283 * H Printer destination is paused (held) *
1284 * I Intervention required *
1285 * N Printer has raised a notification alert *
1286 * O Printer is offline *
1287 * P Printer is out of paper *
1288 * *
1289 * REXX ARGUMENTS: *
1290 * 1. The name of the printer device being queried. (REQUIRED) *
1291 * 2. The name of the stem in which to return the results. (REQUIRED) *
1292 * *
1293 * REXX RETURN VALUE: *
1294 * 1 on success, or 0 if an error occurred. *
1295 * ------------------------------------------------------------------------- */
1296ULONG APIENTRY RPUPrinterQuery( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1297{
1298 PPRDINFO3 pInfo = NULL;
1299 PVOID pbuf = NULL;
1300 ULONG cbBuf = 0,
1301 cbNeeded = 0;
1302 PSZ pszDeviceName;
1303 CHAR szFlags[ 8 ] = {0},
1304 szStem[ US_STEM_MAXZ ]; // compound variable stem name
1305 SPLERR rc;
1306
1307
1308 // Reset the error indicator
1309 WriteErrorCode( 0, NULL );
1310
1311 // Validate the REXX arguments
1312 if (( argc != 2 ) ||
1313 ( ! RXVALIDSTRING( argv[0] )) || ( ! RXVALIDSTRING( argv[1] )))
1314 return ( 40 );
1315
1316 pszDeviceName = argv[0].strptr;
1317//printf("Device: \"%s\"\n", pszDeviceName );
1318
1319 // Initialize the result stem name
1320 if ( RXSTRLEN(argv[1]) > US_STEM_MAXZ ) return ( 40 );
1321 if ( argv[1].strptr[ argv[1].strlength-1 ] == '.') argv[1].strlength--;
1322 strncpy( szStem, argv[1].strptr, RXSTRLEN( argv[1] ));
1323 szStem[ RXSTRLEN( argv[1] ) ] = '\0';
1324
1325
1326 // Query the amount of available data
1327 rc = SplQueryDevice( NULL, pszDeviceName, 3L, NULL, 0L, &cbNeeded );
1328 if (( rc != ERROR_MORE_DATA ) && ( rc != NERR_BufTooSmall ))
1329 {
1330 WriteErrorCode( rc, "SplQueryDevice");
1331 MAKERXSTRING( *prsResult, "0", 1 );
1332 return ( 0 );
1333 }
1334
1335 // Now get the actual data
1336 pbuf = malloc( cbNeeded );
1337 if ( !pbuf ) {
1338 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
1339 MAKERXSTRING( *prsResult, "0", 1 );
1340 return ( 0 );
1341 }
1342 cbBuf = cbNeeded;
1343 rc = SplQueryDevice( NULL, pszDeviceName, 3L, pbuf, cbBuf, &cbNeeded );
1344 if ( rc == NO_ERROR ) {
1345 pInfo = (PPRDINFO3) pbuf;
1346 WriteCompoundVariable( szStem, "!name", pInfo->pszPrinterName );
1347 WriteCompoundVariable( szStem, "!port", pInfo->pszLogAddr );
1348 WriteCompoundVariable( szStem, "!description", pInfo->pszComment );
1349 WriteCompoundVariable( szStem, "!drivers", pInfo->pszDrivers );
1350 if ( pInfo->fsStatus & PRJ_ERROR ) strcat( szFlags, "E");
1351 if ( pInfo->fsStatus & PRJ_DESTPAUSED ) strcat( szFlags, "H");
1352 if ( pInfo->fsStatus & PRJ_INTERV ) strcat( szFlags, "I");
1353 if ( pInfo->fsStatus & PRJ_NOTIFY ) strcat( szFlags, "N");
1354 if ( pInfo->fsStatus & PRJ_DESTOFFLINE ) strcat( szFlags, "O");
1355 if ( pInfo->fsStatus & PRJ_DESTNOPAPER ) strcat( szFlags, "P");
1356 WriteCompoundVariable( szStem, "!jobflags", szFlags );
1357 MAKERXSTRING( *prsResult, "1", 1 );
1358 }
1359 else {
1360 WriteErrorCode( rc, "SplQueryDevice");
1361 MAKERXSTRING( *prsResult, "0", 1 );
1362 }
1363
1364 free( pbuf );
1365 return ( 0 );
1366}
1367
1368
1369/* ------------------------------------------------------------------------- *
1370 * RPUQueueDefault *
1371 * *
1372 * Sets the requested printer queue as the system default printer. *
1373 * *
1374 * REXX ARGUMENTS: *
1375 * 1. The name of the printer queue to set as default. (REQUIRED) *
1376 * *
1377 * REXX RETURN VALUE: *
1378 * 1 on success, or 0 if an error occurred. *
1379 * ------------------------------------------------------------------------- */
1380ULONG APIENTRY RPUQueueDefault( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1381{
1382 HOBJECT hObj;
1383 BOOL fRC;
1384
1385 // Reset the error indicator
1386 WriteErrorCode( 0, NULL );
1387
1388 // Make sure we have at least one valid argument (the queue name)
1389 if ( argc != 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
1390 pszName = argv[0].strptr;
1391
1392 hObj = PrinterObjectHandle( pszName );
1393 if ( !hObj ) return ( 40 );
1394
1395 fRC = WinSetObjectData( hObj, "APPDEFAULT=YES;");
1396
1397 MAKERXSTRING( *prsResult, fRC? "1": "0", 1 );
1398 return ( 0 );
1399}
1400
1401
1402/* ------------------------------------------------------------------------- *
1403 * RPUQueueHold *
1404 * *
1405 * Holds or releases the specified print queue. *
1406 * *
1407 * REXX ARGUMENTS: *
1408 * 1. The name of the printer queue to hold or release. (REQUIRED) *
1409 * 2. Action flag, one of: *
1410 * Y Hold printer (DEFAULT) *
1411 * N Release printer *
1412 * *
1413 * REXX RETURN VALUE: *
1414 * 1 on success, or 0 if an error occurred. *
1415 * ------------------------------------------------------------------------- */
1416ULONG APIENTRY RPUQueueHold( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1417{
1418 PSZ pszQueueName; // Print queue name
1419 BOOL fHold = TRUE;
1420 SPLERR rc;
1421
1422
1423 // Reset the error indicator
1424 WriteErrorCode( 0, NULL );
1425
1426 // Make sure we have at least one valid argument (the queue name)
1427 if ( argc < 1 || argc > 2 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
1428 pszQueueName = argv[0].strptr;
1429
1430 // Second argument: view (optional, but must be correct if specified)
1431 if ( argc == 2 ) {
1432 if ( RXVALIDSTRING(argv[1]) ) {
1433 switch ( argv[1].strptr[0] ) {
1434 case 'y':
1435 case 'Y': fHold = TRUE; break;
1436 case 'n':
1437 case 'N': fHold = FALSE; break;
1438 default : return ( 40 );
1439 }
1440 }
1441 else return ( 40 );
1442 }
1443
1444 rc = fHold ? SplHoldQueue( NULL, pszQueueName ) :
1445 SplReleaseQueue( NULL, pszQueueName );
1446 if ( rc != NO_ERROR ) {
1447 WriteErrorCode( rc, fHold ? "SplHoldQueue" : "SplReleaseQueue");
1448 MAKERXSTRING( *prsResult, "0", 1 );
1449 }
1450 else MAKERXSTRING( *prsResult, "1", 1 );
1451
1452 return ( 0 );
1453}
1454
1455
1456/* ************************************************************************* *
1457 * INTERNAL PRINTER-RELATED FUNCTIONS *
1458 * ************************************************************************* */
1459
1460/* ------------------------------------------------------------------------- *
1461 * PrinterObjectHandle *
1462 * *
1463 * Given a print queue name, obtain the handle to the WPS printer object *
1464 * associated with that queue. This is done by querying OS2.INI for the *
1465 * queues defined in PM_PrintObject and reading the associated handle IDs. *
1466 * *
1467 * ARGUMENTS: *
1468 * PSZ pszQueueName: The printer queue name. This is NOT the same as the *
1469 * printer device name, although in many cases they may *
1470 * have the same value. *
1471 * *
1472 * RETURNS: HOBJECT *
1473 * WPS object handle of the printer object; NULLHANDLE if no object was *
1474 * found or an error occurred. *
1475 * ------------------------------------------------------------------------- */
1476HOBJECT PrinterObjectHandle( PSZ pszQueueName )
1477{
1478 PVOID pbuf = NULL,
1479 pdata = NULL;
1480 PBYTE pb,
1481 pbEnd,
1482 pbGrpEnd;
1483 POBJINFO pInfo = NULL;
1484 POITAG pTag = NULL;
1485 PSZ psz,
1486 pszName;
1487 CHAR szValue[ 256 ],
1488 szObject[ 5 ];
1489 ULONG cbTotal = 0,
1490 cbActual = 0,
1491 ulHandle;
1492 HOBJECT hObj = 0;
1493 BOOL fFound,
1494 fRC;
1495
1496
1497 fRC = PrfQueryProfileSize( HINI_USERPROFILE,
1498 APPNAME_PM_PRINT_OBJECT, NULL, &cbTotal );
1499 if ( !fRC || !cbTotal ) {
1500 //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileSize");
1501 return NULLHANDLE;
1502 }
1503 pbuf = malloc( cbTotal );
1504 if ( !pbuf ) {
1505 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
1506 return NULLHANDLE;
1507 }
1508
1509 cbActual = PrfQueryProfileString( HINI_USERPROFILE, APPNAME_PM_PRINT_OBJECT,
1510 NULL, NULL, pbuf, cbTotal );
1511 if ( !cbActual ) {
1512 //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString");
1513 return NULLHANDLE;
1514 }
1515
1516 fFound = FALSE;
1517 psz = (PSZ) pbuf;
1518 while ( *psz && !fFound ) {
1519#if 1
1520
1521 /* Just checking the queue names associated with the handle in
1522 * PM_PrintObject is not reliable, because remote (LAN) printers
1523 * will store the name of the _remote_ queue there (which is not
1524 * only impossible to map to a local queue name, it may actually
1525 * be the same AS a local queue name... for a different printer).
1526 *
1527 * So what we do here is parse the queue name out of the object
1528 * settings (from PM_Abstract:Objects). We also have to handle
1529 * remote printers slightly differently from local ones.
1530 *
1531 * I am indebted to Henk Kelder's WPTOOLS source code for providing
1532 * the basic logic, although the code below is largely original.
1533 */
1534
1535 if ( sscanf( psz, "%u", &ulHandle ) && LOUSHORT( ulHandle )) {
1536 sprintf( szObject, "%X", LOUSHORT( ulHandle ));
1537 fRC = PrfQueryProfileSize( HINI_USERPROFILE, APPNAME_PM_ABS_OBJECTS,
1538 szObject, &cbTotal );
1539 if ( !fRC || ( cbTotal < 25 ) || ((pdata = malloc( cbTotal )) == NULL )) {
1540 psz += strlen( psz ) + 1;
1541 continue;
1542 }
1543 if ( PrfQueryProfileData( HINI_USERPROFILE, APPNAME_PM_ABS_OBJECTS,
1544 szObject, (PVOID) pdata, &cbTotal ))
1545 {
1546 BOOL fRemote;
1547 pbEnd = (PBYTE) pdata + cbTotal;
1548 pszName = (PSZ) pdata + sizeof( ULONG );
1549 fRemote = strcmp( pszName, "WPRPrinter") ? FALSE : TRUE;
1550 pb = (PBYTE) pszName + strlen( pszName ) + 17;
1551 while (( pb < pbEnd ) && !fFound ) {
1552 pInfo = (POBJINFO) pb;
1553 pb += sizeof( OBJINFO ) + strlen( pInfo->szName );
1554 pbGrpEnd = pb + pInfo->cbData;
1555 if ( pbGrpEnd > pbEnd ) pbGrpEnd = pbEnd;
1556 while (( pb < pbGrpEnd ) && !fFound ) {
1557 pTag = (POITAG) pb;
1558 pb += sizeof( OITAG );
1559 /* For network printers, tag ID 3 indicates the name of
1560 * the queue on the remote server. We want the local
1561 * queue name, which has tag ID 13.
1562 */
1563 if ( fRemote && ( pTag->usTag == ID_OBJINFO_RPQUEUENAME )) {
1564 memcpy( szValue, pb, pTag->cbTag );
1565 if ( !strcmp( szValue, pszQueueName )) {
1566 hObj = (HOBJECT) ulHandle;
1567 fFound = TRUE;
1568 }
1569 }
1570 // For local printers, just look for tag ID 3
1571 else if ( pTag->usTag == ID_OBJINFO_QUEUENAME ) {
1572 memcpy( szValue, pb, pTag->cbTag );
1573 if ( !strcmp( szValue, pszQueueName )) {
1574 hObj = (HOBJECT) ulHandle;
1575 fFound = TRUE;
1576 }
1577 }
1578 pb += pTag->cbTag;
1579 }
1580 }
1581 }
1582 free( pdata );
1583 }
1584#else
1585 /* Old method, do not use (unreliable for the reasons noted above).
1586 */
1587 if ( !PrfQueryProfileString( HINI_USERPROFILE, APPNAME_PM_PRINT_OBJECT,
1588 psz, NULL, szValue, 255 ))
1589 {
1590 //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString");
1591 break;
1592 }
1593 if (( strcmp( szValue, pszQueueName ) == 0 ) &&
1594 ( sscanf( psz, "%u", (PULONG) &hObj ) == 1 ))
1595 {
1596 fFound = TRUE;
1597 break;
1598 }
1599#endif
1600 psz += strlen( psz ) + 1;
1601 }
1602
1603 free ( pbuf );
1604 return ( hObj );
1605}
1606
1607
1608/* ------------------------------------------------------------------------- *
1609 * GetObjectID *
1610 * *
1611 * Given a WPS object handle, find out the corresponding object ID if one *
1612 * exists. *
1613 * *
1614 * ARGUMENTS: *
1615 * PSZ pszHandle: The WPS object handle, as a hexadecimal string. This *
1616 * should have no prefix, and be all upper-case. *
1617 * *
1618 * RETURNS: PSZ *
1619 * The object ID string. It is up to the caller to free it when done. *
1620 * This will be NULL if no object ID was found. *
1621 * ------------------------------------------------------------------------- */
1622PSZ GetObjectID( PSZ pszHandle )
1623{
1624 PVOID pbuf = NULL;
1625 ULONG cbTotal = 0,
1626 cbActual = 0;
1627 CHAR szValue[ 9 ];
1628 PSZ psz,
1629 pszObjID = NULL;
1630 BOOL fRC;
1631
1632
1633 fRC = PrfQueryProfileSize( HINI_USERPROFILE,
1634 APPNAME_PM_WPS_LOCATION, NULL, &cbTotal );
1635 if ( !fRC || !cbTotal ) {
1636 //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileSize");
1637 return NULL;
1638 }
1639 pbuf = malloc( cbTotal );
1640 if ( !pbuf ) {
1641 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
1642 return NULL;
1643 }
1644
1645 cbActual = PrfQueryProfileString( HINI_USERPROFILE, APPNAME_PM_WPS_LOCATION,
1646 NULL, NULL, pbuf, cbTotal );
1647 if ( !cbActual ) {
1648 //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString");
1649 return NULL;
1650 }
1651
1652 psz = (PSZ) pbuf;
1653 while ( *psz ) {
1654 if ( !PrfQueryProfileString( HINI_USERPROFILE, APPNAME_PM_WPS_LOCATION,
1655 psz, NULL, szValue, 8 ))
1656 {
1657 //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString");
1658 break;
1659 }
1660 if (( strcmp( szValue, pszHandle ) == 0 ) &&
1661 (( pszObjID = strdup( psz )) != NULL ))
1662 {
1663 break;
1664 }
1665 psz += strlen( psz ) + 1;
1666 }
1667
1668 free ( pbuf );
1669 return ( pszObjID );
1670}
1671
1672
1673/* ------------------------------------------------------------------------- *
1674 * UniqueDeviceName *
1675 * *
1676 * Check (and, if necessary, modify) the specified print device name to make *
1677 * sure it is unique. *
1678 * *
1679 * ARGUMENTS: *
1680 * PSZ pszName : Pointer to device name buffer (9-byte CHAR buffer) *
1681 * *
1682 * RETURNS: ULONG *
1683 * 0 on success, 1 if no unique name could be generated, or the return *
1684 * code from SplEnumDevice() if an error occurred. *
1685 * ------------------------------------------------------------------------- */
1686ULONG UniqueDeviceName( PSZ pszName )
1687{
1688 PBYTE pBuf;
1689 PPRDINFO3 pprd3;
1690 CHAR szNumber[ US_PRTDEV_MAXZ ] = {0};
1691 ULONG i, n, pos,
1692 ulNumber = 0,
1693 ulAvail = 0,
1694 cbBuf = 0;
1695 BOOL fUnique = FALSE;
1696 SPLERR rc;
1697
1698
1699 rc = SplEnumDevice( NULL, 3, NULL, 0, &ulNumber, &ulAvail, &cbBuf, NULL );
1700 if ( rc == ERROR_MORE_DATA || rc == NERR_BufTooSmall ) {
1701 pBuf = malloc( cbBuf );
1702 if ( pBuf ) {
1703 rc = SplEnumDevice( NULL, 3, pBuf, cbBuf, &ulNumber, &ulAvail, &cbBuf, NULL );
1704 if ( rc == NO_ERROR ) {
1705 n = 1;
1706 while ( !fUnique && ( n < 999 )) { // max 999 as a sanity check
1707 for ( i = 0; i < ulNumber; i++ ) {
1708 pprd3 = (PPRDINFO3) pBuf + i;
1709 if ( stricmp( pszName, pprd3->pszPrinterName ) == 0 ) break;
1710 }
1711 if ( i >= ulNumber ) fUnique = TRUE;
1712 else {
1713 sprintf( szNumber, "%u", n++ );
1714 pos = strlen( pszName ) - strlen( szNumber );
1715 pszName[ pos ] = '\0';
1716 strncat( pszName, szNumber, US_PRTDEV_MAXZ-1 );
1717 }
1718 }
1719 }
1720 free( pBuf );
1721 if ( rc == NO_ERROR && !fUnique ) return 1;
1722 }
1723 }
1724 else if ( rc == NO_ERROR ) fUnique = TRUE;
1725 return rc;
1726}
1727
1728
1729/* ************************************************************************* *
1730 * INTERNAL REXX DLL UTILITY FUNCTIONS *
1731 * ************************************************************************* */
1732
1733
1734/* ------------------------------------------------------------------------- *
1735 * SaveResultString *
1736 * *
1737 * Writes new string contents to the specified RXSTRING, allocating any *
1738 * additional memory that may be required. If the string to be written has *
1739 * zero length, nothing is done. *
1740 * *
1741 * This function should be used in place of MAKERXSTRING if there is a *
1742 * possibility that the string contents could be longer than 256 characters. *
1743 * *
1744 * ARGUMENTS: *
1745 * PRXSTRING prsResult: Pointer to an existing RXSTRING for writing. *
1746 * PCH pchBytes : The string contents to write to prsResult. *
1747 * ULONG ulBytes : The number of bytes in pchBytes to write. *
1748 * *
1749 * RETURNS: BOOL *
1750 * TRUE if prsResult was successfully updated. FALSE otherwise. *
1751 * ------------------------------------------------------------------------- */
1752BOOL SaveResultString( PRXSTRING prsResult, PCH pchBytes, ULONG ulBytes )
1753{
1754 ULONG ulRC;
1755 PCH pchNew;
1756
1757 if ( ulBytes == 0 ) return ( FALSE );
1758 if ( ulBytes > 256 ) {
1759 // REXX provides 256 bytes by default; allocate more if necessary
1760 ulRC = DosAllocMem( (PVOID) &pchNew, ulBytes, PAG_WRITE | PAG_COMMIT );
1761 if ( ulRC != 0 ) {
1762 WriteErrorCode( ulRC, "DosAllocMem");
1763 return ( FALSE );
1764 }
1765 DosFreeMem( prsResult->strptr );
1766 prsResult->strptr = pchNew;
1767 }
1768 memcpy( prsResult->strptr, pchBytes, ulBytes );
1769 prsResult->strlength = ulBytes;
1770
1771 return ( TRUE );
1772}
1773
1774
1775/* ------------------------------------------------------------------------- *
1776 * WriteStemElement *
1777 * *
1778 * Creates a stem element (compound variable) in the calling REXX program *
1779 * using the REXX shared variable pool interface. *
1780 * *
1781 * ARGUMENTS: *
1782 * PSZ pszStem : The name of the stem (before the '.') *
1783 * ULONG ulIndex : The number of the stem element (after the '.') *
1784 * PSZ pszValue : The value to write to the compound variable. *
1785 * *
1786 * RETURNS: BOOL *
1787 * TRUE on success, FALSE on failure. *
1788 * ------------------------------------------------------------------------- */
1789BOOL WriteStemElement( PSZ pszStem, ULONG ulIndex, PSZ pszValue )
1790{
1791 SHVBLOCK shvVar; // REXX shared variable pool block
1792 ULONG ulRc,
1793 ulBytes;
1794 CHAR szCompoundName[ US_COMPOUND_MAXZ ],
1795 *pchValue;
1796
1797 sprintf( szCompoundName, "%s.%d", pszStem, ulIndex );
1798 if ( pszValue == NULL ) {
1799 pchValue = "";
1800 ulBytes = 0;
1801 } else {
1802 ulBytes = strlen( pszValue );
1803 ulRc = DosAllocMem( (PVOID) &pchValue, ulBytes + 1, PAG_WRITE | PAG_COMMIT );
1804 if ( ulRc != 0 ) {
1805 WriteErrorCode( ulRc, "DosAllocMem");
1806 return FALSE;
1807 }
1808 memcpy( pchValue, pszValue, ulBytes );
1809 }
1810 MAKERXSTRING( shvVar.shvname, szCompoundName, strlen(szCompoundName) );
1811 shvVar.shvvalue.strptr = pchValue;
1812 shvVar.shvvalue.strlength = ulBytes;
1813 shvVar.shvnamelen = RXSTRLEN( shvVar.shvname );
1814 shvVar.shvvaluelen = RXSTRLEN( shvVar.shvvalue );
1815 shvVar.shvcode = RXSHV_SYSET;
1816 shvVar.shvnext = NULL;
1817 ulRc = RexxVariablePool( &shvVar );
1818 if ( ulRc > 1 ) {
1819 WriteErrorCode( shvVar.shvret, "RexxVariablePool (SHVBLOCK.shvret)");
1820 return FALSE;
1821 }
1822 return TRUE;
1823
1824}
1825
1826
1827/* ------------------------------------------------------------------------- *
1828 * WriteCompoundVariable *
1829 * *
1830 * Creates a compound variable in the calling REXX program using the REXX *
1831 * shared variable pool interface. *
1832 * *
1833 * ARGUMENTS: *
1834 * PSZ pszStem : The name of the stem (before the '.') *
1835 * PSZ pszTail : The name of the trailing portion (after the '.') *
1836 * PSZ pszValue : The value to write to the compound variable. *
1837 * *
1838 * RETURNS: BOOL *
1839 * TRUE on success, FALSE on failure. *
1840 * ------------------------------------------------------------------------- */
1841BOOL WriteCompoundVariable( PSZ pszStem, PSZ pszTail, PSZ pszValue )
1842{
1843 SHVBLOCK shvVar; // REXX shared variable pool block
1844 ULONG ulRc,
1845 ulBytes;
1846 CHAR szCompoundName[ US_COMPOUND_MAXZ ],
1847 *pchValue;
1848
1849 sprintf( szCompoundName, "%s.%s", pszStem, pszTail );
1850 if ( pszValue == NULL ) {
1851 pchValue = "";
1852 ulBytes = 0;
1853 } else {
1854 ulBytes = strlen( pszValue );
1855 ulRc = DosAllocMem( (PVOID) &pchValue, ulBytes + 1, PAG_WRITE | PAG_COMMIT );
1856 if ( ulRc != 0 ) {
1857 WriteErrorCode( ulRc, "DosAllocMem");
1858 return FALSE;
1859 }
1860 memcpy( pchValue, pszValue, ulBytes );
1861 }
1862 MAKERXSTRING( shvVar.shvname, szCompoundName, strlen(szCompoundName) );
1863 shvVar.shvvalue.strptr = pchValue;
1864 shvVar.shvvalue.strlength = ulBytes;
1865 shvVar.shvnamelen = RXSTRLEN( shvVar.shvname );
1866 shvVar.shvvaluelen = RXSTRLEN( shvVar.shvvalue );
1867 shvVar.shvcode = RXSHV_SYSET;
1868 shvVar.shvnext = NULL;
1869 ulRc = RexxVariablePool( &shvVar );
1870 if ( ulRc > 1 ) {
1871 WriteErrorCode( shvVar.shvret, "RexxVariablePool (SHVBLOCK.shvret)");
1872 return FALSE;
1873 }
1874 return TRUE;
1875}
1876
1877
1878/* ------------------------------------------------------------------------- *
1879 * WriteErrorCode *
1880 * *
1881 * Writes an error code to a special variable in the calling REXX program *
1882 * using the REXX shared variable pool interface. This is used to return *
1883 * API error codes to the REXX program, since the REXX functions themselves *
1884 * normally return string values. *
1885 * *
1886 * ARGUMENTS: *
1887 * ULONG ulError : The error code returned by the failing API call. *
1888 * PSZ pszContext: A string describing the API call that failed. *
1889 * *
1890 * RETURNS: N/A *
1891 * ------------------------------------------------------------------------- */
1892void WriteErrorCode( ULONG ulError, PSZ pszContext )
1893{
1894 SHVBLOCK shvVar; // REXX shared variable pool block
1895 ULONG ulRc;
1896 CHAR szErrorText[ US_ERRSTR_MAXZ ];
1897
1898 if ( pszContext == NULL )
1899 sprintf( szErrorText, "%X", ulError );
1900 else
1901 sprintf( szErrorText, "%X: %s", ulError, pszContext );
1902 MAKERXSTRING( shvVar.shvname, SZ_ERROR_NAME, strlen(SZ_ERROR_NAME) );
1903 MAKERXSTRING( shvVar.shvvalue, szErrorText, strlen(szErrorText) );
1904 shvVar.shvnamelen = RXSTRLEN( shvVar.shvname );
1905 shvVar.shvvaluelen = RXSTRLEN( shvVar.shvvalue );
1906 shvVar.shvcode = RXSHV_SYSET;
1907 shvVar.shvnext = NULL;
1908 ulRc = RexxVariablePool( &shvVar );
1909 if ( ulRc > 1 )
1910 printf("Unable to set %s: rc = %d\n", shvVar.shvname.strptr, shvVar.shvret );
1911}
1912
1913
Note: See TracBrowser for help on using the repository browser.