Bug Summary

File:/rootdir/_build/../src/ring.cc
Warning:line 685, column 13
Dereference of null pointer

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name ring.cc -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -mframe-pointer=none -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/rootdir/_build -resource-dir /usr/lib/llvm-14/lib/clang/14.0.6 -I src/libbte-2.91.so.0.6500.0.p -I src -I ../src -I . -I .. -I src/bte -I ../src/bte -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/pango-1.0 -I /usr/include/harfbuzz -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/fribidi -I /usr/include/uuid -I /usr/include/cairo -I /usr/include/pixman-1 -I /usr/include/p11-kit-1 -I /usr/include/ctk-3.0 -I /usr/include/gdk-pixbuf-2.0 -I /usr/include/x86_64-linux-gnu -I /usr/include/gio-unix-2.0 -I /usr/include/atk-1.0 -I /usr/include/at-spi2-atk/2.0 -I /usr/include/at-spi-2.0 -I /usr/include/dbus-1.0 -I /usr/lib/x86_64-linux-gnu/dbus-1.0/include -D _FILE_OFFSET_BITS=64 -D G_LOG_DOMAIN="BTE" -D LOCALEDIR="/usr/local/share/locale" -D BTE_DISABLE_DEPRECATION_WARNINGS -D BTE_COMPILATION -U PARSER_INCLUDE_NOP -D CDK_VERSION_MIN_REQUIRED=(G_ENCODE_VERSION(3,18)) -D CDK_VERSION_MAX_ALLOWED=(G_ENCODE_VERSION(3,20)) -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/12/../../../../include/x86_64-linux-gnu/c++/12 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/backward -internal-isystem /usr/lib/llvm-14/lib/clang/14.0.6/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O3 -Wno-address-of-packed-member -Wno-missing-field-initializers -Wno-packed -Wno-switch-enum -Wno-unused-parameter -Wwrite-strings -std=gnu++17 -fdeprecated-macro -fdebug-compilation-dir=/rootdir/_build -ferror-limit 19 -fvisibility hidden -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-checker deadcode.DeadStores -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastSize -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.core.IdenticalExpr -analyzer-checker alpha.core.SizeofPtr -analyzer-checker alpha.security.ArrayBoundV2 -analyzer-checker alpha.security.MallocOverflow -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.NotNullTerminated -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /rootdir/html-report/2022-12-29-232038-10783-1 -x c++ ../src/ring.cc
1/*
2 * Copyright (C) 2002,2009,2010 Red Hat, Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 * Red Hat Author(s): Nalin Dahyabhai, Behdad Esfahbod
19 */
20
21#include "config.h"
22
23#include "debug.h"
24#include "ring.hh"
25#include "bterowdata.hh"
26
27#include <string.h>
28
29/*
30 * Copy the common attributes from BteCellAttr to BteStreamCellAttr or vice versa.
31 */
32static inline void
33_attrcpy (void *dst, void *src)
34{
35 memcpy(dst, src, BTE_CELL_ATTR_COMMON_BYTES12);
36}
37
38using namespace bte::base;
39
40/*
41 * BteRing: A buffer ring
42 */
43
44#ifdef BTE_DEBUG
45void
46Ring::validate()do { } while(0) const
47{
48 _bte_debug_print(BTE_DEBUG_RING,do { } while(0)
49 " Delta = %lu, Length = %lu, Next = %lu, Max = %lu, Writable = %lu.\n",do { } while(0)
50 m_start, m_end - m_start, m_end,do { } while(0)
51 m_max, m_end - m_writable)do { } while(0);
52
53 g_assert_cmpuint(m_start, <=, m_writable)do { guint64 __n1 = (m_start), __n2 = (m_writable); if (__n1 <=
__n2) ; else g_assertion_message_cmpnum ("BTE", "../src/ring.cc"
, 53, ((const char*) (__PRETTY_FUNCTION__)), "m_start" " " "<="
" " "m_writable", (long double) __n1, "<=", (long double)
__n2, 'i'); } while (0)
;
54 g_assert_cmpuint(m_writable, <=, m_end)do { guint64 __n1 = (m_writable), __n2 = (m_end); if (__n1 <=
__n2) ; else g_assertion_message_cmpnum ("BTE", "../src/ring.cc"
, 54, ((const char*) (__PRETTY_FUNCTION__)), "m_writable" " "
"<=" " " "m_end", (long double) __n1, "<=", (long double
) __n2, 'i'); } while (0)
;
55
56 g_assert_cmpuint(m_end - m_start, <=, m_max)do { guint64 __n1 = (m_end - m_start), __n2 = (m_max); if (__n1
<= __n2) ; else g_assertion_message_cmpnum ("BTE", "../src/ring.cc"
, 56, ((const char*) (__PRETTY_FUNCTION__)), "m_end - m_start"
" " "<=" " " "m_max", (long double) __n1, "<=", (long double
) __n2, 'i'); } while (0)
;
57 g_assert_cmpuint(m_end - m_writable, <=, m_mask)do { guint64 __n1 = (m_end - m_writable), __n2 = (m_mask); if
(__n1 <= __n2) ; else g_assertion_message_cmpnum ("BTE", "../src/ring.cc"
, 57, ((const char*) (__PRETTY_FUNCTION__)), "m_end - m_writable"
" " "<=" " " "m_mask", (long double) __n1, "<=", (long
double) __n2, 'i'); } while (0)
;
58}
59#else
60#define validate(...)do { } while(0) do { } while(0)
61#endif
62
63Ring::Ring(row_t max_rows,
64 bool has_streams)
65 : m_max{MAX(max_rows, 3)(((max_rows) > (3)) ? (max_rows) : (3))},
66 m_has_streams{has_streams},
67 m_last_attr{basic_cell.attr}
68{
69 _bte_debug_print(BTE_DEBUG_RING, "New ring %p.\n", this)do { } while(0);
70
71 m_array = (BteRowData* ) g_malloc0 (sizeof (m_array[0]) * (m_mask + 1));
72
73 if (has_streams) {
74 m_attr_stream = _bte_file_stream_new ();
75 m_text_stream = _bte_file_stream_new ();
76 m_row_stream = _bte_file_stream_new ();
77 } else {
78 m_attr_stream = m_text_stream = m_row_stream = nullptr;
79 }
80
81 m_utf8_buffer = g_string_sized_new (128);
82
83 _bte_row_data_init (&m_cached_row);
84
85 m_hyperlinks = g_ptr_array_new();
86 auto empty_str = g_string_new_len("", 0);
87 g_ptr_array_add(m_hyperlinks, empty_str);
88
89 validate()do { } while(0);
90}
91
92Ring::~Ring()
93{
94 for (size_t i = 0; i <= m_mask; i++)
95 _bte_row_data_fini (&m_array[i]);
96
97 g_free (m_array);
98
99 if (m_has_streams) {
100 g_object_unref (m_attr_stream);
101 g_object_unref (m_text_stream);
102 g_object_unref (m_row_stream);
103 }
104
105 g_string_free (m_utf8_buffer, TRUE(!(0)));
106
107 for (size_t i = 0; i < m_hyperlinks->len; i++)
108 g_string_free (hyperlink_get(i), TRUE(!(0)));
109 g_ptr_array_free (m_hyperlinks, TRUE(!(0)));
110
111 _bte_row_data_fini(&m_cached_row);
112}
113
114#define SET_BIT(buf, n)buf[(n) / 8] |= (1 << ((n) % 8)) buf[(n) / 8] |= (1 << ((n) % 8))
115#define GET_BIT(buf, n)((buf[(n) / 8] >> ((n) % 8)) & 1) ((buf[(n) / 8] >> ((n) % 8)) & 1)
116
117/*
118 * Do a round of garbage collection. Hyperlinks that no longer occur in the ring are wiped out.
119 */
120void
121Ring::hyperlink_gc()
122{
123 row_t i, j;
124 hyperlink_idx_t idx;
125 BteRowData* row;
126 char *used;
127
128 _bte_debug_print (BTE_DEBUG_HYPERLINK,do { } while(0)
129 "hyperlink: GC starting (highest used idx is %d)\n",do { } while(0)
130 m_hyperlink_highest_used_idx)do { } while(0);
131
132 m_hyperlink_maybe_gc_counter = 0;
133
134 if (m_hyperlink_highest_used_idx == 0) {
135 _bte_debug_print (BTE_DEBUG_HYPERLINK,do { } while(0)
136 "hyperlink: GC done (no links at all, nothing to do)\n")do { } while(0);
137 return;
138 }
139
140 /* One bit for each idx to see if it's used. */
141 used = (char *) g_malloc0 (m_hyperlink_highest_used_idx / 8 + 1);
142
143 /* A few special values not to be garbage collected. */
144 SET_BIT(used, m_hyperlink_current_idx)used[(m_hyperlink_current_idx) / 8] |= (1 << ((m_hyperlink_current_idx
) % 8))
;
145 SET_BIT(used, m_hyperlink_hover_idx)used[(m_hyperlink_hover_idx) / 8] |= (1 << ((m_hyperlink_hover_idx
) % 8))
;
146 SET_BIT(used, m_last_attr.hyperlink_idx)used[(m_last_attr.hyperlink_idx) / 8] |= (1 << ((m_last_attr
.hyperlink_idx) % 8))
;
147
148 for (i = m_writable; i < m_end; i++) {
149 row = get_writable_index(i);
150 for (j = 0; j < row->len; j++) {
151 idx = row->cells[j].attr.hyperlink_idx;
152 SET_BIT(used, idx)used[(idx) / 8] |= (1 << ((idx) % 8));
153 }
154 }
155
156 for (idx = 1; idx <= m_hyperlink_highest_used_idx; idx++) {
157 if (!GET_BIT(used, idx)((used[(idx) / 8] >> ((idx) % 8)) & 1) && hyperlink_get(idx)->len != 0) {
158 _bte_debug_print (BTE_DEBUG_HYPERLINK,do { } while(0)
159 "hyperlink: GC purging link %d to id;uri=\"%s\"\n",do { } while(0)
160 idx, hyperlink_get(idx)->str)do { } while(0);
161 /* Wipe out the ID and URI itself so it doesn't linger on in the memory for a long time */
162 memset(hyperlink_get(idx)->str, 0, hyperlink_get(idx)->len);
163 g_string_truncate (hyperlink_get(idx), 0);
164 }
165 }
166
167 while (m_hyperlink_highest_used_idx >= 1 && hyperlink_get(m_hyperlink_highest_used_idx)->len == 0) {
168 m_hyperlink_highest_used_idx--;
169 }
170
171 _bte_debug_print (BTE_DEBUG_HYPERLINK,do { } while(0)
172 "hyperlink: GC done (highest used idx is now %d)\n",do { } while(0)
173 m_hyperlink_highest_used_idx)do { } while(0);
174
175 g_free (used);
176}
177
178/*
179 * Cumulate the given value, and do a GC when 65536 is reached.
180 */
181void
182Ring::hyperlink_maybe_gc(row_t increment)
183{
184 m_hyperlink_maybe_gc_counter += increment;
185
186 _bte_debug_print (BTE_DEBUG_HYPERLINK,do { } while(0)
187 "hyperlink: maybe GC, counter at %ld\n",do { } while(0)
188 m_hyperlink_maybe_gc_counter)do { } while(0);
189
190 if (m_hyperlink_maybe_gc_counter >= 65536)
191 hyperlink_gc();
192}
193
194/*
195 * Find existing idx for the hyperlink or allocate a new one.
196 *
197 * Returns 0 if given no hyperlink or an empty one, or if the pool is full.
198 * Returns the idx (either already existing or newly allocated) from 1 up to
199 * BTE_HYPERLINK_COUNT_MAX inclusive otherwise.
200 *
201 * FIXME do something more effective than a linear search
202 */
203Ring::hyperlink_idx_t
204Ring::get_hyperlink_idx_no_update_current(char const* hyperlink)
205{
206 hyperlink_idx_t idx;
207 gsize len;
208 GString *str;
209
210 if (!hyperlink || !hyperlink[0])
13
Assuming 'hyperlink' is null
211 return 0;
212
213 len = strlen(hyperlink);
214
215 /* Linear search for this particular URI */
216 auto const last_idx = m_hyperlink_highest_used_idx + 1;
217 for (idx = 1; idx < last_idx; ++idx) {
218 if (strcmp(hyperlink_get(idx)->str, hyperlink) == 0) {
219 _bte_debug_print (BTE_DEBUG_HYPERLINK,do { } while(0)
220 "get_hyperlink_idx: already existing idx %d for id;uri=\"%s\"\n",do { } while(0)
221 idx, hyperlink)do { } while(0);
222 return idx;
223 }
224 }
225
226 /* FIXME it's the second time we're GCing if coming from get_hyperlink_idx */
227 hyperlink_gc();
228
229 /* Another linear search for an empty slot where a GString is already allocated */
230 for (idx = 1; idx < m_hyperlinks->len; idx++) {
231 if (hyperlink_get(idx)->len == 0) {
232 _bte_debug_print (BTE_DEBUG_HYPERLINK,do { } while(0)
233 "get_hyperlink_idx: reassigning old idx %d for id;uri=\"%s\"\n",do { } while(0)
234 idx, hyperlink)do { } while(0);
235 /* Grow size if required, however, never shrink to avoid long-term memory fragmentation. */
236 g_string_append_len (hyperlink_get(idx), hyperlink, len);
237 m_hyperlink_highest_used_idx = MAX (m_hyperlink_highest_used_idx, idx)(((m_hyperlink_highest_used_idx) > (idx)) ? (m_hyperlink_highest_used_idx
) : (idx))
;
238 return idx;
239 }
240 }
241
242 /* All allocated slots are in use. Gotta allocate a new one */
243 g_assert_cmpuint(m_hyperlink_highest_used_idx + 1, ==, m_hyperlinks->len)do { guint64 __n1 = (m_hyperlink_highest_used_idx + 1), __n2 =
(m_hyperlinks->len); if (__n1 == __n2) ; else g_assertion_message_cmpnum
("BTE", "../src/ring.cc", 243, ((const char*) (__PRETTY_FUNCTION__
)), "m_hyperlink_highest_used_idx + 1" " " "==" " " "m_hyperlinks->len"
, (long double) __n1, "==", (long double) __n2, 'i'); } while
(0)
;
244
245 /* BTE_HYPERLINK_COUNT_MAX should be big enough for this not to happen under
246 normal circumstances. Anyway, it's cheap to protect against extreme ones. */
247 if (m_hyperlink_highest_used_idx == BTE_HYPERLINK_COUNT_MAX((1 << 20) - 2)) {
248 _bte_debug_print (BTE_DEBUG_HYPERLINK,do { } while(0)
249 "get_hyperlink_idx: idx 0 (ran out of available idxs) for id;uri=\"%s\"\n",do { } while(0)
250 hyperlink)do { } while(0);
251 return 0;
252 }
253
254 idx = ++m_hyperlink_highest_used_idx;
255 _bte_debug_print (BTE_DEBUG_HYPERLINK,do { } while(0)
256 "get_hyperlink_idx: brand new idx %d for id;uri=\"%s\"\n",do { } while(0)
257 idx, hyperlink)do { } while(0);
258 str = g_string_new_len (hyperlink, len);
259 g_ptr_array_add(m_hyperlinks, str);
260
261 g_assert_cmpuint(m_hyperlink_highest_used_idx + 1, ==, m_hyperlinks->len)do { guint64 __n1 = (m_hyperlink_highest_used_idx + 1), __n2 =
(m_hyperlinks->len); if (__n1 == __n2) ; else g_assertion_message_cmpnum
("BTE", "../src/ring.cc", 261, ((const char*) (__PRETTY_FUNCTION__
)), "m_hyperlink_highest_used_idx + 1" " " "==" " " "m_hyperlinks->len"
, (long double) __n1, "==", (long double) __n2, 'i'); } while
(0)
;
262
263 return idx;
264}
265
266/*
267 * Find existing idx for the hyperlink or allocate a new one.
268 *
269 * Returns 0 if given no hyperlink or an empty one, or if the pool is full.
270 * Returns the idx (either already existing or newly allocated) from 1 up to
271 * BTE_HYPERLINK_COUNT_MAX inclusive otherwise.
272 *
273 * The current idx is also updated, in order not to be garbage collected.
274 */
275Ring::hyperlink_idx_t
276Ring::get_hyperlink_idx(char const* hyperlink)
277{
278 /* Release current idx and do a round of GC to possibly purge its hyperlink,
279 * even if new hyperlink is nullptr or empty. */
280 m_hyperlink_current_idx = 0;
281 hyperlink_gc();
282
283 m_hyperlink_current_idx = get_hyperlink_idx_no_update_current(hyperlink);
284 return m_hyperlink_current_idx;
285}
286
287void
288Ring::freeze_row(row_t position,
289 BteRowData const* row)
290{
291 BteCell *cell;
292 GString *buffer = m_utf8_buffer;
293 GString *hyperlink;
294 int i;
295 gboolean froze_hyperlink = FALSE(0);
296
297 _bte_debug_print (BTE_DEBUG_RING, "Freezing row %lu.\n", position)do { } while(0);
298
299 g_assert(m_has_streams)do { if (__builtin_expect (__extension__ ({ int _g_boolean_var_
; if (m_has_streams) _g_boolean_var_ = 1; else _g_boolean_var_
= 0; _g_boolean_var_; }), 1)) ; else g_assertion_message_expr
("BTE", "../src/ring.cc", 299, ((const char*) (__PRETTY_FUNCTION__
)), "m_has_streams"); } while (0)
;
300
301 RowRecord record;
302 memset(&record, 0, sizeof(record));
303 record.text_start_offset = _bte_stream_head(m_text_stream);
304 record.attr_start_offset = _bte_stream_head(m_attr_stream);
305 record.is_ascii = 1;
306
307 g_string_set_size (buffer, 0);
308 for (i = 0, cell = row->cells; i < row->len; i++, cell++) {
309 BteCellAttr attr;
310 int num_chars;
311
312 /* Attr storage:
313 *
314 * 1. We don't store attrs for fragments. They can be
315 * reconstructed using the columns of their start cell.
316 *
317 * 2. We store one attr per bteunistr character starting
318 * from the second character, with columns=0.
319 *
320 * That's enough to reconstruct the attrs, and to store
321 * the text in real UTF-8.
322 */
323 attr = cell->attr;
324 if (G_LIKELY (!attr.fragment())(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
!attr.fragment()) _g_boolean_var_ = 1; else _g_boolean_var_ =
0; _g_boolean_var_; }), 1))
) {
325 CellAttrChange attr_change;
326 guint16 hyperlink_length;
327
328 if (memcmp(&m_last_attr, &attr, sizeof (BteCellAttr)) != 0) {
329 m_last_attr_text_start_offset = record.text_start_offset + buffer->len;
330 memset(&attr_change, 0, sizeof (attr_change));
331 attr_change.text_end_offset = m_last_attr_text_start_offset;
332 _attrcpy(&attr_change.attr, &m_last_attr);
333 hyperlink = hyperlink_get(m_last_attr.hyperlink_idx);
334 attr_change.attr.hyperlink_length = hyperlink->len;
335 _bte_stream_append (m_attr_stream, (char const* ) &attr_change, sizeof (attr_change));
336 if (G_UNLIKELY (hyperlink->len != 0)(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
hyperlink->len != 0) _g_boolean_var_ = 1; else _g_boolean_var_
= 0; _g_boolean_var_; }), 0))
) {
337 _bte_stream_append (m_attr_stream, hyperlink->str, hyperlink->len);
338 froze_hyperlink = TRUE(!(0));
339 }
340 hyperlink_length = attr_change.attr.hyperlink_length;
341 _bte_stream_append (m_attr_stream, (char const* ) &hyperlink_length, 2);
342 if (!buffer->len)
343 /* This row doesn't use last_attr, adjust */
344 record.attr_start_offset += sizeof (attr_change) + hyperlink_length + 2;
345 m_last_attr = attr;
346 }
347
348 num_chars = _bte_unistr_strlen (cell->c);
349 if (num_chars > 1) {
350 /* Combining chars */
351 attr.set_columns(0);
352 m_last_attr_text_start_offset = record.text_start_offset + buffer->len
353 + g_unichar_to_utf8 (_bte_unistr_get_base (cell->c), nullptr);
354 memset(&attr_change, 0, sizeof (attr_change));
355 attr_change.text_end_offset = m_last_attr_text_start_offset;
356 _attrcpy(&attr_change.attr, &m_last_attr);
357 hyperlink = hyperlink_get(m_last_attr.hyperlink_idx);
358 attr_change.attr.hyperlink_length = hyperlink->len;
359 _bte_stream_append (m_attr_stream, (char const* ) &attr_change, sizeof (attr_change));
360 if (G_UNLIKELY (hyperlink->len != 0)(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
hyperlink->len != 0) _g_boolean_var_ = 1; else _g_boolean_var_
= 0; _g_boolean_var_; }), 0))
) {
361 _bte_stream_append (m_attr_stream, hyperlink->str, hyperlink->len);
362 froze_hyperlink = TRUE(!(0));
363 }
364 hyperlink_length = attr_change.attr.hyperlink_length;
365 _bte_stream_append (m_attr_stream, (char const* ) &hyperlink_length, 2);
366 m_last_attr = attr;
367 }
368
369 if (cell->c < 32 || cell->c > 126) record.is_ascii = 0;
370 _bte_unistr_append_to_string (cell->c, buffer);
371 }
372 }
373 if (!row->attr.soft_wrapped)
374 g_string_append_c (buffer, '\n')g_string_append_c_inline (buffer, '\n');
375 record.soft_wrapped = row->attr.soft_wrapped;
376 record.bidi_flags = row->attr.bidi_flags;
377
378 _bte_stream_append(m_text_stream, buffer->str, buffer->len);
379 append_row_record(&record, position);
380
381 /* After freezing some hyperlinks, do a hyperlink GC. The constant is totally arbitrary, feel free to fine tune. */
382 if (froze_hyperlink)
383 hyperlink_maybe_gc(1024);
384}
385
386/* If do_truncate (data is placed back from the stream to the ring), real new hyperlink idxs are looked up or allocated.
387 *
388 * If !do_truncate (data is fetched only to be displayed), hyperlinked cells are given the pseudo idx BTE_HYPERLINK_IDX_TARGET_IN_STREAM,
389 * except for the hyperlink_hover_idx which gets this real idx. This is important for hover underlining.
390 *
391 * Optionally updates the hyperlink parameter to point to the ring-owned hyperlink target. */
392void
393Ring::thaw_row(row_t position,
394 BteRowData* row,
395 bool do_truncate,
396 int hyperlink_column,
397 char const** hyperlink)
398{
399 RowRecord records[2], record;
400 BteCellAttr attr;
401 CellAttrChange attr_change;
402 BteCell cell;
403 char const* p, *q, *end;
404 GString *buffer = m_utf8_buffer;
405 char hyperlink_readbuf[BTE_HYPERLINK_TOTAL_LENGTH_MAX(250 + 1 + 2083) + 1];
406
407 hyperlink_readbuf[0] = '\0';
408 if (hyperlink) {
409 m_hyperlink_buf[0] = '\0';
410 *hyperlink = m_hyperlink_buf;
411 }
412
413 _bte_debug_print (BTE_DEBUG_RING, "Thawing row %lu.\n", position)do { } while(0);
414
415 g_assert(m_has_streams)do { if (__builtin_expect (__extension__ ({ int _g_boolean_var_
; if (m_has_streams) _g_boolean_var_ = 1; else _g_boolean_var_
= 0; _g_boolean_var_; }), 1)) ; else g_assertion_message_expr
("BTE", "../src/ring.cc", 415, ((const char*) (__PRETTY_FUNCTION__
)), "m_has_streams"); } while (0)
;
416
417 _bte_row_data_clear (row);
418
419 attr_change.text_end_offset = 0;
420
421 if (!read_row_record(&records[0], position))
422 return;
423 if ((position + 1) * sizeof (records[0]) < _bte_stream_head (m_row_stream)) {
424 if (!read_row_record(&records[1], position + 1))
425 return;
426 } else
427 records[1].text_start_offset = _bte_stream_head (m_text_stream);
428
429 g_string_set_size (buffer, records[1].text_start_offset - records[0].text_start_offset);
430 if (!_bte_stream_read (m_text_stream, records[0].text_start_offset, buffer->str, buffer->len))
431 return;
432
433 record = records[0];
434
435 if (G_LIKELY (buffer->len && buffer->str[buffer->len - 1] == '\n')(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
buffer->len && buffer->str[buffer->len - 1] ==
'\n') _g_boolean_var_ = 1; else _g_boolean_var_ = 0; _g_boolean_var_
; }), 1))
)
436 g_string_truncate (buffer, buffer->len - 1);
437 else
438 row->attr.soft_wrapped = TRUE(!(0));
439 row->attr.bidi_flags = records[0].bidi_flags;
440
441 p = buffer->str;
442 end = p + buffer->len;
443 while (p < end) {
444 if (record.text_start_offset >= m_last_attr_text_start_offset) {
445 attr = m_last_attr;
446 strcpy(hyperlink_readbuf, hyperlink_get(attr.hyperlink_idx)->str);
447 } else {
448 if (record.text_start_offset >= attr_change.text_end_offset) {
449 if (!_bte_stream_read (m_attr_stream, record.attr_start_offset, (char *) &attr_change, sizeof (attr_change)))
450 return;
451 record.attr_start_offset += sizeof (attr_change);
452 g_assert_cmpuint (attr_change.attr.hyperlink_length, <=, BTE_HYPERLINK_TOTAL_LENGTH_MAX)do { guint64 __n1 = (attr_change.attr.hyperlink_length), __n2
= ((250 + 1 + 2083)); if (__n1 <= __n2) ; else g_assertion_message_cmpnum
("BTE", "../src/ring.cc", 452, ((const char*) (__PRETTY_FUNCTION__
)), "attr_change.attr.hyperlink_length" " " "<=" " " "BTE_HYPERLINK_TOTAL_LENGTH_MAX"
, (long double) __n1, "<=", (long double) __n2, 'i'); } while
(0)
;
453 if (attr_change.attr.hyperlink_length && !_bte_stream_read (m_attr_stream, record.attr_start_offset, hyperlink_readbuf, attr_change.attr.hyperlink_length))
454 return;
455 hyperlink_readbuf[attr_change.attr.hyperlink_length] = '\0';
456 record.attr_start_offset += attr_change.attr.hyperlink_length + 2;
457
458 _attrcpy(&attr, &attr_change.attr);
459 attr.hyperlink_idx = 0;
460 if (G_UNLIKELY (attr_change.attr.hyperlink_length)(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
attr_change.attr.hyperlink_length) _g_boolean_var_ = 1; else _g_boolean_var_
= 0; _g_boolean_var_; }), 0))
) {
461 if (do_truncate) {
462 /* Find the existing idx or allocate a new one, just as when receiving an OSC 8 escape sequence.
463 * Do not update the current idx though. */
464 attr.hyperlink_idx = get_hyperlink_idx_no_update_current(hyperlink_readbuf);
465 } else {
466 /* Use a special hyperlink idx, except if to be underlined because the hyperlink is the same as the hovered cell's. */
467 attr.hyperlink_idx = BTE_HYPERLINK_IDX_TARGET_IN_STREAM(((1 << 20) - 2) + 1);
468 if (m_hyperlink_hover_idx != 0 && strcmp(hyperlink_readbuf, hyperlink_get(m_hyperlink_hover_idx)->str) == 0) {
469 /* FIXME here we're calling the expensive strcmp() above and get_hyperlink_idx_no_update_current() way too many times. */
470 attr.hyperlink_idx = get_hyperlink_idx_no_update_current(hyperlink_readbuf);
471 }
472 }
473 }
474 }
475 }
476
477 cell.attr = attr;
478 _BTE_DEBUG_IF(BTE_DEBUG_RING | BTE_DEBUG_HYPERLINK)if (0) {
479 /* Debug: Reverse the colors for the stream's contents. */
480 if (!do_truncate) {
481 cell.attr.attr ^= BTE_ATTR_REVERSE(1U << ((((((((0) + (4)) + (1)) + (1)) + (1)) + (2)) + (
1)) + (1)))
;
482 }
483 }
484 cell.c = g_utf8_get_char (p);
485
486 q = g_utf8_next_char (p)(char *)((p) + g_utf8_skip[*(const guchar *)(p)]);
487 record.text_start_offset += q - p;
488 p = q;
489
490 if (G_UNLIKELY (cell.attr.columns() == 0)(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
cell.attr.columns() == 0) _g_boolean_var_ = 1; else _g_boolean_var_
= 0; _g_boolean_var_; }), 0))
) {
491 if (G_LIKELY (row->len)(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
row->len) _g_boolean_var_ = 1; else _g_boolean_var_ = 0; _g_boolean_var_
; }), 1))
) {
492 /* Combine it */
493 row->cells[row->len - 1].c = _bte_unistr_append_unichar (row->cells[row->len - 1].c, cell.c);
494 /* Spread it to all the previous cells of a potentially multicell character */
495 for (int i = row->len - 1; i >= 1 && row->cells[i].attr.fragment(); i--) {
496 row->cells[i - 1].c = row->cells[i].c;
497 }
498 } else {
499 cell.attr.set_columns(1);
500 if (row->len == hyperlink_column && hyperlink != nullptr)
501 *hyperlink = strcpy(m_hyperlink_buf, hyperlink_readbuf);
502 _bte_row_data_append (row, &cell);
503 }
504 } else {
505 if (row->len == hyperlink_column && hyperlink != nullptr)
506 *hyperlink = strcpy(m_hyperlink_buf, hyperlink_readbuf);
507 _bte_row_data_append (row, &cell);
508 if (cell.attr.columns() > 1) {
509 /* Add the fragments */
510 int i, columns = cell.attr.columns();
511 cell.attr.set_fragment(true);
512 cell.attr.set_columns(1);
513 for (i = 1; i < columns; i++) {
514 if (row->len == hyperlink_column && hyperlink != nullptr)
515 *hyperlink = strcpy(m_hyperlink_buf, hyperlink_readbuf);
516 _bte_row_data_append (row, &cell);
517 }
518 }
519 }
520 }
521
522 /* FIXME this is extremely complicated (by design), figure out something better.
523 This is the only place where we need to walk backwards in attr_stream,
524 which is the reason for the hyperlink's length being repeated after the hyperlink itself. */
525 if (do_truncate) {
526 gsize attr_stream_truncate_at = records[0].attr_start_offset;
527 _bte_debug_print (BTE_DEBUG_RING, "Truncating\n")do { } while(0);
528 if (records[0].text_start_offset <= m_last_attr_text_start_offset) {
529 /* Check the previous attr record. If its text ends where truncating, this attr record also needs to be removed. */
530 guint16 hyperlink_length;
531 if (_bte_stream_read (m_attr_stream, attr_stream_truncate_at - 2, (char *) &hyperlink_length, 2)) {
532 g_assert_cmpuint (hyperlink_length, <=, BTE_HYPERLINK_TOTAL_LENGTH_MAX)do { guint64 __n1 = (hyperlink_length), __n2 = ((250 + 1 + 2083
)); if (__n1 <= __n2) ; else g_assertion_message_cmpnum ("BTE"
, "../src/ring.cc", 532, ((const char*) (__PRETTY_FUNCTION__)
), "hyperlink_length" " " "<=" " " "BTE_HYPERLINK_TOTAL_LENGTH_MAX"
, (long double) __n1, "<=", (long double) __n2, 'i'); } while
(0)
;
533 if (_bte_stream_read (m_attr_stream, attr_stream_truncate_at - 2 - hyperlink_length - sizeof (attr_change), (char *) &attr_change, sizeof (attr_change))) {
534 if (records[0].text_start_offset == attr_change.text_end_offset) {
535 _bte_debug_print (BTE_DEBUG_RING, "... at attribute change\n")do { } while(0);
536 attr_stream_truncate_at -= sizeof (attr_change) + hyperlink_length + 2;
537 }
538 }
539 }
540 /* Reconstruct last_attr from the first record of attr_stream that we cut off,
541 last_attr_text_start_offset from the last record that we keep. */
542 if (_bte_stream_read (m_attr_stream, attr_stream_truncate_at, (char *) &attr_change, sizeof (attr_change))) {
543 _attrcpy(&m_last_attr, &attr_change.attr);
544 m_last_attr.hyperlink_idx = 0;
545 if (attr_change.attr.hyperlink_length && _bte_stream_read (m_attr_stream, attr_stream_truncate_at + sizeof (attr_change), (char *) &hyperlink_readbuf, attr_change.attr.hyperlink_length)) {
546 hyperlink_readbuf[attr_change.attr.hyperlink_length] = '\0';
547 m_last_attr.hyperlink_idx = get_hyperlink_idx(hyperlink_readbuf);
548 }
549 if (_bte_stream_read (m_attr_stream, attr_stream_truncate_at - 2, (char *) &hyperlink_length, 2)) {
550 g_assert_cmpuint (hyperlink_length, <=, BTE_HYPERLINK_TOTAL_LENGTH_MAX)do { guint64 __n1 = (hyperlink_length), __n2 = ((250 + 1 + 2083
)); if (__n1 <= __n2) ; else g_assertion_message_cmpnum ("BTE"
, "../src/ring.cc", 550, ((const char*) (__PRETTY_FUNCTION__)
), "hyperlink_length" " " "<=" " " "BTE_HYPERLINK_TOTAL_LENGTH_MAX"
, (long double) __n1, "<=", (long double) __n2, 'i'); } while
(0)
;
551 if (_bte_stream_read (m_attr_stream, attr_stream_truncate_at - 2 - hyperlink_length - sizeof (attr_change), (char *) &attr_change, sizeof (attr_change))) {
552 m_last_attr_text_start_offset = attr_change.text_end_offset;
553 } else {
554 m_last_attr_text_start_offset = 0;
555 }
556 } else {
557 m_last_attr_text_start_offset = 0;
558 }
559 } else {
560 m_last_attr_text_start_offset = 0;
561 m_last_attr = basic_cell.attr;
562 }
563 }
564 _bte_stream_truncate (m_row_stream, position * sizeof (record));
565 _bte_stream_truncate (m_attr_stream, attr_stream_truncate_at);
566 _bte_stream_truncate (m_text_stream, records[0].text_start_offset);
567 }
568}
569
570void
571Ring::reset_streams(row_t position)
572{
573 _bte_debug_print (BTE_DEBUG_RING, "Reseting streams to %lu.\n", position)do { } while(0);
574
575 if (m_has_streams) {
576 _bte_stream_reset(m_row_stream, position * sizeof(RowRecord));
577 _bte_stream_reset(m_text_stream, _bte_stream_head(m_text_stream));
578 _bte_stream_reset(m_attr_stream, _bte_stream_head(m_attr_stream));
579 }
580
581 m_last_attr_text_start_offset = 0;
582 m_last_attr = basic_cell.attr;
583}
584
585Ring::row_t
586Ring::reset()
587{
588 _bte_debug_print (BTE_DEBUG_RING, "Reseting the ring at %lu.\n", m_end)do { } while(0);
589
590 reset_streams(m_end);
591 m_start = m_writable = m_end;
592 m_cached_row_num = (row_t)-1;
593
594 return m_end;
595}
596
597BteRowData const*
598Ring::index(row_t position)
599{
600 if (G_LIKELY (position >= m_writable)(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
position >= m_writable) _g_boolean_var_ = 1; else _g_boolean_var_
= 0; _g_boolean_var_; }), 1))
)
601 return get_writable_index(position);
602
603 if (m_cached_row_num != position) {
604 _bte_debug_print(BTE_DEBUG_RING, "Caching row %lu.\n", position)do { } while(0);
605 thaw_row(position, &m_cached_row, false, -1, nullptr);
606 m_cached_row_num = position;
607 }
608
609 return &m_cached_row;
610}
611
612bool
613Ring::is_soft_wrapped(row_t position)
614{
615 const BteRowData *row;
616 RowRecord record;
617
618 if (G_UNLIKELY (position < m_start || position >= m_end)(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
position < m_start || position >= m_end) _g_boolean_var_
= 1; else _g_boolean_var_ = 0; _g_boolean_var_; }), 0))
)
619 return false;
620
621 if (G_LIKELY (position >= m_writable)(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
position >= m_writable) _g_boolean_var_ = 1; else _g_boolean_var_
= 0; _g_boolean_var_; }), 1))
) {
622 row = get_writable_index(position);
623 return row->attr.soft_wrapped;
624 }
625
626 /* The row is scrolled out to the stream. Save work by not reading the actual row.
627 * The requested information is readily available in row_stream, too. */
628 if (G_UNLIKELY (!read_row_record(&record, position))(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
!read_row_record(&record, position)) _g_boolean_var_ = 1;
else _g_boolean_var_ = 0; _g_boolean_var_; }), 0))
)
629 return false;
630 return record.soft_wrapped;
631}
632
633/*
634 * Returns the hyperlink idx at the given position.
635 *
636 * Updates the hyperlink parameter to point to the hyperlink's target.
637 * The buffer is owned by the ring and must not be modified by the caller.
638 *
639 * Optionally also updates the internal concept of the hovered idx. In this case,
640 * a real idx is looked up or newly allocated in the hyperlink pool even if the
641 * cell is scrolled out to the streams.
642 * This is to be able to underline all cells that share the same hyperlink.
643 *
644 * Otherwise cells from the stream might get the pseudo idx BTE_HYPERLINK_IDX_TARGET_IN_STREAM.
645 */
646Ring::hyperlink_idx_t
647Ring::get_hyperlink_at_position(row_t position,
648 column_t col,
649 bool update_hover_idx,
650 char const** hyperlink)
651{
652 hyperlink_idx_t idx;
653 char const* hp;
654
655 if (hyperlink == nullptr)
1
Assuming the condition is false
2
Taking false branch
656 hyperlink = &hp;
657 *hyperlink = nullptr;
658
659 if (update_hover_idx) {
3
Assuming 'update_hover_idx' is false
4
Taking false branch
660 /* Invalidate the cache because new hover idx might result in new idxs to report. */
661 m_cached_row_num = (row_t)-1;
662 }
663
664 if (G_UNLIKELY (!contains(position) || col < 0)(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
!contains(position) || col < 0) _g_boolean_var_ = 1; else _g_boolean_var_
= 0; _g_boolean_var_; }), 0))
) {
5
Assuming 'col' is >= 0
6
Taking false branch
7
Taking false branch
665 if (update_hover_idx)
666 m_hyperlink_hover_idx = 0;
667 return 0;
668 }
669
670 if (G_LIKELY (position >= m_writable)(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
position >= m_writable) _g_boolean_var_ = 1; else _g_boolean_var_
= 0; _g_boolean_var_; }), 1))
) {
8
Assuming 'position' is < field 'm_writable'
9
Taking false branch
10
Taking false branch
671 BteRowData* row = get_writable_index(position);
672 if (col >= _bte_row_data_length(row)((row)->len + 0)) {
673 if (update_hover_idx)
674 m_hyperlink_hover_idx = 0;
675 return 0;
676 }
677 *hyperlink = hyperlink_get(row->cells[col].attr.hyperlink_idx)->str;
678 idx = row->cells[col].attr.hyperlink_idx;
679 } else {
680 thaw_row(position, &m_cached_row, false, col, hyperlink);
11
Assigning value
681 /* Note: Intentionally don't set cached_row_num. We're about to update
682 * m_hyperlink_hover_idx which makes some idxs no longer valid. */
683 idx = get_hyperlink_idx_no_update_current(*hyperlink);
12
Calling 'Ring::get_hyperlink_idx_no_update_current'
14
Returning from 'Ring::get_hyperlink_idx_no_update_current'
684 }
685 if (**hyperlink == '\0')
15
Dereference of null pointer
686 *hyperlink = nullptr;
687 if (update_hover_idx)
688 m_hyperlink_hover_idx = idx;
689 return idx;
690}
691
692BteRowData*
693Ring::index_writable(row_t position)
694{
695 ensure_writable(position);
696 return get_writable_index(position);
697}
698
699void
700Ring::freeze_one_row()
701{
702 BteRowData* row;
703
704 if (G_UNLIKELY (m_writable == m_start)(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
m_writable == m_start) _g_boolean_var_ = 1; else _g_boolean_var_
= 0; _g_boolean_var_; }), 0))
)
705 reset_streams(m_writable);
706
707 row = get_writable_index(m_writable);
708 freeze_row(m_writable, row);
709
710 m_writable++;
711}
712
713void
714Ring::thaw_one_row()
715{
716 BteRowData* row;
717
718 g_assert_cmpuint(m_start, <, m_writable)do { guint64 __n1 = (m_start), __n2 = (m_writable); if (__n1 <
__n2) ; else g_assertion_message_cmpnum ("BTE", "../src/ring.cc"
, 718, ((const char*) (__PRETTY_FUNCTION__)), "m_start" " " "<"
" " "m_writable", (long double) __n1, "<", (long double) __n2
, 'i'); } while (0)
;
719
720 ensure_writable_room();
721
722 m_writable--;
723
724 if (m_writable == m_cached_row_num)
725 m_cached_row_num = (row_t)-1; /* Invalidate cached row */
726
727 row = get_writable_index(m_writable);
728 thaw_row(m_writable, row, true, -1, nullptr);
729}
730
731void
732Ring::discard_one_row()
733{
734 m_start++;
735 if (G_UNLIKELY(m_start == m_writable)(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
m_start == m_writable) _g_boolean_var_ = 1; else _g_boolean_var_
= 0; _g_boolean_var_; }), 0))
) {
736 reset_streams(m_writable);
737 } else if (m_start < m_writable) {
738 RowRecord record;
739 _bte_stream_advance_tail(m_row_stream, m_start * sizeof (record));
740 if (G_LIKELY(read_row_record(&record, m_start))(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
read_row_record(&record, m_start)) _g_boolean_var_ = 1; else
_g_boolean_var_ = 0; _g_boolean_var_; }), 1))
) {
741 _bte_stream_advance_tail(m_text_stream, record.text_start_offset);
742 _bte_stream_advance_tail(m_attr_stream, record.attr_start_offset);
743 }
744 } else {
745 m_writable = m_start;
746 }
747}
748
749void
750Ring::maybe_freeze_one_row()
751{
752 /* See the comment about m_visible_rows + 1 at ensure_writable_room(). */
753 if (G_LIKELY(m_mask >= m_visible_rows + 1 &&(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
m_mask >= m_visible_rows + 1 && m_writable + m_mask
+ 1 == m_end) _g_boolean_var_ = 1; else _g_boolean_var_ = 0;
_g_boolean_var_; }), 1))
754 m_writable + m_mask + 1 == m_end)(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
m_mask >= m_visible_rows + 1 && m_writable + m_mask
+ 1 == m_end) _g_boolean_var_ = 1; else _g_boolean_var_ = 0;
_g_boolean_var_; }), 1))
)
755 freeze_one_row();
756 else
757 ensure_writable_room();
758}
759
760//FIXMEchpe maybe inline this one
761void
762Ring::maybe_discard_one_row()
763{
764 if (length() == m_max)
765 discard_one_row();
766}
767
768void
769Ring::ensure_writable_room()
770{
771 row_t new_mask, old_mask, i, end;
772 BteRowData* old_array, *new_array;;
773
774 /* Keep at least m_visible_rows + 1 rows in the ring.
775 * The BiDi spec requires that the just scrolled out row
776 * is still alterable (can be switched to hard line ending).
777 * It's nice anyway to make that hard wrapped upon a clear. */
778 if (G_LIKELY(m_mask >= m_visible_rows + 1 &&(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
m_mask >= m_visible_rows + 1 && m_writable + m_mask
+ 1 > m_end) _g_boolean_var_ = 1; else _g_boolean_var_ = 0
; _g_boolean_var_; }), 1))
779 m_writable + m_mask + 1 > m_end)(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
m_mask >= m_visible_rows + 1 && m_writable + m_mask
+ 1 > m_end) _g_boolean_var_ = 1; else _g_boolean_var_ = 0
; _g_boolean_var_; }), 1))
)
780 return;
781
782 old_mask = m_mask;
783 old_array = m_array;
784
785 do {
786 m_mask = (m_mask << 1) + 1;
787 } while (m_mask < m_visible_rows + 1 || m_writable + m_mask + 1 <= m_end);
788
789 _bte_debug_print(BTE_DEBUG_RING, "Enlarging writable array from %lu to %lu\n", old_mask, m_mask)do { } while(0);
790
791 m_array = (BteRowData* ) g_malloc0(sizeof (m_array[0]) * (m_mask + 1));
792
793 new_mask = m_mask;
794 new_array = m_array;
795
796 end = m_writable + old_mask + 1;
797 for (i = m_writable; i < end; i++)
798 new_array[i & new_mask] = old_array[i & old_mask];
799
800 g_free (old_array);
801}
802
803void
804Ring::ensure_writable(row_t position)
805{
806 if (G_LIKELY(position >= m_writable)(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
position >= m_writable) _g_boolean_var_ = 1; else _g_boolean_var_
= 0; _g_boolean_var_; }), 1))
)
807 return;
808
809 _bte_debug_print(BTE_DEBUG_RING, "Ensure writable %lu.\n", position)do { } while(0);
810
811 //FIXMEchpe surely this can be optimised
812 while (position < m_writable)
813 thaw_one_row();
814}
815
816/**
817 * Ring::resize:
818 * @max_rows: new maximum numbers of rows in the ring
819 *
820 * Changes the number of lines the ring can contain.
821 */
822void
823Ring::resize(row_t max_rows)
824{
825 _bte_debug_print(BTE_DEBUG_RING, "Resizing to %lu.\n", max_rows)do { } while(0);
826
827 validate()do { } while(0);
828
829 /* Adjust the start of tail chunk now */
830 if (length() > max_rows) {
831 m_start = m_end - max_rows;
832 if (m_start >= m_writable) {
833 reset_streams(m_writable);
834 m_writable = m_start;
835 }
836 }
837
838 m_max = max_rows;
839}
840
841void
842Ring::shrink(row_t max_len)
843{
844 if (length() <= max_len)
845 return;
846
847 _bte_debug_print(BTE_DEBUG_RING, "Shrinking to %lu.\n", max_len)do { } while(0);
848
849 validate()do { } while(0);
850
851 if (m_writable - m_start <= max_len)
852 m_end = m_start + max_len;
853 else {
854 while (m_writable - m_start > max_len) {
855 ensure_writable(m_writable - 1);
856 m_end = m_writable;
857 }
858 }
859
860 /* TODO May want to shrink down m_array */
861
862 validate()do { } while(0);
863}
864
865/**
866 * Ring::insert:
867 * @position: an index
868 *
869 * Inserts a new, empty, row into @ring at the @position'th offset.
870 * The item at that position and any items after that are shifted down.
871 *
872 * Return: the newly added row.
873 */
874BteRowData*
875Ring::insert(row_t position, guint8 bidi_flags)
876{
877 row_t i;
878 BteRowData* row, tmp;
879
880 _bte_debug_print(BTE_DEBUG_RING, "Inserting at position %lu.\n", position)do { } while(0);
881 validate()do { } while(0);
882
883 maybe_discard_one_row();
884 ensure_writable(position);
885 ensure_writable_room();
886
887 g_assert_cmpuint (position, >=, m_writable)do { guint64 __n1 = (position), __n2 = (m_writable); if (__n1
>= __n2) ; else g_assertion_message_cmpnum ("BTE", "../src/ring.cc"
, 887, ((const char*) (__PRETTY_FUNCTION__)), "position" " " ">="
" " "m_writable", (long double) __n1, ">=", (long double)
__n2, 'i'); } while (0)
;
888 g_assert_cmpuint (position, <=, m_end)do { guint64 __n1 = (position), __n2 = (m_end); if (__n1 <=
__n2) ; else g_assertion_message_cmpnum ("BTE", "../src/ring.cc"
, 888, ((const char*) (__PRETTY_FUNCTION__)), "position" " " "<="
" " "m_end", (long double) __n1, "<=", (long double) __n2
, 'i'); } while (0)
;
889
890 //FIXMEchpe WTF use better data structures!
891 tmp = *get_writable_index(m_end);
892 for (i = m_end; i > position; i--)
893 *get_writable_index(i) = *get_writable_index(i - 1);
894 *get_writable_index(position) = tmp;
895
896 row = get_writable_index(position);
897 _bte_row_data_clear (row);
898 row->attr.bidi_flags = bidi_flags;
899 m_end++;
900
901 maybe_freeze_one_row();
902 validate()do { } while(0);
903 return row;
904}
905
906/**
907 * Ring::remove:
908 * @position: an index
909 *
910 * Removes the @position'th item from @ring.
911 */
912void
913Ring::remove(row_t position)
914{
915 row_t i;
916 BteRowData tmp;
917
918 _bte_debug_print(BTE_DEBUG_RING, "Removing item at position %lu.\n", position)do { } while(0);
919 validate()do { } while(0);
920
921 if (G_UNLIKELY(!contains(position))(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
!contains(position)) _g_boolean_var_ = 1; else _g_boolean_var_
= 0; _g_boolean_var_; }), 0))
)
922 return;
923
924 ensure_writable(position);
925
926 //FIXMEchpe WTF as above
927 tmp = *get_writable_index(position);
928 for (i = position; i < m_end - 1; i++)
929 *get_writable_index(i) = *get_writable_index(i + 1);
930 *get_writable_index(m_end - 1) = tmp;
931
932 if (m_end > m_writable)
933 m_end--;
934
935 validate()do { } while(0);
936}
937
938
939/**
940 * Ring::append:
941 * @data: the new item
942 *
943 * Appends a new item to the ring.
944 *
945 * Return: the newly added row.
946 */
947BteRowData*
948Ring::append(guint8 bidi_flags)
949{
950 return insert(next(), bidi_flags);
951}
952
953
954/**
955 * Ring::drop_scrollback:
956 * @position: drop contents up to this point, which must be in the writable region.
957 *
958 * Drop the scrollback (offscreen contents).
959 *
960 * TODOegmont: We wouldn't need the position argument after addressing 708213#c29.
961 */
962void
963Ring::drop_scrollback(row_t position)
964{
965 ensure_writable(position);
966
967 m_start = m_writable = position;
968 reset_streams(position);
969}
970
971/**
972 * Ring::set_visible_rows:
973 * @rows: the number of visible rows
974 *
975 * Set the number of visible rows.
976 * It's required to be set correctly for the alternate screen so that it
977 * never hits the streams. It's also required for clearing the scrollback.
978 */
979void
980Ring::set_visible_rows(row_t rows)
981{
982 m_visible_rows = rows;
983}
984
985
986/* Convert a (row,col) into a CellTextOffset.
987 * Requires the row to be frozen, or be outsize the range covered by the ring.
988 */
989bool
990Ring::frozen_row_column_to_text_offset(row_t position,
991 column_t column,
992 CellTextOffset* offset)
993{
994 RowRecord records[2];
995 BteCell *cell;
996 GString *buffer = m_utf8_buffer;
997 BteRowData const* row;
998 unsigned int i, num_chars, off;
999
1000 if (position >= m_end) {
1001 offset->text_offset = _bte_stream_head(m_text_stream) + position - m_end;
1002 offset->fragment_cells = 0;
1003 offset->eol_cells = column;
1004 return true;
1005 }
1006
1007 if (G_UNLIKELY (position < m_start)(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
position < m_start) _g_boolean_var_ = 1; else _g_boolean_var_
= 0; _g_boolean_var_; }), 0))
) {
1008 /* This happens when the marker (saved cursor position) is
1009 scrolled off at the top of the scrollback buffer. */
1010 position = m_start;
1011 column = 0;
1012 /* go on */
1013 }
1014
1015 g_assert_cmpuint(position, <, m_writable)do { guint64 __n1 = (position), __n2 = (m_writable); if (__n1
< __n2) ; else g_assertion_message_cmpnum ("BTE", "../src/ring.cc"
, 1015, ((const char*) (__PRETTY_FUNCTION__)), "position" " "
"<" " " "m_writable", (long double) __n1, "<", (long double
) __n2, 'i'); } while (0)
;
1016 if (!read_row_record(&records[0], position))
1017 return false;
1018 if ((position + 1) * sizeof (records[0]) < _bte_stream_head(m_row_stream)) {
1019 if (!read_row_record(&records[1], position + 1))
1020 return false;
1021 } else
1022 records[1].text_start_offset = _bte_stream_head(m_text_stream);
1023
1024 g_string_set_size (buffer, records[1].text_start_offset - records[0].text_start_offset);
1025 if (!_bte_stream_read(m_text_stream, records[0].text_start_offset, buffer->str, buffer->len))
1026 return false;
1027
1028 if (G_LIKELY (buffer->len && buffer->str[buffer->len - 1] == '\n')(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
buffer->len && buffer->str[buffer->len - 1] ==
'\n') _g_boolean_var_ = 1; else _g_boolean_var_ = 0; _g_boolean_var_
; }), 1))
)
1029 g_string_truncate (buffer, buffer->len - 1);
1030
1031 row = index(position);
1032
1033 /* row and buffer now contain the same text, in different representation */
1034
1035 /* count the number of characters up to the given column */
1036 offset->fragment_cells = 0;
1037 offset->eol_cells = -1;
1038 num_chars = 0;
1039 for (i = 0, cell = row->cells; i < row->len && i < column; i++, cell++) {
1040 if (G_LIKELY (!cell->attr.fragment())(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
!cell->attr.fragment()) _g_boolean_var_ = 1; else _g_boolean_var_
= 0; _g_boolean_var_; }), 1))
) {
1041 if (G_UNLIKELY (i + cell->attr.columns() > column)(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
i + cell->attr.columns() > column) _g_boolean_var_ = 1;
else _g_boolean_var_ = 0; _g_boolean_var_; }), 0))
) {
1042 offset->fragment_cells = column - i;
1043 break;
1044 }
1045 num_chars += _bte_unistr_strlen(cell->c);
1046 }
1047 }
1048 if (i >= row->len) {
1049 offset->eol_cells = column - i;
1050 }
1051
1052 /* count the number of UTF-8 bytes for the given number of characters */
1053 off = 0;
1054 while (num_chars > 0 && off < buffer->len) {
1055 off++;
1056 if ((buffer->str[off] & 0xC0) != 0x80) num_chars--;
1057 }
1058 offset->text_offset = records[0].text_start_offset + off;
1059 return true;
1060}
1061
1062
1063/* Given a row number and a CellTextOffset, compute the column within that row.
1064 It's the caller's responsibility to ensure that CellTextOffset really falls into that row.
1065 Requires the row to be frozen, or be outsize the range covered by the ring.
1066 */
1067bool
1068Ring::frozen_row_text_offset_to_column(row_t position,
1069 CellTextOffset const* offset,
1070 column_t* column)
1071{
1072 RowRecord records[2];
1073 BteCell *cell;
1074 GString *buffer = m_utf8_buffer;
1075 BteRowData const* row;
1076 unsigned int i, off, num_chars, nc;
1077
1078 if (position >= m_end) {
1079 *column = offset->eol_cells;
1080 return true;
1081 }
1082
1083 if (G_UNLIKELY (position < m_start)(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
position < m_start) _g_boolean_var_ = 1; else _g_boolean_var_
= 0; _g_boolean_var_; }), 0))
) {
1084 /* This happens when the marker (saved cursor position) is
1085 scrolled off at the top of the scrollback buffer. */
1086 *column = 0;
1087 return true;
1088 }
1089
1090 g_assert_cmpuint(position, <, m_writable)do { guint64 __n1 = (position), __n2 = (m_writable); if (__n1
< __n2) ; else g_assertion_message_cmpnum ("BTE", "../src/ring.cc"
, 1090, ((const char*) (__PRETTY_FUNCTION__)), "position" " "
"<" " " "m_writable", (long double) __n1, "<", (long double
) __n2, 'i'); } while (0)
;
1091 if (!read_row_record(&records[0], position))
1092 return false;
1093 if ((position + 1) * sizeof (records[0]) < _bte_stream_head(m_row_stream)) {
1094 if (!read_row_record(&records[1], position + 1))
1095 return false;
1096 } else
1097 records[1].text_start_offset = _bte_stream_head (m_text_stream);
1098
1099 g_string_set_size (buffer, records[1].text_start_offset - records[0].text_start_offset);
1100 if (!_bte_stream_read(m_text_stream, records[0].text_start_offset, buffer->str, buffer->len))
1101 return false;
1102
1103 if (G_LIKELY (buffer->len && buffer->str[buffer->len - 1] == '\n')(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
buffer->len && buffer->str[buffer->len - 1] ==
'\n') _g_boolean_var_ = 1; else _g_boolean_var_ = 0; _g_boolean_var_
; }), 1))
)
1104 g_string_truncate (buffer, buffer->len - 1);
1105
1106 /* Now that we've chopped off the likely trailing newline (which is only rarely missing,
1107 * if the ring ends in a soft wrapped line; see bug 181), the position we're about to
1108 * locate can be anywhere in the string, including just after its last character,
1109 * but not beyond that. */
1110 g_assert_cmpuint(offset->text_offset, >=, records[0].text_start_offset)do { guint64 __n1 = (offset->text_offset), __n2 = (records
[0].text_start_offset); if (__n1 >= __n2) ; else g_assertion_message_cmpnum
("BTE", "../src/ring.cc", 1110, ((const char*) (__PRETTY_FUNCTION__
)), "offset->text_offset" " " ">=" " " "records[0].text_start_offset"
, (long double) __n1, ">=", (long double) __n2, 'i'); } while
(0)
;
1111 g_assert_cmpuint(offset->text_offset, <=, records[0].text_start_offset + buffer->len)do { guint64 __n1 = (offset->text_offset), __n2 = (records
[0].text_start_offset + buffer->len); if (__n1 <= __n2)
; else g_assertion_message_cmpnum ("BTE", "../src/ring.cc", 1111
, ((const char*) (__PRETTY_FUNCTION__)), "offset->text_offset"
" " "<=" " " "records[0].text_start_offset + buffer->len"
, (long double) __n1, "<=", (long double) __n2, 'i'); } while
(0)
;
1112
1113 row = index(position);
1114
1115 /* row and buffer now contain the same text, in different representation */
1116
1117 /* count the number of characters for the given UTF-8 text offset */
1118 off = offset->text_offset - records[0].text_start_offset;
1119 num_chars = 0;
1120 for (i = 0; i < off; i++) {
1121 if ((buffer->str[i] & 0xC0) != 0x80) num_chars++;
1122 }
1123
1124 /* count the number of columns for the given number of characters */
1125 for (i = 0, cell = row->cells; i < row->len; i++, cell++) {
1126 if (G_LIKELY (!cell->attr.fragment())(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
!cell->attr.fragment()) _g_boolean_var_ = 1; else _g_boolean_var_
= 0; _g_boolean_var_; }), 1))
) {
1127 if (num_chars == 0) break;
1128 nc = _bte_unistr_strlen(cell->c);
1129 if (nc > num_chars) break;
1130 num_chars -= nc;
1131 }
1132 }
1133
1134 /* always add fragment_cells, but add eol_cells only if we're at eol */
1135 i += offset->fragment_cells;
1136 if (G_UNLIKELY (offset->eol_cells >= 0 && i == row->len)(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
offset->eol_cells >= 0 && i == row->len) _g_boolean_var_
= 1; else _g_boolean_var_ = 0; _g_boolean_var_; }), 0))
)
1137 i += offset->eol_cells;
1138 *column = i;
1139 return true;
1140}
1141
1142
1143/**
1144 * Ring::rewrap:
1145 * @columns: new number of columns
1146 * @markers: 0-terminated array of #BteVisualPosition
1147 *
1148 * Reflow the @ring to match the new number of @columns.
1149 * For all @markers, find the cell at that position and update them to
1150 * reflect the cell's new position.
1151 */
1152/* See ../doc/rewrap.txt for design and implementation details. */
1153void
1154Ring::rewrap(column_t columns,
1155 BteVisualPosition** markers)
1156{
1157 row_t old_row_index, new_row_index;
1158 int i;
1159 int num_markers = 0;
1160 CellTextOffset *marker_text_offsets;
1161 BteVisualPosition *new_markers;
1162 RowRecord old_record;
1163 CellAttrChange attr_change;
1164 BteStream *new_row_stream;
1165 gsize paragraph_start_text_offset;
1166 gsize paragraph_end_text_offset;
1167 gsize paragraph_len; /* excluding trailing '\n' */
1168 gsize attr_offset;
1169 gsize old_ring_end;
1170
1171 if (G_UNLIKELY(length() == 0)(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
length() == 0) _g_boolean_var_ = 1; else _g_boolean_var_ = 0;
_g_boolean_var_; }), 0))
)
1172 return;
1173 _bte_debug_print(BTE_DEBUG_RING, "Ring before rewrapping:\n")do { } while(0);
1174 validate()do { } while(0);
1175 new_row_stream = _bte_file_stream_new();
1176
1177 /* Freeze everything, because rewrapping is really complicated and we don't want to
1178 duplicate the code for frozen and thawed rows. */
1179 while (m_writable < m_end)
1180 freeze_one_row();
1181
1182 /* For markers given as (row,col) pairs find their offsets in the text stream.
1183 This code requires that the rows are already frozen. */
1184 while (markers[num_markers] != nullptr)
1185 num_markers++;
1186 marker_text_offsets = (CellTextOffset *) g_malloc(num_markers * sizeof (marker_text_offsets[0]));
1187 new_markers = (BteVisualPosition *) g_malloc(num_markers * sizeof (new_markers[0]));
1188 for (i = 0; i < num_markers; i++) {
1189 /* Convert visual column into byte offset */
1190 if (!frozen_row_column_to_text_offset(markers[i]->row, markers[i]->col, &marker_text_offsets[i]))
1191 goto err;
1192 new_markers[i].row = new_markers[i].col = -1;
1193 _bte_debug_print(BTE_DEBUG_RING,do { } while(0)
1194 "Marker #%d old coords: row %ld col %ld -> text_offset %" G_GSIZE_FORMAT " fragment_cells %d eol_cells %d\n",do { } while(0)
1195 i, markers[i]->row, markers[i]->col, marker_text_offsets[i].text_offset,do { } while(0)
1196 marker_text_offsets[i].fragment_cells, marker_text_offsets[i].eol_cells)do { } while(0);
1197 }
1198
1199 /* Prepare for rewrapping */
1200 if (!read_row_record(&old_record, m_start))
1201 goto err;
1202 paragraph_start_text_offset = old_record.text_start_offset;
1203 paragraph_end_text_offset = _bte_stream_head(m_text_stream); /* initialized to silence gcc */
1204 new_row_index = 0;
1205
1206 attr_offset = old_record.attr_start_offset;
1207 if (!_bte_stream_read(m_attr_stream, attr_offset, (char *) &attr_change, sizeof (attr_change))) {
1208 _attrcpy(&attr_change.attr, &m_last_attr);
1209 attr_change.attr.hyperlink_length = hyperlink_get(m_last_attr.hyperlink_idx)->len;
1210 attr_change.text_end_offset = _bte_stream_head(m_text_stream);
1211 }
1212
1213 old_row_index = m_start + 1;
1214 while (paragraph_start_text_offset < _bte_stream_head(m_text_stream)) {
1215 /* Find the boundaries of the next paragraph */
1216 gboolean prev_record_was_soft_wrapped = FALSE(0);
1217 gboolean paragraph_is_ascii = TRUE(!(0));
1218 guint8 paragraph_bidi_flags = old_record.bidi_flags;
1219 gsize text_offset = paragraph_start_text_offset;
1220 RowRecord new_record;
1221 column_t col = 0;
1222
1223 _bte_debug_print(BTE_DEBUG_RING,do { } while(0)
1224 " Old paragraph: row %lu (text_offset %" G_GSIZE_FORMAT ") up to (exclusive) ", /* no '\n' */do { } while(0)
1225 old_row_index - 1,do { } while(0)
1226 paragraph_start_text_offset)do { } while(0);
1227 while (old_row_index <= m_end) {
1228 prev_record_was_soft_wrapped = old_record.soft_wrapped;
1229 paragraph_is_ascii = paragraph_is_ascii && old_record.is_ascii;
1230 if (G_LIKELY (old_row_index < m_end)(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
old_row_index < m_end) _g_boolean_var_ = 1; else _g_boolean_var_
= 0; _g_boolean_var_; }), 1))
) {
1231 if (!read_row_record(&old_record, old_row_index))
1232 goto err;
1233 paragraph_end_text_offset = old_record.text_start_offset;
1234 } else {
1235 paragraph_end_text_offset = _bte_stream_head (m_text_stream);
1236 }
1237 old_row_index++;
1238 if (!prev_record_was_soft_wrapped)
1239 break;
1240 }
1241
1242 paragraph_len = paragraph_end_text_offset - paragraph_start_text_offset;
1243 if (!prev_record_was_soft_wrapped) /* The last paragraph can be soft wrapped! */
1244 paragraph_len--; /* Strip trailing '\n' */
1245 _bte_debug_print(BTE_DEBUG_RING,do { } while(0)
1246 "row %lu (text_offset %" G_GSIZE_FORMAT ")%s len %" G_GSIZE_FORMAT " is_ascii %d\n",do { } while(0)
1247 old_row_index - 1,do { } while(0)
1248 paragraph_end_text_offset,do { } while(0)
1249 prev_record_was_soft_wrapped ? " soft_wrapped" : "",do { } while(0)
1250 paragraph_len, paragraph_is_ascii)do { } while(0);
1251
1252 /* Wrap the paragraph */
1253 if (attr_change.text_end_offset <= text_offset) {
1254 /* Attr change at paragraph boundary, advance to next attr. */
1255 attr_offset += sizeof (attr_change) + attr_change.attr.hyperlink_length + 2;
1256 if (!_bte_stream_read(m_attr_stream, attr_offset, (char *) &attr_change, sizeof (attr_change))) {
1257 _attrcpy(&attr_change.attr, &m_last_attr);
1258 attr_change.attr.hyperlink_length = hyperlink_get(m_last_attr.hyperlink_idx)->len;
1259 attr_change.text_end_offset = _bte_stream_head(m_text_stream);
1260 }
1261 }
1262 memset(&new_record, 0, sizeof (new_record));
1263 new_record.text_start_offset = text_offset;
1264 new_record.attr_start_offset = attr_offset;
1265 new_record.is_ascii = paragraph_is_ascii;
1266 new_record.bidi_flags = paragraph_bidi_flags;
1267
1268 while (paragraph_len > 0) {
1269 /* Wrap one continuous run of identical attributes within the paragraph. */
1270 gsize runlength; /* number of bytes we process in one run: identical attributes, within paragraph */
1271 if (attr_change.text_end_offset <= text_offset) {
1272 /* Attr change at line boundary, advance to next attr. */
1273 attr_offset += sizeof (attr_change) + attr_change.attr.hyperlink_length + 2;
1274 if (!_bte_stream_read(m_attr_stream, attr_offset, (char *) &attr_change, sizeof (attr_change))) {
1275 _attrcpy(&attr_change.attr, &m_last_attr);
1276 attr_change.attr.hyperlink_length = hyperlink_get(m_last_attr.hyperlink_idx)->len;
1277 attr_change.text_end_offset = _bte_stream_head(m_text_stream);
1278 }
1279 }
1280 runlength = MIN(paragraph_len, attr_change.text_end_offset - text_offset)(((paragraph_len) < (attr_change.text_end_offset - text_offset
)) ? (paragraph_len) : (attr_change.text_end_offset - text_offset
))
;
1281
1282 if (G_UNLIKELY (attr_change.attr.columns() == 0)(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
attr_change.attr.columns() == 0) _g_boolean_var_ = 1; else _g_boolean_var_
= 0; _g_boolean_var_; }), 0))
) {
1283 /* Combining characters all fit in the current row */
1284 text_offset += runlength;
1285 paragraph_len -= runlength;
1286 } else {
1287 while (runlength) {
1288 if (col >= columns - attr_change.attr.columns() + 1) {
1289 /* Wrap now, write the soft wrapped row's record */
1290 new_record.soft_wrapped = 1;
1291 _bte_stream_append(new_row_stream, (char const* ) &new_record, sizeof (new_record));
1292 _bte_debug_print(BTE_DEBUG_RING,do { } while(0)
1293 " New row %ld text_offset %" G_GSIZE_FORMAT " attr_offset %" G_GSIZE_FORMAT " soft_wrapped\n",do { } while(0)
1294 new_row_index,do { } while(0)
1295 new_record.text_start_offset, new_record.attr_start_offset)do { } while(0);
1296 for (i = 0; i < num_markers; i++) {
1297 if (G_UNLIKELY (marker_text_offsets[i].text_offset >= new_record.text_start_offset &&(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
marker_text_offsets[i].text_offset >= new_record.text_start_offset
&& marker_text_offsets[i].text_offset < text_offset
) _g_boolean_var_ = 1; else _g_boolean_var_ = 0; _g_boolean_var_
; }), 0))
1298 marker_text_offsets[i].text_offset < text_offset)(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
marker_text_offsets[i].text_offset >= new_record.text_start_offset
&& marker_text_offsets[i].text_offset < text_offset
) _g_boolean_var_ = 1; else _g_boolean_var_ = 0; _g_boolean_var_
; }), 0))
) {
1299 new_markers[i].row = new_row_index;
1300 _bte_debug_print(BTE_DEBUG_RING,do { } while(0)
1301 " Marker #%d will be here in row %lu\n", i, new_row_index)do { } while(0);
1302 }
1303 }
1304 new_row_index++;
1305 new_record.text_start_offset = text_offset;
1306 new_record.attr_start_offset = attr_offset;
1307 col = 0;
1308 }
1309 if (paragraph_is_ascii) {
1310 /* Shortcut for quickly wrapping ASCII (excluding TAB) text.
1311 Don't read text_stream, and advance by a whole row of characters. */
1312 int len = MIN(runlength, (gsize) (columns - col))(((runlength) < ((gsize) (columns - col))) ? (runlength) :
((gsize) (columns - col)))
;
1313 col += len;
1314 text_offset += len;
1315 paragraph_len -= len;
1316 runlength -= len;
1317 } else {
1318 /* Process one character only. */
1319 char textbuf[6]; /* fits at least one UTF-8 character */
1320 int textbuf_len;
1321 col += attr_change.attr.columns();
1322 /* Find beginning of next UTF-8 character */
1323 text_offset++; paragraph_len--; runlength--;
1324 textbuf_len = MIN(runlength, sizeof (textbuf))(((runlength) < (sizeof (textbuf))) ? (runlength) : (sizeof
(textbuf)))
;
1325 if (!_bte_stream_read(m_text_stream, text_offset, textbuf, textbuf_len))
1326 goto err;
1327 for (i = 0; i < textbuf_len && (textbuf[i] & 0xC0) == 0x80; i++) {
1328 text_offset++; paragraph_len--; runlength--;
1329 }
1330 }
1331 }
1332 }
1333 }
1334
1335 /* Write the record of the paragraph's last row. */
1336 /* Hard wrapped, except maybe at the end of the very last paragraph */
1337 new_record.soft_wrapped = prev_record_was_soft_wrapped;
1338 _bte_stream_append(new_row_stream, (char const* ) &new_record, sizeof (new_record));
1339 _bte_debug_print(BTE_DEBUG_RING,do { } while(0)
1340 " New row %ld text_offset %" G_GSIZE_FORMAT " attr_offset %" G_GSIZE_FORMAT "\n",do { } while(0)
1341 new_row_index,do { } while(0)
1342 new_record.text_start_offset, new_record.attr_start_offset)do { } while(0);
1343 for (i = 0; i < num_markers; i++) {
1344 if (G_UNLIKELY (marker_text_offsets[i].text_offset >= new_record.text_start_offset &&(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
marker_text_offsets[i].text_offset >= new_record.text_start_offset
&& marker_text_offsets[i].text_offset < paragraph_end_text_offset
) _g_boolean_var_ = 1; else _g_boolean_var_ = 0; _g_boolean_var_
; }), 0))
1345 marker_text_offsets[i].text_offset < paragraph_end_text_offset)(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
marker_text_offsets[i].text_offset >= new_record.text_start_offset
&& marker_text_offsets[i].text_offset < paragraph_end_text_offset
) _g_boolean_var_ = 1; else _g_boolean_var_ = 0; _g_boolean_var_
; }), 0))
) {
1346 new_markers[i].row = new_row_index;
1347 _bte_debug_print(BTE_DEBUG_RING,do { } while(0)
1348 " Marker #%d will be here in row %lu\n", i, new_row_index)do { } while(0);
1349 }
1350 }
1351 new_row_index++;
1352 paragraph_start_text_offset = paragraph_end_text_offset;
1353 }
1354
1355 /* Update the ring. */
1356 old_ring_end = m_end;
1357 g_object_unref(m_row_stream);
1358 m_row_stream = new_row_stream;
1359 m_writable = m_end = new_row_index;
1360 m_start = 0;
1361 if (m_end > m_max)
1362 m_start = m_end - m_max;
1363 m_cached_row_num = (row_t) -1;
1364
1365 /* Find the markers. This requires that the ring is already updated. */
1366 for (i = 0; i < num_markers; i++) {
1367 /* Compute the row for markers beyond the ring */
1368 if (new_markers[i].row == -1)
1369 new_markers[i].row = markers[i]->row - old_ring_end + m_end;
1370 /* Convert byte offset into visual column */
1371 if (!frozen_row_text_offset_to_column(new_markers[i].row, &marker_text_offsets[i], &new_markers[i].col))
1372 goto err;
1373 _bte_debug_print(BTE_DEBUG_RING,do { } while(0)
1374 "Marker #%d new coords: text_offset %" G_GSIZE_FORMAT " fragment_cells %d eol_cells %d -> row %ld col %ld\n",do { } while(0)
1375 i, marker_text_offsets[i].text_offset, marker_text_offsets[i].fragment_cells,do { } while(0)
1376 marker_text_offsets[i].eol_cells, new_markers[i].row, new_markers[i].col)do { } while(0);
1377 markers[i]->row = new_markers[i].row;
1378 markers[i]->col = new_markers[i].col;
1379 }
1380 g_free(marker_text_offsets);
1381 g_free(new_markers);
1382
1383 _bte_debug_print(BTE_DEBUG_RING, "Ring after rewrapping:\n")do { } while(0);
1384 validate()do { } while(0);
1385 return;
1386
1387err:
1388#ifdef BTE_DEBUG
1389 _bte_debug_print(BTE_DEBUG_RING,do { } while(0)
1390 "Error while rewrapping\n")do { } while(0);
1391 g_assert_not_reached()do { g_assertion_message_expr ("BTE", "../src/ring.cc", 1391,
((const char*) (__PRETTY_FUNCTION__)), __null); } while (0)
;
1392#endif
1393 g_object_unref(new_row_stream);
1394 g_free(marker_text_offsets);
1395 g_free(new_markers);
1396}
1397
1398
1399bool
1400Ring::write_row(GOutputStream* stream,
1401 BteRowData* row,
1402 BteWriteFlags flags,
1403 GCancellable* cancellable,
1404 GError** error)
1405{
1406 BteCell *cell;
1407 GString *buffer = m_utf8_buffer;
1408 int i;
1409 gsize bytes_written;
1410
1411 /* Simple version of the loop in freeze_row().
1412 * TODO Should unify one day */
1413 g_string_set_size (buffer, 0);
1414 for (i = 0, cell = row->cells; i < row->len; i++, cell++) {
1415 if (G_LIKELY (!cell->attr.fragment())(__builtin_expect (__extension__ ({ int _g_boolean_var_; if (
!cell->attr.fragment()) _g_boolean_var_ = 1; else _g_boolean_var_
= 0; _g_boolean_var_; }), 1))
)
1416 _bte_unistr_append_to_string (cell->c, buffer);
1417 }
1418 if (!row->attr.soft_wrapped)
1419 g_string_append_c (buffer, '\n')g_string_append_c_inline (buffer, '\n');
1420
1421 return g_output_stream_write_all (stream, buffer->str, buffer->len, &bytes_written, cancellable, error);
1422}
1423
1424/**
1425 * Ring::write_contents:
1426 * @stream: a #GOutputStream to write to
1427 * @flags: a set of #BteWriteFlags
1428 * @cancellable: optional #GCancellable object, %nullptr to ignore
1429 * @error: a #GError location to store the error occuring, or %nullptr to ignore
1430 *
1431 * Write entire ring contents to @stream according to @flags.
1432 *
1433 * Return: %TRUE on success, %FALSE if there was an error
1434 */
1435bool
1436Ring::write_contents(GOutputStream* stream,
1437 BteWriteFlags flags,
1438 GCancellable* cancellable,
1439 GError** error)
1440{
1441 row_t i;
1442
1443 _bte_debug_print(BTE_DEBUG_RING, "Writing contents to GOutputStream.\n")do { } while(0);
1444
1445 if (m_start < m_writable)
1446 {
1447 RowRecord record;
1448
1449 if (read_row_record(&record, m_start))
1450 {
1451 gsize start_offset = record.text_start_offset;
1452 gsize end_offset = _bte_stream_head(m_text_stream);
1453 char buf[4096];
1454 while (start_offset < end_offset)
1455 {
1456 gsize bytes_written, len;
1457
1458 len = MIN (G_N_ELEMENTS (buf), end_offset - start_offset)((((sizeof (buf) / sizeof ((buf)[0]))) < (end_offset - start_offset
)) ? ((sizeof (buf) / sizeof ((buf)[0]))) : (end_offset - start_offset
))
;
1459
1460 if (!_bte_stream_read (m_text_stream, start_offset,
1461 buf, len))
1462 return false;
1463
1464 if (!g_output_stream_write_all (stream, buf, len,
1465 &bytes_written, cancellable,
1466 error))
1467 return false;
1468
1469 start_offset += len;
1470 }
1471 }
1472 else
1473 //FIXMEchpe g_set_error!!
1474 return false;
1475 }
1476
1477 for (i = m_writable; i < m_end; i++) {
1478 if (!write_row(stream,
1479 get_writable_index(i),
1480 flags, cancellable, error))
1481 return false;
1482 }
1483
1484 return true;
1485}