home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / vile-src.zip / vile-8.1 / buffer.c < prev    next >
C/C++ Source or Header  |  1998-09-29  |  47KB  |  2,150 lines

  1. /*
  2.  * Buffer management.
  3.  * Some of the functions are internal,
  4.  * and some are actually attached to user
  5.  * keys. Like everyone else, they set hints
  6.  * for the display system.
  7.  *
  8.  * $Header: /usr/build/vile/vile/RCS/buffer.c,v 1.177 1998/09/30 01:30:11 tom Exp $
  9.  *
  10.  */
  11.  
  12. #include    "estruct.h"
  13. #include    "edef.h"
  14.  
  15. /*--------------------------------------------------------------------------*/
  16. #if    OPT_UPBUFF
  17. static    int    show_BufferList ( BUFFER *bp );
  18. #define    update_on_chg(bp) (!b_is_temporary(bp) || show_all)
  19. #endif
  20.  
  21. #if    OPT_PROCEDURES
  22. static    void    run_buffer_hook (void);
  23. #endif
  24.  
  25. #if    !OPT_MAJORMODE
  26. static    int    cmode_active ( BUFFER *bp );
  27. #endif
  28.  
  29. static    BUFFER *find_BufferList (void);
  30. static    BUFFER *find_b_number ( const char *number  );
  31. static    BUFFER *find_latest (void);
  32. static    BUFFER *find_nth_created ( int n );
  33. static    BUFFER *find_nth_used ( int n );
  34. static    int    countBuffers (void);
  35. static    int    hist_show (void);
  36. static    int    lookup_hist ( BUFFER *bp );
  37. static    void    FreeBuffer ( BUFFER *bp );
  38. static    void    MarkDeleted ( BUFFER *bp );
  39. static    void    MarkUnused ( BUFFER *bp );
  40. static    void    TrackAlternate ( BUFFER *bp );
  41. static    void    makebufflist (LIST_ARGS);
  42.  
  43. #if !SMALLER
  44. static    void    footnote ( int c );
  45. #endif
  46.  
  47. /*--------------------------------------------------------------------------*/
  48.  
  49. static    BUFFER    *last_bp,    /* noautobuffer value */
  50.         *this_bp,    /* '%' buffer */
  51.         *that_bp;    /* '#' buffer */
  52. static    int    show_all,    /* true iff we show all buffers */
  53.         updating_list;
  54.  
  55. /*--------------------------------------------------------------------------*/
  56.  
  57. /*
  58.  * Returns the buffer-list pointer, if it exists.
  59.  */
  60. static BUFFER *
  61. find_BufferList(void)
  62. {
  63.     return find_b_name(BUFFERLIST_BufName);
  64. }
  65.  
  66. /*
  67.  * Look for a buffer-pointer in the list, to see if it has been delinked yet.
  68.  */
  69. BUFFER *find_bp(BUFFER *bp1)
  70. {
  71.     register BUFFER *bp;
  72.     for_each_buffer(bp)
  73.         if (bp == bp1)
  74.             return bp;
  75.     return 0;
  76. }
  77.  
  78. /*
  79.  * Returns the total number of buffers in the list.
  80.  */
  81. static int
  82. countBuffers(void)
  83. {
  84.     register BUFFER *bp;
  85.     register int    count = 0;
  86.     for_each_buffer(bp)
  87.         count++;
  88.     return count;
  89. }
  90.  
  91. /*
  92.  * Returns the n'th buffer created
  93.  */
  94. static BUFFER *
  95. find_nth_created(int n)
  96. {
  97.     register BUFFER *bp;
  98.  
  99.     for_each_buffer(bp)
  100.         if (bp->b_created == n)
  101.             return bp;
  102.     return 0;
  103. }
  104.  
  105. /*
  106.  * Returns the n'th buffer used
  107.  */
  108. static BUFFER *
  109. find_nth_used(int n)
  110. {
  111.     register BUFFER *bp;
  112.  
  113.     for_each_buffer(bp)
  114.         if (bp->b_last_used == n)
  115.             return bp;
  116.     return 0;
  117. }
  118.  
  119. /*
  120.  * Returns the buffer with the largest value of 'b_last_used'.
  121.  */
  122. static BUFFER *
  123. find_latest(void)
  124. {
  125.     register BUFFER *bp, *maxbp = 0;
  126.  
  127.     for_each_buffer(bp) {
  128.         if (maxbp == 0)
  129.             maxbp = bp;
  130.         else if (maxbp->b_last_used < bp->b_last_used)
  131.             maxbp = bp;
  132.     }
  133.     return maxbp;
  134. }
  135.  
  136. /*
  137.  * Look for a filename in the buffer-list
  138.  */
  139. BUFFER *
  140. find_b_file(const char *fname)
  141. {
  142.     register BUFFER *bp;
  143.     char    nfname[NFILEN];
  144.  
  145.     (void)lengthen_path(strcpy(nfname, fname));
  146.     for_each_buffer(bp)
  147.         if (same_fname(nfname, bp, FALSE))
  148.             return bp;
  149.     return 0;
  150. }
  151.  
  152. /*
  153.  * Look for a specific buffer number
  154.  */
  155. BUFFER *
  156. find_b_hist(int number)
  157. {
  158.     register BUFFER *bp;
  159.  
  160.     if (number >= 0) {
  161.         for_each_buffer(bp)
  162.             if (!b_is_temporary(bp) && (number-- <= 0))
  163.                 break;
  164.     } else
  165.         bp = 0;
  166.     return bp;
  167. }
  168.  
  169. /*
  170.  * Look for a buffer-number (i.e., one from the buffer-list display)
  171.  */
  172. static BUFFER *
  173. find_b_number(const char *number)
  174. {
  175.     register int    c = 0;
  176.     while (isDigit(*number))
  177.         c = (c * 10) + (*number++ - '0');
  178.     if (!*number)
  179.         return find_b_hist(c);
  180.     return 0;
  181. }
  182.  
  183. /*
  184.  * Find buffer, given (possibly) filename, buffer name or buffer number
  185.  */
  186. BUFFER *
  187. find_any_buffer(const char *name)
  188. {
  189.     register BUFFER *bp;
  190.  
  191.     if ((bp=find_b_name(name)) == 0        /* Try buffer */
  192.      && (bp=find_b_file(name)) == 0        /* ...then filename */
  193.      && (bp=find_b_number(name)) == 0) {    /* ...then number */
  194.         mlforce("[No such buffer] %s", name);
  195.         return 0;
  196.     }
  197.  
  198.     return bp;
  199. }
  200.  
  201. /*
  202.  * Delete all instances of window pointer for a given buffer pointer
  203.  */
  204. int
  205. zotwp(BUFFER *bp)
  206. {
  207.     register WINDOW *wp;
  208.     register BUFFER *nbp;
  209.     register BUFFER *obp = 0;
  210.     WINDOW    dummy;
  211.     int s = FALSE;
  212.  
  213.     TRACE(("zotwp(%s)\n", bp->b_bname))
  214.  
  215.     /*
  216.      * Locate buffer to switch to after deleting windows.  It can't be
  217.      * the same as the buffer which is being deleted and if it's an
  218.      * invisible or scratch buffer, it must have been already visible.
  219.      * From the set of allowable buffers, choose the most recently
  220.      * used.
  221.      */
  222.     for_each_buffer(nbp) {
  223.         if (nbp != bp && (!b_is_temporary(nbp) || nbp->b_nwnd != 0)) {
  224.             if (obp == 0)
  225.                 obp = nbp;
  226.             else if (obp->b_last_used < nbp->b_last_used)
  227.                 obp = nbp;
  228.         }
  229.     }
  230.  
  231.     /* Delete window instances...*/
  232.     for_each_window(wp) {
  233.         if (wp->w_bufp == bp && wheadp->w_wndp != NULL) {
  234.             dummy.w_wndp = wp->w_wndp;
  235.             s = delwp(wp);
  236.             wp = &dummy;
  237.         }
  238.     }
  239.     if (obp != NULL)
  240.         s = swbuffer(obp);
  241.  
  242.     return s;
  243. }
  244.  
  245. /*
  246.  * Mark a buffer's created member to 0 (unused), adjusting the other buffers
  247.  * so that there are no gaps.
  248.  */
  249. static void
  250. MarkDeleted(register BUFFER *bp)
  251. {
  252.     int    created = bp->b_created;
  253.  
  254.     if (created) {
  255.         bp->b_created = 0;
  256.         for_each_buffer(bp) {
  257.             if (bp->b_created > created)
  258.                 bp->b_created -= 1;
  259.         }
  260.     }
  261. }
  262.  
  263. /*
  264.  * Mark a buffer's last-used member to 0 (unused), adjusting the other buffers
  265.  * so that there are no gaps.
  266.  */
  267. static void
  268. MarkUnused(register BUFFER *bp)
  269. {
  270.     int    used = bp->b_last_used;
  271.  
  272.     if (used) {
  273.         bp->b_last_used = 0;
  274.         for_each_buffer(bp) {
  275.             if (bp->b_last_used > used)
  276.                 bp->b_last_used -= 1;
  277.         }
  278.     }
  279. }
  280.  
  281. /*
  282.  * After 'bclear()', frees remaining storage for a buffer.
  283.  */
  284. static void
  285. FreeBuffer(BUFFER *bp)
  286. {
  287.     if (bp->b_fname != out_of_mem)
  288.         FreeIfNeeded(bp->b_fname);
  289.  
  290. #if !WINMARK
  291.     if (is_header_line(MK, bp)) {
  292.         MK.l = null_ptr;
  293.         MK.o = 0;
  294.     }
  295. #endif
  296.     lfree(buf_head(bp), bp);        /* Release header line. */
  297.     if (delink_bp(bp) || bp == bminip) {
  298.         if (curbp == bp)
  299.             curbp = NULL;
  300.  
  301. #if OPT_HILITEMATCH
  302.         clobber_save_curbp(bp);
  303. #endif
  304. #if OPT_PERL || OPT_TCL
  305.         api_free_private(bp->b_api_private);
  306. #endif
  307.         free((char *) bp);        /* Release buffer block */
  308.     }
  309. }
  310.  
  311. /*
  312.  * Adjust buffer-list's last-used member to account for a new buffer.
  313.  */
  314. static void
  315. TrackAlternate(BUFFER *newbp)
  316. {
  317.     register BUFFER *bp;
  318.  
  319.     if (!updating_list) {
  320.         MarkUnused(newbp);
  321.         if ((bp = find_latest()) != 0) {
  322.             newbp->b_last_used = (bp->b_last_used + 1);
  323.         } else {    /* shouldn't happen... */
  324.             newbp->b_last_used = 1;
  325.         }
  326.     }
  327. }
  328.  
  329. /* c is an index (counting only visible/nonscratch) into buffer list */
  330. char *
  331. hist_lookup(int c)
  332. {
  333.     register BUFFER *bp = find_b_hist(c);
  334.  
  335.     return (bp != 0) ? bp->b_bname : 0;
  336. }
  337.  
  338. /* returns the buffer corresponding to the given number in the history */
  339. static int
  340. lookup_hist(BUFFER *bp1)
  341. {
  342.     register BUFFER *bp;
  343.     register int    count = -1;
  344.  
  345.     for_each_buffer(bp)
  346.         if (!b_is_temporary(bp)) {
  347.             count++;
  348.             if (bp == bp1)
  349.                 return count;
  350.         }
  351.     return -1;    /* no match */
  352. }
  353.  
  354. /*
  355.  * Run the $buffer-hook procedure, if it's defined.  Note that we mustn't do
  356.  * this if curwp->w_bufp != curbp, since that would break the use of DOT and MK
  357.  * throughout the program when performing editing operations.
  358.  */
  359. #if OPT_PROCEDURES
  360. static int bufhooking;
  361. #define DisableBufferHook bufhooking++;
  362. #define EnableBufferHook  bufhooking--;
  363.  
  364. static void
  365. run_buffer_hook(void)
  366. {
  367.     if (!bufhooking
  368.      && !reading_msg_line
  369.      && *bufhook
  370.      && curwp != 0
  371.      && curwp->w_bufp == curbp) {
  372.         DisableBufferHook;
  373.         run_procedure(bufhook);
  374.         EnableBufferHook;
  375.     }
  376. }
  377. #else
  378. #define run_buffer_hook() /*EMPTY*/
  379. #define DisableBufferHook /*EMPTY*/
  380. #define EnableBufferHook  /*EMPTY*/
  381. #endif
  382.  
  383. static int
  384. hist_show(void)
  385. {
  386.     register BUFFER *bp;
  387.     register int i = 0;
  388.     char line[NLINE];
  389.     BUFFER *abp = (BUFFER *)0;
  390.  
  391.     if (!global_g_val(GMDABUFF))
  392.         abp = find_alt();
  393.  
  394.     (void)strcpy(line,"");
  395.     for_each_buffer(bp) {
  396.         if (!b_is_temporary(bp)) {
  397.             if (bp != curbp) {    /* don't bother with current */
  398.                 (void)lsprintf(line+strlen(line), "  %d%s%s %s",
  399.                     i,
  400.                     b_is_changed(bp) ? "*" : "",
  401.                     (abp && abp == bp) ? "#" : "",
  402.                     bp->b_bname);
  403.             }
  404.             if (++i > 9)    /* limit to single-digit */
  405.                 break;
  406.         }
  407.     }
  408.     if (strcmp(line,"")) {
  409.         mlforce("%s",line);
  410.         return TRUE;
  411.     } else {
  412.         return FALSE;
  413.     }
  414. }
  415.  
  416. /*
  417.  * Given a buffer, returns any corresponding WINDOW pointer
  418.  */
  419. WINDOW *
  420. bp2any_wp(BUFFER *bp)
  421. {
  422.     register WINDOW *wp;
  423.     for_each_visible_window(wp)
  424.         if (wp->w_bufp == bp)
  425.             break;
  426.     return wp;
  427. }
  428.  
  429. /*
  430.  * Lets the user select a given buffer by its number.
  431.  */
  432. int
  433. histbuff(int f, int n)
  434. {
  435.     register int thiskey, c;
  436.     register BUFFER *bp = 0;
  437.     char *bufn;
  438.  
  439.     if (f == FALSE) {
  440.         if (!hist_show())
  441.             return FALSE;
  442.         thiskey = lastkey;
  443.         c = keystroke8();
  444.         mlerase();
  445.         if (c == thiskey) {
  446.             c = lookup_hist(bp = find_alt());
  447.         } else if (isDigit(c)) {
  448.             c = c - '0';
  449.         } else {
  450.             if (!isreturn(c))
  451.                 unkeystroke(c);
  452.             return FALSE;
  453.         }
  454.     } else {
  455.         c = n;
  456.     }
  457.  
  458.     if (c >= 0) {
  459.         if ((bufn = hist_lookup(c)) == NULL) {
  460.             mlwarn("[No such buffer.]");
  461.             return FALSE;
  462.         }
  463.         /* first assume its a buffer name, then a file name */
  464.         if ((bp = find_b_name(bufn)) == NULL)
  465.             return getfile(bufn,TRUE);
  466.  
  467.     } else if (bp == 0) {
  468.         mlwarn("[No alternate buffer]");
  469.         return FALSE;
  470.     }
  471.  
  472.     return swbuffer(bp);
  473. }
  474.  
  475. /*
  476.  * Returns the alternate-buffer pointer, if any
  477.  */
  478. BUFFER *
  479. find_alt(void)
  480. {
  481.     register BUFFER *bp;
  482.  
  483.     if (global_g_val(GMDABUFF)) {
  484.         BUFFER *any_bp = 0;
  485.         if ((bp = find_bp(curbp)) == 0)
  486.             bp = bheadp;
  487.         for (; bp; bp = bp->b_bufp) {
  488.             if (bp != curbp) {
  489.                 /*
  490.                  * If we allow temporary buffers to be selected as the
  491.                  * alternate, we get into the situation where we try
  492.                  * to load the message buffer, which has a NULL filename.
  493.                  * Note that the 'noautobuffer' case, below, never selects
  494.                  * a temporary buffer as the alternate buffer.
  495.                  */
  496.                 if (!b_is_temporary(bp))
  497.                     return bp;
  498.             }
  499.         }
  500.         return any_bp;
  501.     } else {
  502.         register BUFFER *last = 0,
  503.                 *next = find_latest();
  504.  
  505.         for_each_buffer(bp) {
  506.             if ((bp != next)
  507.              && !b_is_temporary(bp)) {
  508.                 if (last) {
  509.                     if (last->b_last_used < bp->b_last_used)
  510.                         last = bp;
  511.                 } else
  512.                     last = bp;
  513.             }
  514.         }
  515.         return last;
  516.     }
  517. }
  518.  
  519. /* make '#' buffer in noautobuffer-mode, given filename */
  520. void
  521. imply_alt(
  522. char *    fname,
  523. int    copy,
  524. int    lockfl)
  525. {
  526.     register BUFFER *bp;
  527.     register LINE    *lp;
  528.     BUFFER *savebp;
  529.     char nfname[NFILEN];
  530.  
  531.     if (interrupted() || fname == 0) /* didn't really have a filename */
  532.         return;
  533.  
  534.     (void)lengthen_path(strcpy(nfname, fname));
  535.     if (global_g_val(GMDIMPLYBUFF)
  536.      && curbp != 0
  537.      && curbp->b_fname != 0
  538.      && !same_fname(nfname, curbp, FALSE)
  539.      && !isInternalName(fname)) {
  540.         savebp = curbp;
  541.         if ((bp = find_b_file(nfname)) == 0) {
  542.             L_NUM    top, now;
  543.  
  544.             if ((bp = make_bp(fname, 0)) == 0) {
  545.                 mlforce("[Cannot create buffer]");
  546.                 return;
  547.             }
  548.  
  549.             /* fill the buffer */
  550.             b_clr_flags(bp, BFINVS|BFCHG);
  551.             b_set_flags(bp, BFIMPLY);
  552.             bp->b_active = TRUE;
  553.             ch_fname(bp, nfname);
  554.             make_local_b_val(bp,MDNEWLINE);
  555.             if (curwp != 0 && curwp->w_bufp == curbp) {
  556.                 top = line_no(curbp, curwp->w_line.l);
  557.                 now = line_no(curbp, DOT.l);
  558.             } else
  559.                 top = now = -1;
  560.  
  561.             if (copy) {
  562.                 for_each_line(lp, savebp) {
  563.                     if (addline(bp,lp->l_text,lp->l_used) != TRUE) {
  564.                         mlforce("[Copy-buffer failed]");
  565.                         return;
  566.                     }
  567.                 }
  568.                 set_b_val(bp, MDNEWLINE, b_val(savebp,MDNEWLINE));
  569.                 setm_by_suffix(bp);
  570.                 setm_by_preamble(bp);
  571.             } else
  572.                 readin(fname, lockfl, bp, FALSE);
  573.  
  574.             /* setup so that buffer-toggle works as in vi (i.e.,
  575.              * the top/current lines of the screen are the same).
  576.              */
  577.             if (now >= 0) {
  578.                 for_each_line(lp,bp) {
  579.                     if (--now == 0) {
  580.                         bp->b_dot.l = lp;
  581.                         bp->b_dot.o = curbp->b_dot.o;
  582.                         break;
  583.                     }
  584.                     if (--top == 0) {
  585.                         bp->b_wline.l = lp;
  586.                     }
  587.                 }
  588.             }
  589.         }
  590.         DisableBufferHook;
  591.         make_current(bp);
  592.         make_current(savebp);
  593.         EnableBufferHook;
  594.     }
  595. }
  596.  
  597. /* switch back to the most recent buffer */
  598. /* ARGSUSED */
  599. int
  600. altbuff(int f GCC_UNUSED, int n GCC_UNUSED)
  601. {
  602.     register BUFFER *bp = find_alt();
  603.     if (bp == 0) {
  604.         mlwarn("[No alternate filename to substitute for #]");
  605.         return FALSE;
  606.     } else {
  607.         return swbuffer(bp);
  608.     }
  609. }
  610.  
  611. /*
  612.  * Attach a buffer to a window. The
  613.  * values of dot and mark come from the buffer
  614.  * if the use count is 0. Otherwise, they come
  615.  * from some other window.
  616.  */
  617. /* ARGSUSED */
  618. int
  619. usebuffer(int f GCC_UNUSED, int n GCC_UNUSED)
  620. {
  621.     register BUFFER *bp;
  622.     register int    s;
  623.     char        bufn[NBUFN];
  624.  
  625.     bufn[0] = EOS;
  626.     if ((s=mlreply("Use buffer: ", bufn, sizeof(bufn))) != TRUE)
  627.         return s;
  628.     if ((bp=find_any_buffer(bufn)) == 0)    /* Try buffer */
  629.         return FALSE;
  630.     return swbuffer(bp);
  631. }
  632.  
  633. /* switch back to the first buffer (i.e., ":rewind") */
  634. /* ARGSUSED */
  635. int
  636. firstbuffer(int f GCC_UNUSED, int n GCC_UNUSED)
  637. {
  638.     int s = histbuff(TRUE,0);
  639.     if (!global_g_val(GMDABUFF))
  640.         last_bp = s ? curbp : 0;
  641.     return s;
  642. }
  643.  
  644. /* ARGSUSED */
  645. int
  646. nextbuffer(int f GCC_UNUSED, int n GCC_UNUSED)    /* switch to the next buffer in the buffer list */
  647. {
  648.     register BUFFER *bp;    /* eligible buffer to switch to*/
  649.     register BUFFER *stopatbp;    /* eligible buffer to switch to*/
  650.  
  651.     if (global_g_val(GMDABUFF)) {    /* go backward thru buffer-list */
  652.         stopatbp = NULL;
  653.         while (stopatbp != bheadp) {
  654.             /* get the last buffer in the list */
  655.             bp = bheadp;
  656.             while(bp != 0 && bp->b_bufp != stopatbp)
  657.                 bp = bp->b_bufp;
  658.             /* if that one's invisible, back up and try again */
  659.             if (b_is_invisible(bp))
  660.                 stopatbp = bp;
  661.             else
  662.                 return swbuffer(bp);
  663.         }
  664.     } else {            /* go forward thru args-list */
  665.         if ((stopatbp = curbp) == 0)
  666.             stopatbp = find_nth_created(1);
  667.         if (last_bp == 0)
  668.             last_bp = find_b_hist(0);
  669.         if (last_bp != 0) {
  670.             for (bp = last_bp->b_bufp; bp; bp = bp->b_bufp) {
  671.                 if (b_is_argument(bp))
  672.                     return swbuffer(last_bp = bp);
  673.             }
  674.         }
  675.         mlforce("[No more files to edit]");
  676.     }
  677.     /* we're back to the top -- they were all invisible */
  678.     return swbuffer(stopatbp);
  679. }
  680.  
  681. #if !SMALLER
  682. /* ARGSUSED */
  683. int
  684. prevbuffer(int f GCC_UNUSED, int n GCC_UNUSED)    /* switch to the previous buffer in the buffer list */
  685. {
  686.     register BUFFER *bp;    /* eligible buffer to switch to*/
  687.     register BUFFER *stopatbp;    /* eligible buffer to switch to*/
  688.  
  689.     if (global_g_val(GMDABUFF)) {    /* go forward thru buffer-list */
  690.         if ((bp = curbp) == 0)
  691.             bp = bheadp;
  692.         stopatbp = bp;
  693.         while ((bp != 0) && (bp = bp->b_bufp) != 0) {
  694.             /* get the next buffer in the list */
  695.             /* if that one's invisible, skip it and try again */
  696.             if (!b_is_invisible(bp))
  697.                 return swbuffer(bp);
  698.         }
  699.     } else {            /* go backward thru args-list */
  700.         if ((stopatbp = curbp) == 0)
  701.             stopatbp = find_nth_created(1);
  702.         else if ((bp = find_nth_created(curbp->b_created - 1)) != 0)
  703.             stopatbp = bp;
  704.         else
  705.             mlforce("[No more files to edit]");
  706.     }
  707.     /* we're back to the top -- they were all invisible */
  708.     return swbuffer(stopatbp);
  709. }
  710. #endif /* !SMALLER */
  711.  
  712. /* bring nbp to the top of the list, where curbp usually lives */
  713. void
  714. make_current(BUFFER *nbp)
  715. {
  716.     register BUFFER *bp;
  717. #if OPT_PROCEDURES
  718.     register BUFFER *ocurbp;
  719.  
  720.     ocurbp = curbp;
  721. #endif
  722.  
  723.     TrackAlternate(nbp);
  724.  
  725.     if (!updating_list && global_g_val(GMDABUFF)) {
  726.         if (nbp != bheadp) {    /* remove nbp from the list */
  727.             bp = bheadp;
  728.             while(bp != 0 && bp->b_bufp != nbp)
  729.                 bp = bp->b_bufp;
  730.             bp->b_bufp = nbp->b_bufp;
  731.  
  732.             /* put it at the head */
  733.             nbp->b_bufp = bheadp;
  734.  
  735.             bheadp = nbp;
  736.         }
  737.         curbp = bheadp;
  738.     } else
  739.         curbp = nbp;
  740.  
  741. #if OPT_PROCEDURES
  742.     if (curbp != ocurbp) {
  743.         run_buffer_hook();
  744.     }
  745. #endif
  746.     curtabval = tabstop_val(curbp);
  747.  
  748. #if OPT_TITLE
  749.     {
  750.         char title[256];
  751.         sprintf(title, "vile - %s", curbp->b_bname);
  752.         TTtitle(title);
  753.     }
  754. #endif
  755. }
  756.  
  757.  
  758. int
  759. swbuffer(register BUFFER *bp)    /* make buffer BP current */
  760. {
  761.     return swbuffer_lfl(bp, TRUE);
  762. }
  763.  
  764. static int
  765. suckitin (BUFFER *bp, int copy, int lockfl)
  766. {
  767.     int s = TRUE;
  768.  
  769.     if (copy) {
  770.         register WINDOW *wp;
  771.  
  772.         for_each_window(wp) {
  773.             if (wp->w_bufp == bp && (is_visible_window(wp) || bp->b_active != TRUE))
  774.                 copy_traits(&(wp->w_traits), &(bp->b_wtraits));
  775.         }
  776.     }
  777.     curwp->w_flag |= WFMODE|WFHARD;        /* Quite nasty.        */
  778.  
  779.     if (bp->b_active != TRUE) {        /* buffer not active yet*/
  780.         s = bp2readin(bp, lockfl);    /* read and activate it */
  781.     }
  782. #ifdef MDCHK_MODTIME
  783.     else
  784.         (void)check_modtime( bp, bp->b_fname );
  785. #endif
  786.     updatelistbuffers();
  787.     run_buffer_hook();
  788.     return s;
  789. }
  790.  
  791. int
  792. swbuffer_lfl(register BUFFER *bp, int lockfl)    /* make buffer BP current */
  793. {
  794.     int s = TRUE;
  795.  
  796.     if (!bp) {
  797.         mlforce("BUG:  swbuffer passed null bp");
  798.         return FALSE;
  799.     }
  800.  
  801.     TRACE(("swbuffer(%s) nwnd=%d\n", bp->b_bname, bp->b_nwnd))
  802.     if (curbp == bp
  803.      && curwp != 0
  804.      && DOT.l != 0
  805.      && curwp->w_bufp == bp) {  /* no switching to be done */
  806.  
  807.         if (!bp->b_active) /* on second thought, yes there is */
  808.             s = suckitin(bp, TRUE, lockfl);
  809.  
  810.         return s;
  811.      }
  812.  
  813.     if (curbp) {
  814.         /* if we'll have to take over this window, and it's the last */
  815.         if (bp->b_nwnd == 0 && curbp->b_nwnd != 0 &&
  816.                     --(curbp->b_nwnd) == 0) {
  817.             undispbuff(curbp,curwp);
  818.         }
  819.     }
  820.  
  821.     /* don't let make_current() call the hook -- there's
  822.         more to be done down below */
  823.     DisableBufferHook;
  824.     make_current(bp);    /* sets curbp */
  825.     EnableBufferHook;
  826.  
  827.     bp = curbp;  /* if running the bufhook caused an error, we may
  828.                 be in a different buffer than we thought
  829.                 we were going to */
  830.  
  831.     /* get it already on the screen if possible */
  832.     if (bp->b_nwnd != 0)  { /* then it's on the screen somewhere */
  833.         register WINDOW *wp = bp2any_wp(bp);
  834.         if (!wp)
  835.             mlforce("BUG: swbuffer: wp still NULL");
  836.         curwp = wp;
  837.         upmode();
  838. #ifdef MDCHK_MODTIME
  839.         (void)check_modtime( bp, bp->b_fname );
  840. #endif
  841. #if OPT_UPBUFF
  842.         if (bp != find_BufferList())
  843.             updatelistbuffers();
  844. #endif
  845.         run_buffer_hook();
  846.         return (find_bp(bp) != 0);
  847.     } else if (curwp == 0) {
  848.         return FALSE;    /* we haven't started displaying yet */
  849.     }
  850.  
  851.     /* oh well, suck it into this window */
  852.  
  853.     curwp->w_bufp  = bp;
  854.     return suckitin(bp, (bp->b_nwnd++ == 0), lockfl);
  855. }
  856.  
  857. #if NEEDED
  858. /* check to ensure any buffer that thinks it's displayed _is_ displayed */
  859. void
  860. buf_win_sanity(void)
  861. {
  862.     register BUFFER *bp;
  863.     for_each_buffer(bp) {
  864.         if (bp->b_nwnd != 0)  { /* then it's on the screen somewhere */
  865.         register WINDOW *wp = bp2any_wp(bp);
  866.         if (!wp) {
  867.             dbgwrite("BUG: swbuffer 1: wp is NULL");
  868.         }
  869.         }
  870.     }
  871. }
  872. #endif
  873.  
  874. void
  875. undispbuff(
  876. register BUFFER *bp,
  877. register WINDOW *wp)
  878. {
  879.     /* get rid of it completely if it's a scratch buffer,
  880.         or it's empty and unmodified */
  881.     if (b_is_scratch(bp)) {
  882.         /* Save the location within the help-file */
  883.         if (eql_bname(bp, HELP_BufName))
  884.             help_at = line_no(bp, wp->w_dot.l);
  885.         (void)zotbuf(bp);
  886.     } else if ( global_g_val(GMDABUFF) && !b_is_changed(bp) &&
  887.             is_empty_buf(bp) && !ffexists(bp->b_fname)) {
  888.         (void)zotbuf(bp);
  889.     } else {  /* otherwise just adjust it off the screen */
  890.         copy_traits(&(bp->b_wtraits), &(wp->w_traits));
  891.     }
  892. }
  893.  
  894. #if    !OPT_MAJORMODE
  895. /* return true iff c-mode is active for this buffer */
  896. static int
  897. cmode_active(register BUFFER *bp)
  898. {
  899.     if (is_local_b_val(bp,MDCMOD))
  900.         return is_c_mode(bp);
  901.     else
  902.         return (is_c_mode(bp) && has_C_suffix(bp));
  903. }
  904. #endif
  905.  
  906. /* return the correct tabstop setting for this buffer */
  907. int
  908. tabstop_val(register BUFFER *bp)
  909. {
  910. #if    OPT_MAJORMODE
  911.     int i = b_val(bp, VAL_TAB);
  912. #else
  913.     int i = b_val(bp, (cmode_active(bp) ? VAL_C_TAB : VAL_TAB));
  914. #endif
  915.     if (i == 0) return 1;
  916.     return i;
  917. }
  918.  
  919. /* return the correct shiftwidth setting for this buffer */
  920. int
  921. shiftwid_val(register BUFFER *bp)
  922. {
  923. #if    OPT_MAJORMODE
  924.     int i = b_val(bp, VAL_SWIDTH);
  925. #else
  926.     int i = b_val(bp, (cmode_active(bp) ? VAL_C_SWIDTH : VAL_SWIDTH));
  927. #endif
  928.     if (i == 0) return 1;
  929.     return i;
  930. }
  931.  
  932. #if    !OPT_MAJORMODE
  933. int
  934. has_C_suffix(register BUFFER *bp)
  935. {
  936.     int s;
  937.     int save = ignorecase;
  938. #if OPT_CASELESS
  939.     ignorecase = TRUE;
  940. #else
  941.     ignorecase = FALSE;
  942. #endif
  943.     s =  regexec(global_g_val_rexp(GVAL_CSUFFIXES)->reg,
  944.             bp->b_fname, (char *)0, 0, -1);
  945.     ignorecase = save;
  946.     return s;
  947. }
  948. #endif
  949.  
  950. /*
  951.  * Dispose of a buffer, by name.  If this is a screen-command, pick the name up
  952.  * from the screen.  Otherwise, ask for the name.  Look it up (don't get too
  953.  * upset if it isn't there at all!).  Get quite upset if the buffer is being
  954.  * displayed.  Clear the buffer (ask if the buffer has been changed).  Then
  955.  * free the header line and the buffer header.
  956.  *
  957.  * If we are given a repeat-count, try to kill that many buffers.  Killing from
  958.  * names selected from the buffer-list is a special case, because the cursor
  959.  * may be pointing to the buffer-number column.  In that case, we must
  960.  * recompute the buffer-contents.  Otherwise, move the cursor down one line
  961.  * (staying in the same column), so we can pick up the names from successive
  962.  * lines.
  963.  */
  964. int
  965. killbuffer(int f, int n)
  966. {
  967.     register BUFFER *bp;
  968.     register int    s;
  969.     char bufn[NFILEN];
  970.  
  971. #if OPT_UPBUFF
  972.     C_NUM    save_COL;
  973.     MARK    save_DOT;
  974.     MARK    save_TOP;
  975.     int    animated = f
  976.             && (n > 1)
  977.             && (curbp != 0)
  978.             && (curbp == find_BufferList());
  979.     int    special  = animated && (DOT.o == 2);
  980.  
  981.     if (animated && !special) {
  982.         save_COL = getccol(FALSE);
  983.         save_DOT = DOT;
  984.         save_TOP = curwp->w_line;
  985.     } else
  986.         save_COL = 0;    /* appease gcc */
  987. #endif
  988.  
  989.     if (!f)
  990.         n = 1;
  991.     for_ever {
  992.         bufn[0] = EOS;
  993.         if (clexec || isnamedcmd) {
  994.             if ((s=mlreply("Kill buffer: ", bufn, sizeof(bufn))) != TRUE)
  995.                 break;
  996.         } else if ((s = screen_to_bname(bufn)) != TRUE) {
  997.             mlforce("[Nothing selected]");
  998.             break;
  999.         }
  1000.  
  1001.         if ((bp=find_any_buffer(bufn)) == 0) {    /* Try buffer */
  1002.             s = FALSE;
  1003.             break;
  1004.         }
  1005.  
  1006.         if ((curbp == bp) && (find_alt() == 0)) {
  1007.             mlforce("[Can't kill that buffer]");
  1008.             s = FALSE;
  1009.             break;
  1010.         }
  1011.  
  1012.         (void)strcpy(bufn, bp->b_bname); /* ...for info-message */
  1013.         if (bp->b_nwnd != 0)  { /* then it's on the screen somewhere */
  1014.             (void)zotwp(bp);
  1015.             if (find_bp(bp) == 0) { /* delwp must have zotted us */
  1016.                 s = FALSE;
  1017.                 break;
  1018.             }
  1019.         }
  1020.         if ((s = zotbuf(bp)) != TRUE)
  1021.             break;
  1022.         mlwrite("Buffer %s gone", bufn);
  1023.         if (--n > 0) {
  1024. #if OPT_UPBUFF
  1025.             if (special)
  1026.                 (void)update(TRUE);
  1027.             else
  1028. #endif
  1029.              if (!forwline(FALSE,1))
  1030.                 break;
  1031.         } else
  1032.             break;
  1033.     }
  1034.  
  1035. #if OPT_UPBUFF
  1036.     if (animated && !special) {
  1037.         curgoal = save_COL;
  1038.         DOT     = save_DOT;
  1039.         DOT.o   = getgoal(DOT.l);
  1040.         curwp->w_line = save_TOP;
  1041.         curwp->w_flag &= (USHORT) ~WFMOVE;
  1042.     }
  1043. #endif
  1044.     return s;
  1045. }
  1046.  
  1047. /*
  1048.  * Unlink a buffer from the list, adjusting last-used and created counts.
  1049.  */
  1050. int
  1051. delink_bp(BUFFER *bp)
  1052. {
  1053.     register BUFFER *bp1, *bp2;
  1054.  
  1055.     bp1 = NULL;                /* Find the header.    */
  1056.     bp2 = bheadp;
  1057.     while (bp2 != bp) {
  1058.         bp1 = bp2;
  1059.         bp2 = bp2->b_bufp;
  1060.         if (bp2 == 0)
  1061.             return FALSE;
  1062.     }
  1063.     bp2 = bp2->b_bufp;            /* Next one in chain.    */
  1064.     if (bp1 == NULL)            /* Unlink it.        */
  1065.         bheadp = bp2;
  1066.     else
  1067.         bp1->b_bufp = bp2;
  1068.     MarkUnused(bp);
  1069.     MarkDeleted(bp);
  1070.     if (bp == last_bp)
  1071.         last_bp = 0;
  1072.     return TRUE;
  1073. }
  1074.  
  1075. char *
  1076. strip_brackets(char *dst, const char *src)
  1077. {
  1078.     size_t len;
  1079.  
  1080.     if (*src == SCRTCH_LEFT[0])
  1081.         src++;
  1082.     (void) strcpy(dst, src);
  1083.     if ((len = strlen(dst)) != 0
  1084.      && dst[len-1] == SCRTCH_RIGHT[0]) {
  1085.         dst[--len] = EOS;
  1086.     }
  1087.     return dst;
  1088. }
  1089.  
  1090. char *
  1091. add_brackets(char *dst, const char *src)
  1092. {
  1093.     dst[0] = SCRTCH_LEFT[0];
  1094.     (void)strcat(strncpy(&dst[1], src, NBUFN-3), SCRTCH_RIGHT);
  1095.     return dst;
  1096. }
  1097.  
  1098. int
  1099. zotbuf(register BUFFER *bp)    /* kill the buffer pointed to by bp */
  1100. {
  1101.     register int    s;
  1102.     register int    didswitch = FALSE;
  1103.  
  1104.     if (find_bp(bp) == 0)     /* delwp may have zotted us, pointer obsolete */
  1105.         return TRUE;
  1106.  
  1107.     TRACE(("zotbuf(%s)\n", bp->b_bname))
  1108.  
  1109. #define no_del
  1110. #ifdef no_del
  1111.     if (bp->b_nwnd != 0) {            /* Error if on screen.    */
  1112.         mlforce("[Buffer is being displayed]");
  1113.         return (FALSE);
  1114.     }
  1115.     if (is_fake_window(wheadp))  {
  1116.         WINDOW *wp;
  1117.         WINDOW dummy;
  1118.         /* Not on screen, but a fake window might refer to it.  So
  1119.            delete all such fake windows */
  1120.         for_each_window(wp) {
  1121.             if (is_fake_window(wp) 
  1122.              && wp->w_bufp == bp && wheadp->w_wndp != NULL) {
  1123.                 dummy.w_wndp = wp->w_wndp;
  1124.                 s = delwp(wp);
  1125.                 wp = &dummy;
  1126.             }
  1127.         }
  1128.     }
  1129. #else
  1130.     if (curbp == bp) {
  1131.         didswitch = TRUE;
  1132.         if (find_alt() == 0) {
  1133.             mlforce("[Can't kill that buffer]");
  1134.             return FALSE;
  1135.         }
  1136.     }
  1137.     if (bp->b_nwnd != 0 || is_fake_window(wheadp))  {
  1138.                     /* then it's on the screen somewhere 
  1139.                    or there are fake windows to worry about */
  1140.         (void)zotwp(bp);
  1141.         if (find_bp(bp) == 0) /* delwp must have zotted us */
  1142.             return TRUE;
  1143.     }
  1144.  
  1145. #endif
  1146. #if OPT_LCKFILES
  1147.     /* If Buffer is killed and not locked by other then release own lock */
  1148.     if ( global_g_val(GMDUSEFILELOCK) ) {
  1149.         if ( bp->b_active )
  1150.              if (!b_val (curbp, MDLOCKED) && !b_val (curbp, MDVIEW))
  1151.                 release_lock(bp->b_fname);
  1152.     }
  1153. #endif
  1154.     /* Blow text away.    */
  1155.     if ((s=bclear(bp)) != TRUE) {
  1156.         /* the user must have answered no */
  1157.         if (didswitch)
  1158.             (void)swbuffer(bp);
  1159.     } else {
  1160. #if OPT_NAMEBST
  1161.         if (is_scratchname(bp->b_bname)) {
  1162.             char procname[NBUFN];
  1163.             delete_namebst(strip_brackets(procname, bp->b_bname),
  1164.                     TRUE);
  1165.         }
  1166. #endif
  1167.         FreeBuffer(bp);
  1168.         updatelistbuffers();
  1169.     }
  1170.     return (s);
  1171. }
  1172.  
  1173.  
  1174. /* Rename a buffer given a buffer pointer and new buffer name. */
  1175. int
  1176. renamebuffer(BUFFER *rbp, char *bufname)
  1177. {
  1178.     register BUFFER *bp;    /* pointer to scan through all buffers */
  1179.     char bufn[NBUFN];    /* buffer to hold buffer name */
  1180.     WINDOW *wp;
  1181.  
  1182.     strncpy(bufn, bufname, NBUFN-1);
  1183.     bufn[NBUFN-1] = 0;
  1184.  
  1185.     if (*mktrimmed(bufn) == EOS)
  1186.         return(ABORT);
  1187.  
  1188.     bp = find_b_name(bufn);
  1189.  
  1190.     if (bp == curbp)
  1191.         return(ABORT);        /* no change */
  1192.  
  1193.     if (bp != 0)
  1194.         return FALSE;        /* name already in use */
  1195.  
  1196. #if OPT_NAMEBST
  1197.     if (is_scratchname(rbp->b_bname)) {
  1198.         char procname[NBUFN];
  1199.         (void) strip_brackets(procname, rbp->b_bname);
  1200.         if (search_namebst(procname)
  1201.          && rename_namebst(procname, bufn) != TRUE)
  1202.             return ABORT;
  1203.     }
  1204. #endif
  1205.     set_bname(rbp, bufn);    /* copy buffer name to structure */
  1206.  
  1207.     for_each_visible_window(wp)
  1208.         if (wp->w_bufp == rbp)
  1209.             wp->w_flag |= WFMODE;
  1210.  
  1211.     return(TRUE);
  1212. }
  1213.  
  1214. #define NEW_NAMEBUFFER 1
  1215. #if NEW_NAMEBUFFER
  1216. /* ARGSUSED */
  1217. int
  1218. namebuffer(int f GCC_UNUSED, int n GCC_UNUSED)    /*    Rename the current buffer    */
  1219. {
  1220.     static char bufn[NBUFN];    /* buffer to hold buffer name */
  1221.     const char *prompt = "New name for buffer: ";
  1222.     int status;
  1223.  
  1224.     /* prompt for and get the new buffer name */
  1225.     do {
  1226.         if (mlreply(prompt, bufn, sizeof(bufn)) != TRUE)
  1227.             return(FALSE);
  1228.         if (*mktrimmed(bufn) == EOS)
  1229.             return(FALSE);
  1230.         prompt = "That name's been used.  New name: ";
  1231.         status = renamebuffer(curbp, bufn);
  1232.         if (status == ABORT)
  1233.             return(FALSE);
  1234.     } while (status == FALSE);
  1235.  
  1236.     b_clr_scratch(curbp);    /* if renamed, we must want it */
  1237.     mlerase();
  1238.     updatelistbuffers();
  1239.     return(TRUE);
  1240. }
  1241. #else
  1242. /* ARGSUSED */
  1243. int
  1244. namebuffer(int f GCC_UNUSED, int n GCC_UNUSED)    /*    Rename the current buffer    */
  1245. {
  1246.     register BUFFER *bp;    /* pointer to scan through all buffers */
  1247.     static char bufn[NBUFN];    /* buffer to hold buffer name */
  1248.     const char *prompt = "New name for buffer: ";
  1249.  
  1250.     /* prompt for and get the new buffer name */
  1251.     do {
  1252.         if (mlreply(prompt, bufn, sizeof(bufn)) != TRUE)
  1253.             return(FALSE);
  1254.         if (*mktrimmed(bufn) == EOS)
  1255.             return(FALSE);
  1256.         prompt = "That name's been used.  New name: ";
  1257.         bp = find_b_name(bufn);
  1258.         if (bp == curbp)    /* no change */
  1259.             return(FALSE);
  1260.     } while (bp != 0);
  1261.  
  1262. #if OPT_NAMEBST
  1263.     if (is_scratchname(curbp->b_bname)) {
  1264.         char procname[NBUFN];
  1265.         (void) strip_brackets(procname, curbp->b_bname);
  1266.         if (rename_namebst(procname, bufn) != TRUE)
  1267.             return FALSE;
  1268.     }
  1269. #endif
  1270.     set_bname(curbp, bufn);    /* copy buffer name to structure */
  1271.     curwp->w_flag |= WFMODE;/* make mode line replot */
  1272.     b_clr_scratch(curbp);    /* if renamed, we must want it */
  1273.     mlerase();
  1274.     updatelistbuffers();
  1275.     return(TRUE);
  1276. }
  1277. #endif /* NEW_NAMEBUFFER */
  1278.  
  1279. /* create or find a window, and stick this buffer in it.  when
  1280.     done, we own the window and the buffer, but they are _not_
  1281.     necessarily curwp and curbp */
  1282. int
  1283. popupbuff(BUFFER *bp)
  1284. {
  1285.     register WINDOW *wp;
  1286.  
  1287.     if (!curbp) {
  1288.         curbp = bp;  /* possibly at startup time */
  1289.         curwp->w_bufp = curbp;
  1290.         ++curbp->b_nwnd;
  1291.     } else if (bp->b_nwnd == 0) {    /* Not on screen yet. */
  1292.         if ((wp = wpopup()) == NULL)
  1293.             return FALSE;
  1294.         if (--wp->w_bufp->b_nwnd == 0)
  1295.             undispbuff(wp->w_bufp,wp);
  1296.         wp->w_bufp  = bp;
  1297.         ++bp->b_nwnd;
  1298.     }
  1299.  
  1300.     for_each_window(wp) {
  1301.         if (wp->w_bufp == bp) {
  1302.             wp->w_line.l = lforw(buf_head(bp));
  1303.             wp->w_dot.l  = lforw(buf_head(bp));
  1304.             wp->w_dot.o  = 0;
  1305. #if WINMARK
  1306.             wp->w_mark = nullmark;
  1307. #endif
  1308.             wp->w_lastdot = nullmark;
  1309.             wp->w_values = global_w_values;
  1310.             wp->w_flag |= WFMODE|WFHARD;
  1311.         }
  1312.     }
  1313.     return swbuffer(bp);
  1314. }
  1315.  
  1316. /*
  1317.  * Invoked after changing mode 'autobuffer', this relinks the list of buffers
  1318.  * sorted according to the mode: by creation or last-used order.
  1319.  */
  1320. void
  1321. sortlistbuffers(void)
  1322. {
  1323.     register BUFFER *bp, *newhead = 0;
  1324.     register int    c;
  1325.  
  1326.     if (global_g_val(GMDABUFF)) {
  1327.         c = 1;
  1328.         while ((bp = find_nth_used(c++)) != 0) {
  1329.             bp->b_relink = newhead;
  1330.             newhead = bp;
  1331.         }
  1332.     } else {
  1333.         c = countBuffers();
  1334.         while ((bp = find_nth_created(c--)) != 0) {
  1335.             bp->b_relink = newhead;
  1336.             newhead = bp;
  1337.         }
  1338.     }
  1339.  
  1340.     for (bp = newhead; bp; bp = bp->b_relink)
  1341.         bp->b_bufp = bp->b_relink;
  1342.     bheadp = newhead;
  1343.  
  1344.     updatelistbuffers();
  1345. }
  1346.  
  1347. /*
  1348.  * List all of the active buffers.  First update the special buffer that holds
  1349.  * the list.  Next make sure at least 1 window is displaying the buffer list,
  1350.  * splitting the screen if this is what it takes.  Lastly, repaint all of the
  1351.  * windows that are displaying the list.  A numeric argument forces it to
  1352.  * toggle the listing invisible buffers as well (a subsequent argument forces
  1353.  * it to a normal listing).
  1354.  */
  1355. int
  1356. togglelistbuffers(int f, int n)
  1357. {
  1358.     int status;
  1359.     register BUFFER *bp;
  1360.  
  1361.     /* if it doesn't exist, create it */
  1362.     if ((bp = find_BufferList()) == NULL) {
  1363.         status = listbuffers(f,n);
  1364.     /* if user supplied argument, re-create */
  1365.     } else if (f) {
  1366.         status = listbuffers(!show_all,n);
  1367.     } else {
  1368.         /* if it does exist, delete the window, which in turn
  1369.            will kill the BFSCRTCH buffer */
  1370.         status = zotwp(bp);
  1371.     }
  1372.  
  1373.     return status;
  1374. }
  1375.  
  1376. /*
  1377.  * Track/emit footnotes for 'makebufflist()', showing only the ones we use.
  1378.  */
  1379. #if !SMALLER
  1380. static void
  1381. footnote(int c)
  1382. {
  1383.     static    struct    {
  1384.         const char *name;
  1385.         int    flag;
  1386.     } table[] = {
  1387.         {"automatic",    0},
  1388.         {"invisible",    0},
  1389.         {"modified",    0},
  1390.         {"scratch",    0},
  1391.         {"unread",    0},
  1392.         };
  1393.     register SIZE_T    j, next;
  1394.  
  1395.     for (j = next = 0; j < TABLESIZE(table); j++) {
  1396.         if (c != 0) {
  1397.             if (table[j].name[0] == c) {
  1398.                 bputc(c);
  1399.                 table[j].flag = TRUE;
  1400.                 break;
  1401.             }
  1402.         } else if (table[j].flag) {
  1403.             bprintf("%s '%c'%s %s",
  1404.                 next ? "," : "notes:",    table[j].name[0],
  1405.                 next ? ""  : " is",    table[j].name);
  1406.             next++;
  1407.             table[j].flag = 0;
  1408.         }
  1409.     }
  1410.     if (next)
  1411.         bputc('\n');
  1412. }
  1413. #define    MakeNote(c)    footnote(c)
  1414. #define    ShowNotes()    footnote(0)
  1415. #else
  1416. #define    MakeNote(c)    bputc(c)
  1417. #define    ShowNotes()
  1418. #endif
  1419.  
  1420. /*
  1421.  * This routine rebuilds the text in the buffer that holds the buffer list.  It
  1422.  * is called by the list buffers command.  Return TRUE if everything works.
  1423.  * Return FALSE if there is an error (if there is no memory).  The variable
  1424.  * 'show_all' (set if the command had a repeat-count) indicates whether to list
  1425.  * hidden buffers.
  1426.  */
  1427. /* ARGSUSED */
  1428. static void
  1429. makebufflist(
  1430.     int iflag GCC_UNUSED,    /* list hidden buffer flag */
  1431.     void *dummy GCC_UNUSED)
  1432. {
  1433.     register BUFFER *bp;
  1434.     LINEPTR curlp;        /* entry corresponding to buffer-list */
  1435.     int nbuf = 0;        /* no. of buffers */
  1436.     int this_or_that;
  1437.  
  1438.     curlp = null_ptr;
  1439.  
  1440.     if (this_bp == 0)
  1441.         this_bp = curbp;
  1442.     if (this_bp == that_bp)
  1443.         that_bp = find_alt();
  1444.  
  1445.     bprintf("      %7s %*s %s\n", "Size",NBUFN-1,"Buffer name","Contents");
  1446.     bprintf("      %7p %*p %30p\n", '-',NBUFN-1,'-','-');
  1447.  
  1448.     /* output the list of buffers */
  1449.     for_each_buffer(bp) {
  1450.         /* skip those buffers which don't get updated when changed */
  1451. #if    OPT_UPBUFF
  1452.         if (!update_on_chg(bp)) {
  1453.             continue;
  1454.         }
  1455. #endif
  1456.  
  1457.         /* output status flag (e.g., has the file been read in?) */
  1458.         if (b_is_scratch(bp))
  1459.             MakeNote('s');
  1460.         else if (!(bp->b_active))
  1461.             MakeNote('u');
  1462.         else if (b_is_implied(bp))
  1463.             MakeNote('a');
  1464.         else if (b_is_invisible(bp))
  1465.             MakeNote('i');
  1466.         else if (b_is_changed(bp))
  1467.             MakeNote('m');
  1468.         else
  1469.             bputc(' ');
  1470.  
  1471.         this_or_that = (bp == this_bp)
  1472.             ? EXPC_THIS
  1473.             : (bp == that_bp)
  1474.                 ? EXPC_THAT
  1475.                 : ' ';
  1476.  
  1477.         if (b_is_temporary(bp))
  1478.             bprintf("   %c ", this_or_that);
  1479.         else
  1480.             bprintf(" %2d%c ", nbuf++, this_or_that);
  1481.  
  1482.         (void)bsizes(bp);
  1483.         bprintf("%7ld %*S ", bp->b_bytecount, NBUFN-1, bp->b_bname );
  1484.         {
  1485.             char    temp[NFILEN];
  1486.             char    *p;
  1487.  
  1488.             if ((p = bp->b_fname) != 0)
  1489.                 p = shorten_path(strcpy(temp, p), TRUE);
  1490.  
  1491.             if (p != 0)
  1492.                 bprintf("%s",p);
  1493.         }
  1494.         bprintf("\n");
  1495.         if (bp == curbp)
  1496.             curlp = lback(lback(buf_head(curbp)));
  1497.     }
  1498.     ShowNotes();
  1499.     bprintf("             %*s %s", NBUFN-1, "Current dir:",
  1500.         current_directory(FALSE));
  1501.  
  1502.     /* show the actual size of the buffer-list */
  1503.     if (curlp != null_ptr) {
  1504.         char    temp[20];
  1505.         (void)bsizes(curbp);
  1506.         (void)lsprintf(temp, "%7ld", curbp->b_bytecount);
  1507.         (void)memcpy(curlp->l_text + 6, temp,
  1508.                      (SIZE_T)strlen(temp));
  1509.     }
  1510. }
  1511.  
  1512. #if    OPT_UPBUFF
  1513. /*
  1514.  * (Re)compute the contents of the buffer-list.  Use the flag 'updating_list'
  1515.  * as a semaphore to avoid adjusting the last used/created indices while
  1516.  * cycling over the list of buffers.
  1517.  */
  1518. /*ARGSUSED*/
  1519. static int
  1520. show_BufferList(BUFFER *bp GCC_UNUSED)
  1521. {
  1522.     register int    status;
  1523.     if ((status = (!updating_list++ != 0)) != FALSE) {
  1524.         this_bp = curbp;
  1525.         that_bp = find_alt();
  1526.         status = liststuff(BUFFERLIST_BufName, FALSE,
  1527.                 makebufflist, 0, (void *)0);
  1528.     }
  1529.     updating_list--;
  1530.     return status;
  1531. }
  1532.  
  1533. /*
  1534.  * If the list-buffers window is visible, update it after operations that
  1535.  * would modify the list.
  1536.  */
  1537. void
  1538. updatelistbuffers(void)
  1539. {
  1540.     update_scratch(BUFFERLIST_BufName, show_BufferList);
  1541. }
  1542.  
  1543. /* mark a scratch/temporary buffer for update */
  1544. void
  1545. update_scratch(const char *name, int (*func)(BUFFER *))
  1546. {
  1547.     register BUFFER *bp = find_b_name(name);
  1548.  
  1549.     if (bp != 0) {
  1550.         bp->b_upbuff = func;
  1551.         b_set_obsolete(bp);
  1552.     }
  1553. }
  1554. #endif    /* OPT_UPBUFF */
  1555.  
  1556. /* ARGSUSED */
  1557. int
  1558. listbuffers(int f GCC_UNUSED, int n GCC_UNUSED)
  1559. {
  1560. #if    OPT_UPBUFF
  1561.     register int status;
  1562.  
  1563.     show_all = f;    /* save this to use in automatic updating */
  1564.     if (find_BufferList() != 0) {
  1565.         updatelistbuffers();
  1566.         return TRUE;
  1567.     }
  1568.     this_bp = 0;
  1569.     that_bp = curbp;
  1570.     status  = liststuff(BUFFERLIST_BufName, FALSE,
  1571.                 makebufflist, 0, (void *)0);
  1572.     b_clr_obsolete(curbp);
  1573.     return status;
  1574. #else
  1575.     show_all = f;
  1576.     this_bp = 0;
  1577.     that_bp = curbp;
  1578.     return liststuff(BUFFERLIST_BufName, FALSE,
  1579.                 makebufflist, 0, (void *)0);
  1580. #endif
  1581. }
  1582.  
  1583. /*
  1584.  * The argument "text" points to
  1585.  * a string. Append this line to the
  1586.  * buffer. Handcraft the EOL
  1587.  * on the end. Return TRUE if it worked and
  1588.  * FALSE if you ran out of room.
  1589.  */
  1590. int
  1591. addline(register BUFFER *bp, const char *text, int len)
  1592. {
  1593.     if (add_line_at (bp, lback(buf_head(bp)), text, len) == TRUE) {
  1594.         /* If "." is at the end, move it to new line  */
  1595.         if (sameline(bp->b_dot, bp->b_line))
  1596.             bp->b_dot.l = lback(buf_head(bp));
  1597.         return TRUE;
  1598.     }
  1599.     return FALSE;
  1600. }
  1601.  
  1602. /*
  1603.  * Add a LINE filled with the given text after the specified LINE.
  1604.  */
  1605. int
  1606. add_line_at (
  1607. register BUFFER    *bp,
  1608. LINEPTR    prevp,
  1609. const char *text,
  1610. int    len)
  1611. {
  1612.     register LINEPTR newlp;
  1613.     register LINEPTR nextp;
  1614.     register LINE *    lp;
  1615.     register int    ntext;
  1616.  
  1617.     nextp = lforw(prevp);
  1618.     ntext = (len < 0) ? strlen(text) : len;
  1619.     newlp = lalloc(ntext, bp);
  1620.     if (newlp == null_ptr)
  1621.         return (FALSE);
  1622.  
  1623.     lp = newlp;
  1624.     if (ntext > 0)
  1625.         (void)memcpy(lp->l_text, text, (SIZE_T)ntext);
  1626.  
  1627.     /* try to maintain byte/line counts? */
  1628.     if (b_is_counted(bp)) {
  1629.         if (nextp == buf_head(bp)) {
  1630.             make_local_b_val(bp,MDNEWLINE);
  1631.             set_b_val(bp, MDNEWLINE, TRUE);
  1632.             bp->b_bytecount += (ntext+1);
  1633.             bp->b_linecount += 1;
  1634. #if !SMALLER        /* tradeoff between codesize & data */
  1635.             lp->l_number = bp->b_linecount;
  1636. #endif
  1637.         } else
  1638.             b_clr_counted(bp);
  1639.     }
  1640.  
  1641.     set_lforw(prevp, newlp);    /* link into the buffer */
  1642.     set_lback(newlp, prevp);
  1643.     set_lback(nextp, newlp);
  1644.     set_lforw(newlp, nextp);
  1645.  
  1646.     return (TRUE);
  1647. }
  1648.  
  1649. /*
  1650.  * Look through the list of
  1651.  * buffers. Return TRUE if there
  1652.  * are any changed buffers. Buffers
  1653.  * that hold magic internal stuff are
  1654.  * not considered; who cares if the
  1655.  * list of buffer names is hacked.
  1656.  * Return FALSE if no buffers
  1657.  * have been changed.
  1658.  * Return a pointer to the first changed buffer,
  1659.  * in case there's only one -- it's useful info
  1660.  * then.
  1661.  */
  1662. int
  1663. any_changed_buf(BUFFER **bpp)
  1664. {
  1665.     register BUFFER *bp;
  1666.     register int cnt = 0;
  1667.     if (bpp) *bpp = NULL;
  1668.  
  1669.     for_each_buffer(bp) {
  1670.         if (!b_is_invisible(bp) && b_is_changed(bp)) {
  1671.                 if (bpp && !*bpp)
  1672.                 *bpp = bp;
  1673.             cnt++;
  1674.         }
  1675.     }
  1676.     return (cnt);
  1677. }
  1678.  
  1679. /* similar to above, for buffers that have not been visited */
  1680. int
  1681. any_unread_buf(BUFFER **bpp)
  1682. {
  1683.     register BUFFER *bp;
  1684.     register int cnt = 0;
  1685.     if (bpp) *bpp = NULL;
  1686.  
  1687.     for_each_buffer(bp) {
  1688.         if (!b_is_invisible(bp) && !bp->b_active) {
  1689.                 if (bpp && !*bpp)
  1690.                 *bpp = bp;
  1691.             cnt++;
  1692.         }
  1693.     }
  1694.     return (cnt);
  1695. }
  1696.  
  1697. /*
  1698.  * Copies string to a buffer-name, trimming trailing blanks for consistency.
  1699.  */
  1700. void
  1701. set_bname(BUFFER *bp, const char *name)
  1702. {
  1703.     register int j, k;
  1704.     register char *d;
  1705.  
  1706.     (void)strncpy0(bp->b_bname, name, NBUFN);
  1707.  
  1708.     d = bp->b_bname;
  1709.     for (j = 0, k = -1; d[j]; j++) {
  1710.         if (!isSpace(d[j]))
  1711.             k = -1;
  1712.         else if (k < 0)
  1713.             k = j;
  1714.     }
  1715.     if (k >= 0)
  1716.         d[k] = EOS;
  1717. }
  1718.  
  1719. #if BEFORE
  1720. /*
  1721.  * Copies buffer-name to a null-terminated buffer (for use in code that
  1722.  * cannot conveniently use the name without a null-termination).
  1723.  */
  1724. char *
  1725. XXX still needed XXX get_bname(BUFFER *bp)
  1726. {
  1727.     static    char    bname[NBUFN];
  1728.     if (bp) {
  1729.         (void)strncpy0(bname, bp->b_bname, NBUFN);
  1730.         bname[NBUFN] = EOS;
  1731.     } else {
  1732.         *bname = EOS;
  1733.     }
  1734.     return bname;
  1735. }
  1736. #endif
  1737.  
  1738. /*
  1739.  * Look for a buffer-name in the buffer-list.  This assumes that the argument
  1740.  * is null-terminated.  If the length is longer than will fit in a b_bname
  1741.  * field, then it is probably a filename.
  1742.  */
  1743. BUFFER *
  1744. find_b_name(const char *bname)
  1745. {
  1746.     register BUFFER *bp;
  1747.     BUFFER    temp;
  1748.  
  1749.     set_bname(&temp, bname); /* make a canonical buffer-name */
  1750.  
  1751.     for_each_buffer(bp)
  1752.         if (eql_bname(bp, temp.b_bname))
  1753.             return bp;
  1754.     return 0;
  1755. }
  1756.  
  1757. /*
  1758.  * Find a buffer, by name. Return a pointer
  1759.  * to the BUFFER structure associated with it.
  1760.  * If the buffer is not found, create it. The "bflag" is
  1761.  * the settings for the flags in in buffer.
  1762.  */
  1763. BUFFER    *
  1764. bfind(const char *bname, UINT bflag)
  1765. {
  1766.     register BUFFER *bp;
  1767.     register LINEPTR lp;
  1768.     register BUFFER *lastb = NULL;    /* buffer to insert after */
  1769.     register BUFFER *bp2;
  1770.  
  1771.     for_each_buffer(bp) {
  1772.         if (eql_bname(bp, bname))
  1773.             return (bp);
  1774.         lastb = bp;
  1775.     }
  1776.  
  1777.     /* set everything to 0's unless we want nonzero */
  1778.     if ((bp = typecalloc(BUFFER)) == NULL) {
  1779.         (void)no_memory("BUFFER");
  1780.         return (NULL);
  1781.     }
  1782.  
  1783.     /* set this first, to make it simple to trace */
  1784.     set_bname(bp, bname);
  1785.  
  1786.     lp = lalloc(0, bp);
  1787.     if (lp == null_ptr) {
  1788.         free((char *) bp);
  1789.         (void)no_memory("BUFFER head");
  1790.         return (NULL);
  1791.     }
  1792.  
  1793.     /* and set up the other buffer fields */
  1794.     bp->b_values = global_b_values;
  1795.     bp->b_wtraits.w_vals = global_w_values;
  1796.     bp->b_active = FALSE;
  1797.     bp->b_dot.l  = lp;
  1798.     bp->b_dot.o  = 0;
  1799.     bp->b_wline  = bp->b_dot;
  1800.     bp->b_line   = bp->b_dot;
  1801. #if WINMARK
  1802.     bp->b_mark = nullmark;
  1803. #endif
  1804.     bp->b_lastdot = nullmark;
  1805. #if OPT_VIDEO_ATTRS
  1806. #endif
  1807.     bp->b_flag  = bflag;
  1808.     bp->b_acount = b_val(bp, VAL_ASAVECNT);
  1809.     bp->b_fname = NULL;
  1810.     ch_fname(bp, "");
  1811. #if    OPT_ENCRYPT
  1812.     if (!b_is_temporary(bp)
  1813.      && cryptkey != 0 && *cryptkey != EOS) {
  1814.         (void)strcpy(bp->b_key, cryptkey);
  1815.         make_local_b_val(bp, MDCRYPT);
  1816.         set_b_val(bp, MDCRYPT, TRUE);
  1817.     } else
  1818.         bp->b_key[0] = EOS;
  1819. #endif
  1820.     bp->b_udstks[0] = bp->b_udstks[1] = null_ptr;
  1821.     bp->b_ulinep = null_ptr;
  1822.     bp->b_udtail = null_ptr;
  1823.     bp->b_udlastsep = null_ptr;
  1824.  
  1825.     b_set_counted(bp);    /* buffer is empty */
  1826.     set_lforw(lp, lp);
  1827.     set_lback(lp, lp);
  1828.  
  1829.     /* append at the end */
  1830.     if (lastb)
  1831.         lastb->b_bufp = bp;
  1832.     else
  1833.         bheadp = bp;
  1834.     bp->b_bufp = NULL;
  1835.     bp->b_created = countBuffers();
  1836.  
  1837.     for_each_buffer(bp2)
  1838.         bp2->b_last_used += 1;
  1839.     bp->b_last_used = 1;
  1840.  
  1841. #if OPT_PERL || OPT_TCL
  1842.     bp->b_api_private = 0;
  1843. #endif
  1844.  
  1845.     return (bp);
  1846. }
  1847.  
  1848. /*
  1849.  * Given a filename, set up a buffer pointer to correspond
  1850.  */
  1851. BUFFER *
  1852. make_bp (const char *fname, UINT flags)
  1853. {
  1854.     BUFFER *bp;
  1855.     char bname[NBUFN];
  1856.  
  1857.     makename(bname, fname);
  1858.     unqname(bname);
  1859.  
  1860.     if ((bp = bfind(bname, flags)) != 0)
  1861.         ch_fname(bp, fname);
  1862.     return bp;
  1863. }
  1864.  
  1865. /*
  1866.  * This routine blows away all of the text
  1867.  * in a buffer. If the buffer is marked as changed
  1868.  * then we ask if it is ok to blow it away; this is
  1869.  * to save the user the grief of losing text. The
  1870.  * window chain is nearly always wrong if this gets
  1871.  * called; the caller must arrange for the updates
  1872.  * that are required. Return TRUE if everything
  1873.  * looks good.
  1874.  */
  1875. int
  1876. bclear(register BUFFER *bp)
  1877. {
  1878.     register LINEPTR lp;
  1879.  
  1880.     if (!b_is_temporary(bp)        /* Not invisible or scratch */
  1881.      &&  b_is_changed(bp)) {    /* Something changed    */
  1882.         char ques[30+NBUFN];
  1883.         (void)strcat(strcpy(ques,"Discard changes to "), bp->b_bname);
  1884.         if (mlyesno(ques) != TRUE)
  1885.             return FALSE;
  1886.     }
  1887. #if OPT_UPBUFF
  1888.     if (bp->b_rmbuff != 0)
  1889.         (bp->b_rmbuff)(bp);
  1890. #endif
  1891.     b_clr_changed(bp);        /* Not changed        */
  1892.  
  1893.     freeundostacks(bp,TRUE);    /* do this before removing lines */
  1894.     FreeAndNull(bp->b_nmmarks);     /* free the named marks */
  1895. #if OPT_SELECTIONS
  1896.     free_attribs(bp);
  1897. #endif
  1898.  
  1899.     while ((lp=lforw(buf_head(bp))) != buf_head(bp)) {
  1900.         lremove(bp,lp);
  1901.         lfree(lp,bp);
  1902.     }
  1903.  
  1904.     if (bp->b_ulinep != null_ptr) {
  1905.         lfree(bp->b_ulinep, bp);
  1906.         bp->b_ulinep = null_ptr;
  1907.     }
  1908.     FreeAndNull(bp->b_ltext);
  1909.     bp->b_ltext_end = NULL;
  1910.  
  1911.     FreeAndNull(bp->b_LINEs);
  1912.     bp->b_LINEs_end = NULL;
  1913.  
  1914.     bp->b_freeLINEs = NULL;
  1915.  
  1916.     bp->b_dot  = bp->b_line;    /* Fix "."        */
  1917. #if WINMARK
  1918.     bp->b_mark = nullmark;        /* Invalidate "mark"    */
  1919. #endif
  1920.     bp->b_lastdot = nullmark;    /* Invalidate "mark"    */
  1921.  
  1922.     b_set_counted(bp);
  1923.     bp->b_bytecount = 0;
  1924.     bp->b_linecount = 0;
  1925.  
  1926.     free_local_vals(b_valnames, global_b_values.bv, bp->b_values.bv);
  1927.  
  1928.     return (TRUE);
  1929. }
  1930.  
  1931. /*
  1932.  * Update the counts for the # of bytes and lines, to use in various display
  1933.  * commands.
  1934.  */
  1935. int
  1936. bsizes(BUFFER *bp)
  1937. {
  1938.     register LINE    *lp;        /* current line */
  1939.     register B_COUNT numchars = 0;    /* # of chars in file */
  1940.     register L_NUM   numlines = 0;    /* # of lines in file */
  1941.  
  1942.     if (b_is_counted(bp))
  1943.         return FALSE;
  1944.  
  1945.     /* count chars and lines */
  1946.     for_each_line(lp,bp) {
  1947.         ++numlines;
  1948.         numchars += llength(lp) + 1;
  1949. #if !SMALLER    /* tradeoff between codesize & data */
  1950.         lp->l_number = numlines;
  1951. #endif
  1952.     }
  1953.     if (!b_val(bp,MDNEWLINE))
  1954.         numchars--;
  1955.  
  1956.     bp->b_bytecount = numchars;
  1957.     bp->b_linecount = numlines;
  1958.     b_set_counted(bp);
  1959.     return TRUE;
  1960. }
  1961.  
  1962. /*
  1963.  * Mark a buffer 'changed'
  1964.  */
  1965. void
  1966. chg_buff(register BUFFER *bp, register USHORT flag)
  1967. {
  1968.     register WINDOW *wp;
  1969.  
  1970.     if (bp == bminip)
  1971.         return;
  1972.  
  1973.     b_clr_counted(bp);
  1974.     b_match_attrs_dirty(bp);
  1975.     if (bp->b_nwnd != 1)        /* Ensure hard.        */
  1976.         flag |= WFHARD;
  1977.     if (!b_is_changed(bp)) {    /* First change, so    */
  1978.         flag |= WFMODE;        /* update mode lines.    */
  1979.         b_set_changed(bp);
  1980.     }
  1981. #if    OPT_UPBUFF
  1982.     if (update_on_chg(bp))
  1983.         updatelistbuffers();
  1984. #endif
  1985.     for_each_visible_window(wp)
  1986.         if (wp->w_bufp == bp) {
  1987.             wp->w_flag |= flag;
  1988. #ifdef WMDLINEWRAP
  1989.             /* The change may affect the line-height displayed
  1990.              * on the screen.  Assume the worst-case.
  1991.              */
  1992.             if ((flag & WFEDIT) && w_val(wp,WMDLINEWRAP))
  1993.                 wp->w_flag |= WFHARD;
  1994. #endif
  1995.         }
  1996. }
  1997.  
  1998. /*
  1999.  * Mark a buffer 'unchanged'
  2000.  */
  2001. void
  2002. unchg_buff(register BUFFER *bp, register USHORT flag)
  2003. {
  2004.     register WINDOW *wp;
  2005.  
  2006.     if (b_is_changed(bp)) {
  2007.         if (bp->b_nwnd != 1)        /* Ensure hard.        */
  2008.             flag |= WFHARD;
  2009.         flag |= WFMODE;            /* update mode lines.    */
  2010.         b_clr_changed(bp);
  2011.         b_clr_counted(bp);
  2012.         b_match_attrs_dirty(bp);
  2013.  
  2014.         for_each_visible_window(wp) {
  2015.             if (wp->w_bufp == bp)
  2016.                 wp->w_flag |= flag;
  2017.         }
  2018. #if    OPT_UPBUFF
  2019.         if (update_on_chg(bp))
  2020.             updatelistbuffers();
  2021. #endif
  2022.     }
  2023. }
  2024.  
  2025. /* ARGSUSED */
  2026. int
  2027. unmark(int f GCC_UNUSED, int n GCC_UNUSED)    /* unmark the current buffers change flag */
  2028. {
  2029.     unchg_buff(curbp, 0);
  2030.     return (TRUE);
  2031. }
  2032.  
  2033. /* write all _changed_ buffers */
  2034. /* if you get the urge to tinker in here, bear in mind that you need
  2035.    to test:  :ww, 1:ww, :wwq, 1:wwq, all with 1 buffer, both modified and
  2036.    unmodified, with 2 buffers (0, 1, or both modified), and with errors,
  2037.    like either buffer not writeable.  oh yes -- you also need to turn
  2038.    on "autowrite", and try ^Z and :!cmd.
  2039.  
  2040.    by default, we should have scrolling messages, a pressreturn call, and
  2041.    a screen update.
  2042.  
  2043.    any numeric argument will suppress the pressreturn call, and therefore
  2044.    also the scrolling of the messages and screen update.
  2045.  
  2046.    if you're leaving (quitting, or suspending to the shell, you want the
  2047.    scrolling messages, but no press-return, and no update.
  2048.  
  2049.    if there's a failure, you want a pressreturn, and an update, and you
  2050.    need to return non-TRUE so you won't try to quit.  we also switch to
  2051.    the buffer that failed in that case.
  2052. */
  2053. int
  2054. writeallchanged(int f, int n)
  2055. {
  2056.     return writeall(f,n,!f,FALSE,FALSE);
  2057. }
  2058.  
  2059. int
  2060. writeall(int f, int n, int promptuser, int leaving, int autowriting)
  2061. {
  2062.     register BUFFER *bp;    /* scanning pointer to buffers */
  2063.     register BUFFER *oldbp; /* original current buffer */
  2064.     register int status = TRUE;
  2065.     int count = 0;
  2066.     int failure = FALSE;
  2067.     int dirtymsgline = FALSE;
  2068.  
  2069.     oldbp = curbp;                /* save in case we fail */
  2070.  
  2071.     for_each_buffer(bp) {
  2072.         if (autowriting && !b_val(bp,MDAUTOWRITE))
  2073.             continue;
  2074.         if (b_val(bp,MDREADONLY))    /* ignore read-only buffer */
  2075.             continue;
  2076.         if (b_is_changed(bp) && !b_is_invisible(bp)) {
  2077.             make_current(bp);
  2078.             if (dirtymsgline && (promptuser || leaving)) {
  2079.                 mlforce("\n");
  2080.                 dirtymsgline = FALSE;
  2081.             }
  2082.             status = filesave(f, n);
  2083.             dirtymsgline = TRUE;
  2084.             failure = (status != TRUE);
  2085.             if (failure) {
  2086.                 mlforce("\n");
  2087.                 mlforce("[Save of %s failed]",bp->b_fname);
  2088.                 /* dirtymsgline still TRUE */
  2089.                 break;
  2090.             }
  2091.             count++;
  2092.         }
  2093.     }
  2094.  
  2095.     /* shortcut out */
  2096.     if (autowriting && !failure && !count)
  2097.         return TRUE;
  2098.  
  2099.     /* do we want a press-return message?  */
  2100.     if (failure || ((promptuser && !leaving) && count)) {
  2101.         if (dirtymsgline)
  2102.             mlforce("\n");
  2103.         dirtymsgline = FALSE;
  2104.         pressreturn();
  2105.     }
  2106.  
  2107.     /* get our old buffer back */
  2108.     make_current(oldbp);
  2109.  
  2110.     /* and then switch to the failed buffer */
  2111.     if (failure && !insertmode /* can happen from ^Z and autowrite */ )
  2112.         swbuffer(bp);
  2113.  
  2114.     /* if we asked "press-return", then we certainly need an update */
  2115.     if (failure || ((promptuser && !leaving) && count)) {
  2116.         sgarbf = TRUE;
  2117.         (void)update(TRUE);
  2118.     } else if (dirtymsgline && (promptuser || leaving)) {
  2119.         mlforce("\n");
  2120.     }
  2121.  
  2122.     /* final message -- appears on the message line after the update,
  2123.       or as the last line before the post-quit prompt */
  2124.     if (count)
  2125.         mlforce("[Wrote %d buffer%s]", count, PLURAL(count));
  2126.     else
  2127.         mlforce("[No buffers written]");
  2128.  
  2129.     if (dirtymsgline)
  2130.         sgarbf = TRUE;
  2131.  
  2132.     return status;
  2133. }
  2134.  
  2135. /* For memory-leak testing (only!), releases all buffer storage. */
  2136. #if    NO_LEAKS
  2137. void    bp_leaks(void)
  2138. {
  2139.     register BUFFER *bp;
  2140.  
  2141.     if (bminip != 0)
  2142.         FreeBuffer(bminip);
  2143.     while ((bp = bheadp) != 0) {
  2144.         b_clr_changed(bp);    /* discard any changes */
  2145.         bclear(bheadp);
  2146.         FreeBuffer(bheadp);
  2147.     }
  2148. }
  2149. #endif
  2150.