source: rxprtutl/trunk/rxprtutl.c@ 15

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

Added new RPUDeviceDelete function.
Replaced malloc() with DosAllocMem() in attempt to avoid occasional errors sometimes seen with repeated calls to RPUEnumModels.

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