Initial import of binutils 2.22 on the new vendor branch
[dragonfly.git] / contrib / texinfo / info / window.c
1 /* window.c -- windows in Info.
2    $Id: window.c,v 1.17 2008/09/13 10:01:31 gray Exp $
3
4    Copyright (C) 1993, 1997, 1998, 2001, 2002, 2003, 2004, 2007, 2008
5    Free Software 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 3 of the License, or
10    (at your option) 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, see <http://www.gnu.org/licenses/>.
19
20    Written by Brian Fox (bfox@ai.mit.edu). */
21
22 #include "info.h"
23 #include "nodes.h"
24 #include "window.h"
25 #include "display.h"
26 #include "info-utils.h"
27 #include "infomap.h"
28
29 /* The window which describes the screen. */
30 WINDOW *the_screen = NULL;
31
32 /* The window which describes the echo area. */
33 WINDOW *the_echo_area = NULL;
34
35 /* The list of windows in Info. */
36 WINDOW *windows = NULL;
37
38 /* Pointer to the active window in WINDOW_LIST. */
39 WINDOW *active_window = NULL;
40
41 /* The size of the echo area in Info.  It never changes, irregardless of the
42    size of the screen. */
43 #define ECHO_AREA_HEIGHT 1
44
45 /* Macro returns the amount of space that the echo area truly requires relative
46    to the entire screen. */
47 #define echo_area_required (1 + the_echo_area->height)
48
49 /* Show malformed multibyte sequences */
50 int show_malformed_multibyte_p = 0;
51
52 /* Initalize the window system by creating THE_SCREEN and THE_ECHO_AREA.
53    Create the first window ever.
54    You pass the dimensions of the total screen size. */
55 void
56 window_initialize_windows (int width, int height)
57 {
58   the_screen = xmalloc (sizeof (WINDOW));
59   the_echo_area = xmalloc (sizeof (WINDOW));
60   windows = xmalloc (sizeof (WINDOW));
61   active_window = windows;
62
63   zero_mem (the_screen, sizeof (WINDOW));
64   zero_mem (the_echo_area, sizeof (WINDOW));
65   zero_mem (active_window, sizeof (WINDOW));
66
67   /* None of these windows has a goal column yet. */
68   the_echo_area->goal_column = -1;
69   active_window->goal_column = -1;
70   the_screen->goal_column = -1;
71
72   /* The active and echo_area windows are visible.
73      The echo_area is permanent.
74      The screen is permanent. */
75   active_window->flags = W_WindowVisible;
76   the_echo_area->flags = W_WindowIsPerm | W_InhibitMode | W_WindowVisible;
77   the_screen->flags    = W_WindowIsPerm;
78
79   /* The height of the echo area never changes.  It is statically set right
80      here, and it must be at least 1 line for display.  The size of the
81      initial window cannot be the same size as the screen, since the screen
82      includes the echo area.  So, we make the height of the initial window
83      equal to the screen's displayable region minus the height of the echo
84      area. */
85   the_echo_area->height = ECHO_AREA_HEIGHT;
86   active_window->height = the_screen->height - 1 - the_echo_area->height;
87   window_new_screen_size (width, height);
88
89   /* The echo area uses a different keymap than normal info windows. */
90   the_echo_area->keymap = echo_area_keymap;
91   active_window->keymap = info_keymap;
92 }
93
94 /* Given that the size of the screen has changed to WIDTH and HEIGHT
95    from whatever it was before (found in the_screen->height, ->width),
96    change the size (and possibly location) of each window in the screen.
97    If a window would become too small, call the function DELETER on it,
98    after deleting the window from our chain of windows.  If DELETER is NULL,
99    nothing extra is done.  The last window can never be deleted, but it can
100    become invisible. */
101
102 /* If non-null, a function to call with WINDOW as argument when the function
103    window_new_screen_size () has deleted WINDOW. */
104 VFunction *window_deletion_notifier = NULL;
105
106 void
107 window_new_screen_size (int width, int height)
108 {
109   register WINDOW *win;
110   int delta_height, delta_each, delta_leftover;
111   int numwins;
112
113   /* If no change, do nothing. */
114   if (width == the_screen->width && height == the_screen->height)
115     return;
116
117   /* If the new window height is too small, make it be zero. */
118   if (height < (WINDOW_MIN_SIZE + the_echo_area->height))
119     height = 0;
120   if (width < 0)
121     width = 0;
122
123   /* Find out how many windows will change. */
124   for (numwins = 0, win = windows; win; win = win->next, numwins++);
125
126   /* See if some windows will need to be deleted.  This is the case if
127      the screen is getting smaller, and the available space divided by
128      the number of windows is less than WINDOW_MIN_SIZE.  In that case,
129      delete some windows and try again until there is either enough
130      space to divy up among the windows, or until there is only one
131      window left. */
132   while ((height - echo_area_required) / numwins <= WINDOW_MIN_SIZE)
133     {
134       /* If only one window, make the size of it be zero, and return
135          immediately. */
136       if (!windows->next)
137         {
138           windows->height = 0;
139           maybe_free (windows->line_starts);
140           windows->line_starts = NULL;
141           windows->line_count = 0;
142           break;
143         }
144
145       /* If we have some temporary windows, delete one of them. */
146       for (win = windows; win; win = win->next)
147         if (win->flags & W_TempWindow)
148           break;
149
150       /* Otherwise, delete the first window, and try again. */
151       if (!win)
152         win = windows;
153
154       if (window_deletion_notifier)
155         (*window_deletion_notifier) (win);
156
157       window_delete_window (win);
158       numwins--;
159     }
160
161   /* The screen has changed height and width. */
162   delta_height = height - the_screen->height;   /* This is how much. */
163   the_screen->height = height;                  /* This is the new height. */
164   the_screen->width = width;                    /* This is the new width. */
165
166   /* Set the start of the echo area. */
167   the_echo_area->first_row = height - the_echo_area->height;
168   the_echo_area->width = width;
169
170   /* Check to see if the screen can really be changed this way. */
171   if ((!windows->next) && ((windows->height == 0) && (delta_height < 0)))
172     return;
173
174   /* Divide the change in height among the available windows. */
175   delta_each = delta_height / numwins;
176   delta_leftover = delta_height - (delta_each * numwins);
177
178   /* Change the height of each window in the chain by delta_each.  Change
179      the height of the last window in the chain by delta_each and by the
180      leftover amount of change.  Change the width of each window to be
181      WIDTH. */
182   for (win = windows; win; win = win->next)
183     {
184       if ((win->width != width) && ((win->flags & W_InhibitMode) == 0))
185         {
186           win->width = width;
187           maybe_free (win->modeline);
188           win->modeline = xmalloc (1 + width);
189         }
190
191       win->height += delta_each;
192
193       /* If the previous height of this window was zero, it was the only
194          window, and it was not visible.  Thus we need to compensate for
195          the echo_area. */
196       if (win->height == delta_each)
197         win->height -= (1 + the_echo_area->height);
198
199       /* If this is not the first window in the chain, then change the
200          first row of it.  We cannot just add delta_each to the first row,
201          since this window's first row is the sum of the collective increases
202          that have gone before it.  So we just add one to the location of the
203          previous window's modeline. */
204       if (win->prev)
205         win->first_row = (win->prev->first_row + win->prev->height) + 1;
206
207       /* The last window in the chain gets the extra space (or shrinkage). */
208       if (!win->next)
209         win->height += delta_leftover;
210
211       if (win->node)
212         recalculate_line_starts (win);
213
214       win->flags |= W_UpdateWindow;
215     }
216
217   /* If the screen got smaller, check over the windows just shrunk to
218      keep them within bounds.  Some of the windows may have gotten smaller
219      than WINDOW_MIN_HEIGHT in which case some of the other windows are
220      larger than the available display space in the screen.  Because of our
221      intial test above, we know that there is enough space for all of the
222      windows. */
223   if ((delta_each < 0) && ((windows->height != 0) && windows->next))
224     {
225       int avail;
226
227       avail = the_screen->height - (numwins + the_echo_area->height);
228       win = windows;
229
230       while (win)
231         {
232           if ((win->height < WINDOW_MIN_HEIGHT) ||
233               (win->height > avail))
234             {
235               WINDOW *lastwin = NULL;
236
237               /* Split the space among the available windows. */
238               delta_each = avail / numwins;
239               delta_leftover = avail - (delta_each * numwins);
240
241               for (win = windows; win; win = win->next)
242                 {
243                   lastwin = win;
244                   if (win->prev)
245                     win->first_row =
246                       (win->prev->first_row + win->prev->height) + 1;
247                   win->height = delta_each;
248                 }
249
250               /* Give the leftover space (if any) to the last window. */
251               lastwin->height += delta_leftover;
252               break;
253             }
254           else
255             win = win->next;
256         }
257     }
258
259   /* One more loop.  If any heights or widths have become negative,
260      set them to zero.  This can apparently happen with resizing down to
261      very small sizes.  Sadly, it is not apparent to me where in the
262      above calculations it goes wrong.  */
263   for (win = windows; win; win = win->next)
264     {
265       if (win->height < 0)
266         win->height = 0;
267
268       if (win->width < 0)
269         win->width = 0;
270     }
271 }
272
273 /* Make a new window showing NODE, and return that window structure.
274    If NODE is passed as NULL, then show the node showing in the active
275    window.  If the window could not be made return a NULL pointer.  The
276    active window is not changed.*/
277 WINDOW *
278 window_make_window (NODE *node)
279 {
280   WINDOW *window;
281
282   if (!node)
283     node = active_window->node;
284
285   /* If there isn't enough room to make another window, return now. */
286   if ((active_window->height / 2) < WINDOW_MIN_SIZE)
287     return NULL;
288
289   /* Make and initialize the new window.
290      The fudging about with -1 and +1 is because the following window in the
291      chain cannot start at window->height, since that is where the modeline
292      for the previous window is displayed.  The inverse adjustment is made
293      in window_delete_window (). */
294   window = xmalloc (sizeof (WINDOW));
295   window->width = the_screen->width;
296   window->height = (active_window->height / 2) - 1;
297 #if defined (SPLIT_BEFORE_ACTIVE)
298   window->first_row = active_window->first_row;
299 #else
300   window->first_row = active_window->first_row +
301     (active_window->height - window->height);
302 #endif
303   window->keymap = info_keymap;
304   window->goal_column = -1;
305   memset (&window->line_map, 0, sizeof (window->line_map));
306   window->modeline = xmalloc (1 + window->width);
307   window->line_starts = NULL;
308   window->flags = W_UpdateWindow | W_WindowVisible;
309   window_set_node_of_window (window, node);
310
311   /* Adjust the height of the old active window. */
312   active_window->height -= (window->height + 1);
313 #if defined (SPLIT_BEFORE_ACTIVE)
314   active_window->first_row += (window->height + 1);
315 #endif
316   active_window->flags |= W_UpdateWindow;
317
318   /* Readjust the new and old windows so that their modelines and contents
319      will be displayed correctly. */
320 #if defined (NOTDEF)
321   /* We don't have to do this for WINDOW since window_set_node_of_window ()
322      already did. */
323   window_adjust_pagetop (window);
324   window_make_modeline (window);
325 #endif /* NOTDEF */
326
327   /* We do have to readjust the existing active window. */
328   window_adjust_pagetop (active_window);
329   window_make_modeline (active_window);
330
331 #if defined (SPLIT_BEFORE_ACTIVE)
332   /* This window is just before the active one.  The active window gets
333      bumped down one.  The active window is not changed. */
334   window->next = active_window;
335
336   window->prev = active_window->prev;
337   active_window->prev = window;
338
339   if (window->prev)
340     window->prev->next = window;
341   else
342     windows = window;
343 #else
344   /* This window is just after the active one.  Which window is active is
345      not changed. */
346   window->prev = active_window;
347   window->next = active_window->next;
348   active_window->next = window;
349   if (window->next)
350     window->next->prev = window;
351 #endif /* !SPLIT_BEFORE_ACTIVE */
352   return window;
353 }
354
355 /* These useful macros make it possible to read the code in
356    window_change_window_height (). */
357 #define grow_me_shrinking_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 grow_me_shrinking_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 #define shrink_me_growing_next(me, next, diff) \
374   do { \
375     me->height -= diff; \
376     next->height += diff; \
377     next->first_row -= diff; \
378     window_adjust_pagetop (next); \
379   } while (0)
380
381 #define shrink_me_growing_prev(me, prev, diff) \
382   do { \
383     me->height -= diff; \
384     prev->height += diff; \
385     me->first_row += diff; \
386     window_adjust_pagetop (prev);               \
387   } while (0)
388
389 /* Change the height of WINDOW by AMOUNT.  This also automagically adjusts
390    the previous and next windows in the chain.  If there is only one user
391    window, then no change takes place. */
392 void
393 window_change_window_height (WINDOW *window, int amount)
394 {
395   register WINDOW *win, *prev, *next;
396
397   /* If there is only one window, or if the amount of change is zero,
398      return immediately. */
399   if (!windows->next || amount == 0)
400     return;
401
402   /* Find this window in our chain. */
403   for (win = windows; win; win = win->next)
404     if (win == window)
405       break;
406
407   /* If the window is isolated (i.e., doesn't appear in our window list,
408      then quit now. */
409   if (!win)
410     return;
411
412   /* Change the height of this window by AMOUNT, if that is possible.
413      It can be impossible if there isn't enough available room on the
414      screen, or if the resultant window would be too small. */
415
416     prev = window->prev;
417     next = window->next;
418
419   /* WINDOW decreasing in size? */
420   if (amount < 0)
421     {
422       int abs_amount = -amount; /* It is easier to deal with this way. */
423
424       /* If the resultant window would be too small, stop here. */
425       if ((window->height - abs_amount) < WINDOW_MIN_HEIGHT)
426         return;
427
428       /* If we have two neighboring windows, choose the smaller one to get
429          larger. */
430       if (next && prev)
431         {
432           if (prev->height < next->height)
433             shrink_me_growing_prev (window, prev, abs_amount);
434           else
435             shrink_me_growing_next (window, next, abs_amount);
436         }
437       else if (next)
438         shrink_me_growing_next (window, next, abs_amount);
439       else
440         shrink_me_growing_prev (window, prev, abs_amount);
441     }
442
443   /* WINDOW increasing in size? */
444   if (amount > 0)
445     {
446       int total_avail, next_avail = 0, prev_avail = 0;
447
448       if (next)
449         next_avail = next->height - WINDOW_MIN_SIZE;
450
451       if (prev)
452         prev_avail = prev->height - WINDOW_MIN_SIZE;
453
454       total_avail = next_avail + prev_avail;
455
456       /* If there isn't enough space available to grow this window, give up. */
457       if (amount > total_avail)
458         return;
459
460       /* If there aren't two neighboring windows, or if one of the neighbors
461          is larger than the other one by at least AMOUNT, grow that one. */
462       if ((next && !prev) || ((next_avail - amount) >= prev_avail))
463         grow_me_shrinking_next (window, next, amount);
464       else if ((prev && !next) || ((prev_avail - amount) >= next_avail))
465         grow_me_shrinking_prev (window, prev, amount);
466       else
467         {
468           int change;
469
470           /* This window has two neighbors.  They both must be shrunk in to
471              make enough space for WINDOW to grow.  Make them both the same
472              size. */
473           if (prev_avail > next_avail)
474             {
475               change = prev_avail - next_avail;
476               grow_me_shrinking_prev (window, prev, change);
477               amount -= change;
478             }
479           else
480             {
481               change = next_avail - prev_avail;
482               grow_me_shrinking_next (window, next, change);
483               amount -= change;
484             }
485
486           /* Both neighbors are the same size.  Split the difference in
487              AMOUNT between them. */
488           while (amount)
489             {
490               window->height++;
491               amount--;
492
493               /* Odd numbers grow next, even grow prev. */
494               if (amount & 1)
495                 {
496                   prev->height--;
497                   window->first_row--;
498                 }
499               else
500                 {
501                   next->height--;
502                   next->first_row++;
503                 }
504             }
505           window_adjust_pagetop (prev);
506           window_adjust_pagetop (next);
507         }
508     }
509   if (prev)
510     prev->flags |= W_UpdateWindow;
511
512   if (next)
513     next->flags |= W_UpdateWindow;
514
515   window->flags |= W_UpdateWindow;
516   window_adjust_pagetop (window);
517 }
518
519 /* Tile all of the windows currently displayed in the global variable
520    WINDOWS.  If argument STYLE is TILE_INTERNALS, tile windows displaying
521    internal nodes as well, otherwise do not change the height of such
522    windows. */
523 void
524 window_tile_windows (int style)
525 {
526   WINDOW *win, *last_adjusted;
527   int numwins, avail, per_win_height, leftover;
528   int do_internals;
529
530   numwins = avail = 0;
531   do_internals = (style == TILE_INTERNALS);
532
533   for (win = windows; win; win = win->next)
534     if (do_internals || !win->node ||
535         (win->node->flags & N_IsInternal) == 0)
536       {
537         avail += win->height;
538         numwins++;
539       }
540
541   if (numwins <= 1 || !the_screen->height)
542     return;
543
544   /* Find the size for each window.  Divide the size of the usable portion
545      of the screen by the number of windows. */
546   per_win_height = avail / numwins;
547   leftover = avail - (per_win_height * numwins);
548
549   last_adjusted = NULL;
550   for (win = windows; win; win = win->next)
551     {
552       if (do_internals || !win->node ||
553           (win->node->flags & N_IsInternal) == 0)
554         {
555           last_adjusted = win;
556           win->height = per_win_height;
557         }
558     }
559
560   if (last_adjusted)
561     last_adjusted->height += leftover;
562
563   /* Readjust the first_row of every window in the chain. */
564   for (win = windows; win; win = win->next)
565     {
566       if (win->prev)
567         win->first_row = win->prev->first_row + win->prev->height + 1;
568
569       window_adjust_pagetop (win);
570       win->flags |= W_UpdateWindow;
571     }
572 }
573
574 /* Toggle the state of line wrapping in WINDOW.  This can do a bit of fancy
575    redisplay. */
576 void
577 window_toggle_wrap (WINDOW *window)
578 {
579   if (window->flags & W_NoWrap)
580     window->flags &= ~W_NoWrap;
581   else
582     window->flags |= W_NoWrap;
583
584   if (window != the_echo_area)
585     {
586       char **old_starts;
587       int old_lines, old_pagetop;
588
589       old_starts = window->line_starts;
590       old_lines = window->line_count;
591       old_pagetop = window->pagetop;
592
593       calculate_line_starts (window);
594
595       /* Make sure that point appears within this window. */
596       window_adjust_pagetop (window);
597
598       /* If the pagetop hasn't changed maybe we can do some scrolling now
599          to speed up the display.  Many of the line starts will be the same,
600          so scrolling here is a very good optimization.*/
601       if (old_pagetop == window->pagetop)
602         display_scroll_line_starts
603           (window, old_pagetop, old_starts, old_lines);
604       maybe_free (old_starts);
605     }
606   window->flags |= W_UpdateWindow;
607 }
608
609 /* Set WINDOW to display NODE. */
610 void
611 window_set_node_of_window (WINDOW *window, NODE *node)
612 {
613   window->node = node;
614   window->pagetop = 0;
615   window->point = 0;
616   recalculate_line_starts (window);
617   window->flags |= W_UpdateWindow;
618   /* The display_pos member is nonzero if we're displaying an anchor.  */
619   window->point = node ? node->display_pos : 0;
620   window_adjust_pagetop (window);
621   window_make_modeline (window);
622 }
623 \f
624 /* Delete WINDOW from the list of known windows.  If this window was the
625    active window, make the next window in the chain be the active window.
626    If the active window is the next or previous window, choose that window
627    as the recipient of the extra space.  Otherwise, prefer the next window. */
628 void
629 window_delete_window (WINDOW *window)
630 {
631   WINDOW *next, *prev, *window_to_fix;
632
633   next = window->next;
634   prev = window->prev;
635
636   /* You cannot delete the only window or a permanent window. */
637   if ((!next && !prev) || (window->flags & W_WindowIsPerm))
638     return;
639
640   if (next)
641     next->prev = prev;
642
643   if (!prev)
644     windows = next;
645   else
646     prev->next = next;
647
648   if (window->line_starts)
649     free (window->line_starts);
650
651   if (window->modeline)
652     free (window->modeline);
653
654   if (window == active_window)
655     {
656       /* If there isn't a next window, then there must be a previous one,
657          since we cannot delete the last window.  If there is a next window,
658          prefer to use that as the active window. */
659       if (next)
660         active_window = next;
661       else
662         active_window = prev;
663     }
664
665   if (next && active_window == next)
666     window_to_fix = next;
667   else if (prev && active_window == prev)
668     window_to_fix = prev;
669   else if (next)
670     window_to_fix = next;
671   else if (prev)
672     window_to_fix = prev;
673   else
674     window_to_fix = windows;
675     
676   if (window_to_fix->first_row > window->first_row)
677     {
678       int diff;
679
680       /* Try to adjust the visible part of the node so that as little
681          text as possible has to move. */
682       diff = window_to_fix->first_row - window->first_row;
683       window_to_fix->first_row = window->first_row;
684
685       window_to_fix->pagetop -= diff;
686       if (window_to_fix->pagetop < 0)
687         window_to_fix->pagetop = 0;
688     }
689
690   /* The `+ 1' is to offset the difference between the first_row locations.
691      See the code in window_make_window (). */
692   window_to_fix->height += window->height + 1;
693   window_to_fix->flags |= W_UpdateWindow;
694
695   free (window);
696 }
697
698 /* For every window in CHAIN, set the flags member to have FLAG set. */
699 void
700 window_mark_chain (WINDOW *chain, int flag)
701 {
702   register WINDOW *win;
703
704   for (win = chain; win; win = win->next)
705     win->flags |= flag;
706 }
707
708 /* For every window in CHAIN, clear the flags member of FLAG. */
709 void
710 window_unmark_chain (WINDOW *chain, int flag)
711 {
712   register WINDOW *win;
713
714   for (win = chain; win; win = win->next)
715     win->flags &= ~flag;
716 }
717
718 /* Return the number of characters it takes to display CHARACTER on the
719    screen at HPOS. */
720 int
721 character_width (int character, int hpos)
722 {
723   int printable_limit = 127;
724   int width = 1;
725
726   if (ISO_Latin_p)
727     printable_limit = 255;
728
729   if (character > printable_limit)
730     width = 3;
731   else if (iscntrl (character))
732     {
733       switch (character)
734         {
735         case '\r':
736         case '\n':
737           width = the_screen->width - hpos;
738           break;
739         case '\t':
740           width = ((hpos + 8) & 0xf8) - hpos;
741           break;
742         default:
743           width = 2;
744         }
745     }
746   else if (character == DEL)
747     width = 2;
748
749   return width;
750 }
751
752 /* Return the number of characters it takes to display STRING on the screen
753    at HPOS. */
754 int
755 string_width (char *string, int hpos)
756 {
757   register int i, width, this_char_width;
758
759   for (width = 0, i = 0; string[i]; i++)
760     {
761       /* Support ANSI escape sequences for -R.  */
762       if (raw_escapes_p
763           && string[i] == '\033'
764           && string[i+1] == '['
765           && isdigit (string[i+2])
766           && (string[i+3] == 'm'
767               || (isdigit (string[i+3]) && string[i+4] == 'm')))
768         {
769           while (string[i] != 'm')
770             i++;
771           this_char_width = 0;
772         }
773       else
774         this_char_width = character_width (string[i], hpos);
775       width += this_char_width;
776       hpos += this_char_width;
777     }
778   return width;
779 }
780
781 /* Quickly guess the approximate number of lines that NODE would
782    take to display.  This really only counts carriage returns. */
783 int
784 window_physical_lines (NODE *node)
785 {
786   register int i, lines;
787   char *contents;
788
789   if (!node)
790     return 0;
791
792   contents = node->contents;
793   for (i = 0, lines = 1; i < node->nodelen; i++)
794     if (contents[i] == '\n')
795       lines++;
796
797   return lines;
798 }
799
800 \f
801 struct calc_closure {
802   WINDOW *win;
803   int line_starts_slots; /* FIXME: size_t */
804 };
805
806 static int
807 _calc_line_starts (void *closure, size_t line_index,
808                    const char *src_line,
809                    char *printed_line, size_t pl_index, size_t pl_count)
810 {
811   struct calc_closure *cp = closure;
812   add_pointer_to_array (src_line,
813                         cp->win->line_count, cp->win->line_starts,
814                         cp->line_starts_slots, 100, char *);
815   return 0;
816 }
817
818 void
819 calculate_line_starts (WINDOW *window)
820 {
821   struct calc_closure closure;
822
823   window->line_starts = NULL;
824   window->line_count = 0;
825
826   if (!window->node)
827     return;
828   
829   closure.win = window;
830   closure.line_starts_slots = 0;
831   process_node_text (window, window->node->contents, 0,
832                      _calc_line_starts, &closure);
833 }
834
835 /* Given WINDOW, recalculate the line starts for the node it displays. */
836 void
837 recalculate_line_starts (WINDOW *window)
838 {
839   maybe_free (window->line_starts);
840   calculate_line_starts (window);
841 }
842
843 \f
844 /* Global variable control redisplay of scrolled windows.  If non-zero,
845    it is the desired number of lines to scroll the window in order to
846    make point visible.  A value of 1 produces smooth scrolling.  If set
847    to zero, the line containing point is centered within the window. */
848 int window_scroll_step = 1;
849
850 /* Adjust the pagetop of WINDOW such that the cursor point will be visible. */
851 void
852 window_adjust_pagetop (WINDOW *window)
853 {
854   register int line = 0;
855   char *contents;
856
857   if (!window->node)
858     return;
859
860   contents = window->node->contents;
861
862   /* Find the first printed line start which is after WINDOW->point. */
863   for (line = 0; line < window->line_count; line++)
864     {
865       char *line_start;
866
867       line_start = window->line_starts[line];
868
869       if ((line_start - contents) > window->point)
870         break;
871     }
872
873   /* The line index preceding the line start which is past point is the
874      one containing point. */
875   line--;
876
877   /* If this line appears in the current displayable page, do nothing.
878      Otherwise, adjust the top of the page to make this line visible. */
879   if ((line < window->pagetop) ||
880       (line - window->pagetop > (window->height - 1)))
881     {
882       /* The user-settable variable "scroll-step" is used to attempt
883          to make point visible, iff it is non-zero.  If that variable
884          is zero, then the line containing point is centered within
885          the window. */
886       if (window_scroll_step < window->height)
887         {
888           if ((line < window->pagetop) &&
889               ((window->pagetop - window_scroll_step) <= line))
890             window->pagetop -= window_scroll_step;
891           else if ((line - window->pagetop > (window->height - 1)) &&
892                    ((line - (window->pagetop + window_scroll_step)
893                      < window->height)))
894             window->pagetop += window_scroll_step;
895           else
896             window->pagetop = line - ((window->height - 1) / 2);
897         }
898       else
899         window->pagetop = line - ((window->height - 1) / 2);
900
901       if (window->pagetop < 0)
902         window->pagetop = 0;
903       window->flags |= W_UpdateWindow;
904     }
905 }
906
907 /* Return the index of the line containing point. */
908 int
909 window_line_of_point (WINDOW *window)
910 {
911   register int i, start = 0;
912
913   /* Try to optimize.  Check to see if point is past the pagetop for
914      this window, and if so, start searching forward from there. */
915   if ((window->pagetop > -1 && window->pagetop < window->line_count) &&
916       (window->line_starts[window->pagetop] - window->node->contents)
917       <= window->point)
918     start = window->pagetop;
919
920   for (i = start; i < window->line_count; i++)
921     {
922       if ((window->line_starts[i] - window->node->contents) > window->point)
923         break;
924     }
925
926   return i - 1;
927 }
928
929 /* Get and return the goal column for this window. */
930 int
931 window_get_goal_column (WINDOW *window)
932 {
933   if (!window->node)
934     return -1;
935
936   if (window->goal_column != -1)
937     return window->goal_column;
938
939   /* Okay, do the work.  Find the printed offset of the cursor
940      in this window. */
941   return window_get_cursor_column (window);
942 }
943
944 /* Get and return the printed column offset of the cursor in this window. */
945 int
946 window_get_cursor_column (WINDOW *window)
947 {
948   return window_point_to_column (window, window->point, &window->point);
949 }
950
951 /* Count the number of characters in LINE that precede the printed column
952    offset of GOAL. */
953 int
954 window_chars_to_goal (WINDOW *win, int goal)
955 {
956   window_compute_line_map (win);
957   if (goal >= win->line_map.used)
958     goal = win->line_map.used - 1;
959   return win->line_map.map[goal] - win->line_map.map[0];
960 }
961
962 /* Create a modeline for WINDOW, and store it in window->modeline. */
963 void
964 window_make_modeline (WINDOW *window)
965 {
966   register int i;
967   char *modeline;
968   char location_indicator[4];
969   int lines_remaining;
970
971   /* Only make modelines for those windows which have one. */
972   if (window->flags & W_InhibitMode)
973     return;
974
975   /* Find the number of lines actually displayed in this window. */
976   lines_remaining = window->line_count - window->pagetop;
977
978   if (window->pagetop == 0)
979     {
980       if (lines_remaining <= window->height)
981         strcpy (location_indicator, "All");
982       else
983         strcpy (location_indicator, "Top");
984     }
985   else
986     {
987       if (lines_remaining <= window->height)
988         strcpy (location_indicator, "Bot");
989       else
990         {
991           float pt, lc;
992           int percentage;
993
994           pt = (float)window->pagetop;
995           lc = (float)window->line_count;
996
997           percentage = 100 * (pt / lc);
998
999           sprintf (location_indicator, "%2d%%", percentage);
1000         }
1001     }
1002
1003   /* Calculate the maximum size of the information to stick in MODELINE. */
1004   {
1005     int modeline_len = 0;
1006     char *parent = NULL, *filename = "*no file*";
1007     char *nodename = "*no node*";
1008     const char *update_message = NULL;
1009     NODE *node = window->node;
1010
1011     if (node)
1012       {
1013         if (node->nodename)
1014           nodename = node->nodename;
1015
1016         if (node->parent)
1017           {
1018             parent = filename_non_directory (node->parent);
1019             modeline_len += strlen ("Subfile: ") + strlen (node->filename);
1020           }
1021
1022         if (node->filename)
1023           filename = filename_non_directory (node->filename);
1024
1025         if (node->flags & N_UpdateTags)
1026           update_message = _("--*** Tags out of Date ***");
1027       }
1028
1029     if (update_message)
1030       modeline_len += strlen (update_message);
1031     modeline_len += strlen (filename);
1032     modeline_len += strlen (nodename);
1033     modeline_len += 4;          /* strlen (location_indicator). */
1034
1035     /* 10 for the decimal representation of the number of lines in this
1036        node, and the remainder of the text that can appear in the line. */
1037     modeline_len += 10 + strlen (_("-----Info: (), lines ----, "));
1038     modeline_len += window->width;
1039
1040     modeline = xmalloc (1 + modeline_len);
1041
1042     /* Special internal windows have no filename. */
1043     if (!parent && !*filename)
1044       sprintf (modeline, _("-%s---Info: %s, %d lines --%s--"),
1045                (window->flags & W_NoWrap) ? "$" : "-",
1046                nodename, window->line_count, location_indicator);
1047     else
1048       sprintf (modeline, _("-%s%s-Info: (%s)%s, %d lines --%s--"),
1049                (window->flags & W_NoWrap) ? "$" : "-",
1050                (node && (node->flags & N_IsCompressed)) ? "zz" : "--",
1051                parent ? parent : filename,
1052                nodename, window->line_count, location_indicator);
1053
1054     if (parent)
1055       sprintf (modeline + strlen (modeline), _(" Subfile: %s"), filename);
1056
1057     if (update_message)
1058       sprintf (modeline + strlen (modeline), "%s", update_message);
1059
1060     i = strlen (modeline);
1061
1062     if (i >= window->width)
1063       modeline[window->width] = '\0';
1064     else
1065       {
1066         while (i < window->width)
1067           modeline[i++] = '-';
1068         modeline[i] = '\0';
1069       }
1070
1071     strcpy (window->modeline, modeline);
1072     free (modeline);
1073   }
1074 }
1075
1076 /* Make WINDOW start displaying at PERCENT percentage of its node. */
1077 void
1078 window_goto_percentage (WINDOW *window, int percent)
1079 {
1080   int desired_line;
1081
1082   if (!percent)
1083     desired_line = 0;
1084   else
1085     desired_line =
1086       (int) ((float)window->line_count * ((float)percent / 100.0));
1087
1088   window->pagetop = desired_line;
1089   window->point =
1090     window->line_starts[window->pagetop] - window->node->contents;
1091   window->flags |= W_UpdateWindow;
1092   window_make_modeline (window);
1093 }
1094
1095 /* Get the state of WINDOW, and save it in STATE. */
1096 void
1097 window_get_state (WINDOW *window, SEARCH_STATE *state)
1098 {
1099   state->node = window->node;
1100   state->pagetop = window->pagetop;
1101   state->point = window->point;
1102 }
1103
1104 /* Set the node, pagetop, and point of WINDOW. */
1105 void
1106 window_set_state (WINDOW *window, SEARCH_STATE *state)
1107 {
1108   if (window->node != state->node)
1109     window_set_node_of_window (window, state->node);
1110   window->pagetop = state->pagetop;
1111   window->point = state->point;
1112 }
1113
1114 \f
1115 /* Manipulating home-made nodes.  */
1116
1117 /* A place to buffer echo area messages. */
1118 static NODE *echo_area_node = NULL;
1119
1120 /* Make the node of the_echo_area be an empty one. */
1121 static void
1122 free_echo_area (void)
1123 {
1124   if (echo_area_node)
1125     {
1126       maybe_free (echo_area_node->contents);
1127       free (echo_area_node);
1128     }
1129
1130   echo_area_node = NULL;
1131   window_set_node_of_window (the_echo_area, echo_area_node);
1132 }
1133   
1134 /* Clear the echo area, removing any message that is already present.
1135    The echo area is cleared immediately. */
1136 void
1137 window_clear_echo_area (void)
1138 {
1139   free_echo_area ();
1140   display_update_one_window (the_echo_area);
1141 }
1142
1143 /* Make a message appear in the echo area, built from FORMAT, ARG1 and ARG2.
1144    The arguments are treated similar to printf () arguments, but not all of
1145    printf () hair is present.  The message appears immediately.  If there was
1146    already a message appearing in the echo area, it is removed. */
1147 void
1148 window_message_in_echo_area (const char *format, void *arg1, void *arg2)
1149 {
1150   free_echo_area ();
1151   echo_area_node = build_message_node (format, arg1, arg2);
1152   window_set_node_of_window (the_echo_area, echo_area_node);
1153   display_update_one_window (the_echo_area);
1154 }
1155
1156 /* Place a temporary message in the echo area built from FORMAT, ARG1
1157    and ARG2.  The message appears immediately, but does not destroy
1158    any existing message.  A future call to unmessage_in_echo_area ()
1159    restores the old contents. */
1160 static NODE **old_echo_area_nodes = NULL;
1161 static int old_echo_area_nodes_index = 0;
1162 static int old_echo_area_nodes_slots = 0;
1163
1164 void
1165 message_in_echo_area (const char *format, void *arg1, void *arg2)
1166 {
1167   if (echo_area_node)
1168     {
1169       add_pointer_to_array (echo_area_node, old_echo_area_nodes_index,
1170                             old_echo_area_nodes, old_echo_area_nodes_slots,
1171                             4, NODE *);
1172     }
1173   echo_area_node = NULL;
1174   window_message_in_echo_area (format, arg1, arg2);
1175 }
1176
1177 void
1178 unmessage_in_echo_area (void)
1179 {
1180   free_echo_area ();
1181
1182   if (old_echo_area_nodes_index)
1183     echo_area_node = old_echo_area_nodes[--old_echo_area_nodes_index];
1184
1185   window_set_node_of_window (the_echo_area, echo_area_node);
1186   display_update_one_window (the_echo_area);
1187 }
1188
1189 /* A place to build a message. */
1190 static char *message_buffer = NULL;
1191 static int message_buffer_index = 0;
1192 static int message_buffer_size = 0;
1193
1194 /* Ensure that there is enough space to stuff LENGTH characters into
1195    MESSAGE_BUFFER. */
1196 static void
1197 message_buffer_resize (int length)
1198 {
1199   if (!message_buffer)
1200     {
1201       message_buffer_size = length + 1;
1202       message_buffer = xmalloc (message_buffer_size);
1203       message_buffer_index = 0;
1204     }
1205
1206   while (message_buffer_size <= message_buffer_index + length)
1207     message_buffer = (char *)
1208       xrealloc (message_buffer,
1209                 message_buffer_size += 100 + (2 * length));
1210 }
1211
1212 /* Format MESSAGE_BUFFER with the results of printing FORMAT with ARG1 and
1213    ARG2. */
1214 static void
1215 build_message_buffer (const char *format, void *arg1, void *arg2, void *arg3)
1216 {
1217   register int i, len;
1218   void *args[3];
1219   int arg_index = 0;
1220
1221   args[0] = arg1;
1222   args[1] = arg2;
1223   args[2] = arg3;
1224
1225   len = strlen (format);
1226
1227   message_buffer_resize (len);
1228
1229   for (i = 0; format[i]; i++)
1230     {
1231       if (format[i] != '%')
1232         {
1233           message_buffer[message_buffer_index++] = format[i];
1234           len--;
1235         }
1236       else
1237         {
1238           char c;
1239           const char *fmt_start = format + i;
1240           char *fmt;
1241           int fmt_len, formatted_len;
1242           int paramed = 0;
1243
1244         format_again:
1245           i++;
1246           while (format[i] && strchr ("-. +0123456789", format[i]))
1247             i++;
1248           c = format[i];
1249
1250           if (c == '\0')
1251             abort ();
1252
1253           if (c == '$') {
1254             /* position parameter parameter */
1255             /* better to use bprintf from bfox's metahtml? */
1256             arg_index = atoi(fmt_start + 1) - 1;
1257             if (arg_index < 0)
1258               arg_index = 0;
1259             if (arg_index >= 2)
1260               arg_index = 1;
1261             paramed = 1;
1262             goto format_again;
1263           }
1264
1265           fmt_len = format + i - fmt_start + 1;
1266           fmt = xmalloc (fmt_len + 1);
1267           strncpy (fmt, fmt_start, fmt_len);
1268           fmt[fmt_len] = '\0';
1269
1270           if (paramed) {
1271             /* removed positioned parameter */
1272             char *p;
1273             for (p = fmt + 1; *p && *p != '$'; p++) {
1274               ;
1275             }
1276             strcpy(fmt + 1, p + 1);
1277           }
1278
1279           /* If we have "%-98s", maybe 98 calls for a longer string.  */
1280           if (fmt_len > 2)
1281             {
1282               int j;
1283
1284               for (j = fmt_len - 2; j >= 0; j--)
1285                 if (isdigit (fmt[j]) || fmt[j] == '$')
1286                   break;
1287
1288               formatted_len = atoi (fmt + j);
1289             }
1290           else
1291             formatted_len = c == 's' ? 0 : 1; /* %s can produce empty string */
1292
1293           switch (c)
1294             {
1295             case '%':           /* Insert a percent sign. */
1296               message_buffer_resize (len + formatted_len);
1297               sprintf
1298                 (message_buffer + message_buffer_index, fmt, "%");
1299               message_buffer_index += formatted_len;
1300               break;
1301
1302             case 's':           /* Insert the current arg as a string. */
1303               {
1304                 char *string;
1305                 int string_len;
1306
1307                 string = (char *)args[arg_index++];
1308                 string_len = strlen (string);
1309
1310                 if (formatted_len > string_len)
1311                   string_len = formatted_len;
1312                 message_buffer_resize (len + string_len);
1313                 sprintf
1314                   (message_buffer + message_buffer_index, fmt, string);
1315                 message_buffer_index += string_len;
1316               }
1317               break;
1318
1319             case 'd':           /* Insert the current arg as an integer. */
1320               {
1321                 long long_val;
1322                 int integer;
1323
1324                 long_val = (long)args[arg_index++];
1325                 integer = (int)long_val;
1326
1327                 message_buffer_resize (len + formatted_len > 32
1328                                        ? formatted_len : 32);
1329                 sprintf
1330                   (message_buffer + message_buffer_index, fmt, integer);
1331                 message_buffer_index = strlen (message_buffer);
1332               }
1333               break;
1334
1335             case 'c':           /* Insert the current arg as a character. */
1336               {
1337                 long long_val;
1338                 int character;
1339
1340                 long_val = (long)args[arg_index++];
1341                 character = (int)long_val;
1342
1343                 message_buffer_resize (len + formatted_len);
1344                 sprintf
1345                   (message_buffer + message_buffer_index, fmt, character);
1346                 message_buffer_index += formatted_len;
1347               }
1348               break;
1349
1350             default:
1351               abort ();
1352             }
1353           free (fmt);
1354         }
1355     }
1356   message_buffer[message_buffer_index] = '\0';
1357 }
1358
1359 /* Build a new node which has FORMAT printed with ARG1 and ARG2 as the
1360    contents. */
1361 NODE *
1362 build_message_node (const char *format, void *arg1, void *arg2)
1363 {
1364   NODE *node;
1365
1366   message_buffer_index = 0;
1367   build_message_buffer (format, arg1, arg2, 0);
1368
1369   node = message_buffer_to_node ();
1370   return node;
1371 }
1372
1373 /* Convert the contents of the message buffer to a node. */
1374 NODE *
1375 message_buffer_to_node (void)
1376 {
1377   NODE *node;
1378
1379   node = xmalloc (sizeof (NODE));
1380   node->filename = NULL;
1381   node->parent = NULL;
1382   node->nodename = NULL;
1383   node->flags = 0;
1384   node->display_pos =0;
1385
1386   /* Make sure that this buffer ends with a newline. */
1387   node->nodelen = 1 + strlen (message_buffer);
1388   node->contents = xmalloc (1 + node->nodelen);
1389   strcpy (node->contents, message_buffer);
1390   node->contents[node->nodelen - 1] = '\n';
1391   node->contents[node->nodelen] = '\0';
1392   return node;
1393 }
1394
1395 /* Useful functions can be called from outside of window.c. */
1396 void
1397 initialize_message_buffer (void)
1398 {
1399   message_buffer_index = 0;
1400 }
1401
1402 /* Print FORMAT with ARG1,2 to the end of the current message buffer. */
1403 void
1404 printf_to_message_buffer (const char *format, void *arg1, void *arg2, void *arg3)
1405 {
1406   build_message_buffer (format, arg1, arg2, arg3);
1407 }
1408
1409 /* Return the current horizontal position of the "cursor" on the most
1410    recently output message buffer line. */
1411 int
1412 message_buffer_length_this_line (void)
1413 {
1414   register int i;
1415
1416   if (!message_buffer_index)
1417     return 0;
1418
1419   for (i = message_buffer_index; i && message_buffer[i - 1] != '\n'; i--);
1420
1421   return string_width (message_buffer + i, 0);
1422 }
1423
1424 /* Pad STRING to COUNT characters by inserting blanks. */
1425 int
1426 pad_to (int count, char *string)
1427 {
1428   register int i;
1429
1430   i = strlen (string);
1431
1432   if (i >= count)
1433     string[i++] = ' ';
1434   else
1435     {
1436       while (i < count)
1437         string[i++] = ' ';
1438     }
1439   string[i] = '\0';
1440
1441   return i;
1442 }
1443
1444 \f
1445 #define ITER_SETBYTES(iter,n) ((iter).cur.bytes = n)
1446 #define ITER_LIMIT(iter) ((iter).limit - (iter).cur.ptr)
1447
1448 /* If ITER points to an ANSI escape sequence, process it, set PLEN to its
1449    length in bytes, and return 1.
1450    Otherwise, return 0.
1451  */
1452 static int
1453 ansi_escape (mbi_iterator_t iter, size_t *plen)
1454 {
1455   if (raw_escapes_p && *mbi_cur_ptr (iter) == '\033' && mbi_avail (iter))
1456     {
1457       mbi_advance (iter);
1458       if (*mbi_cur_ptr (iter) == '[' &&  mbi_avail (iter))
1459         {
1460           ITER_SETBYTES (iter, 1);
1461           mbi_advance (iter);
1462           if (isdigit (*mbi_cur_ptr (iter)) && mbi_avail (iter))
1463             {   
1464               ITER_SETBYTES (iter, 1);
1465               mbi_advance (iter);
1466               if (*mbi_cur_ptr (iter) == 'm')
1467                 {
1468                   *plen = 4;
1469                   return 1;
1470                 }
1471               else if (isdigit (*mbi_cur_ptr (iter)) && mbi_avail (iter))
1472                 {
1473                   ITER_SETBYTES (iter, 1);
1474                   mbi_advance (iter);
1475                   if (*mbi_cur_ptr (iter) == 'm')
1476                     {
1477                       *plen = 5;
1478                       return 1;
1479                     }
1480                 }
1481             }
1482         }
1483     }
1484                 
1485   return 0;
1486 }
1487
1488 /* If ITER points to an info tag, process it, set PLEN to its
1489    length in bytes, and return 1.
1490    Otherwise, return 0.
1491
1492    Collected tag is processed if HANDLE!=0.
1493 */
1494 static int
1495 info_tag (mbi_iterator_t iter, int handle, size_t *plen)
1496 {
1497   if (*mbi_cur_ptr (iter) == '\0' && mbi_avail (iter))
1498     {
1499       mbi_advance (iter);
1500       if (*mbi_cur_ptr (iter) == '\b' && mbi_avail (iter))
1501         {
1502           mbi_advance (iter);
1503           if (*mbi_cur_ptr (iter) == '[' && mbi_avail (iter))
1504             {
1505               const char *ptr, *end;
1506               mbi_advance (iter);
1507               ptr = mbi_cur_ptr (iter);
1508               end = memmem (ptr, ITER_LIMIT (iter), "\0\b]", 3);
1509               if (end)
1510                 {
1511                   size_t len = end - ptr;
1512
1513                   if (handle)
1514                     {
1515                       char *elt = xmalloc (len + 1);
1516                       memcpy (elt, ptr, len);
1517                       elt[len] = 0;
1518                       handle_tag (elt);
1519                       free (elt);
1520                     }
1521                   *plen = len + 6;
1522                   return 1;
1523                 }
1524             }
1525         }
1526     }
1527
1528   return 0;
1529 }
1530
1531 /* Process contents of the current node from WIN, beginning from START, using
1532    callback function FUN.
1533
1534    FUN is called for every line collected from the node. Its arguments:
1535
1536      int (*fun) (void *closure, size_t line_no,
1537                   const char *src_line, char *prt_line,
1538                   size_t prt_bytes, size_t prt_chars)
1539
1540      closure  -- An opaque pointer passed as 5th parameter to process_node_text;
1541      line_no  -- Number of processed line (starts from 0);
1542      src_line -- Pointer to the source line (unmodified);
1543      prt_line -- Collected line contents, ready for output;
1544      prt_bytes -- Number of bytes in prt_line;
1545      prt_chars -- Number of characters in prt_line.
1546
1547    If FUN returns non zero, process_node_text stops processing and returns
1548    immediately.
1549
1550    If DO_TAGS is not zero, process info tags, otherwise ignore them.
1551
1552    Return value: number of lines processed.
1553 */
1554    
1555 size_t
1556 process_node_text (WINDOW *win, char *start,
1557                    int do_tags,
1558                    int (*fun) (void *, size_t, const char *, char *, size_t, size_t),
1559                    void *closure)
1560 {
1561   char *printed_line;      /* Buffer for a printed line. */
1562   size_t pl_count = 0;     /* Number of *characters* written to PRINTED_LINE */
1563   size_t pl_index = 0;     /* Index into PRINTED_LINE. */
1564   size_t in_index = 0;
1565   size_t line_index = 0;   /* Number of lines done so far. */
1566   size_t allocated_win_width;
1567   mbi_iterator_t iter;
1568   
1569   /* Print each line in the window into our local buffer, and then
1570      check the contents of that buffer against the display.  If they
1571      differ, update the display. */
1572   allocated_win_width = win->width + 1;
1573   printed_line = xmalloc (allocated_win_width);
1574
1575   for (mbi_init (iter, start, 
1576                  win->node->contents + win->node->nodelen - start),
1577          pl_count = 0;
1578        mbi_avail (iter);
1579        mbi_advance (iter))
1580     {
1581       const char *carried_over_ptr;
1582       size_t carried_over_len, carried_over_count;
1583       const char *cur_ptr = mbi_cur_ptr (iter);
1584       int cur_len = mb_len (mbi_cur (iter));
1585       int replen;
1586       int delim = 0;
1587       int rc;
1588
1589       if (mb_isprint (mbi_cur (iter)))
1590         {
1591           replen = 1;
1592         }
1593       else if (cur_len == 1)
1594         {
1595           if (*cur_ptr == '\r' || *cur_ptr == '\n')
1596             {
1597               replen = win->width - pl_count;
1598               delim = 1;
1599             }
1600           else if (ansi_escape (iter, &cur_len))
1601             {
1602               replen = 0;
1603               ITER_SETBYTES (iter, cur_len);
1604             }
1605           else if (info_tag (iter, do_tags, &cur_len)) 
1606             {
1607               ITER_SETBYTES (iter, cur_len);
1608               continue;
1609             }
1610           else
1611             {
1612               if (*cur_ptr == '\t')
1613                 delim = 1;
1614               cur_ptr = printed_representation (cur_ptr, cur_len, pl_count,
1615                                                 &cur_len);
1616               replen = cur_len;
1617             }
1618         }
1619       else if (show_malformed_multibyte_p || mbi_cur (iter).wc_valid)
1620         {
1621           /* FIXME: I'm not sure it's the best way to deal with unprintable
1622              multibyte characters */
1623           cur_ptr = printed_representation (cur_ptr, cur_len, pl_count,
1624                                             &cur_len);
1625           replen = cur_len;
1626         }
1627
1628       /* Ensure there is enough space in the buffer */
1629       while (pl_index + cur_len + 2 > allocated_win_width - 1)
1630         printed_line = x2realloc (printed_line, &allocated_win_width);
1631
1632       /* If this character can be printed without passing the width of
1633          the line, then stuff it into the line. */
1634       if (pl_count + replen < win->width)
1635         {
1636           int i;
1637           
1638           for (i = 0; i < cur_len; i++)
1639             printed_line[pl_index++] = cur_ptr[i];
1640           pl_count += replen;
1641           in_index += mb_len (mbi_cur (iter));
1642         }
1643       else
1644         {
1645           /* If this character cannot be printed in this line, we have
1646              found the end of this line as it would appear on the screen.
1647              Carefully print the end of the line, and then compare. */
1648           if (delim)
1649             {
1650               printed_line[pl_index] = '\0';
1651               carried_over_ptr = NULL;
1652             }
1653           else
1654             {
1655               /* The printed representation of this character extends into
1656                  the next line. */
1657
1658               carried_over_count = replen;
1659               if (replen == 1)
1660                 {
1661                   /* It is a single (possibly multibyte) character */
1662                   /* FIXME? */
1663                   carried_over_ptr = cur_ptr;
1664                   carried_over_len = cur_len;
1665                 }
1666               else
1667                 {
1668                   int i;
1669                   
1670                   /* Remember the offset of the last character printed out of
1671                      REP so that we can carry the character over to the next
1672                      line. */
1673                   for (i = 0; pl_count < (win->width - 1);
1674                        pl_count++)
1675                     printed_line[pl_index++] = cur_ptr[i++];
1676
1677                   carried_over_ptr = cur_ptr + i;
1678                   carried_over_len = cur_len;
1679                 }
1680
1681               /* If printing the last character in this window couldn't
1682                  possibly cause the screen to scroll, place a backslash
1683                  in the rightmost column. */
1684               if (1 + line_index + win->first_row < the_screen->height)
1685                 {
1686                   if (win->flags & W_NoWrap)
1687                     printed_line[pl_index++] = '$';
1688                   else
1689                     printed_line[pl_index++] = '\\';
1690                   pl_count++;
1691                 }
1692               printed_line[pl_index] = '\0';
1693             }
1694
1695           rc = fun (closure, line_index, mbi_cur_ptr (iter) - in_index,
1696                     printed_line, pl_index, pl_count);
1697
1698           ++line_index;
1699
1700           /* Reset all data to the start of the line. */
1701           pl_index = 0;
1702           pl_count = 0;
1703           in_index = 0;
1704
1705           if (rc)
1706             break;
1707           
1708           /* If there are bytes carried over, stuff them
1709              into the buffer now. */
1710           if (carried_over_ptr)
1711             {
1712               for (; carried_over_len;
1713                    carried_over_len--, carried_over_ptr++, pl_index++)
1714                 printed_line[pl_index] = *carried_over_ptr;
1715               pl_count += carried_over_count;
1716             }
1717         
1718           /* If this window has chosen not to wrap lines, skip to the end
1719              of the physical line in the buffer, and start a new line here. */
1720           if (pl_index && win->flags & W_NoWrap)
1721             {
1722               for (; mbi_avail (iter); mbi_advance (iter))
1723                 if (mb_len (mbi_cur (iter)) == 1
1724                     && *mbi_cur_ptr (iter) == '\n')
1725                   break;
1726
1727               pl_index = 0;
1728               pl_count = 0;
1729               in_index = 0;
1730               printed_line[0] = 0;
1731             }
1732         }
1733     }
1734
1735   if (pl_count)
1736     fun (closure, line_index, mbi_cur_ptr (iter) - in_index,
1737          printed_line, pl_index, pl_count);
1738
1739   free (printed_line);
1740   return line_index;
1741 }
1742
1743 void
1744 clean_manpage (char *manpage)
1745 {
1746   mbi_iterator_t iter;
1747   size_t len = strlen (manpage);
1748   char *newpage = xmalloc (len + 1);
1749   char *np = newpage;
1750   int prev_len = 0;
1751   
1752   for (mbi_init (iter, manpage, len);
1753        mbi_avail (iter);
1754        mbi_advance (iter))
1755     {
1756       const char *cur_ptr = mbi_cur_ptr (iter);
1757       int cur_len = mb_len (mbi_cur (iter));
1758
1759       if (cur_len == 1)
1760         {
1761           if (*cur_ptr == '\b' || *cur_ptr == '\f')
1762             {
1763               if (np >= newpage + prev_len)
1764                 np -= prev_len;
1765             }
1766           else if (ansi_escape (iter, &cur_len))
1767             {
1768               memcpy (np, cur_ptr, cur_len);
1769               np += cur_len;
1770               ITER_SETBYTES (iter, cur_len);
1771             }
1772           else if (show_malformed_multibyte_p || mbi_cur (iter).wc_valid)
1773             *np++ = *cur_ptr;
1774         }
1775       else
1776         {
1777           memcpy (np, cur_ptr, cur_len);
1778           np += cur_len;
1779         }
1780       prev_len = cur_len;
1781     }
1782   *np = 0;
1783   
1784   strcpy (manpage, newpage);
1785   free (newpage);
1786 }
1787
1788 static void
1789 line_map_init (LINE_MAP *map, NODE *node, int line)
1790 {
1791   map->node = node;
1792   map->nline = line;
1793   map->used = 0;
1794 }
1795
1796 static void
1797 line_map_add (LINE_MAP *map, long pos)
1798 {
1799   if (map->used == map->size)
1800     {
1801       if (map->size == 0)                                      
1802         map->size = 80; /* Initial allocation */               
1803       map->map = x2nrealloc (map->map,
1804                              &map->size,
1805                              sizeof (map->map[0]));
1806     }
1807
1808   map->map[map->used++] = pos;
1809 }
1810
1811 /* Initialize (clear) WIN's line map. */
1812 void
1813 window_line_map_init (WINDOW *win)
1814 {
1815   win->line_map.used = 0;
1816 }
1817
1818 /* Scan the line number LINE in WIN.  If PHYS is true, stop scanning at
1819    the end of physical line, i.e. at the newline character.  Otherwise,
1820    stop it at the end of logical line.
1821
1822    If FUN is supplied, call it for each processed multibyte character.
1823    Arguments of FUN are
1824
1825      closure  -  Function-specific data passed as 5th argument to
1826                  window_scan_line;
1827      cpos     -  Current point value;
1828      replen   -  Size of screen representation of this character, in
1829                  columns.  This value may be 0 (for ANSI sequences and
1830                  info tags), or > 1 (for tabs).
1831  */
1832 int
1833 window_scan_line (WINDOW *win, int line, int phys,
1834                   void (*fun) (void *closure, long cpos, int replen),
1835                   void *closure)
1836 {
1837   mbi_iterator_t iter;
1838   long cpos = win->line_starts[line] - win->node->contents;
1839   int delim = 0;
1840   char *endp;
1841   
1842   if (!phys && line + 1 < win->line_count)
1843     endp = win->line_starts[line + 1];
1844   else
1845     endp = win->node->contents + win->node->nodelen;
1846   
1847   for (mbi_init (iter,
1848                  win->line_starts[line], 
1849                  win->node->contents + win->node->nodelen -
1850                    win->line_starts[line]);
1851        !delim && mbi_avail (iter);
1852        mbi_advance (iter))
1853     {
1854       const char *cur_ptr = mbi_cur_ptr (iter);
1855       int cur_len = mb_len (mbi_cur (iter));
1856       int replen;
1857
1858       if (cur_ptr >= endp)
1859         break;
1860       
1861       if (mb_isprint (mbi_cur (iter)))
1862         {
1863           replen = 1;
1864         }
1865       else if (cur_len == 1)
1866         {
1867           if (*cur_ptr == '\r' || *cur_ptr == '\n')
1868             {
1869               replen = 1;
1870               delim = 1;
1871             }
1872           else if (ansi_escape (iter, &cur_len))
1873             {
1874               ITER_SETBYTES (iter, cur_len);
1875               replen = 0;
1876             }
1877           else if (info_tag (iter, 0, &cur_len)) 
1878             {
1879               ITER_SETBYTES (iter, cur_len);
1880               cpos += cur_len;
1881               replen = 0;
1882             }
1883           else
1884             {
1885               printed_representation (cur_ptr, cur_len,
1886                                       win->line_map.used,
1887                                       &replen);
1888             }
1889         }
1890       else
1891         {
1892           /* FIXME: I'm not sure it's the best way to deal with unprintable
1893              multibyte characters */
1894           printed_representation (cur_ptr, cur_len, win->line_map.used,
1895                                   &replen);
1896         }
1897
1898       if (fun)
1899         fun (closure, cpos, replen);
1900       cpos += cur_len;
1901     }
1902   return cpos;
1903 }
1904
1905 static void
1906 add_line_map (void *closure, long cpos, int replen)
1907 {
1908   WINDOW *win = closure;
1909
1910   while (replen--)
1911     line_map_add (&win->line_map, cpos);
1912 }
1913
1914 /* Compute the line map for the current line in WIN. */
1915 void
1916 window_compute_line_map (WINDOW *win)
1917 {
1918   int line = window_line_of_point (win);
1919
1920   if (win->line_map.node == win->node && win->line_map.nline == line
1921       && win->line_map.used)
1922     return;
1923   line_map_init (&win->line_map, win->node, line);
1924   if (win->node)
1925     window_scan_line (win, line, 0, add_line_map, win);
1926 }
1927
1928 /* Return offset of the end of current physical line.
1929  */
1930 long
1931 window_end_of_line (WINDOW *win)
1932 {
1933   int line = window_line_of_point (win);
1934   if (win->node)
1935     return window_scan_line (win, line, 1, NULL, NULL) - 1;
1936   return 0;
1937 }
1938
1939 /* Translate the value of POINT into a column number.  If NP is given
1940    store there the value of point corresponding to the beginning of a
1941    multibyte character in this column.
1942  */
1943 int
1944 window_point_to_column (WINDOW *win, long point, long *np)
1945 {
1946   int i;
1947   
1948   window_compute_line_map (win);
1949   if (!win->line_map.map || point < win->line_map.map[0])
1950     return 0;
1951   for (i = 0; i < win->line_map.used; i++)
1952     if (win->line_map.map[i] > point)
1953       break;
1954   if (np)
1955     *np = win->line_map.map[i-1];
1956   return i - 1;
1957 }
1958