summaryrefslogtreecommitdiff
path: root/pico/pico.c
diff options
context:
space:
mode:
Diffstat (limited to 'pico/pico.c')
-rw-r--r--pico/pico.c1939
1 files changed, 1939 insertions, 0 deletions
diff --git a/pico/pico.c b/pico/pico.c
new file mode 100644
index 00000000..166a3d3d
--- /dev/null
+++ b/pico/pico.c
@@ -0,0 +1,1939 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: pico.c 921 2008-01-31 02:09:25Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Main Pine Composer routines
+ *
+ *
+ * WEEMACS/PICO NOTES:
+ *
+ * 01 Nov 89 - MicroEmacs 3.6 vastly pared down and becomes a function call
+ * weemacs() and plugged into the Pine mailer. Lots of unused
+ * MicroEmacs code laying around.
+ *
+ * 17 Jan 90 - weemacs() became weemacs() the composer. Header editing
+ * functions added.
+ *
+ * 09 Sep 91 - weemacs() renamed pico() for the PIne COmposer.
+ *
+ */
+
+
+/*
+ * This program is in public domain; written by Dave G. Conroy.
+ * This file contains the main driving routine, and some keyboard processing
+ * code, for the MicroEMACS screen editor.
+ *
+ * REVISION HISTORY:
+ *
+ * 1.0 Steve Wilhite, 30-Nov-85
+ * - Removed the old LK201 and VT100 logic. Added code to support the
+ * DEC Rainbow keyboard (which is a LK201 layout) using the the Level
+ * 1 Console In ROM INT. See "rainbow.h" for the function key defs
+ * Steve Wilhite, 1-Dec-85
+ * - massive cleanup on code in display.c and search.c
+ *
+ * 2.0 George Jones, 12-Dec-85
+ * - Ported to Amiga.
+ *
+ * 3.0 Daniel Lawrence, 29-Dec-85
+ * 16-apr-86
+ * - updated documentation and froze development for 3.6 net release
+ */
+
+/* make global definitions not external */
+#define maindef
+
+#include "../c-client/mail.h"
+#include "../c-client/utf8.h"
+
+#ifdef _WINDOWS
+/* wingdi.h uses ERROR (!) and we aren't using the c-client ERROR so... */
+#undef ERROR
+
+#endif
+#include "headers.h"
+#include "ebind.h" /* default key bindings */
+#include "../pith/charconv/filesys.h"
+
+
+void func_init(void);
+void breplace(void *w);
+int any_header_changes(void);
+int cleanwhitespace(void);
+int isquotedspace(LINE *);
+
+
+/*
+ * function key mappings
+ */
+static UCS pfkm[12][2] = {
+ { F1, (CTRL|'G')},
+ { F2, (CTRL|'C')},
+ { F3, (CTRL|'X')},
+ { F4, (CTRL|'J')},
+ { F5, (CTRL|'R')},
+ { F6, (CTRL|'W')},
+ { F7, (CTRL|'Y')},
+ { F8, (CTRL|'V')},
+ { F9, (CTRL|'K')},
+ { F10, (CTRL|'U')},
+ { F11, (CTRL|'O')},
+#ifdef SPELLER
+ { F12, (CTRL|'T')}
+#else
+ { F12, (CTRL|'D')}
+#endif
+};
+
+
+/*
+ * flag for the various functions in pico() to set when ready
+ * for pico() to return...
+ */
+int pico_all_done = 0;
+jmp_buf finstate;
+UCS *pico_anchor = NULL;
+extern struct headerentry *headents;
+
+/*
+ * pico - the main routine for Pine's composer.
+ *
+ */
+int
+pico(PICO *pm)
+{
+ UCS c;
+ register int f;
+ register int n;
+ char bname[NBUFN]; /* buffer name of file to read */
+ extern struct on_display ods;
+ int checkpointcnt = 0, input = 0;
+ int ret;
+ char chkptfile[NLINE];
+#ifdef _WINDOWS
+ int cursor_shown;
+#endif
+
+ Pmaster = pm;
+ gmode = MDWRAP;
+ gmode |= pm->pine_flags; /* high 4 bits rsv'd for pine */
+
+ alt_speller = pm->alt_spell;
+ pico_all_done = 0;
+ km_popped = 0;
+
+ if(!vtinit()) /* Init Displays. */
+ return(COMP_CANCEL);
+
+ strncpy(bname, "main", sizeof(bname)); /* default buffer name */
+ bname[sizeof(bname)-1] = '\0';
+ edinit(bname); /* Buffers, windows. */
+
+ if(InitMailHeader(pm)) /* init mail header structure */
+ gmode &= ~(P_BODY | P_HEADEND); /* flip off special header stuff */
+
+ /* setup to process commands */
+ lastflag = 0; /* Fake last flags. */
+ curbp->b_mode |= gmode; /* and set default modes*/
+
+ if(Pmaster->pine_anchor)
+ pico_anchor = utf8_to_ucs4_cpystr(Pmaster->pine_anchor);
+ else
+ pico_anchor = NULL;
+
+ if(Pmaster->quote_str)
+ glo_quote_str = utf8_to_ucs4_cpystr(Pmaster->quote_str);
+ else
+ glo_quote_str = NULL;
+
+ if(Pmaster->wordseps)
+ glo_wordseps = ucs4_cpystr(Pmaster->wordseps);
+ else
+ glo_wordseps = NULL;
+
+ bindtokey(DEL, (gmode & P_DELRUBS) ? forwdel : backdel);
+
+ if(pm->msgtext)
+ breplace(pm->msgtext);
+
+#ifdef _WINDOWS
+ cursor_shown = mswin_showcaret(1); /* turn on for main window */
+ mswin_allowpaste(MSWIN_PASTE_FULL);
+ mswin_setscrollcallback (pico_scroll_callback);
+#endif
+
+ /* prepare for checkpointing */
+ chkptfile[0] = '\0';
+ chkptinit((*Pmaster->ckptdir)(chkptfile, sizeof(chkptfile)), sizeof(chkptfile));
+ if(gmode & P_CHKPTNOW)
+ writeout(chkptfile, TRUE);
+
+ pico_all_done = setjmp(finstate); /* jump out of HUP handler ? */
+
+ if(gmode & MDALTNOW){
+ while(!pico_all_done){
+ if(((gmode & P_BODY) || !Pmaster->headents)
+ && alt_editor(0, 1) < 0)
+ break; /* if problem, drop into pico */
+
+ if(Pmaster->headents){
+ update(); /* paint screen, n' start editing... */
+ HeaderEditor((gmode & (P_HEADEND | P_BODY)) ? 2 : 0, 0);
+ gmode |= P_BODY; /* make sure we enter alt ed next */
+ }
+ else
+ pico_all_done = COMP_EXIT;
+ }
+ }
+ else if(!pico_all_done){
+ if(gmode & P_BODY){ /* begin editing the header? */
+ ArrangeHeader(); /* line up pointers */
+ /*
+ * Move to the offset pine asked us to move to.
+ * Perhaps we should be checking to see if this is
+ * a reasonable number before moving.
+ */
+ if(Pmaster && Pmaster->edit_offset)
+ forwchar(FALSE, Pmaster->edit_offset);
+ }
+ else{
+ update(); /* paint screen, */
+ HeaderEditor((gmode & P_HEADEND) ? 2 : 0, 0);
+ }
+ }
+
+ while(1){
+ if(pico_all_done){
+#ifdef _WINDOWS
+ if(!cursor_shown)
+ mswin_showcaret(0);
+
+ mswin_allowpaste(MSWIN_PASTE_DISABLE);
+ mswin_setscrollcallback (NULL);
+#endif
+ ret = anycb() ? BUF_CHANGED : 0;
+ switch(pico_all_done){ /* prepare for/handle final events */
+ case COMP_EXIT : /* already confirmed */
+ packheader();
+ if(Pmaster
+ && (Pmaster->strip_ws_before_send
+ || Pmaster->allow_flowed_text))
+ cleanwhitespace();
+ ret |= COMP_EXIT;
+ break;
+
+ case COMP_CANCEL : /* also already confirmed */
+ packheader();
+ ret = COMP_CANCEL;
+ break;
+
+ case COMP_GOTHUP:
+ /*
+ * pack up and let caller know that we've received a SIGHUP
+ */
+ if(ComposerEditing) /* expand addr if needed */
+ call_builder(&headents[ods.cur_e], NULL, NULL);
+
+ packheader();
+ ret |= COMP_GOTHUP;
+ break;
+
+ case COMP_SUSPEND :
+ default: /* safest if internal error */
+ /*
+ * If we're in the headers mark the current header line
+ * with start_here bit so caller knows where to reset.
+ * Also set the edit_offset, which is either the offset
+ * into this header line or the offset into the body.
+ * Packheader will adjust edit_offset for multi-line
+ * headers.
+ */
+ if(ComposerEditing){ /* in the headers */
+ headents[ods.cur_e].start_here = 1;
+ Pmaster->edit_offset = ods.p_ind;
+ }
+ else{
+ register LINE *clp;
+ register long offset;
+
+ for(clp = lforw(curbp->b_linep), offset = 0L;
+ clp != curwp->w_dotp;
+ clp = lforw(clp))
+ offset += (llength(clp) + 1);
+
+ Pmaster->edit_offset = offset + curwp->w_doto;
+ }
+
+ packheader();
+ ret |= COMP_SUSPEND;
+ break;
+ }
+
+ if(pico_anchor)
+ fs_give((void **) &pico_anchor);
+ if(glo_quote_str)
+ fs_give((void **) &glo_quote_str);
+ if(glo_wordseps)
+ fs_give((void **) &glo_wordseps);
+
+ vttidy(); /* clean up tty modes */
+ zotdisplay(); /* blast display buffers */
+ zotedit();
+ our_unlink(chkptfile);
+ Pmaster = NULL; /* blat global */
+
+ return(ret);
+ }
+
+ if(km_popped){
+ km_popped--;
+ if(km_popped == 0) /* cause bottom three lines to be repainted */
+ curwp->w_flag |= WFHARD;
+ }
+
+ if(km_popped){ /* temporarily change to cause menu to be painted */
+ term.t_mrow = 2;
+ curwp->w_ntrows -= 2;
+ curwp->w_flag |= WFMODE;
+ movecursor(term.t_nrow-2, 0); /* clear status line, too */
+ peeol();
+ }
+
+ update(); /* Fix up the screen */
+ if(km_popped){
+ term.t_mrow = 0;
+ curwp->w_ntrows += 2;
+ }
+
+#ifdef MOUSE
+#ifdef EX_MOUSE
+ /* New mouse function for real mouse text seletion. */
+ register_mfunc(mouse_in_pico, 2, 0, term.t_nrow - (term.t_mrow+1),
+ term.t_ncol);
+#else
+ mouse_in_content(KEY_MOUSE, -1, -1, -1, 0);
+ register_mfunc(mouse_in_content, 2, 0, term.t_nrow - (term.t_mrow + 1),
+ term.t_ncol);
+#endif
+#endif
+#ifdef _WINDOWS
+ mswin_setdndcallback (composer_file_drop);
+ mswin_mousetrackcallback(pico_cursor);
+#endif
+ c = GetKey();
+ if (term.t_nrow < 6 && c != NODATA){
+ (*term.t_beep)();
+ emlwrite(_("Please make the screen bigger."), NULL);
+ continue;
+ }
+
+#ifdef MOUSE
+#ifdef EX_MOUSE
+ clear_mfunc(mouse_in_pico);
+#else
+ clear_mfunc(mouse_in_content);
+#endif
+#endif
+#ifdef _WINDOWS
+ mswin_cleardndcallback ();
+ mswin_mousetrackcallback(NULL);
+#endif
+ if(c == NODATA || time_to_check()){ /* new mail ? */
+ if((*Pmaster->newmail)(c == NODATA ? 0 : 2, 1) >= 0){
+ int rv;
+
+ if(km_popped){
+ term.t_mrow = 2;
+ curwp->w_ntrows -= 2;
+ curwp->w_flag |= WFHARD;
+ km_popped = 0;
+ }
+
+ clearcursor();
+ mlerase();
+ rv = (*Pmaster->showmsg)(c);
+ ttresize();
+ picosigs(); /* restore altered handlers */
+ if(rv) /* Did showmsg corrupt the display? */
+ PaintBody(0); /* Yes, repaint */
+
+ mpresf = 1;
+ input = 0;
+ }
+
+ clearcursor();
+ movecursor(0, 0);
+ }
+
+ if(km_popped)
+ switch(c){
+ case NODATA:
+ case (CTRL|'L'):
+ km_popped++;
+ break;
+
+ default:
+ mlerase();
+ break;
+ }
+
+ if(c == NODATA) /* no op, getkey timed out */
+ continue;
+ else if(!input++)
+ (*Pmaster->keybinput)();
+
+ if (mpresf != FALSE) { /* message stay around only */
+ if (mpresf++ > NMMESSDELAY) /* so long! */
+ mlerase();
+ }
+
+ f = FALSE; /* vestigial */
+ n = 1;
+ /* Do it. */
+ execute(normalize_cmd(c, pfkm, 2), f, n);
+ if(++checkpointcnt >= CHKPTDELAY){
+ checkpointcnt = 0;
+ writeout(chkptfile, TRUE);
+ }
+ }
+}
+
+/*
+ * Initialize all of the buffers and windows. The buffer name is passed down
+ * as an argument, because the main routine may have been told to read in a
+ * file by default, and we want the buffer name to be right.
+ */
+
+/*
+ * For the pine composer, we don't want to take over the whole screen
+ * for editing. the first some odd lines are to be used for message
+ * header information editing.
+ */
+void
+edinit(char bname[])
+{
+ register BUFFER *bp;
+ register WINDOW *wp;
+
+ if(Pmaster)
+ func_init();
+
+ bp = bfind(bname, TRUE, BFWRAPOPEN); /* First buffer */
+ wp = (WINDOW *) malloc(sizeof(WINDOW)); /* First window */
+
+ if (bp==NULL || wp==NULL){
+ if(Pmaster)
+ return;
+ else
+ exit(1);
+ }
+
+ curbp = bp; /* Make this current */
+ wheadp = wp;
+ curwp = wp;
+ wp->w_wndp = NULL; /* Initialize window */
+ wp->w_bufp = bp;
+ bp->b_nwnd = 1; /* Displayed. */
+ wp->w_linep = bp->b_linep;
+ wp->w_dotp = bp->b_linep;
+ wp->w_doto = 0;
+ wp->w_markp = wp->w_imarkp = NULL;
+ wp->w_marko = wp->w_imarko = 0;
+ bp->b_linecnt = -1;
+
+ if(Pmaster){
+ term.t_mrow = Pmaster->menu_rows;
+ wp->w_toprow = ComposerTopLine = COMPOSER_TOP_LINE;
+ wp->w_ntrows = term.t_nrow - COMPOSER_TOP_LINE - term.t_mrow;
+ fillcol = Pmaster->fillcolumn;
+ strncpy(opertree,
+ (Pmaster->oper_dir && strlen(Pmaster->oper_dir) < NLINE)
+ ? Pmaster->oper_dir : "", sizeof(opertree));
+ opertree[sizeof(opertree)-1] = '\0';
+ input_cs = Pmaster->input_cs;
+ }
+ else{
+ if(sup_keyhelp)
+ term.t_mrow = 0;
+ else
+ term.t_mrow = 2;
+
+ wp->w_toprow = 2;
+ wp->w_ntrows = term.t_nrow - 2 - term.t_mrow;
+ if(userfillcol > 0) /* set fill column */
+ fillcol = userfillcol;
+ else
+ fillcol = term.t_ncol - 6;
+ }
+
+ /*
+ * MDSCUR mode implies MDTREE mode with a opertree of home directory,
+ * unless opertree has been set differently.
+ */
+ if((gmode & MDSCUR) && !opertree[0]){
+ strncpy(opertree, gethomedir(NULL), sizeof(opertree));
+ opertree[sizeof(opertree)-1] = '\0';
+ }
+
+ if(*opertree)
+ fixpath(opertree, sizeof(opertree));
+
+ wp->w_force = 0;
+ wp->w_flag = WFMODE|WFHARD; /* Full. */
+}
+
+
+/*
+ * This is the general command execution routine. It handles the fake binding
+ * of all the keys to "self-insert". It also clears out the "thisflag" word,
+ * and arranges to move it to the "lastflag", so that the next command can
+ * look at it. Return the status of command.
+ */
+int
+execute(UCS c, int f, int n)
+{
+ KEYTAB *ktp;
+ int status, ww;
+
+ ktp = (Pmaster) ? &keytab[0] : &pkeytab[0];
+
+ while (ktp->k_fp != NULL) {
+ if (ktp->k_code == c) {
+
+ if(lastflag&CFFILL){
+ curwp->w_flag |= WFMODE;
+ if(Pmaster == NULL)
+ sgarbk = TRUE;
+ }
+
+ thisflag = 0;
+ status = (*ktp->k_fp)(f, n);
+ if((lastflag & CFFILL) && !(thisflag & CFFILL))
+ fdelete();
+ if((lastflag & CFFLBF) && !(thisflag & CFFLBF))
+ kdelete();
+
+ lastflag = thisflag;
+
+ /*
+ * Reset flag saying wrap should open a new line whenever
+ * we execute a command (as opposed to just typing in text).
+ * However, if that command leaves us in the same line on the
+ * screen, then don't reset.
+ */
+ if(curwp->w_flag & (WFMOVE | WFHARD))
+ curbp->b_flag |= BFWRAPOPEN; /* wrap should open new line */
+
+ return (status);
+ }
+ ++ktp;
+ }
+
+ if(lastflag & CFFILL) /* blat unusable fill data */
+ fdelete();
+ if(lastflag & CFFLBF)
+ kdelete();
+
+ if (VALID_KEY(c)) { /* Self inserting. */
+
+ if (n <= 0) { /* Fenceposts. */
+ lastflag = 0;
+ return (n<0 ? FALSE : TRUE);
+ }
+ thisflag = 0; /* For the future. */
+
+ /* do the appropriate insertion */
+ /* pico never does C mode, this is simple */
+ status = linsert(n, c);
+
+ /*
+ * Check to make sure we didn't go off of the screen
+ * with that character. Take into account tab expansion.
+ * If so wrap the line...
+ */
+ if(curwp->w_bufp->b_mode & MDWRAP){
+ int j, wid;
+
+ wid = 0;
+ for(j = 0; j < llength(curwp->w_dotp); j++)
+ if(ucs4_isspace(lgetc(curwp->w_dotp, j).c)){
+ if(lgetc(curwp->w_dotp, j).c == TAB){
+ ++wid;
+ while(wid & 0x07)
+ ++wid;
+ }
+ else
+ ++wid;
+ }
+ else{
+ ww = wcellwidth((UCS) lgetc(curwp->w_dotp, j).c);
+ wid += (ww >= 0 ? ww : 1);
+ if(wid > fillcol){
+ wrapword();
+ break;
+ }
+ }
+ }
+
+ lastflag = thisflag;
+ return (status);
+ }
+
+ unknown_command(c);
+
+ lastflag = 0; /* Fake last flags. */
+ return (FALSE);
+}
+
+
+
+/*
+ * Fancy quit command, as implemented by Norm. If the any buffer has
+ * changed do a write on that buffer and exit emacs, otherwise simply exit.
+ */
+int
+quickexit(int f, int n)
+{
+ register BUFFER *bp; /* scanning pointer to buffers */
+
+ bp = bheadp;
+ while (bp != NULL) {
+ if ((bp->b_flag&BFCHG) != 0 /* Changed. */
+ && (bp->b_flag&BFTEMP) == 0) { /* Real. */
+ curbp = bp; /* make that buffer cur */
+ filesave(f, n);
+ }
+ bp = bp->b_bufp; /* on to the next buffer */
+ }
+ return(wquit(f, n)); /* conditionally quit */
+}
+
+
+
+/*
+ * abort_composer - ask the question here, then go quit or
+ * return FALSE
+ */
+int
+abort_composer(int f, int n)
+{
+ char *result;
+
+ result = "";
+
+ Pmaster->arm_winch_cleanup++;
+ if(Pmaster->canceltest){
+ if(((Pmaster->pine_flags & MDHDRONLY) && !any_header_changes())
+ || (result = (*Pmaster->canceltest)(redraw_pico_for_callback))){
+ pico_all_done = COMP_CANCEL;
+ emlwrite(result, NULL);
+ Pmaster->arm_winch_cleanup--;
+ return(TRUE);
+ }
+ else{
+ /* TRANSLATORS: The user typed the Cancel command and was
+ asked to confirm that. Instead they canceled the cancel
+ command. */
+ emlwrite(_("Cancel Cancelled"), NULL);
+ curwp->w_flag |= WFMODE; /* and modeline so we */
+ sgarbk = TRUE; /* redraw the keymenu */
+ pclear(term.t_nrow-1, term.t_nrow);
+ Pmaster->arm_winch_cleanup--;
+ return(FALSE);
+ }
+ }
+ else switch(mlyesno_utf8(Pmaster->headents
+ ? _("Cancel message (answering \"Yes\" will abandon your mail message)")
+ : (anycb() == FALSE)
+ ? _("Cancel Edit (and abandon changes)")
+ : _("Cancel Edit"),
+ FALSE)){
+ case TRUE:
+ pico_all_done = COMP_CANCEL;
+ return(TRUE);
+
+ case ABORT:
+ emlwrite(_("\007Cancel Cancelled"), NULL);
+ break;
+
+ default:
+ mlerase();
+ }
+ return(FALSE);
+}
+
+
+/*
+ * suspend_composer - return to pine with what's been edited so far
+ */
+int
+suspend_composer(int f, int n)
+{
+ if(Pmaster && Pmaster->headents)
+ pico_all_done = COMP_SUSPEND;
+ else
+ (*term.t_beep)();
+
+ return(TRUE);
+}
+
+
+
+/*
+ * Quit command. If an argument, always quit. Otherwise confirm if a buffer
+ * has been changed and not written out. Normally bound to "C-X C-C".
+ */
+int
+wquit(int f, int n)
+{
+ register int s;
+
+ if(Pmaster){
+ char *result = NULL;
+ int ret;
+
+ /* First, make sure there are no outstanding problems */
+ if(AttachError()){
+ emlwrite(_("\007Problem with attachments! Fix errors or delete attachments."), NULL);
+ return(FALSE);
+ }
+
+#ifdef SPELLER
+ if(Pmaster->always_spell_check)
+ if(spell(0, 0) == -1)
+ sleep(3); /* problem, show error */
+#endif
+ /*
+ * if we're not in header, show some of it as we verify sending...
+ */
+ display_for_send();
+ packheader();
+ Pmaster->arm_winch_cleanup++;
+ if((!(Pmaster->pine_flags & MDHDRONLY) || any_header_changes())
+ && (ret = (*Pmaster->exittest)(Pmaster->headents,
+ redraw_pico_for_callback,
+ Pmaster->allow_flowed_text,
+ &result))){
+ Pmaster->arm_winch_cleanup--;
+
+ if(ret == -1){
+ pico_all_done = COMP_CANCEL;
+ }
+ else{
+ if(sgarbf)
+ update();
+
+ lchange(WFHARD); /* set update flags... */
+ curwp->w_flag |= WFMODE; /* and modeline so we */
+ sgarbk = TRUE; /* redraw the keymenu */
+ pclear(term.t_nrow-2, term.t_nrow);
+ }
+
+ if(result && *result)
+ emlwrite(result, NULL);
+ }
+ else{
+ Pmaster->arm_winch_cleanup--;
+ pico_all_done = COMP_EXIT;
+ return(TRUE);
+ }
+ }
+ else{
+ if (f != FALSE /* Argument forces it. */
+ || anycb() == FALSE /* All buffers clean. */
+ /* User says it's OK. */
+ /* TRANSLATORS: buffer is the in-memory copy of a file */
+ || (s=mlyesno_utf8(_("Save modified buffer (ANSWERING \"No\" WILL DESTROY CHANGES)"), -1)) == FALSE) {
+ vttidy();
+#if defined(USE_TERMCAP) || defined(USE_TERMINFO) || defined(VMS)
+ kbdestroy(kbesc);
+#endif
+ exit(0);
+ }
+
+ if(s == TRUE){
+ if(filewrite(0,1) == TRUE)
+ wquit(1, 0);
+ }
+ else if(s == ABORT){
+ emlwrite(_("Exit cancelled"), NULL);
+ if(term.t_mrow == 0)
+ curwp->w_flag |= WFHARD; /* cause bottom 3 lines to paint */
+ }
+ return(s);
+ }
+
+ return(FALSE);
+}
+
+
+/*
+ * Has any editing been done to headers?
+ */
+int
+any_header_changes(void)
+{
+ struct headerentry *he;
+
+ for(he = Pmaster->headents; he->name != NULL; he++)
+ if(he->dirty)
+ break;
+
+ return(he->name && he->dirty);
+}
+
+int
+isquotedspace(LINE *line)
+{
+ int i, was_quote = 0;
+ for(i = 0; i < llength(line); i++){
+ if(lgetc(line, i).c == '>')
+ was_quote = 1;
+ else if(was_quote && lgetc(line, i).c == ' '){
+ if(i+1 < llength(line) && ucs4_isspace(lgetc(line,i+1).c))
+ return 1;
+ else
+ return 0;
+ }
+ else
+ return 0;
+ }
+ return 0;
+}
+
+/*
+ * This function serves two purposes, 1) to strip white space when
+ * Pmaster asks that the composition have its trailing white space
+ * stripped, or 2) to prepare the text as flowed text, as Pmaster
+ * is telling us that we're working with flowed text.
+ *
+ * What flowed currently means to us is stripping all trailing white
+ * space, except for one space if the following line is a continuation
+ * of the paragraph. Also, we space-stuff all lines beginning
+ * with white-space, and leave siglines alone.
+ */
+int
+cleanwhitespace(void)
+{
+ LINE *cursor_dotp = NULL, **lp = NULL;
+ int i = 0, cursor_doto = 0, is_cursor_line = 0;
+ int do_space_stuffing = 0;
+
+ if(Pmaster && Pmaster->allow_flowed_text && !(*Pmaster->user_says_noflow)())
+ do_space_stuffing++;
+
+ cursor_dotp = curwp->w_dotp;
+ cursor_doto = curwp->w_doto;
+ gotobob(FALSE, 1);
+
+ for(lp = &curwp->w_dotp; (*lp) != curbp->b_linep; (*lp) = lforw(*lp)){
+ if(!(llength(*lp) == 3
+ && lgetc(*lp, 0).c == '-'
+ && lgetc(*lp, 1).c == '-'
+ && lgetc(*lp, 2).c == ' ')
+ && llength(*lp)){
+ is_cursor_line = (cursor_dotp == (*lp));
+ /* trim trailing whitespace, to be added back if flowing */
+ for(i = llength(*lp); i; i--)
+ if(!ucs4_isspace(lgetc(*lp, i - 1).c))
+ break;
+ if(i != llength(*lp)){
+ int flow_line = 0;
+
+ if(Pmaster && !Pmaster->strip_ws_before_send
+ && lforw(*lp) != curbp->b_linep
+ && llength(lforw(*lp))
+ && !(ucs4_isspace(lgetc(lforw(*lp), 0).c)
+ || isquotedspace(lforw(*lp)))
+ && !(llength(lforw(*lp)) == 3
+ && lgetc(lforw(*lp), 0).c == '-'
+ && lgetc(lforw(*lp), 1).c == '-'
+ && lgetc(lforw(*lp), 2).c == ' '))
+ flow_line = 1;
+ if(flow_line && i && lgetc(*lp, i).c == ' '){
+ /* flowed line ending with space */
+ i++;
+ if(i != llength(*lp)){
+ curwp->w_doto = i;
+ ldelete(llength(*lp) - i, NULL);
+ }
+ }
+ else if(flow_line && i && ucs4_isspace(lgetc(*lp, i).c)){
+ /* flowed line ending with whitespace other than space*/
+ curwp->w_doto = i;
+ ldelete(llength(*lp) - i, NULL);
+ linsert(1, ' ');
+ }
+ else{
+ curwp->w_doto = i;
+ ldelete(llength(*lp) - i, NULL);
+ }
+ }
+ if(do_space_stuffing && llength(*lp) && ucs4_isspace(lgetc(*lp, 0).c)){
+ /* space-stuff only if flowed */
+ if(Pmaster)
+ Pmaster->space_stuffed = 1;
+ curwp->w_doto = 0;
+ if(is_cursor_line && cursor_doto)
+ cursor_doto++;
+ linsert(1, ' ');
+ }
+ if(is_cursor_line)
+ cursor_dotp = (*lp);
+ }
+ }
+
+ /* put the cursor back where we found it */
+ gotobob(FALSE, 1);
+ curwp->w_dotp = cursor_dotp;
+ curwp->w_doto = (cursor_doto < llength(curwp->w_dotp))
+ ? cursor_doto : llength(curwp->w_dotp) - 1;
+
+ return(0);
+}
+
+/*
+ * Remove all trailing white space from the text
+ */
+int
+stripwhitespace(void)
+{
+ int i;
+ LINE *cur_line = lforw(curbp->b_linep);
+
+ do{
+ /* we gotta test for the sigdash case here */
+ if(!(cur_line->l_used == 3 &&
+ lgetc(cur_line, 0).c == '-' &&
+ lgetc(cur_line, 1).c == '-' &&
+ lgetc(cur_line, 2).c == ' '))
+ for(i = cur_line->l_used - 1; i >= 0; i--)
+ if(ucs4_isspace(lgetc(cur_line, i).c))
+ cur_line->l_used--;
+ else
+ break;
+ }while((cur_line = lforw(cur_line)) != curbp->b_linep);
+ return 0;
+}
+
+/*
+ * Abort.
+ * Beep the beeper. Kill off any keyboard macro, etc., that is in progress.
+ * Sometimes called as a routine, to do general aborting of stuff.
+ */
+int
+ctrlg(int f, int n)
+{
+ emlwrite(_("Cancelled"), NULL);
+ return (ABORT);
+}
+
+
+/* tell the user that this command is illegal while we are in
+ * VIEW (read-only) mode
+ */
+int
+rdonly(void)
+{
+ (*term.t_beep)();
+ emlwrite("Key illegal in VIEW mode", NULL);
+ return(FALSE);
+}
+
+
+
+/*
+ * reset all globals to their initial values
+ */
+void
+func_init(void)
+{
+ extern int vtrow;
+ extern int vtcol;
+ extern int lbound;
+
+ /*
+ * re-initialize global buffer type variables ....
+ */
+ fillcol = (term.t_ncol > 80) ? 77 : term.t_ncol - 6;
+ sgarbf = TRUE;
+ mpresf = FALSE;
+ mline_open = FALSE;
+ ComposerEditing = FALSE;
+
+ /*
+ * re-initialize hardware display variables ....
+ */
+ vtrow = vtcol = lbound = 0;
+ clearcursor();
+
+ pat[0] = rpat[0] = '\0';
+ browse_dir[0] = '\0';
+}
+
+
+/*
+ * pico_help - help function for standalone composer
+ * Oops - looks like utf8title is unused!
+ *
+ * This should be fixed to handle TAB characters.
+ */
+int
+pico_help(char *utf8text[], char *utf8title, int i)
+{
+ register int numline = 0;
+ char **p;
+
+ p = utf8text;
+ while(*p++ != NULL)
+ numline++;
+
+ return(wscrollw(COMPOSER_TOP_LINE, term.t_nrow-1, utf8text, numline));
+}
+
+
+
+/*
+ * zotedit() - kills the buffer and frees all lines associated with it!!!
+ */
+void
+zotedit(void)
+{
+ wheadp->w_linep = wheadp->w_dotp = wheadp->w_markp = wheadp->w_imarkp = NULL;
+ bheadp->b_linep = bheadp->b_dotp = bheadp->b_markp = NULL;
+
+ free((char *) wheadp); /* clean up window */
+ wheadp = NULL;
+ curwp = NULL;
+
+ free((char *) bheadp); /* clean up buffers */
+ bheadp = NULL;
+ curbp = NULL;
+
+ zotheader(); /* blast header lines */
+
+ kdelete(); /* blast kill buffer */
+
+}
+
+
+#ifdef MOUSE
+/*
+ * Generic mouse handling functions
+ */
+MENUITEM menuitems[12]; /* key labels and functions */
+MENUITEM *mfunc = NULL; /* list of regional functions */
+mousehandler_t mtrack; /* mouse tracking handler */
+
+/* last mouse position */
+static unsigned long levent = 0L;
+static int lrow = 0, lcol = 0, doubleclick, lbutton, lflags;
+#ifdef DOS
+static clock_t lastcalled = 0;
+#else
+static time_t lastcalled = 0;
+#endif
+static mousehandler_t lastf;
+
+
+/*
+ * register_mfunc - register the given function to get called
+ * on mouse events in the given display region
+ */
+int
+register_mfunc(mousehandler_t f, int tlr, int tlc, int brr, int brc)
+{
+ MENUITEM **mp;
+
+ if(!mouseexist())
+ return(FALSE);
+
+ for(mp = &mfunc; *mp; mp = &(*mp)->next)
+ ;
+
+ *mp = (MENUITEM *)malloc(sizeof(MENUITEM));
+ memset(*mp, 0, sizeof(MENUITEM));
+
+ (*mp)->action = f;
+ (*mp)->tl.r = tlr;
+ (*mp)->br.r = brr;
+ (*mp)->tl.c = tlc;
+ (*mp)->br.c = brc;
+ (*mp)->lbl.c = (*mp)->lbl.r = 0;
+ (*mp)->label = "";
+ return(TRUE);
+}
+
+
+/*
+ * clear_mfunc - clear any previously set mouse function
+ */
+void
+clear_mfunc(mousehandler_t f)
+{
+ MENUITEM *mp, *tp;
+
+ if((mp = mfunc) != NULL){
+ if(mp->action == f)
+ mfunc = mp->next;
+ else
+ for(tp = mp; tp->next; tp = tp->next)
+ if(tp->next->action == f){
+ mp = tp->next;
+ tp->next = tp->next->next;
+ break;
+ }
+
+ if(mp){
+ mp->action = NULL;
+ free(mp);
+ }
+ }
+}
+
+
+
+#ifdef EX_MOUSE
+
+void
+clear_mtrack(void)
+{
+ mtrack = NULL;
+ mswin_allowmousetrack (FALSE);
+}
+
+
+void
+register_mtrack(mousehandler_t f)
+{
+ if (f) {
+ mtrack = f;
+ mswin_allowmousetrack (TRUE);
+ }
+ else
+ clear_mtrack ();
+}
+
+
+static void
+move_dot_to(int row, int col)
+{
+ LINE *lp;
+ int i;
+
+ lp = curwp->w_linep;
+ i = row - ((Pmaster) ? ComposerTopLine : 2);
+ while(i-- && lp != curbp->b_linep) /* count from top */
+ lp = lforw(lp);
+ curgoal = col;
+ curwp->w_dotp = lp; /* to new dot. */
+ curwp->w_doto = getgoal(lp);
+ curwp->w_flag |= WFMOVE;
+}
+
+
+/*
+ * mouse_in_pico
+ *
+ * When the mouse goes down in the body we set the mark and start
+ * tracking.
+ *
+ * As the mouse moves we update the dot and redraw the screen.
+ *
+ * If the mouse moves above or below the pico body region of the
+ * screen we scroll the text and update the dot position.
+ *
+ * When the mouse comes up we clean up. If the mouse did not
+ * move, then we clear the mark and turn off the selection.
+ *
+ * Most of the mouse processing is handled here. The exception is
+ * mouse down in the header. Can't call HeaderEditor() from here so
+ * we send up the KEY_MOUSE character, which gets dispatched to
+ * mousepress(), which _can_ call HeaderEditor().
+ */
+unsigned long
+mouse_in_pico(unsigned long mevent, int row, int col, int button, int flags)
+{
+ unsigned long rv = 0; /* Our return value. */
+ int trow, tcol; /* translated row and col. */
+
+ static int lheader = FALSE; /* Mouse down was in header. */
+
+
+ /*
+ * What's up.
+ */
+ switch (mevent) {
+ case M_EVENT_DOWN:
+ if(button != M_BUTTON_LEFT)
+ break;
+
+ /* Ignore mouse down if not in pico body region. */
+ if (row < 2 || row > term.t_nrow - (term.t_mrow+1)) {
+ clear_mtrack ();
+ break;
+ }
+
+ /* Detect double clicks. Not that we do anything with em, just
+ * detect them. */
+#ifdef DOS
+#ifdef CLOCKS_PER_SEC
+ doubleclick = (lrow == row && lcol == col
+ && clock() < (lastcalled + CLOCKS_PER_SEC/2));
+#else
+#ifdef CLK_TCK
+doubleclick = (lrow == row && lcol == col
+ && clock() < (lastcalled + CLK_TCK/2));
+#else
+ doubleclick = FALSE;
+#endif
+#endif
+ lastcalled = clock();
+#else
+ doubleclick = (lrow == row && lcol == col
+ && time(0) < (lastcalled + 2));
+ lastcalled = time(0);
+#endif
+ lheader = FALSE; /* Rember mouse down position. */
+ levent = mevent;
+ lrow = row;
+ lcol = col;
+ lbutton = button;
+ lflags = flags;
+
+ /* Mouse down in body? */
+ if (row < (Pmaster ? ComposerTopLine : 2)) {
+ /* Mouse down in message header -> no tracking, just remember
+ * where */
+ lheader = TRUE;
+ }
+ else {
+ /* Mouse down in message.
+ * If no shift key and an existing mark -> clear the mark.
+ * If shift key and no existing mark -> set mark before moving */
+ if (!(flags & M_KEY_SHIFT) && curwp->w_markp)
+ setmark (0,1); /* this clears the mark. */
+ else if (flags & M_KEY_SHIFT && !curwp->w_markp)
+ setmark (0,1); /* while this sets the mark. */
+
+ /* Reposition dot to mouse down. */
+ move_dot_to (row, col);
+
+ /* Set the mark to dot if no existing mark. */
+ if (curwp->w_markp == NULL)
+ setmark (0,1);
+
+ /* Track mouse movement. */
+ register_mtrack (mouse_in_pico);
+ update ();
+ lheader = FALSE; /* Just to be sure. */
+ }
+ break;
+
+
+ case M_EVENT_TRACK:
+ /* Mouse tracking. */
+ if (lheader) /* Ignore mouse movement in header. */
+ break;
+
+ /* If above or below body, scroll body and adjust the row and col. */
+ if (row < (Pmaster ? ComposerTopLine : 2)) {
+ /* Scroll text down screen and move dot to top left corner. */
+ scrollupline (0,1);
+ trow = (Pmaster) ? ComposerTopLine : 2;
+ tcol = 0;
+ }
+ else if (row > term.t_nrow - (term.t_mrow + 1)) {
+ /* Scroll text up screen and move dot to bottom right corner. */
+ scrolldownline (0,1);
+ trow = term.t_nrow - (term.t_mrow + 1);
+ tcol = term.t_ncol;
+ }
+ else {
+ trow = row;
+ tcol = col;
+ }
+
+ /* Move dot to target column. */
+ move_dot_to (trow, tcol);
+
+ /* Update screen. */
+ update ();
+ break;
+
+
+ case M_EVENT_UP:
+ if(button == M_BUTTON_RIGHT){
+#ifdef _WINDOWS
+ pico_popup();
+#endif
+ break;
+ }
+ else if(button != M_BUTTON_LEFT)
+ break;
+
+ if (lheader) {
+ lheader = FALSE;
+ /* Last down in header. */
+ if (row == lrow && col == lcol) {
+ /* Mouse up and down in same place in header. Means the
+ * user want to edit the header. Return KEY_MOUSE which
+ * will cause mousepress to be called, which will
+ * call HeaderEditor. Can't call HeaderEditor from here
+ * because that would mess up layering. */
+ if (curwp->w_marko)
+ setmark (0,1);
+ rv = (unsigned long) KEY_MOUSE;
+ }
+ }
+ else {
+ /* If up at same place, clear mark */
+ if (curwp->w_markp == curwp->w_dotp &&
+ curwp->w_marko == curwp->w_doto) {
+ setmark (0,1);
+ curwp->w_flag |= WFMOVE;
+ }
+ clear_mtrack ();
+ update ();
+ }
+ break;
+ }
+
+ return(rv);
+}
+#endif
+
+
+
+/*
+ * mouse_in_content - general mechanism used to pass recognized mouse
+ * events in predefined region back thru the usual
+ * keyboard input stream. The actual return value
+ * passed back from this function is set dynamically
+ * via the "down" argument which is read when both the
+ * "row" and "col" arguments are negative.
+ */
+unsigned long
+mouse_in_content(unsigned long mevent, int row, int col, int button, int flags)
+{
+ unsigned long rv = 0;
+ static unsigned long mouse_val = KEY_MOUSE;
+
+ if(row == -1 && col == -1){
+ mouse_val = mevent; /* setting return value */
+ }
+ else {
+ /* A real event. */
+ levent = mevent;
+ switch (mevent) {
+ case M_EVENT_DOWN:
+ /* Mouse down does not mean anything, just keep track of
+ * where it went down and if this is a double click. */
+#ifdef DOS
+#ifdef CLOCKS_PER_SEC
+ doubleclick = (lrow == row && lcol == col
+ && clock() < (lastcalled + CLOCKS_PER_SEC/2));
+#else
+#ifdef CLK_TCK
+ doubleclick = (lrow == row && lcol == col
+ && clock() < (lastcalled + CLK_TCK/2));
+#else
+ doubleclick = FALSE;
+#endif
+#endif
+ lastcalled = clock();
+#else
+ doubleclick = (lrow == row && lcol == col
+ && time(0) < (lastcalled + 2));
+ lastcalled = time(0);
+#endif
+ lrow = row;
+ lcol = col;
+ lbutton = button;
+ lflags = flags;
+ break;
+
+ case M_EVENT_UP:
+ /* Mouse up. If in the same position as it went down
+ * then we return the value set above, which goes into
+ * the character input stream, which gets processed as
+ * a mouse event by some upper layer, which calls to
+ * mouse_get_last(). */
+ if (lrow == row && lcol == col) {
+ rv = mouse_val;
+ }
+ break;
+
+ case M_EVENT_TRACK:
+ break;
+ }
+ }
+
+ return(rv);
+}
+
+
+/*
+ * mouse_get_last - Get last mouse event.
+ *
+ */
+void
+mouse_get_last(mousehandler_t *f, MOUSEPRESS *mp)
+{
+ if (f != NULL)
+ *f = lastf;
+ if (mp != NULL) {
+ mp->mevent = levent;
+ mp->row = lrow;
+ mp->col = lcol;
+ mp->doubleclick = doubleclick;
+ mp->button = lbutton;
+ mp->flags = lflags;
+ }
+}
+
+
+
+/*
+ * register_key - register the given keystroke to accept mouse events
+ */
+void
+register_key(int i, unsigned rval, char *label, void (*label_printer)(),
+ int row, int col, int len, COLOR_PAIR *kn, COLOR_PAIR *kl)
+{
+ if(i > 11)
+ return;
+
+ menuitems[i].val = rval;
+ menuitems[i].tl.r = menuitems[i].br.r = row;
+ menuitems[i].tl.c = col;
+ menuitems[i].br.c = col + len;
+ menuitems[i].lbl.r = menuitems[i].tl.r;
+ menuitems[i].lbl.c = menuitems[i].tl.c;
+ menuitems[i].label_hiliter = label_printer;
+ if(menuitems[i].label){
+ free(menuitems[i].label);
+ menuitems[i].label = NULL;
+ }
+ if(menuitems[i].kncp)
+ free_color_pair(&menuitems[i].kncp);
+ if(menuitems[i].klcp)
+ free_color_pair(&menuitems[i].klcp);
+ if(kn)
+ menuitems[i].kncp = new_color_pair(kn->fg, kn->bg);
+ else
+ menuitems[i].kncp = NULL;
+ if(kl)
+ menuitems[i].klcp = new_color_pair(kl->fg, kl->bg);
+ else
+ menuitems[i].klcp = NULL;
+
+ if(label){
+ size_t len;
+
+ len = strlen(label);
+ if((menuitems[i].label = (char *)malloc((len+1)*sizeof(char))) != NULL){
+ strncpy(menuitems[i].label, label, len);
+ menuitems[i].label[len] = '\0';
+ }
+ }
+}
+
+
+int
+mouse_on_key(int row, int col)
+{
+ int i;
+
+ for(i = 0; i < 12; i++)
+ if(M_ACTIVE(row, col, &menuitems[i]))
+ return(TRUE);
+
+ return(FALSE);
+}
+#endif /* MOUSE */
+
+
+/*
+ * Below are functions for use outside pico to manipulate text
+ * in a pico's native format (circular linked list of lines).
+ *
+ * The idea is to streamline pico use by making it fairly easy
+ * for outside programs to prepare text intended for pico's use.
+ * The simple char * alternative is messy as it requires two copies
+ * of the same text, and isn't very economic in limited memory
+ * situations (THANKS BELLEVUE-BILLY.).
+ */
+typedef struct picotext {
+ LINE *linep;
+ LINE *dotp;
+ int doto;
+ short crinread;
+} PICOTEXT;
+
+#define PT(X) ((PICOTEXT *)(X))
+
+/*
+ * pico_get - return window struct pointer used as a handle
+ * to the other pico_xxx routines.
+ */
+void *
+pico_get(void)
+{
+ PICOTEXT *wp = NULL;
+ LINE *lp = NULL;
+
+ if((wp = (PICOTEXT *)malloc(sizeof(PICOTEXT))) != NULL){
+ wp->crinread = 0;
+ if((lp = lalloc(0)) == NULL){
+ free(wp);
+ return(NULL);
+ }
+
+ wp->dotp = wp->linep = lp->l_fp = lp->l_bp = lp;
+ wp->doto = 0;
+ }
+ else
+ emlwrite("Can't allocate space for text", NULL);
+
+ return((void *)wp);
+}
+
+/*
+ * pico_give - free resources and give up picotext struct
+ */
+void
+pico_give(void *w)
+{
+ register LINE *lp;
+ register LINE *fp;
+
+ fp = lforw(PT(w)->linep);
+ while((lp = fp) != PT(w)->linep){
+ fp = lforw(lp);
+ free(lp);
+ }
+ free(PT(w)->linep);
+ free((PICOTEXT *)w);
+}
+
+/*
+ * pico_readc - return char at current point. Up to calling routines
+ * to keep cumulative count of chars.
+ * The characters in PT are UCS-4 characters. The caller
+ * of pico_readc is expecting UTF-8 chars. We convert
+ * each UCS-4 character to a string of UTF-8 characters
+ * and return them one at a time.
+ */
+int
+pico_readc(void *w, unsigned char *c, int flags)
+{
+ int rv = 0;
+ UCS ucs;
+ static unsigned char obuf[6];
+ static unsigned char *obufpend = obuf;
+ static unsigned char *obufpnext = obuf;
+
+ if(!(flags & PICOREADC_NOUCS)){
+ if(obufpend > obuf){
+ *c = *obufpnext++;
+ rv++;
+ if(obufpnext >= obufpend){
+ obufpend = obuf;
+ obufpnext = obuf;
+ }
+
+ return(rv);
+ }
+ }
+
+ if(PT(w)->crinread){
+ *c = '\012'; /* return LF */
+ PT(w)->crinread = 0;
+ rv++;
+ }
+ else if(PT(w)->doto < llength(PT(w)->dotp)){ /* normal char to return */
+ if(flags & PICOREADC_NOUCS){
+ *c = (unsigned char) lgetc(PT(w)->dotp, (PT(w)->doto)++).c;
+ rv++;
+ }
+ else{
+ rv++;
+ ucs = lgetc(PT(w)->dotp, (PT(w)->doto)++).c;
+ obufpend = utf8_put(obuf, (unsigned long) ucs);
+ obufpnext = obuf;
+ if(obufpend > obuf){
+ *c = *obufpnext++;
+ if(obufpnext >= obufpend){
+ obufpend = obuf;
+ obufpnext = obuf;
+ }
+ }
+ else
+ *c = '?';
+ }
+ }
+ else if(PT(w)->dotp != PT(w)->linep){ /* return line break */
+ PT(w)->dotp = lforw(PT(w)->dotp);
+ PT(w)->doto = 0;
+#if defined(DOS) || defined(OS2)
+ *c = '\015';
+ PT(w)->crinread++;
+#else
+ *c = '\012'; /* return local eol! */
+#endif
+ rv++;
+ } /* else no chars to return */
+
+ return(rv);
+}
+
+
+/*
+ * pico_writec - write a char into picotext and advance pointers.
+ * Up to calling routines to keep track of total chars
+ * written.
+ * Incoming text (c) is UTF-8 and written chars are UCS-4.
+ * We need to collect up multiple chars to make a single
+ * UCS-4 char, so there needs to be some state between calls.
+ */
+int
+pico_writec(void *w, int c, int flags)
+{
+ int rv = 0;
+
+ if(c == '\r') /* ignore CR's */
+ rv++; /* so fake it */
+ else if(c == '\n'){ /* insert newlines on LF */
+ /*
+ * OK, if there are characters on the current line or
+ * dotp is pointing to the delimiter line, insert a newline
+ * No here's the tricky bit; preserve the implicit EOF newline.
+ */
+ if(lforw(PT(w)->dotp) == PT(w)->linep && PT(w)->dotp != PT(w)->linep){
+ PT(w)->dotp = PT(w)->linep;
+ PT(w)->doto = 0;
+ }
+ else{
+ register LINE *lp;
+
+ if((lp = lalloc(0)) == NULL){
+ emlwrite("Can't allocate space for more characters",NULL);
+ return(0);
+ }
+
+ if(PT(w)->dotp == PT(w)->linep){
+ lforw(lp) = PT(w)->linep;
+ lback(lp) = lback(PT(w)->linep);
+ lforw(lback(lp)) = lback(PT(w)->linep) = lp;
+ }
+ else{
+ lforw(lp) = lforw(PT(w)->dotp);
+ lback(lp) = PT(w)->dotp;
+ lback(lforw(lp)) = lforw(PT(w)->dotp) = lp;
+ PT(w)->dotp = lp;
+ PT(w)->doto = 0;
+ }
+ }
+
+ rv++;
+ }
+ else{
+ if(flags & PICOREADC_NOUCS){
+ /*
+ * With this flag we're reverting to the old behavior where no
+ * UTF-8 to UCS-4 translation takes place. We assume nothing
+ * about the incoming byte stream. We just write a byte at a
+ * time. So even though it's being stored in PT each
+ * item is really limited to a single octet value.
+ */
+ rv = geninsert(&PT(w)->dotp, &PT(w)->doto, PT(w)->linep, c, 0, 1, NULL);
+ }
+ else{
+ static unsigned char cbuf[6];
+ static unsigned char *cbufp = cbuf;
+ UCS obuf[MAX(MB_LEN_MAX,32)];
+ int i, outchars = 0;
+
+ if(cbufp < cbuf+sizeof(cbuf)){
+ unsigned char *inputp;
+ unsigned long remaining_octets;
+ UCS ucs;
+
+ *cbufp++ = (unsigned char) c;
+ inputp = cbuf;
+ remaining_octets = (cbufp - cbuf) * sizeof(unsigned char);
+ ucs = (UCS) utf8_get(&inputp, &remaining_octets);
+
+ switch(ucs){
+ case U8G_ENDSTRG: /* incomplete character, wait */
+ case U8G_ENDSTRI: /* incomplete character, wait */
+ break;
+
+ default:
+ if(ucs & U8G_ERROR || ucs == UBOGON){
+ /*
+ * None of these cases is supposed to happen. If it
+ * does happen then the input stream isn't UTF-8
+ * so something is wrong. Treat each character in the
+ * input buffer as a separate error character and
+ * print a '?' for each.
+ */
+ for(inputp = cbuf; inputp < cbufp; inputp++)
+ obuf[outchars++] = '?';
+
+ cbufp = cbuf;
+ }
+ else{
+ /* got a character */
+ if(ucs >= 0x80 && wcellwidth(ucs) < 0){
+ /*
+ * This happens when we have a UTF-8 character that
+ * we aren't able to print in our locale. For example,
+ * if the locale is setup with the terminal
+ * expecting ISO-8859-1 characters then there are
+ * lots of UTF-8 characters that can't be printed.
+ * Print a '?' instead.
+ This may be the wrong thing to do. What happens if user
+ is just forwarding and doesn't edit. We are going to lose
+ the original value, aren't we? Maybe we do this only
+ when printing to the screen instead.
+ */
+ obuf[outchars++] = '?';
+ }
+ else{
+ /*
+ * A regular ucs character
+ */
+ obuf[outchars++] = ucs;
+ }
+
+ /* update the input buffer */
+ if(inputp >= cbufp) /* this should be the case */
+ cbufp = cbuf;
+ else{ /* extra chars for some reason? */
+ unsigned char *q, *newcbufp;
+
+ newcbufp = (cbufp - inputp) + cbuf;
+ q = cbuf;
+ while(inputp < cbufp)
+ *q++ = *inputp++;
+
+ cbufp = newcbufp;
+ }
+ }
+
+ break;
+ }
+ }
+ else{ /* error */
+ obuf[0] = '?';
+ outchars = 1;
+ cbufp = cbuf; /* start over */
+ }
+
+ /*
+ * Unless we have trouble translating outchars will be
+ * either 1 or 0. It is the UCS-4 character that we converted
+ * the input to. It's an array just so it can hold ? when
+ * there is trouble.
+ */
+ rv = 1;
+ for(i = 0; rv && i < outchars; i++)
+ if(!geninsert(&PT(w)->dotp, &PT(w)->doto, PT(w)->linep, obuf[i], 0, 1, NULL))
+ rv = 0;
+ }
+ }
+
+ /*
+ * Warning, this is no longer number written, because when we have a
+ * multibyte UTF-8 character we won't write anything until we get all
+ * the bytes.
+ */
+ return((rv) ? 1 : 0); /* return number written */
+}
+
+
+/*
+ * pico_puts - just write the given string into the text
+ */
+int
+pico_puts(void *w, char *s, int flags)
+{
+ int rv = 0;
+
+ if(*s != '\0'){
+ rv = 1;
+ while(rv && *s != '\0')
+ if(!pico_writec(w, (int)*s++, flags))
+ rv = 0;
+ }
+
+ return((rv) ? 1 : 0);
+}
+
+
+/*
+ * pico_seek - position dotp and dot at requested location
+ */
+int
+pico_seek(void *w, long offset, int orig)
+{
+ register LINE *lp;
+
+ PT(w)->crinread = 0;
+ switch(orig){
+ case 0 : /* SEEK_SET */
+ PT(w)->dotp = lforw(PT(w)->linep);
+ PT(w)->doto = 0;
+ case 1 : /* SEEK_CUR */
+ lp = PT(w)->dotp;
+ while(lp != PT(w)->linep){
+ if(offset <= llength(lp)){
+ PT(w)->doto = (int)offset;
+ PT(w)->dotp = lp;
+ break;
+ }
+
+ offset -= ((long)llength(lp)
+#if defined(DOS) || defined(OS2)
+ + 2L);
+#else
+ + 1L);
+#endif
+ lp = lforw(lp);
+ }
+ break;
+
+ case 2 : /* SEEK_END */
+ PT(w)->dotp = lback(PT(w)->linep);
+ PT(w)->doto = llength(PT(w)->dotp);
+ break;
+ default :
+ return(-1);
+ }
+
+ return(0);
+}
+
+
+/*
+ * breplace - replace the current window's text with the given
+ * LINEs
+ */
+void
+breplace(void *w)
+{
+ register LINE *lp;
+ register LINE *fp;
+
+ fp = lforw(curbp->b_linep);
+ while((lp = fp) != curbp->b_linep){ /* blast old lines */
+ fp = lforw(lp);
+ free(lp);
+ }
+ free(curbp->b_linep);
+
+ curbp->b_linep = PT(w)->linep; /* arrange pointers */
+
+ /*
+ * Undo space-stuffing that was done when we were preparing to send.
+ * Some error happened so we're back to the composer.
+ */
+ if(Pmaster && Pmaster->space_stuffed){
+ Pmaster->space_stuffed = 0;
+ for(lp = lforw(curbp->b_linep); lp != curbp->b_linep; lp = lforw(lp)){
+ if(llength(lp) && ucs4_isspace(lgetc(lp, 0).c)){
+ curwp->w_dotp = lp;
+ curwp->w_doto = 0;
+ forwdel(FALSE,1);
+ }
+ }
+ }
+
+ curwp->w_linep = lforw(curbp->b_linep);
+ curwp->w_dotp = lforw(curbp->b_linep);
+ curwp->w_doto = 0;
+ curwp->w_markp = curwp->w_imarkp = NULL;
+ curwp->w_marko = curwp->w_imarko = 0;
+
+ curbp->b_dotp = curwp->w_dotp;
+ curbp->b_doto = curbp->b_marko = 0;
+ curbp->b_markp = NULL;
+ curbp->b_linecnt = -1;
+
+ curwp->w_flag |= WFHARD;
+}
+
+
+#ifdef _WINDOWS
+/*
+ *
+ */
+int
+composer_file_drop(int x, int y, char *filename)
+{
+ int attached = 0;
+ EML eml;
+
+ if((ComposerTopLine > 2 && x <= ComposerTopLine)
+ || !LikelyASCII(filename)){
+ AppendAttachment(filename, NULL, NULL);
+ attached++;
+ }
+ else{
+ setimark(FALSE, 1);
+ ifile(filename);
+ swapimark(FALSE, 1);
+ }
+
+ if(ComposerEditing){ /* update display */
+ PaintBody(0);
+ }
+ else{
+ pico_refresh(0, 1);
+ update();
+ }
+
+ eml.s = filename;
+ if(attached)
+ emlwrite(_("Attached dropped file \"%s\""), &eml);
+ else
+ emlwrite(_("Inserted dropped file \"%s\""), &eml);
+
+ if(ComposerEditing){ /* restore cursor */
+ HeaderPaintCursor();
+ }
+ else{
+ curwp->w_flag |= WFHARD;
+ update();
+ }
+
+ return(1);
+}
+
+
+int
+pico_cursor(int col, long row)
+{
+ return((row > 1 && row <= term.t_nrow - (term.t_mrow + 1))
+ ? MSWIN_CURSOR_IBEAM
+ : MSWIN_CURSOR_ARROW);
+}
+#endif /* _WINDOWS */