1 | /* |
2 | Copyright (c) 2016 Raspberry Pi (Trading) Ltd. |
3 | All rights reserved. |
4 | |
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the following conditions are met: |
7 | * Redistributions of source code must retain the above copyright |
8 | notice, this list of conditions and the following disclaimer. |
9 | * Redistributions in binary form must reproduce the above copyright |
10 | notice, this list of conditions and the following disclaimer in the |
11 | documentation and/or other materials provided with the distribution. |
12 | * Neither the name of the copyright holder nor the |
13 | names of its contributors may be used to endorse or promote products |
14 | derived from this software without specific prior written permission. |
15 | |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY |
20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 | */ |
27 | |
28 | #include <stdio.h> |
29 | #include <stdlib.h> |
30 | #include <string.h> |
31 | #include <unistd.h> |
32 | #include <stdarg.h> |
33 | #include <sys/stat.h> |
34 | #include <errno.h> |
35 | |
36 | #include "utils.h" |
37 | |
38 | #define OVERLAY_HELP_INDENT 8 |
39 | |
40 | int opt_verbose; |
41 | int opt_dry_run; |
42 | static STRING_T *allocated_strings; |
43 | |
44 | struct overlay_help_state_struct |
45 | { |
46 | FILE *fp; |
47 | long rec_pos; |
48 | int line_len; |
49 | int line_pos; |
50 | int blank_count; |
51 | int end_of_field; |
52 | char line_buf[82]; |
53 | }; |
54 | |
55 | static int overlay_help_get_line(OVERLAY_HELP_STATE_T *state); |
56 | |
57 | OVERLAY_HELP_STATE_T *overlay_help_open(const char *helpfile) |
58 | { |
59 | OVERLAY_HELP_STATE_T *state = NULL; |
60 | FILE *fp = fopen(helpfile, "r" ); |
61 | if (fp) |
62 | { |
63 | state = calloc(1, sizeof(OVERLAY_HELP_STATE_T)); |
64 | if (!state) |
65 | fatal_error("Out of memory" ); |
66 | state->fp = fp; |
67 | state->line_pos = -1; |
68 | state->rec_pos = -1; |
69 | } |
70 | |
71 | return state; |
72 | } |
73 | |
74 | void overlay_help_close(OVERLAY_HELP_STATE_T *state) |
75 | { |
76 | fclose(state->fp); |
77 | free(state); |
78 | } |
79 | |
80 | int overlay_help_find(OVERLAY_HELP_STATE_T *state, const char *name) |
81 | { |
82 | state->line_pos = -1; |
83 | state->rec_pos = -1; |
84 | state->blank_count = 0; |
85 | |
86 | fseek(state->fp, 0, SEEK_SET); |
87 | |
88 | while (overlay_help_find_field(state, "Name" )) |
89 | { |
90 | const char *overlay = overlay_help_field_data(state); |
91 | if (overlay && (strcmp(overlay, name) == 0)) |
92 | { |
93 | state->rec_pos = (long)ftell(state->fp); |
94 | return 1; |
95 | } |
96 | } |
97 | |
98 | return 0; |
99 | } |
100 | |
101 | int overlay_help_find_field(OVERLAY_HELP_STATE_T *state, const char *field) |
102 | { |
103 | int field_len = strlen(field); |
104 | int found = 0; |
105 | |
106 | if (state->rec_pos >= 0) |
107 | fseek(state->fp, state->rec_pos, SEEK_SET); |
108 | |
109 | while (!found) |
110 | { |
111 | int line_len = overlay_help_get_line(state); |
112 | if (line_len < 0) |
113 | break; |
114 | |
115 | /* Check for the "<field>:" prefix */ |
116 | if ((line_len >= (field_len + 1)) && |
117 | (state->line_buf[field_len] == ':') && |
118 | (memcmp(state->line_buf, field, field_len) == 0)) |
119 | { |
120 | /* Found it |
121 | If this initial line has no content then skip it */ |
122 | if (line_len > OVERLAY_HELP_INDENT) |
123 | state->line_pos = OVERLAY_HELP_INDENT; |
124 | else |
125 | state->line_pos = -1; |
126 | state->end_of_field = 0; |
127 | found = 1; |
128 | } |
129 | else |
130 | { |
131 | state->line_pos = -1; |
132 | } |
133 | } |
134 | |
135 | return found; |
136 | } |
137 | |
138 | const char *overlay_help_field_data(OVERLAY_HELP_STATE_T *state) |
139 | { |
140 | int line_len, pos; |
141 | |
142 | if (state->end_of_field) |
143 | return NULL; |
144 | |
145 | line_len = state->line_len; |
146 | |
147 | if ((state->line_pos < 0) || |
148 | (state->line_pos >= line_len)) |
149 | { |
150 | line_len = overlay_help_get_line(state); |
151 | |
152 | /* Fields end at the start of the next field or the end of the record */ |
153 | if ((line_len < 0) || (state->line_buf[0] != ' ')) |
154 | { |
155 | state->end_of_field = 1; |
156 | return NULL; |
157 | } |
158 | |
159 | if (line_len == 0) |
160 | return "" ; |
161 | } |
162 | |
163 | /* Return field data starting at OVERLAY_HELP_INDENT, if there is any */ |
164 | pos = line_len; |
165 | if (pos > OVERLAY_HELP_INDENT) |
166 | pos = OVERLAY_HELP_INDENT; |
167 | |
168 | state->line_pos = -1; |
169 | return &state->line_buf[pos]; |
170 | } |
171 | |
172 | void overlay_help_print_field(OVERLAY_HELP_STATE_T *state, |
173 | const char *field, const char *label, |
174 | int indent, int strip_blanks) |
175 | { |
176 | if (!overlay_help_find_field(state, field)) |
177 | return; |
178 | |
179 | while (1) |
180 | { |
181 | const char *line = overlay_help_field_data(state); |
182 | if (!line) |
183 | break; |
184 | |
185 | if (label) |
186 | { |
187 | int spaces = indent - strlen(label); |
188 | if (spaces < 0) |
189 | spaces = 0; |
190 | |
191 | printf("%s%*s%s\n" , label, spaces, "" , line); |
192 | label = NULL; |
193 | } |
194 | else if (line[0]) |
195 | { |
196 | printf("%*s%s\n" , indent, "" , line); |
197 | } |
198 | else if (!strip_blanks) |
199 | { |
200 | printf("\n" ); |
201 | } |
202 | } |
203 | |
204 | if (!strip_blanks) |
205 | printf("\n" ); |
206 | } |
207 | |
208 | /* Returns the length of the line, or -1 on end of file or record */ |
209 | static int overlay_help_get_line(OVERLAY_HELP_STATE_T *state) |
210 | { |
211 | int line_len; |
212 | |
213 | if (state->line_pos >= 0) |
214 | return state->line_len; |
215 | |
216 | get_next_line: |
217 | state->line_buf[sizeof(state->line_buf) - 1] = ' '; |
218 | line_len = -1; |
219 | if (fgets(state->line_buf, sizeof(state->line_buf), state->fp)) |
220 | { |
221 | // Check for overflow |
222 | |
223 | // Strip the newline |
224 | line_len = strlen(state->line_buf); |
225 | if (line_len && (state->line_buf[line_len - 1] == '\n')) |
226 | { |
227 | line_len--; |
228 | state->line_buf[line_len] = '\0'; |
229 | } |
230 | } |
231 | |
232 | if (state->rec_pos >= 0) |
233 | { |
234 | if (line_len == 0) |
235 | { |
236 | state->blank_count++; |
237 | if (state->blank_count >= 2) |
238 | return -1; |
239 | state->line_pos = 0; |
240 | goto get_next_line; |
241 | } |
242 | else if (state->blank_count) |
243 | { |
244 | /* Return a single blank line now - the non-empty line will be |
245 | returned next time */ |
246 | state->blank_count = 0; |
247 | return 0; |
248 | } |
249 | } |
250 | |
251 | state->line_len = line_len; |
252 | state->line_pos = (line_len >= 0) ? 0 : -1; |
253 | return line_len; |
254 | } |
255 | |
256 | int run_cmd(const char *fmt, ...) |
257 | { |
258 | va_list ap; |
259 | char *cmd; |
260 | int ret; |
261 | va_start(ap, fmt); |
262 | cmd = vsprintf_dup(fmt, ap); |
263 | va_end(ap); |
264 | |
265 | if (opt_dry_run || opt_verbose) |
266 | fprintf(stderr, "run_cmd: %s\n" , cmd); |
267 | |
268 | ret = opt_dry_run ? 0 : system(cmd); |
269 | |
270 | free_string(cmd); |
271 | |
272 | return ret; |
273 | } |
274 | |
275 | |
276 | /* Not thread safe */ |
277 | void free_string(const char *string) |
278 | { |
279 | STRING_T *str; |
280 | |
281 | if (!string) |
282 | return; |
283 | |
284 | str = (STRING_T *)(string - sizeof(STRING_T)); |
285 | if (str == allocated_strings) |
286 | { |
287 | allocated_strings = str->next; |
288 | if (allocated_strings == str) |
289 | allocated_strings = NULL; |
290 | } |
291 | str->prev->next = str->next; |
292 | str->next->prev = str->prev; |
293 | free(str); |
294 | } |
295 | |
296 | /* Not thread safe */ |
297 | void free_strings(void) |
298 | { |
299 | if (allocated_strings) |
300 | { |
301 | STRING_T *str = allocated_strings; |
302 | do |
303 | { |
304 | STRING_T *t = str; |
305 | str = t->next; |
306 | free(t); |
307 | } while (str != allocated_strings); |
308 | allocated_strings = NULL; |
309 | } |
310 | } |
311 | |
312 | /* Not thread safe */ |
313 | char *sprintf_dup(const char *fmt, ...) |
314 | { |
315 | va_list ap; |
316 | char *str; |
317 | va_start(ap, fmt); |
318 | str = vsprintf_dup(fmt, ap); |
319 | va_end(ap); |
320 | return str; |
321 | } |
322 | |
323 | /* Not thread safe */ |
324 | char *vsprintf_dup(const char *fmt, va_list ap) |
325 | { |
326 | char scratch[512]; |
327 | int len; |
328 | STRING_T *str; |
329 | len = vsnprintf(scratch, sizeof(scratch), fmt, ap) + 1; |
330 | |
331 | if (len > sizeof(scratch)) |
332 | fatal_error("Maximum string length exceeded" ); |
333 | |
334 | str = malloc(sizeof(STRING_T) + len); |
335 | if (!str) |
336 | fatal_error("Out of memory" ); |
337 | |
338 | memcpy(str->data, scratch, len); |
339 | if (allocated_strings) |
340 | { |
341 | str->next = allocated_strings; |
342 | str->prev = allocated_strings->prev; |
343 | str->next->prev = str; |
344 | str->prev->next = str; |
345 | } |
346 | else |
347 | { |
348 | str->next = str; |
349 | str->prev = str; |
350 | allocated_strings = str; |
351 | } |
352 | |
353 | return str->data; |
354 | } |
355 | |
356 | int dir_exists(const char *dirname) |
357 | { |
358 | struct stat finfo; |
359 | return (stat(dirname, &finfo) == 0) && S_ISDIR(finfo.st_mode); |
360 | } |
361 | |
362 | int file_exists(const char *dirname) |
363 | { |
364 | struct stat finfo; |
365 | return (stat(dirname, &finfo) == 0) && S_ISREG(finfo.st_mode); |
366 | } |
367 | |
368 | void string_vec_init(STRING_VEC_T *vec) |
369 | { |
370 | vec->num_strings = 0; |
371 | vec->max_strings = 0; |
372 | vec->strings = NULL; |
373 | } |
374 | |
375 | char *string_vec_add(STRING_VEC_T *vec, const char *str, int len) |
376 | { |
377 | char *copy; |
378 | if (vec->num_strings == vec->max_strings) |
379 | { |
380 | if (vec->max_strings) |
381 | vec->max_strings *= 2; |
382 | else |
383 | vec->max_strings = 16; |
384 | vec->strings = realloc(vec->strings, vec->max_strings * sizeof(const char *)); |
385 | if (!vec->strings) |
386 | fatal_error("Out of memory" ); |
387 | } |
388 | |
389 | if (len) |
390 | { |
391 | copy = malloc(len + 1); |
392 | strncpy(copy, str, len); |
393 | copy[len] = '\0'; |
394 | } |
395 | else |
396 | copy = strdup(str); |
397 | |
398 | if (!copy) |
399 | fatal_error("Out of memory" ); |
400 | |
401 | vec->strings[vec->num_strings++] = copy; |
402 | |
403 | return copy; |
404 | } |
405 | |
406 | int string_vec_find(STRING_VEC_T *vec, const char *str, int len) |
407 | { |
408 | int i; |
409 | |
410 | for (i = 0; i < vec->num_strings; i++) |
411 | { |
412 | if (len) |
413 | { |
414 | if ((strncmp(vec->strings[i], str, len) == 0) && |
415 | (vec->strings[i][len] == '\0')) |
416 | return i; |
417 | } |
418 | else if (strcmp(vec->strings[i], str) == 0) |
419 | return i; |
420 | } |
421 | |
422 | return -1; |
423 | } |
424 | |
425 | int string_vec_compare(const void *a, const void *b) |
426 | { |
427 | return strcmp(*(const char **)a, *(const char **)b); |
428 | } |
429 | |
430 | void string_vec_sort(STRING_VEC_T *vec) |
431 | { |
432 | qsort(vec->strings, vec->num_strings, sizeof(char *), &string_vec_compare); |
433 | } |
434 | |
435 | void string_vec_uninit(STRING_VEC_T *vec) |
436 | { |
437 | int i; |
438 | for (i = 0; i < vec->num_strings; i++) |
439 | free(vec->strings[i]); |
440 | free(vec->strings); |
441 | } |
442 | |
443 | int error(const char *fmt, ...) |
444 | { |
445 | va_list ap; |
446 | fprintf(stderr, "* " ); |
447 | va_start(ap, fmt); |
448 | vfprintf(stderr, fmt, ap); |
449 | va_end(ap); |
450 | fprintf(stderr, "\n" ); |
451 | return 1; |
452 | } |
453 | |
454 | void fatal_error(const char *fmt, ...) |
455 | { |
456 | va_list ap; |
457 | fprintf(stderr, "* " ); |
458 | va_start(ap, fmt); |
459 | vfprintf(stderr, fmt, ap); |
460 | va_end(ap); |
461 | fprintf(stderr, "\n" ); |
462 | exit(1); |
463 | } |
464 | |