source: rxprtutl/trunk/rxprtutl.c@ 32

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

Move several common functions into shared directory.

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