From: alex Date: Sat, 3 Sep 2022 18:10:29 +0000 (-0700) Subject: Completed v1.0.0 functionality X-Git-Tag: v1.0.0 X-Git-Url: http://git.infiniteadaptability.org/?a=commitdiff_plain;h=df3c096a86787653778b701c44c96d4c834f1394;p=events Completed v1.0.0 functionality -Added optional arg to --upcoming (default = 1W) -changed duration to until -added man page -added some minor color/formatting to ls -added bash completions --- diff --git a/.gitignore b/.gitignore index a7921fc..24a31b4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ ev events +!completions/ev + # build objects *.o *.log diff --git a/Makefile.am b/Makefile.am index 7b8e14c..4ad2767 100644 --- a/Makefile.am +++ b/Makefile.am @@ -16,7 +16,14 @@ ev_SOURCES = \ src/copy.c \ src/cut.c \ src/defaults.c \ - src/event.c \ + src/event/free.c \ + src/event/init.c \ + src/event/name.c \ + src/event/parse.c \ + src/event/period.c \ + src/event/place.c \ + src/event/serial.c \ + src/event/time.c \ src/file/line.c \ src/file/mv.c \ src/file/open.c \ @@ -24,12 +31,12 @@ ev_SOURCES = \ 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/opt/until.c \ src/postpone.c \ src/prune.c \ src/rm.c \ @@ -53,6 +60,11 @@ ev_SOURCES += \ inc/seek.h \ inc/usage.h +man_MANS = man/ev.1 + +bashcompletiondir = $(datadir)/bash-completion/completions +dist_bashcompletion_SCRIPTS = completions/ev + SUBDIRS = . test/unit unit: diff --git a/completions/ev b/completions/ev new file mode 100644 index 0000000..96991f8 --- /dev/null +++ b/completions/ev @@ -0,0 +1,93 @@ +_ev() { + IFS=$'\n' + COMPREPLY=() + local commands=$'add\ndismiss\nls\npostpone\nprune\nrm' + + declare -A options + options[--all]="--all" + options[-a]="-a" + options[--end-date]="--end-date" + options[-e]="-e" + options[--file]="--file" + options[-f]="-f" + options[--help]="--help" + options[-h]="-h" + options[--overdue]="--overdue" + options[-o]="-o" + options[--recurring]="--recurring" + options[-r]="-r" + options[--start-date]="--start-date" + options[-s]="-s" + options[--upcoming]="--upcoming" + options[-u]="-u" + + # remove set args + for arg in "${COMP_WORDS[@]}"; do + case "$arg" in + --all) unset options[-a] ;; + --end-date) unset options[-e] ;; + --file) unset options[-f] ;; + --help) unset options[-h] ;; + --overdue) unset options[-o] ;; + --recurring) unset options[-r] ;; + --start-date) unset options[-s] ;; + --upcoming) unset options[-u] ;; + + -a) unset options[--all] ;; + -e) unset options[--end-date] ;; + -f) unset options[--file] ;; + -h) unset options[--help] ;; + -o) unset options[--overdue] ;; + -r) unset options[--recurring] ;; + -s) unset options[--start-date] ;; + -u) unset options[--upcoming] ;; + esac + unset options[$arg] + done + + opts= + for val in "${options[@]}"; do + opts+="$val" + opts+=$'\n' + done + + echo "COMP_LINE: ${COMP_LINE}" + + case "$3" in + add) + local addformat="[NAME] [PLACE] [DATE]" + compopt -o nosort + COMPREPLY=("$addformat" "") + ;; + dismiss) + local dismissformat="[INDEX]" + echo "$dismissformat" + COMPREPLY=() + ;; + ls) ;; + postpone) + local postponeformat="[DATE] [INDEX] [PERIOD]" + echo "$postponeformat" + COMPREPLY=() + ;; + prune) ;; + rm) + local rmformat="[INDEX]" + echo "$rmformat" + COMPREPLY=() + ;; + ev) + COMPREPLY=($(compgen -W "${commands}" -- "$2")) + COMPREPLY+=($(compgen -W "${opts}" -- "$2")) + ;; + -e|--end-date|-s|--start-date) + COMPREPLY=($(date +%Y-%m-%d)) + ;; + -r|--recurring|-u|--upcoming) + local periodformat="1W" + COMPREPLY=() + ;; + esac +} >> /tmp/ev.output + +complete -F _ev ev diff --git a/configure.ac b/configure.ac index 1030934..1461b9d 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ([2.69]) -AC_INIT([ev], [0.0.0]) +AC_INIT([ev], [1.0.0]) # Store build files not in main directory AC_CONFIG_AUX_DIR([build-aux]) @@ -50,14 +50,16 @@ AC_PROG_INSTALL # Checks for libraries. # Checks for header files. -AC_CHECK_HEADERS([stdlib.h string.h]) +AC_CHECK_HEADERS([limits.h stdlib.h string.h unistd.h]) # Checks for typedefs, structures, and compiler characteristics. AC_TYPE_SIZE_T +AC_TYPE_SSIZE_T # Checks for library functions. AC_FUNC_MALLOC -AC_CHECK_FUNCS([memset strdup strptime]) +AC_FUNC_MKTIME +AC_CHECK_FUNCS([memmove memset setenv strchr strdup strptime strtoul tzset]) AC_CONFIG_FILES([Makefile test/unit/Makefile]) diff --git a/inc/event.h b/inc/event.h index ef64ed3..87670d9 100644 --- a/inc/event.h +++ b/inc/event.h @@ -28,11 +28,10 @@ enum time_period { }; struct event_options { - unsigned int duration; - enum time_period duration_period; - unsigned int recur; enum time_period recur_period; + + struct tm until; }; struct event { @@ -49,15 +48,17 @@ struct event_filter { 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*); +void event_options_init(struct event_options*); enum time_period event_options_period(char); +char event_options_period_char(enum time_period); 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_time_compare(const struct tm*,time_t); +int event_time_set(struct tm*, const char*); int event_within(struct event*, struct event_filter*); #endif diff --git a/inc/opt.h b/inc/opt.h index 16f6a3a..28018e0 100644 --- a/inc/opt.h +++ b/inc/opt.h @@ -17,13 +17,13 @@ struct options { 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(); +int opt_recur_set(const char*); +int opt_until_set(const char*); #endif diff --git a/man/ev.1 b/man/ev.1 new file mode 100644 index 0000000..53c1953 --- /dev/null +++ b/man/ev.1 @@ -0,0 +1,183 @@ +.TH EV "1" "2022 September 13" "v0.0.0" "Events" + +.SH NAME +ev \- human readable events organizer + +.SH SYNOPSIS +.B ev +[ +.I OPTIONS +]... [ +.I COMMAND +] [ +.I ARGS +]... + +.SH DESCRIPTION +.B ev +is a tool for managing and organizing events in a human readable format. +.PP +It is intended to be easily parasable and able to be editted by hand. +.PP +Best used within +.BR git (1). + +If no COMMAND is specified, COMMAND defaults to ls. + +.SH COMMANDS +.IP \(bu +.B add +.IP \(bu +.B dismiss +.IP \(bu +.B ls +.IP \(bu +.B postpone +.IP \(bu +.B prune +.IP \(bu +.B rm + +.SH OPTIONS +.TP +\fB\-\-all\fR, \fB\-a\fR +show all +.TP +\fB\-\-end\-date\fR, \fB\-e\fR +ending date +.TP +\fB\-\-file\fR, \fB\-f\fR +events file location +.TP +\fB\-\-help\fR, \fB\-h\fR +show this help +.TP +\fB\-\-overdue\fR, \fB\-o\fR +show only overdue events +.TP +\fB\-\-recurring\fR, \fB\-r\fR +denote event as recurring +.TP +\fB\-\-start\-date\fR, \fB\-s\fR +starting date +.TP +\fB\-\-upcoming\fR, \fB\-u\fR +show only upcoming events + +.SH TIME PERIOD SPEC +A couple of the commands require time period specifications. A time period specification consists of an unsigned integer and then a single character. +.TP +The following characters are accepted time periods: +.IP \(bu +Y: year +.IP \(bu +M: month +.IP \(bu +W: week +.IP \(bu +D: day +.IP \(bu +h: hour +.IP \(bu +m: minute +.IP \(bu +s: second + +.SH ENVIRONMENTAL VARIABLES + +.TP +.I EVENTS_EVENT_FILTER +Overrides date range. + +.TP +.I EVENTS_FILE +Overrides events file + +.TP +.I EVENTS_RECUR +Overrides recur value + +.TP +.I EVENTS_UNTIL +Overrides event recurruing until value. + +.SH EXAMPLES +.TP +get today's and overdue events +.B ev + +.TP +get today's and overdue events using file ~/.events +.B EVENTS_FILE=~/.events ev + +.TP +prune old events +.B ev prune + +.TP +dismiss 0th event +.B ev dismiss 0 + +.TP +get events on 2022-09-01 +.B ev 2022-09-01 + +.TP +get upcoming events for the next month +.B +ev --upcoming 1M + +.TP +add recurring event +.B +ev add --recurring 1Y "Alex Birthday" "2023-01-24" + +.TP +add event with 2 day duration +.B +ev add --duration 2D "Alex Birthday" "2023-01-24" + +.TP +add event@location with time +.B +ev add "Roger Waters" "Chase Center" "2022-09-23 20:00:00" + +.TP +delete event with name 'Test Event' on 2022-09-01 +.B +ev rm "Test Event" 2022-09-01 + +.TP +deletes today's event in the 0th position +.B +ev rm 0 + +.TP +postpones event in the 1st position on 2022-09-01 by 1 day +.B +ev postpone 2022-09-01 1 1D + +.TP +postpones event in the 0th position by 1 week +.B +ev postpone 0 1W + +.TP +postpones event in the 0th position by 1 day +.B +ev postpone 0 + +.SH "SEE ALSO" +.BR git (1) + +.SH AUTHOR +.B ev +was written by +.MT alex@infiniteadaptability.org +Alex Joss +.ME . +See +.UR https://\:infiniteadaptability.org/ +infiniteadaptability.org +.UE +for more information. diff --git a/src/add.c b/src/add.c index df98e35..20a4228 100644 --- a/src/add.c +++ b/src/add.c @@ -3,6 +3,8 @@ 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*); +static int handle_args(int,char**,struct event*); +static int handle_options(struct event*); int add(int argc, char **argv) { struct event ev; @@ -11,28 +13,8 @@ int add(int argc, char **argv) { 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; - } + if(handle_options(&ev)<0) { return EXIT_FAILURE; } + if(handle_args(argc,argv,&ev)<0) { return EXIT_FAILURE; } strftime(timebuf,40,"%a, %d %b %Y %T %z",&(ev.datetime)); @@ -92,7 +74,7 @@ static int add_time_set(struct event *ev, const char *str) { if(NULL==str) { return -1; } - if(event_time_set(ev,str)<0) { + if(event_time_set(&(ev->datetime),str)<0) { fprintf(stderr,"invalid date format provided\n"); usage(); return -1; @@ -138,3 +120,40 @@ int add_to_file(struct event *to_add) { return 1; } + +int handle_args(int argc, char **argv, struct event *ev) { + assert(ev!=NULL); + + switch(argc-optind) { + case 4: + if(add_place_set(ev,argv[optind+2])<0) { return -1; } + case 3: + if(add_name_set(ev,argv[optind+1])<0) { return -1; } + if(add_time_set(ev,argv[argc-1])<0) { return -1; } + break; + default: + fprintf(stderr,"invalid number of arguments\n"); + usage(); + return -1; + } + + return 1; +} + +int handle_options(struct event *ev) { + struct tm *time; + + 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; + + if(global_options.event_filter.end>0) { + time = localtime(&(global_options.event_filter.end)); + if(NULL==time) { return -1; } + + memcpy(&(ev->options.until),time,sizeof(struct tm)); + } + } + + return 1; +} diff --git a/src/args.c b/src/args.c index 21232ab..2fc32e3 100644 --- a/src/args.c +++ b/src/args.c @@ -2,14 +2,13 @@ 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'}, + {"help", no_argument, 0, 'h'}, {"overdue", no_argument, 0, 'o'}, {"recurring", required_argument, 0, 'r'}, {"start-date", required_argument, 0, 's'}, - {"upcoming", required_argument, 0, 'u'}, + {"upcoming", optional_argument, 0, 'u'}, {0,0,0,0} }; @@ -19,15 +18,12 @@ enum sub_command args(int argc, char **argv) { while(1) { int option_index = 0; - if((c = getopt_long(argc,argv,"ad:e:f:or:s:u:",long_options,&option_index))==-1) { break; } + if((c = getopt_long(argc,argv,"ae:f:hor:s:t: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), @@ -52,8 +48,14 @@ enum sub_command args(int argc, char **argv) { )<0) { goto fail; } break; case 'u': - if(opt_event_filter_range_set("upcoming",optarg)<0) { goto fail; } + if((NULL==optarg)&&(argv[optind]!=NULL)&&(argv[optind][0]!='-')) { + if(opt_event_filter_range_set("upcoming",argv[optind])<0) { goto fail; } + optind++; + } else { + if(opt_event_filter_range_set("upcoming",optarg)<0) { goto fail; } + } break; + case 'h': case '?': default: goto fail; diff --git a/src/copy.c b/src/copy.c index 0c3edc1..1e2f80e 100644 --- a/src/copy.c +++ b/src/copy.c @@ -13,7 +13,7 @@ int copy(FILE *to, FILE *src, time_t until) { if(event_parse(buf,EVENT_SERIALIZE_MAX_LENGTH,&ev)<0) { return -1; } - if(event_date_compare(&ev,until)>0) { + if(event_time_compare(&(ev.datetime),until)>0) { event_free(&ev); if(fseek(src,-i,SEEK_CUR)!=0) { return -1; } break; diff --git a/src/event.c b/src/event.c deleted file mode 100644 index 236d4d1..0000000 --- a/src/event.c +++ /dev/null @@ -1,420 +0,0 @@ -#include - -static void event_options_init(struct event_options*); -static char event_options_period_char(enum time_period period); -static int event_parse_name(char*, size_t, struct event*); -static int event_parse_options(char*, size_t, struct event*); -static int event_parse_place(char*, size_t, struct event*); -static int event_parse_time(char*, size_t, struct event*); -static int event_serialize_name(char*, size_t, const struct event*); -static int event_serialize_options(char*, size_t, const struct event*); -static int event_serialize_place(char*, size_t, const struct event*); -static int event_serialize_time(char*, size_t, const struct event*); - -int event_date_compare(const struct event *ev, time_t time) { - struct tm tm; - time_t event_time; - memcpy(&tm,&(ev->datetime),sizeof(struct tm)); - - event_time = mktime(&tm); - - if(event_time>time) { return 1; } - if(event_timename!=NULL) { free(p->name); } - if(p->place!=NULL) { free(p->place); } -} - -void event_init(struct event *p) { - assert(p!=NULL); - - event_options_init(&(p->options)); - - p->name = NULL; - p->place = NULL; - - memset(&(p->datetime),0,sizeof(struct tm)); -} - -static void event_options_init(struct event_options *p) { - assert(p!=NULL); - - p->recur = 0; - p->recur_period = TIME_PERIOD_NONE; - - p->duration = 0; - p->duration_period = TIME_PERIOD_NONE; -} - -enum time_period event_options_period(char period) { - switch(period) { - case 'Y': - return TIME_PERIOD_YEAR; - case 'M': - return TIME_PERIOD_MONTH; - case 'W': - return TIME_PERIOD_WEEK; - case 'D': - return TIME_PERIOD_DAY; - case 'h': - return TIME_PERIOD_HOUR; - case 'm': - return TIME_PERIOD_MINUTE; - case 's': - return TIME_PERIOD_SECOND; - default: - return TIME_PERIOD_NONE; - } -} - -static char event_options_period_char(enum time_period period) { - switch(period) { - case TIME_PERIOD_SECOND: - return 's'; - case TIME_PERIOD_MINUTE: - return 'm'; - case TIME_PERIOD_HOUR: - return 'h'; - case TIME_PERIOD_DAY: - return 'D'; - case TIME_PERIOD_WEEK: - return 'W'; - case TIME_PERIOD_MONTH: - return 'M'; - case TIME_PERIOD_YEAR: - return 'Y'; - default: - return '\0'; - } -} - -int event_name_set(struct event *ev, const char *name) { - size_t len; - - if(ev->name!=NULL) { free(ev->name); } - - ev->name = strdup(name); - if(ev->name==NULL) { return -1; } - - len = strlen(ev->name); - len--; - if(ev->name[len]=='\n') { - ev->name[len] = '\0'; - } - - return 1; -} - -int event_parse(char *buf, size_t buf_size, struct event *ev) { - event_init(ev); - - if(event_parse_options(buf,buf_size,ev)<0) { return -1; } - if(event_parse_time(buf,buf_size,ev)<0) { return -1; } - if(event_parse_name(buf,buf_size,ev)<0) { return -1; } - if(event_parse_place(buf,buf_size,ev)<0) { return -1; } - - return 1; -} - -static int event_parse_name(char *buf, size_t buf_size, struct event *ev) { - const char escaped[] = "\""; - const char normal[] = ":"; - const char *token; - char *p; - - if(buf[0]=='"') { - token = escaped; - } else { - token = normal; - } - - p = strtok(buf,token); - if(p!=NULL) { - if(event_name_set(ev,p)<0) { return -1; } - } - - p = strtok(NULL,token); - - /* - * fix string for if place is also escaped - */ - if(token[0]!=':') { - if(p[1]=='\0') { - p[1] = '"'; - } - p++; - } - - if(p!=NULL) { - memmove(buf,p,(buf_size - (p - buf))); - } else { - buf[0] = '\0'; - } - - return 1; -} - -static int event_parse_options(char *buf, size_t buf_size, struct event *ev) { - char period; - char *options, *p; - size_t diff; - - if(buf[0]!='[') { return 0; } - - options = strtok(buf,"]"); - - diff = strlen(options)+1; - - p = strtok(&(options[1]),","); - while(p!=NULL) { - if(memcmp(p,"duration",8)==0) { - if(sscanf( - p, - "duration:%u%c", - &(ev->options.duration), - &period - )!=2) { - if(errno!=0) { - perror("sccanf"); - return -1; - } - } - ev->options.duration_period = event_options_period(period); - } else if(memcmp(p,"recur",5)==0) { - if(sscanf( - p, - "recur:%u%c", - &(ev->options.recur), - &period - )!=2) { - if(errno!=0) { - perror("sccanf"); - return -1; - } - } - ev->options.recur_period = event_options_period(period); - } else { return -1; } - - p = strtok(NULL,","); - } - - memmove(buf,&(buf[diff]),(buf_size - diff)); - return 1; -} - -static int event_parse_place(char *buf, size_t buf_size, struct event *ev) { - const char escaped[] = "\""; - const char normal[] = ":"; - const char *token; - char *p; - - if(buf[0]=='"') { - token = escaped; - } else { - token = normal; - } - - p = strtok(buf,token); - if(p!=NULL) { - if(event_place_set(ev,p)<0) { return -1; } - } - - return 1; -} - -static int event_parse_time(char *buf, size_t buf_size, struct event *ev) { - char *p; - - if(buf[10]!=' ') { - buf[10] = '\0'; - p = &(buf[11]); - } else if(buf[19]!=' ') { - buf[19] = '\0'; - p = &(buf[20]); - } else { - buf[25] = '\0'; - p = &(buf[26]); - } - - if(event_time_set(ev,buf)<0) { return -1; } - - memmove(buf,p,(buf_size - (p - buf))); - - return 1; -} - -int event_place_set(struct event *ev, const char *place) { - size_t len; - - if(ev->place!=NULL) { free(ev->place); } - - ev->place = strdup(place); - if(ev->place==NULL) { return -1; } - - len = strlen(ev->place); - len--; - if(ev->place[len]=='\n') { - ev->place[len] = '\0'; - } - - return 1; -} - -int event_serialize(char *buf, size_t buf_size, const struct event *ev) { - assert(buf!=NULL); - - memset(buf,0,buf_size); - - if(event_serialize_options(buf,buf_size,ev)<0) { return -1; } - if(event_serialize_time(buf,buf_size,ev)<0) { return -1; } - if(event_serialize_name(buf,buf_size,ev)<0) { return -1; } - if(event_serialize_place(buf,buf_size,ev)<0) { return -1; } - - event_free(ev); - - strcat(buf,"\n"); - - return 1; -} - -static int event_serialize_name(char *buf, size_t buf_size, const struct event *ev) { - char *p; - - if(NULL==ev->name) { return -1; } - - if(strlen(buf)+strlen(ev->name)+2>buf_size) { return -1; } - - strcat(buf,":"); - - p = strchr(ev->name,':'); - if(p!=NULL) { strcat(buf,"\""); } - - strcat(buf,ev->name); - - if(p!=NULL) { strcat(buf,"\""); } - - return 1; -} - -static int event_serialize_options(char *buf, size_t buf_size, const struct event *ev) { - size_t pos; - - if(!((ev->options.duration>0)||(ev->options.recur>0))) { return 0; } - - strcpy(buf,"["); - pos = 1; - - if(ev->options.duration>0) { - if(sprintf( - &(buf[pos]), - "duration:%u%c", - ev->options.duration, - event_options_period_char(ev->options.duration_period) - )<0) { return -1; } - - if(ev->options.recur>0) { - strcat(buf,","); - } - - pos = strlen(buf); - } - - if(ev->options.recur>0) { - if(sprintf( - &(buf[pos]), - "recur:%u%c", - ev->options.recur, - event_options_period_char(ev->options.recur_period) - )<0) { return -1; } - } - - strcat(buf,"]"); - - return 1; -} - -static int event_serialize_place(char *buf, size_t buf_size, const struct event *ev) { - char *p; - - assert(buf!=NULL); - - if(NULL==ev->place) { return 0; } - - if(strlen(buf)+strlen(ev->place)+2>buf_size) { return -1; } - - strcat(buf,":"); - - p = strchr(ev->name,':'); - if(p!=NULL) { strcat(buf,"\""); } - - strcat(buf,ev->place); - - if(p!=NULL) { strcat(buf,"\""); } - - return 1; -} - -static int event_serialize_time(char *buf, size_t buf_size, const struct event *ev) { - size_t len; - - assert(buf!=NULL); - - len = strlen(buf); - - if(ev->datetime.tm_sec==0 && ev->datetime.tm_min==0 && ev->datetime.tm_hour == 0) { - if(0==strftime( - &(buf[len]), /* char *s */ - buf_size - len, /* size_t max */ - "%Y-%m-%d", /* const char *format */ - &(ev->datetime) /* const struct tm *tm */ - )) { return -1; } - } else { - if(0==strftime( - &(buf[len]), /* char *s */ - buf_size - len, /* size_t max */ - "%Y-%m-%d %H:%M:%S %z", /* const char *format */ - &(ev->datetime) /* const struct tm *tm */ - )) { return -1; } - } - - return 1; -} - -int event_time_set(struct event *ev, const char *str) { - const char *p; - - p = str; - - memset(&(ev->datetime), 0, sizeof(struct tm)); - - /* minimum expected date format */ - p = strptime(p,"%Y-%m-%d",&(ev->datetime)); - if(NULL==p) { return -1; } - - /* attempt to get time information */ - p = strptime(p,"%H:%M:%S",&(ev->datetime)); - - /* attempt to get timezone information */ - if(p!=NULL) { - strptime(p,"%z",&(ev->datetime)); - } - - return 1; -} - -int event_within(struct event *ev, struct event_filter *filter) { - struct tm tm; - time_t event_time; - - memcpy(&tm,&(ev->datetime),sizeof(struct tm)); - - event_time = mktime(&tm); - - if(filter->start>event_time) { return -1; } - if(filter->end + +void event_free(const struct event *p) { + if(p->name!=NULL) { free(p->name); } + if(p->place!=NULL) { free(p->place); } +} diff --git a/src/event/init.c b/src/event/init.c new file mode 100644 index 0000000..1872152 --- /dev/null +++ b/src/event/init.c @@ -0,0 +1,22 @@ +#include + +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)); +} + +void event_options_init(struct event_options *p) { + assert(p!=NULL); + + p->recur = 0; + p->recur_period = TIME_PERIOD_NONE; + + memset(&(p->until),0,sizeof(struct tm)); +} + diff --git a/src/event/name.c b/src/event/name.c new file mode 100644 index 0000000..ae20e91 --- /dev/null +++ b/src/event/name.c @@ -0,0 +1,18 @@ +#include + +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; +} diff --git a/src/event/parse.c b/src/event/parse.c new file mode 100644 index 0000000..b1cf3d1 --- /dev/null +++ b/src/event/parse.c @@ -0,0 +1,133 @@ +#include + +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*); + +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,"until:",6)==0) { + if(event_time_set(&(ev->options.until),&(p[6]))<0) { return -1; } + } 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->datetime),buf)<0) { return -1; } + + memmove(buf,p,(buf_size - (p - buf))); + + return 1; +} diff --git a/src/event/period.c b/src/event/period.c new file mode 100644 index 0000000..9a7b5dc --- /dev/null +++ b/src/event/period.c @@ -0,0 +1,43 @@ +#include + +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; + } +} + +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'; + } +} diff --git a/src/event/place.c b/src/event/place.c new file mode 100644 index 0000000..01f293c --- /dev/null +++ b/src/event/place.c @@ -0,0 +1,18 @@ +#include + +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; +} diff --git a/src/event/serial.c b/src/event/serial.c new file mode 100644 index 0000000..df20225 --- /dev/null +++ b/src/event/serial.c @@ -0,0 +1,123 @@ +#include + +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 tm*); + +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->datetime))<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.recur>0)) { return 0; } + + strcpy(buf,"["); + pos = 1; + + 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; } + + if(event_time_compare(&(ev->options.until),0)>0) { + strcat(buf,",until:"); + + if(event_serialize_time( + &(buf[pos]), + buf_size, + &(ev->options.until) + )<0) { return -1; } + + pos = strlen(buf); + } + } + + 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 tm *datetime) { + size_t len; + + assert(buf!=NULL); + + len = strlen(buf); + + if(datetime->tm_sec==0 && datetime->tm_min==0 && datetime->tm_hour == 0) { + if(0==strftime( + &(buf[len]), /* char *s */ + buf_size - len, /* size_t max */ + "%Y-%m-%d", /* const char *format */ + 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 */ + datetime /* const struct tm *tm */ + )) { return -1; } + } + + return 1; +} diff --git a/src/event/time.c b/src/event/time.c new file mode 100644 index 0000000..1a46aaf --- /dev/null +++ b/src/event/time.c @@ -0,0 +1,51 @@ +#include + +int event_time_compare(const struct tm *datetime, time_t time) { + struct tm tm; + time_t event_time; + + assert(datetime!=NULL); + + memcpy(&tm,datetime,sizeof(struct tm)); + event_time = mktime(&tm); + + if(event_time>time) { return 1; } + if(event_timedatetime),sizeof(struct tm)); + + event_time = mktime(&tm); + + if(filter->start>event_time) { return -1; } + if(filter->endname) { return -1; } + char datebuf[20]; if( (ev->datetime.tm_sec==0) && @@ -46,13 +50,17 @@ static int event_print(struct event *ev) { 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,"%-20s\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(event_time_compare(&(ev->datetime),time(NULL))<1) { + if(fprintf(stdout," \x1B[31m[OVERDUE]\x1B[0m")<0) { return -1; } + } + if(fprintf(stdout,"\n")<0) { return -1; } return 1; diff --git a/src/opt/duration.c b/src/opt/duration.c deleted file mode 100644 index 82c882e..0000000 --- a/src/opt/duration.c +++ /dev/null @@ -1,25 +0,0 @@ -#include - -int opt_duration_set(const char *p) { - unsigned int duration; - char c; - enum time_period period; - - if(NULL==p) { return -1; } - - if(sscanf(p,"%u%c",&duration,&c)!=2) { - fprintf(stderr,"invalid duration option value: %s\n",p); - return -1; - } - - period = event_options_period(c); - if(TIME_PERIOD_NONE==period) { - fprintf(stderr,"invalid duration option value: %s\n",p); - return -1; - } - - global_options.event_options.duration = duration; - global_options.event_options.duration_period = period; - - return 1; -} diff --git a/src/opt/env.c b/src/opt/env.c index acf6298..0415641 100644 --- a/src/opt/env.c +++ b/src/opt/env.c @@ -10,10 +10,10 @@ 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); + CHECK_ENV("UNTIL",opt_until_set); return 1; } diff --git a/src/opt/filter.c b/src/opt/filter.c index 60f6d7b..b0e6e37 100644 --- a/src/opt/filter.c +++ b/src/opt/filter.c @@ -8,11 +8,11 @@ int opt_event_filter_date_set(time_t *to_set, const char *date_string) { assert(to_set!=NULL); /* - * This function uses the event_time_set(struct event *ev, const char *str) + * This function uses the event_time_set(struct tm *datetime, const char *str) * method for consistencies' sake. */ - if(event_time_set(&ev,date_string)<0) { + if(event_time_set(&(ev.datetime),date_string)<0) { fprintf(stderr,"invalid date given: %s\n",date_string); return -1; } @@ -24,7 +24,7 @@ int opt_event_filter_date_set(time_t *to_set, const char *date_string) { 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) { + if(opt_event_filter_range_set_upcoming((NULL==arg)?"1W":arg)<0) { fprintf(stderr, "invalid time period given as argument to --upcoming option\n" ); diff --git a/src/opt/global.c b/src/opt/global.c index 74bdd75..3774c4f 100644 --- a/src/opt/global.c +++ b/src/opt/global.c @@ -3,12 +3,8 @@ void opt_global_init() { global_options.file = NULL; - global_options.event_options.duration = 0; - global_options.event_options.duration_period = TIME_PERIOD_NONE; + event_options_init(&(global_options.event_options)); - global_options.event_options.recur = 0; - global_options.event_options.recur_period = TIME_PERIOD_NONE; - global_options.event_filter.start = 0; global_options.event_filter.end = 0; } diff --git a/src/opt/until.c b/src/opt/until.c new file mode 100644 index 0000000..49d1522 --- /dev/null +++ b/src/opt/until.c @@ -0,0 +1,18 @@ +#include + +int opt_until_set(const char *p) { + struct event ev; + + if(NULL==p) { return -1; } + + event_init(&ev); + + if(event_time_set(&(ev.datetime),p)<0) { + fprintf(stderr,"invalid until option value: %s\n",p); + return -1; + } + + global_options.event_options.until = ev.datetime; + + return 1; +} diff --git a/src/rm.c b/src/rm.c index 5bc2e7f..819996f 100644 --- a/src/rm.c +++ b/src/rm.c @@ -6,11 +6,13 @@ 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]; + struct tm tm; 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; @@ -37,6 +39,11 @@ static int check_need_readd(struct event *ev) { return -1; } + memcpy(&tm,&(ev->datetime),sizeof(struct tm)); + if(event_time_compare(&(ev->options.until),0)>0) { + if(event_time_compare(&(ev->options.until),mktime(&tm))<0) { return 1; } + } + strftime(timebuf,40,"%a, %d %b %Y %T %z",&(ev->datetime)); if(snprintf( diff --git a/src/seek.c b/src/seek.c index 8378202..0166ef3 100644 --- a/src/seek.c +++ b/src/seek.c @@ -11,7 +11,7 @@ int seek(FILE *fp, time_t time) { 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) { + if(event_time_compare(&(ev.datetime),time)>=0) { event_free(&ev); if(fseek(fp,-i,SEEK_CUR)!=0) { return -1; } break; diff --git a/src/usage.c b/src/usage.c index 4b6c991..1b73693 100644 --- a/src/usage.c +++ b/src/usage.c @@ -1,5 +1,25 @@ #include +struct option_description { + const char *long_opt; + const char short_opt; + const char *description; +}; + +static struct option_description options[] = { + {"all",'a',"show all"}, + {"end-date",'e',"ending date"}, + {"file",'f',"events file location"}, + {"help",'h',"show this help"}, + {"overdue",'o',"show only overdue events"}, + {"recurring",'r',"denote event as recurring"}, + {"start-date",'s',"starting date"}, + {"upcoming",'u',"show only upcoming events"}, + {NULL,0,NULL} +}; + +static void usage_print_options(); + void usage() { fprintf(stderr,"Usage:\n"); fprintf(stderr,"\tev [options] [command = 'ls'] [arg1, arg2, ..., argn]\n"); @@ -14,8 +34,27 @@ void usage() { fprintf(stderr,"\n"); fprintf(stderr,"Options:\n"); + + usage_print_options(); + fprintf(stderr,"\n"); - fprintf(stderr,"See `man ev` for more information\n"); + fprintf(stderr,"See `man ev` for more information.\n"); return; } + +static void usage_print_options() { + size_t i; + + // NOTE: long options defined in src/args.c + + i = 0; + while( + !((NULL==options[i].long_opt) && + (0==options[i].short_opt) && + (NULL==options[i].description))) { + + fprintf(stderr,"\t--%s, -%c\t\t%s\n",options[i].long_opt,options[i].short_opt,options[i].description); + i++; + } +} diff --git a/test/unit/Makefile.am b/test/unit/Makefile.am index 50b3bc2..ba94b39 100644 --- a/test/unit/Makefile.am +++ b/test/unit/Makefile.am @@ -6,13 +6,7 @@ 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 +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 usage.tests TESTS = $(check_PROGRAMS) if ENABLE_MEMCHECK @@ -22,13 +16,23 @@ endif common_SOURCES = \ test_utils.c \ + $(top_srcdir)/src/event/init.c \ $(top_srcdir)/src/opt/global.c +event_SOURCES = \ + $(top_srcdir)/src/event/free.c \ + $(top_srcdir)/src/event/name.c \ + $(top_srcdir)/src/event/parse.c \ + $(top_srcdir)/src/event/period.c \ + $(top_srcdir)/src/event/place.c \ + $(top_srcdir)/src/event/serial.c \ + $(top_srcdir)/src/event/time.c + add_tests_SOURCES = \ $(common_SOURCES) \ + $(event_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 \ @@ -43,26 +47,27 @@ 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/event/period.c \ + $(top_srcdir)/src/event/time.c \ $(top_srcdir)/src/opt/file.c \ $(top_srcdir)/src/opt/filter.c \ $(top_srcdir)/src/opt/recur.c \ + $(top_srcdir)/src/opt/until.c \ $(top_srcdir)/src/usage.c copy_tests_SOURCES = \ $(common_SOURCES) \ + $(event_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) \ + $(event_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 \ @@ -73,8 +78,9 @@ cut_tests_CPPFLAGS = $(AM_CPPFLAGS) \ -DCUT_SRC_FILE="$(top_srcdir)/src/cut.c" event_tests_SOURCES = \ - event.tests.c \ - $(top_srcdir)/src/event.c + $(common_SOURCES) \ + $(event_SOURCES) \ + event.tests.c file_tests_SOURCES = \ $(common_SOURCES) \ @@ -87,23 +93,30 @@ file_tests_SOURCES = \ ls_tests_SOURCES = \ $(common_SOURCES) \ - ls.tests.c + $(event_SOURCES) \ + ls.tests.c \ + $(top_srcdir)/src/file/line.c \ + $(top_srcdir)/src/file/open.c + +ls_tests_CPPFLAGS = $(AM_CPPFLAGS) \ + -DLS_SRC_FILE="$(top_srcdir)/src/ls.c" opt_tests_SOURCES = \ $(common_SOURCES) \ opt.tests.c \ - $(top_srcdir)/src/event.c \ - $(top_srcdir)/src/opt/duration.c \ + $(top_srcdir)/src/event/period.c \ + $(top_srcdir)/src/event/time.c \ $(top_srcdir)/src/opt/filter.c \ - $(top_srcdir)/src/opt/recur.c + $(top_srcdir)/src/opt/recur.c \ + $(top_srcdir)/src/opt/until.c postpone_tests_SOURCES = \ $(common_SOURCES) \ + $(event_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 \ @@ -117,8 +130,8 @@ postpone_tests_CPPFLAGS = $(AM_CPPFLAGS) \ prune_tests_SOURCES = \ $(common_SOURCES) \ + $(event_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 \ @@ -129,11 +142,11 @@ prune_tests_SOURCES = \ rm_tests_SOURCES = \ $(common_SOURCES) \ + $(event_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 \ @@ -147,7 +160,20 @@ rm_tests_CPPFLAGS = $(AM_CPPFLAGS) \ seek_tests_SOURCES = \ $(common_SOURCES) \ + $(event_SOURCES) \ seek.tests.c \ - $(top_srcdir)/src/event.c \ $(top_srcdir)/src/file/line.c \ $(top_srcdir)/src/seek.c + +usage_tests_SOURCES = \ + $(common_SOURCES) \ + $(event_SOURCES) \ + usage.tests.c \ + $(top_srcdir)/src/opt/file.c \ + $(top_srcdir)/src/opt/filter.c \ + $(top_srcdir)/src/opt/recur.c \ + $(top_srcdir)/src/opt/until.c + +usage_tests_CPPFLAGS = $(AM_CPPFLAGS) \ + -DARGS_SRC_FILE="$(top_srcdir)/src/args.c" \ + -DUSAGE_SRC_FILE="$(top_srcdir)/src/usage.c" diff --git a/test/unit/add.tests.c b/test/unit/add.tests.c index 1d56e26..b34370a 100644 --- a/test/unit/add.tests.c +++ b/test/unit/add.tests.c @@ -12,10 +12,15 @@ 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(); +static void handle_args_basic_test(); +static void handle_options_basic_test(); int main() { setup_env(); + handle_args_basic_test(); + handle_options_basic_test(); + add_name_set_basic_test(); add_place_set_basic_test(); add_time_set_basic_test(); @@ -180,3 +185,74 @@ static void add_to_file_extensive_test() { rounds++; } } + +static void handle_args_basic_test() { + struct event ev; + + event_init(&ev); + + char *args_with_place[] = { + "ev", + "add", + "event", + "place", + "2022-01-01", + NULL + }; + assert(handle_args(0,args_with_place,&ev)==-1); + assert(handle_args(5,args_with_place,&ev)==1); + assert(strcmp(ev.name,"event")==0); + assert(strcmp(ev.place,"place")==0); + assert(mktime(&(ev.datetime))==1641024000); + + char *args[] = { + "ev", + "add", + "event23", + "2020-01-01", + NULL + }; + + event_free(&ev); + + event_init(&ev); + + assert(handle_args(4,args,&ev)==1); + assert(strcmp(ev.name,"event23")==0); + assert(ev.place==NULL); + assert(mktime(&(ev.datetime))==1577865600); + + event_free(&ev); +} + +static void handle_options_basic_test() { + struct event ev; + struct tm dummy; + + event_init(&ev); + + memset(&dummy,0,sizeof(struct tm)); + + assert(1==handle_options(&ev)); + assert(0==ev.options.recur); + assert(TIME_PERIOD_NONE==ev.options.recur_period); + assert(memcmp(&dummy,&(ev.options.until),sizeof(struct tm))==0); + + global_options.event_filter.end = 1000; + + assert(1==handle_options(&ev)); + assert(0==ev.options.recur); + assert(TIME_PERIOD_NONE==ev.options.recur_period); + assert(memcmp(&dummy,&(ev.options.until),sizeof(struct tm))==0); + + global_options.event_options.recur = 1; + global_options.event_options.recur_period = TIME_PERIOD_DAY; + global_options.event_filter.end = 1600000000; + + assert(1==handle_options(&ev)); + assert(1==ev.options.recur); + assert(TIME_PERIOD_DAY==ev.options.recur_period); + assert(mktime(&(ev.options.until))==1600000000); + + reset_env(); +} diff --git a/test/unit/args.tests.c b/test/unit/args.tests.c index a517266..41da3da 100644 --- a/test/unit/args.tests.c +++ b/test/unit/args.tests.c @@ -13,6 +13,7 @@ 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_ls_upcoming_optional_arg_test(); static void args_order_test(); static void args_postpone_basic_test(); static void args_prune_basic_test(); @@ -51,6 +52,7 @@ static void args_basic_test() { args_ls_end_date_basic_test(); args_ls_overdue_basic_test(); args_ls_upcoming_basic_test(); + args_ls_upcoming_optional_arg_test(); args_ls_start_date_basic_test(); args_postpone_basic_test(); args_prune_basic_test(); @@ -75,17 +77,13 @@ 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(SUB_COMMAND_ADD==args(6,long_opts_args)); 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); @@ -249,33 +247,96 @@ static void args_ls_upcoming_basic_test() { reset_env(); } + +static void args_ls_upcoming_optional_arg_test() { + char *ls_upcoming_no_arg[] = { + "ev", + "ls", + "-u", + NULL + }; + assert(SUB_COMMAND_LS==args(3,ls_upcoming_no_arg)); + 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_long_optional_arg[] = { + "ev", + "--upcoming", + NULL + }; + assert(SUB_COMMAND_LS==args(2,ls_upcoming_long_optional_arg)); + 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_optional_arg_fail[] = { + "ev", + "-u", + "ls", + NULL + }; + assert(SUB_COMMAND_INVALID_ARGS==args(3,ls_upcoming_short_optional_arg_fail)); + optind = 0; + + reset_env(); + + char *ls_upcoming_long_optional_arg_fail[] = { + "ev", + "--upcoming", + "ls", + NULL + }; + assert(SUB_COMMAND_INVALID_ARGS==args(3,ls_upcoming_long_optional_arg_fail)); + optind = 0; + + reset_env(); + + char *ls_upcoming_short_optional_arg[] = { + "ev", + "-u", + "1D", + "ls", + NULL + }; + + assert(SUB_COMMAND_LS==args(4,ls_upcoming_short_optional_arg)); + 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", + "--recur", "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); + assert(global_options.event_options.recur==1); + assert(global_options.event_options.recur_period==TIME_PERIOD_WEEK); optind = 0; reset_env(); char *swapped[] = { "ev", - "--duration", + "--recur", "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; + assert(global_options.event_options.recur==1); + assert(global_options.event_options.recur_period==TIME_PERIOD_WEEK); optind = 0; reset_env(); } @@ -314,17 +375,13 @@ 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(SUB_COMMAND_ADD==args(6,short_opts_args)); 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); diff --git a/test/unit/event.tests.c b/test/unit/event.tests.c index 3ea0f35..09ef926 100644 --- a/test/unit/event.tests.c +++ b/test/unit/event.tests.c @@ -1,19 +1,24 @@ +#include + #include int main(); -static void event_date_compare_basic_test(); static void event_init_basic_test(); static void event_name_set_basic_test(); static void event_parse_basic_test(); static void event_place_set_basic_test(); static void event_serialize_basic_test(); static void event_serialize_parse_exactness_test(); +static void event_time_compare_basic_test(); static void event_time_set_basic_test(); +static void time_unset(struct tm*); int main() { + setup_env(); + event_init_basic_test(); - event_date_compare_basic_test(); + event_time_compare_basic_test(); event_name_set_basic_test(); event_parse_basic_test(); event_place_set_basic_test(); @@ -21,57 +26,24 @@ int main() { 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)); + clean_env(); - event_free(&ev); + return EXIT_SUCCESS; } 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); + time_unset(&(ev.options.until)); + time_unset(&(ev.datetime)); event_free(&ev); } @@ -98,10 +70,9 @@ static void event_parse_basic_test() { 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); + time_unset(&(ev1.options.until)); assert(strcmp(ev1.name,"test event")==0); assert(strcmp(ev1.place,"test place")==0); assert(mktime(&(ev1.datetime))==1662019200); @@ -109,10 +80,9 @@ static void event_parse_basic_test() { 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); + time_unset(&(ev2.options.until)); assert(strcmp(ev2.name,"test: event2")==0); assert(strcmp(ev2.place,"test: place2")==0); assert(mktime(&(ev2.datetime))==1662105600); @@ -120,10 +90,9 @@ static void event_parse_basic_test() { 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); + time_unset(&(ev3.options.until)); assert(strcmp(ev3.name,"test event3")==0); assert(strcmp(ev3.place,"test place3")==0); assert(mktime(&(ev3.datetime))==1662226200); @@ -131,10 +100,9 @@ static void event_parse_basic_test() { 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); + time_unset(&(ev4.options.until)); assert(strcmp(ev4.name,"test event4")==0); assert(strcmp(ev4.place,"test place4")==0); assert(mktime(&(ev4.datetime))==1662312600); @@ -142,43 +110,39 @@ static void event_parse_basic_test() { 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); + time_unset(&(ev5.options.until)); 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"; + char ex6[] = "[recur: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(ev6.options.recur==1); + assert(ev6.options.recur_period==TIME_PERIOD_WEEK); + time_unset(&(ev6.options.until)); 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"; + char ex7[] = "[recur:1W,until:2022-11-01]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(mktime(&(ev7.options.until))==1667289600); 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"; + char ex8[] = "[until:2022-10-10,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(mktime(&(ev8.options.until))==1665388800); assert(strcmp(ev8.name,"test event8")==0); assert(strcmp(ev8.place,"test place8")==0); assert(mktime(&(ev8.datetime))==1664645400); @@ -186,10 +150,9 @@ static void event_parse_basic_test() { 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); + time_unset(&(ev9.options.until)); assert(strcmp(ev9.name,"test")==0); assert(NULL==ev9.place); assert(mktime(&(ev9.datetime))==1659340800); @@ -227,7 +190,7 @@ static void event_serialize_basic_test() { event_init(&ev); - assert(1==event_time_set(&ev,"2022-07-01 08:01:15 -0100")); + assert(1==event_time_set(&(ev.datetime),"2022-07-01 08:01:15 -0100")); assert(-1==event_serialize(buf,EVENT_SERIALIZE_MAX_LENGTH,&ev)); assert(1==event_name_set(&ev,"event")); @@ -235,57 +198,56 @@ static void event_serialize_basic_test() { 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_time_set(&(ev.datetime),"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_time_set(&(ev.datetime),"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_time_set(&(ev.datetime),"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_time_set(&(ev.datetime),"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_DAY; assert(1==event_serialize(buf,EVENT_SERIALIZE_MAX_LENGTH,&ev)); - assert(strcmp(buf,"[duration:30D]2022-06-21:event:place\n")==0); + assert(strcmp(buf,"[recur:1D]2022-06-21:event:place\n")==0); event_init(&ev); - assert(1==event_time_set(&ev,"2022-06-21")); + assert(1==event_time_set(&(ev.datetime),"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_time_set(&(ev.options.until),"2024-01-01")); assert(1==event_serialize(buf,EVENT_SERIALIZE_MAX_LENGTH,&ev)); - assert(strcmp(buf,"[duration:30D,recur:1Y]2022-06-21:event:place\n")==0); + assert(strcmp(buf,"[recur:1Y,until:2024-01-01]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"; + const char expected[] = "[recur:1Y,until:2000-02-01]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)); + assert(57==strlen(expected)); strcpy(buf,expected); assert(1==event_parse(buf,EVENT_SERIALIZE_MAX_LENGTH,&ev)); @@ -293,7 +255,39 @@ static void event_serialize_parse_exactness_test() { memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); assert(1==event_serialize(buf,EVENT_SERIALIZE_MAX_LENGTH,&ev)); - assert(memcmp(expected,buf,53)==0); + assert(memcmp(expected,buf,57)==0); +} + +static void event_time_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.datetime),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_time_compare(&(ev.datetime),timet)); + + timet += 2000; + assert(-1==event_time_compare(&(ev.datetime),timet)); + + timet -= 1000; + assert(0==event_time_compare(&(ev.datetime),timet)); + + event_free(&ev); } static void event_time_set_basic_test() { @@ -301,12 +295,12 @@ static void event_time_set_basic_test() { event_init(&ev); - assert(-1==event_time_set(&ev,"nota date")); + assert(-1==event_time_set(&(ev.datetime),"nota date")); - assert(-1==event_time_set(&ev,"99")); - assert(-1==event_time_set(&ev,"99-02")); + assert(-1==event_time_set(&(ev.datetime),"99")); + assert(-1==event_time_set(&(ev.datetime),"99-02")); - assert(1==event_time_set(&ev,"1999-02-28")); + assert(1==event_time_set(&(ev.datetime),"1999-02-28")); assert(ev.datetime.tm_sec==0); assert(ev.datetime.tm_min==0); assert(ev.datetime.tm_hour==0); @@ -316,9 +310,9 @@ static void event_time_set_basic_test() { 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(ev.datetime.tm_gmtoff==0); - assert(1==event_time_set(&ev,"1997-01-28 not a time")); + assert(1==event_time_set(&(ev.datetime),"1997-01-28 not a time")); assert(ev.datetime.tm_sec==0); assert(ev.datetime.tm_min==0); assert(ev.datetime.tm_hour==0); @@ -328,9 +322,9 @@ static void event_time_set_basic_test() { 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(ev.datetime.tm_gmtoff==0); - assert(1==event_time_set(&ev,"2002-05-24 121212")); + assert(1==event_time_set(&(ev.datetime),"2002-05-24 121212")); assert(ev.datetime.tm_sec==0); assert(ev.datetime.tm_min==0); assert(ev.datetime.tm_hour==12); @@ -340,9 +334,9 @@ static void event_time_set_basic_test() { 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(ev.datetime.tm_gmtoff==0); - assert(1==event_time_set(&ev,"2012-12-22 12:1212")); + assert(1==event_time_set(&(ev.datetime),"2012-12-22 12:1212")); assert(ev.datetime.tm_sec==0); assert(ev.datetime.tm_min==12); assert(ev.datetime.tm_hour==12); @@ -352,9 +346,9 @@ static void event_time_set_basic_test() { 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(ev.datetime.tm_gmtoff==0); - assert(1==event_time_set(&ev,"2022-01-24 09:19:55")); + assert(1==event_time_set(&(ev.datetime),"2022-01-24 09:19:55")); assert(ev.datetime.tm_sec==55); assert(ev.datetime.tm_min==19); assert(ev.datetime.tm_hour==9); @@ -364,9 +358,9 @@ static void event_time_set_basic_test() { 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(ev.datetime.tm_gmtoff==0); - assert(1==event_time_set(&ev,"2009-01-03 13:25:25 -0800")); + assert(1==event_time_set(&(ev.datetime),"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); @@ -376,5 +370,11 @@ static void event_time_set_basic_test() { assert(ev.datetime.tm_wday==6); assert(ev.datetime.tm_yday==2); assert(ev.datetime.tm_isdst==0); - assert(ev.datetime.__tm_gmtoff==-28800); + assert(ev.datetime.tm_gmtoff==-28800); +} + +static void time_unset(struct tm *tm) { + struct tm dummy; + memset(&dummy, 0, sizeof(struct tm)); + assert(memcmp(tm,&dummy,sizeof(struct tm))==0); } diff --git a/test/unit/ls.tests.c b/test/unit/ls.tests.c index ec2b828..61d4f93 100644 --- a/test/unit/ls.tests.c +++ b/test/unit/ls.tests.c @@ -2,8 +2,27 @@ #include +#include INCLUDE(LS_SRC_FILE) + int main(); +static void event_print_basic_test(); int main() { + event_print_basic_test(); + return EXIT_SUCCESS; } + +static void event_print_basic_test() { + struct event ev; + + event_init(&ev); + + assert(event_print(&ev)==-1); + + assert(1==event_name_set(&ev,"test")); + + assert(event_print(&ev)==1); + + event_free(&ev); +} diff --git a/test/unit/opt.tests.c b/test/unit/opt.tests.c index 24e8110..0124f01 100644 --- a/test/unit/opt.tests.c +++ b/test/unit/opt.tests.c @@ -3,43 +3,26 @@ #include int main(); -static void opt_duration_set_basic_test(); static void opt_event_filter_date_set_basic_test(); static void opt_event_filter_range_set_basic_test(); static void opt_event_filter_set_basic_test(); static void opt_recur_set_basic_test(); +static void opt_until_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(); + opt_until_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")); @@ -111,3 +94,14 @@ static void opt_recur_set_basic_test() { reset_env(); } + +static void opt_until_set_basic_test() { + assert(-1==opt_until_set(NULL)); + + assert(-1==opt_until_set("notdsoifjaoisdjfoiasdj")); + + assert(1==opt_until_set("2022-01-01")); + assert(mktime(&(global_options.event_options.until))==1641024000); + + reset_env(); +} diff --git a/test/unit/postpone.tests.c b/test/unit/postpone.tests.c index f54d95e..64a2d5f 100644 --- a/test/unit/postpone.tests.c +++ b/test/unit/postpone.tests.c @@ -121,7 +121,7 @@ static void postpone_event_basic_test() { memset(buf,0,EVENT_SERIALIZE_MAX_LENGTH); assert(event_name_set(&ev,"test")==1); - assert(event_time_set(&ev,"2020-01-01")==1); + assert(event_time_set(&(ev.datetime),"2020-01-01")==1); postpone_offset_string = NULL; assert(postpone_event(&ev)==1); diff --git a/test/unit/rm.tests.c b/test/unit/rm.tests.c index 1ceae1b..4c10ced 100644 --- a/test/unit/rm.tests.c +++ b/test/unit/rm.tests.c @@ -6,6 +6,7 @@ int main(); static void check_need_readd_basic_test(); +static void check_need_readd_until_test(); static void handle_args_basic_test(); static void rm_basic_test(); static void rm_dismiss_basic_test(); @@ -17,6 +18,7 @@ int main() { rm_basic_test(); check_need_readd_basic_test(); + check_need_readd_until_test(); rm_dismiss_basic_test(); clean_env(); @@ -37,7 +39,7 @@ static void check_need_readd_basic_test() { assert(fclose(src)==0); assert(event_name_set(&ev,"test")==1); - assert(event_time_set(&ev,"2000-01-01")==1); + assert(event_time_set(&(ev.datetime),"2000-01-01")==1); assert(1==check_need_readd(&ev)); @@ -66,6 +68,67 @@ static void check_need_readd_basic_test() { reset_env(); } +static void check_need_readd_until_test() { + char buf[EVENT_SERIALIZE_MAX_LENGTH]; + struct event ev; + time_t target_timet; + struct tm *target; + FILE *src; + + event_init(&ev); + + src = fopen(global_options.file,"w"); + assert(src!=NULL); + + assert(event_name_set(&ev,"test until")==1); + assert(event_time_set(&(ev.datetime),"2020-01-01")==1); + + ev.options.recur = 1; + ev.options.recur_period = TIME_PERIOD_WEEK; + target_timet = 1580545370; // approx. 2020-02-01 + target = localtime(&target_timet); + memcpy(&(ev.options.until),target,sizeof(struct tm)); + + 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)==64); + assert(memcmp(buf,"[recur:1W,until:2020-02-01 00:22:50 -0800]2020-01-08:test until\n",64)==0); + + assert(fclose(src)==0); + + reset_env(); + + src = fopen(global_options.file,"w"); + assert(src!=NULL); + + event_init(&ev); + + assert(event_name_set(&ev,"test2 until")==1); + assert(event_time_set(&(ev.datetime),"2020-01-01")==1); + ev.options.recur = 1; + ev.options.recur_period = TIME_PERIOD_YEAR; + memcpy(&(ev.options.until),target,sizeof(struct tm)); + + 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)==0); + assert(feof(src)!=0); + assert(fclose(src)==0); + + event_free(&ev); + + reset_env(); +} + static void handle_args_basic_test() { unsigned long int offset; time_t start_time; diff --git a/test/unit/test_utils.c b/test/unit/test_utils.c index 883e906..cccc21d 100644 --- a/test/unit/test_utils.c +++ b/test/unit/test_utils.c @@ -24,6 +24,9 @@ void reset_env() { } void setup_env() { + assert(setenv("TZ","America/Los_Angeles",1)==0); + tzset(); + srand(time(NULL)); global_options.file = strdup(default_file); diff --git a/test/unit/usage.tests.c b/test/unit/usage.tests.c new file mode 100644 index 0000000..bc34ddd --- /dev/null +++ b/test/unit/usage.tests.c @@ -0,0 +1,24 @@ +#include + +#include + +#include INCLUDE(ARGS_SRC_FILE) +#include INCLUDE(USAGE_SRC_FILE) + +int main(); +static void args_usage_basic_test(); + +int main() { + args_usage_basic_test(); + + return EXIT_SUCCESS; +} + +static void args_usage_basic_test() { + size_t i = 0; + + while(options[i].long_opt!=NULL) { + assert(strcmp(long_options[i].name,options[i].long_opt)==0); + i++; + } +}