minor refactor v1.1.0
authoralex <[email protected]>
Thu, 23 Sep 2021 18:47:36 +0000 (11:47 -0700)
committeralex <[email protected]>
Sat, 25 Sep 2021 07:45:44 +0000 (00:45 -0700)
updated autoconf/automake flags
changed workouts.db location to be dependent on current directory, --homedir now changes directory
add const qualifiers to functions where appropriate
refactored ls
added --name-only option to workouts ls (added for bash completion functionality)
fixed existing ls tests; added more tests to cover --last-done and --name-only
created bash completion script and added to automake

41 files changed:
.gitignore
Makefile.am
bash-completion/workouts [new file with mode: 0644]
configure.ac
include/data.h
include/date.h
include/ls.h
include/opt.h
src/add.c
src/data/attr.c
src/data/recent.c
src/data/setup.c
src/data/workout.c
src/date.c
src/default.c
src/ls/attr.c
src/ls/last.c
src/ls/ls.c
src/ls/name.c [new file with mode: 0644]
src/ls/recent.c
src/ls/single.c
src/ls/workout.c
src/opt/homedir.c
src/opt/rows.c
src/rm.c
src/update.c
src/usage.c
test/integration/package-lock.json
test/integration/test/basic.test.js
test/integration/test/ls.integration.test.js
test/unit/Makefile.am
test/unit/add.tests.c
test/unit/data.attr.tests.c
test/unit/data.recent.tests.c
test/unit/data.workout.tests.c
test/unit/ls.tests.c
test/unit/ls.tests.h [deleted file]
test/unit/test_utils.c
test/unit/test_utils.h
test/unit/toggle.tests.c
test/unit/update.tests.c

index 70f6825d024b7e4a73acf8128e297e8181ff9666..93255bd4db6a2ccc46a06e3ce8232006e3711820 100644 (file)
@@ -1,4 +1,5 @@
 workouts
+!bash-completion/workouts
 workouts.db
 workouts*/
 workouts*.tar.gz
index 6498bf3f9ff4f00efa75fb8950dee4ca6ec4dadf..d556a2b7e50ee3292bc90c99754475292641e9d1 100644 (file)
@@ -1,6 +1,6 @@
 AM_CPPFLAGS = \
-       -I$(top_builddir)/include/ \
-       -I$(top_srcdir)/include/
+       -Wall \
+       -Werror
 
 bin_PROGRAMS = workouts
 workouts_SOURCES = \
@@ -16,6 +16,7 @@ workouts_SOURCES = \
        src/ls/head.c \
        src/ls/last.c \
        src/ls/ls.c \
+       src/ls/name.c \
        src/ls/single.c \
        src/ls/recent.c \
        src/ls/workout.c \
@@ -42,4 +43,7 @@ workouts_SOURCES += \
        include/update.h \
        include/usage.h
 
-SUBDIRS = . test/integration test/unit
+bashcompletiondir = $(datadir)/bash-completion/completions
+dist_bashcompletion_DATA = bash-completion/workouts
+
+SUBDIRS = . test/integration test/unit 
diff --git a/bash-completion/workouts b/bash-completion/workouts
new file mode 100644 (file)
index 0000000..6f15ea1
--- /dev/null
@@ -0,0 +1,126 @@
+_workouts() {
+       IFS=$'\n'
+       COMPREPLY=()
+       local commands=$'add\nls\nrm\ntoggle\nupdate'
+       
+       declare -A options
+       options[--attribute]="--attribute"
+       options[--attr]="--attr"
+       options[-a]="-a"
+       options[--help]="--help"
+       options[--homedir]="--homedir"
+       options[-d]="-d"
+       options[--quiet]="--quiet"
+       options[--recent]="--recent"
+       options[-l]="-l"
+       options[--rows]="--rows"
+       options[-r]="-r"
+       options[--verbose]="--verbose"
+       options[-v]="-v"
+       options[--workout]="--workout"
+       options[-w]="-w"
+
+       local ls_options=$'--filter\n-f\n--last-done\n--name-only'
+
+       local type="default"
+       local utility="workouts"
+
+       # search for type (only bother with latest; rest will be discarded)
+       for arg in "${COMP_WORDS[@]}"; do
+               case "$arg" in
+                       --attribute|--attr|-a) type="attribute" ;;
+                       --recent|-l) type="recent" ;;
+                       --workout|-w) type="workout" ;;
+                       add) utility="add" ;;
+                       ls) utility="ls" ;;
+                       rm) utility="rm" ;;
+                       toggle) utility="toggle" ;;
+                       update) utility="update" ;;
+               esac
+               unset options[$arg]
+       done
+
+       opts=
+       for val in "${options[@]}"; do
+               opts+="$val"
+               opts+=$'\n'
+       done
+
+       case "$3" in
+               add)
+                       # don't need completions for attributes
+                       if [[ "$type" == "recent" ]]; then
+                               COMPREPLY+=($(workouts -w ls --name-only | grep "$2"))
+                       fi
+                       # don't need completions for workouts
+                       ;;
+               --homedir|-d)
+                       compopt -o nospace
+                       COMPREPLY+=($(compgen -o dirnames -- "$2"))
+                       ;;
+               --filter|-f)
+                       local count=($(workouts -a ls | wc -l))
+                       local template
+                       for (( i = 0; i< "$count"; i++ )); do
+                               template+="x"
+                       done
+                       COMPREPLY+=("$template")
+                       ;;
+               ls)
+                       case "$type" in
+                               # don't need completions for attributes
+                               # don't need completions for recent
+                               workout|default)
+                                       COMPREPLY+=($(compgen -W "${ls_options}" -- "$2"))
+                                       COMPREPLY+=($(workouts -w ls --name-only | grep -- "$2"))
+                                       ;;
+                       esac
+                       ;;
+               rm)
+                       case "$type" in
+                               attribute)
+                                       COMPREPLY+=($(workouts --attribute ls | grep "$2"))
+                                       ;;
+                               recent|default)
+                                       COMPREPLY+=($(workouts -w ls --name-only | grep "$2"))
+                                       ;;
+                               workout)
+                                       COMPREPLY+=($(workouts -w ls --name-only | grep "$2"))
+                                       ;;
+                       esac
+                       ;;
+               toggle)
+                       case "$type" in
+                               # don't need completions for attributes
+                               # don't need completions for recent
+                               workout|default)
+                                       COMPREPLY+=($(workouts -w ls --name-only | grep "$2"))
+                       esac
+                       ;;
+               update)
+                       case "$type" in
+                               attribute)
+                                       COMPREPLY+=($(workouts --attribute ls | grep "$2"))
+                                       ;;
+                               recent|default)
+                                       COMPREPLY+=($(workouts -w ls --name-only | grep "$2"))
+                                       ;;
+                               workout)
+                                       COMPREPLY+=($(workouts -w ls --name-only | grep "$2"))
+                                       ;;
+                       esac
+                       ;;
+               workouts)
+                       COMPREPLY+=($(compgen -W "${commands}" -- "$2"))
+                       COMPREPLY+=($(compgen -W "${opts}" -- "$2"))
+                       ;;
+               *)
+                       if [[ "$utility" == "workouts" ]]; then
+                               COMPREPLY+=($(compgen -W "${commands}" -- "$2"))
+                               COMPREPLY+=($(compgen -W "${opts}" -- "$2"))
+                       fi
+                       ;;
+       esac
+}
+
+complete -F _workouts workouts
index c57fecc25d5158ff050d8df0061de83009088f9c..138365934a124615928379409fbef6226686f5fd 100644 (file)
@@ -4,7 +4,7 @@ AC_INIT([workouts], [1.0.0])
 # Store build files not in main directory
 AC_CONFIG_AUX_DIR([build-aux])
 
-AM_INIT_AUTOMAKE([foreign subdir-objects nostdinc -Wall -Werror])
+AM_INIT_AUTOMAKE([foreign subdir-objects -Wall -Werror])
 
 AC_CONFIG_SRCDIR([src/main.c])
 AC_CONFIG_HEADERS([include/config.h])
@@ -35,7 +35,7 @@ AM_CONDITIONAL([HAVE_VALGRIND], [test -n "$VALGRIND"])
 AC_MSG_CHECKING([if debugging])
 if test x$enable_debug != xno; then
        AC_MSG_RESULT(yes)
-       CFLAGS="-ggdb -O0"
+       CFLAGS="-ggdb3 -O0"
 else
        AC_MSG_RESULT(no)
 fi
index 66264cfa02645eb1e0df0bd36f253f176f2395a2..bc0207a54074985e95504d9b1185eb06161921a6 100644 (file)
@@ -7,6 +7,7 @@
 
 #include<opt.h>
 
+#define DB_FILENAME "workouts.db"
 #define EMPTY_STRING ""
 
 // attributes
 int attribute_count();
 
 #define ATTRIBUTE_DELETE_SQL "DELETE FROM `" ATTRIBUTE_TABLE_NAME_SQL "` WHERE `name` = ?;"
-int attribute_delete(char*);
+int attribute_delete(const char*);
 
 #define ATTRIBUTE_GET_SQL "SELECT `name` FROM " ATTRIBUTE_TABLE_NAME_SQL " WHERE 1 ORDER BY `order` ASC;"
 int attribute_get(void (*)(const unsigned char*));
-int attribute_index(char*);
+int attribute_index(const char*);
 void attribute_index_helper(const unsigned char*);
 
 #define ATTRIBUTE_INSERT_SQL "INSERT INTO " ATTRIBUTE_TABLE_NAME_SQL " (`name`,`order`) VALUES (?,(SELECT MAX(`order`)+1 FROM `" ATTRIBUTE_TABLE_NAME_SQL "`));"
-int attribute_insert(char*);
-int attribute_parse(char*,int*,int*);
-unsigned int attribute_template(char*,int);
+int attribute_insert(const char*);
+int attribute_parse(const char*,int*,int*);
+unsigned int attribute_template(const char*,int);
 
 #define ATTRIBUTE_UPDATE_SQL "UPDATE `" ATTRIBUTE_TABLE_NAME_SQL "` SET `name` = ? WHERE `name` = ?;"
-int attribute_update(char*,char*);
+int attribute_update(const char*,const char*);
 
 // recent
 #define RECENT_TABLE_NAME_SQL "recent"
@@ -48,18 +49,18 @@ int attribute_update(char*,char*);
        ");"
 
 #define RECENT_DELETE_SQL "DELETE FROM `" RECENT_TABLE_NAME_SQL "` WHERE `name` = ? AND `date` = ?;"
-int recent_delete(char*,char*);
+int recent_delete(const char*,const char*);
 
 #define RECENT_GET_BASE_SQL "SELECT name, date FROM `" RECENT_TABLE_NAME_SQL "`"
 #define RECENT_GET_SQL RECENT_GET_BASE_SQL " ORDER BY date(`date`) DESC,`name` ASC LIMIT ?;"
 #define RECENT_GET_SEARCH_SQL RECENT_GET_BASE_SQL " WHERE name LIKE ('%' || ? || '%') ORDER BY date(`date`) DESC,`name` ASC LIMIT ?;"
-int recent_get(char*,int,void(*)(const unsigned char*,const unsigned char*));
+int recent_get(const char*,int,void(*)(const unsigned char*,const unsigned char*));
 
 #define RECENT_INSERT_SQL "INSERT INTO " RECENT_TABLE_NAME_SQL " (name,date) VALUES (?,?);"
-int recent_insert(char*,char*);
+int recent_insert(const char*,const char*);
 
 #define RECENT_UPDATE_SQL "UPDATE `" RECENT_TABLE_NAME_SQL "` SET date = ? WHERE date = ? AND name = ?;"
-int recent_update(char*,char*,char*);
+int recent_update(const char*,const char*,const char*);
 
 // workouts
 #define WORKOUT_TABLE_NAME_SQL "workouts"
@@ -70,7 +71,7 @@ int recent_update(char*,char*,char*);
        ");"
 
 #define WORKOUT_DELETE_SQL "DELETE FROM `" WORKOUT_TABLE_NAME_SQL "` WHERE `name` = ?;"
-int workout_delete(char*);
+int workout_delete(const char*);
 
 #define WORKOUT_GET_BASE_SQL "SELECT " \
        "`" WORKOUT_TABLE_NAME_SQL "`.name, " \
@@ -82,17 +83,17 @@ int workout_delete(char*);
 
 #define WORKOUT_GET_SQL WORKOUT_GET_BASE_SQL " WHERE (attributes & ?1) = ?1 AND (~attributes & ?2) = ?2 ORDER BY `last` DESC, `" WORKOUT_TABLE_NAME_SQL "`.name ASC LIMIT ?;"
 #define WORKOUT_GET_SEARCH_SQL WORKOUT_GET_BASE_SQL " WHERE `" WORKOUT_TABLE_NAME_SQL "`.name LIKE ('%' || ? || '%') AND (attributes & ?2) = ?2 AND (~attributes & ?3) = ?3 ORDER BY `last` DESC, `" WORKOUT_TABLE_NAME_SQL "`.name ASC LIMIT ?;"
-int workout_get(char*,char*,int,void (*)(const unsigned char*,int,const unsigned char*));
+int workout_get(const char*,const char*,int,void (*)(const unsigned char*,int,const unsigned char*));
 
 #define WORKOUT_INSERT_SQL "INSERT INTO " WORKOUT_TABLE_NAME_SQL " (name,attributes) VALUES (?,?);"
-int workout_insert(char*, unsigned int);
+int workout_insert(const char*, unsigned int);
 
-int workout_toggle(char*,char*);
+int workout_toggle(const char*,const char*);
 void workout_toggle_helper(const unsigned char*,int,const unsigned char*);
 
 #define WORKOUT_UPDATE_NAME_SQL "UPDATE `" WORKOUT_TABLE_NAME_SQL "` SET name = ? WHERE name = ?;"
 #define WORKOUT_UPDATE_ATTRIBUTES_SQL "UPDATE `" WORKOUT_TABLE_NAME_SQL "` SET attributes = ? WHERE name = ?;"
-int workout_update(char*,char*,int);
+int workout_update(const char*,const char*,int);
 
 #define WORKOUT_SHIFT_ATTRIBUTE_FLAGS_SQL "UPDATE `" WORKOUT_TABLE_NAME_SQL "` SET attributes = (((attributes<<(?3 - ?1))&?2)>>(?3 - ?1)) + ((attributes>>(?1+1))<<?1);"
 
index 2a1d7f44668168d20896fedb4b45b4c4d03269dc..aaf586f9b3ad9de13860bfbb14179919bd70c223 100644 (file)
@@ -10,6 +10,6 @@
 
 #define DATE_MESSAGE_INVALID_DATE "invalid date given\n"
 
-int date_parse(char*,char*,size_t);
+int date_parse(const char*,char*,size_t);
 
 #endif
index 401fc0e66e085d88b078b5a2f27723b5c1d74302..b0d12d5bb68d548e112410612554f919b852a1a0 100644 (file)
 #define LS_GENERATE_LAST_DONE_INVALID_DATE_OUTPUT "unexpected output\n"
 #define LS_RECENT_COMMAND_FAILED "command failed\n"
 #define LS_PRINT_SINGLE_ATTRIBUTE_GET_FAILED "arribute get failed unexpectedly\n"
+#define LS_MESSAGE_INVALID_FORMAT "invalid format\n"
+#define LS_MESSAGE_INVALID_FILTER "invalid filter; wrong number of attributes given (%d != %d)\n"
+
+#define WORKOUT_PRINT_ATTRIBUTE_HEADER "%-30.30s\t"
+#define WORKOUT_PRINT_ATTRIBUTE_LABEL "Attributes:"
+#define WORKOUT_PRINT_ATTRIBUTE_END "\n"
 
 struct ls_helper {
        int i;
@@ -23,20 +29,22 @@ struct ls_helper {
        time_t now;
 };
 
+enum ls_format {
+       LS_FORMAT_LAST_DONE,
+       LS_FORMAT_NAME_ONLY,
+       LS_FORMAT_WITH_ATTRIBUTES,
+       LS_FORMAT_DEFAULT = LS_FORMAT_WITH_ATTRIBUTES
+};
+
 extern struct ls_helper helper;
 
-void generate_last_done(const unsigned char*,int, const unsigned char*);
 int last_done();
 int ls(int,char**);
 int ls_attribute();
+int ls_name_only(const char*,const char*);
 int ls_recent(int,char**);
-int ls_single(char*,char*);
+int ls_single(const char*,const char*);
 int ls_workout(int,char**);
-void print_attribute(const unsigned char*);
-void print_last_done();
-void print_recent(const unsigned char*,const unsigned char*);
-void print_single(const unsigned char*,int,const unsigned char*);
 void print_header(const unsigned char*);
-void print_workout(const unsigned char*,int,const unsigned char*);
 
 #endif
index b54cf9f488f9cf0e8bdbb695ef063b157a50a2ac..f830de79a9d2f43d696d4b4cb3df70e0ff58adfe 100644 (file)
@@ -1,11 +1,11 @@
 #ifndef __OPT_H_
 #define __OPT_H_
 
-#include<dirent.h>
 #include<errno.h>
 #include<stdio.h>
 #include<stdlib.h>
 #include<string.h>
+#include<unistd.h>
 
 #include<log.h>
 
@@ -18,8 +18,6 @@ enum workout_data_type {
 
 // global options
 struct options {
-       char *db_location;
-       char *homedir;
        int rows;
        enum log_level verbose;
        enum workout_data_type target;
@@ -28,9 +26,9 @@ struct options {
 extern struct options global_opts;
 
 // specific option setters
-int opt_set_homedir(char*);
+int opt_set_homedir(const char*);
 void opt_set_log_level(enum log_level);
 void opt_set_rows(int);
 void opt_set_target(enum workout_data_type);
 
-#endif
\ No newline at end of file
+#endif
index 6d5835453b9825c28340eb87e622ac20dbc431ea..f6d2805bab02a1f3593be2dd4b22c5bc57699b18 100644 (file)
--- a/src/add.c
+++ b/src/add.c
@@ -9,6 +9,8 @@ int add(int argc, char **argv) {
                        return add_recent(argc,argv);
                case WORKOUT_DATA_TYPE_WORKOUT:
                        return add_workout(argc,argv);
+               default:
+                       return EXIT_FAILURE;
        }
 }
 
index 2d06d0931c6941fb0f72f47d95e7a53cb754fd44..d4b7ba9845645a21d3ecb950bf2fde8fc416f6df 100644 (file)
@@ -6,7 +6,7 @@ int attribute_count() {
        sqlite3 *db_p = NULL;
        sqlite3_stmt *stmt_p = NULL;
 
-       if(sqlite3_open_v2(global_opts.db_location,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; }
+       if(sqlite3_open_v2(DB_FILENAME,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; }
 
        if(sqlite3_prepare_v2(db_p,ATTRIBUTE_COUNT_SQL,-1,&stmt_p,NULL)!=SQLITE_OK) { goto cleanup; }
 
@@ -26,7 +26,7 @@ int attribute_count() {
                return -1;
 }
 
-int attribute_delete(char *name) {
+int attribute_delete(const char *name) {
        sqlite3 *db_p = NULL;
        sqlite3_stmt *stmt_p = NULL;
        int i, index, count, mask;
@@ -45,7 +45,7 @@ int attribute_delete(char *name) {
                i--;
        }
 
-       if(sqlite3_open_v2(global_opts.db_location,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; }
+       if(sqlite3_open_v2(DB_FILENAME,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; }
 
 
        if(sqlite3_exec(db_p,"BEGIN;",NULL,NULL,NULL)!=SQLITE_OK) { goto cleanup; }
@@ -85,7 +85,7 @@ int attribute_get(void (*print)(const unsigned char*)) {
        sqlite3 *db_p = NULL;
        sqlite3_stmt *stmt_p = NULL;
 
-       if(sqlite3_open_v2(global_opts.db_location,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; }
+       if(sqlite3_open_v2(DB_FILENAME,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; }
 
        if(sqlite3_prepare_v2(db_p,ATTRIBUTE_GET_SQL,-1,&stmt_p,NULL)!=SQLITE_OK) { goto cleanup; }
 
@@ -112,7 +112,7 @@ int attribute_get(void (*print)(const unsigned char*)) {
 struct attribute_indexer {
        int i;
        int index;
-       char *name;
+       const char *name;
 };
 
 struct attribute_indexer indexer = {
@@ -121,7 +121,7 @@ struct attribute_indexer indexer = {
        NULL,
 };
 
-int attribute_index(char *name) {
+int attribute_index(const char *name) {
        indexer.i = 0;
        indexer.index = -1;
        indexer.name = name;
@@ -138,11 +138,11 @@ void attribute_index_helper(const unsigned char *name) {
        indexer.i++;
 }
 
-int attribute_insert(char *name) {
+int attribute_insert(const char *name) {
        sqlite3 *db_p = NULL;
        sqlite3_stmt *stmt_p = NULL;
 
-       if(sqlite3_open_v2(global_opts.db_location,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; }
+       if(sqlite3_open_v2(DB_FILENAME,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; }
 
        if(sqlite3_prepare_v2(db_p,ATTRIBUTE_INSERT_SQL,-1,&stmt_p,NULL)!=SQLITE_OK) { goto cleanup; }
        if(sqlite3_bind_text(stmt_p,1,name,-1,SQLITE_STATIC)!=SQLITE_OK) { goto cleanup; }
@@ -160,7 +160,7 @@ int attribute_insert(char *name) {
                return -1;
 }
 
-int attribute_parse(char *str, int *required, int *exclude) {
+int attribute_parse(const char *str, int *required, int *exclude) {
        if(NULL==str) { return -1; }
        if(NULL==required) { return -1; }
 
@@ -185,7 +185,7 @@ int attribute_parse(char *str, int *required, int *exclude) {
        return 1;
 }
 
-unsigned int attribute_template(char *p, int len) {
+unsigned int attribute_template(const char *p, int len) {
        unsigned int attr_flags;
        
        attr_flags = 0;
@@ -199,11 +199,11 @@ unsigned int attribute_template(char *p, int len) {
        return attr_flags;
 }
 
-int attribute_update(char *from, char *to) {
+int attribute_update(const char *from, const char *to) {
        sqlite3 *db_p = NULL;
        sqlite3_stmt *stmt_p = NULL;
 
-       if(sqlite3_open_v2(global_opts.db_location,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; }
+       if(sqlite3_open_v2(DB_FILENAME,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; }
        
        if(sqlite3_prepare_v2(db_p,ATTRIBUTE_UPDATE_SQL,-1,&stmt_p,NULL)!=SQLITE_OK) { goto cleanup; }
        if(sqlite3_bind_text(stmt_p,1,to,-1,SQLITE_STATIC)!=SQLITE_OK) { goto cleanup; }
index 2fa96e1c12f17c9e14d960290910a2b257a05354..f665eb9b1bcd19252edc2873c607b47582a179bd 100644 (file)
@@ -1,10 +1,10 @@
 #include<data.h>
 
-int recent_delete(char *workout, char *date) {
+int recent_delete(const char *workout, const char *date) {
        sqlite3 *db_p = NULL;
        sqlite3_stmt *stmt_p = NULL;
 
-       if(sqlite3_open_v2(global_opts.db_location,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; }
+       if(sqlite3_open_v2(DB_FILENAME,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; }
 
        if(sqlite3_prepare_v2(db_p,RECENT_DELETE_SQL,-1,&stmt_p,NULL)!=SQLITE_OK) { goto cleanup; }
        if(sqlite3_bind_text(stmt_p,1,workout,-1,SQLITE_STATIC)!=SQLITE_OK) { goto cleanup; }
@@ -23,11 +23,11 @@ int recent_delete(char *workout, char *date) {
                return -1;
 }
 
-int recent_get(char *term, int limit, void (*f)(const unsigned char*,const unsigned char*)) {
+int recent_get(const char *term, int limit, void (*f)(const unsigned char*,const unsigned char*)) {
        sqlite3 *db_p = NULL;
        sqlite3_stmt *stmt_p = NULL;
 
-       if(sqlite3_open_v2(global_opts.db_location,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; }
+       if(sqlite3_open_v2(DB_FILENAME,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; }
 
        if(NULL==term) {
                if(sqlite3_prepare_v2(db_p,RECENT_GET_SQL,-1,&stmt_p,NULL)!=SQLITE_OK) { goto cleanup; }
@@ -58,11 +58,11 @@ int recent_get(char *term, int limit, void (*f)(const unsigned char*,const unsig
                return -1;
 }
 
-int recent_insert(char *workout, char *date) {
+int recent_insert(const char *workout, const char *date) {
        sqlite3 *db_p = NULL;
        sqlite3_stmt *stmt_p = NULL;
 
-       if(sqlite3_open_v2(global_opts.db_location,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; }
+       if(sqlite3_open_v2(DB_FILENAME,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; }
 
        if(sqlite3_exec(db_p,"PRAGMA foreign_keys = ON;",NULL,NULL,NULL)!=SQLITE_OK) { goto cleanup; }
 
@@ -83,11 +83,11 @@ int recent_insert(char *workout, char *date) {
                return -1;
 }
 
-int recent_update(char *workout, char *from_date, char *to_date) {
+int recent_update(const char *workout, const char *from_date, const char *to_date) {
        sqlite3 *db_p = NULL;
        sqlite3_stmt *stmt_p = NULL;
 
-       if(sqlite3_open_v2(global_opts.db_location,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; }
+       if(sqlite3_open_v2(DB_FILENAME,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; }
        
        if(sqlite3_prepare_v2(db_p,RECENT_UPDATE_SQL,-1,&stmt_p,NULL)!=SQLITE_OK) { goto cleanup; }
        if(sqlite3_bind_text(stmt_p,1,to_date,-1,SQLITE_STATIC)!=SQLITE_OK) { goto cleanup; }
index 5d96a325662e6faf80c92010e2da151c7d0cfaa2..3b001d54555f1f48315ea1df69b1caeea6bc0f17 100644 (file)
@@ -3,7 +3,7 @@
 int setup() {
        sqlite3 *db_p = NULL;
        
-       if(sqlite3_open_v2(global_opts.db_location,&db_p,SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,NULL)!=SQLITE_OK) { goto cleanup; }
+       if(sqlite3_open_v2(DB_FILENAME,&db_p,SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,NULL)!=SQLITE_OK) { goto cleanup; }
 
        if(sqlite3_exec(db_p,CREATE_SCHEMA_SQL,NULL,NULL,NULL)!=SQLITE_OK) { goto cleanup; }
        
@@ -13,4 +13,4 @@ int setup() {
        cleanup:
                if(db_p!=NULL) { sqlite3_close_v2(db_p); }
                return -1;
-}
\ No newline at end of file
+}
index 735728d702fbcf862c2b6c504141a669dd162676..d6328a2ea3a5be15b82f5eca45bed1c6051b8b65 100644 (file)
@@ -1,10 +1,10 @@
 #include<data.h>
 
-int workout_delete(char *name) {
+int workout_delete(const char *name) {
        sqlite3 *db_p = NULL;
        sqlite3_stmt *stmt_p = NULL;
 
-       if(sqlite3_open_v2(global_opts.db_location,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; }
+       if(sqlite3_open_v2(DB_FILENAME,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; }
 
        if(sqlite3_exec(db_p,"PRAGMA foreign_keys = ON;",NULL,NULL,NULL)!=SQLITE_OK) { goto cleanup; }
 
@@ -24,7 +24,7 @@ int workout_delete(char *name) {
                return -1;
 }
 
-int workout_get(char *term, char *filter, int limit, void (*print_row)(const unsigned char*,int,const unsigned char*)) {
+int workout_get(const char *term, const char *filter, int limit, void (*print_row)(const unsigned char*,int,const unsigned char*)) {
        sqlite3 *db_p = NULL;
        sqlite3_stmt *stmt_p = NULL;
 
@@ -34,7 +34,7 @@ int workout_get(char *term, char *filter, int limit, void (*print_row)(const uns
                if(attribute_parse(filter,&required,&exclude)<0) { return -1; }
        }
 
-       if(sqlite3_open_v2(global_opts.db_location,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; }
+       if(sqlite3_open_v2(DB_FILENAME,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; }
 
        if(NULL==term) {
                if(sqlite3_prepare_v2(db_p,WORKOUT_GET_SQL,-1,&stmt_p,NULL)!=SQLITE_OK) { goto cleanup; }
@@ -70,11 +70,11 @@ int workout_get(char *term, char *filter, int limit, void (*print_row)(const uns
                return -1;
 }
 
-int workout_insert(char *name, unsigned int flags) {
+int workout_insert(const char *name, unsigned int flags) {
        sqlite3 *db_p = NULL;
        sqlite3_stmt *stmt_p = NULL;
 
-       if(sqlite3_open_v2(global_opts.db_location,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; }
+       if(sqlite3_open_v2(DB_FILENAME,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; }
 
        if(sqlite3_prepare_v2(db_p,WORKOUT_INSERT_SQL,-1,&stmt_p,NULL)!=SQLITE_OK) { goto cleanup; }
        if(sqlite3_bind_text(stmt_p,1,name,-1,SQLITE_STATIC)!=SQLITE_OK) { goto cleanup; }
@@ -94,7 +94,7 @@ int workout_insert(char *name, unsigned int flags) {
 }
 
 struct workout_toggler {
-       char *name;
+       const char *name;
        int attributes;
 };
 
@@ -103,7 +103,7 @@ struct workout_toggler toggler = {
        0
 };
 
-int workout_toggle(char *name, char *attribute) {
+int workout_toggle(const char *name, const char *attribute) {
        int index = attribute_index(attribute);
        if(index<0) { return -1; }
 
@@ -134,11 +134,11 @@ void workout_toggle_helper(const unsigned char *name, int flags, const unsigned
        }
 }
 
-int workout_update(char *old, char *name, int flags) {
+int workout_update(const char *old, const char *name, int flags) {
        sqlite3 *db_p = NULL;
        sqlite3_stmt *stmt_p = NULL;
 
-       if(sqlite3_open_v2(global_opts.db_location,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; }
+       if(sqlite3_open_v2(DB_FILENAME,&db_p,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) { goto cleanup; }
        
        if(sqlite3_exec(db_p,"PRAGMA foreign_keys = ON;",NULL,NULL,NULL)!=SQLITE_OK) { goto cleanup; }
 
index 43ec5da3d43ee8c18d20e09cdba8e36386b118ce..f255bcb69be8a462b9b1df1c6ce5c76502bc161b 100644 (file)
@@ -1,6 +1,6 @@
 #include<date.h>
 
-int date_parse(char *str, char *buf, size_t len) {
+int date_parse(const char *str, char *buf, size_t len) {
        assert(DATE_BUF_LEN==len);
 
        if(NULL==str) {
index 0469748241609b9cee9cd85d6ca4f8a86a940c30..33a83ac6ddbb843182a688ae01e0a8b432c29ecb 100644 (file)
@@ -1,30 +1,12 @@
 #include<default.h>
 
 struct options global_opts = {
-       NULL, /* db_location */
-       NULL, /* homedir */
        -1, /* rows */
-       0 /* verbose */
+       0, /* verbose */
+       0 /* target */
 };
 
 int defaults() {
-       char *p;
-       
-       // homedir
-       p = getenv("WORKOUTS_HOME");
-       if(p==NULL) {
-               p = getenv("HOME");
-               if(NULL==p) {
-                       log_err("HOME or WORKOUTS_HOME env variable must be defined\n");
-                       return -1;
-               }
-       }
-
-       if(opt_set_homedir(p)<0) {
-               log_err("HOME or WORKOUTS_HOME env value invalid\n");
-               return -1;
-       }
-
        opt_set_rows(-1);
        
        opt_set_log_level(LOG_LEVEL_DEFAULT);
@@ -32,4 +14,4 @@ int defaults() {
        opt_set_target(WORKOUT_DATA_TYPE_DEFAULT);
 
        return 0;
-}
\ No newline at end of file
+}
index e0be01ff93ff1fed95a18c12425407a753cce564..efa10ebc405936b765bc0fbed78a0ee331a8426b 100644 (file)
@@ -1,5 +1,7 @@
 #include<ls.h>
 
+static void print_attribute(const unsigned char*);
+
 int ls_attribute() {
        if(attribute_get(&print_attribute)<0) { return EXIT_FAILURE; }
 
@@ -8,6 +10,6 @@ int ls_attribute() {
 
 #define ATTRIBUTE_PRINT_FORMAT "%s\n"
 
-void print_attribute(const unsigned char *name) {
+static void print_attribute(const unsigned char *name) {
        printf(ATTRIBUTE_PRINT_FORMAT,name);
 }
index b52e128444d6dc2394658ca06464bbff43004aeb..bf36078fc7ebebff9e2b44aeec7874d317f80e56 100644 (file)
@@ -1,14 +1,20 @@
 #include<ls.h>
 
-int last_done(char *search_term, char *filter_p) {
+static void generate_last_done(const unsigned char*,int, const unsigned char*);
+static void print_last_done();
+
+int last_done(const char *search_term, const char *filter_p) {
        int i;
 
+       printf(WORKOUT_PRINT_ATTRIBUTE_HEADER,WORKOUT_PRINT_ATTRIBUTE_LABEL);
+       if(attribute_get(&print_header)<0) { return EXIT_FAILURE; }
+       printf(WORKOUT_PRINT_ATTRIBUTE_END);
+
        helper.days_ago = malloc(helper.attr_count*sizeof(int));
        if(NULL==helper.days_ago) { return EXIT_FAILURE; }
        for(i=0;i<helper.attr_count;i++) {
                helper.days_ago[i] = INT_MAX;
        }
-       time(&helper.now);
        
        if(workout_get(
                search_term, /* term */
@@ -16,24 +22,25 @@ int last_done(char *search_term, char *filter_p) {
                global_opts.rows, /* limit */
                &generate_last_done /* print_function */
        )<0) {
+               free(helper.days_ago);
                return EXIT_FAILURE;
        }
 
        print_last_done();
        free(helper.days_ago);
 
-       return EXIT_FAILURE;
+       return EXIT_SUCCESS;
 }
 
-void generate_last_done(const unsigned char *workout, int flags, const unsigned char *date) {
+static void generate_last_done(const unsigned char *workout, int flags, const unsigned char *date) {
        int i, YY, MM, DD, diff;
        struct tm when;
 
        if(date!=NULL) {
                for(i=0;i<helper.attr_count;i++) {
-                       if(flags&1>0) {
+                       if((flags&1)>0) {
                                memset(&when,0,sizeof(struct tm));
-                               if(sscanf(date,"%d-%d-%d",&YY,&MM,&DD)!=3) {
+                               if(sscanf((const char*)date,"%d-%d-%d",&YY,&MM,&DD)!=3) {
                                        log_err(LS_GENERATE_LAST_DONE_INVALID_DATE_OUTPUT);
                                }
 
@@ -56,7 +63,7 @@ void generate_last_done(const unsigned char *workout, int flags, const unsigned
 #define LAST_DONE_PRINT_ATTRIBUTE "%d\t"
 #define LAST_DONE_PRINT_SUGGESTED_LABEL "Suggested filter: "
 
-void print_last_done() {
+static void print_last_done() {
        int i, max = 0;
        printf(LAST_DONE_PRINT_HEADER,LAST_DONE_PRINT_LABEL);
        for(i=0;i<helper.attr_count;i++) {
index 226b8fee248441babf37a1216894b24c31efa6a8..472acf4f42c74c99cc372303d787494b71da4627 100644 (file)
@@ -1,11 +1,12 @@
 #include<ls.h>
 
-struct ls_helper helper = {
-       0,
-       0
-};
+struct ls_helper helper;
+
+static void helper_init();
 
 int ls(int argc, char **argv) {
+       helper_init();
+
        switch(global_opts.target) {
                case WORKOUT_DATA_TYPE_ATTRIBUTE:
                        return ls_attribute();
@@ -14,5 +15,14 @@ int ls(int argc, char **argv) {
                case WORKOUT_DATA_TYPE_DEFAULT:
                case WORKOUT_DATA_TYPE_WORKOUT:
                        return ls_workout(argc,argv);
+               default:
+                       return EXIT_FAILURE;
        }
 }
+
+static void helper_init() {
+       helper.i = 0;
+       helper.attr_count = 0;
+       helper.days_ago = NULL;
+       time(&helper.now);
+}
diff --git a/src/ls/name.c b/src/ls/name.c
new file mode 100644 (file)
index 0000000..bb2998c
--- /dev/null
@@ -0,0 +1,20 @@
+#include<ls.h>
+
+static void print_workout_name(const unsigned char*,int,const unsigned char*);
+
+int ls_name_only(const char *search_term, const char *filter_p) {
+       if(workout_get(
+               search_term, /* term */
+               filter_p, /* filter */
+               global_opts.rows, /* limit */
+               &print_workout_name /* print function */
+       )<0) { return EXIT_FAILURE; }
+
+       return EXIT_SUCCESS;
+}
+
+#define WORKOUT_PRINT_NAME_ONLY "%s\n"
+
+static void print_workout_name(const unsigned char *name, int attr_flags, const unsigned char *last) {
+       printf(WORKOUT_PRINT_NAME_ONLY,name);
+}
index 8dbbeff0c82e2b7d99ce53a6206d8958a5505dd6..a97a382cc07ef6751d0cf448f8536b7a1dab4a6b 100644 (file)
@@ -1,5 +1,7 @@
 #include<ls.h>
 
+static void print_recent(const unsigned char*,const unsigned char*);
+
 int ls_recent(int argc, char **argv) {
        if(recent_get(
                (argc>=2)?argv[1]:NULL, /* term */
@@ -15,6 +17,6 @@ int ls_recent(int argc, char **argv) {
 
 #define RECENT_PRINT_FORMAT "%-30.30s\t%s\n"
 
-void print_recent(const unsigned char *workout, const unsigned char *date) {
+static void print_recent(const unsigned char *workout, const unsigned char *date) {
        printf(RECENT_PRINT_FORMAT,workout,date);
 }
index 2d2db867a995c92c71237c6158d3ca0024b2c2a6..dd79c54d54d4aafbc6c3e5ae472098d6c6e08c2e 100644 (file)
@@ -1,6 +1,8 @@
 #include<ls.h>
 
-int ls_single(char *search_term, char *filter_p) {
+static void print_single(const unsigned char*,int,const unsigned char*);
+
+int ls_single(const char *search_term, const char *filter_p) {
        if(workout_get(
                search_term, /* term */
                filter_p, /* filter */
@@ -19,19 +21,19 @@ int ls_single(char *search_term, char *filter_p) {
 #define WORKOUT_PRINT_SINGLE_LAST_DONE "[Last done: %s]\n"
 #define WORKOUT_PRINT_SINGLE_LAST_NULL "[Last done: N/A]\n"
 
-void print_single(const unsigned char *name, int attr_flags, const unsigned char *last) {
+static void print_single(const unsigned char *name, int attr_flags, const unsigned char *last) {
        int i;
 
-       printf(WORKOUT_PRINT_SINGLE_ATTRIBUTE_HEADER,strlen(name),WORKOUT_PRINT_SINGLE_ATTRIBUTE_LABEL);
+       printf(WORKOUT_PRINT_SINGLE_ATTRIBUTE_HEADER,(int)strlen((const char*)name),WORKOUT_PRINT_SINGLE_ATTRIBUTE_LABEL);
        if(attribute_get(&print_header)<0) {
                log_err(LS_PRINT_SINGLE_ATTRIBUTE_GET_FAILED);
                        return;
        }
        printf(WORKOUT_PRINT_SINGLE_ATTRIBUTE_END);
 
-       printf(WORKOUT_PRINT_SINGLE_NAME,strlen(WORKOUT_PRINT_SINGLE_ATTRIBUTE_LABEL),name);
+       printf(WORKOUT_PRINT_SINGLE_NAME,(int)strlen(WORKOUT_PRINT_SINGLE_ATTRIBUTE_LABEL),name);
        for(i=0;i<helper.attr_count;i++) {
-               printf(WORKOUT_PRINT_SINGLE_ATTRIBUTE,(attr_flags&1>0)?"yes":"no");
+               printf(WORKOUT_PRINT_SINGLE_ATTRIBUTE,((attr_flags&1)>0)?"yes":"no");
                attr_flags >>= 1;
        }
 
index 30d6667e8dfd076c4457a4020bd995292463c845..516ce4550638315ede9481602e6cdf15afcc454e 100644 (file)
@@ -1,12 +1,12 @@
 #include<ls.h>
 
-#define WORKOUT_PRINT_ATTRIBUTE_HEADER "%-30.30s\t"
-#define WORKOUT_PRINT_ATTRIBUTE_LABEL "Attributes:"
-#define WORKOUT_PRINT_ATTRIBUTE_END "\n"
+static int ls_with_attributes(const char*,const char*);
+static void print_workout(const unsigned char*,int,const unsigned char*);
 
 static struct option ls_long_options[] = {
        {"filter", required_argument, 0, 'f'},
        {"last-done", no_argument, 0, 1},
+       {"name-only", no_argument, 0, 2},
        {0,0,0,0}
 };
 
@@ -23,9 +23,10 @@ int ls_workout(int argc, char **argv) {
         * x: can have attribute
         */
 
-       int c, j, last_done_flag = 0;
+       int c;
        char *filter_p = NULL;
        char *search_term = NULL;
+       enum ls_format format = LS_FORMAT_DEFAULT;
 
        // reset optind; see `man getopt.3` NOTES section for justification
        optind = 0;
@@ -46,11 +47,22 @@ int ls_workout(int argc, char **argv) {
                                return EXIT_FAILURE;
                                
                                break;
-                       case 1:
-                               last_done_flag = 1;
+                       case 1: // --last-done
+                               format = LS_FORMAT_LAST_DONE;
+                               break;
+                       case 2: // --name-only
+                               format = LS_FORMAT_NAME_ONLY;
                                break;
                        case 'f':
                                filter_p = optarg;
+                               {
+                                       int count;
+                                       if((count = attribute_count())<0) { return EXIT_FAILURE; }
+                                       if(strlen(filter_p)!=count) {
+                                               log_err(LS_MESSAGE_INVALID_FILTER,strlen(filter_p),count);
+                                               return EXIT_FAILURE;
+                                       }
+                               }
                                break;
                        case '?':
                        default:
@@ -63,16 +75,29 @@ int ls_workout(int argc, char **argv) {
                search_term = argv[optind];
        }
 
-       if((global_opts.rows==1)&&(!last_done_flag)) {
-               return ls_single(search_term,filter_p);
+       switch(format) {
+               case LS_FORMAT_LAST_DONE:
+                       return last_done(search_term, filter_p);
+               case LS_FORMAT_NAME_ONLY:
+                       return ls_name_only(search_term,filter_p);
+               case LS_FORMAT_WITH_ATTRIBUTES:
+                       if(global_opts.rows==1) {
+                               return ls_single(search_term,filter_p);
+                       } else {
+                               return ls_with_attributes(search_term,filter_p);
+                       }
+                       break;
+               default:
+                       log_err(LS_MESSAGE_INVALID_FORMAT);
+                       return EXIT_FAILURE;
        }
+}
 
+static int ls_with_attributes(const char *search_term, const char *filter_p) {
        printf(WORKOUT_PRINT_ATTRIBUTE_HEADER,WORKOUT_PRINT_ATTRIBUTE_LABEL);
-       if(attribute_get(&print_header)<0) { return -1; }
+       if(attribute_get(&print_header)<0) { return EXIT_FAILURE; }
        printf(WORKOUT_PRINT_ATTRIBUTE_END);
 
-       if(last_done_flag) { return last_done(search_term, filter_p); }
-
        if(workout_get(
                search_term, /* term */
                filter_p, /* filter */
@@ -88,12 +113,12 @@ int ls_workout(int argc, char **argv) {
 #define WORKOUT_PRINT_LAST_DONE "[Last done: %s]\n"
 #define WORKOUT_PRINT_LAST_NULL "[Last done: N/A]\n"
 
-void print_workout(const unsigned char *name, int attr_flags, const unsigned char *last) {
+static void print_workout(const unsigned char *name, int attr_flags, const unsigned char *last) {
        int i;
        
        printf(WORKOUT_PRINT_NAME,name);
        for(i=0;i<helper.attr_count;i++) {
-               printf(WORKOUT_PRINT_ATTRIBUTE,(attr_flags&1>0)?"yes":"no");
+               printf(WORKOUT_PRINT_ATTRIBUTE,((attr_flags&1)>0)?"yes":"no");
                attr_flags >>= 1;
        }
        printf((NULL==last)?WORKOUT_PRINT_LAST_NULL:WORKOUT_PRINT_LAST_DONE,last);
index da7dd7585c290c19c09fc5f7dbc88b4ce97161af..b65d0b3785f56883dfacaf1129dfea3aee85edbb 100644 (file)
@@ -1,44 +1,10 @@
 #include<opt.h>
 
-#define DB_FILENAME "workouts.db"
-#define DB_FILENAME_LENGTH 11
-
-int opt_set_homedir(char *to_set) {
-       char *homedir, *db_location;
-       int len = strlen(to_set);
-       if(len<1) { return -1; }
-
-       homedir = malloc(sizeof(char)*(len+1));
-       if(NULL==homedir) { return -1; }
-
-       db_location = malloc(sizeof(char)*(len+DB_FILENAME_LENGTH+2));
-       if(NULL==db_location) { return -1; }
-
-       strcpy(homedir,to_set);
-       strcpy(db_location,homedir);
-       
-       for(int i=0;;i++) {
-               if(db_location[i]=='\0') {
-                       if(db_location[i-1]!='/') {
-                               strcat(db_location,"/");
-                       }
-                       break;
-               }
-       }
-
-       strcat(db_location,DB_FILENAME);
-
-       DIR *dir = opendir(homedir);
-       if(NULL==dir) {
+int opt_set_homedir(const char *to_set) {
+       if(chdir(to_set)!=0) {
+               perror("chdir");
                return -1;
        }
-       closedir(dir);
-       
-       if(global_opts.homedir!=NULL) { free(global_opts.homedir); }
-       if(global_opts.db_location!=NULL) { free(global_opts.db_location); }
 
-       global_opts.homedir = homedir;
-       global_opts.db_location = db_location;
-       
        return 1;
 }
index 18b67b52e5f1a444ee901f7afebced1e122fb853..0e50c72a8b97ddc8a0e12de21cd528ba1dc0ba23 100644 (file)
@@ -2,4 +2,4 @@
 
 void opt_set_rows(int to_set) {
        global_opts.rows = to_set;
-}
\ No newline at end of file
+}
index 0f9573ef8d6f1406df2e05805ddc19730c180e13..3afe0767e269ad091f60cf61bdf5b23272e79f54 100644 (file)
--- a/src/rm.c
+++ b/src/rm.c
@@ -9,6 +9,8 @@ int rm(int argc, char **argv) {
                        return rm_recent(argc,argv);
                case WORKOUT_DATA_TYPE_WORKOUT:
                        return rm_workout(argc,argv);
+               default:
+                       return EXIT_FAILURE;
        }
 }
 
@@ -58,4 +60,4 @@ int rm_workout(int argc, char **argv) {
        log_msg(RM_MESSAGE_WORKOUT_DELETED,argv[1]);
 
        return EXIT_SUCCESS;
-}
\ No newline at end of file
+}
index d8fe0dfc0a0e144b3a5b63acbb23cd1a5ca7b54f..922f43d664478cdb78dd1550a845be76b5b6dada 100644 (file)
@@ -9,6 +9,8 @@ int update(int argc, char **argv) {
                        return update_recent(argc,argv);
                case WORKOUT_DATA_TYPE_WORKOUT:
                        return update_workout(argc,argv);
+               default:
+                       return EXIT_FAILURE;
        }
 }
 
index d757b9a0e1072534f17d06307f80e3f9205513ba..52d463ed3ea92106c3da1a79f4f9c9848c2b7505 100644 (file)
@@ -3,7 +3,7 @@
 void usage() {
        log_err("Usage:\n");
        log_err("\tworkouts [options] add [!name] [date] [attribute filter]\n");
-       log_err("\tworkouts [options] ls [--filter {attribute filter}] [--last-done] [search term]\n");
+       log_err("\tworkouts [options] ls [--filter {attribute filter}] [--last-done] [--name-only] [search term]\n");
        log_err("\tworkouts [options] rm [!name] [date]\n");
        log_err("\tworkouts [options] toggle [!workout name] [!attr]\n");
        log_err("\tworkouts [options] update [workout name] [!from] [!to]\n");
index 3057817626cf17b5aaaee6dcfc6a385776018c62..2ad0b88b7498794b835304d3d39d3e6d168b136a 100644 (file)
@@ -1,8 +1,987 @@
 {
   "name": "workouts-integration-tests",
   "version": "0.0.0",
-  "lockfileVersion": 1,
+  "lockfileVersion": 2,
   "requires": true,
+  "packages": {
+    "": {
+      "name": "workouts-integration-tests",
+      "version": "0.0.0",
+      "license": "UNLICENSED",
+      "devDependencies": {
+        "mocha": "^8.4.0"
+      }
+    },
+    "node_modules/@ungap/promise-all-settled": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz",
+      "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==",
+      "dev": true
+    },
+    "node_modules/ansi-colors": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+      "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/ansi-regex": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+      "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/anymatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
+      "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+      "dev": true,
+      "dependencies": {
+        "normalize-path": "3.0.0",
+        "picomatch": "2.3.0"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+      "dev": true
+    },
+    "node_modules/balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "dev": true
+    },
+    "node_modules/binary-extensions": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+      "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "dependencies": {
+        "balanced-match": "1.0.2",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/braces": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+      "dev": true,
+      "dependencies": {
+        "fill-range": "7.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/browser-stdout": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
+      "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
+      "dev": true
+    },
+    "node_modules/camelcase": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz",
+      "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "4.3.0",
+        "supports-color": "7.2.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/chalk/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/chokidar": {
+      "version": "3.5.1",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz",
+      "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==",
+      "dev": true,
+      "dependencies": {
+        "anymatch": "3.1.2",
+        "braces": "3.0.2",
+        "fsevents": "2.3.2",
+        "glob-parent": "5.1.2",
+        "is-binary-path": "2.1.0",
+        "is-glob": "4.0.1",
+        "normalize-path": "3.0.0",
+        "readdirp": "3.5.0"
+      },
+      "engines": {
+        "node": ">= 8.10.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "2.3.2"
+      }
+    },
+    "node_modules/cliui": {
+      "version": "7.0.4",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+      "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+      "dev": true,
+      "dependencies": {
+        "string-width": "4.2.2",
+        "strip-ansi": "6.0.0",
+        "wrap-ansi": "7.0.0"
+      }
+    },
+    "node_modules/cliui/node_modules/ansi-regex": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+      "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/cliui/node_modules/is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/cliui/node_modules/string-width": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
+      "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
+      "dev": true,
+      "dependencies": {
+        "emoji-regex": "8.0.0",
+        "is-fullwidth-code-point": "3.0.0",
+        "strip-ansi": "6.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/cliui/node_modules/strip-ansi": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+      "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+      "dev": true,
+      "dependencies": {
+        "ansi-regex": "5.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "node_modules/concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+      "dev": true
+    },
+    "node_modules/debug": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
+      "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
+      "dev": true,
+      "dependencies": {
+        "ms": "2.1.2"
+      },
+      "engines": {
+        "node": ">=6.0"
+      }
+    },
+    "node_modules/debug/node_modules/ms": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+      "dev": true
+    },
+    "node_modules/decamelize": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
+      "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/diff": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
+      "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.3.1"
+      }
+    },
+    "node_modules/emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "dev": true
+    },
+    "node_modules/escalade": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+      "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/escape-string-regexp": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/fill-range": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+      "dev": true,
+      "dependencies": {
+        "to-regex-range": "5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/find-up": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+      "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+      "dev": true,
+      "dependencies": {
+        "locate-path": "6.0.0",
+        "path-exists": "4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/flat": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
+      "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
+      "dev": true,
+      "bin": {
+        "flat": "cli.js"
+      }
+    },
+    "node_modules/fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+      "dev": true
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/get-caller-file": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+      "dev": true,
+      "engines": {
+        "node": "6.* || 8.* || >= 10.*"
+      }
+    },
+    "node_modules/glob": {
+      "version": "7.1.6",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+      "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+      "dev": true,
+      "dependencies": {
+        "fs.realpath": "1.0.0",
+        "inflight": "1.0.6",
+        "inherits": "2.0.4",
+        "minimatch": "3.0.4",
+        "once": "1.4.0",
+        "path-is-absolute": "1.0.1"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+      "dev": true,
+      "dependencies": {
+        "is-glob": "4.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/growl": {
+      "version": "1.10.5",
+      "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
+      "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
+      "dev": true,
+      "engines": {
+        "node": ">=4.x"
+      }
+    },
+    "node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/he": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+      "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+      "dev": true,
+      "bin": {
+        "he": "bin/he"
+      }
+    },
+    "node_modules/inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+      "dev": true,
+      "dependencies": {
+        "once": "1.4.0",
+        "wrappy": "1.0.2"
+      }
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "dev": true
+    },
+    "node_modules/is-binary-path": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+      "dev": true,
+      "dependencies": {
+        "binary-extensions": "2.2.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-fullwidth-code-point": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+      "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/is-glob": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+      "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+      "dev": true,
+      "dependencies": {
+        "is-extglob": "2.1.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.12.0"
+      }
+    },
+    "node_modules/is-plain-obj": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
+      "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+      "dev": true
+    },
+    "node_modules/js-yaml": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz",
+      "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==",
+      "dev": true,
+      "dependencies": {
+        "argparse": "2.0.1"
+      },
+      "bin": {
+        "js-yaml": "bin/js-yaml.js"
+      }
+    },
+    "node_modules/locate-path": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+      "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+      "dev": true,
+      "dependencies": {
+        "p-locate": "5.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/log-symbols": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz",
+      "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==",
+      "dev": true,
+      "dependencies": {
+        "chalk": "4.1.2"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/minimatch": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+      "dev": true,
+      "dependencies": {
+        "brace-expansion": "1.1.11"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/mocha": {
+      "version": "8.4.0",
+      "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz",
+      "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==",
+      "dev": true,
+      "dependencies": {
+        "@ungap/promise-all-settled": "1.1.2",
+        "ansi-colors": "4.1.1",
+        "browser-stdout": "1.3.1",
+        "chokidar": "3.5.1",
+        "debug": "4.3.1",
+        "diff": "5.0.0",
+        "escape-string-regexp": "4.0.0",
+        "find-up": "5.0.0",
+        "glob": "7.1.6",
+        "growl": "1.10.5",
+        "he": "1.2.0",
+        "js-yaml": "4.0.0",
+        "log-symbols": "4.0.0",
+        "minimatch": "3.0.4",
+        "ms": "2.1.3",
+        "nanoid": "3.1.20",
+        "serialize-javascript": "5.0.1",
+        "strip-json-comments": "3.1.1",
+        "supports-color": "8.1.1",
+        "which": "2.0.2",
+        "wide-align": "1.1.3",
+        "workerpool": "6.1.0",
+        "yargs": "16.2.0",
+        "yargs-parser": "20.2.4",
+        "yargs-unparser": "2.0.0"
+      },
+      "bin": {
+        "_mocha": "bin/_mocha",
+        "mocha": "bin/mocha"
+      },
+      "engines": {
+        "node": ">= 10.12.0"
+      }
+    },
+    "node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "dev": true
+    },
+    "node_modules/nanoid": {
+      "version": "3.1.20",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz",
+      "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==",
+      "dev": true,
+      "bin": {
+        "nanoid": "bin/nanoid.cjs"
+      },
+      "engines": {
+        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+      }
+    },
+    "node_modules/normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+      "dev": true,
+      "dependencies": {
+        "wrappy": "1.0.2"
+      }
+    },
+    "node_modules/p-limit": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+      "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+      "dev": true,
+      "dependencies": {
+        "yocto-queue": "0.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/p-locate": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+      "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+      "dev": true,
+      "dependencies": {
+        "p-limit": "3.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/path-exists": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+      "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/picomatch": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
+      "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==",
+      "dev": true,
+      "engines": {
+        "node": ">=8.6"
+      }
+    },
+    "node_modules/randombytes": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+      "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+      "dev": true,
+      "dependencies": {
+        "safe-buffer": "5.2.1"
+      }
+    },
+    "node_modules/readdirp": {
+      "version": "3.5.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz",
+      "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==",
+      "dev": true,
+      "dependencies": {
+        "picomatch": "2.3.0"
+      },
+      "engines": {
+        "node": ">=8.10.0"
+      }
+    },
+    "node_modules/require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "dev": true
+    },
+    "node_modules/serialize-javascript": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz",
+      "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==",
+      "dev": true,
+      "dependencies": {
+        "randombytes": "2.1.0"
+      }
+    },
+    "node_modules/string-width": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+      "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+      "dev": true,
+      "dependencies": {
+        "is-fullwidth-code-point": "2.0.0",
+        "strip-ansi": "4.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/strip-ansi": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+      "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+      "dev": true,
+      "dependencies": {
+        "ansi-regex": "3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/strip-json-comments": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/supports-color": {
+      "version": "8.1.1",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+      "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+      "dev": true,
+      "dependencies": {
+        "is-number": "7.0.0"
+      },
+      "engines": {
+        "node": ">=8.0"
+      }
+    },
+    "node_modules/which": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+      "dev": true,
+      "dependencies": {
+        "isexe": "2.0.0"
+      },
+      "bin": {
+        "node-which": "bin/node-which"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/wide-align": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
+      "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
+      "dev": true,
+      "dependencies": {
+        "string-width": "2.1.1"
+      }
+    },
+    "node_modules/workerpool": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz",
+      "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==",
+      "dev": true
+    },
+    "node_modules/wrap-ansi": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "4.3.0",
+        "string-width": "4.2.2",
+        "strip-ansi": "6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/wrap-ansi/node_modules/ansi-regex": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+      "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/wrap-ansi/node_modules/string-width": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
+      "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
+      "dev": true,
+      "dependencies": {
+        "emoji-regex": "8.0.0",
+        "is-fullwidth-code-point": "3.0.0",
+        "strip-ansi": "6.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/wrap-ansi/node_modules/strip-ansi": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+      "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+      "dev": true,
+      "dependencies": {
+        "ansi-regex": "5.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+      "dev": true
+    },
+    "node_modules/y18n": {
+      "version": "5.0.8",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+      "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/yargs": {
+      "version": "16.2.0",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+      "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+      "dev": true,
+      "dependencies": {
+        "cliui": "7.0.4",
+        "escalade": "3.1.1",
+        "get-caller-file": "2.0.5",
+        "require-directory": "2.1.1",
+        "string-width": "4.2.2",
+        "y18n": "5.0.8",
+        "yargs-parser": "20.2.4"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/yargs-parser": {
+      "version": "20.2.4",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz",
+      "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/yargs-unparser": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz",
+      "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==",
+      "dev": true,
+      "dependencies": {
+        "camelcase": "6.2.0",
+        "decamelize": "4.0.0",
+        "flat": "5.0.2",
+        "is-plain-obj": "2.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/yargs/node_modules/ansi-regex": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+      "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/yargs/node_modules/is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/yargs/node_modules/string-width": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
+      "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
+      "dev": true,
+      "dependencies": {
+        "emoji-regex": "8.0.0",
+        "is-fullwidth-code-point": "3.0.0",
+        "strip-ansi": "6.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/yargs/node_modules/strip-ansi": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+      "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+      "dev": true,
+      "dependencies": {
+        "ansi-regex": "5.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/yocto-queue": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+      "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      }
+    }
+  },
   "dependencies": {
     "@ungap/promise-all-settled": {
       "version": "1.1.2",
index a11baac63f564a782c8902117f4d9155ba178736..9ba8c0c77682b032b1d2e325b24fab47663063f2 100644 (file)
@@ -1,5 +1,6 @@
 const assert = require('assert');
 const {cwd} = require('process');
+const path = require('path');
 const util = require('util');
 
 const exec = util.promisify(require('child_process').exec);
@@ -19,7 +20,7 @@ describe('basic tests', () => {
        it('should print usage options when run with --help option', async() => {
                let usage = `Usage:\n`;
                usage += `\tworkouts [options] add [!name] [date] [attribute filter]\n`;
-               usage += `\tworkouts [options] ls [--filter {attribute filter}] [--last-done] [search term]\n`;
+               usage += `\tworkouts [options] ls [--filter {attribute filter}] [--last-done] [--name-only] [search term]\n`;
                usage += `\tworkouts [options] rm [!name] [date]\n`;
                usage += `\tworkouts [options] toggle [!workout name] [!attr]\n`;
                usage += `\tworkouts [options] update [workout name] [!from] [!to]\n`;
@@ -52,8 +53,17 @@ describe('basic tests', () => {
        });
 
        it(`should throw when given option --homedir which doesn't exist`, async() => {
-               await assert.rejects(async() => await exec(`${workouts} --homedir=${pwd}/doesnt_exist`));
-               await assert.rejects(async() => await exec(`${workouts} -d ${pwd}/doesnt_exist`));
+               const baddir = path.join(cwd(),'doesnt_exist');
+               await assert.rejects(async() => await exec(`${workouts} --homedir=${baddir}`),(err) => {
+                       assert.strictEqual(err.code,1);
+                       assert.strictEqual(err.stderr,`chdir: No such file or directory\n`);
+                       return true;
+               });
+               await assert.rejects(async() => await exec(`${workouts} -d ${baddir}`),(err) => {
+                       assert.strictEqual(err.code,1);
+                       assert.strictEqual(err.stderr,`chdir: No such file or directory\n`);
+                       return true;
+               });
        });
 
        after(async() => {
index 6b2a548291a2713464d8587124b059f9e86d3f39..788edb4dbcbaa9003982c92f48fc5d81eabae7b4 100644 (file)
@@ -11,11 +11,24 @@ describe('ls integration tests', () => {
 
        const workouts = `../../workouts -d ${cwd()}`;
 
+       // helper methods for setting dates
+       const now = new Date();
+       const daysAgo = (offset) => {
+               const date = new Date();
+               date.setDate(now.getDate() - offset);
+               const y = date.getFullYear();
+               const m = (date.getMonth()+1).toString().padStart(2,'0');
+               const d = (date.getDate()).toString().padStart(2,'0');
+               return `${y}-${m}-${d}`;
+       };
+
        const recent = [
-               {name:"workout1",date:"2020-07-10"},
-               {name:"apple",date:"2020-07-09"},
-               {name:"test",date:"2020-07-09"},
-               {name:"workout2",date:"2020-07-09"}
+               {name:"workout1",date:daysAgo(0)},
+               {name:"apple",date:daysAgo(1)},
+               {name:"test",date:daysAgo(1)},
+               {name:"workout2",date:daysAgo(2)},
+               {name:"workout4",date:daysAgo(10)},
+               {name:"workout3",date:daysAgo(20)}
        ];
 
        before(async() => {
@@ -32,11 +45,17 @@ describe('ls integration tests', () => {
        beforeEach(async() => {
                await assert.doesNotReject(async() => await exec(`${workouts} --attr add lower`));
                await assert.doesNotReject(async() => await exec(`${workouts} --attr add upper`));
-
-               await assert.doesNotReject(async() => await exec(`${workouts} --workout add workout1 01`));
-               await assert.doesNotReject(async() => await exec(`${workouts} --workout add workout2 10`));
-               await assert.doesNotReject(async() => await exec(`${workouts} --workout add test 00`));
-               await assert.doesNotReject(async() => await exec(`${workouts} --workout add apple 11`));
+               await assert.doesNotReject(async() => await exec(`${workouts} --attr add cardio`));
+               await assert.doesNotReject(async() => await exec(`${workouts} --attr add back`));
+               await assert.doesNotReject(async() => await exec(`${workouts} --attr add core`));
+               await assert.doesNotReject(async() => await exec(`${workouts} --attr add other`));
+
+               await assert.doesNotReject(async() => await exec(`${workouts} --workout add workout1 010000`));
+               await assert.doesNotReject(async() => await exec(`${workouts} --workout add workout2 100000`));
+               await assert.doesNotReject(async() => await exec(`${workouts} --workout add test 001000`));
+               await assert.doesNotReject(async() => await exec(`${workouts} --workout add apple 110000`));
+               await assert.doesNotReject(async() => await exec(`${workouts} --workout add workout3 110011`));
+               await assert.doesNotReject(async() => await exec(`${workouts} --workout add workout4 001100`));
 
                for(const i of recent) {
                        await assert.doesNotReject(async() => await exec(`${workouts} --recent add ${i.name} ${i.date}`));
@@ -49,7 +68,7 @@ describe('ls integration tests', () => {
 
        describe('ls_attribute', () => {
                it('should successfully list all attributes in order they were added', async() => {
-                       const attributes = ["lower","upper"];
+                       const attributes = ["lower","upper", "cardio", "back", "core", "other"];
 
                        await assert.doesNotReject(async() => {
                                const {stdout,stderr} = await exec(`${workouts} --attr ls`);
@@ -81,7 +100,7 @@ describe('ls integration tests', () => {
                it('should success filter recent workouts', async() => {
                        const {stdout,stderr} = await exec(`${workouts} --recent ls test`);
                        
-                       const expected = `test`.padEnd(30,' ') + `\t2020-07-09\n`;
+                       const expected = `test`.padEnd(30,' ') + `\t${daysAgo(1)}\n`;
                        assert.strictEqual(stdout,expected);
                });
        });
@@ -93,15 +112,19 @@ describe('ls integration tests', () => {
                                const {stdout,stderr} = await exec(`${workouts}`);
 
                                let expected = `Attributes:`.padEnd(30,' ');
-                               expected += `\tlower\tupper\t\n`;
+                               expected += `\tlower\tupper\tcardio\tback\tcore\tother\t\n`;
                                expected += `workout1`.padEnd(30,' ');
-                               expected += `\tno\tyes\t[Last done: 2020-07-10]\n`;
+                               expected += `\tno\tyes\tno\tno\tno\tno\t[Last done: ${daysAgo(0)}]\n`;
                                expected += `apple`.padEnd(30,' ');
-                               expected += `\tyes\tyes\t[Last done: 2020-07-09]\n`;
+                               expected += `\tyes\tyes\tno\tno\tno\tno\t[Last done: ${daysAgo(1)}]\n`;
                                expected += `test`.padEnd(30,' ');
-                               expected += `\tno\tno\t[Last done: 2020-07-09]\n`;
+                               expected += `\tno\tno\tyes\tno\tno\tno\t[Last done: ${daysAgo(1)}]\n`;
                                expected += `workout2`.padEnd(30,' ');
-                               expected += `\tyes\tno\t[Last done: 2020-07-09]\n`;
+                               expected += `\tyes\tno\tno\tno\tno\tno\t[Last done: ${daysAgo(2)}]\n`;
+                               expected += `workout4`.padEnd(30,' ');
+                               expected += `\tno\tno\tyes\tyes\tno\tno\t[Last done: ${daysAgo(10)}]\n`;
+                               expected += `workout3`.padEnd(30,' ');
+                               expected += `\tyes\tyes\tno\tno\tyes\tyes\t[Last done: ${daysAgo(20)}]\n`;
 
                                assert.strictEqual(stdout,expected);
                        });
@@ -112,9 +135,9 @@ describe('ls integration tests', () => {
                                const {stdout,stderr} = await exec(`${workouts} --rows=1 ls`);
 
                                let expected = `Attributes:`;
-                               expected += `\tlower\tupper\t\n`;
+                               expected += `\tlower\tupper\tcardio\tback\tcore\tother\t\n`;
                                expected +=    `workout1   `;
-                               expected += `\tno\tyes\t[Last done: 2020-07-10]\n`;
+                               expected += `\tno\tyes\tno\tno\tno\tno\t[Last done: ${daysAgo(0)}]\n`;
                                assert.strictEqual(stdout,expected);
 
                                const res = await exec(`${workouts} --workout update workout2 workoutworkoutworkoutworkoutworkout`);
@@ -123,9 +146,9 @@ describe('ls integration tests', () => {
                                const res2 = await exec(`${workouts} --rows=1 ls workoutworkout`);
                                
                                expected =  `Attributes:                        `;
-                               expected += `\tlower\tupper\t\n`;
+                               expected += `\tlower\tupper\tcardio\tback\tcore\tother\t\n`;
                                expected += `workoutworkoutworkoutworkoutworkout`;
-                               expected += `\tyes\tno\t[Last done: 2020-07-09]\n`;
+                               expected += `\tyes\tno\tno\tno\tno\tno\t[Last done: ${daysAgo(2)}]\n`;
                                assert.strictEqual(res2.stdout,expected);
                        });
                });
@@ -135,11 +158,15 @@ describe('ls integration tests', () => {
                                const {stdout,stderr} = await exec(`${workouts} ls workout`);
 
                                let expected = `Attributes:`.padEnd(30,' ');
-                               expected += `\tlower\tupper\t\n`;
+                               expected += `\tlower\tupper\tcardio\tback\tcore\tother\t\n`;
                                expected += `workout1`.padEnd(30,' ');
-                               expected += `\tno\tyes\t[Last done: 2020-07-10]\n`;
+                               expected += `\tno\tyes\tno\tno\tno\tno\t[Last done: ${daysAgo(0)}]\n`;
                                expected += `workout2`.padEnd(30,' ');
-                               expected += `\tyes\tno\t[Last done: 2020-07-09]\n`;
+                               expected += `\tyes\tno\tno\tno\tno\tno\t[Last done: ${daysAgo(2)}]\n`;
+                               expected += `workout4`.padEnd(30,' ');
+                               expected += `\tno\tno\tyes\tyes\tno\tno\t[Last done: ${daysAgo(10)}]\n`;
+                               expected += `workout3`.padEnd(30,' ');
+                               expected += `\tyes\tyes\tno\tno\tyes\tyes\t[Last done: ${daysAgo(20)}]\n`;
 
                                assert.strictEqual(stdout,expected);
                        });
@@ -147,53 +174,133 @@ describe('ls integration tests', () => {
 
                it('should reject when `ls` term is not given and attempting to filter', async() => {
                        await assert.rejects(async() => {
-                               await exec(`${workouts} --filter 01`);
+                               await exec(`${workouts} --filter 010000`);
                        });
                });
+
+               it('should throw when given a filter with the wrong amount of attributes', async() => {
+                       let num = 1;
+                       const err = (err) => {
+                               assert.strictEqual(err.code,1);
+                               if(num===6) { num++; }
+                               assert.strictEqual(err.stderr,`invalid filter; wrong number of attributes given (${num++} != 6)\n`);
+                               return true;
+                       };
+
+                       await assert.rejects(async() => {
+                               await exec(`${workouts} ls --filter 0`);
+                       },err);
+                       await assert.rejects(async() => {
+                               await exec(`${workouts} ls --filter 01`);
+                       },err);
+
+                       await assert.rejects(async() => {
+                               await exec(`${workouts} ls --filter 010`);
+                       },err);
+                       await assert.rejects(async() => {
+                               await exec(`${workouts} ls --filter 0100`);
+                       },err);
+                       await assert.rejects(async() => {
+                               await exec(`${workouts} ls --filter 01000`);
+                       },err);
+                       await assert.rejects(async() => {
+                               await exec(`${workouts} ls --filter 0100000`);
+                       },err);
+               });
        
                it('should successfully filter workouts by attribute', async() => {
                        await assert.doesNotReject(async() => {
-                               const {stdout,stderr} = await exec(`${workouts} ls --filter 10`);
+                               const {stdout,stderr} = await exec(`${workouts} ls --filter 100000`);
 
                                let expected = `Attributes:`.padEnd(30,' ');
-                               expected += `\tlower\tupper\t\n`;
+                               expected += `\tlower\tupper\tcardio\tback\tcore\tother\t\n`;
                                expected += `workout2`.padEnd(30,' ');
-                               expected += `\tyes\tno\t[Last done: 2020-07-09]\n`;
+                               expected += `\tyes\tno\tno\tno\tno\tno\t[Last done: ${daysAgo(2)}]\n`;
 
                                assert.strictEqual(stdout,expected);
                        });
 
                        await assert.doesNotReject(async() => {
-                               const {stdout,stderr} = await exec(`${workouts} ls --filter 01`);
+                               const {stdout,stderr} = await exec(`${workouts} ls --filter 010000`);
 
                                let expected = `Attributes:`.padEnd(30,' ');
-                               expected += `\tlower\tupper\t\n`;
+                               expected += `\tlower\tupper\tcardio\tback\tcore\tother\t\n`;
                                expected += `workout1`.padEnd(30,' ');
-                               expected += `\tno\tyes\t[Last done: 2020-07-10]\n`;
+                               expected += `\tno\tyes\tno\tno\tno\tno\t[Last done: ${daysAgo(0)}]\n`;
 
                                assert.strictEqual(stdout,expected);
                        });
 
                        await assert.doesNotReject(async() => {
-                               const {stdout,stderr} = await exec(`${workouts} ls --filter 1x`);
+                               const {stdout,stderr} = await exec(`${workouts} ls --filter 1xxxxx`);
 
                                let expected = `Attributes:`.padEnd(30,' ');
-                               expected += `\tlower\tupper\t\n`;
+                               expected += `\tlower\tupper\tcardio\tback\tcore\tother\t\n`;
                                expected += `apple`.padEnd(30,' ');
-                               expected += `\tyes\tyes\t[Last done: 2020-07-09]\n`;
+                               expected += `\tyes\tyes\tno\tno\tno\tno\t[Last done: ${daysAgo(1)}]\n`;
                                expected += `workout2`.padEnd(30,' ');
-                               expected += `\tyes\tno\t[Last done: 2020-07-09]\n`;
+                               expected += `\tyes\tno\tno\tno\tno\tno\t[Last done: ${daysAgo(2)}]\n`;
+                               expected += `workout3`.padEnd(30,' ');
+                               expected += `\tyes\tyes\tno\tno\tyes\tyes\t[Last done: ${daysAgo(20)}]\n`;
 
                                assert.strictEqual(stdout,expected);
                        });
 
                        await assert.doesNotReject(async() => {
-                               const {stdout,stderr} = await exec(`${workouts} ls --filter 00`);
+                               const {stdout,stderr} = await exec(`${workouts} ls --filter 00xxxx`);
 
                                let expected = `Attributes:`.padEnd(30,' ');
-                               expected += `\tlower\tupper\t\n`;
+                               expected += `\tlower\tupper\tcardio\tback\tcore\tother\t\n`;
                                expected += `test`.padEnd(30,' ');
-                               expected += `\tno\tno\t[Last done: 2020-07-09]\n`;
+                               expected += `\tno\tno\tyes\tno\tno\tno\t[Last done: ${daysAgo(1)}]\n`;
+                               expected += `workout4`.padEnd(30,' ');
+                               expected += `\tno\tno\tyes\tyes\tno\tno\t[Last done: ${daysAgo(10)}]\n`;
+
+                               assert.strictEqual(stdout,expected);
+                       });
+               });
+
+               it('should successfully generate last done array when given --last-done option', async() => {
+                       await assert.doesNotReject(async() => {
+                               const {stdout,stderr} = await exec(`${workouts} ls --last-done`);
+                               let expected = `Attributes:`.padEnd(30,' ');
+                               expected += `\tlower\tupper\tcardio\tback\tcore\tother\t\n`;
+                               expected += `Last Done:`.padEnd(30,' ');
+                               expected += `\t1\t0\t1\t10\t20\t20\t\n`;
+                               expected += `Suggested filter: xxxx11\n`;
+
+                               assert.strictEqual(stdout,expected);
+                       });
+               });
+
+
+               it('should successfully print only workout names when given --name-only option', async() => {
+                       await assert.doesNotReject(async() => {
+                               const {stdout,stderr} = await exec(`${workouts} ls --name-only`);
+                               let expected = `workout1\n`;
+                               expected += `apple\n`;
+                               expected += `test\n`;
+                               expected += `workout2\n`;
+                               expected += `workout4\n`;
+                               expected += `workout3\n`;
+
+                               assert.strictEqual(stdout,expected);
+                       });
+
+                       await assert.doesNotReject(async() => {
+                               const {stdout,stderr} = await exec(`${workouts} ls --name-only workout`);
+                               let expected = `workout1\n`;
+                               expected += `workout2\n`;
+                               expected += `workout4\n`;
+                               expected += `workout3\n`;
+
+                               assert.strictEqual(stdout,expected);
+                       });
+
+                       await assert.doesNotReject(async() => {
+                               const {stdout,stderr} = await exec(`${workouts} ls --name-only -f 11x0xx`);
+                               let expected = `apple\n`;
+                               expected += `workout3\n`;
 
                                assert.strictEqual(stdout,expected);
                        });
index 1147729e3ddc5843a919865c5d213d78c5b0aa41..120d633d1f7547bbc44c9432a28644b2ea13dc16 100644 (file)
@@ -1,8 +1,6 @@
 AM_CPPFLAGS = \
-       -I$(top_builddir)/include/ \
-       -I$(top_builddir)/test/unit/ \
-       -I$(top_srcdir)/include/ \
-       -I$(top_srcdir)/test/unit/
+       -Wall \
+       -Werror
 
 EXTRA_DIST = \
        test_utils.h \
@@ -10,7 +8,6 @@ EXTRA_DIST = \
        data.attr.tests.h \
        data.recent.tests.h \
        data.workout.tests.h \
-       ls.tests.h \
        toggle.tests.h \
        update.tests.h
 
@@ -24,70 +21,72 @@ endif
 
 common_SOURCES = test_utils.c
 
-TEST_SRC_DIR = $(top_srcdir)/src
-
-common_SOURCES += $(TEST_SRC_DIR)/default.c $(TEST_SRC_DIR)/log.c
-common_SOURCES += $(TEST_SRC_DIR)/data/setup.c
-common_SOURCES += $(TEST_SRC_DIR)/opt/homedir.c $(TEST_SRC_DIR)/opt/loglvl.c $(TEST_SRC_DIR)/opt/rows.c $(TEST_SRC_DIR)/opt/target.c
+common_SOURCES += $(top_srcdir)/src/default.c $(top_srcdir)/src/log.c
+common_SOURCES += $(top_srcdir)/src/data/setup.c
+common_SOURCES += $(top_srcdir)/src/opt/homedir.c
+common_SOURCES += $(top_srcdir)/src/opt/loglvl.c
+common_SOURCES += $(top_srcdir)/src/opt/rows.c
+common_SOURCES += $(top_srcdir)/src/opt/target.c
 
 add_tests_SOURCES = \
        $(common_SOURCES) \
        add.tests.c \
-       $(TEST_SRC_DIR)/add.c \
-       $(TEST_SRC_DIR)/data/attr.c \
-       $(TEST_SRC_DIR)/data/recent.c \
-       $(TEST_SRC_DIR)/data/workout.c \
-       $(TEST_SRC_DIR)/date.c \
-       $(TEST_SRC_DIR)/usage.c
+       $(top_srcdir)/src/add.c \
+       $(top_srcdir)/src/data/attr.c \
+       $(top_srcdir)/src/data/recent.c \
+       $(top_srcdir)/src/data/workout.c \
+       $(top_srcdir)/src/date.c \
+       $(top_srcdir)/src/usage.c
 
 data_attr_tests_SOURCES = \
        $(common_SOURCES) \
        data.attr.tests.c \
-       $(TEST_SRC_DIR)/data/attr.c \
-       $(TEST_SRC_DIR)/data/workout.c
+       $(top_srcdir)/src/data/attr.c \
+       $(top_srcdir)/src/data/workout.c
 
 data_recent_tests_SOURCES = \
        $(common_SOURCES) \
        data.recent.tests.c \
-       $(TEST_SRC_DIR)/data/recent.c \
-       $(TEST_SRC_DIR)/data/workout.c \
-       $(TEST_SRC_DIR)/data/attr.c
+       $(top_srcdir)/src/data/recent.c \
+       $(top_srcdir)/src/data/workout.c \
+       $(top_srcdir)/src/data/attr.c
 
 data_workout_tests_SOURCES = \
        $(common_SOURCES) \
        data.workout.tests.c \
-       $(TEST_SRC_DIR)/data/workout.c \
-       $(TEST_SRC_DIR)/data/attr.c \
-       $(TEST_SRC_DIR)/data/recent.c
+       $(top_srcdir)/src/data/workout.c \
+       $(top_srcdir)/src/data/attr.c \
+       $(top_srcdir)/src/data/recent.c
 
 ls_tests_SOURCES = \
        $(common_SOURCES) \
        ls.tests.c \
-       $(TEST_SRC_DIR)/data/attr.c \
-       $(TEST_SRC_DIR)/data/recent.c \
-       $(TEST_SRC_DIR)/data/workout.c \
-       $(TEST_SRC_DIR)/ls/attr.c \
-       $(TEST_SRC_DIR)/ls/head.c \
-       $(TEST_SRC_DIR)/ls/last.c \
-       $(TEST_SRC_DIR)/ls/ls.c \
-       $(TEST_SRC_DIR)/ls/recent.c \
-       $(TEST_SRC_DIR)/ls/single.c \
-       $(TEST_SRC_DIR)/ls/workout.c \
-       $(TEST_SRC_DIR)/usage.c
+       $(top_srcdir)/src/data/attr.c \
+       $(top_srcdir)/src/data/recent.c \
+       $(top_srcdir)/src/data/workout.c \
+       $(top_srcdir)/src/ls/attr.c \
+       $(top_srcdir)/src/ls/head.c \
+       $(top_srcdir)/src/ls/last.c \
+       $(top_srcdir)/src/ls/ls.c \
+       $(top_srcdir)/src/ls/name.c \
+       $(top_srcdir)/src/ls/recent.c \
+       $(top_srcdir)/src/ls/single.c \
+       $(top_srcdir)/src/ls/workout.c \
+       $(top_srcdir)/src/usage.c
 
 toggle_tests_SOURCES = \
        $(common_SOURCES) \
        toggle.tests.c \
-       $(TEST_SRC_DIR)/data/attr.c \
-       $(TEST_SRC_DIR)/data/workout.c \
-       $(TEST_SRC_DIR)/toggle.c \
-       $(TEST_SRC_DIR)/usage.c
+       $(top_srcdir)/src/data/attr.c \
+       $(top_srcdir)/src/data/workout.c \
+       $(top_srcdir)/src/toggle.c \
+       $(top_srcdir)/src/usage.c
 
 update_tests_SOURCES = \
        $(common_SOURCES) \
        update.tests.c \
-       $(TEST_SRC_DIR)/data/attr.c \
-       $(TEST_SRC_DIR)/data/recent.c \
-       $(TEST_SRC_DIR)/data/workout.c \
-       $(TEST_SRC_DIR)/date.c \
-       $(TEST_SRC_DIR)/update.c
+       $(top_srcdir)/src/data/attr.c \
+       $(top_srcdir)/src/data/recent.c \
+       $(top_srcdir)/src/data/workout.c \
+       $(top_srcdir)/src/date.c \
+       $(top_srcdir)/src/update.c
index ed1cb59bf4e58487befa88981807795d75f88123..8f2987058a3ad0b61a3d00c566d6541d9d988c93 100644 (file)
@@ -7,7 +7,7 @@ int main() {
        add_recent_basic_test();
        add_workout_basic_test();
 
-       clean();
+       clean_env();
 
        return EXIT_SUCCESS;
 }
@@ -90,4 +90,4 @@ void add_workout_basic_test() {
        assert(EXIT_FAILURE==add(3,argv));
 
        reset_env();
-}
\ No newline at end of file
+}
index 9aabd0f3578476ae0dff17799c5aed69cac46648..0e2e7ae1379d00f1efd183b012b6d8005d5429db 100644 (file)
@@ -11,7 +11,7 @@ int main() {
        attribute_insert_test();
        attribute_parse_test();
 
-       clean();
+       clean_env();
 
        return EXIT_SUCCESS;
 }
index a33d5de4c0d9215b4cef81ee8579f160b5a1ec0d..ecbd1e40b5520cdcc501d90bb8b61303abe3e650 100644 (file)
@@ -7,7 +7,7 @@ int main() {
        recent_get_test();
        recent_insert_test();
 
-       clean();
+       clean_env();
 
        return EXIT_SUCCESS;
 }
index c2d307fcfaed0bb8f56da8b083ec143b38c552f8..5aff7ca239e228002af1391abc046788a4147f91 100644 (file)
@@ -10,7 +10,7 @@ int main() {
        workout_toggle_test();
        workout_update_test();
 
-       clean();
+       clean_env();
 
        return EXIT_SUCCESS;
 }
index 0e5ef4bc1ce0932b45191d64b09b17ca6b0723a7..e99ad67b8584bae84d7686d295341d4efedd6d92 100644 (file)
@@ -1,4 +1,13 @@
-#include<ls.tests.h>
+#include<test_utils.h>
+
+#include<ls.h>
+
+static void daysAgo(int,char*,int);
+static void ls_attribute_basic_test();
+static void ls_recent_basic_test();
+static void ls_workout_basic_test();
+static void ls_workout_last_done_test();
+static void ls_workout_name_only_test();
 
 int main() {
        setup_env();
@@ -6,17 +15,28 @@ int main() {
        ls_attribute_basic_test();
        ls_recent_basic_test();
        ls_workout_basic_test();
+       ls_workout_last_done_test();
+       ls_workout_name_only_test();
 
-       clean();
+       clean_env();
 
        return EXIT_SUCCESS;
 }
 
-void ls_attribute_basic_test() {
+#define DATE_BUF_LEN 11
+
+static void daysAgo(int days,char *buf, int buflen) {
+       time_t t = time(NULL);
+       struct tm now = *localtime(&t);
+       now.tm_mday -= days;
+       assert(strftime(buf,buflen,"&Y-%m-%d",&now)!=0);
+}
+
+static void ls_attribute_basic_test() {
        assert(EXIT_SUCCESS==ls_attribute());
 }
 
-void ls_recent_basic_test() {
+static void ls_recent_basic_test() {
        char *opts[] = {
                "ls",
                "test workout",
@@ -28,7 +48,7 @@ void ls_recent_basic_test() {
        assert(EXIT_SUCCESS==ls_recent(2,opts));
 }
 
-void ls_workout_basic_test() {
+static void ls_workout_basic_test() {
        global_opts.target = WORKOUT_DATA_TYPE_WORKOUT;
 
        char *bad_opt[] = {
@@ -86,3 +106,64 @@ void ls_workout_basic_test() {
 
        reset_env();
 }
+
+static void ls_workout_last_done_test() {
+       char timebuf[DATE_BUF_LEN];
+
+       char *args[] = {
+               "ls",
+               "--last-done",
+               "P90X",
+               NULL
+       };
+
+       assert(attribute_insert("test")==1);
+       assert(attribute_insert("test2")==1);
+       assert(attribute_insert("test3")==1);
+       assert(attribute_insert("test4")==1);
+       assert(attribute_insert("test5")==1);
+       assert(attribute_insert("test6")==1);
+
+       assert(workout_insert("P90X - workout 1",37)==1);
+       assert(workout_insert("Rushfit - workout 2",24)==1);
+       assert(workout_insert("Hard Core - workout 3",2)==1);
+
+       daysAgo(1,timebuf,DATE_BUF_LEN);
+       assert(recent_insert("P90X - workout 1",timebuf)==1);
+       daysAgo(2,timebuf,DATE_BUF_LEN);
+       assert(recent_insert("Rushfit - workout 2",timebuf)==1);
+       daysAgo(3,timebuf,DATE_BUF_LEN);
+       assert(recent_insert("Hard Core - workout 3",timebuf)==1);
+
+       assert(EXIT_SUCCESS==ls(1,args));
+       assert(EXIT_SUCCESS==ls(2,args));
+       assert(EXIT_SUCCESS==ls(3,args));
+
+       reset_env();
+}
+
+static void ls_workout_name_only_test() {
+       char *args[] = {
+               "ls",
+               "--name-only",
+               "P90X",
+               NULL
+       };
+
+       assert(attribute_insert("test")==1);
+       assert(attribute_insert("test2")==1);
+       assert(attribute_insert("test3")==1);
+       assert(attribute_insert("test4")==1);
+       assert(attribute_insert("test5")==1);
+       assert(attribute_insert("test6")==1);
+
+       assert(workout_insert("P90X - workout 1",37)==1);
+       assert(workout_insert("Rushfit - workout 2",24)==1);
+       assert(workout_insert("Hard Core - workout 3",2)==1);
+
+       assert(EXIT_SUCCESS==ls(1,args));
+       assert(EXIT_SUCCESS==ls(2,args));
+       assert(EXIT_SUCCESS==ls(3,args));
+
+       reset_env();
+}
diff --git a/test/unit/ls.tests.h b/test/unit/ls.tests.h
deleted file mode 100644 (file)
index 529c5b5..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef __LS_TESTS_H_
-#define __LS_TESTS_H_
-
-#include<test_utils.h>
-
-#include<ls.h>
-
-int main();
-void ls_attribute_basic_test();
-void ls_recent_basic_test();
-void ls_workout_basic_test();
-
-#endif
\ No newline at end of file
index 84ec43c9f3b116425fd37bf86e989390b7627a6f..8d2af2d1e592d182e4df0709f40460a4e9322a40 100644 (file)
@@ -1,12 +1,11 @@
 #include<test_utils.h>
 
-void clean() {
-       free(global_opts.db_location);
-       free(global_opts.homedir);
+void clean_env() {
+       assert((remove(DB_FILENAME)==0)||(errno==ENOENT));
 }
 
 void reset_env() {
-       assert((remove(global_opts.db_location)==0)||(errno==ENOENT));
+       clean_env();
        setup();
 }
 
index f1ef33d1c78be039b3cbcdb6151f8cd311d23e69..140316a9a07e487277d146e41a0081fe6bab5626 100644 (file)
@@ -11,7 +11,7 @@
 #include<data.h>
 #include<default.h>
 
-void clean();
+void clean_env();
 void setup_env();
 void reset_env();
 
index 309b515757bd9412b16d7a8cc35611ccc7f27f1f..380dea7c59ecdf0d20f5e0ec6b9f78fb7336ec39 100644 (file)
@@ -5,7 +5,7 @@ int main() {
 
        toggle_basic_test();
 
-       clean();
+       clean_env();
 
        return EXIT_SUCCESS;
 }
@@ -43,4 +43,4 @@ void toggle_basic_test() {
        assert(EXIT_SUCCESS==toggle(3,argv));
 
        reset_env();
-}
\ No newline at end of file
+}
index 9018acb2a23bc502e16b35f34b0266bbc82ec3c5..e9fc00fc2638263fb04782f8cc65c9523e04fb8a 100644 (file)
@@ -7,7 +7,7 @@ int main() {
        update_recent_basic_test();
        update_workout_basic_test();
 
-       clean();
+       clean_env();
 
        return EXIT_SUCCESS;
 }