Bug Summary

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

/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))
14
Assuming 'stream' is equal to NULL
101 return PARSERUTILS_BADPARM;
15
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