Bug Summary

File:desktop/global_history.c
Warning:line 810, 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-monkey -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_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 monkey -D nsmonkey -D MONKEY_RESPATH="/var/lib/jenkins/artifacts-x86_64-linux-gnu/share/netsurf/" -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-05-08-185630-652128-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(struct core_window_callback_table *cw_t,
727 void *core_window_handle)
728{
729 nserror err;
730
731 err = treeview_init();
732 if (err != NSERROR_OK) {
733 return err;
734 }
735
736 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, 736
, }; nslog__log(&_nslog_ctx, "Loading global history"); }
} while(0)
;
737
738 /* Init. global history treeview time */
739 err = global_history_initialise_time();
740 if (err != NSERROR_OK) {
741 gh_ctx.tree = NULL((void*)0);
742 return err;
743 }
744
745 /* Init. global history treeview entry fields */
746 err = global_history_initialise_entry_fields();
747 if (err != NSERROR_OK) {
748 gh_ctx.tree = NULL((void*)0);
749 return err;
750 }
751
752 /* Load the entries */
753 urldb_iterate_entries(global_history_add_entry);
754
755 /* Create the global history treeview */
756 err = treeview_create(&gh_ctx.tree, &gh_tree_cb_t,
757 N_FIELDS, gh_ctx.fields,
758 cw_t, core_window_handle,
759 TREEVIEW_NO_MOVES | TREEVIEW_DEL_EMPTY_DIRS |
760 TREEVIEW_SEARCHABLE);
761 if (err != NSERROR_OK) {
762 gh_ctx.tree = NULL((void*)0);
763 return err;
764 }
765
766 /* Ensure there is a folder for today */
767 err = global_history_create_dir(GH_TODAY);
768 if (err != NSERROR_OK) {
769 return err;
770 }
771
772 /* Add the history to the treeview */
773 err = global_history_init_entries();
774 if (err != NSERROR_OK) {
775 return err;
776 }
777
778 /* Expand the "Today" folder node */
779 err = treeview_node_expand(gh_ctx.tree,
780 gh_ctx.folders[GH_TODAY].folder);
781 if (err != NSERROR_OK) {
782 return err;
783 }
784
785 /* History tree is built
786 * We suppress the treeview height callback on entry insertion before
787 * the treeview is built. */
788 gh_ctx.built = true1;
789
790 /* Inform client of window height */
791 treeview_get_height(gh_ctx.tree);
792
793 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, 793
, }; nslog__log(&_nslog_ctx, "Loaded global history"); } }
while(0)
;
794
795 return NSERROR_OK;
796}
797
798
799/* Exported interface, documented in global_history.h */
800nserror global_history_fini(void)
801{
802 int i;
803 nserror err;
804
805 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, 805
, }; nslog__log(&_nslog_ctx, "Finalising global history")
; } } while(0)
;
806
807 gh_ctx.built = false0;
808
809 /* Destroy the global history treeview */
810 err = treeview_destroy(gh_ctx.tree);
Value stored to 'err' is never read
811 gh_ctx.tree = NULL((void*)0);
812
813 /* Free global history treeview entry fields */
814 for (i = 0; i < N_FIELDS; i++)
815 if (gh_ctx.fields[i].field != NULL((void*)0))
816 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", 816, __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); }
;
817
818 err = treeview_fini();
819 if (err != NSERROR_OK) {
820 return err;
821 }
822
823 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, 823
, }; nslog__log(&_nslog_ctx, "Finalised global history");
} } while(0)
;
824
825 return err;
826}
827
828
829/* Exported interface, documented in global_history.h */
830nserror global_history_add(nsurl *url)
831{
832 const struct url_data *data;
833
834 /* If we don't have a global history at the moment, just return OK */
835 if (gh_ctx.tree == NULL((void*)0))
836 return NSERROR_OK;
837
838 data = urldb_get_url_data(url);
839 if (data == NULL((void*)0)) {
840 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, 841
, }; nslog__log(&_nslog_ctx, "Can't add URL to history that's not present in urldb."
); } } while(0)
841 "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, 841
, }; nslog__log(&_nslog_ctx, "Can't add URL to history that's not present in urldb."
); } } while(0)
;
842 return NSERROR_BAD_PARAMETER;
843 }
844
845 global_history_add_entry(url, data);
846
847 return NSERROR_OK;
848}
849
850
851struct treeview_export_walk_ctx {
852 FILE *fp;
853};
854/** Callback for treeview_walk node entering */
855static nserror global_history_export_enter_cb(void *ctx, void *node_data,
856 enum treeview_node_type type, bool_Bool *abort)
857{
858 struct treeview_export_walk_ctx *tw = ctx;
859 nserror ret;
860
861 if (type == TREE_NODE_ENTRY) {
862 struct global_history_entry *e = node_data;
863 char *t_text;
864 char *u_text;
865
866 ret = utf8_to_html(e->data[GH_TITLE].value, "iso-8859-1",
867 e->data[GH_TITLE].value_len, &t_text);
868 if (ret != NSERROR_OK)
869 return NSERROR_SAVE_FAILED;
870
871 ret = utf8_to_html(e->data[GH_URL].value, "iso-8859-1",
872 e->data[GH_URL].value_len, &u_text);
873 if (ret != NSERROR_OK) {
874 free(t_text);
875 return NSERROR_SAVE_FAILED;
876 }
877
878 fprintf(tw->fp, "<li><a href=\"%s\">%s</a></li>\n",
879 u_text, t_text);
880
881 free(t_text);
882 free(u_text);
883
884 } else if (type == TREE_NODE_FOLDER) {
885 struct global_history_folder *f = node_data;
886 char *f_text;
887
888 ret = utf8_to_html(f->data.value, "iso-8859-1",
889 f->data.value_len, &f_text);
890 if (ret != NSERROR_OK)
891 return NSERROR_SAVE_FAILED;
892
893 fprintf(tw->fp, "<li><h4>%s</h4>\n<ul>\n", f_text);
894
895 free(f_text);
896 }
897
898 return NSERROR_OK;
899}
900/** Callback for treeview_walk node leaving */
901static nserror global_history_export_leave_cb(void *ctx, void *node_data,
902 enum treeview_node_type type, bool_Bool *abort)
903{
904 struct treeview_export_walk_ctx *tw = ctx;
905
906 if (type == TREE_NODE_FOLDER) {
907 fputs("</ul></li>\n", tw->fp);
908 }
909
910 return NSERROR_OK;
911}
912/* Exported interface, documented in global_history.h */
913nserror global_history_export(const char *path, const char *title)
914{
915 struct treeview_export_walk_ctx tw;
916 nserror err;
917 FILE *fp;
918
919 fp = fopen(path, "w");
920 if (fp == NULL((void*)0))
921 return NSERROR_SAVE_FAILED;
922
923 if (title == NULL((void*)0))
924 title = "NetSurf Browsing History";
925
926 fputs("<!DOCTYPE html "
927 "PUBLIC \"//W3C/DTD HTML 4.01//EN\" "
928 "\"http://www.w3.org/TR/html4/strict.dtd\">\n", fp);
929 fputs("<html>\n<head>\n", fp);
930 fputs("<meta http-equiv=\"Content-Type\" "
931 "content=\"text/html; charset=iso-8859-1\">\n", fp);
932 fprintf(fp, "<title>%s</title>\n", title);
933 fputs("</head>\n<body>\n<ul>\n", fp);
934
935 tw.fp = fp;
936 err = treeview_walk(gh_ctx.tree, NULL((void*)0),
937 global_history_export_enter_cb,
938 global_history_export_leave_cb,
939 &tw, TREE_NODE_ENTRY | TREE_NODE_FOLDER);
940 if (err != NSERROR_OK)
941 return err;
942
943 fputs("</ul>\n</body>\n</html>\n", fp);
944
945 fclose(fp);
946
947 return NSERROR_OK;
948}
949
950
951/* Exported interface, documented in global_history.h */
952void global_history_redraw(int x, int y, struct rect *clip,
953 const struct redraw_context *ctx)
954{
955 treeview_redraw(gh_ctx.tree, x, y, clip, ctx);
956}
957
958
959/* Exported interface, documented in global_history.h */
960void global_history_mouse_action(browser_mouse_state mouse, int x, int y)
961{
962 treeview_mouse_action(gh_ctx.tree, mouse, x, y);
963}
964
965
966/* Exported interface, documented in global_history.h */
967bool_Bool global_history_keypress(uint32_t key)
968{
969 return treeview_keypress(gh_ctx.tree, key);
970}
971
972
973/* Exported interface, documented in global_history.h */
974bool_Bool global_history_has_selection(void)
975{
976 return treeview_has_selection(gh_ctx.tree);
977}
978
979
980/* Exported interface, documented in global_history.h */
981bool_Bool global_history_get_selection(nsurl **url, const char **title)
982{
983 struct global_history_entry *e;
984 enum treeview_node_type type;
985 void *v;
986
987 type = treeview_get_selection(gh_ctx.tree, &v);
988 if (type != TREE_NODE_ENTRY || v == NULL((void*)0)) {
989 *url = NULL((void*)0);
990 *title = NULL((void*)0);
991 return false0;
992 }
993
994 e = (struct global_history_entry *)v;
995
996 *url = e->url;
997 *title = e->data[GH_TITLE].value;
998 return true1;
999}
1000
1001
1002/* Exported interface, documented in global_history.h */
1003nserror global_history_expand(bool_Bool only_folders)
1004{
1005 return treeview_expand(gh_ctx.tree, only_folders);
1006}
1007
1008
1009/* Exported interface, documented in global_history.h */
1010nserror global_history_contract(bool_Bool all)
1011{
1012 return treeview_contract(gh_ctx.tree, all);
1013}
1014