diff options
Diffstat (limited to 'src/LYHistory.c')
-rw-r--r-- | src/LYHistory.c | 1166 |
1 files changed, 1166 insertions, 0 deletions
diff --git a/src/LYHistory.c b/src/LYHistory.c new file mode 100644 index 0000000..1d7c84a --- /dev/null +++ b/src/LYHistory.c @@ -0,0 +1,1166 @@ +/* + * $LynxId: LYHistory.c,v 1.91 2020/01/21 21:34:20 tom Exp $ + */ +#include <HTUtils.h> +#include <HTTP.h> +#include <GridText.h> +#include <HTAlert.h> +#include <HText.h> +#include <LYGlobalDefs.h> +#include <LYUtils.h> +#include <LYHistory.h> +#include <LYPrint.h> +#include <LYDownload.h> +#include <LYOptions.h> +#include <LYKeymap.h> +#include <LYList.h> +#include <LYShowInfo.h> +#include <LYStrings.h> +#include <LYCharUtils.h> +#include <LYCharSets.h> +#include <LYrcFile.h> +#ifdef DISP_PARTIAL +#include <LYMainLoop.h> +#endif + +#ifdef DIRED_SUPPORT +#include <LYUpload.h> +#include <LYLocal.h> +#endif /* DIRED_SUPPORT */ + +#include <LYexit.h> +#include <LYLeaks.h> +#include <HTCJK.h> + +HTList *Visited_Links = NULL; /* List of safe popped docs. */ +int Visited_Links_As = VISITED_LINKS_AS_LATEST | VISITED_LINKS_REVERSE; + +static VisitedLink *PrevVisitedLink = NULL; /* NULL on auxiliary */ +static VisitedLink *PrevActiveVisitedLink = NULL; /* Last non-auxillary */ +static VisitedLink Latest_first; +static VisitedLink Latest_last; +static VisitedLink *Latest_tree; +static VisitedLink *First_tree; +static VisitedLink *Last_by_first; + +int nhist_extra; + +#ifdef LY_FIND_LEAKS +static int already_registered_free_messages_stack = 0; +static int already_registered_clean_all_history = 0; +#endif + +#ifdef LY_FIND_LEAKS +/* + * Utility for freeing the list of visited links. - FM + */ +static void Visited_Links_free(void) +{ + VisitedLink *vl; + HTList *cur = Visited_Links; + + PrevVisitedLink = NULL; + PrevActiveVisitedLink = NULL; + if (!cur) + return; + + while (NULL != (vl = (VisitedLink *) HTList_nextObject(cur))) { + FREE(vl->address); + FREE(vl->title); + FREE(vl); + } + HTList_delete(Visited_Links); + Visited_Links = NULL; + Latest_last.prev_latest = &Latest_first; + Latest_first.next_latest = &Latest_last; + Last_by_first = Latest_tree = First_tree = 0; + return; +} +#endif /* LY_FIND_LEAKS */ + +#ifdef DEBUG +static void trace_history(const char *tag) +{ + if (TRACE) { + CTRACE((tfp, "HISTORY %s %d/%d (%d extra)\n", + tag, nhist, size_history, nhist_extra)); + CTRACE_FLUSH(tfp); + } +} +#else +#define trace_history(tag) /* nothing */ +#endif /* DEBUG */ + +/* + * Utility for listing visited links, making any repeated links the most + * current in the list. - FM + */ +void LYAddVisitedLink(DocInfo *doc) +{ + VisitedLink *tmp; + HTList *cur; + const char *title = (doc->title ? doc->title : NO_TITLE); + + if (isEmpty(doc->address)) { + PrevVisitedLink = NULL; + return; + } + + /* + * Exclude POST or HEAD replies, and bookmark, menu or list files. - FM + */ + if (doc->post_data || doc->isHEAD || doc->bookmark || + ( /* special url or a temp file */ + (!StrNCmp(doc->address, "LYNX", 4) || + !StrNCmp(doc->address, "file://localhost/", 17)))) { + int related = 1; /* First approximation only */ + + if (LYIsUIPage(doc->address, UIP_HISTORY) || + LYIsUIPage(doc->address, UIP_VLINKS) || + LYIsUIPage(doc->address, UIP_SHOWINFO) || + isLYNXMESSAGES(doc->address) || + ((related = 0) != 0) || +#ifdef DIRED_SUPPORT + LYIsUIPage(doc->address, UIP_DIRED_MENU) || + LYIsUIPage(doc->address, UIP_UPLOAD_OPTIONS) || + LYIsUIPage(doc->address, UIP_PERMIT_OPTIONS) || +#endif /* DIRED_SUPPORT */ + LYIsUIPage(doc->address, UIP_PRINT_OPTIONS) || + LYIsUIPage(doc->address, UIP_DOWNLOAD_OPTIONS) || + LYIsUIPage(doc->address, UIP_OPTIONS_MENU) || + isLYNXEDITMAP(doc->address) || + isLYNXKEYMAP(doc->address) || + LYIsUIPage(doc->address, UIP_LIST_PAGE) || +#ifdef USE_ADDRLIST_PAGE + LYIsUIPage(doc->address, UIP_ADDRLIST_PAGE) || +#endif + LYIsUIPage(doc->address, UIP_CONFIG_DEF) || + LYIsUIPage(doc->address, UIP_LYNXCFG) || + isLYNXCOOKIE(doc->address) || + LYIsUIPage(doc->address, UIP_TRACELOG)) { + if (!related) + PrevVisitedLink = NULL; + return; + } + } + + if (!Visited_Links) { + Visited_Links = HTList_new(); +#ifdef LY_FIND_LEAKS + atexit(Visited_Links_free); +#endif + Latest_last.prev_latest = &Latest_first; + Latest_first.next_latest = &Latest_last; + Latest_last.next_latest = NULL; /* Find bugs quick! */ + Latest_first.prev_latest = NULL; + Last_by_first = Latest_tree = First_tree = NULL; + } + + cur = Visited_Links; + while (NULL != (tmp = (VisitedLink *) HTList_nextObject(cur))) { + if (!strcmp(NonNull(tmp->address), + NonNull(doc->address))) { + PrevVisitedLink = PrevActiveVisitedLink = tmp; + /* Already visited. Update the last-visited info. */ + if (tmp->next_latest == &Latest_last) /* optimization */ + return; + + /* Remove from "latest" chain */ + tmp->prev_latest->next_latest = tmp->next_latest; + tmp->next_latest->prev_latest = tmp->prev_latest; + + /* Insert at the end of the "latest" chain */ + Latest_last.prev_latest->next_latest = tmp; + tmp->prev_latest = Latest_last.prev_latest; + tmp->next_latest = &Latest_last; + Latest_last.prev_latest = tmp; + return; + } + } + + if ((tmp = typecalloc(VisitedLink)) == NULL) + outofmem(__FILE__, "LYAddVisitedLink"); + + StrAllocCopy(tmp->address, doc->address); + LYformTitle(&(tmp->title), title); + + /* First-visited chain */ + HTList_appendObject(Visited_Links, tmp); /* At end */ + tmp->prev_first = Last_by_first; + Last_by_first = tmp; + + /* Tree structure */ + if (PrevVisitedLink) { + VisitedLink *a = PrevVisitedLink; + VisitedLink *b = a->next_tree; + int l = PrevVisitedLink->level; + + /* Find last on the deeper levels */ + while (b && b->level > l) + a = b, b = b->next_tree; + + if (!b) /* a == Latest_tree */ + Latest_tree = tmp; + tmp->next_tree = a->next_tree; + a->next_tree = tmp; + + tmp->level = PrevVisitedLink->level + 1; + } else { + if (Latest_tree) + Latest_tree->next_tree = tmp; + tmp->level = 0; + tmp->next_tree = NULL; + Latest_tree = tmp; + } + PrevVisitedLink = PrevActiveVisitedLink = tmp; + if (!First_tree) + First_tree = tmp; + + /* "latest" chain */ + Latest_last.prev_latest->next_latest = tmp; + tmp->prev_latest = Latest_last.prev_latest; + tmp->next_latest = &Latest_last; + Latest_last.prev_latest = tmp; + + return; +} + +/* + * Returns true if this is a page that we would push onto the stack if not + * forced. If docurl is NULL, only the title is considered; otherwise also + * check the URL whether it is (likely to be) a generated special page. + */ +BOOLEAN LYwouldPush(const char *title, + const char *docurl) +{ + BOOLEAN rc = FALSE; + + /* + * All non-pushable generated pages have URLs that begin with + * "file://localhost/" and end with HTML_SUFFIX. - kw + */ + if (docurl) { + size_t ulen; + + if (StrNCmp(docurl, "file://localhost/", 17) != 0 || + (ulen = strlen(docurl)) <= strlen(HTML_SUFFIX) || + strcmp(docurl + ulen - strlen(HTML_SUFFIX), HTML_SUFFIX) != 0) { + /* + * If it is not a local HTML file, it may be a Web page that + * accidentally has the same title. So return TRUE now. - kw + */ + return TRUE; + } + } + + if (docurl) { + rc = (BOOLEAN) + !(LYIsUIPage(docurl, UIP_HISTORY) + || LYIsUIPage(docurl, UIP_PRINT_OPTIONS) +#ifdef DIRED_SUPPORT + || LYIsUIPage(docurl, UIP_DIRED_MENU) + || LYIsUIPage(docurl, UIP_UPLOAD_OPTIONS) + || LYIsUIPage(docurl, UIP_PERMIT_OPTIONS) +#endif /* DIRED_SUPPORT */ + ); + } else { + rc = (BOOLEAN) + !(!strcmp(title, HISTORY_PAGE_TITLE) + || !strcmp(title, PRINT_OPTIONS_TITLE) +#ifdef DIRED_SUPPORT + || !strcmp(title, DIRED_MENU_TITLE) + || !strcmp(title, UPLOAD_OPTIONS_TITLE) + || !strcmp(title, PERMIT_OPTIONS_TITLE) +#endif /* DIRED_SUPPORT */ + ); + } + return rc; +} + +/* + * Free post-data for 'DocInfo' + */ +void LYFreePostData(DocInfo *doc) +{ + BStrFree(doc->post_data); + FREE(doc->post_content_type); +} + +/* + * Free strings associated with a 'DocInfo' struct. + */ +void LYFreeDocInfo(DocInfo *doc) +{ + FREE(doc->title); + FREE(doc->address); + FREE(doc->bookmark); + LYFreePostData(doc); +} + +/* + * Free the information in the last history entry. + */ +static void clean_extra_history(void) +{ + trace_history("clean_extra_history"); + nhist += nhist_extra; + while (nhist_extra > 0) { + nhist--; + LYFreeDocInfo(&HDOC(nhist)); + nhist_extra--; + } + trace_history("...clean_extra_history"); +} + +/* + * Free the entire history stack, for auditing memory leaks. + */ +#ifdef LY_FIND_LEAKS +static void clean_all_history(void) +{ + trace_history("clean_all_history"); + clean_extra_history(); + while (nhist > 0) { + nhist--; + LYFreeDocInfo(&HDOC(nhist)); + } + trace_history("...clean_all_history"); +} +#endif + +/* FIXME What is the relationship to are_different() from the mainloop?! */ +static int are_identical(HistInfo * doc, DocInfo *doc1) +{ + return (STREQ(doc1->address, doc->hdoc.address) + && BINEQ(doc1->post_data, doc->hdoc.post_data) + && !strcmp(NonNull(doc1->bookmark), + NonNull(doc->hdoc.bookmark)) + && doc1->isHEAD == doc->hdoc.isHEAD); +} + +void LYAllocHistory(int entries) +{ + CTRACE((tfp, "LYAllocHistory %d vs %d\n", entries, size_history)); + if (entries + 1 >= size_history) { + unsigned want; + int save = size_history; + + size_history = (entries + 2) * 2; + want = (unsigned) size_history *(unsigned) sizeof(*history); + + if (history == 0) { + history = typeMallocn(HistInfo, want); + } else { + history = typeRealloc(HistInfo, history, want); + } + if (history == 0) + outofmem(__FILE__, "LYAllocHistory"); + + while (save < size_history) { + memset(&history[save++], 0, sizeof(history[0])); + } + } + CTRACE((tfp, "...LYAllocHistory %d vs %d\n", entries, size_history)); +} + +/* + * Push the current filename, link and line number onto the history list. + */ +int LYpush(DocInfo *doc, int force_push) +{ + /* + * Don't push NULL file names. + */ + if (*doc->address == '\0') + return 0; + + /* + * Check whether this is a document we don't push unless forced. - FM + */ + if (!force_push) { + /* + * Don't push the history, printer, or download lists. + */ + if (!LYwouldPush(doc->title, doc->address)) { + if (!LYforce_no_cache) + LYoverride_no_cache = TRUE; + return 0; + } + } + + /* + * If file is identical to one before it, don't push it. + * But do not duplicate it if there is only one on the stack, + * note that HDOC() starts from 0, so nhist should be > 0. + */ + if (nhist >= 1 && are_identical(&(history[nhist - 1]), doc)) { + if (HDOC(nhist - 1).internal_link == doc->internal_link) { + /* But it is nice to have the last position remembered! + - kw */ + HDOC(nhist - 1).link = doc->link; + HDOC(nhist - 1).line = doc->line; + return 0; + } + } + + /* + * If file is identical to the current document, just move the pointer. + */ + if (nhist_extra >= 1 && are_identical(&(history[nhist]), doc)) { + HDOC(nhist).link = doc->link; + HDOC(nhist).line = doc->line; + nhist_extra--; + LYAllocHistory(nhist); + nhist++; + trace_history("LYpush: just move the cursor"); + return 1; + } + + clean_extra_history(); +#ifdef LY_FIND_LEAKS + if (!already_registered_clean_all_history) { + already_registered_clean_all_history = 1; + atexit(clean_all_history); + } +#endif + + /* + * OK, push it... + */ + LYAllocHistory(nhist); + HDOC(nhist).link = doc->link; + HDOC(nhist).line = doc->line; + + HDOC(nhist).title = NULL; + LYformTitle(&(HDOC(nhist).title), doc->title); + + HDOC(nhist).address = NULL; + StrAllocCopy(HDOC(nhist).address, doc->address); + + HDOC(nhist).post_data = NULL; + BStrCopy(HDOC(nhist).post_data, doc->post_data); + + HDOC(nhist).post_content_type = NULL; + StrAllocCopy(HDOC(nhist).post_content_type, doc->post_content_type); + + HDOC(nhist).bookmark = NULL; + StrAllocCopy(HDOC(nhist).bookmark, doc->bookmark); + + HDOC(nhist).isHEAD = doc->isHEAD; + HDOC(nhist).safe = doc->safe; + + HDOC(nhist).internal_link = FALSE; /* by default */ + history[nhist].intern_seq_start = -1; /* by default */ + if (doc->internal_link) { + /* Now some tricky stuff: if the caller thinks that the doc + to push was the result of following an internal + (fragment) link, we check whether we believe it. + It is only accepted as valid if the immediately preceding + item on the history stack is actually the same document + except for fragment and location info. I.e. the Parent + Anchors are the same. + Also of course this requires that this is not the first + history item. - kw */ + if (nhist > 0) { + DocAddress WWWDoc; + HTParentAnchor *thisparent, *thatparent = NULL; + + WWWDoc.address = doc->address; + WWWDoc.post_data = doc->post_data; + WWWDoc.post_content_type = doc->post_content_type; + WWWDoc.bookmark = doc->bookmark; + WWWDoc.isHEAD = doc->isHEAD; + WWWDoc.safe = doc->safe; + thisparent = + HTAnchor_findAddress(&WWWDoc); + /* Now find the ParentAnchor for the previous history + * item - kw + */ + if (thisparent) { + /* If the last-pushed item is a LYNXIMGMAP but THIS one + * isn't, compare the physical URLs instead. - kw + */ + if (isLYNXIMGMAP(HDOC(nhist - 1).address) && + !isLYNXIMGMAP(doc->address)) { + WWWDoc.address = HDOC(nhist - 1).address + LEN_LYNXIMGMAP; + /* + * If THIS item is a LYNXIMGMAP but the last-pushed one + * isn't, fake it by using THIS item's address for + * thatparent... - kw + */ + } else if (isLYNXIMGMAP(doc->address) && + !isLYNXIMGMAP(HDOC(nhist - 1).address)) { + char *temp = NULL; + + StrAllocCopy(temp, STR_LYNXIMGMAP); + StrAllocCat(temp, doc->address + LEN_LYNXIMGMAP); + WWWDoc.address = temp; + WWWDoc.post_content_type = HDOC(nhist - 1).post_content_type; + WWWDoc.bookmark = HDOC(nhist - 1).bookmark; + WWWDoc.isHEAD = HDOC(nhist - 1).isHEAD; + WWWDoc.safe = HDOC(nhist - 1).safe; + thatparent = + HTAnchor_findAddress(&WWWDoc); + FREE(temp); + } else { + WWWDoc.address = HDOC(nhist - 1).address; + } + if (!thatparent) { /* if not yet done */ + WWWDoc.post_data = HDOC(nhist - 1).post_data; + WWWDoc.post_content_type = HDOC(nhist - 1).post_content_type; + WWWDoc.bookmark = HDOC(nhist - 1).bookmark; + WWWDoc.isHEAD = HDOC(nhist - 1).isHEAD; + WWWDoc.safe = HDOC(nhist - 1).safe; + thatparent = + HTAnchor_findAddress(&WWWDoc); + } + /* In addition to equality of the ParentAnchors, require + * that IF we have a HTMainText (i.e., it wasn't just + * HTuncache'd by mainloop), THEN it has to be consistent + * with what we are trying to push. + * + * This may be overkill... - kw + */ + if (thatparent == thisparent && + (!HTMainText || HTMainAnchor == thisparent) + ) { + HDOC(nhist).internal_link = TRUE; + history[nhist].intern_seq_start = + history[nhist - 1].intern_seq_start >= 0 ? + history[nhist - 1].intern_seq_start : nhist - 1; + CTRACE((tfp, "\nLYpush: pushed as internal link, OK\n")); + } + } + } + if (!HDOC(nhist).internal_link) { + CTRACE((tfp, "\nLYpush: push as internal link requested, %s\n", + "but didn't check out!")); + } + } + CTRACE((tfp, "\nLYpush[%d]: address:%s\n title:%s\n", + nhist, doc->address, doc->title)); + nhist++; + return 1; +} + +/* + * Pop the previous filename, link and line number from the history list. + */ +void LYpop(DocInfo *doc) +{ + if (nhist > 0) { + clean_extra_history(); + nhist--; + + LYFreeDocInfo(doc); + + *doc = HDOC(nhist); + +#ifdef DISP_PARTIAL + /* assume we pop the 'doc' to show it soon... */ + LYSetNewline(doc->line); /* reinitialize */ +#endif /* DISP_PARTIAL */ + CTRACE((tfp, "LYpop[%d]: address:%s\n title:%s\n", + nhist, doc->address, doc->title)); + } +} + +/* + * Move to the previous filename, link and line number from the history list. + */ +void LYhist_prev(DocInfo *doc) +{ + trace_history("LYhist_prev"); + if (nhist > 0 && (nhist_extra || nhist < size_history)) { + nhist--; + nhist_extra++; + LYpop_num(nhist, doc); + trace_history("...LYhist_prev"); + } +} + +/* + * Called before calling LYhist_prev(). + */ +void LYhist_prev_register(DocInfo *doc) +{ + trace_history("LYhist_prev_register"); + if (nhist > 1) { + if (nhist_extra) { /* Make something to return back */ + /* Store the new position */ + HDOC(nhist).link = doc->link; + HDOC(nhist).line = doc->line; + } else if (LYpush(doc, 0)) { + nhist--; + nhist_extra++; + } + trace_history("...LYhist_prev_register"); + } +} + +/* + * Move to the next filename, link and line number from the history. + */ +int LYhist_next(DocInfo *doc, DocInfo *newdoc) +{ + if (nhist_extra <= 1) /* == 1 when we are the last one */ + return 0; + /* Store the new position */ + HDOC(nhist).link = doc->link; + HDOC(nhist).line = doc->line; + LYAllocHistory(nhist); + nhist++; + nhist_extra--; + LYpop_num(nhist, newdoc); + return 1; +} + +/* + * Pop the specified hist entry, link and line number from the history list but + * don't actually remove the entry, just return it. + * (This procedure is badly named :) + */ +void LYpop_num(int number, + DocInfo *doc) +{ + if (number >= 0 && (nhist + nhist_extra) > number) { + doc->link = HDOC(number).link; + doc->line = HDOC(number).line; + StrAllocCopy(doc->title, HDOC(number).title); + StrAllocCopy(doc->address, HDOC(number).address); + BStrCopy(doc->post_data, HDOC(number).post_data); + StrAllocCopy(doc->post_content_type, HDOC(number).post_content_type); + StrAllocCopy(doc->bookmark, HDOC(number).bookmark); + doc->isHEAD = HDOC(number).isHEAD; + doc->safe = HDOC(number).safe; + doc->internal_link = HDOC(number).internal_link; /* ?? */ +#ifdef DISP_PARTIAL + /* assume we pop the 'doc' to show it soon... */ + LYSetNewline(doc->line); /* reinitialize */ +#endif /* DISP_PARTIAL */ + if (TRACE) { + CTRACE((tfp, "LYpop_num(%d)\n", number)); + CTRACE((tfp, " link %d\n", doc->link)); + CTRACE((tfp, " line %d\n", doc->line)); + CTRACE((tfp, " title %s\n", NonNull(doc->title))); + CTRACE((tfp, " address %s\n", NonNull(doc->address))); + } + } +} + +/* + * This procedure outputs the history buffer into a temporary file. + */ +int showhistory(char **newfile) +{ + static char tempfile[LY_MAXPATH] = "\0"; + char *Title = NULL; + int x = 0; + FILE *fp0; + + if ((fp0 = InternalPageFP(tempfile, TRUE)) == 0) + return (-1); + + LYLocalFileToURL(newfile, tempfile); + + LYforce_HTML_mode = TRUE; /* force this file to be HTML */ + LYforce_no_cache = TRUE; /* force this file to be new */ + + BeginInternalPage(fp0, HISTORY_PAGE_TITLE, HISTORY_PAGE_HELP); + + fprintf(fp0, "<p align=right> <a href=\"%s\">[%s]</a>\n", + STR_LYNXMESSAGES, STATUSLINES_TITLE); + + fprintf(fp0, "<pre>\n"); + + fprintf(fp0, "<em>%s</em>\n", gettext("You selected:")); + for (x = nhist + nhist_extra - 1; x >= 0; x--) { + /* + * The number of the document in the hist stack, its title in a link, + * and its address. - FM + */ + if (HDOC(x).title != NULL) { + StrAllocCopy(Title, HDOC(x).title); + LYEntify(&Title, TRUE); + LYTrimLeading(Title); + LYTrimTrailing(Title); + if (*Title == '\0') + StrAllocCopy(Title, NO_TITLE); + } else { + StrAllocCopy(Title, NO_TITLE); + } + fprintf(fp0, + "%s<em>%d</em>. <tab id=t%d><a href=\"%s%d\">%s</a>\n", + (x > 99 ? "" : x < 10 ? " " : " "), + x, x, STR_LYNXHIST, x, Title); + if (HDOC(x).address != NULL) { + StrAllocCopy(Title, HDOC(x).address); + LYEntify(&Title, TRUE); + } else { + StrAllocCopy(Title, gettext("(no address)")); + } + if (HDOC(x).internal_link) { + if (history[x].intern_seq_start == history[nhist - 1].intern_seq_start) + StrAllocCat(Title, gettext(" (internal)")); + else + StrAllocCat(Title, gettext(" (was internal)")); + } + fprintf(fp0, "<tab to=t%d>%s\n", x, Title); + } + fprintf(fp0, "</pre>\n"); + EndInternalPage(fp0); + + LYCloseTempFP(fp0); + FREE(Title); + return (0); +} + +/* + * This function makes the history page seem like any other type of file since + * more info is needed than can be provided by the normal link structure. We + * saved out the history number to a special URL. + * + * The info looks like: LYNXHIST:# + */ +BOOLEAN historytarget(DocInfo *newdoc) +{ + int number; + DocAddress WWWDoc; + HTParentAnchor *tmpanchor; + HText *text; + BOOLEAN treat_as_intern = FALSE; + + if ((!newdoc || !newdoc->address) || + strlen(newdoc->address) < 10 || !isdigit(UCH(*(newdoc->address + 9)))) + return (FALSE); + + if ((number = atoi(newdoc->address + 9)) > nhist + nhist_extra || number < 0) + return (FALSE); + + /* + * Optimization: assume we came from the History Page, + * so never return back - always a new version next time. + * But check first whether HTMainText is really the History + * Page document - in some obscure situations this may not be + * the case. If HTMainText seems to be a History Page document, + * also check that it really hasn't been pushed. - LP, kw + */ + if (HTMainText && nhist > 0 && + !strcmp(HTLoadedDocumentTitle(), HISTORY_PAGE_TITLE) && + LYIsUIPage3(HTLoadedDocumentURL(), UIP_HISTORY, 0) && + strcmp(HTLoadedDocumentURL(), HDOC(nhist - 1).address)) { + HTuncache_current_document(); /* don't waste the cache */ + } + + LYpop_num(number, newdoc); + if (((newdoc->internal_link && + history[number].intern_seq_start == history[nhist - 1].intern_seq_start) + || (number < nhist - 1 && + HDOC(nhist - 1).internal_link && + number == history[nhist - 1].intern_seq_start)) + && !(LYforce_no_cache == TRUE && LYoverride_no_cache == FALSE)) { + if (track_internal_links) { + LYforce_no_cache = FALSE; + LYinternal_flag = TRUE; + newdoc->internal_link = TRUE; + treat_as_intern = TRUE; + } + } else { + newdoc->internal_link = FALSE; + } + /* + * If we have POST content, and have LYresubmit_posts set or have no_cache + * set or do not still have the text cached, ask the user whether to + * resubmit the form. - FM + */ + if (newdoc->post_data != NULL) { + WWWDoc.address = newdoc->address; + WWWDoc.post_data = newdoc->post_data; + WWWDoc.post_content_type = newdoc->post_content_type; + WWWDoc.bookmark = newdoc->bookmark; + WWWDoc.isHEAD = newdoc->isHEAD; + WWWDoc.safe = newdoc->safe; + tmpanchor = HTAnchor_findAddress(&WWWDoc); + text = (HText *) HTAnchor_document(tmpanchor); + if (((((LYresubmit_posts == TRUE) || + (LYforce_no_cache == TRUE && + LYoverride_no_cache == FALSE)) && + !(treat_as_intern && !reloading)) || + text == NULL) && + (isLYNXIMGMAP(newdoc->address) || + HTConfirm(CONFIRM_POST_RESUBMISSION) == TRUE)) { + LYforce_no_cache = TRUE; + LYoverride_no_cache = FALSE; + } else if (text != NULL) { + LYforce_no_cache = FALSE; + LYoverride_no_cache = TRUE; + } else { + HTInfoMsg(CANCELLED); + return (FALSE); + } + } + + if (number != 0) + StrAllocCat(newdoc->title, gettext(" (From History)")); + return (TRUE); +} + +/* + * This procedure outputs the Visited Links list into a temporary file. - FM + * Returns links's number to make active (1-based), or 0 if not required. + */ +int LYShowVisitedLinks(char **newfile) +{ + static char tempfile[LY_MAXPATH] = "\0"; + char *Title = NULL; + char *Address = NULL; + int x, tot; + FILE *fp0; + VisitedLink *vl; + HTList *cur = Visited_Links; + int offset; + int ret = 0; + const char *arrow, *post_arrow; + + if (!cur) + return (-1); + + if ((fp0 = InternalPageFP(tempfile, TRUE)) == 0) + return (-1); + + LYLocalFileToURL(newfile, tempfile); + LYRegisterUIPage(*newfile, UIP_VLINKS); + + LYforce_HTML_mode = TRUE; /* force this file to be HTML */ + LYforce_no_cache = TRUE; /* force this file to be new */ + + BeginInternalPage(fp0, VISITED_LINKS_TITLE, VISITED_LINKS_HELP); + +#ifndef NO_OPTION_FORMS + fprintf(fp0, "<form action=\"%s\" method=\"post\">\n", STR_LYNXOPTIONS); + LYMenuVisitedLinks(fp0, FALSE); + fprintf(fp0, "<input type=\"submit\" value=\"Accept Changes\">\n"); + fprintf(fp0, "</form>\n"); + fprintf(fp0, "<P>\n"); +#endif + + fprintf(fp0, "<pre>\n"); + fprintf(fp0, "<em>%s</em>\n", + gettext("You visited (POSTs, bookmark, menu and list files excluded):")); + if (Visited_Links_As & VISITED_LINKS_REVERSE) + tot = x = HTList_count(Visited_Links); + else + tot = x = -1; + + if (Visited_Links_As & VISITED_LINKS_AS_TREE) { + vl = First_tree; + } else if (Visited_Links_As & VISITED_LINKS_AS_LATEST) { + if (Visited_Links_As & VISITED_LINKS_REVERSE) + vl = Latest_last.prev_latest; + else + vl = Latest_first.next_latest; + if (vl == &Latest_last || vl == &Latest_first) + vl = NULL; + } else { + if (Visited_Links_As & VISITED_LINKS_REVERSE) + vl = Last_by_first; + else + vl = (VisitedLink *) HTList_nextObject(cur); + } + while (NULL != vl) { + /* + * The number of the document (most recent highest), its title in a + * link, and its address. - FM + */ + post_arrow = arrow = ""; + if (Visited_Links_As & VISITED_LINKS_REVERSE) + x--; + else + x++; + if (vl == PrevActiveVisitedLink) { + if (Visited_Links_As & VISITED_LINKS_REVERSE) + ret = tot - x + 2; + else + ret = x + 3; + } + if (vl == PrevActiveVisitedLink) { + post_arrow = "<A NAME=current></A>"; + /* Otherwise levels 0 and 1 look the same when with arrow: */ + arrow = (vl->level && (Visited_Links_As & VISITED_LINKS_AS_TREE)) + ? "==>" : "=>"; + StrAllocCat(*newfile, "#current"); + } + if (Visited_Links_As & VISITED_LINKS_AS_TREE) { + offset = 2 * vl->level; + if (offset > 24) + offset = (offset + 24) / 2; + if (offset > LYcols * 3 / 4) + offset = LYcols * 3 / 4; + } else + offset = (x > 99 ? 0 : x < 10 ? 2 : 1); + if (non_empty(vl->title)) { + StrAllocCopy(Title, vl->title); + LYEntify(&Title, TRUE); + LYTrimLeading(Title); + LYTrimTrailing(Title); + if (*Title == '\0') + StrAllocCopy(Title, NO_TITLE); + } else { + StrAllocCopy(Title, NO_TITLE); + } + if (non_empty(vl->address)) { + StrAllocCopy(Address, vl->address); + LYEntify(&Address, FALSE); + fprintf(fp0, + "%-*s%s<em>%d</em>. <tab id=t%d><a href=\"%s\">%s</a>\n", + offset, arrow, post_arrow, + x, x, Address, Title); + } else { + fprintf(fp0, + "%-*s%s<em>%d</em>. <tab id=t%d><em>%s</em>\n", + offset, arrow, post_arrow, + x, x, Title); + } + if (Address != NULL) { + StrAllocCopy(Address, vl->address); + LYEntify(&Address, TRUE); + } + fprintf(fp0, "<tab to=t%d>%s\n", x, + ((Address != NULL) ? Address : gettext("(no address)"))); + if (Visited_Links_As & VISITED_LINKS_AS_TREE) + vl = vl->next_tree; + else if (Visited_Links_As & VISITED_LINKS_AS_LATEST) { + if (Visited_Links_As & VISITED_LINKS_REVERSE) + vl = vl->prev_latest; + else + vl = vl->next_latest; + if (vl == &Latest_last || vl == &Latest_first) + vl = NULL; + } else { + if (Visited_Links_As & VISITED_LINKS_REVERSE) + vl = vl->prev_first; + else + vl = (VisitedLink *) HTList_nextObject(cur); + } + } + fprintf(fp0, "</pre>\n"); + EndInternalPage(fp0); + + LYCloseTempFP(fp0); + FREE(Title); + FREE(Address); + return (ret); +} + +/* + * Keep cycled buffer for statusline messages. + * But allow user to change how big it will be from userdefs.h + */ +#ifndef STATUSBUFSIZE +#define STATUSBUFSIZE 40 +#endif + +int status_buf_size = STATUSBUFSIZE; + +static char **buffstack; +static int topOfStack = 0; + +#ifdef LY_FIND_LEAKS +static void free_messages_stack(void) +{ + if (buffstack != 0) { + topOfStack = status_buf_size; + + while (--topOfStack >= 0) { + FREE(buffstack[topOfStack]); + } + FREE(buffstack); + } +} +#endif + +static void to_stack(char *str) +{ + /* + * Cycle buffer: + */ + if (topOfStack >= status_buf_size) { + topOfStack = 0; + } + + /* + * Register string. + */ + if (buffstack == 0) + buffstack = typecallocn(char *, (size_t) status_buf_size); + + FREE(buffstack[topOfStack]); + buffstack[topOfStack] = str; + topOfStack++; +#ifdef LY_FIND_LEAKS + if (!already_registered_free_messages_stack) { + already_registered_free_messages_stack = 1; + atexit(free_messages_stack); + } +#endif + if (topOfStack >= status_buf_size) { + topOfStack = 0; + } +} + +/* + * Dump statusline messages into the buffer. + * Called from mainloop() when exit immediately with an error: + * can not access startfile (first_file) so a couple of alert messages + * will be very useful on exit. + * (Don't expect everyone will look a trace log in case of difficulties:)) + */ +void LYstatusline_messages_on_exit(char **buf) +{ + int i; + + if (buffstack != 0) { + StrAllocCat(*buf, "\n"); + /* print messages in chronological order: + * probably a single message but let's do it. + */ + i = topOfStack - 1; + while (++i < status_buf_size) { + if (buffstack[i] != NULL) { + StrAllocCat(*buf, buffstack[i]); + StrAllocCat(*buf, "\n"); + } + } + i = -1; + while (++i < topOfStack) { + if (buffstack[i] != NULL) { + StrAllocCat(*buf, buffstack[i]); + StrAllocCat(*buf, "\n"); + } + } + } +} + +void LYstore_message2(const char *message, + const char *argument) +{ + + if (message != NULL) { + char *temp = NULL; + + HTSprintf0(&temp, message, NonNull(argument)); + to_stack(temp); + } +} + +void LYstore_message(const char *message) +{ + if (message != NULL) { + char *temp = NULL; + + StrAllocCopy(temp, message); + to_stack(temp); + } +} + +/* LYLoadMESSAGES + * -------------- + * Create a text/html stream with a list of recent statusline messages. + * LYNXMESSAGES:/ internal page. + * [implementation based on LYLoadKeymap()]. + */ + +static int LYLoadMESSAGES(const char *arg GCC_UNUSED, + HTParentAnchor *anAnchor, + HTFormat format_out, + HTStream *sink) +{ + HTFormat format_in = WWW_HTML; + HTStream *target = NULL; + char *buf = NULL; + int nummsg = 0; + + int i; + char *temp = NULL; + + if (buffstack != 0) { + i = status_buf_size; + while (--i >= 0) { + if (buffstack[i] != NULL) + nummsg++; + } + } + + /* + * Set up the stream. - FM + */ + target = HTStreamStack(format_in, format_out, sink, anAnchor); + + if (target == NULL) { + HTSprintf0(&buf, CANNOT_CONVERT_I_TO_O, + HTAtom_name(format_in), HTAtom_name(format_out)); + HTAlert(buf); + FREE(buf); + return (HT_NOT_LOADED); + } + anAnchor->no_cache = TRUE; + +#define PUTS(buf) (*target->isa->put_block)(target, buf, (int) strlen(buf)) + + HTSprintf0(&buf, "<html>\n<head>\n"); + PUTS(buf); + /* + * This page is a list of messages in display character set. + */ + HTSprintf0(&buf, "<META %s content=\"" STR_HTML ";charset=%s\">\n", + "http-equiv=\"content-type\"", + LYCharSet_UC[current_char_set].MIMEname); + PUTS(buf); + HTSprintf0(&buf, "<title>%s</title>\n</head>\n<body>\n", + STATUSLINES_TITLE); + PUTS(buf); + + if (nummsg != 0) { + HTSprintf0(&buf, "<ol>\n"); + PUTS(buf); + /* print messages in reverse order: */ + i = topOfStack; + while (--i >= 0) { + if (buffstack[i] != NULL) { + StrAllocCopy(temp, buffstack[i]); + LYEntify(&temp, TRUE); + HTSprintf0(&buf, "<li value=%d> <em>%s</em>\n", nummsg, temp); + nummsg--; + PUTS(buf); + } + } + i = status_buf_size; + while (--i >= topOfStack) { + if (buffstack[i] != NULL) { + StrAllocCopy(temp, buffstack[i]); + LYEntify(&temp, TRUE); + HTSprintf0(&buf, "<li value=%d> <em>%s</em>\n", nummsg, temp); + nummsg--; + PUTS(buf); + } + } + FREE(temp); + HTSprintf0(&buf, "</ol>\n</body>\n</html>\n"); + } else { + HTSprintf0(&buf, "<p>%s\n</body>\n</html>\n", + gettext("(No messages yet)")); + } + PUTS(buf); + + (*target->isa->_free) (target); + FREE(buf); + return (HT_LOADED); +} + +#ifdef GLOBALDEF_IS_MACRO +#define _LYMESSAGES_C_GLOBALDEF_1_INIT { "LYNXMESSAGES", LYLoadMESSAGES, 0} +GLOBALDEF(HTProtocol, LYLynxStatusMessages, _LYMESSAGES_C_GLOBALDEF_1_INIT); +#else +GLOBALDEF HTProtocol LYLynxStatusMessages = +{"LYNXMESSAGES", LYLoadMESSAGES, 0}; +#endif /* GLOBALDEF_IS_MACRO */ |