* clear out some warnings by gcc 9.3.1.
[alpine.git] / pico / pico.c
blob020f7c0591c37bd2946fd52872da40bb4b4a0219
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-2020 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 emlwwrite(_("Cancel 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 emlwwrite(_("Problem 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 #ifdef _WINDOWS
809 if(dictionary != NULL){
810 int i;
811 for(i = 0; dictionary[i] != NULL; i++)
812 fs_give((void **)&dictionary[i]);
813 fs_give((void **)dictionary);
815 #endif /* _WINDOWS */
816 wquit(1, 0);
819 else if(s == ABORT){
820 emlwrite(_("Exit cancelled"), NULL);
821 if(term.t_mrow == 0)
822 curwp->w_flag |= WFHARD; /* cause bottom 3 lines to paint */
824 return(s);
827 return(FALSE);
832 * Has any editing been done to headers?
835 any_header_changes(void)
837 struct headerentry *he;
839 for(he = Pmaster->headents; he->name != NULL; he++)
840 if(he->dirty)
841 break;
843 return(he->name && he->dirty);
847 isquotedspace(LINE *line)
849 int i, was_quote = 0;
850 for(i = 0; i < llength(line); i++){
851 if(lgetc(line, i).c == '>')
852 was_quote = 1;
853 else if(was_quote && lgetc(line, i).c == ' '){
854 if(i+1 < llength(line) && ucs4_isspace(lgetc(line,i+1).c))
855 return 1;
856 else
857 return 0;
859 else
860 return 0;
862 return 0;
866 * This function serves two purposes, 1) to strip white space when
867 * Pmaster asks that the composition have its trailing white space
868 * stripped, or 2) to prepare the text as flowed text, as Pmaster
869 * is telling us that we're working with flowed text.
871 * What flowed currently means to us is stripping all trailing white
872 * space, except for one space if the following line is a continuation
873 * of the paragraph. Also, we space-stuff all lines beginning
874 * with white-space, and leave siglines alone.
877 cleanwhitespace(void)
879 LINE *cursor_dotp = NULL, **lp = NULL;
880 int i = 0, cursor_doto = 0, is_cursor_line = 0;
881 int do_space_stuffing = 0;
883 if(Pmaster && Pmaster->allow_flowed_text && !(*Pmaster->user_says_noflow)())
884 do_space_stuffing++;
886 cursor_dotp = curwp->w_dotp;
887 cursor_doto = curwp->w_doto;
888 gotobob(FALSE, 1);
890 for(lp = &curwp->w_dotp; (*lp) != curbp->b_linep; (*lp) = lforw(*lp)){
891 if(!(llength(*lp) == 3
892 && lgetc(*lp, 0).c == '-'
893 && lgetc(*lp, 1).c == '-'
894 && lgetc(*lp, 2).c == ' ')
895 && llength(*lp)){
896 is_cursor_line = (cursor_dotp == (*lp));
897 /* trim trailing whitespace, to be added back if flowing */
898 for(i = llength(*lp); i; i--)
899 if(!ucs4_isspace(lgetc(*lp, i - 1).c))
900 break;
901 if(i != llength(*lp)){
902 int flow_line = 0;
904 if(Pmaster && !Pmaster->strip_ws_before_send
905 && lforw(*lp) != curbp->b_linep
906 && llength(lforw(*lp))
907 && !(ucs4_isspace(lgetc(lforw(*lp), 0).c)
908 || isquotedspace(lforw(*lp)))
909 && !(llength(lforw(*lp)) == 3
910 && lgetc(lforw(*lp), 0).c == '-'
911 && lgetc(lforw(*lp), 1).c == '-'
912 && lgetc(lforw(*lp), 2).c == ' '))
913 flow_line = 1;
914 if(flow_line && i && lgetc(*lp, i).c == ' '){
915 /* flowed line ending with space */
916 i++;
917 if(i != llength(*lp)){
918 curwp->w_doto = i;
919 ldelete(llength(*lp) - i, NULL);
922 else if(flow_line && i && ucs4_isspace(lgetc(*lp, i).c)){
923 /* flowed line ending with whitespace other than space*/
924 curwp->w_doto = i;
925 ldelete(llength(*lp) - i, NULL);
926 linsert(1, ' ');
928 else{
929 curwp->w_doto = i;
930 ldelete(llength(*lp) - i, NULL);
933 if(do_space_stuffing && llength(*lp) && ucs4_isspace(lgetc(*lp, 0).c)){
934 /* space-stuff only if flowed */
935 if(Pmaster)
936 Pmaster->space_stuffed = 1;
937 curwp->w_doto = 0;
938 if(is_cursor_line && cursor_doto)
939 cursor_doto++;
940 linsert(1, ' ');
942 if(is_cursor_line)
943 cursor_dotp = (*lp);
947 /* put the cursor back where we found it */
948 gotobob(FALSE, 1);
949 curwp->w_dotp = cursor_dotp;
950 curwp->w_doto = (cursor_doto < llength(curwp->w_dotp))
951 ? cursor_doto : llength(curwp->w_dotp) - 1;
953 return(0);
957 * Remove all trailing white space from the text
960 stripwhitespace(void)
962 int i;
963 LINE *cur_line = lforw(curbp->b_linep);
966 /* we gotta test for the sigdash case here */
967 if(!(cur_line->l_used == 3 &&
968 lgetc(cur_line, 0).c == '-' &&
969 lgetc(cur_line, 1).c == '-' &&
970 lgetc(cur_line, 2).c == ' ')){
971 for(i = cur_line->l_used - 1; i >= 0; i--)
972 if(ucs4_isspace(lgetc(cur_line, i).c))
973 cur_line->l_used--;
974 else
975 break;
977 }while((cur_line = lforw(cur_line)) != curbp->b_linep);
978 return 0;
982 * Abort.
983 * Beep the beeper. Kill off any keyboard macro, etc., that is in progress.
984 * Sometimes called as a routine, to do general aborting of stuff.
987 ctrlg(int f, int n)
989 emlwrite(_("Cancelled"), NULL);
990 return (ABORT);
994 /* tell the user that this command is illegal while we are in
995 * VIEW (read-only) mode
998 rdonly(void)
1000 (*term.t_beep)();
1001 emlwrite("Key illegal in VIEW mode", NULL);
1002 return(FALSE);
1008 * reset all globals to their initial values
1010 void
1011 func_init(void)
1013 extern int vtrow;
1014 extern int vtcol;
1015 extern int lbound;
1018 * re-initialize global buffer type variables ....
1020 fillcol = (term.t_ncol > 80) ? 77 : term.t_ncol - 6;
1021 sgarbf = TRUE;
1022 mpresf = FALSE;
1023 mline_open = FALSE;
1024 ComposerEditing = FALSE;
1027 * re-initialize hardware display variables ....
1029 vtrow = vtcol = lbound = 0;
1030 clearcursor();
1032 pat[0] = rpat[0] = '\0';
1033 browse_dir[0] = '\0';
1038 * pico_help - help function for standalone composer
1039 * Oops - looks like utf8title is unused!
1041 * This should be fixed to handle TAB characters.
1044 pico_help(char *utf8text[], char *utf8title, int i)
1046 register int numline = 0;
1047 char **p;
1049 p = utf8text;
1050 while(*p++ != NULL)
1051 numline++;
1053 return(wscrollw(COMPOSER_TOP_LINE, term.t_nrow-1, utf8text, numline));
1059 * zotedit() - kills the buffer and frees all lines associated with it!!!
1061 void
1062 zotedit(void)
1064 wheadp->w_linep = wheadp->w_dotp = wheadp->w_markp = wheadp->w_imarkp = NULL;
1065 bheadp->b_linep = bheadp->b_dotp = bheadp->b_markp = NULL;
1067 free((char *) wheadp); /* clean up window */
1068 wheadp = NULL;
1069 curwp = NULL;
1071 free((char *) bheadp); /* clean up buffers */
1072 bheadp = NULL;
1073 curbp = NULL;
1075 zotheader(); /* blast header lines */
1077 kdelete(); /* blast kill buffer */
1082 #ifdef MOUSE
1084 * Generic mouse handling functions
1086 MENUITEM menuitems[12]; /* key labels and functions */
1087 MENUITEM *mfunc = NULL; /* list of regional functions */
1088 mousehandler_t mtrack; /* mouse tracking handler */
1090 /* last mouse position */
1091 static unsigned long levent = 0L;
1092 static int lrow = 0, lcol = 0, doubleclick, lbutton, lflags;
1093 #ifdef DOS
1094 static clock_t lastcalled = 0;
1095 #else
1096 static time_t lastcalled = 0;
1097 #endif
1098 static mousehandler_t lastf;
1102 * register_mfunc - register the given function to get called
1103 * on mouse events in the given display region
1106 register_mfunc(mousehandler_t f, int tlr, int tlc, int brr, int brc)
1108 MENUITEM **mp;
1110 if(!mouseexist())
1111 return(FALSE);
1113 for(mp = &mfunc; *mp; mp = &(*mp)->next)
1116 *mp = (MENUITEM *)malloc(sizeof(MENUITEM));
1117 memset(*mp, 0, sizeof(MENUITEM));
1119 (*mp)->action = f;
1120 (*mp)->tl.r = tlr;
1121 (*mp)->br.r = brr;
1122 (*mp)->tl.c = tlc;
1123 (*mp)->br.c = brc;
1124 (*mp)->lbl.c = (*mp)->lbl.r = 0;
1125 (*mp)->label = "";
1126 return(TRUE);
1131 * clear_mfunc - clear any previously set mouse function
1133 void
1134 clear_mfunc(mousehandler_t f)
1136 MENUITEM *mp, *tp;
1138 if((mp = mfunc) != NULL){
1139 if(mp->action == f)
1140 mfunc = mp->next;
1141 else
1142 for(tp = mp; tp->next; tp = tp->next)
1143 if(tp->next->action == f){
1144 mp = tp->next;
1145 tp->next = tp->next->next;
1146 break;
1149 if(mp){
1150 mp->action = NULL;
1151 free(mp);
1158 #ifdef EX_MOUSE
1160 void
1161 clear_mtrack(void)
1163 mtrack = NULL;
1164 mswin_allowmousetrack (FALSE);
1168 void
1169 register_mtrack(mousehandler_t f)
1171 if (f) {
1172 mtrack = f;
1173 mswin_allowmousetrack (TRUE);
1175 else
1176 clear_mtrack ();
1180 static void
1181 move_dot_to(int row, int col)
1183 LINE *lp;
1184 int i;
1186 lp = curwp->w_linep;
1187 i = row - ((Pmaster) ? ComposerTopLine : 2);
1188 while(i-- && lp != curbp->b_linep) /* count from top */
1189 lp = lforw(lp);
1190 curgoal = col;
1191 curwp->w_dotp = lp; /* to new dot. */
1192 curwp->w_doto = getgoal(lp);
1193 curwp->w_flag |= WFMOVE;
1198 * mouse_in_pico
1200 * When the mouse goes down in the body we set the mark and start
1201 * tracking.
1203 * As the mouse moves we update the dot and redraw the screen.
1205 * If the mouse moves above or below the pico body region of the
1206 * screen we scroll the text and update the dot position.
1208 * When the mouse comes up we clean up. If the mouse did not
1209 * move, then we clear the mark and turn off the selection.
1211 * Most of the mouse processing is handled here. The exception is
1212 * mouse down in the header. Can't call HeaderEditor() from here so
1213 * we send up the KEY_MOUSE character, which gets dispatched to
1214 * mousepress(), which _can_ call HeaderEditor().
1216 unsigned long
1217 mouse_in_pico(unsigned long mevent, int row, int col, int button, int flags)
1219 unsigned long rv = 0; /* Our return value. */
1220 int trow, tcol; /* translated row and col. */
1222 static int lheader = FALSE; /* Mouse down was in header. */
1226 * What's up.
1228 switch (mevent) {
1229 case M_EVENT_DOWN:
1230 if(button != M_BUTTON_LEFT)
1231 break;
1233 /* Ignore mouse down if not in pico body region. */
1234 if (row < 2 || row > term.t_nrow - (term.t_mrow+1)) {
1235 clear_mtrack ();
1236 break;
1239 /* Detect double clicks. Not that we do anything with em, just
1240 * detect them. */
1241 #ifdef DOS
1242 #ifdef CLOCKS_PER_SEC
1243 doubleclick = (lrow == row && lcol == col
1244 && clock() < (lastcalled + CLOCKS_PER_SEC/2));
1245 #else
1246 #ifdef CLK_TCK
1247 doubleclick = (lrow == row && lcol == col
1248 && clock() < (lastcalled + CLK_TCK/2));
1249 #else
1250 doubleclick = FALSE;
1251 #endif
1252 #endif
1253 lastcalled = clock();
1254 #else
1255 doubleclick = (lrow == row && lcol == col
1256 && time(0) < (lastcalled + 2));
1257 lastcalled = time(0);
1258 #endif
1259 lheader = FALSE; /* Remember mouse down position. */
1260 levent = mevent;
1261 lrow = row;
1262 lcol = col;
1263 lbutton = button;
1264 lflags = flags;
1266 /* Mouse down in body? */
1267 if (row < (Pmaster ? ComposerTopLine : 2)) {
1268 /* Mouse down in message header -> no tracking, just remember
1269 * where */
1270 lheader = TRUE;
1272 else {
1273 /* Mouse down in message.
1274 * If no shift key and an existing mark -> clear the mark.
1275 * If shift key and no existing mark -> set mark before moving */
1276 if (!(flags & M_KEY_SHIFT) && curwp->w_markp)
1277 setmark (0,1); /* this clears the mark. */
1278 else if (flags & M_KEY_SHIFT && !curwp->w_markp)
1279 setmark (0,1); /* while this sets the mark. */
1281 /* Reposition dot to mouse down. */
1282 move_dot_to (row, col);
1284 /* Set the mark to dot if no existing mark. */
1285 if (curwp->w_markp == NULL)
1286 setmark (0,1);
1288 /* Track mouse movement. */
1289 register_mtrack (mouse_in_pico);
1290 update ();
1291 lheader = FALSE; /* Just to be sure. */
1293 break;
1296 case M_EVENT_TRACK:
1297 /* Mouse tracking. */
1298 if (lheader) /* Ignore mouse movement in header. */
1299 break;
1301 /* If above or below body, scroll body and adjust the row and col. */
1302 if (row < (Pmaster ? ComposerTopLine : 2)) {
1303 /* Scroll text down screen and move dot to top left corner. */
1304 scrollupline (0,1);
1305 trow = (Pmaster) ? ComposerTopLine : 2;
1306 tcol = 0;
1308 else if (row > term.t_nrow - (term.t_mrow + 1)) {
1309 /* Scroll text up screen and move dot to bottom right corner. */
1310 scrolldownline (0,1);
1311 trow = term.t_nrow - (term.t_mrow + 1);
1312 tcol = term.t_ncol;
1314 else {
1315 trow = row;
1316 tcol = col;
1319 /* Move dot to target column. */
1320 move_dot_to (trow, tcol);
1322 /* Update screen. */
1323 update ();
1324 break;
1327 case M_EVENT_UP:
1328 if(button == M_BUTTON_RIGHT){
1329 #ifdef _WINDOWS
1330 pico_popup();
1331 #endif
1332 break;
1334 else if(button != M_BUTTON_LEFT)
1335 break;
1337 if (lheader) {
1338 lheader = FALSE;
1339 /* Last down in header. */
1340 if (row == lrow && col == lcol) {
1341 /* Mouse up and down in same place in header. Means the
1342 * user want to edit the header. Return KEY_MOUSE which
1343 * will cause mousepress to be called, which will
1344 * call HeaderEditor. Can't call HeaderEditor from here
1345 * because that would mess up layering. */
1346 if (curwp->w_marko)
1347 setmark (0,1);
1348 rv = (unsigned long) KEY_MOUSE;
1351 else {
1352 /* If up at same place, clear mark */
1353 if (curwp->w_markp == curwp->w_dotp &&
1354 curwp->w_marko == curwp->w_doto) {
1355 setmark (0,1);
1356 curwp->w_flag |= WFMOVE;
1358 clear_mtrack ();
1359 update ();
1361 break;
1364 return(rv);
1366 #endif
1371 * mouse_in_content - general mechanism used to pass recognized mouse
1372 * events in predefined region back thru the usual
1373 * keyboard input stream. The actual return value
1374 * passed back from this function is set dynamically
1375 * via the "down" argument which is read when both the
1376 * "row" and "col" arguments are negative.
1378 unsigned long
1379 mouse_in_content(unsigned long mevent, int row, int col, int button, int flags)
1381 unsigned long rv = 0;
1382 static unsigned long mouse_val = KEY_MOUSE;
1384 if(row == -1 && col == -1){
1385 mouse_val = mevent; /* setting return value */
1387 else {
1388 /* A real event. */
1389 levent = mevent;
1390 switch (mevent) {
1391 case M_EVENT_DOWN:
1392 /* Mouse down does not mean anything, just keep track of
1393 * where it went down and if this is a double click. */
1394 #ifdef DOS
1395 #ifdef CLOCKS_PER_SEC
1396 doubleclick = (lrow == row && lcol == col
1397 && clock() < (lastcalled + CLOCKS_PER_SEC/2));
1398 #else
1399 #ifdef CLK_TCK
1400 doubleclick = (lrow == row && lcol == col
1401 && clock() < (lastcalled + CLK_TCK/2));
1402 #else
1403 doubleclick = FALSE;
1404 #endif
1405 #endif
1406 lastcalled = clock();
1407 #else
1408 doubleclick = (lrow == row && lcol == col
1409 && time(0) < (lastcalled + 2));
1410 lastcalled = time(0);
1411 #endif
1412 lrow = row;
1413 lcol = col;
1414 lbutton = button;
1415 lflags = flags;
1416 break;
1418 case M_EVENT_UP:
1419 /* Mouse up. If in the same position as it went down
1420 * then we return the value set above, which goes into
1421 * the character input stream, which gets processed as
1422 * a mouse event by some upper layer, which calls to
1423 * mouse_get_last(). */
1424 if (lrow == row && lcol == col) {
1425 rv = mouse_val;
1427 break;
1429 case M_EVENT_TRACK:
1430 break;
1434 return(rv);
1439 * mouse_get_last - Get last mouse event.
1442 void
1443 mouse_get_last(mousehandler_t *f, MOUSEPRESS *mp)
1445 if (f != NULL)
1446 *f = lastf;
1447 if (mp != NULL) {
1448 mp->mevent = levent;
1449 mp->row = lrow;
1450 mp->col = lcol;
1451 mp->doubleclick = doubleclick;
1452 mp->button = lbutton;
1453 mp->flags = lflags;
1460 * register_key - register the given keystroke to accept mouse events
1462 void
1463 register_key(int i, unsigned rval, char *label, void (*label_printer)(),
1464 int row, int col, int len, COLOR_PAIR *kn, COLOR_PAIR *kl)
1466 if(i > 11)
1467 return;
1469 menuitems[i].val = rval;
1470 menuitems[i].tl.r = menuitems[i].br.r = row;
1471 menuitems[i].tl.c = col;
1472 menuitems[i].br.c = col + len;
1473 menuitems[i].lbl.r = menuitems[i].tl.r;
1474 menuitems[i].lbl.c = menuitems[i].tl.c;
1475 menuitems[i].label_hiliter = label_printer;
1476 if(menuitems[i].label){
1477 free(menuitems[i].label);
1478 menuitems[i].label = NULL;
1480 if(menuitems[i].kncp)
1481 free_color_pair(&menuitems[i].kncp);
1482 if(menuitems[i].klcp)
1483 free_color_pair(&menuitems[i].klcp);
1484 if(kn)
1485 menuitems[i].kncp = new_color_pair(kn->fg, kn->bg);
1486 else
1487 menuitems[i].kncp = NULL;
1488 if(kl)
1489 menuitems[i].klcp = new_color_pair(kl->fg, kl->bg);
1490 else
1491 menuitems[i].klcp = NULL;
1493 if(label){
1494 size_t len;
1496 len = strlen(label);
1497 if((menuitems[i].label = (char *)malloc((len+1)*sizeof(char))) != NULL){
1498 strncpy(menuitems[i].label, label, len);
1499 menuitems[i].label[len] = '\0';
1506 mouse_on_key(int row, int col)
1508 int i;
1510 for(i = 0; i < 12; i++)
1511 if(M_ACTIVE(row, col, &menuitems[i]))
1512 return(TRUE);
1514 return(FALSE);
1516 #endif /* MOUSE */
1520 * Below are functions for use outside pico to manipulate text
1521 * in a pico's native format (circular linked list of lines).
1523 * The idea is to streamline pico use by making it fairly easy
1524 * for outside programs to prepare text intended for pico's use.
1525 * The simple char * alternative is messy as it requires two copies
1526 * of the same text, and isn't very economic in limited memory
1527 * situations (THANKS BELLEVUE-BILLY.).
1529 typedef struct picotext {
1530 LINE *linep;
1531 LINE *dotp;
1532 int doto;
1533 short crinread;
1534 } PICOTEXT;
1536 #define PT(X) ((PICOTEXT *)(X))
1539 * pico_get - return window struct pointer used as a handle
1540 * to the other pico_xxx routines.
1542 void *
1543 pico_get(void)
1545 PICOTEXT *wp = NULL;
1546 LINE *lp = NULL;
1548 if((wp = (PICOTEXT *)malloc(sizeof(PICOTEXT))) != NULL){
1549 wp->crinread = 0;
1550 if((lp = lalloc(0)) == NULL){
1551 free(wp);
1552 return(NULL);
1555 wp->dotp = wp->linep = lp->l_fp = lp->l_bp = lp;
1556 wp->doto = 0;
1558 else
1559 emlwrite("Can't allocate space for text", NULL);
1561 return((void *)wp);
1565 * pico_give - free resources and give up picotext struct
1567 void
1568 pico_give(void *w)
1570 register LINE *lp;
1571 register LINE *fp;
1573 fp = lforw(PT(w)->linep);
1574 while((lp = fp) != PT(w)->linep){
1575 fp = lforw(lp);
1576 free(lp);
1578 free(PT(w)->linep);
1579 free((PICOTEXT *)w);
1583 * pico_readc - return char at current point. Up to calling routines
1584 * to keep cumulative count of chars.
1585 * The characters in PT are UCS-4 characters. The caller
1586 * of pico_readc is expecting UTF-8 chars. We convert
1587 * each UCS-4 character to a string of UTF-8 characters
1588 * and return them one at a time.
1591 pico_readc(void *w, unsigned char *c, int flags)
1593 int rv = 0;
1594 UCS ucs;
1595 static unsigned char obuf[6];
1596 static unsigned char *obufpend = obuf;
1597 static unsigned char *obufpnext = obuf;
1599 if(!(flags & PICOREADC_NOUCS)){
1600 if(obufpend > obuf){
1601 *c = *obufpnext++;
1602 rv++;
1603 if(obufpnext >= obufpend){
1604 obufpend = obuf;
1605 obufpnext = obuf;
1608 return(rv);
1612 if(PT(w)->crinread){
1613 *c = '\012'; /* return LF */
1614 PT(w)->crinread = 0;
1615 rv++;
1617 else if(PT(w)->doto < llength(PT(w)->dotp)){ /* normal char to return */
1618 if(flags & PICOREADC_NOUCS){
1619 *c = (unsigned char) lgetc(PT(w)->dotp, (PT(w)->doto)++).c;
1620 rv++;
1622 else{
1623 rv++;
1624 ucs = lgetc(PT(w)->dotp, (PT(w)->doto)++).c;
1625 obufpend = utf8_put(obuf, (unsigned long) ucs);
1626 obufpnext = obuf;
1627 if(obufpend > obuf){
1628 *c = *obufpnext++;
1629 if(obufpnext >= obufpend){
1630 obufpend = obuf;
1631 obufpnext = obuf;
1634 else
1635 *c = '?';
1638 else if(PT(w)->dotp != PT(w)->linep){ /* return line break */
1639 PT(w)->dotp = lforw(PT(w)->dotp);
1640 PT(w)->doto = 0;
1641 #if defined(DOS) || defined(OS2)
1642 *c = '\015';
1643 PT(w)->crinread++;
1644 #else
1645 *c = '\012'; /* return local eol! */
1646 #endif
1647 rv++;
1648 } /* else no chars to return */
1650 return(rv);
1655 * pico_writec - write a char into picotext and advance pointers.
1656 * Up to calling routines to keep track of total chars
1657 * written.
1658 * Incoming text (c) is UTF-8 and written chars are UCS-4.
1659 * We need to collect up multiple chars to make a single
1660 * UCS-4 char, so there needs to be some state between calls.
1663 pico_writec(void *w, int c, int flags)
1665 int rv = 0;
1667 if(c == '\r') /* ignore CR's */
1668 rv++; /* so fake it */
1669 else if(c == '\n'){ /* insert newlines on LF */
1671 * OK, if there are characters on the current line or
1672 * dotp is pointing to the delimiter line, insert a newline
1673 * No here's the tricky bit; preserve the implicit EOF newline.
1675 if(lforw(PT(w)->dotp) == PT(w)->linep && PT(w)->dotp != PT(w)->linep){
1676 PT(w)->dotp = PT(w)->linep;
1677 PT(w)->doto = 0;
1679 else{
1680 register LINE *lp;
1682 if((lp = lalloc(0)) == NULL){
1683 emlwrite("Can't allocate space for more characters",NULL);
1684 return(0);
1687 if(PT(w)->dotp == PT(w)->linep){
1688 lforw(lp) = PT(w)->linep;
1689 lback(lp) = lback(PT(w)->linep);
1690 lforw(lback(lp)) = lback(PT(w)->linep) = lp;
1692 else{
1693 lforw(lp) = lforw(PT(w)->dotp);
1694 lback(lp) = PT(w)->dotp;
1695 lback(lforw(lp)) = lforw(PT(w)->dotp) = lp;
1696 PT(w)->dotp = lp;
1697 PT(w)->doto = 0;
1701 rv++;
1703 else{
1704 if(flags & PICOREADC_NOUCS){
1706 * With this flag we're reverting to the old behavior where no
1707 * UTF-8 to UCS-4 translation takes place. We assume nothing
1708 * about the incoming byte stream. We just write a byte at a
1709 * time. So even though it's being stored in PT each
1710 * item is really limited to a single octet value.
1712 rv = geninsert(&PT(w)->dotp, &PT(w)->doto, PT(w)->linep, c, 0, 1, NULL);
1714 else{
1715 static unsigned char cbuf[6];
1716 static unsigned char *cbufp = cbuf;
1717 UCS obuf[MAX(MB_LEN_MAX,32)];
1718 int i, outchars = 0;
1720 if(cbufp < cbuf+sizeof(cbuf)){
1721 unsigned char *inputp;
1722 unsigned long remaining_octets;
1723 UCS ucs;
1725 *cbufp++ = (unsigned char) c;
1726 inputp = cbuf;
1727 remaining_octets = (cbufp - cbuf) * sizeof(unsigned char);
1728 ucs = (UCS) utf8_get(&inputp, &remaining_octets);
1730 switch(ucs){
1731 case U8G_ENDSTRG: /* incomplete character, wait */
1732 case U8G_ENDSTRI: /* incomplete character, wait */
1733 break;
1735 default:
1736 if(ucs & U8G_ERROR || ucs == UBOGON){
1738 * None of these cases is supposed to happen. If it
1739 * does happen then the input stream isn't UTF-8
1740 * so something is wrong. Treat each character in the
1741 * input buffer as a separate error character and
1742 * print a '?' for each.
1744 for(inputp = cbuf; inputp < cbufp; inputp++)
1745 obuf[outchars++] = '?';
1747 cbufp = cbuf;
1749 else{
1750 /* got a character */
1751 if(ucs >= 0x80 && wcellwidth(ucs) < 0){
1753 * This happens when we have a UTF-8 character that
1754 * we aren't able to print in our locale. For example,
1755 * if the locale is setup with the terminal
1756 * expecting ISO-8859-1 characters then there are
1757 * lots of UTF-8 characters that can't be printed.
1758 * Print a '?' instead.
1759 This may be the wrong thing to do. What happens if user
1760 is just forwarding and doesn't edit. We are going to lose
1761 the original value, aren't we? Maybe we do this only
1762 when printing to the screen instead.
1764 obuf[outchars++] = '?';
1766 else{
1768 * A regular ucs character
1770 obuf[outchars++] = ucs;
1773 /* update the input buffer */
1774 if(inputp >= cbufp) /* this should be the case */
1775 cbufp = cbuf;
1776 else{ /* extra chars for some reason? */
1777 unsigned char *q, *newcbufp;
1779 newcbufp = (cbufp - inputp) + cbuf;
1780 q = cbuf;
1781 while(inputp < cbufp)
1782 *q++ = *inputp++;
1784 cbufp = newcbufp;
1788 break;
1791 else{ /* error */
1792 obuf[0] = '?';
1793 outchars = 1;
1794 cbufp = cbuf; /* start over */
1798 * Unless we have trouble translating outchars will be
1799 * either 1 or 0. It is the UCS-4 character that we converted
1800 * the input to. It's an array just so it can hold ? when
1801 * there is trouble.
1803 rv = 1;
1804 for(i = 0; rv && i < outchars; i++)
1805 if(!geninsert(&PT(w)->dotp, &PT(w)->doto, PT(w)->linep, obuf[i], 0, 1, NULL))
1806 rv = 0;
1811 * Warning, this is no longer number written, because when we have a
1812 * multibyte UTF-8 character we won't write anything until we get all
1813 * the bytes.
1815 return((rv) ? 1 : 0); /* return number written */
1820 * pico_puts - just write the given string into the text
1823 pico_puts(void *w, char *s, int flags)
1825 int rv = 0;
1827 if(*s != '\0'){
1828 rv = 1;
1829 while(rv && *s != '\0')
1830 if(!pico_writec(w, (int)*s++, flags))
1831 rv = 0;
1834 return((rv) ? 1 : 0);
1839 * pico_seek - position dotp and dot at requested location
1842 pico_seek(void *w, long offset, int orig)
1844 register LINE *lp;
1846 PT(w)->crinread = 0;
1847 switch(orig){
1848 case 0 : /* SEEK_SET */
1849 PT(w)->dotp = lforw(PT(w)->linep);
1850 PT(w)->doto = 0;
1851 case 1 : /* SEEK_CUR */
1852 lp = PT(w)->dotp;
1853 while(lp != PT(w)->linep){
1854 if(offset <= llength(lp)){
1855 PT(w)->doto = (int)offset;
1856 PT(w)->dotp = lp;
1857 break;
1860 offset -= ((long)llength(lp)
1861 #if defined(DOS) || defined(OS2)
1862 + 2L);
1863 #else
1864 + 1L);
1865 #endif
1866 lp = lforw(lp);
1868 break;
1870 case 2 : /* SEEK_END */
1871 PT(w)->dotp = lback(PT(w)->linep);
1872 PT(w)->doto = llength(PT(w)->dotp);
1873 break;
1874 default :
1875 return(-1);
1878 return(0);
1883 * breplace - replace the current window's text with the given
1884 * LINEs
1886 void
1887 breplace(void *w)
1889 register LINE *lp;
1890 register LINE *fp;
1892 fp = lforw(curbp->b_linep);
1893 while((lp = fp) != curbp->b_linep){ /* blast old lines */
1894 fp = lforw(lp);
1895 free(lp);
1897 free(curbp->b_linep);
1899 curbp->b_linep = PT(w)->linep; /* arrange pointers */
1902 * Undo space-stuffing that was done when we were preparing to send.
1903 * Some error happened so we're back to the composer.
1905 if(Pmaster && Pmaster->space_stuffed){
1906 Pmaster->space_stuffed = 0;
1907 for(lp = lforw(curbp->b_linep); lp != curbp->b_linep; lp = lforw(lp)){
1908 if(llength(lp) && ucs4_isspace(lgetc(lp, 0).c)){
1909 curwp->w_dotp = lp;
1910 curwp->w_doto = 0;
1911 forwdel(FALSE,1);
1916 curwp->w_linep = lforw(curbp->b_linep);
1917 curwp->w_dotp = lforw(curbp->b_linep);
1918 curwp->w_doto = 0;
1919 curwp->w_markp = curwp->w_imarkp = NULL;
1920 curwp->w_marko = curwp->w_imarko = 0;
1922 curbp->b_dotp = curwp->w_dotp;
1923 curbp->b_doto = curbp->b_marko = 0;
1924 curbp->b_markp = NULL;
1925 curbp->b_linecnt = -1;
1927 curwp->w_flag |= WFHARD;
1930 void
1931 free_pico_module_globals(void)
1933 int i;
1935 #ifdef MOUSE
1936 for(i = 0; i < 12; i++){
1937 if(menuitems[i].kncp)
1938 free_color_pair(&menuitems[i].kncp);
1939 if(menuitems[i].klcp)
1940 free_color_pair(&menuitems[i].klcp);
1941 if(menuitems[i].label){
1942 free((void *) menuitems[i].label);
1943 menuitems[i].label = NULL;
1946 #endif /* MOUSE */
1951 #ifdef _WINDOWS
1956 composer_file_drop(int x, int y, char *filename)
1958 int attached = 0;
1959 EML eml;
1961 if((ComposerTopLine > 2 && x <= ComposerTopLine)
1962 || !LikelyASCII(filename)){
1963 AppendAttachment(filename, NULL, NULL);
1964 attached++;
1966 else{
1967 setimark(FALSE, 1);
1968 ifile(filename);
1969 swapimark(FALSE, 1);
1972 if(ComposerEditing){ /* update display */
1973 PaintBody(0);
1975 else{
1976 pico_refresh(0, 1);
1977 update();
1980 eml.s = filename;
1981 if(attached)
1982 emlwrite(_("Attached dropped file \"%s\""), &eml);
1983 else
1984 emlwrite(_("Inserted dropped file \"%s\""), &eml);
1986 if(ComposerEditing){ /* restore cursor */
1987 HeaderPaintCursor();
1989 else{
1990 curwp->w_flag |= WFHARD;
1991 update();
1994 return(1);
1999 pico_cursor(int col, long row)
2001 return((row > 1 && row <= term.t_nrow - (term.t_mrow + 1))
2002 ? MSWIN_CURSOR_IBEAM
2003 : MSWIN_CURSOR_ARROW);
2005 #endif /* _WINDOWS */