/* WorldWideWeb - Wide Area Informaion Server Access HTWAIS.c
** ==================================================
**
** This module allows a WWW server or client to read data from a
** remote WAIS
** server, and provide that data to a WWW client in hypertext form.
** Source files, once retrieved, are stored and used to provide
** information about the index when that is acessed.
**
** Authors
** BK Brewster Kahle, Thinking Machines, <Brewster@think.com>
** TBL Tim Berners-Lee, CERN <timbl@info.cern.ch>
** FM Foteos Macrides, WFEB <macrides@sci.wfeb.edu>
**
** History
** Sep 91 TBL adapted shell-ui.c (BK) with HTRetrieve.c from WWW.
** Feb 91 TBL Generated HTML cleaned up a bit (quotes, escaping)
** Refers to lists of sources.
** Mar 93 TBL Lib 2.0 compatible module made.
** May 94 FM Added DIRECT_WAIS support for VMS.
**
** Bugs
** Uses C stream i/o to read and write sockets, which won't work
** on VMS TCP systems.
**
** Should cache connections.
**
** ANSI C only as written
**
** Bugs fixed
** NT Nathan Torkington (Nathan.Torkington@vuw.ac.nz)
**
** WAIS comments:
**
** 1. Separate directories for different system's .o would help
** 2. Document ids are rather long!
**
** W WW Address mapping convention:
**
** /servername/database/type/length/document-id
**
** /servername/database?word+word+word
*/
/* WIDE AREA INFORMATION SERVER SOFTWARE:
No guarantees or restrictions. See the readme file for the full standard
disclaimer.
Brewster@think.com
*/
#include <HTUtils.h>
#include <HTParse.h>
#include <HTAccess.h> /* We implement a protocol */
#include <HTML.h> /* The object we will generate */
#include <HTWSRC.h>
#include <HTTCP.h>
#include <HTCJK.h>
#include <HTAlert.h>
/* From WAIS
** ---------
*/
#ifdef VMS
#include <HTVMS_WaisUI.h>
#include <HTVMS_WaisProt.h>
#else
#include <ui.h>
#endif /* VMS */
#define MAX_MESSAGE_LEN 100000
#define CHARS_PER_PAGE 10000 /* number of chars retrieved in each request */
#define WAISSEARCH_DATE "Fri Jul 19 1991"
/* FROM WWW
** --------
*/
#include <LYUtils.h>
#include <LYLeaks.h>
#define DIRECTORY "/cnidr.org:210/directory-of-servers"
/* #define DIRECTORY "/quake.think.com:210/directory-of-servers" */
#define BIG 1024 /* identifier size limit @@@@@ */
#define BUFFER_SIZE 4096 /* Arbitrary size for efficiency */
#define HEX_ESCAPE '%'
PRIVATE BOOL as_gate; /* Client is using us as gateway */
PRIVATE char line[2048]; /* For building strings to display */
/* Must be able to take id */
#define PUTC(c) (*target->isa->put_character)(target, c)
#define PUTS(s) (*target->isa->put_string)(target, s)
#define START(e) (*target->isa->start_element)(target, e, 0, 0, -1, 0)
#define END(e) (*target->isa->end_element)(target, e, 0)
#define MAYBE_END(e) if (HTML_dtd.tags[e].contents != SGML_EMPTY) \
(*target->isa->end_element)(target, e, 0)
#define FREE_TARGET (*target->isa->_free)(target)
struct _HTStructured {
CONST HTStructuredClass * isa;
/* ... */
};
struct _HTStream {
CONST HTStreamClass * isa;
/* ... */
};
/* ------------------------------------------------------------------------ */
/* ---------------- Local copy of connect_to_server calls ----------------- */
/* ------------------------------------------------------------------------ */
/* Returns 1 on success, 0 on fail, -1 on interrupt. */
PRIVATE int fd_mosaic_connect_to_server ARGS3(
char *, host_name,
long, port,
long *, fd)
{
char *dummy = NULL;
int status;
int result;
HTSprintf0(&dummy, "wais://%s:%d/", host_name, port);
status = HTDoConnect (dummy, "WAIS", 210, (int *)fd);
if (status == HT_INTERRUPTED) {
result = -1;
} else if (status < 0) {
result = 0;
} else {
result = 1;
}
FREE(dummy);
return result;
}
/* Returns 1 on success, 0 on fail, -1 on interrupt. */
#ifdef VMS
PRIVATE int mosaic_connect_to_server ARGS3(
char *, host_name,
long, port,
long *, fdp)
#else
PRIVATE int mosaic_connect_to_server ARGS3(
char *, host_name,
long, port,
FILE **, fp)
#endif /* VMS */
{
#ifndef VMS
FILE* file;
#endif /* VMS */
long fd;
int rv;
rv = fd_mosaic_connect_to_server (host_name, port, &fd);
if (rv == 0) {
HTAlert (gettext("Could not connect to WAIS server."));
return 0;
} else if (rv == -1) {
HTAlert (CONNECTION_INTERRUPTED);
return -1;
}
#ifndef VMS
if ((file = fdopen(fd,"r+")) == NULL) {
HTAlert (gettext("Could not open WAIS connection for reading."));
return 0;
}
*fp = file;
#else
*fdp = fd;
#endif /* VMS */
return 1;
}
/* ------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------ */
/* showDiags
*/
/* modified from Jonny G's version in ui/question.c */
PRIVATE void showDiags ARGS2(
HTStream *, target,
diagnosticRecord **, d)
{
long i;
for (i = 0; d[i] != NULL; i++) {
if (d[i]->ADDINFO != NULL) {
PUTS(gettext("Diagnostic code is "));
PUTS(d[i]->DIAG);
PUTC(' ');
PUTS(d[i]->ADDINFO);
PUTC('\n');
}
}
}
/* Matrix of allowed characters in filenames
** -----------------------------------------
*/
PRIVATE BOOL acceptable[256];
PRIVATE BOOL acceptable_inited = NO;
PRIVATE void init_acceptable NOARGS
{
unsigned int i;
char * good =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./-_$";
for(i=0; i<256; i++) acceptable[i] = NO;
for(;*good; good++) acceptable[(unsigned int)*good] = YES;
acceptable_inited = YES;
}
/* Transform file identifier into WWW address
** ------------------------------------------
**
**
** On exit,
** returns nil if error
** pointer to malloced string (must be freed) if ok
*/
PRIVATE char * WWW_from_archie ARGS1(
char *, file)
{
char * end;
char * result;
char * colon;
for(end=file; *end > ' '; end++); /* assumes ASCII encoding*/
result = (char *)malloc(10 + (end-file));
if (!result) return result; /* Malloc error */
strcpy(result, "file://");
strncat(result, file, end-file);
colon = strchr(result+7, ':'); /* Expect colon after host */
if (colon) {
for(; colon[0]; colon[0]=colon[1], colon++); /* move down */
}
return result;
} /* WWW_from_archie */
/* Transform document identifier into URL
** --------------------------------------
**
** Bugs: A static buffer of finite size is used!
** The format of the docid MUST be good!
**
** On exit,
** returns nil if error
** pointer to malloced string (must be freed) if ok
*/
PRIVATE char hex [17] = "0123456789ABCDEF";
PRIVATE char * WWW_from_WAIS ARGS1(
any *, docid)
{
static char buf[BIG];
char * q = buf;
char * p = (docid->bytes);
char * result = NULL;
int i, l;
if (TRACE) {
char *p;
fprintf(tfp, "WAIS id (%d bytes) is ", (int)docid->size);
for (p = docid->bytes; p < docid->bytes+docid->size; p++) {
if ((*p >= ' ') && (*p<= '~')) /* Assume ASCII! */
fprintf(tfp, "%c", *p);
else
fprintf(tfp, "<%x>", (unsigned)*p);
}
fprintf(tfp, "\n");
}
for (p = docid->bytes;
(p < docid->bytes+docid->size) && (q < &buf[BIG]);) {
CTRACE((tfp, " Record type %d, length %d\n", p[0], p[1]));
if (*p > 10) {
CTRACE((tfp, "Eh? DOCID record type of %d!\n", *p));
return 0;
}
{ /* Bug fix -- allow any byte value 15 Apr 93 */
unsigned int i = (unsigned) *p++;
if (i > 99) {
*q++ = (i/100) + '0';
i = i % 100;
}
if (i > 9) {
*q++ = (i/10) + '0';
i = i % 10;
}
*q++ = i + '0'; /* Record type */
}
*q++ = '='; /* Separate */
l = *p++; /* Length */
for (i = 0; i < l; i++, p++){
if (!acceptable[*p]) {
*q++ = HEX_ESCAPE; /* Means hex coming */
*q++ = hex[(*p) >> 4];
*q++ = hex[(*p) & 15];
}
else *q++ = *p;
}
*q++= ';'; /* Terminate field */
}
*q++ = 0; /* Terminate string */
CTRACE((tfp, "WWW form of id: %s\n", buf));
StrAllocCopy(result, buf);
return result;
} /* WWW_from_WAIS */
/* Transform URL into WAIS document identifier
** -------------------------------------------
**
** On entry,
** docname points to valid name produced originally by
** WWW_from_WAIS
** On exit,
** docid->size is valid
** docid->bytes is malloced and must later be freed.
*/
PRIVATE any * WAIS_from_WWW ARGS2(
any *, docid,
char *, docname)
{
char *z; /* Output pointer */
char *sor; /* Start of record - points to size field. */
char *p; /* Input pointer */
char *q; /* Poisition of "=" */
char *s; /* Position of semicolon */
int n; /* size */
CTRACE((tfp, "WWW id (to become WAIS id): %s\n", docname));
for (n = 0, p = docname; *p; p++) { /* Count sizes of strings */
n++;
if (*p == ';')
n--; /* Not converted */
else if (*p == HEX_ESCAPE)
n = n-2; /* Save two bytes */
docid->size = n;
}
if (!(docid->bytes = (char *) malloc(docid->size))) /* result record */
outofmem(__FILE__, "WAIS_from_WWW");
z = docid->bytes;
for (p = docname; *p; ) { /* Convert of strings */
/* Record type */
*z = 0; /* Initialize record type */
while (*p >= '0' && *p <= '9') {
*z = *z*10 + (*p++ - '0'); /* Decode decimal record type */
}
z++;
if (*p != '=')
return 0;
q = p;
/* *z++ = *p++ - '0';
q = strchr(p , '=');
if (!q) return 0;
*/
s = strchr(q, ';'); /* (Check only) */
if (!s)
return 0; /* Bad! No ';'; */
sor = z; /* Remember where the size field was */
z++; /* Skip record size for now */
for (p = q+1; *p != ';';) {
if (*p == HEX_ESCAPE) {
char c;
unsigned int b;
p++;
c = *p++;
b = from_hex(c);
c = *p++;
if (!c)
break; /* Odd number of chars! */
*z++ = (b<<4) + from_hex(c);
} else {
*z++ = *p++; /* Record */
}
}
*sor = (z-sor-1); /* Fill in size -- not counting size itself */
p++; /* After semicolon: start of next record */
}
if (TRACE) {
char *p;
fprintf(tfp, "WAIS id (%d bytes) is ", (int)docid->size);
for (p = docid->bytes; p < docid->bytes+docid->size; p++) {
if ((*p >= ' ') && (*p<= '~')) /* Assume ASCII! */
fprintf(tfp, "%c", *p);
else
fprintf(tfp, "<%x>", (unsigned)*p);
}
fprintf(tfp, "\n");
}
return docid; /* Ok */
} /* WAIS_from_WWW */
/* Send a plain text record to the client output_text_record()
** --------------------------------------
*/
PRIVATE void output_text_record ARGS4(
HTStream *, target,
WAISDocumentText *, record,
boolean, quote_string_quotes,
boolean, binary)
{
long count;
/* printf(" Text\n");
print_any(" DocumentID: ", record->DocumentID);
printf(" VersionNumber: %d\n", record->VersionNumber);
*/
if (binary) {
(*target->isa->put_block)(target,
record->DocumentText->bytes,
record->DocumentText->size);
return;
}
for (count = 0; count < record->DocumentText->size; count++){
long ch = (unsigned char)record->DocumentText->bytes[count];
if (ch == 27) { /* What is this in for? Tim */
/* then we have an escape code */
/* if the next letter is '(' or ')', then ignore two letters */
if ('(' == record->DocumentText->bytes[count + 1] ||
')' == record->DocumentText->bytes[count + 1])
count += 1; /* it is a term marker */
else count += 4; /* it is a paragraph marker */
} else if (ch == '\n' || ch == '\r') {
PUTC('\n');
} else if (HTCJK != NOCJK || ch == '\t' || isprint(ch)){
PUTC(ch);
}
}
} /* output text record */
/* Format A Search response for the client display_search_response
** ---------------------------------------
*/
/* modified from tracy shen's version in wutil.c
* displays either a text record or a set of headlines.
*/
PRIVATE void display_search_response ARGS4(
HTStructured *, target,
SearchResponseAPDU *, response,
char *, database,
char *, keywords)
{
WAISSearchResponse *info;
long i, k;
BOOL archie = strstr(database, "archie")!=0; /* Special handling */
CTRACE((tfp, "HTWAIS: Displaying search response\n"));
PUTS(gettext("Index "));
START(HTML_EM);
PUTS(database);
END(HTML_EM);
sprintf(line, gettext(" contains the following %d item%s relevant to \""),
(int)(response->NumberOfRecordsReturned),
response->NumberOfRecordsReturned ==1 ? "" : "s");
PUTS(line);
START(HTML_EM);
PUTS(keywords);
END(HTML_EM);
PUTS("\".\n");
PUTS(gettext("The first figure after each entry is its relative score, "));
PUTS(gettext("the second is the number of lines in the item."));
START(HTML_BR);
START(HTML_BR);
PUTC('\n');
START(HTML_OL);
if (response->DatabaseDiagnosticRecords != 0) {
info = (WAISSearchResponse *)response->DatabaseDiagnosticRecords;
i =0;
if (info->Diagnostics != NULL)
showDiags((HTStream*)target, info->Diagnostics);
if (info->DocHeaders != 0) {
for (k = 0; info->DocHeaders[k] != 0; k++ ) {
WAISDocumentHeader* head = info->DocHeaders[k];
char * headline = trim_junk(head->Headline);
any * docid = head->DocumentID;
char * docname; /* printable version of docid */
i++;
/*
** Make a printable string out of the document id.
*/
CTRACE((tfp, "HTWAIS: %2ld: Score: %4ld, lines:%4ld '%s'\n",
i,
(long int)(info->DocHeaders[k]->Score),
(long int)(info->DocHeaders[k]->Lines),
headline));
START(HTML_LI);
if (archie) {
char * www_name = WWW_from_archie(headline);
if (www_name) {
HTStartAnchor(target, NULL, www_name);
PUTS(headline);
END(HTML_A);
FREE(www_name);
} else {
PUTS(headline);
PUTS(gettext(" (bad file name)"));
}
} else { /* Not archie */
docname = WWW_from_WAIS(docid);
if (docname) {
if ((head->Types) &&
(!strcmp(head->Types[0], "URL"))) {
HTStartAnchor(target, NULL, headline);
} else{
char * dbname = HTEscape(database, URL_XPALPHAS);
char * w3_address = NULL;
HTSprintf0(&w3_address,
"/%s/%s/%d/%s",
dbname,
head->Types ? head->Types[0] : "TEXT",
(int)(head->DocumentLength),
docname);
HTStartAnchor(target, NULL, w3_address);
FREE(w3_address);
FREE(dbname);
}
PUTS(headline);
END(HTML_A);
FREE(docname);
} else {
PUTS(gettext("(bad doc id)"));
}
}
sprintf(line, "%5ld %5ld ",
head->Score,
head->Lines);
PUTS( line);
MAYBE_END(HTML_LI);
} /* next document header */
} /* if there were any document headers */
if (info->ShortHeaders != 0) {
k = 0;
while (info->ShortHeaders[k] != 0) {
i++;
PUTS( gettext("(Short Header record, can't display)"));
}
}
if (info->LongHeaders != 0) {
k = 0;
while (info->LongHeaders[k] != 0) {
i++;
PUTS( gettext("\nLong Header record, can't display\n"));
}
}
if (info->Text != 0) {
k = 0;
while (info->Text[k] != 0) {
i++;
PUTS( gettext("\nText record\n"));
output_text_record((HTStream*)target,
info->Text[k++], false, false);
}
}
if (info->Headlines != 0) {
k = 0;
while (info->Headlines[k] != 0) {
i++;
PUTS( gettext("\nHeadline record, can't display\n"));
/* dsply_headline_record( info->Headlines[k++]); */
}
}
if (info->Codes != 0) {
k = 0;
while (info->Codes[k] != 0) {
i++;
PUTS( gettext("\nCode record, can't display\n"));
/* dsply_code_record( info->Codes[k++]); */
}
}
} /* Loop: display user info */
END(HTML_OL);
PUTC('\n');
}
/* Load by name HTLoadWAIS
** ============
**
** This renders any object or search as required.
*/
PUBLIC int HTLoadWAIS ARGS4(
CONST char *, arg,
HTParentAnchor *, anAnchor,
HTFormat, format_out,
HTStream*, sink)
#define MAX_KEYWORDS_LENGTH 1000
#define MAX_SERVER_LENGTH 1000
#define MAX_DATABASE_LENGTH 1000
#define MAX_SERVICE_LENGTH 1000
#define MAXDOCS 200
{
char * key; /* pointer to keywords in URL */
char* request_message = NULL; /* arbitrary message limit */
char* response_message = NULL; /* arbitrary message limit */
long request_buffer_length; /* how of the request is left */
SearchResponseAPDU *retrieval_response = 0;
char keywords[MAX_KEYWORDS_LENGTH + 1];
char *server_name;
char *wais_database = NULL; /* name of current database */
char *www_database; /* Same name escaped */
char *service;
char *doctype;
char *doclength;
long document_length;
char *docname;
#ifdef VMS
long connection = 0;
#else
FILE *connection = NULL;
#endif /* VMS */
char * names; /* Copy of arg to be hacked up */
BOOL ok = NO;
int return_status = HT_LOADED;
int rv;
extern FILE * connect_to_server();
if (!acceptable_inited)
init_acceptable();
/* Decipher and check syntax of WWW address:
** ----------------------------------------
**
** First we remove the "wais:" if it was specified. 920110
*/
names = HTParse(arg, "", PARSE_HOST | PARSE_PATH | PARSE_PUNCTUATION);
key = strchr(names, '?');
if (key) {
char * p;
*key++ = 0; /* Split off keywords */
for (p=key; *p; p++) if (*p == '+') *p = ' ';
HTUnEscape(key);
}
if (names[0] == '/') {
server_name = names+1;
if ((as_gate =(*server_name == '/')) != 0)
server_name++; /* Accept one or two */
www_database = strchr(server_name,'/');
if (www_database) {
*www_database++ = 0; /* Separate database name */
doctype = strchr(www_database, '/');
if (key) ok = YES; /* Don't need doc details */
else if (doctype) { /* If not search parse doc details */
*doctype++ = 0; /* Separate rest of doc address */
doclength = strchr(doctype, '/');
if (doclength) {
*doclength++ = 0;
document_length = atol(doclength);
if (document_length) {
docname = strchr(doclength, '/');
if (docname) {
*docname++ = 0;
ok = YES; /* To avoid a goto! */
} /* if docname */
} /* if document_length valid */
} /* if doclength */
} else { /* no doctype? Assume index required */
if (!key)
key = "";
ok = YES;
} /* if doctype */
} /* if database */
}
if (!ok)
return HTLoadError(sink, 500, gettext("Syntax error in WAIS URL"));
CTRACE((tfp, "HTWAIS: Parsed OK\n"));
service = strchr(names, ':');
if (service)
*service++ = 0;
else
service = "210";
if (server_name[0] == 0) {
#ifdef VMS
connection = 0;
#else
connection = NULL;
#endif /* VMS */
} else if (!(key && !*key)) {
int status;
CTRACE((tfp, "===WAIS=== calling mosaic_connect_to_server\n"));
status = mosaic_connect_to_server(server_name,
atoi(service),
&connection);
if (status == 0) {
CTRACE((tfp, "===WAIS=== connection failed\n"));
FREE(names);
return HT_NOT_LOADED;
} else if (status == -1) {
CTRACE((tfp, "===WAIS=== connection interrupted\n"));
FREE(names);
return HT_NOT_LOADED;
}
}
StrAllocCopy(wais_database,www_database);
HTUnEscape(wais_database);
/*
** This below fixed size stuff is terrible.
*/
#ifdef VMS
if ((request_message = typecallocn(char, MAX_MESSAGE_LEN)) == 0)
outofmem(__FILE__, "HTLoadWAIS");
if ((response_message = typecallocn(char, MAX_MESSAGE_LEN)) == 0)
outofmem(__FILE__, "HTLoadWAIS");
#else
request_message = (char*)s_malloc((size_t)MAX_MESSAGE_LEN * sizeof(char));
response_message = (char*)s_malloc((size_t)MAX_MESSAGE_LEN * sizeof(char));
#endif /* VMS */
/*
** If keyword search is performed but there are no keywords,
** the user has followed a link to the index itself. It would be
** appropriate at this point to send him the .SRC file - how?
*/
if (key && !*key) { /* I N D E X */
#ifdef CACHE_FILE_PREFIX
char * filename = NULL;
FILE * fp;
#endif
HTStructured * target = HTML_new(anAnchor, format_out, sink);
START(HTML_HEAD);
PUTC('\n');
HTStartIsIndex(target, HTWAIS_SOLICIT_QUERY , NULL);
PUTC('\n');
{
START(HTML_TITLE);
PUTS(wais_database);
PUTS(gettext(" (WAIS Index)"));
END(HTML_TITLE);
PUTC('\n');
END(HTML_HEAD);
PUTC('\n');
START(HTML_H1);
PUTS(gettext("WAIS Index: "));
START(HTML_EM);
PUTS(wais_database);
END(HTML_EM);
END(HTML_H1);
PUTC('\n');
PUTS(gettext("This is a link for searching the "));
START(HTML_EM);
PUTS(wais_database);
END(HTML_EM);
PUTS(gettext(" WAIS Index.\n"));
}
/*
** If we have seen a source file for this database, use that.
*/
#ifdef CACHE_FILE_PREFIX
HTSprintf0(&filename, "%sWSRC-%s:%s:%.100s.txt",
CACHE_FILE_PREFIX,
server_name, service, www_database);
fp = fopen(filename, "r"); /* Have we found this already? */
CTRACE((tfp, "HTWAIS: Description of server %s %s.\n",
filename,
fp ? "exists already" : "does NOT exist!"));
if (fp) {
char c;
START(HTML_PRE); /* Preformatted description */
PUTC('\n');
while((c=getc(fp))!=EOF) PUTC(c); /* Transfer file */
END(HTML_PRE);
fclose(fp);
}
FREE(filename);
#endif
START(HTML_P);
PUTS(gettext("\nEnter the 's'earch command and then specify search words.\n"));
FREE_TARGET;
} else if (key)num_choices]);
}
}
sprintf(Cnum, "%d: ", num_choices);
Lnum = strlen(Cnum);
num_choices--;
/*
* Let's assume for the sake of sanity that ly is the number
* corresponding to the line the option is on.
* Let's also assume that cur_choice is the number of the
* choice that should be initially selected, with 0 being
* the first choice.
* So what we have, is the top equal to the current screen line
* subtracting the cur_choice + 1 (the one must be for the top
* line we will draw in a box). If the top goes under 0, then
* consider it 0.
*/
top = ly - (cur_choice + 1);
if (top < 0)
top = 0;
/*
* Check and see if we need to put the i_length parameter up to
* the number of real choices.
*/
if (i_length < 1) {
i_length = num_choices;
} else {
/*
* Otherwise, it is really one number too high.
*/
i_length--;
}
/*
* The bottom is the value of the top plus the number of choices
* to view plus 3 (one for the top line, one for the bottom line,
* and one to offset the 0 counted in the num_choices).
*/
bottom = top + i_length + 3;
if (for_mouse && user_mode == NOVICE_MODE && DisplayLines > 2)
DisplayLines--;
/*
* Hmm... If the bottom goes beyond the number of lines available,
*/
if (bottom > DisplayLines) {
/*
* Position the window at the top if we have more
* choices than will fit in the window.
*/
if ((i_length + 3) > DisplayLines) {
top = 0;
bottom = (top + (i_length + 3));
if (bottom > DisplayLines)
bottom = (DisplayLines + 1);
} else {
/*
* Try to position the window so that the selected choice will
* appear where the choice box currently is positioned.
* It could end up too high, at this point, but we'll move it
* down latter, if that has happened.
*/
top = (DisplayLines + 1) - (i_length + 3);
bottom = (DisplayLines + 1);
}
}
/*
* This is really fun, when the length is 4, it means 0 to 4, or 5.
*/
length = (bottom - top) - 2;
/*
* Move the window down if it's too high.
*/
if (bottom < ly + 2) {
bottom = ly + 2;
if (bottom > DisplayLines + 1)
bottom = DisplayLines + 1;
top = bottom - length - 2;
}
if (for_mouse) {
/* shift horizontally to lie within screen width, if possible */
if (Lnum + (int)width + 4 < LYcols) {
if (lx - 1 + (Lnum + (int)width + 4) > LYcols)
lx = LYcols + 1 - (Lnum + width + 4);
else if (lx <= 0)
lx = 1;
}
}
/*
* Set up the overall window, including the boxing characters ('*'),
* if it all fits. Otherwise, set up the widest window possible. - FM
*/
#ifdef USE_SLANG
SLsmg_fill_region(top, lx - 1, bottom - top, (Lnum + width + 4), ' ');
#else
if (!(form_window = newwin(bottom - top, (Lnum + width + 4),
top, (lx - 1))) &&
!(form_window = newwin(bottom - top, 0, top, 0))) {
HTAlert(POPUP_FAILED);
return(orig_choice);
}
scrollok(form_window, TRUE);
#ifdef PDCURSES
keypad(form_window, TRUE);
#endif /* PDCURSES */
#if defined(NCURSES) || defined(PDCURSES)
LYsubwindow(form_window);
#endif
#if defined(HAVE_GETBKGD)/* not defined in ncurses 1.8.7 */
wbkgd(form_window, getbkgd(stdscr));
wbkgdset(form_window, getbkgd(stdscr));
#endif
#endif /* USE_SLANG */
/*
* Clear the command line and write
* the popup statusline. - FM
*/
move((LYlines - 2), 0);
clrtoeol();
if (disabled) {
StrAllocCopy(popup_status_msg, CHOICE_LIST_UNM_MSG);
} else if (!for_mouse) {
StrAllocCopy(popup_status_msg, CHOICE_LIST_MESSAGE);
#if defined(NCURSES_MOUSE_VERSION) || defined(PDCURSES_MOUSE_VERSION)
} else {
StrAllocCopy(popup_status_msg, gettext(
"Left mouse button or return to select, arrow keys to scroll."));
#endif
}
_statusline(popup_status_msg);
/*
* Set up the window_offset for choices.
* cur_choice ranges from 0...n
* length ranges from 0...m
*/
if (cur_choice >= length) {
window_offset = cur_choice - length + 1;
}
/*
* Compute the number of popup window pages. - FM
*/
npages = ((num_choices + 1) > length) ?
(((num_choices + 1) + (length - 1))/(length))
: 1;
/*
* OH! I LOVE GOTOs! hack hack hack
*/
redraw:
Cptr = choices;
/*
* Display the boxed choices.
*/
for (i = 0; i <= num_choices; i++) {
if (i >= window_offset && i - window_offset < length) {
FormatChoiceNum(Cnum, i, "");
#ifdef USE_SLANG
SLsmg_gotorc(top + ((i + 1) - window_offset), (lx - 1 + 2));
addstr(Cnum);
LYaddnstr(Cptr[i], width);
#else
wmove(form_window, ((i + 1) - window_offset), 2);
wclrtoeol(form_window);
waddstr(form_window, Cnum);
LYwaddstr(form_window, Cptr[i]);
#endif /* USE_SLANG */
}
}
#ifdef USE_SLANG
SLsmg_draw_box(top, (lx - 1), (bottom - top), (Lnum + width + 4));
#else
#ifdef VMS
VMSbox(form_window, (bottom - top), (Lnum + width + 4));
#else
LYbox(form_window, FALSE);
#endif /* VMS */
wrefresh(form_window);
#endif /* USE_SLANG */
Cptr = NULL;
/*
* Loop on user input.
*/
while (cmd != LYK_ACTIVATE) {
/*
* Unreverse cur choice.
*/
if (Cptr != NULL) {
FormatChoiceNum(Cnum, i, "");
#ifdef USE_SLANG
SLsmg_gotorc((top + ((i + 1) - window_offset)), (lx - 1 + 2));
addstr(Cnum);
LYaddnstr(Cptr[i], width);
#else
wmove(form_window, ((i + 1) - window_offset), 2);
waddstr(form_window, Cnum);
LYwaddstr(form_window, Cptr[i]);
#endif /* USE_SLANG */
}
Cptr = choices;
i = cur_choice;
FormatChoiceNum(Cnum, i, "");
#ifdef USE_SLANG
SLsmg_gotorc((top + ((i + 1) - window_offset)), (lx - 1 + 2));
addstr(Cnum);
SLsmg_set_color(2);
LYaddnstr(Cptr[i], width);
SLsmg_set_color(0);
/*
* If LYShowCursor is ON, move the cursor to the left
* of the current choice, so that blind users, who are
* most likely to have LYShowCursor ON, will have it's
* string spoken or passed to the braille interface as
* each choice is made current. Otherwise, move it to
* the bottom, right column of the screen, to "hide"
* the cursor as for the main document, and let sighted
* users rely on the current choice's highlighting or
* color without the distraction of a blinking cursor
* in the window. - FM
*/
if (LYShowCursor)
SLsmg_gotorc((top + ((i + 1) - window_offset)), (lx - 1 + 1));
else
SLsmg_gotorc((LYlines - 1), (LYcols - 1));
SLsmg_refresh();
#else
wmove(form_window, ((i + 1) - window_offset), 2);
waddstr(form_window, Cnum);
#if defined(WIN_EX) /* 1997/10/18 (Sat) 00:10:51 */
wattron(form_window, A_REVERSE);
#else
wstart_reverse(form_window);
#endif
LYwaddstr(form_window, Cptr[i]);
#if defined(WIN_EX) /* 1997/10/18 (Sat) 00:10:58 */
wattroff(form_window, A_REVERSE);
#else
wstop_reverse(form_window);
#endif
/*
* If LYShowCursor is ON, move the cursor to the left
* of the current choice, so that blind users, who are
* most likely to have LYShowCursor ON, will have it's
* string spoken or passed to the braille interface as
* each choice is made current. Otherwise, leave it to
* the right of the current choice, since we can't move
* it out of the window, and let sighted users rely on
* the highlighting of the current choice without the
* distraction of a blinking cursor preceding it. - FM
*/
if (LYShowCursor)
wmove(form_window, ((i + 1) - window_offset), 1);
wrefresh(form_window);
#endif /* USE_SLANG */
term_options = FALSE;
c = LYgetch_for(FOR_CHOICE);
if (term_options || c == 3 || c == 7) {
cmd = LYK_QUIT;
#ifndef USE_SLANG
} else if (c == MOUSE_KEY) {
if ((cmd = fancy_mouse(form_window, i + 1 - window_offset, &cur_choice)) < 0)
goto redraw;
if (cmd == LYK_ACTIVATE)
break;
#endif
} else {
cmd = LKC_TO_LAC(keymap,c);
}
#ifdef VMS
if (HadVMSInterrupt) {
HadVMSInterrupt = FALSE;
cmd = LYK_QUIT;
}
#endif /* VMS */
switch(cmd) {
case LYK_F_LINK_NUM:
c = '\0';
/* FALLTHRU */
case LYK_1: /* FALLTHRU */
case LYK_2: /* FALLTHRU */
case LYK_3: /* FALLTHRU */
case LYK_4: /* FALLTHRU */
case LYK_5: /* FALLTHRU */
case LYK_6: /* FALLTHRU */
case LYK_7: /* FALLTHRU */
case LYK_8: /* FALLTHRU */
case LYK_9:
/*
* Get a number from the user, possibly with
* a 'g' or 'p' suffix (which will be loaded
* into c). - FM & LE
*/
number = get_popup_choice_number((int *)&c);
/*
* Check for a 'p' suffix. - FM
*/
if (c == 'p') {
/*
* Treat 1 or less as the first page. - FM
*/
if (number <= 1) {
if (window_offset == 0) {
HTUserMsg(ALREADY_AT_CHOICE_BEGIN);
_statusline(popup_status_msg);
break;
}
window_offset = 0;
cur_choice = 0;
_statusline(popup_status_msg);
goto redraw;
}
/*
* Treat a number equal to or greater than the
* number of pages as the last page. - FM
*/
if (number >= npages) {
if (window_offset >= ((num_choices - length) + 1)) {
HTUserMsg(ALREADY_AT_CHOICE_END);
_statusline(popup_status_msg);
break;
}
window_offset = ((npages - 1) * length);
if (window_offset > (num_choices - length)) {
window_offset = (num_choices - length + 1);
}
if (cur_choice < window_offset)
cur_choice = window_offset;
_statusline(popup_status_msg);
goto redraw;
}
/*
* We want an intermediate page. - FM
*/
if (((number - 1) * length) == window_offset) {
sprintf(buffer, ALREADY_AT_CHOICE_PAGE, number);
HTUserMsg(buffer);
_statusline(popup_status_msg);
break;
}
cur_choice = window_offset = ((number - 1) * length);
_statusline(popup_status_msg);
goto redraw;
}
/*
* Check for a positive number, which signifies
* that a choice should be sought. - FM
*/
if (number > 0) {
/*
* Decrement the number so as to correspond
* with our cur_choice values. - FM
*/
number--;
/*
* If the number is in range and had no legal
* suffix, select the indicated choice. - FM
*/
if (number <= num_choices && c == '\0') {
cur_choice = number;
cmd = LYK_ACTIVATE;
break;
}
/*
* Verify that we had a 'g' suffix,
* and act on the number. - FM
*/
if (c == 'g') {
if (cur_choice == number) {
/*
* The choice already is current. - FM
*/
sprintf(buffer,
CHOICE_ALREADY_CURRENT, (number + 1));
HTUserMsg(buffer);
_statusline(popup_status_msg);
break;
}
if (number <= num_choices) {
/*
* The number is in range and had a 'g'
* suffix, so make it the current choice,
* scrolling if needed. - FM
*/
j = (number - cur_choice);
cur_choice = number;
if ((j > 0) &&
(cur_choice - window_offset) >= length) {
window_offset += j;
if (window_offset > (num_choices - length + 1))
window_offset = (num_choices - length + 1);
} else if ((cur_choice - window_offset) < 0) {
window_offset -= abs(j);
if (window_offset < 0)
window_offset = 0;
}
_statusline(popup_status_msg);
goto redraw;
}
/*
* Not in range. - FM
*/
HTUserMsg(BAD_CHOICE_NUM_ENTERED);
}
}
/*
* Restore the popup statusline. - FM
*/
_statusline(popup_status_msg);
break;
case LYK_PREV_LINK:
case LYK_LPOS_PREV_LINK:
case LYK_UP_LINK:
if (cur_choice > 0)
cur_choice--;
/*
* Scroll the window up if necessary.
*/
if ((cur_choice - window_offset) < 0) {
window_offset--;
goto redraw;
}
break;
case LYK_NEXT_LINK:
case LYK_LPOS_NEXT_LINK:
case LYK_DOWN_LINK:
if (cur_choice < num_choices)
cur_choice++;
/*
* Scroll the window down if necessary
*/
if ((cur_choice - window_offset) >= length) {
window_offset++;
goto redraw;
}
break;
case LYK_NEXT_PAGE:
/*
* Okay, are we on the last page of the choices list?
* If not then,
*/
if (window_offset != (num_choices - length + 1)) {
/*
* Modify the current choice to not be a
* coordinate in the list, but a coordinate
* on the item selected in the window.
*/
cur_choice -= window_offset;
/*
* Page down the proper length for the list.
* If simply to far, back up.
*/
window_offset += length;
if (window_offset > (num_choices - length)) {
window_offset = (num_choices - length + 1);
}
/*
* Readjust the current choice to be a choice
* list coordinate rather than window.
* Redraw this thing.
*/
cur_choice += window_offset;
goto redraw;
}
else if (cur_choice < num_choices) {
/*
* Already on last page of the choice list so
* just redraw it with the last item selected.
*/
cur_choice = num_choices;
}
break;
case LYK_PREV_PAGE:
/*
* Are we on the first page of the choice list?
* If not then,
*/
if (window_offset != 0) {
/*
* Modify the current choice to not be a choice
* list coordinate, but a window coordinate.
*/
cur_choice -= window_offset;
/*
* Page up the proper length.
* If too far, back up.
*/
window_offset -= length;
if (window_offset < 0) {
window_offset = 0;
}
/*
* Readjust the current choice.
*/
cur_choice += window_offset;
goto redraw;
} else if (cur_choice > 0) {
/*
* Already on the first page so just
* back up to the first item.
*/
cur_choice = 0;
}
break;
case LYK_HOME:
cur_choice = 0;
if (window_offset > 0) {
window_offset = 0;
goto redraw;
}
break;
case LYK_END:
cur_choice = num_choices;
if (window_offset != (num_choices - length + 1)) {
window_offset = (num_choices - length + 1);
goto redraw;
}
break;
case LYK_DOWN_TWO:
cur_choice += 2;
if (cur_choice > num_choices)
cur_choice = num_choices;
/*
* Scroll the window down if necessary.
*/
if ((cur_choice - window_offset) >= length) {
window_offset += 2;
if (window_offset > (num_choices - length + 1))
window_offset = (num_choices - length + 1);
goto redraw;
}
break;
case LYK_UP_TWO:
cur_choice -= 2;
if (cur_choice < 0)
cur_choice = 0;
/*
* Scroll the window up if necessary.
*/
if ((cur_choice - window_offset) < 0) {
window_offset -= 2;
if (window_offset < 0)
window_offset = 0;
goto redraw;
}
break;
case LYK_DOWN_HALF:
cur_choice += (length/2);
if (cur_choice > num_choices)
cur_choice = num_choices;
/*
* Scroll the window down if necessary.
*/
if ((cur_choice - window_offset) >= length) {
window_offset += (length/2);
if (window_offset > (num_choices - length + 1))
window_offset = (num_choices - length + 1);
goto redraw;
}
break;
case LYK_UP_HALF:
cur_choice -= (length/2);
if (cur_choice < 0)
cur_choice = 0;
/*
* Scroll the window up if necessary.
*/
if ((cur_choice - window_offset) < 0) {
window_offset -= (length/2);
if (window_offset < 0)
window_offset = 0;
goto redraw;
}
break;
case LYK_REFRESH:
lynx_force_repaint();
refresh();
break;
case LYK_NEXT:
if (recall && *prev_target_buffer == '\0') {
/*
* We got a 'n'ext command with no prior query
* specified within the popup window. See if
* one was entered when the popup was retracted,
* and if so, assume that's what's wanted. Note
* that it will become the default within popups,
* unless another is entered within a popup. If
* the within popup default is to be changed at
* that point, use WHEREIS ('/') and enter it,
* or the up- or down-arrow keys to seek any of
* the previously entered queries, regardless of
* whether they were entered within or outside
* of a popup window. - FM
*/
if ((cp = (char *)HTList_objectAt(search_queries,
0)) != NULL) {
LYstrncpy(prev_target_buffer, cp, sizeof(prev_target_buffer) - 1);
QueryNum = 0;
FirstRecall = FALSE;
}
}
strcpy(prev_target, prev_target_buffer);
/* FALLTHRU */
case LYK_WHEREIS:
if (*prev_target == '\0' ) {
_statusline(ENTER_WHEREIS_QUERY);
if ((ch = LYgetstr(prev_target, VISIBLE,
sizeof(prev_target_buffer),
recall)) < 0) {
/*
* User cancelled the search via ^G. - FM
*/
HTInfoMsg(CANCELLED);
goto restore_popup_statusline;
}
}
check_recall:
if (*prev_target == '\0' &&
!(recall && (ch == UPARROW || ch == DNARROW))) {
/*
* No entry. Simply break. - FM
*/
HTInfoMsg(CANCELLED);
goto restore_popup_statusline;
}
if (recall && ch == UPARROW) {
if (FirstRecall) {
/*
* Use the current string or
* last query in the list. - FM
*/
FirstRecall = FALSE;
if (*prev_target_buffer) {
for (QueryNum = (QueryTotal - 1);
QueryNum > 0; QueryNum--) {
if ((cp = (char *)HTList_objectAt(
search_queries,
QueryNum)) != NULL &&
!strcmp(prev_target_buffer, cp)) {
break;
}
}
} else {
QueryNum = 0;
}
} else {
/*
* Go back to the previous query in the list. - FM
*/
QueryNum++;
}
if (QueryNum >= QueryTotal)
/*
* Roll around to the last query in the list. - FM
*/
QueryNum = 0;
if ((cp = (char *)HTList_objectAt(search_queries,
QueryNum)) != NULL) {
LYstrncpy(prev_target, cp, sizeof(prev_target) - 1);
if (*prev_target_buffer &&
!strcmp(prev_target_buffer, prev_target)) {
_statusline(EDIT_CURRENT_QUERY);
} else if ((*prev_target_buffer && QueryTotal == 2) ||
(!(*prev_target_buffer) &&
QueryTotal == 1)) {
_statusline(EDIT_THE_PREV_QUERY);
} else {
_statusline(EDIT_A_PREV_QUERY);
}
if ((ch = LYgetstr(prev_target, VISIBLE,
sizeof(prev_target_buffer), recall)) < 0) {
/*
* User cancelled the search via ^G. - FM
*/
HTInfoMsg(CANCELLED);
goto restore_popup_statusline;
}
goto check_recall;
}
} else if (recall && ch == DNARROW) {
if (FirstRecall) {
/*
* Use the current string or
* first query in the list. - FM
*/
FirstRecall = FALSE;
if (*prev_target_buffer) {
for (QueryNum = 0;
QueryNum < (QueryTotal - 1); QueryNum++) {
if ((cp = (char *)HTList_objectAt(
search_queries,
QueryNum)) != NULL &&
!strcmp(prev_target_buffer, cp)) {
break;
}
}
} else {
QueryNum = (QueryTotal - 1);
}
} else {
/*
* Advance to the next query in the list. - FM
*/
QueryNum--;
}
if (QueryNum < 0)
/*
* Roll around to the first query in the list. - FM
*/
QueryNum = (QueryTotal - 1);
if ((cp = (char *)HTList_objectAt(search_queries,
QueryNum)) != NULL) {
LYstrncpy(prev_target, cp, sizeof(prev_target) - 1);
if (*prev_target_buffer &&
!strcmp(prev_target_buffer, prev_target)) {
_statusline(EDIT_CURRENT_QUERY);
} else if ((*prev_target_buffer &&
QueryTotal == 2) ||
(!(*prev_target_buffer) &&
QueryTotal == 1)) {
_statusline(EDIT_THE_PREV_QUERY);
} else {
_statusline(EDIT_A_PREV_QUERY);
}
if ((ch = LYgetstr(prev_target, VISIBLE,
sizeof(prev_target_buffer),
recall)) < 0) {
/*
* User cancelled the search via ^G. - FM
*/
HTInfoMsg(CANCELLED);
goto restore_popup_statusline;
}
goto check_recall;
}
}
/*
* Replace the search string buffer with the new target. - FM
*/
strcpy(prev_target_buffer, prev_target);
HTAddSearchQuery(prev_target_buffer);
/*
* Start search at the next choice. - FM
*/
for (j = 1; Cptr[i+j] != NULL; j++) {
FormatChoiceNum(buffer, (i + j), Cptr[i+j]);
if (case_sensitive) {
if (strstr(buffer, prev_target_buffer) != NULL)
break;
} else {
if (LYstrstr(buffer, prev_target_buffer) != NULL)
break;
}
}
if (Cptr[i+j] != NULL) {
/*
* We have a hit, so make that choice the current. - FM
*/
cur_choice += j;
/*
* Scroll the window down if necessary.
*/
if ((cur_choice - window_offset) >= length) {
window_offset += j;
if (window_offset > (num_choices - length + 1))
window_offset = (num_choices - length + 1);
ReDraw = TRUE;
}
goto restore_popup_statusline;
}
/*
* If we started at the beginning, it can't be present. - FM
*/
if (cur_choice == 0) {
HTUserMsg2(STRING_NOT_FOUND, prev_target_buffer);
goto restore_popup_statusline;
}
/*
* Search from the beginning to the current choice. - FM
*/
for (j = 0; j < cur_choice; j++) {
FormatChoiceNum(buffer, (j + 1), Cptr[j]);
if (case_sensitive) {
if (strstr(buffer, prev_target_buffer) != NULL)
break;
} else {
if (LYstrstr(buffer, prev_target_buffer) != NULL)
break;
}
}
if (j < cur_choice) {
/*
* We have a hit, so make that choice the current. - FM
*/
j = (cur_choice - j);
cur_choice -= j;
/*
* Scroll the window up if necessary.
*/
if ((cur_choice - window_offset) < 0) {
window_offset -= j;
if (window_offset < 0)
window_offset = 0;
ReDraw = TRUE;
}
goto restore_popup_statusline;
}
/*
* Didn't find it in the preceding choices either. - FM
*/
HTUserMsg2(STRING_NOT_FOUND, prev_target_buffer);
restore_popup_statusline:
/*
* Restore the popup statusline and
* reset the search variables. - FM
*/
_statusline(popup_status_msg);
*prev_target = '\0';
QueryTotal = (search_queries ? HTList_count(search_queries)
: 0);
recall = ((QueryTotal >= 1) ? RECALL : NORECALL);
QueryNum = QueryTotal;
if (ReDraw == TRUE) {
ReDraw = FALSE;
goto redraw;
}
break;
case LYK_QUIT:
case LYK_ABORT:
case LYK_PREV_DOC:
cur_choice = orig_choice;
term_options = TRUE;
if (!for_mouse) {
HTUserMsg(CANCELLED);
}
cmd = LYK_ACTIVATE; /* to exit */
break;
}
}
FREE(popup_status_msg);
#ifndef USE_SLANG
touchwin(stdscr);
delwin(form_window);
#ifdef NCURSES
LYsubwindow(0);
#endif
#endif /* !USE_SLANG */
if (disabled || term_options) {
_statusline("");
return(orig_choice);
} else {
if (!for_mouse) {
_statusline(VALUE_ACCEPTED);
}
return(cur_choice);
}
}
#endif /* !NO_OPTION_MENU */
#ifndef NO_OPTION_FORMS
/*
* I'm paranoid about mistyping strings. Also, this way they get combined
* so we don't have to worry about the intelligence of the compiler.
* We don't need to burn memory like it's cheap. We're better than that.
*/
#define SELECTED(flag) (flag) ? selected_string : ""
#define DISABLED(flag) (flag) ? disabled_string : ""
#define NOTEMPTY(text) (text && text[0]) ? text : ""
typedef struct {
int value;
CONST char *LongName;
CONST char *HtmlName;
} OptValues;
typedef struct {
char * tag;
char * value;
} PostPair;
static CONST char selected_string[] = "selected";
static CONST char disabled_string[] = "disabled";
static CONST char on_string[] = "ON";
static CONST char off_string[] = "OFF";
static CONST char never_string[] = "NEVER";
static CONST char always_string[] = "ALWAYS";
static OptValues bool_values[] = {
{ FALSE, "OFF", "OFF" },
{ TRUE, "ON", "ON" },
{ 0, 0, 0 }};
static char * secure_string = "secure";
static char * secure_value = NULL;
static char * save_options_string = "save_options";
/*
* Personal Preferences
*/
static char * cookies_string = "cookies";
static char * cookies_ignore_all_string = "ignore";
static char * cookies_up_to_user_string = "ask user";
static char * cookies_accept_all_string = "accept all";
static char * x_display_string = "display";
static char * editor_string = "editor";
static char * emacs_keys_string = "emacs_keys";
#if defined(ENABLE_OPTS_CHANGE_EXEC) && (defined(EXEC_LINKS) || defined(EXEC_SCRIPTS))
#define EXEC_ALWAYS 2
#define EXEC_LOCAL 1
#define EXEC_NEVER 0
static char * exec_links_string = "exec_options";
static OptValues exec_links_values[] = {
{ EXEC_NEVER, "ALWAYS OFF", "ALWAYS OFF" },
{ EXEC_LOCAL, "FOR LOCAL FILES ONLY", "FOR LOCAL FILES ONLY" },
#ifndef NEVER_ALLOW_REMOTE_EXEC
{ EXEC_ALWAYS, "ALWAYS ON", "ALWAYS ON" },
#endif
{ 0, 0, 0 }};
#endif /* ENABLE_OPTS_CHANGE_EXEC */
#ifdef EXP_KEYBOARD_LAYOUT
static char * kblayout_string = "kblayout";
#endif
static char * keypad_mode_string = "keypad_mode";
static OptValues keypad_mode_values[] = {
{ NUMBERS_AS_ARROWS, "Numbers act as arrows", "number_arrows" },
{ LINKS_ARE_NUMBERED, "Links are numbered", "links_numbered" },
{ LINKS_AND_FIELDS_ARE_NUMBERED,
"Links and form fields are numbered",
"links_and_forms" },
{ 0, 0, 0 }};
static char * lineedit_style_string = "lineedit_style";
static char * mail_address_string = "mail_address";
static char * search_type_string = "search_type";
static OptValues search_type_values[] = {
{ FALSE, "Case insensitive", "case_insensitive" },
{ TRUE, "Case sensitive", "case_sensitive" },
{ 0, 0, 0 }};
#if defined(USE_SLANG) || defined(COLOR_CURSES)
static char * show_color_string = "show_color";
static OptValues show_color_values[] = {
{ SHOW_COLOR_NEVER, never_string, never_string },
{ SHOW_COLOR_OFF, off_string, off_string },
{ SHOW_COLOR_ON, on_string, on_string },
{ SHOW_COLOR_ALWAYS, always_string, always_string },
{ 0, 0, 0 }};
#endif
static char * show_cursor_string = "show_cursor";
static char * user_mode_string = "user_mode";
static OptValues user_mode_values[] = {
{ NOVICE_MODE, "Novice", "Novice" },
{ INTERMEDIATE_MODE, "Intermediate", "Intermediate" },
{ ADVANCED_MODE, "Advanced", "Advanced" },
{ 0, 0, 0 }};
static char * vi_keys_string = "vi_keys";
static char * visited_pages_type_string = "visited_pages_type";
static OptValues visited_pages_type_values[] = {
{ VISITED_LINKS_AS_FIRST_V, "By First Visit", "first_visited" },
{ VISITED_LINKS_AS_FIRST_V | VISITED_LINKS_REVERSE,
"By First Visit Reversed", "first_visited_reversed" },
{ VISITED_LINKS_AS_TREE, "As Visit Tree", "visit_tree" },
{ VISITED_LINKS_AS_LATEST, "By Last Visit", "last_visited" },
{ VISITED_LINKS_AS_LATEST | VISITED_LINKS_REVERSE,
"By Last Visit Reversed", "last_visited_reversed" },
{ 0, 0, 0 }};
/*
* Document Layout
*/
#ifndef SH_EX /* 1999/01/19 (Tue) */
static char * DTD_recovery_string = "DTD";
static OptValues DTD_type_values[] = {
/* Old_DTD variable */
{ TRUE, "relaxed (TagSoup mode)", "tagsoup" },
{ FALSE, "strict (SortaSGML mode)", "sortasgml" },
{ 0, 0, 0 }};
#endif
static char * select_popups_string = "select_popups";
static char * images_string = "images";
static char * images_ignore_all_string = "ignore";
static char * images_use_label_string = "as labels";
static char * images_use_links_string = "as links";
static char * verbose_images_string = "verbose_images";
static OptValues verbose_images_type_values[] = {
/* verbose_img variable */
{ FALSE, "OFF", "OFF" },
{ TRUE, "show filename", "ON" },
{ 0, 0, 0 }};
/*
* Bookmark Options
*/
static char * mbm_advanced_string = "ADVANCED";
static char * mbm_off_string = "OFF";
static char * mbm_standard_string = "STANDARD";
static char * mbm_string = "multi_bookmarks_mode";
static char * single_bookmark_string = "single_bookmark_name";
/*
* Character Set Options
*/
static char * assume_char_set_string = "assume_char_set";
static char * display_char_set_string = "display_char_set";
static char * raw_mode_string = "raw_mode";
/*
* File Management Options
*/
static char * show_dotfiles_string = "show_dotfiles";
#ifdef DIRED_SUPPORT
static char * dired_sort_string = "dired_sort";
static OptValues dired_values[] = {
{ 0, "Directories first", "dired_dir" },
{ FILES_FIRST, "Files first", "dired_files" },
{ MIXED_STYLE, "Mixed style", "dired_mixed" },
{ 0, 0, 0 }};
#endif /* DIRED_SUPPORT */
static char * ftp_sort_string = "ftp_sort";
static OptValues ftp_sort_values[] = {
{ FILE_BY_NAME, "By Name", "ftp_by_name" },
{ FILE_BY_TYPE, "By Type", "ftp_by_type" },
{ FILE_BY_SIZE, "By Size", "ftp_by_size" },
{ FILE_BY_DATE, "By Date", "ftp_by_date" },
{ 0, 0, 0 }};
/*
* Headers transferred to remote server
*/
static char * preferred_doc_char_string = "preferred_doc_char";
static char * preferred_doc_lang_string = "preferred_doc_lang";
static char * user_agent_string = "user_agent";
#define PutLabel(fp, text) \
fprintf(fp," %-33s: ", text)
#define PutLabelNotSaved(fp, text) \
if (!no_option_save) { \
int l=strlen(text); \
fprintf(fp," %s", text); \
fprintf(fp,"%s%-*s: ", (l<30)?" ":"", (l<30)?32-l:3, "(!)"); \
} else PutLabel(fp, text)
#define PutTextInput(fp, Name, Value, Size, disable) \
fprintf(fp,\
"<input size=%d type=\"text\" name=\"%s\" value=\"%s\" %s>\n",\
(int) Size, Name, Value, disable_all?disabled_string:disable)
#define PutOption(fp, flag, html, name) \
fprintf(fp,"<option value=\"%s\" %s>%s\n", html, SELECTED(flag), name)
#define BeginSelect(fp, text) \
fprintf(fp,"<select name=\"%s\" %s>\n", text, disable_all?disabled_string:"")
#define MaybeSelect(fp, flag, text) \
fprintf(fp,"<select name=\"%s\" %s>\n", text, disable_all?disabled_string:DISABLED(flag))
#define EndSelect(fp)\
fprintf(fp,"</select>\n")
PRIVATE void PutOptValues ARGS3(
FILE *, fp,
int, value,
OptValues *, table)
{
while (table->LongName != 0) {
if (table->HtmlName) {
PutOption(fp,
value == table->value,
table->HtmlName,
table->LongName);
}
table++;
}
}
PRIVATE BOOLEAN GetOptValues ARGS3(
OptValues *, table,
char *, value,
int *, result)
{
while (table->LongName != 0) {
if (table->HtmlName && !strcmp(value, table->HtmlName)) {
*result = table->value;
return TRUE;
}
table++;
}
return FALSE;
}
/*
* Break cgi line into array of pairs of pointers. Don't bother trying to
* be efficient. We're not called all that often.
* We come in with a string looking like:
* tag1=value1&tag2=value2&...&tagN=valueN
* We leave with an array of post_pairs. The last element in the array
* will have a tag pointing to NULL.
* Not pretty, but works. Hey, if strings can be null terminate arrays...
*/
PRIVATE PostPair * break_data ARGS1(
char *, data)
{
char * p = data;
PostPair * q = NULL;
int count = 0;
if (p==NULL || p[0]=='\0')
return NULL;
CTRACE((tfp, "break_data %s\n", data));
q = calloc(sizeof(PostPair), 1);
if (q==NULL)
outofmem(__FILE__, "break_data(calloc)");
do {
/*
* First, break up on '&', sliding 'p' on down the line.
*/
q[count].value = LYstrsep(&p, "&");
/*
* Then break up on '=', sliding value down, and setting tag.
*/
q[count].tag = LYstrsep(&(q[count].value), "=");
/*
* Clean them up a bit, in case user entered a funky string.
*/
HTUnEscape(q[count].tag);
/* In the value field we have '+' instead of ' '. So do a simple
* find&replace on the value field before UnEscaping() - SKY
*/
{
size_t i, len;
len = strlen(q[count].value);
for (i = 0; i < len; i++) {
if (q[count].value[i] == '+') {
#ifdef UNIX
/*
* Allow for special case of options which begin with a "+" on
* Unix - TD
*/
if (i > 0
&& q[count].value[i+1] == '+'
&& isalnum(q[count].value[i+2])) {
q[count].value[i++] = ' ';
i++;
continue;
}
#endif
q[count].value[i] = ' ';
}
}
}
HTUnEscape(q[count].value);
CTRACE((tfp, "...item[%d] tag=%s, value=%s\n", count, q[count].tag, q[count].value));
count++;
/*
* Like I said, screw efficiency. Sides, realloc is fast on
* Linux ;->
*/
q = realloc(q, sizeof(PostPair)*(count+1));
if (q==NULL)
outofmem(__FILE__, "break_data(realloc)");
q[count].tag=NULL;
} while (p!=NULL && p[0]!='\0');
return q;
}
PRIVATE int gen_options PARAMS((char **newfile));
/*
* Handle options from the pseudo-post. I think we really only need
* post_data here, but bring along everything just in case. It's only a
* pointer. MRC
*
* Options are processed in order according to gen_options(), we should not
* depend on it and add boolean flags where the order is essential (save,
* character sets...)
*
* Security: some options are disabled in gen_options() under certain
* conditions. We *should* duplicate the same conditions here in postoptions()
* to prevent user with a limited access from editing HTML options code
* manually (e.g., doing 'e'dit in 'o'ptions) and submit it to access the
* restricted items. Prevent spoofing attempts from index overrun. - LP
*
* Exit status: NULLFILE (reload) or NORMAL (use HText cache).
*
* On exit, got the document which was current before the Options menu:
*
* (from cache) nothing changed or no visual effect supposed:
* editor name, e-mail, etc.
*
* (reload locally) to see the effect of certain changes:
* display_char_set, assume_charset, etc.
* (use 'need_reload' flag where necessary).
*
* (reload from remote server and uncache on a proxy)
* few options changes should be transferred to remote server:
* preferred language, fake browser name, etc.
* (use 'need_end_reload' flag).
*/
PUBLIC int postoptions ARGS1(
document *, newdoc)
{
PostPair *data = 0;
DocAddress WWWDoc; /* need on exit */
int i;
int code;
BOOLEAN save_all = FALSE;
int display_char_set_old = current_char_set;
BOOLEAN raw_mode_old = LYRawMode;
BOOLEAN assume_char_set_changed = FALSE;
BOOLEAN need_reload = FALSE;
BOOLEAN need_end_reload = FALSE;
#if defined(USE_SLANG) || defined(COLOR_CURSES)
int CurrentShowColor = LYShowColor;
#endif
/*-------------------------------------------------
* kludge a link from mbm_menu, the URL was:
* "<a href=\"LYNXOPTIONS://MBM_MENU\">Goto multi-bookmark menu</a>\n"
*--------------------------------------------------*/
if (strstr(newdoc->address, "LYNXOPTIONS://MBM_MENU")) {
FREE(newdoc->post_data);
if (no_bookmark) {
HTAlert(BOOKMARK_CHANGE_DISALLOWED); /* anonymous */
return(NULLFILE);
} else if (dump_output_immediately) {
return(NOT_FOUND);
} else {
edit_bookmarks();
return(NULLFILE);
}
}
data = break_data(newdoc->post_data);
if (!data) {
int status;
/*-------------------------------------------------
* kludge gen_options() call:
*--------------------------------------------------*/
status = gen_options(&newdoc->address);
if (status != NORMAL) {
HTAlwaysAlert("Unexpected way of accessing", newdoc->address);
FREE(newdoc->address);
return(status);
}
/* exit to getfile() cycle */
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;
if (!HTLoadAbsolute(&WWWDoc))
return(NOT_FOUND);
LYRegisterUIPage(newdoc->address, UIP_OPTIONS_MENU);
#ifdef DIRED_SUPPORT
lynx_edit_mode = FALSE;
#endif /* DIRED_SUPPORT */
return(NORMAL);
}
if (!LYIsUIPage3(HTLoadedDocumentURL(), UIP_OPTIONS_MENU, 0) &&
!LYIsUIPage3(HTLoadedDocumentURL(), UIP_VLINKS, 0)) {
char *buf = NULL;
/* We may have been spoofed? */
HTSprintf0(&buf,
gettext("Use %s to invoke the Options menu!"),
key_for_func_ext(LYK_OPTIONS, FOR_PANEL));
HTAlert(buf);
FREE(buf);
FREE(data);
return(NOT_FOUND);
}
for (i = 0; data[i].tag != NULL; i++) {
/*
* This isn't really for security, but rather for avoiding that
* the user may revisit an older instance from the history stack
* and submit stuff which accidentally undoes changes that had
* been done from a newer instance. - kw
*/
if (!strcmp(data[i].tag, secure_string)) {
if (!secure_value || strcmp(data[i].value, secure_value)) {
char *buf = NULL;
/*
* We probably came from an older instance of the Options
* page that had been on the history stack. - kw
*/
HTSprintf0(&buf,
gettext("Use %s to invoke the Options menu!"),
key_for_func_ext(LYK_OPTIONS, FOR_PANEL));
HTAlert(buf);
FREE(data);
return(NULLFILE);
}
FREE(secure_value);
}
/* Save options */
if (!strcmp(data[i].tag, save_options_string) && (!no_option_save)) {
save_all = TRUE;
}
/* Cookies: SELECT */
if (!strcmp(data[i].tag, cookies_string)) {
if (!strcmp(data[i].value, cookies_ignore_all_string)) {
LYSetCookies = FALSE;
} else if (!strcmp(data[i].value, cookies_up_to_user_string)) {
LYSetCookies = TRUE;
LYAcceptAllCookies = FALSE;
} else if (!strcmp(data[i].value, cookies_accept_all_string)) {
LYSetCookies = TRUE;
LYAcceptAllCookies = TRUE;
}
}
/* X Display: INPUT */
if (!strcmp(data[i].tag, x_display_string)) {
LYsetXDisplay(data[i].value);
validate_x_display();
summarize_x_display(data[i].value);
}
/* Editor: INPUT */
if (!strcmp(data[i].tag, editor_string)) {
FREE(editor);
StrAllocCopy(editor, data[i].value);
}
/* Emacs keys: ON/OFF */
if (!strcmp(data[i].tag, emacs_keys_string)
&& GetOptValues(bool_values, data[i].value, &code)) {
if ((emacs_keys = (BOOL) code) != FALSE) {
set_emacs_keys();
} else {
reset_emacs_keys();
}
}
/* Execution links: SELECT */
#if defined(ENABLE_OPTS_CHANGE_EXEC) && (defined(EXEC_LINKS) || defined(EXEC_SCRIPTS))
if (!strcmp(data[i].tag, exec_links_string)
&& GetOptValues(exec_links_values, data[i].value, &code)) {
#ifndef NEVER_ALLOW_REMOTE_EXEC
local_exec = (code == EXEC_ALWAYS);
#endif /* !NEVER_ALLOW_REMOTE_EXEC */
local_exec_on_local_files = (code == EXEC_LOCAL);
}
#endif /* ENABLE_OPTS_CHANGE_EXEC */
/* Keypad Mode: SELECT */
if (!strcmp(data[i].tag, keypad_mode_string)) {
int newval;
if (GetOptValues(keypad_mode_values, data[i].value, &newval)
&& keypad_mode != newval) {
keypad_mode = newval;
need_reload = TRUE;
if (keypad_mode == NUMBERS_AS_ARROWS) {
set_numbers_as_arrows();
} else {
reset_numbers_as_arrows();
}
}
}
/* Line edit style: SELECT */
if (!strcmp(data[i].tag, lineedit_style_string)) {
int newval = atoi(data[i].value);
int j;
/* prevent spoofing attempt */
for (j = 0; LYLineeditNames[j]; j++) {
if (j==newval) current_lineedit = newval;
}
}
#ifdef EXP_KEYBOARD_LAYOUT
/* Keyboard layout: SELECT */
if (!strcmp(data[i].tag, kblayout_string)) {
int newval = atoi(data[i].value);
int j;
/* prevent spoofing attempt */
for (j = 0; LYKbLayoutNames[j]; j++) {
if (j==newval) current_layout = newval;
}
}
#endif /* EXP_KEYBOARD_LAYOUT */
/* Mail Address: INPUT */
if (!strcmp(data[i].tag, mail_address_string)) {
FREE(personal_mail_address);
StrAllocCopy(personal_mail_address, data[i].value);
}
/* Search Type: SELECT */
if (!strcmp(data[i].tag, search_type_string)
&& GetOptValues(search_type_values, data[i].value, &code)) {
case_sensitive = (BOOL) code;
}
#ifndef SH_EX /* 1999/01/19 (Tue) */
/* HTML error tolerance: SELECT */
if (!strcmp(data[i].tag, DTD_recovery_string)
&& GetOptValues(DTD_type_values, data[i].value, &code)) {
if (Old_DTD != code) {
Old_DTD = code;
HTSwitchDTD(!Old_DTD);
need_reload = TRUE;
}
}
#endif
/* Select Popups: ON/OFF */
if (!strcmp(data[i].tag, select_popups_string)
&& GetOptValues(bool_values, data[i].value, &code)) {
LYSelectPopups = (BOOL) code;
}
#if defined(USE_SLANG) || defined(COLOR_CURSES)
/* Show Color: SELECT */
if (!strcmp(data[i].tag, show_color_string)
&& GetOptValues(show_color_values, data[i].value,
&LYChosenShowColor)) {
if (can_do_colors)
LYShowColor = LYChosenShowColor;
if (CurrentShowColor != LYShowColor) {
lynx_force_repaint();
}
CurrentShowColor = LYShowColor;
#ifdef USE_SLANG
SLtt_Use_Ansi_Colors = (LYShowColor > 1 ? 1 : 0);
#endif
}
#endif /* USE_SLANG || COLOR_CURSES */
/* Show Cursor: ON/OFF */
if (!strcmp(data[i].tag, show_cursor_string)
&& GetOptValues(bool_values, data[i].value, &code)) {
LYShowCursor = (BOOL) code;
}
/* User Mode: SELECT */
if (!strcmp(data[i].tag, user_mode_string)
&& GetOptValues(user_mode_values, data[i].value, &user_mode)) {
if (user_mode == NOVICE_MODE) {
display_lines = (LYlines - 4);
} else {
display_lines = LYlines-2;
}
}
/* Type of visited pages page: SELECT */
if (!strcmp(data[i].tag, visited_pages_type_string))
GetOptValues(visited_pages_type_values, data[i].value, &Visited_Links_As);
/* Show Images: SELECT */
if (!strcmp(data[i].tag, images_string)) {
if (!strcmp(data[i].value, images_ignore_all_string)
&& !(pseudo_inline_alts == FALSE && clickable_images == FALSE)) {
pseudo_inline_alts = FALSE;
clickable_images = FALSE;
need_reload = TRUE;
} else if (!strcmp(data[i].value, images_use_label_string)
&& !(pseudo_inline_alts == TRUE && clickable_images == FALSE)) {
pseudo_inline_alts = TRUE;
clickable_images = FALSE;
need_reload = TRUE;
} else if (!strcmp(data[i].value, images_use_links_string)
&& !(clickable_images == TRUE)) {
clickable_images = TRUE;
need_reload = TRUE;
}
}
/* Verbose Images: ON/OFF */
if (!strcmp(data[i].tag, verbose_images_string)
&& GetOptValues(verbose_images_type_values, data[i].value, &code)) {
if (verbose_img != code) {
verbose_img = (BOOL) code;
need_reload = TRUE;
}
}
/* VI Keys: ON/OFF */
if (!strcmp(data[i].tag, vi_keys_string)
&& GetOptValues(bool_values, data[i].value, &code)) {
if ((vi_keys = (BOOL) code) != FALSE) {
set_vi_keys();
} else {
reset_vi_keys();
}
}
/* Bookmarks File Menu: SELECT */
if (!strcmp(data[i].tag, mbm_string) && (!LYMBMBlocked)) {
if (!strcmp(data[i].value, mbm_off_string)) {
LYMultiBookmarks = FALSE;
} else if (!strcmp(data[i].value, mbm_standard_string)) {
LYMultiBookmarks = TRUE;
LYMBMAdvanced = FALSE;
} else if (!strcmp(data[i].value, mbm_advanced_string)) {
LYMultiBookmarks = TRUE;
LYMBMAdvanced = TRUE;
}
}
/* Default Bookmarks filename: INPUT */
if (!strcmp(data[i].tag, single_bookmark_string) && (!no_bookmark)) {
if (strcmp(data[i].value, "")) {
FREE(bookmark_page);
StrAllocCopy(bookmark_page, data[i].value);
}
}
/* Assume Character Set: SELECT */
if (!strcmp(data[i].tag, assume_char_set_string)) {
int newval = UCGetLYhndl_byMIME(data[i].value);
if (newval >= 0
&& ((raw_mode_old &&
newval != safeUCGetLYhndl_byMIME(UCAssume_MIMEcharset))
|| (!raw_mode_old &&
newval != UCLYhndl_for_unspec)
)) {
UCLYhndl_for_unspec = newval;
StrAllocCopy(UCAssume_MIMEcharset, data[i].value);
assume_char_set_changed = TRUE;
}
}
/* Display Character Set: SELECT */
if (!strcmp(data[i].tag, display_char_set_string)) {
int newval = atoi(data[i].value);
int j;
/* prevent spoofing attempt */
for (j = 0; LYchar_set_names[j]; j++) {
if (j==newval) current_char_set = newval;
}
}
/* Raw Mode: ON/OFF */
if (!strcmp(data[i].tag, raw_mode_string)
&& GetOptValues(bool_values, data[i].value, &code)) {
LYRawMode = (BOOL) code;
}
/*
* ftp sort: SELECT
*/
if (!strcmp(data[i].tag, ftp_sort_string)) {
GetOptValues(ftp_sort_values, data[i].value, &HTfileSortMethod);
}
#ifdef DIRED_SUPPORT
/* Local Directory Sort: SELECT */
if (!strcmp(data[i].tag, dired_sort_string)) {
GetOptValues(dired_values, data[i].value, &dir_list_style);
}
#endif /* DIRED_SUPPORT */
/* Show dot files: ON/OFF */
if (!strcmp(data[i].tag, show_dotfiles_string) && (!no_dotfiles)
&& GetOptValues(bool_values, data[i].value, &code)) {
show_dotfiles = (BOOL) code;
}
/* Preferred Document Character Set: INPUT */
if (!strcmp(data[i].tag, preferred_doc_char_string)) {
if (strcmp(pref_charset, data[i].value)) {
FREE(pref_charset);
StrAllocCopy(pref_charset, data[i].value);
need_end_reload = TRUE;
}
}
/* Preferred Document Language: INPUT */
if (!strcmp(data[i].tag, preferred_doc_lang_string)) {
if (strcmp(language, data[i].value)) {
FREE(language);
StrAllocCopy(language, data[i].value);
need_end_reload = TRUE;
}
}
/* User Agent: INPUT */
if (!strcmp(data[i].tag, user_agent_string) && (!no_useragent)) {
if (strcmp(LYUserAgent, data[i].value)) {
need_end_reload = TRUE;
FREE(LYUserAgent);
/* ignore Copyright warning ? */
StrAllocCopy(LYUserAgent,
*(data[i].value)
? data[i].value
: LYUserAgentDefault);
if (LYUserAgent && *LYUserAgent &&
!strstr(LYUserAgent, "Lynx") &&
!strstr(LYUserAgent, "lynx") &&
!strstr(LYUserAgent, "L_y_n_x") &&
!strstr(LYUserAgent, "l_y_n_x")) {
HTAlert(UA_PLEASE_USE_LYNX);
}
}
}
} /* end of loop */
/*
* Process the flags:
*/
if ( display_char_set_old != current_char_set ||
raw_mode_old != LYRawMode ||
assume_char_set_changed ) {
/*
* charset settings: the order is essential here.
*/
if (display_char_set_old != current_char_set) {
/*
* Set the LYUseDefaultRawMode value and character
* handling if LYRawMode was changed. - FM
*/
LYUseDefaultRawMode = TRUE;
HTMLUseCharacterSet(current_char_set);
}
if (assume_char_set_changed && HTCJK != JAPANESE) {
LYRawMode = (BOOL) (UCLYhndl_for_unspec == current_char_set);
}
if (raw_mode_old != LYRawMode || assume_char_set_changed) {
/*
* Set the raw 8-bit or CJK mode defaults and
* character set if changed. - FM
*/
HTMLSetUseDefaultRawMode(current_char_set, LYRawMode);
HTMLSetCharacterHandling(current_char_set);
}
need_reload = TRUE;
} /* end of charset settings */
/*
* FIXME: Golly gee, we need to write all of this out now, don't we?
*/
FREE(newdoc->post_data);
FREE(data);
if (save_all) {
HTInfoMsg(SAVING_OPTIONS);
if (save_rc(NULL)) {
LYrcShowColor = LYChosenShowColor;
HTInfoMsg(OPTIONS_SAVED);
} else {
HTAlert(OPTIONS_NOT_SAVED);
}
}
/*
* Exit: working around the previous document.
* Being out of mainloop()/getfile() cycle, do things manually.
*/
CTRACE((tfp, "\nLYOptions.c/postoptions(): exiting...\n"));
CTRACE((tfp, " need_reload = %s\n",
need_reload ? "TRUE" : "FALSE"));
CTRACE((tfp, " need_end_reload = %s\n",
need_end_reload ? "TRUE" : "FALSE"));
/* Options menu was pushed before postoptions(), so pop-up. */
LYpop(newdoc);
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;
LYforce_no_cache = FALSE; /* ! */
LYoverride_no_cache = TRUE; /* ! */
/*
* Working out of getfile() cycle we reset *no_cache manually here so
* HTLoadAbsolute() will return "Document already in memory": it was
* forced reloading Options Menu again without this (overhead).
*
* Probably *no_cache was set in a wrong position because of
* the internal page...
*/
if (!HTLoadAbsolute(&WWWDoc))
return(NOT_FOUND);
/* comment out to avoid warning when removing forms content... */
/* HTuncache_current_document(); */ /* will never use again */
/*
* Return to previous doc, not to options menu!
* Reload the document we had before the options menu
* but uncache only when necessary (Hurrah, user!):
*/
LYpop(newdoc);
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;
LYforce_no_cache = FALSE; /* see below */
LYoverride_no_cache = TRUE; /* see below */
/*
* Re-setting of *no_cache is probably not required here but this is a
* guarantee against _double_ reloading over the net in case prev document
* has its own "no cache" attribute and options menu set "need_reload"
* also. Force this HTLoadAbsolute() to return "Document already in
* memory".
*/
if (!HTLoadAbsolute(&WWWDoc))
return(NOT_FOUND);
/*
* Now most interesting part: reload document when necessary.
* **********************************************************
*/
reloading = FALSE; /* set manually */
/* force end-to-end reload from remote server if change LYUserAgent
* or language or pref_charset (marked by need_end_reload flag above),
* from old-style LYK_OPTIONS (mainloop):
*/
if ((need_end_reload == TRUE &&
(strncmp(newdoc->address, "http", 4) == 0 ||
strncmp(newdoc->address, "lynxcgi:", 8) == 0))) {
/*
* An option has changed which may influence
* content negotiation, and the resource is from
* a http or https or lynxcgi URL (the only protocols
* which currently do anything with this information).
* Set reloading = TRUE so that proxy caches will be
* flushed, which is necessary until the time when
* all proxies understand HTTP 1.1 Vary: and all
* Servers properly use it... Treat like
* case LYK_RELOAD (see comments there). - KW
*/
reloading = TRUE; /* global flag */
need_reload = TRUE; /* this was probably already TRUE, don't worry */
}
if (need_reload == FALSE) {
/* no uncache, already loaded */
CTRACE((tfp, "LYOptions.c/postoptions(): now really exit.\n\n"));
return(NORMAL);
} else {
/* update HText cache */
/*
* see LYK_RELOAD & LYK_OPTIONS in mainloop for details...
*/
if (HTisDocumentSource()) {
srcmode_for_next_retrieval(1);
}
#ifdef SOURCE_CACHE
if (reloading == FALSE) {
/* one more attempt to be smart enough: */
if (HTcan_reparse_document()) {
if (!HTreparse_document())
srcmode_for_next_retrieval(0);
CTRACE((tfp, "LYOptions.c/postoptions(): now really exit.\n\n"));
return(NORMAL);
}
}
#endif
if (newdoc->post_data != NULL && !newdoc->safe &&
confirm_post_resub(newdoc->address, newdoc->title, 2, 1) == FALSE) {
HTInfoMsg(WILL_NOT_RELOAD_DOC);
if (HTisDocumentSource()) {
srcmode_for_next_retrieval(0);
}
return(NORMAL);
}
HEAD_request = HTLoadedDocumentIsHEAD();
/* uncache and load again */
HTuncache_current_document();
LYpush(newdoc, FALSE);
CTRACE((tfp, "LYOptions.c/postoptions(): now really exit.\n\n"));
return(NULLFILE);
}
/******** Done! **************************************************/
}
PRIVATE char *NewSecureValue NOARGS
{
FREE(secure_value);
if ((secure_value = malloc(80)) != 0) {
#if defined(HAVE_RAND) && defined(HAVE_SRAND) && defined(RAND_MAX)
long key = rand();
#else
long key = (long)secure_value + (long)time(0);
#endif
sprintf(secure_value, "%ld", key);
return secure_value;
}
return "?";
}
/*
* Okay, someone wants to change options. So, lets gen up a form for them
* and pass it around. Gor, this is ugly. Be a lot easier in Bourne with
* "here" documents. :->
* Basic Strategy: For each option, throw up the appropriate type of
* control, giving defaults as appropriate. If nothing else, we're
* probably going to test every control there is. MRC
*
* This function is synchronized with postoptions(). Read the comments in
* postoptions() header if you change something in gen_options().
*/
PRIVATE int gen_options ARGS1(
char **, newfile)
{
int i;
static char tempfile[LY_MAXPATH] = "\0";
BOOLEAN disable_all = FALSE;
FILE *fp0;
size_t cset_len = 0;
size_t text_len = COLS - 38; /* cf: PutLabel */
if (LYReuseTempfiles) {
fp0 = LYOpenTempRewrite(tempfile, HTML_SUFFIX, "w");
} else {
LYRemoveTemp(tempfile);
fp0 = LYOpenTemp(tempfile, HTML_SUFFIX, "w");
}
if (fp0 == NULL) {
HTAlert(UNABLE_TO_OPEN_TEMPFILE);
return(NOT_FOUND);
}
LYLocalFileToURL(newfile, tempfile);
/* This should not be needed if we regenerate the temp file every
time with a new name, which just happened above in the case
LYReuseTempfiles==FALSE. Even for LYReuseTempfiles=TRUE, code
at the end of postoptions() may remove an older cached version
from memory if that version of the page was left by submitting
changes. (But that code doesn't do that - HTuncache_current_document
is currently commented out.) - kw 1999-11-27
If access to the actual file via getfile() later fails
(maybe because of some restrictions), mainloop may leave
this flag on after popping the previous doc which is then
unnecessarily reloaded. But I changed mainloop to reset
the flag. - kw 1999-05-24 */
LYforce_no_cache = TRUE;
/*
* Without LYUseFormsOptions set we should maybe not even get here.
* However, it's possible we do; disable the form in that case. - kw
*/
#ifndef NO_OPTION_MENU
if (!LYUseFormsOptions)
disable_all = TRUE;
#endif
BeginInternalPage(fp0, OPTIONS_TITLE, NULL); /* help link below */
/*
* I do C, not HTML. Feel free to pretty this up.
*/
fprintf(fp0,"<form action=\"LYNXOPTIONS:\" method=\"post\">\n");
/*
* use following with some sort of one shot secret key akin to NCSA
* (or was it CUTE?) telnet one shot password to allow ftp to self.
* to prevent spoofing.
*/
fprintf(fp0,"<input name=\"%s\" type=\"hidden\" value=\"%s\">\n",
secure_string, NewSecureValue());
/*
* visible text begins here
*/
/* Submit/Reset/Help */
fprintf(fp0,"<p align=center>\n");
if (!disable_all) {
fprintf(fp0,"<input type=\"submit\" value=\"%s\"> - \n", ACCEPT_CHANGES);
fprintf(fp0,"<input type=\"reset\" value=\"%s\">\n", RESET_CHANGES);
fprintf(fp0,"%s\n", CANCEL_CHANGES);
}
fprintf(fp0, "<a href=\"%s%s\">%s</a>\n",
helpfilepath, OPTIONS_HELP, TO_HELP);
/* Save options */
if (!no_option_save) {
if (!disable_all) {
fprintf(fp0, "<p align=center>%s: ", SAVE_OPTIONS);
fprintf(fp0, "<input type=\"checkbox\" name=\"%s\">\n",
save_options_string);
}
fprintf(fp0, "<br>(options marked with (!) will not be saved)\n");
}
/*
* preformatted text follows
*/
fprintf(fp0,"<pre>\n");
fprintf(fp0,"\n <em>%s</em>\n", gettext("Personal Preferences"));
/* Cookies: SELECT */
/* @@@ This is inconsistent - LYAcceptAllCookies gets saved to RC file
but LYSetCookies doesn't! */
PutLabelNotSaved(fp0, gettext("Cookies"));
BeginSelect(fp0, cookies_string);
PutOption(fp0, !LYSetCookies,
cookies_ignore_all_string,
cookies_ignore_all_string);
PutOption(fp0, LYSetCookies && !LYAcceptAllCookies,
cookies_up_to_user_string,
cookies_up_to_user_string);
PutOption(fp0, LYSetCookies && LYAcceptAllCookies,
cookies_accept_all_string,
cookies_accept_all_string);
EndSelect(fp0);
/* Editor: INPUT */
PutLabel(fp0, gettext("Editor"));
PutTextInput(fp0, editor_string, NOTEMPTY(editor), text_len,
DISABLED(no_editor || system_editor));
/* Emacs keys: ON/OFF */
PutLabel(fp0, gettext("Emacs keys"));
BeginSelect(fp0, emacs_keys_string);
PutOptValues(fp0, emacs_keys, bool_values);
EndSelect(fp0);
/* Keypad Mode: SELECT */
PutLabel(fp0, gettext("Keypad mode"));
BeginSelect(fp0, keypad_mode_string);
PutOptValues(fp0, keypad_mode, keypad_mode_values);
EndSelect(fp0);
/* Line edit style: SELECT */
if (LYLineeditNames[1]) { /* well, at least 2 line edit styles available */
PutLabel(fp0, "Line edit style");
BeginSelect(fp0, lineedit_style_string);
for (i = 0; LYLineeditNames[i]; i++) {
char temp[16];
sprintf(temp, "%d", i);
PutOption(fp0, i==current_lineedit, temp, LYLineeditNames[i]);
}
EndSelect(fp0);
}
#ifdef EXP_KEYBOARD_LAYOUT
/* Keyboard layout: SELECT */
PutLabel(fp0, "Keyboard layout");
BeginSelect(fp0, kblayout_string);
for (i = 0; LYKbLayoutNames[i]; i++) {
char temp[16];
sprintf(temp, "%d", i);
PutOption(fp0, i==current_layout, temp, LYKbLayoutNames[i]);
}
EndSelect(fp0);
#endif /* EXP_KEYBOARD_LAYOUT */
/* Mail Address: INPUT */
PutLabel(fp0, gettext("Personal mail address"));
PutTextInput(fp0, mail_address_string,
NOTEMPTY(personal_mail_address), text_len, "");
/* Search Type: SELECT */
PutLabel(fp0, gettext("Searching type"));
BeginSelect(fp0, search_type_string);
PutOptValues(fp0, case_sensitive, search_type_values);
EndSelect(fp0);
/* Show Color: SELECT */
#if defined(USE_SLANG) || defined(COLOR_CURSES)
SetupChosenShowColor();
PutLabel(fp0, gettext("Show color"));
if (no_option_save) {
MaybeSelect(fp0, !can_do_colors, show_color_string);
if (LYShowColor == SHOW_COLOR_NEVER) {
LYShowColor = SHOW_COLOR_OFF;
} else if (LYShowColor == SHOW_COLOR_ALWAYS) {
LYShowColor = SHOW_COLOR_ON;
}
PutOptValues(fp0, LYShowColor - SHOW_COLOR_OFF, bool_values);
} else {
BeginSelect(fp0, show_color_string);
if (can_do_colors) {
show_color_values[2].HtmlName = on_string;
show_color_values[3].LongName = always_string;
} else {
show_color_values[2].HtmlName = NULL; /* suppress "ON" - kw */
show_color_values[3].LongName = "Always try";
}
PutOptValues(fp0, LYChosenShowColor, show_color_values);
}
EndSelect(fp0);
#endif /* USE_SLANG || COLOR_CURSES */
/* Show cursor: ON/OFF */
PutLabel(fp0, gettext("Show cursor"));
BeginSelect(fp0, show_cursor_string);
PutOptValues(fp0, LYShowCursor, bool_values);
EndSelect(fp0);
/* User Mode: SELECT */
PutLabel(fp0, gettext("User mode"));
BeginSelect(fp0, user_mode_string);
PutOptValues(fp0, user_mode, user_mode_values);
EndSelect(fp0);
/* VI Keys: ON/OFF */
PutLabel(fp0, gettext("VI keys"));
BeginSelect(fp0, vi_keys_string);
PutOptValues(fp0, vi_keys, bool_values);
EndSelect(fp0);
/* Visited Pages: SELECT */
PutLabel(fp0, gettext("Visited Pages"));
BeginSelect(fp0, visited_pages_type_string);
PutOptValues(fp0, Visited_Links_As, visited_pages_type_values);
EndSelect(fp0);
/* Display Character Set: SELECT */
PutLabel(fp0, gettext("Display character set"));
BeginSelect(fp0, display_char_set_string);
for (i = 0; LYchar_set_names[i]; i++) {
char temp[10];
size_t len = strlen(LYchar_set_names[i]);
if (len > cset_len)
cset_len = len;
sprintf(temp, "%d", i);
#ifdef EXP_CHARSET_CHOICE
if (!charset_subsets[i].hide_display)
#endif
PutOption(fp0, i==current_char_set, temp, LYchar_set_names[i]);
}
EndSelect(fp0);
/* X Display: INPUT */
PutLabelNotSaved(fp0, gettext("X Display"));
PutTextInput(fp0, x_display_string, NOTEMPTY(x_display), text_len, "");
/*
* Document Layout
*/
fprintf(fp0,"\n <em>%s</em>\n", gettext("Document Layout"));
/* Assume Character Set: SELECT */
/* if (user_mode==ADVANCED_MODE) */
{
int curval;
curval = UCLYhndl_for_unspec;
/*
* FIXME: If bogus value in lynx.cfg, then in old way, that is the
* string that was displayed. Now, user will never see that. Good
* or bad? I don't know. MRC
*/
if (curval == current_char_set) {
/* ok, LYRawMode, so use UCAssume_MIMEcharset */
curval = safeUCGetLYhndl_byMIME(UCAssume_MIMEcharset);
}
PutLabelNotSaved(fp0, gettext("Assumed document character set"));
BeginSelect(fp0, assume_char_set_string);
for (i = 0; i < LYNumCharsets; i++) {
#ifdef EXP_CHARSET_CHOICE
if (!charset_subsets[i].hide_assumed)
#endif
PutOption(fp0, i==curval,
LYCharSet_UC[i].MIMEname,
LYCharSet_UC[i].MIMEname);
}
EndSelect(fp0);
}
/* Raw Mode: ON/OFF */
if (LYHaveCJKCharacterSet) {
/*
* Since CJK people hardly mixed with other world
* we split the header to make it more readable:
* "CJK mode" for CJK display charsets, and "Raw 8-bit" for others.
*/
PutLabelNotSaved(fp0, gettext("CJK mode"));
} else {
PutLabelNotSaved(fp0, gettext("Raw 8-bit"));
}
BeginSelect(fp0, raw_mode_string);
PutOptValues(fp0, LYRawMode, bool_values);
EndSelect(fp0);
#ifndef SH_EX /* 1999/01/19 (Tue) */
/* HTML error recovery: SELECT */
PutLabelNotSaved(fp0, gettext("HTML error recovery"));
BeginSelect(fp0, DTD_recovery_string);
PutOptValues(fp0, Old_DTD, DTD_type_values);
EndSelect(fp0);
#endif
/* Select Popups: ON/OFF */
PutLabel(fp0, gettext("Popups for select fields"));
BeginSelect(fp0, select_popups_string);
PutOptValues(fp0, LYSelectPopups, bool_values);
EndSelect(fp0);
/* Show Images: SELECT */
PutLabelNotSaved(fp0, gettext("Show images"));
BeginSelect(fp0, images_string);
PutOption(fp0, !pseudo_inline_alts && !clickable_images,
images_ignore_all_string,
images_ignore_all_string);
PutOption(fp0, pseudo_inline_alts && !clickable_images,
images_use_label_string,
images_use_label_string);
PutOption(fp0, clickable_images,
images_use_links_string,
images_use_links_string);
EndSelect(fp0);
/* Verbose Images: ON/OFF */
PutLabel(fp0, gettext("Verbose images"));
BeginSelect(fp0, verbose_images_string);
PutOptValues(fp0, verbose_img, verbose_images_type_values);
EndSelect(fp0);
/*
* Bookmark Options
*/
fprintf(fp0,"\n <em>%s</em>\n", gettext("Bookmark Options"));
/* Multi-Bookmark Mode: SELECT */
if (!LYMBMBlocked) {
PutLabel(fp0, gettext("Multi-bookmarks"));
BeginSelect(fp0, mbm_string);
PutOption(fp0, !LYMultiBookmarks,
mbm_off_string,
mbm_off_string);
PutOption(fp0, LYMultiBookmarks && !LYMBMAdvanced,
mbm_standard_string,
mbm_standard_string);
PutOption(fp0, LYMultiBookmarks && LYMBMAdvanced,
mbm_advanced_string,
mbm_advanced_string);
EndSelect(fp0);
}
/* Bookmarks File Menu: LINK/INPUT */
if (LYMultiBookmarks) {
PutLabel(fp0, gettext("Review/edit Bookmarks files"));
fprintf(fp0, "<a href=\"LYNXOPTIONS://MBM_MENU\">%s</a>\n",
gettext("Goto multi-bookmark menu"));
} else {
PutLabel(fp0, gettext("Bookmarks file"));
PutTextInput(fp0, single_bookmark_string,
NOTEMPTY(bookmark_page), text_len, "");
}
/*
* File Management Options
*/
fprintf(fp0,"\n <em>%s</em>\n", DIRED_MENU_TITLE);
/* FTP sort: SELECT */
PutLabel(fp0, gettext("FTP sort criteria"));
BeginSelect(fp0, ftp_sort_string);
PutOptValues(fp0, HTfileSortMethod, ftp_sort_values);
EndSelect(fp0);
#ifdef DIRED_SUPPORT
/* Local Directory Sort: SELECT */
PutLabel(fp0, gettext("Local directory sort criteria"));
BeginSelect(fp0, dired_sort_string);
PutOptValues(fp0, dir_list_style, dired_values);
EndSelect(fp0);
#endif /* DIRED_SUPPORT */
/* Show dot files: ON/OFF */
if (!no_dotfiles) {
PutLabel(fp0, gettext("Show dot files"));
BeginSelect(fp0, show_dotfiles_string);
PutOptValues(fp0, show_dotfiles, bool_values);
EndSelect(fp0);
}
/* Execution links: SELECT */
#if defined(ENABLE_OPTS_CHANGE_EXEC) && (defined(EXEC_LINKS) || defined(EXEC_SCRIPTS))
PutLabel(fp0, gettext("Execution links"));
BeginSelect(fp0, exec_links_string);
#ifndef NEVER_ALLOW_REMOTE_EXEC
PutOptValues(fp0, local_exec
? EXEC_ALWAYS
: (local_exec_on_local_files
? EXEC_LOCAL
: EXEC_NEVER),
exec_links_values);
#else
PutOptValues(fp0, local_exec_on_local_files
? EXEC_LOCAL
: EXEC_NEVER,
exec_links_values);
#endif /* !NEVER_ALLOW_REMOTE_EXEC */
EndSelect(fp0);
#endif /* ENABLE_OPTS_CHANGE_EXEC */
/*
* Headers transferred to remote server
*/
fprintf(fp0,"\n <em>%s</em>\n", gettext("Headers transferred to remote server"));
/* Preferred Document Character Set: INPUT */
PutLabel(fp0, gettext("Preferred document character set"));
PutTextInput(fp0, preferred_doc_char_string,
NOTEMPTY(pref_charset), cset_len+2, "");
/* Preferred Document Language: INPUT */
PutLabel(fp0, gettext("Preferred document language"));
PutTextInput(fp0, preferred_doc_lang_string,
NOTEMPTY(language), cset_len+2, "");
/* User Agent: INPUT */
if (!no_useragent) {
PutLabelNotSaved(fp0, gettext("User-Agent header"));
PutTextInput(fp0, user_agent_string,
NOTEMPTY(LYUserAgent), text_len, "");
}
if (!no_lynxcfg_info) {
fprintf(fp0,
"\n Check your <a href=\"LYNXCFG:\">lynx.cfg</a> here\n");
}
fprintf(fp0,"\n</pre>\n");
/* Submit/Reset */
if (!disable_all) {
fprintf(fp0,"<p align=center>\n");
fprintf(fp0,"<input type=\"submit\" value=\"%s\">\n - ", ACCEPT_CHANGES);
fprintf(fp0,"<input type=\"reset\" value=\"%s\">\n", RESET_CHANGES);
fprintf(fp0,"%s\n", CANCEL_CHANGES);
}
/*
* close HTML
*/
fprintf(fp0,"</form>\n");
EndInternalPage(fp0);
LYCloseTempFP(fp0);
return(NORMAL);
}
#endif /* !NO_OPTION_FORMS */