58ff11de31
patches in a bit. -josh
1483 lines
39 KiB
C
1483 lines
39 KiB
C
/* window.c -- Windows in Info. */
|
||
|
||
/* This file is part of GNU Info, a program for reading online documentation
|
||
stored in Info format.
|
||
|
||
Copyright (C) 1993 Free Software Foundation, Inc.
|
||
|
||
This program is free software; you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation; either version 2, or (at your option)
|
||
any later version.
|
||
|
||
This program is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with this program; if not, write to the Free Software
|
||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||
|
||
Written by Brian Fox (bfox@ai.mit.edu). */
|
||
|
||
#include <stdio.h>
|
||
#include <ctype.h>
|
||
#include <sys/types.h>
|
||
#include <sys/stat.h>
|
||
#include "nodes.h"
|
||
#include "window.h"
|
||
#include "display.h"
|
||
#include "info-utils.h"
|
||
#include "infomap.h"
|
||
|
||
/* The window which describes the screen. */
|
||
WINDOW *the_screen = (WINDOW *)NULL;
|
||
|
||
/* The window which describes the echo area. */
|
||
WINDOW *the_echo_area = (WINDOW *)NULL;
|
||
|
||
/* The list of windows in Info. */
|
||
WINDOW *windows = (WINDOW *)NULL;
|
||
|
||
/* Pointer to the active window in WINDOW_LIST. */
|
||
WINDOW *active_window = (WINDOW *)NULL;
|
||
|
||
/* The size of the echo area in Info. It never changes, irregardless of the
|
||
size of the screen. */
|
||
#define ECHO_AREA_HEIGHT 1
|
||
|
||
/* Macro returns the amount of space that the echo area truly requires relative
|
||
to the entire screen. */
|
||
#define echo_area_required (1 + the_echo_area->height)
|
||
|
||
/* Initalize the window system by creating THE_SCREEN and THE_ECHO_AREA.
|
||
Create the first window ever.
|
||
You pass the dimensions of the total screen size. */
|
||
void
|
||
window_initialize_windows (width, height)
|
||
int width, height;
|
||
{
|
||
the_screen = (WINDOW *)xmalloc (sizeof (WINDOW));
|
||
the_echo_area = (WINDOW *)xmalloc (sizeof (WINDOW));
|
||
windows = (WINDOW *)xmalloc (sizeof (WINDOW));
|
||
active_window = windows;
|
||
|
||
zero_mem (the_screen, sizeof (WINDOW));
|
||
zero_mem (the_echo_area, sizeof (WINDOW));
|
||
zero_mem (active_window, sizeof (WINDOW));
|
||
|
||
/* None of these windows has a goal column yet. */
|
||
the_echo_area->goal_column = -1;
|
||
active_window->goal_column = -1;
|
||
the_screen->goal_column = -1;
|
||
|
||
/* The active and echo_area windows are visible.
|
||
The echo_area is permanent.
|
||
The screen is permanent. */
|
||
active_window->flags = W_WindowVisible;
|
||
the_echo_area->flags = W_WindowIsPerm | W_InhibitMode | W_WindowVisible;
|
||
the_screen->flags = W_WindowIsPerm;
|
||
|
||
/* The height of the echo area never changes. It is statically set right
|
||
here, and it must be at least 1 line for display. The size of the
|
||
initial window cannot be the same size as the screen, since the screen
|
||
includes the echo area. So, we make the height of the initial window
|
||
equal to the screen's displayable region minus the height of the echo
|
||
area. */
|
||
the_echo_area->height = ECHO_AREA_HEIGHT;
|
||
active_window->height = the_screen->height - 1 - the_echo_area->height;
|
||
window_new_screen_size (width, height, (VFunction *)NULL);
|
||
|
||
/* The echo area uses a different keymap than normal info windows. */
|
||
the_echo_area->keymap = echo_area_keymap;
|
||
active_window->keymap = info_keymap;
|
||
}
|
||
|
||
/* Given that the size of the screen has changed to WIDTH and HEIGHT
|
||
from whatever it was before (found in the_screen->height, ->width),
|
||
change the size (and possibly location) of each window in the screen.
|
||
If a window would become too small, call the function DELETER on it,
|
||
after deleting the window from our chain of windows. If DELETER is NULL,
|
||
nothing extra is done. The last window can never be deleted, but it can
|
||
become invisible. */
|
||
|
||
/* If non-null, a function to call with WINDOW as argument when the function
|
||
window_new_screen_size () has deleted WINDOW. */
|
||
VFunction *window_deletion_notifier = (VFunction *)NULL;
|
||
|
||
void
|
||
window_new_screen_size (width, height)
|
||
int width, height;
|
||
{
|
||
register WINDOW *win;
|
||
int delta_height, delta_each, delta_leftover;
|
||
int numwins;
|
||
|
||
/* If no change, do nothing. */
|
||
if (width == the_screen->width && height == the_screen->height)
|
||
return;
|
||
|
||
/* If the new window height is too small, make it be zero. */
|
||
if (height < (WINDOW_MIN_SIZE + the_echo_area->height))
|
||
height = 0;
|
||
if (width < 0)
|
||
width = 0;
|
||
|
||
/* Find out how many windows will change. */
|
||
for (numwins = 0, win = windows; win; win = win->next, numwins++);
|
||
|
||
/* See if some windows will need to be deleted. This is the case if
|
||
the screen is getting smaller, and the available space divided by
|
||
the number of windows is less than WINDOW_MIN_SIZE. In that case,
|
||
delete some windows and try again until there is either enough
|
||
space to divy up among the windows, or until there is only one
|
||
window left. */
|
||
while ((height - echo_area_required) / numwins <= WINDOW_MIN_SIZE)
|
||
{
|
||
/* If only one window, make the size of it be zero, and return
|
||
immediately. */
|
||
if (!windows->next)
|
||
{
|
||
windows->height = 0;
|
||
maybe_free (windows->line_starts);
|
||
windows->line_starts = (char **)NULL;
|
||
windows->line_count = 0;
|
||
break;
|
||
}
|
||
|
||
/* If we have some temporary windows, delete one of them. */
|
||
for (win = windows; win; win = win->next)
|
||
if (win->flags & W_TempWindow)
|
||
break;
|
||
|
||
/* Otherwise, delete the first window, and try again. */
|
||
if (!win)
|
||
win = windows;
|
||
|
||
if (window_deletion_notifier)
|
||
(*window_deletion_notifier) (win);
|
||
|
||
window_delete_window (win);
|
||
numwins--;
|
||
}
|
||
|
||
/* The screen has changed height and width. */
|
||
delta_height = height - the_screen->height; /* This is how much. */
|
||
the_screen->height = height; /* This is the new height. */
|
||
the_screen->width = width; /* This is the new width. */
|
||
|
||
/* Set the start of the echo area. */
|
||
the_echo_area->first_row = height - the_echo_area->height;
|
||
the_echo_area->width = width;
|
||
|
||
/* Check to see if the screen can really be changed this way. */
|
||
if ((!windows->next) && ((windows->height == 0) && (delta_height < 0)))
|
||
return;
|
||
|
||
/* Divide the change in height among the available windows. */
|
||
delta_each = delta_height / numwins;
|
||
delta_leftover = delta_height - (delta_each * numwins);
|
||
|
||
/* Change the height of each window in the chain by delta_each. Change
|
||
the height of the last window in the chain by delta_each and by the
|
||
leftover amount of change. Change the width of each window to be
|
||
WIDTH. */
|
||
for (win = windows; win; win = win->next)
|
||
{
|
||
if ((win->width != width) && ((win->flags & W_InhibitMode) == 0))
|
||
{
|
||
win->width = width;
|
||
maybe_free (win->modeline);
|
||
win->modeline = (char *)xmalloc (1 + width);
|
||
}
|
||
|
||
win->height += delta_each;
|
||
|
||
/* If the previous height of this window was zero, it was the only
|
||
window, and it was not visible. Thus we need to compensate for
|
||
the echo_area. */
|
||
if (win->height == delta_each)
|
||
win->height -= (1 + the_echo_area->height);
|
||
|
||
/* If this is not the first window in the chain, then change the
|
||
first row of it. We cannot just add delta_each to the first row,
|
||
since this window's first row is the sum of the collective increases
|
||
that have gone before it. So we just add one to the location of the
|
||
previous window's modeline. */
|
||
if (win->prev)
|
||
win->first_row = (win->prev->first_row + win->prev->height) + 1;
|
||
|
||
/* The last window in the chain gets the extra space (or shrinkage). */
|
||
if (!win->next)
|
||
win->height += delta_leftover;
|
||
|
||
if (win->node)
|
||
recalculate_line_starts (win);
|
||
|
||
win->flags |= W_UpdateWindow;
|
||
}
|
||
|
||
/* If the screen got smaller, check over the windows just shrunk to
|
||
keep them within bounds. Some of the windows may have gotten smaller
|
||
than WINDOW_MIN_HEIGHT in which case some of the other windows are
|
||
larger than the available display space in the screen. Because of our
|
||
intial test above, we know that there is enough space for all of the
|
||
windows. */
|
||
if ((delta_each < 0) && ((windows->height != 0) && windows->next))
|
||
{
|
||
int avail;
|
||
|
||
avail = the_screen->height - (numwins + the_echo_area->height);
|
||
win = windows;
|
||
|
||
while (win)
|
||
{
|
||
if ((win->height < WINDOW_MIN_HEIGHT) ||
|
||
(win->height > avail))
|
||
{
|
||
WINDOW *lastwin;
|
||
|
||
/* Split the space among the available windows. */
|
||
delta_each = avail / numwins;
|
||
delta_leftover = avail - (delta_each * numwins);
|
||
|
||
for (win = windows; win; win = win->next)
|
||
{
|
||
lastwin = win;
|
||
if (win->prev)
|
||
win->first_row =
|
||
(win->prev->first_row + win->prev->height) + 1;
|
||
win->height = delta_each;
|
||
}
|
||
|
||
/* Give the leftover space (if any) to the last window. */
|
||
lastwin->height += delta_leftover;
|
||
break;
|
||
}
|
||
else
|
||
win= win->next;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Make a new window showing NODE, and return that window structure.
|
||
If NODE is passed as NULL, then show the node showing in the active
|
||
window. If the window could not be made return a NULL pointer. The
|
||
active window is not changed.*/
|
||
WINDOW *
|
||
window_make_window (node)
|
||
NODE *node;
|
||
{
|
||
WINDOW *window;
|
||
|
||
if (!node)
|
||
node = active_window->node;
|
||
|
||
/* If there isn't enough room to make another window, return now. */
|
||
if ((active_window->height / 2) < WINDOW_MIN_SIZE)
|
||
return ((WINDOW *)NULL);
|
||
|
||
/* Make and initialize the new window.
|
||
The fudging about with -1 and +1 is because the following window in the
|
||
chain cannot start at window->height, since that is where the modeline
|
||
for the previous window is displayed. The inverse adjustment is made
|
||
in window_delete_window (). */
|
||
window = (WINDOW *)xmalloc (sizeof (WINDOW));
|
||
window->width = the_screen->width;
|
||
window->height = (active_window->height / 2) - 1;
|
||
#if defined (SPLIT_BEFORE_ACTIVE)
|
||
window->first_row = active_window->first_row;
|
||
#else
|
||
window->first_row = active_window->first_row +
|
||
(active_window->height - window->height);
|
||
#endif
|
||
window->keymap = info_keymap;
|
||
window->goal_column = -1;
|
||
window->modeline = (char *)xmalloc (1 + window->width);
|
||
window->line_starts = (char **)NULL;
|
||
window->flags = W_UpdateWindow | W_WindowVisible;
|
||
window_set_node_of_window (window, node);
|
||
|
||
/* Adjust the height of the old active window. */
|
||
active_window->height -= (window->height + 1);
|
||
#if defined (SPLIT_BEFORE_ACTIVE)
|
||
active_window->first_row += (window->height + 1);
|
||
#endif
|
||
active_window->flags |= W_UpdateWindow;
|
||
|
||
/* Readjust the new and old windows so that their modelines and contents
|
||
will be displayed correctly. */
|
||
#if defined (NOTDEF)
|
||
/* We don't have to do this for WINDOW since window_set_node_of_window ()
|
||
already did. */
|
||
window_adjust_pagetop (window);
|
||
window_make_modeline (window);
|
||
#endif /* NOTDEF */
|
||
|
||
/* We do have to readjust the existing active window. */
|
||
window_adjust_pagetop (active_window);
|
||
window_make_modeline (active_window);
|
||
|
||
#if defined (SPLIT_BEFORE_ACTIVE)
|
||
/* This window is just before the active one. The active window gets
|
||
bumped down one. The active window is not changed. */
|
||
window->next = active_window;
|
||
|
||
window->prev = active_window->prev;
|
||
active_window->prev = window;
|
||
|
||
if (window->prev)
|
||
window->prev->next = window;
|
||
else
|
||
windows = window;
|
||
#else
|
||
/* This window is just after the active one. Which window is active is
|
||
not changed. */
|
||
window->prev = active_window;
|
||
window->next = active_window->next;
|
||
active_window->next = window;
|
||
if (window->next)
|
||
window->next->prev = window;
|
||
#endif /* !SPLIT_BEFORE_ACTIVE */
|
||
return (window);
|
||
}
|
||
|
||
/* These useful macros make it possible to read the code in
|
||
window_change_window_height (). */
|
||
#define grow_me_shrinking_next(me, next, diff) \
|
||
do { \
|
||
me->height += diff; \
|
||
next->height -= diff; \
|
||
next->first_row += diff; \
|
||
window_adjust_pagetop (next); \
|
||
} while (0)
|
||
|
||
#define grow_me_shrinking_prev(me, prev, diff) \
|
||
do { \
|
||
me->height += diff; \
|
||
prev->height -= diff; \
|
||
me->first_row -=diff; \
|
||
window_adjust_pagetop (prev); \
|
||
} while (0)
|
||
|
||
#define shrink_me_growing_next(me, next, diff) \
|
||
do { \
|
||
me->height -= diff; \
|
||
next->height += diff; \
|
||
next->first_row -= diff; \
|
||
window_adjust_pagetop (next); \
|
||
} while (0)
|
||
|
||
#define shrink_me_growing_prev(me, prev, diff) \
|
||
do { \
|
||
me->height -= diff; \
|
||
prev->height += diff; \
|
||
me->first_row += diff; \
|
||
window_adjust_pagetop (prev); \
|
||
} while (0)
|
||
|
||
/* Change the height of WINDOW by AMOUNT. This also automagically adjusts
|
||
the previous and next windows in the chain. If there is only one user
|
||
window, then no change takes place. */
|
||
void
|
||
window_change_window_height (window, amount)
|
||
WINDOW *window;
|
||
int amount;
|
||
{
|
||
register WINDOW *win, *prev, *next;
|
||
|
||
/* If there is only one window, or if the amount of change is zero,
|
||
return immediately. */
|
||
if (!windows->next || amount == 0)
|
||
return;
|
||
|
||
/* Find this window in our chain. */
|
||
for (win = windows; win; win = win->next)
|
||
if (win == window)
|
||
break;
|
||
|
||
/* If the window is isolated (i.e., doesn't appear in our window list,
|
||
then quit now. */
|
||
if (!win)
|
||
return;
|
||
|
||
/* Change the height of this window by AMOUNT, if that is possible.
|
||
It can be impossible if there isn't enough available room on the
|
||
screen, or if the resultant window would be too small. */
|
||
|
||
prev = window->prev;
|
||
next = window->next;
|
||
|
||
/* WINDOW decreasing in size? */
|
||
if (amount < 0)
|
||
{
|
||
int abs_amount = -amount; /* It is easier to deal with this way. */
|
||
|
||
/* If the resultant window would be too small, stop here. */
|
||
if ((window->height - abs_amount) < WINDOW_MIN_HEIGHT)
|
||
return;
|
||
|
||
/* If we have two neighboring windows, choose the smaller one to get
|
||
larger. */
|
||
if (next && prev)
|
||
{
|
||
if (prev->height < next->height)
|
||
shrink_me_growing_prev (window, prev, abs_amount);
|
||
else
|
||
shrink_me_growing_next (window, next, abs_amount);
|
||
}
|
||
else if (next)
|
||
shrink_me_growing_next (window, next, abs_amount);
|
||
else
|
||
shrink_me_growing_prev (window, prev, abs_amount);
|
||
}
|
||
|
||
/* WINDOW increasing in size? */
|
||
if (amount > 0)
|
||
{
|
||
int total_avail, next_avail = 0, prev_avail = 0;
|
||
|
||
if (next)
|
||
next_avail = next->height - WINDOW_MIN_SIZE;
|
||
|
||
if (prev)
|
||
prev_avail = prev->height - WINDOW_MIN_SIZE;
|
||
|
||
total_avail = next_avail + prev_avail;
|
||
|
||
/* If there isn't enough space available to grow this window, give up. */
|
||
if (amount > total_avail)
|
||
return;
|
||
|
||
/* If there aren't two neighboring windows, or if one of the neighbors
|
||
is larger than the other one by at least AMOUNT, grow that one. */
|
||
if ((next && !prev) || ((next_avail - amount) >= prev_avail))
|
||
grow_me_shrinking_next (window, next, amount);
|
||
else if ((prev && !next) || ((prev_avail - amount) >= next_avail))
|
||
grow_me_shrinking_prev (window, prev, amount);
|
||
else
|
||
{
|
||
int change;
|
||
|
||
/* This window has two neighbors. They both must be shrunk in to
|
||
make enough space for WINDOW to grow. Make them both the same
|
||
size. */
|
||
if (prev_avail > next_avail)
|
||
{
|
||
change = prev_avail - next_avail;
|
||
grow_me_shrinking_prev (window, prev, change);
|
||
amount -= change;
|
||
}
|
||
else
|
||
{
|
||
change = next_avail - prev_avail;
|
||
grow_me_shrinking_next (window, next, change);
|
||
amount -= change;
|
||
}
|
||
|
||
/* Both neighbors are the same size. Split the difference in
|
||
AMOUNT between them. */
|
||
while (amount)
|
||
{
|
||
window->height++;
|
||
amount--;
|
||
|
||
/* Odd numbers grow next, even grow prev. */
|
||
if (amount & 1)
|
||
{
|
||
prev->height--;
|
||
window->first_row--;
|
||
}
|
||
else
|
||
{
|
||
next->height--;
|
||
next->first_row++;
|
||
}
|
||
}
|
||
window_adjust_pagetop (prev);
|
||
window_adjust_pagetop (next);
|
||
}
|
||
}
|
||
if (prev)
|
||
prev->flags |= W_UpdateWindow;
|
||
|
||
if (next)
|
||
next->flags |= W_UpdateWindow;
|
||
|
||
window->flags |= W_UpdateWindow;
|
||
window_adjust_pagetop (window);
|
||
}
|
||
|
||
/* Tile all of the windows currently displayed in the global variable
|
||
WINDOWS. If argument STYLE is TILE_INTERNALS, tile windows displaying
|
||
internal nodes as well, otherwise do not change the height of such
|
||
windows. */
|
||
void
|
||
window_tile_windows (style)
|
||
int style;
|
||
{
|
||
WINDOW *win, *last_adjusted;
|
||
int numwins, avail, per_win_height, leftover;
|
||
int do_internals;
|
||
|
||
numwins = avail = 0;
|
||
do_internals = (style == TILE_INTERNALS);
|
||
|
||
for (win = windows; win; win = win->next)
|
||
if (do_internals || !win->node ||
|
||
(win->node->flags & N_IsInternal) == 0)
|
||
{
|
||
avail += win->height;
|
||
numwins++;
|
||
}
|
||
|
||
if (numwins <= 1 || !the_screen->height)
|
||
return;
|
||
|
||
/* Find the size for each window. Divide the size of the usable portion
|
||
of the screen by the number of windows. */
|
||
per_win_height = avail / numwins;
|
||
leftover = avail - (per_win_height * numwins);
|
||
|
||
last_adjusted = (WINDOW *)NULL;
|
||
for (win = windows; win; win = win->next)
|
||
{
|
||
if (do_internals || !win->node ||
|
||
(win->node->flags & N_IsInternal) == 0)
|
||
{
|
||
last_adjusted = win;
|
||
win->height = per_win_height;
|
||
}
|
||
}
|
||
|
||
if (last_adjusted)
|
||
last_adjusted->height += leftover;
|
||
|
||
/* Readjust the first_row of every window in the chain. */
|
||
for (win = windows; win; win = win->next)
|
||
{
|
||
if (win->prev)
|
||
win->first_row = win->prev->first_row + win->prev->height + 1;
|
||
|
||
window_adjust_pagetop (win);
|
||
win->flags |= W_UpdateWindow;
|
||
}
|
||
}
|
||
|
||
/* Toggle the state of line wrapping in WINDOW. This can do a bit of fancy
|
||
redisplay. */
|
||
void
|
||
window_toggle_wrap (window)
|
||
WINDOW *window;
|
||
{
|
||
if (window->flags & W_NoWrap)
|
||
window->flags &= ~W_NoWrap;
|
||
else
|
||
window->flags |= W_NoWrap;
|
||
|
||
if (window != the_echo_area)
|
||
{
|
||
char **old_starts;
|
||
int old_lines, old_pagetop;
|
||
|
||
old_starts = window->line_starts;
|
||
old_lines = window->line_count;
|
||
old_pagetop = window->pagetop;
|
||
|
||
calculate_line_starts (window);
|
||
|
||
/* Make sure that point appears within this window. */
|
||
window_adjust_pagetop (window);
|
||
|
||
/* If the pagetop hasn't changed maybe we can do some scrolling now
|
||
to speed up the display. Many of the line starts will be the same,
|
||
so scrolling here is a very good optimization.*/
|
||
if (old_pagetop == window->pagetop)
|
||
display_scroll_line_starts
|
||
(window, old_pagetop, old_starts, old_lines);
|
||
maybe_free (old_starts);
|
||
}
|
||
window->flags |= W_UpdateWindow;
|
||
}
|
||
|
||
/* Set WINDOW to display NODE. */
|
||
void
|
||
window_set_node_of_window (window, node)
|
||
WINDOW *window;
|
||
NODE *node;
|
||
{
|
||
window->node = node;
|
||
window->pagetop = 0;
|
||
window->point = 0;
|
||
recalculate_line_starts (window);
|
||
window->flags |= W_UpdateWindow;
|
||
window_adjust_pagetop (window);
|
||
window_make_modeline (window);
|
||
}
|
||
|
||
/* Delete WINDOW from the list of known windows. If this window was the
|
||
active window, make the next window in the chain be the active window.
|
||
If the active window is the next or previous window, choose that window
|
||
as the recipient of the extra space. Otherwise, prefer the next window. */
|
||
void
|
||
window_delete_window (window)
|
||
WINDOW *window;
|
||
{
|
||
WINDOW *next, *prev, *window_to_fix;
|
||
|
||
next = window->next;
|
||
prev = window->prev;
|
||
|
||
/* You cannot delete the only window or a permanent window. */
|
||
if ((!next && !prev) || (window->flags & W_WindowIsPerm))
|
||
return;
|
||
|
||
if (next)
|
||
next->prev = prev;
|
||
|
||
if (!prev)
|
||
windows = next;
|
||
else
|
||
prev->next = next;
|
||
|
||
if (window->line_starts)
|
||
free (window->line_starts);
|
||
|
||
if (window->modeline)
|
||
free (window->modeline);
|
||
|
||
if (window == active_window)
|
||
{
|
||
/* If there isn't a next window, then there must be a previous one,
|
||
since we cannot delete the last window. If there is a next window,
|
||
prefer to use that as the active window. */
|
||
if (next)
|
||
active_window = next;
|
||
else
|
||
active_window = prev;
|
||
}
|
||
|
||
if (next && active_window == next)
|
||
window_to_fix = next;
|
||
else if (prev && active_window == prev)
|
||
window_to_fix = prev;
|
||
else if (next)
|
||
window_to_fix = next;
|
||
else if (prev)
|
||
window_to_fix = prev;
|
||
else
|
||
window_to_fix = windows;
|
||
|
||
if (window_to_fix->first_row > window->first_row)
|
||
{
|
||
int diff;
|
||
|
||
/* Try to adjust the visible part of the node so that as little
|
||
text as possible has to move. */
|
||
diff = window_to_fix->first_row - window->first_row;
|
||
window_to_fix->first_row = window->first_row;
|
||
|
||
window_to_fix->pagetop -= diff;
|
||
if (window_to_fix->pagetop < 0)
|
||
window_to_fix->pagetop = 0;
|
||
}
|
||
|
||
/* The `+ 1' is to offset the difference between the first_row locations.
|
||
See the code in window_make_window (). */
|
||
window_to_fix->height += window->height + 1;
|
||
window_to_fix->flags |= W_UpdateWindow;
|
||
|
||
free (window);
|
||
}
|
||
|
||
/* For every window in CHAIN, set the flags member to have FLAG set. */
|
||
void
|
||
window_mark_chain (chain, flag)
|
||
WINDOW *chain;
|
||
int flag;
|
||
{
|
||
register WINDOW *win;
|
||
|
||
for (win = chain; win; win = win->next)
|
||
win->flags |= flag;
|
||
}
|
||
|
||
/* For every window in CHAIN, clear the flags member of FLAG. */
|
||
void
|
||
window_unmark_chain (chain, flag)
|
||
WINDOW *chain;
|
||
int flag;
|
||
{
|
||
register WINDOW *win;
|
||
|
||
for (win = chain; win; win = win->next)
|
||
win->flags &= ~flag;
|
||
}
|
||
|
||
/* Return the number of characters it takes to display CHARACTER on the
|
||
screen at HPOS. */
|
||
int
|
||
character_width (character, hpos)
|
||
int character, hpos;
|
||
{
|
||
int printable_limit = 127;
|
||
int width = 1;
|
||
|
||
if (ISO_Latin_p)
|
||
printable_limit = 160;
|
||
|
||
if (character > printable_limit)
|
||
width = 3;
|
||
else if (iscntrl (character))
|
||
{
|
||
switch (character)
|
||
{
|
||
case '\r':
|
||
case '\n':
|
||
width = the_screen->width - hpos;
|
||
break;
|
||
case '\t':
|
||
width = ((hpos + 8) & 0xf8) - hpos;
|
||
break;
|
||
default:
|
||
width = 2;
|
||
}
|
||
}
|
||
else if (character == DEL)
|
||
width = 2;
|
||
|
||
return (width);
|
||
}
|
||
|
||
/* Return the number of characters it takes to display STRING on the screen
|
||
at HPOS. */
|
||
int
|
||
string_width (string, hpos)
|
||
char *string;
|
||
int hpos;
|
||
{
|
||
register int i, width, this_char_width;
|
||
|
||
for (width = 0, i = 0; string[i]; i++)
|
||
{
|
||
this_char_width = character_width (string[i], hpos);
|
||
width += this_char_width;
|
||
hpos += this_char_width;
|
||
}
|
||
return (width);
|
||
}
|
||
|
||
/* Quickly guess the approximate number of lines to that NODE would
|
||
take to display. This really only counts carriage returns. */
|
||
int
|
||
window_physical_lines (node)
|
||
NODE *node;
|
||
{
|
||
register int i, lines;
|
||
char *contents;
|
||
|
||
if (!node)
|
||
return (0);
|
||
|
||
contents = node->contents;
|
||
for (i = 0, lines = 1; i < node->nodelen; i++)
|
||
if (contents[i] == '\n')
|
||
lines++;
|
||
|
||
return (lines);
|
||
}
|
||
|
||
/* Calculate a list of line starts for the node belonging to WINDOW. The line
|
||
starts are pointers to the actual text within WINDOW->NODE. */
|
||
void
|
||
calculate_line_starts (window)
|
||
WINDOW *window;
|
||
{
|
||
register int i, hpos;
|
||
char **line_starts = (char **)NULL;
|
||
int line_starts_index = 0, line_starts_slots = 0;
|
||
int bump_index;
|
||
NODE *node;
|
||
|
||
window->line_starts = (char **)NULL;
|
||
window->line_count = 0;
|
||
node = window->node;
|
||
|
||
if (!node)
|
||
return;
|
||
|
||
/* Grovel the node starting at the top, and for each line calculate the
|
||
width of the characters appearing in that line. Add each line start
|
||
to our array. */
|
||
i = 0;
|
||
hpos = 0;
|
||
bump_index = 0;
|
||
|
||
while (i < node->nodelen)
|
||
{
|
||
char *line = node->contents + i;
|
||
unsigned int cwidth, c;
|
||
|
||
add_pointer_to_array (line, line_starts_index, line_starts,
|
||
line_starts_slots, 100, char *);
|
||
if (bump_index)
|
||
{
|
||
i++;
|
||
bump_index = 0;
|
||
}
|
||
|
||
while (1)
|
||
{
|
||
c = node->contents[i];
|
||
cwidth = character_width (c, hpos);
|
||
|
||
/* If this character fits within this line, just do the next one. */
|
||
if ((hpos + cwidth) < window->width)
|
||
{
|
||
i++;
|
||
hpos += cwidth;
|
||
continue;
|
||
}
|
||
else
|
||
{
|
||
/* If this character would position the cursor at the start of
|
||
the next printed screen line, then do the next line. */
|
||
if (c == '\n' || c == '\r' || c == '\t')
|
||
{
|
||
i++;
|
||
hpos = 0;
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
/* This character passes the window width border. Postion
|
||
the cursor after the printed character, but remember this
|
||
line start as where this character is. A bit tricky. */
|
||
|
||
/* If this window doesn't wrap lines, proceed to the next
|
||
physical line here. */
|
||
if (window->flags & W_NoWrap)
|
||
{
|
||
hpos = 0;
|
||
while (i < node->nodelen && node->contents[i] != '\n')
|
||
i++;
|
||
|
||
if (node->contents[i] == '\n')
|
||
i++;
|
||
}
|
||
else
|
||
{
|
||
hpos = the_screen->width - hpos;
|
||
bump_index++;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
window->line_starts = line_starts;
|
||
window->line_count = line_starts_index;
|
||
}
|
||
|
||
/* Given WINDOW, recalculate the line starts for the node it displays. */
|
||
void
|
||
recalculate_line_starts (window)
|
||
WINDOW *window;
|
||
{
|
||
maybe_free (window->line_starts);
|
||
calculate_line_starts (window);
|
||
}
|
||
|
||
/* Global variable control redisplay of scrolled windows. If non-zero, it
|
||
is the desired number of lines to scroll the window in order to make
|
||
point visible. A user might set this to 1 for smooth scrolling. If
|
||
set to zero, the line containing point is centered within the window. */
|
||
int window_scroll_step = 0;
|
||
|
||
/* Adjust the pagetop of WINDOW such that the cursor point will be visible. */
|
||
void
|
||
window_adjust_pagetop (window)
|
||
WINDOW *window;
|
||
{
|
||
register int line = 0;
|
||
char *contents;
|
||
|
||
if (!window->node)
|
||
return;
|
||
|
||
contents = window->node->contents;
|
||
|
||
/* Find the first printed line start which is after WINDOW->point. */
|
||
for (line = 0; line < window->line_count; line++)
|
||
{
|
||
char *line_start;
|
||
|
||
line_start = window->line_starts[line];
|
||
|
||
if ((line_start - contents) > window->point)
|
||
break;
|
||
}
|
||
|
||
/* The line index preceding the line start which is past point is the
|
||
one containing point. */
|
||
line--;
|
||
|
||
/* If this line appears in the current displayable page, do nothing.
|
||
Otherwise, adjust the top of the page to make this line visible. */
|
||
if ((line < window->pagetop) ||
|
||
(line - window->pagetop > (window->height - 1)))
|
||
{
|
||
/* The user-settable variable "scroll-step" is used to attempt
|
||
to make point visible, iff it is non-zero. If that variable
|
||
is zero, then the line containing point is centered within
|
||
the window. */
|
||
if (window_scroll_step < window->height)
|
||
{
|
||
if ((line < window->pagetop) &&
|
||
((window->pagetop - window_scroll_step) <= line))
|
||
window->pagetop -= window_scroll_step;
|
||
else if ((line - window->pagetop > (window->height - 1)) &&
|
||
((line - (window->pagetop + window_scroll_step)
|
||
< window->height)))
|
||
window->pagetop += window_scroll_step;
|
||
else
|
||
window->pagetop = line - ((window->height - 1) / 2);
|
||
}
|
||
else
|
||
window->pagetop = line - ((window->height - 1) / 2);
|
||
|
||
if (window->pagetop < 0)
|
||
window->pagetop = 0;
|
||
window->flags |= W_UpdateWindow;
|
||
}
|
||
}
|
||
|
||
/* Return the index of the line containing point. */
|
||
int
|
||
window_line_of_point (window)
|
||
WINDOW *window;
|
||
{
|
||
register int i, start = 0;
|
||
|
||
/* Try to optimize. Check to see if point is past the pagetop for
|
||
this window, and if so, start searching forward from there. */
|
||
if ((window->pagetop > -1 && window->pagetop < window->line_count) &&
|
||
(window->line_starts[window->pagetop] - window->node->contents)
|
||
<= window->point)
|
||
start = window->pagetop;
|
||
|
||
for (i = start; i < window->line_count; i++)
|
||
{
|
||
if ((window->line_starts[i] - window->node->contents) > window->point)
|
||
break;
|
||
}
|
||
|
||
return (i - 1);
|
||
}
|
||
|
||
/* Get and return the goal column for this window. */
|
||
int
|
||
window_get_goal_column (window)
|
||
WINDOW *window;
|
||
{
|
||
if (!window->node)
|
||
return (-1);
|
||
|
||
if (window->goal_column != -1)
|
||
return (window->goal_column);
|
||
|
||
/* Okay, do the work. Find the printed offset of the cursor
|
||
in this window. */
|
||
return (window_get_cursor_column (window));
|
||
}
|
||
|
||
/* Get and return the printed column offset of the cursor in this window. */
|
||
int
|
||
window_get_cursor_column (window)
|
||
WINDOW *window;
|
||
{
|
||
int i, hpos, end;
|
||
char *line;
|
||
|
||
i = window_line_of_point (window);
|
||
|
||
if (i < 0)
|
||
return (-1);
|
||
|
||
line = window->line_starts[i];
|
||
end = window->point - (line - window->node->contents);
|
||
|
||
for (hpos = 0, i = 0; i < end; i++)
|
||
hpos += character_width (line[i], hpos);
|
||
|
||
return (hpos);
|
||
}
|
||
|
||
/* Count the number of characters in LINE that precede the printed column
|
||
offset of GOAL. */
|
||
int
|
||
window_chars_to_goal (line, goal)
|
||
char *line;
|
||
int goal;
|
||
{
|
||
register int i, check, hpos;
|
||
|
||
for (hpos = 0, i = 0; line[i] != '\n'; i++)
|
||
{
|
||
|
||
check = hpos + character_width (line[i], hpos);
|
||
|
||
if (check > goal)
|
||
break;
|
||
|
||
hpos = check;
|
||
}
|
||
return (i);
|
||
}
|
||
|
||
/* Create a modeline for WINDOW, and store it in window->modeline. */
|
||
void
|
||
window_make_modeline (window)
|
||
WINDOW *window;
|
||
{
|
||
register int i;
|
||
char *modeline;
|
||
char location_indicator[4];
|
||
int lines_remaining;
|
||
|
||
/* Only make modelines for those windows which have one. */
|
||
if (window->flags & W_InhibitMode)
|
||
return;
|
||
|
||
/* Find the number of lines actually displayed in this window. */
|
||
lines_remaining = window->line_count - window->pagetop;
|
||
|
||
if (window->pagetop == 0)
|
||
{
|
||
if (lines_remaining <= window->height)
|
||
strcpy (location_indicator, "All");
|
||
else
|
||
strcpy (location_indicator, "Top");
|
||
}
|
||
else
|
||
{
|
||
if (lines_remaining <= window->height)
|
||
strcpy (location_indicator, "Bot");
|
||
else
|
||
{
|
||
float pt, lc;
|
||
int percentage;
|
||
|
||
pt = (float)window->pagetop;
|
||
lc = (float)window->line_count;
|
||
|
||
percentage = 100 * (pt / lc);
|
||
|
||
sprintf (location_indicator, "%2d%%", percentage);
|
||
}
|
||
}
|
||
|
||
/* Calculate the maximum size of the information to stick in MODELINE. */
|
||
{
|
||
int modeline_len = 0;
|
||
char *parent = (char *)NULL, *filename = "*no file*";
|
||
char *nodename = "*no node*";
|
||
char *update_message = (char *)NULL;
|
||
NODE *node = window->node;
|
||
|
||
if (node)
|
||
{
|
||
if (node->nodename)
|
||
nodename = node->nodename;
|
||
|
||
if (node->parent)
|
||
{
|
||
parent = filename_non_directory (node->parent);
|
||
modeline_len += strlen ("Subfile: ") + strlen (node->filename);
|
||
}
|
||
|
||
if (node->filename)
|
||
filename = filename_non_directory (node->filename);
|
||
|
||
if (node->flags & N_UpdateTags)
|
||
update_message = "--*** Tags out of Date ***";
|
||
}
|
||
|
||
if (update_message)
|
||
modeline_len += strlen (update_message);
|
||
modeline_len += strlen (filename);
|
||
modeline_len += strlen (nodename);
|
||
modeline_len += 4; /* strlen (location_indicator). */
|
||
|
||
/* 10 for the decimal representation of the number of lines in this
|
||
node, and the remainder of the text that can appear in the line. */
|
||
modeline_len += 10 + strlen ("-----Info: (), lines ----, ");
|
||
modeline_len += window->width;
|
||
|
||
modeline = (char *)xmalloc (1 + modeline_len);
|
||
|
||
/* Special internal windows have no filename. */
|
||
if (!parent && !*filename)
|
||
sprintf (modeline, "-%s---Info: %s, %d lines --%s--",
|
||
(window->flags & W_NoWrap) ? "$" : "-",
|
||
nodename, window->line_count, location_indicator);
|
||
else
|
||
sprintf (modeline, "-%s%s-Info: (%s)%s, %d lines --%s--",
|
||
(window->flags & W_NoWrap) ? "$" : "-",
|
||
(node && (node->flags & N_IsCompressed)) ? "zz" : "--",
|
||
parent ? parent : filename,
|
||
nodename, window->line_count, location_indicator);
|
||
|
||
if (parent)
|
||
sprintf (modeline + strlen (modeline), " Subfile: %s", filename);
|
||
|
||
if (update_message)
|
||
sprintf (modeline + strlen (modeline), "%s", update_message);
|
||
|
||
i = strlen (modeline);
|
||
|
||
if (i >= window->width)
|
||
modeline[window->width] = '\0';
|
||
else
|
||
{
|
||
while (i < window->width)
|
||
modeline[i++] = '-';
|
||
modeline[i] = '\0';
|
||
}
|
||
|
||
strcpy (window->modeline, modeline);
|
||
free (modeline);
|
||
}
|
||
}
|
||
|
||
/* Make WINDOW start displaying at PERCENT percentage of its node. */
|
||
void
|
||
window_goto_percentage (window, percent)
|
||
WINDOW *window;
|
||
int percent;
|
||
{
|
||
int desired_line;
|
||
|
||
if (!percent)
|
||
desired_line = 0;
|
||
else
|
||
desired_line =
|
||
(int) ((float)window->line_count * ((float)percent / 100.0));
|
||
|
||
window->pagetop = desired_line;
|
||
window->point =
|
||
window->line_starts[window->pagetop] - window->node->contents;
|
||
window->flags |= W_UpdateWindow;
|
||
window_make_modeline (window);
|
||
}
|
||
|
||
/* Get the state of WINDOW, and save it in STATE. */
|
||
void
|
||
window_get_state (window, state)
|
||
WINDOW *window;
|
||
WINDOW_STATE *state;
|
||
{
|
||
state->node = window->node;
|
||
state->pagetop = window->pagetop;
|
||
state->point = window->point;
|
||
}
|
||
|
||
/* Set the node, pagetop, and point of WINDOW. */
|
||
void
|
||
window_set_state (window, state)
|
||
WINDOW *window;
|
||
WINDOW_STATE *state;
|
||
{
|
||
if (window->node != state->node)
|
||
window_set_node_of_window (window, state->node);
|
||
window->pagetop = state->pagetop;
|
||
window->point = state->point;
|
||
}
|
||
|
||
|
||
/* **************************************************************** */
|
||
/* */
|
||
/* Manipulating Home-Made Nodes */
|
||
/* */
|
||
/* **************************************************************** */
|
||
|
||
/* A place to buffer echo area messages. */
|
||
static NODE *echo_area_node = (NODE *)NULL;
|
||
|
||
/* Make the node of the_echo_area be an empty one. */
|
||
static void
|
||
free_echo_area ()
|
||
{
|
||
if (echo_area_node)
|
||
{
|
||
maybe_free (echo_area_node->contents);
|
||
free (echo_area_node);
|
||
}
|
||
|
||
echo_area_node = (NODE *)NULL;
|
||
window_set_node_of_window (the_echo_area, echo_area_node);
|
||
}
|
||
|
||
/* Clear the echo area, removing any message that is already present.
|
||
The echo area is cleared immediately. */
|
||
void
|
||
window_clear_echo_area ()
|
||
{
|
||
free_echo_area ();
|
||
display_update_one_window (the_echo_area);
|
||
}
|
||
|
||
/* Make a message appear in the echo area, built from FORMAT, ARG1 and ARG2.
|
||
The arguments are treated similar to printf () arguments, but not all of
|
||
printf () hair is present. The message appears immediately. If there was
|
||
already a message appearing in the echo area, it is removed. */
|
||
void
|
||
window_message_in_echo_area (format, arg1, arg2)
|
||
char *format;
|
||
void *arg1, *arg2;
|
||
{
|
||
free_echo_area ();
|
||
echo_area_node = build_message_node (format, arg1, arg2);
|
||
window_set_node_of_window (the_echo_area, echo_area_node);
|
||
display_update_one_window (the_echo_area);
|
||
}
|
||
|
||
/* Place a temporary message in the echo area built from FORMAT, ARG1
|
||
and ARG2. The message appears immediately, but does not destroy
|
||
any existing message. A future call to unmessage_in_echo_area ()
|
||
restores the old contents. */
|
||
static NODE **old_echo_area_nodes = (NODE **)NULL;
|
||
static int old_echo_area_nodes_index = 0;
|
||
static int old_echo_area_nodes_slots = 0;
|
||
|
||
void
|
||
message_in_echo_area (format, arg1, arg2)
|
||
char *format;
|
||
void *arg1, *arg2;
|
||
{
|
||
if (echo_area_node)
|
||
{
|
||
add_pointer_to_array (echo_area_node, old_echo_area_nodes_index,
|
||
old_echo_area_nodes, old_echo_area_nodes_slots,
|
||
4, NODE *);
|
||
}
|
||
echo_area_node = (NODE *)NULL;
|
||
window_message_in_echo_area (format, arg1, arg2);
|
||
}
|
||
|
||
void
|
||
unmessage_in_echo_area ()
|
||
{
|
||
free_echo_area ();
|
||
|
||
if (old_echo_area_nodes_index)
|
||
echo_area_node = old_echo_area_nodes[--old_echo_area_nodes_index];
|
||
|
||
window_set_node_of_window (the_echo_area, echo_area_node);
|
||
display_update_one_window (the_echo_area);
|
||
}
|
||
|
||
/* A place to build a message. */
|
||
static char *message_buffer = (char *)NULL;
|
||
static int message_buffer_index = 0;
|
||
static int message_buffer_size = 0;
|
||
|
||
/* Ensure that there is enough space to stuff LENGTH characters into
|
||
MESSAGE_BUFFER. */
|
||
static void
|
||
message_buffer_resize (length)
|
||
int length;
|
||
{
|
||
if (!message_buffer)
|
||
{
|
||
message_buffer_size = length + 1;
|
||
message_buffer = (char *)xmalloc (message_buffer_size);
|
||
message_buffer_index = 0;
|
||
}
|
||
|
||
while (message_buffer_size <= message_buffer_index + length)
|
||
message_buffer = (char *)
|
||
xrealloc (message_buffer,
|
||
message_buffer_size += 100 + (2 * length));
|
||
}
|
||
|
||
/* Format MESSAGE_BUFFER with the results of printing FORMAT with ARG1 and
|
||
ARG2. */
|
||
static void
|
||
build_message_buffer (format, arg1, arg2)
|
||
char *format;
|
||
void *arg1, *arg2;
|
||
{
|
||
register int i, len;
|
||
void *args[2];
|
||
int arg_index = 0;
|
||
|
||
args[0] = arg1;
|
||
args[1] = arg2;
|
||
|
||
len = strlen (format);
|
||
|
||
message_buffer_resize (len);
|
||
|
||
for (i = 0; format[i]; i++)
|
||
{
|
||
if (format[i] != '%')
|
||
{
|
||
message_buffer[message_buffer_index++] = format[i];
|
||
len--;
|
||
}
|
||
else
|
||
{
|
||
char c;
|
||
|
||
c = format[++i];
|
||
|
||
switch (c)
|
||
{
|
||
case '%': /* Insert a percent sign. */
|
||
message_buffer_resize (len + 1);
|
||
message_buffer[message_buffer_index++] = '%';
|
||
break;
|
||
|
||
case 's': /* Insert the current arg as a string. */
|
||
{
|
||
char *string;
|
||
int string_len;
|
||
|
||
string = (char *)args[arg_index++];
|
||
string_len = strlen (string);
|
||
|
||
message_buffer_resize (len + string_len);
|
||
sprintf
|
||
(message_buffer + message_buffer_index, "%s", string);
|
||
message_buffer_index += string_len;
|
||
}
|
||
break;
|
||
|
||
case 'd': /* Insert the current arg as an integer. */
|
||
{
|
||
long long_val;
|
||
int integer;
|
||
|
||
long_val = (long)args[arg_index++];
|
||
integer = (int)long_val;
|
||
|
||
message_buffer_resize (len + 32);
|
||
sprintf
|
||
(message_buffer + message_buffer_index, "%d", integer);
|
||
message_buffer_index = strlen (message_buffer);
|
||
}
|
||
break;
|
||
|
||
case 'c': /* Insert the current arg as a character. */
|
||
{
|
||
long long_val;
|
||
int character;
|
||
|
||
long_val = (long)args[arg_index++];
|
||
character = (int)long_val;
|
||
|
||
message_buffer_resize (len + 1);
|
||
message_buffer[message_buffer_index++] = character;
|
||
}
|
||
break;
|
||
|
||
default:
|
||
abort ();
|
||
}
|
||
}
|
||
}
|
||
message_buffer[message_buffer_index] = '\0';
|
||
}
|
||
|
||
/* Build a new node which has FORMAT printed with ARG1 and ARG2 as the
|
||
contents. */
|
||
NODE *
|
||
build_message_node (format, arg1, arg2)
|
||
char *format;
|
||
void *arg1, *arg2;
|
||
{
|
||
NODE *node;
|
||
|
||
message_buffer_index = 0;
|
||
build_message_buffer (format, arg1, arg2);
|
||
|
||
node = message_buffer_to_node ();
|
||
return (node);
|
||
}
|
||
|
||
/* Convert the contents of the message buffer to a node. */
|
||
NODE *
|
||
message_buffer_to_node ()
|
||
{
|
||
NODE *node;
|
||
|
||
node = (NODE *)xmalloc (sizeof (NODE));
|
||
node->filename = (char *)NULL;
|
||
node->parent = (char *)NULL;
|
||
node->nodename = (char *)NULL;
|
||
node->flags = 0;
|
||
|
||
/* Make sure that this buffer ends with a newline. */
|
||
node->nodelen = 1 + strlen (message_buffer);
|
||
node->contents = (char *)xmalloc (1 + node->nodelen);
|
||
strcpy (node->contents, message_buffer);
|
||
node->contents[node->nodelen - 1] = '\n';
|
||
node->contents[node->nodelen] = '\0';
|
||
return (node);
|
||
}
|
||
|
||
/* Useful functions can be called from outside of window.c. */
|
||
void
|
||
initialize_message_buffer ()
|
||
{
|
||
message_buffer_index = 0;
|
||
}
|
||
|
||
/* Print FORMAT with ARG1,2 to the end of the current message buffer. */
|
||
void
|
||
printf_to_message_buffer (format, arg1, arg2)
|
||
char *format;
|
||
void *arg1, *arg2;
|
||
{
|
||
build_message_buffer (format, arg1, arg2);
|
||
}
|
||
|
||
/* Return the current horizontal position of the "cursor" on the most
|
||
recently output message buffer line. */
|
||
int
|
||
message_buffer_length_this_line ()
|
||
{
|
||
register int i;
|
||
|
||
if (!message_buffer_index)
|
||
return (0);
|
||
|
||
for (i = message_buffer_index; i && message_buffer[i - 1] != '\n'; i--);
|
||
|
||
return (string_width (message_buffer + i, 0));
|
||
}
|
||
|
||
/* Pad STRING to COUNT characters by inserting blanks. */
|
||
int
|
||
pad_to (count, string)
|
||
int count;
|
||
char *string;
|
||
{
|
||
register int i;
|
||
|
||
i = strlen (string);
|
||
|
||
if (i >= count)
|
||
string[i++] = ' ';
|
||
else
|
||
{
|
||
while (i < count)
|
||
string[i++] = ' ';
|
||
}
|
||
string[i] = '\0';
|
||
|
||
return (i);
|
||
}
|