* clear out some warnings by gcc 9.3.1.
[alpine.git] / pico / composer.c
bloba20314b5096973fef6b847f6be44ff8494841b36
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
5 /*
6 * ========================================================================
7 * Copyright 2006-2009 University of Washington
8 * Copyright 2013-2020 Eduardo Chappa
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
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
18 * Program: Pine composer routines
20 * NOTES:
22 * - composer.c is the composer for the PINE mail system
24 * - tabled 01/19/90
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 ....
31 * - untabled 01/30/90
33 * Notes: Installed header line editing, with wrapping and unwrapping,
34 * context sensitive help, and other mail header editing features.
36 * - finalish code cleanup 07/15/91
38 * Notes: Killed/yanked header lines use emacs kill buffer.
39 * Arbitrarily large headers are handled gracefully.
40 * All formatting handled by FormatLines.
42 * - Work done to optimize display painting 06/26/92
43 * Not as clean as it should be, needs more thought
46 #include "headers.h"
48 #include "osdep/terminal.h"
49 #include "../pith/string.h"
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 *);
86 * definition header field array, structures defined in pico.h
88 struct headerentry *headents;
92 * structure that keeps track of the range of header lines that are
93 * to be displayed and other fun stuff about the header
95 struct on_display ods; /* global on_display struct */
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
101 struct hdr_line *subject_line = NULL;
104 * useful macros
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)
112 #ifdef MOUSE
114 * Redefine HeaderEditor to install wrapper required for mouse event
115 * handling...
117 #define HeaderEditor HeaderEditorWork
118 #endif /* MOUSE */
120 #define HDR_DELIM "----- Message Text -----"
123 * useful declarations
125 static short delim_ps = 0; /* previous state */
126 static short invert_ps = 0; /* previous state */
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}
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
157 * function key mappings for header editor
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')}
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.
182 * Returns: TRUE if special header handling was requested,
183 * FALSE under standard default behavior.
186 InitMailHeader(PICO *mp)
188 char *addrbuf;
189 struct headerentry *he;
190 int rv;
192 if(!mp->headents){
193 headents = NULL;
194 return(FALSE);
198 * initialize some of on_display structure, others below...
200 ods.p_ind = 0;
201 ods.p_line = COMPOSER_TOP_LINE;
202 ods.top_l = ods.cur_l = NULL;
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;
219 ofp = NLINE - 35;
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;
229 else{
230 ofp1 = ofp;
231 ofp2 = 0;
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 ? ") " : "");
244 /* append description, escaping quotes */
245 if(ap->description){
246 char *dp = ap->description, *bufp = &buf[strlen(buf)];
247 int escaped = 0;
250 if(*dp == '\"' && !escaped){
251 *bufp++ = '\\';
252 ofp2--;
254 else if(escaped){
255 *bufp++ = '\\';
256 escaped = 0;
258 while(--ofp2 > 0 && (*bufp++ = *dp++));
260 *bufp = '\0';
263 snprintf(buf + strlen(buf), sizeof(buf)-strlen(buf), "\"%s", ap->next ? "," : "");
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);
274 strncat(addrbuf, buf, addrbuflen-strlen(addrbuf)-1);
275 addrbuf[addrbuflen-1] = '\0';
277 ap = ap->next;
279 InitEntryText(addrbuf, he);
280 free((char *)addrbuf);
281 } else {
282 InitEntryText("", he);
284 he->realaddr = NULL;
285 } else {
286 if(!he->blank)
287 addrbuf = *(he->realaddr);
288 else
289 addrbuf = "";
291 InitEntryText(addrbuf, he);
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.
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);
306 rv = TRUE;
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;
315 UpdateHeader(0);
316 return(rv);
322 * InitEntryText - Add the given header text into the header entry
323 * line structure.
326 InitEntryText(char *utf8text, struct headerentry *e)
328 struct hdr_line *curline;
329 register int longest;
332 * get first chunk of memory, and tie it to structure...
334 if((curline = HALLOC()) == NULL){
335 emlwrite("Unable to make room for full Header.", NULL);
336 return(FALSE);
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 */
345 if(FormatLines(curline, utf8text, longest, e->break_on_comma, 0) == -1)
346 return(FALSE);
347 else
348 return(TRUE);
354 * ResizeHeader - Handle resizing display when SIGWINCH received.
356 * notes:
357 * works OK, but needs thorough testing
361 ResizeHeader(void)
363 register struct headerentry *i;
364 register int offset;
366 if(!headents)
367 return(TRUE);
369 offset = (ComposerEditing) ? HeaderOffset(ods.cur_e) : 0;
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);
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;
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 */
391 if(!l){
392 e = i - headents;
393 l = headents[e].hd_text;
396 HeaderFocus(e, -1); /* put focus on last line */
399 if(ComposerTopLine != COMPOSER_TOP_LINE)
400 UpdateHeader(0);
402 PaintBody(0);
404 if(ComposerEditing)
405 movecursor(ods.p_line, ods.p_ind+headents[ods.cur_e].prwid);
407 (*term.t_flush)();
408 return(TRUE);
414 * HeaderOffset - return the character offset into the given header
417 HeaderOffset(int h)
419 register struct hdr_line *l;
420 int i = 0;
422 l = headents[h].hd_text;
424 while(l != ods.cur_l){
425 i += ucs4_strlen(l->text);
426 l = l->next;
428 return(i+ods.p_ind);
434 * HeaderFocus - put the dot at the given offset into the given header
437 HeaderFocus(int h, int offset)
439 register struct hdr_line *l;
440 register int i;
441 int last = 0;
443 if(offset == -1) /* focus on last line */
444 last = 1;
446 l = headents[h].hd_text;
447 while(1){
448 if(last && l->next == NULL){
449 break;
451 else{
452 if((i=ucs4_strlen(l->text)) >= offset)
453 break;
454 else
455 offset -= i;
457 if((l = l->next) == NULL)
458 return(FALSE);
461 ods.cur_l = l;
462 ods.p_len = ucs4_strlen(l->text);
463 ods.p_ind = (last) ? 0 : offset;
465 return(TRUE);
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
481 HeaderEditor(int f, int n)
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
494 if(!headents)
495 return(TRUE); /* nothing to edit! */
497 ComposerEditing = TRUE;
498 display_delimiter(0); /* provide feedback */
500 #ifdef _WINDOWS
501 mswin_setscrollrange (0, 0);
502 #endif /* _WINDOWS */
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;
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.
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.
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;
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;
534 ods.p_ind = 1000; /* and make sure at EOL */
536 else{
538 * note: assumes that ods.cur_e and ods.cur_l haven't changed
539 * since we left...
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;
550 /* and scroll back if needed */
551 if(f == 3)
552 for(i = 0; header_upline(0) && i <= FULL_SCR(); i++)
556 ods.p_line = ComposerTopLine - 2;
558 /* else just trust what ods contains */
560 InvertPrompt(ods.cur_e, TRUE); /* highlight header field */
561 sgarbk = 1;
564 lastch = ch;
565 if(km_popped){
566 km_popped--;
567 if(km_popped == 0)
568 sgarbk = 1;
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();
578 else if(term.t_mrow == 0)
579 PaintBody(1);
581 ShowPrompt(); /* display correct options */
582 sgarbk = 0;
583 if(km_popped){
584 term.t_mrow = 0;
585 curwp->w_ntrows += 2;
589 ch = LineEdit(!(gmode&MDVIEW), &lastch); /* work on the current line */
591 if(km_popped)
592 switch(ch){
593 case NODATA:
594 case (CTRL|'L'):
595 km_popped++;
596 break;
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;
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;
617 else
618 goto bleep;
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 */
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);
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;
658 ods.p_ind = 0;
659 ods.cur_e = cur_e;
660 ods.cur_l = headents[ods.cur_e].hd_text;
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;
671 case (CTRL|'C') : /* bag whole thing ?*/
672 if(abort_composer(1, 0) == TRUE)
673 return(FALSE);
675 break;
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...
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);
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);
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;
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);
710 fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
712 if(wquit(1,0) == TRUE)
713 return(TRUE);
715 if(i > 0){
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).
721 UpdateHeader(0);
722 PaintHeader(COMPOSER_TOP_LINE, FALSE);
723 PaintBody(1);
725 break;
727 case (CTRL|'Z') : /* Suspend compose */
728 if(gmode&MDSSPD){ /* is it allowed? */
729 bktoshell(0, 1);
730 PaintBody(0);
732 else
733 unknown_command(ch);
735 break;
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;
745 case (CTRL|'O') : /* Suspend message */
746 if(Pmaster->pine_flags & MDHDRONLY)
747 goto bleep;
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;
767 else if(headents[ods.cur_e].builder)
768 i = call_builder(&headents[ods.cur_e], &mangled, &err);
770 fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
772 if(i < 0) /* don't leave without a valid addr */
773 break;
775 suspend_composer(1, 0);
776 return(TRUE);
778 #ifdef ATTACHMENTS
779 case (CTRL|'J') : /* handle attachments */
780 if(Pmaster->pine_flags & MDHDRONLY)
781 goto bleep;
783 { char cmt[NLINE];
784 LMLIST *lm = NULL, *lmp;
785 char buf[NLINE], *bfp;
786 int saved_km_popped;
787 size_t len;
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.
796 saved_km_popped = km_popped;
797 km_popped = 0;
799 if(AskAttach(cmt, sizeof(cmt), &lm)){
801 for(lmp = lm; lmp; lmp = lmp->next){
802 size_t space;
804 len = lmp->dir ? strlen(lmp->dir)+1 : 0;
805 len += lmp->fname ? strlen(lmp->fname) : 0;
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;
816 else{
817 bfp = buf;
818 space = sizeof(buf);
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 : "");
827 (void) QuoteAttach(bfp, space);
829 (void) AppendAttachment(bfp, lm->size, cmt);
831 if(bfp != buf)
832 free(bfp);
835 zotlmlist(lm);
838 km_popped = saved_km_popped;
839 sgarbk = 1; /* clean up prompt */
841 break;
842 #endif
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;
856 strng = ods.cur_l->text;
857 offset = HeaderOffset(ods.cur_e);
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 != ',')){
868 end = start+1;
870 while(start > strng
871 && *(start-1)
872 && *(start-1) != ',')
873 start--;
875 while(ucs4_isspace(*start))
876 start++;
878 if(*end != ',' && ods.cur_l->next)
879 add_a_comma++;
882 * Nickname prefix begins with start and ends
883 * with end-1. Replace those characters with
884 * completed nickname.
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);
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
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.
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.
915 ambiguity = (*(headents[ods.cur_e].nickcmpl))(prefix,
916 &new_nickname, (lastch == ch), 0);
919 * Don't fall through on no-match TAB unless
920 * it is the second TAB.
922 if(ambiguity == 0 && lastch != ch){
923 lastch = 0;
924 break;
927 if(ambiguity != 1)
928 lastch = 0;
930 if(ambiguity == 0)
931 goto nomore_to_complete;
933 if(new_nickname){
934 if(strcmp(new_nickname, prefix)){
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.
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);
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);
958 fs_give((void **) &uprefix);
961 * Delete everything up to end by
962 * copying characters to start of buffer.
964 up1 = before_start;
965 up2 = end;
966 for(i = ods.p_len - (end - before_start) + 1; i > 0; i--)
967 *up1++ = *up2++;
969 ods.p_len -= (end - before_start);
971 if(saveprefix){
972 l1 = strlen(saveprefix);
973 l2 = strlen(new_nickname);
974 l = l1 + l2;
976 /* add a comma? */
977 if(add_a_comma && ambiguity == 2){
978 l++;
979 offset++;
981 else
982 add_a_comma = 0;
984 insert = (char *) fs_get((l+1) * sizeof(char));
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.
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] = ',';
997 insert[l] = '\0';
1000 fs_give((void **) &saveprefix);
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);
1010 if(insert)
1011 fs_give((void **) &insert);
1013 HeaderFocus(ods.cur_e, offset);
1015 else{
1016 (*term.t_beep)();
1019 ambig = new_nickname;
1020 new_nickname = NULL;
1023 if(!ambig && prefix){
1024 ambig = prefix;
1025 prefix = NULL;
1029 if(ambiguity == 2 && fallthru){
1030 if(prefix)
1031 fs_give((void **) &prefix);
1033 if(new_nickname)
1034 fs_give((void **) &new_nickname);
1036 if(ambig)
1037 fs_give((void **) &ambig);
1039 UpdateHeader(0);
1040 PaintBody(0);
1041 goto nomore_to_complete;
1044 UpdateHeader(0);
1045 PaintBody(0);
1047 if(prefix)
1048 fs_give((void **) &prefix);
1050 if(new_nickname)
1051 fs_give((void **) &new_nickname);
1053 if(ambig)
1054 fs_give((void **) &ambig);
1056 else{
1057 goto nomore_to_complete;
1060 break;
1062 else{
1063 nomore_to_complete:
1064 ods.p_ind = 0; /* fall through... */
1067 case (CTRL|'N') :
1068 case KEY_DOWN :
1069 header_downline(!hdr_only, hdr_only);
1070 break;
1072 case (CTRL|'P') :
1073 case KEY_UP :
1074 header_upline(1);
1075 break;
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 */
1085 else{
1086 int move_down, bot_pline;
1087 struct hdr_line *new_cur_l, *line, *next_line, *prev_line;
1089 move_down = BOTTOM() - 2 - ods.p_line;
1090 if(move_down < 0)
1091 move_down = 0;
1094 * Count down move_down lines to find the pointer to the line
1095 * that we want to become the current line.
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;
1104 new_cur_l = next_line;
1107 if(headents[cur_e].blank){
1108 next_line = next_sel_hline(&cur_e, new_cur_l);
1109 if(!next_line)
1110 break;
1112 new_cur_l = next_line;
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.
1121 while(ods.cur_l != new_cur_l && header_downline(0, 0))
1125 * Count back up, if we're at the bottom, to find the new
1126 * top line.
1128 cur_e = ods.cur_e;
1129 if(next_hline(&cur_e, ods.cur_l) == NULL){
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.
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;
1145 line = prev_line;
1148 ods.top_l = line;
1149 ods.top_e = cur_e;
1150 ods.p_line = i;
1153 else{
1154 ods.top_l = ods.cur_l;
1155 ods.top_e = ods.cur_e;
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.
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;
1170 line = next_line;
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.
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;
1186 line = prev_line;
1189 ods.top_l = line;
1190 ods.top_e = cur_e;
1193 * Special case. If p_line is within one of the bottom,
1194 * move it to the bottom.
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;
1205 UpdateHeader(0);
1206 PaintHeader(COMPOSER_TOP_LINE, FALSE);
1207 PaintBody(1);
1210 break;
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;
1218 break;
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)
1232 else {
1233 for (i = mp.row-ods.p_line;
1234 i > 0 && header_downline(!hdr_only, 0);
1235 --i)
1239 if((ods.p_ind = mp.col - headents[ods.cur_e].prwid) <= 0)
1240 ods.p_ind = 0;
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.
1246 if (ods.p_line >= ComposerTopLine)
1247 retval = -3;
1250 break;
1252 case M_BUTTON_RIGHT :
1253 #ifdef _WINDOWS
1254 pico_popup();
1255 #endif
1256 case M_BUTTON_MIDDLE :
1257 default : /* NOOP */
1258 break;
1261 break;
1262 #endif /* MOUSE */
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;
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;
1284 for(lmp = lm; lmp; lmp = lmp->next){
1285 size_t space;
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);
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;
1301 else{
1302 bfp = buf;
1303 space = sizeof(buf);
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 : "");
1312 (void) QuoteAttach(bfp, space);
1314 snprintf(bfp + strlen(bfp), space-strlen(bfp), " (%s) \"\"%s", lmp->size,
1315 (!headents[ods.cur_e].hd_text->text[0]) ? "":",");
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);
1323 if(bfp != buf)
1324 free(bfp);
1326 UpdateHeader(0);
1329 zotlmlist(lm);
1330 } /* else, nothing of interest */
1331 } else if (headents[ods.cur_e].selector != NULL) {
1332 VARS_TO_SAVE *saved_state;
1334 /*---- General selector for non-attachments -----*/
1337 * Since the selector may make a new call back to pico()
1338 * we need to save and restore the pico state here.
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 */
1347 else{
1348 char *s = "Not enough memory";
1349 size_t len;
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;
1358 if(bufp != NULL) {
1359 mangled = 0;
1360 err = NULL;
1361 if(headents[ods.cur_e].break_on_comma) {
1362 /*--- Must be an address ---*/
1365 * If current line is empty and there are more
1366 * lines that follow, delete the empty lines
1367 * before adding the new address.
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);
1376 ods.p_ind = 0;
1378 if(ods.cur_l->text[0] != '\0'){
1379 struct hdr_line *h, *start_of_addr;
1380 int q = 0;
1382 /* cur is not first line */
1383 if(ods.cur_l != headents[ods.cur_e].hd_text){
1385 * Protect against adding a new entry into
1386 * the middle of a long, continued entry.
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;
1395 else if(start_of_addr == NULL)
1396 start_of_addr = h;
1398 if(h->next == ods.cur_l)
1399 break;
1402 if(start_of_addr){
1403 ods.cur_l = start_of_addr;
1404 ods.p_len = ucs4_strlen(ods.cur_l->text);
1408 for(i = ++ods.p_len; i; i--)
1409 ods.cur_l->text[i] = ods.cur_l->text[i-1];
1411 ods.cur_l->text[0] = ',';
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;
1424 * If the "selector" has a "builder" as well, pass
1425 * what was just selected thru the builder...
1427 if(headents[ods.cur_e].builder){
1428 struct hdr_line *l;
1429 int cur_row, top_too = 0;
1431 for(l = headents[ods.cur_e].hd_text, cur_row = 0;
1432 l && l != ods.cur_l;
1433 l = l->next, cur_row++)
1436 top_too = headents[ods.cur_e].hd_text == ods.top_l;
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);
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--)
1449 if(top_too)
1450 ods.top_l = headents[ods.cur_e].hd_text;
1453 UpdateHeader(0);
1454 } else {
1455 UCS *u;
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);
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);
1471 } else {
1472 /*----- No selector -----*/
1473 (*term.t_beep)();
1474 continue;
1477 PaintBody(0);
1478 if(errmss != NULL) {
1479 (*term.t_beep)();
1480 emlwrite(errmss, NULL);
1481 free(errmss);
1482 errmss = NULL;
1484 continue;
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;
1495 if(!ComposerHelp(ods.cur_e))
1496 break; /* else, fall through... */
1498 case (CTRL|'L'): /* redraw requested */
1499 PaintBody(0);
1500 break;
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;
1511 * Since the fileedit will make a new call back to pico()
1512 * we need to save and restore the pico state here.
1514 if((saved_state = save_pico_state()) != NULL){
1515 UCS *u;
1517 e = &headents[ods.cur_e];
1519 for(line = e->hd_text; line != NULL; line = line->next)
1520 sz += ucs4_strlen(line->text);
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);
1528 filename = ucs4_to_utf8_cpystr(u);
1529 free(u);
1532 errmss = (*(headents[ods.cur_e].fileedit))(filename);
1534 if(filename)
1535 free(filename);
1537 restore_pico_state(saved_state);
1538 free_pico_state(saved_state);
1539 ttresize(); /* fixup screen bufs */
1540 picosigs(); /* restore altered signals */
1542 else{
1543 char *s = "Not enough memory";
1544 size_t len;
1546 len = strlen(s);
1547 errmss = (char *)malloc((len+1) * sizeof(char));
1548 strncpy(errmss, s, len+1);
1549 errmss[len] = '\0';
1552 PaintBody(0);
1554 if(errmss != NULL) {
1555 (*term.t_beep)();
1556 emlwrite(errmss, NULL);
1557 free(errmss);
1558 errmss = NULL;
1561 continue;
1563 else
1564 goto bleep;
1566 break;
1568 default : /* huh? */
1569 bleep:
1570 unknown_command(ch);
1572 case NODATA:
1573 break;
1576 while (ods.p_line < ComposerTopLine);
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);
1588 return(retval);
1596 header_downline(int beyond, int gripe)
1598 struct hdr_line *new_l, *l;
1599 int new_e, status, fullpaint, len, e, incr = 0;
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){
1606 if(gripe){
1607 char xx[81];
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);
1624 return(0);
1628 * Because of blank header lines the cursor may need to move down
1629 * more than one line. Figure out how far.
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 */
1640 ods.p_line += incr;
1641 fullpaint = ods.p_line >= BOTTOM(); /* force full redraw? */
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 */
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);
1656 } else if(headents[ods.cur_e].builder) { /* expand addresses */
1657 int mangled = 0;
1658 char *err = NULL;
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;
1666 if(new_l) /* if new_l, force validity */
1667 new_l = headents[new_e].hd_text;
1669 NewTop(0); /* get new top_l */
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);
1678 fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
1681 if(new_l){ /* if one below, turn it on */
1682 InvertPrompt(new_e, TRUE);
1683 sgarbk = 1; /* paint keymenu too */
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;
1694 if(!new_l || status || fullpaint){ /* handle big screen paint */
1695 UpdateHeader(0);
1696 PaintHeader(COMPOSER_TOP_LINE, FALSE);
1697 PaintBody(1);
1699 if(!new_l){ /* make sure we're done */
1700 ods.p_line = ComposerTopLine;
1701 InvertPrompt(ods.cur_e, FALSE); /* turn off current entry */
1705 return(new_l ? 1 : 0);
1713 header_upline(int gripe)
1715 struct hdr_line *new_l, *l;
1716 int new_e, status, fullpaint, len, e, incr = 0;
1717 EML eml;
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);
1729 return(0);
1733 * Because of blank header lines the cursor may need to move up
1734 * more than one line. Figure out how far.
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 */
1745 ods.p_line -= incr;
1746 fullpaint = ods.p_line <= COMPOSER_TOP_LINE;
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);
1759 else if(headents[ods.cur_e].builder){
1760 int mangled = 0;
1761 char *err = NULL;
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)
1771 /* and cur_l (required in fix_and... */
1772 ods.cur_l = new_l;
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);
1781 fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
1784 InvertPrompt(new_e, TRUE);
1785 sgarbk = 1;
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;
1793 if(status > 0 || fullpaint){
1794 UpdateHeader(0);
1795 PaintHeader(COMPOSER_TOP_LINE, FALSE);
1796 PaintBody(1);
1799 return(1);
1807 AppendAttachment(char *fn, char *sz, char *cmt)
1809 int a_e, status, spaces;
1810 struct hdr_line *lp;
1811 char b[256];
1812 UCS *u;
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;
1823 /* append new attachment line */
1824 for(lp = headents[a_e].hd_text; lp->next; lp=lp->next)
1827 /* build new attachment line */
1828 if(lp->text[0]){ /* adding a line? */
1829 UCS comma[2];
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;
1839 else{
1840 emlwwrite(_("Can't allocate line for new attachment!"), NULL);
1841 return(0);
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);
1857 /* validate the new attachment, and reformat if needed */
1858 if((status = SyncAttach()) != 0){
1859 EML eml;
1861 if(status < 0){
1862 eml.s = fn;
1863 emlwwrite(_("Problem attaching: %s"), &eml);
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);
1874 UpdateHeader(0);
1875 PaintHeader(COMPOSER_TOP_LINE, status != 0);
1876 PaintBody(1);
1877 return(status != 0);
1882 * LineEdit - Always use insert mode and handle line wrapping
1884 * returns:
1885 * Any characters typed in that aren't printable
1886 * (i.e. commands)
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.
1896 LineEdit(int allowedit, UCS *lastch)
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 */
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;
1916 u = ucs4_particular_width(strng, LINEWID());
1917 ods.p_ind = u - strng;
1920 while(1){ /* edit the line... */
1922 if(Pmaster && subject_line != NULL
1923 && ods.cur_l == subject_line
1924 && ods.cur_l->text[0] == 0)
1925 (*Pmaster->newthread)();
1927 if(skipmove)
1928 skipmove = 0;
1929 else
1930 HeaderPaintCursor();
1932 last_key = ch;
1933 if(ch && lastch)
1934 *lastch = ch;
1936 (*term.t_flush)(); /* get everything out */
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
1948 ch = GetKey();
1950 if (term.t_nrow < 6 && ch != NODATA){
1951 (*term.t_beep)();
1952 emlwrite(_("Please make the screen larger."), NULL);
1953 continue;
1956 #ifdef MOUSE
1957 clear_mfunc(mouse_in_content);
1958 #endif
1959 #ifdef _WINDOWS
1960 mswin_cleardndcallback ();
1961 mswin_mousetrackcallback(NULL);
1962 #endif
1964 switch(ch){
1965 case DEL :
1966 if(gmode & P_DELRUBS)
1967 ch = KEY_DEL;
1969 default :
1970 (*Pmaster->keybinput)();
1971 if(!time_to_check())
1972 break;
1974 case NODATA : /* new mail ? */
1975 if((*Pmaster->newmail)(ch == NODATA ? 0 : 2, 1) >= 0){
1976 int rv;
1978 if(km_popped){
1979 term.t_mrow = 2;
1980 curwp->w_ntrows -= 2;
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 */
1991 mpresf = 1;
1992 if(km_popped){
1993 term.t_mrow = 0;
1994 curwp->w_ntrows += 2;
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;
2004 break;
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);
2015 tbufp = &strng[ods.p_len];
2017 if(VALID_KEY(ch)){ /* char input */
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 ...
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;
2030 if(headents[ods.cur_e].single_space){
2031 if(ch == ' '
2032 && (strng[ods.p_ind]==' ' || strng[ods.p_ind-1]==' '))
2033 continue;
2037 * go ahead and add the character...
2039 if(ods.p_len < HLSZ){
2040 tbufp = &strng[++ods.p_len]; /* find the end */
2042 *tbufp = tbufp[-1];
2043 } while(--tbufp > &strng[ods.p_ind]); /* shift right */
2044 strng[ods.p_ind++] = ch; /* add char to str */
2047 /* mark this entry dirty */
2048 mark_sticky(&headents[ods.cur_e]);
2049 headents[ods.cur_e].dirty = 1;
2052 * then find out where things fit...
2054 if(ucs4_str_width_ptr_to_ptr(&strng[0], &strng[ods.p_len]) < LINEWID()){
2055 CELL c;
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! */
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;
2070 else{
2072 * during the format, the dot may have moved
2073 * down to the next line...
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;
2082 ods.p_len = ucs4_strlen(strng);
2085 UpdateHeader(0);
2086 PaintHeader(COMPOSER_TOP_LINE, FALSE);
2087 PaintBody(1);
2088 continue;
2091 else{
2092 rdonly();
2093 continue;
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--;
2102 if(ods.p_ind == 0)
2104 if(ods.p_line != COMPOSER_TOP_LINE)
2105 ods.p_ind = 1000; /* put cursor at end of line */
2106 return(KEY_UP);
2109 while(ods.p_ind > 0 && !ucs4_isalnum(strng[ods.p_ind]))
2110 ods.p_ind--; /* skip any whitespace we're in */
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--;
2118 continue;
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 */
2125 while(strng[ods.p_ind] && !ucs4_isalnum(strng[ods.p_ind]))
2126 ods.p_ind++; /* skip any whitespace after it */
2128 if(strng[ods.p_ind] == '\0'){
2129 ods.p_ind = 0; /* end of line, let caller handle it */
2130 return(KEY_DOWN);
2133 continue;
2135 case (CTRL|'K') : /* kill line cursor's on */
2136 if(!allowedit){
2137 rdonly();
2138 continue;
2141 lp = ods.cur_l;
2142 if (!(gmode & MDDTKILL))
2143 ods.p_ind = 0;
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);
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);
2156 i = (ods.p_line == COMPOSER_TOP_LINE);
2157 UpdateHeader(0);
2158 PaintHeader(COMPOSER_TOP_LINE, TRUE);
2160 if(km_popped){
2161 km_popped--;
2162 movecursor(term.t_nrow, 0);
2163 peeol();
2166 PaintBody(1);
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;
2175 case (CTRL|'U') : /* un-delete deleted lines */
2176 if(!allowedit){
2177 rdonly();
2178 continue;
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();
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;
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;
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;
2208 else if(gmode & MDHDRONLY)
2209 continue;
2211 ods.p_ind = 0;
2212 return(KEY_DOWN);
2214 case (CTRL|'B') :
2215 case KEY_LEFT : /* move character left */
2216 if(ods.p_ind > 0){
2217 ods.p_ind--;
2218 continue;
2220 if(ods.p_line != COMPOSER_TOP_LINE)
2221 ods.p_ind = 1000; /* put cursor at end of line */
2222 return(KEY_UP);
2224 case (CTRL|'M') : /* goto next field */
2225 ods.p_ind = 0;
2226 return(KEY_DOWN);
2228 case KEY_HOME :
2229 case (CTRL|'A') : /* goto beginning of line */
2230 ods.p_ind = 0;
2231 continue;
2233 case KEY_END :
2234 case (CTRL|'E') : /* goto end of line */
2235 ods.p_ind = ods.p_len;
2236 continue;
2238 case (CTRL|'D') : /* blast this char */
2239 case KEY_DEL :
2240 if(!allowedit){
2241 rdonly();
2242 continue;
2244 else if(ods.p_ind >= ucs4_strlen(strng))
2245 continue;
2247 if(headents[ods.cur_e].is_attach && intag(strng, ods.p_ind)){
2248 emlwwrite(_("Can't edit attachment number!"), NULL);
2249 continue;
2252 pputc(strng[ods.p_ind++], 0); /* drop through and rubout */
2254 case DEL : /* blast previous char */
2255 case (CTRL|'H') :
2256 if(!allowedit){
2257 rdonly();
2258 continue;
2261 if(headents[ods.cur_e].is_attach && intag(strng, ods.p_ind-1)){
2262 emlwwrite(_("Can't edit attachment number!"), NULL);
2263 continue;
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]);
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 */
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;
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;
2294 else{
2295 headents[ods.cur_e].sticky = 0;
2296 ods.p_ind = 0;
2299 tbufp = &strng[ods.p_ind];
2302 if((status = FormatLines(ods.cur_l, "", LINEWID(),
2303 headents[ods.cur_e].break_on_comma,0))==-1){
2304 (*term.t_beep)();
2305 continue;
2307 else{
2309 * beware, the dot may have moved...
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;
2321 if(UpdateHeader(0))
2322 status = TRUE;
2324 PaintHeader(COMPOSER_TOP_LINE, FALSE);
2325 if(status == TRUE)
2326 PaintBody(1);
2329 movecursor(ods.p_line,
2330 ucs4_str_width_ptr_to_ptr(strng, &strng[ods.p_ind])+headents[ods.cur_e].prwid);
2332 if(skipmove)
2333 continue;
2335 break;
2337 default :
2338 return(ch);
2342 while ((tbufp-strng) < HLSZ && *tbufp != '\0') /* synchronizing loop */
2343 pputc(*tbufp++, 0);
2345 if(ucs4_str_width_ptr_to_ptr(&strng[0], &strng[ods.p_len]) < LINEWID())
2346 peeol();
2351 void
2352 HeaderPaintCursor(void)
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);
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.
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.
2369 * returns:
2370 * -1 on error
2371 * FALSE if only this line is changed
2372 * TRUE if text below the first line is changed
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 */
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;
2392 ostr = h->text;
2393 nlp = h->next;
2394 if(utf8_instr)
2395 free_istr = istr = utf8_to_ucs4_cpystr(utf8_instr);
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);
2402 return(-1);
2405 if(ucs4_str_width(istr) + ucs4_str_width(ostr) >= maxwid){ /* break then fixup below */
2407 if((l=ucs4_str_width(istr)) < maxwid){ /* room for more */
2409 if(break_on_comma && (bp = ucs4_strqchr(istr, ',', &quoted, -1))){
2410 bp += (bp[1] == ' ') ? 2 : 1;
2411 for(tp = bp; *tp && *tp == ' '; tp++)
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';
2419 for(i = 0; &istr[i] < bp; i++)
2420 ostr[i] = istr[i];
2422 ostr[i] = '\0';
2423 retval = TRUE;
2425 else{
2426 breakp = break_point(ostr, maxwid-ucs4_str_width(istr),
2427 break_on_comma ? ',' : ' ',
2428 break_on_comma ? &quoted : NULL);
2430 if(breakp == ostr){ /* no good breakpoint */
2431 if(break_on_comma && *breakp == ','){
2432 breakp = ostr + 1;
2433 retval = TRUE;
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';
2441 else{ /* istr's broken as we can get it */
2443 * Break at maxwid - width of istr
2445 breakp = ucs4_particular_width(ostr, maxwid - ucs4_str_width(istr)-1);
2446 retval = TRUE;
2449 else
2450 retval = TRUE;
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';
2459 else{
2460 *breakp = '\0'; /* more work to break it */
2461 i = ucs4_strlen(istr);
2463 * shift ostr i chars
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 */
2474 * Short-circuit the recursion in this case.
2475 * No time right now to figure out how to do it better.
2477 else if(l > 2*maxwid){
2478 UCS *istrp, *saveostr = NULL;
2480 retval = TRUE;
2481 if(ostr && *ostr)
2482 saveostr = ucs4_cpystr(ostr);
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);
2492 else{
2493 breakp = break_point(istrp, maxwid, ' ', NULL);
2495 if(breakp == istrp) /* no good break point */
2496 breakp = ucs4_particular_width(istrp, maxwid-1);
2499 for(tp=ostr,bp=istrp; bp < breakp; tp++, bp++)
2500 *tp = *bp;
2502 *tp = '\0';
2503 l -= ucs4_str_width_ptr_to_ptr(istrp, breakp);
2504 istrp = breakp;
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);
2512 return(-1);
2515 lp->next = h->next;
2516 if(h->next)
2517 h->next->prev = lp;
2519 h->next = lp;
2520 lp->prev = h;
2521 lp->text[0] = '\0';
2522 h = h->next;
2523 ostr = h->text;
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.
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);
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);
2546 else{
2547 breakp = break_point(istrp, maxwid, ' ', NULL);
2549 if(breakp == istrp) /* no good break point */
2550 breakp = ucs4_particular_width(istrp, maxwid-1);
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';
2558 for(tp=ostr,bp=istr; bp < breakp; tp++, bp++)
2559 *tp = *bp;
2561 *tp = '\0';
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);
2570 else{
2571 breakp = break_point(istr, maxwid, ' ', NULL);
2573 if(breakp == istr) /* no good break point */
2574 breakp = ucs4_particular_width(istr, maxwid-1);
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';
2582 for(tp=ostr,bp=istr; bp < breakp; tp++, bp++)
2583 *tp = *bp;
2585 *tp = '\0';
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);
2595 return(-1);
2598 if(TERM_OPTIMIZE && (i = physical_line(h)) != -1)
2599 scrolldown(wheadp, i - 1, 1);
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;
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';
2615 ucs4_strncat(buf, ostr, len+10-ucs4_strlen(buf)-1);
2616 buf[len+10-1] = '\0';
2618 ucs4_strncpy(ostr, buf, HLSZ); /* copy back to ostr */
2619 ostr[HLSZ-1] = '\0';
2622 *buf = '\0';
2623 breakp = NULL;
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';
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);
2638 return(-1);
2641 if(TERM_OPTIMIZE && (i = physical_line(h)) != -1)
2642 scrolldown(wheadp, i - 1, 1);
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;
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);
2660 if(breakp == nlp->text){ /* commas this line? */
2661 for(tp=ostr; *tp && *tp != ' '; tp++)
2664 if(!*tp){ /* no commas, get next best */
2665 breakp += maxwid - ucs4_str_width(ostr) - 1;
2666 retval = TRUE;
2668 else
2669 retval = FALSE;
2671 else
2672 retval = TRUE;
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';
2684 else{
2685 ucs4_strncat(ostr, nlp->text, HLSZ-ucs4_strlen(ostr)-1);
2686 ostr[HLSZ-1] = '\0';
2688 if(TERM_OPTIMIZE && (i = physical_line(nlp)) != -1)
2689 scrollup(wheadp, i, 1);
2691 hldelete(nlp);
2693 if(!(nlp = h->next)){
2694 free(buf);
2695 if(free_istr)
2696 fs_give((void **) &free_istr);
2698 return(TRUE); /* can't go further */
2700 else
2701 retval = TRUE; /* more work to do? */
2705 else{
2706 free(buf);
2707 if(free_istr)
2708 fs_give((void **) &free_istr);
2710 return(FALSE);
2715 utf8 = ucs4_to_utf8_cpystr(buf);
2716 free(buf);
2717 if(free_istr)
2718 fs_give((void **) &free_istr);
2720 if(utf8){
2721 int rv;
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;
2734 default:
2735 rv = TRUE;
2738 return(rv);
2740 else
2741 return(-1);
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.
2750 FormatSyncAttach(void)
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());
2760 * PaintHeader - do the work of displaying the header from the given
2761 * physical screen line the end of the header.
2763 * 17 July 91 - fixed reshow to deal with arbitrarily large headers.
2765 void
2766 PaintHeader(int line, /* physical line on screen */
2767 int clear) /* clear before painting */
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;
2778 if(Pmaster && Pmaster->colors){
2779 lastc = pico_get_cur_color();
2780 pico_set_colorp(Pmaster->colors->ntcp, PSC_NONE);
2783 if(clear)
2784 pclear(COMPOSER_TOP_LINE, ComposerTopLine-1);
2786 curline = COMPOSER_TOP_LINE;
2787 curindex = curoffset = 0;
2789 for(lp = ods.top_l, e = ods.top_e; ; curline++){
2790 if((curline == line) || ((lp = next_hline(&e, lp)) == NULL))
2791 break;
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] = ' ';
2803 buf[i] = '\0';
2806 else if(!is_blank(curline, 0, headents[e].prwid)){
2807 for(i = 0; i < headents[e].prwid; i++)
2808 buf[i] = ' ';
2810 buf[i] = '\0';
2813 if(*(bufp = buf) != '\0'){ /* need to paint? */
2814 movecursor(curline, 0); /* paint the line... */
2815 while(*bufp != '\0')
2816 pputc(*bufp++, 0);
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;
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;
2843 if(curoffset < term.t_ncol){
2844 movecursor(curline, curoffset);
2845 peeol();
2847 curline++;
2848 curindex = curoffset = 0;
2849 if(curline >= BOTTOM())
2850 break;
2852 lp = lp->next;
2855 if(curline >= BOTTOM())
2856 return; /* don't paint delimiter */
2858 while(headents[++e].name != NULL)
2859 if(headents[e].display_it){
2860 lp = headents[e].hd_text;
2861 break;
2865 display_delimiter(ComposerEditing ? 0 : 1);
2867 if(lastc){
2868 pico_set_colorp(lastc, PSC_NONE);
2869 free_color_pair(&lastc);
2876 * PaintBody() - generic call to handle repainting everything BUT the
2877 * header
2879 * notes:
2880 * The header redrawing in a level 0 body paint gets done
2881 * in update()
2883 void
2884 PaintBody(int level)
2886 curwp->w_flag |= WFHARD; /* make sure framing's right */
2887 if(level == 0) /* specify what to update */
2888 sgarbf = TRUE;
2890 update(); /* display message body */
2892 if(level == 0 && ComposerEditing){
2893 mlerase(); /* clear the error line */
2894 ShowPrompt();
2900 * display_for_send - paint the composer from the top line and return.
2902 void
2903 display_for_send(void)
2905 int i = 0;
2906 struct hdr_line *l;
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;
2917 * fake that the cursor's in the first header line
2918 * and force repaint...
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);
2929 UpdateHeader(0); /* redraw whole enchilada */
2930 PaintHeader(COMPOSER_TOP_LINE, TRUE);
2931 PaintBody(0);
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);
2939 /* in case we don't exit, set up restoring the screen */
2940 sgarbf = TRUE; /* force redraw */
2946 * ArrangeHeader - set up display parm such that header is reasonably
2947 * displayed
2949 void
2950 ArrangeHeader(void)
2952 int e;
2953 register struct hdr_line *l;
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;
2964 UpdateHeader(1);
2969 * ComposerHelp() - display mail help in a context sensitive way
2970 * based on the level passed ...
2973 ComposerHelp(int level)
2975 char buf[80];
2976 VARS_TO_SAVE *saved_state;
2978 curwp->w_flag |= WFMODE;
2979 sgarbf = TRUE;
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);
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);
2999 ttresize();
3000 picosigs(); /* restore altered handlers */
3001 return(TRUE);
3007 * ToggleHeader() - set or unset pico values to the full screen size
3008 * painting header if need be.
3011 ToggleHeader(int show)
3014 * check to see if we need to display the header...
3016 if(show){
3017 UpdateHeader(0); /* figure bounds */
3018 PaintHeader(COMPOSER_TOP_LINE, FALSE); /* draw it */
3020 else{
3022 * set bounds for no header display
3024 curwp->w_toprow = ComposerTopLine = COMPOSER_TOP_LINE;
3025 curwp->w_ntrows = BOTTOM() - ComposerTopLine;
3027 return(TRUE);
3033 * HeaderLen() - return the length in lines of the exposed portion of the
3034 * header
3037 HeaderLen(void)
3039 register struct hdr_line *lp;
3040 int e;
3041 int i;
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++;
3050 return(i);
3056 * first_hline() - return a pointer to the first displayable header line
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
3063 struct hdr_line *
3064 first_hline(int *entry)
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);
3071 *entry = 0;
3072 return(NULL); /* this shouldn't happen */
3077 * first_sel_hline() - return a pointer to the first selectable header line
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
3084 struct hdr_line *
3085 first_sel_hline(int *entry)
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);
3092 *entry = 0;
3093 return(NULL); /* this shouldn't happen */
3099 * next_hline() - return a pointer to the next line structure
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
3106 struct hdr_line *
3107 next_hline(int *entry, struct hdr_line *line)
3109 if(line == NULL)
3110 return(NULL);
3112 if(line->next == NULL){
3113 while(headents[++(*entry)].name != NULL){
3114 if(headents[*entry].display_it)
3115 return(headents[*entry].hd_text);
3117 --(*entry);
3118 return(NULL);
3120 else
3121 return(line->next);
3126 * next_sel_hline() - return a pointer to the next selectable line structure
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
3133 struct hdr_line *
3134 next_sel_hline(int *entry, struct hdr_line *line)
3136 if(line == NULL)
3137 return(NULL);
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);
3144 --(*entry);
3145 return(NULL);
3147 else
3148 return(line->next);
3154 * prev_hline() - return a pointer to the next line structure back
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.
3163 struct hdr_line *
3164 prev_hline(int *entry, struct hdr_line *line)
3166 if(line == NULL)
3167 return(NULL);
3169 if(line->prev == NULL){
3170 int orig_entry;
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);
3182 *entry = orig_entry;
3183 return(NULL);
3185 else
3186 return(line->prev);
3191 * prev_sel_hline() - return a pointer to the previous selectable line
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.
3200 struct hdr_line *
3201 prev_sel_hline(int *entry, struct hdr_line *line)
3203 if(line == NULL)
3204 return(NULL);
3206 if(line->prev == NULL){
3207 int orig_entry;
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);
3219 *entry = orig_entry;
3220 return(NULL);
3222 else
3223 return(line->prev);
3229 * first_requested_hline() - return pointer to first line that pico's caller
3230 * asked that we start on.
3232 struct hdr_line *
3233 first_requested_hline(int *ent)
3235 int i, reqfield;
3236 struct hdr_line *rv = NULL;
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;
3248 return(rv);
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
3258 * showtop -- Attempt to show all header lines if they'll fit.
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
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
3276 UpdateHeader(int showtop)
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;
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;
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){
3297 * this checks to make sure cur_l is below top_l and that
3298 * cur_l is on the screen...
3300 if((lp = next_hline(&le, lp)) == NULL || ++i >= BOTTOM()){
3301 NewTop(0);
3302 ret = TRUE;
3303 break;
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++;
3316 if(!ret)
3317 ret = !(ods.p_line == old_p);
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 */
3325 if(!ret)
3326 ret = !(ComposerTopLine == old_top);
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;
3333 return(ret);
3339 * NewTop() - calculate a new top_l based on the cur_l
3341 * showtop -- Attempt to show all the header lines if they'll fit
3343 * returns:
3344 * with ods.top_l and top_e pointing at a reasonable line
3345 * entry
3347 void
3348 NewTop(int showtop)
3350 register struct hdr_line *lp;
3351 register int i;
3352 int e;
3354 lp = ods.cur_l;
3355 e = ods.cur_e;
3356 i = showtop ? FULL_SCR() : HALF_SCR();
3358 while(lp != NULL && (--i > 0)){
3359 ods.top_l = lp;
3360 ods.top_e = e;
3361 lp = prev_hline(&e, lp);
3368 * display_delimiter() - just paint the header/message body delimiter with
3369 * inverse value specified by state.
3371 void
3372 display_delimiter(int state)
3374 UCS *bufp, *buf;
3375 COLOR_PAIR *lastc = NULL;
3377 if(ComposerTopLine - 1 >= BOTTOM()) /* silently forget it */
3378 return;
3380 if(Pmaster && Pmaster->colors){
3381 lastc = pico_get_cur_color();
3382 pico_set_colorp(Pmaster->colors->ntcp, PSC_NONE);
3385 buf = utf8_to_ucs4_cpystr((gmode & MDHDRONLY) ? "" : HDR_DELIM);
3386 if(!buf)
3387 return;
3389 bufp = buf;
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++)
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! */
3403 delim_ps = state;
3405 movecursor(ComposerTopLine - 1, 0);
3406 if(state)
3407 (*term.t_rev)(1);
3409 while(*bufp != '\0')
3410 pputc(*bufp++, state ? 1 : 0);
3412 if(state)
3413 (*term.t_rev)(0);
3415 peeol();
3416 fs_give((void **) &buf);
3418 if(lastc){
3419 pico_set_colorp(lastc, PSC_NONE);
3420 free_color_pair(&lastc);
3427 * InvertPrompt() - invert the prompt associated with header entry to state
3428 * state (true if invert, false otherwise).
3429 * returns:
3430 * non-zero if nothing done
3431 * 0 if prompt inverted successfully
3433 * notes:
3434 * come to think of it, this func and the one above could
3435 * easily be combined
3438 InvertPrompt(int entry, int state)
3440 UCS *buf, *bufp;
3441 UCS *end;
3442 int i;
3444 buf = utf8_to_ucs4_cpystr(headents[entry].prompt); /* fresh prompt paint */
3445 if(!buf)
3446 return(-1);
3448 bufp = buf;
3449 if((i = entry_line(entry, FALSE)) == -1){
3450 fs_give((void **) &buf);
3451 return(-1); /* silently forget it */
3454 end = buf + ucs4_strlen(buf);
3457 * Makes sure that the prompt doesn't take up more than prwid of screen space.
3458 * The caller should do that, too, in order to make it look right so
3459 * this should most likely be a no-op
3461 if(ucs4_str_width_ptr_to_ptr(buf, end) > headents[entry].prwid){
3462 end = ucs4_particular_width(buf, headents[entry].prwid);
3463 *end = '\0';
3466 if(entry < 16 && (invert_ps&(1<<entry))
3467 == (state ? 1<<entry : 0)){ /* optimize ? */
3468 int j;
3470 for(j = 0; bufp[j] && pscr(i, j)->c == bufp[j]; j++)
3473 if(bufp[j] == '\0'){
3474 if(state)
3475 invert_ps |= 1<<entry;
3476 else
3477 invert_ps &= ~(1<<entry);
3479 fs_give((void **) &buf);
3480 return(0); /* already displayed! */
3484 if(entry < 16){ /* if > 16, cannot be stored in invert_ps */
3485 if(state)
3486 invert_ps |= 1<<entry;
3487 else
3488 invert_ps &= ~(1<<entry);
3491 movecursor(i, 0);
3492 if(state)
3493 (*term.t_rev)(1);
3495 while(*bufp && *(bufp + 1))
3496 pputc(*bufp++, 1); /* putc up to last char */
3498 if(state)
3499 (*term.t_rev)(0);
3501 pputc(*bufp, 0); /* last char not inverted */
3503 fs_give((void **) &buf);
3505 return(TRUE);
3511 * partial_entries() - toggle display of the bcc and fcc fields.
3513 * returns:
3514 * TRUE if there are partial entries on the display
3515 * FALSE otherwise.
3518 partial_entries(void)
3520 register struct headerentry *h;
3521 int is_on;
3523 /*---- find out status of first rich header ---*/
3524 for(h = headents; !h->rich_header && h->name != NULL; h++)
3527 is_on = h->display_it;
3528 for(h = headents; h->name != NULL; h++)
3529 if(h->rich_header)
3530 h->display_it = ! is_on;
3532 return(is_on);
3538 * entry_line() - return the physical line on the screen associated
3539 * with the given header entry field. Note: the field
3540 * may span lines, so if the last char is set, return
3541 * the appropriate value.
3543 * returns:
3544 * 1) physical line number of entry
3545 * 2) -1 if entry currently not on display
3548 entry_line(int entry, int lastchar)
3550 register int p_line = COMPOSER_TOP_LINE;
3551 int i;
3552 register struct hdr_line *line;
3554 for(line = ods.top_l, i = ods.top_e;
3555 headents && headents[i].name && i <= entry;
3556 p_line++){
3557 if(p_line >= BOTTOM())
3558 break;
3559 if(i == entry){
3560 if(lastchar){
3561 if(line->next == NULL)
3562 return(p_line);
3564 else if(line->prev == NULL)
3565 return(p_line);
3566 else
3567 return(-1);
3569 line = next_hline(&i, line);
3571 return(-1);
3577 * physical_line() - return the physical line on the screen associated
3578 * with the given header line pointer.
3580 * returns:
3581 * 1) physical line number of entry
3582 * 2) -1 if entry currently not on display
3585 physical_line(struct hdr_line *l)
3587 register int p_line = COMPOSER_TOP_LINE;
3588 register struct hdr_line *lp;
3589 int i;
3591 for(lp=ods.top_l, i=ods.top_e; headents[i].name && lp != NULL; p_line++){
3592 if(p_line >= BOTTOM())
3593 break;
3595 if(lp == l)
3596 return(p_line);
3598 lp = next_hline(&i, lp);
3600 return(-1);
3606 * call_builder() - resolve any nicknames in the address book associated
3607 * with the given entry...
3609 * NOTES:
3611 * BEWARE: this function can cause cur_l and top_l to get lost so BE
3612 * CAREFUL before and after you call this function!!!
3614 * There could to be something here to resolve cur_l and top_l
3615 * reasonably into the new linked list for this entry.
3617 * The reason this would mostly work without it is resolve_niks gets
3618 * called for the most part in between fields. Since we're moving
3619 * to the beginning or end (i.e. the next/prev pointer in the old
3620 * freed cur_l is NULL) of the next entry, we get a new cur_l
3621 * pointing at a good line. Then since top_l is based on cur_l in
3622 * NewTop() we have pretty much lucked out.
3624 * Where we could get burned is in a canceled exit (ctrl|x). Here
3625 * nicknames get resolved into addresses, which invalidates cur_l
3626 * and top_l. Since we don't actually leave, we could begin editing
3627 * again with bad pointers. This would usually results in a nice
3628 * core dump.
3630 * NOTE: The mangled argument is a little strange. It's used on both
3631 * input and output. On input, if it is not set, then that tells the
3632 * builder not to do anything that might take a long time, like a
3633 * white pages lookup. On return, it tells the caller that the screen
3634 * and signals may have been mangled so signals should be reset, window
3635 * resized, and screen redrawn.
3637 * RETURNS:
3638 * > 0 if any names where resolved, otherwise
3639 * 0 if not, or
3640 * < 0 on error
3641 * -1: move to next line
3642 * -2: don't move off this line
3645 call_builder(struct headerentry *entry, int *mangled, char **err)
3647 register int retval = 0;
3648 register int i;
3649 register struct hdr_line *line;
3650 int quoted = 0;
3651 int sbuflen;
3652 char *sbuf;
3653 char *s = NULL;
3654 char *tmp;
3655 struct headerentry *e;
3656 BUILDER_ARG *nextarg, *arg = NULL, *headarg = NULL;
3657 VARS_TO_SAVE *saved_state;
3659 if(!entry->builder)
3660 return(0);
3662 line = entry->hd_text;
3663 sbuflen = 0;
3664 while(line != NULL){
3665 sbuflen += (6*term.t_ncol);
3666 line = line->next;
3669 if((sbuf=(char *)malloc(sbuflen * sizeof(*sbuf))) == NULL){
3670 emlwrite("Can't malloc space to expand address", NULL);
3671 return(-1);
3674 *sbuf = '\0';
3677 * cat the whole entry into one string...
3679 line = entry->hd_text;
3680 while(line != NULL){
3681 i = ucs4_strlen(line->text);
3684 * To keep pine address builder happy, addresses should be separated
3685 * by ", ". Add this space if needed, otherwise...
3686 * (This is some ancient requirement that is no longer needed.)
3688 * If this line is NOT a continuation of the previous line, add
3689 * white space for pine's address builder if its not already there...
3690 * (This is some ancient requirement that is no longer needed.)
3692 * Also if it's not a continuation (i.e., there's already and addr on
3693 * the line), and there's another line below, treat the new line as
3694 * an implied comma.
3695 * (This should only be done for address-type lines, not for regular
3696 * text lines like subjects. Key off of the break_on_comma bit which
3697 * should only be set on those that won't mind a comma being added.)
3699 if(entry->break_on_comma){
3700 UCS *space, commaspace[3];
3702 commaspace[0] = ',';
3703 commaspace[1] = ' ';
3704 commaspace[2] = '\0';
3705 space = commaspace+1;
3707 if(i && line->text[i-1] == ','){
3708 ucs4_strncat(line->text, space, HLSZ-i-1); /* help address builder */
3709 line->text[HLSZ-1] = '\0';
3711 else if(line->next != NULL && !strend(line->text, ',')){
3712 if(ucs4_strqchr(line->text, ',', &quoted, -1)){
3713 ucs4_strncat(line->text, commaspace, HLSZ-i-1); /* implied comma */
3714 line->text[HLSZ-1] = '\0';
3717 else if(line->prev != NULL && line->next != NULL){
3718 if(ucs4_strchr(line->prev->text, ' ') != NULL
3719 && line->text[i-1] != ' '){
3720 ucs4_strncat(line->text, space, HLSZ-i-1);
3721 line->text[HLSZ-1] = '\0';
3726 tmp = ucs4_to_utf8_cpystr(line->text);
3727 if(tmp){
3728 strncat(sbuf, tmp, sbuflen-strlen(sbuf)-1);
3729 sbuf[sbuflen-1] = '\0';
3730 fs_give((void **) &tmp);
3733 line = line->next;
3736 if(entry->affected_entry){
3737 /* check if any non-sticky affected entries */
3738 for(e = entry->affected_entry; e; e = e->next_affected)
3739 if(!e->sticky)
3740 break;
3742 /* there is at least one non-sticky so make a list to pass */
3743 if(e){
3744 for(e = entry->affected_entry; e; e = e->next_affected){
3745 if(!arg){
3746 headarg = arg = (BUILDER_ARG *)malloc(sizeof(BUILDER_ARG));
3747 if(!arg){
3748 emlwrite("Can't malloc space for fcc", NULL);
3749 return(-1);
3751 else{
3752 arg->next = NULL;
3753 arg->tptr = NULL;
3754 arg->aff = &(e->bldr_private);
3755 arg->me = &(entry->bldr_private);
3758 else{
3759 nextarg = (BUILDER_ARG *)malloc(sizeof(BUILDER_ARG));
3760 if(!nextarg){
3761 emlwrite("Can't malloc space for fcc", NULL);
3762 return(-1);
3764 else{
3765 nextarg->next = NULL;
3766 nextarg->tptr = NULL;
3767 nextarg->aff = &(e->bldr_private);
3768 nextarg->me = &(entry->bldr_private);
3769 arg->next = nextarg;
3770 arg = arg->next;
3774 if(!e->sticky){
3775 line = e->hd_text;
3776 arg->tptr = ucs4_to_utf8_cpystr(line->text);
3783 * Even if there are no affected entries, we still need the arg
3784 * to pass the "me" pointer.
3786 if(!headarg){
3787 headarg = (BUILDER_ARG *)malloc(sizeof(BUILDER_ARG));
3788 if(!headarg){
3789 emlwrite("Can't malloc space", NULL);
3790 return(-1);
3792 else{
3793 headarg->next = NULL;
3794 headarg->tptr = NULL;
3795 headarg->aff = NULL;
3796 headarg->me = &(entry->bldr_private);
3801 * The builder may make a new call back to pico() so we save and
3802 * restore the pico state.
3804 saved_state = save_pico_state();
3805 retval = (*entry->builder)(sbuf, &s, err, headarg, mangled);
3806 if(saved_state){
3807 restore_pico_state(saved_state);
3808 free_pico_state(saved_state);
3811 if(mangled && *mangled & BUILDER_MESSAGE_DISPLAYED){
3812 *mangled &= ~ BUILDER_MESSAGE_DISPLAYED;
3813 if(mpresf == FALSE)
3814 mpresf = TRUE;
3817 if(mangled && *mangled & BUILDER_FOOTER_MANGLED){
3818 *mangled &= ~ BUILDER_FOOTER_MANGLED;
3819 sgarbk = TRUE;
3820 pclear(term.t_nrow-1, term.t_nrow);
3823 if(retval >= 0){
3824 if(strcmp(sbuf, s)){
3825 line = entry->hd_text;
3826 InitEntryText(s, entry); /* arrange new one */
3827 zotentry(line); /* blast old list o'entries */
3828 entry->dirty = 1; /* mark it dirty */
3829 retval = 1;
3832 for(e = entry->affected_entry, arg = headarg;
3834 e = e->next_affected, arg = arg ? arg->next : NULL){
3835 if(!e->sticky){
3836 line = e->hd_text;
3837 tmp = ucs4_to_utf8_cpystr(line->text);
3838 if(strcmp(tmp, arg ? arg->tptr : "")){ /* it changed */
3839 /* make sure they see it if changed */
3840 e->display_it = 1;
3841 InitEntryText(arg ? arg->tptr : "", e);
3842 if(line == ods.top_l)
3843 ods.top_l = e->hd_text;
3845 zotentry(line); /* blast old list o'entries */
3846 e->dirty = 1; /* mark it dirty */
3847 retval = 1;
3850 if(tmp)
3851 fs_give((void **) &tmp);
3856 if(s)
3857 free(s);
3859 for(arg = headarg; arg; arg = nextarg){
3860 /* Don't free xtra or me, they just point to headerentry data */
3861 nextarg = arg->next;
3862 if(arg->tptr)
3863 free(arg->tptr);
3865 free(arg);
3868 free(sbuf);
3869 return(retval);
3873 void
3874 call_expander(void)
3876 char **s = NULL;
3877 VARS_TO_SAVE *saved_state;
3878 int expret;
3880 if(!Pmaster->expander)
3881 return;
3884 * Since expander may make a call back to pico() we need to
3885 * save and restore pico state.
3887 if((saved_state = save_pico_state()) != NULL){
3889 expret = (*Pmaster->expander)(headents, &s);
3891 restore_pico_state(saved_state);
3892 free_pico_state(saved_state);
3893 ttresize();
3894 picosigs();
3896 if(expret > 0 && s){
3897 char *tbuf, *p;
3898 int i, biggest = 100;
3899 struct headerentry *e;
3902 * Use tbuf to cat together multiple line entries before comparing.
3904 tbuf = (char *)malloc((biggest+1) * sizeof(*tbuf));
3905 for(e = headents, i=0; e->name != NULL; e++,i++){
3906 int sz = 0;
3907 struct hdr_line *line;
3909 while(e->name && e->blank)
3910 e++;
3912 if(e->name == NULL)
3913 continue;
3915 for(line = e->hd_text; line != NULL; line = line->next){
3916 p = ucs4_to_utf8_cpystr(line->text);
3917 if(p){
3918 sz += strlen(p);
3919 fs_give((void **) &p);
3923 if(sz > biggest){
3924 biggest = sz;
3925 free(tbuf);
3926 tbuf = (char *)malloc((biggest+1) * sizeof(*tbuf));
3929 tbuf[0] = '\0';
3930 for(line = e->hd_text; line != NULL; line = line->next){
3931 p = ucs4_to_utf8_cpystr(line->text);
3932 if(p){
3933 strncat(tbuf, p, biggest+1-strlen(tbuf)-1);
3934 tbuf[biggest] = '\0';
3935 fs_give((void **) &p);
3939 if(strcmp(tbuf, s[i])){ /* it changed */
3940 struct hdr_line *zline;
3942 line = zline = e->hd_text;
3943 InitEntryText(s[i], e);
3946 * If any of the lines for this entry are current or
3947 * top, fix that.
3949 for(; line != NULL; line = line->next){
3950 if(line == ods.top_l)
3951 ods.top_l = e->hd_text;
3953 if(line == ods.cur_l)
3954 ods.cur_l = e->hd_text;
3957 zotentry(zline); /* blast old list o'entries */
3961 free(tbuf);
3964 if(s){
3965 char **p;
3967 for(p = s; *p; p++)
3968 free(*p);
3970 free(s);
3974 return;
3979 * strend - neglecting white space, returns TRUE if c is at the
3980 * end of the given line. otherwise FALSE.
3983 strend(UCS *s, UCS ch)
3985 UCS *b;
3987 if(s == NULL || *s == '\0')
3988 return(FALSE);
3990 for(b = &s[ucs4_strlen(s)] - 1; *b && ucs4_isspace(*b); b--){
3991 if(b == s)
3992 return(FALSE);
3995 return(*b == ch);
4000 * ucs4_strqchr - returns pointer to first non-quote-enclosed occurance of ch in
4001 * the given string. otherwise NULL.
4002 * s -- the string
4003 * ch -- the character we're looking for
4004 * q -- q tells us if we start out inside quotes on entry and is set
4005 * correctly on exit.
4006 * m -- max characters we'll check for ch (set to -1 for no max)
4008 UCS *
4009 ucs4_strqchr(UCS *s, UCS ch, int *q, int m)
4011 int quoted = (q) ? *q : 0;
4013 for(; s && *s && m != 0; s++, m--){
4014 if(*s == '"'){
4015 quoted = !quoted;
4016 if(q)
4017 *q = quoted;
4020 if(!quoted && *s == ch)
4021 return(s);
4024 return(NULL);
4029 * KillHeaderLine() - kill a line in the header
4031 * notes:
4032 * This is pretty simple. Just using the emacs kill buffer
4033 * and its accompanying functions to cut the text from lines.
4035 * returns:
4036 * TRUE if hldelete worked
4037 * FALSE otherwise
4040 KillHeaderLine(struct hdr_line *l, int append)
4042 UCS *c;
4043 int i = ods.p_ind;
4044 int nl = TRUE;
4046 if(!append)
4047 kdelete();
4049 c = l->text;
4050 if (gmode & MDDTKILL){
4051 if (c[i] == '\0') /* don't insert a new line after this line*/
4052 nl = FALSE;
4053 /*put to be deleted part into kill buffer */
4054 for (i=ods.p_ind; c[i] != '\0'; i++)
4055 kinsert(c[i]);
4056 }else{
4057 while(*c != '\0') /* splat out the line */
4058 kinsert(*c++);
4061 if (nl)
4062 kinsert('\n'); /* helpful to yank in body */
4064 #ifdef _WINDOWS
4065 mswin_killbuftoclip (kremove);
4066 #endif
4068 if (gmode & MDDTKILL){
4069 if (l->text[0]=='\0'){
4071 if(l->next && l->prev)
4072 ods.cur_l = next_hline(&ods.cur_e, l);
4073 else if(l->prev)
4074 ods.cur_l = prev_hline(&ods.cur_e, l);
4076 if(l == ods.top_l)
4077 ods.top_l = ods.cur_l;
4079 return(hldelete(l));
4081 else {
4082 l->text[ods.p_ind]='\0'; /* delete part of the line from the cursor */
4083 return(TRUE);
4085 }else{
4086 if(l->next && l->prev)
4087 ods.cur_l = next_hline(&ods.cur_e, l);
4088 else if(l->prev)
4089 ods.cur_l = prev_hline(&ods.cur_e, l);
4091 if(l == ods.top_l)
4092 ods.top_l = ods.cur_l;
4094 return(hldelete(l)); /* blast it */
4101 * SaveHeaderLines() - insert the saved lines in the list before the
4102 * current line in the header
4104 * notes:
4105 * Once again, just using emacs' kill buffer and its
4106 * functions.
4108 * returns:
4109 * TRUE if something good happened
4110 * FALSE otherwise
4113 SaveHeaderLines(void)
4115 UCS *buf; /* malloc'd copy of buffer */
4116 UCS *bp; /* pointer to above buffer */
4117 register unsigned i; /* index */
4118 UCS *work_buf, *work_buf_begin;
4119 char empty[1];
4120 int len, buf_len, work_buf_len, tentative_p_ind = 0;
4121 struct hdr_line *travel, *tentative_cur_l = NULL;
4123 if(ksize()){
4124 if((bp = buf = (UCS *) malloc((ksize()+5) * sizeof(*buf))) == NULL){
4125 emlwrite("Can't malloc space for saved text", NULL);
4126 return(FALSE);
4129 else
4130 return(FALSE);
4132 for(i=0; i < ksize(); i++)
4133 if(kremove(i) != '\n') /* filter out newlines */
4134 *bp++ = (UCS) kremove(i);
4136 *bp = '\0';
4138 while(--bp >= buf) /* kill trailing white space */
4139 if(*bp != ' '){
4140 if(ods.cur_l->text[0] != '\0'){
4141 if(*bp == '>'){ /* inserting an address */
4142 *++bp = ','; /* so add separator */
4143 *++bp = '\0';
4146 else{ /* nothing in field yet */
4147 if(*bp == ','){ /* so blast any extra */
4148 *bp = '\0'; /* separators */
4151 break;
4154 /* insert new text at the dot position */
4155 buf_len = ucs4_strlen(buf);
4156 tentative_p_ind = ods.p_ind + buf_len;
4157 work_buf_len = ucs4_strlen(ods.cur_l->text) + buf_len;
4158 work_buf = (UCS *) malloc((work_buf_len + 1) * sizeof(UCS));
4159 if (work_buf == NULL) {
4160 emlwrite("Can't malloc space for saved text", NULL);
4161 return(FALSE);
4164 work_buf[0] = '\0';
4165 work_buf_begin = work_buf;
4166 i = MIN(ods.p_ind, work_buf_len);
4167 ucs4_strncpy(work_buf, ods.cur_l->text, i);
4168 work_buf[i] = '\0';
4169 ucs4_strncat(work_buf, buf, work_buf_len+1-ucs4_strlen(work_buf)-1);
4170 work_buf[work_buf_len] = '\0';
4171 ucs4_strncat(work_buf, &ods.cur_l->text[ods.p_ind], work_buf_len+1-ucs4_strlen(work_buf)-1);
4172 work_buf[work_buf_len] = '\0';
4173 empty[0]='\0';
4174 ods.p_ind = 0;
4176 i = TRUE;
4178 /* insert text in HLSZ character chunks */
4179 while(work_buf_len + ods.p_ind > HLSZ) {
4180 ucs4_strncpy(&ods.cur_l->text[ods.p_ind], work_buf, HLSZ-ods.p_ind);
4181 work_buf += (HLSZ - ods.p_ind);
4182 work_buf_len -= (HLSZ - ods.p_ind);
4184 if(FormatLines(ods.cur_l, empty, LINEWID(),
4185 headents[ods.cur_e].break_on_comma, 0) == -1) {
4186 i = FALSE;
4187 break;
4188 } else {
4189 i = TRUE;
4190 len = 0;
4191 travel = ods.cur_l;
4192 while (len < HLSZ){
4193 len += ucs4_strlen(travel->text);
4194 if (len >= HLSZ)
4195 break;
4198 * This comes after the break above because it will
4199 * be accounted for in the while loop below.
4201 if(!tentative_cur_l){
4202 if(tentative_p_ind <= ucs4_strlen(travel->text))
4203 tentative_cur_l = travel;
4204 else
4205 tentative_p_ind -= ucs4_strlen(travel->text);
4208 travel = travel->next;
4211 ods.cur_l = travel;
4212 ods.p_ind = ucs4_strlen(travel->text) - len + HLSZ;
4216 /* insert the remainder of text */
4217 if (i != FALSE && work_buf_len > 0) {
4218 ucs4_strncpy(&ods.cur_l->text[ods.p_ind], work_buf, HLSZ-ods.p_ind);
4219 ods.cur_l->text[HLSZ-1] = '\0';
4220 work_buf = work_buf_begin;
4221 free(work_buf);
4223 if(FormatLines(ods.cur_l, empty, LINEWID(),
4224 headents[ods.cur_e].break_on_comma, 0) == -1) {
4225 i = FALSE;
4226 } else {
4227 len = 0;
4228 travel = ods.cur_l;
4229 while (len < work_buf_len + ods.p_ind){
4230 if(!tentative_cur_l){
4231 if(tentative_p_ind <= ucs4_strlen(travel->text))
4232 tentative_cur_l = travel;
4233 else
4234 tentative_p_ind -= ucs4_strlen(travel->text);
4237 len += ucs4_strlen(travel->text);
4238 if (len >= work_buf_len + ods.p_ind)
4239 break;
4241 travel = travel->next;
4244 ods.cur_l = travel;
4245 ods.p_ind = ucs4_strlen(travel->text) - len + work_buf_len + ods.p_ind;
4246 if(tentative_cur_l
4247 && tentative_p_ind >= 0
4248 && tentative_p_ind <= ucs4_strlen(tentative_cur_l->text)){
4249 ods.cur_l = tentative_cur_l;
4250 ods.p_ind = tentative_p_ind;
4255 free(buf);
4256 return(i);
4262 * break_point - Break the given line at the most reasonable character breakch
4263 * within maxwid max characters.
4265 * returns:
4266 * Pointer to the best break point in s, or
4267 * Pointer to the beginning of s if no break point found
4269 UCS *
4270 break_point(UCS *line, int maxwid, UCS breakch, int *quotedarg)
4272 UCS *bp; /* break point */
4273 int quoted;
4276 * Start at maxwid and work back until first opportunity to break.
4278 bp = ucs4_particular_width(line, maxwid);
4281 * Quoted should be set up for the start of line. Since we want
4282 * to move to bp and work our way back we need to scan through the
4283 * line up to bp setting quoted appropriately.
4285 if(quotedarg)
4286 ucs4_strqchr(line, '\0', quotedarg, bp-line);
4288 quoted = quotedarg ? *quotedarg : 0;
4290 while(bp != line){
4291 if(breakch == ',' && *bp == '"') /* don't break on quoted ',' */
4292 quoted = !quoted; /* toggle quoted state */
4294 if(*bp == breakch && !quoted){
4295 if(breakch == ' '){
4296 if(ucs4_str_width_ptr_to_ptr(line, bp+1) < maxwid){
4297 bp++; /* leave the ' ' */
4298 break;
4301 else{
4303 * if break char isn't a space, leave a space after
4304 * the break char.
4306 if(!(ucs4_str_width_ptr_to_ptr(line, bp+1) >= maxwid
4307 || (bp[1] == ' ' && ucs4_str_width_ptr_to_ptr(line, bp+2) >= maxwid))){
4308 bp += (bp[1] == ' ') ? 2 : 1;
4309 break;
4314 bp--;
4317 if(quotedarg)
4318 *quotedarg = quoted;
4320 return((quoted) ? line : bp);
4327 * hldelete() - remove the header line pointed to by l from the linked list
4328 * of lines.
4330 * notes:
4331 * the case of first line in field is kind of bogus. since
4332 * the array of headers has a pointer to the first line, and
4333 * i don't want to worry about this too much, i just copied
4334 * the line below and removed it rather than the first one
4335 * from the list.
4337 * returns:
4338 * TRUE if it worked
4339 * FALSE otherwise
4342 hldelete(struct hdr_line *l)
4344 register struct hdr_line *lp;
4346 if(l == NULL)
4347 return(FALSE);
4349 if(l->next == NULL && l->prev == NULL){ /* only one line in field */
4350 l->text[0] = '\0';
4351 return(TRUE); /* no free only line in list */
4353 else if(l->next == NULL){ /* last line in field */
4354 l->prev->next = NULL;
4356 else if(l->prev == NULL){ /* first line in field */
4357 ucs4_strncpy(l->text, l->next->text, HLSZ);
4358 l->text[HLSZ-1] = '\0';
4359 lp = l->next;
4360 if((l->next = lp->next) != NULL)
4361 l->next->prev = l;
4362 l = lp;
4364 else{ /* some where in field */
4365 l->prev->next = l->next;
4366 l->next->prev = l->prev;
4369 l->next = NULL;
4370 l->prev = NULL;
4371 free((char *)l);
4372 return(TRUE);
4378 * is_blank - returns true if the next n chars from coordinates row, col
4379 * on display are spaces
4382 is_blank(int row, int col, int n)
4384 n += col;
4385 for( ;col < n; col++){
4386 if(pscr(row, col) == NULL || pscr(row, col)->c != ' ')
4387 return(0);
4389 return(1);
4394 * ShowPrompt - display key help corresponding to the current header entry
4396 void
4397 ShowPrompt(void)
4399 if(headents[ods.cur_e].key_label){
4400 menu_header[TO_KEY].name = "^T";
4401 menu_header[TO_KEY].label = headents[ods.cur_e].key_label;
4402 KS_OSDATASET(&menu_header[TO_KEY], KS_OSDATAGET(&headents[ods.cur_e]));
4404 else
4405 menu_header[TO_KEY].name = NULL;
4407 if(Pmaster && Pmaster->exit_label)
4408 menu_header[SEND_KEY].label = Pmaster->exit_label;
4409 else if(gmode & (MDVIEW | MDHDRONLY))
4410 menu_header[SEND_KEY].label = (gmode & MDHDRONLY) ? "eXit/Save" : "eXit";
4411 else
4412 menu_header[SEND_KEY].label = N_("Send");
4414 if(gmode & MDVIEW){
4415 menu_header[CUT_KEY].name = NULL;
4416 menu_header[DEL_KEY].name = NULL;
4417 menu_header[UDEL_KEY].name = NULL;
4419 else{
4420 menu_header[CUT_KEY].name = "^K";
4421 menu_header[DEL_KEY].name = "^D";
4422 menu_header[UDEL_KEY].name = "^U";
4425 if(Pmaster->ctrlr_label){
4426 menu_header[RICH_KEY].label = Pmaster->ctrlr_label;
4427 menu_header[RICH_KEY].name = "^R";
4429 else if(gmode & MDHDRONLY){
4430 menu_header[RICH_KEY].name = NULL;
4432 else{
4433 menu_header[RICH_KEY].label = N_("Rich Hdr");
4434 menu_header[RICH_KEY].name = "^R";
4437 if(gmode & MDHDRONLY){
4438 if(headents[ods.cur_e].fileedit){
4439 menu_header[PONE_KEY].name = "^_";
4440 menu_header[PONE_KEY].label = N_("Edit File");
4442 else
4443 menu_header[PONE_KEY].name = NULL;
4445 menu_header[ATT_KEY].name = NULL;
4447 else{
4448 menu_header[PONE_KEY].name = "^O";
4449 menu_header[PONE_KEY].label = N_("Postpone");
4451 menu_header[ATT_KEY].name = "^J";
4454 wkeyhelp(menu_header);
4459 * packheader - packup all of the header fields for return to caller.
4460 * NOTE: all of the header info passed in, including address
4461 * of the pointer to each string is contained in the
4462 * header entry array "headents".
4465 packheader(void)
4467 register int i = 0; /* array index */
4468 register int count; /* count of chars in a field */
4469 register int retval = TRUE;
4470 register char *bufp;
4471 register struct hdr_line *line;
4472 char *p;
4474 if(!headents)
4475 return(TRUE);
4477 while(headents[i].name != NULL){
4478 #ifdef ATTACHMENTS
4480 * attachments are special case, already in struct we pass back
4482 if(headents[i].is_attach){
4483 i++;
4484 continue;
4486 #endif
4488 if(headents[i].blank){
4489 i++;
4490 continue;
4494 * count chars to see if we need a new malloc'd space for our
4495 * array.
4497 line = headents[i].hd_text;
4498 count = 0;
4499 while(line != NULL){
4501 * add one for possible concatenation of a ' ' character ...
4503 p = ucs4_to_utf8_cpystr(line->text);
4504 if(p){
4505 count += strlen(p);
4506 if(p[0] && p[strlen(p)-1] == ',')
4507 count++;
4509 fs_give((void **) &p);
4512 line = line->next;
4515 line = headents[i].hd_text;
4516 if(count <= headents[i].maxlen){
4517 *headents[i].realaddr[0] = '\0';
4519 else{
4521 * don't forget to include space for the null terminator!!!!
4523 if((bufp = (char *)malloc((count+1) * sizeof(char))) != NULL){
4524 *bufp = '\0';
4526 free(*headents[i].realaddr);
4527 *headents[i].realaddr = bufp;
4528 headents[i].maxlen = count;
4530 else{
4531 emlwrite("Can't make room to pack header field.", NULL);
4532 retval = FALSE;
4536 if(retval != FALSE){
4537 int saw_current_line = 0;
4539 while(line != NULL){
4541 /* pass the cursor offset back in Pmaster struct */
4542 if(headents[i].start_here && Pmaster && !saw_current_line){
4543 if(ods.cur_l == line)
4544 saw_current_line++;
4545 else
4546 Pmaster->edit_offset += ucs4_strlen(line->text);
4549 p = ucs4_to_utf8_cpystr(line->text);
4550 if(p){
4551 strncat(*headents[i].realaddr, p, headents[i].maxlen+1-strlen(*headents[i].realaddr)-1);
4552 (*headents[i].realaddr)[headents[i].maxlen] = '\0';
4554 if(p[0] && p[strlen(p)-1] == ','){
4555 strncat(*headents[i].realaddr, " ", headents[i].maxlen+1-strlen(*headents[i].realaddr)-1);
4556 (*headents[i].realaddr)[headents[i].maxlen] = '\0';
4559 fs_give((void **) &p);
4562 line = line->next;
4566 i++;
4569 return(retval);
4575 * zotheader - free all malloc'd lines associated with the header structs
4577 void
4578 zotheader(void)
4580 register struct headerentry *i;
4582 for(i = headents; headents && i->name; i++)
4583 zotentry(i->hd_text);
4588 * zotentry - free malloc'd space associated with the given linked list
4590 void
4591 zotentry(struct hdr_line *l)
4593 register struct hdr_line *ld, *lf = l;
4595 while((ld = lf) != NULL){
4596 lf = ld->next;
4597 ld->next = ld->prev = NULL;
4598 free((char *) ld);
4605 * zotcomma - blast any trailing commas and white space from the end
4606 * of the given line
4609 zotcomma(UCS *s)
4611 UCS *p;
4612 int retval = FALSE;
4614 p = &s[ucs4_strlen(s)];
4615 while(--p >= s){
4616 if(*p != ' '){
4617 if(*p == ','){
4618 *p = '\0';
4619 retval = TRUE;
4622 return(retval);
4626 return(retval);
4631 * Save the current state of global variables so that we can restore
4632 * them later. This is so we can call pico again.
4633 * Also have to initialize some variables that normally would be set to
4634 * zero on startup.
4636 VARS_TO_SAVE *
4637 save_pico_state(void)
4639 VARS_TO_SAVE *ret;
4640 extern int vtrow;
4641 extern int vtcol;
4642 extern int lbound;
4643 extern VIDEO **vscreen;
4644 extern VIDEO **pscreen;
4645 extern int pico_all_done;
4646 extern jmp_buf finstate;
4647 extern UCS *pico_anchor;
4649 if((ret = (VARS_TO_SAVE *)malloc(sizeof(VARS_TO_SAVE))) == NULL)
4650 return(ret);
4652 ret->vtrow = vtrow;
4653 ret->vtcol = vtcol;
4654 ret->lbound = lbound;
4655 ret->vscreen = vscreen;
4656 ret->pscreen = pscreen;
4657 ret->ods = ods;
4658 ret->delim_ps = delim_ps;
4659 ret->invert_ps = invert_ps;
4660 ret->pico_all_done = pico_all_done;
4661 memcpy(ret->finstate, finstate, sizeof(jmp_buf));
4662 ret->pico_anchor = pico_anchor;
4663 ret->Pmaster = Pmaster;
4664 ret->fillcol = fillcol;
4665 if((ret->pat = (UCS *)malloc(sizeof(UCS) * (ucs4_strlen(pat)+1))) != NULL)
4666 ucs4_strncpy(ret->pat, pat, ucs4_strlen(pat)+1);
4668 ret->ComposerTopLine = ComposerTopLine;
4669 ret->ComposerEditing = ComposerEditing;
4670 ret->gmode = gmode;
4671 ret->alt_speller = alt_speller;
4672 ret->quote_str = glo_quote_str;
4673 ret->wordseps = glo_wordseps;
4674 ret->currow = currow;
4675 ret->curcol = curcol;
4676 ret->thisflag = thisflag;
4677 ret->lastflag = lastflag;
4678 ret->curgoal = curgoal;
4679 ret->opertree = (char *) malloc(sizeof(char) * (strlen(opertree) + 1));
4680 if(ret->opertree != NULL)
4681 strncpy(ret->opertree, opertree, strlen(opertree)+1);
4683 ret->curwp = curwp;
4684 ret->wheadp = wheadp;
4685 ret->curbp = curbp;
4686 ret->bheadp = bheadp;
4687 ret->km_popped = km_popped;
4688 ret->mrow = term.t_mrow;
4690 /* Initialize for next pico call */
4691 wheadp = NULL;
4692 curwp = NULL;
4693 bheadp = NULL;
4694 curbp = NULL;
4696 return(ret);
4700 void
4701 restore_pico_state(VARS_TO_SAVE *state)
4703 extern int vtrow;
4704 extern int vtcol;
4705 extern int lbound;
4706 extern VIDEO **vscreen;
4707 extern VIDEO **pscreen;
4708 extern int pico_all_done;
4709 extern jmp_buf finstate;
4710 extern UCS *pico_anchor;
4712 clearcursor();
4713 vtrow = state->vtrow;
4714 vtcol = state->vtcol;
4715 lbound = state->lbound;
4716 vscreen = state->vscreen;
4717 pscreen = state->pscreen;
4718 ods = state->ods;
4719 delim_ps = state->delim_ps;
4720 invert_ps = state->invert_ps;
4721 pico_all_done = state->pico_all_done;
4722 memcpy(finstate, state->finstate, sizeof(jmp_buf));
4723 pico_anchor = state->pico_anchor;
4724 Pmaster = state->Pmaster;
4725 if(Pmaster)
4726 headents = Pmaster->headents;
4728 fillcol = state->fillcol;
4729 if(state->pat)
4730 ucs4_strncpy(pat, state->pat, NPAT);
4732 ComposerTopLine = state->ComposerTopLine;
4733 ComposerEditing = state->ComposerEditing;
4734 gmode = state->gmode;
4735 alt_speller = state->alt_speller;
4736 glo_quote_str = state->quote_str;
4737 glo_wordseps = state->wordseps;
4738 currow = state->currow;
4739 curcol = state->curcol;
4740 thisflag = state->thisflag;
4741 lastflag = state->lastflag;
4742 curgoal = state->curgoal;
4743 if(state->opertree){
4744 strncpy(opertree, state->opertree, sizeof(opertree));
4745 opertree[sizeof(opertree)-1] = '\0';
4748 curwp = state->curwp;
4749 wheadp = state->wheadp;
4750 curbp = state->curbp;
4751 bheadp = state->bheadp;
4752 km_popped = state->km_popped;
4753 term.t_mrow = state->mrow;
4757 void
4758 free_pico_state(VARS_TO_SAVE *state)
4760 if(state->pat)
4761 free(state->pat);
4763 if(state->opertree)
4764 free(state->opertree);
4766 free(state);
4771 * Ok to call this twice in a row because it won't do anything the second
4772 * time.
4774 void
4775 fix_mangle_and_err(int *mangled, char **errmsg, char *name)
4777 if(mangled && *mangled){
4778 ttresize();
4779 picosigs();
4780 PaintBody(0);
4781 *mangled = 0;
4784 if(errmsg && *errmsg){
4785 if(**errmsg){
4786 char err[500];
4788 snprintf(err, sizeof(err), "%s field: %s", name, *errmsg);
4789 (*term.t_beep)();
4790 emlwrite(err, NULL);
4792 else
4793 mlerase();
4795 free(*errmsg);
4796 *errmsg = NULL;
4802 * What is this for?
4803 * This is so that the To line will be appended to by an Lcc
4804 * entry unless the user types in the To line after the Lcc
4805 * has already been set.
4807 void
4808 mark_sticky(struct headerentry *h)
4810 if(h && (!h->sticky_special || h->bldr_private))
4811 h->sticky = 1;
4815 #ifdef MOUSE
4816 #undef HeaderEditor
4819 * Wrapper function for the real header editor.
4820 * Does the important tasks of:
4821 * 1) verifying that we _can_ edit the headers.
4822 * 2) acting on the result code from the header editor.
4825 HeaderEditor(int f, int n)
4827 int retval;
4830 #ifdef _WINDOWS
4831 /* Sometimes we get here from a scroll callback, which
4832 * is no good at all because mswin is not ready to process input and
4833 * this _headeredit() will never do anything.
4834 * Putting this test here was the most general solution I could think
4835 * of. */
4836 if (!mswin_caninput())
4837 return (-1);
4838 #endif
4840 retval = HeaderEditorWork(f, n);
4841 if (retval == -3) {
4842 retval = mousepress(0,0);
4844 return (retval);
4846 #endif