1 /*
2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6
7 #include "prof_int.h"
8
9 #include <stdio.h>
10 #include <string.h>
11 #ifdef HAVE_STDLIB_H
12 #include <stdlib.h>
13 #endif
14 #include <errno.h>
15 #include <ctype.h>
16
17 #define SECTION_SEP_CHAR '/'
18
19 #define STATE_INIT_COMMENT 1
20 #define STATE_STD_LINE 2
21 #define STATE_GET_OBRACE 3
22
23 struct parse_state {
24 int state;
25 int group_level;
26 struct profile_node *root_section;
27 struct profile_node *current_section;
28 };
29
skip_over_blanks(char * cp)30 static char *skip_over_blanks(char *cp)
31 {
32 while (*cp && isspace((int) (*cp)))
33 cp++;
34 return cp;
35 }
36
strip_line(char * line)37 static void strip_line(char *line)
38 {
39 char *p = line + strlen(line);
40 while (p > line && (p[-1] == '\n' || p[-1] == '\r'))
41 *p-- = 0;
42 }
43
parse_quoted_string(char * str)44 static void parse_quoted_string(char *str)
45 {
46 char *to, *from;
47
48 to = from = str;
49
50 for (to = from = str; *from && *from != '"'; to++, from++) {
51 if (*from == '\\') {
52 from++;
53 switch (*from) {
54 case 'n':
55 *to = '\n';
56 break;
57 case 't':
58 *to = '\t';
59 break;
60 case 'b':
61 *to = '\b';
62 break;
63 default:
64 *to = *from;
65 }
66 continue;
67 }
68 *to = *from;
69 }
70 *to = '\0';
71 }
72
73
parse_init_state(struct parse_state * state)74 static errcode_t parse_init_state(struct parse_state *state)
75 {
76 state->state = STATE_INIT_COMMENT;
77 state->group_level = 0;
78
79 return profile_create_node("(root)", 0, &state->root_section);
80 }
81
parse_std_line(char * line,struct parse_state * state)82 static errcode_t parse_std_line(char *line, struct parse_state *state)
83 {
84 char *cp, ch, *tag, *value;
85 char *p;
86 errcode_t retval;
87 struct profile_node *node;
88 int do_subsection = 0;
89 void *iter = 0;
90
91 if (*line == 0)
92 return 0;
93 cp = skip_over_blanks(line);
94 if (cp[0] == ';' || cp[0] == '#')
95 return 0;
96 strip_line(cp);
97 ch = *cp;
98 if (ch == 0)
99 return 0;
100 if (ch == '[') {
101 if (state->group_level > 0)
102 return PROF_SECTION_NOTOP;
103 cp++;
104 p = strchr(cp, ']');
105 if (p == NULL)
106 return PROF_SECTION_SYNTAX;
107 *p = '\0';
108 retval = profile_find_node_subsection(state->root_section,
109 cp, &iter, 0,
110 &state->current_section);
111 if (retval == PROF_NO_SECTION) {
112 retval = profile_add_node(state->root_section,
113 cp, 0,
114 &state->current_section);
115 if (retval)
116 return retval;
117 } else if (retval)
118 return retval;
119
120 /*
121 * Finish off the rest of the line.
122 */
123 cp = p+1;
124 if (*cp == '*') {
125 profile_make_node_final(state->current_section);
126 cp++;
127 }
128 /*
129 * A space after ']' should not be fatal
130 */
131 cp = skip_over_blanks(cp);
132 if (*cp)
133 return PROF_SECTION_SYNTAX;
134 return 0;
135 }
136 if (ch == '}') {
137 if (state->group_level == 0)
138 return PROF_EXTRA_CBRACE;
139 if (*(cp+1) == '*')
140 profile_make_node_final(state->current_section);
141 retval = profile_get_node_parent(state->current_section,
142 &state->current_section);
143 if (retval)
144 return retval;
145 state->group_level--;
146 return 0;
147 }
148 /*
149 * Parse the relations
150 */
151 tag = cp;
152 cp = strchr(cp, '=');
153 if (!cp)
154 return PROF_RELATION_SYNTAX;
155 if (cp == tag)
156 return PROF_RELATION_SYNTAX;
157 *cp = '\0';
158 p = tag;
159 /* Look for whitespace on left-hand side. */
160 while (p < cp && !isspace((int)*p))
161 p++;
162 if (p < cp) {
163 /* Found some sort of whitespace. */
164 *p++ = 0;
165 /* If we have more non-whitespace, it's an error. */
166 while (p < cp) {
167 if (!isspace((int)*p))
168 return PROF_RELATION_SYNTAX;
169 p++;
170 }
171 }
172 cp = skip_over_blanks(cp+1);
173 value = cp;
174 if (value[0] == '"') {
175 value++;
176 parse_quoted_string(value);
177 } else if (value[0] == 0) {
178 do_subsection++;
179 state->state = STATE_GET_OBRACE;
180 } else if (value[0] == '{' && *(skip_over_blanks(value+1)) == 0)
181 do_subsection++;
182 else {
183 cp = value + strlen(value) - 1;
184 while ((cp > value) && isspace((int) (*cp)))
185 *cp-- = 0;
186 }
187 if (do_subsection) {
188 p = strchr(tag, '*');
189 if (p)
190 *p = '\0';
191 retval = profile_add_node(state->current_section,
192 tag, 0, &state->current_section);
193 if (retval)
194 return retval;
195 if (p)
196 profile_make_node_final(state->current_section);
197 state->group_level++;
198 return 0;
199 }
200 p = strchr(tag, '*');
201 if (p)
202 *p = '\0';
203 profile_add_node(state->current_section, tag, value, &node);
204 if (p)
205 profile_make_node_final(node);
206 return 0;
207 }
208
parse_line(char * line,struct parse_state * state)209 static errcode_t parse_line(char *line, struct parse_state *state)
210 {
211 char *cp;
212
213 switch (state->state) {
214 case STATE_INIT_COMMENT:
215 if (line[0] != '[')
216 return 0;
217 state->state = STATE_STD_LINE;
218 /*FALLTHRU*/
219 case STATE_STD_LINE:
220 return parse_std_line(line, state);
221 case STATE_GET_OBRACE:
222 cp = skip_over_blanks(line);
223 if (*cp != '{')
224 return PROF_MISSING_OBRACE;
225 state->state = STATE_STD_LINE;
226 /*FALLTHRU*/
227 }
228 return 0;
229 }
230
profile_parse_file(FILE * f,struct profile_node ** root)231 errcode_t profile_parse_file(FILE *f, struct profile_node **root)
232 {
233 #define BUF_SIZE 2048
234 char *bptr;
235 errcode_t retval;
236 struct parse_state state;
237
238 bptr = malloc (BUF_SIZE);
239 if (!bptr)
240 return ENOMEM;
241
242 retval = parse_init_state(&state);
243 if (retval) {
244 free (bptr);
245 return retval;
246 }
247 while (!feof(f)) {
248 if (fgets(bptr, BUF_SIZE, f) == NULL)
249 break;
250 #ifndef PROFILE_SUPPORTS_FOREIGN_NEWLINES
251 retval = parse_line(bptr, &state);
252 if (retval) {
253 /* Solaris Kerberos: check if an unconfigured file */
254 if (strstr(bptr, "___"))
255 retval = PROF_NO_PROFILE;
256 free (bptr);
257 return retval;
258 }
259 #else
260 {
261 char *p, *end;
262
263 if (strlen(bptr) >= BUF_SIZE - 1) {
264 /* The string may have foreign newlines and
265 gotten chopped off on a non-newline
266 boundary. Seek backwards to the last known
267 newline. */
268 long offset;
269 char *c = bptr + strlen (bptr);
270 for (offset = 0; offset > -BUF_SIZE; offset--) {
271 if (*c == '\r' || *c == '\n') {
272 *c = '\0';
273 fseek (f, offset, SEEK_CUR);
274 break;
275 }
276 c--;
277 }
278 }
279
280 /* First change all newlines to \n */
281 for (p = bptr; *p != '\0'; p++) {
282 if (*p == '\r')
283 *p = '\n';
284 }
285 /* Then parse all lines */
286 p = bptr;
287 end = bptr + strlen (bptr);
288 while (p < end) {
289 char* newline;
290 char* newp;
291
292 newline = strchr (p, '\n');
293 if (newline != NULL)
294 *newline = '\0';
295
296 /* parse_line modifies contents of p */
297 newp = p + strlen (p) + 1;
298 retval = parse_line (p, &state);
299 if (retval) {
300 free (bptr);
301 return retval;
302 }
303
304 p = newp;
305 }
306 }
307 #endif
308 }
309 *root = state.root_section;
310
311 free (bptr);
312 return 0;
313 }
314
315 /*
316 * Return TRUE if the string begins or ends with whitespace
317 */
need_double_quotes(char * str)318 static int need_double_quotes(char *str)
319 {
320 if (!str)
321 return 0;
322 if (str[0] == '\0')
323 return 1;
324 if (isspace((int) (*str)) ||isspace((int) (*(str + strlen(str) - 1))))
325 return 1;
326 if (strchr(str, '\n') || strchr(str, '\t') || strchr(str, '\b'))
327 return 1;
328 return 0;
329 }
330
331 /*
332 * Output a string with double quotes, doing appropriate backquoting
333 * of characters as necessary.
334 */
output_quoted_string(char * str,void (* cb)(const char *,void *),void * data)335 static void output_quoted_string(char *str, void (*cb)(const char *,void *),
336 void *data)
337 {
338 char ch;
339 char buf[2];
340
341 cb("\"", data);
342 if (!str) {
343 cb("\"", data);
344 return;
345 }
346 buf[1] = 0;
347 while ((ch = *str++)) {
348 switch (ch) {
349 case '\\':
350 cb("\\\\", data);
351 break;
352 case '\n':
353 cb("\\n", data);
354 break;
355 case '\t':
356 cb("\\t", data);
357 break;
358 case '\b':
359 cb("\\b", data);
360 break;
361 default:
362 /* This would be a lot faster if we scanned
363 forward for the next "interesting"
364 character. */
365 buf[0] = ch;
366 cb(buf, data);
367 break;
368 }
369 }
370 cb("\"", data);
371 }
372
373
374
375 #if defined(_WIN32)
376 #define EOL "\r\n"
377 #endif
378
379 #ifndef EOL
380 #define EOL "\n"
381 #endif
382
383 /* Errors should be returned, not ignored! */
dump_profile(struct profile_node * root,int level,void (* cb)(const char *,void *),void * data)384 static void dump_profile(struct profile_node *root, int level,
385 void (*cb)(const char *, void *), void *data)
386 {
387 int i;
388 struct profile_node *p;
389 void *iter;
390 long retval;
391 char *name, *value;
392
393 iter = 0;
394 do {
395 retval = profile_find_node_relation(root, 0, &iter,
396 &name, &value);
397 if (retval)
398 break;
399 for (i=0; i < level; i++)
400 cb("\t", data);
401 if (need_double_quotes(value)) {
402 cb(name, data);
403 cb(" = ", data);
404 output_quoted_string(value, cb, data);
405 cb(EOL, data);
406 } else {
407 cb(name, data);
408 cb(" = ", data);
409 cb(value, data);
410 cb(EOL, data);
411 }
412 } while (iter != 0);
413
414 iter = 0;
415 do {
416 retval = profile_find_node_subsection(root, 0, &iter,
417 &name, &p);
418 if (retval)
419 break;
420 if (level == 0) { /* [xxx] */
421 cb("[", data);
422 cb(name, data);
423 cb("]", data);
424 cb(profile_is_node_final(p) ? "*" : "", data);
425 cb(EOL, data);
426 dump_profile(p, level+1, cb, data);
427 cb(EOL, data);
428 } else { /* xxx = { ... } */
429 for (i=0; i < level; i++)
430 cb("\t", data);
431 cb(name, data);
432 cb(" = {", data);
433 cb(EOL, data);
434 dump_profile(p, level+1, cb, data);
435 for (i=0; i < level; i++)
436 cb("\t", data);
437 cb("}", data);
438 cb(profile_is_node_final(p) ? "*" : "", data);
439 cb(EOL, data);
440 }
441 } while (iter != 0);
442 }
443
dump_profile_to_file_cb(const char * str,void * data)444 static void dump_profile_to_file_cb(const char *str, void *data)
445 {
446 fputs(str, data);
447 }
448
profile_write_tree_file(struct profile_node * root,FILE * dstfile)449 errcode_t profile_write_tree_file(struct profile_node *root, FILE *dstfile)
450 {
451 dump_profile(root, 0, dump_profile_to_file_cb, dstfile);
452 return 0;
453 }
454
455 struct prof_buf {
456 char *base;
457 size_t cur, max;
458 int err;
459 };
460
add_data_to_buffer(struct prof_buf * b,const void * d,size_t len)461 static void add_data_to_buffer(struct prof_buf *b, const void *d, size_t len)
462 {
463 if (b->err)
464 return;
465 if (b->max - b->cur < len) {
466 size_t newsize;
467 char *newptr;
468
469 newsize = b->max + (b->max >> 1) + len + 1024;
470 newptr = realloc(b->base, newsize);
471 if (newptr == NULL) {
472 b->err = 1;
473 return;
474 }
475 b->base = newptr;
476 b->max = newsize;
477 }
478 memcpy(b->base + b->cur, d, len);
479 b->cur += len; /* ignore overflow */
480 }
481
dump_profile_to_buffer_cb(const char * str,void * data)482 static void dump_profile_to_buffer_cb(const char *str, void *data)
483 {
484 add_data_to_buffer((struct prof_buf *)data, str, strlen(str));
485 }
486
profile_write_tree_to_buffer(struct profile_node * root,char ** buf)487 errcode_t profile_write_tree_to_buffer(struct profile_node *root,
488 char **buf)
489 {
490 struct prof_buf prof_buf = { 0, 0, 0, 0 };
491
492 dump_profile(root, 0, dump_profile_to_buffer_cb, &prof_buf);
493 if (prof_buf.err) {
494 *buf = NULL;
495 return ENOMEM;
496 }
497 add_data_to_buffer(&prof_buf, "", 1); /* append nul */
498 if (prof_buf.max - prof_buf.cur > (prof_buf.max >> 3)) {
499 char *newptr = realloc(prof_buf.base, prof_buf.cur);
500 if (newptr)
501 prof_buf.base = newptr;
502 }
503 *buf = prof_buf.base;
504 return 0;
505 }
506