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