1 /* 2 * Copyright 2002-2003 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 <stdio.h> 9 #include <string.h> 10 #ifdef HAVE_STDLIB_H 11 #include <stdlib.h> 12 #endif 13 #include <errno.h> 14 #include <ctype.h> 15 16 #include "prof_int.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(cp) 32 char *cp; 33 { 34 while (*cp && isspace(*cp)) 35 cp++; 36 return cp; 37 } 38 39 static void strip_line(line) 40 char *line; 41 { 42 char *p; 43 44 while (*line) { 45 p = line + strlen(line) - 1; 46 if ((*p == '\n') || (*p == '\r')) 47 *p = 0; 48 else 49 break; 50 } 51 } 52 53 static void parse_quoted_string(char *str) 54 { 55 char *to, *from; 56 57 to = from = str; 58 59 for (to = from = str; *from && *from != '"'; to++, from++) { 60 if (*from == '\\') { 61 from++; 62 switch (*from) { 63 case 'n': 64 *to = '\n'; 65 break; 66 case 't': 67 *to = '\t'; 68 break; 69 case 'b': 70 *to = '\b'; 71 break; 72 default: 73 *to = *from; 74 } 75 continue; 76 } 77 *to = *from; 78 } 79 *to = '\0'; 80 } 81 82 83 static errcode_t parse_init_state(state) 84 struct parse_state *state; 85 { 86 state->state = STATE_INIT_COMMENT; 87 state->group_level = 0; 88 89 return profile_create_node("(root)", 0, &state->root_section); 90 } 91 92 static errcode_t parse_std_line(line, state) 93 char *line; 94 struct parse_state *state; 95 { 96 char *cp, ch, *tag, *value; 97 char *p; 98 errcode_t retval; 99 struct profile_node *node; 100 int do_subsection = 0; 101 void *iter = 0; 102 103 if (*line == 0) 104 return 0; 105 if (line[0] == ';' || line[0] == '#') 106 return 0; 107 strip_line(line); 108 cp = skip_over_blanks(line); 109 ch = *cp; 110 if (ch == 0) 111 return 0; 112 if (ch == '[') { 113 if (state->group_level > 0) 114 return PROF_SECTION_NOTOP; 115 cp++; 116 p = strchr(cp, ']'); 117 if (p == NULL) 118 return PROF_SECTION_SYNTAX; 119 *p = '\0'; 120 retval = profile_find_node_subsection(state->root_section, 121 cp, &iter, 0, 122 &state->current_section); 123 if (retval == PROF_NO_SECTION) { 124 retval = profile_add_node(state->root_section, 125 cp, 0, 126 &state->current_section); 127 if (retval) 128 return retval; 129 } else if (retval) 130 return retval; 131 132 /* 133 * Finish off the rest of the line. 134 */ 135 cp = p+1; 136 137 if (*cp == '*') { 138 profile_make_node_final(state->current_section); 139 cp++; 140 } 141 142 /* 143 * A space after ']' should not be fatal 144 */ 145 cp = skip_over_blanks(cp); 146 if (*cp) 147 return PROF_SECTION_SYNTAX; 148 return 0; 149 } 150 if (ch == '}') { 151 if (state->group_level == 0) 152 return PROF_EXTRA_CBRACE; 153 if (*(cp+1) == '*') 154 profile_make_node_final(state->current_section); 155 retval = profile_get_node_parent(state->current_section, 156 &state->current_section); 157 if (retval) 158 return retval; 159 state->group_level--; 160 return 0; 161 } 162 /* 163 * Parse the relations 164 */ 165 tag = cp; 166 cp = strchr(cp, '='); 167 if (!cp) 168 return PROF_RELATION_SYNTAX; 169 *cp = '\0'; 170 p = strchr(tag, ' '); 171 if (p) { 172 *p = '\0'; 173 p = skip_over_blanks(p+1); 174 if (p != cp) 175 return PROF_RELATION_SYNTAX; 176 } 177 cp = skip_over_blanks(cp+1); 178 value = cp; 179 if (value[0] == '"') { 180 value++; 181 parse_quoted_string(value); 182 } else if (value[0] == 0) { 183 do_subsection++; 184 state->state = STATE_GET_OBRACE; 185 } else if (value[0] == '{' && value[1] == 0) 186 do_subsection++; 187 else { 188 /* 189 * Skip over trailing whitespace characters 190 */ 191 cp = value + strlen(value) - 1; 192 while ((cp > value) && isspace(*cp)) 193 *cp-- = 0; 194 } 195 196 if (do_subsection) { 197 p = strchr(tag, '*'); 198 if (p) 199 *p = '\0'; 200 retval = profile_add_node(state->current_section, 201 tag, 0, &state->current_section); 202 if (retval) 203 return retval; 204 if (p) 205 profile_make_node_final(state->current_section); 206 state->group_level++; 207 return 0; 208 } 209 p = strchr(tag, '*'); 210 if (p) 211 *p = '\0'; 212 profile_add_node(state->current_section, tag, value, &node); 213 if (p) 214 profile_make_node_final(node); 215 return 0; 216 } 217 218 static errcode_t parse_line(line, state) 219 char *line; 220 struct parse_state *state; 221 { 222 char *cp; 223 224 switch (state->state) { 225 case STATE_INIT_COMMENT: 226 if (line[0] != '[') 227 return 0; 228 state->state = STATE_STD_LINE; 229 /*FALLTHRU*/ 230 case STATE_STD_LINE: 231 return parse_std_line(line, state); 232 case STATE_GET_OBRACE: 233 cp = skip_over_blanks(line); 234 if (*cp != '{') 235 return PROF_MISSING_OBRACE; 236 state->state = STATE_STD_LINE; 237 /*FALLTHRU*/ 238 } 239 return 0; 240 } 241 242 errcode_t profile_parse_file(f, root) 243 FILE *f; 244 struct profile_node **root; 245 { 246 #define BUF_SIZE 2048 247 char *bptr; 248 errcode_t retval; 249 struct parse_state state; 250 251 bptr = (char *) malloc (BUF_SIZE); 252 if (!bptr) 253 return ENOMEM; 254 255 retval = parse_init_state(&state); 256 if (retval) { 257 free (bptr); 258 return retval; 259 } 260 while (!feof(f)) { 261 if (fgets(bptr, BUF_SIZE, f) == NULL) 262 break; 263 retval = parse_line(bptr, &state); 264 if (retval) { 265 /* check if an unconfigured file */ 266 if (strstr(bptr, "___")) 267 retval = PROF_NO_PROFILE; 268 free (bptr); 269 return retval; 270 } 271 } 272 *root = state.root_section; 273 274 free (bptr); 275 return 0; 276 } 277 278 /* 279 * Return TRUE if the string begins or ends with whitespace 280 */ 281 static int need_double_quotes(str) 282 char *str; 283 { 284 if (!str || !*str) 285 return 0; 286 if (isspace(*str) ||isspace(*(str + strlen(str) - 1))) 287 return 1; 288 if (strchr(str, '\n') || strchr(str, '\t') || strchr(str, '\b')) 289 return 1; 290 return 0; 291 } 292 293 /* 294 * Output a string with double quotes, doing appropriate backquoting 295 * of characters as necessary. 296 */ 297 static void output_quoted_string(str, f) 298 char *str; 299 FILE *f; 300 { 301 char ch; 302 303 fputc('"', f); 304 if (!str) { 305 fputc('"', f); 306 return; 307 } 308 while ((ch = *str++)) { 309 switch (ch) { 310 case '\\': 311 fputs("\\\\", f); 312 break; 313 case '\n': 314 fputs("\\n", f); 315 break; 316 case '\t': 317 fputs("\\t", f); 318 break; 319 case '\b': 320 fputs("\\b", f); 321 break; 322 default: 323 fputc(ch, f); 324 break; 325 } 326 } 327 fputc('"', f); 328 } 329 330 331 332 #if defined(_MSDOS) || defined(_WIN32) 333 #define EOL "\r\n" 334 #endif 335 336 #ifdef macintosh 337 #define EOL "\r" 338 #endif 339 340 #ifndef EOL 341 #define EOL "\n" 342 #endif 343 344 static void dump_profile_to_file(root, level, dstfile) 345 struct profile_node *root; 346 int level; 347 FILE *dstfile; 348 { 349 int i; 350 struct profile_node *p; 351 void *iter; 352 long retval; 353 char *name, *value; 354 355 iter = 0; 356 do { 357 retval = profile_find_node_relation(root, 0, &iter, 358 &name, &value); 359 if (retval) 360 break; 361 for (i=0; i < level; i++) 362 fprintf(dstfile, "\t"); 363 if (need_double_quotes(value)) { 364 fputs(name, dstfile); 365 fputs(" = ", dstfile); 366 output_quoted_string(value, dstfile); 367 fputs(EOL, dstfile); 368 } else 369 fprintf(dstfile, "%s = %s%s", name, value, EOL); 370 } while (iter != 0); 371 372 iter = 0; 373 do { 374 retval = profile_find_node_subsection(root, 0, &iter, 375 &name, &p); 376 if (retval) 377 break; 378 if (level == 0) { /* [xxx] */ 379 for (i=0; i < level; i++) 380 fprintf(dstfile, "\t"); 381 fprintf(dstfile, "[%s]%s%s", name, 382 profile_is_node_final(p) ? "*" : "", EOL); 383 dump_profile_to_file(p, level+1, dstfile); 384 fprintf(dstfile, EOL); 385 } else { /* xxx = { ... } */ 386 for (i=0; i < level; i++) 387 fprintf(dstfile, "\t"); 388 fprintf(dstfile, "%s = {%s", name, EOL); 389 dump_profile_to_file(p, level+1, dstfile); 390 for (i=0; i < level; i++) 391 fprintf(dstfile, "\t"); 392 fprintf(dstfile, "}%s%s", 393 profile_is_node_final(p) ? "*" : "", EOL); 394 } 395 } while (iter != 0); 396 } 397 398 errcode_t profile_write_tree_file(root, dstfile) 399 struct profile_node *root; 400 FILE *dstfile; 401 { 402 dump_profile_to_file(root, 0, dstfile); 403 return 0; 404 } 405