440 lines
11 KiB
C
440 lines
11 KiB
C
#include "miniDB.h"
|
|
|
|
Movie* head = NULL;
|
|
int size = 0;
|
|
|
|
int main(int argc, char** argv) {
|
|
if (argc < 4) {
|
|
printf("Usage: miniDB <database> <commands> <output>\n");
|
|
} else {
|
|
if (load(argv[1]) != 0) {
|
|
printf("Error while loading database from '%s'.\n", argv[1]);
|
|
return 2;
|
|
}
|
|
if (read(argv[2], argv[3]) != 0) {
|
|
printf("Error while reading commands from '%s'.\n", argv[2]);
|
|
return 3;
|
|
}
|
|
if (save(argv[1]) != 0) {
|
|
printf("Error while saving database to '%s'.\n", argv[1]);
|
|
return 4;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int read(char* filename, char* fileOut) {
|
|
if (filename == NULL) {
|
|
return 1;
|
|
}
|
|
|
|
FILE* file = fopen(filename, "r");
|
|
if (file == NULL)
|
|
return 2;
|
|
|
|
FILE* output = fopen(fileOut, "w"); // If appending, switch to "a".
|
|
if (output == NULL)
|
|
return 3;
|
|
|
|
int length = 512*3;
|
|
char buffer[length];
|
|
|
|
while (fgets(buffer, length, file) != NULL && strcmp(buffer, "") != 0) {
|
|
printf("Command read: %s\n", buffer);
|
|
|
|
char* token = strtok(buffer, ", ");
|
|
if (token == NULL) {
|
|
token = strtok(buffer, ",");
|
|
if (token == NULL)
|
|
return 4;
|
|
}
|
|
|
|
if (strcmp(token, "ADD") == 0) {
|
|
token = strtok(buffer, NULL);
|
|
if (token == NULL)
|
|
return 5;
|
|
char* title = strdup(token);
|
|
|
|
token = strtok(buffer, NULL);
|
|
if (token == NULL)
|
|
return 5;
|
|
char* date = strdup(token);
|
|
|
|
token = strtok(buffer, NULL);
|
|
if (token == NULL)
|
|
return 5;
|
|
char* director = strdup(token);
|
|
|
|
token = strtok(buffer, NULL);
|
|
if (token == NULL)
|
|
return 5;
|
|
int id = atoi(token);
|
|
|
|
if (add(title, date, director, id) != 0)
|
|
return 6;
|
|
|
|
free(title);
|
|
free(date);
|
|
free(director);
|
|
title = NULL;
|
|
date = NULL;
|
|
director = NULL;
|
|
} else if (strcmp(token, "LOOKUP") == 0) {
|
|
token = strtok(buffer, NULL);
|
|
if (token == NULL)
|
|
return 5;
|
|
char* feature = strdup(token);
|
|
|
|
token = strtok(buffer, NULL);
|
|
if (token == NULL)
|
|
return 5;
|
|
char* data = strdup(token);
|
|
|
|
if (lookup(feature, data, output) != 0)
|
|
return 6;
|
|
|
|
free(feature);
|
|
free(data);
|
|
feature = NULL;
|
|
data = NULL;
|
|
} else if (strcmp(token, "DISPLAY") == 0) {
|
|
token = strtok(buffer, NULL);
|
|
if (token == NULL)
|
|
return 5;
|
|
char* feature = strdup(token);
|
|
|
|
token = strtok(buffer, NULL);
|
|
if (token == NULL)
|
|
return 5;
|
|
int order = atoi(token);
|
|
|
|
token = strtok(buffer, NULL);
|
|
if (token == NULL)
|
|
return 5;
|
|
int max = atoi(token);
|
|
|
|
if (display(feature, order, max, output) != 0)
|
|
return 6;
|
|
free(feature);
|
|
feature = NULL;
|
|
} else if (strcmp(token, "EDIT") == 0) {
|
|
token = strtok(buffer, NULL);
|
|
if (token == NULL)
|
|
return 5;
|
|
int id = atoi(token);
|
|
|
|
token = strtok(buffer, NULL);
|
|
if (token == NULL)
|
|
return 5;
|
|
char* feature = strdup(token);
|
|
|
|
token = strtok(buffer, NULL);
|
|
if (token == NULL)
|
|
return 5;
|
|
char* data = strdup(token);
|
|
|
|
if (edit(id, feature, data) != 0)
|
|
return 6;
|
|
|
|
free(feature);
|
|
free(data);
|
|
feature = NULL;
|
|
data = NULL;
|
|
} else if (strcmp(token, "REMOVE") == 0) {
|
|
token = strtok(buffer, NULL);
|
|
if (token == NULL)
|
|
return 5;
|
|
int id = atoi(token);
|
|
|
|
if (removeId(id) != 0)
|
|
return 6;
|
|
}
|
|
}
|
|
|
|
printf("End of commands.\n");
|
|
|
|
if (fclose(file) != 0)
|
|
return 7;
|
|
if (fclose(output) != 0)
|
|
return 8;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int load(char* filename) {
|
|
if (filename == NULL) {
|
|
return 1;
|
|
}
|
|
|
|
FILE* file = fopen(filename, "r");
|
|
if (file == NULL)
|
|
return 2;
|
|
|
|
int length = 512*3; // HOLD ALL THE CHARS!
|
|
char buffer[length];
|
|
|
|
while (fgets(buffer, length, file) != NULL && strcmp(buffer, "") != 0) {
|
|
printf("Line read: %s\n", buffer);
|
|
|
|
char* token = strtok(buffer, ", ");
|
|
if (token == NULL) {
|
|
token = strtok(buffer, ",");
|
|
if (token == NULL)
|
|
return 3;
|
|
} // Now that it's kicked off with the correct delimeter...
|
|
|
|
char* title = strdup(token);
|
|
|
|
token = strtok(buffer, NULL);
|
|
if (token == NULL)
|
|
return 5;
|
|
char* date = strdup(token);
|
|
|
|
token = strtok(buffer, NULL);
|
|
if (token == NULL)
|
|
return 5;
|
|
char* director = strdup(token);
|
|
|
|
token = strtok(buffer, NULL);
|
|
if (token == NULL)
|
|
return 5;
|
|
int id = atoi(token);
|
|
|
|
if (add(title, date, director, id) != 0)
|
|
return 6;
|
|
|
|
free(title);
|
|
free(date);
|
|
free(director);
|
|
title = NULL;
|
|
date = NULL;
|
|
director = NULL;
|
|
}
|
|
printf("End of file.\n");
|
|
|
|
if (fclose(file) != 0) {
|
|
return 4;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
Movie* get(int id) {
|
|
if (head == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
Movie* target = head;
|
|
while (target->id != id) {
|
|
if (target->next != NULL) {
|
|
target = target->next;
|
|
} else
|
|
return NULL;
|
|
}
|
|
|
|
return target;
|
|
}
|
|
|
|
int add(char* title, char* date, char* director, int id) {
|
|
if (title == NULL || date == NULL || director == NULL)
|
|
return 1;
|
|
|
|
Movie* mov = (Movie *) malloc(sizeof(Movie));
|
|
if (mov == NULL)
|
|
return 2;
|
|
|
|
if (head == NULL) {
|
|
head = mov;
|
|
} else {
|
|
Movie* target = head;
|
|
while (target->next != NULL) {
|
|
target = target->next;
|
|
}
|
|
target->next = mov;
|
|
mov->next = NULL;
|
|
}
|
|
|
|
size++;
|
|
|
|
mov->title = strdup(title);
|
|
mov->date = strdup(date);
|
|
mov->director = strdup(director);
|
|
mov->id = id;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int edit(int id, char* feature, char* data) {
|
|
if (feature == NULL || data == NULL)
|
|
return 1;
|
|
|
|
Movie* mov = get(id);
|
|
if (mov == NULL)
|
|
return 2;
|
|
|
|
if (strcmp(feature, "ID") == 0) {
|
|
mov->id = atoi(data);
|
|
} else if (strcmp(feature, "TITLE") == 0) {
|
|
free(mov->title);
|
|
mov->title = strdup(data);
|
|
} else if (strcmp(feature, "DATE") == 0) {
|
|
free(mov->date);
|
|
mov->date = strdup(data);
|
|
} else if (strcmp(feature, "DIRECTOR") == 0) {
|
|
free(mov->director);
|
|
mov->director = strdup(data);
|
|
} else {
|
|
return 3;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int removeId(int id) {
|
|
if (head == NULL) {
|
|
return 1;
|
|
}
|
|
|
|
Movie* prev = NULL;
|
|
Movie* target = head;
|
|
while (target->id != id) {
|
|
if (target->next != NULL) {
|
|
prev = target;
|
|
target = target->next;
|
|
} else
|
|
return 2;
|
|
}
|
|
|
|
if (prev == NULL && target != NULL) {
|
|
head = target->next; // If next is NULL, that's okay.
|
|
} else {
|
|
prev->next = target->next; // Again, a NULL next is okay.
|
|
}
|
|
|
|
size--;
|
|
|
|
free(target->title);
|
|
free(target->date);
|
|
free(target->director);
|
|
target->title = NULL;
|
|
target->date = NULL;
|
|
target->director = NULL;
|
|
free(target);
|
|
target = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int lookup(char* feature, char* data, FILE* out) {
|
|
if (feature == NULL || data == NULL || head == NULL) {
|
|
return 1;
|
|
}
|
|
|
|
printf("LOOKUP, %s, %s\n", feature, data);
|
|
fprintf(out, "LOOKUP, %s, %s\n", feature, data);
|
|
|
|
Movie* target = head;
|
|
|
|
while (target != NULL) {
|
|
char success = 0;
|
|
|
|
if (strcmp(feature, "ID") == 0) {
|
|
if (get(atoi(data)) == NULL)
|
|
success++;
|
|
} else if (strcmp(feature, "TITLE") == 0) {
|
|
success += fnmatch(data, target->title, "FNM_NOCASE");
|
|
} else if (strcmp(feature, "DATE") == 0) {
|
|
success += fnmatch(data, target->date, "FNM_NOCASE");
|
|
} else if (strcmp(feature, "DIRECTOR") == 0) {
|
|
success += fnmatch(data, target->director, "FNM_NOCASE");
|
|
}
|
|
|
|
if (success == 0) {
|
|
printf("%s, %s, %s, %d\n", target->title, target->date, target->director, target->id);
|
|
fprintf(out, "%s, %s, %s, %d\n", target->title, target->date, target->director, target->id);
|
|
}
|
|
|
|
target = target->next;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int display(char* feature, int order, int max, FILE* out) {
|
|
if (feature == NULL)
|
|
return 1;
|
|
|
|
printf("DISPLAY, %s, %d, %d\n", feature, order, max);
|
|
fprintf(out, "DISPLAY, %s, %d, %d\n", feature, order, max);
|
|
|
|
if (max <= 0) {
|
|
return 0;
|
|
} else if (max > size) {
|
|
max = size;
|
|
}
|
|
|
|
Movie* target = head;
|
|
int count = 0; // Number of Movies displayed.
|
|
int* countedIds = calloc(max, sizeof(int)); // Set 'em all to 0.
|
|
Movie* best = target;
|
|
|
|
while (count < max) {
|
|
while (target != NULL) {
|
|
if (idIncluded(target->id, countedIds, max) == 0) { // ID not counted yet. (Ignore counted ones)
|
|
if (order == 0) { // Ascending: smallest first.
|
|
if ( (strcmp(feature, "TITLE") == 0 && strcmp(target->title, best->title) < 0)
|
|
|| (strcmp(feature, "DATE") == 0 && strcmp(target->date, best->date) < 0)
|
|
|| (strcmp(feature, "DIRECTOR") == 0 && strcmp(target->director, best->director) < 0)
|
|
|| (strcmp(feature, "ID") == 0 && target->id < best->id) ) {
|
|
best = target;
|
|
}
|
|
} else { // Descending: largest first.
|
|
if ( (strcmp(feature, "TITLE") == 0 && strcmp(target->title, best->title) > 0)
|
|
|| (strcmp(feature, "DATE") == 0 && strcmp(target->date, best->date) > 0)
|
|
|| (strcmp(feature, "DIRECTOR") == 0 && strcmp(target->director, best->director) > 0)
|
|
|| (strcmp(feature, "ID") == 0 && target->id > best->id) ) {
|
|
best = target;
|
|
}
|
|
}
|
|
}
|
|
target = target->next; // Okay if target is NULL.
|
|
}
|
|
|
|
countedIds[count] = best->id;
|
|
printf("%s, %s, %s, %d\n", best->title, best->date, best->director, best->id);
|
|
fprintf(out, "%s, %s, %s, %d\n", best->title, best->date, best->director, best->id);
|
|
count++;
|
|
|
|
// Set best to an uncounted ID.
|
|
Movie* nextBest = head;
|
|
while (nextBest != NULL) {
|
|
if (idIncluded(nextBest->id, countedIds, max) == 0) {
|
|
best = nextBest;
|
|
break;
|
|
} else
|
|
nextBest = nextBest->next;
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int idIncluded(int id, int* idList, size_t len) {
|
|
for (int i = 0; i < len; i++) {
|
|
if (id == idList[i])
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|