magic/textio/txInput.c

1459 lines
38 KiB
C

/*
* 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/magsgtty.h"
#include "utils/magic.h"
#include "utils/magsgtty.h"
#include "utils/main.h"
#include "textio/textio.h"
#include "utils/geometry.h"
#include "textio/txcommands.h"
#include "textio/textioInt.h"
#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(
#if defined(HAVE_TERMIOS_H)
struct termios *buf
#elif defined(HAVE_TERMIO_H)
struct termio *buf
#else
txTermState *buf
#endif
)
{
#if defined(HAVE_TERMIOS_H) /* POSIX */
(void) tcgetattr(fileno(stdin), buf);
#elif defined(HAVE_TERMIO_H) /* SYSV */
ioctl( fileno( stdin ), TCGETA, buf);
#else /* fallback to sgtty-style */
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(
#if defined(HAVE_TERMIOS_H)
struct termios *buf
#elif defined(HAVE_TERMIO_H)
struct termio *buf
#else
txTermState *buf
#endif
)
{
#if defined(HAVE_TERMIOS_H) /* POSIX */
(void) tcsetattr( fileno(stdin), TCSANOW, buf );
#elif defined(HAVE_TERMIO_H) /* SYSV */
ioctl( fileno(stdin), TCSETAF, buf );
#else /* fallback to sgtty-style */
/* 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(
#if defined(HAVE_TERMIOS_H)
struct termios *buf
#elif defined(HAVE_TERMIO_H)
struct termio *buf
#else
txTermState *buf
#endif
)
{
#if defined(HAVE_TERMIOS_H) || defined(HAVE_TERMIO_H)
buf->c_lflag = ISIG; /* raw: no echo and no processing, allow signals */
buf->c_cc[ VMIN ] = 1;
buf->c_cc[ VTIME ] = 0;
#else /* sgtty-style interface */
/* 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
}
#if defined(HAVE_TERMIOS_H)
struct termios closeTermState;
#elif defined(HAVE_TERMIO_H)
struct termio closeTermState;
#else
static txTermState closeTermState;
#endif
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) || defined(HAVE_TERMIO_H)
# if defined(HAVE_TERMIOS_H)
(void) tcgetattr( fileno( stdin ), &closeTermState);
# else /* HAVE_TERMIO_H */
ioctl( fileno( stdin ), TCGETA, &closeTermState);
# endif /* HAVE_TERMIOS_H || HAVE_TERMIO_H */
txEraseChar = closeTermState.c_cc[VERASE];
txKillChar = closeTermState.c_cc[VKILL];
TxEOFChar = closeTermState.c_cc[VEOF];
TxInterruptChar = closeTermState.c_cc[VINTR];
haveCloseState = TRUE;
#else /* sgtty-style interface */
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)
{
#if defined(HAVE_TERMIOS_H)
struct termios buf;
#elif defined(HAVE_TERMIO_H)
struct termio buf;
#else
txTermState buf;
#endif
#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);
}