Skip to content

Commit

Permalink
add mv command (#113)
Browse files Browse the repository at this point in the history
* add mv command
  • Loading branch information
liquidaty authored Mar 15, 2023
1 parent adb83e3 commit 392269b
Show file tree
Hide file tree
Showing 10 changed files with 170 additions and 10 deletions.
6 changes: 3 additions & 3 deletions app/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,8 @@ endif

ZSV=$(BINDIR)/zsv${EXE}

SOURCES= echo count count-pull select select-pull 2tsv 2json serialize flatten pretty stack desc sql 2db compare prop rm jq
CLI_SOURCES=echo select desc count 2tsv pretty sql flatten 2json serialize stack 2db compare prop rm jq
SOURCES= echo count count-pull select select-pull 2tsv 2json serialize flatten pretty stack desc sql 2db compare prop rm mv jq
CLI_SOURCES=echo select desc count 2tsv pretty sql flatten 2json serialize stack 2db compare prop rm mv jq

CFLAGS+= -DUSE_JQ

Expand Down Expand Up @@ -255,7 +255,7 @@ help:
@echo "which will build and test all apps, or to build/test a single app:"
@echo " ${MAKE} test-xx"
@echo "where xx is any of:"
@echo " echo count count-pull select select-pull 2tsv 2json serialize flatten pretty stack desc sql 2db prop rm"
@echo " echo count count-pull select select-pull 2tsv 2json serialize flatten pretty stack desc sql 2db prop rm mv"
@echo ""

install: ${ZSV}
Expand Down
1 change: 1 addition & 0 deletions app/builtin/help.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ static int main_help(int argc, const char *argv[]) {
" prop : save parsing options associated with a file that are subsequently",
" applied by default when processing that file",
" rm : remove a file and its related cache",
" mv : rename (move) a file and/or its related cache",
#ifdef USE_JQ
" jq : run a jq filter on json input",
#endif
Expand Down
4 changes: 3 additions & 1 deletion app/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ ZSV_MAIN_DECL(compare);
ZSV_MAIN_DECL(echo);
ZSV_MAIN_NO_OPTIONS_DECL(prop);
ZSV_MAIN_NO_OPTIONS_DECL(rm);
ZSV_MAIN_NO_OPTIONS_DECL(mv);

#ifdef USE_JQ
ZSV_MAIN_NO_OPTIONS_DECL(jq);
Expand Down Expand Up @@ -99,7 +100,8 @@ struct builtin_cmd builtin_cmds[] = {
CLI_BUILTIN_COMMAND(compare),
CLI_BUILTIN_COMMAND(echo),
CLI_BUILTIN_NO_OPTIONS_COMMAND(prop),
CLI_BUILTIN_NO_OPTIONS_COMMAND(rm)
CLI_BUILTIN_NO_OPTIONS_COMMAND(rm),
CLI_BUILTIN_NO_OPTIONS_COMMAND(mv)
#ifdef USE_JQ
, CLI_BUILTIN_NO_OPTIONS_COMMAND(jq)
#endif
Expand Down
2 changes: 2 additions & 0 deletions app/ext_example/test/expected/zsvext-test-3.out
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ Other commands:
prop : save parsing options associated with a file that are subsequently
applied by default when processing that file
rm : remove a file and its related cache
mv : rename (move) a file and/or its related cache
jq : run a jq filter on json input

(No extended commands)
Expand Down Expand Up @@ -111,6 +112,7 @@ Other commands:
prop : save parsing options associated with a file that are subsequently
applied by default when processing that file
rm : remove a file and its related cache
mv : rename (move) a file and/or its related cache
jq : run a jq filter on json input

Extended commands:
Expand Down
112 changes: 112 additions & 0 deletions app/mv.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/* Copyright (C) 2022 Guarnerix Inc dba Liquidaty - All Rights Reserved
* Unauthorized copying of this file, via any medium is strictly prohibited
* Proprietary and confidential
* Written by Matt Wong <[email protected]>
*/

/*
* Move a given file and its cache as follows:
* 1. check that the destination file doesn't exist. if it does, exit with an error
* 2. if a cache exists, check that the destination file cache dir doesn't exist. if it does, exit with an error
* 3. move the file. if it fails, exit with an error
* 4. move the cache, if it exists. if it fails, attempt to move the file back to its original location, and exit with an error
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h> // unlink()
#include <errno.h>

#define ZSV_COMMAND_NO_OPTIONS
#define ZSV_COMMAND mv
#include "zsv_command.h"

#include <zsv/utils/dirs.h>
#include <zsv/utils/file.h>
#include <zsv/utils/cache.h>

const char *zsv_mv_usage_msg[] = {
APPNAME ": move a file and its related cache",
"",
"Usage: " APPNAME " [options] <source> <destination>",
" where options may be:",
" -v,--verbose: verbose output",
" -C,--cache : only move related cache (not the file)",
NULL
};

static int zsv_mv_usage(FILE *target) {
for(int j = 0; zsv_mv_usage_msg[j]; j++)
fprintf(target, "%s\n", zsv_mv_usage_msg[j]);
return target == stdout ? 0 : 1;
}

int ZSV_MAIN_NO_OPTIONS_FUNC(ZSV_COMMAND)(int argc, const char *argv[]) {
int err = 0;
if(argc > 1 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")))
err = zsv_mv_usage(stdout);
else if(argc < 2)
err = zsv_mv_usage(stderr);
else {
const char *source = NULL;
const char *dest = NULL;

char move_file = 1;
char verbose = 0;
for(int i = 1; !err && i < argc; i++) {
const char *arg = argv[i];
if(*arg == '-') {
if(!strcmp(arg, "-v") || !strcmp(arg, "--verbose"))
verbose = 1;
else if(!strcmp(arg, "-C") || !strcmp(arg, "--cache"))
move_file = 0;
else
err = zsv_printerr(1, "Unrecognized option: %s", arg);
} else if(!source)
source = arg;
else if(!dest)
dest = arg;
else
err = zsv_printerr(1, "Unrecognized option: %s", arg);
}

if(!err) {
unsigned char *source_cache_dir = zsv_cache_path((const unsigned char *)source, NULL, 0);
unsigned char *dest_cache_dir = zsv_cache_path((const unsigned char *)dest, NULL, 0);
if(!source || !dest) {
err = zsv_mv_usage(stderr);
} else if(move_file && !zsv_file_exists(source)) {
err = errno = ENOENT;
perror(source);
} else if(move_file && zsv_file_exists(dest)) {
err = errno = EEXIST;
perror(dest);
} else if(zsv_dir_exists((const char *)source_cache_dir) && zsv_dir_exists((const char *)dest_cache_dir)) {
err = errno = EEXIST;
perror((char*)dest_cache_dir);
fprintf(stderr, "Use `mv --cache %s <destination>` to move or `rm --cache %s` to remove, then try again\n",
dest, dest);
} else if(move_file && (verbose ? fprintf(stderr, "Renaming files\n") : 1)
&& rename(source, dest)) {
err = errno;
fprintf(stderr, "%s -> %s: ", source, dest);
perror(NULL);
} else if(zsv_dir_exists((const char *)source_cache_dir) && (verbose ? fprintf(stderr, "Moving caches\n") : 1)
&& rename((char*)source_cache_dir, (char*)dest_cache_dir)) {
err = errno;
fprintf(stderr, "%s -> %s: ", source_cache_dir, dest_cache_dir);
perror(NULL);

// try to revert the prior rename
if(rename(dest, source)) {
fprintf(stderr, "%s -> %s: ", dest, source);
perror(NULL);
}
}
free(source_cache_dir);
free(dest_cache_dir);
}
}
return err;
}
4 changes: 4 additions & 0 deletions app/prop.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,16 @@ const char *zsv_property_usage_msg[] = {
" and options may be one or more of:",
" -d,--header-row-span <value>: set/unset/auto-detect header depth (see below)",
" -R,--skip-head <value> : set/unset/auto-detect initial rows to skip (see below)",
" --list-files : x", // output a list of all cache files
" --clear : delete all properties",
" --clear-orphans : delete properties of all orphaned files in the given file or directory path",
" --auto : guess the best property values. This is equivalent to:",
" -d auto -R auto",
" when using this option, a dash (-) can be used instead",
" of a filepath to read from stdin",
" --save [-f,--overwrite] : (only applicable with --auto) save the detected result",
" --export <output path> : export all properties to a single JSON file (- for stdout)", // to do: add option to check for valid JSON
" --import <input path> : import properties from a single JSON file (- for stdin)", // to do: add option to check for valid JSON
" -f,--overwrite : overwrite any previously-saved properties",
"",
"For --header-row-span or --skip-head options, <value> can be:",
Expand Down
9 changes: 4 additions & 5 deletions app/rm.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@
const char *zsv_rm_usage_msg[] = {
APPNAME ": remove a file and its related cache",
"",
"Usage: " APPNAME " <filepath> <options>",
"Usage: " APPNAME " [options] <filepath>",
" where options may be:",
" -v,--verbose: do not prompt for confirmation",
" -v,--verbose: verbose output",
#ifndef NO_STDIN
" -f,--force : do not prompt for confirmation",
#endif
Expand Down Expand Up @@ -107,11 +107,10 @@ int ZSV_MAIN_NO_OPTIONS_FUNC(ZSV_COMMAND)(int argc, const char *argv[]) {
fprintf(stderr, "Removing %s", filepath);
err = unlink(filepath);
if(err) {
perror(filepath);
if(force)
if(err == ENOENT && force)
err = 0;
else
fprintf(stderr, "Cached files (if any) not removed\n");
perror(filepath);
}
}
if(!err) {
Expand Down
19 changes: 18 additions & 1 deletion app/test/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ TEST_DATA_DIR=${THIS_LIB_BASE}/data
SOURCES= echo count count-pull select select-pull sql 2json serialize flatten pretty desc stack 2db 2tsv jq compare
TARGETS=$(addprefix ${BUILD_DIR}/bin/zsv_,$(addsuffix ${EXE},${SOURCES}))

TESTS=test-blank-leading-rows $(addprefix test-,${SOURCES})
TESTS=test-blank-leading-rows $(addprefix test-,${SOURCES}) test-rm test-mv

COLOR_NONE=\033[0m
COLOR_GREEN=\033[1;32m
Expand Down Expand Up @@ -223,6 +223,23 @@ test-fixed-4-select test-fixed-4-select-pull: ${BUILD_DIR}/bin/zsv_select${EXE}
@${PREFIX} $< ${TEST_DATA_DIR}/fixed-auto3.txt --fixed-auto ${REDIRECT} ${TMP_DIR}/$@.out
@${CMP} ${TMP_DIR}/$@.out expected/test-fixed-4-select.out && ${TEST_PASS} || ${TEST_FAIL}

test-rm: ${BUILD_DIR}/bin/zsv_prop${EXE} ${BUILD_DIR}/bin/zsv_rm${EXE}
@${TEST_INIT}
@echo 'hi' > ${TMP_DIR}/$@.csv
@${PREFIX} ${BUILD_DIR}/bin/zsv_prop${EXE} ${TMP_DIR}/$@.csv -R 1 -d 2 ${REDIRECT} /dev/null
@find ${TMP_DIR}/.zsv/data/$@.csv/props.json -type f >/dev/null
@${PREFIX} ${BUILD_DIR}/bin/zsv_rm${EXE} ${TMP_DIR}/$@.csv -f ${REDIRECT} /dev/null
@find ${TMP_DIR}/.zsv/data/$@.csv/props.json -type f 2>/dev/null && ${TEST_FAIL} || ${TEST_PASS}

test-mv: ${BUILD_DIR}/bin/zsv_prop${EXE} ${BUILD_DIR}/bin/zsv_mv${EXE}
@${TEST_INIT}
@echo 'hi' > ${TMP_DIR}/$@.csv
@rm -rf ${TMP_DIR}/.zsv/data/$@.csv ${TMP_DIR}/.zsv/data/$@-moved.csv ${TMP_DIR}/$@-moved.csv
@${PREFIX} ${BUILD_DIR}/bin/zsv_prop${EXE} ${TMP_DIR}/$@.csv -R 1 -d 2 ${REDIRECT} /dev/null
@find ${TMP_DIR}/.zsv/data/$@.csv/props.json -type f >/dev/null
@${PREFIX} ${BUILD_DIR}/bin/zsv_mv${EXE} ${TMP_DIR}/$@.csv ${TMP_DIR}/$@-moved.csv ${REDIRECT} /dev/null
@find ${TMP_DIR}/.zsv/data/$@-moved.csv/props.json -type f >/dev/null && ${TEST_PASS} || ${TEST_FAIL}


test-blank-leading-rows: test-blank-leading-rows-1 test-blank-leading-rows-2 test-blank-leading-rows-3 test-blank-leading-rows-4

Expand Down
15 changes: 15 additions & 0 deletions app/utils/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,21 @@ void zsv_redirect_file_from_temp(FILE *f, int bak, int old_fd) {
close(bak);
}

#if defined(_WIN32) || defined(WIN32) || defined(WIN)
int zsv_file_exists(const char* filename) {
DWORD attributes = GetFileAttributes(filename);
return (attributes != INVALID_FILE_ATTRIBUTES && !(attributes & FILE_ATTRIBUTE_DIRECTORY));
}
#else
# include <sys/stat.h> // S_IRUSR S_IWUSR

int zsv_file_exists(const char* filename) {
struct stat buffer;
return (stat(filename, &buffer) == 0);
}
#endif


int zsv_file_readable(const char *filename, int *err, FILE **f_out) {
FILE *f;
int rc;
Expand Down
8 changes: 8 additions & 0 deletions include/zsv/utils/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@
*/
char *zsv_get_temp_filename(const char *prefix);

/**
* Check if a file exists and is readable (with fopen + "rb")
*
* @param filename
* @returns: true (1) if file exists
*/
int zsv_file_exists(const char* filename);

/**
* Check if a file exists and is readable (with fopen + "rb")
*
Expand Down

0 comments on commit 392269b

Please sign in to comment.