1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: composer.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2006-2009 University of Washington
8 * Copyright 2013-2015 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
18 * Program: Pine composer routines
22 * - composer.c is the composer for the PINE mail system
26 * Notes: These routines aren't incorporated yet, because the composer as
27 * a whole still needs development. These are ideas that should
28 * be implemented in later releases of PINE. See the notes in
29 * pico.c concerning what needs to be done ....
33 * Notes: Installed header line editing, with wrapping and unwrapping,
34 * context sensitive help, and other mail header editing features.
36 * - finalish code cleanup 07/15/91
38 * Notes: Killed/yanked header lines use emacs kill buffer.
39 * Arbitrarily large headers are handled gracefully.
40 * All formatting handled by FormatLines.
42 * - Work done to optimize display painting 06/26/92
43 * Not as clean as it should be, needs more thought
48 #include "osdep/terminal.h"
49 #include "../pith/string.h"
51 int InitEntryText(char *, struct headerentry
*);
52 int HeaderOffset(int);
53 int HeaderFocus(int, int);
54 UCS
LineEdit(int, UCS
*);
55 int header_downline(int, int);
56 int header_upline(int);
57 int FormatLines(struct hdr_line
*, char *, int, int, int);
58 int FormatSyncAttach(void);
59 UCS
*ucs4_strqchr(UCS
*, UCS
, int *, int);
60 int ComposerHelp(int);
62 void display_delimiter(int);
63 void zotentry(struct hdr_line
*);
64 int InvertPrompt(int, int);
65 int partial_entries(void);
66 int physical_line(struct hdr_line
*);
67 int strend(UCS
*, UCS
);
68 int KillHeaderLine(struct hdr_line
*, int);
69 int SaveHeaderLines(void);
70 UCS
*break_point(UCS
*, int, UCS
, int *);
71 int hldelete(struct hdr_line
*);
72 int is_blank(int, int, int);
74 struct hdr_line
*first_hline(int *);
75 struct hdr_line
*first_sel_hline(int *);
76 struct hdr_line
*next_hline(int *, struct hdr_line
*);
77 struct hdr_line
*next_sel_hline(int *, struct hdr_line
*);
78 struct hdr_line
*prev_hline(int *, struct hdr_line
*);
79 struct hdr_line
*prev_sel_hline(int *, struct hdr_line
*);
80 struct hdr_line
*first_requested_hline(int *);
81 void fix_mangle_and_err(int *, char **, char *);
82 void mark_sticky(struct headerentry
*);
86 * definition header field array, structures defined in pico.h
88 struct headerentry
*headents
;
92 * structure that keeps track of the range of header lines that are
93 * to be displayed and other fun stuff about the header
95 struct on_display ods
; /* global on_display struct */
97 /* a pointer to the subject line. This is so that we do not have to compute
98 * the header line in every call. It saves a few CPU cycles
101 struct hdr_line
*subject_line
= NULL
;
106 #define HALLOC() (struct hdr_line *)malloc(sizeof(struct hdr_line))
107 #define LINEWID() (((term.t_ncol - headents[ods.cur_e].prwid) > 0) ? ((unsigned) (term.t_ncol - headents[ods.cur_e].prwid)) : 0)
108 #define BOTTOM() (term.t_nrow - term.t_mrow)
109 #define FULL_SCR() (BOTTOM() - 3)
110 #define HALF_SCR() (FULL_SCR()/2)
114 * Redefine HeaderEditor to install wrapper required for mouse event
117 #define HeaderEditor HeaderEditorWork
120 #define HDR_DELIM "----- Message Text -----"
123 * useful declarations
125 static short delim_ps
= 0; /* previous state */
126 static short invert_ps
= 0; /* previous state */
129 static KEYMENU menu_header
[] = {
130 /* TRANSLATORS: command key labels, Send means send the message
131 we are currently working on. */
132 {"^G", N_("Get Help"), KS_SCREENHELP
}, {"^X", N_("Send"), KS_SEND
},
133 /* TRANSLATORS: Rich Headers is a command to display more headers. It
134 is triggered with the ^R key. PrvPg stands for Previous Page. */
135 {"^R", N_("Rich Hdr"), KS_RICHHDR
}, {"^Y", N_("PrvPg/Top"), KS_PREVPAGE
},
136 /* TRANSLATORS: Cut Line means remove a line. Postpone means to save
137 a message being composed so that it can be worked on later. */
138 {"^K", N_("Cut Line"), KS_CURPOSITION
}, {"^O", N_("Postpone"), KS_POSTPONE
},
139 /* TRANSLATORS: Del Char is Delete Character */
140 {"^C", N_("Cancel"), KS_CANCEL
}, {"^D", N_("Del Char"), KS_NONE
},
141 /* TRANSLATORS: Next Page */
142 {"^J", N_("Attach"), KS_ATTACH
}, {"^V", N_("NxtPg/End"), KS_NEXTPAGE
},
143 /* TRANSLATORS: Undelete a line that was just deleted */
144 {"^U", N_("UnDel Line"), KS_NONE
}, {NULL
, NULL
}
157 * function key mappings for header editor
159 static UCS ckm
[12][2] = {
176 * InitMailHeader - initialize header array, and set beginning editor row
177 * range. The header entry structure should look just like
178 * what is written on the screen, the vector
179 * (entry, line, offset) will describe the current cursor
180 * position in the header.
182 * Returns: TRUE if special header handling was requested,
183 * FALSE under standard default behavior.
186 InitMailHeader(PICO
*mp
)
189 struct headerentry
*he
;
198 * initialize some of on_display structure, others below...
201 ods
.p_line
= COMPOSER_TOP_LINE
;
202 ods
.top_l
= ods
.cur_l
= NULL
;
204 headents
= mp
->headents
;
205 /*--- initialize the fields in the headerent structure ----*/
206 for(he
= headents
; he
->name
!= NULL
; he
++){
208 he
->display_it
= he
->display_it
? he
->display_it
: !he
->rich_header
;
210 /*--- A lot of work to do since attachments are special ---*/
212 if(mp
->attachments
!= NULL
){
215 int l1
, ofp
, ofp1
, ofp2
; /* OverFlowProtection */
216 size_t addrbuflen
= 4 * NLINE
; /* malloc()ed size of addrbuf */
217 PATMT
*ap
= mp
->attachments
;
221 addrbuf
= (char *)malloc(addrbuflen
);
225 if((l1
= strlen(ap
->filename
)) <= ofp
){
235 snprintf(buf
, sizeof(buf
), "%d. %.*s%s %s%s%s\"",
239 (l1
> ofp
) ? "...]" : "",
241 ap
->size
? ap
->size
: "",
242 ap
->size
? ") " : "");
244 /* append description, escaping quotes */
246 char *dp
= ap
->description
, *bufp
= &buf
[strlen(buf
)];
250 if(*dp
== '\"' && !escaped
){
258 while(--ofp2
> 0 && (*bufp
++ = *dp
++));
263 snprintf(buf
+ strlen(buf
), sizeof(buf
)-strlen(buf
), "\"%s", ap
->next
? "," : "");
265 if(strlen(addrbuf
) + strlen(buf
) >= addrbuflen
){
266 addrbuflen
+= NLINE
* 4;
267 if(!(addrbuf
= (char *)realloc(addrbuf
, addrbuflen
))){
268 emlwrite("\007Can't realloc addrbuf to %d bytes",
269 (void *) addrbuflen
);
274 strncat(addrbuf
, buf
, addrbuflen
-strlen(addrbuf
)-1);
275 addrbuf
[addrbuflen
-1] = '\0';
279 InitEntryText(addrbuf
, he
);
280 free((char *)addrbuf
);
282 InitEntryText("", he
);
287 addrbuf
= *(he
->realaddr
);
291 InitEntryText(addrbuf
, he
);
296 * finish initialization and then figure out display layout.
297 * first, look for any field the caller requested we start in,
298 * and set the offset into that field.
300 if((ods
.cur_l
= first_requested_hline(&ods
.cur_e
)) != NULL
){
301 ods
.top_e
= 0; /* init top_e */
302 ods
.top_l
= first_hline(&ods
.top_e
);
303 if(!HeaderFocus(ods
.cur_e
, Pmaster
? Pmaster
->edit_offset
: 0))
304 HeaderFocus(ods
.cur_e
, 0);
309 ods
.top_l
= first_hline(&ods
.cur_e
);
310 ods
.cur_l
= first_sel_hline(&ods
.cur_e
);
311 ods
.top_e
= ods
.cur_e
;
322 * InitEntryText - Add the given header text into the header entry
326 InitEntryText(char *utf8text
, struct headerentry
*e
)
328 struct hdr_line
*curline
;
329 register int longest
;
332 * get first chunk of memory, and tie it to structure...
334 if((curline
= HALLOC()) == NULL
){
335 emlwrite("Unable to make room for full Header.", NULL
);
339 longest
= term
.t_ncol
- e
->prwid
- 1;
340 curline
->text
[0] = '\0';
341 curline
->next
= NULL
;
342 curline
->prev
= NULL
;
343 e
->hd_text
= curline
; /* tie it into the list */
345 if(FormatLines(curline
, utf8text
, longest
, e
->break_on_comma
, 0) == -1)
354 * ResizeHeader - Handle resizing display when SIGWINCH received.
357 * works OK, but needs thorough testing
363 register struct headerentry
*i
;
369 offset
= (ComposerEditing
) ? HeaderOffset(ods
.cur_e
) : 0;
371 for(i
=headents
; i
->name
; i
++){ /* format each entry */
372 if(FormatLines(i
->hd_text
, "", (term
.t_ncol
- i
->prwid
),
373 i
->break_on_comma
, 0) == -1){
378 if(ComposerEditing
) /* restart at the top */
379 HeaderFocus(ods
.cur_e
, offset
); /* fix cur_l and p_ind */
384 for(i
= headents
; i
->name
!= NULL
; i
++); /* Find last line */
387 l
= headents
[e
].hd_text
;
388 if(!headents
[e
].display_it
|| headents
[e
].blank
)
389 l
= prev_sel_hline(&e
, l
); /* last selectable line */
393 l
= headents
[e
].hd_text
;
396 HeaderFocus(e
, -1); /* put focus on last line */
399 if(ComposerTopLine
!= COMPOSER_TOP_LINE
)
405 movecursor(ods
.p_line
, ods
.p_ind
+headents
[ods
.cur_e
].prwid
);
414 * HeaderOffset - return the character offset into the given header
419 register struct hdr_line
*l
;
422 l
= headents
[h
].hd_text
;
424 while(l
!= ods
.cur_l
){
425 i
+= ucs4_strlen(l
->text
);
434 * HeaderFocus - put the dot at the given offset into the given header
437 HeaderFocus(int h
, int offset
)
439 register struct hdr_line
*l
;
443 if(offset
== -1) /* focus on last line */
446 l
= headents
[h
].hd_text
;
448 if(last
&& l
->next
== NULL
){
452 if((i
=ucs4_strlen(l
->text
)) >= offset
)
457 if((l
= l
->next
) == NULL
)
462 ods
.p_len
= ucs4_strlen(l
->text
);
463 ods
.p_ind
= (last
) ? 0 : offset
;
471 * HeaderEditor() - edit the mail header field by field, trapping
472 * important key sequences, hand the hard work off
475 * -3 if we drop out bottom *and* want to process mouse event
476 * -1 if we drop out the bottom
477 * FALSE if editing is cancelled
478 * TRUE if editing is finished
481 HeaderEditor(int f
, int n
)
486 struct headerentry
*h
;
487 int cur_e
, mangled
, retval
= -1,
488 hdr_only
= (gmode
& MDHDRONLY
) ? 1 : 0;
495 return(TRUE
); /* nothing to edit! */
497 ComposerEditing
= TRUE
;
498 display_delimiter(0); /* provide feedback */
501 mswin_setscrollrange (0, 0);
502 #endif /* _WINDOWS */
505 for (subject_line
= NULL
, i
=0; headents
[i
].name
; i
++)
506 if(strcmp(headents
[i
].name
, "Subject") == 0)
507 subject_line
= headents
[i
].hd_text
;
510 * Decide where to begin editing. if f == TRUE begin editing
511 * at the bottom. this case results from the cursor backing
512 * into the editor from the bottom. otherwise, the user explicitly
513 * requested editing the header, and we begin at the top.
515 * further, if f == 1, we moved into the header by hitting the up arrow
516 * in the message text, else if f == 2, we moved into the header by
517 * moving past the left edge of the top line in the message. so, make
518 * the end of the last line of the last entry the current cursor position
519 * lastly, if f == 3, we moved into the header by hitting backpage() on
520 * the top line of the message, so scroll a page back.
523 if(f
== 2){ /* 2 leaves cursor at end */
524 struct hdr_line
*l
= ods
.cur_l
;
527 /*--- make sure on last field ---*/
528 while((l
= next_sel_hline(&e
, l
)) != NULL
)
529 if(headents
[ods
.cur_e
].display_it
){
534 ods
.p_ind
= 1000; /* and make sure at EOL */
538 * note: assumes that ods.cur_e and ods.cur_l haven't changed
543 if(curwp
->w_doto
< headents
[ods
.cur_e
].prwid
)
545 else if(curwp
->w_doto
< ods
.p_ind
+ headents
[ods
.cur_e
].prwid
)
546 ods
.p_ind
= curwp
->w_doto
- headents
[ods
.cur_e
].prwid
;
550 /* and scroll back if needed */
552 for(i
= 0; header_upline(0) && i
<= FULL_SCR(); i
++)
556 ods
.p_line
= ComposerTopLine
- 2;
558 /* else just trust what ods contains */
560 InvertPrompt(ods
.cur_e
, TRUE
); /* highlight header field */
572 if(km_popped
){ /* temporarily change to cause menu to paint */
574 curwp
->w_ntrows
-= 2;
575 movecursor(term
.t_nrow
-2, 0); /* clear status line, too */
578 else if(term
.t_mrow
== 0)
581 ShowPrompt(); /* display correct options */
585 curwp
->w_ntrows
+= 2;
589 ch
= LineEdit(!(gmode
&MDVIEW
), &lastch
); /* work on the current line */
599 movecursor(term
.t_nrow
-2, 0);
601 movecursor(term
.t_nrow
-1, 0);
603 movecursor(term
.t_nrow
, 0);
609 case (CTRL
|'R') : /* Toggle header display */
610 if(Pmaster
->pine_flags
& MDHDRONLY
){
611 if(Pmaster
->expander
){
621 /*---- Are there any headers to expand above us? ---*/
622 for(h
= headents
; h
!= &headents
[ods
.cur_e
]; h
++)
626 InvertPrompt(ods
.cur_e
, FALSE
); /* Yes, don't leave inverted */
630 if(partial_entries()){
631 /*--- Just turned off all rich headers --*/
632 if(headents
[ods
.cur_e
].rich_header
){
633 /*-- current header got turned off too --*/
635 if(headents
[ods
.cur_e
].is_attach
){
636 /* verify the attachments */
637 if((i
= FormatSyncAttach()) != 0){
638 FormatLines(headents
[ods
.cur_e
].hd_text
, "",
639 term
.t_ncol
- headents
[ods
.cur_e
].prwid
,
640 headents
[ods
.cur_e
].break_on_comma
, 0);
644 if(headents
[ods
.cur_e
].builder
) /* verify text */
645 i
= call_builder(&headents
[ods
.cur_e
], &mangled
, &err
)>0;
647 for(cur_e
=ods
.cur_e
; headents
[cur_e
].name
!=NULL
; cur_e
++)
648 if(!headents
[cur_e
].rich_header
)
650 if(headents
[cur_e
].name
== NULL
) {
651 /* didn't find one, check above */
652 for(cur_e
=ods
.cur_e
; headents
[cur_e
].name
!=NULL
;
654 if(!headents
[cur_e
].rich_header
)
660 ods
.cur_l
= headents
[ods
.cur_e
].hd_text
;
664 ods
.p_line
= 0; /* force update */
666 PaintHeader(COMPOSER_TOP_LINE
, FALSE
);
668 fix_mangle_and_err(&mangled
, &err
, headents
[ods
.cur_e
].name
);
671 case (CTRL
|'C') : /* bag whole thing ?*/
672 if(abort_composer(1, 0) == TRUE
)
677 case (CTRL
|'X') : /* Done. Send it. */
680 if(headents
[ods
.cur_e
].is_attach
){
681 /* verify the attachments, and pretty things up in case
682 * we come back to the composer due to error...
684 if((i
= FormatSyncAttach()) != 0){
685 sleep(2); /* give time for error to absorb */
686 FormatLines(headents
[ods
.cur_e
].hd_text
, "",
687 term
.t_ncol
- headents
[ods
.cur_e
].prwid
,
688 headents
[ods
.cur_e
].break_on_comma
, 0);
695 if(headents
[ods
.cur_e
].builder
) /* verify text? */
696 i
= call_builder(&headents
[ods
.cur_e
], &mangled
, &err
);
698 if(i
< 0){ /* don't leave without a valid addr */
699 fix_mangle_and_err(&mangled
, &err
, headents
[ods
.cur_e
].name
);
703 ods
.cur_l
= headents
[ods
.cur_e
].hd_text
; /* attach cur_l */
705 ods
.p_line
= 0; /* force realignment */
706 fix_mangle_and_err(&mangled
, &err
, headents
[ods
.cur_e
].name
);
710 fix_mangle_and_err(&mangled
, &err
, headents
[ods
.cur_e
].name
);
712 if(wquit(1,0) == TRUE
)
717 * need to be careful here because pointers might be messed up.
718 * also, could do a better job of finding the right place to
719 * put the dot back (i.e., the addr/list that was expanded).
722 PaintHeader(COMPOSER_TOP_LINE
, FALSE
);
727 case (CTRL
|'Z') : /* Suspend compose */
728 if(gmode
&MDSSPD
){ /* is it allowed? */
738 #if defined MOUSE && !defined(_WINDOWS)
739 toggle_xterm_mouse(0,1);
745 case (CTRL
|'O') : /* Suspend message */
746 if(Pmaster
->pine_flags
& MDHDRONLY
)
752 if(headents
[ods
.cur_e
].is_attach
){
753 if(FormatSyncAttach() < 0){
754 if(mlyesno_utf8(_("Problem with attachments. Postpone anyway?"),
756 if(FormatLines(headents
[ods
.cur_e
].hd_text
, "",
757 term
.t_ncol
- headents
[ods
.cur_e
].prwid
,
758 headents
[ods
.cur_e
].break_on_comma
, 0) == -1)
759 emlwrite("\007Format lines failed!", NULL
);
761 PaintHeader(COMPOSER_TOP_LINE
, FALSE
);
767 else if(headents
[ods
.cur_e
].builder
)
768 i
= call_builder(&headents
[ods
.cur_e
], &mangled
, &err
);
770 fix_mangle_and_err(&mangled
, &err
, headents
[ods
.cur_e
].name
);
772 if(i
< 0) /* don't leave without a valid addr */
775 suspend_composer(1, 0);
779 case (CTRL
|'J') : /* handle attachments */
780 if(Pmaster
->pine_flags
& MDHDRONLY
)
784 LMLIST
*lm
= NULL
, *lmp
;
785 char buf
[NLINE
], *bfp
;
790 * Attachment questions mess with km_popped and assume
791 * it is zero to start with. If we don't set it to zero
792 * on entry, the message about mime type will be erased
793 * by PaintBody. If we don't reset it when we come back,
794 * the bottom three lines may be messed up.
796 saved_km_popped
= km_popped
;
799 if(AskAttach(cmt
, sizeof(cmt
), &lm
)){
801 for(lmp
= lm
; lmp
; lmp
= lmp
->next
){
804 len
= lmp
->dir
? strlen(lmp
->dir
)+1 : 0;
805 len
+= lmp
->fname
? strlen(lmp
->fname
) : 0;
807 if(len
+3 > sizeof(buf
)){
809 bfp
= malloc(space
*sizeof(char));
811 emlwrite("\007Can't malloc space for filename",
821 if(lmp
->dir
&& lmp
->dir
[0])
822 snprintf(bfp
, space
, "%s%c%s", lmp
->dir
, C_FILESEP
,
823 lmp
->fname
? lmp
->fname
: "");
825 snprintf(bfp
, space
, "%s", lmp
->fname
? lmp
->fname
: "");
827 (void) QuoteAttach(bfp
, space
);
829 (void) AppendAttachment(bfp
, lm
->size
, cmt
);
838 km_popped
= saved_km_popped
;
839 sgarbk
= 1; /* clean up prompt */
844 case (CTRL
|'I') : /* tab */
845 if(headents
[ods
.cur_e
].nickcmpl
!= NULL
){
846 char *new_nickname
= NULL
;
848 UCS
*start
= NULL
, *end
= NULL
, *before_start
= NULL
;
849 UCS
*uprefix
= NULL
, *up1
, *up2
;
850 char *prefix
= NULL
, *saveprefix
= NULL
, *insert
= NULL
;
852 int offset
, prefixlen
, add_a_comma
= 0;
854 int ambiguity
, fallthru
= 1;
856 strng
= ods
.cur_l
->text
;
857 offset
= HeaderOffset(ods
.cur_e
);
860 && (start
= &strng
[ods
.p_ind
-1])
862 || ucs4_isspace(*(start
+1))
863 || *(start
+1) == ',')
865 && !ucs4_isspace(*start
)
872 && *(start
-1) != ',')
875 while(ucs4_isspace(*start
))
878 if(*end
!= ',' && ods
.cur_l
->next
)
882 * Nickname prefix begins with start and ends
883 * with end-1. Replace those characters with
884 * completed nickname.
886 prefixlen
= end
-start
;
887 uprefix
= (UCS
*) fs_get((prefixlen
+1) * sizeof(UCS
));
888 ucs4_strncpy(uprefix
, start
, prefixlen
);
889 uprefix
[prefixlen
] = '\0';
890 prefix
= ucs4_to_utf8_cpystr(uprefix
);
891 fs_give((void **) &uprefix
);
894 * Ambiguity == 0 means no matches exist.
895 * 1 means it is ambiguous and the longest
896 * unambiguous prefix beginning with prefix
897 * is in new_nickname.
898 * 2 means it is unambiguous and the answer
901 * The expansion from == 2 doesn't have to have prefix
902 * as a prefix. For example, if the prefix is a prefix
903 * of the full name or a prefix of a nickname the answer
904 * might be the full address instead of the expanded
905 * prefix. In fact, that's probably the expected thing.
907 * Due to the way the build_abook_tries sets up the tries
908 * it is unfortunately the case that the expanded address
909 * is not entered in any trie, so after you get an
910 * unambiguous expansion if you try TAB again at that
911 * point you'll probably get a no match returned instead
912 * of an unambiguous. So be aware of that and handle
913 * that case ok by falling through to header_downline.
915 ambiguity
= (*(headents
[ods
.cur_e
].nickcmpl
))(prefix
,
916 &new_nickname
, (lastch
== ch
), 0);
919 * Don't fall through on no-match TAB unless
920 * it is the second TAB.
922 if(ambiguity
== 0 && lastch
!= ch
){
931 goto nomore_to_complete
;
934 if(strcmp(new_nickname
, prefix
)){
936 * We're trying to work with the way
937 * FormatLines works. It inserts text at the
938 * beginning of the line we pass in.
939 * So, remove the beginning of the line and
940 * have FormatLines put it back.
943 /* save part before start */
945 before_start
= strng
;
946 uprefix
= (UCS
*) fs_get((start
-before_start
+1) * sizeof(UCS
));
947 ucs4_strncpy(uprefix
, before_start
, start
-before_start
);
948 uprefix
[start
-before_start
] = '\0';
949 saveprefix
= ucs4_to_utf8_cpystr(uprefix
);
951 /* figure out new cursor offset */
952 up1
= utf8_to_ucs4_cpystr(new_nickname
);
954 offset
+= (ucs4_strlen(up1
) - prefixlen
);
955 fs_give((void **) &up1
);
958 fs_give((void **) &uprefix
);
961 * Delete everything up to end by
962 * copying characters to start of buffer.
966 for(i
= ods
.p_len
- (end
- before_start
) + 1; i
> 0; i
--)
969 ods
.p_len
-= (end
- before_start
);
972 l1
= strlen(saveprefix
);
973 l2
= strlen(new_nickname
);
977 if(add_a_comma
&& ambiguity
== 2){
984 insert
= (char *) fs_get((l
+1) * sizeof(char));
987 * Insert is the before start stuff plus the
988 * new nickname, and we're going to let
989 * FormatLines put it together for us.
992 strncpy(insert
, saveprefix
, l
);
993 strncpy(insert
+l1
, new_nickname
, l
-l1
);
1000 fs_give((void **) &saveprefix
);
1004 if(insert
&& FormatLines(ods
.cur_l
, insert
,
1005 term
.t_ncol
- headents
[ods
.cur_e
].prwid
,
1006 headents
[ods
.cur_e
].break_on_comma
,0)==-1){
1007 emlwrite("\007Format lines failed!", NULL
);
1011 fs_give((void **) &insert
);
1013 HeaderFocus(ods
.cur_e
, offset
);
1019 ambig
= new_nickname
;
1020 new_nickname
= NULL
;
1023 if(!ambig
&& prefix
){
1029 if(ambiguity
== 2 && fallthru
){
1031 fs_give((void **) &prefix
);
1034 fs_give((void **) &new_nickname
);
1037 fs_give((void **) &ambig
);
1041 goto nomore_to_complete
;
1048 fs_give((void **) &prefix
);
1051 fs_give((void **) &new_nickname
);
1054 fs_give((void **) &ambig
);
1057 goto nomore_to_complete
;
1064 ods
.p_ind
= 0; /* fall through... */
1069 header_downline(!hdr_only
, hdr_only
);
1077 case (CTRL
|'V') : /* down a page */
1080 if(!next_sel_hline(&cur_e
, ods
.cur_l
)){
1081 header_downline(!hdr_only
, hdr_only
);
1082 if(!(gmode
& MDHDRONLY
))
1083 retval
= -1; /* tell caller we fell out */
1086 int move_down
, bot_pline
;
1087 struct hdr_line
*new_cur_l
, *line
, *next_line
, *prev_line
;
1089 move_down
= BOTTOM() - 2 - ods
.p_line
;
1094 * Count down move_down lines to find the pointer to the line
1095 * that we want to become the current line.
1097 new_cur_l
= ods
.cur_l
;
1099 for(i
= 0; i
< move_down
; i
++){
1100 next_line
= next_hline(&cur_e
, new_cur_l
);
1104 new_cur_l
= next_line
;
1107 if(headents
[cur_e
].blank
){
1108 next_line
= next_sel_hline(&cur_e
, new_cur_l
);
1112 new_cur_l
= next_line
;
1116 * Now call header_downline until we get down to the
1117 * new current line, so that the builders all get called.
1118 * New_cur_l will remain valid since we won't be calling
1119 * a builder for it during this loop.
1121 while(ods
.cur_l
!= new_cur_l
&& header_downline(0, 0))
1125 * Count back up, if we're at the bottom, to find the new
1129 if(next_hline(&cur_e
, ods
.cur_l
) == NULL
){
1131 * Cursor stops at bottom of headers, which is where
1132 * we are right now. Put as much of headers on
1133 * screen as will fit. Count up to figure
1134 * out which line is top_l and which p_line cursor is on.
1138 /* leave delimiter on screen, too */
1139 bot_pline
= BOTTOM() - 1 - ((gmode
& MDHDRONLY
) ? 0 : 1);
1140 for(i
= COMPOSER_TOP_LINE
; i
< bot_pline
; i
++){
1141 prev_line
= prev_hline(&cur_e
, line
);
1154 ods
.top_l
= ods
.cur_l
;
1155 ods
.top_e
= ods
.cur_e
;
1157 * We don't want to scroll down further than the
1158 * delimiter, so check to see if that is the case.
1159 * If it is, we move the p_line down the screen
1160 * until the bottom line is where we want it.
1162 bot_pline
= BOTTOM() - 1 - ((gmode
& MDHDRONLY
) ? 0 : 1);
1165 for(i
= bot_pline
; i
> COMPOSER_TOP_LINE
; i
--){
1166 next_line
= next_hline(&cur_e
, line
);
1174 * i is the desired value of p_line.
1175 * If it is greater than COMPOSER_TOP_LINE, then
1176 * we need to adjust top_l.
1181 for(; i
> COMPOSER_TOP_LINE
; i
--){
1182 prev_line
= prev_hline(&cur_e
, line
);
1193 * Special case. If p_line is within one of the bottom,
1194 * move it to the bottom.
1196 if(ods
.p_line
== bot_pline
- 1){
1197 header_downline(0, 0);
1198 /* but put these back where we want them */
1199 ods
.p_line
= bot_pline
;
1206 PaintHeader(COMPOSER_TOP_LINE
, FALSE
);
1212 case (CTRL
|'Y') : /* up a page */
1214 for(i
= 0; header_upline(0) && i
<= FULL_SCR(); i
++)
1222 mouse_get_last (NULL
, &mp
);
1224 case M_BUTTON_LEFT
:
1225 if (!mp
.doubleclick
) {
1226 if (mp
.row
< ods
.p_line
) {
1227 for (i
= ods
.p_line
- mp
.row
;
1228 i
> 0 && header_upline(0);
1233 for (i
= mp
.row
-ods
.p_line
;
1234 i
> 0 && header_downline(!hdr_only
, 0);
1239 if((ods
.p_ind
= mp
.col
- headents
[ods
.cur_e
].prwid
) <= 0)
1242 /* -3 is returned if we drop out bottom
1243 * *and* want to process a mousepress. The Headereditor
1244 * wrapper should make sense of this return code.
1246 if (ods
.p_line
>= ComposerTopLine
)
1252 case M_BUTTON_RIGHT
:
1256 case M_BUTTON_MIDDLE
:
1257 default : /* NOOP */
1264 case (CTRL
|'T') : /* Call field selector */
1266 if(headents
[ods
.cur_e
].is_attach
) {
1267 /*--- selector for attachments ----*/
1268 char dir
[NLINE
], fn
[NLINE
], sz
[NLINE
];
1269 LMLIST
*lm
= NULL
, *lmp
;
1271 strncpy(dir
, (gmode
& MDCURDIR
)
1272 ? (browse_dir
[0] ? browse_dir
: ".")
1273 : ((gmode
& MDTREE
) || opertree
[0])
1275 : (browse_dir
[0] ? browse_dir
1276 : gethomedir(NULL
)), sizeof(dir
));
1277 dir
[sizeof(dir
)-1] = '\0';
1278 fn
[0] = sz
[0] = '\0';
1279 if(FileBrowse(dir
, sizeof(dir
), fn
, sizeof(fn
), sz
, sizeof(sz
),
1280 FB_READ
| FB_ATTACH
| FB_LMODEPOS
, &lm
) == 1){
1281 char buf
[NLINE
], *bfp
;
1284 for(lmp
= lm
; lmp
; lmp
= lmp
->next
){
1287 len
= lmp
->dir
? strlen(lmp
->dir
)+1 : 0;
1288 len
+= lmp
->fname
? strlen(lmp
->fname
) : 0;
1290 len
+= strlen(lmp
->size
);
1292 if(len
+3 > sizeof(buf
)){
1294 bfp
= malloc(space
*sizeof(char));
1296 emlwrite("\007Can't malloc space for filename",
1303 space
= sizeof(buf
);
1306 if(lmp
->dir
&& lmp
->dir
[0])
1307 snprintf(bfp
, space
, "%s%c%s", lmp
->dir
, C_FILESEP
,
1308 lmp
->fname
? lmp
->fname
: "");
1310 snprintf(bfp
, space
, "%s", lmp
->fname
? lmp
->fname
: "");
1312 (void) QuoteAttach(bfp
, space
);
1314 snprintf(bfp
+ strlen(bfp
), space
-strlen(bfp
), " (%s) \"\"%s", lmp
->size
,
1315 (!headents
[ods
.cur_e
].hd_text
->text
[0]) ? "":",");
1317 if(FormatLines(headents
[ods
.cur_e
].hd_text
, bfp
,
1318 term
.t_ncol
- headents
[ods
.cur_e
].prwid
,
1319 headents
[ods
.cur_e
].break_on_comma
,0)==-1){
1320 emlwrite("\007Format lines failed!", NULL
);
1330 } /* else, nothing of interest */
1331 } else if (headents
[ods
.cur_e
].selector
!= NULL
) {
1332 VARS_TO_SAVE
*saved_state
;
1334 /*---- General selector for non-attachments -----*/
1337 * Since the selector may make a new call back to pico()
1338 * we need to save and restore the pico state here.
1340 if((saved_state
= save_pico_state()) != NULL
){
1341 bufp
= (*(headents
[ods
.cur_e
].selector
))(&errmss
);
1342 restore_pico_state(saved_state
);
1343 free_pico_state(saved_state
);
1344 ttresize(); /* fixup screen bufs */
1345 picosigs(); /* restore altered signals */
1348 char *s
= "Not enough memory";
1352 errmss
= (char *)malloc((len
+1) * sizeof(char));
1353 strncpy(errmss
, s
, len
+1);
1361 if(headents
[ods
.cur_e
].break_on_comma
) {
1362 /*--- Must be an address ---*/
1365 * If current line is empty and there are more
1366 * lines that follow, delete the empty lines
1367 * before adding the new address.
1369 if(ods
.cur_l
->text
[0] == '\0' && ods
.cur_l
->next
){
1371 KillHeaderLine(ods
.cur_l
, 1);
1372 ods
.p_len
= ucs4_strlen(ods
.cur_l
->text
);
1373 } while(ods
.cur_l
->text
[0] == '\0' && ods
.cur_l
->next
);
1378 if(ods
.cur_l
->text
[0] != '\0'){
1379 struct hdr_line
*h
, *start_of_addr
;
1382 /* cur is not first line */
1383 if(ods
.cur_l
!= headents
[ods
.cur_e
].hd_text
){
1385 * Protect against adding a new entry into
1386 * the middle of a long, continued entry.
1388 start_of_addr
= NULL
; /* cur_l is a good place to be */
1390 for(h
= headents
[ods
.cur_e
].hd_text
; h
; h
= h
->next
){
1391 if(ucs4_strqchr(h
->text
, ',', &q
, -1)){
1392 start_of_addr
= NULL
;
1395 else if(start_of_addr
== NULL
)
1398 if(h
->next
== ods
.cur_l
)
1403 ods
.cur_l
= start_of_addr
;
1404 ods
.p_len
= ucs4_strlen(ods
.cur_l
->text
);
1408 for(i
= ++ods
.p_len
; i
; i
--)
1409 ods
.cur_l
->text
[i
] = ods
.cur_l
->text
[i
-1];
1411 ods
.cur_l
->text
[0] = ',';
1414 if(FormatLines(ods
.cur_l
, bufp
,
1415 (term
.t_ncol
-headents
[ods
.cur_e
].prwid
),
1416 headents
[ods
.cur_e
].break_on_comma
, 0) == -1){
1417 emlwrite("Problem adding address to header !",
1424 * If the "selector" has a "builder" as well, pass
1425 * what was just selected thru the builder...
1427 if(headents
[ods
.cur_e
].builder
){
1429 int cur_row
, top_too
= 0;
1431 for(l
= headents
[ods
.cur_e
].hd_text
, cur_row
= 0;
1432 l
&& l
!= ods
.cur_l
;
1433 l
= l
->next
, cur_row
++)
1436 top_too
= headents
[ods
.cur_e
].hd_text
== ods
.top_l
;
1438 if(call_builder(&headents
[ods
.cur_e
], &mangled
,
1440 fix_mangle_and_err(&mangled
, &err
,
1441 headents
[ods
.cur_e
].name
);
1444 for(ods
.cur_l
= headents
[ods
.cur_e
].hd_text
;
1445 ods
.cur_l
->next
&& cur_row
;
1446 ods
.cur_l
= ods
.cur_l
->next
, cur_row
--)
1450 ods
.top_l
= headents
[ods
.cur_e
].hd_text
;
1457 u
= utf8_to_ucs4_cpystr(bufp
);
1459 ucs4_strncpy(headents
[ods
.cur_e
].hd_text
->text
, u
, HLSZ
);
1460 headents
[ods
.cur_e
].hd_text
->text
[HLSZ
-1] = '\0';
1461 fs_give((void **) &u
);
1466 /* mark this entry dirty */
1467 mark_sticky(&headents
[ods
.cur_e
]);
1468 headents
[ods
.cur_e
].dirty
= 1;
1469 fix_mangle_and_err(&mangled
,&err
,headents
[ods
.cur_e
].name
);
1472 /*----- No selector -----*/
1478 if(errmss
!= NULL
) {
1480 emlwrite(errmss
, NULL
);
1486 case (CTRL
|'G'): /* HELP */
1487 if(term
.t_mrow
== 0){
1490 sgarbk
= 1; /* bring up menu */
1495 if(!ComposerHelp(ods
.cur_e
))
1496 break; /* else, fall through... */
1498 case (CTRL
|'L'): /* redraw requested */
1502 case (CTRL
|'_'): /* file editor */
1503 if(headents
[ods
.cur_e
].fileedit
!= NULL
){
1504 struct headerentry
*e
;
1505 struct hdr_line
*line
;
1507 char *filename
= NULL
;
1508 VARS_TO_SAVE
*saved_state
;
1511 * Since the fileedit will make a new call back to pico()
1512 * we need to save and restore the pico state here.
1514 if((saved_state
= save_pico_state()) != NULL
){
1517 e
= &headents
[ods
.cur_e
];
1519 for(line
= e
->hd_text
; line
!= NULL
; line
= line
->next
)
1520 sz
+= ucs4_strlen(line
->text
);
1522 u
= (UCS
*)malloc((sz
+1) * sizeof(*u
));
1525 for(line
= e
->hd_text
; line
!= NULL
; line
= line
->next
)
1526 ucs4_strncat(u
, line
->text
, sz
+1-ucs4_strlen(u
)-1);
1528 filename
= ucs4_to_utf8_cpystr(u
);
1532 errmss
= (*(headents
[ods
.cur_e
].fileedit
))(filename
);
1537 restore_pico_state(saved_state
);
1538 free_pico_state(saved_state
);
1539 ttresize(); /* fixup screen bufs */
1540 picosigs(); /* restore altered signals */
1543 char *s
= "Not enough memory";
1547 errmss
= (char *)malloc((len
+1) * sizeof(char));
1548 strncpy(errmss
, s
, len
+1);
1554 if(errmss
!= NULL
) {
1556 emlwrite(errmss
, NULL
);
1568 default : /* huh? */
1570 unknown_command(ch
);
1576 while (ods
.p_line
< ComposerTopLine
);
1578 display_delimiter(1);
1579 curwp
->w_flag
|= WFMODE
;
1580 movecursor(currow
, curcol
);
1581 ComposerEditing
= FALSE
;
1582 if (ComposerTopLine
== BOTTOM()){
1584 PaintHeader(COMPOSER_TOP_LINE
, FALSE
);
1596 header_downline(int beyond
, int gripe
)
1598 struct hdr_line
*new_l
, *l
;
1599 int new_e
, status
, fullpaint
, len
, e
, incr
= 0;
1601 /* calculate the next line: physical *and* logical */
1604 if((new_l
= next_sel_hline(&new_e
, ods
.cur_l
)) == NULL
&& !beyond
){
1609 strncpy(xx
, "Can't move down. Use ^X to ", sizeof(xx
));
1610 xx
[sizeof(xx
)-1] = '\0';
1611 strncat(xx
, (Pmaster
&& Pmaster
->exit_label
)
1612 ? Pmaster
->exit_label
1613 : (gmode
& MDHDRONLY
)
1617 : "Send", sizeof(xx
)-strlen(xx
)-1);
1618 xx
[sizeof(xx
)-1] = '\0';
1619 strncat(xx
, ".", sizeof(xx
)-strlen(xx
)-1);
1620 xx
[sizeof(xx
)-1] = '\0';
1628 * Because of blank header lines the cursor may need to move down
1629 * more than one line. Figure out how far.
1634 if((l
= next_hline(&e
, l
)) != NULL
)
1637 break; /* can't happen */
1641 fullpaint
= ods
.p_line
>= BOTTOM(); /* force full redraw? */
1643 /* expand what needs expanding */
1644 if(new_e
!= ods
.cur_e
|| !new_l
){ /* new (or last) field ! */
1646 InvertPrompt(ods
.cur_e
, FALSE
); /* turn off current entry */
1648 if(headents
[ods
.cur_e
].is_attach
) { /* verify data ? */
1649 if((status
= FormatSyncAttach()) != 0){ /* fixup if 1 or -1 */
1650 headents
[ods
.cur_e
].rich_header
= 0;
1651 if(FormatLines(headents
[ods
.cur_e
].hd_text
, "",
1652 term
.t_ncol
-headents
[new_e
].prwid
,
1653 headents
[ods
.cur_e
].break_on_comma
, 0) == -1)
1654 emlwrite("\007Format lines failed!", NULL
);
1656 } else if(headents
[ods
.cur_e
].builder
) { /* expand addresses */
1660 if((status
= call_builder(&headents
[ods
.cur_e
], &mangled
, &err
))>0){
1661 struct hdr_line
*l
; /* fixup ods.cur_l */
1662 ods
.p_line
= 0; /* force top line recalc */
1663 for(l
= headents
[ods
.cur_e
].hd_text
; l
; l
= l
->next
)
1666 if(new_l
) /* if new_l, force validity */
1667 new_l
= headents
[new_e
].hd_text
;
1669 NewTop(0); /* get new top_l */
1671 else if(status
< 0){ /* bad addr? no leave! */
1673 fix_mangle_and_err(&mangled
, &err
, headents
[ods
.cur_e
].name
);
1674 InvertPrompt(ods
.cur_e
, TRUE
);
1678 fix_mangle_and_err(&mangled
, &err
, headents
[ods
.cur_e
].name
);
1681 if(new_l
){ /* if one below, turn it on */
1682 InvertPrompt(new_e
, TRUE
);
1683 sgarbk
= 1; /* paint keymenu too */
1687 if(new_l
){ /* fixup new pointers */
1688 ods
.cur_l
= (ods
.cur_e
!= new_e
) ? headents
[new_e
].hd_text
: new_l
;
1690 if(ods
.p_ind
> (len
= ucs4_strlen(ods
.cur_l
->text
)))
1694 if(!new_l
|| status
|| fullpaint
){ /* handle big screen paint */
1696 PaintHeader(COMPOSER_TOP_LINE
, FALSE
);
1699 if(!new_l
){ /* make sure we're done */
1700 ods
.p_line
= ComposerTopLine
;
1701 InvertPrompt(ods
.cur_e
, FALSE
); /* turn off current entry */
1705 return(new_l
? 1 : 0);
1713 header_upline(int gripe
)
1715 struct hdr_line
*new_l
, *l
;
1716 int new_e
, status
, fullpaint
, len
, e
, incr
= 0;
1719 /* calculate the next line: physical *and* logical */
1722 if(!(new_l
= prev_sel_hline(&new_e
, ods
.cur_l
))){ /* all the way up! */
1723 ods
.p_line
= COMPOSER_TOP_LINE
;
1725 eml
.s
= (Pmaster
->pine_flags
& MDHDRONLY
) ? "entry" : "header";
1726 emlwrite(_("Can't move beyond top of %s"), &eml
);
1733 * Because of blank header lines the cursor may need to move up
1734 * more than one line. Figure out how far.
1739 if((l
= prev_hline(&e
, l
)) != NULL
)
1742 break; /* can't happen */
1746 fullpaint
= ods
.p_line
<= COMPOSER_TOP_LINE
;
1748 if(new_e
!= ods
.cur_e
){ /* new field ! */
1749 InvertPrompt(ods
.cur_e
, FALSE
);
1750 if(headents
[ods
.cur_e
].is_attach
){
1751 if((status
= FormatSyncAttach()) != 0){ /* non-zero ? reformat field */
1752 headents
[ods
.cur_e
].rich_header
= 0;
1753 if(FormatLines(headents
[ods
.cur_e
].hd_text
, "",
1754 term
.t_ncol
- headents
[ods
.cur_e
].prwid
,
1755 headents
[ods
.cur_e
].break_on_comma
,0) == -1)
1756 emlwrite("\007Format lines failed!", NULL
);
1759 else if(headents
[ods
.cur_e
].builder
){
1763 if((status
= call_builder(&headents
[ods
.cur_e
], &mangled
,
1766 for(new_l
= headents
[new_e
].hd_text
;
1771 /* and cur_l (required in fix_and... */
1776 fix_mangle_and_err(&mangled
, &err
, headents
[ods
.cur_e
].name
);
1777 InvertPrompt(ods
.cur_e
, TRUE
);
1781 fix_mangle_and_err(&mangled
, &err
, headents
[ods
.cur_e
].name
);
1784 InvertPrompt(new_e
, TRUE
);
1788 ods
.cur_e
= new_e
; /* update pointers */
1790 if(ods
.p_ind
> (len
= ucs4_strlen(ods
.cur_l
->text
)))
1793 if(status
> 0 || fullpaint
){
1795 PaintHeader(COMPOSER_TOP_LINE
, FALSE
);
1807 AppendAttachment(char *fn
, char *sz
, char *cmt
)
1809 int a_e
, status
, spaces
;
1810 struct hdr_line
*lp
;
1814 /*--- Find headerentry that is attachments (only first) --*/
1815 for(a_e
= 0; headents
[a_e
].name
!= NULL
; a_e
++ )
1816 if(headents
[a_e
].is_attach
){
1817 /* make sure field stays displayed */
1818 headents
[a_e
].rich_header
= 0;
1819 headents
[a_e
].display_it
= 1;
1823 /* append new attachment line */
1824 for(lp
= headents
[a_e
].hd_text
; lp
->next
; lp
=lp
->next
)
1827 /* build new attachment line */
1828 if(lp
->text
[0]){ /* adding a line? */
1833 ucs4_strncat(lp
->text
, comma
, HLSZ
-ucs4_strlen(lp
->text
)-1); /* append delimiter */
1834 if((lp
->next
= HALLOC()) != NULL
){ /* allocate new line */
1835 lp
->next
->prev
= lp
;
1836 lp
->next
->next
= NULL
;
1840 emlwrite("\007Can't allocate line for new attachment!", NULL
);
1846 spaces
= (*fn
== '\"') ? 0 : (strpbrk(fn
, "(), \t") != NULL
);
1847 snprintf(b
, sizeof(b
), "%s%s%s (%s) \"%.*s\"",
1848 spaces
? "\"" : "", fn
, spaces
? "\"" : "",
1849 sz
? sz
: "", 80, cmt
? cmt
: "");
1850 u
= utf8_to_ucs4_cpystr(b
);
1852 ucs4_strncpy(lp
->text
, u
, HLSZ
);
1853 lp
->text
[HLSZ
-1] = '\0';
1854 fs_give((void **) &u
);
1857 /* validate the new attachment, and reformat if needed */
1858 if((status
= SyncAttach()) != 0){
1863 emlwrite("\007Problem attaching: %s", &eml
);
1866 if(FormatLines(headents
[a_e
].hd_text
, "",
1867 term
.t_ncol
- headents
[a_e
].prwid
,
1868 headents
[a_e
].break_on_comma
, 0) == -1){
1869 emlwrite("\007Format lines failed!", NULL
);
1875 PaintHeader(COMPOSER_TOP_LINE
, status
!= 0);
1877 return(status
!= 0);
1882 * LineEdit - Always use insert mode and handle line wrapping
1885 * Any characters typed in that aren't printable
1889 * Assume we are guaranteed that there is sufficiently
1890 * more buffer space in a line than screen width (just one
1891 * less thing to worry about). If you want to change this,
1892 * then pputc will have to be taught to check the line buffer
1893 * length, and HALLOC() will probably have to become a func.
1896 LineEdit(int allowedit
, UCS
*lastch
)
1898 register struct hdr_line
*lp
; /* temporary line pointer */
1901 register int status
; /* various func's return val */
1902 UCS
*tbufp
; /* temporary buffer pointers */
1905 UCS last_key
; /* last keystroke */
1907 strng
= ods
.cur_l
->text
; /* initialize offsets */
1908 ods
.p_len
= MIN(ucs4_strlen(strng
), HLSZ
);
1909 if(ods
.p_ind
< 0) /* offset within range? */
1911 else if(ods
.p_ind
> ods
.p_len
)
1912 ods
.p_ind
= ods
.p_len
;
1913 else if(ucs4_str_width_ptr_to_ptr(&strng
[0], &strng
[ods
.p_ind
]) > LINEWID()){
1916 u
= ucs4_particular_width(strng
, LINEWID());
1917 ods
.p_ind
= u
- strng
;
1920 while(1){ /* edit the line... */
1922 if(Pmaster
&& subject_line
!= NULL
1923 && ods
.cur_l
== subject_line
1924 && ods
.cur_l
->text
[0] == 0)
1925 (*Pmaster
->newthread
)();
1930 HeaderPaintCursor();
1936 (*term
.t_flush
)(); /* get everything out */
1939 mouse_in_content(KEY_MOUSE
, -1, -1, 0, 0);
1940 register_mfunc(mouse_in_content
,2,0,term
.t_nrow
-(term
.t_mrow
+1),
1944 mswin_setdndcallback (composer_file_drop
);
1945 mswin_mousetrackcallback(pico_cursor
);
1950 if (term
.t_nrow
< 6 && ch
!= NODATA
){
1952 emlwrite(_("Please make the screen larger."), NULL
);
1957 clear_mfunc(mouse_in_content
);
1960 mswin_cleardndcallback ();
1961 mswin_mousetrackcallback(NULL
);
1966 if(gmode
& P_DELRUBS
)
1970 (*Pmaster
->keybinput
)();
1971 if(!time_to_check())
1974 case NODATA
: /* new mail ? */
1975 if((*Pmaster
->newmail
)(ch
== NODATA
? 0 : 2, 1) >= 0){
1980 curwp
->w_ntrows
-= 2;
1985 rv
= (*Pmaster
->showmsg
)(ch
);
1988 if(rv
) /* Did showmsg corrupt the display? */
1989 PaintBody(0); /* Yes, repaint */
1994 curwp
->w_ntrows
+= 2;
1999 movecursor(ods
.p_line
,
2000 ucs4_str_width_ptr_to_ptr(strng
, &strng
[ods
.p_ind
])+headents
[ods
.cur_e
].prwid
);
2001 if(ch
== NODATA
) /* GetKey timed out */
2007 if(mpresf
){ /* blast old messages */
2008 if(mpresf
++ > NMMESSDELAY
){ /* every few keystrokes */
2010 movecursor(ods
.p_line
,
2011 ucs4_str_width_ptr_to_ptr(strng
, &strng
[ods
.p_ind
])+headents
[ods
.cur_e
].prwid
);
2015 tbufp
= &strng
[ods
.p_len
];
2017 if(VALID_KEY(ch
)){ /* char input */
2019 * if we are allowing editing, insert the new char
2020 * end up leaving tbufp pointing to newly
2021 * inserted character in string, and offset to the
2022 * index of the character after the inserted ch ...
2025 if(headents
[ods
.cur_e
].is_attach
&& intag(strng
,ods
.p_ind
)){
2026 emlwrite(_("\007Can't edit attachment number!"), NULL
);
2030 if(headents
[ods
.cur_e
].single_space
){
2032 && (strng
[ods
.p_ind
]==' ' || strng
[ods
.p_ind
-1]==' '))
2037 * go ahead and add the character...
2039 if(ods
.p_len
< HLSZ
){
2040 tbufp
= &strng
[++ods
.p_len
]; /* find the end */
2043 } while(--tbufp
> &strng
[ods
.p_ind
]); /* shift right */
2044 strng
[ods
.p_ind
++] = ch
; /* add char to str */
2047 /* mark this entry dirty */
2048 mark_sticky(&headents
[ods
.cur_e
]);
2049 headents
[ods
.cur_e
].dirty
= 1;
2052 * then find out where things fit...
2054 if(ucs4_str_width_ptr_to_ptr(&strng
[0], &strng
[ods
.p_len
]) < LINEWID()){
2057 c
.c
= ch
& CELLMASK
;
2059 if(pinsert(c
)){ /* add char to str */
2060 skipmove
++; /* must'a been optimal */
2061 continue; /* on to the next! */
2065 if((status
= FormatLines(ods
.cur_l
, "", LINEWID(),
2066 headents
[ods
.cur_e
].break_on_comma
,0)) == -1){
2072 * during the format, the dot may have moved
2073 * down to the next line...
2075 if(ods
.p_ind
>= ucs4_strlen(strng
)){
2077 ods
.p_ind
-= ucs4_strlen(strng
);
2078 ods
.cur_l
= ods
.cur_l
->next
;
2079 strng
= ods
.cur_l
->text
;
2082 ods
.p_len
= ucs4_strlen(strng
);
2086 PaintHeader(COMPOSER_TOP_LINE
, FALSE
);
2096 else { /* interpret ch as a command */
2097 switch (ch
= normalize_cmd(ch
, ckm
, 2)) {
2098 case (CTRL
|KEY_LEFT
): /* word skip left */
2099 if(ods
.p_ind
> 0) /* Scoot one char left if possible */
2104 if(ods
.p_line
!= COMPOSER_TOP_LINE
)
2105 ods
.p_ind
= 1000; /* put cursor at end of line */
2109 while(ods
.p_ind
> 0 && !ucs4_isalnum(strng
[ods
.p_ind
]))
2110 ods
.p_ind
--; /* skip any whitespace we're in */
2112 while(ods
.p_ind
> 0) {
2113 /* Bail if the character right before this one is whitespace */
2114 if(ods
.p_ind
> 1 && !ucs4_isalnum(strng
[ods
.p_ind
- 1]))
2120 case (CTRL
|'@') : /* word skip */
2121 case (CTRL
|KEY_RIGHT
):
2122 while(ucs4_isalnum(strng
[ods
.p_ind
]))
2123 ods
.p_ind
++; /* skip any text we're in */
2125 while(strng
[ods
.p_ind
] && !ucs4_isalnum(strng
[ods
.p_ind
]))
2126 ods
.p_ind
++; /* skip any whitespace after it */
2128 if(strng
[ods
.p_ind
] == '\0'){
2129 ods
.p_ind
= 0; /* end of line, let caller handle it */
2135 case (CTRL
|'K') : /* kill line cursor's on */
2142 if (!(gmode
& MDDTKILL
))
2145 if(KillHeaderLine(lp
, (last_key
== (CTRL
|'K')))){
2147 !(ods
.cur_l
->prev
==NULL
&& ods
.cur_l
->next
==NULL
))
2148 scrollup(wheadp
, ods
.p_line
, 1);
2150 if(ods
.cur_l
->next
== NULL
)
2151 if(zotcomma(ods
.cur_l
->text
)){
2153 ods
.p_ind
= ucs4_strlen(ods
.cur_l
->text
);
2156 i
= (ods
.p_line
== COMPOSER_TOP_LINE
);
2158 PaintHeader(COMPOSER_TOP_LINE
, TRUE
);
2162 movecursor(term
.t_nrow
, 0);
2169 strng
= ods
.cur_l
->text
;
2170 ods
.p_len
= ucs4_strlen(strng
);
2171 headents
[ods
.cur_e
].sticky
= 0;
2172 headents
[ods
.cur_e
].dirty
= 1;
2175 case (CTRL
|'U') : /* un-delete deleted lines */
2181 if(SaveHeaderLines()){
2183 PaintHeader(COMPOSER_TOP_LINE
, FALSE
);
2186 movecursor(term
.t_nrow
, 0);
2191 strng
= ods
.cur_l
->text
;
2192 ods
.p_len
= ucs4_strlen(strng
);
2193 mark_sticky(&headents
[ods
.cur_e
]);
2194 headents
[ods
.cur_e
].dirty
= 1;
2197 /* TRANSLATORS: Killing text is deleting it and
2198 Unkilling text is undeleting killed text. */
2199 emlwrite(_("Problem Unkilling text"), NULL
);
2203 case KEY_RIGHT
: /* move character right */
2204 if(ods
.p_ind
< ods
.p_len
){
2208 else if(gmode
& MDHDRONLY
)
2215 case KEY_LEFT
: /* move character left */
2220 if(ods
.p_line
!= COMPOSER_TOP_LINE
)
2221 ods
.p_ind
= 1000; /* put cursor at end of line */
2224 case (CTRL
|'M') : /* goto next field */
2229 case (CTRL
|'A') : /* goto beginning of line */
2234 case (CTRL
|'E') : /* goto end of line */
2235 ods
.p_ind
= ods
.p_len
;
2238 case (CTRL
|'D') : /* blast this char */
2244 else if(ods
.p_ind
>= ucs4_strlen(strng
))
2247 if(headents
[ods
.cur_e
].is_attach
&& intag(strng
, ods
.p_ind
)){
2248 emlwrite(_("\007Can't edit attachment number!"), NULL
);
2252 pputc(strng
[ods
.p_ind
++], 0); /* drop through and rubout */
2254 case DEL
: /* blast previous char */
2261 if(headents
[ods
.cur_e
].is_attach
&& intag(strng
, ods
.p_ind
-1)){
2262 emlwrite(_("\007Can't edit attachment number!"), NULL
);
2266 if(ods
.p_ind
> 0){ /* just shift left one char */
2268 headents
[ods
.cur_e
].dirty
= 1;
2270 headents
[ods
.cur_e
].sticky
= 0;
2272 mark_sticky(&headents
[ods
.cur_e
]);
2274 tbufp
= &strng
[--ods
.p_ind
];
2275 while(*tbufp
++ != '\0')
2277 tbufp
= &strng
[ods
.p_ind
];
2278 if(pdel()) /* physical screen delete */
2279 skipmove
++; /* must'a been optimal */
2281 else{ /* may have work to do */
2282 if(ods
.cur_l
->prev
== NULL
){
2283 (*term
.t_beep
)(); /* no erase into next field */
2288 ods
.cur_l
= ods
.cur_l
->prev
;
2289 strng
= ods
.cur_l
->text
;
2290 if((i
=ucs4_strlen(strng
)) > 0){
2291 strng
[i
-1] = '\0'; /* erase the character */
2295 headents
[ods
.cur_e
].sticky
= 0;
2299 tbufp
= &strng
[ods
.p_ind
];
2302 if((status
= FormatLines(ods
.cur_l
, "", LINEWID(),
2303 headents
[ods
.cur_e
].break_on_comma
,0))==-1){
2309 * beware, the dot may have moved...
2311 while((ods
.p_len
=ucs4_strlen(strng
)) < ods
.p_ind
){
2313 ods
.p_ind
-= ucs4_strlen(strng
);
2314 ods
.cur_l
= ods
.cur_l
->next
;
2315 strng
= ods
.cur_l
->text
;
2316 ods
.p_len
= ucs4_strlen(strng
);
2317 tbufp
= &strng
[ods
.p_ind
];
2324 PaintHeader(COMPOSER_TOP_LINE
, FALSE
);
2329 movecursor(ods
.p_line
,
2330 ucs4_str_width_ptr_to_ptr(strng
, &strng
[ods
.p_ind
])+headents
[ods
.cur_e
].prwid
);
2342 while ((tbufp
-strng
) < HLSZ
&& *tbufp
!= '\0') /* synchronizing loop */
2345 if(ucs4_str_width_ptr_to_ptr(&strng
[0], &strng
[ods
.p_len
]) < LINEWID())
2352 HeaderPaintCursor(void)
2354 movecursor(ods
.p_line
, ucs4_str_width_ptr_to_ptr(&ods
.cur_l
->text
[0], &ods
.cur_l
->text
[ods
.p_ind
])+headents
[ods
.cur_e
].prwid
);
2360 * FormatLines - Place the given text at the front of the given line->text
2361 * making sure to properly format the line, then check
2362 * all lines below for proper format.
2365 * Not much optimization at all. Right now, it recursively
2366 * fixes all remaining lines in the entry. Some speed might
2367 * gained if this was built to iteratively scan the lines.
2371 * FALSE if only this line is changed
2372 * TRUE if text below the first line is changed
2375 FormatLines(struct hdr_line
*h
, /* where to begin formatting */
2376 char *utf8_instr
, /* input string */
2377 int maxwid
, /* max width of a line */
2378 int break_on_comma
, /* break lines on commas */
2379 int quoted
) /* this line inside quotes */
2384 UCS
*ostr
; /* pointer to output string */
2385 UCS
*free_istr
= NULL
;
2387 UCS
*breakp
; /* pointer to line break */
2388 UCS
*bp
, *tp
; /* temporary pointers */
2389 UCS
*buf
; /* string to add later */
2390 struct hdr_line
*nlp
, *lp
;
2395 free_istr
= istr
= utf8_to_ucs4_cpystr(utf8_instr
);
2397 len
= ucs4_strlen(istr
) + ucs4_strlen(ostr
);
2398 if((buf
= (UCS
*) malloc((len
+10) * sizeof(*buf
))) == NULL
){
2400 fs_give((void **) &free_istr
);
2405 if(ucs4_str_width(istr
) + ucs4_str_width(ostr
) >= maxwid
){ /* break then fixup below */
2407 if((l
=ucs4_str_width(istr
)) < maxwid
){ /* room for more */
2409 if(break_on_comma
&& (bp
= ucs4_strqchr(istr
, ',', "ed
, -1))){
2410 bp
+= (bp
[1] == ' ') ? 2 : 1;
2411 for(tp
= bp
; *tp
&& *tp
== ' '; tp
++)
2414 ucs4_strncpy(buf
, tp
, len
+10);
2415 buf
[len
+10-1] = '\0';
2416 ucs4_strncat(buf
, ostr
, len
+10-ucs4_strlen(buf
)-1);
2417 buf
[len
+10-1] = '\0';
2419 for(i
= 0; &istr
[i
] < bp
; i
++)
2426 breakp
= break_point(ostr
, maxwid
-ucs4_str_width(istr
),
2427 break_on_comma
? ',' : ' ',
2428 break_on_comma
? "ed
: NULL
);
2430 if(breakp
== ostr
){ /* no good breakpoint */
2431 if(break_on_comma
&& *breakp
== ','){
2435 else if(ucs4_strchr(istr
,(break_on_comma
&& !quoted
)?',':' ')){
2436 ucs4_strncpy(buf
, ostr
, len
+10);
2437 buf
[len
+10-1] = '\0';
2438 ucs4_strncpy(ostr
, istr
, HLSZ
);
2439 ostr
[HLSZ
-1] = '\0';
2441 else{ /* istr's broken as we can get it */
2443 * Break at maxwid - width of istr
2445 breakp
= ucs4_particular_width(ostr
, maxwid
- ucs4_str_width(istr
)-1);
2453 ucs4_strncpy(buf
, breakp
, len
+10); /* save broken line */
2454 buf
[len
+10-1] = '\0';
2456 ucs4_strncpy(ostr
, istr
, HLSZ
); /* simple if no break */
2457 ostr
[HLSZ
-1] = '\0';
2460 *breakp
= '\0'; /* more work to break it */
2461 i
= ucs4_strlen(istr
);
2463 * shift ostr i chars
2465 for(bp
=breakp
; bp
>= ostr
&& i
; bp
--)
2467 for(tp
=ostr
, bp
=istr
; *bp
!= '\0'; tp
++, bp
++)
2468 *tp
= *bp
; /* then add istr */
2474 * Short-circuit the recursion in this case.
2475 * No time right now to figure out how to do it better.
2477 else if(l
> 2*maxwid
){
2478 UCS
*istrp
, *saveostr
= NULL
;
2482 saveostr
= ucs4_cpystr(ostr
);
2485 while(l
> 2*maxwid
){
2486 if(break_on_comma
|| maxwid
== 1){
2487 breakp
= (!(bp
= ucs4_strqchr(istrp
, ',', "ed
, maxwid
))
2488 || ucs4_str_width_ptr_to_ptr(istrp
, bp
) >= maxwid
|| maxwid
== 1)
2489 ? ucs4_particular_width(istrp
, maxwid
)
2490 : bp
+ ((bp
[1] == ' ') ? 2 : 1);
2493 breakp
= break_point(istrp
, maxwid
, ' ', NULL
);
2495 if(breakp
== istrp
) /* no good break point */
2496 breakp
= ucs4_particular_width(istrp
, maxwid
-1);
2499 for(tp
=ostr
,bp
=istrp
; bp
< breakp
; tp
++, bp
++)
2503 l
-= ucs4_str_width_ptr_to_ptr(istrp
, breakp
);
2506 if((lp
= HALLOC()) == NULL
){
2507 emlwrite("Can't allocate any more lines for header!", NULL
);
2510 fs_give((void **) &free_istr
);
2527 * Now l is still > maxwid. Do it the recursive way,
2528 * like the else clause below. Surely we could fix up the
2529 * flow control some here, but this works for now.
2535 ucs4_strncpy(ostr
, saveostr
, HLSZ
);
2536 ostr
[HLSZ
-1] = '\0';
2537 fs_give((void **) &saveostr
);
2540 if(break_on_comma
|| maxwid
== 1){
2541 breakp
= (!(bp
= ucs4_strqchr(istrp
, ',', "ed
, maxwid
))
2542 || ucs4_str_width_ptr_to_ptr(istrp
, bp
) >= maxwid
|| maxwid
== 1)
2543 ? ucs4_particular_width(istrp
, maxwid
)
2544 : bp
+ ((bp
[1] == ' ') ? 2 : 1);
2547 breakp
= break_point(istrp
, maxwid
, ' ', NULL
);
2549 if(breakp
== istrp
) /* no good break point */
2550 breakp
= ucs4_particular_width(istrp
, maxwid
-1);
2553 ucs4_strncpy(buf
, breakp
, len
+10); /* save broken line */
2554 buf
[len
+10-1] = '\0';
2555 ucs4_strncat(buf
, ostr
, len
+10-ucs4_strlen(buf
)-1); /* add line that was there */
2556 buf
[len
+10-1] = '\0';
2558 for(tp
=ostr
,bp
=istr
; bp
< breakp
; tp
++, bp
++)
2563 else{ /* utf8_instr > maxwid ! */
2564 if(break_on_comma
|| maxwid
== 1){
2565 breakp
= (!(bp
= ucs4_strqchr(istr
, ',', "ed
, maxwid
))
2566 || ucs4_str_width_ptr_to_ptr(istr
, bp
) >= maxwid
|| maxwid
== 1)
2567 ? ucs4_particular_width(istr
, maxwid
)
2568 : bp
+ ((bp
[1] == ' ') ? 2 : 1);
2571 breakp
= break_point(istr
, maxwid
, ' ', NULL
);
2573 if(breakp
== istr
) /* no good break point */
2574 breakp
= ucs4_particular_width(istr
, maxwid
-1);
2577 ucs4_strncpy(buf
, breakp
, len
+10); /* save broken line */
2578 buf
[len
+10-1] = '\0';
2579 ucs4_strncat(buf
, ostr
, len
+10-ucs4_strlen(buf
)-1); /* add line that was there */
2580 buf
[len
+10-1] = '\0';
2582 for(tp
=ostr
,bp
=istr
; bp
< breakp
; tp
++, bp
++)
2588 if(nlp
== NULL
){ /* no place to add below? */
2589 if((lp
= HALLOC()) == NULL
){
2590 emlwrite("Can't allocate any more lines for header!", NULL
);
2593 fs_give((void **) &free_istr
);
2598 if(TERM_OPTIMIZE
&& (i
= physical_line(h
)) != -1)
2599 scrolldown(wheadp
, i
- 1, 1);
2601 h
->next
= lp
; /* fix up links */
2609 else{ /* combined width < max */
2612 ucs4_strncpy(buf
, istr
, len
+10); /* insert istr before ostr */
2613 buf
[len
+10-1] = '\0';
2615 ucs4_strncat(buf
, ostr
, len
+10-ucs4_strlen(buf
)-1);
2616 buf
[len
+10-1] = '\0';
2618 ucs4_strncpy(ostr
, buf
, HLSZ
); /* copy back to ostr */
2619 ostr
[HLSZ
-1] = '\0';
2625 if(break_on_comma
&& (breakp
= ucs4_strqchr(ostr
, ',', "ed
, -1))){
2626 breakp
+= (breakp
[1] == ' ') ? 2 : 1;
2627 ucs4_strncpy(buf
, breakp
, len
+10);
2628 buf
[len
+10-1] = '\0';
2632 if((lp
= HALLOC()) == NULL
){
2633 emlwrite("Can't allocate any more lines for header!",NULL
);
2636 fs_give((void **) &free_istr
);
2641 if(TERM_OPTIMIZE
&& (i
= physical_line(h
)) != -1)
2642 scrolldown(wheadp
, i
- 1, 1);
2644 h
->next
= lp
; /* fix up links */
2654 if(!*buf
&& !breakp
){
2655 if(ucs4_str_width(ostr
) + ucs4_str_width(nlp
->text
) >= maxwid
){
2656 breakp
= break_point(nlp
->text
, maxwid
-ucs4_str_width(ostr
),
2657 break_on_comma
? ',' : ' ',
2658 break_on_comma
? "ed
: NULL
);
2660 if(breakp
== nlp
->text
){ /* commas this line? */
2661 for(tp
=ostr
; *tp
&& *tp
!= ' '; tp
++)
2664 if(!*tp
){ /* no commas, get next best */
2665 breakp
+= maxwid
- ucs4_str_width(ostr
) - 1;
2674 if(retval
){ /* only if something to do */
2675 for(tp
= &ostr
[ucs4_strlen(ostr
)],bp
=nlp
->text
; bp
<breakp
;
2677 *tp
= *bp
; /* add breakp to this line */
2679 for(tp
=nlp
->text
, bp
=breakp
; *bp
!= '\0'; tp
++, bp
++)
2680 *tp
= *bp
; /* shift next line to left */
2685 ucs4_strncat(ostr
, nlp
->text
, HLSZ
-ucs4_strlen(ostr
)-1);
2686 ostr
[HLSZ
-1] = '\0';
2688 if(TERM_OPTIMIZE
&& (i
= physical_line(nlp
)) != -1)
2689 scrollup(wheadp
, i
, 1);
2693 if(!(nlp
= h
->next
)){
2696 fs_give((void **) &free_istr
);
2698 return(TRUE
); /* can't go further */
2701 retval
= TRUE
; /* more work to do? */
2708 fs_give((void **) &free_istr
);
2715 utf8
= ucs4_to_utf8_cpystr(buf
);
2718 fs_give((void **) &free_istr
);
2723 i
= FormatLines(nlp
, utf8
, maxwid
, break_on_comma
, quoted
);
2724 fs_give((void **) &utf8
);
2726 case -1: /* bubble up worst case */
2730 if(retval
== FALSE
){
2745 * Format the lines before parsing attachments so we
2746 * don't expand a bunch of attachments that we don't
2747 * have the buffer space for.
2750 FormatSyncAttach(void)
2752 FormatLines(headents
[ods
.cur_e
].hd_text
, "",
2753 term
.t_ncol
- headents
[ods
.cur_e
].prwid
,
2754 headents
[ods
.cur_e
].break_on_comma
, 0);
2755 return(SyncAttach());
2760 * PaintHeader - do the work of displaying the header from the given
2761 * physical screen line the end of the header.
2763 * 17 July 91 - fixed reshow to deal with arbitrarily large headers.
2766 PaintHeader(int line
, /* physical line on screen */
2767 int clear
) /* clear before painting */
2769 struct hdr_line
*lp
;
2778 pclear(COMPOSER_TOP_LINE
, ComposerTopLine
-1);
2780 curline
= COMPOSER_TOP_LINE
;
2781 curindex
= curoffset
= 0;
2783 for(lp
= ods
.top_l
, e
= ods
.top_e
; ; curline
++){
2784 if((curline
== line
) || ((lp
= next_hline(&e
, lp
)) == NULL
))
2788 while(headents
[e
].name
!= NULL
){ /* begin to redraw */
2791 if((!lp
->prev
|| curline
== COMPOSER_TOP_LINE
) && !curoffset
){
2792 if(InvertPrompt(e
, (e
== ods
.cur_e
&& ComposerEditing
)) == -1
2793 && !is_blank(curline
, 0, headents
[e
].prwid
)){
2794 for(i
= 0; i
< headents
[e
].prwid
; i
++)
2800 else if(!is_blank(curline
, 0, headents
[e
].prwid
)){
2801 for(i
= 0; i
< headents
[e
].prwid
; i
++)
2807 if(*(bufp
= buf
) != '\0'){ /* need to paint? */
2808 movecursor(curline
, 0); /* paint the line... */
2809 while(*bufp
!= '\0')
2813 bufp
= &(lp
->text
[curindex
]); /* skip chars already there */
2814 curoffset
+= headents
[e
].prwid
;
2815 curindex
= index_from_col(curline
, curoffset
);
2816 while(pscr(curline
, curindex
) != NULL
&&
2817 *bufp
== pscr(curline
, curindex
)->c
&& *bufp
!= '\0'){
2818 w
= wcellwidth(*bufp
);
2819 curoffset
+= (w
>= 0 ? w
: 1);
2822 if(curoffset
>= term
.t_ncol
)
2826 if(*bufp
!= '\0'){ /* need to move? */
2827 movecursor(curline
, curoffset
);
2828 while(*bufp
!= '\0'){ /* display what's not there */
2830 w
= wcellwidth(*bufp
);
2831 curoffset
+= (w
>= 0 ? w
: 1);
2837 if(curoffset
< term
.t_ncol
){
2838 movecursor(curline
, curoffset
);
2842 curindex
= curoffset
= 0;
2843 if(curline
>= BOTTOM())
2849 if(curline
>= BOTTOM())
2850 return; /* don't paint delimiter */
2852 while(headents
[++e
].name
!= NULL
)
2853 if(headents
[e
].display_it
){
2854 lp
= headents
[e
].hd_text
;
2859 display_delimiter(ComposerEditing
? 0 : 1);
2865 * PaintBody() - generic call to handle repainting everything BUT the
2869 * The header redrawing in a level 0 body paint gets done
2873 PaintBody(int level
)
2875 curwp
->w_flag
|= WFHARD
; /* make sure framing's right */
2876 if(level
== 0) /* specify what to update */
2879 update(); /* display message body */
2881 if(level
== 0 && ComposerEditing
){
2882 mlerase(); /* clear the error line */
2889 * display_for_send - paint the composer from the top line and return.
2892 display_for_send(void)
2897 /* if first header line isn't displayed, there's work to do */
2898 if(headents
&& ((l
= first_hline(&i
)) != ods
.top_l
2899 || ComposerTopLine
== COMPOSER_TOP_LINE
2901 struct on_display orig_ods
;
2902 int orig_edit
= ComposerEditing
,
2903 orig_ct_line
= ComposerTopLine
;
2906 * fake that the cursor's in the first header line
2907 * and force repaint...
2911 ods
.top_l
= ods
.cur_l
= l
;
2912 ods
.top_e
= ods
.cur_e
;
2913 ods
.p_line
= COMPOSER_TOP_LINE
;
2914 ComposerEditing
= TRUE
; /* to fool update() */
2915 setimark(FALSE
, 1); /* remember where we were */
2918 UpdateHeader(0); /* redraw whole enchilada */
2919 PaintHeader(COMPOSER_TOP_LINE
, TRUE
);
2922 ods
= orig_ods
; /* restore original state */
2923 ComposerEditing
= orig_edit
;
2924 ComposerTopLine
= curwp
->w_toprow
= orig_ct_line
;
2925 curwp
->w_ntrows
= BOTTOM() - ComposerTopLine
;
2926 swapimark(FALSE
, 1);
2928 /* in case we don't exit, set up restoring the screen */
2929 sgarbf
= TRUE
; /* force redraw */
2935 * ArrangeHeader - set up display parm such that header is reasonably
2942 register struct hdr_line
*l
;
2944 ods
.p_line
= ods
.p_ind
= 0;
2946 l
= ods
.top_l
= headents
[e
].hd_text
;
2947 while(headents
[e
+1].name
|| (l
&& l
->next
))
2948 if((l
= next_sel_hline(&e
, l
)) != NULL
){
2958 * ComposerHelp() - display mail help in a context sensitive way
2959 * based on the level passed ...
2962 ComposerHelp(int level
)
2965 VARS_TO_SAVE
*saved_state
;
2967 curwp
->w_flag
|= WFMODE
;
2970 if(level
< 0 || !headents
[level
].name
){
2972 emlwrite("Sorry, I can't help you with that.", NULL
);
2977 snprintf(buf
, sizeof(buf
), "Help for %s %.40s Field",
2978 (Pmaster
->pine_flags
& MDHDRONLY
) ? "Address Book"
2980 headents
[level
].name
);
2981 saved_state
= save_pico_state();
2982 (*Pmaster
->helper
)(headents
[level
].help
, buf
, 1);
2984 restore_pico_state(saved_state
);
2985 free_pico_state(saved_state
);
2989 picosigs(); /* restore altered handlers */
2996 * ToggleHeader() - set or unset pico values to the full screen size
2997 * painting header if need be.
3000 ToggleHeader(int show
)
3003 * check to see if we need to display the header...
3006 UpdateHeader(0); /* figure bounds */
3007 PaintHeader(COMPOSER_TOP_LINE
, FALSE
); /* draw it */
3011 * set bounds for no header display
3013 curwp
->w_toprow
= ComposerTopLine
= COMPOSER_TOP_LINE
;
3014 curwp
->w_ntrows
= BOTTOM() - ComposerTopLine
;
3022 * HeaderLen() - return the length in lines of the exposed portion of the
3028 register struct hdr_line
*lp
;
3036 lp
= next_hline(&e
, lp
);
3045 * first_hline() - return a pointer to the first displayable header line
3048 * 1) pointer to first displayable line in header and header
3049 * entry, via side effect, that the first line is a part of
3050 * 2) NULL if no next line, leaving entry at LASTHDR
3053 first_hline(int *entry
)
3055 /* init *entry so we're sure to start from the top */
3056 for(*entry
= 0; headents
[*entry
].name
; (*entry
)++)
3057 if(headents
[*entry
].display_it
)
3058 return(headents
[*entry
].hd_text
);
3061 return(NULL
); /* this shouldn't happen */
3066 * first_sel_hline() - return a pointer to the first selectable header line
3069 * 1) pointer to first selectable line in header and header
3070 * entry, via side effect, that the first line is a part of
3071 * 2) NULL if no next line, leaving entry at LASTHDR
3074 first_sel_hline(int *entry
)
3076 /* init *entry so we're sure to start from the top */
3077 for(*entry
= 0; headents
[*entry
].name
; (*entry
)++)
3078 if(headents
[*entry
].display_it
&& !headents
[*entry
].blank
)
3079 return(headents
[*entry
].hd_text
);
3082 return(NULL
); /* this shouldn't happen */
3088 * next_hline() - return a pointer to the next line structure
3091 * 1) pointer to next displayable line in header and header
3092 * entry, via side effect, that the next line is a part of
3093 * 2) NULL if no next line, leaving entry at LASTHDR
3096 next_hline(int *entry
, struct hdr_line
*line
)
3101 if(line
->next
== NULL
){
3102 while(headents
[++(*entry
)].name
!= NULL
){
3103 if(headents
[*entry
].display_it
)
3104 return(headents
[*entry
].hd_text
);
3115 * next_sel_hline() - return a pointer to the next selectable line structure
3118 * 1) pointer to next selectable line in header and header
3119 * entry, via side effect, that the next line is a part of
3120 * 2) NULL if no next line, leaving entry at LASTHDR
3123 next_sel_hline(int *entry
, struct hdr_line
*line
)
3128 if(line
->next
== NULL
){
3129 while(headents
[++(*entry
)].name
!= NULL
){
3130 if(headents
[*entry
].display_it
&& !headents
[*entry
].blank
)
3131 return(headents
[*entry
].hd_text
);
3143 * prev_hline() - return a pointer to the next line structure back
3146 * 1) pointer to previous displayable line in header and
3147 * the header entry that the next line is a part of
3149 * 2) NULL if no next line, leaving entry unchanged from
3150 * the value it had on entry.
3153 prev_hline(int *entry
, struct hdr_line
*line
)
3158 if(line
->prev
== NULL
){
3161 orig_entry
= *entry
;
3162 while(--(*entry
) >= 0){
3163 if(headents
[*entry
].display_it
){
3164 line
= headents
[*entry
].hd_text
;
3165 while(line
->next
!= NULL
)
3171 *entry
= orig_entry
;
3180 * prev_sel_hline() - return a pointer to the previous selectable line
3183 * 1) pointer to previous selectable line in header and
3184 * the header entry that the next line is a part of
3186 * 2) NULL if no next line, leaving entry unchanged from
3187 * the value it had on entry.
3190 prev_sel_hline(int *entry
, struct hdr_line
*line
)
3195 if(line
->prev
== NULL
){
3198 orig_entry
= *entry
;
3199 while(--(*entry
) >= 0){
3200 if(headents
[*entry
].display_it
&& !headents
[*entry
].blank
){
3201 line
= headents
[*entry
].hd_text
;
3202 while(line
->next
!= NULL
)
3208 *entry
= orig_entry
;
3218 * first_requested_hline() - return pointer to first line that pico's caller
3219 * asked that we start on.
3222 first_requested_hline(int *ent
)
3225 struct hdr_line
*rv
= NULL
;
3227 for(reqfield
= -1, i
= 0; headents
[i
].name
; i
++)
3228 if(headents
[i
].start_here
){
3229 headents
[i
].start_here
= 0; /* clear old setting */
3230 if(reqfield
< 0){ /* if not already, set up */
3231 headents
[i
].display_it
= 1; /* make sure it's shown */
3232 *ent
= reqfield
= i
;
3233 rv
= headents
[i
].hd_text
;
3243 * UpdateHeader() - determines the best range of lines to be displayed
3244 * using the global ods value for the current line and the
3245 * top line, also sets ComposerTopLine and pico limits
3247 * showtop -- Attempt to show all header lines if they'll fit.
3250 * This is pretty ugly because it has to keep the current line
3251 * on the screen in a reasonable location no matter what.
3252 * There are also a couple of rules to follow:
3253 * 1) follow paging conventions of pico (ie, half page
3255 * 2) if more than one page, always display last half when
3256 * pline is toward the end of the header
3259 * TRUE if anything changed (side effects: new p_line, top_l
3260 * top_e, and pico parms)
3261 * FALSE if nothing changed
3265 UpdateHeader(int showtop
)
3267 register struct hdr_line
*lp
;
3270 int old_top
= ComposerTopLine
;
3271 int old_p
= ods
.p_line
;
3273 if(ods
.p_line
< COMPOSER_TOP_LINE
||
3274 ((ods
.p_line
== ComposerTopLine
-2) ? 2: 0) + ods
.p_line
>= BOTTOM()){
3275 /* NewTop if cur header line is at bottom of screen or two from */
3276 /* the bottom of the screen if cur line is bottom header line */
3277 NewTop(showtop
); /* get new top_l */
3280 else{ /* make sure p_line's OK */
3281 i
= COMPOSER_TOP_LINE
;
3284 while(lp
!= ods
.cur_l
){
3286 * this checks to make sure cur_l is below top_l and that
3287 * cur_l is on the screen...
3289 if((lp
= next_hline(&le
, lp
)) == NULL
|| ++i
>= BOTTOM()){
3297 ods
.p_line
= COMPOSER_TOP_LINE
; /* find p_line... */
3300 while(lp
&& lp
!= ods
.cur_l
){
3301 lp
= next_hline(&le
, lp
);
3306 ret
= !(ods
.p_line
== old_p
);
3308 ComposerTopLine
= ods
.p_line
; /* figure top composer line */
3309 while(lp
&& ComposerTopLine
<= BOTTOM()){
3310 lp
= next_hline(&le
, lp
);
3311 ComposerTopLine
+= (lp
) ? 1 : 2; /* allow for delim at end */
3315 ret
= !(ComposerTopLine
== old_top
);
3317 if(wheadp
->w_toprow
!= ComposerTopLine
){ /* update pico params... */
3318 wheadp
->w_toprow
= ComposerTopLine
;
3319 wheadp
->w_ntrows
= ((i
= BOTTOM() - ComposerTopLine
) > 0) ? i
: 0;
3328 * NewTop() - calculate a new top_l based on the cur_l
3330 * showtop -- Attempt to show all the header lines if they'll fit
3333 * with ods.top_l and top_e pointing at a reasonable line
3339 register struct hdr_line
*lp
;
3345 i
= showtop
? FULL_SCR() : HALF_SCR();
3347 while(lp
!= NULL
&& (--i
> 0)){
3350 lp
= prev_hline(&e
, lp
);
3357 * display_delimiter() - just paint the header/message body delimiter with
3358 * inverse value specified by state.
3361 display_delimiter(int state
)
3365 if(ComposerTopLine
- 1 >= BOTTOM()) /* silently forget it */
3368 buf
= utf8_to_ucs4_cpystr((gmode
& MDHDRONLY
) ? "" : HDR_DELIM
);
3374 if(state
== delim_ps
){ /* optimize ? */
3375 for(delim_ps
= 0; bufp
[delim_ps
] && pscr(ComposerTopLine
-1,delim_ps
) != NULL
&& pscr(ComposerTopLine
-1,delim_ps
)->c
== bufp
[delim_ps
];delim_ps
++)
3378 if(bufp
[delim_ps
] == '\0' && !(gmode
& MDHDRONLY
)){
3380 fs_give((void **) &buf
);
3381 return; /* already displayed! */
3387 movecursor(ComposerTopLine
- 1, 0);
3391 while(*bufp
!= '\0')
3392 pputc(*bufp
++, state
? 1 : 0);
3398 fs_give((void **) &buf
);
3404 * InvertPrompt() - invert the prompt associated with header entry to state
3405 * state (true if invert, false otherwise).
3407 * non-zero if nothing done
3408 * 0 if prompt inverted successfully
3411 * come to think of it, this func and the one above could
3412 * easily be combined
3415 InvertPrompt(int entry
, int state
)
3421 buf
= utf8_to_ucs4_cpystr(headents
[entry
].prompt
); /* fresh prompt paint */
3426 if((i
= entry_line(entry
, FALSE
)) == -1){
3427 fs_give((void **) &buf
);
3428 return(-1); /* silently forget it */
3431 end
= buf
+ ucs4_strlen(buf
);
3434 * Makes sure that the prompt doesn't take up more than prwid of screen space.
3435 * The caller should do that, too, in order to make it look right so
3436 * this should most likely be a no-op
3438 if(ucs4_str_width_ptr_to_ptr(buf
, end
) > headents
[entry
].prwid
){
3439 end
= ucs4_particular_width(buf
, headents
[entry
].prwid
);
3443 if(entry
< 16 && (invert_ps
&(1<<entry
))
3444 == (state
? 1<<entry
: 0)){ /* optimize ? */
3447 for(j
= 0; bufp
[j
] && pscr(i
, j
)->c
== bufp
[j
]; j
++)
3450 if(bufp
[j
] == '\0'){
3452 invert_ps
|= 1<<entry
;
3454 invert_ps
&= ~(1<<entry
);
3456 fs_give((void **) &buf
);
3457 return(0); /* already displayed! */
3461 if(entry
< 16){ /* if > 16, cannot be stored in invert_ps */
3463 invert_ps
|= 1<<entry
;
3465 invert_ps
&= ~(1<<entry
);
3472 while(*bufp
&& *(bufp
+ 1))
3473 pputc(*bufp
++, 1); /* putc upto last char */
3478 pputc(*bufp
, 0); /* last char not inverted */
3480 fs_give((void **) &buf
);
3488 * partial_entries() - toggle display of the bcc and fcc fields.
3491 * TRUE if there are partial entries on the display
3495 partial_entries(void)
3497 register struct headerentry
*h
;
3500 /*---- find out status of first rich header ---*/
3501 for(h
= headents
; !h
->rich_header
&& h
->name
!= NULL
; h
++)
3504 is_on
= h
->display_it
;
3505 for(h
= headents
; h
->name
!= NULL
; h
++)
3507 h
->display_it
= ! is_on
;
3515 * entry_line() - return the physical line on the screen associated
3516 * with the given header entry field. Note: the field
3517 * may span lines, so if the last char is set, return
3518 * the appropriate value.
3521 * 1) physical line number of entry
3522 * 2) -1 if entry currently not on display
3525 entry_line(int entry
, int lastchar
)
3527 register int p_line
= COMPOSER_TOP_LINE
;
3529 register struct hdr_line
*line
;
3531 for(line
= ods
.top_l
, i
= ods
.top_e
;
3532 headents
&& headents
[i
].name
&& i
<= entry
;
3534 if(p_line
>= BOTTOM())
3538 if(line
->next
== NULL
)
3541 else if(line
->prev
== NULL
)
3546 line
= next_hline(&i
, line
);
3554 * physical_line() - return the physical line on the screen associated
3555 * with the given header line pointer.
3558 * 1) physical line number of entry
3559 * 2) -1 if entry currently not on display
3562 physical_line(struct hdr_line
*l
)
3564 register int p_line
= COMPOSER_TOP_LINE
;
3565 register struct hdr_line
*lp
;
3568 for(lp
=ods
.top_l
, i
=ods
.top_e
; headents
[i
].name
&& lp
!= NULL
; p_line
++){
3569 if(p_line
>= BOTTOM())
3575 lp
= next_hline(&i
, lp
);
3583 * call_builder() - resolve any nicknames in the address book associated
3584 * with the given entry...
3588 * BEWARE: this function can cause cur_l and top_l to get lost so BE
3589 * CAREFUL before and after you call this function!!!
3591 * There could to be something here to resolve cur_l and top_l
3592 * reasonably into the new linked list for this entry.
3594 * The reason this would mostly work without it is resolve_niks gets
3595 * called for the most part in between fields. Since we're moving
3596 * to the beginning or end (i.e. the next/prev pointer in the old
3597 * freed cur_l is NULL) of the next entry, we get a new cur_l
3598 * pointing at a good line. Then since top_l is based on cur_l in
3599 * NewTop() we have pretty much lucked out.
3601 * Where we could get burned is in a canceled exit (ctrl|x). Here
3602 * nicknames get resolved into addresses, which invalidates cur_l
3603 * and top_l. Since we don't actually leave, we could begin editing
3604 * again with bad pointers. This would usually results in a nice
3607 * NOTE: The mangled argument is a little strange. It's used on both
3608 * input and output. On input, if it is not set, then that tells the
3609 * builder not to do anything that might take a long time, like a
3610 * white pages lookup. On return, it tells the caller that the screen
3611 * and signals may have been mangled so signals should be reset, window
3612 * resized, and screen redrawn.
3615 * > 0 if any names where resolved, otherwise
3618 * -1: move to next line
3619 * -2: don't move off this line
3622 call_builder(struct headerentry
*entry
, int *mangled
, char **err
)
3624 register int retval
= 0;
3626 register struct hdr_line
*line
;
3632 struct headerentry
*e
;
3633 BUILDER_ARG
*nextarg
, *arg
= NULL
, *headarg
= NULL
;
3634 VARS_TO_SAVE
*saved_state
;
3639 line
= entry
->hd_text
;
3641 while(line
!= NULL
){
3642 sbuflen
+= (6*term
.t_ncol
);
3646 if((sbuf
=(char *)malloc(sbuflen
* sizeof(*sbuf
))) == NULL
){
3647 emlwrite("Can't malloc space to expand address", NULL
);
3654 * cat the whole entry into one string...
3656 line
= entry
->hd_text
;
3657 while(line
!= NULL
){
3658 i
= ucs4_strlen(line
->text
);
3661 * To keep pine address builder happy, addresses should be separated
3662 * by ", ". Add this space if needed, otherwise...
3663 * (This is some ancient requirement that is no longer needed.)
3665 * If this line is NOT a continuation of the previous line, add
3666 * white space for pine's address builder if its not already there...
3667 * (This is some ancient requirement that is no longer needed.)
3669 * Also if it's not a continuation (i.e., there's already and addr on
3670 * the line), and there's another line below, treat the new line as
3672 * (This should only be done for address-type lines, not for regular
3673 * text lines like subjects. Key off of the break_on_comma bit which
3674 * should only be set on those that won't mind a comma being added.)
3676 if(entry
->break_on_comma
){
3677 UCS
*space
, commaspace
[3];
3679 commaspace
[0] = ',';
3680 commaspace
[1] = ' ';
3681 commaspace
[2] = '\0';
3682 space
= commaspace
+1;
3684 if(i
&& line
->text
[i
-1] == ','){
3685 ucs4_strncat(line
->text
, space
, HLSZ
-i
-1); /* help address builder */
3686 line
->text
[HLSZ
-1] = '\0';
3688 else if(line
->next
!= NULL
&& !strend(line
->text
, ',')){
3689 if(ucs4_strqchr(line
->text
, ',', "ed
, -1)){
3690 ucs4_strncat(line
->text
, commaspace
, HLSZ
-i
-1); /* implied comma */
3691 line
->text
[HLSZ
-1] = '\0';
3694 else if(line
->prev
!= NULL
&& line
->next
!= NULL
){
3695 if(ucs4_strchr(line
->prev
->text
, ' ') != NULL
3696 && line
->text
[i
-1] != ' '){
3697 ucs4_strncat(line
->text
, space
, HLSZ
-i
-1);
3698 line
->text
[HLSZ
-1] = '\0';
3703 tmp
= ucs4_to_utf8_cpystr(line
->text
);
3705 strncat(sbuf
, tmp
, sbuflen
-strlen(sbuf
)-1);
3706 sbuf
[sbuflen
-1] = '\0';
3707 fs_give((void **) &tmp
);
3713 if(entry
->affected_entry
){
3714 /* check if any non-sticky affected entries */
3715 for(e
= entry
->affected_entry
; e
; e
= e
->next_affected
)
3719 /* there is at least one non-sticky so make a list to pass */
3721 for(e
= entry
->affected_entry
; e
; e
= e
->next_affected
){
3723 headarg
= arg
= (BUILDER_ARG
*)malloc(sizeof(BUILDER_ARG
));
3725 emlwrite("Can't malloc space for fcc", NULL
);
3731 arg
->aff
= &(e
->bldr_private
);
3732 arg
->me
= &(entry
->bldr_private
);
3736 nextarg
= (BUILDER_ARG
*)malloc(sizeof(BUILDER_ARG
));
3738 emlwrite("Can't malloc space for fcc", NULL
);
3742 nextarg
->next
= NULL
;
3743 nextarg
->tptr
= NULL
;
3744 nextarg
->aff
= &(e
->bldr_private
);
3745 nextarg
->me
= &(entry
->bldr_private
);
3746 arg
->next
= nextarg
;
3753 arg
->tptr
= ucs4_to_utf8_cpystr(line
->text
);
3760 * Even if there are no affected entries, we still need the arg
3761 * to pass the "me" pointer.
3764 headarg
= (BUILDER_ARG
*)malloc(sizeof(BUILDER_ARG
));
3766 emlwrite("Can't malloc space", NULL
);
3770 headarg
->next
= NULL
;
3771 headarg
->tptr
= NULL
;
3772 headarg
->aff
= NULL
;
3773 headarg
->me
= &(entry
->bldr_private
);
3778 * The builder may make a new call back to pico() so we save and
3779 * restore the pico state.
3781 saved_state
= save_pico_state();
3782 retval
= (*entry
->builder
)(sbuf
, &s
, err
, headarg
, mangled
);
3784 restore_pico_state(saved_state
);
3785 free_pico_state(saved_state
);
3788 if(mangled
&& *mangled
& BUILDER_MESSAGE_DISPLAYED
){
3789 *mangled
&= ~ BUILDER_MESSAGE_DISPLAYED
;
3794 if(mangled
&& *mangled
& BUILDER_FOOTER_MANGLED
){
3795 *mangled
&= ~ BUILDER_FOOTER_MANGLED
;
3797 pclear(term
.t_nrow
-1, term
.t_nrow
);
3801 if(strcmp(sbuf
, s
)){
3802 line
= entry
->hd_text
;
3803 InitEntryText(s
, entry
); /* arrange new one */
3804 zotentry(line
); /* blast old list o'entries */
3805 entry
->dirty
= 1; /* mark it dirty */
3809 for(e
= entry
->affected_entry
, arg
= headarg
;
3811 e
= e
->next_affected
, arg
= arg
? arg
->next
: NULL
){
3814 tmp
= ucs4_to_utf8_cpystr(line
->text
);
3815 if(strcmp(tmp
, arg
? arg
->tptr
: "")){ /* it changed */
3816 /* make sure they see it if changed */
3818 InitEntryText(arg
? arg
->tptr
: "", e
);
3819 if(line
== ods
.top_l
)
3820 ods
.top_l
= e
->hd_text
;
3822 zotentry(line
); /* blast old list o'entries */
3823 e
->dirty
= 1; /* mark it dirty */
3828 fs_give((void **) &tmp
);
3836 for(arg
= headarg
; arg
; arg
= nextarg
){
3837 /* Don't free xtra or me, they just point to headerentry data */
3838 nextarg
= arg
->next
;
3854 VARS_TO_SAVE
*saved_state
;
3857 if(!Pmaster
->expander
)
3861 * Since expander may make a call back to pico() we need to
3862 * save and restore pico state.
3864 if((saved_state
= save_pico_state()) != NULL
){
3866 expret
= (*Pmaster
->expander
)(headents
, &s
);
3868 restore_pico_state(saved_state
);
3869 free_pico_state(saved_state
);
3873 if(expret
> 0 && s
){
3875 int i
, biggest
= 100;
3876 struct headerentry
*e
;
3879 * Use tbuf to cat together multiple line entries before comparing.
3881 tbuf
= (char *)malloc((biggest
+1) * sizeof(*tbuf
));
3882 for(e
= headents
, i
=0; e
->name
!= NULL
; e
++,i
++){
3884 struct hdr_line
*line
;
3886 while(e
->name
&& e
->blank
)
3892 for(line
= e
->hd_text
; line
!= NULL
; line
= line
->next
){
3893 p
= ucs4_to_utf8_cpystr(line
->text
);
3896 fs_give((void **) &p
);
3903 tbuf
= (char *)malloc((biggest
+1) * sizeof(*tbuf
));
3907 for(line
= e
->hd_text
; line
!= NULL
; line
= line
->next
){
3908 p
= ucs4_to_utf8_cpystr(line
->text
);
3910 strncat(tbuf
, p
, biggest
+1-strlen(tbuf
)-1);
3911 tbuf
[biggest
] = '\0';
3912 fs_give((void **) &p
);
3916 if(strcmp(tbuf
, s
[i
])){ /* it changed */
3917 struct hdr_line
*zline
;
3919 line
= zline
= e
->hd_text
;
3920 InitEntryText(s
[i
], e
);
3923 * If any of the lines for this entry are current or
3926 for(; line
!= NULL
; line
= line
->next
){
3927 if(line
== ods
.top_l
)
3928 ods
.top_l
= e
->hd_text
;
3930 if(line
== ods
.cur_l
)
3931 ods
.cur_l
= e
->hd_text
;
3934 zotentry(zline
); /* blast old list o'entries */
3956 * strend - neglecting white space, returns TRUE if c is at the
3957 * end of the given line. otherwise FALSE.
3960 strend(UCS
*s
, UCS ch
)
3964 if(s
== NULL
|| *s
== '\0')
3967 for(b
= &s
[ucs4_strlen(s
)] - 1; *b
&& ucs4_isspace(*b
); b
--){
3977 * ucs4_strqchr - returns pointer to first non-quote-enclosed occurance of ch in
3978 * the given string. otherwise NULL.
3980 * ch -- the character we're looking for
3981 * q -- q tells us if we start out inside quotes on entry and is set
3982 * correctly on exit.
3983 * m -- max characters we'll check for ch (set to -1 for no max)
3986 ucs4_strqchr(UCS
*s
, UCS ch
, int *q
, int m
)
3988 int quoted
= (q
) ? *q
: 0;
3990 for(; s
&& *s
&& m
!= 0; s
++, m
--){
3997 if(!quoted
&& *s
== ch
)
4006 * KillHeaderLine() - kill a line in the header
4009 * This is pretty simple. Just using the emacs kill buffer
4010 * and its accompanying functions to cut the text from lines.
4013 * TRUE if hldelete worked
4017 KillHeaderLine(struct hdr_line
*l
, int append
)
4027 if (gmode
& MDDTKILL
){
4028 if (c
[i
] == '\0') /* don't insert a new line after this line*/
4030 /*put to be deleted part into kill buffer */
4031 for (i
=ods
.p_ind
; c
[i
] != '\0'; i
++)
4034 while(*c
!= '\0') /* splat out the line */
4039 kinsert('\n'); /* helpful to yank in body */
4042 mswin_killbuftoclip (kremove
);
4045 if (gmode
& MDDTKILL
){
4046 if (l
->text
[0]=='\0'){
4048 if(l
->next
&& l
->prev
)
4049 ods
.cur_l
= next_hline(&ods
.cur_e
, l
);
4051 ods
.cur_l
= prev_hline(&ods
.cur_e
, l
);
4054 ods
.top_l
= ods
.cur_l
;
4056 return(hldelete(l
));
4059 l
->text
[ods
.p_ind
]='\0'; /* delete part of the line from the cursor */
4063 if(l
->next
&& l
->prev
)
4064 ods
.cur_l
= next_hline(&ods
.cur_e
, l
);
4066 ods
.cur_l
= prev_hline(&ods
.cur_e
, l
);
4069 ods
.top_l
= ods
.cur_l
;
4071 return(hldelete(l
)); /* blast it */
4078 * SaveHeaderLines() - insert the saved lines in the list before the
4079 * current line in the header
4082 * Once again, just using emacs' kill buffer and its
4086 * TRUE if something good happend
4090 SaveHeaderLines(void)
4092 UCS
*buf
; /* malloc'd copy of buffer */
4093 UCS
*bp
; /* pointer to above buffer */
4094 register unsigned i
; /* index */
4095 UCS
*work_buf
, *work_buf_begin
;
4097 int len
, buf_len
, work_buf_len
, tentative_p_ind
= 0;
4098 struct hdr_line
*travel
, *tentative_cur_l
= NULL
;
4101 if((bp
= buf
= (UCS
*) malloc((ksize()+5) * sizeof(*buf
))) == NULL
){
4102 emlwrite("Can't malloc space for saved text", NULL
);
4109 for(i
=0; i
< ksize(); i
++)
4110 if(kremove(i
) != '\n') /* filter out newlines */
4111 *bp
++ = (UCS
) kremove(i
);
4115 while(--bp
>= buf
) /* kill trailing white space */
4117 if(ods
.cur_l
->text
[0] != '\0'){
4118 if(*bp
== '>'){ /* inserting an address */
4119 *++bp
= ','; /* so add separator */
4123 else{ /* nothing in field yet */
4124 if(*bp
== ','){ /* so blast any extra */
4125 *bp
= '\0'; /* separators */
4131 /* insert new text at the dot position */
4132 buf_len
= ucs4_strlen(buf
);
4133 tentative_p_ind
= ods
.p_ind
+ buf_len
;
4134 work_buf_len
= ucs4_strlen(ods
.cur_l
->text
) + buf_len
;
4135 work_buf
= (UCS
*) malloc((work_buf_len
+ 1) * sizeof(UCS
));
4136 if (work_buf
== NULL
) {
4137 emlwrite("Can't malloc space for saved text", NULL
);
4142 work_buf_begin
= work_buf
;
4143 i
= MIN(ods
.p_ind
, work_buf_len
);
4144 ucs4_strncpy(work_buf
, ods
.cur_l
->text
, i
);
4146 ucs4_strncat(work_buf
, buf
, work_buf_len
+1-ucs4_strlen(work_buf
)-1);
4147 work_buf
[work_buf_len
] = '\0';
4148 ucs4_strncat(work_buf
, &ods
.cur_l
->text
[ods
.p_ind
], work_buf_len
+1-ucs4_strlen(work_buf
)-1);
4149 work_buf
[work_buf_len
] = '\0';
4155 /* insert text in HLSZ character chunks */
4156 while(work_buf_len
+ ods
.p_ind
> HLSZ
) {
4157 ucs4_strncpy(&ods
.cur_l
->text
[ods
.p_ind
], work_buf
, HLSZ
-ods
.p_ind
);
4158 work_buf
+= (HLSZ
- ods
.p_ind
);
4159 work_buf_len
-= (HLSZ
- ods
.p_ind
);
4161 if(FormatLines(ods
.cur_l
, empty
, LINEWID(),
4162 headents
[ods
.cur_e
].break_on_comma
, 0) == -1) {
4170 len
+= ucs4_strlen(travel
->text
);
4175 * This comes after the break above because it will
4176 * be accounted for in the while loop below.
4178 if(!tentative_cur_l
){
4179 if(tentative_p_ind
<= ucs4_strlen(travel
->text
))
4180 tentative_cur_l
= travel
;
4182 tentative_p_ind
-= ucs4_strlen(travel
->text
);
4185 travel
= travel
->next
;
4189 ods
.p_ind
= ucs4_strlen(travel
->text
) - len
+ HLSZ
;
4193 /* insert the remainder of text */
4194 if (i
!= FALSE
&& work_buf_len
> 0) {
4195 ucs4_strncpy(&ods
.cur_l
->text
[ods
.p_ind
], work_buf
, HLSZ
-ods
.p_ind
);
4196 ods
.cur_l
->text
[HLSZ
-1] = '\0';
4197 work_buf
= work_buf_begin
;
4200 if(FormatLines(ods
.cur_l
, empty
, LINEWID(),
4201 headents
[ods
.cur_e
].break_on_comma
, 0) == -1) {
4206 while (len
< work_buf_len
+ ods
.p_ind
){
4207 if(!tentative_cur_l
){
4208 if(tentative_p_ind
<= ucs4_strlen(travel
->text
))
4209 tentative_cur_l
= travel
;
4211 tentative_p_ind
-= ucs4_strlen(travel
->text
);
4214 len
+= ucs4_strlen(travel
->text
);
4215 if (len
>= work_buf_len
+ ods
.p_ind
)
4218 travel
= travel
->next
;
4222 ods
.p_ind
= ucs4_strlen(travel
->text
) - len
+ work_buf_len
+ ods
.p_ind
;
4224 && tentative_p_ind
>= 0
4225 && tentative_p_ind
<= ucs4_strlen(tentative_cur_l
->text
)){
4226 ods
.cur_l
= tentative_cur_l
;
4227 ods
.p_ind
= tentative_p_ind
;
4239 * break_point - Break the given line at the most reasonable character breakch
4240 * within maxwid max characters.
4243 * Pointer to the best break point in s, or
4244 * Pointer to the beginning of s if no break point found
4247 break_point(UCS
*line
, int maxwid
, UCS breakch
, int *quotedarg
)
4249 UCS
*bp
; /* break point */
4253 * Start at maxwid and work back until first opportunity to break.
4255 bp
= ucs4_particular_width(line
, maxwid
);
4258 * Quoted should be set up for the start of line. Since we want
4259 * to move to bp and work our way back we need to scan through the
4260 * line up to bp setting quoted appropriately.
4263 ucs4_strqchr(line
, '\0', quotedarg
, bp
-line
);
4265 quoted
= quotedarg
? *quotedarg
: 0;
4268 if(breakch
== ',' && *bp
== '"') /* don't break on quoted ',' */
4269 quoted
= !quoted
; /* toggle quoted state */
4271 if(*bp
== breakch
&& !quoted
){
4273 if(ucs4_str_width_ptr_to_ptr(line
, bp
+1) < maxwid
){
4274 bp
++; /* leave the ' ' */
4280 * if break char isn't a space, leave a space after
4283 if(!(ucs4_str_width_ptr_to_ptr(line
, bp
+1) >= maxwid
4284 || (bp
[1] == ' ' && ucs4_str_width_ptr_to_ptr(line
, bp
+2) >= maxwid
))){
4285 bp
+= (bp
[1] == ' ') ? 2 : 1;
4295 *quotedarg
= quoted
;
4297 return((quoted
) ? line
: bp
);
4304 * hldelete() - remove the header line pointed to by l from the linked list
4308 * the case of first line in field is kind of bogus. since
4309 * the array of headers has a pointer to the first line, and
4310 * i don't want to worry about this too much, i just copied
4311 * the line below and removed it rather than the first one
4319 hldelete(struct hdr_line
*l
)
4321 register struct hdr_line
*lp
;
4326 if(l
->next
== NULL
&& l
->prev
== NULL
){ /* only one line in field */
4328 return(TRUE
); /* no free only line in list */
4330 else if(l
->next
== NULL
){ /* last line in field */
4331 l
->prev
->next
= NULL
;
4333 else if(l
->prev
== NULL
){ /* first line in field */
4334 ucs4_strncpy(l
->text
, l
->next
->text
, HLSZ
);
4335 l
->text
[HLSZ
-1] = '\0';
4337 if((l
->next
= lp
->next
) != NULL
)
4341 else{ /* some where in field */
4342 l
->prev
->next
= l
->next
;
4343 l
->next
->prev
= l
->prev
;
4355 * is_blank - returns true if the next n chars from coordinates row, col
4356 * on display are spaces
4359 is_blank(int row
, int col
, int n
)
4362 for( ;col
< n
; col
++){
4363 if(pscr(row
, col
) == NULL
|| pscr(row
, col
)->c
!= ' ')
4371 * ShowPrompt - display key help corresponding to the current header entry
4376 if(headents
[ods
.cur_e
].key_label
){
4377 menu_header
[TO_KEY
].name
= "^T";
4378 menu_header
[TO_KEY
].label
= headents
[ods
.cur_e
].key_label
;
4379 KS_OSDATASET(&menu_header
[TO_KEY
], KS_OSDATAGET(&headents
[ods
.cur_e
]));
4382 menu_header
[TO_KEY
].name
= NULL
;
4384 if(Pmaster
&& Pmaster
->exit_label
)
4385 menu_header
[SEND_KEY
].label
= Pmaster
->exit_label
;
4386 else if(gmode
& (MDVIEW
| MDHDRONLY
))
4387 menu_header
[SEND_KEY
].label
= (gmode
& MDHDRONLY
) ? "eXit/Save" : "eXit";
4389 menu_header
[SEND_KEY
].label
= N_("Send");
4392 menu_header
[CUT_KEY
].name
= NULL
;
4393 menu_header
[DEL_KEY
].name
= NULL
;
4394 menu_header
[UDEL_KEY
].name
= NULL
;
4397 menu_header
[CUT_KEY
].name
= "^K";
4398 menu_header
[DEL_KEY
].name
= "^D";
4399 menu_header
[UDEL_KEY
].name
= "^U";
4402 if(Pmaster
->ctrlr_label
){
4403 menu_header
[RICH_KEY
].label
= Pmaster
->ctrlr_label
;
4404 menu_header
[RICH_KEY
].name
= "^R";
4406 else if(gmode
& MDHDRONLY
){
4407 menu_header
[RICH_KEY
].name
= NULL
;
4410 menu_header
[RICH_KEY
].label
= N_("Rich Hdr");
4411 menu_header
[RICH_KEY
].name
= "^R";
4414 if(gmode
& MDHDRONLY
){
4415 if(headents
[ods
.cur_e
].fileedit
){
4416 menu_header
[PONE_KEY
].name
= "^_";
4417 menu_header
[PONE_KEY
].label
= N_("Edit File");
4420 menu_header
[PONE_KEY
].name
= NULL
;
4422 menu_header
[ATT_KEY
].name
= NULL
;
4425 menu_header
[PONE_KEY
].name
= "^O";
4426 menu_header
[PONE_KEY
].label
= N_("Postpone");
4428 menu_header
[ATT_KEY
].name
= "^J";
4431 wkeyhelp(menu_header
);
4436 * packheader - packup all of the header fields for return to caller.
4437 * NOTE: all of the header info passed in, including address
4438 * of the pointer to each string is contained in the
4439 * header entry array "headents".
4444 register int i
= 0; /* array index */
4445 register int count
; /* count of chars in a field */
4446 register int retval
= TRUE
;
4447 register char *bufp
;
4448 register struct hdr_line
*line
;
4454 while(headents
[i
].name
!= NULL
){
4457 * attachments are special case, already in struct we pass back
4459 if(headents
[i
].is_attach
){
4465 if(headents
[i
].blank
){
4471 * count chars to see if we need a new malloc'd space for our
4474 line
= headents
[i
].hd_text
;
4476 while(line
!= NULL
){
4478 * add one for possible concatination of a ' ' character ...
4480 p
= ucs4_to_utf8_cpystr(line
->text
);
4483 if(p
[0] && p
[strlen(p
)-1] == ',')
4486 fs_give((void **) &p
);
4492 line
= headents
[i
].hd_text
;
4493 if(count
<= headents
[i
].maxlen
){
4494 *headents
[i
].realaddr
[0] = '\0';
4498 * don't forget to include space for the null terminator!!!!
4500 if((bufp
= (char *)malloc((count
+1) * sizeof(char))) != NULL
){
4503 free(*headents
[i
].realaddr
);
4504 *headents
[i
].realaddr
= bufp
;
4505 headents
[i
].maxlen
= count
;
4508 emlwrite("Can't make room to pack header field.", NULL
);
4513 if(retval
!= FALSE
){
4514 int saw_current_line
= 0;
4516 while(line
!= NULL
){
4518 /* pass the cursor offset back in Pmaster struct */
4519 if(headents
[i
].start_here
&& Pmaster
&& !saw_current_line
){
4520 if(ods
.cur_l
== line
)
4523 Pmaster
->edit_offset
+= ucs4_strlen(line
->text
);
4526 p
= ucs4_to_utf8_cpystr(line
->text
);
4528 strncat(*headents
[i
].realaddr
, p
, headents
[i
].maxlen
+1-strlen(*headents
[i
].realaddr
)-1);
4529 (*headents
[i
].realaddr
)[headents
[i
].maxlen
] = '\0';
4531 if(p
[0] && p
[strlen(p
)-1] == ','){
4532 strncat(*headents
[i
].realaddr
, " ", headents
[i
].maxlen
+1-strlen(*headents
[i
].realaddr
)-1);
4533 (*headents
[i
].realaddr
)[headents
[i
].maxlen
] = '\0';
4536 fs_give((void **) &p
);
4552 * zotheader - free all malloc'd lines associated with the header structs
4557 register struct headerentry
*i
;
4559 for(i
= headents
; headents
&& i
->name
; i
++)
4560 zotentry(i
->hd_text
);
4565 * zotentry - free malloc'd space associated with the given linked list
4568 zotentry(struct hdr_line
*l
)
4570 register struct hdr_line
*ld
, *lf
= l
;
4572 while((ld
= lf
) != NULL
){
4574 ld
->next
= ld
->prev
= NULL
;
4582 * zotcomma - blast any trailing commas and white space from the end
4591 p
= &s
[ucs4_strlen(s
)];
4608 * Save the current state of global variables so that we can restore
4609 * them later. This is so we can call pico again.
4610 * Also have to initialize some variables that normally would be set to
4614 save_pico_state(void)
4620 extern VIDEO
**vscreen
;
4621 extern VIDEO
**pscreen
;
4622 extern int pico_all_done
;
4623 extern jmp_buf finstate
;
4624 extern UCS
*pico_anchor
;
4626 if((ret
= (VARS_TO_SAVE
*)malloc(sizeof(VARS_TO_SAVE
))) == NULL
)
4631 ret
->lbound
= lbound
;
4632 ret
->vscreen
= vscreen
;
4633 ret
->pscreen
= pscreen
;
4635 ret
->delim_ps
= delim_ps
;
4636 ret
->invert_ps
= invert_ps
;
4637 ret
->pico_all_done
= pico_all_done
;
4638 memcpy(ret
->finstate
, finstate
, sizeof(jmp_buf));
4639 ret
->pico_anchor
= pico_anchor
;
4640 ret
->Pmaster
= Pmaster
;
4641 ret
->fillcol
= fillcol
;
4642 if((ret
->pat
= (UCS
*)malloc(sizeof(UCS
) * (ucs4_strlen(pat
)+1))) != NULL
)
4643 ucs4_strncpy(ret
->pat
, pat
, ucs4_strlen(pat
)+1);
4645 ret
->ComposerTopLine
= ComposerTopLine
;
4646 ret
->ComposerEditing
= ComposerEditing
;
4648 ret
->alt_speller
= alt_speller
;
4649 ret
->quote_str
= glo_quote_str
;
4650 ret
->wordseps
= glo_wordseps
;
4651 ret
->currow
= currow
;
4652 ret
->curcol
= curcol
;
4653 ret
->thisflag
= thisflag
;
4654 ret
->lastflag
= lastflag
;
4655 ret
->curgoal
= curgoal
;
4656 ret
->opertree
= (char *) malloc(sizeof(char) * (strlen(opertree
) + 1));
4657 if(ret
->opertree
!= NULL
)
4658 strncpy(ret
->opertree
, opertree
, strlen(opertree
)+1);
4661 ret
->wheadp
= wheadp
;
4663 ret
->bheadp
= bheadp
;
4664 ret
->km_popped
= km_popped
;
4665 ret
->mrow
= term
.t_mrow
;
4667 /* Initialize for next pico call */
4678 restore_pico_state(VARS_TO_SAVE
*state
)
4683 extern VIDEO
**vscreen
;
4684 extern VIDEO
**pscreen
;
4685 extern int pico_all_done
;
4686 extern jmp_buf finstate
;
4687 extern UCS
*pico_anchor
;
4690 vtrow
= state
->vtrow
;
4691 vtcol
= state
->vtcol
;
4692 lbound
= state
->lbound
;
4693 vscreen
= state
->vscreen
;
4694 pscreen
= state
->pscreen
;
4696 delim_ps
= state
->delim_ps
;
4697 invert_ps
= state
->invert_ps
;
4698 pico_all_done
= state
->pico_all_done
;
4699 memcpy(finstate
, state
->finstate
, sizeof(jmp_buf));
4700 pico_anchor
= state
->pico_anchor
;
4701 Pmaster
= state
->Pmaster
;
4703 headents
= Pmaster
->headents
;
4705 fillcol
= state
->fillcol
;
4707 ucs4_strncpy(pat
, state
->pat
, NPAT
);
4709 ComposerTopLine
= state
->ComposerTopLine
;
4710 ComposerEditing
= state
->ComposerEditing
;
4711 gmode
= state
->gmode
;
4712 alt_speller
= state
->alt_speller
;
4713 glo_quote_str
= state
->quote_str
;
4714 glo_wordseps
= state
->wordseps
;
4715 currow
= state
->currow
;
4716 curcol
= state
->curcol
;
4717 thisflag
= state
->thisflag
;
4718 lastflag
= state
->lastflag
;
4719 curgoal
= state
->curgoal
;
4720 if(state
->opertree
){
4721 strncpy(opertree
, state
->opertree
, sizeof(opertree
));
4722 opertree
[sizeof(opertree
)-1] = '\0';
4725 curwp
= state
->curwp
;
4726 wheadp
= state
->wheadp
;
4727 curbp
= state
->curbp
;
4728 bheadp
= state
->bheadp
;
4729 km_popped
= state
->km_popped
;
4730 term
.t_mrow
= state
->mrow
;
4735 free_pico_state(VARS_TO_SAVE
*state
)
4741 free(state
->opertree
);
4748 * Ok to call this twice in a row because it won't do anything the second
4752 fix_mangle_and_err(int *mangled
, char **errmsg
, char *name
)
4754 if(mangled
&& *mangled
){
4761 if(errmsg
&& *errmsg
){
4765 snprintf(err
, sizeof(err
), "%s field: %s", name
, *errmsg
);
4767 emlwrite(err
, NULL
);
4780 * This is so that the To line will be appended to by an Lcc
4781 * entry unless the user types in the To line after the Lcc
4782 * has already been set.
4785 mark_sticky(struct headerentry
*h
)
4787 if(h
&& (!h
->sticky_special
|| h
->bldr_private
))
4796 * Wraper function for the real header editor.
4797 * Does the important tasks of:
4798 * 1) verifying that we _can_ edit the headers.
4799 * 2) acting on the result code from the header editor.
4802 HeaderEditor(int f
, int n
)
4808 /* Sometimes we get here from a scroll callback, which
4809 * is no good at all because mswin is not ready to process input and
4810 * this _headeredit() will never do anything.
4811 * Putting this test here was the most general solution I could think
4813 if (!mswin_caninput())
4817 retval
= HeaderEditorWork(f
, n
);
4819 retval
= mousepress(0,0);