1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: composer.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
3 #endif
4
5 /*
6 * ========================================================================
7 * Copyright 2006-2009 University of Washington
8 * Copyright 2013-2021 Eduardo Chappa
9 *
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
13 *
14 * http://www.apache.org/licenses/LICENSE-2.0
15 *
16 * ========================================================================
17 *
18 * Program: Pine composer routines
19 *
20 * NOTES:
21 *
22 * - composer.c is the composer for the PINE mail system
23 *
24 * - tabled 01/19/90
25 *
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 ....
30 *
31 * - untabled 01/30/90
32 *
33 * Notes: Installed header line editing, with wrapping and unwrapping,
34 * context sensitive help, and other mail header editing features.
35 *
36 * - finalish code cleanup 07/15/91
37 *
38 * Notes: Killed/yanked header lines use emacs kill buffer.
39 * Arbitrarily large headers are handled gracefully.
40 * All formatting handled by FormatLines.
41 *
42 * - Work done to optimize display painting 06/26/92
43 * Not as clean as it should be, needs more thought
44 *
45 */
46 #include "headers.h"
47
48 #include "osdep/terminal.h"
49 #include "../pith/string.h"
50
51 int InitEntryText(char *, struct headerentry *);
52 int HeaderOffset(int);
53 int HeaderFocus(int, int);
54 UCS LineEdit(int, UCS *);
55 int header_downline(int, int);
56 int header_upline(int);
57 int FormatLines(struct hdr_line *, char *, int, int, int);
58 int FormatSyncAttach(void);
59 UCS *ucs4_strqchr(UCS *, UCS, int *, int);
60 int ComposerHelp(int);
61 void NewTop(int);
62 void display_delimiter(int);
63 void zotentry(struct hdr_line *);
64 int InvertPrompt(int, int);
65 int partial_entries(void);
66 int physical_line(struct hdr_line *);
67 int strend(UCS *, UCS);
68 int KillHeaderLine(struct hdr_line *, int);
69 int SaveHeaderLines(void);
70 UCS *break_point(UCS *, int, UCS, int *);
71 int hldelete(struct hdr_line *);
72 int is_blank(int, int, int);
73 int zotcomma(UCS *);
74 struct hdr_line *first_hline(int *);
75 struct hdr_line *first_sel_hline(int *);
76 struct hdr_line *next_hline(int *, struct hdr_line *);
77 struct hdr_line *next_sel_hline(int *, struct hdr_line *);
78 struct hdr_line *prev_hline(int *, struct hdr_line *);
79 struct hdr_line *prev_sel_hline(int *, struct hdr_line *);
80 struct hdr_line *first_requested_hline(int *);
81 void fix_mangle_and_err(int *, char **, char *);
82 void mark_sticky(struct headerentry *);
83
84
85 /*
86 * definition header field array, structures defined in pico.h
87 */
88 struct headerentry *headents;
89
90
91 /*
92 * structure that keeps track of the range of header lines that are
93 * to be displayed and other fun stuff about the header
94 */
95 struct on_display ods; /* global on_display struct */
96
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
99 */
100
101 struct hdr_line *subject_line = NULL;
102
103 /*
104 * useful macros
105 */
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)
111
112 #ifdef MOUSE
113 /*
114 * Redefine HeaderEditor to install wrapper required for mouse event
115 * handling...
116 */
117 #define HeaderEditor HeaderEditorWork
118 #endif /* MOUSE */
119
120 #define HDR_DELIM "----- Message Text -----"
121
122 /*
123 * useful declarations
124 */
125 static short delim_ps = 0; /* previous state */
126 static short invert_ps = 0; /* previous state */
127
128
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}
145 };
146 #define SEND_KEY 1
147 #define RICH_KEY 2
148 #define CUT_KEY 4
149 #define PONE_KEY 5
150 #define DEL_KEY 7
151 #define ATT_KEY 8
152 #define UDEL_KEY 10
153 #define TO_KEY 11
154
155
156 /*
157 * function key mappings for header editor
158 */
159 static UCS ckm[12][2] = {
160 { F1, (CTRL|'G')},
161 { F2, (CTRL|'C')},
162 { F3, (CTRL|'X')},
163 { F4, (CTRL|'D')},
164 { F5, (CTRL|'R')},
165 { F6, (CTRL|'J')},
166 { F7, 0 },
167 { F8, 0 },
168 { F9, (CTRL|'K')},
169 { F10, (CTRL|'U')},
170 { F11, (CTRL|'O')},
171 { F12, (CTRL|'T')}
172 };
173
174
175 /*
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.
181 *
182 * Returns: TRUE if special header handling was requested,
183 * FALSE under standard default behavior.
184 */
185 int
InitMailHeader(PICO * mp)186 InitMailHeader(PICO *mp)
187 {
188 char *addrbuf;
189 struct headerentry *he;
190 int rv;
191
192 if(!mp->headents){
193 headents = NULL;
194 return(FALSE);
195 }
196
197 /*
198 * initialize some of on_display structure, others below...
199 */
200 ods.p_ind = 0;
201 ods.p_line = COMPOSER_TOP_LINE;
202 ods.top_l = ods.cur_l = NULL;
203
204 headents = mp->headents;
205 /*--- initialize the fields in the headerent structure ----*/
206 for(he = headents; he->name != NULL; he++){
207 he->hd_text = NULL;
208 he->display_it = he->display_it ? he->display_it : !he->rich_header;
209 if(he->is_attach) {
210 /*--- A lot of work to do since attachments are special ---*/
211 he->maxlen = 0;
212 if(mp->attachments != NULL){
213 char buf[NLINE];
214 int attno = 0;
215 int l1, ofp, ofp1, ofp2; /* OverFlowProtection */
216 size_t addrbuflen = 4 * NLINE; /* malloc()ed size of addrbuf */
217 PATMT *ap = mp->attachments;
218
219 ofp = NLINE - 35;
220
221 addrbuf = (char *)malloc(addrbuflen);
222 addrbuf[0] = '\0';
223 buf[0] = '\0';
224 while(ap){
225 if((l1 = strlen(ap->filename)) <= ofp){
226 ofp1 = l1;
227 ofp2 = ofp - l1;
228 }
229 else{
230 ofp1 = ofp;
231 ofp2 = 0;
232 }
233
234 if(ap->filename){
235 snprintf(buf, sizeof(buf), "%d. %.*s%s %s%s%s\"",
236 ++attno,
237 ofp1,
238 ap->filename,
239 (l1 > ofp) ? "...]" : "",
240 ap->size ? "(" : "",
241 ap->size ? ap->size : "",
242 ap->size ? ") " : "");
243
244 /* append description, escaping quotes */
245 if(ap->description){
246 char *dp = ap->description, *bufp = &buf[strlen(buf)];
247 int escaped = 0;
248
249 do
250 if(*dp == '\"' && !escaped){
251 *bufp++ = '\\';
252 ofp2--;
253 }
254 else if(escaped){
255 *bufp++ = '\\';
256 escaped = 0;
257 }
258 while(--ofp2 > 0 && (*bufp++ = *dp++));
259
260 *bufp = '\0';
261 }
262
263 snprintf(buf + strlen(buf), sizeof(buf)-strlen(buf), "\"%s", ap->next ? "," : "");
264
265 if(strlen(addrbuf) + strlen(buf) >= addrbuflen){
266 addrbuflen += NLINE * 4;
267 if(!(addrbuf = (char *)realloc(addrbuf, addrbuflen))){
268 emlwwrite(_("Can't realloc addrbuf to %d bytes"),
269 (void *) addrbuflen);
270 return(ABORT);
271 }
272 }
273
274 strncat(addrbuf, buf, addrbuflen-strlen(addrbuf)-1);
275 addrbuf[addrbuflen-1] = '\0';
276 }
277 ap = ap->next;
278 }
279 InitEntryText(addrbuf, he);
280 free((char *)addrbuf);
281 } else {
282 InitEntryText("", he);
283 }
284 he->realaddr = NULL;
285 } else {
286 if(!he->blank)
287 addrbuf = *(he->realaddr);
288 else
289 addrbuf = "";
290
291 InitEntryText(addrbuf, he);
292 }
293 }
294
295 /*
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.
299 */
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);
305
306 rv = TRUE;
307 }
308 else{
309 ods.top_l = first_hline(&ods.cur_e);
310 ods.cur_l = first_sel_hline(&ods.cur_e);
311 ods.top_e = ods.cur_e;
312 rv = 0;
313 }
314
315 UpdateHeader(0);
316 return(rv);
317 }
318
319
320
321 /*
322 * InitEntryText - Add the given header text into the header entry
323 * line structure.
324 */
325 int
InitEntryText(char * utf8text,struct headerentry * e)326 InitEntryText(char *utf8text, struct headerentry *e)
327 {
328 struct hdr_line *curline;
329 register int longest;
330
331 /*
332 * get first chunk of memory, and tie it to structure...
333 */
334 if((curline = HALLOC()) == NULL){
335 emlwrite("Unable to make room for full Header.", NULL);
336 return(FALSE);
337 }
338
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 */
344
345 if(FormatLines(curline, utf8text, longest, e->break_on_comma, 0) == -1)
346 return(FALSE);
347 else
348 return(TRUE);
349 }
350
351
352
353 /*
354 * ResizeHeader - Handle resizing display when SIGWINCH received.
355 *
356 * notes:
357 * works OK, but needs thorough testing
358 *
359 */
360 int
ResizeHeader(void)361 ResizeHeader(void)
362 {
363 register struct headerentry *i;
364 register int offset;
365
366 if(!headents)
367 return(TRUE);
368
369 offset = (ComposerEditing) ? HeaderOffset(ods.cur_e) : 0;
370
371 for(i=headents; i->name; i++){ /* format each entry */
372 if(FormatLines(i->hd_text, "", (term.t_ncol - i->prwid),
373 i->break_on_comma, 0) == -1){
374 return(-1);
375 }
376 }
377
378 if(ComposerEditing) /* restart at the top */
379 HeaderFocus(ods.cur_e, offset); /* fix cur_l and p_ind */
380 else {
381 struct hdr_line *l;
382 int e;
383
384 for(i = headents; i->name != NULL; i++); /* Find last line */
385 i--;
386 e = i - headents;
387 l = headents[e].hd_text;
388 if(!headents[e].display_it || headents[e].blank)
389 l = prev_sel_hline(&e, l); /* last selectable line */
390
391 if(!l){
392 e = i - headents;
393 l = headents[e].hd_text;
394 }
395
396 HeaderFocus(e, -1); /* put focus on last line */
397 }
398
399 if(ComposerTopLine != COMPOSER_TOP_LINE)
400 UpdateHeader(0);
401
402 PaintBody(0);
403
404 if(ComposerEditing)
405 movecursor(ods.p_line, ods.p_ind+headents[ods.cur_e].prwid);
406
407 (*term.t_flush)();
408 return(TRUE);
409 }
410
411
412
413 /*
414 * HeaderOffset - return the character offset into the given header
415 */
416 int
HeaderOffset(int h)417 HeaderOffset(int h)
418 {
419 register struct hdr_line *l;
420 int i = 0;
421
422 l = headents[h].hd_text;
423
424 while(l != ods.cur_l){
425 i += ucs4_strlen(l->text);
426 l = l->next;
427 }
428 return(i+ods.p_ind);
429 }
430
431
432
433 /*
434 * HeaderFocus - put the dot at the given offset into the given header
435 */
436 int
HeaderFocus(int h,int offset)437 HeaderFocus(int h, int offset)
438 {
439 register struct hdr_line *l;
440 register int i;
441 int last = 0;
442
443 if(offset == -1) /* focus on last line */
444 last = 1;
445
446 l = headents[h].hd_text;
447 while(1){
448 if(last && l->next == NULL){
449 break;
450 }
451 else{
452 if((i=ucs4_strlen(l->text)) >= offset)
453 break;
454 else
455 offset -= i;
456 }
457 if((l = l->next) == NULL)
458 return(FALSE);
459 }
460
461 ods.cur_l = l;
462 ods.p_len = ucs4_strlen(l->text);
463 ods.p_ind = (last) ? 0 : offset;
464
465 return(TRUE);
466 }
467
468
469
470 /*
471 * HeaderEditor() - edit the mail header field by field, trapping
472 * important key sequences, hand the hard work off
473 * to LineEdit().
474 * returns:
475 * -3 if we drop out bottom *and* want to process mouse event
476 * -1 if we drop out the bottom
477 * FALSE if editing is cancelled
478 * TRUE if editing is finished
479 */
480 int
HeaderEditor(int f,int n)481 HeaderEditor(int f, int n)
482 {
483 register int i;
484 UCS ch = 0, lastch;
485 register char *bufp;
486 struct headerentry *h;
487 int cur_e, mangled, retval = -1,
488 hdr_only = (gmode & MDHDRONLY) ? 1 : 0;
489 char *errmss, *err;
490 #ifdef MOUSE
491 MOUSEPRESS mp;
492 #endif
493
494 if(!headents)
495 return(TRUE); /* nothing to edit! */
496
497 ComposerEditing = TRUE;
498 display_delimiter(0); /* provide feedback */
499
500 #ifdef _WINDOWS
501 mswin_setscrollrange (0, 0);
502 #endif /* _WINDOWS */
503
504 if(Pmaster)
505 for (subject_line = NULL, i=0; headents[i].name; i++)
506 if(strcmp(headents[i].name, "Subject") == 0)
507 subject_line = headents[i].hd_text;
508
509 /*
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.
514 *
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.
521 */
522 if(f){
523 if(f == 2){ /* 2 leaves cursor at end */
524 struct hdr_line *l = ods.cur_l;
525 int e = ods.cur_e;
526
527 /*--- make sure on last field ---*/
528 while((l = next_sel_hline(&e, l)) != NULL)
529 if(headents[ods.cur_e].display_it){
530 ods.cur_l = l;
531 ods.cur_e = e;
532 }
533
534 ods.p_ind = 1000; /* and make sure at EOL */
535 }
536 else{
537 /*
538 * note: assumes that ods.cur_e and ods.cur_l haven't changed
539 * since we left...
540 */
541
542 /* fix position */
543 if(curwp->w_doto < headents[ods.cur_e].prwid)
544 ods.p_ind = 0;
545 else if(curwp->w_doto < ods.p_ind + headents[ods.cur_e].prwid)
546 ods.p_ind = curwp->w_doto - headents[ods.cur_e].prwid;
547 else
548 ods.p_ind = 1000;
549
550 /* and scroll back if needed */
551 if(f == 3)
552 for(i = 0; header_upline(0) && i <= FULL_SCR(); i++)
553 ;
554 }
555
556 ods.p_line = ComposerTopLine - 2;
557 }
558 /* else just trust what ods contains */
559
560 InvertPrompt(ods.cur_e, TRUE); /* highlight header field */
561 sgarbk = 1;
562
563 do{
564 lastch = ch;
565 if(km_popped){
566 km_popped--;
567 if(km_popped == 0)
568 sgarbk = 1;
569 }
570
571 if(sgarbk){
572 if(km_popped){ /* temporarily change to cause menu to paint */
573 term.t_mrow = 2;
574 curwp->w_ntrows -= 2;
575 movecursor(term.t_nrow-2, 0); /* clear status line, too */
576 peeol();
577 }
578 else if(term.t_mrow == 0)
579 PaintBody(1);
580
581 ShowPrompt(); /* display correct options */
582 sgarbk = 0;
583 if(km_popped){
584 term.t_mrow = 0;
585 curwp->w_ntrows += 2;
586 }
587 }
588
589 ch = LineEdit(!(gmode&MDVIEW), &lastch); /* work on the current line */
590
591 if(km_popped)
592 switch(ch){
593 case NODATA:
594 case (CTRL|'L'):
595 km_popped++;
596 break;
597
598 default:
599 movecursor(term.t_nrow-2, 0);
600 peeol();
601 movecursor(term.t_nrow-1, 0);
602 peeol();
603 movecursor(term.t_nrow, 0);
604 peeol();
605 break;
606 }
607
608 switch (ch){
609 case (CTRL|'R') : /* Toggle header display */
610 if(Pmaster->pine_flags & MDHDRONLY){
611 if(Pmaster->expander){
612 packheader();
613 call_expander();
614 PaintBody(0);
615 break;
616 }
617 else
618 goto bleep;
619 }
620
621 /*---- Are there any headers to expand above us? ---*/
622 for(h = headents; h != &headents[ods.cur_e]; h++)
623 if(h->rich_header)
624 break;
625 if(h->rich_header)
626 InvertPrompt(ods.cur_e, FALSE); /* Yes, don't leave inverted */
627
628 mangled = 0;
629 err = NULL;
630 if(partial_entries()){
631 /*--- Just turned off all rich headers --*/
632 if(headents[ods.cur_e].rich_header){
633 /*-- current header got turned off too --*/
634 #ifdef ATTACHMENTS
635 if(headents[ods.cur_e].is_attach){
636 /* verify the attachments */
637 if((i = FormatSyncAttach()) != 0){
638 FormatLines(headents[ods.cur_e].hd_text, "",
639 term.t_ncol - headents[ods.cur_e].prwid,
640 headents[ods.cur_e].break_on_comma, 0);
641 }
642 }
643 #endif
644 if(headents[ods.cur_e].builder) /* verify text */
645 i = call_builder(&headents[ods.cur_e], &mangled, &err)>0;
646 /* Check below */
647 for(cur_e =ods.cur_e; headents[cur_e].name!=NULL; cur_e++)
648 if(!headents[cur_e].rich_header)
649 break;
650 if(headents[cur_e].name == NULL) {
651 /* didn't find one, check above */
652 for(cur_e =ods.cur_e; headents[cur_e].name!=NULL;
653 cur_e--)
654 if(!headents[cur_e].rich_header)
655 break;
656
657 }
658 ods.p_ind = 0;
659 ods.cur_e = cur_e;
660 ods.cur_l = headents[ods.cur_e].hd_text;
661 }
662 }
663
664 ods.p_line = 0; /* force update */
665 UpdateHeader(0);
666 PaintHeader(COMPOSER_TOP_LINE, FALSE);
667 PaintBody(1);
668 fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
669 break;
670
671 case (CTRL|'C') : /* bag whole thing ?*/
672 if(abort_composer(1, 0) == TRUE)
673 return(FALSE);
674
675 break;
676
677 case (CTRL|'X') : /* Done. Send it. */
678 i = 0;
679 #ifdef ATTACHMENTS
680 if(headents[ods.cur_e].is_attach){
681 /* verify the attachments, and pretty things up in case
682 * we come back to the composer due to error...
683 */
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);
689 }
690 }
691 else
692 #endif
693 mangled = 0;
694 err = NULL;
695 if(headents[ods.cur_e].builder) /* verify text? */
696 i = call_builder(&headents[ods.cur_e], &mangled, &err);
697
698 if(i < 0){ /* don't leave without a valid addr */
699 fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
700 break;
701 }
702 else if(i > 0){
703 ods.cur_l = headents[ods.cur_e].hd_text; /* attach cur_l */
704 ods.p_ind = 0;
705 ods.p_line = 0; /* force realignment */
706 fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
707 NewTop(0);
708 }
709
710 fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
711
712 if(wquit(1,0) == TRUE)
713 return(TRUE);
714
715 if(i > 0){
716 /*
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).
720 */
721 UpdateHeader(0);
722 PaintHeader(COMPOSER_TOP_LINE, FALSE);
723 PaintBody(1);
724 }
725 break;
726
727 case (CTRL|'Z') : /* Suspend compose */
728 if(gmode&MDSSPD){ /* is it allowed? */
729 bktoshell(0, 1);
730 PaintBody(0);
731 }
732 else
733 unknown_command(ch);
734
735 break;
736
737 case (CTRL|'\\') :
738 #if defined MOUSE && !defined(_WINDOWS)
739 toggle_xterm_mouse(0,1);
740 #else
741 unknown_command(ch);
742 #endif
743 break;
744
745 case (CTRL|'O') : /* Suspend message */
746 if(Pmaster->pine_flags & MDHDRONLY)
747 goto bleep;
748
749 i = 0;
750 mangled = 0;
751 err = NULL;
752 if(headents[ods.cur_e].is_attach){
753 if(FormatSyncAttach() < 0){
754 if(mlyesno_utf8(_("Problem with attachments. Postpone anyway?"),
755 FALSE) != TRUE){
756 if(FormatLines(headents[ods.cur_e].hd_text, "",
757 term.t_ncol - headents[ods.cur_e].prwid,
758 headents[ods.cur_e].break_on_comma, 0) == -1)
759 emlwwrite(_("Format lines failed!"), NULL);
760 UpdateHeader(0);
761 PaintHeader(COMPOSER_TOP_LINE, FALSE);
762 PaintBody(1);
763 continue;
764 }
765 }
766 }
767 else if(headents[ods.cur_e].builder)
768 i = call_builder(&headents[ods.cur_e], &mangled, &err);
769
770 fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
771
772 if(i < 0) /* don't leave without a valid addr */
773 break;
774
775 suspend_composer(1, 0);
776 return(TRUE);
777
778 #ifdef ATTACHMENTS
779 case (CTRL|'J') : /* handle attachments */
780 if(Pmaster->pine_flags & MDHDRONLY)
781 goto bleep;
782
783 { char cmt[NLINE];
784 LMLIST *lm = NULL, *lmp;
785 char buf[NLINE], *bfp;
786 int saved_km_popped;
787 size_t len;
788
789 /*
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.
795 */
796 saved_km_popped = km_popped;
797 km_popped = 0;
798
799 if(AskAttach(cmt, sizeof(cmt), &lm)){
800
801 for(lmp = lm; lmp; lmp = lmp->next){
802 size_t space;
803
804 len = lmp->dir ? strlen(lmp->dir)+1 : 0;
805 len += lmp->fname ? strlen(lmp->fname) : 0;
806
807 if(len+3 > sizeof(buf)){
808 space = len+3;
809 bfp = malloc(space*sizeof(char));
810 if(bfp == NULL){
811 emlwwrite(_("Can't malloc space for filename"),
812 NULL);
813 continue;
814 }
815 }
816 else{
817 bfp = buf;
818 space = sizeof(buf);
819 }
820
821 if(lmp->dir && lmp->dir[0])
822 snprintf(bfp, space, "%s%c%s", lmp->dir, C_FILESEP,
823 lmp->fname ? lmp->fname : "");
824 else
825 snprintf(bfp, space, "%s", lmp->fname ? lmp->fname : "");
826
827 (void) QuoteAttach(bfp, space);
828
829 (void) AppendAttachment(bfp, lm->size, cmt);
830
831 if(bfp != buf)
832 free(bfp);
833 }
834
835 zotlmlist(lm);
836 }
837
838 km_popped = saved_km_popped;
839 sgarbk = 1; /* clean up prompt */
840 }
841 break;
842 #endif
843
844 case (CTRL|'I') : /* tab */
845 if(headents[ods.cur_e].nickcmpl != NULL){
846 char *new_nickname = NULL;
847 UCS *strng;
848 UCS *start = NULL, *end = NULL, *before_start = NULL;
849 UCS *uprefix = NULL, *up1, *up2;
850 char *prefix = NULL, *saveprefix = NULL, *insert = NULL;
851 char *ambig = NULL;
852 int offset, prefixlen, add_a_comma = 0;
853 size_t l, l1, l2;
854 int ambiguity, fallthru = 1;
855
856 strng = ods.cur_l->text;
857 offset = HeaderOffset(ods.cur_e);
858
859 if(ods.p_ind > 0
860 && (start = &strng[ods.p_ind-1])
861 && (!*(start+1)
862 || ucs4_isspace(*(start+1))
863 || *(start+1) == ',')
864 && (*start
865 && !ucs4_isspace(*start)
866 && *start != ',')){
867
868 end = start+1;
869
870 while(start > strng
871 && *(start-1)
872 && *(start-1) != ',')
873 start--;
874
875 while(ucs4_isspace(*start))
876 start++;
877
878 if(*end != ',' && ods.cur_l->next)
879 add_a_comma++;
880
881 /*
882 * Nickname prefix begins with start and ends
883 * with end-1. Replace those characters with
884 * completed nickname.
885 */
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);
892
893 /*
894 * Ambiguity == 0 means no matches exist.
895 * 1 means it is ambiguous and the longest
896 * unambiguous prefix beginning with prefix
897 * is in new_nickname.
898 * 2 means it is unambiguous and the answer
899 * is in new_nickname
900 *
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.
906 *
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.
914 */
915 ambiguity = (*(headents[ods.cur_e].nickcmpl))(prefix,
916 &new_nickname, (lastch == ch), 0);
917
918 /*
919 * Don't fall through on no-match TAB unless
920 * it is the second TAB.
921 */
922 if(ambiguity == 0 && lastch != ch){
923 lastch = 0;
924 break;
925 }
926
927 if(ambiguity != 1)
928 lastch = 0;
929
930 if(ambiguity == 0)
931 goto nomore_to_complete;
932
933 if(new_nickname){
934 if(strcmp(new_nickname, prefix)){
935 /*
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.
941 */
942
943 /* save part before start */
944 fallthru = 0;
945 before_start = strng;
946 uprefix = (UCS *) fs_get((start-before_start+1) * sizeof(UCS));
947 ucs4_strncpy(uprefix, before_start, start-before_start);
948 uprefix[start-before_start] = '\0';
949 saveprefix = ucs4_to_utf8_cpystr(uprefix);
950
951 /* figure out new cursor offset */
952 up1 = utf8_to_ucs4_cpystr(new_nickname);
953 if(up1){
954 offset += (ucs4_strlen(up1) - prefixlen);
955 fs_give((void **) &up1);
956 }
957
958 fs_give((void **) &uprefix);
959
960 /*
961 * Delete everything up to end by
962 * copying characters to start of buffer.
963 */
964 up1 = before_start;
965 up2 = end;
966 for(i = ods.p_len - (end - before_start) + 1; i > 0; i--)
967 *up1++ = *up2++;
968
969 ods.p_len -= (end - before_start);
970
971 if(saveprefix){
972 l1 = strlen(saveprefix);
973 l2 = strlen(new_nickname);
974 l = l1 + l2;
975
976 /* add a comma? */
977 if(add_a_comma && ambiguity == 2){
978 l++;
979 offset++;
980 }
981 else
982 add_a_comma = 0;
983
984 insert = (char *) fs_get((l+1) * sizeof(char));
985
986 /*
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.
990 */
991 if(insert){
992 strncpy(insert, saveprefix, l);
993 strncpy(insert+l1, new_nickname, l-l1);
994 if(add_a_comma)
995 insert[l-1] = ',';
996
997 insert[l] = '\0';
998 }
999
1000 fs_give((void **) &saveprefix);
1001 }
1002
1003
1004 if(insert && FormatLines(ods.cur_l, insert,
1005 term.t_ncol - headents[ods.cur_e].prwid,
1006 headents[ods.cur_e].break_on_comma,0)==-1){
1007 emlwwrite(_("Format lines failed!"), NULL);
1008 }
1009
1010 if(insert)
1011 fs_give((void **) &insert);
1012
1013 HeaderFocus(ods.cur_e, offset);
1014 }
1015 else{
1016 (*term.t_beep)();
1017 }
1018
1019 ambig = new_nickname;
1020 new_nickname = NULL;
1021 }
1022
1023 if(!ambig && prefix){
1024 ambig = prefix;
1025 prefix = NULL;
1026 }
1027
1028
1029 if(ambiguity == 2 && fallthru){
1030 if(prefix)
1031 fs_give((void **) &prefix);
1032
1033 if(new_nickname)
1034 fs_give((void **) &new_nickname);
1035
1036 if(ambig)
1037 fs_give((void **) &ambig);
1038
1039 UpdateHeader(0);
1040 PaintBody(0);
1041 goto nomore_to_complete;
1042 }
1043
1044 UpdateHeader(0);
1045 PaintBody(0);
1046
1047 if(prefix)
1048 fs_give((void **) &prefix);
1049
1050 if(new_nickname)
1051 fs_give((void **) &new_nickname);
1052
1053 if(ambig)
1054 fs_give((void **) &ambig);
1055 }
1056 else{
1057 goto nomore_to_complete;
1058 }
1059
1060 break;
1061 }
1062 else{
1063 nomore_to_complete:
1064 ods.p_ind = 0; /* fall through... */
1065 }
1066
1067 case (CTRL|'N') :
1068 case KEY_DOWN :
1069 header_downline(!hdr_only, hdr_only);
1070 break;
1071
1072 case (CTRL|'P') :
1073 case KEY_UP :
1074 header_upline(1);
1075 break;
1076
1077 case (CTRL|'V') : /* down a page */
1078 case KEY_PGDN :
1079 cur_e = ods.cur_e;
1080 if(!next_sel_hline(&cur_e, ods.cur_l)){
1081 header_downline(!hdr_only, hdr_only);
1082 if(!(gmode & MDHDRONLY))
1083 retval = -1; /* tell caller we fell out */
1084 }
1085 else{
1086 int move_down, bot_pline;
1087 struct hdr_line *new_cur_l, *line, *next_line, *prev_line;
1088
1089 move_down = BOTTOM() - 2 - ods.p_line;
1090 if(move_down < 0)
1091 move_down = 0;
1092
1093 /*
1094 * Count down move_down lines to find the pointer to the line
1095 * that we want to become the current line.
1096 */
1097 new_cur_l = ods.cur_l;
1098 cur_e = ods.cur_e;
1099 for(i = 0; i < move_down; i++){
1100 next_line = next_hline(&cur_e, new_cur_l);
1101 if(!next_line)
1102 break;
1103
1104 new_cur_l = next_line;
1105 }
1106
1107 if(headents[cur_e].blank){
1108 next_line = next_sel_hline(&cur_e, new_cur_l);
1109 if(!next_line)
1110 break;
1111
1112 new_cur_l = next_line;
1113 }
1114
1115 /*
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.
1120 */
1121 while(ods.cur_l != new_cur_l && header_downline(0, 0))
1122 ;
1123
1124 /*
1125 * Count back up, if we're at the bottom, to find the new
1126 * top line.
1127 */
1128 cur_e = ods.cur_e;
1129 if(next_hline(&cur_e, ods.cur_l) == NULL){
1130 /*
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.
1135 */
1136 cur_e = ods.cur_e;
1137 line = ods.cur_l;
1138 /* leave delimiter on screen, too */
1139 bot_pline = BOTTOM() - 1 - ((gmode & MDHDRONLY) ? 0 : 1);
1140 for(i = COMPOSER_TOP_LINE; i < bot_pline; i++){
1141 prev_line = prev_hline(&cur_e, line);
1142 if(!prev_line)
1143 break;
1144
1145 line = prev_line;
1146 }
1147
1148 ods.top_l = line;
1149 ods.top_e = cur_e;
1150 ods.p_line = i;
1151
1152 }
1153 else{
1154 ods.top_l = ods.cur_l;
1155 ods.top_e = ods.cur_e;
1156 /*
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.
1161 */
1162 bot_pline = BOTTOM() - 1 - ((gmode & MDHDRONLY) ? 0 : 1);
1163 cur_e = ods.cur_e;
1164 line = ods.cur_l;
1165 for(i = bot_pline; i > COMPOSER_TOP_LINE; i--){
1166 next_line = next_hline(&cur_e, line);
1167 if(!next_line)
1168 break;
1169
1170 line = next_line;
1171 }
1172
1173 /*
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.
1177 */
1178 ods.p_line = i;
1179 line = ods.top_l;
1180 cur_e = ods.top_e;
1181 for(; i > COMPOSER_TOP_LINE; i--){
1182 prev_line = prev_hline(&cur_e, line);
1183 if(!prev_line)
1184 break;
1185
1186 line = prev_line;
1187 }
1188
1189 ods.top_l = line;
1190 ods.top_e = cur_e;
1191
1192 /*
1193 * Special case. If p_line is within one of the bottom,
1194 * move it to the bottom.
1195 */
1196 if(ods.p_line == bot_pline - 1){
1197 header_downline(0, 0);
1198 /* but put these back where we want them */
1199 ods.p_line = bot_pline;
1200 ods.top_l = line;
1201 ods.top_e = cur_e;
1202 }
1203 }
1204
1205 UpdateHeader(0);
1206 PaintHeader(COMPOSER_TOP_LINE, FALSE);
1207 PaintBody(1);
1208 }
1209
1210 break;
1211
1212 case (CTRL|'Y') : /* up a page */
1213 case KEY_PGUP :
1214 for(i = 0; header_upline(0) && i <= FULL_SCR(); i++)
1215 if(i < 0)
1216 break;
1217
1218 break;
1219
1220 #ifdef MOUSE
1221 case KEY_MOUSE:
1222 mouse_get_last (NULL, &mp);
1223 switch(mp.button){
1224 case M_BUTTON_LEFT :
1225 if (!mp.doubleclick) {
1226 if (mp.row < ods.p_line) {
1227 for (i = ods.p_line - mp.row;
1228 i > 0 && header_upline(0);
1229 --i)
1230 ;
1231 }
1232 else {
1233 for (i = mp.row-ods.p_line;
1234 i > 0 && header_downline(!hdr_only, 0);
1235 --i)
1236 ;
1237 }
1238
1239 if((ods.p_ind = mp.col - headents[ods.cur_e].prwid) <= 0)
1240 ods.p_ind = 0;
1241
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.
1245 */
1246 if (ods.p_line >= ComposerTopLine)
1247 retval = -3;
1248 }
1249
1250 break;
1251
1252 case M_BUTTON_RIGHT :
1253 #ifdef _WINDOWS
1254 pico_popup();
1255 #endif
1256 case M_BUTTON_MIDDLE :
1257 default : /* NOOP */
1258 break;
1259 }
1260
1261 break;
1262 #endif /* MOUSE */
1263
1264 case (CTRL|'T') : /* Call field selector */
1265 errmss = NULL;
1266 if(headents[ods.cur_e].is_attach) {
1267 /*--- selector for attachments ----*/
1268 char dir[NLINE], fn[NLINE], sz[NLINE];
1269 LMLIST *lm = NULL, *lmp;
1270
1271 strncpy(dir, (gmode & MDCURDIR)
1272 ? (browse_dir[0] ? browse_dir : ".")
1273 : ((gmode & MDTREE) || opertree[0])
1274 ? opertree
1275 : (browse_dir[0] ? browse_dir
1276 : gethomedir(NULL)), sizeof(dir));
1277 dir[sizeof(dir)-1] = '\0';
1278 fn[0] = sz[0] = '\0';
1279 if(FileBrowse(dir, sizeof(dir), fn, sizeof(fn), sz, sizeof(sz),
1280 FB_READ | FB_ATTACH | FB_LMODEPOS, &lm) == 1){
1281 char buf[NLINE], *bfp;
1282 size_t len;
1283
1284 for(lmp = lm; lmp; lmp = lmp->next){
1285 size_t space;
1286
1287 len = lmp->dir ? strlen(lmp->dir)+1 : 0;
1288 len += lmp->fname ? strlen(lmp->fname) : 0;
1289 len += 7;
1290 len += strlen(lmp->size);
1291
1292 if(len+3 > sizeof(buf)){
1293 space = len+3;
1294 bfp = malloc(space*sizeof(char));
1295 if(bfp == NULL){
1296 emlwwrite(_("Can't malloc space for filename"),
1297 NULL);
1298 continue;
1299 }
1300 }
1301 else{
1302 bfp = buf;
1303 space = sizeof(buf);
1304 }
1305
1306 if(lmp->dir && lmp->dir[0])
1307 snprintf(bfp, space, "%s%c%s", lmp->dir, C_FILESEP,
1308 lmp->fname ? lmp->fname : "");
1309 else
1310 snprintf(bfp, space, "%s", lmp->fname ? lmp->fname : "");
1311
1312 (void) QuoteAttach(bfp, space);
1313
1314 snprintf(bfp + strlen(bfp), space-strlen(bfp), " (%s) \"\"%s", lmp->size,
1315 (!headents[ods.cur_e].hd_text->text[0]) ? "":",");
1316
1317 if(FormatLines(headents[ods.cur_e].hd_text, bfp,
1318 term.t_ncol - headents[ods.cur_e].prwid,
1319 headents[ods.cur_e].break_on_comma,0)==-1){
1320 emlwwrite(_("Format lines failed!"), NULL);
1321 }
1322
1323 if(bfp != buf)
1324 free(bfp);
1325
1326 UpdateHeader(0);
1327 }
1328
1329 zotlmlist(lm);
1330 } /* else, nothing of interest */
1331 } else if (headents[ods.cur_e].selector != NULL) {
1332 VARS_TO_SAVE *saved_state;
1333
1334 /*---- General selector for non-attachments -----*/
1335
1336 /*
1337 * Since the selector may make a new call back to pico()
1338 * we need to save and restore the pico state here.
1339 */
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 */
1346 }
1347 else{
1348 char *s = "Not enough memory";
1349 size_t len;
1350
1351 len = strlen(s);
1352 errmss = (char *)malloc((len+1) * sizeof(char));
1353 strncpy(errmss, s, len+1);
1354 errmss[len] = '\0';
1355 bufp = NULL;
1356 }
1357
1358 if(bufp != NULL) {
1359 mangled = 0;
1360 err = NULL;
1361 if(headents[ods.cur_e].break_on_comma) {
1362 /*--- Must be an address ---*/
1363
1364 /*
1365 * If current line is empty and there are more
1366 * lines that follow, delete the empty lines
1367 * before adding the new address.
1368 */
1369 if(ods.cur_l->text[0] == '\0' && ods.cur_l->next){
1370 do {
1371 KillHeaderLine(ods.cur_l, 1);
1372 ods.p_len = ucs4_strlen(ods.cur_l->text);
1373 } while(ods.cur_l->text[0] == '\0' && ods.cur_l->next);
1374 }
1375
1376 ods.p_ind = 0;
1377
1378 if(ods.cur_l->text[0] != '\0'){
1379 struct hdr_line *h, *start_of_addr;
1380 int q = 0;
1381
1382 /* cur is not first line */
1383 if(ods.cur_l != headents[ods.cur_e].hd_text){
1384 /*
1385 * Protect against adding a new entry into
1386 * the middle of a long, continued entry.
1387 */
1388 start_of_addr = NULL; /* cur_l is a good place to be */
1389 q = 0;
1390 for(h = headents[ods.cur_e].hd_text; h; h = h->next){
1391 if(ucs4_strqchr(h->text, ',', &q, -1)){
1392 start_of_addr = NULL;
1393 q = 0;
1394 }
1395 else if(start_of_addr == NULL)
1396 start_of_addr = h;
1397
1398 if(h->next == ods.cur_l)
1399 break;
1400 }
1401
1402 if(start_of_addr){
1403 ods.cur_l = start_of_addr;
1404 ods.p_len = ucs4_strlen(ods.cur_l->text);
1405 }
1406 }
1407
1408 for(i = ++ods.p_len; i; i--)
1409 ods.cur_l->text[i] = ods.cur_l->text[i-1];
1410
1411 ods.cur_l->text[0] = ',';
1412 }
1413
1414 if(FormatLines(ods.cur_l, bufp,
1415 (term.t_ncol-headents[ods.cur_e].prwid),
1416 headents[ods.cur_e].break_on_comma, 0) == -1){
1417 emlwrite("Problem adding address to header !",
1418 NULL);
1419 (*term.t_beep)();
1420 break;
1421 }
1422
1423 /*
1424 * If the "selector" has a "builder" as well, pass
1425 * what was just selected thru the builder...
1426 */
1427 if(headents[ods.cur_e].builder){
1428 struct hdr_line *l;
1429 int cur_row, top_too = 0;
1430
1431 for(l = headents[ods.cur_e].hd_text, cur_row = 0;
1432 l && l != ods.cur_l;
1433 l = l->next, cur_row++)
1434 ;
1435
1436 top_too = headents[ods.cur_e].hd_text == ods.top_l;
1437
1438 if(call_builder(&headents[ods.cur_e], &mangled,
1439 &err) < 0){
1440 fix_mangle_and_err(&mangled, &err,
1441 headents[ods.cur_e].name);
1442 }
1443
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--)
1447 ;
1448
1449 if(top_too)
1450 ods.top_l = headents[ods.cur_e].hd_text;
1451 }
1452
1453 UpdateHeader(0);
1454 } else {
1455 UCS *u;
1456
1457 u = utf8_to_ucs4_cpystr(bufp);
1458 if(u){
1459 ucs4_strncpy(headents[ods.cur_e].hd_text->text, u, HLSZ);
1460 headents[ods.cur_e].hd_text->text[HLSZ-1] = '\0';
1461 fs_give((void **) &u);
1462 }
1463 }
1464
1465 free(bufp);
1466 /* mark this entry dirty */
1467 mark_sticky(&headents[ods.cur_e]);
1468 headents[ods.cur_e].dirty = 1;
1469 fix_mangle_and_err(&mangled,&err,headents[ods.cur_e].name);
1470 }
1471 } else {
1472 /*----- No selector -----*/
1473 (*term.t_beep)();
1474 continue;
1475 }
1476
1477 PaintBody(0);
1478 if(errmss != NULL) {
1479 (*term.t_beep)();
1480 emlwrite(errmss, NULL);
1481 free(errmss);
1482 errmss = NULL;
1483 }
1484 continue;
1485
1486 case (CTRL|'G'): /* HELP */
1487 if(term.t_mrow == 0){
1488 if(km_popped == 0){
1489 km_popped = 2;
1490 sgarbk = 1; /* bring up menu */
1491 break;
1492 }
1493 }
1494
1495 if(!ComposerHelp(ods.cur_e))
1496 break; /* else, fall through... */
1497
1498 case (CTRL|'L'): /* redraw requested */
1499 PaintBody(0);
1500 break;
1501
1502 case (CTRL|'_'): /* file editor */
1503 if(headents[ods.cur_e].fileedit != NULL){
1504 struct headerentry *e;
1505 struct hdr_line *line;
1506 int sz = 0;
1507 char *filename = NULL;
1508 VARS_TO_SAVE *saved_state;
1509
1510 /*
1511 * Since the fileedit will make a new call back to pico()
1512 * we need to save and restore the pico state here.
1513 */
1514 if((saved_state = save_pico_state()) != NULL){
1515 UCS *u;
1516
1517 e = &headents[ods.cur_e];
1518
1519 for(line = e->hd_text; line != NULL; line = line->next)
1520 sz += ucs4_strlen(line->text);
1521
1522 u = (UCS *)malloc((sz+1) * sizeof(*u));
1523 if(u){
1524 u[0] = '\0';
1525 for(line = e->hd_text; line != NULL; line = line->next)
1526 ucs4_strncat(u, line->text, sz+1-ucs4_strlen(u)-1);
1527
1528 filename = ucs4_to_utf8_cpystr(u);
1529 free(u);
1530 }
1531
1532 errmss = (*(headents[ods.cur_e].fileedit))(filename);
1533
1534 if(filename)
1535 free(filename);
1536
1537 restore_pico_state(saved_state);
1538 free_pico_state(saved_state);
1539 ttresize(); /* fixup screen bufs */
1540 picosigs(); /* restore altered signals */
1541 }
1542 else{
1543 char *s = "Not enough memory";
1544 size_t len;
1545
1546 len = strlen(s);
1547 errmss = (char *)malloc((len+1) * sizeof(char));
1548 strncpy(errmss, s, len+1);
1549 errmss[len] = '\0';
1550 }
1551
1552 PaintBody(0);
1553
1554 if(errmss != NULL) {
1555 (*term.t_beep)();
1556 emlwrite(errmss, NULL);
1557 free(errmss);
1558 errmss = NULL;
1559 }
1560
1561 continue;
1562 }
1563 else
1564 goto bleep;
1565
1566 break;
1567
1568 default : /* huh? */
1569 bleep:
1570 unknown_command(ch);
1571
1572 case NODATA:
1573 break;
1574 }
1575 }
1576 while (ods.p_line < ComposerTopLine);
1577
1578 display_delimiter(1);
1579 curwp->w_flag |= WFMODE;
1580 movecursor(currow, curcol);
1581 ComposerEditing = FALSE;
1582 if (ComposerTopLine == BOTTOM()){
1583 UpdateHeader(0);
1584 PaintHeader(COMPOSER_TOP_LINE, FALSE);
1585 PaintBody(1);
1586 }
1587
1588 return(retval);
1589 }
1590
1591
1592 /*
1593 *
1594 */
1595 int
header_downline(int beyond,int gripe)1596 header_downline(int beyond, int gripe)
1597 {
1598 struct hdr_line *new_l, *l;
1599 int new_e, status, fullpaint, len, e, incr = 0;
1600
1601 /* calculate the next line: physical *and* logical */
1602 status = 0;
1603 new_e = ods.cur_e;
1604 if((new_l = next_sel_hline(&new_e, ods.cur_l)) == NULL && !beyond){
1605
1606 if(gripe){
1607 char xx[81];
1608
1609 strncpy(xx, "Can't move down. Use ^X to ", sizeof(xx));
1610 xx[sizeof(xx)-1] = '\0';
1611 strncat(xx, (Pmaster && Pmaster->exit_label)
1612 ? Pmaster->exit_label
1613 : (gmode & MDHDRONLY)
1614 ? "eXit/Save"
1615 : (gmode & MDVIEW)
1616 ? "eXit"
1617 : "Send", sizeof(xx)-strlen(xx)-1);
1618 xx[sizeof(xx)-1] = '\0';
1619 strncat(xx, ".", sizeof(xx)-strlen(xx)-1);
1620 xx[sizeof(xx)-1] = '\0';
1621 emlwrite(xx, NULL);
1622 }
1623
1624 return(0);
1625 }
1626
1627 /*
1628 * Because of blank header lines the cursor may need to move down
1629 * more than one line. Figure out how far.
1630 */
1631 e = ods.cur_e;
1632 l = ods.cur_l;
1633 while(l != new_l){
1634 if((l = next_hline(&e, l)) != NULL)
1635 incr++;
1636 else
1637 break; /* can't happen */
1638 }
1639
1640 ods.p_line += incr;
1641 fullpaint = ods.p_line >= BOTTOM(); /* force full redraw? */
1642
1643 /* expand what needs expanding */
1644 if(new_e != ods.cur_e || !new_l){ /* new (or last) field ! */
1645 if(new_l)
1646 InvertPrompt(ods.cur_e, FALSE); /* turn off current entry */
1647
1648 if(headents[ods.cur_e].is_attach) { /* verify data ? */
1649 if((status = FormatSyncAttach()) != 0){ /* fixup if 1 or -1 */
1650 headents[ods.cur_e].rich_header = 0;
1651 if(FormatLines(headents[ods.cur_e].hd_text, "",
1652 term.t_ncol-headents[new_e].prwid,
1653 headents[ods.cur_e].break_on_comma, 0) == -1)
1654 emlwwrite(_("Format lines failed!"), NULL);
1655 }
1656 } else if(headents[ods.cur_e].builder) { /* expand addresses */
1657 int mangled = 0;
1658 char *err = NULL;
1659
1660 if((status = call_builder(&headents[ods.cur_e], &mangled, &err))>0){
1661 struct hdr_line *l; /* fixup ods.cur_l */
1662 ods.p_line = 0; /* force top line recalc */
1663 for(l = headents[ods.cur_e].hd_text; l; l = l->next)
1664 ods.cur_l = l;
1665
1666 if(new_l) /* if new_l, force validity */
1667 new_l = headents[new_e].hd_text;
1668
1669 NewTop(0); /* get new top_l */
1670 }
1671 else if(status < 0){ /* bad addr? no leave! */
1672 --ods.p_line;
1673 fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
1674 InvertPrompt(ods.cur_e, TRUE);
1675 return(0);
1676 }
1677
1678 fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
1679 }
1680
1681 if(new_l){ /* if one below, turn it on */
1682 InvertPrompt(new_e, TRUE);
1683 sgarbk = 1; /* paint keymenu too */
1684 }
1685 }
1686
1687 if(new_l){ /* fixup new pointers */
1688 ods.cur_l = (ods.cur_e != new_e) ? headents[new_e].hd_text : new_l;
1689 ods.cur_e = new_e;
1690 if(ods.p_ind > (len = ucs4_strlen(ods.cur_l->text)))
1691 ods.p_ind = len;
1692 }
1693
1694 if(!new_l || status || fullpaint){ /* handle big screen paint */
1695 UpdateHeader(0);
1696 PaintHeader(COMPOSER_TOP_LINE, FALSE);
1697 PaintBody(1);
1698
1699 if(!new_l){ /* make sure we're done */
1700 ods.p_line = ComposerTopLine;
1701 InvertPrompt(ods.cur_e, FALSE); /* turn off current entry */
1702 }
1703 }
1704
1705 return(new_l ? 1 : 0);
1706 }
1707
1708
1709 /*
1710 *
1711 */
1712 int
header_upline(int gripe)1713 header_upline(int gripe)
1714 {
1715 struct hdr_line *new_l, *l;
1716 int new_e, status, fullpaint, len, e, incr = 0;
1717 EML eml;
1718
1719 /* calculate the next line: physical *and* logical */
1720 status = 0;
1721 new_e = ods.cur_e;
1722 if(!(new_l = prev_sel_hline(&new_e, ods.cur_l))){ /* all the way up! */
1723 ods.p_line = COMPOSER_TOP_LINE;
1724 if(gripe){
1725 eml.s = (Pmaster->pine_flags & MDHDRONLY) ? "entry" : "header";
1726 emlwrite(_("Can't move beyond top of %s"), &eml);
1727 }
1728
1729 return(0);
1730 }
1731
1732 /*
1733 * Because of blank header lines the cursor may need to move up
1734 * more than one line. Figure out how far.
1735 */
1736 e = ods.cur_e;
1737 l = ods.cur_l;
1738 while(l != new_l){
1739 if((l = prev_hline(&e, l)) != NULL)
1740 incr++;
1741 else
1742 break; /* can't happen */
1743 }
1744
1745 ods.p_line -= incr;
1746 fullpaint = ods.p_line <= COMPOSER_TOP_LINE;
1747
1748 if(new_e != ods.cur_e){ /* new field ! */
1749 InvertPrompt(ods.cur_e, FALSE);
1750 if(headents[ods.cur_e].is_attach){
1751 if((status = FormatSyncAttach()) != 0){ /* non-zero ? reformat field */
1752 headents[ods.cur_e].rich_header = 0;
1753 if(FormatLines(headents[ods.cur_e].hd_text, "",
1754 term.t_ncol - headents[ods.cur_e].prwid,
1755 headents[ods.cur_e].break_on_comma,0) == -1)
1756 emlwwrite(_("Format lines failed!"), NULL);
1757 }
1758 }
1759 else if(headents[ods.cur_e].builder){
1760 int mangled = 0;
1761 char *err = NULL;
1762
1763 if((status = call_builder(&headents[ods.cur_e], &mangled,
1764 &err)) >= 0){
1765 /* repair new_l */
1766 for(new_l = headents[new_e].hd_text;
1767 new_l->next;
1768 new_l=new_l->next)
1769 ;
1770
1771 /* and cur_l (required in fix_and... */
1772 ods.cur_l = new_l;
1773 }
1774 else{
1775 ++ods.p_line;
1776 fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
1777 InvertPrompt(ods.cur_e, TRUE);
1778 return(0);
1779 }
1780
1781 fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
1782 }
1783
1784 InvertPrompt(new_e, TRUE);
1785 sgarbk = 1;
1786 }
1787
1788 ods.cur_e = new_e; /* update pointers */
1789 ods.cur_l = new_l;
1790 if(ods.p_ind > (len = ucs4_strlen(ods.cur_l->text)))
1791 ods.p_ind = len;
1792
1793 if(status > 0 || fullpaint){
1794 UpdateHeader(0);
1795 PaintHeader(COMPOSER_TOP_LINE, FALSE);
1796 PaintBody(1);
1797 }
1798
1799 return(1);
1800 }
1801
1802
1803 /*
1804 *
1805 */
1806 int
AppendAttachment(char * fn,char * sz,char * cmt)1807 AppendAttachment(char *fn, char *sz, char *cmt)
1808 {
1809 int a_e, status, spaces;
1810 struct hdr_line *lp;
1811 char b[256];
1812 UCS *u;
1813
1814 /*--- Find headerentry that is attachments (only first) --*/
1815 for(a_e = 0; headents[a_e].name != NULL; a_e++ )
1816 if(headents[a_e].is_attach){
1817 /* make sure field stays displayed */
1818 headents[a_e].rich_header = 0;
1819 headents[a_e].display_it = 1;
1820 break;
1821 }
1822
1823 /* append new attachment line */
1824 for(lp = headents[a_e].hd_text; lp->next; lp=lp->next)
1825 ;
1826
1827 /* build new attachment line */
1828 if(lp->text[0]){ /* adding a line? */
1829 UCS comma[2];
1830
1831 comma[0] = ',';
1832 comma[1] = '\0';
1833 ucs4_strncat(lp->text, comma, HLSZ-ucs4_strlen(lp->text)-1); /* append delimiter */
1834 if((lp->next = HALLOC()) != NULL){ /* allocate new line */
1835 lp->next->prev = lp;
1836 lp->next->next = NULL;
1837 lp = lp->next;
1838 }
1839 else{
1840 emlwwrite(_("Can't allocate line for new attachment!"), NULL);
1841 return(0);
1842 }
1843 }
1844
1845
1846 spaces = (*fn == '\"') ? 0 : (strpbrk(fn, "(), \t") != NULL);
1847 snprintf(b, sizeof(b), "%s%s%s (%s) \"%.*s\"",
1848 spaces ? "\"" : "", fn, spaces ? "\"" : "",
1849 sz ? sz : "", 80, cmt ? cmt : "");
1850 u = utf8_to_ucs4_cpystr(b);
1851 if(u){
1852 ucs4_strncpy(lp->text, u, HLSZ);
1853 lp->text[HLSZ-1] = '\0';
1854 fs_give((void **) &u);
1855 }
1856
1857 /* validate the new attachment, and reformat if needed */
1858 if((status = SyncAttach()) != 0){
1859 EML eml;
1860
1861 if(status < 0){
1862 eml.s = fn;
1863 emlwwrite(_("Problem attaching: %s"), &eml);
1864 }
1865
1866 if(FormatLines(headents[a_e].hd_text, "",
1867 term.t_ncol - headents[a_e].prwid,
1868 headents[a_e].break_on_comma, 0) == -1){
1869 emlwwrite(_("Format lines failed!"), NULL);
1870 return(0);
1871 }
1872 }
1873
1874 UpdateHeader(0);
1875 PaintHeader(COMPOSER_TOP_LINE, status != 0);
1876 PaintBody(1);
1877 return(status != 0);
1878 }
1879
1880
1881 /*
1882 * LineEdit - Always use insert mode and handle line wrapping
1883 *
1884 * returns:
1885 * Any characters typed in that aren't printable
1886 * (i.e. commands)
1887 *
1888 * notes:
1889 * Assume we are guaranteed that there is sufficiently
1890 * more buffer space in a line than screen width (just one
1891 * less thing to worry about). If you want to change this,
1892 * then pputc will have to be taught to check the line buffer
1893 * length, and HALLOC() will probably have to become a func.
1894 */
1895 UCS
LineEdit(int allowedit,UCS * lastch)1896 LineEdit(int allowedit, UCS *lastch)
1897 {
1898 register struct hdr_line *lp; /* temporary line pointer */
1899 register int i;
1900 UCS ch = 0;
1901 register int status; /* various func's return val */
1902 UCS *tbufp; /* temporary buffer pointers */
1903 int skipmove = 0;
1904 UCS *strng;
1905 UCS last_key; /* last keystroke */
1906
1907 strng = ods.cur_l->text; /* initialize offsets */
1908 ods.p_len = MIN(ucs4_strlen(strng), HLSZ);
1909 if(ods.p_ind < 0) /* offset within range? */
1910 ods.p_ind = 0;
1911 else if(ods.p_ind > ods.p_len)
1912 ods.p_ind = ods.p_len;
1913 else if(ucs4_str_width_ptr_to_ptr(&strng[0], &strng[ods.p_ind]) > LINEWID()){
1914 UCS *u;
1915
1916 u = ucs4_particular_width(strng, LINEWID());
1917 ods.p_ind = u - strng;
1918 }
1919
1920 while(1){ /* edit the line... */
1921
1922 if(Pmaster && subject_line != NULL
1923 && ods.cur_l == subject_line
1924 && ods.cur_l->text[0] == 0)
1925 (*Pmaster->newthread)();
1926
1927 if(skipmove)
1928 skipmove = 0;
1929 else
1930 HeaderPaintCursor();
1931
1932 last_key = ch;
1933 if(ch && lastch)
1934 *lastch = ch;
1935
1936 (*term.t_flush)(); /* get everything out */
1937
1938 #ifdef MOUSE
1939 mouse_in_content(KEY_MOUSE, -1, -1, 0, 0);
1940 register_mfunc(mouse_in_content,2,0,term.t_nrow-(term.t_mrow+1),
1941 term.t_ncol);
1942 #endif
1943 #ifdef _WINDOWS
1944 mswin_setdndcallback (composer_file_drop);
1945 mswin_mousetrackcallback(pico_cursor);
1946 #endif
1947
1948 ch = GetKey();
1949
1950 if (term.t_nrow < 6 && ch != NODATA){
1951 (*term.t_beep)();
1952 emlwrite(_("Please make the screen larger."), NULL);
1953 continue;
1954 }
1955
1956 #ifdef MOUSE
1957 clear_mfunc(mouse_in_content);
1958 #endif
1959 #ifdef _WINDOWS
1960 mswin_cleardndcallback ();
1961 mswin_mousetrackcallback(NULL);
1962 #endif
1963
1964 switch(ch){
1965 case DEL :
1966 if(gmode & P_DELRUBS)
1967 ch = KEY_DEL;
1968
1969 default :
1970 (*Pmaster->keybinput)();
1971 if(!time_to_check())
1972 break;
1973
1974 case NODATA : /* new mail ? */
1975 if((*Pmaster->newmail)(ch == NODATA ? 0 : 2, 1) >= 0){
1976 int rv;
1977
1978 if(km_popped){
1979 term.t_mrow = 2;
1980 curwp->w_ntrows -= 2;
1981 }
1982
1983 clearcursor();
1984 mlerase();
1985 rv = (*Pmaster->showmsg)(ch);
1986 ttresize();
1987 picosigs();
1988 if(rv) /* Did showmsg corrupt the display? */
1989 PaintBody(0); /* Yes, repaint */
1990
1991 mpresf = 1;
1992 if(km_popped){
1993 term.t_mrow = 0;
1994 curwp->w_ntrows += 2;
1995 }
1996 }
1997
1998 clearcursor();
1999 movecursor(ods.p_line,
2000 ucs4_str_width_ptr_to_ptr(strng, &strng[ods.p_ind])+headents[ods.cur_e].prwid);
2001 if(ch == NODATA) /* GetKey timed out */
2002 continue;
2003
2004 break;
2005 }
2006
2007 if(mpresf){ /* blast old messages */
2008 if(mpresf++ > NMMESSDELAY){ /* every few keystrokes */
2009 mlerase();
2010 movecursor(ods.p_line,
2011 ucs4_str_width_ptr_to_ptr(strng, &strng[ods.p_ind])+headents[ods.cur_e].prwid);
2012 }
2013 }
2014
2015 tbufp = &strng[ods.p_len];
2016
2017 if(VALID_KEY(ch)){ /* char input */
2018 /*
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 ...
2023 */
2024 if(allowedit){
2025 if(headents[ods.cur_e].is_attach && intag(strng,ods.p_ind)){
2026 emlwwrite(_("Can't edit attachment number!"), NULL);
2027 continue;
2028 }
2029
2030 if(headents[ods.cur_e].single_space){
2031 if(ch == ' '
2032 && (strng[ods.p_ind]==' ' || strng[ods.p_ind-1]==' '))
2033 continue;
2034 }
2035
2036 /*
2037 * go ahead and add the character...
2038 */
2039 if(ods.p_len < HLSZ){
2040 tbufp = &strng[++ods.p_len]; /* find the end */
2041 do{
2042 *tbufp = tbufp[-1];
2043 } while(--tbufp > &strng[ods.p_ind]); /* shift right */
2044 strng[ods.p_ind++] = ch; /* add char to str */
2045 }
2046
2047 /* mark this entry dirty */
2048 mark_sticky(&headents[ods.cur_e]);
2049 headents[ods.cur_e].dirty = 1;
2050
2051 /*
2052 * then find out where things fit...
2053 */
2054 if(ucs4_str_width_ptr_to_ptr(&strng[0], &strng[ods.p_len]) < LINEWID()){
2055 CELL c;
2056
2057 c.c = ch & CELLMASK;
2058 c.a = 0;
2059 if(pinsert(c)){ /* add char to str */
2060 skipmove++; /* must'a been optimal */
2061 continue; /* on to the next! */
2062 }
2063 }
2064 else{
2065 if((status = FormatLines(ods.cur_l, "", LINEWID(),
2066 headents[ods.cur_e].break_on_comma,0)) == -1){
2067 (*term.t_beep)();
2068 continue;
2069 }
2070 else{
2071 /*
2072 * during the format, the dot may have moved
2073 * down to the next line...
2074 */
2075 if(ods.p_ind >= ucs4_strlen(strng)){
2076 ods.p_line++;
2077 ods.p_ind -= ucs4_strlen(strng);
2078 ods.cur_l = ods.cur_l->next;
2079 strng = ods.cur_l->text;
2080 }
2081
2082 ods.p_len = ucs4_strlen(strng);
2083 }
2084
2085 UpdateHeader(0);
2086 PaintHeader(COMPOSER_TOP_LINE, FALSE);
2087 PaintBody(1);
2088 continue;
2089 }
2090 }
2091 else{
2092 rdonly();
2093 continue;
2094 }
2095 }
2096 else { /* interpret ch as a command */
2097 switch (ch = normalize_cmd(ch, ckm, 2)) {
2098 case (CTRL|KEY_LEFT): /* word skip left */
2099 if(ods.p_ind > 0) /* Scoot one char left if possible */
2100 ods.p_ind--;
2101
2102 if(ods.p_ind == 0)
2103 {
2104 if(ods.p_line != COMPOSER_TOP_LINE)
2105 ods.p_ind = 1000; /* put cursor at end of line */
2106 return(KEY_UP);
2107 }
2108
2109 while(ods.p_ind > 0 && !ucs4_isalnum(strng[ods.p_ind]))
2110 ods.p_ind--; /* skip any whitespace we're in */
2111
2112 while(ods.p_ind > 0) {
2113 /* Bail if the character right before this one is whitespace */
2114 if(ods.p_ind > 1 && !ucs4_isalnum(strng[ods.p_ind - 1]))
2115 break;
2116 ods.p_ind--;
2117 }
2118 continue;
2119
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 */
2124
2125 while(strng[ods.p_ind] && !ucs4_isalnum(strng[ods.p_ind]))
2126 ods.p_ind++; /* skip any whitespace after it */
2127
2128 if(strng[ods.p_ind] == '\0'){
2129 ods.p_ind = 0; /* end of line, let caller handle it */
2130 return(KEY_DOWN);
2131 }
2132
2133 continue;
2134
2135 case (CTRL|'K') : /* kill line cursor's on */
2136 if(!allowedit){
2137 rdonly();
2138 continue;
2139 }
2140
2141 lp = ods.cur_l;
2142 if (!(gmode & MDDTKILL))
2143 ods.p_ind = 0;
2144
2145 if(KillHeaderLine(lp, (last_key == (CTRL|'K')))){
2146 if(TERM_OPTIMIZE &&
2147 !(ods.cur_l->prev==NULL && ods.cur_l->next==NULL))
2148 scrollup(wheadp, ods.p_line, 1);
2149
2150 if(ods.cur_l->next == NULL)
2151 if(zotcomma(ods.cur_l->text)){
2152 if(ods.p_ind > 0)
2153 ods.p_ind = ucs4_strlen(ods.cur_l->text);
2154 }
2155
2156 i = (ods.p_line == COMPOSER_TOP_LINE);
2157 UpdateHeader(0);
2158 PaintHeader(COMPOSER_TOP_LINE, TRUE);
2159
2160 if(km_popped){
2161 km_popped--;
2162 movecursor(term.t_nrow, 0);
2163 peeol();
2164 }
2165
2166 PaintBody(1);
2167
2168 }
2169 strng = ods.cur_l->text;
2170 ods.p_len = ucs4_strlen(strng);
2171 headents[ods.cur_e].sticky = 0;
2172 headents[ods.cur_e].dirty = 1;
2173 continue;
2174
2175 case (CTRL|'U') : /* un-delete deleted lines */
2176 if(!allowedit){
2177 rdonly();
2178 continue;
2179 }
2180
2181 if(SaveHeaderLines()){
2182 UpdateHeader(0);
2183 PaintHeader(COMPOSER_TOP_LINE, FALSE);
2184 if(km_popped){
2185 km_popped--;
2186 movecursor(term.t_nrow, 0);
2187 peeol();
2188 }
2189
2190 PaintBody(1);
2191 strng = ods.cur_l->text;
2192 ods.p_len = ucs4_strlen(strng);
2193 mark_sticky(&headents[ods.cur_e]);
2194 headents[ods.cur_e].dirty = 1;
2195 }
2196 else
2197 /* TRANSLATORS: Killing text is deleting it and
2198 Unkilling text is undeleting killed text. */
2199 emlwrite(_("Problem Unkilling text"), NULL);
2200 continue;
2201
2202 case (CTRL|'F') :
2203 case KEY_RIGHT: /* move character right */
2204 if(ods.p_ind < ods.p_len){
2205 ods.p_ind++;
2206 continue;
2207 }
2208 else if(gmode & MDHDRONLY)
2209 continue;
2210
2211 ods.p_ind = 0;
2212 return(KEY_DOWN);
2213
2214 case (CTRL|'B') :
2215 case KEY_LEFT : /* move character left */
2216 if(ods.p_ind > 0){
2217 ods.p_ind--;
2218 continue;
2219 }
2220 if(ods.p_line != COMPOSER_TOP_LINE)
2221 ods.p_ind = 1000; /* put cursor at end of line */
2222 return(KEY_UP);
2223
2224 case (CTRL|'M') : /* goto next field */
2225 ods.p_ind = 0;
2226 return(KEY_DOWN);
2227
2228 case KEY_HOME :
2229 case (CTRL|'A') : /* goto beginning of line */
2230 ods.p_ind = 0;
2231 continue;
2232
2233 case KEY_END :
2234 case (CTRL|'E') : /* goto end of line */
2235 ods.p_ind = ods.p_len;
2236 continue;
2237
2238 case (CTRL|'D') : /* blast this char */
2239 case KEY_DEL :
2240 if(!allowedit){
2241 rdonly();
2242 continue;
2243 }
2244 else if(ods.p_ind >= ucs4_strlen(strng))
2245 continue;
2246
2247 if(headents[ods.cur_e].is_attach && intag(strng, ods.p_ind)){
2248 emlwwrite(_("Can't edit attachment number!"), NULL);
2249 continue;
2250 }
2251
2252 pputc(strng[ods.p_ind++], 0); /* drop through and rubout */
2253
2254 case DEL : /* blast previous char */
2255 case (CTRL|'H') :
2256 if(!allowedit){
2257 rdonly();
2258 continue;
2259 }
2260
2261 if(headents[ods.cur_e].is_attach && intag(strng, ods.p_ind-1)){
2262 emlwwrite(_("Can't edit attachment number!"), NULL);
2263 continue;
2264 }
2265
2266 if(ods.p_ind > 0){ /* just shift left one char */
2267 ods.p_len--;
2268 headents[ods.cur_e].dirty = 1;
2269 if(ods.p_len == 0)
2270 headents[ods.cur_e].sticky = 0;
2271 else
2272 mark_sticky(&headents[ods.cur_e]);
2273
2274 tbufp = &strng[--ods.p_ind];
2275 while(*tbufp++ != '\0')
2276 tbufp[-1] = *tbufp;
2277 tbufp = &strng[ods.p_ind];
2278 if(pdel()) /* physical screen delete */
2279 skipmove++; /* must'a been optimal */
2280 }
2281 else{ /* may have work to do */
2282 if(ods.cur_l->prev == NULL){
2283 (*term.t_beep)(); /* no erase into next field */
2284 continue;
2285 }
2286
2287 ods.p_line--;
2288 ods.cur_l = ods.cur_l->prev;
2289 strng = ods.cur_l->text;
2290 if((i=ucs4_strlen(strng)) > 0){
2291 strng[i-1] = '\0'; /* erase the character */
2292 ods.p_ind = i-1;
2293 }
2294 else{
2295 headents[ods.cur_e].sticky = 0;
2296 ods.p_ind = 0;
2297 }
2298
2299 tbufp = &strng[ods.p_ind];
2300 }
2301
2302 if((status = FormatLines(ods.cur_l, "", LINEWID(),
2303 headents[ods.cur_e].break_on_comma,0))==-1){
2304 (*term.t_beep)();
2305 continue;
2306 }
2307 else{
2308 /*
2309 * beware, the dot may have moved...
2310 */
2311 while((ods.p_len=ucs4_strlen(strng)) < ods.p_ind){
2312 ods.p_line++;
2313 ods.p_ind -= ucs4_strlen(strng);
2314 ods.cur_l = ods.cur_l->next;
2315 strng = ods.cur_l->text;
2316 ods.p_len = ucs4_strlen(strng);
2317 tbufp = &strng[ods.p_ind];
2318 status = TRUE;
2319 }
2320
2321 if(UpdateHeader(0))
2322 status = TRUE;
2323
2324 PaintHeader(COMPOSER_TOP_LINE, FALSE);
2325 if(status == TRUE)
2326 PaintBody(1);
2327 }
2328
2329 movecursor(ods.p_line,
2330 ucs4_str_width_ptr_to_ptr(strng, &strng[ods.p_ind])+headents[ods.cur_e].prwid);
2331
2332 if(skipmove)
2333 continue;
2334
2335 break;
2336
2337 default :
2338 return(ch);
2339 }
2340 }
2341
2342 while ((tbufp-strng) < HLSZ && *tbufp != '\0') /* synchronizing loop */
2343 pputc(*tbufp++, 0);
2344
2345 if(ucs4_str_width_ptr_to_ptr(&strng[0], &strng[ods.p_len]) < LINEWID())
2346 peeol();
2347 }
2348 }
2349
2350
2351 void
HeaderPaintCursor(void)2352 HeaderPaintCursor(void)
2353 {
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);
2355 }
2356
2357
2358
2359 /*
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.
2363 *
2364 * notes:
2365 * Not much optimization at all. Right now, it recursively
2366 * fixes all remaining lines in the entry. Some speed might
2367 * gained if this was built to iteratively scan the lines.
2368 *
2369 * returns:
2370 * -1 on error
2371 * FALSE if only this line is changed
2372 * TRUE if text below the first line is changed
2373 */
2374 int
FormatLines(struct hdr_line * h,char * utf8_instr,int maxwid,int break_on_comma,int quoted)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 */
2380 {
2381 int retval = FALSE;
2382 int i, l, len;
2383 char *utf8;
2384 UCS *ostr; /* pointer to output string */
2385 UCS *free_istr = NULL;
2386 UCS *istr = NULL;
2387 UCS *breakp; /* pointer to line break */
2388 UCS *bp, *tp; /* temporary pointers */
2389 UCS *buf; /* string to add later */
2390 struct hdr_line *nlp, *lp;
2391
2392 ostr = h->text;
2393 nlp = h->next;
2394 if(utf8_instr)
2395 free_istr = istr = utf8_to_ucs4_cpystr(utf8_instr);
2396
2397 len = ucs4_strlen(istr) + ucs4_strlen(ostr);
2398 if((buf = (UCS *) malloc((len+10) * sizeof(*buf))) == NULL){
2399 if(free_istr)
2400 fs_give((void **) &free_istr);
2401
2402 return(-1);
2403 }
2404
2405 if(ucs4_str_width(istr) + ucs4_str_width(ostr) >= maxwid){ /* break then fixup below */
2406
2407 if((l=ucs4_str_width(istr)) < maxwid){ /* room for more */
2408
2409 if(break_on_comma && (bp = ucs4_strqchr(istr, ',', "ed, -1))){
2410 bp += (bp[1] == ' ') ? 2 : 1;
2411 for(tp = bp; *tp && *tp == ' '; tp++)
2412 ;
2413
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';
2418
2419 for(i = 0; &istr[i] < bp; i++)
2420 ostr[i] = istr[i];
2421
2422 ostr[i] = '\0';
2423 retval = TRUE;
2424 }
2425 else{
2426 breakp = break_point(ostr, maxwid-ucs4_str_width(istr),
2427 break_on_comma ? ',' : ' ',
2428 break_on_comma ? "ed : NULL);
2429
2430 if(breakp == ostr){ /* no good breakpoint */
2431 if(break_on_comma && *breakp == ','){
2432 breakp = ostr + 1;
2433 retval = TRUE;
2434 }
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';
2440 }
2441 else{ /* istr's broken as we can get it */
2442 /*
2443 * Break at maxwid - width of istr
2444 */
2445 breakp = ucs4_particular_width(ostr, maxwid - ucs4_str_width(istr)-1);
2446 retval = TRUE;
2447 }
2448 }
2449 else
2450 retval = TRUE;
2451
2452 if(retval){
2453 ucs4_strncpy(buf, breakp, len+10); /* save broken line */
2454 buf[len+10-1] = '\0';
2455 if(breakp == ostr){
2456 ucs4_strncpy(ostr, istr, HLSZ); /* simple if no break */
2457 ostr[HLSZ-1] = '\0';
2458 }
2459 else{
2460 *breakp = '\0'; /* more work to break it */
2461 i = ucs4_strlen(istr);
2462 /*
2463 * shift ostr i chars
2464 */
2465 for(bp=breakp; bp >= ostr && i; bp--)
2466 *(bp+i) = *bp;
2467 for(tp=ostr, bp=istr; *bp != '\0'; tp++, bp++)
2468 *tp = *bp; /* then add istr */
2469 }
2470 }
2471 }
2472 }
2473 /*
2474 * Short-circuit the recursion in this case.
2475 * No time right now to figure out how to do it better.
2476 */
2477 else if(l > 2*maxwid){
2478 UCS *istrp, *saveostr = NULL;
2479
2480 retval = TRUE;
2481 if(ostr && *ostr)
2482 saveostr = ucs4_cpystr(ostr);
2483
2484 istrp = istr;
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);
2491 }
2492 else{
2493 breakp = break_point(istrp, maxwid, ' ', NULL);
2494
2495 if(breakp == istrp) /* no good break point */
2496 breakp = ucs4_particular_width(istrp, maxwid-1);
2497 }
2498
2499 for(tp=ostr,bp=istrp; bp < breakp; tp++, bp++)
2500 *tp = *bp;
2501
2502 *tp = '\0';
2503 l -= ucs4_str_width_ptr_to_ptr(istrp, breakp);
2504 istrp = breakp;
2505
2506 if((lp = HALLOC()) == NULL){
2507 emlwrite("Can't allocate any more lines for header!", NULL);
2508 free(buf);
2509 if(free_istr)
2510 fs_give((void **) &free_istr);
2511
2512 return(-1);
2513 }
2514
2515 lp->next = h->next;
2516 if(h->next)
2517 h->next->prev = lp;
2518
2519 h->next = lp;
2520 lp->prev = h;
2521 lp->text[0] = '\0';
2522 h = h->next;
2523 ostr = h->text;
2524 }
2525
2526 /*
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.
2530 */
2531
2532 nlp = h->next;
2533 istr = istrp;
2534 if(saveostr){
2535 ucs4_strncpy(ostr, saveostr, HLSZ);
2536 ostr[HLSZ-1] = '\0';
2537 fs_give((void **) &saveostr);
2538 }
2539
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);
2545 }
2546 else{
2547 breakp = break_point(istrp, maxwid, ' ', NULL);
2548
2549 if(breakp == istrp) /* no good break point */
2550 breakp = ucs4_particular_width(istrp, maxwid-1);
2551 }
2552
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';
2557
2558 for(tp=ostr,bp=istr; bp < breakp; tp++, bp++)
2559 *tp = *bp;
2560
2561 *tp = '\0';
2562 }
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);
2569 }
2570 else{
2571 breakp = break_point(istr, maxwid, ' ', NULL);
2572
2573 if(breakp == istr) /* no good break point */
2574 breakp = ucs4_particular_width(istr, maxwid-1);
2575 }
2576
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';
2581
2582 for(tp=ostr,bp=istr; bp < breakp; tp++, bp++)
2583 *tp = *bp;
2584
2585 *tp = '\0';
2586 }
2587
2588 if(nlp == NULL){ /* no place to add below? */
2589 if((lp = HALLOC()) == NULL){
2590 emlwrite("Can't allocate any more lines for header!", NULL);
2591 free(buf);
2592 if(free_istr)
2593 fs_give((void **) &free_istr);
2594
2595 return(-1);
2596 }
2597
2598 if(TERM_OPTIMIZE && (i = physical_line(h)) != -1)
2599 scrolldown(wheadp, i - 1, 1);
2600
2601 h->next = lp; /* fix up links */
2602 lp->prev = h;
2603 lp->next = NULL;
2604 lp->text[0] = '\0';
2605 nlp = lp;
2606 retval = TRUE;
2607 }
2608 }
2609 else{ /* combined width < max */
2610 buf[0] = '\0';
2611 if(istr && *istr){
2612 ucs4_strncpy(buf, istr, len+10); /* insert istr before ostr */
2613 buf[len+10-1] = '\0';
2614
2615 ucs4_strncat(buf, ostr, len+10-ucs4_strlen(buf)-1);
2616 buf[len+10-1] = '\0';
2617
2618 ucs4_strncpy(ostr, buf, HLSZ); /* copy back to ostr */
2619 ostr[HLSZ-1] = '\0';
2620 }
2621
2622 *buf = '\0';
2623 breakp = NULL;
2624
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';
2629 *breakp = '\0';
2630
2631 if(*buf && !nlp){
2632 if((lp = HALLOC()) == NULL){
2633 emlwrite("Can't allocate any more lines for header!",NULL);
2634 free(buf);
2635 if(free_istr)
2636 fs_give((void **) &free_istr);
2637
2638 return(-1);
2639 }
2640
2641 if(TERM_OPTIMIZE && (i = physical_line(h)) != -1)
2642 scrolldown(wheadp, i - 1, 1);
2643
2644 h->next = lp; /* fix up links */
2645 lp->prev = h;
2646 lp->next = NULL;
2647 lp->text[0] = '\0';
2648 nlp = lp;
2649 retval = TRUE;
2650 }
2651 }
2652
2653 if(nlp){
2654 if(!*buf && !breakp){
2655 if(ucs4_str_width(ostr) + ucs4_str_width(nlp->text) >= maxwid){
2656 breakp = break_point(nlp->text, maxwid-ucs4_str_width(ostr),
2657 break_on_comma ? ',' : ' ',
2658 break_on_comma ? "ed : NULL);
2659
2660 if(breakp == nlp->text){ /* commas this line? */
2661 for(tp=ostr; *tp && *tp != ' '; tp++)
2662 ;
2663
2664 if(!*tp){ /* no commas, get next best */
2665 breakp += maxwid - ucs4_str_width(ostr) - 1;
2666 retval = TRUE;
2667 }
2668 else
2669 retval = FALSE;
2670 }
2671 else
2672 retval = TRUE;
2673
2674 if(retval){ /* only if something to do */
2675 for(tp = &ostr[ucs4_strlen(ostr)],bp=nlp->text; bp<breakp;
2676 tp++, bp++)
2677 *tp = *bp; /* add breakp to this line */
2678 *tp = '\0';
2679 for(tp=nlp->text, bp=breakp; *bp != '\0'; tp++, bp++)
2680 *tp = *bp; /* shift next line to left */
2681 *tp = '\0';
2682 }
2683 }
2684 else{
2685 ucs4_strncat(ostr, nlp->text, HLSZ-ucs4_strlen(ostr)-1);
2686 ostr[HLSZ-1] = '\0';
2687
2688 if(TERM_OPTIMIZE && (i = physical_line(nlp)) != -1)
2689 scrollup(wheadp, i, 1);
2690
2691 hldelete(nlp);
2692
2693 if(!(nlp = h->next)){
2694 free(buf);
2695 if(free_istr)
2696 fs_give((void **) &free_istr);
2697
2698 return(TRUE); /* can't go further */
2699 }
2700 else
2701 retval = TRUE; /* more work to do? */
2702 }
2703 }
2704 }
2705 else{
2706 free(buf);
2707 if(free_istr)
2708 fs_give((void **) &free_istr);
2709
2710 return(FALSE);
2711 }
2712
2713 }
2714
2715 utf8 = ucs4_to_utf8_cpystr(buf);
2716 free(buf);
2717 if(free_istr)
2718 fs_give((void **) &free_istr);
2719
2720 if(utf8){
2721 int rv;
2722
2723 i = FormatLines(nlp, utf8, maxwid, break_on_comma, quoted);
2724 fs_give((void **) &utf8);
2725 switch(i){
2726 case -1: /* bubble up worst case */
2727 rv = -1;
2728 break;
2729 case FALSE:
2730 if(retval == FALSE){
2731 rv = FALSE;
2732 break;
2733 }
2734 default:
2735 rv = TRUE;
2736 }
2737
2738 return(rv);
2739 }
2740 else
2741 return(-1);
2742 }
2743
2744 /*
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.
2748 */
2749 int
FormatSyncAttach(void)2750 FormatSyncAttach(void)
2751 {
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());
2756 }
2757
2758
2759 /*
2760 * PaintHeader - do the work of displaying the header from the given
2761 * physical screen line the end of the header.
2762 *
2763 * 17 July 91 - fixed reshow to deal with arbitrarily large headers.
2764 */
2765 void
PaintHeader(int line,int clear)2766 PaintHeader(int line, /* physical line on screen */
2767 int clear) /* clear before painting */
2768 {
2769 struct hdr_line *lp;
2770 int curline;
2771 int curindex;
2772 int curoffset;
2773 UCS buf[NLINE];
2774 UCS *bufp;
2775 int i, e, w;
2776 COLOR_PAIR *lastc = NULL;
2777
2778 if(Pmaster && Pmaster->colors){
2779 lastc = pico_get_cur_color();
2780 pico_set_colorp(Pmaster->colors->ntcp, PSC_NONE);
2781 }
2782
2783 if(clear)
2784 pclear(COMPOSER_TOP_LINE, ComposerTopLine-1);
2785
2786 curline = COMPOSER_TOP_LINE;
2787 curindex = curoffset = 0;
2788
2789 for(lp = ods.top_l, e = ods.top_e; ; curline++){
2790 if((curline == line) || ((lp = next_hline(&e, lp)) == NULL))
2791 break;
2792 }
2793
2794 while(headents[e].name != NULL){ /* begin to redraw */
2795 while(lp != NULL){
2796 buf[0] = '\0';
2797 if((!lp->prev || curline == COMPOSER_TOP_LINE) && !curoffset){
2798 if(InvertPrompt(e, (e == ods.cur_e && ComposerEditing)) == -1
2799 && !is_blank(curline, 0, headents[e].prwid)){
2800 for(i = 0; i < headents[e].prwid; i++)
2801 buf[i] = ' ';
2802
2803 buf[i] = '\0';
2804 }
2805 }
2806 else if(!is_blank(curline, 0, headents[e].prwid)){
2807 for(i = 0; i < headents[e].prwid; i++)
2808 buf[i] = ' ';
2809
2810 buf[i] = '\0';
2811 }
2812
2813 if(*(bufp = buf) != '\0'){ /* need to paint? */
2814 movecursor(curline, 0); /* paint the line... */
2815 while(*bufp != '\0')
2816 pputc(*bufp++, 0);
2817 }
2818
2819 bufp = &(lp->text[curindex]); /* skip chars already there */
2820 curoffset += headents[e].prwid;
2821 curindex = index_from_col(curline, curoffset);
2822 while(pscr(curline, curindex) != NULL &&
2823 *bufp == pscr(curline, curindex)->c && *bufp != '\0'){
2824 w = wcellwidth(*bufp);
2825 curoffset += (w >= 0 ? w : 1);
2826 ++bufp;
2827 ++curindex;
2828 if(curoffset >= term.t_ncol)
2829 break;
2830 }
2831
2832 if(*bufp != '\0'){ /* need to move? */
2833 movecursor(curline, curoffset);
2834 while(*bufp != '\0'){ /* display what's not there */
2835 pputc(*bufp, 0);
2836 w = wcellwidth(*bufp);
2837 curoffset += (w >= 0 ? w : 1);
2838 ++bufp;
2839 ++curindex;
2840 }
2841 }
2842
2843 if(curoffset < term.t_ncol){
2844 movecursor(curline, curoffset);
2845 peeol();
2846 }
2847 curline++;
2848 curindex = curoffset = 0;
2849 if(curline >= BOTTOM())
2850 break;
2851
2852 lp = lp->next;
2853 }
2854
2855 if(curline >= BOTTOM())
2856 return; /* don't paint delimiter */
2857
2858 while(headents[++e].name != NULL)
2859 if(headents[e].display_it){
2860 lp = headents[e].hd_text;
2861 break;
2862 }
2863 }
2864
2865 display_delimiter(ComposerEditing ? 0 : 1);
2866
2867 if(lastc){
2868 pico_set_colorp(lastc, PSC_NONE);
2869 free_color_pair(&lastc);
2870 }
2871 }
2872
2873
2874
2875 /*
2876 * PaintBody() - generic call to handle repainting everything BUT the
2877 * header
2878 *
2879 * notes:
2880 * The header redrawing in a level 0 body paint gets done
2881 * in update()
2882 */
2883 void
PaintBody(int level)2884 PaintBody(int level)
2885 {
2886 curwp->w_flag |= WFHARD; /* make sure framing's right */
2887 if(level == 0) /* specify what to update */
2888 sgarbf = TRUE;
2889
2890 update(); /* display message body */
2891
2892 if(level == 0 && ComposerEditing){
2893 mlerase(); /* clear the error line */
2894 ShowPrompt();
2895 }
2896 }
2897
2898
2899 /*
2900 * display_for_send - paint the composer from the top line and return.
2901 */
2902 void
display_for_send(void)2903 display_for_send(void)
2904 {
2905 int i = 0;
2906 struct hdr_line *l;
2907
2908 /* if first header line isn't displayed, there's work to do */
2909 if(headents && ((l = first_hline(&i)) != ods.top_l
2910 || ComposerTopLine == COMPOSER_TOP_LINE
2911 || !ods.p_line)){
2912 struct on_display orig_ods;
2913 int orig_edit = ComposerEditing,
2914 orig_ct_line = ComposerTopLine;
2915
2916 /*
2917 * fake that the cursor's in the first header line
2918 * and force repaint...
2919 */
2920 orig_ods = ods;
2921 ods.cur_e = i;
2922 ods.top_l = ods.cur_l = l;
2923 ods.top_e = ods.cur_e;
2924 ods.p_line = COMPOSER_TOP_LINE;
2925 ComposerEditing = TRUE; /* to fool update() */
2926 setimark(FALSE, 1); /* remember where we were */
2927 gotobob(FALSE, 1);
2928
2929 UpdateHeader(0); /* redraw whole enchilada */
2930 PaintHeader(COMPOSER_TOP_LINE, TRUE);
2931 PaintBody(0);
2932
2933 ods = orig_ods; /* restore original state */
2934 ComposerEditing = orig_edit;
2935 ComposerTopLine = curwp->w_toprow = orig_ct_line;
2936 curwp->w_ntrows = BOTTOM() - ComposerTopLine;
2937 swapimark(FALSE, 1);
2938
2939 /* in case we don't exit, set up restoring the screen */
2940 sgarbf = TRUE; /* force redraw */
2941 }
2942 }
2943
2944
2945 /*
2946 * ArrangeHeader - set up display parm such that header is reasonably
2947 * displayed
2948 */
2949 void
ArrangeHeader(void)2950 ArrangeHeader(void)
2951 {
2952 int e;
2953 register struct hdr_line *l;
2954
2955 ods.p_line = ods.p_ind = 0;
2956 e = ods.top_e = 0;
2957 l = ods.top_l = headents[e].hd_text;
2958 while(headents[e+1].name || (l && l->next))
2959 if((l = next_sel_hline(&e, l)) != NULL){
2960 ods.cur_l = l;
2961 ods.cur_e = e;
2962 }
2963
2964 UpdateHeader(1);
2965 }
2966
2967
2968 /*
2969 * ComposerHelp() - display mail help in a context sensitive way
2970 * based on the level passed ...
2971 */
2972 int
ComposerHelp(int level)2973 ComposerHelp(int level)
2974 {
2975 char buf[80];
2976 VARS_TO_SAVE *saved_state;
2977
2978 curwp->w_flag |= WFMODE;
2979 sgarbf = TRUE;
2980
2981 if(level < 0 || !headents[level].name){
2982 (*term.t_beep)();
2983 emlwrite("Sorry, I can't help you with that.", NULL);
2984 sleep(2);
2985 return(FALSE);
2986 }
2987
2988 snprintf(buf, sizeof(buf), "Help for %s %.40s Field",
2989 (Pmaster->pine_flags & MDHDRONLY) ? "Address Book"
2990 : "Composer",
2991 headents[level].name);
2992 saved_state = save_pico_state();
2993 (*Pmaster->helper)(headents[level].help, buf, 1);
2994 if(saved_state){
2995 restore_pico_state(saved_state);
2996 free_pico_state(saved_state);
2997 }
2998
2999 ttresize();
3000 picosigs(); /* restore altered handlers */
3001 return(TRUE);
3002 }
3003
3004
3005
3006 /*
3007 * ToggleHeader() - set or unset pico values to the full screen size
3008 * painting header if need be.
3009 */
3010 int
ToggleHeader(int show)3011 ToggleHeader(int show)
3012 {
3013 /*
3014 * check to see if we need to display the header...
3015 */
3016 if(show){
3017 UpdateHeader(0); /* figure bounds */
3018 PaintHeader(COMPOSER_TOP_LINE, FALSE); /* draw it */
3019 }
3020 else{
3021 /*
3022 * set bounds for no header display
3023 */
3024 curwp->w_toprow = ComposerTopLine = COMPOSER_TOP_LINE;
3025 curwp->w_ntrows = BOTTOM() - ComposerTopLine;
3026 }
3027 return(TRUE);
3028 }
3029
3030
3031
3032 /*
3033 * HeaderLen() - return the length in lines of the exposed portion of the
3034 * header
3035 */
3036 int
HeaderLen(void)3037 HeaderLen(void)
3038 {
3039 register struct hdr_line *lp;
3040 int e;
3041 int i;
3042
3043 i = 1;
3044 lp = ods.top_l;
3045 e = ods.top_e;
3046 while(lp != NULL){
3047 lp = next_hline(&e, lp);
3048 i++;
3049 }
3050 return(i);
3051 }
3052
3053
3054
3055 /*
3056 * first_hline() - return a pointer to the first displayable header line
3057 *
3058 * returns:
3059 * 1) pointer to first displayable line in header and header
3060 * entry, via side effect, that the first line is a part of
3061 * 2) NULL if no next line, leaving entry at LASTHDR
3062 */
3063 struct hdr_line *
first_hline(int * entry)3064 first_hline(int *entry)
3065 {
3066 /* init *entry so we're sure to start from the top */
3067 for(*entry = 0; headents[*entry].name; (*entry)++)
3068 if(headents[*entry].display_it)
3069 return(headents[*entry].hd_text);
3070
3071 *entry = 0;
3072 return(NULL); /* this shouldn't happen */
3073 }
3074
3075
3076 /*
3077 * first_sel_hline() - return a pointer to the first selectable header line
3078 *
3079 * returns:
3080 * 1) pointer to first selectable line in header and header
3081 * entry, via side effect, that the first line is a part of
3082 * 2) NULL if no next line, leaving entry at LASTHDR
3083 */
3084 struct hdr_line *
first_sel_hline(int * entry)3085 first_sel_hline(int *entry)
3086 {
3087 /* init *entry so we're sure to start from the top */
3088 for(*entry = 0; headents[*entry].name; (*entry)++)
3089 if(headents[*entry].display_it && !headents[*entry].blank)
3090 return(headents[*entry].hd_text);
3091
3092 *entry = 0;
3093 return(NULL); /* this shouldn't happen */
3094 }
3095
3096
3097
3098 /*
3099 * next_hline() - return a pointer to the next line structure
3100 *
3101 * returns:
3102 * 1) pointer to next displayable line in header and header
3103 * entry, via side effect, that the next line is a part of
3104 * 2) NULL if no next line, leaving entry at LASTHDR
3105 */
3106 struct hdr_line *
next_hline(int * entry,struct hdr_line * line)3107 next_hline(int *entry, struct hdr_line *line)
3108 {
3109 if(line == NULL)
3110 return(NULL);
3111
3112 if(line->next == NULL){
3113 while(headents[++(*entry)].name != NULL){
3114 if(headents[*entry].display_it)
3115 return(headents[*entry].hd_text);
3116 }
3117 --(*entry);
3118 return(NULL);
3119 }
3120 else
3121 return(line->next);
3122 }
3123
3124
3125 /*
3126 * next_sel_hline() - return a pointer to the next selectable line structure
3127 *
3128 * returns:
3129 * 1) pointer to next selectable line in header and header
3130 * entry, via side effect, that the next line is a part of
3131 * 2) NULL if no next line, leaving entry at LASTHDR
3132 */
3133 struct hdr_line *
next_sel_hline(int * entry,struct hdr_line * line)3134 next_sel_hline(int *entry, struct hdr_line *line)
3135 {
3136 if(line == NULL)
3137 return(NULL);
3138
3139 if(line->next == NULL){
3140 while(headents[++(*entry)].name != NULL){
3141 if(headents[*entry].display_it && !headents[*entry].blank)
3142 return(headents[*entry].hd_text);
3143 }
3144 --(*entry);
3145 return(NULL);
3146 }
3147 else
3148 return(line->next);
3149 }
3150
3151
3152
3153 /*
3154 * prev_hline() - return a pointer to the next line structure back
3155 *
3156 * returns:
3157 * 1) pointer to previous displayable line in header and
3158 * the header entry that the next line is a part of
3159 * via side effect
3160 * 2) NULL if no next line, leaving entry unchanged from
3161 * the value it had on entry.
3162 */
3163 struct hdr_line *
prev_hline(int * entry,struct hdr_line * line)3164 prev_hline(int *entry, struct hdr_line *line)
3165 {
3166 if(line == NULL)
3167 return(NULL);
3168
3169 if(line->prev == NULL){
3170 int orig_entry;
3171
3172 orig_entry = *entry;
3173 while(--(*entry) >= 0){
3174 if(headents[*entry].display_it){
3175 line = headents[*entry].hd_text;
3176 while(line->next != NULL)
3177 line = line->next;
3178 return(line);
3179 }
3180 }
3181
3182 *entry = orig_entry;
3183 return(NULL);
3184 }
3185 else
3186 return(line->prev);
3187 }
3188
3189
3190 /*
3191 * prev_sel_hline() - return a pointer to the previous selectable line
3192 *
3193 * returns:
3194 * 1) pointer to previous selectable line in header and
3195 * the header entry that the next line is a part of
3196 * via side effect
3197 * 2) NULL if no next line, leaving entry unchanged from
3198 * the value it had on entry.
3199 */
3200 struct hdr_line *
prev_sel_hline(int * entry,struct hdr_line * line)3201 prev_sel_hline(int *entry, struct hdr_line *line)
3202 {
3203 if(line == NULL)
3204 return(NULL);
3205
3206 if(line->prev == NULL){
3207 int orig_entry;
3208
3209 orig_entry = *entry;
3210 while(--(*entry) >= 0){
3211 if(headents[*entry].display_it && !headents[*entry].blank){
3212 line = headents[*entry].hd_text;
3213 while(line->next != NULL)
3214 line = line->next;
3215 return(line);
3216 }
3217 }
3218
3219 *entry = orig_entry;
3220 return(NULL);
3221 }
3222 else
3223 return(line->prev);
3224 }
3225
3226
3227
3228 /*
3229 * first_requested_hline() - return pointer to first line that pico's caller
3230 * asked that we start on.
3231 */
3232 struct hdr_line *
first_requested_hline(int * ent)3233 first_requested_hline(int *ent)
3234 {
3235 int i, reqfield;
3236 struct hdr_line *rv = NULL;
3237
3238 for(reqfield = -1, i = 0; headents[i].name; i++)
3239 if(headents[i].start_here){
3240 headents[i].start_here = 0; /* clear old setting */
3241 if(reqfield < 0){ /* if not already, set up */
3242 headents[i].display_it = 1; /* make sure it's shown */
3243 *ent = reqfield = i;
3244 rv = headents[i].hd_text;
3245 }
3246 }
3247
3248 return(rv);
3249 }
3250
3251
3252
3253 /*
3254 * UpdateHeader() - determines the best range of lines to be displayed
3255 * using the global ods value for the current line and the
3256 * top line, also sets ComposerTopLine and pico limits
3257 *
3258 * showtop -- Attempt to show all header lines if they'll fit.
3259 *
3260 * notes:
3261 * This is pretty ugly because it has to keep the current line
3262 * on the screen in a reasonable location no matter what.
3263 * There are also a couple of rules to follow:
3264 * 1) follow paging conventions of pico (ie, half page
3265 * scroll)
3266 * 2) if more than one page, always display last half when
3267 * pline is toward the end of the header
3268 *
3269 * returns:
3270 * TRUE if anything changed (side effects: new p_line, top_l
3271 * top_e, and pico parms)
3272 * FALSE if nothing changed
3273 *
3274 */
3275 int
UpdateHeader(int showtop)3276 UpdateHeader(int showtop)
3277 {
3278 register struct hdr_line *lp;
3279 int i, le;
3280 int ret = FALSE;
3281 int old_top = ComposerTopLine;
3282 int old_p = ods.p_line;
3283
3284 if(ods.p_line < COMPOSER_TOP_LINE ||
3285 ((ods.p_line == ComposerTopLine-2) ? 2: 0) + ods.p_line >= BOTTOM()){
3286 /* NewTop if cur header line is at bottom of screen or two from */
3287 /* the bottom of the screen if cur line is bottom header line */
3288 NewTop(showtop); /* get new top_l */
3289 ret = TRUE;
3290 }
3291 else{ /* make sure p_line's OK */
3292 i = COMPOSER_TOP_LINE;
3293 lp = ods.top_l;
3294 le = ods.top_e;
3295 while(lp != ods.cur_l){
3296 /*
3297 * this checks to make sure cur_l is below top_l and that
3298 * cur_l is on the screen...
3299 */
3300 if((lp = next_hline(&le, lp)) == NULL || ++i >= BOTTOM()){
3301 NewTop(0);
3302 ret = TRUE;
3303 break;
3304 }
3305 }
3306 }
3307
3308 ods.p_line = COMPOSER_TOP_LINE; /* find p_line... */
3309 lp = ods.top_l;
3310 le = ods.top_e;
3311 while(lp && lp != ods.cur_l){
3312 lp = next_hline(&le, lp);
3313 ods.p_line++;
3314 }
3315
3316 if(!ret)
3317 ret = !(ods.p_line == old_p);
3318
3319 ComposerTopLine = ods.p_line; /* figure top composer line */
3320 while(lp && ComposerTopLine <= BOTTOM()){
3321 lp = next_hline(&le, lp);
3322 ComposerTopLine += (lp) ? 1 : 2; /* allow for delim at end */
3323 }
3324
3325 if(!ret)
3326 ret = !(ComposerTopLine == old_top);
3327
3328 if(wheadp->w_toprow != ComposerTopLine){ /* update pico params... */
3329 wheadp->w_toprow = ComposerTopLine;
3330 wheadp->w_ntrows = ((i = BOTTOM() - ComposerTopLine) > 0) ? i : 0;
3331 ret = TRUE;
3332 }
3333 return(ret);
3334 }
3335
3336
3337
3338 /*
3339 * NewTop() - calculate a new top_l based on the cur_l
3340 *
3341 * showtop -- Attempt to show all the header lines if they'll fit
3342 *
3343 * returns:
3344 * with ods.top_l and top_e pointing at a reasonable line
3345 * entry
3346 */
3347 void
NewTop(int showtop)3348 NewTop(int showtop)
3349 {
3350 register struct hdr_line *lp;
3351 register int i;
3352 int e;
3353
3354 lp = ods.cur_l;
3355 e = ods.cur_e;
3356 i = showtop ? FULL_SCR() : HALF_SCR();
3357
3358 while(lp != NULL && (--i > 0)){
3359 ods.top_l = lp;
3360 ods.top_e = e;
3361 lp = prev_hline(&e, lp);
3362 }
3363 }
3364
3365
3366
3367 /*
3368 * display_delimiter() - just paint the header/message body delimiter with
3369 * inverse value specified by state.
3370 */
3371 void
display_delimiter(int state)3372 display_delimiter(int state)
3373 {
3374 UCS *bufp, *buf;
3375 COLOR_PAIR *lastc = NULL;
3376
3377 if(ComposerTopLine - 1 >= BOTTOM()) /* silently forget it */
3378 return;
3379
3380 if(Pmaster && Pmaster->colors){
3381 lastc = pico_get_cur_color();
3382 pico_set_colorp(Pmaster->colors->ntcp, PSC_NONE);
3383 }
3384
3385 buf = utf8_to_ucs4_cpystr((gmode & MDHDRONLY) ? "" : HDR_DELIM);
3386 if(!buf)
3387 return;
3388
3389 bufp = buf;
3390
3391 if(state == delim_ps){ /* optimize ? */
3392 for(delim_ps = 0; bufp[delim_ps] && pscr(ComposerTopLine-1,delim_ps) != NULL && pscr(ComposerTopLine-1,delim_ps)->c == bufp[delim_ps];delim_ps++)
3393 ;
3394
3395 if(bufp[delim_ps] == '\0' && !(gmode & MDHDRONLY)){
3396 delim_ps = state;
3397 fs_give((void **) &buf);
3398 if(lastc) free_color_pair(&lastc);
3399 return; /* already displayed! */
3400 }
3401 }
3402
3403 delim_ps = state;
3404
3405 movecursor(ComposerTopLine - 1, 0);
3406 if(state)
3407 (*term.t_rev)(1);
3408 else if (*term.t_eri)
3409 (*term.t_eri)();
3410
3411 while(*bufp != '\0')
3412 pputc(*bufp++, state ? 1 : 0);
3413
3414 if(state)
3415 (*term.t_rev)(0);
3416
3417 peeol();
3418 fs_give((void **) &buf);
3419
3420 if(lastc){
3421 pico_set_colorp(lastc, PSC_NONE);
3422 free_color_pair(&lastc);
3423 }
3424 }
3425
3426
3427
3428 /*
3429 * InvertPrompt() - invert the prompt associated with header entry to state
3430 * state (true if invert, false otherwise).
3431 * returns:
3432 * non-zero if nothing done
3433 * 0 if prompt inverted successfully
3434 *
3435 * notes:
3436 * come to think of it, this func and the one above could
3437 * easily be combined
3438 */
3439 int
InvertPrompt(int entry,int state)3440 InvertPrompt(int entry, int state)
3441 {
3442 UCS *buf, *bufp;
3443 UCS *end;
3444 int i;
3445
3446 buf = utf8_to_ucs4_cpystr(headents[entry].prompt); /* fresh prompt paint */
3447 if(!buf)
3448 return(-1);
3449
3450 bufp = buf;
3451 if((i = entry_line(entry, FALSE)) == -1){
3452 fs_give((void **) &buf);
3453 return(-1); /* silently forget it */
3454 }
3455
3456 end = buf + ucs4_strlen(buf);
3457
3458 /*
3459 * Makes sure that the prompt doesn't take up more than prwid of screen space.
3460 * The caller should do that, too, in order to make it look right so
3461 * this should most likely be a no-op
3462 */
3463 if(ucs4_str_width_ptr_to_ptr(buf, end) > headents[entry].prwid){
3464 end = ucs4_particular_width(buf, headents[entry].prwid);
3465 *end = '\0';
3466 }
3467
3468 if(entry < 16 && (invert_ps&(1<<entry))
3469 == (state ? 1<<entry : 0)){ /* optimize ? */
3470 int j;
3471
3472 for(j = 0; bufp[j] && pscr(i, j)->c == bufp[j]; j++)
3473 ;
3474
3475 if(bufp[j] == '\0'){
3476 if(state)
3477 invert_ps |= 1<<entry;
3478 else
3479 invert_ps &= ~(1<<entry);
3480
3481 fs_give((void **) &buf);
3482 return(0); /* already displayed! */
3483 }
3484 }
3485
3486 if(entry < 16){ /* if > 16, cannot be stored in invert_ps */
3487 if(state)
3488 invert_ps |= 1<<entry;
3489 else
3490 invert_ps &= ~(1<<entry);
3491 }
3492
3493 movecursor(i, 0);
3494 if(state)
3495 (*term.t_rev)(1);
3496 else if (*term.t_eri)
3497 (*term.t_eri)();
3498
3499 while(*bufp && *(bufp + 1))
3500 pputc(*bufp++, 1); /* putc up to last char */
3501
3502 if(state)
3503 (*term.t_rev)(0);
3504
3505 pputc(*bufp, 0); /* last char not inverted */
3506
3507 fs_give((void **) &buf);
3508
3509 return(TRUE);
3510 }
3511
3512
3513
3514 /*
3515 * partial_entries() - toggle display of the bcc and fcc fields.
3516 *
3517 * returns:
3518 * TRUE if there are partial entries on the display
3519 * FALSE otherwise.
3520 */
3521 int
partial_entries(void)3522 partial_entries(void)
3523 {
3524 register struct headerentry *h;
3525 int is_on;
3526
3527 /*---- find out status of first rich header ---*/
3528 for(h = headents; !h->rich_header && h->name != NULL; h++)
3529 ;
3530
3531 is_on = h->display_it;
3532 for(h = headents; h->name != NULL; h++)
3533 if(h->rich_header)
3534 h->display_it = ! is_on;
3535
3536 return(is_on);
3537 }
3538
3539
3540
3541 /*
3542 * entry_line() - return the physical line on the screen associated
3543 * with the given header entry field. Note: the field
3544 * may span lines, so if the last char is set, return
3545 * the appropriate value.
3546 *
3547 * returns:
3548 * 1) physical line number of entry
3549 * 2) -1 if entry currently not on display
3550 */
3551 int
entry_line(int entry,int lastchar)3552 entry_line(int entry, int lastchar)
3553 {
3554 register int p_line = COMPOSER_TOP_LINE;
3555 int i;
3556 register struct hdr_line *line;
3557
3558 for(line = ods.top_l, i = ods.top_e;
3559 headents && headents[i].name && i <= entry;
3560 p_line++){
3561 if(p_line >= BOTTOM())
3562 break;
3563 if(i == entry){
3564 if(lastchar){
3565 if(line->next == NULL)
3566 return(p_line);
3567 }
3568 else if(line->prev == NULL)
3569 return(p_line);
3570 else
3571 return(-1);
3572 }
3573 line = next_hline(&i, line);
3574 }
3575 return(-1);
3576 }
3577
3578
3579
3580 /*
3581 * physical_line() - return the physical line on the screen associated
3582 * with the given header line pointer.
3583 *
3584 * returns:
3585 * 1) physical line number of entry
3586 * 2) -1 if entry currently not on display
3587 */
3588 int
physical_line(struct hdr_line * l)3589 physical_line(struct hdr_line *l)
3590 {
3591 register int p_line = COMPOSER_TOP_LINE;
3592 register struct hdr_line *lp;
3593 int i;
3594
3595 for(lp=ods.top_l, i=ods.top_e; headents[i].name && lp != NULL; p_line++){
3596 if(p_line >= BOTTOM())
3597 break;
3598
3599 if(lp == l)
3600 return(p_line);
3601
3602 lp = next_hline(&i, lp);
3603 }
3604 return(-1);
3605 }
3606
3607
3608
3609 /*
3610 * call_builder() - resolve any nicknames in the address book associated
3611 * with the given entry...
3612 *
3613 * NOTES:
3614 *
3615 * BEWARE: this function can cause cur_l and top_l to get lost so BE
3616 * CAREFUL before and after you call this function!!!
3617 *
3618 * There could to be something here to resolve cur_l and top_l
3619 * reasonably into the new linked list for this entry.
3620 *
3621 * The reason this would mostly work without it is resolve_niks gets
3622 * called for the most part in between fields. Since we're moving
3623 * to the beginning or end (i.e. the next/prev pointer in the old
3624 * freed cur_l is NULL) of the next entry, we get a new cur_l
3625 * pointing at a good line. Then since top_l is based on cur_l in
3626 * NewTop() we have pretty much lucked out.
3627 *
3628 * Where we could get burned is in a canceled exit (ctrl|x). Here
3629 * nicknames get resolved into addresses, which invalidates cur_l
3630 * and top_l. Since we don't actually leave, we could begin editing
3631 * again with bad pointers. This would usually results in a nice
3632 * core dump.
3633 *
3634 * NOTE: The mangled argument is a little strange. It's used on both
3635 * input and output. On input, if it is not set, then that tells the
3636 * builder not to do anything that might take a long time, like a
3637 * white pages lookup. On return, it tells the caller that the screen
3638 * and signals may have been mangled so signals should be reset, window
3639 * resized, and screen redrawn.
3640 *
3641 * RETURNS:
3642 * > 0 if any names where resolved, otherwise
3643 * 0 if not, or
3644 * < 0 on error
3645 * -1: move to next line
3646 * -2: don't move off this line
3647 */
3648 int
call_builder(struct headerentry * entry,int * mangled,char ** err)3649 call_builder(struct headerentry *entry, int *mangled, char **err)
3650 {
3651 register int retval = 0;
3652 register int i;
3653 register struct hdr_line *line;
3654 int quoted = 0;
3655 int sbuflen;
3656 char *sbuf;
3657 char *s = NULL;
3658 char *tmp;
3659 struct headerentry *e;
3660 BUILDER_ARG *nextarg, *arg = NULL, *headarg = NULL;
3661 VARS_TO_SAVE *saved_state;
3662
3663 if(!entry->builder)
3664 return(0);
3665
3666 line = entry->hd_text;
3667 sbuflen = 0;
3668 while(line != NULL){
3669 sbuflen += (6*term.t_ncol);
3670 line = line->next;
3671 }
3672 sbuflen++;
3673 if((sbuf=(char *)malloc(sbuflen * sizeof(*sbuf))) == NULL){
3674 emlwrite("Can't malloc space to expand address", NULL);
3675 return(-1);
3676 }
3677
3678 *sbuf = '\0';
3679
3680 /*
3681 * cat the whole entry into one string...
3682 */
3683 line = entry->hd_text;
3684 while(line != NULL){
3685 i = ucs4_strlen(line->text);
3686
3687 /*
3688 * To keep pine address builder happy, addresses should be separated
3689 * by ", ". Add this space if needed, otherwise...
3690 * (This is some ancient requirement that is no longer needed.)
3691 *
3692 * If this line is NOT a continuation of the previous line, add
3693 * white space for pine's address builder if its not already there...
3694 * (This is some ancient requirement that is no longer needed.)
3695 *
3696 * Also if it's not a continuation (i.e., there's already and addr on
3697 * the line), and there's another line below, treat the new line as
3698 * an implied comma.
3699 * (This should only be done for address-type lines, not for regular
3700 * text lines like subjects. Key off of the break_on_comma bit which
3701 * should only be set on those that won't mind a comma being added.)
3702 */
3703 if(entry->break_on_comma){
3704 UCS *space, commaspace[3];
3705
3706 commaspace[0] = ',';
3707 commaspace[1] = ' ';
3708 commaspace[2] = '\0';
3709 space = commaspace+1;
3710
3711 if(i && line->text[i-1] == ','){
3712 ucs4_strncat(line->text, space, HLSZ-i-1); /* help address builder */
3713 line->text[HLSZ-1] = '\0';
3714 }
3715 else if(line->next != NULL && !strend(line->text, ',')){
3716 if(ucs4_strqchr(line->text, ',', "ed, -1)){
3717 ucs4_strncat(line->text, commaspace, HLSZ-i-1); /* implied comma */
3718 line->text[HLSZ-1] = '\0';
3719 }
3720 }
3721 else if(line->prev != NULL && line->next != NULL){
3722 if(ucs4_strchr(line->prev->text, ' ') != NULL
3723 && line->text[i-1] != ' '){
3724 ucs4_strncat(line->text, space, HLSZ-i-1);
3725 line->text[HLSZ-1] = '\0';
3726 }
3727 }
3728 }
3729
3730 tmp = ucs4_to_utf8_cpystr(line->text);
3731 if(tmp){
3732 strncat(sbuf, tmp, sbuflen-strlen(sbuf)-1);
3733 sbuf[sbuflen-1] = '\0';
3734 fs_give((void **) &tmp);
3735 }
3736
3737 line = line->next;
3738 }
3739
3740 if(entry->affected_entry){
3741 /* check if any non-sticky affected entries */
3742 for(e = entry->affected_entry; e; e = e->next_affected)
3743 if(!e->sticky)
3744 break;
3745
3746 /* there is at least one non-sticky so make a list to pass */
3747 if(e){
3748 for(e = entry->affected_entry; e; e = e->next_affected){
3749 if(!arg){
3750 headarg = arg = (BUILDER_ARG *)malloc(sizeof(BUILDER_ARG));
3751 if(!arg){
3752 emlwrite("Can't malloc space for fcc", NULL);
3753 return(-1);
3754 }
3755 else{
3756 arg->next = NULL;
3757 arg->tptr = NULL;
3758 arg->aff = &(e->bldr_private);
3759 arg->me = &(entry->bldr_private);
3760 }
3761 }
3762 else{
3763 nextarg = (BUILDER_ARG *)malloc(sizeof(BUILDER_ARG));
3764 if(!nextarg){
3765 emlwrite("Can't malloc space for fcc", NULL);
3766 return(-1);
3767 }
3768 else{
3769 nextarg->next = NULL;
3770 nextarg->tptr = NULL;
3771 nextarg->aff = &(e->bldr_private);
3772 nextarg->me = &(entry->bldr_private);
3773 arg->next = nextarg;
3774 arg = arg->next;
3775 }
3776 }
3777
3778 if(!e->sticky){
3779 line = e->hd_text;
3780 arg->tptr = ucs4_to_utf8_cpystr(line->text);
3781 }
3782 }
3783 }
3784 }
3785
3786 /*
3787 * Even if there are no affected entries, we still need the arg
3788 * to pass the "me" pointer.
3789 */
3790 if(!headarg){
3791 headarg = (BUILDER_ARG *)malloc(sizeof(BUILDER_ARG));
3792 if(!headarg){
3793 emlwrite("Can't malloc space", NULL);
3794 return(-1);
3795 }
3796 else{
3797 headarg->next = NULL;
3798 headarg->tptr = NULL;
3799 headarg->aff = NULL;
3800 headarg->me = &(entry->bldr_private);
3801 }
3802 }
3803
3804 /*
3805 * The builder may make a new call back to pico() so we save and
3806 * restore the pico state.
3807 */
3808 saved_state = save_pico_state();
3809 retval = (*entry->builder)(sbuf, &s, err, headarg, mangled);
3810 if(saved_state){
3811 restore_pico_state(saved_state);
3812 free_pico_state(saved_state);
3813 }
3814
3815 if(mangled && *mangled & BUILDER_MESSAGE_DISPLAYED){
3816 *mangled &= ~ BUILDER_MESSAGE_DISPLAYED;
3817 if(mpresf == FALSE)
3818 mpresf = TRUE;
3819 }
3820
3821 if(mangled && *mangled & BUILDER_FOOTER_MANGLED){
3822 *mangled &= ~ BUILDER_FOOTER_MANGLED;
3823 sgarbk = TRUE;
3824 pclear(term.t_nrow-1, term.t_nrow);
3825 }
3826
3827 if(retval >= 0){
3828 if(strcmp(sbuf, s)){
3829 line = entry->hd_text;
3830 InitEntryText(s, entry); /* arrange new one */
3831 zotentry(line); /* blast old list o'entries */
3832 entry->dirty = 1; /* mark it dirty */
3833 retval = 1;
3834 }
3835
3836 for(e = entry->affected_entry, arg = headarg;
3837 e;
3838 e = e->next_affected, arg = arg ? arg->next : NULL){
3839 if(!e->sticky){
3840 line = e->hd_text;
3841 tmp = ucs4_to_utf8_cpystr(line->text);
3842 if(strcmp(tmp, arg ? arg->tptr : "")){ /* it changed */
3843 /* make sure they see it if changed */
3844 e->display_it = 1;
3845 InitEntryText(arg ? arg->tptr : "", e);
3846 if(line == ods.top_l)
3847 ods.top_l = e->hd_text;
3848
3849 zotentry(line); /* blast old list o'entries */
3850 e->dirty = 1; /* mark it dirty */
3851 retval = 1;
3852 }
3853
3854 if(tmp)
3855 fs_give((void **) &tmp);
3856 }
3857 }
3858 }
3859
3860 if(s)
3861 free(s);
3862
3863 for(arg = headarg; arg; arg = nextarg){
3864 /* Don't free xtra or me, they just point to headerentry data */
3865 nextarg = arg->next;
3866 if(arg->tptr)
3867 free(arg->tptr);
3868
3869 free(arg);
3870 }
3871
3872 free(sbuf);
3873 return(retval);
3874 }
3875
3876
3877 void
call_expander(void)3878 call_expander(void)
3879 {
3880 char **s = NULL;
3881 VARS_TO_SAVE *saved_state;
3882 int expret;
3883
3884 if(!Pmaster->expander)
3885 return;
3886
3887 /*
3888 * Since expander may make a call back to pico() we need to
3889 * save and restore pico state.
3890 */
3891 if((saved_state = save_pico_state()) != NULL){
3892
3893 expret = (*Pmaster->expander)(headents, &s);
3894
3895 restore_pico_state(saved_state);
3896 free_pico_state(saved_state);
3897 ttresize();
3898 picosigs();
3899
3900 if(expret > 0 && s){
3901 char *tbuf, *p;
3902 int i, biggest = 100;
3903 struct headerentry *e;
3904
3905 /*
3906 * Use tbuf to cat together multiple line entries before comparing.
3907 */
3908 tbuf = (char *)malloc((biggest+1) * sizeof(*tbuf));
3909 for(e = headents, i=0; e->name != NULL; e++,i++){
3910 int sz = 0;
3911 struct hdr_line *line;
3912
3913 while(e->name && e->blank)
3914 e++;
3915
3916 if(e->name == NULL)
3917 continue;
3918
3919 for(line = e->hd_text; line != NULL; line = line->next){
3920 p = ucs4_to_utf8_cpystr(line->text);
3921 if(p){
3922 sz += strlen(p);
3923 fs_give((void **) &p);
3924 }
3925 }
3926
3927 if(sz > biggest){
3928 biggest = sz;
3929 free(tbuf);
3930 tbuf = (char *)malloc((biggest+1) * sizeof(*tbuf));
3931 }
3932
3933 tbuf[0] = '\0';
3934 for(line = e->hd_text; line != NULL; line = line->next){
3935 p = ucs4_to_utf8_cpystr(line->text);
3936 if(p){
3937 strncat(tbuf, p, biggest+1-strlen(tbuf)-1);
3938 tbuf[biggest] = '\0';
3939 fs_give((void **) &p);
3940 }
3941 }
3942
3943 if(strcmp(tbuf, s[i])){ /* it changed */
3944 struct hdr_line *zline;
3945
3946 line = zline = e->hd_text;
3947 InitEntryText(s[i], e);
3948
3949 /*
3950 * If any of the lines for this entry are current or
3951 * top, fix that.
3952 */
3953 for(; line != NULL; line = line->next){
3954 if(line == ods.top_l)
3955 ods.top_l = e->hd_text;
3956
3957 if(line == ods.cur_l)
3958 ods.cur_l = e->hd_text;
3959 }
3960
3961 zotentry(zline); /* blast old list o'entries */
3962 }
3963 }
3964
3965 free(tbuf);
3966 }
3967
3968 if(s){
3969 char **p;
3970
3971 for(p = s; *p; p++)
3972 free(*p);
3973
3974 free(s);
3975 }
3976 }
3977
3978 return;
3979 }
3980
3981
3982 /*
3983 * strend - neglecting white space, returns TRUE if c is at the
3984 * end of the given line. otherwise FALSE.
3985 */
3986 int
strend(UCS * s,UCS ch)3987 strend(UCS *s, UCS ch)
3988 {
3989 UCS *b;
3990
3991 if(s == NULL || *s == '\0')
3992 return(FALSE);
3993
3994 for(b = &s[ucs4_strlen(s)] - 1; *b && ucs4_isspace(*b); b--){
3995 if(b == s)
3996 return(FALSE);
3997 }
3998
3999 return(*b == ch);
4000 }
4001
4002
4003 /*
4004 * ucs4_strqchr - returns pointer to first non-quote-enclosed occurance of ch in
4005 * the given string. otherwise NULL.
4006 * s -- the string
4007 * ch -- the character we're looking for
4008 * q -- q tells us if we start out inside quotes on entry and is set
4009 * correctly on exit.
4010 * m -- max characters we'll check for ch (set to -1 for no max)
4011 */
4012 UCS *
ucs4_strqchr(UCS * s,UCS ch,int * q,int m)4013 ucs4_strqchr(UCS *s, UCS ch, int *q, int m)
4014 {
4015 int quoted = (q) ? *q : 0;
4016
4017 for(; s && *s && m != 0; s++, m--){
4018 if(*s == '"'){
4019 quoted = !quoted;
4020 if(q)
4021 *q = quoted;
4022 }
4023
4024 if(!quoted && *s == ch)
4025 return(s);
4026 }
4027
4028 return(NULL);
4029 }
4030
4031
4032 /*
4033 * KillHeaderLine() - kill a line in the header
4034 *
4035 * notes:
4036 * This is pretty simple. Just using the emacs kill buffer
4037 * and its accompanying functions to cut the text from lines.
4038 *
4039 * returns:
4040 * TRUE if hldelete worked
4041 * FALSE otherwise
4042 */
4043 int
KillHeaderLine(struct hdr_line * l,int append)4044 KillHeaderLine(struct hdr_line *l, int append)
4045 {
4046 UCS *c;
4047 int i = ods.p_ind;
4048 int nl = TRUE;
4049
4050 if(!append)
4051 kdelete();
4052
4053 c = l->text;
4054 if (gmode & MDDTKILL){
4055 if (c[i] == '\0') /* don't insert a new line after this line*/
4056 nl = FALSE;
4057 /*put to be deleted part into kill buffer */
4058 for (i=ods.p_ind; c[i] != '\0'; i++)
4059 kinsert(c[i]);
4060 }else{
4061 while(*c != '\0') /* splat out the line */
4062 kinsert(*c++);
4063 }
4064
4065 if (nl)
4066 kinsert('\n'); /* helpful to yank in body */
4067
4068 #ifdef _WINDOWS
4069 mswin_killbuftoclip (kremove);
4070 #endif
4071
4072 if (gmode & MDDTKILL){
4073 if (l->text[0]=='\0'){
4074
4075 if(l->next && l->prev)
4076 ods.cur_l = next_hline(&ods.cur_e, l);
4077 else if(l->prev)
4078 ods.cur_l = prev_hline(&ods.cur_e, l);
4079
4080 if(l == ods.top_l)
4081 ods.top_l = ods.cur_l;
4082
4083 return(hldelete(l));
4084 }
4085 else {
4086 l->text[ods.p_ind]='\0'; /* delete part of the line from the cursor */
4087 return(TRUE);
4088 }
4089 }else{
4090 if(l->next && l->prev)
4091 ods.cur_l = next_hline(&ods.cur_e, l);
4092 else if(l->prev)
4093 ods.cur_l = prev_hline(&ods.cur_e, l);
4094
4095 if(l == ods.top_l)
4096 ods.top_l = ods.cur_l;
4097
4098 return(hldelete(l)); /* blast it */
4099 }
4100 }
4101
4102
4103
4104 /*
4105 * SaveHeaderLines() - insert the saved lines in the list before the
4106 * current line in the header
4107 *
4108 * notes:
4109 * Once again, just using emacs' kill buffer and its
4110 * functions.
4111 *
4112 * returns:
4113 * TRUE if something good happened
4114 * FALSE otherwise
4115 */
4116 int
SaveHeaderLines(void)4117 SaveHeaderLines(void)
4118 {
4119 UCS *buf; /* malloc'd copy of buffer */
4120 UCS *bp; /* pointer to above buffer */
4121 register unsigned i; /* index */
4122 UCS *work_buf, *work_buf_begin;
4123 char empty[1];
4124 int len, buf_len, work_buf_len, tentative_p_ind = 0;
4125 struct hdr_line *travel, *tentative_cur_l = NULL;
4126
4127 if(ksize()){
4128 if((bp = buf = (UCS *) malloc((ksize()+5) * sizeof(*buf))) == NULL){
4129 emlwrite("Can't malloc space for saved text", NULL);
4130 return(FALSE);
4131 }
4132 }
4133 else
4134 return(FALSE);
4135
4136 for(i=0; i < ksize(); i++)
4137 if(kremove(i) != '\n') /* filter out newlines */
4138 *bp++ = (UCS) kremove(i);
4139
4140 *bp = '\0';
4141
4142 while(--bp >= buf) /* kill trailing white space */
4143 if(*bp != ' '){
4144 if(ods.cur_l->text[0] != '\0'){
4145 if(*bp == '>'){ /* inserting an address */
4146 *++bp = ','; /* so add separator */
4147 *++bp = '\0';
4148 }
4149 }
4150 else{ /* nothing in field yet */
4151 if(*bp == ','){ /* so blast any extra */
4152 *bp = '\0'; /* separators */
4153 }
4154 }
4155 break;
4156 }
4157
4158 /* insert new text at the dot position */
4159 buf_len = ucs4_strlen(buf);
4160 tentative_p_ind = ods.p_ind + buf_len;
4161 work_buf_len = ucs4_strlen(ods.cur_l->text) + buf_len;
4162 work_buf = (UCS *) malloc((work_buf_len + 1) * sizeof(UCS));
4163 if (work_buf == NULL) {
4164 emlwrite("Can't malloc space for saved text", NULL);
4165 return(FALSE);
4166 }
4167
4168 work_buf[0] = '\0';
4169 work_buf_begin = work_buf;
4170 i = MIN(ods.p_ind, work_buf_len);
4171 ucs4_strncpy(work_buf, ods.cur_l->text, i);
4172 work_buf[i] = '\0';
4173 ucs4_strncat(work_buf, buf, work_buf_len+1-ucs4_strlen(work_buf)-1);
4174 work_buf[work_buf_len] = '\0';
4175 ucs4_strncat(work_buf, &ods.cur_l->text[ods.p_ind], work_buf_len+1-ucs4_strlen(work_buf)-1);
4176 work_buf[work_buf_len] = '\0';
4177 empty[0]='\0';
4178 ods.p_ind = 0;
4179
4180 i = TRUE;
4181
4182 /* insert text in HLSZ character chunks */
4183 while(work_buf_len + ods.p_ind > HLSZ) {
4184 ucs4_strncpy(&ods.cur_l->text[ods.p_ind], work_buf, HLSZ-ods.p_ind);
4185 work_buf += (HLSZ - ods.p_ind);
4186 work_buf_len -= (HLSZ - ods.p_ind);
4187
4188 if(FormatLines(ods.cur_l, empty, LINEWID(),
4189 headents[ods.cur_e].break_on_comma, 0) == -1) {
4190 i = FALSE;
4191 break;
4192 } else {
4193 i = TRUE;
4194 len = 0;
4195 travel = ods.cur_l;
4196 while (len < HLSZ){
4197 len += ucs4_strlen(travel->text);
4198 if (len >= HLSZ)
4199 break;
4200
4201 /*
4202 * This comes after the break above because it will
4203 * be accounted for in the while loop below.
4204 */
4205 if(!tentative_cur_l){
4206 if(tentative_p_ind <= ucs4_strlen(travel->text))
4207 tentative_cur_l = travel;
4208 else
4209 tentative_p_ind -= ucs4_strlen(travel->text);
4210 }
4211
4212 travel = travel->next;
4213 }
4214
4215 ods.cur_l = travel;
4216 ods.p_ind = ucs4_strlen(travel->text) - len + HLSZ;
4217 }
4218 }
4219
4220 /* insert the remainder of text */
4221 if (i != FALSE && work_buf_len > 0) {
4222 ucs4_strncpy(&ods.cur_l->text[ods.p_ind], work_buf, HLSZ-ods.p_ind);
4223 ods.cur_l->text[HLSZ-1] = '\0';
4224 work_buf = work_buf_begin;
4225 free(work_buf);
4226
4227 if(FormatLines(ods.cur_l, empty, LINEWID(),
4228 headents[ods.cur_e].break_on_comma, 0) == -1) {
4229 i = FALSE;
4230 } else {
4231 len = 0;
4232 travel = ods.cur_l;
4233 while (len < work_buf_len + ods.p_ind){
4234 if(!tentative_cur_l){
4235 if(tentative_p_ind <= ucs4_strlen(travel->text))
4236 tentative_cur_l = travel;
4237 else
4238 tentative_p_ind -= ucs4_strlen(travel->text);
4239 }
4240
4241 len += ucs4_strlen(travel->text);
4242 if (len >= work_buf_len + ods.p_ind)
4243 break;
4244
4245 travel = travel->next;
4246 }
4247
4248 ods.cur_l = travel;
4249 ods.p_ind = ucs4_strlen(travel->text) - len + work_buf_len + ods.p_ind;
4250 if(tentative_cur_l
4251 && tentative_p_ind >= 0
4252 && tentative_p_ind <= ucs4_strlen(tentative_cur_l->text)){
4253 ods.cur_l = tentative_cur_l;
4254 ods.p_ind = tentative_p_ind;
4255 }
4256 }
4257 }
4258
4259 free(buf);
4260 return(i);
4261 }
4262
4263
4264
4265 /*
4266 * break_point - Break the given line at the most reasonable character breakch
4267 * within maxwid max characters.
4268 *
4269 * returns:
4270 * Pointer to the best break point in s, or
4271 * Pointer to the beginning of s if no break point found
4272 */
4273 UCS *
break_point(UCS * line,int maxwid,UCS breakch,int * quotedarg)4274 break_point(UCS *line, int maxwid, UCS breakch, int *quotedarg)
4275 {
4276 UCS *bp; /* break point */
4277 int quoted;
4278
4279 /*
4280 * Start at maxwid and work back until first opportunity to break.
4281 */
4282 bp = ucs4_particular_width(line, maxwid);
4283
4284 /*
4285 * Quoted should be set up for the start of line. Since we want
4286 * to move to bp and work our way back we need to scan through the
4287 * line up to bp setting quoted appropriately.
4288 */
4289 if(quotedarg)
4290 ucs4_strqchr(line, '\0', quotedarg, bp-line);
4291
4292 quoted = quotedarg ? *quotedarg : 0;
4293
4294 while(bp != line){
4295 if(breakch == ',' && *bp == '"') /* don't break on quoted ',' */
4296 quoted = !quoted; /* toggle quoted state */
4297
4298 if(*bp == breakch && !quoted){
4299 if(breakch == ' '){
4300 if(ucs4_str_width_ptr_to_ptr(line, bp+1) < maxwid){
4301 bp++; /* leave the ' ' */
4302 break;
4303 }
4304 }
4305 else{
4306 /*
4307 * if break char isn't a space, leave a space after
4308 * the break char.
4309 */
4310 if(!(ucs4_str_width_ptr_to_ptr(line, bp+1) >= maxwid
4311 || (bp[1] == ' ' && ucs4_str_width_ptr_to_ptr(line, bp+2) >= maxwid))){
4312 bp += (bp[1] == ' ') ? 2 : 1;
4313 break;
4314 }
4315 }
4316 }
4317
4318 bp--;
4319 }
4320
4321 if(quotedarg)
4322 *quotedarg = quoted;
4323
4324 return((quoted) ? line : bp);
4325 }
4326
4327
4328
4329
4330 /*
4331 * hldelete() - remove the header line pointed to by l from the linked list
4332 * of lines.
4333 *
4334 * notes:
4335 * the case of first line in field is kind of bogus. since
4336 * the array of headers has a pointer to the first line, and
4337 * i don't want to worry about this too much, i just copied
4338 * the line below and removed it rather than the first one
4339 * from the list.
4340 *
4341 * returns:
4342 * TRUE if it worked
4343 * FALSE otherwise
4344 */
4345 int
hldelete(struct hdr_line * l)4346 hldelete(struct hdr_line *l)
4347 {
4348 register struct hdr_line *lp;
4349
4350 if(l == NULL)
4351 return(FALSE);
4352
4353 if(l->next == NULL && l->prev == NULL){ /* only one line in field */
4354 l->text[0] = '\0';
4355 return(TRUE); /* no free only line in list */
4356 }
4357 else if(l->next == NULL){ /* last line in field */
4358 l->prev->next = NULL;
4359 }
4360 else if(l->prev == NULL){ /* first line in field */
4361 ucs4_strncpy(l->text, l->next->text, HLSZ);
4362 l->text[HLSZ-1] = '\0';
4363 lp = l->next;
4364 if((l->next = lp->next) != NULL)
4365 l->next->prev = l;
4366 l = lp;
4367 }
4368 else{ /* some where in field */
4369 l->prev->next = l->next;
4370 l->next->prev = l->prev;
4371 }
4372
4373 l->next = NULL;
4374 l->prev = NULL;
4375 free((char *)l);
4376 return(TRUE);
4377 }
4378
4379
4380
4381 /*
4382 * is_blank - returns true if the next n chars from coordinates row, col
4383 * on display are spaces
4384 */
4385 int
is_blank(int row,int col,int n)4386 is_blank(int row, int col, int n)
4387 {
4388 n += col;
4389 for( ;col < n; col++){
4390 if(pscr(row, col) == NULL || pscr(row, col)->c != ' ')
4391 return(0);
4392 }
4393 return(1);
4394 }
4395
4396
4397 /*
4398 * ShowPrompt - display key help corresponding to the current header entry
4399 */
4400 void
ShowPrompt(void)4401 ShowPrompt(void)
4402 {
4403 if(headents[ods.cur_e].key_label){
4404 menu_header[TO_KEY].name = "^T";
4405 menu_header[TO_KEY].label = headents[ods.cur_e].key_label;
4406 KS_OSDATASET(&menu_header[TO_KEY], KS_OSDATAGET(&headents[ods.cur_e]));
4407 }
4408 else
4409 menu_header[TO_KEY].name = NULL;
4410
4411 if(Pmaster && Pmaster->exit_label)
4412 menu_header[SEND_KEY].label = Pmaster->exit_label;
4413 else if(gmode & (MDVIEW | MDHDRONLY))
4414 menu_header[SEND_KEY].label = (gmode & MDHDRONLY) ? "eXit/Save" : "eXit";
4415 else
4416 menu_header[SEND_KEY].label = N_("Send");
4417
4418 if(gmode & MDVIEW){
4419 menu_header[CUT_KEY].name = NULL;
4420 menu_header[DEL_KEY].name = NULL;
4421 menu_header[UDEL_KEY].name = NULL;
4422 }
4423 else{
4424 menu_header[CUT_KEY].name = "^K";
4425 menu_header[DEL_KEY].name = "^D";
4426 menu_header[UDEL_KEY].name = "^U";
4427 }
4428
4429 if(Pmaster->ctrlr_label){
4430 menu_header[RICH_KEY].label = Pmaster->ctrlr_label;
4431 menu_header[RICH_KEY].name = "^R";
4432 }
4433 else if(gmode & MDHDRONLY){
4434 menu_header[RICH_KEY].name = NULL;
4435 }
4436 else{
4437 menu_header[RICH_KEY].label = N_("Rich Hdr");
4438 menu_header[RICH_KEY].name = "^R";
4439 }
4440
4441 if(gmode & MDHDRONLY){
4442 if(headents[ods.cur_e].fileedit){
4443 menu_header[PONE_KEY].name = "^_";
4444 menu_header[PONE_KEY].label = N_("Edit File");
4445 }
4446 else
4447 menu_header[PONE_KEY].name = NULL;
4448
4449 menu_header[ATT_KEY].name = NULL;
4450 }
4451 else{
4452 menu_header[PONE_KEY].name = "^O";
4453 menu_header[PONE_KEY].label = N_("Postpone");
4454
4455 menu_header[ATT_KEY].name = "^J";
4456 }
4457
4458 wkeyhelp(menu_header);
4459 }
4460
4461
4462 /*
4463 * packheader - packup all of the header fields for return to caller.
4464 * NOTE: all of the header info passed in, including address
4465 * of the pointer to each string is contained in the
4466 * header entry array "headents".
4467 */
4468 int
packheader(void)4469 packheader(void)
4470 {
4471 register int i = 0; /* array index */
4472 register int count; /* count of chars in a field */
4473 register int retval = TRUE;
4474 register char *bufp;
4475 register struct hdr_line *line;
4476 char *p;
4477
4478 if(!headents)
4479 return(TRUE);
4480
4481 while(headents[i].name != NULL){
4482 #ifdef ATTACHMENTS
4483 /*
4484 * attachments are special case, already in struct we pass back
4485 */
4486 if(headents[i].is_attach){
4487 i++;
4488 continue;
4489 }
4490 #endif
4491
4492 if(headents[i].blank){
4493 i++;
4494 continue;
4495 }
4496
4497 /*
4498 * count chars to see if we need a new malloc'd space for our
4499 * array.
4500 */
4501 line = headents[i].hd_text;
4502 count = 0;
4503 while(line != NULL){
4504 /*
4505 * add one for possible concatenation of a ' ' character ...
4506 */
4507 p = ucs4_to_utf8_cpystr(line->text);
4508 if(p){
4509 count += strlen(p);
4510 if(p[0] && p[strlen(p)-1] == ',')
4511 count++;
4512
4513 fs_give((void **) &p);
4514 }
4515
4516 line = line->next;
4517 }
4518
4519 line = headents[i].hd_text;
4520 if(count <= headents[i].maxlen){
4521 *headents[i].realaddr[0] = '\0';
4522 }
4523 else{
4524 /*
4525 * don't forget to include space for the null terminator!!!!
4526 */
4527 if((bufp = (char *)malloc((count+1) * sizeof(char))) != NULL){
4528 *bufp = '\0';
4529
4530 free(*headents[i].realaddr);
4531 *headents[i].realaddr = bufp;
4532 headents[i].maxlen = count;
4533 }
4534 else{
4535 emlwrite("Can't make room to pack header field.", NULL);
4536 retval = FALSE;
4537 }
4538 }
4539
4540 if(retval != FALSE){
4541 int saw_current_line = 0;
4542
4543 while(line != NULL){
4544
4545 /* pass the cursor offset back in Pmaster struct */
4546 if(headents[i].start_here && Pmaster && !saw_current_line){
4547 if(ods.cur_l == line)
4548 saw_current_line++;
4549 else
4550 Pmaster->edit_offset += ucs4_strlen(line->text);
4551 }
4552
4553 p = ucs4_to_utf8_cpystr(line->text);
4554 if(p){
4555 strncat(*headents[i].realaddr, p, headents[i].maxlen+1-strlen(*headents[i].realaddr)-1);
4556 (*headents[i].realaddr)[headents[i].maxlen] = '\0';
4557
4558 if(p[0] && p[strlen(p)-1] == ','){
4559 strncat(*headents[i].realaddr, " ", headents[i].maxlen+1-strlen(*headents[i].realaddr)-1);
4560 (*headents[i].realaddr)[headents[i].maxlen] = '\0';
4561 }
4562
4563 fs_give((void **) &p);
4564 }
4565
4566 line = line->next;
4567 }
4568 }
4569
4570 i++;
4571 }
4572
4573 return(retval);
4574 }
4575
4576
4577
4578 /*
4579 * zotheader - free all malloc'd lines associated with the header structs
4580 */
4581 void
zotheader(void)4582 zotheader(void)
4583 {
4584 register struct headerentry *i;
4585
4586 for(i = headents; headents && i->name; i++)
4587 zotentry(i->hd_text);
4588 }
4589
4590
4591 /*
4592 * zotentry - free malloc'd space associated with the given linked list
4593 */
4594 void
zotentry(struct hdr_line * l)4595 zotentry(struct hdr_line *l)
4596 {
4597 register struct hdr_line *ld, *lf = l;
4598
4599 while((ld = lf) != NULL){
4600 lf = ld->next;
4601 ld->next = ld->prev = NULL;
4602 free((char *) ld);
4603 }
4604 }
4605
4606
4607
4608 /*
4609 * zotcomma - blast any trailing commas and white space from the end
4610 * of the given line
4611 */
4612 int
zotcomma(UCS * s)4613 zotcomma(UCS *s)
4614 {
4615 UCS *p;
4616 int retval = FALSE;
4617
4618 p = &s[ucs4_strlen(s)];
4619 while(--p >= s){
4620 if(*p != ' '){
4621 if(*p == ','){
4622 *p = '\0';
4623 retval = TRUE;
4624 }
4625
4626 return(retval);
4627 }
4628 }
4629
4630 return(retval);
4631 }
4632
4633
4634 /*
4635 * Save the current state of global variables so that we can restore
4636 * them later. This is so we can call pico again.
4637 * Also have to initialize some variables that normally would be set to
4638 * zero on startup.
4639 */
4640 VARS_TO_SAVE *
save_pico_state(void)4641 save_pico_state(void)
4642 {
4643 VARS_TO_SAVE *ret;
4644 extern int vtrow;
4645 extern int vtcol;
4646 extern int lbound;
4647 extern VIDEO **vscreen;
4648 extern VIDEO **pscreen;
4649 extern int pico_all_done;
4650 extern jmp_buf finstate;
4651 extern UCS *pico_anchor;
4652
4653 if((ret = (VARS_TO_SAVE *)malloc(sizeof(VARS_TO_SAVE))) == NULL)
4654 return(ret);
4655
4656 ret->vtrow = vtrow;
4657 ret->vtcol = vtcol;
4658 ret->lbound = lbound;
4659 ret->vscreen = vscreen;
4660 ret->pscreen = pscreen;
4661 ret->ods = ods;
4662 ret->delim_ps = delim_ps;
4663 ret->invert_ps = invert_ps;
4664 ret->pico_all_done = pico_all_done;
4665 memcpy(ret->finstate, finstate, sizeof(jmp_buf));
4666 ret->pico_anchor = pico_anchor;
4667 ret->Pmaster = Pmaster;
4668 ret->fillcol = fillcol;
4669 if((ret->pat = (UCS *)malloc(sizeof(UCS) * (ucs4_strlen(pat)+1))) != NULL)
4670 ucs4_strncpy(ret->pat, pat, ucs4_strlen(pat)+1);
4671
4672 ret->ComposerTopLine = ComposerTopLine;
4673 ret->ComposerEditing = ComposerEditing;
4674 ret->gmode = gmode;
4675 ret->alt_speller = alt_speller;
4676 ret->quote_str = glo_quote_str;
4677 ret->wordseps = glo_wordseps;
4678 ret->currow = currow;
4679 ret->curcol = curcol;
4680 ret->thisflag = thisflag;
4681 ret->lastflag = lastflag;
4682 ret->curgoal = curgoal;
4683 ret->opertree = (char *) malloc(sizeof(char) * (strlen(opertree) + 1));
4684 if(ret->opertree != NULL)
4685 strncpy(ret->opertree, opertree, strlen(opertree)+1);
4686
4687 ret->curwp = curwp;
4688 ret->wheadp = wheadp;
4689 ret->curbp = curbp;
4690 ret->bheadp = bheadp;
4691 ret->km_popped = km_popped;
4692 ret->mrow = term.t_mrow;
4693
4694 /* Initialize for next pico call */
4695 wheadp = NULL;
4696 curwp = NULL;
4697 bheadp = NULL;
4698 curbp = NULL;
4699
4700 return(ret);
4701 }
4702
4703
4704 void
restore_pico_state(VARS_TO_SAVE * state)4705 restore_pico_state(VARS_TO_SAVE *state)
4706 {
4707 extern int vtrow;
4708 extern int vtcol;
4709 extern int lbound;
4710 extern VIDEO **vscreen;
4711 extern VIDEO **pscreen;
4712 extern int pico_all_done;
4713 extern jmp_buf finstate;
4714 extern UCS *pico_anchor;
4715
4716 clearcursor();
4717 vtrow = state->vtrow;
4718 vtcol = state->vtcol;
4719 lbound = state->lbound;
4720 vscreen = state->vscreen;
4721 pscreen = state->pscreen;
4722 ods = state->ods;
4723 delim_ps = state->delim_ps;
4724 invert_ps = state->invert_ps;
4725 pico_all_done = state->pico_all_done;
4726 memcpy(finstate, state->finstate, sizeof(jmp_buf));
4727 pico_anchor = state->pico_anchor;
4728 Pmaster = state->Pmaster;
4729 if(Pmaster)
4730 headents = Pmaster->headents;
4731
4732 fillcol = state->fillcol;
4733 if(state->pat)
4734 ucs4_strncpy(pat, state->pat, NPAT);
4735
4736 ComposerTopLine = state->ComposerTopLine;
4737 ComposerEditing = state->ComposerEditing;
4738 gmode = state->gmode;
4739 alt_speller = state->alt_speller;
4740 glo_quote_str = state->quote_str;
4741 glo_wordseps = state->wordseps;
4742 currow = state->currow;
4743 curcol = state->curcol;
4744 thisflag = state->thisflag;
4745 lastflag = state->lastflag;
4746 curgoal = state->curgoal;
4747 if(state->opertree){
4748 strncpy(opertree, state->opertree, sizeof(opertree));
4749 opertree[sizeof(opertree)-1] = '\0';
4750 }
4751
4752 curwp = state->curwp;
4753 wheadp = state->wheadp;
4754 curbp = state->curbp;
4755 bheadp = state->bheadp;
4756 km_popped = state->km_popped;
4757 term.t_mrow = state->mrow;
4758 }
4759
4760
4761 void
free_pico_state(VARS_TO_SAVE * state)4762 free_pico_state(VARS_TO_SAVE *state)
4763 {
4764 if(state->pat)
4765 free(state->pat);
4766
4767 if(state->opertree)
4768 free(state->opertree);
4769
4770 free(state);
4771 }
4772
4773
4774 /*
4775 * Ok to call this twice in a row because it won't do anything the second
4776 * time.
4777 */
4778 void
fix_mangle_and_err(int * mangled,char ** errmsg,char * name)4779 fix_mangle_and_err(int *mangled, char **errmsg, char *name)
4780 {
4781 if(mangled && *mangled){
4782 ttresize();
4783 picosigs();
4784 PaintBody(0);
4785 *mangled = 0;
4786 }
4787
4788 if(errmsg && *errmsg){
4789 if(**errmsg){
4790 char err[500];
4791
4792 snprintf(err, sizeof(err), "%s field: %s", name, *errmsg);
4793 (*term.t_beep)();
4794 emlwrite(err, NULL);
4795 }
4796 else
4797 mlerase();
4798
4799 free(*errmsg);
4800 *errmsg = NULL;
4801 }
4802 }
4803
4804
4805 /*
4806 * What is this for?
4807 * This is so that the To line will be appended to by an Lcc
4808 * entry unless the user types in the To line after the Lcc
4809 * has already been set.
4810 */
4811 void
mark_sticky(struct headerentry * h)4812 mark_sticky(struct headerentry *h)
4813 {
4814 if(h && (!h->sticky_special || h->bldr_private))
4815 h->sticky = 1;
4816 }
4817
4818
4819 #ifdef MOUSE
4820 #undef HeaderEditor
4821
4822 /*
4823 * Wrapper function for the real header editor.
4824 * Does the important tasks of:
4825 * 1) verifying that we _can_ edit the headers.
4826 * 2) acting on the result code from the header editor.
4827 */
4828 int
HeaderEditor(int f,int n)4829 HeaderEditor(int f, int n)
4830 {
4831 int retval;
4832
4833
4834 #ifdef _WINDOWS
4835 /* Sometimes we get here from a scroll callback, which
4836 * is no good at all because mswin is not ready to process input and
4837 * this _headeredit() will never do anything.
4838 * Putting this test here was the most general solution I could think
4839 * of. */
4840 if (!mswin_caninput())
4841 return (-1);
4842 #endif
4843
4844 retval = HeaderEditorWork(f, n);
4845 if (retval == -3) {
4846 retval = mousepress(0,0);
4847 }
4848 return (retval);
4849 }
4850 #endif
4851