Planer događaja sa vremenskim upitima

Data je datoteka sa dnevnim događajima i druga datoteka sa vremenskim upitima (intervali). Potrebno je za svaki upit ispisati sve događaje koji podpadaju pod dati vremenski interval.

Događaji se čuvaju u strukturama, sortiraju po vremenu (qsort), a za svaki upit početak intervala se pronalazi binarnom pretragom.

Napomena: Nije moguće koristiti bsearch kao lower_bound, jer bsearch vraća samo tačan podudaran element, a ne prvi veći ili jednak.

Ulaz

Program se pokreće komandnom linijom:

./planner <dogadjaji_fajl> <upiti_fajl>

dogadjaji_fajl sadrži po jedan događaj u liniji:

HH:MM <naziv_dogadjaja>

<naziv_dogadjaja> je ostatak linije nakon vremena (moze sadrzati razmake) i neće biti duži od $256$ karaktera.

upiti_fajl sadrži po jedan upit u liniji:

HH:MM HH:MM

Vremena su u $24h$ formatu.

Izlaz

Na standardni izlaz, za svaki upit, redom, ispisati sve nazive događaje (razdvojene znakom ;) koji leze u intervalu. Ukoliko nama događaja u datom intervalu, ispisati NONE.

Greške:

Primer

events.txt

09:00 Standup
12:30 Lunch with team
15:00 Demo
18:00 Gym

queries.txt

08:00 12:00
12:00 16:00
19:00 20:00

command line

./planner events.txt queries.txt

stdout

Standup;Lunch with team
Demo
NONE

Primer

command line

./planner missing.txt queries.txt

stderr

Error: Could not open file missing.txt

Primer

command line

./planner events.txt

stderr

Error: Invalid number of arguments.

Primer

events.txt

command line

./planner events.txt queries.txt

stderr

Error: No data.

Rešenje

main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define INIT_CAPACITY 8
#define MAX_LINE_SIZE 512

typedef struct {
	int minutes;
	char *title;
} Event;

static int cmp_event(const void *a, const void *b)
{
	const Event *event_a = a;
	const Event *event_b = b;

	return event_a->minutes - event_b->minutes;
}

static size_t lower_bound_event(const Event *events, size_t n, int target)
{
	size_t l = 0, r = n;
	while (l < r) {
		size_t m = l + (r - l) / 2;
		if (events[m].minutes < target) {
			l = m + 1;
		} else {
			r = m;
		}
	}
	return l;
}

int main(int argc, char *argv[])
{
	if (argc != 3) {
		fprintf(stderr, "Error: Invalid number of arguments.\n");
		exit(EXIT_FAILURE);
	}

	FILE *fevents = fopen(argv[1], "r");
	if (fevents == NULL) {
		fprintf(stderr, "Error: Could not open file %s\n", argv[1]);
		exit(EXIT_FAILURE);
	}
	

	size_t cap = INIT_CAPACITY, n = 0;
	Event *events = malloc(cap * sizeof (Event));
	if (events == NULL) {
		fclose(fevents);
		exit(EXIT_FAILURE);
	}

	char line[MAX_LINE_SIZE];
	while (fgets(line, sizeof(line), fevents) != NULL) {
		if (n == cap) {
			cap *= 2;
			Event *tmp = realloc(events, cap * sizeof(Event));
			if (tmp == NULL) {
				for (size_t i = 0; i < n; i++) {
					free(events[i].title);
				}
				free(events);
				fclose(fevents);
				exit(EXIT_FAILURE);
			}
			events = tmp;
		}

		int hour, minute;
		sscanf(line, "%2d:%2d", &hour, &minute);

		events[n].minutes = hour * 60 + minute;

		size_t title_size = strlen(line + 6) + 1; // +1 for null terminator
		events[n].title = malloc(title_size * sizeof (char));
		if (events[n].title == NULL) {
			for (size_t i = 0; i < n; i++) {
				free(events[i].title);
			}
			free(events);
			fclose(fevents);
			exit(EXIT_FAILURE);
		}
		strcpy(events[n].title, line + 6);
		// Remove newline character if present; -2 because fgets includes newline
		if (title_size > 1 && events[n].title[title_size - 2] == '\n') {
			events[n].title[title_size - 2] = '\0';
		}

		n++;
	}
	fclose(fevents);

	if (n == 0) {
		fprintf(stderr, "Error: No data.\n");
		free(events);
		exit(EXIT_FAILURE);
	}

	qsort(events, n, sizeof (Event), cmp_event);

	FILE *fqueries = fopen(argv[2], "r");
	if (fqueries == NULL) {
		fprintf(stderr, "Error: Could not open file %s\n", argv[2]);
		for (size_t i = 0; i < n; i++) {
			free(events[i].title);
		}
		free(events);
		exit(EXIT_FAILURE);
	}

	int start_h, start_m, end_h, end_m;
	while (fscanf(fqueries, "%2d:%2d %2d:%2d", &start_h, &start_m, &end_h, &end_m) != EOF) {
		int start_time = start_h * 60 + start_m;
		int end_time = end_h * 60 + end_m;

		size_t idx = lower_bound_event(events, n, start_time);
		int printed = 0;
		for (size_t i = idx; i < n && events[i].minutes <= end_time; i++) {
			if (printed) {
				printf(";");
			}
			printf("%s", events[i].title);
			printed = 1;
		}
		if (!printed) {
			printf("NONE");
		}
		printf("\n");
	}
	
	fclose(fqueries);

	for (size_t i = 0; i < n; i++) {
		free(events[i].title);
	}

	free(events);

	exit(EXIT_SUCCESS);
}