1/* 2 * $XConsortium: msg.c /main/2 1996/01/14 16:51:45 kaleb $ 3 * 4 * 5 * COPYRIGHT 1987, 1989 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/* $XFree86: xc/programs/xmh/msg.c,v 1.4 2002/04/05 21:06:28 dickey Exp $ */ 28 29/* msgs.c -- handle operations on messages. */ 30 31#include "xmh.h" 32#include "tocintrnl.h" 33#include "actions.h" 34 35#include <X11/Xaw/Cardinals.h> 36 37static int SetScrn(Msg, Scrn, Boolean, XtCallbackList, XtCallbackList); 38 39/* Function Name: SetEditable 40 * Description: Sets the editable flag for this message. 41 * Arguments: msg - the message. 42 * edit - set editable to this. 43 * Returns: none 44 */ 45 46static void 47SetEditable(Msg msg, Boolean edit) 48{ 49 Arg args[1]; 50 51 if (edit) 52 XtSetArg(args[0], XtNeditType, XawtextEdit); 53 else 54 XtSetArg(args[0], XtNeditType, XawtextRead); 55 56 XtSetValues(msg->source, args, ONE); 57} 58 59/* Function Name: IsEditable 60 * Description: Returns true if this is an editable message. 61 * Arguments: msg - the message to edit. 62 * Returns: TRUE if editable. 63 */ 64 65static Boolean 66IsEditable(Msg msg) 67{ 68 Arg args[1]; 69 XawTextEditType type; 70 71 XtSetArg(args[0], XtNeditType, &type); 72 XtGetValues(msg->source, args, ONE); 73 74 return(type == XawtextEdit); 75} 76 77/* Return the user-viewable name of the given message. */ 78 79char *MsgName(Msg msg) 80{ 81 static char result[100]; 82 snprintf(result, sizeof(result), "%s:%d", msg->toc->foldername, msg->msgid); 83 return result; 84} 85 86 87/* Update the message titlebar in the given scrn. */ 88 89static void ResetMsgLabel(Scrn scrn) 90{ 91 Msg msg; 92 char str[200]; 93 if (scrn) { 94 msg = scrn->msg; 95 if (msg == NULL) (void) strcpy(str, app_resources.banner); 96 else { 97 (void) strcpy(str, MsgName(msg)); 98 switch (msg->fate) { 99 case Fdelete: 100 (void) strcat(str, " -> *Delete*"); 101 break; 102 case Fcopy: 103 case Fmove: 104 (void) strcat(str, " -> "); 105 (void) strcat(str, msg->desttoc->foldername); 106 if (msg->fate == Fcopy) 107 (void) strcat(str, " (Copy)"); 108 default: 109 break; 110 } 111 if (msg->temporary) (void)strcat(str, " [Temporary]"); 112 } 113 ChangeLabel((Widget) scrn->viewlabel, str); 114 } 115} 116 117 118/* A major msg change has occurred; redisplay it. (This also should 119work even if we now have a new source to display stuff from.) This 120routine arranges to hide boring headers, and also will set the text 121insertion point to the proper place if this is a composition and we're 122viewing it for the first time. */ 123 124static void RedisplayMsg(Scrn scrn) 125{ 126 Msg msg; 127 XawTextPosition startPos, lastPos, nextPos; 128 int length; char str[100]; 129 XawTextBlock block; 130 if (scrn) { 131 msg = scrn->msg; 132 if (msg) { 133 startPos = 0; 134 if (app_resources.hide_boring_headers && scrn->kind != STcomp) { 135 lastPos = XawTextSourceScan(msg->source, (XawTextPosition) 0, 136 XawstAll, XawsdRight, 1, FALSE); 137 while (startPos < lastPos) { 138 nextPos = startPos; 139 length = 0; 140 while (length < 8 && nextPos < lastPos) { 141 nextPos = XawTextSourceRead(msg->source, nextPos, 142 &block, 8 - length); 143 (void) strncpy(str + length, block.ptr, block.length); 144 length += block.length; 145 } 146 if (length == 8) { 147 if (strncmp(str, "From:", 5) == 0 || 148 strncmp(str, "To:", 3) == 0 || 149 strncmp(str, "Date:", 5) == 0 || 150 strncmp(str, "Subject:", 8) == 0) break; 151 } 152 startPos = XawTextSourceScan(msg->source, startPos, 153 XawstEOL, XawsdRight, 1, TRUE); 154 } 155 if (startPos >= lastPos) startPos = 0; 156 } 157 XawTextSetSource(scrn->viewwidget, msg->source, startPos); 158 if (msg->startPos > 0) { 159 XawTextSetInsertionPoint(scrn->viewwidget, msg->startPos); 160 msg->startPos = 0; /* Start in magic place only once. */ 161 } 162 } else { 163 XawTextSetSource(scrn->viewwidget, PNullSource, 164 (XawTextPosition)0); 165 } 166 } 167} 168 169 170 171static char tempDraftFile[100] = ""; 172 173/* Temporarily move the draftfile somewhere else, so we can exec an mh 174 command that affects it. */ 175 176static void TempMoveDraft(void) 177{ 178 char *ptr; 179 if (FileExists(draftFile)) { 180 do { 181 ptr = MakeNewTempFileName(); 182 (void) strcpy(tempDraftFile, draftFile); 183 (void) strcpy(strrchr(tempDraftFile, '/'), strrchr(ptr, '/')); 184 } while (FileExists(tempDraftFile)); 185 RenameAndCheck(draftFile, tempDraftFile); 186 } 187} 188 189 190 191/* Restore the draftfile from its temporary hiding place. */ 192 193static void RestoreDraft(void) 194{ 195 if (*tempDraftFile) { 196 RenameAndCheck(tempDraftFile, draftFile); 197 *tempDraftFile = 0; 198 } 199} 200 201 202 203/* Public routines */ 204 205 206/* Given a message, return the corresponding filename. */ 207 208char *MsgFileName(Msg msg) 209{ 210 static char result[500]; 211 snprintf(result, sizeof(result), "%s/%d", msg->toc->path, msg->msgid); 212 return result; 213} 214 215 216 217/* Save any changes to a message. Also calls the toc routine to update the 218 scanline for this msg. Returns True if saved, false otherwise. */ 219 220int MsgSaveChanges(Msg msg) 221{ 222 int i; 223 Window w; 224 if (msg->source) { 225 if (XawAsciiSave(msg->source)) { 226 for (i=0; i < (int) msg->num_scrns; i++) 227 EnableProperButtons(msg->scrn[i]); 228 if (!msg->temporary) 229 TocMsgChanged(msg->toc, msg); 230 return True; 231 } 232 else { 233 char str[256]; 234 snprintf(str, sizeof(str), "Cannot save changes to \"%s/%d\"!", 235 msg->toc->foldername, msg->msgid); 236 PopupError((Widget)NULL, str); 237 return False; 238 } 239 } 240 w= (msg->source?XtWindow(msg->source):None); 241 Feep(XkbBI_Failure,0,w); 242 return False; 243} 244 245 246/* 247 * Show the given message in the given scrn. If a message is changed, and we 248 * are removing it from any scrn, then ask for confirmation first. If the 249 * scrn was showing a temporary msg that is not being shown in any other scrn, 250 * it is deleted. If scrn is NULL, then remove the message from every scrn 251 * that's showing it. 252 */ 253 254 255/*ARGSUSED*/ 256static void ConfirmedNoScrn( 257 Widget widget, /* unused */ 258 XtPointer client_data, 259 XtPointer call_data) /* unused */ 260{ 261 Msg msg = (Msg) client_data; 262 register int i; 263 264 for (i=msg->num_scrns - 1 ; i >= 0 ; i--) 265 SetScrn((Msg)NULL, msg->scrn[i], TRUE, (XtCallbackList) NULL, 266 (XtCallbackList) NULL); 267} 268 269 270static void RemoveMsgConfirmed(Scrn scrn) 271{ 272 if (scrn->kind == STtocAndView && MsgChanged(scrn->msg)) { 273 Arg args[1]; 274 XtSetArg(args[0], XtNtranslations, scrn->read_translations); 275 XtSetValues(scrn->viewwidget, args, (Cardinal) 1); 276 } 277 scrn->msg->scrn[0] = NULL; 278 scrn->msg->num_scrns = 0; 279 XawTextSetSource(scrn->viewwidget, PNullSource, (XawTextPosition) 0); 280 XtDestroyWidget(scrn->msg->source); 281 scrn->msg->source = NULL; 282 if (scrn->msg->temporary) { 283 (void) unlink(MsgFileName(scrn->msg)); 284 TocRemoveMsg(scrn->msg->toc, scrn->msg); 285 MsgFree(scrn->msg); 286 } 287} 288 289 290static void SetScrnNewMsg( 291 Msg msg, 292 Scrn scrn) 293{ 294 scrn->msg = msg; 295 if (msg == NULL) { 296 XawTextSetSource(scrn->viewwidget, PNullSource, (XawTextPosition) 0); 297 ResetMsgLabel(scrn); 298 EnableProperButtons(scrn); 299 if (scrn->kind != STtocAndView && scrn->kind != STcomp) { 300 StoreWindowName(scrn, progName); 301 DestroyScrn(scrn); 302 } 303 } else { 304 msg->num_scrns++; 305 msg->scrn = XtReallocArray(msg->scrn, msg->num_scrns, sizeof(Scrn)); 306 msg->scrn[msg->num_scrns - 1] = scrn; 307 if (msg->source == NULL) 308 msg->source = CreateFileSource(scrn->viewwidget, MsgFileName(msg), 309 scrn->kind == STcomp); 310 ResetMsgLabel(scrn); 311 RedisplayMsg(scrn); 312 EnableProperButtons(scrn); 313 if (scrn->kind != STtocAndView) 314 StoreWindowName(scrn, MsgName(msg)); 315 } 316} 317 318typedef struct _MsgAndScrn { 319 Msg msg; 320 Scrn scrn; 321} MsgAndScrnRec, *MsgAndScrn; 322 323/*ARGSUSED*/ 324static void ConfirmedWithScrn( 325 Widget widget, /* unused */ 326 XtPointer client_data, 327 XtPointer call_data) /* unused */ 328{ 329 MsgAndScrn mas = (MsgAndScrn) client_data; 330 RemoveMsgConfirmed(mas->scrn); 331 SetScrnNewMsg(mas->msg, mas->scrn); 332 XtFree((char *) mas); 333} 334 335 336static int SetScrn( 337 Msg msg, 338 Scrn scrn, 339 Boolean force, /* if true, force msg set scrn */ 340 XtCallbackList confirms, /* callbacks upon confirmation */ 341 XtCallbackList cancels) /* callbacks upon cancellation */ 342{ 343 register int i, num_scrns; 344 static XtCallbackRec yes_callbacks[] = { 345 {(XtCallbackProc) NULL, (XtPointer) NULL}, 346 {(XtCallbackProc) NULL, (XtPointer) NULL}, 347 {(XtCallbackProc) NULL, (XtPointer) NULL} 348 }; 349 350 if (scrn == NULL) { 351 if (msg == NULL || msg->num_scrns == 0) return 0; 352 if (!force && XawAsciiSourceChanged(msg->source)) { 353 char str[150]; 354 snprintf(str, sizeof(str), 355 "Are you sure you want to remove changes to %s?", 356 MsgName(msg)); 357 358 yes_callbacks[0].callback = ConfirmedNoScrn; 359 yes_callbacks[0].closure = (XtPointer) msg; 360 yes_callbacks[1].callback = confirms[0].callback; 361 yes_callbacks[1].closure = confirms[0].closure; 362 363 PopupConfirm((Widget) NULL, str, yes_callbacks, cancels); 364 return NEEDS_CONFIRMATION; 365 } 366 ConfirmedNoScrn((Widget)NULL, (XtPointer) msg, (XtPointer) NULL); 367 return 0; 368 } 369 370 if (scrn->msg == msg) return 0; 371 372 if (scrn->msg) { 373 num_scrns = scrn->msg->num_scrns; 374 for (i=0 ; i<num_scrns ; i++) 375 if (scrn->msg->scrn[i] == scrn) break; 376 if (i >= num_scrns) Punt("Couldn't find scrn in SetScrn!"); 377 if (num_scrns > 1) 378 scrn->msg->scrn[i] = scrn->msg->scrn[--(scrn->msg->num_scrns)]; 379 else { 380 if (!force && XawAsciiSourceChanged(scrn->msg->source)) { 381 char str[100]; 382 MsgAndScrn cb_data; 383 384 cb_data = XtNew(MsgAndScrnRec); 385 cb_data->msg = msg; 386 cb_data->scrn = scrn; 387 snprintf(str, sizeof(str), 388 "Are you sure you want to remove changes to %s?", 389 MsgName(scrn->msg)); 390 yes_callbacks[0].callback = ConfirmedWithScrn; 391 yes_callbacks[0].closure = (XtPointer) cb_data; 392 yes_callbacks[1].callback = confirms[0].callback; 393 yes_callbacks[1].closure = confirms[0].closure; 394 PopupConfirm(scrn->viewwidget, str, yes_callbacks, cancels); 395 return NEEDS_CONFIRMATION; 396 } 397 RemoveMsgConfirmed(scrn); 398 } 399 } 400 SetScrnNewMsg(msg, scrn); 401 return 0; 402} 403 404 405 406/* Associate the given msg and scrn, asking for confirmation if necessary. */ 407 408int MsgSetScrn( 409 Msg msg, 410 Scrn scrn, 411 XtCallbackList confirms, 412 XtCallbackList cancels) 413{ 414 return SetScrn(msg, scrn, FALSE, confirms, cancels); 415} 416 417 418/* Same as above, but with the extra information that the message is actually 419 a composition. (Nothing currently takes advantage of that extra fact.) */ 420 421void MsgSetScrnForComp(Msg msg, Scrn scrn) 422{ 423 (void) SetScrn(msg, scrn, FALSE, (XtCallbackList) NULL, 424 (XtCallbackList) NULL); 425} 426 427 428/* Associate the given msg and scrn, even if it means losing some unsaved 429 changes. */ 430 431void MsgSetScrnForce(Msg msg, Scrn scrn) 432{ 433 (void) SetScrn(msg, scrn, TRUE, (XtCallbackList) NULL, 434 (XtCallbackList) NULL); 435} 436 437 438 439/* Set the fate of the given message. */ 440 441void MsgSetFate(Msg msg, FateType fate, Toc desttoc) 442{ 443 Toc toc = msg->toc; 444 XawTextBlock block; 445 int i; 446 msg->fate = fate; 447 msg->desttoc = desttoc; 448 if (fate == Fignore && msg == msg->toc->curmsg) 449 block.ptr = "+"; 450 else { 451 switch (fate) { 452 case Fignore: block.ptr = " "; break; 453 case Fcopy: block.ptr = "C"; break; 454 case Fmove: block.ptr = "^"; break; 455 case Fdelete: block.ptr = "D"; break; 456 } 457 } 458 block.firstPos = 0; 459 block.format = FMT8BIT; 460 block.length = 1; 461 if (toc->stopupdate) 462 toc->needsrepaint = TRUE; 463 if (toc->num_scrns && msg->visible && !toc->needsrepaint && 464 *block.ptr != msg->buf[MARKPOS]) 465 (void)XawTextReplace(msg->toc->scrn[0]->tocwidget, /*%%%SourceReplace*/ 466 msg->position + MARKPOS, 467 msg->position + MARKPOS + 1, &block); 468 else 469 msg->buf[MARKPOS] = *block.ptr; 470 for (i=0; i < (int) msg->num_scrns; i++) 471 ResetMsgLabel(msg->scrn[i]); 472} 473 474 475 476/* Get the fate of this message. */ 477 478FateType MsgGetFate(Msg msg, Toc *toc) 479{ 480 if (toc) *toc = msg->desttoc; 481 return msg->fate; 482} 483 484 485/* Make this a temporary message. */ 486 487void MsgSetTemporary(Msg msg) 488{ 489 int i; 490 msg->temporary = TRUE; 491 for (i=0; i < (int) msg->num_scrns; i++) 492 ResetMsgLabel(msg->scrn[i]); 493} 494 495 496/* Make this a permanent message. */ 497 498void MsgSetPermanent(Msg msg) 499{ 500 int i; 501 msg->temporary = FALSE; 502 for (i=0; i < (int) msg->num_scrns; i++) 503 ResetMsgLabel(msg->scrn[i]); 504} 505 506 507 508/* Return the id# of this message. */ 509 510int MsgGetId(Msg msg) 511{ 512 return msg->msgid; 513} 514 515 516/* Return the scanline for this message. */ 517 518char *MsgGetScanLine(Msg msg) 519{ 520 return msg->buf; 521} 522 523 524 525/* Return the toc this message is in. */ 526 527Toc MsgGetToc(Msg msg) 528{ 529 return msg->toc; 530} 531 532 533/* Set the reapable flag for this msg. */ 534 535void MsgSetReapable(Msg msg) 536{ 537 int i; 538 msg->reapable = TRUE; 539 for (i=0; i < (int) msg->num_scrns; i++) 540 EnableProperButtons(msg->scrn[i]); 541} 542 543 544 545/* Clear the reapable flag for this msg. */ 546 547void MsgClearReapable(Msg msg) 548{ 549 int i; 550 msg->reapable = FALSE; 551 for (i=0; i < (int) msg->num_scrns; i++) 552 EnableProperButtons(msg->scrn[i]); 553} 554 555 556/* Get the reapable value for this msg. Returns TRUE iff the reapable flag 557 is set AND no changes have been made. */ 558 559int MsgGetReapable(Msg msg) 560{ 561 return msg == NULL || (msg->reapable && 562 (msg->source == NULL || 563 !XawAsciiSourceChanged(msg->source))); 564} 565 566 567/* Make it possible to edit the given msg. */ 568void MsgSetEditable(Msg msg) 569{ 570 int i; 571 if (msg && msg->source) { 572 SetEditable(msg, TRUE); 573 for (i=0; i < (int) msg->num_scrns; i++) 574 EnableProperButtons(msg->scrn[i]); 575 } 576} 577 578 579 580/* Turn off editing for the given msg. */ 581 582void MsgClearEditable(Msg msg) 583{ 584 int i; 585 if (msg && msg->source) { 586 SetEditable(msg, FALSE); 587 for (i=0; i < (int) msg->num_scrns; i++) 588 EnableProperButtons(msg->scrn[i]); 589 } 590} 591 592 593 594/* Get whether the msg is editable. */ 595 596int MsgGetEditable(Msg msg) 597{ 598 return msg && msg->source && IsEditable(msg); 599} 600 601 602/* Get whether the msg has changed since last saved. */ 603 604int MsgChanged(Msg msg) 605{ 606 return msg && msg->source && XawAsciiSourceChanged(msg->source); 607} 608 609/* Call the given function when the msg changes. */ 610 611void 612MsgSetCallOnChange(Msg msg, void (*func)(XMH_CB_ARGS), XtPointer param) 613{ 614 Arg args[1]; 615 static XtCallbackRec cb[] = { {NULL, NULL}, {NULL, NULL} }; 616 617 if (func != NULL) { 618 cb[0].callback = func; 619 cb[0].closure = param; 620 XtSetArg(args[0], XtNcallback, cb); 621 } 622 else 623 XtSetArg(args[0], XtNcallback, NULL); 624 625 XtSetValues(msg->source, args, (Cardinal) 1); 626 627} 628 629/* Send (i.e., mail) the given message as is. First break it up into lines, 630 and copy it to a new file in the process. The new file is one of 10 631 possible draft files; we rotate among the 10 so that the user can have up 632 to 10 messages being sent at once. (Using a file in /tmp is a bad idea 633 because these files never actually get deleted, but renamed with some 634 prefix. Also, these should stay in an area private to the user for 635 security.) */ 636 637void MsgSend(Msg msg) 638{ 639 FILEPTR from; 640 FILEPTR to; 641 int p, c, l, inheader, sendwidth, sendbreakwidth; 642 char *ptr, *ptr2, **argv, str[100]; 643 static int sendcount = -1; 644 (void) MsgSaveChanges(msg); 645 from = FOpenAndCheck(MsgFileName(msg), "r"); 646 sendcount = (sendcount + 1) % 10; 647 snprintf(str, sizeof(str), "%s%d", xmhDraftFile, sendcount); 648 to = FOpenAndCheck(str, "w"); 649 sendwidth = app_resources.send_line_width; 650 sendbreakwidth = app_resources.break_send_line_width; 651 inheader = TRUE; 652 while ((ptr = ReadLine(from))) { 653 if (inheader) { 654 if (strncmpIgnoringCase(ptr, "sendwidth:", 10) == 0) { 655 if (atoi(ptr+10) > 0) sendwidth = atoi(ptr+10); 656 continue; 657 } 658 if (strncmpIgnoringCase(ptr, "sendbreakwidth:", 15) == 0) { 659 if (atoi(ptr+15) > 0) sendbreakwidth = atoi(ptr+15); 660 continue; 661 } 662 for (l = 0, ptr2 = ptr ; *ptr2 && !l ; ptr2++) 663 l = (*ptr2 != ' ' && *ptr2 != '\t' && *ptr != '-'); 664 if (l) { 665 (void) fprintf(to, "%s\n", ptr); 666 continue; 667 } 668 inheader = FALSE; 669 if (sendbreakwidth < sendwidth) sendbreakwidth = sendwidth; 670 } 671 do { 672 for (p = c = l = 0, ptr2 = ptr; 673 *ptr2 && c < sendbreakwidth; 674 p++, ptr2++) { 675 if (*ptr2 == ' ' && c < sendwidth) 676 l = p; 677 if (*ptr2 == '\t') { 678 if (c < sendwidth) l = p; 679 c += 8 - (c % 8); 680 } 681 else 682 c++; 683 } 684 if (c < sendbreakwidth) { 685 (void) fprintf(to, "%s\n", ptr); 686 *ptr = 0; 687 } 688 else 689 if (l) { 690 ptr[l] = 0; 691 (void) fprintf(to, "%s\n", ptr); 692 ptr += l + 1; 693 } 694 else { 695 for (c = 0; c < sendwidth; ) { 696 if (*ptr == '\t') c += 8 - (c % 8); 697 else c++; 698 (void) fputc(*ptr++, to); 699 } 700 (void) fputc('\n', to); 701 } 702 } while (*ptr); 703 } 704 myfclose(from); 705 myfclose(to); 706 argv = MakeArgv(3); 707 argv[0] = "send"; 708 argv[1] = "-push"; 709 argv[2] = str; 710 DoCommand(argv, (char *) NULL, (char *) NULL); 711 XtFree((char *) argv); 712} 713 714 715/* Make the msg into the form for a generic composition. Set msg->startPos 716 so that the text insertion point will be placed at the end of the first 717 line (which is usually the "To:" field). */ 718 719void MsgLoadComposition(Msg msg) 720{ 721 static const char *blankcomp = NULL; /* Array containing comp template */ 722 static int compsize = 0; 723 static XawTextPosition startPos; 724 char *file, **argv; 725 int fid; 726 if (blankcomp == NULL) { 727 file = MakeNewTempFileName(); 728 argv = MakeArgv(5); 729 argv[0] = "comp"; 730 argv[1] = "-file"; 731 argv[2] = file; 732 argv[3] = "-nowhatnowproc"; 733 argv[4] = "-nodraftfolder"; 734 DoCommand(argv, (char *) NULL, (char *) NULL); 735 XtFree((char *) argv); 736 compsize = GetFileLength(file); 737 if (compsize > 0) { 738 char *readcomp = XtMalloc((Cardinal) compsize); 739 blankcomp = readcomp; 740 fid = myopen(file, O_RDONLY, 0666); 741 if (compsize != read(fid, readcomp, compsize)) 742 Punt("Error reading in MsgLoadComposition!"); 743 myclose(fid); 744 DeleteFileAndCheck(file); 745 } else { 746 blankcomp = "To: \n--------\n"; 747 compsize = strlen(blankcomp); 748 } 749 startPos = strchr(blankcomp, '\n') - blankcomp; 750 } 751 fid = myopen(MsgFileName(msg), O_WRONLY | O_TRUNC | O_CREAT, 0666); 752 if (compsize != write(fid, blankcomp, compsize)) 753 Punt("Error writing in MsgLoadComposition!"); 754 myclose(fid); 755 TocSetCacheValid(msg->toc); 756 msg->startPos = startPos; 757} 758 759 760 761/* Load a msg with a template of a reply to frommsg. Set msg->startPos so 762 that the text insertion point will be placed at the beginning of the 763 message body. */ 764 765void MsgLoadReply( 766 Msg msg, 767 Msg frommsg, 768 String *params, 769 Cardinal num_params) 770{ 771 char **argv; 772 char str[100]; 773 int status; 774 775 TempMoveDraft(); 776 argv = MakeArgv(5 + num_params); 777 argv[0] = "repl"; 778 argv[1] = TocMakeFolderName(frommsg->toc); 779 snprintf(str, sizeof(str), "%d", frommsg->msgid); 780 argv[2] = str; 781 argv[3] = "-nowhatnowproc"; 782 argv[4] = "-nodraftfolder"; 783 memmove( (char *)(argv + 5), (char *)params, num_params * sizeof(String *)); 784 status = DoCommand(argv, (char *) NULL, (char *) NULL); 785 XtFree(argv[1]); 786 XtFree((char*)argv); 787 if (!status) { 788 RenameAndCheck(draftFile, MsgFileName(msg)); 789 RestoreDraft(); 790 TocSetCacheValid(frommsg->toc); /* If -anno is set, this keeps us from 791 rescanning folder. */ 792 TocSetCacheValid(msg->toc); 793 msg->startPos = GetFileLength(MsgFileName(msg)); 794 } 795} 796 797 798 799/* Load a msg with a template of forwarding a list of messages. Set 800 msg->startPos so that the text insertion point will be placed at the end 801 of the first line (which is usually a "To:" field). */ 802 803void MsgLoadForward( 804 Scrn scrn, 805 Msg msg, 806 MsgList mlist, 807 String *params, 808 Cardinal num_params) 809{ 810 char **argv, str[100]; 811 int i; 812 TempMoveDraft(); 813 argv = MakeArgv(4 + mlist->nummsgs + num_params); 814 argv[0] = "forw"; 815 argv[1] = TocMakeFolderName(mlist->msglist[0]->toc); 816 for (i = 0; i < mlist->nummsgs; i++) { 817 snprintf(str, sizeof(str), "%d", mlist->msglist[i]->msgid); 818 argv[2 + i] = XtNewString(str); 819 } 820 argv[2 + i] = "-nowhatnowproc"; 821 argv[3 + i] = "-nodraftfolder"; 822 memmove( (char *)(argv + 4 + i), (char *)params, 823 num_params * sizeof(String *)); 824 DoCommand(argv, (char *) NULL, (char *) NULL); 825 for (i = 1; i < 2 + mlist->nummsgs; i++) 826 XtFree((char *) argv[i]); 827 XtFree((char *) argv); 828 RenameAndCheck(draftFile, MsgFileName(msg)); 829 RestoreDraft(); 830 TocSetCacheValid(msg->toc); 831 msg->source = CreateFileSource(scrn->viewlabel, MsgFileName(msg), True); 832 msg->startPos = XawTextSourceScan(msg->source, (XawTextPosition) 0, 833 XawstEOL, XawsdRight, 1, False); 834} 835 836 837/* Load msg with a copy of frommsg. */ 838 839void MsgLoadCopy(Msg msg, Msg frommsg) 840{ 841 char str[500]; 842 (void)strcpy(str, MsgFileName(msg)); 843 CopyFileAndCheck(MsgFileName(frommsg), str); 844 TocSetCacheValid(msg->toc); 845} 846 847/* Checkpoint the given message if it contains unsaved edits. */ 848 849void MsgCheckPoint(Msg msg) 850{ 851 int len; 852 char file[500]; 853 854 if (!msg || !msg->source || !IsEditable(msg) || 855 !XawAsciiSourceChanged(msg->source)) 856 return; 857 858 if (*app_resources.checkpoint_name_format == '/') { 859 snprintf(file, sizeof(file), 860 app_resources.checkpoint_name_format, msg->msgid); 861 } else { 862 snprintf(file, sizeof(file), "%s/", msg->toc->path); 863 len = strlen(file); 864 snprintf(file + len, sizeof(file) - len, 865 app_resources.checkpoint_name_format, msg->msgid); 866 } 867 if (!XawAsciiSaveAsFile(msg->source, file)) { 868 char str[256]; 869 snprintf(str, sizeof(str), 870 "Unsaved edits cannot be checkpointed to %s.",file); 871 PopupError((Widget)NULL, str); 872 } 873 TocSetCacheValid(msg->toc); 874} 875 876/* Free the storage being used by the given msg. */ 877 878void MsgFree(Msg msg) 879{ 880 XtFree(msg->buf); 881 XtFree((char *)msg); 882} 883 884/* Insert the associated message, if any, filtering it first */ 885 886/*ARGSUSED*/ 887void XmhInsert( 888 Widget w, 889 XEvent *event, 890 String *params, 891 Cardinal *num_params) 892{ 893 Scrn scrn = ScrnFromWidget(w); 894 Msg msg = scrn->msg; 895 XawTextPosition pos; 896 XawTextBlock block; 897 898 if (msg == NULL || scrn->assocmsg == NULL) return; 899 900 if (app_resources.insert_filter && *app_resources.insert_filter) { 901 char command[1024]; 902 char *argv[4]; 903 argv[0] = "/bin/sh"; 904 argv[1] = "-c"; 905 snprintf(command, sizeof(command), "%s %s", app_resources.insert_filter, 906 MsgFileName(scrn->assocmsg)); 907 argv[2] = command; 908 argv[3] = NULL; 909 block.ptr = DoCommandToString(argv); 910 block.length = strlen(block.ptr); 911 } 912 else { 913 /* default filter is equivalent to 'echo "<filename>"' */ 914 block.ptr = XtNewString(MsgFileName(scrn->assocmsg)); 915 block.length = strlen(block.ptr); 916 } 917 block.firstPos = 0; 918 block.format = FMT8BIT; 919 pos = XawTextGetInsertionPoint(scrn->viewwidget); 920 if (XawTextReplace(scrn->viewwidget, pos, pos, &block) != XawEditDone) 921 PopupError(scrn->parent, "Insertion failed!"); 922 XtFree(block.ptr); 923} 924 925/* Function Name: CreateFileSource 926 * Description: Creates an AsciiSource for a file. 927 * Arguments: w - the widget to create the source for. 928 * filename - the file to assign to this source. 929 * edit - if TRUE then this disk source is editable. 930 * Returns: the source. 931 */ 932 933Widget 934CreateFileSource(Widget w, String filename, Boolean edit) 935{ 936 Arg arglist[10]; 937 Cardinal num_args = 0; 938 939 XtSetArg(arglist[num_args], XtNtype, XawAsciiFile); num_args++; 940 XtSetArg(arglist[num_args], XtNstring, filename); num_args++; 941 if (edit) 942 XtSetArg(arglist[num_args], XtNeditType, XawtextEdit); 943 else 944 XtSetArg(arglist[num_args], XtNeditType, XawtextRead); 945 num_args++; 946 947 return(XtCreateWidget("textSource", asciiSrcObjectClass, w, 948 arglist, num_args)); 949} 950