Bug Summary

File:content/handlers/text/textplain.c
Warning:line 272, column 13
The left operand of '==' is a garbage value

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 textplain.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -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 -pic-is-pie -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/var/lib/jenkins/workspace/scan-build-netsurf -fcoverage-compilation-dir=/var/lib/jenkins/workspace/scan-build-netsurf -resource-dir /usr/lib/llvm-19/lib/clang/19 -isystem /usr/include/mit-krb5 -I . -I include -I build/Linux-gtk2 -I frontends -I content/handlers -D WITH_JPEG -U WITH_PDF_EXPORT -D LIBICONV_PLUG -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -I /usr/include/x86_64-linux-gnu -I /usr/include/p11-kit-1 -D WITH_CURL -D WITH_OPENSSL -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D UTF8PROC_EXPORTS -D WITH_UTF8PROC -I /usr/include/webp -D WITH_WEBP -I /usr/include/libpng16 -D WITH_PNG -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include/ -D WITH_BMP -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D WITH_GIF -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D WITH_NS_SVG -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D WITH_NSSPRITE -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D WITH_NSPSL -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D WITH_NSLOG -D NETSURF_UA_FORMAT_STRING="Mozilla/5.0 (%s) NetSurf/%d.%d" -D NETSURF_HOMEPAGE="about:welcome" -D NETSURF_LOG_LEVEL=VERBOSE -D NETSURF_BUILTIN_LOG_FILTER="(level:WARNING || cat:jserrors)" -D NETSURF_BUILTIN_VERBOSE_FILTER="(level:VERBOSE || cat:jserrors)" -D STMTEXPR=1 -I /usr/include/librsvg-2.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/gdk-pixbuf-2.0 -I /usr/include/libpng16 -I /usr/include/x86_64-linux-gnu -I /usr/include/webp -I /usr/include/cairo -I /usr/include/freetype2 -I /usr/include/pixman-1 -I /usr/include/harfbuzz -I /usr/include/libxml2 -I /usr/include/pango-1.0 -I /usr/include/fribidi -D WITH_RSVG -I /usr/include/gtk-2.0 -I /usr/lib/x86_64-linux-gnu/gtk-2.0/include -I /usr/include/pango-1.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -I /usr/include/harfbuzz -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/fribidi -I /usr/include/cairo -I /usr/include/pixman-1 -I /usr/include/gdk-pixbuf-2.0 -I /usr/include/x86_64-linux-gnu -I /usr/include/webp -I /usr/include/atk-1.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -D gtk -D nsgtk -D G_DISABLE_SINGLE_INCLUDES -D G_DISABLE_DEPRECATED -D GTK_DISABLE_SINGLE_INCLUDES -D GTK_MULTIHEAD_SAFE -D PANGO_DISABLE_DEPRECATED -D GTK_DISABLE_DEPRECATED -D _XOPEN_SOURCE=700 -D _POSIX_C_SOURCE=200809L -D _BSD_SOURCE -D _DEFAULT_SOURCE -D _NETBSD_SOURCE -D GTK_RESPATH="/var/lib/jenkins/artifacts-x86_64-linux-gnu/share/netsurf/:./frontends/gtk/res/" -D WITH_GRESOURCE -D DUK_OPT_HAVE_CUSTOM_H -internal-isystem /usr/lib/llvm-19/lib/clang/19/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wwrite-strings -Wno-unused-parameter -Wno-unused-but-set-variable -std=c99 -fconst-strings -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -vectorize-loops -vectorize-slp -analyzer-display-progress -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /var/lib/jenkins/workspace/scan-build-netsurf/clangScanBuildReports/2025-11-30-114731-3042003-1 -x c content/handlers/text/textplain.c

content/handlers/text/textplain.c

1/*
2 * Copyright 2006 James Bursa <bursa@users.sourceforge.net>
3 * Copyright 2006 Adrian Lees <adrianl@users.sourceforge.net>
4 *
5 * This file is part of NetSurf, http://www.netsurf-browser.org/
6 *
7 * NetSurf 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; version 2 of the License.
10 *
11 * NetSurf is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20/**
21 * \file
22 *
23 * plain text content handling implementation.
24 */
25
26#include <string.h>
27#include <parserutils/input/inputstream.h>
28
29#include "utils/errors.h"
30#include "utils/corestrings.h"
31#include "utils/http.h"
32#include "utils/log.h"
33#include "utils/messages.h"
34#include "utils/utils.h"
35#include "utils/utf8.h"
36#include "utils/nsoption.h"
37#include "netsurf/content.h"
38#include "netsurf/keypress.h"
39#include "netsurf/browser_window.h"
40#include "netsurf/plotters.h"
41#include "netsurf/layout.h"
42#include "content/content_protected.h"
43#include "content/content_factory.h"
44#include "content/hlcache.h"
45#include "content/textsearch.h"
46#include "content/handlers/css/utils.h"
47#include "desktop/selection.h"
48#include "desktop/gui_internal.h"
49
50#include "text/textplain.h"
51
52struct textplain_line {
53 size_t start;
54 size_t length;
55};
56
57/**
58 * plain text content
59 */
60typedef struct textplain_content {
61 struct content base;
62
63 lwc_string *encoding;
64 void *inputstream;
65 char *utf8_data;
66 size_t utf8_data_size;
67 size_t utf8_data_allocated;
68 unsigned long physical_line_count;
69 struct textplain_line *physical_line;
70 int formatted_width;
71 struct browser_window *bw;
72
73 struct selection *sel; /** Selection state */
74
75} textplain_content;
76
77
78#define CHUNK32768 32768 /* Must be a power of 2 */
79#define MARGIN4 4
80
81#define TAB_WIDTH8 8 /* must be power of 2 currently */
82#define TEXT_SIZE10 * (1 << (10)) 10 * PLOT_STYLE_SCALE(1 << (10)) /* Unscaled text size in pt */
83
84static plot_font_style_t textplain_style = {
85 .family = PLOT_FONT_FAMILY_MONOSPACE,
86 .size = TEXT_SIZE10 * (1 << (10)),
87 .weight = 400,
88 .flags = FONTF_NONE,
89 .background = 0xffffff,
90 .foreground = 0x000000,
91};
92
93static int textplain_tab_width = 256; /* try for a sensible default */
94
95static lwc_string *textplain_default_charset;
96
97
98/**
99 * Clean up after the text content handler
100 */
101static void textplain_fini(void)
102{
103 lwc_string_unref(textplain_default_charset){ lwc_string *__lwc_s = (textplain_default_charset); if (__lwc_s
!= ((void*)0)) { __lwc_s->refcnt--; if ((__lwc_s->refcnt
== 0) || ((__lwc_s->refcnt == 1) && (__lwc_s->
insensitive == __lwc_s))) lwc_string_destroy(__lwc_s); } }
;
104 textplain_default_charset = NULL((void*)0);
105}
106
107
108/**
109 * Work around feature in libparserutils
110 *
111 * if the client provides an encoding up front, but does not provide a
112 * charset detection callback, then libparserutils will replace the
113 * provided encoding with UTF-8. This breaks our input handling.
114 *
115 * Avoid this by providing a callback that does precisely nothing,
116 * thus preserving whatever charset information we decided on in
117 * textplain_create.
118 */
119static parserutils_error
120textplain_charset_hack(const uint8_t *data,
121 size_t len,
122 uint16_t *mibenum,
123 uint32_t *source)
124{
125 return PARSERUTILS_OK;
126}
127
128
129/**
130 * setup plain text render.
131 *
132 * \param[in] c content object.
133 * \param[in] encoding the encoding of the content.
134 * \return NSERROR_OK else appropriate error code.
135 */
136static nserror
137textplain_create_internal(textplain_content *c, lwc_string *encoding)
138{
139 char *utf8_data;
140 parserutils_inputstream *stream;
141 parserutils_error error;
142
143 textplain_style.size = (nsoption_int(font_size)(nsoptions[NSOPTION_font_size].value.i) * PLOT_STYLE_SCALE(1 << (10))) / 10;
144
145 utf8_data = malloc(CHUNK32768);
146 if (utf8_data == NULL((void*)0))
147 goto no_memory;
148
149 error = parserutils_inputstream_create(lwc_string_data(encoding)({((encoding != ((void*)0)) ? (void) (0) : __assert_fail ("encoding != NULL"
, "content/handlers/text/textplain.c", 149, __extension__ __PRETTY_FUNCTION__
)); (const char *)((encoding)+1);})
, 0,
150 textplain_charset_hack, &stream);
151 if (error == PARSERUTILS_BADENCODING) {
152 /* Fall back to Windows-1252 */
153 error = parserutils_inputstream_create("Windows-1252", 0,
154 textplain_charset_hack, &stream);
155 }
156 if (error != PARSERUTILS_OK) {
157 free(utf8_data);
158 goto no_memory;
159 }
160
161 c->encoding = lwc_string_ref(encoding)({lwc_string *__lwc_s = (encoding); ((__lwc_s != ((void*)0)) ?
(void) (0) : __assert_fail ("__lwc_s != NULL", "content/handlers/text/textplain.c"
, 161, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
++; __lwc_s;})
;
162 c->inputstream = stream;
163 c->utf8_data = utf8_data;
164 c->utf8_data_size = 0;
165 c->utf8_data_allocated = CHUNK32768;
166 c->physical_line = 0;
167 c->physical_line_count = 0;
168 c->formatted_width = 0;
169 c->bw = NULL((void*)0);
170 c->sel = selection_create((struct content *)c);
171
172 return NSERROR_OK;
173
174no_memory:
175 content_broadcast_error(&c->base, NSERROR_NOMEM, NULL((void*)0));
176
177 return NSERROR_NOMEM;
178}
179
180
181/**
182 * Create a CONTENT_TEXTPLAIN.
183 */
184static nserror
185textplain_create(const content_handler *handler,
186 lwc_string *imime_type,
187 const struct http_parameter *params,
188 llcache_handle *llcache,
189 const char *fallback_charset,
190 bool_Bool quirks,
191 struct content **c)
192{
193 textplain_content *text;
194 nserror error;
195 lwc_string *encoding;
196
197 text = calloc(1, sizeof(textplain_content));
198 if (text == NULL((void*)0)) {
199 return NSERROR_NOMEM;
200 }
201
202 error = content__init(&text->base, handler, imime_type, params,
203 llcache, fallback_charset, quirks);
204 if (error != NSERROR_OK) {
205 free(text);
206 return error;
207 }
208
209 error = http_parameter_list_find_item(params, corestring_lwc_charset,
210 &encoding);
211 if (error != NSERROR_OK) {
212 encoding = lwc_string_ref(textplain_default_charset)({lwc_string *__lwc_s = (textplain_default_charset); ((__lwc_s
!= ((void*)0)) ? (void) (0) : __assert_fail ("__lwc_s != NULL"
, "content/handlers/text/textplain.c", 212, __extension__ __PRETTY_FUNCTION__
)); __lwc_s->refcnt++; __lwc_s;})
;
213 }
214
215 error = textplain_create_internal(text, encoding);
216 if (error != NSERROR_OK) {
217 lwc_string_unref(encoding){ lwc_string *__lwc_s = (encoding); if (__lwc_s != ((void*)0)
) { __lwc_s->refcnt--; if ((__lwc_s->refcnt == 0) || ((
__lwc_s->refcnt == 1) && (__lwc_s->insensitive ==
__lwc_s))) lwc_string_destroy(__lwc_s); } }
;
218 free(text);
219 return error;
220 }
221
222 lwc_string_unref(encoding){ lwc_string *__lwc_s = (encoding); if (__lwc_s != ((void*)0)
) { __lwc_s->refcnt--; if ((__lwc_s->refcnt == 0) || ((
__lwc_s->refcnt == 1) && (__lwc_s->insensitive ==
__lwc_s))) lwc_string_destroy(__lwc_s); } }
;
223
224 *c = (struct content *) text;
225
226 return NSERROR_OK;
227}
228
229
230/**
231 * copy utf8 encoded data
232 */
233static bool_Bool
234textplain_copy_utf8_data(textplain_content *c, const uint8_t *buf, size_t len)
235{
236 if (c->utf8_data_size + len >= c->utf8_data_allocated) {
237 /* Compute next multiple of chunk above the required space */
238 size_t allocated;
239 char *utf8_data;
240
241 allocated = (c->utf8_data_size + len + CHUNK32768 - 1) & ~(CHUNK32768 - 1);
242 utf8_data = realloc(c->utf8_data, allocated);
243 if (utf8_data == NULL((void*)0))
244 return false0;
245
246 c->utf8_data = utf8_data;
247 c->utf8_data_allocated = allocated;
248 }
249
250 memcpy(c->utf8_data + c->utf8_data_size, buf, len);
251 c->utf8_data_size += len;
252
253 return true1;
254}
255
256
257/**
258 * drain input
259 */
260static bool_Bool
261textplain_drain_input(textplain_content *c,
262 parserutils_inputstream *stream,
263 parserutils_error terminator)
264{
265 static const uint8_t *u_fffd = (const uint8_t *) "\xef\xbf\xbd";
266 const uint8_t *ch;
267 size_t chlen, offset = 0;
13
'chlen' declared without an initial value
268
269 while (parserutils_inputstream_peek(stream, offset, &ch, &chlen) !=
14
Calling 'parserutils_inputstream_peek'
17
Returning from 'parserutils_inputstream_peek'
270 terminator) {
271 /* Replace all instances of NUL with U+FFFD */
272 if (chlen == 1 && *ch == 0) {
18
The left operand of '==' is a garbage value
273 if (offset > 0) {
274 /* Obtain pointer to start of input data */
275 parserutils_inputstream_peek(stream, 0,
276 &ch, &chlen);
277 /* Copy from it up to the start of the NUL */
278 if (textplain_copy_utf8_data(c, ch,
279 offset) == false0)
280 return false0;
281 }
282
283 /* Emit U+FFFD */
284 if (textplain_copy_utf8_data(c, u_fffd, 3) == false0)
285 return false0;
286
287 /* Advance inputstream past the NUL we just read */
288 parserutils_inputstream_advance(stream, offset + 1);
289 /* Reset the read offset */
290 offset = 0;
291 } else {
292 /* Accumulate input */
293 offset += chlen;
294
295 if (offset > CHUNK32768) {
296 /* Obtain pointer to start of input data */
297 parserutils_inputstream_peek(stream, 0,
298 &ch, &chlen);
299
300 /* Emit the data we've read */
301 if (textplain_copy_utf8_data(c, ch,
302 offset) == false0)
303 return false0;
304
305 /* Advance the inputstream */
306 parserutils_inputstream_advance(stream, offset);
307 /* Reset the read offset */
308 offset = 0;
309 }
310 }
311 }
312
313 if (offset > 0) {
314 /* Obtain pointer to start of input data */
315 parserutils_inputstream_peek(stream, 0, &ch, &chlen);
316 /* Emit any data remaining */
317 if (textplain_copy_utf8_data(c, ch, offset) == false0)
318 return false0;
319
320 /* Advance the inputstream past the data we've read */
321 parserutils_inputstream_advance(stream, offset);
322 }
323
324 return true1;
325}
326
327
328/**
329 * Process data for CONTENT_TEXTPLAIN.
330 */
331static bool_Bool
332textplain_process_data(struct content *c, const char *data, unsigned int size)
333{
334 textplain_content *text = (textplain_content *) c;
335 parserutils_inputstream *stream = text->inputstream;
336 parserutils_error error;
337
338 error = parserutils_inputstream_append(stream,
339 (const uint8_t *) data, size);
340 if (error != PARSERUTILS_OK) {
10
Assuming 'error' is equal to PARSERUTILS_OK
11
Taking false branch
341 goto no_memory;
342 }
343
344 if (textplain_drain_input(text, stream, PARSERUTILS_NEEDDATA) == false0)
12
Calling 'textplain_drain_input'
345 goto no_memory;
346
347 return true1;
348
349no_memory:
350 content_broadcast_error(c, NSERROR_NOMEM, NULL((void*)0));
351 return false0;
352}
353
354
355/**
356 * Convert a CONTENT_TEXTPLAIN for display.
357 */
358static bool_Bool textplain_convert(struct content *c)
359{
360 textplain_content *text = (textplain_content *) c;
361 parserutils_inputstream *stream = text->inputstream;
362 parserutils_error error;
363
364 error = parserutils_inputstream_append(stream, NULL((void*)0), 0);
365 if (error != PARSERUTILS_OK) {
366 return false0;
367 }
368
369 if (textplain_drain_input(text, stream, PARSERUTILS_EOF) == false0)
370 return false0;
371
372 parserutils_inputstream_destroy(stream);
373 text->inputstream = NULL((void*)0);
374
375 content_set_ready(c);
376 content_set_done(c);
377 content_set_status(c, messages_get("Done"));
378
379 return true1;
380}
381
382
383/**
384 * Calculate the line height, in pixels
385 *
386 * \return Line height, in pixels
387 */
388static float textplain_line_height(void)
389{
390 /* Size is in points, so convert to pixels.
391 * Then use a constant line height of 1.2 x font size.
392 */
393 return FIXTOFLT(FDIV((FMUL(FLTTOFIX(1.2), FMUL(nscss_screen_dpi, INTTOFIX((textplain_style.size / PLOT_STYLE_SCALE))))), F_72))((float) ((css_divide_fixed((((css_multiply_fixed((((css_fixed
) ((1.2) * (float) (1 << 10)))), ((css_multiply_fixed((
nscss_screen_dpi), ((css_int_to_fixed((textplain_style.size /
(1 << (10))))))))))))), (0x00012000)))) / (float) (1 <<
10))
;
394}
395
396
397/**
398 * Reformat a CONTENT_TEXTPLAIN to a new width.
399 */
400static void textplain_reformat(struct content *c, int width, int height)
401{
402 textplain_content *text = (textplain_content *) c;
403 char *utf8_data = text->utf8_data;
404 size_t utf8_data_size = text->utf8_data_size;
405 unsigned long line_count = 0;
406 struct textplain_line *line = text->physical_line;
407 struct textplain_line *line1;
408 size_t i, space, col;
409 size_t columns = 80;
410 int character_width;
411 size_t line_start;
412 nserror res;
413
414 NSLOG(netsurf, INFO, "content %p w:%d h:%d", c, width, height)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/text/textplain.c", sizeof
("content/handlers/text/textplain.c") - 1, __PRETTY_FUNCTION__
, sizeof(__PRETTY_FUNCTION__) - 1, 414, }; nslog__log(&_nslog_ctx
, "content %p w:%d h:%d", c, width, height); } } while(0)
;
415
416 /* compute available columns (assuming monospaced font) - use 8
417 * characters for better accuracy
418 */
419 res = guit->layout->width(&textplain_style,
420 "ABCDEFGH", 8,
421 &character_width);
422 if (res != NSERROR_OK) {
423 return;
424 }
425
426 columns = (width - MARGIN4 - MARGIN4) * 8 / character_width;
427 textplain_tab_width = (TAB_WIDTH8 * character_width) / 8;
428
429 text->formatted_width = width;
430
431 text->physical_line_count = 0;
432
433 if (!line) {
434 text->physical_line = line =
435 malloc(sizeof(struct textplain_line) * (1024 + 3));
436 if (!line)
437 goto no_memory;
438 }
439
440 line[line_count++].start = line_start = 0;
441 space = 0;
442 i = 0;
443 col = 0;
444 while (i < utf8_data_size) {
445 size_t csize; /* number of bytes in character */
446 uint32_t chr;
447 bool_Bool term;
448 size_t next_col;
449 parserutils_error perror;
450
451 perror = parserutils_charset_utf8_to_ucs4((const uint8_t *)utf8_data + i, utf8_data_size - i, &chr, &csize);
452 if (perror != PARSERUTILS_OK) {
453 chr = 0xfffd;
454 }
455
456 term = (chr == '\n' || chr == '\r');
457
458 next_col = col + 1;
459
460 if (chr == '\t') {
461 next_col = (next_col + TAB_WIDTH8 - 1) & ~(TAB_WIDTH8 - 1);
462 }
463
464 if (term || next_col >= columns) {
465 if (line_count % 1024 == 0) {
466 line1 = realloc(line,
467 sizeof(struct textplain_line) *
468 (line_count + 1024 + 3));
469 if (!line1)
470 goto no_memory;
471 text->physical_line = line = line1;
472 }
473
474 if (term) {
475 line[line_count-1].length = i - line_start;
476
477 /* skip second char of CR/LF or LF/CR pair */
478 if (i + 1 < utf8_data_size &&
479 utf8_data[i+1] != utf8_data[i] &&
480 (utf8_data[i+1] == '\n' ||
481 utf8_data[i+1] == '\r')) {
482 i++;
483 }
484 } else {
485 if (space) {
486 /* break at last space in line */
487 i = space;
488 line[line_count-1].length = (i + 1) - line_start;
489 } else
490 line[line_count-1].length = i - line_start;
491 }
492
493 line[line_count++].start = line_start = i + 1;
494 col = 0;
495 space = 0;
496 } else {
497 col++;
498 if (chr == ' ')
499 space = i;
500 }
501 i += csize;
502 }
503 line[line_count-1].length = i - line[line_count-1].start;
504 line[line_count].start = utf8_data_size;
505
506 text->physical_line_count = line_count;
507 c->width = width;
508 c->height = line_count * textplain_line_height() + MARGIN4 + MARGIN4;
509
510 return;
511
512no_memory:
513 NSLOG(netsurf, INFO, "out of memory (line_count %lu)", line_count)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/text/textplain.c", sizeof
("content/handlers/text/textplain.c") - 1, __PRETTY_FUNCTION__
, sizeof(__PRETTY_FUNCTION__) - 1, 513, }; nslog__log(&_nslog_ctx
, "out of memory (line_count %lu)", line_count); } } while(0)
;
514 return;
515}
516
517
518/**
519 * Destroy a CONTENT_TEXTPLAIN and free all resources it owns.
520 */
521
522static void textplain_destroy(struct content *c)
523{
524 textplain_content *text = (textplain_content *) c;
525
526 lwc_string_unref(text->encoding){ lwc_string *__lwc_s = (text->encoding); if (__lwc_s != (
(void*)0)) { __lwc_s->refcnt--; if ((__lwc_s->refcnt ==
0) || ((__lwc_s->refcnt == 1) && (__lwc_s->insensitive
== __lwc_s))) lwc_string_destroy(__lwc_s); } }
;
527
528 if (text->inputstream != NULL((void*)0)) {
529 parserutils_inputstream_destroy(text->inputstream);
530 }
531
532 if (text->physical_line != NULL((void*)0)) {
533 free(text->physical_line);
534 }
535
536 if (text->utf8_data != NULL((void*)0)) {
537 free(text->utf8_data);
538 }
539
540 if (text->sel != NULL((void*)0)) {
541 selection_destroy(text->sel);
542 }
543}
544
545
546static nserror textplain_clone(const struct content *old, struct content **newc)
1
[debug] analyzing from textplain_clone
547{
548 const textplain_content *old_text = (textplain_content *) old;
549 textplain_content *text;
550 nserror error;
551 const uint8_t *data;
552 size_t size;
553
554 text = calloc(1, sizeof(textplain_content));
555 if (text == NULL((void*)0))
2
Assuming 'text' is not equal to NULL
3
Taking false branch
556 return NSERROR_NOMEM;
557
558 error = content__clone(old, &text->base);
559 if (error != NSERROR_OK) {
4
Assuming 'error' is equal to NSERROR_OK
5
Taking false branch
560 content_destroy(&text->base);
561 return error;
562 }
563
564 /* Simply replay create/process/convert */
565 error = textplain_create_internal(text, old_text->encoding);
566 if (error
5.1
'error' is equal to NSERROR_OK
5.1
'error' is equal to NSERROR_OK
!= NSERROR_OK) {
6
Taking false branch
567 content_destroy(&text->base);
568 return error;
569 }
570
571 data = content__get_source_data(&text->base, &size);
572 if (size > 0) {
7
Assuming 'size' is > 0
8
Taking true branch
573 if (textplain_process_data(&text->base,
9
Calling 'textplain_process_data'
574 (const char *)data,
575 size) == false0) {
576 content_destroy(&text->base);
577 return NSERROR_NOMEM;
578 }
579 }
580
581 if (old->status == CONTENT_STATUS_READY ||
582 old->status == CONTENT_STATUS_DONE) {
583 if (textplain_convert(&text->base) == false0) {
584 content_destroy(&text->base);
585 return NSERROR_CLONE_FAILED;
586 }
587 }
588
589 return NSERROR_OK;
590}
591
592
593static content_type textplain_content_type(void)
594{
595 return CONTENT_TEXTPLAIN;
596}
597
598
599/**
600 * Return byte offset within UTF8 textplain content.
601 *
602 * given the co-ordinates of a point within a textplain content. 'dir'
603 * specifies the direction in which to search (-1 = above-left, +1 =
604 * below-right) if the co-ordinates are not contained within a line.
605 *
606 * \param[in] c content of type CONTENT_TEXTPLAIN
607 * \param[in] x x ordinate of point
608 * \param[in] y y ordinate of point
609 * \param[in] dir direction of search if not within line
610 * \return byte offset of character containing (or nearest to) point
611 */
612static size_t
613textplain_offset_from_coords(struct content *c, int x, int y, int dir)
614{
615 textplain_content *textc = (textplain_content *) c;
616 float line_height = textplain_line_height();
617 struct textplain_line *line;
618 const char *text;
619 unsigned nlines;
620 size_t length;
621 int idx;
622
623 assert(c != NULL)((c != ((void*)0)) ? (void) (0) : __assert_fail ("c != NULL",
"content/handlers/text/textplain.c", 623, __extension__ __PRETTY_FUNCTION__
))
;
624
625 y = (int)((float)(y - MARGIN4) / line_height);
626 x -= MARGIN4;
627
628 nlines = textc->physical_line_count;
629 if (!nlines)
630 return 0;
631
632 if (y <= 0) y = 0;
633 else if ((unsigned)y >= nlines)
634 y = nlines - 1;
635
636 line = &textc->physical_line[y];
637 text = textc->utf8_data + line->start;
638 length = line->length;
639 idx = 0;
640
641 while (x > 0) {
642 size_t next_offset = 0;
643 int width = INT_MAX2147483647;
644
645 while (next_offset < length && text[next_offset] != '\t') {
646 next_offset = utf8_next(text, length, next_offset);
647 }
648
649 if (next_offset < length) {
650 guit->layout->width(&textplain_style,
651 text,
652 next_offset,
653 &width);
654 }
655
656 if (x <= width) {
657 int pixel_offset;
658 size_t char_offset;
659
660 guit->layout->position(&textplain_style,
661 text, next_offset, x,
662 &char_offset, &pixel_offset);
663
664 idx += char_offset;
665 break;
666 }
667
668 x -= width;
669 length -= next_offset;
670 text += next_offset;
671 idx += next_offset;
672
673 /* check if it's within the tab */
674 width = textplain_tab_width - (width % textplain_tab_width);
675 if (x <= width) break;
676
677 x -= width;
678 length--;
679 text++;
680 idx++;
681 }
682
683 return line->start + idx;
684}
685
686
687/**
688 * Handle mouse clicks and movements in a TEXTPLAIN content window.
689 *
690 * \param c content of type textplain
691 * \param bw browser window
692 * \param mouse mouse state on action
693 * \param x coordinate of mouse
694 * \param y coordinate of mouse
695 */
696static nserror
697textplain_mouse_action(struct content *c,
698 struct browser_window *bw,
699 browser_mouse_state mouse,
700 int x, int y)
701{
702 textplain_content *text = (textplain_content *) c;
703 browser_pointer_shape pointer = BROWSER_POINTER_DEFAULT;
704 union content_msg_data msg_data;
705 const char *status = 0;
706 size_t idx;
707 int dir = 0;
708
709 browser_window_set_drag_type(bw, DRAGGING_NONE, NULL((void*)0));
710
711 idx = textplain_offset_from_coords(c, x, y, dir);
712 if (selection_click(text->sel, text->bw, mouse, idx)) {
713
714 if (selection_dragging(text->sel)) {
715 browser_window_set_drag_type(bw,
716 DRAGGING_SELECTION, NULL((void*)0));
717 status = messages_get("Selecting");
718 }
719
720 } else {
721 if (mouse & (BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_DRAG_2)) {
722 browser_window_page_drag_start(bw, x, y);
723 pointer = BROWSER_POINTER_MOVE;
724 }
725 }
726
727 msg_data.explicit_status_text = status;
728 content_broadcast(c, CONTENT_MSG_STATUS, &msg_data);
729
730 msg_data.pointer = pointer;
731 content_broadcast(c, CONTENT_MSG_POINTER, &msg_data);
732
733 return NSERROR_OK;
734}
735
736
737/**
738 * Handle mouse tracking (including drags) in a TEXTPLAIN content window.
739 *
740 * \param c content of type textplain
741 * \param bw browser window
742 * \param mouse state of mouse buttons and modifier keys
743 * \param x coordinate of mouse
744 * \param y coordinate of mouse
745 */
746static nserror
747textplain_mouse_track(struct content *c,
748 struct browser_window *bw,
749 browser_mouse_state mouse,
750 int x, int y)
751{
752 textplain_content *text = (textplain_content *) c;
753
754 if (browser_window_get_drag_type(bw) == DRAGGING_SELECTION && !mouse) {
755 int dir = -1;
756 size_t idx;
757
758 if (selection_dragging_start(text->sel))
759 dir = 1;
760
761 idx = textplain_offset_from_coords(c, x, y, dir);
762 selection_track(text->sel, mouse, idx);
763
764 browser_window_set_drag_type(bw, DRAGGING_NONE, NULL((void*)0));
765 }
766
767 switch (browser_window_get_drag_type(bw)) {
768
769 case DRAGGING_SELECTION: {
770 int dir = -1;
771 size_t idx;
772
773 if (selection_dragging_start(text->sel)) dir = 1;
774
775 idx = textplain_offset_from_coords(c, x, y, dir);
776 selection_track(text->sel, mouse, idx);
777 }
778 break;
779
780 default:
781 textplain_mouse_action(c, bw, mouse, x, y);
782 break;
783 }
784
785 return NSERROR_OK;
786}
787
788
789/**
790 * Handle keypresses.
791 *
792 * \param c content of type CONTENT_TEXTPLAIN
793 * \param key The UCS4 character codepoint
794 * \return true if key handled, false otherwise
795 */
796static bool_Bool textplain_keypress(struct content *c, uint32_t key)
797{
798 textplain_content *text = (textplain_content *) c;
799 struct selection *sel = text->sel;
800
801 switch (key) {
802 case NS_KEY_COPY_SELECTION:
803 selection_copy_to_clipboard(sel);
804 return true1;
805
806 case NS_KEY_CLEAR_SELECTION:
807 selection_clear(sel, true1);
808 return true1;
809
810 case NS_KEY_SELECT_ALL:
811 selection_select_all(sel);
812 return true1;
813
814 case NS_KEY_ESCAPE:
815 /* if there's no selection, leave Escape for the caller */
816 return selection_clear(sel, true1);
817 }
818
819 return false0;
820}
821
822
823/**
824 * Redraw a text string with highlighting
825 * (for selection/search)
826 *
827 * \param utf8_text pointer to UTF-8 text string
828 * \param utf8_len length of string, in bytes
829 * \param offset byte offset within textual representation
830 * \param x x ordinate at which to plot text
831 * \param y y ordinate at which to plot text
832 * \param clip pointer to current clip rectangle
833 * \param height height of text string
834 * \param scale current display scale (1.0 = 100%)
835 * \param text Content being redrawn.
836 * \param sel Selection context
837 * \param search Search context
838 * \param ctx current redraw context
839 * \return true iff successful and redraw should proceed
840 */
841static bool_Bool
842text_draw(const char *utf8_text,
843 size_t utf8_len,
844 size_t offset,
845 int x,
846 int y,
847 const struct rect *clip,
848 int height,
849 float scale,
850 textplain_content *text,
851 const struct selection *sel,
852 const struct redraw_context *ctx)
853{
854 bool_Bool highlighted = false0;
855 plot_font_style_t plot_fstyle;
856 nserror res;
857
858 /* Need scaled text size to pass to plotters */
859 plot_fstyle = textplain_style;
860 plot_fstyle.size *= scale;
861
862 /* is this box part of a selection? */
863 if (ctx->interactive == true1) {
864 unsigned len = utf8_len;
865 unsigned start_idx;
866 unsigned end_idx;
867
868 /* first try the browser window's current selection */
869 if (selection_highlighted(sel,
870 offset,
871 offset + len,
872 &start_idx,
873 &end_idx)) {
874 highlighted = true1;
875 }
876
877 /* what about the current search operation, if any? */
878 if (!highlighted &&
879 (text->base.textsearch.context != NULL((void*)0)) &&
880 content_textsearch_ishighlighted(text->base.textsearch.context,
881 offset,
882 offset + len,
883 &start_idx,
884 &end_idx)) {
885 highlighted = true1;
886 }
887
888 /* \todo make search terms visible within selected text */
889 if (highlighted) {
890 struct rect r;
891 unsigned endtxt_idx = end_idx;
892 bool_Bool clip_changed = false0;
893 bool_Bool text_visible = true1;
894 int startx, endx;
895 plot_style_t pstyle_fill_hback = *plot_style_fill_white;
896 plot_font_style_t fstyle_hback = plot_fstyle;
897
898 if (end_idx > utf8_len) {
899 /* adjust for trailing space, not present in
900 * utf8_text
901 */
902 assert(end_idx == utf8_len + 1)((end_idx == utf8_len + 1) ? (void) (0) : __assert_fail ("end_idx == utf8_len + 1"
, "content/handlers/text/textplain.c", 902, __extension__ __PRETTY_FUNCTION__
))
;
903 endtxt_idx = utf8_len;
904 }
905
906 res = guit->layout->width(&textplain_style,
907 utf8_text,
908 start_idx,
909 &startx);
910 if (res != NSERROR_OK) {
911 startx = 0;
912 }
913
914 res = guit->layout->width(&textplain_style,
915 utf8_text,
916 endtxt_idx,
917 &endx);
918 if (res != NSERROR_OK) {
919 endx = 0;
920 }
921
922 if (scale != 1.0) {
923 startx *= scale;
924 endx *= scale;
925 }
926
927 /* draw any text preceding highlighted portion */
928 if (start_idx > 0) {
929 res = ctx->plot->text(ctx,
930 &plot_fstyle,
931 x,
932 y + (int)(height * 0.75 * scale),
933 utf8_text,
934 start_idx);
935 if (res != NSERROR_OK) {
936 return false0;
937 }
938 }
939
940 pstyle_fill_hback.fill_colour = textplain_style.foreground;
941
942 /* highlighted portion */
943 r.x0 = x + startx;
944 r.y0 = y;
945 r.x1 = x + endx;
946 r.y1 = y + height * scale;
947 res = ctx->plot->rectangle(ctx, &pstyle_fill_hback, &r);
948 if (res != NSERROR_OK) {
949 return false0;
950 }
951
952 if (start_idx > 0) {
953 int px0 = max(x + startx, clip->x0)(((x + startx)>(clip->x0))?(x + startx):(clip->x0));
954 int px1 = min(x + endx, clip->x1)(((x + endx)<(clip->x1))?(x + endx):(clip->x1));
955
956 if (px0 < px1) {
957 r.x0 = px0;
958 r.y0 = clip->y0;
959 r.x1 = px1;
960 r.y1 = clip->y1;
961 res = ctx->plot->clip(ctx, &r);
962 if (res != NSERROR_OK) {
963 return false0;
964 }
965
966 clip_changed = true1;
967 } else {
968 text_visible = false0;
969 }
970 }
971
972 fstyle_hback.background =
973 pstyle_fill_hback.fill_colour;
974 fstyle_hback.foreground = colour_to_bw_furthest(((((((pstyle_fill_hback.fill_colour & 0x0000ff) * 77) >>
8) + (((pstyle_fill_hback.fill_colour & 0x00ff00) * 151)
>> 16) + (((pstyle_fill_hback.fill_colour & 0xff0000
) * 30) >> 24)) > (0xff / 2)) ? 0x000000 : 0xffffff)
975 pstyle_fill_hback.fill_colour)((((((pstyle_fill_hback.fill_colour & 0x0000ff) * 77) >>
8) + (((pstyle_fill_hback.fill_colour & 0x00ff00) * 151)
>> 16) + (((pstyle_fill_hback.fill_colour & 0xff0000
) * 30) >> 24)) > (0xff / 2)) ? 0x000000 : 0xffffff)
;
976
977 if (text_visible &&
978 (ctx->plot->text(ctx,
979 &fstyle_hback,
980 x,
981 y + (int)(height * 0.75 * scale),
982 utf8_text,
983 endtxt_idx) != NSERROR_OK)) {
984 return false0;
985 }
986
987 /* draw any text succeeding highlighted portion */
988 if (endtxt_idx < utf8_len) {
989 int px0 = max(x + endx, clip->x0)(((x + endx)>(clip->x0))?(x + endx):(clip->x0));
990 if (px0 < clip->x1) {
991
992 r.x0 = px0;
993 r.y0 = clip->y0;
994 r.x1 = clip->x1;
995 r.y1 = clip->y1;
996 res = ctx->plot->clip(ctx, &r);
997 if (res != NSERROR_OK) {
998 return false0;
999 }
1000
1001 clip_changed = true1;
1002
1003 res = ctx->plot->text(ctx,
1004 &plot_fstyle,
1005 x,
1006 y + (int)(height * 0.75 * scale),
1007 utf8_text,
1008 utf8_len);
1009 if (res != NSERROR_OK) {
1010 return false0;
1011 }
1012 }
1013 }
1014
1015 if (clip_changed &&
1016 (ctx->plot->clip(ctx, clip) != NSERROR_OK)) {
1017 return false0;
1018 }
1019 }
1020 }
1021
1022 if (!highlighted) {
1023 res = ctx->plot->text(ctx,
1024 &plot_fstyle,
1025 x,
1026 y + (int) (height * 0.75 * scale),
1027 utf8_text,
1028 utf8_len);
1029 if (res != NSERROR_OK) {
1030 return false0;
1031 }
1032 }
1033 return true1;
1034}
1035
1036
1037/**
1038 * Draw a CONTENT_TEXTPLAIN using the current set of plotters (plot).
1039 *
1040 * x, y, clip_[xy][01] are in target coordinates.
1041 *
1042 * \param c content of type CONTENT_TEXTPLAIN
1043 * \param data redraw data for this content redraw
1044 * \param clip current clip region
1045 * \param ctx current redraw context
1046 * \return true if successful, false otherwise
1047 */
1048static bool_Bool
1049textplain_redraw(struct content *c,
1050 struct content_redraw_data *data,
1051 const struct rect *clip,
1052 const struct redraw_context *ctx)
1053{
1054 textplain_content *text = (textplain_content *) c;
1055 struct browser_window *bw = text->bw;
1056 char *utf8_data = text->utf8_data;
1057 long lineno;
1058 int x = data->x;
1059 int y = data->y;
1060 unsigned long line_count = text->physical_line_count;
1061 float line_height = textplain_line_height();
1062 float scaled_line_height = line_height * data->scale;
1063 long line0 = (clip->y0 - y * data->scale) / scaled_line_height - 1;
1064 long line1 = (clip->y1 - y * data->scale) / scaled_line_height + 1;
1065 struct textplain_line *line = text->physical_line;
1066 size_t length;
1067 plot_style_t *plot_style_highlight;
1068 nserror res;
1069
1070 if (line0 < 0)
1071 line0 = 0;
1072 if (line1 < 0)
1073 line1 = 0;
1074 if (line_count < (unsigned long) line0)
1075 line0 = line_count;
1076 if (line_count < (unsigned long) line1)
1077 line1 = line_count;
1078 if (line1 < line0)
1079 line1 = line0;
1080
1081 res = ctx->plot->rectangle(ctx, plot_style_fill_white, clip);
1082 if (res != NSERROR_OK) {
1083 return false0;
1084 }
1085
1086 if (!line)
1087 return true1;
1088
1089 /* choose a suitable background colour for any highlighted text */
1090 if ((data->background_colour & 0x808080) == 0x808080)
1091 plot_style_highlight = plot_style_fill_black;
1092 else
1093 plot_style_highlight = plot_style_fill_white;
1094
1095 /* Set up font plot style */
1096 textplain_style.background = data->background_colour;
1097
1098 x = (x + MARGIN4) * data->scale;
1099 y = (y + MARGIN4) * data->scale;
1100 for (lineno = line0; lineno != line1; lineno++) {
1101 const char *text_d = utf8_data + line[lineno].start;
1102 int tab_width = textplain_tab_width * data->scale;
1103 size_t offset = 0;
1104 int tx = x;
1105
1106 if (!tab_width) tab_width = 1;
1107
1108 length = line[lineno].length;
1109 if (!length)
1110 continue;
1111
1112 while (offset < length) {
1113 size_t next_offset = offset;
1114 int width;
1115 int ntx;
1116 nserror res;
1117
1118 while ((next_offset < length) &&
1119 (text_d[next_offset] != '\t')) {
1120 next_offset = utf8_next(text_d,
1121 length,
1122 next_offset);
1123 }
1124
1125 if (!text_draw(text_d + offset,
1126 next_offset - offset,
1127 line[lineno].start + offset,
1128 tx,
1129 y + (lineno * scaled_line_height),
1130 clip,
1131 line_height,
1132 data->scale,
1133 text,
1134 text->sel,
1135 ctx)) {
1136 return false0;
1137 }
1138
1139 if (next_offset >= length)
1140 break;
1141
1142 res = guit->layout->width(&textplain_style,
1143 &text_d[offset],
1144 next_offset - offset,
1145 &width);
1146 /* locate end of string and align to next tab position */
1147 if (res == NSERROR_OK) {
1148 tx += (int)(width * data->scale);
1149 }
1150
1151 ntx = x + ((1 + (tx - x) / tab_width) * tab_width);
1152
1153 /* if the tab character lies within the
1154 * selection, if any, then we must draw it as
1155 * a filled rectangle so that it's consistent
1156 * with background of the selected text
1157 */
1158
1159 if (bw) {
1160 unsigned tab_ofst = line[lineno].start + next_offset;
1161 struct selection *sel = text->sel;
1162 bool_Bool highlighted = false0;
1163
1164 unsigned start_idx, end_idx;
1165 if (selection_highlighted(sel,
1166 tab_ofst,
1167 tab_ofst + 1,
1168 &start_idx,
1169 &end_idx)) {
1170 highlighted = true1;
1171 }
1172
1173
1174 if (!highlighted &&
1175 (c->textsearch.context != NULL((void*)0))) {
1176 unsigned start_idx, end_idx;
1177 if (content_textsearch_ishighlighted(
1178 c->textsearch.context,
1179 tab_ofst,
1180 tab_ofst + 1,
1181 &start_idx,
1182 &end_idx)) {
1183 highlighted = true1;
1184 }
1185 }
1186
1187 if (highlighted) {
1188 struct rect rect;
1189 rect.x0 = tx;
1190 rect.y0 = y + (lineno * scaled_line_height);
1191 rect.x1 = ntx;
1192 rect.y1 = rect.y0 + scaled_line_height;
1193 res = ctx->plot->rectangle(ctx,
1194 plot_style_highlight,
1195 &rect);
1196 if (res != NSERROR_OK) {
1197 return false0;
1198 }
1199 }
1200 }
1201
1202 offset = next_offset + 1;
1203 tx = ntx;
1204 }
1205 }
1206
1207 return true1;
1208}
1209
1210
1211/**
1212 * Handle a window containing a CONTENT_TEXTPLAIN being opened.
1213 */
1214static nserror
1215textplain_open(struct content *c,
1216 struct browser_window *bw,
1217 struct content *page,
1218 struct object_params *params)
1219{
1220 textplain_content *text = (textplain_content *) c;
1221
1222 text->bw = bw;
1223
1224 /* text selection */
1225 selection_init(text->sel);
1226
1227 return NSERROR_OK;
1228}
1229
1230
1231/**
1232 * Handle a window containing a CONTENT_TEXTPLAIN being closed.
1233 */
1234static nserror textplain_close(struct content *c)
1235{
1236 textplain_content *text = (textplain_content *) c;
1237
1238 text->bw = NULL((void*)0);
1239
1240 return NSERROR_OK;
1241}
1242
1243
1244/**
1245 * Return an textplain content's selection context
1246 */
1247static char *textplain_get_selection(struct content *c)
1248{
1249 textplain_content *text = (textplain_content *) c;
1250
1251 return selection_get_copy(text->sel);
1252}
1253
1254
1255/**
1256 * Convert a character offset within a line of text into the
1257 * horizontal co-ordinate
1258 *
1259 * The conversion takes into account the font being used and any tabs
1260 * in the text
1261 *
1262 * \param text line of text
1263 * \param offset char offset within text
1264 * \param length line length
1265 * \return x ordinate
1266 */
1267static int
1268textplain_coord_from_offset(const char *text, size_t offset, size_t length)
1269{
1270 int x = 0;
1271
1272 while (offset > 0) {
1273 size_t next_offset = 0;
1274 int tx;
1275
1276 while (next_offset < offset && text[next_offset] != '\t') {
1277 next_offset = utf8_next(text, length, next_offset);
1278 }
1279
1280 guit->layout->width(&textplain_style, text, next_offset, &tx);
1281
1282 x += tx;
1283
1284 if (next_offset >= offset)
1285 break;
1286
1287 /* align to next tab boundary */
1288 next_offset++;
1289 x = (1 + (x / textplain_tab_width)) * textplain_tab_width;
1290 offset -= next_offset;
1291 text += next_offset;
1292 length -= next_offset;
1293 }
1294
1295 return x;
1296}
1297
1298
1299/**
1300 * Retrieve number of lines in content
1301 *
1302 * \param[in] c Content to retrieve line count from
1303 * \return Number of lines
1304 */
1305static unsigned long textplain_line_count(struct content *c)
1306{
1307 textplain_content *text = (textplain_content *) c;
1308
1309 assert(c != NULL)((c != ((void*)0)) ? (void) (0) : __assert_fail ("c != NULL",
"content/handlers/text/textplain.c", 1309, __extension__ __PRETTY_FUNCTION__
))
;
1310
1311 return text->physical_line_count;
1312}
1313
1314
1315/**
1316 * Return a pointer to the requested line of text.
1317 *
1318 * \param[in] c content of type CONTENT_TEXTPLAIN
1319 * \param[in] lineno line number
1320 * \param[out] poffset receives byte offset of line start within text
1321 * \param[out] plen receives length of returned line
1322 * \return pointer to text, or NULL if invalid line number
1323 */
1324static char *
1325textplain_get_line(struct content *c,
1326 unsigned lineno,
1327 size_t *poffset,
1328 size_t *plen)
1329{
1330 textplain_content *text = (textplain_content *) c;
1331 struct textplain_line *line;
1332
1333 assert(c != NULL)((c != ((void*)0)) ? (void) (0) : __assert_fail ("c != NULL",
"content/handlers/text/textplain.c", 1333, __extension__ __PRETTY_FUNCTION__
))
;
1334
1335 if (lineno >= text->physical_line_count)
1336 return NULL((void*)0);
1337 line = &text->physical_line[lineno];
1338
1339 *poffset = line->start;
1340 *plen = line->length;
1341 return text->utf8_data + line->start;
1342}
1343
1344
1345/**
1346 * Find line number of byte in text
1347 *
1348 * Given a byte offset within the text, return the line number
1349 * of the line containing that offset.
1350 *
1351 * \param[in] c content of type CONTENT_TEXTPLAIN
1352 * \param[in] offset byte offset within textual representation
1353 * \return line number, or -1 if offset invalid (larger than size)
1354 */
1355static int textplain_find_line(struct content *c, unsigned offset)
1356{
1357 textplain_content *text = (textplain_content *) c;
1358 struct textplain_line *line;
1359 int nlines;
1360 int lineno = 0;
1361
1362 assert(c != NULL)((c != ((void*)0)) ? (void) (0) : __assert_fail ("c != NULL",
"content/handlers/text/textplain.c", 1362, __extension__ __PRETTY_FUNCTION__
))
;
1363
1364 line = text->physical_line;
1365 nlines = text->physical_line_count;
1366
1367 if (offset > text->utf8_data_size) {
1368 return -1;
1369 }
1370
1371/* \todo - implement binary search here */
1372 while (lineno < nlines && line[lineno].start < offset) {
1373 lineno++;
1374 }
1375 if (line[lineno].start > offset) {
1376 lineno--;
1377 }
1378
1379 return lineno;
1380}
1381
1382
1383/**
1384 * Finds all occurrences of a given string in a textplain content
1385 *
1386 * \param c the content to be searched
1387 * \param context The search context to add the entry to.
1388 * \param pattern the string pattern to search for
1389 * \param p_len pattern length
1390 * \param case_sens whether to perform a case sensitive search
1391 * \return NSERROR_OK on success else error code on faliure
1392 */
1393static nserror
1394textplain_textsearch_find(struct content *c,
1395 struct textsearch_context *context,
1396 const char *pattern,
1397 int p_len,
1398 bool_Bool case_sens)
1399{
1400 int nlines = textplain_line_count(c);
1401 int line;
1402 nserror res = NSERROR_OK;
1403
1404 for(line = 0; line < nlines; line++) {
1405 size_t offset, length;
1406 const char *text;
1407
1408 text = textplain_get_line(c, line, &offset, &length);
1409 if (text) {
1410 while (length > 0) {
1411 unsigned match_length;
1412 size_t start_idx;
1413 const char *new_text;
1414 const char *pos;
1415
1416 pos = content_textsearch_find_pattern(
1417 text,
1418 length,
1419 pattern,
1420 p_len,
1421 case_sens,
1422 &match_length);
1423 if (!pos)
1424 break;
1425
1426 /* found string in line => add to list */
1427 start_idx = offset + (pos - text);
1428 res = content_textsearch_add_match(context,
1429 start_idx,
1430 start_idx + match_length,
1431 NULL((void*)0),
1432 NULL((void*)0));
1433 if (res != NSERROR_OK) {
1434 return res;
1435 }
1436
1437 new_text = pos + match_length;
1438 offset += (new_text - text);
1439 length -= (new_text - text);
1440 text = new_text;
1441 }
1442 }
1443 }
1444
1445 return res;
1446}
1447
1448
1449/**
1450 * Given a range of byte offsets within a UTF8 textplain content,
1451 * return a box that fully encloses the text
1452 *
1453 * \param[in] c content of type CONTENT_TEXTPLAIN
1454 * \param[in] start byte offset of start of text range
1455 * \param[in] end byte offset of end
1456 * \param[out] r rectangle to be completed
1457 */
1458static void
1459textplain_coords_from_range(struct content *c,
1460 unsigned start,
1461 unsigned end,
1462 struct rect *r)
1463{
1464 textplain_content *text = (textplain_content *) c;
1465 float line_height = textplain_line_height();
1466 char *utf8_data;
1467 struct textplain_line *line;
1468 unsigned lineno = 0;
1469 unsigned nlines;
1470
1471 assert(c != NULL)((c != ((void*)0)) ? (void) (0) : __assert_fail ("c != NULL",
"content/handlers/text/textplain.c", 1471, __extension__ __PRETTY_FUNCTION__
))
;
1472 assert(start <= end)((start <= end) ? (void) (0) : __assert_fail ("start <= end"
, "content/handlers/text/textplain.c", 1472, __extension__ __PRETTY_FUNCTION__
))
;
1473 assert(end <= text->utf8_data_size)((end <= text->utf8_data_size) ? (void) (0) : __assert_fail
("end <= text->utf8_data_size", "content/handlers/text/textplain.c"
, 1473, __extension__ __PRETTY_FUNCTION__))
;
1474
1475 utf8_data = text->utf8_data;
1476 nlines = text->physical_line_count;
1477 line = text->physical_line;
1478
1479 /* find start */
1480 lineno = textplain_find_line(c, start);
1481
1482 r->y0 = (int)(MARGIN4 + lineno * line_height);
1483
1484 if (lineno + 1 <= nlines || line[lineno + 1].start >= end) {
1485 /* \todo - it may actually be more efficient just to
1486 * run forwards most of the time
1487 */
1488
1489 /* find end */
1490 lineno = textplain_find_line(c, end);
1491
1492 r->x0 = 0;
1493 r->x1 = text->formatted_width;
1494 } else {
1495 /* single line */
1496 const char *text = utf8_data + line[lineno].start;
1497
1498 r->x0 = textplain_coord_from_offset(text,
1499 start - line[lineno].start,
1500 line[lineno].length);
1501
1502 r->x1 = textplain_coord_from_offset(text,
1503 end - line[lineno].start,
1504 line[lineno].length);
1505 }
1506
1507 r->y1 = (int)(MARGIN4 + (lineno + 1) * line_height);
1508}
1509
1510
1511/**
1512 * Return a pointer to the raw UTF-8 data, as opposed to the reformatted
1513 * text to fit the window width. Thus only hard newlines are preserved
1514 * in the saved/copied text of a selection.
1515 *
1516 * \param[in] c content of type CONTENT_TEXTPLAIN
1517 * \param[in] start starting byte offset within UTF-8 text
1518 * \param[in] end ending byte offset
1519 * \param[out] plen receives validated length
1520 * \return pointer to text, or NULL if no text
1521 */
1522static char *
1523textplain_get_raw_data(struct content *c,
1524 unsigned start,
1525 unsigned end,
1526 size_t *plen)
1527{
1528 textplain_content *text = (textplain_content *) c;
1529 size_t utf8_size;
1530
1531 assert(c != NULL)((c != ((void*)0)) ? (void) (0) : __assert_fail ("c != NULL",
"content/handlers/text/textplain.c", 1531, __extension__ __PRETTY_FUNCTION__
))
;
1532
1533 utf8_size = text->utf8_data_size;
1534
1535 /* any text at all? */
1536 if (!utf8_size) return NULL((void*)0);
1537
1538 /* clamp to valid offset range */
1539 if (start >= utf8_size) start = utf8_size;
1540 if (end >= utf8_size) end = utf8_size;
1541
1542 *plen = end - start;
1543
1544 return text->utf8_data + start;
1545}
1546
1547
1548/**
1549 * get bounds of a free text search match
1550 */
1551static nserror
1552textplain_textsearch_bounds(struct content *c,
1553 unsigned start_idx,
1554 unsigned end_idx,
1555 struct box *start_box,
1556 struct box *end_box,
1557 struct rect *bounds)
1558{
1559 textplain_coords_from_range(c, start_idx, end_idx, bounds);
1560
1561 return NSERROR_OK;
1562}
1563
1564
1565/**
1566 * invalidate a region based on offsets into the text cauing a redraw
1567 */
1568static nserror
1569textplain_textselection_redraw(struct content *c,
1570 unsigned start_idx,
1571 unsigned end_idx)
1572{
1573 struct rect r;
1574
1575 if (end_idx <= start_idx) {
1576 return NSERROR_BAD_PARAMETER;
1577 }
1578
1579 textplain_coords_from_range(c, start_idx, end_idx, &r);
1580
1581 content__request_redraw(c, r.x0, r.y0, r.x1 - r.x0, r.y1 - r.y0);
1582
1583 return NSERROR_OK;
1584}
1585
1586static nserror
1587textplain_textselection_copy(struct content *c,
1588 unsigned start_idx,
1589 unsigned end_idx,
1590 struct selection_string *selstr)
1591{
1592 const char *text;
1593 size_t length;
1594 bool_Bool res = false0;
1595
1596 text = textplain_get_raw_data(c, start_idx, end_idx, &length);
1597 if (text != NULL((void*)0)) {
1598 res = selection_string_append(text, length, false0, NULL((void*)0), selstr);
1599 }
1600 if (res == false0) {
1601 return NSERROR_NOMEM;
1602 }
1603 return NSERROR_OK;
1604}
1605
1606
1607/**
1608 * Retrieve the index of the end of the text
1609 *
1610 * \param[in] c Content to retrieve size of
1611 * \return Size, in bytes, of data
1612 */
1613static nserror
1614textplain_textselection_get_end(struct content *c, unsigned *end_idx)
1615{
1616 textplain_content *text = (textplain_content *)c;
1617
1618 *end_idx = text->utf8_data_size;
1619 return NSERROR_OK;
1620}
1621
1622
1623/**
1624 * plain text content handler table
1625 */
1626static const content_handler textplain_content_handler = {
1627 .fini = textplain_fini,
1628 .create = textplain_create,
1629 .process_data = textplain_process_data,
1630 .data_complete = textplain_convert,
1631 .reformat = textplain_reformat,
1632 .destroy = textplain_destroy,
1633 .mouse_track = textplain_mouse_track,
1634 .mouse_action = textplain_mouse_action,
1635 .keypress = textplain_keypress,
1636 .redraw = textplain_redraw,
1637 .open = textplain_open,
1638 .close = textplain_close,
1639 .get_selection = textplain_get_selection,
1640 .clone = textplain_clone,
1641 .type = textplain_content_type,
1642 .textsearch_find = textplain_textsearch_find,
1643 .textsearch_bounds = textplain_textsearch_bounds,
1644 .textselection_redraw = textplain_textselection_redraw,
1645 .textselection_copy = textplain_textselection_copy,
1646 .textselection_get_end = textplain_textselection_get_end,
1647 .no_share = true1,
1648};
1649
1650
1651/* exported interface documented in html/textplain.h */
1652nserror textplain_init(void)
1653{
1654 lwc_error lerror;
1655 nserror error;
1656
1657 lerror = lwc_intern_string("Windows-1252",
1658 SLEN("Windows-1252")(sizeof(("Windows-1252")) - 1),
1659 &textplain_default_charset);
1660 if (lerror != lwc_error_ok) {
1661 return NSERROR_NOMEM;
1662 }
1663
1664 error = content_factory_register_handler("text/plain",
1665 &textplain_content_handler);
1666 if (error != NSERROR_OK) {
1667 lwc_string_unref(textplain_default_charset){ lwc_string *__lwc_s = (textplain_default_charset); if (__lwc_s
!= ((void*)0)) { __lwc_s->refcnt--; if ((__lwc_s->refcnt
== 0) || ((__lwc_s->refcnt == 1) && (__lwc_s->
insensitive == __lwc_s))) lwc_string_destroy(__lwc_s); } }
;
1668 }
1669
1670 error = content_factory_register_handler("application/json",
1671 &textplain_content_handler);
1672 if (error != NSERROR_OK) {
1673 lwc_string_unref(textplain_default_charset){ lwc_string *__lwc_s = (textplain_default_charset); if (__lwc_s
!= ((void*)0)) { __lwc_s->refcnt--; if ((__lwc_s->refcnt
== 0) || ((__lwc_s->refcnt == 1) && (__lwc_s->
insensitive == __lwc_s))) lwc_string_destroy(__lwc_s); } }
;
1674 }
1675
1676 return error;
1677}
1678
1679
1680
1681

/var/lib/jenkins/artifacts-x86_64-linux-gnu/include/parserutils/input/inputstream.h

1/*
2 * This file is part of LibParserUtils.
3 * Licensed under the MIT License,
4 * http://www.opensource.org/licenses/mit-license.php
5 * Copyright 2007 John-Mark Bell <jmb@netsurf-browser.org>
6 */
7
8#ifndef parserutils_input_inputstream_h_
9#define parserutils_input_inputstream_h_
10
11#ifdef __cplusplus
12extern "C"
13{
14#endif
15
16#include <stdbool.h>
17#ifndef NDEBUG
18#include <stdio.h>
19#endif
20#include <stdlib.h>
21#include <inttypes.h>
22
23#include <parserutils/errors.h>
24#include <parserutils/functypes.h>
25#include <parserutils/types.h>
26#include <parserutils/charset/utf8.h>
27#include <parserutils/utils/buffer.h>
28
29/**
30 * Type of charset detection function
31 */
32typedef parserutils_error (*parserutils_charset_detect_func)(
33 const uint8_t *data, size_t len,
34 uint16_t *mibenum, uint32_t *source);
35
36/**
37 * Input stream object
38 */
39typedef struct parserutils_inputstream
40{
41 parserutils_buffer *utf8; /**< Buffer containing UTF-8 data */
42
43 uint32_t cursor; /**< Byte offset of current position */
44
45 bool_Bool had_eof; /**< Whether EOF has been reached */
46} parserutils_inputstream;
47
48/* Create an input stream */
49parserutils_error parserutils_inputstream_create(const char *enc,
50 uint32_t encsrc, parserutils_charset_detect_func csdetect,
51 parserutils_inputstream **stream);
52/* Destroy an input stream */
53parserutils_error parserutils_inputstream_destroy(
54 parserutils_inputstream *stream);
55
56/* Append data to an input stream */
57parserutils_error parserutils_inputstream_append(
58 parserutils_inputstream *stream,
59 const uint8_t *data, size_t len);
60/* Insert data into stream at current location */
61parserutils_error parserutils_inputstream_insert(
62 parserutils_inputstream *stream,
63 const uint8_t *data, size_t len);
64
65/* Slow form of css_inputstream_peek. */
66parserutils_error parserutils_inputstream_peek_slow(
67 parserutils_inputstream *stream,
68 size_t offset, const uint8_t **ptr, size_t *length);
69
70/**
71 * Look at the character in the stream that starts at
72 * offset bytes from the cursor
73 *
74 * \param stream Stream to look in
75 * \param offset Byte offset of start of character
76 * \param ptr Pointer to location to receive pointer to character data
77 * \param length Pointer to location to receive character length (in bytes)
78 * \return PARSERUTILS_OK on success,
79 * _NEEDDATA on reaching the end of available input,
80 * _EOF on reaching the end of all input,
81 * _BADENCODING if the input cannot be decoded,
82 * _NOMEM on memory exhaustion,
83 * _BADPARM if bad parameters are passed.
84 *
85 * Once the character pointed to by the result of this call has been advanced
86 * past (i.e. parserutils_inputstream_advance has caused the stream cursor to
87 * pass over the character), then no guarantee is made as to the validity of
88 * the data pointed to. Thus, any attempt to dereference the pointer after
89 * advancing past the data it points to is a bug.
90 */
91static inline parserutils_error parserutils_inputstream_peek(
92 parserutils_inputstream *stream, size_t offset,
93 const uint8_t **ptr, size_t *length)
94{
95 parserutils_error error = PARSERUTILS_OK;
96 const parserutils_buffer *utf8;
97 const uint8_t *utf8_data;
98 size_t len, off, utf8_len;
99
100 if (stream == NULL((void*)0) || ptr == NULL((void*)0) || length == NULL((void*)0))
15
Assuming 'stream' is equal to NULL
101 return PARSERUTILS_BADPARM;
16
Returning without writing to '*length'
102
103#ifndef NDEBUG
104#ifdef VERBOSE_INPUTSTREAM
105 fprintf(stdoutstdout, "Peek: len: %zu cur: %u off: %zu\n",
106 stream->utf8->length, stream->cursor, offset);
107#endif
108#ifdef RANDOMISE_INPUTSTREAM
109 parserutils_buffer_randomise(stream->utf8);
110#endif
111#endif
112
113 utf8 = stream->utf8;
114 utf8_data = utf8->data;
115 utf8_len = utf8->length;
116 off = stream->cursor + offset;
117
118#define IS_ASCII(x) (((x) & 0x80) == 0)
119
120 if (off < utf8_len) {
121 if (IS_ASCII(utf8_data[off])) {
122 /* Early exit for ASCII case */
123 (*length) = 1;
124 (*ptr) = (utf8_data + off);
125 return PARSERUTILS_OK;
126 } else {
127 error = parserutils_charset_utf8_char_byte_length(
128 utf8_data + off, &len);
129
130 if (error == PARSERUTILS_OK) {
131 (*length) = len;
132 (*ptr) = (utf8_data + off);
133 return PARSERUTILS_OK;
134 } else if (error != PARSERUTILS_NEEDDATA) {
135 return error;
136 }
137 }
138 }
139
140#undef IS_ASCII
141
142 return parserutils_inputstream_peek_slow(stream, offset, ptr, length);
143}
144
145/**
146 * Advance the stream's current position
147 *
148 * \param stream The stream whose position to advance
149 * \param bytes The number of bytes to advance
150 */
151static inline void parserutils_inputstream_advance(
152 parserutils_inputstream *stream, size_t bytes)
153{
154 if (stream == NULL((void*)0))
155 return;
156
157#if !defined(NDEBUG) && defined(VERBOSE_INPUTSTREAM)
158 fprintf(stdoutstdout, "Advance: len: %zu cur: %u bytes: %zu\n",
159 stream->utf8->length, stream->cursor, bytes);
160#endif
161
162 if (bytes > stream->utf8->length - stream->cursor)
163 bytes = stream->utf8->length - stream->cursor;
164
165 if (stream->cursor == stream->utf8->length)
166 return;
167
168 stream->cursor += bytes;
169}
170
171/* Read the document charset */
172const char *parserutils_inputstream_read_charset(
173 parserutils_inputstream *stream, uint32_t *source);
174/* Change the document charset */
175parserutils_error parserutils_inputstream_change_charset(
176 parserutils_inputstream *stream,
177 const char *enc, uint32_t source);
178
179#ifdef __cplusplus
180}
181#endif
182
183#endif
184