2 Internal file viewer for the Midnight Commander
3 Function for work with coordinate cache (ccache)
5 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
6 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
8 Written by: 1994, 1995, 1998 Miguel de Icaza
9 1994, 1995 Janne Kukonlehto
14 2004 Roland Illig <roland.illig@gmx.de>
15 2005 Roland Illig <roland.illig@gmx.de>
16 2009 Slava Zanko <slavazanko@google.com>
17 2009 Andrew Borodin <aborodin@vmail.ru>
18 2009 Ilia Maslakov <il.smind@gmail.com>
20 This file is part of the Midnight Commander.
22 The Midnight Commander is free software; you can redistribute it
23 and/or modify it under the terms of the GNU General Public License as
24 published by the Free Software Foundation; either version 2 of the
25 License, or (at your option) any later version.
27 The Midnight Commander is distributed in the hope that it will be
28 useful, but WITHOUT ANY WARRANTY; without even the implied warranty
29 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
30 General Public License for more details.
32 You should have received a copy of the GNU General Public License
33 along with this program; if not, write to the Free Software
34 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
39 This cache provides you with a fast lookup to map file offsets into
40 line/column pairs and vice versa. The interface to the mapping is
41 provided by the functions mcview_coord_to_offset() and
42 mcview_offset_to_coord().
44 The cache is implemented as a simple sorted array holding entries
45 that map some of the offsets to their line/column pair. Entries that
46 are not cached themselves are interpolated (exactly) from their
47 neighbor entries. The algorithm used for determining the line/column
48 for a specific offset needs to be kept synchronized with the one used
54 #include "../src/global.h"
55 #include "../src/tty/tty.h"
58 #define VIEW_COORD_CACHE_GRANUL 1024
60 /*** global variables ****************************************************************************/
62 /*** file scope macro definitions ****************************************************************/
64 /*** file scope type declarations ****************************************************************/
66 /*** file scope variables ************************************************************************/
68 /*** file scope functions ************************************************************************/
70 /*** public functions ****************************************************************************/
72 /* --------------------------------------------------------------------------------------------- */
75 mcview_coord_cache_entry_less (const struct coord_cache_entry
*a
,
76 const struct coord_cache_entry
*b
, enum ccache_type crit
,
79 if (crit
== CCACHE_OFFSET
)
80 return (a
->cc_offset
< b
->cc_offset
);
82 if (a
->cc_line
< b
->cc_line
)
85 if (a
->cc_line
== b
->cc_line
) {
87 return (a
->cc_nroff_column
< b
->cc_nroff_column
);
89 return (a
->cc_column
< b
->cc_column
);
95 /* --------------------------------------------------------------------------------------------- */
97 #ifdef MC_ENABLE_DEBUGGING_CODE
100 mcview_ccache_dump (mcview_t
* view
)
103 off_t offset
, line
, column
, nextline_offset
, filesize
;
105 const struct coord_cache_entry
*cache
;
107 assert (view
->coord_cache
!= NULL
);
109 filesize
= mcview_get_filesize (view
);
110 cache
= &(g_array_index (view
->coord_cache
, struct coord_cache_entry
, 0));
112 f
= fopen ("mcview-ccache.out", "w");
115 (void) setvbuf (f
, NULL
, _IONBF
, 0);
118 for (i
= 0; i
< view
->coord_cache
->len
; i
++) {
121 "offset %8" OFFSETTYPE_PRId
" "
122 "line %8" OFFSETTYPE_PRId
" "
123 "column %8" OFFSETTYPE_PRId
" "
124 "nroff_column %8" OFFSETTYPE_PRId
"\n",
125 (unsigned int) i
, cache
[i
].cc_offset
, cache
[i
].cc_line
,
126 cache
[i
].cc_column
, cache
[i
].cc_nroff_column
);
128 (void) fprintf (f
, "\n");
130 /* offset -> line/column translation */
131 for (offset
= 0; offset
< filesize
; offset
++) {
132 mcview_offset_to_coord (view
, &line
, &column
, offset
);
134 "offset %8" OFFSETTYPE_PRId
" "
135 "line %8" OFFSETTYPE_PRId
" "
136 "column %8" OFFSETTYPE_PRId
"\n", offset
, line
, column
);
139 /* line/column -> offset translation */
140 for (line
= 0; TRUE
; line
++) {
141 mcview_coord_to_offset (view
, &nextline_offset
, line
+ 1, 0);
142 (void) fprintf (f
, "nextline_offset %8" OFFSETTYPE_PRId
"\n", nextline_offset
);
144 for (column
= 0; TRUE
; column
++) {
145 mcview_coord_to_offset (view
, &offset
, line
, column
);
146 if (offset
>= nextline_offset
)
150 "line %8" OFFSETTYPE_PRId
" column %8" OFFSETTYPE_PRId
" offset %8"
151 OFFSETTYPE_PRId
"\n", line
, column
, offset
);
154 if (nextline_offset
>= filesize
- 1)
162 /* --------------------------------------------------------------------------------------------- */
164 /* Find and return the index of the last cache entry that is
165 * smaller than ''coord'', according to the criterion ''sort_by''. */
167 mcview_ccache_find (mcview_t
* view
, const struct coord_cache_entry
*cache
,
168 const struct coord_cache_entry
*coord
, enum ccache_type sort_by
)
170 guint base
, i
, limit
;
172 limit
= view
->coord_cache
->len
;
177 i
= base
+ limit
/ 2;
178 if (mcview_coord_cache_entry_less (coord
, &cache
[i
], sort_by
, view
->text_nroff_mode
)) {
179 /* continue the search in the lower half of the cache */
181 /* continue the search in the upper half of the cache */
184 limit
= (limit
+ 1) / 2;
190 /* --------------------------------------------------------------------------------------------- */
192 /* Look up the missing components of ''coord'', which are given by
193 * ''lookup_what''. The function returns the smallest value that
194 * matches the existing components of ''coord''.
197 mcview_ccache_lookup (mcview_t
* view
, struct coord_cache_entry
*coord
,
198 enum ccache_type lookup_what
)
201 struct coord_cache_entry
*cache
, current
, next
, entry
;
202 enum ccache_type sorter
;
210 if (!view
->coord_cache
) {
211 view
->coord_cache
= g_array_new (FALSE
, FALSE
, sizeof (struct coord_cache_entry
));
212 current
.cc_offset
= 0;
214 current
.cc_column
= 0;
215 current
.cc_nroff_column
= 0;
216 g_array_append_val (view
->coord_cache
, current
);
219 sorter
= (lookup_what
== CCACHE_OFFSET
) ? CCACHE_LINECOL
: CCACHE_OFFSET
;
221 tty_enable_interrupt_key ();
224 /* find the two neighbor entries in the cache */
225 cache
= &(g_array_index (view
->coord_cache
, struct coord_cache_entry
, 0));
226 i
= mcview_ccache_find (view
, cache
, coord
, sorter
);
227 /* now i points to the lower neighbor in the cache */
230 if (i
+ 1 < view
->coord_cache
->len
)
231 limit
= cache
[i
+ 1].cc_offset
;
233 limit
= current
.cc_offset
+ VIEW_COORD_CACHE_GRANUL
;
236 nroff_state
= NROFF_START
;
237 for (; current
.cc_offset
< limit
; current
= next
) {
240 if (! mcview_get_byte (view
, current
.cc_offset
, &c
))
243 if (!mcview_coord_cache_entry_less (¤t
, coord
, sorter
, view
->text_nroff_mode
)) {
244 if (lookup_what
== CCACHE_OFFSET
&& view
->text_nroff_mode
&& nroff_state
!= NROFF_START
) {
245 /* don't break here */
251 /* Provide useful default values for ''next'' */
252 next
.cc_offset
= current
.cc_offset
+ 1;
253 next
.cc_line
= current
.cc_line
;
254 next
.cc_column
= current
.cc_column
+ 1;
255 next
.cc_nroff_column
= current
.cc_nroff_column
+ 1;
257 /* and override some of them as necessary. */
259 mcview_get_byte_indexed (view
, current
.cc_offset
, 1, &nextc
);
261 /* Ignore '\r' if it is followed by '\r' or '\n'. If it is
262 * followed by anything else, it is a Mac line ending and
263 * produces a line break.
265 if (nextc
== '\r' || nextc
== '\n') {
266 next
.cc_column
= current
.cc_column
;
267 next
.cc_nroff_column
= current
.cc_nroff_column
;
269 next
.cc_line
= current
.cc_line
+ 1;
271 next
.cc_nroff_column
= 0;
274 } else if (nroff_state
== NROFF_BACKSPACE
) {
275 next
.cc_nroff_column
= current
.cc_nroff_column
- 1;
277 } else if (c
== '\t') {
278 next
.cc_column
= mcview_offset_rounddown (current
.cc_column
, 8) + 8;
279 next
.cc_nroff_column
= mcview_offset_rounddown (current
.cc_nroff_column
, 8) + 8;
281 } else if (c
== '\n') {
282 next
.cc_line
= current
.cc_line
+ 1;
284 next
.cc_nroff_column
= 0;
287 /* Use all default values from above */
290 switch (nroff_state
) {
292 case NROFF_CONTINUATION
:
293 if (mcview_is_nroff_sequence (view
, current
.cc_offset
))
294 nroff_state
= NROFF_BACKSPACE
;
296 nroff_state
= NROFF_START
;
298 case NROFF_BACKSPACE
:
299 nroff_state
= NROFF_CONTINUATION
;
303 /* Cache entries must guarantee that for each i < j,
304 * line[i] <= line[j] and column[i] < column[j]. In the case of
305 * nroff sequences and '\r' characters, this is not guaranteed,
306 * so we cannot save them. */
307 if (nroff_state
== NROFF_START
&& c
!= '\r')
311 if (i
+ 1 == view
->coord_cache
->len
&& entry
.cc_offset
!= cache
[i
].cc_offset
) {
312 g_array_append_val (view
->coord_cache
, entry
);
314 if (!tty_got_interrupt ())
318 tty_disable_interrupt_key ();
320 if (lookup_what
== CCACHE_OFFSET
) {
321 coord
->cc_offset
= current
.cc_offset
;
323 coord
->cc_line
= current
.cc_line
;
324 coord
->cc_column
= current
.cc_column
;
325 coord
->cc_nroff_column
= current
.cc_nroff_column
;
329 /* --------------------------------------------------------------------------------------------- */