tocutil.c revision c9e2be55
1/*
2 * $XConsortium: tocutil.c,v 2.60 95/01/09 16:52:53 swick Exp $
3 * $XFree86: xc/programs/xmh/tocutil.c,v 3.3 2001/10/28 03:34:40 tsi Exp $
4 *
5 *
6 *			COPYRIGHT 1987, 1989
7 *		   DIGITAL EQUIPMENT CORPORATION
8 *		       MAYNARD, MASSACHUSETTS
9 *			ALL RIGHTS RESERVED.
10 *
11 * THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE AND
12 * SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT CORPORATION.
13 * DIGITAL MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR
14 * ANY PURPOSE.  IT IS SUPPLIED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
15 *
16 * IF THE SOFTWARE IS MODIFIED IN A MANNER CREATING DERIVATIVE COPYRIGHT
17 * RIGHTS, APPROPRIATE LEGENDS MAY BE PLACED ON THE DERIVATIVE WORK IN
18 * ADDITION TO THAT SET FORTH ABOVE.
19 *
20 * Permission to use, copy, modify, and distribute this software and its
21 * documentation for any purpose and without fee is hereby granted, provided
22 * that the above copyright notice appear in all copies and that both that
23 * copyright notice and this permission notice appear in supporting
24 * documentation, and that the name of Digital Equipment Corporation not be
25 * used in advertising or publicity pertaining to distribution of the software
26 * without specific, written prior permission.
27 */
28
29/* tocutil.c -- internal routines for toc stuff. */
30
31#include "xmh.h"
32#include "toc.h"
33#include "tocutil.h"
34#include "tocintrnl.h"
35
36#ifdef X_NOT_POSIX
37extern long lseek();
38#endif
39
40Toc TUMalloc(void)
41{
42    Toc toc;
43    toc = XtNew(TocRec);
44    bzero((char *)toc, (int) sizeof(TocRec));
45    toc->msgs = (Msg *) NULL;
46    toc->seqlist = (Sequence *) NULL;
47    toc->validity = unknown;
48    return toc;
49}
50
51
52/* Returns TRUE if the scan file for the given toc is out of date. */
53
54int TUScanFileOutOfDate(Toc toc)
55{
56    return LastModifyDate(toc->path) > toc->lastreaddate;
57}
58
59
60/* Make sure the sequence menu entries correspond exactly to the sequences
61 * for this toc.
62 */
63
64void TUCheckSequenceMenu(Toc toc)
65{
66    Scrn	scrn;
67    register int i, n;
68    Arg		query_args[2];
69    char 	*name;
70    Cardinal	j;
71    int		numChildren;
72    Widget	menu, item;
73    Button	button;
74    WidgetList	children;
75
76    static XtCallbackRec callbacks[] = {
77	{ DoSelectSequence,		(XtPointer) NULL},
78	{ (XtCallbackProc) NULL,	(XtPointer) NULL},
79    };
80    static Arg  args[] = {
81	{ XtNcallback,			(XtArgVal) callbacks},
82	{ XtNleftMargin, 		(XtArgVal) 18},
83    };
84
85    for (j=0; j < toc->num_scrns; j++) {
86	scrn = toc->scrn[j];
87
88	/* Find the sequence menu and the number of entries in it. */
89
90	name = MenuBoxButtons[XMH_SEQUENCE].button_name;
91	button = BBoxFindButtonNamed(scrn->mainbuttons, name);
92	menu = BBoxMenuOfButton(button);
93	XtSetArg(query_args[0], XtNnumChildren, &numChildren);
94	XtSetArg(query_args[1], XtNchildren, &children);
95	XtGetValues(menu, query_args, (Cardinal) 2);
96	n = MenuBoxButtons[XMH_SEQUENCE].num_entries;
97	if (strcmp(XtName(children[0]), "menuLabel") == 0)
98	    n++;
99
100	/* Erase the current check mark. */
101
102	for (i=(n-1); i < numChildren; i++)
103	    ToggleMenuItem(children[i], False);
104
105	/* Delete any entries which should be deleted. */
106
107	for (i=n; i < numChildren; i++)
108	    if (! TocGetSeqNamed(toc, XtName(children[i])))
109		XtDestroyWidget(children[i]);
110
111	/* Create any entries which should be created. */
112
113	callbacks[0].closure = (XtPointer) scrn;
114	for (i=1; i < toc->numsequences; i++)
115	    if (! XtNameToWidget(menu, toc->seqlist[i]->name))
116		XtCreateManagedWidget(toc->seqlist[i]->name, smeBSBObjectClass,
117				      menu, args, XtNumber(args));
118
119	/* Set the check mark. */
120
121	name = toc->viewedseq->name;
122	if ((item = XtNameToWidget(menu, name)) != NULL)
123	    ToggleMenuItem(item, True);
124    }
125    TocSetSelectedSequence(toc, toc->viewedseq);
126}
127
128
129void TUScanFileForToc(Toc toc)
130{
131    Scrn scrn;
132    char  **argv, str[100];
133    if (toc) {
134	TUGetFullFolderInfo(toc);
135	if (toc->num_scrns) scrn = toc->scrn[0];
136	else scrn = scrnList[0];
137
138	(void) sprintf(str, "Rescanning %s", toc->foldername);
139	ChangeLabel(scrn->toclabel, str);
140
141	argv = MakeArgv(5);
142	argv[0] = "scan";
143	argv[1] = TocMakeFolderName(toc);
144	argv[2] = "-width";
145	(void) sprintf(str, "%d", app_resources.toc_width);
146	argv[3] = str;
147	argv[4] = "-noheader";
148	DoCommand(argv, (char *) NULL, toc->scanfile);
149	XtFree(argv[1]);
150	XtFree((char *) argv);
151
152	toc->needslabelupdate = True;
153	toc->validity = valid;
154	toc->curmsg = NULL;	/* Get cur msg somehow! %%% */
155    }
156}
157
158
159
160int TUGetMsgPosition(Toc toc, Msg msg)
161{
162    int msgid, h = 0, l, m;
163    char str[100];
164    static Boolean ordered = True;
165    msgid = msg->msgid;
166    if (ordered) {
167	l = 0;
168	h = toc->nummsgs - 1;
169	while (l < h - 1) {
170	    m = (l + h) / 2;
171	    if (toc->msgs[m]->msgid > msgid)
172		h = m;
173	    else
174		l = m;
175	}
176	if (toc->msgs[l] == msg) return l;
177	if (toc->msgs[h] == msg) return h;
178    }
179    ordered = False;
180    for (l = 0; l < toc->nummsgs; l++) {
181	if (msgid == toc->msgs[l]->msgid) return l;
182    }
183    (void) sprintf(str,
184		   "TUGetMsgPosition search failed! hi=%d, lo=%d, msgid=%d",
185		   h, l, msgid);
186    Punt(str);
187    return 0; /* Keep lint happy. */
188}
189
190
191void TUResetTocLabel(Scrn scrn)
192{
193    char str[500];
194    Toc toc;
195    if (scrn) {
196	toc = scrn->toc;
197	if (toc == NULL)
198	    (void) strcpy(str, " ");
199	else {
200	    if (toc->stopupdate) {
201		toc->needslabelupdate = TRUE;
202		return;
203	    }
204	    (void) sprintf(str, "%s:%s", toc->foldername,
205			   toc->viewedseq->name);
206	    toc->needslabelupdate = FALSE;
207	}
208	ChangeLabel((Widget) scrn->toclabel, str);
209    }
210}
211
212
213/* A major toc change has occured; redisplay it.  (This also should work even
214   if we now have a new source to display stuff from.) */
215
216void TURedisplayToc(Scrn scrn)
217{
218    Toc toc;
219    Widget source;
220    if (scrn != NULL && scrn->tocwidget != NULL) {
221	toc = scrn->toc;
222 	if (toc) {
223	    if (toc->stopupdate) {
224		toc->needsrepaint = TRUE;
225		return;
226	    }
227	    XawTextDisableRedisplay(scrn->tocwidget);
228	    source = XawTextGetSource(scrn->tocwidget);
229	    if (toc->force_reset || source != toc->source) {
230		XawTextSetSource(scrn->tocwidget, toc->source,
231				 (XawTextPosition) 0);
232		toc->force_reset = False; /* %%% temporary */
233	    }
234	    TocSetCurMsg(toc, TocGetCurMsg(toc));
235	    XawTextEnableRedisplay(scrn->tocwidget);
236	    TUCheckSequenceMenu(toc);
237	    toc->needsrepaint = FALSE;
238	} else {
239	    XawTextSetSource(scrn->tocwidget, PNullSource, (XawTextPosition) 0);
240	}
241    }
242}
243
244
245void TULoadSeqLists(Toc toc)
246{
247    Sequence seq;
248    FILEPTR fid;
249    char    str[500], *ptr, *ptr2, viewed[500], selected[500];
250    int     i;
251    if (toc->viewedseq) (void) strcpy(viewed, toc->viewedseq->name);
252    else *viewed = 0;
253    if (toc->selectseq) (void) strcpy(selected, toc->selectseq->name);
254    else *selected = 0;
255    for (i = 0; i < toc->numsequences; i++) {
256	seq = toc->seqlist[i];
257	XtFree((char *) seq->name);
258	if (seq->mlist) FreeMsgList(seq->mlist);
259	XtFree((char *)seq);
260    }
261    toc->numsequences = 1;
262    toc->seqlist = (Sequence *) XtRealloc((char *) toc->seqlist,
263					  (Cardinal) sizeof(Sequence));
264    seq = toc->seqlist[0] = XtNew(SequenceRec);
265    seq->name = XtNewString("all");
266    seq->mlist = NULL;
267    toc->viewedseq = seq;
268    toc->selectseq = seq;
269    (void) sprintf(str, "%s/.mh_sequences", toc->path);
270    fid = myfopen(str, "r");
271    if (fid) {
272	while ((ptr = ReadLine(fid))) {
273	    ptr2 = strchr(ptr, ':');
274	    if (ptr2) {
275		*ptr2 = 0;
276		if (strcmp(ptr, "all") != 0 &&
277		    strcmp(ptr, "cur") != 0 &&
278		    strcmp(ptr, "unseen") != 0) {
279		    toc->numsequences++;
280		    toc->seqlist = (Sequence *)
281			XtRealloc((char *) toc->seqlist, (Cardinal)
282				  toc->numsequences * sizeof(Sequence));
283		    seq = toc->seqlist[toc->numsequences - 1] =
284			XtNew(SequenceRec);
285		    seq->name = XtNewString(ptr);
286		    seq->mlist = StringToMsgList(toc, ptr2 + 1);
287		    if (strcmp(seq->name, viewed) == 0) {
288			toc->viewedseq = seq;
289			*viewed = 0;
290		    }
291		    if (strcmp(seq->name, selected) == 0) {
292			toc->selectseq = seq;
293			*selected = 0;
294		    }
295		}
296	    }
297	}
298	(void) myfclose(fid);
299    }
300}
301
302
303
304/* Refigure what messages are visible. */
305
306void TURefigureWhatsVisible(Toc toc)
307{
308    MsgList mlist;
309    Msg msg, oldcurmsg;
310    int i;
311    int	w, changed, newval, msgid;
312    Sequence seq = toc->viewedseq;
313    mlist = seq->mlist;
314    oldcurmsg = toc->curmsg;
315    TocSetCurMsg(toc, (Msg)NULL);
316    w = 0;
317    changed = FALSE;
318
319    for (i = 0; i < toc->nummsgs; i++) {
320	msg = toc->msgs[i];
321	msgid = msg->msgid;
322	while (mlist && mlist->msglist[w] && mlist->msglist[w]->msgid < msgid)
323	    w++;
324	newval = (!mlist
325		  || (mlist->msglist[w] && mlist->msglist[w]->msgid == msgid));
326	if (newval != msg->visible) {
327	    changed = TRUE;
328	    msg->visible = newval;
329	}
330    }
331    if (changed) {
332	TURefigureTocPositions(toc);
333	if (oldcurmsg) {
334	    if (!oldcurmsg->visible) {
335		toc->curmsg = TocMsgAfter(toc, oldcurmsg);
336		if (toc->curmsg == NULL)
337		    toc->curmsg = TocMsgBefore(toc, oldcurmsg);
338	    } else toc->curmsg = oldcurmsg;
339	}
340	for (i=0 ; i<toc->num_scrns ; i++)
341	    TURedisplayToc(toc->scrn[i]);
342    } else TocSetCurMsg(toc, oldcurmsg);
343    for (i=0 ; i<toc->num_scrns ; i++)
344	TUResetTocLabel(toc->scrn[i]);
345}
346
347
348/* (Re)load the toc from the scanfile.  If reloading, this makes efforts to
349   keep the fates of msgs, and to keep msgs that are being edited.  Note that
350   this routine must know of all places that msg ptrs are stored; it expects
351   them to be kept only in tocs, in scrns, and in msg sequences. */
352
353#define SeemsIdentical(msg1, msg2) ((msg1)->msgid == (msg2)->msgid &&	      \
354				    ((msg1)->temporary || (msg2)->temporary ||\
355				     strcmp((msg1)->buf, (msg2)->buf) == 0))
356
357void TULoadTocFile(Toc toc)
358{
359    int maxmsgs, l, orignummsgs, i, j, origcurmsgid;
360    FILEPTR fid;
361    XawTextPosition position;
362    char *ptr;
363    Msg msg, curmsg;
364    Msg *origmsgs;
365    int bufsiz = app_resources.toc_width + 1;
366    static char *buf;
367
368    if (!buf)
369	buf = XtMalloc((Cardinal) bufsiz);
370    TocStopUpdate(toc);
371    toc->lastreaddate = LastModifyDate(toc->scanfile);
372    if (toc->curmsg) {
373	origcurmsgid = toc->curmsg->msgid;
374	TocSetCurMsg(toc, (Msg)NULL);
375    } else origcurmsgid = 0;  /* The "default" current msg; 0 means none */
376    fid = FOpenAndCheck(toc->scanfile, "r");
377    maxmsgs = orignummsgs = toc->nummsgs;
378    if (maxmsgs == 0) maxmsgs = 100;
379    toc->nummsgs = 0;
380    origmsgs = toc->msgs;
381    toc->msgs = (Msg *) XtMalloc((Cardinal) maxmsgs * sizeof(Msg));
382    position = 0;
383    i = 0;
384    curmsg = NULL;
385    while ((ptr = fgets(buf, bufsiz, fid))) {
386	toc->msgs[toc->nummsgs++] = msg = XtNew(MsgRec);
387	bzero((char *) msg, sizeof(MsgRec));
388	msg->toc = toc;
389	msg->position = position;
390	msg->length = l = strlen(ptr);
391	position += l;
392	if (l == app_resources.toc_width && buf[bufsiz-2] != '\n') {
393	    buf[bufsiz-2] = '\n';
394	    msg->buf = strcpy(XtMalloc((Cardinal) ++l), ptr);
395	    msg->msgid = atoi(ptr);
396	    do
397		ptr = fgets(buf, bufsiz, fid);
398	    while (ptr && (int) strlen(ptr) == app_resources.toc_width
399		   && buf[bufsiz-2] != '\n');
400	} else {
401	    msg->buf = strcpy(XtMalloc((Cardinal) ++l), ptr);
402	    msg->msgid = atoi(ptr);
403	}
404	if (msg->msgid == origcurmsgid)
405	    curmsg = msg;
406	msg->buf[MARKPOS] = ' ';
407	msg->changed = FALSE;
408	msg->fate = Fignore;
409	msg->desttoc = NULL;
410	msg->visible = TRUE;
411	if (toc->nummsgs >= maxmsgs) {
412	    maxmsgs += 100;
413	    toc->msgs = (Msg *) XtRealloc((char *) toc->msgs,
414					  (Cardinal) maxmsgs * sizeof(Msg));
415	}
416	while (i < orignummsgs && origmsgs[i]->msgid < msg->msgid) i++;
417	if (i < orignummsgs) {
418	    origmsgs[i]->buf[MARKPOS] = ' ';
419	    if (SeemsIdentical(origmsgs[i], msg))
420		MsgSetFate(msg, origmsgs[i]->fate, origmsgs[i]->desttoc);
421	}
422    }
423    toc->length = toc->origlength = toc->lastPos = position;
424    toc->msgs = (Msg *) XtRealloc((char *) toc->msgs,
425				  (Cardinal) toc->nummsgs * sizeof(Msg));
426    (void) myfclose(fid);
427    if ( (toc->source == NULL) && ( toc->num_scrns > 0 ) ) {
428        Arg args[1];
429
430	XtSetArg(args[0], XtNtoc, toc);
431	toc->source = XtCreateWidget("tocSource", tocSourceWidgetClass,
432				     toc->scrn[0]->tocwidget,
433				     args, (Cardinal) 1);
434    }
435    for (i=0 ; i<numScrns ; i++) {
436	msg = scrnList[i]->msg;
437	if (msg && msg->toc == toc) {
438	    for (j=0 ; j<toc->nummsgs ; j++) {
439		if (SeemsIdentical(toc->msgs[j], msg)) {
440		    msg->position = toc->msgs[j]->position;
441		    msg->visible = TRUE;
442		    ptr = toc->msgs[j]->buf;
443		    l = toc->msgs[j]->length;
444		    *(toc->msgs[j]) = *msg;
445		    toc->msgs[j]->buf = ptr;
446		    toc->msgs[j]->length = l;
447		    scrnList[i]->msg = toc->msgs[j];
448		    break;
449		}
450	    }
451	    if (j >= toc->nummsgs) {
452		msg->temporary = FALSE;	/* Don't try to auto-delete msg. */
453		MsgSetScrnForce(msg, (Scrn) NULL);
454	    }
455	}
456    }
457    for (i=0 ; i<orignummsgs ; i++)
458	MsgFree(origmsgs[i]);
459    XtFree((char *)origmsgs);
460    TocSetCurMsg(toc, curmsg);
461    TULoadSeqLists(toc);
462    TocStartUpdate(toc);
463}
464
465
466void TUSaveTocFile(Toc toc)
467{
468    Msg msg;
469    int fid;
470    int i;
471    XawTextPosition position;
472    char c;
473    if (toc->stopupdate) {
474	toc->needscachesave = TRUE;
475	return;
476    }
477    fid = -1;
478    position = 0;
479    for (i = 0; i < toc->nummsgs; i++) {
480	msg = toc->msgs[i];
481	if (fid < 0 && msg->changed) {
482	    fid = myopen(toc->scanfile, O_RDWR, 0666);
483	    (void) lseek(fid, (long)position, 0);
484	}
485	if (fid >= 0) {
486	    c = msg->buf[MARKPOS];
487	    msg->buf[MARKPOS] = ' ';
488	    (void) write(fid, msg->buf, msg->length);
489	    msg->buf[MARKPOS] = c;
490	}
491	position += msg->length;
492    }
493    if (fid < 0 && toc->length != toc->origlength)
494	fid = myopen(toc->scanfile, O_RDWR, 0666);
495    if (fid >= 0) {
496#if defined(SYSV) && (defined(i386) || defined(MOTOROLA))
497	(void) ftruncate_emu(fid, toc->length, toc->scanfile);
498#else
499	(void) ftruncate(fid, toc->length);
500	myclose(fid);
501#endif
502	toc->origlength = toc->length;
503    }
504    toc->needscachesave = FALSE;
505    toc->lastreaddate = LastModifyDate(toc->scanfile);
506}
507
508
509static Boolean UpdateScanFile(
510  XtPointer client_data)	/* Toc */
511{
512    Toc toc = (Toc)client_data;
513    int i;
514
515    if (app_resources.block_events_on_busy) ShowBusyCursor();
516
517    TUScanFileForToc(toc);
518    TULoadTocFile(toc);
519
520    for (i=0 ; i<toc->num_scrns ; i++)
521	TURedisplayToc(toc->scrn[i]);
522
523    if (app_resources.block_events_on_busy) UnshowBusyCursor();
524
525    return True;
526}
527
528
529void TUEnsureScanIsValidAndOpen(Toc toc, Boolean delay)
530{
531    if (toc) {
532	TUGetFullFolderInfo(toc);
533	if (TUScanFileOutOfDate(toc)) {
534	    if (!delay)
535		UpdateScanFile((XtPointer)toc);
536	    else {
537		/* this is a hack to get the screen mapped before
538		 * spawning the subprocess (and blocking).
539		 * Need to make sure the scanfile exists at this point.
540		 */
541		int fid = myopen(toc->scanfile, O_RDWR|O_CREAT, 0666);
542		myclose(fid);
543		XtAppAddWorkProc(XtWidgetToApplicationContext(toplevel),
544				 UpdateScanFile,
545				 (XtPointer)toc);
546	    }
547	}
548	if (toc->source == NULL)
549	    TULoadTocFile(toc);
550    }
551}
552
553
554
555/* Refigure all the positions, based on which lines are visible. */
556
557void TURefigureTocPositions(Toc toc)
558{
559    int i;
560    Msg msg;
561    XawTextPosition position, length;
562    position = length = 0;
563    for (i=0; i<toc->nummsgs ; i++) {
564	msg = toc->msgs[i];
565	msg->position = position;
566	if (msg->visible) position += msg->length;
567	length += msg->length;
568    }
569    toc->lastPos = position;
570    toc->length = length;
571}
572
573
574
575/* Make sure we've loaded ALL the folder info for this toc, including its
576   path and sequence lists. */
577
578void TUGetFullFolderInfo(Toc toc)
579{
580    char str[500];
581    if (! toc->scanfile) {
582	if (! toc->path) {
583	    /* usually preset by TocFolderExists */
584	    (void) sprintf(str, "%s/%s", app_resources.mail_path,
585			   toc->foldername);
586	    toc->path = XtNewString(str);
587	}
588	(void) sprintf(str, "%s/.xmhcache", toc->path);
589	toc->scanfile = XtNewString(str);
590	toc->lastreaddate = LastModifyDate(toc->scanfile);
591	if (TUScanFileOutOfDate(toc))
592	    toc->validity = invalid;
593	else {
594	    toc->validity = valid;
595	    TULoadTocFile(toc);
596	}
597    }
598}
599
600/* Append a message to the end of the toc.  It has the given scan line.  This
601   routine will figure out the message number, and change the scan line
602   accordingly. */
603
604Msg TUAppendToc(Toc toc, char *ptr)
605{
606    Msg msg;
607    int msgid;
608
609    TUGetFullFolderInfo(toc);
610    if (toc->validity != valid)
611	return NULL;
612
613    if (toc->nummsgs > 0)
614	msgid = toc->msgs[toc->nummsgs - 1]->msgid + 1;
615    else
616	msgid = 1;
617    (toc->nummsgs)++;
618    toc->msgs = (Msg *) XtRealloc((char *) toc->msgs,
619				  (Cardinal) toc->nummsgs * sizeof(Msg));
620    toc->msgs[toc->nummsgs - 1] = msg = XtNew(MsgRec);
621    bzero((char *) msg, (int) sizeof(MsgRec));
622    msg->toc = toc;
623    msg->buf = XtNewString(ptr);
624    if (msgid >= 10000)
625	msgid %= 10000;
626    (void)sprintf(msg->buf, "%4d", msgid);
627    msg->buf[MARKPOS] = ' ';
628    msg->msgid = msgid;
629    msg->position = toc->lastPos;
630    msg->length = strlen(ptr);
631    msg->changed = TRUE;
632    msg->fate = Fignore;
633    msg->desttoc = NULL;
634    if (toc->viewedseq == toc->seqlist[0]) {
635	msg->visible = TRUE;
636	toc->lastPos += msg->length;
637    }
638    else
639	msg->visible = FALSE;
640    toc->length += msg->length;
641    if ( (msg->visible) && (toc->source != NULL) )
642	TSourceInvalid(toc, msg->position, msg->length);
643    TUSaveTocFile(toc);
644    return msg;
645}
646