bookmark.c (20339B)
1 /* bookmark.c 2 * (c) 2002 Petr 'Brain' Kulhavy, Karel 'Clock' Kulhavy 3 * This file is a part of the Links program, released under GPL. 4 */ 5 6 #include "links.h" 7 8 static struct stat bookmarks_st; 9 10 static struct list *bookmark_new_item(void *); 11 static unsigned char *bookmark_type_item(struct terminal *, struct list *, int); 12 static void bookmark_delete_item(struct list *); 13 static void bookmark_edit_item(struct dialog_data *, struct list *, 14 void (*)(struct dialog_data *, struct list *, 15 struct list *, 16 struct list_description *), 17 struct list *, unsigned char); 18 static void bookmark_copy_item(struct list *, struct list *); 19 static void bookmark_goto_item(struct session *, struct list *); 20 static void *bookmark_default_value(struct session *, unsigned char); 21 static struct list *bookmark_find_item(struct list *start, unsigned char *str, 22 int direction); 23 static void save_bookmarks(struct session *ses); 24 25 struct list bookmarks = { init_list_1st(&bookmarks.list_entry) 0, -1, NULL }; 26 27 static struct history bookmark_search_history = { 28 0, {&bookmark_search_history.items, &bookmark_search_history.items} 29 }; 30 31 /* when you change anything, don't forget to change it in reinit_bookmarks too 32 * !*/ 33 34 struct bookmark_ok_struct { 35 void (*fn)(struct dialog_data *, struct list *, struct list *, 36 struct list_description *); 37 struct list *data; 38 struct dialog_data *dlg; 39 }; 40 41 struct bookmark_list { 42 list_head_1st 43 /* bookmark specific */ 44 unsigned char *title; 45 unsigned char *url; 46 list_head_last 47 }; 48 49 static struct list_description bookmark_ld = { 50 1, /* 0= flat; 1=tree */ 51 &bookmarks, /* list */ 52 bookmark_new_item, /* no codepage translations */ 53 bookmark_edit_item, /* translate when create dialog and translate back 54 when ok is pressed */ 55 bookmark_default_value, /* codepage translation from 56 current_page_encoding to UTF8 */ 57 bookmark_delete_item, /* no codepage translations */ 58 bookmark_copy_item, /* no codepage translations */ 59 bookmark_type_item, /* no codepage translations (bookmarks are 60 internally in UTF8) */ 61 bookmark_find_item, 62 &bookmark_search_history, 63 0, /* codepage */ 64 15, /* # of items in main window */ 65 T_BOOKMARK, 66 T_BOOKMARKS_ALREADY_IN_USE, 67 T_BOOKMARK_MANAGER, 68 T_DELETE_BOOKMARK, 69 T_GOTO, 70 bookmark_goto_item, /* FIXME: should work (URL in UTF8), but who knows? 71 */ 72 save_bookmarks, 73 74 NULL, 75 NULL, 76 0, 77 0, /* internal vars */ 78 0, /* modified */ 79 NULL, 80 NULL, 81 0, 82 }; 83 84 struct kawasaki { 85 unsigned char *title; 86 unsigned char *url; 87 }; 88 89 /* clears the bookmark list */ 90 static void 91 free_bookmarks(void) 92 { 93 struct list *b = NULL; 94 struct list_head *lb; 95 96 foreach (struct list, b, lb, bookmarks.list_entry) { 97 struct bookmark_list *bm = 98 get_struct(b, struct bookmark_list, head); 99 free(bm->title); 100 free(bm->url); 101 lb = lb->prev; 102 del_from_list(b); 103 free(bm); 104 } 105 } 106 107 /* called before exiting the links */ 108 void 109 finalize_bookmarks(void) 110 { 111 free_bookmarks(); 112 free_history(bookmark_search_history); 113 } 114 115 /* allocates struct kawasaki and puts current page title and url */ 116 /* type: 0=item, 1=directory */ 117 /* on error returns NULL */ 118 static void * 119 bookmark_default_value(struct session *ses, unsigned char type) 120 { 121 struct kawasaki *zelena; 122 unsigned char *txt; 123 124 txt = xmalloc(MAX_STR_LEN); 125 126 zelena = xmalloc(sizeof(struct kawasaki)); 127 128 zelena->url = NULL; 129 zelena->title = NULL; 130 if (get_current_url(ses, txt, MAX_STR_LEN)) { 131 if (ses->screen->f_data) { 132 zelena->url = stracpy(txt); 133 clr_white(zelena->url); 134 } else 135 zelena->url = stracpy(txt); 136 } 137 /* ses->screen->f_data must exist here */ 138 if (get_current_title(ses->screen, txt, MAX_STR_LEN)) { 139 zelena->title = stracpy(txt); 140 clr_white(zelena->title); 141 } 142 143 free(txt); 144 145 return zelena; 146 } 147 148 static void 149 bookmark_copy_item(struct list *in, struct list *out) 150 { 151 struct bookmark_list *item_in = 152 get_struct(in, struct bookmark_list, head); 153 struct bookmark_list *item_out = 154 get_struct(out, struct bookmark_list, head); 155 156 item_out->head.type = item_in->head.type; 157 item_out->head.depth = item_in->head.depth; 158 159 free(item_out->title); 160 item_out->title = stracpy(item_in->title); 161 free(item_out->url); 162 item_out->url = stracpy(item_in->url); 163 } 164 165 static unsigned char *const bm_add_msg[] = { 166 TEXT_(T_NNAME), 167 TEXT_(T_URL), 168 }; 169 170 /* Called to setup the add bookmark dialog */ 171 static void 172 bookmark_edit_item_fn(struct dialog_data *dlg) 173 { 174 int max = 0, min = 0; 175 int w, rw; 176 int y = -1; 177 struct terminal *term; 178 int a; 179 180 term = dlg->win->term; 181 182 for (a = 0; a < dlg->n - 2; a++) { 183 max_text_width(term, bm_add_msg[a], &max, AL_LEFT); 184 min_text_width(term, bm_add_msg[a], &min, AL_LEFT); 185 } 186 max_buttons_width(term, dlg->items + dlg->n - 2, 2, &max); 187 min_buttons_width(term, dlg->items + dlg->n - 2, 2, &min); 188 w = term->x * 9 / 10 - 2 * DIALOG_LB; 189 190 if (w < min) 191 w = min; 192 193 rw = w; 194 195 for (a = 0; a < dlg->n - 2; a++, y++) 196 dlg_format_text_and_field(dlg, NULL, bm_add_msg[a], 197 &dlg->items[a], 0, &y, w, &rw, 198 COLOR_DIALOG_TEXT, AL_LEFT); 199 dlg_format_buttons(dlg, NULL, dlg->items + dlg->n - 2, 2, 0, &y, w, &rw, 200 AL_CENTER); 201 w = rw; 202 dlg->xw = w + 2 * DIALOG_LB; 203 dlg->yw = y + 2 * DIALOG_TB; 204 center_dlg(dlg); 205 draw_dlg(dlg); 206 y = dlg->y + DIALOG_TB; 207 for (a = 0; a < dlg->n - 2; a++, y++) 208 dlg_format_text_and_field(dlg, term, bm_add_msg[a], 209 &dlg->items[a], dlg->x + DIALOG_LB, 210 &y, w, NULL, COLOR_DIALOG_TEXT, 211 AL_LEFT); 212 dlg_format_buttons(dlg, term, &dlg->items[dlg->n - 2], 2, 213 dlg->x + DIALOG_LB, &y, w, NULL, AL_CENTER); 214 } 215 216 /* Puts url and title into the bookmark item */ 217 static void 218 bookmark_edit_done(void *data) 219 { 220 struct dialog *d = (struct dialog *)data; 221 struct bookmark_list *item = (struct bookmark_list *)d->udata; 222 unsigned char *title, *url; 223 struct bookmark_ok_struct *s = (struct bookmark_ok_struct *)d->udata2; 224 int a; 225 226 if (item->head.type & 1) 227 a = 4; 228 else 229 a = 5; 230 title = (unsigned char *)&d->items[a]; 231 url = title + MAX_STR_LEN; 232 233 free(item->title); 234 item->title = stracpy(title); 235 clr_white(item->title); 236 237 free(item->url); 238 item->url = stracpy(url); 239 clr_white(item->url); 240 241 s->fn(s->dlg, s->data, &item->head, &bookmark_ld); 242 d->udata = NULL; /* for abort function */ 243 } 244 245 /* destroys an item, this function is called when edit window is aborted */ 246 static void 247 bookmark_edit_abort(struct dialog_data *data) 248 { 249 struct bookmark_list *item = (struct bookmark_list *)data->dlg->udata; 250 struct dialog *dlg = data->dlg; 251 252 free(dlg->udata2); 253 if (item) 254 bookmark_delete_item(&item->head); 255 } 256 257 /* dlg_title is TITLE_EDIT or TITLE_ADD */ 258 /* edit item function */ 259 static void 260 bookmark_edit_item(struct dialog_data *dlg, struct list *data, 261 void (*ok_fn)(struct dialog_data *, struct list *, 262 struct list *, struct list_description *), 263 struct list *ok_arg, unsigned char dlg_title) 264 { 265 struct bookmark_list *item = 266 get_struct(data, struct bookmark_list, head); 267 unsigned char *title, *url, *txt; 268 struct dialog *d; 269 struct bookmark_ok_struct *s; 270 int a; 271 272 /* Create the dialog */ 273 s = xmalloc(sizeof(struct bookmark_ok_struct)); 274 s->fn = ok_fn; 275 s->data = ok_arg; 276 s->dlg = dlg; 277 278 if (item->head.type & 1) 279 a = 4; /* folder */ 280 else 281 a = 5; 282 d = mem_calloc(sizeof(struct dialog) + a * sizeof(struct dialog_item) 283 + 2 * MAX_STR_LEN); 284 285 title = (unsigned char *)&d->items[a]; 286 url = title + MAX_STR_LEN; 287 288 txt = stracpy(item->title); 289 clr_white(txt); 290 safe_strncpy(title, txt, MAX_STR_LEN); 291 free(txt); 292 293 txt = stracpy(item->url); 294 clr_white(txt); 295 safe_strncpy(url, txt, MAX_STR_LEN); 296 free(txt); 297 298 switch (dlg_title) { 299 case TITLE_EDIT: 300 if (item->head.type & 1) 301 d->title = TEXT_(T_EDIT_FOLDER); 302 else 303 d->title = TEXT_(T_EDIT_BOOKMARK); 304 break; 305 case TITLE_ADD: 306 if (item->head.type & 1) 307 d->title = TEXT_(T_ADD_FOLDER); 308 else 309 d->title = TEXT_(T_ADD_BOOKMARK); 310 break; 311 default: 312 internal("Unsupported dialog title.\n"); 313 } 314 d->fn = bookmark_edit_item_fn; 315 d->udata = item; 316 d->udata2 = s; 317 d->refresh = bookmark_edit_done; 318 d->abort = bookmark_edit_abort; 319 d->refresh_data = d; 320 321 d->items[0].type = D_FIELD; 322 d->items[0].dlen = MAX_STR_LEN; 323 d->items[0].data = title; 324 d->items[0].fn = check_nonempty; 325 326 a = 0; 327 if (!(item->head.type & 1)) { 328 d->items[1].type = D_FIELD; 329 d->items[1].dlen = MAX_STR_LEN; 330 d->items[1].data = url; 331 d->items[1].fn = check_nonempty; 332 a++; 333 } 334 335 d->items[a + 1].type = D_BUTTON; 336 d->items[a + 1].gid = B_ENTER; 337 d->items[a + 1].fn = ok_dialog; 338 d->items[a + 1].text = TEXT_(T_OK); 339 340 d->items[a + 2].type = D_BUTTON; 341 d->items[a + 2].gid = B_ESC; 342 d->items[a + 2].text = TEXT_(T_CANCEL); 343 d->items[a + 2].fn = cancel_dialog; 344 345 d->items[a + 3].type = D_END; 346 347 do_dialog(dlg->win->term, d, getml(d, NULL)); 348 } 349 350 /* create new bookmark item and returns pointer to it, on error returns 0*/ 351 /* bookmark is filled with given data, data are deallocated afterwards */ 352 static struct list * 353 bookmark_new_item(void *data) 354 { 355 struct bookmark_list *b; 356 struct kawasaki *zelena = (struct kawasaki *)data; 357 358 b = xmalloc(sizeof(struct bookmark_list)); 359 360 if (zelena && zelena->title) 361 b->title = zelena->title; 362 else 363 b->title = stracpy(cast_uchar ""); 364 365 if (zelena && zelena->url) 366 b->url = zelena->url; 367 else 368 b->url = stracpy(cast_uchar ""); 369 370 free(zelena); 371 372 return &b->head; 373 } 374 375 /* allocate string and print bookmark into it */ 376 /* x: 0=type all, 1=type title only */ 377 static unsigned char * 378 bookmark_type_item(struct terminal *term, struct list *data, int x) 379 { 380 unsigned char *txt, *txt1; 381 struct bookmark_list *item; 382 383 if (data == &bookmarks) /* head */ 384 return stracpy(get_text_translation(TEXT_(T_BOOKMARKS), term)); 385 386 item = get_struct(data, struct bookmark_list, head); 387 txt = stracpy(item->title); 388 389 if (!(item->head.type & 1)) { 390 add_to_strn(&txt, cast_uchar " ("); 391 add_to_strn(&txt, item->url); 392 add_to_strn(&txt, cast_uchar ")"); 393 } 394 395 txt1 = stracpy(txt); 396 clr_white(txt1); 397 free(txt); 398 return txt1; 399 } 400 401 /* goto bookmark (called when goto button is pressed) */ 402 static void 403 bookmark_goto_item(struct session *ses, struct list *i) 404 { 405 struct bookmark_list *item = get_struct(i, struct bookmark_list, head); 406 407 goto_url_utf8(ses, item->url); 408 } 409 410 /* delete bookmark from list */ 411 static void 412 bookmark_delete_item(struct list *data) 413 { 414 struct bookmark_list *item = 415 get_struct(data, struct bookmark_list, head); 416 417 if (item->head.list_entry.next) 418 del_from_list(&item->head); 419 free(item->url); 420 free(item->title); 421 free(item); 422 } 423 424 static int 425 substr_utf8(unsigned char *string, unsigned char *substr) 426 { 427 int r; 428 string = unicode_upcase_string(string); 429 substr = unicode_upcase_string(substr); 430 r = !!strstr((char *)string, (char *)substr); 431 free(string); 432 free(substr); 433 return r; 434 } 435 436 static int 437 test_entry(struct list *e, unsigned char *str) 438 { 439 struct bookmark_list *list = get_struct(e, struct bookmark_list, head); 440 if (substr_utf8(list->title, str)) 441 return 1; 442 return casestrstr(list->url, str); 443 } 444 445 static struct list * 446 bookmark_find_item(struct list *s, unsigned char *str, int direction) 447 { 448 struct list *e; 449 450 if (direction >= 0) 451 for (e = list_next(s); e != s; e = list_next(e)) { 452 if (e->depth >= 0 && test_entry(e, str)) 453 return e; 454 } 455 else 456 for (e = list_prev(s); e != s; e = list_prev(e)) 457 if (e->depth >= 0 && test_entry(e, str)) 458 return e; 459 460 if (e->depth >= 0 && test_entry(e, str)) 461 return e; 462 463 return NULL; 464 } 465 466 /* returns previous item in the same folder and with same the depth, or father 467 * if there's no previous item */ 468 /* we suppose that previous items have correct pointer fotr */ 469 static struct list * 470 previous_on_this_level(struct list *item) 471 { 472 struct list *p; 473 474 for (p = list_prev(item); p->depth > item->depth; p = p->fotr) 475 ; 476 return p; 477 } 478 479 /* create new bookmark at the end of the list */ 480 /* if url is NULL, create folder */ 481 /* both strings are null terminated */ 482 static void 483 add_bookmark(unsigned char *title, unsigned char *url, int depth) 484 { 485 struct bookmark_list *b; 486 struct list *p; 487 struct document_options *dop; 488 489 if (!title) 490 return; 491 492 b = xmalloc(sizeof(struct bookmark_list)); 493 494 dop = mem_calloc(sizeof(struct document_options)); 495 dop->cp = 0; 496 497 b->title = stracpy(title); 498 clr_white(b->title); 499 500 if (url) { 501 b->url = stracpy(url); 502 clr_white(b->url); 503 b->head.type = 0; 504 } else { 505 b->url = stracpy(cast_uchar ""); 506 b->head.type = 1; 507 } 508 509 b->head.depth = depth; 510 511 add_to_list_end(bookmarks.list_entry, &b->head); 512 513 p = previous_on_this_level(&b->head); 514 if (p->depth < b->head.depth) 515 /* directory b belongs into */ 516 b->head.fotr = p; 517 else 518 b->head.fotr = p->fotr; 519 520 free(dop); 521 } 522 523 static void 524 load_bookmarks(struct session *ses) 525 { 526 unsigned char *buf; 527 long len; 528 529 unsigned char *p, *end; 530 unsigned char *name, *attr; 531 int namelen; 532 int status; 533 unsigned char *title = 0; 534 unsigned char *url = 0; 535 int depth; 536 537 struct document_options dop; 538 int rs; 539 540 memset(&dop, 0, sizeof(dop)); 541 dop.plain = 1; 542 543 /* status: 544 * 0 = find <dt> or </dl> element 545 * 1 = find <a> or <h3> element 546 * 2 = reading bookmark, find </a> element, title is pointer 547 * behind the leading <a> element 548 * 3 = reading folder name, find </h3> element, title is 549 * pointer behind leading <h3> element 550 */ 551 552 buf = read_config_file(bookmarks_file); 553 if (!buf) { 554 bookmark_ld.modified = 1; 555 save_bookmarks(ses); 556 return; 557 } 558 559 len = strlen(cast_const_char buf); 560 561 p = buf; 562 end = buf + len; 563 564 status = 0; /* find bookmark */ 565 depth = 0; 566 567 d_opt = &dop; 568 while (1) { 569 unsigned char *s; 570 571 /* find start of html tag */ 572 for (; p < end && *p != '<'; p++) 573 ; 574 575 if (p >= end) 576 break; 577 s = p; 578 if (end - p >= 2 && (p[1] == '!' || p[1] == '?')) { 579 p = skip_comment(p, end); 580 continue; 581 } 582 if (parse_element(p, end, &name, &namelen, &attr, &p)) { 583 p++; 584 continue; 585 } 586 587 switch (status) { 588 case 0: /* <dt> or </dl> */ 589 if (namelen == 2 && !casecmp(name, cast_uchar "dt", 2)) 590 status = 1; 591 else if (namelen == 3 592 && !casecmp(name, cast_uchar "/dl", 3)) { 593 depth--; 594 if (depth == -1) 595 goto smitec; 596 } 597 continue; 598 599 case 1: /* find "a" element */ 600 if (namelen == 1 && !casecmp(name, cast_uchar "a", 1)) { 601 if (!(url = get_attr_val(attr, 602 cast_uchar "href"))) 603 continue; 604 status = 2; 605 title = p; 606 } else if (namelen == 2 607 && !casecmp(name, cast_uchar "h3", 1)) { 608 status = 3; 609 title = p; 610 } 611 continue; 612 613 case 2: /* find "/a" element */ 614 if (namelen != 2 || casecmp(name, cast_uchar "/a", 2)) 615 /* ignore all other elements */ 616 continue; 617 *s = 0; 618 add_bookmark(title, url, depth); 619 free(url); 620 status = 0; 621 continue; 622 623 case 3: /* find "/h3" element */ 624 if (namelen != 3 || casecmp(name, cast_uchar "/h3", 2)) 625 /* ignore all other elements */ 626 continue; 627 *s = 0; 628 add_bookmark(title, NULL, depth); 629 status = 0; 630 depth++; 631 continue; 632 } 633 } 634 if (status == 2) 635 free(url); 636 smitec: 637 free(buf); 638 d_opt = &dd_opt; 639 bookmark_ld.modified = 0; 640 641 EINTRLOOP(rs, stat((char *)bookmarks_file, &bookmarks_st)); 642 if (rs) 643 memset(&bookmarks_st, -1, sizeof bookmarks_st); 644 } 645 646 void 647 init_bookmarks(void) 648 { 649 memset(&bookmarks_st, -1, sizeof bookmarks_st); 650 if (!*bookmarks_file) { 651 unsigned char *e; 652 safe_strncpy(bookmarks_file, 653 links_home ? links_home : (unsigned char *)"", 654 MAX_STR_LEN); 655 e = cast_uchar strchr((char *)bookmarks_file, 0); 656 safe_strncpy(e, cast_uchar "bookmarks.html", 657 MAX_STR_LEN - (e - bookmarks_file)); 658 } 659 660 load_bookmarks(NULL); 661 } 662 663 void 664 reinit_bookmarks(struct session *ses, unsigned char *new_bookmarks_file) 665 { 666 unsigned char *buf; 667 if (test_list_window_in_use(&bookmark_ld, ses->term)) 668 return; 669 670 if (!strcmp((char *)bookmarks_file, (char *)new_bookmarks_file)) 671 goto save_only; 672 673 buf = read_config_file(new_bookmarks_file); 674 if (buf) { 675 free(buf); 676 free_bookmarks(); 677 safe_strncpy(bookmarks_file, new_bookmarks_file, MAX_STR_LEN); 678 load_bookmarks(ses); 679 reinit_list_window(&bookmark_ld); 680 } else { 681 save_only: 682 safe_strncpy(bookmarks_file, new_bookmarks_file, MAX_STR_LEN); 683 bookmark_ld.modified = 1; 684 save_bookmarks(ses); 685 } 686 } 687 688 /* gets str, converts all < = > & to appropriate entity 689 * returns allocated string with result 690 */ 691 static unsigned char * 692 convert_to_entity_string(unsigned char *str) 693 { 694 unsigned char *dst, *p = NULL; 695 size_t dstl = 0; 696 697 for (p = str; *p; p++) 698 switch (*p) { 699 case '<': 700 dstl = add_to_str(&dst, dstl, cast_uchar "<"); 701 break; 702 case '>': 703 dstl = add_to_str(&dst, dstl, cast_uchar ">"); 704 break; 705 706 case '=': 707 dstl = add_to_str(&dst, dstl, cast_uchar "="); 708 break; 709 710 case '&': 711 dstl = add_to_str(&dst, dstl, cast_uchar "&"); 712 break; 713 714 case '"': 715 dstl = add_to_str(&dst, dstl, cast_uchar """); 716 break; 717 718 default: 719 dstl = add_chr_to_str(&dst, dstl, *p); 720 } 721 return dst; 722 } 723 724 /* writes bookmarks to disk */ 725 static void 726 save_bookmarks(struct session *ses) 727 { 728 struct list *li = NULL; 729 struct list_head *lli; 730 int depth; 731 int a; 732 unsigned char *data; 733 size_t l; 734 int err; 735 int rs; 736 737 if (!bookmark_ld.modified) 738 return; 739 data = NULL; 740 l = add_to_str(&data, 0, 741 cast_uchar 742 "<HTML>\n" 743 "<HEAD>\n" 744 "<!-- This is an automatically generated file.\n" 745 "It will be read and overwritten.\n" 746 "Do Not Edit! -->\n" 747 "<TITLE>Links bookmarks</TITLE>\n" 748 "</HEAD>\n" 749 "<H1>Links bookmarks</H1>\n\n" 750 "<DL><P>\n"); 751 depth = 0; 752 foreach (struct list, li, lli, bookmarks.list_entry) { 753 struct bookmark_list *b = 754 get_struct(li, struct bookmark_list, head); 755 for (a = b->head.depth; a < depth; a++) 756 l = add_to_str(&data, l, cast_uchar "</DL>\n"); 757 depth = b->head.depth; 758 759 if (b->head.type & 1) { 760 unsigned char *txt, *txt1; 761 txt = stracpy(b->title); 762 clr_white(txt); 763 txt1 = convert_to_entity_string(txt); 764 l = add_to_str(&data, l, cast_uchar " <DT><H3>"); 765 l = add_to_str(&data, l, txt1); 766 l = add_to_str(&data, l, cast_uchar "</H3>\n<DL>\n"); 767 free(txt); 768 free(txt1); 769 depth++; 770 } else { 771 unsigned char *txt1, *txt2, *txt11; 772 txt1 = stracpy(b->title); 773 clr_white(txt1); 774 txt2 = stracpy(b->url); 775 clr_white(txt2); 776 txt11 = convert_to_entity_string(txt1); 777 l = add_to_str(&data, l, 778 cast_uchar " <DT><A HREF=\""); 779 l = add_to_str(&data, l, txt2); 780 l = add_to_str(&data, l, cast_uchar "\">"); 781 l = add_to_str(&data, l, txt11); 782 l = add_to_str(&data, l, cast_uchar "</A>\n"); 783 free(txt1); 784 free(txt2); 785 free(txt11); 786 } 787 } 788 for (a = 0; a < depth; a++) 789 l = add_to_str(&data, l, cast_uchar "</DL>\n"); 790 l = add_to_str(&data, l, 791 cast_uchar "</DL><P>\n" 792 "</HTML>\n"); 793 err = write_to_config_file(bookmarks_file, data, 1); 794 free(data); 795 if (!err) 796 bookmark_ld.modified = 0; 797 else if (ses) { 798 unsigned char *f = stracpy(bookmarks_file); 799 msg_box(ses->term, getml(f, NULL), TEXT_(T_BOOKMARK_ERROR), 800 AL_CENTER, TEXT_(T_UNABLE_TO_WRITE_TO_BOOKMARK_FILE), 801 cast_uchar " ", f, cast_uchar ": ", get_err_msg(err), 802 MSG_BOX_END, NULL, 1, TEXT_(T_CANCEL), msg_box_null, 803 B_ENTER | B_ESC); 804 } 805 806 EINTRLOOP(rs, stat(cast_const_char bookmarks_file, &bookmarks_st)); 807 if (rs) 808 memset(&bookmarks_st, -1, sizeof bookmarks_st); 809 } 810 811 void 812 menu_bookmark_manager(struct terminal *term, void *fcp, void *ses_) 813 { 814 struct session *ses = (struct session *)ses_; 815 struct stat st; 816 int rs; 817 EINTRLOOP(rs, stat(cast_const_char bookmarks_file, &st)); 818 if (!rs 819 && (st.st_ctime != bookmarks_st.st_ctime 820 || st.st_mtime != bookmarks_st.st_mtime 821 || st.st_size != bookmarks_st.st_size)) { 822 if (!test_list_window_in_use(&bookmark_ld, NULL)) { 823 free_bookmarks(); 824 load_bookmarks(ses); 825 reinit_list_window(&bookmark_ld); 826 } 827 } 828 create_list_window(&bookmark_ld, &bookmarks, term, ses); 829 }