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
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",
134 "RPUPortInstall",
[13]135 "RPUPortQuery",
[3]136 "RPUPortSet",
[5]137 "RPUPrinterCreate",
[7]138 "RPUPrinterDelete",
[3]139 "RPUPrinterQuery",
140 "RPUVersion"
141};
142
143
144// FUNCTION DECLARATIONS
145
146// Exported REXX functions
147RexxFunctionHandler RPULoadFuncs;
148RexxFunctionHandler RPUDropFuncs;
149RexxFunctionHandler RPUVersion;
[5]150RexxFunctionHandler RPUEnumModels;
[3]151RexxFunctionHandler RPUEnumDrivers;
152RexxFunctionHandler RPUEnumPorts;
153RexxFunctionHandler RPUEnumPrinters;
[5]154RexxFunctionHandler RPUOpenView;
[10]155RexxFunctionHandler RPUPortDelete;
[3]156RexxFunctionHandler RPUPortDialog;
157RexxFunctionHandler RPUPortInstall;
[13]158RexxFunctionHandler RPUPortQuery;
[3]159RexxFunctionHandler RPUPortSet;
[5]160RexxFunctionHandler RPUPrinterCreate;
[7]161RexxFunctionHandler RPUPrinterDelete;
[3]162RexxFunctionHandler RPUPrinterQuery;
[5]163RexxFunctionHandler RPUQueueDefault;
164RexxFunctionHandler RPUQueueHold;
[3]165
166// TODO
167
168// Internal functions
169PSZ GetObjectID( PSZ pszHandle );
170HOBJECT PrinterObjectHandle( PSZ pszQueueName );
171BOOL SaveResultString( PRXSTRING prsResult, PCH pchBytes, ULONG ulBytes );
[8]172ULONG UniqueDeviceName( PSZ pszName );
[3]173BOOL WriteCompoundVariable( PSZ pszStem, PSZ pszTail, PSZ pszValue );
[13]174BOOL WriteSimpleVariable( PSZ pszName, PSZ pszValue );
[3]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/* ------------------------------------------------------------------------- *
[5]260 * RPUEnumModels *
[3]261 * *
[5]262 * Gets a list of the printer models supported by the specified print *
263 * driver. *
[3]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 * ------------------------------------------------------------------------- */
[5]272ULONG APIENTRY RPUEnumModels( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
[3]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
[13]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 */
[12]537 ULONG flType = SPL_PR_QUEUE | SPL_PR_DIRECT_DEVICE | SPL_PR_LOCAL_ONLY,
[3]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 */
[11]634 rc = SplQueryQueue( pPInfo->pszComputerName,
635 pPInfo->pszPrintDestinationName,
[3]636 6L, NULL, 0L, &cbNeeded );
637 if (( rc != NO_ERROR ) && ( rc != NERR_BufTooSmall )) {
638 WriteErrorCode( rc, "SplQueryQueue");
639 MAKERXSTRING( *prsResult, "0", 1 );
[11]640 //goto cleanup;
641 continue;
[3]642 }
643 pbQueue = malloc( cbNeeded );
644 if ( !pbQueue ) {
645 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
646 MAKERXSTRING( *prsResult, "0", 1 );
[11]647 //goto cleanup;
648 continue;
[3]649 }
650 cbBuf = cbNeeded;
[11]651 rc = SplQueryQueue( pPInfo->pszComputerName, pPInfo->pszPrintDestinationName,
[3]652 6L, pbQueue, cbBuf, &cbNeeded );
653 if ( rc != NO_ERROR ) {
654 WriteErrorCode( rc, "SplQueryQueue");
655 MAKERXSTRING( *prsResult, "0", 1 );
656 free( pbQueue );
[11]657 //goto cleanup;
658 continue;
[3]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/* ------------------------------------------------------------------------- *
[5]707 * RPUOpenView *
[3]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 * ------------------------------------------------------------------------- */
[5]725ULONG APIENTRY RPUOpenView( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
[3]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 );
[11]759 if ( !hObj ) {
760 MAKERXSTRING( *prsResult, "0", 1 );
761 return ( 0 );
762 }
[3]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 *
[13]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) *
[3]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/* ------------------------------------------------------------------------- *
[13]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/* ------------------------------------------------------------------------- *
[3]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 );
[13]1032 pszPdrName = strupr( argv[0].strptr );
[3]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 );
[13]1122 pszPdrName = strupr( argv[0].strptr );
[3]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
[5]1174/* ------------------------------------------------------------------------- *
[10]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/* ------------------------------------------------------------------------- *
[5]1213 * RPUPrinterCreate *
1214 * *
1215 * Creates a new local printer object. The associated print queue and *
[8]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. *
[5]1219 * *
[8]1220 * The WPS object is created with default settings, and will be assigned an *
1221 * object-ID automatically by the WPS. *
[5]1222 * *
[8]1223 * NOTE: This function will NOT create a remote (LAN) printer object. *
[5]1224 * *
1225 * REXX ARGUMENTS: *
[8]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) *
[5]1229 * 3. The name of the printer port, which must exist already. (REQUIRED) *
1230 * 4. The default printer driver.model to be associated with *
[8]1231 * the device. If specified, this must be installed in the *
1232 * system already. If not specified, IBMNULL is assumed. (OPTIONAL) *
[5]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,
[6]1244 pszTitle = NULL;
[8]1245 CHAR szDevice[ US_PRTDEV_MAXZ ] = {0};
[5]1246 SPLERR rc;
1247
1248
1249 // Reset the error indicator
1250 WriteErrorCode( 0, NULL );
1251
1252 // Validate the REXX arguments
[8]1253 if (( argc < 3 ) || ( argc > 4 ) || ( ! RXVALIDSTRING( argv[0] )) ||
[5]1254 ( ! RXVALIDSTRING( argv[1] )) || ( ! RXVALIDSTRING( argv[2] )))
1255 return ( 40 );
1256 if (( argc > 3 ) && ( ! RXVALIDSTRING( argv[3] )))
1257 return ( 40 );
1258
[8]1259 pszTitle = argv[0].strptr;
1260 pszQueueName = argv[1].strptr;
[5]1261 pszPortName = argv[2].strptr;
1262 if ( argc > 3 ) pszModel = argv[3].strptr;
1263
[8]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 }
[5]1274
[8]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");
[5]1285 MAKERXSTRING( *prsResult, "0", 1 );
1286 return ( 0 );
1287 }
1288
[8]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");
[5]1300 MAKERXSTRING( *prsResult, "0", 1 );
[8]1301 SplDeleteDevice( NULL, szDevice );
[5]1302 return ( 0 );
1303 }
1304
[7]1305 MAKERXSTRING( *prsResult, "1", 1 );
1306 return ( 0 );
1307}
[6]1308
[7]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 *
[10]1321 * device were destroyed successfully.) *
[7]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 );
[6]1348 }
[7]1349 pInfo = (PPRQINFO3) malloc( cbBuf );
1350 if ( !pInfo ) {
1351 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
[5]1352 MAKERXSTRING( *prsResult, "0", 1 );
1353 return ( 0 );
1354 }
[7]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
[13]1370 // - NB This causes a long delay when deleting an offline LAN printer
[7]1371 if ( hObj != NULLHANDLE ) WinDestroyObject( hObj );
1372
1373 MAKERXSTRING( *prsResult, "1", 1 );
1374cleanup:
1375 free( pInfo );
[5]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 *
[12]1388 * (stem).i.!jobflags Zero or more of the following flags (any order): *
[5]1389 * E A printer error has occurred *
[12]1390 * H Printer destination is paused (held) *
[5]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;
[12]1410 CHAR szFlags[ 8 ] = {0},
1411 szStem[ US_STEM_MAXZ ]; // compound variable stem name
[5]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 );
[12]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 );
[5]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
[3]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{
[11]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 ];
[3]1596 ULONG cbTotal = 0,
[11]1597 cbActual = 0,
1598 ulHandle;
1599 HOBJECT hObj = 0;
1600 BOOL fFound,
1601 fRC;
[3]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
[11]1623 fFound = FALSE;
[3]1624 psz = (PSZ) pbuf;
[11]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 */
[3]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 {
[11]1703 fFound = TRUE;
[3]1704 break;
1705 }
[11]1706#endif
[3]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
[8]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 );
[9]1828 if ( rc == NO_ERROR && !fUnique ) return 1;
[8]1829 }
1830 }
[9]1831 else if ( rc == NO_ERROR ) fUnique = TRUE;
1832 return rc;
[8]1833}
1834
1835
[3]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/* ------------------------------------------------------------------------- *
[13]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/* ------------------------------------------------------------------------- *
[3]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.