summaryrefslogtreecommitdiff
path: root/bookmarks.c
diff options
context:
space:
mode:
Diffstat (limited to 'bookmarks.c')
-rw-r--r--bookmarks.c900
1 files changed, 900 insertions, 0 deletions
diff --git a/bookmarks.c b/bookmarks.c
new file mode 100644
index 0000000..3a094c6
--- /dev/null
+++ b/bookmarks.c
@@ -0,0 +1,900 @@
+/* bookmarks.c
+ * (c) 2002 Petr 'Brain' Kulhavy, Karel 'Clock' Kulhavy
+ * This file is a part of the Links program, released under GPL.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "links.h"
+
+#define SEARCH_IN_URL
+
+#ifdef SEARCH_IN_URL
+#define SHOW_URL
+#endif
+
+int bookmarks_codepage=0;
+
+unsigned char bookmarks_file[MAX_STR_LEN]="";
+
+static struct stat bookmarks_st;
+
+static void *bookmark_new_item(void *);
+static unsigned char *bookmark_type_item(struct terminal *, void *, int);
+static void bookmark_delete_item(void *);
+static void bookmark_edit_item(struct dialog_data *,void *,void (*)(struct dialog_data *,void *,void *,struct list_description *),void *, unsigned char);
+static void bookmark_copy_item(void *, void *);
+static void bookmark_goto_item(struct session *, void *);
+static void *bookmark_default_value(struct session*, unsigned char);
+static void *bookmark_find_item(void *start, unsigned char *str, int direction);
+static void save_bookmarks(struct session *ses);
+
+struct list bookmarks={&bookmarks,&bookmarks,0,-1,NULL};
+
+static struct history bookmark_search_history = { 0, { &bookmark_search_history.items, &bookmark_search_history.items } };
+
+/* when you change anything, don't forget to change it in reinit_bookmarks too !*/
+
+struct bookmark_ok_struct{
+ void (*fn)(struct dialog_data *,void *,void *,struct list_description *);
+ void *data;
+ struct dialog_data *dlg;
+};
+
+
+struct bookmark_list{
+ /* common for all lists */
+ struct bookmark_list *next;
+ struct bookmark_list *prev;
+ unsigned char type;
+ int depth;
+ void *fotr;
+
+ /* bookmark specific */
+ unsigned char *title;
+ unsigned char *url;
+};
+
+
+static struct list_description bookmark_ld=
+{
+ 1, /* 0= flat; 1=tree */
+ &bookmarks, /* list */
+ bookmark_new_item, /* no codepage translations */
+ bookmark_edit_item, /* translate when create dialog and translate back when ok is pressed */
+ bookmark_default_value, /* codepage translation from current_page_encoding to UTF8 */
+ bookmark_delete_item, /* no codepage translations */
+ bookmark_copy_item, /* no codepage translations */
+ bookmark_type_item, /* no codepage translations (bookmarks are internally in UTF8) */
+ bookmark_find_item,
+ &bookmark_search_history,
+ 0, /* this is set in init_bookmarks function */
+ 15, /* # of items in main window */
+ T_BOOKMARK,
+ T_BOOKMARKS_ALREADY_IN_USE,
+ T_BOOKMARK_MANAGER,
+ T_DELETE_BOOKMARK,
+ T_GOTO,
+ bookmark_goto_item, /* FIXME: should work (URL in UTF8), but who knows? */
+ save_bookmarks,
+
+ NULL,NULL,0,0, /* internal vars */
+ 0, /* modified */
+ NULL,
+ NULL,
+ 0,
+};
+
+
+struct kawasaki
+{
+ unsigned char *title;
+ unsigned char *url;
+};
+
+
+/* clears the bookmark list */
+static void free_bookmarks(void)
+{
+ struct bookmark_list *bm;
+
+ foreach(bm, bookmarks) {
+ mem_free(bm->title);
+ mem_free(bm->url);
+ }
+
+ free_list(bookmarks);
+ free_list(bookmark_search_history.items);
+}
+
+
+/* called before exiting the links */
+void finalize_bookmarks(void)
+{
+ free_bookmarks();
+}
+
+
+
+/* allocates struct kawasaki and puts current page title and url */
+/* type: 0=item, 1=directory */
+/* on error returns NULL */
+static void *bookmark_default_value(struct session *ses, unsigned char type)
+{
+ struct kawasaki *zelena;
+ unsigned char *txt;
+
+ txt=mem_alloc(MAX_STR_LEN);
+
+ zelena=mem_alloc(sizeof(struct kawasaki));
+
+ zelena->url=NULL;
+ zelena->title=NULL;
+ if (get_current_url(ses,txt,MAX_STR_LEN))
+ {
+ if (ses->screen->f_data)
+ {
+ struct conv_table* ct;
+
+ ct=get_translation_table(ses->term->spec->charset,bookmark_ld.codepage);
+ zelena->url=convert_string(ct,txt,strlen(txt),NULL);
+ clr_white(zelena->url);
+ }
+ else
+ zelena->url=stracpy(txt);
+ }
+ if (get_current_title(ses,txt,MAX_STR_LEN)) /* ses->screen->f_data must exist here */
+ {
+ struct conv_table* ct;
+
+ ct=get_translation_table(ses->term->spec->charset,bookmark_ld.codepage);
+ zelena->title=convert_string(ct,txt,strlen(txt),NULL);
+ clr_white(zelena->title);
+ }
+
+ mem_free(txt);
+
+ return zelena;
+}
+
+
+static void bookmark_copy_item(void *in, void *out)
+{
+ struct bookmark_list *item_in=(struct bookmark_list*)in;
+ struct bookmark_list *item_out=(struct bookmark_list*)out;
+
+ item_out->type=item_in->type;
+ item_out->depth=item_in->depth;
+
+ if (item_out->title)
+ {
+ mem_free(item_out->title);
+ item_out->title=stracpy(item_in->title);
+ }
+ else internal("Bookmarks inconsistency.\n");
+ if (item_out->url)
+ {
+ mem_free(item_out->url);
+ item_out->url=stracpy(item_in->url);
+ }
+ else internal("Bookmarks inconsistency.\n");
+ return;
+}
+
+
+static unsigned char *bm_add_msg[] = {
+ TEXT_(T_NNAME),
+ TEXT_(T_URL),
+};
+
+
+/* Called to setup the add bookmark dialog */
+static void bookmark_edit_item_fn(struct dialog_data *dlg)
+{
+ int max = 0, min = 0;
+ int w, rw;
+ int y = gf_val(-1, -1*G_BFU_FONT_SIZE);
+ struct terminal *term;
+ int a;
+ if (dlg->win->term->spec->braille) y += gf_val(1, G_BFU_FONT_SIZE);
+
+ term = dlg->win->term;
+
+ for (a=0;a<dlg->n-2;a++)
+ {
+ max_text_width(term, bm_add_msg[a], &max, AL_LEFT);
+ min_text_width(term, bm_add_msg[a], &min, AL_LEFT);
+ }
+ max_buttons_width(term, dlg->items + dlg->n-2, 2, &max);
+ min_buttons_width(term, dlg->items + dlg->n-2, 2, &min);
+ w = term->x * 9 / 10 - 2 * DIALOG_LB;
+
+ /*if (w > max) w = max;*/
+ if (w < min) w = min;
+ /*
+ if (w > term->x - 2 * DIALOG_LB) w = term->x - 2 * DIALOG_LB;
+ if (w < 1) w = 1;
+ */
+
+ /*w = rw = gf_val(50,30*G_BFU_FONT_SIZE);*/
+ rw = w;
+
+ for (a=0;a<dlg->n-2;a++)
+ {
+ dlg_format_text_and_field(dlg, NULL, bm_add_msg[a], &dlg->items[a], 0, &y, w, &rw, COLOR_DIALOG_TEXT, AL_LEFT);
+ y += gf_val(1,1*G_BFU_FONT_SIZE);
+ }
+ dlg_format_buttons(dlg, NULL, dlg->items+dlg->n-2, 2, 0, &y, w, &rw, AL_CENTER);
+ w = rw;
+ dlg->xw = w + 2 * DIALOG_LB;
+ dlg->yw = y + 2 * DIALOG_TB;
+ center_dlg(dlg);
+ draw_dlg(dlg);
+ y = dlg->y + DIALOG_TB;
+ if (dlg->win->term->spec->braille) y += gf_val(1, G_BFU_FONT_SIZE);
+ for (a=0;a<dlg->n-2;a++)
+ {
+ dlg_format_text_and_field(dlg, term, bm_add_msg[a], &dlg->items[a], dlg->x + DIALOG_LB, &y, w, NULL, COLOR_DIALOG_TEXT, AL_LEFT);
+ y+=gf_val(1,G_BFU_FONT_SIZE);
+ }
+ dlg_format_buttons(dlg, term, &dlg->items[dlg->n-2], 2, dlg->x + DIALOG_LB, &y, w, NULL, AL_CENTER);
+}
+
+
+/* Puts url and title into the bookmark item */
+static void bookmark_edit_done(void *data)
+{
+ struct dialog *d=(struct dialog*)data;
+ struct bookmark_list *item=(struct bookmark_list *)d->udata;
+ unsigned char *title, *url;
+ struct bookmark_ok_struct* s=(struct bookmark_ok_struct*)d->udata2;
+ int a;
+
+ if ((item->type)&1)a=4; /* folder */
+ else a=5;
+ title = (unsigned char *)&d->items[a];
+ url = title + MAX_STR_LEN;
+
+ if (item->title)
+ {
+ struct conv_table* ct;
+
+ mem_free(item->title);
+ ct=get_translation_table(s->dlg->win->term->spec->charset,bookmark_ld.codepage);
+ item->title=convert_string(ct,title,strlen(title),NULL);
+ clr_white(item->title);
+ }
+
+ if (item->url)
+ {
+ struct conv_table* ct;
+
+ mem_free(item->url);
+ ct=get_translation_table(s->dlg->win->term->spec->charset,bookmark_ld.codepage);
+ item->url=convert_string(ct,url,strlen(url),NULL);
+ clr_white(item->url);
+ }
+
+ s->fn(s->dlg,s->data,item,&bookmark_ld);
+ d->udata=NULL; /* for abort function */
+}
+
+
+/* destroys an item, this function is called when edit window is aborted */
+static void bookmark_edit_abort(struct dialog_data *data)
+{
+ struct bookmark_list *item=(struct bookmark_list*)data->dlg->udata;
+ struct dialog *dlg=data->dlg;
+
+ mem_free(dlg->udata2);
+ if (item)bookmark_delete_item(item);
+}
+
+
+/* dlg_title is TITLE_EDIT or TITLE_ADD */
+/* edit item function */
+static void bookmark_edit_item(struct dialog_data *dlg,void *data,void (*ok_fn)(struct dialog_data *,void * ,void *, struct list_description *),void * ok_arg, unsigned char dlg_title)
+{
+ struct bookmark_list *item=(struct bookmark_list *)data;
+ unsigned char *title, *url;
+ struct dialog *d;
+ struct bookmark_ok_struct *s;
+ int a;
+
+ /* Create the dialog */
+ s=mem_alloc(sizeof(struct bookmark_ok_struct));
+ s->fn=ok_fn;
+ s->data=ok_arg;
+ s->dlg=dlg;
+
+
+ if ((item->type)&1)a=4; /* folder */
+ else a=5;
+ d = mem_calloc(sizeof(struct dialog) + a * sizeof(struct dialog_item) + 2 * MAX_STR_LEN);
+
+
+ title = (unsigned char *)&d->items[a];
+ url = title + MAX_STR_LEN;
+
+ {
+ unsigned char *txt;
+ struct conv_table* ct;
+
+ ct=get_translation_table(bookmark_ld.codepage,dlg->win->term->spec->charset);
+ txt=convert_string(ct,item->title,strlen(item->title),NULL);
+ clr_white(txt);
+ safe_strncpy(title,txt,MAX_STR_LEN);
+ mem_free(txt);
+
+ txt=convert_string(ct,item->url,strlen(item->url),NULL);
+ clr_white(txt);
+ safe_strncpy(url,txt,MAX_STR_LEN);
+ mem_free(txt);
+ }
+
+ switch (dlg_title)
+ {
+ case TITLE_EDIT:
+ if ((item->type)&1)d->title=TEXT_(T_EDIT_FOLDER);
+ else d->title=TEXT_(T_EDIT_BOOKMARK);
+ break;
+
+ case TITLE_ADD:
+ if ((item->type)&1)d->title=TEXT_(T_ADD_FOLDER);
+ else d->title=TEXT_(T_ADD_BOOKMARK);
+ break;
+
+ default:
+ internal("Unsupported dialog title.\n");
+ }
+ d->fn = bookmark_edit_item_fn;
+ d->udata=data; /* item */
+ d->udata2=s;
+ d->refresh = bookmark_edit_done;
+ d->abort = bookmark_edit_abort;
+ d->refresh_data = d;
+
+ d->items[0].type = D_FIELD;
+ d->items[0].dlen = MAX_STR_LEN;
+ d->items[0].data = title;
+ d->items[0].fn = check_nonempty;
+
+ a=0;
+ if (!((item->type)&1)) /* item */
+ {
+ d->items[1].type = D_FIELD;
+ d->items[1].dlen = MAX_STR_LEN;
+ d->items[1].data = url;
+ d->items[1].fn = check_nonempty;
+ a++;
+ }
+
+ d->items[a+1].type = D_BUTTON;
+ d->items[a+1].gid = B_ENTER;
+ d->items[a+1].fn = ok_dialog;
+ d->items[a+1].text = TEXT_(T_OK);
+
+ d->items[a+2].type = D_BUTTON;
+ d->items[a+2].gid = B_ESC;
+ d->items[a+2].text = TEXT_(T_CANCEL);
+ d->items[a+2].fn = cancel_dialog;
+
+ d->items[a+3].type = D_END;
+
+ do_dialog(dlg->win->term, d, getml(d, NULL));
+}
+
+
+/* create new bookmark item and returns pointer to it, on error returns 0*/
+/* bookmark is filled with given data, data are deallocated afterwards */
+static void *bookmark_new_item(void * data)
+{
+ struct bookmark_list *b;
+ struct kawasaki *zelena=(struct kawasaki *)data;
+
+ b=mem_alloc(sizeof(struct bookmark_list));
+
+ b->url=mem_alloc(1);
+ b->title=mem_alloc(1);
+
+ *(b->url)=0; /* empty strings */
+ *(b->title)=0;
+
+ if (!zelena) return b;
+
+ if (zelena->title)
+ {
+ add_to_strn(&(b->title),zelena->title);
+ mem_free(zelena->title);
+ }
+ if (zelena->url)
+ {
+ add_to_strn(&(b->url),zelena->url);
+ mem_free(zelena->url);
+ }
+
+ mem_free(zelena);
+
+ return b;
+}
+
+
+/* allocate string and print bookmark into it */
+/* x: 0=type all, 1=type title only */
+static unsigned char *bookmark_type_item(struct terminal *term, void *data, int x)
+{
+ unsigned char *txt, *txt1;
+ struct bookmark_list* item=(struct bookmark_list*)data;
+ struct conv_table *table;
+
+ if (item==(struct bookmark_list*)(void *)&bookmarks) /* head */
+ return stracpy(_(TEXT_(T_BOOKMARKS),term));
+
+ txt=stracpy(item->title);
+#ifdef SHOW_URL
+ x=0;
+#endif
+ if (!x&&!((item->type)&1))
+ {
+ add_to_strn(&txt," (");
+ if (item->url)add_to_strn(&txt,item->url);
+ add_to_strn(&txt,")");
+ }
+
+ table=get_translation_table(bookmark_ld.codepage,term->spec->charset);
+ txt1=convert_string(table,txt,strlen(txt),NULL);
+ clr_white(txt1);
+ mem_free(txt);
+ return txt1;
+}
+
+
+/* goto bookmark (called when goto button is pressed) */
+static void bookmark_goto_item(struct session *ses, void *i)
+{
+ struct bookmark_list *item=(struct bookmark_list*)i;
+
+ goto_url(ses,item->url);
+}
+
+
+/* delete bookmark from list */
+static void bookmark_delete_item(void *data)
+{
+ struct bookmark_list* item=(struct bookmark_list*)data;
+ struct bookmark_list *prev=item->prev;
+ struct bookmark_list *next=item->next;
+
+ if (list_empty(*item)||((struct list*)data==&bookmarks))return; /* empty list or head */
+ if (item->url)mem_free(item->url);
+ if (item->title)mem_free(item->title);
+ if (next)next->prev=item->prev;
+ if (prev)prev->next=item->next;
+ mem_free(item);
+}
+
+static int substr_utf8(unsigned char *string, unsigned char *substr)
+{
+ int r;
+ string = unicode_upcase_string(string);
+ substr = unicode_upcase_string(substr);
+ r = !!strstr(string, substr);
+ mem_free(string);
+ mem_free(substr);
+ return r;
+}
+
+static void * bookmark_find_item(void *start, unsigned char *str, int direction)
+{
+ struct bookmark_list *b,*s=(struct bookmark_list *)start;
+
+
+ if (direction==1)
+ {
+ for (b=s->next; b!=s; b=b->next)
+ if (b->depth>-1)
+ {
+ if (b->title && substr_utf8(b->title,str)) return b;
+#ifdef SEARCH_IN_URL
+ if (b->url && casestrstr(b->url,str)) return b;
+#endif
+ }
+ }
+ else
+ {
+ for (b=s->prev; b!=s; b=b->prev)
+ if (b->depth>-1)
+ {
+ if (b->title && substr_utf8(b->title,str)) return b;
+#ifdef SEARCH_IN_URL
+ if (b->url && casestrstr(b->url,str)) return b;
+#endif
+ }
+ }
+ if (b==s&&b->depth>-1&&b->title && substr_utf8(b->title,str)) return b;
+#ifdef SEARCH_IN_URL
+ if (b==s&&b->depth>-1&&b->url && casestrstr(b->url,str)) return b;
+#endif
+
+ return NULL;
+}
+
+
+/* returns previous item in the same folder and with same the depth, or father if there's no previous item */
+/* we suppose that previous items have correct pointer fotr */
+static struct bookmark_list *previous_on_this_level(struct bookmark_list *item)
+{
+ struct bookmark_list *p;
+
+ for (p=item->prev;p->depth>item->depth;p=p->fotr)
+ ;
+ return p;
+}
+
+
+/* create new bookmark at the end of the list */
+/* if url is NULL, create folder */
+/* both strings are null terminated */
+static void add_bookmark(unsigned char *title, unsigned char *url, int depth)
+{
+ struct bookmark_list *b,*p;
+ struct document_options *dop;
+
+ if (!title) return;
+
+ b=mem_alloc(sizeof(struct bookmark_list));
+
+ dop=mem_calloc(sizeof(struct document_options));
+ dop->cp=bookmarks_codepage;
+
+ {
+ struct conv_table* ct;
+
+ ct=get_translation_table(bookmarks_codepage,bookmark_ld.codepage);
+ b->title=convert_string(ct,title,strlen(title),dop);
+ clr_white(b->title);
+ }
+
+ if (url)
+ {
+ struct conv_table* ct;
+
+ dop->plain=1;
+ ct=get_translation_table(bookmarks_codepage,bookmark_ld.codepage);
+ b->url=convert_string(ct,url,strlen(url),dop);
+ clr_white(b->url);
+ dop->plain=0;
+
+ b->type=0;
+ }
+ else
+ {
+ b->url=mem_alloc(1);
+ *(b->url)=0;
+ b->type=1;
+ }
+
+ b->depth=depth;
+
+ p=bookmarks.prev;
+ b->prev=p;
+ b->next=(struct bookmark_list *)(void *)&bookmarks;
+ p->next=b;
+ bookmarks.prev=b;
+
+
+ p=previous_on_this_level(b);
+ if (p->depth<b->depth)b->fotr=p; /* directory b belongs into */
+ else b->fotr=p->fotr;
+ mem_free(dop);
+}
+
+/* Created pre-cooked bookmarks */
+static void create_initial_bookmarks(void)
+{
+ bookmarks_codepage=utf8_table;
+ add_bookmark("Links",NULL,0);
+ add_bookmark("English",NULL,1);
+ add_bookmark("Calibration Procedure","http://atrey.karlin.mff.cuni.cz/~clock/twibright/links/calibration.html",2);
+ add_bookmark("Links Homepage","http://atrey.karlin.mff.cuni.cz/~clock/twibright/links/",2);
+ add_bookmark("Links Manual","http://links.twibright.com/user_en.html",2);
+ add_bookmark("Cesky",NULL,1);
+ add_bookmark("Kalibracni procedura","http://atrey.karlin.mff.cuni.cz/~clock/twibright/links/kalibrace.html",2);
+ add_bookmark("Links: domaci stranka","http://atrey.karlin.mff.cuni.cz/~clock/twibright/links/index_cz.html",2);
+ add_bookmark("Manual k Linksu","http://links.twibright.com/user.html",2);
+}
+
+static void load_bookmarks(struct session *ses)
+{
+ unsigned char *buf;
+ long len;
+
+ unsigned char *p, *end;
+ unsigned char *name, *attr;
+ int namelen;
+ int status;
+ unsigned char *title=0;
+ unsigned char *url=0;
+ int depth;
+
+ struct document_options dop;
+ int rs;
+
+ memset(&dop, 0, sizeof(dop));
+ dop.plain=1;
+
+ /* status:
+ * 0 = find <dt> or </dl> element
+ * 1 = find <a> or <h3> element
+ * 2 = reading bookmark, find </a> element, title is pointer
+ * behind the leading <a> element
+ * 3 = reading folder name, find </h3> element, title is
+ * pointer behind leading <h3> element
+ */
+
+ buf=read_config_file(bookmarks_file);
+ if (!buf){
+ create_initial_bookmarks();
+ bookmark_ld.modified=1;
+ save_bookmarks(ses);
+ return;
+ }
+
+ len=strlen(buf);
+
+ p=buf;
+ end=buf+len;
+
+ status=0; /* find bookmark */
+ depth=0;
+
+ d_opt=&dop;
+ while (1)
+ {
+ unsigned char *s;
+
+ while (p<end&&*p!='<')p++; /* find start of html tag */
+ if (p>=end)break; /* parse end */
+ s=p;
+ if (p+2<=end&&(p[1]=='!'||p[1]=='?')){p=skip_comment(p,end);continue;}
+ if (parse_element(p, end, &name, &namelen, &attr, &p)){p++;continue;}
+
+ switch (status)
+ {
+ case 0: /* <dt> or </dl> */
+ if (namelen==2&&!casecmp(name,"dt",2))
+ status=1;
+ else if (namelen==3&&!casecmp(name,"/dl",3))
+ {
+ depth--;
+ if (depth==-1)goto smitec;
+ }
+ continue;
+
+ case 1: /* find "a" element */
+ if (namelen==1&&!casecmp(name,"a",1))
+ {
+ if (!(url=get_attr_val(attr,"href")))continue;
+ status=2;
+ title=p;
+ }
+ if (namelen==2&&!casecmp(name,"h3",1))
+ {
+ status=3;
+ title=p;
+ }
+ continue;
+
+ case 2: /* find "/a" element */
+ if (namelen!=2||casecmp(name,"/a",2))continue; /* ignore all other elements */
+ *s=0;
+ add_bookmark(title,url,depth);
+ mem_free(url);
+ status=0;
+ continue;
+
+ case 3: /* find "/h3" element */
+ if (namelen!=3||casecmp(name,"/h3",2))continue; /* ignore all other elements */
+ *s=0;
+ add_bookmark(title,NULL,depth);
+ status=0;
+ depth++;
+ continue;
+ }
+ }
+ if (status==2)mem_free(url);
+smitec:
+ mem_free(buf);
+ d_opt=&dd_opt;
+ bookmark_ld.modified=0;
+
+ EINTRLOOP(rs, stat(bookmarks_file, &bookmarks_st));
+ if (rs)
+ memset(&bookmarks_st, 0, sizeof bookmarks_st);
+}
+
+void init_bookmarks(void)
+{
+ memset(&bookmarks_st, 0, sizeof bookmarks_st);
+ if (!*bookmarks_file)
+ snprintf(bookmarks_file,MAX_STR_LEN,"%sbookmarks.html",links_home?links_home:(unsigned char*)"");
+
+ bookmark_ld.codepage=utf8_table;
+ load_bookmarks(NULL);
+}
+
+void reinit_bookmarks(struct session *ses, unsigned char *new_bookmarks_file, int new_bookmarks_codepage)
+{
+ unsigned char *buf;
+ if (test_list_window_in_use(&bookmark_ld, ses->term))
+ return;
+
+ if (!strcmp(bookmarks_file, new_bookmarks_file)) {
+ goto save_only;
+ }
+
+ buf=read_config_file(new_bookmarks_file);
+ if (buf) {
+ mem_free(buf);
+ free_bookmarks();
+ safe_strncpy(bookmarks_file,new_bookmarks_file,MAX_STR_LEN);
+ bookmarks_codepage=new_bookmarks_codepage;
+ load_bookmarks(ses);
+ reinit_list_window(&bookmark_ld);
+ } else {
+ save_only:
+ safe_strncpy(bookmarks_file,new_bookmarks_file,MAX_STR_LEN);
+ bookmarks_codepage=new_bookmarks_codepage;
+ bookmark_ld.modified=1;
+ save_bookmarks(ses);
+ }
+}
+
+
+/* gets str, converts all < = > & to appropriate entity
+ * returns allocated string with result
+ */
+static unsigned char *convert_to_entity_string(unsigned char *str)
+{
+ unsigned char *dst, *p, *q;
+ int size;
+
+ for (size=1,p=str;*p;size+=*p=='&'?5:*p=='<'||*p=='>'||*p=='='?4:*p=='"'?6:1,p++)
+ ;
+
+ dst=mem_alloc(size);
+
+ for (p=str,q=dst;*p;p++,q++)
+ {
+ switch(*p)
+ {
+ case '<':
+ case '>':
+ q[0]='&',q[1]=*p=='<'?'l':'g',q[2]='t',q[3]=';',q+=3;
+ break;
+
+ case '=':
+ q[0]='&',q[1]='e',q[2]='q',q[3]=';',q+=3;
+ break;
+
+ case '&':
+ q[0]='&',q[1]='a',q[2]='m',q[3]='p',q[4]=';',q+=4;
+ break;
+
+ case '"':
+ q[0]='&',q[1]='q',q[2]='u',q[3]='o',q[4]='t',q[5]=';',q+=5;
+ break;
+
+ default:
+ *q=*p;
+ break;
+ }
+ }
+ *q=0;
+ return dst;
+}
+
+/* writes bookmarks to disk */
+static void save_bookmarks(struct session *ses)
+{
+ struct bookmark_list *b;
+ int depth;
+ int a;
+ struct conv_table *ct;
+ unsigned char *data;
+ int l;
+ int err;
+ int rs;
+
+ if (!bookmark_ld.modified)return;
+ ct=get_translation_table(bookmark_ld.codepage,bookmarks_codepage);
+ data=init_str();
+ l=0;
+ add_to_str(&data, &l,
+ "<HTML>\n"
+ "<HEAD>\n"
+ "<!-- This is an automatically generated file.\n"
+ "It will be read and overwritten.\n"
+ "Do Not Edit! -->\n"
+ "<TITLE>Links bookmarks</TITLE>\n"
+ "</HEAD>\n"
+ "<H1>Links bookmarks</H1>\n\n"
+ "<DL><P>\n"
+ );
+ depth=0;
+ foreach(b,bookmarks)
+ {
+ for (a=b->depth;a<depth;a++)add_to_str(&data, &l,"</DL>\n");
+ depth=b->depth;
+
+ if ((b->type)&1)
+ {
+ unsigned char *txt, *txt1;
+ txt=convert_string(ct,b->title,strlen(b->title),NULL);
+ clr_white(txt);
+ txt1=convert_to_entity_string(txt);
+ add_to_str(&data, &l, " <DT><H3>");
+ add_to_str(&data, &l, txt1);
+ add_to_str(&data, &l, "</H3>\n<DL>\n");
+ mem_free(txt);
+ mem_free(txt1);
+ depth++;
+ }
+ else
+ {
+ unsigned char *txt1, *txt2, *txt11;
+ txt1=convert_string(ct,b->title,strlen(b->title),NULL);
+ clr_white(txt1);
+ txt2=convert_string(ct,b->url,strlen(b->url),NULL);
+ clr_white(txt2);
+ txt11=convert_to_entity_string(txt1);
+ add_to_str(&data, &l, " <DT><A HREF=\"");
+ add_to_str(&data, &l, txt2);
+ add_to_str(&data, &l, "\">");
+ add_to_str(&data, &l, txt11);
+ add_to_str(&data, &l, "</A>\n");
+ mem_free(txt1);
+ mem_free(txt2);
+ mem_free(txt11);
+ }
+ }
+ for (a=0;a<depth;a++)add_to_str(&data, &l,"</DL>\n");
+ add_to_str(&data, &l,
+ "</DL><P>\n"
+ "</HTML>\n"
+ );
+ err = write_to_config_file(bookmarks_file, data);
+ mem_free(data);
+ if (!err) {
+ bookmark_ld.modified=0;
+ } else {
+ if (ses) {
+ unsigned char *f = stracpy(bookmarks_file);
+ msg_box(ses->term, getml(f, NULL), TEXT_(T_BOOKMARK_ERROR), AL_CENTER | AL_EXTD_TEXT, TEXT_(T_UNABLE_TO_WRITE_TO_BOOKMARK_FILE), " ", f, ": ", get_err_msg(err), NULL, NULL, 1, TEXT_(T_CANCEL), NULL, B_ENTER | B_ESC);
+ }
+ }
+
+ EINTRLOOP(rs, stat(bookmarks_file, &bookmarks_st));
+ if (rs)
+ memset(&bookmarks_st, 0, sizeof bookmarks_st);
+}
+
+void menu_bookmark_manager(struct terminal *term,void *fcp,struct session *ses)
+{
+ struct stat st;
+ int rs;
+ EINTRLOOP(rs, stat(bookmarks_file, &st));
+ if (!rs &&
+ (st.st_ctime != bookmarks_st.st_ctime ||
+ st.st_mtime != bookmarks_st.st_mtime ||
+ st.st_size != bookmarks_st.st_size)) {
+ if (!test_list_window_in_use(&bookmark_ld, NULL)) {
+ free_bookmarks();
+ load_bookmarks(ses);
+ reinit_list_window(&bookmark_ld);
+ }
+ }
+ create_list_window(&bookmark_ld,&bookmarks,term,ses);
+}
+