VI: Finally test vi file-related command, fixed bugs, and add file read command

This commit is contained in:
Gregory Nutt 2014-01-30 18:59:43 -06:00
parent 8ae7858929
commit 4986c865b6

View File

@ -81,6 +81,31 @@
#define TABMASK 7 /* Mask for TAB alignment */
#define NEXT_TAB(p) (((p) + TABSIZE) & ~TABMASK)
/* Parsed command action bits */
#define CMD_READ (1 << 0) /* Bit 0: Read */
#define CMD_WRITE_MASK (3 << 1) /* Bits 1-2: x1=Write operation */
# define CMD_WRITE (1 << 1) /* 01=Write (without overwriting) */
# define CMD_OWRITE (3 << 1) /* 11=Overwrite */
#define CMD_QUIT_MASK (3 << 3) /* Bits 3-4: x1=Quite operation */
# define CMD_QUIT (1 << 3) /* 01=Quit if saved */
# define CMD_DISCARD (3 << 3) /* 11=Quit without saving */
#define CMD_NONE (0) /* No command */
#define CMD_WRITE_QUIT (CMD_WRITE | CMD_QUIT) /* Write file and quit command */
#define CMD_OWRITE_QUIT (CMD_OWRITE | CMD_QUIT) /* Overwrite file and quit command */
#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)
#define CMD_FILE_MASK (CMD_READ | CMD_WRITE)
#define USES_FILE(a) (((uint8_t)(a) & CMD_FILE_MASK) != 0)
/* Debug */
#ifndef CONFIG_SYSTEM_VI_DEBUGLEVEL
@ -167,6 +192,7 @@ enum vi_insmode_key_e
enum vi_colonmode_key_e
{
KEY_COLMODE_READ = 'r', /* Read file */
KEY_COLMODE_QUIT = 'q', /* Quit vi */
KEY_COLMODE_WRITE = 'w', /* Write file */
KEY_COLMODE_FORCE = '!', /* Force operation */
@ -280,10 +306,11 @@ static void vi_shrinktext(FAR struct vi_s *vi, off_t pos, size_t size);
/* File access */
static bool vi_insertfile(FAR struct vi_s *vi, FAR char *filename);
static bool vi_savetext(FAR struct vi_s *vi, FAR char *filename,
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 char *filename);
static bool vi_checkfile(FAR struct vi_s *vi, FAR const char *filename);
/* Mode management */
@ -381,6 +408,7 @@ 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";
/****************************************************************************
* Private Functions
@ -1037,7 +1065,8 @@ static void vi_shrinktext(FAR struct vi_s *vi, off_t pos, size_t size)
*
****************************************************************************/
static bool vi_insertfile(FAR struct vi_s *vi, FAR char *filename)
static bool vi_insertfile(FAR struct vi_s *vi, off_t pos,
FAR const char *filename)
{
struct stat buf;
FILE *stream;
@ -1046,7 +1075,7 @@ static bool vi_insertfile(FAR struct vi_s *vi, FAR char *filename)
int result;
bool ret;
vivdbg("filename=\"%s\"\n", filename);
vivdbg("pos=%ld filename=\"%s\"\n", (long)pos, filename);
/* Get the size of the file */
@ -1079,19 +1108,19 @@ static bool vi_insertfile(FAR struct vi_s *vi, FAR char *filename)
*/
ret = false;
if (vi_extendtext(vi, vi->curpos, filesize))
if (vi_extendtext(vi, pos, filesize))
{
/* Read the contents of the file into the text buffer at the
* current cursor position.
*/
nread = fread(vi->text + vi->curpos, 1, filesize, stream);
nread = fread(vi->text + pos, 1, filesize, stream);
if (nread < filesize)
{
/* Report the error (or partial read), EINTR is not handled */
vi_error(vi, g_fmtcmdfail, "fread", errno);
vi_shrinktext(vi, vi->curpos, filesize);
vi_shrinktext(vi, pos, filesize);
}
else
{
@ -1111,8 +1140,8 @@ static bool vi_insertfile(FAR struct vi_s *vi, FAR char *filename)
*
****************************************************************************/
static bool vi_savetext(FAR struct vi_s *vi, FAR char *filename, off_t pos,
size_t size)
static bool vi_savetext(FAR struct vi_s *vi, FAR const char *filename,
off_t pos, size_t size)
{
FILE *stream;
size_t nwritten;
@ -1154,7 +1183,7 @@ static bool vi_savetext(FAR struct vi_s *vi, FAR char *filename, off_t pos,
*
****************************************************************************/
static bool vi_checkfile(FAR struct vi_s *vi, FAR char *filename)
static bool vi_checkfile(FAR struct vi_s *vi, FAR const char *filename)
{
struct stat buf;
int ret;
@ -1251,6 +1280,10 @@ static void vi_setsubmode(FAR struct vi_s *vi, uint8_t mode, char prompt,
vi_putch(vi, prompt);
vi_attriboff(vi);
/* Clear to the end of the line */
vi_clrtoeol(vi);
/* Update the cursor position */
vi->cursor.column = 1;
@ -2434,15 +2467,12 @@ static void vi_cmdbackspace(FAR struct vi_s *vi)
static void vi_parsecolon(FAR struct vi_s *vi)
{
FAR char *filename = NULL; /* May be replaced with new filename in scratch buffer */
bool dowrite = false; /* True: We need to write the text buffer to a file */
bool doquit = false; /* True: Exit vi */
bool forcewrite = false; /* True: Write even if the new file exists */
bool forcequit = false; /* True: Quite even if there are un-saved changes */
bool done = false; /* True: Terminates parsing loop */
int ch;
int lastch = 0;
FAR const char *filename = NULL;
uint8_t cmd = CMD_NONE;
bool done = false;
bool forced;
int col;
int ch;
vivdbg("Parse: \"%s\"\n", vi->scratch);
@ -2454,33 +2484,97 @@ static void vi_parsecolon(FAR struct vi_s *vi)
for (col = 0; col < vi->cmdlen && !done; col++)
{
/* Get the next command character from the scratch buffer */
ch = vi->scratch[col];
/* Check if the next after that is KEY_COLMODE_FORCE */
forced = false;
if (col < vi->cmdlen && vi->scratch[col + 1] == KEY_COLMODE_FORCE)
{
/* Yes.. the operation is forced */
forced = true;
col++;
}
/* Then process the command character */
switch (ch)
{
case KEY_COLMODE_READ:
{
/* Reading a file should not be forced */
if (cmd == CMD_NONE && !forced)
{
cmd = CMD_READ;
}
else
{
/* The read operation is not compatible with writing or
* quitting
*/
goto errout_bad_command;
}
}
break;
case KEY_COLMODE_WRITE:
{
dowrite = true;
lastch = ch;
/* Are we just writing? Or writing then quitting? */
if (cmd == CMD_NONE)
{
/* Just writing.. do we force overwriting? */
cmd = (forced ? CMD_OWRITE : CMD_WRITE);
}
else if (cmd == CMD_QUIT)
{
/* Both ... do we force overwriting the file? */
cmd = (forced ? CMD_OWRITE_QUIT : CMD_WRITE_QUIT);
}
else
{
/* Anything else, including a forced quit is a syntax error */
goto errout_bad_command;
}
}
break;
case KEY_COLMODE_QUIT:
{
doquit = true;
lastch = ch;
}
break;
/* Are we just quitting? Or writing then quitting? */
case KEY_COLMODE_FORCE:
{
if (lastch == KEY_COLMODE_WRITE)
if (cmd == CMD_NONE)
{
forcewrite = true;
/* Just quitting... should we discard any changes? */
cmd = (forced ? CMD_DISCARD : CMD_QUIT);
}
else if (lastch == KEY_COLMODE_QUIT)
/* If we are also writing, then it makes no sense to force the
* quit operation.
*/
else if (cmd == CMD_WRITE && !forced)
{
forcequit = true;
cmd = CMD_WRITE_QUIT;
}
else if (cmd == CMD_OWRITE && !forced)
{
cmd = CMD_OWRITE_QUIT;
}
else
{
/* Quit is not compatible with reading */
goto errout_bad_command;
}
}
break;
@ -2496,11 +2590,12 @@ static void vi_parsecolon(FAR struct vi_s *vi)
done = true;
/* If there is anything else on the line, then it must be
* a file name. If we are writing, then we need to copy
* the file into the filename storage area.
* 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' && dowrite)
if (ch != '\0')
{
/* For now, just remember where the file is in the
* scratch buffer.
@ -2514,40 +2609,94 @@ static void vi_parsecolon(FAR struct vi_s *vi)
}
}
/* Did we find any valid command? A read command requires a filename.
* A filename where one is not needed is also an error.
*/
vivdbg("cmd=%d filename=\"%s\"\n", cmd, vi->filename);
if (cmd == CMD_NONE || (IS_READ(cmd) && !filename) ||
(!USES_FILE(cmd) && filename))
{
goto errout_bad_command;
}
/* Are we writing to a new filename? If we are not forcing the write,
* then we have to check if the file exists.
*/
vivdbg("dowrite=%d forcewrite=%d filename=\"%s\"\n",
dowrite, forcewrite, filename ? filename : vi->filename);
if (dowrite && filename && !forcewrite && vi_checkfile(vi, vi->filename))
if (filename && IS_NOWRITE(cmd))
{
vi_error(vi, g_fmtfileexists);
dowrite = false;
doquit = false;
/* Check if the file exists */
if (vi_checkfile(vi, filename))
{
/* It does... show an error and exit */
vi_error(vi, g_fmtfileexists);
goto errout;
}
}
/* Check if we are trying to quit with un-saved changes. The use must
/* Check if we are trying to quit with un-saved changes. The user must
* force quitting in this case.
*/
vivdbg("doquit=%d forcequit=%d modified=%d\n",
doquit, forcequit, vi->modified);
if (doquit && vi->modified && !forcequit)
if (vi->modified && IS_NDISCARD(cmd))
{
/* Show an error and exit */
vi_error(vi, g_fmtmodified);
dowrite = false;
doquit = false;
goto errout;
}
/* Are we now commit to writing the file? */
/* Are we now committed to reading the file? */
vivdbg("dowrite=%d filename=\"%s modified=%d\n",
dowrite, filename ? filename : vi->filename, vi->modified);
if (IS_READ(cmd))
{
/* Was the text buffer empty? */
if (dowrite)
bool empty = (vi->textsize == 0);
/* Yes.. get the cursor position of the beginning of the next line */
off_t pos = vi_nextline(vi, vi->curpos);
/* Then read the file into the text buffer at that position. */
if (vi_insertfile(vi, pos, filename))
{
/* Was the text buffer empty before we inserted the file? */
if (empty)
{
/* Yes.. then we want to save the filename and mark the text
* as unmodified.
*/
strncpy(vi->filename, filename, MAX_STRING - 1);
/* Make sure that the (possibly truncated) file name is NUL
* terminated
*/
vi->filename[MAX_STRING - 1] = '\0';
vi->modified = false;
}
else
{
/* No.. then we want to retain the filename and mark the text
* as modified.
*/
vi->modified = true;
}
}
}
/* Are we now committed to writing the file? */
if (IS_WRITE(cmd))
{
/* If we are writing to a new file, then we need to copy the filename
* from the scratch buffer to the filename buffer.
@ -2572,23 +2721,26 @@ static void vi_parsecolon(FAR struct vi_s *vi)
{
/* Now, finally, we can save the file */
if (vi_savetext(vi, vi->filename, 0, vi->textsize) && !forcequit)
if (!vi_savetext(vi, vi->filename, 0, vi->textsize))
{
/* An error occurred while saving the file and we are
* not forcing the quit operation. So cancel the
* quit.
* not forcing the quit operation. So error out without
* quitting until the user decides what to do about
* the save failure.
*/
doquit = false;
goto errout;
}
/* The text buffer contents are no longer modified */
vi->modified = false;
}
}
/* Are we committed to exit-ing? */
vivdbg("doquit=%d\n", doquit);
if (doquit)
if (IS_QUIT(cmd))
{
/* Yes... free resources and exit */
@ -2599,6 +2751,13 @@ static void vi_parsecolon(FAR struct vi_s *vi)
/* Otherwise, revert to command mode */
vi_exitsubmode(vi, MODE_COMMAND);
return;
errout_bad_command:
vi_error(vi, g_fmtnotcmd, vi->scratch);
errout:
vi_exitsubmode(vi, MODE_COMMAND);
}
/****************************************************************************
@ -3468,7 +3627,7 @@ int vi_main(int argc, char **argv)
/* Load the file into memory */
(void)vi_insertfile(vi, vi->filename);
(void)vi_insertfile(vi, 0, vi->filename);
/* Skip over the filename argument. There should nothing after this */