Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add output_path to zsv writer options and use in desc/echo/sql #310

Merged
merged 6 commits into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 4 additions & 7 deletions app/desc.c
Original file line number Diff line number Diff line change
Expand Up @@ -505,15 +505,12 @@ int ZSV_MAIN_FUNC(ZSV_COMMAND)(int argc, const char *argv[], struct zsv_opts *op

struct zsv_csv_writer_options writer_opts = zsv_writer_get_default_opts();

for (int arg_i = 1; !data.err && arg_i < argc; arg_i++) {
for (int arg_i = 1; !err && !data.err && arg_i < argc; arg_i++) {
if (!strcmp(argv[arg_i], "-b") || !strcmp(argv[arg_i], "--with-bom"))
writer_opts.with_bom = 1;
else if (!strcmp(argv[arg_i], "-o") || !strcmp(argv[arg_i], "--output")) {
if (++arg_i >= argc)
data.err = zsv_printerr(zsv_desc_status_error, "%s option requires a filename", argv[arg_i - 1]);
else if (!(writer_opts.stream = fopen(argv[arg_i], "wb")))
data.err = zsv_printerr(zsv_desc_status_error, "Unable to open for write: %s", argv[arg_i]);
} else if (!strcmp(argv[arg_i], "-a") || !strcmp(argv[arg_i], "--all"))
else if (!strcmp(argv[arg_i], "-o") || !strcmp(argv[arg_i], "--output"))
writer_opts.output_path = zsv_next_arg(++arg_i, argc, argv, (int *)&data.err);
else if (!strcmp(argv[arg_i], "-a") || !strcmp(argv[arg_i], "--all"))
data.flags = 0xff;
else if (!strcmp(argv[arg_i], "-q") || !strcmp(argv[arg_i], "--quick"))
data.quick = 1;
Expand Down
10 changes: 3 additions & 7 deletions app/echo.c
Original file line number Diff line number Diff line change
Expand Up @@ -151,13 +151,9 @@ int ZSV_MAIN_FUNC(ZSV_COMMAND)(int argc, const char *argv[], struct zsv_opts *op
const char *arg = argv[arg_i];
if (!strcmp(arg, "-b"))
writer_opts.with_bom = 1;
else if (!strcmp(arg, "-o")) {
const char *output_filename = zsv_next_arg(++arg_i, argc, argv, &err);
if (output_filename && !err) {
if (!(writer_opts.stream = fopen(output_filename, "wb")))
perror(output_filename);
}
} else if (!strcmp(arg, "--contiguous"))
else if (!strcmp(arg, "-o"))
writer_opts.output_path = zsv_next_arg(++arg_i, argc, argv, &err);
else if (!strcmp(arg, "--contiguous"))
data.contiguous = 1;
else if (!strcmp(arg, "--trim-columns"))
data.trim_columns = 1;
Expand Down
3 changes: 1 addition & 2 deletions app/pretty.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

#define ZSV_COMMAND pretty
#include "zsv_command.h"
#include <zsv/utils/writer.h>
#include <zsv/utils/string.h>

#define ZSV_PRETTY_DEFAULT_LINE_MAX_WIDTH 160
Expand Down Expand Up @@ -686,7 +685,7 @@ int ZSV_MAIN_FUNC(ZSV_COMMAND)(int argc, const char *argv[], struct zsv_opts *pa
zsv_pretty_flush(h);
zsv_pretty_destroy(h);
}
if (opts.out)
if (opts.out && opts.out != stdout)
fclose(opts.out);
if (in && in != stdin)
fclose(in);
Expand Down
18 changes: 6 additions & 12 deletions app/sql.c
Original file line number Diff line number Diff line change
Expand Up @@ -188,15 +188,9 @@ int ZSV_MAIN_FUNC(ZSV_COMMAND)(int argc, const char *argv[], struct zsv_opts *op
err = 1;
}
}
} else if (!strcmp(arg, "-o") || !strcmp(arg, "--output")) {
if (!(++arg_i < argc)) {
fprintf(stderr, "option %s requires a filename\n", arg);
err = 1;
} else if (!(writer_opts.stream = fopen(argv[arg_i], "wb"))) {
fprintf(stderr, "Could not open for writing: %s\n", argv[arg_i]);
err = 1;
}
} else if (!strcmp(arg, "--memory"))
} else if (!strcmp(arg, "-o") || !strcmp(arg, "--output"))
writer_opts.output_path = zsv_next_arg(++arg_i, argc, argv, &err);
else if (!strcmp(arg, "--memory"))
data.in_memory = 1;
else if (!strcmp(arg, "-b"))
writer_opts.with_bom = 1;
Expand Down Expand Up @@ -281,11 +275,11 @@ int ZSV_MAIN_FUNC(ZSV_COMMAND)(int argc, const char *argv[], struct zsv_opts *op
}
}

if (f) {
zsv_csv_writer cw = zsv_writer_new(&writer_opts);
if (f && cw) {
fclose(f); // to do: don't open in the first place
f = NULL;

zsv_csv_writer cw = zsv_writer_new(&writer_opts);
unsigned char cw_buff[1024];
zsv_writer_set_temp_buff(cw, cw_buff, sizeof(cw_buff));

Expand Down Expand Up @@ -440,14 +434,14 @@ int ZSV_MAIN_FUNC(ZSV_COMMAND)(int argc, const char *argv[], struct zsv_opts *op
else
err = 0;

zsv_writer_delete(cw);
zsv_sqlite3_db_delete(zdb);
}
}
if (f)
fclose(f);
zsv_sql_finalize(&data);
zsv_sql_cleanup(&data);
zsv_writer_delete(cw);

if (tmpfn) {
unlink(tmpfn);
Expand Down
1 change: 1 addition & 0 deletions app/test/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,7 @@ test-sheet-12: ${BUILD_DIR}/bin/zsv_sheet${EXE} worldcitiespop_mil.csv
tmux send-keys -t $@ "f" "el" "Enter" && \
sleep 0.5 && \
tmux send-keys -t $@ "f" "al" "Enter" && \
sleep 1 && \
tmux capture-pane -t $@ -p ${REDIRECT1} ${TMP_DIR}/[email protected] && \
tmux send-keys -t $@ "q" && \
${CMP} ${TMP_DIR}/[email protected] expected/[email protected] && ${TEST_PASS} || (echo 'Incorrect output:' && cat ${TMP_DIR}/[email protected] && ${TEST_FAIL}))
Expand Down
2 changes: 1 addition & 1 deletion app/test/expected/test-sheet-12.out
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ Row # Country City AccentCit Region Populatio Latitude Longitude
415 mx el salado El Salado 07 29.644444 -101.7666
590 af abbaskhel Abbaskhel 29 32.831993 69.12336
807 af `abdul `a `Abdul `A 10 31.229677 64.206403
(59456 filtered rows) 117
(4950 filtered rows) 117
55 changes: 42 additions & 13 deletions app/utils/writer.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

// clang-format off

Expand Down Expand Up @@ -107,6 +108,8 @@ struct zsv_output_buff {
size_t (*write)(const void *restrict, size_t size, size_t nitems, void *restrict stream);
void *stream;
size_t used;
unsigned char close_on_delete : 1;
unsigned char _ : 7;
};

struct zsv_writer_data {
Expand Down Expand Up @@ -152,19 +155,40 @@ void zsv_writer_set_temp_buff(zsv_csv_writer w, unsigned char *buff, size_t buff
w->buffsize = buffsize;
}

static int writer_opts_ok(struct zsv_csv_writer_options *opts) {
if (opts) {
if (opts->output_path) {
if ((opts->write && opts->write != (size_t(*)(const void *restrict, size_t, size_t, void *restrict))fwrite) ||
(opts->stream && opts->stream != stdout)) {
fprintf(stderr, "Invalid zsv writer options: non-NULL 'output_path' with invalid 'write' and/or 'stream'\n");
errno = EINVAL;
return 0;
}
}
}
return 1;
}

zsv_csv_writer zsv_writer_new(struct zsv_csv_writer_options *opts) {
struct zsv_writer_data *w = calloc(1, sizeof(*w));
if (!(writer_opts_ok(opts)))
return NULL;
if (w) {
if (!(w->out.buff = malloc(ZSV_OUTPUT_BUFF_SIZE))) {
free(w); // out of memory!
return NULL;
}
if (!(w->out.buff = malloc(ZSV_OUTPUT_BUFF_SIZE)))
goto zsv_writer_new_err;

if (!opts) {
w->out.write = (size_t(*)(const void *restrict, size_t, size_t, void *restrict))fwrite;
w->out.stream = stdout;
} else {
if (opts->write) {
if (opts->output_path) {
if (!(w->out.stream = fopen(opts->output_path, "wb"))) {
perror(opts->output_path);
goto zsv_writer_new_err;
}
w->out.close_on_delete = 1;
w->out.write = (size_t(*)(const void *restrict, size_t, size_t, void *restrict))fwrite;
} else if (opts->write) {
w->out.write = opts->write;
w->out.stream = opts->stream;
} else {
Expand All @@ -178,6 +202,13 @@ zsv_csv_writer zsv_writer_new(struct zsv_csv_writer_options *opts) {
}
}
return w;

zsv_writer_new_err : {
int e = errno;
zsv_writer_delete(w);
errno = e;
}
return NULL;
}

enum zsv_writer_status zsv_writer_flush(zsv_csv_writer w) {
Expand All @@ -192,19 +223,17 @@ enum zsv_writer_status zsv_writer_delete(zsv_csv_writer w) {
if (!w)
return zsv_writer_status_missing_handle;

zsv_output_buff_flush(&w->out);
if (w->out.stream && w->out.write && w->out.buff)
zsv_output_buff_flush(&w->out);

if (w->started)
w->out.write("\n", 1, 1, w->out.stream);

/* closing the stream should be handled by whatever code first opened the stream
if (w->out.stream && w->out.stream != stdout) {
fclose(w->out.stream);
w->out.stream = NULL;
}
*/

if (w->out.buff)
free(w->out.buff);

if (w->out.close_on_delete && w->out.stream)
fclose(w->out.stream);
free(w);
return zsv_writer_status_ok;
}
Expand Down
12 changes: 11 additions & 1 deletion include/zsv/utils/writer.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,18 @@
#define ZSV_WRITER_NEW_ROW 1
#define ZSV_WRITER_SAME_ROW 0

/*** csv writer ***/
/**
* csv writer options
*
* If `output_path` is non-NULL, `stream` must be NULL or stdout and `write` must be NULL or fwrite
***/
struct zsv_csv_writer_options {
char with_bom;
size_t (*write)(const void *restrict, size_t size, size_t nitems, void *restrict stream);
void *stream;
void (*table_init)(void *);
void *table_init_ctx;
const char *output_path; // if provided, will be created by zsv_writer_new() and closed by zsv_writer_delete()
};

void zsv_writer_set_default_opts(struct zsv_csv_writer_options opts);
Expand All @@ -36,6 +41,11 @@ enum zsv_writer_status {
struct zsv_writer_data;
typedef struct zsv_writer_data *zsv_csv_writer;

/**
* Get a new CSV writer
*
* @returns handle, or NULL on error, in which case errno will be set
*/
zsv_csv_writer zsv_writer_new(struct zsv_csv_writer_options *opts);
enum zsv_writer_status zsv_writer_delete(zsv_csv_writer w);

Expand Down