Full project completed.
Added the following sub-commands:
-add
-dismiss
-ls
-rm
-postpone
-prune
Added unit tests to verify functionality.
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+#ifndef __ADD_H_
+#define __ADD_H_
+
+#include<getopt.h>
+#include<stdio.h>
+#include<stdlib.h>
+#include<string.h>
+#include<time.h>
+
+#include<copy.h>
+#include<event.h>
+#include<file.h>
+#include<opt.h>
+#include<usage.h>
+
+int add();
+int add_to_file(struct event*);
+
+#endif
--- /dev/null
+#ifndef __ARGS_H_
+#define __ARGS_H_
+
+#include<getopt.h>
+
+#include<opt.h>
+#include<usage.h>
+
+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
--- /dev/null
+#ifndef __COPY_H_
+#define __COPY_H_
+
+#include<stdio.h>
+
+#include<event.h>
+#include<file.h>
+
+int copy(FILE*,FILE*,time_t);
+int copy_next_event(FILE*,FILE*);
+
+#endif
--- /dev/null
+#ifndef __CUT_H_
+#define __CUT_H_
+
+#include<copy.h>
+#include<event.h>
+#include<file.h>
+
+int cut(struct event*, time_t, size_t);
+
+#endif
--- /dev/null
+#ifndef __DEFAULTS_H_
+#define __DEFAULTS_H_
+
+#include<stdlib.h>
+#include<string.h>
+
+#include<opt.h>
+
+#define EVENTS_FILE "events"
+
+int defaults();
+
+#endif
--- /dev/null
+#ifndef __EVENT_H_
+#define __EVENT_H_
+
+/* needed for strptime */
+#define _XOPEN_SOURCE
+#define _POSIX_C_SOURCE 200809L
+
+#include<assert.h>
+#include<errno.h>
+#include<stdlib.h>
+#include<stdio.h>
+#include<string.h>
+#include<time.h>
+
+#include<usage.h>
+
+#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
--- /dev/null
+#ifndef __FILE_H_
+#define __FILE_H_
+
+#include<stdio.h>
+#include<stdlib.h>
+
+#include<opt.h>
+
+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
--- /dev/null
+#ifndef __LS_H_
+#define __LS_H_
+
+#include<stdio.h>
+#include<stdlib.h>
+#include<time.h>
+
+#include<event.h>
+#include<file.h>
+#include<opt.h>
+
+int ls();
+
+#endif
--- /dev/null
+#ifndef __MAIN_H_
+#define __MAIN_H_
+
+#include<stdlib.h>
+
+#include<add.h>
+#include<args.h>
+#include<defaults.h>
+#include<ls.h>
+#include<opt.h>
+#include<postpone.h>
+#include<prune.h>
+#include<rm.h>
+
+int main(int,char**);
+
+#endif
--- /dev/null
+#ifndef __OPT_H_
+#define __OPT_H_
+
+#include<limits.h>
+#include<stdlib.h>
+#include<string.h>
+
+#include<event.h>
+
+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
--- /dev/null
+#ifndef __POSTPONE_H_
+#define __POSTPONE_H_
+
+#include<stdio.h>
+#include<stdlib.h>
+
+#include<add.h>
+#include<cut.h>
+#include<event.h>
+#include<usage.h>
+
+int postpone();
+
+#endif
--- /dev/null
+#ifndef __PRUNE_H_
+#define __PRUNE_H_
+
+#include<stdio.h>
+#include<stdlib.h>
+
+#include<file.h>
+#include<seek.h>
+
+int prune();
+
+#endif
--- /dev/null
+#ifndef __RM_H_
+#define __RM_H_
+
+#include<stdio.h>
+#include<stdlib.h>
+#include<time.h>
+
+#include<add.h>
+#include<cut.h>
+#include<event.h>
+#include<file.h>
+
+#define RM_FLAG_DISMISS 1
+
+int rm(int,char**,int);
+
+#endif
--- /dev/null
+#ifndef __SEEK_H_
+#define __SEEK_H_
+
+#include<event.h>
+#include<file.h>
+
+int seek(FILE*,time_t);
+
+#endif
--- /dev/null
+#ifndef __USAGE_H_
+#define __USAGE_H_
+
+#include<stdio.h>
+
+void usage();
+
+#endif
--- /dev/null
+#include<add.h>
+
+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;
+}
--- /dev/null
+#include<args.h>
+
+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;
+}
--- /dev/null
+#include<copy.h>
+
+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;
+}
--- /dev/null
+#include<cut.h>
+
+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;
+}
--- /dev/null
+#include<defaults.h>
+
+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);
+}
--- /dev/null
+#include<event.h>
+
+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_time<time) { return -1; }
+ return 0;
+}
+
+void event_free(const struct event *p) {
+ if(p->name!=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<event_time) { return -1; }
+
+ return 1;
+}
--- /dev/null
+#include<file.h>
+
+ssize_t file_line_next(char *buf, size_t n, FILE *stream) {
+ return getline(&buf,&n,stream);
+}
--- /dev/null
+#include<file.h>
+
+int file_move(const char *tempname) {
+ if(rename(tempname,global_options.file)==-1) {
+ perror("rename");
+ return -1;
+ }
+
+ return 1;
+}
--- /dev/null
+#include<file.h>
+
+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;
+}
--- /dev/null
+#include<file.h>
+
+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;
+}
--- /dev/null
+#include<file.h>
+
+FILE *file_temp(char *template) {
+ int fd = mkstemp(template);
+ if(fd<0) {
+ perror("mkstemp");
+ return NULL;
+ }
+
+ return fdopen(fd,"w");
+}
--- /dev/null
+#include<ls.h>
+
+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;
+}
--- /dev/null
+#include<main.h>
+
+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;
+ }
+}
--- /dev/null
+#include<opt.h>
+
+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;
+}
--- /dev/null
+#include<opt.h>
+
+#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;
+}
--- /dev/null
+#include<opt.h>
+
+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;
+}
--- /dev/null
+#include<opt.h>
+
+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;
+}
--- /dev/null
+#include<opt.h>
+
+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;
+}
--- /dev/null
+#include<opt.h>
+
+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;
+}
--- /dev/null
+#include<postpone.h>
+
+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;
+}
--- /dev/null
+#include<prune.h>
+
+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;
+}
--- /dev/null
+#include<rm.h>
+
+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;
+}
--- /dev/null
+#include<seek.h>
+
+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;
+}
--- /dev/null
+#include<usage.h>
+
+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;
+}
--- /dev/null
+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
--- /dev/null
+#include<test_utils.h>
+
+#include<add.h>
+
+#include<unistd.h>
+
+#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++;
+ }
+}
--- /dev/null
+#include<test_utils.h>
+
+#include<args.h>
+
+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();
+}
--- /dev/null
+#include<test_utils.h>
+
+#include<copy.h>
+
+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();
+}
--- /dev/null
+#include<test_utils.h>
+
+#include<cut.h>
+
+#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();
+}
--- /dev/null
+#include<test_utils.h>
+
+#include<rm.h>
+
+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);
+}
--- /dev/null
+#include<event.h>
+
+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);
+}
--- /dev/null
+#include<test_utils.h>
+
+#include<file.h>
+
+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);
+}
--- /dev/null
+#include<test_utils.h>
+
+#include<ls.h>
+
+int main();
+
+int main() {
+ return EXIT_SUCCESS;
+}
--- /dev/null
+#include<test_utils.h>
+
+#include<opt.h>
+
+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();
+}
--- /dev/null
+#include<test_utils.h>
+
+#include<postpone.h>
+
+#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);
+}
--- /dev/null
+#include<test_utils.h>
+
+#include<prune.h>
+
+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();
+}
--- /dev/null
+#include<test_utils.h>
+
+#include<rm.h>
+
+#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();
+}
--- /dev/null
+#include<test_utils.h>
+
+#include<seek.h>
+
+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();
+}
--- /dev/null
+#ifndef __TEST_MACROS_H_
+#define __TEST_MACROS_H_
+
+#define __include(x) #x
+#define _include(x) __include(x)
+#define INCLUDE(x) _include(x)
+
+#endif
--- /dev/null
+#include<test_utils.h>
+
+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);
+}
--- /dev/null
+#ifndef __TEST_UTILS_H_
+#define __TEST_UTILS_H_
+
+#include<unistd.h>
+
+#include<test_macros.h>
+
+#include<opt.h>
+
+void clean_env();
+void reset_env();
+void setup_env();
+
+#endif