source: rxprtutl/trunk/rxprtutl.c@ 13

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

Added RPUPortQuery function, improved discussion of port driver settings in 'notes'.

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