refactor(cli): Extract strvec interface

This commit is contained in:
Rachel 2024-12-30 14:52:01 -08:00
parent ee982cadfd
commit aa488232cf
3 changed files with 193 additions and 138 deletions

32
cli/include/strvec.h Normal file
View File

@ -0,0 +1,32 @@
/*
* Copyright 2024 <lhearachel@proton.me>
*
* Licensed 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.
*/
#ifndef NARC_STRVEC_H
#define NARC_STRVEC_H
#include <stddef.h>
struct strvec {
char **s;
size_t count;
size_t capacity;
};
struct strvec *strvec_new(size_t capacity);
void strvec_del(struct strvec *strvec);
int strvec_push(struct strvec *strvec, char *s);
int strvec_from_file(struct strvec *strvec, const char *fname);
#endif // NARC_STRVEC_H

View File

@ -27,6 +27,7 @@
#include "defs/narc.h"
#include "defs/vfs.h"
#include "strvec.h"
#include "utils.h"
typedef intptr_t ssize_t;
@ -71,14 +72,9 @@ struct array {
size_t capacity;
};
static struct array *build_pack_list(DIR *dir, const char *order_fname, const char *ignore_fname);
static ssize_t read_line(char **lineptr, size_t *n, FILE *stream);
static int read_file_lines(const char *fname, struct array *out_array);
static void free_array(struct array *arr);
static int parse_opts(int *argc, const char ***argv, struct options *opts);
static int pack(struct options *opts);
static struct strvec *build_pack_list(DIR *dir, const char *order_fname, const char *ignore_fname);
int create(int argc, const char **argv)
{
@ -159,14 +155,14 @@ static int pack(struct options *opts)
FAIL("narc create: could not get current working directory: %s\n", strerror(errno));
}
struct array *to_pack = build_pack_list(dir, opts->order, opts->ignore);
struct strvec *to_pack = build_pack_list(dir, opts->order, opts->ignore);
chdir(opts->input);
ctx = narc_pack_start();
for (size_t i = 0; i < to_pack->count; i++) {
FILE *f = fopen(to_pack->elems[i], "rb");
FILE *f = fopen(to_pack->s[i], "rb");
if (f == NULL) {
FAIL("narc create: error while opening file “%s” for reading: %s\n", to_pack->elems[i], strerror(errno));
FAIL("narc create: error while opening file “%s” for reading: %s\n", to_pack->s[i], strerror(errno));
}
fseek(f, 0, SEEK_END);
@ -175,7 +171,7 @@ static int pack(struct options *opts)
unsigned char *image = malloc(fsize);
if (image == NULL) {
FAIL("narc create: error while reading file “%s”: %s\n", to_pack->elems[i], strerror(errno));
FAIL("narc create: error while reading file “%s”: %s\n", to_pack->s[i], strerror(errno));
}
fread(image, 1, fsize, f);
@ -213,135 +209,18 @@ fail:
return EXIT_FAILURE;
}
static ssize_t read_line(char **lineptr, size_t *n, FILE *stream)
static struct strvec *build_pack_list(DIR *dir, const char *order_fname, const char *ignore_fname)
{
if (lineptr == NULL || n == NULL) {
errno = EINVAL;
return -1;
}
struct strvec *all_files = NULL, *ignored = NULL;
int c = fgetc(stream);
if (c == EOF) {
return -1;
}
if (*lineptr == NULL) {
*lineptr = malloc(128);
if (*lineptr == NULL) {
return -1;
}
*n = 128;
}
size_t pos = 0;
do {
if (pos + 1 >= *n) {
size_t new_size = *n + (*n >> 2);
if (new_size < 128) {
new_size = 128;
}
char *new_ptr = realloc(*lineptr, new_size);
if (new_ptr == NULL) {
return -1;
}
*n = new_size;
*lineptr = new_ptr;
}
((unsigned char *)(*lineptr))[pos++] = c;
if (c == '\n') {
break;
}
} while ((c = fgetc(stream)) != EOF);
(*lineptr)[pos] = '\0';
return pos;
}
static void free_array(struct array *arr)
{
for (size_t i = 0; i < arr->count; i++) {
free(arr->elems[i]);
}
free(arr->elems);
free(arr);
}
static int append_array(struct array *arr, char *elem)
{
if (arr->count + 1 == arr->capacity) {
char **tmp = realloc(arr->elems, arr->capacity * 2);
if (tmp == NULL) {
return -1;
}
arr->elems = tmp;
arr->capacity *= 2;
}
arr->elems[arr->count] = elem;
arr->count++;
return 0;
}
static int read_file_lines(const char *fname, struct array *out_array)
{
if (fname == NULL) {
return 0;
}
FILE *f = fopen(fname, "r");
if (f == NULL) {
return -1;
}
char *buf = NULL;
size_t buf_size = 0;
ssize_t read_size;
while ((read_size = read_line(&buf, &buf_size, f)) != -1) {
// Skip over empty lines
if (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n') || buf[0] == '\0') {
continue;
}
char *line = malloc(read_size);
strncpy(line, buf, read_size - 1);
line[read_size] = '\0';
if (append_array(out_array, line)) {
return -1;
}
}
free(buf);
return 0;
}
static struct array *build_pack_list(DIR *dir, const char *order_fname, const char *ignore_fname)
{
struct array *all_files = NULL, *ignored = NULL;
all_files = malloc(sizeof(struct array));
all_files->elems = malloc(sizeof(char *) * 5000);
all_files->count = 0;
all_files->capacity = 5000;
// Collect pre-orderd files first. These don't need to be validated against ignored entries.
if (read_file_lines(order_fname, all_files)) {
all_files = strvec_new(5000);
if (strvec_from_file(all_files, order_fname)) {
goto cleanup_error;
}
// Load the list of `ignored` names/patterns. This list is kept separate.
ignored = malloc(sizeof(struct array));
ignored->elems = malloc(sizeof(char *) * 100);
ignored->count = 0;
ignored->capacity = 100;
if (read_file_lines(ignore_fname, ignored)) {
ignored = strvec_new(100);
if (strvec_from_file(ignored, ignore_fname)) {
goto cleanup_error;
}
@ -356,7 +235,7 @@ static struct array *build_pack_list(DIR *dir, const char *order_fname, const ch
// Check the ignored list first, since it should be much smaller
bool exclude = false;
for (size_t i = 0; i < ignored->count; i++) {
if (fnmatch(ignored->elems[i], entry->d_name, FNM_PERIOD) == 0) {
if (fnmatch(ignored->s[i], entry->d_name, FNM_PERIOD) == 0) {
exclude = true;
break;
}
@ -368,7 +247,7 @@ static struct array *build_pack_list(DIR *dir, const char *order_fname, const ch
// Now check the ordered list, to avoid double includes
for (size_t i = 0; i < all_files->count; i++) {
if (strcmp(all_files->elems[i], entry->d_name) == 0) {
if (strcmp(all_files->s[i], entry->d_name) == 0) {
exclude = true;
break;
}
@ -381,13 +260,13 @@ static struct array *build_pack_list(DIR *dir, const char *order_fname, const ch
char *fname = malloc(strlen(entry->d_name) + 1);
strcpy(fname, entry->d_name);
fname[strlen(entry->d_name)] = '\0';
append_array(all_files, fname);
strvec_push(all_files, fname);
}
return all_files;
cleanup_error:
free_array(all_files);
free_array(ignored);
strvec_del(all_files);
strvec_del(ignored);
return NULL;
}

144
cli/src/strvec.c Normal file
View File

@ -0,0 +1,144 @@
/*
* Copyright 2024 <lhearachel@proton.me>
*
* Licensed 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.
*/
#include "strvec.h"
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef intptr_t ssize_t;
struct strvec *strvec_new(size_t capacity)
{
struct strvec *vec = malloc(sizeof(struct strvec));
vec->s = malloc(capacity * sizeof(char *));
vec->count = 0;
vec->capacity = capacity;
return vec;
}
void strvec_del(struct strvec *strvec)
{
for (size_t i = 0; i < strvec->count; i++) {
free(strvec->s[i]);
}
free(strvec->s);
free(strvec);
}
int strvec_push(struct strvec *strvec, char *s)
{
if (strvec->count + 1 > strvec->capacity) {
char **p = realloc(strvec->s, strvec->capacity * sizeof(char *));
if (p == NULL) {
return -1;
}
strvec->s = p;
strvec->capacity *= 2;
}
strvec->s[strvec->count] = s;
strvec->count++;
return 0;
}
static ssize_t read_line(char **lineptr, size_t *n, FILE *stream)
{
if (lineptr == NULL || n == NULL) {
errno = EINVAL;
return -1;
}
int c = fgetc(stream);
if (c == EOF) {
return -1;
}
if (*lineptr == NULL) {
*lineptr = malloc(128);
if (*lineptr == NULL) {
return -1;
}
*n = 128;
}
size_t pos = 0;
do {
if (pos + 1 >= *n) {
size_t new_size = *n + (*n >> 2);
if (new_size < 128) {
new_size = 128;
}
char *new_ptr = realloc(*lineptr, new_size);
if (new_ptr == NULL) {
return -1;
}
*n = new_size;
*lineptr = new_ptr;
}
((unsigned char *)(*lineptr))[pos++] = c;
if (c == '\n') {
break;
}
} while ((c = fgetc(stream)) != EOF);
(*lineptr)[pos] = '\0';
return pos;
}
int strvec_from_file(struct strvec *strvec, const char *fname)
{
if (fname == NULL) {
return 0;
}
FILE *f = fopen(fname, "r");
if (f == NULL) {
return -1;
}
char *buf = NULL;
size_t buf_size = 0;
ssize_t read_size;
while ((read_size = read_line(&buf, &buf_size, f)) != -1) {
// Skip over empty lines
if (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n') || buf[0] == '\0') {
continue;
}
char *line = malloc(read_size);
strncpy(line, buf, read_size - 1);
line[read_size] = '\0';
if (strvec_push(strvec, line)) {
return -1;
}
}
free(buf);
return 0;
}