source: rxprtutl/trunk/rxprtutl.c@ 37

Last change on this file since 37 was 37, checked in by Alex Taylor, 9 years ago

Added some more documentation.

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