1 /*
2  * Copyright (c) 1994-2009, 2013-2016, 2019 Paul Mattes.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *     * Redistributions of source code must retain the above copyright
9  *       notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above copyright
11  *       notice, this list of conditions and the following disclaimer in the
12  *       documentation and/or other materials provided with the distribution.
13  *     * Neither the name of Paul Mattes nor his contributors may be used
14  *       to endorse or promote products derived from this software without
15  *       specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY PAUL MATTES "AS IS" AND ANY EXPRESS
18  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL PAUL MATTES BE LIABLE FOR ANY DIRECT,
21  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
26  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 /*
31  *	save.c
32  *		Implements the response to the WM_SAVE_YOURSELF message and
33  *		x3270 profiles.
34  */
35 
36 #include "globals.h"
37 #include "xglobals.h"
38 
39 #include <X11/StringDefs.h>
40 #include <X11/Xatom.h>
41 #include <pwd.h>
42 #include <errno.h>
43 #include "appres.h"
44 #include "resources.h"
45 
46 #include "codepage.h"
47 #if !defined(USE_APP_DEFAULTS) /*[*/
48 # include "fallbacks.h"
49 #endif /*]*/
50 #include "idle.h"
51 #include "keymap.h"
52 #include "lazya.h"
53 #include "popups.h"
54 #include "save.h"
55 #include "screen.h"
56 #include "toggles.h"
57 #include "utils.h"
58 #include "xappres.h"
59 #include "xkeypad.h"
60 #include "xsave.h"
61 #include "xscreen.h"
62 
63 /* Support for WM_SAVE_YOURSELF. */
64 
65 char *command_string = NULL;
66 
67 static char *cmd;
68 static int cmd_len;
69 
70 #define NWORDS	1024
71 
72 static char **tmp_cmd;
73 static int tcs;
74 
75 static Status x_get_window_attributes(Window w, XWindowAttributes *wa);
76 
77 /* Search for an option in the tmp_cmd array. */
78 static int
cmd_srch(const char * s)79 cmd_srch(const char *s)
80 {
81     int i;
82 
83     for (i = 1; i < tcs; i++) {
84 	if (tmp_cmd[i] && !strcmp(tmp_cmd[i], s)) {
85 	    return i;
86 	}
87     }
88     return 0;
89 }
90 
91 /* Replace an options in the tmp_cmd array. */
92 static void
cmd_replace(int ix,const char * s)93 cmd_replace(int ix, const char *s)
94 {
95     XtFree(tmp_cmd[ix]);
96     tmp_cmd[ix] = XtNewString(s);
97 }
98 
99 /* Append an option to the tmp_cmd array. */
100 static void
cmd_append(const char * s)101 cmd_append(const char *s)
102 {
103     tmp_cmd[tcs++] = XtNewString(s);
104     tmp_cmd[tcs] = (char *) NULL;
105 }
106 
107 /* Delete an option from the tmp_cmd array. */
108 static void
cmd_delete(int ix)109 cmd_delete(int ix)
110 {
111     XtFree(tmp_cmd[ix]);
112     tmp_cmd[ix] = (char *) NULL;
113 }
114 
115 /* Save the screen geometry. */
116 static void
save_xy(void)117 save_xy(void)
118 {
119     char *tbuf;
120     Window window, frame, child;
121     XWindowAttributes wa;
122     int x, y;
123     int ix;
124 
125     window = XtWindow(toplevel);
126     if (!x_get_window_attributes(window, &wa))
127 	return;
128     XTranslateCoordinates(display, window, wa.root,
129 	    -wa.border_width, -wa.border_width,
130 	    &x, &y, &child);
131 
132     frame = XtWindow(toplevel);
133     while (true) {
134 	Window root, parent;
135 	Window *wchildren;
136 	unsigned int nchildren;
137 
138 	int status = XQueryTree(display, frame, &root, &parent, &wchildren,
139 		&nchildren);
140 	if (status && wchildren) {
141 	    XFree((char *)wchildren);
142 	}
143 	if (parent == root || !parent || !status) {
144 	    break;
145 	}
146 	frame = parent;
147     }
148     if (frame != window) {
149 	if (!x_get_window_attributes(frame, &wa)) {
150 	    return;
151 	}
152 	x = wa.x;
153 	y = wa.y;
154     }
155 
156     tbuf = lazyaf("+%d+%d", x, y);
157     if ((ix = cmd_srch("-geometry"))) {
158 	cmd_replace(ix + 1, tbuf);
159     } else {
160 	cmd_append("-geometry");
161 	cmd_append(tbuf);
162     }
163 }
164 
165 /* Save the icon information: state, label, geometry. */
166 static void
save_icon(void)167 save_icon(void)
168 {
169     unsigned char *data;
170     int iconX, iconY;
171     char *tbuf;
172     int ix;
173     unsigned long nitems;
174 
175     {
176 	Atom actual_type;
177 	int actual_format;
178 	unsigned long leftover;
179 
180 	if (XGetWindowProperty(display, XtWindow(toplevel), a_state, 0L, 2L,
181 		    False, a_state, &actual_type, &actual_format, &nitems,
182 		    &leftover, &data) != Success) {
183 	    return;
184 	}
185 	if (actual_type != a_state || actual_format != 32 || nitems < 1) {
186 	    return;
187 	}
188     }
189 
190     ix = cmd_srch("-iconic");
191     if (*(unsigned long *)data == IconicState) {
192 	if (!ix) {
193 	    cmd_append("-iconic");
194 	}
195     } else {
196 	if (ix) {
197 	    cmd_delete(ix);
198 	}
199     }
200 
201     if (nitems < 2) {
202 	return;
203     }
204 
205     {
206 	Window icon_window;
207 	XWindowAttributes wa;
208 	Window child;
209 
210 	icon_window = *(Window *)(data + sizeof(unsigned long));
211 	if (icon_window == None) {
212 	    return;
213 	}
214 	if (!x_get_window_attributes(icon_window, &wa)) {
215 	    return;
216 	}
217 	XTranslateCoordinates(display, icon_window, wa.root,
218 		-wa.border_width, -wa.border_width, &iconX, &iconY, &child);
219 	if (!iconX && !iconY) {
220 	    return;
221 	}
222     }
223 
224     tbuf = lazyaf("%d", iconX);
225     ix = cmd_srch(OptIconX);
226     if (ix) {
227 	cmd_replace(ix + 1, tbuf);
228     } else {
229 	cmd_append(OptIconX);
230 	cmd_append(tbuf);
231     }
232 
233     tbuf = lazyaf("%d", iconY);
234     ix = cmd_srch(OptIconY);
235     if (ix) {
236 	cmd_replace(ix + 1, tbuf);
237     } else {
238 	cmd_append(OptIconY);
239 	cmd_append(tbuf);
240     }
241     return;
242 }
243 
244 /* Save the keymap information. */
245 static void
save_keymap(void)246 save_keymap(void)
247 {
248    /* Note: keymap propogation is deliberately disabled, because it
249       may vary from workstation to workstation.  The recommended
250       way of specifying keymaps is through your .Xdefaults or the
251       KEYMAP or KEYBD environment variables, which can be easily set
252       in your .login or .profile to machine-specific values; the
253       -keymap switch is really for debugging or testing keymaps.
254 
255       I'm sure I'll regret this.  */
256 
257 #if defined(notdef) /*[*/
258     if (current_keymap) {
259 	add_string(v, OptKeymap);
260 	add_string(v, current_keymap);
261     }
262 #endif /*]*/
263 }
264 
265 /* Save the model name. */
266 static void
save_model(void)267 save_model(void)
268 {
269     int ix;
270 
271     if (!model_changed) {
272 	return;
273     }
274     if ((ix = cmd_srch(OptModel)) && strcmp(tmp_cmd[ix], model_name)) {
275 	cmd_replace(ix + 1, model_name);
276     } else {
277 	cmd_append(OptModel);
278 	cmd_append(model_name);
279     }
280 }
281 
282 /* Save the emulator font. */
283 static void
save_efont(void)284 save_efont(void)
285 {
286     int ix;
287 
288     if (!efont_changed) {
289 	return;
290     }
291     if ((ix = cmd_srch(OptEmulatorFont)) && strcmp(tmp_cmd[ix], efontname)) {
292 	cmd_replace(ix + 1, efontname);
293     } else {
294 	cmd_append(OptEmulatorFont);
295 	cmd_append(efontname);
296     }
297 }
298 
299 /* Save the keypad state. */
300 static void
save_keypad(void)301 save_keypad(void)
302 {
303     int ix;
304 
305     ix = cmd_srch(OptKeypadOn);
306     if (xappres.keypad_on || keypad_popped) {
307 	if (!ix) {
308 	    cmd_append(OptKeypadOn);
309 	}
310     } else {
311 	if (ix) {
312 	    cmd_delete(ix);
313 	}
314     }
315 }
316 
317 /* Save the scrollbar state. */
318 static void
save_scrollbar(void)319 save_scrollbar(void)
320 {
321     int i_on, i_off;
322 
323     if (!scrollbar_changed) {
324 	return;
325     }
326     i_on = cmd_srch(OptScrollBar);
327     i_off = cmd_srch(OptNoScrollBar);
328     if (toggled(SCROLL_BAR)) {
329 	if (!i_on) {
330 	    if (i_off) {
331 		cmd_replace(i_off, OptScrollBar);
332 	    } else {
333 	    }
334 		cmd_append(OptScrollBar);
335 	}
336     } else {
337 	if (!i_off) {
338 	    if (i_on) {
339 		cmd_replace(i_on, OptNoScrollBar);
340 	    } else {
341 		cmd_append(OptNoScrollBar);
342 	    }
343 	}
344     }
345 }
346 
347 /* Save the name of the host we are connected to. */
348 static void
save_host(void)349 save_host(void)
350 {
351     char *space;
352 
353     if (!CONNECTED) {
354 	return;
355     }
356     space = strchr(full_current_host, ' ');
357     if (space == (char *) NULL) {
358 	cmd_append(full_current_host);
359     } else {
360 	char *tmp = XtNewString(full_current_host);
361 	char *port;
362 
363 	space = strchr(tmp, ' ');
364 	*space = '\0';
365 	cmd_append(tmp);
366 	port = space + 1;
367 	while (*port == ' ') {
368 	    port++;
369 	}
370 	if (*port) {
371 	    cmd_append(port);
372 	}
373 	XtFree(tmp);
374     }
375 }
376 
377 /* Save the settings of each of the toggles. */
378 static void
save_toggles(void)379 save_toggles(void)
380 {
381     toggle_index_t i;
382     int j;
383     int ix;
384 
385     for (i = 0; i < N_TOGGLES; i++) {
386 	toggle_index_t tix = toggle_names[i].index;
387 
388 	if (!toggle_changed(tix)) {
389 	    continue;
390 	}
391 
392 	/*
393 	 * Find the last "-set" or "-clear" for this toggle.
394 	 * If there is a preferred alias, delete them instead.
395 	 */
396 	ix = 0;
397 	for (j = 1; j < tcs; j++) {
398 	    if (tmp_cmd[j] &&
399 		(!strcmp(tmp_cmd[j], OptSet) ||
400 		 !strcmp(tmp_cmd[j], OptClear)) &&
401 		tmp_cmd[j+1] &&
402 		!strcmp(tmp_cmd[j+1], toggle_names[i].name)) {
403 
404 		if (toggle_names[i].is_alias) {
405 		    cmd_delete(j);
406 		    cmd_delete(j + 1);
407 		} else {
408 		    ix = j;
409 		}
410 	    }
411 	}
412 
413 	/* Handle aliased switches. */
414 	switch (tix) {
415 	case SCROLL_BAR:
416 	    continue;	/* +sb/-sb done separately */
417 	case TRACING:
418 	    ix = cmd_srch(OptTrace);
419 	    if (toggled(TRACING)) {
420 		if (!ix) {
421 		    cmd_append(OptTrace);
422 		}
423 	    } else {
424 		if (ix) {
425 		    cmd_delete(ix);
426 		}
427 	    }
428 	    continue;
429 	default:
430 	    break;
431 	}
432 
433 	/* If need be, switch "-set" with "-clear", or append one. */
434 	if (toggled(tix)) {
435 	    if (ix && strcmp(tmp_cmd[ix], OptSet)) {
436 		cmd_replace(ix, OptSet);
437 	    } else if (!ix) {
438 		cmd_append(OptSet);
439 		cmd_append(toggle_names[i].name);
440 	    }
441 	} else {
442 	    if (ix && strcmp(tmp_cmd[ix], OptClear)) {
443 		cmd_replace(ix, OptClear);
444 	    } else if (!ix) {
445 		cmd_append(OptClear);
446 		    cmd_append(toggle_names[i].name);
447 	    }
448 	}
449     }
450 }
451 
452 /* Remove a positional parameter from the command line. */
453 static void
remove_positional(char * s)454 remove_positional(char *s)
455 {
456     char *c;
457 
458     c = cmd + cmd_len - 2;	/* last byte of last arg */
459     while (*c && c >= cmd) {
460 	    c--;
461     }
462     if (strcmp(s, c + 1)) {
463 	XtError("Command-line switches must precede positional arguments");
464     }
465     cmd_len = c - cmd;
466 }
467 
468 /* Save a copy of he XA_WM_COMMAND poperty. */
469 void
save_init(int argc,char * hostname,char * port)470 save_init(int argc, char *hostname, char *port)
471 {
472     Atom actual_type;
473     int actual_format;
474     unsigned long nitems;
475     unsigned long bytes_after;
476 
477     /*
478      * Fetch the initial value of the XA_COMMAND property and store
479      * it in 'cmd'.
480      */
481     XGetWindowProperty(display, XtWindow(toplevel), XA_WM_COMMAND,
482 	    0L, 1000000L, False, XA_STRING, &actual_type, &actual_format,
483 	    &nitems, &bytes_after, (unsigned char **)&cmd);
484     if (nitems == 0) {
485 	XtError("Could not get initial XA_COMMAND property");
486     }
487     cmd_len = nitems * (actual_format / 8);
488 
489     /*
490      * Now locate the hostname and port positional arguments, and
491      * remove them.  If they aren't the last two components of the
492      * command line, abort.
493      */
494     switch (argc) {
495     case 3:
496 	remove_positional(port);
497 	/* fall through */
498     case 2:
499 	remove_positional(hostname);
500 	break;
501     }
502 }
503 
504 /* Handle a WM_SAVE_YOURSELF ICCM. */
505 void
save_yourself(void)506 save_yourself(void)
507 {
508     int i;
509     char *c, *c2;
510     int len;
511 
512     Replace(command_string, NULL);
513 
514     /* Copy the original command line into tmp_cmd. */
515     tmp_cmd = (char **) XtMalloc(sizeof(char *) * NWORDS);
516     tcs = 0;
517     i = 0;
518     c = cmd;
519     while (i < cmd_len) {
520 	c = cmd + i;
521 	tmp_cmd[tcs++] = XtNewString(c);
522 	i += strlen(c);
523 	i++;
524     }
525     tmp_cmd[tcs] = (char *) NULL;
526 
527     /* Replace the first element with the program name. */
528     cmd_replace(0, programname);
529 
530     /* Save options. */
531     save_xy();
532     save_icon();
533     save_keymap();
534     save_model();
535     save_efont();
536     save_keypad();
537     save_scrollbar();
538     save_toggles();
539     save_host();
540 
541     /* Copy what's left into contiguous memory. */
542     len = 0;
543     for (i = 0; i < tcs; i++) {
544 	if (tmp_cmd[i]) {
545 	    len += strlen(tmp_cmd[i]) + 1;
546 	}
547     }
548     c = XtMalloc(len);
549     c[0] = '\0';
550     c2 = c;
551     for (i = 0; i < tcs; i++) {
552 	if (tmp_cmd[i]) {
553 	    strcpy(c2, tmp_cmd[i]);
554 	    c2 += strlen(c2) + 1;
555 	    XtFree(tmp_cmd[i]);
556 	}
557     }
558     Free(tmp_cmd);
559 
560     /* Change the property. */
561     XChangeProperty(display, XtWindow(toplevel), XA_WM_COMMAND,
562 	    XA_STRING, 8, PropModeReplace, (unsigned char *)c, len);
563 
564     /* Save a readable copy of the command string for posterity. */
565     command_string = c;
566     while (((c2 = strchr(c, '\0')) != NULL) &&
567 	   (c2 - command_string < len-1)) {
568 	*c2 = ' ';
569 	c = c2 + 1;
570     }
571 }
572 
573 /* Support for x3270 profiles. */
574 
575 #define PROFILE_ENV	"X3270PRO"
576 #define NO_PROFILE_ENV	"NOX3270PRO"
577 #define RDB_ENV		"X3270RDB"
578 #define DEFAULT_PROFILE	"~/.x3270pro"
579 
580 char *profile_name = NULL;
581 static char *xcmd;
582 static int xargc;
583 static char **xargv;
584 
585 typedef struct scs {
586     struct scs *next;
587     char *name;
588 } scs_t;
589 scs_t *cc_list = NULL;
590 
591 void
charset_list_changed(char * charset)592 charset_list_changed(char *charset)
593 {
594     scs_t *c;
595 
596     for (c = cc_list; c != NULL; c = c->next) {
597 	if (!strcasecmp(c->name, charset)) {
598 	    return;
599 	}
600     }
601     c = (scs_t *)Malloc(sizeof(scs_t));
602     c->name = NewString(charset);
603     c->next = cc_list;
604     cc_list = c;
605 }
606 
607 /* Save one option in the file. */
608 static void
save_opt(FILE * f,const char * full_name,const char * opt_name,const char * res_name,const char * value)609 save_opt(FILE *f, const char *full_name, const char *opt_name,
610 	const char *res_name, const char *value)
611 {
612     fprintf(f, "! %s ", full_name);
613     if (opt_name != NULL) {
614 	fprintf(f, " (%s)", opt_name);
615     }
616     fprintf(f, "\n%s.%s: %s\n", XtName(toplevel), res_name, value);
617 }
618 
619 /* Save the current options settings in a profile. */
620 bool
save_options(char * n)621 save_options(char *n)
622 {
623     FILE *f;
624     bool exists = false;
625     char *ct;
626     toggle_index_t i;
627     time_t clk;
628     char *buf;
629     bool any_toggles = false;
630 
631     if (n == NULL || *n == '\0') {
632 	return false;
633     }
634 
635     /* Open the file. */
636     n = do_subst(n, DS_VARS | DS_TILDE);
637     f = fopen(n, "r");
638     if (f != NULL) {
639 	fclose(f);
640 	exists = true;
641     }
642     f = fopen(n, "a");
643     if (f == NULL) {
644 	popup_an_errno(errno, "Cannot open %s", n);
645 	XtFree(n);
646 	return false;
647     }
648 
649     /* Save the name. */
650     Replace(profile_name, n);
651 
652     /* Print the header. */
653     clk = time((time_t *)0);
654     ct = ctime(&clk);
655     if (ct[strlen(ct)-1] == '\n') {
656 	ct[strlen(ct)-1] = '\0';
657     }
658     if (exists) {
659 	fprintf(f, "! File updated %s by %s\n", ct, build);
660     } else {
661 	fprintf(f,
662 "! x3270 profile\n\
663 ! File created %s by %s\n\
664 ! This file overrides xrdb and .Xdefaults.\n\
665 ! To skip reading this file, set %s in the environment.\n\
666 !\n",
667 		ct, build, NO_PROFILE_ENV);
668     }
669 
670     /* Save most of the toggles. */
671     for (i = 0; toggle_names[i].name; i++) {
672 	toggle_index_t tix = toggle_names[i].index;
673 
674 	if (toggle_names[i].is_alias || !toggle_changed(tix)) {
675 	    continue;
676 	}
677 	if (!any_toggles) {
678 	    fprintf(f, "! toggles (%s, %s)\n", OptSet, OptClear);
679 	    any_toggles = true;
680 	}
681 	fprintf(f, "%s.%s: %s\n", XtName(toplevel),
682 		toggle_names[i].name,
683 		toggled(tix)? ResTrue: ResFalse);
684     }
685 
686     /* Save the keypad state. */
687     if (keypad_changed) {
688 	save_opt(f, "keypad state", OptKeypadOn, ResKeypadOn,
689 		(xappres.keypad_on || keypad_popped)? ResTrue: ResFalse);
690     }
691 
692     /* Save other menu-changeable options. */
693     if (efont_changed) {
694 	save_opt(f, "emulator font", OptEmulatorFont, ResEmulatorFont,
695 		efontname);
696     }
697     if (model_changed) {
698 	buf = xs_buffer("%d", model_num);
699 	save_opt(f, "model", OptModel, ResModel, buf);
700 	Free(buf);
701     }
702     if (oversize_changed) {
703 	buf = xs_buffer("%dx%d", ov_cols, ov_rows);
704 	save_opt(f, "oversize", OptOversize, ResOversize, buf);
705 	Free(buf);
706     }
707     if (scheme_changed && xappres.color_scheme != NULL) {
708 	save_opt(f, "color scheme", OptColorScheme, ResColorScheme,
709 	    xappres.color_scheme);
710     }
711     if (keymap_changed && current_keymap != NULL) {
712 	save_opt(f, "keymap", OptKeymap, ResKeymap, current_keymap);
713     }
714     if (codepage_changed) {
715 	save_opt(f, "codepage", OptCodePage, ResCodePage, get_codepage_name());
716     }
717     if (idle_changed) {
718 	save_opt(f, "idle command", NULL, ResIdleCommand, idle_command);
719 	save_opt(f, "idle timeout", NULL, ResIdleTimeout, idle_timeout_string);
720 	save_opt(f, "idle enabled", NULL, ResIdleCommandEnabled,
721 		(idle_user_enabled == IDLE_PERM)?  ResTrue: ResFalse);
722     }
723 
724     /* Done. */
725     fclose(f);
726 
727     return true;
728 }
729 
730 /* Save a copy of the command-line options. */
731 void
save_args(int argc,char * argv[])732 save_args(int argc, char *argv[])
733 {
734     int i;
735     int len = 0;
736 
737     for (i = 0; i < argc; i++) {
738 	len += strlen(argv[i]) + 1;
739     }
740     xcmd = XtMalloc(len + 1);
741     xargv = (char **)XtMalloc((argc + 1) * sizeof(char *));
742     len = 0;
743     for (i = 0; i < argc; i++) {
744 	xargv[i] = xcmd + len;
745 	strcpy(xcmd + len, argv[i]);
746 	len += strlen(argv[i]) + 1;
747     }
748     xargv[i] = NULL;
749     *(xcmd + len) = '\0';
750     xargc = argc;
751 }
752 
753 #if !defined(USE_APP_DEFAULTS) /*[*/
754 #define DEF_NAME	"x3270"
755 #define NLEN		(sizeof(DEF_NAME) - 1)
756 #define DOT_NAME	DEF_NAME "."
757 #define STAR_NAME	DEF_NAME "*"
758 
759 /* Substitute an alternate name in the fallback resource definitions. */
760 static char *
subst_name(unsigned char * fallbacks)761 subst_name(unsigned char *fallbacks)
762 {
763     char *tlname;
764     char *s, *t;
765     bool eol = true;
766     int nname = 0;
767     size_t nlen;
768     int flen;
769     char *new_fallbacks;
770 
771     /* If the name is the same, do nothing. */
772     if (!strcmp((tlname = XtName(toplevel)), DEF_NAME)) {
773 	return (char *)fallbacks;
774     }
775 
776     /* Count the number of instances of "x3270" in the fallbacks. */
777     s = (char *)fallbacks;
778     while (*s) {
779 	if (eol && (!strncmp(s, DOT_NAME, NLEN + 1) ||
780 		    !strncmp(s, STAR_NAME, NLEN + 1))) {
781 	    nname++;
782 	    s += NLEN;
783 	    eol = false;
784 	} else if (*s == '\n') {
785 	    eol = true;
786 	} else {
787 	    eol = false;
788 	}
789 	s++;
790     }
791     if (!nname) {
792 	return (char *)fallbacks;
793     }
794 
795     /* Allocate a buffer to do the substitution into. */
796     if ((nlen = strlen(tlname)) > NLEN) {
797 	flen = strlen((char *)fallbacks) + ((nlen - NLEN) * nname) + 1;
798     } else {
799 	flen = strlen((char *)fallbacks) - ((NLEN - nlen) * nname) + 1;
800     }
801     new_fallbacks = Malloc(flen);
802 
803     /* Substitute. */
804     s = (char *)fallbacks;
805     t = new_fallbacks;
806     while (*s) {
807 	if (eol && (!strncmp(s, DOT_NAME, NLEN + 1) ||
808 		    !strncmp(s, STAR_NAME, NLEN + 1))) {
809 	    strcpy(t, tlname);
810 	    t += nlen;
811 	    s += NLEN;
812 	    eol = false;
813 	} else if (*s == '\n') {
814 	    eol = true;
815 	} else {
816 	    eol = false;
817 	}
818 	*t++ = *s++;
819     }
820     *t = '\0';
821     return new_fallbacks;
822 }
823 #endif /*]*/
824 
825 /* Merge in the options settings from a profile. */
826 void
merge_profile(XrmDatabase * d,char * session,bool mono)827 merge_profile(XrmDatabase *d, char *session, bool mono)
828 {
829     const char *fname;
830     char *env_resources;
831     XrmDatabase dd;
832 
833 #if !defined(USE_APP_DEFAULTS) /*[*/
834     /* Start with the fallbacks. */
835     dd = XrmGetStringDatabase(subst_name(common_fallbacks));
836     if (dd == NULL) {
837 	XtError("Can't parse common fallbacks");
838     }
839     XrmMergeDatabases(dd, d);
840     dd = XrmGetStringDatabase(subst_name(mono? mono_fallbacks:
841 					       color_fallbacks));
842     if (dd == NULL) {
843 	XtError("Can't parse mono/color fallbacks");
844     }
845     XrmMergeDatabases(dd, d);
846 #endif /*]*/
847 
848     if (session == NULL && getenv(NO_PROFILE_ENV) != NULL) {
849 	profile_name = do_subst(DEFAULT_PROFILE, DS_VARS | DS_TILDE);
850     } else {
851 	/* Open the file. */
852 	if (session != NULL) {
853 	    fname = session;
854 	} else {
855 	    fname = getenv(PROFILE_ENV);
856 	}
857 	if (fname == NULL || *fname == '\0') {
858 	    fname = DEFAULT_PROFILE;
859 	}
860 	profile_name = do_subst(fname, DS_VARS | DS_TILDE);
861 
862 	/* Create a resource database from the file. */
863 	dd = XrmGetFileDatabase(profile_name);
864 	if (dd != NULL) {
865 	    /* Merge in the profile options. */
866 	    XrmMergeDatabases(dd, d);
867 	} else if (session != NULL) {
868 	    Error("Session file not found");
869 	}
870     }
871 
872     /* See if there are any environment resources. */
873     env_resources = getenv(RDB_ENV);
874     if (env_resources != NULL) {
875 	dd = XrmGetStringDatabase(env_resources);
876 	if (dd != NULL) {
877 	    XrmMergeDatabases(dd, d);
878 	}
879     }
880 
881     /* Merge the saved command-line options back on top of those. */
882     dd = NULL;
883     XrmParseCommand(&dd, options, num_options, programname, &xargc, xargv);
884     XrmMergeDatabases(dd, d);
885 
886     /* Free the saved command-line options. */
887     XtFree(xcmd);
888     xcmd = NULL;
889     Replace(xargv, NULL);
890 }
891 
892 bool
read_resource_file(const char * filename,bool fatal)893 read_resource_file(const char *filename, bool fatal)
894 {
895     XrmDatabase dd, rdb;
896 
897     dd = XrmGetFileDatabase(filename);
898     if (dd == NULL) {
899 	return false;
900     }
901 
902     rdb = XtDatabase(display);
903     XrmMergeDatabases(dd, &rdb);
904     return true;
905 }
906 
907 /*
908  * Safe routine for querying window attributes
909  */
910 static int
dummy_error_handler(Display * d _is_unused,XErrorEvent * e _is_unused)911 dummy_error_handler(Display *d _is_unused, XErrorEvent *e _is_unused)
912 {
913     return 0;
914 }
915 
916 static Status
x_get_window_attributes(Window w,XWindowAttributes * wa)917 x_get_window_attributes(Window w, XWindowAttributes *wa)
918 {
919     XErrorHandler old_handler;
920     Status s;
921 
922     old_handler = XSetErrorHandler(dummy_error_handler);
923 
924     s = XGetWindowAttributes(display, w, wa);
925     if (!s) {
926 	fprintf(stderr, "Error: querying bad window 0x%lx\n", w);
927     }
928 
929     XSetErrorHandler(old_handler);
930 
931     return s;
932 }
933