Home | History | Annotate | Line # | Download | only in info
      1 /*	$NetBSD: window.c,v 1.1.1.1 2016/01/14 00:11:29 christos Exp $	*/
      2 
      3 /* window.c -- windows in Info.
      4    Id: window.c,v 1.4 2004/04/11 17:56:46 karl Exp
      5 
      6    Copyright (C) 1993, 1997, 1998, 2001, 2002, 2003, 2004 Free Software
      7    Foundation, Inc.
      8 
      9    This program is free software; you can redistribute it and/or modify
     10    it under the terms of the GNU General Public License as published by
     11    the Free Software Foundation; either version 2, or (at your option)
     12    any later version.
     13 
     14    This program is distributed in the hope that it will be useful,
     15    but WITHOUT ANY WARRANTY; without even the implied warranty of
     16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17    GNU General Public License for more details.
     18 
     19    You should have received a copy of the GNU General Public License
     20    along with this program; if not, write to the Free Software
     21    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
     22 
     23    Written by Brian Fox (bfox (at) ai.mit.edu). */
     24 
     25 #include "info.h"
     26 #include "nodes.h"
     27 #include "window.h"
     28 #include "display.h"
     29 #include "info-utils.h"
     30 #include "infomap.h"
     31 
     32 /* The window which describes the screen. */
     33 WINDOW *the_screen = NULL;
     34 
     35 /* The window which describes the echo area. */
     36 WINDOW *the_echo_area = NULL;
     37 
     38 /* The list of windows in Info. */
     39 WINDOW *windows = NULL;
     40 
     41 /* Pointer to the active window in WINDOW_LIST. */
     42 WINDOW *active_window = NULL;
     43 
     44 /* The size of the echo area in Info.  It never changes, irregardless of the
     45    size of the screen. */
     46 #define ECHO_AREA_HEIGHT 1
     47 
     48 /* Macro returns the amount of space that the echo area truly requires relative
     49    to the entire screen. */
     50 #define echo_area_required (1 + the_echo_area->height)
     51 
     52 /* Initalize the window system by creating THE_SCREEN and THE_ECHO_AREA.
     53    Create the first window ever.
     54    You pass the dimensions of the total screen size. */
     55 void
     56 window_initialize_windows (int width, int height)
     57 {
     58   the_screen = xmalloc (sizeof (WINDOW));
     59   the_echo_area = xmalloc (sizeof (WINDOW));
     60   windows = xmalloc (sizeof (WINDOW));
     61   active_window = windows;
     62 
     63   zero_mem (the_screen, sizeof (WINDOW));
     64   zero_mem (the_echo_area, sizeof (WINDOW));
     65   zero_mem (active_window, sizeof (WINDOW));
     66 
     67   /* None of these windows has a goal column yet. */
     68   the_echo_area->goal_column = -1;
     69   active_window->goal_column = -1;
     70   the_screen->goal_column = -1;
     71 
     72   /* The active and echo_area windows are visible.
     73      The echo_area is permanent.
     74      The screen is permanent. */
     75   active_window->flags = W_WindowVisible;
     76   the_echo_area->flags = W_WindowIsPerm | W_InhibitMode | W_WindowVisible;
     77   the_screen->flags    = W_WindowIsPerm;
     78 
     79   /* The height of the echo area never changes.  It is statically set right
     80      here, and it must be at least 1 line for display.  The size of the
     81      initial window cannot be the same size as the screen, since the screen
     82      includes the echo area.  So, we make the height of the initial window
     83      equal to the screen's displayable region minus the height of the echo
     84      area. */
     85   the_echo_area->height = ECHO_AREA_HEIGHT;
     86   active_window->height = the_screen->height - 1 - the_echo_area->height;
     87   window_new_screen_size (width, height);
     88 
     89   /* The echo area uses a different keymap than normal info windows. */
     90   the_echo_area->keymap = echo_area_keymap;
     91   active_window->keymap = info_keymap;
     92 }
     93 
     94 /* Given that the size of the screen has changed to WIDTH and HEIGHT
     95    from whatever it was before (found in the_screen->height, ->width),
     96    change the size (and possibly location) of each window in the screen.
     97    If a window would become too small, call the function DELETER on it,
     98    after deleting the window from our chain of windows.  If DELETER is NULL,
     99    nothing extra is done.  The last window can never be deleted, but it can
    100    become invisible. */
    101 
    102 /* If non-null, a function to call with WINDOW as argument when the function
    103    window_new_screen_size () has deleted WINDOW. */
    104 VFunction *window_deletion_notifier = NULL;
    105 
    106 void
    107 window_new_screen_size (int width, int height)
    108 {
    109   register WINDOW *win;
    110   int delta_height, delta_each, delta_leftover;
    111   int numwins;
    112 
    113   /* If no change, do nothing. */
    114   if (width == the_screen->width && height == the_screen->height)
    115     return;
    116 
    117   /* If the new window height is too small, make it be zero. */
    118   if (height < (WINDOW_MIN_SIZE + the_echo_area->height))
    119     height = 0;
    120   if (width < 0)
    121     width = 0;
    122 
    123   /* Find out how many windows will change. */
    124   for (numwins = 0, win = windows; win; win = win->next, numwins++);
    125 
    126   /* See if some windows will need to be deleted.  This is the case if
    127      the screen is getting smaller, and the available space divided by
    128      the number of windows is less than WINDOW_MIN_SIZE.  In that case,
    129      delete some windows and try again until there is either enough
    130      space to divy up among the windows, or until there is only one
    131      window left. */
    132   while ((height - echo_area_required) / numwins <= WINDOW_MIN_SIZE)
    133     {
    134       /* If only one window, make the size of it be zero, and return
    135          immediately. */
    136       if (!windows->next)
    137         {
    138           windows->height = 0;
    139           maybe_free (windows->line_starts);
    140           windows->line_starts = NULL;
    141           windows->line_count = 0;
    142           break;
    143         }
    144 
    145       /* If we have some temporary windows, delete one of them. */
    146       for (win = windows; win; win = win->next)
    147         if (win->flags & W_TempWindow)
    148           break;
    149 
    150       /* Otherwise, delete the first window, and try again. */
    151       if (!win)
    152         win = windows;
    153 
    154       if (window_deletion_notifier)
    155         (*window_deletion_notifier) (win);
    156 
    157       window_delete_window (win);
    158       numwins--;
    159     }
    160 
    161   /* The screen has changed height and width. */
    162   delta_height = height - the_screen->height;   /* This is how much. */
    163   the_screen->height = height;                  /* This is the new height. */
    164   the_screen->width = width;                    /* This is the new width. */
    165 
    166   /* Set the start of the echo area. */
    167   the_echo_area->first_row = height - the_echo_area->height;
    168   the_echo_area->width = width;
    169 
    170   /* Check to see if the screen can really be changed this way. */
    171   if ((!windows->next) && ((windows->height == 0) && (delta_height < 0)))
    172     return;
    173 
    174   /* Divide the change in height among the available windows. */
    175   delta_each = delta_height / numwins;
    176   delta_leftover = delta_height - (delta_each * numwins);
    177 
    178   /* Change the height of each window in the chain by delta_each.  Change
    179      the height of the last window in the chain by delta_each and by the
    180      leftover amount of change.  Change the width of each window to be
    181      WIDTH. */
    182   for (win = windows; win; win = win->next)
    183     {
    184       if ((win->width != width) && ((win->flags & W_InhibitMode) == 0))
    185         {
    186           win->width = width;
    187           maybe_free (win->modeline);
    188           win->modeline = xmalloc (1 + width);
    189         }
    190 
    191       win->height += delta_each;
    192 
    193       /* If the previous height of this window was zero, it was the only
    194          window, and it was not visible.  Thus we need to compensate for
    195          the echo_area. */
    196       if (win->height == delta_each)
    197         win->height -= (1 + the_echo_area->height);
    198 
    199       /* If this is not the first window in the chain, then change the
    200          first row of it.  We cannot just add delta_each to the first row,
    201          since this window's first row is the sum of the collective increases
    202          that have gone before it.  So we just add one to the location of the
    203          previous window's modeline. */
    204       if (win->prev)
    205         win->first_row = (win->prev->first_row + win->prev->height) + 1;
    206 
    207       /* The last window in the chain gets the extra space (or shrinkage). */
    208       if (!win->next)
    209         win->height += delta_leftover;
    210 
    211       if (win->node)
    212         recalculate_line_starts (win);
    213 
    214       win->flags |= W_UpdateWindow;
    215     }
    216 
    217   /* If the screen got smaller, check over the windows just shrunk to
    218      keep them within bounds.  Some of the windows may have gotten smaller
    219      than WINDOW_MIN_HEIGHT in which case some of the other windows are
    220      larger than the available display space in the screen.  Because of our
    221      intial test above, we know that there is enough space for all of the
    222      windows. */
    223   if ((delta_each < 0) && ((windows->height != 0) && windows->next))
    224     {
    225       int avail;
    226 
    227       avail = the_screen->height - (numwins + the_echo_area->height);
    228       win = windows;
    229 
    230       while (win)
    231         {
    232           if ((win->height < WINDOW_MIN_HEIGHT) ||
    233               (win->height > avail))
    234             {
    235               WINDOW *lastwin = NULL;
    236 
    237               /* Split the space among the available windows. */
    238               delta_each = avail / numwins;
    239               delta_leftover = avail - (delta_each * numwins);
    240 
    241               for (win = windows; win; win = win->next)
    242                 {
    243                   lastwin = win;
    244                   if (win->prev)
    245                     win->first_row =
    246                       (win->prev->first_row + win->prev->height) + 1;
    247                   win->height = delta_each;
    248                 }
    249 
    250               /* Give the leftover space (if any) to the last window. */
    251               lastwin->height += delta_leftover;
    252               break;
    253             }
    254           else
    255             win= win->next;
    256         }
    257     }
    258 }
    259 
    260 /* Make a new window showing NODE, and return that window structure.
    261    If NODE is passed as NULL, then show the node showing in the active
    262    window.  If the window could not be made return a NULL pointer.  The
    263    active window is not changed.*/
    264 WINDOW *
    265 window_make_window (NODE *node)
    266 {
    267   WINDOW *window;
    268 
    269   if (!node)
    270     node = active_window->node;
    271 
    272   /* If there isn't enough room to make another window, return now. */
    273   if ((active_window->height / 2) < WINDOW_MIN_SIZE)
    274     return (NULL);
    275 
    276   /* Make and initialize the new window.
    277      The fudging about with -1 and +1 is because the following window in the
    278      chain cannot start at window->height, since that is where the modeline
    279      for the previous window is displayed.  The inverse adjustment is made
    280      in window_delete_window (). */
    281   window = xmalloc (sizeof (WINDOW));
    282   window->width = the_screen->width;
    283   window->height = (active_window->height / 2) - 1;
    284 #if defined (SPLIT_BEFORE_ACTIVE)
    285   window->first_row = active_window->first_row;
    286 #else
    287   window->first_row = active_window->first_row +
    288     (active_window->height - window->height);
    289 #endif
    290   window->keymap = info_keymap;
    291   window->goal_column = -1;
    292   window->modeline = xmalloc (1 + window->width);
    293   window->line_starts = NULL;
    294   window->flags = W_UpdateWindow | W_WindowVisible;
    295   window_set_node_of_window (window, node);
    296 
    297   /* Adjust the height of the old active window. */
    298   active_window->height -= (window->height + 1);
    299 #if defined (SPLIT_BEFORE_ACTIVE)
    300   active_window->first_row += (window->height + 1);
    301 #endif
    302   active_window->flags |= W_UpdateWindow;
    303 
    304   /* Readjust the new and old windows so that their modelines and contents
    305      will be displayed correctly. */
    306 #if defined (NOTDEF)
    307   /* We don't have to do this for WINDOW since window_set_node_of_window ()
    308      already did. */
    309   window_adjust_pagetop (window);
    310   window_make_modeline (window);
    311 #endif /* NOTDEF */
    312 
    313   /* We do have to readjust the existing active window. */
    314   window_adjust_pagetop (active_window);
    315   window_make_modeline (active_window);
    316 
    317 #if defined (SPLIT_BEFORE_ACTIVE)
    318   /* This window is just before the active one.  The active window gets
    319      bumped down one.  The active window is not changed. */
    320   window->next = active_window;
    321 
    322   window->prev = active_window->prev;
    323   active_window->prev = window;
    324 
    325   if (window->prev)
    326     window->prev->next = window;
    327   else
    328     windows = window;
    329 #else
    330   /* This window is just after the active one.  Which window is active is
    331      not changed. */
    332   window->prev = active_window;
    333   window->next = active_window->next;
    334   active_window->next = window;
    335   if (window->next)
    336     window->next->prev = window;
    337 #endif /* !SPLIT_BEFORE_ACTIVE */
    338   return (window);
    339 }
    340 
    341 /* These useful macros make it possible to read the code in
    342    window_change_window_height (). */
    343 #define grow_me_shrinking_next(me, next, diff) \
    344   do { \
    345     me->height += diff; \
    346     next->height -= diff; \
    347     next->first_row += diff; \
    348     window_adjust_pagetop (next); \
    349   } while (0)
    350 
    351 #define grow_me_shrinking_prev(me, prev, diff) \
    352   do { \
    353     me->height += diff; \
    354     prev->height -= diff; \
    355     me->first_row -=diff; \
    356     window_adjust_pagetop (prev); \
    357   } while (0)
    358 
    359 #define shrink_me_growing_next(me, next, diff) \
    360   do { \
    361     me->height -= diff; \
    362     next->height += diff; \
    363     next->first_row -= diff; \
    364     window_adjust_pagetop (next); \
    365   } while (0)
    366 
    367 #define shrink_me_growing_prev(me, prev, diff) \
    368   do { \
    369     me->height -= diff; \
    370     prev->height += diff; \
    371     me->first_row += diff; \
    372     window_adjust_pagetop (prev); \
    373   } while (0)
    374 
    375 /* Change the height of WINDOW by AMOUNT.  This also automagically adjusts
    376    the previous and next windows in the chain.  If there is only one user
    377    window, then no change takes place. */
    378 void
    379 window_change_window_height (WINDOW *window, int amount)
    380 {
    381   register WINDOW *win, *prev, *next;
    382 
    383   /* If there is only one window, or if the amount of change is zero,
    384      return immediately. */
    385   if (!windows->next || amount == 0)
    386     return;
    387 
    388   /* Find this window in our chain. */
    389   for (win = windows; win; win = win->next)
    390     if (win == window)
    391       break;
    392 
    393   /* If the window is isolated (i.e., doesn't appear in our window list,
    394      then quit now. */
    395   if (!win)
    396     return;
    397 
    398   /* Change the height of this window by AMOUNT, if that is possible.
    399      It can be impossible if there isn't enough available room on the
    400      screen, or if the resultant window would be too small. */
    401 
    402     prev = window->prev;
    403     next = window->next;
    404 
    405   /* WINDOW decreasing in size? */
    406   if (amount < 0)
    407     {
    408       int abs_amount = -amount; /* It is easier to deal with this way. */
    409 
    410       /* If the resultant window would be too small, stop here. */
    411       if ((window->height - abs_amount) < WINDOW_MIN_HEIGHT)
    412         return;
    413 
    414       /* If we have two neighboring windows, choose the smaller one to get
    415          larger. */
    416       if (next && prev)
    417         {
    418           if (prev->height < next->height)
    419             shrink_me_growing_prev (window, prev, abs_amount);
    420           else
    421             shrink_me_growing_next (window, next, abs_amount);
    422         }
    423       else if (next)
    424         shrink_me_growing_next (window, next, abs_amount);
    425       else
    426         shrink_me_growing_prev (window, prev, abs_amount);
    427     }
    428 
    429   /* WINDOW increasing in size? */
    430   if (amount > 0)
    431     {
    432       int total_avail, next_avail = 0, prev_avail = 0;
    433 
    434       if (next)
    435         next_avail = next->height - WINDOW_MIN_SIZE;
    436 
    437       if (prev)
    438         prev_avail = prev->height - WINDOW_MIN_SIZE;
    439 
    440       total_avail = next_avail + prev_avail;
    441 
    442       /* If there isn't enough space available to grow this window, give up. */
    443       if (amount > total_avail)
    444         return;
    445 
    446       /* If there aren't two neighboring windows, or if one of the neighbors
    447          is larger than the other one by at least AMOUNT, grow that one. */
    448       if ((next && !prev) || ((next_avail - amount) >= prev_avail))
    449         grow_me_shrinking_next (window, next, amount);
    450       else if ((prev && !next) || ((prev_avail - amount) >= next_avail))
    451         grow_me_shrinking_prev (window, prev, amount);
    452       else
    453         {
    454           int change;
    455 
    456           /* This window has two neighbors.  They both must be shrunk in to
    457              make enough space for WINDOW to grow.  Make them both the same
    458              size. */
    459           if (prev_avail > next_avail)
    460             {
    461               change = prev_avail - next_avail;
    462               grow_me_shrinking_prev (window, prev, change);
    463               amount -= change;
    464             }
    465           else
    466             {
    467               change = next_avail - prev_avail;
    468               grow_me_shrinking_next (window, next, change);
    469               amount -= change;
    470             }
    471 
    472           /* Both neighbors are the same size.  Split the difference in
    473              AMOUNT between them. */
    474           while (amount)
    475             {
    476               window->height++;
    477               amount--;
    478 
    479               /* Odd numbers grow next, even grow prev. */
    480               if (amount & 1)
    481                 {
    482                   prev->height--;
    483                   window->first_row--;
    484                 }
    485               else
    486                 {
    487                   next->height--;
    488                   next->first_row++;
    489                 }
    490             }
    491           window_adjust_pagetop (prev);
    492           window_adjust_pagetop (next);
    493         }
    494     }
    495   if (prev)
    496     prev->flags |= W_UpdateWindow;
    497 
    498   if (next)
    499     next->flags |= W_UpdateWindow;
    500 
    501   window->flags |= W_UpdateWindow;
    502   window_adjust_pagetop (window);
    503 }
    504 
    505 /* Tile all of the windows currently displayed in the global variable
    506    WINDOWS.  If argument STYLE is TILE_INTERNALS, tile windows displaying
    507    internal nodes as well, otherwise do not change the height of such
    508    windows. */
    509 void
    510 window_tile_windows (int style)
    511 {
    512   WINDOW *win, *last_adjusted;
    513   int numwins, avail, per_win_height, leftover;
    514   int do_internals;
    515 
    516   numwins = avail = 0;
    517   do_internals = (style == TILE_INTERNALS);
    518 
    519   for (win = windows; win; win = win->next)
    520     if (do_internals || !win->node ||
    521         (win->node->flags & N_IsInternal) == 0)
    522       {
    523         avail += win->height;
    524         numwins++;
    525       }
    526 
    527   if (numwins <= 1 || !the_screen->height)
    528     return;
    529 
    530   /* Find the size for each window.  Divide the size of the usable portion
    531      of the screen by the number of windows. */
    532   per_win_height = avail / numwins;
    533   leftover = avail - (per_win_height * numwins);
    534 
    535   last_adjusted = NULL;
    536   for (win = windows; win; win = win->next)
    537     {
    538       if (do_internals || !win->node ||
    539           (win->node->flags & N_IsInternal) == 0)
    540         {
    541           last_adjusted = win;
    542           win->height = per_win_height;
    543         }
    544     }
    545 
    546   if (last_adjusted)
    547     last_adjusted->height += leftover;
    548 
    549   /* Readjust the first_row of every window in the chain. */
    550   for (win = windows; win; win = win->next)
    551     {
    552       if (win->prev)
    553         win->first_row = win->prev->first_row + win->prev->height + 1;
    554 
    555       window_adjust_pagetop (win);
    556       win->flags |= W_UpdateWindow;
    557     }
    558 }
    559 
    560 /* Toggle the state of line wrapping in WINDOW.  This can do a bit of fancy
    561    redisplay. */
    562 void
    563 window_toggle_wrap (WINDOW *window)
    564 {
    565   if (window->flags & W_NoWrap)
    566     window->flags &= ~W_NoWrap;
    567   else
    568     window->flags |= W_NoWrap;
    569 
    570   if (window != the_echo_area)
    571     {
    572       char **old_starts;
    573       int old_lines, old_pagetop;
    574 
    575       old_starts = window->line_starts;
    576       old_lines = window->line_count;
    577       old_pagetop = window->pagetop;
    578 
    579       calculate_line_starts (window);
    580 
    581       /* Make sure that point appears within this window. */
    582       window_adjust_pagetop (window);
    583 
    584       /* If the pagetop hasn't changed maybe we can do some scrolling now
    585          to speed up the display.  Many of the line starts will be the same,
    586          so scrolling here is a very good optimization.*/
    587       if (old_pagetop == window->pagetop)
    588         display_scroll_line_starts
    589           (window, old_pagetop, old_starts, old_lines);
    590       maybe_free (old_starts);
    591     }
    592   window->flags |= W_UpdateWindow;
    593 }
    594 
    595 /* Set WINDOW to display NODE. */
    596 void
    597 window_set_node_of_window (WINDOW *window, NODE *node)
    598 {
    599   window->node = node;
    600   window->pagetop = 0;
    601   window->point = 0;
    602   recalculate_line_starts (window);
    603   window->flags |= W_UpdateWindow;
    604   /* The display_pos member is nonzero if we're displaying an anchor.  */
    605   window->point = node ? node->display_pos : 0;
    606   window_adjust_pagetop (window);
    607   window_make_modeline (window);
    608 }
    609 
    610 /* Delete WINDOW from the list of known windows.  If this window was the
    612    active window, make the next window in the chain be the active window.
    613    If the active window is the next or previous window, choose that window
    614    as the recipient of the extra space.  Otherwise, prefer the next window. */
    615 void
    616 window_delete_window (WINDOW *window)
    617 {
    618   WINDOW *next, *prev, *window_to_fix;
    619 
    620   next = window->next;
    621   prev = window->prev;
    622 
    623   /* You cannot delete the only window or a permanent window. */
    624   if ((!next && !prev) || (window->flags & W_WindowIsPerm))
    625     return;
    626 
    627   if (next)
    628     next->prev = prev;
    629 
    630   if (!prev)
    631     windows = next;
    632   else
    633     prev->next = next;
    634 
    635   if (window->line_starts)
    636     free (window->line_starts);
    637 
    638   if (window->modeline)
    639     free (window->modeline);
    640 
    641   if (window == active_window)
    642     {
    643       /* If there isn't a next window, then there must be a previous one,
    644          since we cannot delete the last window.  If there is a next window,
    645          prefer to use that as the active window. */
    646       if (next)
    647         active_window = next;
    648       else
    649         active_window = prev;
    650     }
    651 
    652   if (next && active_window == next)
    653     window_to_fix = next;
    654   else if (prev && active_window == prev)
    655     window_to_fix = prev;
    656   else if (next)
    657     window_to_fix = next;
    658   else if (prev)
    659     window_to_fix = prev;
    660   else
    661     window_to_fix = windows;
    662 
    663   if (window_to_fix->first_row > window->first_row)
    664     {
    665       int diff;
    666 
    667       /* Try to adjust the visible part of the node so that as little
    668          text as possible has to move. */
    669       diff = window_to_fix->first_row - window->first_row;
    670       window_to_fix->first_row = window->first_row;
    671 
    672       window_to_fix->pagetop -= diff;
    673       if (window_to_fix->pagetop < 0)
    674         window_to_fix->pagetop = 0;
    675     }
    676 
    677   /* The `+ 1' is to offset the difference between the first_row locations.
    678      See the code in window_make_window (). */
    679   window_to_fix->height += window->height + 1;
    680   window_to_fix->flags |= W_UpdateWindow;
    681 
    682   free (window);
    683 }
    684 
    685 /* For every window in CHAIN, set the flags member to have FLAG set. */
    686 void
    687 window_mark_chain (WINDOW *chain, int flag)
    688 {
    689   register WINDOW *win;
    690 
    691   for (win = chain; win; win = win->next)
    692     win->flags |= flag;
    693 }
    694 
    695 /* For every window in CHAIN, clear the flags member of FLAG. */
    696 void
    697 window_unmark_chain (WINDOW *chain, int flag)
    698 {
    699   register WINDOW *win;
    700 
    701   for (win = chain; win; win = win->next)
    702     win->flags &= ~flag;
    703 }
    704 
    705 /* Return the number of characters it takes to display CHARACTER on the
    706    screen at HPOS. */
    707 int
    708 character_width (int character, int hpos)
    709 {
    710   int printable_limit = 127;
    711   int width = 1;
    712 
    713   if (ISO_Latin_p)
    714     printable_limit = 255;
    715 
    716   if (character > printable_limit)
    717     width = 3;
    718   else if (iscntrl (character))
    719     {
    720       switch (character)
    721         {
    722         case '\r':
    723         case '\n':
    724           width = the_screen->width - hpos;
    725           break;
    726         case '\t':
    727           width = ((hpos + 8) & 0xf8) - hpos;
    728           break;
    729         default:
    730           width = 2;
    731         }
    732     }
    733   else if (character == DEL)
    734     width = 2;
    735 
    736   return (width);
    737 }
    738 
    739 /* Return the number of characters it takes to display STRING on the screen
    740    at HPOS. */
    741 int
    742 string_width (char *string, int hpos)
    743 {
    744   register int i, width, this_char_width;
    745 
    746   for (width = 0, i = 0; string[i]; i++)
    747     {
    748       /* Support ANSI escape sequences for -R.  */
    749       if (raw_escapes_p
    750 	  && string[i] == '\033'
    751 	  && string[i+1] == '['
    752 	  && isdigit (string[i+2])
    753 	  && (string[i+3] == 'm'
    754 	      || (isdigit (string[i+3]) && string[i+4] == 'm')))
    755 	{
    756 	  while (string[i] != 'm')
    757 	    i++;
    758 	  this_char_width = 0;
    759 	}
    760       else
    761 	this_char_width = character_width (string[i], hpos);
    762       width += this_char_width;
    763       hpos += this_char_width;
    764     }
    765   return (width);
    766 }
    767 
    768 /* Quickly guess the approximate number of lines that NODE would
    769    take to display.  This really only counts carriage returns. */
    770 int
    771 window_physical_lines (NODE *node)
    772 {
    773   register int i, lines;
    774   char *contents;
    775 
    776   if (!node)
    777     return (0);
    778 
    779   contents = node->contents;
    780   for (i = 0, lines = 1; i < node->nodelen; i++)
    781     if (contents[i] == '\n')
    782       lines++;
    783 
    784   return (lines);
    785 }
    786 
    787 /* Calculate a list of line starts for the node belonging to WINDOW.  The line
    788    starts are pointers to the actual text within WINDOW->NODE. */
    789 void
    790 calculate_line_starts (WINDOW *window)
    791 {
    792   register int i, hpos;
    793   char **line_starts = NULL;
    794   int line_starts_index = 0, line_starts_slots = 0;
    795   int bump_index;
    796   NODE *node;
    797 
    798   window->line_starts = NULL;
    799   window->line_count = 0;
    800   node = window->node;
    801 
    802   if (!node)
    803     return;
    804 
    805   /* Grovel the node starting at the top, and for each line calculate the
    806      width of the characters appearing in that line.  Add each line start
    807      to our array. */
    808   i = 0;
    809   hpos = 0;
    810   bump_index = 0;
    811 
    812   while (i < node->nodelen)
    813     {
    814       char *line = node->contents + i;
    815       unsigned int cwidth, c;
    816 
    817       add_pointer_to_array (line, line_starts_index, line_starts,
    818                             line_starts_slots, 100, char *);
    819       if (bump_index)
    820         {
    821           i++;
    822           bump_index = 0;
    823         }
    824 
    825       while (1)
    826         {
    827 	  /* The cast to unsigned char is for 8-bit characters, which
    828 	     could be passed as negative integers to character_width
    829 	     and wreak havoc on some naive implementations of iscntrl.  */
    830           c = (unsigned char) node->contents[i];
    831 
    832 	  /* Support ANSI escape sequences for -R.  */
    833 	  if (raw_escapes_p
    834 	      && c == '\033'
    835 	      && node->contents[i+1] == '['
    836 	      && isdigit (node->contents[i+2]))
    837 	    {
    838 	      if (node->contents[i+3] == 'm')
    839 		{
    840 		  i += 3;
    841 		  cwidth = 0;
    842 		}
    843 	      else if (isdigit (node->contents[i+3])
    844 		       && node->contents[i+4] == 'm')
    845 		{
    846 		  i += 4;
    847 		  cwidth = 0;
    848 		}
    849 	      else
    850 		cwidth = character_width (c, hpos);
    851 	    }
    852 	  else
    853 	    cwidth = character_width (c, hpos);
    854 
    855           /* If this character fits within this line, just do the next one. */
    856           if ((hpos + cwidth) < (unsigned int) window->width)
    857             {
    858               i++;
    859               hpos += cwidth;
    860               continue;
    861             }
    862           else
    863             {
    864               /* If this character would position the cursor at the start of
    865                  the next printed screen line, then do the next line. */
    866               if (c == '\n' || c == '\r' || c == '\t')
    867                 {
    868                   i++;
    869                   hpos = 0;
    870                   break;
    871                 }
    872               else
    873                 {
    874                   /* This character passes the window width border.  Postion
    875                      the cursor after the printed character, but remember this
    876                      line start as where this character is.  A bit tricky. */
    877 
    878                   /* If this window doesn't wrap lines, proceed to the next
    879                      physical line here. */
    880                   if (window->flags & W_NoWrap)
    881                     {
    882                       hpos = 0;
    883                       while (i < node->nodelen && node->contents[i] != '\n')
    884                         i++;
    885 
    886                       if (node->contents[i] == '\n')
    887                         i++;
    888                     }
    889                   else
    890                     {
    891                       hpos = the_screen->width - hpos;
    892                       bump_index++;
    893                     }
    894                   break;
    895                 }
    896             }
    897         }
    898     }
    899   window->line_starts = line_starts;
    900   window->line_count = line_starts_index;
    901 }
    902 
    903 /* Given WINDOW, recalculate the line starts for the node it displays. */
    904 void
    905 recalculate_line_starts (WINDOW *window)
    906 {
    907   maybe_free (window->line_starts);
    908   calculate_line_starts (window);
    909 }
    910 
    911 /* Global variable control redisplay of scrolled windows.  If non-zero, it
    912    is the desired number of lines to scroll the window in order to make
    913    point visible.  A user might set this to 1 for smooth scrolling.  If
    914    set to zero, the line containing point is centered within the window. */
    915 int window_scroll_step = 0;
    916 
    917 /* Adjust the pagetop of WINDOW such that the cursor point will be visible. */
    918 void
    919 window_adjust_pagetop (WINDOW *window)
    920 {
    921   register int line = 0;
    922   char *contents;
    923 
    924   if (!window->node)
    925     return;
    926 
    927   contents = window->node->contents;
    928 
    929   /* Find the first printed line start which is after WINDOW->point. */
    930   for (line = 0; line < window->line_count; line++)
    931     {
    932       char *line_start;
    933 
    934       line_start = window->line_starts[line];
    935 
    936       if ((line_start - contents) > window->point)
    937         break;
    938     }
    939 
    940   /* The line index preceding the line start which is past point is the
    941      one containing point. */
    942   line--;
    943 
    944   /* If this line appears in the current displayable page, do nothing.
    945      Otherwise, adjust the top of the page to make this line visible. */
    946   if ((line < window->pagetop) ||
    947       (line - window->pagetop > (window->height - 1)))
    948     {
    949       /* The user-settable variable "scroll-step" is used to attempt
    950          to make point visible, iff it is non-zero.  If that variable
    951          is zero, then the line containing point is centered within
    952          the window. */
    953       if (window_scroll_step < window->height)
    954         {
    955           if ((line < window->pagetop) &&
    956               ((window->pagetop - window_scroll_step) <= line))
    957             window->pagetop -= window_scroll_step;
    958           else if ((line - window->pagetop > (window->height - 1)) &&
    959                    ((line - (window->pagetop + window_scroll_step)
    960                      < window->height)))
    961             window->pagetop += window_scroll_step;
    962           else
    963             window->pagetop = line - ((window->height - 1) / 2);
    964         }
    965       else
    966         window->pagetop = line - ((window->height - 1) / 2);
    967 
    968       if (window->pagetop < 0)
    969         window->pagetop = 0;
    970       window->flags |= W_UpdateWindow;
    971     }
    972 }
    973 
    974 /* Return the index of the line containing point. */
    975 int
    976 window_line_of_point (WINDOW *window)
    977 {
    978   register int i, start = 0;
    979 
    980   /* Try to optimize.  Check to see if point is past the pagetop for
    981      this window, and if so, start searching forward from there. */
    982   if ((window->pagetop > -1 && window->pagetop < window->line_count) &&
    983       (window->line_starts[window->pagetop] - window->node->contents)
    984       <= window->point)
    985     start = window->pagetop;
    986 
    987   for (i = start; i < window->line_count; i++)
    988     {
    989       if ((window->line_starts[i] - window->node->contents) > window->point)
    990         break;
    991     }
    992 
    993   return (i - 1);
    994 }
    995 
    996 /* Get and return the goal column for this window. */
    997 int
    998 window_get_goal_column (WINDOW *window)
    999 {
   1000   if (!window->node)
   1001     return (-1);
   1002 
   1003   if (window->goal_column != -1)
   1004     return (window->goal_column);
   1005 
   1006   /* Okay, do the work.  Find the printed offset of the cursor
   1007      in this window. */
   1008   return (window_get_cursor_column (window));
   1009 }
   1010 
   1011 /* Get and return the printed column offset of the cursor in this window. */
   1012 int
   1013 window_get_cursor_column (WINDOW *window)
   1014 {
   1015   int i, hpos, end;
   1016   char *line;
   1017 
   1018   i = window_line_of_point (window);
   1019 
   1020   if (i < 0)
   1021     return (-1);
   1022 
   1023   line = window->line_starts[i];
   1024   end = window->point - (line - window->node->contents);
   1025 
   1026   for (hpos = 0, i = 0; i < end; i++)
   1027     {
   1028       /* Support ANSI escape sequences for -R.  */
   1029       if (raw_escapes_p
   1030 	  && line[i] == '\033'
   1031 	  && line[i+1] == '['
   1032 	  && isdigit (line[i+2]))
   1033 	{
   1034 	  if (line[i+3] == 'm')
   1035 	    i += 3;
   1036 	  else if (isdigit (line[i+3]) && line[i+4] == 'm')
   1037 	    i += 4;
   1038 	  else
   1039 	    hpos += character_width (line[i], hpos);
   1040 	}
   1041       else
   1042 	hpos += character_width (line[i], hpos);
   1043     }
   1044 
   1045   return (hpos);
   1046 }
   1047 
   1048 /* Count the number of characters in LINE that precede the printed column
   1049    offset of GOAL. */
   1050 int
   1051 window_chars_to_goal (char *line, int goal)
   1052 {
   1053   register int i, check = 0, hpos;
   1054 
   1055   for (hpos = 0, i = 0; line[i] != '\n'; i++)
   1056     {
   1057       /* Support ANSI escape sequences for -R.  */
   1058       if (raw_escapes_p
   1059 	  && line[i] == '\033'
   1060 	  && line[i+1] == '['
   1061 	  && isdigit (line[i+2])
   1062 	  && (line[i+3] == 'm'
   1063 	      || (isdigit (line[i+3]) && line[i+4] == 'm')))
   1064 	while (line[i] != 'm')
   1065 	  i++;
   1066       else
   1067 	check = hpos + character_width (line[i], hpos);
   1068 
   1069       if (check > goal)
   1070         break;
   1071 
   1072       hpos = check;
   1073     }
   1074   return (i);
   1075 }
   1076 
   1077 /* Create a modeline for WINDOW, and store it in window->modeline. */
   1078 void
   1079 window_make_modeline (WINDOW *window)
   1080 {
   1081   register int i;
   1082   char *modeline;
   1083   char location_indicator[4];
   1084   int lines_remaining;
   1085 
   1086   /* Only make modelines for those windows which have one. */
   1087   if (window->flags & W_InhibitMode)
   1088     return;
   1089 
   1090   /* Find the number of lines actually displayed in this window. */
   1091   lines_remaining = window->line_count - window->pagetop;
   1092 
   1093   if (window->pagetop == 0)
   1094     {
   1095       if (lines_remaining <= window->height)
   1096         strcpy (location_indicator, "All");
   1097       else
   1098         strcpy (location_indicator, "Top");
   1099     }
   1100   else
   1101     {
   1102       if (lines_remaining <= window->height)
   1103         strcpy (location_indicator, "Bot");
   1104       else
   1105         {
   1106           float pt, lc;
   1107           int percentage;
   1108 
   1109           pt = (float)window->pagetop;
   1110           lc = (float)window->line_count;
   1111 
   1112           percentage = 100 * (pt / lc);
   1113 
   1114           sprintf (location_indicator, "%2d%%", percentage);
   1115         }
   1116     }
   1117 
   1118   /* Calculate the maximum size of the information to stick in MODELINE. */
   1119   {
   1120     int modeline_len = 0;
   1121     char *parent = NULL, *filename = "*no file*";
   1122     char *nodename = "*no node*";
   1123     const char *update_message = NULL;
   1124     NODE *node = window->node;
   1125 
   1126     if (node)
   1127       {
   1128         if (node->nodename)
   1129           nodename = node->nodename;
   1130 
   1131         if (node->parent)
   1132           {
   1133             parent = filename_non_directory (node->parent);
   1134             modeline_len += strlen ("Subfile: ") + strlen (node->filename);
   1135           }
   1136 
   1137         if (node->filename)
   1138           filename = filename_non_directory (node->filename);
   1139 
   1140         if (node->flags & N_UpdateTags)
   1141           update_message = _("--*** Tags out of Date ***");
   1142       }
   1143 
   1144     if (update_message)
   1145       modeline_len += strlen (update_message);
   1146     modeline_len += strlen (filename);
   1147     modeline_len += strlen (nodename);
   1148     modeline_len += 4;          /* strlen (location_indicator). */
   1149 
   1150     /* 10 for the decimal representation of the number of lines in this
   1151        node, and the remainder of the text that can appear in the line. */
   1152     modeline_len += 10 + strlen (_("-----Info: (), lines ----, "));
   1153     modeline_len += window->width;
   1154 
   1155     modeline = xmalloc (1 + modeline_len);
   1156 
   1157     /* Special internal windows have no filename. */
   1158     if (!parent && !*filename)
   1159       sprintf (modeline, _("-%s---Info: %s, %d lines --%s--"),
   1160                (window->flags & W_NoWrap) ? "$" : "-",
   1161                nodename, window->line_count, location_indicator);
   1162     else
   1163       sprintf (modeline, _("-%s%s-Info: (%s)%s, %d lines --%s--"),
   1164                (window->flags & W_NoWrap) ? "$" : "-",
   1165                (node && (node->flags & N_IsCompressed)) ? "zz" : "--",
   1166                parent ? parent : filename,
   1167                nodename, window->line_count, location_indicator);
   1168 
   1169     if (parent)
   1170       sprintf (modeline + strlen (modeline), _(" Subfile: %s"), filename);
   1171 
   1172     if (update_message)
   1173       sprintf (modeline + strlen (modeline), "%s", update_message);
   1174 
   1175     i = strlen (modeline);
   1176 
   1177     if (i >= window->width)
   1178       modeline[window->width] = '\0';
   1179     else
   1180       {
   1181         while (i < window->width)
   1182           modeline[i++] = '-';
   1183         modeline[i] = '\0';
   1184       }
   1185 
   1186     strcpy (window->modeline, modeline);
   1187     free (modeline);
   1188   }
   1189 }
   1190 
   1191 /* Make WINDOW start displaying at PERCENT percentage of its node. */
   1192 void
   1193 window_goto_percentage (WINDOW *window, int percent)
   1194 {
   1195   int desired_line;
   1196 
   1197   if (!percent)
   1198     desired_line = 0;
   1199   else
   1200     desired_line =
   1201       (int) ((float)window->line_count * ((float)percent / 100.0));
   1202 
   1203   window->pagetop = desired_line;
   1204   window->point =
   1205     window->line_starts[window->pagetop] - window->node->contents;
   1206   window->flags |= W_UpdateWindow;
   1207   window_make_modeline (window);
   1208 }
   1209 
   1210 /* Get the state of WINDOW, and save it in STATE. */
   1211 void
   1212 window_get_state (WINDOW *window, SEARCH_STATE *state)
   1213 {
   1214   state->node = window->node;
   1215   state->pagetop = window->pagetop;
   1216   state->point = window->point;
   1217 }
   1218 
   1219 /* Set the node, pagetop, and point of WINDOW. */
   1220 void
   1221 window_set_state (WINDOW *window, SEARCH_STATE *state)
   1222 {
   1223   if (window->node != state->node)
   1224     window_set_node_of_window (window, state->node);
   1225   window->pagetop = state->pagetop;
   1226   window->point = state->point;
   1227 }
   1228 
   1229 
   1230 /* Manipulating home-made nodes.  */
   1232 
   1233 /* A place to buffer echo area messages. */
   1234 static NODE *echo_area_node = NULL;
   1235 
   1236 /* Make the node of the_echo_area be an empty one. */
   1237 static void
   1238 free_echo_area (void)
   1239 {
   1240   if (echo_area_node)
   1241     {
   1242       maybe_free (echo_area_node->contents);
   1243       free (echo_area_node);
   1244     }
   1245 
   1246   echo_area_node = NULL;
   1247   window_set_node_of_window (the_echo_area, echo_area_node);
   1248 }
   1249 
   1250 /* Clear the echo area, removing any message that is already present.
   1251    The echo area is cleared immediately. */
   1252 void
   1253 window_clear_echo_area (void)
   1254 {
   1255   free_echo_area ();
   1256   display_update_one_window (the_echo_area);
   1257 }
   1258 
   1259 /* Make a message appear in the echo area, built from FORMAT, ARG1 and ARG2.
   1260    The arguments are treated similar to printf () arguments, but not all of
   1261    printf () hair is present.  The message appears immediately.  If there was
   1262    already a message appearing in the echo area, it is removed. */
   1263 void
   1264 window_message_in_echo_area (char *format, void *arg1, void *arg2)
   1265 {
   1266   free_echo_area ();
   1267   echo_area_node = build_message_node (format, arg1, arg2);
   1268   window_set_node_of_window (the_echo_area, echo_area_node);
   1269   display_update_one_window (the_echo_area);
   1270 }
   1271 
   1272 /* Place a temporary message in the echo area built from FORMAT, ARG1
   1273    and ARG2.  The message appears immediately, but does not destroy
   1274    any existing message.  A future call to unmessage_in_echo_area ()
   1275    restores the old contents. */
   1276 static NODE **old_echo_area_nodes = NULL;
   1277 static int old_echo_area_nodes_index = 0;
   1278 static int old_echo_area_nodes_slots = 0;
   1279 
   1280 void
   1281 message_in_echo_area (char *format, void *arg1, void *arg2)
   1282 {
   1283   if (echo_area_node)
   1284     {
   1285       add_pointer_to_array (echo_area_node, old_echo_area_nodes_index,
   1286                             old_echo_area_nodes, old_echo_area_nodes_slots,
   1287                             4, NODE *);
   1288     }
   1289   echo_area_node = NULL;
   1290   window_message_in_echo_area (format, arg1, arg2);
   1291 }
   1292 
   1293 void
   1294 unmessage_in_echo_area (void)
   1295 {
   1296   free_echo_area ();
   1297 
   1298   if (old_echo_area_nodes_index)
   1299     echo_area_node = old_echo_area_nodes[--old_echo_area_nodes_index];
   1300 
   1301   window_set_node_of_window (the_echo_area, echo_area_node);
   1302   display_update_one_window (the_echo_area);
   1303 }
   1304 
   1305 /* A place to build a message. */
   1306 static char *message_buffer = NULL;
   1307 static int message_buffer_index = 0;
   1308 static int message_buffer_size = 0;
   1309 
   1310 /* Ensure that there is enough space to stuff LENGTH characters into
   1311    MESSAGE_BUFFER. */
   1312 static void
   1313 message_buffer_resize (int length)
   1314 {
   1315   if (!message_buffer)
   1316     {
   1317       message_buffer_size = length + 1;
   1318       message_buffer = xmalloc (message_buffer_size);
   1319       message_buffer_index = 0;
   1320     }
   1321 
   1322   while (message_buffer_size <= message_buffer_index + length)
   1323     message_buffer = (char *)
   1324       xrealloc (message_buffer,
   1325                 message_buffer_size += 100 + (2 * length));
   1326 }
   1327 
   1328 /* Format MESSAGE_BUFFER with the results of printing FORMAT with ARG1 and
   1329    ARG2. */
   1330 static void
   1331 build_message_buffer (char *format, void *arg1, void *arg2, void *arg3)
   1332 {
   1333   register int i, len;
   1334   void *args[3];
   1335   int arg_index = 0;
   1336 
   1337   args[0] = arg1;
   1338   args[1] = arg2;
   1339   args[2] = arg3;
   1340 
   1341   len = strlen (format);
   1342 
   1343   message_buffer_resize (len);
   1344 
   1345   for (i = 0; format[i]; i++)
   1346     {
   1347       if (format[i] != '%')
   1348         {
   1349           message_buffer[message_buffer_index++] = format[i];
   1350           len--;
   1351         }
   1352       else
   1353         {
   1354           char c;
   1355           char *fmt_start = format + i;
   1356           char *fmt;
   1357           int fmt_len, formatted_len;
   1358 	  int paramed = 0;
   1359 
   1360 	format_again:
   1361           i++;
   1362           while (format[i] && strchr ("-. +0123456789", format[i]))
   1363             i++;
   1364           c = format[i];
   1365 
   1366           if (c == '\0')
   1367             abort ();
   1368 
   1369 	  if (c == '$') {
   1370 	    /* position parameter parameter */
   1371 	    /* better to use bprintf from bfox's metahtml? */
   1372 	    arg_index = atoi(fmt_start + 1) - 1;
   1373 	    if (arg_index < 0)
   1374 	      arg_index = 0;
   1375 	    if (arg_index >= 2)
   1376 	      arg_index = 1;
   1377 	    paramed = 1;
   1378 	    goto format_again;
   1379 	  }
   1380 
   1381           fmt_len = format + i - fmt_start + 1;
   1382           fmt = (char *) xmalloc (fmt_len + 1);
   1383           strncpy (fmt, fmt_start, fmt_len);
   1384           fmt[fmt_len] = '\0';
   1385 
   1386 	  if (paramed) {
   1387 	    /* removed positioned parameter */
   1388 	    char *p;
   1389 	    for (p = fmt + 1; *p && *p != '$'; p++) {
   1390 	      ;
   1391 	    }
   1392 	    strcpy(fmt + 1, p + 1);
   1393 	  }
   1394 
   1395           /* If we have "%-98s", maybe 98 calls for a longer string.  */
   1396           if (fmt_len > 2)
   1397             {
   1398               int j;
   1399 
   1400               for (j = fmt_len - 2; j >= 0; j--)
   1401                 if (isdigit (fmt[j]) || fmt[j] == '$')
   1402                   break;
   1403 
   1404               formatted_len = atoi (fmt + j);
   1405             }
   1406           else
   1407             formatted_len = c == 's' ? 0 : 1; /* %s can produce empty string */
   1408 
   1409           switch (c)
   1410             {
   1411             case '%':           /* Insert a percent sign. */
   1412               message_buffer_resize (len + formatted_len);
   1413               sprintf
   1414                 (message_buffer + message_buffer_index, fmt, "%");
   1415               message_buffer_index += formatted_len;
   1416               break;
   1417 
   1418             case 's':           /* Insert the current arg as a string. */
   1419               {
   1420                 char *string;
   1421                 int string_len;
   1422 
   1423                 string = (char *)args[arg_index++];
   1424                 string_len = strlen (string);
   1425 
   1426                 if (formatted_len > string_len)
   1427                   string_len = formatted_len;
   1428                 message_buffer_resize (len + string_len);
   1429                 sprintf
   1430                   (message_buffer + message_buffer_index, fmt, string);
   1431                 message_buffer_index += string_len;
   1432               }
   1433               break;
   1434 
   1435             case 'd':           /* Insert the current arg as an integer. */
   1436               {
   1437                 long long_val;
   1438                 int integer;
   1439 
   1440                 long_val = (long)args[arg_index++];
   1441                 integer = (int)long_val;
   1442 
   1443                 message_buffer_resize (len + formatted_len > 32
   1444                                        ? formatted_len : 32);
   1445                 sprintf
   1446                   (message_buffer + message_buffer_index, fmt, integer);
   1447                 message_buffer_index = strlen (message_buffer);
   1448               }
   1449               break;
   1450 
   1451             case 'c':           /* Insert the current arg as a character. */
   1452               {
   1453                 long long_val;
   1454                 int character;
   1455 
   1456                 long_val = (long)args[arg_index++];
   1457                 character = (int)long_val;
   1458 
   1459                 message_buffer_resize (len + formatted_len);
   1460                 sprintf
   1461                   (message_buffer + message_buffer_index, fmt, character);
   1462                 message_buffer_index += formatted_len;
   1463               }
   1464               break;
   1465 
   1466             default:
   1467               abort ();
   1468             }
   1469           free (fmt);
   1470         }
   1471     }
   1472   message_buffer[message_buffer_index] = '\0';
   1473 }
   1474 
   1475 /* Build a new node which has FORMAT printed with ARG1 and ARG2 as the
   1476    contents. */
   1477 NODE *
   1478 build_message_node (char *format, void *arg1, void *arg2)
   1479 {
   1480   NODE *node;
   1481 
   1482   message_buffer_index = 0;
   1483   build_message_buffer (format, arg1, arg2, 0);
   1484 
   1485   node = message_buffer_to_node ();
   1486   return (node);
   1487 }
   1488 
   1489 /* Convert the contents of the message buffer to a node. */
   1490 NODE *
   1491 message_buffer_to_node (void)
   1492 {
   1493   NODE *node;
   1494 
   1495   node = xmalloc (sizeof (NODE));
   1496   node->filename = NULL;
   1497   node->parent = NULL;
   1498   node->nodename = NULL;
   1499   node->flags = 0;
   1500   node->display_pos =0;
   1501 
   1502   /* Make sure that this buffer ends with a newline. */
   1503   node->nodelen = 1 + strlen (message_buffer);
   1504   node->contents = xmalloc (1 + node->nodelen);
   1505   strcpy (node->contents, message_buffer);
   1506   node->contents[node->nodelen - 1] = '\n';
   1507   node->contents[node->nodelen] = '\0';
   1508   return (node);
   1509 }
   1510 
   1511 /* Useful functions can be called from outside of window.c. */
   1512 void
   1513 initialize_message_buffer (void)
   1514 {
   1515   message_buffer_index = 0;
   1516 }
   1517 
   1518 /* Print FORMAT with ARG1,2 to the end of the current message buffer. */
   1519 void
   1520 printf_to_message_buffer (char *format, void *arg1, void *arg2, void *arg3)
   1521 {
   1522   build_message_buffer (format, arg1, arg2, arg3);
   1523 }
   1524 
   1525 /* Return the current horizontal position of the "cursor" on the most
   1526    recently output message buffer line. */
   1527 int
   1528 message_buffer_length_this_line (void)
   1529 {
   1530   register int i;
   1531 
   1532   if (!message_buffer_index)
   1533     return (0);
   1534 
   1535   for (i = message_buffer_index; i && message_buffer[i - 1] != '\n'; i--);
   1536 
   1537   return (string_width (message_buffer + i, 0));
   1538 }
   1539 
   1540 /* Pad STRING to COUNT characters by inserting blanks. */
   1541 int
   1542 pad_to (int count, char *string)
   1543 {
   1544   register int i;
   1545 
   1546   i = strlen (string);
   1547 
   1548   if (i >= count)
   1549     string[i++] = ' ';
   1550   else
   1551     {
   1552       while (i < count)
   1553         string[i++] = ' ';
   1554     }
   1555   string[i] = '\0';
   1556 
   1557   return (i);
   1558 }
   1559