/*
 * txInput.c --
 *
 * 	Handles 'stdin' and terminal driver settings.
 *
 *     *********************************************************************
 *     * Copyright (C) 1985, 1990 Regents of the University of California. *
 *     * Permission to use, copy, modify, and distribute this              *
 *     * software and its documentation for any purpose and without        *
 *     * fee is hereby granted, provided that the above copyright          *
 *     * notice appear in all copies.  The University of California        *
 *     * makes no representations about the suitability of this            *
 *     * software for any purpose.  It is provided "as is" without         *
 *     * express or implied warranty.  Export of this software outside     *
 *     * of the United States of America may require an export license.    *
 *     *********************************************************************
 */

#ifndef lint
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/textio/txInput.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <sys/types.h>
#include <unistd.h>
#include <ctype.h>
#include <dirent.h>


#include "utils/magic.h"
#include "utils/main.h"
#include "textio/textio.h"
#include "utils/geometry.h"
#include "textio/txcommands.h"
#include "textio/textioInt.h"
#include "utils/magsgtty.h"	/* txTermState */
#include "utils/dqueue.h"
#include "utils/macros.h"
#include "utils/hash.h"
#include "tiles/tile.h"
#include "database/database.h"
#include "windows/windows.h"
#include "graphics/graphics.h"
#include "database/databaseInt.h"
#include "cif/CIFint.h"
#include "cif/CIFread.h"

/* C99 compat */
#include "utils/malloc.h"

#ifdef USE_READLINE
#ifdef HAVE_READLINE
#include <readline/readline.h>
#include <readline/history.h>
#else
#include "readline/readline/readline.h"
#include "readline/readline/history.h"
#endif

int    TxPrefix(void);

/* Globally keep track of the imacro string, if any; */
/* the readline callback takes no parameters.	     */
const char *_rl_prefix;

/* Custom completer function */
char **magic_completion_function(const char *, int, int);

/* match generators */
char  *list_completion_function(const char *, int);
char  *macro_completion_function(const char *, int);
char  *cellname_completion_function(const char *, int);
char  *istyle_completion_function(const char *, int);
char  *ostyle_completion_function(const char *, int);

/* match list generators */
void   make_techtype_list(void);
void   update_cellname_hash(void);

const char * const *completion_list;
const char * const *magic_command_list  = (const char **)NULL;
const char * const *magic_techtype_list = (const char **)NULL;
HashTable cellname_hash;

static int interactive_flag;

const char *magic_direction_list[] = {
  "bottom", "down", "e", "east", "left", "n", "north", "right",
  "s", "south", "top", "up", "w", "west", (char *)NULL };

#define COMMAND_COMPL 0
#define MACRO_COMPL   1
#define IMACRO_COMPL  2
#define LAYER_COMPL   3
#define PLANE_COMPL   4
#define FILE_COMPL    5
#define CELL_COMPL    6
#define ARGS_COMPL    7
#define ISTYLE_COMPL  8
#define OSTYLE_COMPL  9
#define DIRECT_COMPL  10

/* Can only accommodate 32 arg options, increase as necessary */
static const struct cmd_spec {
  const char *cmd;
  int   type;
  const char *args[32];
} magic_cmd_spec[] = {
  { "", COMMAND_COMPL, {NULL} },
  { "help", COMMAND_COMPL, {NULL} },

  { "shell", FILE_COMPL, {NULL} },
  { "addpath", FILE_COMPL, {NULL} },
  { "path", FILE_COMPL, {NULL} },
  { "logcommands", FILE_COMPL, {NULL} },
  { "startrsim", FILE_COMPL, {NULL} },
  { "rsim", FILE_COMPL, {NULL} },
  { "source", FILE_COMPL, {NULL} },
  { "save", FILE_COMPL, {NULL} },

  { "getcell", CELL_COMPL, {NULL} },
  { "child", CELL_COMPL, {NULL} },
  { "parent", CELL_COMPL, {NULL} },
  { "dump", CELL_COMPL, {NULL} },
  { "list", CELL_COMPL, {NULL} },
  { "load", CELL_COMPL, {NULL} },
  { "xload", CELL_COMPL, {NULL} },

  { "macro", MACRO_COMPL, {NULL} },
  { "imacro", IMACRO_COMPL, {NULL} },
  { "see*", LAYER_COMPL, {NULL} },
  { "paint", LAYER_COMPL, {NULL} },

  { "*extract", ARGS_COMPL, {"clrdebug", "clrlength", "driver", "interactions",
                             "intercount", "parents", "receiver", "setdebug",
                             "showdebug", "showparents", "showtech", "stats",
                             "step", "times", NULL} },

  { "*garoute", ARGS_COMPL, {"clrdebug", "setdebug", "showdebug", NULL} },
  { "*groute", ARGS_COMPL, {"clrdebug", "onlynet", "setdebug", "showdebug",
                            "sides", NULL} },
  { "*iroute", ARGS_COMPL, {"debug", "help", "parms", NULL} },
  { "*malloc", ARGS_COMPL, {"all", "off", "on", "only", "watch", "unwatch", NULL} },
  { "*mzroute", ARGS_COMPL, {"debug", "dumpEstimates", "dumpTags", "help",
                             "numberLine", "parms", "plane", "version", NULL} },

  { "*plow", ARGS_COMPL, {"help", "clrdebug", "jogreduce", "lwidth", "lshadow",
                          "mergedown", "mergeup", "move", "outline", "plow",
                          "print", "random", "setdebug", "shadow", "showdebug",
                          "split", "techshow", "trail", "whenbot", "whentop",
                          "width", NULL} },
  { "*plow lwidth", LAYER_COMPL, { NULL } },
  { "*plow width", LAYER_COMPL, { NULL } },
  { "*plow lshadow", LAYER_COMPL, { NULL } },
  { "*plow shadow", LAYER_COMPL, { NULL } },
  { "*plow techshow", FILE_COMPL, { NULL } },
  { "*plow outline", DIRECT_COMPL, { NULL } },
  { "*plow plow", DIRECT_COMPL, { NULL } },

  { "*profile", ARGS_COMPL, {"on", "off", NULL} },
  { "*watch", PLANE_COMPL, {NULL} },
  { "*watch*", ARGS_COMPL, {"demo", "types", NULL} },

  { "calma", ARGS_COMPL, {"help", "flatten", "labels", "lower", "noflatten",
                          "nolabels", "nolower", "read", "write", NULL} },
  { "calma read", FILE_COMPL, { NULL } },
  { "calma write", FILE_COMPL, { NULL } },

  { "cif", ARGS_COMPL, {"help", "arealabels", "idcell", "istyle", "prefix",
                        "ostyle", "read", "see", "statistics", "write", "flat", NULL} },
  { "cif read", FILE_COMPL, { NULL } },
  { "cif flat", FILE_COMPL, { NULL } },
  { "cif write", FILE_COMPL, { NULL } },
  { "cif idcell", ARGS_COMPL, { "yes", "no", NULL } },
  { "cif arealabels", ARGS_COMPL, { "yes", "no", NULL } },
  { "cif prefix", FILE_COMPL, { NULL } },
  { "cif see", LAYER_COMPL, { NULL } },
  { "cif istyle", ISTYLE_COMPL, { NULL } },
  { "cif ostyle", OSTYLE_COMPL, { NULL } },

  { "copy", DIRECT_COMPL, { NULL } },
  { "move", DIRECT_COMPL, { NULL } },
  { "stretch", DIRECT_COMPL, { NULL } },

  { "drc", ARGS_COMPL, {"help", "catchup", "check", "count", "find", "off",
                        "on", "printrules", "rulestats", "statistics", "why", NULL} },
  { "drc printrules", FILE_COMPL, { NULL } },

  { "ext", ARGS_COMPL, {"help", "all", "cell", "do", "length", "no", "parents",
                        "showparents", "style", "unique", "warn", NULL} },
  { "ext style", OSTYLE_COMPL, { NULL } },

  { "feedback", ARGS_COMPL, {"help", "add", "clear", "count", "find", "save",
                             "why", "fill", NULL} },
  { "feedback save", FILE_COMPL, { NULL } },

  { "garoute", ARGS_COMPL, {"help", "channel", "generate", "nowarn", "route",
                            "reset", "warn", NULL} },
  { "getnode", ARGS_COMPL, {"alias", "fast", "abort", NULL} },

  { "iroute", ARGS_COMPL, {"help", "contacts", "layers", "route",
                           "saveParameters", "search", "spacings", "verbosity",
                           "version", "wizard", NULL} },
  { "iroute help", ARGS_COMPL, {"contacts", "layers", "route",
                           "saveParameters", "search", "spacings", "verbosity",
                           "version", "wizard", NULL} },

  { "plot", ARGS_COMPL, {"help", "postscript", "gremlin", "versatec", "pnm",
                         "parameters", NULL} },
  { "plot postscript", FILE_COMPL, { NULL } },
  { "plot gremlin", FILE_COMPL, { NULL } },
  { "plot versatec", FILE_COMPL, { NULL } },
  { "plot pnm", FILE_COMPL, { NULL } },

  { "plow", ARGS_COMPL, {"help", "boundary", "horizon", "jogs", "selection",
                         "straighten", "noboundary", "nojogs", "nostraighten", NULL} },
  { "plow selection", DIRECT_COMPL, { NULL } },

  { "route", ARGS_COMPL, {"help", "end", "jog", "metal", "netlist", "obstacle",
                          "origin", "stats", "settings", "steady", "tech",
                          "vias", "viamin", NULL} },
  { "route netlist", FILE_COMPL, { NULL } },

  { "select", ARGS_COMPL, {"help", "more", "less", "area", "visible", "cell",
                           "clear", "save", "box", NULL} },
  { "select save", FILE_COMPL, { NULL } },
  { "select more", ARGS_COMPL, { "area", "visible", "cell", "box", NULL } },
  { "select less", ARGS_COMPL, { "area", "visible", "cell", "box", NULL } },
  { "select more area", LAYER_COMPL, { NULL } },
  { "select less area", LAYER_COMPL, { NULL } },
  { "select more visible", LAYER_COMPL, { NULL } },
  { "select less visible", LAYER_COMPL, { NULL } },
  { "select more cell", CELL_COMPL, { NULL } },
  { "select less cell", CELL_COMPL, { NULL } },
  { "select more box", LAYER_COMPL, { NULL } },
  { "select less box", LAYER_COMPL, { NULL } },

  { "send", ARGS_COMPL, {"netlist", "color", "layout",
#ifdef THREE_D
	"wind3d",
#endif
		 NULL} },
  { "snap", ARGS_COMPL, {"on", "off", NULL} },
  { "tool", ARGS_COMPL, {"box", "wiring", "netlist", "rsim", NULL} },
  { "wire", ARGS_COMPL, {"help", "horizontal", "leg", "switch", "type",
                         "vertical", NULL} },
  { "wire type", LAYER_COMPL, { NULL } },
  { "wire switch", LAYER_COMPL, { NULL } },

#ifdef USE_READLINE
  { "history", ARGS_COMPL, {"n", "r", "help", NULL} },
#endif
  { NULL , 0, {NULL} }
};
#endif /* USE_READLINE */


/* Characters for input processing.  Set to -1 if not defined */

char *TxGetLinePfix(char *dest, int maxChars, const char *prefix);

char txEraseChar = -1;		/* Erase line (e.g. ^H) */
char txKillChar = -1;		/* Kill line (e.g. ^U or ^X) */
char txWordChar = -1;		/* Erase a word (e.g. ^W) */
char txReprintChar = -1;	/* Reprint the line (e.g. ^R */
char txLitChar = -1;		/* Literal next character (e.g. ^V */
char txBreakChar = -1;		/* Break to a new line (normally -1) */
char TxEOFChar = -1;		/* The current EOF character (e.g. ^D) */
char TxInterruptChar = -1;	/* The current interrupt character (e.g. ^C) */

static char txPromptChar;	/* the current prompt */
bool txHavePrompt = FALSE;	/* is a prompt on the screen? */

static const char *txReprint1 = NULL;
static const char *txReprint2 = NULL;

/*
 * ----------------------------------------------------------------------------
 *
 * TxReprint --
 *
 *	Reprint the current input line.  Used for ^R and after ^Z.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

void
TxReprint(void)
{
    (void) txFprintfBasic(stdout, "\n");
    if (txReprint1 != NULL) (void) txFprintfBasic(stdout, "%s", txReprint1);
    if (txReprint2 != NULL) (void) txFprintfBasic(stdout, "%s", txReprint2);
    (void) fflush(stdout);
}

#ifndef MAGIC_WRAPPER

/*
 * ----------------------------------------------------------------------------
 * TxDialog --
 *
 *	Interactive input with a prompt and a fixed set of responses
 *
 * Results:
 *	Returns the index of the response chosen.  If no response is given,
 *      returns the default value.
 *
 * Side effects:
 *	Terminal input and output (stdout, stdin).
 *
 * ----------------------------------------------------------------------------
 */

int
TxDialog(
    const char *prompt,
    const char * const *responses,
    int deflt)
{
    int code;
    int maxresp;
    char ans[100];

    /* Find size of valid response set */
    for (maxresp = 0; responses[maxresp] != NULL; maxresp++);

    /* Get input until something matches one of the responses */
    do
    {
	TxPrintf("%s ", prompt);
	if ((deflt >= 0) && (deflt < maxresp))
	    TxPrintf("[%s] ", responses[deflt]);
	TxFlushOut();

	if (TxGetLine(ans, sizeof ans) == NULL || (ans[0] == '\0'))
	{
	    code = deflt;
	    break;
	}
    } while ((code = Lookup(ans, responses)) < 0);

    return code;
}


/*
 * ----------------------------------------------------------------------------
 * TxSetPrompt --
 *
 *	Set the current prompt.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	You can guess.
 * ----------------------------------------------------------------------------
 */

void
TxSetPrompt(
    char ch)
{
    txPromptChar = ch;
}

#endif /* !MAGIC_WRAPPER */


/*
 * ----------------------------------------------------------------------------
 * TxPrompt --
 *
 *	Put up the prompt which was set by TxSetPrompt.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	You can guess.
 * ----------------------------------------------------------------------------
 */

void
TxPrompt(void)
{
    static char lastPromptChar;
    static char prompts[2];

    if (txHavePrompt && (lastPromptChar == txPromptChar)) return;

    (void) fflush(stderr);
    if (txHavePrompt) TxUnPrompt();

    prompts[0] = txPromptChar;
    prompts[1] = '\0';

    txReprint1 = prompts;
    if (TxInteractive) txFprintfBasic(stdout, "%s", txReprint1);
    (void) fflush(stdout);
    txHavePrompt = TRUE;
    lastPromptChar = txPromptChar;
}


/*
 * ----------------------------------------------------------------------------
 * TxRestorePrompt --
 *
 *	The prompt was erased for some reason.  Restore it.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	You can guess.
 * ----------------------------------------------------------------------------
 */

void
TxRestorePrompt(void)
{
    if (txHavePrompt)
    {
	txHavePrompt = FALSE;
	TxPrompt();
    }
}


/*
 * ----------------------------------------------------------------------------
 * TxUnPrompt --
 *
 *	Erase the prompt.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	You can guess.
 * ----------------------------------------------------------------------------
 */


void
TxUnPrompt(void)
{
    int i, tlen;

    if (txHavePrompt)
    {
	(void) fflush(stderr);
	if (TxInteractive)
	{
	    tlen = strlen(txReprint1);
	    for (i = 0; i < tlen; i++)
		fputc('\b', stdout);
	    for (i = 0; i < tlen; i++)
		fputc(' ', stdout);
	    for (i = 0; i < tlen; i++)
		fputc('\b', stdout);
	}
	(void) fflush(stdout);
	txReprint1 = NULL;
	txHavePrompt = FALSE;
    }
}


/*
 * ----------------------------------------------------------------------------
 *
 * TxGetChar --
 *
 *	Get a single character from the input stream (terminal or whatever).
 *
 * Results:
 *	One character, or EOF.
 *
 * Side Effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

int
TxGetChar(void)
{
    int ch;
    extern DQueue txInputEvents, txFreeEvents;
    extern TxInputEvent txLastEvent;
    TxInputEvent *event;
    while (TRUE) {
	/* Get an input character.  Don't let TxGetInputEvent return
	 * because of a SigWinch or whatever.  It would be nice to process
	 * SigWinches in a middle of text input, but that is difficult
	 * to do since SigWinches are processed in the normal
	 * command-interpretation process.  Thus, we delay the processing
	 * until after the text is collected.
	 */
	if (DQIsEmpty(&txInputEvents)) TxGetInputEvent(TRUE, FALSE);
	event = (TxInputEvent *) DQPopFront(&txInputEvents);
	ASSERT(event != NULL, "TxGetChar");
	txLastEvent = *event;
	if (event->txe_button == TX_EOF) {
	    ch = EOF;
	    goto gotone;
	}
	if (event->txe_button == TX_CHARACTER) {
	    ch = TranslateChar(event->txe_ch);
	    goto gotone;
	}
	DQPushRear(&txFreeEvents, (ClientData) event);
    }

gotone:
    DQPushRear(&txFreeEvents, (ClientData) event);
    return ch;
}


#ifdef USE_READLINE
#undef free
/*
 * ----------------------------------------------------------------------------
 * TxPrefix --
 *	Instantiation of the readline function *rl_pre_input_hook(), executed
 *	between the prompt and the input (readline v4.1).  Prepends the text
 *	of an interactive macro (if non-NULL) to the command line.
 *
 * ----------------------------------------------------------------------------
 */
int
TxPrefix(void)
{
    if (_rl_prefix != NULL)
	rl_insert_text(_rl_prefix);
    rl_redisplay();
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * magic_completion_function --
 *
 *	Command-name completion function for use with "readline" package.
 *
 * ----------------------------------------------------------------------------
 */

char **
magic_completion_function(
    const char *text,
    int start,
    int end)
{
    CPFunction *completion_func = (CPFunction *)NULL;
    char **matches, **tokens;
    char *line = strdup(rl_line_buffer);
    int i;

    for (i = start; i >= 0; i--)
	if( isspace(line[i]) || line[i] == ';' )
	    break;
    line[i+1] = '\0';

    if ((tokens = history_tokenize(line)) != NULL )
    {
	int entry, start_token, num_tokens;

	num_tokens = 0;
	while (tokens[num_tokens] != (char *)NULL )
	    num_tokens++;

	for (start_token = num_tokens - 1; start_token >= 0; start_token--)
	    if (tokens[start_token][0] == ';')
		break;

	start_token++;

	line[0] = '\0';
	for (i = start_token; i < num_tokens; i++)
	{
	    strcat(line, tokens[i]);
	    if (i < (num_tokens - 1))
		strcat(line, " ");
	}

	entry = LookupStructFull(line, (const char * const *)&magic_cmd_spec,
		sizeof(struct cmd_spec));

	if (entry == -1 && num_tokens >= 1)
	{
	    sprintf(line, "%s*", tokens[0]);
	    entry = LookupStructFull(line, (const char * const *)&magic_cmd_spec,
			sizeof(struct cmd_spec));
	}

	if (entry >= 0)
	{
	    switch (magic_cmd_spec[entry].type)
	    {
		case COMMAND_COMPL:
		    completion_func = list_completion_function;
		    completion_list = magic_command_list;
		    break;
		case MACRO_COMPL:
		    completion_func = macro_completion_function;
		    interactive_flag = 0;
		    break;
		case IMACRO_COMPL:
		    completion_func = macro_completion_function;
		    interactive_flag = 1;
		    break;
		case LAYER_COMPL:
		    completion_func = list_completion_function;
		    if (magic_techtype_list == NULL)
			make_techtype_list();
		    completion_list = magic_techtype_list;
		    break;
		case PLANE_COMPL:
		    completion_func = list_completion_function;
		    completion_list = DBPlaneLongNameTbl;
		    break;
		case FILE_COMPL:
		    completion_func = (text[0] == '~') ?
				rl_username_completion_function :
                                rl_filename_completion_function;
		    break;
		case CELL_COMPL:
		    update_cellname_hash();
		    completion_func = cellname_completion_function;
		    break;
		case ARGS_COMPL:
		    completion_func = list_completion_function;
		    completion_list = magic_cmd_spec[entry].args;
		    break;
		case ISTYLE_COMPL:
		    completion_func = istyle_completion_function;
		    break;
		case OSTYLE_COMPL:
		    completion_func = ostyle_completion_function;
		    break;
		case DIRECT_COMPL:
		    completion_func = list_completion_function;
		    completion_list = magic_direction_list;
		    break;
	    }
	}

	for (i = 0; i < num_tokens; i++)
	    free(tokens[i]);

	free(tokens);

    }
    else
    {
	completion_func = list_completion_function;
	completion_list = magic_command_list;
    }

    free(line);

    matches = (completion_func) ? rl_completion_matches(text, completion_func)
		: (char **)NULL;

    /* If we match nothing, inhibit any matching, except when matching files */

   rl_attempted_completion_over = (matches == (char **)NULL &&
                                  completion_func != rl_username_completion_function &&
                                  completion_func != rl_filename_completion_function);
   return matches;
}

/*
 * ----------------------------------------------------------------------------
 *
 * update_cellname_hash --
 *
 *	Update the hash table of cell names so it can be used by the
 *	cellname completion function.
 *
 * ----------------------------------------------------------------------------
 */
void
update_cellname_hash(void)
{
    extern char *Path;
    const char *path = Path;
    char *dirname = strdup(Path);

    while (nextName(&path, "", dirname, strlen(Path)))
    {
	DIR *dir;

	if (strlen(dirname) == 0)
	    strcpy(dirname, ".");

	dir = opendir(dirname);

	if (dir != NULL)
	{
	    struct dirent *dirent;
	    while ((dirent = readdir(dir)) != (struct dirent *)NULL)
	    {
		char *base;
		if ((base = (char *)strstr(dirent->d_name, DBSuffix))
			!= (char *)NULL && base[strlen(DBSuffix)] == '\0')
		{
		    *base = '\0';
		    if (strlen(dirent->d_name) > 0)
			HashFind(&cellname_hash, dirent->d_name);
		}
	    }
	    closedir(dir);
	}
    }
    free(dirname);
}

/*
 * ----------------------------------------------------------------------------
 *
 * istyle_completion_function --
 *
 *	CIF input style completion function for use with "readline" package.
 *
 * ----------------------------------------------------------------------------
 */
char *
istyle_completion_function(
    const char *text,
    int state)
{
    extern CIFReadKeep *cifReadStyleList;
    static CIFReadKeep *style;
    static int len;

    if (state == 0)
    {
	style = cifReadStyleList;
	len = strlen(text);
    }

    for (; style != NULL; style = style->crs_next)
    {
	if (strncmp(style->crs_name, text, len) == 0)
	{
	    char *name = style->crs_name;
	    style = style->crs_next;
	    return strdup(name);
	}
    }
    return (char *)NULL;
}

/*
 * ----------------------------------------------------------------------------
 *
 * ostyle_completion_function --
 *
 *	CIF output style completion function for use with "readline" package.
 *
 * ----------------------------------------------------------------------------
 */
char *
ostyle_completion_function(
    const char *text,
    int state)
{
    extern CIFKeep *CIFStyleList;
    static CIFKeep *style;
    static int len;

    if (state == 0)
    {
	style = CIFStyleList;
	len = strlen(text);
    }

    for (; style != NULL; style = style->cs_next)
    {
	if (strncmp(style->cs_name, text, len) == 0)
	{
	    char *name = style->cs_name;
	    style = style->cs_next;
	    return strdup(name);
	}
    }
    return (char *)NULL;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cellname_completion_function --
 *
  name completion function for use with "readline" package.
 *
 * ----------------------------------------------------------------------------
 */
char *
cellname_completion_function(
    const char *text,
    int state)
{
    extern HashTable dbCellDefTable;
    static int            len;
    static HashSearch     hs, db_hs;
    HashEntry            *he;

    if (state == 0)
    {
	len = strlen(text);
	HashStartSearch(&hs);
	HashStartSearch(&db_hs);
    }

    while ((he = HashNext(&cellname_hash, &hs)) != (HashEntry *)NULL )
    {
	if (strncmp(he->h_key.h_name, text, len) == 0)
	    return strdup(he->h_key.h_name);
    }

    /* Also complete cells that are in main memory only */

    while ((he = HashNext(&dbCellDefTable, &db_hs)) != (HashEntry *)NULL)
    {
	CellDef *cd = (CellDef *)HashGetValue(he);
	if (cd && !(cd->cd_flags & CDINTERNAL)
		&& strcmp(cd->cd_name, UNNAMED) != 0
		&& strncmp(cd->cd_name, text, len) == 0)
	    return strdup(cd->cd_name);
    }
    return (char *)NULL;
}

/*
 * ----------------------------------------------------------------------------
 *
 * macro_completion_function --
 *
 *	Macro completion function for use with "readline" package.
 *
 * ----------------------------------------------------------------------------
 */
char *
macro_completion_function(
    const char *text,
    int state)
{
    extern HashTable MacroClients;
    static HashSearch hs, mc_hs;
    static int len;
    macrodef *macro;
    HashEntry	*he;

    if (state == 0)
    {
	len = strlen(text);
	HashStartSearch(&hs);
    }

    while ((he = HashNext(&MacroClients, &hs)) != (HashEntry *)NULL )
    {
	HashTable *ht = (HashTable *)HashGetValue(he);
	HashEntry *hm;

	if (state == 0)
	    HashStartSearch(&mc_hs);

	while ((hm = HashNext(ht, &mc_hs)) != (HashEntry *)NULL )
	{
	    macro = (macrodef *)HashGetValue(hm);
	    if ((macro->interactive == TRUE && interactive_flag == 1) ||
        		(macro->interactive == FALSE && interactive_flag == 0))
	    {
		char *macro_name = MacroName(macro->macrotext);
		if (strncmp(macro_name, text, len) == 0)
		    return strdup(macro_name);
	    }
	}
    }
    return (char *)NULL;
}

/*
 * ----------------------------------------------------------------------------
 *
 * list_completion_function --
 *
 *	List completion function for use with "readline" package.
 *
 * ----------------------------------------------------------------------------
 */
char *
list_completion_function(
    const char *text,
    int state)
{
    static int list_index, len;
    const char *match;

    /* If this is a new word to complete, initialize now.  This includes
     * saving the length of TEXT for efficiency, and initializing the index
     * variable to 0.
     */

    if (state == 0)
    {
	list_index = 0;
	len = strlen(text);
    }

    /* Return the next name which partially matches from the command list. */

    while ((match = completion_list[list_index]) != (char *)NULL)
    {
	list_index++;

	if (strncmp(match, text, len) == 0)
	    return strdup(match);
    }
    return (char *)NULL;
}

/*
 * ----------------------------------------------------------------------------
 *
 * make_techtype_list --
 *
 *	Generate list of predefined types for use with completion function
 *
 * ----------------------------------------------------------------------------
 */
void
make_techtype_list(void)
{
    int i, techdep_layers = DBNumUserLayers - TT_TECHDEPBASE;

    const char **tmp = (const char **)mallocMagic(sizeof(char *)
		* (techdep_layers + 8 + 2 + 1));

    for (i = 0; i < techdep_layers; i++)
	tmp[i] = DBTypeLongNameTbl[i+TT_TECHDEPBASE];

    tmp[i++] = "magnet";
    tmp[i++] = "fence";
    tmp[i++] = "rotate";
    tmp[i++] = "subcircuit";
    tmp[i++] = "$";
    tmp[i++] = "*";
    tmp[i++] = "errors";
    tmp[i++] = "labels";
    tmp[i++] = "subcell";

    tmp[i++] = "no";
    tmp[i++] = "allSame";

    tmp[i]   = NULL;

    magic_techtype_list = tmp; /* (const char * const *) */
}

#define free You_should_use_the_Magic_procedure_freeMagic
#endif /* USE_READLINE */

/*
 * ----------------------------------------------------------------------------
 *
 * TxGetLineWPrompt --
 *
 *	Just like the following TxGetLine proc, but it prompts first.  It
 *	is an advantage to have this proc put out the prompt, since that
 *	way if the user suspends Magic and then continues it the prompt will
 *	reappear in the proper fashion.
 *
 * Results:
 *	A char pointer or NULL (see TxGetLine).
 *
 * Side Effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

char *
TxGetLineWPrompt(
    char *dest,
    int maxChars,
    const char *prompt,
    const char *prefix)
{
    char *res, *hist_res, *tmp;
    int return_nothing = 0;

    if (txHavePrompt) TxUnPrompt();
#ifndef USE_READLINE
    /* without GNU readline */

    if (prompt != NULL) TxPrintf("%s", prompt);
    txReprint1 = prompt;

    res = TxGetLinePfix(&dest[0], maxChars, prefix);
    txReprint1 = NULL;
#else
    _rl_prefix = prefix;
    TxResetTerminal (FALSE);

    if (prompt != NULL) {
      res = readline (prompt);
    } else {
      res = readline (TX_CMD_PROMPT);
    }

    hist_res = (char *)NULL;

    if (res && *res && !StrIsWhite(res, FALSE) ) {
      /* The history_expand() function from Readline can return 4 values: */
      /* -1 - There was an error in expansion. Command not added to history. */
      /*      This is the same behavior as tcsh. */
      /*  0 - No expansion possible. Just a normal command. Add command to history, */
      /*      return and execute. */
      /*  1 - Expansion took place. Assign $command to expansion and proceed. */
      /*  2 - Expansion took place (eg. :p modifier was used). Display exspansion */
      /*      but don't execute. Expanded command implicitly added to history by */
      /*      history_expand(). */
      switch( history_expand(res, &hist_res) ) {
        case -1:
          TxError("%s\n", hist_res);
          return_nothing = 1;
          break;
        case 0:
          add_history(res);
          break;
        case 1:
          tmp = res;
          res = hist_res;
          hist_res = tmp;
          add_history(res);
          break;
        case 2:
          TxPrintf("%s\n", hist_res);
          return_nothing = 1;
          break;
      }
    }

    if (res) {
      if (strlen (res) >= maxChars) {
        TxPrintf ("WARNING: input string too long; truncated");
        res[maxChars-1] = '\0';
      }
      if( return_nothing ) {
        dest[0] = '\0';
      } else {
        strcpy (dest, res);
      }
#undef free
      free (res);
      if( hist_res != (char *)NULL ) free(hist_res);
#define free You_should_use_the_Magic_procedure_freeMagic
      res = dest;
    }
    TxSetTerminal ();
#endif /* USE_READLINE */
    return res;
}

/*
 * ----------------------------------------------------------------------------
 */

char *
TxGetLinePrompt(
    char *dest,
    int maxChars,
    const char *prompt)
{
    return TxGetLineWPrompt(dest, maxChars, prompt, NULL);
}

#ifndef MAGIC_WRAPPER


/*
 * ----------------------------------------------------------------------------
 * TxGetLinePfix:
 *
 * 	Reads a line from the input queue (terminal or whatever).
 *
 * Results:
 *	A char pointer to the string is returned.
 *	If an end-of-file is typed, returns (char *) NULL and
 *	stores in the string any characters recieved up to that point.
 *
 * Side effects:
 *	The input stream is read, and 'dest' is filled in with up to maxChars-1
 *	characters.  Up to maxChars of the 'dest' may be changed during the
 *	input process, however, since a '\0' is added at the end.  There is no
 *	newline at the end of 'dest'.
 * ----------------------------------------------------------------------------
 */

char *
TxGetLinePfix(
    char *dest,
    int maxChars,
    const char *prefix)
{
    int i;
    char *ret;
    int ch;
    bool literal;

    if (maxChars < 1) return dest;
    if (txHavePrompt) TxUnPrompt();
    ret = dest;
    dest[0] = '\0';
    txReprint2 = dest;

#define TX_ERASE()	( (i>0) ?(i--, TxPrintf("\b \b"), 0) :0)

    (void) fflush(stderr);
    literal = FALSE;
    i = 0;
    if (prefix != NULL)
    {
	while (prefix[i] != '\0') {
	    if (i >= maxChars - 1) break;
	    dest[i] = prefix[i];
	    txFprintfBasic(stdout, "%c", prefix[i]);
	    i++;
	}
    }
    while (TRUE) {
	dest[i] = '\0';
	if (i >= maxChars - 1) break;
	(void) fflush(stdout);
	ch = TxGetChar();
	if (ch == EOF || ch == -1 || ch == TxEOFChar) {
	    TxError("\nEOF encountered on input stream.\n");
	    ret = NULL;
	    break;
	} else if (literal) {
	    literal = FALSE;
	    dest[i] = ch;
	    txFprintfBasic(stdout, "%c", ch);
	    i++;
	} else if (ch == '\n' || ch == txBreakChar) {
	    break;
	} else if (ch == '\t') {
	    while (TRUE) {
		dest[i] = ' ';
		txFprintfBasic(stdout, " ");
		i++;
		if (((i + strlen(txReprint1)) % 8) == 0) break;
	    }
	} else if (ch == txEraseChar) {
	    TX_ERASE();
	} else if (ch == txKillChar) {
	    while (i > 0) TX_ERASE();
	} else if (ch == txWordChar) {
	    while (i > 0 && isspace(dest[i-1])) TX_ERASE();
	    while (i > 0 && !isspace(dest[i-1])) TX_ERASE();
	} else if (ch == txReprintChar) {
	    TxReprint();
	} else if (ch == txLitChar) {
	    literal = TRUE;
	} else {
	    dest[i] = ch;
	    txFprintfBasic(stdout, "%c", ch);
	    i++;
	}
    }
    txFprintfBasic(stdout, "\n");
    txHavePrompt = FALSE;
    txReprint2 = NULL;

    return ret;
}

#endif /* !MAGIC_WRAPPER */

/*
 * ----------------------------------------------------------------------------
 * TxGetLine:
 */

char *
TxGetLine(
    char *dest,
    int maxChars)
{
    return TxGetLinePfix(dest, maxChars, NULL);
}


/*
 * ----------------------------------------------------------------------------
 * txGetTermState:
 *
 *	Read the state of the terminal and driver.
 *
 * Results:
 *	none.
 *
 * Side effects:
 *	The passed structure is filled in.
 * ----------------------------------------------------------------------------
 */

void
txGetTermState(
    txTermState *buf)
{
#if defined(HAVE_TERMIOS_H)
    (void) tcgetattr(fileno(stdin), &buf->termios);
#elif defined(HAVE_TERMIO_H)
    ioctl(fileno(stdin), TCGETA, &buf->termio);
#else /* sgtty */
    ASSERT(TxStdinIsatty, "txGetTermState");
    /* save the current terminal characteristics */
    (void) ioctl(fileno(stdin), TIOCGETP, (char *) &buf->tx_i_sgtty);
    (void) ioctl(fileno(stdin), TIOCGETC, (char *) &buf->tx_i_tchars);
#endif
}


/*
 * ----------------------------------------------------------------------------
 * txSetTermState:
 *
 *	Set the state of the terminal and driver.
 *
 * Results:
 *	none.
 *
 * Side effects:
 *	The terminal driver is set up.
 * ----------------------------------------------------------------------------
 */

void
txSetTermState(
    txTermState *buf)
{
#if defined(HAVE_TERMIOS_H)
    (void) tcsetattr(fileno(stdin), TCSANOW, &buf->termios);
#elif defined(HAVE_TERMIO_H)
    ioctl(fileno(stdin), TCSETAF, &buf->termio);
#else /* sgtty */
    /* set the current terminal characteristics */
    (void) ioctl(fileno(stdin), TIOCSETN, (char *) &buf->tx_i_sgtty);
    (void) ioctl(fileno(stdin), TIOCSETC, (char *) &buf->tx_i_tchars);
#endif
}



/*
 * ----------------------------------------------------------------------------
 * txInitTermRec:
 *
 * 	Sets the terminal record that it has echo turned off,
 *	cbreak on, and no EOFs the way we like it.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The passed terminal record is modified.
 *	No terminal modes are actually changed.
 * ----------------------------------------------------------------------------
 */

void
txInitTermRec(
    txTermState *buf)
{
#if defined(HAVE_TERMIOS_H)
    buf->termios.c_lflag = ISIG;    /* raw: no echo and no processing, allow signals */
    buf->termios.c_cc[ VMIN ] = 1;
    buf->termios.c_cc[ VTIME ] = 0;
#elif defined(HAVE_TERMIO_H)
    buf->termio.c_lflag = ISIG;    /* raw: no echo and no processing, allow signals */
    buf->termio.c_cc[ VMIN ] = 1;
    buf->termio.c_cc[ VTIME ] = 0;
#else /* sgtty */
    /* set things up for us, turn off echo, turn on cbreak, no EOF */
    buf->tx_i_sgtty.sg_flags |= CBREAK;
    buf->tx_i_sgtty.sg_flags &= ~ECHO;
    buf->tx_i_tchars.t_eofc = -1;
#endif
}



static txTermState closeTermState;

static bool haveCloseState = FALSE;

/*
 * ----------------------------------------------------------------------------
 * txSaveTerm:
 *
 * 	Save the terminal characteristics so they can be restored when
 *	magic leaves.
 *
 *
 * Results:
 *	none.
 *
 * Side effects:
 *	a global variable is set.
 * ----------------------------------------------------------------------------
 */

void
txSaveTerm(void)
{
#if defined(HAVE_TERMIOS_H)
    (void) tcgetattr(fileno(stdin), &closeTermState.termios);
    txEraseChar = closeTermState.termios.c_cc[VERASE];
    txKillChar = closeTermState.termios.c_cc[VKILL];
    TxEOFChar = closeTermState.termios.c_cc[VEOF];
    TxInterruptChar = closeTermState.termios.c_cc[VINTR];
    haveCloseState = TRUE;
#elif defined(HAVE_TERMIO_H)
    ioctl(fileno(stdin), TCGETA, &closeTermState.termio);
    txEraseChar = closeTermState.termio.c_cc[VERASE];
    txKillChar = closeTermState.termio.c_cc[VKILL];
    TxEOFChar = closeTermState.termio.c_cc[VEOF];
    TxInterruptChar = closeTermState.termio.c_cc[VINTR];
    haveCloseState = TRUE;
#else /* sgtty */
    struct ltchars lt;
    txGetTermState(&closeTermState);
    (void) ioctl(fileno(stdin), TIOCGLTC, (char *) &lt);
    txEraseChar = closeTermState.tx_i_sgtty.sg_erase;
    txKillChar = closeTermState.tx_i_sgtty.sg_kill;
    txWordChar = lt.t_werasc;
    txReprintChar = lt.t_rprntc;
    txLitChar = lt.t_lnextc;
    txBreakChar = closeTermState.tx_i_tchars.t_brkc;
    TxEOFChar = closeTermState.tx_i_tchars.t_eofc;
    TxInterruptChar = closeTermState.tx_i_tchars.t_intrc;
    haveCloseState = TRUE;
#endif
}


/*
 * ----------------------------------------------------------------------------
 * TxSetTerminal:
 *
 * 	Sets the terminal up the way we like it.
 *
 * Results:
 *	none.
 *
 * Side effects:
 *	The terminal modes are changed.
 * ----------------------------------------------------------------------------
 */

void
TxSetTerminal(void)
{
    txTermState buf;

#ifdef MAGIC_WRAPPER
    /* If using Tk console, don't mess with the terminal settings;	  */
    /* Otherwise, this prevents running magic in the terminal background. */
    if (TxTkConsole) return;
#endif

    if (TxStdinIsatty)
    {
	if (!haveCloseState) txSaveTerm();
	buf = closeTermState;
	txInitTermRec(&buf);
	txSetTermState(&buf);
    }
}


/*
 * ----------------------------------------------------------------------------
 * TxResetTerminal:
 *
 * 	Returns the terminal to the way it was when Magic started up.
 *
 *      'force' maybe set for atexit() restoration, when the console
 *      input handler is no longer in effect.  Also crash handling, SIGSTOP.
 *
 * Results:
 *	none.
 *
 * Side effects:
 *	The terminal modes are changed.
 * ----------------------------------------------------------------------------
 */

void
TxResetTerminal(
    bool force)
{
#ifdef MAGIC_WRAPPER
    /* If using Tk console, don't mess with the terminal settings;	  */
    /* Otherwise, this prevents running magic in the terminal background. */
    if (TxTkConsole && !force) return;
#endif

    if (TxStdinIsatty && haveCloseState) txSetTermState(&closeTermState);
}
