/* This file contains terminal I/O functions */
static int showmsg
P_((void));
/* This function reads in a line from the terminal. */
int vgets(prompt
, buf
, bsize
)
int prompt
; /* the prompt character, or '\0' for none */
char *buf
; /* buffer into which the string is read */
int bsize
; /* size of the buffer */
int len
; /* how much we've read so far */
int ch
; /* a character from the user */
int quoted
; /* is the next char quoted? */
int tab
; /* column position of cursor */
char widths
[132]; /* widths of characters */
int word
; /* index of first letter of word */
int erased
; /* 0, or first char of a digraph */
if (quoted
|| mode
== MODE_EX
)
/* maybe expand an abbreviation while getting key */
for (word
= len
; --word
>= 0 && !isspace(buf
[word
]); )
ch
= getabkey(WHEN_EX
, &buf
[word
], len
- word
);
ch
= getkey(quoted
? 0 : WHEN_EX
);
/* some special conversions */
if (ch
== ctrl('D') && len
== 0)
if (*o_digraph
&& erased
!= 0 && ch
!= '\b')
ch
= digraph(erased
, ch
);
/* inhibit detection of special chars (except ^J) after a ^V */
if (quoted
&& ch
!= '\n')
/* process the character */
while (widths
[len
]-- > 0)
for (ch
= widths
[len
]; ch
> 0; ch
--)
/* strip off quotation bit */
/* add & echo the char */
if (ch
== '\t' && !quoted
)
widths
[len
] = *o_tabstop
- (tab
% *o_tabstop
);
addstr(" " + 8 - widths
[len
]);
else if (ch
> 0 && ch
< ' ') /* > 0 by GB */
static int manymsgs
; /* This variable keeps msgs from overwriting each other */
static char pmsg
[80]; /* previous message (waiting to be displayed) */
/* if there is no message to show, then don't */
/* display the message */
/* Write a message in an appropriate way. This should really be a varargs
* function, but there is no such thing as vwprintw. Hack!!!
* In MODE_EX or MODE_COLON, the message is written immediately, with a
* In MODE_VI, the message is stored in a character buffer. It is not
* displayed until getkey() is called. msg() will call getkey() itself,
* if necessary, to prevent messages from being lost.
* msg("") - clears the message line
* msg("%s %d", ...) - does a printf onto the message line
void msg (char *fmt
, ...)
void msg(fmt
, arg1
, arg2
, arg3
, arg4
, arg5
, arg6
, arg7
)
long arg1
, arg2
, arg3
, arg4
, arg5
, arg6
, arg7
;
vsprintf (pmsg
, fmt
, ap
);
sprintf(pmsg
, fmt
, arg1
, arg2
, arg3
, arg4
, arg5
, arg6
, arg7
);
/* wait for keypress between consecutive msgs */
vsprintf (pmsg
, fmt
, ap
);
sprintf(pmsg
, fmt
, arg1
, arg2
, arg3
, arg4
, arg5
, arg6
, arg7
);
/* This function calls refresh() if the option exrefresh is set */
/* If this ex command wrote ANYTHING set exwrote so vi's : command
* can tell that it must wait for a user keystroke before redrawing.
for (scan
=kbuf
; scan
<stdscr
; scan
++)
/* now we do the refresh thing */
/* This structure is used to store maps and abbreviations. The distinction
* between them is that maps are stored in the list referenced by the "maps"
* pointer, while abbreviations are referenced by the "abbrs" pointer.
struct _map
*next
; /* another abbreviation */
short len
; /* length of the "rawin" characters */
short flags
; /* various flags */
char *label
; /* label of the map/abbr, or NULL */
char *rawin
; /* the "rawin" characters */
char *cooked
;/* the "cooked" characters */
static char keybuf
[KEYBUFSIZE
];
static int cend
; /* end of input characters */
static int user
; /* from user through end are chars typed by user */
static int next
; /* index of the next character to be returned */
static MAP
*match
; /* the matching map, found by countmatch() */
static MAP
*maps
; /* the map table */
static MAP
*abbrs
; /* the abbreviation table */
/* ring the terminal's bell */
/* do a visible/audible bell */
tputs("\007", 1, faddch
);
/* discard any buffered input, and abort macros */
/* This function replaces a "rawin" character sequence with the "cooked" version,
* by modifying the internal type-ahead buffer.
void execmap(rawlen
, cookedstr
, visual
)
int rawlen
; /* length of rawin text -- string to delete */
char *cookedstr
; /* the cooked text -- string to insert */
int visual
; /* boolean -- chars to be executed in visual mode? */
/* find the length of the cooked string */
cookedlen
= strlen(cookedstr
);
/* if too big to fit in type-ahead buffer, then don't do it */
if (cookedlen
+ (cend
- next
) - rawlen
> KEYBUFSIZE
)
/* shift to make room for cookedstr at the front of keybuf */
src
= &keybuf
[next
+ rawlen
];
dst
= &keybuf
[cookedlen
];
i
= cend
- (next
+ rawlen
);
/* insert cookedstr, and adjust offsets */
cend
+= cookedlen
- rawlen
- next
;
user
+= cookedlen
- rawlen
- next
;
for (dst
= keybuf
, src
= cookedstr
; *src
; )
debout
= fopen("debug.out", "a");
fprintf(debout
, "After execmap(%d, \"%s\", %d)...\n", rawlen
, cookedstr
, visual
);
for (i
= 0; i
< cend
; i
++)
if (i
== next
) fprintf(debout
, "(next)");
if (i
== user
) fprintf(debout
, "(user)");
if (UCHAR(keybuf
[i
]) < ' ')
fprintf(debout
, "^%c", keybuf
[i
] ^ '@');
fprintf(debout
, "%c", keybuf
[i
]);
fprintf(debout
, "(end)\n");
/* made global so that suspend_curses() can reset it. -nox */
/* This function calls ttyread(). If necessary, it will also redraw the screen,
* change the cursor shape, display the mode, and update the ruler. If the
* number of characters read is 0, and we didn't time-out, then it exits because
* we've apparently reached the end of an EX script.
static int fillkeybuf(when
, timeout
)
int when
; /* mixture of WHEN_XXX flags */
int timeout
;/* timeout in 1/10 second increments, or 0 */
static int oldwhen
; /* "when" from last time */
/* make sure the cursor is the right shape */
case WHEN_EX
: do_CX(); break;
case WHEN_VICMD
: do_CV(); break;
case WHEN_VIINP
: do_CI(); break;
case WHEN_VIREP
: do_CR(); break;
/* if "showmode" then say which mode we're in */
if (*o_smd
&& (when
& WHENMASK
))
/* redraw the screen before we check to see whether the
* "showmode" message needs to be redrawn.
redraw(cursor
, !(when
& WHEN_VICMD
));
/* now the "topline" test should be valid */
if (when
& WHEN_VICMD
) str
= "Command";
else if (when
& WHEN_VIINP
) str
= " Input ";
else if (when
& WHEN_VIREP
) str
= "Replace";
else if (when
& WHEN_REP1
) str
= "Replc 1";
else if (when
& WHEN_CUT
) str
= "Buffer ";
else if (when
& WHEN_MARK
) str
= " Mark ";
else if (when
& WHEN_CHAR
) str
= "Dest Ch";
move(LINES
- 1, COLS
- 10);
/* maybe display the ruler */
if (*o_ruler
&& (when
& (WHEN_VICMD
|WHEN_VIINP
|WHEN_VIREP
)))
redraw(cursor
, !(when
& WHEN_VICMD
));
pfetch(markline(cursor
));
sprintf(buf
, "%7ld,%-4d", markline(cursor
), 1 + idx2col(cursor
, ptext
, when
& (WHEN_VIINP
|WHEN_VIREP
)));
move(LINES
- 1, COLS
- 22);
/* redraw, so the cursor is in the right place */
redraw(cursor
, !(when
& (WHENMASK
& ~(WHEN_VIREP
|WHEN_VIINP
))));
/* Okay, now we can finally read the rawin keystrokes */
nkeys
= ttyread(keybuf
+ cend
, sizeof keybuf
- cend
, timeout
);
/* if nkeys == 0 then we've reached EOF of an ex script. */
if (nkeys
== 0 && timeout
== 0)
/* This function counts the number of maps that could match the characters
* between &keybuf[next] and &keybuf[cend], including incomplete matches.
* The longest comlete match is remembered via the "match" variable.
static int countmatch(when
)
int when
; /* mixture of WHEN_XXX flags */
/* clear the "match" variable */
for (count
= 0, map
= maps
; map
; map
= map
->next
)
/* can't match if wrong mode */
if ((map
->flags
& when
) == 0)
/* would this be a complete match? */
if (map
->len
<= cend
- next
)
/* Yes, it would be. Now does it really match? */
if (!strncmp(map
->rawin
, &keybuf
[next
], map
->len
))
/* if this is the longest complete match,
if (!match
|| match
->len
< map
->len
)
/* No, it wouldn't. But check for partial match */
if (!strncmp(map
->rawin
, &keybuf
[next
], cend
- next
))
/* increment by 2 instead of 1 so that, in the
* event that we have a partial match with a
* single map, we don't mistakenly assume we
* have resolved the map yet.
/* This function checks to see whether a word is an abbreviation. If it is,
* then an appropriate number of backspoace characters is inserted into the
* type-ahead buffer, followed by the expanded form of the abbreviation.
static void expandabbr(word
, wlen
)
/* if the next character wouldn't end the word, then don't expand */
if (isalnum(keybuf
[next
]) || keybuf
[next
] == ctrl('V') || keybuf
[next
] == '\b')
/* find the abbreviation, if any */
abbr
&& (abbr
->len
!= wlen
|| strncmp(abbr
->rawin
, word
, wlen
));
/* If an abbreviation was found, then expand it by inserting the long
* version into the type-ahead buffer, and then inserting (in front of
* the long version) enough backspaces to erase to the short version.
execmap(0, abbr
->cooked
, FALSE
);
execmap(0, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", FALSE
);
execmap(0, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" + 15 - wlen
, FALSE
);
/* This function calls getabkey() without attempting to expand abbreviations */
int when
; /* mixture of WHEN_XXX flags */
return getabkey(when
, "", 0);
/* This is it. This function returns keystrokes one-at-a-time, after mapping
* and abbreviations have been taken into account.
int getabkey(when
, word
, wlen
)
int when
; /* mixture of WHEN_XXX flags */
char *word
; /* a word that may need to be expanded as an abbr */
int wlen
; /* length of "word" -- since "word" might not have \0 */
/* if this key is needed for delay between multiple error messages,
* then reset the manymsgs flag and abort any mapped key sequence.
execmap(user
, "", FALSE
);
/* periodically check for screwed up internal tables */
/* if buffer empty, read some characters without timeout */
/* try to map the key, unless already mapped and not ":set noremap" */
if (next
<= user
|| *o_remap
)
matches
= countmatch(when
);
} while (matches
> 1 && fillkeybuf(when
, *o_keytime
) > 0);
execmap(match
->len
, match
->cooked
,
(match
->flags
& WHEN_INMV
) != 0
&& (when
& (WHEN_VIINP
|WHEN_VIREP
)) != 0);
} while (*o_remap
&& matches
== 1);
/* ERASEKEY should always be mapped to '\b'. */
if (keybuf
[next
] == ERASEKEY
)
/* try to expand an abbreviation, except in visual command mode */
if (wlen
> 0 && (mode
& (WHEN_EX
|WHEN_VIINP
|WHEN_VIREP
)) != 0)
/* return the next key */
/* This function maps or unmaps a key */
void mapkey(rawin
, cooked
, when
, name
)
char *rawin
; /* the input key sequence, before mapping */
char *cooked
;/* after mapping -- or NULL to remove map */
int when
; /* bitmap of when mapping should happen */
char *name
; /* name of the key, NULL for no name, "abbr" for abbr */
MAP
**head
; /* head of list of maps or abbreviations */
MAP
*scan
; /* used for scanning through the list */
MAP
*prev
; /* used during deletions */
/* Is this a map or an abbreviation? Choose the right list. */
head
= ((!name
|| strcmp(name
, "abbr")) ? &maps
: &abbrs
);
/* try to find the map in the list */
for (scan
= *head
, prev
= (MAP
*)0;
scan
&& (strcmp(rawin
, scan
->rawin
) && strcmp(rawin
, scan
->cooked
) ||
!(scan
->flags
& when
& (WHEN_EX
|WHEN_VICMD
|WHEN_VIINP
|WHEN_VIREP
)));
prev
= scan
, scan
= scan
->next
)
/* trying to map? (not unmap) */
/* if map starts with "visual ", then mark it as a visual map */
if (head
== &maps
&& !strncmp(cooked
, "visual ", 7))
/* "visual" maps always work in input mode */
when
|= WHEN_VIINP
|WHEN_VIREP
|WHEN_POPUP
;
/* if not already in the list, then allocate a new structure */
scan
= (MAP
*)malloc(sizeof(MAP
));
scan
->len
= strlen(rawin
);
scan
->rawin
= malloc((unsigned)(scan
->len
+ 1));
strcpy(scan
->rawin
, rawin
);
else /* recycle old structure */
scan
->cooked
= malloc((unsigned)(strlen(cooked
) + 1));
strcpy(scan
->cooked
, cooked
);
/* if nothing to unmap, then exit silently */
/* unlink the structure from the list */
/* free it, and the strings that it refers to */
/* This function returns a printable version of a string. It uses tmpblk.c */
char *str
; /* the string to convert */
char *build
; /* used for building the string */
for (build
= tmpblk
.c
; *str
; str
++)
if (UCHAR(*str
) < ' ' || *str
== '\177')
/* This function displays the contents of either the map table or the
* abbreviation table. User commands call this function as follows:
* :map dumpkey(WHEN_VICMD, FALSE);
* :map! dumpkey(WHEN_VIREP|WHEN_VIINP, FALSE);
* :abbr dumpkey(WHEN_VIINP|WHEN_VIREP, TRUE);
* :abbr! dumpkey(WHEN_EX|WHEN_VIINP|WHEN_VIREP, TRUE);
int when
; /* WHEN_XXXX of mappings to be dumped */
int abbr
; /* boolean: dump abbreviations instead of maps? */
for (scan
= (abbr
? abbrs
: maps
); scan
; scan
= scan
->next
)
for (scan
= maps
; scan
; scan
= scan
->next
)
/* skip entries that don't match "when" */
if ((scan
->flags
& when
) == 0)
/* dump the key label, if any */
len
-= strlen(scan
->label
);
/* dump the rawin version */
str
= printable(scan
->rawin
);
/* dump the mapped version */
if ((scan
->flags
& WHEN_INMV
) && (when
& (WHEN_VIINP
|WHEN_VIREP
)))
str
= printable(scan
->cooked
);
static void safequote(str
)
build
= tmpblk
.c
+ strlen(tmpblk
.c
);
if (*str
<= ' ' && *str
>= 1 || *str
== '|')
/* This function saves the contents of either the map table or the
* abbreviation table into a file. Both the "bang" and "no bang" versions
* :map dumpkey(WHEN_VICMD, FALSE);
* :map! dumpkey(WHEN_VIREP|WHEN_VIINP, FALSE);
* :abbr dumpkey(WHEN_VIINP|WHEN_VIREP, TRUE);
* :abbr! dumpkey(WHEN_EX|WHEN_VIINP|WHEN_VIREP, TRUE);
int fd
; /* file descriptor of an open file to write to */
int abbr
; /* boolean: do abbr table? (else do map table) */
for (scan
= (abbr
? abbrs
: maps
); scan
; scan
= scan
->next
)
for (scan
= maps
; scan
; scan
= scan
->next
)
/* skip maps that have labels, except for function keys */
if (scan
->label
&& *scan
->label
!= '#')
for (bang
= 0; bang
< 2; bang
++)
/* decide which "when" flags we want */
when
= (bang
? WHEN_EX
|WHEN_VIINP
|WHEN_VIREP
: WHEN_VIINP
|WHEN_VIREP
);
when
= (bang
? WHEN_VIREP
|WHEN_VIINP
: WHEN_VICMD
);
/* skip entries that don't match "when" */
if ((scan
->flags
& when
) == 0)
/* write a "map" or "abbr" command name */
strcpy(tmpblk
.c
, "abbr");
/* maybe write a bang. Definitely write a space */
/* write the rawin version */
strcat(tmpblk
.c
, scan
->label
);
/* dump the mapped version */
if ((scan
->flags
& WHEN_INMV
) && (when
& (WHEN_VIINP
|WHEN_VIREP
)))
strcat(tmpblk
.c
, "visual ");
twrite(fd
, tmpblk
.c
, strlen(tmpblk
.c
));