2 * ========================================================================
3 * Copyright 2006-2009 University of Washington
4 * Copyright 2013-2022 Eduardo Chappa
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
14 * Program: Pine composer routines
18 * - composer.c is the composer for the PINE mail system
22 * Notes: These routines aren't incorporated yet, because the composer as
23 * a whole still needs development. These are ideas that should
24 * be implemented in later releases of PINE. See the notes in
25 * pico.c concerning what needs to be done ....
29 * Notes: Installed header line editing, with wrapping and unwrapping,
30 * context sensitive help, and other mail header editing features.
32 * - finalish code cleanup 07/15/91
34 * Notes: Killed/yanked header lines use emacs kill buffer.
35 * Arbitrarily large headers are handled gracefully.
36 * All formatting handled by FormatLines.
38 * - Work done to optimize display painting 06/26/92
39 * Not as clean as it should be, needs more thought
44 #include "osdep/terminal.h"
45 #include "../pith/string.h"
47 int InitEntryText(char *, struct headerentry
*);
48 int HeaderOffset(int);
49 int HeaderFocus(int, int);
50 UCS
LineEdit(int, UCS
*);
51 int header_downline(int, int);
52 int header_upline(int);
53 int FormatLines(struct hdr_line
*, char *, int, int, int);
54 int FormatSyncAttach(void);
55 UCS
*ucs4_strqchr(UCS
*, UCS
, int *, int);
56 int ComposerHelp(int);
58 void display_delimiter(int);
59 void zotentry(struct hdr_line
*);
60 int InvertPrompt(int, int);
61 int partial_entries(void);
62 int physical_line(struct hdr_line
*);
63 int strend(UCS
*, UCS
);
64 int KillHeaderLine(struct hdr_line
*, int);
65 int SaveHeaderLines(void);
66 UCS
*break_point(UCS
*, int, UCS
, int *);
67 int hldelete(struct hdr_line
*);
68 int is_blank(int, int, int);
70 struct hdr_line
*first_hline(int *);
71 struct hdr_line
*first_sel_hline(int *);
72 struct hdr_line
*next_hline(int *, struct hdr_line
*);
73 struct hdr_line
*next_sel_hline(int *, struct hdr_line
*);
74 struct hdr_line
*prev_hline(int *, struct hdr_line
*);
75 struct hdr_line
*prev_sel_hline(int *, struct hdr_line
*);
76 struct hdr_line
*first_requested_hline(int *);
77 void fix_mangle_and_err(int *, char **, char *);
78 void mark_sticky(struct headerentry
*);
82 * definition header field array, structures defined in pico.h
84 struct headerentry
*headents
;
88 * structure that keeps track of the range of header lines that are
89 * to be displayed and other fun stuff about the header
91 struct on_display ods
; /* global on_display struct */
93 /* a pointer to the subject line. This is so that we do not have to compute
94 * the header line in every call. It saves a few CPU cycles
97 struct hdr_line
*subject_line
= NULL
;
102 #define HALLOC() (struct hdr_line *)malloc(sizeof(struct hdr_line))
103 #define LINEWID() (((term.t_ncol - headents[ods.cur_e].prwid) > 0) ? ((unsigned) (term.t_ncol - headents[ods.cur_e].prwid)) : 0)
104 #define BOTTOM() (term.t_nrow - term.t_mrow)
105 #define FULL_SCR() (BOTTOM() - 3)
106 #define HALF_SCR() (FULL_SCR()/2)
110 * Redefine HeaderEditor to install wrapper required for mouse event
113 #define HeaderEditor HeaderEditorWork
116 #define HDR_DELIM "----- Message Text -----"
119 * useful declarations
121 static short delim_ps
= 0; /* previous state */
122 static short invert_ps
= 0; /* previous state */
125 static KEYMENU menu_header
[] = {
126 /* TRANSLATORS: command key labels, Send means send the message
127 we are currently working on. */
128 {"^G", N_("Get Help"), KS_SCREENHELP
}, {"^X", N_("Send"), KS_SEND
},
129 /* TRANSLATORS: Rich Headers is a command to display more headers. It
130 is triggered with the ^R key. PrvPg stands for Previous Page. */
131 {"^R", N_("Rich Hdr"), KS_RICHHDR
}, {"^Y", N_("PrvPg/Top"), KS_PREVPAGE
},
132 /* TRANSLATORS: Cut Line means remove a line. Postpone means to save
133 a message being composed so that it can be worked on later. */
134 {"^K", N_("Cut Line"), KS_CURPOSITION
}, {"^O", N_("Postpone"), KS_POSTPONE
},
135 /* TRANSLATORS: Del Char is Delete Character */
136 {"^C", N_("Cancel"), KS_CANCEL
}, {"^D", N_("Del Char"), KS_NONE
},
137 /* TRANSLATORS: Next Page */
138 {"^J", N_("Attach"), KS_ATTACH
}, {"^V", N_("NxtPg/End"), KS_NEXTPAGE
},
139 /* TRANSLATORS: Undelete a line that was just deleted */
140 {"^U", N_("UnDel Line"), KS_NONE
}, {NULL
, NULL
}
153 * function key mappings for header editor
155 static UCS ckm
[12][2] = {
172 * InitMailHeader - initialize header array, and set beginning editor row
173 * range. The header entry structure should look just like
174 * what is written on the screen, the vector
175 * (entry, line, offset) will describe the current cursor
176 * position in the header.
178 * Returns: TRUE if special header handling was requested,
179 * FALSE under standard default behavior.
182 InitMailHeader(PICO
*mp
)
185 struct headerentry
*he
;
194 * initialize some of on_display structure, others below...
197 ods
.p_line
= COMPOSER_TOP_LINE
;
198 ods
.top_l
= ods
.cur_l
= NULL
;
200 headents
= mp
->headents
;
201 /*--- initialize the fields in the headerent structure ----*/
202 for(he
= headents
; he
->name
!= NULL
; he
++){
204 he
->display_it
= he
->display_it
? he
->display_it
: !he
->rich_header
;
206 /*--- A lot of work to do since attachments are special ---*/
208 if(mp
->attachments
!= NULL
){
211 int l1
, ofp
, ofp1
, ofp2
; /* OverFlowProtection */
212 size_t addrbuflen
= 4 * NLINE
; /* malloc()ed size of addrbuf */
213 PATMT
*ap
= mp
->attachments
;
217 addrbuf
= (char *)malloc(addrbuflen
);
221 if((l1
= strlen(ap
->filename
)) <= ofp
){
231 snprintf(buf
, sizeof(buf
), "%d. %.*s%s %s%s%s\"",
235 (l1
> ofp
) ? "...]" : "",
237 ap
->size
? ap
->size
: "",
238 ap
->size
? ") " : "");
240 /* append description, escaping quotes */
242 char *dp
= ap
->description
, *bufp
= &buf
[strlen(buf
)];
246 if(*dp
== '\"' && !escaped
){
254 while(--ofp2
> 0 && (*bufp
++ = *dp
++));
259 snprintf(buf
+ strlen(buf
), sizeof(buf
)-strlen(buf
), "\"%s", ap
->next
? "," : "");
261 if(strlen(addrbuf
) + strlen(buf
) >= addrbuflen
){
262 addrbuflen
+= NLINE
* 4;
263 if(!(addrbuf
= (char *)realloc(addrbuf
, addrbuflen
))){
264 emlwwrite(_("Can't realloc addrbuf to %d bytes"),
265 (void *) addrbuflen
);
270 strncat(addrbuf
, buf
, addrbuflen
-strlen(addrbuf
)-1);
271 addrbuf
[addrbuflen
-1] = '\0';
275 InitEntryText(addrbuf
, he
);
276 free((char *)addrbuf
);
278 InitEntryText("", he
);
283 addrbuf
= *(he
->realaddr
);
287 InitEntryText(addrbuf
, he
);
292 * finish initialization and then figure out display layout.
293 * first, look for any field the caller requested we start in,
294 * and set the offset into that field.
296 if((ods
.cur_l
= first_requested_hline(&ods
.cur_e
)) != NULL
){
297 ods
.top_e
= 0; /* init top_e */
298 ods
.top_l
= first_hline(&ods
.top_e
);
299 if(!HeaderFocus(ods
.cur_e
, Pmaster
? Pmaster
->edit_offset
: 0))
300 HeaderFocus(ods
.cur_e
, 0);
305 ods
.top_l
= first_hline(&ods
.cur_e
);
306 ods
.cur_l
= first_sel_hline(&ods
.cur_e
);
307 ods
.top_e
= ods
.cur_e
;
318 * InitEntryText - Add the given header text into the header entry
322 InitEntryText(char *utf8text
, struct headerentry
*e
)
324 struct hdr_line
*curline
;
325 register int longest
;
328 * get first chunk of memory, and tie it to structure...
330 if((curline
= HALLOC()) == NULL
){
331 emlwrite("Unable to make room for full Header.", NULL
);
335 longest
= term
.t_ncol
- e
->prwid
- 1;
336 curline
->text
[0] = '\0';
337 curline
->next
= NULL
;
338 curline
->prev
= NULL
;
339 e
->hd_text
= curline
; /* tie it into the list */
341 if(FormatLines(curline
, utf8text
, longest
, e
->break_on_comma
, 0) == -1)
350 * ResizeHeader - Handle resizing display when SIGWINCH received.
353 * works OK, but needs thorough testing
359 register struct headerentry
*i
;
365 offset
= (ComposerEditing
) ? HeaderOffset(ods
.cur_e
) : 0;
367 for(i
=headents
; i
->name
; i
++){ /* format each entry */
368 if(FormatLines(i
->hd_text
, "", (term
.t_ncol
- i
->prwid
),
369 i
->break_on_comma
, 0) == -1){
374 if(ComposerEditing
) /* restart at the top */
375 HeaderFocus(ods
.cur_e
, offset
); /* fix cur_l and p_ind */
380 for(i
= headents
; i
->name
!= NULL
; i
++); /* Find last line */
383 l
= headents
[e
].hd_text
;
384 if(!headents
[e
].display_it
|| headents
[e
].blank
)
385 l
= prev_sel_hline(&e
, l
); /* last selectable line */
389 l
= headents
[e
].hd_text
;
392 HeaderFocus(e
, -1); /* put focus on last line */
395 if(ComposerTopLine
!= COMPOSER_TOP_LINE
)
401 movecursor(ods
.p_line
, ods
.p_ind
+headents
[ods
.cur_e
].prwid
);
410 * HeaderOffset - return the character offset into the given header
415 register struct hdr_line
*l
;
418 l
= headents
[h
].hd_text
;
420 while(l
!= ods
.cur_l
){
421 i
+= ucs4_strlen(l
->text
);
430 * HeaderFocus - put the dot at the given offset into the given header
433 HeaderFocus(int h
, int offset
)
435 register struct hdr_line
*l
;
439 if(offset
== -1) /* focus on last line */
442 l
= headents
[h
].hd_text
;
444 if(last
&& l
->next
== NULL
){
448 if((i
=ucs4_strlen(l
->text
)) >= offset
)
453 if((l
= l
->next
) == NULL
)
458 ods
.p_len
= ucs4_strlen(l
->text
);
459 ods
.p_ind
= (last
) ? 0 : offset
;
467 * HeaderEditor() - edit the mail header field by field, trapping
468 * important key sequences, hand the hard work off
471 * -3 if we drop out bottom *and* want to process mouse event
472 * -1 if we drop out the bottom
473 * FALSE if editing is cancelled
474 * TRUE if editing is finished
477 HeaderEditor(int f
, int n
)
482 struct headerentry
*h
;
483 int cur_e
, mangled
, retval
= -1,
484 hdr_only
= (gmode
& MDHDRONLY
) ? 1 : 0;
491 return(TRUE
); /* nothing to edit! */
493 ComposerEditing
= TRUE
;
494 display_delimiter(0); /* provide feedback */
497 mswin_setscrollrange (0, 0);
498 #endif /* _WINDOWS */
501 for (subject_line
= NULL
, i
=0; headents
[i
].name
; i
++)
502 if(strcmp(headents
[i
].name
, "Subject") == 0)
503 subject_line
= headents
[i
].hd_text
;
506 * Decide where to begin editing. if f == TRUE begin editing
507 * at the bottom. this case results from the cursor backing
508 * into the editor from the bottom. otherwise, the user explicitly
509 * requested editing the header, and we begin at the top.
511 * further, if f == 1, we moved into the header by hitting the up arrow
512 * in the message text, else if f == 2, we moved into the header by
513 * moving past the left edge of the top line in the message. so, make
514 * the end of the last line of the last entry the current cursor position
515 * lastly, if f == 3, we moved into the header by hitting backpage() on
516 * the top line of the message, so scroll a page back.
519 if(f
== 2){ /* 2 leaves cursor at end */
520 struct hdr_line
*l
= ods
.cur_l
;
523 /*--- make sure on last field ---*/
524 while((l
= next_sel_hline(&e
, l
)) != NULL
)
525 if(headents
[ods
.cur_e
].display_it
){
530 ods
.p_ind
= 1000; /* and make sure at EOL */
534 * note: assumes that ods.cur_e and ods.cur_l haven't changed
539 if(curwp
->w_doto
< headents
[ods
.cur_e
].prwid
)
541 else if(curwp
->w_doto
< ods
.p_ind
+ headents
[ods
.cur_e
].prwid
)
542 ods
.p_ind
= curwp
->w_doto
- headents
[ods
.cur_e
].prwid
;
546 /* and scroll back if needed */
548 for(i
= 0; header_upline(0) && i
<= FULL_SCR(); i
++)
552 ods
.p_line
= ComposerTopLine
- 2;
554 /* else just trust what ods contains */
556 InvertPrompt(ods
.cur_e
, TRUE
); /* highlight header field */
568 if(km_popped
){ /* temporarily change to cause menu to paint */
570 curwp
->w_ntrows
-= 2;
571 movecursor(term
.t_nrow
-2, 0); /* clear status line, too */
574 else if(term
.t_mrow
== 0)
577 ShowPrompt(); /* display correct options */
581 curwp
->w_ntrows
+= 2;
585 ch
= LineEdit(!(gmode
&MDVIEW
), &lastch
); /* work on the current line */
595 movecursor(term
.t_nrow
-2, 0);
597 movecursor(term
.t_nrow
-1, 0);
599 movecursor(term
.t_nrow
, 0);
605 case (CTRL
|'R') : /* Toggle header display */
606 if(Pmaster
->pine_flags
& MDHDRONLY
){
607 if(Pmaster
->expander
){
617 /*---- Are there any headers to expand above us? ---*/
618 for(h
= headents
; h
!= &headents
[ods
.cur_e
]; h
++)
622 InvertPrompt(ods
.cur_e
, FALSE
); /* Yes, don't leave inverted */
626 if(partial_entries()){
627 /*--- Just turned off all rich headers --*/
628 if(headents
[ods
.cur_e
].rich_header
){
629 /*-- current header got turned off too --*/
631 if(headents
[ods
.cur_e
].is_attach
){
632 /* verify the attachments */
633 if((i
= FormatSyncAttach()) != 0){
634 FormatLines(headents
[ods
.cur_e
].hd_text
, "",
635 term
.t_ncol
- headents
[ods
.cur_e
].prwid
,
636 headents
[ods
.cur_e
].break_on_comma
, 0);
640 if(headents
[ods
.cur_e
].builder
) /* verify text */
641 i
= call_builder(&headents
[ods
.cur_e
], &mangled
, &err
)>0;
643 for(cur_e
=ods
.cur_e
; headents
[cur_e
].name
!=NULL
; cur_e
++)
644 if(!headents
[cur_e
].rich_header
)
646 if(headents
[cur_e
].name
== NULL
) {
647 /* didn't find one, check above */
648 for(cur_e
=ods
.cur_e
; headents
[cur_e
].name
!=NULL
;
650 if(!headents
[cur_e
].rich_header
)
656 ods
.cur_l
= headents
[ods
.cur_e
].hd_text
;
660 ods
.p_line
= 0; /* force update */
662 PaintHeader(COMPOSER_TOP_LINE
, FALSE
);
664 fix_mangle_and_err(&mangled
, &err
, headents
[ods
.cur_e
].name
);
667 case (CTRL
|'C') : /* bag whole thing ?*/
668 if(abort_composer(1, 0) == TRUE
)
673 case (CTRL
|'X') : /* Done. Send it. */
676 if(headents
[ods
.cur_e
].is_attach
){
677 /* verify the attachments, and pretty things up in case
678 * we come back to the composer due to error...
680 if((i
= FormatSyncAttach()) != 0){
681 sleep(2); /* give time for error to absorb */
682 FormatLines(headents
[ods
.cur_e
].hd_text
, "",
683 term
.t_ncol
- headents
[ods
.cur_e
].prwid
,
684 headents
[ods
.cur_e
].break_on_comma
, 0);
691 if(headents
[ods
.cur_e
].builder
) /* verify text? */
692 i
= call_builder(&headents
[ods
.cur_e
], &mangled
, &err
);
694 if(i
< 0){ /* don't leave without a valid addr */
695 fix_mangle_and_err(&mangled
, &err
, headents
[ods
.cur_e
].name
);
699 ods
.cur_l
= headents
[ods
.cur_e
].hd_text
; /* attach cur_l */
701 ods
.p_line
= 0; /* force realignment */
702 fix_mangle_and_err(&mangled
, &err
, headents
[ods
.cur_e
].name
);
706 fix_mangle_and_err(&mangled
, &err
, headents
[ods
.cur_e
].name
);
708 if(wquit(1,0) == TRUE
)
713 * need to be careful here because pointers might be messed up.
714 * also, could do a better job of finding the right place to
715 * put the dot back (i.e., the addr/list that was expanded).
718 PaintHeader(COMPOSER_TOP_LINE
, FALSE
);
723 case (CTRL
|'Z') : /* Suspend compose */
724 if(gmode
&MDSSPD
){ /* is it allowed? */
734 #if defined MOUSE && !defined(_WINDOWS)
735 toggle_xterm_mouse(0,1);
741 case (CTRL
|'O') : /* Suspend message */
742 if(Pmaster
->pine_flags
& MDHDRONLY
)
748 if(headents
[ods
.cur_e
].is_attach
){
749 if(FormatSyncAttach() < 0){
750 if(mlyesno_utf8(_("Problem with attachments. Postpone anyway?"),
752 if(FormatLines(headents
[ods
.cur_e
].hd_text
, "",
753 term
.t_ncol
- headents
[ods
.cur_e
].prwid
,
754 headents
[ods
.cur_e
].break_on_comma
, 0) == -1)
755 emlwwrite(_("Format lines failed!"), NULL
);
757 PaintHeader(COMPOSER_TOP_LINE
, FALSE
);
763 else if(headents
[ods
.cur_e
].builder
)
764 i
= call_builder(&headents
[ods
.cur_e
], &mangled
, &err
);
766 fix_mangle_and_err(&mangled
, &err
, headents
[ods
.cur_e
].name
);
768 if(i
< 0) /* don't leave without a valid addr */
771 suspend_composer(1, 0);
775 case (CTRL
|'J') : /* handle attachments */
776 if(Pmaster
->pine_flags
& MDHDRONLY
)
780 LMLIST
*lm
= NULL
, *lmp
;
781 char buf
[NLINE
], *bfp
;
786 * Attachment questions mess with km_popped and assume
787 * it is zero to start with. If we don't set it to zero
788 * on entry, the message about mime type will be erased
789 * by PaintBody. If we don't reset it when we come back,
790 * the bottom three lines may be messed up.
792 saved_km_popped
= km_popped
;
795 if(AskAttach(cmt
, sizeof(cmt
), &lm
)){
797 for(lmp
= lm
; lmp
; lmp
= lmp
->next
){
800 len
= lmp
->dir
? strlen(lmp
->dir
)+1 : 0;
801 len
+= lmp
->fname
? strlen(lmp
->fname
) : 0;
803 if(len
+3 > sizeof(buf
)){
805 bfp
= malloc(space
*sizeof(char));
807 emlwwrite(_("Can't malloc space for filename"),
817 if(lmp
->dir
&& lmp
->dir
[0])
818 snprintf(bfp
, space
, "%s%c%s", lmp
->dir
, C_FILESEP
,
819 lmp
->fname
? lmp
->fname
: "");
821 snprintf(bfp
, space
, "%s", lmp
->fname
? lmp
->fname
: "");
823 (void) QuoteAttach(bfp
, space
);
825 (void) AppendAttachment(bfp
, lm
->size
, cmt
);
834 km_popped
= saved_km_popped
;
835 sgarbk
= 1; /* clean up prompt */
840 case (CTRL
|'I') : /* tab */
841 if(headents
[ods
.cur_e
].nickcmpl
!= NULL
){
842 char *new_nickname
= NULL
;
844 UCS
*start
= NULL
, *end
= NULL
, *before_start
= NULL
;
845 UCS
*uprefix
= NULL
, *up1
, *up2
;
846 char *prefix
= NULL
, *saveprefix
= NULL
, *insert
= NULL
;
848 int offset
, prefixlen
, add_a_comma
= 0;
850 int ambiguity
, fallthru
= 1;
852 strng
= ods
.cur_l
->text
;
853 offset
= HeaderOffset(ods
.cur_e
);
856 && (start
= &strng
[ods
.p_ind
-1])
858 || ucs4_isspace(*(start
+1))
859 || *(start
+1) == ',')
861 && !ucs4_isspace(*start
)
868 && *(start
-1) != ',')
871 while(ucs4_isspace(*start
))
874 if(*end
!= ',' && ods
.cur_l
->next
)
878 * Nickname prefix begins with start and ends
879 * with end-1. Replace those characters with
880 * completed nickname.
882 prefixlen
= end
-start
;
883 uprefix
= (UCS
*) fs_get((prefixlen
+1) * sizeof(UCS
));
884 ucs4_strncpy(uprefix
, start
, prefixlen
);
885 uprefix
[prefixlen
] = '\0';
886 prefix
= ucs4_to_utf8_cpystr(uprefix
);
887 fs_give((void **) &uprefix
);
890 * Ambiguity == 0 means no matches exist.
891 * 1 means it is ambiguous and the longest
892 * unambiguous prefix beginning with prefix
893 * is in new_nickname.
894 * 2 means it is unambiguous and the answer
897 * The expansion from == 2 doesn't have to have prefix
898 * as a prefix. For example, if the prefix is a prefix
899 * of the full name or a prefix of a nickname the answer
900 * might be the full address instead of the expanded
901 * prefix. In fact, that's probably the expected thing.
903 * Due to the way the build_abook_tries sets up the tries
904 * it is unfortunately the case that the expanded address
905 * is not entered in any trie, so after you get an
906 * unambiguous expansion if you try TAB again at that
907 * point you'll probably get a no match returned instead
908 * of an unambiguous. So be aware of that and handle
909 * that case ok by falling through to header_downline.
911 ambiguity
= (*(headents
[ods
.cur_e
].nickcmpl
))(prefix
,
912 &new_nickname
, (lastch
== ch
), 0);
915 * Don't fall through on no-match TAB unless
916 * it is the second TAB.
918 if(ambiguity
== 0 && lastch
!= ch
){
927 goto nomore_to_complete
;
930 if(strcmp(new_nickname
, prefix
)){
932 * We're trying to work with the way
933 * FormatLines works. It inserts text at the
934 * beginning of the line we pass in.
935 * So, remove the beginning of the line and
936 * have FormatLines put it back.
939 /* save part before start */
941 before_start
= strng
;
942 uprefix
= (UCS
*) fs_get((start
-before_start
+1) * sizeof(UCS
));
943 ucs4_strncpy(uprefix
, before_start
, start
-before_start
);
944 uprefix
[start
-before_start
] = '\0';
945 saveprefix
= ucs4_to_utf8_cpystr(uprefix
);
947 /* figure out new cursor offset */
948 up1
= utf8_to_ucs4_cpystr(new_nickname
);
950 offset
+= (ucs4_strlen(up1
) - prefixlen
);
951 fs_give((void **) &up1
);
954 fs_give((void **) &uprefix
);
957 * Delete everything up to end by
958 * copying characters to start of buffer.
962 for(i
= ods
.p_len
- (end
- before_start
) + 1; i
> 0; i
--)
965 ods
.p_len
-= (end
- before_start
);
968 l1
= strlen(saveprefix
);
969 l2
= strlen(new_nickname
);
973 if(add_a_comma
&& ambiguity
== 2){
980 insert
= (char *) fs_get((l
+1) * sizeof(char));
983 * Insert is the before start stuff plus the
984 * new nickname, and we're going to let
985 * FormatLines put it together for us.
988 strncpy(insert
, saveprefix
, l
);
989 strncpy(insert
+l1
, new_nickname
, l
-l1
);
996 fs_give((void **) &saveprefix
);
1000 if(insert
&& FormatLines(ods
.cur_l
, insert
,
1001 term
.t_ncol
- headents
[ods
.cur_e
].prwid
,
1002 headents
[ods
.cur_e
].break_on_comma
,0)==-1){
1003 emlwwrite(_("Format lines failed!"), NULL
);
1007 fs_give((void **) &insert
);
1009 HeaderFocus(ods
.cur_e
, offset
);
1015 ambig
= new_nickname
;
1016 new_nickname
= NULL
;
1019 if(!ambig
&& prefix
){
1025 if(ambiguity
== 2 && fallthru
){
1027 fs_give((void **) &prefix
);
1030 fs_give((void **) &new_nickname
);
1033 fs_give((void **) &ambig
);
1037 goto nomore_to_complete
;
1044 fs_give((void **) &prefix
);
1047 fs_give((void **) &new_nickname
);
1050 fs_give((void **) &ambig
);
1053 goto nomore_to_complete
;
1060 ods
.p_ind
= 0; /* fall through... */
1065 header_downline(!hdr_only
, hdr_only
);
1073 case (CTRL
|'V') : /* down a page */
1076 if(!next_sel_hline(&cur_e
, ods
.cur_l
)){
1077 header_downline(!hdr_only
, hdr_only
);
1078 if(!(gmode
& MDHDRONLY
))
1079 retval
= -1; /* tell caller we fell out */
1082 int move_down
, bot_pline
;
1083 struct hdr_line
*new_cur_l
, *line
, *next_line
, *prev_line
;
1085 move_down
= BOTTOM() - 2 - ods
.p_line
;
1090 * Count down move_down lines to find the pointer to the line
1091 * that we want to become the current line.
1093 new_cur_l
= ods
.cur_l
;
1095 for(i
= 0; i
< move_down
; i
++){
1096 next_line
= next_hline(&cur_e
, new_cur_l
);
1100 new_cur_l
= next_line
;
1103 if(headents
[cur_e
].blank
){
1104 next_line
= next_sel_hline(&cur_e
, new_cur_l
);
1108 new_cur_l
= next_line
;
1112 * Now call header_downline until we get down to the
1113 * new current line, so that the builders all get called.
1114 * New_cur_l will remain valid since we won't be calling
1115 * a builder for it during this loop.
1117 while(ods
.cur_l
!= new_cur_l
&& header_downline(0, 0))
1121 * Count back up, if we're at the bottom, to find the new
1125 if(next_hline(&cur_e
, ods
.cur_l
) == NULL
){
1127 * Cursor stops at bottom of headers, which is where
1128 * we are right now. Put as much of headers on
1129 * screen as will fit. Count up to figure
1130 * out which line is top_l and which p_line cursor is on.
1134 /* leave delimiter on screen, too */
1135 bot_pline
= BOTTOM() - 1 - ((gmode
& MDHDRONLY
) ? 0 : 1);
1136 for(i
= COMPOSER_TOP_LINE
; i
< bot_pline
; i
++){
1137 prev_line
= prev_hline(&cur_e
, line
);
1150 ods
.top_l
= ods
.cur_l
;
1151 ods
.top_e
= ods
.cur_e
;
1153 * We don't want to scroll down further than the
1154 * delimiter, so check to see if that is the case.
1155 * If it is, we move the p_line down the screen
1156 * until the bottom line is where we want it.
1158 bot_pline
= BOTTOM() - 1 - ((gmode
& MDHDRONLY
) ? 0 : 1);
1161 for(i
= bot_pline
; i
> COMPOSER_TOP_LINE
; i
--){
1162 next_line
= next_hline(&cur_e
, line
);
1170 * i is the desired value of p_line.
1171 * If it is greater than COMPOSER_TOP_LINE, then
1172 * we need to adjust top_l.
1177 for(; i
> COMPOSER_TOP_LINE
; i
--){
1178 prev_line
= prev_hline(&cur_e
, line
);
1189 * Special case. If p_line is within one of the bottom,
1190 * move it to the bottom.
1192 if(ods
.p_line
== bot_pline
- 1){
1193 header_downline(0, 0);
1194 /* but put these back where we want them */
1195 ods
.p_line
= bot_pline
;
1202 PaintHeader(COMPOSER_TOP_LINE
, FALSE
);
1208 case (CTRL
|'Y') : /* up a page */
1210 for(i
= 0; header_upline(0) && i
<= FULL_SCR(); i
++)
1218 mouse_get_last (NULL
, &mp
);
1220 case M_BUTTON_LEFT
:
1221 if (!mp
.doubleclick
) {
1222 if (mp
.row
< ods
.p_line
) {
1223 for (i
= ods
.p_line
- mp
.row
;
1224 i
> 0 && header_upline(0);
1229 for (i
= mp
.row
-ods
.p_line
;
1230 i
> 0 && header_downline(!hdr_only
, 0);
1235 if((ods
.p_ind
= mp
.col
- headents
[ods
.cur_e
].prwid
) <= 0)
1238 /* -3 is returned if we drop out bottom
1239 * *and* want to process a mousepress. The Headereditor
1240 * wrapper should make sense of this return code.
1242 if (ods
.p_line
>= ComposerTopLine
)
1248 case M_BUTTON_RIGHT
:
1252 case M_BUTTON_MIDDLE
:
1253 default : /* NOOP */
1260 case (CTRL
|'T') : /* Call field selector */
1262 if(headents
[ods
.cur_e
].is_attach
) {
1263 /*--- selector for attachments ----*/
1264 char dir
[NLINE
], fn
[NLINE
], sz
[NLINE
];
1265 LMLIST
*lm
= NULL
, *lmp
;
1267 strncpy(dir
, (gmode
& MDCURDIR
)
1268 ? (browse_dir
[0] ? browse_dir
: ".")
1269 : ((gmode
& MDTREE
) || opertree
[0])
1271 : (browse_dir
[0] ? browse_dir
1272 : gethomedir(NULL
)), sizeof(dir
));
1273 dir
[sizeof(dir
)-1] = '\0';
1274 fn
[0] = sz
[0] = '\0';
1275 if(FileBrowse(dir
, sizeof(dir
), fn
, sizeof(fn
), sz
, sizeof(sz
),
1276 FB_READ
| FB_ATTACH
| FB_LMODEPOS
, &lm
) == 1){
1277 char buf
[NLINE
], *bfp
;
1280 for(lmp
= lm
; lmp
; lmp
= lmp
->next
){
1283 len
= lmp
->dir
? strlen(lmp
->dir
)+1 : 0;
1284 len
+= lmp
->fname
? strlen(lmp
->fname
) : 0;
1286 len
+= strlen(lmp
->size
);
1288 if(len
+3 > sizeof(buf
)){
1290 bfp
= malloc(space
*sizeof(char));
1292 emlwwrite(_("Can't malloc space for filename"),
1299 space
= sizeof(buf
);
1302 if(lmp
->dir
&& lmp
->dir
[0])
1303 snprintf(bfp
, space
, "%s%c%s", lmp
->dir
, C_FILESEP
,
1304 lmp
->fname
? lmp
->fname
: "");
1306 snprintf(bfp
, space
, "%s", lmp
->fname
? lmp
->fname
: "");
1308 (void) QuoteAttach(bfp
, space
);
1310 snprintf(bfp
+ strlen(bfp
), space
-strlen(bfp
), " (%s) \"\"%s", lmp
->size
,
1311 (!headents
[ods
.cur_e
].hd_text
->text
[0]) ? "":",");
1313 if(FormatLines(headents
[ods
.cur_e
].hd_text
, bfp
,
1314 term
.t_ncol
- headents
[ods
.cur_e
].prwid
,
1315 headents
[ods
.cur_e
].break_on_comma
,0)==-1){
1316 emlwwrite(_("Format lines failed!"), NULL
);
1326 } /* else, nothing of interest */
1327 } else if (headents
[ods
.cur_e
].selector
!= NULL
) {
1328 VARS_TO_SAVE
*saved_state
;
1330 /*---- General selector for non-attachments -----*/
1333 * Since the selector may make a new call back to pico()
1334 * we need to save and restore the pico state here.
1336 if((saved_state
= save_pico_state()) != NULL
){
1337 bufp
= (*(headents
[ods
.cur_e
].selector
))(&errmss
);
1338 restore_pico_state(saved_state
);
1339 free_pico_state(saved_state
);
1340 ttresize(); /* fixup screen bufs */
1341 picosigs(); /* restore altered signals */
1344 char *s
= "Not enough memory";
1348 errmss
= (char *)malloc((len
+1) * sizeof(char));
1349 strncpy(errmss
, s
, len
+1);
1357 if(headents
[ods
.cur_e
].break_on_comma
) {
1358 /*--- Must be an address ---*/
1361 * If current line is empty and there are more
1362 * lines that follow, delete the empty lines
1363 * before adding the new address.
1365 if(ods
.cur_l
->text
[0] == '\0' && ods
.cur_l
->next
){
1367 KillHeaderLine(ods
.cur_l
, 1);
1368 ods
.p_len
= ucs4_strlen(ods
.cur_l
->text
);
1369 } while(ods
.cur_l
->text
[0] == '\0' && ods
.cur_l
->next
);
1374 if(ods
.cur_l
->text
[0] != '\0'){
1375 struct hdr_line
*h
, *start_of_addr
;
1378 /* cur is not first line */
1379 if(ods
.cur_l
!= headents
[ods
.cur_e
].hd_text
){
1381 * Protect against adding a new entry into
1382 * the middle of a long, continued entry.
1384 start_of_addr
= NULL
; /* cur_l is a good place to be */
1386 for(h
= headents
[ods
.cur_e
].hd_text
; h
; h
= h
->next
){
1387 if(ucs4_strqchr(h
->text
, ',', &q
, -1)){
1388 start_of_addr
= NULL
;
1391 else if(start_of_addr
== NULL
)
1394 if(h
->next
== ods
.cur_l
)
1399 ods
.cur_l
= start_of_addr
;
1400 ods
.p_len
= ucs4_strlen(ods
.cur_l
->text
);
1404 for(i
= ++ods
.p_len
; i
; i
--)
1405 ods
.cur_l
->text
[i
] = ods
.cur_l
->text
[i
-1];
1407 ods
.cur_l
->text
[0] = ',';
1410 if(FormatLines(ods
.cur_l
, bufp
,
1411 (term
.t_ncol
-headents
[ods
.cur_e
].prwid
),
1412 headents
[ods
.cur_e
].break_on_comma
, 0) == -1){
1413 emlwrite("Problem adding address to header !",
1420 * If the "selector" has a "builder" as well, pass
1421 * what was just selected thru the builder...
1423 if(headents
[ods
.cur_e
].builder
){
1425 int cur_row
, top_too
= 0;
1427 for(l
= headents
[ods
.cur_e
].hd_text
, cur_row
= 0;
1428 l
&& l
!= ods
.cur_l
;
1429 l
= l
->next
, cur_row
++)
1432 top_too
= headents
[ods
.cur_e
].hd_text
== ods
.top_l
;
1434 if(call_builder(&headents
[ods
.cur_e
], &mangled
,
1436 fix_mangle_and_err(&mangled
, &err
,
1437 headents
[ods
.cur_e
].name
);
1440 for(ods
.cur_l
= headents
[ods
.cur_e
].hd_text
;
1441 ods
.cur_l
->next
&& cur_row
;
1442 ods
.cur_l
= ods
.cur_l
->next
, cur_row
--)
1446 ods
.top_l
= headents
[ods
.cur_e
].hd_text
;
1453 u
= utf8_to_ucs4_cpystr(bufp
);
1455 ucs4_strncpy(headents
[ods
.cur_e
].hd_text
->text
, u
, HLSZ
);
1456 headents
[ods
.cur_e
].hd_text
->text
[HLSZ
-1] = '\0';
1457 fs_give((void **) &u
);
1462 /* mark this entry dirty */
1463 mark_sticky(&headents
[ods
.cur_e
]);
1464 headents
[ods
.cur_e
].dirty
= 1;
1465 fix_mangle_and_err(&mangled
,&err
,headents
[ods
.cur_e
].name
);
1468 /*----- No selector -----*/
1474 if(errmss
!= NULL
) {
1476 emlwrite(errmss
, NULL
);
1482 case (CTRL
|'G'): /* HELP */
1483 if(term
.t_mrow
== 0){
1486 sgarbk
= 1; /* bring up menu */
1491 if(!ComposerHelp(ods
.cur_e
))
1492 break; /* else, fall through... */
1494 case (CTRL
|'L'): /* redraw requested */
1498 case (CTRL
|'_'): /* file editor */
1499 if(headents
[ods
.cur_e
].fileedit
!= NULL
){
1500 struct headerentry
*e
;
1501 struct hdr_line
*line
;
1503 char *filename
= NULL
;
1504 VARS_TO_SAVE
*saved_state
;
1507 * Since the fileedit will make a new call back to pico()
1508 * we need to save and restore the pico state here.
1510 if((saved_state
= save_pico_state()) != NULL
){
1513 e
= &headents
[ods
.cur_e
];
1515 for(line
= e
->hd_text
; line
!= NULL
; line
= line
->next
)
1516 sz
+= ucs4_strlen(line
->text
);
1518 u
= (UCS
*)malloc((sz
+1) * sizeof(*u
));
1521 for(line
= e
->hd_text
; line
!= NULL
; line
= line
->next
)
1522 ucs4_strncat(u
, line
->text
, sz
+1-ucs4_strlen(u
)-1);
1524 filename
= ucs4_to_utf8_cpystr(u
);
1528 errmss
= (*(headents
[ods
.cur_e
].fileedit
))(filename
);
1533 restore_pico_state(saved_state
);
1534 free_pico_state(saved_state
);
1535 ttresize(); /* fixup screen bufs */
1536 picosigs(); /* restore altered signals */
1539 char *s
= "Not enough memory";
1543 errmss
= (char *)malloc((len
+1) * sizeof(char));
1544 strncpy(errmss
, s
, len
+1);
1550 if(errmss
!= NULL
) {
1552 emlwrite(errmss
, NULL
);
1564 default : /* huh? */
1566 unknown_command(ch
);
1572 while (ods
.p_line
< ComposerTopLine
);
1574 display_delimiter(1);
1575 curwp
->w_flag
|= WFMODE
;
1576 movecursor(currow
, curcol
);
1577 ComposerEditing
= FALSE
;
1578 if (ComposerTopLine
== BOTTOM()){
1580 PaintHeader(COMPOSER_TOP_LINE
, FALSE
);
1592 header_downline(int beyond
, int gripe
)
1594 struct hdr_line
*new_l
, *l
;
1595 int new_e
, status
, fullpaint
, len
, e
, incr
= 0;
1597 /* calculate the next line: physical *and* logical */
1600 if((new_l
= next_sel_hline(&new_e
, ods
.cur_l
)) == NULL
&& !beyond
){
1605 strncpy(xx
, "Can't move down. Use ^X to ", sizeof(xx
));
1606 xx
[sizeof(xx
)-1] = '\0';
1607 strncat(xx
, (Pmaster
&& Pmaster
->exit_label
)
1608 ? Pmaster
->exit_label
1609 : (gmode
& MDHDRONLY
)
1613 : "Send", sizeof(xx
)-strlen(xx
)-1);
1614 xx
[sizeof(xx
)-1] = '\0';
1615 strncat(xx
, ".", sizeof(xx
)-strlen(xx
)-1);
1616 xx
[sizeof(xx
)-1] = '\0';
1624 * Because of blank header lines the cursor may need to move down
1625 * more than one line. Figure out how far.
1630 if((l
= next_hline(&e
, l
)) != NULL
)
1633 break; /* can't happen */
1637 fullpaint
= ods
.p_line
>= BOTTOM(); /* force full redraw? */
1639 /* expand what needs expanding */
1640 if(new_e
!= ods
.cur_e
|| !new_l
){ /* new (or last) field ! */
1642 InvertPrompt(ods
.cur_e
, FALSE
); /* turn off current entry */
1644 if(headents
[ods
.cur_e
].is_attach
) { /* verify data ? */
1645 if((status
= FormatSyncAttach()) != 0){ /* fixup if 1 or -1 */
1646 headents
[ods
.cur_e
].rich_header
= 0;
1647 if(FormatLines(headents
[ods
.cur_e
].hd_text
, "",
1648 term
.t_ncol
-headents
[new_e
].prwid
,
1649 headents
[ods
.cur_e
].break_on_comma
, 0) == -1)
1650 emlwwrite(_("Format lines failed!"), NULL
);
1652 } else if(headents
[ods
.cur_e
].builder
) { /* expand addresses */
1656 if((status
= call_builder(&headents
[ods
.cur_e
], &mangled
, &err
))>0){
1657 struct hdr_line
*l
; /* fixup ods.cur_l */
1658 ods
.p_line
= 0; /* force top line recalc */
1659 for(l
= headents
[ods
.cur_e
].hd_text
; l
; l
= l
->next
)
1662 if(new_l
) /* if new_l, force validity */
1663 new_l
= headents
[new_e
].hd_text
;
1665 NewTop(0); /* get new top_l */
1667 else if(status
< 0){ /* bad addr? no leave! */
1669 fix_mangle_and_err(&mangled
, &err
, headents
[ods
.cur_e
].name
);
1670 InvertPrompt(ods
.cur_e
, TRUE
);
1674 fix_mangle_and_err(&mangled
, &err
, headents
[ods
.cur_e
].name
);
1677 if(new_l
){ /* if one below, turn it on */
1678 InvertPrompt(new_e
, TRUE
);
1679 sgarbk
= 1; /* paint keymenu too */
1683 if(new_l
){ /* fixup new pointers */
1684 ods
.cur_l
= (ods
.cur_e
!= new_e
) ? headents
[new_e
].hd_text
: new_l
;
1686 if(ods
.p_ind
> (len
= ucs4_strlen(ods
.cur_l
->text
)))
1690 if(!new_l
|| status
|| fullpaint
){ /* handle big screen paint */
1692 PaintHeader(COMPOSER_TOP_LINE
, FALSE
);
1695 if(!new_l
){ /* make sure we're done */
1696 ods
.p_line
= ComposerTopLine
;
1697 InvertPrompt(ods
.cur_e
, FALSE
); /* turn off current entry */
1701 return(new_l
? 1 : 0);
1709 header_upline(int gripe
)
1711 struct hdr_line
*new_l
, *l
;
1712 int new_e
, status
, fullpaint
, len
, e
, incr
= 0;
1715 /* calculate the next line: physical *and* logical */
1718 if(!(new_l
= prev_sel_hline(&new_e
, ods
.cur_l
))){ /* all the way up! */
1719 ods
.p_line
= COMPOSER_TOP_LINE
;
1721 eml
.s
= (Pmaster
->pine_flags
& MDHDRONLY
) ? "entry" : "header";
1722 emlwrite(_("Can't move beyond top of %s"), &eml
);
1729 * Because of blank header lines the cursor may need to move up
1730 * more than one line. Figure out how far.
1735 if((l
= prev_hline(&e
, l
)) != NULL
)
1738 break; /* can't happen */
1742 fullpaint
= ods
.p_line
<= COMPOSER_TOP_LINE
;
1744 if(new_e
!= ods
.cur_e
){ /* new field ! */
1745 InvertPrompt(ods
.cur_e
, FALSE
);
1746 if(headents
[ods
.cur_e
].is_attach
){
1747 if((status
= FormatSyncAttach()) != 0){ /* non-zero ? reformat field */
1748 headents
[ods
.cur_e
].rich_header
= 0;
1749 if(FormatLines(headents
[ods
.cur_e
].hd_text
, "",
1750 term
.t_ncol
- headents
[ods
.cur_e
].prwid
,
1751 headents
[ods
.cur_e
].break_on_comma
,0) == -1)
1752 emlwwrite(_("Format lines failed!"), NULL
);
1755 else if(headents
[ods
.cur_e
].builder
){
1759 if((status
= call_builder(&headents
[ods
.cur_e
], &mangled
,
1762 for(new_l
= headents
[new_e
].hd_text
;
1767 /* and cur_l (required in fix_and... */
1772 fix_mangle_and_err(&mangled
, &err
, headents
[ods
.cur_e
].name
);
1773 InvertPrompt(ods
.cur_e
, TRUE
);
1777 fix_mangle_and_err(&mangled
, &err
, headents
[ods
.cur_e
].name
);
1780 InvertPrompt(new_e
, TRUE
);
1784 ods
.cur_e
= new_e
; /* update pointers */
1786 if(ods
.p_ind
> (len
= ucs4_strlen(ods
.cur_l
->text
)))
1789 if(status
> 0 || fullpaint
){
1791 PaintHeader(COMPOSER_TOP_LINE
, FALSE
);
1803 AppendAttachment(char *fn
, char *sz
, char *cmt
)
1805 int a_e
, status
, spaces
;
1806 struct hdr_line
*lp
;
1810 /*--- Find headerentry that is attachments (only first) --*/
1811 for(a_e
= 0; headents
[a_e
].name
!= NULL
; a_e
++ )
1812 if(headents
[a_e
].is_attach
){
1813 /* make sure field stays displayed */
1814 headents
[a_e
].rich_header
= 0;
1815 headents
[a_e
].display_it
= 1;
1819 /* append new attachment line */
1820 for(lp
= headents
[a_e
].hd_text
; lp
->next
; lp
=lp
->next
)
1823 /* build new attachment line */
1824 if(lp
->text
[0]){ /* adding a line? */
1829 ucs4_strncat(lp
->text
, comma
, HLSZ
-ucs4_strlen(lp
->text
)-1); /* append delimiter */
1830 if((lp
->next
= HALLOC()) != NULL
){ /* allocate new line */
1831 lp
->next
->prev
= lp
;
1832 lp
->next
->next
= NULL
;
1836 emlwwrite(_("Can't allocate line for new attachment!"), NULL
);
1842 spaces
= (*fn
== '\"') ? 0 : (strpbrk(fn
, "(), \t") != NULL
);
1843 snprintf(b
, sizeof(b
), "%s%s%s (%s) \"%.*s\"",
1844 spaces
? "\"" : "", fn
, spaces
? "\"" : "",
1845 sz
? sz
: "", 80, cmt
? cmt
: "");
1846 u
= utf8_to_ucs4_cpystr(b
);
1848 ucs4_strncpy(lp
->text
, u
, HLSZ
);
1849 lp
->text
[HLSZ
-1] = '\0';
1850 fs_give((void **) &u
);
1853 /* validate the new attachment, and reformat if needed */
1854 if((status
= SyncAttach()) != 0){
1859 emlwwrite(_("Problem attaching: %s"), &eml
);
1862 if(FormatLines(headents
[a_e
].hd_text
, "",
1863 term
.t_ncol
- headents
[a_e
].prwid
,
1864 headents
[a_e
].break_on_comma
, 0) == -1){
1865 emlwwrite(_("Format lines failed!"), NULL
);
1871 PaintHeader(COMPOSER_TOP_LINE
, status
!= 0);
1873 return(status
!= 0);
1878 * LineEdit - Always use insert mode and handle line wrapping
1881 * Any characters typed in that aren't printable
1885 * Assume we are guaranteed that there is sufficiently
1886 * more buffer space in a line than screen width (just one
1887 * less thing to worry about). If you want to change this,
1888 * then pputc will have to be taught to check the line buffer
1889 * length, and HALLOC() will probably have to become a func.
1892 LineEdit(int allowedit
, UCS
*lastch
)
1894 register struct hdr_line
*lp
; /* temporary line pointer */
1897 register int status
; /* various func's return val */
1898 UCS
*tbufp
; /* temporary buffer pointers */
1901 UCS last_key
; /* last keystroke */
1903 strng
= ods
.cur_l
->text
; /* initialize offsets */
1904 ods
.p_len
= MIN(ucs4_strlen(strng
), HLSZ
);
1905 if(ods
.p_ind
< 0) /* offset within range? */
1907 else if(ods
.p_ind
> ods
.p_len
)
1908 ods
.p_ind
= ods
.p_len
;
1909 else if(ucs4_str_width_ptr_to_ptr(&strng
[0], &strng
[ods
.p_ind
]) > LINEWID()){
1912 u
= ucs4_particular_width(strng
, LINEWID());
1913 ods
.p_ind
= u
- strng
;
1916 while(1){ /* edit the line... */
1918 if(Pmaster
&& subject_line
!= NULL
1919 && ods
.cur_l
== subject_line
1920 && ods
.cur_l
->text
[0] == 0)
1921 (*Pmaster
->newthread
)();
1926 HeaderPaintCursor();
1932 (*term
.t_flush
)(); /* get everything out */
1935 mouse_in_content(KEY_MOUSE
, -1, -1, 0, 0);
1936 register_mfunc(mouse_in_content
,2,0,term
.t_nrow
-(term
.t_mrow
+1),
1940 mswin_setdndcallback (composer_file_drop
);
1941 mswin_mousetrackcallback(pico_cursor
);
1946 if (term
.t_nrow
< 6 && ch
!= NODATA
){
1948 emlwrite(_("Please make the screen larger."), NULL
);
1953 clear_mfunc(mouse_in_content
);
1956 mswin_cleardndcallback ();
1957 mswin_mousetrackcallback(NULL
);
1962 if(gmode
& P_DELRUBS
)
1966 (*Pmaster
->keybinput
)();
1967 if(!time_to_check())
1970 case NODATA
: /* new mail ? */
1971 if((*Pmaster
->newmail
)(ch
== NODATA
? 0 : 2, 1) >= 0){
1976 curwp
->w_ntrows
-= 2;
1981 rv
= (*Pmaster
->showmsg
)(ch
);
1984 if(rv
) /* Did showmsg corrupt the display? */
1985 PaintBody(0); /* Yes, repaint */
1990 curwp
->w_ntrows
+= 2;
1995 movecursor(ods
.p_line
,
1996 ucs4_str_width_ptr_to_ptr(strng
, &strng
[ods
.p_ind
])+headents
[ods
.cur_e
].prwid
);
1997 if(ch
== NODATA
) /* GetKey timed out */
2003 if(mpresf
){ /* blast old messages */
2004 if(mpresf
++ > NMMESSDELAY
){ /* every few keystrokes */
2006 movecursor(ods
.p_line
,
2007 ucs4_str_width_ptr_to_ptr(strng
, &strng
[ods
.p_ind
])+headents
[ods
.cur_e
].prwid
);
2011 tbufp
= &strng
[ods
.p_len
];
2013 if(VALID_KEY(ch
)){ /* char input */
2015 * if we are allowing editing, insert the new char
2016 * end up leaving tbufp pointing to newly
2017 * inserted character in string, and offset to the
2018 * index of the character after the inserted ch ...
2021 if(headents
[ods
.cur_e
].is_attach
&& intag(strng
,ods
.p_ind
)){
2022 emlwwrite(_("Can't edit attachment number!"), NULL
);
2026 if(headents
[ods
.cur_e
].single_space
){
2028 && (strng
[ods
.p_ind
]==' ' || strng
[ods
.p_ind
-1]==' '))
2033 * go ahead and add the character...
2035 if(ods
.p_len
< HLSZ
){
2036 tbufp
= &strng
[++ods
.p_len
]; /* find the end */
2039 } while(--tbufp
> &strng
[ods
.p_ind
]); /* shift right */
2040 strng
[ods
.p_ind
++] = ch
; /* add char to str */
2043 /* mark this entry dirty */
2044 mark_sticky(&headents
[ods
.cur_e
]);
2045 headents
[ods
.cur_e
].dirty
= 1;
2048 * then find out where things fit...
2050 if(ucs4_str_width_ptr_to_ptr(&strng
[0], &strng
[ods
.p_len
]) < LINEWID()){
2053 c
.c
= ch
& CELLMASK
;
2055 if(pinsert(c
)){ /* add char to str */
2056 skipmove
++; /* must'a been optimal */
2057 continue; /* on to the next! */
2061 if((status
= FormatLines(ods
.cur_l
, "", LINEWID(),
2062 headents
[ods
.cur_e
].break_on_comma
,0)) == -1){
2068 * during the format, the dot may have moved
2069 * down to the next line...
2071 if(ods
.p_ind
>= ucs4_strlen(strng
)){
2073 ods
.p_ind
-= ucs4_strlen(strng
);
2074 ods
.cur_l
= ods
.cur_l
->next
;
2075 strng
= ods
.cur_l
->text
;
2078 ods
.p_len
= ucs4_strlen(strng
);
2082 PaintHeader(COMPOSER_TOP_LINE
, FALSE
);
2092 else { /* interpret ch as a command */
2093 switch (ch
= normalize_cmd(ch
, ckm
, 2)) {
2094 case (CTRL
|KEY_LEFT
): /* word skip left */
2095 if(ods
.p_ind
> 0) /* Scoot one char left if possible */
2100 if(ods
.p_line
!= COMPOSER_TOP_LINE
)
2101 ods
.p_ind
= 1000; /* put cursor at end of line */
2105 while(ods
.p_ind
> 0 && !ucs4_isalnum(strng
[ods
.p_ind
]))
2106 ods
.p_ind
--; /* skip any whitespace we're in */
2108 while(ods
.p_ind
> 0) {
2109 /* Bail if the character right before this one is whitespace */
2110 if(ods
.p_ind
> 1 && !ucs4_isalnum(strng
[ods
.p_ind
- 1]))
2116 case (CTRL
|'@') : /* word skip */
2117 case (CTRL
|KEY_RIGHT
):
2118 while(ucs4_isalnum(strng
[ods
.p_ind
]))
2119 ods
.p_ind
++; /* skip any text we're in */
2121 while(strng
[ods
.p_ind
] && !ucs4_isalnum(strng
[ods
.p_ind
]))
2122 ods
.p_ind
++; /* skip any whitespace after it */
2124 if(strng
[ods
.p_ind
] == '\0'){
2125 ods
.p_ind
= 0; /* end of line, let caller handle it */
2131 case (CTRL
|'K') : /* kill line cursor's on */
2138 if (!(gmode
& MDDTKILL
))
2141 if(KillHeaderLine(lp
, (last_key
== (CTRL
|'K')))){
2143 !(ods
.cur_l
->prev
==NULL
&& ods
.cur_l
->next
==NULL
))
2144 scrollup(wheadp
, ods
.p_line
, 1);
2146 if(ods
.cur_l
->next
== NULL
)
2147 if(zotcomma(ods
.cur_l
->text
)){
2149 ods
.p_ind
= ucs4_strlen(ods
.cur_l
->text
);
2152 i
= (ods
.p_line
== COMPOSER_TOP_LINE
);
2154 PaintHeader(COMPOSER_TOP_LINE
, TRUE
);
2158 movecursor(term
.t_nrow
, 0);
2165 strng
= ods
.cur_l
->text
;
2166 ods
.p_len
= ucs4_strlen(strng
);
2167 headents
[ods
.cur_e
].sticky
= 0;
2168 headents
[ods
.cur_e
].dirty
= 1;
2171 case (CTRL
|'U') : /* un-delete deleted lines */
2177 if(SaveHeaderLines()){
2179 PaintHeader(COMPOSER_TOP_LINE
, FALSE
);
2182 movecursor(term
.t_nrow
, 0);
2187 strng
= ods
.cur_l
->text
;
2188 ods
.p_len
= ucs4_strlen(strng
);
2189 mark_sticky(&headents
[ods
.cur_e
]);
2190 headents
[ods
.cur_e
].dirty
= 1;
2193 /* TRANSLATORS: Killing text is deleting it and
2194 Unkilling text is undeleting killed text. */
2195 emlwrite(_("Problem Unkilling text"), NULL
);
2199 case KEY_RIGHT
: /* move character right */
2200 if(ods
.p_ind
< ods
.p_len
){
2204 else if(gmode
& MDHDRONLY
)
2211 case KEY_LEFT
: /* move character left */
2216 if(ods
.p_line
!= COMPOSER_TOP_LINE
)
2217 ods
.p_ind
= 1000; /* put cursor at end of line */
2220 case (CTRL
|'M') : /* goto next field */
2225 case (CTRL
|'A') : /* goto beginning of line */
2230 case (CTRL
|'E') : /* goto end of line */
2231 ods
.p_ind
= ods
.p_len
;
2234 case (CTRL
|'D') : /* blast this char */
2240 else if(ods
.p_ind
>= ucs4_strlen(strng
))
2243 if(headents
[ods
.cur_e
].is_attach
&& intag(strng
, ods
.p_ind
)){
2244 emlwwrite(_("Can't edit attachment number!"), NULL
);
2248 pputc(strng
[ods
.p_ind
++], 0); /* drop through and rubout */
2250 case DEL
: /* blast previous char */
2257 if(headents
[ods
.cur_e
].is_attach
&& intag(strng
, ods
.p_ind
-1)){
2258 emlwwrite(_("Can't edit attachment number!"), NULL
);
2262 if(ods
.p_ind
> 0){ /* just shift left one char */
2264 headents
[ods
.cur_e
].dirty
= 1;
2266 headents
[ods
.cur_e
].sticky
= 0;
2268 mark_sticky(&headents
[ods
.cur_e
]);
2270 tbufp
= &strng
[--ods
.p_ind
];
2271 while(*tbufp
++ != '\0')
2273 tbufp
= &strng
[ods
.p_ind
];
2274 if(pdel()) /* physical screen delete */
2275 skipmove
++; /* must'a been optimal */
2277 else{ /* may have work to do */
2278 if(ods
.cur_l
->prev
== NULL
){
2279 (*term
.t_beep
)(); /* no erase into next field */
2284 ods
.cur_l
= ods
.cur_l
->prev
;
2285 strng
= ods
.cur_l
->text
;
2286 if((i
=ucs4_strlen(strng
)) > 0){
2287 strng
[i
-1] = '\0'; /* erase the character */
2291 headents
[ods
.cur_e
].sticky
= 0;
2295 tbufp
= &strng
[ods
.p_ind
];
2298 if((status
= FormatLines(ods
.cur_l
, "", LINEWID(),
2299 headents
[ods
.cur_e
].break_on_comma
,0))==-1){
2305 * beware, the dot may have moved...
2307 while((ods
.p_len
=ucs4_strlen(strng
)) < ods
.p_ind
){
2309 ods
.p_ind
-= ucs4_strlen(strng
);
2310 ods
.cur_l
= ods
.cur_l
->next
;
2311 strng
= ods
.cur_l
->text
;
2312 ods
.p_len
= ucs4_strlen(strng
);
2313 tbufp
= &strng
[ods
.p_ind
];
2320 PaintHeader(COMPOSER_TOP_LINE
, FALSE
);
2325 movecursor(ods
.p_line
,
2326 ucs4_str_width_ptr_to_ptr(strng
, &strng
[ods
.p_ind
])+headents
[ods
.cur_e
].prwid
);
2338 while ((tbufp
-strng
) < HLSZ
&& *tbufp
!= '\0') /* synchronizing loop */
2341 if(ucs4_str_width_ptr_to_ptr(&strng
[0], &strng
[ods
.p_len
]) < LINEWID())
2348 HeaderPaintCursor(void)
2350 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
);
2356 * FormatLines - Place the given text at the front of the given line->text
2357 * making sure to properly format the line, then check
2358 * all lines below for proper format.
2361 * Not much optimization at all. Right now, it recursively
2362 * fixes all remaining lines in the entry. Some speed might
2363 * gained if this was built to iteratively scan the lines.
2367 * FALSE if only this line is changed
2368 * TRUE if text below the first line is changed
2371 FormatLines(struct hdr_line
*h
, /* where to begin formatting */
2372 char *utf8_instr
, /* input string */
2373 int maxwid
, /* max width of a line */
2374 int break_on_comma
, /* break lines on commas */
2375 int quoted
) /* this line inside quotes */
2380 UCS
*ostr
; /* pointer to output string */
2381 UCS
*free_istr
= NULL
;
2383 UCS
*breakp
; /* pointer to line break */
2384 UCS
*bp
, *tp
; /* temporary pointers */
2385 UCS
*buf
; /* string to add later */
2386 struct hdr_line
*nlp
, *lp
;
2391 free_istr
= istr
= utf8_to_ucs4_cpystr(utf8_instr
);
2393 len
= ucs4_strlen(istr
) + ucs4_strlen(ostr
);
2394 if((buf
= (UCS
*) malloc((len
+10) * sizeof(*buf
))) == NULL
){
2396 fs_give((void **) &free_istr
);
2401 if(ucs4_str_width(istr
) + ucs4_str_width(ostr
) >= maxwid
){ /* break then fixup below */
2403 if((l
=ucs4_str_width(istr
)) < maxwid
){ /* room for more */
2405 if(break_on_comma
&& (bp
= ucs4_strqchr(istr
, ',', "ed
, -1))){
2406 bp
+= (bp
[1] == ' ') ? 2 : 1;
2407 for(tp
= bp
; *tp
&& *tp
== ' '; tp
++)
2410 ucs4_strncpy(buf
, tp
, len
+10);
2411 buf
[len
+10-1] = '\0';
2412 ucs4_strncat(buf
, ostr
, len
+10-ucs4_strlen(buf
)-1);
2413 buf
[len
+10-1] = '\0';
2415 for(i
= 0; &istr
[i
] < bp
; i
++)
2422 breakp
= break_point(ostr
, maxwid
-ucs4_str_width(istr
),
2423 break_on_comma
? ',' : ' ',
2424 break_on_comma
? "ed
: NULL
);
2426 if(breakp
== ostr
){ /* no good breakpoint */
2427 if(break_on_comma
&& *breakp
== ','){
2431 else if(ucs4_strchr(istr
,(break_on_comma
&& !quoted
)?',':' ')){
2432 ucs4_strncpy(buf
, ostr
, len
+10);
2433 buf
[len
+10-1] = '\0';
2434 ucs4_strncpy(ostr
, istr
, HLSZ
);
2435 ostr
[HLSZ
-1] = '\0';
2437 else{ /* istr's broken as we can get it */
2439 * Break at maxwid - width of istr
2441 breakp
= ucs4_particular_width(ostr
, maxwid
- ucs4_str_width(istr
)-1);
2449 ucs4_strncpy(buf
, breakp
, len
+10); /* save broken line */
2450 buf
[len
+10-1] = '\0';
2452 ucs4_strncpy(ostr
, istr
, HLSZ
); /* simple if no break */
2453 ostr
[HLSZ
-1] = '\0';
2456 *breakp
= '\0'; /* more work to break it */
2457 i
= ucs4_strlen(istr
);
2459 * shift ostr i chars
2461 for(bp
=breakp
; bp
>= ostr
&& i
; bp
--)
2463 for(tp
=ostr
, bp
=istr
; *bp
!= '\0'; tp
++, bp
++)
2464 *tp
= *bp
; /* then add istr */
2470 * Short-circuit the recursion in this case.
2471 * No time right now to figure out how to do it better.
2473 else if(l
> 2*maxwid
){
2474 UCS
*istrp
, *saveostr
= NULL
;
2478 saveostr
= ucs4_cpystr(ostr
);
2481 while(l
> 2*maxwid
){
2482 if(break_on_comma
|| maxwid
== 1){
2483 breakp
= (!(bp
= ucs4_strqchr(istrp
, ',', "ed
, maxwid
))
2484 || ucs4_str_width_ptr_to_ptr(istrp
, bp
) >= maxwid
|| maxwid
== 1)
2485 ? ucs4_particular_width(istrp
, maxwid
)
2486 : bp
+ ((bp
[1] == ' ') ? 2 : 1);
2489 breakp
= break_point(istrp
, maxwid
, ' ', NULL
);
2491 if(breakp
== istrp
) /* no good break point */
2492 breakp
= ucs4_particular_width(istrp
, maxwid
-1);
2495 for(tp
=ostr
,bp
=istrp
; bp
< breakp
; tp
++, bp
++)
2499 l
-= ucs4_str_width_ptr_to_ptr(istrp
, breakp
);
2502 if((lp
= HALLOC()) == NULL
){
2503 emlwrite("Can't allocate any more lines for header!", NULL
);
2506 fs_give((void **) &free_istr
);
2523 * Now l is still > maxwid. Do it the recursive way,
2524 * like the else clause below. Surely we could fix up the
2525 * flow control some here, but this works for now.
2531 ucs4_strncpy(ostr
, saveostr
, HLSZ
);
2532 ostr
[HLSZ
-1] = '\0';
2533 fs_give((void **) &saveostr
);
2536 if(break_on_comma
|| maxwid
== 1){
2537 breakp
= (!(bp
= ucs4_strqchr(istrp
, ',', "ed
, maxwid
))
2538 || ucs4_str_width_ptr_to_ptr(istrp
, bp
) >= maxwid
|| maxwid
== 1)
2539 ? ucs4_particular_width(istrp
, maxwid
)
2540 : bp
+ ((bp
[1] == ' ') ? 2 : 1);
2543 breakp
= break_point(istrp
, maxwid
, ' ', NULL
);
2545 if(breakp
== istrp
) /* no good break point */
2546 breakp
= ucs4_particular_width(istrp
, maxwid
-1);
2549 ucs4_strncpy(buf
, breakp
, len
+10); /* save broken line */
2550 buf
[len
+10-1] = '\0';
2551 ucs4_strncat(buf
, ostr
, len
+10-ucs4_strlen(buf
)-1); /* add line that was there */
2552 buf
[len
+10-1] = '\0';
2554 for(tp
=ostr
,bp
=istr
; bp
< breakp
; tp
++, bp
++)
2559 else{ /* utf8_instr > maxwid ! */
2560 if(break_on_comma
|| maxwid
== 1){
2561 breakp
= (!(bp
= ucs4_strqchr(istr
, ',', "ed
, maxwid
))
2562 || ucs4_str_width_ptr_to_ptr(istr
, bp
) >= maxwid
|| maxwid
== 1)
2563 ? ucs4_particular_width(istr
, maxwid
)
2564 : bp
+ ((bp
[1] == ' ') ? 2 : 1);
2567 breakp
= break_point(istr
, maxwid
, ' ', NULL
);
2569 if(breakp
== istr
) /* no good break point */
2570 breakp
= ucs4_particular_width(istr
, maxwid
-1);
2573 ucs4_strncpy(buf
, breakp
, len
+10); /* save broken line */
2574 buf
[len
+10-1] = '\0';
2575 ucs4_strncat(buf
, ostr
, len
+10-ucs4_strlen(buf
)-1); /* add line that was there */
2576 buf
[len
+10-1] = '\0';
2578 for(tp
=ostr
,bp
=istr
; bp
< breakp
; tp
++, bp
++)
2584 if(nlp
== NULL
){ /* no place to add below? */
2585 if((lp
= HALLOC()) == NULL
){
2586 emlwrite("Can't allocate any more lines for header!", NULL
);
2589 fs_give((void **) &free_istr
);
2594 if(TERM_OPTIMIZE
&& (i
= physical_line(h
)) != -1)
2595 scrolldown(wheadp
, i
- 1, 1);
2597 h
->next
= lp
; /* fix up links */
2605 else{ /* combined width < max */
2608 ucs4_strncpy(buf
, istr
, len
+10); /* insert istr before ostr */
2609 buf
[len
+10-1] = '\0';
2611 ucs4_strncat(buf
, ostr
, len
+10-ucs4_strlen(buf
)-1);
2612 buf
[len
+10-1] = '\0';
2614 ucs4_strncpy(ostr
, buf
, HLSZ
); /* copy back to ostr */
2615 ostr
[HLSZ
-1] = '\0';
2621 if(break_on_comma
&& (breakp
= ucs4_strqchr(ostr
, ',', "ed
, -1))){
2622 breakp
+= (breakp
[1] == ' ') ? 2 : 1;
2623 ucs4_strncpy(buf
, breakp
, len
+10);
2624 buf
[len
+10-1] = '\0';
2628 if((lp
= HALLOC()) == NULL
){
2629 emlwrite("Can't allocate any more lines for header!",NULL
);
2632 fs_give((void **) &free_istr
);
2637 if(TERM_OPTIMIZE
&& (i
= physical_line(h
)) != -1)
2638 scrolldown(wheadp
, i
- 1, 1);
2640 h
->next
= lp
; /* fix up links */
2650 if(!*buf
&& !breakp
){
2651 if(ucs4_str_width(ostr
) + ucs4_str_width(nlp
->text
) >= maxwid
){
2652 breakp
= break_point(nlp
->text
, maxwid
-ucs4_str_width(ostr
),
2653 break_on_comma
? ',' : ' ',
2654 break_on_comma
? "ed
: NULL
);
2656 if(breakp
== nlp
->text
){ /* commas this line? */
2657 for(tp
=ostr
; *tp
&& *tp
!= ' '; tp
++)
2660 if(!*tp
){ /* no commas, get next best */
2661 breakp
+= maxwid
- ucs4_str_width(ostr
) - 1;
2670 if(retval
){ /* only if something to do */
2671 for(tp
= &ostr
[ucs4_strlen(ostr
)],bp
=nlp
->text
; bp
<breakp
;
2673 *tp
= *bp
; /* add breakp to this line */
2675 for(tp
=nlp
->text
, bp
=breakp
; *bp
!= '\0'; tp
++, bp
++)
2676 *tp
= *bp
; /* shift next line to left */
2681 ucs4_strncat(ostr
, nlp
->text
, HLSZ
-ucs4_strlen(ostr
)-1);
2682 ostr
[HLSZ
-1] = '\0';
2684 if(TERM_OPTIMIZE
&& (i
= physical_line(nlp
)) != -1)
2685 scrollup(wheadp
, i
, 1);
2689 if(!(nlp
= h
->next
)){
2692 fs_give((void **) &free_istr
);
2694 return(TRUE
); /* can't go further */
2697 retval
= TRUE
; /* more work to do? */
2704 fs_give((void **) &free_istr
);
2711 utf8
= ucs4_to_utf8_cpystr(buf
);
2714 fs_give((void **) &free_istr
);
2719 i
= FormatLines(nlp
, utf8
, maxwid
, break_on_comma
, quoted
);
2720 fs_give((void **) &utf8
);
2722 case -1: /* bubble up worst case */
2726 if(retval
== FALSE
){
2741 * Format the lines before parsing attachments so we
2742 * don't expand a bunch of attachments that we don't
2743 * have the buffer space for.
2746 FormatSyncAttach(void)
2748 FormatLines(headents
[ods
.cur_e
].hd_text
, "",
2749 term
.t_ncol
- headents
[ods
.cur_e
].prwid
,
2750 headents
[ods
.cur_e
].break_on_comma
, 0);
2751 return(SyncAttach());
2756 * PaintHeader - do the work of displaying the header from the given
2757 * physical screen line the end of the header.
2759 * 17 July 91 - fixed reshow to deal with arbitrarily large headers.
2762 PaintHeader(int line
, /* physical line on screen */
2763 int clear
) /* clear before painting */
2765 struct hdr_line
*lp
;
2772 COLOR_PAIR
*lastc
= NULL
;
2774 if(Pmaster
&& Pmaster
->colors
){
2775 lastc
= pico_get_cur_color();
2776 pico_set_colorp(Pmaster
->colors
->ntcp
, PSC_NONE
);
2780 pclear(COMPOSER_TOP_LINE
, ComposerTopLine
-1);
2782 curline
= COMPOSER_TOP_LINE
;
2783 curindex
= curoffset
= 0;
2785 for(lp
= ods
.top_l
, e
= ods
.top_e
; ; curline
++){
2786 if((curline
== line
) || ((lp
= next_hline(&e
, lp
)) == NULL
))
2790 while(headents
[e
].name
!= NULL
){ /* begin to redraw */
2793 if((!lp
->prev
|| curline
== COMPOSER_TOP_LINE
) && !curoffset
){
2794 if(InvertPrompt(e
, (e
== ods
.cur_e
&& ComposerEditing
)) == -1
2795 && !is_blank(curline
, 0, headents
[e
].prwid
)){
2796 for(i
= 0; i
< headents
[e
].prwid
; i
++)
2802 else if(!is_blank(curline
, 0, headents
[e
].prwid
)){
2803 for(i
= 0; i
< headents
[e
].prwid
; i
++)
2809 if(*(bufp
= buf
) != '\0'){ /* need to paint? */
2810 movecursor(curline
, 0); /* paint the line... */
2811 while(*bufp
!= '\0')
2815 bufp
= &(lp
->text
[curindex
]); /* skip chars already there */
2816 curoffset
+= headents
[e
].prwid
;
2817 curindex
= index_from_col(curline
, curoffset
);
2818 while(pscr(curline
, curindex
) != NULL
&&
2819 *bufp
== pscr(curline
, curindex
)->c
&& *bufp
!= '\0'){
2820 w
= wcellwidth(*bufp
);
2821 curoffset
+= (w
>= 0 ? w
: 1);
2824 if(curoffset
>= term
.t_ncol
)
2828 if(*bufp
!= '\0'){ /* need to move? */
2829 movecursor(curline
, curoffset
);
2830 while(*bufp
!= '\0'){ /* display what's not there */
2832 w
= wcellwidth(*bufp
);
2833 curoffset
+= (w
>= 0 ? w
: 1);
2839 if(curoffset
< term
.t_ncol
){
2840 movecursor(curline
, curoffset
);
2844 curindex
= curoffset
= 0;
2845 if(curline
>= BOTTOM())
2851 if(curline
>= BOTTOM())
2852 return; /* don't paint delimiter */
2854 while(headents
[++e
].name
!= NULL
)
2855 if(headents
[e
].display_it
){
2856 lp
= headents
[e
].hd_text
;
2861 display_delimiter(ComposerEditing
? 0 : 1);
2864 pico_set_colorp(lastc
, PSC_NONE
);
2865 free_color_pair(&lastc
);
2872 * PaintBody() - generic call to handle repainting everything BUT the
2876 * The header redrawing in a level 0 body paint gets done
2880 PaintBody(int level
)
2882 curwp
->w_flag
|= WFHARD
; /* make sure framing's right */
2883 if(level
== 0) /* specify what to update */
2886 update(); /* display message body */
2888 if(level
== 0 && ComposerEditing
){
2889 mlerase(); /* clear the error line */
2896 * display_for_send - paint the composer from the top line and return.
2899 display_for_send(void)
2904 /* if first header line isn't displayed, there's work to do */
2905 if(headents
&& ((l
= first_hline(&i
)) != ods
.top_l
2906 || ComposerTopLine
== COMPOSER_TOP_LINE
2908 struct on_display orig_ods
;
2909 int orig_edit
= ComposerEditing
,
2910 orig_ct_line
= ComposerTopLine
;
2913 * fake that the cursor's in the first header line
2914 * and force repaint...
2918 ods
.top_l
= ods
.cur_l
= l
;
2919 ods
.top_e
= ods
.cur_e
;
2920 ods
.p_line
= COMPOSER_TOP_LINE
;
2921 ComposerEditing
= TRUE
; /* to fool update() */
2922 setimark(FALSE
, 1); /* remember where we were */
2925 UpdateHeader(0); /* redraw whole enchilada */
2926 PaintHeader(COMPOSER_TOP_LINE
, TRUE
);
2929 ods
= orig_ods
; /* restore original state */
2930 ComposerEditing
= orig_edit
;
2931 ComposerTopLine
= curwp
->w_toprow
= orig_ct_line
;
2932 curwp
->w_ntrows
= BOTTOM() - ComposerTopLine
;
2933 swapimark(FALSE
, 1);
2935 /* in case we don't exit, set up restoring the screen */
2936 sgarbf
= TRUE
; /* force redraw */
2942 * ArrangeHeader - set up display parm such that header is reasonably
2949 register struct hdr_line
*l
;
2951 ods
.p_line
= ods
.p_ind
= 0;
2953 l
= ods
.top_l
= headents
[e
].hd_text
;
2954 while(headents
[e
+1].name
|| (l
&& l
->next
))
2955 if((l
= next_sel_hline(&e
, l
)) != NULL
){
2965 * ComposerHelp() - display mail help in a context sensitive way
2966 * based on the level passed ...
2969 ComposerHelp(int level
)
2972 VARS_TO_SAVE
*saved_state
;
2974 curwp
->w_flag
|= WFMODE
;
2977 if(level
< 0 || !headents
[level
].name
){
2979 emlwrite("Sorry, I can't help you with that.", NULL
);
2984 snprintf(buf
, sizeof(buf
), "Help for %s %.40s Field",
2985 (Pmaster
->pine_flags
& MDHDRONLY
) ? "Address Book"
2987 headents
[level
].name
);
2988 saved_state
= save_pico_state();
2989 (*Pmaster
->helper
)(headents
[level
].help
, buf
, 1);
2991 restore_pico_state(saved_state
);
2992 free_pico_state(saved_state
);
2996 picosigs(); /* restore altered handlers */
3003 * ToggleHeader() - set or unset pico values to the full screen size
3004 * painting header if need be.
3007 ToggleHeader(int show
)
3010 * check to see if we need to display the header...
3013 UpdateHeader(0); /* figure bounds */
3014 PaintHeader(COMPOSER_TOP_LINE
, FALSE
); /* draw it */
3018 * set bounds for no header display
3020 curwp
->w_toprow
= ComposerTopLine
= COMPOSER_TOP_LINE
;
3021 curwp
->w_ntrows
= BOTTOM() - ComposerTopLine
;
3029 * HeaderLen() - return the length in lines of the exposed portion of the
3035 register struct hdr_line
*lp
;
3043 lp
= next_hline(&e
, lp
);
3052 * first_hline() - return a pointer to the first displayable header line
3055 * 1) pointer to first displayable line in header and header
3056 * entry, via side effect, that the first line is a part of
3057 * 2) NULL if no next line, leaving entry at LASTHDR
3060 first_hline(int *entry
)
3062 /* init *entry so we're sure to start from the top */
3063 for(*entry
= 0; headents
[*entry
].name
; (*entry
)++)
3064 if(headents
[*entry
].display_it
)
3065 return(headents
[*entry
].hd_text
);
3068 return(NULL
); /* this shouldn't happen */
3073 * first_sel_hline() - return a pointer to the first selectable header line
3076 * 1) pointer to first selectable line in header and header
3077 * entry, via side effect, that the first line is a part of
3078 * 2) NULL if no next line, leaving entry at LASTHDR
3081 first_sel_hline(int *entry
)
3083 /* init *entry so we're sure to start from the top */
3084 for(*entry
= 0; headents
[*entry
].name
; (*entry
)++)
3085 if(headents
[*entry
].display_it
&& !headents
[*entry
].blank
)
3086 return(headents
[*entry
].hd_text
);
3089 return(NULL
); /* this shouldn't happen */
3095 * next_hline() - return a pointer to the next line structure
3098 * 1) pointer to next displayable line in header and header
3099 * entry, via side effect, that the next line is a part of
3100 * 2) NULL if no next line, leaving entry at LASTHDR
3103 next_hline(int *entry
, struct hdr_line
*line
)
3108 if(line
->next
== NULL
){
3109 while(headents
[++(*entry
)].name
!= NULL
){
3110 if(headents
[*entry
].display_it
)
3111 return(headents
[*entry
].hd_text
);
3122 * next_sel_hline() - return a pointer to the next selectable line structure
3125 * 1) pointer to next selectable line in header and header
3126 * entry, via side effect, that the next line is a part of
3127 * 2) NULL if no next line, leaving entry at LASTHDR
3130 next_sel_hline(int *entry
, struct hdr_line
*line
)
3135 if(line
->next
== NULL
){
3136 while(headents
[++(*entry
)].name
!= NULL
){
3137 if(headents
[*entry
].display_it
&& !headents
[*entry
].blank
)
3138 return(headents
[*entry
].hd_text
);
3150 * prev_hline() - return a pointer to the next line structure back
3153 * 1) pointer to previous displayable line in header and
3154 * the header entry that the next line is a part of
3156 * 2) NULL if no next line, leaving entry unchanged from
3157 * the value it had on entry.
3160 prev_hline(int *entry
, struct hdr_line
*line
)
3165 if(line
->prev
== NULL
){
3168 orig_entry
= *entry
;
3169 while(--(*entry
) >= 0){
3170 if(headents
[*entry
].display_it
){
3171 line
= headents
[*entry
].hd_text
;
3172 while(line
->next
!= NULL
)
3178 *entry
= orig_entry
;
3187 * prev_sel_hline() - return a pointer to the previous selectable line
3190 * 1) pointer to previous selectable line in header and
3191 * the header entry that the next line is a part of
3193 * 2) NULL if no next line, leaving entry unchanged from
3194 * the value it had on entry.
3197 prev_sel_hline(int *entry
, struct hdr_line
*line
)
3202 if(line
->prev
== NULL
){
3205 orig_entry
= *entry
;
3206 while(--(*entry
) >= 0){
3207 if(headents
[*entry
].display_it
&& !headents
[*entry
].blank
){
3208 line
= headents
[*entry
].hd_text
;
3209 while(line
->next
!= NULL
)
3215 *entry
= orig_entry
;
3225 * first_requested_hline() - return pointer to first line that pico's caller
3226 * asked that we start on.
3229 first_requested_hline(int *ent
)
3232 struct hdr_line
*rv
= NULL
;
3234 for(reqfield
= -1, i
= 0; headents
[i
].name
; i
++)
3235 if(headents
[i
].start_here
){
3236 headents
[i
].start_here
= 0; /* clear old setting */
3237 if(reqfield
< 0){ /* if not already, set up */
3238 headents
[i
].display_it
= 1; /* make sure it's shown */
3239 *ent
= reqfield
= i
;
3240 rv
= headents
[i
].hd_text
;
3250 * UpdateHeader() - determines the best range of lines to be displayed
3251 * using the global ods value for the current line and the
3252 * top line, also sets ComposerTopLine and pico limits
3254 * showtop -- Attempt to show all header lines if they'll fit.
3257 * This is pretty ugly because it has to keep the current line
3258 * on the screen in a reasonable location no matter what.
3259 * There are also a couple of rules to follow:
3260 * 1) follow paging conventions of pico (ie, half page
3262 * 2) if more than one page, always display last half when
3263 * pline is toward the end of the header
3266 * TRUE if anything changed (side effects: new p_line, top_l
3267 * top_e, and pico parms)
3268 * FALSE if nothing changed
3272 UpdateHeader(int showtop
)
3274 register struct hdr_line
*lp
;
3277 int old_top
= ComposerTopLine
;
3278 int old_p
= ods
.p_line
;
3280 if(ods
.p_line
< COMPOSER_TOP_LINE
||
3281 ((ods
.p_line
== ComposerTopLine
-2) ? 2: 0) + ods
.p_line
>= BOTTOM()){
3282 /* NewTop if cur header line is at bottom of screen or two from */
3283 /* the bottom of the screen if cur line is bottom header line */
3284 NewTop(showtop
); /* get new top_l */
3287 else{ /* make sure p_line's OK */
3288 i
= COMPOSER_TOP_LINE
;
3291 while(lp
!= ods
.cur_l
){
3293 * this checks to make sure cur_l is below top_l and that
3294 * cur_l is on the screen...
3296 if((lp
= next_hline(&le
, lp
)) == NULL
|| ++i
>= BOTTOM()){
3304 ods
.p_line
= COMPOSER_TOP_LINE
; /* find p_line... */
3307 while(lp
&& lp
!= ods
.cur_l
){
3308 lp
= next_hline(&le
, lp
);
3313 ret
= !(ods
.p_line
== old_p
);
3315 ComposerTopLine
= ods
.p_line
; /* figure top composer line */
3316 while(lp
&& ComposerTopLine
<= BOTTOM()){
3317 lp
= next_hline(&le
, lp
);
3318 ComposerTopLine
+= (lp
) ? 1 : 2; /* allow for delim at end */
3322 ret
= !(ComposerTopLine
== old_top
);
3324 if(wheadp
->w_toprow
!= ComposerTopLine
){ /* update pico params... */
3325 wheadp
->w_toprow
= ComposerTopLine
;
3326 wheadp
->w_ntrows
= ((i
= BOTTOM() - ComposerTopLine
) > 0) ? i
: 0;
3335 * NewTop() - calculate a new top_l based on the cur_l
3337 * showtop -- Attempt to show all the header lines if they'll fit
3340 * with ods.top_l and top_e pointing at a reasonable line
3346 register struct hdr_line
*lp
;
3352 i
= showtop
? FULL_SCR() : HALF_SCR();
3354 while(lp
!= NULL
&& (--i
> 0)){
3357 lp
= prev_hline(&e
, lp
);
3364 * display_delimiter() - just paint the header/message body delimiter with
3365 * inverse value specified by state.
3368 display_delimiter(int state
)
3371 COLOR_PAIR
*lastc
= NULL
;
3373 if(ComposerTopLine
- 1 >= BOTTOM()) /* silently forget it */
3376 if(Pmaster
&& Pmaster
->colors
){
3377 lastc
= pico_get_cur_color();
3378 pico_set_colorp(Pmaster
->colors
->ntcp
, PSC_NONE
);
3381 buf
= utf8_to_ucs4_cpystr((gmode
& MDHDRONLY
) ? "" : HDR_DELIM
);
3387 if(state
== delim_ps
){ /* optimize ? */
3388 for(delim_ps
= 0; bufp
[delim_ps
] && pscr(ComposerTopLine
-1,delim_ps
) != NULL
&& pscr(ComposerTopLine
-1,delim_ps
)->c
== bufp
[delim_ps
];delim_ps
++)
3391 if(bufp
[delim_ps
] == '\0' && !(gmode
& MDHDRONLY
)){
3393 fs_give((void **) &buf
);
3394 if(lastc
) free_color_pair(&lastc
);
3395 return; /* already displayed! */
3401 movecursor(ComposerTopLine
- 1, 0);
3404 else if (*term
.t_eri
)
3407 while(*bufp
!= '\0')
3408 pputc(*bufp
++, state
? 1 : 0);
3414 fs_give((void **) &buf
);
3417 pico_set_colorp(lastc
, PSC_NONE
);
3418 free_color_pair(&lastc
);
3425 * InvertPrompt() - invert the prompt associated with header entry to state
3426 * state (true if invert, false otherwise).
3428 * non-zero if nothing done
3429 * 0 if prompt inverted successfully
3432 * come to think of it, this func and the one above could
3433 * easily be combined
3436 InvertPrompt(int entry
, int state
)
3442 buf
= utf8_to_ucs4_cpystr(headents
[entry
].prompt
); /* fresh prompt paint */
3447 if((i
= entry_line(entry
, FALSE
)) == -1){
3448 fs_give((void **) &buf
);
3449 return(-1); /* silently forget it */
3452 end
= buf
+ ucs4_strlen(buf
);
3455 * Makes sure that the prompt doesn't take up more than prwid of screen space.
3456 * The caller should do that, too, in order to make it look right so
3457 * this should most likely be a no-op
3459 if(ucs4_str_width_ptr_to_ptr(buf
, end
) > headents
[entry
].prwid
){
3460 end
= ucs4_particular_width(buf
, headents
[entry
].prwid
);
3464 if(entry
< 16 && (invert_ps
&(1<<entry
))
3465 == (state
? 1<<entry
: 0)){ /* optimize ? */
3468 for(j
= 0; bufp
[j
] && pscr(i
, j
)->c
== bufp
[j
]; j
++)
3471 if(bufp
[j
] == '\0'){
3473 invert_ps
|= 1<<entry
;
3475 invert_ps
&= ~(1<<entry
);
3477 fs_give((void **) &buf
);
3478 return(0); /* already displayed! */
3482 if(entry
< 16){ /* if > 16, cannot be stored in invert_ps */
3484 invert_ps
|= 1<<entry
;
3486 invert_ps
&= ~(1<<entry
);
3492 else if (*term
.t_eri
)
3495 while(*bufp
&& *(bufp
+ 1))
3496 pputc(*bufp
++, 1); /* putc up to last char */
3501 pputc(*bufp
, 0); /* last char not inverted */
3503 fs_give((void **) &buf
);
3511 * partial_entries() - toggle display of the bcc and fcc fields.
3514 * TRUE if there are partial entries on the display
3518 partial_entries(void)
3520 register struct headerentry
*h
;
3523 /*---- find out status of first rich header ---*/
3524 for(h
= headents
; !h
->rich_header
&& h
->name
!= NULL
; h
++)
3527 is_on
= h
->display_it
;
3528 for(h
= headents
; h
->name
!= NULL
; h
++)
3530 h
->display_it
= ! is_on
;
3538 * entry_line() - return the physical line on the screen associated
3539 * with the given header entry field. Note: the field
3540 * may span lines, so if the last char is set, return
3541 * the appropriate value.
3544 * 1) physical line number of entry
3545 * 2) -1 if entry currently not on display
3548 entry_line(int entry
, int lastchar
)
3550 register int p_line
= COMPOSER_TOP_LINE
;
3552 register struct hdr_line
*line
;
3554 for(line
= ods
.top_l
, i
= ods
.top_e
;
3555 headents
&& headents
[i
].name
&& i
<= entry
;
3557 if(p_line
>= BOTTOM())
3561 if(line
->next
== NULL
)
3564 else if(line
->prev
== NULL
)
3569 line
= next_hline(&i
, line
);
3577 * physical_line() - return the physical line on the screen associated
3578 * with the given header line pointer.
3581 * 1) physical line number of entry
3582 * 2) -1 if entry currently not on display
3585 physical_line(struct hdr_line
*l
)
3587 register int p_line
= COMPOSER_TOP_LINE
;
3588 register struct hdr_line
*lp
;
3591 for(lp
=ods
.top_l
, i
=ods
.top_e
; headents
[i
].name
&& lp
!= NULL
; p_line
++){
3592 if(p_line
>= BOTTOM())
3598 lp
= next_hline(&i
, lp
);
3606 * call_builder() - resolve any nicknames in the address book associated
3607 * with the given entry...
3611 * BEWARE: this function can cause cur_l and top_l to get lost so BE
3612 * CAREFUL before and after you call this function!!!
3614 * There could to be something here to resolve cur_l and top_l
3615 * reasonably into the new linked list for this entry.
3617 * The reason this would mostly work without it is resolve_niks gets
3618 * called for the most part in between fields. Since we're moving
3619 * to the beginning or end (i.e. the next/prev pointer in the old
3620 * freed cur_l is NULL) of the next entry, we get a new cur_l
3621 * pointing at a good line. Then since top_l is based on cur_l in
3622 * NewTop() we have pretty much lucked out.
3624 * Where we could get burned is in a canceled exit (ctrl|x). Here
3625 * nicknames get resolved into addresses, which invalidates cur_l
3626 * and top_l. Since we don't actually leave, we could begin editing
3627 * again with bad pointers. This would usually results in a nice
3630 * NOTE: The mangled argument is a little strange. It's used on both
3631 * input and output. On input, if it is not set, then that tells the
3632 * builder not to do anything that might take a long time, like a
3633 * white pages lookup. On return, it tells the caller that the screen
3634 * and signals may have been mangled so signals should be reset, window
3635 * resized, and screen redrawn.
3638 * > 0 if any names where resolved, otherwise
3641 * -1: move to next line
3642 * -2: don't move off this line
3645 call_builder(struct headerentry
*entry
, int *mangled
, char **err
)
3647 register int retval
= 0;
3649 register struct hdr_line
*line
;
3655 struct headerentry
*e
;
3656 BUILDER_ARG
*nextarg
, *arg
= NULL
, *headarg
= NULL
;
3657 VARS_TO_SAVE
*saved_state
;
3662 line
= entry
->hd_text
;
3664 while(line
!= NULL
){
3665 sbuflen
+= (6*term
.t_ncol
);
3669 if((sbuf
=(char *)malloc(sbuflen
* sizeof(*sbuf
))) == NULL
){
3670 emlwrite("Can't malloc space to expand address", NULL
);
3677 * cat the whole entry into one string...
3679 line
= entry
->hd_text
;
3680 while(line
!= NULL
){
3681 i
= ucs4_strlen(line
->text
);
3684 * To keep pine address builder happy, addresses should be separated
3685 * by ", ". Add this space if needed, otherwise...
3686 * (This is some ancient requirement that is no longer needed.)
3688 * If this line is NOT a continuation of the previous line, add
3689 * white space for pine's address builder if its not already there...
3690 * (This is some ancient requirement that is no longer needed.)
3692 * Also if it's not a continuation (i.e., there's already and addr on
3693 * the line), and there's another line below, treat the new line as
3695 * (This should only be done for address-type lines, not for regular
3696 * text lines like subjects. Key off of the break_on_comma bit which
3697 * should only be set on those that won't mind a comma being added.)
3699 if(entry
->break_on_comma
){
3700 UCS
*space
, commaspace
[3];
3702 commaspace
[0] = ',';
3703 commaspace
[1] = ' ';
3704 commaspace
[2] = '\0';
3705 space
= commaspace
+1;
3707 if(i
&& line
->text
[i
-1] == ','){
3708 ucs4_strncat(line
->text
, space
, HLSZ
-i
-1); /* help address builder */
3709 line
->text
[HLSZ
-1] = '\0';
3711 else if(line
->next
!= NULL
&& !strend(line
->text
, ',')){
3712 if(ucs4_strqchr(line
->text
, ',', "ed
, -1)){
3713 ucs4_strncat(line
->text
, commaspace
, HLSZ
-i
-1); /* implied comma */
3714 line
->text
[HLSZ
-1] = '\0';
3717 else if(line
->prev
!= NULL
&& line
->next
!= NULL
){
3718 if(ucs4_strchr(line
->prev
->text
, ' ') != NULL
3719 && line
->text
[i
-1] != ' '){
3720 ucs4_strncat(line
->text
, space
, HLSZ
-i
-1);
3721 line
->text
[HLSZ
-1] = '\0';
3726 tmp
= ucs4_to_utf8_cpystr(line
->text
);
3728 strncat(sbuf
, tmp
, sbuflen
-strlen(sbuf
)-1);
3729 sbuf
[sbuflen
-1] = '\0';
3730 fs_give((void **) &tmp
);
3736 if(entry
->affected_entry
){
3737 /* check if any non-sticky affected entries */
3738 for(e
= entry
->affected_entry
; e
; e
= e
->next_affected
)
3742 /* there is at least one non-sticky so make a list to pass */
3744 for(e
= entry
->affected_entry
; e
; e
= e
->next_affected
){
3746 headarg
= arg
= (BUILDER_ARG
*)malloc(sizeof(BUILDER_ARG
));
3748 emlwrite("Can't malloc space for fcc", NULL
);
3754 arg
->aff
= &(e
->bldr_private
);
3755 arg
->me
= &(entry
->bldr_private
);
3759 nextarg
= (BUILDER_ARG
*)malloc(sizeof(BUILDER_ARG
));
3761 emlwrite("Can't malloc space for fcc", NULL
);
3765 nextarg
->next
= NULL
;
3766 nextarg
->tptr
= NULL
;
3767 nextarg
->aff
= &(e
->bldr_private
);
3768 nextarg
->me
= &(entry
->bldr_private
);
3769 arg
->next
= nextarg
;
3776 arg
->tptr
= ucs4_to_utf8_cpystr(line
->text
);
3783 * Even if there are no affected entries, we still need the arg
3784 * to pass the "me" pointer.
3787 headarg
= (BUILDER_ARG
*)malloc(sizeof(BUILDER_ARG
));
3789 emlwrite("Can't malloc space", NULL
);
3793 headarg
->next
= NULL
;
3794 headarg
->tptr
= NULL
;
3795 headarg
->aff
= NULL
;
3796 headarg
->me
= &(entry
->bldr_private
);
3801 * The builder may make a new call back to pico() so we save and
3802 * restore the pico state.
3804 saved_state
= save_pico_state();
3805 retval
= (*entry
->builder
)(sbuf
, &s
, err
, headarg
, mangled
);
3807 restore_pico_state(saved_state
);
3808 free_pico_state(saved_state
);
3811 if(mangled
&& *mangled
& BUILDER_MESSAGE_DISPLAYED
){
3812 *mangled
&= ~ BUILDER_MESSAGE_DISPLAYED
;
3817 if(mangled
&& *mangled
& BUILDER_FOOTER_MANGLED
){
3818 *mangled
&= ~ BUILDER_FOOTER_MANGLED
;
3820 pclear(term
.t_nrow
-1, term
.t_nrow
);
3824 if(strcmp(sbuf
, s
)){
3825 line
= entry
->hd_text
;
3826 InitEntryText(s
, entry
); /* arrange new one */
3827 zotentry(line
); /* blast old list o'entries */
3828 entry
->dirty
= 1; /* mark it dirty */
3832 for(e
= entry
->affected_entry
, arg
= headarg
;
3834 e
= e
->next_affected
, arg
= arg
? arg
->next
: NULL
){
3837 tmp
= ucs4_to_utf8_cpystr(line
->text
);
3838 if(strcmp(tmp
, arg
? arg
->tptr
: "")){ /* it changed */
3839 /* make sure they see it if changed */
3841 InitEntryText(arg
? arg
->tptr
: "", e
);
3842 if(line
== ods
.top_l
)
3843 ods
.top_l
= e
->hd_text
;
3845 zotentry(line
); /* blast old list o'entries */
3846 e
->dirty
= 1; /* mark it dirty */
3851 fs_give((void **) &tmp
);
3859 for(arg
= headarg
; arg
; arg
= nextarg
){
3860 /* Don't free xtra or me, they just point to headerentry data */
3861 nextarg
= arg
->next
;
3877 VARS_TO_SAVE
*saved_state
;
3880 if(!Pmaster
->expander
)
3884 * Since expander may make a call back to pico() we need to
3885 * save and restore pico state.
3887 if((saved_state
= save_pico_state()) != NULL
){
3889 expret
= (*Pmaster
->expander
)(headents
, &s
);
3891 restore_pico_state(saved_state
);
3892 free_pico_state(saved_state
);
3896 if(expret
> 0 && s
){
3898 int i
, biggest
= 100;
3899 struct headerentry
*e
;
3902 * Use tbuf to cat together multiple line entries before comparing.
3904 tbuf
= (char *)malloc((biggest
+1) * sizeof(*tbuf
));
3905 for(e
= headents
, i
=0; e
->name
!= NULL
; e
++,i
++){
3907 struct hdr_line
*line
;
3909 while(e
->name
&& e
->blank
)
3915 for(line
= e
->hd_text
; line
!= NULL
; line
= line
->next
){
3916 p
= ucs4_to_utf8_cpystr(line
->text
);
3919 fs_give((void **) &p
);
3926 tbuf
= (char *)malloc((biggest
+1) * sizeof(*tbuf
));
3930 for(line
= e
->hd_text
; line
!= NULL
; line
= line
->next
){
3931 p
= ucs4_to_utf8_cpystr(line
->text
);
3933 strncat(tbuf
, p
, biggest
+1-strlen(tbuf
)-1);
3934 tbuf
[biggest
] = '\0';
3935 fs_give((void **) &p
);
3939 if(strcmp(tbuf
, s
[i
])){ /* it changed */
3940 struct hdr_line
*zline
;
3942 line
= zline
= e
->hd_text
;
3943 InitEntryText(s
[i
], e
);
3946 * If any of the lines for this entry are current or
3949 for(; line
!= NULL
; line
= line
->next
){
3950 if(line
== ods
.top_l
)
3951 ods
.top_l
= e
->hd_text
;
3953 if(line
== ods
.cur_l
)
3954 ods
.cur_l
= e
->hd_text
;
3957 zotentry(zline
); /* blast old list o'entries */
3979 * strend - neglecting white space, returns TRUE if c is at the
3980 * end of the given line. otherwise FALSE.
3983 strend(UCS
*s
, UCS ch
)
3987 if(s
== NULL
|| *s
== '\0')
3990 for(b
= &s
[ucs4_strlen(s
)] - 1; *b
&& ucs4_isspace(*b
); b
--){
4000 * ucs4_strqchr - returns pointer to first non-quote-enclosed occurance of ch in
4001 * the given string. otherwise NULL.
4003 * ch -- the character we're looking for
4004 * q -- q tells us if we start out inside quotes on entry and is set
4005 * correctly on exit.
4006 * m -- max characters we'll check for ch (set to -1 for no max)
4009 ucs4_strqchr(UCS
*s
, UCS ch
, int *q
, int m
)
4011 int quoted
= (q
) ? *q
: 0;
4013 for(; s
&& *s
&& m
!= 0; s
++, m
--){
4020 if(!quoted
&& *s
== ch
)
4029 * KillHeaderLine() - kill a line in the header
4032 * This is pretty simple. Just using the emacs kill buffer
4033 * and its accompanying functions to cut the text from lines.
4036 * TRUE if hldelete worked
4040 KillHeaderLine(struct hdr_line
*l
, int append
)
4050 if (gmode
& MDDTKILL
){
4051 if (c
[i
] == '\0') /* don't insert a new line after this line*/
4053 /*put to be deleted part into kill buffer */
4054 for (i
=ods
.p_ind
; c
[i
] != '\0'; i
++)
4057 while(*c
!= '\0') /* splat out the line */
4062 kinsert('\n'); /* helpful to yank in body */
4065 mswin_killbuftoclip (kremove
);
4068 if (gmode
& MDDTKILL
){
4069 if (l
->text
[0]=='\0'){
4071 if(l
->next
&& l
->prev
)
4072 ods
.cur_l
= next_hline(&ods
.cur_e
, l
);
4074 ods
.cur_l
= prev_hline(&ods
.cur_e
, l
);
4077 ods
.top_l
= ods
.cur_l
;
4079 return(hldelete(l
));
4082 l
->text
[ods
.p_ind
]='\0'; /* delete part of the line from the cursor */
4086 if(l
->next
&& l
->prev
)
4087 ods
.cur_l
= next_hline(&ods
.cur_e
, l
);
4089 ods
.cur_l
= prev_hline(&ods
.cur_e
, l
);
4092 ods
.top_l
= ods
.cur_l
;
4094 return(hldelete(l
)); /* blast it */
4101 * SaveHeaderLines() - insert the saved lines in the list before the
4102 * current line in the header
4105 * Once again, just using emacs' kill buffer and its
4109 * TRUE if something good happened
4113 SaveHeaderLines(void)
4115 UCS
*buf
; /* malloc'd copy of buffer */
4116 UCS
*bp
; /* pointer to above buffer */
4117 register unsigned i
; /* index */
4118 UCS
*work_buf
, *work_buf_begin
;
4120 int len
, buf_len
, work_buf_len
, tentative_p_ind
= 0;
4121 struct hdr_line
*travel
, *tentative_cur_l
= NULL
;
4124 if((bp
= buf
= (UCS
*) malloc((ksize()+5) * sizeof(*buf
))) == NULL
){
4125 emlwrite("Can't malloc space for saved text", NULL
);
4132 for(i
=0; i
< ksize(); i
++)
4133 if(kremove(i
) != '\n') /* filter out newlines */
4134 *bp
++ = (UCS
) kremove(i
);
4138 while(--bp
>= buf
) /* kill trailing white space */
4140 if(ods
.cur_l
->text
[0] != '\0'){
4141 if(*bp
== '>'){ /* inserting an address */
4142 *++bp
= ','; /* so add separator */
4146 else{ /* nothing in field yet */
4147 if(*bp
== ','){ /* so blast any extra */
4148 *bp
= '\0'; /* separators */
4154 /* insert new text at the dot position */
4155 buf_len
= ucs4_strlen(buf
);
4156 tentative_p_ind
= ods
.p_ind
+ buf_len
;
4157 work_buf_len
= ucs4_strlen(ods
.cur_l
->text
) + buf_len
;
4158 work_buf
= (UCS
*) malloc((work_buf_len
+ 1) * sizeof(UCS
));
4159 if (work_buf
== NULL
) {
4160 emlwrite("Can't malloc space for saved text", NULL
);
4165 work_buf_begin
= work_buf
;
4166 i
= MIN(ods
.p_ind
, work_buf_len
);
4167 ucs4_strncpy(work_buf
, ods
.cur_l
->text
, i
);
4169 ucs4_strncat(work_buf
, buf
, work_buf_len
+1-ucs4_strlen(work_buf
)-1);
4170 work_buf
[work_buf_len
] = '\0';
4171 ucs4_strncat(work_buf
, &ods
.cur_l
->text
[ods
.p_ind
], work_buf_len
+1-ucs4_strlen(work_buf
)-1);
4172 work_buf
[work_buf_len
] = '\0';
4178 /* insert text in HLSZ character chunks */
4179 while(work_buf_len
+ ods
.p_ind
> HLSZ
) {
4180 ucs4_strncpy(&ods
.cur_l
->text
[ods
.p_ind
], work_buf
, HLSZ
-ods
.p_ind
);
4181 work_buf
+= (HLSZ
- ods
.p_ind
);
4182 work_buf_len
-= (HLSZ
- ods
.p_ind
);
4184 if(FormatLines(ods
.cur_l
, empty
, LINEWID(),
4185 headents
[ods
.cur_e
].break_on_comma
, 0) == -1) {
4193 len
+= ucs4_strlen(travel
->text
);
4198 * This comes after the break above because it will
4199 * be accounted for in the while loop below.
4201 if(!tentative_cur_l
){
4202 if(tentative_p_ind
<= ucs4_strlen(travel
->text
))
4203 tentative_cur_l
= travel
;
4205 tentative_p_ind
-= ucs4_strlen(travel
->text
);
4208 travel
= travel
->next
;
4212 ods
.p_ind
= ucs4_strlen(travel
->text
) - len
+ HLSZ
;
4216 /* insert the remainder of text */
4217 if (i
!= FALSE
&& work_buf_len
> 0) {
4218 ucs4_strncpy(&ods
.cur_l
->text
[ods
.p_ind
], work_buf
, HLSZ
-ods
.p_ind
);
4219 ods
.cur_l
->text
[HLSZ
-1] = '\0';
4220 work_buf
= work_buf_begin
;
4223 if(FormatLines(ods
.cur_l
, empty
, LINEWID(),
4224 headents
[ods
.cur_e
].break_on_comma
, 0) == -1) {
4229 while (len
< work_buf_len
+ ods
.p_ind
){
4230 if(!tentative_cur_l
){
4231 if(tentative_p_ind
<= ucs4_strlen(travel
->text
))
4232 tentative_cur_l
= travel
;
4234 tentative_p_ind
-= ucs4_strlen(travel
->text
);
4237 len
+= ucs4_strlen(travel
->text
);
4238 if (len
>= work_buf_len
+ ods
.p_ind
)
4241 travel
= travel
->next
;
4245 ods
.p_ind
= ucs4_strlen(travel
->text
) - len
+ work_buf_len
+ ods
.p_ind
;
4247 && tentative_p_ind
>= 0
4248 && tentative_p_ind
<= ucs4_strlen(tentative_cur_l
->text
)){
4249 ods
.cur_l
= tentative_cur_l
;
4250 ods
.p_ind
= tentative_p_ind
;
4262 * break_point - Break the given line at the most reasonable character breakch
4263 * within maxwid max characters.
4266 * Pointer to the best break point in s, or
4267 * Pointer to the beginning of s if no break point found
4270 break_point(UCS
*line
, int maxwid
, UCS breakch
, int *quotedarg
)
4272 UCS
*bp
; /* break point */
4276 * Start at maxwid and work back until first opportunity to break.
4278 bp
= ucs4_particular_width(line
, maxwid
);
4281 * Quoted should be set up for the start of line. Since we want
4282 * to move to bp and work our way back we need to scan through the
4283 * line up to bp setting quoted appropriately.
4286 ucs4_strqchr(line
, '\0', quotedarg
, bp
-line
);
4288 quoted
= quotedarg
? *quotedarg
: 0;
4291 if(breakch
== ',' && *bp
== '"') /* don't break on quoted ',' */
4292 quoted
= !quoted
; /* toggle quoted state */
4294 if(*bp
== breakch
&& !quoted
){
4296 if(ucs4_str_width_ptr_to_ptr(line
, bp
+1) < maxwid
){
4297 bp
++; /* leave the ' ' */
4303 * if break char isn't a space, leave a space after
4306 if(!(ucs4_str_width_ptr_to_ptr(line
, bp
+1) >= maxwid
4307 || (bp
[1] == ' ' && ucs4_str_width_ptr_to_ptr(line
, bp
+2) >= maxwid
))){
4308 bp
+= (bp
[1] == ' ') ? 2 : 1;
4318 *quotedarg
= quoted
;
4320 return((quoted
) ? line
: bp
);
4327 * hldelete() - remove the header line pointed to by l from the linked list
4331 * the case of first line in field is kind of bogus. since
4332 * the array of headers has a pointer to the first line, and
4333 * i don't want to worry about this too much, i just copied
4334 * the line below and removed it rather than the first one
4342 hldelete(struct hdr_line
*l
)
4344 register struct hdr_line
*lp
;
4349 if(l
->next
== NULL
&& l
->prev
== NULL
){ /* only one line in field */
4351 return(TRUE
); /* no free only line in list */
4353 else if(l
->next
== NULL
){ /* last line in field */
4354 l
->prev
->next
= NULL
;
4356 else if(l
->prev
== NULL
){ /* first line in field */
4357 ucs4_strncpy(l
->text
, l
->next
->text
, HLSZ
);
4358 l
->text
[HLSZ
-1] = '\0';
4360 if((l
->next
= lp
->next
) != NULL
)
4364 else{ /* some where in field */
4365 l
->prev
->next
= l
->next
;
4366 l
->next
->prev
= l
->prev
;
4378 * is_blank - returns true if the next n chars from coordinates row, col
4379 * on display are spaces
4382 is_blank(int row
, int col
, int n
)
4385 for( ;col
< n
; col
++){
4386 if(pscr(row
, col
) == NULL
|| pscr(row
, col
)->c
!= ' ')
4394 * ShowPrompt - display key help corresponding to the current header entry
4399 if(headents
[ods
.cur_e
].key_label
){
4400 menu_header
[TO_KEY
].name
= "^T";
4401 menu_header
[TO_KEY
].label
= headents
[ods
.cur_e
].key_label
;
4402 KS_OSDATASET(&menu_header
[TO_KEY
], KS_OSDATAGET(&headents
[ods
.cur_e
]));
4405 menu_header
[TO_KEY
].name
= NULL
;
4407 if(Pmaster
&& Pmaster
->exit_label
)
4408 menu_header
[SEND_KEY
].label
= Pmaster
->exit_label
;
4409 else if(gmode
& (MDVIEW
| MDHDRONLY
))
4410 menu_header
[SEND_KEY
].label
= (gmode
& MDHDRONLY
) ? "eXit/Save" : "eXit";
4412 menu_header
[SEND_KEY
].label
= N_("Send");
4415 menu_header
[CUT_KEY
].name
= NULL
;
4416 menu_header
[DEL_KEY
].name
= NULL
;
4417 menu_header
[UDEL_KEY
].name
= NULL
;
4420 menu_header
[CUT_KEY
].name
= "^K";
4421 menu_header
[DEL_KEY
].name
= "^D";
4422 menu_header
[UDEL_KEY
].name
= "^U";
4425 if(Pmaster
->ctrlr_label
){
4426 menu_header
[RICH_KEY
].label
= Pmaster
->ctrlr_label
;
4427 menu_header
[RICH_KEY
].name
= "^R";
4429 else if(gmode
& MDHDRONLY
){
4430 menu_header
[RICH_KEY
].name
= NULL
;
4433 menu_header
[RICH_KEY
].label
= N_("Rich Hdr");
4434 menu_header
[RICH_KEY
].name
= "^R";
4437 if(gmode
& MDHDRONLY
){
4438 if(headents
[ods
.cur_e
].fileedit
){
4439 menu_header
[PONE_KEY
].name
= "^_";
4440 menu_header
[PONE_KEY
].label
= N_("Edit File");
4443 menu_header
[PONE_KEY
].name
= NULL
;
4445 menu_header
[ATT_KEY
].name
= NULL
;
4448 menu_header
[PONE_KEY
].name
= "^O";
4449 menu_header
[PONE_KEY
].label
= N_("Postpone");
4451 menu_header
[ATT_KEY
].name
= "^J";
4454 wkeyhelp(menu_header
);
4459 * packheader - packup all of the header fields for return to caller.
4460 * NOTE: all of the header info passed in, including address
4461 * of the pointer to each string is contained in the
4462 * header entry array "headents".
4467 register int i
= 0; /* array index */
4468 register int count
; /* count of chars in a field */
4469 register int retval
= TRUE
;
4470 register char *bufp
;
4471 register struct hdr_line
*line
;
4477 while(headents
[i
].name
!= NULL
){
4480 * attachments are special case, already in struct we pass back
4482 if(headents
[i
].is_attach
){
4488 if(headents
[i
].blank
){
4494 * count chars to see if we need a new malloc'd space for our
4497 line
= headents
[i
].hd_text
;
4499 while(line
!= NULL
){
4501 * add one for possible concatenation of a ' ' character ...
4503 p
= ucs4_to_utf8_cpystr(line
->text
);
4506 if(p
[0] && p
[strlen(p
)-1] == ',')
4509 fs_give((void **) &p
);
4515 line
= headents
[i
].hd_text
;
4516 if(count
<= headents
[i
].maxlen
){
4517 *headents
[i
].realaddr
[0] = '\0';
4521 * don't forget to include space for the null terminator!!!!
4523 if((bufp
= (char *)malloc((count
+1) * sizeof(char))) != NULL
){
4526 free(*headents
[i
].realaddr
);
4527 *headents
[i
].realaddr
= bufp
;
4528 headents
[i
].maxlen
= count
;
4531 emlwrite("Can't make room to pack header field.", NULL
);
4536 if(retval
!= FALSE
){
4537 int saw_current_line
= 0;
4539 while(line
!= NULL
){
4541 /* pass the cursor offset back in Pmaster struct */
4542 if(headents
[i
].start_here
&& Pmaster
&& !saw_current_line
){
4543 if(ods
.cur_l
== line
)
4546 Pmaster
->edit_offset
+= ucs4_strlen(line
->text
);
4549 p
= ucs4_to_utf8_cpystr(line
->text
);
4551 strncat(*headents
[i
].realaddr
, p
, headents
[i
].maxlen
+1-strlen(*headents
[i
].realaddr
)-1);
4552 (*headents
[i
].realaddr
)[headents
[i
].maxlen
] = '\0';
4554 if(p
[0] && p
[strlen(p
)-1] == ','){
4555 strncat(*headents
[i
].realaddr
, " ", headents
[i
].maxlen
+1-strlen(*headents
[i
].realaddr
)-1);
4556 (*headents
[i
].realaddr
)[headents
[i
].maxlen
] = '\0';
4559 fs_give((void **) &p
);
4575 * zotheader - free all malloc'd lines associated with the header structs
4580 register struct headerentry
*i
;
4582 for(i
= headents
; headents
&& i
->name
; i
++)
4583 zotentry(i
->hd_text
);
4588 * zotentry - free malloc'd space associated with the given linked list
4591 zotentry(struct hdr_line
*l
)
4593 register struct hdr_line
*ld
, *lf
= l
;
4595 while((ld
= lf
) != NULL
){
4597 ld
->next
= ld
->prev
= NULL
;
4605 * zotcomma - blast any trailing commas and white space from the end
4614 p
= &s
[ucs4_strlen(s
)];
4631 * Save the current state of global variables so that we can restore
4632 * them later. This is so we can call pico again.
4633 * Also have to initialize some variables that normally would be set to
4637 save_pico_state(void)
4643 extern VIDEO
**vscreen
;
4644 extern VIDEO
**pscreen
;
4645 extern int pico_all_done
;
4646 extern jmp_buf finstate
;
4647 extern UCS
*pico_anchor
;
4649 if((ret
= (VARS_TO_SAVE
*)malloc(sizeof(VARS_TO_SAVE
))) == NULL
)
4654 ret
->lbound
= lbound
;
4655 ret
->vscreen
= vscreen
;
4656 ret
->pscreen
= pscreen
;
4658 ret
->delim_ps
= delim_ps
;
4659 ret
->invert_ps
= invert_ps
;
4660 ret
->pico_all_done
= pico_all_done
;
4661 memcpy(ret
->finstate
, finstate
, sizeof(jmp_buf));
4662 ret
->pico_anchor
= pico_anchor
;
4663 ret
->Pmaster
= Pmaster
;
4664 ret
->fillcol
= fillcol
;
4665 if((ret
->pat
= (UCS
*)malloc(sizeof(UCS
) * (ucs4_strlen(pat
)+1))) != NULL
)
4666 ucs4_strncpy(ret
->pat
, pat
, ucs4_strlen(pat
)+1);
4668 ret
->ComposerTopLine
= ComposerTopLine
;
4669 ret
->ComposerEditing
= ComposerEditing
;
4671 ret
->alt_speller
= alt_speller
;
4672 ret
->quote_str
= glo_quote_str
;
4673 ret
->wordseps
= glo_wordseps
;
4674 ret
->currow
= currow
;
4675 ret
->curcol
= curcol
;
4676 ret
->thisflag
= thisflag
;
4677 ret
->lastflag
= lastflag
;
4678 ret
->curgoal
= curgoal
;
4679 ret
->opertree
= (char *) malloc(sizeof(char) * (strlen(opertree
) + 1));
4680 if(ret
->opertree
!= NULL
)
4681 strncpy(ret
->opertree
, opertree
, strlen(opertree
)+1);
4684 ret
->wheadp
= wheadp
;
4686 ret
->bheadp
= bheadp
;
4687 ret
->km_popped
= km_popped
;
4688 ret
->mrow
= term
.t_mrow
;
4690 /* Initialize for next pico call */
4701 restore_pico_state(VARS_TO_SAVE
*state
)
4706 extern VIDEO
**vscreen
;
4707 extern VIDEO
**pscreen
;
4708 extern int pico_all_done
;
4709 extern jmp_buf finstate
;
4710 extern UCS
*pico_anchor
;
4713 vtrow
= state
->vtrow
;
4714 vtcol
= state
->vtcol
;
4715 lbound
= state
->lbound
;
4716 vscreen
= state
->vscreen
;
4717 pscreen
= state
->pscreen
;
4719 delim_ps
= state
->delim_ps
;
4720 invert_ps
= state
->invert_ps
;
4721 pico_all_done
= state
->pico_all_done
;
4722 memcpy(finstate
, state
->finstate
, sizeof(jmp_buf));
4723 pico_anchor
= state
->pico_anchor
;
4724 Pmaster
= state
->Pmaster
;
4726 headents
= Pmaster
->headents
;
4728 fillcol
= state
->fillcol
;
4730 ucs4_strncpy(pat
, state
->pat
, NPAT
);
4732 ComposerTopLine
= state
->ComposerTopLine
;
4733 ComposerEditing
= state
->ComposerEditing
;
4734 gmode
= state
->gmode
;
4735 alt_speller
= state
->alt_speller
;
4736 glo_quote_str
= state
->quote_str
;
4737 glo_wordseps
= state
->wordseps
;
4738 currow
= state
->currow
;
4739 curcol
= state
->curcol
;
4740 thisflag
= state
->thisflag
;
4741 lastflag
= state
->lastflag
;
4742 curgoal
= state
->curgoal
;
4743 if(state
->opertree
){
4744 strncpy(opertree
, state
->opertree
, sizeof(opertree
));
4745 opertree
[sizeof(opertree
)-1] = '\0';
4748 curwp
= state
->curwp
;
4749 wheadp
= state
->wheadp
;
4750 curbp
= state
->curbp
;
4751 bheadp
= state
->bheadp
;
4752 km_popped
= state
->km_popped
;
4753 term
.t_mrow
= state
->mrow
;
4758 free_pico_state(VARS_TO_SAVE
*state
)
4764 free(state
->opertree
);
4771 * Ok to call this twice in a row because it won't do anything the second
4775 fix_mangle_and_err(int *mangled
, char **errmsg
, char *name
)
4777 if(mangled
&& *mangled
){
4784 if(errmsg
&& *errmsg
){
4788 snprintf(err
, sizeof(err
), "%s field: %s", name
, *errmsg
);
4790 emlwrite(err
, NULL
);
4803 * This is so that the To line will be appended to by an Lcc
4804 * entry unless the user types in the To line after the Lcc
4805 * has already been set.
4808 mark_sticky(struct headerentry
*h
)
4810 if(h
&& (!h
->sticky_special
|| h
->bldr_private
))
4819 * Wrapper function for the real header editor.
4820 * Does the important tasks of:
4821 * 1) verifying that we _can_ edit the headers.
4822 * 2) acting on the result code from the header editor.
4825 HeaderEditor(int f
, int n
)
4831 /* Sometimes we get here from a scroll callback, which
4832 * is no good at all because mswin is not ready to process input and
4833 * this _headeredit() will never do anything.
4834 * Putting this test here was the most general solution I could think
4836 if (!mswin_caninput())
4840 retval
= HeaderEditorWork(f
, n
);
4842 retval
= mousepress(0,0);