* Update to packages/alpine.spec to account for the new location of man
[alpine.git] / pico / pico.c
blob9260d34fcccfd837da6b5655c021c754d74b805e
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-2017 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 remove_directions_mark(void);
76 void func_init(void);
77 void breplace(void *w);
78 int any_header_changes(void);
79 int cleanwhitespace(void);
80 int isquotedspace(LINE *);
84 * function key mappings
86 static UCS pfkm[12][2] = {
87 { F1, (CTRL|'G')},
88 { F2, (CTRL|'C')},
89 { F3, (CTRL|'X')},
90 { F4, (CTRL|'J')},
91 { F5, (CTRL|'R')},
92 { F6, (CTRL|'W')},
93 { F7, (CTRL|'Y')},
94 { F8, (CTRL|'V')},
95 { F9, (CTRL|'K')},
96 { F10, (CTRL|'U')},
97 { F11, (CTRL|'O')},
98 #ifdef SPELLER
99 { F12, (CTRL|'T')}
100 #else
101 { F12, (CTRL|'D')}
102 #endif
106 void
107 remove_directions_mark(void)
109 LINE *lp;
110 int i;
111 UCS c;
113 for(lp = lforw(curbp->b_linep); lp != curbp->b_linep; lp = lforw(lp)){
114 for(i = 0; i < llength(lp);){
115 c = lgetc(lp, i).c;
116 if(c == 0x200E || c == 0x200F){
117 curwp->w_dotp = lp;
118 curwp->w_doto = i;
119 if(i < llength(lp))
120 forwdel(FALSE, 1);
121 else
122 llength(lp)--;
123 direction = c == 0x200E ? 0 : 1;
125 else
126 lgetc(lp,i++).d = direction;
129 curwp->w_linep = lforw(curbp->b_linep);
130 curwp->w_dotp = lforw(curbp->b_linep);
131 curwp->w_doto = 0;
136 * flag for the various functions in pico() to set when ready
137 * for pico() to return...
139 int pico_all_done = 0;
140 jmp_buf finstate;
141 UCS *pico_anchor = NULL;
142 extern struct headerentry *headents;
145 * pico - the main routine for Pine's composer.
149 pico(PICO *pm)
151 UCS c;
152 register int f;
153 register int n;
154 char bname[NBUFN]; /* buffer name of file to read */
155 extern struct on_display ods;
156 int checkpointcnt = 0, input = 0;
157 int ret;
158 char chkptfile[NLINE];
159 #ifdef _WINDOWS
160 int cursor_shown;
161 #endif
163 Pmaster = pm;
164 gmode = MDWRAP;
165 gmode |= pm->pine_flags; /* high 4 bits rsv'd for pine */
167 alt_speller = pm->alt_spell;
168 #ifdef _WINDOWS
169 dictionary = pm->dict;
170 chosen_dict = pm->chosen_dict;
171 #endif /* _WINDOWS */
172 pico_all_done = 0;
173 km_popped = 0;
175 if(!vtinit()) /* Init Displays. */
176 return(COMP_CANCEL);
178 strncpy(bname, "main", sizeof(bname)); /* default buffer name */
179 bname[sizeof(bname)-1] = '\0';
180 edinit(bname); /* Buffers, windows. */
182 if(InitMailHeader(pm)) /* init mail header structure */
183 gmode &= ~(P_BODY | P_HEADEND); /* flip off special header stuff */
185 /* setup to process commands */
186 lastflag = 0; /* Fake last flags. */
187 curbp->b_mode |= gmode; /* and set default modes*/
189 if(Pmaster->pine_anchor)
190 pico_anchor = utf8_to_ucs4_cpystr(Pmaster->pine_anchor);
191 else
192 pico_anchor = NULL;
194 if(Pmaster->quote_str)
195 glo_quote_str = utf8_to_ucs4_cpystr(Pmaster->quote_str);
196 else
197 glo_quote_str = NULL;
199 if(Pmaster->wordseps)
200 glo_wordseps = ucs4_cpystr(Pmaster->wordseps);
201 else
202 glo_wordseps = NULL;
204 bindtokey(DEL, (gmode & P_DELRUBS) ? forwdel : backdel);
206 if(pm->msgtext)
207 breplace(pm->msgtext);
209 remove_directions_mark();
211 #ifdef _WINDOWS
212 cursor_shown = mswin_showcaret(1); /* turn on for main window */
213 mswin_allowpaste(MSWIN_PASTE_FULL);
214 mswin_setscrollcallback (pico_scroll_callback);
215 #endif
217 /* prepare for checkpointing */
218 chkptfile[0] = '\0';
219 chkptinit((*Pmaster->ckptdir)(chkptfile, sizeof(chkptfile)), sizeof(chkptfile));
220 if(gmode & P_CHKPTNOW)
221 writeout(chkptfile, TRUE);
223 pico_all_done = setjmp(finstate); /* jump out of HUP handler ? */
225 if(gmode & MDALTNOW){
226 while(!pico_all_done){
227 if(((gmode & P_BODY) || !Pmaster->headents)
228 && alt_editor(0, 1) < 0)
229 break; /* if problem, drop into pico */
231 if(Pmaster->headents){
232 update(); /* paint screen, n' start editing... */
233 HeaderEditor((gmode & (P_HEADEND | P_BODY)) ? 2 : 0, 0);
234 gmode |= P_BODY; /* make sure we enter alt ed next */
236 else
237 pico_all_done = COMP_EXIT;
240 else if(!pico_all_done){
241 if(gmode & P_BODY){ /* begin editing the header? */
242 ArrangeHeader(); /* line up pointers */
244 * Move to the offset pine asked us to move to.
245 * Perhaps we should be checking to see if this is
246 * a reasonable number before moving.
248 if(Pmaster && Pmaster->edit_offset)
249 forwchar(FALSE, Pmaster->edit_offset);
251 else{
252 update(); /* paint screen, */
253 HeaderEditor((gmode & P_HEADEND) ? 2 : 0, 0);
257 while(1){
258 if(pico_all_done){
259 #ifdef _WINDOWS
260 if(!cursor_shown)
261 mswin_showcaret(0);
263 mswin_allowpaste(MSWIN_PASTE_DISABLE);
264 mswin_setscrollcallback (NULL);
265 #endif
266 ret = anycb() ? BUF_CHANGED : 0;
267 switch(pico_all_done){ /* prepare for/handle final events */
268 case COMP_EXIT : /* already confirmed */
269 packheader();
270 if(Pmaster
271 && (Pmaster->strip_ws_before_send
272 || Pmaster->allow_flowed_text))
273 cleanwhitespace();
274 ret |= COMP_EXIT;
275 break;
277 case COMP_CANCEL : /* also already confirmed */
278 packheader();
279 ret = COMP_CANCEL;
280 break;
282 case COMP_GOTHUP:
284 * pack up and let caller know that we've received a SIGHUP
286 if(ComposerEditing) /* expand addr if needed */
287 call_builder(&headents[ods.cur_e], NULL, NULL);
289 packheader();
290 ret |= COMP_GOTHUP;
291 break;
293 case COMP_SUSPEND :
294 default: /* safest if internal error */
296 * If we're in the headers mark the current header line
297 * with start_here bit so caller knows where to reset.
298 * Also set the edit_offset, which is either the offset
299 * into this header line or the offset into the body.
300 * Packheader will adjust edit_offset for multi-line
301 * headers.
303 if(ComposerEditing){ /* in the headers */
304 headents[ods.cur_e].start_here = 1;
305 Pmaster->edit_offset = ods.p_ind;
307 else{
308 register LINE *clp;
309 register long offset;
311 for(clp = lforw(curbp->b_linep), offset = 0L;
312 clp != curwp->w_dotp;
313 clp = lforw(clp))
314 offset += (llength(clp) + 1);
316 Pmaster->edit_offset = offset + curwp->w_doto;
319 packheader();
320 ret |= COMP_SUSPEND;
321 break;
324 if(pico_anchor)
325 fs_give((void **) &pico_anchor);
326 if(glo_quote_str)
327 fs_give((void **) &glo_quote_str);
328 if(glo_wordseps)
329 fs_give((void **) &glo_wordseps);
331 vttidy(); /* clean up tty modes */
332 zotdisplay(); /* blast display buffers */
333 zotedit();
334 our_unlink(chkptfile);
335 Pmaster = NULL; /* blat global */
337 return(ret);
340 if(km_popped){
341 km_popped--;
342 if(km_popped == 0) /* cause bottom three lines to be repainted */
343 curwp->w_flag |= WFHARD;
346 if(km_popped){ /* temporarily change to cause menu to be painted */
347 term.t_mrow = 2;
348 curwp->w_ntrows -= 2;
349 curwp->w_flag |= WFMODE;
350 movecursor(term.t_nrow-2, 0); /* clear status line, too */
351 peeol();
354 update(); /* Fix up the screen */
355 if(km_popped){
356 term.t_mrow = 0;
357 curwp->w_ntrows += 2;
360 #ifdef MOUSE
361 #ifdef EX_MOUSE
362 /* New mouse function for real mouse text seletion. */
363 register_mfunc(mouse_in_pico, 2, 0, term.t_nrow - (term.t_mrow+1),
364 term.t_ncol);
365 #else
366 mouse_in_content(KEY_MOUSE, -1, -1, -1, 0);
367 register_mfunc(mouse_in_content, 2, 0, term.t_nrow - (term.t_mrow + 1),
368 term.t_ncol);
369 #endif
370 #endif
371 #ifdef _WINDOWS
372 mswin_setdndcallback (composer_file_drop);
373 mswin_mousetrackcallback(pico_cursor);
374 #endif
375 c = GetKey();
376 if (term.t_nrow < 6 && c != NODATA){
377 (*term.t_beep)();
378 emlwrite(_("Please make the screen bigger."), NULL);
379 continue;
382 #ifdef MOUSE
383 #ifdef EX_MOUSE
384 clear_mfunc(mouse_in_pico);
385 #else
386 clear_mfunc(mouse_in_content);
387 #endif
388 #endif
389 #ifdef _WINDOWS
390 mswin_cleardndcallback ();
391 mswin_mousetrackcallback(NULL);
392 #endif
393 if(c == NODATA || time_to_check()){ /* new mail ? */
394 if((*Pmaster->newmail)(c == NODATA ? 0 : 2, 1) >= 0){
395 int rv;
397 if(km_popped){
398 term.t_mrow = 2;
399 curwp->w_ntrows -= 2;
400 curwp->w_flag |= WFHARD;
401 km_popped = 0;
404 clearcursor();
405 mlerase();
406 rv = (*Pmaster->showmsg)(c);
407 ttresize();
408 picosigs(); /* restore altered handlers */
409 if(rv) /* Did showmsg corrupt the display? */
410 PaintBody(0); /* Yes, repaint */
412 mpresf = 1;
413 input = 0;
416 clearcursor();
417 movecursor(0, 0);
420 if(km_popped)
421 switch(c){
422 case NODATA:
423 case (CTRL|'L'):
424 km_popped++;
425 break;
427 default:
428 mlerase();
429 break;
432 if(c == NODATA) /* no op, getkey timed out */
433 continue;
434 else if(!input++)
435 (*Pmaster->keybinput)();
437 if (mpresf != FALSE) { /* message stay around only */
438 if (mpresf++ > NMMESSDELAY) /* so long! */
439 mlerase();
442 f = FALSE; /* vestigial */
443 n = 1;
444 /* Do it. */
445 execute(normalize_cmd(c, pfkm, 2), f, n);
446 if(++checkpointcnt >= CHKPTDELAY){
447 checkpointcnt = 0;
448 writeout(chkptfile, TRUE);
454 * Initialize all of the buffers and windows. The buffer name is passed down
455 * as an argument, because the main routine may have been told to read in a
456 * file by default, and we want the buffer name to be right.
460 * For the pine composer, we don't want to take over the whole screen
461 * for editing. the first some odd lines are to be used for message
462 * header information editing.
464 void
465 edinit(char bname[])
467 register BUFFER *bp;
468 register WINDOW *wp;
470 if(Pmaster)
471 func_init();
473 bp = bfind(bname, TRUE, BFWRAPOPEN); /* First buffer */
474 wp = (WINDOW *) malloc(sizeof(WINDOW)); /* First window */
476 if (bp==NULL || wp==NULL){
477 if(Pmaster)
478 return;
479 else
480 exit(1);
483 curbp = bp; /* Make this current */
484 wheadp = wp;
485 curwp = wp;
486 wp->w_wndp = NULL; /* Initialize window */
487 wp->w_bufp = bp;
488 bp->b_nwnd = 1; /* Displayed. */
489 wp->w_linep = bp->b_linep;
490 wp->w_dotp = bp->b_linep;
491 wp->w_doto = 0;
492 wp->w_markp = wp->w_imarkp = NULL;
493 wp->w_marko = wp->w_imarko = 0;
494 bp->b_linecnt = -1;
496 if(Pmaster){
497 term.t_mrow = Pmaster->menu_rows;
498 wp->w_toprow = ComposerTopLine = COMPOSER_TOP_LINE;
499 wp->w_ntrows = term.t_nrow - COMPOSER_TOP_LINE - term.t_mrow;
500 fillcol = Pmaster->fillcolumn;
501 strncpy(opertree,
502 (Pmaster->oper_dir && strlen(Pmaster->oper_dir) < NLINE)
503 ? Pmaster->oper_dir : "", sizeof(opertree));
504 opertree[sizeof(opertree)-1] = '\0';
505 input_cs = Pmaster->input_cs;
507 else{
508 if(sup_keyhelp)
509 term.t_mrow = 0;
510 else
511 term.t_mrow = 2;
513 wp->w_toprow = 2;
514 wp->w_ntrows = term.t_nrow - 2 - term.t_mrow;
515 if(userfillcol > 0) /* set fill column */
516 fillcol = userfillcol;
517 else
518 fillcol = term.t_ncol - 6;
522 * MDSCUR mode implies MDTREE mode with a opertree of home directory,
523 * unless opertree has been set differently.
525 if((gmode & MDSCUR) && !opertree[0]){
526 strncpy(opertree, gethomedir(NULL), sizeof(opertree));
527 opertree[sizeof(opertree)-1] = '\0';
530 if(*opertree)
531 fixpath(opertree, sizeof(opertree));
533 wp->w_force = 0;
534 wp->w_flag = WFMODE|WFHARD; /* Full. */
539 * This is the general command execution routine. It handles the fake binding
540 * of all the keys to "self-insert". It also clears out the "thisflag" word,
541 * and arranges to move it to the "lastflag", so that the next command can
542 * look at it. Return the status of command.
545 execute(UCS c, int f, int n)
547 KEYTAB *ktp;
548 int status, ww;
550 ktp = (Pmaster) ? &keytab[0] : &pkeytab[0];
552 while (ktp->k_fp != NULL) {
553 if (ktp->k_code == c) {
555 if(lastflag&CFFILL){
556 curwp->w_flag |= WFMODE;
557 if(Pmaster == NULL)
558 sgarbk = TRUE;
561 thisflag = 0;
562 status = (*ktp->k_fp)(f, n);
563 if((lastflag & CFFILL) && !(thisflag & CFFILL))
564 fdelete();
565 if((lastflag & CFFLBF) && !(thisflag & CFFLBF))
566 kdelete();
568 lastflag = thisflag;
571 * Reset flag saying wrap should open a new line whenever
572 * we execute a command (as opposed to just typing in text).
573 * However, if that command leaves us in the same line on the
574 * screen, then don't reset.
576 if(curwp->w_flag & (WFMOVE | WFHARD))
577 curbp->b_flag |= BFWRAPOPEN; /* wrap should open new line */
579 return (status);
581 ++ktp;
584 if(lastflag & CFFILL) /* blat unusable fill data */
585 fdelete();
586 if(lastflag & CFFLBF)
587 kdelete();
589 if (VALID_KEY(c)) { /* Self inserting. */
591 if (n <= 0) { /* Fenceposts. */
592 lastflag = 0;
593 return (n<0 ? FALSE : TRUE);
595 thisflag = 0; /* For the future. */
597 /* do the appropriate insertion */
598 /* pico never does C mode, this is simple */
599 status = linsert(n, c);
602 * Check to make sure we didn't go off of the screen
603 * with that character. Take into account tab expansion.
604 * If so wrap the line...
606 if(curwp->w_bufp->b_mode & MDWRAP){
607 int j, wid;
609 wid = 0;
610 for(j = 0; j < llength(curwp->w_dotp); j++)
611 if(ucs4_isspace(lgetc(curwp->w_dotp, j).c)){
612 if(lgetc(curwp->w_dotp, j).c == TAB){
613 ++wid;
614 while(wid & 0x07)
615 ++wid;
617 else
618 ++wid;
620 else{
621 ww = wcellwidth((UCS) lgetc(curwp->w_dotp, j).c);
622 wid += (ww >= 0 ? ww : 1);
623 if(wid > fillcol){
624 wrapword();
625 break;
630 lastflag = thisflag;
631 return (status);
634 unknown_command(c);
636 lastflag = 0; /* Fake last flags. */
637 return (FALSE);
643 * Fancy quit command, as implemented by Norm. If the any buffer has
644 * changed do a write on that buffer and exit emacs, otherwise simply exit.
647 quickexit(int f, int n)
649 register BUFFER *bp; /* scanning pointer to buffers */
651 bp = bheadp;
652 while (bp != NULL) {
653 if ((bp->b_flag&BFCHG) != 0 /* Changed. */
654 && (bp->b_flag&BFTEMP) == 0) { /* Real. */
655 curbp = bp; /* make that buffer cur */
656 filesave(f, n);
658 bp = bp->b_bufp; /* on to the next buffer */
660 return(wquit(f, n)); /* conditionally quit */
666 * abort_composer - ask the question here, then go quit or
667 * return FALSE
670 abort_composer(int f, int n)
672 char *result;
674 result = "";
676 Pmaster->arm_winch_cleanup++;
677 if(Pmaster->canceltest){
678 if(((Pmaster->pine_flags & MDHDRONLY) && !any_header_changes())
679 || (result = (*Pmaster->canceltest)(redraw_pico_for_callback))){
680 pico_all_done = COMP_CANCEL;
681 emlwrite(result, NULL);
682 Pmaster->arm_winch_cleanup--;
683 return(TRUE);
685 else{
686 /* TRANSLATORS: The user typed the Cancel command and was
687 asked to confirm that. Instead they canceled the cancel
688 command. */
689 emlwrite(_("Cancel Cancelled"), NULL);
690 curwp->w_flag |= WFMODE; /* and modeline so we */
691 sgarbk = TRUE; /* redraw the keymenu */
692 pclear(term.t_nrow-1, term.t_nrow);
693 Pmaster->arm_winch_cleanup--;
694 return(FALSE);
697 else switch(mlyesno_utf8(Pmaster->headents
698 ? _("Cancel message (answering \"Yes\" will abandon your mail message)")
699 : (anycb() == FALSE)
700 ? _("Cancel Edit (and abandon changes)")
701 : _("Cancel Edit"),
702 FALSE)){
703 case TRUE:
704 pico_all_done = COMP_CANCEL;
705 return(TRUE);
707 case ABORT:
708 emlwrite(_("\007Cancel Cancelled"), NULL);
709 break;
711 default:
712 mlerase();
714 return(FALSE);
719 * suspend_composer - return to pine with what's been edited so far
722 suspend_composer(int f, int n)
724 if(Pmaster && Pmaster->headents)
725 pico_all_done = COMP_SUSPEND;
726 else
727 (*term.t_beep)();
729 return(TRUE);
735 * Quit command. If an argument, always quit. Otherwise confirm if a buffer
736 * has been changed and not written out. Normally bound to "C-X C-C".
739 wquit(int f, int n)
741 register int s;
743 if(Pmaster){
744 char *result = NULL;
745 int ret;
747 /* First, make sure there are no outstanding problems */
748 if(AttachError()){
749 emlwrite(_("\007Problem with attachments! Fix errors or delete attachments."), NULL);
750 return(FALSE);
753 #ifdef SPELLER
754 if(Pmaster->always_spell_check)
755 if(spell(0, 0) == -1)
756 sleep(3); /* problem, show error */
757 #endif
759 * if we're not in header, show some of it as we verify sending...
761 display_for_send();
762 packheader();
763 Pmaster->arm_winch_cleanup++;
764 if((!(Pmaster->pine_flags & MDHDRONLY) || any_header_changes())
765 && (ret = (*Pmaster->exittest)(Pmaster->headents,
766 redraw_pico_for_callback,
767 Pmaster->allow_flowed_text,
768 &result))){
769 Pmaster->arm_winch_cleanup--;
771 if(ret == -1){
772 pico_all_done = COMP_CANCEL;
774 else{
775 if(sgarbf)
776 update();
778 lchange(WFHARD); /* set update flags... */
779 curwp->w_flag |= WFMODE; /* and modeline so we */
780 sgarbk = TRUE; /* redraw the keymenu */
781 pclear(term.t_nrow-2, term.t_nrow);
784 if(result && *result)
785 emlwrite(result, NULL);
787 else{
788 Pmaster->arm_winch_cleanup--;
789 pico_all_done = COMP_EXIT;
790 return(TRUE);
793 else{
794 if (f != FALSE /* Argument forces it. */
795 || anycb() == FALSE /* All buffers clean. */
796 /* User says it's OK. */
797 /* TRANSLATORS: buffer is the in-memory copy of a file */
798 || (s=mlyesno_utf8(_("Save modified buffer (ANSWERING \"No\" WILL DESTROY CHANGES)"), -1)) == FALSE) {
799 vttidy();
800 #if defined(USE_TERMCAP) || defined(USE_TERMINFO) || defined(VMS)
801 kbdestroy(kbesc);
802 #endif
803 exit(0);
806 if(s == TRUE){
807 if(filewrite(0,1) == TRUE)
808 wquit(1, 0);
810 else if(s == ABORT){
811 emlwrite(_("Exit cancelled"), NULL);
812 if(term.t_mrow == 0)
813 curwp->w_flag |= WFHARD; /* cause bottom 3 lines to paint */
815 return(s);
818 return(FALSE);
823 * Has any editing been done to headers?
826 any_header_changes(void)
828 struct headerentry *he;
830 for(he = Pmaster->headents; he->name != NULL; he++)
831 if(he->dirty)
832 break;
834 return(he->name && he->dirty);
838 isquotedspace(LINE *line)
840 int i, was_quote = 0;
841 for(i = 0; i < llength(line); i++){
842 if(lgetc(line, i).c == '>')
843 was_quote = 1;
844 else if(was_quote && lgetc(line, i).c == ' '){
845 if(i+1 < llength(line) && ucs4_isspace(lgetc(line,i+1).c))
846 return 1;
847 else
848 return 0;
850 else
851 return 0;
853 return 0;
857 * This function serves two purposes, 1) to strip white space when
858 * Pmaster asks that the composition have its trailing white space
859 * stripped, or 2) to prepare the text as flowed text, as Pmaster
860 * is telling us that we're working with flowed text.
862 * What flowed currently means to us is stripping all trailing white
863 * space, except for one space if the following line is a continuation
864 * of the paragraph. Also, we space-stuff all lines beginning
865 * with white-space, and leave siglines alone.
868 cleanwhitespace(void)
870 LINE *cursor_dotp = NULL, **lp = NULL;
871 int i = 0, cursor_doto = 0, is_cursor_line = 0;
872 int do_space_stuffing = 0;
874 if(Pmaster && Pmaster->allow_flowed_text && !(*Pmaster->user_says_noflow)())
875 do_space_stuffing++;
877 cursor_dotp = curwp->w_dotp;
878 cursor_doto = curwp->w_doto;
879 gotobob(FALSE, 1);
881 for(lp = &curwp->w_dotp; (*lp) != curbp->b_linep; (*lp) = lforw(*lp)){
882 if(!(llength(*lp) == 3
883 && lgetc(*lp, 0).c == '-'
884 && lgetc(*lp, 1).c == '-'
885 && lgetc(*lp, 2).c == ' ')
886 && llength(*lp)){
887 is_cursor_line = (cursor_dotp == (*lp));
888 /* trim trailing whitespace, to be added back if flowing */
889 for(i = llength(*lp); i; i--)
890 if(!ucs4_isspace(lgetc(*lp, i - 1).c))
891 break;
892 if(i != llength(*lp)){
893 int flow_line = 0;
895 if(Pmaster && !Pmaster->strip_ws_before_send
896 && lforw(*lp) != curbp->b_linep
897 && llength(lforw(*lp))
898 && !(ucs4_isspace(lgetc(lforw(*lp), 0).c)
899 || isquotedspace(lforw(*lp)))
900 && !(llength(lforw(*lp)) == 3
901 && lgetc(lforw(*lp), 0).c == '-'
902 && lgetc(lforw(*lp), 1).c == '-'
903 && lgetc(lforw(*lp), 2).c == ' '))
904 flow_line = 1;
905 if(flow_line && i && lgetc(*lp, i).c == ' '){
906 /* flowed line ending with space */
907 i++;
908 if(i != llength(*lp)){
909 curwp->w_doto = i;
910 ldelete(llength(*lp) - i, NULL);
913 else if(flow_line && i && ucs4_isspace(lgetc(*lp, i).c)){
914 /* flowed line ending with whitespace other than space*/
915 curwp->w_doto = i;
916 ldelete(llength(*lp) - i, NULL);
917 linsert(1, ' ');
919 else{
920 curwp->w_doto = i;
921 ldelete(llength(*lp) - i, NULL);
924 if(do_space_stuffing && llength(*lp) && ucs4_isspace(lgetc(*lp, 0).c)){
925 /* space-stuff only if flowed */
926 if(Pmaster)
927 Pmaster->space_stuffed = 1;
928 curwp->w_doto = 0;
929 if(is_cursor_line && cursor_doto)
930 cursor_doto++;
931 linsert(1, ' ');
933 if(is_cursor_line)
934 cursor_dotp = (*lp);
938 /* put the cursor back where we found it */
939 gotobob(FALSE, 1);
940 curwp->w_dotp = cursor_dotp;
941 curwp->w_doto = (cursor_doto < llength(curwp->w_dotp))
942 ? cursor_doto : llength(curwp->w_dotp) - 1;
944 return(0);
948 * Remove all trailing white space from the text
951 stripwhitespace(void)
953 int i;
954 LINE *cur_line = lforw(curbp->b_linep);
957 /* we gotta test for the sigdash case here */
958 if(!(cur_line->l_used == 3 &&
959 lgetc(cur_line, 0).c == '-' &&
960 lgetc(cur_line, 1).c == '-' &&
961 lgetc(cur_line, 2).c == ' ')){
962 for(i = cur_line->l_used - 1; i >= 0; i--)
963 if(ucs4_isspace(lgetc(cur_line, i).c))
964 cur_line->l_used--;
965 else
966 break;
968 }while((cur_line = lforw(cur_line)) != curbp->b_linep);
969 return 0;
973 * Abort.
974 * Beep the beeper. Kill off any keyboard macro, etc., that is in progress.
975 * Sometimes called as a routine, to do general aborting of stuff.
978 ctrlg(int f, int n)
980 emlwrite(_("Cancelled"), NULL);
981 return (ABORT);
985 /* tell the user that this command is illegal while we are in
986 * VIEW (read-only) mode
989 rdonly(void)
991 (*term.t_beep)();
992 emlwrite("Key illegal in VIEW mode", NULL);
993 return(FALSE);
999 * reset all globals to their initial values
1001 void
1002 func_init(void)
1004 extern int vtrow;
1005 extern int vtcol;
1006 extern int lbound;
1009 * re-initialize global buffer type variables ....
1011 fillcol = (term.t_ncol > 80) ? 77 : term.t_ncol - 6;
1012 sgarbf = TRUE;
1013 mpresf = FALSE;
1014 mline_open = FALSE;
1015 ComposerEditing = FALSE;
1018 * re-initialize hardware display variables ....
1020 vtrow = vtcol = lbound = 0;
1021 clearcursor();
1023 pat[0] = rpat[0] = '\0';
1024 browse_dir[0] = '\0';
1029 * pico_help - help function for standalone composer
1030 * Oops - looks like utf8title is unused!
1032 * This should be fixed to handle TAB characters.
1035 pico_help(char *utf8text[], char *utf8title, int i)
1037 register int numline = 0;
1038 char **p;
1040 p = utf8text;
1041 while(*p++ != NULL)
1042 numline++;
1044 return(wscrollw(COMPOSER_TOP_LINE, term.t_nrow-1, utf8text, numline));
1050 * zotedit() - kills the buffer and frees all lines associated with it!!!
1052 void
1053 zotedit(void)
1055 wheadp->w_linep = wheadp->w_dotp = wheadp->w_markp = wheadp->w_imarkp = NULL;
1056 bheadp->b_linep = bheadp->b_dotp = bheadp->b_markp = NULL;
1058 free((char *) wheadp); /* clean up window */
1059 wheadp = NULL;
1060 curwp = NULL;
1062 free((char *) bheadp); /* clean up buffers */
1063 bheadp = NULL;
1064 curbp = NULL;
1066 zotheader(); /* blast header lines */
1068 kdelete(); /* blast kill buffer */
1073 #ifdef MOUSE
1075 * Generic mouse handling functions
1077 MENUITEM menuitems[12]; /* key labels and functions */
1078 MENUITEM *mfunc = NULL; /* list of regional functions */
1079 mousehandler_t mtrack; /* mouse tracking handler */
1081 /* last mouse position */
1082 static unsigned long levent = 0L;
1083 static int lrow = 0, lcol = 0, doubleclick, lbutton, lflags;
1084 #ifdef DOS
1085 static clock_t lastcalled = 0;
1086 #else
1087 static time_t lastcalled = 0;
1088 #endif
1089 static mousehandler_t lastf;
1093 * register_mfunc - register the given function to get called
1094 * on mouse events in the given display region
1097 register_mfunc(mousehandler_t f, int tlr, int tlc, int brr, int brc)
1099 MENUITEM **mp;
1101 if(!mouseexist())
1102 return(FALSE);
1104 for(mp = &mfunc; *mp; mp = &(*mp)->next)
1107 *mp = (MENUITEM *)malloc(sizeof(MENUITEM));
1108 memset(*mp, 0, sizeof(MENUITEM));
1110 (*mp)->action = f;
1111 (*mp)->tl.r = tlr;
1112 (*mp)->br.r = brr;
1113 (*mp)->tl.c = tlc;
1114 (*mp)->br.c = brc;
1115 (*mp)->lbl.c = (*mp)->lbl.r = 0;
1116 (*mp)->label = "";
1117 return(TRUE);
1122 * clear_mfunc - clear any previously set mouse function
1124 void
1125 clear_mfunc(mousehandler_t f)
1127 MENUITEM *mp, *tp;
1129 if((mp = mfunc) != NULL){
1130 if(mp->action == f)
1131 mfunc = mp->next;
1132 else
1133 for(tp = mp; tp->next; tp = tp->next)
1134 if(tp->next->action == f){
1135 mp = tp->next;
1136 tp->next = tp->next->next;
1137 break;
1140 if(mp){
1141 mp->action = NULL;
1142 free(mp);
1149 #ifdef EX_MOUSE
1151 void
1152 clear_mtrack(void)
1154 mtrack = NULL;
1155 mswin_allowmousetrack (FALSE);
1159 void
1160 register_mtrack(mousehandler_t f)
1162 if (f) {
1163 mtrack = f;
1164 mswin_allowmousetrack (TRUE);
1166 else
1167 clear_mtrack ();
1171 static void
1172 move_dot_to(int row, int col)
1174 LINE *lp;
1175 int i;
1177 lp = curwp->w_linep;
1178 i = row - ((Pmaster) ? ComposerTopLine : 2);
1179 while(i-- && lp != curbp->b_linep) /* count from top */
1180 lp = lforw(lp);
1181 curgoal = col;
1182 curwp->w_dotp = lp; /* to new dot. */
1183 curwp->w_doto = getgoal(lp);
1184 curwp->w_flag |= WFMOVE;
1189 * mouse_in_pico
1191 * When the mouse goes down in the body we set the mark and start
1192 * tracking.
1194 * As the mouse moves we update the dot and redraw the screen.
1196 * If the mouse moves above or below the pico body region of the
1197 * screen we scroll the text and update the dot position.
1199 * When the mouse comes up we clean up. If the mouse did not
1200 * move, then we clear the mark and turn off the selection.
1202 * Most of the mouse processing is handled here. The exception is
1203 * mouse down in the header. Can't call HeaderEditor() from here so
1204 * we send up the KEY_MOUSE character, which gets dispatched to
1205 * mousepress(), which _can_ call HeaderEditor().
1207 unsigned long
1208 mouse_in_pico(unsigned long mevent, int row, int col, int button, int flags)
1210 unsigned long rv = 0; /* Our return value. */
1211 int trow, tcol; /* translated row and col. */
1213 static int lheader = FALSE; /* Mouse down was in header. */
1217 * What's up.
1219 switch (mevent) {
1220 case M_EVENT_DOWN:
1221 if(button != M_BUTTON_LEFT)
1222 break;
1224 /* Ignore mouse down if not in pico body region. */
1225 if (row < 2 || row > term.t_nrow - (term.t_mrow+1)) {
1226 clear_mtrack ();
1227 break;
1230 /* Detect double clicks. Not that we do anything with em, just
1231 * detect them. */
1232 #ifdef DOS
1233 #ifdef CLOCKS_PER_SEC
1234 doubleclick = (lrow == row && lcol == col
1235 && clock() < (lastcalled + CLOCKS_PER_SEC/2));
1236 #else
1237 #ifdef CLK_TCK
1238 doubleclick = (lrow == row && lcol == col
1239 && clock() < (lastcalled + CLK_TCK/2));
1240 #else
1241 doubleclick = FALSE;
1242 #endif
1243 #endif
1244 lastcalled = clock();
1245 #else
1246 doubleclick = (lrow == row && lcol == col
1247 && time(0) < (lastcalled + 2));
1248 lastcalled = time(0);
1249 #endif
1250 lheader = FALSE; /* Rember mouse down position. */
1251 levent = mevent;
1252 lrow = row;
1253 lcol = col;
1254 lbutton = button;
1255 lflags = flags;
1257 /* Mouse down in body? */
1258 if (row < (Pmaster ? ComposerTopLine : 2)) {
1259 /* Mouse down in message header -> no tracking, just remember
1260 * where */
1261 lheader = TRUE;
1263 else {
1264 /* Mouse down in message.
1265 * If no shift key and an existing mark -> clear the mark.
1266 * If shift key and no existing mark -> set mark before moving */
1267 if (!(flags & M_KEY_SHIFT) && curwp->w_markp)
1268 setmark (0,1); /* this clears the mark. */
1269 else if (flags & M_KEY_SHIFT && !curwp->w_markp)
1270 setmark (0,1); /* while this sets the mark. */
1272 /* Reposition dot to mouse down. */
1273 move_dot_to (row, col);
1275 /* Set the mark to dot if no existing mark. */
1276 if (curwp->w_markp == NULL)
1277 setmark (0,1);
1279 /* Track mouse movement. */
1280 register_mtrack (mouse_in_pico);
1281 update ();
1282 lheader = FALSE; /* Just to be sure. */
1284 break;
1287 case M_EVENT_TRACK:
1288 /* Mouse tracking. */
1289 if (lheader) /* Ignore mouse movement in header. */
1290 break;
1292 /* If above or below body, scroll body and adjust the row and col. */
1293 if (row < (Pmaster ? ComposerTopLine : 2)) {
1294 /* Scroll text down screen and move dot to top left corner. */
1295 scrollupline (0,1);
1296 trow = (Pmaster) ? ComposerTopLine : 2;
1297 tcol = 0;
1299 else if (row > term.t_nrow - (term.t_mrow + 1)) {
1300 /* Scroll text up screen and move dot to bottom right corner. */
1301 scrolldownline (0,1);
1302 trow = term.t_nrow - (term.t_mrow + 1);
1303 tcol = term.t_ncol;
1305 else {
1306 trow = row;
1307 tcol = col;
1310 /* Move dot to target column. */
1311 move_dot_to (trow, tcol);
1313 /* Update screen. */
1314 update ();
1315 break;
1318 case M_EVENT_UP:
1319 if(button == M_BUTTON_RIGHT){
1320 #ifdef _WINDOWS
1321 pico_popup();
1322 #endif
1323 break;
1325 else if(button != M_BUTTON_LEFT)
1326 break;
1328 if (lheader) {
1329 lheader = FALSE;
1330 /* Last down in header. */
1331 if (row == lrow && col == lcol) {
1332 /* Mouse up and down in same place in header. Means the
1333 * user want to edit the header. Return KEY_MOUSE which
1334 * will cause mousepress to be called, which will
1335 * call HeaderEditor. Can't call HeaderEditor from here
1336 * because that would mess up layering. */
1337 if (curwp->w_marko)
1338 setmark (0,1);
1339 rv = (unsigned long) KEY_MOUSE;
1342 else {
1343 /* If up at same place, clear mark */
1344 if (curwp->w_markp == curwp->w_dotp &&
1345 curwp->w_marko == curwp->w_doto) {
1346 setmark (0,1);
1347 curwp->w_flag |= WFMOVE;
1349 clear_mtrack ();
1350 update ();
1352 break;
1355 return(rv);
1357 #endif
1362 * mouse_in_content - general mechanism used to pass recognized mouse
1363 * events in predefined region back thru the usual
1364 * keyboard input stream. The actual return value
1365 * passed back from this function is set dynamically
1366 * via the "down" argument which is read when both the
1367 * "row" and "col" arguments are negative.
1369 unsigned long
1370 mouse_in_content(unsigned long mevent, int row, int col, int button, int flags)
1372 unsigned long rv = 0;
1373 static unsigned long mouse_val = KEY_MOUSE;
1375 if(row == -1 && col == -1){
1376 mouse_val = mevent; /* setting return value */
1378 else {
1379 /* A real event. */
1380 levent = mevent;
1381 switch (mevent) {
1382 case M_EVENT_DOWN:
1383 /* Mouse down does not mean anything, just keep track of
1384 * where it went down and if this is a double click. */
1385 #ifdef DOS
1386 #ifdef CLOCKS_PER_SEC
1387 doubleclick = (lrow == row && lcol == col
1388 && clock() < (lastcalled + CLOCKS_PER_SEC/2));
1389 #else
1390 #ifdef CLK_TCK
1391 doubleclick = (lrow == row && lcol == col
1392 && clock() < (lastcalled + CLK_TCK/2));
1393 #else
1394 doubleclick = FALSE;
1395 #endif
1396 #endif
1397 lastcalled = clock();
1398 #else
1399 doubleclick = (lrow == row && lcol == col
1400 && time(0) < (lastcalled + 2));
1401 lastcalled = time(0);
1402 #endif
1403 lrow = row;
1404 lcol = col;
1405 lbutton = button;
1406 lflags = flags;
1407 break;
1409 case M_EVENT_UP:
1410 /* Mouse up. If in the same position as it went down
1411 * then we return the value set above, which goes into
1412 * the character input stream, which gets processed as
1413 * a mouse event by some upper layer, which calls to
1414 * mouse_get_last(). */
1415 if (lrow == row && lcol == col) {
1416 rv = mouse_val;
1418 break;
1420 case M_EVENT_TRACK:
1421 break;
1425 return(rv);
1430 * mouse_get_last - Get last mouse event.
1433 void
1434 mouse_get_last(mousehandler_t *f, MOUSEPRESS *mp)
1436 if (f != NULL)
1437 *f = lastf;
1438 if (mp != NULL) {
1439 mp->mevent = levent;
1440 mp->row = lrow;
1441 mp->col = lcol;
1442 mp->doubleclick = doubleclick;
1443 mp->button = lbutton;
1444 mp->flags = lflags;
1451 * register_key - register the given keystroke to accept mouse events
1453 void
1454 register_key(int i, unsigned rval, char *label, void (*label_printer)(),
1455 int row, int col, int len, COLOR_PAIR *kn, COLOR_PAIR *kl)
1457 if(i > 11)
1458 return;
1460 menuitems[i].val = rval;
1461 menuitems[i].tl.r = menuitems[i].br.r = row;
1462 menuitems[i].tl.c = col;
1463 menuitems[i].br.c = col + len;
1464 menuitems[i].lbl.r = menuitems[i].tl.r;
1465 menuitems[i].lbl.c = menuitems[i].tl.c;
1466 menuitems[i].label_hiliter = label_printer;
1467 if(menuitems[i].label){
1468 free(menuitems[i].label);
1469 menuitems[i].label = NULL;
1471 if(menuitems[i].kncp)
1472 free_color_pair(&menuitems[i].kncp);
1473 if(menuitems[i].klcp)
1474 free_color_pair(&menuitems[i].klcp);
1475 if(kn)
1476 menuitems[i].kncp = new_color_pair(kn->fg, kn->bg);
1477 else
1478 menuitems[i].kncp = NULL;
1479 if(kl)
1480 menuitems[i].klcp = new_color_pair(kl->fg, kl->bg);
1481 else
1482 menuitems[i].klcp = NULL;
1484 if(label){
1485 size_t len;
1487 len = strlen(label);
1488 if((menuitems[i].label = (char *)malloc((len+1)*sizeof(char))) != NULL){
1489 strncpy(menuitems[i].label, label, len);
1490 menuitems[i].label[len] = '\0';
1497 mouse_on_key(int row, int col)
1499 int i;
1501 for(i = 0; i < 12; i++)
1502 if(M_ACTIVE(row, col, &menuitems[i]))
1503 return(TRUE);
1505 return(FALSE);
1507 #endif /* MOUSE */
1511 * Below are functions for use outside pico to manipulate text
1512 * in a pico's native format (circular linked list of lines).
1514 * The idea is to streamline pico use by making it fairly easy
1515 * for outside programs to prepare text intended for pico's use.
1516 * The simple char * alternative is messy as it requires two copies
1517 * of the same text, and isn't very economic in limited memory
1518 * situations (THANKS BELLEVUE-BILLY.).
1520 typedef struct picotext {
1521 LINE *linep;
1522 LINE *dotp;
1523 int doto;
1524 short crinread;
1525 } PICOTEXT;
1527 #define PT(X) ((PICOTEXT *)(X))
1530 * pico_get - return window struct pointer used as a handle
1531 * to the other pico_xxx routines.
1533 void *
1534 pico_get(void)
1536 PICOTEXT *wp = NULL;
1537 LINE *lp = NULL;
1539 if((wp = (PICOTEXT *)malloc(sizeof(PICOTEXT))) != NULL){
1540 wp->crinread = 0;
1541 if((lp = lalloc(0)) == NULL){
1542 free(wp);
1543 return(NULL);
1546 wp->dotp = wp->linep = lp->l_fp = lp->l_bp = lp;
1547 wp->doto = 0;
1549 else
1550 emlwrite("Can't allocate space for text", NULL);
1552 return((void *)wp);
1556 * pico_give - free resources and give up picotext struct
1558 void
1559 pico_give(void *w)
1561 register LINE *lp;
1562 register LINE *fp;
1564 fp = lforw(PT(w)->linep);
1565 while((lp = fp) != PT(w)->linep){
1566 fp = lforw(lp);
1567 free(lp);
1569 free(PT(w)->linep);
1570 free((PICOTEXT *)w);
1574 * pico_readc - return char at current point. Up to calling routines
1575 * to keep cumulative count of chars.
1576 * The characters in PT are UCS-4 characters. The caller
1577 * of pico_readc is expecting UTF-8 chars. We convert
1578 * each UCS-4 character to a string of UTF-8 characters
1579 * and return them one at a time.
1582 pico_readc(void *w, unsigned char *c, int flags)
1584 int rv = 0;
1585 UCS ucs;
1586 static unsigned char obuf[6];
1587 static unsigned char *obufpend = obuf;
1588 static unsigned char *obufpnext = obuf;
1590 if(!(flags & PICOREADC_NOUCS)){
1591 if(obufpend > obuf){
1592 *c = *obufpnext++;
1593 rv++;
1594 if(obufpnext >= obufpend){
1595 obufpend = obuf;
1596 obufpnext = obuf;
1599 return(rv);
1603 if(PT(w)->crinread){
1604 *c = '\012'; /* return LF */
1605 PT(w)->crinread = 0;
1606 rv++;
1608 else if(PT(w)->doto < llength(PT(w)->dotp)){ /* normal char to return */
1609 if(flags & PICOREADC_NOUCS){
1610 *c = (unsigned char) lgetc(PT(w)->dotp, (PT(w)->doto)++).c;
1611 rv++;
1613 else{
1614 rv++;
1615 ucs = lgetc(PT(w)->dotp, (PT(w)->doto)++).c;
1616 obufpend = utf8_put(obuf, (unsigned long) ucs);
1617 obufpnext = obuf;
1618 if(obufpend > obuf){
1619 *c = *obufpnext++;
1620 if(obufpnext >= obufpend){
1621 obufpend = obuf;
1622 obufpnext = obuf;
1625 else
1626 *c = '?';
1629 else if(PT(w)->dotp != PT(w)->linep){ /* return line break */
1630 PT(w)->dotp = lforw(PT(w)->dotp);
1631 PT(w)->doto = 0;
1632 #if defined(DOS) || defined(OS2)
1633 *c = '\015';
1634 PT(w)->crinread++;
1635 #else
1636 *c = '\012'; /* return local eol! */
1637 #endif
1638 rv++;
1639 } /* else no chars to return */
1641 return(rv);
1646 * pico_writec - write a char into picotext and advance pointers.
1647 * Up to calling routines to keep track of total chars
1648 * written.
1649 * Incoming text (c) is UTF-8 and written chars are UCS-4.
1650 * We need to collect up multiple chars to make a single
1651 * UCS-4 char, so there needs to be some state between calls.
1654 pico_writec(void *w, int c, int flags)
1656 int rv = 0;
1658 if(c == '\r') /* ignore CR's */
1659 rv++; /* so fake it */
1660 else if(c == '\n'){ /* insert newlines on LF */
1662 * OK, if there are characters on the current line or
1663 * dotp is pointing to the delimiter line, insert a newline
1664 * No here's the tricky bit; preserve the implicit EOF newline.
1666 if(lforw(PT(w)->dotp) == PT(w)->linep && PT(w)->dotp != PT(w)->linep){
1667 PT(w)->dotp = PT(w)->linep;
1668 PT(w)->doto = 0;
1670 else{
1671 register LINE *lp;
1673 if((lp = lalloc(0)) == NULL){
1674 emlwrite("Can't allocate space for more characters",NULL);
1675 return(0);
1678 if(PT(w)->dotp == PT(w)->linep){
1679 lforw(lp) = PT(w)->linep;
1680 lback(lp) = lback(PT(w)->linep);
1681 lforw(lback(lp)) = lback(PT(w)->linep) = lp;
1683 else{
1684 lforw(lp) = lforw(PT(w)->dotp);
1685 lback(lp) = PT(w)->dotp;
1686 lback(lforw(lp)) = lforw(PT(w)->dotp) = lp;
1687 PT(w)->dotp = lp;
1688 PT(w)->doto = 0;
1692 rv++;
1694 else{
1695 if(flags & PICOREADC_NOUCS){
1697 * With this flag we're reverting to the old behavior where no
1698 * UTF-8 to UCS-4 translation takes place. We assume nothing
1699 * about the incoming byte stream. We just write a byte at a
1700 * time. So even though it's being stored in PT each
1701 * item is really limited to a single octet value.
1703 rv = geninsert(&PT(w)->dotp, &PT(w)->doto, PT(w)->linep, c, 0, 1, NULL);
1705 else{
1706 static unsigned char cbuf[6];
1707 static unsigned char *cbufp = cbuf;
1708 UCS obuf[MAX(MB_LEN_MAX,32)];
1709 int i, outchars = 0;
1711 if(cbufp < cbuf+sizeof(cbuf)){
1712 unsigned char *inputp;
1713 unsigned long remaining_octets;
1714 UCS ucs;
1716 *cbufp++ = (unsigned char) c;
1717 inputp = cbuf;
1718 remaining_octets = (cbufp - cbuf) * sizeof(unsigned char);
1719 ucs = (UCS) utf8_get(&inputp, &remaining_octets);
1721 switch(ucs){
1722 case U8G_ENDSTRG: /* incomplete character, wait */
1723 case U8G_ENDSTRI: /* incomplete character, wait */
1724 break;
1726 default:
1727 if(ucs & U8G_ERROR || ucs == UBOGON){
1729 * None of these cases is supposed to happen. If it
1730 * does happen then the input stream isn't UTF-8
1731 * so something is wrong. Treat each character in the
1732 * input buffer as a separate error character and
1733 * print a '?' for each.
1735 for(inputp = cbuf; inputp < cbufp; inputp++)
1736 obuf[outchars++] = '?';
1738 cbufp = cbuf;
1740 else{
1741 /* got a character */
1742 if(ucs >= 0x80 && wcellwidth(ucs) < 0){
1744 * This happens when we have a UTF-8 character that
1745 * we aren't able to print in our locale. For example,
1746 * if the locale is setup with the terminal
1747 * expecting ISO-8859-1 characters then there are
1748 * lots of UTF-8 characters that can't be printed.
1749 * Print a '?' instead.
1750 This may be the wrong thing to do. What happens if user
1751 is just forwarding and doesn't edit. We are going to lose
1752 the original value, aren't we? Maybe we do this only
1753 when printing to the screen instead.
1755 obuf[outchars++] = '?';
1757 else{
1759 * A regular ucs character
1761 obuf[outchars++] = ucs;
1764 /* update the input buffer */
1765 if(inputp >= cbufp) /* this should be the case */
1766 cbufp = cbuf;
1767 else{ /* extra chars for some reason? */
1768 unsigned char *q, *newcbufp;
1770 newcbufp = (cbufp - inputp) + cbuf;
1771 q = cbuf;
1772 while(inputp < cbufp)
1773 *q++ = *inputp++;
1775 cbufp = newcbufp;
1779 break;
1782 else{ /* error */
1783 obuf[0] = '?';
1784 outchars = 1;
1785 cbufp = cbuf; /* start over */
1789 * Unless we have trouble translating outchars will be
1790 * either 1 or 0. It is the UCS-4 character that we converted
1791 * the input to. It's an array just so it can hold ? when
1792 * there is trouble.
1794 rv = 1;
1795 for(i = 0; rv && i < outchars; i++)
1796 if(!geninsert(&PT(w)->dotp, &PT(w)->doto, PT(w)->linep, obuf[i], 0, 1, NULL))
1797 rv = 0;
1802 * Warning, this is no longer number written, because when we have a
1803 * multibyte UTF-8 character we won't write anything until we get all
1804 * the bytes.
1806 return((rv) ? 1 : 0); /* return number written */
1811 * pico_puts - just write the given string into the text
1814 pico_puts(void *w, char *s, int flags)
1816 int rv = 0;
1818 if(*s != '\0'){
1819 rv = 1;
1820 while(rv && *s != '\0')
1821 if(!pico_writec(w, (int)*s++, flags))
1822 rv = 0;
1825 return((rv) ? 1 : 0);
1830 * pico_seek - position dotp and dot at requested location
1833 pico_seek(void *w, long offset, int orig)
1835 register LINE *lp;
1837 PT(w)->crinread = 0;
1838 switch(orig){
1839 case 0 : /* SEEK_SET */
1840 PT(w)->dotp = lforw(PT(w)->linep);
1841 PT(w)->doto = 0;
1842 case 1 : /* SEEK_CUR */
1843 lp = PT(w)->dotp;
1844 while(lp != PT(w)->linep){
1845 if(offset <= llength(lp)){
1846 PT(w)->doto = (int)offset;
1847 PT(w)->dotp = lp;
1848 break;
1851 offset -= ((long)llength(lp)
1852 #if defined(DOS) || defined(OS2)
1853 + 2L);
1854 #else
1855 + 1L);
1856 #endif
1857 lp = lforw(lp);
1859 break;
1861 case 2 : /* SEEK_END */
1862 PT(w)->dotp = lback(PT(w)->linep);
1863 PT(w)->doto = llength(PT(w)->dotp);
1864 break;
1865 default :
1866 return(-1);
1869 return(0);
1874 * breplace - replace the current window's text with the given
1875 * LINEs
1877 void
1878 breplace(void *w)
1880 register LINE *lp;
1881 register LINE *fp;
1883 fp = lforw(curbp->b_linep);
1884 while((lp = fp) != curbp->b_linep){ /* blast old lines */
1885 fp = lforw(lp);
1886 free(lp);
1888 free(curbp->b_linep);
1890 curbp->b_linep = PT(w)->linep; /* arrange pointers */
1893 * Undo space-stuffing that was done when we were preparing to send.
1894 * Some error happened so we're back to the composer.
1896 if(Pmaster && Pmaster->space_stuffed){
1897 Pmaster->space_stuffed = 0;
1898 for(lp = lforw(curbp->b_linep); lp != curbp->b_linep; lp = lforw(lp)){
1899 if(llength(lp) && ucs4_isspace(lgetc(lp, 0).c)){
1900 curwp->w_dotp = lp;
1901 curwp->w_doto = 0;
1902 forwdel(FALSE,1);
1907 curwp->w_linep = lforw(curbp->b_linep);
1908 curwp->w_dotp = lforw(curbp->b_linep);
1909 curwp->w_doto = 0;
1910 curwp->w_markp = curwp->w_imarkp = NULL;
1911 curwp->w_marko = curwp->w_imarko = 0;
1913 curbp->b_dotp = curwp->w_dotp;
1914 curbp->b_doto = curbp->b_marko = 0;
1915 curbp->b_markp = NULL;
1916 curbp->b_linecnt = -1;
1918 curwp->w_flag |= WFHARD;
1922 #ifdef _WINDOWS
1927 composer_file_drop(int x, int y, char *filename)
1929 int attached = 0;
1930 EML eml;
1932 if((ComposerTopLine > 2 && x <= ComposerTopLine)
1933 || !LikelyASCII(filename)){
1934 AppendAttachment(filename, NULL, NULL);
1935 attached++;
1937 else{
1938 setimark(FALSE, 1);
1939 ifile(filename);
1940 swapimark(FALSE, 1);
1943 if(ComposerEditing){ /* update display */
1944 PaintBody(0);
1946 else{
1947 pico_refresh(0, 1);
1948 update();
1951 eml.s = filename;
1952 if(attached)
1953 emlwrite(_("Attached dropped file \"%s\""), &eml);
1954 else
1955 emlwrite(_("Inserted dropped file \"%s\""), &eml);
1957 if(ComposerEditing){ /* restore cursor */
1958 HeaderPaintCursor();
1960 else{
1961 curwp->w_flag |= WFHARD;
1962 update();
1965 return(1);
1970 pico_cursor(int col, long row)
1972 return((row > 1 && row <= term.t_nrow - (term.t_mrow + 1))
1973 ? MSWIN_CURSOR_IBEAM
1974 : MSWIN_CURSOR_ARROW);
1976 #endif /* _WINDOWS */