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