source: rxprtutl/trunk/rxprtutl.c@ 5

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

Added RPUPrinterCreate function. Renamed several other functions for improved consistency.

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