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
RevLine 
[3]1/******************************************************************************
2 * REXX OS/2 Printer Utility API (RXPRTUTL.DLL) *
3 * (C) 2011 Alex Taylor *
4 * *
5 * LICENSE: *
6 * *
7 * Redistribution and use in source and binary forms, with or without *
8 * modification, are permitted provided that the following conditions are *
9 * met: *
10 * *
11 * 1. Redistributions of source code must retain the above copyright *
12 * notice, this list of conditions and the following disclaimer. *
13 * *
14 * 2. Redistributions in binary form must reproduce the above copyright *
15 * notice, this list of conditions and the following disclaimer in the *
16 * documentation and/or other materials provided with the distribution. *
17 * *
18 * 3. The name of the author may not be used to endorse or promote products *
19 * derived from this software without specific prior written permission. *
20 * *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR *
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED *
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
24 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, *
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, *
29 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN *
30 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *
31 * POSSIBILITY OF SUCH DAMAGE. *
32 * *
33 ******************************************************************************/
34
35#define INCL_DEV
36#define INCL_DOSERRORS
37#define INCL_DOSMISC
38#define INCL_DOSMODULEMGR
39#define INCL_DOSPROFILE
40#define INCL_SPL
41#define INCL_SPLDOSPRINT
42#define INCL_SPLERRORS
43#define INCL_PM
44#define INCL_WIN
45#define INCL_WINWORKPLACE
46#ifndef OS2_INCLUDED
47 #include <os2.h>
48#endif
49#include <ctype.h>
50#include <stdio.h>
51#include <stdlib.h>
52#include <string.h>
53#define INCL_RXSHV
54#define INCL_RXFUNC
55#include <rexxsaa.h>
56
57
58// CONSTANTS
59
60#define SZ_LIBRARY_NAME "RXPRTUTL" // Name of this library
61#define SZ_ERROR_NAME "RPUERROR" // REXX variable used to store error codes
[13]62#define SZ_VERSION "0.2.2" // Current version of this library
[3]63
64#define APPNAME_LEAD_STR "PM_"
65#define APPNAME_PM_PORT_DRIVER "PM_PORT_DRIVER"
66#define APPNAME_PM_SPOOLER_PORT "PM_SPOOLER_PORT"
67#define APPNAME_PM_PRINT_OBJECT "PM_PrintObject"
[11]68#define APPNAME_PM_ABS_OBJECTS "PM_Abstract:Objects"
[3]69#define APPNAME_PM_WPS_LOCATION "PM_Workplace:Location"
70
[11]71#define ID_OBJINFO_QUEUENAME 3
72#define ID_OBJINFO_RPQUEUENAME 13
73
[3]74// Values that should be defined in pmsplb.h if it actually existed
[13]75#define TYPE_SHORT_WAIT 1
[3]76#define TYPE_LONG_WAIT 2
77#define BIDI_SET_PORTDRV 0x19
[13]78#define BIDI_Q_PORTDRV 0x8019
[3]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
[8]96#define US_PRTDEV_MAXZ 9 // ...of a print device name
[3]97
[11]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
[3]122// List of functions to be registered by RPULoadFuncs
123static PSZ RxFunctionTbl[] = {
124 "RPUDropFuncs",
[5]125 "RPUEnumModels",
[3]126 "RPUEnumDrivers",
127 "RPUEnumPorts",
128 "RPUEnumPrinters",
[5]129 "RPUQueueDefault",
130 "RPUQueueHold",
131 "RPUOpenView",
[10]132 "RPUPortDelete",
[3]133 "RPUPortDialog",
[14]134 "RPUPortInfo",
[3]135 "RPUPortInstall",
[13]136 "RPUPortQuery",
[3]137 "RPUPortSet",
[5]138 "RPUPrinterCreate",
[7]139 "RPUPrinterDelete",
[3]140 "RPUPrinterQuery",
141 "RPUVersion"
142};
143
144
145// FUNCTION DECLARATIONS
146
147// Exported REXX functions
148RexxFunctionHandler RPULoadFuncs;
149RexxFunctionHandler RPUDropFuncs;
150RexxFunctionHandler RPUVersion;
[5]151RexxFunctionHandler RPUEnumModels;
[3]152RexxFunctionHandler RPUEnumDrivers;
153RexxFunctionHandler RPUEnumPorts;
154RexxFunctionHandler RPUEnumPrinters;
[5]155RexxFunctionHandler RPUOpenView;
[10]156RexxFunctionHandler RPUPortDelete;
[3]157RexxFunctionHandler RPUPortDialog;
[14]158RexxFunctionHandler RPUPortInfo;
[3]159RexxFunctionHandler RPUPortInstall;
[13]160RexxFunctionHandler RPUPortQuery;
[3]161RexxFunctionHandler RPUPortSet;
[5]162RexxFunctionHandler RPUPrinterCreate;
[7]163RexxFunctionHandler RPUPrinterDelete;
[3]164RexxFunctionHandler RPUPrinterQuery;
[5]165RexxFunctionHandler RPUQueueDefault;
166RexxFunctionHandler RPUQueueHold;
[3]167
168// TODO
169
170// Internal functions
171PSZ GetObjectID( PSZ pszHandle );
172HOBJECT PrinterObjectHandle( PSZ pszQueueName );
173BOOL SaveResultString( PRXSTRING prsResult, PCH pchBytes, ULONG ulBytes );
[8]174ULONG UniqueDeviceName( PSZ pszName );
[3]175BOOL WriteCompoundVariable( PSZ pszStem, PSZ pszTail, PSZ pszValue );
[13]176BOOL WriteSimpleVariable( PSZ pszName, PSZ pszValue );
[3]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/* ------------------------------------------------------------------------- *
[5]262 * RPUEnumModels *
[3]263 * *
[5]264 * Gets a list of the printer models supported by the specified print *
265 * driver. *
[3]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 * ------------------------------------------------------------------------- */
[5]274ULONG APIENTRY RPUEnumModels( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
[3]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
[13]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 */
[12]539 ULONG flType = SPL_PR_QUEUE | SPL_PR_DIRECT_DEVICE | SPL_PR_LOCAL_ONLY,
[3]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 */
[11]636 rc = SplQueryQueue( pPInfo->pszComputerName,
637 pPInfo->pszPrintDestinationName,
[3]638 6L, NULL, 0L, &cbNeeded );
639 if (( rc != NO_ERROR ) && ( rc != NERR_BufTooSmall )) {
640 WriteErrorCode( rc, "SplQueryQueue");
641 MAKERXSTRING( *prsResult, "0", 1 );
[11]642 //goto cleanup;
643 continue;
[3]644 }
645 pbQueue = malloc( cbNeeded );
646 if ( !pbQueue ) {
647 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
648 MAKERXSTRING( *prsResult, "0", 1 );
[11]649 //goto cleanup;
650 continue;
[3]651 }
652 cbBuf = cbNeeded;
[11]653 rc = SplQueryQueue( pPInfo->pszComputerName, pPInfo->pszPrintDestinationName,
[3]654 6L, pbQueue, cbBuf, &cbNeeded );
655 if ( rc != NO_ERROR ) {
656 WriteErrorCode( rc, "SplQueryQueue");
657 MAKERXSTRING( *prsResult, "0", 1 );
658 free( pbQueue );
[11]659 //goto cleanup;
660 continue;
[3]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/* ------------------------------------------------------------------------- *
[5]709 * RPUOpenView *
[3]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 * ------------------------------------------------------------------------- */
[5]727ULONG APIENTRY RPUOpenView( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
[3]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 );
[11]761 if ( !hObj ) {
762 MAKERXSTRING( *prsResult, "0", 1 );
763 return ( 0 );
764 }
[3]765
766 fRC = WinOpenObject( hObj, ulView, TRUE );
767
768 MAKERXSTRING( *prsResult, fRC? "1": "0", 1 );
769 return ( 0 );
770}
771
772
773/* ------------------------------------------------------------------------- *
[14]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/* ------------------------------------------------------------------------- *
[3]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 *
[13]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) *
[3]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/* ------------------------------------------------------------------------- *
[13]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/* ------------------------------------------------------------------------- *
[3]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 );
[13]1115 pszPdrName = strupr( argv[0].strptr );
[3]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 );
[13]1205 pszPdrName = strupr( argv[0].strptr );
[3]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
[5]1257/* ------------------------------------------------------------------------- *
[10]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/* ------------------------------------------------------------------------- *
[5]1296 * RPUPrinterCreate *
1297 * *
1298 * Creates a new local printer object. The associated print queue and *
[8]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. *
[5]1302 * *
[8]1303 * The WPS object is created with default settings, and will be assigned an *
1304 * object-ID automatically by the WPS. *
[5]1305 * *
[8]1306 * NOTE: This function will NOT create a remote (LAN) printer object. *
[5]1307 * *
1308 * REXX ARGUMENTS: *
[8]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) *
[5]1312 * 3. The name of the printer port, which must exist already. (REQUIRED) *
1313 * 4. The default printer driver.model to be associated with *
[8]1314 * the device. If specified, this must be installed in the *
1315 * system already. If not specified, IBMNULL is assumed. (OPTIONAL) *
[5]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,
[6]1327 pszTitle = NULL;
[8]1328 CHAR szDevice[ US_PRTDEV_MAXZ ] = {0};
[5]1329 SPLERR rc;
1330
1331
1332 // Reset the error indicator
1333 WriteErrorCode( 0, NULL );
1334
1335 // Validate the REXX arguments
[8]1336 if (( argc < 3 ) || ( argc > 4 ) || ( ! RXVALIDSTRING( argv[0] )) ||
[5]1337 ( ! RXVALIDSTRING( argv[1] )) || ( ! RXVALIDSTRING( argv[2] )))
1338 return ( 40 );
1339 if (( argc > 3 ) && ( ! RXVALIDSTRING( argv[3] )))
1340 return ( 40 );
1341
[8]1342 pszTitle = argv[0].strptr;
1343 pszQueueName = argv[1].strptr;
[5]1344 pszPortName = argv[2].strptr;
1345 if ( argc > 3 ) pszModel = argv[3].strptr;
1346
[8]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 }
[5]1357
[8]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");
[5]1368 MAKERXSTRING( *prsResult, "0", 1 );
1369 return ( 0 );
1370 }
1371
[8]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");
[5]1383 MAKERXSTRING( *prsResult, "0", 1 );
[8]1384 SplDeleteDevice( NULL, szDevice );
[5]1385 return ( 0 );
1386 }
1387
[7]1388 MAKERXSTRING( *prsResult, "1", 1 );
1389 return ( 0 );
1390}
[6]1391
[7]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 *
[10]1404 * device were destroyed successfully.) *
[7]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 );
[6]1431 }
[7]1432 pInfo = (PPRQINFO3) malloc( cbBuf );
1433 if ( !pInfo ) {
1434 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
[5]1435 MAKERXSTRING( *prsResult, "0", 1 );
1436 return ( 0 );
1437 }
[7]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
[13]1453 // - NB This causes a long delay when deleting an offline LAN printer
[7]1454 if ( hObj != NULLHANDLE ) WinDestroyObject( hObj );
1455
1456 MAKERXSTRING( *prsResult, "1", 1 );
1457cleanup:
1458 free( pInfo );
[5]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 *
[12]1471 * (stem).i.!jobflags Zero or more of the following flags (any order): *
[5]1472 * E A printer error has occurred *
[12]1473 * H Printer destination is paused (held) *
[5]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;
[12]1493 CHAR szFlags[ 8 ] = {0},
1494 szStem[ US_STEM_MAXZ ]; // compound variable stem name
[5]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 );
[12]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 );
[5]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
[3]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{
[11]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 ];
[3]1678 ULONG cbTotal = 0,
[11]1679 cbActual = 0,
1680 ulHandle;
1681 HOBJECT hObj = 0;
1682 BOOL fFound,
1683 fRC;
[3]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
[11]1705 fFound = FALSE;
[3]1706 psz = (PSZ) pbuf;
[11]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 */
[3]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 {
[11]1785 fFound = TRUE;
[3]1786 break;
1787 }
[11]1788#endif
[3]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
[8]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 );
[9]1910 if ( rc == NO_ERROR && !fUnique ) return 1;
[8]1911 }
1912 }
[9]1913 else if ( rc == NO_ERROR ) fUnique = TRUE;
1914 return rc;
[8]1915}
1916
1917
[3]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/* ------------------------------------------------------------------------- *
[13]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/* ------------------------------------------------------------------------- *
[3]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.