Back to index

tetex-bin  3.0
window.c
Go to the documentation of this file.
00001 /* window.c -- windows in Info.
00002    $Id: window.c,v 1.4 2004/04/11 17:56:46 karl Exp $
00003 
00004    Copyright (C) 1993, 1997, 1998, 2001, 2002, 2003, 2004 Free Software
00005    Foundation, Inc.
00006 
00007    This program is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU General Public License as published by
00009    the Free Software Foundation; either version 2, or (at your option)
00010    any later version.
00011 
00012    This program is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015    GNU General Public License for more details.
00016 
00017    You should have received a copy of the GNU General Public License
00018    along with this program; if not, write to the Free Software
00019    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00020 
00021    Written by Brian Fox (bfox@ai.mit.edu). */
00022 
00023 #include "info.h"
00024 #include "nodes.h"
00025 #include "window.h"
00026 #include "display.h"
00027 #include "info-utils.h"
00028 #include "infomap.h"
00029 
00030 /* The window which describes the screen. */
00031 WINDOW *the_screen = NULL;
00032 
00033 /* The window which describes the echo area. */
00034 WINDOW *the_echo_area = NULL;
00035 
00036 /* The list of windows in Info. */
00037 WINDOW *windows = NULL;
00038 
00039 /* Pointer to the active window in WINDOW_LIST. */
00040 WINDOW *active_window = NULL;
00041 
00042 /* The size of the echo area in Info.  It never changes, irregardless of the
00043    size of the screen. */
00044 #define ECHO_AREA_HEIGHT 1
00045 
00046 /* Macro returns the amount of space that the echo area truly requires relative
00047    to the entire screen. */
00048 #define echo_area_required (1 + the_echo_area->height)
00049 
00050 /* Initalize the window system by creating THE_SCREEN and THE_ECHO_AREA.
00051    Create the first window ever.
00052    You pass the dimensions of the total screen size. */
00053 void
00054 window_initialize_windows (int width, int height)
00055 {
00056   the_screen = xmalloc (sizeof (WINDOW));
00057   the_echo_area = xmalloc (sizeof (WINDOW));
00058   windows = xmalloc (sizeof (WINDOW));
00059   active_window = windows;
00060 
00061   zero_mem (the_screen, sizeof (WINDOW));
00062   zero_mem (the_echo_area, sizeof (WINDOW));
00063   zero_mem (active_window, sizeof (WINDOW));
00064 
00065   /* None of these windows has a goal column yet. */
00066   the_echo_area->goal_column = -1;
00067   active_window->goal_column = -1;
00068   the_screen->goal_column = -1;
00069 
00070   /* The active and echo_area windows are visible.
00071      The echo_area is permanent.
00072      The screen is permanent. */
00073   active_window->flags = W_WindowVisible;
00074   the_echo_area->flags = W_WindowIsPerm | W_InhibitMode | W_WindowVisible;
00075   the_screen->flags    = W_WindowIsPerm;
00076 
00077   /* The height of the echo area never changes.  It is statically set right
00078      here, and it must be at least 1 line for display.  The size of the
00079      initial window cannot be the same size as the screen, since the screen
00080      includes the echo area.  So, we make the height of the initial window
00081      equal to the screen's displayable region minus the height of the echo
00082      area. */
00083   the_echo_area->height = ECHO_AREA_HEIGHT;
00084   active_window->height = the_screen->height - 1 - the_echo_area->height;
00085   window_new_screen_size (width, height);
00086 
00087   /* The echo area uses a different keymap than normal info windows. */
00088   the_echo_area->keymap = echo_area_keymap;
00089   active_window->keymap = info_keymap;
00090 }
00091 
00092 /* Given that the size of the screen has changed to WIDTH and HEIGHT
00093    from whatever it was before (found in the_screen->height, ->width),
00094    change the size (and possibly location) of each window in the screen.
00095    If a window would become too small, call the function DELETER on it,
00096    after deleting the window from our chain of windows.  If DELETER is NULL,
00097    nothing extra is done.  The last window can never be deleted, but it can
00098    become invisible. */
00099 
00100 /* If non-null, a function to call with WINDOW as argument when the function
00101    window_new_screen_size () has deleted WINDOW. */
00102 VFunction *window_deletion_notifier = NULL;
00103 
00104 void
00105 window_new_screen_size (int width, int height)
00106 {
00107   register WINDOW *win;
00108   int delta_height, delta_each, delta_leftover;
00109   int numwins;
00110 
00111   /* If no change, do nothing. */
00112   if (width == the_screen->width && height == the_screen->height)
00113     return;
00114 
00115   /* If the new window height is too small, make it be zero. */
00116   if (height < (WINDOW_MIN_SIZE + the_echo_area->height))
00117     height = 0;
00118   if (width < 0)
00119     width = 0;
00120 
00121   /* Find out how many windows will change. */
00122   for (numwins = 0, win = windows; win; win = win->next, numwins++);
00123 
00124   /* See if some windows will need to be deleted.  This is the case if
00125      the screen is getting smaller, and the available space divided by
00126      the number of windows is less than WINDOW_MIN_SIZE.  In that case,
00127      delete some windows and try again until there is either enough
00128      space to divy up among the windows, or until there is only one
00129      window left. */
00130   while ((height - echo_area_required) / numwins <= WINDOW_MIN_SIZE)
00131     {
00132       /* If only one window, make the size of it be zero, and return
00133          immediately. */
00134       if (!windows->next)
00135         {
00136           windows->height = 0;
00137           maybe_free (windows->line_starts);
00138           windows->line_starts = NULL;
00139           windows->line_count = 0;
00140           break;
00141         }
00142 
00143       /* If we have some temporary windows, delete one of them. */
00144       for (win = windows; win; win = win->next)
00145         if (win->flags & W_TempWindow)
00146           break;
00147 
00148       /* Otherwise, delete the first window, and try again. */
00149       if (!win)
00150         win = windows;
00151 
00152       if (window_deletion_notifier)
00153         (*window_deletion_notifier) (win);
00154 
00155       window_delete_window (win);
00156       numwins--;
00157     }
00158 
00159   /* The screen has changed height and width. */
00160   delta_height = height - the_screen->height;   /* This is how much. */
00161   the_screen->height = height;                  /* This is the new height. */
00162   the_screen->width = width;                    /* This is the new width. */
00163 
00164   /* Set the start of the echo area. */
00165   the_echo_area->first_row = height - the_echo_area->height;
00166   the_echo_area->width = width;
00167 
00168   /* Check to see if the screen can really be changed this way. */
00169   if ((!windows->next) && ((windows->height == 0) && (delta_height < 0)))
00170     return;
00171 
00172   /* Divide the change in height among the available windows. */
00173   delta_each = delta_height / numwins;
00174   delta_leftover = delta_height - (delta_each * numwins);
00175 
00176   /* Change the height of each window in the chain by delta_each.  Change
00177      the height of the last window in the chain by delta_each and by the
00178      leftover amount of change.  Change the width of each window to be
00179      WIDTH. */
00180   for (win = windows; win; win = win->next)
00181     {
00182       if ((win->width != width) && ((win->flags & W_InhibitMode) == 0))
00183         {
00184           win->width = width;
00185           maybe_free (win->modeline);
00186           win->modeline = xmalloc (1 + width);
00187         }
00188 
00189       win->height += delta_each;
00190 
00191       /* If the previous height of this window was zero, it was the only
00192          window, and it was not visible.  Thus we need to compensate for
00193          the echo_area. */
00194       if (win->height == delta_each)
00195         win->height -= (1 + the_echo_area->height);
00196 
00197       /* If this is not the first window in the chain, then change the
00198          first row of it.  We cannot just add delta_each to the first row,
00199          since this window's first row is the sum of the collective increases
00200          that have gone before it.  So we just add one to the location of the
00201          previous window's modeline. */
00202       if (win->prev)
00203         win->first_row = (win->prev->first_row + win->prev->height) + 1;
00204 
00205       /* The last window in the chain gets the extra space (or shrinkage). */
00206       if (!win->next)
00207         win->height += delta_leftover;
00208 
00209       if (win->node)
00210         recalculate_line_starts (win);
00211 
00212       win->flags |= W_UpdateWindow;
00213     }
00214 
00215   /* If the screen got smaller, check over the windows just shrunk to
00216      keep them within bounds.  Some of the windows may have gotten smaller
00217      than WINDOW_MIN_HEIGHT in which case some of the other windows are
00218      larger than the available display space in the screen.  Because of our
00219      intial test above, we know that there is enough space for all of the
00220      windows. */
00221   if ((delta_each < 0) && ((windows->height != 0) && windows->next))
00222     {
00223       int avail;
00224 
00225       avail = the_screen->height - (numwins + the_echo_area->height);
00226       win = windows;
00227 
00228       while (win)
00229         {
00230           if ((win->height < WINDOW_MIN_HEIGHT) ||
00231               (win->height > avail))
00232             {
00233               WINDOW *lastwin = NULL;
00234 
00235               /* Split the space among the available windows. */
00236               delta_each = avail / numwins;
00237               delta_leftover = avail - (delta_each * numwins);
00238 
00239               for (win = windows; win; win = win->next)
00240                 {
00241                   lastwin = win;
00242                   if (win->prev)
00243                     win->first_row =
00244                       (win->prev->first_row + win->prev->height) + 1;
00245                   win->height = delta_each;
00246                 }
00247 
00248               /* Give the leftover space (if any) to the last window. */
00249               lastwin->height += delta_leftover;
00250               break;
00251             }
00252           else
00253             win= win->next;
00254         }
00255     }
00256 }
00257 
00258 /* Make a new window showing NODE, and return that window structure.
00259    If NODE is passed as NULL, then show the node showing in the active
00260    window.  If the window could not be made return a NULL pointer.  The
00261    active window is not changed.*/
00262 WINDOW *
00263 window_make_window (NODE *node)
00264 {
00265   WINDOW *window;
00266 
00267   if (!node)
00268     node = active_window->node;
00269 
00270   /* If there isn't enough room to make another window, return now. */
00271   if ((active_window->height / 2) < WINDOW_MIN_SIZE)
00272     return (NULL);
00273 
00274   /* Make and initialize the new window.
00275      The fudging about with -1 and +1 is because the following window in the
00276      chain cannot start at window->height, since that is where the modeline
00277      for the previous window is displayed.  The inverse adjustment is made
00278      in window_delete_window (). */
00279   window = xmalloc (sizeof (WINDOW));
00280   window->width = the_screen->width;
00281   window->height = (active_window->height / 2) - 1;
00282 #if defined (SPLIT_BEFORE_ACTIVE)
00283   window->first_row = active_window->first_row;
00284 #else
00285   window->first_row = active_window->first_row +
00286     (active_window->height - window->height);
00287 #endif
00288   window->keymap = info_keymap;
00289   window->goal_column = -1;
00290   window->modeline = xmalloc (1 + window->width);
00291   window->line_starts = NULL;
00292   window->flags = W_UpdateWindow | W_WindowVisible;
00293   window_set_node_of_window (window, node);
00294 
00295   /* Adjust the height of the old active window. */
00296   active_window->height -= (window->height + 1);
00297 #if defined (SPLIT_BEFORE_ACTIVE)
00298   active_window->first_row += (window->height + 1);
00299 #endif
00300   active_window->flags |= W_UpdateWindow;
00301 
00302   /* Readjust the new and old windows so that their modelines and contents
00303      will be displayed correctly. */
00304 #if defined (NOTDEF)
00305   /* We don't have to do this for WINDOW since window_set_node_of_window ()
00306      already did. */
00307   window_adjust_pagetop (window);
00308   window_make_modeline (window);
00309 #endif /* NOTDEF */
00310 
00311   /* We do have to readjust the existing active window. */
00312   window_adjust_pagetop (active_window);
00313   window_make_modeline (active_window);
00314 
00315 #if defined (SPLIT_BEFORE_ACTIVE)
00316   /* This window is just before the active one.  The active window gets
00317      bumped down one.  The active window is not changed. */
00318   window->next = active_window;
00319 
00320   window->prev = active_window->prev;
00321   active_window->prev = window;
00322 
00323   if (window->prev)
00324     window->prev->next = window;
00325   else
00326     windows = window;
00327 #else
00328   /* This window is just after the active one.  Which window is active is
00329      not changed. */
00330   window->prev = active_window;
00331   window->next = active_window->next;
00332   active_window->next = window;
00333   if (window->next)
00334     window->next->prev = window;
00335 #endif /* !SPLIT_BEFORE_ACTIVE */
00336   return (window);
00337 }
00338 
00339 /* These useful macros make it possible to read the code in
00340    window_change_window_height (). */
00341 #define grow_me_shrinking_next(me, next, diff) \
00342   do { \
00343     me->height += diff; \
00344     next->height -= diff; \
00345     next->first_row += diff; \
00346     window_adjust_pagetop (next); \
00347   } while (0)
00348 
00349 #define grow_me_shrinking_prev(me, prev, diff) \
00350   do { \
00351     me->height += diff; \
00352     prev->height -= diff; \
00353     me->first_row -=diff; \
00354     window_adjust_pagetop (prev); \
00355   } while (0)
00356 
00357 #define shrink_me_growing_next(me, next, diff) \
00358   do { \
00359     me->height -= diff; \
00360     next->height += diff; \
00361     next->first_row -= diff; \
00362     window_adjust_pagetop (next); \
00363   } while (0)
00364 
00365 #define shrink_me_growing_prev(me, prev, diff) \
00366   do { \
00367     me->height -= diff; \
00368     prev->height += diff; \
00369     me->first_row += diff; \
00370     window_adjust_pagetop (prev); \
00371   } while (0)
00372 
00373 /* Change the height of WINDOW by AMOUNT.  This also automagically adjusts
00374    the previous and next windows in the chain.  If there is only one user
00375    window, then no change takes place. */
00376 void
00377 window_change_window_height (WINDOW *window, int amount)
00378 {
00379   register WINDOW *win, *prev, *next;
00380 
00381   /* If there is only one window, or if the amount of change is zero,
00382      return immediately. */
00383   if (!windows->next || amount == 0)
00384     return;
00385 
00386   /* Find this window in our chain. */
00387   for (win = windows; win; win = win->next)
00388     if (win == window)
00389       break;
00390 
00391   /* If the window is isolated (i.e., doesn't appear in our window list,
00392      then quit now. */
00393   if (!win)
00394     return;
00395 
00396   /* Change the height of this window by AMOUNT, if that is possible.
00397      It can be impossible if there isn't enough available room on the
00398      screen, or if the resultant window would be too small. */
00399 
00400     prev = window->prev;
00401     next = window->next;
00402 
00403   /* WINDOW decreasing in size? */
00404   if (amount < 0)
00405     {
00406       int abs_amount = -amount; /* It is easier to deal with this way. */
00407 
00408       /* If the resultant window would be too small, stop here. */
00409       if ((window->height - abs_amount) < WINDOW_MIN_HEIGHT)
00410         return;
00411 
00412       /* If we have two neighboring windows, choose the smaller one to get
00413          larger. */
00414       if (next && prev)
00415         {
00416           if (prev->height < next->height)
00417             shrink_me_growing_prev (window, prev, abs_amount);
00418           else
00419             shrink_me_growing_next (window, next, abs_amount);
00420         }
00421       else if (next)
00422         shrink_me_growing_next (window, next, abs_amount);
00423       else
00424         shrink_me_growing_prev (window, prev, abs_amount);
00425     }
00426 
00427   /* WINDOW increasing in size? */
00428   if (amount > 0)
00429     {
00430       int total_avail, next_avail = 0, prev_avail = 0;
00431 
00432       if (next)
00433         next_avail = next->height - WINDOW_MIN_SIZE;
00434 
00435       if (prev)
00436         prev_avail = prev->height - WINDOW_MIN_SIZE;
00437 
00438       total_avail = next_avail + prev_avail;
00439 
00440       /* If there isn't enough space available to grow this window, give up. */
00441       if (amount > total_avail)
00442         return;
00443 
00444       /* If there aren't two neighboring windows, or if one of the neighbors
00445          is larger than the other one by at least AMOUNT, grow that one. */
00446       if ((next && !prev) || ((next_avail - amount) >= prev_avail))
00447         grow_me_shrinking_next (window, next, amount);
00448       else if ((prev && !next) || ((prev_avail - amount) >= next_avail))
00449         grow_me_shrinking_prev (window, prev, amount);
00450       else
00451         {
00452           int change;
00453 
00454           /* This window has two neighbors.  They both must be shrunk in to
00455              make enough space for WINDOW to grow.  Make them both the same
00456              size. */
00457           if (prev_avail > next_avail)
00458             {
00459               change = prev_avail - next_avail;
00460               grow_me_shrinking_prev (window, prev, change);
00461               amount -= change;
00462             }
00463           else
00464             {
00465               change = next_avail - prev_avail;
00466               grow_me_shrinking_next (window, next, change);
00467               amount -= change;
00468             }
00469 
00470           /* Both neighbors are the same size.  Split the difference in
00471              AMOUNT between them. */
00472           while (amount)
00473             {
00474               window->height++;
00475               amount--;
00476 
00477               /* Odd numbers grow next, even grow prev. */
00478               if (amount & 1)
00479                 {
00480                   prev->height--;
00481                   window->first_row--;
00482                 }
00483               else
00484                 {
00485                   next->height--;
00486                   next->first_row++;
00487                 }
00488             }
00489           window_adjust_pagetop (prev);
00490           window_adjust_pagetop (next);
00491         }
00492     }
00493   if (prev)
00494     prev->flags |= W_UpdateWindow;
00495 
00496   if (next)
00497     next->flags |= W_UpdateWindow;
00498 
00499   window->flags |= W_UpdateWindow;
00500   window_adjust_pagetop (window);
00501 }
00502 
00503 /* Tile all of the windows currently displayed in the global variable
00504    WINDOWS.  If argument STYLE is TILE_INTERNALS, tile windows displaying
00505    internal nodes as well, otherwise do not change the height of such
00506    windows. */
00507 void
00508 window_tile_windows (int style)
00509 {
00510   WINDOW *win, *last_adjusted;
00511   int numwins, avail, per_win_height, leftover;
00512   int do_internals;
00513 
00514   numwins = avail = 0;
00515   do_internals = (style == TILE_INTERNALS);
00516 
00517   for (win = windows; win; win = win->next)
00518     if (do_internals || !win->node ||
00519         (win->node->flags & N_IsInternal) == 0)
00520       {
00521         avail += win->height;
00522         numwins++;
00523       }
00524 
00525   if (numwins <= 1 || !the_screen->height)
00526     return;
00527 
00528   /* Find the size for each window.  Divide the size of the usable portion
00529      of the screen by the number of windows. */
00530   per_win_height = avail / numwins;
00531   leftover = avail - (per_win_height * numwins);
00532 
00533   last_adjusted = NULL;
00534   for (win = windows; win; win = win->next)
00535     {
00536       if (do_internals || !win->node ||
00537           (win->node->flags & N_IsInternal) == 0)
00538         {
00539           last_adjusted = win;
00540           win->height = per_win_height;
00541         }
00542     }
00543 
00544   if (last_adjusted)
00545     last_adjusted->height += leftover;
00546 
00547   /* Readjust the first_row of every window in the chain. */
00548   for (win = windows; win; win = win->next)
00549     {
00550       if (win->prev)
00551         win->first_row = win->prev->first_row + win->prev->height + 1;
00552 
00553       window_adjust_pagetop (win);
00554       win->flags |= W_UpdateWindow;
00555     }
00556 }
00557 
00558 /* Toggle the state of line wrapping in WINDOW.  This can do a bit of fancy
00559    redisplay. */
00560 void
00561 window_toggle_wrap (WINDOW *window)
00562 {
00563   if (window->flags & W_NoWrap)
00564     window->flags &= ~W_NoWrap;
00565   else
00566     window->flags |= W_NoWrap;
00567 
00568   if (window != the_echo_area)
00569     {
00570       char **old_starts;
00571       int old_lines, old_pagetop;
00572 
00573       old_starts = window->line_starts;
00574       old_lines = window->line_count;
00575       old_pagetop = window->pagetop;
00576 
00577       calculate_line_starts (window);
00578 
00579       /* Make sure that point appears within this window. */
00580       window_adjust_pagetop (window);
00581 
00582       /* If the pagetop hasn't changed maybe we can do some scrolling now
00583          to speed up the display.  Many of the line starts will be the same,
00584          so scrolling here is a very good optimization.*/
00585       if (old_pagetop == window->pagetop)
00586         display_scroll_line_starts
00587           (window, old_pagetop, old_starts, old_lines);
00588       maybe_free (old_starts);
00589     }
00590   window->flags |= W_UpdateWindow;
00591 }
00592 
00593 /* Set WINDOW to display NODE. */
00594 void
00595 window_set_node_of_window (WINDOW *window, NODE *node)
00596 {
00597   window->node = node;
00598   window->pagetop = 0;
00599   window->point = 0;
00600   recalculate_line_starts (window);
00601   window->flags |= W_UpdateWindow;
00602   /* The display_pos member is nonzero if we're displaying an anchor.  */
00603   window->point = node ? node->display_pos : 0;
00604   window_adjust_pagetop (window);
00605   window_make_modeline (window);
00606 }
00607 
00608 /* Delete WINDOW from the list of known windows.  If this window was the
00609    active window, make the next window in the chain be the active window.
00610    If the active window is the next or previous window, choose that window
00611    as the recipient of the extra space.  Otherwise, prefer the next window. */
00612 void
00613 window_delete_window (WINDOW *window)
00614 {
00615   WINDOW *next, *prev, *window_to_fix;
00616 
00617   next = window->next;
00618   prev = window->prev;
00619 
00620   /* You cannot delete the only window or a permanent window. */
00621   if ((!next && !prev) || (window->flags & W_WindowIsPerm))
00622     return;
00623 
00624   if (next)
00625     next->prev = prev;
00626 
00627   if (!prev)
00628     windows = next;
00629   else
00630     prev->next = next;
00631 
00632   if (window->line_starts)
00633     free (window->line_starts);
00634 
00635   if (window->modeline)
00636     free (window->modeline);
00637 
00638   if (window == active_window)
00639     {
00640       /* If there isn't a next window, then there must be a previous one,
00641          since we cannot delete the last window.  If there is a next window,
00642          prefer to use that as the active window. */
00643       if (next)
00644         active_window = next;
00645       else
00646         active_window = prev;
00647     }
00648 
00649   if (next && active_window == next)
00650     window_to_fix = next;
00651   else if (prev && active_window == prev)
00652     window_to_fix = prev;
00653   else if (next)
00654     window_to_fix = next;
00655   else if (prev)
00656     window_to_fix = prev;
00657   else
00658     window_to_fix = windows;
00659     
00660   if (window_to_fix->first_row > window->first_row)
00661     {
00662       int diff;
00663 
00664       /* Try to adjust the visible part of the node so that as little
00665          text as possible has to move. */
00666       diff = window_to_fix->first_row - window->first_row;
00667       window_to_fix->first_row = window->first_row;
00668 
00669       window_to_fix->pagetop -= diff;
00670       if (window_to_fix->pagetop < 0)
00671         window_to_fix->pagetop = 0;
00672     }
00673 
00674   /* The `+ 1' is to offset the difference between the first_row locations.
00675      See the code in window_make_window (). */
00676   window_to_fix->height += window->height + 1;
00677   window_to_fix->flags |= W_UpdateWindow;
00678 
00679   free (window);
00680 }
00681 
00682 /* For every window in CHAIN, set the flags member to have FLAG set. */
00683 void
00684 window_mark_chain (WINDOW *chain, int flag)
00685 {
00686   register WINDOW *win;
00687 
00688   for (win = chain; win; win = win->next)
00689     win->flags |= flag;
00690 }
00691 
00692 /* For every window in CHAIN, clear the flags member of FLAG. */
00693 void
00694 window_unmark_chain (WINDOW *chain, int flag)
00695 {
00696   register WINDOW *win;
00697 
00698   for (win = chain; win; win = win->next)
00699     win->flags &= ~flag;
00700 }
00701 
00702 /* Return the number of characters it takes to display CHARACTER on the
00703    screen at HPOS. */
00704 int
00705 character_width (int character, int hpos)
00706 {
00707   int printable_limit = 127;
00708   int width = 1;
00709 
00710   if (ISO_Latin_p)
00711     printable_limit = 255;
00712 
00713   if (character > printable_limit)
00714     width = 3;
00715   else if (iscntrl (character))
00716     {
00717       switch (character)
00718         {
00719         case '\r':
00720         case '\n':
00721           width = the_screen->width - hpos;
00722           break;
00723         case '\t':
00724           width = ((hpos + 8) & 0xf8) - hpos;
00725           break;
00726         default:
00727           width = 2;
00728         }
00729     }
00730   else if (character == DEL)
00731     width = 2;
00732 
00733   return (width);
00734 }
00735 
00736 /* Return the number of characters it takes to display STRING on the screen
00737    at HPOS. */
00738 int
00739 string_width (char *string, int hpos)
00740 {
00741   register int i, width, this_char_width;
00742 
00743   for (width = 0, i = 0; string[i]; i++)
00744     {
00745       /* Support ANSI escape sequences for -R.  */
00746       if (raw_escapes_p
00747          && string[i] == '\033'
00748          && string[i+1] == '['
00749          && isdigit (string[i+2])
00750          && (string[i+3] == 'm'
00751              || (isdigit (string[i+3]) && string[i+4] == 'm')))
00752        {
00753          while (string[i] != 'm')
00754            i++;
00755          this_char_width = 0;
00756        }
00757       else
00758        this_char_width = character_width (string[i], hpos);
00759       width += this_char_width;
00760       hpos += this_char_width;
00761     }
00762   return (width);
00763 }
00764 
00765 /* Quickly guess the approximate number of lines that NODE would
00766    take to display.  This really only counts carriage returns. */
00767 int
00768 window_physical_lines (NODE *node)
00769 {
00770   register int i, lines;
00771   char *contents;
00772 
00773   if (!node)
00774     return (0);
00775 
00776   contents = node->contents;
00777   for (i = 0, lines = 1; i < node->nodelen; i++)
00778     if (contents[i] == '\n')
00779       lines++;
00780 
00781   return (lines);
00782 }
00783 
00784 /* Calculate a list of line starts for the node belonging to WINDOW.  The line
00785    starts are pointers to the actual text within WINDOW->NODE. */
00786 void
00787 calculate_line_starts (WINDOW *window)
00788 {
00789   register int i, hpos;
00790   char **line_starts = NULL;
00791   int line_starts_index = 0, line_starts_slots = 0;
00792   int bump_index;
00793   NODE *node;
00794 
00795   window->line_starts = NULL;
00796   window->line_count = 0;
00797   node = window->node;
00798 
00799   if (!node)
00800     return;
00801 
00802   /* Grovel the node starting at the top, and for each line calculate the
00803      width of the characters appearing in that line.  Add each line start
00804      to our array. */
00805   i = 0;
00806   hpos = 0;
00807   bump_index = 0;
00808 
00809   while (i < node->nodelen)
00810     {
00811       char *line = node->contents + i;
00812       unsigned int cwidth, c;
00813 
00814       add_pointer_to_array (line, line_starts_index, line_starts,
00815                             line_starts_slots, 100, char *);
00816       if (bump_index)
00817         {
00818           i++;
00819           bump_index = 0;
00820         }
00821 
00822       while (1)
00823         {
00824          /* The cast to unsigned char is for 8-bit characters, which
00825             could be passed as negative integers to character_width
00826             and wreak havoc on some naive implementations of iscntrl.  */
00827           c = (unsigned char) node->contents[i];
00828 
00829          /* Support ANSI escape sequences for -R.  */
00830          if (raw_escapes_p
00831              && c == '\033'
00832              && node->contents[i+1] == '['
00833              && isdigit (node->contents[i+2]))
00834            {
00835              if (node->contents[i+3] == 'm')
00836               {
00837                 i += 3;
00838                 cwidth = 0;
00839               }
00840              else if (isdigit (node->contents[i+3])
00841                      && node->contents[i+4] == 'm')
00842               {
00843                 i += 4;
00844                 cwidth = 0;
00845               }
00846              else
00847               cwidth = character_width (c, hpos);
00848            }
00849          else
00850            cwidth = character_width (c, hpos);
00851 
00852           /* If this character fits within this line, just do the next one. */
00853           if ((hpos + cwidth) < (unsigned int) window->width)
00854             {
00855               i++;
00856               hpos += cwidth;
00857               continue;
00858             }
00859           else
00860             {
00861               /* If this character would position the cursor at the start of
00862                  the next printed screen line, then do the next line. */
00863               if (c == '\n' || c == '\r' || c == '\t')
00864                 {
00865                   i++;
00866                   hpos = 0;
00867                   break;
00868                 }
00869               else
00870                 {
00871                   /* This character passes the window width border.  Postion
00872                      the cursor after the printed character, but remember this
00873                      line start as where this character is.  A bit tricky. */
00874 
00875                   /* If this window doesn't wrap lines, proceed to the next
00876                      physical line here. */
00877                   if (window->flags & W_NoWrap)
00878                     {
00879                       hpos = 0;
00880                       while (i < node->nodelen && node->contents[i] != '\n')
00881                         i++;
00882 
00883                       if (node->contents[i] == '\n')
00884                         i++;
00885                     }
00886                   else
00887                     {
00888                       hpos = the_screen->width - hpos;
00889                       bump_index++;
00890                     }
00891                   break;
00892                 }
00893             }
00894         }
00895     }
00896   window->line_starts = line_starts;
00897   window->line_count = line_starts_index;
00898 }
00899 
00900 /* Given WINDOW, recalculate the line starts for the node it displays. */
00901 void
00902 recalculate_line_starts (WINDOW *window)
00903 {
00904   maybe_free (window->line_starts);
00905   calculate_line_starts (window);
00906 }
00907 
00908 /* Global variable control redisplay of scrolled windows.  If non-zero, it
00909    is the desired number of lines to scroll the window in order to make
00910    point visible.  A user might set this to 1 for smooth scrolling.  If
00911    set to zero, the line containing point is centered within the window. */
00912 int window_scroll_step = 0;
00913 
00914 /* Adjust the pagetop of WINDOW such that the cursor point will be visible. */
00915 void
00916 window_adjust_pagetop (WINDOW *window)
00917 {
00918   register int line = 0;
00919   char *contents;
00920 
00921   if (!window->node)
00922     return;
00923 
00924   contents = window->node->contents;
00925 
00926   /* Find the first printed line start which is after WINDOW->point. */
00927   for (line = 0; line < window->line_count; line++)
00928     {
00929       char *line_start;
00930 
00931       line_start = window->line_starts[line];
00932 
00933       if ((line_start - contents) > window->point)
00934         break;
00935     }
00936 
00937   /* The line index preceding the line start which is past point is the
00938      one containing point. */
00939   line--;
00940 
00941   /* If this line appears in the current displayable page, do nothing.
00942      Otherwise, adjust the top of the page to make this line visible. */
00943   if ((line < window->pagetop) ||
00944       (line - window->pagetop > (window->height - 1)))
00945     {
00946       /* The user-settable variable "scroll-step" is used to attempt
00947          to make point visible, iff it is non-zero.  If that variable
00948          is zero, then the line containing point is centered within
00949          the window. */
00950       if (window_scroll_step < window->height)
00951         {
00952           if ((line < window->pagetop) &&
00953               ((window->pagetop - window_scroll_step) <= line))
00954             window->pagetop -= window_scroll_step;
00955           else if ((line - window->pagetop > (window->height - 1)) &&
00956                    ((line - (window->pagetop + window_scroll_step)
00957                      < window->height)))
00958             window->pagetop += window_scroll_step;
00959           else
00960             window->pagetop = line - ((window->height - 1) / 2);
00961         }
00962       else
00963         window->pagetop = line - ((window->height - 1) / 2);
00964 
00965       if (window->pagetop < 0)
00966         window->pagetop = 0;
00967       window->flags |= W_UpdateWindow;
00968     }
00969 }
00970 
00971 /* Return the index of the line containing point. */
00972 int
00973 window_line_of_point (WINDOW *window)
00974 {
00975   register int i, start = 0;
00976 
00977   /* Try to optimize.  Check to see if point is past the pagetop for
00978      this window, and if so, start searching forward from there. */
00979   if ((window->pagetop > -1 && window->pagetop < window->line_count) &&
00980       (window->line_starts[window->pagetop] - window->node->contents)
00981       <= window->point)
00982     start = window->pagetop;
00983 
00984   for (i = start; i < window->line_count; i++)
00985     {
00986       if ((window->line_starts[i] - window->node->contents) > window->point)
00987         break;
00988     }
00989 
00990   return (i - 1);
00991 }
00992 
00993 /* Get and return the goal column for this window. */
00994 int
00995 window_get_goal_column (WINDOW *window)
00996 {
00997   if (!window->node)
00998     return (-1);
00999 
01000   if (window->goal_column != -1)
01001     return (window->goal_column);
01002 
01003   /* Okay, do the work.  Find the printed offset of the cursor
01004      in this window. */
01005   return (window_get_cursor_column (window));
01006 }
01007 
01008 /* Get and return the printed column offset of the cursor in this window. */
01009 int
01010 window_get_cursor_column (WINDOW *window)
01011 {
01012   int i, hpos, end;
01013   char *line;
01014 
01015   i = window_line_of_point (window);
01016 
01017   if (i < 0)
01018     return (-1);
01019 
01020   line = window->line_starts[i];
01021   end = window->point - (line - window->node->contents);
01022 
01023   for (hpos = 0, i = 0; i < end; i++)
01024     {
01025       /* Support ANSI escape sequences for -R.  */
01026       if (raw_escapes_p
01027          && line[i] == '\033'
01028          && line[i+1] == '['
01029          && isdigit (line[i+2]))
01030        {
01031          if (line[i+3] == 'm')
01032            i += 3;
01033          else if (isdigit (line[i+3]) && line[i+4] == 'm')
01034            i += 4;
01035          else
01036            hpos += character_width (line[i], hpos);
01037        }
01038       else
01039        hpos += character_width (line[i], hpos);
01040     }
01041 
01042   return (hpos);
01043 }
01044 
01045 /* Count the number of characters in LINE that precede the printed column
01046    offset of GOAL. */
01047 int
01048 window_chars_to_goal (char *line, int goal)
01049 {
01050   register int i, check = 0, hpos;
01051 
01052   for (hpos = 0, i = 0; line[i] != '\n'; i++)
01053     {
01054       /* Support ANSI escape sequences for -R.  */
01055       if (raw_escapes_p
01056          && line[i] == '\033'
01057          && line[i+1] == '['
01058          && isdigit (line[i+2])
01059          && (line[i+3] == 'm'
01060              || (isdigit (line[i+3]) && line[i+4] == 'm')))
01061        while (line[i] != 'm')
01062          i++;
01063       else
01064        check = hpos + character_width (line[i], hpos);
01065 
01066       if (check > goal)
01067         break;
01068 
01069       hpos = check;
01070     }
01071   return (i);
01072 }
01073 
01074 /* Create a modeline for WINDOW, and store it in window->modeline. */
01075 void
01076 window_make_modeline (WINDOW *window)
01077 {
01078   register int i;
01079   char *modeline;
01080   char location_indicator[4];
01081   int lines_remaining;
01082 
01083   /* Only make modelines for those windows which have one. */
01084   if (window->flags & W_InhibitMode)
01085     return;
01086 
01087   /* Find the number of lines actually displayed in this window. */
01088   lines_remaining = window->line_count - window->pagetop;
01089 
01090   if (window->pagetop == 0)
01091     {
01092       if (lines_remaining <= window->height)
01093         strcpy (location_indicator, "All");
01094       else
01095         strcpy (location_indicator, "Top");
01096     }
01097   else
01098     {
01099       if (lines_remaining <= window->height)
01100         strcpy (location_indicator, "Bot");
01101       else
01102         {
01103           float pt, lc;
01104           int percentage;
01105 
01106           pt = (float)window->pagetop;
01107           lc = (float)window->line_count;
01108 
01109           percentage = 100 * (pt / lc);
01110 
01111           sprintf (location_indicator, "%2d%%", percentage);
01112         }
01113     }
01114 
01115   /* Calculate the maximum size of the information to stick in MODELINE. */
01116   {
01117     int modeline_len = 0;
01118     char *parent = NULL, *filename = "*no file*";
01119     char *nodename = "*no node*";
01120     const char *update_message = NULL;
01121     NODE *node = window->node;
01122 
01123     if (node)
01124       {
01125         if (node->nodename)
01126           nodename = node->nodename;
01127 
01128         if (node->parent)
01129           {
01130             parent = filename_non_directory (node->parent);
01131             modeline_len += strlen ("Subfile: ") + strlen (node->filename);
01132           }
01133 
01134         if (node->filename)
01135           filename = filename_non_directory (node->filename);
01136 
01137         if (node->flags & N_UpdateTags)
01138           update_message = _("--*** Tags out of Date ***");
01139       }
01140 
01141     if (update_message)
01142       modeline_len += strlen (update_message);
01143     modeline_len += strlen (filename);
01144     modeline_len += strlen (nodename);
01145     modeline_len += 4;          /* strlen (location_indicator). */
01146 
01147     /* 10 for the decimal representation of the number of lines in this
01148        node, and the remainder of the text that can appear in the line. */
01149     modeline_len += 10 + strlen (_("-----Info: (), lines ----, "));
01150     modeline_len += window->width;
01151 
01152     modeline = xmalloc (1 + modeline_len);
01153 
01154     /* Special internal windows have no filename. */
01155     if (!parent && !*filename)
01156       sprintf (modeline, _("-%s---Info: %s, %d lines --%s--"),
01157                (window->flags & W_NoWrap) ? "$" : "-",
01158                nodename, window->line_count, location_indicator);
01159     else
01160       sprintf (modeline, _("-%s%s-Info: (%s)%s, %d lines --%s--"),
01161                (window->flags & W_NoWrap) ? "$" : "-",
01162                (node && (node->flags & N_IsCompressed)) ? "zz" : "--",
01163                parent ? parent : filename,
01164                nodename, window->line_count, location_indicator);
01165 
01166     if (parent)
01167       sprintf (modeline + strlen (modeline), _(" Subfile: %s"), filename);
01168 
01169     if (update_message)
01170       sprintf (modeline + strlen (modeline), "%s", update_message);
01171 
01172     i = strlen (modeline);
01173 
01174     if (i >= window->width)
01175       modeline[window->width] = '\0';
01176     else
01177       {
01178         while (i < window->width)
01179           modeline[i++] = '-';
01180         modeline[i] = '\0';
01181       }
01182 
01183     strcpy (window->modeline, modeline);
01184     free (modeline);
01185   }
01186 }
01187 
01188 /* Make WINDOW start displaying at PERCENT percentage of its node. */
01189 void
01190 window_goto_percentage (WINDOW *window, int percent)
01191 {
01192   int desired_line;
01193 
01194   if (!percent)
01195     desired_line = 0;
01196   else
01197     desired_line =
01198       (int) ((float)window->line_count * ((float)percent / 100.0));
01199 
01200   window->pagetop = desired_line;
01201   window->point =
01202     window->line_starts[window->pagetop] - window->node->contents;
01203   window->flags |= W_UpdateWindow;
01204   window_make_modeline (window);
01205 }
01206 
01207 /* Get the state of WINDOW, and save it in STATE. */
01208 void
01209 window_get_state (WINDOW *window, SEARCH_STATE *state)
01210 {
01211   state->node = window->node;
01212   state->pagetop = window->pagetop;
01213   state->point = window->point;
01214 }
01215 
01216 /* Set the node, pagetop, and point of WINDOW. */
01217 void
01218 window_set_state (WINDOW *window, SEARCH_STATE *state)
01219 {
01220   if (window->node != state->node)
01221     window_set_node_of_window (window, state->node);
01222   window->pagetop = state->pagetop;
01223   window->point = state->point;
01224 }
01225 
01226 
01227 /* Manipulating home-made nodes.  */
01228 
01229 /* A place to buffer echo area messages. */
01230 static NODE *echo_area_node = NULL;
01231 
01232 /* Make the node of the_echo_area be an empty one. */
01233 static void
01234 free_echo_area (void)
01235 {
01236   if (echo_area_node)
01237     {
01238       maybe_free (echo_area_node->contents);
01239       free (echo_area_node);
01240     }
01241 
01242   echo_area_node = NULL;
01243   window_set_node_of_window (the_echo_area, echo_area_node);
01244 }
01245   
01246 /* Clear the echo area, removing any message that is already present.
01247    The echo area is cleared immediately. */
01248 void
01249 window_clear_echo_area (void)
01250 {
01251   free_echo_area ();
01252   display_update_one_window (the_echo_area);
01253 }
01254 
01255 /* Make a message appear in the echo area, built from FORMAT, ARG1 and ARG2.
01256    The arguments are treated similar to printf () arguments, but not all of
01257    printf () hair is present.  The message appears immediately.  If there was
01258    already a message appearing in the echo area, it is removed. */
01259 void
01260 window_message_in_echo_area (char *format, void *arg1, void *arg2)
01261 {
01262   free_echo_area ();
01263   echo_area_node = build_message_node (format, arg1, arg2);
01264   window_set_node_of_window (the_echo_area, echo_area_node);
01265   display_update_one_window (the_echo_area);
01266 }
01267 
01268 /* Place a temporary message in the echo area built from FORMAT, ARG1
01269    and ARG2.  The message appears immediately, but does not destroy
01270    any existing message.  A future call to unmessage_in_echo_area ()
01271    restores the old contents. */
01272 static NODE **old_echo_area_nodes = NULL;
01273 static int old_echo_area_nodes_index = 0;
01274 static int old_echo_area_nodes_slots = 0;
01275 
01276 void
01277 message_in_echo_area (char *format, void *arg1, void *arg2)
01278 {
01279   if (echo_area_node)
01280     {
01281       add_pointer_to_array (echo_area_node, old_echo_area_nodes_index,
01282                             old_echo_area_nodes, old_echo_area_nodes_slots,
01283                             4, NODE *);
01284     }
01285   echo_area_node = NULL;
01286   window_message_in_echo_area (format, arg1, arg2);
01287 }
01288 
01289 void
01290 unmessage_in_echo_area (void)
01291 {
01292   free_echo_area ();
01293 
01294   if (old_echo_area_nodes_index)
01295     echo_area_node = old_echo_area_nodes[--old_echo_area_nodes_index];
01296 
01297   window_set_node_of_window (the_echo_area, echo_area_node);
01298   display_update_one_window (the_echo_area);
01299 }
01300 
01301 /* A place to build a message. */
01302 static char *message_buffer = NULL;
01303 static int message_buffer_index = 0;
01304 static int message_buffer_size = 0;
01305 
01306 /* Ensure that there is enough space to stuff LENGTH characters into
01307    MESSAGE_BUFFER. */
01308 static void
01309 message_buffer_resize (int length)
01310 {
01311   if (!message_buffer)
01312     {
01313       message_buffer_size = length + 1;
01314       message_buffer = xmalloc (message_buffer_size);
01315       message_buffer_index = 0;
01316     }
01317 
01318   while (message_buffer_size <= message_buffer_index + length)
01319     message_buffer = (char *)
01320       xrealloc (message_buffer,
01321                 message_buffer_size += 100 + (2 * length));
01322 }
01323 
01324 /* Format MESSAGE_BUFFER with the results of printing FORMAT with ARG1 and
01325    ARG2. */
01326 static void
01327 build_message_buffer (char *format, void *arg1, void *arg2, void *arg3)
01328 {
01329   register int i, len;
01330   void *args[3];
01331   int arg_index = 0;
01332 
01333   args[0] = arg1;
01334   args[1] = arg2;
01335   args[2] = arg3;
01336 
01337   len = strlen (format);
01338 
01339   message_buffer_resize (len);
01340 
01341   for (i = 0; format[i]; i++)
01342     {
01343       if (format[i] != '%')
01344         {
01345           message_buffer[message_buffer_index++] = format[i];
01346           len--;
01347         }
01348       else
01349         {
01350           char c;
01351           char *fmt_start = format + i;
01352           char *fmt;
01353           int fmt_len, formatted_len;
01354          int paramed = 0;
01355 
01356        format_again:
01357           i++;
01358           while (format[i] && strchr ("-. +0123456789", format[i]))
01359             i++;
01360           c = format[i];
01361 
01362           if (c == '\0')
01363             abort ();
01364 
01365          if (c == '$') {
01366            /* position parameter parameter */
01367            /* better to use bprintf from bfox's metahtml? */
01368            arg_index = atoi(fmt_start + 1) - 1;
01369            if (arg_index < 0)
01370              arg_index = 0;
01371            if (arg_index >= 2)
01372              arg_index = 1;
01373            paramed = 1;
01374            goto format_again;
01375          }
01376 
01377           fmt_len = format + i - fmt_start + 1;
01378           fmt = (char *) xmalloc (fmt_len + 1);
01379           strncpy (fmt, fmt_start, fmt_len);
01380           fmt[fmt_len] = '\0';
01381 
01382          if (paramed) {
01383            /* removed positioned parameter */
01384            char *p;
01385            for (p = fmt + 1; *p && *p != '$'; p++) {
01386              ;
01387            }
01388            strcpy(fmt + 1, p + 1);
01389          }
01390 
01391           /* If we have "%-98s", maybe 98 calls for a longer string.  */
01392           if (fmt_len > 2)
01393             {
01394               int j;
01395 
01396               for (j = fmt_len - 2; j >= 0; j--)
01397                 if (isdigit (fmt[j]) || fmt[j] == '$')
01398                   break;
01399 
01400               formatted_len = atoi (fmt + j);
01401             }
01402           else
01403             formatted_len = c == 's' ? 0 : 1; /* %s can produce empty string */
01404 
01405           switch (c)
01406             {
01407             case '%':           /* Insert a percent sign. */
01408               message_buffer_resize (len + formatted_len);
01409               sprintf
01410                 (message_buffer + message_buffer_index, fmt, "%");
01411               message_buffer_index += formatted_len;
01412               break;
01413 
01414             case 's':           /* Insert the current arg as a string. */
01415               {
01416                 char *string;
01417                 int string_len;
01418 
01419                 string = (char *)args[arg_index++];
01420                 string_len = strlen (string);
01421 
01422                 if (formatted_len > string_len)
01423                   string_len = formatted_len;
01424                 message_buffer_resize (len + string_len);
01425                 sprintf
01426                   (message_buffer + message_buffer_index, fmt, string);
01427                 message_buffer_index += string_len;
01428               }
01429               break;
01430 
01431             case 'd':           /* Insert the current arg as an integer. */
01432               {
01433                 long long_val;
01434                 int integer;
01435 
01436                 long_val = (long)args[arg_index++];
01437                 integer = (int)long_val;
01438 
01439                 message_buffer_resize (len + formatted_len > 32
01440                                        ? formatted_len : 32);
01441                 sprintf
01442                   (message_buffer + message_buffer_index, fmt, integer);
01443                 message_buffer_index = strlen (message_buffer);
01444               }
01445               break;
01446 
01447             case 'c':           /* Insert the current arg as a character. */
01448               {
01449                 long long_val;
01450                 int character;
01451 
01452                 long_val = (long)args[arg_index++];
01453                 character = (int)long_val;
01454 
01455                 message_buffer_resize (len + formatted_len);
01456                 sprintf
01457                   (message_buffer + message_buffer_index, fmt, character);
01458                 message_buffer_index += formatted_len;
01459               }
01460               break;
01461 
01462             default:
01463               abort ();
01464             }
01465           free (fmt);
01466         }
01467     }
01468   message_buffer[message_buffer_index] = '\0';
01469 }
01470 
01471 /* Build a new node which has FORMAT printed with ARG1 and ARG2 as the
01472    contents. */
01473 NODE *
01474 build_message_node (char *format, void *arg1, void *arg2)
01475 {
01476   NODE *node;
01477 
01478   message_buffer_index = 0;
01479   build_message_buffer (format, arg1, arg2, 0);
01480 
01481   node = message_buffer_to_node ();
01482   return (node);
01483 }
01484 
01485 /* Convert the contents of the message buffer to a node. */
01486 NODE *
01487 message_buffer_to_node (void)
01488 {
01489   NODE *node;
01490 
01491   node = xmalloc (sizeof (NODE));
01492   node->filename = NULL;
01493   node->parent = NULL;
01494   node->nodename = NULL;
01495   node->flags = 0;
01496   node->display_pos =0;
01497 
01498   /* Make sure that this buffer ends with a newline. */
01499   node->nodelen = 1 + strlen (message_buffer);
01500   node->contents = xmalloc (1 + node->nodelen);
01501   strcpy (node->contents, message_buffer);
01502   node->contents[node->nodelen - 1] = '\n';
01503   node->contents[node->nodelen] = '\0';
01504   return (node);
01505 }
01506 
01507 /* Useful functions can be called from outside of window.c. */
01508 void
01509 initialize_message_buffer (void)
01510 {
01511   message_buffer_index = 0;
01512 }
01513 
01514 /* Print FORMAT with ARG1,2 to the end of the current message buffer. */
01515 void
01516 printf_to_message_buffer (char *format, void *arg1, void *arg2, void *arg3)
01517 {
01518   build_message_buffer (format, arg1, arg2, arg3);
01519 }
01520 
01521 /* Return the current horizontal position of the "cursor" on the most
01522    recently output message buffer line. */
01523 int
01524 message_buffer_length_this_line (void)
01525 {
01526   register int i;
01527 
01528   if (!message_buffer_index)
01529     return (0);
01530 
01531   for (i = message_buffer_index; i && message_buffer[i - 1] != '\n'; i--);
01532 
01533   return (string_width (message_buffer + i, 0));
01534 }
01535 
01536 /* Pad STRING to COUNT characters by inserting blanks. */
01537 int
01538 pad_to (int count, char *string)
01539 {
01540   register int i;
01541 
01542   i = strlen (string);
01543 
01544   if (i >= count)
01545     string[i++] = ' ';
01546   else
01547     {
01548       while (i < count)
01549         string[i++] = ' ';
01550     }
01551   string[i] = '\0';
01552 
01553   return (i);
01554 }