чување 4884068e586b9ebd5825af5ed3d13d0f47cd6b92
родитељ fd02c742e936c73af8ef729d32d01201b999245c
Аутор: Страхиња Радић <contact@strahinja.org>
Датум: Fri, 7 Jun 2024 22:24:35 +0200
merge refs/heads/dynsize into refs/heads/master
Diffstat:
измењених датотека: 12, додавања: 621(+), брисања: 228(-)
diff --git a/Makefile b/Makefile
@@ -29,15 +29,15 @@ makedate:
makeversion:
sh lib/makeversion -l
-sled.o: sled.c config.h draw.h termbox.h util.h version.h
+sled.o: sled.c config.h draw.h limit.h termbox.h util.h version.h
$(MANPAGE): $(MANPAGE).in date version
version.h: version.h.in date version
-draw.o: draw.c config.h draw.h termbox.h util.h version.h
+draw.o: draw.c config.h draw.h limit.h termbox.h util.h version.h
-util.o: util.c termbox.h util.h
+util.o: util.c limit.h termbox.h util.h
$(PROG): $(OBJS) $(SRC) $(HEADERS)
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
diff --git a/TODO b/TODO
@@ -1,6 +1,13 @@
TODO
====
+[ ] *str*cpy -> memccpy?
+
+[ ] Bugfix: cursor after paste when on last line
+
+< > Make allocation errors prompt to save the file before exiting?
+ - Would this make sense?
+
[~] Code optimization (struct coord...)
diff --git a/config.mk b/config.mk
@@ -7,9 +7,9 @@ CPPFLAGS = -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L \
# -D_XOPEN_SOURCE=700 -D_BSD_SOURCE
INSTALL = install
LIBS =
-SRC = draw.c sled.c strlcat.c strlcpy.c util.c
-HEADERS = config.h draw.h termbox.h util.h version.h
-OBJS = draw.o sled.o strlcat.o strlcpy.o util.o
+SRC = draw.c limit.c sled.c strlcat.c strlcpy.c util.c
+HEADERS = config.h draw.h limit.h termbox.h util.h version.h
+OBJS = draw.o limit.o sled.o strlcat.o strlcpy.o util.o
PREFIX = /usr/local
PROG = sled
diff --git a/draw.c b/draw.c
@@ -8,6 +8,7 @@
#include <string.h>
#include "version.h"
+#include "limit.h"
#include "util.h"
#include "draw.h"
#include "config.h"
@@ -77,6 +78,7 @@ init_bufferline(struct BufferLine* bl)
return;
bl->text = NULL;
bl->length = 0;
+ bl->size = 0;
bl->fg = 0;
bl->bg = 0;
}
@@ -136,11 +138,12 @@ init_drawstate(struct DrawState* state, char* error, char* action,
}
void
-free_bufferline(struct BufferLine* bl)
+free_bufferline(struct BufferLine* bl, const int free_text)
{
if (!bl)
return;
- free(bl->text);
+ if (free_text)
+ free(bl->text);
bl->text = NULL;
bl->length = 0;
bl->fg = 0;
@@ -152,14 +155,14 @@ free_drawstate(struct DrawState* state, int skip_paste_free)
{
size_t row = 0;
for (row = 0; row < state->buffer_size; row++)
- free_bufferline(state->buffer + row);
+ free_bufferline(state->buffer + row, 1);
free(state->backup_filename);
free(state->real_filename);
free(state->buffer);
if (!skip_paste_free)
{
for (row = 0; row < state->paste_size; row++)
- free_bufferline(state->paste + row);
+ free_bufferline(state->paste + row, 1);
free(state->paste);
}
free(state->input);
@@ -730,7 +733,7 @@ draw_screen(const struct DrawState* state)
const struct DrawState*
draw_status(const struct DrawState* state)
{
- char buf[MAXBUFLINE];
+ char* buf = NULL;
int seg_size;
int current_start = 0;
const struct StatusSegment* pseg;
@@ -769,13 +772,18 @@ draw_status(const struct DrawState* state)
{
int last = pseg - status_segments
== LEN(status_segments) - 1;
+
+ buf = calloc(limits[L_LINE_MAX], 1);
+ if (!buf)
+ return NULL;
+
buf[0] = 0;
if (pseg->callback)
- pseg->callback(buf, MAXBUFLINE, pseg->format,
- seg_size, state);
+ pseg->callback(buf, limits[L_LINE_MAX],
+ pseg->format, seg_size, state);
else
- strncpy(buf, pseg->format, MAXBUFLINE - 1);
- buf[MAXBUFLINE - 1] = 0;
+ strncpy(buf, pseg->format, limits[L_LINE_MAX]);
+ buf[limits[L_LINE_MAX] - 1] = 0;
if (pseg == status_segments + LEN(status_segments) - 1)
seg_size = state->maxx - 1 - current_start;
if (!draw_string(current_start, state->maxy - 1,
@@ -784,7 +792,11 @@ draw_status(const struct DrawState* state)
last ? seg_size + 1 : seg_size, 1, 1,
pseg->alignment, 0, 1, NULL, 0, 0, 0, 0, 0,
0))
+ {
+ free(buf);
return NULL;
+ }
+ free(buf);
pseg++;
current_start += seg_size;
}
diff --git a/draw.h b/draw.h
@@ -21,7 +21,6 @@
#define INPUT_DLG_WIDTH 40
#define INPUT_DLG_HEIGHT 4
#define TAB_SIZE 8
-#define MAXINPUT (512 + 1)
#define M_NONE 0
#define M_COMMAND 1
@@ -48,7 +47,8 @@ struct Coord {
struct BufferLine {
uint32_t* text;
- size_t length;
+ size_t length; /* both in uint32_t units */
+ size_t size;
uint16_t fg;
uint16_t bg;
};
@@ -113,7 +113,7 @@ void init_bufferline(struct BufferLine* bl);
void init_coord(struct Coord* c, const int col, const int row);
void init_drawstate(struct DrawState* state, char* error, char* action,
char* filename, char* prompt, int skip_paste_and_mode_init);
-void free_bufferline(struct BufferLine* bl);
+void free_bufferline(struct BufferLine* bl, const int free_text);
void free_drawstate(struct DrawState* state, int skip_paste_free);
void assign_coord(struct Coord* dest, const struct Coord src);
size_t display_length(const struct DrawState* state, uint32_t* buffer,
diff --git a/limit.c b/limit.c
@@ -0,0 +1,60 @@
+/* This program is licensed under the terms of GNU GPL v3 or (at your option)
+ * any later version. Copyright (C) 2022-2024 Страхиња Радић.
+ * See the file LICENSE for exact copyright and license details. */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "limit.h"
+
+long limits[L_LIMIT_COUNT];
+
+#ifndef LINE_MAX
+#define LINE_MAX _POSIX_LINK_MAX
+#endif
+
+#ifndef PATH_MAX
+#define PATH_MAX _POSIX_PATH_MAX
+#endif
+
+int
+init_limits(const char* dir)
+{
+ int cwdfd;
+ int result = 0;
+
+ errno = 0;
+
+ if ((cwdfd = open(dir, O_RDONLY)) < 0)
+ return -1;
+
+ limits[L_PATH_MAX] = fpathconf(cwdfd, _PC_PATH_MAX);
+ if (limits[L_PATH_MAX] == -1)
+ {
+ if (errno)
+ {
+ result = -1;
+ goto init_limits_cleanup;
+ }
+ limits[L_PATH_MAX] = PATH_MAX;
+ }
+
+ limits[L_LINE_MAX] = sysconf(_SC_LINE_MAX);
+ if (limits[L_LINE_MAX] == -1)
+ {
+ if (errno)
+ {
+ result = -1;
+ goto init_limits_cleanup;
+ }
+ limits[L_LINE_MAX] = LINE_MAX;
+ }
+
+init_limits_cleanup:
+ close(cwdfd);
+
+ return result;
+}
diff --git a/limit.h b/limit.h
@@ -0,0 +1,21 @@
+/* This program is licensed under the terms of GNU GPL v3 or (at your option)
+ * any later version. Copyright (C) 2022-2024 Страхиња Радић.
+ * See the file LICENSE for exact copyright and license details. */
+
+/* Limits */
+/* clang-format off */
+enum {
+ L_PATH_MAX = 0,
+ L_LINE_MAX = 1,
+ L_LIMIT_COUNT = 2
+};
+/* clang-format on */
+
+extern long limits[];
+
+#define ALLOC_DELTA 50
+#define LINE_ALLOC_DELTA 85
+#define LINE_DEFAULT 85
+#define MAXINPUT (512 + 1)
+
+int init_limits(const char* dir);
diff --git a/sled.c b/sled.c
@@ -31,6 +31,7 @@
#define ONLY_DEFS
#include "config.h"
#include "version.h"
+#include "limit.h"
#include "util.h"
#include "draw.h"
#undef ONLY_DEFS
@@ -72,7 +73,7 @@ extern const char* program_name;
int clear_selection(struct DrawState* state);
int cp(const char* from, const char* to);
int create_file(struct DrawState* dstate);
-char* dirname(char* path);
+char* dirname(char* dir, char* path, const size_t path_size);
int do_search_replace(struct DrawState* state);
int erase_selection(struct DrawState* state);
void get_selection(int* ssr, int* ssc, int* ser, int* sec,
@@ -181,12 +182,13 @@ create_file(struct DrawState* state)
for (size_t row = 0; row < ALLOC_DELTA; row++)
{
init_bufferline(newchunk + row);
- newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
+ newtext = calloc(LINE_DEFAULT, sizeof(uint32_t));
if (!newtext)
{
state->running = 0;
return LOAD_ERR_CANT_ALLOC;
}
+ newchunk[row].size = LINE_DEFAULT;
newchunk[row].text = newtext;
}
state->buffer = newchunk;
@@ -199,59 +201,77 @@ create_file(struct DrawState* state)
}
char*
-dirname(char* path)
+dirname(char* dir, char* path, const size_t dir_size)
{
- static char* result = NULL;
- static const char* dot = ".";
- char* p = NULL;
- char* slash = NULL;
+ char* p = NULL;
- if (!path || !*path)
+ if (!dir || !path || !*path)
return NULL;
- free(result);
- result = malloc(MAXPATH);
- memset(result, 0, MAXPATH);
+ MEMCCPY(path, dir, dir_size, p);
- STRLCPY(path, result, MAXPATH);
- p = result + strlen(result) - 1;
- if (*p == '/')
+ if (p > dir + 2 && *(p - 2) == '/')
*p = 0;
- slash = strrchr(result, '/');
- if (!slash)
- return (char*)dot;
-
- *slash = 0;
-
- return result;
+ while (p != dir && *p != '/')
+ {
+ p--;
+ if (*p == '/')
+ {
+ *p = 0;
+ return dir;
+ }
+ }
+ dir[0] = '.';
+ dir[1] = 0;
+ return dir;
}
int
do_search_replace(struct DrawState* state)
{
- uint32_t buf[MAXBUFLINE];
+ uint32_t* buf = NULL;
+ uint32_t* temp = NULL;
+ /* minimum size is LINE_DEFAULT to avoid 0-allocation */
+ size_t bufsize = LINE_DEFAULT; /* size in uint32_t units */
+ size_t buflen = 0;
size_t search_len = u32_strlen(state->search);
int start_row = 0;
int end_row = state->rows_count;
+ uint32_t* before = NULL;
+ uint32_t* found = NULL;
+ uint32_t* after = NULL;
if (SEL_NOT_EMPTY(state))
get_selection(&start_row, NULL, &end_row, NULL, state);
for (int row = start_row; row < end_row; row++)
+ if (state->buffer[row].size > bufsize)
+ bufsize = state->buffer[row].size;
+
+ buf = malloc(bufsize * sizeof(uint32_t));
+ if (!buf)
+ goto do_search_replace_error;
+
+ for (int row = start_row; row < end_row; row++)
{
- const uint32_t* before = state->buffer[row].text;
- uint32_t* found = (uint32_t*)u32_strstr(before, state->search);
- const uint32_t* after = before;
+ before = state->buffer[row].text;
+ found = (uint32_t*)u32_strstr(before, state->search);
+ after = before;
*buf = 0;
+ buflen = 0;
while (found)
{
state->dirty = 1;
*found = 0;
- u32_strncat(buf, before, MAXBUFLINE);
- u32_strncat(buf, state->input, MAXBUFLINE);
+ buflen += u32_strlen(before) + u32_strlen(state->input);
+
+ ENSURE_SIZE(buf, temp, bufsize, buflen + 1, buflen + 1,
+ do_search_replace_error, uint32_t);
+ u32_strncat(buf, before, bufsize);
+ u32_strncat(buf, state->input, bufsize);
before = found + search_len;
found = (uint32_t*)u32_strstr(before, state->search);
if (found)
@@ -260,12 +280,25 @@ do_search_replace(struct DrawState* state)
after = before;
}
if (state->buffer[row].text + state->buffer[row].length > after)
- u32_strncat(buf, after, MAXBUFLINE);
+ {
+ buflen += u32_strlen(after);
+ ENSURE_SIZE(buf, temp, bufsize, buflen + 1, buflen + 1,
+ do_search_replace_error, uint32_t);
+ u32_strncat(buf, after, bufsize);
+ }
- u32_strncpy(state->buffer[row].text, buf, MAXBUFLINE);
+ U32_SAFE_COPY(state->buffer[row].text, temp,
+ state->buffer[row].size, buf, bufsize,
+ do_search_replace_error);
state->buffer[row].length = u32_strlen(buf);
}
+ free(buf);
return 0;
+
+do_search_replace_error:
+ exit(print_error(ENOMEM, "Memory allocation failed"));
+ /* Unreachable */
+ return -1;
}
int
@@ -277,20 +310,21 @@ erase_selection(struct DrawState* state)
int ssr = 0;
int ssc = 0;
/* Selection end row/col */
- int ser = 0;
- int sec = 0;
- uint32_t* buf = NULL;
+ int ser = 0;
+ int sec = 0;
+ uint32_t* buf = NULL;
+ size_t bufsize = state->buffer[ser].size;
- buf = calloc(MAXBUFLINE, sizeof(uint32_t));
+ buf = calloc(bufsize, sizeof(uint32_t));
if (!buf)
exit(print_error(ENOMEM, "Memory allocation failed"));
get_selection(&ssr, &ssc, &ser, &sec, state);
- u32_strncpy(buf, state->buffer[ser].text + sec, MAXBUFLINE);
+ u32_strncpy(buf, state->buffer[ser].text + sec, bufsize);
*(state->buffer[ssr].text + ssc) = 0;
- u32_strncat(state->buffer[ssr].text, buf, MAXBUFLINE);
+ u32_strncat(state->buffer[ssr].text, buf, bufsize);
state->buffer[ssr].length = u32_strlen(state->buffer[ssr].text);
if (ssr != ser)
@@ -300,7 +334,7 @@ erase_selection(struct DrawState* state)
{
u32_strncpy(state->buffer[ssr + row + 1].text,
state->buffer[ser + row + 1].text,
- MAXBUFLINE);
+ bufsize);
state->buffer[ssr + row + 1].length
= state->buffer[ser + row + 1].length;
}
@@ -385,7 +419,7 @@ go_to_line_callback(struct DrawState* state)
if (!u8_input)
{
STRLCPY(errors[ERR_CANT_ALLOC], state->error,
- MAXBUFLINE);
+ LINE_DEFAULT);
state->running = 0;
return;
}
@@ -394,7 +428,7 @@ go_to_line_callback(struct DrawState* state)
lineno = strtoul(u8_input, NULL, 10);
if (errno)
STRLCPY(errors[ERR_INVALID_NUM], state->error,
- MAXBUFLINE);
+ LINE_DEFAULT);
if (!*state->error)
{
if (lineno > state->rows_count - 1)
@@ -462,7 +496,7 @@ handle_key_event(struct tb_event* ev, struct DrawState* state)
input_insert_char(state, ev->ch);
else
STRLCPY(errors[ERR_INPUT_NONNUM], state->error,
- MAXBUFLINE);
+ LINE_DEFAULT);
return;
}
}
@@ -479,7 +513,7 @@ handle_key_event(struct tb_event* ev, struct DrawState* state)
if (IN(state->mode, M_HELP))
{
- STRLCPY(errors[ERR_DLG_OPEN], state->error, MAXBUFLINE);
+ STRLCPY(errors[ERR_DLG_OPEN], state->error, LINE_DEFAULT);
return;
}
@@ -527,7 +561,7 @@ handle_key_event(struct tb_event* ev, struct DrawState* state)
}
}
- STRLCPY(errors[ERR_UNKNOWN_KEY], state->error, MAXBUFLINE);
+ STRLCPY(errors[ERR_UNKNOWN_KEY], state->error, LINE_DEFAULT);
}
int
@@ -577,10 +611,14 @@ insert_char(struct DrawState* state, uint32_t ch)
struct BufferLine* buffer_line = NULL;
size_t i;
int add_null;
- int len;
+ size_t len;
+ size_t size;
+ uint32_t* ptext = NULL;
- if (state->coord.col >= MAXBUFLINE - 1)
- return 1;
+ ENSURE_SIZE(state->buffer[state->coord.row].text, ptext,
+ state->buffer[state->coord.row].size, state->coord.col + 1,
+ state->coord.col + LINE_ALLOC_DELTA, insert_char_error,
+ uint32_t);
if (SEL_NOT_EMPTY(state))
{
@@ -592,10 +630,11 @@ insert_char(struct DrawState* state, uint32_t ch)
buffer_line = &state->buffer[state->coord.row];
add_null = buffer_line->text[state->coord.col] == 0;
len = buffer_line->length;
+ size = buffer_line->size;
i = len + 1;
if (!IN(state->mode, M_OVERWRITE))
- while (i < MAXBUFLINE - 2 && i > state->coord.col)
+ while (i < size - 1 && i > state->coord.col)
{
buffer_line->text[i] = buffer_line->text[i - 1];
i--;
@@ -634,6 +673,9 @@ insert_char(struct DrawState* state, uint32_t ch)
state->dirty = 1;
return 0;
+
+insert_char_error:
+ return -1;
}
int
@@ -648,6 +690,7 @@ join_lines(struct DrawState* state)
int saved_row = state->coord.row;
struct BufferLine* current = NULL;
struct BufferLine* next = NULL;
+ uint32_t* ptext = NULL;
if (SEL_NOT_EMPTY(state))
{
@@ -670,25 +713,26 @@ join_lines(struct DrawState* state)
{
current = state->buffer + ssr;
next = state->buffer + ssr + 1;
- if (current->length + 2 < MAXBUFLINE)
+ ENSURE_SIZE(current->text, ptext, current->size,
+ current->length + 3, current->length + LINE_ALLOC_DELTA,
+ join_lines_error, uint32_t);
+ state->coord.row = ssr;
+ if (current->length > 0
+ && !u32_is_word_boundary(current->text[current->length
+ - 1],
+ 1)
+ && !u32_is_word_boundary(next->text[0], 1))
{
- state->coord.row = ssr;
- if (current->length > 0
- && !u32_is_word_boundary(
- current->text[current->length - 1], 1)
- && !u32_is_word_boundary(next->text[0], 1))
- {
- current->text[current->length] = ' ';
- current->text[current->length + 1] = 0;
- current->length++;
- state->coord.col = current->length - 1;
- }
- else
- state->coord.col = current->length;
- state->display_column = display_length(state,
- current->text, state->coord.col);
- simple_join_lines(state);
+ current->text[current->length] = ' ';
+ current->text[current->length + 1] = 0;
+ current->length++;
+ state->coord.col = current->length - 1;
}
+ else
+ state->coord.col = current->length;
+ state->display_column = display_length(state, current->text,
+ state->coord.col);
+ simple_join_lines(state);
}
state->coord.row = saved_row;
@@ -696,6 +740,9 @@ join_lines(struct DrawState* state)
position_to(state, ssr, ssc);
return 0;
+
+join_lines_error:
+ return -1;
}
int
@@ -704,28 +751,38 @@ load_file(struct DrawState* state, long* lineno, long* col)
struct BufferLine* newchunk = NULL;
struct BufferLine* line = NULL;
FILE* input = NULL;
- char input_line[MAXBUFLINE];
- uint32_t* newtext = NULL;
+ char* input_line = NULL;
+ size_t input_size = limits[L_LINE_MAX];
+ size_t input_len = 0;
+ uint32_t* newtext = NULL;
+ uint32_t* ptext = NULL;
+ input_line = calloc(input_size, 1);
+ if (!input_line)
+ {
+ state->running = 0;
+ goto load_file_alloc_error;
+ }
newchunk = calloc(ALLOC_DELTA, sizeof(struct BufferLine));
if (!newchunk)
{
state->running = 0;
- return LOAD_ERR_CANT_ALLOC;
+ goto load_file_alloc_error;
}
for (size_t row = 0; row < ALLOC_DELTA; row++)
{
init_bufferline(newchunk + row);
- newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
+ newtext = calloc(LINE_DEFAULT, sizeof(uint32_t));
if (!newtext)
{
state->running = 0;
- return LOAD_ERR_CANT_ALLOC;
+ goto load_file_alloc_error;
}
+ newchunk[row].size = LINE_DEFAULT;
newchunk[row].text = newtext;
}
- STRLCPY(actions[ACT_READING], state->action, MAXBUFLINE);
+ STRLCPY(actions[ACT_READING], state->action, LINE_DEFAULT);
redraw_status(state);
@@ -740,7 +797,7 @@ load_file(struct DrawState* state, long* lineno, long* col)
{
char* eol = NULL;
- if (!fgets(input_line, MAXBUFLINE, input))
+ if (!fgets(input_line, input_size, input))
continue;
eol = strchr(input_line, '\n');
if (eol)
@@ -754,25 +811,31 @@ load_file(struct DrawState* state, long* lineno, long* col)
if (!newchunk)
{
state->running = 0;
- return LOAD_ERR_CANT_ALLOC;
+ goto load_file_alloc_error;
}
state->buffer = newchunk;
for (size_t row = state->buffer_size - ALLOC_DELTA;
row < state->buffer_size; row++)
{
init_bufferline(state->buffer + row);
- newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
+ newtext = calloc(LINE_DEFAULT, sizeof(uint32_t));
if (!newtext)
{
state->running = 0;
- return LOAD_ERR_CANT_ALLOC;
+ goto load_file_alloc_error;
}
+ state->buffer[row].size = LINE_DEFAULT;
state->buffer[row].text = newtext;
}
}
- line = state->buffer + state->rows_count;
+ line = state->buffer + state->rows_count;
+ input_len = strlen(input_line);
+ ENSURE_SIZE(line->text, ptext, line->size, input_len + 1,
+ input_len + LINE_ALLOC_DELTA, load_file_alloc_error,
+ uint32_t);
+
line->length
- = u32_u8_strncpy(line->text, input_line, MAXBUFLINE);
+ = u32_u8_strncpy(line->text, input_line, line->size);
state->rows_count++;
}
fclose(input);
@@ -781,7 +844,12 @@ load_file(struct DrawState* state, long* lineno, long* col)
if (state->rows_count == 0)
state->rows_count++;
+ free(input_line);
return LOAD_ERR_NONE;
+
+load_file_alloc_error:
+ free(input_line);
+ return LOAD_ERR_CANT_ALLOC;
}
/*
@@ -887,10 +955,10 @@ position_to_selection_start(struct DrawState* state)
int
print_error(const int code, const char* msg, ...)
{
- char buf[MAXBUFLINE];
+ char buf[LINE_DEFAULT];
va_list args;
va_start(args, msg);
- vsnprintf(buf, MAXBUFLINE, msg, args);
+ vsnprintf(buf, LINE_DEFAULT, msg, args);
va_end(args);
fprintf(stderr, "%s: %s\n", program_name, buf);
return code;
@@ -922,11 +990,11 @@ reread_file(struct DrawState* state)
switch (result)
{
case LOAD_ERR_CANT_ALLOC:
- STRLCPY(errors[ERR_CANT_ALLOC], state->error, MAXBUFLINE);
+ STRLCPY(errors[ERR_CANT_ALLOC], state->error, LINE_DEFAULT);
state->running = 0;
break;
case LOAD_ERR_CANT_OPEN_FILE:
- STRLCPY(errors[ERR_CANT_OPEN], state->error, MAXBUFLINE);
+ STRLCPY(errors[ERR_CANT_OPEN], state->error, LINE_DEFAULT);
state->running = 0;
break;
default:;
@@ -948,17 +1016,19 @@ quit_callback(struct DrawState* state, struct tb_event* ev)
int
save_file(struct DrawState* state)
{
- FILE* output = NULL;
- char* buf = NULL;
+ FILE* output = NULL;
+ char* buf = NULL;
+ char* pbuf = NULL;
+ size_t bufsize = LINE_DEFAULT;
#ifdef CREATE_BACKUPS
struct stat st;
#endif
int result = SAVE_ERR_NONE;
- if (!(buf = calloc(MAXBUFLINE, 1)))
+ if (!(buf = calloc(bufsize, 1)))
return SAVE_ERR_CANT_ALLOC;
- STRLCPY(actions[ACT_WRITING], state->action, MAXBUFLINE);
+ STRLCPY(actions[ACT_WRITING], state->action, LINE_DEFAULT);
redraw_status(state);
errno = 0;
@@ -970,7 +1040,10 @@ save_file(struct DrawState* state)
if (state->backup_possible)
{
if (unlink(state->backup_filename) < 0 && errno != ENOENT)
- return SAVE_ERR_CANT_COPY_FILE;
+ {
+ result = SAVE_ERR_CANT_COPY_FILE;
+ goto save_file_cleanup;
+ }
errno = 0;
cp(state->rfn_len > 0 ? state->real_filename : state->filename,
state->backup_filename);
@@ -1058,11 +1131,18 @@ save_skip_backup_symlink:
for (size_t row = 0; row < state->rows_count; row++)
{
- unicode_string_to_u8(buf, state->buffer[row].text, MAXBUFLINE);
+ ENSURE_SIZE(buf, pbuf, bufsize, state->buffer[row].length + 1,
+ state->buffer[row].length + LINE_ALLOC_DELTA,
+ save_file_alloc_error, char);
+ unicode_string_to_u8(buf, state->buffer[row].text, bufsize);
fprintf(output, "%s\n", buf);
}
state->new_file = 0;
+ goto save_file_cleanup;
+
+save_file_alloc_error:
+ result = SAVE_ERR_CANT_ALLOC;
save_file_cleanup:
fclose(output);
@@ -1104,7 +1184,7 @@ search_replace_replace_dialog_callback(struct DrawState* state)
void
search_update_callback(struct DrawState* state)
{
- u32_strncpy(state->search, state->input, MAXBUFLINE);
+ u32_strncpy(state->search, state->input, MAXINPUT);
}
int
@@ -1116,7 +1196,8 @@ cancel_close(struct DrawState* state)
if (SEL_NOT_EMPTY(state))
clear_selection(state);
else
- STRLCPY(errors[ERR_EXIT_KEY], state->error, MAXBUFLINE);
+ STRLCPY(errors[ERR_EXIT_KEY], state->error,
+ LINE_DEFAULT);
}
else
{
@@ -1207,7 +1288,7 @@ erase_forward(struct DrawState* state)
if (buffer_line->text[state->coord.col])
{
size_t i = state->coord.col;
- while (i < MAXBUFLINE - 2 && buffer_line->text[i + 1])
+ while (i + 2 < buffer_line->size && buffer_line->text[i + 1])
{
buffer_line->text[i] = buffer_line->text[i + 1];
i++;
@@ -1218,11 +1299,8 @@ erase_forward(struct DrawState* state)
}
else if (state->coord.row < state->rows_count - 1)
{
- struct BufferLine* current = state->buffer + state->coord.row;
- if (state->coord.row == state->rows_count - 1
- || current->length
- + state->buffer[state->coord.row + 1].length
- >= MAXBUFLINE)
+ // struct BufferLine* current = state->buffer + state->coord.row;
+ if (state->coord.row + 1 == state->rows_count)
goto skip_join_lines;
simple_join_lines(state);
skip_join_lines:;
@@ -1240,14 +1318,15 @@ erase_line(struct DrawState* state)
state->buffer[0].length = 0;
return 0;
}
+ free(state->buffer[state->coord.row].text);
for (size_t row = state->coord.row; row < state->rows_count - 1; row++)
{
current = state->buffer + row;
- current->length = u32_strncpy(current->text,
- state->buffer[row + 1].text, MAXBUFLINE);
+ current->text = state->buffer[row + 1].text;
+ current->size = state->buffer[row + 1].size;
+ current->length = state->buffer[row + 1].length;
}
- *state->buffer[state->rows_count - 1].text = 0;
- state->buffer[state->rows_count - 1].length = 0;
+ free_bufferline(&state->buffer[state->rows_count - 1], 0);
if (state->coord.row == state->rows_count - 1)
state->coord.row--;
position_to(state, state->coord.row, 0);
@@ -1340,7 +1419,7 @@ exit_program(struct DrawState* state)
if (state->dirty)
{
SET(state->mode, M_PROMPT);
- STRLCPY(prompt_dirty, state->prompt, MAXBUFLINE);
+ STRLCPY(prompt_dirty, state->prompt, LINE_DEFAULT);
state->prompt_callback = quit_callback;
}
else
@@ -1416,7 +1495,7 @@ input_erase_forward(struct DrawState* state)
if (state->input)
{
size_t i = state->input_column;
- while (i < MAXBUFLINE - 2 && state->input[i + 1])
+ while (i + 2 < MAXINPUT && state->input[i + 1])
{
state->input[i] = state->input[i + 1];
i++;
@@ -1433,7 +1512,7 @@ input_erase_to_end(struct DrawState* state)
{
state->input[state->input_column] = 0;
input_move_end(state);
- u32_strncpy(state->search, state->input, MAXBUFLINE);
+ u32_strncpy(state->search, state->input, MAXINPUT);
if (state->input_update_callback)
state->input_update_callback(state);
return 0;
@@ -1772,9 +1851,12 @@ int
paste_from_paste(struct DrawState* state)
{
uint32_t* newtext = NULL;
+ uint32_t* ptext = NULL;
+ size_t newsize = 0;
struct BufferLine* line = NULL;
struct BufferLine* newbuf = NULL;
uint32_t* rest = NULL;
+ size_t rest_size = 0;
if (!state->paste_count)
return 0;
@@ -1800,7 +1882,8 @@ paste_from_paste(struct DrawState* state)
row++)
{
init_bufferline(newbuf + row);
- newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
+ newtext = calloc(LINE_DEFAULT, sizeof(uint32_t));
+ newbuf[row].size = LINE_DEFAULT;
newbuf[row].text = newtext;
}
state->buffer = newbuf;
@@ -1808,59 +1891,77 @@ paste_from_paste(struct DrawState* state)
}
line = state->buffer + state->coord.row;
- newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
+ newsize = line->size + state->paste[0].length + 1;
+ newtext = calloc(newsize, sizeof(uint32_t));
if (!newtext)
{
state->running = 0;
- STRLCPY(errors[ERR_CANT_ALLOC], state->error, MAXBUFLINE);
+ STRLCPY(errors[ERR_CANT_ALLOC], state->error, LINE_DEFAULT);
goto paste_from_paste_cleanup;
}
u32_strncpy(newtext, line->text, state->coord.col + 1);
- u32_strncat(newtext, state->paste[0].text, MAXBUFLINE);
+ u32_strncat(newtext, state->paste[0].text, newsize);
if (state->paste_count == 1)
{
- u32_strncat(newtext, line->text + state->coord.col, MAXBUFLINE);
- line->length = u32_strncpy(line->text, newtext, MAXBUFLINE);
+ u32_strncat(newtext, line->text + state->coord.col, newsize);
+ line->length = u32_strncpy(line->text, newtext, newsize);
+ line->size = newsize;
state->coord.col += state->paste[state->paste_count - 1].length;
}
else
{
state->rows_count += state->paste_count - 1;
- rest = calloc(MAXBUFLINE, sizeof(uint32_t));
+ rest_size = line->size - state->coord.col;
+ rest = calloc(rest_size, sizeof(uint32_t));
if (!rest)
{
state->running = 0;
STRLCPY(errors[ERR_CANT_ALLOC], state->error,
- MAXBUFLINE);
+ LINE_DEFAULT);
goto paste_from_paste_cleanup;
}
- u32_strncpy(rest, line->text + state->coord.col, MAXBUFLINE);
- line->length = u32_strncpy(line->text, newtext, MAXBUFLINE);
+ u32_strncpy(rest, line->text + state->coord.col, rest_size);
+ U32_SAFE_COPY(line->text, ptext, line->size, newtext, newsize,
+ paste_from_paste_cleanup);
+ line->length = u32_strlen(line->text);
for (size_t row = state->rows_count - state->coord.row
- (state->paste_count - 2) - 1;
row > 0; row--)
{
+ struct BufferLine* copied_line
+ = &state->buffer[state->coord.row + row - 1];
+
line = state->buffer + state->coord.row
+ state->paste_count - 2 + row;
- line->length = u32_strncpy(line->text,
- state->buffer[state->coord.row + row - 1].text,
- MAXBUFLINE);
+ U32_SAFE_COPY(line->text, ptext, line->size,
+ copied_line->text, copied_line->size,
+ paste_from_paste_cleanup);
+ line->length = u32_strlen(line->text);
}
- line = state->buffer + state->coord.row;
- line->length = u32_strncpy(line->text, newtext, MAXBUFLINE);
+ line = state->buffer + state->coord.row;
+ U32_SAFE_COPY(line->text, ptext, line->size, newtext, newsize,
+ paste_from_paste_cleanup);
+ line->length = u32_strlen(line->text);
for (size_t row = 1; row < state->paste_count - 1; row++)
{
- line = state->buffer + state->coord.row + row;
- line->length = u32_strncpy(line->text,
- state->paste[row].text, MAXBUFLINE);
+ line = state->buffer + state->coord.row + row;
+ U32_SAFE_COPY(line->text, ptext, line->size,
+ state->paste[row].text, state->paste[row].size,
+ paste_from_paste_cleanup);
+ line->length = u32_strlen(line->text);
}
line = state->buffer + state->coord.row + state->paste_count
- 1;
- u32_strncpy(newtext, state->paste[state->paste_count - 1].text,
- MAXBUFLINE);
- u32_strncat(newtext, rest, MAXBUFLINE);
- line->length = u32_strncpy(line->text, newtext, MAXBUFLINE);
+ U32_SAFE_COPY(newtext, ptext, newsize,
+ state->paste[state->paste_count - 1].text,
+ state->paste[state->paste_count - 1].size + rest_size
+ + 1,
+ paste_from_paste_cleanup);
+ u32_strncat(newtext, rest, newsize);
+ U32_SAFE_COPY(line->text, ptext, line->size, newtext, newsize,
+ paste_from_paste_cleanup);
+ line->length = u32_strlen(line->text);
state->coord.col = state->paste[state->paste_count - 1].length;
state->coord.row += state->paste_count - 1;
}
@@ -1928,7 +2029,7 @@ reread_or_callback(struct DrawState* state)
if (state->dirty)
{
SET(state->mode, M_PROMPT);
- STRLCPY(prompt_reread_dirty, state->prompt, MAXBUFLINE);
+ STRLCPY(prompt_reread_dirty, state->prompt, LINE_DEFAULT);
state->prompt_callback = reread_callback;
}
else
@@ -1947,7 +2048,7 @@ show_go_to_line(struct DrawState* state)
{
state->running = 0;
STRLCPY(errors[ERR_CANT_ALLOC], state->error,
- MAXBUFLINE);
+ LINE_DEFAULT);
return 0;
}
}
@@ -1970,7 +2071,7 @@ show_replace(struct DrawState* state)
{
state->running = 0;
STRLCPY(errors[ERR_CANT_ALLOC], state->error,
- MAXBUFLINE);
+ LINE_DEFAULT);
return 0;
}
}
@@ -1991,7 +2092,7 @@ show_search(struct DrawState* state)
{
state->running = 0;
STRLCPY(errors[ERR_CANT_ALLOC], state->error,
- MAXBUFLINE);
+ LINE_DEFAULT);
return 0;
}
}
@@ -2005,7 +2106,7 @@ show_search(struct DrawState* state)
{
state->running = 0;
STRLCPY(errors[ERR_CANT_ALLOC], state->error,
- MAXBUFLINE);
+ LINE_DEFAULT);
return 0;
}
}
@@ -2026,7 +2127,7 @@ show_search_replace(struct DrawState* state)
{
state->running = 0;
STRLCPY(errors[ERR_CANT_ALLOC], state->error,
- MAXBUFLINE);
+ LINE_DEFAULT);
return 0;
}
}
@@ -2040,7 +2141,7 @@ show_search_replace(struct DrawState* state)
{
state->running = 0;
STRLCPY(errors[ERR_CANT_ALLOC], state->error,
- MAXBUFLINE);
+ LINE_DEFAULT);
return 0;
}
}
@@ -2066,7 +2167,7 @@ simple_insert_line(struct DrawState* state)
{
struct BufferLine* newchunk = NULL;
struct BufferLine* line = NULL;
- uint32_t* newtext = NULL;
+ uint32_t* ptext = NULL;
if (state->rows_count == state->buffer_size)
{
@@ -2077,7 +2178,7 @@ simple_insert_line(struct DrawState* state)
{
state->running = 0;
STRLCPY(errors[ERR_CANT_ALLOC], state->error,
- MAXBUFLINE);
+ LINE_DEFAULT);
return;
}
state->buffer = newchunk;
@@ -2086,52 +2187,69 @@ simple_insert_line(struct DrawState* state)
{
line = state->buffer + row;
init_bufferline(line);
- newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
- if (!newtext)
- {
- state->running = 0;
- STRLCPY(errors[ERR_CANT_ALLOC], state->error,
- MAXBUFLINE);
- return;
- }
- line->text = newtext;
+ ENSURE_SIZE(line->text, ptext, line->size, LINE_DEFAULT,
+ LINE_DEFAULT, simple_insert_line_error,
+ uint32_t);
}
}
state->rows_count++;
for (size_t i = state->rows_count - 1; i > state->coord.row + 1; i--)
- state->buffer[i].length = u32_strncpy(state->buffer[i].text,
- state->buffer[i - 1].text, MAXBUFLINE);
+ {
+ U32_SAFE_COPY(state->buffer[i].text, ptext,
+ state->buffer[i].size, state->buffer[i - 1].text,
+ state->buffer[i - 1].size, simple_insert_line_error);
+ state->buffer[i].length = u32_strlen(state->buffer[i].text);
+ }
+ U32_SAFE_COPY(state->buffer[state->coord.row + 1].text, ptext,
+ state->buffer[state->coord.row + 1].size,
+ state->buffer[state->coord.row].text + state->coord.col,
+ state->buffer[state->coord.row].size, simple_insert_line_error);
state->buffer[state->coord.row + 1].length
- = u32_strncpy(state->buffer[state->coord.row + 1].text,
- state->buffer[state->coord.row].text + state->coord.col,
- MAXBUFLINE);
+ = u32_strlen(state->buffer[state->coord.row + 1].text);
state->buffer[state->coord.row].text[state->coord.col] = 0;
state->buffer[state->coord.row].length = state->coord.col;
state->dirty = 1;
+ return;
+
+simple_insert_line_error:
+ return;
}
void
simple_join_lines(struct DrawState* state)
{
size_t first_line = state->coord.row;
+ uint32_t* ptext = NULL;
if (first_line == state->rows_count - 1)
return;
+ ENSURE_SIZE(state->buffer[first_line].text, ptext,
+ state->buffer[first_line].size,
+ state->buffer[first_line].length
+ + state->buffer[first_line + 1].length + 1,
+ state->buffer[first_line].length
+ + state->buffer[first_line + 1].length + 1,
+ simple_join_lines_error, uint32_t);
u32_strncat(state->buffer[first_line].text,
- state->buffer[first_line + 1].text, MAXBUFLINE);
+ state->buffer[first_line + 1].text,
+ state->buffer[first_line].size);
state->buffer[first_line].length
= u32_strlen(state->buffer[first_line].text);
for (size_t i = first_line + 1; i < state->rows_count - 1; i++)
{
- u32_strncpy(state->buffer[i].text, state->buffer[i + 1].text,
- MAXBUFLINE);
+ U32_SAFE_COPY(state->buffer[i].text, ptext,
+ state->buffer[i].size, state->buffer[i + 1].text,
+ state->buffer[i + 1].size, simple_join_lines_error);
state->buffer[i].length = state->buffer[i + 1].length;
}
- *state->buffer[state->rows_count - 1].text = 0;
- state->buffer[state->rows_count - 1].length = 0;
+ free_bufferline(&state->buffer[state->rows_count - 1], 1);
state->rows_count--;
state->dirty = 1;
+ return;
+
+simple_join_lines_error:
+ return;
}
void
@@ -2145,6 +2263,7 @@ simple_yank(struct DrawState* state)
int sec = 0;
struct BufferLine* newbuf = NULL;
size_t initialize_start;
+ uint32_t* ptext = NULL;
if (!SEL_NOT_EMPTY(state))
{
@@ -2179,35 +2298,46 @@ simple_yank(struct DrawState* state)
newbuf = state->paste;
for (size_t row = initialize_start; row < state->paste_size; row++)
{
- uint32_t* newtext = NULL;
init_bufferline(newbuf + row);
- newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
- if (!newtext)
- {
- state->running = 0;
- STRLCPY(errors[ERR_CANT_ALLOC], state->error,
- MAXBUFLINE);
- }
- newbuf[row].text = newtext;
- newbuf[row].length = 0;
+ ENSURE_SIZE(newbuf[row].text, ptext, newbuf[row].size,
+ LINE_DEFAULT, LINE_DEFAULT, simple_yank_alloc_error,
+ uint32_t);
}
state->paste = newbuf;
if (state->paste_count == 1)
- state->paste[0].length = u32_strncpy(state->paste[0].text,
- state->buffer[ssr].text + ssc, sec - ssc + 1);
+ {
+ U32_SAFE_COPY(state->paste[0].text, ptext, state->paste[0].size,
+ state->buffer[ssr].text + ssc, sec - ssc + 1,
+ simple_yank_alloc_error);
+ state->paste[0].length = u32_strlen(state->paste[0].text);
+ }
else
{
- state->paste[0].length = u32_strncpy(state->paste[0].text,
- state->buffer[ssr].text + ssc, MAXBUFLINE);
+ U32_SAFE_COPY(state->paste[0].text, ptext, state->paste[0].size,
+ state->buffer[ssr].text + ssc, sec - ssc + 1,
+ simple_yank_alloc_error);
+ state->paste[0].length = u32_strlen(state->paste[0].text);
for (size_t row = 1; row < state->paste_count - 1; row++)
+ {
+ U32_SAFE_COPY(state->paste[row].text, ptext,
+ state->paste[row].size,
+ state->buffer[ssr + row].text,
+ state->buffer[ssr + row].size,
+ simple_yank_alloc_error);
state->paste[row].length
- = u32_strncpy(state->paste[row].text,
- state->buffer[ssr + row].text,
- MAXBUFLINE);
+ = u32_strlen(state->paste[row].text);
+ }
+ U32_SAFE_COPY(state->paste[state->paste_count - 1].text, ptext,
+ state->paste[state->paste_count - 1].size,
+ state->buffer[ser].text, sec + 1,
+ simple_yank_alloc_error);
state->paste[state->paste_count - 1].length
- = u32_strncpy(state->paste[state->paste_count - 1].text,
- state->buffer[ser].text, sec + 1);
+ = u32_strlen(state->paste[state->paste_count - 1].text);
}
+ return;
+
+simple_yank_alloc_error:
+ return;
}
int
@@ -2264,22 +2394,22 @@ write_file(struct DrawState* state)
switch (result)
{
case SAVE_ERR_CANT_ALLOC:
- STRLCPY(errors[ERR_CANT_ALLOC], state->error, MAXBUFLINE);
+ STRLCPY(errors[ERR_CANT_ALLOC], state->error, LINE_DEFAULT);
state->running = 0;
break;
case SAVE_ERR_CANT_OPEN_FILE:
- STRLCPY(errors[ERR_CANT_SAVE], state->error, MAXBUFLINE);
+ STRLCPY(errors[ERR_CANT_SAVE], state->error, LINE_DEFAULT);
state->running = 0;
break;
case SAVE_ERR_CANT_COPY_FILE:
- STRLCPY(errors[ERR_CANT_COPY], state->error, MAXBUFLINE);
+ STRLCPY(errors[ERR_CANT_COPY], state->error, LINE_DEFAULT);
state->running = 0;
break;
case SAVE_ERR_CANT_READLINK:
case SAVE_ERR_CANT_STAT_FILE:
case SAVE_ERR_CANT_SYMLINK:
case SAVE_ERR_CANT_UNLINK:
- STRLCPY(strerror(errno), state->error, MAXBUFLINE);
+ STRLCPY(strerror(errno), state->error, LINE_DEFAULT);
state->running = 0;
break;
case SAVE_ERR_NONE:
@@ -2304,23 +2434,24 @@ int
main(int argc, char** argv)
{
struct tb_event ev;
- char error[MAXBUFLINE];
- char action[MAXBUFLINE];
- char prompt[MAXBUFLINE];
+ char error[LINE_DEFAULT];
+ char action[LINE_DEFAULT];
+ char prompt[LINE_DEFAULT];
struct DrawState state;
- char filename[MAXPATH];
+ char* filename = NULL;
+ size_t filename_size = 0;
struct stat st;
long lineno = 1;
long col = 1;
int result;
int dir_access;
int dir_access_errno;
- char* dir = NULL;
+ char* dir = NULL;
+ char* pdir = NULL;
- *error = 0;
- *action = 0;
- *prompt = 0;
- *filename = 0;
+ *error = 0;
+ *action = 0;
+ *prompt = 0;
#ifdef __OpenBSD__
if (pledge("stdio tty unveil rpath wpath cpath fattr", NULL) < 0)
@@ -2334,7 +2465,8 @@ main(int argc, char** argv)
if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
{
- printf("Usage:\t%s -h | --help | -V | --full-version | -v |"
+ printf("Usage:\t%s -h | --help | -V | --full-version | "
+ "-v |"
" --version\n"
"\t%s filename\n",
program_name, program_name);
@@ -2368,12 +2500,40 @@ main(int argc, char** argv)
printf(" WRAP_WIDTH:\t\t%d\n", WRAP_WIDTH);
return 0;
}
- else
+
+ /*
+ * Chicken and egg problem: to get fpathconf(dirfd, _PC_PATH_MAX)
+ * we need a dir, which needs to be allocated, and to allocate it we
+ * need to know the value returned by fpathconf(dirfd, _PC_PATH_MAX)...
+ */
+ /* argv[1] is the filename; BTW, "filename" is a misnomer */
+ filename_size = strlen(argv[1]) + 1;
+ if (!(dir = malloc(filename_size)))
+ return print_error(1, "Allocation failed (out of memory?)");
+
+ (void)dirname(dir, argv[1], filename_size);
+ if (init_limits(dir) < 0)
{
- strncpy(filename, argv[1], MAXPATH - 1);
- filename[MAXPATH - 1] = 0;
- if (!*filename)
- return print_error(EINVAL, "No filename given");
+ perror("sled: init_limits");
+ free(dir);
+ exit(1);
+ }
+
+ filename = calloc(limits[L_PATH_MAX], 1);
+ if (!filename)
+ {
+ free(dir);
+ return print_error(1, "Allocation failed (out of memory?)");
+ }
+
+ *filename = 0;
+ strncpy(filename, argv[1], limits[L_PATH_MAX]);
+ filename[limits[L_PATH_MAX] - 1] = 0;
+ if (!*filename)
+ {
+ free(filename);
+ free(dir);
+ return print_error(EINVAL, "No filename given");
}
init_drawstate(&state, error, action, filename, prompt, 0);
@@ -2382,15 +2542,18 @@ main(int argc, char** argv)
if (unveil(state.filename, "rwc") < 0)
{
perror("sled: unveil");
+ free(filename);
+ free(dir);
exit(1);
}
#endif
- dir = dirname(state.filename);
#ifdef __OpenBSD__
if (unveil(dir, "rwc") < 0)
{
perror("sled: unveil");
+ free(filename);
+ free(dir);
exit(1);
}
#endif
@@ -2400,13 +2563,18 @@ main(int argc, char** argv)
{
if (S_ISLNK(st.st_mode))
{
- if (!(state.real_filename = calloc(MAXPATH, 1)))
+ if (!(state.real_filename
+ = calloc(limits[L_PATH_MAX], 1)))
+ {
+ free(filename);
+ free(dir);
return print_error(1,
"Allocation failed (out of "
"memory?)");
+ }
state.rfn_len = readlink(state.filename,
- state.real_filename, MAXPATH);
+ state.real_filename, limits[L_PATH_MAX]);
if (state.rfn_len == -1)
goto file_error;
state.real_filename[state.rfn_len] = 0;
@@ -2414,6 +2582,7 @@ main(int argc, char** argv)
if (unveil(state.real_filename, "rwc") < 0)
{
perror("sled: unveil");
+ free(filename);
exit(1);
}
#endif
@@ -2426,23 +2595,28 @@ main(int argc, char** argv)
else
goto file_error;
- if (!(state.backup_filename = calloc(MAXPATH, 1)))
+ if (!(state.backup_filename = calloc(limits[L_PATH_MAX], 1)))
{
free(state.real_filename);
+ free(filename);
+ free(dir);
return print_error(1, "Allocation failed (out of memory?)");
}
- /* Backup filename is the target file + suffix if the file to write
- * is not a new file and is a symlink, otherwise file + suffix */
+ /* Backup filename is the target file + suffix if the file to
+ * write is not a new file and is a symlink, otherwise file +
+ * suffix */
STRLCPY(state.new_file ? state.filename
: (state.rfn_len > 0 ? state.real_filename
: state.filename),
- state.backup_filename, MAXPATH);
- STRLCAT(backup_suffix, state.backup_filename, MAXPATH);
+ state.backup_filename, limits[L_PATH_MAX]);
+ STRLCAT(backup_suffix, state.backup_filename, limits[L_PATH_MAX]);
#ifdef __OpenBSD__
if (unveil(state.backup_filename, "rwc") < 0)
{
perror("sled: unveil");
+ free(filename);
+ free(dir);
exit(1);
}
#endif
@@ -2450,11 +2624,21 @@ main(int argc, char** argv)
if (state.rfn_len > 0)
{
- dir = dirname(state.real_filename);
+ if (!(pdir = realloc(dir, limits[L_PATH_MAX])))
+ {
+ free(filename);
+ return print_error(1,
+ "Allocation failed (out of memory?)");
+ }
+ dir = pdir;
+ *dir = 0;
+ (void)dirname(dir, state.real_filename, state.rfn_len + 1);
#ifdef __OpenBSD__
if (unveil(dir, "rwc") < 0)
{
perror("sled: unveil");
+ free(filename);
+ free(dir);
exit(1);
}
#endif
@@ -2500,6 +2684,8 @@ main(int argc, char** argv)
if (unveil(NULL, NULL) < 0)
{
perror("sled: unveil");
+ free(filename);
+ free(dir);
exit(1);
}
#endif
@@ -2533,6 +2719,8 @@ file_error:
general_error:
free(state.real_filename);
free(state.backup_filename);
+ free(filename);
+ free(dir);
return result;
init_termbox:
@@ -2550,7 +2738,8 @@ init_termbox:
if (!draw(&state))
{
state.running = 0;
- STRLCPY(errors[ERR_CANT_ALLOC], state.error, MAXBUFLINE);
+ STRLCPY(errors[ERR_CANT_ALLOC], state.error,
+ LINE_DEFAULT);
}
tb_present();
result = tb_poll_event(&ev);
@@ -2564,9 +2753,12 @@ init_termbox:
{
if (errno != 0)
perror(program_name);
+ free(filename);
return print_error(1, state.error);
}
free_drawstate(&state, 0);
+ free(filename);
+ free(dir);
return 0;
}
diff --git a/tests/003-replace-text-100000-lines.test b/tests/003-replace-text-100000-lines.test
@@ -0,0 +1,34 @@
+#!/bin/sh
+# vim: set ft=bash:
+exec 2>/dev/null
+testfile=tests/testfile
+text=$( for k in $(seq 0 9)
+ do
+ printf "$k"
+ done)
+text=$( for j in $(seq 7)
+ do
+ printf "%s" "$text"
+ done)
+text=$( for i in $(seq 100)
+ do
+ printf "%s\n" "$text"
+ done)
+
+for i in $(seq 500)
+do
+ printf "%s\n" "$text"
+done > "$testfile"
+
+set -x
+cat "$testfile" "$testfile" > "${testfile}~"
+mv "${testfile}~" "$testfile"
+
+expect tests/replace-ext.expect
+if [ -n "$(tail -n 1 "$testfile" | grep "brown fox")" ]; then
+ result=0
+else
+ result=1
+fi
+rm "$testfile"
+exit "$result"
diff --git a/tests/replace-ext.expect b/tests/replace-ext.expect
@@ -0,0 +1,21 @@
+# vim: set ft=expect:
+spawn ./sled tests/testfile
+interact timeout 1 return
+send_user "Entering command mode"
+send "\034"
+interact timeout 1 return
+send_user "Pressing \\"
+send "\\"
+interact timeout 1 return
+send_user "Entering text"
+send "4567\r"
+interact timeout 1 return
+send_user "Entering replacement"
+send "Quick brown fox\r"
+interact timeout 3 return
+send_user "Pressing Ctrl+S"
+send "\023"
+interact timeout 5 return
+send_user "Pressing Ctrl+Q"
+send "\021"
+exit
diff --git a/util.c b/util.c
@@ -7,6 +7,7 @@
#include <stdlib.h>
#include <string.h>
+#include "limit.h"
#include "termbox.h"
#include "util.h"
@@ -27,9 +28,9 @@ u8_string_to_unicode(uint32_t* us, const char* s, const size_t max)
u8_len = tb_utf8_char_to_unicode(&uch, s);
if (added + 1 > max)
break;
- *pus++ = uch;
if (u8_len != TB_ERR)
{
+ *pus++ = uch;
s += u8_len;
added++;
}
@@ -155,9 +156,7 @@ u32_strncpy(uint32_t* to, const uint32_t* from, const size_t max)
{
const uint32_t* pfrom = from;
size_t copied = 0;
- /* pfrom - from < max - 1 ... except we can't reliably subtract with
- * size_t */
- while (*pfrom && pfrom + 1 < max + from)
+ while (*pfrom && copied < max)
{
*to++ = *pfrom++;
copied++;
@@ -175,12 +174,12 @@ u32_strncat(uint32_t* to, const uint32_t* from, const size_t max)
{
const uint32_t* pfrom = from;
size_t copied = 0;
- while (*to && copied + 1 < max)
+ while (*to && copied < max)
{
to++;
copied++;
}
- while (*pfrom && copied + 1 < max)
+ while (*pfrom && copied < max)
{
*to++ = *pfrom++;
copied++;
@@ -192,9 +191,9 @@ u32_strncat(uint32_t* to, const uint32_t* from, const size_t max)
size_t
u32_u8_strncpy(uint32_t* to, const char* from, const size_t max)
{
- uint32_t* ufrom = calloc(max + 1, sizeof(uint32_t));
+ uint32_t* ufrom = NULL;
size_t len;
- if (!ufrom)
+ if (!(ufrom = malloc(max * sizeof(uint32_t))))
return 0;
u8_string_to_unicode(ufrom, from, max);
len = u32_strncpy(to, ufrom, max);
diff --git a/util.h b/util.h
@@ -4,10 +4,21 @@
#include <stdint.h>
#include <sys/types.h>
+#include <unistd.h>
+
#define ABS(a) ((a < 0) ? (-a) : (a))
#define LEN(x) (sizeof(x) / sizeof(x[0]))
#define MAX(a, b) ((a > b) ? a : b)
#define MIN(a, b) ((a < b) ? a : b)
+#define MEMCCPY(from, to, tolen, temp) \
+ do \
+ { \
+ if (!(temp = memccpy(to, from, 0, tolen))) \
+ { \
+ print_error(1, "memccpy:%d: Overflow", __LINE__); \
+ exit(1); \
+ } \
+ } while (0)
#define STRLCAT(tail, head, totallen) \
do \
{ \
@@ -27,6 +38,47 @@
} \
} while (0)
+#define ENSURE_SIZE(to, temp, tosize, testsize, fromsize, label, type) \
+ do \
+ { \
+ if (tosize < testsize) \
+ { \
+ tosize = fromsize; \
+ temp = realloc(to, tosize * sizeof(type)); \
+ if (!temp) \
+ { \
+ state->running = 0; \
+ STRLCPY(errors[ERR_CANT_ALLOC], state->error, \
+ LINE_DEFAULT); \
+ goto label; \
+ } \
+ to = temp; \
+ } \
+ } while (0)
+/*
+ * - Make sure the size of the destination string is large enough to copy, and
+ * copy
+ * - Sizes are in bytes
+ */
+#define U8_SAFE_COPY(to, temp, tosize, from, fromsize, label) \
+ do \
+ { \
+ ENSURE_SIZE(to, temp, tosize, fromsize, fromsize, label, char); \
+ u8_strncpy(to, from, tosize); \
+ } while (0)
+/*
+ * - Make sure the size of the destination string is large enough to copy, and
+ * copy
+ * - Sizes are in uint32_t units
+ */
+#define U32_SAFE_COPY(to, temp, tosize, from, fromsize, label) \
+ do \
+ { \
+ ENSURE_SIZE(to, temp, tosize, fromsize, fromsize, label, \
+ uint32_t); \
+ u32_strncpy(to, from, tosize); \
+ } while (0)
+
enum {
UNKNOWN_MODE,
COMMAND_MODE,
@@ -53,14 +105,9 @@ enum {
enum { ACT_WRITING, ACT_READING };
-#define ALLOC_DELTA 50
-#define MAXBUFLINE 4096
#define MAXCOPYBUF 8192
#define MAXDATEBUF 80
#define MAXFLAGSBUF (4 + 1)
-#define MAXMSGLINE 1024
-#define MSGDELTA 1024
-#define MAXPATH 1024
/* maximum chars for UTF-8 representations of Unicode chars, per Unicode char */
#define UTF8REPMAX 6
#define TB_KEY_CTRL_SPACE 0x00