Bug Summary

File:desktop/global_history.c
Warning:line 809, column 2
Value stored to 'err' is never read

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 global_history.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-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 -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 -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/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/cairo -I /usr/include/pixman-1 -I /usr/include/freetype2 -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/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/atk-1.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -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-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-27-084849-3370695-1 -x c desktop/global_history.c
1/*
2 * Copyright 2012 - 2013 Michael Drake <tlsa@netsurf-browser.org>
3 *
4 * This file is part of NetSurf, http://www.netsurf-browser.org/
5 *
6 * NetSurf is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * NetSurf is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20#include <stdlib.h>
21#include <string.h>
22#include <time.h>
23
24#include "utils/messages.h"
25#include "utils/utils.h"
26#include "utils/utf8.h"
27#include "utils/libdom.h"
28#include "utils/log.h"
29#include "utils/nsurl.h"
30#include "content/urldb.h"
31
32#include "desktop/global_history.h"
33#include "desktop/treeview.h"
34#include "netsurf/browser_window.h"
35
36#define N_DAYS28 28
37#define N_SEC_PER_DAY(60 * 60 * 24) (60 * 60 * 24)
38
39enum global_history_folders {
40 GH_TODAY = 0,
41 GH_YESTERDAY,
42 GH_2_DAYS_AGO,
43 GH_3_DAYS_AGO,
44 GH_4_DAYS_AGO,
45 GH_5_DAYS_AGO,
46 GH_6_DAYS_AGO,
47 GH_LAST_WEEK,
48 GH_2_WEEKS_AGO,
49 GH_3_WEEKS_AGO,
50 GH_N_FOLDERS
51};
52
53enum global_history_fields {
54 GH_TITLE,
55 GH_URL,
56 GH_LAST_VISIT,
57 GH_VISITS,
58 GH_PERIOD,
59 N_FIELDS
60};
61
62struct global_history_folder {
63 treeview_node *folder;
64 struct treeview_field_data data;
65};
66
67struct global_history_ctx {
68 treeview *tree;
69 struct treeview_field_desc fields[N_FIELDS];
70 struct global_history_folder folders[GH_N_FOLDERS];
71 time_t today;
72 int weekday;
73 bool_Bool built;
74};
75struct global_history_ctx gh_ctx;
76
77struct global_history_entry {
78 bool_Bool user_delete;
79
80 int slot;
81 nsurl *url;
82 time_t t;
83 treeview_node *entry;
84 struct global_history_entry *next;
85 struct global_history_entry *prev;
86
87 struct treeview_field_data data[N_FIELDS - 1];
88};
89struct global_history_entry *gh_list[N_DAYS28];
90
91
92/**
93 * Find an entry in the global history
94 *
95 * \param url The URL to find
96 * \return Pointer to history entry, or NULL if not found
97 */
98static struct global_history_entry *global_history_find(nsurl *url)
99{
100 int i;
101 struct global_history_entry *e;
102
103 for (i = 0; i < N_DAYS28; i++) {
104 e = gh_list[i];
105
106 while (e != NULL((void*)0)) {
107 if (nsurl_compare(e->url, url,
108 NSURL_COMPLETE) == true1) {
109 /* Got a match */
110 return e;
111 }
112 e = e->next;
113 }
114
115 }
116
117 /* No match found */
118 return NULL((void*)0);
119}
120
121
122/**
123 * Initialise the treeview directories
124 *
125 * \param f Ident for folder to create
126 * \return NSERROR_OK on success, appropriate error otherwise
127 */
128static nserror global_history_create_dir(enum global_history_folders f)
129{
130 nserror err;
131 treeview_node *relation = NULL((void*)0);
132 enum treeview_relationship rel = TREE_REL_FIRST_CHILD;
133 const char *label;
134 int i;
135
136 switch (f) {
137 case GH_TODAY:
138 label = "DateToday";
139 break;
140
141 case GH_YESTERDAY:
142 label = "DateYesterday";
143 break;
144
145 case GH_2_DAYS_AGO:
146 label = "Date2Days";
147 break;
148
149 case GH_3_DAYS_AGO:
150 label = "Date3Days";
151 break;
152
153 case GH_4_DAYS_AGO:
154 label = "Date4Days";
155 break;
156
157 case GH_5_DAYS_AGO:
158 label = "Date5Days";
159 break;
160
161 case GH_6_DAYS_AGO:
162 label = "Date6Days";
163 break;
164
165 case GH_LAST_WEEK:
166 label = "Date1Week";
167 break;
168
169 case GH_2_WEEKS_AGO:
170 label = "Date2Week";
171 break;
172
173 case GH_3_WEEKS_AGO:
174 label = "Date3Week";
175 break;
176
177 default:
178 return NSERROR_BAD_PARAMETER;
179 }
180
181 label = messages_get(label);
182
183 for (i = f - 1; i >= 0; i--) {
184 if (gh_ctx.folders[i].folder != NULL((void*)0)) {
185 relation = gh_ctx.folders[i].folder;
186 rel = TREE_REL_NEXT_SIBLING;
187 break;
188 }
189 }
190
191 gh_ctx.folders[f].data.field = gh_ctx.fields[N_FIELDS - 1].field;
192 gh_ctx.folders[f].data.value = label;
193 gh_ctx.folders[f].data.value_len = strlen(label);
194 err = treeview_create_node_folder(gh_ctx.tree,
195 &gh_ctx.folders[f].folder,
196 relation, rel,
197 &gh_ctx.folders[f].data,
198 &gh_ctx.folders[f],
199 gh_ctx.built ? TREE_OPTION_NONE :
200 TREE_OPTION_SUPPRESS_RESIZE |
201 TREE_OPTION_SUPPRESS_REDRAW);
202
203 return err;
204}
205
206
207/**
208 * Get the treeview folder for history entires in a particular slot
209 *
210 * \param parent Updated to parent folder.
211 * \param slot Global history slot of entry we want folder node for
212 * \return NSERROR_OK on success, appropriate error otherwise
213 */
214static inline nserror global_history_get_parent_treeview_node(
215 treeview_node **parent, int slot)
216{
217 int folder_index;
218 struct global_history_folder *f;
219 nserror err;
220
221 if (slot < 7) {
222 folder_index = slot;
223
224 } else if (slot < 14) {
225 folder_index = GH_LAST_WEEK;
226
227 } else if (slot < 21) {
228 folder_index = GH_2_WEEKS_AGO;
229
230 } else if (slot < N_DAYS28) {
231 folder_index = GH_3_WEEKS_AGO;
232
233 } else {
234 /* Slot value is invalid */
235 return NSERROR_BAD_PARAMETER;
236 }
237
238 /* Get the folder */
239 f = &(gh_ctx.folders[folder_index]);
240
241 if (f->folder == NULL((void*)0)) {
242 err = global_history_create_dir(folder_index);
243 if (err != NSERROR_OK) {
244 *parent = NULL((void*)0);
245 return err;
246 }
247 }
248
249 /* Return the parent treeview folder */
250 *parent = f->folder;
251 return NSERROR_OK;
252}
253
254
255/**
256 * Set a global history entry's data from the url_data.
257 *
258 * \param e Global history entry to set up
259 * \param data Data associated with entry's URL
260 * \return NSERROR_OK on success, appropriate error otherwise
261 */
262static nserror global_history_create_treeview_field_data(
263 struct global_history_entry *e,
264 const struct url_data *data)
265{
266 const char *title = (data->title != NULL((void*)0)) ?
267 data->title : messages_get("NoTitle");
268 char buffer[16];
269 struct tm *lvtime;
270 char *last_visited = NULL((void*)0);
271 size_t len = 0;
272
273 e->data[GH_TITLE].field = gh_ctx.fields[GH_TITLE].field;
274 e->data[GH_TITLE].value = strdup(title);
275 e->data[GH_TITLE].value_len = (e->data[GH_TITLE].value != NULL((void*)0)) ?
276 strlen(title) : 0;
277
278 e->data[GH_URL].field = gh_ctx.fields[GH_URL].field;
279 e->data[GH_URL].value = nsurl_access(e->url);
280 e->data[GH_URL].value_len = nsurl_length(e->url);
281
282 if ((lvtime = localtime(&data->last_visit)) != NULL((void*)0)) {
283 const size_t lvsize = 256;
284 last_visited = malloc(lvsize);
285 if (last_visited != NULL((void*)0)) {
286 len = strftime(last_visited, lvsize,
287 "%a %b %e %H:%M:%S %Y", lvtime);
288 }
289 }
290
291 e->data[GH_LAST_VISIT].field = gh_ctx.fields[GH_LAST_VISIT].field;
292 e->data[GH_LAST_VISIT].value = last_visited;
293 e->data[GH_LAST_VISIT].value_len = len;
294
295 len = snprintf(buffer, 16, "%u", data->visits);
296 if (len == 16) {
297 len--;
298 buffer[len] = '\0';
299 }
300
301 e->data[GH_VISITS].field = gh_ctx.fields[GH_VISITS].field;
302 e->data[GH_VISITS].value = strdup(buffer);
303 e->data[GH_VISITS].value_len = len;
304
305 return NSERROR_OK;
306}
307
308/**
309 * Add a global history entry to the treeview
310 *
311 * \param e entry to add to treeview
312 * \param slot global history slot containing entry
313 * \return NSERROR_OK on success, or appropriate error otherwise
314 *
315 * It is assumed that the entry is unique (for its URL) in the global
316 * history table
317 */
318static nserror global_history_entry_insert(struct global_history_entry *e,
319 int slot)
320{
321 nserror err;
322
323 treeview_node *parent;
324 err = global_history_get_parent_treeview_node(&parent, slot);
325 if (err != NSERROR_OK) {
326 return err;
327 }
328
329 err = treeview_create_node_entry(gh_ctx.tree, &(e->entry),
330 parent, TREE_REL_FIRST_CHILD, e->data, e,
331 gh_ctx.built ? TREE_OPTION_NONE :
332 TREE_OPTION_SUPPRESS_RESIZE |
333 TREE_OPTION_SUPPRESS_REDRAW);
334 if (err != NSERROR_OK) {
335 return err;
336 }
337
338 return NSERROR_OK;
339}
340
341
342/**
343 * Add an entry to the global history (creates the entry).
344 *
345 * If the treeview has already been created, the entry will be added to the
346 * treeview. Otherwise, the entry will have to be added to the treeview later.
347 *
348 * When we first create the global history we create it without the treeview, to
349 * simplfy sorting the entries.
350 *
351 * \param url URL for entry to add to history
352 * \param slot Global history slot to contain history entry
353 * \param data URL data for the entry
354 * \param got_treeview Whether the treeview has been created already
355 * \return NSERROR_OK on success, or appropriate error otherwise
356 */
357static nserror global_history_add_entry_internal(nsurl *url, int slot,
358 const struct url_data *data, bool_Bool got_treeview)
359{
360 nserror err;
361 struct global_history_entry *e;
362
363 /* Create new local history entry */
364 e = malloc(sizeof(struct global_history_entry));
365 if (e == NULL((void*)0)) {
366 return NSERROR_NOMEM;
367 }
368
369 e->user_delete = false0;
370 e->slot = slot;
371 e->url = nsurl_ref(url);
372 e->t = data->last_visit;
373 e->entry = NULL((void*)0);
374 e->next = NULL((void*)0);
375 e->prev = NULL((void*)0);
376
377 err = global_history_create_treeview_field_data(e, data);
378 if (err != NSERROR_OK) {
379 return err;
380 }
381
382 if (gh_list[slot] == NULL((void*)0)) {
383 /* list empty */
384 gh_list[slot] = e;
385
386 } else if (gh_list[slot]->t < e->t) {
387 /* Insert at list head */
388 e->next = gh_list[slot];
389 gh_list[slot]->prev = e;
390 gh_list[slot] = e;
391 } else {
392 struct global_history_entry *prev = gh_list[slot];
393 struct global_history_entry *curr = prev->next;
394 while (curr != NULL((void*)0)) {
395 if (curr->t < e->t) {
396 break;
397 }
398 prev = curr;
399 curr = curr->next;
400 }
401
402 /* insert after prev */
403 e->next = curr;
404 e->prev = prev;
405 prev->next = e;
406
407 if (curr != NULL((void*)0))
408 curr->prev = e;
409 }
410
411 if (got_treeview) {
412 err = global_history_entry_insert(e, slot);
413 if (err != NSERROR_OK) {
414 return err;
415 }
416 }
417
418 return NSERROR_OK;
419}
420
421
422/**
423 * Delete a global history entry
424 *
425 * This does not delete the treeview node, rather it should only be called from
426 * the treeview node delete event message.
427 *
428 * \param e Entry to delete
429 */
430static void global_history_delete_entry_internal(
431 struct global_history_entry *e)
432{
433 assert(e != NULL)((e != ((void*)0)) ? (void) (0) : __assert_fail ("e != NULL",
"desktop/global_history.c", 433, __extension__ __PRETTY_FUNCTION__
))
;
434 assert(e->entry == NULL)((e->entry == ((void*)0)) ? (void) (0) : __assert_fail ("e->entry == NULL"
, "desktop/global_history.c", 434, __extension__ __PRETTY_FUNCTION__
))
;
435
436 /* Unlink */
437 if (gh_list[e->slot] == e) {
438 /* e is first entry */
439 gh_list[e->slot] = e->next;
440
441 if (e->next != NULL((void*)0))
442 e->next->prev = NULL((void*)0);
443
444 } else if (e->next == NULL((void*)0)) {
445 /* e is last entry */
446 e->prev->next = NULL((void*)0);
447
448 } else {
449 /* e has an entry before and after */
450 e->prev->next = e->next;
451 e->next->prev = e->prev;
452 }
453
454 if (e->user_delete) {
455 /* User requested delete, so delete from urldb too. */
456 urldb_reset_url_visit_data(e->url);
457 }
458
459 /* Destroy fields */
460 free((void *)e->data[GH_TITLE].value); /* Eww */
461 free((void *)e->data[GH_LAST_VISIT].value); /* Eww */
462 free((void *)e->data[GH_VISITS].value); /* Eww */
463 nsurl_unref(e->url);
464
465 /* Destroy entry */
466 free(e);
467}
468
469/**
470 * Internal routine to actually perform global history addition
471 *
472 * \param url The URL to add
473 * \param data URL data associated with URL
474 * \return true (for urldb_iterate_entries)
475 */
476static bool_Bool global_history_add_entry(nsurl *url,
477 const struct url_data *data)
478{
479 int slot;
480 time_t visit_date;
481 time_t earliest_date = gh_ctx.today - (N_DAYS28 - 1) * N_SEC_PER_DAY(60 * 60 * 24);
482 bool_Bool got_treeview = gh_ctx.tree != NULL((void*)0);
483
484 assert((url != NULL) && (data != NULL))(((url != ((void*)0)) && (data != ((void*)0))) ? (void
) (0) : __assert_fail ("(url != NULL) && (data != NULL)"
, "desktop/global_history.c", 484, __extension__ __PRETTY_FUNCTION__
))
;
485
486 visit_date = data->last_visit;
487
488 /* Find day array slot for entry */
489 if (visit_date >= gh_ctx.today) {
490 slot = 0;
491 } else if (visit_date >= earliest_date) {
492 slot = (gh_ctx.today - visit_date) / N_SEC_PER_DAY(60 * 60 * 24) + 1;
493 } else {
494 /* too old */
495 return true1;
496 }
497
498 if (got_treeview == true1) {
499 /* The treeview for global history already exists */
500 struct global_history_entry *e;
501
502 /* Delete any existing entry for this URL */
503 e = global_history_find(url);
504 if (e != NULL((void*)0)) {
505 treeview_delete_node(gh_ctx.tree, e->entry,
506 TREE_OPTION_SUPPRESS_REDRAW |
507 TREE_OPTION_SUPPRESS_RESIZE);
508 }
509 }
510
511 if (global_history_add_entry_internal(url, slot, data,
512 got_treeview) != NSERROR_OK) {
513 return false0;
514 }
515
516 return true1;
517}
518
519/**
520 * Initialise the treeview entry feilds
521 *
522 * \return NSERROR_OK on success, or appropriate error otherwise
523 */
524static nserror global_history_initialise_entry_fields(void)
525{
526 int i;
527 const char *label;
528
529 for (i = 0; i < N_FIELDS; i++)
530 gh_ctx.fields[i].field = NULL((void*)0);
531
532 gh_ctx.fields[GH_TITLE].flags = TREE_FLAG_DEFAULT;
533 label = "TreeviewLabelTitle";
534 label = messages_get(label);
535 if (lwc_intern_string(label, strlen(label),
536 &gh_ctx.fields[GH_TITLE].field) !=
537 lwc_error_ok) {
538 goto error;
539 }
540
541 gh_ctx.fields[GH_URL].flags =
542 TREE_FLAG_COPY_TEXT |
543 TREE_FLAG_SEARCHABLE;
544 label = "TreeviewLabelURL";
545 label = messages_get(label);
546 if (lwc_intern_string(label, strlen(label),
547 &gh_ctx.fields[GH_URL].field) !=
548 lwc_error_ok) {
549 goto error;
550 }
551
552 gh_ctx.fields[GH_LAST_VISIT].flags = TREE_FLAG_SHOW_NAME;
553 label = "TreeviewLabelLastVisit";
554 label = messages_get(label);
555 if (lwc_intern_string(label, strlen(label),
556 &gh_ctx.fields[GH_LAST_VISIT].field) !=
557 lwc_error_ok) {
558 goto error;
559 }
560
561 gh_ctx.fields[GH_VISITS].flags = TREE_FLAG_SHOW_NAME;
562 label = "TreeviewLabelVisits";
563 label = messages_get(label);
564 if (lwc_intern_string(label, strlen(label),
565 &gh_ctx.fields[GH_VISITS].field) !=
566 lwc_error_ok) {
567 goto error;
568 }
569
570 gh_ctx.fields[GH_PERIOD].flags = TREE_FLAG_DEFAULT;
571 label = "TreeviewLabelPeriod";
572 label = messages_get(label);
573 if (lwc_intern_string(label, strlen(label),
574 &gh_ctx.fields[GH_PERIOD].field) !=
575 lwc_error_ok) {
576 return false0;
577 }
578
579 return NSERROR_OK;
580
581error:
582 for (i = 0; i < N_FIELDS; i++)
583 if (gh_ctx.fields[i].field != NULL((void*)0))
584 lwc_string_unref(gh_ctx.fields[i].field){ lwc_string *__lwc_s = (gh_ctx.fields[i].field); ((__lwc_s !=
((void*)0)) ? (void) (0) : __assert_fail ("__lwc_s != NULL",
"desktop/global_history.c", 584, __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); }
;
585
586 return NSERROR_UNKNOWN;
587}
588
589
590/**
591 * Initialise the time
592 *
593 * \return NSERROR_OK on success, or appropriate error otherwise
594 */
595static nserror global_history_initialise_time(void)
596{
597 struct tm *full_time;
598 time_t t;
599
600 /* get the current time */
601 t = time(NULL((void*)0));
602 if (t == -1) {
603 NSLOG(netsurf, INFO, "time info unaviable")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "desktop/global_history.c", sizeof("desktop/global_history.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 603
, }; nslog__log(&_nslog_ctx, "time info unaviable"); } } while
(0)
;
604 return NSERROR_UNKNOWN;
605 }
606
607 /* get the time at the start of today */
608 full_time = localtime(&t);
609 full_time->tm_sec = 0;
610 full_time->tm_min = 0;
611 full_time->tm_hour = 0;
612 t = mktime(full_time);
613 if (t == -1) {
614 NSLOG(netsurf, INFO, "mktime failed")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "desktop/global_history.c", sizeof("desktop/global_history.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 614
, }; nslog__log(&_nslog_ctx, "mktime failed"); } } while(
0)
;
615 return NSERROR_UNKNOWN;
616 }
617
618 gh_ctx.today = t;
619 gh_ctx.weekday = full_time->tm_wday;
620
621 return NSERROR_OK;
622}
623
624
625/**
626 * Initialise the treeview entries
627 *
628 * \return NSERROR_OK on success, or appropriate error otherwise
629 */
630static nserror global_history_init_entries(void)
631{
632 int i;
633 nserror err;
634
635 /* Itterate over all global history data, inserting it into treeview */
636 for (i = 0; i < N_DAYS28; i++) {
637 struct global_history_entry *l = NULL((void*)0);
638 struct global_history_entry *e = gh_list[i];
639
640 /* Insert in reverse order; find last */
641 while (e != NULL((void*)0)) {
642 l = e;
643 e = e->next;
644 }
645
646 /* Insert the entries into the treeview */
647 while (l != NULL((void*)0)) {
648 err = global_history_entry_insert(l, i);
649 if (err != NSERROR_OK) {
650 return err;
651 }
652 l = l->prev;
653 }
654 }
655
656 return NSERROR_OK;
657}
658
659
660static nserror global_history_tree_node_folder_cb(
661 struct treeview_node_msg msg, void *data)
662{
663 struct global_history_folder *f = data;
664
665 switch (msg.msg) {
666 case TREE_MSG_NODE_DELETE:
667 f->folder = NULL((void*)0);
668 break;
669
670 case TREE_MSG_NODE_EDIT:
671 break;
672
673 case TREE_MSG_NODE_LAUNCH:
674 break;
675 }
676
677 return NSERROR_OK;
678}
679
680static nserror
681global_history_tree_node_entry_cb(struct treeview_node_msg msg, void *data)
682{
683 struct global_history_entry *e = data;
684 nserror ret = NSERROR_OK;
685
686 switch (msg.msg) {
687 case TREE_MSG_NODE_DELETE:
688 e->entry = NULL((void*)0);
689 e->user_delete = msg.data.delete.user;
690 global_history_delete_entry_internal(e);
691 break;
692
693 case TREE_MSG_NODE_EDIT:
694 break;
695
696 case TREE_MSG_NODE_LAUNCH:
697 {
698 struct browser_window *existing = NULL((void*)0);
699 enum browser_window_create_flags flags = BW_CREATE_HISTORY;
700
701 /* TODO: Set existing to window that new tab appears in */
702
703 if (msg.data.node_launch.mouse &
704 (BROWSER_MOUSE_MOD_1 | BROWSER_MOUSE_MOD_2) ||
705 existing == NULL((void*)0)) {
706 /* Shift or Ctrl launch, open in new window rather
707 * than tab. */
708 /* TODO: flags ^= BW_CREATE_TAB; */
709 }
710
711 ret = browser_window_create(flags, e->url, NULL((void*)0),
712 existing, NULL((void*)0));
713 }
714 break;
715 }
716 return ret;
717}
718
719struct treeview_callback_table gh_tree_cb_t = {
720 .folder = global_history_tree_node_folder_cb,
721 .entry = global_history_tree_node_entry_cb
722};
723
724
725/* Exported interface, documented in global_history.h */
726nserror global_history_init(void *core_window_handle)
727{
728 nserror err;
729
730 err = treeview_init();
731 if (err != NSERROR_OK) {
732 return err;
733 }
734
735 NSLOG(netsurf, INFO, "Loading global history")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "desktop/global_history.c", sizeof("desktop/global_history.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 735
, }; nslog__log(&_nslog_ctx, "Loading global history"); }
} while(0)
;
736
737 /* Init. global history treeview time */
738 err = global_history_initialise_time();
739 if (err != NSERROR_OK) {
740 gh_ctx.tree = NULL((void*)0);
741 return err;
742 }
743
744 /* Init. global history treeview entry fields */
745 err = global_history_initialise_entry_fields();
746 if (err != NSERROR_OK) {
747 gh_ctx.tree = NULL((void*)0);
748 return err;
749 }
750
751 /* Load the entries */
752 urldb_iterate_entries(global_history_add_entry);
753
754 /* Create the global history treeview */
755 err = treeview_create(&gh_ctx.tree, &gh_tree_cb_t,
756 N_FIELDS, gh_ctx.fields,
757 core_window_handle,
758 TREEVIEW_NO_MOVES | TREEVIEW_DEL_EMPTY_DIRS |
759 TREEVIEW_SEARCHABLE);
760 if (err != NSERROR_OK) {
761 gh_ctx.tree = NULL((void*)0);
762 return err;
763 }
764
765 /* Ensure there is a folder for today */
766 err = global_history_create_dir(GH_TODAY);
767 if (err != NSERROR_OK) {
768 return err;
769 }
770
771 /* Add the history to the treeview */
772 err = global_history_init_entries();
773 if (err != NSERROR_OK) {
774 return err;
775 }
776
777 /* Expand the "Today" folder node */
778 err = treeview_node_expand(gh_ctx.tree,
779 gh_ctx.folders[GH_TODAY].folder);
780 if (err != NSERROR_OK) {
781 return err;
782 }
783
784 /* History tree is built
785 * We suppress the treeview height callback on entry insertion before
786 * the treeview is built. */
787 gh_ctx.built = true1;
788
789 /* Inform client of window height */
790 treeview_get_height(gh_ctx.tree);
791
792 NSLOG(netsurf, INFO, "Loaded global history")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "desktop/global_history.c", sizeof("desktop/global_history.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 792
, }; nslog__log(&_nslog_ctx, "Loaded global history"); } }
while(0)
;
793
794 return NSERROR_OK;
795}
796
797
798/* Exported interface, documented in global_history.h */
799nserror global_history_fini(void)
800{
801 int i;
802 nserror err;
803
804 NSLOG(netsurf, INFO, "Finalising global history")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "desktop/global_history.c", sizeof("desktop/global_history.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 804
, }; nslog__log(&_nslog_ctx, "Finalising global history")
; } } while(0)
;
805
806 gh_ctx.built = false0;
807
808 /* Destroy the global history treeview */
809 err = treeview_destroy(gh_ctx.tree);
Value stored to 'err' is never read
810 gh_ctx.tree = NULL((void*)0);
811
812 /* Free global history treeview entry fields */
813 for (i = 0; i < N_FIELDS; i++)
814 if (gh_ctx.fields[i].field != NULL((void*)0))
815 lwc_string_unref(gh_ctx.fields[i].field){ lwc_string *__lwc_s = (gh_ctx.fields[i].field); ((__lwc_s !=
((void*)0)) ? (void) (0) : __assert_fail ("__lwc_s != NULL",
"desktop/global_history.c", 815, __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); }
;
816
817 err = treeview_fini();
818 if (err != NSERROR_OK) {
819 return err;
820 }
821
822 NSLOG(netsurf, INFO, "Finalised global history")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "desktop/global_history.c", sizeof("desktop/global_history.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 822
, }; nslog__log(&_nslog_ctx, "Finalised global history");
} } while(0)
;
823
824 return err;
825}
826
827
828/* Exported interface, documented in global_history.h */
829nserror global_history_add(nsurl *url)
830{
831 const struct url_data *data;
832
833 /* If we don't have a global history at the moment, just return OK */
834 if (gh_ctx.tree == NULL((void*)0))
835 return NSERROR_OK;
836
837 data = urldb_get_url_data(url);
838 if (data == NULL((void*)0)) {
839 NSLOG(netsurf, INFO,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "desktop/global_history.c", sizeof("desktop/global_history.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 840
, }; nslog__log(&_nslog_ctx, "Can't add URL to history that's not present in urldb."
); } } while(0)
840 "Can't add URL to history that's not present in urldb.")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "desktop/global_history.c", sizeof("desktop/global_history.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 840
, }; nslog__log(&_nslog_ctx, "Can't add URL to history that's not present in urldb."
); } } while(0)
;
841 return NSERROR_BAD_PARAMETER;
842 }
843
844 global_history_add_entry(url, data);
845
846 return NSERROR_OK;
847}
848
849
850struct treeview_export_walk_ctx {
851 FILE *fp;
852};
853/** Callback for treeview_walk node entering */
854static nserror global_history_export_enter_cb(void *ctx, void *node_data,
855 enum treeview_node_type type, bool_Bool *abort)
856{
857 struct treeview_export_walk_ctx *tw = ctx;
858 nserror ret;
859
860 if (type == TREE_NODE_ENTRY) {
861 struct global_history_entry *e = node_data;
862 char *t_text;
863 char *u_text;
864
865 ret = utf8_to_html(e->data[GH_TITLE].value, "iso-8859-1",
866 e->data[GH_TITLE].value_len, &t_text);
867 if (ret != NSERROR_OK)
868 return NSERROR_SAVE_FAILED;
869
870 ret = utf8_to_html(e->data[GH_URL].value, "iso-8859-1",
871 e->data[GH_URL].value_len, &u_text);
872 if (ret != NSERROR_OK) {
873 free(t_text);
874 return NSERROR_SAVE_FAILED;
875 }
876
877 fprintf(tw->fp, "<li><a href=\"%s\">%s</a></li>\n",
878 u_text, t_text);
879
880 free(t_text);
881 free(u_text);
882
883 } else if (type == TREE_NODE_FOLDER) {
884 struct global_history_folder *f = node_data;
885 char *f_text;
886
887 ret = utf8_to_html(f->data.value, "iso-8859-1",
888 f->data.value_len, &f_text);
889 if (ret != NSERROR_OK)
890 return NSERROR_SAVE_FAILED;
891
892 fprintf(tw->fp, "<li><h4>%s</h4>\n<ul>\n", f_text);
893
894 free(f_text);
895 }
896
897 return NSERROR_OK;
898}
899/** Callback for treeview_walk node leaving */
900static nserror global_history_export_leave_cb(void *ctx, void *node_data,
901 enum treeview_node_type type, bool_Bool *abort)
902{
903 struct treeview_export_walk_ctx *tw = ctx;
904
905 if (type == TREE_NODE_FOLDER) {
906 fputs("</ul></li>\n", tw->fp);
907 }
908
909 return NSERROR_OK;
910}
911/* Exported interface, documented in global_history.h */
912nserror global_history_export(const char *path, const char *title)
913{
914 struct treeview_export_walk_ctx tw;
915 nserror err;
916 FILE *fp;
917
918 fp = fopen(path, "w");
919 if (fp == NULL((void*)0))
920 return NSERROR_SAVE_FAILED;
921
922 if (title == NULL((void*)0))
923 title = "NetSurf Browsing History";
924
925 fputs("<!DOCTYPE html "
926 "PUBLIC \"//W3C/DTD HTML 4.01//EN\" "
927 "\"http://www.w3.org/TR/html4/strict.dtd\">\n", fp);
928 fputs("<html>\n<head>\n", fp);
929 fputs("<meta http-equiv=\"Content-Type\" "
930 "content=\"text/html; charset=iso-8859-1\">\n", fp);
931 fprintf(fp, "<title>%s</title>\n", title);
932 fputs("</head>\n<body>\n<ul>\n", fp);
933
934 tw.fp = fp;
935 err = treeview_walk(gh_ctx.tree, NULL((void*)0),
936 global_history_export_enter_cb,
937 global_history_export_leave_cb,
938 &tw, TREE_NODE_ENTRY | TREE_NODE_FOLDER);
939 if (err != NSERROR_OK)
940 return err;
941
942 fputs("</ul>\n</body>\n</html>\n", fp);
943
944 fclose(fp);
945
946 return NSERROR_OK;
947}
948
949
950/* Exported interface, documented in global_history.h */
951void global_history_redraw(int x, int y, struct rect *clip,
952 const struct redraw_context *ctx)
953{
954 treeview_redraw(gh_ctx.tree, x, y, clip, ctx);
955}
956
957
958/* Exported interface, documented in global_history.h */
959void global_history_mouse_action(browser_mouse_state mouse, int x, int y)
960{
961 treeview_mouse_action(gh_ctx.tree, mouse, x, y);
962}
963
964
965/* Exported interface, documented in global_history.h */
966bool_Bool global_history_keypress(uint32_t key)
967{
968 return treeview_keypress(gh_ctx.tree, key);
969}
970
971
972/* Exported interface, documented in global_history.h */
973bool_Bool global_history_has_selection(void)
974{
975 return treeview_has_selection(gh_ctx.tree);
976}
977
978
979/* Exported interface, documented in global_history.h */
980bool_Bool global_history_get_selection(nsurl **url, const char **title)
981{
982 struct global_history_entry *e;
983 enum treeview_node_type type;
984 void *v;
985
986 type = treeview_get_selection(gh_ctx.tree, &v);
987 if (type != TREE_NODE_ENTRY || v == NULL((void*)0)) {
988 *url = NULL((void*)0);
989 *title = NULL((void*)0);
990 return false0;
991 }
992
993 e = (struct global_history_entry *)v;
994
995 *url = e->url;
996 *title = e->data[GH_TITLE].value;
997 return true1;
998}
999
1000
1001/* Exported interface, documented in global_history.h */
1002nserror global_history_expand(bool_Bool only_folders)
1003{
1004 return treeview_expand(gh_ctx.tree, only_folders);
1005}
1006
1007
1008/* Exported interface, documented in global_history.h */
1009nserror global_history_contract(bool_Bool all)
1010{
1011 return treeview_contract(gh_ctx.tree, all);
1012}
1013