source: rxprtutl/trunk/rxprtutl.c@ 12

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

RPUEnumPrinters: Do not attempt to read the remote printer queue of LAN printers.
RPUPrinterQuery: Return additional job flags.

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