source: trunk/rxprtutl.c@ 1

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

RXPRTUTL: initial import

File size: 66.4 KB
Line 
1/******************************************************************************
2 * REXX OS/2 Printer Utility API (RXPRTUTL.DLL) *
3 * (C) 2011 Alex Taylor *
4 * *
5 * LICENSE: *
6 * *
7 * Redistribution and use in source and binary forms, with or without *
8 * modification, are permitted provided that the following conditions are *
9 * met: *
10 * *
11 * 1. Redistributions of source code must retain the above copyright *
12 * notice, this list of conditions and the following disclaimer. *
13 * *
14 * 2. Redistributions in binary form must reproduce the above copyright *
15 * notice, this list of conditions and the following disclaimer in the *
16 * documentation and/or other materials provided with the distribution. *
17 * *
18 * 3. The name of the author may not be used to endorse or promote products *
19 * derived from this software without specific prior written permission. *
20 * *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR *
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED *
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
24 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, *
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, *
29 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN *
30 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *
31 * POSSIBILITY OF SUCH DAMAGE. *
32 * *
33 ******************************************************************************/
34
35#define INCL_DEV
36#define INCL_DOSERRORS
37#define INCL_DOSMISC
38#define INCL_DOSMODULEMGR
39#define INCL_DOSPROFILE
40#define INCL_SPL
41#define INCL_SPLDOSPRINT
42#define INCL_SPLERRORS
43#define INCL_PM
44#define INCL_WIN
45#define INCL_WINWORKPLACE
46#ifndef OS2_INCLUDED
47 #include <os2.h>
48#endif
49#include <ctype.h>
50#include <stdio.h>
51#include <stdlib.h>
52#include <string.h>
53#define INCL_RXSHV
54#define INCL_RXFUNC
55#include <rexxsaa.h>
56
57
58// CONSTANTS
59
60#define SZ_LIBRARY_NAME "RXPRTUTL" // Name of this library
61#define SZ_ERROR_NAME "RPUERROR" // REXX variable used to store error codes
62#define SZ_VERSION "0.1.0" // Current version of this library
63
64#define APPNAME_LEAD_STR "PM_"
65#define APPNAME_PM_PORT_DRIVER "PM_PORT_DRIVER"
66#define APPNAME_PM_SPOOLER_PORT "PM_SPOOLER_PORT"
67#define APPNAME_PM_PRINT_OBJECT "PM_PrintObject"
68#define APPNAME_PM_WPS_LOCATION "PM_Workplace:Location"
69
70// Values that should be defined in pmsplb.h if it actually existed
71#define TYPE_LONG_WAIT 2
72#define BIDI_SET_PORTDRV 0x19
73
74// Values used by WinOpenObject
75#define OPEN_DEFAULT 0
76#define OPEN_CONTENTS 1
77#define OPEN_SETTINGS 2
78#define OPEN_HELP 3
79#define OPEN_TREE 101
80#define OPEN_DETAILS 102
81
82// Maximum string lengths...
83#define US_COMPOUND_MAXZ 250 // ...of a compound variable
84#define US_INTEGER_MAXZ 12 // ...of an integer string
85#define US_STEM_MAXZ ( US_COMPOUND_MAXZ - US_INTEGER_MAXZ ) // ...of a stem
86#define US_ERRSTR_MAXZ 250 // ...of an error string
87#define US_DRVINFO_MAXZ ( CCHMAXPATH + 8 + 32 ) // ...of an driver/port info string
88#define US_PRTINFO_MAXZ 180 // ...of a printer info string
89#define US_PORT_MAXZ 64 // ...of a port name
90
91// List of functions to be registered by RPULoadFuncs
92static PSZ RxFunctionTbl[] = {
93 "RPUDropFuncs",
94 "RPUEnumDevices",
95 "RPUEnumDrivers",
96 "RPUEnumPorts",
97 "RPUEnumPrinters",
98 "RPUHoldQueue",
99 "RPUPortDialog",
100 "RPUPortInstall",
101 "RPUPortSet",
102 "RPUPrinterDefault",
103 "RPUPrinterOpen",
104 "RPUPrinterQuery",
105 "RPUVersion"
106};
107
108
109// FUNCTION DECLARATIONS
110
111// Exported REXX functions
112RexxFunctionHandler RPULoadFuncs;
113RexxFunctionHandler RPUDropFuncs;
114RexxFunctionHandler RPUVersion;
115RexxFunctionHandler RPUEnumDevices;
116RexxFunctionHandler RPUEnumDrivers;
117RexxFunctionHandler RPUEnumPorts;
118RexxFunctionHandler RPUEnumPrinters;
119RexxFunctionHandler RPUHoldQueue;
120RexxFunctionHandler RPUPrinterOpen;
121RexxFunctionHandler RPUPortDialog;
122RexxFunctionHandler RPUPortInstall;
123RexxFunctionHandler RPUPortSet;
124RexxFunctionHandler RPUPrinterQuery;
125RexxFunctionHandler RPUPrinterDefault;
126
127// TODO
128RexxFunctionHandler RPUPortDelete;
129// - function to delete a printer (object, device and queue)
130
131// Internal functions
132PSZ GetObjectID( PSZ pszHandle );
133HOBJECT PrinterObjectHandle( PSZ pszQueueName );
134BOOL SaveResultString( PRXSTRING prsResult, PCH pchBytes, ULONG ulBytes );
135BOOL WriteCompoundVariable( PSZ pszStem, PSZ pszTail, PSZ pszValue );
136BOOL WriteStemElement( PSZ pszStem, ULONG ulIndex, PSZ pszValue );
137void WriteErrorCode( ULONG ulError, PSZ pszContext );
138
139
140
141/* ************************************************************************* *
142 * EXPORTED REXX FUNCTIONS *
143 * ************************************************************************* */
144
145/* ------------------------------------------------------------------------- *
146 * RPULoadFuncs *
147 * *
148 * Register all RPU* REXX functions except this one. *
149 * *
150 * REXX ARGUMENTS: None *
151 * REXX RETURN VALUE: "" *
152 * ------------------------------------------------------------------------- */
153ULONG APIENTRY RPULoadFuncs( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
154{
155 int entries,
156 i;
157
158 // Reset the error indicator
159 WriteErrorCode( 0, NULL );
160
161 if ( argc > 0 ) return ( 40 );
162 entries = sizeof(RxFunctionTbl) / sizeof(PSZ);
163 for ( i = 0; i < entries; i++ )
164 RexxRegisterFunctionDll( RxFunctionTbl[i], SZ_LIBRARY_NAME, RxFunctionTbl[i] );
165
166 MAKERXSTRING( *prsResult, "", 0 );
167 return ( 0 );
168}
169
170
171/* ------------------------------------------------------------------------- *
172 * RPUDropFuncs *
173 * *
174 * Deregister all RPU* REXX functions, including this one. *
175 * *
176 * REXX ARGUMENTS: None *
177 * REXX RETURN VALUE: "" *
178 * ------------------------------------------------------------------------- */
179ULONG APIENTRY RPUDropFuncs( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
180{
181 int entries,
182 i;
183
184 // Reset the error indicator
185 WriteErrorCode( 0, NULL );
186
187 if ( argc > 0 ) return ( 40 );
188 entries = sizeof(RxFunctionTbl) / sizeof(PSZ);
189 for ( i = 0; i < entries; i++ )
190 RexxDeregisterFunction( RxFunctionTbl[i] );
191
192 MAKERXSTRING( *prsResult, "", 0 );
193 return ( 0 );
194}
195
196
197/* ------------------------------------------------------------------------- *
198 * RPUVersion *
199 * *
200 * Returns the current library version. *
201 * *
202 * REXX ARGUMENTS: None *
203 * REXX RETURN VALUE: Current version in the form "major.minor.refresh" *
204 * ------------------------------------------------------------------------- */
205ULONG APIENTRY RPUVersion( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
206{
207 CHAR szVersion[ 12 ];
208
209 // Reset the error indicator
210 WriteErrorCode( 0, NULL );
211
212 if ( argc > 0 ) return ( 40 );
213 sprintf( szVersion, "%s", SZ_VERSION );
214
215 MAKERXSTRING( *prsResult, szVersion, strlen(szVersion) );
216 return ( 0 );
217}
218
219
220/* ------------------------------------------------------------------------- *
221 * RPUEnumDevices *
222 * *
223 * Gets a list of the printer devices (models) supported by the specified *
224 * print driver. *
225 * *
226 * REXX ARGUMENTS: *
227 * 1. Filespec of the printer driver to query. (REQUIRED) *
228 * 2. The name of the stem in which to return the results. (REQUIRED) *
229 * *
230 * REXX RETURN VALUE: *
231 * 1 on success, or 0 if an error occurred. *
232 * ------------------------------------------------------------------------- */
233ULONG APIENTRY RPUEnumDevices( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
234{
235 HAB hab; // desktop anchor-block handle
236 PSZ pszDriver; // print driver filespec
237 CHAR szStem[ US_STEM_MAXZ ], // buffers used for building stem
238 szNumber[ US_INTEGER_MAXZ ]; // ...
239 BOOL fSuccess; // success indicator
240 PSTR32 aDeviceName; // array of device names
241 PSTR64 aDeviceDesc; // array of device descriptions
242 PSTR16 aDataType; // array of data types
243 LONG pldn = 0L, // number of device names/descriptions
244 pldt = 0L, // number of data types
245 i;
246
247
248 // Reset the error indicator
249 WriteErrorCode( 0, NULL );
250
251 // Make sure we have exactly two valid arguments
252 if (( argc != 2 ) ||
253 ( ! RXVALIDSTRING( argv[0] )) ||
254 ( ! RXVALIDSTRING( argv[1] )) ||
255 ( RXSTRLEN( argv[1] ) > US_STEM_MAXZ ))
256 return ( 40 );
257 pszDriver = argv[0].strptr;
258
259 if ( argv[1].strptr[ argv[1].strlength-1 ] == '.') argv[1].strlength--;
260 strncpy( szStem, argv[1].strptr, RXSTRLEN( argv[1] ));
261 szStem[ RXSTRLEN( argv[1] ) ] = '\0';
262
263 hab = WinInitialize( 0 );
264 if ( !hab ) {
265 WriteErrorCode( 0, "WinInitialize");
266 MAKERXSTRING( *prsResult, "0", 1 );
267 return ( 0 );
268 }
269
270 // Query the size of the available data
271 fSuccess = DevQueryDeviceNames( hab, pszDriver, &pldn, NULL, NULL, &pldt, NULL );
272 if ( !fSuccess ) {
273 WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "DevQueryDeviceNames");
274 MAKERXSTRING( *prsResult, "0", 1 );
275 goto cleanup;
276 }
277
278 // Now get the actual data
279 aDeviceName = malloc( pldn * sizeof( STR32 ));
280 aDeviceDesc = malloc( pldn * sizeof( STR64 ));
281 aDataType = malloc( pldt * sizeof( STR16 ));
282 if ( !aDeviceName || !aDeviceDesc || ! aDataType ) {
283 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
284 MAKERXSTRING( *prsResult, "0", 1 );
285 goto cleanup;
286 }
287 fSuccess = DevQueryDeviceNames( hab, pszDriver, &pldn, aDeviceName,
288 aDeviceDesc, &pldt, aDataType );
289 for ( i = 0; i < pldn; i++ ) {
290 WriteStemElement( szStem, i+1, (PSZ)aDeviceName[i] );
291 }
292 sprintf( szNumber, "%u", pldn );
293 WriteStemElement( szStem, 0, szNumber );
294
295 // Return 1 on success
296 MAKERXSTRING( *prsResult, "1", 1 );
297
298 free( aDeviceName );
299 free( aDeviceDesc );
300 free( aDataType );
301
302cleanup:
303 WinTerminate( hab );
304 return ( 0 );
305}
306
307
308/* ------------------------------------------------------------------------- *
309 * RPUEnumPorts *
310 * *
311 * Gets a list of the printer ports currently defined on the system. Returns *
312 * a REXX array (stem) consisting of one string per port, of the form: *
313 * <port> <driver> <driver path> *
314 * *
315 * REXX ARGUMENTS: *
316 * 1. The name of the stem in which to return the results. (REQUIRED) *
317 * *
318 * REXX RETURN VALUE: *
319 * 1 on success, or 0 if an error occurred. *
320 * ------------------------------------------------------------------------- */
321ULONG APIENTRY RPUEnumPorts( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
322{
323 PPRPORTINFO1 pInfo;
324 PVOID pbuf;
325 ULONG cbBuf,
326 cTotal,
327 cReturned,
328 cbNeeded,
329 i;
330 CHAR szStem[ US_STEM_MAXZ ], // buffers used for building stem
331 szNumber[ US_INTEGER_MAXZ ], // ...
332 szInfo[ US_DRVINFO_MAXZ ];
333 SPLERR rc;
334
335 // Reset the error indicator
336 WriteErrorCode( 0, NULL );
337
338 // Make sure we have exactly one valid argument (the stem name)
339 if (( argc != 1 ) || ( ! RXVALIDSTRING( argv[0] )))
340 return ( 40 );
341 if ( argv[0].strptr[ argv[0].strlength-1 ] == '.') argv[0].strlength--;
342 strncpy( szStem, argv[0].strptr, RXSTRLEN( argv[0] ));
343 szStem[ RXSTRLEN( argv[0] ) ] = '\0';
344
345 // Query the amount of available data
346 rc = SplEnumPort( NULL, 1, NULL, 0L, &cReturned, &cTotal, &cbNeeded, NULL );
347 if (( rc != ERROR_MORE_DATA ) && ( rc != NERR_BufTooSmall )) {
348 WriteErrorCode( rc, "SplEnumPort");
349 MAKERXSTRING( *prsResult, "0", 1 );
350 return ( 0 );
351 }
352
353 // Now get the actual data
354 pbuf = malloc( cbNeeded );
355 if ( !pbuf ) {
356 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
357 MAKERXSTRING( *prsResult, "0", 1 );
358 return ( 0 );
359 }
360 cbBuf = cbNeeded;
361 rc = SplEnumPort( NULL, 1, pbuf, cbBuf,
362 &cReturned, &cTotal, &cbNeeded, NULL );
363 if ( rc == NO_ERROR ) {
364 pInfo = (PPRPORTINFO1) pbuf;
365 for ( i = 0; i < cReturned; i++ ) {
366 sprintf( szInfo, "%.32s %.8s %s",
367 pInfo->pszPortName,
368 pInfo->pszPortDriverName? pInfo->pszPortDriverName: "",
369 pInfo->pszPortDriverPathName? pInfo->pszPortDriverPathName: "");
370 WriteStemElement( szStem, i+1, szInfo );
371 pInfo++;
372 }
373 sprintf( szNumber, "%u", cReturned );
374 WriteStemElement( szStem, 0, szNumber );
375 MAKERXSTRING( *prsResult, "1", 1 );
376 }
377 else {
378 WriteErrorCode( rc, "SplEnumPort");
379 MAKERXSTRING( *prsResult, "0", 1 );
380 }
381 free( pbuf );
382
383 return ( 0 );
384}
385
386
387/* ------------------------------------------------------------------------- *
388 * RPUEnumDrivers *
389 * *
390 * Gets a list of the printer drivers currently installed on the system. *
391 * Returns a REXX array (stem) with each item being a driver name string. *
392 * *
393 * REXX ARGUMENTS: *
394 * 1. The name of the stem in which to return the results. (REQUIRED) *
395 * *
396 * REXX RETURN VALUE: *
397 * 1 on success, or 0 if an error occurred. *
398 * ------------------------------------------------------------------------- */
399ULONG APIENTRY RPUEnumDrivers( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
400{
401 PSZ pszDriverName;
402 PBYTE pbuf;
403 ULONG cbBuf,
404 cTotal,
405 cReturned,
406 cbNeeded,
407 i;
408 CHAR szStem[ US_STEM_MAXZ ], // buffers used for building stem
409 szNumber[ US_INTEGER_MAXZ ]; // ...
410 SPLERR rc;
411
412
413 // Reset the error indicator
414 WriteErrorCode( 0, NULL );
415
416 // Make sure we have exactly one valid argument (the stem name)
417 if (( argc != 1 ) || ( ! RXVALIDSTRING( argv[0] )))
418 return ( 40 );
419 if ( argv[0].strptr[ argv[0].strlength-1 ] == '.') argv[0].strlength--;
420 strncpy( szStem, argv[0].strptr, RXSTRLEN( argv[0] ));
421 szStem[ RXSTRLEN( argv[0] ) ] = '\0';
422
423 // Query the amount of available data
424 rc = SplEnumDriver( NULL, 0L, NULL, 0L, &cReturned, &cTotal, &cbNeeded, NULL );
425 if (( rc != ERROR_MORE_DATA ) && ( rc != NERR_BufTooSmall )) {
426 WriteErrorCode( rc, "SplEnumDriver");
427 MAKERXSTRING( *prsResult, "0", 1 );
428 return ( 0 );
429 }
430
431 // Now get the actual data
432 pbuf = (PBYTE) malloc( cbNeeded );
433 if ( !pbuf ) {
434 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
435 MAKERXSTRING( *prsResult, "0", 1 );
436 return ( 0 );
437 }
438 cbBuf = cbNeeded;
439 rc = SplEnumDriver( NULL, 0L, pbuf, cbBuf, &cReturned ,&cTotal, &cbNeeded, NULL ) ;
440 if ( rc == NO_ERROR ) {
441 pszDriverName = (PSZ) pbuf;
442 for ( i = 0; i < cReturned ; i++ ) {
443 WriteStemElement( szStem, i+1, pszDriverName );
444 pszDriverName += DRIV_NAME_SIZE + DRIV_DEVICENAME_SIZE + 2;
445 }
446 sprintf( szNumber, "%u", cReturned );
447 WriteStemElement( szStem, 0, szNumber );
448 MAKERXSTRING( *prsResult, "1", 1 );
449 }
450 else {
451 WriteErrorCode( rc, "SplEnumDriver");
452 MAKERXSTRING( *prsResult, "0", 1 );
453 }
454 free( pbuf );
455
456 return ( 0 );
457}
458
459
460/* ------------------------------------------------------------------------- *
461 * RPUEnumPrinters *
462 * *
463 * Gets a list of the printers currently defined on the system. Returns a *
464 * compound (stem) variable with the following format: *
465 * *
466 * (stem).0 Number of printers *
467 * (stem).i.!name Printer device (a.k.a. physical) name *
468 * (stem).i.!queue Printer queue name ('' for direct printers) *
469 * (stem).i.!host Name of LAN host where printer queue is located *
470 * ('' for non-LAN printers) *
471 * (stem).i.!description Printer description (name of WPS object) *
472 * (stem).i.!handle Printer's object handle ('' for direct printers) *
473 * (stem).i.!flags Zero or more of the following flags (any order): *
474 * D This is the default printer queue *
475 * P Printer queue is paused (held) *
476 * *
477 * REXX ARGUMENTS: *
478 * 1. The name of the stem in which to return the results. (REQUIRED) *
479 * *
480 * REXX RETURN VALUE: *
481 * 1 on success, or 0 if an error occurred. *
482 * ------------------------------------------------------------------------- */
483ULONG APIENTRY RPUEnumPrinters( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
484{
485 PPRINTERINFO pPInfo = NULL; // pointer to enumerated printer information
486 PPRQINFO6 pQInfo = NULL; // queue information (level 6)
487 PVOID pbEnum = NULL, // buffer for SplEnumPrinter
488 pbQueue = NULL; // buffer for SplQueryQueue
489 PSZ pszToken;
490 HOBJECT hObj; // printer WPS object handle
491 ULONG flType = SPL_PR_QUEUE | SPL_PR_DIRECT_DEVICE,
492 cbBuf = 0,
493 cTotal = 0,
494 cReturned = 0,
495 cbNeeded = 0,
496 ulCount = 0, // number of printers found
497 i;
498 CHAR szFlags[ 3 ], // string of flag characters
499 szHandle[ 9 ], // string for hex object handle
500 // buffers used for building compound variables:
501 szStem[ US_STEM_MAXZ - US_INTEGER_MAXZ - 1 ],
502 szNumber[ US_INTEGER_MAXZ ],
503 szStemNode[ US_STEM_MAXZ ];
504 SPLERR rc;
505
506
507 // Reset the error indicator
508 WriteErrorCode( 0, NULL );
509
510 // Make sure we have exactly one valid argument (the stem name)
511 if (( argc != 1 ) || ( ! RXVALIDSTRING( argv[0] )))
512 return ( 40 );
513 if ( argv[0].strptr[ argv[0].strlength-1 ] == '.') argv[0].strlength--;
514 strncpy( szStem, argv[0].strptr, RXSTRLEN( argv[0] ));
515 szStem[ RXSTRLEN( argv[0] ) ] = '\0';
516
517
518 /* Our enumeration query asks for two types of devices: direct (non-queued)
519 * printers, and printer queues.
520 *
521 * In the latter case, it's important to note that we ask for the queues
522 * themselves rather than queued printers. Why? Because we'll need to
523 * gather information about both printers and their underlying queues (some
524 * things associated with printer objects actually belong to the queue
525 * rather than the printer device).
526 *
527 * The thing is, given a printer name, there doesn't seem to be an easy way
528 * to find out the queue associated with it. So we need to do it the other
529 * way around: get the queue names, then we can identify the printers
530 * connected to them.
531 */
532
533 // Query the amount of available data
534 rc = SplEnumPrinter( NULL, 0L, flType, NULL, 0L,
535 &cReturned, &cTotal, &cbNeeded, NULL );
536 if (( rc != ERROR_MORE_DATA ) && ( rc != NERR_BufTooSmall ))
537 // && ( rc != ERROR_INVALID_PARAMETER ))
538 {
539 WriteErrorCode( rc, "SplEnumPrinter");
540 MAKERXSTRING( *prsResult, "0", 1 );
541 return ( 0 );
542 }
543
544 // Now get the actual data
545 pbEnum = malloc( cbNeeded );
546 if ( !pbEnum ) {
547 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
548 MAKERXSTRING( *prsResult, "0", 1 );
549 return ( 0 );
550 }
551 cbBuf = cbNeeded;
552 rc = SplEnumPrinter( NULL, 0L, flType, pbEnum, cbBuf,
553 &cReturned, &cTotal, &cbNeeded, NULL );
554 if ( rc != NO_ERROR ) {
555 WriteErrorCode( rc, "SplEnumPrinter");
556 MAKERXSTRING( *prsResult, "0", 1 );
557 goto cleanup;
558 }
559
560 pPInfo = (PPRINTERINFO) pbEnum;
561 for ( i = 0; i < cReturned; i++ ) {
562
563 if ( pPInfo->flType == SPL_PR_DIRECT_DEVICE ) {
564
565 /* Direct print device. No queue, no handle; flags do not apply.
566 * The destination name and description are those of the printer
567 * device itself, so we don't need any additional queries.
568 */
569 sprintf( szStemNode, "%s.%u", szStem, ++ulCount );
570
571 WriteCompoundVariable( szStemNode, "!name",
572 pPInfo->pszPrintDestinationName );
573 WriteCompoundVariable( szStemNode, "!description",
574 pPInfo->pszDescription );
575 WriteCompoundVariable( szStemNode, "!queue", "");
576 WriteCompoundVariable( szStemNode, "!flags", "");
577 WriteCompoundVariable( szStemNode, "!handle", "");
578
579 WriteCompoundVariable( szStemNode, "!host",
580 pPInfo->pszComputerName?
581 pPInfo->pszComputerName: "");
582 }
583 else if ( pPInfo->flType == SPL_PR_QUEUE ) {
584
585 /* Print queue. We need to query the queue to find out the details
586 * of the print device(s) connected to it.
587 */
588 rc = SplQueryQueue( NULL, pPInfo->pszPrintDestinationName,
589 6L, NULL, 0L, &cbNeeded );
590 if (( rc != NO_ERROR ) && ( rc != NERR_BufTooSmall )) {
591 WriteErrorCode( rc, "SplQueryQueue");
592 MAKERXSTRING( *prsResult, "0", 1 );
593 goto cleanup;
594 }
595 pbQueue = malloc( cbNeeded );
596 if ( !pbQueue ) {
597 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
598 MAKERXSTRING( *prsResult, "0", 1 );
599 goto cleanup;
600 }
601 cbBuf = cbNeeded;
602 rc = SplQueryQueue( NULL, pPInfo->pszPrintDestinationName,
603 6L, pbQueue, cbBuf, &cbNeeded );
604 if ( rc != NO_ERROR ) {
605 WriteErrorCode( rc, "SplQueryQueue");
606 MAKERXSTRING( *prsResult, "0", 1 );
607 free( pbQueue );
608 goto cleanup;
609 }
610 pQInfo = (PPRQINFO6) pbQueue;
611
612 // Now go through the list of printers connected to this queue...
613 pszToken = strtok( pQInfo->pszPrinters, ",");
614 while ( pszToken ) {
615 sprintf( szStemNode, "%s.%u", szStem, ++ulCount );
616 WriteCompoundVariable( szStemNode, "!name", pszToken );
617 WriteCompoundVariable( szStemNode, "!description", pQInfo->pszComment );
618 WriteCompoundVariable( szStemNode, "!queue", pQInfo->pszName );
619 WriteCompoundVariable( szStemNode, "!driver", pQInfo->pszDriverName );
620 WriteCompoundVariable( szStemNode, "!host",
621 pQInfo->pszRemoteComputerName ?
622 pQInfo->pszRemoteComputerName: "");
623
624 hObj = PrinterObjectHandle( pQInfo->pszName );
625 if ( hObj )
626 sprintf( szHandle, "%X", hObj );
627 else
628 sprintf( szHandle, "");
629 WriteCompoundVariable( szStemNode, "!handle", szHandle );
630
631 memset( szFlags, 0, sizeof( szFlags ));
632 if ( pQInfo->fsType & PRQ3_TYPE_APPDEFAULT )
633 strcat( szFlags, "D");
634 if ( pQInfo->fsStatus & PRQ3_PAUSED )
635 strcat( szFlags, "P");
636 WriteCompoundVariable( szStemNode, "!flags", szFlags );
637
638 pszToken = strtok( NULL, ",");
639 } // while
640
641 free( pbQueue );
642 }
643
644 pPInfo++;
645 }
646 sprintf( szNumber, "%u", ulCount );
647 WriteStemElement( szStem, 0, szNumber );
648 MAKERXSTRING( *prsResult, "1", 1 );
649
650cleanup:
651 free( pbEnum );
652 return ( 0 );
653}
654
655
656/* ------------------------------------------------------------------------- *
657 * RPUPrinterQuery *
658 * *
659 * Gets information about the specified printer device. *
660 * *
661 * (stem).i.!description Printer description (name of WPS object) *
662 * (stem).i.!port Name of the port the printer is using *
663 * (stem).i.!driver List of the drivers used by this printer *
664 * TODO: *
665 * (stem).i.!flags Zero or more of the following flags (any order): *
666 * E A printer error has occurred *
667 * H Printer destination is paused *
668 * I Intervention required *
669 * N Printer has raised a notification alert *
670 * O Printer is offline *
671 * P Printer is out of paper *
672 * X Printer is not processing/paused *
673 * *
674 * REXX ARGUMENTS: *
675 * 1. The name of the printer device being queried. (REQUIRED) *
676 * 2. The name of the stem in which to return the results. (REQUIRED) *
677 * *
678 * REXX RETURN VALUE: *
679 * 1 on success, or 0 if an error occurred. *
680 * ------------------------------------------------------------------------- */
681ULONG APIENTRY RPUPrinterQuery( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
682{
683 PPRDINFO3 pInfo = NULL;
684 PVOID pbuf = NULL;
685 ULONG cbBuf = 0,
686 cbNeeded = 0;
687 PSZ pszDeviceName;
688 CHAR szStem[ US_STEM_MAXZ ]; // compound variable stem name
689 SPLERR rc;
690
691
692 // Reset the error indicator
693 WriteErrorCode( 0, NULL );
694
695 // Validate the REXX arguments
696 if (( argc != 2 ) ||
697 ( ! RXVALIDSTRING( argv[0] )) || ( ! RXVALIDSTRING( argv[1] )))
698 return ( 40 );
699
700 pszDeviceName = argv[0].strptr;
701//printf("Device: \"%s\"\n", pszDeviceName );
702
703 // Initialize the result stem name
704 if ( RXSTRLEN(argv[1]) > US_STEM_MAXZ ) return ( 40 );
705 if ( argv[1].strptr[ argv[1].strlength-1 ] == '.') argv[1].strlength--;
706 strncpy( szStem, argv[1].strptr, RXSTRLEN( argv[1] ));
707 szStem[ RXSTRLEN( argv[1] ) ] = '\0';
708
709
710 // Query the amount of available data
711 rc = SplQueryDevice( NULL, pszDeviceName, 3L, NULL, 0L, &cbNeeded );
712 if (( rc != ERROR_MORE_DATA ) && ( rc != NERR_BufTooSmall ))
713 {
714 WriteErrorCode( rc, "SplQueryDevice");
715 MAKERXSTRING( *prsResult, "0", 1 );
716 return ( 0 );
717 }
718
719 // Now get the actual data
720 pbuf = malloc( cbNeeded );
721 if ( !pbuf ) {
722 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
723 MAKERXSTRING( *prsResult, "0", 1 );
724 return ( 0 );
725 }
726 cbBuf = cbNeeded;
727 rc = SplQueryDevice( NULL, pszDeviceName, 3L, pbuf, cbBuf, &cbNeeded );
728 if ( rc == NO_ERROR ) {
729 pInfo = (PPRDINFO3) pbuf;
730 WriteCompoundVariable( szStem, "!name", pInfo->pszPrinterName );
731 WriteCompoundVariable( szStem, "!port", pInfo->pszLogAddr );
732 WriteCompoundVariable( szStem, "!description", pInfo->pszComment );
733 WriteCompoundVariable( szStem, "!drivers", pInfo->pszDrivers );
734 MAKERXSTRING( *prsResult, "1", 1 );
735 }
736 else {
737 WriteErrorCode( rc, "SplQueryDevice");
738 MAKERXSTRING( *prsResult, "0", 1 );
739 }
740
741 free( pbuf );
742 return ( 0 );
743}
744
745
746/* ------------------------------------------------------------------------- *
747 * RPUPrinterOpen *
748 * *
749 * Opens the requested view of the WPS object corresponding to the specified *
750 * print queue. (Note that WPS printer objects are associated with a queue *
751 * rather than a printer device, so it is the queue name that must be passed *
752 * to this function.) *
753 * *
754 * REXX ARGUMENTS: *
755 * 1. The name of the printer queue for the object to open. (REQUIRED) *
756 * 2. Optional view flag, one of: *
757 * O Object's default view (DEFAULT) *
758 * D Details view *
759 * I Icon view *
760 * S Settings *
761 * *
762 * REXX RETURN VALUE: *
763 * 1 on success, or 0 if an error occurred. *
764 * ------------------------------------------------------------------------- */
765ULONG APIENTRY RPUPrinterOpen( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
766{
767 HOBJECT hObj;
768 ULONG ulView = OPEN_DEFAULT;
769 BOOL fRC;
770
771
772 // Reset the error indicator
773 WriteErrorCode( 0, NULL );
774
775 // Make sure we have at least one valid argument (the queue name)
776 if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
777 pszName = argv[0].strptr;
778
779 // Second argument: view (optional, but must be correct if specified)
780 if ( argc == 2 ) {
781 if ( RXVALIDSTRING(argv[1]) ) {
782 switch ( argv[1].strptr[0] ) {
783 case 'o':
784 case 'O': ulView = OPEN_DEFAULT; break;
785 case 'd':
786 case 'D': ulView = OPEN_DETAILS; break;
787 case 'i':
788 case 'I': ulView = OPEN_CONTENTS; break;
789 case 's':
790 case 'S': ulView = OPEN_SETTINGS; break;
791 default : return ( 40 );
792 }
793 }
794 else return ( 40 );
795 }
796 if ( argc > 2 ) return ( 40 );
797
798 hObj = PrinterObjectHandle( pszName );
799 if ( !hObj ) return ( 40 );
800
801 fRC = WinOpenObject( hObj, ulView, TRUE );
802
803 MAKERXSTRING( *prsResult, fRC? "1": "0", 1 );
804 return ( 0 );
805}
806
807
808/* ------------------------------------------------------------------------- *
809 * RPUPrinterDefault *
810 * *
811 * Sets the requested printer queue as the system default printer. *
812 * *
813 * REXX ARGUMENTS: *
814 * 1. The name of the printer queue to set as default. (REQUIRED) *
815 * *
816 * REXX RETURN VALUE: *
817 * 1 on success, or 0 if an error occurred. *
818 * ------------------------------------------------------------------------- */
819ULONG APIENTRY RPUPrinterDefault( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
820{
821 HOBJECT hObj;
822 BOOL fRC;
823
824 // Reset the error indicator
825 WriteErrorCode( 0, NULL );
826
827 // Make sure we have at least one valid argument (the queue name)
828 if ( argc != 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
829 pszName = argv[0].strptr;
830
831 hObj = PrinterObjectHandle( pszName );
832 if ( !hObj ) return ( 40 );
833
834 fRC = WinSetObjectData( hObj, "APPDEFAULT=YES;");
835
836 MAKERXSTRING( *prsResult, fRC? "1": "0", 1 );
837 return ( 0 );
838}
839
840
841
842/* ------------------------------------------------------------------------- *
843 * RPUHoldQueue *
844 * *
845 * Holds or releases the specified print queue. *
846 * *
847 * REXX ARGUMENTS: *
848 * 1. The name of the printer queue to hold or release. (REQUIRED) *
849 * 2. Action flag, one of: *
850 * Y Hold printer (DEFAULT) *
851 * N Release printer *
852 * *
853 * REXX RETURN VALUE: *
854 * 1 on success, or 0 if an error occurred. *
855 * ------------------------------------------------------------------------- */
856ULONG APIENTRY RPUHoldQueue( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
857{
858 PSZ pszQueueName; // Print queue name
859 BOOL fHold = TRUE;
860 SPLERR rc;
861
862
863 // Reset the error indicator
864 WriteErrorCode( 0, NULL );
865
866 // Make sure we have at least one valid argument (the queue name)
867 if ( argc < 1 || argc > 2 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
868 pszQueueName = argv[0].strptr;
869
870 // Second argument: view (optional, but must be correct if specified)
871 if ( argc == 2 ) {
872 if ( RXVALIDSTRING(argv[1]) ) {
873 switch ( argv[1].strptr[0] ) {
874 case 'y':
875 case 'Y': fHold = TRUE; break;
876 case 'n':
877 case 'N': fHold = FALSE; break;
878 default : return ( 40 );
879 }
880 }
881 else return ( 40 );
882 }
883
884 rc = fHold ? SplHoldQueue( NULL, pszQueueName ) :
885 SplReleaseQueue( NULL, pszQueueName );
886 if ( rc != NO_ERROR ) {
887 WriteErrorCode( rc, fHold ? "SplHoldQueue" : "SplReleaseQueue");
888 MAKERXSTRING( *prsResult, "0", 1 );
889 }
890 else MAKERXSTRING( *prsResult, "1", 1 );
891
892 return ( 0 );
893}
894
895
896/* ------------------------------------------------------------------------- *
897 * RPUPortInstall *
898 * *
899 * Creates a new printer port. *
900 * *
901 * REXX ARGUMENTS: *
902 * 1. The name of the port driver without any extension. This must be *
903 * installed and registered in OS2.INI already. (REQUIRED) *
904 * 2. The name of the new port to be created. If not specified, the port *
905 * driver will be responsible for using a default name. (DEFAULT: none) *
906 * *
907 * REXX RETURN VALUE: *
908 * 1 on success, or 0 if an error occurred. *
909 * ------------------------------------------------------------------------- */
910ULONG APIENTRY RPUPortInstall( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
911{
912 HAB hab; // desktop anchor-block handle
913 HMODULE hPdr = NULLHANDLE; // handle to port driver module
914 PFN pfnInstallPort; // pointer to driver's SplPdInstallPort entrypoint
915 ULONG ulCB; // return value from PrfQueryProfileString()
916 PSZ pszPdrName = NULL, // name of the specified port driver
917 pszPortName = NULL; // name of the new port to create
918 CHAR szPathName[ CCHMAXPATH+1 ]; // FQN of the port driver module
919 APIRET rc = 0; // return code from Dos...() functions
920
921
922 // Reset the error indicator
923 WriteErrorCode( 0, NULL );
924
925 // Make sure we have at least one valid argument (the port driver name)
926 if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
927 pszPdrName = argv[0].strptr;
928
929 // Second argument: new port name (optional, but must be correct if specified)
930 if ( argc >= 2 ) {
931 if ( RXVALIDSTRING(argv[1]) )
932 pszPortName = strupr( argv[1].strptr );
933 else return ( 40 );
934 }
935
936 // Get the path to the installed port driver
937 hab = WinInitialize( 0 );
938 if ( !hab ) {
939 WriteErrorCode( 0, "WinInitialize");
940 MAKERXSTRING( *prsResult, "0", 1 );
941 return ( 0 );
942 }
943
944 ulCB = PrfQueryProfileString( HINI_SYSTEMPROFILE, APPNAME_PM_PORT_DRIVER,
945 pszPdrName, NULL, (PVOID) szPathName, CCHMAXPATH );
946 if ( !ulCB ) {
947 WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString");
948 MAKERXSTRING( *prsResult, "0", 1 );
949 goto cleanup;
950 }
951
952 // Load the port driver DLL and register its port install routine
953 rc = DosLoadModule( NULL, 0, szPathName, &hPdr );
954 if ( rc != NO_ERROR || !hPdr ) {
955 WriteErrorCode( rc, "DosLoadModule");
956 MAKERXSTRING( *prsResult, "0", 1 );
957 goto cleanup;
958 }
959 rc = DosQueryProcAddr( hPdr, 0, "SPLPDINSTALLPORT", &pfnInstallPort );
960 if ( rc != NO_ERROR ) {
961 WriteErrorCode( rc, "DosQueryProcAddr");
962 MAKERXSTRING( *prsResult, "0", 1 );
963 goto finish;
964 }
965
966 // Now create the new port (the driver will assign it default settings)
967 rc = pfnInstallPort( hab, pszPortName );
968 if ( rc == NO_ERROR ) {
969 // Return 1 on success
970 MAKERXSTRING( *prsResult, "1", 1 );
971 }
972 else {
973 WriteErrorCode( rc, "SplPdInstallPort");
974 MAKERXSTRING( *prsResult, "0", 1 );
975 }
976
977finish:
978 DosFreeModule( hPdr );
979cleanup:
980 WinTerminate( hab );
981 return ( 0 );
982}
983
984
985/* ------------------------------------------------------------------------- *
986 * RPUPortSet *
987 * *
988 * Sets the specified port's configuration settings. IMPORTANT: not all *
989 * port drivers support this; the standard OS/2 serial and parallel port *
990 * drivers do NOT. Depending on the port driver, there are two possible *
991 * outcomes when this API is not supported: *
992 * A. Port driver does not have an entrypoint for SplPdSet. This is the *
993 * case for any port driver using pre-Warp conventions. RPUPortSet will *
994 * return 0 and RPUERROR will be "1: DosQueryProcAddr". *
995 * B. Port driver supports SplPdSet but does not (correctly) implement the *
996 * BIDI_SET_PORTDRV command. This is the case for some third-party port *
997 * drivers. In this case, whether or not RPUPortSet returns a success *
998 * code entirely depends on how conscientiously the port driver itself *
999 * is written, but the "INITIALIZATION" key (and possibly others) in *
1000 * the port's entry in OS2SYS.INI will not be updated. The application *
1001 * should always check for this after calling RPUPortSet! *
1002 * If the application determines that either of these failure conditions has *
1003 * occurred, it is advisable to call RPUPortDialog as a fallback measure. *
1004 * *
1005 * Because of the above, use of this function is not generally encouraged *
1006 * except when absolutely necessary. *
1007 * *
1008 * REXX ARGUMENTS: *
1009 * 1. The name of the port driver without any extension. This must be *
1010 * installed and registered in OS2.INI already. (REQUIRED) *
1011 * 2. The name of the port to be configured. (REQUIRED) *
1012 * 3. Byte sequence representing the new port configuration. The format *
1013 * depends on the port driver (and it is up to the caller to know what *
1014 * it is). (REQUIRED) *
1015 * *
1016 * REXX RETURN VALUE: *
1017 * 1 on success, or 0 if an error occurred. *
1018 * ------------------------------------------------------------------------- */
1019ULONG APIENTRY RPUPortSet( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1020{
1021 HAB hab; // desktop anchor-block handle
1022 HMODULE hPdr = NULLHANDLE; // handle to port driver module
1023 PFN pfnSetPort; // pointer to driver's SplPdSet entrypoint
1024 ULONG ulCB, // return value from PrfQueryProfileString()
1025 cBuf; // size of configuration buffer
1026 PSZ pszPdrName = NULL, // name of the specified port driver
1027 pszPortName = NULL; // name of the new port to create
1028 CHAR szPathName[ CCHMAXPATH+1 ]; // FQN of the port driver module
1029 PBYTE pBuf; // Pointer to configuration buffer
1030 APIRET rc = 0; // return code from Dos...() functions
1031
1032
1033 // Reset the error indicator
1034 WriteErrorCode( 0, NULL );
1035
1036 // Make sure we have exactly three valid arguments
1037 if ( argc != 3 ||
1038 !RXVALIDSTRING( argv[0] ) ||
1039 !RXVALIDSTRING( argv[1] ) ||
1040 !RXVALIDSTRING( argv[2] ) )
1041 return ( 40 );
1042 pszPdrName = argv[0].strptr;
1043 pszPortName = strupr( argv[1].strptr );
1044 pBuf = argv[2].strptr;
1045 cBuf = argv[2].strlength;
1046
1047 // Get the path to the installed port driver
1048 hab = WinInitialize( 0 );
1049 if ( !hab ) {
1050 WriteErrorCode( 0, "WinInitialize");
1051 MAKERXSTRING( *prsResult, "0", 1 );
1052 return ( 0 );
1053 }
1054
1055 ulCB = PrfQueryProfileString( HINI_SYSTEMPROFILE, APPNAME_PM_PORT_DRIVER,
1056 pszPdrName, NULL, (PVOID) szPathName, CCHMAXPATH );
1057 if ( !ulCB ) {
1058 WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString");
1059 MAKERXSTRING( *prsResult, "0", 1 );
1060 goto cleanup;
1061 }
1062
1063 // Load the port driver DLL and register its configuration routine
1064 rc = DosLoadModule( NULL, 0, szPathName, &hPdr );
1065 if ( rc != NO_ERROR || !hPdr ) {
1066 WriteErrorCode( rc, "DosLoadModule");
1067 MAKERXSTRING( *prsResult, "0", 1 );
1068 goto cleanup;
1069 }
1070 rc = DosQueryProcAddr( hPdr, 0, "SPLPDSET", &pfnSetPort );
1071 if ( rc != NO_ERROR ) {
1072 WriteErrorCode( rc, "DosQueryProcAddr");
1073 MAKERXSTRING( *prsResult, "0", 1 );
1074 goto finish;
1075 }
1076
1077 // Now set the new port configuration
1078 rc = pfnSetPort( pszPortName, TYPE_LONG_WAIT, BIDI_SET_PORTDRV, pBuf, cBuf );
1079 if ( rc == NO_ERROR ) {
1080 // Return 1 on success
1081 MAKERXSTRING( *prsResult, "1", 1 );
1082 }
1083 else {
1084 WriteErrorCode( rc, "SplPdSet");
1085 MAKERXSTRING( *prsResult, "0", 1 );
1086 }
1087
1088finish:
1089 DosFreeModule( hPdr );
1090cleanup:
1091 WinTerminate( hab );
1092 return ( 0 );
1093}
1094
1095
1096/* ------------------------------------------------------------------------- *
1097 * RPUPortDialog *
1098 * *
1099 * Brings up the specified port's configuration dialog. This function can *
1100 * only be used from a PM process. *
1101 * *
1102 * REXX ARGUMENTS: *
1103 * 1. The name of the port driver without any extension. This must be *
1104 * installed and registered in OS2.INI already. (REQUIRED) *
1105 * 2. The name of the port to be configured. (REQUIRED) *
1106 * *
1107 * REXX RETURN VALUE: *
1108 * 1 if the user modified the port configuration. *
1109 * 0 if no changes were made, or if an error occurred. *
1110 * ------------------------------------------------------------------------- */
1111ULONG APIENTRY RPUPortDialog( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1112{
1113 HAB hab; // desktop anchor-block handle
1114 HMODULE hPdr = NULLHANDLE; // handle to port driver module
1115 PFN pfnSetPort; // pointer to driver's SplPdSet entrypoint
1116 ULONG ulCB, // return value from PrfQueryProfileString()
1117 flModified = 0; // flag indicating settings were changed
1118 PSZ pszPdrName = NULL, // name of the specified port driver
1119 pszPortName = NULL; // name of the new port to create
1120 CHAR szPathName[ CCHMAXPATH+1 ]; // FQN of the port driver module
1121 APIRET rc = 0; // return code from Dos...() functions
1122
1123
1124 // Reset the error indicator
1125 WriteErrorCode( 0, NULL );
1126
1127 // Make sure we have exactly two valid arguments
1128 if ( argc != 2 ||
1129 !RXVALIDSTRING( argv[0] ) ||
1130 !RXVALIDSTRING( argv[1] ) )
1131 return ( 40 );
1132 pszPdrName = argv[0].strptr;
1133 pszPortName = strupr( argv[1].strptr );
1134
1135 // Get the path to the installed port driver
1136 hab = WinInitialize( 0 );
1137 if ( !hab ) {
1138 WriteErrorCode( 0, "WinInitialize");
1139 MAKERXSTRING( *prsResult, "0", 1 );
1140 return ( 0 );
1141 }
1142
1143 ulCB = PrfQueryProfileString( HINI_SYSTEMPROFILE, APPNAME_PM_PORT_DRIVER,
1144 pszPdrName, NULL, (PVOID) szPathName, CCHMAXPATH );
1145 if ( !ulCB ) {
1146 WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString");
1147 MAKERXSTRING( *prsResult, "0", 1 );
1148 goto cleanup;
1149 }
1150
1151 // Load the port driver DLL and register its dialog routine
1152 rc = DosLoadModule( NULL, 0, szPathName, &hPdr );
1153 if ( rc != NO_ERROR || !hPdr ) {
1154 WriteErrorCode( rc, "DosLoadModule");
1155 MAKERXSTRING( *prsResult, "0", 1 );
1156 goto cleanup;
1157 }
1158 rc = DosQueryProcAddr( hPdr, 0, "SPLPDSETPORT", &pfnSetPort );
1159 if ( rc != NO_ERROR ) {
1160 WriteErrorCode( rc, "DosQueryProcAddr");
1161 MAKERXSTRING( *prsResult, "0", 1 );
1162 goto finish;
1163 }
1164
1165 // Now show the port configuration dialog
1166 rc = pfnSetPort( hab, pszPortName, &flModified );
1167 if ( rc == NO_ERROR ) {
1168 // Return 1 if settings were modified
1169 MAKERXSTRING( *prsResult, flModified? "1": "0", 1 );
1170 }
1171 else {
1172 WriteErrorCode( rc, "SplPdSetPort");
1173 MAKERXSTRING( *prsResult, "0", 1 );
1174 }
1175
1176finish:
1177 DosFreeModule( hPdr );
1178cleanup:
1179 WinTerminate( hab );
1180 return ( 0 );
1181}
1182
1183
1184/* ************************************************************************* *
1185 * INTERNAL PRINTER-RELATED FUNCTIONS *
1186 * ************************************************************************* */
1187
1188/* ------------------------------------------------------------------------- *
1189 * PrinterObjectHandle *
1190 * *
1191 * Given a print queue name, obtain the handle to the WPS printer object *
1192 * associated with that queue. This is done by querying OS2.INI for the *
1193 * queues defined in PM_PrintObject and reading the associated handle IDs. *
1194 * *
1195 * ARGUMENTS: *
1196 * PSZ pszQueueName: The printer queue name. This is NOT the same as the *
1197 * printer device name, although in many cases they may *
1198 * have the same value. *
1199 * *
1200 * RETURNS: HOBJECT *
1201 * WPS object handle of the printer object; NULLHANDLE if no object was *
1202 * found or an error occurred. *
1203 * ------------------------------------------------------------------------- */
1204HOBJECT PrinterObjectHandle( PSZ pszQueueName )
1205{
1206 PVOID pbuf = NULL;
1207 PSZ psz;
1208 CHAR szValue[ 256 ];
1209 ULONG cbTotal = 0,
1210 cbActual = 0;
1211 HOBJECT hObj = 0;
1212 BOOL fRC;
1213
1214
1215 fRC = PrfQueryProfileSize( HINI_USERPROFILE,
1216 APPNAME_PM_PRINT_OBJECT, NULL, &cbTotal );
1217 if ( !fRC || !cbTotal ) {
1218 //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileSize");
1219 return NULLHANDLE;
1220 }
1221 pbuf = malloc( cbTotal );
1222 if ( !pbuf ) {
1223 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
1224 return NULLHANDLE;
1225 }
1226
1227 cbActual = PrfQueryProfileString( HINI_USERPROFILE, APPNAME_PM_PRINT_OBJECT,
1228 NULL, NULL, pbuf, cbTotal );
1229 if ( !cbActual ) {
1230 //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString");
1231 return NULLHANDLE;
1232 }
1233
1234 psz = (PSZ) pbuf;
1235 while ( *psz ) {
1236 if ( !PrfQueryProfileString( HINI_USERPROFILE, APPNAME_PM_PRINT_OBJECT,
1237 psz, NULL, szValue, 255 ))
1238 {
1239 //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString");
1240 break;
1241 }
1242 if (( strcmp( szValue, pszQueueName ) == 0 ) &&
1243 ( sscanf( psz, "%u", (PULONG) &hObj ) == 1 ))
1244 {
1245 break;
1246 }
1247 psz += strlen( psz ) + 1;
1248 }
1249
1250 free ( pbuf );
1251 return ( hObj );
1252}
1253
1254
1255/* ------------------------------------------------------------------------- *
1256 * GetObjectID *
1257 * *
1258 * Given a WPS object handle, find out the corresponding object ID if one *
1259 * exists. *
1260 * *
1261 * ARGUMENTS: *
1262 * PSZ pszHandle: The WPS object handle, as a hexadecimal string. This *
1263 * should have no prefix, and be all upper-case. *
1264 * *
1265 * RETURNS: PSZ *
1266 * The object ID string. It is up to the caller to free it when done. *
1267 * This will be NULL if no object ID was found. *
1268 * ------------------------------------------------------------------------- */
1269PSZ GetObjectID( PSZ pszHandle )
1270{
1271 PVOID pbuf = NULL;
1272 ULONG cbTotal = 0,
1273 cbActual = 0;
1274 CHAR szValue[ 9 ];
1275 PSZ psz,
1276 pszObjID = NULL;
1277 BOOL fRC;
1278
1279
1280 fRC = PrfQueryProfileSize( HINI_USERPROFILE,
1281 APPNAME_PM_WPS_LOCATION, NULL, &cbTotal );
1282 if ( !fRC || !cbTotal ) {
1283 //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileSize");
1284 return NULL;
1285 }
1286 pbuf = malloc( cbTotal );
1287 if ( !pbuf ) {
1288 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
1289 return NULL;
1290 }
1291
1292 cbActual = PrfQueryProfileString( HINI_USERPROFILE, APPNAME_PM_WPS_LOCATION,
1293 NULL, NULL, pbuf, cbTotal );
1294 if ( !cbActual ) {
1295 //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString");
1296 return NULL;
1297 }
1298
1299 psz = (PSZ) pbuf;
1300 while ( *psz ) {
1301 if ( !PrfQueryProfileString( HINI_USERPROFILE, APPNAME_PM_WPS_LOCATION,
1302 psz, NULL, szValue, 8 ))
1303 {
1304 //WriteErrorCode( ERRORIDERROR( WinGetLastError( hab )), "PrfQueryProfileString");
1305 break;
1306 }
1307 if (( strcmp( szValue, pszHandle ) == 0 ) &&
1308 (( pszObjID = strdup( psz )) != NULL ))
1309 {
1310 break;
1311 }
1312 psz += strlen( psz ) + 1;
1313 }
1314
1315 free ( pbuf );
1316 return ( pszObjID );
1317}
1318
1319
1320/* ************************************************************************* *
1321 * INTERNAL REXX DLL UTILITY FUNCTIONS *
1322 * ************************************************************************* */
1323
1324
1325/* ------------------------------------------------------------------------- *
1326 * SaveResultString *
1327 * *
1328 * Writes new string contents to the specified RXSTRING, allocating any *
1329 * additional memory that may be required. If the string to be written has *
1330 * zero length, nothing is done. *
1331 * *
1332 * This function should be used in place of MAKERXSTRING if there is a *
1333 * possibility that the string contents could be longer than 256 characters. *
1334 * *
1335 * ARGUMENTS: *
1336 * PRXSTRING prsResult: Pointer to an existing RXSTRING for writing. *
1337 * PCH pchBytes : The string contents to write to prsResult. *
1338 * ULONG ulBytes : The number of bytes in pchBytes to write. *
1339 * *
1340 * RETURNS: BOOL *
1341 * TRUE if prsResult was successfully updated. FALSE otherwise. *
1342 * ------------------------------------------------------------------------- */
1343BOOL SaveResultString( PRXSTRING prsResult, PCH pchBytes, ULONG ulBytes )
1344{
1345 ULONG ulRC;
1346 PCH pchNew;
1347
1348 if ( ulBytes == 0 ) return ( FALSE );
1349 if ( ulBytes > 256 ) {
1350 // REXX provides 256 bytes by default; allocate more if necessary
1351 ulRC = DosAllocMem( (PVOID) &pchNew, ulBytes, PAG_WRITE | PAG_COMMIT );
1352 if ( ulRC != 0 ) {
1353 WriteErrorCode( ulRC, "DosAllocMem");
1354 return ( FALSE );
1355 }
1356 DosFreeMem( prsResult->strptr );
1357 prsResult->strptr = pchNew;
1358 }
1359 memcpy( prsResult->strptr, pchBytes, ulBytes );
1360 prsResult->strlength = ulBytes;
1361
1362 return ( TRUE );
1363}
1364
1365
1366/* ------------------------------------------------------------------------- *
1367 * WriteStemElement *
1368 * *
1369 * Creates a stem element (compound variable) in the calling REXX program *
1370 * using the REXX shared variable pool interface. *
1371 * *
1372 * ARGUMENTS: *
1373 * PSZ pszStem : The name of the stem (before the '.') *
1374 * ULONG ulIndex : The number of the stem element (after the '.') *
1375 * PSZ pszValue : The value to write to the compound variable. *
1376 * *
1377 * RETURNS: BOOL *
1378 * TRUE on success, FALSE on failure. *
1379 * ------------------------------------------------------------------------- */
1380BOOL WriteStemElement( PSZ pszStem, ULONG ulIndex, PSZ pszValue )
1381{
1382 SHVBLOCK shvVar; // REXX shared variable pool block
1383 ULONG ulRc,
1384 ulBytes;
1385 CHAR szCompoundName[ US_COMPOUND_MAXZ ],
1386 *pchValue;
1387
1388 sprintf( szCompoundName, "%s.%d", pszStem, ulIndex );
1389 if ( pszValue == NULL ) {
1390 pchValue = "";
1391 ulBytes = 0;
1392 } else {
1393 ulBytes = strlen( pszValue );
1394 ulRc = DosAllocMem( (PVOID) &pchValue, ulBytes + 1, PAG_WRITE | PAG_COMMIT );
1395 if ( ulRc != 0 ) {
1396 WriteErrorCode( ulRc, "DosAllocMem");
1397 return FALSE;
1398 }
1399 memcpy( pchValue, pszValue, ulBytes );
1400 }
1401 MAKERXSTRING( shvVar.shvname, szCompoundName, strlen(szCompoundName) );
1402 shvVar.shvvalue.strptr = pchValue;
1403 shvVar.shvvalue.strlength = ulBytes;
1404 shvVar.shvnamelen = RXSTRLEN( shvVar.shvname );
1405 shvVar.shvvaluelen = RXSTRLEN( shvVar.shvvalue );
1406 shvVar.shvcode = RXSHV_SYSET;
1407 shvVar.shvnext = NULL;
1408 ulRc = RexxVariablePool( &shvVar );
1409 if ( ulRc > 1 ) {
1410 WriteErrorCode( shvVar.shvret, "RexxVariablePool (SHVBLOCK.shvret)");
1411 return FALSE;
1412 }
1413 return TRUE;
1414
1415}
1416
1417
1418/* ------------------------------------------------------------------------- *
1419 * WriteCompoundVariable *
1420 * *
1421 * Creates a compound variable in the calling REXX program using the REXX *
1422 * shared variable pool interface. *
1423 * *
1424 * ARGUMENTS: *
1425 * PSZ pszStem : The name of the stem (before the '.') *
1426 * PSZ pszTail : The name of the trailing portion (after the '.') *
1427 * PSZ pszValue : The value to write to the compound variable. *
1428 * *
1429 * RETURNS: BOOL *
1430 * TRUE on success, FALSE on failure. *
1431 * ------------------------------------------------------------------------- */
1432BOOL WriteCompoundVariable( PSZ pszStem, PSZ pszTail, PSZ pszValue )
1433{
1434 SHVBLOCK shvVar; // REXX shared variable pool block
1435 ULONG ulRc,
1436 ulBytes;
1437 CHAR szCompoundName[ US_COMPOUND_MAXZ ],
1438 *pchValue;
1439
1440 sprintf( szCompoundName, "%s.%s", pszStem, pszTail );
1441 if ( pszValue == NULL ) {
1442 pchValue = "";
1443 ulBytes = 0;
1444 } else {
1445 ulBytes = strlen( pszValue );
1446 ulRc = DosAllocMem( (PVOID) &pchValue, ulBytes + 1, PAG_WRITE | PAG_COMMIT );
1447 if ( ulRc != 0 ) {
1448 WriteErrorCode( ulRc, "DosAllocMem");
1449 return FALSE;
1450 }
1451 memcpy( pchValue, pszValue, ulBytes );
1452 }
1453 MAKERXSTRING( shvVar.shvname, szCompoundName, strlen(szCompoundName) );
1454 shvVar.shvvalue.strptr = pchValue;
1455 shvVar.shvvalue.strlength = ulBytes;
1456 shvVar.shvnamelen = RXSTRLEN( shvVar.shvname );
1457 shvVar.shvvaluelen = RXSTRLEN( shvVar.shvvalue );
1458 shvVar.shvcode = RXSHV_SYSET;
1459 shvVar.shvnext = NULL;
1460 ulRc = RexxVariablePool( &shvVar );
1461 if ( ulRc > 1 ) {
1462 WriteErrorCode( shvVar.shvret, "RexxVariablePool (SHVBLOCK.shvret)");
1463 return FALSE;
1464 }
1465 return TRUE;
1466}
1467
1468
1469/* ------------------------------------------------------------------------- *
1470 * WriteErrorCode *
1471 * *
1472 * Writes an error code to a special variable in the calling REXX program *
1473 * using the REXX shared variable pool interface. This is used to return *
1474 * API error codes to the REXX program, since the REXX functions themselves *
1475 * normally return string values. *
1476 * *
1477 * ARGUMENTS: *
1478 * ULONG ulError : The error code returned by the failing API call. *
1479 * PSZ pszContext: A string describing the API call that failed. *
1480 * *
1481 * RETURNS: N/A *
1482 * ------------------------------------------------------------------------- */
1483void WriteErrorCode( ULONG ulError, PSZ pszContext )
1484{
1485 SHVBLOCK shvVar; // REXX shared variable pool block
1486 ULONG ulRc;
1487 CHAR szErrorText[ US_ERRSTR_MAXZ ];
1488
1489 if ( pszContext == NULL )
1490 sprintf( szErrorText, "%X", ulError );
1491 else
1492 sprintf( szErrorText, "%X: %s", ulError, pszContext );
1493 MAKERXSTRING( shvVar.shvname, SZ_ERROR_NAME, strlen(SZ_ERROR_NAME) );
1494 MAKERXSTRING( shvVar.shvvalue, szErrorText, strlen(szErrorText) );
1495 shvVar.shvnamelen = RXSTRLEN( shvVar.shvname );
1496 shvVar.shvvaluelen = RXSTRLEN( shvVar.shvvalue );
1497 shvVar.shvcode = RXSHV_SYSET;
1498 shvVar.shvnext = NULL;
1499 ulRc = RexxVariablePool( &shvVar );
1500 if ( ulRc > 1 )
1501 printf("Unable to set %s: rc = %d\n", shvVar.shvname.strptr, shvVar.shvret );
1502}
1503
1504
Note: See TracBrowser for help on using the repository browser.