source: rxprtutl/trunk/rxprtutl.c@ 30

Last change on this file since 30 was 27, checked in by Alex Taylor, 10 years ago

Fixed memory leak in writing of compound variables; removed erroneous DosFreeMem from SaveResultString.

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