* Reimplementation of the code that allows the .pinerc file to be a
[alpine.git] / pico / composer.c
blob05b8608a3e63279c8b9284ebec9470568334819b
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-2015 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 emlwrite("\007Can'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 postition */
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 emlwrite("\007Format 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 emlwrite("\007Can'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 emlwrite("\007Format 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 emlwrite("\007Can'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 emlwrite("\007Format 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 emlwrite("\007Format 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 emlwrite("\007Format 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 emlwrite("\007Can'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 emlwrite("\007Problem 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 emlwrite("\007Format 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 emlwrite(_("\007Can'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 emlwrite(_("\007Can'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 emlwrite(_("\007Can'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;
2777 if(clear)
2778 pclear(COMPOSER_TOP_LINE, ComposerTopLine-1);
2780 curline = COMPOSER_TOP_LINE;
2781 curindex = curoffset = 0;
2783 for(lp = ods.top_l, e = ods.top_e; ; curline++){
2784 if((curline == line) || ((lp = next_hline(&e, lp)) == NULL))
2785 break;
2788 while(headents[e].name != NULL){ /* begin to redraw */
2789 while(lp != NULL){
2790 buf[0] = '\0';
2791 if((!lp->prev || curline == COMPOSER_TOP_LINE) && !curoffset){
2792 if(InvertPrompt(e, (e == ods.cur_e && ComposerEditing)) == -1
2793 && !is_blank(curline, 0, headents[e].prwid)){
2794 for(i = 0; i < headents[e].prwid; i++)
2795 buf[i] = ' ';
2797 buf[i] = '\0';
2800 else if(!is_blank(curline, 0, headents[e].prwid)){
2801 for(i = 0; i < headents[e].prwid; i++)
2802 buf[i] = ' ';
2804 buf[i] = '\0';
2807 if(*(bufp = buf) != '\0'){ /* need to paint? */
2808 movecursor(curline, 0); /* paint the line... */
2809 while(*bufp != '\0')
2810 pputc(*bufp++, 0);
2813 bufp = &(lp->text[curindex]); /* skip chars already there */
2814 curoffset += headents[e].prwid;
2815 curindex = index_from_col(curline, curoffset);
2816 while(pscr(curline, curindex) != NULL &&
2817 *bufp == pscr(curline, curindex)->c && *bufp != '\0'){
2818 w = wcellwidth(*bufp);
2819 curoffset += (w >= 0 ? w : 1);
2820 ++bufp;
2821 ++curindex;
2822 if(curoffset >= term.t_ncol)
2823 break;
2826 if(*bufp != '\0'){ /* need to move? */
2827 movecursor(curline, curoffset);
2828 while(*bufp != '\0'){ /* display what's not there */
2829 pputc(*bufp, 0);
2830 w = wcellwidth(*bufp);
2831 curoffset += (w >= 0 ? w : 1);
2832 ++bufp;
2833 ++curindex;
2837 if(curoffset < term.t_ncol){
2838 movecursor(curline, curoffset);
2839 peeol();
2841 curline++;
2842 curindex = curoffset = 0;
2843 if(curline >= BOTTOM())
2844 break;
2846 lp = lp->next;
2849 if(curline >= BOTTOM())
2850 return; /* don't paint delimiter */
2852 while(headents[++e].name != NULL)
2853 if(headents[e].display_it){
2854 lp = headents[e].hd_text;
2855 break;
2859 display_delimiter(ComposerEditing ? 0 : 1);
2865 * PaintBody() - generic call to handle repainting everything BUT the
2866 * header
2868 * notes:
2869 * The header redrawing in a level 0 body paint gets done
2870 * in update()
2872 void
2873 PaintBody(int level)
2875 curwp->w_flag |= WFHARD; /* make sure framing's right */
2876 if(level == 0) /* specify what to update */
2877 sgarbf = TRUE;
2879 update(); /* display message body */
2881 if(level == 0 && ComposerEditing){
2882 mlerase(); /* clear the error line */
2883 ShowPrompt();
2889 * display_for_send - paint the composer from the top line and return.
2891 void
2892 display_for_send(void)
2894 int i = 0;
2895 struct hdr_line *l;
2897 /* if first header line isn't displayed, there's work to do */
2898 if(headents && ((l = first_hline(&i)) != ods.top_l
2899 || ComposerTopLine == COMPOSER_TOP_LINE
2900 || !ods.p_line)){
2901 struct on_display orig_ods;
2902 int orig_edit = ComposerEditing,
2903 orig_ct_line = ComposerTopLine;
2906 * fake that the cursor's in the first header line
2907 * and force repaint...
2909 orig_ods = ods;
2910 ods.cur_e = i;
2911 ods.top_l = ods.cur_l = l;
2912 ods.top_e = ods.cur_e;
2913 ods.p_line = COMPOSER_TOP_LINE;
2914 ComposerEditing = TRUE; /* to fool update() */
2915 setimark(FALSE, 1); /* remember where we were */
2916 gotobob(FALSE, 1);
2918 UpdateHeader(0); /* redraw whole enchilada */
2919 PaintHeader(COMPOSER_TOP_LINE, TRUE);
2920 PaintBody(0);
2922 ods = orig_ods; /* restore original state */
2923 ComposerEditing = orig_edit;
2924 ComposerTopLine = curwp->w_toprow = orig_ct_line;
2925 curwp->w_ntrows = BOTTOM() - ComposerTopLine;
2926 swapimark(FALSE, 1);
2928 /* in case we don't exit, set up restoring the screen */
2929 sgarbf = TRUE; /* force redraw */
2935 * ArrangeHeader - set up display parm such that header is reasonably
2936 * displayed
2938 void
2939 ArrangeHeader(void)
2941 int e;
2942 register struct hdr_line *l;
2944 ods.p_line = ods.p_ind = 0;
2945 e = ods.top_e = 0;
2946 l = ods.top_l = headents[e].hd_text;
2947 while(headents[e+1].name || (l && l->next))
2948 if((l = next_sel_hline(&e, l)) != NULL){
2949 ods.cur_l = l;
2950 ods.cur_e = e;
2953 UpdateHeader(1);
2958 * ComposerHelp() - display mail help in a context sensitive way
2959 * based on the level passed ...
2962 ComposerHelp(int level)
2964 char buf[80];
2965 VARS_TO_SAVE *saved_state;
2967 curwp->w_flag |= WFMODE;
2968 sgarbf = TRUE;
2970 if(level < 0 || !headents[level].name){
2971 (*term.t_beep)();
2972 emlwrite("Sorry, I can't help you with that.", NULL);
2973 sleep(2);
2974 return(FALSE);
2977 snprintf(buf, sizeof(buf), "Help for %s %.40s Field",
2978 (Pmaster->pine_flags & MDHDRONLY) ? "Address Book"
2979 : "Composer",
2980 headents[level].name);
2981 saved_state = save_pico_state();
2982 (*Pmaster->helper)(headents[level].help, buf, 1);
2983 if(saved_state){
2984 restore_pico_state(saved_state);
2985 free_pico_state(saved_state);
2988 ttresize();
2989 picosigs(); /* restore altered handlers */
2990 return(TRUE);
2996 * ToggleHeader() - set or unset pico values to the full screen size
2997 * painting header if need be.
3000 ToggleHeader(int show)
3003 * check to see if we need to display the header...
3005 if(show){
3006 UpdateHeader(0); /* figure bounds */
3007 PaintHeader(COMPOSER_TOP_LINE, FALSE); /* draw it */
3009 else{
3011 * set bounds for no header display
3013 curwp->w_toprow = ComposerTopLine = COMPOSER_TOP_LINE;
3014 curwp->w_ntrows = BOTTOM() - ComposerTopLine;
3016 return(TRUE);
3022 * HeaderLen() - return the length in lines of the exposed portion of the
3023 * header
3026 HeaderLen(void)
3028 register struct hdr_line *lp;
3029 int e;
3030 int i;
3032 i = 1;
3033 lp = ods.top_l;
3034 e = ods.top_e;
3035 while(lp != NULL){
3036 lp = next_hline(&e, lp);
3037 i++;
3039 return(i);
3045 * first_hline() - return a pointer to the first displayable header line
3047 * returns:
3048 * 1) pointer to first displayable line in header and header
3049 * entry, via side effect, that the first line is a part of
3050 * 2) NULL if no next line, leaving entry at LASTHDR
3052 struct hdr_line *
3053 first_hline(int *entry)
3055 /* init *entry so we're sure to start from the top */
3056 for(*entry = 0; headents[*entry].name; (*entry)++)
3057 if(headents[*entry].display_it)
3058 return(headents[*entry].hd_text);
3060 *entry = 0;
3061 return(NULL); /* this shouldn't happen */
3066 * first_sel_hline() - return a pointer to the first selectable header line
3068 * returns:
3069 * 1) pointer to first selectable line in header and header
3070 * entry, via side effect, that the first line is a part of
3071 * 2) NULL if no next line, leaving entry at LASTHDR
3073 struct hdr_line *
3074 first_sel_hline(int *entry)
3076 /* init *entry so we're sure to start from the top */
3077 for(*entry = 0; headents[*entry].name; (*entry)++)
3078 if(headents[*entry].display_it && !headents[*entry].blank)
3079 return(headents[*entry].hd_text);
3081 *entry = 0;
3082 return(NULL); /* this shouldn't happen */
3088 * next_hline() - return a pointer to the next line structure
3090 * returns:
3091 * 1) pointer to next displayable line in header and header
3092 * entry, via side effect, that the next line is a part of
3093 * 2) NULL if no next line, leaving entry at LASTHDR
3095 struct hdr_line *
3096 next_hline(int *entry, struct hdr_line *line)
3098 if(line == NULL)
3099 return(NULL);
3101 if(line->next == NULL){
3102 while(headents[++(*entry)].name != NULL){
3103 if(headents[*entry].display_it)
3104 return(headents[*entry].hd_text);
3106 --(*entry);
3107 return(NULL);
3109 else
3110 return(line->next);
3115 * next_sel_hline() - return a pointer to the next selectable line structure
3117 * returns:
3118 * 1) pointer to next selectable line in header and header
3119 * entry, via side effect, that the next line is a part of
3120 * 2) NULL if no next line, leaving entry at LASTHDR
3122 struct hdr_line *
3123 next_sel_hline(int *entry, struct hdr_line *line)
3125 if(line == NULL)
3126 return(NULL);
3128 if(line->next == NULL){
3129 while(headents[++(*entry)].name != NULL){
3130 if(headents[*entry].display_it && !headents[*entry].blank)
3131 return(headents[*entry].hd_text);
3133 --(*entry);
3134 return(NULL);
3136 else
3137 return(line->next);
3143 * prev_hline() - return a pointer to the next line structure back
3145 * returns:
3146 * 1) pointer to previous displayable line in header and
3147 * the header entry that the next line is a part of
3148 * via side effect
3149 * 2) NULL if no next line, leaving entry unchanged from
3150 * the value it had on entry.
3152 struct hdr_line *
3153 prev_hline(int *entry, struct hdr_line *line)
3155 if(line == NULL)
3156 return(NULL);
3158 if(line->prev == NULL){
3159 int orig_entry;
3161 orig_entry = *entry;
3162 while(--(*entry) >= 0){
3163 if(headents[*entry].display_it){
3164 line = headents[*entry].hd_text;
3165 while(line->next != NULL)
3166 line = line->next;
3167 return(line);
3171 *entry = orig_entry;
3172 return(NULL);
3174 else
3175 return(line->prev);
3180 * prev_sel_hline() - return a pointer to the previous selectable line
3182 * returns:
3183 * 1) pointer to previous selectable line in header and
3184 * the header entry that the next line is a part of
3185 * via side effect
3186 * 2) NULL if no next line, leaving entry unchanged from
3187 * the value it had on entry.
3189 struct hdr_line *
3190 prev_sel_hline(int *entry, struct hdr_line *line)
3192 if(line == NULL)
3193 return(NULL);
3195 if(line->prev == NULL){
3196 int orig_entry;
3198 orig_entry = *entry;
3199 while(--(*entry) >= 0){
3200 if(headents[*entry].display_it && !headents[*entry].blank){
3201 line = headents[*entry].hd_text;
3202 while(line->next != NULL)
3203 line = line->next;
3204 return(line);
3208 *entry = orig_entry;
3209 return(NULL);
3211 else
3212 return(line->prev);
3218 * first_requested_hline() - return pointer to first line that pico's caller
3219 * asked that we start on.
3221 struct hdr_line *
3222 first_requested_hline(int *ent)
3224 int i, reqfield;
3225 struct hdr_line *rv = NULL;
3227 for(reqfield = -1, i = 0; headents[i].name; i++)
3228 if(headents[i].start_here){
3229 headents[i].start_here = 0; /* clear old setting */
3230 if(reqfield < 0){ /* if not already, set up */
3231 headents[i].display_it = 1; /* make sure it's shown */
3232 *ent = reqfield = i;
3233 rv = headents[i].hd_text;
3237 return(rv);
3243 * UpdateHeader() - determines the best range of lines to be displayed
3244 * using the global ods value for the current line and the
3245 * top line, also sets ComposerTopLine and pico limits
3247 * showtop -- Attempt to show all header lines if they'll fit.
3249 * notes:
3250 * This is pretty ugly because it has to keep the current line
3251 * on the screen in a reasonable location no matter what.
3252 * There are also a couple of rules to follow:
3253 * 1) follow paging conventions of pico (ie, half page
3254 * scroll)
3255 * 2) if more than one page, always display last half when
3256 * pline is toward the end of the header
3258 * returns:
3259 * TRUE if anything changed (side effects: new p_line, top_l
3260 * top_e, and pico parms)
3261 * FALSE if nothing changed
3265 UpdateHeader(int showtop)
3267 register struct hdr_line *lp;
3268 int i, le;
3269 int ret = FALSE;
3270 int old_top = ComposerTopLine;
3271 int old_p = ods.p_line;
3273 if(ods.p_line < COMPOSER_TOP_LINE ||
3274 ((ods.p_line == ComposerTopLine-2) ? 2: 0) + ods.p_line >= BOTTOM()){
3275 /* NewTop if cur header line is at bottom of screen or two from */
3276 /* the bottom of the screen if cur line is bottom header line */
3277 NewTop(showtop); /* get new top_l */
3278 ret = TRUE;
3280 else{ /* make sure p_line's OK */
3281 i = COMPOSER_TOP_LINE;
3282 lp = ods.top_l;
3283 le = ods.top_e;
3284 while(lp != ods.cur_l){
3286 * this checks to make sure cur_l is below top_l and that
3287 * cur_l is on the screen...
3289 if((lp = next_hline(&le, lp)) == NULL || ++i >= BOTTOM()){
3290 NewTop(0);
3291 ret = TRUE;
3292 break;
3297 ods.p_line = COMPOSER_TOP_LINE; /* find p_line... */
3298 lp = ods.top_l;
3299 le = ods.top_e;
3300 while(lp && lp != ods.cur_l){
3301 lp = next_hline(&le, lp);
3302 ods.p_line++;
3305 if(!ret)
3306 ret = !(ods.p_line == old_p);
3308 ComposerTopLine = ods.p_line; /* figure top composer line */
3309 while(lp && ComposerTopLine <= BOTTOM()){
3310 lp = next_hline(&le, lp);
3311 ComposerTopLine += (lp) ? 1 : 2; /* allow for delim at end */
3314 if(!ret)
3315 ret = !(ComposerTopLine == old_top);
3317 if(wheadp->w_toprow != ComposerTopLine){ /* update pico params... */
3318 wheadp->w_toprow = ComposerTopLine;
3319 wheadp->w_ntrows = ((i = BOTTOM() - ComposerTopLine) > 0) ? i : 0;
3320 ret = TRUE;
3322 return(ret);
3328 * NewTop() - calculate a new top_l based on the cur_l
3330 * showtop -- Attempt to show all the header lines if they'll fit
3332 * returns:
3333 * with ods.top_l and top_e pointing at a reasonable line
3334 * entry
3336 void
3337 NewTop(int showtop)
3339 register struct hdr_line *lp;
3340 register int i;
3341 int e;
3343 lp = ods.cur_l;
3344 e = ods.cur_e;
3345 i = showtop ? FULL_SCR() : HALF_SCR();
3347 while(lp != NULL && (--i > 0)){
3348 ods.top_l = lp;
3349 ods.top_e = e;
3350 lp = prev_hline(&e, lp);
3357 * display_delimiter() - just paint the header/message body delimiter with
3358 * inverse value specified by state.
3360 void
3361 display_delimiter(int state)
3363 UCS *bufp, *buf;
3365 if(ComposerTopLine - 1 >= BOTTOM()) /* silently forget it */
3366 return;
3368 buf = utf8_to_ucs4_cpystr((gmode & MDHDRONLY) ? "" : HDR_DELIM);
3369 if(!buf)
3370 return;
3372 bufp = buf;
3374 if(state == delim_ps){ /* optimize ? */
3375 for(delim_ps = 0; bufp[delim_ps] && pscr(ComposerTopLine-1,delim_ps) != NULL && pscr(ComposerTopLine-1,delim_ps)->c == bufp[delim_ps];delim_ps++)
3378 if(bufp[delim_ps] == '\0' && !(gmode & MDHDRONLY)){
3379 delim_ps = state;
3380 fs_give((void **) &buf);
3381 return; /* already displayed! */
3385 delim_ps = state;
3387 movecursor(ComposerTopLine - 1, 0);
3388 if(state)
3389 (*term.t_rev)(1);
3391 while(*bufp != '\0')
3392 pputc(*bufp++, state ? 1 : 0);
3394 if(state)
3395 (*term.t_rev)(0);
3397 peeol();
3398 fs_give((void **) &buf);
3404 * InvertPrompt() - invert the prompt associated with header entry to state
3405 * state (true if invert, false otherwise).
3406 * returns:
3407 * non-zero if nothing done
3408 * 0 if prompt inverted successfully
3410 * notes:
3411 * come to think of it, this func and the one above could
3412 * easily be combined
3415 InvertPrompt(int entry, int state)
3417 UCS *buf, *bufp;
3418 UCS *end;
3419 int i;
3421 buf = utf8_to_ucs4_cpystr(headents[entry].prompt); /* fresh prompt paint */
3422 if(!buf)
3423 return(-1);
3425 bufp = buf;
3426 if((i = entry_line(entry, FALSE)) == -1){
3427 fs_give((void **) &buf);
3428 return(-1); /* silently forget it */
3431 end = buf + ucs4_strlen(buf);
3434 * Makes sure that the prompt doesn't take up more than prwid of screen space.
3435 * The caller should do that, too, in order to make it look right so
3436 * this should most likely be a no-op
3438 if(ucs4_str_width_ptr_to_ptr(buf, end) > headents[entry].prwid){
3439 end = ucs4_particular_width(buf, headents[entry].prwid);
3440 *end = '\0';
3443 if(entry < 16 && (invert_ps&(1<<entry))
3444 == (state ? 1<<entry : 0)){ /* optimize ? */
3445 int j;
3447 for(j = 0; bufp[j] && pscr(i, j)->c == bufp[j]; j++)
3450 if(bufp[j] == '\0'){
3451 if(state)
3452 invert_ps |= 1<<entry;
3453 else
3454 invert_ps &= ~(1<<entry);
3456 fs_give((void **) &buf);
3457 return(0); /* already displayed! */
3461 if(entry < 16){ /* if > 16, cannot be stored in invert_ps */
3462 if(state)
3463 invert_ps |= 1<<entry;
3464 else
3465 invert_ps &= ~(1<<entry);
3468 movecursor(i, 0);
3469 if(state)
3470 (*term.t_rev)(1);
3472 while(*bufp && *(bufp + 1))
3473 pputc(*bufp++, 1); /* putc upto last char */
3475 if(state)
3476 (*term.t_rev)(0);
3478 pputc(*bufp, 0); /* last char not inverted */
3480 fs_give((void **) &buf);
3482 return(TRUE);
3488 * partial_entries() - toggle display of the bcc and fcc fields.
3490 * returns:
3491 * TRUE if there are partial entries on the display
3492 * FALSE otherwise.
3495 partial_entries(void)
3497 register struct headerentry *h;
3498 int is_on;
3500 /*---- find out status of first rich header ---*/
3501 for(h = headents; !h->rich_header && h->name != NULL; h++)
3504 is_on = h->display_it;
3505 for(h = headents; h->name != NULL; h++)
3506 if(h->rich_header)
3507 h->display_it = ! is_on;
3509 return(is_on);
3515 * entry_line() - return the physical line on the screen associated
3516 * with the given header entry field. Note: the field
3517 * may span lines, so if the last char is set, return
3518 * the appropriate value.
3520 * returns:
3521 * 1) physical line number of entry
3522 * 2) -1 if entry currently not on display
3525 entry_line(int entry, int lastchar)
3527 register int p_line = COMPOSER_TOP_LINE;
3528 int i;
3529 register struct hdr_line *line;
3531 for(line = ods.top_l, i = ods.top_e;
3532 headents && headents[i].name && i <= entry;
3533 p_line++){
3534 if(p_line >= BOTTOM())
3535 break;
3536 if(i == entry){
3537 if(lastchar){
3538 if(line->next == NULL)
3539 return(p_line);
3541 else if(line->prev == NULL)
3542 return(p_line);
3543 else
3544 return(-1);
3546 line = next_hline(&i, line);
3548 return(-1);
3554 * physical_line() - return the physical line on the screen associated
3555 * with the given header line pointer.
3557 * returns:
3558 * 1) physical line number of entry
3559 * 2) -1 if entry currently not on display
3562 physical_line(struct hdr_line *l)
3564 register int p_line = COMPOSER_TOP_LINE;
3565 register struct hdr_line *lp;
3566 int i;
3568 for(lp=ods.top_l, i=ods.top_e; headents[i].name && lp != NULL; p_line++){
3569 if(p_line >= BOTTOM())
3570 break;
3572 if(lp == l)
3573 return(p_line);
3575 lp = next_hline(&i, lp);
3577 return(-1);
3583 * call_builder() - resolve any nicknames in the address book associated
3584 * with the given entry...
3586 * NOTES:
3588 * BEWARE: this function can cause cur_l and top_l to get lost so BE
3589 * CAREFUL before and after you call this function!!!
3591 * There could to be something here to resolve cur_l and top_l
3592 * reasonably into the new linked list for this entry.
3594 * The reason this would mostly work without it is resolve_niks gets
3595 * called for the most part in between fields. Since we're moving
3596 * to the beginning or end (i.e. the next/prev pointer in the old
3597 * freed cur_l is NULL) of the next entry, we get a new cur_l
3598 * pointing at a good line. Then since top_l is based on cur_l in
3599 * NewTop() we have pretty much lucked out.
3601 * Where we could get burned is in a canceled exit (ctrl|x). Here
3602 * nicknames get resolved into addresses, which invalidates cur_l
3603 * and top_l. Since we don't actually leave, we could begin editing
3604 * again with bad pointers. This would usually results in a nice
3605 * core dump.
3607 * NOTE: The mangled argument is a little strange. It's used on both
3608 * input and output. On input, if it is not set, then that tells the
3609 * builder not to do anything that might take a long time, like a
3610 * white pages lookup. On return, it tells the caller that the screen
3611 * and signals may have been mangled so signals should be reset, window
3612 * resized, and screen redrawn.
3614 * RETURNS:
3615 * > 0 if any names where resolved, otherwise
3616 * 0 if not, or
3617 * < 0 on error
3618 * -1: move to next line
3619 * -2: don't move off this line
3622 call_builder(struct headerentry *entry, int *mangled, char **err)
3624 register int retval = 0;
3625 register int i;
3626 register struct hdr_line *line;
3627 int quoted = 0;
3628 int sbuflen;
3629 char *sbuf;
3630 char *s = NULL;
3631 char *tmp;
3632 struct headerentry *e;
3633 BUILDER_ARG *nextarg, *arg = NULL, *headarg = NULL;
3634 VARS_TO_SAVE *saved_state;
3636 if(!entry->builder)
3637 return(0);
3639 line = entry->hd_text;
3640 sbuflen = 0;
3641 while(line != NULL){
3642 sbuflen += (6*term.t_ncol);
3643 line = line->next;
3646 if((sbuf=(char *)malloc(sbuflen * sizeof(*sbuf))) == NULL){
3647 emlwrite("Can't malloc space to expand address", NULL);
3648 return(-1);
3651 *sbuf = '\0';
3654 * cat the whole entry into one string...
3656 line = entry->hd_text;
3657 while(line != NULL){
3658 i = ucs4_strlen(line->text);
3661 * To keep pine address builder happy, addresses should be separated
3662 * by ", ". Add this space if needed, otherwise...
3663 * (This is some ancient requirement that is no longer needed.)
3665 * If this line is NOT a continuation of the previous line, add
3666 * white space for pine's address builder if its not already there...
3667 * (This is some ancient requirement that is no longer needed.)
3669 * Also if it's not a continuation (i.e., there's already and addr on
3670 * the line), and there's another line below, treat the new line as
3671 * an implied comma.
3672 * (This should only be done for address-type lines, not for regular
3673 * text lines like subjects. Key off of the break_on_comma bit which
3674 * should only be set on those that won't mind a comma being added.)
3676 if(entry->break_on_comma){
3677 UCS *space, commaspace[3];
3679 commaspace[0] = ',';
3680 commaspace[1] = ' ';
3681 commaspace[2] = '\0';
3682 space = commaspace+1;
3684 if(i && line->text[i-1] == ','){
3685 ucs4_strncat(line->text, space, HLSZ-i-1); /* help address builder */
3686 line->text[HLSZ-1] = '\0';
3688 else if(line->next != NULL && !strend(line->text, ',')){
3689 if(ucs4_strqchr(line->text, ',', &quoted, -1)){
3690 ucs4_strncat(line->text, commaspace, HLSZ-i-1); /* implied comma */
3691 line->text[HLSZ-1] = '\0';
3694 else if(line->prev != NULL && line->next != NULL){
3695 if(ucs4_strchr(line->prev->text, ' ') != NULL
3696 && line->text[i-1] != ' '){
3697 ucs4_strncat(line->text, space, HLSZ-i-1);
3698 line->text[HLSZ-1] = '\0';
3703 tmp = ucs4_to_utf8_cpystr(line->text);
3704 if(tmp){
3705 strncat(sbuf, tmp, sbuflen-strlen(sbuf)-1);
3706 sbuf[sbuflen-1] = '\0';
3707 fs_give((void **) &tmp);
3710 line = line->next;
3713 if(entry->affected_entry){
3714 /* check if any non-sticky affected entries */
3715 for(e = entry->affected_entry; e; e = e->next_affected)
3716 if(!e->sticky)
3717 break;
3719 /* there is at least one non-sticky so make a list to pass */
3720 if(e){
3721 for(e = entry->affected_entry; e; e = e->next_affected){
3722 if(!arg){
3723 headarg = arg = (BUILDER_ARG *)malloc(sizeof(BUILDER_ARG));
3724 if(!arg){
3725 emlwrite("Can't malloc space for fcc", NULL);
3726 return(-1);
3728 else{
3729 arg->next = NULL;
3730 arg->tptr = NULL;
3731 arg->aff = &(e->bldr_private);
3732 arg->me = &(entry->bldr_private);
3735 else{
3736 nextarg = (BUILDER_ARG *)malloc(sizeof(BUILDER_ARG));
3737 if(!nextarg){
3738 emlwrite("Can't malloc space for fcc", NULL);
3739 return(-1);
3741 else{
3742 nextarg->next = NULL;
3743 nextarg->tptr = NULL;
3744 nextarg->aff = &(e->bldr_private);
3745 nextarg->me = &(entry->bldr_private);
3746 arg->next = nextarg;
3747 arg = arg->next;
3751 if(!e->sticky){
3752 line = e->hd_text;
3753 arg->tptr = ucs4_to_utf8_cpystr(line->text);
3760 * Even if there are no affected entries, we still need the arg
3761 * to pass the "me" pointer.
3763 if(!headarg){
3764 headarg = (BUILDER_ARG *)malloc(sizeof(BUILDER_ARG));
3765 if(!headarg){
3766 emlwrite("Can't malloc space", NULL);
3767 return(-1);
3769 else{
3770 headarg->next = NULL;
3771 headarg->tptr = NULL;
3772 headarg->aff = NULL;
3773 headarg->me = &(entry->bldr_private);
3778 * The builder may make a new call back to pico() so we save and
3779 * restore the pico state.
3781 saved_state = save_pico_state();
3782 retval = (*entry->builder)(sbuf, &s, err, headarg, mangled);
3783 if(saved_state){
3784 restore_pico_state(saved_state);
3785 free_pico_state(saved_state);
3788 if(mangled && *mangled & BUILDER_MESSAGE_DISPLAYED){
3789 *mangled &= ~ BUILDER_MESSAGE_DISPLAYED;
3790 if(mpresf == FALSE)
3791 mpresf = TRUE;
3794 if(mangled && *mangled & BUILDER_FOOTER_MANGLED){
3795 *mangled &= ~ BUILDER_FOOTER_MANGLED;
3796 sgarbk = TRUE;
3797 pclear(term.t_nrow-1, term.t_nrow);
3800 if(retval >= 0){
3801 if(strcmp(sbuf, s)){
3802 line = entry->hd_text;
3803 InitEntryText(s, entry); /* arrange new one */
3804 zotentry(line); /* blast old list o'entries */
3805 entry->dirty = 1; /* mark it dirty */
3806 retval = 1;
3809 for(e = entry->affected_entry, arg = headarg;
3811 e = e->next_affected, arg = arg ? arg->next : NULL){
3812 if(!e->sticky){
3813 line = e->hd_text;
3814 tmp = ucs4_to_utf8_cpystr(line->text);
3815 if(strcmp(tmp, arg ? arg->tptr : "")){ /* it changed */
3816 /* make sure they see it if changed */
3817 e->display_it = 1;
3818 InitEntryText(arg ? arg->tptr : "", e);
3819 if(line == ods.top_l)
3820 ods.top_l = e->hd_text;
3822 zotentry(line); /* blast old list o'entries */
3823 e->dirty = 1; /* mark it dirty */
3824 retval = 1;
3827 if(tmp)
3828 fs_give((void **) &tmp);
3833 if(s)
3834 free(s);
3836 for(arg = headarg; arg; arg = nextarg){
3837 /* Don't free xtra or me, they just point to headerentry data */
3838 nextarg = arg->next;
3839 if(arg->tptr)
3840 free(arg->tptr);
3842 free(arg);
3845 free(sbuf);
3846 return(retval);
3850 void
3851 call_expander(void)
3853 char **s = NULL;
3854 VARS_TO_SAVE *saved_state;
3855 int expret;
3857 if(!Pmaster->expander)
3858 return;
3861 * Since expander may make a call back to pico() we need to
3862 * save and restore pico state.
3864 if((saved_state = save_pico_state()) != NULL){
3866 expret = (*Pmaster->expander)(headents, &s);
3868 restore_pico_state(saved_state);
3869 free_pico_state(saved_state);
3870 ttresize();
3871 picosigs();
3873 if(expret > 0 && s){
3874 char *tbuf, *p;
3875 int i, biggest = 100;
3876 struct headerentry *e;
3879 * Use tbuf to cat together multiple line entries before comparing.
3881 tbuf = (char *)malloc((biggest+1) * sizeof(*tbuf));
3882 for(e = headents, i=0; e->name != NULL; e++,i++){
3883 int sz = 0;
3884 struct hdr_line *line;
3886 while(e->name && e->blank)
3887 e++;
3889 if(e->name == NULL)
3890 continue;
3892 for(line = e->hd_text; line != NULL; line = line->next){
3893 p = ucs4_to_utf8_cpystr(line->text);
3894 if(p){
3895 sz += strlen(p);
3896 fs_give((void **) &p);
3900 if(sz > biggest){
3901 biggest = sz;
3902 free(tbuf);
3903 tbuf = (char *)malloc((biggest+1) * sizeof(*tbuf));
3906 tbuf[0] = '\0';
3907 for(line = e->hd_text; line != NULL; line = line->next){
3908 p = ucs4_to_utf8_cpystr(line->text);
3909 if(p){
3910 strncat(tbuf, p, biggest+1-strlen(tbuf)-1);
3911 tbuf[biggest] = '\0';
3912 fs_give((void **) &p);
3916 if(strcmp(tbuf, s[i])){ /* it changed */
3917 struct hdr_line *zline;
3919 line = zline = e->hd_text;
3920 InitEntryText(s[i], e);
3923 * If any of the lines for this entry are current or
3924 * top, fix that.
3926 for(; line != NULL; line = line->next){
3927 if(line == ods.top_l)
3928 ods.top_l = e->hd_text;
3930 if(line == ods.cur_l)
3931 ods.cur_l = e->hd_text;
3934 zotentry(zline); /* blast old list o'entries */
3938 free(tbuf);
3941 if(s){
3942 char **p;
3944 for(p = s; *p; p++)
3945 free(*p);
3947 free(s);
3951 return;
3956 * strend - neglecting white space, returns TRUE if c is at the
3957 * end of the given line. otherwise FALSE.
3960 strend(UCS *s, UCS ch)
3962 UCS *b;
3964 if(s == NULL || *s == '\0')
3965 return(FALSE);
3967 for(b = &s[ucs4_strlen(s)] - 1; *b && ucs4_isspace(*b); b--){
3968 if(b == s)
3969 return(FALSE);
3972 return(*b == ch);
3977 * ucs4_strqchr - returns pointer to first non-quote-enclosed occurance of ch in
3978 * the given string. otherwise NULL.
3979 * s -- the string
3980 * ch -- the character we're looking for
3981 * q -- q tells us if we start out inside quotes on entry and is set
3982 * correctly on exit.
3983 * m -- max characters we'll check for ch (set to -1 for no max)
3985 UCS *
3986 ucs4_strqchr(UCS *s, UCS ch, int *q, int m)
3988 int quoted = (q) ? *q : 0;
3990 for(; s && *s && m != 0; s++, m--){
3991 if(*s == '"'){
3992 quoted = !quoted;
3993 if(q)
3994 *q = quoted;
3997 if(!quoted && *s == ch)
3998 return(s);
4001 return(NULL);
4006 * KillHeaderLine() - kill a line in the header
4008 * notes:
4009 * This is pretty simple. Just using the emacs kill buffer
4010 * and its accompanying functions to cut the text from lines.
4012 * returns:
4013 * TRUE if hldelete worked
4014 * FALSE otherwise
4017 KillHeaderLine(struct hdr_line *l, int append)
4019 UCS *c;
4020 int i = ods.p_ind;
4021 int nl = TRUE;
4023 if(!append)
4024 kdelete();
4026 c = l->text;
4027 if (gmode & MDDTKILL){
4028 if (c[i] == '\0') /* don't insert a new line after this line*/
4029 nl = FALSE;
4030 /*put to be deleted part into kill buffer */
4031 for (i=ods.p_ind; c[i] != '\0'; i++)
4032 kinsert(c[i]);
4033 }else{
4034 while(*c != '\0') /* splat out the line */
4035 kinsert(*c++);
4038 if (nl)
4039 kinsert('\n'); /* helpful to yank in body */
4041 #ifdef _WINDOWS
4042 mswin_killbuftoclip (kremove);
4043 #endif
4045 if (gmode & MDDTKILL){
4046 if (l->text[0]=='\0'){
4048 if(l->next && l->prev)
4049 ods.cur_l = next_hline(&ods.cur_e, l);
4050 else if(l->prev)
4051 ods.cur_l = prev_hline(&ods.cur_e, l);
4053 if(l == ods.top_l)
4054 ods.top_l = ods.cur_l;
4056 return(hldelete(l));
4058 else {
4059 l->text[ods.p_ind]='\0'; /* delete part of the line from the cursor */
4060 return(TRUE);
4062 }else{
4063 if(l->next && l->prev)
4064 ods.cur_l = next_hline(&ods.cur_e, l);
4065 else if(l->prev)
4066 ods.cur_l = prev_hline(&ods.cur_e, l);
4068 if(l == ods.top_l)
4069 ods.top_l = ods.cur_l;
4071 return(hldelete(l)); /* blast it */
4078 * SaveHeaderLines() - insert the saved lines in the list before the
4079 * current line in the header
4081 * notes:
4082 * Once again, just using emacs' kill buffer and its
4083 * functions.
4085 * returns:
4086 * TRUE if something good happend
4087 * FALSE otherwise
4090 SaveHeaderLines(void)
4092 UCS *buf; /* malloc'd copy of buffer */
4093 UCS *bp; /* pointer to above buffer */
4094 register unsigned i; /* index */
4095 UCS *work_buf, *work_buf_begin;
4096 char empty[1];
4097 int len, buf_len, work_buf_len, tentative_p_ind = 0;
4098 struct hdr_line *travel, *tentative_cur_l = NULL;
4100 if(ksize()){
4101 if((bp = buf = (UCS *) malloc((ksize()+5) * sizeof(*buf))) == NULL){
4102 emlwrite("Can't malloc space for saved text", NULL);
4103 return(FALSE);
4106 else
4107 return(FALSE);
4109 for(i=0; i < ksize(); i++)
4110 if(kremove(i) != '\n') /* filter out newlines */
4111 *bp++ = (UCS) kremove(i);
4113 *bp = '\0';
4115 while(--bp >= buf) /* kill trailing white space */
4116 if(*bp != ' '){
4117 if(ods.cur_l->text[0] != '\0'){
4118 if(*bp == '>'){ /* inserting an address */
4119 *++bp = ','; /* so add separator */
4120 *++bp = '\0';
4123 else{ /* nothing in field yet */
4124 if(*bp == ','){ /* so blast any extra */
4125 *bp = '\0'; /* separators */
4128 break;
4131 /* insert new text at the dot position */
4132 buf_len = ucs4_strlen(buf);
4133 tentative_p_ind = ods.p_ind + buf_len;
4134 work_buf_len = ucs4_strlen(ods.cur_l->text) + buf_len;
4135 work_buf = (UCS *) malloc((work_buf_len + 1) * sizeof(UCS));
4136 if (work_buf == NULL) {
4137 emlwrite("Can't malloc space for saved text", NULL);
4138 return(FALSE);
4141 work_buf[0] = '\0';
4142 work_buf_begin = work_buf;
4143 i = MIN(ods.p_ind, work_buf_len);
4144 ucs4_strncpy(work_buf, ods.cur_l->text, i);
4145 work_buf[i] = '\0';
4146 ucs4_strncat(work_buf, buf, work_buf_len+1-ucs4_strlen(work_buf)-1);
4147 work_buf[work_buf_len] = '\0';
4148 ucs4_strncat(work_buf, &ods.cur_l->text[ods.p_ind], work_buf_len+1-ucs4_strlen(work_buf)-1);
4149 work_buf[work_buf_len] = '\0';
4150 empty[0]='\0';
4151 ods.p_ind = 0;
4153 i = TRUE;
4155 /* insert text in HLSZ character chunks */
4156 while(work_buf_len + ods.p_ind > HLSZ) {
4157 ucs4_strncpy(&ods.cur_l->text[ods.p_ind], work_buf, HLSZ-ods.p_ind);
4158 work_buf += (HLSZ - ods.p_ind);
4159 work_buf_len -= (HLSZ - ods.p_ind);
4161 if(FormatLines(ods.cur_l, empty, LINEWID(),
4162 headents[ods.cur_e].break_on_comma, 0) == -1) {
4163 i = FALSE;
4164 break;
4165 } else {
4166 i = TRUE;
4167 len = 0;
4168 travel = ods.cur_l;
4169 while (len < HLSZ){
4170 len += ucs4_strlen(travel->text);
4171 if (len >= HLSZ)
4172 break;
4175 * This comes after the break above because it will
4176 * be accounted for in the while loop below.
4178 if(!tentative_cur_l){
4179 if(tentative_p_ind <= ucs4_strlen(travel->text))
4180 tentative_cur_l = travel;
4181 else
4182 tentative_p_ind -= ucs4_strlen(travel->text);
4185 travel = travel->next;
4188 ods.cur_l = travel;
4189 ods.p_ind = ucs4_strlen(travel->text) - len + HLSZ;
4193 /* insert the remainder of text */
4194 if (i != FALSE && work_buf_len > 0) {
4195 ucs4_strncpy(&ods.cur_l->text[ods.p_ind], work_buf, HLSZ-ods.p_ind);
4196 ods.cur_l->text[HLSZ-1] = '\0';
4197 work_buf = work_buf_begin;
4198 free(work_buf);
4200 if(FormatLines(ods.cur_l, empty, LINEWID(),
4201 headents[ods.cur_e].break_on_comma, 0) == -1) {
4202 i = FALSE;
4203 } else {
4204 len = 0;
4205 travel = ods.cur_l;
4206 while (len < work_buf_len + ods.p_ind){
4207 if(!tentative_cur_l){
4208 if(tentative_p_ind <= ucs4_strlen(travel->text))
4209 tentative_cur_l = travel;
4210 else
4211 tentative_p_ind -= ucs4_strlen(travel->text);
4214 len += ucs4_strlen(travel->text);
4215 if (len >= work_buf_len + ods.p_ind)
4216 break;
4218 travel = travel->next;
4221 ods.cur_l = travel;
4222 ods.p_ind = ucs4_strlen(travel->text) - len + work_buf_len + ods.p_ind;
4223 if(tentative_cur_l
4224 && tentative_p_ind >= 0
4225 && tentative_p_ind <= ucs4_strlen(tentative_cur_l->text)){
4226 ods.cur_l = tentative_cur_l;
4227 ods.p_ind = tentative_p_ind;
4232 free(buf);
4233 return(i);
4239 * break_point - Break the given line at the most reasonable character breakch
4240 * within maxwid max characters.
4242 * returns:
4243 * Pointer to the best break point in s, or
4244 * Pointer to the beginning of s if no break point found
4246 UCS *
4247 break_point(UCS *line, int maxwid, UCS breakch, int *quotedarg)
4249 UCS *bp; /* break point */
4250 int quoted;
4253 * Start at maxwid and work back until first opportunity to break.
4255 bp = ucs4_particular_width(line, maxwid);
4258 * Quoted should be set up for the start of line. Since we want
4259 * to move to bp and work our way back we need to scan through the
4260 * line up to bp setting quoted appropriately.
4262 if(quotedarg)
4263 ucs4_strqchr(line, '\0', quotedarg, bp-line);
4265 quoted = quotedarg ? *quotedarg : 0;
4267 while(bp != line){
4268 if(breakch == ',' && *bp == '"') /* don't break on quoted ',' */
4269 quoted = !quoted; /* toggle quoted state */
4271 if(*bp == breakch && !quoted){
4272 if(breakch == ' '){
4273 if(ucs4_str_width_ptr_to_ptr(line, bp+1) < maxwid){
4274 bp++; /* leave the ' ' */
4275 break;
4278 else{
4280 * if break char isn't a space, leave a space after
4281 * the break char.
4283 if(!(ucs4_str_width_ptr_to_ptr(line, bp+1) >= maxwid
4284 || (bp[1] == ' ' && ucs4_str_width_ptr_to_ptr(line, bp+2) >= maxwid))){
4285 bp += (bp[1] == ' ') ? 2 : 1;
4286 break;
4291 bp--;
4294 if(quotedarg)
4295 *quotedarg = quoted;
4297 return((quoted) ? line : bp);
4304 * hldelete() - remove the header line pointed to by l from the linked list
4305 * of lines.
4307 * notes:
4308 * the case of first line in field is kind of bogus. since
4309 * the array of headers has a pointer to the first line, and
4310 * i don't want to worry about this too much, i just copied
4311 * the line below and removed it rather than the first one
4312 * from the list.
4314 * returns:
4315 * TRUE if it worked
4316 * FALSE otherwise
4319 hldelete(struct hdr_line *l)
4321 register struct hdr_line *lp;
4323 if(l == NULL)
4324 return(FALSE);
4326 if(l->next == NULL && l->prev == NULL){ /* only one line in field */
4327 l->text[0] = '\0';
4328 return(TRUE); /* no free only line in list */
4330 else if(l->next == NULL){ /* last line in field */
4331 l->prev->next = NULL;
4333 else if(l->prev == NULL){ /* first line in field */
4334 ucs4_strncpy(l->text, l->next->text, HLSZ);
4335 l->text[HLSZ-1] = '\0';
4336 lp = l->next;
4337 if((l->next = lp->next) != NULL)
4338 l->next->prev = l;
4339 l = lp;
4341 else{ /* some where in field */
4342 l->prev->next = l->next;
4343 l->next->prev = l->prev;
4346 l->next = NULL;
4347 l->prev = NULL;
4348 free((char *)l);
4349 return(TRUE);
4355 * is_blank - returns true if the next n chars from coordinates row, col
4356 * on display are spaces
4359 is_blank(int row, int col, int n)
4361 n += col;
4362 for( ;col < n; col++){
4363 if(pscr(row, col) == NULL || pscr(row, col)->c != ' ')
4364 return(0);
4366 return(1);
4371 * ShowPrompt - display key help corresponding to the current header entry
4373 void
4374 ShowPrompt(void)
4376 if(headents[ods.cur_e].key_label){
4377 menu_header[TO_KEY].name = "^T";
4378 menu_header[TO_KEY].label = headents[ods.cur_e].key_label;
4379 KS_OSDATASET(&menu_header[TO_KEY], KS_OSDATAGET(&headents[ods.cur_e]));
4381 else
4382 menu_header[TO_KEY].name = NULL;
4384 if(Pmaster && Pmaster->exit_label)
4385 menu_header[SEND_KEY].label = Pmaster->exit_label;
4386 else if(gmode & (MDVIEW | MDHDRONLY))
4387 menu_header[SEND_KEY].label = (gmode & MDHDRONLY) ? "eXit/Save" : "eXit";
4388 else
4389 menu_header[SEND_KEY].label = N_("Send");
4391 if(gmode & MDVIEW){
4392 menu_header[CUT_KEY].name = NULL;
4393 menu_header[DEL_KEY].name = NULL;
4394 menu_header[UDEL_KEY].name = NULL;
4396 else{
4397 menu_header[CUT_KEY].name = "^K";
4398 menu_header[DEL_KEY].name = "^D";
4399 menu_header[UDEL_KEY].name = "^U";
4402 if(Pmaster->ctrlr_label){
4403 menu_header[RICH_KEY].label = Pmaster->ctrlr_label;
4404 menu_header[RICH_KEY].name = "^R";
4406 else if(gmode & MDHDRONLY){
4407 menu_header[RICH_KEY].name = NULL;
4409 else{
4410 menu_header[RICH_KEY].label = N_("Rich Hdr");
4411 menu_header[RICH_KEY].name = "^R";
4414 if(gmode & MDHDRONLY){
4415 if(headents[ods.cur_e].fileedit){
4416 menu_header[PONE_KEY].name = "^_";
4417 menu_header[PONE_KEY].label = N_("Edit File");
4419 else
4420 menu_header[PONE_KEY].name = NULL;
4422 menu_header[ATT_KEY].name = NULL;
4424 else{
4425 menu_header[PONE_KEY].name = "^O";
4426 menu_header[PONE_KEY].label = N_("Postpone");
4428 menu_header[ATT_KEY].name = "^J";
4431 wkeyhelp(menu_header);
4436 * packheader - packup all of the header fields for return to caller.
4437 * NOTE: all of the header info passed in, including address
4438 * of the pointer to each string is contained in the
4439 * header entry array "headents".
4442 packheader(void)
4444 register int i = 0; /* array index */
4445 register int count; /* count of chars in a field */
4446 register int retval = TRUE;
4447 register char *bufp;
4448 register struct hdr_line *line;
4449 char *p;
4451 if(!headents)
4452 return(TRUE);
4454 while(headents[i].name != NULL){
4455 #ifdef ATTACHMENTS
4457 * attachments are special case, already in struct we pass back
4459 if(headents[i].is_attach){
4460 i++;
4461 continue;
4463 #endif
4465 if(headents[i].blank){
4466 i++;
4467 continue;
4471 * count chars to see if we need a new malloc'd space for our
4472 * array.
4474 line = headents[i].hd_text;
4475 count = 0;
4476 while(line != NULL){
4478 * add one for possible concatination of a ' ' character ...
4480 p = ucs4_to_utf8_cpystr(line->text);
4481 if(p){
4482 count += strlen(p);
4483 if(p[0] && p[strlen(p)-1] == ',')
4484 count++;
4486 fs_give((void **) &p);
4489 line = line->next;
4492 line = headents[i].hd_text;
4493 if(count <= headents[i].maxlen){
4494 *headents[i].realaddr[0] = '\0';
4496 else{
4498 * don't forget to include space for the null terminator!!!!
4500 if((bufp = (char *)malloc((count+1) * sizeof(char))) != NULL){
4501 *bufp = '\0';
4503 free(*headents[i].realaddr);
4504 *headents[i].realaddr = bufp;
4505 headents[i].maxlen = count;
4507 else{
4508 emlwrite("Can't make room to pack header field.", NULL);
4509 retval = FALSE;
4513 if(retval != FALSE){
4514 int saw_current_line = 0;
4516 while(line != NULL){
4518 /* pass the cursor offset back in Pmaster struct */
4519 if(headents[i].start_here && Pmaster && !saw_current_line){
4520 if(ods.cur_l == line)
4521 saw_current_line++;
4522 else
4523 Pmaster->edit_offset += ucs4_strlen(line->text);
4526 p = ucs4_to_utf8_cpystr(line->text);
4527 if(p){
4528 strncat(*headents[i].realaddr, p, headents[i].maxlen+1-strlen(*headents[i].realaddr)-1);
4529 (*headents[i].realaddr)[headents[i].maxlen] = '\0';
4531 if(p[0] && p[strlen(p)-1] == ','){
4532 strncat(*headents[i].realaddr, " ", headents[i].maxlen+1-strlen(*headents[i].realaddr)-1);
4533 (*headents[i].realaddr)[headents[i].maxlen] = '\0';
4536 fs_give((void **) &p);
4539 line = line->next;
4543 i++;
4546 return(retval);
4552 * zotheader - free all malloc'd lines associated with the header structs
4554 void
4555 zotheader(void)
4557 register struct headerentry *i;
4559 for(i = headents; headents && i->name; i++)
4560 zotentry(i->hd_text);
4565 * zotentry - free malloc'd space associated with the given linked list
4567 void
4568 zotentry(struct hdr_line *l)
4570 register struct hdr_line *ld, *lf = l;
4572 while((ld = lf) != NULL){
4573 lf = ld->next;
4574 ld->next = ld->prev = NULL;
4575 free((char *) ld);
4582 * zotcomma - blast any trailing commas and white space from the end
4583 * of the given line
4586 zotcomma(UCS *s)
4588 UCS *p;
4589 int retval = FALSE;
4591 p = &s[ucs4_strlen(s)];
4592 while(--p >= s){
4593 if(*p != ' '){
4594 if(*p == ','){
4595 *p = '\0';
4596 retval = TRUE;
4599 return(retval);
4603 return(retval);
4608 * Save the current state of global variables so that we can restore
4609 * them later. This is so we can call pico again.
4610 * Also have to initialize some variables that normally would be set to
4611 * zero on startup.
4613 VARS_TO_SAVE *
4614 save_pico_state(void)
4616 VARS_TO_SAVE *ret;
4617 extern int vtrow;
4618 extern int vtcol;
4619 extern int lbound;
4620 extern VIDEO **vscreen;
4621 extern VIDEO **pscreen;
4622 extern int pico_all_done;
4623 extern jmp_buf finstate;
4624 extern UCS *pico_anchor;
4626 if((ret = (VARS_TO_SAVE *)malloc(sizeof(VARS_TO_SAVE))) == NULL)
4627 return(ret);
4629 ret->vtrow = vtrow;
4630 ret->vtcol = vtcol;
4631 ret->lbound = lbound;
4632 ret->vscreen = vscreen;
4633 ret->pscreen = pscreen;
4634 ret->ods = ods;
4635 ret->delim_ps = delim_ps;
4636 ret->invert_ps = invert_ps;
4637 ret->pico_all_done = pico_all_done;
4638 memcpy(ret->finstate, finstate, sizeof(jmp_buf));
4639 ret->pico_anchor = pico_anchor;
4640 ret->Pmaster = Pmaster;
4641 ret->fillcol = fillcol;
4642 if((ret->pat = (UCS *)malloc(sizeof(UCS) * (ucs4_strlen(pat)+1))) != NULL)
4643 ucs4_strncpy(ret->pat, pat, ucs4_strlen(pat)+1);
4645 ret->ComposerTopLine = ComposerTopLine;
4646 ret->ComposerEditing = ComposerEditing;
4647 ret->gmode = gmode;
4648 ret->alt_speller = alt_speller;
4649 ret->quote_str = glo_quote_str;
4650 ret->wordseps = glo_wordseps;
4651 ret->currow = currow;
4652 ret->curcol = curcol;
4653 ret->thisflag = thisflag;
4654 ret->lastflag = lastflag;
4655 ret->curgoal = curgoal;
4656 ret->opertree = (char *) malloc(sizeof(char) * (strlen(opertree) + 1));
4657 if(ret->opertree != NULL)
4658 strncpy(ret->opertree, opertree, strlen(opertree)+1);
4660 ret->curwp = curwp;
4661 ret->wheadp = wheadp;
4662 ret->curbp = curbp;
4663 ret->bheadp = bheadp;
4664 ret->km_popped = km_popped;
4665 ret->mrow = term.t_mrow;
4667 /* Initialize for next pico call */
4668 wheadp = NULL;
4669 curwp = NULL;
4670 bheadp = NULL;
4671 curbp = NULL;
4673 return(ret);
4677 void
4678 restore_pico_state(VARS_TO_SAVE *state)
4680 extern int vtrow;
4681 extern int vtcol;
4682 extern int lbound;
4683 extern VIDEO **vscreen;
4684 extern VIDEO **pscreen;
4685 extern int pico_all_done;
4686 extern jmp_buf finstate;
4687 extern UCS *pico_anchor;
4689 clearcursor();
4690 vtrow = state->vtrow;
4691 vtcol = state->vtcol;
4692 lbound = state->lbound;
4693 vscreen = state->vscreen;
4694 pscreen = state->pscreen;
4695 ods = state->ods;
4696 delim_ps = state->delim_ps;
4697 invert_ps = state->invert_ps;
4698 pico_all_done = state->pico_all_done;
4699 memcpy(finstate, state->finstate, sizeof(jmp_buf));
4700 pico_anchor = state->pico_anchor;
4701 Pmaster = state->Pmaster;
4702 if(Pmaster)
4703 headents = Pmaster->headents;
4705 fillcol = state->fillcol;
4706 if(state->pat)
4707 ucs4_strncpy(pat, state->pat, NPAT);
4709 ComposerTopLine = state->ComposerTopLine;
4710 ComposerEditing = state->ComposerEditing;
4711 gmode = state->gmode;
4712 alt_speller = state->alt_speller;
4713 glo_quote_str = state->quote_str;
4714 glo_wordseps = state->wordseps;
4715 currow = state->currow;
4716 curcol = state->curcol;
4717 thisflag = state->thisflag;
4718 lastflag = state->lastflag;
4719 curgoal = state->curgoal;
4720 if(state->opertree){
4721 strncpy(opertree, state->opertree, sizeof(opertree));
4722 opertree[sizeof(opertree)-1] = '\0';
4725 curwp = state->curwp;
4726 wheadp = state->wheadp;
4727 curbp = state->curbp;
4728 bheadp = state->bheadp;
4729 km_popped = state->km_popped;
4730 term.t_mrow = state->mrow;
4734 void
4735 free_pico_state(VARS_TO_SAVE *state)
4737 if(state->pat)
4738 free(state->pat);
4740 if(state->opertree)
4741 free(state->opertree);
4743 free(state);
4748 * Ok to call this twice in a row because it won't do anything the second
4749 * time.
4751 void
4752 fix_mangle_and_err(int *mangled, char **errmsg, char *name)
4754 if(mangled && *mangled){
4755 ttresize();
4756 picosigs();
4757 PaintBody(0);
4758 *mangled = 0;
4761 if(errmsg && *errmsg){
4762 if(**errmsg){
4763 char err[500];
4765 snprintf(err, sizeof(err), "%s field: %s", name, *errmsg);
4766 (*term.t_beep)();
4767 emlwrite(err, NULL);
4769 else
4770 mlerase();
4772 free(*errmsg);
4773 *errmsg = NULL;
4779 * What is this for?
4780 * This is so that the To line will be appended to by an Lcc
4781 * entry unless the user types in the To line after the Lcc
4782 * has already been set.
4784 void
4785 mark_sticky(struct headerentry *h)
4787 if(h && (!h->sticky_special || h->bldr_private))
4788 h->sticky = 1;
4792 #ifdef MOUSE
4793 #undef HeaderEditor
4796 * Wraper function for the real header editor.
4797 * Does the important tasks of:
4798 * 1) verifying that we _can_ edit the headers.
4799 * 2) acting on the result code from the header editor.
4802 HeaderEditor(int f, int n)
4804 int retval;
4807 #ifdef _WINDOWS
4808 /* Sometimes we get here from a scroll callback, which
4809 * is no good at all because mswin is not ready to process input and
4810 * this _headeredit() will never do anything.
4811 * Putting this test here was the most general solution I could think
4812 * of. */
4813 if (!mswin_caninput())
4814 return (-1);
4815 #endif
4817 retval = HeaderEditorWork(f, n);
4818 if (retval == -3) {
4819 retval = mousepress(0,0);
4821 return (retval);
4823 #endif