code / bp

Lines4.3K C3.3K Markdown541 YAML273 make110 Shell77 Lua54
(164 lines)
1 //
2 // utils.c - Some helper code for debugging and error logging.
3 //
5 #include <ctype.h>
6 #include <err.h>
7 #include <stdarg.h>
8 #include <stdbool.h>
9 #include <stdlib.h>
10 #include <unistd.h>
12 #include "utils.h"
14 //
15 // Helper function to skip past all spaces (and comments)
16 // Returns a pointer to the first non-space character.
17 //
18 public
19 const char *after_spaces(const char *str, bool skip_nl, const char *end) {
20 // Skip whitespace and comments:
21 skip_whitespace:
22 if (str >= end) return str;
23 switch (*str) {
24 case '\r':
25 case '\n':
26 if (!skip_nl) break;
27 __attribute__((fallthrough));
28 case ' ':
29 case '\t': {
30 ++str;
31 goto skip_whitespace;
33 case '#': {
34 while (str < end && *str != '\n')
35 ++str;
36 goto skip_whitespace;
38 default: break;
40 return str;
43 //
44 // Return the first character after a valid BP name, or NULL if none is
45 // found.
46 //
47 public
48 const char *after_name(const char *str, const char *end) {
49 if (str >= end) return end;
50 if (*str == '|') return &str[1];
51 if (*str == '^' || *str == '_' || *str == '$') {
52 return (&str[1] < end && str[1] == *str) ? &str[2] : &str[1];
54 if (!isalpha(*str)) return NULL;
55 for (++str; str < end; ++str) {
56 if (!(isalnum(*str) || *str == '-')) break;
58 return str;
61 //
62 // Check if a character is found and if so, move past it.
63 //
64 public
65 bool matchchar(const char **str, char c, bool skip_nl, const char *end) {
66 const char *next = after_spaces(*str, skip_nl, end);
67 if (next >= end) return false;
68 if (*next == c) {
69 *str = next + 1;
70 return true;
72 return false;
75 //
76 // Check if a string is found and if so, move past it.
77 //
78 public
79 bool matchstr(const char **str, const char *target, bool skip_nl, const char *end) {
80 const char *next = after_spaces(*str, skip_nl, end);
81 if (next + strlen(target) > end) return false;
82 if (strncmp(next, target, strlen(target)) == 0) {
83 *str = &next[strlen(target)];
84 return true;
86 return false;
89 //
90 // Process a string escape sequence for a character and return the
91 // character that was escaped.
92 // Set *end = the first character past the end of the escape sequence.
93 //
94 public
95 char unescapechar(const char *escaped, const char **after, const char *end) {
96 size_t len = 0;
97 unsigned char ret = '\\';
98 if (escaped >= end) goto finished;
99 ret = (unsigned char)*escaped;
100 ++len;
101 switch (*escaped) {
102 case 'a': ret = '\a'; break;
103 case 'b': ret = '\b'; break;
104 case 'n': ret = '\n'; break;
105 case 'r': ret = '\r'; break;
106 case 't': ret = '\t'; break;
107 case 'v': ret = '\v'; break;
108 case 'e': ret = '\033'; break;
109 case '\\': ret = '\\'; break;
110 case 'x': { // Hex
111 static const unsigned char hextable[255] = {
112 ['0'] = 0x10, ['1'] = 0x1, ['2'] = 0x2, ['3'] = 0x3, ['4'] = 0x4, ['5'] = 0x5, ['6'] = 0x6, ['7'] = 0x7,
113 ['8'] = 0x8, ['9'] = 0x9, ['a'] = 0xa, ['b'] = 0xb, ['c'] = 0xc, ['d'] = 0xd, ['e'] = 0xe, ['f'] = 0xf,
114 ['A'] = 0xa, ['B'] = 0xb, ['C'] = 0xc, ['D'] = 0xd, ['E'] = 0xe, ['F'] = 0xf,
116 if (escaped + 2 >= end) {
117 len = 0;
118 goto finished;
119 } else if (hextable[(int)escaped[1]] && hextable[(int)escaped[2]]) {
120 ret = (hextable[(int)escaped[1]] << 4) | (hextable[(int)escaped[2]] & 0xF);
121 len = 3;
123 break;
125 case '0':
126 case '1':
127 case '2':
128 case '3':
129 case '4':
130 case '5':
131 case '6':
132 case '7': { // Octal
133 ret = (unsigned char)(escaped[0] - '0');
134 if (escaped + 2 >= end) {
135 len = 0;
136 goto finished;
137 } else if ('0' <= escaped[1] && escaped[1] <= '7') {
138 ++len;
139 ret = (ret << 3) | (escaped[1] - '0');
140 if ('0' <= escaped[2] && escaped[2] <= '7') {
141 ++len;
142 ret = (ret << 3) | (escaped[2] - '0');
145 break;
147 default: len = 0; goto finished;
149 finished:
150 if (after) *after = &escaped[len];
151 return (char)ret;
155 // Free memory, but also set the pointer to NULL for safety
157 public
158 void delete(void *p) {
159 if (*(void **)p == NULL) errx(EXIT_FAILURE, "attempt to free(NULL)");
160 free(*(void **)p);
161 *((void **)p) = NULL;
164 // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0