Blame src/LYJump.c

Packit f574b8
/*
Packit f574b8
 * $LynxId: LYJump.c,v 1.52 2018/03/18 19:17:00 tom Exp $
Packit f574b8
 */
Packit f574b8
#include <HTUtils.h>
Packit f574b8
#include <HTAlert.h>
Packit f574b8
#include <LYUtils.h>
Packit f574b8
#include <LYStrings.h>
Packit f574b8
#include <LYGlobalDefs.h>
Packit f574b8
#include <LYJump.h>
Packit f574b8
#include <LYKeymap.h>
Packit f574b8
#include <GridText.h>
Packit f574b8
Packit f574b8
#include <LYLeaks.h>
Packit f574b8
Packit f574b8
#ifdef _WINDOWS
Packit f574b8
#include <stdlib.h>		/* bsearch() */
Packit f574b8
#endif
Packit f574b8
Packit f574b8
#ifdef VMS
Packit f574b8
#include <fab.h>
Packit f574b8
#endif /* VMS */
Packit f574b8
Packit f574b8
struct JumpTable *JThead = NULL;
Packit f574b8
Packit f574b8
static int LYCompare(const void *e1, const void *e2);
Packit f574b8
static unsigned LYRead_Jumpfile(struct JumpTable *jtp);
Packit f574b8
Packit f574b8
void LYJumpTable_free(void)
Packit f574b8
{
Packit f574b8
    struct JumpTable *cur = JThead;
Packit f574b8
    struct JumpTable *next;
Packit f574b8
Packit f574b8
    while (cur) {
Packit f574b8
	next = cur->next;
Packit f574b8
	FREE(cur->msg);
Packit f574b8
	FREE(cur->file);
Packit f574b8
	FREE(cur->shortcut);
Packit f574b8
	if (cur->history) {
Packit f574b8
	    LYFreeStringList(cur->history);
Packit f574b8
	    cur->history = NULL;
Packit f574b8
	}
Packit f574b8
	FREE(cur->table);
Packit f574b8
	FREE(cur->mp);
Packit f574b8
	FREE(cur);
Packit f574b8
	cur = next;
Packit f574b8
    }
Packit f574b8
    JThead = NULL;
Packit f574b8
    return;
Packit f574b8
}
Packit f574b8
Packit f574b8
/*
Packit f574b8
 * Utility for listing shortcuts, making any repeated
Packit f574b8
 * shortcut the most current in the list. - FM
Packit f574b8
 */
Packit f574b8
void LYAddJumpShortcut(HTList *historyp, char *shortcut)
Packit f574b8
{
Packit f574b8
    char *tmp = NULL;
Packit f574b8
    char *old;
Packit f574b8
    HTList *cur = historyp;
Packit f574b8
Packit f574b8
    if (!historyp || isEmpty(shortcut))
Packit f574b8
	return;
Packit f574b8
Packit f574b8
    StrAllocCopy(tmp, shortcut);
Packit f574b8
Packit f574b8
    while (NULL != (old = (char *) HTList_nextObject(cur))) {
Packit f574b8
	if (!strcmp(old, tmp)) {
Packit f574b8
	    HTList_removeObject(historyp, old);
Packit f574b8
	    FREE(old);
Packit f574b8
	    break;
Packit f574b8
	}
Packit f574b8
    }
Packit f574b8
    HTList_addObject(historyp, tmp);
Packit f574b8
Packit f574b8
    return;
Packit f574b8
}
Packit f574b8
Packit f574b8
BOOL LYJumpInit(char *config)
Packit f574b8
{
Packit f574b8
    struct JumpTable *jtp;
Packit f574b8
    char *cp;
Packit f574b8
Packit f574b8
    /*
Packit f574b8
     * Create a JumpTable structure.
Packit f574b8
     */
Packit f574b8
    jtp = typecalloc(struct JumpTable);
Packit f574b8
Packit f574b8
    if (jtp == NULL) {
Packit f574b8
	outofmem(__FILE__, "LYJumpInit");
Packit f574b8
    }
Packit f574b8
Packit f574b8
    /*
Packit f574b8
     * config is JUMPFILE:path[:optional_key[:optional_prompt]]
Packit f574b8
     *
Packit f574b8
     * Skip JUMPFILE.
Packit f574b8
     */
Packit f574b8
    cp = strtok(config, ":\n");
Packit f574b8
    if (!cp) {
Packit f574b8
	FREE(jtp);
Packit f574b8
	return FALSE;
Packit f574b8
    }
Packit f574b8
Packit f574b8
    /*
Packit f574b8
     * Get the path.
Packit f574b8
     */
Packit f574b8
    cp = strtok(NULL, ":\n");
Packit f574b8
    if (!cp) {
Packit f574b8
	FREE(jtp);
Packit f574b8
	return FALSE;
Packit f574b8
    }
Packit f574b8
    StrAllocCopy(jtp->file, cp);
Packit f574b8
#ifdef LY_FIND_LEAKS
Packit f574b8
    if (!JThead)
Packit f574b8
	atexit(LYJumpTable_free);
Packit f574b8
#endif /* LY_FIND_LEAKS */
Packit f574b8
Packit f574b8
    /*
Packit f574b8
     * Get the key, if present.
Packit f574b8
     */
Packit f574b8
    cp = strtok(NULL, ":\n");
Packit f574b8
Packit f574b8
    /*
Packit f574b8
     * If no key, check whether we are resetting the default jumps file.
Packit f574b8
     */
Packit f574b8
    if (!cp && JThead) {
Packit f574b8
	struct JumpTable *jtptmp = JThead;
Packit f574b8
Packit f574b8
	jumpfile = jtp->file;
Packit f574b8
	FREE(jtp);
Packit f574b8
	while (jtptmp && jtptmp->key)
Packit f574b8
	    jtptmp = jtptmp->next;
Packit f574b8
	if (!jtptmp)
Packit f574b8
	    return FALSE;
Packit f574b8
	StrAllocCopy(jtptmp->file, jumpfile);
Packit f574b8
	StrAllocCopy(jtptmp->msg, jumpprompt);
Packit f574b8
	return TRUE;
Packit f574b8
    }
Packit f574b8
Packit f574b8
    /*
Packit f574b8
     * If a key is present and we have no default, create one,
Packit f574b8
     * using the path from config, and the current jumpprompt.
Packit f574b8
     */
Packit f574b8
    if (cp && !JThead) {
Packit f574b8
	JThead = jtp;
Packit f574b8
	StrAllocCopy(JThead->msg, jumpprompt);
Packit f574b8
	if (isEmpty(jumpfile))
Packit f574b8
	    StrAllocCopy(jumpfile, JThead->file);
Packit f574b8
	jtp = typecalloc(struct JumpTable);
Packit f574b8
Packit f574b8
	if (jtp == NULL) {
Packit f574b8
	    outofmem(__FILE__, "LYJumpInit");
Packit f574b8
	}
Packit f574b8
Packit f574b8
	StrAllocCopy(jtp->file, JThead->file);
Packit f574b8
    }
Packit f574b8
Packit f574b8
    /*
Packit f574b8
     * Complete the initialization of config.
Packit f574b8
     */
Packit f574b8
    if (cp) {
Packit f574b8
	jtp->key = remap(cp, "JUMP", FALSE);	/* key is present, (re)map it */
Packit f574b8
	cp = strtok(NULL, "\n");	/* get prompt, if present */
Packit f574b8
	if (non_empty(cp))
Packit f574b8
	    StrAllocCopy(jtp->msg, cp);		/* prompt is present, load it */
Packit f574b8
	else
Packit f574b8
	    cp = NULL;
Packit f574b8
    }
Packit f574b8
    if (!cp)			/* no prompt, use default */
Packit f574b8
	StrAllocCopy(jtp->msg, jumpprompt);
Packit f574b8
    if (jtp->msg[strlen(jtp->msg) - 1] != ' ')	/* ensure a trailing space */
Packit f574b8
	StrAllocCat(jtp->msg, " ");
Packit f574b8
    jtp->history = HTList_new();
Packit f574b8
    jtp->next = JThead;
Packit f574b8
    JThead = jtp;
Packit f574b8
    return TRUE;
Packit f574b8
}
Packit f574b8
Packit f574b8
char *LYJump(int key)
Packit f574b8
{
Packit f574b8
    static bstring *buf = NULL;
Packit f574b8
Packit f574b8
    JumpDatum seeking;
Packit f574b8
    JumpDatum *found;
Packit f574b8
    char *bp, *cp;
Packit f574b8
    struct JumpTable *jtp;
Packit f574b8
    int ch;
Packit f574b8
    RecallType recall;
Packit f574b8
    int ShortcutTotal;
Packit f574b8
    int ShortcutNum;
Packit f574b8
    BOOLEAN FirstShortcutRecall = TRUE;
Packit f574b8
Packit f574b8
    if (!JThead)
Packit f574b8
	return NULL;
Packit f574b8
    jtp = JThead;
Packit f574b8
    while (jtp && jtp->key && jtp->key != key)
Packit f574b8
	jtp = jtp->next;
Packit f574b8
    if (!jtp) {
Packit f574b8
	char *msg = 0;
Packit f574b8
Packit f574b8
	HTSprintf0(&msg, KEY_NOT_MAPPED_TO_JUMP_FILE, key);
Packit f574b8
	HTAlert(msg);
Packit f574b8
	FREE(msg);
Packit f574b8
	return NULL;
Packit f574b8
    }
Packit f574b8
    if (!jtp->table)
Packit f574b8
	jtp->nel = LYRead_Jumpfile(jtp);
Packit f574b8
    if (jtp->nel == 0)
Packit f574b8
	return NULL;
Packit f574b8
Packit f574b8
    if (!jump_buffer || isEmpty(jtp->shortcut)) {
Packit f574b8
	BStrCopy0(buf, "");
Packit f574b8
    } else if (non_empty(jtp->shortcut)) {
Packit f574b8
	size_t len = (size_t) BStrLen(buf);
Packit f574b8
Packit f574b8
	if (strlen(jtp->shortcut) > len) {
Packit f574b8
	    jtp->shortcut[len] = '\0';
Packit f574b8
	    BStrCopy0(buf, jtp->shortcut);
Packit f574b8
	}
Packit f574b8
    }
Packit f574b8
Packit f574b8
    ShortcutTotal = (jtp->history ? HTList_count(jtp->history) : 0);
Packit f574b8
    if (jump_buffer && !isBEmpty(buf)) {
Packit f574b8
	recall = ((ShortcutTotal > 1) ? RECALL_URL : NORECALL);
Packit f574b8
	ShortcutNum = 0;
Packit f574b8
	FirstShortcutRecall = FALSE;
Packit f574b8
    } else {
Packit f574b8
	recall = ((ShortcutTotal >= 1) ? RECALL_URL : NORECALL);
Packit f574b8
	ShortcutNum = ShortcutTotal;
Packit f574b8
	FirstShortcutRecall = TRUE;
Packit f574b8
    }
Packit f574b8
Packit f574b8
    statusline(jtp->msg);
Packit f574b8
    if ((ch = LYgetBString(&buf, FALSE, 0, recall)) < 0) {
Packit f574b8
	/*
Packit f574b8
	 * User cancelled the Jump via ^G. - FM
Packit f574b8
	 */
Packit f574b8
	HTInfoMsg(CANCELLED);
Packit f574b8
	return NULL;
Packit f574b8
    }
Packit f574b8
Packit f574b8
  check_recall:
Packit f574b8
    bp = buf->str;
Packit f574b8
    if (TOUPPER(key) == 'G' && StrNCmp(buf->str, "o ", 2) == 0)
Packit f574b8
	bp++;
Packit f574b8
    bp = LYSkipBlanks(bp);
Packit f574b8
    if (*bp == '\0' &&
Packit f574b8
	!(recall && (ch == UPARROW_KEY || ch == DNARROW_KEY))) {
Packit f574b8
	/*
Packit f574b8
	 * User cancelled the Jump via a zero-length string. - FM
Packit f574b8
	 */
Packit f574b8
	BStrCopy0(buf, "");
Packit f574b8
	StrAllocCopy(jtp->shortcut, buf->str);
Packit f574b8
	HTInfoMsg(CANCELLED);
Packit f574b8
	return NULL;
Packit f574b8
    }
Packit f574b8
#ifdef PERMIT_GOTO_FROM_JUMP
Packit f574b8
    if (StrChr(bp, ':') || StrChr(bp, '/')) {
Packit f574b8
	char *temp = NULL;
Packit f574b8
Packit f574b8
	LYJumpFileURL = FALSE;
Packit f574b8
	if (no_goto) {
Packit f574b8
	    BStrCopy0(buf, "");
Packit f574b8
	    StrAllocCopy(jtp->shortcut, buf->str);
Packit f574b8
	    HTUserMsg(RANDOM_URL_DISALLOWED);
Packit f574b8
	    return NULL;
Packit f574b8
	}
Packit f574b8
	HTSprintf0(&temp, "Go %s", bp);
Packit f574b8
	BStrCopy0(buf, temp);
Packit f574b8
	FREE(temp);
Packit f574b8
	return (bp = buf->str);
Packit f574b8
    }
Packit f574b8
#endif /* PERMIT_GOTO_FROM_JUMP */
Packit f574b8
Packit f574b8
    if (recall && ch == UPARROW_KEY) {
Packit f574b8
	if (FirstShortcutRecall) {
Packit f574b8
	    /*
Packit f574b8
	     * Use last Shortcut in the list. - FM
Packit f574b8
	     */
Packit f574b8
	    FirstShortcutRecall = FALSE;
Packit f574b8
	    ShortcutNum = 0;
Packit f574b8
	} else {
Packit f574b8
	    /*
Packit f574b8
	     * Go back to the previous Shortcut in the list. - FM
Packit f574b8
	     */
Packit f574b8
	    ShortcutNum++;
Packit f574b8
	}
Packit f574b8
	if (ShortcutNum >= ShortcutTotal)
Packit f574b8
	    /*
Packit f574b8
	     * Roll around to the last Shortcut in the list. - FM
Packit f574b8
	     */
Packit f574b8
	    ShortcutNum = 0;
Packit f574b8
	if ((cp = (char *) HTList_objectAt(jtp->history,
Packit f574b8
					   ShortcutNum)) != NULL) {
Packit f574b8
	    BStrCopy0(buf, cp);
Packit f574b8
	    if (jump_buffer && jtp->shortcut &&
Packit f574b8
		!strcmp(buf->str, jtp->shortcut)) {
Packit f574b8
		_statusline(EDIT_CURRENT_SHORTCUT);
Packit f574b8
	    } else if ((jump_buffer && ShortcutTotal == 2) ||
Packit f574b8
		       (!jump_buffer && ShortcutTotal == 1)) {
Packit f574b8
		_statusline(EDIT_THE_PREV_SHORTCUT);
Packit f574b8
	    } else {
Packit f574b8
		_statusline(EDIT_A_PREV_SHORTCUT);
Packit f574b8
	    }
Packit f574b8
	    if ((ch = LYgetBString(&buf, FALSE, 0, recall)) < 0) {
Packit f574b8
		/*
Packit f574b8
		 * User cancelled the jump via ^G.
Packit f574b8
		 */
Packit f574b8
		HTInfoMsg(CANCELLED);
Packit f574b8
		return NULL;
Packit f574b8
	    }
Packit f574b8
	    goto check_recall;
Packit f574b8
	}
Packit f574b8
    } else if (recall && ch == DNARROW_KEY) {
Packit f574b8
	if (FirstShortcutRecall) {
Packit f574b8
	    /*
Packit f574b8
	     * Use the first Shortcut in the list. - FM
Packit f574b8
	     */
Packit f574b8
	    FirstShortcutRecall = FALSE;
Packit f574b8
	    ShortcutNum = ShortcutTotal - 1;
Packit f574b8
	} else {
Packit f574b8
	    /*
Packit f574b8
	     * Advance to the next Shortcut in the list. - FM
Packit f574b8
	     */
Packit f574b8
	    ShortcutNum--;
Packit f574b8
	}
Packit f574b8
	if (ShortcutNum < 0)
Packit f574b8
	    /*
Packit f574b8
	     * Roll around to the first Shortcut in the list. - FM
Packit f574b8
	     */
Packit f574b8
	    ShortcutNum = ShortcutTotal - 1;
Packit f574b8
	if ((cp = (char *) HTList_objectAt(jtp->history,
Packit f574b8
					   ShortcutNum)) != NULL) {
Packit f574b8
	    BStrCopy0(buf, cp);
Packit f574b8
	    if (jump_buffer && jtp->shortcut &&
Packit f574b8
		!strcmp(buf->str, jtp->shortcut)) {
Packit f574b8
		_statusline(EDIT_CURRENT_SHORTCUT);
Packit f574b8
	    } else if ((jump_buffer && ShortcutTotal == 2) ||
Packit f574b8
		       (!jump_buffer && ShortcutTotal == 1)) {
Packit f574b8
		_statusline(EDIT_THE_PREV_SHORTCUT);
Packit f574b8
	    } else {
Packit f574b8
		_statusline(EDIT_A_PREV_SHORTCUT);
Packit f574b8
	    }
Packit f574b8
	    if ((ch = LYgetBString(&buf, FALSE, 0, recall)) < 0) {
Packit f574b8
		/*
Packit f574b8
		 * User cancelled the jump via ^G.
Packit f574b8
		 */
Packit f574b8
		HTInfoMsg(CANCELLED);
Packit f574b8
		return NULL;
Packit f574b8
	    }
Packit f574b8
	    goto check_recall;
Packit f574b8
	}
Packit f574b8
    }
Packit f574b8
Packit f574b8
    seeking.key = bp;
Packit f574b8
    found = (JumpDatum *) bsearch((char *) &seeking, (char *) jtp->table,
Packit f574b8
				  (size_t) jtp->nel, sizeof(JumpDatum), LYCompare);
Packit f574b8
    if (!found) {
Packit f574b8
	user_message("Unknown target '%s'", buf->str);
Packit f574b8
	LYSleepAlert();
Packit f574b8
    }
Packit f574b8
Packit f574b8
    StrAllocCopy(jtp->shortcut, bp);
Packit f574b8
    LYAddJumpShortcut(jtp->history, jtp->shortcut);
Packit f574b8
    return found ? found->url : NULL;
Packit f574b8
}
Packit f574b8
Packit f574b8
static unsigned LYRead_Jumpfile(struct JumpTable *jtp)
Packit f574b8
{
Packit f574b8
    struct stat st;
Packit f574b8
    unsigned int nel;
Packit f574b8
    char *mp;
Packit f574b8
    int fd;
Packit f574b8
Packit f574b8
#ifdef VMS
Packit f574b8
    int blocksize = 1024;
Packit f574b8
    FILE *fp;
Packit f574b8
    BOOL IsStream_LF = TRUE;
Packit f574b8
#endif /* VMS */
Packit f574b8
    char *cp;
Packit f574b8
    unsigned i;
Packit f574b8
Packit f574b8
    if (isEmpty(jtp->file))
Packit f574b8
	return 0;
Packit f574b8
Packit f574b8
    CTRACE((tfp, "Read Jumpfile %s\n", jtp->file));
Packit f574b8
    if (stat(jtp->file, &st) < 0) {
Packit f574b8
	HTAlert(CANNOT_LOCATE_JUMP_FILE);
Packit f574b8
	return 0;
Packit f574b8
    }
Packit f574b8
Packit f574b8
    /* allocate storage to read entire file */
Packit f574b8
    if ((mp = typecallocn(char, (size_t) st.st_size + 1)) == NULL) {
Packit f574b8
	HTAlert(OUTOF_MEM_FOR_JUMP_FILE);
Packit f574b8
	return 0;
Packit f574b8
    }
Packit f574b8
#ifdef VMS
Packit f574b8
    if (st.st_fab_rfm != (char) FAB$C_STMLF) {
Packit f574b8
	/** It's a record-oriented file. **/
Packit f574b8
	IsStream_LF = FALSE;
Packit f574b8
	if ((fp = fopen(jtp->file, "r", "mbc=32")) == NULL) {
Packit f574b8
	    HTAlert(CANNOT_OPEN_JUMP_FILE);
Packit f574b8
	    FREE(mp);
Packit f574b8
	    return 0;
Packit f574b8
	}
Packit f574b8
    } else if ((fd = open(jtp->file, O_RDONLY, "mbc=32")) < 0)
Packit f574b8
#else
Packit f574b8
    if ((fd = open(jtp->file, O_RDONLY)) < 0)
Packit f574b8
#endif /* VMS */
Packit f574b8
    {
Packit f574b8
	HTAlert(CANNOT_OPEN_JUMP_FILE);
Packit f574b8
	FREE(mp);
Packit f574b8
	return 0;
Packit f574b8
    }
Packit f574b8
#ifdef VMS
Packit f574b8
    if (IsStream_LF) {
Packit f574b8
    /** Handle as a stream. **/
Packit f574b8
#endif /* VMS */
Packit f574b8
	if (read(fd, mp, (size_t) st.st_size) != st.st_size) {
Packit f574b8
	    HTAlert(ERROR_READING_JUMP_FILE);
Packit f574b8
	    FREE(mp);
Packit f574b8
	    close(fd);
Packit f574b8
	    return 0;
Packit f574b8
	}
Packit f574b8
	mp[st.st_size] = '\0';
Packit f574b8
	close(fd);
Packit f574b8
#ifdef VMS
Packit f574b8
    } else {
Packit f574b8
	/** Handle as a series of records. **/
Packit f574b8
	if (fgets(mp, blocksize, fp) == NULL) {
Packit f574b8
	    HTAlert(ERROR_READING_JUMP_FILE);
Packit f574b8
	    FREE(mp);
Packit f574b8
	    close(fd);
Packit f574b8
	    return 0;
Packit f574b8
	} else {
Packit f574b8
	    while (fgets(mp + strlen(mp), blocksize, fp) != NULL) {
Packit f574b8
		;
Packit f574b8
	    }
Packit f574b8
	}
Packit f574b8
	LYCloseInput(fp);
Packit f574b8
	close(fd);
Packit f574b8
    }
Packit f574b8
#endif /* VMS */
Packit f574b8
Packit f574b8
    /* quick scan for approximate number of entries */
Packit f574b8
    nel = 0;
Packit f574b8
    cp = mp;
Packit f574b8
    while ((cp = StrChr(cp, '\n')) != NULL) {
Packit f574b8
	nel++;
Packit f574b8
	cp++;
Packit f574b8
    }
Packit f574b8
Packit f574b8
    jtp->table = (JumpDatum *) malloc((nel + 1) * sizeof(JumpDatum));
Packit f574b8
    if (jtp->table == NULL) {
Packit f574b8
	HTAlert(OUTOF_MEM_FOR_JUMP_TABLE);
Packit f574b8
	FREE(mp);
Packit f574b8
	return 0;
Packit f574b8
    }
Packit f574b8
Packit f574b8
    cp = jtp->mp = mp;
Packit f574b8
    for (i = 0; i < nel;) {
Packit f574b8
	if (StrNCmp(cp, "
Packit f574b8
	    cp = StrChr(cp, '\n');
Packit f574b8
	    if (cp == NULL)
Packit f574b8
		break;
Packit f574b8
	    cp++;
Packit f574b8
	    continue;
Packit f574b8
	}
Packit f574b8
	cp = LYstrstr(cp, "
");
Packit f574b8
	if (cp == NULL)
Packit f574b8
	    break;
Packit f574b8
	cp += 4;
Packit f574b8
	jtp->table[i].key = cp;
Packit f574b8
	cp = LYstrstr(cp, "
");
Packit f574b8
	if (cp == NULL)
Packit f574b8
	    break;
Packit f574b8
	*cp = '\0';
Packit f574b8
	cp += 4;
Packit f574b8
	cp = LYstrstr(cp, "href=\"");
Packit f574b8
	if (cp == NULL)
Packit f574b8
	    break;
Packit f574b8
	cp += 6;
Packit f574b8
	jtp->table[i].url = cp;
Packit f574b8
	cp = StrChr(cp, '"');
Packit f574b8
	if (cp == NULL)
Packit f574b8
	    break;
Packit f574b8
	*cp = '\0';
Packit f574b8
	cp++;
Packit f574b8
	cp = StrChr(cp, '\n');
Packit f574b8
	if (cp == NULL)
Packit f574b8
	    break;
Packit f574b8
	cp++;
Packit f574b8
	CTRACE((tfp, "Read jumpfile[%u] key='%s', url='%s'\n",
Packit f574b8
		i, jtp->table[i].key, jtp->table[i].url));
Packit f574b8
	i++;
Packit f574b8
    }
Packit f574b8
Packit f574b8
    return i;
Packit f574b8
}
Packit f574b8
Packit f574b8
static int LYCompare(const void *e1, const void *e2)
Packit f574b8
{
Packit f574b8
    return strcasecomp(((const JumpDatum *) e1)->key,
Packit f574b8
		       ((const JumpDatum *) e2)->key);
Packit f574b8
}