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