source: rxprtutl/trunk/rxprtutl.c@ 14

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

Added RPUPortInfo function.

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