|
|
1.1 ! root 1: #ifndef lint ! 2: static char rcs_id[] = "$Header: msg.c,v 1.18 87/08/20 11:14:28 swick Exp $"; ! 3: #endif lint ! 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 RIGHTS, ! 16: * APPROPRIATE LEGENDS MAY BE PLACED ON THE DERIVATIVE WORK IN ADDITION TO THAT ! 17: * SET FORTH ABOVE. ! 18: * ! 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 documentation, ! 24: * and that the name of Digital Equipment Corporation not be used in advertising ! 25: * or publicity pertaining to distribution of the software without specific, ! 26: * written prior permission. ! 27: */ ! 28: ! 29: /* msgs.c -- handle operations on messages. */ ! 30: ! 31: #include "xmh.h" ! 32: #include "tocintrnl.h" ! 33: #include <sys/file.h> ! 34: ! 35: #ifdef NOGRABFOCUS ! 36: /* Overload this Xlib routine, as the toolkit widgets use it directly. */ ! 37: ! 38: XSetInputFocus(d, w, r, t) ! 39: Display d; ! 40: Window w; ! 41: int r; ! 42: Time t; ! 43: { ! 44: } ! 45: #endif ! 46: ! 47: ! 48: /* Return the user-viewable name of the given message. */ ! 49: ! 50: static char *NameOfMsg(msg) ! 51: Msg msg; ! 52: { ! 53: static char result[100]; ! 54: (void) sprintf(result, "%s:%d", msg->toc->foldername, msg->msgid); ! 55: return result; ! 56: } ! 57: ! 58: ! 59: /* Update the message titlebar in the given scrn. */ ! 60: ! 61: static void ResetMsgLabel(scrn) ! 62: Scrn scrn; ! 63: { ! 64: Msg msg; ! 65: char str[200]; ! 66: if (scrn) { ! 67: msg = scrn->msg; ! 68: if (msg == NULL) (void) strcpy(str, Version()); ! 69: else { ! 70: (void) strcpy(str, NameOfMsg(msg)); ! 71: switch (msg->fate) { ! 72: case Fdelete: ! 73: (void) strcat(str, " -> *Delete*"); ! 74: break; ! 75: case Fcopy: ! 76: case Fmove: ! 77: (void) strcat(str, " -> "); ! 78: (void) strcat(str, msg->desttoc->foldername); ! 79: if (msg->fate == Fcopy) ! 80: (void) strcat(str, " (Copy)"); ! 81: break; ! 82: } ! 83: if (msg->temporary) (void)strcat(str, " [Temporary]"); ! 84: } ! 85: ChangeLabel(scrn->viewlabel, str); ! 86: } ! 87: } ! 88: ! 89: ! 90: /* A major msg change has occured; redisplay it. (This also should ! 91: work even if we now have a new source to display stuff from.) This ! 92: routine arranges to hide boring headers, and also will set the text ! 93: insertion point to the proper place if this is a composition and we're ! 94: viewing it for the first time. */ ! 95: ! 96: static void RedisplayMsg(scrn) ! 97: Scrn scrn; ! 98: { ! 99: Msg msg; ! 100: XtTextPosition startPos, lastPos, nextPos; ! 101: int length; char str[100]; ! 102: XtTextBlock text; ! 103: if (scrn) { ! 104: msg = scrn->msg; ! 105: if (msg) { ! 106: startPos = 0; ! 107: if (defHideBoringHeaders && scrn->kind != STcomp) { ! 108: lastPos = msg->source->getLastPos(msg->source); ! 109: while (startPos < lastPos) { ! 110: nextPos = startPos; ! 111: length = 0; ! 112: while (length < 8 && nextPos < lastPos) { ! 113: nextPos = (*msg->source->read)(msg->source, nextPos, ! 114: &text, 8 - length); ! 115: (void) strncpy(str + length, text.ptr, text.length); ! 116: length += text.length; ! 117: } ! 118: if (length == 8) { ! 119: if (strncmp(str, "From:", 5) == 0 || ! 120: strncmp(str, "To:", 3) == 0 || ! 121: strncmp(str, "Date:", 5) == 0 || ! 122: strncmp(str, "Subject:", 8) == 0) break; ! 123: } ! 124: startPos = (*msg->source->scan) ! 125: (msg->source, startPos, XtstEOL, XtsdRight, 1,TRUE); ! 126: } ! 127: } ! 128: if (startPos >= lastPos) startPos = 0; ! 129: XtTextNewSource(DISPLAY scrn->viewwindow, msg->source, startPos); ! 130: if (msg->startPos > 0) { ! 131: #ifdef X10 ! 132: /* Do an incredibly disgusting hack to make things display right. ! 133: The X10 toolkit stinks. */ ! 134: MapScrn(scrn); ! 135: EmptyEventQueue(); ! 136: #endif X10 ! 137: XtTextSetInsertionPoint(DISPLAY scrn->viewwindow, ! 138: msg->startPos); ! 139: msg->startPos = 0; /* Start in magic place only once. */ ! 140: } ! 141: } else { ! 142: XtTextNewSource(DISPLAY scrn->viewwindow, ! 143: NullSource, (XtTextPosition) 0); ! 144: } ! 145: } ! 146: } ! 147: ! 148: ! 149: ! 150: static char tempDraftFile[100] = ""; ! 151: ! 152: /* Temporarily move the draftfile somewhere else, so we can exec an mh ! 153: command that affects it. */ ! 154: ! 155: static void TempMoveDraft() ! 156: { ! 157: char *ptr; ! 158: if (FileExists(draftFile)) { ! 159: do { ! 160: ptr = MakeNewTempFileName(); ! 161: (void) strcpy(tempDraftFile, draftFile); ! 162: (void) strcpy(rindex(tempDraftFile, '/'), rindex(ptr, '/')); ! 163: } while (FileExists(tempDraftFile)); ! 164: RenameAndCheck(draftFile, tempDraftFile); ! 165: } ! 166: } ! 167: ! 168: ! 169: ! 170: /* Restore the draftfile from its temporary hiding place. */ ! 171: ! 172: static void RestoreDraft() ! 173: { ! 174: if (*tempDraftFile) { ! 175: RenameAndCheck(tempDraftFile, draftFile); ! 176: *tempDraftFile = 0; ! 177: } ! 178: } ! 179: ! 180: ! 181: ! 182: /* Public routines */ ! 183: ! 184: ! 185: /* Given a message, return the corresponding filename. */ ! 186: ! 187: char *MsgFileName(msg) ! 188: Msg msg; ! 189: { ! 190: static char result[500]; ! 191: (void) sprintf(result, "%s/%d", msg->toc->path, msg->msgid); ! 192: return result; ! 193: } ! 194: ! 195: ! 196: ! 197: /* Save any changes to a message. Also calls the toc routine to update the ! 198: scanline for this msg. */ ! 199: ! 200: void MsgSaveChanges(msg) ! 201: Msg msg; ! 202: { ! 203: if (msg->source) { ! 204: XtEDiskSaveFile(msg->source); ! 205: EnableProperButtons(msg->scrn); ! 206: if (!msg->temporary) ! 207: TocMsgChanged(msg->toc, msg); ! 208: } ! 209: } ! 210: ! 211: /*ARGSUSED*/ ! 212: static Boolean IfMapWindow(dpy, event, scrn) ! 213: Display *dpy; ! 214: XEvent *event; ! 215: Scrn scrn; ! 216: { ! 217: return (event->type == MapNotify && event->xany.window == scrn->window); ! 218: } ! 219: ! 220: /* Associate the given message with the given scrn. If a message is ! 221: changed, and we are removing it from any scrn, then ask for confirmation ! 222: first. If the message is a temporary one and it is removed from any scrn, ! 223: it is deleted. */ ! 224: ! 225: static int SetScrn(msg, scrn, force) ! 226: Msg msg; ! 227: Scrn scrn; ! 228: Boolean force; /* If TRUE, don't ask for confirm; just do it */ ! 229: { ! 230: char str[100]; ! 231: if (msg == NULL && scrn == NULL) return 0; ! 232: if (scrn && scrn->msg != msg) ! 233: if (SetScrn(scrn->msg, (Scrn) NULL, force)) ! 234: return DELETEABORTED; ! 235: if (msg == NULL) ! 236: return 0; ! 237: if (msg->scrn == scrn) return 0; ! 238: if (msg->scrn) { ! 239: if (msg->source && scrn == NULL) { ! 240: if (XtEDiskChanged(msg->source) && !force) { ! 241: (void)sprintf(str, ! 242: "Are you sure you want to remove changes to %s?", ! 243: NameOfMsg(msg)); ! 244: if (!Confirm(msg->scrn, str)) return DELETEABORTED; ! 245: } ! 246: XtDestroyEDiskSource(msg->source); ! 247: msg->source = NULL; ! 248: } ! 249: msg->scrn->msg = NULL; ! 250: ResetMsgLabel(msg->scrn); ! 251: RedisplayMsg(msg->scrn); ! 252: EnableProperButtons(msg->scrn); ! 253: if (msg->scrn->kind != STtocAndView) ! 254: QXStoreName(theDisplay, msg->scrn->window, progName); ! 255: msg->scrn = NULL; ! 256: if (scrn == NULL) { ! 257: if (msg->temporary) { ! 258: (void) unlink(MsgFileName(msg)); ! 259: TocRemoveMsg(msg->toc, msg); ! 260: MsgFree(msg); ! 261: } ! 262: return 0; ! 263: } ! 264: } ! 265: if (msg->source == NULL) ! 266: msg->source = XtCreateEDiskSource(MsgFileName(msg), XttextRead); ! 267: if (scrn->kind == STcomp) { ! 268: XtEDiskChangeEditMode(msg->source, XttextEdit); ! 269: if (defGrabFocus) { ! 270: XEvent event; ! 271: QXMapWindow(theDisplay, scrn->window); ! 272: XPeekIfEvent(theDisplay, &event, IfMapWindow, (char *)scrn); ! 273: QXSetInputFocus(theDisplay, scrn->viewwindow, ! 274: RevertToParent, CurrentTime); ! 275: } ! 276: } ! 277: ! 278: msg->scrn = scrn; ! 279: scrn->msg = msg; ! 280: ResetMsgLabel(msg->scrn); ! 281: RedisplayMsg(msg->scrn); ! 282: EnableProperButtons(msg->scrn); ! 283: if (msg->scrn->kind != STtocAndView) ! 284: QXStoreName(theDisplay, msg->scrn->window, NameOfMsg(msg)); ! 285: return 0; ! 286: } ! 287: ! 288: ! 289: /* Associate the given msg and scrn, asking for confirmation if necessary. */ ! 290: ! 291: int MsgSetScrn(msg, scrn) ! 292: Msg msg; ! 293: Scrn scrn; ! 294: { ! 295: return SetScrn(msg, scrn, FALSE); ! 296: } ! 297: ! 298: ! 299: /* Same as above, but with the extra information that the message is actually ! 300: a composition. (Nothing currently takes advantage of that extra fact.) */ ! 301: ! 302: int MsgSetScrnForComp(msg, scrn) ! 303: Msg msg; ! 304: Scrn scrn; ! 305: { ! 306: return SetScrn(msg, scrn, FALSE); ! 307: } ! 308: ! 309: ! 310: ! 311: /* Associate the given msg and scrn, even if it means losing some unsaved ! 312: changes. */ ! 313: ! 314: void MsgSetScrnForce(msg, scrn) ! 315: Msg msg; ! 316: Scrn scrn; ! 317: { ! 318: (void) SetScrn(msg, scrn, TRUE); ! 319: } ! 320: ! 321: ! 322: ! 323: /* Return what screen (if any) is displaying the given msg. */ ! 324: ! 325: Scrn MsgGetScrn(msg) ! 326: Msg msg; ! 327: { ! 328: return msg->scrn; ! 329: } ! 330: ! 331: ! 332: ! 333: /* Set the fate of the given message. */ ! 334: ! 335: void MsgSetFate(msg, fate, desttoc) ! 336: Msg msg; ! 337: FateType fate; ! 338: Toc desttoc; ! 339: { ! 340: Toc toc = msg->toc; ! 341: XtTextBlock text; ! 342: msg->fate = fate; ! 343: msg->desttoc = desttoc; ! 344: if (fate == Fignore && msg == msg->toc->curmsg) ! 345: text.ptr = "+"; ! 346: else { ! 347: switch (fate) { ! 348: case Fignore: text.ptr = " "; break; ! 349: case Fcopy: text.ptr = "C"; break; ! 350: case Fmove: text.ptr = "^"; break; ! 351: case Fdelete: text.ptr = "D"; break; ! 352: } ! 353: } ! 354: text.length = 1; ! 355: text.firstPos = msg->position + MARKPOS; ! 356: if (toc->stopupdate) ! 357: toc->needsrepaint = TRUE; ! 358: if (toc->scrn && msg->visible && !toc->needsrepaint && ! 359: *text.ptr != msg->buf[MARKPOS]) ! 360: (void) XtTextReplace(DISPLAY toc->scrn->tocwindow, ! 361: msg->position + MARKPOS, ! 362: msg->position + MARKPOS + 1, &text); ! 363: else ! 364: msg->buf[MARKPOS] = *text.ptr; ! 365: if (msg->scrn) ! 366: ResetMsgLabel(msg->scrn); ! 367: } ! 368: ! 369: ! 370: ! 371: /* Get the fate of this message. */ ! 372: ! 373: FateType MsgGetFate(msg, toc) ! 374: Msg msg; ! 375: Toc *toc; /* RETURN */ ! 376: { ! 377: if (toc) *toc = msg->desttoc; ! 378: return msg->fate; ! 379: } ! 380: ! 381: ! 382: /* Make this a temporary message. */ ! 383: ! 384: void MsgSetTemporary(msg) ! 385: Msg msg; ! 386: { ! 387: msg->temporary = TRUE; ! 388: ResetMsgLabel(msg->scrn); ! 389: } ! 390: ! 391: ! 392: /* Make this a permanent message. */ ! 393: ! 394: void MsgSetPermanent(msg) ! 395: Msg msg; ! 396: { ! 397: msg->temporary = FALSE; ! 398: ResetMsgLabel(msg->scrn); ! 399: } ! 400: ! 401: ! 402: ! 403: /* Return the id# of this message. */ ! 404: ! 405: int MsgGetId(msg) ! 406: Msg msg; ! 407: { ! 408: return msg->msgid; ! 409: } ! 410: ! 411: ! 412: /* Return the scanline for this message. */ ! 413: ! 414: char *MsgGetScanLine(msg) ! 415: Msg msg; ! 416: { ! 417: return msg->buf; ! 418: } ! 419: ! 420: ! 421: ! 422: /* Return the toc this message is in. */ ! 423: ! 424: Toc MsgGetToc(msg) ! 425: Msg msg; ! 426: { ! 427: return msg->toc; ! 428: } ! 429: ! 430: ! 431: /* Set the reapable flag for this msg. */ ! 432: ! 433: void MsgSetReapable(msg) ! 434: Msg msg; ! 435: { ! 436: msg->reapable = TRUE; ! 437: EnableProperButtons(msg->scrn); ! 438: } ! 439: ! 440: ! 441: ! 442: /* Clear the reapable flag for this msg. */ ! 443: ! 444: void MsgClearReapable(msg) ! 445: Msg msg; ! 446: { ! 447: msg->reapable = FALSE; ! 448: EnableProperButtons(msg->scrn); ! 449: } ! 450: ! 451: ! 452: /* Get the reapable value for this msg. Returns TRUE iff the reapable flag ! 453: is set AND no changes have been made. */ ! 454: ! 455: int MsgGetReapable(msg) ! 456: Msg msg; ! 457: { ! 458: return msg == NULL || (msg->reapable && ! 459: (msg->source == NULL || ! 460: !XtEDiskChanged(msg->source))); ! 461: } ! 462: ! 463: ! 464: /* Make it possible to edit the given msg. */ ! 465: void MsgSetEditable(msg) ! 466: Msg msg; ! 467: { ! 468: if (msg && msg->source) { ! 469: XtEDiskChangeEditMode(msg->source, XttextEdit); ! 470: if (defGrabFocus && msg->scrn) ! 471: QXSetInputFocus(theDisplay, msg->scrn->viewwindow, ! 472: RevertToParent, CurrentTime); ! 473: EnableProperButtons(msg->scrn); ! 474: } ! 475: } ! 476: ! 477: ! 478: ! 479: /* Turn off editing for the given msg. */ ! 480: ! 481: void MsgClearEditable(msg) ! 482: Msg msg; ! 483: { ! 484: if (msg && msg->source) { ! 485: XtEDiskChangeEditMode(msg->source, XttextRead); ! 486: EnableProperButtons(msg->scrn); ! 487: } ! 488: } ! 489: ! 490: ! 491: ! 492: /* Get whether the msg is editable. */ ! 493: ! 494: int MsgGetEditable(msg) ! 495: Msg msg; ! 496: { ! 497: return msg && msg->source && ! 498: (*msg->source->editType)(msg->source) == XttextEdit; ! 499: } ! 500: ! 501: ! 502: /* Get whether the msg has changed since last saved. */ ! 503: ! 504: int MsgChanged(msg) ! 505: Msg msg; ! 506: { ! 507: return msg && msg->source && XtEDiskChanged(msg->source); ! 508: } ! 509: ! 510: ! 511: ! 512: /* Call the given function when the msg changes. */ ! 513: ! 514: void MsgSetCallOnChange(msg, func, param) ! 515: Msg msg; ! 516: void (*func)(); ! 517: caddr_t param; ! 518: { ! 519: XtEDiskSetCallbackWhenChanged(msg->source, func, param); ! 520: } ! 521: ! 522: ! 523: ! 524: /* Call no function when the msg changes. */ ! 525: ! 526: void MsgClearCallOnChange(msg) ! 527: Msg msg; ! 528: { ! 529: XtEDiskSetCallbackWhenChanged(msg->source, NULL, (caddr_t) NULL); ! 530: } ! 531: ! 532: ! 533: /* Send (i.e., mail) the given message as is. First break it up into lines, ! 534: and copy it to a new file in the process. The new file is one of 10 ! 535: possible draft files; we rotate amoung the 10 so that the user can have up ! 536: to 10 messages being sent at once. (Using a file in /tmp is a bad idea ! 537: because these files never actually get deleted, but renamed with some ! 538: prefix. Also, these should stay in an area private to the user for ! 539: security.) */ ! 540: ! 541: void MsgSend(msg) ! 542: Msg msg; ! 543: { ! 544: FILEPTR from; ! 545: FILEPTR to; ! 546: int p, c, l, inheader, sendwidth, sendbreakwidth; ! 547: char *ptr, *ptr2, **argv, str[100]; ! 548: static sendcount = -1; ! 549: MsgSaveChanges(msg); ! 550: from = FOpenAndCheck(MsgFileName(msg), "r"); ! 551: sendcount = (sendcount + 1) % 10; ! 552: (void) sprintf(str, "%s%d", xmhDraftFile, sendcount); ! 553: to = FOpenAndCheck(str, "w"); ! 554: sendwidth = defSendLineWidth; ! 555: sendbreakwidth = defBreakSendLineWidth; ! 556: inheader = TRUE; ! 557: while (ptr = ReadLine(from)) { ! 558: if (inheader) { ! 559: if (strncmpIgnoringCase(ptr, "sendwidth:", 10) == 0) { ! 560: if (atoi(ptr+10) > 0) sendwidth = atoi(ptr+10); ! 561: continue; ! 562: } ! 563: if (strncmpIgnoringCase(ptr, "sendbreakwidth:", 15) == 0) { ! 564: if (atoi(ptr+15) > 0) sendbreakwidth = atoi(ptr+15); ! 565: continue; ! 566: } ! 567: for (l = 0, ptr2 = ptr ; *ptr2 && !l ; ptr2++) ! 568: l = (*ptr2 != ' ' && *ptr2 != '\t' && *ptr != '-'); ! 569: if (l) { ! 570: (void) fprintf(to, "%s\n", ptr); ! 571: continue; ! 572: } ! 573: inheader = FALSE; ! 574: if (sendbreakwidth < sendwidth) sendbreakwidth = sendwidth; ! 575: } ! 576: do { ! 577: for (p = c = l = 0, ptr2 = ptr; ! 578: *ptr2 && c < sendbreakwidth; ! 579: p++, ptr2++) { ! 580: if (*ptr2 == ' ' && c < sendwidth) ! 581: l = p; ! 582: if (*ptr2 == '\t') { ! 583: if (c < sendwidth) l = p; ! 584: c += 8 - (c % 8); ! 585: } ! 586: else ! 587: c++; ! 588: } ! 589: if (c < sendbreakwidth) { ! 590: (void) fprintf(to, "%s\n", ptr); ! 591: *ptr = 0; ! 592: } ! 593: else ! 594: if (l) { ! 595: ptr[l] = 0; ! 596: (void) fprintf(to, "%s\n", ptr); ! 597: ptr += l + 1; ! 598: } ! 599: else { ! 600: for (c = 0; c < sendwidth; ) { ! 601: if (*ptr == '\t') c += 8 - (c % 8); ! 602: else c++; ! 603: (void) fputc(*ptr++, to); ! 604: } ! 605: (void) fputc('\n', to); ! 606: } ! 607: } while (*ptr); ! 608: } ! 609: (void) myfclose(from); ! 610: (void) myfclose(to); ! 611: argv = MakeArgv(3); ! 612: argv[0] = "send"; ! 613: argv[1] = "-push"; ! 614: argv[2] = str; ! 615: DoCommand(argv, (char *) NULL, (char *) NULL); ! 616: XtFree((char *) argv); ! 617: } ! 618: ! 619: ! 620: /* Make the msg into the form for a generic composition. Set msg->startPos ! 621: so that the text insertion point will be placed at the end of the first ! 622: line (which is usually the "To:" field). */ ! 623: ! 624: void MsgLoadComposition(msg) ! 625: Msg msg; ! 626: { ! 627: static char *blankcomp = NULL; /* Array containing comp template */ ! 628: static int compsize = 0; ! 629: static XtTextPosition startPos; ! 630: char *file, **argv; ! 631: int fid; ! 632: if (blankcomp == NULL) { ! 633: file = MakeNewTempFileName(); ! 634: argv = MakeArgv(4); ! 635: argv[0] = "comp"; ! 636: argv[1] = "-file"; ! 637: argv[2] = file; ! 638: argv[3] = "-nowhatnowproc"; ! 639: DoCommand(argv, (char *) NULL, "/dev/null"); ! 640: XtFree((char *) argv); ! 641: compsize = GetFileLength(file); ! 642: blankcomp = XtMalloc((unsigned) compsize); ! 643: fid = myopen(file, O_RDONLY, 0666); ! 644: if (compsize != read(fid, blankcomp, compsize)) ! 645: Punt("Error reading in MsgLoadComposition!"); ! 646: (void) myclose(fid); ! 647: DeleteFileAndCheck(file); ! 648: startPos = index(blankcomp, '\n') - blankcomp; ! 649: } ! 650: fid = myopen(MsgFileName(msg), O_WRONLY | O_TRUNC | O_CREAT, 0666); ! 651: if (compsize != write(fid, blankcomp, compsize)) ! 652: Punt("Error writing in MsgLoadComposition!"); ! 653: (void) myclose(fid); ! 654: TocSetCacheValid(msg->toc); ! 655: msg->startPos = startPos; ! 656: } ! 657: ! 658: ! 659: ! 660: /* Load a msg with a template of a reply to frommsg. Set msg->startPos so ! 661: that the text insertion point will be placed at the beginning of the ! 662: message body. */ ! 663: ! 664: void MsgLoadReply(msg, frommsg) ! 665: Msg msg, frommsg; ! 666: { ! 667: char **argv; ! 668: char str1[100], str2[10]; ! 669: TempMoveDraft(); ! 670: argv = MakeArgv(4); ! 671: argv[0] = "repl"; ! 672: (void) sprintf(str1, "+%s", frommsg->toc->foldername); ! 673: argv[1] = str1; ! 674: (void) sprintf(str2, "%d", frommsg->msgid); ! 675: argv[2] = str2; ! 676: argv[3] = "-nowhatnowproc"; ! 677: DoCommand(argv, (char *) NULL, "/dev/null"); ! 678: RenameAndCheck(draftFile, MsgFileName(msg)); ! 679: RestoreDraft(); ! 680: TocSetCacheValid(frommsg->toc); /* If -anno is set, this keeps us from ! 681: rescanning folder. */ ! 682: TocSetCacheValid(msg->toc); ! 683: msg->startPos = GetFileLength(MsgFileName(msg)); ! 684: } ! 685: ! 686: ! 687: ! 688: /* Load a msg with a template of forwarding a list of messages. Set ! 689: msg->startPos so that the text insertion point will be placed at the end ! 690: of the first line (which is usually a "To:" field). */ ! 691: ! 692: void MsgLoadForward(msg, mlist) ! 693: Msg msg; ! 694: MsgList mlist; ! 695: { ! 696: char **argv, str[100]; ! 697: int i; ! 698: TempMoveDraft(); ! 699: argv = MakeArgv(3 + mlist->nummsgs); ! 700: argv[0] = "forw"; ! 701: (void) sprintf(str, "+%s", mlist->msglist[0]->toc->foldername); ! 702: argv[1] = MallocACopy(str); ! 703: for (i = 0; i < mlist->nummsgs; i++) { ! 704: (void) sprintf(str, "%d", mlist->msglist[i]->msgid); ! 705: argv[2 + i] = MallocACopy(str); ! 706: } ! 707: argv[2 + i] = "-nowhatnowproc"; ! 708: DoCommand(argv, (char *) NULL, "/dev/null"); ! 709: for (i = 1; i < 2 + mlist->nummsgs; i++) ! 710: XtFree((char *) argv[i]); ! 711: XtFree((char *) argv); ! 712: RenameAndCheck(draftFile, MsgFileName(msg)); ! 713: RestoreDraft(); ! 714: TocSetCacheValid(msg->toc); ! 715: msg->source = XtCreateEDiskSource(MsgFileName(msg), XttextEdit); ! 716: msg->startPos = (*msg->source->scan)(msg->source, 0, XtstEOL, XtsdRight, ! 717: 1, FALSE); ! 718: } ! 719: ! 720: ! 721: /* Load msg with a copy of frommsg. */ ! 722: ! 723: void MsgLoadCopy(msg, frommsg) ! 724: Msg msg, frommsg; ! 725: { ! 726: char str[500]; ! 727: (void)strcpy(str, MsgFileName(msg)); ! 728: CopyFileAndCheck(MsgFileName(frommsg), str); ! 729: TocSetCacheValid(msg->toc); ! 730: } ! 731: ! 732: ! 733: ! 734: /* Checkpoint the given message. */ ! 735: ! 736: void MsgCheckPoint(msg) ! 737: Msg msg; ! 738: { ! 739: if (msg && msg->source) { ! 740: XtEDiskMakeCheckpoint(msg->source); ! 741: TocSetCacheValid(msg->toc); ! 742: } ! 743: } ! 744: ! 745: ! 746: /* Free the storage being used by the given msg. */ ! 747: ! 748: void MsgFree(msg) ! 749: Msg msg; ! 750: { ! 751: XtFree(msg->buf); ! 752: XtFree((char *)msg); ! 753: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.