From: alex Date: Sat, 20 Aug 2022 01:44:26 +0000 (-0700) Subject: Intial design complete X-Git-Tag: v1.0.0~1 X-Git-Url: http://git.infiniteadaptability.org/?a=commitdiff_plain;h=d8973f0599b48cc951b01715ccc58877e377ed42;p=events Intial design complete Full project completed. Added the following sub-commands: -add -dismiss -ls -rm -postpone -prune Added unit tests to verify functionality. --- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a7921fc --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +ev +events + +# build objects +*.o +*.log +*.trs +*.tests + +# autoconf/automake +.deps +.dirstamp +aclocal.m4 +autom4te.cache/ +autoscan.log +build-aux/ +config.h +config.h.in +config.h.in~ +config.log +config.status +configure +configure.scan +Makefile +Makefile.in +stamp-h1 diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..7b8e14c --- /dev/null +++ b/Makefile.am @@ -0,0 +1,59 @@ +AM_CPPFLAGS = \ + -Wall \ + -Werror + +if ENABLE_DEBUG +else +AM_CPPFLAGS += \ + -DNDEBUG +endif + +bin_PROGRAMS = ev + +ev_SOURCES = \ + src/add.c \ + src/args.c \ + src/copy.c \ + src/cut.c \ + src/defaults.c \ + src/event.c \ + src/file/line.c \ + src/file/mv.c \ + src/file/open.c \ + src/file/pipe.c \ + src/file/tmp.c \ + src/ls.c \ + src/main.c \ + src/opt/duration.c \ + src/opt/env.c \ + src/opt/file.c \ + src/opt/filter.c \ + src/opt/global.c \ + src/opt/recur.c \ + src/postpone.c \ + src/prune.c \ + src/rm.c \ + src/seek.c \ + src/usage.c + +ev_SOURCES += \ + inc/add.h \ + inc/args.h \ + inc/copy.h \ + inc/cut.h \ + inc/defaults.h \ + inc/event.h \ + inc/file.h \ + inc/ls.h \ + inc/main.h \ + inc/opt.h \ + inc/postpone.h \ + inc/prune.h \ + inc/rm.h \ + inc/seek.h \ + inc/usage.h + +SUBDIRS = . test/unit + +unit: + $(MAKE) -C test/unit check diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..1030934 --- /dev/null +++ b/configure.ac @@ -0,0 +1,65 @@ +AC_PREREQ([2.69]) +AC_INIT([ev], [0.0.0]) + +# Store build files not in main directory +AC_CONFIG_AUX_DIR([build-aux]) + +AM_INIT_AUTOMAKE([foreign subdir-objects -Wall -Werror]) + +AC_CONFIG_SRCDIR([src/main.c]) +AC_CONFIG_HEADERS([inc/config.h]) + +AC_ARG_ENABLE([debug], + [AS_HELP_STRING([--enable-debug], + [enable debugging])], + [enable_debug=$enableval], + [enable_debug=no]) + +AC_ARG_ENABLE([memcheck], + [AS_HELP_STRING([--disable-memcheck], + [disable memcheck with valgrind (enabled by default)])], + [enable_memcheck=$enableval], + [enable_memcheck=yes]) + +AC_PATH_PROG([VALGRIND], [valgrind]) +AM_CONDITIONAL([HAVE_VALGRIND], [test -n "$VALGRIND"]) + +AC_MSG_CHECKING([if debugging]) +if test x$enable_debug != xno; then + AC_MSG_RESULT(yes) + CFLAGS="-ggdb3 -O0" +else + AC_MSG_RESULT(no) +fi + +AM_CONDITIONAL([ENABLE_DEBUG],[test x$enable_debug != xno]) + +AC_MSG_CHECKING([if memcheck should be enabled]) +if test x$enable_memcheck != xno; then + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi + +AM_CONDITIONAL([ENABLE_MEMCHECK],[test x$enable_memcheck = xyes]) + +# Checks for programs. +AC_PROG_CC +AC_PROG_INSTALL + +# Checks for libraries. + +# Checks for header files. +AC_CHECK_HEADERS([stdlib.h string.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_TYPE_SIZE_T + +# Checks for library functions. +AC_FUNC_MALLOC +AC_CHECK_FUNCS([memset strdup strptime]) + +AC_CONFIG_FILES([Makefile + test/unit/Makefile]) + +AC_OUTPUT diff --git a/inc/add.h b/inc/add.h new file mode 100644 index 0000000..41148fd --- /dev/null +++ b/inc/add.h @@ -0,0 +1,19 @@ +#ifndef __ADD_H_ +#define __ADD_H_ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +int add(); +int add_to_file(struct event*); + +#endif diff --git a/inc/args.h b/inc/args.h new file mode 100644 index 0000000..e518298 --- /dev/null +++ b/inc/args.h @@ -0,0 +1,21 @@ +#ifndef __ARGS_H_ +#define __ARGS_H_ + +#include + +#include +#include + +enum sub_command { + SUB_COMMAND_INVALID_ARGS = -1, + SUB_COMMAND_ADD, + SUB_COMMAND_DISMISS, + SUB_COMMAND_LS, + SUB_COMMAND_POSTPONE, + SUB_COMMAND_PRUNE, + SUB_COMMAND_RM +}; + +int args(int,char**); + +#endif diff --git a/inc/copy.h b/inc/copy.h new file mode 100644 index 0000000..ef9a9df --- /dev/null +++ b/inc/copy.h @@ -0,0 +1,12 @@ +#ifndef __COPY_H_ +#define __COPY_H_ + +#include + +#include +#include + +int copy(FILE*,FILE*,time_t); +int copy_next_event(FILE*,FILE*); + +#endif diff --git a/inc/cut.h b/inc/cut.h new file mode 100644 index 0000000..371dac4 --- /dev/null +++ b/inc/cut.h @@ -0,0 +1,10 @@ +#ifndef __CUT_H_ +#define __CUT_H_ + +#include +#include +#include + +int cut(struct event*, time_t, size_t); + +#endif diff --git a/inc/defaults.h b/inc/defaults.h new file mode 100644 index 0000000..21abbd8 --- /dev/null +++ b/inc/defaults.h @@ -0,0 +1,13 @@ +#ifndef __DEFAULTS_H_ +#define __DEFAULTS_H_ + +#include +#include + +#include + +#define EVENTS_FILE "events" + +int defaults(); + +#endif diff --git a/inc/event.h b/inc/event.h new file mode 100644 index 0000000..ef64ed3 --- /dev/null +++ b/inc/event.h @@ -0,0 +1,63 @@ +#ifndef __EVENT_H_ +#define __EVENT_H_ + +/* needed for strptime */ +#define _XOPEN_SOURCE +#define _POSIX_C_SOURCE 200809L + +#include +#include +#include +#include +#include +#include + +#include + +#define EVENT_SERIALIZE_MAX_LENGTH 1024 + +enum time_period { + TIME_PERIOD_NONE = 0, + TIME_PERIOD_SECOND, + TIME_PERIOD_MINUTE, + TIME_PERIOD_HOUR, + TIME_PERIOD_DAY, + TIME_PERIOD_WEEK, + TIME_PERIOD_MONTH, + TIME_PERIOD_YEAR +}; + +struct event_options { + unsigned int duration; + enum time_period duration_period; + + unsigned int recur; + enum time_period recur_period; +}; + +struct event { + struct event_options options; + + char *name; + char *place; + + struct tm datetime; +}; + +struct event_filter { + time_t start; + time_t end; +}; + +int event_date_compare(const struct event*,time_t); +void event_free(const struct event*); +void event_init(struct event*); +int event_name_set(struct event*, const char*); +enum time_period event_options_period(char); +int event_parse(char*, size_t, struct event*); +int event_place_set(struct event*, const char*); +int event_serialize(char*, size_t, const struct event*); +int event_time_set(struct event*, const char*); +int event_within(struct event*, struct event_filter*); + +#endif diff --git a/inc/file.h b/inc/file.h new file mode 100644 index 0000000..b2bc20d --- /dev/null +++ b/inc/file.h @@ -0,0 +1,15 @@ +#ifndef __FILE_H_ +#define __FILE_H_ + +#include +#include + +#include + +ssize_t file_line_next(char*, size_t, FILE*); +int file_move(const char*); +FILE *file_open(); +int file_pipe(FILE*, FILE*); +FILE *file_temp(char*); + +#endif diff --git a/inc/ls.h b/inc/ls.h new file mode 100644 index 0000000..c805335 --- /dev/null +++ b/inc/ls.h @@ -0,0 +1,14 @@ +#ifndef __LS_H_ +#define __LS_H_ + +#include +#include +#include + +#include +#include +#include + +int ls(); + +#endif diff --git a/inc/main.h b/inc/main.h new file mode 100644 index 0000000..7cca03e --- /dev/null +++ b/inc/main.h @@ -0,0 +1,17 @@ +#ifndef __MAIN_H_ +#define __MAIN_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int,char**); + +#endif diff --git a/inc/opt.h b/inc/opt.h new file mode 100644 index 0000000..16f6a3a --- /dev/null +++ b/inc/opt.h @@ -0,0 +1,29 @@ +#ifndef __OPT_H_ +#define __OPT_H_ + +#include +#include +#include + +#include + +struct options { + char *file; + + struct event_options event_options; + + struct event_filter event_filter; +}; + +extern struct options global_options; + +int opt_duration_set(const char*); +int opt_event_filter_date_set(time_t*,const char*); +int opt_event_filter_range_set(const char*, const char*); +int opt_event_filter_set(const char*); +int opt_file_set(const char*); +void opt_global_init(); +int opt_recur_set(const char*); +int opt_load_from_env(); + +#endif diff --git a/inc/postpone.h b/inc/postpone.h new file mode 100644 index 0000000..daa5129 --- /dev/null +++ b/inc/postpone.h @@ -0,0 +1,14 @@ +#ifndef __POSTPONE_H_ +#define __POSTPONE_H_ + +#include +#include + +#include +#include +#include +#include + +int postpone(); + +#endif diff --git a/inc/prune.h b/inc/prune.h new file mode 100644 index 0000000..f45aacc --- /dev/null +++ b/inc/prune.h @@ -0,0 +1,12 @@ +#ifndef __PRUNE_H_ +#define __PRUNE_H_ + +#include +#include + +#include +#include + +int prune(); + +#endif diff --git a/inc/rm.h b/inc/rm.h new file mode 100644 index 0000000..d93856e --- /dev/null +++ b/inc/rm.h @@ -0,0 +1,17 @@ +#ifndef __RM_H_ +#define __RM_H_ + +#include +#include +#include + +#include +#include +#include +#include + +#define RM_FLAG_DISMISS 1 + +int rm(int,char**,int); + +#endif diff --git a/inc/seek.h b/inc/seek.h new file mode 100644 index 0000000..cca9787 --- /dev/null +++ b/inc/seek.h @@ -0,0 +1,9 @@ +#ifndef __SEEK_H_ +#define __SEEK_H_ + +#include +#include + +int seek(FILE*,time_t); + +#endif diff --git a/inc/usage.h b/inc/usage.h new file mode 100644 index 0000000..c05b501 --- /dev/null +++ b/inc/usage.h @@ -0,0 +1,8 @@ +#ifndef __USAGE_H_ +#define __USAGE_H_ + +#include + +void usage(); + +#endif diff --git a/src/add.c b/src/add.c new file mode 100644 index 0000000..df98e35 --- /dev/null +++ b/src/add.c @@ -0,0 +1,140 @@ +#include + +static int add_name_set(struct event*,const char*); +static int add_place_set(struct event*,const char*); +static int add_time_set(struct event*,const char*); + +int add(int argc, char **argv) { + struct event ev; + char buf[EVENT_SERIALIZE_MAX_LENGTH]; + char timebuf[40]; + + event_init(&ev); + + if(global_options.event_options.duration>0) { + ev.options.duration = global_options.event_options.duration; + ev.options.duration_period = global_options.event_options.duration_period; + } + + if(global_options.event_options.recur>0) { + ev.options.recur = global_options.event_options.recur; + ev.options.recur_period = global_options.event_options.recur_period; + } + + switch(argc-optind) { + case 4: + if(add_place_set(&ev,argv[optind+2])<0) { return EXIT_FAILURE; } + case 3: + if(add_name_set(&ev,argv[optind+1])<0) { return EXIT_FAILURE; } + if(add_time_set(&ev,argv[argc-1])<0) { return EXIT_FAILURE; } + break; + default: + fprintf(stderr,"invalid number of arguments\n"); + usage(); + return EXIT_FAILURE; + } + + strftime(timebuf,40,"%a, %d %b %Y %T %z",&(ev.datetime)); + + if(ev.place!=NULL) { + if(snprintf( + buf, + EVENT_SERIALIZE_MAX_LENGTH, + "Added event '%s' at '%s' on %s\n", + ev.name, + ev.place, + timebuf + )<0) { goto too_long; } + } else { + if(snprintf( + buf, + EVENT_SERIALIZE_MAX_LENGTH, + "Added event '%s' on %s\n", + ev.name, + timebuf + )<0) { goto too_long; } + } + + if(add_to_file(&ev)<0) { return EXIT_FAILURE; } + + fprintf(stdout,buf); + + return EXIT_SUCCESS; +too_long: + fprintf(stderr,"event too long\n"); + return EXIT_FAILURE; +} + +static int add_name_set(struct event *ev, const char *p) { + assert(ev!=NULL); + + if(NULL==p) { return -1; } + + ev->name = strdup(p); + if(NULL==ev->name) { return -1; } + + return 1; +} + +static int add_place_set(struct event *ev, const char *p) { + assert(ev!=NULL); + + if(NULL==p) { return -1; } + + ev->place = strdup(p); + if(NULL==ev->place) { return -1; } + + return 1; +} + +static int add_time_set(struct event *ev, const char *str) { + assert(ev!=NULL); + + if(NULL==str) { return -1; } + + if(event_time_set(ev,str)<0) { + fprintf(stderr,"invalid date format provided\n"); + usage(); + return -1; + } + + return 1; +} + +int add_to_file(struct event *to_add) { + char buf[EVENT_SERIALIZE_MAX_LENGTH]; + struct tm tm; + time_t to_add_time; + FILE *src, *to; + ssize_t i; + + assert(to_add!=NULL); + + if(NULL==to_add->name) { return -1; } + + char tempfile[] = "/tmp/XXXXXX"; + to = file_temp(tempfile); + if(NULL==to) { return -1; } + + src = file_open(); + if(NULL==src) { return -1; } + + memcpy(&tm,&(to_add->datetime),sizeof(struct tm)); + to_add_time = mktime(&tm); + + if(copy(to,src,to_add_time)<0) { return -1; } + + if(event_serialize(buf,EVENT_SERIALIZE_MAX_LENGTH,to_add)<0) { return -1; } + + i = strlen(buf); + if(fwrite(buf,1,i,to)!=i) { return -1; } + + if(file_pipe(to,src)<0) { return -1; } + + if(fclose(src)!=0) { return -1; } + if(fclose(to)!=0) { return -1; } + + if(file_move(tempfile)<0) { return -1; } + + return 1; +} diff --git a/src/args.c b/src/args.c new file mode 100644 index 0000000..21232ab --- /dev/null +++ b/src/args.c @@ -0,0 +1,76 @@ +#include + +struct option long_options[] = { + {"all", no_argument, 0, 'a'}, + {"duration", required_argument, 0, 'd'}, + {"end-date", required_argument, 0, 'e'}, + {"file", required_argument, 0, 'f'}, + {"interactive", no_argument, 0, 'i'}, + {"overdue", no_argument, 0, 'o'}, + {"recurring", required_argument, 0, 'r'}, + {"start-date", required_argument, 0, 's'}, + {"upcoming", required_argument, 0, 'u'}, + {0,0,0,0} +}; + +enum sub_command args(int argc, char **argv) { + char c; + + while(1) { + int option_index = 0; + + if((c = getopt_long(argc,argv,"ad:e:f:or:s:u:",long_options,&option_index))==-1) { break; } + + switch(c) { + case 'a': + if(opt_event_filter_set("all")<0) { goto fail; } + break; + case 'd': + if(opt_duration_set(optarg)<0) { goto fail; } + break; + case 'e': + if(opt_event_filter_date_set( + &(global_options.event_filter.end), + /* time_t *to_set */ + optarg /* const char *date_string */ + )<0) { goto fail; } + break; + case 'f': + if(opt_file_set(optarg)<0) { goto fail; } + break; + case 'o': + if(opt_event_filter_set("overdue")<0) { goto fail; } + break; + case 'r': + if(opt_recur_set(optarg)<0) { goto fail; } + break; + case 's': + if(opt_event_filter_date_set( + &(global_options.event_filter.start), + /* time_t *to_set */ + optarg /* const char *date_string */ + )<0) { goto fail; } + break; + case 'u': + if(opt_event_filter_range_set("upcoming",optarg)<0) { goto fail; } + break; + case '?': + default: + goto fail; + } + } + + if(optind>=argc) { return SUB_COMMAND_LS; } + + if(strcmp(argv[optind],"add")==0) { return SUB_COMMAND_ADD; } + if(strcmp(argv[optind],"dismiss")==0) { return SUB_COMMAND_DISMISS; } + if(strcmp(argv[optind],"ls")==0) { return SUB_COMMAND_LS; } + if(strcmp(argv[optind],"postpone")==0) { return SUB_COMMAND_POSTPONE; } + if(strcmp(argv[optind],"prune")==0) { return SUB_COMMAND_PRUNE; } + if(strcmp(argv[optind],"rm")==0) { return SUB_COMMAND_RM; } + + goto fail; +fail: + usage(); + return SUB_COMMAND_INVALID_ARGS; +} diff --git a/src/copy.c b/src/copy.c new file mode 100644 index 0000000..0c3edc1 --- /dev/null +++ b/src/copy.c @@ -0,0 +1,56 @@ +#include + +int copy(FILE *to, FILE *src, time_t until) { + char buf[EVENT_SERIALIZE_MAX_LENGTH]; + struct event ev; + ssize_t i; + + if(NULL==to) { return -1; } + if(NULL==src) { return -1; } + + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + while((i = file_line_next(buf,EVENT_SERIALIZE_MAX_LENGTH,src))>0) { + + if(event_parse(buf,EVENT_SERIALIZE_MAX_LENGTH,&ev)<0) { return -1; } + + if(event_date_compare(&ev,until)>0) { + event_free(&ev); + if(fseek(src,-i,SEEK_CUR)!=0) { return -1; } + break; + } + + if(event_serialize(buf,EVENT_SERIALIZE_MAX_LENGTH,&ev)<0) { return -1; } + + if(fwrite(buf,1,i,to)!=i) { return -1; } + } + + if(i<0) { + if(ferror(src)!=0) { + fprintf(stderr,"failed to get line\n"); + return -1; + } + } + + return 1; +} + +int copy_next_event(FILE *to, FILE *src) { + char buf[EVENT_SERIALIZE_MAX_LENGTH]; + ssize_t i; + + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + i = file_line_next(buf,EVENT_SERIALIZE_MAX_LENGTH,src); + + if(i<0) { + if(ferror(src)!=0) { + fprintf(stderr,"failed to get line"); + return -1; + } + } + + if(i>0) { + if(fwrite(buf,1,i,to)!=i) { return -1; } + } + + return 1; +} diff --git a/src/cut.c b/src/cut.c new file mode 100644 index 0000000..f6239ff --- /dev/null +++ b/src/cut.c @@ -0,0 +1,57 @@ +#include + +static int extract(FILE*, struct event*); + +int cut(struct event *ev, time_t start, size_t offset) { + FILE *src, *to; + + assert(ev!=NULL); + + char tempfile[] = "/tmp/XXXXXX"; + to = file_temp(tempfile); + if(NULL==to) { return -1; } + + src = file_open(); + if(NULL==src) { return -1; } + + if(copy(to,src,start)<0) { return -1; } + + while(offset>0) { + if(copy_next_event(to,src)<0) { return -1; } + offset--; + } + + if(extract(src,ev)<0) { return -1; } + + if(file_pipe(to,src)<0) { return -1; } + + if(fclose(src)!=0) { return -1; } + if(fclose(to)!=0) { return -1; } + + if(file_move(tempfile)<0) { return -1; } + + return 1; +} + +static int extract(FILE *src, struct event *ev) { + char buf[EVENT_SERIALIZE_MAX_LENGTH]; + ssize_t i; + + assert(ev!=NULL); + + if(NULL==src) { return -1; } + + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + + i = file_line_next(buf,EVENT_SERIALIZE_MAX_LENGTH,src); + if(i<0) { + if(ferror(src)!=0) { + fprintf(stderr,"failed to get line"); + return -1; + } + } + + if(event_parse(buf,EVENT_SERIALIZE_MAX_LENGTH,ev)<0) { return -1; } + + return 1; +} diff --git a/src/defaults.c b/src/defaults.c new file mode 100644 index 0000000..cd3d8f6 --- /dev/null +++ b/src/defaults.c @@ -0,0 +1,32 @@ +#include + +struct options global_options; + +static const char default_file[] = EVENTS_FILE; + +static time_t midnight(); + +int defaults() { + opt_global_init(); + + if(opt_file_set(default_file)<0) { return -1; } + + global_options.event_filter.start = 0; + global_options.event_filter.end = midnight(); + + return 1; +} + +static time_t midnight() { + struct tm *now; + time_t now_timet; + + now_timet = time(NULL); + now = localtime(&now_timet); + + now->tm_sec = 59; + now->tm_min = 59; + now->tm_hour = 23; + + return mktime(now); +} diff --git a/src/event.c b/src/event.c new file mode 100644 index 0000000..236d4d1 --- /dev/null +++ b/src/event.c @@ -0,0 +1,420 @@ +#include + +static void event_options_init(struct event_options*); +static char event_options_period_char(enum time_period period); +static int event_parse_name(char*, size_t, struct event*); +static int event_parse_options(char*, size_t, struct event*); +static int event_parse_place(char*, size_t, struct event*); +static int event_parse_time(char*, size_t, struct event*); +static int event_serialize_name(char*, size_t, const struct event*); +static int event_serialize_options(char*, size_t, const struct event*); +static int event_serialize_place(char*, size_t, const struct event*); +static int event_serialize_time(char*, size_t, const struct event*); + +int event_date_compare(const struct event *ev, time_t time) { + struct tm tm; + time_t event_time; + memcpy(&tm,&(ev->datetime),sizeof(struct tm)); + + event_time = mktime(&tm); + + if(event_time>time) { return 1; } + if(event_timename!=NULL) { free(p->name); } + if(p->place!=NULL) { free(p->place); } +} + +void event_init(struct event *p) { + assert(p!=NULL); + + event_options_init(&(p->options)); + + p->name = NULL; + p->place = NULL; + + memset(&(p->datetime),0,sizeof(struct tm)); +} + +static void event_options_init(struct event_options *p) { + assert(p!=NULL); + + p->recur = 0; + p->recur_period = TIME_PERIOD_NONE; + + p->duration = 0; + p->duration_period = TIME_PERIOD_NONE; +} + +enum time_period event_options_period(char period) { + switch(period) { + case 'Y': + return TIME_PERIOD_YEAR; + case 'M': + return TIME_PERIOD_MONTH; + case 'W': + return TIME_PERIOD_WEEK; + case 'D': + return TIME_PERIOD_DAY; + case 'h': + return TIME_PERIOD_HOUR; + case 'm': + return TIME_PERIOD_MINUTE; + case 's': + return TIME_PERIOD_SECOND; + default: + return TIME_PERIOD_NONE; + } +} + +static char event_options_period_char(enum time_period period) { + switch(period) { + case TIME_PERIOD_SECOND: + return 's'; + case TIME_PERIOD_MINUTE: + return 'm'; + case TIME_PERIOD_HOUR: + return 'h'; + case TIME_PERIOD_DAY: + return 'D'; + case TIME_PERIOD_WEEK: + return 'W'; + case TIME_PERIOD_MONTH: + return 'M'; + case TIME_PERIOD_YEAR: + return 'Y'; + default: + return '\0'; + } +} + +int event_name_set(struct event *ev, const char *name) { + size_t len; + + if(ev->name!=NULL) { free(ev->name); } + + ev->name = strdup(name); + if(ev->name==NULL) { return -1; } + + len = strlen(ev->name); + len--; + if(ev->name[len]=='\n') { + ev->name[len] = '\0'; + } + + return 1; +} + +int event_parse(char *buf, size_t buf_size, struct event *ev) { + event_init(ev); + + if(event_parse_options(buf,buf_size,ev)<0) { return -1; } + if(event_parse_time(buf,buf_size,ev)<0) { return -1; } + if(event_parse_name(buf,buf_size,ev)<0) { return -1; } + if(event_parse_place(buf,buf_size,ev)<0) { return -1; } + + return 1; +} + +static int event_parse_name(char *buf, size_t buf_size, struct event *ev) { + const char escaped[] = "\""; + const char normal[] = ":"; + const char *token; + char *p; + + if(buf[0]=='"') { + token = escaped; + } else { + token = normal; + } + + p = strtok(buf,token); + if(p!=NULL) { + if(event_name_set(ev,p)<0) { return -1; } + } + + p = strtok(NULL,token); + + /* + * fix string for if place is also escaped + */ + if(token[0]!=':') { + if(p[1]=='\0') { + p[1] = '"'; + } + p++; + } + + if(p!=NULL) { + memmove(buf,p,(buf_size - (p - buf))); + } else { + buf[0] = '\0'; + } + + return 1; +} + +static int event_parse_options(char *buf, size_t buf_size, struct event *ev) { + char period; + char *options, *p; + size_t diff; + + if(buf[0]!='[') { return 0; } + + options = strtok(buf,"]"); + + diff = strlen(options)+1; + + p = strtok(&(options[1]),","); + while(p!=NULL) { + if(memcmp(p,"duration",8)==0) { + if(sscanf( + p, + "duration:%u%c", + &(ev->options.duration), + &period + )!=2) { + if(errno!=0) { + perror("sccanf"); + return -1; + } + } + ev->options.duration_period = event_options_period(period); + } else if(memcmp(p,"recur",5)==0) { + if(sscanf( + p, + "recur:%u%c", + &(ev->options.recur), + &period + )!=2) { + if(errno!=0) { + perror("sccanf"); + return -1; + } + } + ev->options.recur_period = event_options_period(period); + } else { return -1; } + + p = strtok(NULL,","); + } + + memmove(buf,&(buf[diff]),(buf_size - diff)); + return 1; +} + +static int event_parse_place(char *buf, size_t buf_size, struct event *ev) { + const char escaped[] = "\""; + const char normal[] = ":"; + const char *token; + char *p; + + if(buf[0]=='"') { + token = escaped; + } else { + token = normal; + } + + p = strtok(buf,token); + if(p!=NULL) { + if(event_place_set(ev,p)<0) { return -1; } + } + + return 1; +} + +static int event_parse_time(char *buf, size_t buf_size, struct event *ev) { + char *p; + + if(buf[10]!=' ') { + buf[10] = '\0'; + p = &(buf[11]); + } else if(buf[19]!=' ') { + buf[19] = '\0'; + p = &(buf[20]); + } else { + buf[25] = '\0'; + p = &(buf[26]); + } + + if(event_time_set(ev,buf)<0) { return -1; } + + memmove(buf,p,(buf_size - (p - buf))); + + return 1; +} + +int event_place_set(struct event *ev, const char *place) { + size_t len; + + if(ev->place!=NULL) { free(ev->place); } + + ev->place = strdup(place); + if(ev->place==NULL) { return -1; } + + len = strlen(ev->place); + len--; + if(ev->place[len]=='\n') { + ev->place[len] = '\0'; + } + + return 1; +} + +int event_serialize(char *buf, size_t buf_size, const struct event *ev) { + assert(buf!=NULL); + + memset(buf,0,buf_size); + + if(event_serialize_options(buf,buf_size,ev)<0) { return -1; } + if(event_serialize_time(buf,buf_size,ev)<0) { return -1; } + if(event_serialize_name(buf,buf_size,ev)<0) { return -1; } + if(event_serialize_place(buf,buf_size,ev)<0) { return -1; } + + event_free(ev); + + strcat(buf,"\n"); + + return 1; +} + +static int event_serialize_name(char *buf, size_t buf_size, const struct event *ev) { + char *p; + + if(NULL==ev->name) { return -1; } + + if(strlen(buf)+strlen(ev->name)+2>buf_size) { return -1; } + + strcat(buf,":"); + + p = strchr(ev->name,':'); + if(p!=NULL) { strcat(buf,"\""); } + + strcat(buf,ev->name); + + if(p!=NULL) { strcat(buf,"\""); } + + return 1; +} + +static int event_serialize_options(char *buf, size_t buf_size, const struct event *ev) { + size_t pos; + + if(!((ev->options.duration>0)||(ev->options.recur>0))) { return 0; } + + strcpy(buf,"["); + pos = 1; + + if(ev->options.duration>0) { + if(sprintf( + &(buf[pos]), + "duration:%u%c", + ev->options.duration, + event_options_period_char(ev->options.duration_period) + )<0) { return -1; } + + if(ev->options.recur>0) { + strcat(buf,","); + } + + pos = strlen(buf); + } + + if(ev->options.recur>0) { + if(sprintf( + &(buf[pos]), + "recur:%u%c", + ev->options.recur, + event_options_period_char(ev->options.recur_period) + )<0) { return -1; } + } + + strcat(buf,"]"); + + return 1; +} + +static int event_serialize_place(char *buf, size_t buf_size, const struct event *ev) { + char *p; + + assert(buf!=NULL); + + if(NULL==ev->place) { return 0; } + + if(strlen(buf)+strlen(ev->place)+2>buf_size) { return -1; } + + strcat(buf,":"); + + p = strchr(ev->name,':'); + if(p!=NULL) { strcat(buf,"\""); } + + strcat(buf,ev->place); + + if(p!=NULL) { strcat(buf,"\""); } + + return 1; +} + +static int event_serialize_time(char *buf, size_t buf_size, const struct event *ev) { + size_t len; + + assert(buf!=NULL); + + len = strlen(buf); + + if(ev->datetime.tm_sec==0 && ev->datetime.tm_min==0 && ev->datetime.tm_hour == 0) { + if(0==strftime( + &(buf[len]), /* char *s */ + buf_size - len, /* size_t max */ + "%Y-%m-%d", /* const char *format */ + &(ev->datetime) /* const struct tm *tm */ + )) { return -1; } + } else { + if(0==strftime( + &(buf[len]), /* char *s */ + buf_size - len, /* size_t max */ + "%Y-%m-%d %H:%M:%S %z", /* const char *format */ + &(ev->datetime) /* const struct tm *tm */ + )) { return -1; } + } + + return 1; +} + +int event_time_set(struct event *ev, const char *str) { + const char *p; + + p = str; + + memset(&(ev->datetime), 0, sizeof(struct tm)); + + /* minimum expected date format */ + p = strptime(p,"%Y-%m-%d",&(ev->datetime)); + if(NULL==p) { return -1; } + + /* attempt to get time information */ + p = strptime(p,"%H:%M:%S",&(ev->datetime)); + + /* attempt to get timezone information */ + if(p!=NULL) { + strptime(p,"%z",&(ev->datetime)); + } + + return 1; +} + +int event_within(struct event *ev, struct event_filter *filter) { + struct tm tm; + time_t event_time; + + memcpy(&tm,&(ev->datetime),sizeof(struct tm)); + + event_time = mktime(&tm); + + if(filter->start>event_time) { return -1; } + if(filter->end + +ssize_t file_line_next(char *buf, size_t n, FILE *stream) { + return getline(&buf,&n,stream); +} diff --git a/src/file/mv.c b/src/file/mv.c new file mode 100644 index 0000000..688ab97 --- /dev/null +++ b/src/file/mv.c @@ -0,0 +1,10 @@ +#include + +int file_move(const char *tempname) { + if(rename(tempname,global_options.file)==-1) { + perror("rename"); + return -1; + } + + return 1; +} diff --git a/src/file/open.c b/src/file/open.c new file mode 100644 index 0000000..ecbf85e --- /dev/null +++ b/src/file/open.c @@ -0,0 +1,25 @@ +#include + +static FILE *file_create_prompt(); + +static FILE *file_create_prompt() { + fprintf(stderr,"%s doesn't exist... create? [Y/n] ",global_options.file); + if(fgetc(stdin)!='Y') { return NULL; } + return fopen(global_options.file,"w+"); +} + +FILE *file_open() { + FILE *fp; + + fp = fopen(global_options.file,"r"); + if(NULL==fp) { + if(errno!=ENOENT) { + perror("fopen"); + return NULL; + } else { + fp = file_create_prompt(); + } + } + + return fp; +} diff --git a/src/file/pipe.c b/src/file/pipe.c new file mode 100644 index 0000000..1f6c439 --- /dev/null +++ b/src/file/pipe.c @@ -0,0 +1,14 @@ +#include + +int file_pipe(FILE *to, FILE *from) { + char buf[100]; + size_t i; + + while((i = fread(buf, 1, 100, from))>0) { + if(fwrite(buf,1,i,to)!=i) { return -1; } + } + + if(feof(from)==0) { return -1; } + + return 1; +} diff --git a/src/file/tmp.c b/src/file/tmp.c new file mode 100644 index 0000000..ca4050e --- /dev/null +++ b/src/file/tmp.c @@ -0,0 +1,11 @@ +#include + +FILE *file_temp(char *template) { + int fd = mkstemp(template); + if(fd<0) { + perror("mkstemp"); + return NULL; + } + + return fdopen(fd,"w"); +} diff --git a/src/ls.c b/src/ls.c new file mode 100644 index 0000000..ad07d9c --- /dev/null +++ b/src/ls.c @@ -0,0 +1,59 @@ +#include + +static int event_print(struct event*); + +int ls() { + char buf[EVENT_SERIALIZE_MAX_LENGTH]; + struct event ev; + ssize_t i; + FILE *fp; + + fp = file_open(); + if(fp==NULL) { return EXIT_FAILURE; } + + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + while((i = file_line_next(buf,EVENT_SERIALIZE_MAX_LENGTH,fp))>0) { + + if(event_parse(buf,EVENT_SERIALIZE_MAX_LENGTH,&ev)<0) { goto close; } + + if(event_within(&ev,&(global_options.event_filter))>0) { + if(event_print(&ev)<0) { goto close; } + } + + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + } + + if(fclose(fp)!=0) { + perror("fclose"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +close: + fclose(fp); + return EXIT_FAILURE; +} + +static int event_print(struct event *ev) { + char datebuf[20]; + if( + (ev->datetime.tm_sec==0) && + (ev->datetime.tm_min == 0) && + (ev->datetime.tm_hour == 0) + ) { + if(strftime(datebuf,20,"%Y-%m-%d",&(ev->datetime))!=10) { return -1; } + } else { + if(strftime(datebuf,20,"%Y-%m-%d %H:%M:%S",&(ev->datetime))!=19) { return -1; } + } + + if(fprintf(stdout,"%s\t",datebuf)<0) { return -1; } + if(fprintf(stdout,"%s",ev->name)<0) { return -1; } + + if(ev->place!=NULL) { + if(fprintf(stdout," @ %s",ev->place)<0) { return -1; } + } + + if(fprintf(stdout,"\n")<0) { return -1; } + + return 1; +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..52bf386 --- /dev/null +++ b/src/main.c @@ -0,0 +1,28 @@ +#include + +int main(int argc, char **argv) { + enum sub_command cmd; + + if(defaults()<0) { return EXIT_FAILURE; } + if(opt_load_from_env()<0) { return EXIT_FAILURE; } + + cmd = args(argc,argv); + if(SUB_COMMAND_INVALID_ARGS==cmd) { return EXIT_FAILURE; } + + switch(cmd) { + case SUB_COMMAND_ADD: + return add(argc,argv); + case SUB_COMMAND_DISMISS: + return rm(argc,argv,RM_FLAG_DISMISS); + case SUB_COMMAND_LS: + return ls(); + case SUB_COMMAND_POSTPONE: + return postpone(argc,argv); + case SUB_COMMAND_PRUNE: + return prune(); + case SUB_COMMAND_RM: + return rm(argc,argv,0); + default: + return EXIT_FAILURE; + } +} diff --git a/src/opt/duration.c b/src/opt/duration.c new file mode 100644 index 0000000..82c882e --- /dev/null +++ b/src/opt/duration.c @@ -0,0 +1,25 @@ +#include + +int opt_duration_set(const char *p) { + unsigned int duration; + char c; + enum time_period period; + + if(NULL==p) { return -1; } + + if(sscanf(p,"%u%c",&duration,&c)!=2) { + fprintf(stderr,"invalid duration option value: %s\n",p); + return -1; + } + + period = event_options_period(c); + if(TIME_PERIOD_NONE==period) { + fprintf(stderr,"invalid duration option value: %s\n",p); + return -1; + } + + global_options.event_options.duration = duration; + global_options.event_options.duration_period = period; + + return 1; +} diff --git a/src/opt/env.c b/src/opt/env.c new file mode 100644 index 0000000..acf6298 --- /dev/null +++ b/src/opt/env.c @@ -0,0 +1,19 @@ +#include + +#define CHECK_ENV(x,f) { \ + p = getenv("EVENTS_"x); \ + if(p!=NULL) { \ + if(f(p)<0) { return -1; } \ + } \ +} + +int opt_load_from_env() { + char *p; + + CHECK_ENV("DURATION",opt_duration_set); + CHECK_ENV("EVENT_FILTER",opt_event_filter_set); + CHECK_ENV("FILE",opt_file_set); + CHECK_ENV("RECUR",opt_recur_set); + + return 1; +} diff --git a/src/opt/file.c b/src/opt/file.c new file mode 100644 index 0000000..2e539db --- /dev/null +++ b/src/opt/file.c @@ -0,0 +1,10 @@ +#include + +int opt_file_set(const char *p) { + if(global_options.file!=NULL) { free(global_options.file); } + + global_options.file = strdup(p); + if(NULL==global_options.file) { return -1; } + + return 1; +} diff --git a/src/opt/filter.c b/src/opt/filter.c new file mode 100644 index 0000000..60f6d7b --- /dev/null +++ b/src/opt/filter.c @@ -0,0 +1,100 @@ +#include + +static int opt_event_filter_range_set_upcoming(const char*); + +int opt_event_filter_date_set(time_t *to_set, const char *date_string) { + struct event ev; + + assert(to_set!=NULL); + + /* + * This function uses the event_time_set(struct event *ev, const char *str) + * method for consistencies' sake. + */ + + if(event_time_set(&ev,date_string)<0) { + fprintf(stderr,"invalid date given: %s\n",date_string); + return -1; + } + + (*to_set) = mktime(&(ev.datetime)); + + return 1; +} + +int opt_event_filter_range_set(const char *type, const char *arg) { + if(strcmp(type,"upcoming")==0) { + if(opt_event_filter_range_set_upcoming(arg)<0) { + fprintf(stderr, + "invalid time period given as argument to --upcoming option\n" + ); + return -1; + } + return 1; + } + + fprintf(stderr,"should never get to here\n"); + return -1; +} + +static int opt_event_filter_range_set_upcoming(const char *arg) { + struct tm *now; + time_t now_timet; + unsigned int offset; + char period; + + now_timet = time(NULL); + now = localtime(&now_timet); + + if(sscanf(arg,"%u%c",&offset,&period)!=2) { + if(errno!=0) { perror("sscanf"); } + return -1; + } + + switch(event_options_period(period)) { + case TIME_PERIOD_YEAR: + now->tm_year += offset; + break; + case TIME_PERIOD_MONTH: + now->tm_mon += offset; + break; + case TIME_PERIOD_WEEK: + now->tm_mday += 7*offset; + break; + case TIME_PERIOD_DAY: + now->tm_mday += offset; + break; + case TIME_PERIOD_HOUR: + now->tm_hour += offset; + break; + case TIME_PERIOD_MINUTE: + now->tm_min += offset; + break; + case TIME_PERIOD_SECOND: + now->tm_sec += offset; + break; + default: + return -1; + } + + global_options.event_filter.start = time(NULL); + global_options.event_filter.end = mktime(now); + + return 1; +} + +int opt_event_filter_set(const char *p) { + if(strcmp(p,"all")==0) { + global_options.event_filter.start = 0; + global_options.event_filter.end = INT_MAX; + return 1; + } + + if(strcmp(p,"overdue")==0) { + global_options.event_filter.start = 0; + global_options.event_filter.end = time(NULL); + return 1; + } + + return -1; +} diff --git a/src/opt/global.c b/src/opt/global.c new file mode 100644 index 0000000..74bdd75 --- /dev/null +++ b/src/opt/global.c @@ -0,0 +1,14 @@ +#include + +void opt_global_init() { + global_options.file = NULL; + + global_options.event_options.duration = 0; + global_options.event_options.duration_period = TIME_PERIOD_NONE; + + global_options.event_options.recur = 0; + global_options.event_options.recur_period = TIME_PERIOD_NONE; + + global_options.event_filter.start = 0; + global_options.event_filter.end = 0; +} diff --git a/src/opt/recur.c b/src/opt/recur.c new file mode 100644 index 0000000..d7a4938 --- /dev/null +++ b/src/opt/recur.c @@ -0,0 +1,25 @@ +#include + +int opt_recur_set(const char *p) { + unsigned int recur; + char c; + enum time_period period; + + if(NULL==p) { return -1; } + + if(sscanf(p,"%u%c",&recur,&c)!=2) { + fprintf(stderr,"invalid recur option value: %s\n",p); + return -1; + } + + period = event_options_period(c); + if(TIME_PERIOD_NONE==period) { + fprintf(stderr,"invalid recur option value: %s\n",p); + return -1; + } + + global_options.event_options.recur = recur; + global_options.event_options.recur_period = period; + + return 1; +} diff --git a/src/postpone.c b/src/postpone.c new file mode 100644 index 0000000..263971b --- /dev/null +++ b/src/postpone.c @@ -0,0 +1,135 @@ +#include + +static char *postpone_offset_string = NULL; + +static int handle_args(int,char**,unsigned long int*,time_t*); +static int postpone_event(struct event*); + +static int handle_args(int argc, char **argv, unsigned long int *offset, time_t *start_time) { + unsigned long int i; + + assert(offset!=NULL); + assert(start_time!=NULL); + + switch(argc-optind) { + case 4: /* date index period */ + if(opt_event_filter_date_set(start_time,argv[optind+1])<0) { + goto invalid; + } + + i = strtoul(argv[optind+2],NULL,0); + if(i>UINT_MAX) { goto invalid; } + (*offset) = i; + + postpone_offset_string = strdup(argv[argc-1]); + if(NULL==postpone_offset_string) { goto invalid; } + break; + case 3: /* date index */ + if(opt_event_filter_date_set(start_time,argv[optind+1])<0) { + goto invalid; + } + + i = strtoul(argv[argc-1],NULL,0); + if(i>UINT_MAX) { goto invalid; } + (*offset) = i; + break; + case 2: /* index */ + (*start_time) = 0; + + i = strtoul(argv[argc-1],NULL,0); + if(i>UINT_MAX) { goto invalid; } + (*offset) = i; + break; + default: + goto invalid; + } + + return 1; +invalid: + fprintf(stderr,"invalid number of arguments\n"); + usage(); + return -1; +} + +int postpone(int argc, char **argv) { + char timebuf[40]; + char buf[EVENT_SERIALIZE_MAX_LENGTH]; + time_t start_time; + size_t offset; + struct event ev; + + if(handle_args(argc,argv,&offset,&start_time)<0) { goto fail; } + + if(cut(&ev,start_time,offset)<0) { goto fail; } + + if(postpone_event(&ev)<0) { goto fail; } + + strftime(timebuf,40,"%a, %d %b %Y %T %z",&(ev.datetime)); + + if(snprintf( + buf, + EVENT_SERIALIZE_MAX_LENGTH, + "postponed event '%s' until '%s'\n", + ev.name, + timebuf + )<0) { goto fail; } + + if(add_to_file(&ev)<0) { goto fail; } + + fprintf(stdout,buf); + + return EXIT_SUCCESS; +fail: + if(postpone_offset_string!=NULL) { free(postpone_offset_string); } + return EXIT_FAILURE; +} + +int postpone_event(struct event *ev) { + unsigned int offset; + char period; + + assert(ev!=NULL); + + if(NULL==postpone_offset_string) { + ev->datetime.tm_mday += 1; + return 1; + } + + if(sscanf(postpone_offset_string,"%u%c",&offset,&period)!=2) { + if(errno!=0) { + perror("sscanf"); + return -1; + } + } + + switch(event_options_period(period)) { + case TIME_PERIOD_YEAR: + ev->datetime.tm_year += offset; + break; + case TIME_PERIOD_MONTH: + ev->datetime.tm_mon += offset; + break; + case TIME_PERIOD_WEEK: + ev->datetime.tm_mday += 7*offset; + break; + case TIME_PERIOD_DAY: + ev->datetime.tm_mday += offset; + break; + case TIME_PERIOD_HOUR: + ev->datetime.tm_hour += offset; + break; + case TIME_PERIOD_MINUTE: + ev->datetime.tm_min += offset; + break; + case TIME_PERIOD_SECOND: + ev->datetime.tm_sec += offset; + break; + default: + return -1; + } + + free(postpone_offset_string); + postpone_offset_string = NULL; + + return 1; +} diff --git a/src/prune.c b/src/prune.c new file mode 100644 index 0000000..0c3bc44 --- /dev/null +++ b/src/prune.c @@ -0,0 +1,23 @@ +#include + +int prune() { + FILE *src, *to; + + char tempfile[] = "/tmp/XXXXXX"; + to = file_temp(tempfile); + if(NULL==to) { return EXIT_FAILURE; } + + src = file_open(); + if(NULL==src) { return EXIT_FAILURE; } + + if(seek(src,time(NULL))<0) { return EXIT_FAILURE; } + + if(file_pipe(to,src)<0) { return EXIT_FAILURE; } + + if(fclose(src)!=0) { return EXIT_FAILURE; } + if(fclose(to)!=0) { return EXIT_FAILURE; } + + if(file_move(tempfile)<0) { return EXIT_FAILURE; } + + return EXIT_SUCCESS; +} diff --git a/src/rm.c b/src/rm.c new file mode 100644 index 0000000..5bc2e7f --- /dev/null +++ b/src/rm.c @@ -0,0 +1,107 @@ +#include + +static int check_need_readd(struct event*); +static int handle_args(int,char**,unsigned long int*, time_t *); + +static int check_need_readd(struct event *ev) { + char buf[EVENT_SERIALIZE_MAX_LENGTH]; + char timebuf[40]; + + assert(ev!=NULL); + + if(ev->options.recur==0) { return 1; } + + switch(ev->options.recur_period) { + case TIME_PERIOD_YEAR: + ev->datetime.tm_year += ev->options.recur; + break; + case TIME_PERIOD_MONTH: + ev->datetime.tm_mon += ev->options.recur; + break; + case TIME_PERIOD_WEEK: + ev->datetime.tm_mday += 7*(ev->options.recur); + break; + case TIME_PERIOD_DAY: + ev->datetime.tm_mday += ev->options.recur; + break; + case TIME_PERIOD_HOUR: + ev->datetime.tm_hour += ev->options.recur; + break; + case TIME_PERIOD_MINUTE: + ev->datetime.tm_min += ev->options.recur; + break; + case TIME_PERIOD_SECOND: + ev->datetime.tm_sec += ev->options.recur; + break; + default: + return -1; + } + + strftime(timebuf,40,"%a, %d %b %Y %T %z",&(ev->datetime)); + + if(snprintf( + buf, + EVENT_SERIALIZE_MAX_LENGTH, + "re-added event '%s' on %s\n", + ev->name, + timebuf + )<0) { return -1; } + + if(add_to_file(ev)<0) { return -1; } + + fprintf(stdout,buf); + + return 1; +} + +static int handle_args(int argc, char **argv, unsigned long int *offset, time_t *start_time) { + unsigned long i; + + assert(offset!=NULL); + assert(start_time!=NULL); + + (*start_time) = 0; + + switch(argc-optind) { + case 3: + if(opt_event_filter_date_set(start_time,argv[optind+1])<0) { + goto invalid; + } + case 2: + i = strtoul(argv[argc-1],NULL,0); + if(i>UINT_MAX) { goto invalid; } + (*offset) = i; + break; + default: + goto invalid; + } + + return 1; +invalid: + fprintf(stderr,"invalid arguments\n"); + usage(); + return -1; +} + +int rm(int argc, char **argv, int flags) { + char timebuf[40]; + time_t start_time; + size_t offset; + struct event ev; + + if(handle_args(argc,argv,&offset,&start_time)<0) { return EXIT_FAILURE; } + + if(cut(&ev,start_time,offset)<0) { return EXIT_FAILURE; } + + strftime(timebuf,40,"%a, %d %b %Y %T %z",&(ev.datetime)); + + fprintf(stderr,"removed event %s on %s\n",ev.name,timebuf); + + if(RM_FLAG_DISMISS==flags) { + if(check_need_readd(&ev)<0) { return EXIT_FAILURE; } + } else { + event_free(&ev); + } + + return EXIT_SUCCESS; +} diff --git a/src/seek.c b/src/seek.c new file mode 100644 index 0000000..8378202 --- /dev/null +++ b/src/seek.c @@ -0,0 +1,32 @@ +#include + +int seek(FILE *fp, time_t time) { + char buf[EVENT_SERIALIZE_MAX_LENGTH]; + struct event ev; + ssize_t i; + + if(NULL==fp) { return -1; } + + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + while((i = file_line_next(buf,EVENT_SERIALIZE_MAX_LENGTH,fp))>0) { + if(event_parse(buf,EVENT_SERIALIZE_MAX_LENGTH,&ev)<0) { return -1; } + + if(event_date_compare(&ev,time)>=0) { + event_free(&ev); + if(fseek(fp,-i,SEEK_CUR)!=0) { return -1; } + break; + } + + event_free(&ev); + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + } + + if(i<0) { + if(ferror(fp)!=0) { + fprintf(stderr,"failed to get line"); + return -1; + } + } + + return 1; +} diff --git a/src/usage.c b/src/usage.c new file mode 100644 index 0000000..4b6c991 --- /dev/null +++ b/src/usage.c @@ -0,0 +1,21 @@ +#include + +void usage() { + fprintf(stderr,"Usage:\n"); + fprintf(stderr,"\tev [options] [command = 'ls'] [arg1, arg2, ..., argn]\n"); + fprintf(stderr,"\n"); + fprintf(stderr,"Commands:\n"); + fprintf(stderr,"\tadd\n"); + fprintf(stderr,"\tdismiss\n"); + fprintf(stderr,"\tls\n"); + fprintf(stderr,"\tpostpone\n"); + fprintf(stderr,"\tprune\n"); + fprintf(stderr,"\trm\n"); + fprintf(stderr,"\n"); + + fprintf(stderr,"Options:\n"); + fprintf(stderr,"\n"); + fprintf(stderr,"See `man ev` for more information\n"); + + return; +} diff --git a/test/unit/Makefile.am b/test/unit/Makefile.am new file mode 100644 index 0000000..50b3bc2 --- /dev/null +++ b/test/unit/Makefile.am @@ -0,0 +1,153 @@ +AM_CPPFLAGS = \ + -Wall \ + -Werror + +EXTRA_DIST = \ + test_macros.h \ + test_utils.h + +if ENABLE_DEBUG +else +AM_CPPFLAGS += \ + -DNDEBUG +endif + +check_PROGRAMS = add.tests args.tests copy.tests cut.tests event.tests file.tests ls.tests opt.tests postpone.tests prune.tests rm.tests seek.tests +TESTS = $(check_PROGRAMS) + +if ENABLE_MEMCHECK +LOG_COMPILER = $(VALGRIND) +AM_LOG_FLAGS = --leak-check=full -v --track-origins=yes --error-exitcode=1 +endif + +common_SOURCES = \ + test_utils.c \ + $(top_srcdir)/src/opt/global.c + +add_tests_SOURCES = \ + $(common_SOURCES) \ + add.tests.c \ + $(top_srcdir)/src/copy.c \ + $(top_srcdir)/src/event.c \ + $(top_srcdir)/src/file/line.c \ + $(top_srcdir)/src/file/mv.c \ + $(top_srcdir)/src/file/open.c \ + $(top_srcdir)/src/file/pipe.c \ + $(top_srcdir)/src/file/tmp.c \ + $(top_srcdir)/src/usage.c + +add_tests_CPPFLAGS = $(AM_CPPFLAGS) \ + -DADD_SRC_FILE="$(top_srcdir)/src/add.c" + +args_tests_SOURCES = \ + $(common_SOURCES) \ + args.tests.c \ + $(top_srcdir)/src/args.c \ + $(top_srcdir)/src/event.c \ + $(top_srcdir)/src/opt/duration.c \ + $(top_srcdir)/src/opt/file.c \ + $(top_srcdir)/src/opt/filter.c \ + $(top_srcdir)/src/opt/recur.c \ + $(top_srcdir)/src/usage.c + +copy_tests_SOURCES = \ + $(common_SOURCES) \ + copy.tests.c \ + $(top_srcdir)/src/copy.c \ + $(top_srcdir)/src/event.c \ + $(top_srcdir)/src/file/line.c \ + $(top_srcdir)/src/file/tmp.c + +cut_tests_SOURCES = \ + $(common_SOURCES) \ + cut.tests.c \ + $(top_srcdir)/src/copy.c \ + $(top_srcdir)/src/event.c \ + $(top_srcdir)/src/file/line.c \ + $(top_srcdir)/src/file/open.c \ + $(top_srcdir)/src/file/pipe.c \ + $(top_srcdir)/src/file/mv.c \ + $(top_srcdir)/src/file/tmp.c + +cut_tests_CPPFLAGS = $(AM_CPPFLAGS) \ + -DCUT_SRC_FILE="$(top_srcdir)/src/cut.c" + +event_tests_SOURCES = \ + event.tests.c \ + $(top_srcdir)/src/event.c + +file_tests_SOURCES = \ + $(common_SOURCES) \ + file.tests.c \ + $(top_srcdir)/src/file/line.c \ + $(top_srcdir)/src/file/mv.c \ + $(top_srcdir)/src/file/open.c \ + $(top_srcdir)/src/file/pipe.c \ + $(top_srcdir)/src/file/tmp.c + +ls_tests_SOURCES = \ + $(common_SOURCES) \ + ls.tests.c + +opt_tests_SOURCES = \ + $(common_SOURCES) \ + opt.tests.c \ + $(top_srcdir)/src/event.c \ + $(top_srcdir)/src/opt/duration.c \ + $(top_srcdir)/src/opt/filter.c \ + $(top_srcdir)/src/opt/recur.c + +postpone_tests_SOURCES = \ + $(common_SOURCES) \ + postpone.tests.c \ + $(top_srcdir)/src/add.c \ + $(top_srcdir)/src/copy.c \ + $(top_srcdir)/src/cut.c \ + $(top_srcdir)/src/event.c \ + $(top_srcdir)/src/file/line.c \ + $(top_srcdir)/src/file/mv.c \ + $(top_srcdir)/src/file/open.c \ + $(top_srcdir)/src/file/pipe.c \ + $(top_srcdir)/src/file/tmp.c \ + $(top_srcdir)/src/opt/filter.c \ + $(top_srcdir)/src/usage.c + +postpone_tests_CPPFLAGS = $(AM_CPPFLAGS) \ + -DPOSTPONE_SRC_FILE="$(top_srcdir)/src/postpone.c" + +prune_tests_SOURCES = \ + $(common_SOURCES) \ + prune.tests.c \ + $(top_srcdir)/src/event.c \ + $(top_srcdir)/src/file/line.c \ + $(top_srcdir)/src/file/mv.c \ + $(top_srcdir)/src/file/open.c \ + $(top_srcdir)/src/file/pipe.c \ + $(top_srcdir)/src/file/tmp.c \ + $(top_srcdir)/src/prune.c \ + $(top_srcdir)/src/seek.c + +rm_tests_SOURCES = \ + $(common_SOURCES) \ + rm.tests.c \ + $(top_srcdir)/src/add.c \ + $(top_srcdir)/src/copy.c \ + $(top_srcdir)/src/cut.c \ + $(top_srcdir)/src/event.c \ + $(top_srcdir)/src/file/line.c \ + $(top_srcdir)/src/file/mv.c \ + $(top_srcdir)/src/file/open.c \ + $(top_srcdir)/src/file/pipe.c \ + $(top_srcdir)/src/file/tmp.c \ + $(top_srcdir)/src/opt/filter.c \ + $(top_srcdir)/src/usage.c + +rm_tests_CPPFLAGS = $(AM_CPPFLAGS) \ + -DRM_SRC_FILE="$(top_srcdir)/src/rm.c" + +seek_tests_SOURCES = \ + $(common_SOURCES) \ + seek.tests.c \ + $(top_srcdir)/src/event.c \ + $(top_srcdir)/src/file/line.c \ + $(top_srcdir)/src/seek.c diff --git a/test/unit/add.tests.c b/test/unit/add.tests.c new file mode 100644 index 0000000..1d56e26 --- /dev/null +++ b/test/unit/add.tests.c @@ -0,0 +1,182 @@ +#include + +#include + +#include + +#include INCLUDE(ADD_SRC_FILE) + +int main(); +static void add_name_set_basic_test(); +static void add_place_set_basic_test(); +static void add_time_set_basic_test(); +static void add_to_file_basic_test(); +static void add_to_file_extensive_test(); + +int main() { + setup_env(); + + add_name_set_basic_test(); + add_place_set_basic_test(); + add_time_set_basic_test(); + add_to_file_basic_test(); + add_to_file_extensive_test(); + + clean_env(); + + return EXIT_SUCCESS; +} + +static void add_name_set_basic_test() { + struct event ev; + + event_init(&ev); + + assert(-1==add_name_set(&ev,NULL)); + + assert(1==add_name_set(&ev,"test")); + + assert(strcmp(ev.name,"test")==0); + + event_free(&ev); +} + +static void add_place_set_basic_test() { + struct event ev; + + event_init(&ev); + + assert(-1==add_place_set(&ev,NULL)); + + assert(1==add_place_set(&ev,"test")); + + assert(strcmp(ev.place,"test")==0); + + event_free(&ev); +} + +static void add_time_set_basic_test() { + struct event ev; + + event_init(&ev); + + assert(-1==add_time_set(&ev,NULL)); + + assert(-1==add_time_set(&ev,"test")); + + assert(1==add_time_set(&ev,"2022-09-01")); + + assert(mktime(&(ev.datetime))==1662019200); +} + +static void add_to_file_basic_test() { + struct event ev; + char buf[1000]; + FILE *fp; + + event_init(&ev); + + fp = fopen(global_options.file,"w"); + assert(fp!=NULL); + assert(fclose(fp)==0); + + assert(-1==add_to_file(&ev)); + + assert(1==add_name_set(&ev,"name")); + assert(1==add_time_set(&ev,"2022-08-01")); + + assert(1==add_to_file(&ev)); + + fp = fopen(global_options.file,"r"); + assert(fp!=NULL); + + memset(buf,0,1000); + assert(fread(buf,1,1000,fp)==16); + assert(strcmp(buf,"2022-08-01:name\n")==0); + + assert(fclose(fp)==0); + + assert(1==add_name_set(&ev,"event2")); + assert(1==add_time_set(&ev,"2022-09-01")); + + assert(1==add_to_file(&ev)); + + fp = fopen(global_options.file,"r"); + assert(fp!=NULL); + + memset(buf,0,1000); + assert(fread(buf,1,1000,fp)==34); + assert(strcmp(buf,"2022-08-01:name\n2022-09-01:event2\n")==0); + + assert(fclose(fp)==0); + + assert(1==add_name_set(&ev,"event3")); + assert(1==add_place_set(&ev,"place")); + assert(1==add_time_set(&ev,"2022-06-01 20:00:00")); + + assert(1==add_to_file(&ev)); + + fp = fopen(global_options.file,"r"); + assert(fp!=NULL); + + memset(buf,0,1000); + assert(fread(buf,1,1000,fp)==73); + assert(strcmp(buf,"2022-06-01 20:00:00 +0000:event3:place\n2022-08-01:name\n2022-09-01:event2\n")==0); + + assert(fclose(fp)==0); + + reset_env(); +} + +static void add_to_file_extensive_test() { + int used[12]; + size_t index, rounds; + char date[11]; + char name[10]; + char buf[1000]; + struct event ev; + FILE *fp; + + rounds = 0; + while(rounds<100) { + memset(used,0,sizeof(int)*12); + + fp = fopen(global_options.file,"w"); + assert(fp!=NULL); + assert(fclose(fp)==0); + + for(size_t i=0;i<12;i++) { + memset(date,0,11); + memset(name,0,10); + + index = rand()%12; + while(used[index]!=0) { + index++; + index %= 12; + } + used[index] = 1; + + assert(10==sprintf(date,"2000-%02lu-01",index+1)); + assert(((index>9)?7:6)==sprintf(name,"event%lu",index)); + + event_init(&ev); + assert(1==add_name_set(&ev,name)); + assert(1==add_time_set(&ev,date)); + + assert(1==add_to_file(&ev)); + } + + fp = fopen(global_options.file,"r"); + assert(fp!=NULL); + + memset(buf,0,1000); + assert(fread(buf,1,1000,fp)==218); + assert(strcmp(buf,"2000-01-01:event0\n2000-02-01:event1\n2000-03-01:event2\n2000-04-01:event3\n2000-05-01:event4\n2000-06-01:event5\n2000-07-01:event6\n2000-08-01:event7\n2000-09-01:event8\n2000-10-01:event9\n2000-11-01:event10\n2000-12-01:event11\n")==0); + + assert(fclose(fp)==0); + + reset_env(); + + rounds++; + } +} diff --git a/test/unit/args.tests.c b/test/unit/args.tests.c new file mode 100644 index 0000000..a517266 --- /dev/null +++ b/test/unit/args.tests.c @@ -0,0 +1,334 @@ +#include + +#include + +int main(); +static void args_add_basic_test(); +static void args_basic_test(); +static void args_dismiss_basic_test(); +static void args_long_opts_basic_test(); +static void args_ls_all_basic_test(); +static void args_ls_basic_test(); +static void args_ls_end_date_basic_test(); +static void args_ls_overdue_basic_test(); +static void args_ls_start_date_basic_test(); +static void args_ls_upcoming_basic_test(); +static void args_order_test(); +static void args_postpone_basic_test(); +static void args_prune_basic_test(); +static void args_rm_basic_test(); +static void args_short_opts_basic_test(); + +int main() { + setup_env(); + + args_basic_test(); + args_order_test(); + + clean_env(); + + return EXIT_SUCCESS; +} + +static void args_add_basic_test() { + char *add_args[] = { + "ev", + "add", + NULL + }; + + assert(SUB_COMMAND_ADD==args(2,add_args)); + optind = 0; +} + +static void args_basic_test() { + assert(SUB_COMMAND_LS==args(0,NULL)); + + args_add_basic_test(); + args_dismiss_basic_test(); + args_ls_basic_test(); + args_ls_all_basic_test(); + args_ls_end_date_basic_test(); + args_ls_overdue_basic_test(); + args_ls_upcoming_basic_test(); + args_ls_start_date_basic_test(); + args_postpone_basic_test(); + args_prune_basic_test(); + args_rm_basic_test(); + + args_short_opts_basic_test(); + args_long_opts_basic_test(); +} + +static void args_dismiss_basic_test() { + char *dismiss_args[] = { + "ev", + "dismiss", + NULL + }; + + assert(SUB_COMMAND_DISMISS==args(2,dismiss_args)); + optind = 0; +} + +static void args_long_opts_basic_test() { + char *long_opts_args[] = { + "ev", + "add", + "--duration", + "1W", + "--file", + "/tmp/events.test2", + "--recurring", + "1Y", + NULL + }; + assert(SUB_COMMAND_ADD==args(8,long_opts_args)); + assert(global_options.event_options.duration==1); + assert(global_options.event_options.duration_period==TIME_PERIOD_WEEK); + assert(strcmp(global_options.file,"/tmp/events.test2")==0); + assert(global_options.event_options.recur==1); + assert(global_options.event_options.recur_period==TIME_PERIOD_YEAR); + optind = 0; + + reset_env(); +} + +static void args_ls_all_basic_test() { + char *ls_all_args[] = { + "ev", + "ls", + "--all", + NULL + }; + assert(SUB_COMMAND_LS==args(3,ls_all_args)); + assert(global_options.event_filter.start==0); + assert(global_options.event_filter.end==INT_MAX); + optind = 0; + + reset_env(); + + char *ls_all_short_args[] = { + "ev", + "ls", + "-a", + NULL + }; + assert(SUB_COMMAND_LS==args(3,ls_all_short_args)); + assert(global_options.event_filter.start==0); + assert(global_options.event_filter.end==INT_MAX); + optind = 0; + + reset_env(); +} + +static void args_ls_basic_test() { + char *ls_args[] = { + "ev", + "ls", + NULL + }; + assert(SUB_COMMAND_LS==args(2,ls_args)); + optind = 0; +} + +static void args_ls_end_date_basic_test() { + char *ls_end_date_args[] = { + "ev", + "ls", + "--end-date", + "2004-01-01", + NULL + }; + assert(SUB_COMMAND_LS==args(4,ls_end_date_args)); + assert(global_options.event_filter.start==0); + assert(global_options.event_filter.end==1072944000); + optind = 0; + + reset_env(); + + char *ls_end_date_short_args[] = { + "ev", + "ls", + "-e", + "2005-05-01", + NULL + }; + assert(SUB_COMMAND_LS==args(4,ls_end_date_short_args)); + assert(global_options.event_filter.start==0); + assert(global_options.event_filter.end==1114934400); + optind = 0; + + reset_env(); +} + +static void args_ls_overdue_basic_test() { + char *ls_overdue_args[] = { + "ev", + "ls", + "--overdue", + NULL + }; + assert(SUB_COMMAND_LS==args(3,ls_overdue_args)); + assert(global_options.event_filter.start==0); + assert((time(NULL) - global_options.event_filter.end)<1); + optind = 0; + + reset_env(); + + char *ls_overdue_short_args[] = { + "ev", + "ls", + "-o", + NULL + }; + assert(SUB_COMMAND_LS==args(3,ls_overdue_short_args)); + assert(global_options.event_filter.start==0); + assert((time(NULL) - global_options.event_filter.end)<1); + optind = 0; + + reset_env(); +} + +static void args_ls_start_date_basic_test() { + char *ls_start_date_args[] = { + "ev", + "ls", + "--start-date", + "2001-01-01", + NULL + }; + assert(SUB_COMMAND_LS==args(4,ls_start_date_args)); + assert(global_options.event_filter.start==978336000); + assert(global_options.event_filter.end==0); + optind = 0; + + reset_env(); + + char *ls_start_date_short_args[] = { + "ev", + "ls", + "-s", + "2002-01-01", + NULL + }; + assert(SUB_COMMAND_LS==args(4,ls_start_date_short_args)); + assert(global_options.event_filter.start==1009872000); + assert(global_options.event_filter.end==0); + optind = 0; + + reset_env(); +} + +static void args_ls_upcoming_basic_test() { + char *ls_upcoming_args[] = { + "ev", + "ls", + "--upcoming", + "1W", + NULL + }; + assert(SUB_COMMAND_LS==args(4,ls_upcoming_args)); + assert((time(NULL) - global_options.event_filter.start)<1); + assert((global_options.event_filter.end - global_options.event_filter.start)<(60*60*24*7+1)); + optind = 0; + + reset_env(); + + char *ls_upcoming_short_args[] = { + "ev", + "ls", + "-u", + "1D", + NULL + }; + assert(SUB_COMMAND_LS==args(4,ls_upcoming_short_args)); + assert((time(NULL) - global_options.event_filter.start)<1); + assert((global_options.event_filter.end - global_options.event_filter.start)<(60*60*24+1)); + optind = 0; + + reset_env(); +} + +static void args_order_test() { + char *normal[] = { + "ev", + "add", + "--duration", + "1W", + NULL + }; + assert(SUB_COMMAND_ADD==args(4,normal)); + assert(global_options.event_options.duration==1); + assert(global_options.event_options.duration_period==TIME_PERIOD_WEEK); + optind = 0; + + reset_env(); + + char *swapped[] = { + "ev", + "--duration", + "1W", + "add", + NULL + }; + assert(SUB_COMMAND_ADD==args(4,swapped)); + assert(global_options.event_options.duration==1); + assert(global_options.event_options.duration_period==TIME_PERIOD_WEEK); + optind = 0; + optind = 0; + reset_env(); +} + +static void args_postpone_basic_test() { + char *postpone_args[] = { + "ev", + "postpone", + NULL + }; + assert(SUB_COMMAND_POSTPONE==args(2,postpone_args)); + optind = 0; +} + +static void args_prune_basic_test() { + char *prune_args[] = { + "ev", + "prune", + NULL + }; + assert(SUB_COMMAND_PRUNE==args(2,prune_args)); + optind = 0; +} + +static void args_rm_basic_test() { + char *rm_args[] = { + "ev", + "rm", + NULL + }; + assert(SUB_COMMAND_RM==args(2,rm_args)); + optind = 0; +} + +static void args_short_opts_basic_test() { + char *short_opts_args[] = { + "ev", + "add", + "-d", + "1m", + "-f", + "/tmp/events.test3", + "-r", + "1D", + NULL + }; + assert(SUB_COMMAND_ADD==args(8,short_opts_args)); + assert(global_options.event_options.duration==1); + assert(global_options.event_options.duration_period==TIME_PERIOD_MINUTE); + assert(strcmp(global_options.file,"/tmp/events.test3")==0); + assert(global_options.event_options.recur==1); + assert(global_options.event_options.recur_period==TIME_PERIOD_DAY); + optind = 0; + + reset_env(); +} diff --git a/test/unit/copy.tests.c b/test/unit/copy.tests.c new file mode 100644 index 0000000..868bda2 --- /dev/null +++ b/test/unit/copy.tests.c @@ -0,0 +1,188 @@ +#include + +#include + +int main(); +static void copy_basic_test(); +static void copy_correctness_test(); +static void copy_next_event_basic_test(); + +int main() { + setup_env(); + + copy_basic_test(); + copy_correctness_test(); + copy_next_event_basic_test(); + + clean_env(); + + return EXIT_SUCCESS; +} + +static void copy_basic_test() { + FILE *src, *to; + + src = fopen(global_options.file,"w"); + assert(src!=NULL); + assert(fclose(src)==0); + + src = fopen(global_options.file,"r"); + assert(src!=NULL); + + char tempfile[] = "/tmp/XXXXXX"; + to = file_temp(tempfile); + assert(to!=NULL); + + assert(-1==copy(NULL,NULL,0)); + assert(-1==copy(to,NULL,0)); + + assert(1==copy(to,src,0)); + + reset_env(); +} + +static void copy_correctness_test() { + FILE *src, *to; + char buf[EVENT_SERIALIZE_MAX_LENGTH]; + char got[EVENT_SERIALIZE_MAX_LENGTH]; + time_t until; + + src = fopen(global_options.file,"w"); + assert(src!=NULL); + + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + memset(got,0,EVENT_SERIALIZE_MAX_LENGTH); + + strcpy(buf,"2022-01-01:test\n2022-02-01:test2\n2022-03-01:test3\n"); + assert(fwrite(buf,1,strlen(buf),src)==50); + + assert(fclose(src)==0); + + src = fopen(global_options.file,"r"); + assert(src!=NULL); + + char tempfile[] = "/tmp/XXXXXX"; + to = file_temp(tempfile); + assert(to!=NULL); + + until = 1642986321; /* 2022, Jan 24thish */ + assert(copy(to,src,until)==1); + + assert(fclose(to)==0); + to = fopen(tempfile,"r"); + assert(to!=NULL); + + assert(fread(got,1,EVENT_SERIALIZE_MAX_LENGTH,to)==16); + strcpy(buf,"2022-01-01:test\n"); + assert(memcmp(buf,got,16)==0); + + assert(fclose(to)==0); + assert(remove(tempfile)==0); + + rewind(src); + + char tempfile2[] = "/tmp/XXXXXX"; + to = file_temp(tempfile2); + assert(to!=NULL); + + until = 1645664721; /* 2022, Feb 24thish */ + assert(copy(to,src,until)==1); + + assert(fclose(to)==0); + to = fopen(tempfile2,"r"); + assert(to!=NULL); + + assert(fread(got,1,EVENT_SERIALIZE_MAX_LENGTH,to)==33); + strcpy(buf,"2022-01-01:test\n2022-02-01:test2\n"); + assert(memcmp(buf,got,33)==0); + + assert(fclose(to)==0); + assert(remove(tempfile2)==0); + + rewind(src); + + char tempfile3[] = "/tmp/XXXXXX"; + to = file_temp(tempfile3); + assert(to!=NULL); + + until = INT_MAX; /* END OF TIME */ + assert(copy(to,src,until)==1); + + assert(fclose(to)==0); + to = fopen(tempfile3,"r"); + assert(to!=NULL); + + assert(fread(got,1,EVENT_SERIALIZE_MAX_LENGTH,to)==50); + strcpy(buf,"2022-01-01:test\n2022-02-01:test2\n2022-03-01:test3\n"); + assert(memcmp(buf,got,50)==0); + + assert(fclose(src)==0); + assert(fclose(to)==0); + assert(remove(tempfile3)==0); + + reset_env(); +} + +static void copy_next_event_basic_test() { + FILE *src, *to; + char buf[EVENT_SERIALIZE_MAX_LENGTH]; + char got[EVENT_SERIALIZE_MAX_LENGTH]; + + src = fopen(global_options.file,"w"); + assert(src!=NULL); + + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + memset(got,0,EVENT_SERIALIZE_MAX_LENGTH); + + strcpy(buf,"2022-01-01:test\n2022-02-01:test2\n2022-03-01:test3\n"); + assert(fwrite(buf,1,strlen(buf),src)==50); + + assert(fclose(src)==0); + + src = fopen(global_options.file,"r"); + assert(src!=NULL); + + char tempfile[] = "/tmp/XXXXXX"; + to = file_temp(tempfile); + assert(to!=NULL); + + assert(copy_next_event(to,src)==1); + + assert(fclose(to)==0); + to = fopen(tempfile,"r"); + assert(to!=NULL); + + assert(fread(got,1,EVENT_SERIALIZE_MAX_LENGTH,to)==16); + assert(strcmp(got,"2022-01-01:test\n")==0); + + assert(fclose(to)==0); + to = fopen(tempfile,"a"); + assert(to!=NULL); + + assert(copy_next_event(to,src)==1); + + assert(fclose(to)==0); + to = fopen(tempfile,"r"); + assert(to!=NULL); + + assert(fread(got,1,EVENT_SERIALIZE_MAX_LENGTH,to)==33); + assert(strcmp(got,"2022-01-01:test\n2022-02-01:test2\n")==0); + + assert(fclose(to)==0); + to = fopen(tempfile,"a"); + assert(to!=NULL); + + assert(copy_next_event(to,src)==1); + + assert(fclose(to)==0); + to = fopen(tempfile,"r"); + assert(to!=NULL); + + assert(fread(got,1,EVENT_SERIALIZE_MAX_LENGTH,to)==50); + assert(memcmp(buf,got,50)==0); + + assert(fclose(to)==0); + assert(fclose(src)==0); + + reset_env(); +} diff --git a/test/unit/cut.tests.c b/test/unit/cut.tests.c new file mode 100644 index 0000000..5a1569b --- /dev/null +++ b/test/unit/cut.tests.c @@ -0,0 +1,190 @@ +#include + +#include + +#include INCLUDE(CUT_SRC_FILE) + +int main(); +static void cut_basic_test(); +static void cut_correctness_test(); +static void cut_offset_correctness_test(); +static void extract_basic_test(); + +int main() { + setup_env(); + + cut_basic_test(); + cut_correctness_test(); + cut_offset_correctness_test(); + extract_basic_test(); + + clean_env(); + + return EXIT_SUCCESS; +} + +static void cut_basic_test() { + struct event ev; + FILE *src; + char buf[EVENT_SERIALIZE_MAX_LENGTH]; + char got[EVENT_SERIALIZE_MAX_LENGTH]; + + src = fopen(global_options.file,"w"); + assert(src!=NULL); + + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + memset(got,0,EVENT_SERIALIZE_MAX_LENGTH); + + strcpy(buf,"2012-01-01:test\n2012-02-01:test2\n2012-03-01:test3\n"); + assert(fwrite(buf,1,strlen(buf),src)==50); + + assert(fclose(src)==0); + + assert(1==cut(&ev,0,0)); + assert(strcmp(ev.name,"test")==0); + assert(mktime(&(ev.datetime))==1325404800); + event_free(&ev); + + assert(1==cut(&ev,0,0)); + assert(strcmp(ev.name,"test2")==0); + assert(mktime(&(ev.datetime))==1328083200); + event_free(&ev); + + assert(1==cut(&ev,0,0)); + assert(strcmp(ev.name,"test3")==0); + assert(mktime(&(ev.datetime))==1330588800); + event_free(&ev); + + reset_env(); +} + +static void cut_correctness_test() { + char buf[EVENT_SERIALIZE_MAX_LENGTH]; + struct event ev; + FILE *src; + size_t index, rounds; + time_t start; + int used[12]; + + const unsigned int start_time = 1292392480; + const unsigned int chunk_size = (1323928480 - 1292392480)/12; + + rounds = 0; + while(rounds<100) { + src = fopen(global_options.file,"w"); + assert(src!=NULL); + + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + + strcpy(buf,"2011-01-01:test1\n2011-02-01:test2\n2011-03-01:test3\n2011-04-01:test4\n2011-05-01:test5\n2011-06-01:test6\n2011-07-01:test7\n2011-08-01:test8\n2011-09-01:test9\n2011-10-01:test10\n2011-11-01:test11\n2011-12-01:test12\n"); + assert(fwrite(buf,1,strlen(buf),src)==207); + + assert(fclose(src)==0); + + memset(used,0,sizeof(int)*12); + for(size_t i=0;i<12;i++) { + index = rand()%12; + + while(used[index]!=0) { + index++; + index %= 12; + } + used[index] = 1; + + start = start_time + (index*chunk_size); + assert(1==cut(&ev,start,0)); + + sprintf(buf,"test%lu",index+1); + assert(strcmp(ev.name,buf)==0); + + event_free(&ev); + } + + reset_env(); + + rounds++; + } +} + +static void cut_offset_correctness_test() { + char buf[EVENT_SERIALIZE_MAX_LENGTH]; + struct event ev; + FILE *src; + size_t index, rounds; + int used[12]; + int expected[12]; + + rounds = 0; + while(rounds<100) { + src = fopen(global_options.file,"w"); + assert(src!=NULL); + + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + + strcpy(buf,"2009-01-01:test1\n2009-02-01:test2\n2009-03-01:test3\n2009-04-01:test4\n2009-05-01:test5\n2009-06-01:test6\n2009-07-01:test7\n2009-08-01:test8\n2009-09-01:test9\n2009-10-01:test10\n2009-11-01:test11\n2009-12-01:test12\n"); + assert(fwrite(buf,1,strlen(buf),src)==207); + + assert(fclose(src)==0); + + memset(used,0,sizeof(int)*12); + for(size_t i=0;i<12;i++) { + expected[i] = 1; + index = rand()%(12-i); + + assert(1==cut(&ev,0,index)); + + if(strcmp(ev.name,"test1")==0) { used[0] = 1; } + if(strcmp(ev.name,"test2")==0) { used[1] = 1; } + if(strcmp(ev.name,"test3")==0) { used[2] = 1; } + if(strcmp(ev.name,"test4")==0) { used[3] = 1; } + if(strcmp(ev.name,"test5")==0) { used[4] = 1; } + if(strcmp(ev.name,"test6")==0) { used[5] = 1; } + if(strcmp(ev.name,"test7")==0) { used[6] = 1; } + if(strcmp(ev.name,"test8")==0) { used[7] = 1; } + if(strcmp(ev.name,"test9")==0) { used[8] = 1; } + if(strcmp(ev.name,"test10")==0) { used[9] = 1; } + if(strcmp(ev.name,"test11")==0) { used[10] = 1; } + if(strcmp(ev.name,"test12")==0) { used[11] = 1; } + + event_free(&ev); + } + + assert(memcmp(used,expected,sizeof(int)*12)==0); + + reset_env(); + + rounds++; + } +} + +static void extract_basic_test() { + FILE *src; + char buf[EVENT_SERIALIZE_MAX_LENGTH]; + struct event ev; + + src = fopen(global_options.file,"w"); + assert(src!=NULL); + + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + + strcpy(buf,"2017-01-01:test1\n2017-02-01:test2\n2017-03-01:test3\n"); + assert(fwrite(buf,1,strlen(buf),src)==51); + + assert(fclose(src)==0); + + src = fopen(global_options.file,"r"); + assert(src!=NULL); + + assert(extract(NULL,&ev)==-1); + + assert(extract(src,&ev)==1); + + assert(strcmp(ev.name,"test1")==0); + assert(mktime(&(ev.datetime))==1483257600); + + event_free(&ev); + + assert(fclose(src)==0); + + reset_env(); +} diff --git a/test/unit/dismiss.tests.c b/test/unit/dismiss.tests.c new file mode 100644 index 0000000..08bdc14 --- /dev/null +++ b/test/unit/dismiss.tests.c @@ -0,0 +1,20 @@ +#include + +#include + +int main(); +static void dismiss_basic_test(); + +int main() { + setup_env(); + + dismiss_basic_test(); + + clean_env(); + + return EXIT_SUCCESS; +} + +static void dismiss_basic_test() { + assert(0); +} diff --git a/test/unit/event.tests.c b/test/unit/event.tests.c new file mode 100644 index 0000000..3ea0f35 --- /dev/null +++ b/test/unit/event.tests.c @@ -0,0 +1,380 @@ +#include + +int main(); +static void event_date_compare_basic_test(); +static void event_init_basic_test(); +static void event_name_set_basic_test(); +static void event_parse_basic_test(); +static void event_place_set_basic_test(); +static void event_serialize_basic_test(); +static void event_serialize_parse_exactness_test(); +static void event_time_set_basic_test(); + +int main() { + event_init_basic_test(); + + event_date_compare_basic_test(); + event_name_set_basic_test(); + event_parse_basic_test(); + event_place_set_basic_test(); + event_serialize_basic_test(); + event_serialize_parse_exactness_test(); + event_time_set_basic_test(); + + return EXIT_SUCCESS; +} + +static void event_date_compare_basic_test() { + char timebuf[40]; + struct event ev; + time_t timet; + struct tm *now, got; + + event_init(&ev); + + timet = time(NULL); + now = localtime(&timet); + + assert(strftime(timebuf,40,"%Y-%m-%d %H:%M:%S %z",now)==25); + assert(1==event_time_set(&ev,timebuf)); + + /* daylight savings time set here for some reason. + * resetting timet to be time set in ev... + */ + memcpy(&got,&(ev.datetime),sizeof(struct tm)); + timet = mktime(&got); + + timet -= 1000; + assert(1==event_date_compare(&ev,timet)); + + timet += 2000; + assert(-1==event_date_compare(&ev,timet)); + + timet -= 1000; + assert(0==event_date_compare(&ev,timet)); + + event_free(&ev); +} + +static void event_init_basic_test() { + struct event ev; + struct tm dummy; + + event_init(&ev); + + assert(ev.name==NULL); + assert(ev.place==NULL); + + assert(ev.options.duration==0); + assert(ev.options.duration_period==TIME_PERIOD_NONE); + assert(ev.options.recur==0); + assert(ev.options.recur_period==TIME_PERIOD_NONE); + + memset(&dummy, 0, sizeof(struct tm)); + assert(memcmp(&(ev.datetime),&dummy,sizeof(struct tm))==0); + + event_free(&ev); +} + +static void event_name_set_basic_test() { + struct event ev; + + event_init(&ev); + + assert(ev.name==NULL); + + assert(event_name_set(&ev,"test name")==1); + assert(strcmp(ev.name,"test name")==0); + + assert(event_name_set(&ev,"test name2")==1); + assert(strcmp(ev.name,"test name2")==0); + + event_free(&ev); +} + +static void event_parse_basic_test() { + struct event ev1, ev2, ev3, ev4, ev5, ev6, ev7, ev8, ev9; + + event_init(&ev1); + char ex1[] = "2022-09-01:test event:test place"; + assert(event_parse(ex1,sizeof(ex1),&ev1)==1); + assert(ev1.options.duration==0); + assert(ev1.options.duration_period==TIME_PERIOD_NONE); + assert(ev1.options.recur==0); + assert(ev1.options.recur_period==TIME_PERIOD_NONE); + assert(strcmp(ev1.name,"test event")==0); + assert(strcmp(ev1.place,"test place")==0); + assert(mktime(&(ev1.datetime))==1662019200); + + event_init(&ev2); + char ex2[] = "2022-09-02:\"test: event2\":\"test: place2\""; + assert(event_parse(ex2,sizeof(ex2),&ev2)==1); + assert(ev2.options.duration==0); + assert(ev2.options.duration_period==TIME_PERIOD_NONE); + assert(ev2.options.recur==0); + assert(ev2.options.recur_period==TIME_PERIOD_NONE); + assert(strcmp(ev2.name,"test: event2")==0); + assert(strcmp(ev2.place,"test: place2")==0); + assert(mktime(&(ev2.datetime))==1662105600); + + event_init(&ev3); + char ex3[] = "2022-09-03 09:30:00:test event3:test place3"; + assert(event_parse(ex3,sizeof(ex3),&ev3)==1); + assert(ev3.options.duration==0); + assert(ev3.options.duration_period==TIME_PERIOD_NONE); + assert(ev3.options.recur==0); + assert(ev3.options.recur_period==TIME_PERIOD_NONE); + assert(strcmp(ev3.name,"test event3")==0); + assert(strcmp(ev3.place,"test place3")==0); + assert(mktime(&(ev3.datetime))==1662226200); + + event_init(&ev4); + char ex4[] = "2022-09-04 09:30:00 -0700:test event4:test place4"; + assert(event_parse(ex4,sizeof(ex4),&ev4)==1); + assert(ev4.options.duration==0); + assert(ev4.options.duration_period==TIME_PERIOD_NONE); + assert(ev4.options.recur==0); + assert(ev4.options.recur_period==TIME_PERIOD_NONE); + assert(strcmp(ev4.name,"test event4")==0); + assert(strcmp(ev4.place,"test place4")==0); + assert(mktime(&(ev4.datetime))==1662312600); + + event_init(&ev5); + char ex5[] = "[recur:1M]2022-08-04:test event5:test place5"; + assert(event_parse(ex5,sizeof(ex5),&ev5)==1); + assert(ev5.options.duration==0); + assert(ev5.options.duration_period==TIME_PERIOD_NONE); + assert(ev5.options.recur==1); + assert(ev5.options.recur_period==TIME_PERIOD_MONTH); + assert(strcmp(ev5.name,"test event5")==0); + assert(strcmp(ev5.place,"test place5")==0); + assert(mktime(&(ev5.datetime))==1659600000); + + event_init(&ev6); + char ex6[] = "[duration:1W]2022-06-05 09:30:00 -0700:test event6:test place6"; + assert(event_parse(ex6,sizeof(ex6),&ev6)==1); + assert(ev6.options.duration==1); + assert(ev6.options.duration_period==TIME_PERIOD_WEEK); + assert(ev6.options.recur==0); + assert(ev6.options.recur_period==TIME_PERIOD_NONE); + assert(strcmp(ev6.name,"test event6")==0); + assert(strcmp(ev6.place,"test place6")==0); + assert(mktime(&(ev6.datetime))==1654450200); + + event_init(&ev7); + char ex7[] = "[recur:1W,duration:30m]2022-10-01 09:30:00 -0700:test event7:test place7"; + assert(event_parse(ex7,sizeof(ex7),&ev7)==1); + assert(ev7.options.duration==30); + assert(ev7.options.duration_period==TIME_PERIOD_MINUTE); + assert(ev7.options.recur==1); + assert(ev7.options.recur_period==TIME_PERIOD_WEEK); + assert(strcmp(ev7.name,"test event7")==0); + assert(strcmp(ev7.place,"test place7")==0); + assert(mktime(&(ev7.datetime))==1664645400); + + event_init(&ev8); + char ex8[] = "[duration:30m,recur:1W]2022-10-01 09:30:00 -0700:test event8:test place8"; + assert(event_parse(ex8,sizeof(ex8),&ev8)==1); + assert(ev8.options.duration==30); + assert(ev8.options.duration_period==TIME_PERIOD_MINUTE); + assert(ev8.options.recur==1); + assert(ev8.options.recur_period==TIME_PERIOD_WEEK); + assert(strcmp(ev8.name,"test event8")==0); + assert(strcmp(ev8.place,"test place8")==0); + assert(mktime(&(ev8.datetime))==1664645400); + + event_init(&ev9); + char ex9[] = "2022-08-01:test"; + assert(event_parse(ex9,sizeof(ex9),&ev9)==1); + assert(ev9.options.duration==0); + assert(ev9.options.duration_period==TIME_PERIOD_NONE); + assert(ev9.options.recur==0); + assert(ev9.options.recur_period==TIME_PERIOD_NONE); + assert(strcmp(ev9.name,"test")==0); + assert(NULL==ev9.place); + assert(mktime(&(ev9.datetime))==1659340800); + + event_free(&ev1); + event_free(&ev2); + event_free(&ev3); + event_free(&ev4); + event_free(&ev5); + event_free(&ev6); + event_free(&ev7); + event_free(&ev8); + event_free(&ev9); +} + +static void event_place_set_basic_test() { + struct event ev; + + event_init(&ev); + + assert(ev.place==NULL); + + assert(event_place_set(&ev,"test place")==1); + assert(strcmp(ev.place,"test place")==0); + + assert(event_place_set(&ev,"test name2")==1); + assert(strcmp(ev.place,"test name2")==0); + + event_free(&ev); +} + +static void event_serialize_basic_test() { + char buf[EVENT_SERIALIZE_MAX_LENGTH]; + struct event ev; + + event_init(&ev); + + assert(1==event_time_set(&ev,"2022-07-01 08:01:15 -0100")); + assert(-1==event_serialize(buf,EVENT_SERIALIZE_MAX_LENGTH,&ev)); + + assert(1==event_name_set(&ev,"event")); + assert(1==event_serialize(buf,EVENT_SERIALIZE_MAX_LENGTH,&ev)); + assert(strcmp(buf,"2022-07-01 08:01:15 -0100:event\n")==0); + + event_init(&ev); + assert(1==event_time_set(&ev,"2022-07-01 08:01:15 -0100")); + assert(1==event_name_set(&ev,"event")); + assert(1==event_place_set(&ev,"place")); + assert(1==event_serialize(buf,EVENT_SERIALIZE_MAX_LENGTH,&ev)); + assert(strcmp(buf,"2022-07-01 08:01:15 -0100:event:place\n")==0); + + event_init(&ev); + assert(1==event_time_set(&ev,"2022-07-01 08:01:15 -0100")); + assert(1==event_name_set(&ev,"event: what?")); + assert(1==event_place_set(&ev,"place: here")); + assert(1==event_serialize(buf,EVENT_SERIALIZE_MAX_LENGTH,&ev)); + assert(strcmp(buf,"2022-07-01 08:01:15 -0100:\"event: what?\":\"place: here\"\n")==0); + + event_init(&ev); + assert(1==event_time_set(&ev,"2022-06-21")); + assert(1==event_name_set(&ev,"event")); + assert(1==event_place_set(&ev,"place")); + assert(1==event_serialize(buf,EVENT_SERIALIZE_MAX_LENGTH,&ev)); + assert(strcmp(buf,"2022-06-21:event:place\n")==0); + + event_init(&ev); + assert(1==event_time_set(&ev,"2022-06-21")); + assert(1==event_name_set(&ev,"event")); + assert(1==event_place_set(&ev,"place")); + ev.options.duration = 30; + ev.options.duration_period = TIME_PERIOD_DAY; + assert(1==event_serialize(buf,EVENT_SERIALIZE_MAX_LENGTH,&ev)); + assert(strcmp(buf,"[duration:30D]2022-06-21:event:place\n")==0); + + event_init(&ev); + assert(1==event_time_set(&ev,"2022-06-21")); + assert(1==event_name_set(&ev,"event")); + assert(1==event_place_set(&ev,"place")); + ev.options.duration = 30; + ev.options.duration_period = TIME_PERIOD_DAY; + ev.options.recur = 1; + ev.options.recur_period = TIME_PERIOD_YEAR; + assert(1==event_serialize(buf,EVENT_SERIALIZE_MAX_LENGTH,&ev)); + assert(strcmp(buf,"[duration:30D,recur:1Y]2022-06-21:event:place\n")==0); +} + +static void event_serialize_parse_exactness_test() { + struct event ev; + + const char expected[] = "[duration:30D,recur:1Y]2000-01-01:event:test palaaec\n"; + char buf[EVENT_SERIALIZE_MAX_LENGTH]; + + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + event_init(&ev); + + assert(53==strlen(expected)); + strcpy(buf,expected); + + assert(1==event_parse(buf,EVENT_SERIALIZE_MAX_LENGTH,&ev)); + + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + assert(1==event_serialize(buf,EVENT_SERIALIZE_MAX_LENGTH,&ev)); + + assert(memcmp(expected,buf,53)==0); +} + +static void event_time_set_basic_test() { + struct event ev; + + event_init(&ev); + + assert(-1==event_time_set(&ev,"nota date")); + + assert(-1==event_time_set(&ev,"99")); + assert(-1==event_time_set(&ev,"99-02")); + + assert(1==event_time_set(&ev,"1999-02-28")); + assert(ev.datetime.tm_sec==0); + assert(ev.datetime.tm_min==0); + assert(ev.datetime.tm_hour==0); + assert(ev.datetime.tm_mday==28); + assert(ev.datetime.tm_mon==1); + assert(ev.datetime.tm_year==99); + assert(ev.datetime.tm_wday==0); + assert(ev.datetime.tm_yday==58); + assert(ev.datetime.tm_isdst==0); + assert(ev.datetime.__tm_gmtoff==0); + + assert(1==event_time_set(&ev,"1997-01-28 not a time")); + assert(ev.datetime.tm_sec==0); + assert(ev.datetime.tm_min==0); + assert(ev.datetime.tm_hour==0); + assert(ev.datetime.tm_mday==28); + assert(ev.datetime.tm_mon==0); + assert(ev.datetime.tm_year==97); + assert(ev.datetime.tm_wday==2); + assert(ev.datetime.tm_yday==27); + assert(ev.datetime.tm_isdst==0); + assert(ev.datetime.__tm_gmtoff==0); + + assert(1==event_time_set(&ev,"2002-05-24 121212")); + assert(ev.datetime.tm_sec==0); + assert(ev.datetime.tm_min==0); + assert(ev.datetime.tm_hour==12); + assert(ev.datetime.tm_mday==24); + assert(ev.datetime.tm_mon==4); + assert(ev.datetime.tm_year==102); + assert(ev.datetime.tm_wday==5); + assert(ev.datetime.tm_yday==143); + assert(ev.datetime.tm_isdst==0); + assert(ev.datetime.__tm_gmtoff==0); + + assert(1==event_time_set(&ev,"2012-12-22 12:1212")); + assert(ev.datetime.tm_sec==0); + assert(ev.datetime.tm_min==12); + assert(ev.datetime.tm_hour==12); + assert(ev.datetime.tm_mday==22); + assert(ev.datetime.tm_mon==11); + assert(ev.datetime.tm_year==112); + assert(ev.datetime.tm_wday==6); + assert(ev.datetime.tm_yday==356); + assert(ev.datetime.tm_isdst==0); + assert(ev.datetime.__tm_gmtoff==0); + + assert(1==event_time_set(&ev,"2022-01-24 09:19:55")); + assert(ev.datetime.tm_sec==55); + assert(ev.datetime.tm_min==19); + assert(ev.datetime.tm_hour==9); + assert(ev.datetime.tm_mday==24); + assert(ev.datetime.tm_mon==0); + assert(ev.datetime.tm_year==122); + assert(ev.datetime.tm_wday==1); + assert(ev.datetime.tm_yday==23); + assert(ev.datetime.tm_isdst==0); + assert(ev.datetime.__tm_gmtoff==0); + + assert(1==event_time_set(&ev,"2009-01-03 13:25:25 -0800")); + assert(ev.datetime.tm_sec==25); + assert(ev.datetime.tm_min==25); + assert(ev.datetime.tm_hour==13); + assert(ev.datetime.tm_mday==3); + assert(ev.datetime.tm_mon==0); + assert(ev.datetime.tm_year==109); + assert(ev.datetime.tm_wday==6); + assert(ev.datetime.tm_yday==2); + assert(ev.datetime.tm_isdst==0); + assert(ev.datetime.__tm_gmtoff==-28800); +} diff --git a/test/unit/file.tests.c b/test/unit/file.tests.c new file mode 100644 index 0000000..aafb687 --- /dev/null +++ b/test/unit/file.tests.c @@ -0,0 +1,198 @@ +#include + +#include + +int main(); +static void file_line_next_basic_test(); +static void file_move_basic_test(); +static void file_open_basic_test(); +static void file_pipe_basic_test(); +static void file_temp_basic_test(); + +int main() { + setup_env(); + + file_temp_basic_test(); + + file_line_next_basic_test(); + file_move_basic_test(); + file_pipe_basic_test(); + + /* must be last because of freopen of stdin */ + file_open_basic_test(); + + clean_env(); + + return EXIT_SUCCESS; +} + +static void file_open_basic_test() { + FILE *fp; + + char tempfile[] = "/tmp/XXXXXX"; + fp = file_temp(tempfile); + assert(fp!=NULL); + fclose(fp); + + stdin = freopen(tempfile,"r+",stdin); + assert(stdin!=NULL); + + assert(fputc('n',stdin)=='n'); + rewind(stdin); + assert(NULL==file_open()); + + rewind(stdin); + assert(fputc('l',stdin)=='l'); + rewind(stdin); + assert(NULL==file_open()); + + rewind(stdin); + assert(fputc('y',stdin)=='y'); + rewind(stdin); + assert(NULL==file_open()); + + rewind(stdin); + assert(fputc('Y',stdin)=='Y'); + rewind(stdin); + fp = file_open(); + assert(fp!=NULL); + + fclose(fp); + + reset_env(); +} + +static void file_line_next_basic_test() { + FILE *fp; + char buf[100]; + size_t i; + + char tempfile[] = "/tmp/XXXXXX"; + + fp = file_temp(tempfile); + assert(fp!=NULL); + + strcpy(buf,"this is a test\nline number 2\nline 3????\\n what?"); + i = strlen(buf); + + assert(fwrite(buf,1,i,fp)==i); + + assert(fclose(fp)==0); + + fp = fopen(tempfile,"r"); + assert(fp!=NULL); + + memset(buf, 0, 100); + + assert(file_line_next(buf,100,fp)==15); + assert(strcmp(buf,"this is a test\n")==0); + + assert(file_line_next(buf,100,fp)==14); + assert(strcmp(buf,"line number 2\n")==0); + + assert(file_line_next(buf,100,fp)==18); + assert(strcmp(buf,"line 3????\\n what?")==0); + + assert(fclose(fp)==0); + + assert(remove(tempfile)==0); +} + +static void file_move_basic_test() { + FILE *fp; + char buf[100]; + char got[100]; + size_t i; + + char template[] = "/tmp/XXXXXX"; + + fp = file_temp(template); + assert(fp!=NULL); + + memset(buf,0,100); + + char test_string[] = "asldkfjasoildfjoisajdfoiasjdfiojasoidfjoisadjfioasdjfoajsdoifja"; + + strcpy(buf,test_string); + i = strlen(buf); + assert(i==fwrite(buf,1,i,fp)); + + assert(fclose(fp)==0); + + assert(1==file_move(template)); + + assert(fopen(template,"r")==NULL); + + fp = fopen(global_options.file,"r"); + assert(fp!=NULL); + + memset(got,0,100); + assert(i==fread(got,1,100,fp)); + + assert(memcmp(buf,got,i)==0); + + assert(fclose(fp)==0); + + reset_env(); +} + +static void file_pipe_basic_test() { + FILE *fp1, *fp2; + char buf[100]; + char got[100]; + size_t i; + + char file1[] = "/tmp/XXXXXX"; + char file2[] = "/tmp/XXXXXX"; + + fp1 = file_temp(file1); + assert(fp1!=NULL); + + fp2 = file_temp(file2); + assert(fp2!=NULL); + + memset(buf, 0, 100); + memset(got, 0, 100); + + strcpy(buf,"alksdjflaksjfdlkasjdfkljasdlkfjaskldfjlkasdjfklasjdfkljaslkdfjasd"); + + i = strlen(buf); + assert(i==fwrite(buf,1,i,fp2)); + + assert(fclose(fp2)==0); + fp2 = fopen(file2,"r"); + assert(fp2!=NULL); + + assert(fseek(fp2,20,SEEK_SET)==0); + + assert(file_pipe(fp1,fp2)==1); + + assert(fclose(fp1)==0); + fp1 = fopen(file1,"r"); + assert(fp1!=NULL); + + assert((i-20)==fread(got,1,i,fp1)); + + assert(feof(fp1)!=0); + + assert(memcmp(&(buf[20]),got,(i-20))==0); + + assert(fclose(fp1)==0); + assert(fclose(fp2)==0); + + assert(remove(file1)==0); + assert(remove(file2)==0); +} + +static void file_temp_basic_test() { + FILE *fp; + + char template[] = "/tmp/XXXXXX"; + + fp = file_temp(template); + assert(fp!=NULL); + + assert(fclose(fp)==0); + + assert(remove(template)==0); +} diff --git a/test/unit/ls.tests.c b/test/unit/ls.tests.c new file mode 100644 index 0000000..ec2b828 --- /dev/null +++ b/test/unit/ls.tests.c @@ -0,0 +1,9 @@ +#include + +#include + +int main(); + +int main() { + return EXIT_SUCCESS; +} diff --git a/test/unit/opt.tests.c b/test/unit/opt.tests.c new file mode 100644 index 0000000..24e8110 --- /dev/null +++ b/test/unit/opt.tests.c @@ -0,0 +1,113 @@ +#include + +#include + +int main(); +static void opt_duration_set_basic_test(); +static void opt_event_filter_date_set_basic_test(); +static void opt_event_filter_range_set_basic_test(); +static void opt_event_filter_set_basic_test(); +static void opt_recur_set_basic_test(); + +int main() { + setup_env(); + + opt_duration_set_basic_test(); + opt_event_filter_date_set_basic_test(); + opt_event_filter_range_set_basic_test(); + opt_event_filter_set_basic_test(); + opt_recur_set_basic_test(); + + clean_env(); + + return EXIT_SUCCESS; +} + +static void opt_duration_set_basic_test() { + assert(-1==opt_duration_set(NULL)); + + assert(-1==opt_duration_set("notdsoifjaoisdjfoiasdj")); + assert(global_options.event_options.duration==0); + assert(global_options.event_options.duration_period==TIME_PERIOD_NONE); + + assert(-1==opt_duration_set("1u")); + assert(global_options.event_options.duration==0); + assert(global_options.event_options.duration_period==TIME_PERIOD_NONE); + + assert(1==opt_duration_set("1Y")); + assert(global_options.event_options.duration==1); + assert(global_options.event_options.duration_period==TIME_PERIOD_YEAR); + + reset_env(); +} + +static void opt_event_filter_date_set_basic_test() { + assert(-1==opt_event_filter_date_set(&(global_options.event_filter.start),"notadate")); + + assert(1==opt_event_filter_date_set(&(global_options.event_filter.start),"2009-01-03")); + assert(global_options.event_filter.start==1230969600); + + assert(1==opt_event_filter_date_set(&(global_options.event_filter.end),"2022-08-22")); + assert(global_options.event_filter.end==1661155200); + + reset_env(); +} + +static void opt_event_filter_range_set_basic_test() { + assert(-1==opt_event_filter_range_set("not expected","1W")); + + assert(-1==opt_event_filter_range_set("upcoming","notvalidformat")); + + assert(1==opt_event_filter_range_set("upcoming","1s")); + assert((global_options.event_filter.end - global_options.event_filter.start)<2); + + assert(1==opt_event_filter_range_set("upcoming","1m")); + assert((global_options.event_filter.end - global_options.event_filter.start)<(60+1)); + + assert(1==opt_event_filter_range_set("upcoming","1h")); + assert((global_options.event_filter.end - global_options.event_filter.start)<(60*60+1)); + + assert(1==opt_event_filter_range_set("upcoming","1D")); + assert((global_options.event_filter.end - global_options.event_filter.start)<(60*60*24+1)); + + assert(1==opt_event_filter_range_set("upcoming","1W")); + assert((global_options.event_filter.end - global_options.event_filter.start)<(60*60*24*7+1)); + + /* month and year and left out since they're not consistent */ + assert(1==opt_event_filter_range_set("upcoming","1M")); + assert(1==opt_event_filter_range_set("upcoming","1Y")); + + reset_env(); +} + +static void opt_event_filter_set_basic_test() { + assert(-1==opt_event_filter_set("not expected")); + + assert(1==opt_event_filter_set("all")); + assert(global_options.event_filter.start==0); + assert(global_options.event_filter.end==INT_MAX); + + assert(1==opt_event_filter_set("overdue")); + assert(global_options.event_filter.start==0); + assert((time(NULL) - global_options.event_filter.end)<1); + + reset_env(); +} + +static void opt_recur_set_basic_test() { + assert(-1==opt_recur_set(NULL)); + + assert(-1==opt_recur_set("notdsoifjaoisdjfoiasdj")); + assert(global_options.event_options.recur==0); + assert(global_options.event_options.recur_period==TIME_PERIOD_NONE); + + assert(-1==opt_recur_set("1u")); + assert(global_options.event_options.recur==0); + assert(global_options.event_options.recur_period==TIME_PERIOD_NONE); + + assert(1==opt_recur_set("1Y")); + assert(global_options.event_options.recur==1); + assert(global_options.event_options.recur_period==TIME_PERIOD_YEAR); + + reset_env(); +} diff --git a/test/unit/postpone.tests.c b/test/unit/postpone.tests.c new file mode 100644 index 0000000..f54d95e --- /dev/null +++ b/test/unit/postpone.tests.c @@ -0,0 +1,185 @@ +#include + +#include + +#include INCLUDE(POSTPONE_SRC_FILE) + +int optind; + +int main(); +static void handle_args_basic_test(); +static void postpone_basic_test(); +static void postpone_event_basic_test(); + +int main() { + setup_env(); + + handle_args_basic_test(); + postpone_event_basic_test(); + + postpone_basic_test(); + + clean_env(); + + return EXIT_SUCCESS; +} + +static void handle_args_basic_test() { + unsigned long int offset; + time_t start_time; + + char *args[] = { + "ev", + "postpone", + "0", + NULL + }; + + optind = 1; + assert(handle_args(1,args,&offset,&start_time)==-1); + + assert(handle_args(2,args,&offset,&start_time)==-1); + + assert(handle_args(3,args,&offset,&start_time)==1); + assert(offset==0); + assert(start_time==0); + + char *args_with_date[] = { + "ev", + "postpone", + "2020-03-25", + "1", + NULL + }; + + assert(handle_args(4,args_with_date,&offset,&start_time)==1); + assert(offset==1); + assert(start_time==1585123200); + + char *args_with_date_and_postpone[] = { + "ev", + "postpone", + "2020-03-25", + "2", + "2W", + NULL + }; + + assert(handle_args(5,args_with_date_and_postpone,&offset,&start_time)==1); + assert(offset==2); + assert(start_time==1585123200); + + assert(strcmp(postpone_offset_string,"2W")==0); + + free(postpone_offset_string); + postpone_offset_string = NULL; +} + +static void postpone_basic_test() { + char buf[EVENT_SERIALIZE_MAX_LENGTH]; + char got[EVENT_SERIALIZE_MAX_LENGTH]; + FILE *src; + + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + memset(got,0,EVENT_SERIALIZE_MAX_LENGTH); + + src = fopen(global_options.file,"w"); + assert(src!=NULL); + + strcpy(buf,"2022-08-01:test\n"); + assert(fwrite(buf,1,strlen(buf),src)==16); + + assert(fclose(src)==0); + + char *args[] = { + "ev", + "postpone", + "0", + NULL + }; + + assert(EXIT_SUCCESS==postpone(3,args)); + + src = fopen(global_options.file,"r"); + assert(src!=NULL); + + assert(fread(got,1,EVENT_SERIALIZE_MAX_LENGTH,src)==16); + assert(memcmp(got,"2022-08-02:test\n",16)==0); + + assert(fclose(src)==0); + + reset_env(); +} + +static void postpone_event_basic_test() { + struct event ev; + struct tm expected; + char buf[EVENT_SERIALIZE_MAX_LENGTH]; + + event_init(&ev); + + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + + assert(event_name_set(&ev,"test")==1); + assert(event_time_set(&ev,"2020-01-01")==1); + + postpone_offset_string = NULL; + assert(postpone_event(&ev)==1); + + memcpy(&expected,&(ev.datetime),sizeof(struct tm)); + assert(mktime(&expected)==1577952000); + assert(strftime(buf,EVENT_SERIALIZE_MAX_LENGTH,"%Y-%m-%d",&(ev.datetime))==10); + assert(strcmp(buf,"2020-01-02")==0); + + postpone_offset_string = strdup("1W"); + assert(postpone_event(&ev)==1); + + memcpy(&expected,&(ev.datetime),sizeof(struct tm)); + assert(mktime(&expected)==1578556800); + assert(strftime(buf,EVENT_SERIALIZE_MAX_LENGTH,"%Y-%m-%d",&(ev.datetime))==10); + assert(strcmp(buf,"2020-01-09")==0); + + free(postpone_offset_string); + postpone_offset_string = strdup("1M"); + assert(postpone_event(&ev)==1); + + memcpy(&expected,&(ev.datetime),sizeof(struct tm)); + assert(mktime(&expected)==1581235200); + assert(strftime(buf,EVENT_SERIALIZE_MAX_LENGTH,"%Y-%m-%d",&(ev.datetime))==10); + assert(strcmp(buf,"2020-02-09")==0); + + free(postpone_offset_string); + postpone_offset_string = strdup("1Y"); + assert(postpone_event(&ev)==1); + + memcpy(&expected,&(ev.datetime),sizeof(struct tm)); + assert(mktime(&expected)==1612857600); + assert(strftime(buf,EVENT_SERIALIZE_MAX_LENGTH,"%Y-%m-%d",&(ev.datetime))==10); + assert(strcmp(buf,"2021-02-09")==0); + + free(postpone_offset_string); + postpone_offset_string = strdup("1h"); + assert(postpone_event(&ev)==1); + + memcpy(&expected,&(ev.datetime),sizeof(struct tm)); + assert(mktime(&expected)==1612861200); + + free(postpone_offset_string); + postpone_offset_string = strdup("1m"); + assert(postpone_event(&ev)==1); + + memcpy(&expected,&(ev.datetime),sizeof(struct tm)); + assert(mktime(&expected)==1612861260); + + free(postpone_offset_string); + postpone_offset_string = strdup("1s"); + assert(postpone_event(&ev)==1); + + memcpy(&expected,&(ev.datetime),sizeof(struct tm)); + assert(mktime(&expected)==1612861261); + + free(postpone_offset_string); + postpone_offset_string = NULL; + + event_free(&ev); +} diff --git a/test/unit/prune.tests.c b/test/unit/prune.tests.c new file mode 100644 index 0000000..9d91c58 --- /dev/null +++ b/test/unit/prune.tests.c @@ -0,0 +1,71 @@ +#include + +#include + +int main(); +static void prune_basic_test(); + +int main() { + setup_env(); + + prune_basic_test(); + + clean_env(); + + return EXIT_SUCCESS; +} + +static void prune_basic_test() { + char buf[EVENT_SERIALIZE_MAX_LENGTH]; + char got[EVENT_SERIALIZE_MAX_LENGTH]; + char timebuf[40]; + struct tm curr, *now; + time_t now_timet; + FILE *src; + + now_timet = time(NULL); + now = localtime(&now_timet); + + src = fopen(global_options.file,"w"); + assert(src!=NULL); + + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + memset(timebuf,0,40); + + memcpy(&curr,now,sizeof(struct tm)); + curr.tm_year -= 1; + + assert(strftime(timebuf,20,"%Y-%m-%d",&curr)==10); + for(size_t i=0;i<10;i++) { + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + snprintf(buf,EVENT_SERIALIZE_MAX_LENGTH,"%s:test%lu\n",timebuf,i); + + assert(fwrite(buf,1,strlen(buf),src)==17); + } + + curr.tm_year += 2; + assert(strftime(timebuf,20,"%Y-%m-%d",&curr)==10); + for(size_t i=0;i<10;i++) { + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + snprintf(buf,EVENT_SERIALIZE_MAX_LENGTH,"%s:test%lu\n",timebuf,i); + + assert(fwrite(buf,1,strlen(buf),src)==17); + } + + assert(fclose(src)==0); + + assert(prune()==EXIT_SUCCESS); + + src = fopen(global_options.file,"r"); + assert(src!=NULL); + + assert(fread(got,1,EVENT_SERIALIZE_MAX_LENGTH,src)==170); + + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + sprintf(buf,"%s:test0\n%s:test1\n%s:test2\n%s:test3\n%s:test4\n%s:test5\n%s:test6\n%s:test7\n%s:test8\n%s:test9\n",timebuf,timebuf,timebuf,timebuf,timebuf,timebuf,timebuf,timebuf,timebuf,timebuf); + assert(memcmp(got,buf,170)==0); + + assert(fclose(src)==0); + + reset_env(); +} diff --git a/test/unit/rm.tests.c b/test/unit/rm.tests.c new file mode 100644 index 0000000..1ceae1b --- /dev/null +++ b/test/unit/rm.tests.c @@ -0,0 +1,169 @@ +#include + +#include + +#include INCLUDE(RM_SRC_FILE) + +int main(); +static void check_need_readd_basic_test(); +static void handle_args_basic_test(); +static void rm_basic_test(); +static void rm_dismiss_basic_test(); + +int main() { + setup_env(); + + handle_args_basic_test(); + + rm_basic_test(); + check_need_readd_basic_test(); + rm_dismiss_basic_test(); + + clean_env(); + + return EXIT_SUCCESS; +} + +static void check_need_readd_basic_test() { + char buf[EVENT_SERIALIZE_MAX_LENGTH]; + struct event ev; + FILE *src; + + event_init(&ev); + + src = fopen(global_options.file,"w"); + assert(src!=NULL); + + assert(fclose(src)==0); + + assert(event_name_set(&ev,"test")==1); + assert(event_time_set(&ev,"2000-01-01")==1); + + assert(1==check_need_readd(&ev)); + + src = fopen(global_options.file,"r"); + assert(src!=NULL); + + assert(fread(buf,1,EVENT_SERIALIZE_MAX_LENGTH,src)==0); + assert(feof(src)!=0); + assert(fclose(src)==0); + + ev.options.recur = 1; + ev.options.recur_period = TIME_PERIOD_YEAR; + + assert(1==check_need_readd(&ev)); + + src = fopen(global_options.file,"r"); + assert(src!=NULL); + + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + + assert(fread(buf,1,EVENT_SERIALIZE_MAX_LENGTH,src)==26); + assert(memcmp(buf,"[recur:1Y]2001-01-01:test\n",26)==0); + + assert(fclose(src)==0); + + reset_env(); +} + +static void handle_args_basic_test() { + unsigned long int offset; + time_t start_time; + + char *args[] = { + "ev", + "rm", + "1", + NULL + }; + + assert(-1==handle_args(2,args,&offset,&start_time)); + assert(1==handle_args(3,args,&offset,&start_time)); + assert(offset==1); + assert(start_time==0); + + char *args_with_date[] = { + "ev", + "rm", + "2022-09-01", + "2", + NULL + }; + + assert(1==handle_args(4,args_with_date,&offset,&start_time)); + assert(offset==2); + assert(start_time==1662019200); +} + +static void rm_basic_test() { + char buf[EVENT_SERIALIZE_MAX_LENGTH]; + char got[EVENT_SERIALIZE_MAX_LENGTH]; + FILE *src; + + char *args[] = { + "ev", + "rm", + "0", + NULL + }; + + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + memset(got,0,EVENT_SERIALIZE_MAX_LENGTH); + + + src = fopen(global_options.file,"w"); + assert(src!=NULL); + + strcpy(buf,"2005-06-01:test1\n2006-07-02:test2\n2008-07-09:test3\n"); + assert(fwrite(buf,1,strlen(buf),src)==51); + assert(fclose(src)==0); + + assert(EXIT_FAILURE==rm(2,args,0)); + + assert(EXIT_SUCCESS==rm(3,args,0)); + + src = fopen(global_options.file,"r"); + assert(src!=NULL); + + assert(fread(got,1,EVENT_SERIALIZE_MAX_LENGTH,src)==34); + assert(memcmp(got,"2006-07-02:test2\n2008-07-09:test3\n",34)==0); + + assert(fclose(src)==0); + + reset_env(); +} + +static void rm_dismiss_basic_test() { + char buf[EVENT_SERIALIZE_MAX_LENGTH]; + char got[EVENT_SERIALIZE_MAX_LENGTH]; + FILE *src; + + char *args[] = { + "ev", + "dismiss", + "0", + NULL + }; + + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + memset(got,0,EVENT_SERIALIZE_MAX_LENGTH); + + src = fopen(global_options.file,"w"); + assert(src!=NULL); + + strcpy(buf,"1984-05-10:test1\n1988-11-12:test2\n1992-02-15:test3\n"); + assert(fwrite(buf,1,strlen(buf),src)==51); + assert(fclose(src)==0); + + assert(EXIT_SUCCESS==rm(3,args,0)); + + src = fopen(global_options.file,"r"); + assert(src!=NULL); + + assert(fread(got,1,EVENT_SERIALIZE_MAX_LENGTH,src)==34); + assert(memcmp(got,"1988-11-12:test2\n1992-02-15:test3\n",34)==0); + + assert(fclose(src)==0); + + reset_env(); +} diff --git a/test/unit/seek.tests.c b/test/unit/seek.tests.c new file mode 100644 index 0000000..9f5f610 --- /dev/null +++ b/test/unit/seek.tests.c @@ -0,0 +1,68 @@ +#include + +#include + +int main(); +static void seek_basic_test(); + +int main() { + setup_env(); + + seek_basic_test(); + + clean_env(); + + return EXIT_SUCCESS; +} + +static void seek_basic_test() { + FILE *src; + char buf[EVENT_SERIALIZE_MAX_LENGTH]; + char got[EVENT_SERIALIZE_MAX_LENGTH]; + + src = fopen(global_options.file,"w"); + assert(src!=NULL); + + memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); + memset(got,0,EVENT_SERIALIZE_MAX_LENGTH); + + strcpy(buf,"2008-02-15:test1\n2008-05-01:test2\n2008-10-01:test3\n"); + + assert(fwrite(buf,1,strlen(buf),src)==51); + assert(fclose(src)==0); + + src = fopen(global_options.file,"r"); + assert(src!=NULL); + + assert(-1==seek(NULL,0)); + + assert(1==seek(src,0)); + + assert(fread(got,1,EVENT_SERIALIZE_MAX_LENGTH,src)==51); + assert(strcmp(got,"2008-02-15:test1\n2008-05-01:test2\n2008-10-01:test3\n")==0); + + rewind(src); + + assert(1==seek(src,1199201413)); + + assert(fread(got,1,EVENT_SERIALIZE_MAX_LENGTH,src)==51); + assert(memcmp(got,"2008-02-15:test1\n2008-05-01:test2\n2008-10-01:test3\n",51)==0); + + rewind(src); + + assert(1==seek(src,1204385413)); + + assert(fread(got,1,EVENT_SERIALIZE_MAX_LENGTH,src)==34); + assert(memcmp(got,"2008-05-01:test2\n2008-10-01:test3\n",34)==0); + + rewind(src); + + assert(1==seek(src,1214926213)); + + assert(fread(got,1,EVENT_SERIALIZE_MAX_LENGTH,src)==17); + assert(memcmp(got,"2008-10-01:test3\n",17)==0); + + assert(fclose(src)==0); + + reset_env(); +} diff --git a/test/unit/test_macros.h b/test/unit/test_macros.h new file mode 100644 index 0000000..d3c0bee --- /dev/null +++ b/test/unit/test_macros.h @@ -0,0 +1,8 @@ +#ifndef __TEST_MACROS_H_ +#define __TEST_MACROS_H_ + +#define __include(x) #x +#define _include(x) __include(x) +#define INCLUDE(x) _include(x) + +#endif diff --git a/test/unit/test_utils.c b/test/unit/test_utils.c new file mode 100644 index 0000000..883e906 --- /dev/null +++ b/test/unit/test_utils.c @@ -0,0 +1,31 @@ +#include + +struct options global_options; + +static const char default_file[] = "/tmp/events.test"; + +void clean_env() { + reset_env(); + + free(global_options.file); +} + +void reset_env() { + if(access(default_file, F_OK)==0) { + assert(remove(default_file)==0); + } + + if(global_options.file!=NULL) { free(global_options.file); } + + opt_global_init(); + + global_options.file = strdup(default_file); + assert(global_options.file!=NULL); +} + +void setup_env() { + srand(time(NULL)); + + global_options.file = strdup(default_file); + assert(global_options.file!=NULL); +} diff --git a/test/unit/test_utils.h b/test/unit/test_utils.h new file mode 100644 index 0000000..c15d177 --- /dev/null +++ b/test/unit/test_utils.h @@ -0,0 +1,14 @@ +#ifndef __TEST_UTILS_H_ +#define __TEST_UTILS_H_ + +#include + +#include + +#include + +void clean_env(); +void reset_env(); +void setup_env(); + +#endif