xedit.c revision c05e22d7
1/* $XConsortium: xedit.c,v 1.28 94/03/26 17:06:28 rws Exp $ */
2
3/*
4 *			  COPYRIGHT 1987
5 *		   DIGITAL EQUIPMENT CORPORATION
6 *		       MAYNARD, MASSACHUSETTS
7 *			ALL RIGHTS RESERVED.
8 *
9 * THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE AND
10 * SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT CORPORATION.
11 * DIGITAL MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR
12 * ANY PURPOSE.  IT IS SUPPLIED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
13 *
14 * IF THE SOFTWARE IS MODIFIED IN A MANNER CREATING DERIVATIVE COPYRIGHT RIGHTS,
15 * APPROPRIATE LEGENDS MAY BE PLACED ON THE DERIVATIVE WORK IN ADDITION TO THAT
16 * SET FORTH ABOVE.
17 *
18 *
19 * Permission to use, copy, modify, and distribute this software and its
20 * documentation for any purpose and without fee is hereby granted, provided
21 * that the above copyright notice appear in all copies and that both that
22 * copyright notice and this permission notice appear in supporting
23 * documentation, and that the name of Digital Equipment Corporation not be
24 * used in advertising or publicity pertaining to distribution of the software
25 * without specific, written prior permission.
26 */
27/* $XFree86: xc/programs/xedit/xedit.c,v 1.17 2002/09/22 07:09:05 paulo Exp $ */
28
29#include "xedit.h"
30#include <X11/Xaw/SmeBSB.h>
31#include <time.h>
32#include <sys/stat.h>
33#include <X11/CoreP.h>
34
35#include <stdlib.h>
36
37#define randomize()	srand((unsigned)time((time_t*)NULL))
38
39static XtActionsRec actions[] = {
40{"quit", QuitAction},
41{"save-file", SaveFile},
42{"load-file", LoadFile},
43{"find-file", FindFile},
44{"cancel-find-file", CancelFindFile},
45{"file-completion", FileCompletion},
46{"popup-menu", PopupMenu},
47{"kill-file", KillFile},
48{"split-window", SplitWindow},
49{"dir-window", DirWindow},
50{"delete-window", DeleteWindow},
51{"xedit-focus", XeditFocus},
52{"other-window", OtherWindow},
53{"switch-source", SwitchSource},
54#ifndef __UNIXOS2__
55{"lisp-eval", XeditLispEval},
56{"xedit-print-lisp-eval", XeditPrintLispEval},
57{"xedit-keyboard-reset",XeditKeyboardReset},
58#endif
59{"ispell", IspellAction},
60{"line-edit", LineEditAction},
61{"tags", TagsAction}
62};
63
64#define DEF_HINT_INTERVAL	300	/* in seconds, 5 minutes */
65
66static Atom wm_delete_window;
67static Widget hintswindow;
68static int position_format_mask;
69static XawTextPositionInfo infos[3];
70
71Widget topwindow, textwindow, messwidget, labelwindow, filenamewindow;
72Widget scratch, hpane, vpanes[2], labels[3], texts[3], forms[3], positions[3];
73Widget options_popup, dirlabel, dirwindow;
74Boolean international;
75Boolean line_edit;
76XawTextWrapMode wrapmodes[3];
77
78extern void ResetSourceChanged(xedit_flist_item*);
79
80static void makeButtonsAndBoxes(Widget);
81static void HintsTimer(XtPointer, XtIntervalId*);
82static void PositionChanged(Widget, XtPointer, XtPointer);
83static void StartFormatPosition(void);
84static void StartHints(void);
85
86Display *CurDpy;
87
88struct _app_resources app_resources;
89struct _xedit_flist flist;
90
91#define Offset(field) XtOffsetOf(struct _app_resources, field)
92
93static XtResource resources[] = {
94   {"enableBackups", "EnableBackups", XtRBoolean, sizeof(Boolean),
95         Offset(enableBackups), XtRImmediate, FALSE},
96   {"backupNamePrefix", "BackupNamePrefix", XtRString, sizeof(char *),
97         Offset(backupNamePrefix),XtRString, ""},
98   {"backupNameSuffix", "BackupNameSuffix", XtRString, sizeof(char *),
99         Offset(backupNameSuffix),XtRString, ".BAK"},
100   {"hints", "Hint", XtRString, sizeof(char *),
101	 Offset(hints.resource), XtRImmediate, NULL},
102   {"hintsInterval", XtCInterval, XtRInt, sizeof(long),
103	 Offset(hints.interval), XtRImmediate, (XtPointer)DEF_HINT_INTERVAL},
104   {"changedBitmap", XtRBitmap, XtRString, sizeof(char*),
105	 Offset(changed_pixmap_name), XtRString, "dot"},
106   {"positionFormat", "Format", XtRString, sizeof(char*),
107	 Offset(position_format), XtRString, "L%l"},
108   {"autoReplace", "Replace", XtRString, sizeof(char*),
109	 Offset(auto_replace), XtRImmediate, NULL},
110   {"tagsName", "TagsName", XtRString, sizeof(char *),
111         Offset(tagsName), XtRString, "tags"},
112   {"loadTags", "LoadTags", XtRBoolean, sizeof(Boolean),
113	 Offset(loadTags), XtRImmediate, (XtPointer)TRUE},
114};
115
116#undef Offset
117
118int
119main(int argc, char *argv[])
120{
121    Boolean		exists;
122    char		*filename;
123    FileAccess		file_access;
124    Widget		source;
125    XtAppContext	appcon;
126    Boolean		show_dir;
127    xedit_flist_item	*first_item;
128    unsigned int	i, lineno;
129
130    lineno = 0;
131    show_dir = FALSE;
132    first_item = NULL;
133
134    topwindow = XtAppInitialize(&appcon, "Xedit", NULL, 0, &argc, argv,
135				NULL,
136				NULL, 0);
137
138    XtAppAddActions(appcon, actions, XtNumber(actions));
139    XtOverrideTranslations(topwindow,
140			   XtParseTranslationTable("<Message>WM_PROTOCOLS: quit()"));
141
142    XtGetApplicationResources(topwindow, (XtPointer) &app_resources, resources,
143			      XtNumber(resources), NULL, 0);
144
145    CurDpy = XtDisplay(topwindow);
146    XawSimpleMenuAddGlobalActions(appcon);
147    XtRegisterGrabAction(PopupMenu, True,
148			 ButtonPressMask | ButtonReleaseMask,
149			 GrabModeAsync, GrabModeAsync);
150
151    makeButtonsAndBoxes(topwindow);
152
153    StartHints();
154    StartFormatPosition();
155    (void)StartHooks(appcon);
156    if (position_format_mask == 0) {
157	for (i = 0; i < 3; i++)
158	    XtRemoveCallback(texts[i], XtNpositionCallback,
159			     PositionChanged, NULL);
160    }
161    XtRealizeWidget(topwindow);
162
163#ifndef __UNIXOS2__
164    XeditLispInitialize();
165#endif
166
167    options_popup = XtCreatePopupShell("optionsMenu", simpleMenuWidgetClass,
168				       topwindow, NULL, 0);
169    XtRealizeWidget(options_popup);
170    XtAddCallback(XtCreateManagedWidget("ispell", smeBSBObjectClass,
171					options_popup, NULL, 0),
172		  XtNcallback, IspellCallback, NULL);
173    CreateEditPopup();
174
175    wm_delete_window = XInternAtom(XtDisplay(topwindow), "WM_DELETE_WINDOW",
176				   False);
177    (void)XSetWMProtocols(XtDisplay(topwindow), XtWindow(topwindow),
178			  &wm_delete_window, 1);
179
180    /* This first call is just to save the default font and colors */
181    UpdateTextProperties(0);
182
183    if (argc > 1) {
184	xedit_flist_item	*item;
185	Arg			args[2];
186	unsigned int		num_args;
187
188	for (i = 1; i < argc; i++) {
189	    struct stat st;
190
191	    if (argv[i][0] == '+') {
192		char	*endptr;
193
194		lineno = strtol(argv[i], &endptr, 10);
195		/* Don't warn about incorrect input? */
196		if (*endptr)
197		    lineno = 0;
198		continue;
199	    }
200
201	    filename = ResolveName(argv[i]);
202	    if (filename == NULL || FindTextSource(NULL, filename) != NULL)
203		continue;
204
205	    num_args = 0;
206	    if (stat(filename, &st) == 0 && !S_ISREG(st.st_mode)) {
207		if (S_ISDIR(st.st_mode)) {
208		    if (!first_item) {
209			char path[BUFSIZ + 1];
210
211			strncpy(path, filename, sizeof(path) - 2);
212			path[sizeof(path) - 2] = '\0';
213			if (*path) {
214			    if (path[strlen(path) - 1] != '/')
215				strcat(path, "/");
216			}
217			else
218			    strcpy(path, "./");
219			XtSetArg(args[0], XtNlabel, "");
220			XtSetValues(dirlabel, args, 1);
221			SwitchDirWindow(True);
222			DirWindowCB(dirwindow, path, NULL);
223			show_dir = True;
224		    }
225		    continue;
226		}
227	    }
228
229	    switch (file_access = CheckFilePermissions(filename, &exists)) {
230	    case NO_READ:
231		if (exists)
232		    XeditPrintf("File %s exists, and could not be opened for "
233				"reading.\n", argv[i]);
234		else
235		    XeditPrintf("File %s does not exist, and the directory "
236				"could not be opened for writing.\n", argv[i]);
237		break;
238	    case READ_OK:
239		XtSetArg(args[num_args], XtNeditType, XawtextRead); num_args++;
240		XeditPrintf("File %s opened READ ONLY.\n", argv[i]);
241		break;
242	    case WRITE_OK:
243		XtSetArg(args[num_args], XtNeditType, XawtextEdit); num_args++;
244		XeditPrintf("File %s opened read - write.\n", argv[i]);
245		break;
246	    }
247	    if (file_access != NO_READ) {
248		int flags;
249
250		if (exists) {
251		    flags = EXISTS_BIT;
252		    XtSetArg(args[num_args], XtNstring, filename);num_args++;
253		}
254		else {
255		    flags = 0;
256		    XtSetArg(args[num_args], XtNstring, NULL);	  num_args++;
257		}
258		source = XtVaCreateWidget("textSource", international ?
259					  multiSrcObjectClass
260					  : asciiSrcObjectClass, topwindow,
261					  XtNtype, XawAsciiFile,
262					  XtNeditType, XawtextEdit,
263					  NULL, NULL);
264		XtSetValues(source, args, num_args);
265		item = AddTextSource(source, argv[i], filename,
266				     flags, file_access);
267		XtAddCallback(item->source, XtNcallback, SourceChanged,
268			      (XtPointer)item);
269		if (exists && file_access == WRITE_OK) {
270		    item->mode = st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
271		    item->mtime = st.st_mtime;
272		}
273		if (!first_item && !show_dir)
274		    first_item = item;
275		ResetSourceChanged(item);
276	    }
277	}
278    }
279
280    if (!flist.pixmap && strlen(app_resources.changed_pixmap_name)) {
281	XrmValue from, to;
282
283	from.size = strlen(app_resources.changed_pixmap_name);
284	from.addr = app_resources.changed_pixmap_name;
285	to.size = sizeof(Pixmap);
286	to.addr = (XtPointer)&(flist.pixmap);
287
288	XtConvertAndStore(flist.popup, XtRString, &from, XtRBitmap, &to);
289    }
290
291    if (first_item == NULL) {
292	XtSetKeyboardFocus(topwindow, filenamewindow);
293	XtVaSetValues(textwindow, XtNwrap, XawtextWrapLine, NULL);
294    }
295    else {
296	SwitchTextSource(first_item);
297	XtSetKeyboardFocus(topwindow, textwindow);
298	if (lineno) {
299	    XawTextPosition position;
300
301	    source = XawTextGetSource(textwindow);
302	    position = RSCAN(XawTextGetInsertionPoint(textwindow),
303			     lineno, False);
304	    position = LSCAN(position, 1, False);
305	    XawTextSetInsertionPoint(textwindow, position);
306	}
307    }
308
309    XtAppMainLoop(appcon);
310    return EXIT_SUCCESS;
311}
312
313static void
314makeButtonsAndBoxes(Widget parent)
315{
316    Widget outer, b_row, viewport;
317    Arg arglist[10];
318    Cardinal num_args;
319    xedit_flist_item *item;
320    static char *labelWindow = "labelWindow", *editWindow = "editWindow";
321    static char *formWindow = "formWindow", *positionWindow = "positionWindow";
322
323    outer = XtCreateManagedWidget("paned", panedWidgetClass, parent,
324				  NULL, ZERO);
325
326    b_row = XtCreateManagedWidget("buttons", panedWidgetClass, outer, NULL, ZERO);
327    {
328	MakeCommandButton(b_row, "quit", DoQuit);
329	MakeCommandButton(b_row, "save", DoSave);
330	MakeCommandButton(b_row, "load", DoLoad);
331	filenamewindow = MakeStringBox(b_row, "filename", NULL);
332    }
333    hintswindow = XtCreateManagedWidget("bc_label", labelWidgetClass,
334					outer, NULL, ZERO);
335
336    num_args = 0;
337    XtSetArg(arglist[num_args], XtNeditType, XawtextEdit);		++num_args;
338    messwidget = XtCreateManagedWidget("messageWindow", asciiTextWidgetClass,
339				       outer, arglist, num_args);
340
341    num_args = 0;
342    XtSetArg(arglist[num_args], XtNorientation, XtorientHorizontal);	++num_args;
343    hpane = XtCreateManagedWidget("hpane", panedWidgetClass, outer,
344				  arglist, num_args);
345
346    num_args = 0;
347    XtSetArg(arglist[num_args], XtNorientation, XtorientVertical);	++num_args;
348    vpanes[0] = XtCreateManagedWidget("vpane", panedWidgetClass, hpane,
349				      arglist, num_args);
350    XtSetArg(arglist[num_args], XtNheight, 1);				++num_args;
351    XtSetArg(arglist[num_args], XtNwidth, 1);				++num_args;
352    vpanes[1] = XtCreateWidget("vpane", panedWidgetClass, hpane,
353			       arglist, num_args);
354
355    forms[0] = XtCreateManagedWidget(formWindow, formWidgetClass,
356				     vpanes[0], NULL, 0);
357    labelwindow = XtCreateManagedWidget(labelWindow,labelWidgetClass,
358					forms[0], NULL, 0);
359    labels[0] = labelwindow;
360    positions[0] = XtCreateManagedWidget(positionWindow,labelWidgetClass,
361					 forms[0], NULL, 0);
362
363    forms[2] = XtCreateWidget(formWindow, formWidgetClass,
364			      vpanes[1], NULL, 0);
365    labels[2] = XtCreateManagedWidget(labelWindow,labelWidgetClass,
366				      forms[2], NULL, 0);
367    positions[2] = XtCreateManagedWidget(positionWindow,labelWidgetClass,
368					 forms[2], NULL, 0);
369
370    num_args = 0;
371    XtSetArg(arglist[num_args], XtNtype, XawAsciiFile);			++num_args;
372    XtSetArg(arglist[num_args], XtNeditType, XawtextEdit);		++num_args;
373    textwindow =  XtCreateManagedWidget(editWindow, asciiTextWidgetClass,
374					vpanes[0], arglist, num_args);
375
376    /* Get international resource value form the textwindow */
377    num_args = 0;
378    XtSetArg(arglist[num_args], XtNinternational, &international);	++num_args;
379    XtGetValues(textwindow, arglist, num_args);
380
381    num_args = 0;
382    XtSetArg(arglist[num_args], XtNtype, XawAsciiFile);			++num_args;
383    XtSetArg(arglist[num_args], XtNeditType, XawtextEdit);		++num_args;
384    scratch = XtVaCreateWidget("textSource", international ?
385			       multiSrcObjectClass :
386			       asciiSrcObjectClass, topwindow,
387			       XtNtype, XawAsciiFile,
388			       XtNeditType, XawtextEdit,
389			       NULL, NULL);
390    XtSetValues(scratch, arglist, num_args);
391
392    num_args = 0;
393    XtSetArg(arglist[num_args], XtNtextSource, scratch);		++num_args;
394    XtSetValues(textwindow, arglist, num_args);
395
396    texts[0] = textwindow;
397    num_args = 0;
398    XtSetArg(arglist[num_args], XtNtextSource, scratch);		++num_args;
399    XtSetArg(arglist[num_args], XtNdisplayCaret, False);		++num_args;
400    texts[2] = XtCreateWidget(editWindow, asciiTextWidgetClass,
401			      vpanes[1], arglist, num_args);
402
403    forms[1] = XtCreateWidget(formWindow, formWidgetClass,
404			      vpanes[0], NULL, 0);
405    labels[1] = XtCreateManagedWidget(labelWindow,labelWidgetClass,
406				      forms[1], NULL, 0);
407    positions[1] = XtCreateManagedWidget(positionWindow,labelWidgetClass,
408					 forms[1], NULL, 0);
409
410    texts[1] = XtCreateWidget(editWindow, asciiTextWidgetClass,
411			      vpanes[0], arglist, num_args);
412
413    dirlabel = XtCreateWidget("dirlabel", labelWidgetClass,
414			      vpanes[1], NULL, 0);
415    num_args = 0;
416    XtSetArg(arglist[num_args], XtNheight, 1);				++num_args;
417    XtSetArg(arglist[num_args], XtNwidth, 1);				++num_args;
418    viewport = XtCreateWidget("viewport", viewportWidgetClass,
419			      vpanes[1], arglist, num_args);
420    dirwindow = XtCreateManagedWidget("dirwindow", listWidgetClass,
421				      viewport, NULL, 0);
422
423    item = AddTextSource(scratch, "*scratch*", "*scratch*",
424			 0, WRITE_OK);
425    item->wrap = XawtextWrapLine;
426    item->flags |= WRAP_BIT;
427    XtAddCallback(item->source, XtNcallback, SourceChanged,
428		  (XtPointer)item);
429    ResetSourceChanged(item);
430    flist.current = item;
431
432    for (num_args = 0; num_args < 3; num_args++)
433	XtAddCallback(texts[num_args], XtNpositionCallback, PositionChanged, NULL);
434
435    for (num_args = 0; num_args < 3; num_args++) {
436	XtSetArg(arglist[0], XtNwrap, &wrapmodes[num_args]);
437	XtGetValues(texts[num_args], arglist, 1);
438    }
439
440    XtAddCallback(dirwindow, XtNcallback, DirWindowCB, NULL);
441}
442
443/*	Function Name: Feep
444 *	Description: feeps the bell.
445 *	Arguments: none.
446 *	Returns: none.
447 */
448
449void
450Feep(void)
451{
452  XBell(CurDpy, 0);
453}
454
455#define	l_BIT		0x01
456#define	c_BIT		0x02
457#define	p_BIT		0x04
458#define	s_BIT		0x08
459#define MAX_FMT_LEN	30
460
461static void
462StartFormatPosition(void)
463{
464    char *fmt = app_resources.position_format;
465
466    if (fmt)
467	while (*fmt)
468	    if (*fmt++ == '%') {
469		int len = 0;
470
471		if (*fmt == '-') {
472		    ++fmt;
473		    ++len;
474		}
475		while (*fmt >= '0' && *fmt <= '9') {
476		    ++fmt;
477		    if (++len >= MAX_FMT_LEN) {
478			XtAppWarning(XtWidgetToApplicationContext(topwindow),
479				     "Format too large to formatPosition");
480			position_format_mask = 0;
481			return;
482		    }
483		}
484		switch (*fmt++) {
485		    case 'l':	position_format_mask |= l_BIT;	break;
486		    case 'c':	position_format_mask |= c_BIT;	break;
487		    case 'p':	position_format_mask |= p_BIT;	break;
488		    case 's':	position_format_mask |= s_BIT;	break;
489		    case '%':	break;
490		    default: {
491			char msg[256];
492
493			XmuSnprintf(msg, sizeof(msg),
494				    "Unknown format \"%%%c\" in positionFormat",
495				    fmt[-1]);
496			XtAppWarning(XtWidgetToApplicationContext(topwindow),
497				     msg);
498			position_format_mask = 0;
499			return;
500		    }
501		}
502	    }
503}
504
505/*ARGSUSED*/
506static void
507PositionChanged(Widget w, XtPointer client_data, XtPointer call_data)
508{
509    int idx;
510    XawTextPositionInfo *info = (XawTextPositionInfo*)call_data;
511
512    for (idx = 0; idx < 3; idx++)
513	if (w == texts[idx])
514	    break;
515    if (idx > 2)
516	return;
517
518    if (((position_format_mask & l_BIT)
519	  && infos[idx].line_number != info->line_number)
520	|| ((position_format_mask & c_BIT)
521	    && infos[idx].column_number != info->column_number)
522	|| ((position_format_mask & p_BIT)
523	    && infos[idx].insert_position != info->insert_position)
524	|| ((position_format_mask & s_BIT)
525	    && infos[idx].last_position != info->last_position)
526	|| infos[idx].overwrite_mode != info->overwrite_mode) {
527	int len = 6;
528	Arg args[1];
529	char buffer[256], *str = app_resources.position_format;
530	char fmt_buf[MAX_FMT_LEN + 2], *fmt;
531
532	memcpy(&infos[idx], info, sizeof(XawTextPositionInfo));
533	if (info->overwrite_mode)
534	    strcpy(buffer, "Ovrwt ");
535	else
536	    strcpy(buffer, "      ");
537	while (*str) {
538	    switch (*str) {
539		case '%':
540		    fmt = fmt_buf;
541		    *fmt++ = *str++;
542		    if (*str == '-')
543			*fmt++ = *str++;
544		    /*CONSTCOND*/
545		    while (*str >= '0' && *str <= '9') {
546			/* StartPositionFormat() already checked the format
547			 * length.
548			 */
549			*fmt++ = *str++;
550		    }
551		    *fmt++ = 'd';
552		    *fmt = '\0';
553		    switch (*str) {
554			case 'l':
555			    XmuSnprintf(&buffer[len], sizeof(buffer) - len,
556					fmt_buf, info->line_number);
557			    break;
558			case 'c':
559			    XmuSnprintf(&buffer[len], sizeof(buffer) - len,
560					fmt_buf, info->column_number);
561			    break;
562			case 'p':
563			    XmuSnprintf(&buffer[len], sizeof(buffer) - len,
564					fmt_buf, info->insert_position);
565			    break;
566			case 's':
567			    XmuSnprintf(&buffer[len], sizeof(buffer) - len,
568					fmt_buf, info->last_position);
569			    break;
570			case '%':
571			    strcpy(&buffer[len], "%");
572			    break;
573		    }
574		    len += strlen(&buffer[len]);
575		    break;
576		default:
577		    buffer[len++] = *str;
578		    break;
579	    }
580	    if (len >= sizeof(buffer) - 1)
581		break;
582	    ++str;
583	}
584	buffer[len] = '\0';
585
586	XtSetArg(args[0], XtNlabel, buffer);
587	XtSetValues(positions[idx], args, 1);
588    }
589}
590
591/*ARGSUSED*/
592static void
593HintsTimer(XtPointer closure, XtIntervalId *id)
594{
595    Arg args[1];
596    xedit_hints *hints = (xedit_hints*)closure;
597
598    hints->cur_hint = rand() % hints->num_hints;
599
600    XtSetArg(args[0], XtNlabel, hints->hints[hints->cur_hint]);
601    XtSetValues(hintswindow, args, 1);
602
603    hints->timer = XtAppAddTimeOut(XtWidgetToApplicationContext(topwindow),
604				   hints->interval, HintsTimer, closure);
605}
606
607#define MAX_HINT_LEN		255
608#define MIN_HINT_INTERVAL	5
609static void
610StartHints(void)
611{
612    char *str, *p;
613    unsigned i, len;
614    xedit_hints *hints = &(app_resources.hints);
615
616    /* if resource was not set, or was overriden */
617    if (hints->resource == NULL || !*hints->resource)
618	return;
619
620    randomize();
621
622    if (hints->interval < MIN_HINT_INTERVAL)
623	hints->interval = DEF_HINT_INTERVAL;
624    hints->interval *= 1000;
625    hints->hints = (char**)XtMalloc(sizeof(char*));
626    hints->hints[hints->cur_hint = 0] = p = hints->resource;
627    hints->num_hints = 1;
628
629    while ((p = strchr(p, '\n')) != NULL) {
630	if (*++p == '\0')
631	    break;
632	hints->hints = (char**)
633	    XtRealloc((char*)hints->hints,
634		      sizeof(char*) * (hints->num_hints + 1));
635	hints->hints[hints->num_hints++] = p;
636    }
637
638    /* make a private copy of the resource values, so that one can change
639     * the Xrm database safely.
640     */
641    for (i = 0; i < hints->num_hints; i++) {
642	if ((p = strchr(hints->hints[i], '\n')) != NULL)
643	    len = p - hints->hints[i];
644	else
645	    len = strlen(hints->hints[i]);
646	if (len > MAX_HINT_LEN)
647	    len = MAX_HINT_LEN;
648	str = XtMalloc(len + 1);
649	strncpy(str, hints->hints[i], len);
650	str[len] = '\0';
651	hints->hints[i] = str;
652    }
653
654    hints->timer = XtAppAddTimeOut(XtWidgetToApplicationContext(topwindow),
655				   hints->interval, HintsTimer,
656				   (XtPointer)hints);
657}
658