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