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