* Fix of the list of files in the browser for terminals that need to erase
[alpine.git] / pico / composer.c
blob5c538de14ac623184a7783002a29d4f31f4e9fc0
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-2021 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);
3408 else if (*term.t_eri)
3409 (*term.t_eri)();
3411 while(*bufp != '\0')
3412 pputc(*bufp++, state ? 1 : 0);
3414 if(state)
3415 (*term.t_rev)(0);
3417 peeol();
3418 fs_give((void **) &buf);
3420 if(lastc){
3421 pico_set_colorp(lastc, PSC_NONE);
3422 free_color_pair(&lastc);
3429 * InvertPrompt() - invert the prompt associated with header entry to state
3430 * state (true if invert, false otherwise).
3431 * returns:
3432 * non-zero if nothing done
3433 * 0 if prompt inverted successfully
3435 * notes:
3436 * come to think of it, this func and the one above could
3437 * easily be combined
3440 InvertPrompt(int entry, int state)
3442 UCS *buf, *bufp;
3443 UCS *end;
3444 int i;
3446 buf = utf8_to_ucs4_cpystr(headents[entry].prompt); /* fresh prompt paint */
3447 if(!buf)
3448 return(-1);
3450 bufp = buf;
3451 if((i = entry_line(entry, FALSE)) == -1){
3452 fs_give((void **) &buf);
3453 return(-1); /* silently forget it */
3456 end = buf + ucs4_strlen(buf);
3459 * Makes sure that the prompt doesn't take up more than prwid of screen space.
3460 * The caller should do that, too, in order to make it look right so
3461 * this should most likely be a no-op
3463 if(ucs4_str_width_ptr_to_ptr(buf, end) > headents[entry].prwid){
3464 end = ucs4_particular_width(buf, headents[entry].prwid);
3465 *end = '\0';
3468 if(entry < 16 && (invert_ps&(1<<entry))
3469 == (state ? 1<<entry : 0)){ /* optimize ? */
3470 int j;
3472 for(j = 0; bufp[j] && pscr(i, j)->c == bufp[j]; j++)
3475 if(bufp[j] == '\0'){
3476 if(state)
3477 invert_ps |= 1<<entry;
3478 else
3479 invert_ps &= ~(1<<entry);
3481 fs_give((void **) &buf);
3482 return(0); /* already displayed! */
3486 if(entry < 16){ /* if > 16, cannot be stored in invert_ps */
3487 if(state)
3488 invert_ps |= 1<<entry;
3489 else
3490 invert_ps &= ~(1<<entry);
3493 movecursor(i, 0);
3494 if(state)
3495 (*term.t_rev)(1);
3496 else if (*term.t_eri)
3497 (*term.t_eri)();
3499 while(*bufp && *(bufp + 1))
3500 pputc(*bufp++, 1); /* putc up to last char */
3502 if(state)
3503 (*term.t_rev)(0);
3505 pputc(*bufp, 0); /* last char not inverted */
3507 fs_give((void **) &buf);
3509 return(TRUE);
3515 * partial_entries() - toggle display of the bcc and fcc fields.
3517 * returns:
3518 * TRUE if there are partial entries on the display
3519 * FALSE otherwise.
3522 partial_entries(void)
3524 register struct headerentry *h;
3525 int is_on;
3527 /*---- find out status of first rich header ---*/
3528 for(h = headents; !h->rich_header && h->name != NULL; h++)
3531 is_on = h->display_it;
3532 for(h = headents; h->name != NULL; h++)
3533 if(h->rich_header)
3534 h->display_it = ! is_on;
3536 return(is_on);
3542 * entry_line() - return the physical line on the screen associated
3543 * with the given header entry field. Note: the field
3544 * may span lines, so if the last char is set, return
3545 * the appropriate value.
3547 * returns:
3548 * 1) physical line number of entry
3549 * 2) -1 if entry currently not on display
3552 entry_line(int entry, int lastchar)
3554 register int p_line = COMPOSER_TOP_LINE;
3555 int i;
3556 register struct hdr_line *line;
3558 for(line = ods.top_l, i = ods.top_e;
3559 headents && headents[i].name && i <= entry;
3560 p_line++){
3561 if(p_line >= BOTTOM())
3562 break;
3563 if(i == entry){
3564 if(lastchar){
3565 if(line->next == NULL)
3566 return(p_line);
3568 else if(line->prev == NULL)
3569 return(p_line);
3570 else
3571 return(-1);
3573 line = next_hline(&i, line);
3575 return(-1);
3581 * physical_line() - return the physical line on the screen associated
3582 * with the given header line pointer.
3584 * returns:
3585 * 1) physical line number of entry
3586 * 2) -1 if entry currently not on display
3589 physical_line(struct hdr_line *l)
3591 register int p_line = COMPOSER_TOP_LINE;
3592 register struct hdr_line *lp;
3593 int i;
3595 for(lp=ods.top_l, i=ods.top_e; headents[i].name && lp != NULL; p_line++){
3596 if(p_line >= BOTTOM())
3597 break;
3599 if(lp == l)
3600 return(p_line);
3602 lp = next_hline(&i, lp);
3604 return(-1);
3610 * call_builder() - resolve any nicknames in the address book associated
3611 * with the given entry...
3613 * NOTES:
3615 * BEWARE: this function can cause cur_l and top_l to get lost so BE
3616 * CAREFUL before and after you call this function!!!
3618 * There could to be something here to resolve cur_l and top_l
3619 * reasonably into the new linked list for this entry.
3621 * The reason this would mostly work without it is resolve_niks gets
3622 * called for the most part in between fields. Since we're moving
3623 * to the beginning or end (i.e. the next/prev pointer in the old
3624 * freed cur_l is NULL) of the next entry, we get a new cur_l
3625 * pointing at a good line. Then since top_l is based on cur_l in
3626 * NewTop() we have pretty much lucked out.
3628 * Where we could get burned is in a canceled exit (ctrl|x). Here
3629 * nicknames get resolved into addresses, which invalidates cur_l
3630 * and top_l. Since we don't actually leave, we could begin editing
3631 * again with bad pointers. This would usually results in a nice
3632 * core dump.
3634 * NOTE: The mangled argument is a little strange. It's used on both
3635 * input and output. On input, if it is not set, then that tells the
3636 * builder not to do anything that might take a long time, like a
3637 * white pages lookup. On return, it tells the caller that the screen
3638 * and signals may have been mangled so signals should be reset, window
3639 * resized, and screen redrawn.
3641 * RETURNS:
3642 * > 0 if any names where resolved, otherwise
3643 * 0 if not, or
3644 * < 0 on error
3645 * -1: move to next line
3646 * -2: don't move off this line
3649 call_builder(struct headerentry *entry, int *mangled, char **err)
3651 register int retval = 0;
3652 register int i;
3653 register struct hdr_line *line;
3654 int quoted = 0;
3655 int sbuflen;
3656 char *sbuf;
3657 char *s = NULL;
3658 char *tmp;
3659 struct headerentry *e;
3660 BUILDER_ARG *nextarg, *arg = NULL, *headarg = NULL;
3661 VARS_TO_SAVE *saved_state;
3663 if(!entry->builder)
3664 return(0);
3666 line = entry->hd_text;
3667 sbuflen = 0;
3668 while(line != NULL){
3669 sbuflen += (6*term.t_ncol);
3670 line = line->next;
3672 sbuflen++;
3673 if((sbuf=(char *)malloc(sbuflen * sizeof(*sbuf))) == NULL){
3674 emlwrite("Can't malloc space to expand address", NULL);
3675 return(-1);
3678 *sbuf = '\0';
3681 * cat the whole entry into one string...
3683 line = entry->hd_text;
3684 while(line != NULL){
3685 i = ucs4_strlen(line->text);
3688 * To keep pine address builder happy, addresses should be separated
3689 * by ", ". Add this space if needed, otherwise...
3690 * (This is some ancient requirement that is no longer needed.)
3692 * If this line is NOT a continuation of the previous line, add
3693 * white space for pine's address builder if its not already there...
3694 * (This is some ancient requirement that is no longer needed.)
3696 * Also if it's not a continuation (i.e., there's already and addr on
3697 * the line), and there's another line below, treat the new line as
3698 * an implied comma.
3699 * (This should only be done for address-type lines, not for regular
3700 * text lines like subjects. Key off of the break_on_comma bit which
3701 * should only be set on those that won't mind a comma being added.)
3703 if(entry->break_on_comma){
3704 UCS *space, commaspace[3];
3706 commaspace[0] = ',';
3707 commaspace[1] = ' ';
3708 commaspace[2] = '\0';
3709 space = commaspace+1;
3711 if(i && line->text[i-1] == ','){
3712 ucs4_strncat(line->text, space, HLSZ-i-1); /* help address builder */
3713 line->text[HLSZ-1] = '\0';
3715 else if(line->next != NULL && !strend(line->text, ',')){
3716 if(ucs4_strqchr(line->text, ',', &quoted, -1)){
3717 ucs4_strncat(line->text, commaspace, HLSZ-i-1); /* implied comma */
3718 line->text[HLSZ-1] = '\0';
3721 else if(line->prev != NULL && line->next != NULL){
3722 if(ucs4_strchr(line->prev->text, ' ') != NULL
3723 && line->text[i-1] != ' '){
3724 ucs4_strncat(line->text, space, HLSZ-i-1);
3725 line->text[HLSZ-1] = '\0';
3730 tmp = ucs4_to_utf8_cpystr(line->text);
3731 if(tmp){
3732 strncat(sbuf, tmp, sbuflen-strlen(sbuf)-1);
3733 sbuf[sbuflen-1] = '\0';
3734 fs_give((void **) &tmp);
3737 line = line->next;
3740 if(entry->affected_entry){
3741 /* check if any non-sticky affected entries */
3742 for(e = entry->affected_entry; e; e = e->next_affected)
3743 if(!e->sticky)
3744 break;
3746 /* there is at least one non-sticky so make a list to pass */
3747 if(e){
3748 for(e = entry->affected_entry; e; e = e->next_affected){
3749 if(!arg){
3750 headarg = arg = (BUILDER_ARG *)malloc(sizeof(BUILDER_ARG));
3751 if(!arg){
3752 emlwrite("Can't malloc space for fcc", NULL);
3753 return(-1);
3755 else{
3756 arg->next = NULL;
3757 arg->tptr = NULL;
3758 arg->aff = &(e->bldr_private);
3759 arg->me = &(entry->bldr_private);
3762 else{
3763 nextarg = (BUILDER_ARG *)malloc(sizeof(BUILDER_ARG));
3764 if(!nextarg){
3765 emlwrite("Can't malloc space for fcc", NULL);
3766 return(-1);
3768 else{
3769 nextarg->next = NULL;
3770 nextarg->tptr = NULL;
3771 nextarg->aff = &(e->bldr_private);
3772 nextarg->me = &(entry->bldr_private);
3773 arg->next = nextarg;
3774 arg = arg->next;
3778 if(!e->sticky){
3779 line = e->hd_text;
3780 arg->tptr = ucs4_to_utf8_cpystr(line->text);
3787 * Even if there are no affected entries, we still need the arg
3788 * to pass the "me" pointer.
3790 if(!headarg){
3791 headarg = (BUILDER_ARG *)malloc(sizeof(BUILDER_ARG));
3792 if(!headarg){
3793 emlwrite("Can't malloc space", NULL);
3794 return(-1);
3796 else{
3797 headarg->next = NULL;
3798 headarg->tptr = NULL;
3799 headarg->aff = NULL;
3800 headarg->me = &(entry->bldr_private);
3805 * The builder may make a new call back to pico() so we save and
3806 * restore the pico state.
3808 saved_state = save_pico_state();
3809 retval = (*entry->builder)(sbuf, &s, err, headarg, mangled);
3810 if(saved_state){
3811 restore_pico_state(saved_state);
3812 free_pico_state(saved_state);
3815 if(mangled && *mangled & BUILDER_MESSAGE_DISPLAYED){
3816 *mangled &= ~ BUILDER_MESSAGE_DISPLAYED;
3817 if(mpresf == FALSE)
3818 mpresf = TRUE;
3821 if(mangled && *mangled & BUILDER_FOOTER_MANGLED){
3822 *mangled &= ~ BUILDER_FOOTER_MANGLED;
3823 sgarbk = TRUE;
3824 pclear(term.t_nrow-1, term.t_nrow);
3827 if(retval >= 0){
3828 if(strcmp(sbuf, s)){
3829 line = entry->hd_text;
3830 InitEntryText(s, entry); /* arrange new one */
3831 zotentry(line); /* blast old list o'entries */
3832 entry->dirty = 1; /* mark it dirty */
3833 retval = 1;
3836 for(e = entry->affected_entry, arg = headarg;
3838 e = e->next_affected, arg = arg ? arg->next : NULL){
3839 if(!e->sticky){
3840 line = e->hd_text;
3841 tmp = ucs4_to_utf8_cpystr(line->text);
3842 if(strcmp(tmp, arg ? arg->tptr : "")){ /* it changed */
3843 /* make sure they see it if changed */
3844 e->display_it = 1;
3845 InitEntryText(arg ? arg->tptr : "", e);
3846 if(line == ods.top_l)
3847 ods.top_l = e->hd_text;
3849 zotentry(line); /* blast old list o'entries */
3850 e->dirty = 1; /* mark it dirty */
3851 retval = 1;
3854 if(tmp)
3855 fs_give((void **) &tmp);
3860 if(s)
3861 free(s);
3863 for(arg = headarg; arg; arg = nextarg){
3864 /* Don't free xtra or me, they just point to headerentry data */
3865 nextarg = arg->next;
3866 if(arg->tptr)
3867 free(arg->tptr);
3869 free(arg);
3872 free(sbuf);
3873 return(retval);
3877 void
3878 call_expander(void)
3880 char **s = NULL;
3881 VARS_TO_SAVE *saved_state;
3882 int expret;
3884 if(!Pmaster->expander)
3885 return;
3888 * Since expander may make a call back to pico() we need to
3889 * save and restore pico state.
3891 if((saved_state = save_pico_state()) != NULL){
3893 expret = (*Pmaster->expander)(headents, &s);
3895 restore_pico_state(saved_state);
3896 free_pico_state(saved_state);
3897 ttresize();
3898 picosigs();
3900 if(expret > 0 && s){
3901 char *tbuf, *p;
3902 int i, biggest = 100;
3903 struct headerentry *e;
3906 * Use tbuf to cat together multiple line entries before comparing.
3908 tbuf = (char *)malloc((biggest+1) * sizeof(*tbuf));
3909 for(e = headents, i=0; e->name != NULL; e++,i++){
3910 int sz = 0;
3911 struct hdr_line *line;
3913 while(e->name && e->blank)
3914 e++;
3916 if(e->name == NULL)
3917 continue;
3919 for(line = e->hd_text; line != NULL; line = line->next){
3920 p = ucs4_to_utf8_cpystr(line->text);
3921 if(p){
3922 sz += strlen(p);
3923 fs_give((void **) &p);
3927 if(sz > biggest){
3928 biggest = sz;
3929 free(tbuf);
3930 tbuf = (char *)malloc((biggest+1) * sizeof(*tbuf));
3933 tbuf[0] = '\0';
3934 for(line = e->hd_text; line != NULL; line = line->next){
3935 p = ucs4_to_utf8_cpystr(line->text);
3936 if(p){
3937 strncat(tbuf, p, biggest+1-strlen(tbuf)-1);
3938 tbuf[biggest] = '\0';
3939 fs_give((void **) &p);
3943 if(strcmp(tbuf, s[i])){ /* it changed */
3944 struct hdr_line *zline;
3946 line = zline = e->hd_text;
3947 InitEntryText(s[i], e);
3950 * If any of the lines for this entry are current or
3951 * top, fix that.
3953 for(; line != NULL; line = line->next){
3954 if(line == ods.top_l)
3955 ods.top_l = e->hd_text;
3957 if(line == ods.cur_l)
3958 ods.cur_l = e->hd_text;
3961 zotentry(zline); /* blast old list o'entries */
3965 free(tbuf);
3968 if(s){
3969 char **p;
3971 for(p = s; *p; p++)
3972 free(*p);
3974 free(s);
3978 return;
3983 * strend - neglecting white space, returns TRUE if c is at the
3984 * end of the given line. otherwise FALSE.
3987 strend(UCS *s, UCS ch)
3989 UCS *b;
3991 if(s == NULL || *s == '\0')
3992 return(FALSE);
3994 for(b = &s[ucs4_strlen(s)] - 1; *b && ucs4_isspace(*b); b--){
3995 if(b == s)
3996 return(FALSE);
3999 return(*b == ch);
4004 * ucs4_strqchr - returns pointer to first non-quote-enclosed occurance of ch in
4005 * the given string. otherwise NULL.
4006 * s -- the string
4007 * ch -- the character we're looking for
4008 * q -- q tells us if we start out inside quotes on entry and is set
4009 * correctly on exit.
4010 * m -- max characters we'll check for ch (set to -1 for no max)
4012 UCS *
4013 ucs4_strqchr(UCS *s, UCS ch, int *q, int m)
4015 int quoted = (q) ? *q : 0;
4017 for(; s && *s && m != 0; s++, m--){
4018 if(*s == '"'){
4019 quoted = !quoted;
4020 if(q)
4021 *q = quoted;
4024 if(!quoted && *s == ch)
4025 return(s);
4028 return(NULL);
4033 * KillHeaderLine() - kill a line in the header
4035 * notes:
4036 * This is pretty simple. Just using the emacs kill buffer
4037 * and its accompanying functions to cut the text from lines.
4039 * returns:
4040 * TRUE if hldelete worked
4041 * FALSE otherwise
4044 KillHeaderLine(struct hdr_line *l, int append)
4046 UCS *c;
4047 int i = ods.p_ind;
4048 int nl = TRUE;
4050 if(!append)
4051 kdelete();
4053 c = l->text;
4054 if (gmode & MDDTKILL){
4055 if (c[i] == '\0') /* don't insert a new line after this line*/
4056 nl = FALSE;
4057 /*put to be deleted part into kill buffer */
4058 for (i=ods.p_ind; c[i] != '\0'; i++)
4059 kinsert(c[i]);
4060 }else{
4061 while(*c != '\0') /* splat out the line */
4062 kinsert(*c++);
4065 if (nl)
4066 kinsert('\n'); /* helpful to yank in body */
4068 #ifdef _WINDOWS
4069 mswin_killbuftoclip (kremove);
4070 #endif
4072 if (gmode & MDDTKILL){
4073 if (l->text[0]=='\0'){
4075 if(l->next && l->prev)
4076 ods.cur_l = next_hline(&ods.cur_e, l);
4077 else if(l->prev)
4078 ods.cur_l = prev_hline(&ods.cur_e, l);
4080 if(l == ods.top_l)
4081 ods.top_l = ods.cur_l;
4083 return(hldelete(l));
4085 else {
4086 l->text[ods.p_ind]='\0'; /* delete part of the line from the cursor */
4087 return(TRUE);
4089 }else{
4090 if(l->next && l->prev)
4091 ods.cur_l = next_hline(&ods.cur_e, l);
4092 else if(l->prev)
4093 ods.cur_l = prev_hline(&ods.cur_e, l);
4095 if(l == ods.top_l)
4096 ods.top_l = ods.cur_l;
4098 return(hldelete(l)); /* blast it */
4105 * SaveHeaderLines() - insert the saved lines in the list before the
4106 * current line in the header
4108 * notes:
4109 * Once again, just using emacs' kill buffer and its
4110 * functions.
4112 * returns:
4113 * TRUE if something good happened
4114 * FALSE otherwise
4117 SaveHeaderLines(void)
4119 UCS *buf; /* malloc'd copy of buffer */
4120 UCS *bp; /* pointer to above buffer */
4121 register unsigned i; /* index */
4122 UCS *work_buf, *work_buf_begin;
4123 char empty[1];
4124 int len, buf_len, work_buf_len, tentative_p_ind = 0;
4125 struct hdr_line *travel, *tentative_cur_l = NULL;
4127 if(ksize()){
4128 if((bp = buf = (UCS *) malloc((ksize()+5) * sizeof(*buf))) == NULL){
4129 emlwrite("Can't malloc space for saved text", NULL);
4130 return(FALSE);
4133 else
4134 return(FALSE);
4136 for(i=0; i < ksize(); i++)
4137 if(kremove(i) != '\n') /* filter out newlines */
4138 *bp++ = (UCS) kremove(i);
4140 *bp = '\0';
4142 while(--bp >= buf) /* kill trailing white space */
4143 if(*bp != ' '){
4144 if(ods.cur_l->text[0] != '\0'){
4145 if(*bp == '>'){ /* inserting an address */
4146 *++bp = ','; /* so add separator */
4147 *++bp = '\0';
4150 else{ /* nothing in field yet */
4151 if(*bp == ','){ /* so blast any extra */
4152 *bp = '\0'; /* separators */
4155 break;
4158 /* insert new text at the dot position */
4159 buf_len = ucs4_strlen(buf);
4160 tentative_p_ind = ods.p_ind + buf_len;
4161 work_buf_len = ucs4_strlen(ods.cur_l->text) + buf_len;
4162 work_buf = (UCS *) malloc((work_buf_len + 1) * sizeof(UCS));
4163 if (work_buf == NULL) {
4164 emlwrite("Can't malloc space for saved text", NULL);
4165 return(FALSE);
4168 work_buf[0] = '\0';
4169 work_buf_begin = work_buf;
4170 i = MIN(ods.p_ind, work_buf_len);
4171 ucs4_strncpy(work_buf, ods.cur_l->text, i);
4172 work_buf[i] = '\0';
4173 ucs4_strncat(work_buf, buf, work_buf_len+1-ucs4_strlen(work_buf)-1);
4174 work_buf[work_buf_len] = '\0';
4175 ucs4_strncat(work_buf, &ods.cur_l->text[ods.p_ind], work_buf_len+1-ucs4_strlen(work_buf)-1);
4176 work_buf[work_buf_len] = '\0';
4177 empty[0]='\0';
4178 ods.p_ind = 0;
4180 i = TRUE;
4182 /* insert text in HLSZ character chunks */
4183 while(work_buf_len + ods.p_ind > HLSZ) {
4184 ucs4_strncpy(&ods.cur_l->text[ods.p_ind], work_buf, HLSZ-ods.p_ind);
4185 work_buf += (HLSZ - ods.p_ind);
4186 work_buf_len -= (HLSZ - ods.p_ind);
4188 if(FormatLines(ods.cur_l, empty, LINEWID(),
4189 headents[ods.cur_e].break_on_comma, 0) == -1) {
4190 i = FALSE;
4191 break;
4192 } else {
4193 i = TRUE;
4194 len = 0;
4195 travel = ods.cur_l;
4196 while (len < HLSZ){
4197 len += ucs4_strlen(travel->text);
4198 if (len >= HLSZ)
4199 break;
4202 * This comes after the break above because it will
4203 * be accounted for in the while loop below.
4205 if(!tentative_cur_l){
4206 if(tentative_p_ind <= ucs4_strlen(travel->text))
4207 tentative_cur_l = travel;
4208 else
4209 tentative_p_ind -= ucs4_strlen(travel->text);
4212 travel = travel->next;
4215 ods.cur_l = travel;
4216 ods.p_ind = ucs4_strlen(travel->text) - len + HLSZ;
4220 /* insert the remainder of text */
4221 if (i != FALSE && work_buf_len > 0) {
4222 ucs4_strncpy(&ods.cur_l->text[ods.p_ind], work_buf, HLSZ-ods.p_ind);
4223 ods.cur_l->text[HLSZ-1] = '\0';
4224 work_buf = work_buf_begin;
4225 free(work_buf);
4227 if(FormatLines(ods.cur_l, empty, LINEWID(),
4228 headents[ods.cur_e].break_on_comma, 0) == -1) {
4229 i = FALSE;
4230 } else {
4231 len = 0;
4232 travel = ods.cur_l;
4233 while (len < work_buf_len + ods.p_ind){
4234 if(!tentative_cur_l){
4235 if(tentative_p_ind <= ucs4_strlen(travel->text))
4236 tentative_cur_l = travel;
4237 else
4238 tentative_p_ind -= ucs4_strlen(travel->text);
4241 len += ucs4_strlen(travel->text);
4242 if (len >= work_buf_len + ods.p_ind)
4243 break;
4245 travel = travel->next;
4248 ods.cur_l = travel;
4249 ods.p_ind = ucs4_strlen(travel->text) - len + work_buf_len + ods.p_ind;
4250 if(tentative_cur_l
4251 && tentative_p_ind >= 0
4252 && tentative_p_ind <= ucs4_strlen(tentative_cur_l->text)){
4253 ods.cur_l = tentative_cur_l;
4254 ods.p_ind = tentative_p_ind;
4259 free(buf);
4260 return(i);
4266 * break_point - Break the given line at the most reasonable character breakch
4267 * within maxwid max characters.
4269 * returns:
4270 * Pointer to the best break point in s, or
4271 * Pointer to the beginning of s if no break point found
4273 UCS *
4274 break_point(UCS *line, int maxwid, UCS breakch, int *quotedarg)
4276 UCS *bp; /* break point */
4277 int quoted;
4280 * Start at maxwid and work back until first opportunity to break.
4282 bp = ucs4_particular_width(line, maxwid);
4285 * Quoted should be set up for the start of line. Since we want
4286 * to move to bp and work our way back we need to scan through the
4287 * line up to bp setting quoted appropriately.
4289 if(quotedarg)
4290 ucs4_strqchr(line, '\0', quotedarg, bp-line);
4292 quoted = quotedarg ? *quotedarg : 0;
4294 while(bp != line){
4295 if(breakch == ',' && *bp == '"') /* don't break on quoted ',' */
4296 quoted = !quoted; /* toggle quoted state */
4298 if(*bp == breakch && !quoted){
4299 if(breakch == ' '){
4300 if(ucs4_str_width_ptr_to_ptr(line, bp+1) < maxwid){
4301 bp++; /* leave the ' ' */
4302 break;
4305 else{
4307 * if break char isn't a space, leave a space after
4308 * the break char.
4310 if(!(ucs4_str_width_ptr_to_ptr(line, bp+1) >= maxwid
4311 || (bp[1] == ' ' && ucs4_str_width_ptr_to_ptr(line, bp+2) >= maxwid))){
4312 bp += (bp[1] == ' ') ? 2 : 1;
4313 break;
4318 bp--;
4321 if(quotedarg)
4322 *quotedarg = quoted;
4324 return((quoted) ? line : bp);
4331 * hldelete() - remove the header line pointed to by l from the linked list
4332 * of lines.
4334 * notes:
4335 * the case of first line in field is kind of bogus. since
4336 * the array of headers has a pointer to the first line, and
4337 * i don't want to worry about this too much, i just copied
4338 * the line below and removed it rather than the first one
4339 * from the list.
4341 * returns:
4342 * TRUE if it worked
4343 * FALSE otherwise
4346 hldelete(struct hdr_line *l)
4348 register struct hdr_line *lp;
4350 if(l == NULL)
4351 return(FALSE);
4353 if(l->next == NULL && l->prev == NULL){ /* only one line in field */
4354 l->text[0] = '\0';
4355 return(TRUE); /* no free only line in list */
4357 else if(l->next == NULL){ /* last line in field */
4358 l->prev->next = NULL;
4360 else if(l->prev == NULL){ /* first line in field */
4361 ucs4_strncpy(l->text, l->next->text, HLSZ);
4362 l->text[HLSZ-1] = '\0';
4363 lp = l->next;
4364 if((l->next = lp->next) != NULL)
4365 l->next->prev = l;
4366 l = lp;
4368 else{ /* some where in field */
4369 l->prev->next = l->next;
4370 l->next->prev = l->prev;
4373 l->next = NULL;
4374 l->prev = NULL;
4375 free((char *)l);
4376 return(TRUE);
4382 * is_blank - returns true if the next n chars from coordinates row, col
4383 * on display are spaces
4386 is_blank(int row, int col, int n)
4388 n += col;
4389 for( ;col < n; col++){
4390 if(pscr(row, col) == NULL || pscr(row, col)->c != ' ')
4391 return(0);
4393 return(1);
4398 * ShowPrompt - display key help corresponding to the current header entry
4400 void
4401 ShowPrompt(void)
4403 if(headents[ods.cur_e].key_label){
4404 menu_header[TO_KEY].name = "^T";
4405 menu_header[TO_KEY].label = headents[ods.cur_e].key_label;
4406 KS_OSDATASET(&menu_header[TO_KEY], KS_OSDATAGET(&headents[ods.cur_e]));
4408 else
4409 menu_header[TO_KEY].name = NULL;
4411 if(Pmaster && Pmaster->exit_label)
4412 menu_header[SEND_KEY].label = Pmaster->exit_label;
4413 else if(gmode & (MDVIEW | MDHDRONLY))
4414 menu_header[SEND_KEY].label = (gmode & MDHDRONLY) ? "eXit/Save" : "eXit";
4415 else
4416 menu_header[SEND_KEY].label = N_("Send");
4418 if(gmode & MDVIEW){
4419 menu_header[CUT_KEY].name = NULL;
4420 menu_header[DEL_KEY].name = NULL;
4421 menu_header[UDEL_KEY].name = NULL;
4423 else{
4424 menu_header[CUT_KEY].name = "^K";
4425 menu_header[DEL_KEY].name = "^D";
4426 menu_header[UDEL_KEY].name = "^U";
4429 if(Pmaster->ctrlr_label){
4430 menu_header[RICH_KEY].label = Pmaster->ctrlr_label;
4431 menu_header[RICH_KEY].name = "^R";
4433 else if(gmode & MDHDRONLY){
4434 menu_header[RICH_KEY].name = NULL;
4436 else{
4437 menu_header[RICH_KEY].label = N_("Rich Hdr");
4438 menu_header[RICH_KEY].name = "^R";
4441 if(gmode & MDHDRONLY){
4442 if(headents[ods.cur_e].fileedit){
4443 menu_header[PONE_KEY].name = "^_";
4444 menu_header[PONE_KEY].label = N_("Edit File");
4446 else
4447 menu_header[PONE_KEY].name = NULL;
4449 menu_header[ATT_KEY].name = NULL;
4451 else{
4452 menu_header[PONE_KEY].name = "^O";
4453 menu_header[PONE_KEY].label = N_("Postpone");
4455 menu_header[ATT_KEY].name = "^J";
4458 wkeyhelp(menu_header);
4463 * packheader - packup all of the header fields for return to caller.
4464 * NOTE: all of the header info passed in, including address
4465 * of the pointer to each string is contained in the
4466 * header entry array "headents".
4469 packheader(void)
4471 register int i = 0; /* array index */
4472 register int count; /* count of chars in a field */
4473 register int retval = TRUE;
4474 register char *bufp;
4475 register struct hdr_line *line;
4476 char *p;
4478 if(!headents)
4479 return(TRUE);
4481 while(headents[i].name != NULL){
4482 #ifdef ATTACHMENTS
4484 * attachments are special case, already in struct we pass back
4486 if(headents[i].is_attach){
4487 i++;
4488 continue;
4490 #endif
4492 if(headents[i].blank){
4493 i++;
4494 continue;
4498 * count chars to see if we need a new malloc'd space for our
4499 * array.
4501 line = headents[i].hd_text;
4502 count = 0;
4503 while(line != NULL){
4505 * add one for possible concatenation of a ' ' character ...
4507 p = ucs4_to_utf8_cpystr(line->text);
4508 if(p){
4509 count += strlen(p);
4510 if(p[0] && p[strlen(p)-1] == ',')
4511 count++;
4513 fs_give((void **) &p);
4516 line = line->next;
4519 line = headents[i].hd_text;
4520 if(count <= headents[i].maxlen){
4521 *headents[i].realaddr[0] = '\0';
4523 else{
4525 * don't forget to include space for the null terminator!!!!
4527 if((bufp = (char *)malloc((count+1) * sizeof(char))) != NULL){
4528 *bufp = '\0';
4530 free(*headents[i].realaddr);
4531 *headents[i].realaddr = bufp;
4532 headents[i].maxlen = count;
4534 else{
4535 emlwrite("Can't make room to pack header field.", NULL);
4536 retval = FALSE;
4540 if(retval != FALSE){
4541 int saw_current_line = 0;
4543 while(line != NULL){
4545 /* pass the cursor offset back in Pmaster struct */
4546 if(headents[i].start_here && Pmaster && !saw_current_line){
4547 if(ods.cur_l == line)
4548 saw_current_line++;
4549 else
4550 Pmaster->edit_offset += ucs4_strlen(line->text);
4553 p = ucs4_to_utf8_cpystr(line->text);
4554 if(p){
4555 strncat(*headents[i].realaddr, p, headents[i].maxlen+1-strlen(*headents[i].realaddr)-1);
4556 (*headents[i].realaddr)[headents[i].maxlen] = '\0';
4558 if(p[0] && p[strlen(p)-1] == ','){
4559 strncat(*headents[i].realaddr, " ", headents[i].maxlen+1-strlen(*headents[i].realaddr)-1);
4560 (*headents[i].realaddr)[headents[i].maxlen] = '\0';
4563 fs_give((void **) &p);
4566 line = line->next;
4570 i++;
4573 return(retval);
4579 * zotheader - free all malloc'd lines associated with the header structs
4581 void
4582 zotheader(void)
4584 register struct headerentry *i;
4586 for(i = headents; headents && i->name; i++)
4587 zotentry(i->hd_text);
4592 * zotentry - free malloc'd space associated with the given linked list
4594 void
4595 zotentry(struct hdr_line *l)
4597 register struct hdr_line *ld, *lf = l;
4599 while((ld = lf) != NULL){
4600 lf = ld->next;
4601 ld->next = ld->prev = NULL;
4602 free((char *) ld);
4609 * zotcomma - blast any trailing commas and white space from the end
4610 * of the given line
4613 zotcomma(UCS *s)
4615 UCS *p;
4616 int retval = FALSE;
4618 p = &s[ucs4_strlen(s)];
4619 while(--p >= s){
4620 if(*p != ' '){
4621 if(*p == ','){
4622 *p = '\0';
4623 retval = TRUE;
4626 return(retval);
4630 return(retval);
4635 * Save the current state of global variables so that we can restore
4636 * them later. This is so we can call pico again.
4637 * Also have to initialize some variables that normally would be set to
4638 * zero on startup.
4640 VARS_TO_SAVE *
4641 save_pico_state(void)
4643 VARS_TO_SAVE *ret;
4644 extern int vtrow;
4645 extern int vtcol;
4646 extern int lbound;
4647 extern VIDEO **vscreen;
4648 extern VIDEO **pscreen;
4649 extern int pico_all_done;
4650 extern jmp_buf finstate;
4651 extern UCS *pico_anchor;
4653 if((ret = (VARS_TO_SAVE *)malloc(sizeof(VARS_TO_SAVE))) == NULL)
4654 return(ret);
4656 ret->vtrow = vtrow;
4657 ret->vtcol = vtcol;
4658 ret->lbound = lbound;
4659 ret->vscreen = vscreen;
4660 ret->pscreen = pscreen;
4661 ret->ods = ods;
4662 ret->delim_ps = delim_ps;
4663 ret->invert_ps = invert_ps;
4664 ret->pico_all_done = pico_all_done;
4665 memcpy(ret->finstate, finstate, sizeof(jmp_buf));
4666 ret->pico_anchor = pico_anchor;
4667 ret->Pmaster = Pmaster;
4668 ret->fillcol = fillcol;
4669 if((ret->pat = (UCS *)malloc(sizeof(UCS) * (ucs4_strlen(pat)+1))) != NULL)
4670 ucs4_strncpy(ret->pat, pat, ucs4_strlen(pat)+1);
4672 ret->ComposerTopLine = ComposerTopLine;
4673 ret->ComposerEditing = ComposerEditing;
4674 ret->gmode = gmode;
4675 ret->alt_speller = alt_speller;
4676 ret->quote_str = glo_quote_str;
4677 ret->wordseps = glo_wordseps;
4678 ret->currow = currow;
4679 ret->curcol = curcol;
4680 ret->thisflag = thisflag;
4681 ret->lastflag = lastflag;
4682 ret->curgoal = curgoal;
4683 ret->opertree = (char *) malloc(sizeof(char) * (strlen(opertree) + 1));
4684 if(ret->opertree != NULL)
4685 strncpy(ret->opertree, opertree, strlen(opertree)+1);
4687 ret->curwp = curwp;
4688 ret->wheadp = wheadp;
4689 ret->curbp = curbp;
4690 ret->bheadp = bheadp;
4691 ret->km_popped = km_popped;
4692 ret->mrow = term.t_mrow;
4694 /* Initialize for next pico call */
4695 wheadp = NULL;
4696 curwp = NULL;
4697 bheadp = NULL;
4698 curbp = NULL;
4700 return(ret);
4704 void
4705 restore_pico_state(VARS_TO_SAVE *state)
4707 extern int vtrow;
4708 extern int vtcol;
4709 extern int lbound;
4710 extern VIDEO **vscreen;
4711 extern VIDEO **pscreen;
4712 extern int pico_all_done;
4713 extern jmp_buf finstate;
4714 extern UCS *pico_anchor;
4716 clearcursor();
4717 vtrow = state->vtrow;
4718 vtcol = state->vtcol;
4719 lbound = state->lbound;
4720 vscreen = state->vscreen;
4721 pscreen = state->pscreen;
4722 ods = state->ods;
4723 delim_ps = state->delim_ps;
4724 invert_ps = state->invert_ps;
4725 pico_all_done = state->pico_all_done;
4726 memcpy(finstate, state->finstate, sizeof(jmp_buf));
4727 pico_anchor = state->pico_anchor;
4728 Pmaster = state->Pmaster;
4729 if(Pmaster)
4730 headents = Pmaster->headents;
4732 fillcol = state->fillcol;
4733 if(state->pat)
4734 ucs4_strncpy(pat, state->pat, NPAT);
4736 ComposerTopLine = state->ComposerTopLine;
4737 ComposerEditing = state->ComposerEditing;
4738 gmode = state->gmode;
4739 alt_speller = state->alt_speller;
4740 glo_quote_str = state->quote_str;
4741 glo_wordseps = state->wordseps;
4742 currow = state->currow;
4743 curcol = state->curcol;
4744 thisflag = state->thisflag;
4745 lastflag = state->lastflag;
4746 curgoal = state->curgoal;
4747 if(state->opertree){
4748 strncpy(opertree, state->opertree, sizeof(opertree));
4749 opertree[sizeof(opertree)-1] = '\0';
4752 curwp = state->curwp;
4753 wheadp = state->wheadp;
4754 curbp = state->curbp;
4755 bheadp = state->bheadp;
4756 km_popped = state->km_popped;
4757 term.t_mrow = state->mrow;
4761 void
4762 free_pico_state(VARS_TO_SAVE *state)
4764 if(state->pat)
4765 free(state->pat);
4767 if(state->opertree)
4768 free(state->opertree);
4770 free(state);
4775 * Ok to call this twice in a row because it won't do anything the second
4776 * time.
4778 void
4779 fix_mangle_and_err(int *mangled, char **errmsg, char *name)
4781 if(mangled && *mangled){
4782 ttresize();
4783 picosigs();
4784 PaintBody(0);
4785 *mangled = 0;
4788 if(errmsg && *errmsg){
4789 if(**errmsg){
4790 char err[500];
4792 snprintf(err, sizeof(err), "%s field: %s", name, *errmsg);
4793 (*term.t_beep)();
4794 emlwrite(err, NULL);
4796 else
4797 mlerase();
4799 free(*errmsg);
4800 *errmsg = NULL;
4806 * What is this for?
4807 * This is so that the To line will be appended to by an Lcc
4808 * entry unless the user types in the To line after the Lcc
4809 * has already been set.
4811 void
4812 mark_sticky(struct headerentry *h)
4814 if(h && (!h->sticky_special || h->bldr_private))
4815 h->sticky = 1;
4819 #ifdef MOUSE
4820 #undef HeaderEditor
4823 * Wrapper function for the real header editor.
4824 * Does the important tasks of:
4825 * 1) verifying that we _can_ edit the headers.
4826 * 2) acting on the result code from the header editor.
4829 HeaderEditor(int f, int n)
4831 int retval;
4834 #ifdef _WINDOWS
4835 /* Sometimes we get here from a scroll callback, which
4836 * is no good at all because mswin is not ready to process input and
4837 * this _headeredit() will never do anything.
4838 * Putting this test here was the most general solution I could think
4839 * of. */
4840 if (!mswin_caninput())
4841 return (-1);
4842 #endif
4844 retval = HeaderEditorWork(f, n);
4845 if (retval == -3) {
4846 retval = mousepress(0,0);
4848 return (retval);
4850 #endif