home *** CD-ROM | disk | FTP | other *** search
/ ftp.uv.es / 2014.11.ftp.uv.es.tar / ftp.uv.es / pub / unix / pine4.10.tar.gz / pine4.10.tar / pine4.10 / pico / composer.c < prev    next >
C/C++ Source or Header  |  1998-12-23  |  102KB  |  4,162 lines

  1. #if    !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: composer.c,v 4.166 1998/12/23 21:03:41 hubert 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.  *
  19.  * Pine and Pico are registered trademarks of the University of Washington.
  20.  * No commercial use of these trademarks may be made without prior written
  21.  * permission of the University of Washington.
  22.  * 
  23.  * Pine, Pico, and Pilot software and its included text are Copyright
  24.  * 1989-1998 by the University of Washington.
  25.  * 
  26.  * The full text of our legal notices is contained in the file called
  27.  * CPYRIGHT, included with this distribution.
  28.  *
  29.  *
  30.  * NOTES:
  31.  *
  32.  *  - composer.c is the composer for the PINE mail system
  33.  *
  34.  *  - tabled 01/19/90
  35.  *
  36.  *  Notes: These routines aren't incorporated yet, because the composer as
  37.  *         a whole still needs development.  These are ideas that should
  38.  *         be implemented in later releases of PINE.  See the notes in 
  39.  *         pico.c concerning what needs to be done ....
  40.  *
  41.  *  - untabled 01/30/90
  42.  *
  43.  *  Notes: Installed header line editing, with wrapping and unwrapping, 
  44.  *         context sensitive help, and other mail header editing features.
  45.  *
  46.  *  - finalish code cleanup 07/15/91
  47.  * 
  48.  *  Notes: Killed/yanked header lines use emacs kill buffer.
  49.  *         Arbitrarily large headers are handled gracefully.
  50.  *         All formatting handled by FormatLines.
  51.  *
  52.  *  - Work done to optimize display painting 06/26/92
  53.  *         Not as clean as it should be, needs more thought 
  54.  *
  55.  */
  56. #include "headers.h"
  57.  
  58.  
  59. int              InitEntryText PROTO((char *, struct headerentry *));
  60. int              HeaderOffset PROTO((int));
  61. int              HeaderFocus PROTO((int, int));
  62. int              LineEdit PROTO((int));
  63. int              header_downline PROTO((int, int));
  64. int              header_upline PROTO((int));
  65. int              FormatLines PROTO((struct hdr_line *, char *, int, int, int));
  66. char            *strqchr PROTO((char *, int, int *, int));
  67. int              PaintBody PROTO((int));
  68. int              ComposerHelp PROTO((int));
  69. int              NewTop PROTO((int));
  70. void             display_delimiter PROTO((int));
  71. int              InvertPrompt PROTO((int, int));
  72. int              partial_entries PROTO((void));
  73. int              physical_line PROTO((struct hdr_line *));
  74. int              strend PROTO((char *, int));
  75. int              KillHeaderLine PROTO((struct hdr_line *, int));
  76. int              SaveHeaderLines PROTO((void));
  77. char            *break_point PROTO((char *, int, int, int *));
  78. int              hldelete PROTO((struct hdr_line *));
  79. int              is_blank PROTO((int, int, int));
  80. int              zotentry PROTO((struct hdr_line *));
  81. int              zotcomma PROTO((char *));
  82. struct hdr_line *first_hline PROTO((int *));
  83. struct hdr_line *first_sel_hline PROTO((int *));
  84. struct hdr_line *next_hline PROTO((int *, struct hdr_line *));
  85. struct hdr_line *next_sel_hline PROTO((int *, struct hdr_line *));
  86. struct hdr_line *prev_hline PROTO((int *, struct hdr_line *));
  87. struct hdr_line *prev_sel_hline PROTO((int *, struct hdr_line *));
  88. struct hdr_line *first_requested_hline PROTO((int *));
  89. void             fix_mangle_and_err PROTO((int *, char **, char *));
  90.  
  91.  
  92. /*
  93.  * definition header field array, structures defined in pico.h
  94.  */
  95. struct headerentry *headents;
  96.  
  97.  
  98. /*
  99.  * structure that keeps track of the range of header lines that are
  100.  * to be displayed and other fun stuff about the header
  101.  */
  102. struct on_display ods;                /* global on_display struct */
  103.  
  104.  
  105. /*
  106.  * useful macros
  107.  */
  108. #define    HALLOC()    (struct hdr_line *)malloc(sizeof(struct hdr_line))
  109. #define    LINELEN()    (term.t_ncol - headents[ods.cur_e].prlen)
  110. #define    BOTTOM()    (term.t_nrow - term.t_mrow)
  111. #define    FULL_SCR()    (BOTTOM() - 3)
  112. #define    HALF_SCR()    (FULL_SCR()/2)
  113.  
  114. #ifdef    MOUSE
  115. /*
  116.  * Redefine HeaderEditor to install wrapper required for mouse even
  117.  * handling...
  118.  */
  119. #define    HeaderEditor    HeaderEditorWork
  120. #endif /* MOUSE */
  121.  
  122. #if    (defined(DOS) && !defined(_WINDOWS)) || defined(OS2)
  123. #define    HDR_DELIM    "\xCD\xCD\xCD\xCD\xCD Message Text \xCD\xCD\xCD\xCD\xCD"
  124. #else
  125. #define    HDR_DELIM    "----- Message Text -----"
  126. #endif
  127.  
  128. /*
  129.  * useful declarations
  130.  */
  131. static short delim_ps  = 0;        /* previous state */
  132. static short invert_ps = 0;        /* previous state */
  133.  
  134.  
  135. static KEYMENU menu_header[] = {
  136.     {"^G", "Get Help", KS_SCREENHELP},    {"^X", "Send", KS_SEND},
  137.     {"^R", "Rich Hdr", KS_RICHHDR},    {"^Y", "PrvPg/Top", KS_PREVPAGE},
  138.     {"^K", "Cut Line", KS_CURPOSITION},    {"^O", "Postpone", KS_POSTPONE},
  139.     {"^C", "Cancel", KS_CANCEL},    {"^D", "Del Char", KS_NONE},
  140.     {"^J", "Attach", KS_ATTACH},    {"^V", "NxtPg/End", KS_NEXTPAGE},
  141.     {"^U", "UnDel Line", KS_NONE},    {NULL, NULL}
  142. };
  143. #define    SEND_KEY    1
  144. #define    RICH_KEY    2
  145. #define    CUT_KEY        4
  146. #define    PONE_KEY    5
  147. #define    DEL_KEY        7
  148. #define    ATT_KEY        8
  149. #define    UDEL_KEY    10
  150. #define    TO_KEY        11
  151.  
  152.  
  153. /*
  154.  * function key mappings for header editor
  155.  */
  156. static int ckm[12][2] = {
  157.     { F1,  (CTRL|'G')},
  158.     { F2,  (CTRL|'C')},
  159.     { F3,  (CTRL|'X')},
  160.     { F4,  (CTRL|'D')},
  161.     { F5,  (CTRL|'R')},
  162.     { F6,  (CTRL|'J')},
  163.     { F7,  0 },
  164.     { F8,  0 },
  165.     { F9,  (CTRL|'K')},
  166.     { F10, (CTRL|'U')},
  167.     { F11, (CTRL|'O')},
  168.     { F12, (CTRL|'T')}
  169. };
  170.  
  171.  
  172. /*
  173.  * InitMailHeader - initialize header array, and set beginning editor row 
  174.  *                  range.  The header entry structure should look just like 
  175.  *                  what is written on the screen, the vector 
  176.  *                  (entry, line, offset) will describe the current cursor 
  177.  *                  position in the header.
  178.  *
  179.  *       Returns: TRUE if special header handling was requested,
  180.  *            FALSE under standard default behavior.
  181.  */
  182. InitMailHeader(mp)
  183. PICO  *mp;
  184. {
  185.     char           *addrbuf;
  186.     struct headerentry *he;
  187.     int            rv;
  188.  
  189.     if(!mp->headents){
  190.     headents = NULL;
  191.     return(FALSE);
  192.     }
  193.  
  194.     /*
  195.      * initialize some of on_display structure, others below...
  196.      */
  197.     ods.p_off  = 0;
  198.     ods.p_line = COMPOSER_TOP_LINE;
  199.     ods.top_l = ods.cur_l = NULL;
  200.  
  201.     headents = mp->headents;
  202.     /*--- initialize the fields in the headerent structure ----*/
  203.     for(he = headents; he->name != NULL; he++){
  204.     he->hd_text    = NULL;
  205.     he->display_it = he->display_it ? he->display_it : !he->rich_header;
  206.         if(he->is_attach) {
  207.             /*--- A lot of work to do since attachments are special ---*/
  208.             he->maxlen = 0;
  209.         if(mp->attachments != NULL){
  210.         char   buf[NLINE];
  211.                 int    attno = 0;
  212.         int    l1, ofp, ofp1, ofp2;  /* OverFlowProtection */
  213.         size_t addrbuflen = 4 * NLINE; /* malloc()ed size of addrbuf */
  214.                 PATMT *ap = mp->attachments;
  215.  
  216.         ofp = NLINE - 35;
  217.  
  218.                 addrbuf = (char *)malloc(addrbuflen);
  219.                 addrbuf[0] = '\0';
  220.                 buf[0] = '\0';
  221.                 while(ap){
  222.           if((l1 = strlen(ap->filename)) <= ofp){
  223.               ofp1 = l1;
  224.               ofp2 = ofp - l1;
  225.           }
  226.           else{
  227.               ofp1 = ofp;
  228.               ofp2 = 0;
  229.           }
  230.  
  231.           if(ap->filename){
  232.                      sprintf(buf, "%d. %.*s%s %s%s%s\"",
  233.                              ++attno,
  234.                  ofp1,
  235.                              ap->filename,
  236.                  (l1 > ofp) ? "...]" : "",
  237.                              ap->size ? "(" : "",
  238.                              ap->size ? ap->size : "",
  239.                              ap->size ? ") " : "");
  240.  
  241.              /* append description, escaping quotes */
  242.              if(ap->description){
  243.              char *dp = ap->description, *bufp = &buf[strlen(buf)];
  244.              int   escaped = 0;
  245.  
  246.              do
  247.                if(*dp == '\"' && !escaped){
  248.                    *bufp++ = '\\';
  249.                    ofp2--;
  250.                }
  251.                else if(escaped){
  252.                    *bufp++ = '\\';
  253.                    escaped = 0;
  254.                }
  255.              while(--ofp2 > 0 && (*bufp++ = *dp++));
  256.  
  257.              *bufp = '\0';
  258.              }
  259.  
  260.              sprintf(buf + strlen(buf), "\"%s", ap->next ? "," : "");
  261.  
  262.              if(strlen(addrbuf) + strlen(buf) >= addrbuflen){
  263.              addrbuflen += NLINE * 4;
  264.              if(!(addrbuf = (char *)realloc(addrbuf, addrbuflen))){
  265.                  emlwrite("\007Can't realloc addrbuf to %d bytes",
  266.                       (void *) addrbuflen);
  267.                  return(ABORT);
  268.              }
  269.              }
  270.  
  271.                      strcat(addrbuf, buf);
  272.                  }
  273.                  ap = ap->next;
  274.                 }
  275.                 InitEntryText(addrbuf, he);
  276.                 free((char *)addrbuf);
  277.             } else {
  278.                 InitEntryText("", he);
  279.             }
  280.             he->realaddr = NULL;
  281.         } else {
  282.         if(!he->blank)
  283.               addrbuf = *(he->realaddr);
  284.         else
  285.               addrbuf = "";
  286.  
  287.             InitEntryText(addrbuf, he);
  288.     }
  289.     }
  290.  
  291.     /*
  292.      * finish initialization and then figure out display layout.
  293.      * first, look for any field the caller requested we start in,
  294.      * and set the offset into that field.
  295.      */
  296.     if(ods.cur_l = first_requested_hline(&ods.cur_e)){
  297.     ods.top_e = 0;                /* init top_e */
  298.     ods.top_l = first_hline(&ods.top_e);
  299.     if(!HeaderFocus(ods.cur_e, Pmaster ? Pmaster->edit_offset : 0))
  300.       HeaderFocus(ods.cur_e, 0);
  301.  
  302.     rv = TRUE;
  303.     }
  304.     else{
  305.     ods.top_l = first_hline(&ods.cur_e);
  306.     ods.cur_l = first_sel_hline(&ods.cur_e);
  307.     ods.top_e = ods.cur_e;
  308.     rv = 0;
  309.     }
  310.  
  311.     UpdateHeader(0);
  312.     return(rv);
  313. }
  314.  
  315.  
  316.  
  317. /*
  318.  * InitEntryText - Add the given header text into the header entry 
  319.  *           line structure.
  320.  */
  321. InitEntryText(address, e)
  322. char    *address;
  323. struct headerentry *e;
  324. {
  325.     struct  hdr_line    *curline;
  326.     register  int    longest;
  327.  
  328.     /*
  329.      * get first chunk of memory, and tie it to structure...
  330.      */
  331.     if((curline = HALLOC()) == NULL){
  332.         emlwrite("Unable to make room for full Header.", NULL);
  333.         return(FALSE);
  334.     }
  335.     longest = term.t_ncol - e->prlen - 1;
  336.     curline->text[0] = '\0';
  337.     curline->next = NULL;
  338.     curline->prev = NULL;
  339.     e->hd_text = curline;        /* tie it into the list */
  340.  
  341.     if(FormatLines(curline, address, longest, e->break_on_comma, 0) == -1)
  342.       return(FALSE);
  343.     else
  344.       return(TRUE);
  345. }
  346.  
  347.  
  348.  
  349. /*
  350.  *  ResizeHeader - Handle resizing display when SIGWINCH received.
  351.  *
  352.  *    notes:
  353.  *        works OK, but needs thorough testing
  354.  *          
  355.  */
  356. ResizeHeader()
  357. {
  358.     register struct headerentry *i;
  359.     register int offset;
  360.  
  361.     if(!headents)
  362.       return(TRUE);
  363.  
  364.     offset = (ComposerEditing) ? HeaderOffset(ods.cur_e) : 0;
  365.  
  366.     for(i=headents; i->name; i++){        /* format each entry */
  367.     if(FormatLines(i->hd_text, "", (term.t_ncol - i->prlen),
  368.                i->break_on_comma, 0) == -1){
  369.         return(-1);
  370.     }
  371.     }
  372.  
  373.     if(ComposerEditing)                /* restart at the top */
  374.       HeaderFocus(ods.cur_e, offset);        /* fix cur_l and p_off */
  375.     else {
  376.       struct hdr_line *l;
  377.       int              e;
  378.  
  379.       for(i = headents; i->name != NULL; i++);    /* Find last line */
  380.       i--;
  381.       e = i - headents;
  382.       l = headents[e].hd_text;
  383.       if(!headents[e].display_it || headents[e].blank)
  384.         l = prev_sel_hline(&e, l);        /* last selectable line */
  385.  
  386.       if(!l){
  387.       e = i - headents;
  388.       l = headents[e].hd_text;
  389.       }
  390.  
  391.       HeaderFocus(e, -1);        /* put focus on last line */
  392.     }
  393.  
  394.     if(ComposerTopLine != COMPOSER_TOP_LINE)
  395.       UpdateHeader(0);
  396.  
  397.     PaintBody(0);
  398.  
  399.     if(ComposerEditing)
  400.       movecursor(ods.p_line, ods.p_off+headents[ods.cur_e].prlen);
  401.  
  402.     (*term.t_flush)();
  403.     return(TRUE);
  404. }
  405.  
  406.  
  407.  
  408. /*
  409.  * HeaderOffset - return the character offset into the given header
  410.  */
  411. HeaderOffset(h)
  412. int    h;
  413. {
  414.     register struct hdr_line *l;
  415.     int         i = 0;
  416.  
  417.     l = headents[h].hd_text;
  418.  
  419.     while(l != ods.cur_l){
  420.     i += strlen(l->text);
  421.     l = l->next;
  422.     }
  423.     return(i+ods.p_off);
  424. }
  425.  
  426.  
  427.  
  428. /*
  429.  * HeaderFocus - put the dot at the given offset into the given header
  430.  */
  431. HeaderFocus(h, offset)
  432. int    h, offset;
  433. {
  434.     register struct hdr_line *l;
  435.     register int    i;
  436.     int         last = 0;
  437.  
  438.     if(offset == -1)                /* focus on last line */
  439.       last = 1;
  440.  
  441.     l = headents[h].hd_text;
  442.     while(1){
  443.     if(last && l->next == NULL){
  444.         break;
  445.     }
  446.     else{
  447.         if((i=strlen(l->text)) >= offset)
  448.           break;
  449.         else
  450.           offset -= i;
  451.     }
  452.     if((l = l->next) == NULL)
  453.       return(FALSE);
  454.     }
  455.  
  456.     ods.cur_l = l;
  457.     ods.p_len = strlen(l->text);
  458.     ods.p_off = (last) ? 0 : offset;
  459.  
  460.     return(TRUE);
  461. }
  462.  
  463.  
  464.  
  465. /*
  466.  * HeaderEditor() - edit the mail header field by field, trapping 
  467.  *                  important key sequences, hand the hard work off
  468.  *                  to LineEdit().  
  469.  *    returns:
  470.  *              -3    if we drop out bottom *and* want to process mouse event
  471.  *        -1    if we drop out the bottom 
  472.  *        FALSE if editing is cancelled
  473.  *        TRUE  if editing is finished
  474.  */
  475. HeaderEditor(f, n)
  476. int f, n;
  477. {
  478.     register  int    i;
  479.     register  int    ch;
  480.     register  int    status;            /* return status of something*/
  481.     register  char    *bufp;
  482.     struct headerentry *h;
  483.     int                 cur_e, count, mangled, retval = -1,
  484.                 hdr_only = (gmode & MDHDRONLY) ? 1 : 0;
  485.     char               *errmss, *err;
  486. #ifdef MOUSE
  487.     MOUSEPRESS        mp;
  488. #endif
  489.  
  490.     if(!headents)
  491.       return(TRUE);                /* nothing to edit! */
  492.  
  493.     ComposerEditing = TRUE;
  494.     display_delimiter(0);            /* provide feedback */
  495.  
  496. #ifdef    _WINDOWS
  497.     mswin_setscrollrange (0, 0);
  498. #endif /* _WINDOWS */
  499.  
  500.     /* 
  501.      * Decide where to begin editing.  if f == TRUE begin editing
  502.      * at the bottom.  this case results from the cursor backing
  503.      * into the editor from the bottom.  otherwise, the user explicitly
  504.      * requested editing the header, and we begin at the top.
  505.      * 
  506.      * further, if f == 1, we moved into the header by hitting the up arrow
  507.      * in the message text, else if f == 2, we moved into the header by
  508.      * moving past the left edge of the top line in the message.  so, make 
  509.      * the end of the last line of the last entry the current cursor position
  510.      * lastly, if f == 3, we moved into the header by hitting backpage() on
  511.      * the top line of the message, so scroll a page back.  
  512.      */
  513.     if(f){
  514.     if(f == 2){                /* 2 leaves cursor at end  */
  515.         struct hdr_line *l = ods.cur_l;
  516.         int              e = ods.cur_e;
  517.  
  518.         /*--- make sure on last field ---*/
  519.         while(l = next_sel_hline(&e, l))
  520.           if(headents[ods.cur_e].display_it){
  521.           ods.cur_l = l;
  522.           ods.cur_e = e;
  523.           }
  524.  
  525.         ods.p_off = 1000;            /* and make sure at EOL    */
  526.     }
  527.     else{
  528.         /*
  529.          * note: assumes that ods.cur_e and ods.cur_l haven't changed
  530.          *       since we left...
  531.          */
  532.  
  533.         /* fix postition */
  534.         if(curwp->w_doto < headents[ods.cur_e].prlen)
  535.           ods.p_off = 0;
  536.         else if(curwp->w_doto < ods.p_off + headents[ods.cur_e].prlen)
  537.           ods.p_off = curwp->w_doto - headents[ods.cur_e].prlen;
  538.         else
  539.           ods.p_off = 1000;
  540.  
  541.         /* and scroll back if needed */
  542.         if(f == 3)
  543.           for(i = 0; header_upline(0) && i <= FULL_SCR(); i++)
  544.         ;
  545.     }
  546.  
  547.     ods.p_line = ComposerTopLine - 2;
  548.     }
  549.     /* else just trust what ods contains */
  550.  
  551.     InvertPrompt(ods.cur_e, TRUE);        /* highlight header field */
  552.     sgarbk = 1;
  553.  
  554.     do{
  555.     if(km_popped){
  556.         km_popped--;
  557.         if(km_popped == 0)
  558.           sgarbk = 1;
  559.     }
  560.  
  561.     if(sgarbk){
  562.         if(km_popped){  /* temporarily change to cause menu to paint */
  563.         term.t_mrow = 2;
  564.         curwp->w_ntrows -= 2;
  565.         movecursor(term.t_nrow-2, 0); /* clear status line, too */
  566.         peeol();
  567.         }
  568.         else if(term.t_mrow == 0)
  569.           PaintBody(1);
  570.  
  571.         ShowPrompt();            /* display correct options */
  572.         sgarbk = 0;
  573.         if(km_popped){
  574.         term.t_mrow = 0;
  575.         curwp->w_ntrows += 2;
  576.         }
  577.     }
  578.  
  579.     ch = LineEdit(!(gmode&MDVIEW));        /* work on the current line */
  580.  
  581.     if(km_popped)
  582.       switch(ch){
  583.         case NODATA:
  584.         case (CTRL|'L'):
  585.           km_popped++;
  586.           break;
  587.         
  588.         default:
  589.           movecursor(term.t_nrow-2, 0);
  590.           peeol();
  591.           movecursor(term.t_nrow-1, 0);
  592.           peeol();
  593.           movecursor(term.t_nrow, 0);
  594.           peeol();
  595.           break;
  596.       }
  597.  
  598.         switch (ch){
  599.       case (CTRL|'R') :            /* Toggle header display */
  600.         if(Pmaster->pine_flags & MDHDRONLY){
  601.         if(Pmaster->expander){
  602.             packheader();
  603.             call_expander();
  604.             PaintBody(0);
  605.             break;
  606.         }
  607.         else
  608.           goto bleep;
  609.         }
  610.  
  611.             /*---- Are there any headers to expand above us? ---*/
  612.             for(h = headents; h != &headents[ods.cur_e]; h++)
  613.               if(h->rich_header)
  614.                 break;
  615.             if(h->rich_header)
  616.           InvertPrompt(ods.cur_e, FALSE);    /* Yes, don't leave inverted */
  617.  
  618.         mangled = 0;
  619.         err = NULL;
  620.         if(partial_entries()){
  621.                 /*--- Just turned off all rich headers --*/
  622.         if(headents[ods.cur_e].rich_header){
  623.                     /*-- current header got turned off too --*/
  624.             if(headents[ods.cur_e].builder)    /* verify text */
  625.               i = call_builder(&headents[ods.cur_e], &mangled, &err)>0;
  626.                     /* Check below */
  627.                     for(cur_e =ods.cur_e; headents[cur_e].name!=NULL; cur_e++)
  628.                       if(!headents[cur_e].rich_header)
  629.                         break;
  630.                     if(headents[cur_e].name == NULL) {
  631.                         /* didn't find one, check above */
  632.                         for(cur_e =ods.cur_e; headents[cur_e].name!=NULL;
  633.                             cur_e--)
  634.                           if(!headents[cur_e].rich_header)
  635.                             break;
  636.  
  637.                     }
  638.             ods.p_off = 0;
  639.             ods.cur_e = cur_e;
  640.             ods.cur_l = headents[ods.cur_e].hd_text;
  641.         }
  642.         }
  643.  
  644.         ods.p_line = 0;            /* force update */
  645.         UpdateHeader(0);
  646.         PaintHeader(COMPOSER_TOP_LINE, FALSE);
  647.         PaintBody(1);
  648.         fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
  649.         break;
  650.  
  651.       case (CTRL|'C') :            /* bag whole thing ?*/
  652.         if(abort_composer(1, 0) == TRUE)
  653.           return(FALSE);
  654.  
  655.         break;
  656.  
  657.       case (CTRL|'X') :            /* Done. Send it. */
  658.         i = 0;
  659. #ifdef    ATTACHMENTS
  660.         if(headents[ods.cur_e].is_attach){
  661.         /* verify the attachments, and pretty things up in case
  662.          * we come back to the composer due to error...
  663.          */
  664.         if((i = SyncAttach()) != 0){
  665.             sleep(2);        /* give time for error to absorb */
  666.             FormatLines(headents[ods.cur_e].hd_text, "",
  667.                 term.t_ncol - headents[ods.cur_e].prlen,
  668.                 headents[ods.cur_e].break_on_comma, 0);
  669.         }
  670.         }
  671.         else
  672. #endif
  673.         mangled = 0;
  674.         err = NULL;
  675.         if(headents[ods.cur_e].builder)    /* verify text? */
  676.           i = call_builder(&headents[ods.cur_e], &mangled, &err);
  677.  
  678.         if(i < 0){            /* don't leave without a valid addr */
  679.         fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
  680.         break;
  681.         }
  682.         else if(i > 0){
  683.         ods.cur_l = headents[ods.cur_e].hd_text; /* attach cur_l */
  684.         ods.p_off = 0;
  685.         ods.p_line = 0;            /* force realignment */
  686.             fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
  687.         NewTop(0);
  688.         }
  689.  
  690.         fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
  691.  
  692.         if(wquit(1,0) == TRUE)
  693.           return(TRUE);
  694.  
  695.         if(i > 0){
  696.         /*
  697.          * need to be careful here because pointers might be messed up.
  698.          * also, could do a better job of finding the right place to
  699.          * put the dot back (i.e., the addr/list that was expanded).
  700.          */
  701.         UpdateHeader(0);
  702.         PaintHeader(COMPOSER_TOP_LINE, FALSE);
  703.         PaintBody(1);
  704.         }
  705.         break;
  706.  
  707.       case (CTRL|'Z') :            /* Suspend compose */
  708.         if(gmode&MDSSPD){            /* is it allowed? */
  709.         bktoshell();
  710.         PaintBody(0);
  711.         }
  712.         else{
  713.         (*term.t_beep)();
  714.         emlwrite("Unknown Command: ^Z", NULL);
  715.         }
  716.         break;
  717.  
  718.       case (CTRL|'O') :            /* Suspend message */
  719.         if(Pmaster->pine_flags & MDHDRONLY)
  720.           goto bleep;
  721.  
  722.         i = 0;
  723.         mangled = 0;
  724.         err = NULL;
  725.         if(headents[ods.cur_e].is_attach){
  726.         if(SyncAttach() < 0){
  727.             if(mlyesno("Problem with attachments. Postpone anyway?",
  728.                    FALSE) != TRUE){
  729.             if(FormatLines(headents[ods.cur_e].hd_text, "",
  730.                        term.t_ncol - headents[ods.cur_e].prlen,
  731.                        headents[ods.cur_e].break_on_comma, 0) == -1)
  732.               emlwrite("\007Format lines failed!", NULL);
  733.             UpdateHeader(0);
  734.             PaintHeader(COMPOSER_TOP_LINE, FALSE);
  735.             PaintBody(1);
  736.             continue;
  737.             }
  738.         }
  739.         }
  740.         else if(headents[ods.cur_e].builder)
  741.           i = call_builder(&headents[ods.cur_e], &mangled, &err);
  742.  
  743.         ods.cur_l = headents[ods.cur_e].hd_text;
  744.         fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
  745.  
  746.         if(i < 0)            /* don't leave without a valid addr */
  747.           break;
  748.  
  749.         suspend_composer(1, 0);
  750.         return(TRUE);
  751.  
  752. #ifdef    ATTACHMENTS
  753.       case (CTRL|'J') :            /* handle attachments */
  754.         if(Pmaster->pine_flags & MDHDRONLY)
  755.           goto bleep;
  756.  
  757.         { char fn[NLINE], sz[32], cmt[NLINE];
  758.           int saved_km_popped;
  759.  
  760.           /*
  761.            * Attachment questions mess with km_popped and assume
  762.            * it is zero to start with.  If we don't set it to zero
  763.            * on entry, the message about mime type will be erased
  764.            * by PaintBody.  If we don't reset it when we come back,
  765.            * the bottom three lines may be messed up.
  766.            */
  767.           saved_km_popped = km_popped;
  768.           km_popped = 0;
  769.  
  770.           if(AskAttach(fn, sz, cmt)){
  771.           status = !AppendAttachment(fn, sz, cmt);
  772.           }
  773.  
  774.           km_popped = saved_km_popped;
  775.           sgarbk = 1;            /* clean up prompt */
  776.         }
  777.         break;
  778. #endif
  779.  
  780.       case (CTRL|'I') :            /* tab */
  781.         ods.p_off = 0;            /* fall through... */
  782.  
  783.       case (CTRL|'N') :
  784.       case KEY_DOWN :
  785.         header_downline(!hdr_only, hdr_only);
  786.         break;
  787.  
  788.       case (CTRL|'P') :
  789.       case KEY_UP :
  790.         header_upline(1);
  791.         break;
  792.  
  793.       case (CTRL|'V') :            /* down a page */
  794.       case KEY_PGDN :
  795.         cur_e = ods.cur_e;
  796.         if(!next_sel_hline(&cur_e, ods.cur_l)){
  797.         header_downline(!hdr_only, hdr_only);
  798.         if(!(gmode & MDHDRONLY))
  799.           retval = -1;            /* tell caller we fell out */
  800.         }
  801.         else{
  802.         int move_down, bot_pline;
  803.         struct hdr_line *new_cur_l, *line, *next_line, *prev_line;
  804.  
  805.         move_down = BOTTOM() - 2 - ods.p_line;
  806.         if(move_down < 0)
  807.           move_down = 0;
  808.  
  809.         /*
  810.          * Count down move_down lines to find the pointer to the line
  811.          * that we want to become the current line.
  812.          */
  813.         new_cur_l = ods.cur_l;
  814.         cur_e = ods.cur_e;
  815.         for(i = 0; i < move_down; i++){
  816.             next_line = next_hline(&cur_e, new_cur_l);
  817.             if(!next_line)
  818.               break;
  819.  
  820.             new_cur_l = next_line;
  821.         }
  822.  
  823.         if(headents[cur_e].blank){
  824.             next_line = next_sel_hline(&cur_e, new_cur_l);
  825.             if(!next_line)
  826.               break;
  827.  
  828.             new_cur_l = next_line;
  829.         }
  830.  
  831.         /*
  832.          * Now call header_downline until we get down to the
  833.          * new current line, so that the builders all get called.
  834.          * New_cur_l will remain valid since we won't be calling
  835.          * a builder for it during this loop.
  836.          */
  837.         while(ods.cur_l != new_cur_l && header_downline(0, 0))
  838.           ;
  839.         
  840.         /*
  841.          * Count back up, if we're at the bottom, to find the new
  842.          * top line.
  843.          */
  844.         cur_e = ods.cur_e;
  845.         if(next_hline(&cur_e, ods.cur_l) == NULL){
  846.             /*
  847.              * Cursor stops at bottom of headers, which is where
  848.              * we are right now.  Put as much of headers on
  849.              * screen as will fit.  Count up to figure
  850.              * out which line is top_l and which p_line cursor is on.
  851.              */
  852.             cur_e = ods.cur_e;
  853.             line = ods.cur_l;
  854.             /* leave delimiter on screen, too */
  855.             bot_pline = BOTTOM() - 1 - ((gmode & MDHDRONLY) ? 0 : 1);
  856.             for(i = COMPOSER_TOP_LINE; i < bot_pline; i++){
  857.             prev_line = prev_hline(&cur_e, line);
  858.             if(!prev_line)
  859.               break;
  860.             
  861.             line = prev_line;
  862.             }
  863.  
  864.             ods.top_l = line;
  865.             ods.top_e = cur_e;
  866.             ods.p_line = i;
  867.               
  868.         }
  869.         else{
  870.             ods.top_l = ods.cur_l;
  871.             ods.top_e = ods.cur_e;
  872.             /*
  873.              * We don't want to scroll down further than the
  874.              * delimiter, so check to see if that is the case.
  875.              * If it is, we move the p_line down the screen
  876.              * until the bottom line is where we want it.
  877.              */
  878.             bot_pline = BOTTOM() - 1 - ((gmode & MDHDRONLY) ? 0 : 1);
  879.             cur_e = ods.cur_e;
  880.             line = ods.cur_l;
  881.             for(i = bot_pline; i > COMPOSER_TOP_LINE; i--){
  882.             next_line = next_hline(&cur_e, line);
  883.             if(!next_line)
  884.               break;
  885.  
  886.             line = next_line;
  887.             }
  888.  
  889.             /*
  890.              * i is the desired value of p_line.
  891.              * If it is greater than COMPOSER_TOP_LINE, then
  892.              * we need to adjust top_l.
  893.              */
  894.             ods.p_line = i;
  895.             line = ods.top_l;
  896.             cur_e = ods.top_e;
  897.             for(; i > COMPOSER_TOP_LINE; i--){
  898.             prev_line = prev_hline(&cur_e, line);
  899.             if(!prev_line)
  900.               break;
  901.             
  902.             line = prev_line;
  903.             }
  904.  
  905.             ods.top_l = line;
  906.             ods.top_e = cur_e;
  907.  
  908.             /*
  909.              * Special case.  If p_line is within one of the bottom,
  910.              * move it to the bottom.
  911.              */
  912.             if(ods.p_line == bot_pline - 1){
  913.             header_downline(0, 0);
  914.             /* but put these back where we want them */
  915.             ods.p_line = bot_pline;
  916.             ods.top_l = line;
  917.             ods.top_e = cur_e;
  918.             }
  919.         }
  920.  
  921.         UpdateHeader(0);
  922.         PaintHeader(COMPOSER_TOP_LINE, FALSE);
  923.         PaintBody(1);
  924.         }
  925.  
  926.         break;
  927.  
  928.       case (CTRL|'Y') :            /* up a page */
  929.       case KEY_PGUP :
  930.         for(i = 0; header_upline(0) && i <= FULL_SCR(); i++)
  931.           if(i < 0)
  932.         break;
  933.  
  934.         break;
  935.  
  936. #ifdef    MOUSE
  937.       case KEY_MOUSE:
  938.         mouse_get_last (NULL, &mp);
  939.         switch(mp.button){
  940.           case M_BUTTON_LEFT :
  941.         if (!mp.doubleclick) {
  942.             if (mp.row < ods.p_line) {
  943.             for (i = ods.p_line - mp.row;
  944.                  i > 0 && header_upline(0); 
  945.                  --i)
  946.               ;
  947.             }
  948.             else {
  949.             for (i = mp.row-ods.p_line;
  950.                  i > 0 && header_downline(!hdr_only, 0);
  951.                  --i)
  952.               ;
  953.             }
  954.  
  955.             if((ods.p_off = mp.col - headents[ods.cur_e].prlen) <= 0)
  956.               ods.p_off = 0;
  957.  
  958.             /* -3 is returned  if we drop out bottom
  959.              * *and* want to process a mousepress.  The Headereditor
  960.              * wrapper should make sense of this return code.
  961.              */
  962.             if (ods.p_line >= ComposerTopLine)
  963.               retval = -3;
  964.         }
  965.  
  966.         break;
  967.  
  968.           case M_BUTTON_RIGHT :
  969. #ifdef    _WINDOWS
  970.         pico_popup();
  971. #endif
  972.           case M_BUTTON_MIDDLE :
  973.           default :    /* NOOP */
  974.         break;
  975.         }
  976.  
  977.         break;
  978. #endif /* MOUSE */
  979.  
  980.       case (CTRL|'T') :            /* Call field selector */
  981.         errmss = NULL;
  982.             if(headents[ods.cur_e].is_attach) {
  983.                 /*--- selector for attachments ----*/
  984.         char dir[NLINE], fn[NLINE], sz[NLINE];
  985.  
  986.         strcpy(dir, (gmode & MDCURDIR)
  987.                   ? "."
  988.                   : ((gmode & MDTREE) || opertree[0])
  989.                    ? opertree : gethomedir(NULL));
  990.         fn[0] = '\0';
  991.         if(FileBrowse(dir, NLINE, fn, NLINE, sz, FB_READ) == 1){ /* got a new file */
  992.             char buf[NLINE];
  993.  
  994.             sprintf(buf, "%s%c%s", dir, C_FILESEP, fn);
  995.             (void) QuoteAttach(buf);
  996.             sprintf(buf + strlen(buf), " (%s) \"\"%s", sz,
  997.                 (!headents[ods.cur_e].hd_text->text[0]) ? "":",");
  998.             if(FormatLines(headents[ods.cur_e].hd_text, buf,
  999.                    term.t_ncol - headents[ods.cur_e].prlen,
  1000.                    headents[ods.cur_e].break_on_comma,0)==-1){
  1001.             emlwrite("\007Format lines failed!", NULL);
  1002.             }
  1003.  
  1004.             UpdateHeader(0);
  1005.         }                /* else, nothing of interest */
  1006.             } else if (headents[ods.cur_e].selector != NULL) {
  1007.         VARS_TO_SAVE *saved_state;
  1008.  
  1009.                 /*---- General selector for non-attachments -----*/
  1010.  
  1011.         /*
  1012.          * Since the selector may make a new call back to pico()
  1013.          * we need to save and restore the pico state here.
  1014.          */
  1015.         if((saved_state = save_pico_state()) != NULL){
  1016.             bufp = (*(headents[ods.cur_e].selector))(&errmss);
  1017.             restore_pico_state(saved_state);
  1018.             free_pico_state(saved_state);
  1019.             ttresize();            /* fixup screen bufs */
  1020.             picosigs();            /* restore altered signals */
  1021.         }
  1022.         else{
  1023.             char *s = "Not enough memory";
  1024.  
  1025.             errmss = (char *)malloc((strlen(s)+1) * sizeof(char));
  1026.             strcpy(errmss, s);
  1027.             bufp = NULL;
  1028.         }
  1029.  
  1030.                 if(bufp != NULL) {
  1031.             mangled = 0;
  1032.             err = NULL;
  1033.                     if(headents[ods.cur_e].break_on_comma) {
  1034.                         /*--- Must be an address ---*/
  1035.                         if(ods.cur_l->text[0] != '\0'){
  1036.                 struct hdr_line *h, *hh;
  1037.                 int             q = 0;
  1038.                 
  1039.                 /*--- Check for continuation of previous line ---*/
  1040.                 for(hh = h = headents[ods.cur_e].hd_text; 
  1041.                 h && h->next != ods.cur_l; h = h->next){
  1042.                 if(strqchr(h->text, ',', &q, -1)){
  1043.                     hh = h->next;
  1044.                 }
  1045.                 }
  1046.                 if(hh && hh != ods.cur_l){
  1047.                 /*--- Looks like a continuation ---*/
  1048.                 ods.cur_l = hh;
  1049.                 ods.p_len = strlen(hh->text);
  1050.                 }
  1051.                 for(i = ++ods.p_len; i; i--)
  1052.                   ods.cur_l->text[i] = ods.cur_l->text[i-1];
  1053.  
  1054.                 ods.cur_l->text[0] = ',';
  1055.             }
  1056.  
  1057.                         if(FormatLines(ods.cur_l, bufp,
  1058.                       (term.t_ncol-headents[ods.cur_e].prlen), 
  1059.                                       headents[ods.cur_e].break_on_comma, 0) == -1){
  1060.                             emlwrite("Problem adding address to header !",
  1061.                                      NULL);
  1062.                             (*term.t_beep)();
  1063.                             break;
  1064.                         }
  1065.  
  1066.             /*
  1067.              * If the "selector" has a "builder" as well, pass
  1068.              * what was just selected thru the builder...
  1069.              */
  1070.             if(headents[ods.cur_e].builder){
  1071.                 struct hdr_line *l;
  1072.                 int             cur_row, top_too = 0;
  1073.  
  1074.                 for(l = headents[ods.cur_e].hd_text, cur_row = 0;
  1075.                 l && l != ods.cur_l;
  1076.                 l = l->next, cur_row++)
  1077.                   ;
  1078.  
  1079.                 top_too = headents[ods.cur_e].hd_text == ods.top_l;
  1080.  
  1081.                 if(call_builder(&headents[ods.cur_e], &mangled,
  1082.                         &err) < 0){
  1083.                     fix_mangle_and_err(&mangled, &err,
  1084.                            headents[ods.cur_e].name);
  1085.                 }
  1086.  
  1087.                 for(ods.cur_l = headents[ods.cur_e].hd_text;
  1088.                 ods.cur_l->next && cur_row;
  1089.                 ods.cur_l = ods.cur_l->next, cur_row--)
  1090.                   ;
  1091.  
  1092.                 if(top_too)
  1093.                   ods.top_l = headents[ods.cur_e].hd_text;
  1094.             }
  1095.  
  1096.                     UpdateHeader(0);
  1097.                     } else {
  1098.                         strcpy(headents[ods.cur_e].hd_text->text, bufp);
  1099.                     }
  1100.  
  1101.             free(bufp);
  1102.             /* mark this entry dirty */
  1103.             headents[ods.cur_e].sticky = 1;
  1104.             headents[ods.cur_e].dirty  = 1;
  1105.             fix_mangle_and_err(&mangled,&err,headents[ods.cur_e].name);
  1106.         }
  1107.         } else {
  1108.                 /*----- No selector -----*/
  1109.         (*term.t_beep)();
  1110.         continue;
  1111.         }
  1112.  
  1113.         PaintBody(0);
  1114.         if(errmss != NULL) {
  1115.         (*term.t_beep)();
  1116.         emlwrite(errmss, NULL);
  1117.         free(errmss);
  1118.         errmss = NULL;
  1119.         }
  1120.         continue;
  1121.  
  1122.       case (CTRL|'G'):            /* HELP */
  1123.         if(term.t_mrow == 0){
  1124.         if(km_popped == 0){
  1125.             km_popped = 2;
  1126.             sgarbk = 1;            /* bring up menu */
  1127.             break;
  1128.         }
  1129.         }
  1130.  
  1131.         if(!ComposerHelp(ods.cur_e))
  1132.           break;                /* else, fall through... */
  1133.  
  1134.       case (CTRL|'L'):            /* redraw requested */
  1135.         PaintBody(0);
  1136.         break;
  1137.  
  1138.       case (CTRL|'_'):            /* file editor */
  1139.             if(headents[ods.cur_e].fileedit != NULL){
  1140.         struct headerentry *e;
  1141.         struct hdr_line    *line;
  1142.         int                 sz = 0;
  1143.         char               *filename;
  1144.         VARS_TO_SAVE       *saved_state;
  1145.  
  1146.         /*
  1147.          * Since the fileedit will make a new call back to pico()
  1148.          * we need to save and restore the pico state here.
  1149.          */
  1150.         if((saved_state = save_pico_state()) != NULL){
  1151.             e = &headents[ods.cur_e];
  1152.  
  1153.             for(line = e->hd_text; line != NULL; line = line->next)
  1154.               sz += strlen(line->text);
  1155.             
  1156.             filename = (char *)malloc(sz + 1);
  1157.             filename[0] = '\0';
  1158.  
  1159.             for(line = e->hd_text; line != NULL; line = line->next)
  1160.               strcat(filename, line->text);
  1161.  
  1162.             errmss = (*(headents[ods.cur_e].fileedit))(filename);
  1163.  
  1164.             free(filename);
  1165.             restore_pico_state(saved_state);
  1166.             free_pico_state(saved_state);
  1167.             ttresize();            /* fixup screen bufs */
  1168.             picosigs();            /* restore altered signals */
  1169.         }
  1170.         else{
  1171.             char *s = "Not enough memory";
  1172.  
  1173.             errmss = (char *)malloc((strlen(s)+1) * sizeof(char));
  1174.             strcpy(errmss, s);
  1175.         }
  1176.  
  1177.         PaintBody(0);
  1178.  
  1179.         if(errmss != NULL) {
  1180.             (*term.t_beep)();
  1181.             emlwrite(errmss, NULL);
  1182.             free(errmss);
  1183.             errmss = NULL;
  1184.         }
  1185.  
  1186.         continue;
  1187.         }
  1188.         else
  1189.           goto bleep;
  1190.  
  1191.         break;
  1192.  
  1193.       default :                /* huh? */
  1194. bleep:
  1195.         if(ch&CTRL)
  1196.           emlwrite("\007Unknown command: ^%c", (void *)(ch&0xff));
  1197.         else
  1198.       case BADESC:
  1199.           emlwrite("\007Unknown command", NULL);
  1200.  
  1201.       case NODATA:
  1202.         break;
  1203.     }
  1204.     }
  1205.     while (ods.p_line < ComposerTopLine);
  1206.  
  1207.     display_delimiter(1);
  1208.     curwp->w_flag |= WFMODE;
  1209.     movecursor(currow, curcol);
  1210.     ComposerEditing = FALSE;
  1211.     if (ComposerTopLine == BOTTOM()){
  1212.     UpdateHeader(0);
  1213.     PaintHeader(COMPOSER_TOP_LINE, FALSE);
  1214.     PaintBody(1);
  1215.     }
  1216.     
  1217.     return(retval);
  1218. }
  1219.  
  1220.  
  1221. /*
  1222.  *
  1223.  */
  1224. int
  1225. header_downline(beyond, gripe)
  1226.     int beyond, gripe;
  1227. {
  1228.     struct hdr_line *new_l, *l;
  1229.     int    new_e, status, fullpaint, len, e, incr = 0;
  1230.  
  1231.     /* calculate the next line: physical *and* logical */
  1232.     status    = 0;
  1233.     new_e     = ods.cur_e;
  1234.     if((new_l = next_sel_hline(&new_e, ods.cur_l)) == NULL && !beyond){
  1235.  
  1236.     if(gripe){
  1237.         char xx[81];
  1238.  
  1239.         strcpy(xx, "Can't move down. Use ^X to ");
  1240.         strcat(xx, (Pmaster && Pmaster->exit_label)
  1241.                 ? Pmaster->exit_label
  1242.                 : (gmode & MDHDRONLY)
  1243.                   ? "eXit/Save"
  1244.                   : (gmode & MDVIEW)
  1245.                 ? "eXit"
  1246.                 : "Send");
  1247.         strcat(xx, ".");
  1248.         emlwrite(xx, NULL);
  1249.     }
  1250.  
  1251.         return(0);
  1252.     }
  1253.  
  1254.     /*
  1255.      * Because of blank header lines the cursor may need to move down
  1256.      * more than one line. Figure out how far.
  1257.      */
  1258.     e = ods.cur_e;
  1259.     l = ods.cur_l;
  1260.     while(l != new_l){
  1261.     if((l = next_hline(&e, l)) != NULL)
  1262.       incr++;
  1263.     else
  1264.       break;  /* can't happen */
  1265.     }
  1266.  
  1267.     ods.p_line += incr;
  1268.     fullpaint = ods.p_line >= BOTTOM();    /* force full redraw?       */
  1269.  
  1270.     /* expand what needs expanding */
  1271.     if(new_e != ods.cur_e || !new_l){        /* new (or last) field !    */
  1272.     if(new_l)
  1273.       InvertPrompt(ods.cur_e, FALSE);    /* turn off current entry   */
  1274.  
  1275.     if(headents[ods.cur_e].is_attach) {    /* verify data ?        */
  1276.         if(status = SyncAttach()){        /* fixup if 1 or -1        */
  1277.         headents[ods.cur_e].rich_header = 0;
  1278.         if(FormatLines(headents[ods.cur_e].hd_text, "",
  1279.                    term.t_ncol-headents[new_e].prlen,
  1280.                    headents[ods.cur_e].break_on_comma, 0) == -1)
  1281.           emlwrite("\007Format lines failed!", NULL);
  1282.         }
  1283.     } else if(headents[ods.cur_e].builder) { /* expand addresses        */
  1284.         int mangled = 0;
  1285.         char *err = NULL;
  1286.  
  1287.         if((status = call_builder(&headents[ods.cur_e], &mangled, &err))>0){
  1288.         struct hdr_line *l;        /* fixup ods.cur_l */
  1289.         ods.p_line = 0;            /* force top line recalc */
  1290.         for(l = headents[ods.cur_e].hd_text; l; l = l->next)
  1291.           ods.cur_l = l;
  1292.  
  1293.         if(new_l)            /* if new_l, force validity */
  1294.           new_l = headents[new_e].hd_text;
  1295.  
  1296.         NewTop(0);            /* get new top_l */
  1297.         }
  1298.         else if(status < 0){        /* bad addr? no leave! */
  1299.         --ods.p_line;
  1300.         fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
  1301.         InvertPrompt(ods.cur_e, TRUE);
  1302.         return(0);
  1303.         }
  1304.  
  1305.         fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
  1306.     }
  1307.  
  1308.     if(new_l){                /* if one below, turn it on */
  1309.         InvertPrompt(new_e, TRUE);
  1310.         sgarbk = 1;                /* paint keymenu too        */
  1311.     }
  1312.     }
  1313.  
  1314.     if(new_l){                    /* fixup new pointers        */
  1315.     ods.cur_l = (ods.cur_e != new_e) ? headents[new_e].hd_text : new_l;
  1316.     ods.cur_e = new_e;
  1317.     if(ods.p_off > (len = strlen(ods.cur_l->text)))
  1318.       ods.p_off = len;
  1319.     }
  1320.  
  1321.     if(!new_l || status || fullpaint){        /* handle big screen paint  */
  1322.     UpdateHeader(0);
  1323.     PaintHeader(COMPOSER_TOP_LINE, FALSE);
  1324.     PaintBody(1);
  1325.  
  1326.     if(!new_l){                /* make sure we're done     */
  1327.         ods.p_line = ComposerTopLine;
  1328.         InvertPrompt(ods.cur_e, FALSE);    /* turn off current entry   */
  1329.     }
  1330.     }
  1331.  
  1332.     return(new_l ? 1 : 0);
  1333. }
  1334.  
  1335.  
  1336. /*
  1337.  *
  1338.  */
  1339. int
  1340. header_upline(gripe)
  1341.     int gripe;
  1342. {
  1343.     struct hdr_line *new_l, *l;
  1344.     int    new_e, status, fullpaint, len, e, incr = 0;
  1345.  
  1346.     /* calculate the next line: physical *and* logical */
  1347.     status    = 0;
  1348.     new_e     = ods.cur_e;
  1349.     if(!(new_l = prev_sel_hline(&new_e, ods.cur_l))){    /* all the way up! */
  1350.     ods.p_line = COMPOSER_TOP_LINE;
  1351.     if(gripe)
  1352.       emlwrite("Can't move beyond top of %s",
  1353.           (Pmaster->pine_flags & MDHDRONLY) ? "entry" : "header");
  1354.  
  1355.     return(0);
  1356.     }
  1357.     
  1358.     /*
  1359.      * Because of blank header lines the cursor may need to move up
  1360.      * more than one line. Figure out how far.
  1361.      */
  1362.     e = ods.cur_e;
  1363.     l = ods.cur_l;
  1364.     while(l != new_l){
  1365.     if((l = prev_hline(&e, l)) != NULL)
  1366.       incr++;
  1367.     else
  1368.       break;  /* can't happen */
  1369.     }
  1370.  
  1371.     ods.p_line -= incr;
  1372.     fullpaint = ods.p_line <= COMPOSER_TOP_LINE;
  1373.  
  1374.     if(new_e != ods.cur_e){            /* new field ! */
  1375.     InvertPrompt(ods.cur_e, FALSE);
  1376.     if(headents[ods.cur_e].is_attach){
  1377.         if(status = SyncAttach()){        /* non-zero ? reformat field */
  1378.         headents[ods.cur_e].rich_header = 0;
  1379.         if(FormatLines(headents[ods.cur_e].hd_text, "",
  1380.                    term.t_ncol - headents[ods.cur_e].prlen,
  1381.                    headents[ods.cur_e].break_on_comma,0) == -1)
  1382.           emlwrite("\007Format lines failed!", NULL);
  1383.         }
  1384.     }
  1385.     else if(headents[ods.cur_e].builder){
  1386.         int mangled = 0;
  1387.         char *err = NULL;
  1388.  
  1389.         if((status = call_builder(&headents[ods.cur_e], &mangled,
  1390.                       &err)) >= 0){
  1391.         /* repair new_l */
  1392.         for(new_l = headents[new_e].hd_text;
  1393.             new_l->next;
  1394.             new_l=new_l->next)
  1395.           ;
  1396.  
  1397.         /* and cur_l (required in fix_and... */
  1398.         ods.cur_l = new_l;
  1399.         }
  1400.         else{
  1401.         ++ods.p_line;
  1402.         fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
  1403.         InvertPrompt(ods.cur_e, TRUE);
  1404.         return(0);
  1405.         }
  1406.  
  1407.         fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
  1408.     }
  1409.  
  1410.     InvertPrompt(new_e, TRUE);
  1411.     sgarbk = 1;
  1412.     }
  1413.  
  1414.     ods.cur_e = new_e;                /* update pointers */
  1415.     ods.cur_l = new_l;
  1416.     if(ods.p_off > (len = strlen(ods.cur_l->text)))
  1417.       ods.p_off = len;
  1418.  
  1419.     if(status > 0 || fullpaint){
  1420.     UpdateHeader(0);
  1421.     PaintHeader(COMPOSER_TOP_LINE, FALSE);
  1422.     PaintBody(1);
  1423.     }
  1424.  
  1425.     return(1);
  1426. }
  1427.  
  1428.  
  1429. /*
  1430.  * 
  1431.  */
  1432. int
  1433. AppendAttachment(fn, sz, cmt)
  1434.     char *fn, *sz, *cmt;
  1435. {
  1436.     int     a_e, status, spaces;
  1437.     struct hdr_line *lp;
  1438.  
  1439.     /*--- Find headerentry that is attachments (only first) --*/
  1440.     for(a_e = 0; headents[a_e].name != NULL; a_e++ )
  1441.       if(headents[a_e].is_attach){
  1442.       /* make sure field stays displayed */
  1443.       headents[a_e].rich_header = 0;
  1444.       headents[a_e].display_it = 1;
  1445.       break;
  1446.       }
  1447.  
  1448.     /* append new attachment line */
  1449.     for(lp = headents[a_e].hd_text; lp->next; lp=lp->next)
  1450.       ;
  1451.  
  1452.     /* build new attachment line */
  1453.     if(lp->text[0]){        /* adding a line? */
  1454.     strcat(lp->text, ",");    /* append delimiter */
  1455.     if(lp->next = HALLOC()){    /* allocate new line */
  1456.         lp->next->prev = lp;
  1457.         lp->next->next = NULL;
  1458.         lp = lp->next;
  1459.     }
  1460.     else{
  1461.         emlwrite("\007Can't allocate line for new attachment!", NULL);
  1462.         return(0);
  1463.     }
  1464.     }
  1465.  
  1466.     
  1467.     spaces = (*fn == '\"') ? 0 : (strpbrk(fn, "(), \t") != NULL);
  1468.     sprintf(lp->text, "%s%s%s (%s) \"%.*s\"",
  1469.         spaces ? "\"" : "", fn, spaces ? "\"" : "",
  1470.         sz ? sz : "", 80, cmt ? cmt : "");
  1471.  
  1472.     /* validate the new attachment, and reformat if needed */
  1473.     if(status = SyncAttach()){
  1474.     if(status < 0)
  1475.       emlwrite("\007Problem attaching: %s", fn);
  1476.  
  1477.     if(FormatLines(headents[a_e].hd_text, "",
  1478.                term.t_ncol - headents[a_e].prlen,
  1479.                headents[a_e].break_on_comma, 0) == -1){
  1480.         emlwrite("\007Format lines failed!", NULL);
  1481.         return(0);
  1482.     }
  1483.     }
  1484.  
  1485.     UpdateHeader(0);
  1486.     PaintHeader(COMPOSER_TOP_LINE, status != 0);
  1487.     PaintBody(1);
  1488.     return(status != 0);
  1489. }
  1490.  
  1491.  
  1492.  
  1493.  
  1494. /*
  1495.  * LineEdit - the idea is to manage 7 bit ascii character only input.
  1496.  *            Always use insert mode and handle line wrapping
  1497.  *
  1498.  *    returns:
  1499.  *        Any characters typed in that aren't printable 
  1500.  *        (i.e. commands)
  1501.  *
  1502.  *    notes: 
  1503.  *        Assume we are guaranteed that there is sufficiently 
  1504.  *        more buffer space in a line than screen width (just one 
  1505.  *        less thing to worry about).  If you want to change this,
  1506.  *        then pputc will have to be taught to check the line buffer
  1507.  *        length, and HALLOC() will probably have to become a func.
  1508.  */
  1509. LineEdit(allowedit)
  1510. int    allowedit;
  1511. {
  1512.     register struct    hdr_line   *lp;        /* temporary line pointer    */
  1513.     register int    i;
  1514.     register int    ch = 0;
  1515.     register int    status;            /* various func's return val */
  1516.     register char    *tbufp;            /* temporary buffer pointers */
  1517.          int    skipmove = 0;
  1518.              char    *strng;
  1519.     int      last_key;                /* last keystroke  */
  1520.  
  1521.     strng   = ods.cur_l->text;            /* initialize offsets */
  1522.     ods.p_len = strlen(strng);
  1523.     if(ods.p_off < 0)                /* offset within range? */
  1524.       ods.p_off = 0;
  1525.     else if(ods.p_off > ods.p_len)
  1526.       ods.p_off = ods.p_len;
  1527.     else if(ods.p_off > LINELEN())        /* shouldn't happen, but */
  1528.         ods.p_off = LINELEN();            /* you never know...     */
  1529.  
  1530.     while(1){                    /* edit the line... */
  1531.  
  1532.     if(skipmove)
  1533.       skipmove = 0;
  1534.     else
  1535.       HeaderPaintCursor();
  1536.  
  1537.     last_key = ch;
  1538.  
  1539.     (*term.t_flush)();            /* get everything out */
  1540.  
  1541. #ifdef MOUSE
  1542.     mouse_in_content(KEY_MOUSE, -1, -1, 0, 0);
  1543.     register_mfunc(mouse_in_content,2,0,term.t_nrow-(term.t_mrow+1),
  1544.                term.t_ncol);
  1545. #endif
  1546. #ifdef    _WINDOWS
  1547.     { extern int composer_file_drop();
  1548.     mswin_setdndcallback (composer_file_drop);
  1549.     }
  1550. #endif
  1551.  
  1552.     ch = GetKey();
  1553.     
  1554.     if (term.t_nrow < 6 && ch != NODATA){
  1555.         (*term.t_beep)();
  1556.         emlwrite("Please make the screen bigger.", NULL);
  1557.         continue;
  1558.     }
  1559.  
  1560. #ifdef    MOUSE
  1561.     clear_mfunc(mouse_in_content);
  1562. #endif
  1563. #ifdef    _WINDOWS
  1564.     mswin_cleardndcallback ();
  1565. #endif
  1566.  
  1567.     switch(ch){
  1568.       case DEL :
  1569.         if(gmode & P_DELRUBS)
  1570.           ch = KEY_DEL;
  1571.  
  1572.       default :
  1573.         (*Pmaster->keybinput)();
  1574.         if(!time_to_check())
  1575.           break;
  1576.  
  1577.       case NODATA :            /* new mail ? */
  1578.         if((*Pmaster->newmail)(ch == NODATA ? 0 : 2, 1) >= 0){
  1579.         int rv;
  1580.         
  1581.         if(km_popped){
  1582.             term.t_mrow = 2;
  1583.             curwp->w_ntrows -= 2;
  1584.         }
  1585.  
  1586.         clearcursor();
  1587.         mlerase();
  1588.         rv = (*Pmaster->showmsg)(ch);
  1589.         ttresize();
  1590.         picosigs();
  1591.         if(rv)        /* Did showmsg corrupt the display? */
  1592.           PaintBody(0);    /* Yes, repaint */
  1593.  
  1594.         mpresf = 1;
  1595.         if(km_popped){
  1596.             term.t_mrow = 0;
  1597.             curwp->w_ntrows += 2;
  1598.         }
  1599.         }
  1600.  
  1601.         clearcursor();
  1602.         movecursor(ods.p_line, ods.p_off+headents[ods.cur_e].prlen);
  1603.         if(ch == NODATA)            /* GetKey timed out */
  1604.           continue;
  1605.  
  1606.         break;
  1607.     }
  1608.  
  1609.         if(mpresf){                /* blast old messages */
  1610.         if(mpresf++ > NMMESSDELAY){        /* every few keystrokes */
  1611.         mlerase();
  1612.         movecursor(ods.p_line, ods.p_off+headents[ods.cur_e].prlen);
  1613.         }
  1614.         }
  1615.  
  1616.     if(VALID_KEY(ch)){            /* char input */
  1617.             /*
  1618.              * if we are allowing editing, insert the new char
  1619.              * end up leaving tbufp pointing to newly
  1620.              * inserted character in string, and offset to the
  1621.              * index of the character after the inserted ch ...
  1622.              */
  1623.             if(allowedit){
  1624.         if(headents[ods.cur_e].only_file_chars
  1625.            && !fallowc((unsigned char) ch)){
  1626.             /* no garbage in filenames */
  1627.             emlwrite("\007Can't have a '%c' in folder name",
  1628.                  (void *) ch);
  1629.             continue;
  1630.         }
  1631.         else if(headents[ods.cur_e].is_attach
  1632.             && intag(strng,ods.p_off)){
  1633.             emlwrite("\007Can't edit attachment number!", NULL);
  1634.             continue;
  1635.         }
  1636.  
  1637.         if(headents[ods.cur_e].single_space){
  1638.             if(ch == ' ' 
  1639.                && (strng[ods.p_off]==' ' || strng[ods.p_off-1]==' '))
  1640.               continue;
  1641.         }
  1642.  
  1643.         /*
  1644.          * go ahead and add the character...
  1645.          */
  1646.         tbufp = &strng[++ods.p_len];    /* find the end */
  1647.         do{
  1648.             *tbufp = tbufp[-1];
  1649.         } while(--tbufp > &strng[ods.p_off]);    /* shift right */
  1650.         strng[ods.p_off++] = ch;    /* add char to str */
  1651.  
  1652.         /* mark this entry dirty */
  1653.         headents[ods.cur_e].sticky = 1;
  1654.         headents[ods.cur_e].dirty  = 1;
  1655.  
  1656.         /*
  1657.          * then find out where things fit...
  1658.          */
  1659.         if(ods.p_len < LINELEN()){
  1660.             CELL c;
  1661.  
  1662.             c.c = ch;
  1663.             c.a = 0;
  1664.             if(pinsert(c)){        /* add char to str */
  1665.             skipmove++;        /* must'a been optimal */
  1666.             continue;         /* on to the next! */
  1667.             }
  1668.         }
  1669.         else{
  1670.                     if((status = FormatLines(ods.cur_l, "", LINELEN(), 
  1671.                         headents[ods.cur_e].break_on_comma,0)) == -1){
  1672.                         (*term.t_beep)();
  1673.                         continue;
  1674.                     }
  1675.                     else{
  1676.             /*
  1677.              * during the format, the dot may have moved
  1678.              * down to the next line...
  1679.              */
  1680.             if(ods.p_off >= strlen(strng)){
  1681.                 ods.p_line++;
  1682.                 ods.p_off -= strlen(strng);
  1683.                 ods.cur_l = ods.cur_l->next;
  1684.                 strng = ods.cur_l->text;
  1685.             }
  1686.             ods.p_len = strlen(strng);
  1687.             }
  1688.             UpdateHeader(0);
  1689.             PaintHeader(COMPOSER_TOP_LINE, FALSE);
  1690.             PaintBody(1);
  1691.                     continue;
  1692.         }
  1693.             }
  1694.             else{  
  1695.                 rdonly();
  1696.                 continue;
  1697.             } 
  1698.         }
  1699.         else {                    /* interpret ch as a command */
  1700.             switch (ch = normalize_cmd(ch, ckm, 2)) {
  1701.           case (CTRL|'@') :        /* word skip */
  1702.         while(strng[ods.p_off]
  1703.               && isalnum((unsigned char)strng[ods.p_off]))
  1704.           ods.p_off++;        /* skip any text we're in */
  1705.  
  1706.         while(strng[ods.p_off]
  1707.               && !isalnum((unsigned char)strng[ods.p_off]))
  1708.           ods.p_off++;        /* skip any whitespace after it */
  1709.  
  1710.         if(strng[ods.p_off] == '\0'){
  1711.             ods.p_off = 0;    /* end of line, let caller handle it */
  1712.             return(KEY_DOWN);
  1713.         }
  1714.  
  1715.         continue;
  1716.  
  1717.           case (CTRL|'K') :            /* kill line cursor's on */
  1718.         if(!allowedit){
  1719.             rdonly();
  1720.             continue;
  1721.         }
  1722.  
  1723.         lp = ods.cur_l;
  1724.         if (!(gmode & MDDTKILL))
  1725.           ods.p_off = 0;
  1726.  
  1727.         if(KillHeaderLine(lp, (last_key == (CTRL|'K')))){
  1728.             if(optimize && 
  1729.                !(ods.cur_l->prev==NULL && ods.cur_l->next==NULL)) 
  1730.               scrollup(wheadp, ods.p_line, 1);
  1731.  
  1732.             if(ods.cur_l->next == NULL)
  1733.               if(zotcomma(ods.cur_l->text)){
  1734.               if(ods.p_off > 0)
  1735.                 ods.p_off = strlen(ods.cur_l->text);
  1736.               }
  1737.             
  1738.             i = (ods.p_line == COMPOSER_TOP_LINE);
  1739.             UpdateHeader(0);
  1740.             PaintHeader(COMPOSER_TOP_LINE, TRUE);
  1741.  
  1742.             if(km_popped){
  1743.             km_popped--;
  1744.             movecursor(term.t_nrow, 0);
  1745.             peeol();
  1746.             }
  1747.  
  1748.             PaintBody(1);
  1749.  
  1750.         }
  1751.         strng = ods.cur_l->text;
  1752.         ods.p_len = strlen(strng);
  1753.         headents[ods.cur_e].sticky = 0;
  1754.         headents[ods.cur_e].dirty  = 1;
  1755.         continue;
  1756.  
  1757.           case (CTRL|'U') :            /* un-delete deleted lines */
  1758.         if(!allowedit){
  1759.             rdonly();
  1760.             continue;
  1761.         }
  1762.  
  1763.         if(SaveHeaderLines()){
  1764.             UpdateHeader(0);
  1765.             PaintHeader(COMPOSER_TOP_LINE, FALSE);
  1766.             if(km_popped){
  1767.             km_popped--;
  1768.             movecursor(term.t_nrow, 0);
  1769.             peeol();
  1770.             }
  1771.  
  1772.             PaintBody(1);
  1773.  
  1774.             if (!(gmode & MDDTKILL))
  1775.               ods.p_off = 0;        /* dot hasn't moved! */
  1776.             strng = ods.cur_l->text;
  1777.             ods.p_len = strlen(strng);
  1778.             headents[ods.cur_e].sticky = 1;
  1779.             headents[ods.cur_e].dirty  = 1;
  1780.         }
  1781.         else
  1782.           emlwrite("Problem Unkilling text", NULL);
  1783.         continue;
  1784.  
  1785.           case (CTRL|'F') :
  1786.           case KEY_RIGHT:            /* move character right */
  1787.         if(ods.p_off < ods.p_len){
  1788.             pputc(pscr(ods.p_line, 
  1789.                    (ods.p_off++)+headents[ods.cur_e].prlen)->c,0);
  1790.             skipmove++;
  1791.             continue;
  1792.         }
  1793.         else if(gmode & MDHDRONLY)
  1794.           continue;
  1795.  
  1796.         ods.p_off = 0;
  1797.         return(KEY_DOWN);
  1798.  
  1799.           case (CTRL|'B') :
  1800.           case KEY_LEFT    :        /* move character left */
  1801.         if(ods.p_off > 0){
  1802.             ods.p_off--;
  1803.             continue;
  1804.         }
  1805.         if(ods.p_line != COMPOSER_TOP_LINE)
  1806.           ods.p_off = 1000;        /* put cursor at end of line */
  1807.         return(KEY_UP);
  1808.  
  1809.           case (CTRL|'M') :            /* goto next field */
  1810.         ods.p_off = 0;
  1811.         return(KEY_DOWN);
  1812.  
  1813.           case KEY_HOME :
  1814.           case (CTRL|'A') :            /* goto beginning of line */
  1815.         ods.p_off = 0;
  1816.         continue;
  1817.  
  1818.           case KEY_END  :
  1819.           case (CTRL|'E') :            /* goto end of line */
  1820.         ods.p_off = ods.p_len;
  1821.         continue;
  1822.  
  1823.           case (CTRL|'D')   :        /* blast this char */
  1824.           case KEY_DEL :
  1825.         if(!allowedit){
  1826.             rdonly();
  1827.             continue;
  1828.         }
  1829.         else if(ods.p_off >= strlen(strng))
  1830.           continue;
  1831.  
  1832.         if(headents[ods.cur_e].is_attach && intag(strng, ods.p_off)){
  1833.             emlwrite("\007Can't edit attachment number!", NULL);
  1834.             continue;
  1835.         }
  1836.  
  1837.         pputc(strng[ods.p_off++], 0);     /* drop through and rubout */
  1838.  
  1839.           case DEL        :            /* blast previous char */
  1840.           case (CTRL|'H') :
  1841.         if(!allowedit){
  1842.             rdonly();
  1843.             continue;
  1844.         }
  1845.  
  1846.         if(headents[ods.cur_e].is_attach && intag(strng, ods.p_off-1)){
  1847.             emlwrite("\007Can't edit attachment number!", NULL);
  1848.             continue;
  1849.         }
  1850.  
  1851.         if(ods.p_off > 0){        /* just shift left one char */
  1852.             ods.p_len--;
  1853.             headents[ods.cur_e].dirty  = 1;
  1854.             if(ods.p_len == 0)
  1855.               headents[ods.cur_e].sticky = 0;
  1856.             else
  1857.               headents[ods.cur_e].sticky = 1;
  1858.  
  1859.             tbufp = &strng[--ods.p_off];
  1860.             while(*tbufp++ != '\0')
  1861.               tbufp[-1] = *tbufp;
  1862.             tbufp = &strng[ods.p_off];
  1863.             if(pdel())            /* physical screen delete */
  1864.               skipmove++;        /* must'a been optimal */
  1865.         }
  1866.         else{                /* may have work to do */
  1867.             if(ods.cur_l->prev == NULL){  
  1868.             (*term.t_beep)();    /* no erase into next field */
  1869.             continue;
  1870.             }
  1871.  
  1872.             ods.p_line--;
  1873.             ods.cur_l = ods.cur_l->prev;
  1874.             strng = ods.cur_l->text;
  1875.             if((i=strlen(strng)) > 0){
  1876.             strng[i-1] = '\0';    /* erase the character */
  1877.             ods.p_off = i-1;
  1878.             }
  1879.             else{
  1880.             headents[ods.cur_e].sticky = 0;
  1881.             ods.p_off = 0;
  1882.             }
  1883.             
  1884.             tbufp = &strng[ods.p_off];
  1885.         }
  1886.  
  1887.         if((status = FormatLines(ods.cur_l, "", LINELEN(), 
  1888.                    headents[ods.cur_e].break_on_comma,0))==-1){
  1889.             (*term.t_beep)();
  1890.             continue;
  1891.         }
  1892.         else{
  1893.             /*
  1894.              * beware, the dot may have moved...
  1895.              */
  1896.             while((ods.p_len=strlen(strng)) < ods.p_off){
  1897.             ods.p_line++;
  1898.             ods.p_off -= strlen(strng);
  1899.             ods.cur_l = ods.cur_l->next;
  1900.             strng = ods.cur_l->text;
  1901.             ods.p_len = strlen(strng);
  1902.             tbufp = &strng[ods.p_off];
  1903.             status = TRUE;
  1904.             }
  1905.             UpdateHeader(0);
  1906.             PaintHeader(COMPOSER_TOP_LINE, FALSE);
  1907.             if(status == TRUE)
  1908.               PaintBody(1);
  1909.         }
  1910.  
  1911.         movecursor(ods.p_line, ods.p_off+headents[ods.cur_e].prlen);
  1912.  
  1913.         if(skipmove)
  1914.           continue;
  1915.  
  1916.         break;
  1917.  
  1918.               default   :
  1919.         return(ch);
  1920.             }
  1921.         }
  1922.  
  1923.     while (*tbufp != '\0')        /* synchronizing loop */
  1924.       pputc(*tbufp++, 0);
  1925.  
  1926.     if(ods.p_len < LINELEN())
  1927.       peeol();
  1928.  
  1929.     }
  1930. }
  1931.  
  1932.  
  1933. int
  1934. HeaderPaintCursor()
  1935. {
  1936.     movecursor(ods.p_line, ods.p_off+headents[ods.cur_e].prlen);
  1937. }
  1938.  
  1939.  
  1940.  
  1941. /*
  1942.  * FormatLines - Place the given text at the front of the given line->text
  1943.  *               making sure to properly format the line, then check
  1944.  *               all lines below for proper format.
  1945.  *
  1946.  *    notes:
  1947.  *        Not much optimization at all.  Right now, it recursively
  1948.  *        fixes all remaining lines in the entry.  Some speed might
  1949.  *        gained if this was built to iteratively scan the lines.
  1950.  *
  1951.  *    returns:
  1952.  *        -1 on error
  1953.  *        FALSE if only this line is changed
  1954.  *        TRUE  if text below the first line is changed
  1955.  */
  1956. FormatLines(h, instr, maxlen, break_on_comma, quoted)
  1957. struct  hdr_line  *h;                /* where to begin formatting */
  1958. char    *instr;                    /* input string */
  1959. int    maxlen;                    /* max chars on a line */
  1960. int    break_on_comma;                /* break lines on commas */
  1961. int    quoted;                    /* this line inside quotes */
  1962. {
  1963.     int        retval = FALSE;
  1964.     register    int    i, l;
  1965.     char    *ostr;                /* pointer to output string */
  1966.     register    char    *breakp;        /* pointer to line break */
  1967.     register    char    *bp, *tp;        /* temporary pointers */
  1968.     char    *buf;                /* string to add later */
  1969.     struct hdr_line    *nlp, *lp;
  1970.  
  1971.     ostr = h->text;
  1972.     nlp = h->next;
  1973.     l = strlen(instr) + strlen(ostr);
  1974.     if((buf = (char *)malloc(l+10)) == NULL)
  1975.       return(-1);
  1976.  
  1977.     if(l >= maxlen){                /* break then fixup below */
  1978.  
  1979.     if((l=strlen(instr)) < maxlen){        /* room for more */
  1980.  
  1981.         if(break_on_comma && (bp = (char *)strqchr(instr, ',', "ed, -1))){
  1982.         bp += (bp[1] == ' ') ? 2 : 1;
  1983.         for(tp = bp; *tp && *tp == ' '; tp++)
  1984.           ;
  1985.  
  1986.         strcpy(buf, tp);
  1987.         strcat(buf, ostr);
  1988.         for(i = 0; &instr[i] < bp; i++)
  1989.           ostr[i] = instr[i];
  1990.         ostr[i] = '\0';
  1991.         retval = TRUE;
  1992.         }
  1993.         else{
  1994.         breakp = break_point(ostr, maxlen-strlen(instr),
  1995.                      break_on_comma ? ',' : ' ',
  1996.                      break_on_comma ? "ed : NULL);
  1997.  
  1998.         if(breakp == ostr){    /* no good breakpoint */
  1999.             if(break_on_comma && *breakp == ','){
  2000.             breakp = ostr + 1;
  2001.             retval = TRUE;
  2002.             }
  2003.             else if(strchr(instr,(break_on_comma && !quoted)?',':' ')){
  2004.             strcpy(buf, ostr);
  2005.             strcpy(ostr, instr);
  2006.             }
  2007.             else{        /* instr's broken as we can get it */
  2008.             breakp = &ostr[maxlen-strlen(instr)-1];
  2009.             retval = TRUE;
  2010.             }
  2011.         }
  2012.         else
  2013.           retval = TRUE;
  2014.         
  2015.         if(retval){
  2016.             strcpy(buf, breakp);    /* save broken line  */
  2017.             if(breakp == ostr){
  2018.             strcpy(ostr, instr);    /* simple if no break */
  2019.             }
  2020.             else{
  2021.             *breakp = '\0';        /* more work to break it */
  2022.             i = strlen(instr);
  2023.             /*
  2024.              * shift ostr i chars
  2025.              */
  2026.             for(bp=breakp; bp >= ostr && i; bp--)
  2027.               *(bp+i) = *bp;
  2028.             for(tp=ostr, bp=instr; *bp != '\0'; tp++, bp++)
  2029.               *tp = *bp;        /* then add instr */
  2030.             }
  2031.         }
  2032.         }
  2033.     }
  2034.     /*
  2035.      * Short-circuit the recursion in this case.
  2036.      * No time right now to figure out how to do it better.
  2037.      */
  2038.     else if(l > 2*maxlen){
  2039.         char *instrp, *saveostr = NULL;
  2040.  
  2041.         retval = TRUE;
  2042.         if(ostr && *ostr){
  2043.         saveostr = (char *)malloc(strlen(ostr) + 1);
  2044.         strcpy(saveostr, ostr);
  2045.         ostr[0] = '\0';
  2046.         }
  2047.  
  2048.         instrp = instr;
  2049.         while(l > 2*maxlen){
  2050.         if(break_on_comma || maxlen == 1){
  2051.             breakp = (!(bp = strqchr(instrp, ',', "ed, maxlen))
  2052.                   || bp - instrp >= maxlen || maxlen == 1)
  2053.                    ? &instrp[maxlen]
  2054.                    : bp + ((bp[1] == ' ') ? 2 : 1);
  2055.         }
  2056.         else{
  2057.             breakp = break_point(instrp, maxlen, ' ', NULL);
  2058.  
  2059.             if(breakp == instrp)    /* no good break point */
  2060.               breakp = &instrp[maxlen - 1];
  2061.         }
  2062.         
  2063.         for(tp=ostr,bp=instrp; bp < breakp; tp++, bp++)
  2064.           *tp = *bp;
  2065.  
  2066.         *tp = '\0';
  2067.         l -= (breakp - instrp);
  2068.         instrp = breakp;
  2069.  
  2070.         if((lp = HALLOC()) == NULL){
  2071.             emlwrite("Can't allocate any more lines for header!", NULL);
  2072.             free(buf);
  2073.             return(-1);
  2074.         }
  2075.  
  2076.         lp->next = h->next;
  2077.         if(h->next)
  2078.           h->next->prev = lp;
  2079.  
  2080.         h->next = lp;
  2081.         lp->prev = h;
  2082.         lp->text[0] = '\0';
  2083.         h = h->next;
  2084.         ostr = h->text;
  2085.         }
  2086.  
  2087.         /*
  2088.          * Now l is still > maxlen. Do it the recursive way,
  2089.          * like the else clause below. Surely we could fix up the
  2090.          * flow control some here, but this works for now.
  2091.          */
  2092.  
  2093.         nlp = h->next;
  2094.         instr = instrp;
  2095.         if(saveostr){
  2096.         strcpy(ostr, saveostr);
  2097.         free(saveostr);
  2098.         }
  2099.  
  2100.         if(break_on_comma || maxlen == 1){
  2101.         breakp = (!(bp = strqchr(instrp, ',', "ed, maxlen))
  2102.               || bp - instrp >= maxlen || maxlen == 1)
  2103.                ? &instrp[maxlen]
  2104.                : bp + ((bp[1] == ' ') ? 2 : 1);
  2105.         }
  2106.         else{
  2107.         breakp = break_point(instrp, maxlen, ' ', NULL);
  2108.  
  2109.         if(breakp == instrp)        /* no good break point */
  2110.           breakp = &instrp[maxlen - 1];
  2111.         }
  2112.         
  2113.         strcpy(buf, breakp);        /* save broken line */
  2114.         strcat(buf, ostr);            /* add line that was there */
  2115.         for(tp=ostr,bp=instr; bp < breakp; tp++, bp++)
  2116.           *tp = *bp;
  2117.  
  2118.         *tp = '\0';
  2119.     }
  2120.     else{                    /* instr > maxlen ! */
  2121.         if(break_on_comma || maxlen == 1){
  2122.         breakp = (!(bp = strqchr(instr, ',', "ed, maxlen))
  2123.               || bp - instr >= maxlen || maxlen == 1)
  2124.                ? &instr[maxlen]
  2125.                : bp + ((bp[1] == ' ') ? 2 : 1);
  2126.         }
  2127.         else{
  2128.         breakp = break_point(instr, maxlen, ' ', NULL);
  2129.  
  2130.         if(breakp == instr)        /* no good break point */
  2131.           breakp = &instr[maxlen - 1];
  2132.         }
  2133.         
  2134.         strcpy(buf, breakp);        /* save broken line */
  2135.         strcat(buf, ostr);            /* add line that was there */
  2136.         for(tp=ostr,bp=instr; bp < breakp; tp++, bp++)
  2137.           *tp = *bp;
  2138.  
  2139.         *tp = '\0';
  2140.     }
  2141.  
  2142.     if(nlp == NULL){            /* no place to add below? */
  2143.         if((lp = HALLOC()) == NULL){
  2144.         emlwrite("Can't allocate any more lines for header!", NULL);
  2145.         free(buf);
  2146.         return(-1);
  2147.         }
  2148.  
  2149.         if(optimize && (i = physical_line(h)) != -1)
  2150.           scrolldown(wheadp, i - 1, 1);
  2151.  
  2152.         h->next = lp;            /* fix up links */
  2153.         lp->prev = h;
  2154.         lp->next = NULL;
  2155.         lp->text[0] = '\0';
  2156.         nlp = lp;
  2157.         retval = TRUE;
  2158.     }
  2159.     }
  2160.     else{                    /* combined length < max */
  2161.     if(*instr){
  2162.          strcpy(buf, instr);        /* insert instr before ostr */
  2163.          strcat(buf, ostr);
  2164.          strcpy(ostr, buf);
  2165.     }
  2166.  
  2167.     *buf = '\0';
  2168.     breakp = NULL;
  2169.  
  2170.     if(break_on_comma && (breakp = strqchr(ostr, ',', "ed, -1))){
  2171.         breakp += (breakp[1] == ' ') ? 2 : 1;
  2172.         strcpy(buf, breakp);
  2173.         *breakp = '\0';
  2174.  
  2175.         if(strlen(buf) && !nlp){
  2176.         if((lp = HALLOC()) == NULL){
  2177.             emlwrite("Can't allocate any more lines for header!",NULL);
  2178.             free(buf);
  2179.             return(-1);
  2180.         }
  2181.  
  2182.         if(optimize && (i = physical_line(h)) != -1)
  2183.           scrolldown(wheadp, i - 1, 1);
  2184.  
  2185.         h->next = lp;        /* fix up links */
  2186.         lp->prev = h;
  2187.         lp->next = NULL;
  2188.         lp->text[0] = '\0';
  2189.         nlp = lp;
  2190.         retval = TRUE;
  2191.         }
  2192.     }
  2193.  
  2194.     if(nlp){
  2195.         if(!strlen(buf) && !breakp){
  2196.         if(strlen(ostr) + strlen(nlp->text) >= maxlen){
  2197.             breakp = break_point(nlp->text, maxlen-strlen(ostr), 
  2198.                      break_on_comma ? ',' : ' ',
  2199.                      break_on_comma ? "ed : NULL);
  2200.             
  2201.             if(breakp == nlp->text){    /* commas this line? */
  2202.             for(tp=ostr; *tp  && *tp != ' '; tp++)
  2203.               ;
  2204.  
  2205.             if(!*tp){        /* no commas, get next best */
  2206.                 breakp += maxlen - strlen(ostr) - 1;
  2207.                 retval = TRUE;
  2208.             }
  2209.             else
  2210.               retval = FALSE;
  2211.             }
  2212.             else
  2213.               retval = TRUE;
  2214.  
  2215.             if(retval){            /* only if something to do */
  2216.             for(tp = &ostr[strlen(ostr)],bp=nlp->text; bp<breakp; 
  2217.             tp++, bp++)
  2218.               *tp = *bp;        /* add breakp to this line */
  2219.             *tp = '\0';
  2220.             for(tp=nlp->text, bp=breakp; *bp != '\0'; tp++, bp++)
  2221.               *tp = *bp;        /* shift next line to left */
  2222.             *tp = '\0';
  2223.             }
  2224.         }
  2225.         else{
  2226.             strcat(ostr, nlp->text);
  2227.  
  2228.             if(optimize && (i = physical_line(nlp)) != -1)
  2229.               scrollup(wheadp, i, 1);
  2230.  
  2231.             hldelete(nlp);
  2232.  
  2233.             if(!(nlp = h->next)){
  2234.             free(buf);
  2235.             return(TRUE);        /* can't go further */
  2236.             }
  2237.             else
  2238.               retval = TRUE;        /* more work to do? */
  2239.         }
  2240.         }
  2241.     }
  2242.     else{
  2243.         free(buf);
  2244.         return(FALSE);
  2245.     }
  2246.  
  2247.     }
  2248.  
  2249.     i = FormatLines(nlp, buf, maxlen, break_on_comma, quoted);
  2250.     free(buf);
  2251.     switch(i){
  2252.       case -1:                    /* bubble up worst case */
  2253.     return(-1);
  2254.       case FALSE:
  2255.     if(retval == FALSE)
  2256.       return(FALSE);
  2257.       default:
  2258.     return(TRUE);
  2259.     }
  2260. }
  2261.  
  2262.  
  2263.  
  2264. /*
  2265.  * PaintHeader - do the work of displaying the header from the given 
  2266.  *               physical screen line the end of the header.
  2267.  *
  2268.  *       17 July 91 - fixed reshow to deal with arbitrarily large headers.
  2269.  */
  2270. void
  2271. PaintHeader(line, clear)
  2272.     int    line;                    /* physical line on screen   */
  2273.     int    clear;                    /* clear before painting */
  2274. {
  2275.     register struct hdr_line    *lp;
  2276.     register char    *bufp;
  2277.     register int    curline;
  2278.     register int    curoffset;
  2279.     char     buf[NLINE];
  2280.     int      e;
  2281.  
  2282.     if(clear)
  2283.       pclear(COMPOSER_TOP_LINE, ComposerTopLine);
  2284.  
  2285.     curline   = COMPOSER_TOP_LINE;
  2286.     curoffset = 0;
  2287.  
  2288.     for(lp = ods.top_l, e = ods.top_e; ; curline++){
  2289.     if((curline == line) || ((lp = next_hline(&e, lp)) == NULL))
  2290.       break;
  2291.     }
  2292.  
  2293.     while(headents[e].name != NULL){            /* begin to redraw */
  2294.     while(lp != NULL){
  2295.         buf[0] = '\0';
  2296.             if((!lp->prev || curline == COMPOSER_TOP_LINE) && !curoffset){
  2297.             if(InvertPrompt(e, (e == ods.cur_e && ComposerEditing)) == -1
  2298.            && !is_blank(curline, 0, headents[e].prlen))
  2299.            sprintf(buf, "%*s", headents[e].prlen, " ");
  2300.         }
  2301.         else if(!is_blank(curline, 0, headents[e].prlen))
  2302.           sprintf(buf, "%*s", headents[e].prlen, " ");
  2303.  
  2304.         if(*(bufp = buf) != '\0'){        /* need to paint? */
  2305.         movecursor(curline, 0);        /* paint the line... */
  2306.         while(*bufp != '\0')
  2307.           pputc(*bufp++, 0);
  2308.         }
  2309.  
  2310.         bufp = &(lp->text[curoffset]);    /* skip chars already there */
  2311.         curoffset += headents[e].prlen;
  2312.         while(pscr(curline, curoffset) != NULL &&
  2313.           *bufp == pscr(curline, curoffset)->c && *bufp != '\0'){
  2314.         bufp++;
  2315.         if(++curoffset >= term.t_ncol)
  2316.           break;
  2317.         }
  2318.  
  2319.         if(*bufp != '\0'){            /* need to move? */
  2320.         movecursor(curline, curoffset);
  2321.         while(*bufp != '\0'){        /* display what's not there */
  2322.             pputc(*bufp++, 0);
  2323.             curoffset++;
  2324.         }
  2325.         }
  2326.  
  2327.         if(curoffset < term.t_ncol 
  2328.            && !is_blank(curline, curoffset, term.t_ncol - curoffset)){
  2329.              movecursor(curline, curoffset);
  2330.              peeol();
  2331.         }
  2332.         curline++;
  2333.  
  2334.             curoffset = 0;
  2335.         if(curline >= BOTTOM())
  2336.           break;
  2337.  
  2338.         lp = lp->next;
  2339.         }
  2340.  
  2341.     if(curline >= BOTTOM())
  2342.       return;                /* don't paint delimiter */
  2343.  
  2344.     while(headents[++e].name != NULL)
  2345.       if(headents[e].display_it){
  2346.           lp = headents[e].hd_text;
  2347.           break;
  2348.       }
  2349.     }
  2350.  
  2351.     display_delimiter(ComposerEditing ? 0 : 1);
  2352. }
  2353.  
  2354.  
  2355.  
  2356.  
  2357. /*
  2358.  * PaintBody() - generic call to handle repainting everything BUT the 
  2359.  *         header
  2360.  *
  2361.  *    notes:
  2362.  *        The header redrawing in a level 0 body paint gets done
  2363.  *        in update()
  2364.  */
  2365. PaintBody(level)
  2366. int    level;
  2367. {
  2368.     curwp->w_flag |= WFHARD;            /* make sure framing's right */
  2369.     if(level == 0)                /* specify what to update */
  2370.         sgarbf = TRUE;
  2371.  
  2372.     update();                    /* display message body */
  2373.  
  2374.     if(level == 0 && ComposerEditing){
  2375.     mlerase();                /* clear the error line */
  2376.     ShowPrompt();
  2377.     }
  2378. }
  2379.  
  2380.  
  2381. /*
  2382.  * display_for_send - paint the composer from the top line and return.
  2383.  */
  2384. void
  2385. display_for_send()
  2386. {
  2387.     int             i = 0;
  2388.     struct hdr_line *l;
  2389.  
  2390.     /* if first header line isn't displayed, there's work to do */
  2391.     if(headents && ((l = first_hline(&i)) != ods.top_l
  2392.             || ComposerTopLine == COMPOSER_TOP_LINE
  2393.             || !ods.p_line)){
  2394.     struct on_display orig_ods;
  2395.     int          orig_edit    = ComposerEditing,
  2396.               orig_ct_line = ComposerTopLine;
  2397.  
  2398.     /*
  2399.      * fake that the cursor's in the first header line
  2400.      * and force repaint...
  2401.      */
  2402.     orig_ods    = ods;
  2403.     ods.cur_e    = i;
  2404.     ods.top_l    = ods.cur_l = l;
  2405.     ods.top_e    = ods.cur_e;
  2406.     ods.p_line    = COMPOSER_TOP_LINE;
  2407.     ComposerEditing = TRUE;            /* to fool update() */
  2408.     setimark(FALSE, 1);            /* remember where we were */
  2409.     gotobob(FALSE, 1);
  2410.  
  2411.     UpdateHeader(0);            /* redraw whole enchilada */
  2412.     PaintHeader(COMPOSER_TOP_LINE, TRUE);
  2413.     PaintBody(0);
  2414.  
  2415.     ods = orig_ods;                /* restore original state */
  2416.     ComposerEditing = orig_edit;
  2417.     ComposerTopLine = curwp->w_toprow = orig_ct_line;
  2418.         curwp->w_ntrows = BOTTOM() - ComposerTopLine;
  2419.     swapimark(FALSE, 1);
  2420.  
  2421.     /* in case we don't exit, set up restoring the screen */
  2422.     sgarbf = TRUE;                /* force redraw */
  2423.     }
  2424. }
  2425.  
  2426.  
  2427. /*
  2428.  * ArrangeHeader - set up display parm such that header is reasonably 
  2429.  *                 displayed
  2430.  */
  2431. ArrangeHeader()
  2432. {
  2433.     int      e;
  2434.     register struct hdr_line *l;
  2435.  
  2436.     ods.p_line = ods.p_off = 0;
  2437.     e = ods.top_e = 0;
  2438.     l = ods.top_l = headents[e].hd_text;
  2439.     while(headents[e+1].name || (l && l->next))
  2440.       if(l = next_sel_hline(&e, l)){
  2441.       ods.cur_l = l;
  2442.       ods.cur_e = e;
  2443.       }
  2444.  
  2445.     UpdateHeader(1);
  2446. }
  2447.  
  2448.  
  2449. /*
  2450.  * ComposerHelp() - display mail help in a context sensitive way
  2451.  *                  based on the level passed ...
  2452.  */
  2453. ComposerHelp(level)
  2454. int    level;
  2455. {
  2456.     char buf[80];
  2457.     VARS_TO_SAVE *saved_state;
  2458.  
  2459.     curwp->w_flag |= WFMODE;
  2460.     sgarbf = TRUE;
  2461.  
  2462.     if(level < 0 || !headents[level].name){
  2463.     (*term.t_beep)();
  2464.     emlwrite("Sorry, I can't help you with that.", NULL);
  2465.     sleep(2);
  2466.     return(FALSE);
  2467.     }
  2468.  
  2469.     sprintf(buf, "Help for %s %.40s Field",
  2470.          (Pmaster->pine_flags & MDHDRONLY) ? "Address Book"
  2471.                          : "Composer",
  2472.          headents[level].name);
  2473.     saved_state = save_pico_state();
  2474.     (*Pmaster->helper)(headents[level].help, buf, 1);
  2475.     if(saved_state){
  2476.     restore_pico_state(saved_state);
  2477.     free_pico_state(saved_state);
  2478.     }
  2479.  
  2480.     ttresize();
  2481.     picosigs();                    /* restore altered handlers */
  2482.     return(TRUE);
  2483. }
  2484.  
  2485.  
  2486.  
  2487. /*
  2488.  * ToggleHeader() - set or unset pico values to the full screen size
  2489.  *                  painting header if need be.
  2490.  */
  2491. ToggleHeader(show)
  2492. int show;
  2493. {
  2494.     /*
  2495.      * check to see if we need to display the header... 
  2496.      */
  2497.     if(show){
  2498.     UpdateHeader(0);                /* figure bounds  */
  2499.     PaintHeader(COMPOSER_TOP_LINE, FALSE);    /* draw it */
  2500.     }
  2501.     else{
  2502.         /*
  2503.          * set bounds for no header display
  2504.          */
  2505.         curwp->w_toprow = ComposerTopLine = COMPOSER_TOP_LINE;
  2506.         curwp->w_ntrows = BOTTOM() - ComposerTopLine;
  2507.     }
  2508.     return(TRUE);
  2509. }
  2510.  
  2511.  
  2512.  
  2513. /*
  2514.  * HeaderLen() - return the length in lines of the exposed portion of the
  2515.  *               header
  2516.  */
  2517. HeaderLen()
  2518. {
  2519.     register struct hdr_line *lp;
  2520.     int      e;
  2521.     int      i;
  2522.     
  2523.     i = 1;
  2524.     lp = ods.top_l;
  2525.     e  = ods.top_e;
  2526.     while(lp != NULL){
  2527.     lp = next_hline(&e, lp);
  2528.     i++;
  2529.     }
  2530.     return(i);
  2531. }
  2532.  
  2533.  
  2534.  
  2535. /*
  2536.  * first_hline() - return a pointer to the first displayable header line
  2537.  * 
  2538.  *    returns:
  2539.  *        1) pointer to first displayable line in header and header
  2540.  *                 entry, via side effect, that the first line is a part of
  2541.  *              2) NULL if no next line, leaving entry at LASTHDR
  2542.  */
  2543. struct hdr_line *
  2544. first_hline(entry)
  2545.     int *entry;
  2546. {
  2547.     /* init *entry so we're sure to start from the top */
  2548.     for(*entry = 0; headents[*entry].name; (*entry)++)
  2549.       if(headents[*entry].display_it)
  2550.     return(headents[*entry].hd_text);
  2551.  
  2552.     *entry = 0;
  2553.     return(NULL);        /* this shouldn't happen */
  2554. }
  2555.  
  2556.  
  2557. /*
  2558.  * first_sel_hline() - return a pointer to the first selectable header line
  2559.  * 
  2560.  *    returns:
  2561.  *        1) pointer to first selectable line in header and header
  2562.  *                 entry, via side effect, that the first line is a part of
  2563.  *              2) NULL if no next line, leaving entry at LASTHDR
  2564.  */
  2565. struct hdr_line *
  2566. first_sel_hline(entry)
  2567.     int *entry;
  2568. {
  2569.     /* init *entry so we're sure to start from the top */
  2570.     for(*entry = 0; headents[*entry].name; (*entry)++)
  2571.       if(headents[*entry].display_it && !headents[*entry].blank)
  2572.     return(headents[*entry].hd_text);
  2573.  
  2574.     *entry = 0;
  2575.     return(NULL);        /* this shouldn't happen */
  2576. }
  2577.  
  2578.  
  2579.  
  2580. /*
  2581.  * next_hline() - return a pointer to the next line structure
  2582.  * 
  2583.  *    returns:
  2584.  *        1) pointer to next displayable line in header and header
  2585.  *                 entry, via side effect, that the next line is a part of
  2586.  *              2) NULL if no next line, leaving entry at LASTHDR
  2587.  */
  2588. struct hdr_line *
  2589. next_hline(entry, line)
  2590.     int *entry;
  2591.     struct hdr_line *line;
  2592. {
  2593.     if(line == NULL)
  2594.       return(NULL);
  2595.  
  2596.     if(line->next == NULL){
  2597.     while(headents[++(*entry)].name != NULL){
  2598.         if(headents[*entry].display_it)
  2599.           return(headents[*entry].hd_text);
  2600.     }
  2601.     --(*entry);
  2602.     return(NULL);
  2603.     }
  2604.     else
  2605.       return(line->next);
  2606. }
  2607.  
  2608.  
  2609. /*
  2610.  * next_sel_hline() - return a pointer to the next selectable line structure
  2611.  * 
  2612.  *    returns:
  2613.  *        1) pointer to next selectable line in header and header
  2614.  *                 entry, via side effect, that the next line is a part of
  2615.  *              2) NULL if no next line, leaving entry at LASTHDR
  2616.  */
  2617. struct hdr_line *
  2618. next_sel_hline(entry, line)
  2619.     int *entry;
  2620.     struct hdr_line *line;
  2621. {
  2622.     if(line == NULL)
  2623.       return(NULL);
  2624.  
  2625.     if(line->next == NULL){
  2626.     while(headents[++(*entry)].name != NULL){
  2627.         if(headents[*entry].display_it && !headents[*entry].blank)
  2628.           return(headents[*entry].hd_text);
  2629.     }
  2630.     --(*entry);
  2631.     return(NULL);
  2632.     }
  2633.     else
  2634.       return(line->next);
  2635. }
  2636.  
  2637.  
  2638.  
  2639. /*
  2640.  * prev_hline() - return a pointer to the next line structure back
  2641.  * 
  2642.  *    returns:
  2643.  *              1) pointer to previous displayable line in header and 
  2644.  *                 the header entry that the next line is a part of 
  2645.  *                 via side effect
  2646.  *              2) NULL if no next line, leaving entry unchanged from
  2647.  *                 the value it had on entry.
  2648.  */
  2649. struct hdr_line *
  2650. prev_hline(entry, line)
  2651.     int *entry;
  2652.     struct hdr_line *line;
  2653. {
  2654.     if(line == NULL)
  2655.       return(NULL);
  2656.  
  2657.     if(line->prev == NULL){
  2658.     int orig_entry;
  2659.  
  2660.     orig_entry = *entry;
  2661.     while(--(*entry) >= 0){
  2662.         if(headents[*entry].display_it){
  2663.         line = headents[*entry].hd_text;
  2664.         while(line->next != NULL)
  2665.           line = line->next;
  2666.         return(line);
  2667.         }
  2668.     }
  2669.  
  2670.     *entry = orig_entry;
  2671.     return(NULL);
  2672.     }
  2673.     else
  2674.       return(line->prev);
  2675. }
  2676.  
  2677.  
  2678. /*
  2679.  * prev_sel_hline() - return a pointer to the previous selectable line
  2680.  * 
  2681.  *    returns:
  2682.  *              1) pointer to previous selectable line in header and 
  2683.  *                 the header entry that the next line is a part of 
  2684.  *                 via side effect
  2685.  *              2) NULL if no next line, leaving entry unchanged from
  2686.  *                 the value it had on entry.
  2687.  */
  2688. struct hdr_line *
  2689. prev_sel_hline(entry, line)
  2690.     int *entry;
  2691.     struct hdr_line *line;
  2692. {
  2693.     if(line == NULL)
  2694.       return(NULL);
  2695.  
  2696.     if(line->prev == NULL){
  2697.     int orig_entry;
  2698.  
  2699.     orig_entry = *entry;
  2700.     while(--(*entry) >= 0){
  2701.         if(headents[*entry].display_it && !headents[*entry].blank){
  2702.         line = headents[*entry].hd_text;
  2703.         while(line->next != NULL)
  2704.           line = line->next;
  2705.         return(line);
  2706.         }
  2707.     }
  2708.  
  2709.     *entry = orig_entry;
  2710.     return(NULL);
  2711.     }
  2712.     else
  2713.       return(line->prev);
  2714. }
  2715.  
  2716.  
  2717.  
  2718. /*
  2719.  * first_requested_hline() - return pointer to first line that pico's caller
  2720.  *                 asked that we start on.
  2721.  */
  2722. struct hdr_line *
  2723. first_requested_hline(ent)
  2724.     int *ent;
  2725. {
  2726.     int             i, reqfield;
  2727.     struct hdr_line *rv = NULL;
  2728.  
  2729.     for(reqfield = -1, i = 0; headents[i].name;  i++)
  2730.       if(headents[i].start_here){
  2731.       headents[i].start_here = 0;        /* clear old setting */
  2732.       if(reqfield < 0){            /* if not already, set up */
  2733.           headents[i].display_it = 1;    /* make sure it's shown */
  2734.           *ent = reqfield = i;
  2735.           rv = headents[i].hd_text;
  2736.       }
  2737.       }
  2738.  
  2739.     return(rv);
  2740. }
  2741.  
  2742.  
  2743.  
  2744. /*
  2745.  * UpdateHeader() - determines the best range of lines to be displayed 
  2746.  *                  using the global ods value for the current line and the
  2747.  *            top line, also sets ComposerTopLine and pico limits
  2748.  *
  2749.  *    showtop -- Attempt to show all header lines if they'll fit.
  2750.  *                    
  2751.  *      notes:
  2752.  *            This is pretty ugly because it has to keep the current line
  2753.  *        on the screen in a reasonable location no matter what.
  2754.  *        There are also a couple of rules to follow:
  2755.  *                 1) follow paging conventions of pico (ie, half page 
  2756.  *              scroll)
  2757.  *                 2) if more than one page, always display last half when 
  2758.  *                    pline is toward the end of the header
  2759.  * 
  2760.  *      returns:
  2761.  *             TRUE  if anything changed (side effects: new p_line, top_l
  2762.  *             top_e, and pico parms)
  2763.  *             FALSE if nothing changed 
  2764.  *             
  2765.  */
  2766. UpdateHeader(showtop)
  2767.     int showtop;
  2768. {
  2769.     register struct    hdr_line    *lp;
  2770.     int         i, le;
  2771.     int      ret = FALSE;
  2772.     int      old_top = ComposerTopLine;
  2773.     int      old_p = ods.p_line;
  2774.  
  2775.     if(ods.p_line < COMPOSER_TOP_LINE || ods.p_line >= BOTTOM()){
  2776.     NewTop(showtop);            /* get new top_l */
  2777.     ret = TRUE;
  2778.     }
  2779.     else{                    /* make sure p_line's OK */
  2780.     i = COMPOSER_TOP_LINE;
  2781.     lp = ods.top_l;
  2782.     le = ods.top_e;
  2783.     while(lp != ods.cur_l){
  2784.         /*
  2785.          * this checks to make sure cur_l is below top_l and that
  2786.          * cur_l is on the screen...
  2787.          */
  2788.         if((lp = next_hline(&le, lp)) == NULL || ++i >= BOTTOM()){
  2789.         NewTop(0);
  2790.         ret = TRUE;
  2791.         break;
  2792.         }
  2793.     }
  2794.     }
  2795.  
  2796.     ods.p_line = COMPOSER_TOP_LINE;        /* find  p_line... */
  2797.     lp = ods.top_l;
  2798.     le = ods.top_e;
  2799.     while(lp && lp != ods.cur_l){
  2800.     lp = next_hline(&le, lp);
  2801.     ods.p_line++;
  2802.     }
  2803.  
  2804.     if(!ret)
  2805.       ret = !(ods.p_line == old_p);
  2806.  
  2807.     ComposerTopLine = ods.p_line;        /* figure top composer line */
  2808.     while(lp && ComposerTopLine < BOTTOM()){
  2809.     lp = next_hline(&le, lp);
  2810.     ComposerTopLine += (lp) ? 1 : 2;    /* allow for delim at end   */
  2811.     }
  2812.  
  2813.     if(!ret)
  2814.       ret = !(ComposerTopLine == old_top);
  2815.  
  2816.     if(wheadp->w_toprow != ComposerTopLine){    /* update pico params... */
  2817.         wheadp->w_toprow = ComposerTopLine;
  2818.         wheadp->w_ntrows = ((i = BOTTOM() - ComposerTopLine) > 0) ? i : 0;
  2819.     ret = TRUE;
  2820.     }
  2821.     return(ret);
  2822. }
  2823.  
  2824.  
  2825.  
  2826. /*
  2827.  * NewTop() - calculate a new top_l based on the cur_l
  2828.  *
  2829.  *    showtop -- Attempt to show all the header lines if they'll fit
  2830.  *
  2831.  *    returns:
  2832.  *        with ods.top_l and top_e pointing at a reasonable line
  2833.  *        entry
  2834.  */
  2835. NewTop(showtop)
  2836.     int showtop;
  2837. {
  2838.     register struct hdr_line *lp;
  2839.     register int i;
  2840.     int      e;
  2841.  
  2842.     lp = ods.cur_l;
  2843.     e  = ods.cur_e;
  2844.     i  = showtop ? FULL_SCR() : HALF_SCR();
  2845.  
  2846.     while(lp != NULL && i--){
  2847.     ods.top_l = lp;
  2848.     ods.top_e = e;
  2849.     lp = prev_hline(&e, lp);
  2850.     }
  2851. }
  2852.  
  2853.  
  2854.  
  2855. /*
  2856.  * display_delimiter() - just paint the header/message body delimiter with
  2857.  *                       inverse value specified by state.
  2858.  */
  2859. void
  2860. display_delimiter(state)
  2861. int    state;
  2862. {
  2863.     register char    *bufp;
  2864.  
  2865.     if(ComposerTopLine - 1 >= BOTTOM())        /* silently forget it */
  2866.       return;
  2867.  
  2868.     bufp = (gmode & MDHDRONLY) ? "" : HDR_DELIM;
  2869.  
  2870.     if(state == delim_ps){            /* optimize ? */
  2871.     for(delim_ps = 0; bufp[delim_ps] && pscr(ComposerTopLine-1,delim_ps) != NULL && pscr(ComposerTopLine-1,delim_ps)->c == bufp[delim_ps];delim_ps++)
  2872.       ;
  2873.  
  2874.     if(bufp[delim_ps] == '\0' && !(gmode & MDHDRONLY)){
  2875.         delim_ps = state;
  2876.         return;                /* already displayed! */
  2877.     }
  2878.     }
  2879.  
  2880.     delim_ps = state;
  2881.  
  2882.     movecursor(ComposerTopLine - 1, 0);
  2883.     if(state)
  2884.       (*term.t_rev)(1);
  2885.  
  2886.     while(*bufp != '\0')
  2887.       pputc(*bufp++, state ? 1 : 0);
  2888.  
  2889.     if(state)
  2890.       (*term.t_rev)(0);
  2891.  
  2892.     peeol();
  2893. }
  2894.  
  2895.  
  2896.  
  2897. /*
  2898.  * InvertPrompt() - invert the prompt associated with header entry to state
  2899.  *                  state (true if invert, false otherwise).
  2900.  *    returns:
  2901.  *        non-zero if nothing done
  2902.  *        0 if prompt inverted successfully
  2903.  *
  2904.  *    notes:
  2905.  *        come to think of it, this func and the one above could
  2906.  *        easily be combined
  2907.  */
  2908. InvertPrompt(entry, state)
  2909. int    entry, state;
  2910. {
  2911.     register char   *bufp;
  2912.     register int    i;
  2913.  
  2914.     bufp = headents[entry].prompt;        /* fresh prompt paint */
  2915.     if((i = entry_line(entry, FALSE)) == -1)
  2916.       return(-1);                /* silently forget it */
  2917.  
  2918.     if((invert_ps&(1<<entry)) == (state ? 1<<entry : 0)){    /* optimize ? */
  2919.     int j;
  2920.  
  2921.     for(j = 0; bufp[j] && pscr(i, j)->c == bufp[j]; j++)
  2922.       ;
  2923.  
  2924.     if(bufp[j] == '\0'){
  2925.         if(state)
  2926.           invert_ps |= 1<<entry;
  2927.         else
  2928.           invert_ps &= ~(1<<entry);
  2929.         return(0);                /* already displayed! */
  2930.     }
  2931.     }
  2932.  
  2933.     if(state)
  2934.       invert_ps |= 1<<entry;
  2935.     else
  2936.       invert_ps &= ~(1<<entry);
  2937.  
  2938.     movecursor(i, 0);
  2939.     if(state)
  2940.       (*term.t_rev)(1);
  2941.  
  2942.     while(*bufp && *(bufp + 1))
  2943.       pputc(*bufp++, 1);            /* putc upto last char */
  2944.  
  2945.     if(state)
  2946.       (*term.t_rev)(0);
  2947.  
  2948.     pputc(*bufp, 0);                /* last char not inverted */
  2949.     return(TRUE);
  2950. }
  2951.  
  2952.  
  2953.  
  2954.  
  2955. /*
  2956.  * partial_entries() - toggle display of the bcc and fcc fields.
  2957.  *
  2958.  *    returns:
  2959.  *        TRUE if there are partial entries on the display
  2960.  *        FALSE otherwise.
  2961.  */
  2962. partial_entries()
  2963. {
  2964.     register struct headerentry *h;
  2965.     int                          is_on;
  2966.   
  2967.     /*---- find out status of first rich header ---*/
  2968.     for(h = headents; !h->rich_header && h->name != NULL; h++)
  2969.       ;
  2970.  
  2971.     is_on = h->display_it;
  2972.     for(h = headents; h->name != NULL; h++) 
  2973.       if(h->rich_header) 
  2974.         h->display_it = ! is_on;
  2975.  
  2976.     return(is_on);
  2977. }
  2978.  
  2979.  
  2980.  
  2981. /*
  2982.  * entry_line() - return the physical line on the screen associated
  2983.  *                with the given header entry field.  Note: the field
  2984.  *                may span lines, so if the last char is set, return
  2985.  *                the appropriate value.
  2986.  *
  2987.  *    returns:
  2988.  *             1) physical line number of entry
  2989.  *             2) -1 if entry currently not on display
  2990.  */
  2991. entry_line(entry, lastchar)
  2992. int    entry, lastchar;
  2993. {
  2994.     register int    p_line = COMPOSER_TOP_LINE;
  2995.     int    i;
  2996.     register struct hdr_line    *line;
  2997.  
  2998.     for(line = ods.top_l, i = ods.top_e;
  2999.     headents && headents[i].name && i <= entry;
  3000.     p_line++){
  3001.     if(p_line >= BOTTOM())
  3002.       break;
  3003.     if(i == entry){
  3004.         if(lastchar){
  3005.         if(line->next == NULL)
  3006.           return(p_line);
  3007.         }
  3008.         else if(line->prev == NULL)
  3009.           return(p_line);
  3010.         else
  3011.           return(-1);
  3012.     }
  3013.     line = next_hline(&i, line);
  3014.     }
  3015.     return(-1);
  3016. }
  3017.  
  3018.  
  3019.  
  3020. /*
  3021.  * physical_line() - return the physical line on the screen associated
  3022.  *                   with the given header line pointer.
  3023.  *
  3024.  *    returns:
  3025.  *             1) physical line number of entry
  3026.  *             2) -1 if entry currently not on display
  3027.  */
  3028. physical_line(l)
  3029. struct hdr_line *l;
  3030. {
  3031.     register int    p_line = COMPOSER_TOP_LINE;
  3032.     register struct hdr_line    *lp;
  3033.     int    i;
  3034.  
  3035.     for(lp=ods.top_l, i=ods.top_e; headents[i].name && lp != NULL; p_line++){
  3036.     if(p_line >= BOTTOM())
  3037.       break;
  3038.  
  3039.     if(lp == l)
  3040.       return(p_line);
  3041.  
  3042.     lp = next_hline(&i, lp);
  3043.     }
  3044.     return(-1);
  3045. }
  3046.  
  3047.  
  3048.  
  3049. /*
  3050.  * call_builder() - resolve any nicknames in the address book associated
  3051.  *                  with the given entry...
  3052.  *
  3053.  *    NOTES:
  3054.  * 
  3055.  *      BEWARE: this function can cause cur_l and top_l to get lost so BE 
  3056.  *              CAREFUL before and after you call this function!!!
  3057.  * 
  3058.  *      There could to be something here to resolve cur_l and top_l
  3059.  *      reasonably into the new linked list for this entry.  
  3060.  *
  3061.  *      The reason this would mostly work without it is resolve_niks gets
  3062.  *      called for the most part in between fields.  Since we're moving
  3063.  *      to the beginning or end (i.e. the next/prev pointer in the old 
  3064.  *      freed cur_l is NULL) of the next entry, we get a new cur_l
  3065.  *      pointing at a good line.  Then since top_l is based on cur_l in
  3066.  *      NewTop() we have pretty much lucked out.
  3067.  * 
  3068.  *      Where we could get burned is in a canceled exit (ctrl|x).  Here
  3069.  *      nicknames get resolved into addresses, which invalidates cur_l
  3070.  *      and top_l.  Since we don't actually leave, we could begin editing
  3071.  *      again with bad pointers.  This would usually results in a nice 
  3072.  *      core dump.
  3073.  *
  3074.  *      NOTE: The mangled argument is a little strange. It's used on both
  3075.  *      input and output. On input, if it is not set, then that tells the
  3076.  *      builder not to do anything that might take a long time, like a
  3077.  *      white pages lookup. On return, it tells the caller that the screen
  3078.  *      and signals may have been mangled so signals should be reset, window
  3079.  *      resized, and screen redrawn.
  3080.  *
  3081.  *    RETURNS:
  3082.  *              > 0 if any names where resolved, otherwise
  3083.  *                0 if not, or
  3084.  *        < 0 on error
  3085.  *                -1: move to next line
  3086.  *                -2: don't move off this line
  3087.  */
  3088. call_builder(entry, mangled, err)
  3089. struct headerentry *entry;
  3090. int                *mangled;
  3091. char              **err;
  3092. {
  3093.     register    int     retval = 0;
  3094.     register    int    i;
  3095.     register    struct  hdr_line  *line;
  3096.     char    *sbuf;
  3097.     char    *s = NULL, *fcc = NULL;
  3098.     struct headerentry *e;
  3099.     BUILDER_ARG *nextarg, *arg = NULL, *headarg = NULL;
  3100.     VARS_TO_SAVE *saved_state;
  3101.  
  3102.     if(!entry->builder)
  3103.       return(0);
  3104.  
  3105.     line = entry->hd_text;
  3106.     i = 0;
  3107.     while(line != NULL){
  3108.     i += term.t_ncol;
  3109.         line = line->next;
  3110.     }
  3111.     
  3112.     if((sbuf=(char *)malloc((unsigned) i)) == NULL){
  3113.     emlwrite("Can't malloc space to expand address", NULL);
  3114.     return(-1);
  3115.     }
  3116.     
  3117.     *sbuf = '\0';
  3118.     /*
  3119.      * cat the whole entry into one string...
  3120.      */
  3121.     line = entry->hd_text;
  3122.     while(line != NULL){
  3123.     i = strlen(line->text);
  3124.     /*
  3125.      * To keep pine address builder happy, addresses should be separated
  3126.      * by ", ".  Add this space if needed, otherwise...
  3127.      * (This is some ancient requirement that is no longer needed.)
  3128.      *
  3129.      * If this line is NOT a continuation of the previous line, add
  3130.      * white space for pine's address builder if its not already there...
  3131.      * (This is some ancient requirement that is no longer needed.)
  3132.      *
  3133.      * Also if it's not a continuation (i.e., there's already and addr on 
  3134.      * the line), and there's another line below, treat the new line as
  3135.      * an implied comma.
  3136.      * (This should only be done for address-type lines, not for regular
  3137.      * text lines like subjects. Key off of the break_on_comma bit which
  3138.      * should only be set on those that won't mind a comma being added.)
  3139.      */
  3140.     if(entry->break_on_comma){
  3141.         if(i && line->text[i-1] == ',')
  3142.           strcat(line->text, " ");        /* help address builder */
  3143.         else if(line->next != NULL && !strend(line->text, ',')){
  3144.         if(strqchr(line->text, ',', NULL, -1))
  3145.           strcat(line->text, ", ");        /* implied comma */
  3146.         }
  3147.         else if(line->prev != NULL && line->next != NULL){
  3148.         if(strchr(line->prev->text, ' ') != NULL 
  3149.            && line->text[i-1] != ' ')
  3150.           strcat(line->text, " ");
  3151.         }
  3152.     }
  3153.  
  3154.     strcat(sbuf, line->text);
  3155.         line = line->next;
  3156.     }
  3157.  
  3158.     if(entry->affected_entry){
  3159.     /* check if any non-sticky affected entries */
  3160.     for(e = entry->affected_entry; e; e = e->next_affected)
  3161.       if(!e->sticky)
  3162.         break;
  3163.  
  3164.     /* there is at least one non-sticky so make a list to pass */
  3165.     if(e){
  3166.         for(e = entry->affected_entry; e; e = e->next_affected){
  3167.         if(!arg){
  3168.             headarg = arg = (BUILDER_ARG *)malloc(sizeof(BUILDER_ARG));
  3169.             if(!arg){
  3170.             emlwrite("Can't malloc space for fcc", NULL);
  3171.             return(-1);
  3172.             }
  3173.             else{
  3174.             arg->next = NULL;
  3175.             arg->tptr = NULL;
  3176.             arg->aff  = &(e->bldr_private);
  3177.             arg->me   = &(entry->bldr_private);
  3178.             }
  3179.         }
  3180.         else{
  3181.             nextarg = (BUILDER_ARG *)malloc(sizeof(BUILDER_ARG));
  3182.             if(!nextarg){
  3183.             emlwrite("Can't malloc space for fcc", NULL);
  3184.             return(-1);
  3185.             }
  3186.             else{
  3187.             nextarg->next = NULL;
  3188.             nextarg->tptr = NULL;
  3189.             nextarg->aff  = &(e->bldr_private);
  3190.             nextarg->me   = &(entry->bldr_private);
  3191.             arg->next     = nextarg;
  3192.             arg           = arg->next;
  3193.             }
  3194.         }
  3195.  
  3196.         if(!e->sticky){
  3197.             line = e->hd_text;
  3198.             if(!(arg->tptr=(char *)malloc(strlen(line->text) + 1))){
  3199.             emlwrite("Can't malloc space for fcc", NULL);
  3200.             return(-1);
  3201.             }
  3202.             else
  3203.               strcpy(arg->tptr, line->text);
  3204.         }
  3205.         }
  3206.     }
  3207.     }
  3208.  
  3209.     /*
  3210.      * Even if there are no affected entries, we still need the arg
  3211.      * to pass the "me" pointer.
  3212.      */
  3213.     if(!headarg){
  3214.     headarg = (BUILDER_ARG *)malloc(sizeof(BUILDER_ARG));
  3215.     if(!headarg){
  3216.         emlwrite("Can't malloc space", NULL);
  3217.         return(-1);
  3218.     }
  3219.     else{
  3220.         headarg->next = NULL;
  3221.         headarg->tptr = NULL;
  3222.         headarg->aff  = NULL;
  3223.         headarg->me   = &(entry->bldr_private);
  3224.     }
  3225.     }
  3226.  
  3227.     /*
  3228.      * The builder may make a new call back to pico() so we save and
  3229.      * restore the pico state.
  3230.      */
  3231.     saved_state = save_pico_state();
  3232.     retval = (*entry->builder)(sbuf, &s, err, headarg, mangled);
  3233.     if(saved_state){
  3234.     restore_pico_state(saved_state);
  3235.     free_pico_state(saved_state);
  3236.     }
  3237.  
  3238.     if(mangled && *mangled & BUILDER_MESSAGE_DISPLAYED){
  3239.     *mangled &= ~ BUILDER_MESSAGE_DISPLAYED;
  3240.     if(mpresf == FALSE)
  3241.       mpresf = TRUE;
  3242.     }
  3243.  
  3244.     if(retval >= 0){
  3245.     if(strcmp(sbuf, s)){
  3246.         line = entry->hd_text;
  3247.         InitEntryText(s, entry);        /* arrange new one */
  3248.         zotentry(line);             /* blast old list o'entries */
  3249.         retval = 1;
  3250.     }
  3251.  
  3252.     for(e = entry->affected_entry, arg = headarg;
  3253.         e;
  3254.         e = e->next_affected, arg = arg ? arg->next : NULL){
  3255.         if(!e->sticky){
  3256.         line = e->hd_text;
  3257.         if(strcmp(line->text, arg ? arg->tptr : "")){ /* it changed */
  3258.             /* make sure they see it if changed */
  3259.             e->display_it = 1;
  3260.             InitEntryText(arg ? arg->tptr : "", e);
  3261.             if(line == ods.top_l)
  3262.               ods.top_l = e->hd_text;
  3263.  
  3264.             zotentry(line);    /* blast old list o'entries */
  3265.             e->dirty = 1;    /* mark it dirty */
  3266.             retval = 1;
  3267.         }
  3268.         }
  3269.     }
  3270.     }
  3271.  
  3272.     if(s)
  3273.       free(s);
  3274.  
  3275.     for(arg = headarg; arg; arg = nextarg){
  3276.     /* Don't free xtra or me, they just point to headerentry data */
  3277.     nextarg = arg->next;
  3278.     if(arg->tptr)
  3279.       free(arg->tptr);
  3280.     
  3281.     free(arg);
  3282.     }
  3283.  
  3284.     free(sbuf);
  3285.     return(retval);
  3286. }
  3287.  
  3288.  
  3289. void
  3290. call_expander()
  3291. {
  3292.     char **s = NULL;
  3293.  
  3294.     if(!Pmaster->expander)
  3295.       return;
  3296.  
  3297.     if((*Pmaster->expander)(headents, &s) > 0 && s){
  3298.     char               *tbuf;
  3299.     int                 i, biggest = 100;
  3300.     struct headerentry *e;
  3301.  
  3302.     /*
  3303.      * Use tbuf to cat together multiple line entries before comparing.
  3304.      */
  3305.     tbuf = (char *)malloc(biggest + 1);
  3306.     for(e = headents, i=0; e->name != NULL; e++,i++){
  3307.         int sz = 0;
  3308.         struct hdr_line *line;
  3309.  
  3310.         while(e->name && e->blank)
  3311.           e++;
  3312.         
  3313.         if(e->name == NULL)
  3314.           continue;
  3315.  
  3316.         for(line = e->hd_text; line != NULL; line = line->next)
  3317.           sz += strlen(line->text);
  3318.         
  3319.         if(sz > biggest){
  3320.         biggest = sz;
  3321.         free(tbuf);
  3322.         tbuf = (char *)malloc(biggest + 1);
  3323.         }
  3324.  
  3325.         tbuf[0] = '\0';
  3326.         for(line = e->hd_text; line != NULL; line = line->next)
  3327.           strcat(tbuf, line->text);
  3328.           
  3329.         if(strcmp(tbuf, s[i])){ /* it changed */
  3330.         struct hdr_line *zline;
  3331.  
  3332.         line = zline = e->hd_text;
  3333.         InitEntryText(s[i], e);
  3334.         /*
  3335.          * If any of the lines for this entry are current or
  3336.          * top, fix that.
  3337.          */
  3338.         for(; line != NULL; line = line->next){
  3339.             if(line == ods.top_l)
  3340.               ods.top_l = e->hd_text;
  3341.  
  3342.             if(line == ods.cur_l)
  3343.               ods.cur_l = e->hd_text;
  3344.         }
  3345.  
  3346.         zotentry(zline);    /* blast old list o'entries */
  3347.         }
  3348.     }
  3349.  
  3350.     free(tbuf);
  3351.     }
  3352.  
  3353.     if(s){
  3354.     char **p;
  3355.  
  3356.     for(p = s; *p; p++)
  3357.       free(*p);
  3358.  
  3359.     free(s);
  3360.     }
  3361.  
  3362.     return;
  3363. }
  3364.  
  3365.  
  3366. /*
  3367.  * strend - neglecting white space, returns TRUE if c is at the
  3368.  *          end of the given line.  otherwise FALSE.
  3369.  */
  3370. strend(s, ch)
  3371. char *s;
  3372. int   ch;
  3373. {
  3374.     register char *b;
  3375.     register char  c;
  3376.  
  3377.     c = (char)ch;
  3378.  
  3379.     if(s == NULL)
  3380.       return(FALSE);
  3381.  
  3382.     if(*s == '\0')
  3383.       return(FALSE);
  3384.  
  3385.     b = &s[strlen(s)];
  3386.     while(isspace((unsigned char)(*--b))){
  3387.     if(b == s)
  3388.       return(FALSE);
  3389.     }
  3390.  
  3391.     return(*b == c);
  3392. }
  3393.  
  3394.  
  3395. /*
  3396.  * strqchr - returns pointer to first non-quote-enclosed occurance of ch in 
  3397.  *           the given string.  otherwise NULL.
  3398.  *      s -- the string
  3399.  *     ch -- the character we're looking for
  3400.  *      q -- q tells us if we start out inside quotes on entry and is set
  3401.  *           correctly on exit.
  3402.  *      m -- max characters we'll check for ch (set to -1 for no check)
  3403.  */
  3404. char *
  3405. strqchr(s, ch, q, m)
  3406.     char *s;
  3407.     int   ch;
  3408.     int  *q;
  3409.     int   m;
  3410. {
  3411.     int     quoted = (q) ? *q : 0;
  3412.  
  3413.     for(; s && *s && m != 0; s++, m--){
  3414.     if(*s == '"'){
  3415.         quoted = !quoted;
  3416.         if(q)
  3417.           *q = quoted;
  3418.     }
  3419.  
  3420.     if(!quoted && *s == ch)
  3421.       return(s);
  3422.     }
  3423.  
  3424.     return(NULL);
  3425. }
  3426.  
  3427.  
  3428. /*
  3429.  * KillHeaderLine() - kill a line in the header
  3430.  *
  3431.  *    notes:
  3432.  *        This is pretty simple.  Just using the emacs kill buffer
  3433.  *        and its accompanying functions to cut the text from lines.
  3434.  *
  3435.  *    returns:
  3436.  *        TRUE if hldelete worked
  3437.  *        FALSE otherwise
  3438.  */
  3439. KillHeaderLine(l, append)
  3440. struct    hdr_line    *l;
  3441. int     append;
  3442. {
  3443.     register char    *c;
  3444.     int i = ods.p_off;
  3445.     int nl = TRUE;
  3446.  
  3447.     if(!append)
  3448.     kdelete();
  3449.  
  3450.     c = l->text;
  3451.     if (gmode & MDDTKILL){
  3452.     if (c[i] == '\0')  /* don't insert a new line after this line*/
  3453.       nl = FALSE;
  3454.         /*put to be deleted part into kill buffer */
  3455.     for (i=ods.p_off; c[i] != '\0'; i++) 
  3456.       kinsert(c[i]);                       
  3457.     }else{
  3458.     while(*c != '\0')                /* splat out the line */
  3459.       kinsert(*c++);
  3460.     }
  3461.  
  3462.     if (nl)
  3463.         kinsert('\n');                /* helpful to yank in body */
  3464.  
  3465. #ifdef _WINDOWS
  3466.     mswin_killbuftoclip (kremove);
  3467. #endif
  3468.  
  3469.     if (gmode & MDDTKILL){
  3470.     if (l->text[0]=='\0'){
  3471.  
  3472.        if(l->next && l->prev)
  3473.           ods.cur_l = next_hline(&ods.cur_e, l);
  3474.        else if(l->prev)
  3475.           ods.cur_l = prev_hline(&ods.cur_e, l);
  3476.  
  3477.        if(l == ods.top_l)
  3478.           ods.top_l = ods.cur_l;
  3479.  
  3480.        return(hldelete(l));
  3481.     }
  3482.     else {
  3483.       l->text[ods.p_off]='\0';    /* delete part of the line from the cursor */
  3484.       return(TRUE);
  3485.     }
  3486.     }else{
  3487.     if(l->next && l->prev)
  3488.        ods.cur_l = next_hline(&ods.cur_e, l);
  3489.     else if(l->prev)
  3490.        ods.cur_l = prev_hline(&ods.cur_e, l);
  3491.  
  3492.     if(l == ods.top_l)
  3493.        ods.top_l = ods.cur_l;
  3494.  
  3495.         return(hldelete(l));            /* blast it  */
  3496.     }
  3497. }
  3498.  
  3499.  
  3500.  
  3501. /*
  3502.  * SaveHeaderLines() - insert the saved lines in the list before the 
  3503.  *                     current line in the header
  3504.  *
  3505.  *    notes:
  3506.  *        Once again, just using emacs' kill buffer and its 
  3507.  *              functions.
  3508.  *
  3509.  *    returns:
  3510.  *        TRUE if something good happend
  3511.  *        FALSE otherwise
  3512.  */
  3513. SaveHeaderLines()
  3514. {
  3515.     char     *buf;                /* malloc'd copy of buffer */
  3516.     register char       *bp;            /* pointer to above buffer */
  3517.     register unsigned    i;            /* index */
  3518.     char     *work_buf, *work_buf_begin;
  3519.     char     empty[1];
  3520.     int      len, buf_len, work_buf_len;
  3521.     struct hdr_line *travel;
  3522.  
  3523.     if(ksize()){
  3524.     if((bp = buf = (char *)malloc(ksize()+5)) == NULL){
  3525.         emlwrite("Can't malloc space for saved text", NULL);
  3526.         return(FALSE);
  3527.     }
  3528.     }
  3529.     else
  3530.       return(FALSE);
  3531.  
  3532.     for(i=0; i < ksize(); i++)
  3533.       if(kremove(i) != '\n')            /* filter out newlines */
  3534.     *bp++ = kremove(i);
  3535.     *bp = '\0';
  3536.  
  3537.     while(--bp >= buf)                /* kill trailing white space */
  3538.       if(*bp != ' '){
  3539.       if(ods.cur_l->text[0] != '\0'){
  3540.           if(*bp == '>'){            /* inserting an address */
  3541.           *++bp = ',';            /* so add separator */
  3542.           *++bp = '\0';
  3543.           }
  3544.       }
  3545.       else{                    /* nothing in field yet */
  3546.           if(*bp == ','){            /* so blast any extra */
  3547.           *bp = '\0';            /* separators */
  3548.           }
  3549.       }
  3550.       break;
  3551.       }
  3552.  
  3553.     if (gmode & MDDTKILL){
  3554.     /* insert new text at the dot position */
  3555.     buf_len      = strlen(buf);
  3556.     work_buf_len = strlen(ods.cur_l->text) + buf_len;
  3557.     work_buf = (char *) malloc((work_buf_len + 1) * sizeof(char));
  3558.     if (work_buf == NULL) {
  3559.         emlwrite("Can't malloc space for saved text", NULL);
  3560.         return(FALSE);
  3561.     }
  3562.  
  3563.     sprintf(work_buf_begin = work_buf, "%.*s%s%s", ods.p_off,
  3564.         ods.cur_l->text, buf, &ods.cur_l->text[ods.p_off]);
  3565.     empty[0]='\0';
  3566.     ods.p_off = 0;
  3567.  
  3568.     /* insert text in 256-byte chuks */
  3569.     while(work_buf_len + ods.p_off > 256) {
  3570.         strncpy(&ods.cur_l->text[ods.p_off], work_buf, 256-ods.p_off);
  3571.         work_buf += (256 - ods.p_off);
  3572.         work_buf_len -= (256 - ods.p_off);
  3573.  
  3574.         if(FormatLines(ods.cur_l, empty, LINELEN(),
  3575.                headents[ods.cur_e].break_on_comma, 0) == -1) {
  3576.            i = FALSE;
  3577.            break;
  3578.         } else {
  3579.            i = TRUE;
  3580.               /* calculate dot's position after insertion */
  3581.           len = 0;
  3582.           travel = ods.cur_l;
  3583.           while (len < 256){
  3584.           len += strlen(travel->text);
  3585.           if (len >= 256){
  3586.             break;
  3587.           }
  3588.           travel = travel->next;
  3589.           }
  3590.           ods.cur_l = travel;
  3591.           ods.p_off = strlen(travel->text) - len + 256;
  3592.         }
  3593.     }
  3594.  
  3595.     /* insert the remainder of text */
  3596.     if (i != FALSE && work_buf_len > 0) {
  3597.         strcpy(&ods.cur_l->text[ods.p_off], work_buf);
  3598.         work_buf = work_buf_begin;
  3599.         free(work_buf);
  3600.  
  3601.         if(FormatLines(ods.cur_l, empty, LINELEN(),
  3602.                headents[ods.cur_e].break_on_comma, 0) == -1) {
  3603.            i = FALSE;
  3604.         } else {  
  3605.               /* calculate dot's position after insertion */
  3606.           len = 0;
  3607.           travel = ods.cur_l;
  3608.           while (len < work_buf_len + ods.p_off){
  3609.           len += strlen(travel->text);
  3610.           if (len >= work_buf_len + ods.p_off)
  3611.             break;
  3612.           travel = travel->next;
  3613.           }
  3614.           ods.cur_l = travel;
  3615.           ods.p_off = strlen(travel->text) - len + work_buf_len + ods.p_off;
  3616.         }
  3617.     }
  3618.  
  3619.     }else{
  3620.     if(FormatLines(ods.cur_l, buf, LINELEN(),
  3621.            headents[ods.cur_e].break_on_comma, 0) == -1)
  3622.       i = FALSE;
  3623.     else
  3624.       i = TRUE;
  3625.     }
  3626.  
  3627.     free(buf);
  3628.     return(i);
  3629. }
  3630.  
  3631.  
  3632.  
  3633.  
  3634. /*
  3635.  * break_point - Break the given line s at the most reasonable character c
  3636.  *               within l max characters.
  3637.  *
  3638.  *    returns:
  3639.  *        Pointer to the best break point in s, or
  3640.  *        Pointer to the beginning of s if no break point found
  3641.  */
  3642. char *
  3643. break_point(s, l, ch, q)
  3644.     char *s;
  3645.     int   l, ch, *q;
  3646. {
  3647.     register char *b = s + l;
  3648.     int            quoted = (q) ? *q : 0;
  3649.  
  3650.     while(b != s){
  3651.     if(ch == ',' && *b == '"')        /* don't break on quoted ',' */
  3652.       quoted = !quoted;            /* toggle quoted state */
  3653.  
  3654.     if(*b == ch){
  3655.         if(ch == ' '){
  3656.         if(b + 1 < s + l){
  3657.             b++;            /* leave the ' ' */
  3658.             break;
  3659.         }
  3660.         }
  3661.         else{
  3662.         /*
  3663.          * if break char isn't a space, leave a space after
  3664.          * the break char.
  3665.          */
  3666.         if(!(b+1 >= s+l || (b[1] == ' ' && b+2 == s+l))){
  3667.             b += (b[1] == ' ') ? 2 : 1;
  3668.             break;
  3669.         }
  3670.         }
  3671.     }
  3672.     b--;
  3673.     }
  3674.  
  3675.     if(q)
  3676.       *q = quoted;
  3677.  
  3678.     return((quoted) ? s : b);
  3679. }
  3680.  
  3681.  
  3682.  
  3683.  
  3684. /*
  3685.  * hldelete() - remove the header line pointed to by l from the linked list
  3686.  *              of lines.
  3687.  *
  3688.  *    notes:
  3689.  *        the case of first line in field is kind of bogus.  since
  3690.  *              the array of headers has a pointer to the first line, and 
  3691.  *        i don't want to worry about this too much, i just copied 
  3692.  *        the line below and removed it rather than the first one
  3693.  *        from the list.
  3694.  *
  3695.  *    returns:
  3696.  *        TRUE if it worked 
  3697.  *        FALSE otherwise
  3698.  */
  3699. hldelete(l)
  3700. struct hdr_line  *l;
  3701. {
  3702.     register struct hdr_line *lp;
  3703.  
  3704.     if(l == NULL)
  3705.       return(FALSE);
  3706.  
  3707.     if(l->next == NULL && l->prev == NULL){    /* only one line in field */
  3708.     l->text[0] = '\0';
  3709.     return(TRUE);                /* no free only line in list */
  3710.     }
  3711.     else if(l->next == NULL){            /* last line in field */
  3712.     l->prev->next = NULL;
  3713.     }
  3714.     else if(l->prev == NULL){            /* first line in field */
  3715.     strcpy(l->text, l->next->text);
  3716.     lp = l->next;
  3717.     if((l->next = lp->next) != NULL)
  3718.       l->next->prev = l;
  3719.     l = lp;
  3720.     }
  3721.     else{                    /* some where in field */
  3722.     l->prev->next = l->next;
  3723.     l->next->prev = l->prev;
  3724.     }
  3725.  
  3726.     l->next = NULL;
  3727.     l->prev = NULL;
  3728.     free((char *)l);
  3729.     return(TRUE);
  3730. }
  3731.  
  3732.  
  3733.  
  3734. /*
  3735.  * is_blank - returns true if the next n chars from coordinates row, col
  3736.  *           on display are spaces
  3737.  */
  3738. is_blank(row, col, n)
  3739. int row, col, n;
  3740. {
  3741.     n += col;
  3742.     for( ;col < n; col++){
  3743.         if(pscr(row, col) == NULL || pscr(row, col)->c != ' ')
  3744.       return(0);
  3745.     }
  3746.     return(1);
  3747. }
  3748.  
  3749.  
  3750. /*
  3751.  * ShowPrompt - display key help corresponding to the current header entry
  3752.  */
  3753. ShowPrompt()
  3754. {
  3755.     int new_e = ods.cur_e;
  3756.  
  3757.     if(headents[ods.cur_e].key_label){
  3758.     menu_header[TO_KEY].name  = "^T";
  3759.     menu_header[TO_KEY].label = headents[ods.cur_e].key_label;
  3760.     KS_OSDATASET(&menu_header[TO_KEY], KS_OSDATAGET(&headents[ods.cur_e]));
  3761.     }
  3762.     else
  3763.       menu_header[TO_KEY].name  = NULL;
  3764.     
  3765.     if(Pmaster && Pmaster->exit_label)
  3766.       menu_header[SEND_KEY].label = Pmaster->exit_label;
  3767.     else if(gmode & (MDVIEW | MDHDRONLY))
  3768.       menu_header[SEND_KEY].label =  (gmode & MDHDRONLY) ? "eXit/Save" : "eXit";
  3769.     else
  3770.       menu_header[SEND_KEY].label = "Send";
  3771.  
  3772.     if(gmode & MDVIEW){
  3773.     menu_header[CUT_KEY].name  = NULL;
  3774.     menu_header[DEL_KEY].name  = NULL;
  3775.     menu_header[UDEL_KEY].name = NULL;
  3776.     }
  3777.     else{
  3778.     menu_header[CUT_KEY].name  = "^K";
  3779.     menu_header[DEL_KEY].name  = "^D";
  3780.     menu_header[UDEL_KEY].name = "^U";
  3781.     }
  3782.  
  3783.     if(Pmaster->ctrlr_label){
  3784.     menu_header[RICH_KEY].label = Pmaster->ctrlr_label;
  3785.     menu_header[RICH_KEY].name = "^R";
  3786.     }
  3787.     else if(gmode & MDHDRONLY){
  3788.     menu_header[RICH_KEY].name  = NULL;
  3789.     }
  3790.     else{
  3791.     menu_header[RICH_KEY].label = "Rich Hdr";
  3792.     menu_header[RICH_KEY].name  = "^R";
  3793.     }
  3794.  
  3795.     if(gmode & MDHDRONLY){
  3796.     if(headents[ods.cur_e].fileedit){
  3797.         menu_header[PONE_KEY].name  = "^_";
  3798.         menu_header[PONE_KEY].label   = "Edit File";
  3799.     }
  3800.     else
  3801.       menu_header[PONE_KEY].name  = NULL;
  3802.  
  3803.     menu_header[ATT_KEY].name   = NULL;
  3804.     }
  3805.     else{
  3806.     menu_header[PONE_KEY].name  = "^O";
  3807.     menu_header[PONE_KEY].label = "Postpone";
  3808.     KS_OSDATASET(&menu_header[PONE_KEY],KS_OSDATAGET(&headents[ods.cur_e]));
  3809.  
  3810.     menu_header[ATT_KEY].name   = "^J";
  3811.     }
  3812.  
  3813.     wkeyhelp(menu_header);
  3814. }
  3815.  
  3816.  
  3817. /*
  3818.  * packheader - packup all of the header fields for return to caller. 
  3819.  *              NOTE: all of the header info passed in, including address
  3820.  *                    of the pointer to each string is contained in the
  3821.  *                    header entry array "headents".
  3822.  */
  3823. packheader()
  3824. {
  3825.     register int    i = 0;        /* array index */
  3826.     register int    count;        /* count of chars in a field */
  3827.     register int    retval = TRUE;    /* count of chars in a field */
  3828.     register char    *bufp;        /* */
  3829.     register struct    hdr_line *line;
  3830.  
  3831.     if(!headents)
  3832.       return(TRUE);
  3833.  
  3834.     while(headents[i].name != NULL){
  3835. #ifdef    ATTACHMENTS
  3836.     /*
  3837.      * attachments are special case, already in struct we pass back
  3838.      */
  3839.     if(headents[i].is_attach){
  3840.         i++;
  3841.         continue;
  3842.     }
  3843. #endif
  3844.  
  3845.     if(headents[i].blank){
  3846.         i++;
  3847.         continue;
  3848.     }
  3849.  
  3850.         /*
  3851.          * count chars to see if we need a new malloc'd space for our
  3852.          * array.
  3853.          */
  3854.         line = headents[i].hd_text;
  3855.         count = 0;
  3856.         while(line != NULL){
  3857.             /*
  3858.              * add one for possible concatination of a ' ' character ...
  3859.              */
  3860.             count += (strlen(line->text) + 1);
  3861.             line = line->next;
  3862.         }
  3863.  
  3864.         line = headents[i].hd_text;
  3865.         if(count < headents[i].maxlen){        
  3866.             *headents[i].realaddr[0] = '\0';
  3867.         }
  3868.         else{
  3869.             /*
  3870.              * don't forget to include space for the null terminator!!!!
  3871.              */
  3872.             if((bufp = (char *)malloc((count+1) * sizeof(char))) != NULL){
  3873.                 *bufp = '\0';
  3874.  
  3875.                 free(*headents[i].realaddr);
  3876.                 *headents[i].realaddr = bufp;
  3877.             }
  3878.             else{
  3879.                 emlwrite("Can't make room to pack header field.", NULL);
  3880.                 retval = FALSE;
  3881.             }
  3882.         }
  3883.  
  3884.         if(retval != FALSE){
  3885.         while(line != NULL){
  3886.         /* pass the cursor offset back in Pmaster struct */
  3887.         if(headents[i].start_here && ods.cur_l == line && Pmaster)
  3888.           Pmaster->edit_offset += strlen(*headents[i].realaddr);
  3889.  
  3890.                 strcat(*headents[i].realaddr, line->text);
  3891.         if(line->text[0] && line->text[strlen(line->text)-1] == ',')
  3892.           strcat(*headents[i].realaddr, " ");
  3893.  
  3894.                 line = line->next;
  3895.             }
  3896.         }
  3897.  
  3898.         i++;
  3899.     }
  3900.     return(retval);    
  3901. }
  3902.  
  3903.  
  3904.  
  3905. /*
  3906.  * zotheader - free all malloc'd lines associated with the header structs
  3907.  */
  3908. zotheader()
  3909. {
  3910.     register struct headerentry *i;
  3911.   
  3912.     for(i = headents; headents && i->name; i++)
  3913.       zotentry(i->hd_text);
  3914. }
  3915.  
  3916.  
  3917. /*
  3918.  * zotentry - free malloc'd space associated with the given linked list
  3919.  */
  3920. zotentry(l)
  3921. register struct hdr_line *l;
  3922. {
  3923.     register struct hdr_line *ld, *lf = l;
  3924.  
  3925.     while((ld = lf) != NULL){
  3926.     lf = ld->next;
  3927.     ld->next = ld->prev = NULL;
  3928.     free((char *) ld);
  3929.     }
  3930. }
  3931.  
  3932.  
  3933.  
  3934. /*
  3935.  * zotcomma - blast any trailing commas and white space from the end 
  3936.  *          of the given line
  3937.  */
  3938. int
  3939. zotcomma(s)
  3940. char *s;
  3941. {
  3942.     register char *p;
  3943.     int retval = FALSE;
  3944.  
  3945.     p = &s[strlen(s)];
  3946.     while(--p >= s){
  3947.     if(*p != ' '){
  3948.         if(*p == ','){
  3949.         *p = '\0';
  3950.         retval = TRUE;
  3951.         }
  3952.  
  3953.         return(retval);
  3954.     }
  3955.     }
  3956. }
  3957.  
  3958.  
  3959. /*
  3960.  * Save the current state of global variables so that we can restore
  3961.  * them later. This is so we can call pico again.
  3962.  * Also have to initialize some variables that normally would be set to
  3963.  * zero on startup.
  3964.  */
  3965. VARS_TO_SAVE *
  3966. save_pico_state()
  3967. {
  3968.     VARS_TO_SAVE  *ret;
  3969.     extern int     vtrow;
  3970.     extern int     vtcol;
  3971.     extern int     lbound;
  3972.     extern VIDEO **vscreen;
  3973.     extern VIDEO **pscreen;
  3974.     extern int     pico_all_done;
  3975.     extern jmp_buf finstate;
  3976.     extern char   *pico_anchor;
  3977.  
  3978.     if((ret = (VARS_TO_SAVE *)malloc(sizeof(VARS_TO_SAVE))) == NULL)
  3979.       return(ret);
  3980.  
  3981.     ret->vtrow = vtrow;
  3982.     ret->vtcol = vtcol;
  3983.     ret->lbound = lbound;
  3984.     ret->vscreen = vscreen;
  3985.     ret->pscreen = pscreen;
  3986.     ret->ods = ods;
  3987.     ret->delim_ps = delim_ps;
  3988.     ret->invert_ps = invert_ps;
  3989.     ret->pico_all_done = pico_all_done;
  3990.     memcpy(ret->finstate, finstate, sizeof(jmp_buf));
  3991.     ret->pico_anchor = pico_anchor;
  3992.     ret->Pmaster = Pmaster;
  3993.     ret->fillcol = fillcol;
  3994.     if((ret->pat = (char *)malloc(sizeof(char) * (strlen(pat)+1))) != NULL)
  3995.       strcpy(ret->pat, pat);
  3996.  
  3997.     ret->ComposerTopLine = ComposerTopLine;
  3998.     ret->ComposerEditing = ComposerEditing;
  3999.     ret->gmode = gmode;
  4000.     ret->alt_speller = alt_speller;
  4001.     ret->currow = currow;
  4002.     ret->curcol = curcol;
  4003.     ret->thisflag = thisflag;
  4004.     ret->lastflag = lastflag;
  4005.     ret->curgoal = curgoal;
  4006.     ret->opertree = (char *) malloc(sizeof(char) * (strlen(opertree) + 1));
  4007.     if(ret->opertree != NULL)
  4008.       strcpy(ret->opertree, opertree);
  4009.  
  4010.     ret->curwp = curwp;
  4011.     ret->wheadp = wheadp;
  4012.     ret->curbp = curbp;
  4013.     ret->bheadp = bheadp;
  4014.     ret->km_popped = km_popped;
  4015.     ret->mrow = term.t_mrow;
  4016.  
  4017.     /* Initialize for next pico call */
  4018.     wheadp = NULL;
  4019.     curwp = NULL;
  4020.     bheadp = NULL;
  4021.     curbp = NULL;
  4022.  
  4023.     return(ret);
  4024. }
  4025.  
  4026.  
  4027. void
  4028. restore_pico_state(state)
  4029. VARS_TO_SAVE *state;
  4030. {
  4031.     extern int     vtrow;
  4032.     extern int     vtcol;
  4033.     extern int     lbound;
  4034.     extern VIDEO **vscreen;
  4035.     extern VIDEO **pscreen;
  4036.     extern int     pico_all_done;
  4037.     extern jmp_buf finstate;
  4038.     extern char   *pico_anchor;
  4039.  
  4040.     clearcursor();
  4041.     vtrow = state->vtrow;
  4042.     vtcol = state->vtcol;
  4043.     lbound = state->lbound;
  4044.     vscreen = state->vscreen;
  4045.     pscreen = state->pscreen;
  4046.     ods = state->ods;
  4047.     delim_ps = state->delim_ps;
  4048.     invert_ps = state->invert_ps;
  4049.     pico_all_done = state->pico_all_done;
  4050.     memcpy(finstate, state->finstate, sizeof(jmp_buf));
  4051.     pico_anchor = state->pico_anchor;
  4052.     Pmaster = state->Pmaster;
  4053.     if(Pmaster)
  4054.       headents = Pmaster->headents;
  4055.  
  4056.     fillcol = state->fillcol;
  4057.     if(state->pat)
  4058.       strcpy(pat, state->pat);
  4059.  
  4060.     ComposerTopLine = state->ComposerTopLine;
  4061.     ComposerEditing = state->ComposerEditing;
  4062.     gmode = state->gmode;
  4063.     alt_speller = state->alt_speller;
  4064.     currow = state->currow;
  4065.     curcol = state->curcol;
  4066.     thisflag = state->thisflag;
  4067.     lastflag = state->lastflag;
  4068.     curgoal = state->curgoal;
  4069.     if(state->opertree)
  4070.       strcpy(opertree, state->opertree);
  4071.  
  4072.     curwp = state->curwp;
  4073.     wheadp = state->wheadp;
  4074.     curbp = state->curbp;
  4075.     bheadp = state->bheadp;
  4076.     km_popped = state->km_popped;
  4077.     term.t_mrow = state->mrow;
  4078. }
  4079.  
  4080.  
  4081. void
  4082. free_pico_state(state)
  4083. VARS_TO_SAVE *state;
  4084. {
  4085.     if(state->pat)
  4086.       free(state->pat);
  4087.  
  4088.     if(state->opertree)
  4089.       free(state->opertree);
  4090.  
  4091.     free(state);
  4092. }
  4093.  
  4094.  
  4095. /*
  4096.  * Ok to call this twice in a row because it won't do anything the second
  4097.  * time.
  4098.  */
  4099. void
  4100. fix_mangle_and_err(mangled, errmsg, name)
  4101.     int   *mangled;
  4102.     char **errmsg;
  4103.     char  *name;
  4104. {
  4105.     if(mangled && *mangled){
  4106.     ttresize();
  4107.     picosigs();
  4108.     PaintBody(0);
  4109.     *mangled = 0;
  4110.     }
  4111.  
  4112.     if(errmsg && *errmsg){
  4113.     if(**errmsg){
  4114.         char err[500];
  4115.  
  4116.         sprintf(err, "%s field: %s", name, *errmsg);
  4117.         (*term.t_beep)();
  4118.         emlwrite(err, NULL);
  4119.     }
  4120.     else
  4121.         mlerase();
  4122.  
  4123.     free(*errmsg);
  4124.     *errmsg = NULL;
  4125.     }
  4126. }
  4127.  
  4128.  
  4129. #ifdef    MOUSE
  4130. #undef    HeaderEditor
  4131.  
  4132. /*
  4133.  * Wraper function for the real header editor. 
  4134.  * Does the important tasks of:
  4135.  *    1) verifying that we _can_ edit the headers.
  4136.  *    2) acting on the result code from the header editor.
  4137.  */
  4138. int
  4139. HeaderEditor(f, n)
  4140.      int f, n;
  4141. {
  4142.     int  retval;
  4143.     
  4144.     
  4145. #ifdef _WINDOWS
  4146.     /* Sometimes we get here from a scroll callback, which
  4147.      * is no good at all because mswin is not ready to process input and
  4148.      * this _headeredit() will never do anything.
  4149.      * Putting this test here was the most general solution I could think
  4150.      * of. */
  4151.     if (!mswin_caninput()) 
  4152.     return (-1);
  4153. #endif
  4154.  
  4155.     retval = HeaderEditorWork(f, n);
  4156.     if (retval == -3) {
  4157.     retval = mousepress(0,0);
  4158.     }
  4159.     return (retval);
  4160. }
  4161. #endif
  4162.