2018-10-09 15:55:13 +00:00
|
|
|
#include <sys/types.h>
|
2018-10-12 20:30:23 +00:00
|
|
|
#include <sys/mman.h>
|
2018-10-09 15:55:13 +00:00
|
|
|
#include <sys/stat.h>
|
|
|
|
|
2018-10-09 15:55:14 +00:00
|
|
|
#include <assert.h>
|
2018-10-09 15:55:13 +00:00
|
|
|
#include <fcntl.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include "bbs.h"
|
|
|
|
|
|
|
|
void die(const char *msg) {
|
|
|
|
dolog(msg);
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void *malloz(size_t size) {
|
|
|
|
void *p = malloc(size);
|
|
|
|
if (p == NULL)
|
|
|
|
die("Out of memory");
|
|
|
|
memset(p, 0, size);
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *file2str(const char *path) {
|
|
|
|
struct stat s;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
memset(&s, 0, sizeof(s));
|
|
|
|
if (stat(path, &s) < 0)
|
|
|
|
return NULL;
|
|
|
|
if (!S_ISREG(s.st_mode))
|
|
|
|
return NULL;
|
|
|
|
fd = open(path, O_RDONLY);
|
|
|
|
if (fd < 0)
|
|
|
|
return NULL;
|
|
|
|
char *contents = malloz(s.st_size + 1);
|
|
|
|
if (read(fd, contents, s.st_size) != s.st_size) {
|
|
|
|
free(contents);
|
|
|
|
close(fd);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
close(fd);
|
|
|
|
contents[s.st_size] = '\0';
|
|
|
|
return contents;
|
|
|
|
}
|
|
|
|
|
2018-10-12 20:30:23 +00:00
|
|
|
stralloc file2stralloc(const char *path) {
|
|
|
|
struct stat s;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
memset(&s, 0, sizeof(s));
|
|
|
|
if (stat(path, &s) < 0)
|
|
|
|
return EMPTY_STRALLOC;
|
|
|
|
if (!S_ISREG(s.st_mode))
|
|
|
|
return EMPTY_STRALLOC;
|
|
|
|
fd = open(path, O_RDONLY);
|
|
|
|
if (fd < 0)
|
|
|
|
return EMPTY_STRALLOC;
|
|
|
|
size_t len = s.st_size;
|
|
|
|
char *p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
|
|
|
|
if (p == NULL) {
|
|
|
|
close(fd);
|
|
|
|
return EMPTY_STRALLOC;
|
|
|
|
}
|
|
|
|
stralloc sa = EMPTY_STRALLOC;
|
|
|
|
stralloc_copyb(&sa, p, len);
|
2018-10-15 14:37:48 +00:00
|
|
|
munmap(p, len);
|
2018-10-12 20:30:23 +00:00
|
|
|
close(fd);
|
|
|
|
|
|
|
|
return sa;
|
|
|
|
}
|
|
|
|
|
2018-10-09 15:55:13 +00:00
|
|
|
char *str5dup(const char *a, const char *b, const char *c, const char *d, const char *e) {
|
|
|
|
char *p;
|
|
|
|
size_t alen, blen, clen, dlen, elen;
|
|
|
|
|
|
|
|
if (a == NULL)
|
|
|
|
a = "";
|
|
|
|
if (b == NULL)
|
|
|
|
b = "";
|
|
|
|
if (c == NULL)
|
|
|
|
c = "";
|
|
|
|
if (d == NULL)
|
|
|
|
d = "";
|
|
|
|
if (e == NULL)
|
|
|
|
e = "";
|
|
|
|
|
|
|
|
alen = strlen(a);
|
|
|
|
blen = strlen(b);
|
|
|
|
clen = strlen(c);
|
|
|
|
dlen = strlen(d);
|
|
|
|
elen = strlen(e);
|
|
|
|
|
|
|
|
p = malloz(alen + blen + clen + dlen + elen + 1);
|
|
|
|
memmove(p, a, alen);
|
|
|
|
memmove(p + alen, b, blen);
|
|
|
|
memmove(p + alen + blen, c, clen);
|
|
|
|
memmove(p + alen + blen + clen, d, dlen);
|
|
|
|
memmove(p + alen + blen + clen + dlen, e, elen);
|
|
|
|
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *str4dup(const char *a, const char *b, const char *c, const char *d) {
|
|
|
|
return str5dup(a, b, c, d, "");
|
|
|
|
}
|
|
|
|
|
|
|
|
char *str3dup(const char *a, const char *b, const char *c) {
|
|
|
|
return str5dup(a, b, c, "", "");
|
|
|
|
}
|
|
|
|
|
|
|
|
char *str2dup(const char *a, const char *b) {
|
|
|
|
return str5dup(a, b, "", "", "");
|
|
|
|
}
|
2018-10-09 15:55:14 +00:00
|
|
|
|
|
|
|
void init_ptr_vector(struct ptr_vector *vec) {
|
|
|
|
assert(vec != NULL);
|
|
|
|
memset(vec, 0, sizeof(*vec));
|
|
|
|
}
|
|
|
|
|
|
|
|
void ptr_vector_clear(struct ptr_vector *vec) {
|
|
|
|
assert(vec != NULL);
|
|
|
|
vec->len = 0;
|
|
|
|
memset(vec->ptrs, 0, sizeof(void *) * vec->capacity);
|
|
|
|
}
|
|
|
|
|
|
|
|
void *ptr_vector_get(struct ptr_vector *vec, size_t i) {
|
|
|
|
assert(vec != NULL);
|
|
|
|
if (i >= vec->len)
|
|
|
|
return NULL;
|
|
|
|
assert(vec->ptrs != NULL);
|
|
|
|
return vec->ptrs[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
int ptr_vector_put(struct ptr_vector *vec, void *p, size_t i) {
|
|
|
|
assert(vec != NULL);
|
|
|
|
if (i >= vec->len)
|
|
|
|
return 0;
|
|
|
|
assert(vec->ptrs != NULL);
|
|
|
|
vec->ptrs[i] = p;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
More use of ptr_vector; avoid unnecessary copies.
Recast more code in terms of the ptr_vector abstraction.
The mail_menu.c code also made a lot of unnecessary copies
of strings. For example, there was this code sequence:
for (i = z; i < lines - 1; i++) {
free(content[i]);
content[i] = strdup(content[i + 1]);
}
free(content[i]);
lines--;
content = (char **)realloc(content, sizeof(char *) * lines);
Here, `content` represents an array of lines of text.
This code is removing an element from somewhere in that
array (possibly in the middle), and then shifting the
remaining elements over one position.
But observe the calls to `free` and `strdup` in the loop
body: the content is already dynamically allocated. We
free whatever was in the selected position, and then make
*another copy* of the data in the next position to put
into the now-available slot in the array: repeat for the
remainder of the array's elements.
Instead, we could change this code to just shift things
down:
free(content[z]);
for (i = z; i < (lines - 1); ++i)
content[i] = content[i + 1];
--lines;
ncontent = realloc(content, sizeof(char *) * lines);
assert(ncontent == NULL);
content = ncontent;
However, the ptr_vector abstraction provides us a function,
`ptr_vector_del` that deletes an element from the array and
returns the pointer, so we can rewrite this as simply:
free(ptr_vector_del(&content, z));
No additional malloc()/free() required, which means less
pressure on the memory allocator and less copying of data.
Signed-off-by: Dan Cross <patchdev@fat-dragon.org>
2018-10-10 17:50:37 +00:00
|
|
|
int ptr_vector_ins(struct ptr_vector *vec, void *p, size_t i) {
|
|
|
|
assert(vec != NULL);
|
|
|
|
if (i > vec->len)
|
|
|
|
return 0;
|
|
|
|
// Note: If we're inserting at the end of the array
|
|
|
|
// and we're not reallocating, the call to `memmove()`
|
|
|
|
// below would take a dest argument pointing immediately
|
|
|
|
// after the end of the array. It is unclear whether
|
|
|
|
// this is undefined behavior according to the ISO C
|
|
|
|
// standard, even though the size in that case would be
|
|
|
|
// zero, so sidestep the issue by explicitly testing
|
|
|
|
// for and simply appending in this case.
|
|
|
|
if (i == vec->len)
|
|
|
|
return ptr_vector_append(vec, p);
|
2018-10-11 14:02:55 +00:00
|
|
|
ptr_vector_append(vec, NULL); // Make space in the vector.
|
More use of ptr_vector; avoid unnecessary copies.
Recast more code in terms of the ptr_vector abstraction.
The mail_menu.c code also made a lot of unnecessary copies
of strings. For example, there was this code sequence:
for (i = z; i < lines - 1; i++) {
free(content[i]);
content[i] = strdup(content[i + 1]);
}
free(content[i]);
lines--;
content = (char **)realloc(content, sizeof(char *) * lines);
Here, `content` represents an array of lines of text.
This code is removing an element from somewhere in that
array (possibly in the middle), and then shifting the
remaining elements over one position.
But observe the calls to `free` and `strdup` in the loop
body: the content is already dynamically allocated. We
free whatever was in the selected position, and then make
*another copy* of the data in the next position to put
into the now-available slot in the array: repeat for the
remainder of the array's elements.
Instead, we could change this code to just shift things
down:
free(content[z]);
for (i = z; i < (lines - 1); ++i)
content[i] = content[i + 1];
--lines;
ncontent = realloc(content, sizeof(char *) * lines);
assert(ncontent == NULL);
content = ncontent;
However, the ptr_vector abstraction provides us a function,
`ptr_vector_del` that deletes an element from the array and
returns the pointer, so we can rewrite this as simply:
free(ptr_vector_del(&content, z));
No additional malloc()/free() required, which means less
pressure on the memory allocator and less copying of data.
Signed-off-by: Dan Cross <patchdev@fat-dragon.org>
2018-10-10 17:50:37 +00:00
|
|
|
memmove(vec->ptrs + i + 1, vec->ptrs + i,
|
|
|
|
(vec->len - (i + 1)) * sizeof(void *));
|
|
|
|
vec->ptrs[i] = p;
|
|
|
|
++vec->len;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2018-10-09 15:55:14 +00:00
|
|
|
void *ptr_vector_del(struct ptr_vector *vec, size_t i) {
|
|
|
|
void *p;
|
|
|
|
assert(vec != NULL);
|
|
|
|
if (i >= vec->len)
|
|
|
|
return NULL;
|
|
|
|
assert(vec->ptrs != NULL);
|
|
|
|
p = vec->ptrs[i];
|
|
|
|
memmove(vec->ptrs + i, vec->ptrs + i + 1,
|
|
|
|
(vec->len - (i + 1)) * sizeof(void *));
|
|
|
|
--vec->len;
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ptr_vector_append(struct ptr_vector *vec, void *p) {
|
|
|
|
assert(vec != NULL);
|
|
|
|
if (vec->len == vec->capacity) {
|
|
|
|
void **ps;
|
|
|
|
size_t oldcap = vec->capacity;
|
2018-10-09 17:12:43 +00:00
|
|
|
if (vec->capacity == 0)
|
|
|
|
vec->capacity = 1;
|
|
|
|
else
|
2018-10-09 15:55:14 +00:00
|
|
|
vec->capacity *= 2;
|
2018-10-09 17:12:43 +00:00
|
|
|
ps = realloc(vec->ptrs, vec->capacity * sizeof(void *));
|
|
|
|
assert(ps != NULL);
|
|
|
|
vec->ptrs = ps;
|
2018-10-10 01:12:04 +00:00
|
|
|
memset(vec->ptrs + oldcap, 0, (vec->capacity - oldcap) * sizeof(void *));
|
2018-10-09 17:12:43 +00:00
|
|
|
}
|
|
|
|
vec->ptrs[vec->len] = p;
|
|
|
|
++vec->len;
|
2018-10-09 15:55:14 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t ptr_vector_len(struct ptr_vector *vec) {
|
|
|
|
assert(vec != NULL);
|
|
|
|
return vec->len;
|
|
|
|
}
|
|
|
|
|
2018-10-09 15:55:16 +00:00
|
|
|
void **ptr_vector_ptrs(struct ptr_vector *vec) {
|
|
|
|
assert(vec != NULL);
|
|
|
|
return vec->ptrs;
|
|
|
|
}
|
|
|
|
|
More use of ptr_vector; avoid unnecessary copies.
Recast more code in terms of the ptr_vector abstraction.
The mail_menu.c code also made a lot of unnecessary copies
of strings. For example, there was this code sequence:
for (i = z; i < lines - 1; i++) {
free(content[i]);
content[i] = strdup(content[i + 1]);
}
free(content[i]);
lines--;
content = (char **)realloc(content, sizeof(char *) * lines);
Here, `content` represents an array of lines of text.
This code is removing an element from somewhere in that
array (possibly in the middle), and then shifting the
remaining elements over one position.
But observe the calls to `free` and `strdup` in the loop
body: the content is already dynamically allocated. We
free whatever was in the selected position, and then make
*another copy* of the data in the next position to put
into the now-available slot in the array: repeat for the
remainder of the array's elements.
Instead, we could change this code to just shift things
down:
free(content[z]);
for (i = z; i < (lines - 1); ++i)
content[i] = content[i + 1];
--lines;
ncontent = realloc(content, sizeof(char *) * lines);
assert(ncontent == NULL);
content = ncontent;
However, the ptr_vector abstraction provides us a function,
`ptr_vector_del` that deletes an element from the array and
returns the pointer, so we can rewrite this as simply:
free(ptr_vector_del(&content, z));
No additional malloc()/free() required, which means less
pressure on the memory allocator and less copying of data.
Signed-off-by: Dan Cross <patchdev@fat-dragon.org>
2018-10-10 17:50:37 +00:00
|
|
|
void ptr_vector_apply(struct ptr_vector *vec, void (*f)(void *arg)) {
|
|
|
|
assert(vec != NULL);
|
|
|
|
assert(f != NULL);
|
|
|
|
for (size_t i = 0; i < vec->len; ++i)
|
|
|
|
f(vec->ptrs[i]);
|
|
|
|
}
|
|
|
|
|
2018-10-09 15:55:15 +00:00
|
|
|
void **consume_ptr_vector(struct ptr_vector *vec) {
|
|
|
|
assert(vec != NULL);
|
|
|
|
void **ps = realloc(vec->ptrs, vec->len * sizeof(void *));
|
|
|
|
assert(ps != NULL);
|
|
|
|
vec->ptrs = NULL;
|
|
|
|
vec->len = 0;
|
|
|
|
vec->capacity = 0;
|
|
|
|
return ps;
|
|
|
|
}
|
|
|
|
|
2018-10-09 15:55:14 +00:00
|
|
|
void destroy_ptr_vector(struct ptr_vector *vec) {
|
|
|
|
assert(vec != NULL);
|
|
|
|
free(vec->ptrs);
|
|
|
|
vec->ptrs = NULL;
|
|
|
|
vec->capacity = 0;
|
|
|
|
vec->len = 0;
|
|
|
|
}
|