#include "common.h" #include "configuration.h" #include "strutl.h" #include #include #define DELIMITER "::" /* private functions */ /** * @brief helper function for lookups * @param struct configitem *head - where to start looking * @param const char *tag - tag to look for * @param unsigned long len - length of tag * @param int create - create a new node if it doesn't exist? * @return struct configitem * - node of the item being sought, NULL * if not found */ static struct configitem *config_lookuphlp(struct configitem *head, const char *tag, unsigned long len, int create) { int res = 1; struct configitem *i = head->child; struct configitem **last = &head->child; struct configitem *newitem; /* empty strings match nothing; they are used for lists */ if (len != 0) { for (; i != 0; last = &i->next, i = i->next) if ((res = strcountcmp(i->tag, i->tag+strlen(i->tag), tag, tag + len)) == 0) break; } else for (; i != 0; last = &i->next, i = i->next); if (res == 0) return i; if (create == 0) return NULL; newitem = NEW(struct configitem); memset(newitem, 0, sizeof(struct configitem)); newitem->tag = malloc(len+1); newitem->tag[len] = 0; memcpy(newitem->tag, tag, len); newitem->value = 0; newitem->next = *last; newitem->parent = head; newitem->child = 0; *last = newitem; return newitem; } /* * @brief lookup a given configuration item * @param struct configuration *config - config object to look inside * @param const char *tag - tag of item to look for * @param int create - create if doesn't exist? * @return struct configitem * - item being sough, NULL if doesn't exist */ static struct configitem *config_lookup(struct configuration *config, const char *tag, int create) { const char *start, *end, *tagend; struct configitem *item = config->root; if (tag == 0) return config->root->child; start = tag; end = start + strlen(tag); tagend = tag; for (; end - tagend >= 2; tagend++) { if (tagend[0] == ':' && tagend[1] == ':') { item = config_lookuphlp(item, start, tagend-start, create); if (item == 0) return NULL; tagend = start = tagend + 2; } } /* trailing :: */ if (end - start == 0) if (create == 0) return NULL; item = config_lookuphlp(item, start, end-start, create); return item; } /** * @brief recursive builds the complete tag name, seperated by :: * @param const struct configitem *top - head of config tree * @param const struct configitem *stop - when to stop * @param char *tag - buffer to build the tag * @param const size_t maxlen - length of buffer * @return none */ void config_fulltag(const struct configitem *top, const struct configitem *stop, char *tag, const size_t maxlen) { char buf[maxlen]; buf[0] = 0; if (top->parent == 0 || top->parent->parent == 0 || top->parent == stop) { strncpy(tag, top->tag, maxlen); return; } config_fulltag(top->parent, stop, buf, sizeof(buf)); strvacat(tag, maxlen, buf, DELIMITER, top->tag, (char *) 0); } /* public functions */ static const char *config_get(struct configuration *cfg, const char *tag, const char *defaultvalue) { struct configitem *item = config_lookup(cfg, tag, 0); if (item == 0) return defaultvalue; else return item->value; } static int config_geti(struct configuration *cfg, const char *tag, int defaultvalue) { int res; char *end; struct configitem *item = config_lookup(cfg, tag, 0); if (item == 0) return defaultvalue; res = strtol(item->value, &end, 0); if (end == item->value) return defaultvalue; else return res; } static void config_set(struct configuration *cfg, const char *tag, const char *value) { struct configitem *item = config_lookup(cfg, tag, 1); if (item == 0) return; DELETE(item->value); item->value = STRDUP(value); } static void config_seti(struct configuration *cfg, const char *tag, int value) { char s[50]; snprintf(s, sizeof(s), "%i", value); config_set(cfg, tag, s); } static int config_exists(struct configuration *cfg, const char *tag) { struct configitem *item = config_lookup(cfg, tag, 0); return (item != 0); } static int config_read(struct configuration *cfg, const char *filename) { FILE *infp; char parenttag[256]; char tag[256], value[4096], item[4096]; char buffer[4096]; char *buf; char linebuf[8192]; char *stack[100]; char *s, *q, *start, *stop; char termchar; unsigned int stackpos = 0; int curline = 0, i; int incomment = 0, inquote = 0; int ret = DC_OK; linebuf[0] = 0; parenttag[0] = 0; for (i = 0; i < DIM(stack); i++) stack[i] = NULL; if ((infp = fopen(filename, "r")) == NULL) return DC_NOTOK; while (fgets(buffer, sizeof(buffer), infp)) { curline++; buf = strstrip(buffer); /* multiline comments -- check for end-of-comment */ if (incomment != 0) { for (s = buf; *s != 0; s++) { if (s[0] == '*' && s[1] == '/') { memmove(buf, s+2, strlen(s+2)+1); incomment = 0; break; } } if (incomment != 0) continue; } /* discard single line comments */ inquote = 0; for (s = buf; *s != 0; s++) { if (*s == '\\') { s++; if (*s == 0) break; continue; } if (*s == '"') inquote = !inquote; if (inquote != 0) continue; if (s[0] == '/' && s[1] == '/') { *s = 0; break; } } /* look for multiline comments */ inquote = 0; for (s = buf; *s != 0; s++) { if (*s == '\\') { s++; if (*s == 0) break; continue; } if (*s == '"') inquote = !inquote; if (inquote != 0) continue; if (s[0] == '/' && s[1] == '*') { incomment = 1; for (q = buf; *q != 0; q++) { if (q[0] == '*' && q[1] == '/') { memmove(s, q+2, strlen(q+2)+1); incomment = 0; break; } } if (incomment != 0) { *s = 0; break; } } } /* skip blank lines */ if (buf[0] == 0) continue; /* valid line */ buf = strstrip(buf); inquote = 0; for (s = buf; *s != 0;) { if (*s == '\\') { if (*(s+1) == 0) break; s += 2; continue; } if (*s == '"') inquote = !inquote; if (inquote == 0 && (*s == '{' || *s == ';' || *s == '}')) { /* add the last fragment to the buffer */ start = buf; stop = s; for (; start != s && isspace(*start) != 0; start++); for (; stop != start && isspace(stop[-1]) != 0; stop--); if (linebuf[0] != 0 && stop - start != 0) strcat(linebuf, " "); strncat(linebuf, start, stop-start); /* remove the fragment */ termchar = *s; memmove(buf, s+1, strlen(s+1)+1); s = buf; /* syntax error */ if (termchar == '{' && linebuf[0] == 0) { INFO(INFO_ERROR, "Syntax error %s:%u: block starts with no name", filename, curline); ret = DC_NOTOK; goto out; } if (linebuf[0] == 0) { if (termchar == '}') { if (stackpos == 0) parenttag[0] = 0; else snprintf(parenttag, sizeof(parenttag), "%s", stack[--stackpos]); } continue; } /* parse off the tag */ q = (char *)linebuf; tag[0] = 0; if (strparsecword(&q, tag, sizeof(tag)) == 0 && strparsequoteword(&q, tag, sizeof(tag)) == 0) { INFO(INFO_ERROR, "Syntax error %s:%u: Malformed tag", filename, curline); ret = DC_NOTOK; goto out; } /* parse off the value */ value[0] = 0; if (strparsecword(&q, value, sizeof(value)) == 0 && strparsequoteword(&q, value, sizeof(value)) == 0) { if (termchar != '{') { strncpy(value, tag, sizeof(value)); tag[0] = 0; } } /* extra junk */ if (strlen(q) != 0) { INFO(INFO_ERROR, "Syntax error %s:%u: Extra junk after tag", filename, curline); ret = DC_NOTOK; goto out; } if (termchar == '{') { /* 99, not 100. 100 gives possible off-by-one error */ if (stackpos <= 99) { DELETE(stack[stackpos]); stack[stackpos++] = strdup(parenttag); } if (value[0] != 0) { strvacat(tag, sizeof(tag), DELIMITER, value, (char *) 0); value[0] = 0; } if (parenttag[0] == 0) strncpy(parenttag, tag, sizeof(parenttag)); else strvacat(parenttag, sizeof(parenttag), DELIMITER, tag, (char *) 0); tag[0] = 0; } /* generate the item name */ if (parenttag[0] == 0) strncpy(item, tag, sizeof(item)); else { if (termchar != '{' || tag[0] != 0) { item[0] = 0; strvacat(item, sizeof(item), parenttag, DELIMITER, tag, (char *) 0); } else { strncpy(item, parenttag, sizeof(item)); } } /* insert the data item into the tree */ config_set(cfg, item, value); linebuf[0] = 0; if (termchar == '}') { if (stackpos == 0) parenttag[0] = 0; else { strncpy(parenttag, stack[--stackpos], sizeof(parenttag)); DELETE(stack[stackpos]); } } } else s++; } /* store the line fragment */ strstrip(buf); if (buf[0] != 0 && linebuf[0] != 0) strvacat(linebuf, sizeof(linebuf), " ", (char *) 0); strcat(linebuf, buf); } out: fclose(infp); for (i = 0; i < DIM(stack); i++) DELETE(stack[i]); return ret; } static void config_dump(struct configuration *cfg) { const struct configitem *top = cfg->tree(cfg, 0); char buf[512]; while (top != 0) { buf[0] = 0; config_fulltag(top, 0, buf, sizeof(buf)); printf("%s \"%s\"\n", buf, top->value); if (top->child != 0) { top = top->child; continue; } while (top != 0 && top->next == 0) top = top->parent; if (top != 0) top = top->next; } } static struct configitem *config_tree(struct configuration *cfg, const char *tag) { return config_lookup(cfg, tag, 0); } struct configuration *config_new(void) { struct configuration *config = NEW(struct configuration); memset(config, 0, sizeof(struct configuration)); config->root = NEW(struct configitem); memset(config->root, 0, sizeof(struct configitem)); config->get = config_get; config->geti = config_geti; config->set = config_set; config->seti = config_seti; config->exists = config_exists; config->read = config_read; config->dump = config_dump; config->tree = config_tree; return config; } void config_delete(struct configuration *config) { struct configitem *next; struct configitem *top = config->root; while (top != 0) { if (top->child != 0) { top = top->child; continue; } while (top != 0 && top->next == 0) { next = top->parent; DELETE(top->tag); DELETE(top->value); DELETE(top); top = next; } if (top != 0) { next = top->next; DELETE(top->tag); DELETE(top->value); DELETE(top); top = next; } } DELETE(config); }