utils.h

View source code here on GitHub!

Includes

char *get_data_file(const char *name)

Return a character array containing the whole contents of a file found in _data.

char *get_parent_directory(const char *name, const uint32_t levels)
Answer get_answer(const uint16_t id)

Return an Answer representing a given problem's solution.

struct Answer

The Answer struct represents the solution to a Project Euler problems. It can hold different types of values and an identifier.

union value
uint8_t int8

Represents an 8-bit signed integer.

uint16_t int16

Represents a 16-bit signed integer.

uint32_t int32

Represents a 32-bit signed integer.

uint64_t int64

Represents a 64-bit signed integer.

uint8_t uint8

Represents an 8-bit unsigned integer.

uint16_t uint16

Represents a 16-bit unsigned integer.

uint32_t uint32

Represents a 32-bit unsigned integer.

uint64_t uint64

Represents a 64-bit unsigned integer.

char *string

Represents a string value.

uint16_t id

Represents the problem ID.

AnswerType type

Specifies the type of value stored in the value union. It is of type AnswerType.

enum AnswerType

The AnswerType enum defines various types of values that can be stored in an Answer.

enumerator ERRORT

Represents an error state.

enumerator INT8T

Represents an 8-bit signed integer.

enumerator INT16T

Represents a 16-bit signed integer.

enumerator INT32T

Represents a 32-bit signed integer.

enumerator INT64T

Represents a 64-bit signed integer.

enumerator UINT8T

Represents an 8-bit unsigned integer.

enumerator UINT16T

Represents a 16-bit unsigned integer.

enumerator UINT32T

Represents a 32-bit unsigned integer.

enumerator UINT64T

Represents a 64-bit unsigned integer.

enumerator STRINGT

Represents a string value.

  1#pragma once
  2#include <inttypes.h>
  3#include <stdint.h>
  4#include <stdlib.h>
  5#include <string.h>
  6#include <stdio.h>
  7#include "macros.h"
  8#ifdef _WIN32
  9#include <direct.h>
 10#include <windows.h>
 11#define PATH_SEPARATOR "\\"
 12#else
 13#include <libgen.h>
 14#include <unistd.h>
 15#define PATH_SEPARATOR "/"
 16#endif
 17
 18char* get_parent_directory(char* path, const uint32_t levels) {
 19#ifdef _WIN32
 20    static char drive[_MAX_DRIVE];
 21    static char dir[_MAX_DIR];
 22    _splitpath(path, drive, dir, NULL, NULL);
 23    for (uint32_t i = 0; i < levels; ++i) {
 24        size_t len = strlen(dir);
 25        if (len > 1 && (dir[len - 1] == '\\' || dir[len - 1] == '/'))
 26            dir[len - 1] = '\0';
 27        char* last_slash = strrchr(dir, '\\');
 28        if (!last_slash)
 29            last_slash = strrchr(dir, '/');
 30        if (last_slash)
 31            *last_slash = '\0';
 32    }
 33    static char parent_dir[_MAX_PATH];
 34    snprintf(parent_dir, sizeof(parent_dir), "%s%s", drive, dir);
 35    return parent_dir;
 36#else
 37    char* dir = dirname(path);
 38    for (uint32_t i = 0; i < levels; ++i) {
 39        dir = dirname(dir);
 40        if (strcmp(dir, "/") == 0 || strcmp(dir, ".") == 0)
 41            break;
 42    }
 43    return dir;
 44#endif
 45}
 46
 47char *get_data_file(const char *name) {
 48    const char* start = __FILE__;
 49#ifdef _WIN32
 50    char absolute_path[MAX_PATH];
 51    if (!_fullpath(absolute_path, start, MAX_PATH)) {
 52        perror("_fullpath");
 53        return NULL;
 54    }
 55#else
 56    char* absolute_path = realpath(start, NULL);
 57    if (!absolute_path) {
 58        perror("realpath");
 59        return NULL;
 60    }
 61#endif
 62    char* parents_dir = get_parent_directory(absolute_path, 3);
 63    const size_t p_len = strlen(parents_dir),
 64    name_len = strlen(name);
 65    char *file_path = (char *)malloc(p_len + name_len + 8);
 66    memcpy(file_path, parents_dir, p_len);
 67    memcpy(file_path + p_len, PATH_SEPARATOR "_data" PATH_SEPARATOR, 7);
 68    memcpy(file_path + p_len + 7, name, name_len);
 69    file_path[p_len + name_len + 7] = 0;
 70    FILE* file = fopen(file_path, "r");
 71#ifndef _WIN32
 72    free(absolute_path);
 73#endif
 74//    free(parents_dir);
 75    free(file_path);
 76    if (!file) {
 77        perror("fopen");
 78        return NULL;
 79    }
 80
 81    fseek(file, 0, SEEK_END);
 82    size_t length = ftell(file);
 83    fseek(file, 0, SEEK_SET);
 84
 85    char* buffer = (char*)malloc(length + 1);
 86    if (!buffer) {
 87        perror("malloc");
 88        fclose(file);
 89        return NULL;
 90    }
 91
 92    const size_t ret_code = fread(buffer, 1, length, file);
 93    if (ret_code != length) {
 94        if (feof(file))
 95            printf("Error reading %s: unexpected end of file, read %" PRIu64 " of %"PRIu64" bytes expected\n", name, (uint64_t)ret_code, (uint64_t)length);
 96        else if (ferror(file))
 97            perror("Error reading data file");
 98    }
 99
100    buffer[length] = 0;
101    fclose(file);
102
103    return buffer;
104}
105
106typedef enum {
107	ERRORT,
108	INT8T,
109	INT16T,
110	INT32T,
111	INT64T,
112	UINT8T,
113	UINT16T,
114	UINT32T,
115	UINT64T,
116	STRINGT,
117} AnswerType;
118
119typedef struct {
120    union {
121        uint8_t uint8;
122        uint16_t uint16;
123        uint32_t uint32;
124        uint64_t uint64;
125        int8_t int8;
126        int16_t int16;
127        int32_t int32;
128        int64_t int64;
129        char *string;
130    } value;
131	uint16_t id;
132	AnswerType type;
133} Answer;
134
135#ifdef _WIN32
136#define strtok_r strtok_s
137#endif
138
139Answer EMSCRIPTEN_KEEPALIVE get_answer(uint16_t id) {
140    Answer ret = {
141        .id = id,
142    };
143    char *answers = get_data_file("answers.tsv");
144    char *linepointer, *tabpointer;
145
146    if (!answers) {
147        fprintf(stderr, "Error: Unable to get data from file\n");
148        return ret;
149    }
150
151    char s_id[6];
152    snprintf(s_id, sizeof(s_id), "%" PRIu16, id);
153
154    char *line = strtok_r(answers, "\n", &linepointer);  // skip header
155    while ((line = strtok_r(NULL, "\n", &linepointer)) != NULL) {
156        char *token = strtok_r(line, "\t", &tabpointer);
157        if (strcmp(token, s_id) != 0)
158            continue;
159
160        token = strtok_r(NULL, "\t", &tabpointer);
161        if (!token)
162            continue;
163
164        if (strcmp(token, "uint") == 0)
165            ret.type = UINT8T;  // will adjust size later
166        else if (strcmp(token, "int") == 0)
167            ret.type = INT8T;  // will adjust size later
168        else if (strcmp(token, "str") == 0)
169            ret.type = STRINGT;
170        else {
171            fprintf(stderr, "Error: Unknown type '%s'\n", token);
172            return ret;
173        }
174
175        token = strtok_r(NULL, "\t", &tabpointer);
176        if (!token)
177            continue;
178        size_t size = strtoull(token, NULL, 10);
179
180        token = strtok_r(NULL, "\t", &tabpointer);
181        if (!token)
182            continue;
183
184        switch (ret.type) {
185            case UINT8T:
186            case UINT16T:
187            case UINT32T:
188            case UINT64T:
189                switch (size) {
190                    case 8:
191                        ret.value.uint8 = (uint8_t)strtoul(token, NULL, 10);
192                        break;
193                    case 16:
194                        ret.value.uint16 = (uint16_t)strtoul(token, NULL, 10);
195                        ret.type = UINT16T;
196                        break;
197                    case 32:
198                        ret.value.uint32 = strtoul(token, NULL, 10);
199                        ret.type = UINT32T;
200                        break;
201                    case 64:
202                        ret.value.uint64 = strtoull(token, NULL, 10);
203                        ret.type = UINT64T;
204                        break;
205                    default:
206                        fprintf(stderr, "Error: Unsupported uint size %" PRIu64 "\n", (uint64_t)size);
207                        Answer err = {0};
208                        return err;
209                }
210                break;
211            case INT8T:
212            case INT16T:
213            case INT32T:
214            case INT64T:
215                switch (size) {
216                    case 8:
217                        ret.value.int8 = (int8_t)strtol(token, NULL, 10);
218                        break;
219                    case 16:
220                        ret.value.int16 = (int16_t)strtol(token, NULL, 10);
221                        ret.type = INT16T;
222                        break;
223                    case 32:
224                        ret.value.int32 = strtol(token, NULL, 10);
225                        ret.type = INT32T;
226                        break;
227                    case 64:
228                        ret.value.int64 = strtoll(token, NULL, 10);
229                        ret.type = INT64T;
230                        break;
231                    default:
232                        fprintf(stderr, "Error: Unsupported int size %" PRIu64 "\n", (uint64_t)size);
233                        Answer err = {0};
234                        return err;
235                }
236                break;
237            case STRINGT:
238                ret.value.string = (char *)malloc(size + 1);
239                if (ret.value.string) {
240                    strncpy(ret.value.string, token, size);
241                    ret.value.string[size] = 0;
242                } else {
243                    fprintf(stderr, "Error: Memory allocation failed for string\n");
244                    Answer err = {0};
245                    return err;
246                }
247                break;
248            case ERRORT:
249                fprintf(stderr, "Error: Unknown type (should be unreachable)\n");
250                Answer err = {0};
251                return err;
252        }
253        break;
254    }
255
256    free(answers);
257    return ret;
258}
259
260#ifdef _WIN32
261#undef strtok_r
262#endif

Tags: file-io