* Update to version 2.19.5
[alpine.git] / pico / pico.c
blob40d971b9be6a58a91ed225690e126c01074f5451
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: pico.c 921 2008-01-31 02:09:25Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2006-2008 University of Washington
8 * Copyright 2013-2014 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
20 * Program: Main Pine Composer routines
23 * WEEMACS/PICO NOTES:
25 * 01 Nov 89 - MicroEmacs 3.6 vastly pared down and becomes a function call
26 * weemacs() and plugged into the Pine mailer. Lots of unused
27 * MicroEmacs code laying around.
29 * 17 Jan 90 - weemacs() became weemacs() the composer. Header editing
30 * functions added.
32 * 09 Sep 91 - weemacs() renamed pico() for the PIne COmposer.
38 * This program is in public domain; written by Dave G. Conroy.
39 * This file contains the main driving routine, and some keyboard processing
40 * code, for the MicroEMACS screen editor.
42 * REVISION HISTORY:
44 * 1.0 Steve Wilhite, 30-Nov-85
45 * - Removed the old LK201 and VT100 logic. Added code to support the
46 * DEC Rainbow keyboard (which is a LK201 layout) using the the Level
47 * 1 Console In ROM INT. See "rainbow.h" for the function key defs
48 * Steve Wilhite, 1-Dec-85
49 * - massive cleanup on code in display.c and search.c
51 * 2.0 George Jones, 12-Dec-85
52 * - Ported to Amiga.
54 * 3.0 Daniel Lawrence, 29-Dec-85
55 * 16-apr-86
56 * - updated documentation and froze development for 3.6 net release
59 /* make global definitions not external */
60 #define maindef
62 #include "../c-client/mail.h"
63 #include "../c-client/utf8.h"
65 #ifdef _WINDOWS
66 /* wingdi.h uses ERROR (!) and we aren't using the c-client ERROR so... */
67 #undef ERROR
69 #endif
70 #include "headers.h"
71 #include "ebind.h" /* default key bindings */
72 #include "../pith/charconv/filesys.h"
75 void func_init(void);
76 void breplace(void *w);
77 int any_header_changes(void);
78 int cleanwhitespace(void);
79 int isquotedspace(LINE *);
83 * function key mappings
85 static UCS pfkm[12][2] = {
86 { F1, (CTRL|'G')},
87 { F2, (CTRL|'C')},
88 { F3, (CTRL|'X')},
89 { F4, (CTRL|'J')},
90 { F5, (CTRL|'R')},
91 { F6, (CTRL|'W')},
92 { F7, (CTRL|'Y')},
93 { F8, (CTRL|'V')},
94 { F9, (CTRL|'K')},
95 { F10, (CTRL|'U')},
96 { F11, (CTRL|'O')},
97 #ifdef SPELLER
98 { F12, (CTRL|'T')}
99 #else
100 { F12, (CTRL|'D')}
101 #endif
106 * flag for the various functions in pico() to set when ready
107 * for pico() to return...
109 int pico_all_done = 0;
110 jmp_buf finstate;
111 UCS *pico_anchor = NULL;
112 extern struct headerentry *headents;
115 * pico - the main routine for Pine's composer.
119 pico(PICO *pm)
121 UCS c;
122 register int f;
123 register int n;
124 char bname[NBUFN]; /* buffer name of file to read */
125 extern struct on_display ods;
126 int checkpointcnt = 0, input = 0;
127 int ret;
128 char chkptfile[NLINE];
129 #ifdef _WINDOWS
130 int cursor_shown;
131 #endif
133 Pmaster = pm;
134 gmode = MDWRAP;
135 gmode |= pm->pine_flags; /* high 4 bits rsv'd for pine */
137 alt_speller = pm->alt_spell;
138 pico_all_done = 0;
139 km_popped = 0;
141 if(!vtinit()) /* Init Displays. */
142 return(COMP_CANCEL);
144 strncpy(bname, "main", sizeof(bname)); /* default buffer name */
145 bname[sizeof(bname)-1] = '\0';
146 edinit(bname); /* Buffers, windows. */
148 if(InitMailHeader(pm)) /* init mail header structure */
149 gmode &= ~(P_BODY | P_HEADEND); /* flip off special header stuff */
151 /* setup to process commands */
152 lastflag = 0; /* Fake last flags. */
153 curbp->b_mode |= gmode; /* and set default modes*/
155 if(Pmaster->pine_anchor)
156 pico_anchor = utf8_to_ucs4_cpystr(Pmaster->pine_anchor);
157 else
158 pico_anchor = NULL;
160 if(Pmaster->quote_str)
161 glo_quote_str = utf8_to_ucs4_cpystr(Pmaster->quote_str);
162 else
163 glo_quote_str = NULL;
165 if(Pmaster->wordseps)
166 glo_wordseps = ucs4_cpystr(Pmaster->wordseps);
167 else
168 glo_wordseps = NULL;
170 bindtokey(DEL, (gmode & P_DELRUBS) ? forwdel : backdel);
172 if(pm->msgtext)
173 breplace(pm->msgtext);
175 #ifdef _WINDOWS
176 cursor_shown = mswin_showcaret(1); /* turn on for main window */
177 mswin_allowpaste(MSWIN_PASTE_FULL);
178 mswin_setscrollcallback (pico_scroll_callback);
179 #endif
181 /* prepare for checkpointing */
182 chkptfile[0] = '\0';
183 chkptinit((*Pmaster->ckptdir)(chkptfile, sizeof(chkptfile)), sizeof(chkptfile));
184 if(gmode & P_CHKPTNOW)
185 writeout(chkptfile, TRUE);
187 pico_all_done = setjmp(finstate); /* jump out of HUP handler ? */
189 if(gmode & MDALTNOW){
190 while(!pico_all_done){
191 if(((gmode & P_BODY) || !Pmaster->headents)
192 && alt_editor(0, 1) < 0)
193 break; /* if problem, drop into pico */
195 if(Pmaster->headents){
196 update(); /* paint screen, n' start editing... */
197 HeaderEditor((gmode & (P_HEADEND | P_BODY)) ? 2 : 0, 0);
198 gmode |= P_BODY; /* make sure we enter alt ed next */
200 else
201 pico_all_done = COMP_EXIT;
204 else if(!pico_all_done){
205 if(gmode & P_BODY){ /* begin editing the header? */
206 ArrangeHeader(); /* line up pointers */
208 * Move to the offset pine asked us to move to.
209 * Perhaps we should be checking to see if this is
210 * a reasonable number before moving.
212 if(Pmaster && Pmaster->edit_offset)
213 forwchar(FALSE, Pmaster->edit_offset);
215 else{
216 update(); /* paint screen, */
217 HeaderEditor((gmode & P_HEADEND) ? 2 : 0, 0);
221 while(1){
222 if(pico_all_done){
223 #ifdef _WINDOWS
224 if(!cursor_shown)
225 mswin_showcaret(0);
227 mswin_allowpaste(MSWIN_PASTE_DISABLE);
228 mswin_setscrollcallback (NULL);
229 #endif
230 ret = anycb() ? BUF_CHANGED : 0;
231 switch(pico_all_done){ /* prepare for/handle final events */
232 case COMP_EXIT : /* already confirmed */
233 packheader();
234 if(Pmaster
235 && (Pmaster->strip_ws_before_send
236 || Pmaster->allow_flowed_text))
237 cleanwhitespace();
238 ret |= COMP_EXIT;
239 break;
241 case COMP_CANCEL : /* also already confirmed */
242 packheader();
243 ret = COMP_CANCEL;
244 break;
246 case COMP_GOTHUP:
248 * pack up and let caller know that we've received a SIGHUP
250 if(ComposerEditing) /* expand addr if needed */
251 call_builder(&headents[ods.cur_e], NULL, NULL);
253 packheader();
254 ret |= COMP_GOTHUP;
255 break;
257 case COMP_SUSPEND :
258 default: /* safest if internal error */
260 * If we're in the headers mark the current header line
261 * with start_here bit so caller knows where to reset.
262 * Also set the edit_offset, which is either the offset
263 * into this header line or the offset into the body.
264 * Packheader will adjust edit_offset for multi-line
265 * headers.
267 if(ComposerEditing){ /* in the headers */
268 headents[ods.cur_e].start_here = 1;
269 Pmaster->edit_offset = ods.p_ind;
271 else{
272 register LINE *clp;
273 register long offset;
275 for(clp = lforw(curbp->b_linep), offset = 0L;
276 clp != curwp->w_dotp;
277 clp = lforw(clp))
278 offset += (llength(clp) + 1);
280 Pmaster->edit_offset = offset + curwp->w_doto;
283 packheader();
284 ret |= COMP_SUSPEND;
285 break;
288 if(pico_anchor)
289 fs_give((void **) &pico_anchor);
290 if(glo_quote_str)
291 fs_give((void **) &glo_quote_str);
292 if(glo_wordseps)
293 fs_give((void **) &glo_wordseps);
295 vttidy(); /* clean up tty modes */
296 zotdisplay(); /* blast display buffers */
297 zotedit();
298 our_unlink(chkptfile);
299 Pmaster = NULL; /* blat global */
301 return(ret);
304 if(km_popped){
305 km_popped--;
306 if(km_popped == 0) /* cause bottom three lines to be repainted */
307 curwp->w_flag |= WFHARD;
310 if(km_popped){ /* temporarily change to cause menu to be painted */
311 term.t_mrow = 2;
312 curwp->w_ntrows -= 2;
313 curwp->w_flag |= WFMODE;
314 movecursor(term.t_nrow-2, 0); /* clear status line, too */
315 peeol();
318 update(); /* Fix up the screen */
319 if(km_popped){
320 term.t_mrow = 0;
321 curwp->w_ntrows += 2;
324 #ifdef MOUSE
325 #ifdef EX_MOUSE
326 /* New mouse function for real mouse text seletion. */
327 register_mfunc(mouse_in_pico, 2, 0, term.t_nrow - (term.t_mrow+1),
328 term.t_ncol);
329 #else
330 mouse_in_content(KEY_MOUSE, -1, -1, -1, 0);
331 register_mfunc(mouse_in_content, 2, 0, term.t_nrow - (term.t_mrow + 1),
332 term.t_ncol);
333 #endif
334 #endif
335 #ifdef _WINDOWS
336 mswin_setdndcallback (composer_file_drop);
337 mswin_mousetrackcallback(pico_cursor);
338 #endif
339 c = GetKey();
340 if (term.t_nrow < 6 && c != NODATA){
341 (*term.t_beep)();
342 emlwrite(_("Please make the screen bigger."), NULL);
343 continue;
346 #ifdef MOUSE
347 #ifdef EX_MOUSE
348 clear_mfunc(mouse_in_pico);
349 #else
350 clear_mfunc(mouse_in_content);
351 #endif
352 #endif
353 #ifdef _WINDOWS
354 mswin_cleardndcallback ();
355 mswin_mousetrackcallback(NULL);
356 #endif
357 if(c == NODATA || time_to_check()){ /* new mail ? */
358 if((*Pmaster->newmail)(c == NODATA ? 0 : 2, 1) >= 0){
359 int rv;
361 if(km_popped){
362 term.t_mrow = 2;
363 curwp->w_ntrows -= 2;
364 curwp->w_flag |= WFHARD;
365 km_popped = 0;
368 clearcursor();
369 mlerase();
370 rv = (*Pmaster->showmsg)(c);
371 ttresize();
372 picosigs(); /* restore altered handlers */
373 if(rv) /* Did showmsg corrupt the display? */
374 PaintBody(0); /* Yes, repaint */
376 mpresf = 1;
377 input = 0;
380 clearcursor();
381 movecursor(0, 0);
384 if(km_popped)
385 switch(c){
386 case NODATA:
387 case (CTRL|'L'):
388 km_popped++;
389 break;
391 default:
392 mlerase();
393 break;
396 if(c == NODATA) /* no op, getkey timed out */
397 continue;
398 else if(!input++)
399 (*Pmaster->keybinput)();
401 if (mpresf != FALSE) { /* message stay around only */
402 if (mpresf++ > NMMESSDELAY) /* so long! */
403 mlerase();
406 f = FALSE; /* vestigial */
407 n = 1;
408 /* Do it. */
409 execute(normalize_cmd(c, pfkm, 2), f, n);
410 if(++checkpointcnt >= CHKPTDELAY){
411 checkpointcnt = 0;
412 writeout(chkptfile, TRUE);
418 * Initialize all of the buffers and windows. The buffer name is passed down
419 * as an argument, because the main routine may have been told to read in a
420 * file by default, and we want the buffer name to be right.
424 * For the pine composer, we don't want to take over the whole screen
425 * for editing. the first some odd lines are to be used for message
426 * header information editing.
428 void
429 edinit(char bname[])
431 register BUFFER *bp;
432 register WINDOW *wp;
434 if(Pmaster)
435 func_init();
437 bp = bfind(bname, TRUE, BFWRAPOPEN); /* First buffer */
438 wp = (WINDOW *) malloc(sizeof(WINDOW)); /* First window */
440 if (bp==NULL || wp==NULL){
441 if(Pmaster)
442 return;
443 else
444 exit(1);
447 curbp = bp; /* Make this current */
448 wheadp = wp;
449 curwp = wp;
450 wp->w_wndp = NULL; /* Initialize window */
451 wp->w_bufp = bp;
452 bp->b_nwnd = 1; /* Displayed. */
453 wp->w_linep = bp->b_linep;
454 wp->w_dotp = bp->b_linep;
455 wp->w_doto = 0;
456 wp->w_markp = wp->w_imarkp = NULL;
457 wp->w_marko = wp->w_imarko = 0;
458 bp->b_linecnt = -1;
460 if(Pmaster){
461 term.t_mrow = Pmaster->menu_rows;
462 wp->w_toprow = ComposerTopLine = COMPOSER_TOP_LINE;
463 wp->w_ntrows = term.t_nrow - COMPOSER_TOP_LINE - term.t_mrow;
464 fillcol = Pmaster->fillcolumn;
465 strncpy(opertree,
466 (Pmaster->oper_dir && strlen(Pmaster->oper_dir) < NLINE)
467 ? Pmaster->oper_dir : "", sizeof(opertree));
468 opertree[sizeof(opertree)-1] = '\0';
469 input_cs = Pmaster->input_cs;
471 else{
472 if(sup_keyhelp)
473 term.t_mrow = 0;
474 else
475 term.t_mrow = 2;
477 wp->w_toprow = 2;
478 wp->w_ntrows = term.t_nrow - 2 - term.t_mrow;
479 if(userfillcol > 0) /* set fill column */
480 fillcol = userfillcol;
481 else
482 fillcol = term.t_ncol - 6;
486 * MDSCUR mode implies MDTREE mode with a opertree of home directory,
487 * unless opertree has been set differently.
489 if((gmode & MDSCUR) && !opertree[0]){
490 strncpy(opertree, gethomedir(NULL), sizeof(opertree));
491 opertree[sizeof(opertree)-1] = '\0';
494 if(*opertree)
495 fixpath(opertree, sizeof(opertree));
497 wp->w_force = 0;
498 wp->w_flag = WFMODE|WFHARD; /* Full. */
503 * This is the general command execution routine. It handles the fake binding
504 * of all the keys to "self-insert". It also clears out the "thisflag" word,
505 * and arranges to move it to the "lastflag", so that the next command can
506 * look at it. Return the status of command.
509 execute(UCS c, int f, int n)
511 KEYTAB *ktp;
512 int status, ww;
514 ktp = (Pmaster) ? &keytab[0] : &pkeytab[0];
516 while (ktp->k_fp != NULL) {
517 if (ktp->k_code == c) {
519 if(lastflag&CFFILL){
520 curwp->w_flag |= WFMODE;
521 if(Pmaster == NULL)
522 sgarbk = TRUE;
525 thisflag = 0;
526 status = (*ktp->k_fp)(f, n);
527 if((lastflag & CFFILL) && !(thisflag & CFFILL))
528 fdelete();
529 if((lastflag & CFFLBF) && !(thisflag & CFFLBF))
530 kdelete();
532 lastflag = thisflag;
535 * Reset flag saying wrap should open a new line whenever
536 * we execute a command (as opposed to just typing in text).
537 * However, if that command leaves us in the same line on the
538 * screen, then don't reset.
540 if(curwp->w_flag & (WFMOVE | WFHARD))
541 curbp->b_flag |= BFWRAPOPEN; /* wrap should open new line */
543 return (status);
545 ++ktp;
548 if(lastflag & CFFILL) /* blat unusable fill data */
549 fdelete();
550 if(lastflag & CFFLBF)
551 kdelete();
553 if (VALID_KEY(c)) { /* Self inserting. */
555 if (n <= 0) { /* Fenceposts. */
556 lastflag = 0;
557 return (n<0 ? FALSE : TRUE);
559 thisflag = 0; /* For the future. */
561 /* do the appropriate insertion */
562 /* pico never does C mode, this is simple */
563 status = linsert(n, c);
566 * Check to make sure we didn't go off of the screen
567 * with that character. Take into account tab expansion.
568 * If so wrap the line...
570 if(curwp->w_bufp->b_mode & MDWRAP){
571 int j, wid;
573 wid = 0;
574 for(j = 0; j < llength(curwp->w_dotp); j++)
575 if(ucs4_isspace(lgetc(curwp->w_dotp, j).c)){
576 if(lgetc(curwp->w_dotp, j).c == TAB){
577 ++wid;
578 while(wid & 0x07)
579 ++wid;
581 else
582 ++wid;
584 else{
585 ww = wcellwidth((UCS) lgetc(curwp->w_dotp, j).c);
586 wid += (ww >= 0 ? ww : 1);
587 if(wid > fillcol){
588 wrapword();
589 break;
594 lastflag = thisflag;
595 return (status);
598 unknown_command(c);
600 lastflag = 0; /* Fake last flags. */
601 return (FALSE);
607 * Fancy quit command, as implemented by Norm. If the any buffer has
608 * changed do a write on that buffer and exit emacs, otherwise simply exit.
611 quickexit(int f, int n)
613 register BUFFER *bp; /* scanning pointer to buffers */
615 bp = bheadp;
616 while (bp != NULL) {
617 if ((bp->b_flag&BFCHG) != 0 /* Changed. */
618 && (bp->b_flag&BFTEMP) == 0) { /* Real. */
619 curbp = bp; /* make that buffer cur */
620 filesave(f, n);
622 bp = bp->b_bufp; /* on to the next buffer */
624 return(wquit(f, n)); /* conditionally quit */
630 * abort_composer - ask the question here, then go quit or
631 * return FALSE
634 abort_composer(int f, int n)
636 char *result;
638 result = "";
640 Pmaster->arm_winch_cleanup++;
641 if(Pmaster->canceltest){
642 if(((Pmaster->pine_flags & MDHDRONLY) && !any_header_changes())
643 || (result = (*Pmaster->canceltest)(redraw_pico_for_callback))){
644 pico_all_done = COMP_CANCEL;
645 emlwrite(result, NULL);
646 Pmaster->arm_winch_cleanup--;
647 return(TRUE);
649 else{
650 /* TRANSLATORS: The user typed the Cancel command and was
651 asked to confirm that. Instead they canceled the cancel
652 command. */
653 emlwrite(_("Cancel Cancelled"), NULL);
654 curwp->w_flag |= WFMODE; /* and modeline so we */
655 sgarbk = TRUE; /* redraw the keymenu */
656 pclear(term.t_nrow-1, term.t_nrow);
657 Pmaster->arm_winch_cleanup--;
658 return(FALSE);
661 else switch(mlyesno_utf8(Pmaster->headents
662 ? _("Cancel message (answering \"Yes\" will abandon your mail message)")
663 : (anycb() == FALSE)
664 ? _("Cancel Edit (and abandon changes)")
665 : _("Cancel Edit"),
666 FALSE)){
667 case TRUE:
668 pico_all_done = COMP_CANCEL;
669 return(TRUE);
671 case ABORT:
672 emlwrite(_("\007Cancel Cancelled"), NULL);
673 break;
675 default:
676 mlerase();
678 return(FALSE);
683 * suspend_composer - return to pine with what's been edited so far
686 suspend_composer(int f, int n)
688 if(Pmaster && Pmaster->headents)
689 pico_all_done = COMP_SUSPEND;
690 else
691 (*term.t_beep)();
693 return(TRUE);
699 * Quit command. If an argument, always quit. Otherwise confirm if a buffer
700 * has been changed and not written out. Normally bound to "C-X C-C".
703 wquit(int f, int n)
705 register int s;
707 if(Pmaster){
708 char *result = NULL;
709 int ret;
711 /* First, make sure there are no outstanding problems */
712 if(AttachError()){
713 emlwrite(_("\007Problem with attachments! Fix errors or delete attachments."), NULL);
714 return(FALSE);
717 #ifdef SPELLER
718 if(Pmaster->always_spell_check)
719 if(spell(0, 0) == -1)
720 sleep(3); /* problem, show error */
721 #endif
723 * if we're not in header, show some of it as we verify sending...
725 display_for_send();
726 packheader();
727 Pmaster->arm_winch_cleanup++;
728 if((!(Pmaster->pine_flags & MDHDRONLY) || any_header_changes())
729 && (ret = (*Pmaster->exittest)(Pmaster->headents,
730 redraw_pico_for_callback,
731 Pmaster->allow_flowed_text,
732 &result))){
733 Pmaster->arm_winch_cleanup--;
735 if(ret == -1){
736 pico_all_done = COMP_CANCEL;
738 else{
739 if(sgarbf)
740 update();
742 lchange(WFHARD); /* set update flags... */
743 curwp->w_flag |= WFMODE; /* and modeline so we */
744 sgarbk = TRUE; /* redraw the keymenu */
745 pclear(term.t_nrow-2, term.t_nrow);
748 if(result && *result)
749 emlwrite(result, NULL);
751 else{
752 Pmaster->arm_winch_cleanup--;
753 pico_all_done = COMP_EXIT;
754 return(TRUE);
757 else{
758 if (f != FALSE /* Argument forces it. */
759 || anycb() == FALSE /* All buffers clean. */
760 /* User says it's OK. */
761 /* TRANSLATORS: buffer is the in-memory copy of a file */
762 || (s=mlyesno_utf8(_("Save modified buffer (ANSWERING \"No\" WILL DESTROY CHANGES)"), -1)) == FALSE) {
763 vttidy();
764 #if defined(USE_TERMCAP) || defined(USE_TERMINFO) || defined(VMS)
765 kbdestroy(kbesc);
766 #endif
767 exit(0);
770 if(s == TRUE){
771 if(filewrite(0,1) == TRUE)
772 wquit(1, 0);
774 else if(s == ABORT){
775 emlwrite(_("Exit cancelled"), NULL);
776 if(term.t_mrow == 0)
777 curwp->w_flag |= WFHARD; /* cause bottom 3 lines to paint */
779 return(s);
782 return(FALSE);
787 * Has any editing been done to headers?
790 any_header_changes(void)
792 struct headerentry *he;
794 for(he = Pmaster->headents; he->name != NULL; he++)
795 if(he->dirty)
796 break;
798 return(he->name && he->dirty);
802 isquotedspace(LINE *line)
804 int i, was_quote = 0;
805 for(i = 0; i < llength(line); i++){
806 if(lgetc(line, i).c == '>')
807 was_quote = 1;
808 else if(was_quote && lgetc(line, i).c == ' '){
809 if(i+1 < llength(line) && ucs4_isspace(lgetc(line,i+1).c))
810 return 1;
811 else
812 return 0;
814 else
815 return 0;
817 return 0;
821 * This function serves two purposes, 1) to strip white space when
822 * Pmaster asks that the composition have its trailing white space
823 * stripped, or 2) to prepare the text as flowed text, as Pmaster
824 * is telling us that we're working with flowed text.
826 * What flowed currently means to us is stripping all trailing white
827 * space, except for one space if the following line is a continuation
828 * of the paragraph. Also, we space-stuff all lines beginning
829 * with white-space, and leave siglines alone.
832 cleanwhitespace(void)
834 LINE *cursor_dotp = NULL, **lp = NULL;
835 int i = 0, cursor_doto = 0, is_cursor_line = 0;
836 int do_space_stuffing = 0;
838 if(Pmaster && Pmaster->allow_flowed_text && !(*Pmaster->user_says_noflow)())
839 do_space_stuffing++;
841 cursor_dotp = curwp->w_dotp;
842 cursor_doto = curwp->w_doto;
843 gotobob(FALSE, 1);
845 for(lp = &curwp->w_dotp; (*lp) != curbp->b_linep; (*lp) = lforw(*lp)){
846 if(!(llength(*lp) == 3
847 && lgetc(*lp, 0).c == '-'
848 && lgetc(*lp, 1).c == '-'
849 && lgetc(*lp, 2).c == ' ')
850 && llength(*lp)){
851 is_cursor_line = (cursor_dotp == (*lp));
852 /* trim trailing whitespace, to be added back if flowing */
853 for(i = llength(*lp); i; i--)
854 if(!ucs4_isspace(lgetc(*lp, i - 1).c))
855 break;
856 if(i != llength(*lp)){
857 int flow_line = 0;
859 if(Pmaster && !Pmaster->strip_ws_before_send
860 && lforw(*lp) != curbp->b_linep
861 && llength(lforw(*lp))
862 && !(ucs4_isspace(lgetc(lforw(*lp), 0).c)
863 || isquotedspace(lforw(*lp)))
864 && !(llength(lforw(*lp)) == 3
865 && lgetc(lforw(*lp), 0).c == '-'
866 && lgetc(lforw(*lp), 1).c == '-'
867 && lgetc(lforw(*lp), 2).c == ' '))
868 flow_line = 1;
869 if(flow_line && i && lgetc(*lp, i).c == ' '){
870 /* flowed line ending with space */
871 i++;
872 if(i != llength(*lp)){
873 curwp->w_doto = i;
874 ldelete(llength(*lp) - i, NULL);
877 else if(flow_line && i && ucs4_isspace(lgetc(*lp, i).c)){
878 /* flowed line ending with whitespace other than space*/
879 curwp->w_doto = i;
880 ldelete(llength(*lp) - i, NULL);
881 linsert(1, ' ');
883 else{
884 curwp->w_doto = i;
885 ldelete(llength(*lp) - i, NULL);
888 if(do_space_stuffing && llength(*lp) && ucs4_isspace(lgetc(*lp, 0).c)){
889 /* space-stuff only if flowed */
890 if(Pmaster)
891 Pmaster->space_stuffed = 1;
892 curwp->w_doto = 0;
893 if(is_cursor_line && cursor_doto)
894 cursor_doto++;
895 linsert(1, ' ');
897 if(is_cursor_line)
898 cursor_dotp = (*lp);
902 /* put the cursor back where we found it */
903 gotobob(FALSE, 1);
904 curwp->w_dotp = cursor_dotp;
905 curwp->w_doto = (cursor_doto < llength(curwp->w_dotp))
906 ? cursor_doto : llength(curwp->w_dotp) - 1;
908 return(0);
912 * Remove all trailing white space from the text
915 stripwhitespace(void)
917 int i;
918 LINE *cur_line = lforw(curbp->b_linep);
921 /* we gotta test for the sigdash case here */
922 if(!(cur_line->l_used == 3 &&
923 lgetc(cur_line, 0).c == '-' &&
924 lgetc(cur_line, 1).c == '-' &&
925 lgetc(cur_line, 2).c == ' '))
926 for(i = cur_line->l_used - 1; i >= 0; i--)
927 if(ucs4_isspace(lgetc(cur_line, i).c))
928 cur_line->l_used--;
929 else
930 break;
931 }while((cur_line = lforw(cur_line)) != curbp->b_linep);
932 return 0;
936 * Abort.
937 * Beep the beeper. Kill off any keyboard macro, etc., that is in progress.
938 * Sometimes called as a routine, to do general aborting of stuff.
941 ctrlg(int f, int n)
943 emlwrite(_("Cancelled"), NULL);
944 return (ABORT);
948 /* tell the user that this command is illegal while we are in
949 * VIEW (read-only) mode
952 rdonly(void)
954 (*term.t_beep)();
955 emlwrite("Key illegal in VIEW mode", NULL);
956 return(FALSE);
962 * reset all globals to their initial values
964 void
965 func_init(void)
967 extern int vtrow;
968 extern int vtcol;
969 extern int lbound;
972 * re-initialize global buffer type variables ....
974 fillcol = (term.t_ncol > 80) ? 77 : term.t_ncol - 6;
975 sgarbf = TRUE;
976 mpresf = FALSE;
977 mline_open = FALSE;
978 ComposerEditing = FALSE;
981 * re-initialize hardware display variables ....
983 vtrow = vtcol = lbound = 0;
984 clearcursor();
986 pat[0] = rpat[0] = '\0';
987 browse_dir[0] = '\0';
992 * pico_help - help function for standalone composer
993 * Oops - looks like utf8title is unused!
995 * This should be fixed to handle TAB characters.
998 pico_help(char *utf8text[], char *utf8title, int i)
1000 register int numline = 0;
1001 char **p;
1003 p = utf8text;
1004 while(*p++ != NULL)
1005 numline++;
1007 return(wscrollw(COMPOSER_TOP_LINE, term.t_nrow-1, utf8text, numline));
1013 * zotedit() - kills the buffer and frees all lines associated with it!!!
1015 void
1016 zotedit(void)
1018 wheadp->w_linep = wheadp->w_dotp = wheadp->w_markp = wheadp->w_imarkp = NULL;
1019 bheadp->b_linep = bheadp->b_dotp = bheadp->b_markp = NULL;
1021 free((char *) wheadp); /* clean up window */
1022 wheadp = NULL;
1023 curwp = NULL;
1025 free((char *) bheadp); /* clean up buffers */
1026 bheadp = NULL;
1027 curbp = NULL;
1029 zotheader(); /* blast header lines */
1031 kdelete(); /* blast kill buffer */
1036 #ifdef MOUSE
1038 * Generic mouse handling functions
1040 MENUITEM menuitems[12]; /* key labels and functions */
1041 MENUITEM *mfunc = NULL; /* list of regional functions */
1042 mousehandler_t mtrack; /* mouse tracking handler */
1044 /* last mouse position */
1045 static unsigned long levent = 0L;
1046 static int lrow = 0, lcol = 0, doubleclick, lbutton, lflags;
1047 #ifdef DOS
1048 static clock_t lastcalled = 0;
1049 #else
1050 static time_t lastcalled = 0;
1051 #endif
1052 static mousehandler_t lastf;
1056 * register_mfunc - register the given function to get called
1057 * on mouse events in the given display region
1060 register_mfunc(mousehandler_t f, int tlr, int tlc, int brr, int brc)
1062 MENUITEM **mp;
1064 if(!mouseexist())
1065 return(FALSE);
1067 for(mp = &mfunc; *mp; mp = &(*mp)->next)
1070 *mp = (MENUITEM *)malloc(sizeof(MENUITEM));
1071 memset(*mp, 0, sizeof(MENUITEM));
1073 (*mp)->action = f;
1074 (*mp)->tl.r = tlr;
1075 (*mp)->br.r = brr;
1076 (*mp)->tl.c = tlc;
1077 (*mp)->br.c = brc;
1078 (*mp)->lbl.c = (*mp)->lbl.r = 0;
1079 (*mp)->label = "";
1080 return(TRUE);
1085 * clear_mfunc - clear any previously set mouse function
1087 void
1088 clear_mfunc(mousehandler_t f)
1090 MENUITEM *mp, *tp;
1092 if((mp = mfunc) != NULL){
1093 if(mp->action == f)
1094 mfunc = mp->next;
1095 else
1096 for(tp = mp; tp->next; tp = tp->next)
1097 if(tp->next->action == f){
1098 mp = tp->next;
1099 tp->next = tp->next->next;
1100 break;
1103 if(mp){
1104 mp->action = NULL;
1105 free(mp);
1112 #ifdef EX_MOUSE
1114 void
1115 clear_mtrack(void)
1117 mtrack = NULL;
1118 mswin_allowmousetrack (FALSE);
1122 void
1123 register_mtrack(mousehandler_t f)
1125 if (f) {
1126 mtrack = f;
1127 mswin_allowmousetrack (TRUE);
1129 else
1130 clear_mtrack ();
1134 static void
1135 move_dot_to(int row, int col)
1137 LINE *lp;
1138 int i;
1140 lp = curwp->w_linep;
1141 i = row - ((Pmaster) ? ComposerTopLine : 2);
1142 while(i-- && lp != curbp->b_linep) /* count from top */
1143 lp = lforw(lp);
1144 curgoal = col;
1145 curwp->w_dotp = lp; /* to new dot. */
1146 curwp->w_doto = getgoal(lp);
1147 curwp->w_flag |= WFMOVE;
1152 * mouse_in_pico
1154 * When the mouse goes down in the body we set the mark and start
1155 * tracking.
1157 * As the mouse moves we update the dot and redraw the screen.
1159 * If the mouse moves above or below the pico body region of the
1160 * screen we scroll the text and update the dot position.
1162 * When the mouse comes up we clean up. If the mouse did not
1163 * move, then we clear the mark and turn off the selection.
1165 * Most of the mouse processing is handled here. The exception is
1166 * mouse down in the header. Can't call HeaderEditor() from here so
1167 * we send up the KEY_MOUSE character, which gets dispatched to
1168 * mousepress(), which _can_ call HeaderEditor().
1170 unsigned long
1171 mouse_in_pico(unsigned long mevent, int row, int col, int button, int flags)
1173 unsigned long rv = 0; /* Our return value. */
1174 int trow, tcol; /* translated row and col. */
1176 static int lheader = FALSE; /* Mouse down was in header. */
1180 * What's up.
1182 switch (mevent) {
1183 case M_EVENT_DOWN:
1184 if(button != M_BUTTON_LEFT)
1185 break;
1187 /* Ignore mouse down if not in pico body region. */
1188 if (row < 2 || row > term.t_nrow - (term.t_mrow+1)) {
1189 clear_mtrack ();
1190 break;
1193 /* Detect double clicks. Not that we do anything with em, just
1194 * detect them. */
1195 #ifdef DOS
1196 #ifdef CLOCKS_PER_SEC
1197 doubleclick = (lrow == row && lcol == col
1198 && clock() < (lastcalled + CLOCKS_PER_SEC/2));
1199 #else
1200 #ifdef CLK_TCK
1201 doubleclick = (lrow == row && lcol == col
1202 && clock() < (lastcalled + CLK_TCK/2));
1203 #else
1204 doubleclick = FALSE;
1205 #endif
1206 #endif
1207 lastcalled = clock();
1208 #else
1209 doubleclick = (lrow == row && lcol == col
1210 && time(0) < (lastcalled + 2));
1211 lastcalled = time(0);
1212 #endif
1213 lheader = FALSE; /* Rember mouse down position. */
1214 levent = mevent;
1215 lrow = row;
1216 lcol = col;
1217 lbutton = button;
1218 lflags = flags;
1220 /* Mouse down in body? */
1221 if (row < (Pmaster ? ComposerTopLine : 2)) {
1222 /* Mouse down in message header -> no tracking, just remember
1223 * where */
1224 lheader = TRUE;
1226 else {
1227 /* Mouse down in message.
1228 * If no shift key and an existing mark -> clear the mark.
1229 * If shift key and no existing mark -> set mark before moving */
1230 if (!(flags & M_KEY_SHIFT) && curwp->w_markp)
1231 setmark (0,1); /* this clears the mark. */
1232 else if (flags & M_KEY_SHIFT && !curwp->w_markp)
1233 setmark (0,1); /* while this sets the mark. */
1235 /* Reposition dot to mouse down. */
1236 move_dot_to (row, col);
1238 /* Set the mark to dot if no existing mark. */
1239 if (curwp->w_markp == NULL)
1240 setmark (0,1);
1242 /* Track mouse movement. */
1243 register_mtrack (mouse_in_pico);
1244 update ();
1245 lheader = FALSE; /* Just to be sure. */
1247 break;
1250 case M_EVENT_TRACK:
1251 /* Mouse tracking. */
1252 if (lheader) /* Ignore mouse movement in header. */
1253 break;
1255 /* If above or below body, scroll body and adjust the row and col. */
1256 if (row < (Pmaster ? ComposerTopLine : 2)) {
1257 /* Scroll text down screen and move dot to top left corner. */
1258 scrollupline (0,1);
1259 trow = (Pmaster) ? ComposerTopLine : 2;
1260 tcol = 0;
1262 else if (row > term.t_nrow - (term.t_mrow + 1)) {
1263 /* Scroll text up screen and move dot to bottom right corner. */
1264 scrolldownline (0,1);
1265 trow = term.t_nrow - (term.t_mrow + 1);
1266 tcol = term.t_ncol;
1268 else {
1269 trow = row;
1270 tcol = col;
1273 /* Move dot to target column. */
1274 move_dot_to (trow, tcol);
1276 /* Update screen. */
1277 update ();
1278 break;
1281 case M_EVENT_UP:
1282 if(button == M_BUTTON_RIGHT){
1283 #ifdef _WINDOWS
1284 pico_popup();
1285 #endif
1286 break;
1288 else if(button != M_BUTTON_LEFT)
1289 break;
1291 if (lheader) {
1292 lheader = FALSE;
1293 /* Last down in header. */
1294 if (row == lrow && col == lcol) {
1295 /* Mouse up and down in same place in header. Means the
1296 * user want to edit the header. Return KEY_MOUSE which
1297 * will cause mousepress to be called, which will
1298 * call HeaderEditor. Can't call HeaderEditor from here
1299 * because that would mess up layering. */
1300 if (curwp->w_marko)
1301 setmark (0,1);
1302 rv = (unsigned long) KEY_MOUSE;
1305 else {
1306 /* If up at same place, clear mark */
1307 if (curwp->w_markp == curwp->w_dotp &&
1308 curwp->w_marko == curwp->w_doto) {
1309 setmark (0,1);
1310 curwp->w_flag |= WFMOVE;
1312 clear_mtrack ();
1313 update ();
1315 break;
1318 return(rv);
1320 #endif
1325 * mouse_in_content - general mechanism used to pass recognized mouse
1326 * events in predefined region back thru the usual
1327 * keyboard input stream. The actual return value
1328 * passed back from this function is set dynamically
1329 * via the "down" argument which is read when both the
1330 * "row" and "col" arguments are negative.
1332 unsigned long
1333 mouse_in_content(unsigned long mevent, int row, int col, int button, int flags)
1335 unsigned long rv = 0;
1336 static unsigned long mouse_val = KEY_MOUSE;
1338 if(row == -1 && col == -1){
1339 mouse_val = mevent; /* setting return value */
1341 else {
1342 /* A real event. */
1343 levent = mevent;
1344 switch (mevent) {
1345 case M_EVENT_DOWN:
1346 /* Mouse down does not mean anything, just keep track of
1347 * where it went down and if this is a double click. */
1348 #ifdef DOS
1349 #ifdef CLOCKS_PER_SEC
1350 doubleclick = (lrow == row && lcol == col
1351 && clock() < (lastcalled + CLOCKS_PER_SEC/2));
1352 #else
1353 #ifdef CLK_TCK
1354 doubleclick = (lrow == row && lcol == col
1355 && clock() < (lastcalled + CLK_TCK/2));
1356 #else
1357 doubleclick = FALSE;
1358 #endif
1359 #endif
1360 lastcalled = clock();
1361 #else
1362 doubleclick = (lrow == row && lcol == col
1363 && time(0) < (lastcalled + 2));
1364 lastcalled = time(0);
1365 #endif
1366 lrow = row;
1367 lcol = col;
1368 lbutton = button;
1369 lflags = flags;
1370 break;
1372 case M_EVENT_UP:
1373 /* Mouse up. If in the same position as it went down
1374 * then we return the value set above, which goes into
1375 * the character input stream, which gets processed as
1376 * a mouse event by some upper layer, which calls to
1377 * mouse_get_last(). */
1378 if (lrow == row && lcol == col) {
1379 rv = mouse_val;
1381 break;
1383 case M_EVENT_TRACK:
1384 break;
1388 return(rv);
1393 * mouse_get_last - Get last mouse event.
1396 void
1397 mouse_get_last(mousehandler_t *f, MOUSEPRESS *mp)
1399 if (f != NULL)
1400 *f = lastf;
1401 if (mp != NULL) {
1402 mp->mevent = levent;
1403 mp->row = lrow;
1404 mp->col = lcol;
1405 mp->doubleclick = doubleclick;
1406 mp->button = lbutton;
1407 mp->flags = lflags;
1414 * register_key - register the given keystroke to accept mouse events
1416 void
1417 register_key(int i, unsigned rval, char *label, void (*label_printer)(),
1418 int row, int col, int len, COLOR_PAIR *kn, COLOR_PAIR *kl)
1420 if(i > 11)
1421 return;
1423 menuitems[i].val = rval;
1424 menuitems[i].tl.r = menuitems[i].br.r = row;
1425 menuitems[i].tl.c = col;
1426 menuitems[i].br.c = col + len;
1427 menuitems[i].lbl.r = menuitems[i].tl.r;
1428 menuitems[i].lbl.c = menuitems[i].tl.c;
1429 menuitems[i].label_hiliter = label_printer;
1430 if(menuitems[i].label){
1431 free(menuitems[i].label);
1432 menuitems[i].label = NULL;
1434 if(menuitems[i].kncp)
1435 free_color_pair(&menuitems[i].kncp);
1436 if(menuitems[i].klcp)
1437 free_color_pair(&menuitems[i].klcp);
1438 if(kn)
1439 menuitems[i].kncp = new_color_pair(kn->fg, kn->bg);
1440 else
1441 menuitems[i].kncp = NULL;
1442 if(kl)
1443 menuitems[i].klcp = new_color_pair(kl->fg, kl->bg);
1444 else
1445 menuitems[i].klcp = NULL;
1447 if(label){
1448 size_t len;
1450 len = strlen(label);
1451 if((menuitems[i].label = (char *)malloc((len+1)*sizeof(char))) != NULL){
1452 strncpy(menuitems[i].label, label, len);
1453 menuitems[i].label[len] = '\0';
1460 mouse_on_key(int row, int col)
1462 int i;
1464 for(i = 0; i < 12; i++)
1465 if(M_ACTIVE(row, col, &menuitems[i]))
1466 return(TRUE);
1468 return(FALSE);
1470 #endif /* MOUSE */
1474 * Below are functions for use outside pico to manipulate text
1475 * in a pico's native format (circular linked list of lines).
1477 * The idea is to streamline pico use by making it fairly easy
1478 * for outside programs to prepare text intended for pico's use.
1479 * The simple char * alternative is messy as it requires two copies
1480 * of the same text, and isn't very economic in limited memory
1481 * situations (THANKS BELLEVUE-BILLY.).
1483 typedef struct picotext {
1484 LINE *linep;
1485 LINE *dotp;
1486 int doto;
1487 short crinread;
1488 } PICOTEXT;
1490 #define PT(X) ((PICOTEXT *)(X))
1493 * pico_get - return window struct pointer used as a handle
1494 * to the other pico_xxx routines.
1496 void *
1497 pico_get(void)
1499 PICOTEXT *wp = NULL;
1500 LINE *lp = NULL;
1502 if((wp = (PICOTEXT *)malloc(sizeof(PICOTEXT))) != NULL){
1503 wp->crinread = 0;
1504 if((lp = lalloc(0)) == NULL){
1505 free(wp);
1506 return(NULL);
1509 wp->dotp = wp->linep = lp->l_fp = lp->l_bp = lp;
1510 wp->doto = 0;
1512 else
1513 emlwrite("Can't allocate space for text", NULL);
1515 return((void *)wp);
1519 * pico_give - free resources and give up picotext struct
1521 void
1522 pico_give(void *w)
1524 register LINE *lp;
1525 register LINE *fp;
1527 fp = lforw(PT(w)->linep);
1528 while((lp = fp) != PT(w)->linep){
1529 fp = lforw(lp);
1530 free(lp);
1532 free(PT(w)->linep);
1533 free((PICOTEXT *)w);
1537 * pico_readc - return char at current point. Up to calling routines
1538 * to keep cumulative count of chars.
1539 * The characters in PT are UCS-4 characters. The caller
1540 * of pico_readc is expecting UTF-8 chars. We convert
1541 * each UCS-4 character to a string of UTF-8 characters
1542 * and return them one at a time.
1545 pico_readc(void *w, unsigned char *c, int flags)
1547 int rv = 0;
1548 UCS ucs;
1549 static unsigned char obuf[6];
1550 static unsigned char *obufpend = obuf;
1551 static unsigned char *obufpnext = obuf;
1553 if(!(flags & PICOREADC_NOUCS)){
1554 if(obufpend > obuf){
1555 *c = *obufpnext++;
1556 rv++;
1557 if(obufpnext >= obufpend){
1558 obufpend = obuf;
1559 obufpnext = obuf;
1562 return(rv);
1566 if(PT(w)->crinread){
1567 *c = '\012'; /* return LF */
1568 PT(w)->crinread = 0;
1569 rv++;
1571 else if(PT(w)->doto < llength(PT(w)->dotp)){ /* normal char to return */
1572 if(flags & PICOREADC_NOUCS){
1573 *c = (unsigned char) lgetc(PT(w)->dotp, (PT(w)->doto)++).c;
1574 rv++;
1576 else{
1577 rv++;
1578 ucs = lgetc(PT(w)->dotp, (PT(w)->doto)++).c;
1579 obufpend = utf8_put(obuf, (unsigned long) ucs);
1580 obufpnext = obuf;
1581 if(obufpend > obuf){
1582 *c = *obufpnext++;
1583 if(obufpnext >= obufpend){
1584 obufpend = obuf;
1585 obufpnext = obuf;
1588 else
1589 *c = '?';
1592 else if(PT(w)->dotp != PT(w)->linep){ /* return line break */
1593 PT(w)->dotp = lforw(PT(w)->dotp);
1594 PT(w)->doto = 0;
1595 #if defined(DOS) || defined(OS2)
1596 *c = '\015';
1597 PT(w)->crinread++;
1598 #else
1599 *c = '\012'; /* return local eol! */
1600 #endif
1601 rv++;
1602 } /* else no chars to return */
1604 return(rv);
1609 * pico_writec - write a char into picotext and advance pointers.
1610 * Up to calling routines to keep track of total chars
1611 * written.
1612 * Incoming text (c) is UTF-8 and written chars are UCS-4.
1613 * We need to collect up multiple chars to make a single
1614 * UCS-4 char, so there needs to be some state between calls.
1617 pico_writec(void *w, int c, int flags)
1619 int rv = 0;
1621 if(c == '\r') /* ignore CR's */
1622 rv++; /* so fake it */
1623 else if(c == '\n'){ /* insert newlines on LF */
1625 * OK, if there are characters on the current line or
1626 * dotp is pointing to the delimiter line, insert a newline
1627 * No here's the tricky bit; preserve the implicit EOF newline.
1629 if(lforw(PT(w)->dotp) == PT(w)->linep && PT(w)->dotp != PT(w)->linep){
1630 PT(w)->dotp = PT(w)->linep;
1631 PT(w)->doto = 0;
1633 else{
1634 register LINE *lp;
1636 if((lp = lalloc(0)) == NULL){
1637 emlwrite("Can't allocate space for more characters",NULL);
1638 return(0);
1641 if(PT(w)->dotp == PT(w)->linep){
1642 lforw(lp) = PT(w)->linep;
1643 lback(lp) = lback(PT(w)->linep);
1644 lforw(lback(lp)) = lback(PT(w)->linep) = lp;
1646 else{
1647 lforw(lp) = lforw(PT(w)->dotp);
1648 lback(lp) = PT(w)->dotp;
1649 lback(lforw(lp)) = lforw(PT(w)->dotp) = lp;
1650 PT(w)->dotp = lp;
1651 PT(w)->doto = 0;
1655 rv++;
1657 else{
1658 if(flags & PICOREADC_NOUCS){
1660 * With this flag we're reverting to the old behavior where no
1661 * UTF-8 to UCS-4 translation takes place. We assume nothing
1662 * about the incoming byte stream. We just write a byte at a
1663 * time. So even though it's being stored in PT each
1664 * item is really limited to a single octet value.
1666 rv = geninsert(&PT(w)->dotp, &PT(w)->doto, PT(w)->linep, c, 0, 1, NULL);
1668 else{
1669 static unsigned char cbuf[6];
1670 static unsigned char *cbufp = cbuf;
1671 UCS obuf[MAX(MB_LEN_MAX,32)];
1672 int i, outchars = 0;
1674 if(cbufp < cbuf+sizeof(cbuf)){
1675 unsigned char *inputp;
1676 unsigned long remaining_octets;
1677 UCS ucs;
1679 *cbufp++ = (unsigned char) c;
1680 inputp = cbuf;
1681 remaining_octets = (cbufp - cbuf) * sizeof(unsigned char);
1682 ucs = (UCS) utf8_get(&inputp, &remaining_octets);
1684 switch(ucs){
1685 case U8G_ENDSTRG: /* incomplete character, wait */
1686 case U8G_ENDSTRI: /* incomplete character, wait */
1687 break;
1689 default:
1690 if(ucs & U8G_ERROR || ucs == UBOGON){
1692 * None of these cases is supposed to happen. If it
1693 * does happen then the input stream isn't UTF-8
1694 * so something is wrong. Treat each character in the
1695 * input buffer as a separate error character and
1696 * print a '?' for each.
1698 for(inputp = cbuf; inputp < cbufp; inputp++)
1699 obuf[outchars++] = '?';
1701 cbufp = cbuf;
1703 else{
1704 /* got a character */
1705 if(ucs >= 0x80 && wcellwidth(ucs) < 0){
1707 * This happens when we have a UTF-8 character that
1708 * we aren't able to print in our locale. For example,
1709 * if the locale is setup with the terminal
1710 * expecting ISO-8859-1 characters then there are
1711 * lots of UTF-8 characters that can't be printed.
1712 * Print a '?' instead.
1713 This may be the wrong thing to do. What happens if user
1714 is just forwarding and doesn't edit. We are going to lose
1715 the original value, aren't we? Maybe we do this only
1716 when printing to the screen instead.
1718 obuf[outchars++] = '?';
1720 else{
1722 * A regular ucs character
1724 obuf[outchars++] = ucs;
1727 /* update the input buffer */
1728 if(inputp >= cbufp) /* this should be the case */
1729 cbufp = cbuf;
1730 else{ /* extra chars for some reason? */
1731 unsigned char *q, *newcbufp;
1733 newcbufp = (cbufp - inputp) + cbuf;
1734 q = cbuf;
1735 while(inputp < cbufp)
1736 *q++ = *inputp++;
1738 cbufp = newcbufp;
1742 break;
1745 else{ /* error */
1746 obuf[0] = '?';
1747 outchars = 1;
1748 cbufp = cbuf; /* start over */
1752 * Unless we have trouble translating outchars will be
1753 * either 1 or 0. It is the UCS-4 character that we converted
1754 * the input to. It's an array just so it can hold ? when
1755 * there is trouble.
1757 rv = 1;
1758 for(i = 0; rv && i < outchars; i++)
1759 if(!geninsert(&PT(w)->dotp, &PT(w)->doto, PT(w)->linep, obuf[i], 0, 1, NULL))
1760 rv = 0;
1765 * Warning, this is no longer number written, because when we have a
1766 * multibyte UTF-8 character we won't write anything until we get all
1767 * the bytes.
1769 return((rv) ? 1 : 0); /* return number written */
1774 * pico_puts - just write the given string into the text
1777 pico_puts(void *w, char *s, int flags)
1779 int rv = 0;
1781 if(*s != '\0'){
1782 rv = 1;
1783 while(rv && *s != '\0')
1784 if(!pico_writec(w, (int)*s++, flags))
1785 rv = 0;
1788 return((rv) ? 1 : 0);
1793 * pico_seek - position dotp and dot at requested location
1796 pico_seek(void *w, long offset, int orig)
1798 register LINE *lp;
1800 PT(w)->crinread = 0;
1801 switch(orig){
1802 case 0 : /* SEEK_SET */
1803 PT(w)->dotp = lforw(PT(w)->linep);
1804 PT(w)->doto = 0;
1805 case 1 : /* SEEK_CUR */
1806 lp = PT(w)->dotp;
1807 while(lp != PT(w)->linep){
1808 if(offset <= llength(lp)){
1809 PT(w)->doto = (int)offset;
1810 PT(w)->dotp = lp;
1811 break;
1814 offset -= ((long)llength(lp)
1815 #if defined(DOS) || defined(OS2)
1816 + 2L);
1817 #else
1818 + 1L);
1819 #endif
1820 lp = lforw(lp);
1822 break;
1824 case 2 : /* SEEK_END */
1825 PT(w)->dotp = lback(PT(w)->linep);
1826 PT(w)->doto = llength(PT(w)->dotp);
1827 break;
1828 default :
1829 return(-1);
1832 return(0);
1837 * breplace - replace the current window's text with the given
1838 * LINEs
1840 void
1841 breplace(void *w)
1843 register LINE *lp;
1844 register LINE *fp;
1846 fp = lforw(curbp->b_linep);
1847 while((lp = fp) != curbp->b_linep){ /* blast old lines */
1848 fp = lforw(lp);
1849 free(lp);
1851 free(curbp->b_linep);
1853 curbp->b_linep = PT(w)->linep; /* arrange pointers */
1856 * Undo space-stuffing that was done when we were preparing to send.
1857 * Some error happened so we're back to the composer.
1859 if(Pmaster && Pmaster->space_stuffed){
1860 Pmaster->space_stuffed = 0;
1861 for(lp = lforw(curbp->b_linep); lp != curbp->b_linep; lp = lforw(lp)){
1862 if(llength(lp) && ucs4_isspace(lgetc(lp, 0).c)){
1863 curwp->w_dotp = lp;
1864 curwp->w_doto = 0;
1865 forwdel(FALSE,1);
1870 curwp->w_linep = lforw(curbp->b_linep);
1871 curwp->w_dotp = lforw(curbp->b_linep);
1872 curwp->w_doto = 0;
1873 curwp->w_markp = curwp->w_imarkp = NULL;
1874 curwp->w_marko = curwp->w_imarko = 0;
1876 curbp->b_dotp = curwp->w_dotp;
1877 curbp->b_doto = curbp->b_marko = 0;
1878 curbp->b_markp = NULL;
1879 curbp->b_linecnt = -1;
1881 curwp->w_flag |= WFHARD;
1885 #ifdef _WINDOWS
1890 composer_file_drop(int x, int y, char *filename)
1892 int attached = 0;
1893 EML eml;
1895 if((ComposerTopLine > 2 && x <= ComposerTopLine)
1896 || !LikelyASCII(filename)){
1897 AppendAttachment(filename, NULL, NULL);
1898 attached++;
1900 else{
1901 setimark(FALSE, 1);
1902 ifile(filename);
1903 swapimark(FALSE, 1);
1906 if(ComposerEditing){ /* update display */
1907 PaintBody(0);
1909 else{
1910 pico_refresh(0, 1);
1911 update();
1914 eml.s = filename;
1915 if(attached)
1916 emlwrite(_("Attached dropped file \"%s\""), &eml);
1917 else
1918 emlwrite(_("Inserted dropped file \"%s\""), &eml);
1920 if(ComposerEditing){ /* restore cursor */
1921 HeaderPaintCursor();
1923 else{
1924 curwp->w_flag |= WFHARD;
1925 update();
1928 return(1);
1933 pico_cursor(int col, long row)
1935 return((row > 1 && row <= term.t_nrow - (term.t_mrow + 1))
1936 ? MSWIN_CURSOR_IBEAM
1937 : MSWIN_CURSOR_ARROW);
1939 #endif /* _WINDOWS */