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
RevLine 
[3]1/******************************************************************************
2 * REXX OS/2 Printer Utility API (RXPRTUTL.DLL) *
3 * (C) 2011 Alex Taylor *
4 * *
5 * LICENSE: *
6 * *
7 * Redistribution and use in source and binary forms, with or without *
8 * modification, are permitted provided that the following conditions are *
9 * met: *
10 * *
11 * 1. Redistributions of source code must retain the above copyright *
12 * notice, this list of conditions and the following disclaimer. *
13 * *
14 * 2. Redistributions in binary form must reproduce the above copyright *
15 * notice, this list of conditions and the following disclaimer in the *
16 * documentation and/or other materials provided with the distribution. *
17 * *
18 * 3. The name of the author may not be used to endorse or promote products *
19 * derived from this software without specific prior written permission. *
20 * *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR *
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED *
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
24 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, *
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, *
29 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN *
30 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *
31 * POSSIBILITY OF SUCH DAMAGE. *
32 * *
33 ******************************************************************************/
34
35#define INCL_DEV
36#define INCL_DOSERRORS
37#define INCL_DOSMISC
38#define INCL_DOSMODULEMGR
39#define INCL_DOSPROFILE
40#define INCL_SPL
41#define INCL_SPLDOSPRINT
42#define INCL_SPLERRORS
43#define INCL_PM
44#define INCL_WIN
45#define INCL_WINWORKPLACE
46#ifndef OS2_INCLUDED
47 #include <os2.h>
48#endif
49#include <ctype.h>
50#include <stdio.h>
51#include <stdlib.h>
52#include <string.h>
53#define INCL_RXSHV
54#define INCL_RXFUNC
55#include <rexxsaa.h>
56
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
[11]62#define SZ_VERSION "0.2.1" // Current version of this library
[3]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"
[11]68#define APPNAME_PM_ABS_OBJECTS "PM_Abstract:Objects"
[3]69#define APPNAME_PM_WPS_LOCATION "PM_Workplace:Location"
70
[11]71#define ID_OBJINFO_QUEUENAME 3
72#define ID_OBJINFO_RPQUEUENAME 13
73
[3]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
[8]94#define US_PRTDEV_MAXZ 9 // ...of a print device name
[3]95
[11]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
[3]120// List of functions to be registered by RPULoadFuncs
121static PSZ RxFunctionTbl[] = {
122 "RPUDropFuncs",
[5]123 "RPUEnumModels",
[3]124 "RPUEnumDrivers",
125 "RPUEnumPorts",
126 "RPUEnumPrinters",
[5]127 "RPUQueueDefault",
128 "RPUQueueHold",
129 "RPUOpenView",
[10]130 "RPUPortDelete",
[3]131 "RPUPortDialog",
132 "RPUPortInstall",
133 "RPUPortSet",
[5]134 "RPUPrinterCreate",
[7]135 "RPUPrinterDelete",
[3]136 "RPUPrinterQuery",
137 "RPUVersion"
138};
139
140
141// FUNCTION DECLARATIONS
142
143// Exported REXX functions
144RexxFunctionHandler RPULoadFuncs;
145RexxFunctionHandler RPUDropFuncs;
146RexxFunctionHandler RPUVersion;
[5]147RexxFunctionHandler RPUEnumModels;
[3]148RexxFunctionHandler RPUEnumDrivers;
149RexxFunctionHandler RPUEnumPorts;
150RexxFunctionHandler RPUEnumPrinters;
[5]151RexxFunctionHandler RPUOpenView;
[10]152RexxFunctionHandler RPUPortDelete;
[3]153RexxFunctionHandler RPUPortDialog;
154RexxFunctionHandler RPUPortInstall;
155RexxFunctionHandler RPUPortSet;
[5]156RexxFunctionHandler RPUPrinterCreate;
[7]157RexxFunctionHandler RPUPrinterDelete;
[3]158RexxFunctionHandler RPUPrinterQuery;
[5]159RexxFunctionHandler RPUQueueDefault;
160RexxFunctionHandler RPUQueueHold;
[3]161
162// TODO
163
164// Internal functions
165PSZ GetObjectID( PSZ pszHandle );
166HOBJECT PrinterObjectHandle( PSZ pszQueueName );
167BOOL SaveResultString( PRXSTRING prsResult, PCH pchBytes, ULONG ulBytes );
[8]168ULONG UniqueDeviceName( PSZ pszName );
[3]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/* ------------------------------------------------------------------------- *
[5]255 * RPUEnumModels *
[3]256 * *
[5]257 * Gets a list of the printer models supported by the specified print *
258 * driver. *
[3]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 * ------------------------------------------------------------------------- */
[5]267ULONG APIENTRY RPUEnumModels( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
[3]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 */
[11]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,
[3]645 6L, NULL, 0L, &cbNeeded );
646 if (( rc != NO_ERROR ) && ( rc != NERR_BufTooSmall )) {
647 WriteErrorCode( rc, "SplQueryQueue");
648 MAKERXSTRING( *prsResult, "0", 1 );
[11]649 //goto cleanup;
650 continue;
[3]651 }
652 pbQueue = malloc( cbNeeded );
653 if ( !pbQueue ) {
654 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
655 MAKERXSTRING( *prsResult, "0", 1 );
[11]656 //goto cleanup;
657 continue;
[3]658 }
659 cbBuf = cbNeeded;
[11]660 rc = SplQueryQueue( pPInfo->pszComputerName, pPInfo->pszPrintDestinationName,
[3]661 6L, pbQueue, cbBuf, &cbNeeded );
662 if ( rc != NO_ERROR ) {
663 WriteErrorCode( rc, "SplQueryQueue");
664 MAKERXSTRING( *prsResult, "0", 1 );
665 free( pbQueue );
[11]666 //goto cleanup;
667 continue;
[3]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/* ------------------------------------------------------------------------- *
[5]716 * RPUOpenView *
[3]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 * ------------------------------------------------------------------------- */
[5]734ULONG APIENTRY RPUOpenView( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
[3]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 );
[11]768 if ( !hObj ) {
769 MAKERXSTRING( *prsResult, "0", 1 );
770 return ( 0 );
771 }
[3]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
[5]1068/* ------------------------------------------------------------------------- *
[10]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/* ------------------------------------------------------------------------- *
[5]1107 * RPUPrinterCreate *
1108 * *
1109 * Creates a new local printer object. The associated print queue and *
[8]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. *
[5]1113 * *
[8]1114 * The WPS object is created with default settings, and will be assigned an *
1115 * object-ID automatically by the WPS. *
[5]1116 * *
[8]1117 * NOTE: This function will NOT create a remote (LAN) printer object. *
[5]1118 * *
1119 * REXX ARGUMENTS: *
[8]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) *
[5]1123 * 3. The name of the printer port, which must exist already. (REQUIRED) *
1124 * 4. The default printer driver.model to be associated with *
[8]1125 * the device. If specified, this must be installed in the *
1126 * system already. If not specified, IBMNULL is assumed. (OPTIONAL) *
[5]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,
[6]1138 pszTitle = NULL;
[8]1139 CHAR szDevice[ US_PRTDEV_MAXZ ] = {0};
[5]1140 SPLERR rc;
1141
1142
1143 // Reset the error indicator
1144 WriteErrorCode( 0, NULL );
1145
1146 // Validate the REXX arguments
[8]1147 if (( argc < 3 ) || ( argc > 4 ) || ( ! RXVALIDSTRING( argv[0] )) ||
[5]1148 ( ! RXVALIDSTRING( argv[1] )) || ( ! RXVALIDSTRING( argv[2] )))
1149 return ( 40 );
1150 if (( argc > 3 ) && ( ! RXVALIDSTRING( argv[3] )))
1151 return ( 40 );
1152
[8]1153 pszTitle = argv[0].strptr;
1154 pszQueueName = argv[1].strptr;
[5]1155 pszPortName = argv[2].strptr;
1156 if ( argc > 3 ) pszModel = argv[3].strptr;
1157
[8]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 }
[5]1168
[8]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");
[5]1179 MAKERXSTRING( *prsResult, "0", 1 );
1180 return ( 0 );
1181 }
1182
[8]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");
[5]1194 MAKERXSTRING( *prsResult, "0", 1 );
[8]1195 SplDeleteDevice( NULL, szDevice );
[5]1196 return ( 0 );
1197 }
1198
[7]1199 MAKERXSTRING( *prsResult, "1", 1 );
1200 return ( 0 );
1201}
[6]1202
[7]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 *
[10]1215 * device were destroyed successfully.) *
[7]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 );
[6]1242 }
[7]1243 pInfo = (PPRQINFO3) malloc( cbBuf );
1244 if ( !pInfo ) {
1245 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
[5]1246 MAKERXSTRING( *prsResult, "0", 1 );
1247 return ( 0 );
1248 }
[7]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 );
[5]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
[3]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{
[11]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 ];
[3]1483 ULONG cbTotal = 0,
[11]1484 cbActual = 0,
1485 ulHandle;
1486 HOBJECT hObj = 0;
1487 BOOL fFound,
1488 fRC;
[3]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
[11]1510 fFound = FALSE;
[3]1511 psz = (PSZ) pbuf;
[11]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 */
[3]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 {
[11]1590 fFound = TRUE;
[3]1591 break;
1592 }
[11]1593#endif
[3]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
[8]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 );
[9]1715 if ( rc == NO_ERROR && !fUnique ) return 1;
[8]1716 }
1717 }
[9]1718 else if ( rc == NO_ERROR ) fUnique = TRUE;
1719 return rc;
[8]1720}
1721
1722
[3]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.