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 const 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 snprintf(str, sizeof(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 snprintf(str, sizeof(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 snprintf(str, sizeof(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 snprintf(str, sizeof(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 occurred; 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 snprintf(str, sizeof(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 = XtReallocArray(toc->seqlist, 281 toc->numsequences, sizeof(Sequence)); 282 seq = toc->seqlist[toc->numsequences - 1] = 283 XtNew(SequenceRec); 284 seq->name = XtNewString(ptr); 285 seq->mlist = StringToMsgList(toc, ptr2 + 1); 286 if (strcmp(seq->name, viewed) == 0) { 287 toc->viewedseq = seq; 288 *viewed = 0; 289 } 290 if (strcmp(seq->name, selected) == 0) { 291 toc->selectseq = seq; 292 *selected = 0; 293 } 294 } 295 } 296 } 297 (void) myfclose(fid); 298 } 299} 300 301 302 303/* Refigure what messages are visible. */ 304 305void TURefigureWhatsVisible(Toc toc) 306{ 307 MsgList mlist; 308 Msg msg, oldcurmsg; 309 int i; 310 int w, changed, newval, msgid; 311 Sequence seq = toc->viewedseq; 312 mlist = seq->mlist; 313 oldcurmsg = toc->curmsg; 314 TocSetCurMsg(toc, (Msg)NULL); 315 w = 0; 316 changed = FALSE; 317 318 for (i = 0; i < toc->nummsgs; i++) { 319 msg = toc->msgs[i]; 320 msgid = msg->msgid; 321 while (mlist && mlist->msglist[w] && mlist->msglist[w]->msgid < msgid) 322 w++; 323 newval = (!mlist 324 || (mlist->msglist[w] && mlist->msglist[w]->msgid == msgid)); 325 if (newval != msg->visible) { 326 changed = TRUE; 327 msg->visible = newval; 328 } 329 } 330 if (changed) { 331 TURefigureTocPositions(toc); 332 if (oldcurmsg) { 333 if (!oldcurmsg->visible) { 334 toc->curmsg = TocMsgAfter(toc, oldcurmsg); 335 if (toc->curmsg == NULL) 336 toc->curmsg = TocMsgBefore(toc, oldcurmsg); 337 } else toc->curmsg = oldcurmsg; 338 } 339 for (i=0 ; i<toc->num_scrns ; i++) 340 TURedisplayToc(toc->scrn[i]); 341 } else TocSetCurMsg(toc, oldcurmsg); 342 for (i=0 ; i<toc->num_scrns ; i++) 343 TUResetTocLabel(toc->scrn[i]); 344} 345 346 347/* (Re)load the toc from the scanfile. If reloading, this makes efforts to 348 keep the fates of msgs, and to keep msgs that are being edited. Note that 349 this routine must know of all places that msg ptrs are stored; it expects 350 them to be kept only in tocs, in scrns, and in msg sequences. */ 351 352#define SeemsIdentical(msg1, msg2) ((msg1)->msgid == (msg2)->msgid && \ 353 ((msg1)->temporary || (msg2)->temporary ||\ 354 strcmp((msg1)->buf, (msg2)->buf) == 0)) 355 356void TULoadTocFile(Toc toc) 357{ 358 int maxmsgs, l, orignummsgs, i, j, origcurmsgid; 359 FILEPTR fid; 360 XawTextPosition position; 361 char *ptr; 362 Msg msg, curmsg; 363 Msg *origmsgs; 364 int bufsiz = app_resources.toc_width + 1; 365 static char *buf; 366 367 if (!buf) 368 buf = XtMalloc((Cardinal) bufsiz); 369 TocStopUpdate(toc); 370 toc->lastreaddate = LastModifyDate(toc->scanfile); 371 if (toc->curmsg) { 372 origcurmsgid = toc->curmsg->msgid; 373 TocSetCurMsg(toc, (Msg)NULL); 374 } else origcurmsgid = 0; /* The "default" current msg; 0 means none */ 375 fid = FOpenAndCheck(toc->scanfile, "r"); 376 maxmsgs = orignummsgs = toc->nummsgs; 377 if (maxmsgs == 0) maxmsgs = 100; 378 toc->nummsgs = 0; 379 origmsgs = toc->msgs; 380 toc->msgs = XtMallocArray((Cardinal) maxmsgs, sizeof(Msg)); 381 position = 0; 382 i = 0; 383 curmsg = NULL; 384 while ((ptr = fgets(buf, bufsiz, fid))) { 385 toc->msgs[toc->nummsgs++] = msg = XtNew(MsgRec); 386 bzero((char *) msg, sizeof(MsgRec)); 387 msg->toc = toc; 388 msg->position = position; 389 msg->length = l = strlen(ptr); 390 position += l; 391 if (l == app_resources.toc_width && buf[bufsiz-2] != '\n') { 392 buf[bufsiz-2] = '\n'; 393 msg->buf = strcpy(XtMalloc((Cardinal) ++l), ptr); 394 msg->msgid = atoi(ptr); 395 do 396 ptr = fgets(buf, bufsiz, fid); 397 while (ptr && (int) strlen(ptr) == app_resources.toc_width 398 && buf[bufsiz-2] != '\n'); 399 } else { 400 msg->buf = strcpy(XtMalloc((Cardinal) ++l), ptr); 401 msg->msgid = atoi(ptr); 402 } 403 if (msg->msgid == origcurmsgid) 404 curmsg = msg; 405 msg->buf[MARKPOS] = ' '; 406 msg->changed = FALSE; 407 msg->fate = Fignore; 408 msg->desttoc = NULL; 409 msg->visible = TRUE; 410 if (toc->nummsgs >= maxmsgs) { 411 maxmsgs += 100; 412 toc->msgs = XtReallocArray(toc->msgs, maxmsgs, sizeof(Msg)); 413 } 414 while (i < orignummsgs && origmsgs[i]->msgid < msg->msgid) i++; 415 if (i < orignummsgs) { 416 origmsgs[i]->buf[MARKPOS] = ' '; 417 if (SeemsIdentical(origmsgs[i], msg)) 418 MsgSetFate(msg, origmsgs[i]->fate, origmsgs[i]->desttoc); 419 } 420 } 421 toc->length = toc->origlength = toc->lastPos = position; 422 toc->msgs = XtReallocArray(toc->msgs, toc->nummsgs, sizeof(Msg)); 423 (void) myfclose(fid); 424 if ( (toc->source == NULL) && ( toc->num_scrns > 0 ) ) { 425 Arg args[1]; 426 427 XtSetArg(args[0], XtNtoc, toc); 428 toc->source = XtCreateWidget("tocSource", tocSourceWidgetClass, 429 toc->scrn[0]->tocwidget, 430 args, (Cardinal) 1); 431 } 432 for (i=0 ; i<numScrns ; i++) { 433 msg = scrnList[i]->msg; 434 if (msg && msg->toc == toc) { 435 for (j=0 ; j<toc->nummsgs ; j++) { 436 if (SeemsIdentical(toc->msgs[j], msg)) { 437 msg->position = toc->msgs[j]->position; 438 msg->visible = TRUE; 439 ptr = toc->msgs[j]->buf; 440 l = toc->msgs[j]->length; 441 *(toc->msgs[j]) = *msg; 442 toc->msgs[j]->buf = ptr; 443 toc->msgs[j]->length = l; 444 scrnList[i]->msg = toc->msgs[j]; 445 break; 446 } 447 } 448 if (j >= toc->nummsgs) { 449 msg->temporary = FALSE; /* Don't try to auto-delete msg. */ 450 MsgSetScrnForce(msg, (Scrn) NULL); 451 } 452 } 453 } 454 for (i=0 ; i<orignummsgs ; i++) 455 MsgFree(origmsgs[i]); 456 XtFree((char *)origmsgs); 457 TocSetCurMsg(toc, curmsg); 458 TULoadSeqLists(toc); 459 TocStartUpdate(toc); 460} 461 462 463void TUSaveTocFile(Toc toc) 464{ 465 Msg msg; 466 int fid; 467 int i; 468 off_t position; 469 char c; 470 if (toc->stopupdate) { 471 toc->needscachesave = TRUE; 472 return; 473 } 474 fid = -1; 475 position = 0; 476 for (i = 0; i < toc->nummsgs; i++) { 477 msg = toc->msgs[i]; 478 if (fid < 0 && msg->changed) { 479 fid = myopen(toc->scanfile, O_RDWR, 0666); 480 (void) lseek(fid, position, SEEK_SET); 481 } 482 if (fid >= 0) { 483 c = msg->buf[MARKPOS]; 484 msg->buf[MARKPOS] = ' '; 485 (void) write(fid, msg->buf, msg->length); 486 msg->buf[MARKPOS] = c; 487 } 488 position += msg->length; 489 } 490 if (fid < 0 && toc->length != toc->origlength) 491 fid = myopen(toc->scanfile, O_RDWR, 0666); 492 if (fid >= 0) { 493 (void) ftruncate(fid, toc->length); 494 myclose(fid); 495 toc->origlength = toc->length; 496 } 497 toc->needscachesave = FALSE; 498 toc->lastreaddate = LastModifyDate(toc->scanfile); 499} 500 501 502static Boolean UpdateScanFile( 503 XtPointer client_data) /* Toc */ 504{ 505 Toc toc = (Toc)client_data; 506 int i; 507 508 if (app_resources.block_events_on_busy) ShowBusyCursor(); 509 510 TUScanFileForToc(toc); 511 TULoadTocFile(toc); 512 513 for (i=0 ; i<toc->num_scrns ; i++) 514 TURedisplayToc(toc->scrn[i]); 515 516 if (app_resources.block_events_on_busy) UnshowBusyCursor(); 517 518 return True; 519} 520 521 522void TUEnsureScanIsValidAndOpen(Toc toc, Boolean delay) 523{ 524 if (toc) { 525 TUGetFullFolderInfo(toc); 526 if (TUScanFileOutOfDate(toc)) { 527 if (!delay) 528 UpdateScanFile((XtPointer)toc); 529 else { 530 /* this is a hack to get the screen mapped before 531 * spawning the subprocess (and blocking). 532 * Need to make sure the scanfile exists at this point. 533 */ 534 int fid = myopen(toc->scanfile, O_RDWR|O_CREAT, 0666); 535 myclose(fid); 536 XtAppAddWorkProc(XtWidgetToApplicationContext(toplevel), 537 UpdateScanFile, 538 (XtPointer)toc); 539 } 540 } 541 if (toc->source == NULL) 542 TULoadTocFile(toc); 543 } 544} 545 546 547 548/* Refigure all the positions, based on which lines are visible. */ 549 550void TURefigureTocPositions(Toc toc) 551{ 552 int i; 553 Msg msg; 554 XawTextPosition position, length; 555 position = length = 0; 556 for (i=0; i<toc->nummsgs ; i++) { 557 msg = toc->msgs[i]; 558 msg->position = position; 559 if (msg->visible) position += msg->length; 560 length += msg->length; 561 } 562 toc->lastPos = position; 563 toc->length = length; 564} 565 566 567 568/* Make sure we've loaded ALL the folder info for this toc, including its 569 path and sequence lists. */ 570 571void TUGetFullFolderInfo(Toc toc) 572{ 573 if (! toc->scanfile) { 574 if (! toc->path) { 575 /* usually preset by TocFolderExists */ 576 XtAsprintf(&toc->path, "%s/%s", app_resources.mail_path, 577 toc->foldername); 578 } 579 XtAsprintf(&toc->scanfile, "%s/.xmhcache", toc->path); 580 toc->lastreaddate = LastModifyDate(toc->scanfile); 581 if (TUScanFileOutOfDate(toc)) 582 toc->validity = invalid; 583 else { 584 toc->validity = valid; 585 TULoadTocFile(toc); 586 } 587 } 588} 589 590/* Append a message to the end of the toc. It has the given scan line. This 591 routine will figure out the message number, and change the scan line 592 accordingly. */ 593 594Msg TUAppendToc(Toc toc, const char *ptr) 595{ 596 Msg msg; 597 int msgid; 598 599 TUGetFullFolderInfo(toc); 600 if (toc->validity != valid) 601 return NULL; 602 603 if (toc->nummsgs > 0) 604 msgid = toc->msgs[toc->nummsgs - 1]->msgid + 1; 605 else 606 msgid = 1; 607 (toc->nummsgs)++; 608 toc->msgs = XtReallocArray(toc->msgs, toc->nummsgs, sizeof(Msg)); 609 toc->msgs[toc->nummsgs - 1] = msg = XtNew(MsgRec); 610 bzero((char *) msg, (int) sizeof(MsgRec)); 611 msg->toc = toc; 612 msg->buf = XtNewString(ptr); 613 if (msgid >= 10000) 614 msgid %= 10000; 615 snprintf(msg->buf, strlen(msg->buf) + 1, "%4d", msgid); 616 msg->buf[MARKPOS] = ' '; 617 msg->msgid = msgid; 618 msg->position = toc->lastPos; 619 msg->length = strlen(ptr); 620 msg->changed = TRUE; 621 msg->fate = Fignore; 622 msg->desttoc = NULL; 623 if (toc->viewedseq == toc->seqlist[0]) { 624 msg->visible = TRUE; 625 toc->lastPos += msg->length; 626 } 627 else 628 msg->visible = FALSE; 629 toc->length += msg->length; 630 if ( (msg->visible) && (toc->source != NULL) ) 631 TSourceInvalid(toc, msg->position, msg->length); 632 TUSaveTocFile(toc); 633 return msg; 634} 635