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