1 #ifndef lint
2 static char *rcsid = "$Id: IMProto.c,v 1.20 1999/04/12 08:52:23 ishisone Exp $";
3 #endif
4 /*-
5  * Copyright (c) 1991, 1994  Software Research Associates, Inc.
6  *
7  * Permission to use, copy, modify, and distribute this software and its
8  * documentation for any purpose and without fee is hereby granted, provided
9  * that the above copyright notice appear in all copies and that both that
10  * copyright notice and this permission notice appear in supporting
11  * documentation, and that the name of Software Research Associates not be
12  * used in advertising or publicity pertaining to distribution of the
13  * software without specific, written prior permission.  Software Research
14  * Associates makes no representations about the suitability of this software
15  * for any purpose.  It is provided "as is" without express or implied
16  * warranty.
17  *
18  * Author:  Makoto Ishisone, Software Research Associates, Inc., Japan
19  */
20 
21 /*
22  * X Input Method Protocol handler is considered to be still in
23  * beta testing phase.  Current version has the following
24  * restrictions.
25  *	- it does not support on-demand-synchronous method.
26  *	- it does not support front-end model.
27  *	- it does not SetIMValues operation.
28  *	- it supports only X, local and TCP transports.
29  * Also, there might be various bugs.
30  */
31 
32 #define DEBUG_VAR debug_IMProtocol
33 
34 #include <ctype.h>
35 #include <X11/Xos.h>
36 #include <sys/stat.h>
37 #include <X11/IntrinsicP.h>
38 #include <X11/StringDefs.h>
39 #include <X11/Xatom.h>
40 #include <X11/Xmu/SysUtil.h>
41 #include "IMProtoP.h"
42 #include "ParseKey.h"
43 #include "InputConv.h"
44 #include "im.h"
45 
46 
47 #define SERVER_NAME		"kinput2"
48 #define UNIX_SOCKET_DIR		"/tmp/.ki2-unix"
49 
50 /*- resource table -*/
51 static XtResource resources[] = {
52 #define offset(field) XtOffset(IMProtocolWidget, imp.field)
53     { XtNserverName, XtCServerName, XtRString, sizeof(String),
54 	offset(server_name), XtRString, (XtPointer)SERVER_NAME },
55     { XtNlanguage, XtCLanguage, XtRString, sizeof(String),
56 	offset(language), XtRImmediate, (XtPointer)NULL },
57     { XtNlocales, XtCLocales, XtRString, sizeof(String),
58 	offset(locales), XtRImmediate, (XtPointer)NULL },
59     { XtNinputObjectClass, XtCClass, XtRPointer, sizeof(WidgetClass),
60 	offset(input_object_class), XtRImmediate, (XtPointer)NULL },
61     { XtNdisplayObjectClass, XtCClass, XtRPointer, sizeof(WidgetClass),
62 	offset(display_object_class), XtRImmediate, (XtPointer)NULL },
63     { XtNdefaultFontList, XtCFontList, XtRString, sizeof(String),
64 	offset(default_fontlist), XtRImmediate, (XtPointer)NULL },
65     { XtNconversionStartKeys, XtCConversionStartKeys, XtRString, sizeof(String),
66 	offset(conversion_start_keys), XtRImmediate, (XtPointer)NULL },
67     { XtNforeground, XtCForeground, XtRPixel, sizeof (Pixel),
68 	offset(foreground), XtRString, XtDefaultForeground },
69     { XtNstatusWidth, XtCStatusWidth, XtRDimension, sizeof(Dimension),
70 	offset(status_width), XtRImmediate, (XtPointer)0 },
71     { XtNtransports, XtCTransports, XtRString, sizeof(String),
72 	offset(transport_list), XtRString, (XtPointer)"tcp,unix,x" },
73 #undef offset
74 };
75 
76 static void Initialize _Pt_((Widget req, Widget new,
77 			     ArgList args, Cardinal *num_args));
78 static void Destroy _Pt_((Widget w));
79 static void Realize _Pt_((Widget w, XtValueMask *mask,
80 			  XSetWindowAttributes *value));
81 
82 /*- IMProtocolClassRec -*/
83 IMProtocolClassRec imProtocolClassRec = {
84   { /* core fields */
85     /* superclass		*/	(WidgetClass) &widgetClassRec,
86     /* class_name		*/	"IMProtocol",
87     /* widget_size		*/	sizeof(IMProtocolRec),
88     /* class_initialize		*/	NULL,
89     /* class_part_initialize	*/	NULL,
90     /* class_inited		*/	FALSE,
91     /* initialize		*/	Initialize,
92     /* initialize_hook		*/	NULL,
93     /* realize			*/	Realize,
94     /* actions			*/	NULL,
95     /* num_actions		*/	0,
96     /* resources		*/	resources,
97     /* num_resources		*/	XtNumber(resources),
98     /* xrm_class		*/	NULLQUARK,
99     /* compress_motion		*/	TRUE,
100     /* compress_exposure	*/	TRUE,
101     /* compress_enterleave	*/	TRUE,
102     /* visible_interest		*/	FALSE,
103     /* destroy			*/	Destroy,
104     /* resize			*/	NULL,
105     /* expose			*/	NULL,
106     /* set_values		*/	NULL,
107     /* set_values_hook		*/	NULL,
108     /* set_values_almost	*/	XtInheritSetValuesAlmost,
109     /* get_values_hook		*/	NULL,
110     /* accept_focus		*/	NULL,
111     /* version			*/	XtVersion,
112     /* callback_private		*/	NULL,
113     /* tm_table			*/	NULL,
114     /* query_geometry		*/	XtInheritQueryGeometry,
115     /* display_accelerator	*/	XtInheritDisplayAccelerator,
116     /* extension		*/	NULL
117   },
118   { /* imProtocol fields */
119     /* dummy			*/	0
120   }
121 };
122 
123 WidgetClass imProtocolWidgetClass = (WidgetClass)&imProtocolClassRec;
124 
125 static void getAtoms _Pt_((IMProtocolWidget ipw));
126 static void setProperty _Pt_((IMProtocolWidget ipw));
127 static int ownSelection _Pt_((IMProtocolWidget ipw));
128 static Boolean convertSelection _Pt_((Widget w, Atom *selectionp,
129 				      Atom *targetp, Atom *typep,
130 				      XtPointer *valuep,
131 				      unsigned long *lengthp, int *formatp));
132 static void loseSelection _Pt_((Widget w, Atom *selectionp));
133 #ifdef IM_TCP_TRANSPORT
134 static void acceptTCPService _Pt_((XtPointer client_data,
135 				  int *sourcep, XtInputId *idp));
136 #endif
137 #ifdef IM_UNIX_TRANSPORT
138 static void acceptUnixService _Pt_((XtPointer client_data,
139 				    int *sourcep, XtInputId *idp));
140 #endif
141 #ifdef IM_X_TRANSPORT
142 static void acceptXService _Pt_((Widget w, XtPointer client_data,
143 				 XEvent *event, Boolean *continuep));
144 #endif
145 static void initializeError _Pt_((Widget w, String resname));
146 static char *compactList _Pt_((char *s));
147 static void setTransport _Pt_((Widget w));
148 static int makeConverter _Pt_((Widget w));
149 static void getTriggerKeys _Pt_((Widget w));
150 static void ioeCallback _Pt_((XPointer cldata));
151 
152 
153 /*
154  *+ Core class methods
155  */
156 
157 /*- Initialize: intern Atoms, get default fonts, etc. -*/
158 /* ARGSUSED */
159 static void
Initialize(req,new,args,num_args)160 Initialize(req, new, args, num_args)
161 Widget req;
162 Widget new;
163 ArgList args;
164 Cardinal *num_args;
165 {
166     IMProtocolWidget ipw = (IMProtocolWidget)new;
167 
168     TRACE(("IMProtocolWidget:Initialize()\n"));
169 
170     /*
171      * Check resources which must be specified at the initialization.
172      */
173 #define NULLorEMPTY(p)	((p) == NULL || (p)[0] == '\0')
174     if (NULLorEMPTY(ipw->imp.server_name)) {
175 	initializeError(new, XtNserverName);
176     }
177 
178     ipw->imp.server_name = XtNewString(ipw->imp.server_name);
179 
180     if (NULLorEMPTY(ipw->imp.language)) {
181 	initializeError(new, XtNlanguage);
182     } else if (NULLorEMPTY(ipw->imp.locales)) {
183 	initializeError(new, XtNlocales);
184     } else if (ipw->imp.input_object_class == NULL) {
185 	initializeError(new, XtNinputObjectClass);
186     } else if (ipw->imp.display_object_class == NULL) {
187 	initializeError(new, XtNdisplayObjectClass);
188     }
189     ipw->imp.locales = compactList(XtNewString(ipw->imp.locales));
190 #undef NULLorEMPTY
191 
192     /*
193      * Initialize converter info.
194      */
195     if (makeConverter(new) < 0) {
196 	/*
197 	 * locales is empty.
198 	 */
199 	String params[1];
200 	Cardinal num_params;
201 
202 	params[0] = XtClass(new)->core_class.class_name;
203 	num_params = 1;
204 	XtAppErrorMsg(XtWidgetToApplicationContext(new),
205 		      "initializeError", "invalidValue", "WidgetError",
206 		      "%s: locale list is empty",
207 		      params, &num_params);
208     }
209 
210     /*
211      * Create font bank (a bank of cached fonts) and enter
212      * default fonts.
213      */
214     ipw->imp.font_bank = FontBankCreate(XtDisplay(new), ipw->imp.language);
215     if (ipw->imp.font_bank == NULL) {
216 	/*
217 	 * The specified language is not supported.
218 	 */
219 	String params[2];
220 	Cardinal num_params;
221 
222 	params[0] = XtClass(new)->core_class.class_name;
223 	params[1] = ipw->imp.language;
224 	num_params = 2;
225 	XtAppErrorMsg(XtWidgetToApplicationContext(new),
226 		      "initializeError", "invalidValue", "WidgetError",
227 		      "%s: language %s not supported",
228 		      params, &num_params);
229     }
230 
231     if (ipw->imp.default_fontlist != NULL) {
232 	ipw->imp.default_fontlist = XtNewString(ipw->imp.default_fontlist);
233 
234 	DDPRINT(2, ("cache default fonts: %s\n", ipw->imp.default_fontlist));
235 	ipw->imp.default_fonts = FontBankGet(ipw->imp.font_bank,
236 					     ipw->imp.default_fontlist,
237 					     &ipw->imp.num_default_fonts);
238     } else {
239 	ipw->imp.default_fonts = NULL;
240 	ipw->imp.num_default_fonts = 0;
241     }
242 
243     /*
244      * Initialize private data.
245      */
246     ipw->imp.connection_list = NULL;
247     ipw->imp.no_more_connections = False;
248     ipw->imp.scheduler_queue = NULL;
249     setTransport(new);
250     getTriggerKeys(new);
251     IMInitHash(new);
252     getAtoms(ipw);
253 
254     /*
255      * Initialilze transport layer.
256      */
257     /* 1. TCP/IP */
258     ipw->imp.tcp_sock = -1;
259 #ifdef IM_TCP_TRANSPORT
260     if (ipw->imp.use_tcp_transport) {
261 	ipw->imp.tcp_port = 0;	/* let the system choose the port number */
262 	ipw->imp.tcp_sock = IMCreateTCPService(&ipw->imp.tcp_port, "localhost");
263     }
264     if (ipw->imp.tcp_sock >= 0) {
265 	TRACE(("call XtAppAddInput for tcp socket(%d)\n", ipw->imp.tcp_sock));
266 	ipw->imp.tcp_id = XtAppAddInput(XtWidgetToApplicationContext(new),
267 					ipw->imp.tcp_sock,
268 					(XtPointer)XtInputReadMask,
269 					acceptTCPService, (XtPointer)ipw);
270     }
271 #endif /* IM_TCP_TRANSPORT */
272 
273     /* 2. UNIX domain */
274     ipw->imp.unix_sock = -1;
275 #ifdef IM_UNIX_TRANSPORT
276     if (ipw->imp.use_unix_transport) {
277 	char path[1024];
278 	char *p;
279 
280 	/*
281 	 * The unix domain socket pathname has the following form:
282 	 *   <UNIX_SOCKET_DIR>/<Display Name>-<Language>
283 	 */
284 	(void)mkdir(UNIX_SOCKET_DIR, 01777);
285 #ifdef S_IFLNK
286 	{
287 	    /*
288 	     * This system has symbolic links.  Make sure UNIX_SOCKET_DIR
289 	     * is not a symbolic link but a directory before calling
290 	     * chmod().
291 	     */
292 	    struct stat st;
293 	    if (lstat(UNIX_SOCKET_DIR, &st) == 0 &&
294 		(st.st_mode & S_IFMT) == S_IFDIR) {
295 		(void)chmod(UNIX_SOCKET_DIR, 01777);
296 	    }
297 	}
298 #else
299 	(void)chmod(UNIX_SOCKET_DIR, 01777);
300 #endif
301 	(void)sprintf(path, "%s/%s", UNIX_SOCKET_DIR,
302 		      DisplayString(XtDisplay(new)));
303 	/*
304 	 * Omit screen number and the preceding period.
305 	 */
306 	for (p = path + strlen(path) - 1; p > path && *p != ':'; p--) {
307 	    if (*p == '.') {
308 		*p = '\0';
309 		break;
310 	    }
311 	}
312 	/*
313 	 * Append language part.
314 	 */
315 	(void)strcat(path, "-");
316 	(void)strcat(path, ipw->imp.language);
317 	/*
318 	 * Change every ':' in the path name to '_', since ':' is not
319 	 * included in POSIX Portable Filename Character Set.
320 	 */
321 	for (p = path; *p != '\0'; p++) {
322 	    if (*p == ':') *p = '_';
323 	}
324 	ipw->imp.unix_path = XtNewString(path);
325 	ipw->imp.unix_sock = IMCreateUnixService(ipw->imp.unix_path);
326     }
327     if (ipw->imp.unix_sock >= 0) {
328 	TRACE(("call XtAppAddInput for unix socket(%d)\n", ipw->imp.unix_sock));
329 	ipw->imp.unix_id = XtAppAddInput(XtWidgetToApplicationContext(new),
330 					 ipw->imp.unix_sock,
331 					 (XtPointer)XtInputReadMask,
332 					 acceptUnixService, (XtPointer)ipw);
333 	ipw->imp.ioe_handle = XIOESet(ioeCallback, (XPointer)ipw);
334     }
335 #endif /* IM_UNIX_TRANSPORT */
336 
337 #ifdef IM_X_TRANSPORT
338     if (ipw->imp.use_x_transport) {
339 	TRACE(("call XtAddEventHandler for X transport\n"));
340 	XtAddEventHandler(new, NoEventMask, True, acceptXService,
341 			  (XtPointer)NULL);
342     }
343 #endif /* IM_X_TRANSPORT */
344 
345     /*
346      * Compile request dispatching table.
347      */
348     IMCompileReq();
349 }
350 
351 /*- Destroy: free allocated memory -*/
352 static void
Destroy(w)353 Destroy(w)
354 Widget w;
355 {
356     IMProtocolWidget ipw = (IMProtocolWidget)w;
357     IMConnection *conn;
358     int i;
359 
360     TRACE(("IMProtocolWidget:Destroy()\n"));
361 
362     XtFree(ipw->imp.server_name);
363     XtFree(ipw->imp.locales);
364     if (ipw->imp.default_fontlist != NULL) XtFree(ipw->imp.default_fontlist);
365     if (ipw->imp.trigger_keys != NULL) XtFree((char *)ipw->imp.trigger_keys);
366 
367     for (i = 0; i < ipw->imp.converter.num_locales; i++) {
368 	XtFree(ipw->imp.converter.supported_locales[i]);
369     }
370     XtFree((char *)ipw->imp.converter.supported_locales);
371 
372     /*
373      * Close down all connections.
374      */
375     conn = ipw->imp.connection_list;
376     while (conn != NULL) {
377 	IMConnection *next = conn->next;
378 
379 	IMCloseConnection(conn);
380 	conn = next;
381     }
382 
383     /*
384      * Close down TCP/Unix service sockets.
385      */
386     if (ipw->imp.tcp_sock >= 0) {
387 	XtRemoveInput(ipw->imp.tcp_id);
388 	(void)close(ipw->imp.tcp_sock);
389     }
390     if (ipw->imp.unix_sock >= 0) {
391 	XIOEUnset(ipw->imp.ioe_handle);
392 	(void)unlink(ipw->imp.unix_path);
393 	XtRemoveInput(ipw->imp.unix_id);
394 	(void)close(ipw->imp.unix_sock);
395 	XtFree(ipw->imp.unix_path);
396     }
397 
398     /*
399      * Unload default fonts.
400      */
401     if (ipw->imp.num_default_fonts > 0) {
402 	FontBankFreeFonts(ipw->imp.font_bank,
403 			  ipw->imp.default_fonts,
404 			  ipw->imp.num_default_fonts);
405     }
406 
407     /*
408      * Free font bank.
409      */
410     FontBankDestroy(ipw->imp.font_bank);
411 }
412 
413 /*- Realize: own selection -*/
414 static void
Realize(w,mask,value)415 Realize(w, mask, value)
416 Widget w;
417 XtValueMask *mask;
418 XSetWindowAttributes *value;
419 {
420     IMProtocolWidget ipw = (IMProtocolWidget)w;
421     CoreWidgetClass super = (CoreWidgetClass)XtClass(w)->core_class.superclass;
422 
423     TRACE(("IMProtocolWidget:Realize()\n"));
424 
425     (*super->core_class.realize)(w, mask, value);
426 
427     if (ownSelection(ipw) < 0) {
428 	String params[1];
429 	Cardinal num_params;
430 
431 	params[0] = XtClass(w)->core_class.class_name;
432 	num_params = 1;
433 	XtAppWarningMsg(XtWidgetToApplicationContext(w),
434 			"selectionError", "ownSelection", "WidgetError",
435 			"%s: can't own selection", params, &num_params);
436 
437 	XtDestroyWidget(w);
438     }
439 
440     setProperty(ipw);
441 }
442 
443 /*
444  *+ Atom, property and selection handling
445  */
446 
447 /*- getAtoms: intern atoms -*/
448 static void
getAtoms(ipw)449 getAtoms(ipw)
450 IMProtocolWidget ipw;
451 {
452     Display *dpy = XtDisplay((Widget)ipw);
453     char buf[256];
454 
455     TRACE(("IMProtocolWidget:getAtoms()\n"));
456 
457     (void)strcpy(buf, "@server=");
458     (void)strcat(buf, ipw->imp.server_name);
459 #define MAKEATOM(s)	XInternAtom(dpy, s, False)
460     ipw->imp.server_atom = MAKEATOM(buf);
461     ipw->imp.ctext_atom = MAKEATOM("COMPOUND_TEXT");
462     ipw->imp.locales_atom = MAKEATOM("LOCALES");
463     ipw->imp.transport_atom = MAKEATOM("TRANSPORT");
464     ipw->imp.ki2comm_atom = MAKEATOM("_KINPUT2_COMM");
465     ipw->imp.xim_xconnect = MAKEATOM("_XIM_XCONNECT");
466     ipw->imp.xim_protocol = MAKEATOM("_XIM_PROTOCOL");
467     ipw->imp.xim_moredata = MAKEATOM("_XIM_MOREDATA");
468 #undef MAKEATOM
469 }
470 
471 /*- setProperty: set XIM_SERVERS property -*/
472 static void
setProperty(ipw)473 setProperty(ipw)
474 IMProtocolWidget ipw;
475 {
476     Display *dpy = XtDisplay((Widget)ipw);
477     Atom xim_servers = XInternAtom(dpy, "XIM_SERVERS", False);
478     Atom server_atom = ipw->imp.server_atom;
479     Window root0 = RootWindow(dpy, 0);
480     int op_mode = PropModePrepend;
481     int no_registration = 0;
482     Atom type;
483     int format;
484     unsigned long nitems;
485     unsigned long bytes_after;
486     unsigned char *value;
487     unsigned long data;
488 
489     TRACE(("IMProtocolWidget:setProperty()\n"));
490 
491     /*
492      * For atomic operation, grab the server.
493      */
494 #ifndef DEBUG
495     XGrabServer(dpy);
496 #endif
497 
498     /*
499      * First, check the XIM_SERVERS property's existance.
500      * If it exists, examine the contents.
501      */
502     if (XGetWindowProperty(dpy, root0, xim_servers, 0L, 1024L, False,
503 			   AnyPropertyType, &type, &format, &nitems,
504 			   &bytes_after, &value) == Success) {
505 	if (type != XA_ATOM || format != 32) {
506 	    /*
507 	     * The contents of the property is invalid.
508 	     */
509 	    DDPRINT(2, ("XIM_SERVERS is corrupted (type=%ld, format=%d)\n",
510 		       type, format));
511 	    op_mode = PropModeReplace;
512 	} else {
513 	    int i;
514 	    unsigned long *atoms = (unsigned long *)value;
515 
516 	    for (i = 0; i < nitems; i++) {
517 		if (atoms[i] == server_atom) {
518 		    /*
519 		     * Already registered.
520 		     */
521 		    TRACE(("server is already registered in XIM_SERVERS\n"));
522 		    no_registration = 1;
523 		    break;
524 		}
525 	    }
526 	}
527 	if (value != NULL) XFree((char *)value);
528     }
529 
530     if (!no_registration) {
531 	TRACE(("changing XIM_SERVERS property\n"));
532 	data = ipw->imp.server_atom;
533 	XChangeProperty(dpy, root0, xim_servers, XA_ATOM, 32, op_mode,
534 			(unsigned char *)&data, 1);
535     } else {
536 	TRACE(("touching XIM_SERVERS property to generate PropertyNotify\n"));
537 	XChangeProperty(dpy, root0, xim_servers, XA_ATOM, 32, PropModeAppend,
538 			(unsigned char *)&data, 0);
539     }
540 
541 #ifndef DEBUG
542     XUngrabServer(dpy);
543 #endif
544 }
545 
546 /*- ownSelection: own conversion selection -*/
547 static int
ownSelection(ipw)548 ownSelection(ipw)
549 IMProtocolWidget ipw;
550 {
551     Display *dpy = XtDisplay((Widget)ipw);
552     Time time = XtLastTimestampProcessed(dpy);
553 
554 
555     TRACE(("IMProtocolWidget:ownSelection()\n"));
556 
557     if (!XtOwnSelection((Widget)ipw, ipw->imp.server_atom, time,
558 			convertSelection, loseSelection,
559 			(XtSelectionDoneProc)NULL)) {
560 	DPRINT(("cannot own selection"));
561 	return -1;
562     }
563     DPRINT(("selection atom:%ld owner: %08lx (%ld)\n", ipw->imp.server_atom,
564 	    XtWindow((Widget)ipw), XtWindow((Widget)ipw)));
565     return 0;
566 }
567 
568 /*- convertSelection: convert selections -*/
569 /* ARGSUSED */
570 static Boolean
convertSelection(w,selectionp,targetp,typep,valuep,lengthp,formatp)571 convertSelection(w, selectionp, targetp, typep, valuep, lengthp, formatp)
572 Widget w;
573 Atom *selectionp;
574 Atom *targetp;
575 Atom *typep;
576 XtPointer *valuep;
577 unsigned long *lengthp;
578 int *formatp;
579 {
580     Display *dpy = XtDisplay(w);
581     IMProtocolWidget ipw = (IMProtocolWidget)w;
582 
583     TRACE(("IMProtocolWidget:convertSelection()\n"));
584 
585     if (*targetp == XInternAtom(dpy, "TARGETS", False)) {
586 	Atom *targets;
587 
588 	TRACE(("target is \"TARGETS\"\n"));
589 	targets = (Atom *)XtMalloc(sizeof(Atom) * 2);
590 	targets[0] = ATOM_LOCALES(w);
591 	targets[1] = ATOM_TRANSPORT(w);
592 
593 	*typep = XA_ATOM;
594 	*valuep = (XtPointer)targets;
595 	*lengthp = 2;
596 	*formatp = 32;
597 	return True;
598     } else if (*targetp == ATOM_LOCALES(w)) {
599 	char buf[1024];
600 
601 	TRACE(("target is \"LOCALES\"\n"));
602 	(void)strcpy(buf, "@locale=");
603 	(void)strcat(buf, ipw->imp.locales);
604 	TRACE(("\ttype: STRING, value: %s\n", buf));
605 	/*
606 	 * The protocol spec is unclear on the type of the
607 	 * selection value.  Since R6 sample implementation
608 	 * uses LOCALES, use it.
609 	 */
610 	*typep = *targetp;
611 	/* *typep = XA_STRING; */
612 	*valuep = (XtPointer)XtNewString(buf);
613 	*lengthp = strlen(buf);
614 	*formatp = 8;
615 	return True;
616     } else if (*targetp == ATOM_TRANSPORT(w)) {
617 	char buf[1024];
618 	char hostname[256];
619 
620 	TRACE(("target is \"TRANSPORT\"\n"));
621 
622 	XmuGetHostname(hostname, 256);
623 
624 	(void)strcpy(buf, "@transport=");
625 
626 #ifdef IM_X_TRANSPORT
627 	if (ipw->imp.use_x_transport) {
628 	    (void)strcat(buf, "X/,");
629 	}
630 #endif /* IM_X_TRANSPORT */
631 
632 #ifdef IM_TCP_TRANSPORT
633 	if (ipw->imp.use_tcp_transport) {
634 	    char t_buf[1024];
635 	    (void)sprintf(t_buf, "tcp/%s:%d,", hostname, ipw->imp.tcp_port);
636 	    (void)strcat(buf, t_buf);
637 	}
638 #endif /* IM_TCP_TRANSPORT */
639 
640 #ifdef IM_UNIX_TRANSPORT
641 	if (ipw->imp.use_unix_transport) {
642 	    char u_buf[1024];
643 	    (void)sprintf(u_buf, "local/%s:%s,", hostname, ipw->imp.unix_path);
644 	    (void)strcat(buf, u_buf);
645 	}
646 #endif /* IM_UNIX_TRANSPORT */
647 
648 	/* delete trailing comma */
649 	if (buf[strlen(buf) - 1] == ',') buf[strlen(buf) - 1] = '\0';
650 	TRACE(("\ttype: STRING, value: %s\n", buf));
651 
652 	*typep = *targetp;	/* -- see the comment on LOCALES above */
653 	*valuep = (XtPointer)XtNewString(buf);
654 	*lengthp = strlen(buf);
655 	*formatp = 8;
656 	return True;
657     } else {
658 	DDPRINT(2, ("unknown target atom (%ld)\n", *targetp));
659 	return False;
660     }
661 }
662 
663 /*- loseSelection: disable IM protocol handling -*/
664 /* ARGSUSED */
665 static void
loseSelection(w,selectionp)666 loseSelection(w, selectionp)
667 Widget w;
668 Atom *selectionp;
669 {
670     IMProtocolWidget ipw = (IMProtocolWidget)w;
671 
672     TRACE(("IMProtocolWidget:loseSelection()\n"));
673 
674     /*
675      * Someone takes over the selection.  That means
676      * another kinput2 process has been started.
677      * Let the newly process handle new clients, but
678      * as long as existing clients are remained, we have
679      * to maintain them.
680      */
681 
682     if (ipw->imp.connection_list == NULL) {
683 	/*
684 	 * There are no clients.  It is OK to destroy protocol handler.
685 	 */
686 	XtDestroyWidget(w);
687 	return;
688     }
689 
690     ipw->imp.no_more_connections = True;
691 
692     /*
693      * Close down TCP/Unix service sockets.
694      */
695     if (ipw->imp.tcp_sock >= 0) {
696 	TRACE(("\tclose tcp socket\n"));
697 	XtRemoveInput(ipw->imp.tcp_id);
698 	(void)close(ipw->imp.tcp_sock);
699 	ipw->imp.tcp_sock = -1;
700     }
701     if (ipw->imp.unix_sock >= 0) {
702 	TRACE(("\tclose unix socket\n"));
703 	XtRemoveInput(ipw->imp.unix_id);
704 	(void)close(ipw->imp.unix_sock);
705 	ipw->imp.unix_sock = -1;
706     }
707 }
708 
709 
710 /*
711  *+ Connection acceptance
712  */
713 
714 #ifdef IM_TCP_TRANSPORT
715 /*- acceptTCPService: establish connection via TCP transport -*/
716 /* ARGSUSED */
717 static void
acceptTCPService(client_data,sourcep,idp)718 acceptTCPService(client_data, sourcep, idp)
719 XtPointer client_data;
720 int *sourcep;
721 XtInputId *idp;
722 {
723     IMProtocolWidget ipw = (IMProtocolWidget)client_data;
724     IMConnection *conn;
725 
726     TRACE(("IMProtocolWidget:acceptTCPService()\n"));
727 
728     /*
729      * Accept connection request.
730      */
731     conn = IMTCPConnection((Widget)ipw, *sourcep);
732 
733     /*
734      * Set dispatcher.
735      */
736     if (conn != NULL) IMSetInitialDispatcher(conn);
737 
738     /*
739      * Enter to the connections list.
740      */
741     if (conn != NULL) IMRegisterConnection(conn);
742 }
743 #endif /* IM_TCP_TRANSPORT */
744 
745 #ifdef IM_UNIX_TRANSPORT
746 /*- acceptUnixService: establish connection via UNIX domain transport -*/
747 /* ARGSUSED */
748 static void
acceptUnixService(client_data,sourcep,idp)749 acceptUnixService(client_data, sourcep, idp)
750 XtPointer client_data;
751 int *sourcep;
752 XtInputId *idp;
753 {
754     IMProtocolWidget ipw = (IMProtocolWidget)client_data;
755     IMConnection *conn;
756 
757     TRACE(("IMProtocolWidget:acceptUnixService()\n"));
758 
759     /*
760      * Accept connection request.
761      */
762     conn = IMUnixConnection((Widget)ipw, *sourcep);
763 
764     /*
765      * Set dispatcher.
766      */
767     if (conn != NULL) IMSetInitialDispatcher(conn);
768 
769     /*
770      * Enter to the connections list.
771      */
772     if (conn != NULL) IMRegisterConnection(conn);
773 }
774 #endif /* IM_UNIX_TRANSPORT */
775 
776 #ifdef IM_X_TRANSPORT
777 /*- acceptXService: establish connection via X transport -*/
778 /* ARGSUSED */
779 static void
acceptXService(w,client_data,event,continuep)780 acceptXService(w, client_data, event, continuep)
781 Widget w;
782 XtPointer client_data;
783 XEvent *event;
784 Boolean *continuep;
785 {
786     IMConnection *conn;
787 
788     TRACE(("IMProtocolWidget:acceptXService()\n"));
789 
790     /*
791      * Check if the event is really a connection request.
792      */
793     if (event->type != ClientMessage) return;
794     conn = IMXConnection(w, event);
795 
796     /*
797      * Set dispatcher.
798      */
799     if (conn != NULL) IMSetInitialDispatcher(conn);
800 
801     /*
802      * Enter to the connections list.
803      */
804     if (conn != NULL) IMRegisterConnection(conn);
805 }
806 #endif /* IM_X_TRANSPORT */
807 
808 
809 /*
810  *+ utility functions
811  */
812 
813 /*- initializeError: display error message when resource isn't specified -*/
814 static void
initializeError(w,resname)815 initializeError(w, resname)
816 Widget w;
817 String resname;
818 {
819     String params[2];
820     Cardinal num_params;
821 
822     params[0] = XtClass(w)->core_class.class_name;
823     params[1] = resname;
824     num_params = 2;
825     XtAppErrorMsg(XtWidgetToApplicationContext(w),
826 		  "initializeError", "noResource", "WidgetError",
827 		  "%s: resource %s must be specified at widget creation",
828 		  params, &num_params);
829 }
830 
831 /*- compactList: remove unnecessary spaces in a comma-separated list -*/
832 static char *
compactList(s)833 compactList(s)
834 char *s;
835 {
836     char *src, *dst;
837     int c;
838 
839     src = dst = s;
840     for (;;) {
841 	/* skip leading space */
842 	while (isspace(*src)) src++;
843 
844 	if (*src == '\0') {
845 	    *dst = '\0';
846 	    return s;
847 	}
848 
849 	/* copy string until comma or NUL appears */
850 	while ((c = *dst++ = *src++) != ',') {
851 	    if (c == '\0') return s;
852 	}
853     }
854 }
855 
856 /*- setTransport: determine which transport to be used -*/
857 static void
setTransport(w)858 setTransport(w)
859 Widget w;
860 {
861     IMProtocolWidget ipw = (IMProtocolWidget)w;
862     char *p;
863 
864     TRACE(("IMProtocolWidget:setTransport(%s)\n", ipw->imp.transport_list));
865 
866     ipw->imp.use_tcp_transport = False;
867     ipw->imp.use_unix_transport = False;
868     ipw->imp.use_x_transport = False;
869 
870     p = ipw->imp.transport_list;
871     while (*p != '\0') {
872 	char lower[256];
873 	char *q;
874 
875 	while (isspace(*p)) p++;
876 	if (*p == '\0') break;
877 
878 	q = lower;
879 	while (*p != '\0' && *p != ',' && !isspace(*p)) {
880 	    if (isupper(*p)) {
881 		*q++ = tolower(*p);
882 	    } else {
883 		*q++ = *p;
884 	    }
885 	    p++;
886 	}
887 	*q = '\0';
888 	while (isspace(*p)) p++;
889 	if (*p == ',') p++;
890 
891 	if (!strcmp(lower, "tcp")) {
892 	    TRACE(("\tTCP transport\n"));
893 	    ipw->imp.use_tcp_transport = True;
894 	} else if (!strcmp(lower, "unix")) {
895 	    TRACE(("\tUNIX domain transport\n"));
896 	    ipw->imp.use_unix_transport = True;
897 	} else if (!strcmp(lower, "x")) {
898 	    TRACE(("\tX transport\n"));
899 	    ipw->imp.use_x_transport = True;
900 	}
901     }
902 }
903 
904 /*- makeConverter: create converter record -*/
905 static int
makeConverter(w)906 makeConverter(w)
907 Widget w;
908 {
909     IMProtocolWidget ipw = (IMProtocolWidget)w;
910     char *locales[100];
911     int num_locales;
912     int size;
913     char *p;
914 
915     TRACE(("IMProtocolWidget:makeConverter()\n"));
916 
917     ipw->imp.converter.input_object_class = ipw->imp.input_object_class;
918     ipw->imp.converter.display_object_class = ipw->imp.display_object_class;
919 
920     p = ipw->imp.locales;
921     num_locales = 0;
922     do {
923 	char buf[256];
924 	char *q = buf;
925 
926 	while (isspace(*p)) p++;
927 	if (*p == '\0') break;
928 
929 	while (*p != '\0' && *p != ',' && !isspace(*p)) *q++ = *p++;
930 	*q = '\0';
931 	TRACE(("\tsupported locale: %s\n", buf));
932 	locales[num_locales++] = XtNewString(buf);
933 	while (isspace(*p)) p++;
934 	if (*p == ',') p++;
935     } while (*p != '\0' && num_locales < 100);
936     TRACE(("\tnumber of supported locales: %d\n", num_locales));
937 
938     if (num_locales == 0) return -1;
939     ipw->imp.converter.num_locales = num_locales;
940 
941     size = sizeof(char *) * num_locales;
942     ipw->imp.converter.supported_locales = (char **)XtMalloc(size);
943     bcopy((char *)locales, (char *)ipw->imp.converter.supported_locales, size);
944     return 0;
945 }
946 
947 /*- getTriggerKeys: parse conversion trigger key specification -*/
948 static void
getTriggerKeys(w)949 getTriggerKeys(w)
950 Widget w;
951 {
952     IMProtocolWidget ipw = (IMProtocolWidget)w;
953     char *key_str;
954     IMTriggerKey keys[100];
955     int num_keys;
956     int c, n;
957     ICTriggerKey *ckeys, *ekeys;
958 
959     TRACE(("IMProtocolWidget:getTriggerKeys()\n"));
960 
961     key_str = ipw->imp.conversion_start_keys;
962     num_keys = 0;
963     TRACE(("\tstart keys: %s\n", key_str));
964 
965     if (key_str != NULL) {
966 	do {
967 	    char buf[256];
968 	    char *p = buf;
969 	    KeySym keysym;
970 	    long mods, chk_mods;
971 
972 	    while ((c = *key_str++) != '\0' && c != '\n') {
973 		*p++ = c;
974 	    }
975 	    *p = '\0';
976 	    if (ParseKeyEvent(buf, &keysym, &mods, &chk_mods)) {
977 		TRACE(("\tkeysym: %08lx, modifiers: %04lx, check: %04lx\n",
978 		       keysym, mods, chk_mods));
979 		keys[num_keys].keysym = keysym;
980 		keys[num_keys].modifiers = mods;
981 		keys[num_keys].check_modifiers = chk_mods;
982 		num_keys++;
983 	    }
984 	} while  (c != '\0' && num_keys < 100);
985     }
986 
987     n = ICGetTriggerKeysOfInputObjectClass(ipw->imp.input_object_class,
988 					   &ckeys);
989     for (ekeys = ckeys + n ;
990 	 ckeys < ekeys && num_keys < (sizeof(keys) / sizeof(IMTriggerKey)) ;
991 	 ckeys++) {
992       keys[num_keys].keysym = ckeys->keysym;
993       keys[num_keys].modifiers = ckeys->modifiers;
994       keys[num_keys].check_modifiers = ckeys->modifiermask;
995       num_keys++;
996     }
997 
998     TRACE(("\tnumber of trigger keys: %d\n", num_keys));
999     ipw->imp.num_trigger_keys = num_keys;
1000 
1001     if (num_keys > 0) {
1002 	int size;
1003 
1004 	size = sizeof(IMTriggerKey) * num_keys;
1005 	ipw->imp.trigger_keys = (IMTriggerKey *)XtMalloc(size);
1006 	bcopy((char *)keys, (char *)ipw->imp.trigger_keys, size);
1007     } else {
1008 	ipw->imp.trigger_keys = NULL;
1009     }
1010 }
1011 
1012 /*- ioeCallback: callback procedure for X I/O error -*/
1013 static void
ioeCallback(cldata)1014 ioeCallback(cldata)
1015 XPointer cldata;
1016 {
1017     IMProtocolWidget ipw = (IMProtocolWidget)cldata;
1018 
1019     if (ipw->imp.unix_sock >= 0 && ipw->imp.unix_path != NULL) {
1020 	(void)unlink(ipw->imp.unix_path);
1021     }
1022 }
1023