toc.c revision 66d665a3
1/* $XConsortium: toc.c,v 2.59 95/01/09 16:52:53 swick Exp $ 2 * $XFree86: xc/programs/xmh/toc.c,v 3.4 2001/10/28 03:34:39 tsi Exp $ 3 * 4 * 5 * COPYRIGHT 1987 6 * DIGITAL EQUIPMENT CORPORATION 7 * MAYNARD, MASSACHUSETTS 8 * ALL RIGHTS RESERVED. 9 * 10 * THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE AND 11 * SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT CORPORATION. 12 * DIGITAL MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR 13 * ANY PURPOSE. IT IS SUPPLIED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. 14 * 15 * IF THE SOFTWARE IS MODIFIED IN A MANNER CREATING DERIVATIVE COPYRIGHT 16 * RIGHTS, APPROPRIATE LEGENDS MAY BE PLACED ON THE DERIVATIVE WORK IN 17 * ADDITION TO THAT SET FORTH ABOVE. 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 28/* toc.c -- handle things in the toc widget. */ 29 30#include "xmh.h" 31#include "tocintrnl.h" 32#include "toc.h" 33#include "tocutil.h" 34#include "actions.h" 35 36#include <sys/stat.h> 37 38#ifndef S_ISDIR 39#define S_ISDIR(mode) ((mode & S_IFMT) == S_IFDIR) 40#endif 41 42static int IsDir(char *name) 43{ 44 char str[500]; 45 struct stat buf; 46 if (*name == '.') 47 return FALSE; 48 if (snprintf(str, sizeof(str), "%s/%s", app_resources.mail_path, name) 49 >= sizeof(str)) return False; 50 if (stat(str, &buf) /* failed */) return False; 51 return S_ISDIR(buf.st_mode); 52} 53 54 55static void MakeSureFolderExists( 56 char ***namelistptr, 57 int *numfoldersptr, 58 char *name) 59{ 60 int i; 61 char str[200]; 62 for (i=0 ; i<*numfoldersptr ; i++) 63 if (strcmp((*namelistptr)[i], name) == 0) return; 64 if (snprintf(str, sizeof(str), "%s/%s", app_resources.mail_path, name) 65 >= sizeof(str)) goto punt; 66 (void) mkdir(str, 0700); 67 *numfoldersptr = ScanDir(app_resources.mail_path, namelistptr, IsDir); 68 for (i=0 ; i<*numfoldersptr ; i++) 69 if (strcmp((*namelistptr)[i], name) == 0) return; 70 punt: 71 Punt("Can't create new mail folder!"); 72} 73 74 75static void MakeSureSubfolderExists( 76 char *** namelistptr, 77 int * numfoldersptr, 78 char * name) 79{ 80 char folder[300]; 81 char subfolder_path[300]; 82 char *subfolder; 83 struct stat buf; 84 85 /* Make sure that the parent folder exists */ 86 87 subfolder = strchr( strcpy(folder, name), '/'); 88 *subfolder = '\0'; 89 subfolder++; 90 MakeSureFolderExists(namelistptr, numfoldersptr, folder); 91 92 /* The parent folder exists. Make sure the subfolder exists. */ 93 94 if (snprintf(subfolder_path, sizeof(subfolder_path), "%s/%s", 95 app_resources.mail_path, name) >= sizeof(subfolder_path)) 96 goto punt; 97 if (stat(subfolder_path, &buf) /* failed */) { 98 (void) mkdir(subfolder_path, 0700); 99 if (stat(subfolder_path, &buf) /* failed */) 100 Punt("Can't create new xmh subfolder!"); 101 } 102 if (!S_ISDIR(buf.st_mode)) 103 punt: 104 Punt("Can't create new xmh subfolder!"); 105} 106 107int TocFolderExists(Toc toc) 108{ 109 struct stat buf; 110 if (! toc->path) { 111 XtAsprintf(&toc->path, "%s/%s", 112 app_resources.mail_path, toc->foldername); 113 } 114 return ((stat(toc->path, &buf) == 0) && 115 (S_ISDIR(buf.st_mode))); 116} 117 118static void LoadCheckFiles(void) 119{ 120 FILE *fid; 121 char str[1024]; 122 123 snprintf(str, sizeof(str), "%s/.xmhcheck", homeDir); 124 fid = myfopen(str, "r"); 125 if (fid) { 126 int i; 127 char *ptr, *ptr2; 128 129 while ((ptr = ReadLine(fid))) { 130 while (*ptr == ' ' || *ptr == '\t') ptr++; 131 ptr2 = ptr; 132 while (*ptr2 && *ptr2 != ' ' && *ptr2 != '\t') ptr2++; 133 if (*ptr2 == 0) continue; 134 *ptr2++ = 0; 135 while (*ptr2 == ' ' || *ptr2 == '\t') ptr2++; 136 if (*ptr2 == 0) continue; 137 for (i=0 ; i<numFolders ; i++) { 138 if (strcmp(ptr, folderList[i]->foldername) == 0) { 139 folderList[i]->incfile = XtNewString(ptr2); 140 break; 141 } 142 } 143 } 144 myfclose(fid); 145 } else if ( app_resources.initial_inc_file && 146 *app_resources.initial_inc_file) 147 InitialFolder->incfile = app_resources.initial_inc_file; 148} 149 150 151/* PUBLIC ROUTINES */ 152 153 154/* Read in the list of folders. */ 155 156void TocInit(void) 157{ 158 Toc toc; 159 char **namelist; 160 int i; 161 numFolders = ScanDir(app_resources.mail_path, &namelist, IsDir); 162 if (numFolders < 0) { 163 (void) mkdir(app_resources.mail_path, 0700); 164 numFolders = ScanDir(app_resources.mail_path, &namelist, IsDir); 165 if (numFolders < 0) 166 Punt("Can't create or read mail directory!"); 167 } 168 if (IsSubfolder(app_resources.initial_folder_name)) 169 MakeSureSubfolderExists(&namelist, &numFolders, 170 app_resources.initial_folder_name); 171 else 172 MakeSureFolderExists(&namelist, &numFolders, 173 app_resources.initial_folder_name); 174 175 if (IsSubfolder(app_resources.drafts_folder_name)) 176 MakeSureSubfolderExists(&namelist, &numFolders, 177 app_resources.drafts_folder_name); 178 else 179 MakeSureFolderExists(&namelist, &numFolders, 180 app_resources.drafts_folder_name); 181 folderList = XtMallocArray((Cardinal)numFolders, sizeof(Toc)); 182 for (i=0 ; i<numFolders ; i++) { 183 toc = folderList[i] = TUMalloc(); 184 toc->foldername = XtNewString(namelist[i]); 185 free((char *)namelist[i]); 186 } 187 if (! (InitialFolder = TocGetNamed(app_resources.initial_folder_name))) 188 InitialFolder = TocCreate(app_resources.initial_folder_name); 189 190 if (! (DraftsFolder = TocGetNamed(app_resources.drafts_folder_name))) 191 DraftsFolder = TocCreate(app_resources.drafts_folder_name); 192 free((char *)namelist); 193 LoadCheckFiles(); 194} 195 196 197 198/* Create a toc and add a folder to the folderList. */ 199 200Toc TocCreate(const char *foldername) 201{ 202 Toc toc = TUMalloc(); 203 204 toc->foldername = XtNewString(foldername); 205 folderList = XtReallocArray(folderList, ++numFolders, sizeof(Toc)); 206 folderList[numFolders - 1] = toc; 207 return toc; 208} 209 210 211/* Create a new folder with the given name. */ 212 213Toc TocCreateFolder(const char *foldername) 214{ 215 Toc toc; 216 char str[500]; 217 if (TocGetNamed(foldername)) return NULL; 218 if (snprintf(str, sizeof(str), "%s/%s", app_resources.mail_path, foldername) 219 >= sizeof(str)) return NULL; 220 if (mkdir(str, 0700) < 0) return NULL; 221 toc = TocCreate(foldername); 222 return toc; 223} 224 225int TocHasMail(Toc toc) 226{ 227 return toc->mailpending; 228} 229 230static int CheckForNewMail(Toc toc) 231{ 232 if (toc->incfile) 233 return (GetFileLength(toc->incfile) > 0); 234 else if (toc == InitialFolder) { 235 char **argv; 236 char *result; 237 int hasmail; 238 239 argv = MakeArgv(4); 240 argv[0] = "msgchk"; 241 argv[1] = "-nonotify"; 242 argv[2] = "nomail"; 243 argv[3] = "-nodate"; 244 result = DoCommandToString(argv); 245 hasmail = (*result != '\0'); 246 XtFree(result); 247 XtFree((char*)argv); 248 return hasmail; 249 } 250 return False; 251} 252 253/*ARGSUSED*/ 254void TocCheckForNewMail( 255 Boolean update) /* if True, actually make the check */ 256{ 257 Toc toc; 258 Scrn scrn; 259 int i, j, hasmail; 260 Boolean mail_waiting = False; 261 262 if (update) { 263 for (i=0 ; i<numFolders ; i++) { 264 toc = folderList[i]; 265 if (TocCanIncorporate(toc)) { 266 toc->mailpending = hasmail = CheckForNewMail(toc); 267 if (hasmail) mail_waiting = True; 268 for (j=0 ; j<numScrns ; j++) { 269 scrn = scrnList[j]; 270 if (scrn->kind == STtocAndView) 271 /* give visual indication of new mail waiting */ 272 BBoxMailFlag(scrn->folderbuttons, TocName(toc), 273 hasmail); 274 } 275 } 276 } 277 } else { 278 for (i=0; i < numFolders; i++) { 279 toc = folderList[i]; 280 if (toc->mailpending) { 281 mail_waiting = True; 282 break; 283 } 284 } 285 } 286 287 if (app_resources.mail_waiting_flag) { 288 Arg args[1]; 289 static Boolean icon_state = -1; 290 291 if (icon_state != mail_waiting) { 292 icon_state = mail_waiting; 293 for (i=0; i < numScrns; i++) { 294 scrn = scrnList[i]; 295 if (scrn->kind == STtocAndView) { 296 XtSetArg(args[0], XtNiconPixmap, 297 (mail_waiting ? app_resources.new_mail_icon 298 : app_resources.no_mail_icon)); 299 XtSetValues(scrn->parent, args, (Cardinal)1); 300 } 301 } 302 } 303 } 304} 305 306/* Intended to support mutual exclusion on deleting folders, so that you 307 * cannot have two confirm popups at the same time on the same folder. 308 * 309 * You can have confirm popups on different folders simultaneously. 310 * However, I did not protect the user from popping up a delete confirm 311 * popup on folder A, then popping up a delete confirm popup on folder 312 * A/subA, then deleting A, then deleting A/subA -- which of course is 313 * already gone, and will cause xmh to Punt. 314 * 315 * TocClearDeletePending is a callback from the No confirmation button 316 * of the confirm popup. 317 */ 318 319Boolean TocTestAndSetDeletePending(Toc toc) 320{ 321 Boolean flag; 322 323 flag = toc->delete_pending; 324 toc->delete_pending = True; 325 return flag; 326} 327 328void TocClearDeletePending(Toc toc) 329{ 330 toc->delete_pending = False; 331} 332 333 334/* Recursively delete an entire directory. Nasty. */ 335 336static void NukeDirectory(char *path) 337{ 338 struct stat buf; 339 340#ifdef S_IFLNK 341 /* POSIX.1 does not discuss symbolic links. */ 342 if (lstat(path, &buf) /* failed */) 343 return; 344 if ((buf.st_mode & S_IFMT) == S_IFLNK) { 345 (void) unlink(path); 346 return; 347 } 348#endif 349 if (stat(path, &buf) /* failed */) 350 return; 351 if (buf.st_mode & S_IWRITE) { 352 char **argv = MakeArgv(3); 353 argv[0] = "/bin/rm"; 354 argv[1] = "-rf"; 355 argv[2] = path; 356 (void) DoCommand(argv, (char*)NULL, (char*)NULL); 357 XtFree((char*)argv); 358 } 359} 360 361 362/* Destroy the given folder. */ 363 364void TocDeleteFolder(Toc toc) 365{ 366 Toc toc2; 367 int i, j, w; 368 if (toc == NULL) return; 369 TUGetFullFolderInfo(toc); 370 371 w = -1; 372 for (i=0 ; i<numFolders ; i++) { 373 toc2 = folderList[i]; 374 if (toc2 == toc) 375 w = i; 376 else if (toc2->validity == valid) 377 for (j=0 ; j<toc2->nummsgs ; j++) 378 if (toc2->msgs[j]->desttoc == toc) 379 MsgSetFate(toc2->msgs[j], Fignore, (Toc) NULL); 380 } 381 if (w < 0) Punt("Couldn't find it in TocDeleteFolder!"); 382 NukeDirectory(toc->path); 383 if (toc->validity == valid) { 384 for (i=0 ; i<toc->nummsgs ; i++) { 385 MsgSetScrnForce(toc->msgs[i], (Scrn) NULL); 386 MsgFree(toc->msgs[i]); 387 } 388 XtFree((char *) toc->msgs); 389 } 390 XtFree((char *)toc); 391 numFolders--; 392 for (i=w ; i<numFolders ; i++) folderList[i] = folderList[i+1]; 393} 394 395 396/* 397 * Display the given toc in the given scrn. If scrn is NULL, then remove the 398 * toc from all scrns displaying it. 399 */ 400 401void TocSetScrn(Toc toc, Scrn scrn) 402{ 403 Cardinal i; 404 405 if (toc == NULL && scrn == NULL) return; 406 if (scrn == NULL) { 407 for (i=0 ; i<toc->num_scrns ; i++) 408 TocSetScrn((Toc) NULL, toc->scrn[i]); 409 return; 410 } 411 if (scrn->toc == toc) return; 412 if (scrn->toc != NULL) { 413 for (i=0 ; i<scrn->toc->num_scrns ; i++) 414 if (scrn->toc->scrn[i] == scrn) break; 415 if (i >= scrn->toc->num_scrns) 416 Punt("Couldn't find scrn in TocSetScrn!"); 417 scrn->toc->scrn[i] = scrn->toc->scrn[--scrn->toc->num_scrns]; 418 } 419 scrn->toc = toc; 420 if (toc == NULL) { 421 TUResetTocLabel(scrn); 422 TURedisplayToc(scrn); 423 StoreWindowName(scrn, progName); 424 } else { 425 toc->num_scrns++; 426 toc->scrn = XtReallocArray(toc->scrn, toc->num_scrns, sizeof(Scrn)); 427 toc->scrn[toc->num_scrns - 1] = scrn; 428 TUEnsureScanIsValidAndOpen(toc, True); 429 TUResetTocLabel(scrn); 430 if (app_resources.prefix_wm_and_icon_name) { 431 char wm_name[64]; 432 snprintf(wm_name, sizeof(wm_name), "%s: %s", 433 progName, toc->foldername); 434 StoreWindowName(scrn, wm_name); 435 } 436 else 437 StoreWindowName(scrn, toc->foldername); 438 TURedisplayToc(scrn); 439 SetCurrentFolderName(scrn, toc->foldername); 440 } 441 EnableProperButtons(scrn); 442} 443 444 445 446/* Remove the given message from the toc. Doesn't actually touch the file. 447 Also note that it does not free the storage for the msg. */ 448 449void TocRemoveMsg(Toc toc, Msg msg) 450{ 451 Msg newcurmsg; 452 MsgList mlist; 453 int i; 454 if (toc->validity == unknown) 455 TUGetFullFolderInfo(toc); 456 if (toc->validity != valid) 457 return; 458 newcurmsg = TocMsgAfter(toc, msg); 459 if (newcurmsg) newcurmsg->changed = TRUE; 460 newcurmsg = toc->curmsg; 461 if (msg == toc->curmsg) { 462 newcurmsg = TocMsgAfter(toc, msg); 463 if (newcurmsg == NULL) newcurmsg = TocMsgBefore(toc, msg); 464 toc->curmsg = NULL; 465 } 466 toc->length -= msg->length; 467 if (msg->visible) toc->lastPos -= msg->length; 468 for(i = TUGetMsgPosition(toc, msg), toc->nummsgs--; i<toc->nummsgs ; i++) { 469 toc->msgs[i] = toc->msgs[i+1]; 470 if (msg->visible) toc->msgs[i]->position -= msg->length; 471 } 472 for (i=0 ; i<toc->numsequences ; i++) { 473 mlist = toc->seqlist[i]->mlist; 474 if (mlist) DeleteMsgFromMsgList(mlist, msg); 475 } 476 477 if (msg->visible && toc->num_scrns > 0 && !toc->needsrepaint) 478 TSourceInvalid(toc, msg->position, -msg->length); 479 TocSetCurMsg(toc, newcurmsg); 480 TUSaveTocFile(toc); 481} 482 483 484 485void TocRecheckValidity(Toc toc) 486{ 487 Cardinal i; 488 489 if (toc && toc->validity == valid && TUScanFileOutOfDate(toc)) { 490 if (app_resources.block_events_on_busy) ShowBusyCursor(); 491 492 TUScanFileForToc(toc); 493 if (toc->source) 494 TULoadTocFile(toc); 495 for (i=0 ; i<toc->num_scrns ; i++) 496 TURedisplayToc(toc->scrn[i]); 497 498 if (app_resources.block_events_on_busy) UnshowBusyCursor(); 499 } 500} 501 502 503/* Set the current message. */ 504 505void TocSetCurMsg(Toc toc, Msg msg) 506{ 507 Msg msg2; 508 Cardinal i; 509 510 if (toc->validity != valid) return; 511 if (msg != toc->curmsg) { 512 msg2 = toc->curmsg; 513 toc->curmsg = msg; 514 if (msg2) 515 MsgSetFate(msg2, msg2->fate, msg2->desttoc); 516 } 517 if (msg) { 518 MsgSetFate(msg, msg->fate, msg->desttoc); 519 if (toc->num_scrns) { 520 if (toc->stopupdate) 521 toc->needsrepaint = TRUE; 522 else { 523 for (i=0 ; i<toc->num_scrns ; i++) 524 XawTextSetInsertionPoint(toc->scrn[i]->tocwidget, 525 msg->position); 526 } 527 } 528 } 529} 530 531 532/* Return the current message. */ 533 534Msg TocGetCurMsg(Toc toc) 535{ 536 return toc->curmsg; 537} 538 539 540 541 542/* Return the message after the given one. (If none, return NULL.) */ 543 544Msg TocMsgAfter(Toc toc, Msg msg) 545{ 546 int i; 547 i = TUGetMsgPosition(toc, msg); 548 do { 549 i++; 550 if (i >= toc->nummsgs) 551 return NULL; 552 } while (!(toc->msgs[i]->visible)); 553 return toc->msgs[i]; 554} 555 556 557 558/* Return the message before the given one. (If none, return NULL.) */ 559 560Msg TocMsgBefore(Toc toc, Msg msg) 561{ 562 int i; 563 i = TUGetMsgPosition(toc, msg); 564 do { 565 i--; 566 if (i < 0) 567 return NULL; 568 } while (!(toc->msgs[i]->visible)); 569 return toc->msgs[i]; 570} 571 572 573 574/* The caller KNOWS the toc's information is out of date; rescan it. */ 575 576void TocForceRescan(Toc toc) 577{ 578 register Cardinal i; 579 580 if (toc->num_scrns) { 581 toc->viewedseq = toc->seqlist[0]; 582 for (i=0 ; i<toc->num_scrns ; i++) 583 TUResetTocLabel(toc->scrn[i]); 584 TUScanFileForToc(toc); 585 TULoadTocFile(toc); 586 for (i=0 ; i<toc->num_scrns ; i++) 587 TURedisplayToc(toc->scrn[i]); 588 } else { 589 TUGetFullFolderInfo(toc); 590 (void) unlink(toc->scanfile); 591 toc->validity = invalid; 592 } 593} 594 595 596 597/* The caller has just changed a sequence list. Reread them from mh. */ 598 599void TocReloadSeqLists(Toc toc) 600{ 601 Cardinal i; 602 603 TocSetCacheValid(toc); 604 TULoadSeqLists(toc); 605 TURefigureWhatsVisible(toc); 606 for (i=0 ; i<toc->num_scrns ; i++) { 607 TUResetTocLabel(toc->scrn[i]); 608 EnableProperButtons(toc->scrn[i]); 609 } 610} 611 612 613/*ARGSUSED*/ 614void XmhReloadSeqLists( 615 Widget w, 616 XEvent *event, 617 String *params, 618 Cardinal *num_params) 619{ 620 Scrn scrn = ScrnFromWidget(w); 621 TocReloadSeqLists(scrn->toc); 622 TUCheckSequenceMenu(scrn->toc); 623} 624 625 626 627/* Return TRUE if the toc has an interesting sequence. */ 628 629int TocHasSequences(Toc toc) 630{ 631 return toc && toc->numsequences > 1; 632} 633 634 635/* Change which sequence is being viewed. */ 636 637void TocChangeViewedSeq(Toc toc, Sequence seq) 638{ 639 if (seq == NULL) seq = toc->viewedseq; 640 toc->viewedseq = seq; 641 toc->force_reset = True; /* %%% force Text source to be reset */ 642 TURefigureWhatsVisible(toc); 643} 644 645 646/* Return the sequence with the given name in the given toc. */ 647 648Sequence TocGetSeqNamed(Toc toc, const char *name) 649{ 650 register int i; 651 if (name == NULL) 652 return (Sequence) NULL; 653 654 for (i=0 ; i<toc->numsequences ; i++) 655 if (strcmp(toc->seqlist[i]->name, name) == 0) 656 return toc->seqlist[i]; 657 return (Sequence) NULL; 658} 659 660 661/* Return the sequence currently being viewed in the toc. */ 662 663Sequence TocViewedSequence(Toc toc) 664{ 665 return toc->viewedseq; 666} 667 668 669/* Set the selected sequence in the toc */ 670 671void TocSetSelectedSequence( 672 Toc toc, 673 Sequence sequence) 674{ 675 if (toc) 676 toc->selectseq = sequence; 677} 678 679 680/* Return the sequence currently selected */ 681 682Sequence TocSelectedSequence(Toc toc) 683{ 684 if (toc) return (toc->selectseq); 685 else return (Sequence) NULL; 686} 687 688 689/* Return the list of messages currently selected. */ 690 691#define SrcScan XawTextSourceScan 692 693MsgList TocCurMsgList(Toc toc) 694{ 695 MsgList result; 696 XawTextPosition pos1, pos2; 697 698 if (toc->num_scrns == 0) return NULL; 699 result = MakeNullMsgList(); 700 XawTextGetSelectionPos( toc->scrn[0]->tocwidget, &pos1, &pos2); /* %%% */ 701 if (pos1 < pos2) { 702 pos1 = SrcScan(toc->source, pos1, XawstEOL, XawsdLeft, 1, FALSE); 703 pos2 = SrcScan(toc->source, pos2, XawstPositions, XawsdLeft, 1, TRUE); 704 pos2 = SrcScan(toc->source, pos2, XawstEOL, XawsdRight, 1, FALSE); 705 while (pos1 < pos2) { 706 AppendMsgList(result, MsgFromPosition(toc, pos1, XawsdRight)); 707 pos1 = SrcScan(toc->source, pos1, XawstEOL, XawsdRight, 1, TRUE); 708 } 709 } 710 return result; 711} 712 713 714 715/* Unset the current selection. */ 716 717void TocUnsetSelection(Toc toc) 718{ 719 if (toc->source) 720 XawTextUnsetSelection(toc->scrn[0]->tocwidget); 721} 722 723 724 725/* Create a brand new, blank message. */ 726 727Msg TocMakeNewMsg(Toc toc) 728{ 729 Msg msg; 730 static int looping = False; 731 TUEnsureScanIsValidAndOpen(toc, False); 732 msg = TUAppendToc(toc, "#### empty\n"); 733 if (FileExists(MsgFileName(msg))) { 734 if (looping++) Punt( "Cannot correct scan file" ); 735 DEBUG2("**** FOLDER %s WAS INVALID; msg %d already existed!\n", 736 toc->foldername, msg->msgid); 737 TocForceRescan(toc); 738 return TocMakeNewMsg(toc); /* Try again. Using recursion here is ugly, 739 but what the hack ... */ 740 } 741 CopyFileAndCheck("/dev/null", MsgFileName(msg)); 742 looping = False; 743 return msg; 744} 745 746 747/* Set things to not update cache or display until further notice. */ 748 749void TocStopUpdate(Toc toc) 750{ 751 Cardinal i; 752 753 for (i=0 ; i<toc->num_scrns ; i++) 754 XawTextDisableRedisplay(toc->scrn[i]->tocwidget); 755 toc->stopupdate++; 756} 757 758 759/* Start updating again, and do whatever updating has been queued. */ 760 761void TocStartUpdate(Toc toc) 762{ 763 Cardinal i; 764 765 if (toc->stopupdate && --(toc->stopupdate) == 0) { 766 for (i=0 ; i<toc->num_scrns ; i++) { 767 if (toc->needsrepaint) 768 TURedisplayToc(toc->scrn[i]); 769 if (toc->needslabelupdate) 770 TUResetTocLabel(toc->scrn[i]); 771 } 772 if (toc->needscachesave) 773 TUSaveTocFile(toc); 774 } 775 for (i=0 ; i<toc->num_scrns ; i++) 776 XawTextEnableRedisplay(toc->scrn[i]->tocwidget); 777} 778 779 780 781/* Something has happened that could later convince us that our cache is out 782 of date. Make this not happen; our cache really *is* up-to-date. */ 783 784void TocSetCacheValid(Toc toc) 785{ 786 TUSaveTocFile(toc); 787} 788 789 790/* Return the full folder pathname of the given toc, prefixed w/'+' */ 791 792char *TocMakeFolderName(Toc toc) 793{ 794 char* name; 795 XtAsprintf(&name, "+%s", toc->path); 796 return name; 797} 798 799char *TocName(Toc toc) 800{ 801 return toc->foldername; 802} 803 804 805 806/* Given a foldername, return the corresponding toc. */ 807 808Toc TocGetNamed(const char *name) 809{ 810 int i; 811 for (i=0; i<numFolders ; i++) 812 if (strcmp(folderList[i]->foldername, name) == 0) return folderList[i]; 813 return NULL; 814} 815 816 817Boolean TocHasChanges(Toc toc) 818{ 819 int i; 820 for (i=0 ; i<toc->nummsgs ; i++) 821 if (toc->msgs[i]->fate != Fignore) return True; 822 823 return False; 824} 825 826 827 828/* Throw out all changes to this toc, and close all views of msgs in it. 829 Requires confirmation by the user. */ 830 831/*ARGSUSED*/ 832static void TocCataclysmOkay( 833 Widget widget, /* unused */ 834 XtPointer client_data, 835 XtPointer call_data) /* unused */ 836{ 837 Toc toc = (Toc) client_data; 838 register int i; 839 840 for (i=0; i < toc->nummsgs; i++) 841 MsgSetFate(toc->msgs[i], Fignore, (Toc)NULL); 842 843/* Doesn't make sense to have this MsgSetScrn for loop here. dmc. %%% */ 844 for (i=0; i < toc->nummsgs; i++) 845 MsgSetScrn(toc->msgs[i], (Scrn) NULL, (XtCallbackList) NULL, 846 (XtCallbackList) NULL); 847} 848 849int TocConfirmCataclysm( 850 Toc toc, 851 XtCallbackList confirms, 852 XtCallbackList cancels) 853{ 854 register int i; 855 856 static XtCallbackRec yes_callbacks[] = { 857 {TocCataclysmOkay, (XtPointer) NULL}, 858 {(XtCallbackProc) NULL, (XtPointer) NULL}, 859 {(XtCallbackProc) NULL, (XtPointer) NULL} 860 }; 861 862 if (! toc) 863 return 0; 864 865 if (TocHasChanges(toc)) { 866 char str[300]; 867 Widget tocwidget; 868 869 snprintf(str, sizeof(str), 870 "Are you sure you want to remove all changes to %s?", 871 toc->foldername); 872 yes_callbacks[0].closure = (XtPointer) toc; 873 yes_callbacks[1].callback = confirms[0].callback; 874 yes_callbacks[1].closure = confirms[0].closure; 875 876 tocwidget = NULL; 877 for (i=0; i < toc->num_scrns; i++) 878 if (toc->scrn[i]->mapped) { 879 tocwidget = toc->scrn[i]->tocwidget; 880 break; 881 } 882 883 PopupConfirm(tocwidget, str, yes_callbacks, cancels); 884 return NEEDS_CONFIRMATION; 885 } 886 else { 887/* Doesn't make sense to have this MsgSetFate for loop here. dmc. %%% */ 888 for (i=0 ; i<toc->nummsgs ; i++) 889 MsgSetFate(toc->msgs[i], Fignore, (Toc)NULL); 890 891 for (i=0 ; i<toc->nummsgs ; i++) 892 if (MsgSetScrn(toc->msgs[i], (Scrn) NULL, confirms, cancels)) 893 return NEEDS_CONFIRMATION; 894 return 0; 895 } 896} 897 898 899/* Commit all the changes in this toc; all messages will meet their 'fate'. */ 900 901/*ARGSUSED*/ 902void TocCommitChanges( 903 Widget widget, /* unused */ 904 XtPointer client_data, 905 XtPointer call_data) /* unused */ 906{ 907 Toc toc = (Toc) client_data; 908 Msg msg; 909 int i, cur = 0; 910 char str[100], **argv = NULL; 911 FateType curfate, fate; 912 Toc desttoc; 913 Toc curdesttoc = NULL; 914 XtCallbackRec confirms[2]; 915 916 confirms[0].callback = TocCommitChanges; 917 confirms[0].closure = (XtPointer) toc; 918 confirms[1].callback = (XtCallbackProc) NULL; 919 confirms[1].closure = (XtPointer) NULL; 920 921 if (toc == NULL) return; 922 for (i=0 ; i<toc->nummsgs ; i++) { 923 msg = toc->msgs[i]; 924 fate = MsgGetFate(msg, (Toc *)NULL); 925 if (fate != Fignore && fate != Fcopy) 926 if (MsgSetScrn(msg, (Scrn) NULL, confirms, (XtCallbackList) NULL) 927 == NEEDS_CONFIRMATION) 928 return; 929 } 930 XFlush(XtDisplay(toc->scrn[0]->parent)); 931 for (i=0 ; i<numFolders ; i++) 932 TocStopUpdate(folderList[i]); 933 toc->haschanged = TRUE; 934 if (app_resources.block_events_on_busy) ShowBusyCursor(); 935 936 do { 937 curfate = Fignore; 938 i = 0; 939 while (i < toc->nummsgs) { 940 msg = toc->msgs[i]; 941 fate = MsgGetFate(msg, &desttoc); 942 if (curfate == Fignore && fate != Fignore) { 943 curfate = fate; 944 argv = MakeArgv(2); 945 switch (curfate) { 946 case Fdelete: 947 argv[0] = XtNewString("rmm"); 948 argv[1] = TocMakeFolderName(toc); 949 cur = 2; 950 curdesttoc = NULL; 951 break; 952 case Fmove: 953 case Fcopy: 954 argv[0] = XtNewString("refile"); 955 cur = 1; 956 curdesttoc = desttoc; 957 break; 958 default: 959 break; 960 } 961 } 962 if (curfate != Fignore && 963 curfate == fate && desttoc == curdesttoc) { 964 argv = ResizeArgv(argv, cur + 1); 965 snprintf(str, sizeof(str), "%d", MsgGetId(msg)); 966 argv[cur++] = XtNewString(str); 967 MsgSetFate(msg, Fignore, (Toc)NULL); 968 if (curdesttoc) { 969 (void) TUAppendToc(curdesttoc, MsgGetScanLine(msg)); 970 curdesttoc->haschanged = TRUE; 971 } 972 if (curfate != Fcopy) { 973 TocRemoveMsg(toc, msg); 974 MsgFree(msg); 975 i--; 976 } 977 if (cur > 40) 978 break; /* Do only 40 at a time, just to be safe. */ 979 } 980 i++; 981 } 982 if (curfate != Fignore) { 983 switch (curfate) { 984 case Fmove: 985 case Fcopy: 986 argv = ResizeArgv(argv, cur + 4); 987 argv[cur++] = XtNewString(curfate == Fmove ? "-nolink" 988 : "-link"); 989 argv[cur++] = XtNewString("-src"); 990 argv[cur++] = TocMakeFolderName(toc); 991 argv[cur++] = TocMakeFolderName(curdesttoc); 992 break; 993 default: 994 break; 995 } 996 if (app_resources.debug) { 997 for (i = 0; i < cur; i++) 998 (void) fprintf(stderr, "%s ", argv[i]); 999 (void) fprintf(stderr, "\n"); 1000 (void) fflush(stderr); 1001 } 1002 DoCommand(argv, (char *) NULL, (char *) NULL); 1003 for (i = 0; argv[i]; i++) 1004 XtFree((char *) argv[i]); 1005 XtFree((char *) argv); 1006 } 1007 } while (curfate != Fignore); 1008 for (i=0 ; i<numFolders ; i++) { 1009 if (folderList[i]->haschanged) { 1010 TocReloadSeqLists(folderList[i]); 1011 folderList[i]->haschanged = FALSE; 1012 } 1013 TocStartUpdate(folderList[i]); 1014 } 1015 1016 if (app_resources.block_events_on_busy) UnshowBusyCursor(); 1017} 1018 1019 1020 1021/* Return whether the given toc can incorporate mail. */ 1022 1023int TocCanIncorporate(Toc toc) 1024{ 1025 return (toc && (toc == InitialFolder || toc->incfile)); 1026} 1027 1028 1029/* Incorporate new messages into the given toc. */ 1030 1031int TocIncorporate(Toc toc) 1032{ 1033 char **argv; 1034 char str[100], *file, *ptr; 1035 Msg msg, firstmessage = NULL; 1036 FILEPTR fid; 1037 1038 argv = MakeArgv(toc->incfile ? 7 : 4); 1039 argv[0] = "inc"; 1040 argv[1] = TocMakeFolderName(toc); 1041 argv[2] = "-width"; 1042 snprintf(str, sizeof(str), "%d", app_resources.toc_width); 1043 argv[3] = str; 1044 if (toc->incfile) { 1045 argv[4] = "-file"; 1046 argv[5] = toc->incfile; 1047 argv[6] = "-truncate"; 1048 } 1049 if (app_resources.block_events_on_busy) ShowBusyCursor(); 1050 1051 file = DoCommandToFile(argv); 1052 XtFree(argv[1]); 1053 XtFree((char *)argv); 1054 TUGetFullFolderInfo(toc); 1055 if (toc->validity == valid) { 1056 fid = FOpenAndCheck(file, "r"); 1057 TocStopUpdate(toc); 1058 while ((ptr = ReadLineWithCR(fid))) { 1059 if (atoi(ptr) > 0) { 1060 msg = TUAppendToc(toc, ptr); 1061 if (firstmessage == NULL) firstmessage = msg; 1062 } 1063 } 1064 if (firstmessage && firstmessage->visible) { 1065 TocSetCurMsg(toc, firstmessage); 1066 } 1067 TocStartUpdate(toc); 1068 myfclose(fid); 1069 } 1070 DeleteFileAndCheck(file); 1071 1072 if (app_resources.block_events_on_busy) UnshowBusyCursor(); 1073 1074 toc->mailpending = False; 1075 return (firstmessage != NULL); 1076} 1077 1078 1079/* The given message has changed. Rescan it and change the scanfile. */ 1080 1081void TocMsgChanged(Toc toc, Msg msg) 1082{ 1083 char **argv, str[100], str2[10], *ptr; 1084 int length, delta; 1085 int i; 1086 FateType fate; 1087 Toc desttoc; 1088 1089 if (toc->validity != valid) return; 1090 fate = MsgGetFate(msg, &desttoc); 1091 MsgSetFate(msg, Fignore, (Toc) NULL); 1092 argv = MakeArgv(6); 1093 argv[0] = "scan"; 1094 argv[1] = TocMakeFolderName(toc); 1095 snprintf(str, sizeof(str), "%d", msg->msgid); 1096 argv[2] = str; 1097 argv[3] = "-width"; 1098 snprintf(str2, sizeof(str2), "%d", app_resources.toc_width); 1099 argv[4] = str2; 1100 argv[5] = "-noheader"; 1101 ptr = DoCommandToString(argv); 1102 XtFree(argv[1]); 1103 XtFree((char *) argv); 1104 if (strcmp(ptr, msg->buf) != 0) { 1105 length = strlen(ptr); 1106 delta = length - msg->length; 1107 XtFree(msg->buf); 1108 msg->buf = ptr; 1109 msg->length = length; 1110 toc->length += delta; 1111 if (msg->visible) { 1112 if (delta != 0) { 1113 for (i=TUGetMsgPosition(toc, msg)+1; i<toc->nummsgs ; i++) 1114 toc->msgs[i]->position += delta; 1115 toc->lastPos += delta; 1116 } 1117 for (i=0 ; i<toc->num_scrns ; i++) 1118 TURedisplayToc(toc->scrn[i]); 1119 } 1120 MsgSetFate(msg, fate, desttoc); 1121 TUSaveTocFile(toc); 1122 } else XtFree(ptr); 1123} 1124 1125 1126 1127Msg TocMsgFromId(Toc toc, int msgid) 1128{ 1129 int h, l, m; 1130 l = 0; 1131 h = toc->nummsgs - 1; 1132 if (h < 0) { 1133 DEBUG1("Toc is empty! folder=%s\n", toc->foldername) 1134 return NULL; 1135 } 1136 while (l < h - 1) { 1137 m = (l + h) / 2; 1138 if (toc->msgs[m]->msgid > msgid) 1139 h = m; 1140 else 1141 l = m; 1142 } 1143 if (toc->msgs[l]->msgid == msgid) return toc->msgs[l]; 1144 if (toc->msgs[h]->msgid == msgid) return toc->msgs[h]; 1145 if (app_resources.debug) { 1146 char str[100]; 1147 snprintf(str, sizeof(str), 1148 "TocMsgFromId search failed! hi=%d, lo=%d, msgid=%d\n", 1149 h, l, msgid); 1150 DEBUG( str ) 1151 } 1152 return NULL; 1153} 1154 1155/* Sequence names are put on a stack which is specific to the folder. 1156 * Sequence names are very volatile, so we make our own copies of the strings. 1157 */ 1158 1159/*ARGSUSED*/ 1160void XmhPushSequence( 1161 Widget w, 1162 XEvent *event, 1163 String *params, 1164 Cardinal *count) 1165{ 1166 Scrn scrn = ScrnFromWidget(w); 1167 Toc toc; 1168 Cardinal i; 1169 1170 if (! (toc = scrn->toc)) return; 1171 1172 if (*count == 0) { 1173 if (toc->selectseq) 1174 Push(&toc->sequence_stack, XtNewString(toc->selectseq->name)); 1175 } 1176 else 1177 for (i=0; i < *count; i++) 1178 Push(&toc->sequence_stack, XtNewString(params[i])); 1179} 1180 1181 1182/*ARGSUSED*/ 1183void XmhPopSequence( 1184 Widget w, /* any widget on the screen of interest */ 1185 XEvent *event, 1186 String *params, 1187 Cardinal *count) 1188{ 1189 Scrn scrn = ScrnFromWidget(w); 1190 const char *seqname; 1191 Widget sequenceMenu, selected, original; 1192 Button button; 1193 Sequence sequence; 1194 1195 if ((seqname = Pop(&scrn->toc->sequence_stack)) != NULL) { 1196 1197 button = BBoxFindButtonNamed(scrn->mainbuttons, 1198 MenuBoxButtons[XMH_SEQUENCE].button_name); 1199 sequenceMenu = BBoxMenuOfButton(button); 1200 1201 if ((selected = XawSimpleMenuGetActiveEntry(sequenceMenu))) 1202 ToggleMenuItem(selected, False); 1203 1204 if ((original = XtNameToWidget(sequenceMenu, seqname))) { 1205 ToggleMenuItem(original, True); 1206 sequence = TocGetSeqNamed(scrn->toc, seqname); 1207 TocSetSelectedSequence(scrn->toc, sequence); 1208 } 1209 XtFree((char *)seqname); 1210 } 1211} 1212