Commit 0c9f8376 authored by unknown's avatar unknown

Merge tulin@bk-internal.mysql.com:/home/bk/mysql-4.1

into poseidon.ndb.mysql.com:/home/tomas/mysql-4.1

parents bf14010d 5fb792ec
......@@ -3093,7 +3093,6 @@ AC_CONFIG_FILES(ndb/Makefile ndb/include/Makefile dnl
ndb/src/common/logger/Makefile dnl
ndb/src/common/transporter/Makefile dnl
ndb/src/common/mgmcommon/Makefile dnl
ndb/src/common/editline/Makefile dnl
ndb/src/kernel/Makefile dnl
ndb/src/kernel/error/Makefile dnl
ndb/src/kernel/blocks/Makefile dnl
......
SUBDIRS = portlib debugger util logger transporter mgmcommon editline
SUBDIRS = portlib debugger util logger transporter mgmcommon
noinst_LTLIBRARIES = libcommon.la
......
include .defs.mk
LIB_DIRS := \
portlib \
debugger \
util \
logger
ifneq ($(USE_EDITLINE), N)
LIB_DIRS += editline
endif
DIRS := transporter mgmcommon
include $(NDB_TOP)/Epilogue.mk
File Name Description
--------------------------------------------------------
README Release notes and copyright
MANIFEST This shipping list
Make.os9 OS-9 makefile
Makefile Unix makefile
complete.c Filename completion routines
editline.3 Manual page for editline library
editline.c Line-editing routines
editline_internal.h Internal library header file
os9.h OS-9-specific declarations
sysos9.c OS-9-specific routines
sysunix.c Unix-specific routines
testit.c Test driver
unix.h Unix-specific declarations
noinst_LIBRARIES = libeditline.a
libeditline_a_SOURCES = complete.c editline.c sysunix.c
INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/ndb/include
DEFS = -DANSI_ARROWS -DHAVE_TCGETATTR -DSYS_UNIX
# Don't update the files from bitkeeper
%::SCCS/s.%
include .defs.mk
TYPE :=
ARCHIVE_TARGET := editline
CFLAGS += -DANSI_ARROWS -DHAVE_TCGETATTR -DSYS_UNIX
ifeq ($(NDB_OS), WIN32)
SOURCES = editline_win32.c
else
SOURCES = complete.c editline.c sysunix.c
endif
DIRS := test
include $(NDB_TOP)/Epilogue.mk
--
NOTE: This version has been modified by Ericsson/Alzato. Please
see the cvs changelog for more details.
--
$Revision: 1.2 $
This is a line-editing library. It can be linked into almost any
program to provide command-line editing and recall.
It is call-compatible with the FSF readline library, but it is a
fraction of the size (and offers fewer features). It does not use
standard I/O. It is distributed under a "C News-like" copyright.
Configuration is done in the Makefile. Type "make testit" to get
a small slow shell for testing.
This contains some changes since the posting to comp.sources.misc:
- Bugfix for completion on absolute pathnames.
- Better handling of M-n versus showing raw 8bit chars.
- Better signal handling.
- Now supports termios/termio/sgttyb ioctl's.
- Add M-m command to toggle how 8bit data is displayed.
The following changes, made since the last public release, come from
J.G. Vons <vons@cesar.crbca1.sinet.slb.com>:
- History-searching no longer redraws the line wrong
- Added ESC-ESC as synonym for ESC-?
- SIGQUIT (normally ^\) now sends a signal, not indicating EOF.
- Fixed some typo's and unclear wording in the manpage.
- Fixed completion when all entries shared a common prefix.
- Fixed some meta-char line-redrawing bugs.
Enjoy,
Rich $alz
<rsalz@osf.org>
Copyright 1992,1993 Simmule Turner and Rich Salz. All rights reserved.
This software is not subject to any license of the American Telephone
and Telegraph Company or of the Regents of the University of California.
Permission is granted to anyone to use this software for any purpose on
any computer system, and to alter it and redistribute it freely, subject
to the following restrictions:
1. The authors are not responsible for the consequences of use of this
software, no matter how awful, even if they arise from flaws in it.
2. The origin of this software must not be misrepresented, either by
explicit claim or by omission. Since few users ever read sources,
credits must appear in the documentation.
3. Altered versions must be plainly marked as such, and must not be
misrepresented as being the original software. Since few users
ever read sources, credits must appear in the documentation.
4. This notice may not be removed or altered.
/* -*- c-basic-offset: 4; -*-
** $Revision: 1.8 $
**
** History and file completion functions for editline library.
*/
#include "editline_internal.h"
/*
** strcmp-like sorting predicate for qsort.
*/
static int
compare(const void *p1, const void *p2)
{
const char **v1;
const char **v2;
v1 = (const char **)p1;
v2 = (const char **)p2;
return strcmp(*v1, *v2);
}
/*
** Fill in *avp with an array of names that match file, up to its length.
** Ignore . and .. .
*/
static int
FindMatches(char *dir, char *file, char ***avp)
{
char **av;
char **new;
char *p;
DIR *dp;
struct dirent *ep;
size_t ac;
size_t len;
if ((dp = opendir(dir)) == NULL)
return 0;
av = NULL;
ac = 0;
len = strlen(file);
while ((ep = readdir(dp)) != NULL) {
p = ep->d_name;
if (p[0] == '.' && (p[1] == '\0' || (p[1] == '.' && p[2] == '\0')))
continue;
if (len && strncmp(p, file, len) != 0)
continue;
if ((ac % MEM_INC) == 0) {
if ((new = malloc(sizeof(char*) * (ac + MEM_INC))) == NULL)
break;
if (ac) {
memcpy(new, av, ac * sizeof (char **));
free(av);
}
*avp = av = new;
}
if ((av[ac] = strdup(p)) == NULL) {
if (ac == 0)
free(av);
break;
}
ac++;
}
/* Clean up and return. */
(void)closedir(dp);
if (ac)
qsort(av, ac, sizeof (char **), compare);
return ac;
}
/*
** Split a pathname into allocated directory and trailing filename parts.
*/
static int
SplitPath(char *path, char **dirpart, char ** filepart)
{
static char DOT[] = ".";
char *dpart;
char *fpart;
if ((fpart = strrchr(path, '/')) == NULL) {
if ((dpart = strdup(DOT)) == NULL)
return -1;
if ((fpart = strdup(path)) == NULL) {
free(dpart);
return -1;
}
}
else {
if ((dpart = strdup(path)) == NULL)
return -1;
dpart[fpart - path + 1] = '\0';
if ((fpart = strdup(++fpart)) == NULL) {
free(dpart);
return -1;
}
}
*dirpart = dpart;
*filepart = fpart;
return 0;
}
/*
** Attempt to complete the pathname, returning an allocated copy.
** Fill in *unique if we completed it, or set it to 0 if ambiguous.
*/
char *
rl_complete(char *pathname,int *unique)
{
char **av;
char *dir;
char *file;
char *new;
char *p;
size_t ac;
size_t end;
size_t i;
size_t j;
size_t len;
size_t new_len;
size_t p_len;
if (SplitPath(pathname, &dir, &file) < 0)
return NULL;
if ((ac = FindMatches(dir, file, &av)) == 0) {
free(dir);
free(file);
return NULL;
}
p = NULL;
len = strlen(file);
if (ac == 1) {
/* Exactly one match -- finish it off. */
*unique = 1;
j = strlen(av[0]) - len + 2;
p_len = sizeof(char) * (j + 1);
if ((p = malloc(p_len)) != NULL) {
memcpy(p, av[0] + len, j);
new_len = sizeof(char) * (strlen(dir) + strlen(av[0]) + 2);
new = malloc(new_len);
if(new != NULL) {
snprintf(new, new_len, "%s/%s", dir, av[0]);
rl_add_slash(new, p, p_len);
free(new);
}
}
}
else {
/* Find largest matching substring. */
for (*unique = 0, i = len, end = strlen(av[0]); i < end; i++)
for (j = 1; j < ac; j++)
if (av[0][i] != av[j][i])
goto breakout;
breakout:
if (i > len) {
j = i - len + 1;
if ((p = malloc(sizeof(char) * j)) != NULL) {
memcpy(p, av[0] + len, j);
p[j - 1] = '\0';
}
}
}
/* Clean up and return. */
free(dir);
free(file);
for (i = 0; i < ac; i++)
free(av[i]);
free(av);
return p;
}
/*
** Return all possible completions.
*/
int
rl_list_possib(char *pathname, char ***avp)
{
char *dir;
char *file;
int ac;
if (SplitPath(pathname, &dir, &file) < 0)
return 0;
ac = FindMatches(dir, file, avp);
free(dir);
free(file);
return ac;
}
.\" $Revision: 1.1 $
.TH EDITLINE 3
.SH NAME
editline \- command-line editing library with history
.SH SYNOPSIS
.nf
.B "char *"
.B "readline(prompt)"
.B " char *prompt;"
.B "void"
.B "add_history(line)"
.B " char *line;"
.fi
.SH DESCRIPTION
.I Editline
is a library that provides an line-editing interface with text recall.
It is intended to be compatible with the
.I readline
library provided by the Free Software Foundation, but much smaller.
The bulk of this manual page describes the user interface.
.PP
The
.I readline
routine returns a line of text with the trailing newline removed.
The data is returned in a buffer allocated with
.IR malloc (3),
so the space should be released with
.IR free (3)
when the calling program is done with it.
Before accepting input from the user, the specified
.I prompt
is displayed on the terminal.
.PP
The
.I add_history
routine makes a copy of the specified
.I line
and adds it to the internal history list.
.SS "User Interface"
A program that uses this library provides a simple emacs-like editing
interface to its users.
A line may be edited before it is sent to the calling program by typing either
control characters or escape sequences.
A control character, shown as a caret followed by a letter, is typed by
holding down the ``control'' key while the letter is typed.
For example, ``^A'' is a control-A.
An escape sequence is entered by typing the ``escape'' key followed by one or
more characters.
The escape key is abbreviated as ``ESC''.
Note that unlike control keys, case matters in escape sequences; ``ESC\ F''
is not the same as ``ESC\ f''.
.PP
An editing command may be typed anywhere on the line, not just at the
beginning.
In addition, a return may also be typed anywhere on the line, not just at
the end.
.PP
Most editing commands may be given a repeat count,
.IR n ,
where
.I n
is a number.
To enter a repeat count, type the escape key, the number, and then
the command to execute.
For example, ``ESC\ 4\ ^f'' moves forward four characters.
If a command may be given a repeat count then the text ``[n]'' is given at the
end of its description.
.PP
The following control characters are accepted:
.RS
.nf
.ta \w'ESC DEL 'u
^A Move to the beginning of the line
^B Move left (backwards) [n]
^D Delete character [n]
^E Move to end of line
^F Move right (forwards) [n]
^G Ring the bell
^H Delete character before cursor (backspace key) [n]
^I Complete filename (tab key); see below
^J Done with line (return key)
^K Kill to end of line (or column [n])
^L Redisplay line
^M Done with line (alternate return key)
^N Get next line from history [n]
^P Get previous line from history [n]
^R Search backward (forward if [n]) through history for text;
\& prefixing the string with a caret (^) forces it to
\& match only at the beginning of a history line
^T Transpose characters
^V Insert next character, even if it is an edit command
^W Wipe to the mark
^X^X Exchange current location and mark
^Y Yank back last killed text
^[ Start an escape sequence (escape key)
^]c Move forward to next character ``c''
^? Delete character before cursor (delete key) [n]
.fi
.RE
.PP
The following escape sequences are provided.
.RS
.nf
.ta \w'ESC DEL 'u
ESC\ ^H Delete previous word (backspace key) [n]
ESC\ DEL Delete previous word (delete key) [n]
ESC\ ESC Show possible completions; see below
ESC\ SP Set the mark (space key); see ^X^X and ^Y above
ESC\ . Get the last (or [n]'th) word from previous line
ESC\ ? Show possible completions; see below
ESC\ < Move to start of history
ESC\ > Move to end of history
ESC\ b Move backward a word [n]
ESC\ d Delete word under cursor [n]
ESC\ f Move forward a word [n]
ESC\ l Make word lowercase [n]
ESC\ m Toggle if 8bit chars display as themselves or with
\& an ``M\-'' prefix
ESC\ u Make word uppercase [n]
ESC\ y Yank back last killed text
ESC\ w Make area up to mark yankable
ESC\ nn Set repeat count to the number nn
ESC\ C Read from environment variable ``_C_'', where C is
\& an uppercase letter
.fi
.RE
.PP
The
.I editline
library has a small macro facility.
If you type the escape key followed by an uppercase letter,
.IR C ,
then the contents of the environment variable
.I _C_
are read in as if you had typed them at the keyboard.
For example, if the variable
.I _L_
contains the following:
.RS
^A^Kecho '^V^[[H^V^[[2J'^M
.RE
Then typing ``ESC L'' will move to the beginning of the line, kill the
entire line, enter the echo command needed to clear the terminal (if your
terminal is like a VT-100), and send the line back to the shell.
.PP
The
.I editline
library also does filename completion.
Suppose the root directory has the following files in it:
.RS
.nf
.ta \w'core 'u
bin vmunix
core vmunix.old
.fi
.RE
If you type ``rm\ /v'' and then the tab key.
.I Editline
will then finish off as much of the name as possible by adding ``munix''.
Because the name is not unique, it will then beep.
If you type the escape key followed by either a question mark or another
escape, it will display the two choices.
If you then type a period and a tab, the library will finish off the filename
for you:
.RS
.nf
.RI "rm /v[TAB]" munix ".[TAB]" old
.fi
.RE
The tab key is shown by ``[TAB]'' and the automatically-entered text
is shown in italics.
.SH "BUGS AND LIMITATIONS"
Cannot handle lines more than 80 columns.
.SH AUTHORS
Simmule R. Turner <uunet.uu.net!capitol!sysgo!simmy>
and Rich $alz <rsalz@osf.org>.
Original manual page by DaviD W. Sanderson <dws@ssec.wisc.edu>.
/* -*- c-basic-offset: 4; -*-
** $Revision: 1.6 $
**
** Main editing routines for editline library.
*/
#include <ndb_global.h>
#include "editline_internal.h"
#include <signal.h>
/*
** Manifest constants.
*/
#define SCREEN_WIDTH 80
#define SCREEN_ROWS 24
#define NO_ARG (-1)
#define DEL 127
#define TAB '\t'
#define CTL(x) ((x) & 0x1F)
#define ISCTL(x) ((x) && (x) < ' ')
#define UNCTL(x) ((x) + 64)
#define META(x) ((x) | 0x80)
#define ISMETA(x) ((x) & 0x80)
#define UNMETA(x) ((x) & 0x7F)
#define MAPSIZE 32
#define METAMAPSIZE 16
#if !defined(HIST_SIZE)
#define HIST_SIZE 20
#endif /* !defined(HIST_SIZE) */
/*
** Command status codes.
*/
typedef enum _STATUS {
CSdone, CSeof, CSmove, CSdispatch, CSstay, CSsignal
} STATUS;
/*
** The type of case-changing to perform.
*/
typedef enum _CASE {
TOupper, TOlower
} CASE;
/*
** Key to command mapping.
*/
typedef struct _KEYMAP {
char Key;
char Active;
STATUS (*Function)();
} KEYMAP;
/*
** Command history structure.
*/
typedef struct _HISTORY {
int Size;
int Pos;
char *Lines[HIST_SIZE];
} HISTORY;
/*
** Globals.
*/
int rl_eof;
int rl_erase;
int rl_intr;
int rl_kill;
int rl_quit;
#if defined(DO_SIGTSTP)
int rl_susp;
#endif /* defined(DO_SIGTSTP) */
static char NIL[] = "";
static const char *Input = NIL;
static char *Line;
static const char *Prompt;
static char *Yanked;
static char *Screen;
static char NEWLINE[]= CRLF;
static HISTORY H;
static int Repeat;
static int End;
static int Mark;
static int OldPoint;
static int Point;
static int PushBack;
static int Pushed;
static int Signal;
static KEYMAP Map[MAPSIZE];
static KEYMAP MetaMap[METAMAPSIZE];
static size_t Length;
static size_t ScreenCount;
static size_t ScreenSize;
static char *backspace;
static int TTYwidth;
static int TTYrows;
/* Display print 8-bit chars as `M-x' or as the actual 8-bit char? */
int rl_meta_chars = 1;
/*
** Declarations.
*/
static char *editinput();
#if defined(USE_TERMCAP)
extern char *getenv();
extern char *tgetstr();
extern int tgetent();
extern int tgetnum();
#endif /* defined(USE_TERMCAP) */
/*
** TTY input/output functions.
*/
static void
TTYflush()
{
if (ScreenCount) {
(void)write(1, Screen, ScreenCount);
ScreenCount = 0;
}
}
static void
TTYput(const char c)
{
Screen[ScreenCount] = c;
if (++ScreenCount >= ScreenSize - 1) {
ScreenSize += SCREEN_INC;
Screen = realloc(Screen, sizeof(char) * ScreenSize);
/* XXX what to do if realloc failes? */
}
}
static void
TTYputs(const char *p)
{
while (*p)
TTYput(*p++);
}
static void
TTYshow(char c)
{
if (c == DEL) {
TTYput('^');
TTYput('?');
}
else if (c == TAB) {
/* XXX */
}
else if (ISCTL(c)) {
TTYput('^');
TTYput(UNCTL(c));
}
else if (rl_meta_chars && ISMETA(c)) {
TTYput('M');
TTYput('-');
TTYput(UNMETA(c));
}
else
TTYput(c);
}
static void
TTYstring(char *p)
{
while (*p)
TTYshow(*p++);
}
static int
TTYget()
{
char c;
TTYflush();
if (Pushed) {
Pushed = 0;
return PushBack;
}
if (*Input)
return *Input++;
return read(0, &c, (size_t)1) == 1 ? c : EOF;
}
#define TTYback() (backspace ? TTYputs((const char *)backspace) : TTYput('\b'))
static void
TTYbackn(int n)
{
while (--n >= 0)
TTYback();
}
static void
TTYinfo()
{
static int init;
#if defined(USE_TERMCAP)
char *term;
char buff[2048];
char *bp;
char *p;
#endif /* defined(USE_TERMCAP) */
#if defined(TIOCGWINSZ)
struct winsize W;
#endif /* defined(TIOCGWINSZ) */
if (init) {
#if defined(TIOCGWINSZ)
/* Perhaps we got resized. */
if (ioctl(0, TIOCGWINSZ, &W) >= 0
&& W.ws_col > 0 && W.ws_row > 0) {
TTYwidth = (int)W.ws_col;
TTYrows = (int)W.ws_row;
}
#endif /* defined(TIOCGWINSZ) */
return;
}
init++;
TTYwidth = TTYrows = 0;
#if defined(USE_TERMCAP)
bp = &buff[0];
if ((term = getenv("TERM")) == NULL)
term = "dumb";
if (tgetent(buff, term) < 0) {
TTYwidth = SCREEN_WIDTH;
TTYrows = SCREEN_ROWS;
return;
}
p = tgetstr("le", &bp);
backspace = p ? strdup(p) : NULL;
TTYwidth = tgetnum("co");
TTYrows = tgetnum("li");
#endif /* defined(USE_TERMCAP) */
#if defined(TIOCGWINSZ)
if (ioctl(0, TIOCGWINSZ, &W) >= 0) {
TTYwidth = (int)W.ws_col;
TTYrows = (int)W.ws_row;
}
#endif /* defined(TIOCGWINSZ) */
if (TTYwidth <= 0 || TTYrows <= 0) {
TTYwidth = SCREEN_WIDTH;
TTYrows = SCREEN_ROWS;
}
}
/*
** Print an array of words in columns.
*/
static void
columns(int ac, char **av)
{
char *p;
int i;
int j;
int k;
int len;
int skip;
int longest;
int cols;
/* Find longest name, determine column count from that. */
for (longest = 0, i = 0; i < ac; i++)
if ((j = strlen((char *)av[i])) > longest)
longest = j;
cols = TTYwidth / (longest + 3);
TTYputs((const char *)NEWLINE);
for (skip = ac / cols + 1, i = 0; i < skip; i++) {
for (j = i; j < ac; j += skip) {
for (p = av[j], len = strlen((char *)p), k = len; --k >= 0; p++)
TTYput(*p);
if (j + skip < ac)
while (++len < longest + 3)
TTYput(' ');
}
TTYputs((const char *)NEWLINE);
}
}
static void
reposition()
{
int i;
char *p;
TTYput('\r');
TTYputs((const char *)Prompt);
for (i = Point, p = Line; --i >= 0; p++)
TTYshow(*p);
}
static void
left(STATUS Change)
{
char c;
TTYback();
if (Point) {
c = Line[Point - 1];
if (c == TAB) {
/* XXX */
}
else if (ISCTL(c))
TTYback();
else if (rl_meta_chars && ISMETA(c)) {
TTYback();
TTYback();
}
}
if (Change == CSmove)
Point--;
}
static void
right(STATUS Change)
{
TTYshow(Line[Point]);
if (Change == CSmove)
Point++;
}
static STATUS
ring_bell()
{
TTYput('\07');
TTYflush();
return CSstay;
}
static STATUS
do_macro(int c)
{
char name[4];
name[0] = '_';
name[1] = c;
name[2] = '_';
name[3] = '\0';
if ((Input = (char *)getenv((char *)name)) == NULL) {
Input = NIL;
return ring_bell();
}
return CSstay;
}
static STATUS
do_forward(STATUS move)
{
int i;
char *p;
i = 0;
do {
p = &Line[Point];
for ( ; Point < End && (*p == ' ' || !isalnum((int)*p)); Point++, p++)
if (move == CSmove)
right(CSstay);
for (; Point < End && isalnum((int)*p); Point++, p++)
if (move == CSmove)
right(CSstay);
if (Point == End)
break;
} while (++i < Repeat);
return CSstay;
}
static STATUS
do_case(CASE type)
{
int i;
int end;
int count;
char *p;
(void)do_forward(CSstay);
if (OldPoint != Point) {
if ((count = Point - OldPoint) < 0)
count = -count;
Point = OldPoint;
if ((end = Point + count) > End)
end = End;
for (i = Point, p = &Line[i]; i < end; i++, p++) {
if (type == TOupper) {
if (islower((int)*p))
*p = toupper((int)*p);
}
else if (isupper((int)*p))
*p = tolower((int)*p);
right(CSmove);
}
}
return CSstay;
}
static STATUS
case_down_word()
{
return do_case(TOlower);
}
static STATUS
case_up_word()
{
return do_case(TOupper);
}
static void
ceol()
{
int extras;
int i;
char *p;
for (extras = 0, i = Point, p = &Line[i]; i <= End; i++, p++) {
TTYput(' ');
if (*p == TAB) {
/* XXX */
}
else if (ISCTL(*p)) {
TTYput(' ');
extras++;
}
else if (rl_meta_chars && ISMETA(*p)) {
TTYput(' ');
TTYput(' ');
extras += 2;
}
}
for (i += extras; i > Point; i--)
TTYback();
}
static void
clear_line()
{
Point = -strlen(Prompt);
TTYput('\r');
ceol();
Point = 0;
End = 0;
Line[0] = '\0';
}
static STATUS
insert_string(char *p)
{
size_t len;
int i;
char *new;
char *q;
len = strlen((char *)p);
if (End + len >= Length) {
if ((new = malloc(sizeof(char) * (Length + len + MEM_INC))) == NULL)
return CSstay;
if (Length) {
memcpy(new, Line, Length);
free(Line);
}
Line = new;
Length += len + MEM_INC;
}
for (q = &Line[Point], i = End - Point; --i >= 0; )
q[len + i] = q[i];
memcpy(&Line[Point], p, len);
End += len;
Line[End] = '\0';
TTYstring(&Line[Point]);
Point += len;
return Point == End ? CSstay : CSmove;
}
static STATUS
redisplay()
{
TTYputs((const char *)NEWLINE);
TTYputs((const char *)Prompt);
TTYstring(Line);
return CSmove;
}
static STATUS
redisplay_no_nl()
{
TTYput('\r');
TTYputs((const char *)Prompt);
TTYstring(Line);
return CSmove;
}
static STATUS
toggle_meta_mode()
{
rl_meta_chars = !rl_meta_chars;
return redisplay();
}
static char *
next_hist()
{
return H.Pos >= H.Size - 1 ? NULL : H.Lines[++H.Pos];
}
static char *
prev_hist()
{
return H.Pos == 0 ? NULL : H.Lines[--H.Pos];
}
static STATUS
do_insert_hist(char *p)
{
if (p == NULL)
return ring_bell();
Point = 0;
reposition();
ceol();
End = 0;
return insert_string(p);
}
static STATUS
do_hist(char *(*move)())
{
char *p;
int i;
i = 0;
do {
if ((p = (*move)()) == NULL)
return ring_bell();
} while (++i < Repeat);
return do_insert_hist(p);
}
static STATUS
h_next()
{
return do_hist(next_hist);
}
static STATUS
h_prev()
{
return do_hist(prev_hist);
}
static STATUS
h_first()
{
return do_insert_hist(H.Lines[H.Pos = 0]);
}
static STATUS
h_last()
{
return do_insert_hist(H.Lines[H.Pos = H.Size - 1]);
}
/*
** Return zero if pat appears as a substring in text.
*/
static int
substrcmp(char *text, char *pat,int len)
{
char c;
if ((c = *pat) == '\0')
return *text == '\0';
for ( ; *text; text++)
if (*text == c && strncmp(text, pat, len) == 0)
return 0;
return 1;
}
static char *
search_hist(char *search,char *(*move)())
{
static char *old_search;
int len;
int pos;
int (*match)();
char *pat;
/* Save or get remembered search pattern. */
if (search && *search) {
if (old_search)
free(old_search);
old_search = strdup(search);
}
else {
if (old_search == NULL || *old_search == '\0')
return NULL;
search = old_search;
}
/* Set up pattern-finder. */
if (*search == '^') {
match = strncmp;
pat = (char *)(search + 1);
}
else {
match = substrcmp;
pat = (char *)search;
}
len = strlen(pat);
for (pos = H.Pos; (*move)() != NULL; )
if ((*match)((char *)H.Lines[H.Pos], pat, len) == 0)
return H.Lines[H.Pos];
H.Pos = pos;
return NULL;
}
static STATUS
h_search()
{
static int Searching;
const char *old_prompt;
char *(*move)();
char *p;
if (Searching)
return ring_bell();
Searching = 1;
clear_line();
old_prompt = Prompt;
Prompt = "Search: ";
TTYputs((const char *)Prompt);
move = Repeat == NO_ARG ? prev_hist : next_hist;
p = editinput();
Searching = 0;
if (p == NULL && Signal > 0) {
Signal = 0;
clear_line();
Prompt = old_prompt;
return redisplay_no_nl();
}
p = search_hist(p, move);
clear_line();
Prompt = old_prompt;
if (p == NULL) {
(void)ring_bell();
return redisplay_no_nl();
}
return do_insert_hist(p);
}
static STATUS
fd_char()
{
int i;
i = 0;
do {
if (Point >= End)
break;
right(CSmove);
} while (++i < Repeat);
return CSstay;
}
static void
save_yank(int begin, int i)
{
if (Yanked) {
free(Yanked);
Yanked = NULL;
}
if (i < 1)
return;
if ((Yanked = malloc(sizeof(char) * (i + 1))) != NULL) {
memcpy(Yanked, &Line[begin], i);
Yanked[i] = '\0';
}
}
static STATUS
delete_string(int count)
{
int i;
char *p;
if (count <= 0 || End == Point)
return ring_bell();
if (count == 1 && Point == End - 1) {
/* Optimize common case of delete at end of line. */
End--;
p = &Line[Point];
i = 1;
TTYput(' ');
if (*p == TAB) {
/* XXX */
}
else if (ISCTL(*p)) {
i = 2;
TTYput(' ');
}
else if (rl_meta_chars && ISMETA(*p)) {
i = 3;
TTYput(' ');
TTYput(' ');
}
TTYbackn(i);
*p = '\0';
return CSmove;
}
if (Point + count > End && (count = End - Point) <= 0)
return CSstay;
if (count > 1)
save_yank(Point, count);
ceol();
for (p = &Line[Point], i = End - (Point + count) + 1; --i >= 0; p++)
p[0] = p[count];
End -= count;
TTYstring(&Line[Point]);
return CSmove;
}
static STATUS
bk_char()
{
int i;
i = 0;
do {
if (Point == 0)
break;
left(CSmove);
} while (++i < Repeat);
return CSstay;
}
static STATUS
bk_del_char()
{
int i;
i = 0;
do {
if (Point == 0)
break;
left(CSmove);
} while (++i < Repeat);
return delete_string(i);
}
static STATUS
kill_line()
{
int i;
if (Repeat != NO_ARG) {
if (Repeat < Point) {
i = Point;
Point = Repeat;
reposition();
(void)delete_string(i - Point);
}
else if (Repeat > Point) {
right(CSmove);
(void)delete_string(Repeat - Point - 1);
}
return CSmove;
}
save_yank(Point, End - Point);
ceol();
Line[Point] = '\0';
End = Point;
return CSstay;
}
static STATUS
insert_char(int c)
{
STATUS s;
char buff[2];
char *p;
char *q;
int i;
if (Repeat == NO_ARG || Repeat < 2) {
buff[0] = c;
buff[1] = '\0';
return insert_string(buff);
}
if ((p = malloc(sizeof(char) * (Repeat + 1))) == NULL)
return CSstay;
for (i = Repeat, q = p; --i >= 0; )
*q++ = c;
*q = '\0';
Repeat = 0;
s = insert_string(p);
free(p);
return s;
}
static STATUS
meta()
{
int c;
KEYMAP *kp;
if ((c = TTYget()) == EOF)
return CSeof;
#if defined(ANSI_ARROWS)
/* Also include VT-100 arrows. */
if (c == '[' || c == 'O')
switch ((int)(c = TTYget())) {
default: return ring_bell();
case EOF: return CSeof;
case 'A': return h_prev();
case 'B': return h_next();
case 'C': return fd_char();
case 'D': return bk_char();
}
#endif /* defined(ANSI_ARROWS) */
if (isdigit(c)) {
for (Repeat = c - '0'; (c = TTYget()) != EOF && isdigit(c); )
Repeat = Repeat * 10 + c - '0';
Pushed = 1;
PushBack = c;
return CSstay;
}
if (isupper(c))
return do_macro(c);
for (OldPoint = Point, kp = MetaMap; kp < &MetaMap[METAMAPSIZE]; kp++)
if (kp->Key == c && kp->Active)
return (*kp->Function)();
return ring_bell();
}
static STATUS
emacs(int c)
{
STATUS s;
KEYMAP *kp;
#if 0
/* This test makes it impossible to enter eight-bit characters when
* meta-char mode is enabled. */
if (rl_meta_chars && ISMETA(c)) {
Pushed = 1;
PushBack = UNMETA(c);
return meta();
}
#endif /* 0 */
for (kp = Map; kp < &Map[MAPSIZE]; kp++)
if (kp->Key == c && kp->Active)
break;
s = kp < &Map[MAPSIZE] ? (*kp->Function)() : insert_char((int)c);
if (!Pushed)
/* No pushback means no repeat count; hacky, but true. */
Repeat = NO_ARG;
return s;
}
static STATUS
TTYspecial(int c)
{
if (rl_meta_chars && ISMETA(c))
return CSdispatch;
if (c == rl_erase || c == DEL)
return bk_del_char();
if (c == rl_kill) {
if (Point != 0) {
Point = 0;
reposition();
}
Repeat = NO_ARG;
return kill_line();
}
if (c == rl_eof && Point == 0 && End == 0)
return CSeof;
if (c == rl_intr) {
Signal = SIGINT;
return CSsignal;
}
if (c == rl_quit) {
Signal = SIGQUIT;
return CSsignal;
}
#if defined(DO_SIGTSTP)
if (c == rl_susp) {
Signal = SIGTSTP;
return CSsignal;
}
#endif /* defined(DO_SIGTSTP) */
return CSdispatch;
}
static char *
editinput()
{
int c;
Repeat = NO_ARG;
OldPoint = Point = Mark = End = 0;
Line[0] = '\0';
Signal = -1;
while ((c = TTYget()) != EOF)
switch (TTYspecial(c)) {
case CSdone:
return Line;
case CSeof:
return NULL;
case CSsignal:
return (char *)"";
case CSmove:
reposition();
break;
case CSdispatch:
switch (emacs(c)) {
case CSdone:
return Line;
case CSeof:
return NULL;
case CSsignal:
return (char *)"";
case CSmove:
reposition();
break;
case CSdispatch:
case CSstay:
break;
}
break;
case CSstay:
break;
}
return NULL;
}
static void
hist_add(char *p)
{
int i;
if ((p = strdup(p)) == NULL)
return;
if (H.Size < HIST_SIZE)
H.Lines[H.Size++] = p;
else {
free(H.Lines[0]);
for (i = 0; i < HIST_SIZE - 1; i++)
H.Lines[i] = H.Lines[i + 1];
H.Lines[i] = p;
}
H.Pos = H.Size - 1;
}
static char *
read_redirected()
{
int size;
char *p;
char *line;
char *end;
for (size = MEM_INC, p = line = malloc(sizeof(char) * size), end = p + size; ; p++) {
if (p == end) {
size += MEM_INC;
p = line = realloc(line, size);
end = p + size;
}
if (read(0, p, 1) <= 0) {
/* Ignore "incomplete" lines at EOF, just like we do for a tty. */
free(line);
return NULL;
}
if (*p == '\n')
break;
}
*p = '\0';
return line;
}
/*
** For compatibility with FSF readline.
*/
/* ARGSUSED0 */
void
rl_reset_terminal(char *p)
{
(void)p; /* Suppress warning */
}
void
rl_initialize()
{
}
int
rl_insert(int count, int c)
{
if (count > 0) {
Repeat = count;
(void)insert_char(c);
(void)redisplay_no_nl();
}
return 0;
}
int (*rl_event_hook)();
int
rl_key_action(int c, char flag)
{
KEYMAP *kp;
int size;
(void)flag; /* Suppress warning */
if (ISMETA(c)) {
kp = MetaMap;
size = METAMAPSIZE;
}
else {
kp = Map;
size = MAPSIZE;
}
for ( ; --size >= 0; kp++)
if (kp->Key == c) {
kp->Active = c ? 1 : 0;
return 1;
}
return -1;
}
char *
readline(const char *prompt)
{
char *line;
int s;
if (!isatty(0)) {
TTYflush();
return read_redirected();
}
if (Line == NULL) {
Length = MEM_INC;
if ((Line = malloc(sizeof(char) * Length)) == NULL)
return NULL;
}
TTYinfo();
rl_ttyset(0);
hist_add(NIL);
ScreenSize = SCREEN_INC;
Screen = malloc(sizeof(char) * ScreenSize);
Prompt = prompt ? prompt : (char *)NIL;
TTYputs((const char *)Prompt);
if ((line = editinput()) != NULL) {
line = strdup(line);
TTYputs((const char *)NEWLINE);
TTYflush();
}
rl_ttyset(1);
free(Screen);
free(H.Lines[--H.Size]);
if (Signal > 0) {
s = Signal;
Signal = 0;
(void)kill(getpid(), s);
}
return (char *)line;
}
void
add_history(char *p)
{
if (p == NULL || *p == '\0')
return;
#if defined(UNIQUE_HISTORY)
if (H.Size && strcmp(p, H.Lines[H.Size - 1]) == 0)
return;
#endif /* defined(UNIQUE_HISTORY) */
hist_add((char *)p);
}
static STATUS
beg_line()
{
if (Point) {
Point = 0;
return CSmove;
}
return CSstay;
}
static STATUS
del_char()
{
return delete_string(Repeat == NO_ARG ? 1 : Repeat);
}
static STATUS
end_line()
{
if (Point != End) {
Point = End;
return CSmove;
}
return CSstay;
}
/*
** Return allocated copy of word under cursor, moving cursor after the
** word.
*/
static char *
find_word()
{
static char SEPS[] = "\"#;&|^$=`'{}()<>\n\t ";
char *p;
char *new;
size_t len;
/* Move forward to end of word. */
p = &Line[Point];
for ( ; Point < End && strchr(SEPS, (char)*p) == NULL; Point++, p++)
right(CSstay);
/* Back up to beginning of word. */
for (p = &Line[Point]; p > Line && strchr(SEPS, (char)p[-1]) == NULL; p--)
continue;
len = Point - (p - Line) + 1;
if ((new = malloc(sizeof(char) * len)) == NULL)
return NULL;
memcpy(new, p, len);
new[len - 1] = '\0';
return new;
}
static STATUS
c_complete()
{
char *p;
char *word;
int unique;
word = find_word();
p = (char *)rl_complete((char *)word, &unique);
if (word)
free(word);
if (p && *p) {
(void)insert_string(p);
if (!unique)
(void)ring_bell();
free(p);
return redisplay_no_nl();
}
return ring_bell();
}
static STATUS
c_possible()
{
char **av;
char *word;
int ac;
word = find_word();
ac = rl_list_possib((char *)word, (char ***)&av);
if (word)
free(word);
if (ac) {
columns(ac, av);
while (--ac >= 0)
free(av[ac]);
free(av);
return redisplay_no_nl();
}
return ring_bell();
}
static STATUS
accept_line()
{
Line[End] = '\0';
return CSdone;
}
static STATUS
transpose()
{
char c;
if (Point) {
if (Point == End)
left(CSmove);
c = Line[Point - 1];
left(CSstay);
Line[Point - 1] = Line[Point];
TTYshow(Line[Point - 1]);
Line[Point++] = c;
TTYshow(c);
}
return CSstay;
}
static STATUS
quote()
{
int c;
return (c = TTYget()) == EOF ? CSeof : insert_char((int)c);
}
static STATUS
wipe()
{
int i;
if (Mark > End)
return ring_bell();
if (Point > Mark) {
i = Point;
Point = Mark;
Mark = i;
reposition();
}
return delete_string(Mark - Point);
}
static STATUS
mk_set()
{
Mark = Point;
return CSstay;
}
static STATUS
exchange()
{
int c;
if ((c = TTYget()) != CTL('X'))
return c == EOF ? CSeof : ring_bell();
if ((c = Mark) <= End) {
Mark = Point;
Point = c;
return CSmove;
}
return CSstay;
}
static STATUS
yank()
{
if (Yanked && *Yanked)
return insert_string(Yanked);
return CSstay;
}
static STATUS
copy_region()
{
if (Mark > End)
return ring_bell();
if (Point > Mark)
save_yank(Mark, Point - Mark);
else
save_yank(Point, Mark - Point);
return CSstay;
}
static STATUS
move_to_char()
{
int c;
int i;
char *p;
if ((c = TTYget()) == EOF)
return CSeof;
for (i = Point + 1, p = &Line[i]; i < End; i++, p++)
if (*p == c) {
Point = i;
return CSmove;
}
return CSstay;
}
static STATUS
fd_word()
{
return do_forward(CSmove);
}
static STATUS
fd_kill_word()
{
int i;
(void)do_forward(CSstay);
if (OldPoint != Point) {
i = Point - OldPoint;
Point = OldPoint;
return delete_string(i);
}
return CSstay;
}
static STATUS
bk_word()
{
int i;
char *p;
i = 0;
do {
for (p = &Line[Point]; p > Line && !isalnum((int)p[-1]); p--)
left(CSmove);
for (; p > Line && p[-1] != ' ' && isalnum((int)p[-1]); p--)
left(CSmove);
if (Point == 0)
break;
} while (++i < Repeat);
return CSstay;
}
static STATUS
bk_kill_word()
{
(void)bk_word();
if (OldPoint != Point)
return delete_string(OldPoint - Point);
return CSstay;
}
static int
argify(char *line, char ***avp)
{
char *c;
char **p;
char **new;
int ac;
int i;
i = MEM_INC;
if ((*avp = p = malloc(sizeof(char*) * i))== NULL)
return 0;
for (c = line; isspace((int)*c); c++)
continue;
if (*c == '\n' || *c == '\0')
return 0;
for (ac = 0, p[ac++] = c; *c && *c != '\n'; ) {
if (isspace((int)*c)) {
*c++ = '\0';
if (*c && *c != '\n') {
if (ac + 1 == i) {
new = malloc(sizeof(char*) * (i + MEM_INC));
if (new == NULL) {
p[ac] = NULL;
return ac;
}
memcpy(new, p, i * sizeof (char **));
i += MEM_INC;
free(p);
*avp = p = new;
}
p[ac++] = c;
}
}
else
c++;
}
*c = '\0';
p[ac] = NULL;
return ac;
}
static STATUS
last_argument()
{
char **av;
char *p;
STATUS s;
int ac;
if (H.Size == 1 || (p = H.Lines[H.Size - 2]) == NULL)
return ring_bell();
if ((p = strdup(p)) == NULL)
return CSstay;
ac = argify(p, &av);
if (Repeat != NO_ARG)
s = Repeat < ac ? insert_string(av[Repeat]) : ring_bell();
else
s = ac ? insert_string(av[ac - 1]) : CSstay;
if (ac)
free(av);
free(p);
return s;
}
static KEYMAP Map[MAPSIZE] = {
{ CTL('@'), 1, ring_bell },
{ CTL('A'), 1, beg_line },
{ CTL('B'), 1, bk_char },
{ CTL('D'), 1, del_char },
{ CTL('E'), 1, end_line },
{ CTL('F'), 1, fd_char },
{ CTL('G'), 1, ring_bell },
{ CTL('H'), 1, bk_del_char },
{ CTL('I'), 1, c_complete },
{ CTL('J'), 1, accept_line },
{ CTL('K'), 1, kill_line },
{ CTL('L'), 1, redisplay },
{ CTL('M'), 1, accept_line },
{ CTL('N'), 1, h_next },
{ CTL('O'), 1, ring_bell },
{ CTL('P'), 1, h_prev },
{ CTL('Q'), 1, ring_bell },
{ CTL('R'), 1, h_search },
{ CTL('S'), 1, ring_bell },
{ CTL('T'), 1, transpose },
{ CTL('U'), 1, ring_bell },
{ CTL('V'), 1, quote },
{ CTL('W'), 1, wipe },
{ CTL('X'), 1, exchange },
{ CTL('Y'), 1, yank },
{ CTL('Z'), 1, ring_bell },
{ CTL('['), 1, meta },
{ CTL(']'), 1, move_to_char },
{ CTL('^'), 1, ring_bell },
{ CTL('_'), 1, ring_bell },
};
static KEYMAP MetaMap[16]= {
{ CTL('H'), 1, bk_kill_word },
{ CTL('['), 1, c_possible },
{ DEL, 1, bk_kill_word },
{ ' ', 1, mk_set },
{ '.', 1, last_argument },
{ '<', 1, h_first },
{ '>', 1, h_last },
{ '?', 1, c_possible },
{ 'b', 1, bk_word },
{ 'd', 1, fd_kill_word },
{ 'f', 1, fd_word },
{ 'l', 1, case_down_word },
{ 'm', 1, toggle_meta_mode},
{ 'u', 1, case_up_word },
{ 'y', 1, yank },
{ 'w', 1, copy_region },
};
/* $Revision: 1.2 $
**
** Internal header file for editline library.
*/
#include <ndb_global.h>
#if defined(SYS_UNIX)
#include "unix.h"
#endif /* defined(SYS_UNIX) */
#define MEM_INC 64
#define SCREEN_INC 256
/*
** Variables and routines internal to this package.
*/
extern int rl_eof;
extern int rl_erase;
extern int rl_intr;
extern int rl_kill;
extern int rl_quit;
#if defined(DO_SIGTSTP)
extern int rl_susp;
#endif /* defined(DO_SIGTSTP) */
extern char *rl_complete();
extern int rl_list_possib();
extern void rl_ttyset();
extern void rl_add_slash();
/* $Revision: 1.4 $
**
** Unix system-dependant routines for editline library.
*/
#include "editline_internal.h"
#if defined(HAVE_TCGETATTR)
#include <termios.h>
void
rl_ttyset(int Reset)
{
static struct termios old;
struct termios new;
if (Reset == 0) {
if (tcgetattr(0, &old) < 0) perror("tcgetattr");
rl_erase = old.c_cc[VERASE];
rl_kill = old.c_cc[VKILL];
rl_eof = old.c_cc[VEOF];
rl_intr = old.c_cc[VINTR];
rl_quit = old.c_cc[VQUIT];
#if defined(DO_SIGTSTP)
rl_susp = old.c_cc[VSUSP];
#endif /* defined(DO_SIGTSTP) */
new = old;
new.c_lflag &= ~(ECHO | ICANON | ISIG);
new.c_iflag &= ~(ISTRIP | INPCK);
new.c_cc[VMIN] = 1;
new.c_cc[VTIME] = 0;
if (tcsetattr(0, TCSADRAIN, &new) < 0) perror("tcsetattr");
}
else
(void)tcsetattr(0, TCSADRAIN, &old);
}
#else
#if defined(HAVE_TERMIO)
#include <termio.h>
void
rl_ttyset(int Reset)
{
static struct termio old;
struct termio new;
if (Reset == 0) {
(void)ioctl(0, TCGETA, &old);
rl_erase = old.c_cc[VERASE];
rl_kill = old.c_cc[VKILL];
rl_eof = old.c_cc[VEOF];
rl_intr = old.c_cc[VINTR];
rl_quit = old.c_cc[VQUIT];
#if defined(DO_SIGTSTP)
rl_susp = old.c_cc[VSUSP];
#endif /* defined(DO_SIGTSTP) */
new = old;
new.c_lflag &= ~(ECHO | ICANON | ISIG);
new.c_iflag &= ~(ISTRIP | INPCK);
new.c_cc[VMIN] = 1;
new.c_cc[VTIME] = 0;
(void)ioctl(0, TCSETAW, &new);
}
else
(void)ioctl(0, TCSETAW, &old);
}
#else
#include <sgtty.h>
void
rl_ttyset(int Reset)
{
static struct sgttyb old_sgttyb;
static struct tchars old_tchars;
struct sgttyb new_sgttyb;
struct tchars new_tchars;
#if defined(DO_SIGTSTP)
struct ltchars old_ltchars;
#endif /* defined(DO_SIGTSTP) */
if (Reset == 0) {
(void)ioctl(0, TIOCGETP, &old_sgttyb);
rl_erase = old_sgttyb.sg_erase;
rl_kill = old_sgttyb.sg_kill;
(void)ioctl(0, TIOCGETC, &old_tchars);
rl_eof = old_tchars.t_eofc;
rl_intr = old_tchars.t_intrc;
rl_quit = old_tchars.t_quitc;
#if defined(DO_SIGTSTP)
(void)ioctl(0, TIOCGLTC, &old_ltchars);
rl_susp = old_ltchars.t_suspc;
#endif /* defined(DO_SIGTSTP) */
new_sgttyb = old_sgttyb;
new_sgttyb.sg_flags &= ~ECHO;
new_sgttyb.sg_flags |= RAW;
#if defined(PASS8)
new_sgttyb.sg_flags |= PASS8;
#endif /* defined(PASS8) */
(void)ioctl(0, TIOCSETP, &new_sgttyb);
new_tchars = old_tchars;
new_tchars.t_intrc = -1;
new_tchars.t_quitc = -1;
(void)ioctl(0, TIOCSETC, &new_tchars);
}
else {
(void)ioctl(0, TIOCSETP, &old_sgttyb);
(void)ioctl(0, TIOCSETC, &old_tchars);
}
}
#endif /* defined(HAVE_TERMIO) */
#endif /* defined(HAVE_TCGETATTR) */
void
rl_add_slash(char *path, char *p, size_t p_len)
{
struct stat Sb;
if (stat(path, &Sb) >= 0) {
size_t len= strlen(p);
if (len+1 < p_len) {
p[len]= S_ISDIR(Sb.st_mode) ? '/' : ' ';
p[len+1]= 0;
}
}
}
include .defs.mk
TYPE := util
BIN_TARGET := editline_test
BIN_TARGET_ARCHIVES := editline
SOURCES = testit.c
include $(NDB_TOP)/Epilogue.mk
/* Copyright (C) 2003 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/* -*- c-basic-offset: 4; -*-
** $Revision: 1.5 $
**
** A "micro-shell" to test editline library.
** If given any arguments, commands aren't executed.
*/
#include <ndb_global.h>
#include <editline/editline.h>
int
main(int argc, char **argv)
{
char *prompt;
char *p;
int doit;
(void)argv; /* Suppress warning */
doit = argc == 1;
if ((prompt = getenv("TESTPROMPT")) == NULL)
prompt = "testit> ";
while ((p = readline(prompt)) != NULL) {
(void)printf("\t\t\t|%s|\n", p);
if (doit) {
if (strncmp(p, "cd ", 3) == 0) {
if (chdir(&p[3]) < 0)
perror(&p[3]);
} else {
if (system(p) != 0)
perror(p);
}
}
add_history(p);
free(p);
}
return 0;
}
/* $Revision: 1.3 $
**
** Editline system header file for Unix.
*/
#define CRLF "\r\n"
#include <ndb_global.h>
#include <dirent.h>
......@@ -17,7 +17,195 @@
#include <ndb_global.h>
#include <my_sys.h>
#include "CommandInterpreter.hpp"
// copied from mysql.cc to get readline
extern "C" {
#if defined( __WIN__) || defined(OS2)
#include <conio.h>
#elif !defined(__NETWARE__)
#include <readline/readline.h>
extern "C" int add_history(const char *command); /* From readline directory */
#define HAVE_READLINE
#endif
}
//#define HAVE_GLOBAL_REPLICATION
#include <Vector.hpp>
#ifdef HAVE_GLOBAL_REPLICATION
#include "../rep/repapi/repapi.h"
#endif
#include <mgmapi.h>
class MgmtSrvr;
/**
* @class CommandInterpreter
* @brief Reads command line in management client
*
* This class has one public method which reads a command line
* from a stream. It then interpret that commmand line and calls a suitable
* method in the MgmtSrvr class which executes the command.
*
* For command syntax, see the HELP command.
*/
class CommandInterpreter {
public:
/**
* Constructor
* @param mgmtSrvr: Management server to use when executing commands
*/
CommandInterpreter(const char *);
~CommandInterpreter();
/**
* Reads one line from the stream, parse the line to find
* a command and then calls a suitable method which executes
* the command.
*
* @return true until quit/bye/exit has been typed
*/
int readAndExecute(int _try_reconnect=-1);
int execute(const char *_line, int _try_reconnect=-1);
private:
void printError();
/**
* Analyse the command line, after the first token.
*
* @param processId: DB process id to send command to or -1 if
* command will be sent to all DB processes.
* @param allAfterFirstToken: What the client gave after the
* first token on the command line
*/
void analyseAfterFirstToken(int processId, char* allAfterFirstTokenCstr);
/**
* Parse the block specification part of the LOG* commands,
* things after LOG*: [BLOCK = {ALL|<blockName>+}]
*
* @param allAfterLog: What the client gave after the second token
* (LOG*) on the command line
* @param blocks, OUT: ALL or name of all the blocks
* @return: true if correct syntax, otherwise false
*/
bool parseBlockSpecification(const char* allAfterLog,
Vector<const char*>& blocks);
/**
* A bunch of execute functions: Executes one of the commands
*
* @param processId: DB process id to send command to
* @param parameters: What the client gave after the command name
* on the command line.
* For example if complete input from user is: "1 LOGLEVEL 22" then the
* parameters argument is the string with everything after LOGLEVEL, in
* this case "22". Each function is responsible to check the parameters
* argument.
*/
void executeHelp(char* parameters);
void executeShow(char* parameters);
void executeShutdown(char* parameters);
void executeRun(char* parameters);
void executeInfo(char* parameters);
void executeClusterLog(char* parameters);
public:
void executeStop(int processId, const char* parameters, bool all);
void executeEnterSingleUser(char* parameters);
void executeExitSingleUser(char* parameters);
void executeStart(int processId, const char* parameters, bool all);
void executeRestart(int processId, const char* parameters, bool all);
void executeLogLevel(int processId, const char* parameters, bool all);
void executeError(int processId, const char* parameters, bool all);
void executeTrace(int processId, const char* parameters, bool all);
void executeLog(int processId, const char* parameters, bool all);
void executeLogIn(int processId, const char* parameters, bool all);
void executeLogOut(int processId, const char* parameters, bool all);
void executeLogOff(int processId, const char* parameters, bool all);
void executeTestOn(int processId, const char* parameters, bool all);
void executeTestOff(int processId, const char* parameters, bool all);
void executeSet(int processId, const char* parameters, bool all);
void executeGetStat(int processId, const char* parameters, bool all);
void executeStatus(int processId, const char* parameters, bool all);
void executeEventReporting(int processId, const char* parameters, bool all);
void executeDumpState(int processId, const char* parameters, bool all);
void executeStartBackup(char * parameters);
void executeAbortBackup(char * parameters);
void executeRep(char* parameters);
void executeCpc(char * parameters);
public:
bool connect();
bool disconnect();
/**
* A execute function definition
*/
public:
typedef void (CommandInterpreter::* ExecuteFunction)(int processId,
const char * param,
bool all);
struct CommandFunctionPair {
const char * command;
ExecuteFunction executeFunction;
};
private:
/**
*
*/
void executeForAll(const char * cmd,
ExecuteFunction fun,
const char * param);
NdbMgmHandle m_mgmsrv;
bool connected;
const char *host;
int try_reconnect;
#ifdef HAVE_GLOBAL_REPLICATION
NdbRepHandle m_repserver;
const char *rep_host;
bool rep_connected;
#endif
};
/*
* Facade object for CommandInterpreter
*/
#include "ndb_mgmclient.hpp"
Ndb_mgmclient::Ndb_mgmclient(const char *host)
{
m_cmd= new CommandInterpreter(host);
}
Ndb_mgmclient::~Ndb_mgmclient()
{
delete m_cmd;
}
int Ndb_mgmclient::read_and_execute(int _try_reconnect)
{
return m_cmd->readAndExecute(_try_reconnect);
}
int Ndb_mgmclient::execute(const char *_line, int _try_reconnect)
{
return m_cmd->execute(_line,_try_reconnect);
}
int
Ndb_mgmclient::disconnect()
{
return m_cmd->disconnect();
}
/*
* The CommandInterpreter
*/
#include <mgmapi.h>
#include <mgmapi_debug.h>
......@@ -33,7 +221,10 @@
#endif // HAVE_GLOBAL_REPLICATION
#include "MgmtErrorReporter.hpp"
#include "CpcClient.hpp"
#include <Parser.hpp>
#include <SocketServer.hpp>
#include <util/InputStream.hpp>
#include <util/OutputStream.hpp>
/*****************************************************************************
......@@ -201,7 +392,7 @@ CommandInterpreter::~CommandInterpreter()
host = NULL;
}
bool
static bool
emptyString(const char* s)
{
if (s == NULL) {
......@@ -265,17 +456,47 @@ CommandInterpreter::disconnect()
int
CommandInterpreter::readAndExecute(int _try_reconnect)
{
static char *line_read = (char *)NULL;
/* If the buffer has already been allocated, return the memory
to the free pool. */
if (line_read)
{
free (line_read);
line_read = (char *)NULL;
}
#ifdef HAVE_READLINE
/* Get a line from the user. */
line_read = readline ("ndb_mgm> ");
/* If the line has any text in it, save it on the history. */
if (line_read && *line_read)
add_history (line_read);
#else
static char linebuffer[254];
fputs("ndb_mgm> ", stdout);
linebuffer[sizeof(linebuffer)-1]=0;
line_read = fgets(linebuffer, sizeof(linebuffer)-1, stdin);
if (line_read == linebuffer) {
char *q=linebuffer;
while (*q > 31) q++;
*q=0;
line_read= strdup(linebuffer);
}
#endif
return execute(line_read,_try_reconnect);
}
int
CommandInterpreter::execute(const char *_line, int _try_reconnect)
{
if (_try_reconnect >= 0)
try_reconnect=_try_reconnect;
char* _line = readline_gets();
char * line;
if(_line == NULL) {
// ndbout << endl;
return false;
}
line = my_strdup(_line,MYF(MY_WME));
My_auto_ptr<char> ptr(line);
......@@ -349,10 +570,6 @@ CommandInterpreter::readAndExecute(int _try_reconnect)
strcmp(firstToken, "BYE") == 0) &&
allAfterFirstToken == NULL){
return false;
#if 0
} else if(strcmp(firstToken, "CPC") == 0) {
executeCpc(allAfterFirstToken);
#endif
} else {
/**
* First token should be a digit, node ID
......@@ -1903,169 +2120,4 @@ CommandInterpreter::executeRep(char* parameters)
}
#endif // HAVE_GLOBAL_REPLICATION
/*****************************************************************************
* CPC
*****************************************************************************/
#if 0
#if 0
//#ifdef NDB_SOLARIS // XXX fix me
static char* strsep(char** x, const char* y) { return 0; }
#endif
// Note this code has not been verified
#if 0
static char * my_strsep(char **stringp, const char *delim)
{
char *tmp= *stringp;
if (tmp == 0)
return 0;
*stringp = strtok(tmp, delim);
return tmp;
}
#endif
void
CommandInterpreter::executeCpc(char *parameters)
{
char *host_str = NULL, *port_str = NULL, *end;
long port = 1234; /* XXX */
while((host_str = my_strsep(&parameters, " \t:")) != NULL &&
host_str[0] == '\0');
if(parameters && parameters[0] != '\0') {
while((port_str = my_strsep(&parameters, " \t:")) != NULL &&
port_str[0] == '\0');
errno = 0;
port = strtol(port_str, &end, 0);
if(end[0] != '\0')
goto error;
if((port == LONG_MAX || port == LONG_MIN) &&
errno == ERANGE)
goto error;
}
{
SimpleCpcClient cpc(host_str, port);
bool done = false;
if(cpc.connect() < 0) {
ndbout_c("Cannot connect to %s:%d.", cpc.getHost(), cpc.getPort());
switch(errno) {
case ENOENT:
ndbout << ": " << "No such host" << endl;
break;
default:
ndbout << ": " << strerror(errno) << endl;
break;
}
return;
}
while(!done) {
char *line = readline("CPC> ");
if(line != NULL) {
add_history(line);
char *cmd = strtok(line, " ");
char *arg = strtok(NULL, "");
if(arg != NULL) {
while(arg[0] == ' ')
arg++;
if(strlen(arg) == 0)
arg = NULL;
}
if(cmd != NULL) {
if(strcmp(cmd, "exit") == 0)
done = true;
else if(strcmp(cmd, "list") == 0)
cpc.cmd_list(arg);
else if(strcmp(cmd, "start") == 0)
cpc.cmd_start(arg);
else if(strcmp(cmd, "stop") == 0)
cpc.cmd_stop(arg);
else if(strcmp(cmd, "help") == 0)
cpc.cmd_help(arg);
}
} else {
done = true;
ndbout << endl;
}
}
}
return;
error:
ndbout << "Error: expected a tcp port number, got '" << port_str << "'."
<< endl;
return;
}
#endif
#if 0
static
void
CmdBackupCallback(const MgmtSrvr::BackupEvent & event){
char str[255];
ndbout << endl;
bool ok = false;
switch(event.Event){
case MgmtSrvr::BackupEvent::BackupStarted:
ok = true;
BaseString::snprintf(str, sizeof(str),
"Backup %d started", event.Started.BackupId);
break;
case MgmtSrvr::BackupEvent::BackupFailedToStart:
ok = true;
BaseString::snprintf(str, sizeof(str),
"Backup failed to start (Error %d)",
event.FailedToStart.ErrorCode);
break;
case MgmtSrvr::BackupEvent::BackupCompleted:
ok = true;
BaseString::snprintf(str, sizeof(str),
"Backup %d completed",
event.Completed.BackupId);
ndbout << str << endl;
BaseString::snprintf(str, sizeof(str),
" StartGCP: %d StopGCP: %d",
event.Completed.startGCP, event.Completed.stopGCP);
ndbout << str << endl;
BaseString::snprintf(str, sizeof(str),
" #Records: %d #LogRecords: %d",
event.Completed.NoOfRecords, event.Completed.NoOfLogRecords);
ndbout << str << endl;
BaseString::snprintf(str, sizeof(str),
" Data: %d bytes Log: %d bytes",
event.Completed.NoOfBytes, event.Completed.NoOfLogBytes);
break;
case MgmtSrvr::BackupEvent::BackupAborted:
ok = true;
BaseString::snprintf(str, sizeof(str),
"Backup %d has been aborted reason %d",
event.Aborted.BackupId,
event.Aborted.Reason);
break;
}
if(!ok){
BaseString::snprintf(str, sizeof(str),
"Unknown backup event: %d",
event.Event);
}
ndbout << str << endl;
}
#endif
template class Vector<char const*>;
/* Copyright (C) 2003 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#ifndef CommandInterpreter_H
#define CommandInterpreter_H
//#define HAVE_GLOBAL_REPLICATION
//*****************************************************************************
// Author: Peter Lind
//*****************************************************************************
#include <ndb_global.h>
#include <Vector.hpp>
#include <editline/editline.h>
#ifdef HAVE_GLOBAL_REPLICATION
#include "../rep/repapi/repapi.h"
#endif
#include <mgmapi.h>
class MgmtSrvr;
/**
* @class CommandInterpreter
* @brief Reads command line in management client
*
* This class has one public method which reads a command line
* from a stream. It then interpret that commmand line and calls a suitable
* method in the MgmtSrvr class which executes the command.
*
* For command syntax, see the HELP command.
*/
class CommandInterpreter {
public:
/**
* Constructor
* @param mgmtSrvr: Management server to use when executing commands
*/
CommandInterpreter(const char *);
~CommandInterpreter();
/**
* Reads one line from the stream, parse the line to find
* a command and then calls a suitable method which executes
* the command.
*
* @return true until quit/bye/exit has been typed
*/
int readAndExecute(int _try_reconnect=-1);
private:
/**
* Read a string, and return a pointer to it.
*
* @return NULL on EOF.
*/
char *readline_gets ()
{
static char *line_read = (char *)NULL;
/* If the buffer has already been allocated, return the memory
to the free pool. */
if (line_read)
{
free (line_read);
line_read = (char *)NULL;
}
/* Get a line from the user. */
line_read = readline ("NDB> ");
/* If the line has any text in it, save it on the history. */
if (line_read && *line_read)
add_history (line_read);
return (line_read);
}
void printError();
/**
* Analyse the command line, after the first token.
*
* @param processId: DB process id to send command to or -1 if
* command will be sent to all DB processes.
* @param allAfterFirstToken: What the client gave after the
* first token on the command line
*/
void analyseAfterFirstToken(int processId, char* allAfterFirstTokenCstr);
/**
* Parse the block specification part of the LOG* commands,
* things after LOG*: [BLOCK = {ALL|<blockName>+}]
*
* @param allAfterLog: What the client gave after the second token
* (LOG*) on the command line
* @param blocks, OUT: ALL or name of all the blocks
* @return: true if correct syntax, otherwise false
*/
bool parseBlockSpecification(const char* allAfterLog,
Vector<const char*>& blocks);
/**
* A bunch of execute functions: Executes one of the commands
*
* @param processId: DB process id to send command to
* @param parameters: What the client gave after the command name
* on the command line.
* For example if complete input from user is: "1 LOGLEVEL 22" then the
* parameters argument is the string with everything after LOGLEVEL, in
* this case "22". Each function is responsible to check the parameters
* argument.
*/
void executeHelp(char* parameters);
void executeShow(char* parameters);
void executeShutdown(char* parameters);
void executeRun(char* parameters);
void executeInfo(char* parameters);
void executeClusterLog(char* parameters);
public:
void executeStop(int processId, const char* parameters, bool all);
void executeEnterSingleUser(char* parameters);
void executeExitSingleUser(char* parameters);
void executeStart(int processId, const char* parameters, bool all);
void executeRestart(int processId, const char* parameters, bool all);
void executeLogLevel(int processId, const char* parameters, bool all);
void executeError(int processId, const char* parameters, bool all);
void executeTrace(int processId, const char* parameters, bool all);
void executeLog(int processId, const char* parameters, bool all);
void executeLogIn(int processId, const char* parameters, bool all);
void executeLogOut(int processId, const char* parameters, bool all);
void executeLogOff(int processId, const char* parameters, bool all);
void executeTestOn(int processId, const char* parameters, bool all);
void executeTestOff(int processId, const char* parameters, bool all);
void executeSet(int processId, const char* parameters, bool all);
void executeGetStat(int processId, const char* parameters, bool all);
void executeStatus(int processId, const char* parameters, bool all);
void executeEventReporting(int processId, const char* parameters, bool all);
void executeDumpState(int processId, const char* parameters, bool all);
void executeStartBackup(char * parameters);
void executeAbortBackup(char * parameters);
void executeRep(char* parameters);
void executeCpc(char * parameters);
public:
bool connect();
bool disconnect();
/**
* A execute function definition
*/
public:
typedef void (CommandInterpreter::* ExecuteFunction)(int processId,
const char * param,
bool all);
struct CommandFunctionPair {
const char * command;
ExecuteFunction executeFunction;
};
private:
/**
*
*/
void executeForAll(const char * cmd,
ExecuteFunction fun,
const char * param);
NdbMgmHandle m_mgmsrv;
bool connected;
const char *host;
int try_reconnect;
#ifdef HAVE_GLOBAL_REPLICATION
NdbRepHandle m_repserver;
const char *rep_host;
bool rep_connected;
#endif
};
#endif // CommandInterpreter_H
ndbtools_PROGRAMS = ndb_mgm
noinst_LTLIBRARIES = libndbmgmclient.la
ndb_mgm_SOURCES = \
main.cpp \
CommandInterpreter.cpp \
CpcClient.cpp
libndbmgmclient_la_SOURCES = CommandInterpreter.cpp
ndb_mgm_SOURCES = main.cpp
include $(top_srcdir)/ndb/config/common.mk.am
include $(top_srcdir)/ndb/config/type_ndbapi.mk.am
INCLUDES += -I$(top_srcdir)/ndb/include/mgmapi -I$(top_srcdir)/ndb/src/common/mgmcommon
LDADD_LOC = $(top_builddir)/ndb/src/libndbclient.la \
$(top_builddir)/ndb/src/common/editline/libeditline.a \
LDADD_LOC = $(top_builddir)/ndb/src/mgmclient/libndbmgmclient.la \
@readline_link@ \
$(top_builddir)/ndb/src/libndbclient.la \
$(top_builddir)/dbug/libdbug.a \
$(top_builddir)/mysys/libmysys.a \
$(top_builddir)/strings/libmystrings.a \
......
include .defs.mk
TYPE := ndbapi
BIN_TARGET := mgmtclient
BIN_TARGET_LIBS :=
BIN_TARGET_ARCHIVES := trace logger mgmapi general mgmsrvcommon portlib repapi
BIN_TARGET_ARCHIVES += editline
DIRS = test_cpcd
# Source files of non-templated classes (.cpp files)
SOURCES = \
main.cpp \
CommandInterpreter.cpp \
CpcClient.cpp
CCFLAGS_LOC += -I$(call fixpath,$(NDB_TOP)/include/mgmapi) \
-I$(call fixpath,$(NDB_TOP)/src/common/mgmcommon)
include $(NDB_TOP)/Epilogue.mk
_bins_mkconfig : $(NDB_TOP)/bin/$(BIN_TARGET)
......@@ -23,12 +23,12 @@
#include <ndb_version.h>
#include <LocalConfig.hpp>
#include "CommandInterpreter.hpp"
#include "ndb_mgmclient.hpp"
const char *progname = "ndb_mgm";
static CommandInterpreter* com;
static Ndb_mgmclient* com;
extern "C"
void
......@@ -127,8 +127,8 @@ int main(int argc, char** argv){
signal(SIGPIPE, handler);
com = new CommandInterpreter(buf);
while(com->readAndExecute(_try_reconnect));
com = new Ndb_mgmclient(buf);
while(com->read_and_execute(_try_reconnect));
delete com;
return 0;
......
......@@ -14,19 +14,20 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#ifndef Ndb_mgmclient_h
#define Ndb_mgmclient_h
#include <ndb_global.h>
char* readline(const char* prompt)
class CommandInterpreter;
class Ndb_mgmclient
{
char* szBuf;
printf(prompt);
szBuf = (char*)malloc(256);
return gets(szBuf);
}
void add_history(char* pch)
{
}
public:
Ndb_mgmclient(const char*);
~Ndb_mgmclient();
int read_and_execute(int _try_reconnect=-1);
int execute(const char *_line, int _try_reconnect=-1);
int disconnect();
private:
CommandInterpreter *m_cmd;
};
#endif // Ndb_mgmclient_h
......@@ -23,7 +23,6 @@
#include <ndb_global.h>
#include <Vector.hpp>
#include <editline/editline.h>
#include <BaseString.hpp>
class MgmtSrvr;
......@@ -63,26 +62,27 @@ private:
*/
char *readline_gets ()
{
static char linebuffer[254];
static char *line_read = (char *)NULL;
// Disable the default file-name completion action of TAB
// rl_bind_key ('\t', rl_insert);
/* If the buffer has already been allocated, return the memory
to the free pool. */
if (line_read)
{
free (line_read);
line_read = (char *)NULL;
}
{
free (line_read);
line_read = (char *)NULL;
}
/* Get a line from the user. */
line_read = readline ("NDB> ");
/* If the line has any text in it, save it on the history. */
if (line_read && *line_read)
add_history (line_read);
fputs("ndb_mgmd> ", stdout);
linebuffer[sizeof(linebuffer)-1]=0;
line_read = fgets(linebuffer, sizeof(linebuffer)-1, stdin);
if (line_read == linebuffer) {
char *q=linebuffer;
while (*q > 31) q++;
*q=0;
line_read= strdup(linebuffer);
}
return (line_read);
}
......
......@@ -24,7 +24,6 @@ INCLUDES_LOC = -I$(top_srcdir)/ndb/src/ndbapi \
-I$(top_srcdir)/ndb/src/common/mgmcommon
LDADD_LOC = $(top_builddir)/ndb/src/libndbclient.la \
$(top_builddir)/ndb/src/common/editline/libeditline.a \
$(top_builddir)/dbug/libdbug.a \
$(top_builddir)/mysys/libmysys.a \
$(top_builddir)/strings/libmystrings.a @NDB_SCI_LIBS@
......
......@@ -11,9 +11,8 @@ test_SCRIPTS=atrt-analyze-result.sh atrt-gather-result.sh atrt-setup.sh \
atrt-clear-result.sh make-config.sh make-index.sh make-html-reports.sh
atrt_SOURCES = main.cpp
INCLUDES_LOC = -I$(top_srcdir)/ndb/test/include -I$(top_srcdir)/ndb/src/mgmclient
LDADD_LOC = $(top_builddir)/ndb/src/mgmclient/CpcClient.o \
$(top_builddir)/ndb/test/src/libNDBT.a \
INCLUDES_LOC = -I$(top_srcdir)/ndb/test/include
LDADD_LOC = $(top_builddir)/ndb/test/src/libNDBT.a \
$(top_builddir)/ndb/src/libndbclient.la \
$(top_builddir)/dbug/libdbug.a \
$(top_builddir)/mysys/libmysys.a \
......
......@@ -9,7 +9,8 @@ libNDBT_a_SOURCES = \
HugoAsynchTransactions.cpp UtilTransactions.cpp \
NdbRestarter.cpp NdbRestarts.cpp NDBT_Output.cpp \
NdbBackup.cpp NdbConfig.cpp NdbGrep.cpp NDBT_Table.cpp \
NdbSchemaCon.cpp NdbSchemaOp.cpp getarg.c
NdbSchemaCon.cpp NdbSchemaOp.cpp getarg.c \
CpcClient.cpp
INCLUDES_LOC = -I$(top_srcdir)/ndb/src/common/mgmcommon -I$(top_srcdir)/ndb/include/mgmcommon -I$(top_srcdir)/ndb/include/kernel -I$(top_srcdir)/ndb/src/mgmapi
......
......@@ -20,12 +20,10 @@ copy_tab_SOURCES = copy_tab.cpp
create_index_SOURCES = create_index.cpp
ndb_cpcc_SOURCES = cpcc.cpp
INCLUDES_LOC = -I$(top_srcdir)/ndb/src/mgmclient
include $(top_srcdir)/ndb/config/common.mk.am
include $(top_srcdir)/ndb/config/type_ndbapitest.mk.am
ndb_cpcc_LDADD = $(LDADD) $(top_builddir)/ndb/src/mgmclient/CpcClient.o
ndb_cpcc_LDADD = $(LDADD)
# Don't update the files from bitkeeper
%::SCCS/s.%
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment