Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / lib / DtPrint / PsubUtil.c
1 /* $XConsortium: PsubUtil.c /main/8 1996/10/31 02:09:44 cde-hp $ */
2 /*
3  * DtPrint/PsubUtil.c
4  */
5 /*
6  * (c) Copyright 1996 Digital Equipment Corporation.
7  * (c) Copyright 1996 Hewlett-Packard Company.
8  * (c) Copyright 1996 International Business Machines Corp.
9  * (c) Copyright 1996 Sun Microsystems, Inc.
10  * (c) Copyright 1996 Novell, Inc. 
11  * (c) Copyright 1996 FUJITSU LIMITED.
12  * (c) Copyright 1996 Hitachi.
13  */
14 /*
15  * ------------------------------------------------------------------------
16  * Include Files
17  *
18  */
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <Dt/DtNlUtils.h>
22
23 #include <Dt/PsubUtilI.h>
24
25 /*
26  * ------------------------------------------------------------------------
27  * Constant Definitions
28  *
29  */
30 /*
31  * separator between printer name and display in an X Printer Specifier
32  */
33 #define XPSPEC_NAME_DISP_SEP_CHAR '@'
34 #define XPSPEC_NAME_DISP_SEP "@"
35 #define XPSPEC_NAME_DISP_SEP_LEN 1
36 #define XPSPEC_DISP_SCREEN_SEP "."
37
38 /*
39  * ------------------------------------------------------------------------
40  * Static Function Declarations
41  *
42  */
43 static String* BuildStringList(
44                                String list_string,
45                                int i);
46 static XtEnum OpenXPrinterOnDisplay(
47                                     String printer_name,
48                                     String display_spec,
49                                     Display** new_display,
50                                     char** ct_printer_name);
51 static int SpanNonWhitespace(
52                              char* string);
53 static int SpanWhitespace(
54                           char* string);
55 static int StringToCompoundText(
56                                 Display* display,
57                                 char** compound_text,
58                                 const char* string);
59 static Boolean TrimWhitespace(
60                               String s);
61
62 /*
63  * ------------------------------------------------------------------------
64  * Name: BuildStringList
65  *
66  * Description:
67  *
68  *     Build a newly allocated array of Strings by recursively parsing
69  *     whitespace delimited items out of the passed list String.
70  *
71  * Return value:
72  *
73  *     The array of strings. It is the caller's responsibility to free
74  *     the returned list by calling _DtPrintFreeStringList.
75  *
76  */
77 static String*
78 BuildStringList(String list_string, int i)
79 {
80     String string = NULL;
81     /*
82      * parse the next item out of the string list
83      */
84     if(list_string != (String)NULL)
85     {
86         int length;
87         list_string += SpanWhitespace(list_string);
88         length = SpanNonWhitespace(list_string);
89         if(length != 0)
90         {
91             string = XtCalloc(length+1, sizeof(char));
92             strncpy(string, list_string, length);
93             list_string += length;
94         }
95     }
96     if(string == (String)NULL)
97     {
98         /*
99          * end of string list; allocate the array
100          */
101         return (String*)XtCalloc(i+1, sizeof(String));
102     }
103     else
104     {
105         /*
106          * recurse
107          */
108         String* string_list = BuildStringList(list_string, i+1);
109         /*
110          * set the string in the list and return
111          */
112         string_list[i] = string;
113         return string_list;
114     }
115 }
116
117 /*
118  * ------------------------------------------------------------------------
119  * Name: OpenXPrinterOnDisplay
120  *
121  * Description:
122  *
123  *     Opens 'printer_name' on display 'display_spec'. If successful, the
124  *     passed 'new_display' is updated. This function does *not*
125  *     initialize the print context.
126  *
127  *     The return parm 'ct_printer_name' is updated with the compound
128  *     text version of the printer name used in establishing whether the
129  *     printer is managed by the passed display. If conversion is not
130  *     possible, 'printer_name' is used as is, and the location pointed
131  *     to by 'ct_printer_name' is set to NULL. The 'ct_printer_name'
132  *     return will also be set to NULL if the function return value is
133  *     not DtPRINT_SUCCESS. If the location pointed to by
134  *     'ct_printer_name' is updated to non-NULL, it is the caller's
135  *     responsiblity to free the memory indicated by 'ct_printer_name'
136  *     using XFree().
137  *
138  *     The 'ct_printer_name' return is provided as a convenience for the
139  *     caller to subsequently pass to XpInitContext().
140  *
141  * Return value:
142  *
143  *     DtPRINT_SUCCESS
144  *         An X printer connection was successfully opened.
145  *
146  *     DtPRINT_NO_PRINTER
147  *         The display does not manage the indicated printer.
148  *
149  *     DtPRINT_NOT_XP_DISPLAY
150  *         The display does not support printing.
151  *
152  *     DtPRINT_INVALID_DISPLAY
153  *         The display could not be opened.
154  *
155  */
156 static XtEnum
157 OpenXPrinterOnDisplay(
158                       String printer_name,
159                       String display_spec,
160                       Display** new_display,
161                       char** ct_printer_name)
162 {
163     Display* print_display;
164     XPPrinterList printer_list;
165     int error_base;
166     int event_base;
167     int printer_count;
168
169     *ct_printer_name = (char*)NULL;
170     /*
171      * open the print display
172      */
173     print_display = XOpenDisplay(display_spec);
174     if(print_display != (Display*)NULL)
175     {
176         if(XpQueryExtension(print_display, &event_base, &error_base))
177         {
178             /*
179              * validate the printer
180              */
181             StringToCompoundText(print_display,
182                                  ct_printer_name,
183                                  printer_name);
184             if((char*)NULL == *ct_printer_name)
185             {
186                 printer_list =
187                     XpGetPrinterList(print_display,
188                                      printer_name,
189                                      &printer_count);
190             }
191             else
192             {
193                 printer_list =
194                     XpGetPrinterList(print_display,
195                                      *ct_printer_name,
196                                      &printer_count);
197             }
198             if(printer_list == (XPPrinterList)NULL)
199             {
200                 XCloseDisplay(print_display);
201                 if(*ct_printer_name)
202                     XFree(*ct_printer_name);
203                 return DtPRINT_NO_PRINTER;
204             }
205             else
206             {
207                 *new_display = print_display;
208                 XpFreePrinterList(printer_list);
209                 return DtPRINT_SUCCESS;
210             }
211         }
212         else
213         {
214             XCloseDisplay(print_display);
215             return DtPRINT_NOT_XP_DISPLAY;
216         }
217     }
218     else
219     {
220         return DtPRINT_INVALID_DISPLAY;
221     }
222 }
223
224 /*
225  * ------------------------------------------------------------------------
226  * Name: SpanNonWhitespace
227  *
228  * Description:
229  *
230  *     Returns the length of the initial segment of the passed string
231  *     that consists entirely of non-whitspace characters.
232  *
233  *
234  */
235 static int
236 SpanNonWhitespace(char* string)
237 {
238     char* ptr;
239     for(ptr = string;
240         *ptr != '\0' && !DtIsspace(ptr);
241         ptr = DtNextChar(ptr));
242     return ptr - string;
243 }
244
245 /*
246  * ------------------------------------------------------------------------
247  * Name: SpanWhitespace
248  *
249  * Description:
250  *
251  *     Returns the length of the initial segment of the passed string
252  *     that consists entirely of whitespace characters.
253  *
254  *
255  */
256 static int
257 SpanWhitespace(char* string)
258 {
259     char* ptr;
260     for(ptr = string;
261         *ptr != '\0' && DtIsspace(ptr);
262         ptr = DtNextChar(ptr));
263     return ptr - string;
264 }
265
266 /*
267  * ------------------------------------------------------------------------
268  * Name: StringToCompoundText
269  *
270  * Description:
271  *
272  *     Converts a string to compund text for use with Xp.
273  *
274  * Return Value:
275  *
276  *     The value returned from XmbTextListToTextProperty; e.g. Success if
277  *     successful.
278  *
279  *     The return parm compound_text will contain a pointer to the
280  *     converted text. It is the caller's responsibility to free this
281  *     string using XFree().
282  *
283  */
284 static int
285 StringToCompoundText(
286                      Display* display,
287                      char** compound_text,
288                      const char* string)
289 {
290     XTextProperty text_prop;
291     int status;
292     
293     status = XmbTextListToTextProperty(display,
294                                        (char**)&string, 1,
295                                        XCompoundTextStyle,
296                                        &text_prop);
297     if(Success == status)
298         *compound_text = (char*)text_prop.value;
299     else
300         *compound_text = (char*)NULL;
301
302     return status;
303 }
304
305 /*
306  * ------------------------------------------------------------------------
307  * Name: TrimWhitespace
308  *
309  * Description:
310  *
311  *     Remove leading and trailing whitespace from the passed string.
312  *     This function is multi-byte safe.
313  *
314  * Return value:
315  *
316  *     True if the string was modified; False otherwise.
317  *
318  */
319 static Boolean
320 TrimWhitespace(String string)
321 {
322     String ptr;
323     int i;
324     String last_non_ws;
325     Boolean modified = False;
326
327     if((String)NULL == string)
328         return modified;
329     /*
330      * find the first non-whitespace character
331      */
332     for(ptr = string; *ptr != '\0' && DtIsspace(ptr); ptr = DtNextChar(ptr));
333     /*
334      * reposition the string
335      */
336     if(ptr != string)
337     {
338         modified = True;
339         for(i = 0; ptr[i] != '\0'; i++)
340             string[i] = ptr[i];
341         string[i] = '\0';
342     }
343     /*
344      * find the last non-whitespace character
345      */
346     for(ptr = string, last_non_ws = NULL; *ptr != '\0'; ptr = DtNextChar(ptr))
347         if(!DtIsspace(ptr))
348             last_non_ws = ptr;
349     /*
350      * trim any trailing whitespace
351      */
352     if((String)NULL != last_non_ws)
353     {
354         ptr = DtNextChar(last_non_ws);
355         if(*ptr != '\0')
356         {
357             modified = True;
358             *ptr = '\0';
359         }
360     }
361     return modified;
362 }
363
364 /*
365  * ------------------------------------------------------------------------
366  * Name: _DtPrintCreateXPrinterSpecifier
367  *
368  * Description:
369  *
370  *     Concatinates the passed X printer specifier components into a
371  *     newly allocated String, inserting separators between components as
372  *     needed. All components are optional; i.e. the String parms may be
373  *     passed as NULL, and the int parms may be passed as -1.
374  *
375  *     Separators are inserted as follows:
376  *
377  *         * if both the 'printer_name' and 'host_name' are specified, a
378  *           "@" will be inserted between them.
379  *
380  *         * if the 'display_num' is specified:
381  *
382  *             - if 'spec_net' is TCP_IPC or UNSPECIFIED, a ":" will be
383  *               inserted immediately preceding it.
384  *
385  *             - if 'spec_net' is DEC_NET, a "::" will be inserted
386  *               immediately preceding it.
387  *           
388  *         * if the 'screen_num' is specified, a "." will be inserted
389  *           immediately preceding it.
390  *
391  * Return value:
392  *
393  *     A newly allocated X printer specifier. If all of the components
394  *     are omitted, an empty string ("") will be returned. It is the
395  *     responsibility of the caller to free the returned specifier by
396  *     calling XtFree.
397  */
398 String
399 _DtPrintCreateXPrinterSpecifier(
400                                 String printer_name,
401                                 String host_name,
402                                 DtPrintSpecNet spec_net,
403                                 int display_num,
404                                 int screen_num)
405 {
406     String printer_specifier;
407     char display_string[32];
408     char screen_string[32];
409     int printer_name_len;
410     int host_name_len;
411     int specifier_len = 0;
412     String separator;
413     /*
414      * printer name length
415      */
416     if(printer_name != (String)NULL)
417         specifier_len += printer_name_len = strlen(printer_name);
418     else
419         printer_name_len = 0;
420     /*
421      * host name length
422      */
423     if(host_name != (String)NULL)
424         specifier_len += host_name_len = strlen(host_name);
425     else
426         host_name_len = 0;
427     /*
428      * printer name / display separator length
429      */
430     if(printer_name_len != 0 && (host_name_len != 0 || display_num != -1))
431     {
432         separator = XPSPEC_NAME_DISP_SEP;
433         specifier_len += XPSPEC_NAME_DISP_SEP_LEN;
434     }
435     else
436         separator = (String)NULL;
437     /*
438      * display number
439      */
440     if(display_num == -1)
441         display_string[0] = '\0';
442     else
443         specifier_len +=
444             sprintf(display_string, "%s%d",
445                     spec_net == DtPRINT_DEC_NET ? "::" : ":",
446                     display_num);
447     /*
448      * screen number
449      */
450     if(screen_num == -1)
451         screen_string[0] = '\0';
452     else
453         specifier_len +=
454             sprintf(screen_string, "%s%d", XPSPEC_DISP_SCREEN_SEP, screen_num);
455     /*
456      * create and return the new printer specifier
457      */
458     printer_specifier = XtMalloc(specifier_len + 1);
459     sprintf(printer_specifier,
460             "%s%s%s%s%s",
461             printer_name ? printer_name : "",
462             separator ? separator : "",
463             host_name ? host_name : "",
464             display_string,
465             screen_string);
466     return printer_specifier;
467 }
468
469 /*
470  * ------------------------------------------------------------------------
471  * Name: _DtPrintFreeStringList
472  *
473  * Description:
474  *
475  *     Frees a string list created by BuildStringList.
476  *
477  * Return value:
478  *
479  *     None.
480  *
481  */
482 void
483 _DtPrintFreeStringList(
484                          String* server_list)
485 {
486     if(server_list)
487     {
488         int i;
489         for(i = 0; server_list[i] != (String)NULL; i++)
490         {
491             XtFree(server_list[i]);
492         }
493         XtFree((char*)server_list);
494     }
495 }
496
497 /*
498  * ------------------------------------------------------------------------
499  * Name: _DtPrintGetDefaultXPrinterName
500  *
501  * Description:
502  *
503  *
504  * Return Value:
505  *
506  *     The default printer name, or NULL if no default could be
507  *     determined. It is the responsibility of the caller to free the
508  *     memory allocated for the returned String by calling XtFree.
509  *
510  */
511 String
512 _DtPrintGetDefaultXPrinterName(
513                                Widget w)
514 {
515     String default_printer;
516
517     if((Widget)NULL == w)
518     {
519         default_printer = (String)NULL;
520     }
521     else
522     {
523         XtResource res_struct;
524         /*
525          * initialize the resource structure
526          */
527         res_struct.resource_name = "xpPrinter";
528         res_struct.resource_class = "XpPrinter";
529         res_struct.resource_type =  XmRString;
530         res_struct.resource_size = sizeof(String);
531         res_struct.resource_offset = 0;
532         res_struct.default_type = XmRImmediate;
533         res_struct.default_addr = (XtPointer)NULL;
534         /*
535          * pick up the printer list application resource value for the
536          * passed widget
537          */
538         XtGetApplicationResources(w, (XtPointer)&default_printer,
539                                   &res_struct, 1, (ArgList)NULL, 0);
540     }
541     /*
542      * if the resource is undefined, search for an appropriate
543      * environment variable
544      */
545     if(default_printer != (String)NULL);
546     else if((default_printer = getenv("XPRINTER")) != (String)NULL);
547     else if((default_printer = getenv("PDPRINTER")) != (String)NULL);
548     else if((default_printer = getenv("LPDEST")) != (String)NULL);
549     else if((default_printer = getenv("PRINTER")) != (String)NULL);
550     /*
551      * return a copy of the printer name
552      */
553     return XtNewString(default_printer);
554 }
555
556 /*
557  * ------------------------------------------------------------------------
558  * Name: _DtPrintGetXpPrinterList
559  *
560  * Description:
561  *
562  *     Retrieves the short list of Printers from the XpPrinterList
563  *     resource, or XPPRINTERLIST environment variable.
564  *
565  * Return value:
566  *
567  *     A newly allocated array of printer name Strings. It is the caller's
568  *     responsibility to free the returned array by calling
569  *     _DtPrintFreeStringList.
570  *
571  */
572 String*
573 _DtPrintGetXpPrinterList(
574                          Widget w)
575 {
576     XtResource res_struct;
577     String xp_printer_list;
578     /*
579      * initialize the resource structure
580      */
581     res_struct.resource_name = "xpPrinterList";
582     res_struct.resource_class = "XpPrinterList";
583     res_struct.resource_type =  XmRString;
584     res_struct.resource_size = sizeof(String);
585     res_struct.resource_offset = 0;
586     res_struct.default_type = XmRImmediate;
587     res_struct.default_addr = (XtPointer)NULL;
588     /*
589      * pick up the printer list application resource value for the passed
590      * widget
591      */
592     XtGetApplicationResources(w, (XtPointer)&xp_printer_list,
593                               &res_struct, 1, (ArgList)NULL, 0);
594     /*
595      * if the resource is undefined, use the environment variable
596      */
597     if(xp_printer_list == (String)NULL)
598     {
599         xp_printer_list = getenv("XPRINTERLIST");
600     }
601     /*
602      * build the array of printer names
603      */
604     if(xp_printer_list != (String)NULL)
605     {
606         return BuildStringList(xp_printer_list, 0);
607     }
608     else
609         return (String*)NULL;
610 }
611
612 /*
613  * ------------------------------------------------------------------------
614  * Name: _DtPrintGetXpServerList
615  *
616  * Description:
617  *
618  *     Retrieves the Xp Server list from the XpServerList resource, or if
619  *     the resource is undefined, the XPSERVERLIST environment variable
620  *     is used.
621  *
622  *     Each server name in the return list will be of the form
623  *     "host:display". If any entry from XpServerList does not contain a
624  *     display number, a default of 0 will be used.
625  *
626  * Return value:
627  *
628  *     A newly allocated array of server name Strings. It is the caller's
629  *     responsibility to free the returned array by calling
630  *     _DtPrintFreeStringList.
631  *
632  */
633 String*
634 _DtPrintGetXpServerList(
635                         Widget w)
636 {
637     XtResource res_struct;
638     String xp_server_list;
639     int error_base;
640     int event_base;
641     String* server_list;
642     int i;
643     /*
644      * initialize the resource structure
645      */
646     res_struct.resource_name = "xpServerList";
647     res_struct.resource_class = "XpServerList";
648     res_struct.resource_type =  XmRString;
649     res_struct.resource_size = sizeof(String);
650     res_struct.resource_offset = 0;
651     res_struct.default_type = XmRImmediate;
652     res_struct.default_addr = (XtPointer)NULL;
653     /*
654      * pick up the server list application resource value for the passed
655      * widget
656      */
657     if((Widget)NULL == w)
658         xp_server_list = (String)NULL;
659     else
660         XtGetApplicationResources(w, (XtPointer)&xp_server_list,
661                                   &res_struct, 1, (ArgList)NULL, 0);
662     /*
663      * if the resource is undefined, use the environment variable value
664      */
665     if(xp_server_list == (String)NULL)
666     {
667         xp_server_list = getenv("XPSERVERLIST");
668     }
669     /*
670      * convert to a list of strings
671      */
672     if((Widget)NULL != w
673        &&
674        XpQueryExtension(XtDisplay(w), &event_base, &error_base))
675     {
676         /*
677          * the video server supports the Xp extension, add it to the front
678          * of the list.
679          */
680         server_list = BuildStringList(xp_server_list, 1);
681         server_list[0] = XtNewString(XDisplayString(XtDisplay(w)));
682     }
683     else if(xp_server_list != (String)NULL)
684     {
685         server_list = BuildStringList(xp_server_list, 0);
686     }
687     else
688         server_list = (String*)NULL;
689     /*
690      * default the display number to ":0" if needed
691      */
692     for(i = 0; server_list && server_list[i]; i++)
693     {
694         String host_name;
695         int display_num;
696         /*
697          * check to see if display number is specified
698          */
699         _DtPrintParseXDisplaySpecifier(server_list[i],
700                                        &host_name,
701                                        (DtPrintSpecNet*)NULL,
702                                        &display_num,
703                                        (int*)NULL);
704         if(display_num == -1)
705         {
706             /*
707              * display number not specified; default to ":0"
708              */
709             XtFree(server_list[i]);
710             server_list[i] =
711                 _DtPrintCreateXPrinterSpecifier((String)NULL, host_name,
712                                                 DtPRINT_TCP_IPC, 0, -1);
713         }
714         XtFree(host_name);
715     }
716     /*
717      * return
718      */
719     return server_list;
720 }
721
722
723 /*
724  * ------------------------------------------------------------------------
725  * Name: _DtPrintParseXDisplaySpecifier
726  *
727  * Description:
728  *
729  *     Parse the host name and the display and screen numbers from a
730  *     conventional X Display Specifier (e.g. as in the DISPLAY env var).
731  *
732  *     This function returns the component values into the locations
733  *     pointed to by the host_name, display_num, and screen_num
734  *     parameters. If any component is not desired, the corresponding
735  *     parm may be set to NULL. If a non-NULL host_name is passed, it is
736  *     the responsibility of the caller to free the newly allocated
737  *     String set in this parm by calling XtFree().
738  *
739  *     If the hostname component is missing from the passed display spec,
740  *     an empty string is returned. If the display spec is NULL, NULL is
741  *     returned. (-1) will be returned for the display or screen number
742  *     if the display spec is NULL or if the display or screen component
743  *     is missing from the spec.
744  *
745  * Return value:
746  *
747  *     None.
748  *
749  */
750 void
751 _DtPrintParseXDisplaySpecifier(
752                                const String display_spec,
753                                String* host_name,
754                                DtPrintSpecNet* spec_net,
755                                int* display_num,
756                                int* screen_num)
757 {
758     char* ptr;
759
760     if(display_spec == (String)NULL)
761     {
762         /*
763          * not much to do with a NULL display spec
764          */
765         if(host_name) *host_name = (String)NULL;
766         if(spec_net) *spec_net = DtPRINT_NET_UNSPECIFIED;
767         if(display_num) *display_num = -1;
768         if(screen_num) *screen_num = -1;
769         return;
770     }
771     /*
772      * find the start of the display number in the display spec
773      */
774     ptr = DtStrchr(display_spec, ':');
775     if(ptr == (char*)NULL)
776     {
777         /*
778          * not found, return -1 for display and screen
779          */
780         if(spec_net) *spec_net = DtPRINT_NET_UNSPECIFIED;
781         if(display_num) *display_num = -1;
782         if(screen_num) *screen_num = -1;
783         /*
784          * return the host name as a copy of the display spec
785          */
786         if(host_name) *host_name = XtNewString(display_spec);
787     }
788     else
789     {
790         int num;
791         /*
792          * skip over the ':', determine if this is a DECnet specifier,
793          * and pick up the display num if specified
794          */
795         ++ptr;
796         if(*ptr == '\0')
797         {
798             if(spec_net) *spec_net = DtPRINT_NET_UNSPECIFIED;
799             num = -1;
800         }
801         else
802         {
803             if(*ptr == ':')
804             {
805                 if(spec_net) *spec_net = DtPRINT_DEC_NET;
806                 ++ptr;
807             }
808             else
809             {
810                 if(spec_net) *spec_net = DtPRINT_TCP_IPC;
811             }
812             if(*ptr == '\0')
813                 num = -1;
814             else
815                 num = (int)strtol(ptr, &ptr, 10);
816         }
817         if(display_num) *display_num = num;
818         if(screen_num)
819         {
820             if(num == -1)
821             {
822                 *screen_num = -1;
823             }
824             else
825             {
826                 /*
827                  * parse out the screen number
828                  */
829                 if(*ptr == '.' && *(ptr+1) != '\0')
830                 {
831                     ++ptr;
832                     num = (int)strtol(ptr, &ptr, 10);
833                     if(screen_num) *screen_num = num;
834                 }
835                 else
836                 {
837                     /*
838                      * not found, return -1 for screen
839                      */
840                     *screen_num = -1;
841                 }
842             }
843         }
844         if(host_name)
845         {
846             /*
847              * allocate a new string containing just the host name
848              */
849             int host_name_len = DtStrcspn(display_spec, ":");
850             *host_name = XtMalloc(host_name_len+1);
851             strncpy(*host_name, display_spec, host_name_len);
852             (*host_name)[host_name_len] = '\0';
853         }
854     }
855 }
856
857
858 /*
859  * ------------------------------------------------------------------------
860  * Name: _DtPrintParseXPrinterSpecifier
861  *
862  * Description:
863  *
864  *     Parse the printer name and display specifier components out of an
865  *     X Printer Specifier. This function returns these components as
866  *     newly allocated Strings into the locations pointed to by
867  *     printer_name and display_spec. It is the responsibility of the
868  *     caller to free the Strings by calling XtFree().
869  *
870  *     The printer_name or display_spec parameters may be passed as NULL
871  *     if that component of the specifier is not desired.
872  *
873  *     If the printer specifier is NULL, the locations pointed to
874  *     by the printer_name and display_spec will be set to NULL.
875  *
876  *     If either portion of the specifier is missing, a newly allocated
877  *     empty string will be returned to printer_name or display_spec.
878  *
879  * Return value:
880  *
881  *     None.
882  *
883  */
884 void
885 _DtPrintParseXPrinterSpecifier(
886                                const String specifier,
887                                String* printer_name,
888                                String* display_spec)
889 {
890     if(specifier == (String)NULL)
891     {
892         if(printer_name) *printer_name = (String)NULL;
893         if(display_spec) *display_spec = (String)NULL;
894     }
895     else
896     {
897         String delim_ptr;
898         /*
899          * determine the offset of the printer name / display name delimiter
900          * ('@') within the X Printer Specifier
901          */
902         delim_ptr = DtStrchr(specifier, XPSPEC_NAME_DISP_SEP_CHAR);
903         if(delim_ptr == (String)NULL)
904         {
905             /*
906              * no delimiter found; specifier consists of printer name only
907              */
908             if(printer_name) *printer_name = XtNewString(specifier);
909             if(display_spec) *display_spec = XtNewString("");
910         }
911         else
912         {
913             /*
914              * copy the printer name portion from the specifier
915              */
916             if(printer_name)
917             {
918                 int printer_name_len = delim_ptr - specifier;
919                 *printer_name = (String)XtMalloc(printer_name_len + 1);
920                 strncpy(*printer_name, specifier, printer_name_len);
921                 (*printer_name)[printer_name_len] = '\0';
922             }
923             /*
924              * copy the display name portion from the specifier
925              */
926             if(display_spec) *display_spec = XtNewString(delim_ptr+1);
927         }
928     }
929 }
930
931 /*
932  * ------------------------------------------------------------------------
933  * Name: _DtPrintVerifyXPrinter
934  *
935  * Description:
936  *
937  *     Determines if an X printer specifier is valid by establishing a
938  *     connection to the printer, up to and including initializing a
939  *     print context.
940  *
941  *     If the passed printer specifier is NULL, a default will be
942  *     used. If the passed specifier is incomplete, this function will
943  *     attempt to determine a fully qualified specifier based on the Xp
944  *     server list environment variable or XRM resource. If the specifier
945  *     does not include a display number, ":0" will be used.
946  *
947  *     If this function is successful, the 'new_display' return parameter
948  *     will be set to the Display of the verified printer connection. If
949  *     the passed 'printer_spec' was used as passed to establish the
950  *     connection, the 'new_printer' parm will be set to NULL. If a
951  *     default or fully-qualified version of the printer specifier was
952  *     generated, the generated specifier will be returned via the
953  *     'new_printer' parameter. It is the responsibility of the caller to
954  *     free the generated String by calling XtFree. 'new_printer_spec'
955  *     may be set whether or not _DtPrintVerifyXPrinter successfully
956  *     opens an X printer connection.
957  *
958  * Return value:
959  *
960  *     DtPRINT_SUCCESS
961  *         An X printer connection was successfully opened.
962  *
963  *     DtPRINT_PRINTER_MISSING
964  *         The passed or default printer spec does not include a printer
965  *         name component.
966  *
967  *     DtPRINT_NO_DEFAULT
968  *         The passed printer spec was NULL, and no default printer could
969  *         be determined.
970  *
971  *     DtPRINT_NO_DEFAULT_DISPLAY
972  *         The passed printer spec or default did not include a display
973  *         specifier, and no suitable display could be found within the
974  *         Xp server list.
975  *
976  *     DtPRINT_NO_PRINTER
977  *         The display indicated in the passed printer spec or default
978  *         does not manage the indicated printer.
979  *
980  *     DtPRINT_NOT_XP_DISPLAY
981  *         The display indicated in the passed printer spec or default
982  *         does not support printing.
983  *
984  *     DtPRINT_INVALID_DISPLAY
985  *         The display indicated in the passed printer spec or default
986  *         could not be opened.
987  *
988  */
989 XtEnum
990 _DtPrintVerifyXPrinter(
991                        Widget w,
992                        String printer_spec,
993                        String* new_printer_spec,
994                        Display** new_display,
995                        XPContext* new_context)
996 {
997     String default_printer;
998     String printer_name;
999     String display_spec;
1000     XtEnum status;
1001     Display* print_display;
1002     char* ct_printer_name;
1003     String trimmed_spec;
1004     /*
1005      * initialize the printer spec return parm
1006      */
1007     *new_printer_spec = (String)NULL;
1008     /*
1009      * determine a default printer if the passed printer spec is NULL
1010      */
1011     if(printer_spec == (String)NULL)
1012     {
1013         default_printer = _DtPrintGetDefaultXPrinterName(w);
1014         if(default_printer == (String)NULL)
1015             return DtPRINT_NO_DEFAULT;
1016         else
1017             printer_spec = default_printer;
1018     }
1019     else
1020         default_printer = (String)NULL;
1021     /*
1022      * trim whitespace from the printer spec if needed
1023      */
1024     trimmed_spec = XtNewString(printer_spec);
1025     if(TrimWhitespace(trimmed_spec))
1026     {
1027         printer_spec = trimmed_spec;
1028     }
1029     else
1030     {
1031         XtFree(trimmed_spec);
1032         trimmed_spec = (String)NULL;
1033     }
1034     /*
1035      * break the printer specifier into its printer name and display
1036      * specifier components
1037      */
1038     _DtPrintParseXPrinterSpecifier(printer_spec,
1039                                    &printer_name,
1040                                    &display_spec);
1041     if(*printer_name == '\0')
1042     {
1043         /*
1044          * printer name is missing
1045          */
1046         status = DtPRINT_PRINTER_MISSING;
1047     }
1048     else
1049     {
1050         /*
1051          * if the display spec is empty, search the server list for a
1052          * suitable display
1053          */
1054         if(*display_spec == '\0')
1055         {
1056             String* server_list;
1057             int i;
1058             /*
1059              * find a server in the server list that manages the printer
1060              */
1061             status = DtPRINT_NO_DEFAULT_DISPLAY;
1062             if((server_list = _DtPrintGetXpServerList(w)) != (String*)NULL)
1063             {
1064                 for(i = 0; server_list[i] != (String)NULL; i++)
1065                 {
1066                     if(OpenXPrinterOnDisplay(printer_name,
1067                                              server_list[i],
1068                                              &print_display,
1069                                              &ct_printer_name)
1070                        == DtPRINT_SUCCESS)
1071                     {
1072                         status = DtPRINT_SUCCESS;
1073                         *new_printer_spec =
1074                             _DtPrintCreateXPrinterSpecifier(
1075                                                     printer_name,
1076                                                     server_list[i],
1077                                                     DtPRINT_NET_UNSPECIFIED,
1078                                                     -1, -1);
1079                         break;
1080                     }
1081                 }
1082                 _DtPrintFreeStringList(server_list);
1083             }
1084         }
1085         else
1086         {
1087             String host_name;
1088             int display_num;
1089             /*
1090              * check to see if display number is specified
1091              */
1092             _DtPrintParseXDisplaySpecifier(display_spec,
1093                                            &host_name,
1094                                            (DtPrintSpecNet*)NULL,
1095                                            &display_num,
1096                                            (int*)NULL);
1097             if(display_num == -1)
1098             {
1099                 String new_display_spec;
1100                 /*
1101                  * display number not specified; default to ":0"
1102                  */
1103                 new_display_spec =
1104                     _DtPrintCreateXPrinterSpecifier((String)NULL, host_name,
1105                                                     DtPRINT_TCP_IPC, 0, -1);
1106                 /*
1107                  * create new printer name for return, even if
1108                  * OpenXPrinterOnDisplay is unsuccessful
1109                  */
1110                 *new_printer_spec =
1111                     _DtPrintCreateXPrinterSpecifier(printer_name,
1112                                                     new_display_spec,
1113                                                     DtPRINT_NET_UNSPECIFIED,
1114                                                     -1, -1);
1115                 /*
1116                  * use the new display spec
1117                  */
1118                 XtFree(display_spec);
1119                 display_spec = new_display_spec;
1120             }
1121             XtFree(host_name);
1122             /*
1123              * open the print display
1124              */
1125             status = OpenXPrinterOnDisplay(printer_name,
1126                                            display_spec,
1127                                            &print_display,
1128                                            &ct_printer_name);
1129         }
1130     }
1131     if(status == DtPRINT_SUCCESS)
1132     {
1133         /*
1134          * initialize the print context
1135          */
1136         if((char*)NULL != ct_printer_name)
1137         {
1138             *new_context = XpCreateContext(print_display, ct_printer_name);
1139             XFree(ct_printer_name);
1140         }
1141         else
1142             *new_context = XpCreateContext(print_display, printer_name);
1143         XpSetContext(print_display, *new_context);
1144         /*
1145          * update the display return parm
1146          */
1147         *new_display = print_display;
1148     }
1149     /*
1150      * check to see if the trimmed spec was used
1151      */
1152     if(trimmed_spec != (String)NULL)
1153     {
1154         if(*new_printer_spec == (String)NULL)
1155         {
1156             /*
1157              * the trimmed spec was used as is; return it as the new
1158              * printer specifier
1159              */
1160             *new_printer_spec = trimmed_spec;
1161         }
1162         else
1163         {
1164             /*
1165              * a modified version of the trimmed spec was used
1166              */
1167             XtFree(trimmed_spec);
1168         }
1169         XtFree(default_printer);
1170     }
1171     else if(default_printer != (String)NULL)
1172     {
1173         /*
1174          * check to see if the default printer was used without
1175          * modification
1176          */
1177         if(*new_printer_spec == (String)NULL)
1178         {
1179             /*
1180              * the default printer was used as is; return it as the new
1181              * printer specifier
1182              */
1183             *new_printer_spec = default_printer;
1184         }
1185         else
1186         {
1187             /*
1188              * a modified version of the default printer was used
1189              */
1190             XtFree(default_printer);
1191         }
1192     }
1193     /*
1194      * clean up and return
1195      */
1196     XtFree(printer_name);
1197     XtFree(display_spec);
1198     return status;
1199 }