--- /dev/null
+#ifndef DB_H
+#define DB_H
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+
+#include "strnatcmp.h"
+
+#include "repl.h"
+#include "parser.h"
+#include "storage.h"
+
+
+
+#endif
\ No newline at end of file
--- /dev/null
+#include "db.h"
+
+
+
+
+
+int main(){
+
+ inputBuffer *in = newInputBuffer();
+
+ row r = {"C:/xd", "pepe;soyjak;"};
+ printf("%s\n", r.tags);
+ insertTag(&r, "WOJAK");
+ printf("%s\n", r.tags);
+
+ while(0){
+
+ prompt();
+ getInput(in);
+
+ char **arr;
+ int len;
+ split(in->buffer, ';', &arr, &len);
+ for(int i = 0; i < len; ++i)
+ printf("%s\t", arr[i]);
+ printf("\n");
+
+ /*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;
+ }*/
+ }
+}
\ No newline at end of file
--- /dev/null
+#include "db.h"
+
+
+uint16_t len(const char *s){
+ uint16_t l = -1;
+ while(s[++l]);
+ return l;
+}
+
+bool sameStr(const char* s1, const char *s2){
+ uint16_t i1 = 0, i2 = 0;
+ while(s1[i1] && s2[i2] && s1[i1] == s2[i2])
+ ++i1, ++i2;
+ return !s1[i1] && !s2[i2];
+}
+
+int handleMetaCommand(inputBuffer *in){
+ if(sameStr(in->buffer, ".exit")){
+ freeInputBuffer(in);
+ printf("EXIT M'NIGGA\n");
+ exit(EXIT_SUCCESS);
+ }else{
+ return META_COMMAND_FAIL;
+ }
+}
+
+int handleInput(inputBuffer *in){
+ if(in->buffer[0] == '.'){
+ return handleMetaCommand(in);
+ }else{
+ printf("normal command\n");
+ }
+}
\ No newline at end of file
--- /dev/null
+#ifndef PARSER_H
+#define PARSER_H
+
+#include "db.h"
+
+
+typedef enum {
+ META_COMMAND_SUCCESS,
+ META_COMMAND_FAIL,
+} metaCommandStatus;
+
+
+uint16_t len(const char *s);
+
+bool sameStr(const char* s1, const char *s2);
+
+int handleMetaCommand(inputBuffer *in);
+
+int handleInput(inputBuffer *in);
+
+#endif
\ No newline at end of file
--- /dev/null
+#include "db.h"
+
+
+inputBuffer *newInputBuffer(void){
+ inputBuffer *in = malloc(sizeof(inputBuffer));
+ in->buffer = NULL;
+ in->inputSize = 0;
+ return in;
+}
+
+void freeInputBuffer(inputBuffer *in){
+ free(in->buffer);
+ free(in);
+}
+
+void getInput(inputBuffer *in){
+ size_t n;
+ ssize_t r = getline(&(in->buffer), &n, stdin);
+ if(r <= 0){
+ printf("Error\n");
+ exit(r);
+ }
+ in->inputSize = r-1;
+ in->buffer[in->inputSize] = 0;
+}
+
+void prompt(void){
+ printf("db > ");
+}
\ No newline at end of file
--- /dev/null
+#ifndef REPL_H
+#define REPL_H
+
+#include "db.h"
+
+typedef struct {
+ char *buffer;
+ ssize_t inputSize;
+} inputBuffer;
+
+
+inputBuffer *newInputBuffer(void);
+
+void freeInputBuffer(inputBuffer *in);
+
+void getInput(inputBuffer *in);
+
+void prompt(void);
+
+#endif
\ No newline at end of file
--- /dev/null
+#include "db.h"
+
+
+
+
+// Splits src into words based on a separator character (sep) and stores them in arr, and the length in len. Inspired by https://github.com/joshdk/tag/blob/master/src/dsv.c's split
+void split(const char *src, char sep, char ***arr, int *len){
+ int i = 0, asize = 1, ai = 0, wsize = 1, wi = 0;
+ char c = 0;
+
+ (*arr) = malloc(asize*sizeof(char*));
+ (*arr)[ai] = malloc(wsize*sizeof(char));
+
+ while((c = src[i]) != '\0'){
+ // If there's a new word (new array index) reallocate the array and allocate space for it
+ if(wi == 0){
+ char **tmp;
+ if((tmp = realloc((*arr), (asize+ai)*sizeof(char*))) != NULL){
+ *arr = tmp;
+ tmp = NULL;
+ }else{
+ fprintf(stderr, "Error reallocating array (split)");
+ exit(EXIT_FAILURE);
+ }
+ (*arr)[ai] = malloc(wsize*sizeof(char));
+ }
+ // Allocate space for a new character in a word
+ char *tmp;
+ if((tmp = realloc((*arr)[ai], (wsize+wi)*sizeof(char))) != NULL){
+ strcpy(tmp, (*arr)[ai]);
+ (*arr)[ai] = tmp;
+ tmp = NULL;
+ }else{
+ fprintf(stderr, "Error reallocating word (split)");
+ exit(EXIT_FAILURE);
+ }
+ // If the character is a separator, terminate the string and increment the array index; if not, assign the character and increment the word index
+ if(c == sep){
+ (*arr)[ai][wi] = '\0';
+ wi = 0;
+ ++ai;
+ }else{
+ (*arr)[ai][wi] = c;
+ ++wi;
+ }
+ ++i;
+ }
+ if(src[i-1] != sep){
+ (*arr)[ai][wi] = '\0';
+ ++ai;
+ }
+ (*arr)[ai] = NULL;
+ *len = ai;
+}
+
+void swapWords(char ***arr, int a, int b){
+ char *tmp = (*arr)[a];
+ (*arr)[a] = (*arr)[b];
+ (*arr)[b] = tmp;
+}
+
+char *normalizeTag(char *tag){
+ uint16_t l = len(tag);
+ char *ntag = calloc(l+1, sizeof(char));
+ for(int i = 0; i < l; ++i){
+ ntag[i] = tolower(tag[i]);
+ }
+ return ntag;
+}
+
+// Adds a tag in the tags array in the row r, sorted by natural string comparison with strnatcmp
+// We assume that when adding a tag all other tags are already sorted
+void insertTag(row *r, char *tag){
+
+ tag = normalizeTag(tag);
+
+ // Dump tags into array of strings and add tag
+ char **arr, **tmp;
+ int l;
+ split(r->tags, ';', &arr, &l);
+ if((tmp = realloc(arr, (l+1)*sizeof(char*))) != NULL){
+ arr = tmp;
+ tmp = NULL;
+ }else{
+ fprintf(stderr, "Error reallocating array (insertTag)");
+ exit(EXIT_FAILURE);
+ }
+ arr[l] = malloc((len(tag)+1)*sizeof(char));
+ strcpy(arr[l], tag);
+
+ // Sort tag by natural string comparison, starting from the last element (the new tag)
+ for(int i = l; i > 0; --i){
+ switch(strnatcmp(arr[i-1], arr[i])){
+ case 1:
+ // arr[i-1] is higher than arr[i]; swap them
+ swapWords(&arr, i, i-1);
+ break;
+ case -1:
+ // arr[i-1] is lower than arr[i]; exit loop
+ i = 0;
+ break;
+ case 0:
+ // The tag already exists, no need to alter anything
+ free(arr);
+ return;
+ }
+ }
+ ++l; // Succesfully added new tag
+
+ // Insert tags back into tags member of row structure with the ';' separator in between them
+ int g = 0;
+ for(int i = 0; i < l; ++i){
+ int j = 0;
+ while(arr[i][j] != '\0'){
+ r->tags[g] = arr[i][j];
+ ++j;
+ ++g;
+ }
+ r->tags[g++] = ';';
+ }
+ r->tags[g] = '\0';
+}
\ No newline at end of file
--- /dev/null
+#ifndef STORAGE_H
+#define STORAGE_H
+
+#include "db.h"
+
+#define MAXPATH 4096
+#define MAXTAGS 4096
+
+
+typedef struct{
+ char path[MAXPATH];
+ char tags[MAXTAGS];
+} row;
+
+
+void split(const char *src, char sep, char ***arr, int *len);
+
+void swapWords(char ***arr, int a, int b);
+
+char *normalizeTag(char *tag);
+
+void insertTag(row *r, char *tag);
+
+#endif
\ No newline at end of file
--- /dev/null
+/* -*- mode: c; c-file-style: "k&r" -*-
+
+ strnatcmp.c -- Perform 'natural order' comparisons of strings in C.
+ Copyright (C) 2000, 2004 by Martin Pool <mbp sourcefrog net>
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+/* partial change history:
+ *
+ * 2004-10-10 mbp: Lift out character type dependencies into macros.
+ *
+ * Eric Sosman pointed out that ctype functions take a parameter whose
+ * value must be that of an unsigned int, even on platforms that have
+ * negative chars in their default char type.
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "strnatcmp.h"
+
+
+/* These are defined as macros to make it easier to adapt this code to
+ * different characters types or comparison functions. */
+static inline int
+nat_isdigit(nat_char a)
+{
+ return isdigit((unsigned char) a);
+}
+
+
+static inline int
+nat_isspace(nat_char a)
+{
+ return isspace((unsigned char) a);
+}
+
+
+static inline nat_char
+nat_toupper(nat_char a)
+{
+ return toupper((unsigned char) a);
+}
+
+
+
+static int
+compare_right(nat_char const *a, nat_char const *b)
+{
+ int bias = 0;
+
+ /* The longest run of digits wins. That aside, the greatest
+ value wins, but we can't know that it will until we've scanned
+ both numbers to know that they have the same magnitude, so we
+ remember it in BIAS. */
+ for (;; a++, b++) {
+ if (!nat_isdigit(*a) && !nat_isdigit(*b))
+ return bias;
+ else if (!nat_isdigit(*a))
+ return -1;
+ else if (!nat_isdigit(*b))
+ return +1;
+ else if (*a < *b) {
+ if (!bias)
+ bias = -1;
+ } else if (*a > *b) {
+ if (!bias)
+ bias = +1;
+ } else if (!*a && !*b)
+ return bias;
+ }
+
+ return 0;
+}
+
+
+static int
+compare_left(nat_char const *a, nat_char const *b)
+{
+ /* Compare two left-aligned numbers: the first to have a
+ different value wins. */
+ for (;; a++, b++) {
+ if (!nat_isdigit(*a) && !nat_isdigit(*b))
+ return 0;
+ else if (!nat_isdigit(*a))
+ return -1;
+ else if (!nat_isdigit(*b))
+ return +1;
+ else if (*a < *b)
+ return -1;
+ else if (*a > *b)
+ return +1;
+ }
+
+ return 0;
+}
+
+
+static int strnatcmp0(nat_char const *a, nat_char const *b, int fold_case)
+{
+ int ai, bi;
+ nat_char ca, cb;
+ int fractional, result;
+
+ assert(a && b);
+ ai = bi = 0;
+ while (1) {
+ ca = a[ai]; cb = b[bi];
+
+ /* skip over leading spaces or zeros */
+ while (nat_isspace(ca))
+ ca = a[++ai];
+
+ while (nat_isspace(cb))
+ cb = b[++bi];
+
+ /* process run of digits */
+ if (nat_isdigit(ca) && nat_isdigit(cb)) {
+ fractional = (ca == '0' || cb == '0');
+
+ if (fractional) {
+ if ((result = compare_left(a+ai, b+bi)) != 0)
+ return result;
+ } else {
+ if ((result = compare_right(a+ai, b+bi)) != 0)
+ return result;
+ }
+ }
+
+ if (!ca && !cb) {
+ /* The strings compare the same. Perhaps the caller
+ will want to call strcmp to break the tie. */
+ return 0;
+ }
+
+ if (fold_case) {
+ ca = nat_toupper(ca);
+ cb = nat_toupper(cb);
+ }
+
+ if (ca < cb)
+ return -1;
+ else if (ca > cb)
+ return +1;
+
+ ++ai; ++bi;
+ }
+}
+
+
+
+int strnatcmp(nat_char const *a, nat_char const *b) {
+ return strnatcmp0(a, b, 0);
+}
+
+
+/* Compare, recognizing numeric string and ignoring case. */
+int strnatcasecmp(nat_char const *a, nat_char const *b) {
+ return strnatcmp0(a, b, 1);
+}
\ No newline at end of file
--- /dev/null
+/* -*- mode: c; c-file-style: "k&r" -*-
+
+ strnatcmp.c -- Perform 'natural order' comparisons of strings in C.
+ Copyright (C) 2000, 2004 by Martin Pool <mbp sourcefrog net>
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+/* CUSTOMIZATION SECTION
+ *
+ * You can change this typedef, but must then also change the inline
+ * functions in strnatcmp.c */
+typedef char nat_char;
+
+int strnatcmp(nat_char const *a, nat_char const *b);
+int strnatcasecmp(nat_char const *a, nat_char const *b);
\ No newline at end of file