aboutsummaryrefslogtreecommitdiffstats
path: root/util.c
blob: 59db9cb9b8af9f8b51589006849a1f292f9d0477 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <elf.h>
#include <sys/types.h>
#include <regex.h>
#include "logging.h"
#include "util.h"

/*
 * Read one logical line from a configuration file.
 *
 * Line endings may be escaped with backslashes, to form one logical line from
 * several physical lines.  No end of line character(s) are included in the
 * result.
 *
 * If linenum is not NULL, it is incremented by the number of physical lines
 * which have been read.
 */
char *getline_wrapped(FILE *file, unsigned int *linenum)
{
	int size = 256;
	int i = 0;
	char *buf = NOFAIL(malloc(size));
	for(;;) {
		int ch = getc_unlocked(file);

		switch(ch) {
		case EOF:
			if (i == 0) {
				free(buf);
				return NULL;
			}
			/* else fall through */

		case '\n':
			if (linenum)
				(*linenum)++;
			if (i == size)
				buf = NOFAIL(realloc(buf, size + 1));
			buf[i] = '\0';
			return buf;

		case '\\':
			ch = getc_unlocked(file);

			if (ch == '\n') {
				if (linenum)
					(*linenum)++;
				continue;
			}
			/* else fall through */

		default:
			buf[i++] = ch;

			if (i == size) {
				size *= 2;
				buf = NOFAIL(realloc(buf, size));
			}
		}
	}
}

/*
 * Convert filename to the module name.  Works if filename == modname, too.
 */
void filename2modname(char *modname, const char *filename)
{
	const char *afterslash;
	unsigned int i;

	afterslash = my_basename(filename);

	/* Convert to underscores, stop at first . */
	for (i = 0; afterslash[i] && afterslash[i] != '.'; i++) {
		if (afterslash[i] == '-')
			modname[i] = '_';
		else
			modname[i] = afterslash[i];
	}
	modname[i] = '\0';
}

/*
 * Replace dashes with underscores.
 * Dashes inside character range patterns (e.g. [0-9]) are left unchanged.
 */
char *underscores(char *string)
{
	unsigned int i;

	if (!string)
		return NULL;

	for (i = 0; string[i]; i++) {
		switch (string[i]) {
		case '-':
			string[i] = '_';
			break;

		case ']':
			warn("Unmatched bracket in %s\n", string);
			break;

		case '[':
			i += strcspn(&string[i], "]");
			if (!string[i])
				warn("Unmatched bracket in %s\n", string);
			break;
		}
	}
	return string;
}

/*
 * strtbl_add - add a string to a string table.
 *
 * @str: string to add
 * @tbl: current string table. NULL = allocate new table
 *
 * Allocates an array of pointers to strings.
 * The strings themselves are not actually kept in the table.
 *
 * Returns reallocated and updated string table. NULL = out of memory.
 *
 * Implementation note: The string table is designed to be lighter-weight
 * and faster than a more conventional linked list that stores the strings
 * in the list elements, as it does far fewer malloc/realloc calls
 * and avoids copying entirely.
 */
struct string_table *strtbl_add(const char *str, struct string_table *tbl)
{
	if (tbl == NULL) {
		const char max = 100;
		tbl = malloc(sizeof(*tbl) + sizeof(char *) * max);
		if (!tbl)
			return NULL;
		tbl->max = max;
		tbl->cnt = 0;
	}
	if (tbl->cnt >= tbl->max) {
		tbl->max *= 2;
		tbl = realloc(tbl, sizeof(*tbl) + sizeof(char *) * tbl->max);
		if (!tbl)
			return NULL;
	}
	tbl->str[tbl->cnt] = str;
	tbl->cnt += 1;

	return tbl;
}

/*
 * strtbl_free - string table destructor
 */
void strtbl_free(struct string_table *tbl)
{
	free(tbl);
}

/*
 * Get the basename in a pathname.
 * Unlike the standard implementation, this does not copy the string.
 */
char *my_basename(const char *path)
{
	const char *base = strrchr(path, '/');
	if (base)
		return (char *) base + 1;
	return (char *) path;
}

/*
 * Find the next string in an ELF section.
 */
const char *next_string(const char *string, unsigned long *secsize)
{
	/* Skip non-zero chars */
	while (string[0]) {
		string++;
		if ((*secsize)-- <= 1)
			return NULL;
	}

	/* Skip any zero padding. */
	while (!string[0]) {
		string++;
		if ((*secsize)-- <= 1)
			return NULL;
	}
	return string;
}

/*
 * Get CPU endianness. 0 = unknown, 1 = ELFDATA2LSB = little, 2 = ELFDATA2MSB = big
 */
int __attribute__ ((pure)) native_endianness()
{
	/* Encoding the endianness enums in a string and then reading that
	 * string as a 32-bit int, returns the correct endianness automagically.
	 */
	return (char) *((uint32_t*)("\1\0\0\2"));
}

/*
 * Compare "string" with extended regex "pattern". Include backward compatible
 * matching of "*" as a wildcard by replacing it with ".*" automatically.
 */
int regex_match(const char *string, const char *pattern)
{
	int status;
	regex_t re;
	char *fix_pattern;

	/* backward compatibility with old "match" code */
	if (strncmp("*", pattern, 1) != 0)
		fix_pattern = (char *)pattern;
	else
		fix_pattern = ".*"; /* match everything */

	if (regcomp(&re, fix_pattern, REG_EXTENDED|REG_NOSUB) != 0)
		return 0;	/* alloc failure */

	status = regexec(&re, string, (size_t) 0, NULL, 0);
	regfree(&re);

	if (status != 0)
		return 0;	/* no match */

	return 1;	/* match */
}