source: rxprtutl/trunk/rxprtutl.c@ 7

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

Added RPUPrinterDelete function.

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