1 /* $XConsortium: PsubUtil.c /main/8 1996/10/31 02:09:44 cde-hp $ */
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.
15 * ------------------------------------------------------------------------
21 #include <Dt/DtNlUtils.h>
23 #include <Dt/PsubUtilI.h>
26 * ------------------------------------------------------------------------
27 * Constant Definitions
31 * separator between printer name and display in an X Printer Specifier
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 "."
39 * ------------------------------------------------------------------------
40 * Static Function Declarations
43 static String* BuildStringList(
46 static XtEnum OpenXPrinterOnDisplay(
49 Display** new_display,
50 char** ct_printer_name);
51 static int SpanNonWhitespace(
53 static int SpanWhitespace(
55 static int StringToCompoundText(
59 static Boolean TrimWhitespace(
63 * ------------------------------------------------------------------------
64 * Name: BuildStringList
68 * Build a newly allocated array of Strings by recursively parsing
69 * whitespace delimited items out of the passed list String.
73 * The array of strings. It is the caller's responsibility to free
74 * the returned list by calling _DtPrintFreeStringList.
78 BuildStringList(String list_string, int i)
82 * parse the next item out of the string list
84 if(list_string != (String)NULL)
87 list_string += SpanWhitespace(list_string);
88 length = SpanNonWhitespace(list_string);
91 string = XtCalloc(length+1, sizeof(char));
92 strncpy(string, list_string, length);
93 list_string += length;
96 if(string == (String)NULL)
99 * end of string list; allocate the array
101 return (String*)XtCalloc(i+1, sizeof(String));
108 String* string_list = BuildStringList(list_string, i+1);
110 * set the string in the list and return
112 string_list[i] = string;
118 * ------------------------------------------------------------------------
119 * Name: OpenXPrinterOnDisplay
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.
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'
138 * The 'ct_printer_name' return is provided as a convenience for the
139 * caller to subsequently pass to XpInitContext().
144 * An X printer connection was successfully opened.
147 * The display does not manage the indicated printer.
149 * DtPRINT_NOT_XP_DISPLAY
150 * The display does not support printing.
152 * DtPRINT_INVALID_DISPLAY
153 * The display could not be opened.
157 OpenXPrinterOnDisplay(
160 Display** new_display,
161 char** ct_printer_name)
163 Display* print_display;
164 XPPrinterList printer_list;
169 *ct_printer_name = (char*)NULL;
171 * open the print display
173 print_display = XOpenDisplay(display_spec);
174 if(print_display != (Display*)NULL)
176 if(XpQueryExtension(print_display, &event_base, &error_base))
179 * validate the printer
181 StringToCompoundText(print_display,
184 if((char*)NULL == *ct_printer_name)
187 XpGetPrinterList(print_display,
194 XpGetPrinterList(print_display,
198 if(printer_list == (XPPrinterList)NULL)
200 XCloseDisplay(print_display);
202 XFree(*ct_printer_name);
203 return DtPRINT_NO_PRINTER;
207 *new_display = print_display;
208 XpFreePrinterList(printer_list);
209 return DtPRINT_SUCCESS;
214 XCloseDisplay(print_display);
215 return DtPRINT_NOT_XP_DISPLAY;
220 return DtPRINT_INVALID_DISPLAY;
225 * ------------------------------------------------------------------------
226 * Name: SpanNonWhitespace
230 * Returns the length of the initial segment of the passed string
231 * that consists entirely of non-whitspace characters.
236 SpanNonWhitespace(char* string)
240 *ptr != '\0' && !DtIsspace(ptr);
241 ptr = DtNextChar(ptr));
246 * ------------------------------------------------------------------------
247 * Name: SpanWhitespace
251 * Returns the length of the initial segment of the passed string
252 * that consists entirely of whitespace characters.
257 SpanWhitespace(char* string)
261 *ptr != '\0' && DtIsspace(ptr);
262 ptr = DtNextChar(ptr));
267 * ------------------------------------------------------------------------
268 * Name: StringToCompoundText
272 * Converts a string to compund text for use with Xp.
276 * The value returned from XmbTextListToTextProperty; e.g. Success if
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().
285 StringToCompoundText(
287 char** compound_text,
290 XTextProperty text_prop;
293 status = XmbTextListToTextProperty(display,
297 if(Success == status)
298 *compound_text = (char*)text_prop.value;
300 *compound_text = (char*)NULL;
306 * ------------------------------------------------------------------------
307 * Name: TrimWhitespace
311 * Remove leading and trailing whitespace from the passed string.
312 * This function is multi-byte safe.
316 * True if the string was modified; False otherwise.
320 TrimWhitespace(String string)
325 Boolean modified = False;
327 if((String)NULL == string)
330 * find the first non-whitespace character
332 for(ptr = string; *ptr != '\0' && DtIsspace(ptr); ptr = DtNextChar(ptr));
334 * reposition the string
339 for(i = 0; ptr[i] != '\0'; i++)
344 * find the last non-whitespace character
346 for(ptr = string, last_non_ws = NULL; *ptr != '\0'; ptr = DtNextChar(ptr))
350 * trim any trailing whitespace
352 if((String)NULL != last_non_ws)
354 ptr = DtNextChar(last_non_ws);
365 * ------------------------------------------------------------------------
366 * Name: _DtPrintCreateXPrinterSpecifier
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.
375 * Separators are inserted as follows:
377 * * if both the 'printer_name' and 'host_name' are specified, a
378 * "@" will be inserted between them.
380 * * if the 'display_num' is specified:
382 * - if 'spec_net' is TCP_IPC or UNSPECIFIED, a ":" will be
383 * inserted immediately preceding it.
385 * - if 'spec_net' is DEC_NET, a "::" will be inserted
386 * immediately preceding it.
388 * * if the 'screen_num' is specified, a "." will be inserted
389 * immediately preceding it.
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
399 _DtPrintCreateXPrinterSpecifier(
402 DtPrintSpecNet spec_net,
406 String printer_specifier;
407 char display_string[32];
408 char screen_string[32];
409 int printer_name_len;
411 int specifier_len = 0;
414 * printer name length
416 if(printer_name != (String)NULL)
417 specifier_len += printer_name_len = strlen(printer_name);
419 printer_name_len = 0;
423 if(host_name != (String)NULL)
424 specifier_len += host_name_len = strlen(host_name);
428 * printer name / display separator length
430 if(printer_name_len != 0 && (host_name_len != 0 || display_num != -1))
432 separator = XPSPEC_NAME_DISP_SEP;
433 specifier_len += XPSPEC_NAME_DISP_SEP_LEN;
436 separator = (String)NULL;
440 if(display_num == -1)
441 display_string[0] = '\0';
444 sprintf(display_string, "%s%d",
445 spec_net == DtPRINT_DEC_NET ? "::" : ":",
451 screen_string[0] = '\0';
454 sprintf(screen_string, "%s%d", XPSPEC_DISP_SCREEN_SEP, screen_num);
456 * create and return the new printer specifier
458 printer_specifier = XtMalloc(specifier_len + 1);
459 sprintf(printer_specifier,
461 printer_name ? printer_name : "",
462 separator ? separator : "",
463 host_name ? host_name : "",
466 return printer_specifier;
470 * ------------------------------------------------------------------------
471 * Name: _DtPrintFreeStringList
475 * Frees a string list created by BuildStringList.
483 _DtPrintFreeStringList(
489 for(i = 0; server_list[i] != (String)NULL; i++)
491 XtFree(server_list[i]);
493 XtFree((char*)server_list);
498 * ------------------------------------------------------------------------
499 * Name: _DtPrintGetDefaultXPrinterName
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.
512 _DtPrintGetDefaultXPrinterName(
515 String default_printer;
517 if((Widget)NULL == w)
519 default_printer = (String)NULL;
523 XtResource res_struct;
525 * initialize the resource structure
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;
535 * pick up the printer list application resource value for the
538 XtGetApplicationResources(w, (XtPointer)&default_printer,
539 &res_struct, 1, (ArgList)NULL, 0);
542 * if the resource is undefined, search for an appropriate
543 * environment variable
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);
551 * return a copy of the printer name
553 return XtNewString(default_printer);
557 * ------------------------------------------------------------------------
558 * Name: _DtPrintGetXpPrinterList
562 * Retrieves the short list of Printers from the XpPrinterList
563 * resource, or XPPRINTERLIST environment variable.
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.
573 _DtPrintGetXpPrinterList(
576 XtResource res_struct;
577 String xp_printer_list;
579 * initialize the resource structure
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;
589 * pick up the printer list application resource value for the passed
592 XtGetApplicationResources(w, (XtPointer)&xp_printer_list,
593 &res_struct, 1, (ArgList)NULL, 0);
595 * if the resource is undefined, use the environment variable
597 if(xp_printer_list == (String)NULL)
599 xp_printer_list = getenv("XPRINTERLIST");
602 * build the array of printer names
604 if(xp_printer_list != (String)NULL)
606 return BuildStringList(xp_printer_list, 0);
609 return (String*)NULL;
613 * ------------------------------------------------------------------------
614 * Name: _DtPrintGetXpServerList
618 * Retrieves the Xp Server list from the XpServerList resource, or if
619 * the resource is undefined, the XPSERVERLIST environment variable
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.
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.
634 _DtPrintGetXpServerList(
637 XtResource res_struct;
638 String xp_server_list;
644 * initialize the resource structure
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;
654 * pick up the server list application resource value for the passed
657 if((Widget)NULL == w)
658 xp_server_list = (String)NULL;
660 XtGetApplicationResources(w, (XtPointer)&xp_server_list,
661 &res_struct, 1, (ArgList)NULL, 0);
663 * if the resource is undefined, use the environment variable value
665 if(xp_server_list == (String)NULL)
667 xp_server_list = getenv("XPSERVERLIST");
670 * convert to a list of strings
674 XpQueryExtension(XtDisplay(w), &event_base, &error_base))
677 * the video server supports the Xp extension, add it to the front
680 server_list = BuildStringList(xp_server_list, 1);
681 server_list[0] = XtNewString(XDisplayString(XtDisplay(w)));
683 else if(xp_server_list != (String)NULL)
685 server_list = BuildStringList(xp_server_list, 0);
688 server_list = (String*)NULL;
690 * default the display number to ":0" if needed
692 for(i = 0; server_list && server_list[i]; i++)
697 * check to see if display number is specified
699 _DtPrintParseXDisplaySpecifier(server_list[i],
701 (DtPrintSpecNet*)NULL,
704 if(display_num == -1)
707 * display number not specified; default to ":0"
709 XtFree(server_list[i]);
711 _DtPrintCreateXPrinterSpecifier((String)NULL, host_name,
712 DtPRINT_TCP_IPC, 0, -1);
724 * ------------------------------------------------------------------------
725 * Name: _DtPrintParseXDisplaySpecifier
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).
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().
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.
751 _DtPrintParseXDisplaySpecifier(
752 const String display_spec,
754 DtPrintSpecNet* spec_net,
760 if(display_spec == (String)NULL)
763 * not much to do with a NULL display spec
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;
772 * find the start of the display number in the display spec
774 ptr = DtStrchr(display_spec, ':');
775 if(ptr == (char*)NULL)
778 * not found, return -1 for display and screen
780 if(spec_net) *spec_net = DtPRINT_NET_UNSPECIFIED;
781 if(display_num) *display_num = -1;
782 if(screen_num) *screen_num = -1;
784 * return the host name as a copy of the display spec
786 if(host_name) *host_name = XtNewString(display_spec);
792 * skip over the ':', determine if this is a DECnet specifier,
793 * and pick up the display num if specified
798 if(spec_net) *spec_net = DtPRINT_NET_UNSPECIFIED;
805 if(spec_net) *spec_net = DtPRINT_DEC_NET;
810 if(spec_net) *spec_net = DtPRINT_TCP_IPC;
815 num = (int)strtol(ptr, &ptr, 10);
817 if(display_num) *display_num = num;
827 * parse out the screen number
829 if(*ptr == '.' && *(ptr+1) != '\0')
832 num = (int)strtol(ptr, &ptr, 10);
833 if(screen_num) *screen_num = num;
838 * not found, return -1 for screen
847 * allocate a new string containing just the host name
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';
859 * ------------------------------------------------------------------------
860 * Name: _DtPrintParseXPrinterSpecifier
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().
870 * The printer_name or display_spec parameters may be passed as NULL
871 * if that component of the specifier is not desired.
873 * If the printer specifier is NULL, the locations pointed to
874 * by the printer_name and display_spec will be set to NULL.
876 * If either portion of the specifier is missing, a newly allocated
877 * empty string will be returned to printer_name or display_spec.
885 _DtPrintParseXPrinterSpecifier(
886 const String specifier,
887 String* printer_name,
888 String* display_spec)
890 if(specifier == (String)NULL)
892 if(printer_name) *printer_name = (String)NULL;
893 if(display_spec) *display_spec = (String)NULL;
899 * determine the offset of the printer name / display name delimiter
900 * ('@') within the X Printer Specifier
902 delim_ptr = DtStrchr(specifier, XPSPEC_NAME_DISP_SEP_CHAR);
903 if(delim_ptr == (String)NULL)
906 * no delimiter found; specifier consists of printer name only
908 if(printer_name) *printer_name = XtNewString(specifier);
909 if(display_spec) *display_spec = XtNewString("");
914 * copy the printer name portion from the specifier
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';
924 * copy the display name portion from the specifier
926 if(display_spec) *display_spec = XtNewString(delim_ptr+1);
932 * ------------------------------------------------------------------------
933 * Name: _DtPrintVerifyXPrinter
937 * Determines if an X printer specifier is valid by establishing a
938 * connection to the printer, up to and including initializing a
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.
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.
961 * An X printer connection was successfully opened.
963 * DtPRINT_PRINTER_MISSING
964 * The passed or default printer spec does not include a printer
968 * The passed printer spec was NULL, and no default printer could
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
977 * The display indicated in the passed printer spec or default
978 * does not manage the indicated printer.
980 * DtPRINT_NOT_XP_DISPLAY
981 * The display indicated in the passed printer spec or default
982 * does not support printing.
984 * DtPRINT_INVALID_DISPLAY
985 * The display indicated in the passed printer spec or default
986 * could not be opened.
990 _DtPrintVerifyXPrinter(
993 String* new_printer_spec,
994 Display** new_display,
995 XPContext* new_context)
997 String default_printer;
1001 Display* print_display;
1002 char* ct_printer_name;
1003 String trimmed_spec;
1005 * initialize the printer spec return parm
1007 *new_printer_spec = (String)NULL;
1009 * determine a default printer if the passed printer spec is NULL
1011 if(printer_spec == (String)NULL)
1013 default_printer = _DtPrintGetDefaultXPrinterName(w);
1014 if(default_printer == (String)NULL)
1015 return DtPRINT_NO_DEFAULT;
1017 printer_spec = default_printer;
1020 default_printer = (String)NULL;
1022 * trim whitespace from the printer spec if needed
1024 trimmed_spec = XtNewString(printer_spec);
1025 if(TrimWhitespace(trimmed_spec))
1027 printer_spec = trimmed_spec;
1031 XtFree(trimmed_spec);
1032 trimmed_spec = (String)NULL;
1035 * break the printer specifier into its printer name and display
1036 * specifier components
1038 _DtPrintParseXPrinterSpecifier(printer_spec,
1041 if(*printer_name == '\0')
1044 * printer name is missing
1046 status = DtPRINT_PRINTER_MISSING;
1051 * if the display spec is empty, search the server list for a
1054 if(*display_spec == '\0')
1056 String* server_list;
1059 * find a server in the server list that manages the printer
1061 status = DtPRINT_NO_DEFAULT_DISPLAY;
1062 if((server_list = _DtPrintGetXpServerList(w)) != (String*)NULL)
1064 for(i = 0; server_list[i] != (String)NULL; i++)
1066 if(OpenXPrinterOnDisplay(printer_name,
1072 status = DtPRINT_SUCCESS;
1074 _DtPrintCreateXPrinterSpecifier(
1077 DtPRINT_NET_UNSPECIFIED,
1082 _DtPrintFreeStringList(server_list);
1090 * check to see if display number is specified
1092 _DtPrintParseXDisplaySpecifier(display_spec,
1094 (DtPrintSpecNet*)NULL,
1097 if(display_num == -1)
1099 String new_display_spec;
1101 * display number not specified; default to ":0"
1104 _DtPrintCreateXPrinterSpecifier((String)NULL, host_name,
1105 DtPRINT_TCP_IPC, 0, -1);
1107 * create new printer name for return, even if
1108 * OpenXPrinterOnDisplay is unsuccessful
1111 _DtPrintCreateXPrinterSpecifier(printer_name,
1113 DtPRINT_NET_UNSPECIFIED,
1116 * use the new display spec
1118 XtFree(display_spec);
1119 display_spec = new_display_spec;
1123 * open the print display
1125 status = OpenXPrinterOnDisplay(printer_name,
1131 if(status == DtPRINT_SUCCESS)
1134 * initialize the print context
1136 if((char*)NULL != ct_printer_name)
1138 *new_context = XpCreateContext(print_display, ct_printer_name);
1139 XFree(ct_printer_name);
1142 *new_context = XpCreateContext(print_display, printer_name);
1143 XpSetContext(print_display, *new_context);
1145 * update the display return parm
1147 *new_display = print_display;
1150 * check to see if the trimmed spec was used
1152 if(trimmed_spec != (String)NULL)
1154 if(*new_printer_spec == (String)NULL)
1157 * the trimmed spec was used as is; return it as the new
1160 *new_printer_spec = trimmed_spec;
1165 * a modified version of the trimmed spec was used
1167 XtFree(trimmed_spec);
1169 XtFree(default_printer);
1171 else if(default_printer != (String)NULL)
1174 * check to see if the default printer was used without
1177 if(*new_printer_spec == (String)NULL)
1180 * the default printer was used as is; return it as the new
1183 *new_printer_spec = default_printer;
1188 * a modified version of the default printer was used
1190 XtFree(default_printer);
1194 * clean up and return
1196 XtFree(printer_name);
1197 XtFree(display_spec);