mirror of
https://github.com/zeldaret/oot.git
synced 2024-11-15 06:06:04 +00:00
145 lines
3.8 KiB
C
145 lines
3.8 KiB
C
|
/* SPDX-FileCopyrightText: Copyright (C) 2024 ZeldaRET */
|
||
|
/* SPDX-License-Identifier: CC0-1.0 */
|
||
|
#define _GNU_SOURCE
|
||
|
#include <ctype.h>
|
||
|
#include <stdarg.h>
|
||
|
#include <stdbool.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <errno.h>
|
||
|
|
||
|
#include "util.h"
|
||
|
|
||
|
// TODO ideally we should be collecting all errors and displaying them all before exiting
|
||
|
|
||
|
NORETURN void
|
||
|
error(const char *fmt, ...)
|
||
|
{
|
||
|
va_list ap;
|
||
|
va_start(ap, fmt);
|
||
|
fprintf(stderr, "\x1b[91m"
|
||
|
"Error: "
|
||
|
"\x1b[97m");
|
||
|
vfprintf(stderr, fmt, ap);
|
||
|
fprintf(stderr, "\x1b[0m"
|
||
|
"\n");
|
||
|
va_end(ap);
|
||
|
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
warning(const char *fmt, ...)
|
||
|
{
|
||
|
va_list ap;
|
||
|
va_start(ap, fmt);
|
||
|
fprintf(stderr, "\x1b[95m"
|
||
|
"Warning: "
|
||
|
"\x1b[97m");
|
||
|
vfprintf(stderr, fmt, ap);
|
||
|
fprintf(stderr, "\x1b[0m"
|
||
|
"\n");
|
||
|
va_end(ap);
|
||
|
}
|
||
|
|
||
|
void *
|
||
|
util_read_whole_file(const char *filename, size_t *size_out)
|
||
|
{
|
||
|
FILE *file = fopen(filename, "rb");
|
||
|
void *buffer = NULL;
|
||
|
size_t size;
|
||
|
|
||
|
if (file == NULL)
|
||
|
error("failed to open file '%s' for reading: %s", filename, strerror(errno));
|
||
|
|
||
|
// get size
|
||
|
fseek(file, 0, SEEK_END);
|
||
|
size = ftell(file);
|
||
|
|
||
|
// if the file is empty, return NULL buffer and 0 size
|
||
|
if (size != 0) {
|
||
|
// allocate buffer
|
||
|
buffer = malloc(size + 1);
|
||
|
if (buffer == NULL)
|
||
|
error("could not allocate buffer for file '%s'", filename);
|
||
|
|
||
|
// read file
|
||
|
fseek(file, 0, SEEK_SET);
|
||
|
if (fread(buffer, size, 1, file) != 1)
|
||
|
error("error reading from file '%s': %s", filename, strerror(errno));
|
||
|
|
||
|
// null-terminate the buffer (in case of text files)
|
||
|
((char *)buffer)[size] = '\0';
|
||
|
}
|
||
|
|
||
|
fclose(file);
|
||
|
|
||
|
if (size_out != NULL)
|
||
|
*size_out = size;
|
||
|
return buffer;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
util_write_whole_file(const char *filename, const void *data, size_t size)
|
||
|
{
|
||
|
FILE *file = fopen(filename, "wb");
|
||
|
|
||
|
if (file == NULL)
|
||
|
error("failed to open file '%s' for writing: %s", filename, strerror(errno));
|
||
|
|
||
|
if (fwrite(data, size, 1, file) != 1)
|
||
|
error("error writing to file '%s': %s", filename, strerror(errno));
|
||
|
|
||
|
fclose(file);
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
str_is_c_identifier(const char *str)
|
||
|
{
|
||
|
// A C language identifier must:
|
||
|
// - ONLY contain [_, abc..xyz, ABC..XYZ, 0..9] (we do not support unicode or extensions like $)
|
||
|
// - NOT be a keyword
|
||
|
// - NOT start with a digit [0..9]
|
||
|
|
||
|
static const char *const c_kwds[] = {
|
||
|
"auto", "break", "case", "char", "const", "continue", "default", "do",
|
||
|
"double", "else", "enum", "extern", "float", "for", "goto", "if",
|
||
|
"inline", "int", "long", "register", "restrict", "return", "short", "signed",
|
||
|
"sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void",
|
||
|
"volatile", "while",
|
||
|
|
||
|
"_Alignas", "_Alignof", "_Atomic", "_Bool", "_Complex", "_Generic", "_Imaginary", "_Noreturn",
|
||
|
"_Static_assert", "_Thread_local",
|
||
|
};
|
||
|
|
||
|
if (str == NULL) {
|
||
|
return false;
|
||
|
}
|
||
|
if (isdigit(str[0])) {
|
||
|
// Starts with a digit, fail
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
size_t len = strlen(str);
|
||
|
for (size_t i = 0; i < len; i++) {
|
||
|
char c = str[i];
|
||
|
|
||
|
bool alpha = isalpha(c);
|
||
|
bool digit = isdigit(c);
|
||
|
bool uscore = c == '_';
|
||
|
|
||
|
if (!(alpha || digit || uscore)) {
|
||
|
// Contains bad character, fail
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (size_t i = 0; i < ARRAY_COUNT(c_kwds); i++) {
|
||
|
if (strequ(str, c_kwds[i])) {
|
||
|
// Matched a C keyword, fail
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|