emacs.c revision 3f54fd611f536639ec30dd53c48e5ec1897cc7d9
/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1982-2012 AT&T Intellectual Property *
* and is licensed under the *
* Eclipse Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* David Korn <dgk@research.att.com> *
* *
***********************************************************************/
#pragma prototyped
/* Original version by Michael T. Veach
* Adapted for ksh by David Korn */
/* EMACS_MODES: c tabstop=4
One line screen editor for any program
*/
/* The following is provided by:
*
* Matthijs N. Melchior
* AT&T Network Systems International
* APT Nederland
* HV BZ335 x2962
* hvlpb!mmelchio
*
* These are now on by default
*
* ESH_NFIRST
* - A ^N as first history related command after the prompt will move
* to the next command relative to the last known history position.
* It will not start at the position where the last command was entered
* as is done by the ^P command. Every history related command will
* set both the current and last position. Executing a command will
* only set the current position.
*
* ESH_KAPPEND
* - Successive kill and delete commands will accumulate their data
* in the kill buffer, by appending or prepending as appropriate.
* This mode will be reset by any command not adding something to the
* kill buffer.
*
* ESH_BETTER
* - Some enhancements:
* - argument for a macro is passed to its replacement
* - ^X^H command to find out about history position (debugging)
* - ^X^D command to show any debugging info
*
* I do not pretend these for changes are completely independent,
* but you can use them to seperate features.
*/
#include <ast.h>
#if KSHELL
# include "defs.h"
#else
# include <ctype.h>
#endif /* KSHELL */
#include "io.h"
#include "history.h"
#include "edit.h"
#include "terminal.h"
#define ESH_NFIRST
#define ESH_KAPPEND
#define ESH_BETTER
#define beep() ed_ringbell()
#if SHOPT_MULTIBYTE
# define genncpy(a,b,n) ed_genncpy(a,b,n)
static int print(int);
static int _isword(int);
#else
#endif /*SHOPT_MULTIBYTE */
typedef struct _emacs_
{
int mark;
int in_mult;
char cr_ok;
char CntrlO;
char overflow; /* Screen overflow flag set */
char scvalid; /* Screen is up to date */
char lastdraw; /* last update type */
int offset; /* Screen offset */
enum
{
CRT=0, /* Crt terminal */
PAPER /* Paper terminal */
} terminal;
int prevdirection;
} Emacs_t;
#define LBUF 100
/**********************
A large lookahead helps when the user is inserting
characters in the middle of the line.
************************/
typedef enum
{
FIRST, /* First time thru for logical line, prompt on screen */
REFRESH, /* Redraw entire screen */
APPEND, /* Append char before cursor to screen */
UPDATE, /* Update the screen as need be */
FINAL /* Update screen even if pending look ahead */
} Draw_t;
{
register int c;
register int i;
register int count;
char backslash;
if(!ep)
{
}
{
}
raw = 1;
/* This mess in case the read system call fails */
#if SHOPT_MULTIBYTE
if(reedit)
#endif /* SHOPT_MULTIBYTE */
if(!kstack)
{
kstack[0] = '\0';
}
#ifdef ESH_NFIRST
{
}
{
}
#endif /* ESH_NFIRST */
if (i !=0)
{
{
}
if (i == UEOF)
{
return(0); /* EOF */
}
return(-1); /* some other error */
}
adjust = -1;
backslash = 0;
{
#ifdef ESH_NFIRST
#else
{
# if SHOPT_MULTIBYTE
# endif /* SHOPT_MULTIBYTE */
}
#endif /* ESH_NFIRST */
}
{
if (backslash)
{
backslash = 0;
(c!='\r'&&c!='\n')))
{
/* accept a backslashed character */
cur--;
continue;
}
}
if (c == usrkill)
{
c = KILLCHAR ;
}
else if (c == usrerase)
{
c = ERASECHAR ;
}
else if (c == usrlnext)
{
c = LNEXTCHAR ;
}
{
c = EOFCHAR;
}
#ifdef ESH_KAPPEND
if (--killing <= 0) /* reset killing flag */
killing = 0;
#endif
if(count<0)
count = 1;
adjust = -1;
i = cur;
switch(c)
{
case LNEXTCHAR:
goto do_default_processing;
case cntl('V'):
continue;
case '\0':
continue;
case cntl('X'):
continue;
case EOFCHAR:
return(0);
#ifdef u370
case cntl('S') :
case cntl('Q') :
continue;
#endif /* u370 */
case '\t':
{
{
goto do_escape;
}
{
goto do_escape;
}
}
default:
{
goto process;
}
backslash = (c == '\\');
continue;
case cntl('Y') :
{
{
beep();
continue;
}
while (i = *kptr++)
continue;
}
case '\n':
case '\r':
c = '\n';
goto process;
case DELETE: /* delete char 0x7f */
case '\b': /* backspace, ^h */
case ERASECHAR :
if (count > i)
count = i;
#ifdef ESH_KAPPEND
if (killing) /* prepend to killbuf */
{
while(c--) /* copy stuff */
}
else
*kptr = 0; /* this is end of data */
i -= count;
#else
while ((count--)&&(i>0))
{
i--;
eol--;
}
#endif /* ESH_KAPPEND */
goto update;
case cntl('W') :
#ifdef ESH_KAPPEND
++killing; /* keep killing flag */
#endif
continue;
{
continue;
}
continue;
case cntl('D') :
#ifdef ESH_KAPPEND
if (killing)
else
#else
#endif /* ESH_KAPPEND */
{
eol--;
while(1)
{
break;
i++;
}
i = cur;
}
*kptr = '\0';
goto update;
case cntl('C') :
case cntl('F') :
{
{
if (cntlC)
{
c = out[i];
#if SHOPT_MULTIBYTE
#else
if(islower(c))
#endif /* SHOPT_MULTIBYTE */
{
c += 'A' - 'a';
out[i] = c;
}
}
i++;
}
goto update;
}
case cntl(']') :
{
beep();
continue;
}
if (out[i])
i++;
while (i < eol)
{
goto update;
i++;
}
i = 0;
while (i < cur)
{
break;
i++;
};
cur = i;
continue;
case cntl('B') :
if (count > i)
count = i;
i -= count;
goto update;
case cntl('T') :
i++;
if (i >= 2)
{
c = out[i - 1];
out[i-2] = c;
}
else
{
if(sh_isoption(SH_EMACS))
i--;
beep();
continue;
}
goto update;
case cntl('A') :
i = 0;
goto update;
case cntl('E') :
i = eol;
goto update;
case cntl('U') :
continue;
case KILLCHAR :
cur = 0;
oadjust = -1;
case cntl('K') :
if(oadjust >= 0)
{
#ifdef ESH_KAPPEND
#endif
continue;
}
i = cur;
eol = i;
#ifdef ESH_KAPPEND
if (killing) /* append to kill buffer */
else
#else
#endif /* ESH_KAPPEND */
out[i] = 0;
if (c == KILLCHAR)
{
{
}
if (c != usrkill)
{
continue;
}
else
{
}
}
continue;
case cntl('L'):
continue;
case cntl('[') :
continue;
case cntl('R') :
goto drawline;
case cntl('P') :
#if SHOPT_EDPREDICT
{
{
beep();
continue;
}
goto hupdate;
}
#endif /* SHOPT_EDPREDICT */
else
{
hloff = 0;
}
#ifdef ESH_NFIRST
#else
#endif /* ESH_NFIRST */
{
beep();
#ifndef ESH_NFIRST
continue;
#endif
}
goto common;
case cntl('O') :
c = '\n';
goto process;
case cntl('N') :
#if SHOPT_EDPREDICT
{
{
beep();
continue;
}
continue;
}
#endif /* SHOPT_EDPREDICT */
#ifdef ESH_NFIRST
#endif /* ESH_NFIRST */
{
beep();
#ifdef ESH_NFIRST
#else
continue;
#endif /* ESH_NFIRST */
}
#ifdef ESH_NFIRST
#endif
cur = 0;
#if SHOPT_MULTIBYTE
#endif /* SHOPT_MULTIBYTE */
continue;
}
}
if (c == (-1))
{
lookahead = 0;
beep();
*out = '\0';
}
{
}
if(c == '\n')
{
}
#if SHOPT_MULTIBYTE
#endif /* SHOPT_MULTIBYTE */
if (i)
return(i);
return(-1);
}
{
register int c;
/* save current line */
*out = 0;
cur = 0;
#if SHOPT_MULTIBYTE
#else
#endif /* SHOPT_MULTIBYTE */
if(c!=' ')
/* restore line */
}
{
register int c;
while (c= *sp++)
}
{
register int i,value;
digit = 0;
value = 0;
{
value *= 10;
value += (i - '0');
digit = 1;
}
if (digit)
{
#ifdef ESH_KAPPEND
++killing; /* don't modify killing signal */
#endif
return(value);
}
if(value<0)
value = 1;
switch(ch=i)
{
case cntl('V'):
return(-1);
case ' ':
return(-1);
#ifdef ESH_KAPPEND
case '+': /* M-+ = append next kill */
killing = 2;
return -1; /* no argument for next command */
#endif
case 'p': /* M-p == ^W^Y (copy stack == kill & yank) */
#ifdef ESH_KAPPEND
killing = 0; /* start fresh */
#endif
return(-1);
case 'l': /* M-l == lower-case */
case 'd':
case 'c':
case 'f':
{
i = cur;
{
i++;
i++;
}
if(ch=='l')
{
while (value-- > 0)
{
#if SHOPT_MULTIBYTE
#else
if(isupper(i))
#endif /* SHOPT_MULTIBYTE */
{
i += 'a' - 'A';
}
cur++;
}
return(-1);
}
else if(ch=='f')
goto update;
else if(ch=='c')
{
return(i-cur);
}
else
{
if (i-cur)
{
#ifdef ESH_KAPPEND
++killing; /* keep killing signal */
#endif
return(i-cur);
}
beep();
return(-1);
}
}
case 'b':
case DELETE :
case '\b':
case 'h':
{
i = cur;
while(value-- && i>0)
{
i--;
while ((i>0)&&(!isword(i)))
i--;
while ((i>0)&&(isword(i-1)))
i--;
}
if(ch=='b')
goto update;
else
{
#ifdef ESH_KAPPEND
++killing;
#endif
return(cur-i);
}
}
case '>':
#ifdef ESH_NFIRST
{
}
else
{
}
#else
hloff = 0;
#endif /* ESH_NFIRST */
return(0);
case '<':
hloff = 0;
#ifdef ESH_NFIRST
return 0;
#else
#endif /* ESH_NFIRST */
case '#':
return(-1);
case '_' :
case '.' :
{
char *ptr;
if(ptr==0)
{
beep();
break;
}
{
beep();
return(-1);
}
while(*ptr)
{
eol++;
}
return(-1);
}
#if KSHELL
#if SHOPT_EDPREDICT
case '\n': case '\t':
{
beep();
break;
}
if(ch=='\n')
#endif /* SHOPT_EDPREDICT */
/* file name expansion */
#if SHOPT_EDPREDICT
{
beep();
else
{
return(value);
}
}
#endif /* SHOPT_EDPREDICT */
i = '\\';
case '*': /* filename expansion */
case '=': /* escape = - list all matching file names */
{
{
return(-1);
}
beep();
}
{
if(count>0)
else
{
if(isdigit(i))
}
}
else
{
}
return(-1);
/* search back for character */
{
{
beep();
return(-1);
}
i = cur;
if (i > 0)
i--;
while (i >= 0)
{
goto update;
i--;
}
i = eol;
while (i > cur)
{
break;
i--;
};
}
cur = i;
return(-1);
#ifdef _cmd_tput
sh_trap("tput clear", 0);
return(-1);
#endif
case '[': /* feature not in book */
{
case 'A':
#if SHOPT_EDPREDICT
#else
#endif /* SHOPT_EDPREDICT */
{
{
#if SHOPT_MULTIBYTE
#endif /* SHOPT_MULTIBYTE */
*lstring = '^';
}
if(*lstring)
{
return(-1);
}
}
*lstring = 0;
return(-1);
case 'B':
return(-1);
case 'C':
return(-1);
case 'D':
return(-1);
case 'H':
return(-1);
case 'Y':
return(-1);
default:
}
i = '_';
default:
/* look for user defined macro definitions */
# ifdef ESH_BETTER
return(count); /* pass argument to macro */
# else
return(-1);
# endif /* ESH_BETTER */
#else
cur = i;
return(-1);
default:
#endif /* KSHELL */
beep();
return(-1);
}
return(-1);
}
/*
* This routine process all commands starting with ^X
*/
{
switch(i)
{
cur = i;
return;
#if KSHELL
# ifdef ESH_BETTER
beep();
else
{
#if SHOPT_MULTIBYTE
#endif /* SHOPT_MULTIBYTE */
}
return;
{
if (hloff)
{
}
{
{
}
}
return;
}
# if 0 /* debugging, modify as required */
{
return;
}
# endif /* debugging code */
# endif /* ESH_BETTER */
#endif /* KSHELL */
default:
beep();
return;
}
}
{
#ifndef ESH_NFIRST
#endif
register int i,sl;
/* save current line */
string[0] = '^';
sl = 2;
{
{
if (sl > 2)
{
}
else
goto restore;
continue;
}
goto restore;
if (i==usrkill)
{
beep();
goto restore;
}
if (i == '\\')
{
}
}
if (direction < 1)
{
direction = 1;
}
else
direction = -1;
if (i != 2)
{
#if SHOPT_MULTIBYTE
#endif /* SHOPT_MULTIBYTE */
}
else
i = location.hist_command;
if(i>0)
{
hline = i;
#ifdef ESH_NFIRST
#else
#endif /* ESH_NFIRST */
#if SHOPT_MULTIBYTE
#endif /* SHOPT_MULTIBYTE */
return;
}
if (i < 0)
{
beep();
#ifdef ESH_NFIRST
#else
hloff = 0;
#endif /* ESH_NFIRST */
}
return;
}
/* Adjust screen to agree with inputs: logical line and cursor */
/* If 'first' assume screen is blank */
/* Prompt is always kept on the screen */
{
#define NORMAL ' '
#define LOWER '<'
#define BOTH '*'
#define UPPER '>'
char longline; /* Line overflow */
register int i;
{
{
return;
}
}
/*********************
Do not update screen if pending characters
**********************/
{
return;
}
/***************************************
If in append mode, cursor at end of line, screen up to date,
the previous character was a 'normal' character,
and the window has room for another character.
Then output the character and adjust the screen only.
*****************************************/
#if SHOPT_EDPREDICT
{
}
else if((option==UPDATE||option==APPEND) && drawbuff[0]=='#' && cur>1 && cur==eol && drawbuff[cur-1]!='*')
{
int n;
# if SHOPT_MULTIBYTE
# endif /*SHOPT_MULTIBYTE */
# if SHOPT_MULTIBYTE
# endif /*SHOPT_MULTIBYTE */
{
}
else
ed_ringbell();
}
#endif /* SHOPT_EDPREDICT */
{
return;
}
/* copy the line */
/*********************
Does ncursor appear on the screen?
If not, adjust the screen offset so it does.
**********************/
{
/* Center the cursor on the screen */
}
/*********************
Is the range of screen[0] thru screen[w_size] up-to-date
with nscreen[offset] thru nscreen[offset+w_size] ?
If not, update as need be.
***********************/
i = w_size;
while (i-- > 0)
{
if (*nptr == '\0')
{
*nptr = ' ';
}
if (*sptr == '\0')
{
*sptr = ' ';
}
{
nptr++;
sptr++;
continue;
}
#if SHOPT_MULTIBYTE
{
if(*sptr=='\0')
i--;
}
#endif /* SHOPT_MULTIBYTE */
}
/******************
Screen overflow checks
********************/
{
else
}
else
{
}
/* Update screen overflow indicator if need be */
{
}
return;
}
/*
* put the cursor to the <newp> position within screen buffer
* if <c> is non-zero then output this character
* cursor is set to reflect the change
*/
{
if(c)
{
newp++;
}
return;
}
#if SHOPT_MULTIBYTE
static int print(register int c)
{
}
static int _isword(register int c)
{
}
#endif /* SHOPT_MULTIBYTE */