diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/database.c | 142 | ||||
| -rw-r--r-- | src/main.c | 46 | ||||
| -rw-r--r-- | src/parser.c | 445 | ||||
| -rw-r--r-- | src/repl.c | 13 | ||||
| -rw-r--r-- | src/storage.c | 32 |
5 files changed, 590 insertions, 88 deletions
diff --git a/src/database.c b/src/database.c index 461e02d..40ea86d 100644 --- a/src/database.c +++ b/src/database.c @@ -16,6 +16,34 @@ database *newDatabase(char *name){ return db; } +int freeDatabase(database **db){ + deleteLtable(&(*db)->lfiles); + deleteLtable(&(*db)->ltags); + deleteCtable(&(*db)->cfiles); + deleteCtable(&(*db)->ctags); + deleteTree(&(*db)->hfiles); + deleteTree(&(*db)->htags); + deleteMtable(&(*db)->map); + free((*db)->name); + free(*db); + *db = NULL; + return 0; +} + +int deleteDatabase(database **db){ + if(*db == NULL){ + fprintf(stderr, "Error: can't delete NULL database\n"); + return -1; + } + int r = remove((*db)->name); + if(r != 0){ + fprintf(stderr, "Error deleting database '%s'\n", (*db)->name); + return -1; + } + freeDatabase(db); + return 0; +} + uint64_t addFile(database *db, char *file){ uint32_t l; file = normalizeStrLimit(file, &l, MAXPATH-1); @@ -53,47 +81,6 @@ static void decreaseCount(ctable *ct, uint64_t i){ ct->table[i]--; } -static int addRelation(database *db, relation r){ - if(searchMtable(db->map, r) == UINTMAX_MAX){ - insertMtable(db->map, r); - return 0; - } - return -1; -} - -int addFileTag(database *db, char *file, char *tag){ - uint64_t fi = addFile(db, file), ti = addTag(db, tag); - int r = addRelation(db, (relation){.file = fi, .tag = ti}); - if(r == 0){ - increaseCount(db->cfiles, fi); - increaseCount(db->ctags, ti); - return 0; - } - return -1; -} - -int addFileTags(database *db, char *file, int ntags, ...){ - va_list tags; - va_start(tags, ntags); - for(uint64_t i = 0; i < ntags; ++i){ - char *tag = va_arg(tags, char*); - addFileTag(db, file, tag); - } - va_end(tags); - return 0; -} - -int addTagFiles(database *db, char *tag, int nfiles, ...){ - va_list files; - va_start(files, nfiles); - for(uint64_t i = 0; i < nfiles; ++i){ - char *file = va_arg(files, char*); - addFileTag(db, file, tag); - } - va_end(files); - return 0; -} - static void decreaseHigherIndexNode(node *n, uint64_t i){ if(n == NULL){ return; @@ -167,6 +154,69 @@ int removeTag(database *db, char *tag){ return 0; } +static int addRelation(database *db, relation r){ + if(searchMtable(db->map, r) == UINTMAX_MAX){ + insertMtable(db->map, r); + return 0; + } + return -1; +} + +int addFileTag(database *db, char *file, char *tag){ + uint64_t fi = addFile(db, file), ti = addTag(db, tag); + int r = addRelation(db, (relation){.file = fi, .tag = ti}); + if(r == 0){ + increaseCount(db->cfiles, fi); + increaseCount(db->ctags, ti); + return 0; + } + return -1; +} + +int addFileTags(database *db, char *file, int ntags, ...){ + va_list tags; + va_start(tags, ntags); + for(uint64_t i = 0; i < ntags; ++i){ + char *tag = va_arg(tags, char*); + addFileTag(db, file, tag); + } + va_end(tags); + return 0; +} + +int addTagFiles(database *db, char *tag, int nfiles, ...){ + va_list files; + va_start(files, nfiles); + for(uint64_t i = 0; i < nfiles; ++i){ + char *file = va_arg(files, char*); + addFileTag(db, file, tag); + } + va_end(files); + return 0; +} + +int removeFileTag(database *db, char *file, char *tag){ + uint32_t lf, lt; + file = normalizeStrLimit(file, &lf, MAXPATH-1); + tag = normalizeStrLimit(tag, <, MAXPATH-1); + uint64_t hf = crc64(0, file, lf), ht = crc64(0, tag, lt); + uint64_t fi = searchNode(db->hfiles, hf), ti = searchNode(db->htags, ht); + if(fi == UINTMAX_MAX){ + fprintf(stderr, "Error: no such file '%s'\n", file); + return -1; + } + if(ti == UINTMAX_MAX){ + fprintf(stderr, "Error: no such tag '%s'\n", tag); + return -1; + } + int status = removeMtable(db->map, (relation){.file = fi, .tag = ti}); + if(status == -1){ + fprintf(stderr, "Error: tag '%s' not associated with file '%s'\n", tag, file); + return -1; + } + return 0; +} + int searchFile(database *db, char *file, uint64_t n, uint64_t **r, uint64_t *rl){ uint32_t l; file = normalizeStrLimit(file, &l, MAXPATH-1); @@ -244,6 +294,10 @@ int storeDatabase(database *db, const char *path){ database *loadDatabase(const char* path){ FILE *fp = fopen(path, "rb"); + if(fp == NULL){ + fprintf(stderr, "Error: no such file (%s)\n", path); + return NULL; + } char *header = calloc(2, sizeof(char)); fread(header, sizeof(char), 2, fp); if(!sameStr(header, "DB")){ @@ -277,7 +331,7 @@ void printDatabase(database *db){ printf("\n"); } -void debugAVLtree(node *n){ +static void debugAVLtree(node *n){ if(n != NULL){ printf("\t\t+ %" PRIu64 " -> %" PRIu64 "\n", n->h, n->i); debugAVLtree(n->left); @@ -286,6 +340,10 @@ void debugAVLtree(node *n){ } void debugDatabase(database *db){ + if(db == NULL){ + fprintf(stderr, "Error: database is NULL\n"); + return; + } printf("\n"); printf("Name: %s\n", db->name); printf("\t-lfiles: %d\n", db->lfiles->size); @@ -4,16 +4,17 @@ int main(){ + inputBuffer *in = newInputBuffer(); - database *db = newDatabase("miDB"); + /*database *test = newDatabase("miDB"); - addFileTag(db, "vaca.png", "naturalezas"); - addFileTags(db, "terry-davis.jpg", 3, "holyC", "programmer", "very cool"); - addFileTag(db, "vaca.png", "lovely"); - addFileTags(db, "vaca.png", 3, "nature", "animal", "very cool"); - addFileTag(db, "terry-davis.jpg", "based"); + addFileTag(test, "vaca.png", "naturalezas"); + addFileTags(test, "terry-davis.jpg", 3, "holyC", "programmer", "very cool"); + addFileTag(test, "vaca.png", "lovely"); + addFileTags(test, "vaca.png", 3, "nature", "animal", "very cool"); + addFileTag(test, "terry-davis.jpg", "based"); - storeDatabase(db, "db.db"); + storeDatabase(test, "db.db"); printDatabase(db); @@ -26,29 +27,18 @@ int main(){ for(uint64_t j = 0; j < i; ++j){ printf("\t%s\n", db->ltags->table[l[j]]); - } - - - - while(0){ + }*/ - prompt(); + database *db = NULL; + while(1){ + prompt(db); getInput(in); - - /* - insertTag(r, in->buffer); - printf("Tags of row '%s': %s\n", r->path, r->tags); - printf("Number of tags: %u. Length of tags: %u\n", r->numTags, r->lenTags); - */ - - /*switch(handleInput(in)){ - case META_COMMAND_SUCCESS: - printf("we done it nigger\n"); - break; - case META_COMMAND_FAIL: - printf("uh-oh nigga i dunno what '%s' is!\n", in->buffer); - break; - }*/ + argList *args = splitInput(in); + int status = handleInput(args, &db); + if(status == EXIT_CODE){ + printf("Exiting db\n"); + break; + } } return 0; diff --git a/src/parser.c b/src/parser.c index f26a0cb..0401f43 100644 --- a/src/parser.c +++ b/src/parser.c @@ -1,20 +1,439 @@ #include "db.h" -int handleMetaCommand(inputBuffer *in){ - if(sameStr(in->buffer, ".exit")){ - freeInputBuffer(in); - printf("EXIT M'NIGGA\n"); - exit(EXIT_SUCCESS); - }else{ - return META_COMMAND_FAIL; +char *keywords[NKEYWORDS] = {"EXIT", "DEBUG", + "CREATE", "DELETE", "OPEN", "SAVE", "CLOSE", + "ADDF", "ADDT", "DELETEF", "DELETET", + "TAG", "REMOVE", + "SHOW", "SHOWT", "SHOWF"}; + +argList *newArgList(void){ + argList *args = malloc(sizeof(argList)); + args->argc = 0; + args->argv = NULL; + return args; +} + +argList *splitInput(inputBuffer *in){ + if(in->inputSize == 0){ + return NULL; + } + argList *args = newArgList(); + uint64_t len = args->argc = 0; + + // Remove leading spaces + while(isspace(in->buffer[len])){ + ++len; + } + // Count words (alone or quoted) + while(in->buffer[len] != '\0'){ + while(isspace(in->buffer[len])){ + ++len; + } + if(in->buffer[len] == '"'){ + do{ + ++len; + }while(in->buffer[len] != '"'); + ++args->argc; + }else if(in->buffer[len] == '\''){ + do{ + ++len; + }while(in->buffer[len] != '\''); + ++args->argc; + }else{ + while(in->buffer[len] != '\0' && !isspace(in->buffer[len])){ + ++len; + } + ++args->argc; + } + ++len; + } + uint64_t *wstarts = malloc(args->argc*sizeof(uint64_t)), *wlens = malloc(args->argc*sizeof(uint64_t)); + len = args->argc = 0; + while(in->buffer[len] != '\0'){ + while(isspace(in->buffer[len])){ + ++len; + } + if(in->buffer[len] == '"'){ + wstarts[args->argc] = len+1; + do{ + ++len; + }while(in->buffer[len] != '"'); + wlens[args->argc] = len - wstarts[args->argc]; + ++args->argc; + }else if(in->buffer[len] == '\''){ + wstarts[args->argc] = len+1; + do{ + ++len; + }while(in->buffer[len] != '\''); + wlens[args->argc] = len -wstarts[args->argc]; + ++args->argc; + }else{ + wstarts[args->argc] = len; + while(in->buffer[len] != '\0' && !isspace(in->buffer[len])){ + ++len; + } + wlens[args->argc] = len - wstarts[args->argc]; + ++args->argc; + } + ++len; + } + + args->argv = malloc(args->argc*sizeof(char*)); + for(uint64_t i = 0; i < args->argc; ++i){ + args->argv[i] = calloc(wlens[i]+1, sizeof(char)); + memcpy(args->argv[i], &in->buffer[wstarts[i]], wlens[i]); + } + return args; +} + +static int commandExit(argList *args, database **db){ + // TODO: cleanup functions + return EXIT_CODE; +} + +static int commandDebug(argList *args, database **db){ + if(*db == NULL){ + fprintf(stderr, "Error: no database to debug\n"); + return -1; + } + debugDatabase(*db); + return 0; +} + +static int commandCreate(argList *args, database **db){ + if(args->argc < 2){ + fprintf(stderr, "Error, not enough arguments for command '%s'\n", args->argv[0]); + return -1; + } + for(int i = 1; i < args->argc; ++i){ + int status = storeDatabase(newDatabase(args->argv[i]), args->argv[i]); + if(status != 0){ + fprintf(stderr, "Error creating and storing database '%s'\n", args->argv[i]); + return -1; + } + } + return 0; +} + +static int commandDelete(argList *args, database **db){ + if(args->argc < 2){ + fprintf(stderr, "Error, not enough arguments for command '%s'\n", args->argv[0]); + return -1; + } + for(int i = 1; i < args->argc; ++i){ + database *t = loadDatabase(args->argv[i]); + int status = deleteDatabase(&t); + if(status != 0){ + fprintf(stderr, "Error deleting database '%s'\n", args->argv[i]); + return -1; + } + } + return 0; +} + +static int commandOpen(argList *args, database **db){ + if(args->argc < 2){ + fprintf(stderr, "Error: not enough arguments for command '%s'\n", args->argv[0]); + return -1; + }else if(args->argc > 2){ + fprintf(stderr, "Error: too many arguments for command 'OPEN'. Only opening first one ('%s')\n", args->argv[1]); + } + if(*db != NULL){ + fprintf(stderr, "Error: database '%s' already opened. Close it before proceeding with \"CLOSE '%s'\"\n", (*db)->name, (*db)->name); + return -1; } + *db = loadDatabase(args->argv[1]); + if(*db == NULL){ + fprintf(stderr, "Error loading database '%s'\n", args->argv[1]); + return -1; + } + return 0; } -int handleInput(inputBuffer *in){ - if(in->buffer[0] == '.'){ - return handleMetaCommand(in); - }else{ - printf("normal command\n"); +static int commandSave(argList *args, database **db){ + if(args->argc > 1){ + fprintf(stderr, "Error: too many arguments for function 'SAVE'. Only saving loaded database ('%s')\n", (*db)->name); + } + if(*db == NULL){ + fprintf(stderr, "Error: no database to save\n"); + return -1; + } + int status = storeDatabase(*db, (*db)->name); + if(status != 0){ + fprintf(stderr, "Error storing database '%s'\n", (*db)->name); + return -1; + } + return 0; +} + +static int commandClose(argList *args, database **db){ + if(args->argc > 1){ + fprintf(stderr, "Error: too many arguments for function 'CLOSE'. Only closing loaded database ('%s')\n", (*db)->name); + } + if(*db == NULL){ + fprintf(stderr, "Error: no database to close\n"); + return -1; + } + int status = storeDatabase(*db, (*db)->name); + if(status != 0){ + fprintf(stderr, "Error storing database '%s' before closing it\n", (*db)->name); + return -1; + } + freeDatabase(db); + return 0; +} + +static int commandAddf(argList *args, database **db){ + if(*db == NULL){ + fprintf(stderr, "Error: no database to add files to\n"); + return -1; + } + char **files = malloc((args->argc-1)*sizeof(char*)); + uint64_t fi = 0; + for(uint64_t i = 1; i < args->argc; ++i){ + files[fi++] = args->argv[i]; + } + if(fi == 0){ + fprintf(stderr, "Error: no files given to add to database\n"); + return -1; + } + for(uint64_t i = 0; i < fi; ++i){ + addFile(*db, files[i]); + } + return 0; +} + +static int commandAddt(argList *args, database **db){ + if(*db == NULL){ + fprintf(stderr, "Error: no database to add tags to\n"); + return -1; + } + char **tags = malloc((args->argc-1)*sizeof(char*)); + uint64_t ti = 0; + for(uint64_t i = 1; i < args->argc; ++i){ + tags[ti++] = args->argv[i]; + } + if(ti == 0){ + fprintf(stderr, "Error: no tags given to add to database\n"); + return -1; + } + for(uint64_t i = 0; i < ti; ++i){ + addTag(*db, tags[i]); + } + return 0; +} + +static int commandDeletef(argList *args, database **db){ + if(*db == NULL){ + fprintf(stderr, "Error: no database to delete files from\n"); + return -1; + } + char **files = malloc((args->argc-1)*sizeof(char*)); + uint64_t fi = 0; + for(uint64_t i = 1; i < args->argc; ++i){ + files[fi++] = args->argv[i]; + } + if(fi == 0){ + fprintf(stderr, "Error: no files given to remove from database\n"); + return -1; } -}
\ No newline at end of file + for(uint64_t i = 0; i < fi; ++i){ + removeFile(*db, files[i]); + } + return 0; +} + +static int commandDeletet(argList *args, database **db){ + if(*db == NULL){ + fprintf(stderr, "Error: no database to remove tags from\n"); + return -1; + } + char **tags = malloc((args->argc-1)*sizeof(char*)); + uint64_t ti = 0; + for(uint64_t i = 1; i < args->argc; ++i){ + tags[ti++] = args->argv[i]; + } + if(ti == 0){ + fprintf(stderr, "Error: no tags given to remove from database\n"); + return -1; + } + for(uint64_t i = 0; i < ti; ++i){ + removeTag(*db, tags[i]); + } + return 0; +} + +static int commandTag(argList *args, database **db){ + if(*db == NULL){ + fprintf(stderr, "Error: no database to add files and tags to\n"); + return -1; + } + char **files = malloc((args->argc-1)*sizeof(char*)); + uint64_t i, fi = 0, ti = 0; + bool with = false; + for(i = 1; i < args->argc; ++i){ + uint32_t l1, l2; + if(sameStr(normalizeStr(args->argv[i], &l1), normalizeStr("WITH", &l2))){ + with = true; + ++i; + break; + } + files[fi++] = args->argv[i]; + } + if(fi == 0){ + fprintf(stderr, "Error: no files given to tag\n"); + return -1; + } + if(with == false){ + fprintf(stderr, "Error: missing 'WITH' keyword on 'TAG' command\n"); + return -1; + } + char **tags = malloc((args->argc-i)*sizeof(char*)); + for(; i < args->argc; ++i){ + tags[ti++] = args->argv[i]; + } + if(ti == 0){ + fprintf(stderr, "Error: no tags given to tag files\n"); + return -1; + } + + for(uint64_t f = 0; f < fi; ++f){ + for(uint64_t t = 0; t < ti; ++t){ + int status = addFileTag(*db, files[f], tags[t]); + if(status != 0){ + fprintf(stderr, "Error: couldnt add tag '%s' to file '%s'\n", tags[t], files[f]); + return -1; + } + } + } + return 0; +} + +static int commandRemove(argList *args, database **db){ + if(*db == NULL){ + fprintf(stderr, "Error: no database to remove files and tags from\n"); + return -1; + } + char **tags = malloc((args->argc-1)*sizeof(char*)); + uint64_t i, ti = 0, fi = 0; + bool from = false; + for(i = 1; i < args->argc; ++i){ + uint32_t l1, l2; + if(sameStr(normalizeStr(args->argv[i], &l1), normalizeStr("FROM", &l2))){ + from = true; + ++i; + break; + } + tags[ti++] = args->argv[i]; + } + if(ti == 0){ + fprintf(stderr, "Error: no tags given to remove\n"); + return -1; + } + if(from == false){ + fprintf(stderr, "Error: missing 'FROM' keyword on 'REMOVE' command\n"); + return -1; + } + char **files = malloc((args->argc-i)*sizeof(char*)); + for(; i < args->argc; ++i){ + files[fi++] = args->argv[i]; + } + if(fi == 0){ + fprintf(stderr, "Error: no files given to remove\n"); + return -1; + } + + for(uint64_t f = 0; f < fi; ++f){ + for(uint64_t t = 0; t < ti; ++t){ + int status = removeFileTag(*db, files[f], tags[t]); + if(status != 0){ + fprintf(stderr, "Error: couldnt remove tag '%s' from file '%s'\n", tags[t], files[f]); + return -1; + } + } + } + return 0; +} + +static int commandShow(argList *args, database **db){ + if(*db == NULL){ + fprintf(stderr, "Error: no database to show\n"); + return -1; + } + printDatabase(*db); + return 0; +} + +static int commandShowt(argList *args, database **db){ + // We only show the files of one tag + if(*db == NULL){ + fprintf(stderr, "Error: no database to show tags of file\n"); + return -1; + } + if(args->argc > 2){ + fprintf(stderr, "Error: too many arguments for 'SHOWT', current support for only one file. Only showing tags of first file ('%s')\n", args->argv[1]); + } + + uint64_t rl, *r; + int status = searchFile(*db, args->argv[1], 0, &r, &rl); + if(status == -1){ + fprintf(stderr, "Error searching for file '%s'\n", args->argv[1]); + return -1; + } + printf("Tags of file '%s' (%"PRIu64"):\n", args->argv[1], rl); + for(uint64_t i = 0; i < rl; ++i){ + printf("\t- %s\n", (*db)->ltags->table[r[i]]); + } + printf("\n"); +} + +static int commandShowf(argList *args, database **db){ + // We only show the tags of one file + if(*db == NULL){ + fprintf(stderr, "Error: no database to show files of tag\n"); + return -1; + } + if(args->argc > 2){ + fprintf(stderr, "Error: too many arguments for 'SHOWF', current support for only one tag. Only showing files of first tag ('%s')\n", args->argv[1]); + } + + uint64_t rl, *r; + int status = searchTag(*db, args->argv[1], 0, &r, &rl); + if(status == -1){ + fprintf(stderr, "Error searching for tag '%s'\n", args->argv[1]); + return -1; + } + printf("Files of tag '%s' (%"PRIu64"):\n", args->argv[1], rl); + for(uint64_t i = 0; i < rl; ++i){ + printf("\t- %s\n", (*db)->lfiles->table[r[i]]); + } + printf("\n"); +} + + int (*commands[])(argList*, database**) = { + commandExit, commandDebug, + commandCreate, commandDelete, commandOpen, commandSave, commandClose, + commandAddf, commandAddt, commandDeletef, commandDeletet, + commandTag, commandRemove, + commandShow, commandShowt, commandShowf + }; + +int handleInput(argList *args, database **db){ + if(args == NULL){ + return 0; + } + int command = -1; + for(int i = 0; i < NKEYWORDS; ++i){ + uint32_t l1, l2; + if(sameStr(normalizeStr(args->argv[0], &l1), normalizeStr(keywords[i], &l2))){ + command = i; + break; + } + } + if(command == -1){ + fprintf(stderr, "Error: command '%s' not found\n", args->argv[0]); + return -1; + } + return commands[command](args, db); +} @@ -5,7 +5,6 @@ inputBuffer *newInputBuffer(void){ inputBuffer *in = malloc(sizeof(inputBuffer)); in->buffer = NULL; in->inputSize = 0; - return in; } @@ -18,13 +17,17 @@ void getInput(inputBuffer *in){ size_t n; ssize_t r = getline(&(in->buffer), &n, stdin); if(r <= 0){ - printf("Error\n"); + fprintf(stderr, "Error\n"); exit(r); } in->inputSize = r-1; in->buffer[in->inputSize] = 0; } -void prompt(void){ - printf("db > "); -}
\ No newline at end of file +void prompt(database *db){ + if(db == NULL){ + printf("db > "); + }else{ + printf("%s > ", db->name); + } +} diff --git a/src/storage.c b/src/storage.c index 9afede7..bc5ab6b 100644 --- a/src/storage.c +++ b/src/storage.c @@ -10,6 +10,13 @@ ltable *newLtable(uint64_t size){ return lt; } +int deleteLtable(ltable **lt){ + free((*lt)->table); + free(*lt); + *lt = NULL; + return 0; +} + int insertLtable(ltable *lt, char *str){ uint32_t ls; str = normalizeStrLimit(str, &ls, MAXPATH-1); @@ -105,6 +112,13 @@ ctable *newCtable(uint64_t size){ return ct; } +int deleteCtable(ctable **ct){ + free((*ct)->table); + free(*ct); + *ct = NULL; + return 0; +} + int insertCtable(ctable *ct, uint64_t n){ uint64_t *nct = malloc((ct->size+1)*sizeof(uint64_t)); for(uint64_t i = 0; i < ct->size; ++i){ @@ -184,6 +198,13 @@ mtable *newMtable(uint64_t size){ return mt; } +int deleteMtable(mtable **mt){ + free((*mt)->table); + free(*mt); + *mt = NULL; + return 0; +} + int insertMtable(mtable *mt, relation r){ relation *nmt = malloc((mt->size+1)*sizeof(relation)); for(uint64_t i = 0; i < mt->size; ++i){ @@ -301,6 +322,17 @@ node *newNode(uint64_t h, uint64_t i){ return n; } +int deleteTree(tree *root){ + if(*root == NULL){ + return -1; + } + deleteTree(&(*root)->left); + deleteTree(&(*root)->right); + free(*root); + *root = NULL; + return 0; +} + static node *rotateNodeRight(node *r){ node *nr = r->left; node *nc = nr->right; |
