summary refs log tree commit diff stats
path: root/compiler/passes.nim
Commit message (Collapse)AuthorAgeFilesLines
* bug fixes with sfMainModule, hints, mainPackageNotes, mainPackageId, ↵Timothee Cour2020-06-041-3/+1
| | | | | | | hintSuccessX (#14555) * SuccessX `out` now works with --compileOnly and jsonscript; fix bugs in jsonscript * several bug fixes; eg: `nim doc lib/system/io` now is sane * dummy edit to force docs CI
* Cosmetic compiler cleanup (#12718)Clyybber2019-11-281-7/+7
| | | | | | | | | | | | | | | | | | * Cleanup compiler code base * Unify add calls * Unify len invocations * Unify range operators * Fix oversight * Remove {.procvar.} pragma * initCandidate -> newCandidate where reasonable * Unify safeLen calls
* Incremental compilation (IC): Improvements (#11881)Andreas Rumpf2019-08-081-2/+4
| | | | | | | | | | | | | | | | | | | * IC: C codegen is aware of IC * manual: minor change to make VSCode's RST plugin render it properly * IC: minor refactoring * testament: code refactorings * rodutils: removed dead code * IC: always build the compiler with the IC feature * IC: C codegen improvements * IC: implement the undocumented -d:nimMustCache option for testing purposes * IC: added first basic tests * IC: extensive testing of the deserialization feature * testament: refactoring; better IC tests * IC: removes 'nimMustCache' flag; readonly does the same * testament: minor refactoring * update Nimble version * testament: removed dead code and imports; IC: added simple test * IC: progress
* IR: minor refactoringAndreas Rumpf2019-07-271-1/+4
|
* IC: some progressAndreas Rumpf2019-07-271-0/+9
|
* [refactoring] remove unused imports in the compiler and in some stdlib modulesAraq2019-07-181-3/+3
|
* better run [feature] (#11709)Andreas Rumpf2019-07-111-1/+0
| | | | | | | * track the checksums of all involved Nim files for smarter 'nim c -r' recompiles * don't recompile unless necessary for 'nim c -r' [feature] * [feature] koch boot uses a two step process in order to free the RAM before the GCC/Clang invocations * fixes a serious regression
* styleCheck: make the compiler and large parts of the stdlib compatible with ↵Araq2019-07-101-1/+1
| | | | --styleCheck:error
* Replace countup(x, y-1) with x ..< yClyybber2019-05-071-3/+3
|
* fixes 'nimble install nimble' problemsAndreas Rumpf2019-03-261-2/+3
|
* Initial version of the hot-code reloading support for native targets (#10729)zah2019-02-261-2/+2
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * squashed work by Zahary * squashing a ton of useful history... otherwise rebasing on top of upstream Nim after commit 82c009a2cbc5d07ab9a847f1c58228a20efaf219 would be impossible. * Code review changes; Working test suite (without code reloading enabled) * - documentation - implemented the HCR test - almost works... - fix the issue on Unix where for executable targets the source file for the main module of a project in nimcache was being overwritten with the binary itself (and thus the actual source code was lost) - fixing embedded paths to shared objects on unix (the "lib" prefix was being prepended to the entire path instead of just the filename) - other fixes - removing unnecessary includes since that file is already included in chcks.nim which is in turn included in system.nim (and previously was getting imported in chcks.nim but then system.nim improts something... and that breaks HCR (perhaps it could be fixed but it would be nice not to import anything in system)) * fix for clang & C++ - explicitly casting a function pointer to void* more stable mangling of parameter names when HCR is on the length of the static arrays in the DatInit functions is now part of the name of the variables, so when they get resized they get also recreated more stable mangling for inline functions - no longer depends on the module which first used them work on the new complicated HCR test - turned surprisingly complex - WIP test now successfully passes even when re-running `koch test` (previously when the nimcache wasn't cold that lead to errors) better documentation calling setStackBottomWith for PreMain passes over the HcrInit/DatInit/Init calls of all modules are now in the proper order (first all of one type, then all of the next). Also typeinfo globals are registered (created) in a single pass before the DatInit pass (because of the way generic instantiations are handled) Fix the test suite execution on macOs fix for being able to query the program arguments when using HCR on posix! other fixes * Bugfix: Fix a compilation error in C++ mode when a function pointer is converted to a raw pointer * basic documentation for the new hot code reloading semantics * Add change log entry * Don't re-execute the top-level statements while reloading JS code * fix a number of tests broken in a recent bugfix * Review changes * Added {.executeOnReload.} pragma that indicates top-level statements that should be executed on each reload. To make this work, I've modified the way the `if (hcr_init_) {...}` guards are produced in the init code. This still needs more work as the new guards seem to be inserted within the previously generated guards. This change also removes the need for `lastRegistedGlobal` in nimhcr. * Implemented the `signatureHash` magic and the `hasModuleChanged` API depending on it (the actual logic is not imlemented yet). * Add the "hcr" prefix to all HCR-related symbols in the system module. Added a new `hotcodereloading` module exporting the high-level API to the user. Besides being more hygienic, this was also required in order to make it possible to use macros in the high-level API. Without the split, `system` would have to import `macros`, which was going to produce the well-known init problems. * Attempted to solve the "GC markers problem". Crashes were expected with the previous code, because the GC markers were compiled as normal procs are registered in the GC. When their module is unloaded, dangling pointers will remain in the GC tables. To solve this issue, I don't register any GC markers when HCR is on, but I add them to the HCR globals metadata and I use a single marker registed in nimhcr during the initialization of the system module that will be responsible for marking all globals. * fix a compilation error * - implemented the hasModuleChanged functionality - tuples can be returned and broken into different vars in global scope - added comments for the closnig scopes of the if statements in the init proc - the new executeOnReload pragma works now! - other fixes * finally! fixing this hack in a proper way - declaring the destructor out of line (out of the class body) - we no longer need to forward-declare popCurrentExceptionEx * Force full module parsing This is a temporary hack that breaks some tests. I'll investigate later how these can be fixed. * tuples are now properly handled when global! * these comments mess up the codegen in debug mode when $n is not actually a new line (or something like that) - these labels are intended only for GOTO labels anyway... * "solved" the issue with the .pdb locks on windows when a binary is being debugged and hot code reloading is used at the same time * fixes after rebasing... * small fixes for the test * better handling of globals! no more compiler crashes for locals with the global pragma, also simplified code around loops in global scope which have local vars (actually globals) * we can now use the global pragma even for ... globals! * the right output * lets try those boehm GC tests * after the test is ran it will be at its starting state - no git modifications * clarification in the docs * removed unnecessary line directives for forward declarations of functions - they were causing trouble with hot code reloading when no semantic change propagates to the main module but a line directive got changed and thus the main module had to be recompiled since the .c code had changed * fixed bug! was inserting duplicate keys into the table and later was removing only 1 copy of all the duplicates (after a few reloads) * no longer breaking into DatInit code when not supposed to * fixes after rebasing * yet more fixes after rebasing * Update jssys.nim * Rework the HCR path-handling logic After reviewing the code more carefully, I've noticed that the old logic will be broken when the user overrides the '--out:f' compiler option. Besides fixing this issues, I took the opportunity to implement the missing '--outdir:d' option. Other changes: * ./koch test won't overwrite any HCR and RTL builds located in nim/lib * HCR and RTL are compiled with --threads:on by default * Clean up the globals registration logic * Handle non-flattened top-level stmtlists in JS as well * The HCR is not supported with the Boehm GC yet Also fixes some typos and the expected output of the HCR integration test * The GC marker procs are now properly used as trampolines * Fix the HCR integration test in release builds * Fix ./koch tools * this forward declaration doesn't seem to be necessary, and in fact breaks HCR because a 2nd function pointer is emitted for this externed/rtl func * the forward declaration I removed in the last commit was actually necessary * Attempt to make all tests green * Fix tgenscript * BAT file for running the HCR integration test on Windows [skip ci] * Fix the docgen tests * A final fix for Travis (hopefully)
* fixes #10606Andreas Rumpf2019-02-131-2/+4
|
* fixes #10024Araq2018-12-221-1/+1
|
* fixes #9991Araq2018-12-161-1/+1
|
* fixes #9994Andreas Rumpf2018-12-151-2/+2
|
* fixes #9978Andreas Rumpf2018-12-141-1/+1
|
* Make the registered passes local to the ModuleGraph (#9259)LemonBoy2018-10-091-49/+32
| | | Closes #9068
* make tests green againAndreas Rumpf2018-09-241-1/+5
|
* enable destructors for top level statements; needs to be documentedAndreas Rumpf2018-09-241-1/+20
|
* compiler refactoring; use typesafe path handing; docgen: render symbols ↵Andreas Rumpf2018-09-071-3/+3
| | | | between modules
* refactorings in preparations for the new runtimeAndreas Rumpf2018-07-101-6/+2
|
* avoid AST streaming, experiment what it breaksAraq2018-07-091-2/+6
|
* incremental compilation: implemented basic replay logicAndreas Rumpf2018-06-021-57/+15
|
* baby steps for incremental compilationAndreas Rumpf2018-05-301-12/+3
|
* refactoring: move DB model to incremental.nimAndreas Rumpf2018-05-301-1/+1
|
* more compiler API cleanupsAndreas Rumpf2018-05-281-2/+2
|
* remove more global variables in the Nim compilerAndreas Rumpf2018-05-271-1/+1
|
* preparations of making compiler/msgs.nim free of global variablesAndreas Rumpf2018-05-171-2/+2
|
* move more global variables into ConfigRefAndreas Rumpf2018-05-111-2/+2
|
* big refactoring: mores stuff compilesAndreas Rumpf2018-05-101-11/+13
|
* compiler refactoring, pass config around explicitlyAndreas Rumpf2018-05-051-1/+1
|
* compiler: remove unnecessary FileIndex type conversionsAndreas Rumpf2018-04-241-1/+1
|
* refactoring: make FileIndex a distinct type; make line information an ↵Andreas Rumpf2018-04-211-19/+5
| | | | uint16; fixes #7654
* symbol files: implemented accurate module dependency trackingAraq2018-02-211-0/+2
|
* symbol files: more progressAraq2018-02-201-6/+36
|
* symbol files: fixes the logic for multi-methodsAraq2018-01-031-2/+2
|
* Add sections (type, var, let, const, using) support for reorder pragma (#6326)BigEpsilon2017-10-281-1/+2
|
* first implementation of the 'func' keywordAndreas Rumpf2017-09-231-1/+1
|
* distinguish between 'reorder' and 'noforward'Andreas Rumpf2017-07-261-2/+2
|
* implemented reordering passAndreas Rumpf2017-07-251-1/+3
|
* Merge branch 'devel' into faster-nimsuggestAndreas Rumpf2017-02-241-1/+1
|\
| * removed compiler internal list implementation (#5371)Arne Döring2017-02-221-1/+1
| |
* | big compiler refactoring; avoid globals for multi method dispatcher generationAndreas Rumpf2017-02-221-8/+8
| |
* | nimsuggest uses multithreading and full project recompilesAndreas Rumpf2017-02-131-1/+5
|/
* make tests green againAndreas Rumpf2016-11-241-8/+19
|
* new dependency tracking for nimsuggestAraq2016-11-051-16/+19
|
* big refactoring: step 1Araq2016-10-311-12/+13
|
* nimsuggest improvementsAndreas Rumpf2016-08-291-9/+4
|
* implemented {.noforward:on.} for type sections; useful for c2nim generated ↵Araq2015-07-081-2/+11
| | | | wrappers; to be documented
* Clean up stdin file reading of compiler.def2015-05-161-5/+1
| | | | | Uses "stdinfile" as the module identifier now, which prevents problems with - colliding with the minus operator. This fixes #2422 and #2702.
'>1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478
#include "HTUtils.h"
#include "tcp.h"
#include "HTCJK.h"
#include "HTTP.h"
#include "HTAlert.h"
#include "LYCurses.h"
#include "GridText.h"
#include "LYUtils.h"
#include "LYStructs.h"  /* includes HTForms.h */
#include "LYStrings.h"
#include "LYGlobalDefs.h"
#include "LYKeymap.h"
#include "LYSignal.h"

#include "LYLeaks.h"

#ifdef USE_COLOR_STYLE
#include "AttrList.h"
#include "LYHash.h"
#endif

extern HTCJKlang HTCJK;

PRIVATE int form_getstr PARAMS((
	struct link *	form_link));
PRIVATE int popup_options PARAMS((
	int		cur_selection,
	OptionType *	list,
	int		ly,
	int		lx,
	int		width,
	int		i_length,
	int		disabled));

PUBLIC int change_form_link ARGS6(
	struct link *,	form_link,
	int,		mode,
	document *,	newdoc,
	BOOLEAN *,	refresh_screen,
	char *,		link_name,
	char *,		link_value)
{
    FormInfo *form = form_link->form;
    int c = DO_NOTHING;
    int OrigNumValue;

	/*
	 *  If there is no form to perform action on, don't do anything.
	 */
	if (form == NULL) {
	    return(c);
	}

    /*
     *  Move to the link position.
     */
    move(form_link->ly, form_link->lx);

    switch(form->type) {
	case F_CHECKBOX_TYPE:
	    if (form->disabled == YES)
		break;
	    if (form->num_value) {
		form_link->hightext = unchecked_box;
		form->num_value = 0;
	    } else {
		form_link->hightext = checked_box;
		form->num_value = 1;
	    }
	    break;

	case F_OPTION_LIST_TYPE:
	    if (!form->select_list) {
		HTAlert(BAD_HTML_NO_POPUP);
		c = DO_NOTHING;
		break;
	    }

	    if (form->disabled == YES) {
		int dummy;
		dummy = popup_options(form->num_value, form->select_list,
				form_link->ly, form_link->lx, form->size,
				form->size_l, form->disabled);
#if defined(FANCY_CURSES) || defined(USE_SLANG)
		if (!enable_scrollback)
#if defined(VMS) && !defined(USE_SLANG)
		    c = DO_NOTHING;
#else
		    c = 23;  /* CTRL-W for repaint */
#endif /* VMS && !USE_SLANG */
		else
#endif /* FANCY_CURSES || USE_SLANG */
		    c = 12;  /* CTRL-L for repaint */
		break;
	    }
	    OrigNumValue = form->num_value;
	    form->num_value = popup_options(form->num_value, form->select_list,
				form_link->ly, form_link->lx, form->size,
				form->size_l, form->disabled);

	    {
		OptionType * opt_ptr = form->select_list;
		int i;
		for (i = 0; i < form->num_value; i++, opt_ptr = opt_ptr->next)
		    ; /* null body */
		/*
		 *  Set the name.
		 */
		form->value = opt_ptr->name;
		/*
		 *  Set the value.
		 */
		form->cp_submit_value = opt_ptr->cp_submit_value;
	    }
#if defined(FANCY_CURSES) || defined(USE_SLANG)
	    if (!enable_scrollback && form->num_value == OrigNumValue)
#if defined(VMS) && !defined(USE_SLANG)
		c = DO_NOTHING;
#else
		c = 23;  /* CTRL-W for repaint */
#endif /* VMS && !USE_SLANG */
	    else
#endif /* FANCY_CURSES || USE_SLANG */
		c = 12;  /* CTRL-L for repaint */
	    break;

	case F_RADIO_TYPE:
	    if (form->disabled == YES)
		break;
		/*
		 *  Radio buttons must have one and
		 *  only one down at a time!
		 */
	    if (form->num_value) {
		_statusline(NEED_CHECKED_RADIO_BUTTON);
		sleep(MessageSecs);

	    } else {
		int i;
		/*
		 *  Run though list of the links on the screen and
		 *  unselect any that are selected. :)
		 */
		lynx_start_radio_color ();
		for (i = 0; i < nlinks; i++) {
		    if (links[i].type == WWW_FORM_LINK_TYPE &&
			links[i].form->type == F_RADIO_TYPE &&
			links[i].form->number == form->number &&
			/*
			 *  If it has the same name and its on...
			 */
			!strcmp(links[i].form->name, form->name) &&
			links[i].form->num_value) {
			move(links[i].ly, links[i].lx);
			addstr(unchecked_radio);
			links[i].hightext = unchecked_radio;
		    }
		}
		lynx_stop_radio_color ();
		/*
		 *  Will unselect other button and select this one.
		 */
		HText_activateRadioButton(form);
		/*
		 *  Now highlight this one.
		 */
		form_link->hightext = checked_radio;
	    }
	    break;

	case F_TEXT_TYPE:
	case F_TEXTAREA_TYPE:
	case F_PASSWORD_TYPE:
	    c = form_getstr(form_link);
	    if (form->type == F_PASSWORD_TYPE)
		form_link->hightext = STARS(strlen(form->value));
	    else
		form_link->hightext = form->value;
	    break;

	case F_RESET_TYPE:
	    if (form->disabled == YES)
		break;
	    HText_ResetForm(form);
	    *refresh_screen = TRUE;
	    break;

	case F_TEXT_SUBMIT_TYPE:
	    c = form_getstr(form_link);
	    if (form->disabled == YES &&
		(c == '\r' || c == '\n')) {
		c = '\t';
		break;
	    }
	    if (c == '\r' || c == '\n') {
		form_link->hightext = form->value;
		if (!form->submit_action || *form->submit_action == '\0') {
		    _statusline(NO_FORM_ACTION);
		    sleep(MessageSecs);
		    c = DO_NOTHING;
		    break;
		} else if (form->submit_method == URL_MAIL_METHOD && no_mail) {
		    HTAlert(FORM_MAILTO_DISALLOWED);
		    c = DO_NOTHING;
		    break;
		} else {
		    if (form->no_cache &&
			form->submit_method != URL_MAIL_METHOD) {
			LYforce_no_cache = TRUE;
			reloading = TRUE;
		    }
		    HText_SubmitForm(form, newdoc, link_name, form->value);
		}
		if (form->submit_method == URL_MAIL_METHOD) {
		    *refresh_screen = TRUE;
		} else {
		    /*
		     *  Returns new document URL.
		     */
		    newdoc->link = 0;
		    newdoc->internal_link = FALSE;
		}
		c = DO_NOTHING;
		break;
	    } else {
		form_link->hightext = form->value;
	    }
	    break;

	case F_SUBMIT_TYPE:
	case F_IMAGE_SUBMIT_TYPE:
	    if (form->disabled == YES)
		break;
	    if (form->no_cache &&
		form->submit_method != URL_MAIL_METHOD) {
		LYforce_no_cache = TRUE;
		reloading = TRUE;
	    }
	    HText_SubmitForm(form, newdoc, link_name, link_value);
	    if (form->submit_method == URL_MAIL_METHOD)
		*refresh_screen = TRUE;
	    else {
		/* returns new document URL */
		newdoc->link = 0;
		newdoc->internal_link = FALSE;
	    }
	    break;

    }

    return(c);
}

PRIVATE int form_getstr ARGS1(
	struct link *,	form_link)
{
    FormInfo *form = form_link->form;
    char *value = form->value;
    int ch;
    int far_col;
    int max_length;
    int startcol, startline;
    BOOL HaveMaxlength = FALSE;
    int action;

#ifdef VMS
    extern BOOLEAN HadVMSInterrupt;	/* Flag from cleanup_sig() AST */
#endif

    EditFieldData MyEdit;
    BOOLEAN Edited = FALSE;		/* Value might be updated? */

    /*
     *  Get the initial position of the cursor.
     */
    LYGetYX(startline, startcol);
    if ((startcol + form->size) > (LYcols - 1))
	far_col = (LYcols - 1);
    else
	far_col = (startcol + form->size);

    /*
     *  Make sure the form field value does not exceed our buffer. - FM
     */
    max_length = ((form->maxlength > 0 &&
		   form->maxlength < sizeof(MyEdit.buffer)) ?
					    form->maxlength :
					    (sizeof(MyEdit.buffer) - 1));
    if (strlen(form->value) > max_length) {
	/*
	 *  We can't fit the entire value into the editing buffer,
	 *  so enter as much of the tail as fits. - FM
	 */
	value += (strlen(form->value) - max_length);
	if (!form->disabled &&
	    !(form->submit_method == URL_MAIL_METHOD && no_mail)) {
	    /*
	     *  If we can edit it, report that we are using the tail. - FM
	     */
	    _statusline(FORM_VALUE_TOO_LONG);
	    sleep(MessageSecs);
	    switch(form->type) {
		case F_PASSWORD_TYPE:
		    statusline(FORM_LINK_PASSWORD_MESSAGE);
		    break;
		case F_TEXT_SUBMIT_TYPE:
		    if (form->submit_method == URL_MAIL_METHOD) {
			statusline(FORM_LINK_TEXT_SUBMIT_MAILTO_MSG);
		    } else if (form->no_cache) {
			statusline(FORM_LINK_TEXT_RESUBMIT_MESSAGE);
		    } else {
			statusline(FORM_LINK_TEXT_SUBMIT_MESSAGE);
		    }
		    break;
		case F_TEXT_TYPE:
		case F_TEXTAREA_TYPE:
		    statusline(FORM_LINK_TEXT_MESSAGE);
		    break;
		default:
		    break;
	    }
	    move(startline, startcol);
	}
    }

    /*
     *  Print panned line
     */
    LYSetupEdit(&MyEdit, value, max_length, (far_col - startcol));
    MyEdit.pad = '_';
    MyEdit.hidden = (form->type == F_PASSWORD_TYPE);
    LYRefreshEdit(&MyEdit);

    /*
     *  And go for it!
     */
    for (;;) {
again:
	ch = LYgetch();
#ifdef VMS
	if (HadVMSInterrupt) {
	    HadVMSInterrupt = FALSE;
	    ch = 7;
	}
#endif /* VMS */

	/*
	 *  Filter out global navigation keys that should not be passed
	 *  to line editor, and LYK_REFRESH.
	 */
	action = EditBinding(ch);
	if (action == LYE_ENTER)
	    break;
	if (action == LYE_AIX && HTCJK == NOCJK)
	    break;
	if (action == LYE_TAB) {
	    ch = (int)('\t');
	    break;
	}
	if (action == LYE_ABORT) {
	    return(DO_NOTHING);
	}
	if (keymap[ch + 1] == LYK_REFRESH)
	    goto breakfor;
	switch (ch) {
	    case DNARROW:
	    case UPARROW:
	    case PGUP:
	    case PGDOWN:
#ifdef NOTDEFINED
	    case HOME:
	    case END:
	    case FIND_KEY:
	    case SELECT_KEY:
#endif /* NOTDEFINED */
		goto breakfor;

	    /*
	     *  Left arrrow in column 0 deserves special treatment here,
	     *  else you can get trapped in a form without submit button!
	     */
	    case LTARROW:
		if (MyEdit.pos == 0) {
		    int c = 'Y';    /* Go back immediately if no changes */
		    if (strcmp(MyEdit.buffer, value)) {
			_statusline(PREV_DOC_QUERY);
			c = LYgetch();
		    }
		    if (TOUPPER(c) == 'Y') {
			return(ch);
		    } else {
			if (form->disabled == YES)
			    _statusline(ARROWS_OR_TAB_TO_MOVE);
			else
			    _statusline(ENTER_TEXT_ARROWS_OR_TAB);
		    }
		}
		/* fall through */

	    default:
		if (form->disabled == YES)
		    goto again;
		/*
		 *  Make sure the statusline uses editmode help.
		 */
		LYLineEdit(&MyEdit, ch, TRUE);
		if (MyEdit.strlen >= max_length) {
		    HaveMaxlength = TRUE;
		} else if (HaveMaxlength &&
			   MyEdit.strlen < max_length) {
		    HaveMaxlength = FALSE;
		    _statusline(ENTER_TEXT_ARROWS_OR_TAB);
		}
		if (strcmp(value, MyEdit.buffer)) {
		    Edited = TRUE;
		}
		LYRefreshEdit(&MyEdit);
	}
    }
breakfor:
    if (Edited) {
	char  *p;

	/*
	 *  Load the new value.
	 */
	if (value == form->value) {
	    /*
	     *  The previous value did fit in the line buffer,
	     *  so replace it with the new value. - FM
	     */
	    StrAllocCopy(form->value, MyEdit.buffer);
	} else {
	    /*
	     *  Combine the modified tail with the unmodified head. - FM
	     */
	    form->value[(strlen(form->value) - strlen(value))] = '\0';
	    StrAllocCat(form->value, MyEdit.buffer);
	    _statusline(FORM_TAIL_COMBINED_WITH_HEAD);
	    sleep(MessageSecs);
	}

	/*
	 *  Remove trailing spaces
	 *
	 *  Do we really need to do that here? Trailing spaces will only
	 *  be there if user keyed them in. Rather rude to throw away
	 *  their hard earned spaces. Better deal with trailing spaces
	 *  when submitting the form????
	 */
	p = &(form->value[strlen(form->value)]);
	while ((p != form->value) && (p[-1] == ' '))
	    p--;
	*p = '\0';
    }
    return(ch);
}

/*
**  This function prompts for an option or page number.
**  If a 'g' or 'p' suffix is included, that will be
**  loaded into c.  Otherwise, c is zeroed. - FM & LE
*/
PRIVATE int get_popup_option_number ARGS1(
	int *,		c)
{
    char temp[120];

    /*
     *  Load the c argument into the prompt buffer.
     */
    temp[0] = *c;
    temp[1] = '\0';
    _statusline(SELECT_OPTION_NUMBER);

    /*
     *  Get the number, possibly with a suffix, from the user.
     */
    if (LYgetstr(temp, VISIBLE, sizeof(temp), NORECALL) < 0 || *temp == 0) {
	_statusline(CANCELLED);
	sleep(InfoSecs);
	*c = '\0';
	return(0);
    }

    /*
     *  If we had a 'g' or 'p' suffix, load it into c.
     *  Otherwise, zero c.  Then return the number.
     */
    if (strchr(temp, 'g') != NULL || strchr(temp, 'G') != NULL) {
	*c = 'g';
    } else if (strchr(temp, 'p') != NULL || strchr(temp, 'P') != NULL) {
	*c = 'p';
    } else {
	*c = '\0';
    }
    return(atoi(temp));
}

/* Use this rather than the 'wprintw()' function to write a blank-padded
 * string to the given window, since someone's asserted that printw doesn't
 * handle 8-bit characters unlike addstr (though more info would be useful).
 *
 * We're blank-filling so that with SVr4 curses, it'll show the background
 * color to a uniform width in the popup-menu.
 */
#ifndef USE_SLANG
PRIVATE void paddstr ARGS3(
	WINDOW *,	the_window,
	int,		width,
	char *, 	the_string)
{
    width -= strlen(the_string);
    waddstr(the_window, the_string);
    while (width-- > 0)
	waddstr(the_window, " ");
}
#endif


PRIVATE int popup_options ARGS7(
	int,		cur_selection,
	OptionType *,	list,
	int,		ly,
	int,		lx,
	int,		width,
	int,		i_length,
	int,		disabled)
{
    /*
     *  Revamped to handle within-tag VALUE's, if present,
     *  and to position the popup window appropriately,
     *  taking the user_mode setting into account. -- FM
     */
    int c = 0, cmd = 0, i = 0, j = 0;
    int orig_selection = cur_selection;
#ifndef USE_SLANG
    WINDOW * form_window;
#endif /* !USE_SLANG */
    int num_options = 0, top, bottom, length = -1;
    OptionType * opt_ptr = list;
    int window_offset = 0;
    int display_lines;
    int npages;
#ifdef VMS
    extern BOOLEAN HadVMSInterrupt; /* Flag from cleanup_sig() AST */
#endif /* VMS */
    static char prev_target[512];		/* Search string buffer */
    static char prev_target_buffer[512];	/* Next search buffer */
    static BOOL first = TRUE;
    char *cp;
    int ch = 0, recall;
    int QueryTotal;
    int QueryNum;
    BOOLEAN FirstRecall = TRUE;
    OptionType * tmp_ptr;
    BOOLEAN ReDraw = FALSE;
    int number;
    char buffer[512];

    /*
     * Initialize the search string buffer. - FM
     */
    if (first) {
	*prev_target_buffer = '\0';
	first = FALSE;
    }
    *prev_target = '\0';
    QueryTotal = (search_queries ? HTList_count(search_queries) : 0);
    recall = ((QueryTotal >= 1) ? RECALL : NORECALL);
    QueryNum = QueryTotal;

    /*
     *  Set display_lines based on the user_mode global.
     */
    if (user_mode == NOVICE_MODE)
	display_lines = LYlines-4;
    else
	display_lines = LYlines-2;

    /*
     *  Counting the number of options to be displayed.
     *   num_options ranges 0...n
     */
    for (; opt_ptr->next; num_options++, opt_ptr = opt_ptr->next)
	 ; /* null body */

    /*
     *  Let's assume for the sake of sanity that ly is the number
     *   corresponding to the line the selection box is on.
     *  Let's also assume that cur_selection is the number of the
     *   item that should be initially selected, as 0 beign the
     *   first item.
     *  So what we have, is the top equal to the current screen line
     *   subtracting the cur_selection + 1 (the one must be for the
     *   top line we will draw in a box).  If the top goes under 0,
     *   consider it 0.
     */
    top = ly - (cur_selection + 1);
    if (top < 0)
	top = 0;

    /*
     *  Check and see if we need to put the i_length parameter up to
     *  the number of real options.
     */
    if (!i_length) {
	i_length = num_options;
    } else {
	/*
	 *  Otherwise, it is really one number too high.
	 */
	i_length--;
    }

    /*
     *  The bottom is the value of the top plus the number of options
     *  to view plus 3 (one for the top line, one for the bottom line,
     *  and one to offset the 0 counted in the num_options).
     */
    bottom = top + i_length + 3;

    /*
     *  Hmm...  If the bottom goes beyond the number of lines available,
     */
    if (bottom > display_lines) {
	/*
	 *  Position the window at the top if we have more
	 *  options than will fit in the window.
	 */
	if (i_length+3 > display_lines) {
	    top = 0;
	    bottom = top + i_length+3;
	    if (bottom > display_lines)
		bottom = display_lines + 1;
	} else {
	    /*
	     *  Try to position the window so that the selected option will
	     *    appear where the selection 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 = (display_lines + 1) - (i_length + 3);
	    bottom = (display_lines + 1);
	}
    }

    /*
     *  This is really fun, when the length is 4, it means 0-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 > display_lines + 1)
	    bottom = display_lines + 1;
	top = bottom - length - 2;
    }

    /*
     *  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, width + 4, ' ');
#else
    if (!(form_window = newwin(bottom - top, width + 4, top, lx - 1)) &&
	!(form_window = newwin(bottom - top, 0, top, 0))) {
	HTAlert(POPUP_FAILED);
	return(orig_selection);
    }
    scrollok(form_window, TRUE);
#ifdef NCURSES
#ifdef wgetbkgd
#define getbkgd(w) wgetbkgd(w)	/* workaround pre-1.9.9g bug */
#endif
    LYsubwindow(form_window);
#endif
#if defined(COLOR_CURSES)	/* not defined in ncurses 1.8.7 */
    wbkgd(form_window, getbkgd(stdscr));
    wbkgdset(form_window, getbkgd(stdscr));
#endif
#endif /* USE_SLANG */

    /*
     *  Set up the window_offset for options.
     *   cur_selection ranges from 0...n
     *   length ranges from 0...m
     */
    if (cur_selection >= length) {
	window_offset = cur_selection - length + 1;
    }

    /*
     *  Compute the number of popup window pages. - FM
     */
    npages = ((num_options + 1) > length) ?
		(((num_options + 1) + (length - 1))/(length))
					  : 1;
/*
 * OH!  I LOVE GOTOs! hack hack hack
 *        07-11-94 GAB
 *      MORE hack hack hack
 *        09-05-94 FM
 */
redraw:
    opt_ptr = list;

    /*
     *  Display the boxed options.
     */
    for (i = 0; i <= num_options; i++, opt_ptr = opt_ptr->next) {
	if (i >= window_offset && i - window_offset < length) {
#ifdef USE_SLANG
	    SLsmg_gotorc(top + ((i + 1) - window_offset), (lx - 1 + 2));
	    SLsmg_write_nstring(opt_ptr->name, width);
#else
	    wmove(form_window, ((i + 1) - window_offset), 2);
	    paddstr(form_window, width, opt_ptr->name);
#endif /* USE_SLANG */
	}
    }
#ifdef USE_SLANG
    SLsmg_draw_box(top, (lx - 1), (bottom - top), (width + 4));
#else
#ifdef VMS
    VMSbox(form_window, (bottom - top), (width + 4));
#else
#ifdef CSS
    wcurses_css(form_window, "frame", ABS_ON);
    box(form_window, BOXVERT, BOXHORI);
    wcurses_css(form_window, "frame", ABS_OFF);
#else
    box(form_window, BOXVERT, BOXHORI);
#endif
#endif /* VMS */
    wrefresh(form_window);
#endif /* USE_SLANG */
    opt_ptr = NULL;

    /*
     *  Loop on user input.
     */
    while (cmd != LYK_ACTIVATE) {

	/*
	 *  Unreverse cur selection.
	 */
	if (opt_ptr != NULL) {
#ifdef USE_SLANG
	    SLsmg_gotorc((top + ((i + 1) - window_offset)), (lx - 1 + 2));
	    SLsmg_write_nstring(opt_ptr->name, width);
#else
	    wmove(form_window, ((i + 1) - window_offset), 2);
	    paddstr(form_window, width, opt_ptr->name);
#endif /* USE_SLANG */
	}

	opt_ptr = list;

	for (i = 0; i < cur_selection; i++, opt_ptr = opt_ptr->next)
	    ; /* null body */

#ifdef USE_SLANG
	SLsmg_set_color(2);
	SLsmg_gotorc((top + ((i + 1) - window_offset)), (lx - 1 + 2));
	SLsmg_write_nstring(opt_ptr->name, width);
	SLsmg_set_color(0);
	/*
	 *  If LYShowCursor is ON, move the cursor to the left
	 *  of the current option, 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 option 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 option'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
	wstart_reverse(form_window);
	wmove(form_window, ((i + 1) - window_offset), 2);
	paddstr(form_window, width, opt_ptr->name);
	wstop_reverse(form_window);
	/*
	 *  If LYShowCursor is ON, move the cursor to the left
	 *  of the current option, 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 option is made current.  Otherwise, leave it to
	 *  the right of the current option, since we can't move
	 *  it out of the window, and let sighted users rely on
	 *  the highlighting of the current option 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  */

	c = LYgetch();
	if (c == 3 || c == 7)	/* Control-C or Control-G */
	    cmd = LYK_QUIT;
	else
	    cmd = keymap[c+1];
#ifdef VMS
	if (HadVMSInterrupt) {
	    HadVMSInterrupt = FALSE;
	    cmd = LYK_QUIT;
	}
#endif /* VMS */

	switch(cmd) {
	    case LYK_F_LINK_NUM:
		c = '\0';
	    case LYK_1:
	    case LYK_2:
	    case LYK_3:
	    case LYK_4:
	    case LYK_5:
	    case LYK_6:
	    case LYK_7:
	    case LYK_8:
	    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_option_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) {
			    _statusline(ALREADY_AT_OPTION_BEGIN);
			    sleep(MessageSecs);
			    if (disabled) {
				_statusline(FORM_LINK_OPTION_LIST_UNM_MSG);
			    } else {
				_statusline(FORM_LINK_OPTION_LIST_MESSAGE);
			    }
			    break;
			}
			window_offset = 0;
			cur_selection = 0;
			if (disabled) {
			    _statusline(FORM_LINK_OPTION_LIST_UNM_MSG);
			} else {
			    _statusline(FORM_LINK_OPTION_LIST_MESSAGE);
			}
			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_options - length) + 1)) {
			    _statusline(ALREADY_AT_OPTION_END);
			    sleep(MessageSecs);
			    if (disabled) {
				_statusline(FORM_LINK_OPTION_LIST_UNM_MSG);
			    } else {
				_statusline(FORM_LINK_OPTION_LIST_MESSAGE);
			    }
			    break;
			}
			window_offset = ((npages - 1) * length);
			if (window_offset > (num_options - length)) {
			    window_offset = (num_options - length + 1);
			}
			if (cur_selection < window_offset)
			    cur_selection = window_offset;
			if (disabled) {
			    _statusline(FORM_LINK_OPTION_LIST_UNM_MSG);
			} else {
			    _statusline(FORM_LINK_OPTION_LIST_MESSAGE);
			}
			goto redraw;
		    }

		    /*
		     *  We want an intermediate page. - FM
		     */
		    if (((number - 1) * length) == window_offset) {
			sprintf(buffer, ALREADY_AT_OPTION_PAGE, number);
			_statusline(buffer);
			sleep(MessageSecs);
			if (disabled) {
			    _statusline(FORM_LINK_OPTION_LIST_UNM_MSG);
			} else {
			    _statusline(FORM_LINK_OPTION_LIST_MESSAGE);
			}
			break;
		    }
		    cur_selection = window_offset = ((number - 1) * length);
		    if (disabled) {
			_statusline(FORM_LINK_OPTION_LIST_UNM_MSG);
		    } else {
			_statusline(FORM_LINK_OPTION_LIST_MESSAGE);
		    }
		    goto redraw;

		}

		/*
		 *  Check for a positive number, which signifies
		 *  that an option should be sought. - FM
		 */
		if (number > 0) {
		    /*
		     *  Decrement the number so as to correspond
		     *  with our cur_selection values. - FM
		     */
		    number--;

		    /*
		     *  If the number is in range and had no legal
		     *  suffix, select the indicated option. - FM
		     */
		    if (number <= num_options && c == '\0') {
			cur_selection = number;
			cmd = LYK_ACTIVATE;
			break;
		    }

		    /*
		     *  Verify that we had a 'g' suffix,
		     *  and act on the number. - FM
		     */
		    if (c == 'g') {
			if (cur_selection == number) {
			    /*
			     *  The option already is current. - FM
			     */
			    sprintf(buffer,
				    OPTION_ALREADY_CURRENT, (number + 1));
			    _statusline(buffer);
			    sleep(MessageSecs);
			    if (disabled) {
				_statusline(FORM_LINK_OPTION_LIST_UNM_MSG);
			    } else {
				_statusline(FORM_LINK_OPTION_LIST_MESSAGE);
			    }
			    break;
			}

			if (number <= num_options) {
			    /*
			     *  The number is in range and had a 'g'
			     *  suffix, so make it the current option,
			     *  scrolling if needed. - FM
			     */
			    j = (number - cur_selection);
			    cur_selection = number;
			    if ((j > 0) &&
				(cur_selection - window_offset) >= length) {
				window_offset += j;
				if (window_offset > (num_options - length + 1))
				    window_offset = (num_options - length + 1);
			    } else if ((cur_selection - window_offset) < 0) {
				window_offset -= abs(j);
				if (window_offset < 0)
				    window_offset = 0;
			    }
			    if (disabled) {
				_statusline(FORM_LINK_OPTION_LIST_UNM_MSG);
			    } else {
				_statusline(FORM_LINK_OPTION_LIST_MESSAGE);
			    }
			    goto redraw;
			}

			/*
			 *  Not in range. - FM
			 */
			_statusline(BAD_OPTION_NUM_ENTERED);
			sleep(MessageSecs);
		    }
		}

		/*
		 *  Restore the popup statusline. - FM
		 */
		if (disabled) {
		    _statusline(FORM_LINK_OPTION_LIST_UNM_MSG);
		} else {
		    _statusline(FORM_LINK_OPTION_LIST_MESSAGE);
		}
		break;

	    case LYK_PREV_LINK:
	    case LYK_UP_LINK:

		if (cur_selection > 0)
		    cur_selection--;

		/*
		 *  Scroll the window up if necessary.
		 */
		if ((cur_selection - window_offset) < 0) {
		    window_offset--;
		    goto redraw;
		}
		break;

	    case LYK_NEXT_LINK:
	    case LYK_DOWN_LINK:
		if (cur_selection < num_options)
		    cur_selection++;

		/*
		 *  Scroll the window down if necessary
		 */
		if ((cur_selection - window_offset) >= length) {
		    window_offset++;
		    goto redraw;
		}
		break;

	    case LYK_NEXT_PAGE:
		/*
		 *  Okay, are we on the last page of the list?
		 *  If not then,
		 */
		if (window_offset != (num_options - length + 1)) {
		    /*
		     *  Modify the current selection to not be a
		     *  coordinate in the list, but a coordinate
		     *  on the item selected in the window.
		     */
		    cur_selection -= window_offset;

		    /*
		     *  Page down the proper length for the list.
		     *  If simply to far, back up.
		     */
		    window_offset += length;
		    if (window_offset > (num_options - length)) {
			window_offset = (num_options - length + 1);
		    }

		    /*
		     *  Readjust the current selection to be a
		     *  list coordinate rather than window.
		     *  Redraw this thing.
		     */
		    cur_selection += window_offset;
		    goto redraw;
		}
		else if (cur_selection < num_options) {
		    /*
		     *  Already on last page of the list so just
		     *  redraw it with the last item selected.
		     */
		    cur_selection = num_options;
		}
		break;

	    case LYK_PREV_PAGE:
		/*
		 *  Are we on the first page of the list?
		 *  If not then,
		 */
		if (window_offset != 0) {
		    /*
		     *  Modify the current selection to not be a
		     *  list coordinate, but a window coordinate.
		     */
		    cur_selection -= 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 selection.
		     */
		    cur_selection += window_offset;
		    goto redraw;
		} else if (cur_selection > 0) {
		    /*
		     *  Already on the first page so just
		     *  back up to the first item.
		     */
		    cur_selection = 0;
		}
		break;

	    case LYK_HOME:
		cur_selection = 0;
		if (window_offset > 0) {
		    window_offset = 0;
		    goto redraw;
		}
		break;

	    case LYK_END:
		cur_selection = num_options;
		if (window_offset != (num_options - length + 1)) {
		    window_offset = (num_options - length + 1);
		    goto redraw;
		}
		break;

	    case LYK_DOWN_TWO:
		cur_selection += 2;
		if (cur_selection > num_options)
		    cur_selection = num_options;

		/*
		 *  Scroll the window down if necessary.
		 */
		if ((cur_selection - window_offset) >= length) {
		    window_offset += 2;
		    if (window_offset > (num_options - length + 1))
			window_offset = (num_options - length + 1);
		    goto redraw;
		}
		break;

	    case LYK_UP_TWO:
		cur_selection -= 2;
		if (cur_selection < 0)
		    cur_selection = 0;

		/*
		 *  Scroll the window up if necessary.
		 */
		if ((cur_selection - window_offset) < 0) {
		    window_offset -= 2;
		    if (window_offset < 0)
			window_offset = 0;
		    goto redraw;
		}
		break;

	    case LYK_DOWN_HALF:
		cur_selection += (length/2);
		if (cur_selection > num_options)
		    cur_selection = num_options;

		/*
		 *  Scroll the window down if necessary.
		 */
		if ((cur_selection - window_offset) >= length) {
		    window_offset += (length/2);
		    if (window_offset > (num_options - length + 1))
			window_offset = (num_options - length + 1);
		    goto redraw;
		}
		break;

	    case LYK_UP_HALF:
		cur_selection -= (length/2);
		if (cur_selection < 0)
		    cur_selection = 0;

		/*
		 *  Scroll the window up if necessary.
		 */
		if ((cur_selection - 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) {
			strcpy(prev_target_buffer, cp);
			QueryNum = 0;
			FirstRecall = FALSE;
		    }
		}
		strcpy(prev_target, prev_target_buffer);
	    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
			 */
			_statusline(CANCELLED);
			sleep(InfoSecs);
			goto restore_popup_statusline;
		    }
		}

check_recall:
		if (*prev_target == '\0' &&
		    !(recall && (ch == UPARROW || ch == DNARROW))) {
		    /*
		     *  No entry.  Simply break.   - FM
		     */
		    _statusline(CANCELLED);
		    sleep(InfoSecs);
		    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) {
			strcpy(prev_target, cp);
			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
			     */
			    _statusline(CANCELLED);
			    sleep(InfoSecs);
			    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) {
			strcpy(prev_target, cp);
			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
			     */
			    _statusline(CANCELLED);
			    sleep(InfoSecs);
			    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 option. - FM
		 */
		for (j = 1, tmp_ptr = opt_ptr->next;
		     tmp_ptr != NULL; tmp_ptr = tmp_ptr->next, j++) {
		    if (case_sensitive) {
			if (strstr(tmp_ptr->name, prev_target_buffer) != NULL)
			    break;
		    } else {
			if (LYstrstr(tmp_ptr->name, prev_target_buffer) != NULL)
			    break;
		    }
		}
		if (tmp_ptr != NULL) {
		    /*
		     *  We have a hit, so make that option the current. - FM
		     */
		    cur_selection += j;
		    /*
		     *  Scroll the window down if necessary.
		     */
		    if ((cur_selection - window_offset) >= length) {
			window_offset += j;
			if (window_offset > (num_options - length + 1))
			    window_offset = (num_options - length + 1);
			ReDraw = TRUE;
		    }
		    goto restore_popup_statusline;
		}

		/*
		 *  If we started at the beginning, it can't be present. - FM
		 */
		if (cur_selection == 0) {
		    _user_message(STRING_NOT_FOUND, prev_target_buffer);
		    sleep(MessageSecs);
		    goto restore_popup_statusline;
		}

		/*
		 *  Search from the beginning to the current option. - FM
		 */
		for (j = 0, tmp_ptr = list;
		     j < cur_selection; tmp_ptr = tmp_ptr->next, j++) {
		    if (case_sensitive) {
			if (strstr(tmp_ptr->name, prev_target_buffer) != NULL)
			    break;
		    } else {
			if (LYstrstr(tmp_ptr->name, prev_target_buffer) != NULL)
			    break;
		    }
		}
		if (j < cur_selection) {
		    /*
		     *  We have a hit, so make that option the current. - FM
		     */
		    j = (cur_selection - j);
		    cur_selection -= j;
		    /*
		     *  Scroll the window up if necessary.
		     */
		    if ((cur_selection - 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 options either. - FM
		 */
		_user_message(STRING_NOT_FOUND, prev_target_buffer);
		sleep(MessageSecs);

restore_popup_statusline:
		/*
		 *  Restore the popup statusline and
		 *  reset the search variables. - FM
		 */
		if (disabled)
		    _statusline(FORM_LINK_OPTION_LIST_UNM_MSG);
		else
		    _statusline(FORM_LINK_OPTION_LIST_MESSAGE);
		*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_selection = orig_selection;
		cmd = LYK_ACTIVATE; /* to exit */
		break;
	}

    }
#ifndef USE_SLANG
    delwin(form_window);
#ifdef NCURSES
    LYsubwindow(0);
#endif
#endif /* !USE_SLANG */

    return(disabled ? orig_selection : cur_selection);
}