Compare commits

..

36 Commits

Author SHA1 Message Date
4313a7ef84 updated readme 2024-11-23 15:40:51 +01:00
3bf6fea43d removed uncommented code, updated readme 2024-11-23 15:30:11 +01:00
8e4c37fe88 all unit tests successful, might have fixed bug 2024-11-23 12:24:43 +01:00
2e60c19b0a removed Makefile 2024-11-23 08:50:08 +01:00
ed4d5cc798 install bin with meson 2024-11-17 17:35:52 +01:00
a7d2a49931 added TODO 2024-11-17 10:13:33 +01:00
7bef5ea855 all unit tests migrated to meson 2024-11-17 10:10:52 +01:00
28d6710c0a second unit test works with meson and found source of bug 2024-11-17 10:04:29 +01:00
f69d56d91a first unit test runs with meson 2024-11-17 09:51:05 +01:00
8bdb19d0c0 included failed ics file in unit tests 2024-11-17 09:39:03 +01:00
58e0c8feb3 meson build works and is simple 2024-11-17 09:26:32 +01:00
750d450b75 changed date in test file 2024-11-16 16:35:19 +01:00
0eda58f63f added test file and TODOs 2024-11-11 07:34:17 +01:00
ad96144904 updated readme 2024-10-05 16:05:21 +02:00
7e36e9143d annotated print_upcoming function 2024-10-05 15:56:08 +02:00
773bd82216 tests are looking good for ongoing events 2024-10-05 15:47:47 +02:00
c20f1ca1f0 renamed function parameter 2024-07-10 16:50:24 +02:00
578bf0b0f5 fixed pretty_print_date_time 2024-07-10 16:42:43 +02:00
33d9fed0cf added unit-test for print_upcoming() 2024-07-10 15:25:26 +02:00
9bba31490b added pragma once to header file 2024-07-09 11:17:34 +02:00
2c6f4bb357 renamed unit test
only test one function per unit test file.
2024-07-09 08:49:48 +02:00
2442af328b improved README 2024-07-09 08:40:13 +02:00
37b6e1ce2c removed unneeded includes in main.c 2024-07-09 08:32:32 +02:00
3ead0c07b2 added Makefile for unit test 2024-07-09 08:27:18 +02:00
c4fc332419 added first unit test 2024-07-09 08:08:01 +02:00
ad344b03ac commented function, closed file 2024-07-09 07:13:01 +02:00
7809fb64ab implemented new function parse_ics_file 2024-07-09 07:02:12 +02:00
d225e6aa41 updated README, how to enable git-hooks 2024-07-08 20:42:24 +02:00
5a96c95f69 changed ctags command 2024-07-08 20:39:44 +02:00
f2015b062c added git-hooks dir 2024-07-08 20:25:54 +02:00
a8006f2e28 Update README.md 2024-06-16 08:26:31 +02:00
7fc1a17d3b only go back one character to include the new line before field 2024-03-03 17:05:08 +01:00
f248a1d279 Merge pull request 'unrecognized-event-bug' (#5) from unrecognized-event-bug into master
Reviewed-on: bjoernf/ics_analyzer#5
2024-03-03 16:40:56 +01:00
97208505d8 fixed bug and created script for running tests 2024-03-03 16:29:57 +01:00
75cacc3e04 redacted test file 2024-03-03 16:10:12 +01:00
0c1e64a16e added test file 2024-03-03 16:05:23 +01:00
19 changed files with 643 additions and 148 deletions

1
.gitignore vendored
View File

@ -1,4 +1,5 @@
icscli
.gitconfig
*.o
*.out
tags

View File

@ -1,14 +1,23 @@
Show upcoming events of an .ics file.\
## ics_cli
#### use case
- show upcoming events of an .ics file
- insert events in your calendar
#### installation
```
make
cd src/builddir
```
```
sudo make install
meson compile
```
```
meson install
```
#### usage
@ -31,9 +40,18 @@ icscli -h
#### uninstall
```
sudo make uninstall
sudo rm -f /usr/local/bin/icscli
```
#### git-hooks for ctags pre-commit
The developer has to actively enable git hooks:
```
git config --local core.hooksPath git-hooks
```
This will run `ctags` on every commit.
#### TODO
- more tests
- improve and automate unit testing
- add cli argument that will not show ongoing events, only upcoming events

29
git-hooks/pre-commit Executable file
View File

@ -0,0 +1,29 @@
#!/bin/bash
# make a dir called git-hooks in the top level of your repo
# put the following line in a Readme.md:
# git config --local core.hooksPath git-hooks
# the user has to actively enable git hooks
# this script has to be named "pre-commit" and should be placed
# in the core.hooksPath
if type ctags &>> /dev/null; then
ctags_installed=1
else
ctags_installed=0
fi
if ! (( ${ctags_installed} )); then
printf "Ctags is not installed! Git hooks are enabled and can only be executed if it is installed.\n"
printf "Commit was aborted.\n"
exit 1
fi
if ! ctags -R src/.; then
printf "Ctags failed.\n\n"
exit 1
else
printf "Ctags was successfully executed.\n"
fi
exit 0

View File

@ -1,42 +0,0 @@
CC = gcc
CFLAGS = -Wall -g -O0
LDFLAGS = -luuid
# List of all source files (assuming they're all in the same directory)
SRC_FILES = $(wildcard *.c)
# Generate a list of object files by replacing the .c extension with .o
OBJ_FILES = $(SRC_FILES:.c=.o)
EXECUTABLE = "icscli"
# linking
$(EXECUTABLE): $(OBJ_FILES)
gcc $(CFLAGS) $(OBJ_FILES) -o $(EXECUTABLE) $(LDFLAGS)
main.o: main.c
$(CC) $(CFLAGS) -c $<
# use implicit rule to compile C source files to object files
%.o: %.c %.h
$(CC) $(CFLAGS) -c $<
.PHONY:install
install: $(EXECUTABLE)
cp $(EXECUTABLE) /usr/local/bin/$(EXECUTABLE)
.PHONY:clean
clean:
-rm -f $(EXECUTABLE) *.o
.PHONY:uninstall
uninstall:
-rm /usr/local/bin/$(EXECUTABLE)
.PHONY:test
test:
./$(EXECUTABLE)
@echo
./$(EXECUTABLE) -h
@echo
./$(EXECUTABLE) -f ../tests/calendar.ics

View File

@ -5,94 +5,135 @@
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <assert.h>
// buffer needs to contain a string with a strlen of 15 (format: "xxxxxxxxTxxxxxx")
// or a strlen of 16 (format: "YYYYmmddTHHMMSSZ")
void get_date(char buffer[]) {
void get_date (char buffer[]) {
// add 1 because strlen does not include the null character
size_t buffer_size = strlen(buffer) + 1;
time_t my_unix_ts = time(NULL);
struct tm* my_tm_local = localtime(&my_unix_ts);
if (strlen(buffer) == 15) {
strftime(buffer, buffer_size, "%Y%m%dT%H%M%S", my_tm_local);
} else if (strlen(buffer) == 16) {
strftime(buffer, buffer_size, "%Y%m%dT%H%M%SZ", my_tm_local);
size_t buffer_size = strlen (buffer) + 1;
time_t my_unix_ts = time (NULL);
struct tm* my_tm_local = localtime (&my_unix_ts);
if (strlen (buffer) == 15) {
strftime (buffer, buffer_size, "%Y%m%dT%H%M%S", my_tm_local);
}
else if (strlen (buffer) == 16) {
strftime (buffer, buffer_size, "%Y%m%dT%H%M%SZ", my_tm_local);
}
}
// 20230823T194138 -> 2023-08-23 19:41:38
void pretty_print_date_time(char date_time[]) {
char *date = strtok(date_time, "T");
char *time = strtok(NULL, "T");
if (date == NULL) {
printf ("\nError: date points to NULL!\n");
exit(1);
// 20230823T194138Z -> 2023-08-23 19:41:38
// 20241209 -> 2024-12-09
// caller has to free() the returned char array
char* pretty_date_time (char date_time[]) {
// need one more char in the char pointer for null-termination
int pdt_len = 20;
if (!strchr (date_time, 'T')) {
pdt_len = 11;
}
printf ("%c%c%c%c-", date[0], date[1], date[2], date[3]);
printf ("%c%c-", date[4], date[5]);
printf ("%c%c", date[6], date[7]);
if (time != NULL) {
printf (" %c%c:", time[0], time[1]);
printf ("%c%c:", time[2], time[3]);
printf ("%c%c", time[4], time[5]);
char* pretty_dt = malloc (pdt_len);
memset(pretty_dt, '\0', pdt_len);
int dt_counter = 0;
if (!strcmp(date_time, "")) {
return pretty_dt;
}
for (int i = 0; i < (pdt_len-1); i++) {
if (i == 4 || i == 7) {
pretty_dt[i] = '-';
continue;
}
if (i == 13 || i == 16) {
pretty_dt[i] = ':';
continue;
}
if (date_time[dt_counter] == 'T') {
pretty_dt[i] = ' ';
} else {
pretty_dt[i] = date_time[dt_counter];
}
dt_counter++;
}
// null-terminate string
pretty_dt[strlen(pretty_dt)] = '\0';
return pretty_dt;
}
void marshall_date_time(char date_time[]) {
char transformed_string[strlen(date_time)];
void pretty_print_date_time (char date_time[]) {
char* pdt = pretty_date_time(date_time);
printf("%s", pdt);
free(pdt);
}
void marshall_date_time (char date_time[]) {
char transformed_string[strlen (date_time)];
int j = 0;
remove_nl_and_cr(date_time);
for (int i = 0; i<=strlen(date_time); i++) {
remove_nl_and_cr (date_time);
for (int i = 0; i <= strlen (date_time); i++) {
if (date_time[i] != ':' && date_time[i] != '-') {
if (date_time[i] == ' ') {
transformed_string[j] = 'T';
} else {
}
else {
transformed_string[j] = date_time[i];
}
j++;
}
}
memset(date_time, '\0', strlen(date_time));
strcpy(date_time, transformed_string);
memset (date_time, '\0', strlen (date_time));
strcpy (date_time, transformed_string);
}
void print_end_date(char end_date[], char start_date[]) {
time_t start_uts = transform_date_to_unix_ts(start_date);
time_t end_uts = transform_date_to_unix_ts(end_date);
double time_difference = difftime(end_uts, start_uts);
void print_end_date (char end_date[], char start_date[]) {
time_t start_uts = transform_date_to_unix_ts (start_date);
time_t end_uts = transform_date_to_unix_ts (end_date);
double time_difference = difftime (end_uts, start_uts);
// end_date is all day event
if (strlen(end_date) == 8) {
if (strlen (end_date) == 8) {
if (time_difference == 86400) {
return;
} else {
}
else {
printf (" - ");
end_uts -= 86400;
char *end_date_minus_one = transform_unix_ts_to_date(end_uts);
pretty_print_date_time(end_date_minus_one);
free(end_date_minus_one);
char* end_date_minus_one =
transform_unix_ts_to_date (end_uts);
pretty_print_date_time (end_date_minus_one);
free (end_date_minus_one);
return;
}
} else {
}
else {
// end_date is not an all day event
char *end_date_chunk = strtok(end_date, "T");
char *end_time_chunk = strtok(NULL, "T");
char *start_date_chunk = strtok(start_date, "T");
char* end_date_chunk = strtok (end_date, "T");
char* end_time_chunk = strtok (NULL, "T");
char* start_date_chunk = strtok (start_date, "T");
printf (" - ");
// only print the end date if it is not the same as the start date
if (strcmp(start_date_chunk, end_date_chunk) != 0) {
printf("%c%c%c%c-", end_date_chunk[0], end_date_chunk[1], \
end_date_chunk[2], end_date_chunk[3]);
printf("%c%c-", end_date_chunk[4], end_date_chunk[5]);
printf("%c%c ", end_date_chunk[6], end_date_chunk[7]);
if (strcmp (start_date_chunk, end_date_chunk) != 0) {
printf ("%c%c%c%c-", end_date_chunk[0],
end_date_chunk[1], end_date_chunk[2],
end_date_chunk[3]);
printf ("%c%c-", end_date_chunk[4],
end_date_chunk[5]);
printf ("%c%c ", end_date_chunk[6],
end_date_chunk[7]);
}
printf ("%c%c:", end_time_chunk[0], end_time_chunk[1]);
printf ("%c%c:", end_time_chunk[2], end_time_chunk[3]);
printf ("%c%c", end_time_chunk[4], end_time_chunk[5]);
@ -100,40 +141,41 @@ void print_end_date(char end_date[], char start_date[]) {
}
// YYYYmmdd -> unix timestamp
time_t transform_date_to_unix_ts(char date_str[]) {
time_t transform_date_to_unix_ts (char date_str[]) {
time_t unix_stamp = 0;
int date_only = atoi(date_str);
struct tm my_tm = {0};
int date_only = atoi (date_str);
struct tm my_tm = { 0 };
my_tm.tm_year = date_only / 10000 - 1900;
my_tm.tm_mon = (date_only % 10000) / 100 - 1;
my_tm.tm_mday = date_only % 100;
unix_stamp = mktime(&my_tm);
unix_stamp = mktime (&my_tm);
return unix_stamp;
}
// unix timestamp -> YYYYmmdd
// make sure to free the returned buffer
char *transform_unix_ts_to_date(time_t unix_ts) {
char *date_buffer = malloc(9);
struct tm *my_tm;
my_tm = localtime(&unix_ts);
strftime(date_buffer, 9, "%Y%m%d", my_tm);
char* transform_unix_ts_to_date (time_t unix_ts) {
char* date_buffer = malloc (9);
struct tm* my_tm;
my_tm = localtime (&unix_ts);
strftime (date_buffer, 9, "%Y%m%d", my_tm);
return date_buffer;
}
char *get_tz() {
char *timezone_path = malloc(256);
ssize_t bytes_read = readlink("/etc/localtime", timezone_path, 255);
char* get_tz () {
char* timezone_path = malloc (256);
ssize_t bytes_read = readlink ("/etc/localtime", timezone_path, 255);
if (bytes_read != -1) {
// Null-terminate the string
timezone_path[bytes_read] = '\0';
} else {
perror("readlink");
exit(1);
timezone_path[bytes_read] = '\0';
}
else {
perror ("readlink");
exit (1);
}
return timezone_path;

View File

@ -1,8 +1,11 @@
#pragma once
#include <time.h>
#include <string.h>
void get_date(char buffer[]);
char *get_tz();
char* pretty_date_time(char date_time[]);
void pretty_print_date_time(char date_time[]);
void marshall_date_time(char date_time[]);
void print_end_date(char end_date[], char start_date[]);

View File

@ -46,10 +46,12 @@ void free_list(struct event *head)
}
}
void print_upcoming(struct event *head, char current_start_date[], int show_all_events) {
// print_upcoming() also prints ongoing events
// because you usually also want to see those
void print_upcoming(struct event *head, char current_date[], int show_all_events) {
int i = 0;
while (head != NULL) {
if (strcmp(head->start_date, current_start_date) >= 0) {
if (strcmp(head->end_date, current_date) >= 0) {
pretty_print_date_time(head->start_date);
print_end_date(head->end_date, head->start_date);
printf("\nSUMMARY: %s\n", head->summary);

View File

@ -2,15 +2,7 @@
#include "parse_ics.h"
#include "cli_arg_parsing.h"
#include "date_time_handling.h"
#include "string_handling.h"
#include "read_until_nl.h"
#include "read_until_string.h"
#include "seek_string_a.h"
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <limits.h>
int main(int argc, char **argv) {
char *ics_path = "";
@ -18,33 +10,16 @@ int main(int argc, char **argv) {
get_cli_args(argc, argv, &ics_path, &show_all_events);
char my_event[8192] = "";
char unfolded_event[8192] = "";
int myfd = open(ics_path, O_RDONLY);
if (myfd == -1) {
perror ("Error opening file");
return 1;
}
// initialize linked list
struct event *head = NULL;
static char current_date[] = "xxxxxxxxTxxxxxx";
get_date(current_date);
printf ("Current date and time: ");
pretty_print_date_time(current_date);
printf ("\n\n");
while(read_until_nl(myfd, my_event)) {
if (strncmp(my_event, "BEGIN:VEVENT", 12) == 0) {
memset(my_event, '\0', sizeof(my_event));
read_until_string(myfd, my_event, "END:VEVENT");
unfolding_string(my_event, unfolded_event);
parse_event(unfolded_event, &head);
}
memset(my_event, '\0', sizeof(my_event));
}
// initialize linked list
struct event *head = NULL;
parse_ics_file(ics_path, &head);
print_upcoming(head, current_date, show_all_events);

5
src/meson.build Normal file
View File

@ -0,0 +1,5 @@
project('ics_cli', 'c')
executable('icscli', 'main.c', 'cli_arg_parsing.c', 'date_time_handling.c', 'insert_event.c', \
'list_handling.c', 'parse_ics.c', 'read_until_nl.c', 'read_until_string.c', 'seek_string_a.c', \
'string_handling.c', link_args : '-luuid', install: true, install_dir: '/usr/local/bin')

View File

@ -1,11 +1,15 @@
#include "list_handling.h"
#include "parse_ics.h"
#include "read_until_nl.h"
#include "read_until_string.h"
#include "string_handling.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void parse_event(char event_string[], struct event **head) {
char *start_date = strstr(event_string, "\nDTSTART");
@ -67,3 +71,32 @@ void unfolding_string(char *folded_string, char *unfolded_string) {
}
}
}
/* this function takes the head of an empty initialized event list
* and the path to the ics file
* it will "fill" the list
*/
void parse_ics_file(char *file_path, struct event **head) {
char my_event[8192] = "";
char unfolded_event[8192] = "";
int myfd = open(file_path, O_RDONLY);
if (myfd == -1) {
perror ("Error opening file");
exit(1);
}
while(read_until_nl(myfd, my_event)) {
if (strncmp(my_event, "BEGIN:VEVENT", 12) == 0) {
// include the BEGIN:EVENT to not loose the new line of first field
lseek(myfd, -1, SEEK_CUR);
memset(my_event, '\0', sizeof(my_event));
read_until_string(myfd, my_event, "END:VEVENT");
unfolding_string(my_event, unfolded_event);
parse_event(unfolded_event, head);
}
memset(my_event, '\0', sizeof(my_event));
}
close(myfd);
}

View File

@ -2,3 +2,4 @@
void parse_event(char event_string[], struct event **head);
void unfolding_string(char* folded_string, char* unfolded_string);
void parse_ics_file(char *file_path, struct event **head);

View File

@ -301,9 +301,9 @@ BEGIN:VEVENT
UID:ce6cc0e3ca1bb6a22783ace61034b12c57325b4e
DTSTAMP:20230719T152822Z
DTSTART;TZID=/freeassociation.sourceforge.net/Europe/Berlin:
20230831T180000
20241005T140000
DTEND;TZID=/freeassociation.sourceforge.net/Europe/Berlin:
20230831T220000
20241005T220000
SEQUENCE:3
SUMMARY:dentist
TRANSP:OPAQUE

297
tests/failed_cal.ics Normal file
View File

@ -0,0 +1,297 @@
BEGIN:VCALENDAR
CALSCALE:GREGORIAN
PRODID:-//Ximian//NONSGML Evolution Calendar//EN
VERSION:2.0
X-EVOLUTION-DATA-REVISION:2024-11-06T05:14:31.539689Z(0)
BEGIN:VTIMEZONE
TZID:UTC
BEGIN:STANDARD
DTSTART;VALUE=DATE:16010101
TZOFFSETFROM:+0000
TZOFFSETTO:+0000
END:STANDARD
END:VTIMEZONE
BEGIN:VTIMEZONE
TZID:Europe/Berlin
TZURL:http://tzurl.org/zoneinfo/Europe/Berlin
X-LIC-LOCATION:Europe/Berlin
BEGIN:DAYLIGHT
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
TZNAME:CEST
DTSTART:19810329T020000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
TZNAME:CET
DTSTART:19961027T030000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
END:STANDARD
BEGIN:STANDARD
TZOFFSETFROM:+005328
TZOFFSETTO:+0100
TZNAME:CET
DTSTART:18930401T000000
RDATE:18930401T000000
END:STANDARD
BEGIN:DAYLIGHT
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
TZNAME:CEST
DTSTART:19160430T230000
RDATE:19160430T230000
RDATE:19170416T020000
RDATE:19180415T020000
RDATE:19400401T020000
RDATE:19430329T020000
RDATE:19440403T020000
RDATE:19450402T020000
RDATE:19460414T020000
RDATE:19470406T030000
RDATE:19480418T020000
RDATE:19490410T020000
RDATE:19800406T020000
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
TZNAME:CET
DTSTART:19161001T010000
RDATE:19161001T010000
RDATE:19170917T030000
RDATE:19180916T030000
RDATE:19421102T030000
RDATE:19431004T030000
RDATE:19441002T030000
RDATE:19451118T030000
RDATE:19461007T030000
RDATE:19471005T030000
RDATE:19481003T030000
RDATE:19491002T030000
RDATE:19800928T030000
RDATE:19810927T030000
RDATE:19820926T030000
RDATE:19830925T030000
RDATE:19840930T030000
RDATE:19850929T030000
RDATE:19860928T030000
RDATE:19870927T030000
RDATE:19880925T030000
RDATE:19890924T030000
RDATE:19900930T030000
RDATE:19910929T030000
RDATE:19920927T030000
RDATE:19930926T030000
RDATE:19940925T030000
RDATE:19950924T030000
END:STANDARD
BEGIN:DAYLIGHT
TZOFFSETFROM:+0200
TZOFFSETTO:+0300
TZNAME:CEMT
DTSTART:19450524T020000
RDATE:19450524T020000
RDATE:19470511T030000
END:DAYLIGHT
BEGIN:DAYLIGHT
TZOFFSETFROM:+0300
TZOFFSETTO:+0200
TZNAME:CEST
DTSTART:19450924T030000
RDATE:19450924T030000
RDATE:19470629T030000
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:+0100
TZOFFSETTO:+0100
TZNAME:CET
DTSTART:19460101T000000
RDATE:19460101T000000
RDATE:19800101T000000
END:STANDARD
END:VTIMEZONE
BEGIN:VTIMEZONE
TZID:W. Europe Standard Time
BEGIN:STANDARD
DTSTART:16010101T030000
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:16010101T020000
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VTIMEZONE
TZID:/freeassociation.sourceforge.net/Europe/Berlin
X-LIC-LOCATION:Europe/Berlin
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+005328
TZOFFSETTO:+0100
DTSTART:18930401T000000
END:STANDARD
BEGIN:DAYLIGHT
TZNAME:CEST
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
DTSTART:19160430T230000
END:DAYLIGHT
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
DTSTART:19161001T010000
END:STANDARD
BEGIN:DAYLIGHT
TZNAME:CEST
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
DTSTART:19170416T020000
RRULE:FREQ=YEARLY;UNTIL=19180415T010000Z;BYDAY=3MO;BYMONTH=4
END:DAYLIGHT
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
DTSTART:19170917T030000
RRULE:FREQ=YEARLY;UNTIL=19180916T010000Z;BYDAY=3MO;BYMONTH=9
END:STANDARD
BEGIN:DAYLIGHT
TZNAME:CEST
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
DTSTART:19400401T020000
RDATE:19430329T020000
END:DAYLIGHT
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
DTSTART:19421102T030000
END:STANDARD
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
DTSTART:19431004T030000
RRULE:FREQ=YEARLY;UNTIL=19441002T010000Z;BYDAY=1MO;BYMONTH=10
END:STANDARD
BEGIN:DAYLIGHT
TZNAME:CEST
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
DTSTART:19440403T020000
RRULE:FREQ=YEARLY;UNTIL=19450402T010000Z;BYDAY=1MO;BYMONTH=4
END:DAYLIGHT
BEGIN:DAYLIGHT
TZNAME:CEMT
TZOFFSETFROM:+0200
TZOFFSETTO:+0300
DTSTART:19450524T020000
END:DAYLIGHT
BEGIN:DAYLIGHT
TZNAME:CEST
TZOFFSETFROM:+0300
TZOFFSETTO:+0200
DTSTART:19450924T030000
END:DAYLIGHT
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
DTSTART:19451118T030000
RDATE:19461007T030000
END:STANDARD
BEGIN:DAYLIGHT
TZNAME:CEST
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
DTSTART:19460414T020000
END:DAYLIGHT
BEGIN:DAYLIGHT
TZNAME:CEST
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
DTSTART:19470406T030000
END:DAYLIGHT
BEGIN:DAYLIGHT
TZNAME:CEMT
TZOFFSETFROM:+0200
TZOFFSETTO:+0300
DTSTART:19470511T030000
END:DAYLIGHT
BEGIN:DAYLIGHT
TZNAME:CEST
TZOFFSETFROM:+0300
TZOFFSETTO:+0200
DTSTART:19470629T030000
END:DAYLIGHT
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
DTSTART:19471005T030000
RRULE:FREQ=YEARLY;UNTIL=19491002T010000Z;BYDAY=1SU;BYMONTH=10
END:STANDARD
BEGIN:DAYLIGHT
TZNAME:CEST
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
DTSTART:19480418T020000
RDATE:19490410T020000
RDATE:19800406T020000
END:DAYLIGHT
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
DTSTART:19800928T030000
RRULE:FREQ=YEARLY;UNTIL=19950924T010000Z;BYDAY=-1SU;BYMONTH=9
END:STANDARD
BEGIN:DAYLIGHT
TZNAME:CEST
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
DTSTART:19810329T020000
RRULE:FREQ=YEARLY;UNTIL=20370329T010000Z;BYDAY=-1SU;BYMONTH=3
END:DAYLIGHT
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
DTSTART:19961027T030000
RRULE:FREQ=YEARLY;UNTIL=20361026T010000Z;BYDAY=-1SU;BYMONTH=10
END:STANDARD
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
DTSTART:20371025T030000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
END:STANDARD
BEGIN:DAYLIGHT
TZNAME:CEST
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
DTSTART:20380328T020000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
DTSTAMP:20251106T051352Z
DTSTART:20251215T080000Z
DTEND:20251215T083000Z
SUMMARY:Interview with John Doe
DESCRIPTION:foo
LOCATION:teams meeting
UID:35a4a000-dc20-47c6-b2b0-b4cbbfbb114e
CREATED:20241106T051431Z
LAST-MODIFIED:20241106T051431Z
END:VEVENT
END:VCALENDAR

15
tests/run_tests.sh Executable file
View File

@ -0,0 +1,15 @@
#!/bin/bash
mapfile -t files < <(find ../tests/. -name "*.ics")
i=1
for file in ${files[@]}; do
echo "TEST ${i}: ${file}"
echo "===================================================="
../src/icscli -f "${file}"
echo "===================================================="
echo
((i++))
done
exit 0

View File

@ -0,0 +1,12 @@
BEGIN:VCALENDAR
BEGIN:VEVENT
DTSTART:20240413T070000Z
DTEND:20240413T083000Z
DTSTAMP:20240229T171200Z
SUMMARY:XYZ Exam (XYZ)
DESCRIPTION:XYZ Exam (XYZ)
UID:61792
CREATED:20240229T171308Z
LAST-MODIFIED:20240229T171308Z
END:VEVENT
END:VCALENDAR

16
unit-tests/meson.build Normal file
View File

@ -0,0 +1,16 @@
project('ics_cli', 'c')
t1e = executable('test_print_upcoming', 'test_print_upcoming.c', '../src/parse_ics.c', '../src/string_handling.c', \
'../src/list_handling.c', '../src/date_time_handling.c', '../src/read_until_string.c', '../src/read_until_nl.c')
test('test print_upcoming', t1e)
t2e = executable('test_pretty_print_date_time', 'test_pretty_print_date_time.c', '../src/string_handling.c', \
'../src/date_time_handling.c', '../src/read_until_string.c', '../src/read_until_nl.c')
test('test pretty_print_date_time', t2e)
t3e = executable('test_parse_ics_file', 'test_parse_ics_file.c', '../src/parse_ics.c', '../src/string_handling.c', '../src/list_handling.c', \
'../src/date_time_handling.c', '../src/read_until_string.c', '../src/read_until_nl.c')
test('test parse_ics_file', t3e)

View File

@ -0,0 +1,14 @@
#include "../src/list_handling.h"
#include "../src/parse_ics.h"
#include <stdio.h>
int main() {
// initialize empty list
struct event *head = NULL;
parse_ics_file("../../tests/calendar.ics", &head);
parse_ics_file("../../tests/failed_cal.ics", &head);
return 0;
}

View File

@ -0,0 +1,50 @@
#include "../src/date_time_handling.h"
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int main() {
// 1
char* pdt = pretty_date_time("20251215T080000Z");
assert(!strcmp(pdt, "2025-12-15 08:00:00"));
free(pdt);
// 2
char* pdt2 = pretty_date_time("20251215T080000");
assert(!strcmp(pdt2, "2025-12-15 08:00:00"));
free(pdt2);
// 3
char* pdt3 = pretty_date_time("");
assert(!strcmp(pdt3, ""));
free(pdt3);
// 4
char* pdt4 = pretty_date_time("20251215");
assert(!strcmp(pdt4, "2025-12-15"));
free(pdt4);
// 5
char current_date[] = "20240710T103000";
printf("current_date: %s\n", current_date);
printf("strlen(current_date): %ld\n\n", strlen(current_date));
pretty_print_date_time(current_date);
printf("\n\ncurrent_date: %s\n", current_date);
printf("strlen(current_date): %ld\n", strlen(current_date));
pretty_print_date_time("20251215T080000Z");
printf("\n");
return 0;
}

View File

@ -0,0 +1,24 @@
#include "../src/list_handling.h"
#include "../src/parse_ics.h"
#include <stdio.h>
int main() {
// initialize empty list
struct event *head = NULL;
// 1
printf("\nTesting tests/failed_cal.ics:\n\n");
char *current_date = "20240710T113000";
printf("DEBUG - current_date: %s\n\n", current_date);
parse_ics_file("../../tests/failed_cal.ics", &head);
print_upcoming(head, current_date, 0);
// 2
printf("\nTesting tests/calendar.ics:\n\n");
parse_ics_file("../../tests/calendar.ics", &head);
print_upcoming(head, current_date, 0);
}