diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..81558dd --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +obj +main +tail +test* +chatgpt.c diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..be950bf --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug", + "program": "${workspaceFolder}/main", + "args": ["./src/tail.c", "-n", "20"], + "cwd": "${workspaceFolder}", + "preLaunchTask": "Makefile", + "env": { + "ASAN_OPTIONS": "detect_leaks=0" + } + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..9cf8272 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "makefile.launchConfigurations": [ + { + "cwd": "/home/user/coding/ijc/2hw", + "binaryPath": "/home/user/coding/ijc/2hw/tail", + "binaryArgs": [] + } + ] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..2385146 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,10 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Makefile", + "type": "shell", + "command": "make", + } + ] +} diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..59591be --- /dev/null +++ b/Makefile @@ -0,0 +1,28 @@ +CC := clang +CFLAGS := -g -Wall -std=c11 -pedantic -Wextra -O3 -fsanitize=address +CREATEDIR := $(shell mkdir -p obj) + +.PHONY: all +all: tail #wordcount wordcount-dynamic +# a knihovny "libhtab.a", "libhtab.so + +tail: obj/tail.o + $(CC) $(CFLAGS) -lm $^ -o $@ + +# \ +wordcount: obj/wordcount.o obj/htab.o \ + $(CC) $(CFLAGS) -lm $^ -o $@ \ + \ +wordcount-dynamic: obj/wordcount.o obj/htab.o \ + $(CC) $(CFLAGS) -lm $^ -o $@ + + +obj/tail.o: src/tail.c + +obj/%.o: src/%.c + $(CC) $(CFLAGS) $< -c -o $@ + + +.PHONY: clean +clean: + -rm -r ./obj/ primes primes-i steg-decode diff --git a/src/htab.c b/src/htab.c new file mode 100644 index 0000000..605c095 --- /dev/null +++ b/src/htab.c @@ -0,0 +1,107 @@ +#include +#include "htab.h" + +struct htab_item { + struct htab_pair data; + struct htab_item* next; +}; + +struct htab { + size_t size; + size_t arr_size; + struct htab_item** arr_ptr; +}; + +size_t htab_hash_function(htab_key_t str) { + unsigned h = 0; // musí mít 32 bitů + const unsigned char *p; + for(p = (const unsigned char*)str; *p != '\0'; p++) + h = 65599 * h + *p; + return h; +} + +htab_t *htab_init(const size_t n) { + htab_t *t = malloc(sizeof(htab_t)); + if (!t) { + fputs("couldn't allocate memory for hash tab", stderr); + return NULL; + } + + t->arr_ptr = malloc(n * sizeof(struct htab_item*)); + if (!t->arr_ptr) { + fputs("couldn't allocate memory for array of pointers", stderr); + free(t); + return NULL; + } + + t->size = 0; + t->arr_size = n; + + return t; +} + +size_t htab_size(const htab_t *t) { + return t->size; +} + +size_t htab_bucket_count(const htab_t *t) { + return t->arr_size; +} + +htab_pair_t *htab_find(const htab_t *t, htab_key_t key) { + size_t index = (htab_hash_function(key) % t->arr_size); + struct htab_item* curr_item = t->arr_ptr[index]; + while (curr_item->next != NULL) { + if (strcmp(curr_item->data.key, key) == 0) { + return &(curr_item->data); + } + + curr_item = curr_item->next; + } + return NULL; +} + +htab_pair_t *htab_lookup_add(htab_t *t, htab_key_t key) { + htab_pair_t *return_pair; + if (return_pair = htab_find(t, key)) { + return_pair->value++; + return return_pair; + } + + struct htab_item *item_to_add = malloc(sizeof(struct htab_item)); + if (!item_to_add) { + fputs("couldn't allocate memory for item to add", stderr); + return NULL; + } + struct htab_pair data_to_add = {.key = malloc(sizeof(key)), .value = 1}; + if (!data_to_add.key) { + fputs("couldn't allocate memory for key in item to add", stderr); + free(item_to_add); + return NULL; + } + data_to_add.key = key; + item_to_add->data = data_to_add; + + size_t index = (htab_hash_function(key) % t->arr_size); + struct htab_item* curr_item = t->arr_ptr[index]; + while (curr_item->next != NULL) { + curr_item = curr_item->next; + } + + curr_item->next = item_to_add; +} + +bool htab_erase(htab_t *t, htab_key_t key) { +} + +void htab_for_each(const htab_t *t, void (*f)(htab_pair_t *data)) { +} + +void htab_clear(htab_t *t) { +} + +void htab_free(htab_t *t) { +} + +void htab_statistics(const htab_t *t) { +} diff --git a/src/htab.h b/src/htab.h new file mode 100644 index 0000000..f4846bb --- /dev/null +++ b/src/htab.h @@ -0,0 +1,49 @@ +// htab.h -- rozhraní knihovny htab (řešení IJC-DU2) +// Licence: žádná (Public domain) + +// následující řádky zabrání násobnému vložení: +#ifndef HTAB_H__ +#define HTAB_H__ + +#include // size_t +#include // bool + +// Tabulka: +struct htab; // neúplná deklarace struktury - uživatel nevidí obsah +typedef struct htab htab_t; // typedef podle zadání + +// Typy: +typedef const char * htab_key_t; // typ klíče +typedef int htab_value_t; // typ hodnoty + +// Dvojice dat v tabulce: +typedef struct htab_pair { + htab_key_t key; // klíč + htab_value_t value; // asociovaná hodnota +} htab_pair_t; // typedef podle zadání + +// Rozptylovací (hash) funkce (stejná pro všechny tabulky v programu) +// Pokud si v programu definujete stejnou funkci, použije se ta vaše. +size_t htab_hash_function(htab_key_t str); + +// Funkce pro práci s tabulkou: +htab_t *htab_init(const size_t n); // konstruktor tabulky +size_t htab_size(const htab_t * t); // počet záznamů v tabulce +size_t htab_bucket_count(const htab_t * t); // velikost pole + +htab_pair_t * htab_find(const htab_t * t, htab_key_t key); // hledání +htab_pair_t * htab_lookup_add(htab_t * t, htab_key_t key); + +bool htab_erase(htab_t * t, htab_key_t key); // ruší zadaný záznam + +// for_each: projde všechny záznamy a zavolá na ně funkci f +// Pozor: f nesmí měnit klíč .key ani přidávat/rušit položky +void htab_for_each(const htab_t * t, void (*f)(htab_pair_t *data)); + +void htab_clear(htab_t * t); // ruší všechny záznamy +void htab_free(htab_t * t); // destruktor tabulky + +// výpočet a tisk statistik délky seznamů (min,max,avg) do stderr: +void htab_statistics(const htab_t * t); + +#endif // HTAB_H__ diff --git a/src/tail.c b/src/tail.c new file mode 100644 index 0000000..0d83119 --- /dev/null +++ b/src/tail.c @@ -0,0 +1,217 @@ +// main.c +// Řešení IJC-DU2, příklad 1), 18.4.2023 +// Autor: Matyáš Oujezdský, FIT +// Přeloženo: clang version 10.0.0-4ubuntu1 +#include +#include +#include +#include +#include + +#define MAX_LINE_SIZE 4095 + +typedef struct cb { + char **data; // array of lines + size_t size; // size of the buffer + size_t count; // current number of lines in buffer + size_t start; // index of first line + size_t end; // index of last line +} cb_t; + +// allocate neccesary memory and return pointer to circular buffer +cb_t *cb_create(size_t n) { + cb_t *buffer = malloc(sizeof(cb_t)); + if (!buffer) { + fputs("couldn't allocate memory for buffer\n", stderr); + return NULL; + } + + buffer->data = calloc(n, sizeof(char *)); + if (!buffer->data) { + fputs("couldn't allocate memory for lines in buffer\n", stderr); + free(buffer); + return NULL; + } + + buffer->size = n; + buffer->count = 0; + buffer->start = 0; + buffer->end = 0; + + return buffer; +} + +/* +input line to circular buffer at write pointer +and increase write pointer +*/ +char *cb_put(cb_t *cb, char *line) { + char *old_string = cb->data[cb->end]; + + cb->data[cb->end++] = line; + cb->end %= cb->size; + if (cb->count < cb->size) { + cb->count++; + } + if (old_string) { + cb->start++; + cb->start %= cb->size; + } + return old_string; +} + +/* +return string at the read pointer of circural buffer +and increase read pointer +*/ +char *cb_get(cb_t *cb) { + if (cb->count == 0) { + fputs("buffer is empty\n", stderr); + return NULL; + } + char *curr_line = cb->data[cb->start++]; + cb->start %= cb->size; + cb->count--; + return curr_line; +} + +// free every allocated part of circular buffer +void cb_free(cb_t *cb) { + for (size_t i = 0; i < cb->size; ++i) { + if (cb->data[i]) { + free(cb->data[i]); + } + } + free(cb->data); + free(cb); +} + +// reads file by character and at each \n sends the line to cb_put +void read_file(cb_t *cb, FILE *stream) { + char tmp_char; + char *tmp_string = malloc(MAX_LINE_SIZE); + if (!tmp_string) { + fputs("couldn't allocate memory for tmp_string", stderr); + return; + } + + bool exceeded = false; + bool discard_rest = false; + + int i = 0; + while ((tmp_char = fgetc(stream)) != EOF) { + if (i + 1 >= MAX_LINE_SIZE) { + if (!exceeded) { + fputs("exceeded max line length\n", stderr); + exceeded = true; + } + + discard_rest = true; + } + + if (!discard_rest) { + tmp_string[i] = tmp_char; + } + + // at \n end the string by '\0' and send the string to cb_put + if (tmp_char == '\n') { + tmp_string[i] = 0; + tmp_string = cb_put(cb, tmp_string); + if (!tmp_string) { + tmp_string = malloc(MAX_LINE_SIZE); + } + i = 0; + } + + else if (!discard_rest) { + ++i; + } + } + + if (tmp_string) { + free(tmp_string); + } +} + +// print help (kind of self-explainatory) +void print_help(char *file_name) { + printf("Usage: %s [OPTION] [FILE]\n", file_name); + printf("Print the last 10 lines of each FILE to standard output.\n\n\ +With no FILE, read standard input.\n\n\ +Mandatory arguments to long options are mandatory for short options too.\n\n\ +OPTION%30sFUNCTION\n", + ""); + printf("-n, --number NUM%20soutput the last NUM lines (NUM must be > 1),\n\ +%38sinstead of the last 10\n\n", + "", ""); + printf("-h, --help%26soutput the last NUM lines (NUM must be > 1),\n\ +%38sinstead of the last 10\n", + "", ""); +} + +int main(int argc, char **argv) { + + size_t size = 10; + FILE *stream = stdin; + + + // make sure to read only first instance of -n or file names + bool file_read = false; + bool num_read = false; + + for (int i = 1; i < argc; i++) { + // a argument -h or --help was passed, so print help + if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { + print_help(argv[0]); + return 0; + } + + // a argument -n or --number was passed, + // so chech next argument for number + if ((strcmp(argv[i], "-n") == 0 || + strcmp(argv[i], "--number") == 0) && !num_read) { + + num_read = true; + + // check if there is a next argument and read it + if (argc > i + 1) { + size = atoi(argv[i + 1]); + + // this happens with numbers too big or negative + if (size > LONG_MAX) { + print_help(argv[0]); + return 1; + } + } + + // if the number is zero or not inputed + if (size == 0 || argc <= i + 1) { + print_help(argv[0]); + return 1; + } + + // skip the number in the next loop cycle + i++; + } + + // open the file and set it to be read from + else if (!file_read) { + file_read = true; + stream = fopen(argv[i], "r"); + if (!stream) { + fprintf(stderr, "Counldn't open a file with name %s\n", argv[i]); + return 1; + } + } + } + + cb_t *cb = cb_create(size); + if (!cb || !cb->data) { + return 1; + } + read_file(cb, stream); + while (cb->count) { + printf("%s\n", cb_get(cb)); + } + cb_free(cb); +} diff --git a/src/wordcount.c b/src/wordcount.c new file mode 100644 index 0000000..e69de29