From 0c68944a1da405633f8d26bb6b160ec01072e30f Mon Sep 17 00:00:00 2001 From: Odweta Date: Sat, 23 Dec 2023 18:28:58 +0100 Subject: [PATCH] added writing capacilities to files and made the appropriate changes to the filedemo.c file --- README.md | 14 ++--- demo.c | 27 +++++---- examples/file/filedemo.c | 7 ++- include/file.h | 9 ++- include/vector.h | 8 +-- src/file.c | 126 +++++++++++++++++++++++++++++---------- src/list.c | 2 +- src/vector.c | 65 ++++++++++---------- test.txt | 10 ++-- 9 files changed, 172 insertions(+), 96 deletions(-) diff --git a/README.md b/README.md index af529a6..5a7249d 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ I got bored with having to use complicated checking and stuff when working with files and arrays in C, so I implemented a new `file_t` struct which can load the contents of a file into a dynamically allocated array (yes, there is an implementation of those here as well... (`vector_t` struct)). The struct also stores the number of lines the file has. -Also, I managed to come up with an implementation of a Python-like list (a vector of vectors of any type interpreted as just one vector). Quite handy, I'd say. The `list_t` struct is what you want to use when working with lists. +Also, I managed to come up with an implementation of a kind-of-Python-like list (a vector of vectors of any type interpreted as just one vector). Quite handy, I'd say. The `list_t` struct is what you want to use when working with lists. The difference between a `vector_t` and a `list_t` is that a vector can only be of one type, whereas a list can have values of any type inside it (in this context, any means types defined in this library and default types in C). @@ -22,8 +22,7 @@ Examples of this work of art are in the `/examples/` directory of this repo. 3. Copy a file to another file 4. Move a file to another file 5. Rename a file -6. Delete a file -7. Empty a file +6. Delete a file? 8. Add line numbers at the beggining of each line in a file + You tell me @@ -44,6 +43,7 @@ Examples of this work of art are in the `/examples/` directory of this repo. 1. Removing an element at a given index 2. Vectors of lists +3. Remove a value at a given index + You tell me @@ -67,10 +67,10 @@ Examples of this work of art are in the `/examples/` directory of this repo. ## Installation -`git clone https://gitlab.com/Odweta/odwetalibc.git` -`cd odwetalibc` -`make install` +`git clone https://gitlab.com/Odweta/odwetalibc.git && cd odwetalibc && make install` #### Run demo -`make && ./demo` +(the library has to be installed with `make install`) + +`make && ./demo` diff --git a/demo.c b/demo.c index b4139b1..091313b 100644 --- a/demo.c +++ b/demo.c @@ -1,21 +1,24 @@ -#include +#include #include int main() { - printf("creating a vector using 'vectorInit()' (returns a vector_t*)\npossible type are: INT, FLOAT, CHAR, VECTOR (LIST in the future)\n"); - vector_t* v = vectorInit(INT); + printf("creating a file using 'fileInit()' (returns a file_t*)\n"); + file_t* f = fileInit("test.txt"); - printf("pushing an integer value into the vector\nnote the kind of unusual way of passing the number argument: if you want to pass an expression (e.g. the value '1' or '4*4'), something not stored in a variable, you follow this syntax - &(type){value} (e.g. &(int){1 + 1})\n->'vectorPush(vector_t*, &(int){123})'\n"); - vectorPush(v, &(int){123}); - vectorPrint(v); + printf("printing the file contents\n\n"); + filePrint(f); - printf("popping the last value of the vector; in this case, it is an integer, so you type: 'int poppedValue = *((int *)vectorPop(vector_t*))'\n"); - int poppedValue = *((int *)vectorPop(v)); - printf("popped: %d\n", poppedValue); - vectorPrint(v); + printf("\nconvering the file contents into a string (char*)\n"); + char* string = fileToString(f); + printf("\"%s\"\n", string); - printf("if you are finished working with the vector BEFORE the immediate end of the program, run 'vectorCleanup(vector_t*)', otherwise it is not strictly necessary\n"); - vectorCleanup(v); + printf("writing is quite simple, just call the 'fileWrite(file_t*, char*)' or 'fileWriteln(file_t*, char*)' function, where the second argument is the string you want to write into a file\nthe 'fileWriteln()' function writes the specified string AND a '\\n' after it, whereas the 'fileWrite()' function does not append the '\\n'\n"); + fileWrite(f, "'this will not end with a \"\\n\"' "); + fileWriteln(f, "this line was appended"); + filePrint(f); + + printf("if you are finished working with the file BEFORE the immediate end of the program, run 'fileCleanup(file_t*)', otherwise it is not strictly necessary\nit will, though, append a '\\n' at the end of the file if you have not done so, making writing into files more consistent, so for this reason, I recommend you use it with every file_t*\n"); + fileCleanup(f); return 0; } diff --git a/examples/file/filedemo.c b/examples/file/filedemo.c index e484f83..091313b 100644 --- a/examples/file/filedemo.c +++ b/examples/file/filedemo.c @@ -12,9 +12,12 @@ int main() { char* string = fileToString(f); printf("\"%s\"\n", string); - // TODO -> writing, clearing + printf("writing is quite simple, just call the 'fileWrite(file_t*, char*)' or 'fileWriteln(file_t*, char*)' function, where the second argument is the string you want to write into a file\nthe 'fileWriteln()' function writes the specified string AND a '\\n' after it, whereas the 'fileWrite()' function does not append the '\\n'\n"); + fileWrite(f, "'this will not end with a \"\\n\"' "); + fileWriteln(f, "this line was appended"); + filePrint(f); - printf("if you are finished working with the file BEFORE the immediate end of the program, run 'fileCleanup(file_t*)', otherwise it is not strictly necessary\n"); + printf("if you are finished working with the file BEFORE the immediate end of the program, run 'fileCleanup(file_t*)', otherwise it is not strictly necessary\nit will, though, append a '\\n' at the end of the file if you have not done so, making writing into files more consistent, so for this reason, I recommend you use it with every file_t*\n"); fileCleanup(f); return 0; diff --git a/include/file.h b/include/file.h index b57fc07..a034aa2 100644 --- a/include/file.h +++ b/include/file.h @@ -4,9 +4,10 @@ #include typedef struct { - char* path; // the file descriptor - int length; // line count - vector_t* lines; // 2d array of chars + char* path; // the file descriptor + int length; // line count + vector_t* lines; // 2d array of chars + int not_newline_terminated; // bool: 1 = true, 0 = false } file_t; file_t* fileInit(char* path); // initialization function @@ -21,6 +22,4 @@ void fileWriteln(file_t* f, char* payload); // appends a given string to the fil void fileWrite(file_t* f, char* payload); // appends a given string to the file (not on a newline!) -void fileClear(file_t* f); // wipes the file contents - #endif diff --git a/include/vector.h b/include/vector.h index b473d0b..5272652 100644 --- a/include/vector.h +++ b/include/vector.h @@ -4,10 +4,10 @@ typedef long unsigned int size_t; typedef enum { - INT, - FLOAT, - CHAR, - VECTOR // allows for multi-dimensional arrays + INT = 0, + FLOAT = 1, + CHAR = 2, + VECTOR = 3 // allows for multi-dimensional arrays } dataType_e; typedef struct { diff --git a/src/file.c b/src/file.c index a2c0e51..432720a 100644 --- a/src/file.c +++ b/src/file.c @@ -4,11 +4,15 @@ #include #include -int +int fileCountLines(file_t* f) { char ch; int lines = 0; FILE* fd = fopen(f->path, "r"); + if (!fd) { + fprintf(stderr, "unable to open file"); + exit(EXIT_FAILURE); + } while ((ch = fgetc(fd)) != EOF) { if (ch == '\n') { @@ -21,26 +25,44 @@ fileCountLines(file_t* f) { return lines; } -void -_fileLoadLines(file_t* f, FILE* fd) { - int lineLength; +void +_fileLoadLines(file_t* f) { char ch; - vector_t* line = vectorInit(CHAR); + FILE* fd = fopen(f->path, "r"); + if (!fd) { + fprintf(stderr, "unable to open file %s with mode \"r\"\n", f->path); + fclose(fd); + fileCleanup(f); + exit(EXIT_FAILURE); + } + + char prev; for (int i = 0; i < f->length; i++) { + vector_t* line = vectorInit(CHAR); // Create a new vector for each line + while ((ch = fgetc(fd)) != '\n' && ch != EOF) { vectorPush(line, &ch); } // Skip the newline character if it exists if (ch == '\n') { - char* empty = ""; - vectorPush(line, empty); + char* empty = ""; + vectorPush(line, empty); } vectorPush(f->lines, line); - line = vectorInit(CHAR); + + prev = ch; + // Don't free the line vector here, as it's now owned by the f->lines vector } + if (prev != '\n') { + f->not_newline_terminated = 1; // \n is NOT at the end of a file + } else { + f->not_newline_terminated = 0; // \n IS at the end of a file + } + + fclose(fd); } char* @@ -51,47 +73,91 @@ fileToString(file_t* f) { file_t* fileInit(char* path) { file_t* f = malloc(sizeof(file_t)); - if (f == NULL) { - perror("failed to allocate memory for the file\n"); - exit(EXIT_FAILURE); - } - - f->lines = malloc(sizeof(vector_t)); - if (f->lines == NULL) { - perror("failed to allocate memory for the file contents vector\n"); + if (!f) { + fprintf(stderr, "failed to allocate memory for the file\n"); exit(EXIT_FAILURE); } f->path = path; - FILE* fd = fopen(f->path, "r"); - if (fd == NULL) { - fprintf(stderr, "unable to open file %s with mode \"r\"\n", path); - fclose(fd); - fileCleanup(f); - exit(EXIT_FAILURE); - } - f->length = fileCountLines(f); - f->lines = vectorInit(VECTOR); // vector of vectors of chars = vector of strings - _fileLoadLines(f, fd); - fclose(fd); + f->lines = vectorInit(VECTOR); // vector of vectors of chars = vector of strings + _fileLoadLines(f); return f; } void fileCleanup(file_t* f) { + if (f->not_newline_terminated == 1) { + FILE* fd = fopen(f->path, "a"); + fprintf(fd, "\n"); + fclose(fd); + f->not_newline_terminated = 0; // just for piece of mind + } vectorCleanup(f->lines); free(f); } void filePrint(file_t* f) { - vector_t* line = vectorInit(CHAR); for (int i = 0; i < f->length; i++) { - line = (vector_t *)vectorGetAt(f->lines, i); - vectorPrintAsString(line); + vectorPrintAsString((vector_t *)vectorGetAt(f->lines, i)); } } + +void +fileWriteln(file_t* f, char* payload) { + FILE* fd = fopen(f->path, "a"); + if (!fd) { + fprintf(stderr, "unable to open file %s with mode \"a\"\n", f->path); + fclose(fd); + fileCleanup(f); + exit(EXIT_FAILURE); + } + + fprintf(fd, "%s\n", payload); + + fclose(fd); + + // Update the file structure without re-initializing + if (f->not_newline_terminated == 0) { + vector_t* line = vectorInit(CHAR); + for (int i = 0; i < strlen(payload); i++) { + vectorPush(line, &payload[i]); + } + vectorPush(f->lines, line); + f->length++; + f->not_newline_terminated = 0; + } else if (f->not_newline_terminated == 1) { + for (int i = 0; i < strlen(payload); i++) { + vectorPush((vector_t *)vectorGetAt(f->lines, f->length-1), &payload[i]); + } + f->not_newline_terminated = 0; + } +} + +void +fileWrite(file_t* f, char* payload) { + FILE* fd = fopen(f->path, "a"); + if (!fd) { + fprintf(stderr, "unable to open file %s with mode \"a\"\n", f->path); + fclose(fd); + fileCleanup(f); + exit(EXIT_FAILURE); + } + + fprintf(fd, "%s", payload); + + fclose(fd); + + // Update the file structure without re-initializing + vector_t* line = vectorInit(CHAR); + for (int i = 0; i < strlen(payload); i++) { + vectorPush(line, &payload[i]); + } + vectorPush(f->lines, line); + f->length++; + f->not_newline_terminated = 1; +} diff --git a/src/list.c b/src/list.c index 615056c..b959d80 100644 --- a/src/list.c +++ b/src/list.c @@ -49,7 +49,7 @@ listPush(list_t* l, dataType_e type, void* element) { vectorPush((vector_t *)l->data, newVec); break; default: - perror("specify a valid vector type\n"); + fprintf(stderr, "specify a valid vector type\n"); exit(EXIT_FAILURE); } diff --git a/src/vector.c b/src/vector.c index 7f47342..e0eb657 100644 --- a/src/vector.c +++ b/src/vector.c @@ -1,9 +1,9 @@ #include #include #include -#include +#include -void +void vectorCleanup(vector_t* v) { free(v->data); free(v); @@ -18,7 +18,7 @@ vectorReset(vector_t* v) { } } -void +void _printVectorRecursive(vector_t* v, int depth) { int i; fprintf(stdout, "["); @@ -38,7 +38,7 @@ _printVectorRecursive(vector_t* v, int depth) { _printVectorRecursive((vector_t *)(v->data + i * v->elementSize), depth + 1); break; default: - perror("tried to print unsupported data type\n"); + fprintf(stderr, "tried to print unsupported data type\n"); exit(EXIT_FAILURE); } @@ -54,7 +54,7 @@ _printVectorRecursive(vector_t* v, int depth) { } } -void +void vectorPrint(vector_t* v) { fprintf(stdout, "["); int i; @@ -74,7 +74,7 @@ vectorPrint(vector_t* v) { _printVectorRecursive((vector_t *)(v->data + i * v->elementSize), 0); break; default: - perror("tried to print unsupported data type\n"); + fprintf(stderr, "tried to print unsupported data type\n"); exit(EXIT_FAILURE); } @@ -90,7 +90,7 @@ vectorPrint(vector_t* v) { } } -void +void _printVectorAsStringRecursive(vector_t* v, int depth) { int i; for (i = 0; i < v->size; i++) { @@ -103,14 +103,14 @@ _printVectorAsStringRecursive(vector_t* v, int depth) { _printVectorAsStringRecursive((vector_t *)(v->data + i * v->elementSize), depth + 1); break; default: - perror("tried to print a non-char type vector as string\n"); + fprintf(stderr, "tried to print a non-char type vector as string\n"); exit(EXIT_FAILURE); } } if (depth != 0) fprintf(stdout, "\n"); // do not print the last additional new line } -void +void vectorPrintAsString(vector_t* v) { switch (v->type) { case CHAR: @@ -123,16 +123,16 @@ vectorPrintAsString(vector_t* v) { _printVectorAsStringRecursive(v, 0); break; default: - perror("tried to print a non-char type vector as string\n"); + fprintf(stderr, "tried to print a non-char type vector as string | type: %d\n", v->type); exit(EXIT_FAILURE); } } -vector_t* +vector_t* vectorInit(dataType_e type) { vector_t *v = malloc(sizeof(vector_t)); - if (v == NULL) { - perror("failed to allocate memory for the vector\n"); + if (!v) { + fprintf(stderr, "failed to allocate memory for the vector\n"); exit(EXIT_FAILURE); } @@ -153,20 +153,20 @@ vectorInit(dataType_e type) { v->elementSize = sizeof(vector_t); break; default: - perror("specify a valid vector type\n"); + fprintf(stderr, "specify a valid vector type\n"); exit(EXIT_FAILURE); } v->data = malloc(v->elementSize * v->capacity); if (v->data == NULL) { - perror("failed to allocate memory for vector data\n"); + fprintf(stderr, "failed to allocate memory for vector data\n"); exit(EXIT_FAILURE); } return v; } -void +void vectorPush(vector_t* v, void* element) { switch (v->type) { case INT: @@ -176,28 +176,33 @@ vectorPush(vector_t* v, void* element) { *((float *)(v->data) + v->size) = *((float *)element); break; case CHAR: + // Allocate memory for the character + v->data = realloc(v->data, (v->size + 1) * v->elementSize); + if (!v->data) { + fprintf(stderr, "failed to allocate memory for vector data\n"); + exit(EXIT_FAILURE); + } *((char *)(v->data) + v->size) = *((char *)element); break; case VECTOR: // Instead of reallocating memory for vector_t pointers, allocate memory for the vector data if (v->size == 0) { // Allocate memory for the vector data for the first time - v->data = malloc(v->elementSize * v->capacity); + v->data = realloc(v->data, v->elementSize); } else { // Reallocate memory for the vector data - v->data = realloc(v->data, v->elementSize * v->capacity); + v->data = realloc(v->data, v->elementSize * (v->size + 1)); } - if (v->data == NULL) { - perror("failed to allocate memory for vector data\n"); + if (!v->data) { + fprintf(stderr, "failed to allocate memory for vector data\n"); exit(EXIT_FAILURE); } // Copy the vector data from the element to the vector memcpy((vector_t *)(v->data) + v->size, (vector_t *)element, sizeof(vector_t)); - break; default: - perror("specify a valid vector type\n"); + fprintf(stderr, "specify a valid vector type\n"); exit(EXIT_FAILURE); } @@ -239,7 +244,7 @@ vectorPop(vector_t* v) { return vretval; default: - perror("specify a valid vector type\n"); + fprintf(stderr, "specify a valid vector type\n"); exit(EXIT_FAILURE); } @@ -249,7 +254,7 @@ vectorPop(vector_t* v) { } } -char* +char* vectorToString(vector_t* v) { char* retString; int totalSize = 0; @@ -312,7 +317,7 @@ vectorGetAt(vector_t* v, int index) { case VECTOR: return (vector_t *)(v->data) + index; default: - perror("specify a valid vector type\n"); + fprintf(stderr, "specify a valid vector type\n"); exit(EXIT_FAILURE); } } @@ -320,7 +325,7 @@ vectorGetAt(vector_t* v, int index) { void vectorSetAt(vector_t* v, int index, void* element) { if (index >= v->size) { - perror("index out of range\n"); + fprintf(stderr, "index out of range\n"); exit(EXIT_FAILURE); } @@ -338,15 +343,15 @@ vectorSetAt(vector_t* v, int index, void* element) { *((vector_t *)(v->data) + index) = *((vector_t *)element); break; default: - perror("specify a valid vector type\n"); + fprintf(stderr, "specify a valid vector type\n"); exit(EXIT_FAILURE); } } -void +void vectorInsertAt(vector_t* v, int index, void* element) { if (index >= v->size) { - perror("index out of range\n"); + fprintf(stderr, "index out of range\n"); exit(EXIT_FAILURE); } @@ -384,7 +389,7 @@ vectorInsertAt(vector_t* v, int index, void* element) { *((vector_t *)(v->data) + index) = *((vector_t *)element); break; default: - perror("specify a valid vector type\n"); + fprintf(stderr, "specify a valid vector type\n"); exit(EXIT_FAILURE); } } diff --git a/test.txt b/test.txt index c009799..9985556 100644 --- a/test.txt +++ b/test.txt @@ -1,6 +1,6 @@ -this !1@#% -is a big -mega such large - -test +this is a test +mega such large test and + +this was an empty line +!#%!@# <- symbols!