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