msg.c revision e2264b6d
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 <X11/Xaw/Cardinals.h> 32 33#include "xmh.h" 34#include "tocintrnl.h" 35#include "actions.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 (void) sprintf(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 occured; 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 (void) sprintf(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 (void) sprintf(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 = (Scrn *) XtRealloc((char *)msg->scrn, 306 (unsigned) sizeof(Scrn)*msg->num_scrns); 307 msg->scrn[msg->num_scrns - 1] = scrn; 308 if (msg->source == NULL) 309 msg->source = CreateFileSource(scrn->viewwidget, MsgFileName(msg), 310 scrn->kind == STcomp); 311 ResetMsgLabel(scrn); 312 RedisplayMsg(scrn); 313 EnableProperButtons(scrn); 314 if (scrn->kind != STtocAndView) 315 StoreWindowName(scrn, MsgName(msg)); 316 } 317} 318 319typedef struct _MsgAndScrn { 320 Msg msg; 321 Scrn scrn; 322} MsgAndScrnRec, *MsgAndScrn; 323 324/*ARGSUSED*/ 325static void ConfirmedWithScrn( 326 Widget widget, /* unused */ 327 XtPointer client_data, 328 XtPointer call_data) /* unused */ 329{ 330 MsgAndScrn mas = (MsgAndScrn) client_data; 331 RemoveMsgConfirmed(mas->scrn); 332 SetScrnNewMsg(mas->msg, mas->scrn); 333 XtFree((char *) mas); 334} 335 336 337static int SetScrn( 338 Msg msg, 339 Scrn scrn, 340 Boolean force, /* if true, force msg set scrn */ 341 XtCallbackList confirms, /* callbacks upon confirmation */ 342 XtCallbackList cancels) /* callbacks upon cancellation */ 343{ 344 register int i, num_scrns; 345 static XtCallbackRec yes_callbacks[] = { 346 {(XtCallbackProc) NULL, (XtPointer) NULL}, 347 {(XtCallbackProc) NULL, (XtPointer) NULL}, 348 {(XtCallbackProc) NULL, (XtPointer) NULL} 349 }; 350 351 if (scrn == NULL) { 352 if (msg == NULL || msg->num_scrns == 0) return 0; 353 if (!force && XawAsciiSourceChanged(msg->source)) { 354 char str[100]; 355 (void) sprintf(str, 356 "Are you sure you want to remove changes to %s?", 357 MsgName(msg)); 358 359 yes_callbacks[0].callback = ConfirmedNoScrn; 360 yes_callbacks[0].closure = (XtPointer) msg; 361 yes_callbacks[1].callback = confirms[0].callback; 362 yes_callbacks[1].closure = confirms[0].closure; 363 364 PopupConfirm((Widget) NULL, str, yes_callbacks, cancels); 365 return NEEDS_CONFIRMATION; 366 } 367 ConfirmedNoScrn((Widget)NULL, (XtPointer) msg, (XtPointer) NULL); 368 return 0; 369 } 370 371 if (scrn->msg == msg) return 0; 372 373 if (scrn->msg) { 374 num_scrns = scrn->msg->num_scrns; 375 for (i=0 ; i<num_scrns ; i++) 376 if (scrn->msg->scrn[i] == scrn) break; 377 if (i >= num_scrns) Punt("Couldn't find scrn in SetScrn!"); 378 if (num_scrns > 1) 379 scrn->msg->scrn[i] = scrn->msg->scrn[--(scrn->msg->num_scrns)]; 380 else { 381 if (!force && XawAsciiSourceChanged(scrn->msg->source)) { 382 char str[100]; 383 MsgAndScrn cb_data; 384 385 cb_data = XtNew(MsgAndScrnRec); 386 cb_data->msg = msg; 387 cb_data->scrn = scrn; 388 (void)sprintf(str, 389 "Are you sure you want to remove changes to %s?", 390 MsgName(scrn->msg)); 391 yes_callbacks[0].callback = ConfirmedWithScrn; 392 yes_callbacks[0].closure = (XtPointer) cb_data; 393 yes_callbacks[1].callback = confirms[0].callback; 394 yes_callbacks[1].closure = confirms[0].closure; 395 PopupConfirm(scrn->viewwidget, str, yes_callbacks, cancels); 396 return NEEDS_CONFIRMATION; 397 } 398 RemoveMsgConfirmed(scrn); 399 } 400 } 401 SetScrnNewMsg(msg, scrn); 402 return 0; 403} 404 405 406 407/* Associate the given msg and scrn, asking for confirmation if necessary. */ 408 409int MsgSetScrn( 410 Msg msg, 411 Scrn scrn, 412 XtCallbackList confirms, 413 XtCallbackList cancels) 414{ 415 return SetScrn(msg, scrn, FALSE, confirms, cancels); 416} 417 418 419/* Same as above, but with the extra information that the message is actually 420 a composition. (Nothing currently takes advantage of that extra fact.) */ 421 422void MsgSetScrnForComp(Msg msg, Scrn scrn) 423{ 424 (void) SetScrn(msg, scrn, FALSE, (XtCallbackList) NULL, 425 (XtCallbackList) NULL); 426} 427 428 429/* Associate the given msg and scrn, even if it means losing some unsaved 430 changes. */ 431 432void MsgSetScrnForce(Msg msg, Scrn scrn) 433{ 434 (void) SetScrn(msg, scrn, TRUE, (XtCallbackList) NULL, 435 (XtCallbackList) NULL); 436} 437 438 439 440/* Set the fate of the given message. */ 441 442void MsgSetFate(Msg msg, FateType fate, Toc desttoc) 443{ 444 Toc toc = msg->toc; 445 XawTextBlock block; 446 int i; 447 msg->fate = fate; 448 msg->desttoc = desttoc; 449 if (fate == Fignore && msg == msg->toc->curmsg) 450 block.ptr = "+"; 451 else { 452 switch (fate) { 453 case Fignore: block.ptr = " "; break; 454 case Fcopy: block.ptr = "C"; break; 455 case Fmove: block.ptr = "^"; break; 456 case Fdelete: block.ptr = "D"; break; 457 } 458 } 459 block.firstPos = 0; 460 block.format = FMT8BIT; 461 block.length = 1; 462 if (toc->stopupdate) 463 toc->needsrepaint = TRUE; 464 if (toc->num_scrns && msg->visible && !toc->needsrepaint && 465 *block.ptr != msg->buf[MARKPOS]) 466 (void)XawTextReplace(msg->toc->scrn[0]->tocwidget, /*%%%SourceReplace*/ 467 msg->position + MARKPOS, 468 msg->position + MARKPOS + 1, &block); 469 else 470 msg->buf[MARKPOS] = *block.ptr; 471 for (i=0; i < (int) msg->num_scrns; i++) 472 ResetMsgLabel(msg->scrn[i]); 473} 474 475 476 477/* Get the fate of this message. */ 478 479FateType MsgGetFate(Msg msg, Toc *toc) 480{ 481 if (toc) *toc = msg->desttoc; 482 return msg->fate; 483} 484 485 486/* Make this a temporary message. */ 487 488void MsgSetTemporary(Msg msg) 489{ 490 int i; 491 msg->temporary = TRUE; 492 for (i=0; i < (int) msg->num_scrns; i++) 493 ResetMsgLabel(msg->scrn[i]); 494} 495 496 497/* Make this a permanent message. */ 498 499void MsgSetPermanent(Msg msg) 500{ 501 int i; 502 msg->temporary = FALSE; 503 for (i=0; i < (int) msg->num_scrns; i++) 504 ResetMsgLabel(msg->scrn[i]); 505} 506 507 508 509/* Return the id# of this message. */ 510 511int MsgGetId(Msg msg) 512{ 513 return msg->msgid; 514} 515 516 517/* Return the scanline for this message. */ 518 519char *MsgGetScanLine(Msg msg) 520{ 521 return msg->buf; 522} 523 524 525 526/* Return the toc this message is in. */ 527 528Toc MsgGetToc(Msg msg) 529{ 530 return msg->toc; 531} 532 533 534/* Set the reapable flag for this msg. */ 535 536void MsgSetReapable(Msg msg) 537{ 538 int i; 539 msg->reapable = TRUE; 540 for (i=0; i < (int) msg->num_scrns; i++) 541 EnableProperButtons(msg->scrn[i]); 542} 543 544 545 546/* Clear the reapable flag for this msg. */ 547 548void MsgClearReapable(Msg msg) 549{ 550 int i; 551 msg->reapable = FALSE; 552 for (i=0; i < (int) msg->num_scrns; i++) 553 EnableProperButtons(msg->scrn[i]); 554} 555 556 557/* Get the reapable value for this msg. Returns TRUE iff the reapable flag 558 is set AND no changes have been made. */ 559 560int MsgGetReapable(Msg msg) 561{ 562 return msg == NULL || (msg->reapable && 563 (msg->source == NULL || 564 !XawAsciiSourceChanged(msg->source))); 565} 566 567 568/* Make it possible to edit the given msg. */ 569void MsgSetEditable(Msg msg) 570{ 571 int i; 572 if (msg && msg->source) { 573 SetEditable(msg, TRUE); 574 for (i=0; i < (int) msg->num_scrns; i++) 575 EnableProperButtons(msg->scrn[i]); 576 } 577} 578 579 580 581/* Turn off editing for the given msg. */ 582 583void MsgClearEditable(Msg msg) 584{ 585 int i; 586 if (msg && msg->source) { 587 SetEditable(msg, FALSE); 588 for (i=0; i < (int) msg->num_scrns; i++) 589 EnableProperButtons(msg->scrn[i]); 590 } 591} 592 593 594 595/* Get whether the msg is editable. */ 596 597int MsgGetEditable(Msg msg) 598{ 599 return msg && msg->source && IsEditable(msg); 600} 601 602 603/* Get whether the msg has changed since last saved. */ 604 605int MsgChanged(Msg msg) 606{ 607 return msg && msg->source && XawAsciiSourceChanged(msg->source); 608} 609 610/* Call the given function when the msg changes. */ 611 612void 613MsgSetCallOnChange(Msg msg, void (*func)(XMH_CB_ARGS), XtPointer param) 614{ 615 Arg args[1]; 616 static XtCallbackRec cb[] = { {NULL, NULL}, {NULL, NULL} }; 617 618 if (func != NULL) { 619 cb[0].callback = func; 620 cb[0].closure = param; 621 XtSetArg(args[0], XtNcallback, cb); 622 } 623 else 624 XtSetArg(args[0], XtNcallback, NULL); 625 626 XtSetValues(msg->source, args, (Cardinal) 1); 627 628} 629 630/* Send (i.e., mail) the given message as is. First break it up into lines, 631 and copy it to a new file in the process. The new file is one of 10 632 possible draft files; we rotate amoung the 10 so that the user can have up 633 to 10 messages being sent at once. (Using a file in /tmp is a bad idea 634 because these files never actually get deleted, but renamed with some 635 prefix. Also, these should stay in an area private to the user for 636 security.) */ 637 638void MsgSend(Msg msg) 639{ 640 FILEPTR from; 641 FILEPTR to; 642 int p, c, l, inheader, sendwidth, sendbreakwidth; 643 char *ptr, *ptr2, **argv, str[100]; 644 static int sendcount = -1; 645 (void) MsgSaveChanges(msg); 646 from = FOpenAndCheck(MsgFileName(msg), "r"); 647 sendcount = (sendcount + 1) % 10; 648 (void) sprintf(str, "%s%d", xmhDraftFile, sendcount); 649 to = FOpenAndCheck(str, "w"); 650 sendwidth = app_resources.send_line_width; 651 sendbreakwidth = app_resources.break_send_line_width; 652 inheader = TRUE; 653 while ((ptr = ReadLine(from))) { 654 if (inheader) { 655 if (strncmpIgnoringCase(ptr, "sendwidth:", 10) == 0) { 656 if (atoi(ptr+10) > 0) sendwidth = atoi(ptr+10); 657 continue; 658 } 659 if (strncmpIgnoringCase(ptr, "sendbreakwidth:", 15) == 0) { 660 if (atoi(ptr+15) > 0) sendbreakwidth = atoi(ptr+15); 661 continue; 662 } 663 for (l = 0, ptr2 = ptr ; *ptr2 && !l ; ptr2++) 664 l = (*ptr2 != ' ' && *ptr2 != '\t' && *ptr != '-'); 665 if (l) { 666 (void) fprintf(to, "%s\n", ptr); 667 continue; 668 } 669 inheader = FALSE; 670 if (sendbreakwidth < sendwidth) sendbreakwidth = sendwidth; 671 } 672 do { 673 for (p = c = l = 0, ptr2 = ptr; 674 *ptr2 && c < sendbreakwidth; 675 p++, ptr2++) { 676 if (*ptr2 == ' ' && c < sendwidth) 677 l = p; 678 if (*ptr2 == '\t') { 679 if (c < sendwidth) l = p; 680 c += 8 - (c % 8); 681 } 682 else 683 c++; 684 } 685 if (c < sendbreakwidth) { 686 (void) fprintf(to, "%s\n", ptr); 687 *ptr = 0; 688 } 689 else 690 if (l) { 691 ptr[l] = 0; 692 (void) fprintf(to, "%s\n", ptr); 693 ptr += l + 1; 694 } 695 else { 696 for (c = 0; c < sendwidth; ) { 697 if (*ptr == '\t') c += 8 - (c % 8); 698 else c++; 699 (void) fputc(*ptr++, to); 700 } 701 (void) fputc('\n', to); 702 } 703 } while (*ptr); 704 } 705 myfclose(from); 706 myfclose(to); 707 argv = MakeArgv(3); 708 argv[0] = "send"; 709 argv[1] = "-push"; 710 argv[2] = str; 711 DoCommand(argv, (char *) NULL, (char *) NULL); 712 XtFree((char *) argv); 713} 714 715 716/* Make the msg into the form for a generic composition. Set msg->startPos 717 so that the text insertion point will be placed at the end of the first 718 line (which is usually the "To:" field). */ 719 720void MsgLoadComposition(Msg msg) 721{ 722 static char *blankcomp = NULL; /* Array containing comp template */ 723 static int compsize = 0; 724 static XawTextPosition startPos; 725 char *file, **argv; 726 int fid; 727 if (blankcomp == NULL) { 728 file = MakeNewTempFileName(); 729 argv = MakeArgv(5); 730 argv[0] = "comp"; 731 argv[1] = "-file"; 732 argv[2] = file; 733 argv[3] = "-nowhatnowproc"; 734 argv[4] = "-nodraftfolder"; 735 DoCommand(argv, (char *) NULL, (char *) NULL); 736 XtFree((char *) argv); 737 compsize = GetFileLength(file); 738 if (compsize > 0) { 739 blankcomp = XtMalloc((Cardinal) compsize); 740 fid = myopen(file, O_RDONLY, 0666); 741 if (compsize != read(fid, blankcomp, 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 (void) sprintf(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 (void) sprintf(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 (void) sprintf(file, app_resources.checkpoint_name_format, msg->msgid); 860 } else { 861 (void) sprintf(file, "%s/", msg->toc->path); 862 len = strlen(file); 863 (void) sprintf(file + len, app_resources.checkpoint_name_format, 864 msg->msgid); 865 } 866 if (!XawAsciiSaveAsFile(msg->source, file)) { 867 char str[256]; 868 (void) sprintf(str, "Unsaved edits cannot be checkpointed to %s.", 869 file); 870 PopupError((Widget)NULL, str); 871 } 872 TocSetCacheValid(msg->toc); 873} 874 875/* Free the storage being used by the given msg. */ 876 877void MsgFree(Msg msg) 878{ 879 XtFree(msg->buf); 880 XtFree((char *)msg); 881} 882 883/* Insert the associated message, if any, filtering it first */ 884 885/*ARGSUSED*/ 886void XmhInsert( 887 Widget w, 888 XEvent *event, 889 String *params, 890 Cardinal *num_params) 891{ 892 Scrn scrn = ScrnFromWidget(w); 893 Msg msg = scrn->msg; 894 XawTextPosition pos; 895 XawTextBlock block; 896 897 if (msg == NULL || scrn->assocmsg == NULL) return; 898 899 if (app_resources.insert_filter && *app_resources.insert_filter) { 900 char command[1024]; 901 char *argv[4]; 902 argv[0] = "/bin/sh"; 903 argv[1] = "-c"; 904 sprintf(command, "%s %s", app_resources.insert_filter, 905 MsgFileName(scrn->assocmsg)); 906 argv[2] = command; 907 argv[3] = NULL; 908 block.ptr = DoCommandToString(argv); 909 block.length = strlen(block.ptr); 910 } 911 else { 912 /* default filter is equivalent to 'echo "<filename>"' */ 913 block.ptr = XtNewString(MsgFileName(scrn->assocmsg)); 914 block.length = strlen(block.ptr); 915 } 916 block.firstPos = 0; 917 block.format = FMT8BIT; 918 pos = XawTextGetInsertionPoint(scrn->viewwidget); 919 if (XawTextReplace(scrn->viewwidget, pos, pos, &block) != XawEditDone) 920 PopupError(scrn->parent, "Insertion failed!"); 921 XtFree(block.ptr); 922} 923 924/* Function Name: CreateFileSource 925 * Description: Creates an AsciiSource for a file. 926 * Arguments: w - the widget to create the source for. 927 * filename - the file to assign to this source. 928 * edit - if TRUE then this disk source is editable. 929 * Returns: the source. 930 */ 931 932Widget 933CreateFileSource(Widget w, String filename, Boolean edit) 934{ 935 Arg arglist[10]; 936 Cardinal num_args = 0; 937 938 XtSetArg(arglist[num_args], XtNtype, XawAsciiFile); num_args++; 939 XtSetArg(arglist[num_args], XtNstring, filename); num_args++; 940 if (edit) 941 XtSetArg(arglist[num_args], XtNeditType, XawtextEdit); 942 else 943 XtSetArg(arglist[num_args], XtNeditType, XawtextRead); 944 num_args++; 945 946 return(XtCreateWidget("textSource", asciiSrcObjectClass, w, 947 arglist, num_args)); 948} 949