* apps/system/vi/vi.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <syslog.h>
#include <errno.h>
#include <debug.h>
#include <system/termcurses.h>
#include <graphics/curses.h>
#include <nuttx/ascii.h>
#include <nuttx/vt100.h>
* Pre-processor Definitions
****************************************************************************/
#ifndef CONFIG_SYSTEM_VI_ROWS
# define CONFIG_SYSTEM_VI_ROWS 16
#endif
#ifndef CONFIG_SYSTEM_VI_COLS
# define CONFIG_SYSTEM_VI_COLS 64
#endif
#ifndef CONFIG_SYSTEM_VI_YANK_THRESHOLD
#define CONFIG_SYSTEM_VI_YANK_THRESHOLD 128
#endif
#undef CTRL
#define CTRL(a) ((a) & 0x1f)
#define VI_BEL(vi) vi_putch(vi,CTRL('G'))
#define MAX_STRING 64
#define MAX_FILENAME 128
#define SCRATCH_BUFSIZE 128
#define CMD_BUFSIZE 128
#define TEXT_GULP_SIZE 512
#define TEXT_GULP_MASK 511
#define ALIGN_GULP(x) (((x) + TEXT_GULP_MASK) & ~TEXT_GULP_MASK)
#define VI_TABSIZE 8
#define TABMASK 7
#define NEXT_TAB(p) (((p) + VI_TABSIZE) & ~TABMASK)
#define CMD_READ (1 << 0)
#define CMD_WRITE_MASK (3 << 1)
# define CMD_WRITE (1 << 1)
# define CMD_OWRITE (3 << 1)
#define CMD_QUIT_MASK (3 << 3)
# define CMD_QUIT (1 << 3)
# define CMD_DISCARD (3 << 3)
#define CMD_NONE (0)
#define CMD_WRITE_QUIT (CMD_WRITE | CMD_QUIT)
#define CMD_OWRITE_QUIT (CMD_OWRITE | CMD_QUIT)
#define IS_READ(a) (((uint8_t)(a) & CMD_READ) != 0)
#define IS_WRITE(a) (((uint8_t)(a) & CMD_WRITE) != 0)
#define IS_OWRITE(a) (((uint8_t)(a) & CMD_WRITE_MASK) == CMD_OWRITE)
#define IS_NOWRITE(a) (((uint8_t)(a) & CMD_WRITE_MASK) == CMD_WRITE)
#define IS_QUIT(a) (((uint8_t)(a) & CMD_QUIT) != 0)
#define IS_DISCARD(a) (((uint8_t)(a) & CMD_QUIT_MASK) == CMD_DISCARD)
#define IS_NDISCARD(a) (((uint8_t)(a) & CMD_QUIT_MASK) == CMD_QUIT && !IS_WRITE(a))
#define CMD_FILE_MASK (CMD_READ | CMD_WRITE)
#define USES_FILE(a) (((uint8_t)(a) & CMD_FILE_MASK) != 0)
#define VI_CHAR_SPACE 0
#define VI_CHAR_ALPHA 1
#define VI_CHAR_PUNCT 2
#define VI_CHAR_CRLF 3
#define vi_error(vi, fmt, ...) vi_printf(vi, "ERROR: ", fmt, ##__VA_ARGS__)
#define vi_message(vi, fmt, ...) vi_printf(vi, NULL, fmt, ##__VA_ARGS__)
#ifndef CONFIG_SYSTEM_VI_DEBUGLEVEL
# define CONFIG_SYSTEM_VI_DEBUGLEVEL 0
#endif
#if CONFIG_SYSTEM_VI_DEBUGLEVEL > 0
# define vidbg(format, ...) \
syslog(LOG_DEBUG, EXTRA_FMT format EXTRA_ARG, ##__VA_ARGS__)
# define vvidbg(format, ap) \
vsyslog(LOG_DEBUG, format, ap)
#else
# define vidbg(x...)
# define vvidbg(x...)
#endif
#if CONFIG_SYSTEM_VI_DEBUGLEVEL > 1
# define viinfo(format, ...) \
syslog(LOG_DEBUG, EXTRA_FMT format EXTRA_ARG, ##__VA_ARGS__)
#else
# define viinfo(x...)
#endif
* paste debugging, etc.
*/
* Private Types
****************************************************************************/
enum vi_cmdmode_key_e
{
KEY_CMDMODE_BEGINLINE = '0',
KEY_CMDMODE_APPEND = 'a',
KEY_CMDMODE_WORDBACK = 'b',
KEY_CMDMODE_CHANGE = 'c',
KEY_CMDMODE_DEL_LINE = 'd',
KEY_CMDMODE_FINDINLINE = 'f',
KEY_CMDMODE_GOTOTOP = 'g',
KEY_CMDMODE_LEFT = 'h',
KEY_CMDMODE_INSERT = 'i',
KEY_CMDMODE_DOWN = 'j',
KEY_CMDMODE_UP = 'k',
KEY_CMDMODE_RIGHT = 'l',
KEY_CMDMODE_MARK = 'm',
KEY_CMDMODE_FINDNEXT = 'n',
KEY_CMDMODE_OPENBELOW = 'o',
KEY_CMDMODE_PASTE = 'p',
KEY_CMDMODE_REPLACECH = 'r',
KEY_CMDMODE_SUBSTITUTE = 's',
KEY_CMDMODE_TFINDINLINE = 't',
KEY_CMDMODE_WORDFWD = 'w',
KEY_CMDMODE_DEL = 'x',
KEY_CMDMODE_YANK = 'y',
KEY_CMDMODE_CR = '\r',
KEY_CMDMODE_NL = '\n',
KEY_CMDMODE_APPENDEND = 'A',
KEY_CMDMODE_CHANGETOEOL = 'C',
KEY_CMDMODE_DELTOEOL = 'D',
KEY_CMDMODE_GOTO = 'G',
KEY_CMDMODE_TOP = 'H',
KEY_CMDMODE_JOIN = 'J',
KEY_CMDMODE_BOTTOM = 'L',
KEY_CMDMODE_INSBEGIN = 'I',
KEY_CMDMODE_MIDDLE = 'M',
KEY_CMDMODE_FINDPREV = 'N',
KEY_CMDMODE_OPENABOVE = 'O',
KEY_CMDMODE_PASTEBEFORE = 'P',
KEY_CMDMODE_REPLACE = 'R',
KEY_CMDMODE_DELBACKWARD = 'X',
KEY_CMDMODE_SAVEQUIT = 'Z',
KEY_CMDMODE_COLONMODE = ':',
KEY_CMDMODE_FINDMODE = '/',
KEY_CMDMODE_ENDLINE = '$',
KEY_CMDMODE_REVFINDMODE = '?',
KEY_CMDMODE_REPEAT = '.',
KEY_CMDMODE_FIRSTCHAR = '^',
KEY_CMDMODE_NEXTLINE = '+',
KEY_CMDMODE_PREVLINE = '-',
KEY_CMDMODE_PAGEUP = CTRL('b'),
KEY_CMDMODE_HALFDOWN = CTRL('d'),
KEY_CMDMODE_PAGEDOWN = CTRL('f'),
KEY_CMDMODE_REDRAW = CTRL('l'),
KEY_CMDMODE_REDRAW2 = CTRL('r'),
KEY_CMDMODE_HALFUP = CTRL('u')
};
enum vi_insmode_key_e
{
KEY_INSMODE_QUOTE = '\\',
};
enum vi_colonmode_key_e
{
KEY_COLMODE_READ = 'r',
KEY_COLMODE_QUIT = 'q',
KEY_COLMODE_WRITE = 'w',
KEY_COLMODE_FORCE = '!',
KEY_COLMODE_QUOTE = '\\'
};
enum vi_findmode_key_e
{
KEY_FINDMODE_QUOTE = '\\'
};
enum vi_mode_s
{
MODE_COMMAND = 0,
SUBMODE_COLON,
SUBMODE_FIND,
SUBMODE_REVFIND,
SUBMODE_REPLACECH,
MODE_INSERT,
MODE_REPLACE,
MODE_FINDINLINE
};
struct vi_pos_s
{
uint16_t row;
uint16_t column;
};
#ifdef CONFIG_SYSTEM_VI_INCLUDE_UNDO
struct vi_undo_s
{
off_t curpos;
off_t ybytes;
off_t ibytes;
FAR char *yank;
FAR char *insert;
uint8_t type;
uint8_t complete;
};
#endif
struct vi_s
{
struct vi_pos_s cursor;
struct vi_pos_s cursave;
struct vi_pos_s display;
FAR struct termcurses_s *tcurs;
off_t curpos;
off_t textsize;
off_t winpos;
off_t prevpos;
off_t vscroll;
uint16_t hscroll;
uint16_t value;
uint16_t reqcolumn;
uint8_t mode;
uint8_t cmdlen;
bool modified;
bool error;
bool delarm;
bool chgarm;
bool yankarm;
bool toparm;
bool wqarm;
bool fullredraw;
bool drawtoeos;
bool redrawline;
bool updatereqcol;
bool tfind;
bool revfind;
bool yankcharmode;
FAR char *text;
size_t txtalloc;
FAR char *yank;
size_t yankalloc;
size_t yanksize;
char filename[MAX_FILENAME];
char findstr[MAX_STRING];
char scratch[SCRATCH_BUFSIZE];
#ifdef CONFIG_SYSTEM_VI_INCLUDE_UNDO
struct vi_undo_s undo[CONFIG_SYSTEM_VI_UNDO_LEVELS];
uint16_t undocount;
uint16_t undoindex;
#endif
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
char cmdbuf[CMD_BUFSIZE];
uint16_t cmdindex;
uint16_t cmdcount;
uint16_t repeatvalue;
bool cmdrepeat;
#endif
};
* Private Function Prototypes
****************************************************************************/
static void vi_write(FAR struct vi_s *vi, FAR const char *buffer,
size_t buflen);
static void vi_putch(FAR struct vi_s *vi, char ch);
static int vi_getch(FAR struct vi_s *vi);
#if 0
static void vi_blinkon(FAR struct vi_s *vi);
#endif
static void vi_boldon(FAR struct vi_s *vi);
static void vi_reverseon(FAR struct vi_s *vi);
static void vi_attriboff(FAR struct vi_s *vi);
static void vi_cursoron(FAR struct vi_s *vi);
static void vi_cursoroff(FAR struct vi_s *vi);
#if 0
static void vi_cursorhome(FAR struct vi_s *vi);
#endif
static void vi_setcursor(FAR struct vi_s *vi, uint16_t row,
uint16_t column);
static void vi_clrtoeol(FAR struct vi_s *vi);
#if 0
static void vi_clrscreen(FAR struct vi_s *vi);
#endif
static void vi_printf(FAR struct vi_s *vi, FAR const char *prefix,
FAR const char *fmt, ...) printf_like(3, 4);
static off_t vi_linebegin(FAR struct vi_s *vi, off_t pos);
static off_t vi_prevline(FAR struct vi_s *vi, off_t pos);
static off_t vi_lineend(FAR struct vi_s *vi, off_t pos);
static off_t vi_nextline(FAR struct vi_s *vi, off_t pos);
static bool vi_extendtext(FAR struct vi_s *vi, off_t pos,
size_t increment);
static void vi_shrinkpos(FAR struct vi_s *vi, off_t delpos,
size_t delsize, FAR off_t *pos);
static void vi_shrinktext(FAR struct vi_s *vi, off_t pos, size_t size);
static bool vi_insertfile(FAR struct vi_s *vi, off_t pos,
FAR const char *filename);
static bool vi_savetext(FAR struct vi_s *vi, FAR const char *filename,
off_t pos, size_t size);
static bool vi_checkfile(FAR struct vi_s *vi, FAR const char *filename);
static void vi_setmode(FAR struct vi_s *vi, uint8_t mode, long value);
static void vi_setsubmode(FAR struct vi_s *vi, uint8_t mode,
char prompt, long value);
static void vi_exitsubmode(FAR struct vi_s *vi, uint8_t mode);
static void vi_windowpos(FAR struct vi_s *vi, off_t start, off_t end,
uint16_t *pcolumn, off_t *ppos);
static void vi_scrollcheck(FAR struct vi_s *vi);
static void vi_showtext(FAR struct vi_s *vi);
static void vi_showlinecol(FAR struct vi_s *vi);
static void vi_cusorup(FAR struct vi_s *vi, int nlines);
static void vi_cursordown(FAR struct vi_s *vi, int nlines);
static off_t vi_cursorleft(FAR struct vi_s *vi, off_t curpos,
int ncolumns);
static off_t vi_cursorright(FAR struct vi_s *vi, off_t curpos,
int ncolumns);
static void vi_delforward(FAR struct vi_s *vi);
static void vi_delbackward(FAR struct vi_s *vi);
static void vi_linerange(FAR struct vi_s *vi, off_t *start, off_t *end);
static void vi_delline(FAR struct vi_s *vi);
static void vi_deltoeol(FAR struct vi_s *vi);
static void vi_yanktext(FAR struct vi_s *vi, off_t start, off_t end,
bool yankcharmode, bool del_after_yank);
static void vi_yank(FAR struct vi_s *vi, bool del_after_yank);
static void vi_paste(FAR struct vi_s *vi, bool paste_before);
static void vi_gotoline(FAR struct vi_s *vi);
static void vi_join(FAR struct vi_s *vi);
static void vi_cmd_mode(FAR struct vi_s *vi);
static int vi_gotoscreenbottom(FAR struct vi_s *vi, int rows);
static void vi_gotofirstnonwhite(FAR struct vi_s *vi);
static void vi_cmdch(FAR struct vi_s *vi, char ch);
static void vi_cmdbackspace(FAR struct vi_s *vi);
static void vi_parsecolon(FAR struct vi_s *vi);
static void vi_cmd_submode(FAR struct vi_s *vi);
static bool vi_findstring(FAR struct vi_s *vi);
static bool vi_revfindstring(FAR struct vi_s *vi);
static void vi_parsefind(FAR struct vi_s *vi, bool revfind);
static void vi_find_submode(FAR struct vi_s *vi, bool revfind);
static void vi_replacech(FAR struct vi_s *vi, char ch);
static void vi_replacech_submode(FAR struct vi_s *vi);
static void vi_findinline_mode(FAR struct vi_s *vi);
static void vi_insertch(FAR struct vi_s *vi, char ch);
static void vi_insert_mode(FAR struct vi_s *vi);
static void vi_release(FAR struct vi_s *vi);
static void vi_showusage(FAR struct vi_s *vi, FAR const char *progname,
int exitcode);
static int vi_chartype(char ch);
static off_t vi_findnextword(FAR struct vi_s *vi);
static void vi_gotonextword(FAR struct vi_s *vi);
static off_t vi_findprevword(FAR struct vi_s *vi);
static void vi_gotoprevword(FAR struct vi_s *vi);
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
static void vi_saverepeat(FAR struct vi_s *vi, uint16_t ch);
static void vi_appendrepeat(FAR struct vi_s *vi, uint16_t ch);
#endif
* Private Data
****************************************************************************/
static const char g_cursoron[] = VT100_CURSORON;
static const char g_cursoroff[] = VT100_CURSOROFF;
#if 0
static const char g_cursorhome[] = VT100_CURSORHOME;
#endif
static const char g_erasetoeol[] = VT100_CLEAREOL;
#if 0
static const char g_clrscreen[] = VT100_CLEARSCREEN;
#endif
static const char g_index[] = VT100_INDEX;
static const char g_revindex[] = VT100_REVINDEX;
static const char g_attriboff[] = VT100_MODESOFF;
static const char g_boldon[] = VT100_BOLD;
static const char g_reverseon[] = VT100_REVERSE;
#if 0
static const char g_blinkon[] = VT100_BLINK;
static const char g_boldoff[] = VT100_BOLDOFF;
static const char g_reverseoff[] = VT100_REVERSEOFF;
static const char g_blinkoff[] = VT100_BLINKOFF;
#endif
static const char g_fmtcursorpos[] = VT100_FMT_CURSORPOS;
static const char g_fmtallocfail[] = "Failed to allocate memory";
static const char g_fmtcmdfail[] = "%s failed: %d";
static const char g_fmtnotfile[] = "%s is not a regular file";
static const char g_fmtfileexists[] = "File exists (add ! to override)";
static const char g_fmtmodified[] =
"No write since last change (add ! to override)";
static const char g_fmtnotvalid[] = "Command not valid";
static const char g_fmtnotcmd[] = "Not an editor command: %s";
static const char g_fmtsrcbot[] = "search hit BOTTOM(continuing at TOP)";
static const char g_fmtsrctop[] = "search hit TOP(continuing at BOTTOM)";
static const char g_fmtinsert[] = "--INSERT--";
* Private Functions
****************************************************************************/
* Low-level display and data entry functions
****************************************************************************/
* Name: vi_write
*
* Description:
* Write a sequence of bytes to the console device (stdout, fd = 1).
*
****************************************************************************/
static void vi_write(FAR struct vi_s *vi, FAR const char *buffer,
size_t buflen)
{
ssize_t nwritten;
size_t nremaining = buflen;
* unrecoverable error is encountered)
*/
do
{
nwritten = write(1, buffer, buflen);
if (nwritten <= 0)
{
* received while waiting for write.
*/
int errcode = errno;
if (nwritten == 0 || errcode != EINTR)
{
fprintf(stderr, "ERROR: write to stdout failed: %d\n",
errcode);
vi_release(vi);
exit(EXIT_FAILURE);
}
}
* case of a partial write)
*/
else
{
nremaining -= nwritten;
}
}
while (nremaining > 0);
}
* Name: vi_putch
*
* Description:
* Write a single character to the console device.
*
****************************************************************************/
static void vi_putch(FAR struct vi_s *vi, char ch)
{
vi_write(vi, &ch, 1);
}
* Name: vi_getch
*
* Description:
* Get a single character from the console device (stdin, fd = 0).
*
****************************************************************************/
static int vi_getch(FAR struct vi_s *vi)
{
char buffer;
ssize_t nread;
* error occurs).
*/
if (vi->tcurs != NULL)
{
int specialkey;
int modifiers;
return termcurses_getkeycode(vi->tcurs, &specialkey, &modifiers);
}
else
{
do
{
nread = read(0, &buffer, 1);
if (nread <= 0)
{
* we received while waiting for input.
*/
int errcode = errno;
if (nread == 0 || errcode != EINTR)
{
fprintf(stderr, "ERROR: read from stdin failed: %d\n",
errcode);
vi_release(vi);
exit(EXIT_FAILURE);
}
}
}
while (nread < 1);
}
viinfo("Returning: %c[%02x]\n", isprint(buffer) ? buffer : '.', buffer);
return buffer;
}
* Name: vi_clearbottomline
*
* Description:
* Clear the bottom statusline.
*
****************************************************************************/
static void vi_clearbottomline(FAR struct vi_s *vi)
{
vi_setcursor(vi, vi->display.row - 1, 0);
vi_clrtoeol(vi);
}
* Name: vi_boldon
*
* Description:
* Enable the blinking attribute at the current cursor location
*
****************************************************************************/
static void vi_boldon(FAR struct vi_s *vi)
{
vi_write(vi, g_boldon, sizeof(g_boldon));
}
* Name: vi_reverseon
*
* Description:
* Enable the blinking attribute at the current cursor location
*
****************************************************************************/
static void vi_reverseon(FAR struct vi_s *vi)
{
vi_write(vi, g_reverseon, sizeof(g_reverseon));
}
* Name: vi_attriboff
*
* Description:
* Disable all previously selected attributes.
*
****************************************************************************/
static void vi_attriboff(FAR struct vi_s *vi)
{
vi_write(vi, g_attriboff, sizeof(g_attriboff));
}
* Name: vi_cursoron
*
* Description:
* Turn on the cursor
*
****************************************************************************/
static void vi_cursoron(FAR struct vi_s *vi)
{
vi_write(vi, g_cursoron, sizeof(g_cursoron));
}
* Name: vi_cursoroff
*
* Description:
* Turn off the cursor
*
****************************************************************************/
static void vi_cursoroff(FAR struct vi_s *vi)
{
vi_write(vi, g_cursoroff, sizeof(g_cursoroff));
}
* Name: vi_setcursor
*
* Description:
* Move the current cursor position to position (row,col)
*
****************************************************************************/
static void vi_setcursor(FAR struct vi_s *vi, uint16_t row, uint16_t column)
{
char buffer[16];
int len;
viinfo("row=%d column=%d\n", row, column);
len = snprintf(buffer, sizeof(buffer), g_fmtcursorpos,
row + 1, column + 1);
vi_write(vi, buffer, MIN(len, sizeof(buffer)));
}
* Name: vi_clrtoeol
*
* Description:
* Clear the display from the current cursor position to the end of the
* current line.
*
****************************************************************************/
static void vi_clrtoeol(FAR struct vi_s *vi)
{
vi_write(vi, g_erasetoeol, sizeof(g_erasetoeol));
}
* Name: vi_scrollup
*
* Description:
* Scroll the display up 'nlines' by sending the VT100 INDEX command.
*
****************************************************************************/
static void vi_scrollup(FAR struct vi_s *vi, uint16_t nlines)
{
viinfo("nlines=%d\n", nlines);
for (; nlines; nlines--)
{
vi_write(vi, g_index, sizeof(g_index));
}
vi_setcursor(vi, vi->display.row - 1, 0);
vi_clrtoeol(vi);
}
* Name: vi_scrolldown
*
* Description:
* Scroll the display down 'nlines' by sending the VT100 REVINDEX command.
*
****************************************************************************/
static void vi_scrolldown(FAR struct vi_s *vi, uint16_t nlines)
{
viinfo("nlines=%d\n", nlines);
vi_setcursor(vi, vi->display.row - 2, 0);
vi_clrtoeol(vi);
for (; nlines; nlines--)
{
vi_write(vi, g_revindex, sizeof(g_revindex));
}
}
* Name: vi_printf
*
* Description:
* Show a highlighted message at the final line of the display.
*
****************************************************************************/
static void vi_printf(FAR struct vi_s *vi, FAR const char *prefix,
FAR const char *fmt, ...)
{
struct vi_pos_s cursor;
va_list ap;
int len;
cursor.row = vi->cursor.row;
cursor.column = vi->cursor.column;
vi_setcursor(vi, vi->display.row - 1, 0);
vi_reverseon(vi);
len = prefix ? snprintf(vi->scratch,
sizeof(vi->scratch), "%s", prefix) : 0;
len = MIN(len, sizeof(vi->scratch));
va_start(ap, fmt);
len += vsnprintf(vi->scratch + len, sizeof(vi->scratch) - len, fmt, ap);
len = MIN(len, sizeof(vi->scratch));
vvidbg(fmt, ap);
va_end(ap);
vi_write(vi, vi->scratch, len);
vi_attriboff(vi);
vi_setcursor(vi, cursor.row, cursor.column);
* When the display is refreshed, the last line will not be altered until
* the error is cleared.
*/
vi->error = true;
VI_BEL(vi);
}
* Line positioning
****************************************************************************/
* Name: vi_linebegin
*
* Description:
* Search backward for the beginning of the current line
*
****************************************************************************/
static off_t vi_linebegin(FAR struct vi_s *vi, off_t pos)
{
* the beginning of the text buffer).
*/
while (pos && vi->text[pos - 1] != '\n')
{
pos--;
}
viinfo("Return pos=%ld\n", (long)pos);
return pos;
}
* Name: vi_prevline
*
* Description:
* Search backward for the beginning of the previous line
*
****************************************************************************/
static off_t vi_prevline(FAR struct vi_s *vi, off_t pos)
{
pos = vi_linebegin(vi, pos);
* at the last byte of the previous line.
*/
if (pos > 0)
{
pos = vi_linebegin(vi, pos - 1);
}
viinfo("Return pos=%ld\n", (long)pos);
return pos;
}
* Name: vi_lineend
*
* Description:
* Search forward for the end of the current line
*
****************************************************************************/
static off_t vi_lineend(FAR struct vi_s *vi, off_t pos)
{
* the end of the text buffer).
*/
while (pos < vi->textsize && vi->text[pos] != '\n')
{
pos++;
}
if (vi->text[pos] == '\n')
{
pos--;
}
viinfo("Return pos=%ld\n", (long)pos);
return pos;
}
* Name: vi_nextline
*
* Description:
* Search backward for the start of the next line
*
****************************************************************************/
static off_t vi_nextline(FAR struct vi_s *vi, off_t pos)
{
pos = vi_lineend(vi, pos) + 1;
* for position of the first byte of the next line.
*/
if (pos < vi->textsize)
{
pos++;
}
viinfo("Return pos=%ld\n", (long)pos);
return pos;
}
* Text buffer management
****************************************************************************/
* Name: vi_extendtext
*
* Description:
* Reallocate the in-memory file memory by (at least) 'increment' and make
* space for new text of size 'increment' at the specified cursor position.
*
****************************************************************************/
static bool vi_extendtext(FAR struct vi_s *vi, off_t pos, size_t increment)
{
FAR char *alloc;
int i;
viinfo("pos=%ld increment=%ld\n", (long)pos, (long)increment);
if (!vi->text || vi->textsize + increment > vi->txtalloc)
{
* often.
*/
size_t allocsize = ALIGN_GULP(vi->textsize + increment);
alloc = realloc(vi->text, allocsize);
if (alloc == NULL)
{
vi_error(vi, g_fmtallocfail);
return false;
}
vi->text = alloc;
vi->txtalloc = allocsize;
}
* cursor position
*/
for (i = vi->textsize - 1; i >= pos; i--)
{
vi->text[i + increment] = vi->text[i];
}
vi->textsize += increment;
vi->modified = true;
return true;
}
* Name: vi_shrinkpos
*
* Description:
* This is really part of vi_shrinktext. When any text is deleted, any
* positions lying beyond the deleted region in the text buffer must be
* adjusted.
*
* Input Parameters:
* delpos The position where text was deleted
* delsize The number of bytes deleted.
* pos A pointer to a position that may need to be adjusted.
*
****************************************************************************/
static void vi_shrinkpos(FAR struct vi_s *vi, off_t delpos, size_t delsize,
FAR off_t *pos)
{
viinfo("delpos=%ld delsize=%ld pos=%ld\n",
(long)delpos, (long)delsize, (long)*pos);
if (*pos > delpos + delsize)
{
*pos -= delsize;
}
* beginning of the deleted region.
*/
else if (*pos > delpos)
{
*pos = delpos;
}
* text at the end of the buffer is being deleted
*/
if (*pos >= vi->textsize && vi->mode != MODE_INSERT &&
vi->mode != MODE_REPLACE)
{
*pos = vi->textsize - 1;
}
if (*pos < 0)
{
*pos = 0;
}
}
* Name: vi_shrinktext
*
* Description:
* Delete a region in the text buffer by copying the end of the text buffer
* over the deleted region and adjusting the size of the region. The text
* region may be reallocated in order to recover the unused memory.
*
****************************************************************************/
static void vi_shrinktext(FAR struct vi_s *vi, off_t pos, size_t size)
{
FAR char *alloc;
size_t allocsize;
int i;
viinfo("pos=%ld size=%ld\n", (long)pos, (long)size);
for (i = pos + size; i < vi->textsize; i++)
{
vi->text[i - size] = vi->text[i];
}
if (size > vi->textsize)
{
size = vi->textsize;
}
vi->textsize -= size;
vi->modified = true;
vi_shrinkpos(vi, pos, size, &vi->curpos);
vi_shrinkpos(vi, pos, size, &vi->winpos);
vi_shrinkpos(vi, pos, size, &vi->prevpos);
allocsize = ALIGN_GULP(vi->textsize);
if (allocsize == 0)
{
allocsize = TEXT_GULP_SIZE;
}
if (allocsize < vi->txtalloc)
{
alloc = realloc(vi->text, allocsize);
if (!alloc)
{
vi_error(vi, g_fmtallocfail);
return;
}
vi->text = alloc;
vi->txtalloc = allocsize;
}
}
* File access
****************************************************************************/
* Name: vi_insertfile
*
* Description:
* Insert the contents of a file into the text buffer
*
****************************************************************************/
static bool vi_insertfile(FAR struct vi_s *vi, off_t pos,
FAR const char *filename)
{
struct stat buf;
FILE *stream;
off_t filesize;
size_t nread;
int result;
bool ret;
viinfo("pos=%ld filename=\"%s\"\n", (long)pos, filename);
result = stat(filename, &buf);
if (result < 0)
{
vi_message(vi, "\"%s\" [New File]", filename);
return false;
}
filesize = buf.st_size;
if (filesize < 1)
{
return false;
}
stream = fopen(filename, "r");
if (!stream)
{
vi_error(vi, g_fmtcmdfail, "open", errno);
return false;
}
* cursor position.
*/
ret = false;
if (vi_extendtext(vi, pos, filesize))
{
* current cursor position.
*/
nread = fread(vi->text + pos, 1, filesize, stream);
if (nread < filesize)
{
vi_error(vi, g_fmtcmdfail, "fread", errno);
vi_shrinktext(vi, pos, filesize);
}
else
{
ret = true;
}
}
vi->fullredraw = true;
fclose(stream);
return ret;
}
* Name: vi_savetext
*
* Description:
* Save a region of the text buffer to 'filename'
*
****************************************************************************/
static bool vi_savetext(FAR struct vi_s *vi, FAR const char *filename,
off_t pos, size_t size)
{
FAR FILE *stream;
size_t nwritten;
int len;
viinfo("filename=\"%s\" pos=%ld size=%ld\n",
filename, (long)pos, (long)size);
stream = fopen(filename, "w");
if (!stream)
{
vi_error(vi, g_fmtcmdfail, "fopen", errno);
return false;
}
* through pos + size -1.
*/
nwritten = fwrite(vi->text + pos, 1, size, stream);
if (nwritten < size)
{
vi_error(vi, g_fmtcmdfail, "fwrite", errno);
fclose(stream);
return false;
}
fclose(stream);
len = snprintf(vi->scratch, sizeof(vi->scratch), "%dC written", nwritten);
vi_write(vi, vi->scratch, MIN(len, sizeof(vi->scratch)));
return true;
}
* Name: vi_checkfile
*
* Description:
* Check if a file by this name already exists.
*
****************************************************************************/
static bool vi_checkfile(FAR struct vi_s *vi, FAR const char *filename)
{
struct stat buf;
int ret;
viinfo("filename=\"%s\"\n", filename);
ret = stat(filename, &buf);
if (ret < 0)
{
return false;
}
if (!S_ISREG(buf.st_mode))
{
* this case.
*/
vi_error(vi, g_fmtnotfile, filename);
}
return true;
}
* Mode Management Functions
****************************************************************************/
* Name: vi_setmode
*
* Description:
* Set the new mode (or command sub-mode) and reset all other common state
* variables. NOTE that a numeric value may be passed to the new mode in
* the value field.
*
****************************************************************************/
static void vi_setmode(FAR struct vi_s *vi, uint8_t mode, long value)
{
viinfo("mode=%d value=%ld\n", mode, value);
* across mode changes.
*/
vi->mode = mode;
vi->delarm = false;
vi->yankarm = false;
vi->toparm = false;
vi->chgarm = false;
vi->wqarm = false;
vi->value = value;
vi->cmdlen = 0;
}
* Name: vi_setsubmode
*
* Description:
* Set up one of the data entry sub-modes of the command mode. These are
* modes in which commands or search data will be entered on the final line
* of the display.
*
****************************************************************************/
static void vi_setsubmode(FAR struct vi_s *vi, uint8_t mode, char prompt,
long value)
{
viinfo("mode=%d prompt='%c' value=%ld\n", mode, prompt, value);
vi_setmode(vi, mode, value);
vi->cursave.row = vi->cursor.row;
vi->cursave.column = vi->cursor.column;
vi->cursor.row = vi->display.row - 1;
vi->cursor.column = 0;
vi_setcursor(vi, vi->cursor.row, vi->cursor.column);
vi_boldon(vi);
vi_putch(vi, prompt);
vi_attriboff(vi);
vi_clrtoeol(vi);
vi->cursor.column = 1;
}
* Name: vi_exitsubmode
*
* Description:
* Exit the data entry sub-mode and return to normal command mode.
*
****************************************************************************/
static void vi_exitsubmode(FAR struct vi_s *vi, uint8_t mode)
{
viinfo("mode=%d\n", mode);
vi_setmode(vi, mode, 0);
vi->cursor.row = vi->cursave.row;
vi->cursor.column = vi->cursave.column;
}
* Display Management
****************************************************************************/
* Name: vi_windowpos
*
* Description:
* Based on the position of the cursor in the text buffer, determine the
* horizontal display cursor position, performing TAB expansion as
* necessary.
*
****************************************************************************/
static void vi_windowpos(FAR struct vi_s *vi, off_t start, off_t end,
uint16_t *pcolumn, off_t *ppos)
{
uint16_t column;
off_t pos;
viinfo("start=%ld end=%ld\n", (long)start, (long)end);
* assume that the start position is okay.
*/
if (end > vi->textsize)
{
end = vi->textsize;
}
* is within range.
*/
for (pos = start, column = 0; pos < end && (column < vi->reqcolumn ||
vi->updatereqcol); pos++)
{
if (vi->text[pos] == '\n')
{
if (vi->mode != MODE_INSERT && vi->mode != MODE_REPLACE &&
pos != start)
{
pos--;
column--;
}
break;
}
else if (vi->text[pos] == '\t')
{
column = NEXT_TAB(column);
}
else
{
column++;
}
}
if (((pos == vi->textsize && column != 0) ||
(vi->text[pos] == '\n' && pos != start)) &&
vi->mode != MODE_INSERT && vi->mode != MODE_REPLACE)
{
pos--;
column--;
}
if (ppos)
{
*ppos = pos;
}
if (pcolumn)
{
*pcolumn = column;
}
if (vi->updatereqcol)
{
vi->reqcolumn = column;
vi->updatereqcol = false;
}
}
* Name: vi_scrollcheck
*
* Description:
* Check if any operations will require that we scroll the display.
*
****************************************************************************/
static void vi_scrollcheck(FAR struct vi_s *vi)
{
off_t curline;
off_t pos;
uint16_t tmp;
int column;
int nlines;
if (vi->curpos > vi->textsize)
{
vi->curpos = vi->textsize;
}
curline = vi_linebegin(vi, vi->curpos);
while (curline < vi->winpos)
{
* line line and check again
*/
vi->winpos = vi_prevline(vi, vi->winpos);
vi->vscroll--;
vi->fullredraw = true;
}
* top of the display.
*/
vi->cursor.row = 0;
for (pos = vi->winpos; pos < curline; pos = vi_nextline(vi, pos))
{
vi->cursor.row++;
}
for (; vi->cursor.row >= vi->display.row - 1; vi->cursor.row--)
{
vi->winpos = vi_nextline(vi, vi->winpos);
vi->vscroll++;
vi->fullredraw = true;
}
* unrestricted column number of cursor. hscroll is the horizontal offset
* in characters.
*/
vi_windowpos(vi, curline, vi->curpos, &tmp, NULL);
column = (int)tmp - (int)vi->hscroll;
* column lies to the left of the horizontal scrolling position. If it
* does, move the scroll position to the left by tabs until the cursor
* lies on the display.
*/
while (column < 0)
{
column += VI_TABSIZE;
vi->hscroll -= VI_TABSIZE;
vi->fullredraw = true;
}
* the horizontal scrolling position so that the cursor position does
* lie on the display.
*/
while (column >= vi->display.column)
{
column -= VI_TABSIZE;
vi->hscroll += VI_TABSIZE;
vi->fullredraw = true;
}
vi->cursor.column = column;
* In this case, we will need to scroll up until the new window
* position is at the top of the display.
*/
if (vi->winpos > vi->prevpos)
{
* need to scroll.
*/
for (nlines = 0, pos = vi->prevpos;
pos != vi->winpos && nlines < vi->display.row - 1;
nlines++)
{
pos = vi_nextline(vi, pos);
}
if (nlines < vi->display.row - 1)
{
vi_scrollup(vi, nlines);
vi->fullredraw = true;
}
}
* In this case, we will need to scroll down until the new window
* position is at the top of the display.
*/
else if (vi->winpos < vi->prevpos)
{
for (nlines = 0, pos = vi->prevpos;
pos != vi->winpos && nlines < vi->display.row - 1;
nlines++)
{
pos = vi_prevline(vi, pos);
}
if (nlines < vi->display.row - 1)
{
vi_scrolldown(vi, nlines);
vi->fullredraw = true;
}
}
* This can be modified asynchronously by text deletion operations.
*/
vi->prevpos = vi->winpos;
viinfo("winpos=%ld hscroll=%d\n",
(long)vi->winpos, (long)vi->hscroll);
}
* Name: vi_showtext
*
* Description:
* Update the display based on the last operation. This function is
* called at the beginning of the processing loop in Command and Insert
* modes (and also in the continuous replace mode).
*
****************************************************************************/
static void vi_showtext(FAR struct vi_s *vi)
{
off_t pos;
off_t writefrom;
uint16_t row;
uint16_t endrow;
uint16_t column;
uint16_t endcol;
uint16_t tabcol;
bool redraw_line;
* scroll.
*/
vi_scrollcheck(vi);
if (!vi->fullredraw && !vi->drawtoeos && !vi->redrawline)
{
return;
}
* do not update the last line.
*/
endrow = vi->display.row - 1;
* cursor during the update.
*/
vi_attriboff(vi);
vi_cursoroff(vi);
if (vi->fullredraw)
{
pos = vi->winpos;
row = 0;
vi->drawtoeos = true;
vi->redrawline = true;
}
else
{
pos = vi_linebegin(vi, vi->curpos);
row = vi->cursor.row;
if (vi->drawtoeos)
{
vi->redrawline = true;
}
else
{
endrow = row + 1;
}
}
* tab expansion.
*/
for (; pos < vi->textsize && row < endrow; row++)
{
redraw_line = true;
if (!vi->redrawline)
{
redraw_line = false;
}
else if (row + 1 < vi->cursor.row && !vi->fullredraw)
{
redraw_line = false;
}
else if (row > vi->cursor.row && !vi->drawtoeos)
{
redraw_line = false;
}
else if (row == vi->cursor.row && !vi->redrawline)
{
redraw_line = false;
}
* on the screen which may trigger a scroll.
*/
endcol = vi->display.column;
if (row >= vi->display.row - 1)
{
endcol--;
}
* accounting for horizontal scrolling and tab expansion. Add that to
* the line start offset to get the first offset to consider for
* display.
*/
vi_windowpos(vi, pos, pos + vi->hscroll, NULL, &pos);
* the end of the line.
*/
if (redraw_line)
{
vi_setcursor(vi, row, 0);
writefrom = pos;
for (column = 0; pos < vi->textsize && column < endcol; pos++)
{
* last column is encountered.
*/
if (vi->text[pos] == '\n')
{
break;
}
else if (vi->text[pos] == '\t')
{
if (writefrom != pos)
{
vi_write(vi, &vi->text[writefrom], pos - writefrom);
}
tabcol = NEXT_TAB(column);
if (tabcol < endcol)
{
for (; column < tabcol; column++)
{
vi_putch(vi, ' ');
}
writefrom = pos + 1;
}
else
{
* the line but whitespace.
*/
writefrom = pos;
break;
}
}
else
{
column++;
}
}
if (writefrom != pos)
{
vi_write(vi, &vi->text[writefrom], pos - writefrom);
}
vi_clrtoeol(vi);
}
pos = vi_nextline(vi, pos);
}
if (pos == vi->textsize && vi->text[pos - 1] == '\n')
{
vi_setcursor(vi, row, 0);
vi_clrtoeol(vi);
row++;
}
if (vi->drawtoeos)
{
* remaining lines (except for any possible error line at the
* bottom of the display).
*/
for (; row < endrow; row++)
{
* the end of the line.
*/
vi_setcursor(vi, row, 0);
if (row != endrow && row != 0)
{
vi_putch(vi, '~');
}
vi_clrtoeol(vi);
}
}
vi_cursoron(vi);
vi->fullredraw = false;
vi->drawtoeos = false;
vi->redrawline = false;
}
* Name: vi_showlinecol
*
* Description:
* Update the current line/column on the display status line.
*
****************************************************************************/
static void vi_showlinecol(FAR struct vi_s *vi)
{
size_t len;
vi_cursoroff(vi);
vi_setcursor(vi, vi->display.row - 1, vi->display.column - 15);
len = snprintf(vi->scratch, sizeof(vi->scratch), "%jd,%d",
(uintmax_t)(vi->cursor.row + vi->vscroll + 1),
vi->cursor.column + vi->hscroll + 1);
vi_write(vi, vi->scratch, MIN(len, sizeof(vi->scratch)));
vi_clrtoeol(vi);
vi_cursoron(vi);
}
* Command Mode Functions
****************************************************************************/
* Name: vi_cusorup
*
* Description:
* Move the cursor up one line in the text buffer.
*
****************************************************************************/
static void vi_cusorup(FAR struct vi_s *vi, int nlines)
{
int remaining;
off_t start;
off_t end;
viinfo("nlines=%d\n", nlines);
remaining = (nlines < 1 ? 1 : nlines);
start = vi_linebegin(vi, vi->curpos);
for (; remaining > 0; remaining--)
{
start = vi_prevline(vi, start);
* cursor position on the current line.
*/
end = start + vi->reqcolumn;
vi_windowpos(vi, start, end, NULL, &vi->curpos);
}
}
* Name: vi_cursordown
*
* Description:
* Move the cursor down one line in the text buffer
*
****************************************************************************/
static void vi_cursordown(FAR struct vi_s *vi, int nlines)
{
int remaining;
off_t start;
off_t end;
viinfo("nlines=%d\n", nlines);
remaining = (nlines < 1 ? 1 : nlines);
start = vi_linebegin(vi, vi->curpos);
for (; remaining > 0; remaining--)
{
start = vi_nextline(vi, start);
* cursor position on the current line.
*/
end = start + vi->reqcolumn;
vi_windowpos(vi, start, end, NULL, &vi->curpos);
}
}
* Name: vi_cursorleft
*
* Description:
* Move the cursor left 'ncolumns' columns in the text buffer (without
* moving to the preceding line). Note that a repetition count of 0 means
* to perform the movement once.
*
****************************************************************************/
static off_t vi_cursorleft(FAR struct vi_s *vi, off_t curpos, int ncolumns)
{
int remaining;
viinfo("curpos=%ld ncolumns=%d\n", curpos, ncolumns);
* out early if we hit either the beginning of the text buffer, or the end
* of the previous line.
*/
for (remaining = (ncolumns < 1 ? 1 : ncolumns);
curpos > 0 && remaining > 0 && vi->text[curpos - 1] != '\n';
curpos--, remaining--)
{
}
return curpos;
}
* Name: vi_cursorright
*
* Description:
* Move the cursor right 'ncolumns' columns in the text buffer (without
* moving to the next line). Note that a repetition count of 0 means to
* perform the movement once.
*
****************************************************************************/
static off_t vi_cursorright(FAR struct vi_s *vi, off_t curpos, int ncolumns)
{
int remaining;
viinfo("curpos=%ld ncolumns=%d\n", curpos, ncolumns);
* out early if we hit either the end of the text buffer, or the end of
* the line.
*/
for (remaining = (ncolumns < 1 ? 1 : ncolumns);
curpos < vi->textsize && remaining > 0 && vi->text[curpos] != '\n';
curpos++, remaining--)
{
}
#if 0
if (vi->text[curpos] == '\n' || (curpos == vi->textsize &&
vi->mode != MODE_INSERT && vi->mode != MODE_REPLACE))
{
curpos--;
}
#endif
return curpos;
}
* Name: vi_gotoscreenbottom
*
* Description:
* Move the cursor to the bottom of the screen or the bottom line of
* the file if it doesn't occupy the entire screen.
*
****************************************************************************/
static int vi_gotoscreenbottom(FAR struct vi_s *vi, int rows)
{
off_t pos;
int row;
int target = rows > 0 ? rows >> 1 : vi->display.row - 2;
vi->curpos = vi->winpos;
row = 0;
while (row < target)
{
pos = vi_nextline(vi, vi->curpos);
if (pos == vi->curpos)
{
break;
}
if (pos == vi->textsize)
{
row++;
vi->curpos = pos;
break;
}
vi->curpos = pos;
row++;
}
return row;
}
* Name: vi_gotofirstnonwhite
*
* Description:
* Move the cursor to the first non-whitespace character on the
* current line.
*
****************************************************************************/
static void vi_gotofirstnonwhite(FAR struct vi_s *vi)
{
vi->curpos = vi_linebegin(vi, vi->curpos);
while (vi->curpos <= vi->textsize && (vi->text[vi->curpos] == ' ' ||
vi->text[vi->curpos] == '\t'))
{
vi->curpos++;
}
}
* Name: vi_delforward
*
* Description:
* Delete characters from the current cursor position forward
*
****************************************************************************/
static void vi_delforward(FAR struct vi_s *vi)
{
off_t start;
off_t end;
bool at_end = false;
viinfo("curpos=%ld value=%ld\n", (long)vi->curpos, vi->value);
if (vi->textsize == 0)
{
return;
}
if (vi->cursor.column == 0)
{
if (vi->curpos == vi->textsize ||
vi->text[vi->curpos] == '\n')
{
return;
}
}
* times (which might be <N characters).
*/
end = vi_cursorright(vi, vi->curpos, vi->value);
if (end == vi->curpos && vi->cursor.column > 0)
{
end++;
at_end = true;
}
* characters to be deleted.
*/
start = vi->curpos;
vi_yanktext(vi, start, end - 1, true, true);
vi->curpos = start;
if (at_end)
{
vi->curpos--;
}
vi->redrawline = true;
}
* Name: vi_delbackward
*
* Description:
* Delete characters before the current cursor position
*
****************************************************************************/
static void vi_delbackward(FAR struct vi_s *vi)
{
off_t start;
off_t end;
off_t x;
viinfo("curpos=%ld value=%ld\n", (long)vi->curpos, vi->value);
if (vi->curpos == 0 || vi->text[vi->curpos] == '\n' ||
vi->text[vi->curpos - 1] == '\n')
{
return;
}
end = vi_cursorleft(vi, vi->curpos, 1);
* times (which might be <N characters).
*/
if (vi->value > 1)
{
start = vi_cursorleft(vi, end, vi->value -1);
}
else
{
start = end;
}
for (x = end; x >= start; x--)
{
if (vi->text[x] == '\n')
{
start = x + 1;
break;
}
}
* characters to be deleted.
*/
vi_yanktext(vi, start, end, true, true);
vi->redrawline = true;
}
* Name: vi_linerange
*
* Description:
* Return the start and end positions for N lines in the text buffer,
* beginning at the current line. This is logic common to yanking and
* deleting lines.
*
****************************************************************************/
static void vi_linerange(FAR struct vi_s *vi, off_t *start, off_t *end)
{
off_t next;
int nlines;
*start = vi_linebegin(vi, vi->curpos);
nlines = (vi->value > 0 ? vi->value : 1);
for (next = *start; nlines > 1; nlines--)
{
next = vi_nextline(vi, next);
}
*end = vi_lineend(vi, next);
if (*end != vi->textsize)
{
(*end)++;
}
}
* Name: vi_delline
*
* Description:
* Delete N lines from the text buffer, beginning at the current line.
*
****************************************************************************/
static void vi_delline(FAR struct vi_s *vi)
{
vi_yank(vi, true);
vi->drawtoeos = true;
}
* Name: vi_deltoeol
*
* Description:
* Delete to end of line.
*
****************************************************************************/
static void vi_deltoeol(FAR struct vi_s *vi)
{
int start;
int end;
if (vi->curpos == vi->textsize || vi->text[vi->curpos] == '\n')
{
return;
}
start = vi->curpos;
end = vi_lineend(vi, vi->curpos);
if (end == vi->textsize || vi->text[end] == '\n')
{
end--;
}
vi_yanktext(vi, start, end, true, true);
if (start > 0 && start != vi->textsize && vi->text[start - 1] != '\n')
{
vi->curpos = start - 1;
}
else
{
vi->curpos = start;
}
vi->redrawline = true;
}
* Name: vi_yanktext
*
* Description:
* Yank specified text from the text buffer and delete if requested.
*
****************************************************************************/
static void vi_yanktext(FAR struct vi_s *vi, off_t start, off_t end,
bool yankcharmode, bool del_after_yank)
{
int append_lf = 0;
size_t alloc;
size_t size;
if (vi->text[end] != '\n' && !yankcharmode)
{
append_lf = 1;
}
size = end - start + 1;
alloc = size + append_lf;
if (alloc < CONFIG_SYSTEM_VI_YANK_THRESHOLD)
{
alloc = CONFIG_SYSTEM_VI_YANK_THRESHOLD;
}
if (vi->yank)
{
* than the YANK_THRESHOLD and we need less than that.
*/
if (alloc > vi->yankalloc ||
(alloc == CONFIG_SYSTEM_VI_YANK_THRESHOLD &&
vi->yankalloc > CONFIG_SYSTEM_VI_YANK_THRESHOLD))
{
free(vi->yank);
vi->yank = NULL;
}
}
if (!vi->yank)
{
vi->yankalloc = alloc;
vi->yank = (FAR char *)malloc(vi->yankalloc);
}
vi->yankcharmode = yankcharmode;
if (!vi->yank)
{
vi_error(vi, g_fmtallocfail);
vi->yankalloc = 0;
vi->yanksize = 0;
return;
}
vi->yanksize = size;
memcpy(vi->yank, &vi->text[start], size);
if (append_lf > 0)
{
vi->yank[vi->yanksize] = '\n';
}
if (del_after_yank)
{
vi_shrinktext(vi, start, vi->yanksize);
}
vi->yanksize += append_lf;
}
* Name: vi_yank
*
* Description:
* Remove N lines from the text buffer, beginning at the current line and
* copy the lines to an allocated yank buffer.
*
****************************************************************************/
static void vi_yank(FAR struct vi_s *vi, bool del_after_yank)
{
off_t start;
off_t end;
off_t yank_end;
off_t textsize;
int pos_increment = 0;
bool empty_last_line = false;
* be yanked
*/
vi_linerange(vi, &start, &end);
textsize = vi->textsize;
if (end >= textsize)
{
end = textsize - 1;
}
if (start >= textsize)
{
start = textsize -1;
}
yank_end = end;
if (del_after_yank && end == textsize - 1 && start != end &&
vi->text[end] == '\n')
{
yank_end--;
pos_increment = 1;
}
viinfo("start=%ld end=%ld\n", (long)start, (long)end);
if ((end > 0 && start == end && end == vi->textsize -1 &&
vi->text[end - 1] == '\n') || (start > 1 && end + 1 ==
vi->textsize && vi->text[start - 2] == '\n'))
{
empty_last_line = true;
}
vi_yanktext(vi, start, yank_end, 0, del_after_yank);
* previous line.
*/
if (end + 1 == textsize && start != end && del_after_yank)
{
vi_shrinktext(vi, vi->textsize - 1, 1);
}
if (del_after_yank)
{
if (empty_last_line)
{
vi->curpos = vi->textsize;
}
else
{
vi->curpos = vi_linebegin(vi, vi->curpos + pos_increment);
}
}
}
* Name: vi_paste
*
* Description:
* Copy line(s) from the yank buffer, and past them after the current line.
* The contents of the yank buffer are released.
*
****************************************************************************/
static void vi_paste(FAR struct vi_s *vi, bool paste_before)
{
off_t start;
off_t new_curpos;
int count;
viinfo("curpos=%ld yankalloc=%d\n", (long)vi->curpos, (long)vi->yankalloc);
if (!vi->yank || vi->yanksize <= 0)
{
return;
}
count = vi->value > 0 ? vi->value : 1;
if (count > 1)
{
vi->fullredraw = true;
}
while (count > 0)
{
if (vi->yankcharmode)
{
off_t pos;
if (vi->text[vi->curpos] == '\n' || vi->curpos == vi->textsize ||
paste_before)
{
pos = vi->curpos;
}
else
{
pos = vi->curpos + 1;
}
if (vi_extendtext(vi, pos, vi->yanksize))
{
* at the position where the start of the next line was.
*/
memcpy(&vi->text[pos], vi->yank, vi->yanksize);
vi->curpos = vi->curpos + vi->yanksize;
if (vi->curpos > vi->textsize || vi->text[vi->curpos] == '\n')
{
vi->curpos--;
}
}
}
else
{
off_t size;
if (paste_before)
{
start = vi_linebegin(vi, vi->curpos);
}
else
{
start = vi_nextline(vi, vi->curpos);
}
size = vi->yanksize;
new_curpos = start;
if ((start >= vi->textsize && vi->text[vi->textsize - 1] != '\n')
|| vi->curpos == vi->textsize)
{
off_t textsize = vi->textsize;
bool at_end = vi->curpos == vi->textsize;
vi->curpos = vi->textsize;
vi_insertch(vi, '\n');
start = vi->textsize;
new_curpos = start;
if (vi->text[textsize - 1] != '\n' || at_end)
{
size--;
}
}
else if (start >= vi->textsize)
{
start = vi->textsize;
new_curpos = start;
vi->fullredraw = true;
}
* the beginning of the next line.
*/
if (vi_extendtext(vi, start, size))
{
* at the position where the start of the next line was.
*/
memcpy(&vi->text[start], vi->yank, size);
vi->curpos = new_curpos;
}
}
count--;
}
vi->drawtoeos = true;
}
* Name: vi_join
*
* Description:
* Join line below with current line.
*
****************************************************************************/
static void vi_join(FAR struct vi_s *vi)
{
off_t start;
off_t end;
if (vi->curpos + 1 >= vi->textsize)
{
return;
}
start = vi_lineend(vi, vi->curpos);
if (vi->text[start + 1] != '\n')
{
return;
}
vi->text[++start] = ' ';
end = start + 1;
while ((vi->text[end] == ' ' || vi->text[end] == '\t') &&
end < vi->textsize)
{
end++;
}
if (start + 1 != end)
{
vi_shrinktext(vi, start + 1, end - (start + 1));
}
vi->curpos = start;
vi->drawtoeos = true;
}
* Name: vi_gotoline
*
* Description:
* Position the cursor at the line specified by vi->value. If
* vi->value is zero, then the cursor is position at the end of the text
* buffer.
*
****************************************************************************/
static void vi_gotoline(FAR struct vi_s *vi)
{
viinfo("curpos=%ld value=%ld\n", (long)vi->curpos, vi->value);
if (vi->value == 1)
{
vi->curpos = 0;
}
else if (vi->value > 0)
{
uint32_t line;
for (line = vi->value, vi->curpos = 0;
--line > 0 && vi->curpos < vi->textsize;
)
{
vi->curpos = vi_nextline(vi, vi->curpos);
}
}
else
{
vi->curpos = vi_linebegin(vi, vi->textsize);
}
vi->fullredraw = true;
}
* Name: vi_chartype
*
* Description:
* Determine and return the type of character (i.e. alpha, space,
* punctuation).
*
****************************************************************************/
static int vi_chartype(char ch)
{
int type;
if (ch == ' ' || ch == '\t')
{
type = VI_CHAR_SPACE;
}
else if (isalnum(ch) || ch == '_')
{
type = VI_CHAR_ALPHA;
}
else if (ch == '\r' || ch == '\n')
{
type = VI_CHAR_CRLF;
}
else
{
type = VI_CHAR_PUNCT;
}
return type;
}
* Name: vi_findnextword
*
* Description:
* Find the position in the text buffer of the start of the next word.
*
****************************************************************************/
static off_t vi_findnextword(FAR struct vi_s *vi)
{
int srch_type;
int pos_type;
off_t pos;
* next "word" looks like.
*/
srch_type = vi_chartype(vi->text[vi->curpos]);
pos = vi->curpos + 1;
for (; pos < vi->textsize; pos++)
{
pos_type = vi_chartype(vi->text[pos]);
if (pos_type == VI_CHAR_CRLF)
{
srch_type = VI_CHAR_SPACE;
continue;
}
if (srch_type == VI_CHAR_SPACE &&
pos_type != VI_CHAR_SPACE)
{
break;
}
if (srch_type == VI_CHAR_ALPHA &&
pos_type == VI_CHAR_PUNCT)
{
break;
}
if (srch_type == VI_CHAR_PUNCT &&
pos_type == VI_CHAR_ALPHA)
{
break;
}
* to space so we find whatever is next.
*/
if ((srch_type == VI_CHAR_ALPHA || srch_type == VI_CHAR_PUNCT) &&
pos_type == VI_CHAR_SPACE)
{
srch_type = VI_CHAR_SPACE;
}
}
if (pos == vi->textsize)
{
pos--;
}
return pos;
}
* Name: vi_gotonextword
*
* Description:
* Position the cursor at the start of the next word.
*
****************************************************************************/
static void vi_gotonextword(FAR struct vi_s *vi)
{
int count;
int x;
off_t start = vi->curpos;
off_t end;
off_t pos;
bool crfound;
count = vi->value > 0 ? vi->value : 1;
for (x = 0; x < count; x++ )
{
vi->curpos = vi_findnextword(vi);
}
if (vi->yankarm || vi->delarm || vi->chgarm)
{
pos = vi->curpos;
crfound = false;
while ((vi->text[pos - 1] == ' ' || vi->text[pos - 1] == '\t' ||
vi->text[pos - 1] == '\n') && pos > start)
{
pos--;
if (vi->text[pos] == '\n')
{
crfound = true;
}
}
if (crfound)
{
vi->curpos = pos;
}
* doesn't contain any '\n' characters.
*/
if (count == 1)
{
for (x = start; x < vi->curpos; x++)
{
if (vi->text[x] == '\n')
{
vi->curpos = x;
break;
}
}
}
else
{
vi->fullredraw = vi->delarm || vi->chgarm;
}
end = vi->curpos + 1 == vi->textsize ? vi->curpos : vi->curpos - 1;
if (vi->chgarm)
{
end--;
}
if (!(start == end && vi->text[start] == '\n'))
{
vi_yanktext(vi, start, end, 1, vi->delarm | vi->chgarm);
}
if (vi->delarm | vi->chgarm)
{
vi->redrawline = true;
}
vi->curpos = start;
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
if (vi->delarm | vi->chgarm)
{
vi_saverepeat(vi, vi->delarm ? 'd' : 'c');
vi_appendrepeat(vi, 'w');
}
#endif
if (vi->chgarm)
{
vi_setmode(vi, MODE_INSERT, 0);
}
vi->delarm = false;
vi->chgarm = false;
vi->yankarm = false;
}
}
* Name: vi_findprevword
*
* Description:
* Find the position in the text buffer of the start of the previous word.
*
****************************************************************************/
static off_t vi_findprevword(FAR struct vi_s *vi)
{
int srch_type;
int pos_type;
off_t pos;
* is simple.
*/
if (vi->curpos < 2)
{
return 0;
}
* next "word" looks like.
*/
srch_type = vi_chartype(vi->text[vi->curpos]);
pos = vi->curpos - 1;
pos_type = vi_chartype(vi->text[pos]);
if (srch_type == pos_type)
{
while (pos > 0)
{
pos_type = vi_chartype(vi->text[pos - 1]);
if (pos_type != srch_type && pos_type != VI_CHAR_CRLF)
{
break;
}
pos--;
}
if ((srch_type != VI_CHAR_SPACE && srch_type != VI_CHAR_CRLF) ||
pos == 0)
{
return pos;
}
* non-space character.
*/
pos_type = vi_chartype(vi->text[--pos]);
}
while ((pos_type == VI_CHAR_SPACE || pos_type == VI_CHAR_CRLF) && pos > 0)
{
pos_type = vi_chartype(vi->text[--pos]);
}
if (pos == 0)
{
return pos;
}
srch_type = vi_chartype(vi->text[pos]);
while (pos > 0 && vi_chartype(vi->text[pos - 1]) == srch_type)
{
pos--;
}
return pos;
}
* Name: vi_gotoprevword
*
* Description:
* Position the cursor at the start of the previous word.
*
****************************************************************************/
static void vi_gotoprevword(FAR struct vi_s *vi)
{
int count;
count = vi->value > 0 ? vi->value : 1;
while (count > 0)
{
vi->curpos = vi_findprevword(vi);
count--;
}
}
* Name: vi_bottom_line_debug
*
* Description:
* Print text and paste buffers on bottom line for debug purposes
*
****************************************************************************/
#ifdef ENABLE_BOTTOM_LINE_DEBUG
static void vi_bottom_line_debug(FAR struct vi_s *vi)
{
off_t pos;
int column;
vi_clearbottomline(vi);
vi_putch(vi, '"');
pos = 0;
column = 0;
while (pos < vi->textsize && column < vi->display.column)
{
if (vi->text[pos] == '\n')
{
vi_putch(vi, '\\');
vi_putch(vi, 'n');
}
else if (vi->text[pos] == '\t')
{
vi_putch(vi, '\\');
vi_putch(vi, 'n');
}
else
{
vi_putch(vi, vi->text[pos]);
}
pos++;
column++;
}
vi_putch(vi, '"');
if (!vi->yank)
{
return;
}
vi_putch(vi, ' ');
vi_putch(vi, '"');
pos = 0;
while (pos < vi->yanksize && column < vi->display.column)
{
if (vi->yank[pos] == '\n')
{
vi_putch(vi, '\\');
vi_putch(vi, 'n');
}
else if (vi->yank[pos] == '\t')
{
vi_putch(vi, '\\');
vi_putch(vi, 'n');
}
else
{
vi_putch(vi, vi->yank[pos]);
}
pos++;
}
vi_putch(vi, '"');
}
#endif
* Name: vi_findnext
*
* Description:
* Perform find operation again in forward direction
*
****************************************************************************/
void vi_findnext(FAR struct vi_s *vi)
{
if (vi->curpos < vi->textsize)
{
vi->curpos++;
}
if (!vi_findstring(vi))
{
vi->curpos--;
VI_BEL(vi);
}
}
* Name: vi_findprev
*
* Description:
* Perform find operation again in reverse direction
*
****************************************************************************/
void vi_findprev(FAR struct vi_s *vi)
{
off_t pos = vi->curpos;
if (vi->curpos > 0)
{
vi->curpos--;
}
else
{
vi->curpos = vi->textsize - strlen(vi->findstr);
}
if (!vi_revfindstring(vi))
{
vi->curpos = pos;
}
}
* Name: vi_saverepeat
*
* Description:
* Save ch as the first command repeat entry.
*
****************************************************************************/
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
static void vi_saverepeat(FAR struct vi_s *vi, uint16_t ch)
{
if (vi->cmdrepeat)
{
return;
}
vi->cmdcount = 0;
vi->cmdindex = 0;
if (ch < 256)
{
vi->cmdbuf[vi->cmdcount++] = ch;
vi->repeatvalue = vi->value;
}
}
#endif
* Name: vi_appendrepeat
*
* Description:
* Save ch as the next command repeat entry.
*
****************************************************************************/
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
static void vi_appendrepeat(FAR struct vi_s *vi, uint16_t ch)
{
if (vi->cmdrepeat || vi->cmdcount == 0 || ch > 255)
{
return;
}
if (vi->cmdcount < CMD_BUFSIZE)
{
vi->cmdbuf[vi->cmdcount++] = ch;
}
}
#endif
* Name: vi_cmd_mode
*
* Description:
* Command mode loop
*
****************************************************************************/
static void vi_cmd_mode(FAR struct vi_s *vi)
{
viinfo("Enter command mode\n");
while (vi->mode == MODE_COMMAND)
{
bool preserve;
int ch;
vi_showtext(vi);
vi_showlinecol(vi);
vi_setcursor(vi, vi->cursor.row, vi->cursor.column);
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
if (vi->cmdrepeat && vi->cmdindex == vi->cmdcount)
{
vi->cmdrepeat = false;
}
if (vi->cmdrepeat)
{
ch = vi->cmdbuf[vi->cmdindex++];
}
else
#endif
{
ch = vi_getch(vi);
}
* special case: It means to go to the beginning o the line.
*/
if (isdigit(ch) && (vi->value > 0 || ch != '0'))
{
uint32_t tmp = 10 * vi->value + (ch - '0');
if (tmp > UINT16_MAX)
{
tmp = UINT16_MAX;
}
vi->value = tmp;
viinfo("Value=%ld\n", vi->value);
continue;
}
if (ch != 'f' && ch != 't' && ch != 'w')
{
if (ch != 'd')
{
vi->delarm = false;
}
if (ch != 'y')
{
vi->yankarm = false;
}
if (ch != 'c')
{
vi->chgarm = false;
}
}
if (ch != 'g')
{
vi->toparm = false;
}
if (ch != 'Z')
{
vi->wqarm = false;
}
if (vi->textsize == 0)
{
* text insertion commands.
*/
if (ch != KEY_CMDMODE_APPEND && ch != KEY_CMDMODE_INSERT &&
ch != KEY_CMDMODE_OPENBELOW && ch != KEY_CMDMODE_APPENDEND &&
ch != KEY_CMDMODE_INSBEGIN && ch != KEY_CMDMODE_OPENABOVE &&
ch != KEY_CMDMODE_COLONMODE)
{
continue;
}
}
vi->error = false;
* value will be reset after processing the command. There are a few
* exceptions; 'preserve' will be set to 'true' in those exceptional
* cases.
*/
preserve = false;
vi->updatereqcol = true;
switch (ch)
{
case KEY_CMDMODE_UP:
case KEY_UP:
{
vi->updatereqcol = false;
vi_cusorup(vi, vi->value);
}
break;
case KEY_CMDMODE_DOWN:
case KEY_DOWN:
{
vi->updatereqcol = false;
vi_cursordown(vi, vi->value);
}
break;
case KEY_CMDMODE_LEFT:
case KEY_LEFT:
{
vi->curpos = vi_cursorleft(vi, vi->curpos, vi->value);
}
break;
case KEY_CMDMODE_RIGHT:
case KEY_RIGHT:
{
if (vi->text[vi->curpos] != '\n' &&
vi->text[vi->curpos + 1] != '\n')
{
vi->curpos = vi_cursorright(vi, vi->curpos, vi->value);
if (vi->curpos >= vi->textsize)
{
vi->curpos = vi->textsize - 1;
}
}
}
break;
case KEY_CMDMODE_BEGINLINE:
case KEY_HOME:
{
vi->curpos = vi_linebegin(vi, vi->curpos);
}
break;
case KEY_CMDMODE_ENDLINE:
case KEY_END:
{
vi->curpos = vi_lineend(vi, vi->curpos);
vi->reqcolumn = 65535;
vi->updatereqcol = false;
}
break;
case KEY_CMDMODE_PAGEUP:
case KEY_PPAGE:
{
vi->updatereqcol = false;
vi_cusorup(vi, vi->display.row);
}
break;
case KEY_CMDMODE_PAGEDOWN:
case KEY_NPAGE:
{
vi->updatereqcol = false;
vi_cursordown(vi, vi->display.row);
}
break;
case KEY_CMDMODE_HALFUP:
{
vi->updatereqcol = false;
vi_cusorup(vi, vi->display.row >> 1);
}
break;
case KEY_CMDMODE_HALFDOWN:
{
vi->updatereqcol = false;
vi_cursordown(vi, vi->display.row >> 1);
}
break;
case KEY_CMDMODE_TOP:
{
vi->curpos = vi->winpos;
}
break;
case KEY_CMDMODE_BOTTOM:
{
vi_gotoscreenbottom(vi, 0);
}
break;
case KEY_CMDMODE_MIDDLE:
{
off_t pos = vi_gotoscreenbottom(vi, 0);
vi_gotoscreenbottom(vi, pos);
}
break;
case KEY_CMDMODE_FIRSTCHAR:
{
vi_gotofirstnonwhite(vi);
}
break;
case KEY_CMDMODE_GOTOTOP:
{
if (vi->toparm)
{
vi->curpos = 0;
vi->redrawline = true;
vi->toparm = false;
}
else
{
vi->toparm = true;
}
}
break;
case KEY_CMDMODE_FINDNEXT:
{
if (vi->revfind)
{
vi_findprev(vi);
}
else
{
vi_findnext(vi);
}
break;
}
case KEY_CMDMODE_FINDPREV:
{
if (vi->revfind)
{
vi_findnext(vi);
}
else
{
vi_findprev(vi);
}
break;
}
break;
case ASCII_BS:
{
if (vi->curpos > 0)
{
vi->curpos--;
if (vi->curpos > 0 && vi->text[vi->curpos] == '\n')
{
vi->curpos--;
}
}
}
break;
case KEY_CMDMODE_DEL_LINE:
{
if (vi->delarm)
{
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
vi_saverepeat(vi, ch);
vi_appendrepeat(vi, ch);
#endif
vi_delline(vi);
vi->delarm = false;
}
else
{
vi->delarm = true;
preserve = true;
}
}
break;
case KEY_CMDMODE_CHANGE:
{
if (vi->chgarm)
{
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
vi_saverepeat(vi, ch);
vi_appendrepeat(vi, ch);
#endif
vi_gotofirstnonwhite(vi);
vi_deltoeol(vi);
vi_setmode(vi, MODE_INSERT, 0);
if (vi->curpos == vi->textsize)
{
vi->curpos = vi_cursorright(vi, vi->curpos, 1) + 1;
}
else
{
vi->curpos = vi_cursorright(vi, vi->curpos, 1);
}
vi->chgarm = false;
}
else
{
vi->chgarm = true;
preserve = true;
}
}
break;
case KEY_CMDMODE_DELTOEOL:
{
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
vi_saverepeat(vi, ch);
#endif
vi_deltoeol(vi);
}
break;
case KEY_CMDMODE_DELBACKWARD:
{
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
vi_saverepeat(vi, ch);
#endif
vi_delbackward(vi);
}
break;
case KEY_CMDMODE_YANK:
{
if (vi->yankarm)
{
vi_yank(vi, false);
vi->yankarm = false;
}
else
{
vi->yankarm = true;
preserve = true;
}
}
break;
case KEY_CMDMODE_PASTE:
{
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
vi_saverepeat(vi, ch);
#endif
vi_paste(vi, false);
}
break;
case KEY_CMDMODE_PASTEBEFORE:
{
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
vi_saverepeat(vi, ch);
#endif
vi_paste(vi, true);
}
break;
case KEY_CMDMODE_REPLACECH:
{
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
vi_saverepeat(vi, ch);
#endif
vi_setmode(vi, SUBMODE_REPLACECH, vi->value);
preserve = true;
}
break;
case KEY_CMDMODE_REPLACE:
{
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
vi_saverepeat(vi, ch);
#endif
vi_setmode(vi, MODE_REPLACE, 0);
}
break;
case KEY_CMDMODE_FINDINLINE:
case KEY_CMDMODE_TFINDINLINE:
{
vi->tfind = ch == KEY_CMDMODE_TFINDINLINE;
vi->mode = MODE_FINDINLINE;
preserve = true;
}
break;
case KEY_CMDMODE_OPENBELOW:
{
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
vi_saverepeat(vi, ch);
#endif
vi_setmode(vi, MODE_INSERT, 0);
vi->curpos = vi_lineend(vi, vi->curpos);
if (vi->curpos != vi->textsize)
{
vi->curpos++;
}
* beginning of the new line.
*/
vi_insertch(vi, '\n');
vi->drawtoeos = true;
}
break;
case KEY_CMDMODE_OPENABOVE:
{
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
vi_saverepeat(vi, ch);
#endif
off_t pos = vi_linebegin(vi, vi->curpos);
if (pos == 0)
{
* line.
*/
vi->curpos = 0;
vi_insertch(vi, '\n');
vi->curpos = vi_prevline(vi, vi->curpos);
}
else
{
* point to thebeginning of newly openly line before the
* current line.
*/
pos = vi_prevline(vi, pos);
vi->curpos = vi_lineend(vi, pos)+1;
vi_insertch(vi, '\n');
}
vi_setmode(vi, MODE_INSERT, 0);
vi->drawtoeos = true;
}
break;
case KEY_CMDMODE_CHANGETOEOL:
{
vi_deltoeol(vi);
}
case KEY_CMDMODE_APPEND:
* cursor position */
{
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
vi_saverepeat(vi, ch);
#endif
vi_setmode(vi, MODE_INSERT, 0);
if (vi->curpos == vi->textsize)
{
vi->curpos = vi_cursorright(vi, vi->curpos, 1) + 1;
}
else
{
vi->curpos = vi_cursorright(vi, vi->curpos, 1);
}
}
break;
case KEY_CMDMODE_APPENDEND:
{
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
vi_saverepeat(vi, ch);
#endif
vi_setmode(vi, MODE_INSERT, 0);
vi->curpos = vi_lineend(vi, vi->curpos) + 1;
}
break;
case KEY_CMDMODE_SUBSTITUTE:
case KEY_CMDMODE_DEL:
case KEY_DC:
case ASCII_DEL:
{
off_t pos = vi->curpos;
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
vi_saverepeat(vi, ch);
#endif
if (vi->text[pos] == '\n')
{
break;
}
else if (pos + 1 != vi->textsize && vi->text[pos + 1] == '\n')
{
if (pos > 0)
{
vi_delforward(vi);
vi->curpos = vi_cursorleft(vi, vi->curpos, 1);
}
}
else
{
vi_delforward(vi);
vi->redrawline = true;
}
}
if (ch == KEY_CMDMODE_SUBSTITUTE)
{
vi_setmode(vi, MODE_INSERT, 0);
}
break;
case KEY_CMDMODE_INSBEGIN:
{
vi->curpos = vi_linebegin(vi, vi->curpos);
}
case KEY_CMDMODE_INSERT:
{
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
vi_saverepeat(vi, ch);
#endif
vi_setmode(vi, MODE_INSERT, 0);
}
break;
case KEY_CMDMODE_JOIN:
{
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
vi_saverepeat(vi, ch);
#endif
vi_join(vi);
}
break;
case KEY_CMDMODE_COLONMODE:
{
vi->updatereqcol = false;
vi_setsubmode(vi, SUBMODE_COLON, ':', 0);
}
break;
case KEY_CMDMODE_SAVEQUIT:
{
if (vi->wqarm)
{
strlcpy(vi->scratch, "wq", sizeof(vi->scratch));
vi->cmdlen = 2;
vi_parsecolon(vi);
}
else
{
vi->wqarm = true;
}
}
break;
case KEY_CMDMODE_FINDMODE:
{
vi->updatereqcol = false;
vi_setsubmode(vi, SUBMODE_FIND, '/', 0);
}
break;
case KEY_CMDMODE_REVFINDMODE:
{
vi->updatereqcol = false;
vi_setsubmode(vi, SUBMODE_REVFIND, '?', 0);
}
break;
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
case KEY_CMDMODE_REPEAT:
{
if (vi->cmdcount < CMD_BUFSIZE)
{
vi->cmdindex = 0;
vi->cmdrepeat = true;
vi->value = vi->value > 0 ? vi->value : vi->repeatvalue > 0 ?
vi->repeatvalue : 1;
preserve = true;
}
else
{
VI_BEL(vi);
}
}
break;
#endif
case KEY_CMDMODE_GOTO:
{
vi_gotoline(vi);
}
break;
case KEY_CMDMODE_WORDFWD:
{
vi_gotonextword(vi);
}
break;
case KEY_CMDMODE_WORDBACK:
{
vi_gotoprevword(vi);
}
break;
case KEY_CMDMODE_NEXTLINE:
case '\n':
{
vi->curpos = vi_nextline(vi, vi->curpos);
vi_gotofirstnonwhite(vi);
}
break;
case KEY_CMDMODE_PREVLINE:
{
vi->curpos = vi_prevline(vi, vi->curpos);
vi_gotofirstnonwhite(vi);
}
break;
case KEY_CMDMODE_REDRAW:
case KEY_CMDMODE_REDRAW2:
case KEY_CMDMODE_MARK:
default:
{
if (ch == -1)
{
continue;
}
else
{
VI_BEL(vi);
}
}
break;
}
* been used). There are a few exceptions:
*
* - For the double character sequences, we need to retain the value
* until the next character is entered.
* - If we are changing modes, then we may need to preserve the 'value'
* as well; in some cases settings are passed to the new mode in
* 'value' (vi_setmode() will have set or cleared 'value'
* appropriately).
*/
if (!preserve)
{
vi->value = 0;
}
}
}
* Common Sub-Mode Functions
****************************************************************************/
* Name: vi_cmdch
*
* Description:
* Insert one character into the data entry line
*
****************************************************************************/
static void vi_cmdch(FAR struct vi_s *vi, char ch)
{
int index = vi->cmdlen;
int next = index + 1;
viinfo("cmdlen=%d ch=%c[%02x]\n", vi->cmdlen, isprint(ch) ? ch : '.', ch);
if (next >= SCRATCH_BUFSIZE)
{
vi_exitsubmode(vi, MODE_COMMAND);
return;
}
vi->scratch[index] = ch;
vi->cmdlen = next;
vi->cursor.column = next + 1;
vi_putch(vi, ch);
if (ch == '\n')
{
vi->drawtoeos = true;
}
}
* Name: vi_cmdbackspace
*
* Description:
* Process a backspace character in the data entry line
*
****************************************************************************/
static void vi_cmdbackspace(FAR struct vi_s *vi)
{
viinfo("cmdlen=%d\n", vi->cmdlen);
if (vi->cmdlen > 0)
{
vi_setcursor(vi, vi->display.row - 1, vi->cmdlen);
vi_clrtoeol(vi);
vi->cursor.column = vi->cmdlen;
vi->cmdlen--;
}
}
* Colon Data Entry Sub-Mode Functions
****************************************************************************/
* Name: vi_parsecolon
*
* Description:
* Parse the colon command collected in the scratch buffer
*
****************************************************************************/
static void vi_parsecolon(FAR struct vi_s *vi)
{
FAR const char *filename = NULL;
uint8_t cmd = CMD_NONE;
bool done = false;
bool forced;
int col;
int ch;
viinfo("Parse: \"%s\"\n", vi->scratch);
vi->scratch[vi->cmdlen] = '\0';
if (vi->cmdlen > 1 && vi->scratch[0] == KEY_COLMODE_WRITE &&
vi->scratch[1] == KEY_COLMODE_QUIT)
{
vi->scratch[0] = KEY_COLMODE_QUIT;
vi->scratch[1] = KEY_COLMODE_WRITE;
}
for (col = 0; col < vi->cmdlen && !done; col++)
{
ch = vi->scratch[col];
forced = false;
if (col < vi->cmdlen && vi->scratch[col + 1] == KEY_COLMODE_FORCE)
{
forced = true;
col++;
}
switch (ch)
{
case KEY_COLMODE_READ:
{
if (cmd == CMD_NONE && !forced)
{
cmd = CMD_READ;
}
else
{
* quitting
*/
goto errout_bad_command;
}
}
break;
case KEY_COLMODE_WRITE:
{
if (cmd == CMD_NONE)
{
cmd = (forced ? CMD_OWRITE : CMD_WRITE);
}
else if (cmd == CMD_QUIT)
{
cmd = (forced ? CMD_OWRITE_QUIT : CMD_WRITE_QUIT);
}
else
{
* including a forced quit is a syntax error
*/
goto errout_bad_command;
}
}
break;
case KEY_COLMODE_QUIT:
{
if (cmd == CMD_NONE)
{
cmd = (forced ? CMD_DISCARD : CMD_QUIT);
}
* quit operation.
*/
else if (cmd == CMD_WRITE && !forced)
{
cmd = CMD_WRITE_QUIT;
}
else if (cmd == CMD_OWRITE && !forced)
{
cmd = CMD_OWRITE_QUIT;
}
else
{
goto errout_bad_command;
}
}
break;
default:
{
if (ch != ' ')
{
done = true;
* a file name. If we are writing (or reading with an
* empty text buffer), then we will need to copy the file
* into the filename storage area.
*/
if (ch != '\0')
{
* scratch buffer.
*/
filename = &vi->scratch[col];
}
}
}
break;
}
}
* A filename where one is not needed is also an error.
*/
viinfo("cmd=%d filename=\"%s\"\n", cmd, vi->filename);
if (cmd == CMD_NONE || (IS_READ(cmd) && !filename) ||
(!USES_FILE(cmd) && filename))
{
goto errout_bad_command;
}
* then we have to check if the file exists.
*/
if (filename && IS_NOWRITE(cmd))
{
if (vi_checkfile(vi, filename))
{
vi_error(vi, g_fmtfileexists);
goto errout;
}
}
* force quitting in this case.
*/
if (vi->modified && IS_NDISCARD(cmd))
{
vi_error(vi, g_fmtmodified);
goto errout;
}
if (IS_READ(cmd))
{
bool empty = (vi->textsize == 0);
off_t pos = vi_nextline(vi, vi->curpos);
if (vi_insertfile(vi, pos, filename))
{
if (empty)
{
* as unmodified.
*/
strlcpy(vi->filename, filename, MAX_FILENAME);
vi->modified = false;
}
else
{
* as modified.
*/
vi->modified = true;
}
}
}
if (IS_WRITE(cmd))
{
* from the scratch buffer to the filename buffer.
*/
if (filename)
{
strlcpy(vi->filename, filename, MAX_FILENAME);
}
* buffer, then ignore the write.
*/
if (filename || vi->modified)
{
vi_clearbottomline(vi);
vi_putch(vi, '"');
vi_write(vi, vi->filename, strlen(vi->filename));
vi_putch(vi, '"');
vi_putch(vi, ' ');
if (!vi_savetext(vi, vi->filename, 0, vi->textsize))
{
* not forcing the quit operation. So error out without
* quitting until the user decides what to do about
* the save failure.
*/
goto errout;
}
if (!IS_QUIT(cmd))
{
vi_setcursor(vi, vi->cursor.row, vi->cursor.column);
}
vi->modified = false;
}
}
if (IS_QUIT(cmd))
{
vi_putch(vi, '\n');
vi_release(vi);
exit(EXIT_SUCCESS);
}
vi_exitsubmode(vi, MODE_COMMAND);
return;
errout_bad_command:
vi_error(vi, g_fmtnotcmd, vi->scratch);
errout:
vi_exitsubmode(vi, MODE_COMMAND);
}
* Name: vi_cmd_submode
*
* Description:
* Colon command sub-mode of the command mode processing
*
****************************************************************************/
static void vi_cmd_submode(FAR struct vi_s *vi)
{
int ch;
viinfo("Enter colon command sub-mode\n");
while (vi->mode == SUBMODE_COLON)
{
ch = vi_getch(vi);
switch (ch)
{
case KEY_COLMODE_QUOTE:
{
vi_cmdch(vi, vi_getch(vi));
}
break;
case ASCII_BS:
{
if (vi->cmdlen == 0)
{
vi_exitsubmode(vi, MODE_COMMAND);
vi_clearbottomline(vi);
}
else
{
vi_cmdbackspace(vi);
}
}
break;
case ASCII_ESC:
{
vi_exitsubmode(vi, MODE_COMMAND);
}
break;
case '\n':
{
vi_parsecolon(vi);
}
break;
default:
{
if (isprint(ch))
{
vi_cmdch(vi, ch);
}
else
{
VI_BEL(vi);
}
}
break;
}
}
}
* Find Data Entry Sub-Mode Functions
****************************************************************************/
* Name: vi_findstring
*
* Description:
* Find the string in the findstr buffer by searching for a matching
* sub-string in the text buffer, starting at the current cursor position.
*
****************************************************************************/
static bool vi_findstring(FAR struct vi_s *vi)
{
off_t pos;
int len;
viinfo("findstr: \"%s\"\n", vi->findstr);
len = strlen(vi->findstr);
if (!len)
{
return false;
}
* matching sub-string. Stop loo
*/
vi_clearbottomline(vi);
for (pos = vi->curpos;
pos + len <= vi->textsize;
pos++)
{
if (strncmp(vi->text + pos, vi->scratch, len) == 0)
{
* return success.
*/
vi->curpos = pos;
return true;
}
}
* current cursor position. Start from beginning and search to curpos.
*/
for (pos = 0; pos <= vi->curpos; pos++)
{
if (strncmp(vi->text + pos, vi->scratch, len) == 0)
{
vi_write(vi, g_fmtsrcbot, sizeof(g_fmtsrcbot));
* return success.
*/
vi->curpos = pos;
return true;
}
}
return false;
}
* Name: vi_revfindstring
*
* Description:
* Find the string in the findstr buffer by searching for a matching
* sub-string in the text buffer, starting at the current cursor position.
* The search is performed backward through the file.
*
****************************************************************************/
static bool vi_revfindstring(FAR struct vi_s *vi)
{
off_t pos;
int len;
viinfo("findstr: \"%s\"\n", vi->findstr);
len = strlen(vi->findstr);
if (!len)
{
return false;
}
* matching sub-string. Stop loo
*/
vi_clearbottomline(vi);
for (pos = vi->curpos;
pos > 0; pos--)
{
if (strncmp(vi->text + pos, vi->scratch, len) == 0)
{
* return success.
*/
vi->curpos = pos;
return true;
}
}
* current cursor position. Start from end and search to curpos.
*/
for (pos = vi->textsize - len; pos > vi->curpos; pos--)
{
if (strncmp(vi->text + pos, vi->scratch, len) == 0)
{
vi_write(vi, g_fmtsrctop, sizeof(g_fmtsrctop));
* return success.
*/
vi->curpos = pos;
return true;
}
}
return false;
}
* Name: vi_parsefind
*
* Description:
* Find the string collected in the scratch buffer.
*
****************************************************************************/
static void vi_parsefind(FAR struct vi_s *vi, bool revfind)
{
vi->scratch[vi->cmdlen] = '\0';
* string from the previous find operation.
*/
viinfo("scratch: \"%s\"\n", vi->scratch);
if (vi->cmdlen > 0)
{
strlcpy(vi->findstr, vi->scratch, MAX_STRING);
}
vi->revfind = revfind;
if (revfind)
{
vi_revfindstring(vi);
}
else
{
vi_findstring(vi);
}
vi_exitsubmode(vi, MODE_COMMAND);
}
* Name: vi_find_submode
*
* Description:
* Find command sub-mode of the command mode processing
*
****************************************************************************/
static void vi_find_submode(FAR struct vi_s *vi, bool revfind)
{
int ch;
viinfo("Enter find sub-mode\n");
while (vi->mode == SUBMODE_FIND || vi->mode == SUBMODE_REVFIND)
{
ch = vi_getch(vi);
switch (ch)
{
case KEY_FINDMODE_QUOTE:
{
vi_cmdch(vi, vi_getch(vi));
}
break;
case ASCII_BS:
{
if (vi->cmdlen == 0)
{
vi_exitsubmode(vi, MODE_COMMAND);
vi_clearbottomline(vi);
}
else
{
vi_cmdbackspace(vi);
}
}
break;
case ASCII_ESC:
{
vi_exitsubmode(vi, MODE_COMMAND);
}
break;
case '\n':
{
vi_parsefind(vi, revfind);
}
break;
default:
{
if (isprint(ch))
{
vi_cmdch(vi, ch);
}
else
{
VI_BEL(vi);
}
}
break;
}
}
}
* Replace Text Sub-Mode Functions
****************************************************************************/
* Name: vi_replacech
*
* Description:
* Replace the character at the current position. If the current position
* is the end of line, then insert the character.
*
****************************************************************************/
static void vi_replacech(FAR struct vi_s *vi, char ch)
{
viinfo("curpos=%ld ch=%c[%02x]\n", vi->curpos, isprint(ch) ? ch : '.', ch);
if (vi->text[vi->curpos] == '\n')
{
vi_insertch(vi, ch);
vi->drawtoeos = true;
}
else
{
vi->text[vi->curpos++] = ch;
vi->redrawline = true;
}
}
* Name: vi_replacech_submode
*
* Description:
* Replace character command sub-mode of the command mode processing
*
****************************************************************************/
static void vi_replacech_submode(FAR struct vi_s *vi)
{
off_t end;
long nchars;
bool found = false;
int ch = 0;
nchars = (vi->value > 0 ? vi->value : 1);
viinfo("Enter replaces character(s) sub-mode: nchars=%d\n", nchars);
end = vi_lineend(vi, vi->curpos) + 1;
if (vi->curpos + nchars > end)
{
vi_error(vi, g_fmtnotvalid);
vi_setmode(vi, MODE_COMMAND, 0);
}
while (vi->mode == SUBMODE_REPLACECH && !found)
{
ch = vi_getch(vi);
vi->updatereqcol = true;
switch (ch)
{
case KEY_FINDMODE_QUOTE:
{
ch = vi_getch(vi);
found = true;
}
break;
case ASCII_ESC:
{
vi_setmode(vi, MODE_COMMAND, 0);
if (vi->curpos > 0)
{
--vi->curpos;
}
}
break;
case '\n':
{
found = true;
}
break;
default:
{
if (isprint(ch) || ch == '\t')
{
found = true;
}
else
{
VI_BEL(vi);
}
}
break;
}
}
for (; nchars > 0; nchars--)
{
vi_replacech(vi, ch);
vi->redrawline = true;
}
vi_setmode(vi, MODE_COMMAND, 0);
}
* Name: vi_findinline_mode
*
* Description:
* Find character in current line.
*
****************************************************************************/
static void vi_findinline_mode(FAR struct vi_s *vi)
{
int count;
off_t pos;
int ch = -1;
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
if (vi->cmdrepeat)
{
ch = vi->cmdbuf[vi->cmdindex++];
}
else
#endif
{
while (ch == -1)
{
ch = vi_getch(vi);
}
}
if (!isprint(ch))
{
VI_BEL(vi);
vi_setmode(vi, MODE_COMMAND, 0);
return;
}
vi->updatereqcol = true;
pos = vi->curpos + 1;
count = vi->value > 0 ? vi->value : 1;
while (count > 0 && pos < vi->textsize - 1 && vi->text[pos] != '\n')
{
pos++;
if (vi->text[pos] == ch)
{
count--;
}
}
if (count == 0)
{
if (vi->tfind)
{
pos--;
}
if (vi->yankarm || vi->delarm || vi->chgarm)
{
vi_yanktext(vi, vi->curpos, pos, 1, vi->delarm || vi->chgarm);
if (vi->delarm || vi->chgarm)
{
vi->redrawline = true;
}
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
if (vi->delarm || vi->chgarm)
{
vi_saverepeat(vi, vi->delarm ? 'd' : 'c');
vi_appendrepeat(vi, vi->tfind ? 't' : 'f');
vi_appendrepeat(vi, ch);
}
#endif
if (vi->chgarm)
{
vi->chgarm = false;
vi_setmode(vi, MODE_INSERT, 0);
return;
}
vi->delarm = false;
vi->yankarm = false;
}
else
{
vi->curpos = pos;
}
}
vi_setmode(vi, MODE_COMMAND, 0);
}
* Insert and Replace Mode Functions
****************************************************************************/
* Name: vi_insertch
*
* Description:
* Insert one character into the text buffer
*
****************************************************************************/
static void vi_insertch(FAR struct vi_s *vi, char ch)
{
viinfo("curpos=%ld ch=%c[%02x]\n", vi->curpos, isprint(ch) ? ch : '.', ch);
if (vi_extendtext(vi, vi->curpos, 1))
{
vi->text[vi->curpos++] = ch;
}
}
* Name: vi_insert_mode
*
* Description:
* Insert mode loop
*
****************************************************************************/
static void vi_insert_mode(FAR struct vi_s *vi)
{
off_t start = vi->curpos;
int ch;
viinfo("Enter insert mode\n");
vi_clearbottomline(vi);
vi_write(vi, g_fmtinsert, sizeof(g_fmtinsert));
vi_setcursor(vi, vi->cursor.row, vi->cursor.column);
vi->redrawline = true;
while (vi->mode == MODE_INSERT || vi->mode == MODE_REPLACE)
{
if (vi->redrawline || vi->drawtoeos || vi->fullredraw)
{
vi_showtext(vi);
}
vi_showlinecol(vi);
vi_setcursor(vi, vi->cursor.row, vi->cursor.column);
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
if (vi->cmdrepeat)
{
ch = vi->cmdbuf[vi->cmdindex++];
}
else
#endif
{
ch = vi_getch(vi);
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
if (ch == KEY_UP || ch == KEY_DOWN || ch == KEY_LEFT ||
ch == KEY_RIGHT || ch == KEY_HOME || ch == KEY_END ||
ch == KEY_PPAGE || ch == KEY_NPAGE)
{
vi->cmdcount = 1;
vi->redrawline = true;
}
else
{
vi_appendrepeat(vi, ch);
}
#endif
}
* and this will give better performance.
*/
vi->updatereqcol = true;
if (isprint(ch) || ch == '\t')
{
if (vi->mode == MODE_INSERT)
{
vi_insertch(vi, ch);
}
else
{
vi_replacech(vi, ch);
}
* character, then do a simple putch, otherwise request
* a line redraw.
*/
if (vi->cursor.column + 1 < vi->display.column && ch != '\t' &&
(vi->curpos + 1 == vi->textsize ||
vi->text[vi->curpos + 1] == '\n'))
{
vi_putch(vi, ch);
}
else
{
vi->redrawline = true;
}
vi->cursor.column++;
continue;
}
vi->error = false;
vi->redrawline = true;
switch (ch)
{
case KEY_INSMODE_QUOTE:
{
vi_insertch(vi, vi_getch(vi));
}
break;
case ASCII_DEL:
{
if (vi->curpos < vi->textsize)
{
if (vi->text[vi->curpos] == '\n')
{
vi->drawtoeos = true;
}
vi_shrinktext(vi, vi->curpos, 1);
}
}
break;
case ASCII_BS:
{
if (vi->mode == MODE_INSERT)
{
if (vi->curpos > 0)
{
if (vi->text[vi->curpos - 1] == '\n')
{
vi->drawtoeos = true;
}
vi_shrinktext(vi, vi->curpos - 1, 1);
}
}
else
{
if (vi->curpos > start)
{
vi->curpos = vi_cursorleft(vi, vi->curpos, 1);
}
}
}
break;
case ASCII_ESC:
{
vi_setmode(vi, MODE_COMMAND, 0);
vi->updatereqcol = true;
if (vi->curpos > 0 && vi->text[vi->curpos - 1] != '\n')
{
--vi->curpos;
}
}
break;
case '\n':
{
if (vi->mode == MODE_INSERT)
{
vi_insertch(vi, '\n');
}
else
{
vi_replacech(vi, '\n');
}
vi->drawtoeos = true;
}
break;
case KEY_UP:
{
vi->updatereqcol = false;
vi_cusorup(vi, 1);
}
break;
case KEY_DOWN:
{
vi->updatereqcol = false;
vi_cursordown(vi, 1);
}
break;
case KEY_LEFT:
{
vi->curpos = vi_cursorleft(vi, vi->curpos, 1);
}
break;
case KEY_RIGHT:
{
vi->curpos = vi_cursorright(vi, vi->curpos, 1);
if (vi->curpos >= vi->textsize)
{
vi->curpos = vi->textsize;
}
}
break;
case KEY_HOME:
{
vi->curpos = vi_linebegin(vi, vi->curpos);
}
break;
case KEY_END:
{
vi->curpos = vi_lineend(vi, vi->curpos);
}
break;
case KEY_PPAGE:
{
vi->updatereqcol = false;
vi_cusorup(vi, vi->display.row);
}
break;
case KEY_NPAGE:
{
vi->updatereqcol = false;
vi_cursordown(vi, vi->display.row);
}
break;
default:
{
if (ch == -1)
{
continue;
}
else
{
VI_BEL(vi);
}
}
break;
}
}
vi_clearbottomline(vi);
}
* Command line processing
****************************************************************************/
* Name: vi_showusage
*
* Description:
* Show command line arguments and exit.
*
****************************************************************************/
static void vi_release(FAR struct vi_s *vi)
{
if (vi)
{
if (vi->text)
{
free(vi->text);
}
if (vi->yank)
{
free(vi->yank);
}
if (vi->tcurs)
{
termcurses_deinitterm(vi->tcurs);
}
free(vi);
}
}
* Name: vi_showusage
*
* Description:
* Show command line arguments and exit.
*
****************************************************************************/
static void vi_showusage(FAR struct vi_s *vi, FAR const char *progname,
int exitcode)
{
fprintf(stderr, "\nUSAGE:\t%s [-c <columns] [-r <rows>] [<filename>]\n",
progname);
fprintf(stderr, "\nUSAGE:\t%s -h\n\n",
progname);
fprintf(stderr, "Where:\n");
fprintf(stderr, "\t<filename>:\n");
fprintf(stderr, "\t\tOptional name of the file to open\n");
fprintf(stderr, "\t-c <columns>:\n");
fprintf(stderr,
"\t\tOptional width of the display in columns. Default: %d\n",
CONFIG_SYSTEM_VI_COLS);
fprintf(stderr, "\t-r <rows>:\n");
fprintf(stderr,
"\t\tOptional height of the display in rows. Default: %d\n",
CONFIG_SYSTEM_VI_ROWS);
fprintf(stderr, "\t-h:\n");
fprintf(stderr, "\t\tShows this message and exits.\n");
vi_release(vi);
exit(exitcode);
}
* Public Functions
****************************************************************************/
* Name: vi_main
*
* Description:
* The main entry point into vi.
*
****************************************************************************/
int main(int argc, FAR char *argv[])
{
FAR struct vi_s *vi;
int option;
int ret;
vi = (FAR struct vi_s *)zalloc(sizeof(struct vi_s));
if (vi == NULL)
{
vi_error(vi, g_fmtallocfail);
return EXIT_FAILURE;
}
vi->display.row = CONFIG_SYSTEM_VI_ROWS;
vi->display.column = CONFIG_SYSTEM_VI_COLS;
while ((option = getopt(argc, argv, ":c:r:h")) != ERROR)
{
switch (option)
{
case 'c':
{
unsigned long value = strtoul(optarg, NULL, 10);
if (value <= UINT16_MAX)
{
vi->display.column = (uint16_t)value;
}
else
{
fprintf(stderr, "ERROR: Column value out of range: %lu\n",
value);
vi_showusage(vi, argv[0], EXIT_FAILURE);
}
}
break;
case 'r':
{
unsigned long value = strtoul(optarg, NULL, 10);
if (value <= UINT16_MAX)
{
vi->display.row = (uint16_t)value;
}
else
{
fprintf(stderr, "ERROR: Row value out of range: %lu\n",
value);
vi_showusage(vi, argv[0], EXIT_FAILURE);
}
}
break;
case 'h':
{
vi_showusage(vi, argv[0], EXIT_SUCCESS);
}
break;
case ':':
{
fprintf(stderr, "ERROR: Missing parameter argument\n");
vi_showusage(vi, argv[0], EXIT_FAILURE);
}
break;
case '?':
default:
{
fprintf(stderr, "ERROR: Unrecognized parameter\n");
vi_showusage(vi, argv[0], EXIT_FAILURE);
}
break;
}
}
ret = termcurses_initterm(NULL, 0, 1, &vi->tcurs);
if (ret == OK)
{
struct winsize winsz;
ret = termcurses_getwinsize(vi->tcurs, &winsz);
if (ret == OK)
{
vi->display.row = winsz.ws_row;
vi->display.column = winsz.ws_col;
}
}
if (optind < argc)
{
if (argv[optind][0] == '/')
{
strlcpy(vi->filename, argv[optind], MAX_STRING);
}
else
{
getcwd(vi->filename, MAX_STRING);
strlcat(vi->filename, "/", MAX_STRING);
strlcat(vi->filename, argv[optind], MAX_STRING);
}
vi->filename[MAX_STRING - 1] = '\0';
vi_insertfile(vi, 0, vi->filename);
vi->modified = false;
optind++;
}
if (vi->text == NULL)
{
vi_extendtext(vi, 0, TEXT_GULP_SIZE);
vi->textsize = 0;
vi->modified = 0;
}
if (optind != argc)
{
fprintf(stderr, "ERROR: Too many arguments\n");
vi_showusage(vi, argv[0], EXIT_FAILURE);
}
vi->fullredraw = true;
for (; ; )
{
viinfo("mode=%d\n", vi->mode);
switch (vi->mode)
{
default:
case MODE_COMMAND:
vi_cmd_mode(vi);
break;
case SUBMODE_COLON:
vi_cmd_submode(vi);
break;
case SUBMODE_REVFIND:
case SUBMODE_FIND:
vi_find_submode(vi, vi->mode == SUBMODE_REVFIND);
break;
case SUBMODE_REPLACECH:
vi_replacech_submode(vi);
break;
case MODE_INSERT:
case MODE_REPLACE:
vi_insert_mode(vi);
break;
case MODE_FINDINLINE:
vi_findinline_mode(vi);
break;
}
}
}