home *** CD-ROM | disk | FTP | other *** search
/ vsiftp.vmssoftware.com / VSIPUBLIC@vsiftp.vmssoftware.com.tar / FREEWARE / FREEWARE40.ZIP / pine / pico / composer.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-04-06  |  60.2 KB  |  2,566 lines

  1. #if    !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: composer.c,v 4.10 1993/11/09 00:42:24 mikes Exp $";
  3. #endif
  4. /*
  5.  * Program:    Pine composer routines
  6.  *
  7.  *
  8.  * Michael Seibel
  9.  * Networks and Distributed Computing
  10.  * Computing and Communications
  11.  * University of Washington
  12.  * Administration Builiding, AG-44
  13.  * Seattle, Washington, 98195, USA
  14.  * Internet: mikes@cac.washington.edu
  15.  *
  16.  * Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  17.  *
  18.  * Copyright 1991-1993  University of Washington
  19.  *
  20.  *  Permission to use, copy, modify, and distribute this software and its
  21.  * documentation for any purpose and without fee to the University of
  22.  * Washington is hereby granted, provided that the above copyright notice
  23.  * appears in all copies and that both the above copyright notice and this
  24.  * permission notice appear in supporting documentation, and that the name
  25.  * of the University of Washington not be used in advertising or publicity
  26.  * pertaining to distribution of the software without specific, written
  27.  * prior permission.  This software is made available "as is", and
  28.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  29.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  30.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  31.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  32.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  33.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  34.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  35.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  36.  *
  37.  * Pine and Pico are trademarks of the University of Washington.
  38.  * No commercial use of these trademarks may be made without prior
  39.  * written permission of the University of Washington.
  40.  *
  41.  *
  42.  * NOTES:
  43.  *
  44.  *  - composer.c is the composer for the PINE mail system
  45.  *
  46.  *  - tabled 01/19/90
  47.  *
  48.  *  Notes: These routines aren't incorporated yet, because the composer as
  49.  *         a whole still needs development.  These are ideas that should
  50.  *         be implemented in later releases of PINE.  See the notes in 
  51.  *         pico.c concerning what needs to be done ....
  52.  *
  53.  *  - untabled 01/30/90
  54.  *
  55.  *  Notes: Installed header line editing, with wrapping and unwrapping, 
  56.  *         context sensitive help, and other mail header editing features.
  57.  *
  58.  *  - finalish code cleanup 07/15/91
  59.  * 
  60.  *  Notes: Killed/yanked header lines use emacs kill buffer.
  61.  *         Arbitrarily large headers are handled gracefully.
  62.  *         All formatting handled by FormatLines.
  63.  *
  64.  *  - Work done to optimize display painting 06/26/92
  65.  *         Not as clean as it should be, needs more thought 
  66.  *
  67.  */
  68. #include <stdio.h>
  69. #include <ctype.h>
  70. #include "osdep.h"
  71. #include "pico.h"
  72. #include "estruct.h"
  73. #include "edef.h"
  74. #include "efunc.h"
  75. #ifdef HEBREW
  76. #include "hebrew.h"
  77. #endif
  78.  
  79.  
  80. #ifdef    ANSI
  81.     int InitEntryText(char *, int);
  82.     int HeaderOffset(int);
  83.     int HeaderFocus(int, int);
  84.     int LineEdit(int);
  85.     int FormatLines(struct hdr_line *, char *, int, int);
  86.     int PaintBody(int);
  87.     int ComposerHelp(int);
  88.     int NewTop(void);
  89.     void display_delimiter(int);
  90.     int InvertPrompt(int, int);
  91.     int partial_entries(void);
  92.     int physical_line(struct hdr_line *);
  93.     int strend(char *, int);
  94.     char *strqchr(char *, int);
  95.     int KillHeaderLine(struct hdr_line *, int);
  96.     int SaveHeaderLines(void);
  97.     char *break_point(char *, int, int);
  98.     int hldelete(struct hdr_line *);
  99.     int is_blank(int, int, int);
  100.     int zotentry(struct hdr_line *);
  101.     void zotcomma(char *);
  102. #else
  103.     int InitEntryText();
  104.     int HeaderOffset();
  105.     int HeaderFocus();
  106.     int LineEdit();
  107.     int FormatLines();
  108.     int PaintBody();
  109.     int ComposerHelp();
  110.     int NewTop();
  111.     void display_delimiter();
  112.     int InvertPrompt();
  113.     int partial_entries();
  114.     int physical_line();
  115.     int strend();
  116.     char *strqchr();
  117.     int KillHeaderLine();
  118.     int SaveHeaderLines();
  119.     char *break_point();
  120.     int hldelete();
  121.     int is_blank();
  122.     int zotentry();
  123.     void zotcomma();
  124. #endif
  125.  
  126.  
  127. /*
  128.  * definition header field array, structures defined in pico.h
  129.  */
  130. struct headerentry headents[LASTHDR+1];
  131.  
  132.  
  133. /*
  134.  * structure that keeps track of the range of header lines that are
  135.  * to be displayed and other fun stuff about the header
  136.  */
  137. struct on_display ods;                /* global on_display struct */
  138.  
  139.  
  140. /*
  141.  * useful macros
  142.  */
  143. #define    HALLOC()    (struct hdr_line *)malloc(sizeof(struct hdr_line))
  144. #define    LINELEN()    (term.t_ncol - headents[ods.cur_e].prlen)
  145. #define    BOTTOM()    (term.t_nrow - 2)
  146. #define    HALF_SCR()    ((term.t_nrow-5)/2)
  147.  
  148.  
  149. /*
  150.  * useful declarations
  151.  */
  152. static int     last_key;            /* last keystroke  */
  153.  
  154. /*
  155.  * function key mappings for header editor
  156.  */
  157. static int ckm[12][2] = {
  158.     { F1,  (CTRL|'G')},
  159.     { F2,  (CTRL|'X')},
  160.     { F3,  (CTRL|'C')},
  161.     { F4,  (CTRL|'D')},
  162.     { F5,  (CTRL|'R')},
  163.     { F6,  (CTRL|'J')},
  164.     { F7,  0 },
  165.     { F8,  0 },
  166.     { F9,  (CTRL|'K')},
  167.     { F10, (CTRL|'U')},
  168.     { F11, (CTRL|'O')},
  169.     { F12, (CTRL|'T')}
  170. };
  171.  
  172.  
  173. /*
  174.  * InitMailHeader - initialize header array, and set beginning editor row 
  175.  *                  range.  The header entry structure should look just like 
  176.  *                  what is written on the screen, the vector 
  177.  *                  (entry, line, offset) will describe the current cursor 
  178.  *                  position in the header.
  179.  */
  180. InitMailHeader(mp)
  181. PICO  *mp;
  182. {
  183.     int    i;
  184.     char *addrbuf;
  185.     static  char  toprmt[]  = "To      : ";
  186.     static  char  ccprmt[]  = "Cc      : ";
  187.     static  char  bccprmt[] = "Bcc     : ";
  188.     static  char  fccprmt[] = "Fcc     : ";
  189. #ifdef    ATTACHMENTS
  190.     static  char  attprmt[] = "Attchmnt: ";
  191. #endif
  192.     static  char  subprmt[] = "Subject : ";
  193.  
  194.     /*
  195.      * initialize on_display structure
  196.      */
  197.     ods.p_off = 0;
  198.     ods.top_e = ods.cur_e = TOHDR;
  199.     ods.top_l = ods.cur_l = NULL;
  200.     ods.p_line = COMPOSER_TOP_LINE;
  201.  
  202.     for(i=0;i<=LASTHDR;i++){
  203.     headents[i].hd_text = NULL;
  204.     headents[i].display_it = TRUE;
  205.     switch(i){
  206.         case TOHDR :
  207.         headents[i].prompt = toprmt;
  208.         headents[i].name   = "To";
  209.         headents[i].help   = mp->to_help;
  210.         headents[i].prlen  = 10;
  211.         headents[i].maxlen = mp->tolen;
  212.         headents[i].realaddr = &(mp->tobuf);
  213.         addrbuf = mp->tobuf;
  214.         break;
  215.         case CCHDR :
  216.         headents[i].prompt = ccprmt;
  217.         headents[i].name   = "Cc";
  218.         headents[i].help   = mp->cc_help;
  219.         headents[i].prlen  = 10;
  220.         headents[i].maxlen = mp->cclen;
  221.         headents[i].realaddr = &(mp->ccbuf);
  222.         addrbuf = mp->ccbuf;
  223.         break;
  224.         case BCCHDR :
  225.         headents[i].prompt = bccprmt;
  226.         headents[i].name   = "Bcc";
  227.         headents[i].help   = mp->bcc_help;
  228.         headents[i].prlen  = 10;
  229.         headents[i].maxlen = mp->bcclen;
  230.         headents[i].realaddr = &(mp->bccbuf);
  231.             headents[i].display_it = FALSE;
  232.         addrbuf = mp->bccbuf;
  233.         break;
  234.         case FCCHDR :
  235.         headents[i].prompt = fccprmt;
  236.         headents[i].name   = "Fcc";
  237.         headents[i].help   = mp->fcc_help;
  238.         headents[i].prlen  = 10;
  239.         headents[i].maxlen = mp->fcclen;
  240.         headents[i].realaddr = &(mp->fccbuf);
  241.             headents[i].display_it = FALSE;
  242.         addrbuf = mp->fccbuf;
  243.         break;
  244. #ifdef    ATTACHMENTS
  245.         case ATTCHDR :
  246.         headents[i].prompt = attprmt;
  247.         headents[i].name   = "Attchmnt";
  248.         headents[i].help   = mp->attachment_help;
  249.         headents[i].prlen  = 10;
  250.         headents[i].maxlen = 0;
  251.  
  252.         /* build entries: one to a line then feed to initializer */
  253.         if(mp->attachments != NULL){
  254.             int   x = 0;
  255.             PATMT *ap = mp->attachments;
  256.  
  257.             addrbuf = (char *)malloc((size_t)1024);
  258.             addrbuf[0] = '\0';
  259.             s[0] = '\0';
  260.             while(ap){
  261.             if(ap->filename){
  262.                 sprintf(s, "%d. %s %s%s%s\"%s\"%s",
  263.                     ++x,
  264.                     ap->filename,
  265.                     ap->size ? "(" : "",
  266.                     ap->size ? ap->size : "",
  267.                     ap->size ? ") " : "",
  268.                     ap->description ? ap->description : "", 
  269.                     ap->next ? "," : "");
  270.                 strcat(addrbuf, s);
  271.             }
  272.             ap = ap->next;
  273.             }
  274.             InitEntryText(addrbuf, i);
  275.             free((char *)addrbuf);
  276.         }
  277.         else
  278.           InitEntryText("", i);
  279.         headents[i].realaddr = NULL;
  280.         continue;
  281. #endif
  282.         case SUBJHDR :
  283.         headents[i].prompt = subprmt;
  284.         headents[i].name   = "Subject";
  285.         headents[i].help   = mp->subject_help;
  286.         headents[i].prlen  = 10;
  287.         headents[i].maxlen = mp->sublen;
  288.         headents[i].realaddr = &(mp->subbuf);
  289.         addrbuf = mp->subbuf;
  290.         break;
  291.         default :
  292.         break;
  293.         }
  294.     InitEntryText(addrbuf, i);
  295.     }
  296.  
  297.     /*
  298.      * finish initialization and then figure out display layout...
  299.      */
  300.     ods.top_l = ods.cur_l = headents[TOHDR].hd_text;
  301.     UpdateHeader();
  302. }
  303.  
  304.  
  305.  
  306. /*
  307.  * InitEntryText - Add the given header text into the header entry 
  308.  *           line structure.
  309.  */
  310. InitEntryText(address, h)
  311. char    *address;
  312. int    h;
  313. {
  314.     struct  hdr_line    *curline;
  315.     register  int    longest;
  316.  
  317.     /*
  318.      * get first chunk of memory, and tie it to structure...
  319.      */
  320.     if((curline = HALLOC()) == NULL){
  321.         emlwrite("Unable to make room for full Header.", NULL);
  322.         return(FALSE);
  323.     }
  324.     longest = term.t_ncol - headents[h].prlen - 1;
  325.     curline->text[0] = '\0';
  326.     curline->next = NULL;
  327.     curline->prev = NULL;
  328.     headents[h].hd_text = curline;        /* tie it into the list */
  329.  
  330.     if(FormatLines(curline, address, longest, h) == -1)
  331.       return(FALSE);
  332.     else
  333.       return(TRUE);
  334. }
  335.  
  336.  
  337.  
  338. /*
  339.  *  ResizeHeader - Handle resizing display when SIGWINCH received.
  340.  *
  341.  *    notes:
  342.  *        works OK, but needs thorough testing
  343.  *          
  344.  */
  345. ResizeHeader()
  346. {
  347.     register int i;
  348.     register int offset;
  349.  
  350.     offset = (ComposerEditing) ? HeaderOffset(ods.cur_e) : 0;
  351.  
  352.     for(i=TOHDR; i <= LASTHDR; i++){        /* format each entry */
  353.     if(FormatLines(headents[i].hd_text, "",
  354.                (term.t_ncol-headents[i].prlen), i) == -1){
  355.         return(-1);
  356.     }
  357.     }
  358.  
  359.     if(ComposerEditing)                /* restart at the top */
  360.       HeaderFocus(ods.cur_e, offset);        /* fix cur_l and p_off */
  361.     else
  362.       HeaderFocus(SUBJHDR, -1);            /* put focus on last line */
  363.  
  364.     if(ComposerTopLine != COMPOSER_TOP_LINE)
  365.       UpdateHeader();
  366.  
  367.     PaintBody(0);
  368.  
  369.     if(ComposerEditing)
  370.       movecursor(ods.p_line, ods.p_off+headents[ods.cur_e].prlen);
  371.  
  372.     (*term.t_flush)();
  373.     return(TRUE);
  374. }
  375.  
  376.  
  377.  
  378. /*
  379.  * HeaderOffset - return the character offset into the given header
  380.  */
  381. HeaderOffset(h)
  382. int    h;
  383. {
  384.     register struct hdr_line *l;
  385.     int         i = 0;
  386.  
  387.     l = headents[h].hd_text;
  388.  
  389.     while(l != ods.cur_l){
  390.     i += strlen(l->text);
  391.     l = l->next;
  392.     }
  393.     return(i+ods.p_off);
  394. }
  395.  
  396.  
  397.  
  398. /*
  399.  * HeaderFocus - put the dot at the given offset into the given header
  400.  */
  401. HeaderFocus(h, offset)
  402. int    h, offset;
  403. {
  404.     register struct hdr_line *l;
  405.     register int    i;
  406.     int         last = 0;
  407.  
  408.     if(offset == -1)                /* focus on last line */
  409.       last = 1;
  410.  
  411.     l = headents[h].hd_text;
  412.     while(1){
  413.     if(last && l->next == NULL){
  414.         break;
  415.     }
  416.     else{
  417.         if((i=strlen(l->text)) >= offset)
  418.           break;
  419.         else
  420.           offset -= i;
  421.     }
  422.     if((l = l->next) == NULL)
  423.       return(FALSE);
  424.     }
  425.  
  426.     ods.cur_l = l;
  427.     ods.p_len = strlen(l->text);
  428.     ods.p_off = (last) ? 0 : offset;
  429.  
  430.     return(TRUE);
  431. }
  432.  
  433.  
  434.  
  435. /*
  436.  * HeaderEditor() - edit the mail header field by field, trapping 
  437.  *                  important key sequences, hand the hard work off
  438.  *                  to LineEdit().  
  439.  *    returns:
  440.  *        -1    if we drop out the bottom 
  441.  *        FALSE if editing is cancelled
  442.  *        TRUE  if editing is finished
  443.  */
  444. HeaderEditor(f, n)
  445. int f, n;
  446. {
  447.     register  int    retval = -1;        /* return value */
  448.     register  int    i;
  449.     register  int    ch;
  450.     register  int    status;            /* return status of something*/
  451.     register  char    *bufp;
  452.  
  453.     ComposerEditing = TRUE;
  454.     display_delimiter(0);            /* provide feedback */
  455.  
  456.     /* 
  457.      * Decide where to begin editing.  if f == TRUE begin editing
  458.      * at the bottom.  this case results from the cursor backing
  459.      * into the editor from the bottom.  otherwise, the user explicitly
  460.      * requested editing the header, and we begin at the top.
  461.      * 
  462.      * further, if f == 1, we moved into the header by hitting the up arrow
  463.      * in the message text, else if f == 2, we moved into the header by
  464.      * moving past the left edge of the top line in the message.  so, make 
  465.      * the end of the last line of the last entry the current cursor position
  466.      */
  467.     if(f){
  468.     /*
  469.      * note: assumes that ods.cur_e and ods.cur_l haven't changed
  470.      *       since we left...
  471.      */
  472.     ods.p_line = ComposerTopLine - 2;
  473.     ods.p_off = 1000;
  474.  
  475.     if(f==1){
  476.         if(curwp->w_doto < headents[ods.cur_e].prlen)
  477.           ods.p_off = 0;
  478.         else if(curwp->w_doto < ods.p_off + headents[ods.cur_e].prlen)
  479.           ods.p_off = curwp->w_doto - headents[ods.cur_e].prlen;
  480.     }
  481.     }
  482.     else{        /* use offset 0 of first line of first entry */
  483.     ods.p_line = COMPOSER_TOP_LINE;
  484.     ods.cur_e = ods.top_e;
  485.         ods.cur_l = ods.top_l;
  486.         ods.p_off = 0;
  487.     }
  488.  
  489.     InvertPrompt(ods.cur_e, TRUE);        /* highlight header field */
  490.     ShowPrompt();                /* display correct options */
  491.  
  492.     do{
  493.     ch = LineEdit(TRUE);            /* work on the current line */
  494.  
  495.         switch (ch){
  496.       case (CTRL|'R') :            /* Toggle header display */
  497. #ifdef    ATTACHMENTS
  498.         if(ods.cur_e == SUBJHDR || ods.cur_e == ATTCHDR)
  499. #else
  500.         if(ods.cur_e == SUBJHDR)
  501. #endif
  502.           InvertPrompt(ods.cur_e, FALSE);    /* don't leave inverted */
  503.  
  504.         if(partial_entries()){
  505. #ifdef    ATTACHMENTS
  506.         if(ods.cur_e > CCHDR && ods.cur_e < ATTCHDR){
  507.             ods.p_off = 0;
  508.             ods.cur_e = ATTCHDR;
  509. #else
  510.         if(ods.cur_e > CCHDR && ods.cur_e < SUBJHDR){
  511.             ods.p_off = 0;
  512.             ods.cur_e = SUBJHDR;
  513. #endif
  514.             ods.cur_l = headents[ods.cur_e].hd_text;
  515.         }
  516.         }
  517.  
  518.         ods.p_line = 0;            /* force update */
  519.         UpdateHeader();
  520.         PaintHeader(COMPOSER_TOP_LINE, FALSE);
  521.         PaintBody(1);
  522.         break;
  523.  
  524.       case (CTRL|'C') :            /* bag whole thing ?*/
  525. #ifdef    OLDWAY
  526.         abort_composer(1, 0);
  527. #else
  528.         if(abort_composer(1, 0) == TRUE)
  529.           return(retval);
  530. #endif
  531.         break;
  532.  
  533.       case (CTRL|'X') :            /* Done. Send it. */
  534. #ifdef    ATTACHMENTS
  535.         i = 0;
  536.         if(ods.cur_e == ATTCHDR){
  537.         /* verify the attachments, and pretty things up in case
  538.          * we come back to the composer due to error...
  539.          */
  540.         if((i = SyncAttach()) != 0){
  541.             sleep(2);        /* give time for error to absorb */
  542.             FormatLines(headents[ATTCHDR].hd_text, "",
  543.                 term.t_ncol - headents[ATTCHDR].prlen,
  544.                 ATTCHDR);
  545.         }
  546.         }
  547.         else
  548. #endif
  549.         if(ods.cur_e==BCCHDR || ods.cur_e==TOHDR || ods.cur_e==CCHDR)
  550.           i = resolve_niks(ods.cur_e);
  551.  
  552.         if(wquit(1,0) == TRUE)
  553.           return(retval);
  554.  
  555.         if(i){
  556.         /*
  557.          * need to be careful here because pointers might be messed up.
  558.          * also, could do a better job of finding the right place to
  559.          * put the dot back (i.e., the addr/list that was expanded).
  560.          */
  561.         ods.cur_l = headents[ods.cur_e].hd_text; /* attach cur_l */
  562.         ods.p_off = 0;
  563.         ods.p_line = 0;            /* force realignment */
  564.         UpdateHeader();
  565.         PaintHeader(COMPOSER_TOP_LINE, FALSE);
  566.         PaintBody(1);
  567.         }
  568.         break;
  569.       case (CTRL|'Z') :            /* Suspend compose */
  570.         if(gmode&MDSSPD){            /* is it allowed? */
  571.         bktoshell();
  572.         PaintBody(0);
  573.         }
  574.         else{
  575.         (*term.t_beep)();
  576.         emlwrite("Unknown Command: ^Z", NULL);
  577.         }
  578.         break;
  579.  
  580.       case (CTRL|'O') :            /* Suspend message */
  581.         if(ods.cur_e==BCCHDR || ods.cur_e==TOHDR || ods.cur_e==CCHDR){
  582.         resolve_niks(ods.cur_e);
  583.         }
  584. #ifdef    ATTACHMENTS
  585.         if(ods.cur_e == ATTCHDR){
  586.         if(SyncAttach() < 0){
  587.             if(mlyesno("Problem with attachments. Postpone anyway?",
  588.                    FALSE) != TRUE){
  589.             if(FormatLines(headents[ATTCHDR].hd_text, "",
  590.                        term.t_ncol - headents[ATTCHDR].prlen,
  591.                        ATTCHDR) == -1)
  592.               emlwrite("\007Format lines failed!", NULL);
  593.             UpdateHeader();
  594.             PaintHeader(COMPOSER_TOP_LINE, FALSE);
  595.             PaintBody(1);
  596.             continue;
  597.             }
  598.         }
  599.         }
  600. #endif
  601.         suspend_composer(1,0);
  602.         return(retval);
  603.         break;
  604.  
  605. #ifdef    ATTACHMENTS
  606.       case (CTRL|'J') :            /* handle attachments */
  607.         { char fn[NLINE], sz[32], cmt[NLINE];
  608.  
  609.           if(AskAttach(fn, sz, cmt)){
  610.           /* update display */
  611.           sprintf(s, "%s (%s) \"%s\"%s", fn, sz, cmt, 
  612.               (headents[ATTCHDR].hd_text->text[0]=='\0') ? "":",");
  613.  
  614.           if(FormatLines(headents[ATTCHDR].hd_text, s,
  615.                  term.t_ncol - headents[ATTCHDR].prlen,
  616.                  ATTCHDR) == -1){
  617.               emlwrite("\007Format lines failed!", NULL);
  618.           }
  619.  
  620.           if(SyncAttach() < 0)
  621.             emlwrite("\007Problem attaching: %s", fn);
  622.  
  623.           UpdateHeader();
  624.           PaintHeader(COMPOSER_TOP_LINE, FALSE);
  625.           PaintBody(1);
  626.           }
  627.  
  628.           ShowPrompt();            /* clean up prompt */
  629.         }
  630.         break;
  631. #endif
  632.  
  633.       case (CTRL|'I') :            /* tab */
  634.         ods.p_off = 0;            /* fall through... */
  635.  
  636.       case (CTRL|'N') :
  637.       case K_PAD_DOWN :
  638.         if((++ods.p_line >= (ComposerTopLine - 1))    /* stop? */
  639.            && (ods.cur_e == LASTHDR && ods.cur_l->next == NULL)){
  640.         ods.p_line = ComposerTopLine;
  641.         if(ComposerTopLine == BOTTOM()){
  642.             UpdateHeader();
  643.             PaintHeader(COMPOSER_TOP_LINE, FALSE);
  644.             PaintBody(1);
  645.         }
  646.         ods.p_line = ComposerTopLine;
  647.         InvertPrompt(ods.cur_e, FALSE);
  648.         }
  649.         else{
  650.         status = ods.p_line >= BOTTOM();
  651.         
  652.         i = ods.cur_e;            /* prepare for shifted cur_e */
  653.         ods.cur_l = next_line(&ods.cur_e, ods.cur_l);
  654.  
  655.         if(i != ods.cur_e){    /* new field ! */
  656.             InvertPrompt(i, FALSE);
  657.             switch(i){
  658.               case TOHDR:
  659.               case CCHDR:
  660.               case BCCHDR:
  661.             /* 
  662.              * because of the way resolve_niks works top_l
  663.              * may get broken...
  664.              */
  665.             if((i=resolve_niks(i)) != -1){
  666.                 if(status || i){
  667.                 ods.p_line = 0; /* force new top line */
  668.                 status = TRUE;
  669.                 }
  670.             }
  671.             break;
  672. #ifdef    ATTACHMENTS
  673.               case ATTCHDR:
  674.             /*
  675.              * make sure things are in order, check files
  676.              * and comments
  677.              */
  678.             if(status = SyncAttach()){ /* fixup if 1 or -1 */
  679.                 if(FormatLines(headents[ATTCHDR].hd_text, "",
  680.                        term.t_ncol-headents[ATTCHDR].prlen,
  681.                        ATTCHDR) == -1)
  682.                   emlwrite("\007Format lines failed!", NULL);
  683.             }
  684.             break;
  685. #endif
  686.               default:
  687.             break;
  688.             }
  689.             InvertPrompt(ods.cur_e, TRUE);
  690.             ShowPrompt();
  691.         }
  692.  
  693.         if(ods.p_off > strlen(ods.cur_l->text))
  694.           ods.p_off = strlen(ods.cur_l->text);
  695.  
  696.         if(status){
  697.             UpdateHeader();
  698.             PaintHeader(COMPOSER_TOP_LINE, FALSE);
  699.             PaintBody(1);
  700.         }
  701.         }
  702.         break;
  703.  
  704.       case (CTRL|'P') :
  705.       case K_PAD_UP :
  706.         if(ods.cur_e == TOHDR && ods.cur_l->prev == NULL){
  707.         emlwrite("Can't move beyond top of header", NULL);
  708.         }
  709.         else{
  710.         if(ods.p_line-- == COMPOSER_TOP_LINE)
  711.           status = TRUE;        /* refigure bounds */
  712.         else
  713.           status = FALSE;
  714.  
  715.         i = ods.cur_e;
  716.         ods.cur_l = prev_line(&ods.cur_e, ods.cur_l);
  717.         if(ods.p_off > strlen(ods.cur_l->text))
  718.           ods.p_off = strlen(ods.cur_l->text);
  719.  
  720.         if(i != ods.cur_e){        /* new field ! */
  721.             InvertPrompt(i, FALSE);
  722.             switch(i){
  723.               case TOHDR:
  724.               case CCHDR:
  725.               case BCCHDR:
  726.             if((i = resolve_niks(i)) != -1)
  727.               if(status || i)
  728.                 status = TRUE;
  729.             break;
  730. #ifdef    ATTACHMENTS
  731.               case ATTCHDR:
  732.             /*
  733.              * Check that attachments are in order
  734.              */
  735.             if(status = SyncAttach()){    /* returns 1 or -1 */
  736.                 if(FormatLines(headents[ATTCHDR].hd_text, "",
  737.                        term.t_ncol - headents[ATTCHDR].prlen,
  738.                        ATTCHDR) == -1)
  739.                   emlwrite("\007Format lines failed!", NULL);
  740.             }
  741.             break;
  742. #endif
  743.               default:
  744.             break;
  745.             }
  746.             InvertPrompt(ods.cur_e, TRUE);
  747.             ShowPrompt();
  748.         }
  749.         if(status){
  750.             UpdateHeader();
  751.             PaintHeader(COMPOSER_TOP_LINE, FALSE);
  752.             PaintBody(1);
  753.         }
  754.         }
  755.         break;
  756.  
  757.       case (CTRL|'T') :            /* address book. */
  758.         if(ods.cur_e == FCCHDR){
  759.         if((*Pmaster->folders)(s))        /* pine call */
  760.           strcpy(headents[FCCHDR].hd_text->text, s);
  761.         }
  762.         else if(ods.cur_e==BCCHDR || ods.cur_e==TOHDR || ods.cur_e==CCHDR){
  763.         if((bufp = (*Pmaster->addrbook)(1)) != NULL){    /* pine call */
  764.             if(ods.cur_l->text[0] != '\0')
  765.               strcat(bufp, ", ");
  766.             if(FormatLines(ods.cur_l, bufp,
  767.                    (term.t_ncol-headents[ods.cur_e].prlen), 
  768.                    ods.cur_e) == -1){
  769.             emlwrite("Problem adding address to header !", NULL);
  770.             (*term.t_beep)();
  771.             break;
  772.             }
  773.             UpdateHeader();
  774.         }
  775.         }
  776. #ifdef    ATTACHMENTS
  777.         else if(ods.cur_e == ATTCHDR){
  778.         char dir[NLINE], fn[NLINE], sz[NLINE];
  779.  
  780.         strcpy(dir, gethomedir(NULL));
  781.         switch(FileBrowse(dir, fn, sz)){
  782.           case 1:            /* got a new file */
  783.             sprintf(s, "%s%c%s (%s) \"\"%s", dir, C_FILESEP, fn, sz, 
  784.               (headents[ATTCHDR].hd_text->text[0] == '\0') ? "" : ",");
  785.             if(FormatLines(headents[ATTCHDR].hd_text, s,
  786.                    term.t_ncol - headents[ATTCHDR].prlen,
  787.                    ATTCHDR) == -1){
  788.             emlwrite("\007Format lines failed!", NULL);
  789.             }
  790.             UpdateHeader();
  791.             break;
  792.           case 0:            /* nothing of interest */
  793.             break;
  794.           default:
  795.             break;
  796.         }
  797.         }
  798. #endif
  799.         else{
  800.         (*term.t_beep)();
  801.         continue;
  802.         }
  803.         PaintBody(0);
  804.         continue;
  805.  
  806.       case (CTRL|'G'):            /* HELP */
  807.         ComposerHelp(ods.cur_e);        /* fall through... */
  808.  
  809.       case (CTRL|'L'):            /* redraw requested */
  810.         PaintBody(0);
  811.         break;
  812.  
  813.       default :                /* huh? */
  814.         if(ch&CTRL)
  815.           emlwrite("\007Unknown command: ^%c", (void *)(ch&0xff));
  816.         else
  817.       case BADESC:
  818.           emlwrite("\007Unknown command", NULL);
  819.  
  820.       case NODATA:
  821.         break;
  822.     }
  823.     }
  824.     while (ods.p_line < ComposerTopLine);
  825.  
  826.     display_delimiter(1);
  827.     curwp->w_flag |= WFMODE;
  828.     movecursor(currow, curcol);
  829.     ComposerEditing = FALSE;
  830.     return(retval);
  831. }
  832.  
  833.  
  834.  
  835.  
  836. /*
  837.  * LineEdit - the idea is to manage 7 bit ascii character only input.
  838.  *            Always use insert mode and handle line wrapping
  839.  *
  840.  *    returns:
  841.  *        Any characters typed in that aren't printable 
  842.  *        (i.e. commands)
  843.  *
  844.  *    notes: 
  845.  *        Assume we are guaranteed that there is sufficiently 
  846.  *        more buffer space in a line than screen width (just one 
  847.  *        less thing to worry about).  If you want to change this,
  848.  *        then pputc will have to be taught to check the line buffer
  849.  *        length, and HALLOC() will probably have to become a func.
  850.  */
  851. LineEdit(allowedit)
  852. int    allowedit;
  853. {
  854.     register struct    hdr_line   *lp;        /* temporary line pointer    */
  855.     register int    i;
  856.     register int    ch = 0;
  857.     register int    status;            /* various func's return val */
  858.     register char    *tbufp;            /* temporary buffer pointers */
  859.          int    j;
  860.          int    skipmove = 0;
  861.              char    *strng;
  862.  
  863.     strng   = ods.cur_l->text;            /* initialize offsets */
  864.     ods.p_len = strlen(strng);
  865.     if(ods.p_off < 0)                /* offset within range? */
  866.       ods.p_off = 0;
  867.     else if(ods.p_off > ods.p_len)
  868.       ods.p_off = ods.p_len;
  869.     else if(ods.p_off > LINELEN())        /* shouldn't happen, but */
  870.         ods.p_off = LINELEN();            /* you never know...     */
  871.  
  872.     while(1){                    /* edit the line... */
  873.  
  874.     if(skipmove)
  875.       skipmove = 0;
  876.     else
  877.       movecursor(ods.p_line, ods.p_off+headents[ods.cur_e].prlen);
  878.  
  879.     last_key = ch;
  880.  
  881.     (*term.t_flush)();            /* get everything out */
  882.  
  883. #ifdef HEBREW
  884.     message_mode=1;
  885. #endif
  886.         ch = GetKey();
  887. #ifdef HEBREW
  888.     message_mode=0;
  889. #endif
  890.  
  891.  
  892.     if(ch == NODATA || time_to_check()){    /* new mail ? */
  893.         if((*Pmaster->newmail)(&j, 0, ch == NODATA ? 0 : 2) >= 0){
  894.         mlerase();
  895.         (*Pmaster->showmsg)(ch);
  896.         mpresf = 1;
  897.         }
  898.  
  899.         if(j || mpresf){
  900.         (*term.t_move)(ods.p_line,ods.p_off+headents[ods.cur_e].prlen);
  901.         (Pmaster->clearcur)();
  902.         }
  903.  
  904.         if(ch == NODATA)            /* GetKey timed out */
  905.           continue;
  906.     }
  907.  
  908.         if(mpresf){                /* blast old messages */
  909.         if(mpresf++ > MESSDELAY){        /* every few keystrokes */
  910.         mlerase();
  911.         movecursor(ods.p_line, ods.p_off+headents[ods.cur_e].prlen);
  912.         }
  913.         }
  914.  
  915.  
  916.         if(ch > 0x1f && ch < 0x7f){        /* char input */
  917.             /*
  918.              * if we are allowing editing, insert the new char
  919.              * end up leaving tbufp pointing to newly
  920.              * inserted character in string, and offset to the
  921.              * index of the character after the inserted ch ...
  922.              */
  923.             if(allowedit){
  924.         if(ods.cur_e == FCCHDR && !fallowc((char)ch)){
  925.             /* no garbage in filenames */
  926.             emlwrite("\007Can't have a '%c' in folder name",(void *)ch);
  927.             continue;
  928.         }
  929.         else if(ods.cur_e == ATTCHDR && intag(strng, ods.p_off)){
  930.             emlwrite("\007Can't edit attachment number!", NULL);
  931.             continue;
  932.         }
  933.  
  934.         if(ods.cur_e != SUBJHDR){    /* single spaced except subj.*/
  935.             if(ch == ' ' 
  936.                && (strng[ods.p_off]==' ' || strng[ods.p_off-1]==' '))
  937.               continue;
  938.         }
  939.  
  940.         /*
  941.          * go ahead and add the character...
  942.          */
  943.         tbufp = &strng[++ods.p_len];    /* find the end */
  944.         do{
  945.             *tbufp = tbufp[-1];
  946.         } while(--tbufp > &strng[ods.p_off]);    /* shift right */
  947.         strng[ods.p_off++] = ch;    /* add char to str */
  948.  
  949.         /*
  950.          * then find out where things fit...
  951.          */
  952.         if(ods.p_len < LINELEN()){
  953.             CELL c;
  954.  
  955.             c.a = 0;
  956.             c.c = ch;
  957.             if(pinsert(c)){        /* add char to str */
  958.             skipmove++;        /* must'a been optimal */
  959.             continue;         /* on to the next! */
  960.             }
  961.         }
  962.         else{
  963.                     if((status = FormatLines(ods.cur_l, "", LINELEN(), 
  964.                          ods.cur_e)) == -1){
  965.                         (*term.t_beep)();
  966.                         continue;
  967.                     }
  968.                     else{
  969.             /*
  970.              * during the format, the dot may have moved
  971.              * down to the next line...
  972.              */
  973.             if(ods.p_off >= strlen(strng)){
  974.                 ods.p_line++;
  975.                 ods.p_off -= strlen(strng);
  976.                 ods.cur_l = ods.cur_l->next;
  977.                 strng = ods.cur_l->text;
  978.             }
  979.             ods.p_len = strlen(strng);
  980.             }
  981.             UpdateHeader();
  982.             PaintHeader(COMPOSER_TOP_LINE, FALSE);
  983.             PaintBody(1);
  984.                     continue;
  985.         }
  986.             }
  987.             else{  
  988.                 (*term.t_beep)();
  989.                 continue;
  990.             } 
  991.         }
  992.         else {                    /* interpret ch as a command */
  993.             switch (ch = normal(ch, ckm, 2)) {
  994.           case (CTRL|'@') :        /* word skip */
  995.         while(!isspace(strng[ods.p_off])){
  996.             if(ods.p_off == strlen(strng)){
  997.             ods.p_off = 0;
  998.             return(K_PAD_DOWN);
  999.             }
  1000.             else
  1001.               ods.p_off++;
  1002.         }
  1003.         /* 
  1004.          * move past the space, if that's the end of the line,
  1005.          * move to the next...
  1006.          */
  1007.         if(strng[++ods.p_off] == '\0'){
  1008.             ods.p_off = 0;
  1009.             return(K_PAD_DOWN);
  1010.         }
  1011.         continue;
  1012.  
  1013.           case (CTRL|'K') :            /* kill line cursor's on */
  1014.         lp = ods.cur_l;
  1015.         ods.p_off = 0;
  1016.  
  1017.         if(ods.cur_l->next != NULL && ods.cur_l->prev != NULL)
  1018.           ods.cur_l = next_line(&ods.cur_e, ods.cur_l);
  1019.         else if(ods.cur_l->prev != NULL)
  1020.           ods.cur_l = prev_line(&ods.cur_e, ods.cur_l);
  1021.  
  1022.         if(KillHeaderLine(lp, (last_key == (CTRL|'K')))){
  1023.             if(optimize && 
  1024.                !(ods.cur_l->prev==NULL && ods.cur_l->next==NULL))
  1025.               scrollup(wheadp, ods.p_line, 1);
  1026.  
  1027.             if(ods.cur_l->next == NULL)
  1028.               zotcomma(ods.cur_l->text);
  1029.             
  1030.             i = (ods.p_line == COMPOSER_TOP_LINE);
  1031.             UpdateHeader();
  1032.             PaintHeader(i ? COMPOSER_TOP_LINE: ods.p_line, FALSE);
  1033.             PaintBody(1);
  1034.         }
  1035.         strng = ods.cur_l->text;
  1036.         ods.p_len = strlen(strng);
  1037.         continue;
  1038.  
  1039.           case (CTRL|'U') :            /* un-delete deleted lines */
  1040.         if(SaveHeaderLines()){
  1041.             UpdateHeader();
  1042.             PaintHeader(COMPOSER_TOP_LINE, FALSE);
  1043.             PaintBody(1);
  1044.  
  1045.             ods.p_off = 0;        /* dot hasn't moved! */
  1046.             strng = ods.cur_l->text;
  1047.             ods.p_len = strlen(strng);
  1048.         }
  1049.         else
  1050.           emlwrite("Problem Unkilling text", NULL);
  1051.         continue;
  1052.  
  1053.           case (CTRL|'F') :
  1054.           case K_PAD_RIGHT:            /* move character right */
  1055.         if(ods.p_off < ods.p_len){
  1056.             pputc(pscr(ods.p_line, 
  1057.                    (ods.p_off++)+headents[ods.cur_e].prlen)->c,0);
  1058.             skipmove++;
  1059.             continue;
  1060.         }
  1061.         ods.p_off = 0;
  1062.         return(K_PAD_DOWN);
  1063.  
  1064.           case (CTRL|'B') :
  1065.           case K_PAD_LEFT    :        /* move character left */
  1066.         if(ods.p_off > 0){
  1067.             ods.p_off--;
  1068.             continue;
  1069.         }
  1070.         if(ods.p_line != COMPOSER_TOP_LINE)
  1071.           ods.p_off = 1000;        /* put cursor at end of line */
  1072.         return(K_PAD_UP);
  1073.  
  1074.           case (CTRL|'M') :            /* goto next field */
  1075.         ods.p_off = 0;
  1076.         return(K_PAD_DOWN);
  1077.  
  1078.           case K_PAD_HOME :
  1079.           case (CTRL|'A') :            /* goto beginning of line */
  1080.         ods.p_off = 0;
  1081.         continue;
  1082.  
  1083.           case K_PAD_END  :
  1084.           case (CTRL|'E') :            /* goto end of line */
  1085.         ods.p_off = ods.p_len;
  1086.         continue;
  1087.  
  1088.           case (CTRL|'D')   :        /* blast this char */
  1089.           case K_PAD_DELETE :
  1090.         if(!allowedit){
  1091.             (*term.t_beep)();
  1092.             continue;
  1093.         }
  1094.         else if(ods.p_off >= strlen(strng))
  1095.           continue;
  1096.  
  1097.         if(ods.cur_e == ATTCHDR && intag(strng, ods.p_off)){
  1098.             emlwrite("\007Can't edit attachment number!", NULL);
  1099.             continue;
  1100.         }
  1101.  
  1102.         pputc(strng[ods.p_off++], 0);     /* drop through and rubout */
  1103.  
  1104.           case 0x7f       :            /* blast previous char */
  1105.           case (CTRL|'H') :
  1106.         if(!allowedit){
  1107.             (*term.t_beep)();
  1108.             continue;
  1109.         }
  1110.  
  1111.         if(ods.cur_e == ATTCHDR && intag(strng, ods.p_off - 1)){
  1112.             emlwrite("\007Can't edit attachment number!", NULL);
  1113.             continue;
  1114.         }
  1115.  
  1116.         if(ods.p_off > 0){        /* just shift left one char */
  1117.             ods.p_len--;
  1118.             tbufp = &strng[--ods.p_off];
  1119.             while(*tbufp++ != '\0')
  1120.               tbufp[-1] = *tbufp;
  1121.             tbufp = &strng[ods.p_off];
  1122.             if(pdel())            /* physical screen delete */
  1123.               skipmove++;        /* must'a been optimal */
  1124.         }
  1125.         else{                /* may have work to do */
  1126.             if(ods.cur_l->prev == NULL){  
  1127.             (*term.t_beep)();    /* no erase into next field */
  1128.             continue;
  1129.             }
  1130.  
  1131.             ods.p_line--;
  1132.             ods.cur_l = ods.cur_l->prev;
  1133.             strng = ods.cur_l->text;
  1134.             if((i=strlen(strng)) > 0){
  1135.             strng[i-1] = '\0';    /* erase the character */
  1136.             ods.p_off = i-1;
  1137.             }
  1138.             else
  1139.               ods.p_off = 0;
  1140.             
  1141.             tbufp = &strng[ods.p_off];
  1142.         }
  1143.  
  1144.         if((status = FormatLines(ods.cur_l, "", LINELEN(), 
  1145.                      ods.cur_e)) == -1){
  1146.             (*term.t_beep)();
  1147.             continue;
  1148.         }
  1149.         else{
  1150.             /*
  1151.              * beware, the dot may have moved...
  1152.              */
  1153.             while((ods.p_len=strlen(strng)) < ods.p_off){
  1154.             ods.p_line++;
  1155.             ods.p_off -= strlen(strng);
  1156.             ods.cur_l = ods.cur_l->next;
  1157.             strng = ods.cur_l->text;
  1158.             ods.p_len = strlen(strng);
  1159.             tbufp = &strng[ods.p_off];
  1160.             status = TRUE;
  1161.             }
  1162.             UpdateHeader();
  1163.             PaintHeader(COMPOSER_TOP_LINE, FALSE);
  1164.             if(status == TRUE)
  1165.               PaintBody(1);
  1166.         }
  1167.  
  1168.         movecursor(ods.p_line, ods.p_off+headents[ods.cur_e].prlen);
  1169.  
  1170.         if(skipmove)
  1171.           continue;
  1172.  
  1173.         break;
  1174.  
  1175.               default   :
  1176.         return(ch);
  1177.             }
  1178.         }
  1179.  
  1180.     while (*tbufp != '\0')        /* synchronizing loop */
  1181.       pputc(*tbufp++, 0);
  1182.  
  1183.     if(ods.p_len < LINELEN())
  1184.       peeol();
  1185.  
  1186.     }
  1187. }
  1188.  
  1189.  
  1190.  
  1191. /*
  1192.  * FormatLines - Place the given text at the front of the given line->text
  1193.  *               making sure to properly format the line, then check
  1194.  *               all lines below for proper format.
  1195.  *
  1196.  *    notes:
  1197.  *        this thing is where the actual header formatting gets done.
  1198.  *        currently, all line breaks are done on commas.  this could 
  1199.  *        be a problem if, in the subject field, not breaking on 
  1200.  *        spaces becomes objectionable.  all that needs to be done
  1201.  *        is that this func gets passed the entry as well as the 
  1202.  *        line pointer, and the entry gets compared to SUBJHDR and
  1203.  *        the appropriate break character set when calling 
  1204.  *        break_point().
  1205.  *
  1206.  *        also, i haven't done much optimization at all.  right now,
  1207.  *        FormatLines recursively fixes all remaining lines in the
  1208.  *        entry.  some speed might gained if this was built to
  1209.  *        iteratively scan the lines.
  1210.  *
  1211.  *    returns:
  1212.  *        -1 on error
  1213.  *        FALSE if only this line is changed
  1214.  *        TRUE  if text below the first line is changed
  1215.  */
  1216. FormatLines(h, instr, maxlen, entry)
  1217. struct  hdr_line  *h;                /* where to begin formatting */
  1218. char    *instr;                    /* input string */
  1219. int    maxlen;                    /* max chars on a line */
  1220. int    entry;                    /* which header field */
  1221. {
  1222.     int        retval = FALSE;
  1223.     register    int    i, l, addr;
  1224.     char    *ostr;                /* pointer to output string */
  1225.     register    char    *breakp;        /* pointer to line break */
  1226.     register    char    *bp, *tp;        /* temporary pointers */
  1227.     char    *buf;                /* string to add later */
  1228.     struct hdr_line    *nlp, *lp;
  1229.  
  1230.     ostr = h->text;
  1231.     nlp = h->next;
  1232.     l = strlen(instr) + strlen(ostr);
  1233.     addr = (entry == TOHDR || entry == CCHDR 
  1234. #ifdef    ATTACHMENTS
  1235.         || entry == BCCHDR || entry == ATTCHDR); /* ATTCHDR breaks on ,s too */
  1236. #else
  1237.         || entry == BCCHDR);
  1238. #endif
  1239.     if((buf = (char *)malloc(l+10)) == NULL)
  1240.       return(-1);
  1241.  
  1242.     if(l >= maxlen){                /* break then fixup below */
  1243.     if(strlen(instr) < maxlen){        /* room for more */
  1244.  
  1245.         if(addr && ((bp = (char *)strchr(instr, ',')) != NULL)){
  1246.         if(bp[1] == ' ')
  1247.           bp += 2;
  1248.         else
  1249.           bp++;
  1250.         for(tp = bp;*tp != '\0' && *tp == ' '; tp++)
  1251.           ;
  1252.  
  1253.         strcpy(buf, tp);
  1254.         strcat(buf, ostr);
  1255.         for(i = 0; &instr[i] < bp; i++)
  1256.           ostr[i] = instr[i];
  1257.         ostr[i] = '\0';
  1258.         retval = TRUE;
  1259.         }
  1260.         else{
  1261.  
  1262.         breakp = break_point(ostr, maxlen-strlen(instr),
  1263.                      addr ? ',' : ' ');
  1264.  
  1265.         if(breakp == ostr){        /* no good breakpoint */
  1266.             if(strchr(instr, addr ? ',' : ' ') == NULL){ /* cont'd */
  1267.             breakp = &ostr[maxlen-strlen(instr)-1];
  1268.             retval = TRUE;
  1269.             }
  1270.             else{    /* instr's as broken as we can get it */
  1271.             strcpy(buf, ostr);
  1272.             strcpy(ostr, instr);
  1273.             }
  1274.         }
  1275.         else
  1276.           retval = TRUE;
  1277.         
  1278.         if(retval){
  1279.             strcpy(buf, breakp);    /* save broken line  */
  1280.             if(breakp == ostr){
  1281.             strcpy(ostr, instr);    /* simple if no break */
  1282.             }
  1283.             else{
  1284.             *breakp = '\0';        /* more work to break it */
  1285.             i = strlen(instr);
  1286.             /*
  1287.              * shift ostr i chars
  1288.              */
  1289.             for(bp=breakp; bp >= ostr && i; bp--)
  1290.               *(bp+i) = *bp;
  1291.             for(tp=ostr, bp=instr; *bp != '\0'; tp++, bp++)
  1292.               *tp = *bp;        /* then add instr */
  1293.             }
  1294.         }
  1295.         }
  1296.     }
  1297.     else{                    /* instr > maxlen ! */
  1298.         if(addr){
  1299.         if(((bp=(char *)strchr(instr, ',')) == NULL) 
  1300.            || bp - instr >= maxlen)
  1301.           breakp = &instr[maxlen];
  1302.         else{
  1303.             if(bp[1] == ' ')
  1304.               breakp = bp + 2;
  1305.             else
  1306.               breakp = bp + 1;
  1307.         }
  1308.         }
  1309.         else{
  1310.         breakp = break_point(instr, maxlen, ' ');
  1311.  
  1312.         if(breakp == instr)        /* no good break point */
  1313.           breakp = &instr[maxlen - 1];
  1314.         }
  1315.         
  1316.         strcpy(buf, breakp);        /* save broken line */
  1317.         strcat(buf, ostr);            /* add line that was there */
  1318.         for(tp=ostr,bp=instr; bp < breakp; tp++, bp++)
  1319.           *tp = *bp;
  1320.         *tp = '\0';
  1321.     }
  1322.  
  1323.     if(nlp == NULL){            /* no place to add below? */
  1324.         if((lp = HALLOC()) == NULL){
  1325.         emlwrite("Can't allocate any more lines for header!", NULL);
  1326.         free(buf);
  1327.         return(-1);
  1328.         }
  1329.  
  1330.         if(optimize)
  1331.           if((i=physical_line(h)) != -1)
  1332.           scrolldown(wheadp, i-1, 1);
  1333.  
  1334.         h->next = lp;            /* fix up links */
  1335.         lp->prev = h;
  1336.         lp->next = NULL;
  1337.         lp->text[0] = '\0';
  1338.         nlp = lp;
  1339.         retval = TRUE;
  1340.     }
  1341.     else
  1342.         retval = FALSE;
  1343.     }
  1344.     else{                    /* combined length < max */
  1345.     if(*instr != '\0'){
  1346.         strcpy(buf, instr);            /* insert instr before ostr */
  1347.         strcat(buf, ostr);
  1348.         strcpy(ostr, buf);
  1349.     }
  1350.     *buf = '\0';
  1351.     breakp = NULL;
  1352.  
  1353.     if(addr && (breakp=(char *)strchr(ostr, ',')) != NULL){
  1354.         if(breakp[1] == ' ')
  1355.           breakp += 2;
  1356.         else
  1357.           breakp++;
  1358.  
  1359.         strcpy(buf, breakp);
  1360.         *breakp = '\0';
  1361.  
  1362.         if(strlen(buf)){
  1363.         if(nlp == NULL){
  1364.             if((lp = HALLOC()) == NULL){
  1365.             emlwrite("Can't allocate any more lines for header!", NULL);
  1366.             free(buf);
  1367.             return(-1);
  1368.             }
  1369.  
  1370.             if(optimize)
  1371.               if((i=physical_line(h)) != -1)
  1372.             scrolldown(wheadp, i-1, 1);
  1373.  
  1374.             h->next = lp;        /* fix up links */
  1375.             lp->prev = h;
  1376.             lp->next = NULL;
  1377.             lp->text[0] = '\0';
  1378.             nlp = lp;
  1379.             retval = TRUE;
  1380.         }
  1381.         }
  1382.     }
  1383.  
  1384.     if(nlp == NULL){
  1385.         free(buf);
  1386.         return(FALSE);
  1387.     }
  1388.     else{
  1389.         if(!strlen(buf) && breakp == NULL){
  1390.         if(strlen(ostr) + strlen(nlp->text) >= maxlen){
  1391.             breakp = break_point(nlp->text, maxlen-strlen(ostr), 
  1392.                      addr ? ',' : ' ');
  1393.             
  1394.             if(breakp == nlp->text){    /* commas this line? */
  1395.             for(tp=ostr; *tp != '\0'; tp++){
  1396.                 if(*tp == (addr ? ',' : ' '))
  1397.                   break;
  1398.             }
  1399.             if(*tp == '\0'){
  1400.                 /* no commas, get next best break point */
  1401.                 breakp += maxlen-strlen(ostr)-1;
  1402.                 retval = TRUE;
  1403.             }
  1404.             else
  1405.               retval = FALSE;
  1406.             }
  1407.             else
  1408.               retval = TRUE;
  1409.  
  1410.             if(retval){            /* only if something to do */
  1411.             for(tp = &ostr[strlen(ostr)],bp=nlp->text; bp<breakp; 
  1412.             tp++, bp++)
  1413.               *tp = *bp;        /* add breakp to this line */
  1414.             *tp = '\0';
  1415.             for(tp=nlp->text, bp=breakp; *bp != '\0'; tp++, bp++)
  1416.               *tp = *bp;        /* shift next line to left */
  1417.             *tp = '\0';
  1418.             }
  1419.         }
  1420.         else{
  1421.             strcat(ostr, nlp->text);
  1422.  
  1423.             if(optimize)
  1424.               if((i=physical_line(nlp)) != -1)
  1425.             scrollup(wheadp, i, 1);
  1426.  
  1427.             hldelete(nlp);
  1428.  
  1429.             if((nlp = h->next) == NULL){
  1430.             free(buf);
  1431.             return(TRUE);        /* can't go further */
  1432.             }
  1433.             else
  1434.               retval = TRUE;        /* more work to do? */
  1435.         }
  1436.         }
  1437.     }
  1438.     }
  1439.  
  1440.     i = FormatLines(nlp, buf, maxlen, entry);    /* add buf below */
  1441.     free(buf);
  1442.     switch(i){
  1443.       case -1:                    /* bubble up worst case */
  1444.     return(-1);
  1445.       case FALSE:
  1446.     if(retval == FALSE)
  1447.       return(FALSE);
  1448.       default:
  1449.     return(TRUE);
  1450.     }
  1451. }
  1452.  
  1453.  
  1454.  
  1455. /*
  1456.  * PaintHeader - do the work of displaying the header from the given 
  1457.  *               physical screen line the end of the header.
  1458.  *
  1459.  *       17 July 91 - fixed reshow to deal with arbitrarily large headers.
  1460.  */
  1461. void
  1462. PaintHeader(line, clear)
  1463. int    line;                    /* physical line on screen   */
  1464. int    clear;                    /* clear before painting */
  1465. {
  1466.     register struct hdr_line    *lp;
  1467.     register char    *bufp;
  1468.     register int    curline;
  1469.     register int    curoffset;
  1470.     int      e;
  1471.  
  1472. #ifndef HEBREW
  1473.     if(clear)
  1474.       pclear(COMPOSER_TOP_LINE, ComposerTopLine);
  1475. #else    
  1476.     if(!compose_heb){
  1477.       if(clear)
  1478.          pclear(COMPOSER_TOP_LINE, ComposerTopLine);
  1479.      }
  1480.      else pclear(COMPOSER_TOP_LINE,COMPOSER_TOP_LINE+5);
  1481.     /* How many lines are in header ? */
  1482. #endif    
  1483.  
  1484.     curline   = COMPOSER_TOP_LINE;
  1485.     curoffset = 0;
  1486.  
  1487.     for(lp=ods.top_l, e=ods.top_e; ; curline++){
  1488.     if((curline == line) || ((lp = next_line(&e, lp)) == NULL))
  1489.       break;
  1490.     }
  1491.  
  1492.     while(e <= LASTHDR){            /* begin to redraw */
  1493.     while(lp != NULL){
  1494.         *s = '\0';
  1495.             if((!lp->prev || curline == COMPOSER_TOP_LINE) && !curoffset){
  1496.             if(InvertPrompt(e, (e == ods.cur_e && ComposerEditing)) == -1
  1497.            && !is_blank(curline, 0, headents[e].prlen))
  1498.            sprintf(s, "          ");
  1499.         }
  1500.         else if(!is_blank(curline, 0, headents[e].prlen))
  1501.           sprintf(s, "          ");
  1502.  
  1503.         if(*(bufp = s) != '\0'){        /* need to paint? */
  1504.         movecursor(curline, 0);        /* paint the line... */
  1505.         while(*bufp != '\0')
  1506.           pputc(*bufp++, 0);
  1507.         }
  1508.  
  1509.         bufp = &(lp->text[curoffset]);    /* skip chars already there */
  1510.         curoffset += headents[e].prlen;
  1511.         while(*bufp == pscr(curline, curoffset)->c && *bufp != '\0'){
  1512.         bufp++;
  1513.         curoffset++;
  1514.         }
  1515.  
  1516.         if(*bufp != '\0'){            /* need to move? */
  1517.         movecursor(curline, curoffset);
  1518.         while(*bufp != '\0'){        /* display what's not there */
  1519.             pputc(*bufp++, 0);
  1520.             curoffset++;
  1521.         }
  1522.         }
  1523.  
  1524. #ifndef  HEBREW
  1525.         if(curoffset < term.t_ncol 
  1526.            && !is_blank(curline, curoffset, term.t_ncol - curoffset)){
  1527.         movecursor(curline, curoffset);
  1528.         peeol();
  1529.         }
  1530. #else
  1531.         if(compose_heb || (curoffset < term.t_ncol 
  1532.            && !is_blank(curline, curoffset, term.t_ncol - curoffset))){
  1533.         movecursor(curline, curoffset);
  1534.         peeol();
  1535.         }
  1536. #endif
  1537.         curline++;
  1538.  
  1539.             curoffset = 0;
  1540.         if(curline >= BOTTOM())
  1541.           break;
  1542.  
  1543.         lp = lp->next;
  1544.         }
  1545.  
  1546.     if(curline == BOTTOM())
  1547.       return;                /* don't paint delimiter */
  1548.  
  1549.     while(++e <= LASTHDR)
  1550.       if(headents[e].display_it){
  1551.           lp = headents[e].hd_text;
  1552.           break;
  1553.       }
  1554.     }
  1555.  
  1556.     display_delimiter(ComposerEditing ? 0 : 1);
  1557. }
  1558.  
  1559.  
  1560.  
  1561.  
  1562. /*
  1563.  * PaintBody() - generic call to handle repainting everything BUT the 
  1564.  *         header
  1565.  *
  1566.  *    notes:
  1567.  *        The header redrawing in a level 0 body paint gets done
  1568.  *        in update()
  1569.  */
  1570. PaintBody(level)
  1571. int    level;
  1572. {
  1573.     curwp->w_flag |= WFHARD;            /* make sure framing's right */
  1574.     if(level == 0)                /* specify what to update */
  1575.         sgarbf = TRUE;
  1576.  
  1577.     update();                    /* display message body */
  1578.  
  1579.     if(level == 0 && ComposerEditing){
  1580.     mlerase();                /* clear the error line */
  1581.     ShowPrompt();
  1582.     }
  1583. }
  1584.  
  1585.  
  1586.  
  1587. /*
  1588.  * ArrangeHeader - set up display parm such that header is reasonably 
  1589.  *                 displayed
  1590.  */
  1591. ArrangeHeader()
  1592. {
  1593.     int      e;
  1594.     register struct hdr_line *l;
  1595.  
  1596.     ods.p_line = ods.p_off = 0;
  1597.     e = ods.top_e = TOHDR;
  1598.     l = ods.top_l = headents[e].hd_text;
  1599.     while(!(e == LASTHDR && l->next == NULL))
  1600.       l = next_line(&e, l);
  1601.  
  1602.     ods.cur_l = l;
  1603.     ods.cur_e = e;
  1604.     UpdateHeader();
  1605. }
  1606.  
  1607.  
  1608. /*
  1609.  * ComposerHelp() - display mail help in a context sensitive way
  1610.  *                  based on the level passed ...
  1611.  */
  1612. ComposerHelp(level)
  1613. int    level;
  1614. {
  1615.     curwp->w_flag |= WFMODE;
  1616.     sgarbf = TRUE;
  1617.  
  1618.     if(level < 0 || level > LASTHDR){
  1619.     (*term.t_beep)();
  1620.     emlwrite("Sorry, I can't help you with that.", NULL);
  1621.     return(FALSE);
  1622.     }
  1623.     sprintf(s,"Help for Composer %s Field", headents[level].name);
  1624.     (*Pmaster->helper)(headents[level].help, s, 1);
  1625. }
  1626.  
  1627.  
  1628.  
  1629. /*
  1630.  * ToggleHeader() - set or unset pico values to the full screen size
  1631.  *                  painting header if need be.
  1632.  */
  1633. ToggleHeader(show)
  1634. int show;
  1635. {
  1636.     /*
  1637.      * check to see if we need to display the header... 
  1638.      */
  1639.     if(show){
  1640.     UpdateHeader();                /* figure bounds  */
  1641.     PaintHeader(COMPOSER_TOP_LINE, FALSE);    /* draw it */
  1642.     }
  1643.     else{
  1644.         /*
  1645.          * set bounds for no header display
  1646.          */
  1647.         curwp->w_toprow = ComposerTopLine = COMPOSER_TOP_LINE;
  1648.         curwp->w_ntrows = BOTTOM() - ComposerTopLine;
  1649. #ifdef HEBREW    
  1650.     if(compose_heb)pclear(curwp->w_toprow,curwp->w_toprow+5);
  1651.     /* How many lines are in header ? */
  1652. #endif    
  1653.     }
  1654.     return(TRUE);
  1655. }
  1656.  
  1657.  
  1658.  
  1659. /*
  1660.  * HeaderLen() - return the length in lines of the exposed portion of the
  1661.  *               header
  1662.  */
  1663. HeaderLen()
  1664. {
  1665.     register struct hdr_line *lp;
  1666.     int      e;
  1667.     int      i;
  1668.     
  1669.     i = 1;
  1670.     lp = ods.top_l;
  1671.     e  = ods.top_e;
  1672.     while(lp != NULL){
  1673.     lp = next_line(&e, lp);
  1674.     i++;
  1675.     }
  1676.     return(i);
  1677. }
  1678.  
  1679.  
  1680.  
  1681. /*
  1682.  * next_line() - return a pointer to the next line structure
  1683.  * 
  1684.  *    returns:
  1685.  *        1) pointer to next displayable line in header and header
  1686.  *                 entry, via side effect, that the next line is a part of
  1687.  *              2) NULL if no next line, leaving entry at LASTHDR
  1688.  */
  1689. static struct hdr_line *next_line(entry, line)
  1690. int *entry;
  1691. struct hdr_line *line;
  1692. {
  1693.     if(line == NULL)
  1694.       return(NULL);
  1695.  
  1696.     if(line->next == NULL){
  1697.     while(++(*entry) <= LASTHDR){
  1698.         if(headents[*entry].display_it)
  1699.           return(headents[*entry].hd_text);
  1700.     }
  1701.     --(*entry);
  1702.     return(NULL);
  1703.     }
  1704.     else
  1705.       return(line->next);
  1706. }
  1707.  
  1708.  
  1709.  
  1710. /*
  1711.  * prev_line() - return a pointer to the next line structure back
  1712.  * 
  1713.  *    returns:
  1714.  *              1) pointer to previous displayable line in header and 
  1715.  *                 the header entry that the next line is a part of 
  1716.  *                 via side effect
  1717.  *              2) NULL if we can't go back further
  1718.  */
  1719. static struct hdr_line *prev_line(entry, line)
  1720. int *entry;
  1721. struct hdr_line *line;
  1722. {
  1723.     if(line == NULL)
  1724.       return(NULL);
  1725.  
  1726.     if(line->prev == NULL){
  1727.     while(--(*entry) >= TOHDR){
  1728.         if(headents[*entry].display_it){
  1729.         line = headents[*entry].hd_text;
  1730.         while(line->next != NULL)
  1731.           line = line->next;
  1732.         return(line);
  1733.         }
  1734.     }
  1735.     ++(*entry);
  1736.     return(NULL);
  1737.     }
  1738.     else
  1739.       return(line->prev);
  1740. }
  1741.  
  1742.  
  1743.  
  1744. /*
  1745.  * UpdateHeader() - determines the best range of lines to be displayed 
  1746.  *                  using the global ods value for the current line and the
  1747.  *            top line, also sets ComposerTopLine and pico limits
  1748.  *                    
  1749.  *      notes:
  1750.  *            This is pretty ugly because it has to keep the current line
  1751.  *        on the screen in a reasonable location no matter what.
  1752.  *        There are also a couple of rules to follow:
  1753.  *                 1) follow paging conventions of pico (ie, half page 
  1754.  *              scroll)
  1755.  *                 2) if more than one page, always display last half when 
  1756.  *                    pline is toward the end of the header
  1757.  * 
  1758.  *      returns:
  1759.  *             TRUE  if anything changed (side effects: new p_line, top_l
  1760.  *             top_e, and pico parms)
  1761.  *             FALSE if nothing changed 
  1762.  *             
  1763.  */
  1764. UpdateHeader()
  1765. {
  1766.     register struct    hdr_line    *lp;
  1767.     int         i, le;
  1768.     int      ret = FALSE;
  1769.     int      old_top = ComposerTopLine;
  1770.     int      old_p = ods.p_line;
  1771.  
  1772.     if(ods.p_line < COMPOSER_TOP_LINE || ods.p_line >= BOTTOM()){
  1773.     NewTop();                /* get new top_l */
  1774.     ret = TRUE;
  1775.     }
  1776.     else{                    /* make sure p_line's OK */
  1777.     i = COMPOSER_TOP_LINE;
  1778.     lp = ods.top_l;
  1779.     le = ods.top_e;
  1780.     while(lp != ods.cur_l){
  1781.         /*
  1782.          * this checks to make sure cur_l is below top_l and that
  1783.          * cur_l is on the screen...
  1784.          */
  1785.         if((lp = next_line(&le, lp)) == NULL || ++i >= BOTTOM()){
  1786.         NewTop();
  1787.         ret = TRUE;
  1788.         break;
  1789.         }
  1790.     }
  1791.     }
  1792.  
  1793.     ods.p_line = COMPOSER_TOP_LINE;        /* find  p_line... */
  1794.     lp = ods.top_l;
  1795.     le  = ods.top_e;
  1796.     while(lp != ods.cur_l && lp != NULL){
  1797.     lp = next_line(&le, lp);
  1798.     ods.p_line++;
  1799.     }
  1800.  
  1801.     if(!ret)
  1802.       ret = !(ods.p_line == old_p);
  1803.  
  1804.     ComposerTopLine = ods.p_line;
  1805.     while(lp != NULL && ComposerTopLine < BOTTOM()){
  1806.     lp = next_line(&le, lp);
  1807.     ComposerTopLine++;
  1808.     }
  1809.     if(lp == NULL && ComposerTopLine+1 < BOTTOM())
  1810.       ComposerTopLine++;            /* and add one for delimiter */
  1811.  
  1812.     if(!ret)
  1813.       ret = !(ComposerTopLine == old_top);
  1814.  
  1815.     /*
  1816.      * update pico parms if need be...
  1817.      */
  1818.     if(wheadp->w_toprow != ComposerTopLine){
  1819.         wheadp->w_toprow = ComposerTopLine;
  1820.         wheadp->w_ntrows = BOTTOM() - ComposerTopLine;
  1821.     ret = TRUE;
  1822.     }
  1823.     return(ret);
  1824. }
  1825.  
  1826.  
  1827.  
  1828. /*
  1829.  * NewTop() - calculate a new top_l based on the cur_l
  1830.  *
  1831.  *    returns:
  1832.  *        with ods.top_l and top_e pointing at a reasonable line
  1833.  *        entry
  1834.  */
  1835. NewTop()
  1836. {
  1837.     register struct hdr_line *lp;
  1838.     register int i;
  1839.     int      e;
  1840.  
  1841.     lp = ods.cur_l;
  1842.     e  = ods.cur_e;
  1843.     i  = HALF_SCR();
  1844.  
  1845.     while(lp != NULL && i--){
  1846.     ods.top_l = lp;
  1847.     ods.top_e = e;
  1848.     lp = prev_line(&e, lp);
  1849.     }
  1850. }
  1851.  
  1852.  
  1853.  
  1854. /*
  1855.  * display_delimiter() - just paint the header/message body delimiter with
  1856.  *                       inverse value specified by state.
  1857.  */
  1858. void
  1859. display_delimiter(state)
  1860. int    state;
  1861. {
  1862.     register char    *bufp;
  1863.     static   short   ps = 0;            /* previous state */
  1864.  
  1865.     if(ComposerTopLine > BOTTOM())        /* silently forget it */
  1866.       return;
  1867.  
  1868.     bufp = "----- Message Text -----";
  1869.  
  1870.     if(state == ps){                /* optimize ? */
  1871.     for(ps = 0; bufp[ps]
  1872.         && pscr(ComposerTopLine - 1, ps)->c == bufp[ps]; ps++)
  1873.       ;
  1874.  
  1875.     if(bufp[ps] == '\0'){
  1876.         ps = state;
  1877.         return;                /* already displayed! */
  1878.     }
  1879.     }
  1880.  
  1881.     ps = state;
  1882.  
  1883.     movecursor(ComposerTopLine - 1, 0);
  1884.     if(state)
  1885.       (*term.t_rev)(1);
  1886.  
  1887.     while(*bufp != '\0')
  1888.       pputc(*bufp++, 0);
  1889.  
  1890.     if(state)
  1891.       (*term.t_rev)(0);
  1892.  
  1893.     peeol();
  1894. }
  1895.  
  1896.  
  1897.  
  1898. /*
  1899.  * InvertPrompt() - invert the prompt associated with header entry to state
  1900.  *                  state (true if invert, false otherwise).
  1901.  *    returns:
  1902.  *        non-zero if nothing done
  1903.  *        0 if prompt inverted successfully
  1904.  *
  1905.  *    notes:
  1906.  *        come to think of it, this func and the one above could
  1907.  *        easily be combined
  1908.  */
  1909. InvertPrompt(entry, state)
  1910. int    entry, state;
  1911. {
  1912.     register char   *bufp;
  1913.     register int    i;
  1914.     static   short  ps = 0;             /* prev state of entry e */
  1915.  
  1916.     bufp = headents[entry].prompt;        /* fresh prompt paint */
  1917.     if((i = entry_line(entry, FALSE)) == -1)
  1918.       return(-1);                /* silently forget it */
  1919.  
  1920.     if((ps&(1<<entry)) == (state ? 1<<entry : 0)){    /* optimize ? */
  1921.     int j;
  1922.  
  1923.     for(j = 0; bufp[j] && pscr(i, j)->c == bufp[j]; j++)
  1924.       ;
  1925.  
  1926.     if(bufp[j] == '\0'){
  1927.         if(state)
  1928.           ps |= 1<<entry;
  1929.         else
  1930.           ps &= ~(1<<entry);
  1931.         return(0);                /* already displayed! */
  1932.     }
  1933.     }
  1934.  
  1935.     if(state)
  1936.       ps |= 1<<entry;
  1937.     else
  1938.       ps &= ~(1<<entry);
  1939.  
  1940.     movecursor(i, 0);
  1941.     if(state)
  1942.       (*term.t_rev)(1);
  1943.  
  1944.     while(bufp[1] != '\0')            /* putc upto last char */
  1945.       pputc(*bufp++, 1);
  1946.  
  1947.     if(state)
  1948.       (*term.t_rev)(0);
  1949.  
  1950.     pputc(*bufp, 0);                /* last char not inverted */
  1951.     return(TRUE);
  1952. }
  1953.  
  1954.  
  1955.  
  1956.  
  1957. /*
  1958.  * partial_entries() - toggle display of the bcc and fcc fields.
  1959.  *
  1960.  *    returns:
  1961.  *        TRUE if there are partial entries on the display
  1962.  *        FALSE otherwise.
  1963.  */
  1964. partial_entries()
  1965. {
  1966.     register int   i = 0, rv = 0;
  1967.  
  1968.     if(headents[FCCHDR].display_it){
  1969.         headents[BCCHDR].display_it = FALSE;
  1970.         headents[FCCHDR].display_it = FALSE;
  1971.     rv = 1;
  1972.     }
  1973.     else{
  1974.         while(i <= LASTHDR)
  1975.             headents[i++].display_it = TRUE;
  1976.     }
  1977.     return(rv);
  1978. }
  1979.  
  1980.  
  1981.  
  1982. /*
  1983.  * entry_line() - return the physical line on the screen associated
  1984.  *                with the given header entry field.  Note: the field
  1985.  *                may span lines, so if the last char is set, return
  1986.  *                the appropriate value.
  1987.  *
  1988.  *    returns:
  1989.  *             1) physical line number of entry
  1990.  *             2) -1 if entry currently not on display
  1991.  */
  1992. entry_line(entry, lastchar)
  1993. int    entry, lastchar;
  1994. {
  1995.     register int    p_line = COMPOSER_TOP_LINE;
  1996.     int    i;
  1997.     register struct hdr_line    *line;
  1998.  
  1999.     for(line=ods.top_l, i=ods.top_e; i <= LASTHDR && i <= entry; p_line++){
  2000.     if(p_line >= BOTTOM())
  2001.       break;
  2002.     if(i == entry){
  2003.         if(lastchar){
  2004.         if(line->next == NULL)
  2005.           return(p_line);
  2006.         }
  2007.         else if(line->prev == NULL)
  2008.           return(p_line);
  2009.         else
  2010.           return(-1);
  2011.     }
  2012.     line = next_line(&i, line);
  2013.     }
  2014.     return(-1);
  2015. }
  2016.  
  2017.  
  2018.  
  2019. /*
  2020.  * physical_line() - return the physical line on the screen associated
  2021.  *                   with the given header line pointer.
  2022.  *
  2023.  *    returns:
  2024.  *             1) physical line number of entry
  2025.  *             2) -1 if entry currently not on display
  2026.  */
  2027. physical_line(l)
  2028. struct hdr_line *l;
  2029. {
  2030.     register int    p_line = COMPOSER_TOP_LINE;
  2031.     register struct hdr_line    *lp;
  2032.     int    i;
  2033.  
  2034.     for(lp=ods.top_l, i=ods.top_e; i <= LASTHDR && lp != NULL; p_line++){
  2035.     if(p_line >= BOTTOM())
  2036.       break;
  2037.  
  2038.     if(lp == l)
  2039.       return(p_line);
  2040.  
  2041.     lp = next_line(&i, lp);
  2042.     }
  2043.     return(-1);
  2044. }
  2045.  
  2046.  
  2047.  
  2048. /*
  2049.  * resolve_niks() - resolve any nicknames in the address book associated
  2050.  *                  with the given entry...
  2051.  *
  2052.  *    NOTES:
  2053.  * 
  2054.  *      BEWARE: this function can cause cur_l and top_l to get lost so BE 
  2055.  *              CAREFUL before and after you call this function!!!
  2056.  * 
  2057.  *      There could to be something here to resolve cur_l and top_l
  2058.  *      reasonably into the new linked list for this entry.  
  2059.  *
  2060.  *      The reason this would mostly work without it is resolve_niks gets
  2061.  *      called for the most part in between fields.  Since we're moving
  2062.  *      to the beginning or end (i.e. the next/prev pointer in the old 
  2063.  *      freed cur_l is NULL) of the next entry, we get a new cur_l
  2064.  *      pointing at a good line.  Then since top_l is based on cur_l in
  2065.  *      NewTop() we have pretty much lucked out.
  2066.  * 
  2067.  *      Where we could get burned is in a canceled exit (ctrl|x).  Here
  2068.  *      nicknames get resolved into addresses, which invalidates cur_l
  2069.  *      and top_l.  Since we don't actually leave, we could begin editing
  2070.  *      again with bad pointers.  This would usually results in a nice 
  2071.  *      core dump.
  2072.  *
  2073.  *    RETURNS:
  2074.  *              TRUE if any names where resolved, otherwise
  2075.  *              FALSE if not, or
  2076.  *        -1 on error
  2077.  */
  2078. resolve_niks(entry)
  2079. int    entry;
  2080. {
  2081.     register    int     retval = FALSE;
  2082.     register    int    i;
  2083.     register    struct  hdr_line  *line = headents[entry].hd_text;
  2084.     char    *sbuf;
  2085.     char    *errmsg;
  2086.     
  2087.     line = headents[entry].hd_text;
  2088.     i = 0;
  2089.     while(line != NULL){
  2090.     i += term.t_ncol;
  2091.         line = line->next;
  2092.     }
  2093.     if((sbuf=(char *)malloc((unsigned) i)) == NULL){
  2094.     emlwrite("Can't malloc space to expand address", NULL);
  2095.     return(-1);
  2096.     }
  2097.     
  2098.     *sbuf = '\0';
  2099.     /*
  2100.      * cat the whole entry into one string...
  2101.      */
  2102.     line = headents[entry].hd_text;
  2103.     while(line != NULL){
  2104.     i = strlen(line->text);
  2105.     /*
  2106.      * to keep pine address builder happy, addresses should be separated
  2107.      * by ", ".  Add this space if needed, otherwise...
  2108.      *
  2109.      * if this line is NOT a continuation of the previous line, add
  2110.      * white space for pine's address builder if its not already there...
  2111.      *
  2112.      * also if it's not a continuation (i.e., there's already and addr on 
  2113.      * the line), and there's another line below, treat the new line as
  2114.      * an implied comma
  2115.      */
  2116.         if(line->text[i-1] == ',')
  2117.       strcat(line->text, " ");        /* help address builder */
  2118.     else if(line->next != NULL && !strend(line->text, ',')){
  2119.         if(strqchr(line->text, ',')){
  2120.           strcat(line->text, ", ");        /* implied comma */
  2121.       }
  2122.     }
  2123.     else if(line->prev != NULL && line->next != NULL){
  2124.         if(strchr(line->prev->text, ' ') != NULL 
  2125.            && line->text[i-1] != ' ')
  2126.           strcat(line->text, " ");
  2127.     }
  2128.     strcat(sbuf, line->text);
  2129.         line = line->next;
  2130.     }
  2131.  
  2132.     if((retval=(*Pmaster->buildaddr)(sbuf, s, &errmsg)) == -1){
  2133.     sprintf(s, "%s field: %s", headents[entry].name, errmsg);
  2134.     (*term.t_beep)();
  2135.     emlwrite(s, NULL);
  2136.     }
  2137.     else if(strcmp(sbuf, s)){
  2138.     line = headents[entry].hd_text;
  2139.     InitEntryText(s, entry);        /* arrange new one */
  2140.         zotentry(line);             /* blast old list of entries */
  2141.         retval = TRUE;
  2142.     }
  2143.     free(sbuf);
  2144.     return(retval);
  2145. }
  2146.  
  2147.  
  2148. /*
  2149.  * strend - neglecting white space, returns TRUE if c is at the
  2150.  *          end of the given line.  otherwise FALSE.
  2151.  */
  2152. strend(s, ch)
  2153. char *s;
  2154. int   ch;
  2155. {
  2156.     register char *b;
  2157.     register char  c;
  2158.  
  2159.     c = (char)ch;
  2160.  
  2161.     if(s == NULL)
  2162.       return(FALSE);
  2163.  
  2164.     if(*s == '\0')
  2165.       return(FALSE);
  2166.  
  2167.     b = &s[strlen(s)];
  2168.     while(isspace(*--b)){
  2169.     if(b == s)
  2170.       return(FALSE);
  2171.     }
  2172.  
  2173.     return(*b == c);
  2174. }
  2175.  
  2176.  
  2177. /*
  2178.  * strqchr - returns pointer to first non-quote-enclosed occurance of c in 
  2179.  *           the given string.  otherwise NULL.
  2180.  */
  2181. char *strqchr(s, ch)
  2182. char *s;
  2183. int   ch;
  2184. {
  2185.     register char *b;
  2186.     register char  c;
  2187.  
  2188.     c = (char)ch;
  2189.  
  2190.     if((b = s) == NULL)
  2191.       return(NULL);
  2192.  
  2193.     while(*b != '\0'){
  2194.     if(*b == '"'){
  2195.         for(b++; *b != '"'; b++)
  2196.           if(*b == '\0')
  2197.         return(NULL);
  2198.     }
  2199.     if(*b == c)
  2200.       return(b);
  2201.     b++;
  2202.     }
  2203.     return(NULL);
  2204. }
  2205.  
  2206.  
  2207. /*
  2208.  * KillHeaderLine() - kill a line in the header
  2209.  *
  2210.  *    notes:
  2211.  *        This is pretty simple.  Just using the emacs kill buffer
  2212.  *        and its accompanying functions to cut the text from lines.
  2213.  *
  2214.  *    returns:
  2215.  *        TRUE if hldelete worked
  2216.  *        FALSE otherwise
  2217.  */
  2218. KillHeaderLine(l, append)
  2219. struct    hdr_line    *l;
  2220. int     append;
  2221. {
  2222.     register char    *c;
  2223.  
  2224.     if(!append)
  2225.     kdelete();
  2226.  
  2227.     c = l->text;
  2228.     while(*c != '\0')                /* splat out the line */
  2229.       kinsert(*c++);
  2230.  
  2231.     kinsert('\n');                /* helpful to yank in body */
  2232.  
  2233.     return(hldelete(l));            /* blast it  */
  2234. }
  2235.  
  2236.  
  2237.  
  2238. /*
  2239.  * SaveHeaderLines() - insert the saved lines in the list before the 
  2240.  *                     current line in the header
  2241.  *
  2242.  *    notes:
  2243.  *        Once again, just using emacs' kill buffer and its 
  2244.  *              functions.
  2245.  *
  2246.  *    returns:
  2247.  *        TRUE if something good happend
  2248.  *        FALSE otherwise
  2249.  */
  2250. SaveHeaderLines()
  2251. {
  2252.     extern   unsigned    kused;            /* length of kill buffer */
  2253.     char     *buf;                /* malloc'd copy of buffer */
  2254.     register char       *bp;            /* pointer to above buffer */
  2255.     register unsigned    i;            /* index */
  2256.     
  2257.     if(kused){
  2258.     if((bp = buf = (char *)malloc(kused+5)) == NULL){
  2259.         emlwrite("Can't malloc space for saved text", NULL);
  2260.         return(FALSE);
  2261.     }
  2262.     }
  2263.     else
  2264.       return(FALSE);
  2265.  
  2266.     for(i=0; i < kused; i++)
  2267.       if(kremove(i) != '\n')            /* filter out newlines */
  2268.     *bp++ = kremove(i);
  2269.     *bp = '\0';
  2270.  
  2271.     while(--bp >= buf)                /* kill trailing white space */
  2272.       if(*bp != ' '){
  2273.       if(ods.cur_l->text[0] != '\0'){
  2274.           if(*bp == '>'){            /* inserting an address */
  2275.           *++bp = ',';            /* so add separator */
  2276.           *++bp = '\0';
  2277.           }
  2278.       }
  2279.       else{                    /* nothing in field yet */
  2280.           if(*bp == ','){            /* so blast any extra */
  2281.           *bp = '\0';            /* separators */
  2282.           }
  2283.       }
  2284.       break;
  2285.       }
  2286.  
  2287.     if(FormatLines(ods.cur_l, buf, LINELEN(), ods.cur_e) == -1)
  2288.       i = FALSE;
  2289.     else
  2290.       i = TRUE;
  2291.  
  2292.     free(buf);
  2293.     return(i);
  2294. }
  2295.  
  2296.  
  2297.  
  2298.  
  2299. /*
  2300.  * break_point - Break the given line s at the most reasonable character c
  2301.  *               within l max characters.
  2302.  *
  2303.  *    returns:
  2304.  *        Pointer to the best break point in s, or
  2305.  *        Pointer to the beginning of s if no break point found
  2306.  */
  2307. char *break_point(s, l, ch)
  2308. char *s;
  2309. int  l, ch;
  2310. {
  2311.     register char *b;
  2312.     register char  c;
  2313.  
  2314.     c = (char) ch;
  2315.     b = s+l;
  2316.     while(b != s){
  2317.     if(*b == c){
  2318.         if(c == ' '){
  2319.         if(b + 1 < s + l){
  2320.             b++;            /* leave the ' ' */
  2321.             break;
  2322.         }
  2323.         }
  2324.         else{
  2325.         /*
  2326.          * if break char isn't a space, leave a space after
  2327.          * the break char.
  2328.          */
  2329.         if(!(b+1 >= s+l || (b[1] == ' ' && b+2 == s+l))){
  2330.             b += (b[1] == ' ') ? 2 : 1;
  2331.             break;
  2332.         }
  2333.         }
  2334.     }
  2335.     b--;
  2336.     }
  2337.     return(b);
  2338. }
  2339.  
  2340.  
  2341.  
  2342.  
  2343. /*
  2344.  * hldelete() - remove the header line pointed to by l from the linked list
  2345.  *              of lines.
  2346.  *
  2347.  *    notes:
  2348.  *        the case of first line in field is kind of bogus.  since
  2349.  *              the array of headers has a pointer to the first line, and 
  2350.  *        i don't want to worry about this too much, i just copied 
  2351.  *        the line below and removed it rather than the first one
  2352.  *        from the list.
  2353.  *
  2354.  *    returns:
  2355.  *        TRUE if it worked 
  2356.  *        FALSE otherwise
  2357.  */
  2358. hldelete(l)
  2359. struct hdr_line  *l;
  2360. {
  2361.     register struct hdr_line *lp;
  2362.  
  2363.     if(l == NULL)
  2364.       return(FALSE);
  2365.  
  2366.     if(l->next == NULL && l->prev == NULL){    /* only one line in field */
  2367.     l->text[0] = '\0';
  2368.     return(TRUE);                /* no free only line in list */
  2369.     }
  2370.     else if(l->next == NULL){            /* last line in field */
  2371.     l->prev->next = NULL;
  2372.     }
  2373.     else if(l->prev == NULL){            /* first line in field */
  2374.     strcpy(l->text, l->next->text);
  2375.     lp = l->next;
  2376.     if((l->next = lp->next) != NULL)
  2377.       l->next->prev = l;
  2378.     l = lp;
  2379.     }
  2380.     else{                    /* some where in field */
  2381.     l->prev->next = l->next;
  2382.     l->next->prev = l->prev;
  2383.     }
  2384.  
  2385.     l->next = NULL;
  2386.     l->prev = NULL;
  2387.     free((char *)l);
  2388.     return(TRUE);
  2389. }
  2390.  
  2391.  
  2392.  
  2393. /*
  2394.  * is_blank - returns true if the next n chars from coordinates row, col
  2395.  *           on display are spaces
  2396.  */
  2397. is_blank(row, col, n)
  2398. int row, col, n;
  2399. {
  2400.     n += col;
  2401.     for( ;col < n; col++){
  2402.     if(pscr(row, col)->c != ' ')
  2403.       return(0);
  2404.     }
  2405.     return(1);
  2406. }
  2407.  
  2408.  
  2409. /*
  2410.  * ShowPrompt - display key help corresponding to the current header entry
  2411.  */
  2412. ShowPrompt()
  2413. {
  2414.     switch(ods.cur_e){
  2415.       case TOHDR:
  2416.       case CCHDR:
  2417.       case BCCHDR:
  2418. #ifdef    ATTACHMENTS
  2419.     wkeyhelp("GCR0KOXDJ0UT", "Get Help,Cancel,Rich Hdr,Del Line,Postpone,Send,Del Char,Attach,UnDel Line,To AddrBk");
  2420.     break;
  2421.       case FCCHDR:
  2422.     wkeyhelp("GCR0KOXDJ0UT", "Get Help,Cancel,Rich Hdr,Del Line,Postpone,Send,Del Char,Attach,UnDel Line,To Fldrs");
  2423.     break;
  2424.       case ATTCHDR:
  2425.     wkeyhelp("GCR0KOXDJ0UT", "Get Help,Cancel,Rich Hdr,Del Line,Postpone,Send,Del Char,Attach,UnDel Line,To Files");
  2426.     break;
  2427.       default:
  2428.     wkeyhelp("GCR0KOXDJ0U0", "Get Help,Cancel,Rich Hdr,Del Line,Postpone,Send,Del Char,Attach,UnDel Line");
  2429. #else
  2430.     wkeyhelp("GCR0KOXD00UT", "Get Help,Cancel,Rich Hdr,Del Line,Postpone,Send,Del Char,UnDel Line,To AddrBk");
  2431.     break;
  2432.       case FCCHDR:
  2433.     wkeyhelp("GCR0KOXD00UT", "Get Help,Cancel,Rich Hdr,Del Line,Postpone,Send,Del Char,UnDel Line,To Fldrs");
  2434.     break;
  2435.       default:
  2436.     wkeyhelp("GCR0KOXD00U0", "Get Help,Cancel,Rich Hdr,Del Line,Postpone,Send,Del Char,UnDel Line");
  2437. #endif
  2438.     break;
  2439.     }
  2440. }
  2441.  
  2442.  
  2443. /*
  2444.  * packheader - packup all of the header fields for return to caller. 
  2445.  *              NOTE: all of the header info passed in, including address
  2446.  *                    of the pointer to each string is contained in the
  2447.  *                    header entry array "headents".
  2448.  */
  2449. packheader()
  2450. {
  2451.     register int    i = 0;        /* array index */
  2452.     register int    count;        /* count of chars in a field */
  2453.     register int    retval = TRUE;    /* count of chars in a field */
  2454.     register char    *bufp;        /* */
  2455.     register struct    hdr_line *line;
  2456.  
  2457.     while(i <= LASTHDR){
  2458. #ifdef    ATTACHMENTS
  2459.     /*
  2460.      * attachments are special case, already in struct we pass back
  2461.      */
  2462.     if(i == ATTCHDR){
  2463.         i++;
  2464.         continue;
  2465.     }
  2466. #endif
  2467.  
  2468.         /*
  2469.          * count chars to see if we need a new malloc'd space for our
  2470.          * array.
  2471.          */
  2472.         line = headents[i].hd_text;
  2473.         count = 0;
  2474.         while(line != NULL){
  2475.             /*
  2476.              * add one for possible concatination of a ' ' character ...
  2477.              */
  2478.             count += (strlen(line->text) + 1);
  2479.             line = line->next;
  2480.         }
  2481.         line = headents[i].hd_text;
  2482.         if(count < headents[i].maxlen){        
  2483.             *headents[i].realaddr[0] = '\0';
  2484.         }
  2485.         else{
  2486.             /*
  2487.              * don't forget to include space for the null terminator!!!!
  2488.              */
  2489.             if((bufp = (char *)malloc((count+1) * sizeof(char))) != NULL){
  2490.                 *bufp = '\0';
  2491.  
  2492.                 free(*headents[i].realaddr);
  2493.                 *headents[i].realaddr = bufp;
  2494.             }
  2495.             else{
  2496.                 emlwrite("Can't make room to pack header field.", NULL);
  2497.                 retval = FALSE;
  2498.             }
  2499.         }
  2500.  
  2501.         if(retval != FALSE){
  2502.         while(line != NULL){
  2503.                 strcat(*headents[i].realaddr, line->text);
  2504.         if(line->text[strlen(line->text)-1] == ',')
  2505.           strcat(*headents[i].realaddr, " ");
  2506.                 line = line->next;
  2507.             }
  2508.         }
  2509.  
  2510.         i++;
  2511.     }
  2512.     return(retval);    
  2513. }
  2514.  
  2515.  
  2516.  
  2517. /*
  2518.  * zotheader - free all malloc'd lines associated with the header structs
  2519.  */
  2520. zotheader()
  2521. {
  2522.     register int i;
  2523.  
  2524.     for(i=TOHDR; i <= LASTHDR; i++){
  2525.     zotentry(headents[i].hd_text);
  2526.     }
  2527. }
  2528.  
  2529.  
  2530. /*
  2531.  * zotentry - free malloc'd space associated with the given linked list
  2532.  */
  2533. zotentry(l)
  2534. register struct hdr_line *l;
  2535. {
  2536.     register struct hdr_line *ld, *lf = l;
  2537.  
  2538.     while((ld = lf) != NULL){
  2539.     lf = ld->next;
  2540.     ld->next = ld->prev = NULL;
  2541.     free((char *) ld);
  2542.     }
  2543. }
  2544.  
  2545.  
  2546.  
  2547. /*
  2548.  * zotcomma - blast any trailing commas and white space from the end 
  2549.  *          of the given line
  2550.  */
  2551. void
  2552. zotcomma(s)
  2553. char *s;
  2554. {
  2555.     register char *p;
  2556.  
  2557.     p = &s[strlen(s)];
  2558.     while(--p >= s){
  2559.     if(*p != ' '){
  2560.         if(*p == ',')
  2561.           *p = '\0';
  2562.         return;
  2563.     }
  2564.     }
  2565. }
  2566.