123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 | //
// utils.c - Some helper code for debugging and error logging.
//
#include <ctype.h>
#include <err.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include "utils.h"
//
// Helper function to skip past all spaces (and comments)
// Returns a pointer to the first non-space character.
//
public
const char *after_spaces(const char *str, bool skip_nl, const char *end) {
// Skip whitespace and comments:
skip_whitespace:
if (str >= end) return str;
switch (*str) {
case '\r':
case '\n':
if (!skip_nl) break;
__attribute__((fallthrough));
case ' ':
case '\t': {
++str;
goto skip_whitespace;
}
case '#': {
while (str < end && *str != '\n')
++str;
goto skip_whitespace;
}
default: break;
}
return str;
}
//
// Return the first character after a valid BP name, or NULL if none is
// found.
//
public
const char *after_name(const char *str, const char *end) {
if (str >= end) return end;
if (*str == '|') return &str[1];
if (*str == '^' || *str == '_' || *str == '$') {
return (&str[1] < end && str[1] == *str) ? &str[2] : &str[1];
}
if (!isalpha(*str)) return NULL;
for (++str; str < end; ++str) {
if (!(isalnum(*str) || *str == '-')) break;
}
return str;
}
//
// Check if a character is found and if so, move past it.
//
public
bool matchchar(const char **str, char c, bool skip_nl, const char *end) {
const char *next = after_spaces(*str, skip_nl, end);
if (next >= end) return false;
if (*next == c) {
*str = next + 1;
return true;
}
return false;
}
//
// Check if a string is found and if so, move past it.
//
public
bool matchstr(const char **str, const char *target, bool skip_nl, const char *end) {
const char *next = after_spaces(*str, skip_nl, end);
if (next + strlen(target) > end) return false;
if (strncmp(next, target, strlen(target)) == 0) {
*str = &next[strlen(target)];
return true;
}
return false;
}
//
// Process a string escape sequence for a character and return the
// character that was escaped.
// Set *end = the first character past the end of the escape sequence.
//
public
char unescapechar(const char *escaped, const char **after, const char *end) {
size_t len = 0;
unsigned char ret = '\\';
if (escaped >= end) goto finished;
ret = (unsigned char)*escaped;
++len;
switch (*escaped) {
case 'a': ret = '\a'; break;
case 'b': ret = '\b'; break;
case 'n': ret = '\n'; break;
case 'r': ret = '\r'; break;
case 't': ret = '\t'; break;
case 'v': ret = '\v'; break;
case 'e': ret = '\033'; break;
case '\\': ret = '\\'; break;
case 'x': { // Hex
static const unsigned char hextable[255] = {
['0'] = 0x10, ['1'] = 0x1, ['2'] = 0x2, ['3'] = 0x3, ['4'] = 0x4, ['5'] = 0x5, ['6'] = 0x6, ['7'] = 0x7,
['8'] = 0x8, ['9'] = 0x9, ['a'] = 0xa, ['b'] = 0xb, ['c'] = 0xc, ['d'] = 0xd, ['e'] = 0xe, ['f'] = 0xf,
['A'] = 0xa, ['B'] = 0xb, ['C'] = 0xc, ['D'] = 0xd, ['E'] = 0xe, ['F'] = 0xf,
};
if (escaped + 2 >= end) {
len = 0;
goto finished;
} else if (hextable[(int)escaped[1]] && hextable[(int)escaped[2]]) {
ret = (hextable[(int)escaped[1]] << 4) | (hextable[(int)escaped[2]] & 0xF);
len = 3;
}
break;
}
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7': { // Octal
ret = (unsigned char)(escaped[0] - '0');
if (escaped + 2 >= end) {
len = 0;
goto finished;
} else if ('0' <= escaped[1] && escaped[1] <= '7') {
++len;
ret = (ret << 3) | (escaped[1] - '0');
if ('0' <= escaped[2] && escaped[2] <= '7') {
++len;
ret = (ret << 3) | (escaped[2] - '0');
}
}
break;
}
default: len = 0; goto finished;
}
finished:
if (after) *after = &escaped[len];
return (char)ret;
}
//
// Free memory, but also set the pointer to NULL for safety
//
public
void delete(void *p) {
if (!p || *(void **)p == NULL) errx(EXIT_FAILURE, "attempt to free(NULL)");
free(*(void **)p);
*((void **)p) = NULL;
}
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
|