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 mv command #113

Merged
merged 3 commits into from
Mar 15, 2023
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
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}/[email protected]
@${CMP} ${TMP_DIR}/[email protected] 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}/[email protected]
@${PREFIX} ${BUILD_DIR}/bin/zsv_prop${EXE} ${TMP_DIR}/[email protected] -R 1 -d 2 ${REDIRECT} /dev/null
@find ${TMP_DIR}/.zsv/data/[email protected]/props.json -type f >/dev/null
@${PREFIX} ${BUILD_DIR}/bin/zsv_rm${EXE} ${TMP_DIR}/[email protected] -f ${REDIRECT} /dev/null
@find ${TMP_DIR}/.zsv/data/[email protected]/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}/[email protected]
@rm -rf ${TMP_DIR}/.zsv/data/[email protected] ${TMP_DIR}/.zsv/data/[email protected] ${TMP_DIR}/[email protected]
@${PREFIX} ${BUILD_DIR}/bin/zsv_prop${EXE} ${TMP_DIR}/[email protected] -R 1 -d 2 ${REDIRECT} /dev/null
@find ${TMP_DIR}/.zsv/data/[email protected]/props.json -type f >/dev/null
@${PREFIX} ${BUILD_DIR}/bin/zsv_mv${EXE} ${TMP_DIR}/[email protected] ${TMP_DIR}/[email protected] ${REDIRECT} /dev/null
@find ${TMP_DIR}/.zsv/data/[email protected]/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