Skip to content

Commit

Permalink
Added read command
Browse files Browse the repository at this point in the history
The read command is capable of reading either command output or file
content into the currently active buffer at the current cursor position.
  • Loading branch information
rgburke committed Sep 25, 2016
1 parent a8903d7 commit 30050de
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 38 deletions.
32 changes: 25 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -374,13 +374,14 @@ The table below lists the commands currently available in wed and their
properties:

```
Command | Arguments | Description
--------|--------------------------|----------------------------------------------------
echo | variable | Displays arguments in the status bar
map | string KEYS, string KEYS | Maps a sequence of keys to another sequence of keys
unmap | string KEYS | Unmaps a previously created key mapping
help | none | Display basic help information
filter | shell command CMD | Filter buffer through shell command
Command | Arguments | Description
--------|---------------------------------|----------------------------------------------------
echo | variable | Displays arguments in the status bar
map | string KEYS, string KEYS | Maps a sequence of keys to another sequence of keys
unmap | string KEYS | Unmaps a previously created key mapping
help | none | Display basic help information
filter | shell command CMD | Filter buffer through shell command
read | shell command CMD | string FILE | Read command output or file content into buffer
```

##### echo
Expand Down Expand Up @@ -457,6 +458,23 @@ filter !sed /test/d
The filter command makes the power of the Unix shell commands available in wed
allowing many complex operations to be performed on a buffer.

##### read

File content or command output can be read into the active buffer at the
current cursor position. For example, to read the file `buffer.c` into the
current buffer run:

```
read buffer.c
```

To read command output into the buffer supply a shell command. For example,
to read the current Unix timestamp into the buffer run:

```
read !date +%s
```

#### Config Definitions

Config definitions allow objects to be defined which can be referenced by
Expand Down
50 changes: 40 additions & 10 deletions buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -214,43 +214,65 @@ FileFormat bf_detect_fileformat(const Buffer *buffer)
return file_format;
}

/* Loads file into buffer */
/* Reset buffer and load configured file into buffer */
Status bf_load_file(Buffer *buffer)
{
Status status = STATUS_SUCCESS;
RETURN_IF_FAIL(bf_reset(buffer));

if (!fi_file_exists(&buffer->file_info)) {
/* If the file represented by this buffer doesn't exist
* then the buffer content is empty */
return bf_clear(buffer);
return STATUS_SUCCESS;
}

/* We don't want the inital load into the buffer to be undoable */
bc_disable(&buffer->changes);

Status status = bf_read_file(buffer, &buffer->file_info);

bc_enable(&buffer->changes);

return status;
}

/* Read file content info buffer at current position */
Status bf_read_file(Buffer *buffer, const FileInfo *file_info)
{
if (!fi_file_exists(file_info)) {
return st_get_error(ERR_FILE_DOESNT_EXIST, "File doesn't exist: %s",
file_info->rel_path);
}

FILE *input_file = fopen(buffer->file_info.abs_path, "rb");
FILE *input_file = fopen(file_info->abs_path, "rb");

if (input_file == NULL) {
return st_get_error(ERR_UNABLE_TO_OPEN_FILE,
"Unable to open file %s for reading - %s",
buffer->file_info.file_name, strerror(errno));
file_info->file_name, strerror(errno));
}

gb_set_point(buffer->data, 0);

size_t old_size = bf_length(buffer);
size_t new_size = old_size + file_info->file_stat.st_size;

/* Attempt to allocate necessary memory before loading into gap buffer */
if (!gb_preallocate(buffer->data, buffer->file_info.file_stat.st_size)) {
if (!gb_preallocate(buffer->data, new_size)) {
return st_get_error(ERR_OUT_OF_MEMORY, "Out Of Memory - ",
"File is too large to load into memory");
}

Status status = STATUS_SUCCESS;
char buf[FILE_BUF_SIZE];
size_t read;

gb_set_point(buffer->data, buffer->pos.offset);

do {
read = fread(buf, sizeof(char), FILE_BUF_SIZE, input_file);

if (ferror(input_file)) {
status = st_get_error(ERR_UNABLE_TO_READ_FILE,
"Unable to read from file %s - %s",
buffer->file_info.file_name, strerror(errno));
file_info->file_name, strerror(errno));
break;
}

Expand All @@ -264,7 +286,15 @@ Status bf_load_file(Buffer *buffer)

fclose(input_file);

gb_set_point(buffer->data, 0);
size_t bytes_inserted = bf_length(buffer) - old_size;

if (bytes_inserted > 0) {
ONLY_OVERWRITE_SUCCESS(
status,
bc_add_text_insert(&buffer->changes, bytes_inserted, &buffer->pos)
);
}

bf_set_is_draw_dirty(buffer, 1);

return status;
Expand Down
1 change: 1 addition & 0 deletions buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ Status bf_clear(Buffer *);
Status bf_reset(Buffer *);
FileFormat bf_detect_fileformat(const Buffer *);
Status bf_load_file(Buffer *);
Status bf_read_file(Buffer *, const FileInfo *);
Status bf_write_file(Buffer *, const char *file_path);
char *bf_to_string(const Buffer *);
char *bf_join_lines_string(const Buffer *, const char *seperator);
Expand Down
113 changes: 96 additions & 17 deletions command.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ static Status cm_session_map(const CommandArgs *);
static Status cm_session_unmap(const CommandArgs *);
static Status cm_session_help(const CommandArgs *);
static Status cm_buffer_filter(const CommandArgs *);
static Status cm_buffer_read(const CommandArgs *);

/* Allow the following to exceed 80 columns.
* This format is easier to read and maipulate in visual block mode in vim */
Expand Down Expand Up @@ -193,7 +194,8 @@ static const CommandDefinition cm_commands[] = {
[CMD_SESSION_MAP] = { "map" , cm_session_map , CMDSIG(2, VAL_TYPE_STR, VAL_TYPE_STR), CMDT_SESS_MOD, "string KEYS, string KEYS", "Maps a sequence of keys to another sequence of keys" },
[CMD_SESSION_UNMAP] = { "unmap" , cm_session_unmap , CMDSIG(1, VAL_TYPE_STR) , CMDT_SESS_MOD, "string KEYS", "Unmaps a previously created key mapping" },
[CMD_SESSION_HELP] = { "help" , cm_session_help , CMDSIG_NO_ARGS , CMDT_SESS_MOD, "none", "Display basic help information" },
[CMD_BUFFER_FILTER] = { "filter", cm_buffer_filter , CMDSIG(1, VAL_TYPE_SHELL_COMMAND) , CMDT_BUFFER_MOD, "shell command CMD", "Filter buffer through shell command" }
[CMD_BUFFER_FILTER] = { "filter", cm_buffer_filter , CMDSIG(1, VAL_TYPE_SHELL_COMMAND) , CMDT_BUFFER_MOD, "shell command CMD", "Filter buffer through shell command" },
[CMD_BUFFER_READ] = { "read" , cm_buffer_read , CMDSIG(1, VAL_TYPE_STR | VAL_TYPE_SHELL_COMMAND), CMDT_BUFFER_MOD, "read shell command CMD | string FILE", "Read command output or file content into buffer" }
};

static const OperationDefinition cm_operations[] = {
Expand Down Expand Up @@ -626,14 +628,15 @@ static Status cm_run_command(const CommandDefinition *cmd,
}

for (size_t k = 0; k < cmd_sig->arg_num; k++) {
if (cmd_args->args[k].type != cmd_sig->arg_types[k]) {
return
st_get_error(ERR_INVALID_COMMAND_ARG_TYPE,
"Command expects argument number %zu to "
"have type %s but provided argument has "
"type %s", k + 1,
va_value_type_string(cmd_sig->arg_types[k]),
va_value_type_string(cmd_args->args[k].type));
if (!(cmd_args->args[k].type & cmd_sig->arg_types[k])) {
return st_get_error(ERR_INVALID_COMMAND_ARG_TYPE,
"Command expects argument number %zu to "
"have type %s but provided argument has "
"type %s", k + 1,
va_multi_value_type_string(
cmd_sig->arg_types[k]),
va_value_type_string(
cmd_args->args[k].type));
}
}
}
Expand Down Expand Up @@ -2293,14 +2296,15 @@ static Status cm_buffer_filter(const CommandArgs *cmd_args)
Value cmd = cmd_args->args[0];
BufferPos orig_pos = buffer->pos;
Buffer *err_buffer = NULL;
Status status = STATUS_SUCCESS;

BufferInputStream bis;
BufferOutputStream bos;
BufferOutputStream bes;

memset(&bis, 0 ,sizeof(BufferInputStream));
memset(&bos, 0 ,sizeof(BufferOutputStream));
memset(&bes, 0 ,sizeof(BufferOutputStream));
memset(&bis, 0, sizeof(BufferInputStream));
memset(&bos, 0, sizeof(BufferOutputStream));
memset(&bes, 0, sizeof(BufferOutputStream));

Range range;
bf_select_all_text(buffer);
Expand All @@ -2314,17 +2318,18 @@ static Status cm_buffer_filter(const CommandArgs *cmd_args)

bf_to_buffer_start(buffer, 0);

RETURN_IF_FAIL(bf_get_buffer_input_stream(&bis, buffer, &range));

Status status = bf_get_buffer_output_stream(&bos, buffer, &buffer->pos, 1);
GOTO_IF_FAIL(status, cleanup);

err_buffer = bf_new_empty("cmderror", sess->config);

if (err_buffer == NULL) {
goto cleanup;
}

status = bf_get_buffer_input_stream(&bis, buffer, &range);
GOTO_IF_FAIL(status, cleanup);

status = bf_get_buffer_output_stream(&bos, buffer, &buffer->pos, 1);
GOTO_IF_FAIL(status, cleanup);

status = bf_get_buffer_output_stream(&bes, err_buffer, &err_buffer->pos, 0);
GOTO_IF_FAIL(status, cleanup);

Expand Down Expand Up @@ -2373,6 +2378,80 @@ static Status cm_buffer_filter(const CommandArgs *cmd_args)

if (err_buffer != NULL) {
bf_free(err_buffer);
} else {
status = OUT_OF_MEMORY("Unable to create stderr stream");
}

return status;
}

static Status cm_buffer_read(const CommandArgs *cmd_args)
{
Value source = cmd_args->args[0];
Session *sess = cmd_args->sess;
Buffer *buffer = sess->active_buffer;
Status status;

if (source.type == VAL_TYPE_STR) {
FileInfo file_info;
RETURN_IF_FAIL(fi_init(&file_info, SVAL(source)));
status = bf_read_file(buffer, &file_info);
fi_free(&file_info);
} else if (source.type == VAL_TYPE_SHELL_COMMAND) {
Buffer *err_buffer = NULL;
BufferOutputStream bos;
BufferOutputStream bes;

memset(&bos, 0, sizeof(BufferOutputStream));
memset(&bes, 0, sizeof(BufferOutputStream));

err_buffer = bf_new_empty("cmderror", sess->config);

if (err_buffer == NULL) {
goto cleanup;
}

status = bf_get_buffer_output_stream(&bos, buffer, &buffer->pos, 0);
GOTO_IF_FAIL(status, cleanup);

status = bf_get_buffer_output_stream(&bes, err_buffer,
&err_buffer->pos, 0);
GOTO_IF_FAIL(status, cleanup);

status = bc_start_grouped_changes(&buffer->changes);
GOTO_IF_FAIL(status, cleanup);

int cmd_status;

status = ec_run_command(CVAL(source), NULL, (OutputStream *)&bos,
(OutputStream *)&bes, &cmd_status);
GOTO_IF_FAIL(status, cleanup);

if (!ec_cmd_successfull(cmd_status)) {
char *error = bf_to_string(err_buffer);
error = error == NULL ? "" : error;
status = st_get_error(ERR_SHELL_COMMAND_ERROR,
"Error when running command \"%s\": %s",
CVAL(source), error);
free(error);
}

cleanup:
bc_end_grouped_changes(&buffer->changes);

if (bos.buffer != NULL) {
bos.os.close((OutputStream *)&bos);
}

if (bes.buffer != NULL) {
bes.os.close((OutputStream *)&bes);
}

if (err_buffer != NULL) {
bf_free(err_buffer);
} else {
status = OUT_OF_MEMORY("Unable to create stderr stream");
}
}

return status;
Expand Down
3 changes: 2 additions & 1 deletion command.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ typedef enum {
CMD_SESSION_MAP,
CMD_SESSION_UNMAP,
CMD_SESSION_HELP,
CMD_BUFFER_FILTER
CMD_BUFFER_FILTER,
CMD_BUFFER_READ
} Command;

/* Operations are instances of commands i.e. they define a command with
Expand Down
12 changes: 10 additions & 2 deletions status.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,27 @@
#define STATUS_IS_SUCCESS(status) ((status).error_code == ERR_NONE)

#define RETURN_IF_FAIL(status) do { \
Status _wed_status = (status);\
Status _wed_status = (status); \
if (!STATUS_IS_SUCCESS(_wed_status)) { \
return _wed_status; \
} \
} while (0)

#define GOTO_IF_FAIL(status,label) do { \
Status _wed_status = (status);\
Status _wed_status = (status); \
if (!STATUS_IS_SUCCESS(_wed_status)) { \
goto label; \
} \
} while (0)

#define ONLY_OVERWRITE_SUCCESS(var,exp) \
do { \
Status _wed_status = (exp); \
if (STATUS_IS_SUCCESS((var)) && !STATUS_IS_SUCCESS(_wed_status)) { \
(var) = _wed_status; \
} \
} while (0)

#define OUT_OF_MEMORY(msg) \
st_get_error(ERR_OUT_OF_MEMORY, "Out Of Memory - " msg)

Expand Down
2 changes: 1 addition & 1 deletion undo.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ struct Buffer;

void bc_init(BufferChanges *);
void bc_free(BufferChanges *);
Status bc_add_text_insert(BufferChanges *, size_t str_len,const BufferPos *);
Status bc_add_text_insert(BufferChanges *, size_t str_len, const BufferPos *);
Status bc_add_text_delete(BufferChanges *, const char *str, size_t str_len,
const BufferPos *);
int bc_can_undo(const BufferChanges *);
Expand Down

0 comments on commit 30050de

Please sign in to comment.