448 lines
11 KiB
C
448 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 = (char *) malloc(sizeof(char) * length); //[length];
|
|
char* _buffer = buffer;
|
|
while (fgets(buffer, length, file) != NULL && strcmp(buffer, "") != 0) {
|
|
printf("Command read: %s", buffer); // Buffer has newline.
|
|
|
|
char* tokens[5];
|
|
char* token;
|
|
int i = 0;
|
|
while ((token = strsep(&buffer, ",")) != NULL) { // Inline hax.
|
|
tokens[i] = strdup(token); // creates a duplicate.
|
|
//trimString(tokens[i]);
|
|
i++;
|
|
}
|
|
|
|
/*if (tokens[0] == NULL) { // Try without the space!
|
|
i = 0;
|
|
while ((token = strsep(&buffer, ",")) != NULL) {
|
|
tokens[i] = strdup(token);
|
|
i++;
|
|
}
|
|
}*/
|
|
|
|
if (strcmp(tokens[0], "ADD") == 0) {
|
|
if (add(tokens[1], tokens[2], tokens[3], atoi(tokens[4])) != 0)
|
|
return 6;
|
|
} else if (strcmp(tokens[0], "LOOKUP") == 0) {
|
|
if (lookup(tokens[1], tokens[2], output) != 0)
|
|
return 6;
|
|
} else if (strcmp(tokens[0], "DISPLAY") == 0) {
|
|
if (display(tokens[1], atoi(tokens[2]), atoi(tokens[3]), output) != 0)
|
|
return 6;
|
|
} else if (strcmp(tokens[0], "EDIT") == 0) {
|
|
if (edit(atoi(tokens[1]), tokens[2], tokens[3]) != 0)
|
|
return 6;
|
|
} else if (strcmp(tokens[0], "REMOVE") == 0) {
|
|
if (removeId(atoi(tokens[1]) != 0))
|
|
return 6;
|
|
}
|
|
|
|
// Free from strsep and strdup.
|
|
for (i = 0; i < 5; i++) {
|
|
if (tokens[i] != NULL) {
|
|
free(tokens[i]);
|
|
tokens[i] = NULL;
|
|
}
|
|
}
|
|
|
|
buffer = _buffer; // Realign the buffer. //(char *) malloc(sizeof(char) * length);
|
|
}
|
|
|
|
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 = (char *) malloc(sizeof(char) * length); //[length];
|
|
char* _buffer = buffer;
|
|
|
|
while (fgets(buffer, length, file) != NULL && strcmp(buffer, "") != 0) {
|
|
printf("Line read: %s", buffer); // buffer contains newline.
|
|
|
|
char* tokens[4];
|
|
char* token;
|
|
int i = 0;
|
|
while ((token = strsep(&buffer, ",")) != NULL) { // Inline hax.
|
|
tokens[i] = strdup(token);
|
|
//trimString(tokens[i]);
|
|
i++;
|
|
}
|
|
|
|
/*if (tokens[0] == NULL) { // Try without the space!
|
|
i = 0;
|
|
while ((token = strsep(&buffer, ",")) != NULL) {
|
|
tokens[i] = strdup(token);
|
|
i++;
|
|
}
|
|
}*/
|
|
|
|
if (add(tokens[0], tokens[1], tokens[2], atoi(tokens[3])) != 0)
|
|
return 6;
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
if (tokens[i] != NULL) {
|
|
free(tokens[i]);
|
|
tokens[i] = NULL;
|
|
}
|
|
}
|
|
|
|
buffer = _buffer;//buffer = (char *) malloc(sizeof(char) * length);
|
|
}
|
|
printf("End of file.\n");
|
|
|
|
if (fclose(file) != 0) {
|
|
return 4;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int save(char* filename) {
|
|
if (filename == NULL) {
|
|
return 1;
|
|
}
|
|
|
|
FILE* file = fopen(filename, "w");
|
|
if (file == NULL)
|
|
return 2;
|
|
|
|
Movie* target = head;
|
|
while (target != NULL) {
|
|
fprintf(file, "%s, %s, %s, %d\n", target->title, target->date, target->director, target->id);
|
|
if (target->next != NULL)
|
|
target = target->next;
|
|
}
|
|
|
|
if (fclose(file) != 0) {
|
|
return 3;
|
|
}
|
|
|
|
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_CASEFOLD);
|
|
} else if (strcmp(feature, "DATE") == 0) {
|
|
success += fnmatch(data, target->date, FNM_CASEFOLD);
|
|
} else if (strcmp(feature, "DIRECTOR") == 0) {
|
|
success += fnmatch(data, target->director, FNM_CASEFOLD);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
void trimString(char* str) {
|
|
|
|
/*int length = 0;
|
|
int start = -1;
|
|
int end = -1;
|
|
int i;
|
|
for (i = 0; str[i] != '\0'; i++) {
|
|
if (str[i] != ' ' && start == -1) {
|
|
start = i;
|
|
}
|
|
}
|
|
length = i;
|
|
|
|
int j;
|
|
for (j = length - 1; j > start; j--) {
|
|
if (str[j] != ' ' && end == -1) {
|
|
end = j;
|
|
break;
|
|
}
|
|
}
|
|
|
|
char* str2 = (char *) malloc(sizeof(char) * (end - start + 1));
|
|
strncpy(str2, str + start, end - start + 1);
|
|
str2[end + 1] = '\0';
|
|
return str2;*/
|
|
|
|
char* firstNoSpace = str;
|
|
while (*firstNoSpace != '\0' && *firstNoSpace == ' ') {
|
|
++firstNoSpace;
|
|
}
|
|
|
|
size_t len = strlen(firstNoSpace) + 1;
|
|
memmove(str, firstNoSpace, len);
|
|
|
|
char* endOfStr = str + len;
|
|
|
|
while (str < endOfStr && *endOfStr == ' ') {
|
|
--endOfStr;
|
|
}
|
|
|
|
*endOfStr = '\0';
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|