1 static char *SCCS_Id = "@(#)xprompt.c\tver 1.4 (91/09/28 14:23:33) brachman@cs.ubc.ca";
2 
3 /* vi: set tabstop=4 : */
4 
5 /*
6  * xprompt - prompt the user for one or more replies
7  *
8  *
9  * 28-Sep-91  Convert to X11R5, retain X11R4, lose X11R3
10  *            Casey Leedom <casey@gauss.llnl.gov>
11  *
12  * 28-Jan-90  Convert to X11R4, retain X11R3 compatibility
13  *
14  * 11-Mar-89  Fixed problem with XSetInputFocus being done before windows
15  *            were viewable.
16  *
17  * 24-Jan-89  Written for X11R3.  Released for distribution.
18  *
19  *
20  * Copyright 1989, 1990, 1991, 1995 by
21  * Barry Brachman and the University of British Columbia
22  *
23  * Permission to use, copy, modify, distribute, and sell this software and its
24  * documentation for any purpose is hereby granted without fee, provided that
25  * the above copyright notice appear in all copies and that both that
26  * copyright notice and this permission notice appear in supporting
27  * documentation, and that the name of U.B.C. not be used in advertising or
28  * publicity pertaining to distribution of the software without specific,
29  * written prior permission.  U.B.C. makes no representations about the
30  * suitability of this software for any purpose.  It is provided "as is"
31  * without express or implied warranty.
32  *
33  * -----
34  * Barry Brachman <brachman@cs.ubc.ca>
35  * Dept. of Computer Science
36  * 2366 Main Mall, Univ. of British Columbia
37  * Vancouver, B.C. V6T 1Z4
38  */
39 
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <ctype.h>
43 #include <stdio.h>
44 
45 #include <X11/IntrinsicP.h>
46 #include <X11/StringDefs.h>
47 #include <X11/Shell.h>
48 #include <X11/Xaw/Box.h>
49 #include <X11/Xaw/AsciiText.h>
50 
51 #include "trexp.h"
52 
53 #define maximum(x, y)		((x) > (y) ? (x) : (y))
54 
55 #define GEOM_WIDTH_PAD		25		/* Extra padding for window width */
56 #define GEOM_HEIGHT_PAD		15		/* Same for height */
57 
58 static XrmOptionDescRec table[] = {
59     {"-grab",   "Grab",                XrmoptionNoArg,  (caddr_t) "on"},
60     {"-ibw",    "insideborderWidth",   XrmoptionSepArg, NULL},
61     {"-nograb", "Grab",                XrmoptionNoArg,  (caddr_t) "off"},
62     {"-nowarp", "Warp",                XrmoptionNoArg,  (caddr_t) "off"},
63 	{"-nre",    "returnExit",          XrmoptionNoArg,  (caddr_t) "off"},
64 	{"-p",      "",                    XrmoptionSkipLine, NULL},
65     {"-pfn",    "promptFont",          XrmoptionSepArg, NULL},
66 	{"-re",     "returnExit",          XrmoptionNoArg,  (caddr_t) "on"},
67     {"-rfn",    "replyFont",           XrmoptionSepArg, NULL},
68     {"-rlen",   "Rlen",                XrmoptionSepArg, NULL},
69     {"-tf",     "textTranslationFile", XrmoptionSepArg, NULL},
70     {"-w",      "wordChars",           XrmoptionSepArg, NULL},
71     {"-warp",   "Warp",                XrmoptionNoArg, (caddr_t) "on"},
72 };
73 
74 typedef struct {
75     int rlen;				/* maximum reply length */
76     Boolean grab;			/* If TRUE, grab the keyboard */
77 	Boolean warp;			/* If TRUE, warp to reply window */
78 	Boolean return_exit;	/* If TRUE, exit on <return> if single prompt */
79 	char *texttranslations;
80 	char *texttranslationfile;
81 	char *wordchars;
82     char *geometry;
83     int borderwidth;
84     int insideborderwidth;
85     XFontStruct *font;
86     XFontStruct *pfont;
87     XFontStruct *rfont;
88 } app_resourceRec, *app_res;
89 
90 static app_resourceRec app_resources;
91 
92 static XtResource resources[] = {
93 {"rlen",  "Rlen", XtRInt, sizeof(int),
94    XtOffset(app_res, rlen), XtRImmediate, (caddr_t) 80},
95 {"grab",  "Grab", XtRBoolean, sizeof(Boolean),
96    XtOffset(app_res, grab), XtRImmediate, (caddr_t) TRUE },
97 {"insideborderwidth", "insideborderWidth", XtRInt, sizeof(int),
98    XtOffset(app_res, insideborderwidth), XtRImmediate, (caddr_t) 1},
99 {"replyfont", "replyFont", XtRFontStruct, sizeof(XFontStruct *),
100    XtOffset(app_res, rfont), XtRString, NULL},
101 {"promptfont", "promptFont", XtRFontStruct, sizeof(XFontStruct *),
102    XtOffset(app_res, pfont), XtRString, NULL},
103 {"returnexit", "returnExit", XtRBoolean, sizeof(Boolean),
104    XtOffset(app_res, return_exit), XtRImmediate, (caddr_t) FALSE},
105 {"texttranslationfile", "textTranslationFile", XtRString, sizeof(caddr_t),
106    XtOffset(app_res, texttranslationfile), XtRString, NULL},
107 {"texttranslations", "textTranslations", XtRString, sizeof(caddr_t),
108    XtOffset(app_res, texttranslations), XtRString, NULL},
109 {"wordchars", "wordChars", XtRString, sizeof(caddr_t),
110    XtOffset(app_res, wordchars), XtRString, "a-zA-Z0-9"},
111 {"warp",  "Warp", XtRBoolean, sizeof(Boolean),
112    XtOffset(app_res, warp), XtRImmediate, (caddr_t) FALSE },
113 
114 {XtNgeometry,  "Geometry", XtRString, sizeof(caddr_t),
115    XtOffset(app_res, geometry), XtRString, (caddr_t) "+500+400"},
116 {XtNborderWidth, "borderWidth", XtRInt, sizeof(int),
117    XtOffset(app_res, borderwidth), XtRImmediate, (caddr_t) 1},
118 {XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
119    XtOffset(app_res, font), XtRString, (caddr_t) XtDefaultFont},
120 };
121 
122 static char xprompt_TextTranslations[] =
123 "\
124 Ctrl<Key>C:		    abort() \n\
125 Ctrl<Key>D:		    finish-prompt() \n\
126 Ctrl<Key>J:         next-prompt() \n\
127 Ctrl<Key>M:         next-prompt() \n\
128 <Key>Down:		    next-prompt() \n\
129 <Key>Up:		    previous-prompt() \n\
130 <Key>Linefeed:	    next-prompt() \n\
131 <Key>Return:	    next-prompt-or-finish() \n\
132 Ctrl<Key>U:         erase-line() \n\
133 <Btn1Up>:           finish-prompt() \n\
134 <Btn2Up>:           finish-prompt() \n\
135 <Btn3Up>:           finish-prompt() \n\
136 <Btn1Down>:         ignore() \n\
137 <Btn2Down>:         ignore() \n\
138 <Btn3Down>:         ignore() \n\
139 <Btn1Motion>:       ignore() \n\
140 <Btn2Motion>:       ignore() \n\
141 <Btn3Motion>:       ignore() \n\
142 <VisibilityNotify>: visibility-event() \n\
143 ";
144 
145 static struct promptargs {
146 	char *prompt;
147 	char *reply;
148     Boolean seenprompt;
149 	struct promptargs *next;
150 	struct promptargs *prev;
151 } *promptargs, *npa, *npa_prev;
152 static int cpromptarg, npromptargs, nprompts_seen;
153 
154 static char *promptbuf, *replybuf;
155 static Widget toplevel, box, popup, reply_w, prompt_w;
156 static Widget complete_top_w, complete_w;
157 static Window orig_win;
158 static int orig_x, orig_y;
159 static int reply_x, reply_y;
160 static int prompt_bb_width, prompt_bb_height;
161 static int reply_bb_width, reply_bb_height;
162 static int visible;
163 
164 static trexp *word_tr;
165 static void changeprompt(), null_cursor(), string_changed();
166 static void unparsegeometry(), unwarp();
167 static void Syntax();
168 static int get_user_text_translations();
169 
170 char *malloc();
171 
172 /*ARGSUSED*/
173 static void
EraseLine(ctx,event,args,nargs)174 EraseLine(ctx, event, args, nargs)
175 TextWidget ctx;
176 XEvent event;
177 String *args;
178 Cardinal *nargs;
179 {
180 
181 	replybuf[0] = '\0';
182 	string_changed(reply_w, replybuf, 0);
183 	XawTextSetInsertionPoint(reply_w, 0);
184 	XawTextDisplay(reply_w);
185 }
186 
187 /*ARGSUSED*/
188 static void
EraseWord(ctx,event,args,nargs)189 EraseWord(ctx, event, args, nargs)
190 TextWidget ctx;
191 XEvent event;
192 String *args;
193 Cardinal *nargs;
194 {
195 	XawTextPosition endpos, pos, startpos;
196 
197 	startpos = pos = XawTextGetInsertionPoint(reply_w);
198 
199 	/* Skip any leading "non-word" characters */
200 	while (replybuf[pos] != '\0' && !trexec(word_tr, replybuf[pos]))
201 		pos++;
202 	/* Skip any "word" characters */
203 	while (trexec(word_tr, replybuf[pos]))
204 		pos++;
205 
206 	endpos = pos;
207 	pos = startpos;
208 	while (replybuf[endpos] != '\0')
209 		replybuf[pos++] = replybuf[endpos++];
210 	replybuf[pos] = '\0';
211 
212 	string_changed(reply_w, replybuf, pos);
213 	XawTextSetInsertionPoint(reply_w, startpos);
214 	XawTextDisplay(reply_w);
215 }
216 
217 /*ARGSUSED*/
218 static void
FinishPrompt(w,event,args,nargs)219 FinishPrompt(w, event, args, nargs)
220 Widget w;
221 XEvent event;
222 String *args;
223 Cardinal *nargs;
224 {
225 	int i;
226 
227 	strcpy(npa->reply, replybuf);
228 	for (i = 0, npa = promptargs; i < npromptargs; npa = npa->next, i++)
229 		printf("%s\n", npa->reply);
230 	unwarp();
231 	XtDestroyWidget(toplevel);
232 	exit(0);
233 }
234 
235 /*ARGSUSED*/
236 static void
PreviousPrompt(w,event,args,nargs)237 PreviousPrompt(w, event, args, nargs)
238 Widget w;
239 XEvent event;
240 String *args;
241 Cardinal *nargs;
242 {
243 
244 	npa_prev = npa;
245 	npa = npa->prev;
246 	if (--cpromptarg == 0)
247 		cpromptarg = npromptargs;
248 	changeprompt();
249 }
250 
251 /*ARGSUSED*/
252 static void
NextPrompt(w,event,args,nargs)253 NextPrompt(w, event, args, nargs)
254 Widget w;
255 XEvent event;
256 String *args;
257 Cardinal *nargs;
258 {
259 
260 	if (npromptargs > 1) {
261 		npa_prev = npa;
262 		npa = npa->next;
263 		if (++cpromptarg > npromptargs)
264 			cpromptarg = 1;
265 		changeprompt();
266 	}
267 }
268 
269 /*ARGSUSED*/
270 static void
NextPromptOrFinish(w,event,args,nargs)271 NextPromptOrFinish(w, event, args, nargs)
272 Widget w;
273 XEvent event;
274 String *args;
275 Cardinal *nargs;
276 {
277 
278 	if (nprompts_seen == npromptargs && app_resources.return_exit) {
279 		FinishPrompt(w, event, args, nargs);
280 		/*NOTREACHED*/
281 	}
282 	NextPrompt(w, event, args, nargs);
283 }
284 
285 /*ARGSUSED*/
286 static void
changeprompt()287 changeprompt()
288 {
289 	int replylen, promptlen;
290 
291 	if (npromptargs > 1)
292 		sprintf(promptbuf, "%s[%d/%d]:", npa->prompt, cpromptarg, npromptargs);
293 	else
294 		sprintf(promptbuf, "%s:", npa->prompt);
295 	strcpy(npa_prev->reply, replybuf);
296 	strcpy(replybuf, npa->reply);
297 	if (npa->seenprompt == FALSE) {
298 		npa->seenprompt = TRUE;
299 		nprompts_seen++;
300 	}
301 
302 	replylen = strlen(replybuf);
303 	promptlen = strlen(promptbuf);
304 	string_changed(reply_w, replybuf, replylen);
305 	string_changed(prompt_w, promptbuf, promptlen);
306 
307 	XawTextSetInsertionPoint(reply_w, replylen);
308 	XawTextSetInsertionPoint(prompt_w, promptlen);
309 
310 	XawTextDisplay(reply_w);
311 	XawTextDisplay(prompt_w);
312 }
313 
314 /*ARGSUSED*/
315 static void
Abort(w,event,args,nargs)316 Abort(w, event, args, nargs)
317 Widget w;
318 XEvent event;
319 String *args;
320 Cardinal *nargs;
321 {
322 
323 	unwarp();
324     XtDestroyWidget(toplevel);
325     exit(1);
326 }
327 
328 /*ARGSUSED*/
329 static void
Ignore(w,event,args,nargs)330 Ignore(w, event, args, nargs)
331 Widget w;
332 XEvent event;
333 String *args;
334 Cardinal *nargs;
335 {
336 
337 }
338 
339 /*ARGSUSED*/
340 static void
VisibilityEvent(w,event,args,nargs)341 VisibilityEvent(w, event, args, nargs)
342 Widget w;
343 XEvent event;
344 String *args;
345 Cardinal *nargs;
346 {
347 
348 	visible = 1;
349 }
350 
351 static XtActionsRec xprompt_actions[] = {
352   {"erase-line",		    EraseLine },
353   {"erase-word",            EraseWord },
354   {"next-prompt",		    NextPrompt },
355   {"previous-prompt",	    PreviousPrompt },
356   {"finish-prompt",		    FinishPrompt },
357   {"next-prompt-or-finish", NextPromptOrFinish },
358   {"abort",				    Abort },
359   {"ignore",                Ignore },
360   {"visibility-event",      VisibilityEvent },
361 };
362 
363 static void
Syntax(call)364 Syntax(call)
365 char *call;
366 {
367 
368 	fprintf(stderr, "%s\n", SCCS_Id);
369     fprintf(stderr, "Usage: %s [flags] [xtoolkitargs] -p prompt [-r reply] \
370 [-p prompt [-r reply]] ...\n", call);
371     fprintf(stderr, "where <flags> is one or more of:\n");
372 fprintf(stderr, "-rlen #     (Maximum length of user's reply: default 80)\n");
373 fprintf(stderr, "-ibw #      (Border width of inside window: default 1)\n");
374 fprintf(stderr, "-grab       (Grab keyboard: default)\n");
375 fprintf(stderr, "-nograb     (Don't grab keyboard)\n");
376 fprintf(stderr, "-re         (Return key will exit if there's one prompt)\n");
377 fprintf(stderr, "-nre        (Return key won't exit: default)\n");
378 fprintf(stderr, "-pfn <font> (Prompt font)\n");
379 fprintf(stderr, "-rfn <font> (Reply font)\n");
380 fprintf(stderr, "-tf <file>  (Translation file to override defaults)\n");
381 fprintf(stderr, "-w <str>    (Characters making up a word\n");
382 fprintf(stderr, "-warp       (Warp cursor to reply window)\n");
383 fprintf(stderr, "-nowarp     (Don't warp cursor)\n");
384     exit(1);
385 }
386 
387 void unparsegeometry();
388 
main(argc,argv)389 main(argc, argv)
390 int argc;
391 char **argv;
392 {
393     register int i, j;
394 	int len, maxpromptlen;
395     Arg arg[11];
396     int geom_mask, geom_x, geom_y, geom_width, geom_height;
397     char geom_str[100];
398 	XtTranslations t;
399     XFontStruct *prompt_font, *reply_font;
400 
401     toplevel = XtInitialize(argv[0], "XPrompt", table, XtNumber(table),
402 							&argc, argv);
403 
404     XtGetApplicationResources(toplevel, &app_resources, resources,
405 							  XtNumber(resources), NULL, (Cardinal) 0);
406 
407     j = 0;
408 	promptargs = NULL;
409 	maxpromptlen = 0;
410     for (i = 1; i < argc; i++) {
411 		if (i == argc - 1 || strcmp(argv[i], "-p"))
412 			Syntax(argv[0]);
413 		if (promptargs) {
414 			npa->next =
415 				(struct promptargs *) malloc(sizeof(struct promptargs));
416 			npa->next->prev = npa;
417 			npa = npa->next;
418 			npa->next = NULL;
419 		}
420 		else {
421 			promptargs = npa =
422 				(struct promptargs *) malloc(sizeof(struct promptargs));
423 			npa->prev = npa->next = NULL;
424 		}
425 		npa->prompt = argv[++i];
426 		if ((len = strlen(npa->prompt)) > maxpromptlen)
427 			maxpromptlen = len;
428 		if ((npa->reply = (char *) malloc(app_resources.rlen + 1)) == NULL) {
429 			fprintf(stderr, "Can't alloc reply buffer\n");
430 			exit(1);
431 		}
432 		npa->reply[0] = '\0';
433 		if (argv[i+1] && !strcmp(argv[i+1], "-r")) {
434 			if (++i == argc - 1)
435 				Syntax(argv[0]);
436 			if (strlen(argv[++i]) > app_resources.rlen) {
437 				fprintf(stderr, "xprompt: default reply is too long\n");
438 				exit(1);
439 			}
440 			strcpy(npa->reply, argv[i]);
441 		}
442 		npa->seenprompt = FALSE;
443 		j++;
444 	}
445 	if ((npromptargs = j) == 0)
446 		Syntax(argv[0]);
447 	promptargs->prev = npa;
448 	npa->next = promptargs;
449 	npa = promptargs;
450 	cpromptarg = 1;
451 	replybuf = (char *) malloc(app_resources.rlen + 1);
452 	if (npromptargs > 1) {
453 		maxpromptlen += 5;
454 		if (npromptargs < 10)
455 			maxpromptlen += 2;
456 		else if (npromptargs < 100)
457 			maxpromptlen += 4;
458 		else
459 			maxpromptlen += 10;		/* yuk */
460 	}
461 	else
462 		maxpromptlen += 2;
463 	promptbuf = (char *) malloc(maxpromptlen);
464 
465     if (app_resources.pfont == NULL)
466 		app_resources.pfont = app_resources.font;
467     if (app_resources.rfont == NULL)
468 		app_resources.rfont = app_resources.font;
469 
470     XtAddActions(xprompt_actions, XtNumber(xprompt_actions));
471 
472 	if ((word_tr = trcomp(app_resources.wordchars)) == NULL) {
473 		fprintf(stderr, "xprompt: Parse of word chars failed.\n");
474 		exit(1);
475 	}
476 
477 	/*
478 	 * Determine the window geometry
479 	 */
480     prompt_font = app_resources.pfont;
481     prompt_bb_width = prompt_font->max_bounds.rbearing
482 		- prompt_font->min_bounds.lbearing;
483     prompt_bb_height = prompt_font->max_bounds.ascent
484 		+ prompt_font->max_bounds.descent;
485     reply_font = app_resources.rfont;
486     reply_bb_width = reply_font->max_bounds.rbearing
487 		- reply_font->min_bounds.lbearing;
488     reply_bb_height = reply_font->max_bounds.ascent
489 		+ reply_font->max_bounds.descent;
490 
491     geom_x = 500;				/* an arbitrary default location */
492     geom_y = 400;
493     geom_width = maxpromptlen * prompt_bb_width
494 		+ app_resources.rlen * reply_bb_width + GEOM_WIDTH_PAD;
495     geom_height = maximum(prompt_bb_height, reply_bb_height) + GEOM_HEIGHT_PAD;
496     geom_mask =
497 		XParseGeometry(app_resources.geometry, &geom_x, &geom_y, &geom_width,
498 			&geom_height);
499 	if (geom_mask & WidthValue)
500 		geom_width *= reply_bb_width;
501 	if (geom_mask & HeightValue)
502 		geom_height *= reply_bb_height;
503     unparsegeometry(geom_str, geom_mask,
504 					geom_width, geom_height,
505 					geom_x, geom_y);
506 
507 #ifdef DEBUG
508 	fprintf(stderr, "prompt_bb_width=%d prompt_bb_height=%d\n",
509 			prompt_bb_width, prompt_bb_height);
510 	fprintf(stderr, "reply_bb_width=%d reply_bb_height=%d\n",
511 			reply_bb_width, reply_bb_height);
512 	fprintf(stderr, "geom_width=%d geom_height=%d\n",
513 			maxpromptlen * prompt_bb_width
514 			+ app_resources.rlen * reply_bb_width + GEOM_WIDTH_PAD,
515 			maximum(prompt_bb_height, reply_bb_height) + GEOM_HEIGHT_PAD);
516 	fprintf(stderr, "geom_mask=%d geom_width=%d geom_height=%d\n",
517 			geom_mask, geom_width, geom_height);
518 	fprintf(stderr, "%s\n", geom_str);
519 #endif DEBUG
520 
521     XtSetArg(arg[0], XtNgeometry, geom_str);
522     XtSetArg(arg[1], XtNborderWidth, app_resources.borderwidth);
523 	/*
524 	 * If you prefer a popup window instead of a managed window, you
525 	 * can use the overrideShellWidgetClass instead of the
526 	 * topLevelShellWidgetClass
527 	 *    popup = XtCreatePopupShell("popup", overrideShellWidgetClass,
528 	 *			toplevel, arg, (Cardinal) 2);
529 	 */
530 	popup = XtCreatePopupShell("xprompt", topLevelShellWidgetClass,
531 							   toplevel, arg, (Cardinal) 2);
532 
533 	box =
534 		XtCreateManagedWidget("box", boxWidgetClass,
535 							  popup, NULL, (Cardinal) 0);
536 
537 	if (npromptargs > 1)
538 		sprintf(promptbuf, "%s[1/%d]:", promptargs->prompt, npromptargs);
539 	else
540 		sprintf(promptbuf, "%s:", promptargs->prompt);
541 	promptargs->seenprompt = TRUE;
542 	nprompts_seen = 1;
543     XtSetArg(arg[0], XtNstring, promptbuf);
544 	XtSetArg(arg[1], XtNlength, maxpromptlen);
545     XtSetArg(arg[2], XtNborderWidth, 0);
546     XtSetArg(arg[3], XtNfont, app_resources.pfont);
547 	XtSetArg(arg[4], XtNeditType, XawtextRead);
548 	XtSetArg(arg[5], XtNwidth, prompt_bb_width * maxpromptlen);
549 	XtSetArg(arg[6], XtNsensitive, False);
550 	XtSetArg(arg[7], XtNuseStringInPlace, True);
551 	XtSetArg(arg[8], XtNdisplayCaret, False);
552 	XtSetArg(arg[9], XtNtype, XawAsciiString);
553 	prompt_w = XtCreateManagedWidget("text", asciiTextWidgetClass,
554 									 box, arg, (Cardinal) 10);
555 
556 	strcpy(replybuf, promptargs->reply);
557 	XtSetArg(arg[0], XtNscrollVertical, XawtextScrollWhenNeeded);
558     XtSetArg(arg[1], XtNheight, reply_bb_height + 6);
559     XtSetArg(arg[2], XtNstring, replybuf);
560     XtSetArg(arg[3], XtNlength, app_resources.rlen);
561     XtSetArg(arg[4], XtNeditType, XawtextEdit);
562     XtSetArg(arg[5], XtNborderWidth, app_resources.insideborderwidth);
563     XtSetArg(arg[6], XtNwidth, app_resources.rlen * reply_bb_width);
564     XtSetArg(arg[7], XtNinsertPosition, strlen(promptargs->reply));
565     XtSetArg(arg[8], XtNfont, app_resources.rfont);
566 	XtSetArg(arg[9], XtNuseStringInPlace, True);
567 	XtSetArg(arg[10], XtNtype, XawAsciiString);
568 	reply_w = XtCreateManagedWidget("reply", asciiTextWidgetClass,
569 									box, arg, (Cardinal) 11);
570 
571 	t = XtParseTranslationTable(xprompt_TextTranslations);
572 	XtOverrideTranslations(reply_w, t);
573 	if (app_resources.texttranslations != NULL) {
574 		t = XtParseTranslationTable(app_resources.texttranslations);
575 		if (t == NULL) {
576 			fprintf(stderr, "xprompt: error parsing translation table.\n");
577 			exit(1);
578 		}
579 		XtOverrideTranslations(reply_w, t);
580 	}
581 	if (app_resources.texttranslationfile != NULL
582 		&& get_user_text_translations(app_resources.texttranslationfile) < 0)
583 		exit(1);
584 
585 	visible = 0;
586     XtPopup(popup, XtGrabNonexclusive);
587 
588 	/*
589 	 * Handle the grab and/or warp, if necessary
590 	 */
591 	if (app_resources.grab || app_resources.warp) {
592 		XWindowAttributes reply_w_attr;
593 
594 		if (XGetWindowAttributes(XtDisplay(reply_w), XtWindow(reply_w),
595 								 &reply_w_attr) == 0)
596 			app_resources.warp = app_resources.grab = FALSE;
597 		else if (!visible) {
598 			XEvent event;
599 			long em;
600 
601 			/*
602 			 * The following weirdness waits for the windows to become visible
603 			 * before warping the cursor or setting the input focus.
604 			 * This is necessary to avoid the cases where a window manager
605 			 * repositions things *after* the warp has occurred and where the
606 			 * focus window is not viewable at the time of the XSetInputFocus.
607 			 * Suggestions on the Right Thing welcome.
608 			 */
609 			em = VisibilityChangeMask;
610 			XSelectInput(XtDisplay(popup), XtWindow(popup), em);
611 			while (1) {
612 				bzero(&event, sizeof(event));
613 				XNextEvent(XtDisplay(popup), &event);
614 				if (event.type == VisibilityNotify)
615 					break;
616 			}
617 			em = reply_w_attr.all_event_masks;
618 			XSelectInput(XtDisplay(popup), XtWindow(popup), em);
619 		}
620 
621 		if (app_resources.grab) {
622 			XGrabKeyboard(XtDisplay(reply_w), XtWindow(reply_w), False,
623 						  GrabModeAsync, GrabModeAsync, CurrentTime);
624 			XSetInputFocus(XtDisplay(reply_w), XtWindow(reply_w),
625 						   RevertToPointerRoot, CurrentTime);
626 		}
627 
628 		if (app_resources.warp) {
629 			Window root_return, child_return;
630 			int root_x_return, root_y_return;
631 			int win_x_return, win_y_return;
632 			unsigned int mask_return;
633 
634 			orig_x = -1;
635 			orig_y = -1;
636 			/*
637 			 * Find out where the cursor is so that it can be put back
638 			 * before the program exits.
639 			 */
640 			if (XQueryPointer(XtDisplay(popup), XtWindow(popup),
641 							  &root_return, &child_return,
642 							  &root_x_return, &root_y_return,
643 							  &win_x_return, &win_y_return,
644 							  &mask_return) != 0) {
645 				orig_win = root_return;
646 				orig_x = root_x_return;
647 				orig_y = root_y_return;
648 			}
649 			reply_x = reply_w_attr.width - reply_w_attr.width / 10;
650 			reply_y = reply_w_attr.height / 2;
651 
652 			XWarpPointer(XtDisplay(reply_w), None, XtWindow(reply_w),
653 						 0, 0, 0, 0, reply_x, reply_y);
654 		}
655 	}
656 
657     XtMainLoop();
658 }
659 
660 static int
get_user_text_translations(file)661 get_user_text_translations(file)
662 char *file;
663 {
664 	char *p;
665 	FILE *fp;
666 	struct stat statb;
667 	XtTranslations t;
668 
669 	if ((fp = fopen(file, "r")) == NULL) {
670 		fprintf(stderr, "xprompt: Can't open translation file '%s'.\n", file);
671 		return(-1);
672 	}
673 	if (fstat(fileno(fp), &statb) < 0) {
674 		fprintf(stderr, "xprompt: Can't stat translation file '%s'.\n", file);
675 		fclose(fp);
676 		return(-1);
677 	}
678 	if (statb.st_size == 0) {
679 		fclose(fp);
680 		return(0);
681 	}
682 	if ((p = (char *) malloc((unsigned) statb.st_size)) == NULL) {
683 		fprintf(stderr, "xprompt: Can't malloc translation table.\n");
684 		fclose(fp);
685 		return(-1);
686 	}
687 	if (fread(p, statb.st_size, 1, fp) == 0) {
688 		fprintf(stderr, "xprompt: error reading translation table.\n");
689 		free(p);
690 		fclose(fp);
691 		return(-1);
692 	}
693 	fclose(fp);
694 	if ((t = XtParseTranslationTable(p)) == NULL) {
695 		fprintf(stderr, "xprompt: error parsing translation table.\n");
696 		free(p);
697 		return(-1);
698 	}
699 	XtOverrideTranslations(reply_w, t);
700 	free(p);
701 	return(0);
702 }
703 
704 static void
unwarp()705 unwarp()
706 {
707 
708 	if (app_resources.warp && orig_x != -1 && orig_y != -1) {
709 		XWarpPointer(XtDisplay(reply_w), None, orig_win,
710 					 0, 0, 0, 0, orig_x, orig_y);
711 		XFlush(XtDisplay(reply_w));
712 	}
713 }
714 
715 static void
unparsegeometry(buf,mask,w,h,x,y)716 unparsegeometry(buf, mask, w, h, x, y)
717 char *buf;
718 int mask, w, h, x, y;
719 {
720 
721 	sprintf(buf, "%dx%d%c%d%c%d",
722 			w, h,
723 			mask & XNegative ? '-' : '+', x,
724 			mask & YNegative ? '-' : '+', y);
725 }
726 
727 static void
string_changed(w,buf,pos)728 string_changed(w, buf, pos)
729 Widget w;
730 char *buf;
731 XawTextPosition pos;
732 {
733 	Arg args[20];
734 
735 	XtSetArg(args[0], XtNstring, buf);
736 	XtSetValues(w, args, (Cardinal) 1);
737 }
738