home *** CD-ROM | disk | FTP | other *** search
/ ftp.uv.es / 2014.11.ftp.uv.es.tar / ftp.uv.es / pub / unix / pine4.10.tar.gz / pine4.10.tar / pine4.10 / pico / pico.c < prev    next >
C/C++ Source or Header  |  1998-12-15  |  36KB  |  1,558 lines

  1. #if    !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: pico.c,v 4.118 1998/12/16 03:25:57 mikes Exp $";
  3. #endif
  4. /*
  5.  * Program:    Main Pine Composer routines
  6.  *
  7.  *
  8.  * Michael Seibel
  9.  * Networks and Distributed Computing
  10.  * Computing and Communications
  11.  * University of Washington
  12.  * Administration Builiding, AG-44
  13.  * Seattle, Washington, 98195, USA
  14.  * Internet: mikes@cac.washington.edu
  15.  *
  16.  * Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  17.  *
  18.  *
  19.  * Pine and Pico are registered trademarks of the University of Washington.
  20.  * No commercial use of these trademarks may be made without prior written
  21.  * permission of the University of Washington.
  22.  * 
  23.  * Pine, Pico, and Pilot software and its included text are Copyright
  24.  * 1989-1998 by the University of Washington.
  25.  * 
  26.  * The full text of our legal notices is contained in the file called
  27.  * CPYRIGHT, included with this distribution.
  28.  *
  29.  *
  30.  * WEEMACS/PICO NOTES:
  31.  *
  32.  * 01 Nov 89 - MicroEmacs 3.6 vastly pared down and becomes a function call 
  33.  *            weemacs() and plugged into the Pine mailer.  Lots of unused
  34.  *           MicroEmacs code laying around.
  35.  *
  36.  * 17 Jan 90 - weemacs() became weemacs() the composer.  Header editing
  37.  *           functions added.
  38.  *
  39.  * 09 Sep 91 - weemacs() renamed pico() for the PIne COmposer.
  40.  *
  41.  */
  42.  
  43.  
  44. /*
  45.  * This program is in public domain; written by Dave G. Conroy.
  46.  * This file contains the main driving routine, and some keyboard processing
  47.  * code, for the MicroEMACS screen editor.
  48.  *
  49.  * REVISION HISTORY:
  50.  *
  51.  * 1.0  Steve Wilhite, 30-Nov-85
  52.  *      - Removed the old LK201 and VT100 logic. Added code to support the
  53.  *        DEC Rainbow keyboard (which is a LK201 layout) using the the Level
  54.  *        1 Console In ROM INT. See "rainbow.h" for the function key defs
  55.  *      Steve Wilhite, 1-Dec-85
  56.  *      - massive cleanup on code in display.c and search.c
  57.  *
  58.  * 2.0  George Jones, 12-Dec-85
  59.  *      - Ported to Amiga.
  60.  *
  61.  * 3.0  Daniel Lawrence, 29-Dec-85
  62.  *    16-apr-86
  63.  *    - updated documentation and froze development for 3.6 net release
  64.  */
  65.  
  66. /* make global definitions not external */
  67. #define    maindef
  68.  
  69. #include    "headers.h"
  70. #include    "ebind.h"    /* default key bindings */
  71.  
  72.  
  73. #ifdef    ANSI
  74.     int  func_init(void);
  75.     void breplace(void *);
  76.     int  any_header_changes(void);
  77. #ifdef    _WINDOWS
  78.     int    composer_file_drop(int, int, char *);
  79. #endif
  80. #else
  81.     int  func_init();
  82.     void breplace();
  83.     int  any_header_changes();
  84. #endif
  85.  
  86.  
  87. /*
  88.  * function key mappings
  89.  */
  90. static int pfkm[12][2] = {
  91.     { F1,  (CTRL|'G')},
  92.     { F2,  (CTRL|'C')},
  93.     { F3,  (CTRL|'X')},
  94.     { F4,  (CTRL|'J')},
  95.     { F5,  (CTRL|'R')},
  96.     { F6,  (CTRL|'W')},
  97.     { F7,  (CTRL|'Y')},
  98.     { F8,  (CTRL|'V')},
  99.     { F9,  (CTRL|'K')},
  100.     { F10, (CTRL|'U')},
  101.     { F11, (CTRL|'O')},
  102. #ifdef    SPELLER
  103.     { F12, (CTRL|'T')}
  104. #else
  105.     { F12, (CTRL|'D')}
  106. #endif
  107. };
  108.  
  109.  
  110. /*
  111.  * flag for the various functions in pico() to set when ready
  112.  * for pico() to return...
  113.  */
  114. int      pico_all_done = 0;
  115. jmp_buf  finstate;
  116. char    *pico_anchor = NULL;
  117. extern struct headerentry *headents;
  118.  
  119. /*
  120.  * pico - the main routine for Pine's composer.
  121.  *
  122.  */
  123. pico(pm)
  124. PICO *pm;
  125. {
  126.     register int    c;
  127.     register int    f;
  128.     register int    n;
  129.     char     bname[NBUFN];        /* buffer name of file to read */
  130.     extern   struct on_display ods;
  131.     int      checkpointcnt = 0, input = 0, cursor_shown;
  132.     char     chkptfile[NLINE];
  133.  
  134.     Pmaster       = pm;
  135.     gmode      = MDWRAP;
  136.     gmode        |= pm->pine_flags;    /* high 4 bits rsv'd for pine */
  137.  
  138.     alt_speller   = pm->alt_spell;
  139.     pico_all_done = 0;
  140.     km_popped     = 0;
  141.  
  142.     if(!vtinit())            /* Init Displays.      */
  143.       return(COMP_CANCEL);
  144.  
  145.     strcpy(bname, "main");        /* default buffer name */
  146.     edinit(bname);            /* Buffers, windows.   */
  147.  
  148.     if(InitMailHeader(pm))        /* init mail header structure */
  149.       gmode &= ~(P_BODY | P_HEADEND);    /* flip off special header stuff */
  150.  
  151.     /* setup to process commands */
  152.     lastflag = 0;            /* Fake last flags.     */
  153.     curbp->b_mode |= gmode;        /* and set default modes*/
  154.  
  155.     pico_anchor = (char *)malloc((strlen(Pmaster->pine_anchor) + 1)
  156.                  * sizeof(char));
  157.     if(pico_anchor)
  158.       strcpy(pico_anchor, Pmaster->pine_anchor);
  159.  
  160.     bindtokey(DEL, (gmode & P_DELRUBS) ? forwdel : backdel);
  161.  
  162.     if(pm->msgtext)
  163.       breplace(pm->msgtext);
  164.  
  165. #ifdef    _WINDOWS
  166.     cursor_shown = mswin_showcursor(1);    /* turn on for main window */
  167.     mswin_allowpaste(MSWIN_PASTE_FULL);
  168.     mswin_setscrollcallback (pico_scroll_callback);
  169. #endif
  170.  
  171.     /* prepare for checkpointing */
  172.     chkptfile[0] = '\0';
  173.     chkptinit((*Pmaster->ckptdir)(chkptfile, NLINE), NLINE);
  174.     if(gmode & P_CHKPTNOW)
  175.       writeout(chkptfile, TRUE);
  176.  
  177.     pico_all_done = setjmp(finstate);    /* jump out of HUP handler ? */
  178.  
  179.     if(gmode & MDALTNOW){
  180.     while(!pico_all_done){
  181.         if(((gmode & P_BODY) || !Pmaster->headents)
  182.            && alt_editor(0, 1) < 0)
  183.           break;            /* if problem, drop into pico */
  184.  
  185.         if(Pmaster->headents){
  186.         update();        /* paint screen, n' start editing... */
  187.         HeaderEditor((gmode & (P_HEADEND | P_BODY)) ? 2 : 0, 0);
  188.         gmode |= P_BODY;    /* make sure we enter alt ed next */
  189.         }
  190.         else
  191.           pico_all_done = COMP_EXIT;
  192.     }
  193.     }
  194.     else if(!pico_all_done){
  195.     if(gmode & P_BODY){        /* begin editing the header? */
  196.         ArrangeHeader();        /* line up pointers */
  197.         /*
  198.          * Move to the offset pine asked us to move to.
  199.          * Perhaps we should be checking to see if this is
  200.          * a reasonable number before moving.
  201.          */
  202.         if(Pmaster && Pmaster->edit_offset)
  203.           forwchar(FALSE, Pmaster->edit_offset);
  204.     }
  205.     else{
  206.         update();            /* paint screen, */
  207.         HeaderEditor((gmode & P_HEADEND) ? 2 : 0, 0);
  208.     }
  209.     }
  210.  
  211.     while(1){
  212.     if(pico_all_done){
  213. #ifdef    _WINDOWS
  214.         if(!cursor_shown)
  215.           mswin_showcursor(0);
  216.  
  217.         mswin_allowpaste(MSWIN_PASTE_DISABLE);
  218.         mswin_setscrollcallback (NULL);
  219. #endif
  220.         c = anycb() ? BUF_CHANGED : 0;
  221.         switch(pico_all_done){    /* prepare for/handle final events */
  222.           case COMP_EXIT :        /* already confirmed */
  223.         packheader();
  224.         c |= COMP_EXIT;
  225.         break;
  226.  
  227.           case COMP_CANCEL :    /* also already confirmed */
  228.         packheader();
  229.         c = COMP_CANCEL;
  230.         break;
  231.  
  232.           case COMP_GOTHUP:
  233.         /* 
  234.          * pack up and let caller know that we've received a SIGHUP
  235.          */
  236.         if(ComposerEditing)        /* expand addr if needed */
  237.           call_builder(&headents[ods.cur_e], NULL, NULL);
  238.  
  239.         packheader();
  240.         c |= COMP_GOTHUP;
  241.         break;
  242.  
  243.           case COMP_SUSPEND :
  244.           default:            /* safest if internal error */
  245.         /*
  246.          * If we're in the headers mark the current header line
  247.          * with start_here bit so caller knows where to reset.
  248.          * Also set the edit_offset, which is either the offset
  249.          * into this header line or the offset into the body.
  250.          * Packheader will adjust edit_offset for multi-line
  251.          * headers.
  252.          */
  253.         if(ComposerEditing){        /* in the headers */
  254.             headents[ods.cur_e].start_here = 1;
  255.             Pmaster->edit_offset = ods.p_off;
  256.         }
  257.         else{
  258.             register LINE *clp;
  259.             register long  offset;
  260.  
  261.             for(clp = lforw(curbp->b_linep), offset = 0L;
  262.             clp != curwp->w_dotp;
  263.             clp = lforw(clp))
  264.               offset += (llength(clp) + 1);
  265.  
  266.             Pmaster->edit_offset = offset + curwp->w_doto;
  267.         }
  268.  
  269.         packheader();
  270.         c |= COMP_SUSPEND;
  271.         break;
  272.         }
  273.  
  274.         free(pico_anchor);
  275.         vttidy();            /* clean up tty modes */
  276.         zotdisplay();        /* blast display buffers */
  277.         zotedit();
  278.         unlink(chkptfile);
  279.         Pmaster = NULL;        /* blat global */
  280.  
  281.         return(c);
  282.     }
  283.  
  284.     if(km_popped){
  285.         km_popped--;
  286.         if(km_popped == 0) /* cause bottom three lines to be repainted */
  287.           curwp->w_flag |= WFHARD;
  288.     }
  289.  
  290.     if(km_popped){  /* temporarily change to cause menu to be painted */
  291.         term.t_mrow = 2;
  292.         curwp->w_ntrows -= 2;
  293.         curwp->w_flag |= WFMODE;
  294.         movecursor(term.t_nrow-2, 0); /* clear status line, too */
  295.         peeol();
  296.     }
  297.  
  298.     update();            /* Fix up the screen    */
  299.     if(km_popped){
  300.         term.t_mrow = 0;
  301.         curwp->w_ntrows += 2;
  302.     }
  303.  
  304. #ifdef    MOUSE
  305. #ifdef  EX_MOUSE
  306.     /* New mouse function for real mouse text seletion. */
  307.     register_mfunc(mouse_in_pico, 2, 0, term.t_nrow - (term.t_mrow+1),
  308.                term.t_ncol);
  309. #else
  310.     mouse_in_content(KEY_MOUSE, -1, -1, -1, 0);
  311.     register_mfunc(mouse_in_content, 2, 0, term.t_nrow - (term.t_mrow + 1),
  312.                term.t_ncol);
  313. #endif
  314. #endif
  315. #ifdef    _WINDOWS
  316.     mswin_setdndcallback (composer_file_drop);
  317. #endif
  318.     c = GetKey();
  319.         if (term.t_nrow < 6 && c != NODATA){
  320.             (*term.t_beep)();
  321.             emlwrite("Please make the screen bigger.", NULL);
  322.             continue;
  323.         }
  324.  
  325. #ifdef    MOUSE
  326. #ifdef  EX_MOUSE
  327.     clear_mfunc(mouse_in_pico);
  328. #else
  329.     clear_mfunc(mouse_in_content);
  330. #endif
  331. #endif
  332. #ifdef    _WINDOWS
  333.     mswin_cleardndcallback ();
  334. #endif
  335.     if(c == NODATA || time_to_check()){    /* new mail ? */
  336.         if((*Pmaster->newmail)(c == NODATA ? 0 : 2, 1) >= 0){
  337.         int rv;
  338.  
  339.         if(km_popped){
  340.             term.t_mrow = 2;
  341.             curwp->w_ntrows -= 2;
  342.             curwp->w_flag |= WFHARD;
  343.             km_popped = 0;
  344.         }
  345.  
  346.         clearcursor();
  347.         mlerase();
  348.         rv = (*Pmaster->showmsg)(c);
  349.         ttresize();
  350.         picosigs();    /* restore altered handlers */
  351.         if(rv)        /* Did showmsg corrupt the display? */
  352.           PaintBody(0);    /* Yes, repaint */
  353.  
  354.         mpresf = 1;
  355.         input = 0;
  356.         }
  357.  
  358.         clearcursor();
  359.         movecursor(0, 0);
  360.     }
  361.  
  362.     if(km_popped)
  363.       switch(c){
  364.         case NODATA:
  365.         case (CTRL|'L'):
  366.           km_popped++;
  367.           break;
  368.         
  369.         default:
  370.           mlerase();
  371.           break;
  372.       }
  373.  
  374.     if(c == NODATA)        /* no op, getkey timed out */
  375.       continue;
  376.     else if(!input++)
  377.       (*Pmaster->keybinput)();
  378.  
  379.     if (mpresf != FALSE) {        /* message stay around only  */
  380.         if (mpresf++ > NMMESSDELAY)    /* so long! */
  381.           mlerase();
  382.     }
  383.  
  384.     f = FALSE;            /* vestigial */
  385.     n = 1;
  386.                     /* Do it.               */
  387.     execute(normalize_cmd(c, pfkm, 2), f, n);
  388.     if(++checkpointcnt >= CHKPTDELAY){
  389.         checkpointcnt = 0;
  390.         writeout(chkptfile, TRUE);
  391.     }
  392.     }
  393. }
  394.  
  395. /*
  396.  * Initialize all of the buffers and windows. The buffer name is passed down
  397.  * as an argument, because the main routine may have been told to read in a
  398.  * file by default, and we want the buffer name to be right.
  399.  */
  400.  
  401. /*
  402.  * For the pine composer, we don't want to take over the whole screen
  403.  * for editing.  the first some odd lines are to be used for message 
  404.  * header information editing.
  405.  */
  406. edinit(bname)
  407. char    bname[];
  408. {
  409.     register BUFFER *bp;
  410.     register WINDOW *wp;
  411.  
  412.     if(Pmaster)
  413.       func_init();
  414.  
  415.     bp = bfind(bname, TRUE, BFWRAPOPEN);    /* First buffer         */
  416.     wp = (WINDOW *) malloc(sizeof(WINDOW)); /* First window         */
  417.  
  418.     if (bp==NULL || wp==NULL){
  419.     if(Pmaster)
  420.       return(0);
  421.     else
  422.       exit(1);
  423.     }
  424.  
  425.     curbp  = bp;                            /* Make this current    */
  426.     wheadp = wp;
  427.     curwp  = wp;
  428.     wp->w_wndp  = NULL;                     /* Initialize window    */
  429.     wp->w_bufp  = bp;
  430.     bp->b_nwnd  = 1;                        /* Displayed.           */
  431.     wp->w_linep = bp->b_linep;
  432.     wp->w_dotp  = bp->b_linep;
  433.     wp->w_doto  = 0;
  434.     wp->w_markp = NULL;
  435.     wp->w_marko = 0;
  436.     bp->b_linecnt = -1;
  437.  
  438.     if(Pmaster){
  439.     term.t_mrow = Pmaster->menu_rows;
  440.     wp->w_toprow = ComposerTopLine = COMPOSER_TOP_LINE;
  441.     wp->w_ntrows = term.t_nrow - COMPOSER_TOP_LINE - term.t_mrow;
  442.     fillcol = Pmaster->fillcolumn;
  443.     strcpy(opertree,
  444.            (Pmaster->oper_dir && strlen(Pmaster->oper_dir) < NLINE)
  445.              ? Pmaster->oper_dir : "");
  446.     }
  447.     else{
  448.     if(sup_keyhelp)
  449.       term.t_mrow = 0;
  450.     else
  451.       term.t_mrow = 2;
  452.  
  453.         wp->w_toprow = 2;
  454.         wp->w_ntrows = term.t_nrow - 2 - term.t_mrow;
  455.     if(userfillcol > 0)            /* set fill column */
  456.       fillcol = userfillcol;
  457.     else
  458.       fillcol = term.t_ncol - 6;
  459.     }
  460.  
  461.     /*
  462.      * MDSCUR mode implies MDTREE mode with a opertree of home directory,
  463.      * unless opertree has been set differently.
  464.      */
  465.     if((gmode & MDSCUR) && !opertree[0])
  466.       strncpy(opertree, gethomedir(NULL), NLINE);
  467.  
  468.     if(*opertree)
  469.       fixpath(opertree, NLINE);
  470.  
  471.     wp->w_force = 0;
  472.     wp->w_flag  = WFMODE|WFHARD;            /* Full.                */
  473. }
  474.  
  475.  
  476. /*
  477.  * This is the general command execution routine. It handles the fake binding
  478.  * of all the keys to "self-insert". It also clears out the "thisflag" word,
  479.  * and arranges to move it to the "lastflag", so that the next command can
  480.  * look at it. Return the status of command.
  481.  */
  482. execute(c, f, n)
  483. int c, f, n;
  484. {
  485.     register KEYTAB *ktp;
  486.     register int    status;
  487.     register int    i;
  488.  
  489.     ktp = (Pmaster) ? &keytab[0] : &pkeytab[0];
  490.  
  491.     while (ktp->k_fp != NULL) {
  492.     if (ktp->k_code == c) {
  493.  
  494.         if(lastflag&CFFILL){
  495.         curwp->w_flag |= WFMODE;
  496.         if(Pmaster == NULL)
  497.           sgarbk = TRUE;
  498.         }
  499.  
  500.         thisflag = 0;
  501.         status   = (*ktp->k_fp)(f, n);
  502.         if((lastflag & CFFILL) && (thisflag ^ CFFILL))
  503.           fdelete();
  504.  
  505.         lastflag = thisflag;
  506.  
  507.         /*
  508.          * Reset flag saying wrap should open a new line whenever
  509.          * we execute a command (as opposed to just typing in text).
  510.          * However, if that command leaves us in the same line on the
  511.          * screen, then don't reset.
  512.          */
  513.         if(curwp->w_flag & (WFMOVE | WFHARD))
  514.           curbp->b_flag |= BFWRAPOPEN;    /* wrap should open new line */
  515.  
  516.         return (status);
  517.     }
  518.     ++ktp;
  519.     }
  520.  
  521.     if(lastflag & CFFILL)        /* blat unusable fill data */
  522.       fdelete();
  523.  
  524.     if (VALID_KEY(c)) {            /* Self inserting.      */
  525.  
  526.     if (n <= 0) {                   /* Fenceposts.          */
  527.         lastflag = 0;
  528.         return (n<0 ? FALSE : TRUE);
  529.     }
  530.     thisflag = 0;                   /* For the future.      */
  531.  
  532.     /* do the appropriate insertion */
  533.     /* pico never does C mode, this is simple */
  534.     status = linsert(n, c);
  535.  
  536.     /*
  537.      * Check to make sure we didn't go off of the screen
  538.      * with that character.  Take into account tab expansion.
  539.      * If so wrap the line...
  540.      */
  541.     if(curwp->w_bufp->b_mode & MDWRAP){
  542.         register int j;
  543.         register int k;
  544.  
  545.         for(i = j = k = 0; j < llength(curwp->w_dotp); j++, k++)
  546.           if(isspace((unsigned char)lgetc(curwp->w_dotp, j).c)){
  547.           if(lgetc(curwp->w_dotp, j).c == TAB)
  548.             while(k+1 & 0x07)
  549.               k++;
  550.           }
  551.           else if(k >= fillcol){
  552.           wrapword();
  553.           break;
  554.           }
  555.     }
  556.  
  557.     lastflag = thisflag;
  558.     return (status);
  559.     }
  560.     
  561.     if(c&CTRL)
  562.       emlwrite("\007Unknown Command: ^%c", (void *)(c&0xff));
  563.     else
  564.       emlwrite("\007Unknown Command", NULL);
  565.  
  566.     lastflag = 0;                           /* Fake last flags.     */
  567.     return (FALSE);
  568. }
  569.  
  570.  
  571.  
  572. /*
  573.  * Fancy quit command, as implemented by Norm. If the any buffer has
  574.  * changed do a write on that buffer and exit emacs, otherwise simply exit.
  575.  */
  576. quickexit(f, n)
  577. int f, n;
  578. {
  579.     register BUFFER *bp;    /* scanning pointer to buffers */
  580.  
  581.     bp = bheadp;
  582.     while (bp != NULL) {
  583.     if ((bp->b_flag&BFCHG) != 0    /* Changed.             */
  584.         && (bp->b_flag&BFTEMP) == 0) {    /* Real.                */
  585.         curbp = bp;        /* make that buffer cur    */
  586.         filesave(f, n);
  587.     }
  588.     bp = bp->b_bufp;            /* on to the next buffer */
  589.     }
  590.     wquit(f, n);                             /* conditionally quit   */
  591. }
  592.  
  593.  
  594.  
  595. /* 
  596.  * abort_composer - ask the question here, then go quit or 
  597.  *                  return FALSE
  598.  */
  599. abort_composer(f, n)
  600. int f, n;
  601. {
  602.     char *result;
  603.  
  604.     result = "";
  605.  
  606.     Pmaster->arm_winch_cleanup++;
  607.     if(Pmaster->canceltest){
  608.         if((((Pmaster->pine_flags & MDHDRONLY) && !any_header_changes())
  609.          || (!(Pmaster->pine_flags & MDHDRONLY) && !anycb()))
  610.       || (result = (*Pmaster->canceltest)(redraw_pico_for_callback))){
  611.         pico_all_done = COMP_CANCEL;
  612.         emlwrite(result, NULL);
  613.         Pmaster->arm_winch_cleanup--;
  614.         return(TRUE);
  615.     }
  616.     else{
  617.         emlwrite("Cancel Cancelled", NULL);
  618.         curwp->w_flag |= WFMODE;        /* and modeline so we  */
  619.         sgarbk = TRUE;            /* redraw the keymenu  */
  620.         pclear(term.t_nrow - 1, term.t_nrow + 1);
  621.         Pmaster->arm_winch_cleanup--;
  622.         return(FALSE);
  623.     }
  624.     }
  625.     else switch(mlyesno(Pmaster->headents
  626.      ? "Cancel message (answering \"Yes\" will abandon your mail message)"
  627.      : (anycb() == FALSE)
  628.          ? "Cancel Edit (and abandon changes)"
  629.          : "Cancel Edit",
  630.      FALSE)){
  631.       case TRUE:
  632.     pico_all_done = COMP_CANCEL;
  633.     return(TRUE);
  634.  
  635.       case ABORT:
  636.     emlwrite("\007Cancel Cancelled", NULL);
  637.     break;
  638.  
  639.       default:
  640.     mlerase();
  641.     }
  642.     return(FALSE);
  643. }
  644.  
  645.  
  646. /*
  647.  * suspend_composer - return to pine with what's been edited so far
  648.  */
  649. suspend_composer(f, n)
  650. int f, n;
  651. {
  652.     if(Pmaster && Pmaster->headents)
  653.       pico_all_done = COMP_SUSPEND;
  654.     else
  655.       (*term.t_beep)();
  656. }
  657.  
  658.  
  659.  
  660. /*
  661.  * Quit command. If an argument, always quit. Otherwise confirm if a buffer
  662.  * has been changed and not written out. Normally bound to "C-X C-C".
  663.  */
  664. wquit(f, n)
  665. int f, n;
  666. {
  667.     register int    s;
  668.  
  669.     if(Pmaster){
  670.     char *prompt, *result;
  671.  
  672.     /* First, make sure there are no outstanding problems */ 
  673.     if(AttachError()){
  674.         emlwrite("\007Problem with attachments!  Fix errors or delete attachments.", NULL);
  675.         return(FALSE);
  676.     }
  677.  
  678.     /*
  679.      * if we're not in header, show some of it as we verify sending...
  680.      */
  681.     display_for_send();
  682.     packheader();
  683.     Pmaster->arm_winch_cleanup++;
  684.     if((!(Pmaster->pine_flags & MDHDRONLY) || any_header_changes())
  685.        && (result = (*Pmaster->exittest)(Pmaster->headents,
  686.                          redraw_pico_for_callback))){
  687.         Pmaster->arm_winch_cleanup--;
  688.         if(sgarbf)
  689.           update();
  690.  
  691.         lchange(WFHARD);            /* set update flags... */
  692.         curwp->w_flag |= WFMODE;        /* and modeline so we  */
  693.         sgarbk = TRUE;            /* redraw the keymenu  */
  694.         pclear(term.t_nrow - 2, term.t_nrow + 1);
  695.         if(*result)
  696.           emlwrite(result, NULL);
  697.     }
  698.     else{
  699.         Pmaster->arm_winch_cleanup--;
  700.         pico_all_done = COMP_EXIT;
  701.         return(TRUE);
  702.     }
  703.     }
  704.     else{
  705.         if (f != FALSE                          /* Argument forces it.  */
  706.         || anycb() == FALSE                     /* All buffers clean.   */
  707.                         /* User says it's OK.   */
  708.         || (s=mlyesno("Save modified buffer (ANSWERING \"No\" WILL DESTROY CHANGES)", -1)) == FALSE) {
  709.                 vttidy();
  710. #if     defined(USE_TERMCAP) || defined(USE_TERMINFO) || defined(VMS)
  711.         kbdestroy(kbesc);
  712. #endif
  713.                 exit(0);
  714.         }
  715.  
  716.     if(s == TRUE){
  717.         if(filewrite(0,1) == TRUE)
  718.           wquit(1, 0);
  719.     }
  720.     else if(s == ABORT){
  721.         emlwrite("Exit cancelled", NULL);
  722.         if(term.t_mrow == 0)
  723.           curwp->w_flag |= WFHARD;    /* cause bottom 3 lines to paint */
  724.     }
  725.         return(s);
  726.     }
  727.  
  728.     return(FALSE);
  729. }
  730.  
  731.  
  732. /*
  733.  * Has any editing been done to headers?
  734.  */
  735. any_header_changes()
  736. {
  737.     struct headerentry *he;
  738.  
  739.     for(he = Pmaster->headents; he->name != NULL; he++)
  740.       if(he->dirty)
  741.     break;
  742.     
  743.     return(he->name && he->dirty);
  744. }
  745.  
  746.  
  747. /*
  748.  * Abort.
  749.  * Beep the beeper. Kill off any keyboard macro, etc., that is in progress.
  750.  * Sometimes called as a routine, to do general aborting of stuff.
  751.  */
  752. ctrlg(f, n)
  753. int f, n;
  754. {
  755.     emlwrite("Cancelled", NULL);
  756.     return (ABORT);
  757. }
  758.  
  759.  
  760. /* tell the user that this command is illegal while we are in
  761.  *  VIEW (read-only) mode
  762.  */
  763. rdonly()
  764. {
  765.     (*term.t_beep)();
  766.     emlwrite("Key illegal in VIEW mode", NULL);
  767.     return(FALSE);
  768. }
  769.  
  770.  
  771.  
  772. /*
  773.  * reset all globals to their initial values
  774.  */
  775. func_init()
  776. {
  777.     extern int    vtrow;
  778.     extern int    vtcol;
  779.     extern int    lbound;
  780.  
  781.     /*
  782.      * re-initialize global buffer type variables ....
  783.      */
  784.     fillcol = (term.t_ncol > 80) ? 77 : term.t_ncol - 6;
  785.     eolexist = TRUE;
  786.     revexist = FALSE;
  787.     sgarbf = TRUE;
  788.     mpresf = FALSE;
  789.     mline_open = FALSE;
  790.     ComposerEditing = FALSE;
  791.  
  792.     /*
  793.      * re-initialize hardware display variables ....
  794.      */
  795.     vtrow = vtcol = lbound = 0;
  796.     clearcursor();
  797.  
  798.     pat[0] = rpat[0] = '\0';
  799. }
  800.  
  801.  
  802. /*
  803.  * pico_help - help function for standalone composer
  804.  */
  805. pico_help(text, title, i)
  806. char *text[], *title;
  807. int i;
  808. {
  809.     register    int numline = 0;
  810.     char        **p;
  811.  
  812.     p = text;
  813.     while(*p++ != NULL) 
  814.       numline++;
  815.     return(wscrollw(COMPOSER_TOP_LINE, term.t_nrow-1, text, numline));
  816. }
  817.  
  818.  
  819.  
  820. /*
  821.  * zotedit() - kills the buffer and frees all lines associated with it!!!
  822.  */
  823. zotedit()
  824. {
  825.     wheadp->w_linep = wheadp->w_dotp = wheadp->w_markp = NULL;
  826.     bheadp->b_linep = bheadp->b_dotp = bheadp->b_markp = NULL;
  827.  
  828.     free((char *) wheadp);            /* clean up window */
  829.     wheadp = NULL;
  830.     curwp  = NULL;
  831.  
  832.     free((char *) bheadp);            /* clean up buffers */
  833.     bheadp = NULL;
  834.     curbp  = NULL;
  835.  
  836.     zotheader();                /* blast header lines */
  837.  
  838.     kdelete();                    /* blast kill buffer */
  839.  
  840. }
  841.  
  842.  
  843. #ifdef    MOUSE
  844. /*
  845.  * Generic mouse handling functions
  846.  */
  847. MENUITEM  menuitems[12];        /* key labels and functions */
  848. MENUITEM *mfunc = NULL;            /* list of regional functions  */
  849. mousehandler_t    mtrack;            /* mouse tracking handler */
  850.  
  851. /* last mouse position */
  852. static  int levent = 0, lrow = 0, lcol = 0, doubleclick, lbutton, lflags;
  853. #ifdef    DOS
  854. static  clock_t lastcalled = 0;
  855. #else
  856. static    time_t    lastcalled = 0;
  857. #endif
  858. static    mousehandler_t lastf;
  859.  
  860.  
  861. /*
  862.  * register_mfunc - register the given function to get called
  863.  *             on mouse events in the given display region
  864.  */
  865. register_mfunc(f, tlr, tlc, brr, brc)
  866. mousehandler_t    f;
  867. int        tlr, tlc, brr, brc;
  868. {
  869.     MENUITEM **mp;
  870.  
  871.     if(!mouseexist())
  872.       return(FALSE);
  873.  
  874.     for(mp = &mfunc; *mp; mp = &(*mp)->next)
  875.       ;
  876.  
  877.     *mp = (MENUITEM *)malloc(sizeof(MENUITEM));
  878.     memset(*mp, 0, sizeof(MENUITEM));
  879.  
  880.     (*mp)->action = f;
  881.     (*mp)->tl.r   = tlr;
  882.     (*mp)->br.r   = brr;
  883.     (*mp)->tl.c   = tlc;
  884.     (*mp)->br.c   = brc;
  885.     (*mp)->lbl.c  = (*mp)->lbl.r = 0;
  886.     (*mp)->label  = "";
  887.     return(TRUE);
  888. }
  889.  
  890.  
  891. /*
  892.  * clear_mfunc - clear any previously set mouse function
  893.  */
  894. void
  895. clear_mfunc(f)
  896. mousehandler_t f;
  897. {
  898.     MENUITEM *mp, *tp;
  899.  
  900.     if(mp = mfunc){
  901.     if(mp->action == f)
  902.       mfunc = mp->next;
  903.     else
  904.       for(tp = mp; tp->next; tp = tp->next)
  905.         if(tp->next->action == f){
  906.         mp = tp->next;
  907.         tp->next = tp->next->next;
  908.         break;
  909.         }
  910.  
  911.     if(mp){
  912.         mp->action = NULL;
  913.         free(mp);
  914.     }
  915.     }
  916. }
  917.  
  918.  
  919.  
  920. #ifdef EX_MOUSE
  921.  
  922. void
  923. clear_mtrack ()
  924. {
  925.     mtrack = NULL;
  926.     mswin_allowmousetrack (FALSE);
  927. }
  928.  
  929.  
  930. void
  931. register_mtrack (f)
  932. mousehandler_t    f;
  933. {
  934.     if (f) {
  935.     mtrack = f;
  936.     mswin_allowmousetrack (TRUE);
  937.     }
  938.     else
  939.     clear_mtrack ();
  940. }
  941.  
  942.  
  943. static void
  944. move_dot_to (row, col)
  945.     int row, col;
  946. {
  947.     LINE *lp;
  948.     int    i;
  949.     
  950.     lp = curwp->w_linep;
  951.     i = row - ((Pmaster) ? ComposerTopLine : 2);
  952.     while(i-- && lp != curbp->b_linep)    /* count from top */
  953.       lp = lforw(lp);
  954.     curgoal = col;
  955.     curwp->w_dotp = lp;            /* to new dot. */
  956.     curwp->w_doto = getgoal(lp);
  957.     curwp->w_flag |= WFMOVE;
  958. }
  959.  
  960.  
  961. /*
  962.  * mouse_in_pico
  963.  *
  964.  * When the mouse goes down in the body we set the mark and start 
  965.  * tracking.
  966.  *
  967.  * As the mouse moves we update the dot and redraw the screen.
  968.  *
  969.  * If the mouse moves above or below the pico body region of the 
  970.  * screen we scroll the text and update the dot position.
  971.  *
  972.  * When the mouse comes up we clean up.  If the mouse did not
  973.  * move, then we clear the mark and turn off the selection.
  974.  *
  975.  * Most of the mouse processing is handled here.  The exception is
  976.  * mouse down in the header.  Can't call HeaderEditor() from here so
  977.  * we send up the KEY_MOUSE character, which gets dispatched to
  978.  * mousepress(), which _can_ call HeaderEditor().
  979.  */
  980. unsigned long
  981. mouse_in_pico(mevent, row, col, button, flags)
  982.     int         mevent;
  983.     int      row, col, button, flags;
  984. {
  985.     unsigned long    rv = 0;        /* Our return value. */
  986.     int            trow, tcol;    /* translated row and col. */
  987.  
  988.     static int lheader = FALSE;        /* Mouse down was in header. */
  989.  
  990.   
  991.     /*
  992.      * What's up.
  993.      */
  994.     switch (mevent) {
  995.     case M_EVENT_DOWN:
  996.       if(button != M_BUTTON_LEFT)
  997.     break;
  998.  
  999.     /* Ignore mouse down if not in pico body region. */
  1000.     if (row < 2 || row > term.t_nrow - (term.t_mrow+1)) {
  1001.         clear_mtrack ();
  1002.         break;
  1003.         }
  1004.     
  1005.     /* Detect double clicks.  Not that we do anything with em, just
  1006.      * detect them. */
  1007. #ifdef    DOS
  1008. #ifdef    CLOCKS_PER_SEC
  1009.     doubleclick = (lrow == row && lcol == col
  1010.                && clock() < (lastcalled + CLOCKS_PER_SEC/2));
  1011. #else
  1012. #ifdef    CLK_TCK
  1013. doubleclick = (lrow == row && lcol == col
  1014.                && clock() < (lastcalled + CLK_TCK/2));
  1015. #else
  1016.     doubleclick = FALSE;
  1017. #endif
  1018. #endif
  1019.     lastcalled  = clock();
  1020. #else
  1021.     doubleclick = (lrow == row && lcol == col
  1022.                && time(0) < (lastcalled + 2));
  1023.     lastcalled  = time(0);
  1024. #endif
  1025.     lheader        = FALSE;    /* Rember mouse down position. */
  1026.     levent        = mevent;
  1027.     lrow        = row;
  1028.     lcol        = col;
  1029.     lbutton     = button;
  1030.     lflags      = flags;
  1031.     
  1032.     /* Mouse down in body? */
  1033.     if (row < (Pmaster ? ComposerTopLine : 2)) {
  1034.         /* Mouse down in message header -> no tracking, just remember
  1035.          * where */
  1036.         lheader = TRUE;
  1037.         }
  1038.     else {
  1039.         /* Mouse down in message.
  1040.          * If no shift key and an existing mark -> clear the mark.
  1041.          * If shift key and no existing mark -> set mark before moving */
  1042.         if (!(flags & M_KEY_SHIFT) && curwp->w_markp) 
  1043.         setmark (0,1);        /* this clears the mark. */
  1044.         else if (flags & M_KEY_SHIFT && !curwp->w_markp)
  1045.         setmark (0,1);        /* while this sets the mark. */
  1046.         
  1047.         /* Reposition dot to mouse down. */
  1048.         move_dot_to (row, col);
  1049.         
  1050.         /* Set the mark to dot if no existing mark. */
  1051.         if (curwp->w_markp == NULL) 
  1052.         setmark (0,1);
  1053.  
  1054.         /* Track mouse movement. */
  1055.         register_mtrack (mouse_in_pico);
  1056.         update ();
  1057.         lheader = FALSE;        /* Just to be sure. */
  1058.         }
  1059.     break;
  1060.     
  1061.     
  1062.     case M_EVENT_TRACK:
  1063.     /* Mouse tracking. */
  1064.     if (lheader)            /* Ignore mouse movement in header. */
  1065.         break;
  1066.     
  1067.     /* If above or below body, scroll body and adjust the row and col. */
  1068.     if (row < (Pmaster ? ComposerTopLine : 2)) {
  1069.         /* Scroll text down screen and move dot to top left corner. */
  1070.         scrollupline (0,1);
  1071.         trow = (Pmaster) ? ComposerTopLine : 2;
  1072.         tcol = 0;
  1073.         }
  1074.     else if (row > term.t_nrow - (term.t_mrow + 1)) {
  1075.         /* Scroll text up screen and move dot to bottom right corner. */
  1076.         scrolldownline (0,1);
  1077.         trow = term.t_nrow - (term.t_mrow + 1);
  1078.         tcol = term.t_ncol;
  1079.         }
  1080.     else {
  1081.         trow = row;
  1082.         tcol = col;
  1083.         }
  1084.  
  1085.     /* Move dot to target column. */
  1086.     move_dot_to (trow, tcol);
  1087.     
  1088.     /* Update screen. */
  1089.     update ();
  1090.     break;
  1091.  
  1092.     
  1093.     case M_EVENT_UP:
  1094.       if(button == M_BUTTON_RIGHT){
  1095. #ifdef    _WINDOWS
  1096.       pico_popup();
  1097. #endif
  1098.       break;
  1099.       }
  1100.       else if(button != M_BUTTON_LEFT)
  1101.     break;
  1102.  
  1103.     if (lheader) {
  1104.         lheader = FALSE;
  1105.         /* Last down in header. */
  1106.         if (row == lrow && col == lcol) {
  1107.         /* Mouse up and down in same place in header.  Means the
  1108.          * user want to edit the header.  Return KEY_MOUSE which
  1109.          * will cause mousepress to be called, which will
  1110.          * call HeaderEditor.  Can't call HeaderEditor from here
  1111.          * because that would mess up layering. */
  1112.         if (curwp->w_marko)
  1113.             setmark (0,1);
  1114.         rv = ((unsigned long)KEY_MOUSE << 16) | TRUE;
  1115.         }
  1116.         }
  1117.     else {
  1118.         /* If up at same place, clear mark */
  1119.         if (curwp->w_markp == curwp->w_dotp && 
  1120.             curwp->w_marko == curwp->w_doto) {
  1121.         setmark (0,1);
  1122.         curwp->w_flag |= WFMOVE;
  1123.         }
  1124.         clear_mtrack ();
  1125.         update ();
  1126.         }
  1127.     break;    
  1128.     }
  1129.     
  1130.     return(rv);
  1131. }
  1132. #endif
  1133.  
  1134.  
  1135.  
  1136. /*
  1137.  * mouse_in_content - general mechanism used to pass recognized mouse
  1138.  *              events in predefined region back thru the usual
  1139.  *              keyboard input stream.  The actual return value
  1140.  *              passed back from this function is set dynamically
  1141.  *              via the "down" argument which is read when both the
  1142.  *              "row" and "col" arguments are negative.
  1143.  */
  1144. unsigned long
  1145. mouse_in_content(mevent, row, col, button, flags)
  1146.     int      mevent;
  1147.     int      row, col, button, flags;
  1148. {
  1149.     unsigned long   rv = 0;
  1150.     static unsigned mouse_val = KEY_MOUSE;
  1151.  
  1152.     if(row == -1 && col == -1){
  1153.     mouse_val = mevent;            /* setting return value */
  1154.     }
  1155.     else {
  1156.     /* A real event. */
  1157.     levent = mevent;
  1158.     switch (mevent) {
  1159.     case M_EVENT_DOWN:
  1160.         /* Mouse down does not mean anything, just keep track of 
  1161.          * where it went down and if this is a double click. */
  1162. #ifdef    DOS
  1163. #ifdef    CLOCKS_PER_SEC
  1164.         doubleclick = (lrow == row && lcol == col
  1165.                && clock() < (lastcalled + CLOCKS_PER_SEC/2));
  1166. #else
  1167. #ifdef    CLK_TCK
  1168.         doubleclick = (lrow == row && lcol == col
  1169.                && clock() < (lastcalled + CLK_TCK/2));
  1170. #else
  1171.         doubleclick = FALSE;
  1172. #endif
  1173. #endif
  1174.         lastcalled    = clock();
  1175. #else
  1176.         doubleclick = (lrow == row && lcol == col
  1177.                && time(0) < (lastcalled + 2));
  1178.         lastcalled  = time(0);
  1179. #endif
  1180.         lrow    = row;
  1181.         lcol    = col;
  1182.         lbutton    = button;
  1183.         lflags    = flags;
  1184.         break;
  1185.  
  1186.     case M_EVENT_UP:
  1187.         /* Mouse up.  If in the same position as it went down
  1188.          * then we return the value set above, which goes into
  1189.          * the character input stream, which gets processed as
  1190.          * a mouse event by some upper layer, which calls to 
  1191.          * mouse_get_last(). */
  1192.         if (lrow == row && lcol == col) {
  1193.         rv = mouse_val;
  1194.         rv = (rv << 16) | TRUE;
  1195.         }
  1196.         break;
  1197.         
  1198.     case M_EVENT_TRACK:
  1199.         break;
  1200.     }
  1201.     }
  1202.  
  1203.     return(rv);
  1204. }
  1205.  
  1206.  
  1207. /*
  1208.  * mouse_get_last - Get last mouse event.
  1209.  *
  1210.  */
  1211. void
  1212. mouse_get_last(f, mp)
  1213.     mousehandler_t *f;
  1214.     MOUSEPRESS *mp;
  1215. {
  1216.     if (f != NULL)
  1217.         *f  = lastf;
  1218.     if (mp != NULL) {
  1219.     mp->mevent    = levent;
  1220.     mp->row        = lrow;
  1221.     mp->col        = lcol;
  1222.     mp->doubleclick = doubleclick;
  1223.     mp->button    = lbutton;
  1224.     mp->flags    = lflags;
  1225.     }
  1226. }
  1227.  
  1228.  
  1229.  
  1230. /*
  1231.  * register_key - register the given keystroke to accept mouse events
  1232.  */
  1233. void
  1234. register_key(i, rval, label, label_printer, row, col, len)
  1235. int       i;
  1236. unsigned  rval;
  1237. char     *label;
  1238. void      (*label_printer)();
  1239. int       row, col, len;
  1240. {
  1241.     if(i > 11)
  1242.       return;
  1243.  
  1244.     menuitems[i].val   = rval;
  1245.     menuitems[i].tl.r  = menuitems[i].br.r = row;
  1246.     menuitems[i].tl.c  = col;
  1247.     menuitems[i].br.c  = col + len;
  1248.     menuitems[i].lbl.r = menuitems[i].tl.r;
  1249.     menuitems[i].lbl.c = menuitems[i].tl.c;
  1250.     menuitems[i].label_hiliter = label_printer;
  1251.     if(menuitems[i].label){
  1252.     free(menuitems[i].label);
  1253.     menuitems[i].label = NULL;
  1254.     }
  1255.  
  1256.     if(label
  1257.        && (menuitems[i].label =(char *)malloc((strlen(label)+1)*sizeof(char))))
  1258.       strcpy(menuitems[i].label, label);
  1259. }
  1260. #endif    /* MOUSE */
  1261.  
  1262.  
  1263. /* 
  1264.  * Below are functions for use outside pico to manipulate text
  1265.  * in a pico's native format (circular linked list of lines).
  1266.  *
  1267.  * The idea is to streamline pico use by making it fairly easy
  1268.  * for outside programs to prepare text intended for pico's use.
  1269.  * The simple char * alternative is messy as it requires two copies
  1270.  * of the same text, and isn't very economic in limited memory
  1271.  * situations (THANKS BELLEVUE-BILLY.).
  1272.  */
  1273. typedef struct picotext {
  1274.     LINE *linep;
  1275.     LINE *dotp;
  1276.     int doto;
  1277.     short crinread;
  1278. } PICOTEXT;
  1279.  
  1280. #define PT(X)    ((PICOTEXT *)(X))
  1281.  
  1282. /*
  1283.  * pico_get - return window struct pointer used as a handle
  1284.  *            to the other pico_xxx routines.
  1285.  */
  1286. void *
  1287. pico_get()
  1288. {
  1289.    PICOTEXT *wp = NULL;
  1290.    LINE     *lp = NULL;
  1291.  
  1292.    if(wp = (PICOTEXT *)malloc(sizeof(PICOTEXT))){
  1293.        wp->crinread = 0;
  1294.        if((lp = lalloc(0)) == NULL){
  1295.        free(wp);
  1296.        return(NULL);
  1297.        }
  1298.  
  1299.        wp->dotp = wp->linep = lp->l_fp = lp->l_bp = lp;
  1300.        wp->doto = 0;
  1301.    }
  1302.    else
  1303.      emlwrite("Can't allocate space for text", NULL);
  1304.  
  1305.    return((void *)wp);
  1306. }
  1307.  
  1308. /*
  1309.  * pico_give - free resources and give up picotext struct
  1310.  */
  1311. void
  1312. pico_give(w)
  1313. void *w;
  1314. {
  1315.     register LINE *lp;
  1316.     register LINE *fp;
  1317.  
  1318.     fp = lforw(PT(w)->linep);
  1319.     while((lp = fp) != PT(w)->linep){
  1320.         fp = lforw(lp);
  1321.     free(lp);
  1322.     }
  1323.     free(PT(w)->linep);
  1324.     free((PICOTEXT *)w);
  1325. }
  1326.  
  1327. /*
  1328.  * pico_readc - return char at current point.  Up to calling routines
  1329.  *              to keep cumulative count of chars.
  1330.  */
  1331. int
  1332. pico_readc(w, c)
  1333. void          *w;
  1334. unsigned char *c;
  1335. {
  1336.     int rv     = 0;
  1337.  
  1338.     if(PT(w)->crinread){
  1339.     *c = '\012';                /* return LF */
  1340.     PT(w)->crinread = 0;
  1341.     rv++;
  1342.     }
  1343.     else if(PT(w)->doto < llength(PT(w)->dotp)){ /* normal char to return */
  1344.         *c = (unsigned char) lgetc(PT(w)->dotp, (PT(w)->doto)++).c;
  1345.     rv++;
  1346.     }
  1347.     else if(PT(w)->dotp != PT(w)->linep){ /* return line break */
  1348.     PT(w)->dotp = lforw(PT(w)->dotp);
  1349.     PT(w)->doto = 0;
  1350. #if    defined(DOS) || defined(OS2)
  1351.     *c = '\015';
  1352.     PT(w)->crinread++;
  1353. #else
  1354.     *c = '\012';                /* return local eol! */
  1355. #endif
  1356.     rv++;
  1357.     }                        /* else no chars to return */
  1358.  
  1359.     return(rv);
  1360. }
  1361.  
  1362.  
  1363. /*
  1364.  * pico_writec - write a char into picotext and advance pointers.
  1365.  *               Up to calling routines to keep track of total chars
  1366.  *               written
  1367.  */
  1368. int
  1369. pico_writec(w, c)
  1370. void *w;
  1371. int   c;
  1372. {
  1373.     int   rv = 0;
  1374.  
  1375.     if(c == '\r')                /* ignore CR's */
  1376.       rv++;                    /* so fake it */
  1377.     else if(c == '\n'){                /* insert newlines on LF */
  1378.     /*
  1379.      * OK, if there are characters on the current line or 
  1380.      * dotp is pointing to the delimiter line, insert a newline
  1381.      * No here's the tricky bit; preserve the implicit EOF newline.
  1382.      */
  1383.     if(lforw(PT(w)->dotp) == PT(w)->linep && PT(w)->dotp != PT(w)->linep){
  1384.         PT(w)->dotp = PT(w)->linep;
  1385.         PT(w)->doto = 0;
  1386.     }
  1387.     else{
  1388.         register LINE *lp;
  1389.  
  1390.         if((lp = lalloc(0)) == NULL){
  1391.         emlwrite("Can't allocate space for more characters",NULL);
  1392.         return(0);
  1393.         }
  1394.  
  1395.         if(PT(w)->dotp == PT(w)->linep){
  1396.         lforw(lp) = PT(w)->linep;
  1397.         lback(lp) = lback(PT(w)->linep);
  1398.         lforw(lback(lp)) = lback(PT(w)->linep) = lp;
  1399.         }
  1400.         else{
  1401.         lforw(lp) = lforw(PT(w)->dotp);
  1402.         lback(lp) = PT(w)->dotp;
  1403.         lback(lforw(lp)) = lforw(PT(w)->dotp) = lp;
  1404.         PT(w)->dotp = lp;
  1405.         PT(w)->doto = 0;
  1406.         }
  1407.     }
  1408.  
  1409.     rv++;
  1410.     }
  1411.     else
  1412.       rv = geninsert(&PT(w)->dotp, &PT(w)->doto, PT(w)->linep, c, 0, 1, NULL);
  1413.  
  1414.     return((rv) ? 1 : 0);            /* return number written */
  1415. }
  1416.  
  1417.  
  1418. /*
  1419.  * pico_puts - just write the given string into the text
  1420.  */
  1421. int
  1422. pico_puts(w, s)
  1423. void *w;
  1424. char *s;
  1425. {
  1426.     while(*s != '\0')
  1427.       pico_writec(w, (int)*s++);
  1428. }
  1429.  
  1430.  
  1431. /*
  1432.  * pico_seek - position dotp and dot at requested location
  1433.  */
  1434. int
  1435. pico_seek(w, offset, orig)
  1436. void *w;
  1437. long  offset;
  1438. int   orig;
  1439. {
  1440.     register LINE *lp;
  1441.  
  1442.     PT(w)->crinread = 0;
  1443.     switch(orig){
  1444.       case 0 :                /* SEEK_SET */
  1445.     PT(w)->dotp = lforw(PT(w)->linep);
  1446.     PT(w)->doto = 0;
  1447.       case 1 :                /* SEEK_CUR */
  1448.     lp = PT(w)->dotp;
  1449.     while(lp != PT(w)->linep){
  1450.         if(offset <= llength(lp)){
  1451.         PT(w)->doto = (int)offset;
  1452.         PT(w)->dotp = lp;
  1453.         break;
  1454.         }
  1455.  
  1456.         offset -= ((long)llength(lp)
  1457. #if defined(DOS) || defined(OS2)
  1458.                + 2L);
  1459. #else
  1460.                + 1L);
  1461. #endif
  1462.         lp = lforw(lp);
  1463.     }
  1464.         break;
  1465.  
  1466.       case 2 :                /* SEEK_END */
  1467.     PT(w)->dotp = lback(PT(w)->linep);
  1468.     PT(w)->doto = llength(PT(w)->dotp);
  1469.     break;
  1470.       default :
  1471.         return(-1);
  1472.     }
  1473.  
  1474.     return(0);
  1475. }
  1476.  
  1477.  
  1478. /*
  1479.  * breplace - replace the current window's text with the given 
  1480.  *            LINEs
  1481.  */
  1482. void
  1483. breplace(w)
  1484. void *w;
  1485. {
  1486.     register LINE *lp;
  1487.     register LINE *fp;
  1488.  
  1489.     fp = lforw(curbp->b_linep);
  1490.     while((lp = fp) != curbp->b_linep){        /* blast old lines */
  1491.         fp = lforw(lp);
  1492.     free(lp);
  1493.     }
  1494.     free(curbp->b_linep);
  1495.  
  1496.     curbp->b_linep   = PT(w)->linep;            /* arrange pointers */
  1497.  
  1498.     curwp->w_linep   = lforw(curbp->b_linep);
  1499.     curwp->w_dotp    = lforw(curbp->b_linep);
  1500.     curwp->w_doto    = 0;
  1501.     curwp->w_markp   = curwp->w_imarkp = NULL;
  1502.     curwp->w_marko   = curwp->w_imarko = 0;
  1503.  
  1504.     curbp->b_dotp    = curwp->w_dotp;
  1505.     curbp->b_doto    = curbp->b_marko  = 0;
  1506.     curbp->b_markp   = NULL;
  1507.     curbp->b_linecnt = -1;
  1508.  
  1509.     curwp->w_flag |= WFHARD;
  1510. }
  1511.  
  1512.  
  1513. #ifdef    _WINDOWS
  1514. /*
  1515.  *
  1516.  */
  1517. int
  1518. composer_file_drop(x, y, filename)
  1519.     int   x, y;
  1520.     char *filename;
  1521. {
  1522.     int attached = 0;
  1523.     if((ComposerTopLine > 2 && x <= ComposerTopLine)
  1524.        || !LikelyASCII(filename)){
  1525.     AppendAttachment(filename, NULL, NULL);
  1526.     attached++;
  1527.     }
  1528.     else{
  1529.     setimark(FALSE, 1);
  1530.     ifile(filename);
  1531.     swapimark(FALSE, 1);
  1532.     }
  1533.  
  1534.     if(ComposerEditing){        /* update display */
  1535.     PaintBody(0);
  1536.     }
  1537.     else{
  1538.     refresh(0, 1);
  1539.     update();
  1540.     }
  1541.  
  1542.     if(attached)
  1543.       emlwrite("Attached dropped file \"%s\"", filename);
  1544.     else
  1545.       emlwrite("Inserted dropped file \"%s\"", filename);
  1546.  
  1547.     if(ComposerEditing){        /* restore cursor */
  1548.     HeaderPaintCursor();
  1549.     }
  1550.     else{
  1551.     curwp->w_flag |= WFHARD;
  1552.     update();
  1553.     }
  1554.  
  1555.     return(1);
  1556. }
  1557. #endif
  1558.