1/*
2Copyright (c) 2016 Raspberry Pi (Trading) Ltd.
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, 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
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON 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
25SOFTWARE, 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
40int opt_verbose;
41int opt_dry_run;
42static STRING_T *allocated_strings;
43
44struct 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
55static int overlay_help_get_line(OVERLAY_HELP_STATE_T *state);
56
57OVERLAY_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
74void overlay_help_close(OVERLAY_HELP_STATE_T *state)
75{
76 fclose(state->fp);
77 free(state);
78}
79
80int 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
101int 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
138const 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
172void 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 */
209static 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
216get_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
256int 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 */
277void 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 */
297void 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 */
313char *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 */
324char *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
356int dir_exists(const char *dirname)
357{
358 struct stat finfo;
359 return (stat(dirname, &finfo) == 0) && S_ISDIR(finfo.st_mode);
360}
361
362int file_exists(const char *dirname)
363{
364 struct stat finfo;
365 return (stat(dirname, &finfo) == 0) && S_ISREG(finfo.st_mode);
366}
367
368void string_vec_init(STRING_VEC_T *vec)
369{
370 vec->num_strings = 0;
371 vec->max_strings = 0;
372 vec->strings = NULL;
373}
374
375char *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
406int 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
425int string_vec_compare(const void *a, const void *b)
426{
427 return strcmp(*(const char **)a, *(const char **)b);
428}
429
430void string_vec_sort(STRING_VEC_T *vec)
431{
432 qsort(vec->strings, vec->num_strings, sizeof(char *), &string_vec_compare);
433}
434
435void 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
443int 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
454void 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