"Fossies" - the Fresh Open Source Software Archive

Member "links-1.03/session.c" (21 Nov 2011, 49564 Bytes) of archive /linux/www/links-1.03.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "session.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.8_vs_1.03.

    1 #include "links.h"
    2 
    3 struct list_head downloads = {&downloads, &downloads};
    4 
    5 int are_there_downloads()
    6 {
    7     int d = 0;
    8     struct download *down;
    9     foreach(down, downloads) if (!down->prog) d = 1;
   10     return d;
   11 }
   12 
   13 struct list_head sessions = {&sessions, &sessions};
   14 
   15 int session_id = 1;
   16 
   17 struct strerror_val {
   18     struct strerror_val *next;
   19     struct strerror_val *prev;
   20     unsigned char msg[1];
   21 };
   22 
   23 struct list_head strerror_buf = { &strerror_buf, &strerror_buf };
   24 
   25 void free_strerror_buf()
   26 {
   27     free_list(strerror_buf);
   28 }
   29 
   30 int get_error_from_errno(int errn)
   31 {
   32     if (errn > 0 && (errn < -S_OK || errn > -S_MAX))
   33         return -errn;
   34 #ifdef BEOS
   35     if (-errn > 0 && (-errn < -S_OK || -errn > -S_MAX))
   36         return errn;
   37 #endif
   38     return S_UNKNOWN_ERROR;
   39 }
   40 
   41 unsigned char *get_err_msg(int state)
   42 {
   43     unsigned char *e;
   44     struct strerror_val *s;
   45     if ((state >= S_MAX && state <= S_OK) || state >= S_WAIT) {
   46         int i;
   47         for (i = 0; msg_dsc[i].msg; i++)
   48             if (msg_dsc[i].n == state) return msg_dsc[i].msg;
   49         unk:
   50         return TEXT_(T_UNKNOWN_ERROR);
   51     }
   52 #ifdef BEOS
   53     if ((e = strerror(state)) && *e && !strstr(e, "No Error")) goto have_error;
   54 #endif
   55     if ((e = strerror(-state)) && *e) goto have_error;
   56     goto unk;
   57 have_error:
   58     foreach(s, strerror_buf) if (!strcmp(s->msg, e)) return s->msg;
   59     s = mem_alloc(sizeof(struct strerror_val) + strlen(e));
   60     strcpy(s->msg, e);
   61     add_to_list(strerror_buf, s);
   62     return s->msg;
   63 }
   64 
   65 void add_xnum_to_str(unsigned char **s, int *l, off_t n)
   66 {
   67     unsigned char suff = 0;
   68     int d = -1;
   69     if (n >= 1000000000) suff = 'G', d = (n / 100000000) % 10, n /= 1000000000;
   70     else if (n >= 1000000) suff = 'M', d = (n / 100000) % 10, n /= 1000000;
   71     else if (n >= 1000) suff = 'k', d = (n / 100) % 10, n /= 1000;
   72     add_num_to_str(s, l, n);
   73     if (n < 10 && d != -1) add_chr_to_str(s, l, '.'), add_num_to_str(s, l, d);
   74     add_chr_to_str(s, l, ' ');
   75     if (suff) add_chr_to_str(s, l, suff);
   76     add_chr_to_str(s, l, 'B');
   77 }
   78 
   79 void add_time_to_str(unsigned char **s, int *l, ttime t)
   80 {
   81     unsigned char q[64];
   82     if (t < 0) t = 0;
   83     t &= 0xffffffff;
   84     if (t >= 86400) sprintf(q, "%dd ", (int)(t / 86400)), add_to_str(s, l, q);
   85     if (t >= 3600) t %= 86400, sprintf(q, "%d:%02d", (int)(t / 3600), (int)(t / 60 % 60)), add_to_str(s, l, q);
   86     else sprintf(q, "%d", (int)(t / 60)), add_to_str(s, l, q);
   87     sprintf(q, ":%02d", (int)(t % 60)), add_to_str(s, l, q);
   88 }
   89 
   90 unsigned char *get_stat_msg(struct status *stat, struct terminal *term)
   91 {
   92     if (stat->state == S_TRANS && stat->prg->elapsed / 100) {
   93         unsigned char *m = init_str();
   94         int l = 0;
   95         add_to_str(&m, &l, _(TEXT_(T_RECEIVED), term));
   96         add_to_str(&m, &l, " ");
   97         add_xnum_to_str(&m, &l, stat->prg->pos);
   98         if (stat->prg->size >= 0)
   99             add_to_str(&m, &l, " "), add_to_str(&m, &l, _(TEXT_(T_OF), term)), add_to_str(&m, &l, " "), add_xnum_to_str(&m, &l, stat->prg->size);
  100         add_to_str(&m, &l, ", ");
  101         if (stat->prg->elapsed >= CURRENT_SPD_AFTER * SPD_DISP_TIME)
  102             add_to_str(&m, &l, _(TEXT_(T_AVG), term)), add_to_str(&m, &l, " ");
  103         add_xnum_to_str(&m, &l, (longlong)stat->prg->loaded * 10 / (stat->prg->elapsed / 100));
  104         add_to_str(&m, &l, "/s");
  105         if (stat->prg->elapsed >= CURRENT_SPD_AFTER * SPD_DISP_TIME) 
  106             add_to_str(&m, &l, ", "), add_to_str(&m, &l, _(TEXT_(T_CUR), term)), add_to_str(&m, &l, " "),
  107             add_xnum_to_str(&m, &l, stat->prg->cur_loaded / (CURRENT_SPD_SEC * SPD_DISP_TIME / 1000)),
  108             add_to_str(&m, &l, "/s");
  109         return m;
  110     }
  111     return stracpy(_(get_err_msg(stat->state), term));
  112 }
  113 
  114 void print_screen_status(struct session *ses)
  115 {
  116     struct terminal *term = ses->term;
  117     struct status *stat = NULL;
  118     unsigned char *m;
  119     fill_area(term, 0, 0, term->x, 1, COLOR_TITLE_BG);
  120     fill_area(term, 0, term->y - 1, term->x, 1, COLOR_STATUS_BG);
  121     if (ses->wtd) stat = &ses->loading;
  122     else if (!list_empty(ses->history)) stat = &cur_loc(ses)->stat;
  123     if (stat && stat->state == S_OK) {
  124         struct file_to_load *ftl;
  125         foreach(ftl, ses->more_files) {
  126             if (ftl->req_sent && ftl->stat.state >= 0) {
  127                 stat = &ftl->stat;
  128                 break;
  129             }
  130         }
  131     }
  132     if (stat) {
  133         if (stat->state == S_OK) if ((m = print_current_link(ses))) goto p;
  134         if ((m = get_stat_msg(stat, term))) {
  135             p:
  136             print_text(term, 0, term->y - 1, strlen(m), m, COLOR_STATUS);
  137             mem_free(m);
  138         }
  139         if ((m = print_current_title(ses))) {
  140             int p = term->x - 1 - strlen(m);
  141             if (p < 0) p = 0;
  142             print_text(term, p, 0, strlen(m), m, COLOR_TITLE);
  143             /*set_window_title(0,m);*/
  144             /*set_terminal_title(term, m);*/
  145             mem_free(m);
  146         }
  147         m = stracpy("Links");
  148         if (ses->screen && ses->screen->f_data && ses->screen->f_data->title && ses->screen->f_data->title[0]) add_to_strn(&m, " - "), add_to_strn(&m, ses->screen->f_data->title);
  149         set_terminal_title(term, m);
  150         /* mem_free(m); -- set_terminal_title frees it */
  151     }
  152     redraw_from_window(ses->win);
  153 }
  154 
  155 void print_error_dialog(struct session *ses, struct status *stat, unsigned char *title)
  156 {
  157     unsigned char *t = get_err_msg(stat->state);
  158     if (!t) return;
  159     msg_box(ses->term, NULL, title, AL_CENTER, t, ses, 1, TEXT_(T_CANCEL), NULL, B_ENTER | B_ESC/*, _("Retry"), NULL, 0 !!! FIXME: retry */);
  160 }
  161 
  162 void free_wtd(struct session *ses)
  163 {
  164     if (!ses->wtd) {
  165         internal("no WTD");
  166         return;
  167     }
  168     if (ses->goto_position) mem_free(ses->goto_position), ses->goto_position = NULL;
  169     mem_free(ses->loading_url);
  170     ses->loading_url = NULL;
  171     ses->wtd = WTD_NO;
  172 }
  173 
  174 void abort_files_load(struct session *ses)
  175 {
  176     struct file_to_load *ftl;
  177     int q;
  178     do {
  179         q = 0;
  180         foreach(ftl, ses->more_files) {
  181             if (ftl->stat.state >= 0 && ftl->req_sent) {
  182                 q = 1;
  183                 change_connection(&ftl->stat, NULL, PRI_CANCEL);
  184             }
  185         }
  186     } while (q);
  187 }
  188 
  189 void free_files(struct session *ses)
  190 {
  191     struct file_to_load *ftl;
  192     abort_files_load(ses);
  193     foreach(ftl, ses->more_files) {
  194         if (ftl->ce) ftl->ce->refcount--;
  195         mem_free(ftl->url);
  196     }
  197     free_list(ses->more_files);
  198 }
  199 
  200 void destroy_location(struct location *loc)
  201 {
  202     struct frame *frame;
  203     del_from_list(loc);
  204     foreach(frame, loc->frames) {
  205         destroy_vs(&frame->vs);
  206         mem_free(frame->name);
  207     }
  208     free_list(loc->frames);
  209     destroy_vs(&loc->vs);
  210     mem_free(loc);
  211 }
  212 
  213 void ses_forward(struct session *ses)
  214 {
  215     struct location *l;
  216     size_t len;
  217     free_files(ses);
  218     if (!list_empty(ses->history)) {
  219         l = cur_loc(ses);
  220     }
  221     if (ses->search_word) mem_free(ses->search_word), ses->search_word = NULL;
  222     x:
  223     len = strlen(ses->loading_url);
  224     if (!list_empty(ses->history) && len < strlen(cur_loc(ses)->vs.url))
  225         len = strlen(cur_loc(ses)->vs.url);
  226     l = mem_alloc(sizeof(struct location) + len + 1);
  227     memset(l, 0, sizeof(struct location));
  228     memcpy(&l->stat, &ses->loading, sizeof(struct status));
  229     if (ses->wtd_target && *ses->wtd_target) {
  230         struct frame *frm;
  231         if (list_empty(ses->history)) {
  232             internal("no history");
  233             return;
  234         }
  235         copy_location(l, cur_loc(ses));
  236         add_to_list(ses->history, l);
  237         frm = ses_change_frame_url(ses, ses->wtd_target, ses->loading_url);
  238         if (!frm) {
  239             destroy_location(l);
  240             ses->wtd_target = NULL;
  241             goto x;
  242         }
  243         destroy_vs(&frm->vs);
  244         init_vs(&frm->vs, ses->loading_url);
  245         if (ses->goto_position) {
  246             if (frm->vs.goto_position) mem_free(frm->vs.goto_position);
  247             frm->vs.goto_position = ses->goto_position;
  248             ses->goto_position = NULL;
  249         }
  250         /*request_additional_loading_file(ses, ses->loading_url, &ses->loading, PRI_FRAME);*/
  251     } else {
  252         init_list(l->frames);
  253         init_vs(&l->vs, ses->loading_url);
  254         add_to_list(ses->history, l);
  255         if (ses->goto_position) {
  256             l->vs.goto_position = ses->goto_position;
  257             ses->goto_position = NULL;
  258         }
  259     }
  260 }
  261 
  262 void ses_imgmap(struct session *ses)
  263 {
  264     struct cache_entry *ce;
  265     struct fragment *fr;
  266     struct memory_list *ml;
  267     struct menu_item *menu;
  268     struct f_data_c *fd;
  269     if (find_in_cache(ses->loading_url, &ce)) {
  270         internal("can't find cache entry");
  271         return;
  272     }
  273     ce->refcount--;
  274     defrag_entry(ce);
  275     fr = ce->frag.next;
  276     if ((void *)fr == &ce->frag) return;
  277     if (!(fd = current_frame(ses)) || !fd->f_data) return;
  278     d_opt = &fd->f_data->opt;
  279     if (get_image_map(ce->head, fr->data, fr->data + fr->length, ses->goto_position, &menu, &ml, ses->imgmap_href_base, ses->imgmap_target_base, ses->term->spec->charset, ses->ds.assume_cp, ses->ds.hard_assume))
  280         return;
  281     add_empty_window(ses->term, (void (*)(void *))freeml, ml);
  282     do_menu(ses->term, menu, ses);
  283 }
  284 
  285 void map_selected(struct terminal *term, struct link_def *ld, struct session *ses)
  286 {
  287     goto_url_f(ses, ld->link, ld->target);
  288 }
  289 
  290 void ses_back(struct session *ses)
  291 {
  292     struct location *loc;
  293     free_files(ses);
  294     loc = ses->history.next;
  295     if (ses->search_word) mem_free(ses->search_word), ses->search_word = NULL;
  296     if ((void *)loc == &ses->history) return;
  297     destroy_location(loc);
  298     loc = ses->history.next;
  299     if ((void *)loc == &ses->history) return;
  300     if (!strcmp(loc->vs.url, ses->loading_url)) return;
  301     destroy_location(loc);
  302     ses_forward(ses);
  303 }
  304 
  305 void end_load(struct status *, struct session *);
  306 void doc_end_load(struct status *, struct session *);
  307 void file_end_load(struct status *, struct file_to_load *);
  308 void abort_loading(struct session *);
  309 void abort_preloading(struct session *);
  310 
  311 struct session *get_download_ses(struct download *down)
  312 {
  313     struct session *ses;
  314     foreach(ses, sessions) if (ses == down->ses) return ses;
  315     if (!list_empty(sessions)) return sessions.next;
  316     return NULL;
  317 }
  318 
  319 void abort_download(struct download *down)
  320 {
  321     if (down->win) delete_window(down->win);
  322     if (down->ask) delete_window(down->ask);
  323     if (down->stat.state >= 0) change_connection(&down->stat, NULL, PRI_CANCEL);
  324     mem_free(down->url);
  325     if (down->handle != -1) prealloc_truncate(down->handle, down->last_pos), close(down->handle);
  326     if (down->prog) {
  327         unlink(down->file);
  328         mem_free(down->prog);
  329     }
  330     mem_free(down->file);
  331     del_from_list(down);
  332     mem_free(down);
  333 }
  334 
  335 void kill_downloads_to_file(unsigned char *file)
  336 {
  337     struct download *down;
  338     foreach(down, downloads) if (!strcmp(down->file, file)) down = down->prev, abort_download(down->next);
  339 }
  340 
  341 void undisplay_download(struct download *down)
  342 {
  343     if (down->win) delete_window(down->win);
  344 }
  345 
  346 int dlg_abort_download(struct dialog_data *dlg, struct dialog_item_data *di)
  347 {
  348     register_bottom_half((void (*)(void *))abort_download, dlg->dlg->udata);
  349     return 0;
  350 }
  351 
  352 int dlg_undisplay_download(struct dialog_data *dlg, struct dialog_item_data *di)
  353 {
  354     register_bottom_half((void (*)(void *))undisplay_download, dlg->dlg->udata);
  355     return 0;
  356 }
  357 
  358 void download_abort_function(struct dialog_data *dlg)
  359 {
  360     struct download *down = dlg->dlg->udata;
  361     down->win = NULL;
  362 }
  363 
  364 void download_window_function(struct dialog_data *dlg)
  365 {
  366     struct download *down = dlg->dlg->udata;
  367     struct terminal *term = dlg->win->term;
  368     int max = 0, min = 0;
  369     int w, x, y;
  370     int t = 0;
  371     unsigned char *m, *u;
  372     struct status *stat = &down->stat;
  373     redraw_below_window(dlg->win);
  374     down->win = dlg->win;
  375     if (stat->state == S_TRANS && stat->prg->elapsed / 100) {
  376         int l = 0;
  377         m = init_str();
  378         t = 1;
  379         add_to_str(&m, &l, _(TEXT_(T_RECEIVED), term));
  380         add_to_str(&m, &l, " ");
  381         add_xnum_to_str(&m, &l, stat->prg->pos);
  382         if (stat->prg->size >= 0)
  383             add_to_str(&m, &l, " "), add_to_str(&m, &l, _(TEXT_(T_OF),term)), add_to_str(&m, &l, " "), add_xnum_to_str(&m, &l, stat->prg->size), add_to_str(&m, &l, " ");
  384         add_to_str(&m, &l, "\n");
  385         if (stat->prg->elapsed >= CURRENT_SPD_AFTER * SPD_DISP_TIME)
  386             add_to_str(&m, &l, _(TEXT_(T_AVERAGE_SPEED), term));
  387         else add_to_str(&m, &l, _(TEXT_(T_SPEED), term));
  388         add_to_str(&m, &l, " ");
  389         add_xnum_to_str(&m, &l, (longlong)stat->prg->loaded * 10 / (stat->prg->elapsed / 100));
  390         add_to_str(&m, &l, "/s");
  391         if (stat->prg->elapsed >= CURRENT_SPD_AFTER * SPD_DISP_TIME) 
  392             add_to_str(&m, &l, ", "), add_to_str(&m, &l, _(TEXT_(T_CURRENT_SPEED), term)), add_to_str(&m, &l, " "),
  393             add_xnum_to_str(&m, &l, stat->prg->cur_loaded / (CURRENT_SPD_SEC * SPD_DISP_TIME / 1000)),
  394             add_to_str(&m, &l, "/s");
  395         add_to_str(&m, &l, "\n");
  396         add_to_str(&m, &l, _(TEXT_(T_ELAPSED_TIME), term));
  397         add_to_str(&m, &l, " ");
  398         add_time_to_str(&m, &l, stat->prg->elapsed / 1000);
  399         if (stat->prg->size >= 0 && stat->prg->loaded > 0) {
  400             add_to_str(&m, &l, ", ");
  401             add_to_str(&m, &l, _(TEXT_(T_ESTIMATED_TIME), term));
  402             add_to_str(&m, &l, " ");
  403             /*add_time_to_str(&m, &l, stat->prg->elapsed / 1000 * stat->prg->size / stat->prg->loaded * 1000 - stat->prg->elapsed);*/
  404             add_time_to_str(&m, &l, (stat->prg->size - stat->prg->pos) / ((longlong)stat->prg->loaded * 10 / (stat->prg->elapsed / 100)));
  405         }
  406     } else m = stracpy(_(get_err_msg(stat->state), term));
  407     u = stracpy(down->url);
  408     if (strchr(u, POST_CHAR)) *strchr(u, POST_CHAR) = 0;
  409     max_text_width(term, u, &max);
  410     min_text_width(term, u, &min);
  411     max_text_width(term, m, &max);
  412     min_text_width(term, m, &min);
  413     max_buttons_width(term, dlg->items, dlg->n, &max);
  414     min_buttons_width(term, dlg->items, dlg->n, &min);
  415     w = dlg->win->term->x * 9 / 10 - 2 * DIALOG_LB;
  416     if (w < min) w = min;
  417     if (w > dlg->win->term->x - 2 * DIALOG_LB) w = dlg->win->term->x - 2 * DIALOG_LB;
  418     if (t && stat->prg->size >= 0) {
  419         if (w < DOWN_DLG_MIN) w = DOWN_DLG_MIN;
  420     } else {
  421         if (w > max) w = max;
  422     }
  423     if (w < 1) w = 1;
  424     y = 0;
  425     dlg_format_text(NULL, term, u, 0, &y, w, NULL, COLOR_DIALOG_TEXT, AL_LEFT);
  426     y++;
  427     if (t && stat->prg->size >= 0) y += 2;
  428     dlg_format_text(NULL, term, m, 0, &y, w, NULL, COLOR_DIALOG_TEXT, AL_LEFT);
  429     y++;
  430     dlg_format_buttons(NULL, term, dlg->items, dlg->n, 0, &y, w, NULL, AL_CENTER);
  431     dlg->xw = w + 2 * DIALOG_LB;
  432     dlg->yw = y + 2 * DIALOG_TB;
  433     center_dlg(dlg);
  434     draw_dlg(dlg);
  435     y = dlg->y + DIALOG_TB + 1;
  436     x = dlg->x + DIALOG_LB;
  437     dlg_format_text(term, term, u, x, &y, w, NULL, COLOR_DIALOG_TEXT, AL_LEFT);
  438     if (t && stat->prg->size >= 0) {
  439         unsigned char q[64];
  440         int p = w - 6;
  441         y++;
  442         set_only_char(term, x, y, '[');
  443         set_only_char(term, x + w - 5, y, ']');
  444         fill_area(term, x + 1, y, (int)((longlong)p * (longlong)stat->prg->pos / (longlong)stat->prg->size), 1, COLOR_DIALOG_METER);
  445         sprintf(q, "%3d%%", (int)((longlong)100 * (longlong)stat->prg->pos / (longlong)stat->prg->size));
  446         print_text(term, x + w - 4, y, strlen(q), q, COLOR_DIALOG_TEXT);
  447         y++;
  448     }
  449     y++;
  450     dlg_format_text(term, term, m, x, &y, w, NULL, COLOR_DIALOG_TEXT, AL_LEFT);
  451     y++;
  452     dlg_format_buttons(term, term, dlg->items, dlg->n, x, &y, w, NULL, AL_CENTER);
  453     mem_free(u);
  454     mem_free(m);
  455 }
  456 
  457 void display_download(struct terminal *term, struct download *down, struct session *ses)
  458 {
  459     struct dialog *dlg;
  460     struct download *dd;
  461     foreach(dd, downloads) if (dd == down) goto found;
  462     return;
  463     found:
  464     dlg = mem_alloc(sizeof(struct dialog) + 3 * sizeof(struct dialog_item));
  465     memset(dlg, 0, sizeof(struct dialog) + 3 * sizeof(struct dialog_item));
  466     undisplay_download(down);
  467     down->ses = ses;
  468     dlg->title = TEXT_(T_DOWNLOAD);
  469     dlg->fn = download_window_function;
  470     dlg->abort = download_abort_function;
  471     dlg->udata = down;
  472     dlg->align = AL_CENTER;
  473     dlg->items[0].type = D_BUTTON;
  474     dlg->items[0].gid = B_ENTER | B_ESC;
  475     dlg->items[0].fn = dlg_undisplay_download;
  476     dlg->items[0].text = TEXT_(T_BACKGROUND);
  477     dlg->items[1].type = D_BUTTON;
  478     dlg->items[1].gid = 0;
  479     dlg->items[1].fn = dlg_abort_download;
  480     dlg->items[1].text = TEXT_(T_ABORT);
  481     dlg->items[2].type = D_END;
  482     do_dialog(term, dlg, getml(dlg, NULL));
  483 }
  484 
  485 time_t parse_http_date(const char *date)    /* this functions is bad !!! */
  486 {
  487     const char *months[12] =
  488         {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
  489          "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
  490 
  491     time_t t = 0;
  492     /* Mon, 03 Jan 2000 21:29:33 GMT */
  493     struct tm tm;
  494     if (!date || strlen(date) < 28) return 0;
  495     date += 5;
  496     tm.tm_mday = (date[0] - '0') * 10 + date[1] - '0';
  497     date += 3;
  498     for (tm.tm_mon = 0; tm.tm_mon < 12; tm.tm_mon++)
  499         if (!strncmp(date, months[tm.tm_mon], 3)) break;
  500     date += 4;
  501     tm.tm_year = (date[0] - '0') * 1000 + (date[1] - '0') * 100 + (date[2] - '0') * 10 + date[3] - '0' - 1900;
  502     date += 5;
  503     tm.tm_hour = (date[0] - '0') * 10 + date[1] - '0';
  504     date += 3;
  505     tm.tm_min = (date[0] - '0') * 10 + date[1] - '0';
  506     date += 3;
  507     tm.tm_sec = (date[0] - '0') * 10 + date[1] - '0';
  508     t = mktime(&tm);
  509     if (t == (time_t) - 1) return 0;
  510     else return t;
  511 }
  512 
  513 void download_data(struct status *stat, struct download *down)
  514 {
  515     struct cache_entry *ce;
  516     struct fragment *frag;
  517     if (stat->state >= S_WAIT && stat->state < S_TRANS) goto end_store;
  518     if (!(ce = stat->ce)) goto end_store;
  519     if (ce->last_modified)
  520     down->remotetime = parse_http_date(ce->last_modified);
  521 /*    fprintf(stderr,"\nFEFE date %s\n",ce->last_modified); */
  522     if (ce->redirect && down->redirect_cnt++ < MAX_REDIRECTS) {
  523         unsigned char *u, *p, *pos;
  524         if (stat->state >= 0) change_connection(&down->stat, NULL, PRI_CANCEL);
  525         u = join_urls(down->url, ce->redirect);
  526         if (!u) goto x;
  527         if ((pos = extract_position(u))) mem_free(pos);
  528         if (!http_bugs.bug_302_redirect) if (!ce->redirect_get && (p = strchr(down->url, POST_CHAR))) add_to_strn(&u, p);
  529         mem_free(down->url);
  530         down->url = u;
  531         down->stat.state = S_WAIT_REDIR;
  532         if (down->win) {
  533             struct event ev = { EV_REDRAW, 0, 0, 0 };
  534             ev.x = down->win->term->x;
  535             ev.y = down->win->term->y;
  536             down->win->handler(down->win, &ev, 0);
  537         }
  538         /*if (!strchr(down->url, POST_CHAR)) {*/
  539             load_url(down->url, &down->stat, PRI_DOWNLOAD, down->redirect_cnt < MAX_CACHED_REDIRECTS ? NC_CACHE : NC_RELOAD);
  540             return;
  541         /*} else {
  542             unsigned char *msg = init_str();
  543             int l = 0;
  544             add_bytes_to_str(&msg, &l, down->url, (unsigned char *)strchr(down->url, POST_CHAR) - down->url);
  545             msg_box(get_download_ses(down)->term, getml(msg, NULL), TEXT_(T_WARNING), AL_CENTER | AL_EXTD_TEXT, TEXT_(T_DO_YOU_WANT_TO_FOLLOW_REDIRECT_AND_POST_FORM_DATA_TO_URL), "", msg, "?", NULL, down, 3, TEXT_(T_YES), down_post_yes, B_ENTER, TEXT_(T_NO), down_post_no, 0, TEXT_(T_CANCEL), down_post_cancel, B_ESC);
  546         }*/
  547     }
  548     x:
  549     foreach(frag, ce->frag) while (frag->offset <= down->last_pos && frag->offset + frag->length > down->last_pos) {
  550         int w;
  551 #ifdef HAVE_OPEN_PREALLOC
  552         if (!down->last_pos && (!down->stat.prg || down->stat.prg->size > 0) && can_prealloc(down->file)) {
  553             struct stat st;
  554             if (fstat(down->handle, &st) || !S_ISREG(st.st_mode)) goto skip_prealloc;
  555             close(down->handle);
  556             down->handle = open_prealloc(down->file, O_CREAT|O_WRONLY|O_TRUNC, 0666, down->stat.prg ? down->stat.prg->size : ce->length);
  557             if (down->handle == -1) goto error;
  558             set_bin(down->handle);
  559             skip_prealloc:;
  560         }
  561 #endif
  562         w = write(down->handle, frag->data + (down->last_pos - frag->offset), frag->length - (down->last_pos - frag->offset));
  563         if (w == -1) {
  564 #ifdef HAVE_OPEN_PREALLOC
  565             error:
  566 #endif
  567             if (!list_empty(sessions)) {
  568                 unsigned char *emsg = stracpy(strerror(errno));
  569                 unsigned char *msg = stracpy(down->file);
  570                 msg_box(get_download_ses(down)->term, getml(msg, emsg, NULL), TEXT_(T_DOWNLOAD_ERROR), AL_CENTER | AL_EXTD_TEXT, TEXT_(T_COULD_NOT_WRITE_TO_FILE), " ", msg, ": ", emsg, NULL, NULL, 1, TEXT_(T_CANCEL), NULL, B_ENTER | B_ESC);
  571             }
  572             detach_connection(stat, down->last_pos);
  573             abort_download(down);
  574             return;
  575         }
  576         down->last_pos += w;
  577     }
  578     detach_connection(stat, down->last_pos);
  579     end_store:;
  580     if (stat->state < 0) {
  581         if (stat->state != S_OK) {
  582             unsigned char *t = get_err_msg(stat->state);
  583             if (t) {
  584                 unsigned char *tt = stracpy(down->url);
  585                 if (strchr(tt, POST_CHAR)) *strchr(tt, POST_CHAR) = 0;
  586                 msg_box(get_download_ses(down)->term, getml(tt, NULL), TEXT_(T_DOWNLOAD_ERROR), AL_CENTER | AL_EXTD_TEXT, TEXT_(T_ERROR_DOWNLOADING), " ", tt, ":\n\n", t, NULL, get_download_ses(down), 1, TEXT_(T_CANCEL), NULL, B_ENTER | B_ESC/*, TEXT_(T_RETRY), NULL, 0 !!! FIXME: retry */);
  587             }
  588         } else {
  589             if (down->prog) {
  590                 prealloc_truncate(down->handle, down->last_pos);
  591                 close(down->handle), down->handle = -1;
  592                 exec_on_terminal(get_download_ses(down)->term, down->prog, down->file, !!down->prog_flags);
  593                 mem_free(down->prog), down->prog = NULL;
  594             } else if (down->remotetime && download_utime) {
  595                 struct utimbuf foo;
  596                 foo.actime = foo.modtime = down->remotetime;
  597                 utime(down->file, &foo);
  598             }
  599         }
  600         abort_download(down);
  601         return;
  602     }
  603     if (down->win) {
  604         struct event ev = { EV_REDRAW, 0, 0, 0 };
  605         ev.x = down->win->term->x;
  606         ev.y = down->win->term->y;
  607         down->win->handler(down->win, &ev, 0);
  608     }
  609 }
  610 
  611 int create_download_file(struct terminal *term, unsigned char *fi, int safe)
  612 {
  613     unsigned char *file = fi;
  614     unsigned char *wd = NULL;
  615     int h;
  616     int i;
  617 #ifdef NO_FILE_SECURITY
  618     int sf = 0;
  619 #else
  620     int sf = safe;
  621 #endif
  622     if (!safe) {
  623         wd = get_cwd();
  624         set_cwd(term->cwd);
  625         if (file[0] == '~' && dir_sep(file[1])) {
  626             unsigned char *h = getenv("HOME");
  627             if (h) {
  628                 int fl = 0;
  629                 file = init_str();
  630                 add_to_str(&file, &fl, h);
  631                 add_to_str(&file, &fl, fi + 1);
  632             }
  633         }
  634     }
  635     h = open(file, O_CREAT|O_WRONLY|O_TRUNC|(safe?O_EXCL:0), sf ? 0600 : 0666);
  636     if (h == -1) {
  637         unsigned char *msg, *msge;
  638         int errn = errno;
  639         if (errn == EEXIST && safe) {
  640             h = -2;
  641             goto x;
  642         }
  643         msg = stracpy(file);
  644         msge = stracpy(strerror(errn));
  645         msg_box(term, getml(msg, msge, NULL), TEXT_(T_DOWNLOAD_ERROR), AL_CENTER | AL_EXTD_TEXT, TEXT_(T_COULD_NOT_CREATE_FILE), " ", msg, ": ", msge, NULL, NULL, 1, TEXT_(T_CANCEL), NULL, B_ENTER | B_ESC);
  646         goto x;
  647     }
  648     set_bin(h);
  649     if (safe) goto x;
  650     if (strlen(file) >= MAX_STR_LEN) memcpy(download_dir, file, MAX_STR_LEN - 1), download_dir[MAX_STR_LEN - 1] = 0;
  651     else strcpy(download_dir, file);
  652     for (i = strlen(download_dir) - 1; i >= 0; i--) if (dir_sep(download_dir[i])) {
  653         download_dir[i + 1] = 0;
  654         goto x;
  655     }
  656     download_dir[0] = 0;
  657     x:
  658     if (file != fi) mem_free(file);
  659     if (wd) set_cwd(wd), mem_free(wd);
  660     return h;
  661 }
  662 
  663 unsigned char *get_temp_name(unsigned char *url)
  664 {
  665     int l, nl;
  666     unsigned char *name, *fn, *fnn, *fnnn, *s;
  667     unsigned char *nm;
  668     unsigned char *directory = NULL;
  669 #ifdef WIN32
  670     directory = getenv("TMP");
  671     if (!directory) directory = getenv("TEMP");
  672 #endif
  673     nm = tempnam(directory, "links");
  674     if (!nm) return NULL;
  675 #ifdef OS2
  676     if (strlen(nm) > 4 && !strcasecmp(nm + strlen(nm) - 4, ".tmp")) nm[strlen(nm) - 4] = 0;
  677 #endif
  678     name = init_str();
  679     nl = 0;
  680     add_to_str(&name, &nl, nm);
  681     free(nm);
  682     get_filename_from_url(url, &fn, &l);
  683     fnnn = NULL;
  684     for (fnn = fn; fnn < fn + l; fnn++) if (*fnn == '.') fnnn = fnn;
  685     if (fnnn) {
  686         s = memacpy(fnnn, l - (fnnn - fn));
  687         check_shell_security(&s);
  688         add_to_str(&name, &nl, s);
  689         mem_free(s);
  690     }
  691     return name;
  692 }
  693 
  694 unsigned char *subst_file(unsigned char *prog, unsigned char *file, int cyg_subst)
  695 {
  696     unsigned char *orig_prog = prog;
  697     unsigned char *nn;
  698     unsigned char *n = init_str();
  699     int l = 0;
  700     while (*prog) {
  701         int p;
  702         for (p = 0; prog[p] && prog[p] != '%'; p++)
  703             ;
  704         add_bytes_to_str(&n, &l, prog, p);
  705         prog += p;
  706         if (*prog == '%') {
  707             if (cyg_subst) {
  708                 unsigned char *conv = os_conv_to_external_path(file, orig_prog);
  709                 add_to_str(&n, &l, conv);
  710                 mem_free(conv);
  711             } else {
  712                 add_to_str(&n, &l, file);
  713             }
  714             prog++;
  715         }
  716     }
  717     nn = os_fixup_external_program(n);
  718     mem_free(n);
  719     return nn;
  720 }
  721 
  722 void start_download(struct session *ses, unsigned char *file)
  723 {
  724     struct download *down;
  725     int h;
  726     unsigned char *url = ses->dn_url;
  727     unsigned char *pos;
  728     if (!url) return;
  729     if ((pos = extract_position(url))) mem_free(pos);
  730     kill_downloads_to_file(file);
  731     if ((h = create_download_file(ses->term, file, 0)) < 0) return;
  732     down = mem_alloc(sizeof(struct download));
  733     memset(down, 0, sizeof(struct download));
  734     down->url = stracpy(url);
  735     down->stat.end = (void (*)(struct status *, void *))download_data;
  736     down->stat.data = down;
  737     down->last_pos = 0;
  738     down->file = stracpy(file);
  739     down->handle = h;
  740     down->ses = ses;
  741     down->remotetime = 0;
  742     add_to_list(downloads, down);
  743     load_url(url, &down->stat, PRI_DOWNLOAD, NC_CACHE);
  744     display_download(ses->term, down, ses);
  745 }
  746 
  747 void tp_cancel(struct session *);
  748 
  749 void tp_free(struct session *);
  750 
  751 void continue_download(struct session *ses, unsigned char *file)
  752 {
  753     struct download *down;
  754     int h;
  755     int namecount = 0;
  756     unsigned char *url = ses->tq_url;
  757     if (!url) return;
  758     if (ses->tq_prog) {
  759         new_name:
  760         if (!(file = get_temp_name(url))) {
  761             tp_cancel(ses);
  762             return;
  763         }
  764     }
  765     kill_downloads_to_file(file);
  766     if (!ses->tq_prog) kill_downloads_to_file(file);
  767     if ((h = create_download_file(ses->term, file, !!ses->tq_prog)) < 0) {
  768         if (h == -2 && ses->tq_prog) {
  769             if (++namecount < DOWNLOAD_NAME_TRIES) {
  770                 mem_free(file);
  771                 goto new_name;
  772             }
  773             msg_box(ses->term, NULL, TEXT_(T_DOWNLOAD_ERROR), AL_CENTER | AL_EXTD_TEXT, TEXT_(T_COULD_NOT_CREATE_TEMPORARY_FILE), NULL, NULL, 1, TEXT_(T_CANCEL), NULL, B_ENTER | B_ESC);
  774         }
  775         if (ses->tq_prog) mem_free(file);
  776         tp_cancel(ses);
  777         return;
  778     }
  779     down = mem_alloc(sizeof(struct download));
  780     memset(down, 0, sizeof(struct download));
  781     down->url = stracpy(url);
  782     down->stat.end = (void (*)(struct status *, void *))download_data;
  783     down->stat.data = down;
  784     down->last_pos = 0;
  785     down->file = stracpy(file);
  786     down->handle = h;
  787     down->ses = ses;
  788     if (ses->tq_prog) {
  789         down->prog = subst_file(ses->tq_prog, file, 1);
  790         mem_free(file);
  791         mem_free(ses->tq_prog);
  792         ses->tq_prog = NULL;
  793     }
  794     down->prog_flags = ses->tq_prog_flags;
  795     add_to_list(downloads, down);
  796     change_connection(&ses->tq, &down->stat, PRI_DOWNLOAD);
  797     tp_free(ses);
  798     display_download(ses->term, down, ses);
  799 }
  800 
  801 void tp_free(struct session *ses)
  802 {
  803     ses->tq_ce->refcount--;
  804     mem_free(ses->tq_url);
  805     ses->tq_url = NULL;
  806     if (ses->tq_goto_position) mem_free(ses->tq_goto_position), ses->tq_goto_position = NULL;
  807     ses->tq_ce = NULL;
  808 }
  809 
  810 void tp_cancel(struct session *ses)
  811 {
  812     change_connection(&ses->tq, NULL, PRI_CANCEL);
  813     tp_free(ses);
  814 }
  815 
  816 void tp_save(struct session *ses)
  817 {
  818     if (ses->tq_prog) mem_free(ses->tq_prog), ses->tq_prog = NULL;
  819     query_file(ses, ses->tq_url, continue_download, tp_cancel);
  820 }
  821 
  822 void tp_open(struct session *ses)
  823 {
  824     continue_download(ses, "");
  825 }
  826 
  827 void display_timer(struct session *);
  828 
  829 void tp_display(struct session *ses)    /* !!! FIXME: frames */
  830 {
  831     struct location *l;
  832     l = mem_alloc(sizeof(struct location) + strlen(ses->tq_url));
  833     memset(l, 0, sizeof(struct location));
  834     init_list(l->frames);
  835     memcpy(&l->stat, &ses->tq, sizeof(struct status));
  836     init_vs(&l->vs, ses->tq_url);
  837     if (ses->tq_goto_position) {
  838         l->vs.goto_position = ses->tq_goto_position;
  839         ses->tq_goto_position = NULL;
  840     }
  841     add_to_list(ses->history, l);
  842     cur_loc(ses)->stat.end = (void (*)(struct status *, void *))doc_end_load;
  843     cur_loc(ses)->stat.data = ses;
  844     if (ses->tq.state >= 0) change_connection(&ses->tq, &cur_loc(ses)->stat, PRI_MAIN);
  845     else cur_loc(ses)->stat.state = ses->tq.state;
  846     cur_loc(ses)->vs.plain = 1;
  847     display_timer(ses);
  848     tp_free(ses);
  849 }
  850 
  851 void type_query(struct session *ses, struct cache_entry *ce, unsigned char *ct, struct assoc *a)
  852 {
  853     unsigned char *m1;
  854     unsigned char *m2;
  855     if (ses->tq_prog) mem_free(ses->tq_prog), ses->tq_prog = NULL;
  856     if (a) ses->tq_prog = stracpy(a->prog), ses->tq_prog_flags = a->block;
  857     if (a && !a->ask) {
  858         tp_open(ses);
  859         return;
  860     }
  861     m1 = stracpy(ct);
  862     if (!a) {
  863         if (!anonymous) msg_box(ses->term, getml(m1, NULL), TEXT_(T_UNKNOWN_TYPE), AL_CENTER | AL_EXTD_TEXT, TEXT_(T_CONTEN_TYPE_IS), " ", m1, ".\n", TEXT_(T_DO_YOU_WANT_TO_SAVE_OR_DISLPAY_THIS_FILE), NULL, ses, 3, TEXT_(T_SAVE), tp_save, B_ENTER, TEXT_(T_DISPLAY), tp_display, 0, TEXT_(T_CANCEL), tp_cancel, B_ESC);
  864         else msg_box(ses->term, getml(m1, NULL), TEXT_(T_UNKNOWN_TYPE), AL_CENTER | AL_EXTD_TEXT, TEXT_(T_CONTEN_TYPE_IS), " ", m1, ".\n", TEXT_(T_DO_YOU_WANT_TO_SAVE_OR_DISLPAY_THIS_FILE), NULL, ses, 2, TEXT_(T_DISPLAY), tp_display, B_ENTER, TEXT_(T_CANCEL), tp_cancel, B_ESC);
  865     } else {
  866         m2 = stracpy(a->label ? a->label : (unsigned char *)"");
  867         if (!anonymous) msg_box(ses->term, getml(m1, m2, NULL), TEXT_(T_WHAT_TO_DO), AL_CENTER | AL_EXTD_TEXT, TEXT_(T_CONTEN_TYPE_IS), " ", m1, ".\n", TEXT_(T_DO_YOU_WANT_TO_OPEN_FILE_WITH), " ", m2, ", ", TEXT_(T_SAVE_IT_OR_DISPLAY_IT), NULL, ses, 4, TEXT_(T_OPEN), tp_open, B_ENTER, TEXT_(T_SAVE), tp_save, 0, TEXT_(T_DISPLAY), tp_display, 0, TEXT_(T_CANCEL), tp_cancel, B_ESC);
  868         else msg_box(ses->term, getml(m1, m2, NULL), TEXT_(T_WHAT_TO_DO), AL_CENTER | AL_EXTD_TEXT, TEXT_(T_CONTEN_TYPE_IS), " ", m1, ".\n", TEXT_(T_DO_YOU_WANT_TO_OPEN_FILE_WITH), " ", m2, ", ", TEXT_(T_SAVE_IT_OR_DISPLAY_IT), NULL, ses, 3, TEXT_(T_OPEN), tp_open, B_ENTER, TEXT_(T_DISPLAY), tp_display, 0, TEXT_(T_CANCEL), tp_cancel, B_ESC);
  869     }
  870 }
  871 
  872 int ses_chktype(struct session *ses, struct status **stat, struct cache_entry *ce)
  873 {
  874     struct assoc *a;
  875     unsigned char *ct;
  876     int r = 0;
  877     if (!(ct = get_content_type(ce->head, ce->url))) goto f;
  878     if (is_html_type(ct)) goto ff;
  879     r = 1;
  880     if (!strcasecmp(ct, "text/plain") || !strcasecmp(ct, "file/txt")) goto ff;
  881     if (!(a = get_type_assoc(ses->term, ct)) && strlen(ct) >= 4 && !casecmp(ct, "text", 4)) goto ff;
  882     if (ses->tq_url) internal("type query to %s already in prgress", ses->tq_url);
  883     ses->tq_url = stracpy(ses->loading_url);
  884     change_connection(&ses->loading, *stat = &ses->tq, PRI_MAIN);
  885     (ses->tq_ce = ce)->refcount++;
  886     if (ses->tq_goto_position) mem_free(ses->tq_goto_position);
  887     ses->tq_goto_position = stracpy(ses->goto_position);
  888     type_query(ses, ce, ct, a);
  889     mem_free(ct);
  890     return 1;
  891 
  892     ff:
  893     mem_free(ct);
  894     f:
  895     if (ses->wtd_target && r) *ses->wtd_target = 0;
  896     ses_forward(ses);
  897     cur_loc(ses)->vs.plain = r;
  898     return 0;
  899 }
  900 
  901 struct wtd_data {
  902     struct session *ses;
  903     unsigned char *url;
  904     int pri;
  905     int cache;
  906     int wtd;
  907     unsigned char *target;
  908     unsigned char *pos;
  909     void (*fn)(struct status *, struct session *);
  910 };
  911 
  912 void post_yes(struct wtd_data *w)
  913 {
  914     abort_preloading(w->ses);
  915     if (w->ses->goto_position) mem_free(w->ses->goto_position);
  916     w->ses->goto_position = stracpy(w->pos);
  917     w->ses->loading.end = (void (*)(struct status *, void *))w->fn;
  918     w->ses->loading.data = w->ses;
  919     w->ses->loading_url = stracpy(w->url);
  920     w->ses->wtd = w->wtd;
  921     w->ses->wtd_target = w->target;
  922     load_url(w->ses->loading_url, &w->ses->loading, w->pri, w->cache);
  923 }
  924 
  925 void post_no(struct wtd_data *w)
  926 {
  927     *strchr(w->url, POST_CHAR) = 0;
  928     post_yes(w);
  929 }
  930 
  931 void post_cancel(struct wtd_data *w)
  932 {
  933     reload(w->ses, NC_CACHE);
  934 }
  935 
  936 void ses_goto(struct session *ses, unsigned char *url, unsigned char *target, int pri, int cache, int wtd, unsigned char *pos, void (*fn)(struct status *, struct session *), int redir)
  937 {
  938     struct wtd_data *w;
  939     unsigned char *m1, *m2;
  940     struct cache_entry *e;
  941     e = NULL;
  942     if (!find_in_cache(url, &e)) e->refcount--;
  943     if (!strchr(url, POST_CHAR) || (cache == NC_ALWAYS_CACHE && e && !e->incomplete)) {
  944         if (ses->goto_position) mem_free(ses->goto_position);
  945         ses->goto_position = pos;
  946         ses->loading.end = (void (*)(struct status *, void *))fn;
  947         ses->loading.data = ses;
  948         ses->loading_url = url;
  949         ses->wtd = wtd;
  950         ses->wtd_target = target;
  951         load_url(url, &ses->loading, pri, cache);
  952         return;
  953     }
  954     w = mem_alloc(sizeof(struct wtd_data));
  955     w->ses = ses;
  956     w->url = url;
  957     w->pri = pri;
  958     w->cache = cache;
  959     w->wtd = wtd;
  960     w->target = target;
  961     w->pos = pos;
  962     w->fn = fn;
  963     if (redir) m1 = TEXT_(T_DO_YOU_WANT_TO_FOLLOW_REDIRECT_AND_POST_FORM_DATA_TO_URL);
  964     else if (wtd == WTD_FORWARD) m1 = TEXT_(T_DO_YOU_WANT_TO_POST_FORM_DATA_TO_URL);
  965     else m1 = TEXT_(T_DO_YOU_WANT_TO_REPOST_FORM_DATA_TO_URL);
  966     m2 = memacpy(url, (unsigned char *)strchr(url, POST_CHAR) - url);
  967     msg_box(ses->term, getml(m2, w, w->url, w->pos, NULL), TEXT_(T_WARNING), AL_CENTER | AL_EXTD_TEXT, m1, " ", m2, "?", NULL, w, 3, TEXT_(T_YES), post_yes, B_ENTER, TEXT_(T_NO), post_no, 0, TEXT_(T_CANCEL), post_cancel, B_ESC);
  968 }
  969 
  970 int do_move(struct session *ses, struct status **stat)
  971 {
  972     struct cache_entry *ce = NULL;
  973     if (!ses->loading_url) {
  974         internal("no ses->loading_url");
  975         return 0;
  976     }
  977     if (!(ce = (*stat)->ce) || (ses->wtd == WTD_IMGMAP && (*stat)->state >= 0)) {
  978         return 0;
  979     }
  980     if (ce->redirect && ses->redirect_cnt++ < MAX_REDIRECTS) {
  981         unsigned char *u, *p, *gp, *pos;
  982         int w = ses->wtd;
  983         if (ses->wtd == WTD_BACK && (void *)cur_loc(ses)->next == &ses->history)
  984             goto b;
  985         if (!(u = join_urls(ses->loading_url, ce->redirect))) goto b;
  986         if (!http_bugs.bug_302_redirect) if (!ce->redirect_get && (p = strchr(ses->loading_url, POST_CHAR))) add_to_strn(&u, p);
  987         /* ^^^^ According to RFC2068 POST must not be redirected to GET,
  988             but some BUGGY message boards rely on it :-( */
  989         gp = stracpy(ses->goto_position);
  990         if ((pos = extract_position(u))) {
  991             if (!gp) gp = pos;
  992             else mem_free(pos);
  993         }
  994         abort_loading(ses);
  995         if (!list_empty(ses->history)) *stat = &cur_loc(ses)->stat;
  996         else *stat = NULL;
  997         if (w == WTD_FORWARD || w == WTD_IMGMAP) {
  998             ses_goto(ses, u, ses->wtd_target, PRI_MAIN, ses->redirect_cnt < MAX_CACHED_REDIRECTS ? NC_CACHE : NC_RELOAD, w, gp, end_load, 1);
  999             return 2;
 1000         }
 1001         if (gp) mem_free(gp);
 1002         if (w == WTD_BACK) {
 1003             ses_goto(ses, u, NULL, PRI_MAIN, ses->redirect_cnt < MAX_CACHED_REDIRECTS ? NC_CACHE : NC_RELOAD, WTD_RELOAD, NULL, end_load, 1);
 1004             return 2;
 1005         }
 1006         if (w == WTD_RELOAD) {
 1007             ses_goto(ses, u, NULL, PRI_MAIN, ses->redirect_cnt < MAX_CACHED_REDIRECTS && ses->reloadlevel < NC_RELOAD ? ses->reloadlevel : NC_RELOAD, WTD_RELOAD, NULL, end_load, 1);
 1008             return 2;
 1009         }
 1010     }
 1011     b:
 1012     if (ses->display_timer != -1) kill_timer(ses->display_timer), ses->display_timer = -1;
 1013     if (ses->wtd == WTD_FORWARD) {
 1014         if (ses_chktype(ses, stat, ce)) {
 1015             free_wtd(ses);
 1016             reload(ses, NC_CACHE);
 1017             return 2;
 1018         }
 1019     }
 1020     if (ses->wtd == WTD_IMGMAP) ses_imgmap(ses);
 1021     if (ses->wtd == WTD_BACK) ses_back(ses);
 1022     if (ses->wtd == WTD_RELOAD) ses_back(ses), ses_forward(ses);
 1023     if ((*stat)->state >= 0) change_connection(&ses->loading, *stat = &cur_loc(ses)->stat, PRI_MAIN);
 1024     else cur_loc(ses)->stat.state = ses->loading.state;
 1025     free_wtd(ses);
 1026     return 1;
 1027 }
 1028 
 1029 void request_frameset(struct session *, struct frameset_desc *);
 1030 
 1031 void request_frame(struct session *ses, unsigned char *name, unsigned char *uurl)
 1032 {
 1033     struct location *loc = cur_loc(ses);
 1034     struct frame *frm;
 1035     unsigned char *url, *pos;
 1036     if (list_empty(ses->history)) {
 1037         internal("request_frame: no location");
 1038         return;
 1039     }
 1040     foreach(frm, loc->frames) if (!strcasecmp(frm->name, name)) {
 1041         struct f_data_c *fd;
 1042         foreach(fd, ses->scrn_frames) if (fd->vs == &frm->vs && fd->f_data->frame_desc) {
 1043             request_frameset(ses, fd->f_data->frame_desc);
 1044             return;
 1045         }
 1046         url = stracpy(frm->vs.url);
 1047         /*
 1048         if (frm->vs.f && frm->vs.f->f_data && frm->vs.f->f_data->frame) {
 1049             request_frameset(ses, frm->vs.f->f_data->frame_desc);
 1050             mem_free(url);
 1051             return;
 1052         }*/
 1053         goto found;
 1054     }
 1055     url = stracpy(uurl);
 1056     pos = extract_position(url);
 1057     frm = mem_alloc(sizeof(struct frame) + strlen(url) + 1);
 1058     memset(frm, 0, sizeof(struct frame));
 1059     if (!(frm->name = stracpy(name))) {
 1060         mem_free(frm);
 1061         mem_free(url);
 1062         if (pos) mem_free(pos);
 1063         return;
 1064     }
 1065     init_vs(&frm->vs, url);
 1066     if (pos) frm->vs.goto_position = pos;
 1067     add_to_list(loc->frames, frm);
 1068     found:
 1069     if (*url) request_additional_file(ses, url, PRI_FRAME);
 1070     mem_free(url);
 1071 }
 1072 
 1073 void request_frameset(struct session *ses, struct frameset_desc *fd)
 1074 {
 1075     int i;
 1076     static int depth = 0;
 1077     if (++depth <= HTML_MAX_FRAME_DEPTH) {
 1078         for (i = 0; i < fd->n; i++) {
 1079             if (fd->f[i].subframe) request_frameset(ses, fd->f[i].subframe);
 1080             else if (fd->f[i].name) request_frame(ses, fd->f[i].name, fd->f[i].url);
 1081         }
 1082     }
 1083     depth--;
 1084 }
 1085 
 1086 void load_frames(struct session *ses, struct f_data_c *fd)
 1087 {
 1088     struct f_data *ff = fd->f_data;
 1089     if (!ff || !ff->frame) return;
 1090     request_frameset(ses, ff->frame_desc);
 1091 }
 1092 
 1093 void display_timer(struct session *ses)
 1094 {
 1095     ttime t = get_time();
 1096     html_interpret(ses);
 1097     draw_formatted(ses);
 1098     t = (get_time() - t) * DISPLAY_TIME;
 1099     if (t < DISPLAY_TIME_MIN) t = DISPLAY_TIME_MIN;
 1100     ses->display_timer = install_timer(t, (void (*)(void *))display_timer, ses);
 1101     load_frames(ses, ses->screen);
 1102     process_file_requests(ses);
 1103 }
 1104 
 1105 void end_load(struct status *stat, struct session *ses)
 1106 {
 1107     int d;
 1108     if (!ses->wtd) {
 1109         internal("end_load: !ses->wtd");
 1110         return;
 1111     }
 1112     d = do_move(ses, &stat);
 1113     if (!stat) return;
 1114     if (d == 1) {
 1115         stat->end = (void (*)(struct status *, void *))doc_end_load;
 1116         display_timer(ses);
 1117     }
 1118     if (stat->state < 0) {
 1119         if (d != 2 && ses->wtd) {
 1120             free_wtd(ses);
 1121         }
 1122         if (d == 1) doc_end_load(stat, ses);
 1123     }
 1124     if (stat->state < 0 && stat->state != S_OK && d != 2 && d != 1) {
 1125         print_error_dialog(ses, stat, TEXT_(T_ERROR));
 1126         if (!d) reload(ses, NC_CACHE);
 1127     }
 1128     print_screen_status(ses);
 1129 }
 1130 
 1131 void doc_end_load(struct status *stat, struct session *ses)
 1132 {
 1133     if (stat->state < 0) {
 1134         if (ses->display_timer != -1) kill_timer(ses->display_timer), ses->display_timer = -1;
 1135         html_interpret(ses);
 1136         draw_formatted(ses);
 1137         load_frames(ses, ses->screen);
 1138         process_file_requests(ses);
 1139         if (stat->state != S_OK) print_error_dialog(ses, stat, TEXT_(T_ERROR));
 1140     } else if (ses->display_timer == -1) display_timer(ses);
 1141     print_screen_status(ses);
 1142 }
 1143 
 1144 void file_end_load(struct status *stat, struct file_to_load *ftl)
 1145 {
 1146     if (ftl->stat.ce) {
 1147         if (ftl->ce) ftl->ce->refcount--;
 1148         (ftl->ce = ftl->stat.ce)->refcount++;
 1149     }
 1150     doc_end_load(stat, ftl->ses);
 1151 }
 1152 
 1153 struct file_to_load *request_additional_file(struct session *ses, unsigned char *url, int pri)
 1154 {
 1155     struct file_to_load *ftl;
 1156     foreach(ftl, ses->more_files) {
 1157         if (!strcmp(ftl->url, url)) {
 1158             if (ftl->pri > pri) {
 1159                 ftl->pri = pri;
 1160                 change_connection(&ftl->stat, &ftl->stat, pri);
 1161             }
 1162             return NULL;
 1163         }
 1164     }
 1165     ftl = mem_alloc(sizeof(struct file_to_load));
 1166     ftl->url = stracpy(url);
 1167     ftl->stat.end = (void (*)(struct status *, void *))file_end_load;
 1168     ftl->stat.data = ftl;
 1169     ftl->req_sent = 0;
 1170     ftl->pri = pri;
 1171     ftl->ce = NULL;
 1172     ftl->ses = ses;
 1173     add_to_list(ses->more_files, ftl);
 1174     return ftl;
 1175 }
 1176 
 1177 struct file_to_load *request_additional_loading_file(struct session *ses, unsigned char *url, struct status *stat, int pri)
 1178 {
 1179     struct file_to_load *ftl;
 1180     if (!(ftl = request_additional_file(ses, url, pri))) {
 1181         change_connection(stat, NULL, PRI_CANCEL);
 1182         return NULL;
 1183     }
 1184     ftl->req_sent = 1;
 1185     ftl->ce = stat->ce;
 1186     change_connection(stat, &ftl->stat, pri);
 1187     return ftl;
 1188 }
 1189 
 1190 void process_file_requests(struct session *ses)
 1191 {
 1192     static int stop_recursion = 0;
 1193     struct file_to_load *ftl;
 1194     int more;
 1195     if (stop_recursion) return;
 1196     stop_recursion = 1;
 1197     do {
 1198         more = 0;
 1199         foreach(ftl, ses->more_files) if (!ftl->req_sent) {
 1200             ftl->req_sent = 1;
 1201             load_url(ftl->url, &ftl->stat, ftl->pri, NC_CACHE);
 1202             more = 1;
 1203         }
 1204     } while (more);
 1205     stop_recursion = 0;
 1206 }
 1207 
 1208 struct session *create_session(struct window *win)
 1209 {
 1210     struct terminal *term = win->term;
 1211     struct session *ses;
 1212     ses = mem_alloc(sizeof(struct session));
 1213     memset(ses, 0, sizeof(struct session));
 1214     init_list(ses->history);
 1215     init_list(ses->scrn_frames);
 1216     init_list(ses->more_files);
 1217     ses->term = term;
 1218     ses->win = win;
 1219     ses->id = session_id++;
 1220     ses->screen = NULL;
 1221     ses->wtd = WTD_NO;
 1222     ses->display_timer = -1;
 1223     ses->loading_url = NULL;
 1224     ses->goto_position = NULL;
 1225     memcpy(&ses->ds, &dds, sizeof(struct document_setup));
 1226     add_to_list(sessions, ses);
 1227     if (first_use) {
 1228         first_use = 0;
 1229         msg_box(term, NULL, TEXT_(T_WELCOME), AL_CENTER | AL_EXTD_TEXT, TEXT_(T_WELCOME_TO_LINKS), "\n\n", TEXT_(T_BASIC_HELP), NULL, NULL, 1, TEXT_(T_OK), NULL, B_ENTER | B_ESC);
 1230     }
 1231     return ses;
 1232 }
 1233 
 1234 void copy_session(struct session *old, struct session *new)
 1235 {
 1236     /*struct location *l;
 1237     foreachback(l, old->history) {
 1238         struct location *nl;
 1239         struct frame *frm;
 1240         nl = mem_alloc(sizeof(struct location) + strlen(l->vs.url) + 1);
 1241         memcpy(nl, l, sizeof(struct location) + strlen(l->vs.url) + 1);
 1242         init_list(nl->frames);
 1243         foreachback(frm, l->frames) {
 1244             struct frame *nfrm;
 1245             nfrm = mem_alloc(sizeof(struct frame) + strlen(frm->vs.url) + 1);
 1246             memcpy(nfrm, frm, sizeof(struct frame) + strlen(frm->vs.url) + 1);
 1247             add_to_list(nl->frames, nfrm);
 1248         }
 1249         add_to_list(new->history, nl);
 1250     }*/
 1251     if (!list_empty(old->history)) {
 1252         goto_url(new, cur_loc(old)->vs.url);
 1253     }
 1254 }
 1255 
 1256 void *create_session_info(int cp, unsigned char *url, int *ll)
 1257 {
 1258     int l = strlen(url);
 1259     int *i;
 1260     *ll = 2 * sizeof(int) + l;
 1261     i = mem_alloc(2 * sizeof(int) + l);
 1262     i[0] = cp;
 1263     i[1] = l;
 1264     memcpy(i + 2, url, l);
 1265     return i;
 1266 }
 1267 
 1268 static inline unsigned char hx(int a)
 1269 {
 1270     return a >= 10 ? a + 'A' - 10 : a + '0';
 1271 }
 1272 
 1273 static inline int unhx(unsigned char a)
 1274 {
 1275     if (a >= '0' && a <= '9') return a - '0';
 1276     if (a >= 'A' && a <= 'F') return a - 'A' + 10;
 1277     if (a >= 'a' && a <= 'f') return a - 'a' + 10;
 1278     return -1;
 1279 }
 1280 
 1281 unsigned char *encode_url(unsigned char *url)
 1282 {
 1283     unsigned char *u = init_str();
 1284     int l = 0;
 1285     add_to_str(&u, &l, "+++");
 1286     for (; *url; url++) {
 1287         if (is_safe_in_shell(*url) && *url != '+') add_chr_to_str(&u, &l, *url);
 1288         else add_chr_to_str(&u, &l, '+'), add_chr_to_str(&u, &l, hx(*url >> 4)), add_chr_to_str(&u, &l, hx(*url & 0xf));
 1289     }
 1290     return u;
 1291 }
 1292 
 1293 unsigned char *decode_url(unsigned char *url)
 1294 {
 1295     unsigned char *u;
 1296     int l;
 1297     if (casecmp(url, "+++", 3)) return stracpy(url);
 1298     url += 3;
 1299     u = init_str();
 1300     l = 0;
 1301     for (; *url; url++) {
 1302         if (*url != '+' || unhx(url[1]) == -1 || unhx(url[2]) == -1) add_chr_to_str(&u, &l, *url);
 1303         else add_chr_to_str(&u, &l, (unhx(url[1]) << 4) + unhx(url[2])), url += 2;
 1304     }
 1305     return u;
 1306 }
 1307 
 1308 int read_session_info(int fd, struct session *ses, void *data, int len)
 1309 {
 1310     unsigned char *h;
 1311     int cpfrom, sz;
 1312     struct session *s;
 1313     if (len < 2 * (int)sizeof(int)) return -1;
 1314     cpfrom = *(int *)data;
 1315     sz = *((int *)data + 1);
 1316     foreach(s, sessions) if (s->id == cpfrom) {
 1317         copy_session(s, ses);
 1318         break;
 1319     }
 1320     if (sz) {
 1321         char *u, *uu;
 1322         if (len < 2 * (int)sizeof(int) + sz) return 0;
 1323         if ((unsigned)sz >= MAXINT) overalloc();
 1324         u = mem_alloc(sz + 1);
 1325         memcpy(u, (int *)data + 2, sz);
 1326         u[sz] = 0;
 1327         uu = decode_url(u);
 1328         goto_url(ses, uu);
 1329         mem_free(u);
 1330         mem_free(uu);
 1331     } else if ((h = getenv("WWW_HOME")) && *h) goto_url(ses, h);
 1332     return 0;
 1333 }
 1334 
 1335 void abort_preloading(struct session *ses)
 1336 {
 1337     if (ses->wtd) {
 1338         change_connection(&ses->loading, NULL, PRI_CANCEL);
 1339         free_wtd(ses);
 1340     }
 1341 }
 1342 
 1343 void abort_loading(struct session *ses)
 1344 {
 1345     struct location *l = cur_loc(ses);
 1346     if ((void *)l != &ses->history) {
 1347         if (l->stat.state >= 0)
 1348             change_connection(&l->stat, NULL, PRI_CANCEL);
 1349         abort_files_load(ses);
 1350     }
 1351     abort_preloading(ses);
 1352 }
 1353 
 1354 void destroy_session(struct session *ses)
 1355 {
 1356     struct f_data_c *fdc;
 1357     struct download *d;
 1358     struct location *l;
 1359     if (!ses) return;
 1360     foreach(d, downloads) if (d->ses == ses && d->prog) {
 1361         d = d->prev;
 1362         abort_download(d->next);
 1363     }
 1364     abort_loading(ses);
 1365     free_files(ses);
 1366     if (ses->screen) detach_formatted(ses->screen), mem_free(ses->screen);
 1367     foreach(fdc, ses->scrn_frames) detach_formatted(fdc);
 1368     free_list(ses->scrn_frames);
 1369     while ((void *)(l = ses->history.next) != &ses->history) {
 1370         struct frame *frm;
 1371         while ((void *)(frm = l->frames.next) != &l->frames) {
 1372             destroy_vs(&frm->vs);
 1373             mem_free(frm->name);
 1374             del_from_list(frm);
 1375             mem_free(frm);
 1376         }
 1377         destroy_vs(&l->vs);
 1378         del_from_list(l);
 1379         mem_free(l);
 1380     }
 1381     if (ses->loading_url) mem_free(ses->loading_url);
 1382     if (ses->display_timer != -1) kill_timer(ses->display_timer);
 1383     if (ses->goto_position) mem_free(ses->goto_position);
 1384     if (ses->imgmap_href_base) mem_free(ses->imgmap_href_base);
 1385     if (ses->imgmap_target_base) mem_free(ses->imgmap_target_base);
 1386     if (ses->tq_ce) ses->tq_ce->refcount--;
 1387     if (ses->tq_url) {
 1388         change_connection(&ses->tq, NULL, PRI_CANCEL);
 1389         mem_free(ses->tq_url);
 1390     }
 1391     if (ses->tq_goto_position) mem_free(ses->tq_goto_position);
 1392     if (ses->tq_prog) mem_free(ses->tq_prog);
 1393     if (ses->dn_url) mem_free(ses->dn_url);
 1394     if (ses->search_word) mem_free(ses->search_word);
 1395     if (ses->last_search_word) mem_free(ses->last_search_word);
 1396     del_from_list(ses);
 1397     /*mem_free(ses);*/
 1398 }
 1399 
 1400 void destroy_all_sessions()
 1401 {
 1402     /*while (!list_empty(sessions)) destroy_session(sessions.next);*/
 1403 }
 1404 
 1405 void abort_all_downloads()
 1406 {
 1407     while (!list_empty(downloads)) abort_download(downloads.next);
 1408 }
 1409 
 1410 void reload(struct session *ses, int no_cache)
 1411 {
 1412     struct location *l;
 1413     abort_loading(ses);
 1414     if (no_cache == -1) no_cache = ++ses->reloadlevel;
 1415     else ses->reloadlevel = no_cache;
 1416     if ((void *)(l = ses->history.next) != &ses->history) {
 1417         struct file_to_load *ftl;
 1418         l->stat.data = ses;
 1419         l->stat.end = (void *)doc_end_load;
 1420         load_url(l->vs.url, &l->stat, PRI_MAIN, no_cache);
 1421         foreach(ftl, ses->more_files) {
 1422             if (ftl->req_sent && ftl->stat.state >= 0) continue;
 1423             ftl->stat.data = ftl;
 1424             ftl->stat.end = (void *)file_end_load;
 1425             load_url(ftl->url, &ftl->stat, PRI_FRAME, no_cache);
 1426         }
 1427     }
 1428 }
 1429 
 1430 void go_back(struct session *ses)
 1431 {
 1432     unsigned char *url;
 1433     ses->reloadlevel = NC_CACHE;
 1434     if (ses->wtd) {
 1435         if (1 || ses->wtd != WTD_BACK) {
 1436             abort_loading(ses);
 1437             print_screen_status(ses);
 1438             reload(ses, NC_CACHE);
 1439         }
 1440         return;
 1441     }
 1442     if (ses->history.next == &ses->history || ses->history.next == ses->history.prev)
 1443         return;
 1444     abort_loading(ses);
 1445     url = stracpy(((struct location *)ses->history.next)->next->vs.url);
 1446     ses->redirect_cnt = 0;
 1447     ses_goto(ses, url, NULL, PRI_MAIN, NC_ALWAYS_CACHE, WTD_BACK, NULL, end_load, 0);
 1448 }
 1449 
 1450 void goto_url_w(struct session *ses, unsigned char *url, unsigned char *target, int wtd)
 1451 {
 1452     unsigned char *u;
 1453     unsigned char *pos;
 1454     void (*fn)(struct session *, unsigned char *);
 1455     if ((fn = get_external_protocol_function(url))) {
 1456         fn(ses, url);
 1457         return;
 1458     }
 1459     ses->reloadlevel = NC_CACHE;
 1460     /*struct location *l = ses->history.next;*/
 1461     if (!(u = translate_url(url, ses->term->cwd))) {
 1462         struct status stat = { NULL, NULL, NULL, NULL, S_BAD_URL, PRI_CANCEL, 0, NULL, NULL, NULL };
 1463         print_error_dialog(ses, &stat, TEXT_(T_ERROR));
 1464         return;
 1465     }
 1466     pos = extract_position(u);
 1467     if (ses->wtd == wtd) {
 1468         if (!strcmp(ses->loading_url, u)) {
 1469             mem_free(u);
 1470             if (ses->goto_position) mem_free(ses->goto_position);
 1471             ses->goto_position = pos;
 1472             return;
 1473         }
 1474     }
 1475     abort_loading(ses);
 1476     ses->redirect_cnt = 0;
 1477     ses_goto(ses, u, target, PRI_MAIN, NC_CACHE, wtd, pos, end_load, 0);
 1478     /*abort_loading(ses);*/
 1479 }
 1480 
 1481 void goto_url_f(struct session *ses, unsigned char *url, unsigned char *target)
 1482 {
 1483     goto_url_w(ses, url, target, WTD_FORWARD);
 1484 }
 1485 
 1486 void goto_url(struct session *ses, unsigned char *url)
 1487 {
 1488     goto_url_w(ses, url, NULL, WTD_FORWARD);
 1489 }
 1490 
 1491 void goto_imgmap(struct session *ses, unsigned char *url, unsigned char *href, unsigned char *target)
 1492 {
 1493     if (ses->imgmap_href_base) mem_free(ses->imgmap_href_base);
 1494     ses->imgmap_href_base = href;
 1495     if (ses->imgmap_target_base) mem_free(ses->imgmap_target_base);
 1496     ses->imgmap_target_base = target;
 1497     goto_url_w(ses, url, target, WTD_IMGMAP);
 1498 }
 1499 
 1500 struct frame *ses_find_frame(struct session *ses, unsigned char *name)
 1501 {
 1502     struct location *l = cur_loc(ses);
 1503     struct frame *frm;
 1504     if (list_empty(ses->history)) {
 1505         internal("ses_request_frame: history empty");
 1506         return NULL;
 1507     }
 1508     foreachback(frm, l->frames) if (!strcasecmp(frm->name, name)) return frm;
 1509     /*internal("ses_find_frame: frame not found");*/
 1510     return NULL;
 1511 }
 1512 
 1513 struct frame *ses_change_frame_url(struct session *ses, unsigned char *name, unsigned char *url)
 1514 {
 1515     struct location *l = cur_loc(ses);
 1516     struct frame *frm;
 1517     if (list_empty(ses->history)) {
 1518         internal("ses_change_frame_url: history empty");
 1519         return NULL;
 1520     }
 1521     foreachback(frm, l->frames) if (!strcasecmp(frm->name, name)) {
 1522         if (strlen(url) > strlen(frm->vs.url)) {
 1523             struct f_data_c *fd;
 1524             struct frame *nf = frm;
 1525             nf = mem_realloc(frm, sizeof(struct frame) + strlen(url) + 1);
 1526             nf->prev->next = nf->next->prev = nf;
 1527             foreach(fd, ses->scrn_frames) {
 1528                 if (fd->vs == &frm->vs) fd->vs = &nf->vs;
 1529             }
 1530             frm = nf;
 1531         }
 1532         strcpy(frm->vs.url, url);
 1533         return frm;
 1534     }
 1535     return NULL;
 1536     
 1537 }
 1538 
 1539 void win_func(struct window *win, struct event *ev, int fw)
 1540 {
 1541     struct session *ses = win->data;
 1542     switch ((int)ev->ev) {
 1543         case EV_ABORT:
 1544             if (ses) destroy_session(ses);
 1545             break;
 1546         case EV_INIT:
 1547             if (!(ses = win->data = create_session(win)) ||
 1548                 read_session_info(win->term->fdin, ses, (char *)ev->b + sizeof(int), *(int *)ev->b)) {
 1549                 register_bottom_half((void (*)(void *))destroy_terminal, win->term);
 1550                 return;
 1551             }
 1552             /*make_request(ses, 0);*/
 1553         case EV_RESIZE:
 1554             html_interpret(ses);
 1555             draw_formatted(ses);
 1556             load_frames(ses, ses->screen);
 1557             process_file_requests(ses);
 1558             print_screen_status(ses);
 1559             break;
 1560         case EV_REDRAW:
 1561             draw_formatted(ses);
 1562             print_screen_status(ses);
 1563             break;
 1564         case EV_KBD:
 1565         case EV_MOUSE:
 1566             send_event(ses, ev);
 1567             break;
 1568         default:
 1569             error("ERROR: unknown event");
 1570     }
 1571 }
 1572 
 1573 /* 
 1574   Gets the url being viewed by this session. Writes it into str.
 1575   A maximum of str_size bytes (including null) will be written.
 1576 */  
 1577 unsigned char *get_current_url(struct session *ses, unsigned char *str, size_t str_size) {
 1578     unsigned char *here, *end_of_url;
 1579     size_t url_len = 0;
 1580 
 1581     /* Not looking at anything */
 1582     if (list_empty(ses->history))
 1583         return NULL;
 1584 
 1585     here = cur_loc(ses)->vs.url;
 1586 
 1587     /* Find the length of the url */
 1588     if ((end_of_url = strchr(here, POST_CHAR))) {
 1589         url_len = (size_t)(end_of_url - (unsigned char *)here);
 1590     } else {
 1591         url_len = strlen(here);
 1592     }
 1593 
 1594     /* Ensure that the url size is not greater than str_size */ 
 1595     if (url_len >= str_size)
 1596             url_len = str_size - 1;
 1597 
 1598     safe_strncpy(str, here, url_len + 1);
 1599 
 1600     return str;
 1601 }
 1602 
 1603 
 1604 /* 
 1605   Gets the title of the page being viewed by this session. Writes it into str.
 1606   A maximum of str_size bytes (including null) will be written.
 1607 */  
 1608 unsigned char *get_current_title(struct session *ses, unsigned char *str, size_t str_size) {
 1609     struct f_data_c *fd;
 1610     fd = (struct f_data_c *)current_frame(ses);
 1611 
 1612     /* Ensure that the title is defined */
 1613     if (!fd)
 1614         return NULL;
 1615 
 1616     return safe_strncpy(str, fd->f_data->title, str_size);
 1617 }
 1618 
 1619 /* 
 1620   Gets the url of the link currently selected. Writes it into str.
 1621   A maximum of str_size bytes (including null) will be written.
 1622 */  
 1623 unsigned char *get_current_link_url(struct session *ses, unsigned char *str, size_t str_size) {
 1624     struct f_data_c *fd;
 1625     struct link *l;
 1626     
 1627     fd = (struct f_data_c *)current_frame(ses);
 1628     /* What the hell is an 'fd'? */
 1629     if (!fd)
 1630         return NULL;
 1631     
 1632     /* Nothing selected? */
 1633     if (fd->vs->current_link == -1) 
 1634         return NULL;
 1635 
 1636     l = &fd->f_data->links[fd->vs->current_link];
 1637     /* Only write a link */
 1638     if (l->type != L_LINK) 
 1639         return NULL;
 1640     
 1641     return safe_strncpy(str, l->where ? l->where : l->where_img, str_size);
 1642 }