#include "HTUtils.h" #include "tcp.h" #include "HTAlert.h" #include "LYUtils.h" #include "LYStrings.h" #include "LYGlobalDefs.h" #include "LYJump.h" #include "LYKeymap.h" #include "LYSignal.h" #include "GridText.h" #include "LYLeaks.h" #ifdef VMS #include #endif /* VMS */ #define FREE(x) if (x) {free(x); x = NULL;} struct JumpTable *JThead = NULL; PRIVATE int LYCompare PARAMS ((CONST void *e1, CONST void *e2)); PRIVATE unsigned LYRead_Jumpfile PARAMS ((struct JumpTable *jtp)); PUBLIC void LYJumpTable_free NOARGS { struct JumpTable *cur = JThead; struct JumpTable *next; while (cur) { next = cur->next; FREE(cur->msg); FREE(cur->file); FREE(cur->shortcut); if (cur->history) { char *shortcut; HTList *current = cur->history; while (NULL != (shortcut = (char *)HTList_nextObject(current))) { FREE(shortcut); } HTList_delete(cur->history); cur->history = NULL; } FREE(cur->table); FREE(cur->mp); FREE(cur); cur = next; } JThead = NULL; return; } /* * Utility for listing shortcuts, making any repeated * shortcut the most current in the list. - FM */ PUBLIC void LYAddJumpShortcut ARGS2(HTList *,history, char *,shortcut) { char *new; char *old; HTList *cur = history; if (!history || !(shortcut && *shortcut)) return; if ((new = (char *)calloc(1, (strlen(shortcut) + 1))) == NULL) outofmem(__FILE__, "HTAddJumpShortcut"); strcpy(new, shortcut); while (NULL != (old = (char *)HTList_nextObject(cur))) { if (!strcmp(old, new)) { HTList_removeObject(history, old); FREE(old); break; } } HTList_addObject(history, new); return; } PUBLIC BOOL LYJumpInit ARGS1 (char *, config) { struct JumpTable *jtp; char *cp; /* * Create a JumpTable structure. */ jtp = (struct JumpTable *) calloc(1, sizeof(*jtp)); if (jtp == NULL) { perror("Out of memory in LYJumpInit"); return FALSE; } /* * config is JUMPFILE:path[:optional_key[:optional_prompt]] * * Skip JUMPFILE. */ cp = strtok(config, ":\n"); if (!cp) { FREE(jtp); return FALSE; } /* * Get the path. */ cp = strtok(NULL, ":\n"); if (!cp) { FREE(jtp); return FALSE; } StrAllocCopy(jtp->file, cp); if (!JThead) atexit(LYJumpTable_free); /* * Get the key, if present. */ cp = strtok(NULL, ":\n"); /* * If no key, check whether we are resetting the default jumps file. */ if (!cp && JThead) { struct JumpTable *jtptmp = JThead; jumpfile = jtp->file; FREE(jtp); while (jtptmp && jtptmp->key) jtptmp = jtptmp->next; if (!jtptmp) return FALSE; StrAllocCopy(jtptmp->file, jumpfile); StrAllocCopy(jtptmp->msg, jumpprompt); return TRUE; } /* * If a key is present and we have no default, create one, * using the path from config, and the current jumpprompt. */ if (cp && !JThead) { JThead = jtp; StrAllocCopy(JThead->msg, jumpprompt); if (!jumpfile) StrAllocCopy(jumpfile, JThead->file); jtp = (struct JumpTable *) calloc(1, sizeof(*jtp)); if (jtp == NULL) { perror("Out of memory in LYJumpInit"); return FALSE; } StrAllocCopy(jtp->file, JThead->file); } /* * Complete the initialization of config. */ if (cp) { jtp->key = remap(cp, "JUMP"); /* key is present, (re)map it */ cp = strtok(NULL, "\n"); /* get prompt, if present */ if (cp && *cp) StrAllocCopy(jtp->msg, cp); /* prompt is present, load it */ else cp = NULL; } if (!cp) /* no prompt, use default */ StrAllocCopy(jtp->msg, jumpprompt); if (jtp->msg[strlen(jtp->msg)-1] != ' ') /* ensure a trailing space */ StrAllocCat(jtp->msg, " "); jtp->history = HTList_new(); jtp->next = JThead; JThead = jtp; return TRUE; } PUBLIC char *LYJump ARGS1(int, key) { JumpDatum seeking; JumpDatum *found; static char buf[124]; char *bp, *cp; struct JumpTable *jtp; int ch, recall; int ShortcutTotal; int ShortcutNum; BOOLEAN FirstShortcutRecall = TRUE; if (!JThead) return NULL; jtp = JThead; while (jtp && jtp->key && jtp->key != key) jtp = jtp->next; if (!jtp) { char msg[40]; sprintf(msg, KEY_NOT_MAPPED_TO_JUMP_FILE, key); HTAlert(msg); return NULL; } if (!jtp->table) jtp->nel = LYRead_Jumpfile(jtp); if (jtp->nel == 0) return NULL; if (!jump_buffer || !(jtp->shortcut && *jtp->shortcut)) *buf = '\0'; else if (jtp->shortcut && *jtp->shortcut) { if (strlen(jtp->shortcut) > 119) jtp->shortcut[119] = '\0'; strcpy(buf, jtp->shortcut); } ShortcutTotal = (jtp->history ? HTList_count(jtp->history) : 0); if (jump_buffer && *buf) { recall = ((ShortcutTotal > 1) ? RECALL : NORECALL); ShortcutNum = 0; FirstShortcutRecall = FALSE; } else { recall = ((ShortcutTotal >= 1) ? RECALL : NORECALL); ShortcutNum = ShortcutTotal; FirstShortcutRecall = TRUE; } statusline(jtp->msg); if ((ch=LYgetstr(buf, VISIBLE, (sizeof(buf) - 4), recall)) < 0) { /* * User cancelled the Jump via ^G. - FM */ _statusline(CANCELLED); sleep(InfoSecs); return NULL; } check_recall: bp = buf; if (toupper(key) == 'G' && strncmp(buf, "o ", 2) == 0) bp++; while (isspace(*bp)) bp++; if (*bp == '\0' && !(recall && (ch == UPARROW || ch == DNARROW))) { /* * User cancelled the Jump via a zero-length string. - FM */ *buf = '\0'; StrAllocCopy(jtp->shortcut, buf); _statusline(CANCELLED); sleep(InfoSecs); return NULL; } #ifdef PERMIT_GOTO_FROM_JUMP if (strchr(bp, ':') || strchr(bp, '/')) { char *temp=NULL; LYJumpFileURL = FALSE; if (no_goto) { *buf = '\0'; StrAllocCopy(jtp->shortcut, buf); _statusline(RANDOM_URL_DISALLOWED); sleep(MessageSecs); return NULL; } StrAllocCopy(temp, "Go "); StrAllocCat(temp, bp); strcpy(buf, temp); FREE(temp); return(bp=buf); } #endif /* PERMIT_GOTO_FROM_JUMP */ if (recall && ch == UPARROW) { if (FirstShortcutRecall) { /* * Use last Shortcut in the list. - FM */ FirstShortcutRecall = FALSE; ShortcutNum = 0; } else { /* * Go back to the previous Shortcut in the list. - FM */ ShortcutNum++; } if (ShortcutNum >= ShortcutTotal) /* * Roll around to the last Shortcut in the list. - FM */ ShortcutNum = 0; if ((cp=(char *)HTList_objectAt(jtp->history, ShortcutNum)) != NULL) { strcpy(buf, cp); if (jump_buffer && jtp->shortcut && !strcmp(buf, jtp->shortcut)) { _statusline(EDIT_CURRENT_SHORTCUT); } else if ((jump_buffer && ShortcutTotal == 2) || (!jump_buffer && ShortcutTotal == 1)) { _statusline(EDIT_THE_PREV_SHORTCUT); } else { _statusline(EDIT_A_PREV_SHORTCUT); } if ((ch=LYgetstr(buf, VISIBLE, sizeof(buf), recall)) < 0) { /* * User cancelled the jump via ^G. */ _statusline(CANCELLED); sleep(InfoSecs); return NULL; } goto check_recall; } } else if (recall && ch == DNARROW) { if (FirstShortcutRecall) { /* * Use the first Shortcut in the list. - FM */ FirstShortcutRecall = FALSE; ShortcutNum = ShortcutTotal - 1; } else { /* * Advance to the next Shortcut in the list. - FM */ ShortcutNum--; } if (ShortcutNum < 0) /* * Roll around to the first Shortcut in the list. - FM */ ShortcutNum = ShortcutTotal - 1; if ((cp=(char *)HTList_objectAt(jtp->history, ShortcutNum)) != NULL) { strcpy(buf, cp); if (jump_buffer && jtp->shortcut && !strcmp(buf, jtp->shortcut)) { _statusline(EDIT_CURRENT_SHORTCUT); } else if ((jump_buffer && ShortcutTotal == 2) || (!jump_buffer && ShortcutTotal == 1)) { _statusline(EDIT_THE_PREV_SHORTCUT); } else { _statusline(EDIT_THE_PREV_SHORTCUT); } if ((ch=LYgetstr(buf, VISIBLE, sizeof(buf), recall)) < 0) { /* * User cancelled the jump via ^G. */ _statusline(CANCELLED); sleep(InfoSecs); return NULL; } goto check_recall; } } seeking.key = bp; found = (JumpDatum *)bsearch((char *)&seeking, (char *)jtp->table, jtp->nel, sizeof(JumpDatum), LYCompare); if (!found) { user_message("Unknown target '%s'", buf); sleep(AlertSecs); } StrAllocCopy(jtp->shortcut, bp); LYAddJumpShortcut(jtp->history, jtp->shortcut); return found ? found->url : NULL; } PRIVATE unsigned LYRead_Jumpfile ARGS1(struct JumpTable *,jtp) { struct stat st; unsigned int nel; char *mp; int fd; #ifdef VMS FILE *fp; BOOL IsStream_LF = TRUE; #endif /* VMS */ char *cp; int i; if (jtp->file == NULL || *(jtp->file) == '\0') return 0; if (stat(jtp->file, &st) < 0) { HTAlert(CANNOT_LOCATE_JUMP_FILE); return 0; } /* allocate storage to read entire file */ if ((mp=(char *)calloc(1, st.st_size + 1)) == NULL) { HTAlert(OUTOF_MEM_FOR_JUMP_FILE); return 0; } #ifdef VMS if (st.st_fab_rfm != (char)FAB$C_STMLF) { /** It's a record-oriented file. **/ IsStream_LF = FALSE; if ((fp = fopen(jtp->file, "r", "mbc=32")) == NULL) { HTAlert(CANNOT_OPEN_JUMP_FILE); FREE(mp); return 0; } } else if ((fd=open(jtp->file, O_RDONLY, "mbc=32")) < 0) { #else if ((fd=open(jtp->file, O_RDONLY)) < 0) { #endif /* VMS */ HTAlert(CANNOT_OPEN_JUMP_FILE); FREE(mp); return 0; } #ifdef VMS if (IsStream_LF) { /** Handle as a stream. **/ #endif /* VMS */ if (read(fd, mp, st.st_size) < st.st_size) { HTAlert(ERROR_READING_JUMP_FILE); FREE(mp); return 0; } mp[st.st_size] = '\0'; close(fd); #ifdef VMS } else { /** Handle as a series of records. **/ if(fgets(mp, 1024, fp) == NULL) { HTAlert(ERROR_READING_JUMP_FILE); FREE(mp); return 0; } else while(fgets(mp+strlen(mp), 1024, fp) != NULL) ; fclose(fp); } #endif /* VMS */ /* quick scan for approximate number of entries */ nel = 0; cp = mp; while((cp = strchr(cp, '\n')) != NULL) { nel++; cp++; } jtp->table = (JumpDatum *)malloc(nel * sizeof(JumpDatum)); if (jtp->table == NULL) { HTAlert(OUTOF_MEM_FOR_JUMP_TABLE); FREE(mp); return 0; } cp = jtp->mp = mp; for (i = 0; i < nel; ) { if (strncmp(cp, "